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
|
---
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.
- Without the `alarm` tool (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.
|