Skip to content

CLI reference

The CLI is a single file, m8shift.py 4.1.0 (Python 3.12+, standard library only — the core; the optional RTK filter and Headroom/Kompress compression adapter are version-pinned via install.sh --with-rtk / --with-headroom and gated by --allow-project-local-adapters). Run it from a project root.

All commands return exit code 0 on success, 1 on a refusal or runtime error, and 2 on an argument error. Readiness checks such as wait --once, next --once, and peek return 3 when the agent should not proceed yet.

🟣 claim / append / work · 🟢 done · ⚪ status / next / read-only views · 🟠 stored ledgers

Shipped commands

init

Generate or regenerate the kit in the current folder.

bash
python3 m8shift.py init [--name NAME] [--agents a,b,c] [--lang en|fr] [--force] \
  [--companions LIST | --with-runtime ... | --full | --no-companions] \
  [--companion-source DIR] [--force-companions]
FlagDefaultMeaning
--namefolder nameproject name written into generated files
--agentsclaude,codexrelay roster; at least two names; one shared degree-1 pen
--langenlanguage of generated files (en or fr in the bundled build)
--forceoffalso reset the relay file; otherwise an existing relay is kept

Companion install

init can also copy the selected companion scripts into the kit dir, version-locked to the core. The copy is idempotent and no-clobber (it refuses an edited or newer local companion and never downgrades one), atomic, and preflighted before any mutation — a bad selection exits non-zero with no half-initialized relay. Installed companions are recorded in a merged .m8shift/kit.json manifest, and doctor reports missing, skewed, or edited companions read-only.

FlagDefaultMeaning
--companionsnonecomma-separated companions to copy into the kit dir: runtime,context,worktree,headroom,i18n,e2e
--with-runtimeoffcopy m8shift-runtime.py
--with-contextoffcopy m8shift-context.py
--with-worktreeoffcopy m8shift-worktree.py
--with-headroom-companionoffcopy m8shift-headroom.py (the launcher only, not the venv/deps)
--with-i18noffcopy m8shift-i18n.py
--with-e2eoffcopy m8shift-e2e.py
--fulloffcopy all operational companions (runtime,context,worktree,headroom,i18n)
--no-companionsoffcopy no companions (explicit opt-out; cannot be combined with a selection)
--companion-sourcekit dirdirectory to copy companions from (a release/checkout dir); defaults to the running m8shift.py dir
--force-companionsoffreplace an older or edited local companion (never downgrades a newer one)

status

Print the current lock: holder, state, turn, roster, session, UTC timestamps, and human-facing local time prefixed by the timezone name/offset when available (otherwise local).

The header also identifies where you are: the project name, the working directory (cwd, the real directory the command runs from), and the relay root (root), so multiple open terminals or tabs stay distinguishable. The project label prefers the name given at init --name (persisted on the session start event) and falls back to the relay-root folder name; cwd and root diverge correctly when the tool is invoked from a subdirectory.

bash
python3 m8shift.py status [--for agent] [--json]
  • --for agent adds the next safe action for that agent.
  • --json emits machine-readable status with UTC timestamps, including the same project, cwd, and root keys as the human header.

watch

Continuously reprint status until interrupted — a live, read-only view of the relay as it evolves. It never writes, claims, or steals the pen.

bash
python3 m8shift.py watch [--for agent] [--interval N] [--clear] [--changes-only]
  • --interval N sets the refresh seconds; --changes-only reprints only on a change; --clear redraws in place.
  • Each refresh banner shows the project name and the working directory, so a watch left running in another terminal or tab identifies itself at a glance.

doctor

Run read-only health and lint checks.

bash
python3 m8shift.py doctor [--lint] [--json] [--security] [--contracts] \
  [--install] [--source DIR] [--severity-min info|warning|error]

--lint exits non-zero when findings at or above the selected severity exist. --security adds local security checks. --contracts adds Stage-4 contract validation findings. --install (v3.52.0) adds read-only post-install verification: Python floor, core presence, checksum-manifest validity and drift, companion status, and optional helper states — a missing optional helper is info, never a warning, so a healthy minimal install stays green. --source DIR compares the local core against a newer source copy and adds a workspace.dirty_worktree advisory when the checkout has uncommitted changes.

contract validate

Validate Stage-4 handoff contracts in the turn journal. This is read-only: it never claims, routes, grants permissions, runs tests, or mutates the LOCK.

bash
python3 m8shift.py contract validate [--strict] [--json] [--all] \
  [--severity-min info|warning|error]

schema=stage4.v1 activates validation for a turn. Default mode reports findings and returns success unless the command itself fails. --strict exits non-zero when findings at or above the selected severity exist.

recap

Print a current-session briefing — the lock, recent turns, memory headlines, and open tasks — what an agent reads when it resumes. (recap is the current session; history is the log of past ones.)

bash
python3 m8shift.py recap [--turns N] [--memory N] [--tasks N]
python3 m8shift.py recap --turns 6 --memory 5 --tasks 5

peek

Read the latest handoff addressed to an agent without claiming the pen.

bash
python3 m8shift.py peek <agent>

It returns 3 if the relay is not waiting for that agent.

log

Show the relay timeline.

bash
python3 m8shift.py log [--limit N] [--all] [--oneline]

--all includes archived turns.

history

Show prior sessions (start / reset / done) folded from the append-only M8SHIFT.sessions.jsonl ledger — a readable, reproducible session log, not an automatic summary.

bash
python3 m8shift.py history [--limit N] [--oneline] [--json]
python3 m8shift.py history --oneline
python3 m8shift.py history --json

wait

Block until it is <agent>'s turn.

bash
python3 m8shift.py wait <agent> [--once] [--interval N]
FlagDefaultMeaning
--onceoffcheck once and exit — rc 0 if you may acquire, rc 3 if not yet
--interval60seconds between polls in blocking mode

wait blocks a process; it does not wake an interactive UI. See the VS Code guide.

next

Safe resumption command: wait if needed, then perform the normal claim and print the latest handoff.

bash
python3 m8shift.py next <agent> [--once] [--interval N] [--force]

--once is non-mutating when it is not your turn. --force only recovers a stale WORKING_* lock.

claim

Acquire the pen exclusively. This is the only way to start writing.

bash
python3 m8shift.py claim <agent> [--force|--refresh]
python3 m8shift.py claim <agent> --check [--files CSV] [--turns N]

Re-claiming a lock you already hold refreshes its 30-minute TTL — the manual heartbeat for a long WORKING_<you> (the agent or a headless wrapper re-runs it; no daemon does it for you). --force reclaims a stale lock only. --refresh (v3.46) only extends your own WORKING lock — refused otherwise, mutually exclusive with --force; automated runners must heartbeat with --refresh, never a plain claim. --check is read-only: it reports readiness and advisory file overlap without taking the pen.

guard-exec

Check one agent's live, binding-resolved pen immediately before replacing the wrapper process with one shell-free command:

bash
python3 m8shift.py guard-exec <agent> -- COMMAND [ARG ...]

The child receives the exact M8SHIFT_AGENT, resolved M8SHIFT_ROOT, and the running core path. Native exit codes and signals are preserved. This is the direct-holder path for one Git/forge mutation; it narrows the cooperative check/use window but is not a hostile-host sandbox.

lease-keeper

Run one interactive child while maintaining both the actual write lease and protective liveness:

bash
python3 m8shift.py lease-keeper <agent> \
  [--cadence-seconds N] -- COMMAND [ARG ...]

The wrapper starts only if the agent holds a live pen. While the child lives it runs claim --refresh and heartbeat --source wrapper at a bounded cadence no later than half the TTL. If either guarantee fails, it stops the child process group. A heartbeat alone prevents stale takeover but does not renew write authority; the two operations are intentionally both required.

append

Close your turn and hand the pen to another roster member. Requires that you currently hold the pen (state == WORKING_<you>).

bash
python3 m8shift.py append <agent> --to <other> \
  [--ask "what the next agent should do"] \
  [--done "what you completed"] \
  [--files "a.py,b.md"] \
  [--body PATH|-] \
  [--wait] [--wait-interval N] \
  [--branch B] [--commit SHA] [--tests "cmd"] \
  [--next "next step"] [--blocked-on "reason"] \
  [--schema stage4.v1] [--relation review_request|review_result|handoff|escalation] \
  [--role-from role] [--role-to role] \
  [--requires "required checks"] [--expected-output "deliverable"] \
  [--evidence "tests or commands"] \
  [--decision approve|revise|reject|waive] [--waiver-reason "why"] \
  [--permissions "intent"] \
  [--field key=value]

--to is required and cannot equal the sender. --body - reads from stdin. --wait keeps the caller blocked after handoff until its next turn or DONE, which prevents premature UI/automation exits.

Stage-4 flags serialize to plain advisory turn fields. They are checked only by contract validate / doctor --contracts; the relay still routes exclusively on the LOCK.

remember

Append one durable shared-memory note. It does not require the pen.

bash
python3 m8shift.py remember <agent> "note"

task

Maintain an append-only task ledger. It does not require the pen.

bash
python3 m8shift.py task add <agent> "description" [--for assignee] [--blocked-on reason]
python3 m8shift.py task done <agent> <id>
python3 m8shift.py task drop <agent> <id>
python3 m8shift.py task list [--all]
python3 m8shift.py task show <id>

unclaim

Undo your own unused claim (v4.2). Restores the exact AWAITING_<you> state the claim replaced, without advancing the turn — the listener's turn-reservation cleanup path. Refused once the child has authored the turn, and the refusal is observable rather than silent.

bash
python3 m8shift.py unclaim <agent> --reason TEXT

release

Hand off without recording a numbered turn; this does not increment turn.

bash
python3 m8shift.py release <agent> --to <other> [--force]

done

Mark the relay finished (state: DONE).

bash
python3 m8shift.py done <agent> [--force]

pause / resume

Park an open session with no active work, then resume it only when the maintainer assigns new scope.

bash
python3 m8shift.py pause <holder> --reason "no further assigned work"
python3 m8shift.py resume <agent> --reason "user assigned new scope"
python3 m8shift.py next <agent> --resume --reason "user assigned new scope"

PAUSED has holder=none and no expiry. wait <agent> does not treat it as the agent's turn; resumption is explicit.

archive

Move older turns to M8SHIFT.archive.md, keeping the lock and the most recent turns.

bash
python3 m8shift.py archive [--keep N]

--keep defaults to 6. Turn #0 is never archived.

Optional worktree companion

m8shift-worktree.py is a separate companion for isolated parallel feature work. It creates per-task git worktrees and serializes integration back through one integration pen.

bash
python3 m8shift-worktree.py claim|done|integrate|drop|status ...

Use it when you need parallel branches/worktrees. The core m8shift.py relay remains degree 1 in the shared repository.

Optional runtime companion

m8shift-runtime.py is a separate local companion for provider registry files, roles/workflows, approvals, reports, live presence, operator inbox, progress, runtime status, diagnostics, lane ownership, no-progress checks, and bounded runtime sidecar retention.

bash
python3 m8shift-runtime.py init
python3 m8shift-runtime.py providers list
python3 m8shift-runtime.py watch codex --no-progress-warn-after 300
python3 m8shift-runtime.py progress codex --message "tests running"
python3 m8shift-runtime.py status-runtime --agent codex
python3 m8shift-runtime.py doctor --json
python3 m8shift-runtime.py retention prune --keep 1000

Scoped gateway transport

The live pen holder can grant one distinct gateway actor an immutable, expiring capability for one canonical transport action:

bash
python3 m8shift-runtime.py gateway-mandate create \
  --by HOLDER --actor GATEWAY --action ACTION \
  --target KEY=VALUE [--ttl-seconds 30..900] -- EXACT_ARGV...

python3 m8shift-runtime.py gateway-exec \
  --actor GATEWAY --mandate ID --action ACTION \
  --target KEY=VALUE -- SAME_EXACT_ARGV...

The mandate binds actor, action-specific targets, physical project and working directory, relay turn, argv digest, executable path and executable bytes. It is claimed atomically before launch and cannot be replayed. The allowed actions are exact push, issue comment, PR creation and PR comment command shapes. Merge, close, tag, deletion, mirror, force-push and other integration/destructive work cannot be expressed by this surface and retain explicit human authorization.

fleet

Since v3.61.0 (RFC 072) the runtime companion also plans and reconciles a declarative batch of exact agent identities from a m8shift.fleet.spec.v1 document (curated provider template + explicit model only).

bash
python3 m8shift-runtime.py fleet plan --spec FILE [--json]
python3 m8shift-runtime.py fleet health --spec FILE [--json]
python3 m8shift-runtime.py fleet apply --spec FILE --by HOLDER [--json]
python3 m8shift-runtime.py fleet reconcile|stop|resume --spec FILE \
  [--backend local] [--runner PATH] [--grace 10] [--dry-run] [--json]

fleet plan and fleet health are pure reads. fleet apply is holder-attributed: it writes one git-ignored exact identity artifact per lane and delegates live roster membership to core roster add; reapplying the same spec is a no-op. fleet stop never removes roster membership.

fleet supervise

One control-plane process reconciles every declared lane; it never claims or appends.

bash
python3 m8shift-runtime.py fleet supervise --spec FILE \
  [--backend local] [--runner PATH] [--grace 10] [--dry-run] [--json] \
  [--poll-interval 20] [--max-ticks 0] [--detach] [--reconcile-control]

--detach (v3.61.0, RFC 073 slice 2) launches the single control plane through the selected native service backend (launchd, user systemd, or a Windows service) when available, or a weaker local detached fallback reported explicitly. It persists crash-consistent control, lane, and opaque-session records under .m8shift/runtime/fleet/, reconciles PID start identity on startup, and fails closed to needs_reconciliation on corrupt, stale, reused, or ambiguous evidence. --reconcile-control accepts a changed fleet spec only after durable evidence shows the previous supervisor is not alive.

fleet jobs

Immutable producer jobs with explicit done criteria and shell-free verification recipes — provider exit alone is never completion.

bash
python3 m8shift-runtime.py fleet jobs plan --spec FILE [--json]
python3 m8shift-runtime.py fleet jobs submit --spec FILE --by INTEGRATOR [--json]
python3 m8shift-runtime.py fleet jobs assign --spec FILE --by INTEGRATOR [--json]
python3 m8shift-runtime.py fleet jobs attempt --id ID --by PRODUCER \
  --provider-exit N [--json]
python3 m8shift-runtime.py fleet jobs integrate --id ID --by INTEGRATOR \
  --to AGENT [--json]

submit requires the live holder to be the declared integrator; changed retries fail closed. assign is integrator-gated, delegates worktree creation to the worktree companion, allows at most two active isolated producer worktrees, and never places a producer on the shared target. attempt records the provider exit first, then runs the exact recipe inside the assigned worktree. integrate requires verified evidence and the exact designated integrator — a producer cannot self-integrate.

Advisory companion only

Runtime sidecars never grant the pen, never edit M8SHIFT.md directly, never require network access, and never auto-force a holder. They are removable local observability files under .m8shift/.

Built with ❤️ and M8Shift · Accessibility · r/m8shift