Orientation for AI agents (Claude, Codex, Cursor) working in this repo. Read this first.
cc-multi-cli-plugin is a Claude Code plugin that offloads heavy coding work to external CLIs — Codex, Cursor (headless agent -p), Antigravity, and OpenCode (headless opencode run --format json) — so the orchestrating Claude session spends as few tokens as possible. The plugin's entire value is token reduction by delegation. Keep that goal central to every change.
Claude does as little thinking as possible; the external CLI does the work.
- The per-command subagents (
plugins/multi/agents/codex-*.md) are thin forwarders: they build exactly one companion command, run it, and return stdout unchanged. They must not read files, reason about the task, or "help." - Forwarder models are tuned by role. Forwarders that frame/route the prompt —
codex-execute,codex-rescue, and the cursor/antigravity ones — run on Sonnet, because shaping the task well (model/effort choice, prompt framing) materially improves what the external CLI then produces (this mirrors the officialcodex-plugin-ccrescue subagent). Forwarders that do no framing and only bridge to the companion —codex-review— run on Haiku, the correct cheapest model for a pure forwarder. Keep them all thin either way: never add file reading or task reasoning. - Do not spawn fleets of Claude subagents to do or validate work in this repo — that defeats the plugin's purpose. Validate with
npm test(offline) or by delegating to a CLI.
/codex:<cmd> → multi:codex-<role> (forwarder, Haiku) → multi-cli-companion.mjs <subcommand> --cli <name>
→ lib/adapters/<name>.mjs → (codex app-server broker | cursor headless `agent -p` | antigravity headless `agy -p` | opencode headless `opencode run --format json`) → external CLI
See ARCHITECTURE.md for the full picture and the job / state / broker model.
No dependencies; uses Node's built-in test runner (Node ≥ 20; repo runs on 25).
npm test— fast, offline unit tests. Run this to self-verify any change. No CLI calls, no tokens.npm run test:live— end-to-end; spawns real Codex (costs tokens + time). Run only when touching the live path.
Definition of done for a change: npm test passes, no DEP0190 warnings, and CHANGELOG.md updated for user-facing changes.
plugins/multi/— the hub plugin:agents/(forwarders),commands/(slash cmds),skills/(forwarder contracts),schemas/,prompts/,hooks/.plugins/multi/scripts/multi-cli-companion.mjs— CLI entrypoint; dispatches subcommands (task,review,adversarial-review,status,result,cancel,setup).plugins/multi/scripts/lib/adapters/— one adapter per CLI. Interface inCONTRACT.md.plugins/multi/scripts/lib/— shared runtime: broker lifecycle, app-server, job control, render, git. The ACP client layer lives inlib/acp/(client.mjs=runAcpTurnon the official SDK,resolve.mjs,diagnostics.mjs); a legacylib/acp-client.mjspredates it and is slated for deletion.plugins/{codex,cursor,antigravity,opencode}/— per-CLI command slices that forward intomulti.test/—unit/(offline) +fixtures/(sandbox helper).test:livereusesplugins/multi/scripts/test/.
- Broker lifecycle: the Codex app-server broker is a detached per-cwd daemon, reused across tasks. The SessionEnd hook reaps the session's primary-cwd broker; brokers for any other cwd self-terminate after an idle window (
CODEX_COMPANION_BROKER_IDLE_MS, default 600000 ms — seeapp-server-broker.mjs+lib/broker-lifecycle.mjs→shouldIdleShutdown). Set the env to0to disable idle shutdown. - State is per-cwd: jobs and brokers key off the working directory; always pass
--cwd. Parallel agents should use separate worktrees / cwds to stay isolated. spark(gpt-5.3-codex-spark) is rejected on ChatGPT-auth Codex accounts — expect a 400; not a bug.- Cursor uses headless
agent -pby default (ACP is opt-in viaMULTI_TRANSPORT_CURSOR=acp): on headless the prompt is delivered on stdin (newline-safe), models pass through as flat--modelnames (defaultauto), roles map to flags (delegate→agent+--force --trust,research/explore→--mode ask --force), and cancel is the generic process-tree kill.--until-doneloops--resumeturns. Cursor's shell is slow/unreliable on Windows (host-PATH/WSL, open upstream), so/cursor:delegatedefers build/test verification to the caller (Claude) — file writes and web/codebase reads are unaffected. - OpenCode uses headless
opencode run --format jsonby default (ACP is opt-in viaMULTI_TRANSPORT_OPENCODE=acp): on headless the prompt is on stdin, NDJSON event stream on stdout. Read-only roles (research,explore) are enforced via injected oc-* primary agents with write/edit/bash denied (no--read-onlyflag). Write roles use--dangerously-skip-permissions.--until-doneis supported;--effortis not. Default model:opencode/claude-opus-4-8(Zen). Token-offload caveat:anthropic/*models = zero offload (same Claude bill). Useopencode/*,openai/*,google/*, etc. for real offload. SetOPENCODE_CLI_DEFAULT_MODELorOPENCODE_CLI_PATHfor overrides. - ACP transport (
lib/acp/client.mjs, official@agentclientprotocol/sdk): Cursor + OpenCode have dual-transport adapters selecting ACP vs headless per turn fromMULTI_TRANSPORT_<CLI>. ACP gives in-protocol model select (set_config_optionvs the live options list), read-only viaset_mode/deny-env, andsession/cancel(OpenCode mislabels cancel asend_turn, so the client treats cancel-requested+ended as cancelled). Inactivity watchdog covers the handshake;MULTI_ACP_INACTIVITY_MS/MULTI_ACP_OVERALL_MStune it. Codex (ASP) and Antigravity (agy) have no ACP path. - Forwarders only have
Bash(review additionallygit) — by design. Don't add tools.
- Keep files small and single-purpose. Smaller files = parallel agents don't collide. Splitting the two monoliths —
multi-cli-companion.mjs,lib/adapters/codex.mjs— is ongoing; the pure option normalizers were extracted tolib/task-options.mjsas the first step. Continue by pulling out one cohesive, independently-testable unit at a time and verifying withnpm test. - Add or extend a unit test with every behavior change. Pin contracts in tests, not in a smart model's head — this is what lets the thin forwarders (all
model: sonnet) and offloaded CLIs stay correct. - AI-authored research/plans stay local: put scratch in
.agent/(gitignored).*_RESEARCH.mdand/docs/superpowers/are already gitignored.