Gsd-2 create-gsd-extension
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".
git clone https://github.com/gsd-build/gsd-2
T=$(mktemp -d) && git clone --depth=1 https://github.com/gsd-build/gsd-2 "$T" && mkdir -p ~/.claude/skills && cp -r "$T/src/resources/skills/create-gsd-extension" ~/.claude/skills/gsd-build-gsd-2-create-gsd-extension && rm -rf "$T"
src/resources/skills/create-gsd-extension/SKILL.md<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):
- Global:
or~/.pi/agent/extensions/*.ts~/.pi/agent/extensions/*/index.ts - Project-local:
or.gsd/extensions/*.ts.gsd/extensions/*/index.ts
Note:
~/.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:
- Events — Listen and react (
). Can block tool calls, modify messages, inject context.pi.on("event", handler) - Tools — Give the LLM new abilities (
). LLM calls them autonomously.pi.registerTool() - Commands — Give users slash commands (
). Users typepi.registerCommand()
./mycommand
Non-negotiable rules:
- Use
fromStringEnum
for string enum params (NOT@mariozechner/pi-ai
/Type.Union
— breaks Google's API)Type.Literal - Truncate tool output to 50KB / 2000 lines max (use
/truncateHead
fromtruncateTail
)@mariozechner/pi-coding-agent - Store stateful tool state in
for branching supportdetails - Check
in long-running tool executionssignal?.aborted - Use
notpi.exec()
for shell commandschild_process - Check
before dialog methods (non-interactive modes exist)ctx.hasUI - Session control methods (
,waitForIdle
,newSession
,fork
,navigateTree
) are ONLY available in command handlers — they deadlock in event handlersreload - Lines from
must not exceedrender()
— usewidthtruncateToWidth() - Use theme from callback params, never import directly
- Strip leading
from path params in custom tools (some models add it)@
Available imports:
| Package | Purpose |
|---|---|
| , , , event types, tool utilities, , , , |
| , , , , , |
| (required for string enums), re-export |
| , , , , , , , , , , |
| Node.js built-ins | , , , etc. |
</essential_principles>
<routing> Based on user intent, route to the appropriate workflow:Building a new extension:
- "Create an extension", "build a tool", "I want to add a command" →
workflows/create-extension.md
Adding capabilities to an existing extension:
- "Add a tool to my extension", "add event hook", "add custom rendering" →
workflows/add-capability.md
Debugging an extension:
- "My extension doesn't work", "tool not showing up", "event not firing" →
workflows/debug-extension.md
If 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:
- TypeScript compiles without errors (jiti handles this at runtime)
- Extension loads on GSD startup or
without errors/reload - Tools appear in the LLM's system prompt and are callable
- Commands respond to
input/command - Event hooks fire at the expected lifecycle points
- Custom UI renders correctly within terminal width
- State persists correctly across session restarts (if stateful)
- Output is truncated to safe limits (if tools produce variable output) </success_criteria>