CodeVetter · Repo Unpack · Extension points
earendil-works/pi
Pi's core is deliberately minimal — CONTRIBUTING.md says features that don't belong in core should be extensions. The main seam is the ExtensionAPI: TypeScript modules loaded by jiti from user/project dirs, CLI -e, settings, or npm/git packages, which can register tools, slash commands, providers, CLI flags, keybindings, UI widgets/renderers, and ~35 lifecycle event handlers. Lower-level seams: pluggable tool Operations (e.g. BashOperations for remote execution), Provider registration in pi-ai, Chord facets/services for the experimental stack, skills/prompt-templates/themes as data resources, and custom session entries for extension persistence.
- ExtensionAPI is the central registry: on() subscribes to ~35 typed events (project_trust, session_start, session_compact, context, before_provider_headers, after_provider_response, agent_start/end, turn_start/end, message_*, tool_execution_*, tool_call, tool_result, user_bash, input, model_select, thinking_level_select, ui_prompt_start/end, agent_settled, session_tree, session_shutdown, session_info_changed).
- Extensions register LLM-callable tools via registerTool (typebox-schema params, execute callback, optional renderers), user commands via registerCommand, and providers via registerProvider(name, ProviderConfig)/unregisterProvider — with doc-commented examples for corporate proxies.
- Extensions are discovered from: ~/.pi/agent paths, project .pi/extensions, settings extensions[] arrays, CLI -e, package-managed npm/git sources, and InlineExtension factories passed programmatically; loading uses jiti with module aliases or embedded virtual modules in compiled-binary mode.
- Built-in tools accept pluggable Operations interfaces — BashOperations.exec can delegate to SSH or a micro-VM (the Gondolin example extension does exactly this), and EditOperations/ReadOperations etc. follow the same pattern.
- ~70 example extensions under packages/coding-agent/examples/extensions demonstrate every seam: tools.ts, commands.ts, subagent/, plan-mode/, permission-gate.ts, sandbox/, gondolin/, ssh.ts, custom-provider-anthropic/, todo.ts, handoff.ts, confirm-destructive.ts, protected-paths.ts — doubling as living documentation.
- Skills (Markdown + frontmatter) and prompt templates are pure-data extension points discovered from .pi/skills, .agents/skills ancestors, and configured paths; themes are JSON files with a validator hook (setThemeJsonValidator) that hosts can replace.
- Extensions can persist state in the session file via CustomEntry (customType namespaced data, excluded from LLM context) and inject context via CustomMessageEntry — documented in the session-manager type comments.
- CLI flag extension: unknown flags are collected into parsed.unknownFlags and passed to the runtime as extensionFlagValues, so extensions can define their own --flags which appear in printHelp output.
- pi-ai's Models registry is itself an extension point: setProvider() adds a Provider implementation to the mutable registry; the coding-agent composes configured, extension-registered, and remote-catalog providers through provider-composer.
- The agent loop exposes hooks for hosts: transformContext, convertToLlm, prepareRequest, prepareNextTurn(WithContext), beforeToolCall, afterToolCall, finishTurn, getSteeringMessages, getFollowUpMessages — Agent maps these to public constructor options.
- In the experimental stack, Chord facets/plugins declare provided/required services that a host validates and activates in dependency order; pi-server's RoutedSessionHandle.invokeService routes opaque service envelopes so new application services plug in without server-side schema changes.
- Stream function is injectable: setDefaultStreamFn lets hosts install a default StreamFn for Agent/agentLoop without pi-agent-core depending on a provider catalog; AgentSession wires ModelRuntime's stream through this seam.
- An experimental plugin entry (./experimental/plugin export) plus experimental/ sources (session-worker, coordinator, client-tui, radius-relay, services/) form the in-progress multi-process/plugin architecture — explicitly exported as unstable.