You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# Load an extension with --extension flag
pi --extension examples/extensions/permission-gate.ts
# Or copy to extensions directory for auto-discovery
cp permission-gate.ts ~/.pi/agent/extensions/
Examples
Lifecycle & Safety
Extension
Description
permission-gate.ts
Prompts for confirmation before dangerous bash commands (rm -rf, sudo, etc.)
protected-paths.ts
Blocks writes to protected paths (.env, .git/, node_modules/)
confirm-destructive.ts
Confirms before destructive session actions (clear, switch, fork)
dirty-repo-guard.ts
Prevents session changes with uncommitted git changes
sandbox/
OS-level sandboxing using @anthropic-ai/sandbox-runtime with per-project config
Custom Tools
Extension
Description
todo.ts
Todo list tool + /todos command with custom rendering and state persistence
hello.ts
Minimal custom tool example
question.ts
Demonstrates ctx.ui.select() for asking the user questions with custom UI
questionnaire.ts
Multi-question input with tab bar navigation between questions
tool-override.ts
Override built-in tools (e.g., add logging/access control to read)
dynamic-tools.ts
Register tools after startup (session_start) and at runtime via command, with prompt snippets and tool-specific prompt guidelines
built-in-tool-renderer.ts
Custom compact rendering for built-in tools (read, bash, edit, write) while keeping original behavior
minimal-mode.ts
Override built-in tool rendering for minimal display (only tool calls, no output in collapsed mode)
truncated-tool.ts
Wraps ripgrep with proper output truncation (50KB/2000 lines)
antigravity-image-gen.ts
Generate images via Google Antigravity with optional save-to-disk modes
ssh.ts
Delegate all tools to a remote machine via SSH using pluggable operations
subagent/
Delegate tasks to specialized subagents with isolated context windows
Commands & UI
Extension
Description
preset.ts
Named presets for model, thinking level, tools, and instructions via --preset flag and /preset command
plan-mode/
Claude Code-style plan mode for read-only exploration with /plan command and step tracking
tools.ts
Interactive /tools command to enable/disable tools with session persistence
handoff.ts
Transfer context to a new focused session via /handoff <goal>
qna.ts
Extracts questions from last response into editor via ctx.ui.setEditorText()
status-line.ts
Shows turn progress in footer via ctx.ui.setStatus() with themed colors
widget-placement.ts
Shows widgets above and below the editor via ctx.ui.setWidget() placement
model-status.ts
Shows model changes in status bar via model_select hook
snake.ts
Snake game with custom UI, keyboard handling, and session persistence
send-user-message.ts
Demonstrates pi.sendUserMessage() for sending user messages from extensions
timed-confirm.ts
Demonstrates AbortSignal for auto-dismissing ctx.ui.confirm() and ctx.ui.select() dialogs
importtype{ExtensionAPI}from"@mariozechner/pi-coding-agent";import{Type}from"@sinclair/typebox";exportdefaultfunction(pi: ExtensionAPI){// Subscribe to lifecycle eventspi.on("tool_call",async(event,ctx)=>{if(event.toolName==="bash"&&event.input.command?.includes("rm -rf")){constok=awaitctx.ui.confirm("Dangerous!","Allow rm -rf?");if(!ok)return{block: true,reason: "Blocked by user"};}});// Register custom toolspi.registerTool({name: "greet",label: "Greeting",description: "Generate a greeting",parameters: Type.Object({name: Type.String({description: "Name to greet"}),}),asyncexecute(toolCallId,params,onUpdate,ctx,signal){return{content: [{type: "text",text: `Hello, ${params.name}!`}],details: {},};},});// Register commandspi.registerCommand("hello",{description: "Say hello",handler: async(args,ctx)=>{ctx.ui.notify("Hello!","info");},});}
Key Patterns
Use StringEnum for string parameters (required for Google API compatibility):
import{StringEnum}from"@mariozechner/pi-ai";// Good
action: StringEnum(["list","add"]asconst)// Bad - doesn't work with Google
action: Type.Union([Type.Literal("list"),Type.Literal("add")])
State persistence via details:
// Store state in tool result details for proper forking supportreturn{content: [{type: "text",text: "Done"}],details: {todos: [...todos], nextId },// Persisted in session};// Reconstruct on session eventspi.on("session_start",async(_event,ctx)=>{for(constentryofctx.sessionManager.getBranch()){if(entry.type==="message"&&entry.message.toolName==="my_tool"){constdetails=entry.message.details;// Reconstruct state from details}}});