Create, debug, and iterate on GSD extensions (TypeScript modules that add tools, commands, event hooks, custom UI, and providers to GSD). Use when asked to build an extension, add a tool the LLM can call, register a slash command, hook into GSD events, create custom TUI components, or modify GSD behavior. Triggers on "create extension", "build extension", "add a tool", "register command", "hook into gsd", "custom tool", "gsd plugin", "gsd extension".
<essential_principles>
Extensions are TypeScript modules that hook into GSD's runtime (built on pi). They export a default function receiving ExtensionAPI and use it to subscribe to events, register tools/commands/shortcuts, and interact with the session.
GSD extension paths (community/user-installed extensions):
~/.pi/agent/extensions/*.ts or ~/.pi/agent/extensions/*/index.ts.gsd/extensions/*.ts or .gsd/extensions/*/index.tsNote: ~/.gsd/agent/extensions/ is reserved for bundled extensions synced from the gsd-pi package. Community extensions placed there are silently ignored by the loader.
The three primitives:
pi.on("event", handler)). Can block tool calls, modify messages, inject context.pi.registerTool()). LLM calls them autonomously.pi.registerCommand()). Users type /mycommand.Non-negotiable rules:
StringEnum from @mariozechner/pi-ai for string enum params (NOT Type.Union/Type.Literal — breaks Google's API)truncateHead/truncateTail from @mariozechner/pi-coding-agent)details for branching supportsignal?.aborted in long-running tool executionspi.exec() not child_process for shell commandsctx.hasUI before dialog methods (non-interactive modes exist)waitForIdle, newSession, fork, navigateTree, reload) are ONLY available in command handlers — they deadlock in event handlersrender() must not exceed width — use truncateToWidth()@ from path params in custom tools (some models add it)Available imports:
| Package | Purpose |
|---|---|
@mariozechner/pi-coding-agent | ExtensionAPI, ExtensionContext, Theme, event types, tool utilities, DynamicBorder, BorderedLoader, CustomEditor, highlightCode |
@sinclair/typebox | Type.Object, Type.String, Type.Number, Type.Optional, Type.Boolean, Type.Array |
@mariozechner/pi-ai | StringEnum (required for string enums), Type re-export |
@mariozechner/pi-tui | Text, Box, Container, Spacer, Markdown, SelectList, Input, matchesKey, Key, truncateToWidth, visibleWidth |
| Node.js built-ins | node:fs, node:path, node:child_process, etc. |
</essential_principles>
Building a new extension:
workflows/create-extension.mdAdding capabilities to an existing extension:
workflows/add-capability.mdDebugging an extension:
workflows/debug-extension.mdIf user intent is clear from context, skip the question and go directly to the workflow. </routing>
<reference_index>
All domain knowledge in references/:
Core architecture: extension-lifecycle.md, events-reference.md API surface: extensionapi-reference.md, extensioncontext-reference.md Capabilities: custom-tools.md, custom-commands.md, custom-ui.md, custom-rendering.md Patterns: state-management.md, system-prompt-modification.md, compaction-session-control.md Infrastructure: model-provider-management.md, remote-execution-overrides.md, packaging-distribution.md, mode-behavior.md Gotchas: key-rules-gotchas.md </reference_index>
<workflows_index>
| Workflow | Purpose |
|---|---|
| create-extension.md | Build a new extension from scratch |
| add-capability.md | Add tools, commands, hooks, UI to an existing extension |
| debug-extension.md | Diagnose and fix extension issues |
| </workflows_index> |
<success_criteria> Extension is complete when:
/reload without errors/command input