Zeitgeist
Between v0.21.0 and v0.30.0 (December 14-25, 2025), pi evolved from a coding agent with hooks into a genuine plugin platform. The 298 commits in this stage introduced custom tools, the programmatic SDK, GitHub Copilot as a provider, project-specific settings, sub-agent orchestration, syntax highlighting, credential refactoring, and a unified settings menu. The extensibility surface grew from "intercept events" to "register tools, commands, shortcuts, and renderers." By Christmas Day, when v0.30.0 shipped, pi had become something closer to an IDE than a CLI.
The driving force behind this stage was the realization that hooks alone were insufficient. Hooks could observe and modify, but users wanted to create. The custom tools system (v0.23.0) let TypeScript modules define entirely new tools the LLM could call, with typed parameters, custom TUI rendering, and session lifecycle awareness. The SDK (v0.26.0) exposed createAgentSession() as a factory function, making pi embeddable in any Node.js application. Together, these two features transformed pi from an end-user product into developer infrastructure.
This was also the stage where pi's community contributions accelerated dramatically. Contributors like @nicobailon, @markusylisiurunen, @aliou, @mitsuhiko, @scutifer, @kim0, and @LukeFost submitted PRs ranging from keyboard protocol support to external editor integration. The AGENTS.md style guide -- "no emojis, no fluff" -- ensured a consistent voice across all these contributions, and the changelog rules prevented anyone from modifying released version sections.
Key Developments
Version 0.23.0 (e7097d91) introduced the custom tools system. Tools were TypeScript modules in ~/.pi/agent/tools/*/index.ts or .pi/tools/*/index.ts, exporting a factory function that received a ToolAPI object. Unlike hooks, custom tools defined parameters via TypeBox schemas, had execute() methods that returned structured results, and could render custom TUI components. The factory pattern gave tools access to session lifecycle events (onSession) for maintaining state across branches and restarts. Reserved names prevented shadowing built-in tools. The sub-agent example (#215, eb1d08a5) demonstrated the most powerful use case: a tool that spawned a new AgentSession to handle delegated tasks, with chain streaming showing all sub-agent steps.
The Programmatic SDK
Version 0.26.0 (#272, 5482bf3e) introduced createAgentSession() with a philosophy stated in the documentation: "Omit to discover, provide to override." Every option had a sensible default derived from the standard file system layout (~/.pi/agent/), but any option could be overridden for embedding or testing. The SDK came with 12 examples and comprehensive documentation in docs/sdk.md. SessionManager gained static factories (create(), open(), continueRecent(), inMemory(), list()), and SettingsManager followed the same pattern. This was the formalization of pi as a library, not just a CLI.
GitHub Copilot Provider
Commits b66157c6 through c5543f75 added GitHub Copilot as a provider (#191), including OAuth token integration, model enablement via VS Code, and auto-detection of available models from the Copilot /models endpoint. The model generation script (5f590b7c) could now pull model catalogs from Copilot alongside other providers. This was strategically important: Copilot subscriptions provided free access to models like GPT-4o and Claude Sonnet, meaning pi could be zero-cost for developers already paying for GitHub.
Credential Refactoring: AuthStorage and ModelRegistry
Version 0.28.0 (#296) refactored how pi managed credentials. API keys and OAuth tokens moved from scattered locations (environment variables, settings.json, oauth.json) into a unified auth.json via the new AuthStorage class. ModelRegistry combined built-in models with custom models.json entries and resolved API keys through AuthStorage. The refactoring fixed a real bug: OAuth tokens previously lost priority to settings.json API keys (a965b6f1), causing users with unlimited plans to be billed via pay-as-you-go. The new system established a clear priority chain: runtime overrides, then OAuth, then auth.json, then environment variables.
Syntax Highlighting and Visual Polish
The merge of the syntax-highlighting branch (039b3a08) added both code block highlighting in markdown and intra-line diff highlighting for the edit tool (51171873). The implementation used cli-highlight with theme-aware colors matching the TUI's palette. A subsequent fix (d5dde00d) validated language identifiers before highlighting to prevent stderr spam from malformed code fences. Markdown table rendering was also fixed to respect terminal width (#206, c1113dee), and the HTML export gained syntax highlighting and theme support (#274, series of commits by @scutifer).
Session Lifecycle Hooks and Cancellation
Version 0.27.0 (#278) redesigned the session hooks API with before_* variants (before_switch, before_clear, before_branch) that could cancel actions by returning { cancel: true }. The branch event was merged into the session event with reason discrimination. This was driven by the checkpoint hook pattern: git-based hooks needed to stash changes before a branch and restore them after, and the two-phase event model made this possible without race conditions.
Project-Specific Settings and SYSTEM.md
Version 0.26.0 (#276) introduced project-specific settings loaded from <cwd>/.pi/settings.json, with deep merge against global settings. Project settings were read-only (intended for version control). Version 0.29.1 added automatic SYSTEM.md loading (f8b6164e), letting projects replace pi's default system prompt entirely. This completed the customization hierarchy: AGENTS.md for context, skills for knowledge, hooks for behavior, settings for configuration, and SYSTEM.md for the system prompt itself.
Philosophy Shifts
The documentation at v0.30.0 showed a maturing taxonomy of extension points. A table in docs/custom-tools.md laid out when to use each mechanism: AGENTS.md for always-needed context, slash commands for user-triggered prompts, skills for on-demand capability packages, and custom tools for LLM-callable functions. This wasn't just documentation -- it was a design philosophy that each extension point should have one clear purpose. The hooks documentation explicitly noted that tool_call events had no timeout "since they may prompt the user," while other events had a configurable hookTimeout -- a nuanced distinction that showed the system was being designed for real-world use rather than theoretical elegance.
AGENTS.md itself expanded to include rules about changelog attribution (internal changes cite issues, external contributions cite PRs and usernames), package labels for GitHub issues (pkg:agent, pkg:ai, pkg:coding-agent, etc.), and the removal of browser tools reference in favor of direct tmux-based TUI interaction. The coding-agent-specific section was removed from AGENTS.md (a8b58335), with package-specific guidance moving to per-package DEVELOPMENT.md files.
Looking Forward
The proliferation of extension mechanisms (hooks, custom tools, skills, slash commands, settings, SYSTEM.md) created an ergonomic problem that Stage 6 would address by unifying hooks and custom tools into a single "extensions" concept. The SDK's createAgentSession() factory anticipated the ExtensionAPI unification -- both used the same "omit to discover, provide to override" philosophy. The session lifecycle hooks with cancellation support laid the groundwork for the session tree's navigation events. And the sub-agent example proved that pi's runtime could be composed recursively, a pattern that would become central to the project's long-term vision.