Changelog
Release notes, project updates, and announcements from the Pi team.
Pi 0.39.0
New version of pi. Download from npm or view release on GitHub.
Breaking Changes
before_agent_startevent now receivessystemPromptin the event object and returnssystemPrompt(full replacement) instead ofsystemPromptAppend. Extensions that were appending must now useevent.systemPrompt + extrapattern. (#575)discoverSkills()now returns{ skills: Skill[], warnings: SkillWarning[] }instead ofSkill[]. This allows callers to handle skill loading warnings. (#577 by @cv)
Added
ctx.ui.getAllThemes(),ctx.ui.getTheme(name), andctx.ui.setTheme(name | Theme)methods for extensions to list, load, and switch themes at runtime (#576)--no-toolsflag to disable all built-in tools, allowing extension-only tool setups (#557 by @cv)- Pluggable operations for built-in tools enabling remote execution via SSH or other transports (#564). Interfaces:
ReadOperations,WriteOperations,EditOperations,BashOperations,LsOperations,GrepOperations,FindOperations user_bashevent for intercepting user!/!!commands, allowing extensions to redirect to remote systems (#528)setActiveTools()in ExtensionAPI for dynamic tool management- Built-in renderers used automatically for tool overrides without custom
renderCall/renderResult ssh.tsexample: remote tool execution via--ssh user@host:/pathinteractive-shell.tsexample: run interactive commands (vim, git rebase, htop) with full terminal access via!iprefix or auto-detection- Wayland clipboard support for
/copycommand using wl-copy with xclip/xsel fallback (#570 by @OgulcanCelik) - Experimental:
ctx.ui.custom()now accepts{ overlay: true }option for floating modal components that composite over existing content without clearing the screen (#558 by @nicobailon) AgentSession.skillsandAgentSession.skillWarningsproperties to access loaded skills without rediscovery (#577 by @cv)
Pi 0.38.0
New version of pi. Download from npm or view release on GitHub.
Breaking Changes
ctx.ui.custom()factory signature changed from(tui, theme, done)to(tui, theme, keybindings, done)for keybinding access in custom componentsLoadedExtensiontype renamed toExtensionLoadExtensionsResult.setUIContext()removed, replaced withruntime: ExtensionRuntimeExtensionRunnerconstructor now requiresruntime: ExtensionRuntimeas second parameterExtensionRunner.initialize()signature changed from options object to positional params(actions, contextActions, commandContextActions?, uiContext?)ExtensionRunner.getHasUI()renamed tohasUI()- OpenAI Codex model aliases removed (
gpt-5,gpt-5-mini,gpt-5-nano,codex-mini-latest). Use canonical IDs:gpt-5.1,gpt-5.1-codex-mini,gpt-5.2,gpt-5.2-codex. (#536 by @ghoulr)
Added
--no-extensionsflag to disable extension discovery while still allowing explicit-epaths (#524 by @cv)- SDK:
InteractiveMode,runPrintMode(),runRpcMode()exported for building custom run modes. Seedocs/sdk.md. PI_SKIP_VERSION_CHECKenvironment variable to disable new version notifications at startup (#549 by @aos)thinkingBudgetssetting to customize token budgets per thinking level for token-based providers (#529 by @melihmucuk)- Extension UI dialogs (
ctx.ui.select(),ctx.ui.confirm(),ctx.ui.input()) now support atimeoutoption with live countdown display (#522 by @nicobailon) - Extensions can now provide custom editor components via
ctx.ui.setEditorComponent(). Seeexamples/extensions/modal-editor.tsanddocs/tui.mdPattern 7. - Extension factories can now be async, enabling dynamic imports and lazy-loaded dependencies (#513 by @austinm911)
ctx.shutdown()is now available in extension contexts for requesting a graceful shutdown. In interactive mode, shutdown is deferred until the agent becomes idle (after processing all queued steering and follow-up messages). In RPC mode, shutdown is deferred until after completing the current command response. In print mode, shutdown is a no-op as the process exits automatically when prompts complete. (#542 by @kaofelix)
Pi 0.37.6
New version of pi. Download from npm or view release on GitHub.
Added
- Extension UI dialogs (
ctx.ui.select(),ctx.ui.confirm(),ctx.ui.input()) now accept an optionalAbortSignalto programmatically dismiss dialogs. Useful for implementing timeouts. Seeexamples/extensions/timed-confirm.ts. (#474) - HTML export now shows bridge prompts in model change messages for Codex sessions (#510 by @mitsuhiko)
Pi 0.37.5
New version of pi. Download from npm or view release on GitHub.
Added
- ExtensionAPI:
setModel(),getThinkingLevel(),setThinkingLevel()methods for extensions to change model and thinking level at runtime (#509) - Exported truncation utilities for custom tools:
truncateHead,truncateTail,truncateLine,formatSize,DEFAULT_MAX_BYTES,DEFAULT_MAX_LINES,TruncationOptions,TruncationResult - New example
truncated-tool.tsdemonstrating proper output truncation with custom rendering for extensions - New example
preset.tsdemonstrating preset configurations with model/thinking/tools switching (#347) - Documentation for output truncation best practices in
docs/extensions.md - Exported all UI components for extensions:
ArminComponent,AssistantMessageComponent,BashExecutionComponent,BorderedLoader,BranchSummaryMessageComponent,CompactionSummaryMessageComponent,CustomEditor,CustomMessageComponent,DynamicBorder,ExtensionEditorComponent,ExtensionInputComponent,ExtensionSelectorComponent,FooterComponent,LoginDialogComponent,ModelSelectorComponent,OAuthSelectorComponent,SessionSelectorComponent,SettingsSelectorComponent,ShowImagesSelectorComponent,ThemeSelectorComponent,ThinkingSelectorComponent,ToolExecutionComponent,TreeSelectorComponent,UserMessageComponent,UserMessageSelectorComponent, plus utilitiesrenderDiff,truncateToVisualLines docs/tui.md: Common Patterns section with copy-paste code for SelectList, BorderedLoader, SettingsList, setStatus, setWidget, setFooterdocs/tui.md: Key Rules section documenting critical patterns for extension UI developmentdocs/extensions.md: Exhaustive example links for all ExtensionAPI methods and events- System prompt now references
docs/tui.mdfor TUI component development
Pi 0.37.4
New version of pi. Download from npm or view release on GitHub.
Added
- Session picker (
pi -r) and--sessionflag now support searching/resuming by session ID (UUID prefix) (#495 by @arunsathiya) - Extensions can now replace the startup header with
ctx.ui.setHeader(), seeexamples/extensions/custom-header.ts(#500 by @tudoroancea)
Changed
- Startup help text: fixed misleading "ctrl+k to delete line" to "ctrl+k to delete to end"
- Startup help text and
/hotkeys: added!!shortcut for running bash without adding output to context
Pi 0.37.3
New version of pi. Download from npm or view release on GitHub.
Added
- Extensions can now replace the footer with
ctx.ui.setFooter(), seeexamples/extensions/custom-footer.ts(#481) - Session ID is now forwarded to LLM providers for session-based caching (used by OpenAI Codex for prompt caching).
- Added
blockImagessetting to prevent images from being sent to LLM providers (#492 by @jsinge97) - Extensions can now send user messages via
pi.sendUserMessage()(#483)
Pi 0.37.2
New version of pi. Download from npm or view release on GitHub.
Fixed
- Extension directories in
settings.jsonnow respectpackage.jsonmanifests, matching global extension behavior (#480 by @prateekmedia) - Share viewer: deep links now scroll to the target message when opened via
/share - Bash tool now handles spawn errors gracefully instead of crashing the agent (missing cwd, invalid shell path) (#479 by @robinwander)
Pi 0.37.0
New version of pi. Download from npm or view release on GitHub.
Added
- Share viewer: copy-link button on messages to share URLs that navigate directly to a specific message (#477 by @lockmeister)
- Extension example: add
claude-rulesto load.claude/rules/entries into the system prompt (#461 by @vaayne) - Headless OAuth login: all providers now show paste input for manual URL/code entry, works over SSH without DISPLAY (#428 by @ben-vargas, #468 by @crcatala)
Changed
- OAuth login UI now uses dedicated dialog component with consistent borders
- Assume truecolor support for all terminals except
dumb, empty, orlinux(fixes colors over SSH) - OpenAI Codex clean-up: removed per-thinking-level model variants, thinking level is now set separately and the provider clamps to what each model supports internally (initial implementation in #472 by @ben-vargas)
Pi 0.35.0
New version of pi. Download from npm or view release on GitHub.
Changes
This release unifies hooks and custom tools into a single "extensions" system and renames "slash commands" to "prompt templates". (#454)
Before migrating, read:
- docs/extensions.md - Full API reference
- README.md - Extensions section with examples
- examples/extensions/ - Working examples
Extensions Migration
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 toprompts/on startup (both~/.pi/agent/commands/and.pi/commands/)
Manual migration required:
- Move files from
hooks/andtools/directories toextensions/(deprecation warnings shown on startup) - Update imports and type names in your extension code
- Update
settings.jsonif 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/*.tsExtension discovery rules (in extensions/ directories):
- Direct files:
extensions/*.tsor*.js→ loaded directly - Subdirectory with index:
extensions/myext/index.ts→ loaded as single extension - Subdirectory with package.json:
extensions/myext/package.jsonwith"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→ExtensionAPIHookContext→ExtensionContextHookCommandContext→ExtensionCommandContextHookUIContext→ExtensionUIContextCustomToolAPI→ExtensionAPI(merged)CustomToolContext→ExtensionContext(merged)CustomToolUIContext→ExtensionUIContextCustomTool→ToolDefinitionCustomToolFactory→ExtensionFactoryHookMessage→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 callpi.registerCommand()- Register commands like/mycommandpi.registerShortcut()- Register keyboard shortcuts (shown in/hotkeys)pi.registerFlag()- Register CLI flags (shown in--help)pi.registerMessageRenderer()- Custom TUI rendering for message typespi.on()- Subscribe to lifecycle events (tool_call, session_start, etc.)pi.sendMessage()- Inject messages into the conversationpi.appendEntry()- Persist custom data in session (survives restart/branch)pi.exec()- Run shell commandspi.getActiveTools()/pi.setActiveTools()- Dynamic tool enable/disablepi.getAllTools()- List all available toolspi.events- Event bus for cross-extension communicationctx.ui.confirm()/select()/input()- User promptsctx.ui.notify()- Toast notificationsctx.ui.setStatus()- Persistent status in footer (multiple extensions can set their own)ctx.ui.setWidget()- Widget display above editorctx.ui.setTitle()- Set terminal window titlectx.ui.custom()- Full TUI component with keyboard handlingctx.ui.editor()- Multi-line text editor with external editor supportctx.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.tsPrompt Templates Migration
"Slash commands" (markdown files defining reusable prompts invoked via /name) are renamed to "prompt templates" to avoid confusion with extension-registered commands.
Automatic migration: The commands/ directory is automatically renamed to prompts/ on startup (if prompts/ doesn't exist). Works for both regular directories and symlinks.
Directory changes:
~/.pi/agent/commands/*.md → ~/.pi/agent/prompts/*.md
.pi/commands/*.md → .pi/prompts/*.mdSDK type renames:
FileSlashCommand→PromptTemplateLoadSlashCommandsOptions→LoadPromptTemplatesOptions
SDK function renames:
discoverSlashCommands()→discoverPromptTemplates()loadSlashCommands()→loadPromptTemplates()expandSlashCommand()→expandPromptTemplate()getCommandsDir()→getPromptsDir()
SDK option renames:
CreateAgentSessionOptions.slashCommands→.promptTemplatesAgentSession.fileCommands→.promptTemplatesPromptOptions.expandSlashCommands→.expandPromptTemplates
SDK Migration
Discovery functions:
discoverAndLoadHooks()→discoverAndLoadExtensions()discoverAndLoadCustomTools()→ merged intodiscoverAndLoadExtensions()loadHooks()→loadExtensions()loadCustomTools()→ merged intoloadExtensions()
Runner and wrapper:
HookRunner→ExtensionRunnerwrapToolsWithHooks()→wrapToolsWithExtensions()wrapToolWithHooks()→wrapToolWithExtensions()
CreateAgentSessionOptions:
.hooks→ removed (use.additionalExtensionPathsfor paths).additionalHookPaths→.additionalExtensionPaths.preloadedHooks→.preloadedExtensions.customToolstype changed:Array<{ path?; tool: CustomTool }>→ToolDefinition[].additionalCustomToolPaths→ merged into.additionalExtensionPaths.slashCommands→.promptTemplates
AgentSession:
.hookRunner→.extensionRunner.fileCommands→.promptTemplates.sendHookMessage()→.sendCustomMessage()
Session Migration
Automatic. Session version bumped from 2 to 3. Existing sessions are migrated on first load:
- Message role
"hookMessage"→"custom"
Breaking Changes
- Settings:
hooksandcustomToolsarrays replaced with singleextensionsarray - CLI:
--hookand--toolflags replaced with--extension/-e - Directories:
hooks/,tools/→extensions/;commands/→prompts/ - Types: See type renames above
- SDK: See SDK migration above
Changed
- Extensions can have their own
package.jsonwith dependencies (resolved via jiti) - Documentation:
docs/hooks.mdanddocs/custom-tools.mdmerged intodocs/extensions.md - Examples:
examples/hooks/andexamples/custom-tools/merged intoexamples/extensions/ - README: Extensions section expanded with custom tools, commands, events, state persistence, shortcuts, flags, and UI examples
- SDK:
customToolsoption now acceptsToolDefinition[]directly (simplified fromArray<{ path?, tool }>) - SDK:
extensionsoption acceptsExtensionFactory[]for inline extensions - SDK:
additionalExtensionPathsreplaces bothadditionalHookPathsandadditionalCustomToolPaths
Pi 0.34.0
New version of pi. Download from npm or view release on GitHub.
Added
- Hook API:
pi.getActiveTools()andpi.setActiveTools(toolNames)for dynamically enabling/disabling tools from hooks - Hook API:
pi.getAllTools()to enumerate all configured tools (built-in via --tools or default, plus custom tools) - Hook API:
pi.registerFlag(name, options)andpi.getFlag(name)for hooks to register custom CLI flags (parsed automatically) - Hook API:
pi.registerShortcut(shortcut, options)for hooks to register custom keyboard shortcuts usingKeyId(e.g.,Key.shift("p")). Conflicts with built-in shortcuts are skipped, conflicts between hooks logged as warnings. - Hook API:
ctx.ui.setWidget(key, content)for status displays above the editor. Accepts either a string array or a component factory function. - Hook API:
theme.strikethrough(text)for strikethrough text styling - Hook API:
before_agent_starthandlers can now returnsystemPromptAppendto dynamically append text to the system prompt for that turn. Multiple hooks' appends are concatenated. - Hook API:
before_agent_starthandlers can now return multiple messages (all are injected, not just the first) /hotkeyscommand now shows hook-registered shortcuts in a separate "Hooks" section- New example hook:
plan-mode.ts- Claude Code-style read-only exploration mode:- Toggle via
/plancommand,Shift+Pshortcut, or--planCLI flag - Read-only tools:
read,bash,grep,find,ls(noedit/write) - Bash commands restricted to non-destructive operations (blocks
rm,mv,git commit,npm install, etc.) - Interactive prompt after each response: execute plan, stay in plan mode, or refine
- Todo list widget showing progress with checkboxes and strikethrough for completed items
- Each todo has a unique ID; agent marks items done by outputting
[DONE:id] - Progress updates via
agent_endhook (parses completed items from final message) /todoscommand to view current plan progress- Shows
⏸ planindicator in footer when in plan mode,📋 2/5when executing - State persists across sessions (including todo progress)
- Toggle via
- New example hook:
tools.ts- Interactive/toolscommand to enable/disable tools with session persistence - New example hook:
pirate.ts- DemonstratessystemPromptAppendto make the agent speak like a pirate - Tool registry now contains all built-in tools (read, bash, edit, write, grep, find, ls) even when
--toolslimits the initially active set. Hooks can enable any tool from the registry viapi.setActiveTools(). - System prompt now automatically rebuilds when tools change via
setActiveTools(), updating tool descriptions and guidelines to match the new tool set - Hook errors now display full stack traces for easier debugging
- Event bus (
pi.events) for tool/hook communication: shared pub/sub between custom tools and hooks - Custom tools now have
pi.sendMessage()to send messages directly to the agent session without needing the event bus sendMessage()supportsdeliverAs: "nextTurn"to queue messages for the next user prompt
Changed
- Removed image placeholders after copy & paste, replaced with inserting image file paths directly. (#442 by @mitsuhiko)
Pi 0.33.0
New version of pi. Download from npm or view release on GitHub.
Breaking Changes
- Key detection functions removed from
@mariozechner/pi-tui: AllisXxx()key detection functions (isEnter(),isEscape(),isCtrlC(), etc.) have been removed. UsematchesKey(data, keyId)instead (e.g.,matchesKey(data, "enter"),matchesKey(data, "ctrl+c")). This affects hooks and custom tools that usectx.ui.custom()with keyboard input handling. (#405)
Added
- Clipboard image paste support via
Ctrl+V. Images are saved to a temp file and attached to the message. Works on macOS, Windows, and Linux (X11). (#419) - Configurable keybindings via
~/.pi/agent/keybindings.json. All keyboard shortcuts (editor navigation, deletion, app actions like model cycling, etc.) can now be customized. Supports multiple bindings per action. (#405 by @hjanuschka) /quitand/exitslash commands to gracefully exit the application. Unlike double Ctrl+C, these properly await hook and custom tool cleanup handlers before exiting. (#426 by @ben-vargas)