cmux pi subagent
Delegate a task to a separate pi process that runs in its own visible cmux pane, driven over pi's RPC (JSONL) protocol. The lifecycle is fully managed:
- DEPLOY — create a new cmux split surface (a visible pane), without stealing focus
- READY — wait until the pane's shell actually executes commands (robust against fish + direnv + dropped keystrokes)
- EXECUTE — run
pi --mode rpcin the pane, send the task, stream the work live in the pane - COLLECT — read the subagent's final answer from a result file
- REMOVE — close the pane (no leftover panes)
This replaces the pi-interactive-subagents package for the deploy/execute/remove
use case. Results are captured via files (not screen-scraping), so they are reliable.
Install location
Move this whole directory to pi's user-level skills folder so pi auto-discovers it on every launch (no settings.json change needed):
~/.pi/agent/skills/cmux-pi-subagent/
├── SKILL.md
├── orchestrate.py
├── launch.sh
└── runner.py
(Per pi docs/skills.md, any directory containing a SKILL.md under
~/.pi/agent/skills/ is discovered recursively at startup.)
Preconditions
This skill only works when the current pi session is running inside a cmux surface. Verify before use:
echo "socket=$CMUX_SOCKET_PATH surface=$CMUX_SURFACE_ID"
Both must be non-empty. If they are empty, you are not in cmux and this skill cannot run.
Usage
Run the orchestrator with the task as a single argument. It blocks until the
subagent finishes, then prints the answer between ===== SUBAGENT ANSWER =====
markers and closes the pane automatically.
python3 ~/.pi/agent/skills/cmux-pi-subagent/orchestrate.py \
--name "scout" \
--model claude-haiku-4-5 \
"Use your tools to inspect this repo and report the project name and Swift file count."
For a long task prompt, write it to a file and pass --task-file instead of an
inline argument:
python3 ~/.pi/agent/skills/cmux-pi-subagent/orchestrate.py \
--name "implementer" --task-file /tmp/my-task.md
Options
| Flag | Default | Meaning |
|------|---------|---------|
| task (positional) | — | The task text. Omit if using --task-file. |
| --task-file PATH | — | Read the task from a file (use for long/multiline prompts). |
| --name NAME | pi-subagent | Tab title for the spawned pane. |
| --model MODEL | pi default | Model for the subagent, e.g. claude-haiku-4-5, claude-sonnet-4-6. |
| --cwd PATH | current dir | Working directory the subagent runs in. |
| --direction DIR | right | Split direction: left / right / up / down. |
| --timeout SECS | 600 | Max seconds to wait for the subagent. |
| --keep-open | off | Leave the pane open after finishing (for debugging). |
How it works (internals)
orchestrate.pyis the controller (runs in the current session). It creates the split withcmux new-split <dir> --surface $CMUX_SURFACE_ID --focus false, writes per-run data/config files, launches the checked-inlaunch.shinside the pane viacmux respawn-pane --command, polls for durable markers, readsresult.txt, then closes the pane if cmux has not already removed it.launch.shis stable checked-in code, not generated by the LLM. It reads the per-run config, restores the orchestrator PATH, touchesrunner-started, runsrunner.py, and records the runner exit code.runner.pyruns inside the pane. It spawnspi --mode rpc --no-session, sends the task as oneprompt, streams assistant text + tool activity to the pane so the work is visible, writes heartbeat/error markers, and onagent_endwrites the final assistant text to the result file (atomic write) followed by the done marker.- Per-run scratch files live in
/tmp/cmux-pi-subagent/<run-id>/.
Notes & limits
- The subagent is ephemeral (
--no-session); it does one task and exits. - One subagent at a time per
orchestrate.pyinvocation (it blocks). For parallel fan-out, launch multiple panes (ask to extend the orchestrator for concurrency). - The pane is created without stealing focus, so you can keep working.
- If something goes wrong mid-run, rerun with
--keep-opento inspect the pane.