zen-coding
Quant coding agent built on pi (@earendil-works/pi-coding-agent).
pi is a pinned npm dependency, not vendored: the harness (agent loop, TUI, sessions,
providers) comes from the package; everything zen-specific lives in .pi/extensions/ and
is auto-discovered whenever pi runs inside this repo.
Quick start
Requires Node >= 22.
npm install
# At least one model provider key (see Models below), e.g.:
export DEEPSEEK_API_KEY=... # or ANTHROPIC_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY, ...
export EXA_API_KEY=... # optional, enables the exa_search tool
# Optional: Braintrust tracing of interactive sessions (off by default)
export TRACE_TO_BRAINTRUST=true
export BRAINTRUST_API_KEY=...
export BRAINTRUST_PROJECT=zen-coding
npm run agent # launches the pi TUI with zen extensions loaded
On first launch pi asks you to trust this project (project-local extensions run arbitrary
code). Pick a model with /model, switch behavior with /zen <mode>.
Headless runs (used later by the eval harness and Slack backend):
npx pi -p "explain src/foo.py" # one-shot, prints result
npx pi --mode json -p "..." # streamed JSON events
npx pi --mode rpc # JSON protocol over stdin/stdout
Models
Open-source models are first-class. Two ways to use them:
- Hosted: export the provider key and select with
/model— DeepSeek (DEEPSEEK_API_KEY), Groq (GROQ_API_KEY), Fireworks, Together, OpenRouter (OPENROUTER_API_KEY), Hugging Face (HF_TOKEN), Kimi, Qwen, ZAI/GLM, MiniMax, … - Self-hosted (vLLM / SGLang / Ollama / LM Studio / llama.cpp): set
ZEN_LOCAL_BASE_URLandZEN_LOCAL_MODELS— see.pi/extensions/zen-models.ts.
Closed models (Anthropic/OpenAI/Google) work the same way, which makes cross-model
benchmarking (cost/latency per task, see design.md) a matter of swapping /model.
What's in .pi/extensions/
| Extension | Purpose |
|---|---|
guardrails.ts | Blocks denied bash patterns and writes to protected paths before any tool executes. Rules in .pi/guardrails.json. |
observability.ts | JSONL telemetry per session → .zen/traces/<sessionId>.jsonl: turn/tool latency, token usage, cost, model switches. Full transcripts live in pi's session files. |
Optional hosted tracing: @braintrust/pi-extension (in .pi/settings.json packages) streams
interactive sessions to Braintrust when TRACE_TO_BRAINTRUST=true + BRAINTRUST_API_KEY are set.
Slack-backend (SDK) sessions are traced by src/slack/tracing.ts under the same env vars.
The local JSONL traces remain the source of truth for the eval harness; Braintrust is the dashboard.
| modes.ts | /zen normal\|clarify\|plan — clarify asks follow-up questions first; plan is read-only. |
| zen-models.ts | Registers self-hosted open-source model endpoints from ZEN_LOCAL_* env vars. |
| zen-tools/ | exa_search (web/code/paper search). GitHub goes through gh + bash for now; proprietary data connectors land here. |
Extensions are TypeScript, loaded by pi without a build step. npm run typecheck checks them.
Skills (.pi/skills/)
Skills are on-demand capability packages (Agent Skills standard);
pi puts their descriptions in the system prompt and the agent loads the full
instructions only when a task matches (or force with /skill:<name>).
| Skill | Purpose |
|---|---|
mintlify-docs | Structure, write, build, and maintain internal documentation with Mintlify (docs.json + MDX). Includes reference sheets (navigation schema, frontmatter, components, CLI), a starter template for scaffolding new docs sites, and workflows for validation (mint validate, mint broken-links) and push-to-deploy. Pairs with the mintlify-docs MCP connector below for live reference lookup. |
Slack backend
src/slack/ implements design.md §User Interaction 1: @-mention the bot in a channel
(optionally with a GitHub link) and the agent works in a backend session, streaming its
reply into the thread via message edits. DMs work the same way without the mention.
export SLACK_BOT_TOKEN=xoxb-... # bot token
export SLACK_APP_TOKEN=xapp-... # app-level token (Socket Mode)
npm run slack
Slack app setup (https://api.slack.com/apps → Create New App → From a manifest):
- Pick your workspace and paste
slack-app-manifest.yaml(scopes, events, and Socket Mode are pre-configured). - Basic Information → App-Level Tokens → generate a token with
connections:write→ this isSLACK_APP_TOKEN(xapp-…). - Install App → Install to Workspace → copy the Bot User OAuth Token → this is
SLACK_BOT_TOKEN(xoxb-…). - Invite the bot to a channel (
/invite @zen-coding) and @-mention it, or DM it.
For private channels, additionally add the groups:history scope and the
message.groups event, then reinstall the app.
How it maps to pi:
- Thread ↔ session: each Slack thread (or DM) gets its own pi
AgentSession(SDKcreateAgentSession), persisted so threads survive restarts and idle eviction (mapping in.zen/slack/threads.json). - Repo checkout per thread: a GitHub link in the first message pins the thread to
a clone under
.zen/slack/workspaces/;GITHUB_TOKENenables private repos (token is passed per git call, never written to disk). No link →ZEN_SLACK_DEFAULT_CWD. - Streaming: replies stream into one message via throttled
chat.updateedits, with a tool-activity status line; long answers are chunked. - Steering: messages sent while the agent is running are delivered as steering
input to the ongoing run.
/zen plan|clarify|normalworks from Slack too. - Isolation: zen extensions/guardrails always load from this repo — a cloned
repo's
.pi/extensions/is never executed. Guardrails block writes outside the thread's checkout.ZEN_SLACK_ALLOWED_USERS(comma-separated user IDs) restricts who can drive the bot.
All knobs (model override, clone depth, idle eviction, …) are documented in
src/slack/config.ts.
MCP connectors
MCP servers are bridged via pi-mcp-adapter,
installed as a project package (.pi/settings.json; pi auto-installs it on first run
after trust). Servers are configured in .pi/mcp.json:
- alphaXiv (
https://api.alphaxiv.org/mcp/v1) — quant paper research:discover_papers,get_paper_content,answer_pdf_queries, GitHub repo reading for papers, and library folder management. Auth: create an API key at alphaxiv.org → Settings → API Keys andexport ALPHAXIV_API_KEY=...; in the TUI you can instead run/mcp-auth alphaxivfor browser OAuth. Without credentials the connector logs a warning and the session continues without it. - paper-search (openags/paper-search-mcp,
local stdio server run via
uvx paper-search-mcp— requires uv) — multi-source paper search/download across 24+ platforms: arXiv, PubMed, bioRxiv/medRxiv, Semantic Scholar, OpenAlex, Crossref, SSRN, Google Scholar, …. Unifiedsearch_papers/download_with_fallbackplus per-platform tools. Most sources work without keys; optional keys (CORE, Semantic Scholar, Unpaywall email) go in~/.config/paper-search-mcp/.env. Complements alphaXiv: alphaXiv gives AI-digested reports and library management, paper-search gives raw multi-source retrieval including SSRN. - Mintlify docs (
https://www.mintlify.com/docs/mcp) — live search over the official Mintlify documentation (docs.json schema, MDX components, CLI). Public, no auth. Used by themintlify-docsskill so docs work never relies on stale knowledge of Mintlify's format. - GitHub (
https://api.githubcopilot.com/mcp/, the official github-mcp-server) — structured issue/PR/repo tools:create_issue,add_issue_comment,create_pull_request, searches, notifications, …. Auth reuses yourghCLI login (the adapter runsgh auth tokenat connect time — no separate PAT needed). Note the agent acts as whoeverghis logged in as, regardless of which Slack user drove the request. To restrict capabilities, append/readonlyto the URL or scope with/x/<toolset>paths (e.g./x/issues).
All MCP tools are exposed through a single mcp proxy tool to keep the per-session
context footprint small. MCP config resolves from this repo's working directory, so
repos checked out by the Slack backend cannot inject their own MCP servers via a
committed .mcp.json (verified — a checkout's .mcp.json is not read). Start the
Slack backend from the repo root so the same config applies there.
Architecture & roadmap
The same extensions load in every pi mode, so the quant layer is built once and shared across all interaction surfaces (design.md §User Interaction):
- TUI (done — this scaffold):
npm run agent. - Slack backend (done —
src/slack/, see above): Slack threads ↔ pi sessions via the SDK; repo checkout per thread; streamed replies via message edits. - Sandboxing: run each backend session in a container (RPC subprocess), following pi-chat's isolation model.
- Eval/benchmark runner: drive
pi --mode jsonheadlessly over task datasets (beta-audit, portfolio construction, …), scoring outputs and reading cost/latency from the traces. - MCP connectors (done for alphaXiv + GitHub via
pi-mcp-adapter, see above): next up proprietary data connectors, via.pi/mcp.jsonor native tools inzen-tools/.
References
- pi docs: https://github.com/earendil-works/pi/tree/main/packages/coding-agent/docs (extensions.md, sdk.md, rpc.md, models.md, providers.md)
- Design notes: design.md