Theme

CLI

mimeo — pull your instance's definitions into a git repo, edit them, and push them back through a diff, a validation pass and a rehearsal.

The CLI is small on purpose: auth, pull, diff, push. It carries no knowledge of the file formats at all — it reads your repo into { path => contents }, posts that to your instance, and prints what comes back. Serialization, diffing, validation and dry-running all happen on the instance, so the repo format has exactly one implementation and it's always the one your instance is actually running.

Getting it

It ships inside the manager template repo as bin/mimeo — a single file with no dependencies beyond the Ruby that already ships with macOS and every Linux. There is nothing to install.

git clone <your copy of the template> my-email-operation
cd my-email-operation
./bin/mimeo auth

auth asks for your instance URL and an API token (create one under Settings → API), then checks the connection. The URL lands in .mimeo/config.yml, which is committed so everyone on the repo points at the same place. The token lands in .mimeo/token, which is gitignored — and the CLI adds that line to .gitignore itself if it's missing, because a token in a commit is a token you have to rotate.

The loop

./bin/mimeo pull      # bring the repo up to date with the instance
                      # ... edit files, or ask your agent to ...
./bin/mimeo diff      # what a push would change — nothing is applied
./bin/mimeo push      # diff → validate → rehearse → apply

Commands

CommandWhat it does
mimeo auth Point this repo at an instance. Takes --host and --token, or asks.
mimeo pull Write the instance's definitions here, overwriting. Reports created, updated and removed files.
mimeo diff What a push would change, with validation and a rehearsal of every changed flow. Changes nothing.
mimeo statusThe same thing, by its other name.
mimeo push Show the diff, validate, dry-run, then ask before applying.

Push options

FlagEffect
--prune Also remove what's on the instance but not in the repo. Off by default: a stray rm or a bad merge shouldn't delete a flow. Absences are always reported either way. Emails are archived rather than destroyed, and a flow people have run through refuses regardless — its history is theirs.
--force Apply despite the identity warnings (node_id_freed, node_id_repurposed). Never gets past an actual error, and never past a file that couldn't be read.
--yes, -yDon't ask before applying.

What push actually does

Four steps, in this order, and it stops at the first that fails:

  1. Diff. What changes on the instance, per file. Steps are named by their handle (10-send-pitch) — the same thing the diagram, the logs and the traces call them. A step pointed at a different email is described in the chooser's own terms ("now uses emails/0043-x.md, shared with 3 other places") rather than as an anonymous id change, because it's the same consequential decision.
  2. Validate. Every definition, not just the changed ones. Errors block. Warnings are shown. Definitions that were already broken on the instance are reported separately and don't block — you can't fix them by not pushing.
  3. Rehearse. Every changed flow is dry-run against a real person, with event details that satisfy that flow's own trigger. Nothing is sent and nothing is written.
  4. Apply. All of it or none of it, in one transaction.

Applied changes appear live on any admin page you have open — no refresh.

The two things it refuses over

A step's id is what the runs of people standing on it point at. Give a new step an id an old step had, and everyone parked there silently lands somewhere they were never sent. Nothing in a flow file shows this — an id is just a positive integer — so the instance compares against the definition being replaced:

CodeWhat happened
node_id_freed A new step carries an id this flow handed out and gave up in an earlier save.
node_id_repurposed The delete and the replacement are in the same file, so the id never looks free — what gives it away is that the step under it is a different kind of step than it was.

Both are warnings in the validator and both block a push. The message says how many people are parked on that id right now. The fix is almost always to remove the id from the new step and let the instance mint one; --force exists for the operator who knows those runs are gone.

Environment

VariableEffect
MIMEO_HOSTThe instance URL, overriding .mimeo/config.yml.
MIMEO_TOKENAn API token, overriding .mimeo/token. Read first, so CI never needs a file on disk.
NO_COLORPlain output.

What it touches

Only flows/, segments/, guards/, sequences/, emails/, templates/, fields.yml, products.yml and settings.yml — the definitions repo layout. Your README, your agent skills, your notes and any dotfile inside those directories are yours and are never read or removed.

Mimeo never touches git. Committing, branching and merging are yours; the CLI only ever talks to your instance over HTTP.

See also: the file layout · the API underneath · MCP server