Skip to content
On this page

    Pi Agent: The Minimal Harness That Became My Multi-Tool Glue

    After 2 months switching between Claude Code, Codex, Gemini CLI, and Pi Agent for different coding tasks, Pi's 'primitives not features' philosophy turned it into the connective tissue between heavier tools.

    12 min read

    Pi Agent - There are many agent harnesses but this one is yours

    I run four coding agents daily. Claude Code for planning and implementation. Codex for review loops. Gemini CLI (now Antigravity) for frontend-heavy visual work. And for the past two months, Pi Agent has quietly become the one I reach for when none of the others fit.

    Not because Pi does more. Because it does less.

    The problem is the spaces between these tools. The moments when you need a coding agent but don’t want 10,000 tokens of system prompt or 47 permission dialogs. Pi fills that gap.

    What Pi Actually Solves

    The problem isn’t “we need another coding agent.” We have plenty. The problem is that every existing harness assumes it’s the only tool you use.

    %%{init: {"layout": "dagre"}}%%
    flowchart TD
        subgraph problem["The Sealed-Harness Problem"]
            A[Claude Code] -->|locked to Anthropic| X[Vendor lock-in]
            B[Codex] -->|locked to OpenAI| X
            C[Gemini CLI] -->|locked to Google| X
            X --> Y[Can't mix models mid-task]
            X --> Z[Context overhead per session]
        end
        subgraph solution["Pi's Answer"]
            D[Minimal core: 4 tools, <1000 tokens] --> E[Any provider, any model]
            E --> F[Switch mid-session]
            D --> G[Zero permission friction]
            D --> H[Self-extending via TypeScript]
        end
        problem -.->|"Pi bridges the gaps"| solution

    Claude Code injects thousands of tokens of system prompt on every turn. Codex adds its own orchestration overhead. Gemini CLI brings Google’s safety layers. Each tool makes assumptions about what you need, and those assumptions cost context tokens.

    Pi’s thesis: the models already know how to code. They’ve been RL-trained for it. Give them read, write, edit, bash, and get out of the way.

    The Multi-Tool Reality Nobody Talks About

    Most agent content assumes you pick one tool and commit. That’s not how production workflows actually work.

    ToolWhere I Use ItWhy
    Claude CodePlanning (Opus), implementation (Sonnet + Haiku sub-agents)Deep context, extended thinking, workflow orchestration
    CodexCode review, system improvement loopsAnti-gravity mode, iterative refinement
    Gemini CLIFrontend generation, slide decks, video templatesLong context (1M+), multimodal input, visual reasoning
    Pi AgentGlue tasks, quick explorations, extension authoringSub-1000 token prompt, zero friction, model flexibility

    The switch points matter:

    %%{init: {"layout": "dagre"}}%%
    flowchart LR
        Task[New Task] --> Decision{What kind?}
        Decision -->|"Architecture, deep reasoning"| CC[Claude Code + Opus]
        Decision -->|"Review loop, iterative improvement"| CDX[Codex]
        Decision -->|"Frontend visual, HTML gen, slides"| GEM[Gemini CLI]
        Decision -->|"Quick, multi-model, custom tooling"| PI[Pi Agent]
        CC --> Done[Ship]
        CDX --> Done
        GEM --> Done
        PI --> Done

    The diagram tells the full story. The part it doesn’t show: heavyweight agents add context overhead that actively hurts simple tasks. Pi’s entire system prompt and tool definitions fit in under 1,000 tokens. More context window goes to actual work.

    Why “Primitives Not Features” Actually Works

    Pi’s creator, Mario Zechner, made a series of contrarian choices that sound wrong until you use them daily:

    • No MCP support
    • No sub-agents
    • No plan mode
    • No background bash
    • No built-in to-dos
    • No permission prompts

    Each of these is a deliberate subtraction. And each one forces you into a pattern that’s actually better.

    No MCP: Progressive Disclosure via CLI Tools

    MCP servers like Playwright (21 tools, 13.7k tokens) or Chrome DevTools (26 tools, 18k tokens) dump their full tool descriptions into every session. That’s 7-9% of your context window gone before you type a single character.

    Pi’s alternative: build CLI tools with README files. The agent reads the README only when it needs the tool:

    %%{init: {"layout": "dagre"}}%%
    sequenceDiagram
        participant User
        participant Pi
        participant Tool as CLI Tool
    
        User->>Pi: "search the web for X"
        Pi->>Pi: reads ~/.pi/tools/websearch/README.md
        Pi->>Tool: websearch "query" (via bash)
        Tool-->>Pi: results
        Pi-->>User: summarized answer
        Note over Pi: Token cost paid ONLY this turn

    This is progressive disclosure applied to agent tooling. You pay for what you use, when you use it. Compare that to MCP dumping 13k tokens into every single session whether you search the web or not.

    No Sub-Agents: Full Observability

    In Claude Code, sub-agents are black boxes. You see the output but not the reasoning, not the context gathering, not the missed files. Pi’s approach: spawn yourself via bash.

    ---
    description: Run a code review sub-agent
    ---
    Spawn yourself as a sub-agent via bash to do a code review: $@
    
    Use `pi --print` with appropriate arguments.
    Pass a prompt asking it to review for bugs, security, error handling.
    Report the sub-agent's findings.

    The output streams to your terminal. You see everything. If the sub-agent misses something, you know immediately because you’re watching the full output, not a summary.

    No Background Bash: tmux Instead

    Background process management in coding agents is a solved problem. It’s called tmux.

    # Pi can start a dev server in tmux
    tmux new-session -d -s devserver 'npm run dev'
    
    # Check output later
    tmux capture-pane -t devserver -p
    
    # List all sessions
    tmux list-sessions

    You get observability for free. You can hop into the session yourself. And after context compaction, the agent can still query tmux list-sessions to find its running processes. No special tooling needed.

    Beyond Coding: Pi as a Generalized Agent

    Here’s something most people miss about Pi. It’s marketed as a “coding agent,” but the architecture is completely general-purpose. The core is just: take a system prompt, give the model tools, run a loop until it’s done.

    Swap the tools and system prompt, and you have a different agent entirely:

    %%{init: {"layout": "dagre"}}%%
    flowchart TD
        subgraph core["Pi Core (unchanged)"]
            A[Agent Loop]
            B[Tool Dispatch]
            C[Event Stream]
            D[Session Management]
        end
        subgraph configs["Different SYSTEM.md + Extensions"]
            E["Coding Agent<br/>tools: read, write, edit, bash"]
            F["SRE Agent<br/>tools: kubectl, terraform, aws-cli"]
            G["Research Agent<br/>tools: websearch, fetch, summarize"]
            H["Data Pipeline Agent<br/>tools: dbt, sql, spark-submit"]
        end
        E --> core
        F --> core
        G --> core
        H --> core

    I’ve used Pi for:

    • Infrastructure work: SYSTEM.md that says “you are an SRE, use terraform and kubectl, never run apply without showing the plan first”
    • PRD review: SYSTEM.md in “product-review mode” with read-only tools, prompting the agent to critique a requirements doc
    • Data exploration: extensions that add sql and chart commands for ad-hoc analysis

    The extension system makes this trivial. Write a TypeScript module, add your custom tools, reload. You’re not fighting the harness to do something it wasn’t designed for. You’re configuring it to be exactly what you need.

    This isn’t hypothetical at scale, either. Pi is the harness powering OpenClaw, Peter Steinberger’s personal-agent project that hit 100,000+ GitHub stars and 2 million users within a week before Steinberger joined OpenAI to lead its foundation in February 2026. Same architecture. Same four primitives. Different SYSTEM.md. The generalized-agent thesis proved out at consumer scale before most people even noticed Pi existed.

    Why this matters: Claude Code, Codex, and Gemini CLI are all opinionated about being coding tools. Their system prompts, tools, and workflows assume code editing. Pi assumes nothing. The SYSTEM.md override lets you replace the entire persona.

    Code Change Highlighting: The One Gap

    Every coding agent struggles with the same UX problem: showing you what changed. Pi uses a standard edit tool with exact text matching (old text → new text), similar to Claude Code and Codex. The diffs render as inline text in the terminal.

    Is there a better approach? Honestly, not really. The current state of the art across all harnesses:

    HarnessDiff DisplayLimitation
    Claude CodeInline diff blocks in terminalNo side-by-side, hard to scan large changes
    CodexPatch-style outputBetter for review, worse for streaming
    Gemini CLIStandard diff formatMinimal formatting
    PiRaw old/new text in tool outputPartial JSON streaming shows diffs as they form

    Pi does one thing slightly better: partial JSON parsing during tool call streaming. As the model streams the edit tool arguments, Pi progressively parses them so you can see the diff forming in real-time before the call completes. It’s subtle but makes large edits feel more responsive.

    For anything more sophisticated (side-by-side diffs, syntax-highlighted changes, file-level diff views), you’re looking at extensions or external tooling. Pi’s headless JSON mode (--mode json) makes it straightforward to pipe tool outputs into a custom diff renderer if you want one. Someone will build that extension eventually. Or you can ask Pi to build it for you.

    Context Engineering Without the Abstractions

    Pi loads context files hierarchically:

    ~/.pi/agent/AGENTS.md          → Global instructions (all sessions)
    ./AGENTS.md                    → Project-specific rules
    ./SYSTEM.md                    → Override/append to system prompt

    That’s it. No dynamic injection framework. No RAG pipeline. No memory database. Just markdown files that get prepended to your conversation.

    Context LayerWhat Goes HereToken Cost
    SYSTEM.mdFull system prompt overrideReplaces default ~200 tokens
    Global AGENTS.mdCross-project rules, coding style~500-1000 tokens
    Project AGENTS.mdArchitecture, conventions, deployment~500-2000 tokens
    Skills (on-demand)Capability packs, loaded when needed0 until invoked

    That’s the static layer. But the hierarchy isn’t the ceiling — it’s the default. Extensions can inject messages before each turn, filter message history, or implement custom RAG and long-term memory. Compaction itself is fully pluggable: swap in topic-based summaries, code-aware compression, or route summarization through a cheaper model entirely. The primitives philosophy extends all the way down to context management.

    Why this matters for multi-tool workflows: I maintain one AGENTS.md per project that works across all my tools. Claude Code reads CLAUDE.md. Codex reads its own instructions. Pi reads AGENTS.md. The project context is portable.

    The Extension System: Build What You Need

    Pi ships with four tools: read, write, edit, bash. Everything else is a TypeScript extension.

    Need a plan mode? Build it. Need permission gates? Build them. Need MCP? Wrap it.

    The pattern that unlocked Pi for me: asking Pi to extend itself.

    You: "Write an extension that adds a /deploy command which 
         runs our staging deploy script and waits for health checks"
    
    Pi: *writes extension, saves to .pi/extensions/deploy.ts*
    
    You: /reload
    You: /deploy

    Self-modification, hot-reload, keep going. No pull requests to an upstream repo. No waiting for the next harness release. The tool adapts to you in real-time.

    Multi-Model Switching Mid-Session

    This is where Pi shines for exploratory work. Claude Code locks you to Anthropic. Codex locks you to OpenAI. Gemini CLI locks you to Google. Pi talks to 15+ providers and lets you switch mid-conversation:

    Ctrl+L → pick model from list
    Ctrl+P → cycle through favorites
    /model deepseek-chat → switch instantly

    My daily pattern:

    1. Start with a cheap model (DeepSeek, Haiku) for exploration and context gathering
    2. Switch to Sonnet or GPT-5.1 for the actual implementation
    3. Drop to a local Ollama model for sensitive code that shouldn’t leave my machine

    The context carries over between model switches. Pi handles the translation between provider-specific formats (thinking traces, tool call schemas, signed blobs) automatically. This is the pi-ai layer doing the heavy lifting: converting Anthropic thinking traces to <thinking> tags for OpenAI, normalizing token reporting across providers, handling signed blobs that need replay.

    The cost story is worth noting too. Pi’s session files are plain JSONL with token counts per message, which means community tools like agent-cost-dashboard can read them directly to produce spend timelines, per-model cost breakdowns, and cache-hit-rate analysis. Zero-dependency observability that comes free from the session format. When you’re switching between $0.25/MTok and $15/MTok models in the same session, knowing exactly where your budget went matters.

    Session Branching: The Underrated Killer Feature

    Multi-model switching is useful. But what makes it practical for exploration is Pi’s tree-structured session system. Sessions are stored as JSONL trees where every entry has an id and parentId. This means you can:

    • /tree — visualize your conversation as a branching tree, jump to any prior point
    • /fork — branch from any earlier message without losing the abandoned path
    • /clone — duplicate a branch to try a different approach
    • /export and /share — render the session as an HTML transcript, push to a GitHub gist

    Pi auto-summarizes abandoned branches so context isn’t lost when you switch directions. The combination with model switching is where this gets powerful: start exploring with DeepSeek, hit a wall, /fork back three messages, switch to Sonnet, retry. Both paths preserved. Both paths navigable.

    %%{init: {"layout": "dagre"}}%%
    flowchart TD
        A[Initial prompt] --> B[Exploration with DeepSeek]
        B --> C[Dead end]
        A --> D["/fork → Switch to Sonnet"]
        D --> E[Better approach found]
        E --> F["/fork → Try GPT-5.1 variant"]
        E --> G[Ship this one]
        style C fill:#f9f,stroke:#333,stroke-dasharray: 5 5

    For someone juggling multiple models daily, this is arguably a bigger differentiator than the four-tools minimalism. It’s the piece that makes exploratory branching across providers a real workflow instead of a party trick.

    The Benchmark Validation

    Pi’s minimalist philosophy sounds like cope until you see the numbers. On Terminal-Bench 2.0, Pi with Claude Opus scored competitively against Codex, Cursor, and Windsurf with their native models. All with a system prompt under 1,000 tokens and four tools.

    The even more telling result: Terminus 2, a minimal agent that just gives the model a raw tmux session (no tools, no file operations, just terminal interaction), also holds its own on the leaderboard. The models have been RL-trained to understand coding tasks. The harness overhead is largely wasted context.

    The One Thing I Wish Were Different: It’s TypeScript

    I’d have loved Pi in Python. Not because TypeScript is wrong for this. It’s clearly the right choice for a terminal TUI with streaming and event-driven architecture. But my extension and automation ecosystem is Python-heavy. Data pipelines, ML tooling, internal scripts. When I want to extend Pi with something that touches my existing infrastructure, there’s a translation layer I shouldn’t need.

    The Python alternatives for minimal agent harnesses are thin on the ground:

    OptionLanguagePhilosophyTrade-off
    PiTypeScriptPrimitives, not featuresBest minimal harness, wrong language for my infra
    TauPythonReadability-first, educationalClean architecture, less battle-tested as a daily driver
    opencodeGoClaude Code clone, open sourceMinimal philosophy but not truly extensible
    Build your own on tau_agentPythonFull controlYou’re now maintaining an agent harness

    Tau is the closest Python equivalent. HuggingFace built it to be understood, and the architecture is genuinely the same three-layer split (provider, brain, application shell). The 200-line core loop is identical in spirit to Pi’s agent loop. But Tau optimizes for pedagogy. Pi optimizes for daily use.

    If I were starting a Python-native minimal agent today, I’d seriously consider building on tau_agent as the core and layering Pi-style ergonomics on top: the hierarchical context files, the extension hot-reload, the tree-structured sessions. The architecture is proven. The implementation gap is mostly UX.

    For now, I live with the TypeScript trade-off. Pi’s extension system is productive enough that writing TypeScript extensions doesn’t slow me down for agent-level tooling. But the dream of pip install pi-agent and writing extensions in the same language as my ML stack remains unfulfilled.

    Getting Started

    Pi installs in one command and requires zero configuration to start using:

    # Install globally
    npm install -g @earendil-works/pi-coding-agent
    
    # Or run directly
    npx @earendil-works/pi-coding-agent
    
    # Start with your preferred provider
    export ANTHROPIC_API_KEY=sk-...
    pi
    
    # Or use OAuth for Claude Pro/Max
    pi --oauth

    The minimum useful setup takes about 5 minutes:

    1. Install Pi and set one API key (any provider works)
    2. Create ~/.pi/agent/AGENTS.md with your global coding preferences
    3. Add AGENTS.md to your project root with project-specific instructions
    4. Pick 2-3 favorite models and configure Ctrl+P cycling
    # Minimal global AGENTS.md
    mkdir -p ~/.pi/agent
    cat > ~/.pi/agent/AGENTS.md << 'EOF'
    - Use TypeScript unless told otherwise
    - Prefer functional patterns over classes
    - Run tests after changes
    - Commit messages: conventional commits format
    EOF

    From there, let friction guide you. Hit a wall? Ask Pi to write an extension. Need a skill? Install a package. The tool grows with your workflow, not ahead of it.

    Key resources:

    ResourceWhat You Get
    pi.devOfficial docs, philosophy, package registry
    GitHub mono-repoSource, 50+ extension examples, skills
    Mario’s blog postArchitecture deep-dive, design decisions
    YouTube crash course17-minute walkthrough covering 90% of features

    When to Reach for Pi

    After two months of daily use across all four tools, my routing heuristic:

    Task TypeToolReason
    Architecture planningClaude Code (Opus)Extended thinking, deep context
    Feature implementationClaude Code (Sonnet + sub-agents)Workflow orchestration, parallel agents
    Code review loopsCodexSystem review loops, iterative refinement
    Frontend visual workGemini CLI1M+ context, multimodal, visual reasoning
    HTML/slide generationGemini CLILong output, template iteration
    Quick explorationPi (cheap model)Zero overhead, instant start
    Multi-model experimentsPi (model switching)15+ providers, mid-session swaps
    Custom toolingPi (extensions)Self-modifying, hot-reload
    Non-coding agent tasksPi (SYSTEM.md override)General-purpose harness, not locked to coding
    Sensitive codePi (local Ollama)Nothing leaves the machine

    The pattern: use the heavyweight tools for heavyweight tasks. Use Pi for everything in between.

    The Bottom Line

    Pi isn’t trying to replace Claude Code, Codex, or Gemini CLI. It’s trying to be the agent equivalent of a good text editor: fast to start, minimal by default, infinitely customizable when needed.

    The “primitives not features” philosophy means you build exactly the tool you need. Nothing more. No cruft accumulates. No system prompt changes break your workflow between releases. And crucially, it’s not locked into being a coding agent. Swap the system prompt and tools, and it becomes whatever agent you need it to be.

    After two months, my honest assessment: Pi handles 40-50% of my daily agent interactions. Not the complex ones. The ones where I used to open Claude Code and immediately feel the overhead of 47 tools, permission prompts, and context injection I didn’t ask for.

    Sometimes less tooling produces more output.


    Using multiple coding agents in your workflow? I’d love to hear how you route between them. Reach out on LinkedIn.