Interactive subagents for pi — spawn, orchestrate, and manage sub-agent sessions in cmux terminals
TypeScript
719
173 commits
updated May 12, 2026
Async subagents for pi — spawn, orchestrate, and manage sub-agent sessions in multiplexer panes. Fully non-blocking — the main agent keeps working while subagents run in the background.
https://github.com/user-attachments/assets/30adb156-cfb4-4c47-84ca-dd4aa80cba9f
Call subagent() and it returns immediately. The sub-agent runs in its own terminal pane. A live widget above the input shows all running agents with their current state — starting, active, waiting, stalled, or running. When a sub-agent finishes, its result is steered back into the main session as an async notification — triggering a new turn so the agent can process it.
╭─ Subagents ──────────────────────────── 2 running ─╮
│ 00:23 Scout: Auth (scout) active · bash 7m │
│ 00:45 Scout: DB (scout) waiting 2m │
╰────────────────────────────────────────────────────╯
For parallel execution, just call subagent multiple times — they all run concurrently:
subagent({ name: "Scout: Auth", agent: "scout", task: "Analyze auth module" });
subagent({ name: "Scout: DB", agent: "scout", task: "Map database schema" });
// Both return immediately, results steer back independently
pi install git:github.com/HazAT/pi-interactive-subagents
Supported multiplexers:
Start pi inside one of them:
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTerm — no wrapper needed
Optional: set PI_SUBAGENT_MUX=cmux|tmux|zellij|wezterm to force a specific backend.
If your shell startup is slow and subagent commands sometimes get dropped before the prompt is ready, set PI_SUBAGENT_SHELL_READY_DELAY_MS to a higher value (defaults to 500):
export PI_SUBAGENT_SHELL_READY_DELAY_MS=2500
Subagent panes are created without stealing keyboard focus (cmux, tmux). Launch commands target child surfaces by explicit ID, so focus and command delivery are independent. Note: the interactive option controls parent status notifications, not terminal focus.
Subagents — 4 main-session tools + 3 commands, plus 1 subagent-only tool:
| Tool | Description |
|---|---|
subagent | Spawn a sub-agent in a dedicated multiplexer pane (async — returns immediately) |
subagent_interrupt | Interrupt a running Pi-backed subagent's current turn |
subagents_list | List available agent definitions |
subagent_resume | Resume a previous sub-agent session (async) |
| Command | Description |
|---|---|
/plan | Start a full planning workflow |
/iterate | Fork into a subagent for quick fixes |
/subagent <agent> <task> | Spawn a named agent directly |
| Agent | Model | Role |
|---|---|---|
| planner | Opus (medium thinking) | Brainstorming — clarifies requirements, explores approaches, writes plans, creates todos |
| scout | Haiku | Fast codebase reconnaissance — maps files, patterns, conventions |
| worker | Sonnet | Implements tasks from todos — writes code, runs tests, makes polished commits |
| reviewer | Opus (medium thinking) | Reviews code for bugs, security issues, correctness |
| visual-tester | Sonnet | Visual QA via Chrome CDP — screenshots, responsive testing, interaction testing |
Agent discovery follows priority: project-local (.pi/agents/) > global (~/.pi/agent/agents/) > package-bundled. Override any bundled agent by placing your own version in the higher-priority location.
1. Agent calls subagent() → returns immediately ("started")
2. Sub-agent runs in mux pane → widget shows live status
3. User keeps chatting → main session fully interactive
4. Sub-agent finishes → result steered back as a normal completion/failure
5. Main agent processes result → continues with new context
Multiple subagents run concurrently — each steers its result back independently as it finishes. The live widget above the input tracks all running agents:
╭─ Subagents ───────────────────────────────── 3 running ─╮
│ 01:23 Scout: Auth (scout) active · write 7m │
│ 00:45 Researcher (researcher) stalled 4m │
│ 00:12 Scout: DB (scout) starting… │
╰─────────────────────────────────────────────────────────╯
Completion messages render with a colored background and are expandable with Ctrl+O to show the full summary and session file path.
The widget tracks each Pi-backed sub-agent from a child-written runtime snapshot and labels it with a coarse state:
starting — launched, but no valid child snapshot has been observed yetactive — the child is doing observed runtime work: agent turn, provider request, streaming, or tool executionwaiting — the child finished a turn and is intentionally open for more input or another stagestalled — the parent has gone too long without a valid current child snapshot and can no longer trust the run is healthyrunning — fallback for backends without child snapshots (e.g. Claude)These labels are no longer derived from session-file growth. Session JSONL is still used for transcript, resume, lineage, and result extraction, but Pi-backed liveness now comes from a small activity snapshot written by the child extension. A fixed internal watchdog marks a run as stalled when valid snapshots never appear, stop being readable, or stop matching the current child; valid long-running active or waiting states do not become stalled just because time passes. When a run enters stalled or recovers from it, the parent agent receives a steer message so it can react. All other status transitions stay in the widget only.
Interactive subagents stay silent. Long-running user-driven subagents (e.g. planner, or any /iterate fork) do not wake the parent session on stalled/recovered transitions — the user is working directly in the subagent's pane, and a steer message there would just burn an orchestrator turn on a no-op "still waiting" ping. The widget still updates normally, and child snapshots are still recorded/classified regardless of the interactive setting. By default, agents with auto-exit: true are treated as autonomous and get stall pings; agents without it are treated as interactive and stay quiet. Override per-agent with interactive: true|false in frontmatter, or per-spawn with interactive: true|false on the tool call.
Status display is controlled by config.json in the extension directory. Copy config.json.example to get started:
cp config.json.example config.json
{
"status": {
"enabled": true
}
}
config.json is gitignored so local overrides don't get committed.
// Named agent with defaults from agent definition
subagent({ name: "Scout", agent: "scout", task: "Analyze the codebase..." });
// Force a full-context fork for this spawn
subagent({ name: "Iterate", fork: true, task: "Fix the bug where..." });
// Agent defaults can choose a different session-mode via frontmatter
subagent({ name: "Planner", agent: "planner", task: "Work through the design with me" });
// Custom working directory
subagent({ name: "Designer", agent: "game-designer", cwd: "agents/game-designer", task: "..." });
| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | required | Display name (shown in widget and pane title) |
task | string | required | Task prompt for the sub-agent |
agent | string | — | Load defaults from agent definition |
fork | boolean | false | Force the full-context fork mode for this spawn, overriding any agent session-mode frontmatter |
interactive | boolean | derived | Mark this spawn as interactive (don't wake the parent on stall/recovery). Defaults to the agent's interactive frontmatter, otherwise the inverse of auto-exit. |
model | string | — | Override agent's default model |
systemPrompt | string | — | Append to system prompt |
skills | string | — | Comma-separated skill names |
tools | string | — | Comma-separated tool names |
cwd | string | — | Working directory for the sub-agent (see Role Folders) |
Use subagent_interrupt to cancel the active turn of a running Pi-backed subagent:
subagent_interrupt({ id: "abcd1234" });
// or
subagent_interrupt({ name: "Scout" });
This sends Escape to the child pane, cancelling the in-progress model turn. The subagent session stays alive — the pane, session file, and background polling all remain intact. After the interrupt, the widget immediately moves the child back to waiting, and stale pre-interrupt snapshots are ignored. If the child starts work later, newer snapshots return it to active; completion, failure, and caller_ping still flow through normally.
This is a turn-level interrupt, not a method for forcibly terminating a subagent session.
Note: Only Pi-backed subagents are supported. Claude-backed runs will return an error.
The caller_ping tool lets a subagent request help from its parent agent. When called, the child session exits and the parent receives a notification with the help message. The parent can then resume the child session with a response using subagent_resume.
caller_ping parameters:
message (required): What you need help withsubagent_resume parameters:
sessionPath (required): Path to the child session .jsonl filename (optional): Display name for the resumed pane (defaults to Resume)message (optional): Follow-up prompt to send after resumingautoExit (optional): Whether the resumed session should auto-exit after its next response. Defaults to true for autonomous follow-up work; set false when resuming for an interactive handoff.Interaction flow:
caller_ping({ message: "Not sure which schema to use" })subagent_done)subagent_resume with the responseExample:
// Inside a worker subagent
await caller_ping({
message: "Found two conflicting migration files — should I use v1 or v2?"
});
// Session exits here. Parent receives the ping, then resumes this session
// with guidance like "Use v2, v1 is deprecated"
Note:
caller_pingis only available inside subagent contexts. Calling it from a standalone pi session returns an error.
/plan WorkflowThe /plan command orchestrates a full planning-to-implementation pipeline.
/plan Add a dark mode toggle to the settings page
Phase 1: Investigation → Quick codebase scan
Phase 2: Planning → Interactive planner subagent (user collaborates)
Phase 3: Review Plan → Confirm todos, adjust if needed
Phase 4: Execute → Scout + sequential workers implement todos
Phase 5: Review → Reviewer subagent checks all changes
Tab/window titles update to show current phase:
🔍 Investigating: dark mode → 💬 Planning: dark mode
→ 🔨 Executing: 1/3 → 🔎 Reviewing → ✅ Done
/iterate WorkflowFor quick, focused work without polluting the main session's context.
/iterate Fix the off-by-one error in the pagination logic
This always forks the current session into a subagent with full conversation context. It does not inherit an agent default session-mode. Make the fix, verify it, and exit to return. The main session gets a summary of what was done.
Place a .md file in .pi/agents/ (project) or ~/.pi/agent/agents/ (global):
---
name: my-agent
description: Does something specific
model: anthropic/claude-sonnet-4-6
thinking: minimal
tools: read, bash, edit, write
session-mode: lineage-only
spawning: false
---
# My Agent
You are a specialized agent that does X...
| Field | Type | Description |
|---|---|---|
name | string | Agent name (used in agent: "my-agent") |
description | string | Shown in subagents_list output |
model | string | Default model (e.g. anthropic/claude-sonnet-4-6) |
thinking | string | Thinking level: minimal, medium, high |
tools | string | Comma-separated native pi tools only: read, bash, edit, write, grep, find, ls |
skills | string | Comma-separated skill names to auto-load |
session-mode | string | Default child-session mode: standalone, lineage-only, or fork |
spawning | boolean | Set false to deny all subagent-spawning tools |
deny-tools | string | Comma-separated extension tool names to deny |
auto-exit | boolean | Auto-shutdown when the agent finishes its turn — no subagent_done call needed. If the user sends any input, auto-exit is permanently disabled and the user takes over the session. Recommended for autonomous agents (scout, worker); not for interactive ones (planner). Also determines the default value of interactive (see below). |
interactive | boolean | derived |
cwd | string | Default working directory (absolute or relative to project root) |
disable-model-invocation | boolean | Hide this agent from discovery surfaces like subagents_list. The agent still remains directly invokable by explicit name via subagent({ agent: "name", ... }). |
Discovery still resolves precedence before visibility filtering. If a project-local hidden agent has the same name as a visible global or bundled agent, the hidden project agent wins and the lower-precedence agent does not appear in subagents_list.
session-modeChoose how a subagent session starts:
standalone — default fresh session with no lineage link to the callerlineage-only — fresh blank child session with parentSession linkage, but no copied turns from the callerfork — linked child session seeded with the caller's prior conversation contextlineage-only is useful when you want session discovery and fork lineage UX to show the relationship later, but you do not want the child to inherit the parent's turns.
fork: true on the tool call always forces the fork mode for that specific spawn. /iterate uses this explicit override on purpose.
---
name: planner
session-mode: lineage-only
---
auto-exitWhen set to true, the agent session shuts down automatically as soon as the agent finishes its turn — no explicit subagent_done call is needed.
Behavior:
agent_end event)subagent_doneWhen to use:
---
name: scout
auto-exit: true
---
interactiveControls whether status transitions (stalled, recovered) wake the parent session with a steer message.
Default: the inverse of auto-exit. Autonomous agents (auto-exit: true) are non-interactive and ping the parent on stall/recovery; agents without auto-exit are interactive and stay quiet. Bare spawns with no agent defs (e.g. /iterate with fork: true) are treated as interactive.
Why it exists: Interactive agents can run for minutes or hours while the user thinks, types, and reads in the subagent's pane. Child snapshots still update the widget, but stalled/recovered supervision messages rarely need to wake the parent for user-driven sessions. Skipping the steer keeps the parent quiet until the child actually finishes.
When to override:
interactive: false on an agent that doesn't auto-exit but you still want stall pings forinteractive: true on an autonomous agent you'd rather check on yourself---
name: planner
# interactive defaults to true because auto-exit is not set
---
Or per spawn:
subagent({ name: "Scout", agent: "scout", interactive: true, task: "..." });
By default, every sub-agent can spawn further sub-agents. Control this with frontmatter:
spawning: falseDenies all subagent lifecycle tools (subagent, subagent_interrupt, subagents_list, subagent_resume):
---
name: worker
spawning: false
---
deny-toolsFine-grained control over individual extension tools:
---
name: focused-agent
deny-tools: subagent
---
| Agent | spawning | Rationale |
|---|---|---|
| planner | (default) | Legitimately spawns scouts for investigation |
| worker | false | Should implement tasks, not delegate |
| researcher | false | Should research, not spawn |
| reviewer | false | Should review, not spawn |
| scout | false | Should gather context, not spawn |
The cwd parameter lets sub-agents start in a specific directory with its own configuration:
project/
├── agents/
│ ├── game-designer/
│ │ └── CLAUDE.md ← "You are a game designer..."
│ ├── sre/
│ │ ├── CLAUDE.md ← "You are an SRE specialist..."
│ │ └── .pi/skills/ ← SRE-specific skills
│ └── narrative/
│ └── CLAUDE.md ← "You are a narrative designer..."
subagent({ name: "Game Designer", cwd: "agents/game-designer", task: "Design the combat system" });
subagent({ name: "SRE", cwd: "agents/sre", task: "Review deployment pipeline" });
Set a default cwd in agent frontmatter:
---
name: game-designer
cwd: ./agents/game-designer
spawning: false
---
Every sub-agent session displays a compact tools widget showing available and denied tools. Toggle with Ctrl+J:
[scout] — 12 tools · 4 denied (Ctrl+J) ← collapsed
[scout] — 12 available (Ctrl+J to collapse) ← expanded
read, bash, edit, write, todo, ...
denied: subagent, subagents_list, ...
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTerm
Optional backend override:
export PI_SUBAGENT_MUX=cmux # or tmux, zellij, wezterm
The sub-agent status supervision and turn-only interruption features were inspired by RepoPrompt's sub-agent snapshot polling and run cancellation features.
MIT
170 followers · starred Apr 2026
167 followers · starred Mar 2026
180 followers · starred Mar 2026
115 followers · starred Mar 2026
Interactive subagents for pi — spawn, orchestrate, and manage sub-agent sessions in cmux terminals
TypeScript
719
173 commits
updated May 12, 2026
Async subagents for pi — spawn, orchestrate, and manage sub-agent sessions in multiplexer panes. Fully non-blocking — the main agent keeps working while subagents run in the background.
https://github.com/user-attachments/assets/30adb156-cfb4-4c47-84ca-dd4aa80cba9f
Call subagent() and it returns immediately. The sub-agent runs in its own terminal pane. A live widget above the input shows all running agents with their current state — starting, active, waiting, stalled, or running. When a sub-agent finishes, its result is steered back into the main session as an async notification — triggering a new turn so the agent can process it.
╭─ Subagents ──────────────────────────── 2 running ─╮
│ 00:23 Scout: Auth (scout) active · bash 7m │
│ 00:45 Scout: DB (scout) waiting 2m │
╰────────────────────────────────────────────────────╯
For parallel execution, just call subagent multiple times — they all run concurrently:
subagent({ name: "Scout: Auth", agent: "scout", task: "Analyze auth module" });
subagent({ name: "Scout: DB", agent: "scout", task: "Map database schema" });
// Both return immediately, results steer back independently
pi install git:github.com/HazAT/pi-interactive-subagents
Supported multiplexers:
Start pi inside one of them:
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTerm — no wrapper needed
Optional: set PI_SUBAGENT_MUX=cmux|tmux|zellij|wezterm to force a specific backend.
If your shell startup is slow and subagent commands sometimes get dropped before the prompt is ready, set PI_SUBAGENT_SHELL_READY_DELAY_MS to a higher value (defaults to 500):
export PI_SUBAGENT_SHELL_READY_DELAY_MS=2500
Subagent panes are created without stealing keyboard focus (cmux, tmux). Launch commands target child surfaces by explicit ID, so focus and command delivery are independent. Note: the interactive option controls parent status notifications, not terminal focus.
Subagents — 4 main-session tools + 3 commands, plus 1 subagent-only tool:
| Tool | Description |
|---|---|
subagent | Spawn a sub-agent in a dedicated multiplexer pane (async — returns immediately) |
subagent_interrupt | Interrupt a running Pi-backed subagent's current turn |
subagents_list | List available agent definitions |
subagent_resume | Resume a previous sub-agent session (async) |
| Command | Description |
|---|---|
/plan | Start a full planning workflow |
/iterate | Fork into a subagent for quick fixes |
/subagent <agent> <task> | Spawn a named agent directly |
| Agent | Model | Role |
|---|---|---|
| planner | Opus (medium thinking) | Brainstorming — clarifies requirements, explores approaches, writes plans, creates todos |
| scout | Haiku | Fast codebase reconnaissance — maps files, patterns, conventions |
| worker | Sonnet | Implements tasks from todos — writes code, runs tests, makes polished commits |
| reviewer | Opus (medium thinking) | Reviews code for bugs, security issues, correctness |
| visual-tester | Sonnet | Visual QA via Chrome CDP — screenshots, responsive testing, interaction testing |
Agent discovery follows priority: project-local (.pi/agents/) > global (~/.pi/agent/agents/) > package-bundled. Override any bundled agent by placing your own version in the higher-priority location.
1. Agent calls subagent() → returns immediately ("started")
2. Sub-agent runs in mux pane → widget shows live status
3. User keeps chatting → main session fully interactive
4. Sub-agent finishes → result steered back as a normal completion/failure
5. Main agent processes result → continues with new context
Multiple subagents run concurrently — each steers its result back independently as it finishes. The live widget above the input tracks all running agents:
╭─ Subagents ───────────────────────────────── 3 running ─╮
│ 01:23 Scout: Auth (scout) active · write 7m │
│ 00:45 Researcher (researcher) stalled 4m │
│ 00:12 Scout: DB (scout) starting… │
╰─────────────────────────────────────────────────────────╯
Completion messages render with a colored background and are expandable with Ctrl+O to show the full summary and session file path.
The widget tracks each Pi-backed sub-agent from a child-written runtime snapshot and labels it with a coarse state:
starting — launched, but no valid child snapshot has been observed yetactive — the child is doing observed runtime work: agent turn, provider request, streaming, or tool executionwaiting — the child finished a turn and is intentionally open for more input or another stagestalled — the parent has gone too long without a valid current child snapshot and can no longer trust the run is healthyrunning — fallback for backends without child snapshots (e.g. Claude)These labels are no longer derived from session-file growth. Session JSONL is still used for transcript, resume, lineage, and result extraction, but Pi-backed liveness now comes from a small activity snapshot written by the child extension. A fixed internal watchdog marks a run as stalled when valid snapshots never appear, stop being readable, or stop matching the current child; valid long-running active or waiting states do not become stalled just because time passes. When a run enters stalled or recovers from it, the parent agent receives a steer message so it can react. All other status transitions stay in the widget only.
Interactive subagents stay silent. Long-running user-driven subagents (e.g. planner, or any /iterate fork) do not wake the parent session on stalled/recovered transitions — the user is working directly in the subagent's pane, and a steer message there would just burn an orchestrator turn on a no-op "still waiting" ping. The widget still updates normally, and child snapshots are still recorded/classified regardless of the interactive setting. By default, agents with auto-exit: true are treated as autonomous and get stall pings; agents without it are treated as interactive and stay quiet. Override per-agent with interactive: true|false in frontmatter, or per-spawn with interactive: true|false on the tool call.
Status display is controlled by config.json in the extension directory. Copy config.json.example to get started:
cp config.json.example config.json
{
"status": {
"enabled": true
}
}
config.json is gitignored so local overrides don't get committed.
// Named agent with defaults from agent definition
subagent({ name: "Scout", agent: "scout", task: "Analyze the codebase..." });
// Force a full-context fork for this spawn
subagent({ name: "Iterate", fork: true, task: "Fix the bug where..." });
// Agent defaults can choose a different session-mode via frontmatter
subagent({ name: "Planner", agent: "planner", task: "Work through the design with me" });
// Custom working directory
subagent({ name: "Designer", agent: "game-designer", cwd: "agents/game-designer", task: "..." });
| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | required | Display name (shown in widget and pane title) |
task | string | required | Task prompt for the sub-agent |
agent | string | — | Load defaults from agent definition |
fork | boolean | false | Force the full-context fork mode for this spawn, overriding any agent session-mode frontmatter |
interactive | boolean | derived | Mark this spawn as interactive (don't wake the parent on stall/recovery). Defaults to the agent's interactive frontmatter, otherwise the inverse of auto-exit. |
model | string | — | Override agent's default model |
systemPrompt | string | — | Append to system prompt |
skills | string | — | Comma-separated skill names |
tools | string | — | Comma-separated tool names |
cwd | string | — | Working directory for the sub-agent (see Role Folders) |
Use subagent_interrupt to cancel the active turn of a running Pi-backed subagent:
subagent_interrupt({ id: "abcd1234" });
// or
subagent_interrupt({ name: "Scout" });
This sends Escape to the child pane, cancelling the in-progress model turn. The subagent session stays alive — the pane, session file, and background polling all remain intact. After the interrupt, the widget immediately moves the child back to waiting, and stale pre-interrupt snapshots are ignored. If the child starts work later, newer snapshots return it to active; completion, failure, and caller_ping still flow through normally.
This is a turn-level interrupt, not a method for forcibly terminating a subagent session.
Note: Only Pi-backed subagents are supported. Claude-backed runs will return an error.
The caller_ping tool lets a subagent request help from its parent agent. When called, the child session exits and the parent receives a notification with the help message. The parent can then resume the child session with a response using subagent_resume.
caller_ping parameters:
message (required): What you need help withsubagent_resume parameters:
sessionPath (required): Path to the child session .jsonl filename (optional): Display name for the resumed pane (defaults to Resume)message (optional): Follow-up prompt to send after resumingautoExit (optional): Whether the resumed session should auto-exit after its next response. Defaults to true for autonomous follow-up work; set false when resuming for an interactive handoff.Interaction flow:
caller_ping({ message: "Not sure which schema to use" })subagent_done)subagent_resume with the responseExample:
// Inside a worker subagent
await caller_ping({
message: "Found two conflicting migration files — should I use v1 or v2?"
});
// Session exits here. Parent receives the ping, then resumes this session
// with guidance like "Use v2, v1 is deprecated"
Note:
caller_pingis only available inside subagent contexts. Calling it from a standalone pi session returns an error.
/plan WorkflowThe /plan command orchestrates a full planning-to-implementation pipeline.
/plan Add a dark mode toggle to the settings page
Phase 1: Investigation → Quick codebase scan
Phase 2: Planning → Interactive planner subagent (user collaborates)
Phase 3: Review Plan → Confirm todos, adjust if needed
Phase 4: Execute → Scout + sequential workers implement todos
Phase 5: Review → Reviewer subagent checks all changes
Tab/window titles update to show current phase:
🔍 Investigating: dark mode → 💬 Planning: dark mode
→ 🔨 Executing: 1/3 → 🔎 Reviewing → ✅ Done
/iterate WorkflowFor quick, focused work without polluting the main session's context.
/iterate Fix the off-by-one error in the pagination logic
This always forks the current session into a subagent with full conversation context. It does not inherit an agent default session-mode. Make the fix, verify it, and exit to return. The main session gets a summary of what was done.
Place a .md file in .pi/agents/ (project) or ~/.pi/agent/agents/ (global):
---
name: my-agent
description: Does something specific
model: anthropic/claude-sonnet-4-6
thinking: minimal
tools: read, bash, edit, write
session-mode: lineage-only
spawning: false
---
# My Agent
You are a specialized agent that does X...
| Field | Type | Description |
|---|---|---|
name | string | Agent name (used in agent: "my-agent") |
description | string | Shown in subagents_list output |
model | string | Default model (e.g. anthropic/claude-sonnet-4-6) |
thinking | string | Thinking level: minimal, medium, high |
tools | string | Comma-separated native pi tools only: read, bash, edit, write, grep, find, ls |
skills | string | Comma-separated skill names to auto-load |
session-mode | string | Default child-session mode: standalone, lineage-only, or fork |
spawning | boolean | Set false to deny all subagent-spawning tools |
deny-tools | string | Comma-separated extension tool names to deny |
auto-exit | boolean | Auto-shutdown when the agent finishes its turn — no subagent_done call needed. If the user sends any input, auto-exit is permanently disabled and the user takes over the session. Recommended for autonomous agents (scout, worker); not for interactive ones (planner). Also determines the default value of interactive (see below). |
interactive | boolean | derived |
cwd | string | Default working directory (absolute or relative to project root) |
disable-model-invocation | boolean | Hide this agent from discovery surfaces like subagents_list. The agent still remains directly invokable by explicit name via subagent({ agent: "name", ... }). |
Discovery still resolves precedence before visibility filtering. If a project-local hidden agent has the same name as a visible global or bundled agent, the hidden project agent wins and the lower-precedence agent does not appear in subagents_list.
session-modeChoose how a subagent session starts:
standalone — default fresh session with no lineage link to the callerlineage-only — fresh blank child session with parentSession linkage, but no copied turns from the callerfork — linked child session seeded with the caller's prior conversation contextlineage-only is useful when you want session discovery and fork lineage UX to show the relationship later, but you do not want the child to inherit the parent's turns.
fork: true on the tool call always forces the fork mode for that specific spawn. /iterate uses this explicit override on purpose.
---
name: planner
session-mode: lineage-only
---
auto-exitWhen set to true, the agent session shuts down automatically as soon as the agent finishes its turn — no explicit subagent_done call is needed.
Behavior:
agent_end event)subagent_doneWhen to use:
---
name: scout
auto-exit: true
---
interactiveControls whether status transitions (stalled, recovered) wake the parent session with a steer message.
Default: the inverse of auto-exit. Autonomous agents (auto-exit: true) are non-interactive and ping the parent on stall/recovery; agents without auto-exit are interactive and stay quiet. Bare spawns with no agent defs (e.g. /iterate with fork: true) are treated as interactive.
Why it exists: Interactive agents can run for minutes or hours while the user thinks, types, and reads in the subagent's pane. Child snapshots still update the widget, but stalled/recovered supervision messages rarely need to wake the parent for user-driven sessions. Skipping the steer keeps the parent quiet until the child actually finishes.
When to override:
interactive: false on an agent that doesn't auto-exit but you still want stall pings forinteractive: true on an autonomous agent you'd rather check on yourself---
name: planner
# interactive defaults to true because auto-exit is not set
---
Or per spawn:
subagent({ name: "Scout", agent: "scout", interactive: true, task: "..." });
By default, every sub-agent can spawn further sub-agents. Control this with frontmatter:
spawning: falseDenies all subagent lifecycle tools (subagent, subagent_interrupt, subagents_list, subagent_resume):
---
name: worker
spawning: false
---
deny-toolsFine-grained control over individual extension tools:
---
name: focused-agent
deny-tools: subagent
---
| Agent | spawning | Rationale |
|---|---|---|
| planner | (default) | Legitimately spawns scouts for investigation |
| worker | false | Should implement tasks, not delegate |
| researcher | false | Should research, not spawn |
| reviewer | false | Should review, not spawn |
| scout | false | Should gather context, not spawn |
The cwd parameter lets sub-agents start in a specific directory with its own configuration:
project/
├── agents/
│ ├── game-designer/
│ │ └── CLAUDE.md ← "You are a game designer..."
│ ├── sre/
│ │ ├── CLAUDE.md ← "You are an SRE specialist..."
│ │ └── .pi/skills/ ← SRE-specific skills
│ └── narrative/
│ └── CLAUDE.md ← "You are a narrative designer..."
subagent({ name: "Game Designer", cwd: "agents/game-designer", task: "Design the combat system" });
subagent({ name: "SRE", cwd: "agents/sre", task: "Review deployment pipeline" });
Set a default cwd in agent frontmatter:
---
name: game-designer
cwd: ./agents/game-designer
spawning: false
---
Every sub-agent session displays a compact tools widget showing available and denied tools. Toggle with Ctrl+J:
[scout] — 12 tools · 4 denied (Ctrl+J) ← collapsed
[scout] — 12 available (Ctrl+J to collapse) ← expanded
read, bash, edit, write, todo, ...
denied: subagent, subagents_list, ...
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTerm
Optional backend override:
export PI_SUBAGENT_MUX=cmux # or tmux, zellij, wezterm
The sub-agent status supervision and turn-only interruption features were inspired by RepoPrompt's sub-agent snapshot polling and run cancellation features.
MIT
170 followers · starred Apr 2026
167 followers · starred Mar 2026
180 followers · starred Mar 2026
115 followers · starred Mar 2026