Changelog
Release notes and changes from the Pi changelog.
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/*.ts
Extension 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.ts
Prompt 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/*.md
SDK 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)
Pi 0.32.3
New version of pi. Download from npm or view release on GitHub.
Fixed
--list-modelsno longer shows Google Vertex AI models without explicit authentication configured- JPEG/GIF/WebP images not displaying in terminals using Kitty graphics protocol (Kitty, Ghostty, WezTerm). The protocol requires PNG format, so non-PNG images are now converted before display.
- Version check URL typo preventing update notifications from working (#423 by @skuridin)
- Large images exceeding Anthropic's 5MB limit now retry with progressive quality/size reduction (#424 by @mitsuhiko)
Pi 0.32.2
New version of pi. Download from npm or view release on GitHub.
Added
Changed
- Slash commands and hook commands now work during streaming: Previously, using a slash command or hook command while the agent was streaming would crash with "Agent is already processing". Now:
- Hook commands execute immediately (they manage their own LLM interaction via
pi.sendMessage()) - File-based slash commands are expanded and queued via steer/followUp
steer()andfollowUp()now expand file-based slash commands and error on hook commands (hook commands cannot be queued)prompt()accepts newstreamingBehavioroption ("steer"or"followUp") to specify queueing behavior during streaming- RPC
promptcommand now accepts optionalstreamingBehaviorfield (#420)
- Hook commands execute immediately (they manage their own LLM interaction via
Pi 0.32.1
New version of pi. Download from npm or view release on GitHub.
Added
- Shell commands without context contribution: use
!!commandto execute a bash command that is shown in the TUI and saved to session history but excluded from LLM context. Useful for running commands you don't want the AI to see. (#414)
Pi 0.32.0
New version of pi. Download from npm or view release on GitHub.
Breaking Changes
- Queue API replaced with steer/followUp: The
queueMessage()method has been split into two methods with different delivery semantics (#403):steer(text): Interrupts the agent mid-run (Enter while streaming). Delivered after current tool execution.followUp(text): Waits until the agent finishes (Alt+Enter while streaming). Delivered only when agent stops.
- Settings renamed:
queueModesetting renamed tosteeringMode. Added newfollowUpModesetting. Old settings.json files are migrated automatically. - AgentSession methods renamed:
queueMessage()→steer()andfollowUp()queueModegetter →steeringModeandfollowUpModegetterssetQueueMode()→setSteeringMode()andsetFollowUpMode()queuedMessageCount→pendingMessageCountgetQueuedMessages()→getSteeringMessages()andgetFollowUpMessages()clearQueue()now returns{ steering: string[], followUp: string[] }hasQueuedMessages()→hasPendingMessages()
- Hook API signature changed:
pi.sendMessage()second parameter changed fromtriggerTurn?: booleantooptions?: { triggerTurn?, deliverAs? }. UsedeliverAs: "followUp"for follow-up delivery. Affects both hooks and internalsendHookMessage()method. - RPC API changes:
queue_messagecommand →steerandfollow_upcommandsset_queue_modecommand →set_steering_modeandset_follow_up_modecommandsRpcSessionState.queueMode→steeringModeandfollowUpMode
- Settings UI: "Queue mode" setting split into "Steering mode" and "Follow-up mode"
Added
- Configurable double-escape action: choose whether double-escape with empty editor opens
/tree(default) or/branch. Configure via/settingsordoubleEscapeActionin settings.json (#404) - Vertex AI provider (
google-vertex): access Gemini models via Google Cloud Vertex AI using Application Default Credentials (#300 by @default-anton) - Built-in provider overrides in
models.json: override justbaseUrlto route a built-in provider through a proxy while keeping all its models, or definemodelsto fully replace the provider (#406 by @yevhen) - Automatic image resizing: images larger than 2000x2000 are resized for better model compatibility. Original dimensions are injected into the prompt. Controlled via
/settingsorimages.autoResizein settings.json. (#402 by @mitsuhiko) - Alt+Enter keybind to queue follow-up messages while agent is streaming
ThemeandThemeColortypes now exported for hooks usingctx.ui.custom()- Terminal window title now displays "pi - dirname" to identify which project session you're in (#407 by @kaofelix)
Changed
- Editor component now uses word wrapping instead of character-level wrapping for better readability (#382 by @nickseelert)
Pi 0.31.1
New version of pi. Download from npm or view release on GitHub.
Fixed
- Model selector no longer allows negative index when pressing arrow keys before models finish loading (#398 by @mitsuhiko)
- Type guard functions (
isBashToolResult, etc.) now exported at runtime, not just in type declarations (#397)
Pi 0.31.0
New version of pi. Download from npm or view release on GitHub.
Changes
This release introduces session trees for in-place branching, major API changes to hooks and custom tools, and structured compaction with file tracking.
Session Tree
Sessions now use a tree structure with id/parentId fields. This enables in-place branching: navigate to any previous point with /tree, continue from there, and switch between branches while preserving all history in a single file.
Existing sessions are automatically migrated (v1 → v2) on first load. No manual action required.
New entry types: BranchSummaryEntry (context from abandoned branches), CustomEntry (hook state), CustomMessageEntry (hook-injected messages), LabelEntry (bookmarks).
See docs/session.md for the file format and SessionManager API.
Hooks Migration
The hooks API has been restructured with more granular events and better session access.
Type renames:
HookEventContext→HookContextHookCommandContextis now a new interface extendingHookContextwith session control methods
Event changes:
- The monolithic
sessionevent is now split into granular events:session_start,session_before_switch,session_switch,session_before_branch,session_branch,session_before_compact,session_compact,session_shutdown session_before_switchandsession_switchevents now includereason: "new" | "resume"to distinguish between/newand/resume- New
session_before_treeandsession_treeevents for/treenavigation (hook can provide custom branch summary) - New
before_agent_startevent: inject messages before the agent loop starts - New
contextevent: modify messages non-destructively before each LLM call - Session entries are no longer passed in events. Use
ctx.sessionManager.getEntries()orctx.sessionManager.getBranch()instead
API changes:
pi.send(text, attachments?)→pi.sendMessage(message, triggerTurn?)(createsCustomMessageEntry)- New
pi.appendEntry(customType, data?)for hook state persistence (not in LLM context) - New
pi.registerCommand(name, options)for custom slash commands (handler receivesHookCommandContext) - New
pi.registerMessageRenderer(customType, renderer)for custom TUI rendering - New
ctx.isIdle(),ctx.abort(),ctx.hasQueuedMessages()for agent state (available in all events) - New
ctx.ui.editor(title, prefill?)for multi-line text editing with Ctrl+G external editor support - New
ctx.ui.custom(component)for full TUI component rendering with keyboard focus - New
ctx.ui.setStatus(key, text)for persistent status text in footer (multiple hooks can set their own) - New
ctx.ui.themegetter for styling text with theme colors ctx.exec()moved topi.exec()ctx.sessionFile→ctx.sessionManager.getSessionFile()- New
ctx.modelRegistryandctx.modelfor API key resolution
HookCommandContext (slash commands only):
ctx.waitForIdle()- wait for agent to finish streamingctx.newSession(options?)- create new sessions with optional setup callback- `ctx.fork(entryId) - fork from a specific entry, creating a new session file
ctx.navigateTree(targetId, options?)- navigate the session tree
These methods are only on HookCommandContext (not HookContext) because they can deadlock if called from event handlers that run inside the agent loop.
Removed:
hookTimeoutsetting (hooks no longer have timeouts; use Ctrl+C to abort)resolveApiKeyparameter (usectx.modelRegistry.getApiKey(model))
See docs/hooks.md and examples/hooks/ for the current API.
Custom Tools Migration
The custom tools API has been restructured to mirror the hooks pattern with a context object.
Type renames:
CustomAgentTool→CustomToolToolAPI→CustomToolAPIToolContext→CustomToolContextToolSessionEvent→CustomToolSessionEvent
Execute signature changed:
// Before (v0.30.2)
execute(toolCallId, params, signal, onUpdate)
// After
execute(toolCallId, params, onUpdate, ctx, signal?)
The new ctx: CustomToolContext provides sessionManager, modelRegistry, model, and agent state methods:
ctx.isIdle()- check if agent is streamingctx.hasQueuedMessages()- check if user has queued messages (skip interactive prompts)ctx.abort()- abort current operation (fire-and-forget)
Session event changes:
CustomToolSessionEventnow only hasreasonandpreviousSessionFile- Session entries are no longer in the event. Use
ctx.sessionManager.getBranch()orctx.sessionManager.getEntries()to reconstruct state - Reasons:
"start" | "switch" | "branch" | "tree" | "shutdown"(no separate"new"reason;/newtriggers"switch") dispose()method removed. UseonSessionwithreason: "shutdown"for cleanup
See docs/custom-tools.md and examples/custom-tools/ for the current API.
SDK Migration
Type changes:
CustomAgentTool→CustomToolAppMessage→AgentMessagesessionFilereturnsstring | undefined(wasstring | null)modelreturnsModel | undefined(wasModel | null)Attachmenttype removed. UseImageContentfrom@mariozechner/pi-aiinstead. Add images directly to message content arrays.
AgentSession API:
branch(entryIndex: number)→branch(entryId: string)getUserMessagesForBranching()returns{ entryId, text }instead of{ entryIndex, text }reset()→newSession(options?)where options has optionalparentSessionfor lineage trackingnewSession()andswitchSession()now returnPromise<boolean>(false if cancelled by hook)- New
navigateTree(targetId, options?)for in-place tree navigation
Hook integration:
- New
sendHookMessage(message, triggerTurn?)for hook message injection
SessionManager API:
- Method renames:
saveXXX()→appendXXX()(e.g.,appendMessage,appendCompaction) branchInPlace()→branch()reset()→newSession(options?)with optionalparentSessionfor lineage trackingcreateBranchedSessionFromEntries(entries, index)→createBranchedSession(leafId)SessionHeader.branchedFrom→SessionHeader.parentSessionsaveCompaction(entry)→appendCompaction(summary, firstKeptEntryId, tokensBefore, details?)getEntries()now excludes the session header (usegetHeader()separately)getSessionFile()returnsstring | undefined(undefined for in-memory sessions)- New tree methods:
getTree(),getBranch(),getLeafId(),getLeafEntry(),getEntry(),getChildren(),getLabel() - New append methods:
appendCustomEntry(),appendCustomMessageEntry(),appendLabelChange() - New branch methods:
branch(entryId),branchWithSummary()
ModelRegistry (new):
ModelRegistry is a new class that manages model discovery and API key resolution. It combines built-in models with custom models from models.json and resolves API keys via AuthStorage.
import {
discoverAuthStorage,
discoverModels,
} from "@mariozechner/pi-coding-agent";
const authStorage = discoverAuthStorage(); // ~/.pi/agent/auth.json
const modelRegistry = discoverModels(authStorage); // + ~/.pi/agent/models.json
// Get all models (built-in + custom)
const allModels = modelRegistry.getAll();
// Get only models with valid API keys
const available = await modelRegistry.getAvailable();
// Find specific model
const model = modelRegistry.find("anthropic", "claude-sonnet-4-20250514");
// Get API key for a model
const apiKey = await modelRegistry.getApiKey(model);
This replaces the old resolveApiKey callback pattern. Hooks and custom tools access it via ctx.modelRegistry.
Renamed exports:
messageTransformer→convertToLlmSessionContextaliasLoadedSessionremoved
See docs/sdk.md and examples/sdk/ for the current API.
RPC Migration
Session commands:
resetcommand →new_sessioncommand with optionalparentSessionfield
Branching commands:
branchcommand:entryIndex→entryIdget_branch_messagesresponse:entryIndex→entryId
Type changes:
- Messages are now
AgentMessage(wasAppMessage) promptcommand:attachmentsfield replaced withimagesfield usingImageContentformat
Compaction events:
auto_compaction_startnow includesreasonfield ("threshold"or"overflow")auto_compaction_endnow includeswillRetryfieldcompactresponse includes fullCompactionResult(summary,firstKeptEntryId,tokensBefore,details)
See docs/rpc.md for the current protocol.
Structured Compaction
Compaction and branch summarization now use a structured output format:
- Clear sections: Goal, Progress, Key Information, File Operations
- File tracking:
readFilesandmodifiedFilesarrays indetails, accumulated across compactions - Conversations are serialized to text before summarization to prevent the model from "continuing" them
The before_compact and before_tree hook events allow custom compaction implementations. See docs/compaction.md.
Interactive Mode
/tree command:
- Navigate the full session tree in-place
- Search by typing, page with ←/→
- Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
- Press
lto label entries as bookmarks - Selecting a branch switches context and optionally injects a summary of the abandoned branch
Entry labels:
- Bookmark any entry via
/tree→ select →l - Labels appear in tree view and persist as
LabelEntry
Theme changes (breaking for custom themes):
Custom themes must add these new color tokens or they will fail to load:
selectedBg: background for selected/highlighted items in tree selector and other componentscustomMessageBg: background for hook-injected messages (CustomMessageEntry)customMessageText: text color for hook messagescustomMessageLabel: label color for hook messages (the[customType]prefix)
Total color count increased from 46 to 50. See docs/themes.md for the full color list and copy values from the built-in dark/light themes.
Settings:
enabledModels: allowlist models insettings.json(same format as--modelsCLI)
Added
ctx.ui.setStatus(key, text)for hooks to display persistent status text in the footer (#385 by @prateekmedia)ctx.ui.themegetter for styling status text and other output with theme colors/sharecommand to upload session as a secret GitHub gist and get a shareable URL via pi.dev (#380)- HTML export now includes a tree visualization sidebar for navigating session branches (#375)
- HTML export supports keyboard shortcuts: Ctrl+T to toggle thinking blocks, Ctrl+O to toggle tool outputs
- HTML export supports theme-configurable background colors via optional
exportsection in theme JSON (#387 by @mitsuhiko) - HTML export syntax highlighting now uses theme colors and matches TUI rendering
- Snake game example hook: Demonstrates
ui.custom(),registerCommand(), and session persistence. See examples/hooks/snake.ts. thinkingTexttheme token: Configurable color for thinking block text. (#366 by @paulbettner)
Changed
- Entry IDs: Session entries now use short 8-character hex IDs instead of full UUIDs
- API key priority:
ANTHROPIC_OAUTH_TOKENnow takes precedence overANTHROPIC_API_KEY - HTML export template split into separate files (template.html, template.css, template.js) for easier maintenance
Pi 0.30.1
New version of pi. Download from npm or view release on GitHub.
Fixed
- Sessions saved to wrong directory: In v0.30.0, sessions were being saved to
~/.pi/agent/instead of~/.pi/agent/sessions/<encoded-cwd>/, breaking--resumeand/resume. Misplaced sessions are automatically migrated on startup. (#320 by @aliou) - Custom system prompts missing context: When using a custom system prompt string, project context files (AGENTS.md), skills, date/time, and working directory were not appended. (#321)
Pi 0.30.0
New version of pi. Download from npm or view release on GitHub.
Breaking Changes
- SessionManager API: The second parameter of
create(),continueRecent(), andlist()changed fromagentDirtosessionDir. When provided, it specifies the session directory directly (no cwd encoding). When omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).open()no longer takesagentDir. (#313)
Added
--session-dirflag: Use a custom directory for sessions instead of the default~/.pi/agent/sessions/<encoded-cwd>/. Works with-c(continue) and-r(resume) flags. (#313 by @scutifer)- Reverse model cycling and model selector: Shift+Ctrl+P cycles models backward, Ctrl+L opens model selector (retaining text in editor). (#315 by @mitsuhiko)
Pi 0.29.1
New version of pi. Download from npm or view release on GitHub.
Added
- Automatic custom system prompt loading: Pi now auto-loads
SYSTEM.mdfiles to replace the default system prompt. Project-local.pi/SYSTEM.mdtakes precedence over global~/.pi/agent/SYSTEM.md. CLI--system-promptflag overrides both. (#309) - Unified
/settingscommand: New settings menu consolidating thinking level, theme, queue mode, auto-compact, show images, hide thinking, and collapse changelog. Replaces individual/thinking,/queue,/theme,/autocompact, and/show-imagescommands. (#310)
Pi 0.29.0
New version of pi. Download from npm or view release on GitHub.
Breaking Changes
- Renamed
/clearto/new: The command to start a fresh session is now/new. Hook event reasonsbefore_clear/clearare nowbefore_new/new. Merry Christmas @mitsuhiko! (#305)
Added
- Auto-space before pasted file paths: When pasting a file path (starting with
/,~, or.) after a word character, a space is automatically prepended. (#307 by @mitsuhiko) - Word navigation in input fields: Added Ctrl+Left/Right and Alt+Left/Right for word-by-word cursor movement. (#306 by @kim0)
- Full Unicode input: Input fields now accept Unicode characters beyond ASCII. (#306 by @kim0)
Pi 0.28.0
New version of pi. Download from npm or view release on GitHub.
Changed
Credential storage refactored: API keys and OAuth tokens are now stored in
~/.pi/agent/auth.jsoninstead ofoauth.jsonandsettings.json. Existing credentials are automatically migrated on first run. (#296)SDK API changes (#296):
- Added
AuthStorageclass for credential management (API keys and OAuth tokens) - Added
ModelRegistryclass for model discovery and API key resolution - Added
discoverAuthStorage()anddiscoverModels()discovery functions createAgentSession()now acceptsauthStorageandmodelRegistryoptions- Removed
configureOAuthStorage(),defaultGetApiKey(),findModel(),discoverAvailableModels() - Removed
getApiKeycallback option (useAuthStorage.setRuntimeApiKey()for runtime overrides) - Use
getModel()from@mariozechner/pi-aifor built-in models,modelRegistry.find()for custom models + built-in models - See updated SDK documentation and README
- Added
Settings changes: Removed
apiKeysfromsettings.json. Useauth.jsoninstead. (#296)
Pi 0.27.8
Pi 0.27.7
New version of pi. Download from npm or view release on GitHub.
Fixed
- Thinking tag leakage: Fixed Claude mimicking literal
</thinking>tags in responses. Unsigned thinking blocks (from aborted streams) are now converted to plain text without<thinking>tags. The TUI still displays them as thinking blocks. (#302 by @nicobailon)