Skip to content

Repository files navigation

pi

License: MIT Rust CI pi-ai on crates.io pi-agent on crates.io

A Rust port of earendil-works/pi — the pi agent harness — focused on the core coding-agent loop.

The upstream project is a TypeScript monorepo (~189k LOC). This port covers the agent runtime, multi-provider LLM API, and CLI end-to-end and ships a working pi binary that talks to real Anthropic, OpenAI, Google, and any OpenAI-compatible endpoint.

Layout

pi/
├─ Cargo.toml                       # workspace, version 1.0.0, MSRV 1.80
└─ crates/
   ├─ pi-ai/                        # ←→ packages/ai
   ├─ pi-agent/                     # ←→ packages/agent
   └─ pi-coding-agent/              # ←→ packages/coding-agent (binary: `pi`)
TS package Rust crate Status
@earendil-works/pi-ai pi-ai SSE streaming for Anthropic Messages, OpenAI Chat Completions, Google Generative AI. Retry with Retry-After. Cancellation token. Custom headers. OpenAI-compatible passthrough (OpenRouter, Groq, etc.).
@earendil-works/pi-agent-core pi-agent Streaming run_agent loop with per-tool permission gate, typed AgentError, #[instrument] spans. Builtin tools: read, write, edit, bash, ls, grep, glob, web_fetch, todo.
@earendil-works/pi-coding-agent pi-coding-agent pi CLI: print mode (-p), interactive REPL with streaming render, session persistence + --resume, AGENTS.md / CLAUDE.md loader, slash commands (/help /reset /model /tools /cost /sessions /resume /session), interactive permission prompts (--yolo to skip). pi sessions list/show/delete subcommand.
@earendil-works/pi-tui Not ported (TS terminal renderer).
@earendil-works/pi-web-ui Not ported (browser components).

Reusing the runtime

The streaming LLM API and agent runtime are published on crates.io:

[dependencies]
pi-ai = "1.2"      # provider-agnostic streaming
pi-agent = "1.2"   # agent loop, permission policy, built-in tools

Install the CLI via:

cargo install pi-coding-agent   # installs binary `pi`

See the crate-level docs at crates.io/crates/pi-ai and crates.io/crates/pi-agent.

Quick start

git clone https://github.com/nktkt/pi.git
cd pi
cargo build --release

export ANTHROPIC_API_KEY=sk-ant-...
# or any of: OPENAI_API_KEY, GOOGLE_API_KEY / GEMINI_API_KEY

# One-shot:
./target/release/pi -p "List the files in this directory and summarize them"

# Same prompt, JSON-lines on stdout for scripting:
./target/release/pi -p "..." --json

# Interactive:
./target/release/pi

# Resume a saved session:
./target/release/pi --resume <id>

# Skip permission prompts (bash/write/edit run unconfirmed):
./target/release/pi --yolo -p "Run the test suite"

# List saved sessions:
./target/release/pi sessions list

Pick the model explicitly:

PI_MODEL=claude-opus-4-7   pi -p "..."   # Anthropic
PI_MODEL=gpt-4o            pi -p "..."   # OpenAI
PI_MODEL=gemini-2.0-flash  pi -p "..."   # Google (via GOOGLE_API_KEY)

OpenAI-compatible providers

Any base URL whose API matches OpenAI Chat Completions works through the openai-completions code path. From Rust:

use pi_ai::Model;
let m = Model::openai_compat(
    "openrouter",
    "anthropic/claude-3.5-sonnet",
    "https://openrouter.ai/api/v1",
    200_000, 8_192,
);

Or override StreamOptions::base_url at call time. The same code path is exercised by Groq, Together, Cerebras, DeepSeek, Fireworks, xAI, etc.

Architecture

            ┌────────────────────────────┐
            │   pi-coding-agent (bin)    │
            │ print mode | interactive   │
            │ session persistence        │
            │ permission prompts         │
            │ AGENTS.md loader           │
            └──────────────┬─────────────┘
                           │ AgentConfig + tools + PermissionPolicy
                           ▼
            ┌────────────────────────────┐
            │         pi-agent           │
            │ run_agent / _with_history  │
            │ streaming events           │
            │ permission gate            │
            └──────────────┬─────────────┘
                           │ Context, StreamOptions (incl. CancellationToken)
                           ▼
            ┌────────────────────────────┐
            │           pi-ai            │
            │ stream_simple → Provider   │
            │  ├─ AnthropicProvider      │
            │  ├─ OpenAiProvider         │
            │  └─ GoogleProvider         │
            │  SSE + retry + cancel      │
            └────────────────────────────┘

Agent loop

  1. Append the user prompt onto the message transcript.
  2. Build a Context { system_prompt, messages, tools } and call pi_ai::stream_simple().
  3. Consume the SSE event stream. Emit TextDelta events to the agent subscriber as they arrive (the CLI prints them live).
  4. When the assistant finishes, append it. For each Content::ToolCall, consult PermissionPolicy if the tool flagged requires_permission(), then execute() and append the ToolResultMessage.
  5. Repeat until stop_reason ≠ ToolUse or max_turns is reached.
  6. After every turn the CLI persists the session to disk.

Tests

cargo test          # 10 passing, no network
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings

CI runs the same checks on macOS and Linux against stable + MSRV (1.80).

Roadmap & changelog

  • CHANGELOG.md — what shipped in 1.0.0.
  • ROADMAP.md — future 1.x targets: OpenAI Responses API, Bedrock, prompt caching, MCP client, --json print mode, mdBook docs, crates.io publishing.

License

MIT — same as the upstream project.

About

Rust port of earendil-works/pi — coding agent harness (pi-ai + pi-agent + pi CLI)

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages