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):
- writes this session’s stacked instructions to
/run/pi-session/AGENTS.mdand links~/.pi/agent/AGENTS.mdto it:agent/AGENTS.mdfirst, then the non-emptyAGENTS.mdof each level of the domain’s configuration store, nearest last (spec 008); - writes this session’s merged settings and MCP servers to
/run/pi-session/settings.jsonandmcp.json(tmpfs) and links~/.pi/agent/settings.jsonandmcp.jsonto them. The merge happens on the host:agent/settings.jsonandagent/mcp.jsonare 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-sandboxdocs/config.md); without a store the session gets exactly the files of this repo; - installs every pinned package that is missing or at another version
(
container/install-packages.sh), after copyingagent/npmrcto~/.pi/agent/npm/.npmrc; - 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:
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):{ "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 (
/sessionshows the decision).Fewer router switches:
hysteresisTokensis 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.Smaller worst case:
compactionabove.
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/). Theadvertise: trueagents appear in the parent prompt.defaultModel:github-copilot/claude-sonnet-5.5for any agent without its own model.agentOverrides: model and thinking level for the builtin roles.Agent Model Thinking 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 plusmodel-proxy/mimo-v2.5-pro(used bydocs-writer) and thegateway/…models ofscout,researcherandcommit-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 withtiers.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.jsonin thepi-agent-statevolume (same place and rules as the default model login; never in this repo). Select a model with/modelor-- --model model-proxy/<model-id>.- SSO: model proxy’s browser login with a temporary
127.0.0.1callback cannot work in the sandbox, because no ports are published to the host browser. Use the API key, the proxy’s/sso/cli/startverification-code flow, or paste a token from the model proxy UI. - Not used here for the default
model-proxyprovider: the env-var route (MODEL_PROXY_BASE_URL/MODEL_PROXY_API_KEY) and amodel-proxy.providers.model-proxyblock. Either would have to be declared in the sandbox (compose env,.env.example, Keychain) first. Thegatewayalias 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/generatewith"guardrails": ["jev-compaction"]) or per request. Useful for long agent sessions; checkx-model-proxy-applied-guardrailsand 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/decisionsand/v1/systemoneroutes and an Auto Router withclassifier_type: jevexist, 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
~/.claudein thepi-agent-statevolume (never in this repo). Runclaudewith the bundled binary (or/loginin 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): setprovider.planto"max"on a Max plan (default"pro"),askClaude.enabledtotrueto registerAskClaude(default off).strictMcpConfigstaystrueso the bridge does not load MCP servers from~/.claude.json. - Model scope:
subagents.modelScope.allowdoes not listclaude-bridge/*, so subagents cannot use it; only the entrypoint can.router/autoprefers it: the first model of every tier is aclaude-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.comandclaude.aibecome 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.
| Folder | Upstream (commit) | Licence |
|---|---|---|
agent/design-skills/impeccable | github.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-frontend | github.com/Leonxlnx/taste-skill, skills/taste-skill (12af471), without evals/; README renamed README.upstream.md | MIT |
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-utils | github.com/greensock/gsap-skills (aed9cfd); gsap-react and gsap-frameworks left out (no React here) | MIT (gsap-core/LICENSE) |
agent/design-skills/lenis | written here from the README of github.com/darkroomengineering/lenis v1.3.27 (e01eefb); upstream is a library, not a skill | MIT (upstream) |
agent/design-skills/react-bits | written here for github.com/DavidHDev/react-bits (e12c221); a catalogue and a porting guide to vanilla CSS/JS, no code copied | MIT + Commons Clause (upstream) |
agent/design-skills/awesome-design | written 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, plusindex.mdandlog.md) and follows the never-store rules. It never opens.env*, key files,auth.jsonor 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 <domain>and thenpi-sandbox memory-sync <domain>, 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
## Yoloblock in itsAGENTS.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
reviewersubagent 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 mostclassifier.maxInputCharsof 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:
lowone tier cheaper,highone tier stronger,mediumneutral. 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 abovehysteresisTokens(switching models loses the cache); upgrades are always allowed. Each pick is also emitted asmodel-router:route({tier, model}) foratelier-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/newto 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 inskipForDirect. - 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):
| Tier | Models (preference order) | Thinking | Fallback price |
|---|---|---|---|
| cheap | claude-bridge/claude-haiku-5-5, model-proxy/deepseek-v4.1-flash | low | 0.15 / 0.60 / 0.003 |
| standard | claude-bridge/claude-sonnet-5-5, github-copilot/claude-sonnet-5.5 | medium | 2 / 10 / 0.2 |
| hard | claude-bridge/claude-opus-5-5, github-copilot/claude-opus-5.5 | high | 4 / 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).