Files
Codeman/.changeset/ba4bc996.md
Codeman maintainer 86234db1ef docs(skill): document the per-CLI availability probes, and guard the family
`GET /api/pi/status` shipped undocumented in the agent skill, and only a human
reading the doc noticed. Turns out none of its five siblings were documented
either, so this adds the whole family in one place: spawning with a mode whose
CLI is absent fails with OPERATION_FAILED rather than falling back, which is
exactly what an agent picking a backend it did not choose needs to know. Pi's
extra `.data.version` is called out, since a false `available:false` there means
an unrelated `pi` is in front on PATH.

On whether the endpoint scanner should also check registered-to-documented:
measured, and NO for the general case. The skill documents 34 of 217 registered
endpoints deliberately (it is an agent guide, not an API reference), so a blanket
reverse check needs a 183-entry allowlist that would fail CI on unrelated route
work and get appended to mechanically, which is worse than the gap it closes.
Grouping by path shape does not save it either: the families that yields are
things like `DELETE /api/<any>/:id`, lumping cases, webviews and docker hosts
together, and it would not have caught this gap anyway (the family had zero
documented members).

What IS cheap is a family the schema can enumerate with no allowlist: the new
assertion derives the agent modes from the Zod enum and requires each one's
`/api/<mode>/status` to be documented, so a seventh backend fails here until it
is. The sibling scanner still proves the other direction, that nothing documented
is a 404. Both mutation-checked: dropping pi's probe fails the new guard, and
documenting a nonexistent probe fails the old one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 17:55:43 +02:00

4.6 KiB

aicodeman
aicodeman
minor

Add Pi (pi.dev) as a sixth CLI run mode (#206).

SessionMode gains 'pi', a first-class backend alongside Claude Code, OpenCode, Codex, Gemini and Antigravity: its own PTY, tmux session, rose tab identity, welcome button, run-mode entry, cron agentType, Docker and remote-SSH command defaults, and clone-repo Brain option.

  • New resolver src/utils/pi-cli-resolver.ts. Unlike the sibling resolvers it sanity-probes pi --version and requires semver-shaped output, because pi is a short generic name that a stray binary on $PATH can shadow; the rejected path is logged. GET /api/pi/status returns { available, path, version } so a misresolution is diagnosable.
  • PiConfig maps to --model (accepts provider/id and a :thinking suffix), --provider, --thinking, --session/-c, and the tri-state --approve / --no-approve. Every value is regex-allowlisted and dropped on failure. --api-key is deliberately never wired: it would put a provider secret on the spawn command line.
  • No bypass flag. Pi has no permission prompts and no sandbox, so there is no --dangerously-skip-permissions analog. Its privilege-shaped knob is approveProjectTrust, which makes pi load and execute repo-local .pi/extensions TypeScript and install missing project packages. clampExternalCliBypassForOwner() therefore puts pi in the materialize branch: a non-granted multi-user owner gets --no-approve even when no config was sent, because pi's own default is an interactive prompt the session user could answer themselves. The same materialization applies to cron-fired jobs (clampCronExternalCliConfigs), which carry no per-CLI config and would otherwise launch on pi's own default. Both helpers had no test coverage at all; they now do, for every CLI.
  • Env allowlist gains only the PI_* prefix. Pi's ~34 provider key vars share no prefix and ALLOWED_ENV_PREFIXES is one global list with no mode context, so admitting them would widen the allowlist for every mode at once. Users authenticate via pi's /login or the server process's own environment.
  • Pi stays out of isAltScreenStripMode(). Its default TUI renders into the main screen with terminal-owned scrollback and is mouse-aware, so it consumes \x1b[3J and the mouse DECSETs that the full strip removes, unlike an Ink TUI repainting in place. Note what exclusion does NOT do: pi is tmux-backed, so it still falls through to the narrow isMuxAltScreenOnlyStripMode() strip and its alt-screen toggles are dropped either way. Pi's runtime-switchable fullscreen TUI therefore paints into the main buffer, exactly like vim inside a tmux shell session.
  • Docker: pi installs in its own --ignore-scripts step so that flag cannot affect the other four CLIs, and its credentials are seeded per-file (auth.json, settings.json, trust.json, models.json, models-store.json) rather than whole-dir, since ~/.pi/agent also holds sessions, extensions and installed package trees.
  • Local echo: pi lands on the buffer overlay. Verified that codex's per-keystroke starvation does not reproduce — pi's slash picker re-filters on the whole composer content, so a one-shot flush behaves identically to per-keystroke typing.
  • Mode-list parity: pi is excluded from the Ralph tracker auto-enable on POST /api/sessions/:id/interactive (like every other external CLI, whose output the tracker never parses), carries a REMOTE_CLI_BIN entry so a remote-SSH pi session reports its CLI version, and gets its own badge in the desktop home rail instead of rendering like Claude. The packaged agent skill's mode enumerations list pi too, and it now documents the per-CLI availability probes (GET /api/<mode>/status) that agents should check before spawning a worker on a backend the server may not have installed. Both are pinned by a new guard that derives the mode set from the Zod schema instead of restating it.
  • codeman doctor and the run mode agree about pi. The registry entry resolved a bare which pi while pi-cli-resolver demanded semver output, so the Dependencies panel could report an installed Pi CLI that sessions refuse to launch. Both now share one exported regex, and the registry's new requireVersionMatch reports a non-semver pi as missing rather than installed. Only pi sets it; every other tool keeps its existing behaviour.
  • Installer detection, docs (docs/pi-integration.md), READMEs, and the architecture invariants are updated. Tests: test/pi-mode.test.ts and test/routes/external-cli-bypass-clamp.test.ts, plus extensions to the run-mode, mobile-overview, render-index-html, system-routes and local-echo suites.