CLI
mimeo — pull your Mimeo's definitions into a git repo, edit
them, and push them back through a diff, a validation pass and a rehearsal.
Two facts up front. There is nothing to install — the CLI is
a single file at bin/mimeo in your
manager repo, running on the Ruby that ships
with macOS and every mainstream Linux. And Mimeo never touches
git — pull and push here move definitions
between your working tree and your Mimeo over HTTPS; your Mimeo never
reads your git repo, and no remote is required.
The CLI is small on purpose: init, 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 Mimeo, and prints
what comes back. Serialization, diffing, validation and dry-running all happen
on your Mimeo, so the repo format has exactly one implementation and it's
always the one your Mimeo is actually running.
Getting it
It ships inside the
manager repo
template as bin/mimeo. The easiest start is
Settings → Agents & API → Manager repo on your Mimeo,
which mints a token and hands you these commands with real values filled in:
git clone https://github.com/buildermethods/mimeo-manager.git my-mimeo
cd my-mimeo
git remote remove origin
./bin/mimeo init --host https://your-mimeo.com --token mm_your_token
The remote remove disconnects your clone from the
template, so nothing ever pushes back to it.
init is auth plus a first pull: it
checks the connection, writes every definition into files, points the
committed .mcp.json at your Mimeo's MCP server, and prints
the first-snapshot commit to run. 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.
Every command below is written ./bin/mimeo because that's what it
is — the file in your repo. Nothing lands on your PATH.
The loop
./bin/mimeo pull # sync down first, always
# ... 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
./bin/mimeo pull # after a structural push: pick up ids Mimeo minted
Pull before editing — someone may have changed something in the admin UI since your last sync, and pushing a stale file would quietly revert it. And pull again after a push that added steps or emails: Mimeo mints their ids, and the second pull is how your files catch up.
Commands
| Command | What it does |
|---|---|
mimeo init |
First-time setup: auth, then a first pull, then your next steps printed. Takes --host and --token, or asks. |
mimeo auth |
Point this repo at your Mimeo (and re-point .mcp.json). Takes --host and --token, or asks. |
mimeo pull |
Write your Mimeo's definitions and media 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 status | The same thing, by its other name. |
mimeo push |
Show the diff, validate, dry-run, then ask before applying. |
Push options
| Flag | Effect |
|---|---|
--prune |
Also remove what's on your Mimeo 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, -y | Don't ask before applying. |
What push actually does
Four steps, in this order, and it stops at the first that fails:
-
Diff. What changes on your Mimeo, 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 usesemails/0043-x.md, shared with 3 other places") rather than as an anonymous id change, because it's the same consequential decision. - Validate. Every definition, not just the changed ones. Errors block. Warnings are shown. Definitions that were already broken on your Mimeo are reported separately and don't block — you can't fix them by not pushing.
- 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.
- 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 Mimeo compares against the definition being
replaced:
| Code | What 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 Mimeo mint one; --force
exists for the operator who knows those runs are gone.
Environment
| Variable | Effect |
|---|---|
MIMEO_HOST | Your Mimeo's URL, overriding .mimeo/config.yml. |
MIMEO_TOKEN | An API token, overriding .mimeo/token. Read first, so a secret manager never needs a file on disk. |
NO_COLOR | Plain output. |
Media syncs too
The media library rides along with every pull and
push: the files themselves land under media/, and
media.yml records what each one is on your Mimeo — its hosted
URL, alt text and id. Emails reference a media item by that hosted
url, never by its local path.
-
Add a file by dropping it into
media/—pushuploads it and records the minted URL inmedia.yml. -
Edit alt text in
media.ymland push it like any other change. -
Deletes never travel. A deleted upload 404s inside every
email already sent, so a push never deletes remote media (
--pruneincluded) — a file missing locally is somethingpullrestores. Deleting for real is an explicit act in the admin UI or over the media API.
What it touches
Only flows/, segments/, guards/,
sequences/, emails/, templates/,
media/, fields.yml, products.yml,
themes.yml, settings.yml and media.yml
— the definition files reference.
Your README, your agent skills, your notes and any dotfile inside those
directories are yours and are never read or removed — as is any file in
media/ your Mimeo doesn't have yet, which is an upload waiting
for a push.
Mimeo never touches git. Committing, branching and merging are yours; the CLI only ever talks to your Mimeo over HTTP.
See also: your manager repo · the file layout · the API underneath · MCP server · files or MCP?