Sandbox
@pulseaiclub/phi

Go terminal harness for coding agents and MCP

Phi is a lean terminal harness for running coding agents in a controlled loop. It gives the agent built-in tools, permission checks, sub-agents, MCP access, and extension support, while keeping session state and review inside the CLI.

356 stars16 forksGoUpdated 6d ago
Who it's for

Builders who want a terminal agent with permissions, sub-agents, and MCP in one place.

What it delivers

You can run agent-led coding sessions with reusable structure, safer tool access, and review without leaving the terminal.

What it does

Interactive and headless runs

Start the TUI with `phi` or run a single agent loop with `phi run -p "…"`.

Sub-agents with roles

Spawn isolated `explore`, `review`, and `worker` jobs so the parent context stays lean.

Hashline edits

Edit files with `@file path#TAG` and line hash anchors, with stale anchors rejected to avoid silent corruption.

Permission gate

Ask or deny before destructive tools run, with interactive, readonly, autopilot, and headless-strict modes.

MCP meta-tools

Use `mcp_list`, `mcp_inspect`, and `mcp_call` so tool schemas stay out of the main prompt until needed.

Extensions over PXB

Load Go or Rust extension binaries that add tools, slash commands, event intercepts, and confirm dialogs.

In-TUI diff review

Open `/diff` to inspect working tree, staged, or HEAD changes, add notes, and send them back to the agent.

Session persistence

Save and resume sessions per working directory under `~/.phi/session/`.

How to get it

  1. 1Install the latest release (macOS / Linux)
    curl -fsSL https://raw.githubusercontent.com/pulseaiclub/phi/main/scripts/install.sh | bash
  2. 2Windows (PowerShell 5.1+)
    irm https://raw.githubusercontent.com/pulseaiclub/phi/main/scripts/install.ps1 | iex
  3. 3First launch needs a model. Open the config editor (creates ~/.phi layout and writes…
    phi config
  4. 4Or set env vars for a one-off run
    export PHI_MODEL=gpt-4o
    export PHI_API_KEY=sk-...
  5. 5Then start the TUI
    phi
  6. 6Or build from source (Go 1.26.3+, see go.mod)
    make build          # produces ./phi
    make install        # build and install into $GOBIN

README

English | 中文

phi

Discord License CI Go Release

A lean, high-performance terminal coding agent harness in Go — a sibling to Pi.

Docs: pulseaiclub.github.io

  • Fast and small — ~12 MB release binary, ~21 MB idle RSS, ~40 ms to first frame; no Node / Electron / Python runtime
  • Sub-agents — spawn isolated jobs and watch the full run unfold in the TUI / job logs, without stuffing every turn into the parent context
  • Hashline edits — edit by whole-file @file path#TAG plus line LINE#HASH anchors (same idea as oh-my-pi): the model points at anchors instead of rewriting whole files; stale tags/hashes are rejected so over-edits and silent corruption stop here
  • Permission gate — Gate / Ask before destructive tools fire; safety is not optional when an agent can touch your tree
  • MCP without context death — configure as many MCP servers as you want; their tool schemas never enter the model prompt. The system prompt lists server names only (like the Skills catalog); the agent uses three meta-tools (mcp_list / mcp_inspect / mcp_call) to discover and call on demand. Same Gate / Ask / Hooks path as built-in tools. See MCP
  • Extensions (Go or Rust) — native binaries speak the PXB binary protocol over stdin/stdout; official author SDKs for Go (ext/go) and Rust (ext/rust): LLM tools, slash commands, event intercepts, confirm dialogs — no reflection; JSON at the SDK edges via serde_json. See Extensions
  • In-TUI diff review/diff opens a full-screen git review (working tree / staged / HEAD): syntax-highlighted hunks, line notes, then a sends notes to the agent. See Diff review
  • Any model — OpenAI-compatible or Anthropic, no vendor lock-in

phi welcome

phi TUI

phi diff review

Quick start

Install the latest release (macOS / Linux):

curl -fsSL https://raw.githubusercontent.com/pulseaiclub/phi/main/scripts/install.sh | bash

Windows (PowerShell 5.1+):

irm https://raw.githubusercontent.com/pulseaiclub/phi/main/scripts/install.ps1 | iex

First launch needs a model. Open the config editor (creates ~/.phi layout and writes ~/.phi/config.yaml):

phi config

Or set env vars for a one-off run:

export PHI_MODEL=gpt-4o
export PHI_API_KEY=sk-...

Then start the TUI:

phi

Or build from source (Go 1.26.3+, see go.mod):

make build          # produces ./phi
make install        # build and install into $GOBIN

On first start, phi automatically creates ~/.phi/{bin,skills,hooks,session}. Search tools (fd, rg) download into ~/.phi/bin in the background when missing.

The TUI gives the model four core tools — read, write, edit, and bash — plus grep, find, and ls. The model uses these to fulfill your requests. External HTTP fetch is available via MCP when configured.

Footprint

Lean is not enough — phi is built to feel instant and stay cheap under load. Numbers below are for a stripped release build (CGO_ENABLED=0, -ldflags="-s -w"), measured on macOS arm64 unless noted.

Metricphi
Release binary~12 MB
Idle RSS (1 session)~21 MB
10 idle sessions (total RSS)~196 MB (~20 MB each)
Time to first frame~40 ms (27–65 ms)
Cold go build (empty GOCACHE)~5.5 s
Warm rebuild~0.7 s
Go source (excl. tests)~22k LOC / 107 files
Go packages32
Direct module deps6 (15 modules total)
Linked runtimessystem libs only (no Node / Electron / Python)

Configuration

phi reads ~/.phi/config.yaml (standard YAML). Environment variables override it for one-off runs. phi config opens an HTML editor for the same file in your browser.

phi config

# ~/.phi/config.yaml
models:
  - name: gpt-4o            # model name; "claude-*" routes to the Anthropic API
    api_key: sk-...         # or set PHI_API_KEY
    base_url: https://api.openai.com/v1   # default; PHI_BASE_URL overrides
    context_window: 128000  # optional
    default: true           # the model used at startup; first entry wins if absent
  - name: claude-sonnet-4-20250514   # extra models; switchable at runtime
    api_key: sk-ant-...
    base_url: https://api.anthropic.com
    context_window: 200000

skill_path: ~/.phi/skills # where SKILL.md files are loaded from

agents:
  enabled: true           # default; set false to disable agent_* sub-agent tools
  models:                 # optional per-role defaults; omit → inherit parent model
    explore: cheap-model
    review: strong-model
    worker: coding-model

permissions:
  mode: interactive       # interactive | readonly | autopilot | headless-strict
  bash:
    default: ask          # ask | allow | deny
    allow:
      - "go test ./..."
    deny:
      - "rm -rf *"

Recommended model: DeepSeek Flash

phi + DeepSeek Flash — the best pairing: grounded, low hallucination, cache hit rates near 100%.

Measured data:

39 LLM rounds, same session — prompt 16k→40k, hit rate 95–100% (avg 98.7%).

RoundPrompt tokensCached tokensCache hit
116,17615,87298.1%
1020,16320,09699.7%
2027,60426,62496.4%
3035,24535,07299.5%
3939,79439,55299.4%
xychart-beta
    title "Cache hit % (39 rounds)"
    x-axis [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
    y-axis "Hit %" 95 --> 100
    line [98.1, 98.8, 97.5, 98.1, 98.1, 98.8, 99.1, 99.9, 98.7, 99.7, 99.5, 99.5, 96.3, 99.4, 97.0, 99.7, 99.9, 99.3, 98.3, 96.4, 98.9, 98.7, 97.2, 99.8, 95.0, 99.7, 99.2, 98.9, 99.3, 99.5, 99.5, 99.4, 99.2, 98.6, 99.4, 99.6, 100.0, 97.8, 99.4]

Environment overrides:

VariableOverrides
PHI_API_KEYmodels[].api_key (default model)
PHI_MODELmodels[].name (default model)
PHI_BASE_URLmodels[].base_url (default model)
PHI_SKILL_PATHskill_path

Provider routing: a base URL containing anthropic or a model name starting with claude uses the Anthropic Messages API; everything else uses the OpenAI-compatible /chat/completions path.

Workspace layout

~/.phi/
├── config.yaml   # global configuration
├── bin/          # downloaded search tools (fd, ripgrep)
├── skills/       # SKILL.md skill directories
├── extensions/   # PXB binaries + phi.yaml
├── jobs/         # sub-agent job artifacts (meta, logs, result.md)
└── session/      # persisted sessions, one dir per working directory
    └── <encoded-cwd>/

Interactive mode

phi (or phi tui) starts the TUI: a chat transcript on top, an editor at the bottom, and a footer with the current activity. When a newer release is available, the footer shows a hint like 0.2.0 available · phi update.

Assistant output is rendered as Markdown (CommonMark/GFM): headings, emphasis, strikethrough, links, blockquotes, lists, task checkboxes, and tables are styled with the active theme; fenced code blocks get a muted language caption and per-language syntax highlighting. Structural markers (#, `, *) are stripped.

The editor supports:

  • @ — fuzzy file mention picker (type @ and start typing a path)
  • / — slash command picker (/sessions, /resume, /clear, /diff)
  • ? — shortcut help picker (lists /, !, @, and key bindings; Esc closes)
  • !command — run a shell command locally and stream its output into the transcript (see Commands)
  • Ctrl+K — command palette: settings → model / theme / permissions / agents (incl. per-role models), skills, hooks

Keyboard shortcuts

KeyAction
Ctrl+CQuit phi
EscCancel the running agent / close pickers
Ctrl+KToggle the command palette
Ctrl+AJump to the start of the line
Ctrl+EJump to the end of the line
Ctrl+UClear the composer input, images, and skills
Ctrl+Shift+CCopy the selected transcript text

Themes: Dark (default), Darcula, Pink, and Terminal, switchable from the palette under settings → theme.

Diff review

/diff is a full-screen git review inside the TUI — read the change, leave line notes, then hand them to the agent without leaving the terminal.

CommandWhat opens
/diffWorking tree (git diff)
/diff stagedStaged changes
/diff HEADLast commit (git show)

Slash-picker Enter inserts /diff plus a trailing space into the composer (same pattern as /resume); submit to open. Inside the overlay: j/k move, s side-by-side, i add/edit a note, x delete, a send notes to the agent, ? help, q / Esc close. Notes persist under .phi/review.json.

Commands

CommandDescription
phi / phi tuiStart the interactive TUI
phi run -p "…"Run one agent loop headlessly (see below)
phi updateDownload and install the latest GitHub release
phi update --checkQuery the latest release without installing
phi sessions listList persisted sessions for this directory
/sessionsList sessions for this directory (TUI)
/resume <id>Resume a session by id or unique prefix (TUI)
/clearStart a fresh empty session (TUI)
/diffFull-screen git review — see Diff review
!commandRun a shell command locally, stream output into the transcript; Esc cancels it

In the TUI, !command runs locally via bash -c — outside the agent loop. It doesn't count toward agent busy state, and the running command can be cancelled with Esc without touching an in-flight agent turn.

Sessions

Sessions persist automatically per working directory under ~/.phi/session/<encoded-cwd>/ as JSONL trajectories.

  • phi sessions list — list session id, mtime, and preview for the current directory
  • /sessions in the TUI — same, in-app
  • /resume <id> — continue a session (id or unique prefix)
  • /clear — start a fresh session (new id, empty transcript)
  • phi run --session <id> / phi run --continue-last — resume headlessly

Headless mode

phi run -p "fix the failing test in internal/tools"

Runs one agent loop without a TUI. Human logs go to stderr; with --jsonl, machine-readable events go to stdout, one JSON object per line.

Flags:

FlagDescription
-p, --prompt STRINGPrompt to run (required)
--jsonlEmit JSONL events to stdout
--yoloSkip all permission checks for this run (benchmarks / CI only)
--max-rounds NCap tool rounds (default 64)
--timeout DURATIONLimit the agent run wall-clock time (e.g. 10m; disabled by default)
--session IDResume a persisted session by id or unique prefix
--continue-lastResume the newest persisted session for this directory
--session-dir DIROverride the session storage directory
--tools LISTEnable only these comma-separated built-in tools

--tools accepts built-in names such as read,ls,grep. MCP and agent tools still append when configured; the flag only scopes the built-in toolset.

Exit codes: 0 success · 1 runtime/LLM error · 2 max rounds reached · 3 config/usage error.

In the interactive TUI, exhausting the tool-round budget prompts Continue / Stop. Headless phi run has no confirmation UI, so it exits with code 2.

In headless mode, permission ask decisions are denied (there is no approval UI), so readonly-style safety applies without extra flags. For benchmarks that need arbitrary shell (pytest, npm test, …), pass --yolo to skip the permission gate for that run only.

Skills

Skills are directories containing a SKILL.md file with YAML frontmatter and a Markdown body. They are loaded from ~/.phi/skills/ (or skill_path / PHI_SKILL_PATH) and injected into the agent's context, letting you give the model reusable procedures:

---
name: My Skill
 description: What this skill does
license: MIT
compatibility: claude, openai
---
Instructions the agent should follow when this skill is relevant.

In the TUI, add skills from the palette (skills → list), then submit the message with the selected skills applied.

Permissions

Tool execution is gated by a permission policy, so the agent can run read-only by default and ask before anything destructive. Configure it under permissions: in ~/.phi/config.yaml.

Modes:

ModeBehavior
interactiveDefault. ask decisions prompt in the TUI.
readonlyDeny writes / bash; read tools still work.
autopilotFold ask → allow, run unattended.
headless-strictFold ask → deny (used by phi run).

Per-tool rules: bash.default / bash.allow / bash.deny (exact command prefix matching). Global keys: workspace_only_writes (default true), ask_timeout_sec, and dangerously_allow_all (default false).

In the TUI, an approval dialog replaces the editor with options to approve, deny with feedback, or allow all for the session / for every session. The palette's settings → permissions entry toggles session-wide bypass.

Extensions

Extensions are native binaries speaking the PXB binary protocol over stdin/stdout (author SDKs: Go github.com/pulseaiclub/phi/ext/go/phi and Rust ext/rust, phi-ext). They subscribe to tool/session events, register LLM tools, and add slash commands.

go get github.com/pulseaiclub/phi/ext/go@v0.21.0
package main

import (
	"github.com/pulseaiclub/phi/ext/go"
	"github.com/pulseaiclub/phi/ext/go/phi"
)

func main() {
	m := phi.New("hello", "0.1.0")
	m.OnToolCall(func(ev ext.ToolCallEvent) *ext.ToolCallResult {
		// return &ext.ToolCallResult{Block: true, Reason: "..."}
		return nil
	})
	_ = m.Run()
}

Install under ~/.phi/extensions/<name>/ with a phi.yaml pointing at the binary. In the TUI: Ctrl+Kextensions. Disable with PHI_EXTENSIONS=off. Full guide: doc/extensions.md.

Codec throughput on Apple Silicon (release, single-threaded):

ImplementationHello encode+decodeFrame write+read (in-memory)Allocs
Rust PXB (phi-ext)~0.12 µs~0.06 µs
Go PXB (ext/go/pxb)~0.11 µs~0.05 µs3 / op
Go JSON lines~1.2 µs15 / op

The ~10× gap over JSON lines is the protocol (fixed header + tagged fields), not the language — Rust and Go are within noise on the same codec work. Real extension latency is dominated by process spawn and pipe RTT anyway. Re-probe: cargo run --release --example bench in ext/rust.

MCP

Configure 100 MCP servers. Pay ~0 schema tokens until you call one.

Most MCP hosts dump every tools/list schema into the model context before you ask a question — browser stacks alone can burn 50k+ tokens. phi does not.

Instead the agent gets three meta-tools, and the system prompt lists configured server names (no schemas):

ToolRole
mcp_listList tool names on one server (compact text)
mcp_inspectFetch a slim parameter summary for one tool
mcp_callRun server + tool + args

Flow: pick a server from the prompt → mcp_list(server=…)mcp_inspectmcp_call. Subprocesses start lazily on first use. Calls still go through PreHooks → Gate / Ask → Run → PostHooks.

phi mcp add browsermcp -- npx @browsermcp/mcp@latest
phi mcp doctor
# In the TUI, the model can use configured servers without guessing MCP exists

Config: ~/.phi/mcp.json (project <cwd>/.phi/mcp.json overrides by name). Disable with PHI_MCP=off. Stdio and HTTP in v1.

Full guide: doc/mcp.md.

Sub-agents

Sub-agent tools (agent_spawn, agent_wait, …) are on by default. To keep a session lean, disable them in ~/.phi/config.yaml:

agents:
  enabled: false

Or toggle for the current session via the palette: settings → agents. When disabled, those tools are not registered and the model cannot spawn jobs.

Per-role model defaults (optional) under agents.models pick which configured model name each role uses when spawned. Omitted roles inherit the parent session model. Switch for the current session only via settings → agents → models → explore|review|worker (same session-only semantics as settings → model; does not write config.yaml).

Sub-agents themselves use a role (explore default | review | worker):

RoleToolsUse for
exploreno write/edit; bash except hard deniesMulti-hop recon / map structure
reviewsame as exploreDiffs / checks; report only
workerfull tools except nesting; bash except hard deniesScoped, self-contained edits

Default stays explore (no edits). Prefer worker when the task is to implement a scoped change in an isolated context.

Tools

Built-in tools the model can call (see internal/tools/):

ToolPurpose
bashRun a shell command in the working directory
readRead a file
writeWrite a file (gated by permissions)
editTargeted edit of a file
grepRegex search across files
findFile patterns (fd)
lsDirectory listing
agent_spawnStart an isolated sub-agent job (async)
agent_waitWait for a job; returns short summary only
agent_listList jobs
agent_cancelCancel a running job

Sub-agent transcripts live under ~/.phi/jobs/<id>/ and are not injected into the parent context — only the wait/task summary is.

Fast search tools (fd, ripgrep) are downloaded on first startup into ~/.phi/bin when missing.

See Project layout for the source tree map.

See CONTRIBUTING.md for development setup, code style, and commit conventions.

Files in the repo

Repository payload22 top-level entries
  • .github
  • assets
  • cmd
  • doc
  • ext
  • internal
  • scripts
  • .gitignore
  • .golangci.yml
  • .goreleaser.yaml
  • .markdownlint.yaml
  • AGENTS.md
  • CHANGELOG.md
  • CONTRIBUTING.md
  • go.mod
  • go.sum
  • LICENSE
  • Makefile
  • README.md
  • README.zh-CN.md
  • SECURITY.md
  • verify_released_changelog.sh

Discussion (0)

Ask about usage, or say what you built with it

Sign in to join the discussion.

No comments yet. Be the first to say what this is good for.

More harnesses

affaan-m/
ECC
affaan-m/ECCHarnesses

The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

258k
ruvnet/rufloHarnesses

🌊 The original agent meta-harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, RAG integration, and native Claude Code / Codex / Hermes and many more Integrated

72k

Practical patterns, starters & CLI tools for loop engineering with AI coding agents. Design systems that prompt and orchestrate agents (inspired by Addy Osmani and Boris Cherny). Includes loop-audit, loop-init, loop-cost.

11k