You run out of Claude Pro usage at 2pm. Your Codex quota is untouched. CAPO moves the whole agent team over and keeps going.
CAPO runs one agent team across Claude Code and Codex. When the platform you are on reports a usage limit, every session writes a Markdown checkpoint, and the entire team, root included, relaunches on the other platform from those checkpoints. Two subscriptions you already pay for, one continuous session of work.
Status: v0.1.0. Full runs have finished end to end on both platforms against their real CLIs, and one has been switched from Claude to Codex mid-task and carried through to an integrated result on the other side — two coordinators, each in its own worktree, fixing their own bug, reporting results, and CAPO merging both, running the combined check, and exiting on its own. Every one of those runs found defects a green test suite could not see; they are written up in docs/notes/first-complete-run.md. One thing that would make this a finished product is still unproven: no real usage limit has ever fired, which is the whole reason CAPO exists. Every limit path is exercised against captured fixtures, not a live cap. See What is not done.
- A run has one active platform at a time. Every session runs there.
- Each role has a model per platform. Launching on a platform uses that platform's column.
- On a usage limit, every session checkpoints, all of them close, and the team relaunches on the other platform. The root moves too. Checkpoints are augmented with what CAPO can read itself: commits and uncommitted paths in each coordinator's worktree. On a usage limit no session is asked; each gets that mechanical checkpoint. On a hand-forced switch the session is asked, and asked once more if its current turn ends without answering.
- When both platforms are capped, the run waits for the earliest known reset rather than flapping between them.
capo resumecontinues from the latest checkpoint set on disk, so a crash or a closed laptop costs you nothing.
The checkpoint is the whole trick. Because the team moves all at once, nobody on the new platform has any memory. Whatever each agent writes before it pauses is everything the other side gets. So checkpoints are plain Markdown you can read, edit, and paste into a chat window by hand.
git clone https://github.com/whitewolfx7/capo.git && cd capo
npm install && npm run build
node packages/cli/dist/main.js doctordoctor checks Node, git, and both platform CLIs, and tells you what is
missing before a run starts rather than after it fails.
To get a capo command on your PATH, run npm link -w packages/cli, or
install the plugin into either host and let its skill call the bundled binary.
Everything below also works as node <repo>/packages/cli/dist/main.js ....
Then, in a project you want worked on:
capo run --config ./orchestration.yamlThat prints a run id and detaches. The orchestrator keeps running after you close the shell, which matters because the shell is often on the platform that is about to get capped.
capo status # which platform, which sessions, which tasks
capo switch <run-id> [--workspace <dir>] # checkpoint and move now, before hitting a limit
capo resume <run-id> [--workspace <dir>] # continue from the latest checkpoint
# from anywhere inside the project, or pass --workspaceversion: 1
workspace: .
objective: ./context/GOAL.md
platforms:
claude: { driver: claude-code }
codex: { driver: codex }
start_on: claude
models: # role -> platform -> model, using each CLI's own model names
# Codex model names are account- and CLI-version specific.
# The real list for your account is in
# ~/.codex/models_cache.json. Verified working on
# codex-cli 0.154.0: gpt-6-astra, gpt-5.6-sol.
# `codex exec -m <model> "hi"` settles any doubt.
root: { claude: opus, codex: <your codex model> }
coordinator: { claude: sonnet, codex: <your codex model> }
worker: { claude: haiku, codex: <your codex model> }
roles:
root: ./roles/root.md
coordinator: ./roles/coordinator.md
worker: ./roles/worker.md
context:
- ./context/PROJECT.md
coordinators:
- id: team-a
- id: team-b
tasks: # at least one; each names its coordinator and write scope
- id: component-a
coordinator: team-a
brief: ./tasks/component-a.md
write_scope: [src/component-a/]
- id: component-b
coordinator: team-b
brief: ./tasks/component-b.md
write_scope: [src/component-b/]
setup_command: ["npm", "ci"] # optional; runs in every fresh worktree
transcripts: true # default; writes a live log per session you can tail -f
limits:
max_workers_per_coordinator: 2The model map is the point of the file. Put the expensive model on the root and cheaper ones on coordinators and workers, per platform, and CAPO uses the right column whichever platform it is on.
A bad config is rejected before any model call: an unknown platform in
start_on, a role missing a model for a declared platform, duplicate ids, an
unknown coordinator reference, write scopes that overlap or escape the
workspace, and missing files.
A runnable example lives in examples/two-coordinators/.
This is a real one, and it is the only thing that crosses between platforms.
# Checkpoint: team-a
run: 2026-09-12-001
role: coordinator
platform: claude
written: 2026-09-12T14:20:05.000Z
base_commit: 3f2a9c1
## Objective
Build the HTTP client with retries.
## Decisions made
- Reused the existing fetch wrapper rather than adding a dependency
## Done
- src/a/client.ts written; 4 tests pass at commit 9ab1c02
## In progress
- Exponential backoff: implemented, but the jitter test fails intermittently
## Remaining
- Fix the jitter test
- Document the retry policy in the README
## Blockers and open questions
- Need the root to decide whether retries are capped at 3 or 5Checkpoints are never overwritten. Each pause writes a new numbered set under
.capo/runs/<run-id>/checkpoints/, alongside an index naming the reason for
the pause.
CAPO's sessions are headless, so they never show up in Claude Code's or Codex's session list and there is no window to open. Everything they do is mirrored to disk instead.
tail -f .capo/runs/<run-id>/transcripts/*.mdThat is the live view: what each session says, the tools it runs, and a loud marker when a usage limit hits, followed by CAPO moving the team. Each launch writes a header naming the platform and model, so a transcript that spans a switch shows exactly where the session went.
.capo/runs/<run-id>/STATUS.md is rebuilt on every state change and holds the
same information capo status prints: sessions, tasks, limits seen, and
whether the run is parked waiting for a reset.
Transcripts are on by default. Set transcripts: false in the config to turn
them off.
capo status also prints each session's platform session id. For Claude Code
that is a real session id CAPO generated, so claude --resume <id> should
reopen that conversation.
Every coordinator runs in its own git worktree, on its own branch, however many tasks it owns — a coordinator is one session with one working directory, so the session is the unit that can actually be isolated. Only the root runs in your workspace, and only because it does no work there: it decomposes, judges and reports.
Every task declares a write scope. A result is re-checked against it before anything merges, renames included. Where a coordinator owns several tasks they share its worktree, so that check is against the union of what that coordinator owns; the boundary strictly enforced is the one between coordinators, and nothing may write into another's scope.
A coordinator reports a finished task by sending a fenced # Result: block
naming its commit — unprompted, whenever the work is done. CAPO never asks
for one. A task moves ready -> running -> review -> done/failed,
and once every task has reported, the run integrates: each submitted commit
is re-checked against that task's declared write scope (including files moved
out of it by a rename), accepted results are merged one at a time into an
integration worktree, and check_command runs over the combined tree. A
rejected scope, a conflict, or a failing check fails that task and the run.
If setup_command is set it runs in each coordinator worktree at creation
and in the integration worktree before the check, because a fresh worktree
has no installed dependencies.
A coordinator saying it finished does not make a task done. Its report is a claim; the scope check and the merge are what settle it. The root does not perform integration either — CAPO does it mechanically, and the root's job is judging whether the work meets the objective.
When integration finishes, the run ends and the process exits on its own.
CAPO commits with your own git identity and your own signing configuration. It will tell you if git has no identity configured rather than inventing one.
autonomy decides this, and the default is autonomous, which means what it
says: sessions act without asking. Nobody is watching a headless run, so
there is no one to approve anything mid-flight.
On Claude Code that is a full permission grant — the session may run any
command. It has to be: the narrower mode accepts file edits but denies every
mutating Bash call, so a coordinator could write a fix and then never run the
tests or git commit, which means it could never finish a task. A live run
died on exactly that. On Codex it is --approve-for-me, which keeps the
workspace-write sandbox.
Claude Code sessions are launched with --setting-sources project,local, so
plugins, skills and hooks installed at user scope on your machine are not
loaded into them. A live run showed a root session picking up CAPO's own
plugin skill and treating itself as the control panel. Codex has no
equivalent flag, so Codex sessions still see every plugin in
~/.codex/config.toml; the root's read-only sandbox is what bounds it there.
Be clear about what that grant is and is not. A git worktree bounds where git-tracked changes land; it does not bound what a process can do. A Claude Code session in this mode can run any command on your machine, and the Codex sandbox is the only OS-level boundary either platform offers. What CAPO adds on top: the root is launched read-only on both platforms, every coordinator runs in its own worktree, and nothing merges into your tree until CAPO has re-checked the diff against the declared write scope.
Set autonomy: supervised for a dry run: sessions read and plan and write
nothing. Because a headless run has nobody to answer an approval request, a
supervised run will stop rather than make changes. That is the setting, not a
bug.
See docs/installation-claude.md and docs/installation-codex.md.
Each plugin is a skill that teaches the host to drive the bundled CLI. There is
no MCP server, no background service, and no second implementation. Both hosts
read the same .capo/ directory, so you can start a run from one and check on
it from the other. That is the normal flow, not an edge case: after a switch,
the host you started from may be the capped one.
The host conversation is a control panel. It is never the root session.
No real usage limit has ever fired. Limit detection, the checkpoint, the switch and the relaunch are all exercised against captured fixtures and real CLI output shapes, never against a live cap. This is the one remaining claim in this README that has not been watched happen, and it is the reason CAPO exists.
Also not done:
- Moving a single task between platforms while the rest of the team keeps running. v0.1 moves the whole team at once.
- Running on both subscriptions concurrently. One active platform at a time.
- A long-running service, leases, fencing tokens, or an event journal.
- An adapter SDK or third-party platforms such as Gemini.
- Usage-aware routing ahead of limits, and automatic switch-back.
- Surfacing approvals or questions from a session anywhere. A session that
stalls waiting for approval is only marked
stalled(visible incapo status,STATUS.md, and its transcript); nothing lets a person answer it through CAPO. A live Codex coordinator hit exactly this and sat idle. - Codex sessions still load every plugin in
~/.codex/config.toml; there is no per-session isolation flag. - A coordinator that ignores three result nudges is only marked stalled.
Found today, while running both adapters for real:
- CAPO never spawns a worker itself; coordinators do, using their host's
native subagent mechanism.
roles/worker.mdand the worker model column inmodels:are handed to each coordinator to relay, which means the model choice is a request CAPO cannot enforce. A live Codex coordinator did delegate to a real worker and check its commit before reporting, so the path works — but nothing verifies which model the worker actually ran as. - Registering the plugin with a host from the GitHub marketplace entry has
not been run end to end. A fresh
git cloneof this repository does run the committed bundle with no install step, andcapo doctorpasses from it, so the payload is right; what is untested is the host's own install.
Not yet verified: no real usage limit has fired on either platform.
Claude Code's detection was rewritten today to read the CLI's own structured
rate_limit_event (rate_limit_info.status: allowed | allowed_warning | rejected), confirmed to exist in the installed CLI's schema — a real
improvement over pattern-matching assistant text — but the rejected status
itself has never been observed live. Codex's detection is still
pattern-matching text; its actual wire shape for a limit remains an educated
guess. Codex has also never run a full orchestration, only single turns and
one hand-forced switch. Until a live run confirms all of that, treat limit
detection as unproven.
CAPO drives each vendor's own documented CLI, using your own separate subscriptions, one at a time. It does nothing to circumvent, pool, or evade any platform's usage limits. It reacts to a limit by moving to a different service you also pay for, which is what you would do by hand.
npm test # full suite
npm run typecheck # tsc --build
npm run build:pluginsDesign is in docs/architecture.md, the full future design in docs/roadmap.md, and the build plan in docs/superpowers/plans/.
See CONTRIBUTING.md. The one rule worth reading before you
touch an adapter: a stub proves almost nothing here, and
docs/notes/codex-live-findings.md and
docs/notes/claude-live-findings.md explain
why in detail — a single live Codex run found eight real defects in code that
had 220 passing tests against the stub, and the first run that finished end to
end (docs/notes/first-complete-run.md)
found two more that every test in the suite was blind to. The Claude Code adapter has since had
its own live run too (see the header comment in
packages/core/src/adapters/claude.ts), which found and fixed three more:
a spuriously repeated ready event, a swallowed failed turn, and usage-limit
detection that was pattern-matching text no real run ever produces instead of
reading the CLI's actual structured event. Real coordinators running under
CAPO on Claude Code have since diagnosed and fixed real bugs against the
examples/two-coordinators project, with tests
passing on both sides — the closest thing to evidence yet that the design
works, short of surviving an actual usage limit.
Security issues go through private vulnerability reporting, not public issues. See SECURITY.md.
By participating you agree to the code of conduct.
MIT. See LICENSE.