- Fix external dependency name (pi-agent-core -> pi-agent) - Add missing files: hooks/, custom-tools/, skills.ts, github-copilot.ts - Update components list with all 18 current components - Add Key Abstractions sections for hooks, custom tools, skills - Add 'Adding a New Hook Event' to development guide - Move config.ts to correct location in directory tree |
||
|---|---|---|
| .. | ||
| docs | ||
| examples | ||
| src | ||
| test | ||
| CHANGELOG.md | ||
| DEVELOPMENT.md | ||
| package.json | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.examples.json | ||
| vitest.config.ts | ||
pi
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
- Usage
- Sessions
- Configuration
- CLI Reference
- Tools
- Programmatic Usage
- Philosophy
- Development
- License
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):
- Custom path from
~/.pi/agent/settings.json - Git Bash (
C:\Program Files\Git\bin\bash.exe) bash.exeon 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
Set the environment variable for your provider:
| Provider | Environment Variable |
|---|---|
| Anthropic | ANTHROPIC_API_KEY |
| OpenAI | OPENAI_API_KEY |
GEMINI_API_KEY |
|
| Mistral | MISTRAL_API_KEY |
| Groq | GROQ_API_KEY |
| Cerebras | CEREBRAS_API_KEY |
| xAI | XAI_API_KEY |
| OpenRouter | OPENROUTER_API_KEY |
| ZAI | ZAI_API_KEY |
Anthropic OAuth (Claude Pro/Max):
pi
/login # Select "Anthropic (Claude Pro/Max)", authorize in browser
Tokens stored in ~/.pi/agent/oauth.json. Use /logout to clear.
GitHub Copilot OAuth:
pi
/login # Select "GitHub Copilot", authorize in browser
Press Enter to use github.com, or enter your GitHub Enterprise Server domain (e.g., github.mycompany.com).
If you get "The requested model is not supported" error, enable the model in VS Code: Copilot Chat → model selector → select model → "Enable".
Tokens stored in ~/.pi/agent/oauth.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 |
|---|---|
/model |
Switch models mid-session (fuzzy search, arrow keys, Enter to select) |
/thinking |
Adjust thinking level for reasoning models (off/minimal/low/medium/high) |
/queue |
Set message queue mode: one-at-a-time (default) or all-at-once |
/export [file] |
Export session to self-contained HTML |
/session |
Show session info: path, message counts, token usage, cost |
/changelog |
Display full version history |
/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 |
/clear |
Clear context and start fresh session |
/copy |
Copy last agent message to clipboard |
/compact [instructions] |
Manually compact conversation context |
/autocompact |
Toggle automatic context compaction |
/theme |
Select color theme |
/show-images |
Toggle inline image display (supported terminals only) |
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. They queue and process based on /queue mode. 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 | Start of line |
| Ctrl+E / End | 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) |
| Shift+Tab | Cycle thinking level |
| Ctrl+P | Cycle models (scoped by --models) |
| Ctrl+O | Toggle tool output expansion |
| Ctrl+T | Toggle thinking block visibility |
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
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
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 with /show-images or set terminal.showImages: false in settings.
Sessions
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 with /autocompact. When enabled, triggers in two cases:
- Overflow recovery: LLM returns context overflow error. Compacts and auto-retries.
- Threshold maintenance: Context exceeds
contextWindow - reserveTokensafter a successful turn. Compacts without retry.
When disabled, neither case triggers automatic compaction (use /compact manually if needed).
How it works:
- Cut point calculated to keep ~20k tokens of recent messages
- Messages before cut point are summarized
- Summary replaces old messages as "context handoff"
- Previous compaction summaries chain into new ones
Compaction does not create a new session, but continues the existing one, with a marker in the .jsonl file that encodes the compaction point.
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
/branchto revisit any previous point.
Branching
Use /branch to explore alternative conversation paths:
- Opens selector showing all your user messages
- Select a message to branch from
- Creates new session with history up to that point
- Selected message placed in editor for modification
Configuration
Project Context Files
Pi loads AGENTS.md (or CLAUDE.md) files at startup in this order:
- Global:
~/.pi/agent/AGENTS.md - Parent directories: Walking up from current directory
- 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 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": [...]
}
}
}
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:
- CLI args (
--provider,--model) - First from
--modelsscope (new sessions only) - Restored from session (
--continue,--resume) - Saved default from settings
- First available model with valid API key
pi can help you create custom provider and model configurations.
Themes
Built-in themes: dark (default), light. Auto-detected on first run.
/theme # Interactive selector
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 /theme, 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$@= all arguments joined
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. 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.mdand.claude/skills/*/SKILL.md - Codex CLI:
~/.codex/skills/**/SKILL.md(recursive)
Format:
---
description: Web search via Brave Search API. Use for documentation, facts, or web content.
---
# Brave Search
## Setup
\`\`\`bash
cd {baseDir} && npm install
\`\`\`
## Usage
\`\`\`bash
{baseDir}/search.js "query" # Basic search
{baseDir}/search.js "query" --content # Include page content
\`\`\`
description: Required. Determines when the skill is loaded.{baseDir}: Placeholder for the skill's directory.
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.send(text, attachments?) to inject messages into the session. If the agent is streaming, the message is queued; otherwise a new agent loop starts immediately.
import * as fs from "node:fs";
import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
export default function (pi: HookAPI) {
pi.on("session", async (event) => {
if (event.reason !== "start") return;
fs.watch("/tmp/trigger.txt", () => {
const content = fs.readFileSync("/tmp/trigger.txt", "utf-8").trim();
if (content) pi.send(content);
});
});
}
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:
- Global:
~/.pi/agent/tools/*.ts - Project:
.pi/tools/*.ts - CLI:
--tool <path> - Settings:
customToolsarray insettings.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) {
return {
content: [{ type: "text", text: `Hello, ${params.name}!` }],
details: { greeted: params.name },
};
},
});
export default factory;
Features:
- Access to
pi.cwd,pi.exec(),pi.ui(select/confirm/input dialogs) - Session lifecycle via
onSessioncallback (for state reconstruction) - Custom rendering via
renderCall()andrenderResult()methods - Streaming results via
onUpdatecallback - Abort handling via
signalparameter - 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.
Settings File
~/.pi/agent/settings.json stores persistent preferences:
{
"theme": "dark",
"defaultProvider": "anthropic",
"defaultModel": "claude-sonnet-4-20250514",
"defaultThinkingLevel": "medium",
"queueMode": "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
},
"hooks": ["/path/to/hook.ts"],
"hookTimeout": 30000,
"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 |
- |
queueMode |
Message queue mode: 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 |
hooks |
Additional hook file paths | [] |
hookTimeout |
Timeout for hook operations (ms) | 30000 |
customTools |
Additional custom tool file paths | [] |
CLI Reference
pi [options] [@files...] [messages...]
Options
| Option | Description |
|---|---|
--provider <name> |
Provider: anthropic, openai, google, mistral, xai, groq, cerebras, openrouter, zai, 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 |
--continue, -c |
Continue most recent session |
--resume, -r |
Select session to resume |
--models <patterns> |
Comma-separated patterns for Ctrl+P cycling (e.g., sonnet:high,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 |
--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
# 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
RPC Mode
For embedding pi in other applications:
pi --mode rpc --no-session
Send JSON commands on stdin:
{"type":"prompt","message":"List all .ts files"}
{"type":"abort"}
See RPC documentation for full protocol.
Node.js/TypeScript: Consider using AgentSession directly from @mariozechner/pi-coding-agent instead of subprocess. See src/core/agent-session.ts and src/modes/rpc/rpc-client.ts.
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 a task 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.
For architecture and contribution guidelines, see DEVELOPMENT.md.
License
MIT
See Also
- @mariozechner/pi-ai: Core LLM toolkit
- @mariozechner/pi-agent: Agent framework