Hooks and custom tools are now unified as extensions. Both were TypeScript modules exporting a factory function that receives an API object. Now there's one concept, one discovery location, one CLI flag, one settings.json entry.
Automatic migration:
commands/ directories are automatically renamed to prompts/ on startup (both ~/.pi/agent/commands/ and .pi/commands/)
Manual migration required:
- Move files from
hooks/ and tools/ directories to extensions/ (deprecation warnings shown on startup)
- Update imports and type names in your extension code
- Update
settings.json if you have explicit hook and custom tool paths configured
Directory changes:
# Before
~/.pi/agent/hooks/*.ts → ~/.pi/agent/extensions/*.ts
~/.pi/agent/tools/*.ts → ~/.pi/agent/extensions/*.ts
.pi/hooks/*.ts → .pi/extensions/*.ts
.pi/tools/*.ts → .pi/extensions/*.ts
Extension discovery rules (in extensions/ directories):
- Direct files:
extensions/*.ts or *.js → loaded directly
- Subdirectory with index:
extensions/myext/index.ts → loaded as single extension
- Subdirectory with package.json:
extensions/myext/package.json with "pi" field → loads declared paths
// extensions/my-package/package.json
{
"name": "my-extension-package",
"dependencies": { "zod": "^3.0.0" },
"pi": {
"extensions": ["./src/main.ts", "./src/tools.ts"]
}
}
No recursion beyond one level. Complex packages must use the package.json manifest. Dependencies are resolved via jiti, and extensions can be published to and installed from npm.
Type renames:
HookAPI → ExtensionAPI
HookContext → ExtensionContext
HookCommandContext → ExtensionCommandContext
HookUIContext → ExtensionUIContext
CustomToolAPI → ExtensionAPI (merged)
CustomToolContext → ExtensionContext (merged)
CustomToolUIContext → ExtensionUIContext
CustomTool → ToolDefinition
CustomToolFactory → ExtensionFactory
HookMessage → CustomMessage
Import changes:
// Before (hook)
import type { HookAPI, HookContext } from "@mariozechner/pi-coding-agent";
export default function (pi: HookAPI) { ... }
// Before (custom tool)
import type { CustomToolFactory } from "@mariozechner/pi-coding-agent";
const factory: CustomToolFactory = (pi) => ({ name: "my_tool", ... });
export default factory;
// After (both are now extensions)
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
export default function (pi: ExtensionAPI) {
pi.on("tool_call", async (event, ctx) => { ... });
pi.registerTool({ name: "my_tool", ... });
}
Custom tools now have full context access. Tools registered via pi.registerTool() now receive the same ctx object that event handlers receive. Previously, custom tools had limited context. Now all extension code shares the same capabilities:
pi.registerTool() - Register tools the LLM can call
pi.registerCommand() - Register commands like /mycommand
pi.registerShortcut() - Register keyboard shortcuts (shown in /hotkeys)
pi.registerFlag() - Register CLI flags (shown in --help)
pi.registerMessageRenderer() - Custom TUI rendering for message types
pi.on() - Subscribe to lifecycle events (tool_call, session_start, etc.)
pi.sendMessage() - Inject messages into the conversation
pi.appendEntry() - Persist custom data in session (survives restart/branch)
pi.exec() - Run shell commands
pi.getActiveTools() / pi.setActiveTools() - Dynamic tool enable/disable
pi.getAllTools() - List all available tools
pi.events - Event bus for cross-extension communication
ctx.ui.confirm() / select() / input() - User prompts
ctx.ui.notify() - Toast notifications
ctx.ui.setStatus() - Persistent status in footer (multiple extensions can set their own)
ctx.ui.setWidget() - Widget display above editor
ctx.ui.setTitle() - Set terminal window title
ctx.ui.custom() - Full TUI component with keyboard handling
ctx.ui.editor() - Multi-line text editor with external editor support
ctx.sessionManager - Read session entries, get branch history
Settings changes:
// Before
{
"hooks": ["./my-hook.ts"],
"customTools": ["./my-tool.ts"]
}
// After
{
"extensions": ["./my-extension.ts"]
}
CLI changes:
# Before
pi --hook ./safety.ts --tool ./todo.ts
# After
pi --extension ./safety.ts -e ./todo.ts