skills/pi-subagent/SKILL.md (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
---
name: pi-subagent
description: >-
Delegates work to one or more headless `pi` subagents on any provider/model,
run in the background, and collects each one's final answer plus its actual
billed cost. Covers a single delegated task, the same task fanned out to
several models for independent answers, or a job split into separate tasks
run in parallel, followed by verifying what came back; a run saved as a
session can be followed up, resumed after a timeout, or forked. Use when
the user asks for subagents, to hand something to another model, to get several
models' takes, or to parallelize work across pi runs — and when another
skill (such as pi-review-panel) builds on it. Spawning costs money: never
start subagents on your own initiative without the user asking.
---
# pi subagents
Run `pi` headless as a subagent: a fresh process, a written brief, no memory of
this conversation, one final answer. You stay the orchestrator. Subagents
produce raw material; you check it before it reaches the user.
The fiddly parts (hang avoidance, output extraction, cost accounting, keeping
failed attempts) are solved in `run-agent.sh` next to this file. Use it. Don't
hand-roll `pi -p` invocations — the flag set and the redirection shape are
load-bearing, and the failure modes are silent hangs. The reasons are at the
top of the script. `$SK` below is this skill's directory.
## 0. Shape the job
Pick one of three shapes, and say which to the user if it isn't obvious:
| shape | what | when |
|---|---|---|
| **delegate** | one task, one model | a self-contained job worth running elsewhere: a long investigation, a second opinion from a specific model, work you want off your own context |
| **fan-out** | one brief, N models | you want independent answers to compare: reviews, designs, hard questions. Disagreement is the signal |
| **split** | N briefs, one model each | a job that divides cleanly into parts that don't depend on each other's results |
A subagent knows nothing you don't write down, can't ask you anything, and
can't see your other subagents. If the parts of a split need each other's
output, it's a sequence, not a split — run it in stages.
**Models.** Use exactly the provider/model pairs the user names; don't add
others. If they named none, ask — or, for a fan-out, offer the default panel in
the pi-review-panel skill. Check a name resolves first:
`pi --offline --list-models <search>`. Credentials are already configured in
`pi`.
## 1. Set up a workdir
```bash
P="$(mktemp -d -t pi-subagent)"; mkdir -p "$P/out"
```
`$P` is scratch. One workdir per job; several briefs can live in it
(`$P/brief-<part>.md`), and every run writes `$P/out/<name>.*`.
**Snapshot the inputs.** If the subagents will read files someone else may be
editing — including you — record their content hash now and re-check before you
use the answers. Input that changes mid-run invalidates parts of their
reasoning, and you need to know that happened.
## 2. Write the brief
The brief is the whole interface. Write it as if for a capable contractor who
has never seen the project and will not get to ask questions:
- **The task, first sentence.** What you actually want. "Find bugs in this
diff" and "propose a better design than this one" produce very different
work; don't leave it to be guessed.
- **Context.** One paragraph on the project (from its README) and on why this
task exists.
- **The material, inline** in fenced blocks, even if you also give paths — the
subagent should not have to go looking for the thing it's about.
- **Absolute paths** of relevant files and directories, so it can read the
surroundings with its tools.
- **Criteria**, as hard requirements. If the user gave criteria, quote them
verbatim.
- **Evidence rules.** Verify empirically before asserting, and say which
command, interpreter, or version produced each claim. Distinguish what was
run from what was reasoned.
- **What it may touch** — see §3 — stated plainly. `pi` has edit and write
tools and will use them unless told, or prevented.
- **The deliverable**: format, sections, length. End with a **fixed final
line** where answers will be compared — `VERDICT: SHIP` /
`VERDICT: DO NOT SHIP`, `ANSWER: <x>`, `STATUS: DONE` /
`STATUS: BLOCKED — <why>`. Fixed strings make the tally and the success check
mechanical.
- **No questions, no follow-ups.** Deliver the full result as the final answer;
don't ask for confirmation, don't offer further work.
## 3. Decide what it may touch
Subagents share this machine and its filesystem. Constrain them in this order
of preference:
1. **Read-only by construction**: `--tools read,grep,find,ls`. No shell, so it
can't run or change anything. Right for pure reading and reasoning.
2. **Read-only by instruction**: default tools (bash included), with the brief
saying everything is read-only. Needed when the subagent must run things to
verify claims — tests, interpreters, measurements. Tell it to put scratch
files under its own `$P/scratch-<name>/` and nowhere else.
3. **Writes, isolated**: give each writing subagent its own git worktree and
pass it as `--cwd`; never let two writers share a tree, and never point one
at the user's working copy. Worktrees go under
`~/x/worktrees/<repo>/<name>`. You review and bring over the result; the
subagent doesn't commit, push or post anything — say so in the brief.
Context files (AGENTS.md/CLAUDE.md) are off by default, so the brief is the
only instruction a subagent gets. Pass `--context-files` when it works inside
a repo whose conventions it must follow.
## 4. Launch
Always in the background, so you can keep working:
```bash
"$SK/run-agent.sh" "$P" <provider> "<model>" <name> > "$P/out/_<name>.log" 2>&1 &
```
Options go before the positionals: `--brief FILE` (default `$P/brief.md`),
`--timeout SECS` (default 1800), `--thinking LEVEL` (default `high`),
`--tools LIST`, `--cwd DIR`, `--context-files`, `--deliver TEXT`, and
`--session` to keep the conversation for a follow-up (§10). Names must be
unique within a workdir; a natural scheme is `<provider-abbrev>-<model>` for a
fan-out and `<part>` for a split.
A fan-out of one brief to three models:
```bash
"$SK/run-agent.sh" "$P" fireworks "accounts/fireworks/models/kimi-k3" fw-kimi-k3 > "$P/out/_fw-kimi-k3.log" 2>&1 &
"$SK/run-agent.sh" "$P" openrouter "z-ai/glm-5.3" or-glm-5.3 > "$P/out/_or-glm-5.3.log" 2>&1 &
"$SK/run-agent.sh" "$P" openrouter "google/gemini-3.7-flash" or-gemini > "$P/out/_or-gemini.log" 2>&1 &
```
A split, each part with its own brief:
```bash
for part in parser cli docs; do
"$SK/run-agent.sh" --brief "$P/brief-$part.md" "$P" openrouter "z-ai/glm-5.3" "$part" \
> "$P/out/_$part.log" 2>&1 &
done
```
## 5. Monitor — and work meanwhile
Expect minutes to tens of minutes per run. Check progress with:
```bash
"$SK/status.sh" "$P" # once: running names, then the table so far
"$SK/status.sh" --wait 60 "$P" # block until all are done (exit 0)
```
`status.sh` exits 3 while anything is still running, and also prints free disk
space — the event streams are big while runs are in flight.
**Get woken instead of waiting.** In an interactive pi, the `alarm` tool can
fire when processes exit. Launch, set one alarm on all the runs' PIDs, and end
your turn: when the last run exits, the alarm starts a new turn, or arrives
after the tool call in progress if you're busy by then. For the PIDs, print
`$!` after each launch in the same bash call, or read `"$P"/out/*.running`,
which holds each run's PID while it's in flight.
```
alarm action=set when_exits=[<pids>] in_minutes=<the --timeout, in minutes, + 5>
note="pi-subagent runs <names> in <$P> have ended. Run <$SK>/status.sh <$P>,
handle failures (§6), verify (§7), report with the cost table (§8)."
```
- Write the note with absolute paths. By the time it fires, this context may
have been compacted away.
- One alarm per PID wakes you as each run ends instead. That's worth it for a
long split whose parts you can check as they come; for a fan-out, one
alarm for all is the point.
- The alarm sees no exit status. `status.sh` has it.
- The deadline is a backstop, in case a run hangs past its own timeout.
- In Claude Code, which has no `alarm` but wakes you when a background Bash
task exits: launch the whole job as one `run_in_background` command that
starts the runs with `&` and ends with `wait`, and end your turn.
- Without either (pi in print or RPC mode), monitor with `status.sh`.
**Don't sit on the wait loop.** With an alarm set, there's nothing to wait
for. Otherwise, launching early is only worth it if you use the time: do your
own investigation of the same question, build whatever harness you'll need to
check the answers, establish baselines. You need your own
evidence in §7 regardless. When you do block, give the shell call a timeout
and loop, rather than one unbounded wait.
## 6. Handle failures
A run succeeded if `$P/out/<name>.md` is non-empty and (when you asked for one)
ends with the fixed final line. Exit 124 is the timeout firing. `<name>.err`
and `<name>.jsonl` say what happened otherwise.
Retry a failure once, under the same name: `run-agent.sh` moves the failed
run's files to `<name>.attempt<N>.*` first, cost included. A run started with
`--session` that timed out after real progress is often better continued than
retried: `--followup` it (§10), and it picks up with everything it had read
and run. If it fails twice, report it with its cost and carry on — one dead
provider must not sink the job.
For a split, a failed part is a gap you either fill yourself or report.
## 7. Verify, then use
What comes back is raw material, not results:
- **Reproduce every substantive claim** before passing it on. Models
hallucinate line numbers, APIs, file contents and measurements, with
confidence.
- **Disagreement is where the answer is.** When answers conflict, run the
experiment that decides it and say who was right. With sessions (§10) you
can also put each side's claim to the other first — but a subagent
conceding or holding firm is not evidence either; the experiment is.
- **Ask for the evidence.** With a session, a vague or surprising claim can be
followed up: "show the exact command and output behind X".
- **Consensus is weak evidence.** Several models agreeing can all be making the
same mistake, or testing the same wrong version. One claim you confirmed
outranks four you didn't.
- **Check what was actually tested.** Watch for a claim true of a variant but
not of the thing written — a subagent "verifying" code it silently fixed
first.
- **Distrust your own harness too.** A test extracted to run standalone can
fail for reasons of its own (a missing import, a swallowed exception) that
look exactly like the bug under discussion. Check the baseline passes what it
should.
- **Written work**, from a worktree: read the whole diff and run the tests
yourself before bringing any of it over. Never merge it unseen.
Say what you dropped and why, alongside what you kept — that's what makes the
kept part trustworthy.
## 8. Report cost
Every report that used subagents ends with the cost table; the user should
never have to ask. `status.sh` prints it: per run, from the provider's own
per-message `usage.cost` — billed figures, not estimates — failed attempts as
their own rows, then the total. A timed-out run bills for every token it
generated and can be the most expensive line. When cost per useful result
varies a lot across models, say so: it informs who to use next time.
## 9. Keep what matters
`$P` won't survive the session. If the answers matter, copy the `.md` outputs,
the cost files, and any verification scripts somewhere durable in the project,
and link those instead of `$P`.
## 10. Follow up, resume, fork
Off by default: a run saves nothing, and once it ends it can't be talked to
again. Pass `--session` at launch when you expect to come back to it — a long
run that may time out, a fan-out whose disagreements you'll want to put back
to the subagents, anything you may need to question. It costs nothing extra
until used. The session is kept as `$P/sessions/<timestamp>_<name>.jsonl`.
Then, once that run has finished:
```bash
# Continue the same conversation. Provider and model may differ from before.
"$SK/run-agent.sh" --followup "<message>" "$P" <provider> "<model>" <name>
# Branch it into a new name; the original session is left as it was.
"$SK/run-agent.sh" --fork <name> --followup "<message>" "$P" <provider> "<model>" <new-name>
```
- **Uses.** Continue a run that timed out ("You were cut off. Carry on from
where you stopped and deliver the full result"). Ask for the evidence behind
a claim. Put another subagent's contradicting finding to it ("Another
reviewer says X, because Y. Check it and say who is right, with evidence").
Fork to ask several different questions of the same context without them
seeing each other, or to hand a whole investigation to a different model —
it reads the full history, whoever produced it.
- **The message is sent as written.** No brief, no `--deliver` wrapper, so say
what to deliver and end with the same fixed final line if you'll tally it.
- **What carries over.** A follow-up or fork inherits `--tools`, `--thinking`,
`--cwd` and `--context-files` from the run it continues, so a read-only
subagent stays read-only; pass one again to change it.
- **Outputs.** The earlier round's files move to `<name>.round<N>.*`, so every
answer is kept and every round is its own row in the cost table. Each
follow-up resends the whole conversation as input: a short question to a long
session is not cheap.
- **Limits.** A run killed mid-step loses that step; everything before it is
in the session. `run-agent.sh` refuses to follow up or fork a run still in
flight. A fresh retry under the same name (no `--followup`) starts a new
conversation and moves the old session aside.
- **Don't hand-roll the resume.** By id, pi looks only among sessions started
in the current directory, and where it finds none it silently starts a new,
empty one under that id — exit 0, and an answer from a model that remembers
nothing. `run-agent.sh` opens the session by its file path instead.
|