pi-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pi-mcpspawn two workers to add a search endpoint, lock the acceptance test first"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
pi-coffee
Coordinate coding workers with reviewable acceptance evidence. pi-coffee is an MCP server that lets an orchestrator assign independent implementation to pi workers in isolated git worktrees, review full diffs, and verify the proposed integration before merging. Small fixes stay direct.
Any MCP client can drive it. Codex is the reference client, but Claude, Cursor, or anything else that speaks MCP works too. The client splits a job into well-defined pieces, hands each piece to an agent, reviews what comes back, and merges. The agents do the typing.
Two agents editing the same repository normally overwrite each other. Here each agent works in its own git worktree on its own branch, claims the files it is about to touch, and can message the other agents or ask the orchestrator a question when something is unclear.
What to measure
Delegate when independent work justifies specification, review, and integration overhead.
Keep the required model capability and output quality; a cheaper model must still meet them.
Include failed attempts, rework, and orchestrator review in any cost comparison.
pi_metricsreports usage and cost coverage. Partial token ratios do not prove savings.
Contents
Related MCP server: Vibechemy
Background
A single coding agent is easy to supervise. Several of them on one checkout is not: they save over each other's files, or produce long-lived branches that are painful to merge. The usual fixes (copying the repo, serializing everything) either waste the parallelism or waste your time.
pi-coffee takes the opposite approach. Give every agent its own worktree, make file ownership explicit, and keep one supervisor — Codex — responsible for the final result. That is the whole idea; the rest of this document is the mechanics.
What you get
One worktree and branch per worker. Edits cannot collide because the checkouts are separate.
Advisory file claims. A worker claims a path before writing to it. If another worker already holds a conflicting claim, the write is blocked with an explanation instead of silently racing.
A mailbox and a shared board. Workers can send each other durable messages, and post facts or decisions that everyone can read.
Blocking questions. A stuck worker can ask Codex a question through
coord_ask. It shows up inpi_wait/pi_statusand Codex answers withpi_answer.Acceptance tests written by Codex. Codex drops the test into the worktree and locks it before the worker starts. Digests are checked again at integration; locks alone are not a sandbox.
Requirement and existing-test review.
pi_reviewrecords each hard requirement and explicitly approves changed existing tests/configurations against the exact verified candidate.Candidate acceptance before merging.
pi_verifyruns the fixed acceptance command on the proposed worker + target result. Changed SHAs, failed tests, and out-of-scope edits block merging.Structured specs and a paper trail. Every spawn carries a goal and a scope; the daemon rejects overlapping scopes up front. When the task is done,
pi_reporttells you how much of the work landed on the first try.
Cost and delegation evidence
The default worker configuration is DeepSeek (PI_COFFEE_PROVIDER=deepseek,
PI_COFFEE_MODEL=deepseek-flash). Choose it only when it meets the task's documented quality floor;
model changes follow user approval and project rules. Small fixes and tightly coupled changes should
be completed directly. Workers are useful for clearly scoped, independent, objectively verifiable
work when the expected benefit exceeds coordination overhead.
One daemon serves every Codex chat configured to use it. Worker IDs are global, and the daemon does
not receive a trustworthy Codex chat ID. Each new pi_spawn therefore returns a one-time
control_key; the daemon stores only its hash and requires the key for worker writes, verification,
merge, finish, stop, and cleanup. Keep each key in its originating chat. The first spawn also returns
scope_key: pass it to later spawns in that chat to allow worker-to-worker messages and a shared
board. A new worker's internal token is limited to its own session and that scope, rather than the
daemon's master token. Keep both keys out of prompts, commits, reports, and user-facing output.
pi_status/pi_list show control_required; legacy workers show false and retain their old
behavior. Reads and global audits remain visible to daemon clients, and processes under the same OS
user can access local state; this boundary prevents cross-chat operations through the pi APIs, not
host-level access. Pass your IDs to pi_wait, pi_list, pi_report, and pi_metrics; an unfiltered
report is daemon-wide. Use scoped pi_gc for cleanup. Coordinate daemon restarts across chats: an
idle snapshot does not prevent a new worker from starting immediately afterward.
Check the daemon's live /mcp tools/list for pi_spawn.scope_key and pi_send.control_key.
An existing Codex chat can display cached older tool declarations even while the daemon has the new
schema; a scoped read-only pi_list(session_ids=...) call can confirm that new arguments reach it.
If the live daemon lacks the fields, source changes and an installed skill alone do not activate isolation.
flowchart LR
R[Requirement] --> O[Orchestrator<br/>scope / acceptance / decisions]
O --> W1[Independent worker 1]
O --> W2[Independent worker 2]
W1 --> V[Full diff review<br/>candidate acceptance]
W2 --> V
V --> M[Authorized integration]pi_metrics includes active and historical worker sessions, including failures and corrections.
Missing costs remain unknown. Select a task set with session_ids; use pi_record_cost to register
orchestrator cost evidence with its amount, currency, source (manual, estimate, or provider),
nonempty reference, and covered session IDs.
pi_report includes compact cost coverage alongside the delegation outcomes; pi_metrics returns
the complete per-session evidence. Manual and estimated records are labeled separately from
provider-reported costs.
spec.purpose declares implementation, review, or investigation; pi_report returns separate
workstreams_by_purpose counts and outcomes. Older or unclassified work stays unspecified.
The overall percentages include every purpose; they do not measure code contribution. Describe
actual worker changes and direct orchestrator work alongside them. See the authoritative
orchestration policy for pre-edit delegation assessment and takeover rules.
A worker-output / partial-instruction token ratio measures output distribution, not a savings rate or quality. Complete cost totals require known worker and orchestrator costs covering the same task set in a compatible currency. A savings claim additionally needs an equivalent baseline with the same acceptance, quality and scope. Include waiting, review, failed attempts, and rework. No real model quality or financial benefit is established by the offline tests.
How it works
flowchart LR
Codex[MCP client<br/>e.g. Codex] -- "stdio MCP" --> Proxy[local proxy]
Proxy -- "streamable HTTP MCP" --> Daemon[pi-coffee daemon<br/>registry · locks · mailbox · board]
Daemon -- "RPC JSONL" --> W1[pi worker 1<br/>worktree + branch]
Daemon -- "RPC JSONL" --> W2[pi worker 2<br/>worktree + branch]
Daemon -- "RPC JSONL" --> WN[pi worker N ...]
W1 -. "worker extension:<br/>claims, inbox polling, coord_* tools" .-> Daemon
W2 -. "worker extension" .-> Daemon
WN -. "worker extension" .-> DaemonThe daemon (a small Node process) exposes an MCP endpoint over HTTP and an internal HTTP API. When
Codex calls pi_spawn, the daemon creates a worktree, starts pi --mode rpc as a child process, and
injects a worker-side extension into it. That extension is what enforces file claims, polls the
mailbox, and provides the coord_* tools inside the worker.
Workers talk to the daemon over JSONL lines on stdin/stdout. For a local install, Codex uses a stdio proxy that reads the same configuration file as the daemon and forwards MCP requests over HTTP. The daemon runs the pinned local pi CLI instead of importing pi's internal modules.
Requirements
Node.js ≥ 22.19 | Runtime and test suite. CI covers 22.19 and 24. |
git | Worktrees, diffs, merges, branch cleanup. |
pi | Installed as a pinned local npm dependency. It can authenticate from an API key in the environment, so |
An MCP client | Codex is the reference client; any MCP-capable client works. |
Windows 10+ | Git for Windows with Git Bash, plus Node and Codex CLI on |
Run the daemon, Codex, and the workers as the same user. They need to share file ownership.
Provider credentials can come from the generated env file (see below) or from pi's own
~/.pi/agent/auth.json. Root is not required — any user with a working pi and write access to the
repository is fine.
Install and run
Grab the repository first if you have not already:
git clone https://github.com/lucasjingcn/pi-coffee.git
cd pi-coffeeThen:
npm run install:localThe installer also installs the pinned pi CLI independently under ~/.local/share/pi-cli,
registers pi in ~/.local/bin, and configures your shell PATH (Windows: user PATH).
Open a new terminal and run pi from any project. It keeps your current directory and
uses settings from ~/.pi-coffee/env; explicit provider/model options override those defaults.
To install or repair only this command without restarting the daemon, run npm run install:cli. The installer keeps a private Node runtime in the CLI prefix, so removing an installer cache does not break pi. On Unix, a supported Node on PATH takes precedence.
This one command works in a macOS/Linux shell or Windows PowerShell. It:
runs
npm installandnpm run build;installs the
pi-orchestratorskill for Codex and other Agent Skills clients (~/.agents/skills);registers the stdio proxy as the MCP server with Codex and the installed pi CLI (the proxy forwards to the HTTP daemon and reconnects on its own, so restarting the daemon does not break the session);
asks for your provider, API key, and model, and stores them in the user's
.pi-coffee/envfile;installs and starts a background daemon for the current user at login (LaunchAgent, systemd user service, or Windows Scheduled Task), then checks its health.
Because pi reads provider API keys from the environment, the credential setup means you do not have to
run /login inside pi. Adjust it later with:
npm run setup # provider, API key, model, thinking level (writes ~/.pi-coffee/env)
npm run doctor # preflight: node, git, pi, credentials, data directoryFor foreground debugging, stop the background daemon and run npm run start. Both modes load the
same env file through scripts/start.mjs; point it elsewhere with PI_COFFEE_ENV_FILE.
Check that it came up:
node scripts/check-health.mjsRestart Codex. It connects, picks up the orchestration instructions, loads the pi-orchestrator
skill, and the pi_* tools appear. A new pi session gets the same skill from ~/.agents/skills
and the pi_* tools from the MCP server the installer registered there; other Agent Skills clients
load the skill after a reload or a new session. On Windows the background process starts when this
user signs in; it does not run before login.
Pointing Codex at the daemon
npm run install:local requires codex on your PATH and registers the proxy. For manual repair,
add the server in ~/.codex/config.toml and restart Codex. The local proxy reads the setup file:
[mcp_servers.pi]
command = "node" # an absolute path to node is recommended
args = ["/absolute/path/to/pi-coffee/scripts/proxy.mjs"]For pi, the installer registers the same server; repair or add it manually with:
pi mcp add pi --description "pi-coffee: spawn, review, verify, and merge isolated pi coding workers" \
-- /absolute/path/to/node /absolute/path/to/pi-coffee/scripts/proxy.mjsClient per-tool timeout
pi_verify and pi_exec run the fixed acceptance command for up to 10 minutes, and pi_wait blocks
for its whole window. Most MCP clients cap each tool call at 60 seconds by default, which cuts such a
call off with a transport error. Raise that limit once per client:
[mcp_servers.pi]
# ...command/args as shown above...
tool_timeout_sec = 600// ~/.pi/agent/mcp.json
{ "mcpServers": { "pi": { "timeout": 600 } } }A client timeout stops nothing: the daemon keeps the wait, the worker keeps working, and no
acceptance result is invalidated. Treat it as "the caller stopped listening", not as a worker
failure, then read the real state with pi_status / pi_list. pi_wait already defaults to a
30-second window, so a 60-second client cap is enough for polling; the raised limit matters for
pi_verify and pi_exec on slow suites. Neither CLI can set this for you (codex mcp add and
pi mcp add have no timeout option), so edit the client configuration directly.
If the daemon runs on another machine, connect to it directly and pass a token:
[mcp_servers.pi]
url = "http://daemon-host:8787/mcp"
[mcp_servers.pi.env]
PI_COFFEE_TOKEN = "your-secret"Docker
The included Dockerfile bundles Node, git, and pi, so the only host requirement is Docker.
cp .env.example .env # set your provider API key and model
mkdir -p workspace # put or clone the repository workers should edit here
docker compose up -d --build
curl http://127.0.0.1:8787/internal/health./workspace is mounted at /workspace and is the default repo; a named volume holds /data
(daemon state). The container reads the same PI_COFFEE_* variables and provider key from .env.
To connect Codex, point it at the container in ~/.codex/config.toml:
[mcp_servers.pi]
url = "http://127.0.0.1:8787/mcp"
[mcp_servers.pi.env]
PI_COFFEE_TOKEN = "optional-shared-secret"Set PI_COFFEE_TOKEN in .env too if you want authentication. The port is published on loopback by
default; expose it on 0.0.0.0 only together with a token.
Without Compose:
docker build -t pi-coffee .
docker run --rm -p 127.0.0.1:8787:8787 \
-e PI_COFFEE_PROVIDER=deepseek -e PI_COFFEE_MODEL=deepseek-flash \
-e DEEPSEEK_API_KEY=sk-... \
-v "$PWD/workspace:/workspace" -v pi-coffee-data:/data \
pi-coffeeDay-to-day operations:
docker compose logs -f # follow daemon logs
docker compose build --pull && docker compose up -d # upgrade
docker compose down # stop (state volume is kept)
docker compose down -v # stop and drop daemon stateA task, start to finish
First follow the user's instructions and the target repository's AGENTS.md. Delegate only when
there is a clear expected benefit; a one-line repair is direct work. For ai-gen, its own size gate
also requires at least three independent tasks before considering parallel sub-agents; other
repositories retain their own rules.
Derive acceptance from the requirement. Call
pi_spawnwith aspec(goal,scope,purpose) and a fixedacceptance_command. Add independent tests throughacceptance_fileswhen needed; these are written before the worker starts and their digests are recorded.The worker edits within scope and runs tests. Questions go through
coord_ask; conflicting claims require coordination. The report includes files, commands, results, and unresolved issues.Wait for the worker to settle and review the full
pi_diff. Commit the reviewed changes under the repository's authorization rules; verification requires a clean, settled worker branch.Call
pi_verify. It builds an isolated candidate from the exact worker and target commits and runs the registered acceptance command. Inspect exit code, timeout, output, SHAs, and tree evidence.When requirements or existing validation changes need review, call
pi_reviewwith the exact verification ID and complete requirement/file verdicts as described below.pi_mergerequires current passing evidence and the necessary candidate review. Changed SHAs, acceptance files, scope violations, dirty target checkouts, and failed or timed-out acceptance block integration. Reverify a changed candidate. After a daemon restart, historical evidence does not restore permission to merge.Close with
pi_finish, which stops the worker, then inspectpi_report/pi_metrics. A successful outcome needs passing acceptance and, if code changed, an integration record. Checkinconsistent_outcomesbefore citing recorded successes. Clean up withpi_gcwhen authorized.
Candidate review: declare hard requirements in spec.requirements: [{id,text}] and project-specific
validation definitions in spec.validation_paths (for example package.json or CI scripts).
After pi_verify, if requirements were declared or existingValidationChanges is nonempty, call
pi_review with its exact id as verification_id, one {id,met:true,evidence} per requirement,
and one {path,approved:true,reason} in test_changes per modified existing validation file.
All conventional test edits, deletions and renames require review, including append-only edits;
new test files alone do not. Missing, duplicate, unknown, negative or empty verdicts block approval.
Review the original tests and full diff: structured evidence is an orchestrator judgment, not an
automatic semantic proof. Reverification or source/target/contract changes invalidate approval.
Coordinator acceptance files remain immutable. pi_merge and successful implementation outcomes
require this review in addition to the existing candidate verification. Read-only reviews retain
their separate unchanged-worktree acceptance path. Protected workers receive explicitly addressed
messages and exclude global broadcasts, including old persisted broadcasts.
pi_exec remains a diagnostic command runner; its passing result is not a merge permit. The
orchestrator owns review and final judgment. A worker gets the original task plus one correction;
a third instruction requires a reasoned override. Model changes require applicable user approval.
Commit, push, deployment and paid provider calls each follow user and repository authorization.
Missing credentials block actual model calls; independently authorized local work can continue.
Deployment
The single install command above configures the right background startup for the host. For maintenance:
Logs
The daemon writes every diagnostic line to stderr with an ISO-8601 UTC timestamp. Where that lands depends on the supervisor:
Install | Where the log goes | Rotation |
macOS (launchd) |
| yes: archived to |
Linux (systemd) | the journal: | journald keeps its own limits |
Windows (scheduled task) |
| no: the task pipes it through |
Docker / |
| the collector's own limits |
tail -f ~/.pi-coffee/logs/daemon.err.log # macOS
grep -iE "error|fail|refus|timeout|unreach" ~/.pi-coffee/logs/daemon.err.log
journalctl --user -u pi-coffee -f # Linux
docker compose logs -f # DockerRotation is copytruncate: launchd opens the log before the daemon starts and keeps that descriptor,
so renaming the file would send later lines into the archive. It only touches a log this process
itself writes to, which makes it a no-op under systemd, docker and foreground runs. daemon-side errors
(mcp error:, internal error:, [state] ... failed, reclaimed N stale credential(s)) land here.
Other layers keep their own record: pi appends MCP server logging notifications to
~/.pi/agent/mcp.log, /mcp (or pi mcp list) shows connection errors plus the tail of the
server's stderr, and each worker's full transcript is the pi session log under
PI_COFFEE_DATA_DIR/sessions/<id>/*.jsonl — read the live view with pi_tail or
pi_status({detail:"full"}). node scripts/status.mjs prints the current workers and locks.
Linux (systemd user service)
systemctl --user status pi-coffee
systemctl --user stop pi-coffeemacOS (launchd)
launchctl print gui/$(id -u)/com.picoffee.daemonLogs go to ~/.pi-coffee/logs/daemon.{out,err}.log. Stop the agent with
launchctl bootout gui/$(id -u)/com.picoffee.daemon. The launch agent sets a PATH that includes
Homebrew and ~/.pi/agent/bin so pi and git resolve.
Windows 10+ (current-user login task)
In PowerShell, inspect it with Get-ScheduledTask -TaskName pi-coffee; stop it with
Stop-ScheduledTask -TaskName pi-coffee. Logs are in $HOME\.pi-coffee\logs\daemon.log.
The task runs only after this user logs in. Git for Windows supplies Bash for worker acceptance commands.
To keep everything on one Mac (useful when the repository lives there):
rsync -a --exclude node_modules --exclude dist ./ mac:~/pi-coffee/
# then, on the Mac:
cd ~/pi-coffee && npm run install:localDaemon on another machine
If the repository and the pi credentials live on one host and you only run Codex elsewhere, bind
the daemon to the LAN or VPN and set a shared token:
PI_COFFEE_HOST=0.0.0.0 PI_COFFEE_TOKEN=<secret> ./run.shOn the Codex machine:
PI_COFFEE_TOKEN=<secret> codex mcp add pi \
--url http://<daemon-host>:8787/mcp --bearer-token-env-var PI_COFFEE_TOKENpi_diff, pi_commit, pi_merge, and pi_push let Codex review and integrate code it cannot
reach directly. Do this only on a network you trust; the default bind is loopback.
Tool reference
Tool | What it does |
| Create a worktree and branch, then start a worker. Returns its one-time |
| Send an instruction: |
| Wait for sessions to settle, a question, or a new handoff notice. Defaults to a 30-second window; expiry does not stop workers. Re-poll across calls below the outer deadline; explicit windows up to 120 seconds require a compatible client. Returns concise snapshots; |
| Concise state and pending questions by default, including |
| Read the transcript incrementally by passing the previous |
| Committed, uncommitted, and untracked changes for a worker branch. |
| Stage and commit everything in a worker's worktree. |
| Run the fixed acceptance command against the exact worker + target candidate; record exit code, timeout, output and commit/tree evidence. Failed, stale or restarted evidence cannot authorize integration. |
| Record one evidence verdict per requirement and one approval reason per modified existing validation file, bound to the exact verification ID. Incomplete or stale reviews cannot authorize integration. |
| Merge only with current passing candidate evidence, valid scope/digests, and clean settled worker and target checkouts. A changed source or target requires reverification. |
| Push the current branch (or an explicit one) to a remote. |
| Run a non-login shell command in a worker worktree for diagnostics. This does not create merge evidence. |
| Answer a worker's pending question with |
| Claim paths by hand, release them, or list what is held. |
| Send durable mail to a worker (optionally injecting it into the conversation) and read it back. |
| Pass |
| Stop an unfinished worker early, optionally removing its worktree and branch. |
| Restart a stopped, unfinished worker on its own transcript instead of re-dispatching: keeps its worktree, branch, spec, acceptance record, control key and its provider/model/thinking level, and clears the verification/review evidence of the interrupted run. Refuses sessions that already have an outcome, whose evidence was reclaimed, have an unconfirmed shutdown, or have lost their transcript, worker agent directory, worktree or control credential. Pass |
| Record |
| Concise outcome counts grouped by purpose, per-task notes, cost coverage, and |
| Reclaim selected stopped work with |
| Usage and cost evidence for active/historical sessions, optionally filtered by |
| Register orchestrator cost evidence with |
Worker-side tools
Each daemon-spawned pi session also gets these tools from the worker extension:
coord_ask, coord_send, coord_inbox, coord_claim, coord_release,
coord_board_post, coord_board_read, coord_status.
How file claims work
The extension blocks edit, write, and bash when coordination is unavailable, invalid, or a
required claim is denied. Reconnect and retry after resolving the issue. Recognized writes outside
the worktree, including resolved symlink targets, are refused. Before a worker writes, it claims the path. On edit and write that is the target file. On bash
it scans the command for literal redirect, tee, and sed -i targets and claims those. If a claim
conflicts with another worker's, the tool call is blocked and the worker is told who holds it, so it
can coordinate instead of trampling the other change.
Claims are namespaced by the repository's canonical git common directory, combined with a
repo-relative path. Two checkouts of the same repository — including symlink aliases and linked
worktrees — share a namespace, while unrelated repositories never collide even if their file names
match. Absolute paths inside a worker's worktree are normalized to the same relative key before they
are hashed. pi_claim and pi_release can target a specific repo; if you leave it out, the daemon
default is used, while worker sessions always use their own repository.
The locks are coordination protection, not a security sandbox. They cover recognizable literal targets; dynamic scripts, variables, command substitution and globs are not fully contained. Digest and scope checks block invalid results from integration but cannot undo arbitrary script writes. Untrusted workers require separate OS/container isolation, credential restrictions, and appropriate read-only acceptance mounts; that isolation is not provided by this release.
Cleaning up finished work
A stopped worker keeps everything resume needs: pi_resume restarts it on the same transcript,
worktree, branch, spec and control key. That is why reclamation is narrow. pi_gc, pi_stop, and the
sweeper only remove:
the worktree of a finished worker after
PI_COFFEE_WORKTREE_TTL_MIN, or of a clean unfinished worker afterPI_COFFEE_WORKTREE_FINAL_TTL_MIN(default 7 days); dirty trees are kept;the transcript (
PI_COFFEE_DATA_DIR/sessions/<id>) of a stopped and finished session;branches only when asked for:
pi_stopwithdelete_branch(orPI_COFFEE_DELETE_BRANCHES=1), andpi_gcfor tips it proves merged into the target.
A session's work is beyond recovery once its worktree is gone and it either already has an outcome,
or its worker branch is gone (no committed work left to resurrect). Then the daemon reclaims what it
still holds: the transcript directory under PI_COFFEE_DATA_DIR/sessions/<id>, its control credential,
and the bulk in its history entry — acceptance output and the final message keep a tail, contract
free-text and acceptance hashes are dropped, while the review record, outcome, purpose, cost and token
counters stay, so pi_report and pi_metrics read the same numbers. An unrecorded session with a live
branch is kept, because the branch could still be checked out to continue it. pi_gc reports this as
transcripts_reclaimed, credentials_reclaimed and history_compacted; the sweeper does the same work
under PI_COFFEE_AUTO_CLEAN.
Never reclaimed automatically: branches with new commits, the transcript and credential of any session whose worktree or live worker branch is still there (i.e. anything that could still be resumed or checked out), and the daemon's own bookkeeping (the mailbox, the board and the locks). Sessions whose work is beyond recovery are no longer resumable — dispatch a fresh one instead.
Scoped pi_gc acts only on the supplied session IDs. It stops and evicts selected finished workers
and removes clean accepted worktrees, including those left in history by a restart. An abandoned
worktree is also removed when its branch still points at its dispatch base and its files are clean;
unrecorded work, dirty files, and abandoned branches with new commits remain available. Then gc
deletes eligible branches whose integration is proven.
A branch is only deleted when its tip is an ancestor of the repository's current HEAD. Work
integrated by squash or rebase is not an ancestor, so it is left alone for you to inspect — gc never
force-deletes. Candidates come from persisted metadata, so branches whose worktree is already gone
(for example, after a daemon restart) are still eligible. Branches are deduplicated per repo and
branch name.
Nothing is deleted while it is a current or default branch, checked out in any worktree, attached to
an active session, or attached to an existing dirty worktree. Unfinished work and abandoned work
with new commits are kept; an empty abandoned branch can be removed only when no other session
shares it and ancestry proves it safe. These rules apply no matter how PI_COFFEE_DELETE_BRANCHES is set.
The result reports branches_deleted and a branches_retained entry with a reason for every branch
it kept. If git fails on a branch, it is retained and reported, never counted as deleted for you.
Configuration
Everything is configured through environment variables.
Variable | Default | Meaning |
|
| Bind address. Loopback by default. |
|
| Port for |
| pinned local pi CLI with | Explicit pi CLI override. |
| detected Bash | Bash executable for acceptance commands; on Windows, Git for Windows supplies it. |
| (none) | Default repository for |
|
| Where worktrees are created. |
|
| Worker provider and model. |
| (empty) | Model for an explicit manual upgrade. Empty means no upgrade is available. |
|
| pi thinking level: |
|
| Hard cap on concurrent workers. |
|
| Warn in |
|
| Base ref for new worktrees. |
|
| Remove finished workers' worktrees automatically (branches are kept). |
|
| Minutes a finished, idle worker is kept before the sweeper cleans it. |
|
| Default for the |
| (empty) | Comma-separated glob allowlist of global plugins a worker may inherit: npm/git package sources, external extension entries, and MCP server names. Empty means none; |
| (none) | Optional shared secret for |
|
| Daemon state: locks, mailbox, board, session metadata. |
|
| Archive a daemon log at the next start once it exceeds this size. |
|
| Rotated archives kept per log ( |
|
| File that |
For unattended background installation, set PI_COFFEE_SKIP_SETUP=1 and provide credentials through
the private env file or pi auth.json. Transient shell credentials are not inherited by background
services; doctor --background checks persisted credentials before startup is installed.
Bad values stop startup rather than limping along. Ports must be integers from 1 to 65535, session
caps and warning thresholds must be positive safe integers, and the TTL must be finite and at least
one minute (fractional minutes are fine). Numbers use decimal notation, and booleans accept only 0
or 1. Programmatic overrides passed to loadConfig win over both env vars and defaults. The log
rotation knobs are the one exception: an unreadable value falls back to its default, so a typo can
never stop the daemon from starting.
State and recovery
Each worker snapshots the global pi agent configuration into a private sessions/<id>/agent
directory (0700; copied files 0600). Settings and credentials are read with pi's lock protocol and
bounded asynchronous retries. Worker settings/auth/model files are independent; skill, prompt, and
theme directories remain linked, and relative extension/skill/prompt/theme/package paths retain
their original targets. Global plugins are inherited only when PI_COFFEE_WORKER_PLUGINS allows
them: packages, external extension entries, and MCP servers are denied by default, so an
interactive-only extension cannot change worker behavior or let a worker spawn nested sessions.
Workers also start with --no-skills, so globally installed skills (including orchestrator
policies) cannot steer them; the task contract comes from the spec and the copied AGENTS.md.
Core builtin: extensions, credentials, and context are never filtered for speed. Changes to
global defaults or login credentials apply to newly spawned workers. A configuration-load error
fails startup explicitly instead of accepting pi's fallback defaults. /internal/health identifies
this policy as private-agent-config-v1 so an old running daemon can be distinguished from a
rebuilt one.
Daemon state lives in state.json inside PI_COFFEE_DATA_DIR. Writes are serialized and atomic: a
unique temp file is renamed into place, and the previous validated snapshot is kept as
state.json.bak.
On startup the whole snapshot is validated before any of it is applied. If the primary file is missing or corrupt, a valid backup is used and a warning goes to stderr. If both are bad, or the filesystem cannot be read, startup fails instead of quietly starting from scratch. Save failures are logged; a failed flush during shutdown exits with a non-zero status.
Completed verification is retained as history; daemon restart invalidates its merge permission. Reverify the current candidate before integrating. Cost records remain part of the historical task evidence.
The backup can lag the primary by one save, and there is no power-loss durability or multi-daemon write locking. Run one daemon per state directory.
Security
The daemon binds to loopback and has no authentication by default. Setting
PI_COFFEE_TOKENenables it: the same secret must be presented on/mcpand/internal/*, either asx-pi-coord-tokenorAuthorization: Bearer <token>.If you expose the daemon beyond loopback, use the token and a trusted network or VPN. A token on a shared network is not a substitute for network controls.
File claims are a coordination mechanism, not a sandbox. Treat a worker as able to run arbitrary code in its worktree and plan accordingly.
Troubleshooting
Symptom | Likely cause | What to try |
| Token mismatch between daemon and client | Set the same |
| No default repository configured | Set |
A worker starts and immediately errors out | Provider credentials are missing or invalid | Run |
Acceptance commands fail on Windows | Git Bash is unavailable | Install Git for Windows and rerun |
| The daemon user differs from the repo owner | The daemon already passes |
Sessions show | Shutdown confirmed or survivor ownership unresolved | Inspect handoff; respawn only after safe transfer. Transcript files are preserved. |
The daemon exits with | The port is taken | Set |
|
| Register the server manually in |
| The client's per-tool timeout is shorter than the call | Raise it ( |
A worker is blocked by a file claim | Another worker holds a conflicting claim | Use |
Development
npm ci # install (Node >= 22.19)
npm run build # compile TypeScript to dist/
npm run setup # write provider credentials to ~/.pi-coffee/env
npm run doctor # check node/git/pi/credentials/data directory
npm run dev # run the daemon from source with tsx
npm run typecheck # main sources
npm run typecheck:extensions # extension against the real pi API types
npm test # build, then run the offline test suite
npm run verify # typecheck + typecheck:extensions + offline tests
node scripts/sync-playbook.mjs --check # after build: canonical skill/runtime/installer consistency
./smoke.sh # full end-to-end smoke (a couple of tiny live model calls)
SMOKE_LIVE=0 ./smoke.sh # deterministic smoke only, no model callsnpm test compiles the sources and runs everything under tests/. The offline smoke test builds a
throwaway git repository and daemon and drives the whole control surface — spec validation, the
task-type gate, scope overlap, test-first acceptance, diffing, model overrides, the two-strikes
gate, finishing and reporting, and restart persistence — against a fake pi that speaks JSONL, so
it needs no credentials and no network.
GitHub Actions runs npm ci and npm run verify on Linux and macOS with Node 22.19.0 and 24.
A separate Windows job builds, type-checks, starts the daemon with an isolated setup file, and parses
the PowerShell installers. Check the actual run for remote results; workflow configuration alone is
not evidence of a passing Windows installation. The pinned pi dependency is also the default local
worker CLI. CI does not log in to or call a model provider.
The orchestration policy lives only in codex/pi-orchestrator/SKILL.md: the runtime prompt loads
its body and the installer copies the same file into the Codex skill directory and the shared
~/.agents/skills location. Keep codex/ with src/ and dist/ in deployments.
Rerunning npm run install:local after workers finish updates the installed skill and background
startup; restart Codex (or reload the other client) to load the updated policy.
Repository layout
src/ daemon, MCP server, git plumbing, locks, state store
extensions/ worker-side pi extension (claims, inbox, coord_* tools)
tests/ offline test suite (node --test)
scripts/ setup, startup, health, smoke, and status helpers
deploy/ systemd, launchd, and Windows task installers
codex/ orchestrator skill installed by npm run install:local (Codex + ~/.agents/skills)
Dockerfile image with Node, git, and pi bundled
docker-compose.yml host-facing compose file
.env.example provider key / model template for DockerUpgrade and uninstall
After active workers finish, stop the background daemon with the command in Deployment, then update the checkout and rerun the same installer (macOS/Linux shell or Windows PowerShell):
git pull
npm run install:localDocker:
docker compose build --pull && docker compose up -dTo remove the background startup, use the command for your platform:
systemctl --user disable --now pi-coffee # Linux
rm ~/.config/systemd/user/pi-coffee.service
systemctl --user daemon-reload
launchctl bootout gui/$(id -u)/com.picoffee.daemon # macOS
rm ~/Library/LaunchAgents/com.picoffee.daemon.plistStop-ScheduledTask -TaskName pi-coffee # Windows
Unregister-ScheduledTask -TaskName pi-coffee -Confirm:$false # WindowsThen run codex mcp remove pi. Removing the installed skill and ~/.pi-coffee is optional;
the latter deletes local credentials and daemon state, so preserve anything you still need.
Known limitations
Workers do not survive a daemon restart. Shutting the daemon down stops them, and they are not brought back automatically. Their session files are kept, but you spawn new sessions.
File claims are advisory and only cover literal, recognizable targets.
The git worktree model assumes one worker per branch. Forcing the same branch into two worktrees is not supported.
License
Apache License 2.0. See LICENSE.
Copyright 2026 lucasjing.
pi (@earendil-works/pi-coding-agent) is a separate MIT-licensed program by Mario Zechner. This
repository invokes it as an external tool rather than redistributing its code; the Docker image
installs it from npm at build time. pi is not affiliated with, and does not endorse, this project.
Failure notices and safe handoff
The daemon watchdog warns after 60 seconds without observable generation progress and stops a worker
at the 10-minute silence deadline. Provider failures after retries settle and unexpected worker exits
also produce a durable handoff notice. Local tools and pending questions are excluded. Model quality,
model selection and user authorization remain unchanged; no extra paid retry is launched.
pi_wait defaults to a 30-second window. timedOut:true and its wait_hint describe wait-window
expiry, not worker failure; the wait does not stop workers. Inspect statuses and handoff notices,
then continue waiting for active workers. Keep each window below the script/client/transport deadline
with headroom. With a 60-second outer deadline, use timeout_ms:30000 and one blocking wait per
codemode script, then re-poll in a new codemode call rather than looping inside one script.
@options timeout_ms cannot override an external deadline. Explicit windows up to 120 seconds
remain available for compatible clients. After a transport timeout, inspect scoped pi_status /
pi_list snapshots instead of blindly stopping or re-spawning workers.
A new notice wakes pi_wait, and status/list responses include it. Pass consumed after_notice_ids
to avoid replaying a warning. Stateless MCP cannot wake a chat that is no longer executing; its owner
must keep a scoped wait outstanding or read the persisted notice on return. provider_wait does not
prove a provider-wide outage. Only take over when both safeToTakeOver and locksReleased are true.
stopping is not a confirmed exit. Failed shutdown retains locks and blocks writes. POSIX owns a worker
process group; Windows uses native tree termination. Processes escaping that group/tree are outside
this guarantee; advisory coordination is not an OS sandbox.
Delivered reports trigger awaiting_acceptance; review and finish read-only work promptly, and use the
existing verification/integration gates for implementation. An idle worker without pending questions
stops after 30 minutes with owner_timeout, preserving worktree, branch and report without claiming
success. Configure PI_COFFEE_WORKER_WARN_MS (60000), PI_COFFEE_WORKER_STALL_MS (600000), and
PI_COFFEE_WORKER_IDLE_MS (1800000); stall deadline must exceed warning deadline.
New worker credentials persist separately in ~/.pi-coffee/control-credentials/ (directory0700,
files0600), never in status/history. pi_recover_control requires the matching scope key. If both
chat capabilities were lost, authorized local administration can use
node scripts/worker-control.mjs s<N> (metadata only), or --stop (preserve files and branch).
--show-key explicitly reveals sensitive credentials; never share them. Pre-upgrade workers with
already-lost credentials have no automatic recovery or keyless MCP bypass.
On restart, new worker PID/start-time/group ownership is checked before stopping survivors and releasing reservations. Missing/mismatched identity or orphan tools retain scope and acceptance locks. Windows crash-orphan recovery retains locks for local administration. Legacy sessions lack this new ownership proof.
This server cannot be deployed
Maintenance
Related MCP Connectors
Coordinate coding agents through MCP using existing AI plans, saved work, and independent checks.
Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.
Agent-native collaboration network: orchestrate a team of long-running agents from any MCP client.
AI work orchestration for plans, tasks, teams, and coding-agent dispatch.
Related MCP Servers
- AlicenseAqualityCmaintenanceDelegate coding tasks to external AI coding agents in isolated git worktrees with independent verification, enabling any MCP client to orchestrate multi-agent workflows.5MIT
- AlicenseNot gradedqualityAmaintenanceAn MCP server for orchestrating a fleet of CLI coding agents in isolated git worktrees. It exposes tools for spawning workers, sending instructions, reviewing diffs, and merging changes, with full terminal visibility.3 npm4MIT
- AlicenseAqualityAmaintenanceLocal-first multi-agent delegation and approval control for Codex via MCP, with persistent task DAG, isolated worktrees, and a web console.141MIT
- AlicenseNot gradedqualityBmaintenanceCoordinates multiple AI coding agents by providing a shared task board, memory, and advisory file claims, letting agents work on the same repo concurrently without duplicating work or overwriting each other.7 npm1MIT