---
name: mu
description: Manage AI agents in terminal-multiplexer panes (tmux or herdr) — from a single off-the-cuff helper to a persistent crew coordinated through a built-in task graph. Use when the user asks to "create/spin up a subagent to X", "run X in the background", "do this in parallel", "use one subagent per X to do Y", "kick off a helper to watch/investigate/draft X", or to spawn, send work to, observe, or coordinate one or many agents — especially work you'll keep talking to, long-lived agents, background tasks, or anything that benefits from a dependency graph and parallel-track detection. For zero-ceremony single helpers use the reserved `scratch` workstream; for one-shot "fire and get a result back" prefer pi-subagents.
---

# mu — Multi-agent orchestration

`mu` manages long-lived AI agents in multiplexer panes (tmux or
herdr), coordinated by a SQLite task DAG at
`<XDG_STATE_HOME or ~/.local/state>/mu/mu.db`.

**Trust `mu --help` / `mu <verb> --help` over this skill.** Verbs
not in `--help` do not exist.

## Output + JSON shapes

Default output: textual card on stdout plus a `Next:` block. Read
both. `--json` exists on every verb:
- Success: one stdout object.
- Collection reads (`task list`, `workspace commits`, `archive
  search`, ...): `{items: T[], count: number}`.
- Singletons keep named fields.
- `mu sql --json`: bare array rows.
- `mu log --tail`: NDJSON (one object per line).
- Errors: `{error,message,nextSteps,exitCode}` on stderr.
- Validation errors also include structured `usage`.
- **`nextSteps` survives in JSON**. `mu task wait --first --json`
  puts the cherry-pick command in `.nextSteps[0].command`.

## Vocabulary

- **workstream** — unit of organization; one **mux session** named
  `mu-<name>` (a tmux session, or a herdr workspace).
- **agent** — named worker in a pane (you may be one).
- **mux** — the multiplexer mu drives: tmux, or herdr. One per
  invocation. `mu doctor` names the active one; `MU_MUX` forces it.
  Only relevant to you if a verb fails with a mux error — on herdr,
  `agent spawn` / `send` / `read` are not implemented yet (exit 5).
- **task** — DAG node with mandatory `impact` (1–100) and
  `effort_days`. Status: `OPEN`, `IN_PROGRESS`, `CLOSED`
  (satisfies `--blocked-by`), `REJECTED` (terminal won't-do; still
  blocks), `DEFERRED` (parked; still blocks).
- **claim / release** — atomic take/clear of `tasks.owner`.
- **free** — mark the *agent* available (`mu agent free`); pane
  untouched. **Release is about the task; free is about the agent.**
- **note** — append-only task context; survives sessions.
- **track** — independent DAG subtree; don't spawn more agents than
  ready tracks.
- **workspace** — per-agent VCS copy under
  `<state-dir>/workspaces/<workstream>/<agent>/`.

## When to use mu

Use mu for multi-phase work, review-gated work (`implement → review
→ address → ship`), parallel audits, implementation/reviewer splits,
and anything that must survive context compaction via task notes.

Don't use mu for tiny one-file edits, one-off inspection, or
single-context work where durable coordination adds ceremony.

### Off-the-cuff helpers (the `scratch` workstream)

Want a sub-agent you'll **keep talking to** without a crew or task
DAG? Spawn into the reserved `scratch` workstream. No `mu workstream
init`; it auto-creates and is task-less by design.

```bash
mu agent spawn helper-1 -w scratch     # auto-creates mu-scratch
mu agent send helper-1 'Investigate X. Report findings.'
mu agent read helper-1 -n 50           # check at a natural pause, not in a loop
mu agent close helper-1 -w scratch     # done
```

- **Background watcher:** send the task, then `mu agent wait <name>
  --first` to block until it finishes (busy → idle) instead of a
  `sleep` loop; re-nudge with another `send` (e.g. `'run again'`).
- **Watcher dedupe/memory (log ledger):** a watcher reacting to
  changing external state must remember what it last saw. Chat
  context dies on compaction; use a custom `--kind` tag as a durable
  SQLite ledger instead. Each tick records last-seen state with `mu
  log -w scratch --kind pr-state 'pr=1234 sha=abc ci=red -> spawned
  fixer-1'`; the next tick reads it back with `mu log -w scratch
  --kind pr-state -n 1 --json` (latest wins; `--since <seq>` replays
  missed history). Act only when the new observation differs.
- **Fan-out, one per unit:** loop `mu agent spawn dep-$pkg -w scratch
  --workspace`, then `mu agent wait dep-core dep-cli dep-web` (all) or
  `--any` to react to whichever finishes first. Add `--workspace`
  whenever a helper will edit/build/test a shared repo (see
  "Workspaces prevent trampling"); skip it for read-only helpers.
  `mu state -w scratch` watches them all.

If a helper wedges at `needs_input` right after spawn, it's likely
pi's project-trust prompt; add `--approve` to the existing
`MU_<CLI>_COMMAND`, or pass `--command 'pi --approve'` only when you
intend to override the env-configured command.

**Escalate off `scratch`** the moment helpers have dependencies
(B needs A) or you want gated review → `mu workstream init` + task
DAG. One focused answer, no follow-up → `pi-subagents`. `scratch` is
the middle: "fire, but keep the channel open."

## Mental model

### Workstreams, DAGs, tracks

One workstream = one mux session named `mu-<workstream>`. Every
agent is a pane in that session. DB rows are partitioned by
`workstream`.

One edge type: `blocks`. `mu task block A --by B` means **B blocks
A**. `--by` takes multiple blockers (`--by B,C` or `--by B --by C`),
same shape as `mu task add --blocked-by`. Built-in views: `ready`, `blocked`, `goals`. Bare `mu` shows
parallel tracks with automatic diamond-merge: goals sharing a
prerequisite collapse into one track.

### Workspaces prevent trampling

If an agent may edit/build/test while another agent is active in the
same repo, spawn with `--workspace`. Keep the main checkout for
orchestration: two builds in one checkout corrupt each other.

Workspaces auto-detect jj/sl/git; non-VCS uses `cp -a`. They are
auto-freed on `mu agent close` **iff clean**: no uncommitted changes
and no commits since fork. Non-clean close fails with
`WorkspacePreservedError`; then use `mu workspace free <agent>` or
`mu agent close <agent> --discard-workspace` (lossy).

Between waves:
- `mu workspace refresh <agent>` rebases onto fresh main without
  killing LLM context.
- `mu workspace recreate <agent>` = free + create for fresh
  single-purpose workers; `--force` discards dirty edits.
- `mu workspace commits <agent>` lists since-fork commits for
  cherry-picking.

Claim/send warn when a target workspace is ≥10 commits behind main;
refresh first or pass `--strict-staleness` in scripts.

### Agent names

Use roles: `worker-1`, `worker-2`, `reviewer-1`, `scout-1`,
`auditor-1`, `planner-1`. Smallest unused suffix. Avoid human names.

### Task note contract

End every delegated task with a note containing the applicable
fields:

```text
FILES:    paths inspected/changed (line ranges if precise)
COMMANDS: commands run + exit codes
FINDINGS: what you observed
DECISION: what you chose, and why
NEXT:     follow-on tasks
VERIFIED: tests/checks/output
ODDITIES: weird things not acted on
```

Then close with grounding:

```bash
mu task close <id> -w <ws> --evidence "tests pass: cargo test exit 0"
```

Future agents can reconstruct context via `mu task notes <id>`.

## Orchestrator loop

Every turn:

1. `mu state -w <ws>` — read agents, IN_PROGRESS, ready tasks,
   parallel tracks.
2. Spawn at most one agent per independent ready track.
3. **Claim before sending — even one-shot reviewers/scouts.**
   `mu task claim <id> -w <ws> --for <agent> --evidence "..."`.
   If no task exists, `mu task add` first; include initial context with
   `--note 'REPRO: ...\nSCOPE: ...'` when the title alone is not
   enough. Agent status is noisy; task ownership is durable and
   waitable.
4. Send terse instructions: task id, files/notes to read, workspace
   path, validation command, scope guards, task note contract.
5. End with a loud final-action block:

   ```text
   ⚠️ FINAL ACTION
   git commit -am '...' THEN
   mu task close <id> -w <ws> --evidence '...'
   ```

6. `mu task wait ... --first --any --json --on-stall exit`.
7. Cherry-pick the closed worker's **new** commit(s), verify, return
   control. Do not barrier or loop in shell.
8. Repeat from `mu state`.

## Dispatch rules that prevent real failures

- **Pipeline cherry-picks; don't barrier.** One wait, one
  cherry-pick, one verify, return control. Do not wait on an
  umbrella task for the whole wave; that hides partial progress.
- **Use `--on-stall exit` in non-interactive flows.** Default wait
  warns on stalled alive workers and keeps polling. Exit 7 =
  `STALL_DETECTED`; exit 6 = `REAPER_DETECTED` (dead pane) and wins
  if both happen.
- **Cherry-pick worker commits onto main; don't merge.** Stale
  branches can drag re-reverts.
- **Cherry-pick only new shas.** `workspace commits` lists since
  fork; track what you've already integrated. Don't replay the whole
  worker range each time.
- **Bucket fix waves by file cluster, not severity.** Two workers
  editing one file create merge conflicts.
- **Refresh/recreate workspaces between waves.** The `behind` column
  in `mu workspace list` shows stale-parent risk.
- **Cross-workstream wait/claim:** pass qualified refs
  `<workstream>/<name>`. For `claim --for A/worker-1` on a task in
  B, the agent stays in A; only task ownership crosses.
- **Recover idle agents:** `mu agent show <name> -n N`; then send a
  retry or `mu task release <id>` (bare release reopens
  IN_PROGRESS). Idle threshold: `MU_IDLE_THRESHOLD_MS`, default 5m.
- **Recover wedged tool subprocesses:** `mu agent kick <name>` sends
  SIGINT to the pane TTY foreground process group. Escalate with
  `--signal SIGTERM` / `SIGKILL`. It refuses when the foreground is
  the wrapping CLI; use `mu agent close` then.
- **Use `mu agent send`; never raw `tmux send-keys <text>` or
  `herdr pane send-text`.** mu delivers text atomically — bracketed
  paste on tmux, `agent prompt` on herdr — so `/`, `?`, `f`, etc.
  arrive as text instead of agent-TUI keybindings, and the Enter
  cannot be swallowed by a modal.
- **Prompt quoting:** single-quote prompts containing `$VAR`,
  `$(...)`, backticks, or `!history`, or use a quoted heredoc.

Example wait/cherry-pick skeleton:

```bash
res=$(mu task wait t1 t2 t3 -w ws --any --first --json \
        --timeout 600 --on-stall exit)
worker=$(jq -r .firing.owner <<<"$res")
sha=$(mu workspace commits "$worker" -w ws --json | jq -r '.items[0].sha')
git cherry-pick "$sha" && npm test
```

## Universal flags

- `-w, --workstream <name>` resolves explicit > `$MU_SESSION` >
  current mux session minus `mu-` > error. For entity verbs it is a
  scope check; for pickers it selects which workstream.
- Qualified refs `<workstream>/<name>` skip `-w`; mismatched `-w`
  errors. Bare ambiguous names raise `NameAmbiguousError` (exit 4)
  with one-paste fixes.
- `--evidence "text"` on task `claim` / `close` / `open` / `release`
  (also `reject` / `defer`); recorded verbatim on the emitted op.
- `--json` for composition; `nextSteps` survives.

## CLI overview (only gotchas; use `--help` for full syntax)

- **Workstream:** `init`, `list`, `destroy` (dry-run by default,
  `--yes` commits; writes TOMBSTONE ops so history survives and
  `mu undo <group> --yes` reverses the row deletions;
  `--archive <label>` pins the graph first), `export` (read-only
  markdown bucket for humans/git/docs).
- **Agents:** `spawn` (`--workspace`, `--role read-only`, `--command`),
  `send`, `read`, `show`, `list`, `close`, `free`, `kick`,
  `adopt <pane-id|title>` for orphan panes. Four worth knowing:
  - `wait <names...> --first` — blocks until an agent finishes (busy →
    anything else). The task-less counterpart to `mu task wait`, for
    helpers that own no task. Exit 0 met, 5 timeout, 6 pane died. Use
    it instead of `sleep` loops.
  - `ensure <name>` — watcher-safe spawn: missing → spawn, idle → reuse
    and exit 0, busy → reuse with `busy: true`. `--idle-only` turns the
    busy case into exit 4, i.e. a concurrency lock.
  - `poll` — the non-blocking dual of `wait`: one read-only snapshot of
    the pool (`{name,status,idleMs,lastActivitySeq,workspaceBehind,
    dead}`) for a tick loop to diff against the previous tick.
  - `reap-idle` — sweeps and closes finished, idle, SAFE helpers
    (`--idle-for`, default 300s). Skips dirty workspaces unless
    `--discard-dirty`; `--dry-run` supported.
- **Tasks:** `add` (`--note` for initial context), `list`, `next`,
  `show`, `tree`, `notes`
  (`--tail`, `--since`, `--since-claim`), `note`, `claim`
  (`--for | --self`), `release` (`--reopen` only for un-closing
  terminal tasks), `close` (`--if-ready` no-ops until blockers
  terminal), `open`, `reject`, `defer`, `block`, `unblock`,
  `update`, `reparent`, `wait`, `delete --yes`. Edge direction:
  `block <blocked> --by <blocker>`.
- **Self:** `mu me`, `mu me tasks`, `mu me next`.
- **Workspace:** `create`, `list` (`behind`), `refresh`, `recreate`,
  `commits`, `free`, `path`, `orphans`.
- **Log:** `mu log "text"`, `mu log -n N`, `mu log --tail`. Reads render
  every op as prose ("task close a → CLOSED"), from the op's structured
  intent. Filters: `--intent task.close` (what mu recorded),
  `--kind <tag>` (your own channel tag — the log-ledger pattern),
  `--group <id>` (every op of one action, for undo). `--json` adds a
  `rendered` field so scripts never parse payloads. Use `task wait`, not
  `log --tail`, for waits.
- **Undo:** `mu undo` (no args) lists recent undoable actions with
  their group ids; `mu undo <group>` previews; `mu undo <group> --yes`
  applies. It emits INVERSE OPS for that one group, so it touches only
  that action's rows — not your other workstreams. The undo is itself
  an op in its own group, so REDO is just `mu undo <that group> --yes`
  and it syncs to peers. Refuses with exit 4 if a later action changed
  the same fields (`--force` to override, discarding that newer work).
  Rows only — killed panes and freed workspace dirs do not come back.
  There are no snapshots and no `--to`.
- **Archives:** an archive is a named MARKER pinning a point in the ops
  log, not a copy — so it still restores after its workstream is gone
  (destroy writes tombstones, it does not erase history). Verbs:
  `add <label> -w <ws>` (creates the label on first use), `list
  [label]`, `restore <label> --as <new-ws>` (dry-run; `--yes` applies,
  `-w` picks the source when a label covers several), `export <label>
  --out <dir>`. `mu workstream destroy --archive <label>` pins first.
  Labels are global. No `create`/`remove`/`delete`/`search`/`show` —
  markers are append-only and `list <label>` is the detail view.
- **Recovery:** `mu rebuild <file>` replays the ops log in HLC order
  into a NEW DB file and prints the `mv` command to swap it in; it
  never rebuilds in place. Agents and workspaces are NOT rebuilt (no
  capture triggers, so no ops) and the summary says so — re-spawn
  agents after swapping.
- **State/TUI:** bare `mu` opens the all-workstream TUI on a TTY;
  agents/scripts use `mu state --json`. `mu state --tui` is
  read-only, yanks commands, `?` shows keys, `/` filters popups,
  `Esc`/`q` back, `q`/`Ctrl-C` quits. Non-TTY bare `mu` (or
  `MU_NO_TUI=1`) prints help.
- **Sync (laptop <-> devserver):** `export MU_SYNC_DIR=$HOME/Sync/mu`
  on each machine, pointing at a shared folder (Syncthing recommended).
  Every mu command then flushes your ops and ingests peers' — ambient,
  no daemon, so a bare `mu task list` on the other box already shows
  what you added here. Merge is per-FIELD, so two machines editing
  different fields of one task both keep their edit.
  `mu sync` bare = peer status (machine, last seen, ops behind), plus a
  copy-pasteable rsync line when a peer is stale; mu never runs
  ssh/scp/rsync itself. `--from <peer-mu.db>` reads a peer's ops table
  directly (sshfs or a copy); `--repair <peer>` re-reads a segment from
  zero and is always safe (ingest is idempotent). One-off directory
  needs no flag: `MU_SYNC_DIR=/media/usb mu state`.
  NEVER put `MU_DB_PATH` inside `MU_SYNC_DIR` — it corrupts the DB and
  `mu doctor` hard-fails. Agent/workspace state and task OWNERSHIP are
  machine-local and never travel.
- **Escape hatch:** `mu sql "<query>"` for missing typed verbs. Note
  this is the ONE verb that does not ambient-sync (its no-surprise-
  mutations guarantee is load-bearing).
- **Backup:** `mu db backup <file>` — `VACUUM INTO` copy of the whole
  DB, never overwrites. The "one file I can scp" convenience; real
  recovery is `mu rebuild`.
- **Health:** `mu doctor` (fast checks, exit 0) and `mu doctor --deep`,
  which rebuilds the ops log into a temp DB and diffs it field-by-field
  against the live tables. DRIFT means the log and the tables disagree,
  and since undo / archive / sync are all derived from the log, drift
  breaks all three at once — exit 5, naming table, key and field. It is
  a capture bug, not operator error: back up first and report it, do
  NOT reflexively rebuild (if capture missed a mutation, the live rows
  hold the real work). Run it after anything unusual; ~0.6ms per op, so
  it stays off the default `mu doctor` path.

## `mu task wait` exits

Default target: CLOSED. `--first` = `--any` plus firing id/object.

| Code | Meaning |
|------|---------|
| 0 | All targets met, or `--any` and one met |
| 3 | Missing task id |
| 5 | Timeout |
| 6 | Reaper flipped watched task back to OPEN (target=CLOSED only) |
| 7 | Stall with `--on-stall exit` |

## Models and thinking effort

mu doesn't reason about models; pi does. Controls:

```bash
mu agent spawn r --command "pi --model opus:high"
export MU_PI_COMMAND="pi --model sonnet:medium"
mu agent spawn a --cli pi_big   # uses $MU_PI_BIG_COMMAND
```

Convention: `pi_mini` / `pi` / `pi_big`. Use mini for probing,
modest for build/edit/refactor, big for design/review/incidents.
Discover model strings with `pi --list-models [fuzzy-search]`.

## Reaper and status limits

If an agent pane dies, or `mu agent close` kills it mid-task, owned
IN_PROGRESS tasks revert to OPEN with a `[reaper]` note and `task
reap` op. No manual release after crashes.

Status detection is heuristic and can lag behind custom `--command`
wrappers. For high-stakes decisions:

```bash
mu agent read worker-1 -n 100
mu log -w <ws> --tail
mu task notes <id>
```

## In-pane worker loop

`$MU_AGENT_NAME`, injected at spawn, resolves identity; the pane title
is the fallback that adopted panes need. Env-first means this loop is
identical whichever multiplexer you are in — you never need to know.

- Worker pane: spawned/adopted by mu; bare `mu task claim <id>` works.
- Orchestrator pane: not registered; bare `claim` errors with next
  steps: `--self`, `--for <worker>`, or `mu agent adopt <pane>`.

```bash
mu me
mu me next
mu task show <id>; mu task notes <id>
mu task claim <id> --evidence "starting; read notes"
mu task note <id> "FILES: ...\nDECISION: ...\nVERIFIED: ..."
mu task close <id> --evidence "tests pass: ..."  # LAST action
```

Skipping close makes the orchestrator's wait hang.

## Follow-on prompts

A new `mu agent send` appends to prior LLM context. For unrelated
work, clear first (`/new` for pi/claude-code; `/clear` for codex):

```bash
mu agent send worker-1 '/new'
mu agent send worker-1 'Claim and work on task_x. Read notes first...'
```

No `sleep` needed. `mu agent send` waits for the pane to finish any
re-initialisation before pasting, and re-submits if the TUI swallowed
the Enter. If a send cannot be confirmed it prints a `warning:` to
stderr naming the pane; exit 0 with no warning means submitted.

Budget is `MU_SEND_READINESS_MS` (default 15000; 0 = fire-and-forget).
Sending to a BUSY agent is not delayed — that input queues normally.

## DO / DON'T

DO:
- `mu state -w <ws>` before actions.
- Add a task before assigning work.
- Claim before sending; read notes before claiming.
- Pass `--evidence` on claim and close.
- Drop task notes using the note contract.
- Set `impact` and `effort_days` honestly.
- Use `--workspace` whenever an agent may edit/build/test.
- Send `/new` before unrelated follow-on work (no `sleep` needed).
- Use `mu task wait --first --json`, never shell polling.
- Use `mu doctor` when state looks wrong.

DON'T:
- Barrier on a wave umbrella or loop until "everything is done".
- Fire-and-forget after `mu agent send`.
- Trust status emoji alone.
- Double-quote `$VAR`-laden prompts.
- Bypass mu with `sqlite3`; use `mu sql`.
- Spawn into a named crew workstream without `mu workstream init`
  first (the exception is `-w scratch`, which auto-creates).
- Anthropomorphize agent names.
- Poll `mu agent read` in tight loops.
- Add cross-workstream edges; model as one workstream.
- `mu workstream destroy --yes` without dry-run.
- Use the reserved `mu_` task-id prefix.
- Message agents directly; use task notes and the activity log.
- Prompt workers to run filesystem-wide `find`, broad `grep -r /`,
  or unbounded loops. Pass paths; if wedged, `mu agent kick`.

## What mu is NOT

- Not a build tool, deploy tool, or chat protocol.
- Not a replacement for `pi-subagents`; mu is for long-lived crews.
- Not a place to add config files, daemons, wrapper layers, codegen,
  template discovery, or a render layer beyond current deps.

## See also

- `mu --help`, `mu <verb> --help` — canonical CLI reference.
