# Extension examples

> Reference extension packages: subagent agents, plan-mode, doom-overlay, dynamic resources skill, and auto-commit-on-exit.

- Repository: earendil-works/pi
- GitHub: https://github.com/earendil-works/pi
- Human docs: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1
- Complete Markdown: https://grok-wiki.com/public/docs/earendil-works-pi-7860a70e44d1/llms-full.txt

## Source Files

- `packages/coding-agent/examples/extensions/README.md`
- `packages/coding-agent/examples/extensions/subagent/README.md`
- `packages/coding-agent/examples/extensions/subagent/agents.ts`
- `packages/coding-agent/examples/extensions/plan-mode/README.md`
- `packages/coding-agent/examples/extensions/doom-overlay/README.md`
- `packages/coding-agent/examples/extensions/auto-commit-on-exit.ts`

---

---
title: "Extension examples"
description: "Reference extension packages: subagent agents, plan-mode, doom-overlay, dynamic resources skill, and auto-commit-on-exit."
---

The `examples/extensions/` tree contains loadable `ExtensionAPI` factories that register tools, commands, shortcuts, interactive components, resource discovery hooks, and session lifecycle handlers. These examples range from single TypeScript files to directories with an `index.ts` entry point and supporting assets.

## Load an example

From `packages/coding-agent/`, load an example for the current process with `--extension` or `-e`:

```bash
pi -e ./examples/extensions/subagent
pi -e ./examples/extensions/plan-mode
pi -e ./examples/extensions/doom-overlay
pi -e ./examples/extensions/dynamic-resources
pi -e ./examples/extensions/auto-commit-on-exit.ts
```

For auto-discovery, place single-file extensions in `~/.pi/agent/extensions/` or `.pi/extensions/`. Preserve the complete directory for multi-file extensions:

```text
~/.pi/agent/extensions/
├── auto-commit-on-exit.ts
├── subagent/
│   ├── index.ts
│   └── agents.ts
└── doom-overlay/
    ├── index.ts
    ├── doom-component.ts
    ├── doom-engine.ts
    ├── doom-keys.ts
    ├── wad-finder.ts
    └── doom/
```

Project-local `.pi/extensions/` entries load only after the project is trusted.

<Warning>
Extensions execute with the same system permissions as `pi`. Review extension code, agent definitions, shell commands, downloaded assets, and Git hooks before installing them.
</Warning>

## Example inventory

| Example | Registered surface | State and side effects | Primary constraint |
|---|---|---|---|
| `subagent/` | `subagent` tool | Starts child `pi` processes; no child sessions | Project agent definitions are executable repository-controlled prompts |
| `plan-mode/` | `--plan`, `/plan`, `/todos`, `Ctrl+Alt+P` | Persists plan state in session entries | Its read-only policy is a tool filter and Bash regex, not a sandbox |
| `doom-overlay/` | `/doom-overlay` | Keeps the engine in process memory and may download a WAD | TUI mode and adjacent WASM assets are required |
| `dynamic-resources/` | `resources_discover` handler | Adds a skill, prompt template, and theme | Resources must remain valid and reachable beside the extension |
| `auto-commit-on-exit.ts` | `session_shutdown` handler | Stages and commits the entire Git worktree | Runs for more shutdown reasons than process exit |

## Subagent orchestration

`examples/extensions/subagent/index.ts` registers a `subagent` tool that delegates work to separate `pi` processes. Each child runs in JSON print mode with `--no-session`, receives its agent definition through model, tool, and appended-system-prompt arguments, and streams `message_end` and `tool_result_end` events back to the parent tool call.

### Install the bundled definitions

Loading the extension alone does not install its sample agents or workflow prompt templates. From the repository root:

```bash
mkdir -p ~/.pi/agent/extensions/subagent
ln -sf "$(pwd)/packages/coding-agent/examples/extensions/subagent/index.ts" \
  ~/.pi/agent/extensions/subagent/index.ts
ln -sf "$(pwd)/packages/coding-agent/examples/extensions/subagent/agents.ts" \
  ~/.pi/agent/extensions/subagent/agents.ts

mkdir -p ~/.pi/agent/agents
for file in packages/coding-agent/examples/extensions/subagent/agents/*.md; do
  ln -sf "$(pwd)/$file" ~/.pi/agent/agents/"$(basename "$file")"
done

mkdir -p ~/.pi/agent/prompts
for file in packages/coding-agent/examples/extensions/subagent/prompts/*.md; do
  ln -sf "$(pwd)/$file" ~/.pi/agent/prompts/"$(basename "$file")"
done
```

This installs four sample definitions:

| Agent | Default role | Declared tools |
|---|---|---|
| `scout` | Codebase reconnaissance | `read`, `grep`, `find`, `ls`, `bash` |
| `planner` | Read-only implementation planning | `read`, `grep`, `find`, `ls` |
| `reviewer` | Code and security review | `read`, `grep`, `find`, `ls`, `bash` |
| `worker` | General implementation | No restriction; child defaults apply |

The bundled definitions specify model names. The extension mechanism itself is provider-neutral: `model` is an optional string forwarded to child `pi --model`. Omit it to use the child process’s configured default, or replace it with another configured model.

### Agent definition format

Definitions are Markdown files with YAML frontmatter:

```markdown
---
name: scout
description: Fast codebase recon
tools: read, grep, find, ls, bash
model: configured-model-name
---

System instructions for the child agent.
```

`name` and `description` are required. `tools` is an optional comma-separated allowlist, `model` is optional, and the Markdown body becomes the appended system prompt. Unreadable files and definitions missing required fields are skipped.

Agent discovery runs for every tool invocation:

- User agents: `~/.pi/agent/agents/*.md`
- Project agents: the nearest ancestor `.pi/agents/*.md`
- Default `agentScope`: `user`
- With `agentScope: "both"`, project definitions replace same-named user definitions

### Tool request modes

Exactly one execution mode must be provided.

| Mode | Input | Behavior |
|---|---|---|
| Single | `agent`, `task`, optional `cwd` | Runs one child and returns its final assistant text |
| Parallel | `tasks: [{ agent, task, cwd? }]` | Accepts at most eight tasks and runs up to four concurrently |
| Chain | `chain: [{ agent, task, cwd? }]` | Runs sequentially and replaces every `{previous}` token with the preceding final output |

A chain request has this shape:

```json
{
  "chain": [
    {
      "agent": "scout",
      "task": "Locate the authentication implementation"
    },
    {
      "agent": "planner",
      "task": "Create a change plan from these findings:\n\n{previous}"
    }
  ],
  "agentScope": "user"
}
```

The installed prompt templates provide common chains:

```text
/implement add caching to session lookup
/scout-and-plan separate provider selection from authentication
/implement-and-review validate configuration input
```

These are prompt templates from `prompts/*.md`, not commands registered by the extension.

### Trust and failure behavior

<Warning>
Project agents are repository-controlled prompts that can request shell commands and file modifications. Interactive use asks for confirmation by default when `agentScope` includes `project`. Headless contexts have no confirmation UI, so selecting `project` or `both` is itself the trust decision.
</Warning>

Single and chain failures are detected from a nonzero exit code or a child stop reason of `error` or `aborted`. Chains stop at the first failed step. Parallel execution reports each failure in its aggregate summary but does not mark the aggregate tool result as an error.

Model-visible parallel output is capped at 50 KiB per task; complete results remain in tool details. Cancellation sends `SIGTERM` to running children. Treat child-process cancellation as cooperative cleanup, not process containment.

## Plan mode

`examples/extensions/plan-mode/index.ts` adds a planning state that changes the active tool set, injects planning instructions, extracts numbered steps, and tracks `[DONE:n]` markers.

Start in plan mode:

```bash
pi -e ./examples/extensions/plan-mode --plan
```

Or toggle it during an interactive session:

| Control | Behavior |
|---|---|
| `/plan` | Enter or leave plan mode |
| `Ctrl+Alt+P` | Keyboard equivalent of `/plan` |
| `/todos` | Display the extracted steps and completion state |
| `--plan` | Enter plan mode during session startup |

### State transitions

| State | Active behavior |
|---|---|
| Normal | Uses the current active-tool configuration |
| Planning | Removes `edit` and `write`, preserves other active tools, and adds `read`, `bash`, `grep`, `find`, `ls`, and `questionnaire` |
| Selection | After a valid plan, the TUI offers execute, stay in plan mode, or refine |
| Execution | Restores the pre-plan tool snapshot and injects remaining steps before each agent run |
| Complete | Clears execution state after every item has a matching `[DONE:n]` marker |

A recognized plan must contain a `Plan:` header followed by numbered, single-line items:

```text
Plan:
1. Inspect the current session loader
2. Add a focused regression test
3. Update the implementation
```

Step text is normalized and truncated to 50 display characters. During execution, assistant responses mark progress with case-insensitive tags such as `[DONE:1]`. The extension persists `enabled`, `executing`, the todo list, and the pre-plan tool snapshot in custom `plan-mode` session entries. Resume reconstructs completion state from assistant messages after the most recent execution marker.

The execute/refine selection requires `ctx.hasUI`. In a headless mode, the extension can restrict tools and inject planning context, but it does not display the post-plan selection dialog.

<Warning>
Plan mode is not a security boundary. It preserves custom active tools, including custom tools that may mutate state. Bash filtering uses regular expressions rather than shell parsing; commands such as `curl -o ...` or `git branch <name>` can pass the current rules. Use an OS sandbox or a stricter tool policy when mutation prevention is required.
</Warning>

## DOOM overlay

`examples/extensions/doom-overlay/index.ts` registers `/doom-overlay` and renders the bundled WebAssembly build through a custom TUI overlay.

```bash
pi -e ./examples/extensions/doom-overlay
```

Then run either:

```text
/doom-overlay
/doom-overlay /absolute/path/to/doom1.wad
```

The command rejects non-TUI modes. Without an explicit path, WAD discovery checks:

1. `doom1.wad` beside the extension.
2. `./doom1.wad` and `./DOOM1.WAD`.
3. `~/doom1.wad` and `~/.doom/doom1.wad`.
4. The downloadable shareware WAD.

The downloaded gzip is decompressed, checked for an `IWAD` header, and written beside the extension. A read-only extension installation therefore prevents first-run download. An explicit command argument bypasses discovery and header validation; file and engine errors are caught by the command and shown as a load failure.

The generated `doom/build/doom.js` and `doom/build/doom.wasm` assets must remain adjacent to the extension. A missing JavaScript build reports that `doom/build.sh` must be run.

### Runtime behavior

- The game loop targets 35 ticks per second.
- Each terminal cell uses a 24-bit-color `▀` to represent two framebuffer pixels.
- Render height preserves the effective 3.2:1 terminal-cell aspect ratio, with a minimum height of ten rows and one footer row.
- Current overlay options are `width: "75%"`, `maxHeight: "95%"`, `anchor: "center"`, and a one-row top margin.
- `Q` sends the pause key, stops the render timer, and closes the overlay.
- Reopening the same WAD within the same extension instance resumes the cached engine.
- The native adapter does not provide sound.

| Action | Input |
|---|---|
| Move or strafe | WASD or arrow keys |
| Run | Shift with WASD |
| Fire | `F` or Ctrl combinations other than `Ctrl+C` |
| Use/open | Space |
| Menu and map | Escape and Tab |
| Weapon/input digits | `0`–`9` are forwarded; the footer documents weapons `1`–`7` |
| Pause and close overlay | `Q` |

## Dynamic skills, prompts, and themes

`examples/extensions/dynamic-resources/index.ts` contributes resources through `resources_discover`:

```typescript
pi.on("resources_discover", () => ({
  skillPaths: [join(baseDir, "SKILL.md")],
  promptPaths: [join(baseDir, "dynamic.md")],
  themePaths: [join(baseDir, "dynamic.json")],
}));
```

The paths are based on `import.meta.url`, so discovery does not depend on the session working directory. The event runs after `session_start` during startup and reload. Pi extends the resource loader with the returned paths and rebuilds the session system prompt when necessary.

| File | Discovered interface | Behavior |
|---|---|---|
| `SKILL.md` | `/skill:dynamic-resources` | Loads the `dynamic-resources` skill instructions |
| `dynamic.md` | `/dynamic` | Requests a repository-structure summary with build and test commands |
| `dynamic.json` | Theme named `dynamic-resources` | Adds the theme to the selector; it is not selected automatically |

Use the resources after loading the extension:

```text
/skill:dynamic-resources
/dynamic
/theme
```

<Info>
Resource discovery is portable and provider-neutral. The extension contributes local file paths and does not depend on a hosted skill catalog, connector, or specific model provider. The same pattern can source resources from files, repository packages, or another catalog adapter as long as the handler returns valid local paths.
</Info>

## Auto-commit on shutdown

`examples/extensions/auto-commit-on-exit.ts` registers one `session_shutdown` handler. It exposes no command, tool, flag, or confirmation UI.

Its shutdown sequence is:

1. Run `git status --porcelain`.
2. Return silently outside a Git repository or when the worktree is clean.
3. Find the most recent assistant message and join its text blocks.
4. Build `[pi] <first line>`, limiting the assistant fragment to 50 characters and appending `...` when truncated.
5. Fall back to `[pi] Work in progress` when no assistant text exists.
6. Run `git add -A`.
7. Run `git commit -m <message>`.
8. Notify the user only when the commit succeeds and a UI is available.

<Warning>
The example stages every tracked, untracked, modified, and deleted path visible to `git add -A`, including unrelated work and previously staged changes. It has no branch check, path allowlist, confirmation, or rollback.
</Warning>

Despite its filename, the handler is not limited to process exit. `session_shutdown` runs with reasons `quit`, `reload`, `new`, `resume`, and `fork`. The example ignores the reason, so any of those transitions can create a commit.

It also ignores the result of `git add` and does not report commit failures. Hooks, identity configuration, repository policy, or an empty index can prevent the commit without a UI error. A production adaptation should normally:

- Require `event.reason === "quit"` if commits must occur only at exit.
- Stage explicit paths instead of using `git add -A`.
- Check and report both command results.
- Obtain confirmation before committing.
- Preserve repository-specific commit-message and signing rules.

Verify the result with:

```bash
git status --short
git log -1 --pretty=%s
```

## Troubleshooting

| Symptom | Check |
|---|---|
| `Unknown agent` | Confirm the definition has `name` and `description`, is under the selected user/project agents directory, and matches `agentScope` |
| Project-agent confirmation never appears | Confirmation is TUI-only; headless execution does not display it |
| Plan mode still permits mutation | Inspect preserved custom tools and the regex allowlist; the example only removes `edit` and `write` directly |
| No plan selection appears | The response must contain a `Plan:` header, valid numbered items, and an interactive UI |
| DOOM reports interactive mode required | Run regular interactive `pi`, not print or JSON mode |
| DOOM cannot load its WAD | Check the supplied path, extension-directory write permission, network access, and adjacent WASM build |
| `/dynamic` or the skill is missing | Reload after installing the extension and validate the sibling Markdown frontmatter |
| Auto-commit produces no commit | Check Git status, hooks, identity configuration, and commit errors manually; the example reports success only |

## Related pages

<CardGroup>
  <Card title="Extensions" href="/extensions">
    Extension registration, lifecycle events, resource hooks, UI contracts, and cleanup.
  </Card>
  <Card title="Tools and allowlists" href="/tools">
    Built-in and extension tools, active-tool filtering, and blocked-tool behavior.
  </Card>
  <Card title="Skills" href="/skills">
    Skill discovery, frontmatter validation, invocation names, and collision rules.
  </Card>
  <Card title="Themes and Pi packages" href="/themes-and-packages">
    Theme requirements and packaging extensions with related resources.
  </Card>
</CardGroup>
