A terminal-based coding agent built on Google ADK Go. It connects to multiple LLM providers, runs sandboxed tools, integrates LSP, and ships with a process-based subagent system.
- Multi-provider LLM — Claude (Anthropic), GPT/O-series (OpenAI), Gemini (Google), OpenCode, and Ollama (local or cloud) for models
- Sandboxed tools — read, write, edit, shell, grep, find, tree, and git operations. All tools are restricted to the project directory via
os.Root. - Interactive TUI — Bubble Tea v2 with Markdown rendering (Glamour), slash commands, and theming
- Session persistence — JSONL append-only event logs with branching, compaction, and resume
- Model roles — Named configurations (default, smol, slow, plan, commit) selectable via CLI flags
- Subagents — Process-based multi-agent system with types: explore, plan, designer, reviewer, task, quick_task
- LSP — JSON-RPC client for Go, TypeScript/JS, Python, and Rust, with auto-format and diagnostics hooks
- AI Git tools — Repository overview, file diffs, hunk parsing, and LLM-generated conventional commits (
/commit) - RPC server — Unix socket JSON-RPC 2.0 for IDE/editor integration
- Memory Palace — 4-layer contextual memory with SQLite storage, semantic embeddings (all-MiniLM-L6-v2), temporal knowledge graph, and project/conversation miners
- Extensions — Hooks (shell callbacks), skills (
.SKILL.mdinstructions), and Model Context Protocol (MCP) servers - Skills audit — Security scanning for hidden Unicode characters, BiDi attacks, and supply-chain threats in skill files (
pi audit)
cmd/pi/ Entry point — CLI parsing, output mode selection
internal/
├── agent/ ADK agent setup, retry logic, runner
├── cli/ Cobra CLI flags, output modes (interactive, print, json, rpc)
├── config/ Global and project config (roles, hooks, MCP, themes)
├── audit/ Security scanner for skills (hidden Unicode, supply-chain threats)
├── extension/ Hooks, skills, MCP server integration
├── lsp/ LSP JSON-RPC client, language registry, manager, hooks
├── palace/ Memory Palace — drawers, layers, KG, miners, embedder, search
├── provider/ LLM providers implementing genai model interface
├── rpc/ Unix socket JSON-RPC 2.0 server
├── session/ JSONL persistence, branching, compaction
├── subagent/ Process spawner, orchestrator, concurrency pool
├── tools/ Sandboxed tools (read, write, edit, bash, grep, find, git, lsp)
└── tui/ Bubble Tea v2 UI, slash commands, commit workflow
User input → CLI → Agent → LLM provider → Tool calls → Sandbox → Response → TUI
↕ ↕ ↕
Session store Palace LSP servers
(JSONL events) (memory, (format, diagnostics)
KG, search)
See ARCHITECTURE.md for detailed documentation.
curl -fsSL https://raw.githubusercontent.com/dimetron/pi-go/main/scripts/install.sh | bashThis script detects your OS/arch, downloads the latest release binary, and installs it to /usr/local/bin (or ~/.local/bin if needed).
go install github.com/dimetron/pi-go/cmd/pi@latestMake sure your GOPATH/bin is in your PATH. The binary will be installed as pi.
git clone https://github.com/dimetron/pi-go.git
cd pi-go
go install ./cmd/piDownload the latest release for your platform from the Releases page.
- Go 1.25+
- At least one LLM provider API key or a running Ollama instance
Set the API key for your provider as an environment variable. The provider is inferred from the model name, so --model is usually the only routing you need.
| Provider | Model prefix | API key env var | Base URL env var |
|---|---|---|---|
| Anthropic | claude-* |
ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) |
ANTHROPIC_BASE_URL |
| OpenAI | gpt-* |
OPENAI_API_KEY |
OPENAI_BASE_URL |
| Google Gemini | gemini-* |
GEMINI_API_KEY (or GOOGLE_API_KEY) |
GEMINI_BASE_URL |
| Mistral | mistral-*, magistral-* |
MISTRAL_API_KEY |
MISTRAL_BASE_URL |
| xAI (Grok) | grok-* |
XAI_API_KEY |
XAI_BASE_URL |
| Azure OpenAI | azure/<deployment> |
AZURE_OPENAI_API_KEY |
— |
| OpenCode | opencode/<model> |
OPENCODE_API_KEY |
OPENCODE_BASE_URL |
| Ollama (local) | ollama/<model> |
none | OLLAMA_HOST (default http://localhost:11434) |
| Ollama Cloud | <model>:cloud |
OLLAMA_API_KEY |
https://api.ollama.com |
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export GEMINI_API_KEY="..."
export OPENCODE_API_KEY="..."
export OLLAMA_API_KEY="..." # only for Ollama Cloud (:cloud suffix)A name with no recognized prefix is rejected rather than guessed at — reach for the ollama/ prefix or the :cloud suffix to name an Ollama model explicitly.
make build # build the pi binary
make test # run unit tests
make lint # golangci-lint (vet, staticcheck, errcheck, …)
make e2e # run E2E integration tests
make clean # remove binary# Default interactive mode
pi
# Select a model by prefix
pi --model claude:sonnet
pi --model openai:gpt-4o
pi --model gemini:gemini-2.5-pro
pi --model ollama/gemma4:12b-mlx
pi --model opencode/kimi-k3
pi --model minimax-m3:cloud # automatically detect ollama if :cloud
# Use model roles
pi --smol # fast, cheap model
pi --slow # most capable model
pi --plan # planning-oriented model
# Additional options
pi --continue # continue last session
pi --session <id> # resume specific session
pi --system "..." # custom system instructions
pi --url "..." # custom API endpoint URL
# Non-interactive modes
pi --mode print "explain this codebase"
pi --mode json "list all TODO comments"
pi --mode socket --socket /tmp/pi-go.sock # JSON-RPC 2.0 over a Unix socket
pi --mode rpc # pi-compatible NDJSON over stdio (for pi-acp)| Command | Description |
|---|---|
/help |
Show available commands |
/model |
Switch model mid-conversation |
/session |
List and switch sessions |
/branch |
Create a conversation branch |
/commit |
Generate and apply a git commit |
/compact |
Compact session history |
/agents |
Show running subagents |
/history |
Show command history |
/plan |
Start a Plan-Driven Development (PDD) session (auto-resumes if a spec exists) |
/run |
Execute a spec with task agent |
/skill-create |
Create a new skill |
/skill-list |
List available skills |
/skill-load |
Reload skills from disk |
/memory |
Memory Palace commands (see below) |
/audit |
Scan skills for hidden Unicode threats |
/restart |
Restart pi-go |
/clear |
Clear conversation |
/exit |
Exit the agent |
A 4-layer contextual memory system that gives the agent persistent awareness across sessions.
Layers:
| Layer | Name | Description |
|---|---|---|
| L0 | Identity | Static identity file |
| L1 | Essential Story | Top-15 drawers by importance, injected into system prompt |
| L2 | On-Demand Recall | Context-filtered drawer chunks |
| L3 | Search | Semantic (embedding) or keyword (FTS5) search |
CLI commands:
# Setup
pi memory model download # download all-MiniLM-L6-v2 embedding model
pi memory model status # check model path and status
pi memory init [dir] # create palace.db + generate mempalace.yaml
# Ingest
pi memory mine <dir> # mine source files into drawers
pi memory mine --convos <dir> # mine conversation files (JSONL/text)
# Query
pi memory status # palace overview (drawers, wings, rooms, KG)
pi memory search <query> # semantic or keyword search
pi memory wake-up # print L0+L1 context for system prompt
pi memory recent [project] # recent memory observations
# Knowledge Graph
pi memory kg query <entity> # query triples involving an entity
pi memory kg add <s> <p> <o> # add a fact triple
pi memory kg timeline <entity> # chronological timeline of factsConfiguration via mempalace.yaml in the project root:
wing: my-project
rooms:
- name: auth
patterns: ["internal/auth/**"]
keywords: [jwt, token, session]
- name: api
patterns: ["internal/api/**"]
keywords: [handler, endpoint, route]When the Palace is enabled, the agent also gains tool access: palace-search, palace-add-drawer, palace-kg-query, palace-kg-add, palace-diary-write, palace-traverse, and more.
# Scan all skill files for hidden Unicode characters
pi audit
# Scan with verbose output (include info-level findings)
pi audit -v
# Output as JSON for CI pipelines
pi audit --format json --output report.json
# Auto-remove dangerous characters (creates .bak backups)
pi audit --strip
# Preview what would be removed
pi audit --strip --dry-run
# Scan a specific file
pi audit --file path/to/SKILL.mdSkills are automatically scanned on load — skills with critical findings (Unicode tags, BiDi overrides, variation selector attacks) are blocked from loading.
Pi reads configuration from ~/.pi-go/config.json (global) and .pi-go/config.json (project-local):
- Model roles — Map role names to specific model strings
- Hooks — Shell commands triggered on tool events (e.g., post-write formatting)
- MCP servers — External tool servers via Model Context Protocol
- Themes — Terminal color schemes via
themeconfig field - Base URLs — Per-provider endpoints via the
baseURLsfield
Self-hosted or LAN endpoints can be declared in config instead of exported in every shell:
{
"roles": {
"default": { "model": "ollama/gemma-4-e4b:latest", "provider": "ollama" }
},
"baseURLs": {
"ollama": "http://192.168.1.10:11434"
}
}Precedence is --url flag, then environment variable, then baseURLs config. The matching env vars are
ANTHROPIC_BASE_URL, OPENAI_BASE_URL, GEMINI_BASE_URL, MISTRAL_BASE_URL, XAI_BASE_URL, OPENCODE_BASE_URL, and OLLAMA_HOST. A per-shell or
CI override still takes effect. An empty env var does not mask a configured value.
Ollama's per-request options are left at the server's own defaults, except for an output cap. Each knob below is opt-in: unset means the option is not sent at all, so Ollama's default stays in force. An unparseable value is ignored rather than fatal — a typo in an env var should not take down an otherwise healthy session.
| Env var | Ollama option | Ollama default | Purpose |
|---|---|---|---|
PI_OLLAMA_NUM_PREDICT |
num_predict |
unlimited | Max tokens generated per turn. Pi defaults this to 16384; 0 or less removes the cap. |
PI_OLLAMA_REPEAT_PENALTY |
repeat_penalty |
1.1 |
How strongly repeated tokens are penalised. 1.0 disables. |
PI_OLLAMA_REPEAT_LAST_N |
repeat_last_n |
64 |
How many recent tokens the penalty looks back over. 0 disables, -1 uses the full context. |
PI_OLLAMA_PRESENCE_PENALTY |
presence_penalty |
0.0 |
Flat penalty for tokens already used. |
PI_OLLAMA_FREQUENCY_PENALTY |
frequency_penalty |
0.0 |
Penalty scaled by how often a token was used. |
These matter for models prone to repetition collapse, where a turn stops making
progress and restates the same phrase until it hits a limit. num_predict only
bounds how far such a turn runs; it does not stop it degenerating. The penalty
window is the knob that targets the cause, and the default window is narrow:
Ollama penalises repeats across the last 64 tokens only, while observed
degenerate turns cycle on phrases of roughly 25–55 tokens, so a full cycle can
fall outside the window the penalty can see.
# Widen the repetition window and penalise repeats harder.
export PI_OLLAMA_REPEAT_LAST_N=512
export PI_OLLAMA_REPEAT_PENALTY=1.2Both apply to local Ollama and Ollama Cloud — they share one request path. Raising these trades diversity for repetition control, and a value that helps one model can degrade another, so tune per model rather than setting them globally.
For OpenAI-compatible APIs with model names that Pi cannot infer from a prefix, explicitly set the role provider to
openai and point OPENAI_BASE_URL at the custom endpoint:
export OPENAI_API_KEY="your-api-key"
export OPENAI_BASE_URL="https://api.example.com/v1"{
"roles": {
"default": {
"model": "Qwen3.5-397B-A17B-FP8",
"provider": "openai"
}
}
}Then run Pi normally:
piYou can also pass the endpoint per invocation:
OPENAI_API_KEY="your-api-key" pi --model Qwen3.5-397B-A17B-FP8 --url https://api.example.com/v1When --url or OPENAI_BASE_URL is set, unknown model names are treated as custom OpenAI-compatible models. Setting
provider: "openai" in config avoids relying on model-prefix detection.
Pi supports the Model Context Protocol. Use it to extend the agent with external tools. Configure servers in
~/.pi-go/config.json:
{
"mcp": {
"servers": [
{
"name": "tavily-search",
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
},
{
"name": "filesystem",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/tmp"
]
}
]
}
}Or in standalone ~/.pi-go/mcp.json (Claude Desktop compatible format):
{
"mcpServers": {
"tavily-search": {
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
},
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/tmp"
]
}
}
}Supported transports:
- HTTP/Streamable —
urlfield for cloud-based MCP servers - Stdio —
command+argsfor local subprocess servers
Environment variable substitution: Pi automatically expands ${ENV_VAR} patterns in server URLs using .pi-go/.env
Pi can run as an Agent Client Protocol (ACP) server. Use it from any IDE that supports ACP.
Add pi to Zed's agent_servers in your settings:
{
"agent_servers": {
"pi": {
"type": "custom",
"command": "pi",
"args": ["acp-server", "--model", "glm-5.2:cloud"],
"env": {}
}
}
}Then invoke via Zed's agent panel (⌘⇧A / Ctrl+Shift+A) and select "pi". The agent runs in the current Zed project
directory with full access to pi's tools and memory.
See LICENSE for details.