CLI reference
Every emet subcommand, the gates and records table, exit codes and environment variables. Terms follow GLOSSARY.md. Back to the README. Every command prints its usage with --help.
Subcommands
Three slash commands wrap the CLI inside Claude Code: /emet:run (the pipeline), /emet:init (emet init) and /emet:doctor (emet doctor .). A plain-terminal emet needs install.sh; otherwise run ! emet <subcommand> inside a session.
emet <command> [...args]. emet --version prints the version.
Setup and audit
emet migrate [--dry-run]: renames a legacy repo to the emet names.emet init [--tracker linear|github|local] [--force]: autodetects the package manager, trunk branch and tracker kind, and writesemet.config.jsonand thedocs/agents/*.mdfiles.--tracker localsets up the local tracker.emet doctor [--all] [<path> ...]: read-only audit of any number of repos at once; defaults to the current directory.emet preflight [--run <id>] [--post-dispatch]: confirms the repo is dispatch-ready.emet skill-path <name>: prints the absoluteSKILL.mdpath of a mattpocock-skills skill. Exits 1 if the skill is not found.emet selftest: runs the CLI’s own unit tests. Source installs only; a compiled binary exits 1 and tells you to runemet doctor.
In any git repo:
emet init # autodetects package manager, trunk branch, tracker kindemet preflight # confirms the repo is dispatch-readyemet init leaves a TODO(emet): marker in the generated docs/agents/*.md files for anything it cannot infer on its own (a shared test-database lock, schema ownership, and so on). Fill those in; emet preflight refuses to dispatch while one remains.
Then, inside Claude Code:
/emet:run <describe the feature> # interview → wave 1; later waves via --dispatch/autoChain/emet:run --draft <describe the feature> # stop after tickets are created/emet:run --dispatch <spec-id> # dispatch a --draft'd spec laterFleet check
emet doctor audits several repos at once, read-only:
emet doctor ~/code/*It reports, per repo: a valid emet.config.json, all four docs/agents/*.md files present, zero leftover TODO(emet): markers, whether the commands in commands.selfCheck actually resolve, and that repo’s tracker-specific dependency findings (gh, Linear; see dependencies). The mattpocock-skills plugin and the external CLI flags (gh, claude --bg, claude agents --json, checked via --help) are checked once for the whole sweep, not per repo.
Runs, workers and slots
emet claims --reserve <ticket> --run <id> --anchor <pid>: reserves a ticket for a run.--release <ticket>,--listand--prune [--dry-run]release a claim, list claims, or prune stale ones.emet slots --acquire --run <id> --anchor <pid> --ticket <ticket>: takes a worker slot from the host-wide pool in~/.local/state/emet/slots.--release --run <id>,--release-index <index>and--listfree slots or list them.emet cleanup [--run <id> | --all] [--dry-run] [--force]: bare, reclaims closed or dead runs whose work landed;--runcleans this run;--allis a repo-wide branch sweep. After a merge,emet cleanup --run <runId>frees the wave’s branches, ticket claims and worker slots.emet lock-reason --run <id> --ticket <ticket> [--anchor <pid>]: prints thegit worktree lockreason a worker writes at bootstrap.emet launch <spec-id> --wave <n>: starts a wave as aclaude --bgsession and opens its Orca tab perhandoff.launcher. It does the whole launch (duplicate-session check,claude --bg, tab) and prints{ name, id, attach, orcaTab: { opened, reason } }. Use it, not a bareclaude --bg, whenever you want a wave in another session by hand.emet status [--spec <id>]: Working / Checking / Ready / Needs-decision per wave as JSON plus a summary line.emet status --helpdocuments the JSON.emet usage [--session <id> | --file <path>] [--stalled [--minutes N]] [--project-dir <path>]: reports session usage;--stalledlists stalled workers.emet handoff --check <path> | --render <path>: validates or renders a worker’s.emet/handoff-<ticket>.mdnote.emet metrics [--run <id>]: reports tokens, API-equivalent cost and wall time per ticket, per wave and per run, read from the session transcripts. It also compares the context budget estimated per ticket with what each worker actually used. Its summary goes at the end of the run’s final report.
Planning, review and landing
emet validate --spec <id> | --file <path>: plan validation; see Gates and records.emet surfaces --check <file.json | ->: rejects overlapping allowed-surface globs between a wave’s tickets.emet approve --run <id>,emet materialize-log,emet results,emet verify,emet review-package,emet review,emet gate: see Gates and records.emet assess --base <ref> --head <ref>: classifies a diff aspassive,mediumorhighand prints JSON with the tier, the reasons and the recommended reviewer model and effort. See Models & effort andreview.emet land --spec <s> --branch <integration> --tickets <id,...>: merges an integration branch into local trunk (landing.modemerge).commands.selfCheckstands in for CI and nothing is pushed.emet spec-merge --spec <id>: after a wave auto-merges, merges the spec’sADDED/MODIFIED/REMOVEDrequirement deltas intodocs/specs/<area>.md(specs.dir). It checks every area before writing anything. A conflict, or a requirement that would lose a scenario, exits 1 and leaves every file untouched.emet map [--refresh]: writes a brief of an existing codebase (stack, structure, conventions, testing, hotspots) todocs/agents/codebase.md(map.path), stamped with the commit SHA it was built from. The same commit always produces the same file.emet map --staleexits 1 once HEAD has moved past the stamp, andemet preflightwarns in that case.emet map --for <globs>prints only the sections that match a ticket’s edit surfaces; that output is the only part of the map a worker brief includes.emet tracker create-spec|create-ticket|read|frontier|set-state|comment|close: the local tracker over.scratch/, JSON on stdout. See Local tracker.emet guard: the Claude CodePreToolUsehook; reads hook JSON on stdin.install.shwires it into~/.claude/settings.json. It blocks a handful of known, unattended-run failure triggers (seeemet/src/guard.ts).
Local tracker
With tracker.kind: "local", specs and tickets are markdown files under the main worktree’s .scratch/ (not versioned): .scratch/<slug>/spec.md and .scratch/<slug>/issues/<NN>-<slug>.md, each with Status: and Blocked by: lines and comments under ## Comments. The coordinator reads and writes them through emet tracker (create-spec, create-ticket, read, frontier, set-state, comment, close). emet init --tracker local sets this up. See ADR-0011.
landing.mode is "pr" (default) or "merge", independent of the tracker. In merge mode a wave lands as one local merge into trunk with emet land, with commands.selfCheck standing in for CI and no push. gh is only required when the tracker is github or landing is pr. See ADR-0010 and landing.
Landing PRs and chaining waves
One wave is one PR. Its body carries Closes #N for each ticket (the tracker’s closing keyword), and the coordinator moves the tickets to in-review when it opens the PR. The PR is then merged per pr.autoMerge, or left open when the wave was flagged, autoMerge is false, or CI fixes ran out (the PR then gets the human-decision label). Later waves run in their own sessions, never in the one that ran wave 1. The pr and handoff sections control what happens next; see pr and handoff.
Gates and records
The phases do not decide on their own when a ticket is done, mergeable or worth retrying. They call a CLI command that reads a record on disk and prints the decision. Run records live under .emet/runs/<run-id>/ and the review ledger under .emet/review/, in the main checkout, not in a worker’s worktree.
| Step | Command | What it enforces |
|---|---|---|
| Before approval | emet validate --file <plan.json> |
Every S# is covered by at least one ticket and has an acceptance scenario. Every ticket covers at least one S#. Edit surfaces are disjoint within a wave. No dependency cycles. emet preflight runs it again on the approved plan. |
| Approval | emet approve --run <id> --spec <file> |
Writes approval.json with the spec and ticket-list hashes. Dispatch refuses if either changed afterwards (--check). |
| Ticket creation | emet materialize-log --run <id> --record/--lookup |
Saves each created ticket’s ID as soon as it exists, so an interrupted run never creates duplicates. |
| Worker done | emet results --run <id> --write/--read <ticket> |
Workers write a local result file (DONE, DONE_WITH_CONCERNS, BLOCKED or NEEDS_CONTEXT, plus a Risk: line). Only the coordinator writes to the tracker. |
| After each worker | emet verify <ticket> --run <id> |
Checks that commits exist beyond the dispatch baseline, every changed path is inside the ticket’s edit surfaces, the handoff note passes handoff --check, and commands.workerScopedSuite passes. Review and merge refuse a ticket without a pass record. |
| Review input | emet review-package <base> <head> --out <file> |
Bundles the diff for the reviewer, who receives it as a file path. Exits 3 if the range is empty or not a descendant of base. |
| Review | emet review --ticket <t> --findings <file> |
Keeps the findings ledger and enforces triage order (intent_gap → bad_plan → patch → defer) and the round cap. --escalate moves to the next model tier. --rule/--rulings record and list the “Rulings I made” for the report and the PR body. |
| Merge or chain | emet gate <ticket> --tier … --ci … --integration … |
Prints merge, hold, chain, stop or defer as JSON with its reasons. It reads the verify record, the open findings, the risk tier, CI, the integration suite and the budgets. |
After a wave’s branches are merged together, commands.fullSuite runs once on the integration branch. A red run blocks the PR and autoChain. The gate receives the result as --integration. For the budgets the gate reads, see budgets.
Exit codes
Exit codes are meaningful across every subcommand.
| Code | Meaning |
|---|---|
0 |
Clean. |
1 |
Findings to act on. |
2 |
Config or usage error; the tool never actually ran. Also an unknown command. |
3 |
Internal error. |
Environment variables
User-facing variables. A variable overrides the matching config value when set.
| Variable | Effect |
|---|---|
EMET_SKIP_DEPENDENCY_CHECKS |
EMET_SKIP_DEPENDENCY_CHECKS=1 emet preflight downgrades a dependency block to a warning for that one run. Set it in your own shell, not in anything the coordinator runs unattended. |
EMET_MAX_LIVE_WORKERS |
Overrides concurrency.maxLiveWorkers; accepts a positive integer or auto. |
EMET_RUN_IDLE_THRESHOLD_MS |
Overrides concurrency.runIdleThresholdMs, in milliseconds. |
EMET_STATE_DIR |
Host-wide state directory, which holds the worker slot pool. Resolution order: $EMET_STATE_DIR, then $XDG_STATE_HOME/emet, then ~/.local/state/emet. |
EMET_MEM_RESERVE_GIB |
Memory headroom in GiB that must stay free; an acquire is denied when available memory is below it. Default 8. It also feeds the auto worker cap. |
EMET_MEM_PER_WORKER_GIB |
Memory in GiB assumed per worker when maxLiveWorkers is auto. Default 2. Raise it for a heavier per-worker stack, such as a browser-driven E2E suite per worktree. |
EMET_CPU_OVERSUBSCRIBE |
CPU multiplier for the auto worker cap. Default 1. |
The auto cap is clamp(min(cpuCount × EMET_CPU_OVERSUBSCRIBE, floor((memTotal − EMET_MEM_RESERVE_GIB) / EMET_MEM_PER_WORKER_GIB)), min, max). The legacy FORGE_* names are still read; EMET_* wins when both are set.