# pi-crews usage reference

This page holds the details that the [README](../README.md) leaves out.

## How news reaches the parent

- If the parent waits in `spawn_agent` or `wait_agent`, that call receives the news. A foreground `spawn_agent` waits at most 10 minutes. Then it returns the results so far, and the other children keep running and report as news.
- Children that have not finished are listed in `pending` with the tools they are running and for how long. A command that runs far longer than it should may never end; `cancel_agent` stops it.
- If the parent is idle, a result, question or important message starts a new turn. Plain messages do not wake the parent. They go along with the next turn.
- If the parent is working, news enters at a safe conversation boundary as one batch.
- Every piece of news is delivered once. The result of a child the parent cancelled does not wake the parent.
- If the user steers a working parent, a pending wait ends at once with `steered`. The children keep running.

## How messages reach a child

- A queued or running child receives a `send_agent` message word for word at its next step: after its current tool calls, before its next model request. Text that starts with `/` is not run as a command or template.
- A message that comes after a run's last step still reaches it, and the run continues. If the run has already completed, the message starts the child's next run. A run that is stopped (`cancel_agent`, `/crew cancel`, out of time) or fails drops the message. Its result says what happened.
- A child that asked a question or has ended resumes its conversation with the message.

Per call, a task can set:

- `context`: `fresh` (default, empty conversation) or `fork` (inherits the parent conversation up to the delegation).
- `target`: the name of another child. The new child works inside that child's worktree (for review).
- `budgetMinutes`: 1–1440. See [Run budgets](#run-budgets).

`usage` in a result counts that run the way Pi `/session` does: `{ tokens: { input, output, cacheRead, cacheWrite, total }, cost }`. A forked child does not count the parent conversation it inherited.

Limits on `notify_parent` (32 per run) and `ask_parent` (8 per run) apply. `ask_parent` is rejected if the same message contains another tool call.

## Child sessions

A child gets what a Pi CLI session gets: `AGENTS.md`, `SYSTEM.md`, skills, user extensions, settings, and the built-in CLI extensions (codemode, tool_search, MCP). MCP loads only when the child's tool list contains `mcp__` tools, because MCP connects to servers at session start. If the user turns a built-in extension off with the `extensions` setting, it is off in children too. Built-in tools work relative to the child's working directory.

- **Approval prompts:** Approval and policy extensions work inside children. Prompts appear on the parent screen with `[child name]`. A child cannot change the parent's status line, widgets or theme.
- **Models and auth:** A child's selected chat model connects through a provider in the parent's model registry. Children can also use the parent's classifier and image models through codemode when that tool is enabled. The parent handles auth for every request; credentials are not copied into child storage. The model catalogs are captured when the child session opens, including non-chat providers that differ from its chat provider. New providers or catalog changes become available when a child session is opened again; authentication still resolves on every request.
- **Non-chat operations:** Use Pi's `models.getAvailableOfType()`, `models.classify()` and `models.generateImages()` from codemode. Classifier image input requires Pi 1.1.0 and a model whose `input` includes `image`. Usage reported by these operations counts toward the child's run cost. Cancelling a child also cancels its in-flight model operations. The agent's `model` field still requires a physical chat model.
- **Extension cleanup:** Closing a child session first emits Pi's `session_shutdown` event, then disposes the session. MCP transports and other extension resources are cleaned up by their own Pi handlers. In-memory children keep their idle sessions for later messages; their resources close when the parent runtime ends.
- **Shared directory rule:** A child without a worktree shares the directory with the user and other children. It is told not to revert changes it did not make.
- **Concurrency:** Up to 4 children run at once. The rest wait their turn. A child waiting for the parent's answer does not take a turn, so queued children go on meanwhile.
- **Answers go first:** A child whose question was answered is first in line. If no turn is free, the next running child to finish a step (after its tool calls, before its next model request) gives it its turn and waits, next in line after answered children. It continues the same run with its conversation intact; a message sent to it meanwhile reaches it when it continues. A step that never ends (a command waiting forever) keeps its turn until the step ends.

## Run budgets

Each run gets a work-time budget. The default is 30 minutes. `budgetMinutes` in the agent definition changes the default. `budgetMinutes` (1–1440) in a `spawn_agent` or `send_agent` call overrides the agent definition. The child is told its budget when it starts.

- A child keeps the budget fixed when it is admitted. Editing the agent file does not change children that already exist. The only way to change a budget is `budgetMinutes` on a call.
- A `budgetMinutes` given for a run that has already started has no effect.
- Time spent waiting for other people is not counted: the user answering an approval prompt, the parent answering a question, and other children using every turn after an answer or after the child stepped aside. Time waiting to start a run is not counted either: the budget starts with the run.
- Work time before and after a question adds up. Answering does not restart the budget.
- When one fifth of the budget is left, the child is told to wrap up. The step it is in is stopped first, so a command that never ends cannot keep the notice from it. A stopped command's output so far stays in the conversation. The wrap-up time is 1–5 minutes and never more than half the budget.
- Children are told to give commands that might not finish on their own a bash timeout. Pi's bash has no default timeout.
- If a run ends without a final answer, its result is the last text the child wrote, if any.
- When the budget is used up, the run stops as `timed_out` (error code `EXECUTION_TIMEOUT`). Use `send_agent` with `budgetMinutes` to continue the same child, or spawn a new child. `cancel_agent` stops a running child for good. Changes and the worktree stay.

## Git worktrees

An agent with `isolation: worktree` starts from a snapshot of the current working tree. Uncommitted changes and untracked files are included (ignored files are not). pi-crews does not change your `HEAD`, index or files. A worker can still change your files with `bash`; see below.

### Copying ignored paths into a worktree

A snapshot leaves ignored files out, so a worker that needs `node_modules` or build output starts without them. `worktreeIncludes` lists repository-relative paths that a new worktree gets a copy of:

```yaml
# .pi/agents/worker.yml: give the bundled worker its dependencies in its worktree
isolation: worktree
worktreeIncludes: [node_modules]
```

- After `git worktree add` and before the child starts, each listed path that exists (as a link or otherwise) in your checkout and that Git ignores is copied to the same place in the worktree. The copy shares blocks with your checkout on file systems that support copy-on-write, and falls back to a normal copy on those that do not.
- Copying takes time: about 10 seconds per 20,000 small files (measured on macOS 27 with an APFS SSD and Node 25.9), and copy-on-write did not make many small files faster. The child starts only after the copy, and the copy counts against the run's working time, so a large list delays the spawn and eats into the budget.
- A listed path that does not exist is skipped: dependencies may not be installed yet. A path that Git does not ignore is skipped too, because the snapshot already has it. This includes a folder that holds a tracked file: with `build/*` ignored and `build/.keep` tracked, Git does not call `build` ignored, so listing `build` copies none of the ignored files inside it. List the ignored paths themselves, such as `build/output`; listing both is fine, because a path inside a folder that was copied is not copied twice.
- Nothing is copied by default: without the field, a worktree has no ignored files. `worktreeIncludes` has no effect without `isolation: worktree`, because a child that shares the project folder already sees those paths.
- The worktree is the same repository, so the copies stay ignored. They are not part of a result patch or commit, and `/crew remove` deletes them with the worktree.
- `node_modules` is the usual entry. `.env` is copied only if you list it. **Warning:** listing a secret (`.env`, a key file, a database) copies it into the worktree folder under `~/.pi/agent/crews`, outside the project. The copy is readable on disk for as long as the worktree exists.
- Symbolic links are copied as links, not followed. A relative link points to the same place inside the worktree; an absolute link still points to its original target.
- A copy that fails stops the child's start and says which path failed.

When a run ends, all changes in the worktree are kept in two forms:

- **Patch file:** `…/runs/<n>/changes.patch`
- **Result commit:** `refs/pi-crews/<parent session id>/<child name>/<n>` in the project repository. `artifacts.commit` in the result is this commit. The patch can be rebuilt even if the records folder or patch file is deleted.

Each run's patch holds all changes since the worktree was created, not only that run's. Apply only the latest patch. If you already applied an earlier one, apply the difference between the two result commits instead.

```sh
git apply --check --binary <patchPath>
git apply --binary <patchPath>          # on conflict: git apply --3way --binary <patchPath>

git for-each-ref refs/pi-crews          # result commits that still exist
git diff --binary <baseCommit> <commit> > changes.patch   # rebuild a patch
git diff --binary <earlier commit> <commit> | git apply --binary   # after applying an earlier patch
```

While a worker runs, no other child can use its worktree. After it ends, several reviewers can enter with `target`. If you call a reviewer again with `send_agent`, it is told the path of any new patch. A child counts as using the worktree until its session has fully closed, including the shutdown code of its extensions.

Each worktree run also compares your checkout before and after. When the run starts, and again after its session has closed, pi-crews lists every non-ignored path (tracked and untracked, outside `.gitignore`) and notes each one's file metadata: type, size, modification and change times, inode and mode. It does not read file contents, so clean filters never run, and it writes nothing into your repository. Symbolic links are described by their own metadata, not followed; a submodule or an untracked repository nested in the checkout is described by its folder metadata only, so changes inside it are not seen. The result lists, in `artifacts.sourceChanges.paths`, the paths whose metadata changed between the two readings, sorted and capped at the first 20 ([limits](#limits)); `omitted` counts the rest. The reading costs time in proportion to the number of paths: about 2.4 s for 100,000 tracked files. Because metadata is compared, a change that keeps the size and timestamps identical is not seen. This is an observation, not a verdict: other children, the main agent, or you may have changed those files in that interval. A path whose metadata is the same in both readings is not listed, so a change made and undone within the run is invisible. Ignored files are not checked. A run whose result or patch could not be saved fails without `artifacts`, so it carries no `sourceChanges` either. If the check cannot be made, or no reading from the run's start exists, the result carries `artifacts.sourceChanges.error` instead; the run's status does not change.

The `write` and `edit` tools of a child that works in a worktree refuse a path outside that worktree, following symbolic links before they compare. The block reason names the worktree. The child is told to ask the main agent for changes elsewhere. This is a guard against mistakes, not a sandbox: `bash` can write anywhere.

### Removing a worktree

`/crew remove` deletes a worktree only if nothing that Git holds for it would be lost. All of this must be true:

- **Files:** every file in the worktree equals the starting snapshot or the latest result. Git is made to read every file from disk for this check, whatever your Git settings say about skipping files (sparse checkout, `core.fileMode`, `core.ignoreCase`, file system monitor, untracked cache).
- **Index:** the worktree's own staging area holds nothing beyond its files. Content that was staged and then overwritten on disk keeps the worktree.
- **Commits:** `HEAD` and every commit in the worktree's `HEAD` reflog, on both sides of each entry, are the starting snapshot, a saved result, or older than one. A commit that was made and then reset away keeps the worktree, even after its own reflog entry was deleted or expired. A repository that stores references as `reftable` keeps no reflog file pi-crews can read; there, a worktree with any reflog entry is kept, with the message "cannot check this worktree's commit history".
- **Submodules:** the worktree tracks no submodule that has files in its folder. Git keeps a submodule's own files and staging area outside what pi-crews can compare, so `remove` refuses and names the submodule, as `git worktree remove` does. A submodule that was never set up (an empty folder) does not count. A tracked submodule whose folder is missing or cannot be read is refused too, because that means pi-crews misread its name.
- **Ownership:** the folder is inside this session's folder, and Git sees it as a worktree of the project repository with that folder as its top level. Otherwise the command fails with `WORKSPACE_UNAVAILABLE`, names the folder and changes nothing. The check runs again right before the delete.
- **Not in use:** no child is using it, and none is still shutting down. A worktree is also reserved for the whole removal: a child cannot start in it while its removal waits for its turn. A worktree of another session is also kept while that session is open in another Pi process (see [the section below](#worktrees-of-other-sessions)).

When one of these fails, the command fails and says why. There are three different failures:

- `WORKSPACE_DIRTY`: work would be lost (files, staged changes, commits, a populated submodule). The message says which.
- `WORKSPACE_UNAVAILABLE`: the folder is not this session's worktree of the project repository. It names the folder and changes nothing.
- `WORKSPACE_BUSY`: a child is using the worktree, still shutting down, it is already being removed, or another Pi process is changing worktrees of this repository.

`/crew remove` without a name removes what it can and tells you, for each worktree it keeps, which of these is the reason.

A removal that deleted the worktree folder but failed to delete its result refs is unfinished. The next `/crew remove`, with the name or without, deletes the refs that remain; a removal that fully finished is not run again.

What `/crew remove` does **not** keep:

- **Ignored files** (`node_modules`, build output, `.env`). They are deleted with the worktree, and are not checked.
- Git repositories nested inside ignored folders.
- Link types that need `core.symlinks=false` (a link stored as a plain file).
- Content that a clean filter changes when Git reads it. Files are compared as Git compares them, the same standard as `git stash` and `git worktree remove`.

### Worktrees of other sessions

Sessions that were deleted, in-memory, or forked leave worktrees under `~/.pi/agent/crews/` and `refs/pi-crews/*` in the repository that no session can find by itself. The `/crew` screen and report list them. They are never swept automatically: `/crew remove <session id>/<child name>` or the screen removes one when you ask.

- **Liveness:** a session folder holds one owner file per Pi process that has it open. While any of them names a live process, the worktree is listed as in use and is not removable. An owner file from another host counts as live too, because its process cannot be checked, and a session folder that cannot be read counts as in use rather than as empty. The reason names the process id and the owner file path, so a stale file (a reused process id, for example) can be deleted by hand.
- **Safety:** the same checks as above run. A result ref also counts as saved only when its patch file `runs/<n>/changes.patch` exists and rebuilds the result exactly from where the worktree started. An empty patch of an unchanged run passes; a truncated patch fails. If any result ref of that child has no patch file, or the highest result's patch is incomplete, the worktree and its refs are kept with "result N has no patch file" or "result N has an incomplete patch file". Without the parent session's record, the latest result is the highest-numbered `refs/pi-crews/<session id>/<child name>/<run>` ref, and the base is that result commit's parent. With no result ref, the base is the worktree's `HEAD` only when its `HEAD` reflog shows nothing but the initial checkout; otherwise it is kept with "cannot tell where it started". A repository that stores references as `reftable` has no reflog file pi-crews can read; a worktree with a result there is listed as not removable with "cannot check this worktree's commit history".
- **A folder that is already gone** is listed for the result refs that remain; it is removed only when every result ref has its patch file, and then only those refs are deleted.
- Removing deletes the worktree folder and its result refs. The child folder, its runs, patches and sessions stay, and session folders are never deleted.
- A session's own worktree is never removed through this path; `/crew remove <child name>` handles it.

### Disk used by worktrees

The `/crew` screen shows the disk used by all worktrees in one line, "Disk used by worktrees: <total>", that stays on screen, and below it one row per worktree with its size. Every row is reachable: move the cursor down past the cleanup rows, and a list that does not fit scrolls with the cursor. The figure is the space the file system has allocated (not the sum of file sizes), measured when the screen opens and again on each open. It shows `measuring…` until it is ready. Symbolic links are not followed, and a file with several hard links counts once. A worktree that was removed or is missing from disk shows nothing. Measuring runs in the background and does not slow a run, the panel or the report. The figure is not stored in the session record and is not in the `/crew` report.

Below that, "Worktrees of other sessions" lists one row per worktree left under another session's folder of this project, with its session id, child name, size and whether it can be removed now (see [Worktrees of other sessions](#worktrees-of-other-sessions)). Press Enter on a removable row, then Enter again to remove it.

### Parallel starts and other Pi processes

Git does not lock its list of worktrees, so two `git worktree` commands at the same time can fail ("failed to read .git/worktrees/wN/commondir"). pi-crews runs its `git worktree add` and `git worktree remove` commands one at a time per project repository, across all crews in one Pi process and across Pi processes that share the agent directory. The cross-process lock is a file under `~/.pi/agent/crews/.locks/`, named after the repository's Git directory; it is never written into the user's repository, which may be read-only. Lock files are written complete (a temporary file is hard-linked into place), so a crash never leaves an empty lock, and a lock left by a process that is no longer alive, or older than ten minutes, is taken over.

When another process holds the lock, pi-crews waits up to 60 seconds and then fails with `WORKSPACE_BUSY` saying that another Pi process is changing worktrees of this repository. Run the request again shortly. A run cancelled while it waits ends as cancelled, and no worktree is made.

Two waiters never both take over the same stale lock (see `src/repo-lock.ts`). A lock file whose record is damaged is taken over once it is older than ten minutes.

## Agent definitions

One Markdown file (`<name>.md`) is one agent. The file name is the agent name (`^[a-z0-9][a-z0-9_-]{0,63}$`). The format matches Pi's subagent example (`examples/extensions/subagent`) plus pi-crews fields, so both extensions can share one folder. Lookup order, with a later `.md` replacing an earlier one of the same name:

1. Package `agents/`
2. `~/.pi/agent/agents/`
3. `.pi/agents/` of a trusted project (only the current folder, like other Pi project resources)

A `<name>.yml` file in the user or project folder changes only the settings of an agent. It is a plain YAML mapping with the fields below, and has no frontmatter and no instructions. The instructions always come from the `.md` beneath it, so later fixes to a bundled agent's instructions still reach you.

```yaml
# ~/.pi/agent/agents/scout.yml
model: openrouter/z-ai/glm-5.3-flash
thinkingLevel: null   # null clears a setting: back to "same as the main agent"
```

- **Layers:** Folders apply in the order above. Inside a folder, `<name>.md` is read first, then `<name>.yml` on top of it. A `.yml` changes whatever agent of that name exists so far, from an earlier folder or from the `.md` next to it. A `.md` and a `.yml` side by side are therefore not a conflict.
- **Fields in a `.yml`:** A field you leave out keeps the value from beneath. A field set to `null` clears it, so the default applies. `name` is optional and must equal the file name.
- **No `.md` beneath:** A `.yml` with no agent of that name beneath it is invalid, like a `.md` with an invalid value.

| Field | Value | If omitted |
|---|---|---|
| `name` | Must equal the file name | File name |
| `description` | One line shown in the parent's agent list | Empty |
| `tools` | Explicit names or patterns, as a list (`[read, bash]`) or comma string (`read, bash`). If a listed tool is missing, the child fails with `TOOL_UNAVAILABLE` | Parent's active tools |
| `model` | `provider/model-id` or `model-id` | Parent's current model |
| `thinkingLevel` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` | Parent's current level |
| `isolation` | `worktree` | Shared directory |
| `worktreeIncludes` | Repository-relative paths that a new worktree gets a copy of, as a list (`[node_modules]`) or comma string (`node_modules, .env`). They are copied only where Git ignores them, and only with `isolation: worktree`; otherwise the field has no effect | Nothing |
| `budgetMinutes` | Integer 1–1440 | 30 |

- **Tool modifiers:** `+name` and `-name` are not supported in agent files. They fail with `CONFIG_INVALID` before children start, and cannot be saved by `/crew`. Use an explicit list, or omit `tools` to inherit the parent's active tools. Pi's CLI and `defaultTools` setting still support modifiers.
- **Unknown fields** are ignored, as in other Pi resources. A misspelled field name raises no error.
- **Invalid values:** A `.md` or `.yml` with an invalid value drops out of the list. Requesting that agent fails with `CONFIG_INVALID` and says what value is expected.
- **Finding the `model`:** First an exact `provider/model-id` match, then a model with exactly that id. No match fails with `MODEL_NOT_FOUND`. More than one match fails with `MODEL_AMBIGUOUS`.
- **Thinking level:** If the model does not support the level, the whole request is rejected and no child starts.

Edit files by hand, or use `/crew` in the TUI. Hand edits apply after `/reload`.

## TUI panel

Under the input box, a panel shows running children. Children waiting for approval and children that asked a question come first. When all children are done, the panel shrinks to one line, "Crew: 2 finished  down look back". The line disappears when the user starts typing and returns when another child finishes. You can still reach finished children with ↓.

Each row shows one status. "Waiting for your approval" means an approval prompt is open. "Retrying 2/3" with the reason means a model request failed and Pi is retrying.

| Key | Action |
|---|---|
| ↓ (empty input) | Enter the panel. Finished children are listed. |
| ↑ / ↓ | Pick a child. ↑ on the first child returns to the input. ↓ on the last child stays. |
| Enter | Open that child's conversation full screen. |
| Esc or any letter key | Return to the input. The letter goes into the input. |

Movement is one step per press and repeats while held. This works with plain terminals, application cursor mode and the kitty keyboard protocol. Your shell (zsh, bash, fish) has no effect, because Pi reads the keyboard directly.

While you are in the panel, row order does not change. A child whose state changed stays in place and a new child is added at the bottom. The next time you enter, rows are sorted by urgency again.

### Child conversation screen

The screen looks the same as in the child's own Pi session. It uses Pi's message and tool components, tool renderers from extensions, and your display settings (`hideThinkingBlock`, `outputPad`, `showImages`, `codeBlockIndent`). While a child runs, you see the answer being written and live command output. Extension tool rendering is kept after the child ends. A child restored after reopening Pi shows Pi's default rendering, because no extension was loaded for it. Questions and messages to the parent show as "Asked the main agent" and "Told the main agent" with their text, and messages from the parent as "From the main agent".

The top line shows the position on the right (`2 of 6`). The bottom shows the same footer Pi shows under the main agent: working folder and Git branch, tokens (↑ input, ↓ output, R cache read, W cache write), cache hit rate (CH), cost, context usage, model and thinking level. Finished children show the same values from their recorded session. For forked children, tokens and cost exclude the inherited parent conversation, but context usage includes it because it really occupies the child's context.

| Key | Action |
|---|---|
| ↑ / ↓ | Scroll one line |
| Space (also PageDown) | One screen down |
| `b` or `B` (also PageUp) | One screen up |
| `g` / `G` (also Home / End) | Top / bottom |
| ← / → | Previous / next child in panel order. The panel cursor moves too. |
| ctrl+o | Expand or collapse tool output |
| `s` → Enter | Stop only this child. Changes and records stay, and the result reaches the parent as news. |
| Esc | Back to the panel. The cursor is on the child you saw last. |

At the bottom, the screen follows new output. If you scroll up, the position (`Top`, `26%`) shows before the key hints, and "New output below." appears when output arrives. Press `G` to follow again. Going to another child with ←/→ and back keeps your reading position. Key hints wrap per hint on narrow terminals.

On Pi 1.1.0 and later, shell results with a recorded execution duration show Pi's `Took` value after reopening, rather than time measured since opening the view. Results recorded by older Pi versions keep Pi's fallback behavior.

The screen is view-only. Users cannot message a child directly. The parent agent instructs children. When another window such as an approval prompt takes the keyboard, the conversation screen closes and the panel drops its selection, so the prompt is never hidden. Keys follow Pi's key settings (`tui.select.*`, `tui.altScreen.pageUp/pageDown/top/bottom`, `app.tools.expand`). The letter keys above always work too.

### Terminal program status

Pi 1.1.0 and later report `working`, `blocked`, `done`, and `error` to terminals that support OSC 7501. These states describe the parent session, not the whole crew: `done` can appear while background children still work. Child approval dialogs use the parent's UI and participate in Pi's blocked-state reporting. The crew panel remains the source of child state.

pi-crews does not write terminal program status independently, because it would compete with Pi's own reporter. Whole-crew reporting needs a Pi API that combines extension activity with parent activity; Pi 1.2.0 does not expose one. The new `agent_settled.aborted` field does not change the existing rule that stopping a parent leaves background children running.

## `/crew` screen

In the TUI, `/crew` opens the screen for setting up and cleaning up the crew. At the top it shows the crew logo and "Your crew. Your rules. Their work." If the terminal is shorter than 24 rows or narrower than 40 columns, it shows only the one-line title `Crew` to leave room for the list. Live child state is not here. The panel shows it.

- **Agents:** Pick an agent to change its settings, or choose `+ New agent` at the top.
- **Cleanup** appears below the list only when needed. Both need a second Enter.
  - `Stop all children`: when children are running. Same as `/crew cancel`.
  - `Remove finished worktrees`: when worktrees remain. Same as `/crew remove`. It removes only worktrees that hold no work outside their latest result ([what is checked](#removing-a-worktree)), and says why it kept the others. Files that Git ignores are deleted with a worktree.
- **Disk used by worktrees** appears below the cleanup when worktrees remain: the total, then each worktree's size. Move the cursor down to scroll a long list ([details](#disk-used-by-worktrees)).
- **Worktrees of other sessions** appears after the disk list when another session left one. Each row shows the session id and child name, its size, and whether it can be removed now. Enter on a removable row asks once more, then removing it is the same as `/crew remove <session id>/<child name>` ([details](#worktrees-of-other-sessions)). If the list cannot be read, the screen and the report show one line saying why, and everything else stays.

Outside the TUI, `/crew` prints a report of the children and agents are edited as files.

| Setting | How you choose | Who validates |
|---|---|---|
| Description, Instructions | One-line input, Pi's multi-line editor (ctrl+g opens an external editor) | — |
| Model | Searchable list of chat models from providers you are logged in to | Pi model registry |
| Thinking | Only levels the model supports | Pi (`getSupportedThinkingLevels`) |
| Tools | Checklist of tools in this session. Other names in the file (patterns, tools from another host) are kept. | Pi (a missing tool fails with `TOOL_UNAVAILABLE` when the child session opens) |
| Workspace | Shared folder / own Git worktree | pi-crews |
| Time budget | Number. Outside 1–1440, Enter does not save and the reason shows while typing. Empty means the default 30 minutes. | pi-crews |

- **A choice saves at once.** There is no "Save?" step. While saving, keys are ignored and the screen says what it is doing ("Saving..."). Esc goes back one screen without changing anything. With a search term, the first Esc clears only the term.
- **Lists:** ↑/↓ stop at the ends, like the panel. The bottom line shows the keys you can use now, in Pi's style (`enter save  escape back`).
- **"Same as main agent"** clears the field, so the parent's current value is used.
- **Readiness:** The top line of an agent screen says whether spawning works now. It runs the same checks as `spawn_agent` (model lookup, thinking support, auth) with the parent's current model and level. For example, if a model change makes the thinking level invalid, it shows "Cannot start now: …".
- **Bundled agents:** Package files are never changed. A change to a setting writes only that setting to `~/.pi/agent/agents/<name>.yml`, so the bundled instructions stay in use and later fixes to them reach you. The list shows such an agent as "Edited built-in". Changing the Instructions creates `~/.pi/agent/agents/<name>.md` as a copy of the bundled file with your new instructions. From then on you keep the instructions yourself, and the `.yml` stays on top of that `.md` and keeps applying its settings. Delete the files to get the bundled agent back.
- **New agent:** Asks for name, then description. Name rules and duplicates (including bundled agents) are checked while typing. In a trusted project you choose "all projects (`~/.pi/agent/agents`)" or "this project only (`.pi/agents`)". Model and tools start as "same as parent".
- **File preservation:** On save the file is read again and only changed fields are edited. Fields used by other extensions and YAML comments stay. Before anything is written, the agent that the changed files would make is built the way it is loaded, from every folder up to the one being changed. If that agent would be invalid (for example a damaged `.md` or `.yml` beside the one being changed, or in a folder beneath it), nothing is written. Which file takes a change depends on the agent. A bundled agent: settings go to the user `<name>.yml`, instructions to the user `<name>.md`. An agent with a `.yml` on top: settings go to that `.yml`, instructions to the `<name>.md` in the same folder (copied from the `.md` the instructions come from if it is not there yet). Any other user or project agent: its `.md` is edited in place. If the `.md` beneath a `.yml` was removed meanwhile, the change is refused with "no longer exists" and nothing is written.
- **Delete:** Only user and project agents can be deleted. Delete removes exactly the files the confirmation lists, then whatever they replaced (the bundled agent, or your own agent for a project one) applies again. Normally these are the `.md` and/or `.yml` of the agent in the folder where it comes from. For an agent that cannot be used because its `.md` is invalid, the list also includes the `.yml` files attached to it in later folders, so none is left to apply to the agent that takes over. The screen says what takes over, then needs a second Enter.
- **Existing children:** Changed settings apply to children spawned afterwards.
- If another window such as an approval prompt takes the keyboard, the `/crew` screen closes. Saved changes stay. Text being typed is lost.

## User commands and templates

- `/crew`: In the TUI, opens the screen above. In other modes, prints a report with each child's state, questions, cost, and records and worktree paths. The report is shown on screen only and is not sent to the model.
- `/crew cancel`: Stops all children. File changes and records stay.
- `/crew remove [name]`: Removes a worktree only if no file, staged change or commit in it is missing from the latest result ([details](#removing-a-worktree)). Ignored files are deleted with it. It also deletes the result commit ref, so run it after applying the patch. The patch file stays. Without a name it removes every worktree that qualifies. `/crew remove <session id>/<child name>` removes a worktree that another session left behind, after the same checks ([details](#worktrees-of-other-sessions)).
- `/crew-ship <change>` and `/crew-review [focus]`: see the README.

The parent model gets the agent list and delegation guidance in the `crews_agents` section of its system prompt. Each agent's line shows whether it works in its own worktree, and the `model` and `thinkingLevel` its file sets. An agent without them uses the parent's. Sessions with `spawn_agent` turned off have no such section.

## Instructions that models receive

Each rule has one owner, so a change to a rule is made in one place.

| Surface | File | Owns |
|---|---|---|
| Parent guidance (`crews_agents` section) | `src/index.ts` | When to delegate, how to brief, accepting results, independent review of changes, integration |
| Shared child rules | `src/crew/child-session.ts` | The worktree, scope, escalation, final report, working time, `notify_parent` and `ask_parent` |
| Role instructions | `agents/*.md` | What one role aims for and the evidence it must give. They do not repeat the shared rules |
| Workflow templates | `prompts/*.md` | The order of steps for `/crew-ship` and `/crew-review`. They refer to the parent guidance and the reviewer's report instead of redefining them |
| Tool descriptions | `src/contract.ts` | What each tool does and returns |

These are requests to a model, not guarantees. pi-crews does not check that the revision a reviewer examined is the revision the parent applies, does not record whether a finished run met its goal (`completed` only means the run ended), and does not stop `bash` from writing in a read-only agent. The prompts tell the model to do the checks that pi-crews does not. `test/prompts.test.ts` checks that the prompts are delivered, name only existing tools and keep one owner per rule. It cannot show that a model follows them; `evals/prompt-scenarios.md` lists the scenarios to run with a real model after a change.

## Storage

Records live in the Pi agent directory, not in the project. So `git clean -fdx` cannot delete them, and children work in read-only projects. The project folder name follows Pi's session folder rule (`~/.pi/agent/sessions/--Users-me-app--/` ↔ `~/.pi/agent/crews/--Users-me-app--/`).

```text
~/.pi/agent/crews/--<project path>--/<parent session id>/<child name>/
  runs/<n>/result.md       full text of the run result
  runs/<n>/changes.patch   patch of a worktree child
  sessions/                the child's Pi session files
  worktree/                the worktree child's workspace
~/.pi/agent/crews/.locks/  cross-process locks for `git worktree` commands
```

A session folder also holds one `.owner-<token>` file per Pi process that has the session open (see [Worktrees of other sessions](#worktrees-of-other-sessions)); each is removed at shutdown, by the process that wrote it. The folder itself is created at the first spawn, together with the owner file; a resumed session claims its existing folder at start, and also the folders of restored children that live under an earlier project path. A session that never delegates leaves no folder. If the owner file cannot be written, the first spawn fails with an error and does not create worktrees; a resumed start warns the user and continues. The child list and state are recorded in the parent session. When Pi reopens the session, they come back. A forked or cloned session does not get them: its children stay with the session it was forked from, and you can spawn children with the same names again. Asking such a child by name gives an error that says it is not in this session. Moving between branches with `/tree` keeps the children of the session. A run that was in progress is reported as `RUNTIME_LOST`. You can continue recorded conversations with `send_agent`.

A session that is never reopened again leaves its worktrees and result refs behind. Another session of the same project lists them under "Worktrees of other sessions" and can remove them on request; nothing is removed automatically, and session folders themselves are never deleted.

## Limits

| Item | Value |
|---|---|
| Tasks per call | 8 |
| Concurrent runs | 4 |
| Task description | 16 KiB |
| Message, question, answer | 4 KiB |
| Result text | 8 KiB per result, 24 KiB total per response (longer text is cut and the full text goes to `outputPath`) |
| Whole response | 32 KiB (extra messages go in the next batch) |
| Foreground `spawn_agent` wait | 10 min |
| Source-checkout paths in a result | 20 (`omitted` counts the further changed paths) |
| Cancel confirmation wait | 5 s |

## Constraints

- Worktrees and tool lists are not an OS sandbox. `write` and `edit` refuse paths outside a child's worktree, but `bash` can reach any path with the same account permissions ([details](#git-worktrees)).
- A worktree result's `sourceChanges` compares file metadata of your checkout. It does not see changes elsewhere on disk, inside submodules or nested repositories, or in ignored files, and it cannot tell who made a change.
- Child cost shows in results, the panel, the child conversation screen and the `/crew` report outside the TUI. It is not part of Pi's `/session` total.
- A child loads extensions found in user settings. An extension passed only for one run with `pi -e` does not reach children. An extension's `session_start` runs for every child.
- Automatic wake-up starts a parent turn even while the user is typing. The text being written is kept.
- Worktrees live outside the project. Settings in folders above the repository root are not visible there, and neither are ignored files (`node_modules`, `.env`) unless the agent lists them in `worktreeIncludes` ([details](#copying-ignored-paths-into-a-worktree)). Otherwise the worker installs dependencies itself if needed.
- A worktree child cannot start in a read-only repository, because `git worktree add` writes to `.git`.
- Only Pi processes that share the agent directory take turns on `git worktree` commands. A `git worktree` command that another program runs at the same moment can still fail ("failed to read .git/worktrees/wN/commondir"); run it again ([details](#parallel-starts-and-other-pi-processes)).
- `/crew remove` deletes ignored files without checking them ([what else it does not keep](#removing-a-worktree)).
- `refs/pi-crews/*` shows in `git log --all` and is pushed by `git push --mirror`. A normal `git push` does not push it.

## Source layout

```text
extensions/
  pi-crews             Entry file Pi reads. Pi's settings screen shows this file name as the extension name, so it only re-exports src/index.ts
src/
  index.ts             Pi extension entry: parent tools, /crew command, renderers, Pi event wiring
  contract.ts          What models and users see: tool schemas, limits, result types, error codes
  pi.ts                Knowledge about Pi that Pi does not publish: supported versions, session folder naming, CLI built-in extensions, thinking levels, drawing a conversation with TUI components, status footer of closed sessions
  agents.ts            Agent file lookup, validation and saving (create, edit, delete), resolving model, tools and budget against the parent
  crew-screen.ts       The TUI /crew screen: agent list, settings, pickers, crew cleanup. Pi and agents.ts decide what is valid; this file only shows it
  worktrees.ts         Git snapshot, worktree creation and copied ignored paths, result commit and patch, source checkout readings, safe removal, worktrees of other sessions
  layout.ts            Where a session's records, worktrees, patches and result refs live: every path is built here
  repo-lock.ts         Cross-process lock for git worktree commands on one repository
  owner.ts             Which Pi processes have a crew session open (owner files)
  process-file.ts      A file that names a live process: written whole, judged live, released only by its owner
  disk-usage.ts        Disk space of the worktrees, measured with Node's file system API, for the /crew screen
  presentation.ts      All text the user sees: tool calls and results, news, /crew report, panel rows, footer (design principles in the file's first comment)
  panel.ts             TUI panel: when the panel gets keys, scrolling, conversation screen frame (drawing is Transcript in pi.ts)
  crew/
    crew.ts            Crew: public behavior, run lifecycle, news delivery
    child.ts           Child state model and its record in the parent session (save, restore)
    child-session.ts   Child Pi session setup: open or fork a conversation, provider link, UI, collaboration tools
    run-budget.ts      Run budget clock, excluding waiting time (approvals, questions)
    news.ts            Packs undelivered news into one size-limited batch
agents/                Bundled agent definitions
prompts/               /crew-ship and /crew-review templates
test/                  Product tests (pi.test.ts pins pi.ts to real Pi behavior; performance/: measurements; live/: real pi CLI)
```

When you upgrade Pi, check `src/pi.ts` and `test/pi.test.ts` first. Keep version-specific knowledge there, and use Pi's exported components rather than reproducing their rendering. The load-time check accepts Pi >=1.0.4; that is not a guarantee for untested future versions. CI tests 1.0.4 and 1.2.0. Also run `test/models.test.ts` and `test/mcp.test.ts` for parent-authenticated non-chat operations and extension cleanup. A scheduled `.github/workflows/pi-compat.yml` run tests each new Pi release and opens the baseline-bump PR (an issue when a release fails); the review after a bump still checks `src/pi.ts`.

Pi installs the package's `yaml` dependency when it installs the package from npm or Git. A local checkout is loaded in place with nothing installed, so run `npm ci` in it (README, Development).

The child's message assembly, fork-relative usage view, closed-session footer adapter, work-time budget, and worktree coordination remain crews responsibilities: Pi 1.2.0 does not provide equivalent public interfaces. The child view's scrolling also remains unchanged. Replacing it with `ScrollView` should be a separate change that preserves per-child reading position, follow-end behavior, focus, and keyboard controls.

Optional TUI check with a fake model (no cost): `node --import tsx --test test/live/tui.test.ts` uses tmux to check the panel in a real `pi` TUI.
