co-mono/packages/coding-agent
Mario Zechner 9794868b38 Implement extension discovery with package.json manifest support
Discovery rules:
1. extensions/*.ts or *.js - direct files
2. extensions/*/index.ts or index.js - subdirectory with index
3. extensions/*/package.json with pi field - load declared paths

No recursion beyond one level. Complex packages use package.json manifest.

Added PiManifest type for future theme/skill bundling support.

17 tests covering all discovery scenarios.

refs #454
2026-01-04 23:24:24 +01:00
..
docs Fix event bus async error handling, clear pending messages on session switch, improve SDK docs 2026-01-04 22:04:50 +01:00
examples revert: remove unnecessary themeOverride params from theme functions 2026-01-04 20:27:46 +01:00
scripts Add session migration script for #320 2025-12-26 03:15:46 +01:00
src Implement extension discovery with package.json manifest support 2026-01-04 23:24:24 +01:00
test Implement extension discovery with package.json manifest support 2026-01-04 23:24:24 +01:00
CHANGELOG.md feat(coding-agent): add event bus for tool/hook communication (#431) 2026-01-04 21:36:19 +01:00
package.json Release v0.34.2 2026-01-04 21:06:08 +01:00
README.md docs: add note about macOS Finder image copy behavior 2026-01-04 20:52:51 +01:00
tsconfig.build.json Agent package + coding agent WIP, refactored web-ui prompts 2025-10-17 11:47:01 +02:00
tsconfig.examples.json Switch to more stable, oldder tsgo, switch all check scripts to tsgo 2025-12-18 15:20:41 +01:00
vitest.config.ts Agent package + coding agent WIP, refactored web-ui prompts 2025-10-17 11:47:01 +02:00

pi logo

Discord npm Build status

A terminal-based coding agent with multi-model support, mid-session model switching, and a simple CLI for headless coding tasks.

Works on Linux, macOS, and Windows (requires bash; see Windows Setup).

Table of Contents


Getting Started

Installation

npm (recommended):

npm install -g @mariozechner/pi-coding-agent

Standalone binary:

Download from GitHub Releases:

Platform Archive
macOS Apple Silicon pi-darwin-arm64.tar.gz
macOS Intel pi-darwin-x64.tar.gz
Linux x64 pi-linux-x64.tar.gz
Linux ARM64 pi-linux-arm64.tar.gz
Windows x64 pi-windows-x64.zip
# macOS/Linux
tar -xzf pi-darwin-arm64.tar.gz
./pi

# Windows
unzip pi-windows-x64.zip
pi.exe

macOS note: The binary is unsigned. If blocked, run: xattr -c ./pi

Build from source (requires Bun 1.0+):

git clone https://github.com/badlogic/pi-mono.git
cd pi-mono && npm install
cd packages/coding-agent && npm run build:binary
./dist/pi

Windows Setup

Pi requires a bash shell on Windows. Checked locations (in order):

  1. Custom path from ~/.pi/agent/settings.json
  2. Git Bash (C:\Program Files\Git\bin\bash.exe)
  3. bash.exe on PATH (Cygwin, MSYS2, WSL)

For most users, Git for Windows is sufficient.

Custom shell path:

// ~/.pi/agent/settings.json
{
  "shellPath": "C:\\cygwin64\\bin\\bash.exe"
}

API Keys & OAuth

Option 1: Auth file (recommended)

Add API keys to ~/.pi/agent/auth.json:

{
  "anthropic": { "type": "api_key", "key": "sk-ant-..." },
  "openai": { "type": "api_key", "key": "sk-..." },
  "google": { "type": "api_key", "key": "..." }
}

Option 2: Environment variables

Provider Auth Key Environment Variable
Anthropic anthropic ANTHROPIC_API_KEY
OpenAI openai OPENAI_API_KEY
Google google GEMINI_API_KEY
Mistral mistral MISTRAL_API_KEY
Groq groq GROQ_API_KEY
Cerebras cerebras CEREBRAS_API_KEY
xAI xai XAI_API_KEY
OpenRouter openrouter OPENROUTER_API_KEY
ZAI zai ZAI_API_KEY

Auth file keys take priority over environment variables.

OAuth Providers:

Use /login to authenticate with subscription-based or free-tier providers:

Provider Models Cost
Anthropic (Claude Pro/Max) Claude models via your subscription Subscription
GitHub Copilot GPT-4o, Claude, Gemini via Copilot subscription Subscription
Google Gemini CLI Gemini 2.0/2.5 models Free (Google account)
Google Antigravity Gemini 3, Claude, GPT-OSS Free (Google account)
pi
/login  # Select provider, authorize in browser

Note: /login replaces any existing API key for that provider with OAuth credentials in auth.json.

GitHub Copilot notes:

  • Press Enter for github.com, or enter your GitHub Enterprise Server domain
  • If you get "model not supported" error, enable it in VS Code: Copilot Chat → model selector → select model → "Enable"

Google providers notes:

  • Gemini CLI uses the production Cloud Code Assist endpoint (standard Gemini models)
  • Antigravity uses a sandbox endpoint with access to Gemini 3, Claude (sonnet/opus thinking), and GPT-OSS models
  • Both are free with any Google account, subject to rate limits

Credentials stored in ~/.pi/agent/auth.json. Use /logout to clear.

Quick Start

export ANTHROPIC_API_KEY=sk-ant-...
pi

Then chat:

You: Create a simple Express server in src/server.ts

The agent reads, writes, and edits files, and executes commands via bash.


Usage

Slash Commands

Command Description
/settings Open settings menu (thinking, theme, message delivery modes, toggles)
/model Switch models mid-session (fuzzy search, arrow keys, Enter to select)
/export [file] Export session to self-contained HTML
/share Upload session as secret GitHub gist, get shareable URL (requires gh CLI)
/session Show session info: path, message counts, token usage, cost
/hotkeys Show all keyboard shortcuts
/changelog Display full version history
/tree Navigate session tree in-place (search, filter, label entries)
/branch Create new conversation branch from a previous message
/resume Switch to a different session (interactive selector)
/login OAuth login for subscription-based models
/logout Clear OAuth tokens
/new Start a new session
/copy Copy last agent message to clipboard
/compact [instructions] Manually compact conversation context

Editor Features

File reference (@): Type @ to fuzzy-search project files. Respects .gitignore.

Path completion (Tab): Complete relative paths, ../, ~/, etc.

Drag & drop: Drag files from your file manager into the terminal.

Multi-line paste: Pasted content is collapsed to [paste #N <lines> lines] but sent in full.

Message queuing: Submit messages while the agent is working:

  • Enter queues a steering message, delivered after current tool execution (interrupts remaining tools)
  • Alt+Enter queues a follow-up message, delivered only after the agent finishes all work

Both modes are configurable via /settings: "one-at-a-time" delivers messages one by one waiting for responses, "all" delivers all queued messages at once. Press Escape to abort and restore queued messages to editor.

Keyboard Shortcuts

Navigation:

Key Action
Arrow keys Move cursor / browse history (Up when empty)
Option+Left/Right Move by word
Ctrl+A / Home / Cmd+Left Start of line
Ctrl+E / End / Cmd+Right End of line

Editing:

Key Action
Enter Send message
Shift+Enter / Alt+Enter New line (Ctrl+Enter on WSL)
Ctrl+W / Option+Backspace Delete word backwards
Ctrl+U Delete to start of line
Ctrl+K Delete to end of line

Other:

Key Action
Tab Path completion / accept autocomplete
Escape Cancel autocomplete / abort streaming
Ctrl+C Clear editor (first) / exit (second)
Ctrl+D Exit (when editor is empty)
Ctrl+Z Suspend to background (use fg in shell to resume)
Shift+Tab Cycle thinking level
Ctrl+P / Shift+Ctrl+P Cycle models forward/backward (scoped by --models)
Ctrl+L Open model selector
Ctrl+O Toggle tool output expansion
Ctrl+T Toggle thinking block visibility
Ctrl+G Edit message in external editor ($VISUAL or $EDITOR)
Ctrl+V Paste image from clipboard

Custom Keybindings

All keyboard shortcuts can be customized via ~/.pi/agent/keybindings.json. Each action can be bound to one or more keys.

Key format: modifier+key where modifiers are ctrl, shift, alt and keys are:

  • Letters: a-z
  • Numbers: 0-9
  • Special keys: escape, tab, enter, space, backspace, delete, home, end, up, down, left, right
  • Symbol keys: `, -, =, [, ], \, ;, ', ,, ., /, !, @, #, $, %, ^, &, *, (, ), _, +, |, ~, {, }, :, <, >, ?

Configurable actions:

Action Default Description
cursorUp up Move cursor up
cursorDown down Move cursor down
cursorLeft left Move cursor left
cursorRight right Move cursor right
cursorWordLeft alt+left, ctrl+left Move cursor word left
cursorWordRight alt+right, ctrl+right Move cursor word right
cursorLineStart home, ctrl+a Move to line start
cursorLineEnd end, ctrl+e Move to line end
deleteCharBackward backspace Delete char backward
deleteCharForward delete Delete char forward
deleteWordBackward ctrl+w, alt+backspace Delete word backward
deleteToLineStart ctrl+u Delete to line start
deleteToLineEnd ctrl+k Delete to line end
newLine shift+enter, alt+enter Insert new line
submit enter Submit input
tab tab Tab/autocomplete
interrupt escape Interrupt operation
clear ctrl+c Clear editor
exit ctrl+d Exit (when empty)
suspend ctrl+z Suspend process
cycleThinkingLevel shift+tab Cycle thinking level
cycleModelForward ctrl+p Next model
cycleModelBackward shift+ctrl+p Previous model
selectModel ctrl+l Open model selector
expandTools ctrl+o Expand tool output
toggleThinking ctrl+t Toggle thinking
externalEditor ctrl+g Open external editor
followUp alt+enter Queue follow-up message

Example (Emacs-style):

{
  "cursorUp": ["up", "ctrl+p"],
  "cursorDown": ["down", "ctrl+n"],
  "cursorLeft": ["left", "ctrl+b"],
  "cursorRight": ["right", "ctrl+f"],
  "cursorWordLeft": ["alt+left", "alt+b"],
  "cursorWordRight": ["alt+right", "alt+f"],
  "deleteCharForward": ["delete", "ctrl+d"],
  "deleteCharBackward": ["backspace", "ctrl+h"],
  "newLine": ["shift+enter", "ctrl+j"]
}

Example (Vim-style):

{
  "cursorUp": ["up", "alt+k"],
  "cursorDown": ["down", "alt+j"],
  "cursorLeft": ["left", "alt+h"],
  "cursorRight": ["right", "alt+l"],
  "cursorWordLeft": ["alt+left", "alt+b"],
  "cursorWordRight": ["alt+right", "alt+w"],
  "deleteCharBackward": ["backspace", "ctrl+h"],
  "deleteWordBackward": ["ctrl+w", "alt+backspace"]
}

Example (symbol keys):

{
  "submit": ["enter", "ctrl+j"],
  "newLine": ["shift+enter", "ctrl+;"],
  "toggleThinking": "ctrl+/",
  "cycleModelForward": "ctrl+.",
  "cycleModelBackward": "ctrl+,",
  "interrupt": ["escape", "ctrl+`"]
}

Note: Some ctrl+symbol combinations overlap with ASCII control characters due to terminal legacy behavior (e.g., ctrl+[ is the same as Escape, ctrl+M is the same as Enter). These can still be used with ctrl+shift+key (e.g., ctrl+shift+]). See Kitty keyboard protocol: legacy ctrl mapping of ASCII keys for all unsupported keys.

Bash Mode

Prefix commands with ! to execute them and add output to context:

!ls -la
!git status
!cat package.json | jq '.dependencies'

Output streams in real-time. Press Escape to cancel. Large outputs truncate at 2000 lines / 50KB.

The output becomes part of your next prompt, formatted as:

Ran `ls -la`
``` ```

Run multiple commands before prompting; all outputs are included together.

Image Support

Pasting images: Press Ctrl+V to paste an image from your clipboard.

Note: On macOS, pressing Cmd+C on an image file in Finder copies the file path, not the image contents. Use Preview or another image viewer to copy the actual image, or drag the file onto the terminal instead.

Dragging images: Drag image files onto the terminal to insert their path. On macOS, you can also drag the screenshot thumbnail (after Cmd+Shift+4) directly onto the terminal.

Attaching images: Include image paths in your message:

You: What's in this screenshot? /path/to/image.png

Supported formats: .jpg, .jpeg, .png, .gif, .webp

Auto-resize: Images larger than 2000x2000 pixels are automatically resized to fit within this limit for better compatibility with Anthropic models. The original dimensions are noted in the context so the model can map coordinates back if needed. Disable via images.autoResize: false in settings.

Inline rendering: On terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm) or iTerm2 inline images, images in tool output are rendered inline. On unsupported terminals, a text placeholder is shown instead.

Toggle inline images via /settings or set terminal.showImages: false in settings.


Sessions

Sessions are stored as JSONL files with a tree structure. Each entry has an id and parentId, enabling 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.

See docs/session.md for the file format and programmatic API.

Session Management

Sessions auto-save to ~/.pi/agent/sessions/ organized by working directory.

pi --continue      # Continue most recent session
pi -c              # Short form

pi --resume        # Browse and select from past sessions
pi -r              # Short form

pi --no-session    # Ephemeral mode (don't save)

pi --session /path/to/file.jsonl  # Use specific session file

Context Compaction

Long sessions can exhaust context windows. Compaction summarizes older messages while keeping recent ones.

Manual: /compact or /compact Focus on the API changes

Automatic: Enable via /settings. When enabled, triggers in two cases:

  • Overflow recovery: LLM returns context overflow error. Compacts and auto-retries.
  • Threshold maintenance: Context exceeds contextWindow - reserveTokens after a successful turn. Compacts without retry.

When disabled, neither case triggers automatic compaction (use /compact manually if needed).

Configuration (~/.pi/agent/settings.json):

{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}

Note: Compaction is lossy. The agent loses full conversation access afterward. Size tasks to avoid context limits when possible. For critical context, ask the agent to write a summary to a file, iterate on it until it covers everything, then start a new session with that file. The full session history is preserved in the JSONL file; use /tree to revisit any previous point.

See docs/compaction.md for how compaction works internally and how to customize it via hooks.

Branching

In-place navigation (/tree): Navigate the session tree without creating new files. Select any previous point, continue from there, and switch between branches while preserving all history.

  • Search by typing, page with ←/→
  • Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
  • Press l to label entries as bookmarks
  • When switching branches, you're prompted whether to generate a summary of the abandoned branch (messages up to the common ancestor)

Create new session (/branch): Branch to a new session file:

  1. Opens selector showing all your user messages
  2. Select a message to branch from
  3. Creates new session with history up to that point
  4. Selected message placed in editor for modification

Configuration

Project Context Files

Pi loads AGENTS.md (or CLAUDE.md) files at startup in this order:

  1. Global: ~/.pi/agent/AGENTS.md
  2. Parent directories: Walking up from current directory
  3. Current directory: ./AGENTS.md

Use these for:

  • Project instructions and guidelines
  • Common commands and workflows
  • Architecture documentation
  • Coding conventions
  • Testing instructions
# Common Commands
- npm run build: Build the project
- npm test: Run tests

# Code Style
- Use TypeScript strict mode
- Prefer async/await over promises

Custom System Prompt

Replace the default system prompt entirely by creating a SYSTEM.md file:

  1. Project-local: .pi/SYSTEM.md (takes precedence)
  2. Global: ~/.pi/agent/SYSTEM.md (fallback)

This is useful when using pi as different types of agents across repos (coding assistant, personal assistant, domain-specific agent, etc.).

You are a technical writing assistant. Help users write clear documentation.

Focus on:
- Concise explanations
- Code examples
- Proper formatting

The --system-prompt CLI flag overrides both files. Use --append-system-prompt to add to (rather than replace) the prompt.

Custom Models and Providers

Add custom models (Ollama, vLLM, LM Studio, etc.) via ~/.pi/agent/models.json:

{
  "providers": {
    "ollama": {
      "baseUrl": "http://localhost:11434/v1",
      "apiKey": "OLLAMA_API_KEY",
      "api": "openai-completions",
      "models": [
        {
          "id": "llama-3.1-8b",
          "name": "Llama 3.1 8B (Local)",
          "reasoning": false,
          "input": ["text"],
          "cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
          "contextWindow": 128000,
          "maxTokens": 32000
        }
      ]
    }
  }
}

Supported APIs: openai-completions, openai-responses, anthropic-messages, google-generative-ai

API key resolution: The apiKey field is checked as environment variable name first, then used as literal value.

API override: Set api at provider level (default for all models) or model level (override per model).

Custom headers:

{
  "providers": {
    "custom-proxy": {
      "baseUrl": "https://proxy.example.com/v1",
      "apiKey": "YOUR_API_KEY",
      "api": "anthropic-messages",
      "headers": {
        "User-Agent": "Mozilla/5.0 ...",
        "X-Custom-Auth": "token"
      },
      "models": [...]
    }
  }
}

Overriding built-in providers:

To route a built-in provider (anthropic, openai, google, etc.) through a proxy without redefining all models, just specify the baseUrl:

{
  "providers": {
    "anthropic": {
      "baseUrl": "https://my-proxy.example.com/v1"
    }
  }
}

All built-in Anthropic models remain available with the new endpoint. Existing OAuth or API key auth continues to work.

To fully replace a built-in provider with custom models, include the models array:

{
  "providers": {
    "anthropic": {
      "baseUrl": "https://my-proxy.example.com/v1",
      "apiKey": "ANTHROPIC_API_KEY",
      "api": "anthropic-messages",
      "models": [...]
    }
  }
}

Authorization header: Set authHeader: true to add Authorization: Bearer <apiKey> automatically.

OpenAI compatibility (compat field):

Field Description
supportsStore Whether provider supports store field
supportsDeveloperRole Use developer vs system role
supportsReasoningEffort Support for reasoning_effort parameter
maxTokensField Use max_completion_tokens or max_tokens

Live reload: The file reloads each time you open /model. Edit during session; no restart needed.

Model selection priority:

  1. CLI args (--provider, --model)
  2. First from --models scope (new sessions only)
  3. Restored from session (--continue, --resume)
  4. Saved default from settings
  5. First available model with valid API key

pi can help you create custom provider and model configurations.

Settings File

Settings are loaded from two locations and merged:

  1. Global: ~/.pi/agent/settings.json - user preferences
  2. Project: <cwd>/.pi/settings.json - project-specific overrides (version control friendly)

Project settings override global settings. For nested objects, individual keys merge. Settings changed via TUI (model, thinking level, etc.) are saved to global preferences only.

Global ~/.pi/agent/settings.json stores persistent preferences:

{
  "theme": "dark",
  "defaultProvider": "anthropic",
  "defaultModel": "claude-sonnet-4-20250514",
  "defaultThinkingLevel": "medium",
  "enabledModels": ["anthropic/*", "*gpt*", "gemini-2.5-pro:high"],
  "steeringMode": "one-at-a-time",
  "followUpMode": "one-at-a-time",
  "shellPath": "C:\\path\\to\\bash.exe",
  "hideThinkingBlock": false,
  "collapseChangelog": false,
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  },
  "skills": {
    "enabled": true
  },
  "retry": {
    "enabled": true,
    "maxRetries": 3,
    "baseDelayMs": 2000
  },
  "terminal": {
    "showImages": true
  },
  "images": {
    "autoResize": true
  },
  "hooks": ["/path/to/hook.ts"],
  "customTools": ["/path/to/tool.ts"]
}
Setting Description Default
theme Color theme name auto-detected
defaultProvider Default model provider -
defaultModel Default model ID -
defaultThinkingLevel Thinking level: off, minimal, low, medium, high, xhigh -
enabledModels Model patterns for cycling. Supports glob patterns (github-copilot/*, *sonnet*) and fuzzy matching. Same as --models CLI flag -
steeringMode Steering message delivery: all or one-at-a-time one-at-a-time
followUpMode Follow-up message delivery: all or one-at-a-time one-at-a-time
shellPath Custom bash path (Windows) auto-detected
hideThinkingBlock Hide thinking blocks in output (Ctrl+T to toggle) false
collapseChangelog Show condensed changelog after update false
compaction.enabled Enable auto-compaction true
compaction.reserveTokens Tokens to reserve before compaction triggers 16384
compaction.keepRecentTokens Recent tokens to keep after compaction 20000
skills.enabled Enable skills discovery true
retry.enabled Auto-retry on transient errors true
retry.maxRetries Maximum retry attempts 3
retry.baseDelayMs Base delay for exponential backoff 2000
terminal.showImages Render images inline (supported terminals) true
images.autoResize Auto-resize images to 2000x2000 max for better model compatibility true
doubleEscapeAction Action for double-escape with empty editor: tree or branch tree
hooks Additional hook file paths []
customTools Additional custom tool file paths []

Extensions

Themes

Built-in themes: dark (default), light. Auto-detected on first run.

Select theme via /settings or set in ~/.pi/agent/settings.json.

Custom themes: Create ~/.pi/agent/themes/*.json. Custom themes support live reload.

mkdir -p ~/.pi/agent/themes
cp $(npm root -g)/@mariozechner/pi-coding-agent/dist/theme/dark.json ~/.pi/agent/themes/my-theme.json

Select with /settings, then edit the file. Changes apply on save.

See Theme Documentation on how to create custom themes in detail. Pi can help you create a new one.

VS Code terminal fix: Set terminal.integrated.minimumContrastRatio to 1 for accurate colors.

Custom Slash Commands

Define reusable prompts as Markdown files:

Locations:

  • Global: ~/.pi/agent/commands/*.md
  • Project: .pi/commands/*.md

Format:

---
description: Review staged git changes
---
Review the staged changes (`git diff --cached`). Focus on:
- Bugs and logic errors
- Security issues
- Error handling gaps

Filename (without .md) becomes the command name. Description shown in autocomplete.

Arguments:

---
description: Create a component
---
Create a React component named $1 with features: $@

Usage: /component Button "onClick handler" "disabled support"

  • $1 = Button
  • $@ or $ARGUMENTS = all arguments joined (Button onClick handler disabled support)

Namespacing: Subdirectories create prefixes. .pi/commands/frontend/component.md/component (project:frontend)

Skills

Skills are self-contained capability packages that the agent loads on-demand. Pi implements the Agent Skills standard, warning about violations but remaining lenient.

A skill provides specialized workflows, setup instructions, helper scripts, and reference documentation for specific tasks. Skills are loaded when the agent decides a task matches the description, or when you explicitly ask to use one.

Example use cases:

  • Web search and content extraction (Brave Search API)
  • Browser automation via Chrome DevTools Protocol
  • Google Calendar, Gmail, Drive integration
  • PDF/DOCX processing and creation
  • Speech-to-text transcription
  • YouTube transcript extraction

Skill locations:

  • Pi user: ~/.pi/agent/skills/**/SKILL.md (recursive)
  • Pi project: .pi/skills/**/SKILL.md (recursive)
  • Claude Code: ~/.claude/skills/*/SKILL.md and .claude/skills/*/SKILL.md
  • Codex CLI: ~/.codex/skills/**/SKILL.md (recursive)

Format:

---
name: brave-search
description: Web search via Brave Search API. Use for documentation, facts, or web content.
---

# Brave Search

## Setup
\`\`\`bash
cd /path/to/brave-search && npm install
\`\`\`

## Usage
\`\`\`bash
./search.js "query"           # Basic search
./search.js "query" --content # Include page content
\`\`\`
  • name: Required. Must match parent directory name. Lowercase, hyphens, max 64 chars.
  • description: Required. Max 1024 chars. Determines when the skill is loaded.

Disable skills: pi --no-skills or set skills.enabled: false in settings.

See docs/skills.md for details, examples, and links to skill repositories. pi can help you create new skills.

Hooks

Hooks are TypeScript modules that extend pi's behavior by subscribing to lifecycle events. Use them to:

  • Block dangerous commands (permission gates for rm -rf, sudo, etc.)
  • Checkpoint code state (git stash at each turn, restore on /branch)
  • Protect paths (block writes to .env, node_modules/, etc.)
  • Modify tool output (filter or transform results before the LLM sees them)
  • Inject messages from external sources to wake up the agent (file watchers, webhooks, CI systems)

Hook locations:

  • Global: ~/.pi/agent/hooks/*.ts
  • Project: .pi/hooks/*.ts
  • CLI: --hook <path> (for debugging)

Quick example (permission gate):

import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";

export default function (pi: HookAPI) {
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName === "bash" && /sudo/.test(event.input.command as string)) {
      const ok = await ctx.ui.confirm("Allow sudo?", event.input.command as string);
      if (!ok) return { block: true, reason: "Blocked by user" };
    }
    return undefined;
  });
}

Sending messages from hooks:

Use pi.sendMessage(message, options?) to inject messages into the session. Messages are persisted as CustomMessageEntry and sent to the LLM.

Options:

  • triggerTurn: If true and agent is idle, starts a new agent turn. Default: false.
  • deliverAs: When agent is streaming, controls delivery timing:
    • "steer" (default): Delivered after current tool execution, interrupts remaining tools.
    • "followUp": Delivered only after agent finishes all work.
import * as fs from "node:fs";
import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";

export default function (pi: HookAPI) {
  pi.on("session_start", async () => {
    fs.watch("/tmp/trigger.txt", () => {
      const content = fs.readFileSync("/tmp/trigger.txt", "utf-8").trim();
      if (content) {
        pi.sendMessage({
          customType: "file-trigger",
          content,
          display: true,
        }, true);  // triggerTurn: start agent loop
      }
    });
  });
}

See Hooks Documentation for full API reference. pi can help you create new hooks

See examples/hooks/ for working examples including permission gates, git checkpointing, and path protection.

Custom Tools

Custom tools let you extend the built-in toolset (read, write, edit, bash, ...) and are called by the LLM directly. They are TypeScript modules that define tools with optional custom TUI integration for getting user input and custom tool call and result rendering.

Tool locations (auto-discovered):

  • Global: ~/.pi/agent/tools/*/index.ts
  • Project: .pi/tools/*/index.ts

Explicit paths:

  • CLI: --tool <path> (any .ts file)
  • Settings: customTools array in settings.json

Quick example:

import { Type } from "@sinclair/typebox";
import type { CustomToolFactory } from "@mariozechner/pi-coding-agent";

const factory: CustomToolFactory = (pi) => ({
  name: "greet",
  label: "Greeting",
  description: "Generate a greeting",
  parameters: Type.Object({
    name: Type.String({ description: "Name to greet" }),
  }),

  async execute(toolCallId, params, onUpdate, ctx, signal) {
    const { name } = params as { name: string };
    return {
      content: [{ type: "text", text: `Hello, ${name}!` }],
      details: { greeted: name },
    };
  },
});

export default factory;

Features:

  • Access to pi.cwd, pi.exec(), pi.ui (select/confirm/input dialogs)
  • Session lifecycle via onSession callback (for state reconstruction)
  • Custom rendering via renderCall() and renderResult() methods
  • Streaming results via onUpdate callback
  • Abort handling via signal parameter
  • Multiple tools from one factory (return an array)

See Custom Tools Documentation for the full API reference, TUI component guide, and examples. pi can help you create custom tools.

See examples/custom-tools/ for working examples including a todo list with session state management and a question tool with UI interaction.


CLI Reference

pi [options] [@files...] [messages...]

Options

Option Description
--provider <name> Provider: anthropic, openai, google, mistral, xai, groq, cerebras, openrouter, zai, github-copilot, google-gemini-cli, google-antigravity, or custom
--model <id> Model ID
--api-key <key> API key (overrides environment)
--system-prompt <text|file> Custom system prompt (text or file path)
--append-system-prompt <text|file> Append to system prompt
--mode <mode> Output mode: text, json, rpc (implies --print)
--print, -p Non-interactive: process prompt and exit
--no-session Don't save session
--session <path> Use specific session file
--session-dir <dir> Directory for session storage and lookup
--continue, -c Continue most recent session
--resume, -r Select session to resume
--models <patterns> Comma-separated patterns for Ctrl+P cycling. Supports glob patterns (e.g., anthropic/*, *sonnet*:high) and fuzzy matching (e.g., sonnet,haiku:low)
--tools <tools> Comma-separated tool list (default: read,bash,edit,write)
--thinking <level> Thinking level: off, minimal, low, medium, high
--hook <path> Load a hook file (can be used multiple times)
--no-skills Disable skills discovery and loading
--skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)
--export <file> [output] Export session to HTML
--help, -h Show help
--version, -v Show version

File Arguments

Include files with @ prefix:

pi @prompt.md "Answer this"
pi @screenshot.png "What's in this image?"
pi @requirements.md @design.png "Implement this"

Text files wrapped in <file name="path">content</file>. Images attached as base64.

Examples

# Interactive mode
pi

# Interactive with initial prompt
pi "List all .ts files in src/"

# Non-interactive
pi -p "List all .ts files in src/"

# With files
pi -p @code.ts "Review this code"

# JSON event stream
pi --mode json "List files"

# RPC mode (headless)
pi --mode rpc --no-session

# Continue session
pi -c "What did we discuss?"

# Specific model
pi --provider openai --model gpt-4o "Help me refactor"

# Model cycling with thinking levels
pi --models sonnet:high,haiku:low

# Limit to specific provider with glob pattern
pi --models "github-copilot/*"

# Read-only mode
pi --tools read,grep,find,ls -p "Review the architecture"

# Export session
pi --export session.jsonl output.html

Tools

Default Tools

Tool Description
read Read file contents. Images sent as attachments. Text: first 2000 lines, lines truncated at 2000 chars. Use offset/limit for large files.
write Write/overwrite file. Creates parent directories.
edit Replace exact text in file. Must match exactly including whitespace. Fails if text appears multiple times or not found.
bash Execute command. Returns stdout/stderr. Optional timeout parameter.

Read-Only Tools

Available via --tools flag:

Tool Description
grep Search file contents (regex or literal). Respects .gitignore.
find Search for files by glob pattern. Respects .gitignore.
ls List directory contents. Includes dotfiles.

Example: --tools read,grep,find,ls for code review without modification.

For adding new tools, see Custom Tools in the Configuration section.


Programmatic Usage

SDK

For embedding pi in Node.js/TypeScript applications, use the SDK:

import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@mariozechner/pi-coding-agent";

const authStorage = discoverAuthStorage();
const modelRegistry = discoverModels(authStorage);

const { session } = await createAgentSession({
  sessionManager: SessionManager.inMemory(),
  authStorage,
  modelRegistry,
});

session.subscribe((event) => {
  if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
    process.stdout.write(event.assistantMessageEvent.delta);
  }
});

await session.prompt("What files are in the current directory?");

The SDK provides full control over:

  • Model selection and thinking level
  • System prompt (replace or modify)
  • Tools (built-in subsets, custom tools)
  • Hooks (inline or discovered)
  • Skills, context files, slash commands
  • Session persistence (SessionManager)
  • Settings (SettingsManager)
  • API key resolution and OAuth

Philosophy: "Omit to discover, provide to override." Omit an option and pi discovers from standard locations. Provide an option and your value is used.

See SDK Documentation for the full API reference. See examples/sdk/ for working examples from minimal to full control.

RPC Mode

For embedding pi from other languages or with process isolation:

pi --mode rpc --no-session

Send JSON commands on stdin:

{"type":"prompt","message":"List all .ts files"}
{"type":"abort"}

See RPC Documentation for the full protocol.

HTML Export

pi --export session.jsonl              # Auto-generated filename
pi --export session.jsonl output.html  # Custom filename

Works with both session files and streaming event logs from --mode json.


Philosophy

Pi is opinionated about what it won't do. These are intentional design decisions to minimize context bloat and avoid anti-patterns.

No MCP. Build CLI tools with READMEs (see Skills). The agent reads them on demand. Would you like to know more?

No sub-agents. Spawn pi instances via tmux, or build your own sub-agent tool with custom tools. Full observability and steerability.

No permission popups. Security theater. Run in a container or build your own with Hooks.

No plan mode. Gather context in one session, write plans to file, start fresh for implementation.

No built-in to-dos. They confuse models. Use a TODO.md file, or build your own with custom tools.

No background bash. Use tmux. Full observability, direct interaction.

Read the blog post for the full rationale.


Development

Forking / Rebranding

Configure via package.json:

{
  "piConfig": {
    "name": "pi",
    "configDir": ".pi"
  }
}

Change name, configDir, and bin field for your fork. Affects CLI banner, config paths, and environment variable names.

Path Resolution

Three execution modes: npm install, standalone binary, tsx from source.

Always use src/paths.ts for package assets:

import { getPackageDir, getThemeDir } from "./paths.js";

Never use __dirname directly for package assets.

Debug Command

/debug (hidden) writes rendered lines with ANSI codes to ~/.pi/agent/pi-debug.log for TUI debugging, as well as the last set of messages that were sent to the LLM.


License

MIT

See Also