pi setup

Config

Config reference

Every item in agent/ — what it is, why it exists, and its default or allowed values (constitution Principle II, spec FR-013). scripts/check-docs.sh (run by just check) fails when an item in agent/ has no entry here.

How agent/ is loaded#

agent/ is mounted read-only at /opt/pi-config/agent in the sandbox. On every session start, the container entrypoint (pi-docker-sandbox/container/entrypoint.sh):

  1. writes this session’s stacked instructions to /run/pi-session/AGENTS.md and links ~/.pi/agent/AGENTS.md to it: agent/AGENTS.md first, then the non-empty AGENTS.md of each level of the domain’s configuration store, nearest last (spec 008);
  2. writes this session’s merged settings and MCP servers to /run/pi-session/settings.json and mcp.json (tmpfs) and links ~/.pi/agent/settings.json and mcp.json to them. The merge happens on the host: agent/settings.json and agent/mcp.json are the global level, the session domain’s configuration store ~/pi/config/pi-config-<domain>/ adds the levels down to the project (pi-config spec 007, pi-docker-sandbox docs/config.md); without a store the session gets exactly the files of this repo;
  3. installs every pinned package that is missing or at another version (container/install-packages.sh), after copying agent/npmrc to ~/.pi/agent/npm/.npmrc;
  4. starts pi.

Because these files live only in the session’s tmpfs, changes pi makes at runtime — saving a model with Ctrl+S in /model, /settings, /mcp toggles, pi mcp add, pi install — work for the current session but are discarded when it ends, and parallel sessions never see each other’s. To change something permanently, edit this repo (global) or the domain’s configuration store (domain, customer, project) and restart the session.

pi 1.0.0 only promises automatic installs on startup for project packages, so step 3 installs the user packages explicitly; it is a no-op when everything is already present.

agent/AGENTS.md#

Global instructions for every session: sandbox facts (paths and access), git rules (local Conventional Commits; start each new feature from the freshly pulled default branch unless a project or repo AGENTS.md overrides it; push only in a session with SSH push enabled, only feature branches, only when asked, never force or to a protected branch; spec 011), working style (plan first for non-trivial work, know the check before editing, context-hygiene handoff before /new), communication rules (lead with the result, no narration or recaps, short reports), yolo-mode rules (what a typed /yolo run may do and its hard limits), tool rules (simplest tool first, ctx_* only for large output), delegation rules (which cheap subagent for which task, what to keep, verify results), spec-driven development (use Spec Kit when .specify/ exists), memory rules (spec FR-016, FR-016a: store layout, when to write on its own, the never-store list, scope choice, what to do on every write, never commit or push memory) and a generic data-protection rule. This file is the global part only and names no domain in a rule: rules for a domain, customer or project live in ~/pi/config/pi-config-<domain>/…/AGENTS.md and are stacked after it (spec 008; the WORK rules for work and customer projects are in config/pi-config-work/AGENTS.md). The session-specific scope (which stores exist, their access, the briefings) is not here but in the per-session context file the sandbox appends with --append-system-prompt (session context). Project-level AGENTS.md files add to it.

agent/npmrc#

npm settings for installing the pinned pi packages. pi-docker-sandbox copies it to ~/.pi/agent/npm/.npmrc (pi’s npm project in the state volume) before every package install, so it applies to pi install. Only key: allow-scripts, the comma-separated packages whose install scripts may run — npm ≥ 11.19 warns about unreviewed install scripts and is moving to block them, which would break native builds (better-sqlite3, node-pty). Listed today: better-sqlite3, node-pty, context-mode, @ast-grep/cli (reasons in the file). The supply-chain PR check fails when a pinned package brings an install script that is not listed: review that script, then add the package here in the same PR. Not used: an environment variable (npm_config_allow_scripts counts as a command-line flag and npm refuses it for project installs).

agent/mcp.json#

Global MCP servers, in pi’s own format ({"mcpServers": {"<name>": {…}}}, pi 1.0’s docs/mcp.md). It is the global level of the layered configuration (pi-config spec 007): pi-docker-sandbox merges it with the session domain’s configuration store (~/pi/config/pi-config-<domain>/…/mcp.json) and gives pi only the merged result; deeper levels can add servers, replace them or switch them off ({"<name>": {"enabled": false}}).

Today: gateway-global — the Gateway MCP gateway’s Virtual MCP pi-global (https://mcp.example.net/mcp/pi-global: Joplin notes, Karakeep bookmarks), the tools wanted in every domain. Four tools are direct (toolExposure): Joplin search_notes and read_note, Karakeep search-bookmarks and get-bookmark-content. pi waits for a server with direct tools before each prompt and declares them, so searching notes and bookmarks works at once (a few hundred tokens per session). The other tools keep pi’s default codemode exposure; the description tells the model how to find them (searchTools('joplin …')). Without direct tools pi did not wait for Gateway, and an early tool search found nothing.

The key is ${GATEWAY_GLOBAL_TOKEN}: a Gateway virtual key of its own for pi-global, the same in every domain, because Gateway lets only one key use a Virtual MCP (with the domains’ keys, initialize was denied with 403 for all but one). The sandbox reads it once for all domains from MCP_GATEWAY_GLOBAL_TOKEN_CMD in pi-docker-sandbox/.env (or the Keychain item pi-sandbox / mcp-GATEWAY_GLOBAL_TOKEN; a domain may override it with MCP_<ID>_GATEWAY_GLOBAL_TOKEN_CMD). The domains’ own keys (${GATEWAY_TOKEN}, MCP_<ID>_GATEWAY_TOKEN_CMD) stay with their Virtual MCPs: pi-private (homelab tools, config/pi-config-private/mcp.json) and pi-work (work tools, the work store). Without the key the server is switched off for that session with a warning. Never put credentials here: write ${NAME} placeholders, which the sandbox resolves per session on the host (pi-docker-sandbox docs/config.md). The model proxy gateway server (pi-provider-model-proxy, below) registers itself and needs no entry here.

agent/settings.json#

pi’s global settings. Allowed keys and values are those of pi 1.0.0’s docs/settings.md.

defaultProvider#

Model provider at startup. Value: "router", the provider of the virtual model router/auto (see agent/extensions/model-router/). Was "anthropic" (carried over from the host). Change it together with defaultModel.

defaultModel#

Model at startup. Value: "auto" → router/auto, which picks a physical model per request. Was "claude-sonnet-4-6". If the router extension cannot load, pi falls back to its automatic default; any physical model stays selectable with /model or --model (e.g. --model github-copilot/claude-sonnet-5.5 to bypass the router for a session).

defaultThinkingLevel#

Reasoning level at startup. Value: "medium" (was high; medium is enough for routine work and is cheaper and less chatty — raise it per session or task). Allowed: off, minimal, low, medium, high, xhigh, max.

cacheWarming#

"idle" — keep provider prompt caches warm between runs as well as during them (pi’s default is "streaming", only while a run is active). Pi warms a cache only when the model declares a cache lifetime and the avoided cache-miss cost is at least about $0.05, so it never spends more than it saves. Global setting. Cheaper and faster follow-up turns after short pauses.

showCacheMissNotices#

true — show a notice when a significant prompt-cache miss, a cache warm-up or a provider recovery happens, so cost-relevant behaviour (for example a router tier change) is visible. /session shows per-model cost and cache use. Provider-side retention can be extended with the environment variable PI_CACHE_RETENTION=long (not set by the sandbox yet).

compaction#

modelOverrides for the 1M-context Claude models of the router: reserveTokens: 700000 makes auto-compaction trigger above 300K tokens (the default would let the context grow to ~980K). Why: cache misses are unavoidable now and then (the Copilot Claude cache lives about 5 minutes, see Prompt-cache misses), and a miss re-writes the whole context; at 500K tokens that is roughly $1.25 per miss on Sonnet. Compaction itself costs one miss, so this trades one cheap miss at 300K for many expensive ones at 500K+. Raise reserveTokens back (smaller value) if 300K is too little context for your work. Keys are exact provider/id; the claude-bridge/* ids use dashes (claude-sonnet-5-5).

Prompt-cache misses#

Measured in the session logs (~/.pi/agent/sessions, 2026-10-08): the Copilot Claude cache lives 5 minutes (calls after 3–5 min: 6/6 hits; 5–10 min: 1/5; over 10 min: 0/6). Of the misses, most were an idle pause over 5 minutes, the rest a router tier switch. Three levers:

  1. Warm the cache while idle: cacheWarming: "idle" only works for models that declare a cache lifetime, and the Copilot catalog declares none. Declare it in ~/.pi/agent/models.json (state volume, not managed by this repo):

    json
    {
      "providers": {
        "github-copilot": {
          "modelOverrides": {
            "claude-sonnet-5.5": { "promptCache": { "short": 300 } },
            "claude-opus-5.5": { "promptCache": { "short": 300 } }
          }
        }
      }
    }

    Pi then refreshes the cache shortly before it expires, at most 30 minutes after the last request and only when the saved miss cost is at least $0.05 (/session shows the decision).

  2. Fewer router switches: hysteresisTokens is 20000, so a session that already has more than about 20K tokens of context stays on its tier instead of dropping to a cheaper model and back.

  3. Smaller worst case: compaction above.

Not covered: claude-bridge/* models cache inside Claude Code (pi cannot warm them), and a session rebuild in the bridge re-sends the whole history (upstream README, known issues). PI_CACHE_RETENTION=long (1 h) only helps providers that support it (Anthropic API directly), not Copilot.

theme#

TUI theme. Value: "dark" (from the host). Built-in themes or names from a themes path.

externalEditor#

micro — the editor pi opens with Ctrl+G to write a longer prompt (pi’s app.editor.external keybinding). It runs inside the sandbox, so it must be a terminal editor in the image (pi-docker-sandbox installs micro, pinned); a host GUI editor such as Zed cannot be started from the container. micro uses familiar keys (Ctrl+S save, Ctrl+Q quit back to pi, Ctrl+C/Ctrl+V, mouse). Without this setting pi would fall back to $VISUAL/$EDITOR, which are unset in the image. Can be overridden per level in the configuration store (spec 007).

skills#

Where pi loads skills from. Value: ["/opt/pi-config/agent/skills"] — the read-only mount. Absolute container paths are required, because pi resolves relative paths against ~/.pi/agent. When a level of the configuration store has its own skills/, the sandbox copies these global skills and the level ones (nearest wins by name) to the read-only /opt/pi-layers/skills for that session and points this setting there (spec 008, pi-docker-sandbox docs/config.md).

prompts#

Where pi loads prompt templates from. Value: ["/opt/pi-config/agent/prompts"]. Level templates (prompts/<name>.md in the configuration store) are staged like skills, to /opt/pi-layers/prompts (spec 008).

extensions#

Extension files loaded at startup. Value: model-router/index.ts (the cost-aware router), rtk/index.ts (command-output compression), i-have-adhd/index.ts (ADHD-friendly reply style) and atelier-models/index.ts (MODELS sidebar panel), all under /opt/pi-config/agent/extensions/. Absolute container paths, like skills and prompts. See agent/extensions/.

subagents#

Settings of pi-subagents (models: https://github.com/nicobailon/pi-subagents, docs/models.md). Subagents run on the cheapest model that is good enough for their job; the entrypoint model is chosen by the router.

  • agentScanDirs: ["/opt/pi-config/agent/agents"] — loads the agent definitions of this repo (agent/agents/). The advertise: true agents appear in the parent prompt.

  • defaultModel: github-copilot/claude-sonnet-5.5 for any agent without its own model.

  • agentOverrides: model and thinking level for the builtin roles.

    AgentModelThinking
    scout (recon)gateway/gemini/gemini-3.5-flash-litelow
    researchergateway/gemini/gemini-3.8-flashhigh
    evidence-auditorgithub-copilot/claude-sonnet-5.5medium
    reviewer, worker, delegategithub-copilot/claude-sonnet-5.5medium
    oracle (risky decisions)github-copilot/claude-opus-5.5high

    Custom agents in agent/agents/ set their own model in their frontmatter.

  • modelScope: enforce: true, strict: false, allow = the three tier models of the router plus model-proxy/mimo-v2.5-pro (used by docs-writer) and the gateway/… models of scout, researcher and commit-writer. A model passed explicitly for a run (tool call, --model) outside this list is rejected; models from agent files or defaults only warn. Keep it in sync with tiers.json.

Inspect the live mapping with /subagents-models.

packages#

pi packages (extensions, skills, prompts they ship) installed into the state volume. Every spec must be npm:<name>@<x.y.z> with an exact version, so installs are reproducible and pi update leaves them alone. An entry is either that string or an object { "source": "npm:<name>@<x.y.z>", "skills": ["!<glob>"] }: pi’s package filter, here used to hide skills from the skills list in the system prompt (every listed skill costs tokens on every turn). The tools and commands of the package stay. The docs, supply-chain and install scripts read source from both forms. Packages run with the agent’s full access in the container (spec FR-024); add one only with a documented source, version and reason below. Review of the host packages: migration.md.

npm:pi-subagents@0.83.0#

Delegation to sub-agents and scripted multi-agent workflows. Source: npm pi-subagents.

npm:pi-web-access@0.42.0#

Web search, URL fetching, GitHub repo cloning, PDF and video extraction. Source: npm pi-web-access. Reaches arbitrary hosts (accepted network risk, spec FR-011).

npm:@juicesharp/rpiv-ask-user-question@2.13.9#

Lets the model ask the owner a structured question instead of guessing. Source: npm @juicesharp/rpiv-ask-user-question.

npm:@juicesharp/rpiv-todo@2.13.9#

A todo list for the model, rendered as a live overlay. Source: npm @juicesharp/rpiv-todo.

npm:pi-lens@4.4.5#

Real-time code feedback: LSP diagnostics, linters, formatters, type checks, scanners. Source: npm pi-lens. Downloads language servers and scanners into ~/.pi-lens and caches in the state volume on first use. Disable per session with -- --no-lens.

npm:context-mode@1.0.169#

Context-saving MCP plugin. Source: npm context-mode. Uses the native better-sqlite3 (prebuilt binary). Filter skills: ["!ctx-*"] hides the seven ctx-* slash-command skills (doctor, index, insight, purge, search, stats, upgrade): the matching ctx_* tools stay available, only the skill entries in the prompt go. The context-mode skill stays.

npm:@plannotator/pi-extension@0.28.31#

Interactive plan review with annotations. Source: npm @plannotator/pi-extension. Needs node-pty, compiled in the sandbox with the image’s toolchain. Its browser UI needs a reachable local port, which the sandbox does not publish, so the UI is likely unusable there.

npm:@vigolium/piolium@0.0.15#

Multi-phase security audits with specialist sub-agents (/piolium-* commands, --plm-* flags). Source: npm @vigolium/piolium. Declares pi ^0.74 as peer; verified to load on pi 1.0.0. Filter skills: ["!semgrep-rule-*"] hides semgrep-rule-creator and semgrep-rule-variant-creator, which no piolium agent or mode loads by name. The other piolium skills stay listed on purpose: its audit workflow and agents invoke them by name.

MCP (Model Context Protocol) is not a package: pi ≥ 1.0 has built-in MCP support (builtin:mcp; servers in ~/.pi/agent/mcp.json, pi mcp add/list, /mcp). The former package pi-mcp-adapter was removed on 2026-10-04: it registered /mcp too, replaced the built-in support and caused a startup warning, while no MCP servers were configured. No MCP servers are configured yet; a future server config must reference credentials by env-var name only (${NAME}, spec FR-014).

model-proxy.providers.gateway (settings key model-proxy)#

A second provider alias of pi-provider-model-proxy for the Gateway AI gateway (https://mcp.example.net/v1, OpenAI-compatible). Models are discovered at start from /model/info, falling back to /v1/models, and appear as gateway/<id>, for example gateway/llama-swap/qwen3-14b (local), gateway/gemini/gemini-3.8-flash or gateway/openrouter/free; no model list is kept in this repo. The key is $PI_GATEWAY_API_KEY, read on the host by pi-sandbox (static key or password-manager command, falling back to GATEWAY_GLOBAL_TOKEN; see pi-docker-sandbox docs/config.md). Without a key the provider has no models, and agents that use gateway/… models fail until it is set. Not verified against Gateway yet: check with /model that gateway/… models are listed.

npm:pi-provider-model-proxy@4.5.0#

model proxy proxy provider: discovers the models of a self-hosted model proxy proxy and registers them as provider model-proxy (models appear as model-proxy/<model-id>). Source: npm pi-provider-model-proxy (MIT, github.com/balcsida/pi-provider-model-proxy); needs pi ≥ 0.99.2; added on the owner’s request on 2026-10-03, not part of the host migration.

Since 4.0 the proxy’s MCP gateway (model proxy’s /mcp endpoint) is registered as an MCP server named model-proxy in pi’s built-in MCP client (builtin:mcp, see the MCP note above): tools appear as mcp__model-proxy__<tool> in /mcp, default exposure codemode, credentials are the provider login (sent as Authorization: Bearer, only over https or to loopback). This needs the built-in MCP support enabled — another reason not to install pi-mcp-adapter. Setting model-proxy.mcp.enabled: false turns it off; /mcp reconnect model-proxy reconnects. Up to 3.x the extension used its own REST discovery with tools named mcp_<server>_<tool>_<hash>; none of that is configured here, so nothing needed migrating.

Configure it inside the sandbox, once:

  • /login model-proxy → Sign in with an API key: enter the proxy base URL and API key. Both are stored in ~/.pi/agent/auth.json in the pi-agent-state volume (same place and rules as the default model login; never in this repo). Select a model with /model or -- --model model-proxy/<model-id>.
  • SSO: model proxy’s browser login with a temporary 127.0.0.1 callback cannot work in the sandbox, because no ports are published to the host browser. Use the API key, the proxy’s /sso/cli/start verification-code flow, or paste a token from the model proxy UI.
  • Not used here for the default model-proxy provider: the env-var route (MODEL_PROXY_BASE_URL / MODEL_PROXY_API_KEY) and a model-proxy.providers.model-proxy block. Either would have to be declared in the sandbox (compose env, .env.example, Keychain) first. The gateway alias above is the declared exception.

Jev on the proxy (server side, not configured in this repo). model proxy ≥ 1.104 can run TypeSafe Jev for you. These are changes to the proxy config.yaml that the proxy owner applies, with TYPESAFE_API_KEY in the proxy environment:

  • guardrails: [{guardrail_name: jev-compaction, model-proxy_params: {guardrail: typesafe, mode: pre_call, optional_params: {relevance_threshold: 0.2}}}]: blanks tool results Jev judges irrelevant to the task before the request reaches the model (all-or-nothing per tool exchange, fail-open). Attach it to the virtual key pi uses (/key/generate with "guardrails": ["jev-compaction"]) or per request. Useful for long agent sessions; check x-model-proxy-applied-guardrails and the spend log (exchanges_dropped). Tool output is sent to TypeSafe.
  • Decision Model guardrail (guardrail: decision_model): yes/no questions you write about each request, e.g. prompt injection, with block or log at a probability threshold.
  • Unified POST /v1/decisions and /v1/systemone routes and an Auto Router with classifier_type: jev exist, but pi does not use them (its router is the extension above).

Revoke: /logout model-proxy deletes the local credential only; revoke the key or session on the model proxy proxy itself. The proxy host is an additional outbound endpoint (pi-docker-sandbox/docs/security.md).

npm:pi-claude-bridge@0.9.3#

Claude Code as a model provider (claude-bridge/<model>, e.g. claude-bridge/claude-opus-5) and an optional AskClaude tool, so the owner’s Claude subscription quota is used instead of an API key. Source: npm pi-claude-bridge (MIT, github.com/elidickinson/pi-claude-bridge); needs pi ≥ 0.86.1; added on the owner’s request on 2026-10-07. It runs the Claude Agent SDK (@anthropic-ai/claude-agent-sdk, bundled claude binary as a platform optional dependency) in-process; no install script (checked: npm install-scripts ls), so nothing is added to agent/npmrc.

  • Login (once, inside the sandbox): the bundled Claude Code needs its own subscription login, stored in ~/.claude in the pi-agent-state volume (never in this repo). Run claude with the bundled binary (or /login in a Claude Code session) and use the paste-the-code flow: the browser callback cannot work here because no ports are published.
  • Config: optional ~/.pi/agent/claude-bridge.json (not managed by this repo): set provider.plan to "max" on a Max plan (default "pro"), askClaude.enabled to true to register AskClaude (default off). strictMcpConfig stays true so the bridge does not load MCP servers from ~/.claude.json.
  • Model scope: subagents.modelScope.allow does not list claude-bridge/*, so subagents cannot use it; only the entrypoint can. router/auto prefers it: the first model of every tier is a claude-bridge/* one, with the pay-per-token model as fallback (model-router). Without the Claude login the first request fails and the router demotes the bridge model for the session.
  • Network: api.anthropic.com and claude.ai become additional outbound endpoints (pi-docker-sandbox/docs/security.md). Prompts and tool results go to Anthropic through Claude Code. Anthropic’s terms for using subscriptions via the Agent SDK have changed before (upstream README); check them before relying on it.

npm:@dietrichgebert/ponytail@5.3.2#

“Lazy senior developer” ruleset: before writing code the agent climbs a ladder (does it need to exist, is it already in the codebase, stdlib, platform, an installed dependency) and stops at the first rung that holds; replies end with what was skipped. Source: npm @dietrichgebert/ponytail (MIT, github.com/DietrichGebert/ponytail); declares a pi extension (injects the rules every turn) and six skills: /ponytail, /ponytail-review, /ponytail-audit, /ponytail-debt, /ponytail-gain, /ponytail-help. No install script, no dependencies. Added on the owner’s request on 2026-10-17.

npm:pi-atelier@0.16.0#

Responsive status rail and live activity sidebar (model, context, Git status, usage, tool and subagent activity). Source: npm pi-atelier (MIT, github.com/michaelmjhhhh/pi-atelier); needs pi ≥ 0.84 and Node ≥ 22.19, interactive TUI only; pi core packages are optional peers, no dependencies, no install script. No telemetry; reads Git read-only and only in trusted projects. Open with /atelier or F6; if icons render as boxes, set Settings → Font mode → Plain text. Added on the owner’s request on 2026-10-17.

npm:pi-worktree-extension@2.0.3#

Parallel pi sessions on one repo, each in its own git worktree and branch. /worktree <name> moves the active conversation into .pi/worktrees/<name> (branch worktree-<name>, from origin/HEAD, never fetched); also /worktree-switch, /worktree-list, /worktree-exit, /worktree-remove (only clean, inactive checkouts; branches are kept, no force). Running pi again in a worktree resumes its latest session. Source: npm pi-worktree-extension (MIT, github.com/AjayPoshak/pi-worktree-extension); no install script, no dependencies. The optional pi -w <name> shell wrapper is not set up here; use /worktree inside pi. Creating a worktree needs a clean source checkout. Base can be switched to HEAD with { "base": "head" } in <repo>/.pi/worktree.json. Add .pi/worktrees/ to the project’s .gitignore/.git/info/exclude. Added on the owner’s request on 2026-10-19.

npm:@llblab/pi-telegram@0.58.3#

Telegram bridge: follow and steer the running session from your phone (prompts, queue, /stop, answers, files, voice, model and thinking menus). Source: npm @llblab/pi-telegram (MIT, github.com/llblab/pi-telegram); no dependencies, no install script, outbound only to api.telegram.org (Bot API long polling, no inbound port). Chosen on 2026-10-19 over the own phone-bridge extension (replies did not arrive) and the other Telegram packages as the most used and most active one. It runs with the agent’s full access, and everything it sends or receives passes Telegram’s servers without end-to-end encryption: don’t send customer or personal data or secrets through the chat.

Setup: give the sandbox the bot token as TELEGRAM_BOT_TOKEN (TELEGRAM_BOT_TOKEN_CMD in pi-docker-sandbox/.env), run /telegram-setup in pi (it stores the reference $TELEGRAM_BOT_TOKEN, not the token, in ~/.pi/agent/telegram.json), set your numeric user id as profiles.default.allowedUserId in that file before connecting (otherwise the first user who sends /start becomes the owner), then /telegram-connect and /start in the bot chat. One poller per bot: a second connected session stands down. Runtime state and inbound files live under ~/.pi/agent/tmp/pi-telegram/ (state volume). Added on the owner’s request on 2026-10-19.

agent/skills/ — vendored Matt Pocock skills#

Seven skills copied unchanged from github.com/mattpocock/skills at tag v1.3.1 (commit 0b6cee10f260, MIT, licence in agent/LICENSE.mattpocock), without the per-skill agents/ metadata. The licence sits outside agent/skills/ because pi-sandbox accepts only skill folders there (a file there fails the start when a domain level brings its own skills). Not a pi package, so they are vendored and updated by hand (diff against a newer tag, then replace). A curated subset on purpose: the issue-tracker skills (to-spec, to-tickets, triage, wayfinder, setup-matt-pocock-skills) and the implement* skills need a tracker setup and overlap Spec Kit and the subagents here. Added on the owner’s request on 2026-10-17.

agent/skills/grilling#

Relentless interview to stress-test a plan, decision or idea (also on “grill me”).

agent/skills/tdd#

Test-first loop (red-green-refactor); with mocking.md and tests.md.

agent/skills/diagnosing-bugs#

Diagnosis loop for hard bugs and performance regressions; ships a human-in-the-loop script template (scripts/hitl-loop.template.sh, only copied and run on request).

agent/skills/codebase-design#

Shared vocabulary for deep modules, seams and interfaces; with DEEPENING.md, DESIGN-IT-TWICE.md. tdd points to it.

agent/skills/handoff#

Compacts the conversation into a handoff document for another agent.

agent/skills/wait-what#

“That last message did not land”: re-pitch it in plainer words.

agent/skills/writing-for-agents#

Rules for writing skills and AGENTS.md files; with SKILL-MECHANICS.md.

agent/skills/terraform-skill#

Diagnose-first guidance for Terraform and OpenTofu (modules, tests, CI, scans, state). Public skill by Anton Babenko, v1.17.1 (github.com/antonbabenko/terraform-skill, tag v1.17.1, commit b59d2be), Apache-2.0 — LICENSE is kept next to it. Vendored as plain files (SKILL.md and references/, nothing else from the repo) after a secrets scan; until 2026-10-05 this was a link to a folder outside the repo that did not exist, so the skill never loaded (migration.md). Loaded on demand. The image has no terraform/tofu binary yet.

agent/skills/herdr#

Official skill of Herdr (github.com/herdrdev/herdr, skills/herdr/SKILL.md, master as of 2026-10-08), Apache-2.0 — LICENSE is kept next to it. Vendored unchanged (only SKILL.md). It controls Herdr panes, tabs, workspaces and agents through the herdr CLI and applies only when the owner mentions Herdr and HERDR_ENV=1 is set. The sandbox has no herdr binary and is not a Herdr-managed pane, so the skill is inert here; it is useful where pi runs inside Herdr. Loaded on demand.

agent/skills/workspace-clone#

How the workspace is built: explains the host command pi-sandbox workspace clone|refresh|list (spec 009, pi-docker-sandbox docs/workspace.md), the forge token settings per domain and host, the layout ws/<domain>/<host>/<group>/<subgroup…>/<repo>, and helps write the list of groups — with the session’s domain filled in. It tells the agent never to clone groups itself and never to handle a forge token (sessions have none). Adapted from ghorg’s skill ghorg-create-workspace (github.com/gabrie30/ghorg v1.11.16, Apache-2.0; LICENSE kept next to it). Global, so it is available in every domain. Loaded on demand.

agent/design-skills/ — vendored design skills (designer agent only)#

Skills for the designer subagent, kept outside agent/skills/ on purpose: that folder is loaded into every session’s skills list (every entry costs prompt tokens), while these are reached only through the agent’s skillPath: ../design-skills and skills: list (agent/agents/designer.md). Not pi packages, so vendored by hand and updated by diffing against a newer upstream commit. Added on the owner’s request on 2026-10-27. Each folder keeps its upstream licence; unmodified upstream files, so linter findings in them (innerHTML, callbacks) are upstream’s and not fixed here.

FolderUpstream (commit)Licence
agent/design-skills/impeccablegithub.com/pbakaus/impeccable, .pi/skills/impeccable (d34c87e, skill 4.8.0, engine 0.1.18)Apache-2.0 (LICENSE, NOTICE.md)
agent/design-skills/design-taste-frontendgithub.com/Leonxlnx/taste-skill, skills/taste-skill (12af471), without evals/; README renamed README.upstream.mdMIT
agent/design-skills/emil-design-eng and nine more (animate, animation-performance, animation-vocabulary, break-ui, find-animation-opportunities, improve-animations, pick-ui-library, prototype, review-animations)github.com/emilkowalski/skills (2d22863); the iOS, Android, Swift, Expo and Sonner skills are left out (not web)MIT (emil-design-eng/LICENSE)
agent/design-skills/gsap-core, gsap-timeline, gsap-scrolltrigger, gsap-plugins, gsap-performance, gsap-utilsgithub.com/greensock/gsap-skills (aed9cfd); gsap-react and gsap-frameworks left out (no React here)MIT (gsap-core/LICENSE)
agent/design-skills/leniswritten here from the README of github.com/darkroomengineering/lenis v1.3.27 (e01eefb); upstream is a library, not a skillMIT (upstream)
agent/design-skills/react-bitswritten here for github.com/DavidHDev/react-bits (e12c221); a catalogue and a porting guide to vanilla CSS/JS, no code copiedMIT + Commons Clause (upstream)
agent/design-skills/awesome-designwritten here as a pointer index to github.com/gztchan/awesome-design (89f8111, a link list)CC0 (list)

Impeccable’s launcher scripts/impeccable runs a prebuilt engine binary: it checks a version- pinned cache first and otherwise downloads it once, verified against engine-checksums.txt (needs network). Taste’s scripts (scripts/, a real-browser inspector) need Node and a browser. Neither is installed in the image by this change.

agent/agents/#

Subagent definitions (pi-subagents), loaded through subagents.agentScanDirs. Every file needs its own entry. They inherit the repository’s AGENTS.md (not the global one, which saves tokens) and none holds secrets or push rights.

agent/agents/worker-lite.md#

Cheap implementer (model-proxy/deepseek-v4.1-flash, thinking low) for mechanical, well-specified edits. Tools: read, grep, find, ls, bash, edit, write. Stops and reports instead of making design decisions; never commits.

agent/agents/docs-writer.md#

Documentation specialist on model-proxy/mimo-v2.5-pro (thinking low). Writes and updates docs, READMEs, reference pages, changelog entries and Conventional Commit messages from code, diffs or a description: finds the existing docs first and matches them, checks every statement against the source, marks what it could not verify, never commits. Tools: read, grep, find, ls, bash, edit, write, and subagent restricted to mermaid-diagrammer (allowedAgents, maxSubagentDepth: 2: depth is counted from the top-level session, so docs-writer is depth 1 and the diagrammer depth 2; the diagrammer has no subagent tool and cannot delegate further). Model choice: see specs/010-cost-aware-agents/research.md.

agent/agents/commit-writer.md#

Writes one Conventional Commit message from the staged diff on the local model gateway/llama-swap/qwen3-14b (thinking off, no cost). Tools: read, grep, find, ls, bash; never commits and returns only the message.

agent/agents/mermaid-diagrammer.md#

Mermaid specialist on github-copilot/claude-sonnet-5.5 (thinking low). Creates flowchart, sequence, state, class and ER diagrams from code, config or docs, for Markdown that renders on GitHub, GitLab and Forgejo/Codeberg. Return mode (default, and whenever another agent calls it): the fenced block(s) with a caption. Write mode (task names a file and a place): inserts the block there. Carries the syntax rules behind most broken diagrams (quoted labels, escaped characters, reserved ids, balanced end). It cannot render or parse Mermaid here and says so; validate in the forge preview or with mmdc on the host. Tools: read, grep, find, ls, edit, write; no shell, no nested subagents. Any agent that has the subagent tool may call it.

agent/agents/check-runner.md#

Cheap runner (model-proxy/deepseek-v4.1-flash, thinking low) for tests, linters and builds. Tools: read, grep, find, ls, bash; read-only in effect. Returns a PASS/FAIL verdict with the failing items so large output stays out of the parent context.

agent/agents/designer.md#

Web design specialist (github-copilot/claude-sonnet-5.5, thinking medium) for the Hugo site in pages/. Tools: read, grep, find, ls, bash, edit, write, plus the playwright and figma MCP tools. Gets only the skills of agent/design-skills/ (skillPath: ../design-skills, inheritSkills: false). Runs in the background (async: true), because MCP tools reach a child only that way (pi-subagents docs/agents.md). Rules in the prompt: self-host everything, keep accessibility and reduced motion, verify in the browser at 375/768/1440 px or say it could not, never commit. The MCP servers are not global: they are defined for the pi-setup projects only in pi-config-private/codeberg.org/black0ut-pi-setup/mcp.json: playwright (@playwright/mcp@0.0.83, headless, isolated) and figma (figma-developer-mcp@0.13.2, token from the environment variable FIGMA_API_KEY, set by FIGMA_API_KEY_CMD in the sandbox .env; any tool of the session can read it). playwright drives the image’s Debian chromium (pi-docker-sandbox Dockerfile, ARG CHROMIUM_VERSION) with --executable-path /usr/bin/chromium --no-sandbox (the session container allows no user namespaces), so point it only at the local site. Without FIGMA_API_KEY_CMD the variable is empty and figma calls fail. Neither server has been run end to end yet.

agent/prompts/#

Prompt templates (/name commands), loaded through the prompts setting. Every file here needs its own entry.

agent/prompts/memory-seed.md#

/memory-seed [core|domain|<level path>|project] — fills memory deliberately instead of waiting for it to accumulate (spec 004 US4).

  • project (default): reads the current project’s documentation and configuration (README, CONTRIBUTING, docs index, CI files, build/package manifests, Makefile/justfile, Dockerfile/ compose) and writes the project’s memory level.
  • core, domain, or a level path of the current session (e.g. a host or group folder): interviews the owner, at most 8 short questions per round, and writes that level.
  • Writes in the memory format (briefing ≤ 60 lines, notes with title, scope = level, updated, tags, plus index.md and log.md) and follows the never-store rules. It never opens .env*, key files, auth.json or other credential files, and refuses levels that are not on the current path or are read-only/missing.
  • Ends with a list of what was written and the reminder to run pi-sandbox memory-check &lt;domain&gt; and then pi-sandbox memory-sync &lt;domain&gt;, so nothing leaves the machine unreviewed.

agent/prompts/yolo.md#

/yolo <task> [env=<name>] [test=<command>] [rollback=<steps>] — an autonomous run for one task (spec 012): implement, test, push a feature branch, open and merge its own PR, follow the pipeline, deploy and test in the environment, without asking after the start. Only the typed prompt enables it; it ends when the task is done or the run stops.

  • Inputs: the project’s ## Yolo block in its AGENTS.md (environments, pipeline, checks, dry-run, post-deploy test, rollback) plus the arguments, which override it for the run. Anything missing is asked once, in one grouped question, before any change; later gaps stop the run. Block format: specs/012-yolo-mode/contracts/project-yolo-block.md; no secrets in it.
  • Gates before merge: CI green on the exact head commit, the change’s own tests passed (not runnable = failed), a reviewer subagent verdict with no open findings, and 100% confidence. Before deploy: a stated rollback path (irreversible or missing = stop) and, for production, a dry-run/plan where the tooling provides one. After deploy: the post-deploy test; on failure it rolls back, verifies and stops.
  • Stops on a failed or unverifiable gate, any surprise, 3 failed attempts at the same failure, a gap after the start, or a need outside the named project and environments.
  • Never: force-push, delete or move branches or tags, rewrite history, merge a PR it did not open, force-merge, add or look for credentials. It uses only the session’s push key and the forge tool.
  • Prerequisites (the real limits; the gates are rules, not enforcement): the session has a “Git push” section, the forge enforces branch protection with required CI on the default branch, and the push key and forge token are scoped as narrowly as the forge allows.
  • Ends with a report of every outward action with its gate evidence and what was not verified. Accepted risk and revocation steps: constitution v1.3.0 and pi-docker-sandbox/docs/security.md.

agent/skills/speckit#

Spec-driven development with GitHub Spec Kit (spec 010 follow-up; skill text is original, no upstream text copied). Loads when a project has .specify/ or the owner asks for specs, plans, tasks or a constitution. It tells the agent how to work with the generated artifacts: read the constitution first, order spec → clarify → plan → tasks → analyze → implement, implement from tasks.md with local Conventional Commits, no secrets in specs, delegate mechanical parts to the cheap subagents, and never invent a .specify/ scaffold.

How Spec Kit gets into a project (host only, nothing runs in the sandbox): the owner installs the specify CLI (uv tool install specify-cli --from git+https://github.com/github/spec-kit.git@<tag>, pinned to the project’s recorded speckit_version) and runs specify init --here --integration pi in a new project, or specify integration install pi in one already set up for another agent (pi is installable next to others). This writes the commands as prompt templates to .pi/prompts/speckit.*.md plus the .specify/ scaffold, committed with the project. Those project prompts load after pi’s project trust is granted for the folder. /speckit.taskstoissues needs GitHub MCP and is not supported here. The global prompts are deliberately not duplicated in agent/prompts/: they must match each project’s own .specify/ scripts and templates.

agent/extensions/#

Extensions loaded through the extensions setting (read-only mount). They run with the agent’s full access, so only code reviewed in this repo belongs here. Every file needs its own entry.

agent/extensions/rtk/ — RTK command-output compression#

agent/extensions/rtk/index.ts#

RTK’s own Pi extension (hooks/pi/rtk.ts from github.com/rtk-ai/rtk tag v1.0.1, unchanged; Apache-2.0 + BUSL-1.1, production use free). On every bash tool call it asks rtk rewrite for a compressed equivalent (git status → rtk git status) and swaps the command; no rewrite, a missing binary or any error passes the original command through (fail open). The rtk binary (same version) is baked into the image by pi-docker-sandbox (RTK_VERSION); the extension disables itself when it is missing. Set RTK_DISABLED=1 to switch it off. rtk gain shows the savings. Overlaps context-mode only partly: RTK shrinks bash output at the source, context-mode sandboxes large outputs. Added on the owner’s request on 2026-10-17.

agent/extensions/i-have-adhd/ — ADHD-friendly replies#

Vendored from github.com/ayghri/i-have-adhd commit 1ecea1993b7f (MIT; LICENSE alongside). It is a pi package upstream but not on npm, and packages accepts only exact npm versions, hence the copy. Injects a ten-rule reply style once per session (answer or next action first, numbered steps, lists capped at five, no preamble or recap). Local changes, two lines in index.ts, marked pi-config: the rules file is SKILL.md next to the extension, and the mode is on by default (upstream: off). /i-have-adhd [on|off] toggles it per session; “stop adhd mode” or “normal mode” turns it off; {"alwaysOn": false} in ~/.pi/agent/i-have-adhd.json restores the upstream default. Note it overlaps the “Communication” rules of the global AGENTS.md; if replies feel over-constrained, turn it off. Added on the owner’s request on 2026-10-17.

agent/extensions/i-have-adhd/index.ts#

Extension entry point: /i-have-adhd command, --adhd flag, status badge, injects the rules.

agent/extensions/i-have-adhd/context-compat.ts#

Checks whether the rules are still in the model’s context (survives compaction).

agent/extensions/i-have-adhd/SKILL.md#

The ruleset itself (canonical upstream skills/i-have-adhd/SKILL.md), read by index.ts.

agent/extensions/i-have-adhd/LICENSE#

Upstream MIT licence.

agent/extensions/atelier-models/ — MODELS sidebar panel#

Adds a MODELS panel to the pi-atelier sidebar: the model the router picked last with its tier (router claude-sonnet-5.5 (standard)) and one row per running background subagent with its model (scout gemini-3.5-flash-lite). The router publishes each pick on the event model-router:route; subagents come from subagent:async-started and subagent:async-complete, and their model from the run’s status.json under pi-subagents’ temp dir (PI_SUBAGENTS_TEMP_ROOT is honoured), polled every 2 s while a run is active. Foreground subagents are not listed (pi-subagents’ own FleetView shows them). Contributed panels start hidden: enable MODELS once in /atelier → Settings → Display. Without pi-atelier it does nothing.

agent/extensions/atelier-models/index.ts#

Extension entry point: collects router and subagent models and publishes the panel on pi-atelier’s pi-atelier:sidebar-panels channel.

agent/extensions/model-router/ — cost-aware router (spec 010)#

Registers the virtual model router/auto, the default entrypoint (defaultProvider = router, defaultModel = auto). It appears in /model and the footer shows the routed physical model (auto • medium → claude-sonnet-5.5 • medium).

  • Per user message: a cheap classifier model rates the request cheap, standard or hard; if it fails or takes longer than classifier.timeoutMs, a keyword/size heuristic decides. The classifier sees at most classifier.maxInputChars of the last user message (no files, no history) and replies with one word. Its usage is not added to the session cost (a few hundred tokens at the cheap tier).
  • Virtual thinking level biases the choice: low one tier cheaper, high one tier stronger, medium neutral. The physical thinking level comes from the tier.
  • Prompt-cache aware: tool follow-ups (continuation) stay on the model of the turn; a downgrade is suppressed while the context is above hysteresisTokens (switching models loses the cache); upgrades are always allowed. Each pick is also emitted as model-router:route ({tier, model}) for atelier-models.
  • Subscription first: every tier lists its models in preference order; the first usable one wins. The default lists a claude-bridge/* model (Claude subscription) before the pay-per-token model of the same tier.
  • Failures: a failed request (retry) first demotes the failing model when its tier has a later alternative (subscription out of quota, bridge not logged in): the router then stays on the alternative for the rest of the session (restart or /new to try the subscription again). Without an alternative it moves one tier up. Requests outside the agent loop (context compaction summaries) use the cheap tier and skip the providers in skipForDirect.
  • Unavailable models: if none of a tier’s models has credentials, the router tries the next stronger tier, then weaker ones; it errors only when none is available. If the extension cannot load, pi falls back to its automatic default model.

agent/extensions/model-router/tiers.json#

The only file to edit to retune. provider/id name the virtual model; tiers.<tier> is { "models": ["<provider>/<id>", …], "thinking": "<level>" } (preference order); classifier has model, timeoutMs, maxInputChars, maxOutputTokens; optional jev (provider, path, model, timeoutMs, maxInputChars, minConfidence) names a Jev classifier that is tried first (see below); skipForDirect lists providers not used for compaction summaries (claude-bridge: a Claude Code process per side query is slow); hysteresisTokens (20000) is the context size above which downgrades are suppressed; defaultTier is used when nothing else decides. Default tiers (list prices USD per 1M tokens, input / output / cache read):

TierModels (preference order)ThinkingFallback price
cheapclaude-bridge/claude-haiku-5-5, model-proxy/deepseek-v4.1-flashlow0.15 / 0.60 / 0.003
standardclaude-bridge/claude-sonnet-5-5, github-copilot/claude-sonnet-5.5medium2 / 10 / 0.2
hardclaude-bridge/claude-opus-5-5, github-copilot/claude-opus-5.5high4 / 20 / 0.2

Jev classifier. TypeSafe’s Jev is a fast decision model. For each new user message the router posts one choice question (cheap / standard / hard, same rubric as the chat classifier) to the model proxy proxy pass-through jev.path (/typesafe/v1/systemone, model jev-latest) and uses the answer when the chosen tier has probability ≥ minConfidence (0.5). The proxy holds TYPESAFE_API_KEY; the sandbox needs no TypeSafe credential. Auth is the existing model-proxy login (/login model-proxy): base URL and key are taken from any available model-proxy/* model. On any problem (no model-proxy login, pass-through missing, timeout timeoutMs, HTTP error, unsure answer) it falls through to the chat classifier below, then the heuristic. Requires a proxy ≥ 1.103 with TYPESAFE_API_KEY in its environment and a virtual key allowed to call /typesafe/*. TypeSafe’s benchmark reports ~127 ms median against ~690 ms for a Haiku classifier (their own 80-prompt set; measure on your traffic). The first 8000 characters of each new user message go to TypeSafe via the proxy (a further data processor). Set jev.path to /v1/systemone and jev.model to a model_list name to use the unified route (budgets, fallbacks) instead. Remove the jev block to switch it off.

The claude-bridge/* models cost nothing per token but use the Claude subscription quota (Pro limits are tight, especially for Opus; set provider.plan in claude-bridge.json on Max). The classifier stays on model-proxy/deepseek-v4.1-flash. Keep the non-bridge models in subagents.modelScope in sync (see settings.json → subagents); bridge models are not allowed there on purpose.

agent/extensions/model-router/index.ts#

Extension entry point: registers router/auto, loads tiers.json, resolves tier models against the registry, calls the classifier.

agent/extensions/model-router/route.ts#

Pure routing logic (heuristic rating, classifier-answer parsing, tier shifting, hysteresis, escalation, fallback order, model choice inside a tier, retry demotion, token estimate). No pi imports; unit-tested in tests/model-router/route.test.ts.

agent/extensions/model-router/classify.ts#

Classifier prompt and the classifyTier wrapper: truncation, timeout with abort, never throws. Unit-tested in tests/model-router/classify.test.ts.

Tests: node --test "tests/model-router/*.test.ts", also run by tests/model-router.bats (part of just check; skipped where Node is missing).