In this example the subagent extension restricts visibility to four tools,
and the permission system then applies `ask`/`allow` policy within that
visible set. Both keys coexist without collision.

### Checking the integration

Run `/subagents-doctor` to check the permission system status.
If `ask` prompts from children are not reaching the parent UI, verify both
extensions are installed:

```bash
pi list
```

### How it works

At session start, the interactive (root) session records its own identity in
`PI_SUBAGENT_PARENT_SESSION`. When pi-subagents launches a child, it passes the
launching session's identity to that child explicitly, falling back to the
inherited environment variable. When the permission system inside a child
encounters an `ask` permission, it reads this variable to locate the parent
session and forwards the confirmation request there.

This resolves an interactive prompt only when the parent it points at is the
interactive session — i.e. for the direct children of the root session. A
nested child's parent is itself a headless subagent process with no UI to
surface the prompt, so `ask` policies are best placed on agents that run as
direct children of the interactive session.

## Direct commands

Skip this section until you want exact syntax.

| Command | Description |
|---------|-------------|
| `/run <agent> [task]` | Run one agent; omit the task for self-contained agents |
| `/chain agent1 "task1" -> agent2 "task2"` | Run agents in sequence |
| `/chain scout "scan" -> (reviewer "A" \| reviewer "B") -> writer "fix"` | Run a chain with a static parallel group inline |
| `/parallel agent1 "task1" -> agent2 "task2"` | Run agents in parallel |
| `/run-chain <chainName> -- <task>` | Launch a saved `.chain.md` or `.chain.json` workflow |
| `/subagent-cost` | Show parent plus child subagent token usage and cost for this session |
| `/subagents [agent] [model\|thinking\|prompt\|details]` | Interactively inspect or edit an agent's model, thinking level, or system prompt |
| `/subagents-doctor` | Show read-only setup diagnostics |
| `/subagents-models [agent]` | Show the runtime-loaded builtin model mapping, optionally filtered to one builtin |
| `/subagents-watchdog [status|on|off|recommend-model|model ...|session model ...|check]` | Show or configure the opt-in watchdog; use a strong complementary model such as Opus 4.8 high or GPT 5.5 high |
| `/subagents-profiles` | List saved subagent profiles from `~/.pi/agent/profiles/pi-subagents/` |
| `/subagents-load-profile <name>` | Replace only `settings.subagents` with a saved profile and optionally switch this session to the profile worker model |
| `/subagents-refresh-provider-models <provider> [--force]` | Create or refresh the cached provider model catalog |
| `/subagents-generate-profiles <provider>` | Generate `<provider>.quota.json` and `<provider>.quality.json` profiles |
| `/subagents-check-profile <name>` | Check a saved profile against the current registry and live model probes |

Commands validate agent names locally, support tab completion, and send results back into the conversation.

`/subagents` opens a compact administration flow for builtin, package, user, and project agents. Model choices refresh Pi's model registry first, thinking choices are filtered to levels declared by the selected model, and prompt editing uses a blocking `$VISUAL`/`$EDITOR` command (with MarkEdit as the macOS fallback). Full metadata is opt-in through `details`. Edits are persisted to the field-owning layer: explicit custom-agent frontmatter remains in the agent file, while settings/profile-managed fields remain in `settings.subagents.agentOverrides`. Package-owned fields and definitions loaded through `PI_SUBAGENT_EXTRA_AGENT_DIRS` stay read-only; settings can still supply model or thinking fields omitted by a package definition.

### Profiles and provider model catalogs

Profiles are stored under:

```text
~/.pi/agent/profiles/pi-subagents/
```

Provider model catalogs are cached under:

```text
~/.pi/agent/profiles/pi-subagents/providers/
```

Use the profile workflow like this:

```text
/subagents-refresh-provider-models openai-codex
/subagents-generate-profiles openai-codex
/subagents-load-profile openai-codex.quota
```

`/subagents-refresh-provider-models` writes a serialized provider model catalog with observed registry data, simple role-oriented classification, and live probe results from tiny one-shot `pi -p --model ... --no-tools` checks. The cache refreshes when missing or stale; use `--force` to ignore freshness and probe again immediately.

`/subagents-generate-profiles` uses the provider catalog to produce quota and quality profiles. `/subagents-check-profile` re-checks each assigned model in a saved profile against the current registry and a live probe so you can detect model removals, auth problems, or stale assignments.

### Per-step tasks

Use `->` to separate steps and give each step its own task:

```text
/chain scout "scan the codebase" -> planner "create an implementation plan"
/parallel scanner "find security issues" -> reviewer "check code style"
```

Both double and single quotes work. You can also use `--` as a delimiter:

```text
/chain scout -- scan code -> planner -- analyze auth
```

Steps without a task inherit behavior from the execution mode. Chain steps get `{previous}`, the prior step’s output. Parallel steps use the first available task as a fallback.

### Inline parallel groups in `/chain`

Wrap a group of agents in parentheses and separate them with `|` to fan them out within a single chain step. The group runs all of its tasks concurrently, then the next `->` step continues once they finish:

```text
/chain scout "scan" -> (reviewer "review A" | reviewer "review B") -> writer "fix"
```

Notes:

- Groups must contain at least two tasks separated by ` | `, each with its own task.
- Group syntax is only valid between ` -> ` separators, and the group must appear as a complete step.
- Only a step that *opens* with `(` is a group. Parentheses inside a shared `--` task (e.g. `/chain scout -- inspect auth (backend)`) stay literal text and keep the legacy single-agent behavior.
- A group is treated as the prior step’s output for the next sequential step.
- Tab completion suggests agents inside groups — after `(`, after `|`, and on each new `->` step.

Add a `[...]` suffix right after the closing `)` to set step-level options on the group:

```text
/chain scout "scan" -> (reviewer "A" | reviewer "B")[concurrency=2,failFast,worktree] -> writer "fix"
```

| Group option | Description |
|--------------|-------------|
| `concurrency=N` | Max tasks running at once within the group. |
| `failFast` | Stop the group as soon as one task fails. |
| `worktree` | Run each group task in its own git worktree. |

Dynamic fanout (`expand` / `collect`) is intentionally not available inline — use the
`subagent({ chain: [...] })` tool API or a saved `.chain.json` for data-driven fan-out.

```text
/chain scout "analyze auth" -> planner -> worker
# scout gets "analyze auth"; planner gets scout output; worker gets planner output
```

For a shared task, list agents and place one `--` before the task:

```text
/chain scout planner -- analyze the auth system
/parallel scout reviewer -- check for security issues
```

### Inline per-step config

Append `[key=value,...]` to an agent name to override defaults. `/chain` applies every key below; `/run` and `/parallel` use the execution-behavior keys (`output`, `outputMode`, `reads`, `model`, `skills`, `progress`) and ignore chain-only metadata such as `as`, `label`, `phase`, `count`, `outputSchema`, and `acceptance`.

```text
/chain scout[output=context.md] "scan code" -> planner[reads=context.md] "analyze auth"
/run scout[model=anthropic/claude-sonnet-4] summarize this codebase
/parallel reviewer[skills=code-review+security] "review backend" -> reviewer[model=openai/gpt-5-mini] "review frontend"
```

| Key | Example | Description |
|-----|---------|-------------|
| `output` | `output=context.md` | Write results to a file. Absolute paths are used as-is. Relative paths in `/run` resolve under `singleRunOutputBaseDir` when configured, otherwise under the run's output artifact directory. Relative paths in `/chain` and `/parallel` live under the chain or parallel run directory. |
| `outputMode` | `outputMode=file-only` | Return only a concise file reference for saved output instead of the full saved content. Requires `output`; default is `inline`. |
| `reads` | `reads=a.md+b.md` | Read files before executing. `+` separates multiple paths. |
| `model` | `model=anthropic/claude-sonnet-4` | Override model for this step. |
| `skills` | `skills=planning+review` | Override available skills. `+` separates multiple skills. |
| `progress` | `progress` | Enable progress tracking. |
| `as` | `as=context` | Name this step’s output so later steps can reference it. |
| `label` | `label=Recon` | Human-readable label for the step. |
| `phase` | `phase=analysis` | Group steps into a named phase. |
| `cwd` | `cwd=packages/api` | Run the step in a subdirectory. |
| `count` | `count=3` | Fan a group task into N copies (only inside a `( ... )` group). |
| `outputSchema` | `outputSchema=schema.json` | Validate structured output against a JSON Schema file (path resolved against the session cwd, not an inline step `cwd`). |
| `acceptance` | `acceptance=checked` | Inline acceptance level: `auto`, `attested`, or `checked`. Use the tool API or saved `.chain.json` for object contracts such as `none` or `verified`; `reviewed` is inferred-only. |

Set `output=false`, `reads=false`, or `skills=false` to disable that behavior explicitly. Do not use `output=false` for file-only returns; use `outputMode=file-only` with an `output` path.

Inline `[...]` values must not contain spaces or commas — keep `label`/`phase` to single tokens.

### Background and forked runs

Add `--bg` to run in the background:

```text
/run scout "audit the codebase" --bg
/chain scout "analyze auth" -> planner "design refactor" -> worker --bg
/parallel scout "scan frontend" -> scout "scan backend" --bg
```

Add `--fork` to start each child from a real branched session created from the parent’s current leaf:

```text
/run reviewer "review this diff" --fork
/chain scout "analyze this branch" -> planner "plan next steps" --fork
/parallel scout "audit frontend" -> reviewer "audit backend" --fork
```

You can combine them in either order:

```text
/run reviewer "review this diff" --fork --bg
/run reviewer "review this diff" --bg --fork
```

Background runs are detached. If the parent agent has other independent work, it should keep working. In an interactive chat, it should normally return control when ready to yield and let Pi deliver the completion notification instead of blocking merely to wait. Override that default and use `subagent_wait` when the current request is run-to-completion — for example, the user asked you to report results back before continuing or a skill cannot return before its work finishes. In a non-interactive run, Pi auto-drains current-session work at `agent_end`; use `subagent_wait` when this turn must receive results before it ends. It returns when the next initially active run or registered provider item finishes or a subagent needs attention; use `subagent_wait({ all: true })` for all work active at call time, `subagent_wait({ id })` for one async or remembered detached foreground run, and `subagent_wait({ timeoutMs })` to cap the block.

A foreground child can detach while it waits for a supervisor reply. Reply first, then call `subagent_wait({ id: runId })`. The remembered run stays pending until the child exits, then emits a session-scoped completion notification with recovered output and remains inspectable through `subagent({ action: "status", id: runId })`. Do not call `resume` or launch a replacement while the child remains detached.

Headless sessions also auto-drain current-session subagent and registered provider work at `agent_end`, using one absolute timeout and continuing through attention states. This is a final lifecycle safeguard rather than a replacement for explicit orchestration: `subagent_wait` still lets a model react to each result during the turn. Provider, reconciliation, timeout, and malformed-state failures remain visible errors instead of being treated as successful drains.

The `oracle` and `worker` builtins are designed for an explicit decision loop. A typical pattern is to ask `oracle` for diagnosis and a recommended execution prompt, then only run `worker` after the main agent approves that direction.

## Clarify and launch UI

Tool calls launch directly by default. Set `clarify: true` on single, parallel, or chain runs when you want to preview and edit the workflow before it runs; slash commands launch directly.

Common clarify keys:

- `Enter` runs in the foreground, or in the background if background is toggled on
- `Esc` cancels or backs out
- `↑↓` moves between steps or tasks
- `e` edits the task/template
- `m` selects a model
- `t` selects thinking level
- `s` selects skills
- `b` toggles background execution
- `w` edits output/write behavior where supported
- `r` edits reads where supported
- `p` toggles progress tracking where supported
Picker screens use `↑↓`, `Enter`, `Esc`, and type-to-filter. The full-screen editor supports word wrapping, paste, `Esc` to save, and `Ctrl+C` to discard.

## Agents and chains

Agents are markdown files with YAML frontmatter and a system prompt body. They define the specialist that will run in the child Pi process.

Agent locations, lowest to highest priority:

| Scope | Path |
|-------|------|
| Builtin | `~/.pi/agent/extensions/subagent/agents/` |
| Installed package | `package.json` `pi-subagents.agents` or `pi.subagents.agents` |
| User | `~/.pi/agent/agents/**/*.md` |
| Project | Project config `agents/**/*.md` (`.pi/agents/**/*.md` in standard Pi) |

Project discovery also reads legacy `.agents/**/*.md` files. Nested subdirectories are discovered recursively. `.chain.md` files do not define agents. Installed Pi packages can expose agent directories from either `{"pi-subagents":{"agents":["./agents"]}}` or `{"pi":{"subagents":{"agents":["./agents"]}}}` in their package manifest. Package agents load above builtins and below user/project agents. If both `.agents/` and the project config agents directory define the same parsed runtime agent name, the project config directory wins. Use `agentScope: "user" | "project" | "both"` to control discovery; `both` is the default and project definitions win runtime-name collisions.

Builtin agents load at the lowest priority, so a user or project agent with the same name overrides them. They do not pin a provider model; they inherit your current Pi default model unless you set `subagents.defaultModel` or `subagents.agentOverrides.<name>.model`. `oracle` is an advisory reviewer that critiques direction and proposes an execution prompt without editing files. `worker` is the implementation agent for normal tasks and approved oracle handoffs.

The `researcher` builtin uses `web_search`, `fetch_content`, and `get_search_content`; those require [pi-web-access](https://github.com/nicobailon/pi-web-access):

```bash
pi install npm:pi-web-access
```

### Builtin overrides

You can override selected builtin fields without copying the whole agent. Overrides live in settings:

- User: `~/.pi/agent/settings.json`
- Project: project config settings file (`.pi/settings.json` in standard Pi)

Example:

```json
{
  "subagents": {
    "agentOverrides": {
      "reviewer": {
        "inheritProjectContext": false
      }
    }
  }
}
```

Supported override fields are `model`, `fallbackModels`, `thinking`, `systemPromptMode`, `inheritProjectContext`, `inheritSkills`, `defaultContext`, `acceptanceRole`, `disabled`, `skills`, `tools`, and `systemPrompt`. Use `defaultContext: false` or `acceptanceRole: false` to clear an inherited override. Project overrides beat user overrides.

Set `subagents.defaultModel` to give all subagents without an explicit model their own default model, separate from the parent session model. Per-agent model overrides and agent frontmatter still win.

Set `disabled: true` to hide a builtin from runtime discovery and agent-facing `subagent({ action: "list" })` output. For bulk control, set `subagents.disableBuiltins: true` in settings. You can also toggle a single agent without editing settings by hand: `subagent({ action: "disable", agent: "reviewer" })` writes that override, and `subagent({ action: "enable", agent: "reviewer" })` removes it.

Set `subagents.disableThinking: true` to clear bundled builtin thinking defaults globally for providers that do not support `:low`, `:medium`, `:high`, or similar model suffixes. A higher-precedence per-agent `thinking` override can opt one builtin back in.

### Prompt assembly

Subagents are designed to be narrow by default. Custom agents start with a clean system prompt and only the context you intentionally give them. They do not automatically inherit Pi’s whole base prompt, project instruction files, or discovered skills catalog.

Use these fields when an agent should see more:

| Field | Effect |
|-------|--------|
| `systemPromptMode: append` | Append the agent prompt to Pi’s normal base prompt. |
| `inheritProjectContext: true` | Keep inherited project instructions from files like `AGENTS.md` and `CLAUDE.md`. |
| `inheritSkills: true` | Let the child see Pi’s discovered skills catalog. |
| `defaultContext: fork` | Use forked session context when a launch omits `context`; explicit `context: "fresh"` still wins. |

Builtin agents opt into project instruction inheritance by default so they follow repo-specific rules out of the box. `delegate` also uses append mode because its job is orchestration inside the parent workflow.

### Agent frontmatter

A typical agent looks like this:

```yaml
---
name: scout
# Optional: registers this as code-analysis.scout while preserving name: scout
package: code-analysis
description: Fast codebase recon
tools: read, grep, find, ls, bash, mcp:chrome-devtools
extensions:
subagentOnlyExtensions: ./tools/child-only-search.ts
model: claude-haiku-4-5
fallbackModels: openai/gpt-5-mini, anthropic/claude-sonnet-4
thinking: high
systemPromptMode: replace
inheritProjectContext: false
inheritSkills: false
skills: safe-bash, review-checklist
skillPath: ./skills, ../shared-skills
output: context.md
defaultReads: context.md
defaultProgress: true
async: true
timeoutMs: 900000
turnBudget: {"maxTurns":20,"graceTurns":2}
acceptance: {"level":"none","reason":"lightweight lookup"}
acceptanceRole: read-only
completionGuard: false
interactive: true
maxSubagentDepth: 1
---

Your system prompt goes here.
```

Simple-scalar list fields accept either the existing comma-separated form or a newline block list with one `- item` per line. This applies to `tools`, `defaultReads`, `skill`/`skills`, `skillPath`, `fallbackModels`, `extensions`, and `subagentOnlyExtensions`; for example:

```yaml
tools:
  - read
  - mcp:github/search_repositories
fallbackModels:
  - openai/gpt-5-mini
  - anthropic/claude-sonnet-4
```

Important fields:

| Field | Notes |
|-------|-------|
| `package` | Optional package identifier. A file with `name: scout` and `package: code-analysis` registers as `code-analysis.scout`; serialization keeps `name` and `package` separate. |
| `tools` | Strict child tool allowlist. Named extension tools must also have their provider loaded. `mcp:` entries select direct MCP tools when `pi-mcp-adapter` is installed. |
| `extensions` | Omitted means normal extensions; empty means no extensions; list values allowlist specific extensions. |
| `subagentOnlyExtensions` | Extension paths loaded only in spawned child sessions for this agent. Tools registered there are unavailable to the main agent unless also installed through normal Pi extension configuration. |
| `model` | Default model. Bare ids prefer the current provider when possible, then unique registry matches. |
| `fallbackModels` | Ordered backup models for provider/model failures such as quota, auth, timeout, or unavailable model. Ordinary task failures do not trigger fallback. |
| `thinking` | Appended as a `:level` suffix at runtime unless a suffix is already present. |
| `systemPromptMode` | `replace` by default; `append` keeps Pi’s base prompt. |
| `inheritProjectContext` | Keeps or strips inherited project instruction blocks. |
| `inheritSkills` | Keeps or strips Pi’s discovered skills catalog. |
| `defaultContext` | Optional `fresh` or `fork` launch context default for this agent. |
| `skills` | Selects specific skills for the child, regardless of `inheritSkills`. |
| `skillPath` | Invocation-private skill files or discovery directories. Relative paths resolve from the agent definition file. Local matches take precedence, while unresolved or unreadable matches fall back to normal skill discovery. This field discovers candidates only; `skills` still selects what the child receives. |
| `output` | Default single-agent output file. |
| `defaultReads` | Files to read before running in chain/parallel behavior. |
| `defaultProgress` | Maintain `progress.md`. |
| `async` | Default a single-agent launch to background (`true`) or foreground (`false`) when the call omits `async`. Explicit call values and `forceTopLevelAsync` win. |
| `timeoutMs` | Positive integer default runtime deadline in milliseconds for single-agent launches. An explicit `timeoutMs` or `maxRuntimeMs` wins. |
| `turnBudget` | JSON object default such as `{"maxTurns":20,"graceTurns":2}` for single-agent launches. An explicit call value wins, followed by this agent default, then global `turnBudget` config. |
| `acceptance` | Acceptance default for single-agent launches. Use a scalar level such as `checked` or an inline/block YAML map such as `{ level: "none", reason: "lightweight lookup" }`. Explicit call values win; chain and parallel acceptance remains task/step configuration. |
| `acceptanceRole` | Optional `read-only` or `writer` role for automatic acceptance inference. Explicit task mutation or no-edit intent wins; otherwise the declared role replaces agent-name guessing. This does not grant or revoke tools. |
| `completionGuard` | Set `false` only for non-implementation agents that may mention implementation words while using mutation-capable tools such as `bash`. |
| `interactive` | Parsed for compatibility but not enforced in v1. |
| `maxSubagentDepth` | Tightens nested delegation for this agent's children. |
| `memory` | Opt-in role-specific persistent memory. `memory: { scope: "project" \| "user", path: "<name>" }` injects the first lines of a `MEMORY.md` from a dedicated `agent-memory/` directory into the child system prompt. Agents with write tools (`edit`/`write`/`bash`) get a read-write block; read-only agents get a read-only fallback. Project scope resolves under `<project>/.pi/agent-memory/`, user scope under `~/.pi/agent/agent-memory/`. Paths are validated against traversal and symlink escape. |

Agent-local `skillPath` candidates never enter Pi's parent/global skills catalog. Pair `inheritSkills: false` with explicit `skills` and `skillPath` when a child should receive only its selected private skills.

### Per-agent persistent memory

A recurring custom agent can opt into a durable, role-specific memory scope with the `memory` frontmatter field. This is independent of Pi's own parent/session/project memory system and writes nothing to it; memory lives under a dedicated `agent-memory/` namespace so the two never collide.

```yaml
memory:
  scope: project
  path: security-reviewer
```

On each run, the first 200 lines of `MEMORY.md` in the resolved memory directory are injected into the child system prompt so the agent can recall accumulated role notes such as threat-model entries, release gotchas, or verified commands. Agents that have write tools (`edit`, `write`, or `bash`, or no `tools` allowlist at all) are told they may append concise dated entries to the file. Agents without write tools receive a read-only memory block and are not instructed to edit it, so a read-only reviewer can still recall prior notes without being granted write capability. The memory directory is never created eagerly; the agent's own `write` tool creates it (and `MEMORY.md`) on the first persist. Memory paths are validated against `.`/`..` traversal and symlink escape, and an unsafe or unresolvable scope is silently skipped rather than breaking the run.

Project-scoped memory resolves under `<project>/.pi/agent-memory/<path>` and travels with the repo. User-scoped memory resolves under `~/.pi/agent/agent-memory/<path>` and is shared across projects for that agent.

### Tool and extension selection

If `tools` is omitted, `pi-subagents` does not pass `--tools`, so the child gets Pi’s normal builtin tools. If `tools` is present, regular tool names become an explicit allowlist. An allowlisted name does not load the extension that registers it: load that provider through normal Pi extension discovery, `extensions`, `subagentOnlyExtensions`, or a path-like `tools` entry. `mcp:` entries are split out and forwarded as direct MCP selections. Path-like `tools` entries, such as extension paths or `.ts`/`.js` files, are treated as tool-extension paths rather than tool names. Internal runtime tools such as `structured_output` are added to an explicit allowlist only when their contract is active. Agents that declare only known read-only builtin tools skip the implementation completion guard, but `bash`, unknown tools, and MCP tools stay mutation-capable. Use `completionGuard: false` for bash-enabled validators or advisors that should never be judged as implementation agents.

Examples:

- `tools` omitted and `extensions` omitted: normal builtins and normal extensions.
- `tools: mcp:chrome-devtools`: normal builtins plus direct Chrome DevTools MCP tools.
- `tools: read, bash, mcp:chrome-devtools`: only `read` and `bash` as builtins, plus direct Chrome DevTools MCP tools.
- `tools: subagent, read`: a child-safe `subagent` tool is available inside that child so it can run explicitly assigned nested fanout.
- `tools: read, fixture_search` plus `subagentOnlyExtensions: ./tools/fixture-search.ts`: the provider loads only in this agent's child process, and the registered `fixture_search` name survives the strict allowlist.

Direct MCP tools require [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter). Subagents only receive direct MCP tools when `mcp:` entries are listed in their frontmatter; global `directTools: true` in `mcp.json` is not enough by itself. The generic `mcp` proxy tool can still be used for discovery when available. The adapter caches tool metadata at startup, so after connecting a new MCP server for the first time, restart Pi before relying on direct tools. An `mcp:` entry named `subagent` does not authorize nested fanout; only the builtin `subagent` tool name does.

`extensions` controls child extension loading:

```yaml
# Omitted: all normal extensions load

# Empty: no extensions
extensions:

# Allowlist
extensions: /abs/path/to/ext-a.ts, /abs/path/to/ext-b.ts
```

When `extensions` is present, normal discovered extensions are disabled; the listed extensions, path-like `tools` entries, required pi-subagents runtime extensions, and `subagentOnlyExtensions` still load.

Use `subagentOnlyExtensions` when a custom extension tool should exist only inside child sessions. It is scoped by agent config: every run of that agent receives those extension paths, while other agents do not unless they declare the same field. The current model does not have a separate named-subagent audience inside one agent definition.

Before the first model turn, the child runtime compares every explicit tool name with Pi's final filtered registry. A missing provider now fails the run with the unavailable names and concrete `subagentOnlyExtensions`/`extensions` guidance instead of letting a direct or chained child silently continue without its requested tools.

## Chain files

Chains are reusable workflows stored separately from agent files. Use `.chain.md` for simple sequential saved chains. Use `.chain.json` when a chain needs dynamic fanout.

| Scope | Path |
|-------|------|
| Installed package | `package.json` `pi-subagents.chains` or `pi.subagents.chains` |
| User | `~/.pi/agent/chains/**/*.chain.md`, `~/.pi/agent/chains/**/*.chain.json` |
| Project | Project config `chains/**/*.chain.md`, `chains/**/*.chain.json` (`.pi/chains/...` in standard Pi) |

Nested subdirectories are discovered recursively. Installed Pi packages can expose chain directories from either `{"pi-subagents":{"chains":["./chains"]}}` or `{"pi":{"subagents":{"chains":["./chains"]}}}` in their package manifest. Package chains load below user/project chains. If both `.chain.md` and `.chain.json` define the same parsed runtime chain name in the same scope, `.chain.json` wins. If user and project scopes define the same parsed runtime chain name, the project chain wins. Chains support the same optional `package` frontmatter as agents; `name: review-flow` plus `package: code-analysis` runs as `code-analysis.review-flow`.

Example:

```md
---
name: scout-planner
description: Gather context then plan implementation
---

## scout
phase: Context
label: Map auth flow
as: context
output: context.md

Analyze the codebase for {task}

## planner
phase: Planning
label: Implementation plan
reads: context.md
model: anthropic/claude-sonnet-4-5:high
progress: true

Create an implementation plan based on {outputs.context}
```

Each `.chain.md` `## agent-name` section is a step. Config lines such as `phase`, `label`, `as`, `outputSchema`, `output`, `outputMode`, `reads`, `model`, `skills`, and `progress` go immediately after the header. A blank line separates config from task text. In saved `.chain.md` files, `outputSchema` is a path to a JSON Schema file; direct tool calls and `.chain.json` files can pass the schema object inline.

For `output`, `reads`, `skills`, and `progress`, chain behavior is three-state: omitted inherits from the agent, a value overrides, and `false` disables.

Use `phase` to group related work in status output, `label` for a readable step name, and `as` to store a successful step or parallel task result for later `{outputs.name}` references. Duplicate `as` names, invalid identifiers, and unknown output references fail before child execution.

Dynamic fanout is available only through direct `subagent({ chain: [...] })` JSON or saved `.chain.json` files. It expands an array from a prior structured named output, runs one child template per item, and stores the ordered collection under `collect.as`. The source must be structured output; prose is never parsed. `expand.maxItems` is required, over-limit arrays fail, nested fanout and arbitrary expressions are not supported, and `.chain.md` has no dynamic syntax in this release.

```json
{
  "name": "dynamic-review",
  "description": "Find review targets, fan out reviewers, then synthesize.",
  "chain": [
    {
      "agent": "scout",
      "task": "Return {\"items\":[{\"path\":\"...\",\"reason\":\"...\"}]} via structured_output.",
      "as": "targets",
      "outputSchema": { "type": "object" }
    },
    {
      "expand": {
        "from": { "output": "targets", "path": "/items" },
        "item": "target",
        "key": "/path",
        "maxItems": 12
      },
      "parallel": {
        "agent": "reviewer",
        "label": "Review {target.path}",
        "task": "Review {target.path}. Reason: {target.reason}",
        "outputSchema": { "type": "object" }
      },
      "collect": { "as": "reviews" },
      "concurrency": 4
    },
    {
      "agent": "worker",
      "task": "Synthesize fixes from {outputs.reviews}"
    }
  ]
}
```

Create simple `.chain.md` chains by writing files directly or with the `subagent({ action: "create", config: ... })` management action. Create dynamic `.chain.json` chains by writing the JSON file directly. Run saved chains with natural language or:

```text
/run-chain scout-planner -- refactor authentication
```

## Chain variables

Task templates support:

| Variable | Description |
|----------|-------------|
| `{task}` | Original task from the first step. |
| `{previous}` | Output from the prior step, or aggregated output from a parallel step. |
| `{chain_dir}` | Path to the chain artifact directory. |
| `{outputs.name}` | Text value from a prior step or completed parallel task with `as: "name"`. |

Parallel outputs are aggregated with clear separators before being passed to the next step:

```text
=== Parallel Task 1 (worker) ===
...

=== Parallel Task 2 (worker) ===
...
```

## Skills

Skills are `SKILL.md` files made available to an agent. The prompt includes skill metadata and the file location; the agent reads the full skill file only when the task matches.

Discovery uses project-first precedence:

1. Project config `skills/{name}/SKILL.md` (`.pi/skills/{name}/SKILL.md` in standard Pi)
2. Project packages and project settings packages via `package.json -> pi.skills`
3. Current task cwd package via `package.json -> pi.skills`
4. Project config `settings.json -> skills`
5. `~/.pi/agent/skills/{name}/SKILL.md`
6. User packages and user settings packages via `package.json -> pi.skills`
7. `~/.pi/agent/settings.json -> skills`

Use agent defaults, override them at runtime, or disable them:

```ts
{ agent: "scout", task: "..." }
{ agent: "scout", task: "...", skill: "tmux, safe-bash" }
{ agent: "scout", task: "...", skill: false }
```

For chains, `skill` at the top level is additive. A step-level `skill` overrides that step; `false` disables skills for that step.

Available skills use this shape:

```xml
The following configured skills are available to this subagent.
Use the read tool to load a skill's file when the task matches its description.
When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.

<available_skills>
  <skill>
    <name>safe-bash</name>
    <description>Run shell commands safely.</description>
    <location>/absolute/path/to/safe-bash/SKILL.md</location>
  </skill>
</available_skills>
```

If an agent has an explicit `tools` allowlist and resolved skills, `read` is added for that child run so the listed skill files can be loaded on demand.

Missing skills do not fail execution. The result summary shows a warning.

### Bundled skill

The package bundles a `pi-subagents` skill that is automatically available to the parent agent when the extension is installed. It is for the orchestrating parent only: child subagents never receive it, and their context is explicitly filtered to strip parent-only orchestration instructions.

What the bundled skill covers:
- **Delegation patterns**: when to launch which agent, whether to use single, parallel, chain, or async mode, and whether to use fresh or forked context
- **Prompt workflow recipes**: how to apply the packaged techniques directly with `subagent(...)` when the user describes the workflow in natural language instead of invoking a slash command. This includes parallel review, review-loop, parallel research, parallel context-build, parallel handoff-plan, gather-context-and-clarify, and parallel cleanup
- **Role-agent prompting guidance**: compact contract prompts instead of long scripts, what to include in role-specific meta prompts, and retrieval budgets for researchers
- **Safety boundaries**: child agents must not run subagents unless their resolved builtin tools explicitly include `subagent`, must not invent intercom targets, and must escalate unapproved decisions
- **Intercom conventions**: when to ask vs send, and how parent-side supervisor/result delivery works through the native channel
- **Control and diagnostics**: attention signals, soft interrupts, status, and the `doctor` action

If you are writing an agent that orchestrates subagents, the bundled skill helps it behave correctly without guessing the patterns. If you are a human user, you do not need to read it directly; the README and prompt shortcuts encode the same workflows in user-facing form.

## Extension delegation API

Pi extensions can request one configured foreground agent through the typed v1 event contract:

```ts
import {
  SUBAGENT_DELEGATION_REQUEST_EVENT,
  SUBAGENT_DELEGATION_RESPONSE_EVENT,
  type SubagentDelegationRequest,
  type SubagentDelegationResponse,
} from "pi-subagents/delegation";

const request: SubagentDelegationRequest = {
  version: 1,
  requestId: crypto.randomUUID(),
  agent: "reviewer",
  task: "Review the supplied evidence.",

[660 more lines in file. Use offset=1000 to continue.]


Now I have a good understanding. The pi-subagents package has slash commands like `/run`, `/chain`, `/run-chain`. The user has custom prompt templates (c-*.md) with frontmatter `chain: p-brainstorm -> ...` and text saying `Invoke with: /prompt-workflow c-design-build --bg ...`.

But `/prompt-workflow` is not a documented pi-subagents command. It looks like the user may have created these prompt templates themselves with a custom command that may not exist, or perhaps they are invoking them via `pi -p` somehow.

Wait, let me re-read the prompt templates. They have:
```markdown
---
description: ...
argument-hint: ...
chain: p-brainstorm -> p-plan -> p-test -> p-commit
---
Run the design → plan → test → commit chain for: $@

Invoke with:
/prompt-workflow c-design-build --bg "add a rate-limiter to the API"
```

So the c-* files are prompt templates. When the user types `/c-design-build` in pi, the template expands. The template content itself mentions `/prompt-workflow c-design-build --bg` in the body. So maybe the user's workflow is:
1. Type `/c-design-build add a rate-limiter`
2. The template expands to: `Run the design → plan → test → commit chain for: add a rate-limiter`
3. Then there's a line `Invoke with: /prompt-workflow c-design-build --bg "add a rate-limiter to the API"` which is just instructions/hint text.

But pi-subagents doesn't have `/prompt-workflow`. Maybe the user intends for the parent pi to read this and then call `/run-chain`? Or maybe they have another custom extension/skill. Hmm.

Actually wait - `c-{prompt}` in the user's question might be exactly these prompt template names. And they say "they dont create a new pi -p session". So perhaps when they invoke `/c-design-build`, the content tells pi to run `/prompt-workflow c-design-build --bg ...`, but this command doesn't exist, or it's invoking some wrapper that runs `pi -p` in a new session.

Let me check if there's any custom extension in their setup. They have `@demigodmode/pi-web-agent`, `@ff-labs/pi-fff`, `@juicesharp/rpiv-ask-user-question`, `@a5c-ai/babysitter-pi`, `pi-ollama-cloud`, `context-mode`. No obvious prompt-workflow extension.

Maybe `/prompt-workflow` is intended as natural language? Or the user is running these from their shell using `pi -p` with the prompt as input.

Wait, the user says: "once the chain starts i have no option to stop them". This matches the behavior of `pi -p` (non-interactive print mode). When you run `pi -p` from the shell with a long chain, you can Ctrl+C to stop the process, but if the chain spawns child subagents, those might be detached background processes.

Also, in pi-subagents, `/run-chain` runs in the current session. If you use `--bg`, it runs as a background async run that you can check and stop via `/subagents-stop` or `subagent({ action: "stop", id: ... })`.

The user likely wants to:
1. Not spawn a new `pi -p` process - they want the chain to run inside the current interactive pi session, or at least give them control/stop options.
2. Have an automatic routing tool that picks the best subagent/prompt.

Now, the second question: "is there also an automatic tool that would just use whatever subagent or prompt is the most fitting?"

This is basically asking for an agent router. Pi-subagents allows the parent agent to decide which subagent to use based on natural language. But they want something automatic that maps their c-* prompts. I could suggest:
- Use the built-in `subagent` tool in the parent and let it decide. The bundled skill covers delegation patterns.
- Create a "router" agent or a top-level prompt that picks the right chain.
- Use `/run-chain` with a router agent that selects the appropriate saved chain.

Actually, the most direct answer is:
1. The c-* prompts should be converted to proper `.chain.md` files in `~/.pi/agent/chains/` (or `.pi/chains/`), and invoked with `/run-chain <name> -- [task]` in the current interactive session. This runs inside the current pi session, not a new `pi -p` process. If you want background, add `--bg` and you can stop with `/subagents-stop`.
2. For automatic routing, create a custom agent that reads the task and calls the appropriate chain, or just ask the parent naturally (e.g., "run the right workflow for adding a rate limiter"). But there isn't a built-in "auto-prompt" command that picks c-* vs p-* templates automatically.

Wait, the user has both `p-*.md` and `c-*.md` prompts. The `p-*` are probably regular prompt templates that expand into instructions. The `c-*` are chain prompts. The c-* frontmatter has `chain: p-brainstorm -> p-plan -> ...`. Maybe the user expects a custom `/prompt-workflow` command that reads this `chain:` frontmatter and runs the chain.

Hmm, maybe the user is using a custom implementation or wants one. They ask "is there a way to change the c-{prompt} so they dont create a new pi -p session." So they are creating new pi -p sessions somehow. Maybe they're shell aliasing c-design-build to `pi -p /prompt-workflow ...`.

Let me check the dotfiles for shell aliases or scripts. The current working directory is `/home/liph/dotfiles/pi/.pi/agent/prompts`, so there's likely a dotfiles repo. Let me search for `c-design-build` or `prompt-workflow` in the dotfiles.

bash

find /home/liph/dotfiles -type f \( -name '*.sh' -o -name '*.bash' -o -name '*.zsh' -o -name 'aliases*' -o -name 'config.*' \) -exec grep -l -E 'c-design-build|prompt-workflow|pi -p' {} \; 2>/dev/null | head -20


(no output)


pi/.pi/agent/sessions/--home-liph-dotfiles-pi-.pi-agent-prompts--/2026-07-24T16-05-14-023Z_019f94df-aaa7-7e74-90b6-1799f398b5a9.jsonl  [untracked in git]
 10: {"type":"message","id":"499e09fe","parentId":"460e9606","timestamp":"2026-07-24T16:06:51.818Z","message":{"role":"toolResult","toolCallId":"functions.read:3","toolName":"read","content":[{"type":"text","text":"---\ndescription: Brainstorm → plan → test → commit in one background chain\nargument-hint: \"[task or feature]\"\nchain: p-brainstorm -> p-plan -> p-test -> p-commit\n---\nRun the design → plan → test → commit chain for: $@\n\nInvoke with:\n/prompt-workflow c-design-build --bg \"add a rat
 11: {"type":"message","id":"e23c77e3","parentId":"499e09fe","timestamp":"2026-07-24T16:06:51.818Z","message":{"role":"toolResult","toolCallId":"functions.read:4","toolName":"read","content":[{"type":"text","text":"---\ndescription: Summarize → diagnose → debug → test in one background chain\nargument-hint: \"[symptom, error, or observed behavior]\"\nchain: p-summarize -> p-diagnose -> p-debug -> p-test\n---\nRun the understand → diagnose → fix → regression-test chain for: $@\n\nInvoke with:\n/prompt
 12: {"type":"message","id":"7096270f","parentId":"e23c77e3","timestamp":"2026-07-24T16:06:51.818Z","message":{"role":"toolResult","toolCallId":"functions.read:5","toolName":"read","content":[{"type":"text","text":"---\ndescription: Refactor → test → review → commit in one background chain\nargument-hint: \"[file path or symbol to refactor]\"\nchain: p-refactor -> p-test -> p-review -> p-commit\n---\nRun the safe refactor → validate → review → commit chain for: $@\n\nInvoke with:\n/prompt-workflow c-
 13: {"type":"message","id":"595af1c3","parentId":"7096270f","timestamp":"2026-07-24T16:06:51.818Z","message":{"role":"toolResult","toolCallId":"functions.read:6","toolName":"read","content":[{"type":"text","text":"---\ndescription: Security audit → review → test in one background chain\nargument-hint: \"[file path, diff, or feature to audit]\"\nchain: p-secaudit -> p-review -> p-test\n---\nRun the security-first review loop for: $@\n\nInvoke with:\n/prompt-workflow c-security-loop --bg \"auth endpoi...
 14: {"type":"message","id":"a978d99b","parentId":"595af1c3","timestamp":"2026-07-24T16:06:58.685Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Interesting! These prompt templates have frontmatter with `chain: p-brainstorm -> p-plan -> p-test -> p-commit` and an `Invoke with: /prompt-workflow c-design-build --bg \"...\"` line. This is NOT standard pi prompt template behavior. This looks like it's using an extension or a custom pi package that implements `/prompt-workflow` ...
 29: {"type":"message","id":"729d7030","parentId":"a0f366ca","timestamp":"2026-07-24T16:07:06.556Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Now I understand. The user has pi-subagents package installed. The c-* prompts are custom prompts that use a `/prompt-workflow` command which is likely added by pi-subagents extension, or it's a skill command. The extension directory is empty because packages are loaded from ~/.pi/agent/npm/node_modules/pi-subagents/.\n\nThe user i...
 31: {"type":"message","id":"7047c989","parentId":"a09b45b7","timestamp":"2026-07-24T16:07:08.414Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"This is very relevant. The README is large. I need to read more about prompt templates, saved chains, workflow, and specifically the /prompt-workflow command and chains. Let me search for \"prompt-workflow\" and \"chain\" in the README.","thinkingSignature":"reasoning"},{"type":"toolCall","id":"functions.ffgrep:20","name":"ffgrep",...
 33: {"type":"message","id":"40b6dac2","parentId":"89093449","timestamp":"2026-07-24T16:07:12.996Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Now I have a much clearer picture. The c-* prompts in the user's prompt directory use frontmatter `chain:` and an invocation `Invoke with: /prompt-workflow c-design-build --bg ...`. This seems to be a custom convention they've set up, perhaps expecting pi-subagents to recognize a `prompt-workflow` command. But pi-subagents doesn't ...
 35: {"type":"message","id":"4d65ff73","parentId":"5621f87c","timestamp":"2026-07-24T16:07:27.200Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Now I have a good understanding. The pi-subagents package has slash commands like `/run`, `/chain`, `/run-chain`. The user has custom prompt templates (c-*.md) with frontmatter `chain: p-brainstorm -> ...` and text saying `Invoke with: /prompt-workflow c-design-build --bg ...`.\n\nBut `/prompt-workflow` is not a documented pi-subag...

pi/.pi/agent/prompts/.pi-subagents/artifacts/5f01a9ab-6def-4648-b9b0-54d97946fdbf_delegate_1_transcript.jsonl  [untracked in git]
 1: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909141265,"timestamp":"2026-07-24T16:05:41.265Z","sourceEventType":"initial_prompt","role":"user","text":"Create a structured implementation plan for: add a rate-limit\n   /prompt-workflow c-fix-commit --bg the checkout inter /prompt-workflow c-refactor-commit src/utils.ts /prompt-workflow c-security-loop a...
 2: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909143422,"timestamp":"2026-07-24T16:05:43.422Z","sourceEventType":"message_end","role":"user","text":"Task: Create a structured implementation plan for: add a rate-limit\n   /prompt-workflow c-fix-commit --bg the checkout inter /prompt-workflow c-refactor-commit src/utils.ts /prompt-workflow c-security-loo...
 3: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909146001,"timestamp":"2026-07-24T16:05:46.001Z","sourceEventType":"message_end","role":"assistant","model":"minimax-m3:cloud","stopReason":"toolUse","usage":{"input":3634,"output":169,"cacheRead":0,"cacheWrite":0,"cost":0},"message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wan...
 10: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909147885,"timestamp":"2026-07-24T16:05:47.885Z","sourceEventType":"message_end","role":"toolResult","text":"---\ndescription: Summarize → diagnose → debug → test in one background chain\nargument-hint: \"[symptom, error, or observed behavior]\"\nchain: p-summarize -> p-diagnose -> p-debug -> p-test\n---\nR...
 14: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909204143,"timestamp":"2026-07-24T16:06:44.143Z","sourceEventType":"message_end","role":"toolResult","text":"---\ndescription: Refactor → test → review → commit in one background chain\nargument-hint: \"[file path or symbol to refactor]\"\nchain: p-refactor -> p-test -> p-review -> p-commit\n---\nRun the sa...
 18: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909205391,"timestamp":"2026-07-24T16:06:45.391Z","sourceEventType":"message_end","role":"toolResult","text":"---\ndescription: Security audit → review → test in one background chain\nargument-hint: \"[file path, diff, or feature to audit]\"\nchain: p-secaudit -> p-review -> p-test\n---\nRun the security-fir...
 27: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909209159,"timestamp":"2026-07-24T16:06:49.159Z","sourceEventType":"message_end","role":"assistant","model":"minimax-m3:cloud","stopReason":"toolUse","usage":{"input":4398,"output":51,"cacheRead":0,"cacheWrite":0,"cost":0},"message":{"role":"assistant","content":[{"type":"toolCall","id":"call_function_e10l1...
 28: {"version":1,"recordType":"tool_start","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909209160,"timestamp":"2026-07-24T16:06:49.160Z","sourceEventType":"tool_execution_start","toolName":"bash","argsPreview":"grep -r \"prompt-workflow\" /home/liph/.pi/ -l 2>/dev/null ..."}
 30: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909209965,"timestamp":"2026-07-24T16:06:49.965Z","sourceEventType":"message_end","role":"toolResult","text":"/home/liph/.pi/context-mode/sessions/3ea71be6c83c47ea.db\n/home/liph/.pi/context-mode/sessions/12c859f108b03852.db\n/home/liph/.pi/context-mode/content/aa673b1056f331f7.db\n/home/liph/.pi/agent/npm/n...
 31: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909211120,"timestamp":"2026-07-24T16:06:51.120Z","sourceEventType":"message_end","role":"assistant","model":"minimax-m3:cloud","stopReason":"toolUse","usage":{"input":4989,"output":47,"cacheRead":0,"cacheWrite":0,"cost":0},"message":{"role":"assistant","content":[{"type":"toolCall","id":"call_function_oefsh...
 34: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909211124,"timestamp":"2026-07-24T16:06:51.124Z","sourceEventType":"message_end","role":"toolResult","text":"import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ExtensionAPI, ExtensionContext } from \"@earendil-works/pi-coding-ag...
 38: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909212843,"timestamp":"2026-07-24T16:06:52.843Z","sourceEventType":"message_end","role":"toolResult","text":"import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { keyText, type ExtensionAPI, type ExtensionContext } from \"@earendil-work...
 46: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909216526,"timestamp":"2026-07-24T16:06:56.526Z","sourceEventType":"message_end","role":"toolResult","text":"# Chain Commands Reference\n\nThis file documents how to invoke the `c-*` chain recipes defined in `~/.pi/agent/prompts/`.\n\n## What the chains are\n\nThe `c-*` files are orchestration recipes. They...
 50: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909217986,"timestamp":"2026-07-24T16:06:57.986Z","sourceEventType":"message_end","role":"toolResult","text":"# Prompts\n\nSlash-style prompts for the interactive pi session. Each file is a `p-<verb>.md` template with a frontmatter `description` + `argument-hint` and a structured body. Invoke with `:p-<verb>...
 51: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909220889,"timestamp":"2026-07-24T16:07:00.889Z","sourceEventType":"message_end","role":"assistant","text":"Now I have full context. The user is asking a question that mixes a real ask with a question. Let me parse the task carefully:\n\nThe task title is: \"add a rate-limit\" and they show 3 invocations of...
 62: {"version":1,"recordType":"message","source":"async","runId":"5f01a9ab-6def-4648-b9b0-54d97946fdbf","agent":"delegate","childIndex":1,"cwd":"/home/liph/dotfiles/pi/.pi/agent/prompts","ts":1784909224021,"timestamp":"2026-07-24T16:07:04.021Z","sourceEventType":"message_end","role":"toolResult","text":"/home/liph/.pi/agent/prompts/c-design-build.md\n---\ndescription: Brainstorm → plan → test → commit in one background chain\nargument-hint: \"[task or feature]\"\nchain: p-brainstorm -> p-plan -> p-t...

pi/.pi/agent/sessions/--home-liph-dotfiles-pi-.pi-agent-prompts--/2026-07-24T15-49-05-219Z_019f94d0-e243-73e1-a106-43567b64e99d/00700bed/run-1/session.jsonl  [untracked in git]
 5: {"type":"message","id":"a77d1c0d","parentId":"5b91d99e","timestamp":"2026-07-24T16:05:43.366Z","message":{"role":"user","content":[{"type":"text","text":"Task: Create a structured implementation plan for: add a rate-limit\n   /prompt-workflow c-fix-commit --bg the checkout inter /prompt-workflow c-refactor-commit src/utils.ts /prompt-workflow c-security-loop auth endpoint can i create y shortcut to invoke these prompts more easyly.... or what options do i have\n\nFollow this format:\n1. **Goal**...
 6: {"type":"message","id":"4ff18efb","parentId":"a77d1c0d","timestamp":"2026-07-24T16:05:45.996Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to create a structured implementation plan for adding a rate-limit shortcut to invoke several /prompt-workflow commands more easily. Let me analyze the request:\n\n1. They have several `/prompt-workflow` commands they want to chain/invoke:\n   - `c-fix-commit` (with `--bg` flag, on \"the checkout inter\")\n   - `c...
 9: {"type":"message","id":"bb1353a1","parentId":"91299ce2","timestamp":"2026-07-24T16:05:47.883Z","message":{"role":"toolResult","toolCallId":"call_function_2zug7q023fce_1","toolName":"read","content":[{"type":"text","text":"---\ndescription: Summarize → diagnose → debug → test in one background chain\nargument-hint: \"[symptom, error, or observed behavior]\"\nchain: p-summarize -> p-diagnose -> p-debug -> p-test\n---\nRun the understand → diagnose → fix → regression-test chain for: $@\n\nInvoke wi
 11: {"type":"message","id":"26e1dc28","parentId":"c8616c50","timestamp":"2026-07-24T16:06:44.141Z","message":{"role":"toolResult","toolCallId":"call_function_grmugnrkdwcd_1","toolName":"read","content":[{"type":"text","text":"---\ndescription: Refactor → test → review → commit in one background chain\nargument-hint: \"[file path or symbol to refactor]\"\nchain: p-refactor -> p-test -> p-review -> p-commit\n---\nRun the safe refactor → validate → review → commit chain for: $@\n\nInvoke with:\n/prompt
 13: {"type":"message","id":"7687c91b","parentId":"0c68da45","timestamp":"2026-07-24T16:06:45.389Z","message":{"role":"toolResult","toolCallId":"call_function_vg2thxssnd8k_1","toolName":"read","content":[{"type":"text","text":"---\ndescription: Security audit → review → test in one background chain\nargument-hint: \"[file path, diff, or feature to audit]\"\nchain: p-secaudit -> p-review -> p-test\n---\nRun the security-first review loop for: $@\n\nInvoke with:\n/prompt-workflow c-security-loop --bg \...
 18: {"type":"message","id":"37d687e0","parentId":"6cb88dfa","timestamp":"2026-07-24T16:06:49.158Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_function_e10l1c7200ld_1","name":"bash","arguments":{"command":"grep -r \"prompt-workflow\" /home/liph/.pi/ -l 2>/dev/null | head -20"}}],"api":"openai-completions","provider":"ollama","model":"minimax-m3:cloud","usage":{"input":4398,"output":51,"cacheRead":0,"cacheWrite":0,"reasoning":0,"totalTokens":4449,"cost":{"input":0,"output":...
 19: {"type":"message","id":"10bb2fae","parentId":"37d687e0","timestamp":"2026-07-24T16:06:49.963Z","message":{"role":"toolResult","toolCallId":"call_function_e10l1c7200ld_1","toolName":"bash","content":[{"type":"text","text":"/home/liph/.pi/context-mode/sessions/3ea71be6c83c47ea.db\n/home/liph/.pi/context-mode/sessions/12c859f108b03852.db\n/home/liph/.pi/context-mode/content/aa673b1056f331f7.db\n/home/liph/.pi/agent/npm/node_modules/pi-subagents/CHANGELOG.md\n/home/liph/.pi/agent/npm/node_modules/pi...
 20: {"type":"message","id":"04b5e815","parentId":"10bb2fae","timestamp":"2026-07-24T16:06:51.119Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"call_function_oefsh1evn4tq_1","name":"read","arguments":{"path":"/home/liph/.pi/agent/npm/node_modules/pi-subagents/src/slash/prompt-workflows.ts"}}],"api":"openai-completions","provider":"ollama","model":"minimax-m3:cloud","usage":{"input":4989,"output":47,"cacheRead":0,"cacheWrite":0,"reasoning":0,"totalTokens":5036,"cost":{"input":0,"...
 21: {"type":"message","id":"1b074f21","parentId":"04b5e815","timestamp":"2026-07-24T16:06:51.121Z","message":{"role":"toolResult","toolCallId":"call_function_oefsh1evn4tq_1","toolName":"read","content":[{"type":"text","text":"import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ExtensionAPI, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { parseFrontmatter } from \"../agents/frontmatter.ts\";\nimport ...
 23: {"type":"message","id":"1cb702dc","parentId":"eb3e78e3","timestamp":"2026-07-24T16:06:52.835Z","message":{"role":"toolResult","toolCallId":"call_function_yyavt24fl6av_1","toolName":"read","content":[{"type":"text","text":"import { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { keyText, type ExtensionAPI, type ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { Key, matchesKey, type Component, type TUI } from...
 27: {"type":"message","id":"5748a7ab","parentId":"873fb5d3","timestamp":"2026-07-24T16:06:56.523Z","message":{"role":"toolResult","toolCallId":"call_function_p6340nkhj8xl_1","toolName":"read","content":[{"type":"text","text":"# Chain Commands Reference\n\nThis file documents how to invoke the `c-*` chain recipes defined in `~/.pi/agent/prompts/`.\n\n## What the chains are\n\nThe `c-*` files are orchestration recipes. They do not contain new prompts; they reuse the existing `p-*` prompts and run them...
 29: {"type":"message","id":"69470efd","parentId":"6d0d86a0","timestamp":"2026-07-24T16:06:57.983Z","message":{"role":"toolResult","toolCallId":"call_function_m7a8ktizyn57_1","toolName":"read","content":[{"type":"text","text":"# Prompts\n\nSlash-style prompts for the interactive pi session. Each file is a `p-<verb>.md` template with a frontmatter `description` + `argument-hint` and a structured body. Invoke with `:p-<verb> [arg]` or via the `:prompts` TUI picker.\n\n## When to use which\n\n| Prompt  ...
 30: {"type":"message","id":"3e2d6dbe","parentId":"69470efd","timestamp":"2026-07-24T16:07:00.887Z","message":{"role":"assistant","content":[{"type":"text","text":"Now I have full context. The user is asking a question that mixes a real ask with a question. Let me parse the task carefully:\n\nThe task title is: \"add a rate-limit\" and they show 3 invocations of `/prompt-workflow c-*` commands (using chains for different tasks). Then they ask: \"can i create y shortcut to invoke these prompts more ea...
 35: {"type":"message","id":"8fa0ef4d","parentId":"36764169","timestamp":"2026-07-24T16:07:04.017Z","message":{"role":"toolResult","toolCallId":"call_function_5x1hroczkmee_1","toolName":"bash","content":[{"type":"text","text":"/home/liph/.pi/agent/prompts/c-design-build.md\n---\ndescription: Brainstorm → plan → test → commit in one background chain\nargument-hint: \"[task or feature]\"\nchain: p-brainstorm -> p-plan -> p-test -> p-commit\n---\nRun the design → plan → test → commit chain for: $@\n\nIn

pi/.pi/agent/prompts/.pi-subagents/artifacts/5f01a9ab-6def-4648-b9b0-54d97946fdbf_delegate_0_meta.json  [untracked in git]
 4: "task": "Brainstorm approaches for: add a rate-limit\n   /prompt-workflow c-fix-commit --bg the checkout inter /prompt-workflow c-refactor-commit src/utils.ts /prompt-workflow c-security-loop auth endpoint can i create y shortcut to invoke these prompts more easyly.... or what options do i have\n\nStructure your answer as:\n1. **Problem restatement** — one paragraph; what we're solving, what constraints matter, what \"done\" looks like\n2. **Alternatives** — 3 to 5 distinct approaches. For each:...
 166: "evidence": "Every recommendation is grounded in evidence read from the repo: c-fix-commit.md / c-refactor-commit.md / c-security-loop.md frontmatter, Doc/chain-commands.md (existing /prompt-workflow surface, documented 'pastes text' pitfall for bare /c-... invocation), Doc/prompts.md (existing p-* prompt convention, 'one prompt per turn' guidance, :prompts discoverability note), and test/chain-workflow.test.ts (validates c-*.md frontmatter, chain: resolution, subagent/fork flags — referenced ex
 185: "summary": "Captured documented /prompt-workflow surface and 'pastes text' pitfall that constrains the shortcut design"
 194: "Problem restatement matches the user's actual three chains (c-fix-commit, c-refactor-commit, c-security-loop) and the documented /prompt-workflow c-... --bg '...' surface.",
 202: "Location of the pi-subagents prompt-workflow adapter is not visible in ~/.pi/agent/prompts/ — Alternative D's time-to-ship is an estimate until that is confirmed.",

pi/.pi/agent/prompts/.pi-subagents/artifacts/5f01a9ab-6def-4648-b9b0-54d97946fdbf_delegate_0_output.md  [untracked in git]
 7: You're invoking three chain recipes (`c-fix-commit`, `c-refactor-commit`, `c-security-loop`) via the verbose `/prompt-workflow <recipe> --bg "<args>"` syntax. The friction is **typing + remembering the exact recipe name and flag** every time, not capability — the adapter already works. Constraints that matter: (a) the shortcut must keep running through the existing `pi-subagents` `prompt-workflow` adapter, because that's what `Doc/chain-commands.md` documents and what `test/chain-workflow.test.t...
 13: **Summary.** Define `fcc`, `rcc`, `scl` (or similar) as shell functions in your dotfiles that expand to `pi /prompt-workflow c-fix-commit --bg "$@"` etc.
 15: **How it works.** Shell functions wrap the verbatim command. pi never sees a new surface; the prompt-workflow adapter and chain recipes are unchanged.
 33: **Summary.** Create `p-cfix.md`, `p-crefactor.md`, `p-csec.md` (or similar) that contain a single line of body text instructing the model to invoke `/prompt-workflow c-fix-commit --bg "$@"` and return.
 44: - Indirect: the user types `:c-fix "..."`, the model has to *decide* to call `/prompt-workflow`, which is a behavior contract you now have to maintain.
 54: **How it works.** `/prompt-workflow c-fc --bg "..."` is shorter than `/prompt-workflow c-fix-commit --bg "..."`. No new code, just renaming.
 69: ### Alternative D — Teach the `pi-subagents` prompt-workflow adapter to accept bare recipe names and a `--shortcut` mapping file
 71: **Summary.** Add a config file (e.g., `~/.pi/agent/prompts/shortcuts.yaml`) that maps `fcc → c-fix-commit`, `rcc → c-refactor-commit`, `scl → c-security-loop`, so `/prompt-workflow fcc --bg "..."` Just Works.
 73: **How it works.** A small lookup step at the start of the prompt-workflow adapter resolves the shortcut to a full recipe name, then proceeds exactly as today. The chain recipes and `p-*.md` files are unchanged.
 77: - Discoverable via `/prompt-workflow list --shortcuts` (a small doc/help add).
 93: **How it works.** The body instructs the model to parse the first token as a subcommand and dispatch to the right `/prompt-workflow` invocation. Everything else routes through the existing adapter.
 127: 3. `Doc/chain-commands.md` already says chains run through `/prompt-workflow` and that `/c-...` "just pastes text" — this is documented behavior. The shortcut should sit at the *shell* layer (where you've already absorbed the verbose command into muscle memory) and not at the prompt layer, which would re-introduce the same "pastes text instead of running" pitfall.
 134: fcc()  { pi /prompt-workflow c-fix-commit      --bg "$@"; }
 135: rcc()  { pi /prompt-workflow c-refactor-commit --bg "$@"; }
 136: scl()  { pi /prompt-workflow c-security-loop   --bg "$@"; }
 139: Then your real invocation collapses from `/prompt-workflow c-fix-commit --bg "the checkout inter"` to `fcc "the checkout inter"`. Total on-disk changes: zero.
 145: 1. **Where does the `pi-subagents` `prompt-workflow` adapter actually live?** Is it a package you can edit, a vendored file, or a third-party dependency? That decides whether D is a 30-minute edit or a fork-and-PR. (Not visible in `~/.pi/agent/prompts/` — it's upstream of this tree.)

[Continue with cursor="fff_c1"]


This is a goldmine. There's a `src/slash/prompt-workflows.ts` in pi-subagents. So `/prompt-workflow` IS a pi-subagents command. Let me read that file and the `Doc/chain-commands.md` in the user's repo.

Also, the session transcript reveals that the user previously asked about creating shortcuts and got advice. Now they're asking a follow-up: can the c-* prompts be changed so they don't create a new `pi -p` session, and is there an automatic tool that picks the best subagent/prompt.

The `prompt-workflow` adapter seems to be running `pi -p` to execute chains. The user doesn't like that once started, it's hard to stop. They want it to run in the current interactive session instead.

Let me read the prompt-workflow source to understand how it works.


import * as fs from "node:fs";
import * as path from "node:path";
import { fileURLToPath } from "node:url";
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
import { parseFrontmatter } from "../agents/frontmatter.ts";
import type { SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
import type { ChainStep } from "../shared/settings.ts";
import { getAgentDir, getProjectConfigDir } from "../shared/utils.ts";

interface PromptWorkflow {
	name: string;
	description: string;
	body: string;
	filePath: string;
	agent: string;
	context?: "fresh" | "fork";
	model?: string;
	skill?: string | string[] | false;
	cwd?: string;
	worktree?: boolean;
	chain?: string;
}

type PromptWorkflowRunner = (params: SubagentParamsLike, ctx: ExtensionContext) => Promise<void>;

const RESERVED_COMMAND_NAMES = new Set([
	"chain-prompts",
	"prompt-workflow",
	"run",
	"chain",
	"parallel",
	"run-chain",
	"subagents-doctor",
	"subagents-models",
]);

function packagePromptsDir(): string {
	return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "prompts");
}

function promptDirs(cwd: string): string[] {
	return [
		packagePromptsDir(),
		path.join(getAgentDir(), "prompts"),
		path.join(getProjectConfigDir(cwd), "prompts"),
	];
}

function readPromptFiles(cwd: string): string[] {
	const files: string[] = [];
	for (const dir of promptDirs(cwd)) {
		let entries: fs.Dirent[];
		try {
			entries = fs.readdirSync(dir, { withFileTypes: true });
		} catch {
			continue;
		}
		for (const entry of entries) {
			if (entry.isFile() && entry.name.endsWith(".md")) files.push(path.join(dir, entry.name));
		}
	}
	return files;
}

function firstNonEmptyLine(value: string): string {
	return value.split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "Prompt workflow";
}

function stringField(frontmatter: Record<string, string>, key: string): string | undefined {
	const value = frontmatter[key]?.trim();
	return value ? value : undefined;
}

function booleanField(frontmatter: Record<string, string>, key: string): boolean | undefined {
	const value = frontmatter[key]?.trim().toLowerCase();
	if (value === "true" || value === "yes" || value === "1") return true;
	if (value === "false" || value === "no" || value === "0") return false;
	return undefined;
}

function parseSkill(value: string | undefined): string | string[] | false | undefined {
	if (!value) return undefined;
	if (value === "false") return false;
	const parts = value.split(",").map((part) => part.trim()).filter(Boolean);
	return parts.length > 1 ? parts : parts[0];
}

function parseAgent(frontmatter: Record<string, string>): string {
	const subagent = stringField(frontmatter, "subagent");
	if (!subagent || subagent === "true") return "delegate";
	return subagent;
}

function loadPromptWorkflow(filePath: string): PromptWorkflow | undefined {
	const content = fs.readFileSync(filePath, "utf-8");
	const { frontmatter, body } = parseFrontmatter(content);
	const name = path.basename(filePath, ".md");
	if (!name || RESERVED_COMMAND_NAMES.has(name)) return undefined;
	const model = stringField(frontmatter, "model");
	const skill = parseSkill(stringField(frontmatter, "skill"));
	const cwd = stringField(frontmatter, "cwd");
	const chain = stringField(frontmatter, "chain");
	return {
		name,
		description: stringField(frontmatter, "description") ?? firstNonEmptyLine(body),
		body,
		filePath,
		agent: parseAgent(frontmatter),
		...(booleanField(frontmatter, "inheritContext") === true || booleanField(frontmatter, "fork") === true ? { context: "fork" as const } : {}),
		...(booleanField(frontmatter, "fresh") === true ? { context: "fresh" as const } : {}),
		...(model ? { model } : {}),
		...(skill !== undefined ? { skill } : {}),
		...(cwd ? { cwd } : {}),
		...(booleanField(frontmatter, "worktree") === true ? { worktree: true } : {}),
		...(chain ? { chain } : {}),
	};
}

export function discoverPromptWorkflows(cwd: string): PromptWorkflow[] {
	const workflows = new Map<string, PromptWorkflow>();
	for (const file of readPromptFiles(cwd)) {
		const workflow = loadPromptWorkflow(file);
		if (workflow) workflows.set(workflow.name, workflow);
	}
	return [...workflows.values()].sort((a, b) => a.name.localeCompare(b.name));
}

function shellWords(input: string): string[] {
	const words: string[] = [];
	let current = "";
	let quote: "'" | '"' | undefined;
	let escaped = false;
	for (const ch of input) {
		if (escaped) {
			current += ch;
			escaped = false;
			continue;
		}
		if (ch === "\\") {
			escaped = true;
			continue;
		}
		if (quote) {
			if (ch === quote) quote = undefined;
			else current += ch;
			continue;
		}
		if (ch === "'" || ch === '"') {
			quote = ch;
			continue;
		}
		if (/\s/.test(ch)) {
			if (current) {
				words.push(current);
				current = "";
			}
			continue;
		}
		current += ch;
	}
	if (current) words.push(current);
	return words;
}

function substituteArgs(template: string, args: string[]): string {
	const all = args.join(" ");
	return template
		.replace(/\$ARGUMENTS/g, all)
		.replace(/\$@/g, all)
		.replace(/\$\{(\d+):-([^}]*)\}/g, (_match, index: string, fallback: string) => args[Number(index) - 1] || fallback)
		.replace(/\$(\d+)/g, (_match, index: string) => args[Number(index) - 1] ?? "");
}

function parseRuntimeOptions(words: string[]): { args: string[]; agentOverride?: string; fork?: boolean; fresh?: boolean; worktree?: boolean; bg?: boolean } {
	const args: string[] = [];
	let agentOverride: string | undefined;
	let fork = false;
	let fresh = false;
	let worktree = false;
	let bg = false;
	for (let i = 0; i < words.length; i++) {
		const word = words[i]!;
		if (word === "--fork") {
			fork = true;
			continue;
		}
		if (word === "--fresh") {
			fresh = true;
			continue;
		}
		if (word === "--worktree") {
			worktree = true;
			continue;
		}
		if (word === "--bg" || word === "--async") {
			bg = true;
			continue;
		}
		if (word === "--subagent") {
			agentOverride = words[++i];
			continue;
		}
		const eq = word.match(/^--subagent(?:=|:)(.+)$/);
		if (eq) {
			agentOverride = eq[1];
			continue;
		}
		args.push(word);
	}
	return { args, agentOverride, fork, fresh, worktree, bg };
}

function splitChainDeclaration(input: string): { declaration: string; argsText: string } {
	const delimiter = input.indexOf(" -- ");
	if (delimiter === -1) return { declaration: input.trim(), argsText: "" };
	return { declaration: input.slice(0, delimiter).trim(), argsText: input.slice(delimiter + 4).trim() };
}

function splitPromptChain(input: string): string[] {
	return input.split(" -> ").map((part) => part.trim()).filter(Boolean);
}

function workflowParams(workflow: PromptWorkflow, args: string[], runtime: ReturnType<typeof parseRuntimeOptions>): SubagentParamsLike {
	const task = substituteArgs(workflow.body, args).trim();
	const context = runtime.fork ? "fork" : runtime.fresh ? "fresh" : workflow.context;
	return {
		agent: runtime.agentOverride ?? workflow.agent,
		task,
		clarify: false,
		agentScope: "both",
		...(context ? { context } : {}),
		...(workflow.model ? { model: workflow.model } : {}),
		...(workflow.skill !== undefined ? { skill: workflow.skill } : {}),
		...(workflow.cwd ? { cwd: workflow.cwd } : {}),
		...(runtime.worktree || workflow.worktree ? { worktree: true } : {}),
		...(runtime.bg ? { async: true } : {}),
	};
}

function workflowChainStep(workflow: PromptWorkflow, args: string[], runtime: ReturnType<typeof parseRuntimeOptions>): ChainStep {
	const params = workflowParams(workflow, args, runtime);
	return {
		agent: params.agent ?? "delegate",
		task: params.task,
		...(params.model ? { model: params.model } : {}),
		...(params.skill !== undefined ? { skill: params.skill } : {}),
		...(params.cwd ? { cwd: params.cwd } : {}),
	};
}

function findWorkflow(workflows: PromptWorkflow[], name: string): PromptWorkflow | undefined {
	return workflows.find((workflow) => workflow.name === name);
}

function formatWorkflowList(workflows: PromptWorkflow[]): string {
	if (workflows.length === 0) return "No prompt workflows found in package, user, or project prompts.";
	return [
		"Prompt workflows:",
		...workflows.map((workflow) => `- ${workflow.name}: ${workflow.description} (${workflow.filePath})`),
	].join("\n");
}

export function registerPromptWorkflowCommands(input: {
	pi: ExtensionAPI;
	run: PromptWorkflowRunner;
}): void {
	const { pi, run } = input;

	pi.registerCommand("prompt-workflow", {
		description: "Run a prompt template through native pi-subagents: /prompt-workflow <name> [args]",
		handler: async (rawArgs, ctx) => {
			const words = shellWords(rawArgs);
			const name = words.shift();
			const workflows = discoverPromptWorkflows(ctx.cwd);
			if (!name || name === "list") {
				pi.sendMessage({ content: formatWorkflowList(workflows), display: true });
				return;
			}
			const workflow = findWorkflow(workflows, name);
			if (!workflow) {
				ctx.ui.notify(`Unknown prompt workflow: ${name}`, "error");
				return;
			}
			const runtime = parseRuntimeOptions(words);
			try {
				if (workflow.chain) {
					const chainNames = splitPromptChain(workflow.chain);
					const chain = chainNames.map((stepName) => {
						const step = findWorkflow(workflows, stepName);
						if (!step) throw new Error(`Unknown prompt workflow in chain '${workflow.name}': ${stepName}`);
						return workflowChainStep(step, runtime.args, runtime);
					});
					await run({ chain, task: runtime.args.join(" "), clarify: false, agentScope: "both", ...(runtime.bg ? { async: true } : {}) }, ctx);
					return;
				}
				await run(workflowParams(workflow, runtime.args, runtime), ctx);
			} catch (error) {
				ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
			}
		},
	});

	pi.registerCommand("chain-prompts", {
		description: "Run prompt templates as a native subagent chain: /chain-prompts analyze -> fix -- args",
		handler: async (rawArgs, ctx) => {
			const { declaration, argsText } = splitChainDeclaration(rawArgs);
			const workflows = discoverPromptWorkflows(ctx.cwd);
			if (!declaration || declaration === "list") {
				pi.sendMessage({ content: formatWorkflowList(workflows), display: true });
				return;
			}
			const runtime = parseRuntimeOptions(shellWords(argsText));
			const names = splitPromptChain(declaration);
			if (names.length === 0) {
				ctx.ui.notify("Usage: /chain-prompts prompt-a -> prompt-b -- args", "error");
				return;
			}
			try {
				const chain = names.map((name) => {
					const workflow = findWorkflow(workflows, name);
					if (!workflow) throw new Error(`Unknown prompt workflow: ${name}`);
					return workflowChainStep(workflow, runtime.args, runtime);
				});
				await run({ chain, task: runtime.args.join(" "), clarify: false, agentScope: "both", ...(runtime.bg ? { async: true } : {}) }, ctx);
			} catch (error) {
				ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
			}
		},
	});
}


# Chain Commands Reference

This file documents how to invoke the `c-*` chain recipes defined in `~/.pi/agent/prompts/`.

## What the chains are

The `c-*` files are orchestration recipes. They do not contain new prompts; they reuse the existing `p-*` prompts and run them in sequence through the `pi-subagents` prompt-workflow adapter. Each step is executed as a forked-context subagent, so later steps can see the outputs from earlier steps.

## Available chains

| Command file | Sequence | Use it when |
|---|---|---|
| `c-design-build.md` | `p-brainstorm → p-plan → p-test → p-commit` | You want to design a feature, plan it, add tests, and commit in one go. |
| `c-fix-commit.md` | `p-summarize → p-diagnose → p-debug → p-test` | You need to understand a bug, diagnose it, fix it, and add regression tests. |
| `c-refactor-commit.md` | `p-refactor → p-test → p-review → p-commit` | You want a safe refactor with tests, review, and a commit. |
| `c-security-loop.md` | `p-secaudit → p-review → p-test` | You need a security-first review plus tests before proceeding. |

## How to run a chain

Chains are not invoked with `/c-...` in the editor, because normal slash expansion just pastes the file body into the chat. Instead, run them through the `pi-subagents` adapter with `/prompt-workflow`:

```text
/prompt-workflow c-design-build "add a rate-limiter to the API"
/prompt-workflow c-fix-commit "the checkout intermittently times out"
/prompt-workflow c-refactor-commit "src/utils.ts"
/prompt-workflow c-security-loop "auth endpoint"
```

Each chain runs asynchronously by default. Check progress with:

```text
/subagents-fleet
```

or inspect a specific run with:

```text
subagent({ action: "status" })
```

## Ad-hoc chains

You can also run the same sequence on the fly without creating a recipe file:

```text
/chain-prompts p-brainstorm -> p-plan -> p-test -> p-commit -- "add a rate-limiter"
/chain-prompts p-summarize -> p-diagnose -> p-debug -> p-test -- "intermittent timeout"
/chain-prompts p-refactor -> p-test -> p-review -> p-commit -- "src/utils.ts"
/chain-prompts p-secaudit -> p-review -> p-test -- "auth endpoint"
```

Use `--bg` or `--async` to detach an ad-hoc chain:

```text
/chain-prompts p-refactor -> p-test -> p-review -> p-commit --bg -- "src/utils.ts"
```

## Why individual prompts are the subagents

Only the `p-*.md` files are marked with `subagent: true` and `fork: true`. The `c-*.md` recipe files are pure wrappers. When `/prompt-workflow` loads a recipe:

1. It sees the `chain:` frontmatter.
2. It resolves each named step to a `p-*.md` file.
3. It runs that prompt as a forked-context subagent (`delegate` by default).
4. The output of each step is passed to the next step via the chain runner.

This keeps the chain recipes small and the individual prompts reusable as both standalone slash templates and chain steps.

## Context flow

Because each `p-*` step uses `fork: true`, every subagent inherits the parent session history. That means later steps naturally see what earlier steps produced, without needing explicit `{previous}` placeholders inside the prompt bodies.

The trade-off is higher per-step context usage. If token cost becomes an issue, a future migration could switch the `p-*` prompts to `fresh: true` and introduce explicit `{previous}` handling inside their bodies.

## Troubleshooting

| Symptom | What to check |
|---|---|
| `/c-design-build` just pastes text | Use `/prompt-workflow c-design-build ...` instead. |
| Chain step fails with “Unknown prompt workflow” | Run `/reload`, then `/prompt-workflow list` and confirm each `p-*` in the chain appears. |
| Chain does not run in background | Confirm the recipe has `async: true` in frontmatter, or add `--bg` to the command. |
| Steps seem disconnected | `fork: true` is required on each `p-*.md`. Check frontmatter. |


Now I understand the full picture. Let me also read the prompts.md doc and the p-* prompt files to see how they are configured (subagent: true, fork: true, async: true, etc.).


# Prompts

Slash-style prompts for the interactive pi session. Each file is a `p-<verb>.md` template with a frontmatter `description` + `argument-hint` and a structured body. Invoke with `:p-<verb> [arg]` or via the `:prompts` TUI picker.

## When to use which

| Prompt             | Use it when…                                                                                | Pairs well with                                         |
| ------------------ | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| **`p-plan`**       | You're about to start a non-trivial change and want a structured plan before coding.                            | `p-brainstorm` (first), then `p-refactor` / `p-test`    |
| **`p-brainstorm`** | You have a problem but no chosen approach yet; want 3–5 alternatives with trade-offs.                           | `p-plan` (after picking one)                            |
| **`p-explain`**    | You don't understand a piece of code, a concept, or an error message.                                           | `p-debug` / `p-diagnose` (if it's a problem)            |
| **`p-debug`**      | A test, build, or command is failing with a clear error. You have the error in hand.                            | `p-test` (add a regression test after the fix)          |
| **`p-diagnose`**   | Nothing is "failing" but behavior is wrong — wrong values, races, perf drift, "passes locally, broken in prod." | `p-debug` (if you find a hard failure)                  |
| **`p-review`**     | You want a code review of a diff, file, or staged change. General quality feedback.                             | `p-secaudit` (security subset)                          |
| **`p-secaudit`**   | Touching auth, input handling, secrets, network, deserialization, or any trust boundary.                        | `p-review` (broader quality), `p-test`                  |
| **`p-refactor`**   | Restructuring code without changing behavior. Safety-first, mechanical steps.                                   | `p-test` (first, as the safety net), `p-review` (after) |
| **`p-test`**       | You need to write or expand tests for existing or new code.                                                     | `p-debug` (after a failure), `p-refactor` (before)      |
| **`p-doc`**        | Writing or improving README, docstrings, JSDoc, godoc, or ADRs.                                                 | `p-summarize` (skim the code first)                     |
| **`p-commit`**     | About to commit. You want a conventional message and (optionally) a PR description.                             | `p-review` (final check before committing)              |
| **`p-summarize`**  | A file, log, diff, or document is too long to read in full. You need the gist + key details.                    | `p-plan` / `p-debug` (then dive into specifics)         |

## Decision flow

```
Got an idea or vague problem?
└─ Yes → p-brainstorm  →  pick an approach  →  p-plan  →  implement
                                                          └─ p-test  →  p-commit
Touching existing code that needs to change?
└─ p-refactor (after p-test confirms a safety net)  →  p-review  →  p-commit
Something is broken?
├─ Hard failure (error message, failing test)  →  p-debug
└─ Soft failure (wrong output, race, perf)      →  p-diagnose
                                       →  fix  →  p-test (regression)  →  p-commit
Touching a trust boundary, auth, secrets, or untrusted input?
└─ p-secaudit  (then p-review for general quality)
Need to understand a long file/log/diff quickly?
└─ p-summarize
Need to write or update docs?
└─ p-doc
Don't know what something does or what an error means?
└─ p-explain
```

## Chain recipes

The `c-*` files are not prompts you run directly in the editor. They are orchestration recipes that run a sequence of `p-*` prompts through the `pi-subagents` prompt-workflow adapter. Each `p-*` step is executed as a forked-context subagent, so later steps can see earlier outputs. Chains run asynchronously by default.

| Recipe             | Sequence                                  | Use it when…                                                               |
| ------------------ | ----------------------------------------- | -------------------------------------------------------------------------- |
| **`c-design-build`** | `p-brainstorm → p-plan → p-test → p-commit` | You want to design a feature, plan it, add tests, and commit in one go.    |
| **`c-fix-commit`** | `p-summarize → p-diagnose → p-debug → p-test` | You need to understand a bug, diagnose it, fix it, and add regression tests. |
| **`c-refactor-commit`** | `p-refactor → p-test → p-review → p-commit` | You want a safe refactor with tests, review, and a commit.                  |
| **`c-security-loop`** | `p-secaudit → p-review → p-test`           | You need a security-first review plus tests before proceeding.             |

Invoke a chain with `/prompt-workflow`, not `/c-recipe`:

```text
/prompt-workflow c-design-build --bg "add a rate-limiter to the API"
/prompt-workflow c-fix-commit --bg "the checkout intermittently times out"
/prompt-workflow c-refactor-commit --bg "src/utils.ts"
/prompt-workflow c-security-loop --bg "auth endpoint"
```

Each chain runs in the background. Watch it with `/subagents-fleet` or check status with `subagent({ action: "status" })`. If you want to run the same sequence ad-hoc, use `/chain-prompts`:

```text
/chain-prompts p-brainstorm -> p-plan -> p-test -> p-commit -- "add a rate-limiter"
```

## Conventions

- Every prompt uses `$@` for the user-supplied argument. Pass an argument at invocation time; if the request is under-specified, the prompt will ask clarifying questions.
- Output is structured (numbered sections, fixed categories) so the result is diff-friendly and skim-friendly.
- Prompts intentionally do **not** overlap with subagent workflows (`agents/`, `chains/`, `skills/`) — those are for orchestration; these are for interactive turns.
- Prefer one prompt per turn. Chain them yourself (`p-brainstorm` → `p-plan` → `p-refactor`) rather than asking one prompt to do everything.


---
description: Generate 3–5 design alternatives with trade-offs before committing to an approach
argument-hint: "[problem or feature to design]"
subagent: true
fork: true
---
Brainstorm approaches for: $@

Structure your answer as:
1. **Problem restatement** — one paragraph; what we're solving, what constraints matter, what "done" looks like
2. **Alternatives** — 3 to 5 distinct approaches. For each:
   - **Summary** — one or two sentences
   - **How it works** — the key mechanism
   - **Pros** — what it gets right
   - **Cons** — what it costs, risks, or breaks
   - **Best when** — the conditions that would make this the right pick
3. **Comparison table** — across the alternatives, score or rank on: complexity, time-to-ship, reversibility, performance, fit-with-existing-code
4. **Recommendation** — which one to pick *given the current codebase*, with the second choice as a fallback
5. **Unknowns** — questions that, if answered, would change the recommendation

Be opinionated. Surface real trade-offs, not strawmen. If the obvious approach wins decisively, say so and only enumerate 2 alternatives — don't manufacture options for the sake of a full set. If the problem is under-specified, ask clarifying questions before brainstorming.


---
description: Create a structured implementation plan before coding
argument-hint: "[task or feature]"
subagent: true
fork: true
---
Create a structured implementation plan for: $@

Follow this format:
1. **Goal** — one-sentence objective
2. **Constraints** — existing code, dependencies, tests, or assumptions to respect
3. **Approach** — high-level strategy and rationale
4. **Steps** — numbered, actionable tasks small enough to verify one at a time
5. **Verification** — how to confirm each step works (tests, logs, manual checks)
6. **Risks & Rollbacks** — what could go wrong and how to undo safely
7. **Open Questions** — anything that needs clarification before starting

Use the codebase context as needed. Do not write implementation code yet; only produce the plan.


---
description: Write or expand unit / integration tests for the given code
argument-hint: "[file path, function, or feature to test]"
subagent: true
fork: true
---
Write tests for: $@

Structure your answer as:
1. **Existing conventions** — name patterns, framework, assertion style, fixtures; match what's already there
2. **Test cases to add** — grouped as:
   - **Happy path** — the primary expected behavior
   - **Edge cases** — empty, zero, max, boundary, off-by-one
   - **Error cases** — invalid input, missing deps, timeouts, permission failures
3. **Test code** — minimal, runnable, following the project's framework (jest, pytest, go test, etc.)
4. **How to run** — the exact command, plus how to run just the new tests
5. **Coverage gaps** — anything still untestable without a refactor; flag it but do not refactor in this pass

Keep tests independent, deterministic, and fast. Prefer many small tests over one large one. If the codebase has no test setup yet, propose the smallest viable setup and call it out clearly so the user can approve before you scaffold it.


---
description: Generate a conventional commit message and optional PR description from current changes
argument-hint: "[diff, file list, or 'auto' to use git status]"
subagent: true
fork: true
---
Generate a commit message for: $@

Structure your answer as:
1. **Type & scope** — one of: feat, fix, refactor, perf, test, docs, build, ci, chore, style; scope is the affected module/subsystem
2. **Subject** — imperative mood, ≤72 chars, no trailing period, no emoji
3. **Body** — wrapped at 72 cols, explain *why* not *what*. Use bullet points for multiple distinct changes.
4. **Footer** — `BREAKING CHANGE: …` if applicable; `Refs: #123` for linked issues
5. **PR description** (if the change is non-trivial) — Summary, Motivation, Changes, Test plan, Risks

Format the final message in a single fenced block ready to copy. If the change spans multiple logical commits, propose a split with one subject per commit and the file groups that belong in each.

Read the diff before writing — never invent files, symbols, or behavior. If `git status` is empty or the diff is unclear, ask before fabricating.


Now I have complete context. The user's setup:

1. They have `p-*.md` prompts configured as subagents (`subagent: true`, `fork: true`).
2. They have `c-*.md` chain recipes with `chain:` frontmatter that list sequences of `p-*.md` prompts.
3. They invoke them via `/prompt-workflow c-design-build --bg "..."` using the pi-subagents prompt-workflow adapter.
4. The adapter runs these as subagent chains in the current pi session (not a new `pi -p` session per se), but they run **asynchronously** (`--bg`) which detaches them.

Wait, the user said "create a new pi -p session". But actually `/prompt-workflow` runs them as subagents within the current pi session. Maybe the user is confusing this with `pi -p` because the subagent child processes run headlessly similar to `pi -p`. Or maybe they have a shell alias that uses `pi -p`.

Actually, looking at the session history, the previous question was about shortcuts, and the recommendation was shell functions like `fcc() { pi /prompt-workflow c-fix-commit --bg "$@"; }`. That would indeed run a new `pi` process (interactive mode by default, not `-p`) from the shell. Wait, `pi /prompt-workflow ...` starts a new interactive pi session with that initial message. Then the `/prompt-workflow` command runs in that new session.

Hmm, but the user says "create a new pi -p session". Maybe they use `pi -p /prompt-workflow ...` in their shortcut. Or maybe they think subagents are like `pi -p` sessions.

Anyway, the core answer is: yes, you can change them to run in the foreground in the current session. Just remove `--bg`/`--async` from the invocation, or set `async: false` in the recipe frontmatter. When run in foreground, you can stop them with Esc / Ctrl+C.

But there's a subtlety: the c-*.md files themselves don't run as subagents - they are chain recipes. The chain runs via `run({ chain, task, clarify: false, agentScope: "both", ...(runtime.bg ? { async: true } : {}) }, ctx)`. If no `--bg`, the chain runs in foreground. In foreground, you can interrupt with Escape/Ctrl+C.

Also, the individual p-*.md steps have `fork: true` which means each subagent inherits parent context. They don't create new `pi -p` sessions; they are child pi processes spawned by the parent.

For the second question: is there an automatic tool that picks the most fitting subagent/prompt? Yes, the parent agent can naturally decide, or you can create a custom agent. But there's no built-in "auto-router" command. I can suggest creating a `router` agent or using natural language.

Wait, actually pi-subagents has a bundled skill that helps the parent decide. And the parent agent can use `subagent({ agent: "...", task: "..." })` naturally. But the user specifically asks about "automatic tool that would just use whatever subagent or prompt is the most fitting".

I could also suggest creating a prompt template like `p-route` or a custom agent `router` that takes the task and emits which chain to run. But the simplest is to just ask pi naturally: "Design and implement a rate limiter" and let the bundled skill decide whether to run a chain.

Actually, the most fitting answer is to create a custom agent or use a prompt template that the parent can invoke. For example, a `router.md` prompt or `auto` chain that delegates to the best c-* recipe. But pi-subagents doesn't have a built-in automatic router.

Let me also check the user's dotfiles for any existing `auto` or `router` prompt.


No files found matching pattern


---
description: Diagnose a failing test, build, or runtime error
argument-hint: "[error message, test name, or stack trace]"
subagent: true
fork: true
---
Diagnose the following failure: $@

Structure your answer as:
1. **What failed** — one-sentence summary; quote the exact error line
2. **Likely root cause** — ranked list, most probable first
3. **Minimal fix** — the smallest change that would resolve it
4. **How to verify** — the exact command or test to confirm the fix
5. **How to prevent** — a regression test, invariant, or guard to add

Be specific. If multiple causes are plausible, name each and give a quick disambiguation check (a log line, a flag, a one-line probe) to tell them apart. Do not speculate beyond what the error message and the code actually show — if you need more data, say so explicitly.


---
description: Diagnose unexpected behavior, not just failures (race conditions, perf regressions, weird output)
argument-hint: "[symptom or observed behavior]"
subagent: true
fork: true
---
Diagnose the following unexpected behavior: $@

Structure your answer as:
1. **What's wrong** — restate the observed behavior vs the expected behavior in one sentence each
2. **Where in the code** — point at the specific functions, lines, or modules most likely responsible
3. **Hypotheses** — 2–4 ranked explanations with the evidence for and against each
4. **Disambiguation** — the cheapest experiment (log, flag, breakpoint, minimal repro) that would confirm or rule out each hypothesis
5. **Likely fix** — once a hypothesis is confirmed, the smallest change that would correct it
6. **Why it wasn't caught** — what test, type, or invariant would have caught this earlier

Use this when nothing is "failing" but behavior is wrong: a value drifts, a UI flashes, a query is slow, two users see different results. Distinguish carefully between "the test fails" (use `p-debug`) and "the test passes but production is broken" (this prompt).