Codeman can drive Pi (@earendil-works/pi-coding-agent, MIT) as a
session backend, alongside Claude Code, OpenCode, Codex, Gemini and Antigravity.
pi is a sixth run mode: its own PTY, its own tmux session, its own tab colour
(rose). It is not a location overlay like Docker or remote-SSH cases, and it is not
a web tab.
Tracking issue: #206. The design
rationale behind each decision below lives in docs/pi-integration-plan.md.
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
# or
curl -fsSL https://pi.dev/install.sh | shBoth installers end up going through global npm, so either one uninstalls with
npm uninstall -g @earendil-works/pi-coding-agent.
Codeman finds the binary via which pi and then the usual global-bin locations
(~/.local/bin, /usr/local/bin, ~/.bun/bin, ~/.npm-global/bin, ~/bin).
pi is a short, generic name, so unlike the other CLI resolvers Codeman does
not trust a which hit on its own: it runs pi --version once and requires
semver-shaped output. Anything else is rejected as "not installed" and the
rejected path is logged. Check what it resolved:
curl -s localhost:3000/api/pi/status | jq
# { "available": true, "path": "/home/you/.local/bin", "version": "0.84.1" }That endpoint carries version on top of the shape the sibling /api/*/status
endpoints return, precisely so a misresolution is visible rather than presenting
as "the mode just doesn't work".
Pi supports 15+ providers. Two ways in:
- OAuth subscription login — run
/logininside a pi session. Six providers support it: ChatGPT Plus/Pro, Claude Pro/Max, GitHub Copilot, xAI, OpenRouter and Radius. Credentials land in~/.pi/agent/auth.jsonand pi refreshes them itself. OpenRouter's flow accepts a pasted redirect URL, which is what makes it workable over remote SSH. - API keys — exported in the environment of the Codeman server process.
envOverrides. Pi reads
about 34 provider variables (ANTHROPIC_API_KEY, OPENAI_API_KEY,
DEEPSEEK_API_KEY, HF_TOKEN, BASETEN_API_KEY, …) that share no common prefix.
Codeman's env allowlist is a single global list applied to every mode at once, so
admitting bare provider keys for pi would widen the allowlist for Claude, Codex,
Gemini and everything else too. Only the PI_* prefix was added, which covers
every documented pi input: PI_CODING_AGENT_DIR, PI_CODING_AGENT_SESSION_DIR,
PI_PACKAGE_DIR, PI_OFFLINE, PI_SKIP_VERSION_CHECK, PI_TELEMETRY,
PI_CACHE_RETENTION, PI_SHARE_VIEWER_URL, PI_HARDWARE_CURSOR,
PI_EXPERIMENTAL.
pi auth check verifies credentials before you start a long run.
Note if you authenticate with a Claude Pro/Max subscription: third-party harness usage bills as Anthropic "extra usage" per token rather than against plan limits.
PiConfig (per session, persisted in state.json, round-trips through respawn):
| Field | Flag | Notes |
|---|---|---|
model |
--model <v> |
Accepts provider/id and a :<thinking> suffix (sonnet:high) |
provider |
--provider <v> |
anthropic, openai, google, … |
thinking |
--thinking <v> |
off/minimal/low/medium/high/xhigh/max |
continueSession |
-c |
Skipped when resumeSessionId is set (the two conflict) |
resumeSessionId |
--session <v> |
Ids only, never paths |
approveProjectTrust |
--approve / --no-approve / nothing |
Tri-state, see below |
Every value is regex-validated and dropped (not escaped) if it fails, because
the result is interpolated into the pane's bash -c "…" command.
The Run button sends no PiConfig at all: pi has no permission prompts to
bypass, and project trust is a decision the person at the terminal makes.
--api-key. Never. It would put a provider secret on the spawn command line, visible inps, tmux server state and logs.PI_*overrides go through socket-scopedtmux setenvfor exactly this reason.--tui-mode. Pi's default main-screen TUI is the friendly case for a browser terminal. The fullscreen mode (0.84.0) stays your own runtime choice via/settings.--name,--no-session,-p/--print,--mode json,--mode rpc,--tools/--exclude-tools,-e/--extension,--skill,--system-prompt. Tracked as follow-ups in the plan doc.
Pi has no permission prompts and no sandbox. There is no
--dangerously-skip-permissions analog and none is needed: tools run with the
user's own permissions, always. A pi session can read, write and execute anything
the Codeman user can. If you need isolation, use a Docker case — that is the
isolation story, here as everywhere else in Codeman.
Pi's "project trust" prompt is not a safety boundary (upstream says so too).
It gates loading repo-local .pi/ config, extensions and skills, and
installing missing project packages. It only appears when the cwd or an ancestor
contains .pi/settings.json, .pi/extensions|skills|prompts|themes,
.pi/SYSTEM.md/.pi/APPEND_SYSTEM.md, or .agents/skills. A bare .pi/
directory does not trigger it.
approveProjectTrust: true answers it with --approve, which means pi loads
and executes repository-supplied TypeScript and runs an npm install for missing
project packages. Treat it exactly as seriously as that sounds.
Multi-user mode: for an owner without the privileged-command grant, Codeman
materializes approveProjectTrust: false so the pane launches with
--no-approve and the prompt never appears. Merely omitting --approve would
not be a clamp, since pi's own default is to ask and the session user could just
answer yes.
Also worth knowing: pi auth print-api-key / print-bearer-token and
pi auth check mean a pi session can print its own provider credentials by
design. Isolation is Docker.
Pi's editor uses Shift+Enter / Ctrl+Enter for newline-vs-submit. Without
extended keys, tmux collapses both into a plain \r. Upstream recommends:
set -g extended-keys on
set -g extended-keys-format csi-uextended-keys-format needs tmux 3.5+; on 3.2–3.4 extended-keys on alone works
(pi falls back to xterm modifyOtherKeys).
Codeman's browser input path sends \r for submit, so basic use works
unconfigured — what degrades is newline-in-editor, mostly when you attach to the
pane directly (sc).
tmux kill-server to take effect.
Never run tmux kill-server on Codeman's socket — it would kill every live
session, w1/w2/w3 included.
Measured (tmux 3.4, pi 0.84.1): no kill-server is needed. Setting the option
server-scoped on Codeman's own socket takes effect on the ALREADY-RUNNING server;
the next pi session starts without the warning. Existing sessions keep the old
setting until they respawn.
tmux -L codeman set -s extended-keys on
tmux -L codeman set -s extended-keys-format csi-u # tmux 3.5+ only, see below
tmux -L codeman show-options -s | grep extended # verifyOn tmux 3.4 and older, extended-keys-format does not exist and the second
line fails with invalid option: extended-keys-format. That is harmless — pi
falls back to xterm modifyOtherKeys and extended-keys on alone silences the
warning. Run the two lines independently rather than chained.
Pi tells you which state it is in: an unconfigured session prints
Warning: tmux extended-keys is off. Modified Enter keys may not work. in its
startup banner, so you can verify the change by starting a new pi session.
-L <socket> and -s, never -g on your default socket, and never
kill-server. Codeman does not set this for you: it is a server-wide tmux option
and silently changing key encoding for every session of every backend is not
Codeman's call to make.
On touch devices Codeman buffers typed characters in the LocalEchoOverlay and
flushes them to the PTY on Enter. Pi gets that 'buffer' policy, the same as
Claude, Gemini and OpenCode.
This was an explicit open question, because that policy is exactly what broke
Codex (issues #218/#219/#220/#222): Codex's composer reacts per keystroke, so
buffer-until-Enter starved it. Measured against pi 0.84.1: it does not
reproduce. Pi's slash-command picker re-filters on the whole composer content
rather than on per-keystroke deltas, so a one-shot flush of /set filters the
picker down to settings identically to typing it character by character, and
the delayed \r then selects it. Prose prompts flush and submit correctly too.
If a future pi release changes that, the cheap fallback is one 'off' branch in
_updateLocalEchoState (terminal-ui.js); teaching PredictiveEchoAddon pi's
composer row is the larger follow-up.
The agent image (docker/agent.Dockerfile) installs pi in its own RUN step with
--ignore-scripts, kept out of the shared npm block so the flag cannot change how
the other four CLIs install. Rebuild with:
node scripts/build-agent-image.mjs --no-cache # --no-cache is mandatoryCredentials are seeded, not shared: ~/.pi/agent/auth.json, settings.json,
trust.json, models.json and models-store.json are mounted read-only and
copied into the container's own ~/.pi/agent. So an in-container pi never writes
refreshed OAuth tokens back to the host, and docker commit exports stay
secret-free. models.json is in the list because it holds user-defined custom
providers, which would otherwise silently vanish inside containers.
Only those five files are seeded because ~/.pi/agent also holds sessions/,
extensions/, skills/ and the installed package trees (npm/, git/), which
on an active host is easily gigabytes.
Trade-off: in-container pi sessions are invisible host-side, so pi -c inside
a Docker case only sees that container's own history.
pi mode is routed through an interactive login shell
(exec "$SHELL" -i -l -c 'pi'), because sshd's remote-command PATH does not
include npm's global bin on most hosts. Per-session config and envOverrides do
not cross ssh and are rejected rather than silently ignored; use the per-host
command override instead.
- No idle/completion hook. Pi has no hook system Codeman can install into, so
idle detection falls back to output-stabilization like the other external CLIs.
Pi 0.84.0 shipped an
agent_settledextension event that is a genuine idle signal; a Codeman pi extension using it is the highest-value follow-up. - No response viewer. Pi writes JSONL v3 session files under
~/.pi/agent/sessions/; nothing reads them yet. - Cron jobs mis-detect readiness. The cron readiness poll looks for
❯or a token count, neither of which pi prints, so a pi cron job burns its poll budget and then sends the prompt anyway. It works; it is just slower to start. - Ralph, respawn heuristics, token/CLI-info parsing and the
❯readiness probe are off for pi, as for every external CLI.