docs
ccx, end to end.
Two coding agents from two companies, one write baton, on your machine. This page covers what it does, every flag it has, how the loop terminates, and what it sends anywhere. The answer to the last one is nothing.
What ccx is
ccx is a command line tool that drives the two coding agents you
are already paying for. It does not host a model, proxy one, or hold a key.
It runs the official claude and codex binaries that
are on your PATH, signed in to your own accounts, in your own
working directory.
One task goes in. Both agents read it and argue over who should take it. The winner gets write permission and does the work. Then the other agent — a different model, from a different company, with no stake in defending the code — reads the diff and hunts for bugs in it. Whatever it finds goes back to the author, who fixes and hands it over again. The baton keeps crossing until the review comes back with nothing, or until one of the guards in §8 fires.
The point of two vendors is that a model reviewing its own work is grading its own homework. Two models trained by different labs do not share a blind spot, and exactly one of them can write at a time, enforced with the vendor CLIs' own permission flags rather than by asking politely.
Install
There is no install command yet, because there is not one that works. Nothing is published to npm and the repository is still private, so any command printed here would fail if you ran it. This section gets the real command on the day that changes.
What it will take is already fixed. ccx is a
directory of .mjs files with zero runtime dependencies, no
build step and no postinstall script — the tree runs as it stands, and
installing it means putting bin/claudecodex.mjs on your
PATH under the names ccx and
claudecodex.
You also need three things ccx does not install for you:
- Node 18.17 or newer, which is what
package.jsonrequires. - The
claudeCLI, signed in to an Anthropic subscription. - The
codexCLI, signed in to a ChatGPT subscription.
ccx login prints both marks, says which vendor you are signed out
of, and then runs that vendor's login rather than telling you the command. The
two spell it differently and a fresh machine is the worst place to be guessing.
ccx login
Signed in to both, or nothing runs. A machine with both CLIs installed
and only one signed in is the worst shape this tool can be in: the loop runs
against a single vendor, the reviewer is the same model that wrote the code,
nothing errors, and every number it prints still looks plausible. A definite
signed-out state refuses the run and exits 77.
--why and --json sit deliberately outside that gate,
because they spend nothing and they are what you want working while you fix
your login.
Two ways to run it
A task in argv runs the whole pipeline once and gives you your shell back.
ccx "fix the off-by-one in sumAll"
No task opens a session instead. You get a ccx prompt, and every line you type runs that same pipeline in the same process: the working directory persists, the router's weights accumulate across the whole session rather than being reloaded per invocation, and neither agent ever owns your terminal.
ccx
/help for commands · Ctrl-D to leave
✳>_ ~/dev/thing ▸
The prompt carries both marks and the directory the agents will edit.
Every flag below works inline in a session, so
--no-review fix the parser at the ccx prompt does what it looks
like it does.
Session commands
These exist only at the ccx prompt. Anything that is not one of them is a task.
- /claude <task>/codex <task>
- Force one of them to drive. Recorded as a training signal, weighted three times as heavily as a routing decision the tool made itself.
- /fast <task>
- Skip the conference. The free router picks, instantly, and the work starts.
- /why <task>
- Who would take this, and why. Spends nothing.
- /cd <dir>/pwd
- Change and show the working directory the agents edit.
/cdwith no argument goes home;~/is expanded. - /rounds <2-6>
- How long the two of them argue before work starts, for the rest of the session. Bare
/roundsreports the current setting and what it costs. An inline--roundson one task still wins, because it is the more specific answer. - /stats
- Per-domain record, and how often the free router agreed with the conference.
- /login [claude|codex]
- Runs the login for whichever vendor is signed out. With an argument, that one. Anything that is not
claudeorcodexis refused rather than forwarded. - /whoami
- Who you are signed in as, and nothing else.
- /helpalso: /h, help, ?
- The list above, printed in the terminal.
- /exitalso: /q, exit, quit, Ctrl-D
- Leave. Ctrl-C scraps the line you are typing without leaving.
A bare word only counts as a command when nothing follows it.
exit quits; exit the retry loop early is a task.
Flags
This is the whole set. Anything ccx does not recognise is forwarded to the
agent verbatim, as is everything after a bare --, so
ccx --model x "task" behaves the way you would expect.
- -c, --claude-x, --codex
- Force a driver, skipping the conference's choice. Weighs three times as much as an ordinary outcome when the router learns from the run.
- --why--dry-run
- Print the routing decision and stop. Spends nothing, works signed out.
- --json
- The same decision as JSON. Also spends nothing.
- --robot
- Run the whole thing headless, with no terminal needed, and print exactly one JSON object. Narration goes to stderr, so stdout is safe to pipe into
jq. That promise holds on the failure paths too, not just the happy one. - --fast
- No conference. The local router picks the driver from learned weights, instantly, for nothing. Turns the full-screen show off with it.
- --no-confer--confer
- Same skip as
--fast, but keeps the theatre.--conferis the explicit opposite, for a shell alias that needs to overrule itself. - --rounds <2-6>--rounds=<n>
- The conference ceiling. Two by default. The floor is 2 because one round cannot contain a reply, and the ceiling is 6 because every round costs two agent calls out of a cap of twenty.
- --no-review--review
- Do the work and skip the review loop entirely.
--reviewis the explicit opposite; review is on by default. - --receipt--receipt=<path>
- Write the whole run as one self-contained HTML file: the argument, every finding with the input that would have broken, the diff, and the guard that ended it. Free, and it stays on your machine.
--receipt=-writes the redacted build to stdout, forgh pr comment --body-file -. See receipts. - --share-dry
- Print the exact bytes that would leave this machine if sharing were switched on, after redaction, and send none of them. It also writes the receipt.
- --stats
- Per-domain win record, and how often the free router agreed with the paid conference.
- --login
- Same as the
ccx loginsubcommand. - --demo--intro
--demoplays the full show and launches nothing.--introreplays what a first run looks like: the show, both marks, the logins.- --no-theater
- No panes, no live transcript, no hold, no full-screen show. Plain lines.
- --no-anim--no-animation
- Skip all animation, including the first run's intro.
- -h, --help-v, --version
- The help text, and the version.
Only --rounds consumes the next argument. Everything else
that takes a value must be written as --flag=value. The parser
reads no lookahead on purpose: an unknown dashed token is forwarded to the
agent, so --receipt path would quietly send path to
Claude as an argument. --receipt=path cannot.
How the loop works
Six steps. Only two of them cost anything per round.
1 · The gate
Both agents are checked for presence on PATH and for a live
login before a single call is spent.
2 · The conference
Both agents get the task and argue over who should take it, in the open. Two
rounds by default, six at most, two agent calls per round, with a 20-second
ceiling on each leg. It costs about 12 seconds before work starts, set by the
Claude leg reloading its context on every call, which is why
--fast exists for the times you already know the answer. Every
turn is painted the moment it lands: the argument you are waiting for is the
progress bar.
3 · The baseline
Before the driver touches anything, ccx captures what your tree looks like now,
without writing to git. git stash create makes an
unreferenced commit object and prints its SHA; it touches no refs, no stash
list and no working tree, and git gc collects it later. Untracked
files are recorded separately with git hash-object and no
-w, which computes a hash without writing one. Both are checked
in the test suite, because a tool that writes to your git and gets it wrong
once is dead forever.
4 · The work
The winner gets the real terminal and the write baton: claude
--permission-mode acceptEdits, or codex -s
workspace-write. You watch it work. Every round after the first runs
headless, because a loop that seizes and returns your terminal six times is
unusable.
5 · The review
The other agent reads the delta — not your repository, and not
--uncommitted, which would sweep in unrelated work sitting in
your tree and blame the driver for it. It reads under read-only flags:
codex exec -s read-only, or claude -p --tools ''
--permission-mode plan --disallowedTools Edit Write NotebookEdit. The
diff is passed as the prompt, identically for both, so Claude's findings and
Codex's findings are comparable.
A finding only counts if the reviewer can name inputs or state that produce a wrong result, a crash, or data loss. Style opinions are dropped unread, so a fix round can never churn working code over an adjective.
6 · Fix, review, repeat
Findings go back to the author, who fixes them and hands the tree over again. That alternation continues until the review is clean or a guard fires.
Two hard caps, neither of them a flag. Twenty agent calls per invocation, six fix rounds. A cap that can be raised is a cap that gets raised at 2am, and the thing it protects is a subscription window that takes five hours to come back. The conference and the loop spend from one budget, so a six-round argument leaves fewer calls for fixing rather than being billed on top.
Why the reviewer is kept blind
The failure this whole design is built against is a reviewer that agrees more. Show a reviewer the author's own account of the change and it rationalises agreement: findings drop, rounds shorten, every number on the screen improves, and independent review is dead while looking like a success. Nothing fails. Nobody finds out.
So three things are walled off from the review leg by construction:
- The docket. Which findings the author accepted, refused, or argued with is the driver's memory. The reviewer starts every round from the code, not from the argument.
- The scanner. ccx can also run
ubs, a deterministic static scanner, if you have it. Its findings go to the driver and nowhere else, so the reviewer is never anchored on them. That also makes it usable as ground truth: bugs the scanner caught and the reviewer missed are a reproducible count of review quality, which is impossible if the two of them saw each other's answers. Every failure it can have — missing binary, timeout, unparseable output — resolves to zero findings and the run carries on exactly as it did before. - Anything about this change. ccx does keep a per-repository store of what kinds of bug this codebase has shipped, and injects up to five of them ahead of the diff. They are phrased as questions, they name no file or symbol, and they are extracted only from findings the driver accepted. A question can only make a reviewer look harder in a named direction; it cannot hand it a conclusion. The store is local, decays when a prior stops catching anything, and is turned off with
CCX_LESSONS=0.
Where a run stops
Every run ends in exactly one of these, and the tool prints the sentence next to it. Two models handed each other's work in a loop do not converge on their own, so most of this list is a guard rather than an ending.
- clean
- The reviewer read the change and found nothing that clears the burden of proof. The one you want.
- stuck
- The reviewer reported the identical set of findings two rounds running. Either the author cannot fix it or the two of them disagree about whether it is a bug, and repetition never resolves that. It only bills. Those findings are yours.
- disagreement
- The author examined everything still open, refused all of it with reasons, and the reviewer still says it is a bug. Both sides are handed to you.
- conceded
- The other way the work list empties: the reviewer read the code again and took its own finding back. Both sides now agree and there is nothing to settle.
- no-progress
- The author exited cleanly and changed nothing at all. In practice it does not accept that these are bugs. Without this guard the loop spins at full price producing an identical diff.
- rounds
- Six fix rounds, the hard cap.
- budget
- Twenty agent calls, the other hard cap.
- driver-failed
- The author's CLI exited non-zero during a fix round. Whatever findings were open are reported as they stood.
- review-error
- The review leg could not be run or its output could not be read. Reported as unavailable rather than as a pass, because silence and "no bugs" must never look the same.
- empty
- The driver never wrote anything. There is no diff to review.
- reverted
- A fix round backed the change out and the tree is at the baseline again. Nothing is reported as open, because the findings no longer describe any code that exists. Check the task actually got done, though: it probably went out with the change.
Two more facts get reported alongside a stop rather than causing one:
- Regression. The count of real findings went up after a fix. You are told, and you are still shown the last round's findings, because those are the ones that describe the code sitting on your disk right now.
- Scope collapse. The cheapest way for an author to satisfy a reviewer is to delete the risky change. The delta stays non-empty, findings drop to zero, and it scores as a win. So the size of the change is tracked across rounds and a large shrink is called out.
What leaves your machine
Nothing that ccx sends. There is no account, no key of ours, no usage ping, and no opt-out to hunt for, because there is nothing to opt out of.
You can check that instead of believing it, in three steps:
- Every line of network code lives in
src/share.mjsand funnels through one function there,call(), which is the only placefetchis named anywhere insrc/orbin/. bin/claudecodex.mjsimports six things from that file —buildReceipt,writeReceipt,renderReceipt,buildPayload,previewLinesandpayloadBytes— and not one of them can reachcall().- No flag reaches it.
--share-dryexists specifically to print the exact bytes an upload would carry, after redaction, and then send none of them.
The claude and codex binaries do talk to Anthropic
and OpenAI, because that is what they are. That traffic is between you and
them, under the logins and terms you already have, and it is identical to what
those two CLIs send when you run them yourself. ccx adds no hop, no relay and
no third party to it.
One local artifact is designed for you to hand to someone else, and only if you ask for it: a receipt. That page covers what is in it and what is scrubbed out first.
What it writes to disk
| Path | What it is |
|---|---|
| ~/.claudecodex/brain.json | Which agent tends to win which kind of task, as counters and weights. This is what --stats reports on. Delete it and the router starts over. |
| ~/.claudecodex/prefs.json | Your settings, such as how many review rounds you are willing to pay for. A separate file from the learned store so that nothing that learns can overrule a statement you made. |
| ~/.claudecodex/lessons.json | What this repository has taught the reviewer about where its bugs live. Per repository, and uploaded nowhere. CCX_LESSONS=0 turns it off. |
| ~/.claudecodex/receipts/ | Only when you ask with --receipt. One self-contained HTML file per run. |
| $TMPDIR | Scratch for a run — the agent output files, the schema, the before-snapshot the diff is computed against. Deleted afterwards. |
All four live under $CLAUDECODEX_HOME if you set it, and all
are written 0600, owner-only.
The one thing ccx writes outside its own directory is the unreferenced git
object described in §6, step 3.
What it does not do
- It reviews the change, not your repository. Breakage in a file the author never touched is outside what the reviewer was shown.
- It does not check the task got done. The loop asks whether the diff has bugs. A change that shrinks by half is reported as a retreat, not scored as a win, and that is as far as it goes.
- It does not settle disagreements. When the author refuses every finding and the reviewer still disagrees, it stops and hands both sides to a person.
- It does not run your tests. Nothing here is a substitute for a suite.
Written against the source at bin/claudecodex.mjs,
src/session.mjs, src/loop.mjs,
src/launch.mjs, src/review.mjs and
src/share.mjs. If a sentence here and the code disagree, the code is
right and this is a bug: angus@angusangus.com.