# File Reference — Example Extensions


Upstream example extensions are copied into the managed agent directory during
runtime setup. In source checkouts, use the bundled extension tree under
`src/resources/extensions/` for SF-owned examples and tests.

Common reference roots:

```
src/resources/extensions/
~/.sf/agent/extensions/
```

### Lifecycle & Safety
| File | What It Demonstrates |
|------|---------------------|
| `protected-paths.ts` | Blocking writes to `.env`, `.git/`, `node_modules/` via `tool_call` |
| `dirty-repo-guard.ts` | Preventing session changes with uncommitted git changes |

### Custom Tools
| File | What It Demonstrates |
|------|---------------------|
| `todo.ts` | **Best example** — Stateful tool with persistence, custom rendering, command |
| `hello.ts` | Minimal tool registration |
| `question.ts` | Tool with `ctx.ui.select()` for user interaction |
| `questionnaire.ts` | Multi-question wizard with tab navigation |
| `tool-override.ts` | Overriding built-in `read` with logging/access control |
| `dynamic-tools.ts` | Registering tools after startup and at runtime |
| `truncated-tool.ts` | Output truncation with `truncateHead` |
| `built-in-tool-renderer.ts` | Custom compact rendering for built-in tools |
| `antigravity-image-gen.ts` | Image generation tool |
| `ssh.ts` | Full SSH remote execution with pluggable operations |

### Commands & UI
| File | What It Demonstrates |
|------|---------------------|
| `commands.ts` | Basic command registration |
| `preset.ts` | Named presets (model, thinking, tools) with flag and command |
| `plan-mode/` | Full plan mode — commands, shortcuts, flags, widgets, status, tool management |
| `qna.ts` | Extract questions + `BorderedLoader` + `setEditorText` |
| `send-user-message.ts` | `pi.sendUserMessage()` for injecting user messages |
| `modal-editor.ts` | Vim-like modal editor via `CustomEditor` |
| `snake.ts` | Full game with custom UI, keyboard handling, persistence |
| `space-invaders.ts` | Full game with custom UI |
| `doom-overlay/` | DOOM running as an overlay at 35 FPS |
| `timed-confirm.ts` | Dialogs with `timeout` and `AbortSignal` |
| `overlay-test.ts` | Overlay compositing with inline inputs |
| `overlay-qa-tests.ts` | Comprehensive overlay tests: anchors, margins, stacking |

### System Prompt & Context
| File | What It Demonstrates |
|------|---------------------|
| `pirate.ts` | `before_agent_start` system prompt modification |
| `claude-rules.ts` | Loading rules from `.claude/rules/` into system prompt |
| `system-prompt-header.ts` | Displaying system prompt info |
| `input-transform.ts` | Transforming user input via `input` event |
| `inline-bash.ts` | Expanding `!{command}` patterns in prompts |

### Compaction & Sessions
| File | What It Demonstrates |
|------|---------------------|
| `custom-compaction.ts` | Custom compaction summary via `session_before_compact` |
| `trigger-compact.ts` | Triggering compaction at 100k tokens |
| `git-checkpoint.ts` | Git stash on turns, restore on fork |
| `bookmark.ts` | Labeling entries for `/tree` navigation |
| `session-name.ts` | Naming sessions for selector display |

### UI Components
| File | What It Demonstrates |
|------|---------------------|
| `custom-footer.ts` | `setFooter` with git branch and token stats |
| `custom-header.ts` | `setHeader` for custom startup header |
| `status-line.ts` | `setStatus` for footer indicators |
| `widget-placement.ts` | `setWidget` above and below editor |
| `notify.ts` | Desktop notifications via OSC 777 |
| `titlebar-spinner.ts` | Braille spinner in terminal title |
| `message-renderer.ts` | Custom message rendering with `registerMessageRenderer` |
| `model-status.ts` | `model_select` event for status bar |
| `mac-system-theme.ts` | Auto-sync theme with macOS dark/light mode |

### Providers
| File | What It Demonstrates |
|------|---------------------|
| `custom-provider-anthropic/` | Custom Anthropic provider with OAuth |
| `custom-provider-gitlab-duo/` | GitLab Duo via proxy |
| `custom-provider-qwen-cli/` | Qwen CLI with OAuth device flow |

### Communication
| File | What It Demonstrates |
|------|---------------------|
| `event-bus.ts` | Inter-extension communication via `pi.events` |
| `rpc-demo.ts` | All RPC-supported extension UI methods |
| `reload-runtime.ts` | Safe reload flow: command + LLM tool handoff |
| `shutdown-command.ts` | `ctx.shutdown()` for graceful exit |
| `file-trigger.ts` | File watcher injecting messages via `sendMessage` |

### Misc
| File | What It Demonstrates |
|------|---------------------|
| `with-deps/` | Extension with its own `package.json` and npm dependencies |
| `minimal-mode.ts` | Override built-in tool rendering for minimal display |

---

## Quick Reference: "I want to..."

| Goal | Approach | Key API | Example File |
|------|----------|---------|-------------|
| Block dangerous commands | Listen to `tool_call`, return `{ block: true }` | `pi.on("tool_call", ...)` | `protected-paths.ts` |
| Add a tool the LLM can use | Register a tool with schema and execute | `pi.registerTool({...})` | `todo.ts` |
| Add a slash command | Register a command with handler | `pi.registerCommand(...)` | `commands.ts` |
| Ask the user a question | Use dialog methods | `ctx.ui.select()`, `ctx.ui.confirm()` | `question.ts` |
| Show persistent status | Set footer status | `ctx.ui.setStatus(id, text)` | `status-line.ts` |
| Modify the system prompt | Hook `before_agent_start` | Return `{ systemPrompt: "..." }` | `pirate.ts` |
| Filter messages sent to LLM | Hook `context` event | Return `{ messages: [...] }` | — |
| Save state across restarts | Store in tool details or appendEntry | `details: {...}` / `pi.appendEntry(...)` | `todo.ts` |
| Custom compaction | Hook `session_before_compact` | Return `{ compaction: {...} }` | `custom-compaction.ts` |
| Build a full-screen UI | Use `ctx.ui.custom()` | Component with render/handleInput | `snake.ts` |
| Show a floating dialog | Use overlay mode | `ctx.ui.custom(..., { overlay: true })` | `overlay-test.ts` |
| Replace the input editor | Extend `CustomEditor` | `ctx.ui.setEditorComponent(...)` | `modal-editor.ts` |
| Override a built-in tool | Register tool with same name | `pi.registerTool({ name: "read" })` | `tool-override.ts` |
| Run tools via SSH | Use pluggable operations | `createBashTool(cwd, { operations })` | `ssh.ts` |
| Switch models programmatically | Find and set model | `pi.setModel(model)` | `preset.ts` |
| Register a custom provider | Provide config with models | `pi.registerProvider(...)` | `custom-provider-anthropic/` |
| Transform user input | Hook `input` event | Return `{ action: "transform", text }` | `input-transform.ts` |
| Inject messages | Send custom or user messages | `pi.sendMessage()` / `pi.sendUserMessage()` | `send-user-message.ts` |
| React to model changes | Hook `model_select` | `pi.on("model_select", ...)` | `model-status.ts` |
| Add a keyboard shortcut | Register a shortcut | `pi.registerShortcut("ctrl+x", ...)` | `plan-mode/` |
| Package for distribution | Add `pi` key to package.json | `"pi": { "extensions": [...] }` | See packages.md |

---

*This document was generated from the Pi extension documentation and examples.
Use repository-relative paths for SF source work and `~/.sf/agent/extensions/`
for installed runtime inspection.*
