Skip to content

Latest commit

 

History

402 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
router logo

router

The strongest model for judgment. The cheapest quota for tokens.

A Claude Code plugin that routes coding subtasks to the cheapest capable model — your main session (Opus) plans, reviews, verifies and merges; cheap executors write the code.

ci version status beta license Apache-2.0 node >= 18 Claude Code plugin

English | 中文


✨ The idea

Most of a coding task's tokens go to mechanical labor — reading the repo, writing the implementation, iterating to green (measured: one ~400-line feature burned 1.88M executor input tokens). The part that actually needs your strongest model — planning, reviewing, verifying, merging — is low-token, high-judgment. router splits the work along exactly that line:

Prompting the agent directly With router
Who executes Opus (expensive) the cheaper executor with more quota (codex / sonnet)
Change scope bounded only by the prompt enforced on the diff: allowed globs + changed-line cap
Correctness you check by hand CLI gates the diff (scope + secrets + exec bit); Opus runs the build/tests in your real env
...and laziness trust the model's word ...plus the main session reviews the diff for lazy/wrong work
Where edits land your working tree, immediately a router/<task> branch in your own checkout; your branch changes only on land
Quota / rate limit the run stalls balances codex vs claude by real remaining quota; 429 fallover

router never auto-merges. The gates decide PASS/FAIL; you decide land.

💸 What it saves — measured, not claimed

Measured on this repository's own development (20 real dispatches, router usage --all):

actual spend if all on Opus (est) saved (est)
20 dispatches $23.96 ~$93.34 ~$69.38 (~74%)

The savings figure is a list-price estimate, not a bill — the executors run on plan subscriptions, so real marginal cost is often lower; --explain-savings prints every caveat. Quality is guarded by mechanism, not by trusting the cheap model: every diff passes five mechanical gates, a full-diff review by the main session, real-environment verification, and a mandatory full-chain CI pass before "done" — the acceptance bar is identical to Opus writing the code itself. Measured first-pass rate on the routed tier: 89% (n=9, median wall clock 3.4 min).

🚀 Quick start

Requirements: Claude Code · Node.js >= 18 · git · one executor CLI logged in (codex or claude — a plan subscription is fine, no API key needed).

Install from inside Claude Code:

/plugin marketplace add MisterRaindrop/agent-router-cc
/plugin install router@agent-router-cc
/reload-plugins

No install step beyond that, no config: dist/router.js is a committed, dependency-free bundle, and router auto-creates a gitignored .router/ on first use. No init, no policy file, no commit.

Then just talk to Opus, plan the change together, and:

/router:go

On another machine, or from a script

The same two steps without opening Claude Code. The repository is public, so no SSH key and no gh login are needed:

claude plugin marketplace add MisterRaindrop/agent-router-cc
claude plugin install router@agent-router-cc -y      # -y is required when stdin/stdout is not a TTY

Restart Claude Code to apply, then check what landed:

claude plugin list | grep -A3 router     # → Version, Scope, Status: ✔ enabled

Updating

claude plugin marketplace update agent-router-cc     # refresh the marketplace cache first
claude plugin update router@agent-router-cc

Inside Claude Code the equivalent is /plugin marketplace update agent-router-cc, then update router from the /plugin menu, then /reload-plugins.

Command files (commands/, skills/, hooks/) are read once at startup, so a version that changes one of them needs the restart. The CLI bundle does not: dist/router.js is spawned fresh on every call.

📐 The shape of a run

everyday task:   plan with Opus in conversation  →  /router:go  →  /router:review (optional)
                                                    one package, one       independent, strict
                                                    executor, gate,        review of landed code
                                                    review, land

large feature (opt-in, YOUR call — router never judges task size):
  /router:brainstorm  →  /router:design  →  /router:design-review (opt.)  →  /router:workplan  →  /router:go
  question the idea;     clarify +          independent adversarial          the how: steps,      executes the
  compare with how       research; a        pass; every objection            task breakdown,      approved plan
  others solve it;       DESIGN.md you      adjudicated by you,              verification;        verbatim
  argue the case         approve section    nothing auto-applied             you approve
  against                by section

/router:go pauses at exactly three points — nothing happens without you (when it executes a Plan approved via the design flow, the breakdown confirmation is skipped: you already approved that list at /router:workplan):

  1. Confirm the task breakdown. Every package is shown with its file scope and target model before anything runs.
  2. Unclear tasks stay with you. Anything needing real judgment or design, Opus does with you directly instead of handing it to a cheap model.
  3. Approve before merge. Nothing lands in your branch without your say-so.

In between, the package runs on the quota-picked executor in your own checkout, on a branch called router/<task-id>. That is the point: a fresh git worktree has no dependencies, no build objects and no configure output, so a real project cannot compile in one — and the build has to happen in the main checkout anyway. The whole run holds an exclusive lock on the checkout, your uncommitted work is committed first (with the sha reported), and the executor commits one functional unit at a time so you can review it commit by commit.

One task at a time, by design. Parallel dispatch was removed: measured, it cost almost nothing to run (0.26s of orchestration against 393s of executor time) and a great deal to supervise — and every result still needs reviewing one at a time, so review was the bottleneck the parallelism kept feeding.

At the end Opus runs a mandatory acceptance pass: full-chain CI in your real environment, reading the whole output itself, before reporting done.

🗂️ Task contracts: tier and risk are different questions

Every package is a machine contract at .router/tasks/<id>/task.yaml, authored by the main session from your conversation — there is no global policy file:

# .router/tasks/q2/task.yaml
title: usage --json emits one document per run
plan_id: issue-1234
allowed_globs: ["src/app/**", "test/usage-*.test.ts"]
max_changed_lines: 400   # size it to the real diff shape: tests and deletions count too
tier: weak               # capability needed:  weak | strong | critical
risk: normal             # review it earns:    low  | normal | high   (one-way: only ever raised)
verify: [["npm", "test"]]
depends_on: []
field question decides
tier how much capability does this need? which model and reasoning effort
risk how bad if it is wrong? how much independent review it earns

A mechanical change to an authentication path is weak and high. The CLI raises risk from deterministic signals (line count, invariant paths touched) and never lowers it; quota never demotes a task to a weaker tier.

🤖 How models are picked

tier codex claude
weak gpt-5.6-terra · medium haiku · medium
strong gpt-5.6-sol · high sonnet · high
critical gpt-5.6-sol · xhigh opus · xhigh
  1. Decide the minimum capability tier the task actually requires — the one routing decision that matters.
  2. Within the tier, both executors are candidates; real remaining quota picks (codex usage read from ~/.codex/sessions, claude from an optional statusline snapshot). More headroom goes first; a real 429 switches to the other. Quota reorders within a tier — it never demotes.
  3. Reasoning effort is matched to the work, not maxed: medium for mechanical implementation, high for real capability, xhigh reserved for critical.
  4. The orchestrator's own model appears only at critical — spending it as an ordinary executor would consume the very budget routing exists to protect.

Override any slot in .router/models.yaml; router models prints the resolved table. Nothing ever edits it for you.

🛡️ Two kinds of gate

Environment-free gates — run by the CLI on every diff, the deterministic guarantees a cheap model cannot fake:

check meaning
diff_applies applies cleanly onto the base commit
scope only allowed_globs changed, under the line cap, no test deletion
secret_scan no keys or secrets in the added lines
exec_bit a new script carries the executable bit when its siblings do
verify the task's own verify command(s) exited 0

verify answers a mechanical question — did it run and pass — never is it right.

The real gate is a property of the project, declared once in .router/gate.yaml. The executor works in your own checkout now, so it has the same build environment you do — warm dependencies, warm objects, a real configure result — and the gate runs there under the same exclusive lock the run already holds:

key what it does
gate the incremental build-and-test command
clean_gate the full-rebuild command
clean_triggers globs whose change forces clean_gate instead of gate. Anchored at the repo root, unlike gitignore: CMakeLists.txt is the root one only, the tree needs **/CMakeLists.txt
reset run before verification, to clear state a previous build left behind
lock_wait_minutes how long to wait when another run holds the checkout

Any deletion or rename in the diff forces clean_gate regardless of triggers: an incremental build keeps a stale object for a source file that no longer exists, and nothing tells it to drop it. mode: queue remains for a project that verifies on an integration branch — router gate feeds commits one at a time into your own checkout under an exclusive lock, refusing if tracked files are modified, verifying on the current integration head, keeping the build cache warm (never git clean), and restoring your branch. A gate that fails is re-run on the pre-merge head, so a project that was already red doesn't get blamed on the change.

⚔️ The design flow — approved in order

For a large feature — cross-module work, real approach trade-offs — the user opts in. Every document is yours to approve:

  • /router:brainstormBRAINSTORM.md (optional first stage, for when the goal is not settled yet). Every round owes you four things: a question about why, from an angle you have not considered; a comparison with how other products actually solve this; the strongest argument against building it at all; and at least one alternative you did not propose. Your approach is the best one you have, not necessarily the best one there is. status: rejected is a real outcome — a killed idea with a documented reason is this stage succeeding, and the record is what stops it coming back in three months with nobody remembering why.
  • /router:designDESIGN.md (why / what / what NOT / chosen approach / risks / acceptance criteria). One clarifying question at a time, interleaved with code research (symbol index, file:line evidence); 2–3 approaches with trade-offs and the rejected ones recorded; then the document is drafted section by section, each section confirmed by you before the next is written. No document is generated while the conversation is still open — that is where models start guessing.
  • /router:design-review (optional, any rounds) — an independent model attacks the Design: critique printed verbatim, written in your conversation language, every objection carrying a confidence, uncertainty phrased as questions rather than assertions, and the reviewer must read Alternatives considered so it never re-proposes a road you already closed. Each objection is adjudicated by you — accept / reject / discuss, recorded in DECISIONS.md; nothing touches the document before your verdict. Runs in the background, truncation-guarded, session resumed across rounds.
  • /router:workplanWORKPLAN.md (how: steps, task breakdown, dependencies, verification matrix, rollout) — derived only from an approved Design and bound to its revision: a Design revision drops the work plan back to draft. You approve a summary; /router:go then executes it verbatim. Everyday tasks skip all of this and use /router:go directly.

The task contract carries both the work plan and the Design, verbatim with their sha256s. They answer different questions: the plan says what to do and in what order, the Design says why it is built this way and which invariants may not break — and the second is what an executor can never recover by reading code. BRAINSTORM.md is deliberately excluded: it records the counter-evidence and the rejected directions, so handing it over would hand the executor a pile of ideas that were decided against.

🔍 /router:review — the last gate after green

Green tests are the precondition, not the evidence — the tests themselves are under review. Two lenses, ideally two different models, 16 fixed dimensions:

  • Architect lens (F1–F7): was the need actually solved; should this change exist at all; reuse vs reinvent; root cause vs symptom; simpler-but-still-correct; structure and integration; independent correctness judgment that does not trust the author's tests.
  • Senior-dev lens (D1–D9): robustness beyond the tests; failure modes (no silent fallbacks); complexity/over-design ("an explanation longer than the code is complexity dressed as prose"); test design quality; readability; project-style consistency; comments and shortcut labeling; security; performance sense.

Verdicts are two axes, never collapsed: code_health (did we find defects?) and assurance (is it actually proven?). "No defect found" is not "proven". Blocking must be earned; a clean diff gets a plain "ship it". Mechanical checks (formatting, import order) go to lint/CI, not to the LLM.

🧰 Commands

command what it does
/router:go top-level — execute the plan you just agreed on (or an approved WORKPLAN.md, verbatim). One package, one pinned executor, on a router/<task> branch in your own checkout; runs detached, statusline shows live phase and activity, the session is woken at terminal states
/router:brainstorm question an idea before designing it — compare it with how others solve it, argue the case against, propose the option you were not offered
/router:design opt-in for large features — clarify, research, draft a DESIGN.md you approve section by section
/router:design-review adversarial second opinion on the Design — you adjudicate every objection; nothing auto-applied. Also reports where an outside reader could not follow the document
/router:workplan turn the approved Design into WORKPLAN.md — steps, task breakdown, verification; you approve
/router:review strict, independent two-lens review of the landed code
/router:resume <id> send a failure back to that task's own executor session
/router:result <id> per-check verifier report and log tail for a run
/router:list tasks with their last status and whether the task branch is still there
/router:models the resolved model-tier table (bundled default + overrides)
/router:usage cost vs an all-strongest-model baseline; --routing for routing evidence
/router:symbol out-of-context symbol index — locate code without reading whole files
/router:setup-statusline wire claude-side quota reads into Claude Code's statusLine

docs/workflow.md is the whole protocol end to end — work packages, tiers and risk, both gate modes, what the executor owes back, and when to resume a session. See also docs/quickstart.md and a runnable task in examples/minimal/.

🔒 Isolation & credentials

The executor shares your checkout, so isolation is expressed in git and in permissions rather than in a separate directory:

  • An exclusive lock on the checkout for the whole run, taken before the first write and held until the executor is dead. A second /router:go is turned away naming the holder, with nothing changed. The lock's heartbeat runs in its own process, because verify commands block the event loop and an in-process beat would go silent for exactly as long as the lock's staleness window.
  • Your uncommitted work is committed first, with the file list and sha reported, before anything moves. No git stash (a stash is detached from the branch, and a conflicting pop on a failure path leaves your changes somewhere you have to be told about) and no git clean.
  • Nothing destructive without asserting identity: a reset only runs while the current branch is exactly the task's branch and base_sha is still an ancestor of HEAD.
  • Router never merges and never switches back. The run ends telling you which branch you are standing on.
  • Executors are supervised with a wall timeout and a stall watchdog; their output never enters the orchestrator's context, and no MCP server from your session is inherited.
  • Codex uses its workspace-write sandbox. Claude runs in plain acceptEdits (never bypassPermissions). The Bash grant is an allowlist, not a shell: the task's own verify command plus its program+subcommand prefix, and a narrow set of git subcommands (add, commit, status, diff, log, rev-parse) so the executor can commit its own work. checkout, reset, rebase, branch deletion and push are unreachable.
  • An executor cannot touch .router/. A nested router invocation refuses outright — a reproduced failure, not a hypothetical: an executor given a task that changed router new ran router new --id smoke to try its own work and wrote real orchestration state, invisible to every gate because .router/ is fully gitignored.
  • Executor CLIs receive only the login-session context needed for plan auth plus an explicitly configured provider key — never your full parent environment.
  • Every run ends with a delivery report (gate_ran, scope_drift, escalate_review); a missing header is a contract violation, and a contract conflict (CONTRACT_CONFLICT) stops the run and returns the decision to you.

🛠️ Development

npm ci
npm run check     # tsc --noEmit + core-purity guard + node --test
npm run build     # bundle src/ -> dist/router.js (commit the result)

src/ is layered domain -> core -> io -> app -> cli. core/ is pure (no fs, child_process, process, clock, or randomness — enforced by npm run check:deps), which keeps the gate logic deterministic and unit-testable.

🤝 Contributing

Contributions are welcome — see CONTRIBUTING.md for the build, test, and PR workflow, ROADMAP.md for where the project is headed, and CHANGELOG.md for what each release changed. Security issues go through SECURITY.md, never public issues.

📄 License

Apache-2.0.

About

Claude Code plugin that routes coding subtasks to the cheapest capable model — the main session (Opus) plans, reviews and verifies; codex/claude executors write the code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages