{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreihr7jxgj6yavnbqtcosimwkm62q7ui7u7chflfavydvapacnrktui",
"uri": "at://did:plc:ws6dhxzqnqxu5aqxt4kd27oc/app.bsky.feed.post/3mk5sr7dtnw72"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreif5dgsv7hnlyen3zq2mparienl7gjtrgzx5wfx2a7gujz5jaoi27a"
},
"mimeType": "image/png",
"size": 1415855
},
"description": "A small-core CLI agent built around four tools, TypeScript extensions, and tree-structured sessions.",
"path": "/pi-coding-agent-the-minimal-terminal-harness-you-extend-yourself/",
"publishedAt": "2026-04-23T10:33:31.000Z",
"site": "https://allthings.how",
"tags": [
"Agent Skills standard",
"badlogic/pi-monoThe pi-mono monorepo contains the coding agent, the unified LLM API, the agent runtime, the TUI library, and companion toolsGitHub",
"@mariozechner/pi-coding-agentThe coding agent package on npm, with install instructions and links to documentationnpm",
"@mariozechner",
"@foo",
"@filename",
"@screenshot.png"
],
"textContent": "Pi is a terminal-based coding agent built by Mario Zechner that takes a deliberately stripped-down approach to agentic programming. Instead of bundling every feature other agents ship with, it gives the model four tools, keeps the system prompt short, and pushes everything else into a TypeScript extension layer that you (or the agent itself) can write.\n\n⚡\n\nQuick answer: Pi is an open-source terminal coding agent installed via npm install -g @mariozechner/pi-coding-agent. It ships with read, write, edit, and bash tools, supports 15+ model providers, and is extended through TypeScript extensions, skills, prompt templates, and themes distributed as pi packages.\n\n* * *\n\n### What pi actually is\n\nPi runs in a terminal as a text UI. You talk to a language model, and the model uses a tiny built-in toolset to read files, write files, edit files, and run shell commands. That's the whole default surface. No MCP. No plan mode. No sub-agents. No permission popups. No background bash. No built-in to-do system.\n\nThe design premise is that most \"features\" in other agents can be assembled from primitives when the core is small and the extension API is rich. If you want plan mode, write it. If you want sub-agents, spawn them. If you want MCP, bridge to it with a CLI or an extension. The agent is framed as clay you shape, not a fixed product.\n\nPi lives inside the broader pi-mono monorepo, which also contains the unified LLM API (`pi-ai`), an agent runtime, a terminal UI library, and a Slack bridge called pi-mom. The coding agent is one package among several, but it's the entry point most people care about.\n\n* * *\n\n### Installation and first run\n\n**Step 1:** Install the CLI globally from npm. This gives you the `pi` command in your shell.\n\n\n npm install -g @mariozechner/pi-coding-agent\n\n\n**Step 2:** Authenticate. You can use an API key via environment variable, or log in with an existing subscription through OAuth.\n\n\n export ANTHROPIC_API_KEY=sk-ant-...\n pi\n\n\n**Step 3:** Alternatively, start pi and run `/login` inside the TUI to connect an Anthropic Claude Pro/Max, ChatGPT Plus/Pro (Codex), GitHub Copilot, Google Gemini CLI, or Google Antigravity subscription.\n\nOnce you're in, you just type. The model has four tools by default: `read`, `write`, `edit`, and `bash`. Optional extras like `grep`, `find`, and `ls` can be turned on with the `--tools` flag.\n\n* * *\n\n### Providers and model switching\n\nPi supports a long list of providers and keeps their tool-capable model lists updated with each release. You can switch models mid-session with `/model` or `Ctrl+L`, and cycle through a shortlist with `Ctrl+P`.\n\nAuth type| Supported providers\n---|---\nSubscription (OAuth)| Anthropic Claude Pro/Max, ChatGPT Plus/Pro (Codex), GitHub Copilot, Google Gemini CLI, Google Antigravity\nAPI key| Anthropic, OpenAI, Azure OpenAI, Google Gemini, Google Vertex, Amazon Bedrock, Mistral, Groq, Cerebras, xAI, OpenRouter, Vercel AI Gateway, ZAI, OpenCode Zen, OpenCode Go, Hugging Face, Fireworks, Kimi For Coding, MiniMax\nLocal / custom| Ollama and any OpenAI/Anthropic/Google-compatible endpoint added via `~/.pi/agent/models.json` or an extension\n\nCustom providers that speak a supported API can be added through a JSON file. Anything needing a non-standard API or OAuth flow goes through an extension, which can register a provider and fetch remote model lists at startup.\n\n* * *\n\n### The four modes of operation\n\nPi isn't only a TUI. It runs in four distinct modes depending on how you want to integrate it.\n\nMode| Invocation| Use case\n---|---|---\nInteractive| `pi`| Full terminal UI with editor, messages, footer, and extensions\nPrint| `pi -p \"query\"`| Run once, print the answer, exit. Reads piped stdin.\nJSON| `pi --mode json`| Event stream as JSON lines for scripting and pipelines\nRPC| `pi --mode rpc`| LF-delimited JSONL protocol over stdin/stdout for non-Node integrations\nSDK| `import { createAgentSession } from \"@mariozechner/pi-coding-agent\"`| Embed pi as a library in your own Node app\n\nThe RPC mode is worth calling out. It uses strict LF-delimited JSONL framing, which means clients must split on `\\n` only, never on generic Unicode line separators. Node's built-in `readline` will break the protocol because it splits on additional separators that can appear inside JSON payloads.\n\nThe SDK is the integration path used by OpenClaw, a chat-connected agent that sits on top of pi and runs code on demand. The same primitives let you build Slack bots, Telegram bots, or anything else that wants an agent loop backing a conversation surface.\n\n* * *\n\n### Sessions are trees, not logs\n\nSessions are stored as JSONL files in `~/.pi/agent/sessions/`, organized by working directory. Each entry has an `id` and a `parentId`, which means a session is a tree rather than a linear chat log. You can branch, switch between branches, and rewind without creating separate files.\n\nThe commands that matter here:\n\nCommand| What it does\n---|---\n`/tree`| Navigate the session tree, pick any previous point, and continue from there\n`/fork`| Create a new session file from a previous user message on the active branch\n`/clone`| Duplicate the current active branch into a new session file\n`/compact [prompt]`| Summarize older messages, optionally with custom instructions\n`/share`| Upload the session as a private GitHub gist with a shareable HTML link\n`/export [file]`| Export the session as a standalone HTML file\n\nCompaction is lossy, but the full history stays in the JSONL file, so `/tree` can always revisit the pre-compaction state. Automatic compaction triggers when the context approaches the limit, or recovers after an overflow and retries.\n\nThe practical consequence is that side-quests don't pollute the main thread. If an agent breaks one of its own tools mid-task, you can branch, fix the tool, verify it, then rewind the main branch with a short summary of what happened in the detour.\n\n* * *\n\n### Message queueing while the agent works\n\nYou can type while the model is still producing output. Pi distinguishes two kinds of queued input:\n\n * **Enter** queues a steering message. It's delivered after the current tool call finishes, and it interrupts any remaining queued tool calls in that turn.\n * **Alt+Enter** queues a follow-up. It's only delivered once the agent finishes all pending work.\n * **Escape** aborts and pulls queued messages back into the editor so you can edit them.\n * **Alt+Up** retrieves queued messages without aborting.\n\n\n\nDelivery modes `steeringMode` and `followUpMode` can be set to `one-at-a-time` (the default, where each queued message waits for a response) or `all` (deliver everything queued at once). On Windows Terminal, `Alt+Enter` is mapped to fullscreen by default and needs to be remapped for follow-ups to work.\n\n* * *\n\n### Context files and system prompts\n\nAt startup, pi loads `AGENTS.md` (or `CLAUDE.md`) from three locations, walking up the directory tree:\n\n * `~/.pi/agent/AGENTS.md` for global instructions\n * Every parent directory between `~` and the current working directory\n * The current directory itself\n\n\n\nMatching files are concatenated and appended to the system prompt. Use this for coding conventions, repo-specific commands, and agent instructions. You can disable discovery with `--no-context-files` (or `-nc`).\n\nTo replace the system prompt entirely, create `.pi/SYSTEM.md` per project or `~/.pi/agent/SYSTEM.md` globally. To append without replacing, use `APPEND_SYSTEM.md`. The default system prompt is intentionally short so that project context dominates behavior rather than generic boilerplate.\n\n* * *\n\n### The four extensibility surfaces\n\nPi ships with a specific vocabulary for customization. Each type has its own discovery path and its own purpose.\n\nType| Format| What it does\n---|---|---\nPrompt templates| Markdown files with `{{variables}}`| Reusable prompts invoked by typing `/name` in the editor\nSkills| `SKILL.md` in a folder, Agent Skills standard| On-demand capability packages; loaded automatically or via `/skill:name`. Progressive disclosure avoids busting the prompt cache.\nExtensions| TypeScript modules exporting a factory function| Register tools, commands, keybindings, event handlers, and custom TUI components\nThemes| Theme files placed in theme directories| Color and visual customization; hot-reloads on save\nPi packages| npm or git repo with a `pi` key in `package.json`| Bundle any combination of the above and distribute it\n\nDiscovery paths are consistent. Each resource type is loaded from `~/.pi/agent/<type>/` (global), `.pi/<type>/` (project), or from installed pi packages. Skills additionally support `~/.agents/skills/` and `.agents/skills/`, walking up from the current directory.\n\n* * *\n\n### Writing an extension\n\nAn extension is a TypeScript module with a default export that takes the extension API. The factory can be async, which matters when you need to fetch remote model lists or do one-time setup before pi finishes starting up.\n\n\n export default function (pi: ExtensionAPI) {\n pi.registerTool({ name: \"deploy\", /* ... */ });\n pi.registerCommand(\"stats\", { /* ... */ });\n pi.on(\"tool_call\", async (event, ctx) => { /* ... */ });\n }\n\n\nFrom this API, an extension can register custom tools (or replace the built-in ones entirely), add slash commands and keyboard shortcuts, inject messages before each model turn, filter message history, implement RAG, build long-term memory, render TUI widgets above or below the editor, add status lines and custom footers, or take over the editor with a custom UI.\n\nThe example directory in pi-mono contains over 50 extensions to read or fork, including sub-agents, plan mode, permission gates, path protection, SSH execution, sandboxing, MCP integration, git checkpointing, and a Doom overlay that proves the TUI can host arbitrary rendering.\n\n* * *\n\n### Skills vs extensions vs prompt templates\n\nThe three are easy to confuse. Here's the decision model that maps cleanly to pi's design.\n\nSituation| Use\n---|---\nA reusable prompt you type often with slight parameter changes| Prompt template\nA capability the model should know about but only load when needed| Skill\nA new tool, UI element, keybinding, or event hook| Extension\nA shell-level capability that already works from the command line| Just let the model call it through the `bash` tool; optionally document it in a skill\n\nSkills follow the Agent Skills standard, so a single skill folder is portable across agents that support the spec. Each `SKILL.md` declares when the skill is relevant and the steps to follow. The point of progressive disclosure is that skill content only enters the context when invoked, which keeps the prompt cache intact for everything else.\n\n* * *\n\n### Installing and managing pi packages\n\nPackages are distributed through npm or git. Install, remove, update, and configure them with the `pi` CLI itself.\n\n\n pi install npm:@foo/pi-tools\n pi install npm:@foo/pi-tools@1.2.3\n pi install git:github.com/user/repo\n pi install git:github.com/user/repo@v1\n pi install https://github.com/user/repo@v1\n pi install ssh://git@github.com/user/repo@v1\n pi remove npm:@foo/pi-tools\n pi list\n pi update\n pi config\n\n\nThe `-l` flag installs into a project-local directory (`.pi/git/` or `.pi/npm/`) instead of the global location. Git packages install with `npm install --omit=dev`, so runtime code must declare its dependencies under `dependencies`, not `devDependencies`.\n\nNode version managers can trip up package installs. If you use mise, nvm, or similar, set `npmCommand` in `settings.json` to something stable, for example `[\"mise\", \"exec\", \"node@20\", \"--\", \"npm\"]`.\n\nTo test a package without installing, use the `-e` flag: `pi -e git:github.com/user/repo`. That runs pi with the package loaded for the session only.\n\n⚠️\n\nPi packages run with full system access. Extensions execute arbitrary code, and skills can instruct the model to run any executable. Review the source of third-party packages before installing them.\n\nTo ship your own package, add a `pi` key and the `pi-package` keyword to `package.json`:\n\n\n {\n \"name\": \"my-pi-package\",\n \"keywords\": [\"pi-package\"],\n \"pi\": {\n \"extensions\": [\"./extensions\"],\n \"skills\": [\"./skills\"],\n \"prompts\": [\"./prompts\"],\n \"themes\": [\"./themes\"]\n }\n }\n\n\nWithout an explicit manifest, pi auto-discovers resources from the conventional folders (`extensions/`, `skills/`, `prompts/`, `themes/`).\n\n* * *\n\n### CLI flags worth knowing\n\nThe full flag list is long. These are the ones that come up most often.\n\nFlag| Effect\n---|---\n`-c`, `--continue`| Continue the most recent session for the current directory\n`-r`, `--resume`| Open a picker to resume any past session\n`--no-session`| Ephemeral mode; nothing is written to disk\n`--session <path|id>`| Open a specific session by file path or partial UUID\n`--fork <path|id>`| Fork an existing session into a new one\n`--tools read,grep,find,ls`| Run in read-only mode; no write, edit, or bash\n`--no-tools`| Disable all built-in tools; only extension tools remain\n`--model <pattern>`| Supports `provider/id` and `:<thinking>` shorthand, e.g. `sonnet:high`\n`--thinking <level>`| off, minimal, low, medium, high, xhigh\n`--models \"claude-*,gpt-4o\"`| Patterns for the Ctrl+P cycle list\n`@filename`| Include a file (text or image) in the initial message\n\nPiped stdin is merged into the initial prompt in print mode, which makes one-liners easy:\n\n\n cat README.md | pi -p \"Summarize this text\"\n pi -p @screenshot.png \"What's in this image?\"\n pi --tools read,grep,find,ls -p \"Review the code\"\n\n\n* * *\n\n### Environment variables\n\nVariable| Purpose\n---|---\n`PI_CODING_AGENT_DIR`| Override the config directory (default: `~/.pi/agent`)\n`PI_PACKAGE_DIR`| Override the package directory; useful on Nix/Guix where store paths tokenize poorly\n`PI_SKIP_VERSION_CHECK`| Skip the version check at startup\n`PI_TELEMETRY`| Set to `0`/`false`/`no` to disable anonymous install/update telemetry\n`PI_CACHE_RETENTION`| Set to `long` for extended prompt cache (Anthropic 1h, OpenAI 24h)\n`VISUAL`, `EDITOR`| External editor invoked by `Ctrl+G`\n\nTelemetry can also be disabled per-install by setting `enableInstallTelemetry` to `false` in `settings.json`.\n\n* * *\n\n### What pi deliberately doesn't ship\n\nThe omissions are the design. Each one has a stated rationale and a workaround.\n\nMissing feature| Rationale| Workaround\n---|---|---\nMCP support| MCP tools have to be loaded at session start and can't be hot-swapped without invalidating the cache or confusing the model about older tool calls| Use CLI wrappers like mcporter, or write an extension that bridges MCP\nSub-agents| Too many reasonable implementations to pick one| Spawn pi instances via tmux, or install a sub-agent extension\nPermission popups| Confirmation flows should match your environment and security model| Run in a container, or build a permission gate extension\nPlan mode| Plans vary by team and workflow| Write plans to a file, or install a plan-mode package\nBuilt-in to-dos| They confuse models| Use a `TODO.md` file, or an extension that manages `.pi/todos`\nBackground bash| Hard to observe and interact with reliably| Use tmux for full observability\n\nThe recurring pattern: the core stays minimal so that the customization layer can absorb the variance. Features other agents bake in can be built, borrowed, or replaced without forking pi itself.\n\n* * *\n\n### Community resources\n\nCommunity packages, extensions, and discussion are concentrated in a few places. Pi packages publish under the `pi-package` keyword on npm, making them discoverable with a single search.\n\nbadlogic/pi-monoThe pi-mono monorepo contains the coding agent, the unified LLM API, the agent runtime, the TUI library, and companion toolsGitHub@mariozechner/pi-coding-agentThe coding agent package on npm, with install instructions and links to documentationnpm\n\nIssues and pull requests from new contributors are auto-closed by default; maintainers review the auto-closed queue daily. This keeps the project shippable by a small team while still letting community contributions land after review.\n\nThe underlying idea that makes pi worth paying attention to isn't the tool count or the provider list. It's that extension state persists into sessions, extensions hot-reload, and sessions are trees. Those three properties together let the agent build, test, and iterate on its own tools inside a single working session, which is the closest most developers have come to software that actually builds more software.",
"title": "Pi coding agent: The minimal terminal harness you extend yourself",
"updatedAt": "2026-04-23T10:33:33.424Z"
}