AutoMode
CLAUDE.md8 starsChanged 10 days agoMITView on GitHub
AutoMode is a Claude Code mod: a plugin whose TypeScript registers event handlers. It picks a model and an effort for each prompt, runs a plan, build, verify and review workflow, and watches the session with a guardian. The mod has no install step and no build step. Claude Code loads the .ts files directly. npm tooling exists for development only, and it needs Node 23.6 or later. Run all four checks before you call a change to hooks/ done:…
- Reads credentials
- Installs packages
In this repository3 other files it gives its agents.
- growthSkill
- publish-resultsSkill
- ship-changeSkill
The file
We split this CLAUDE.md into the rules it gives an agent
7 rules. About 0 are common, 2 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 25
Run all four checks before you call a change to hooks/ done: npm run typecheck, npm test, the validator and claude plugin test ..
- Very specific: line 82
Validator rules: Write every mods API call in full ($.fs.read(...), never const fs = $.fs).
- Very specific: line 94
Follow the ship-change skill for every change: one issue, one branch and one pull request per task.
- Very specific: line 96
Do not change version in .claude-plugin/plugin.json.
- Very specific: line 98
Do not claim a cost saving without a repeatable benchmark run.
- Distinctive: line 99
Add or update a test with every behavior change.
- Distinctive: line 100
Write comments and docs in short sentences, in active voice, with one idea per sentence.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
AutoMode is a Claude Code mod: a plugin whose TypeScript registers event handlers. It picks a model and an effort for each prompt, runs a plan, build, verify and review workflow, and watches the session with a guardian. The mod has no install step and no build step. Claude Code loads the .ts files directly. npm tooling exists for development only, and it needs Node 23.6 or later.
Commands
npm install # dev tools only
npm run check # type check, then the Node tests
npm run build # release bundle in dist/, validated
npm run typecheck
npm test # tests/*.test.mts and bench/*.test.mts
node --test tests/bands.test.mts # one Node test file
node --test tests/claims.test.mts # every public page shows the numbers in bench/results/headline.json
node --test --test-name-pattern='demandBand' tests/bands.test.mts # one Node test by name
claude plugin validate . --strict # reads the mod the way Claude Code does
claude plugin test . # tests/*.test.ts and *.test.tsx
claude --plugin-dir . # load the working tree; it reloads on saveRun all four checks before you call a change to hooks/ done: npm run typecheck, npm test, the validator and claude plugin test ..
Two test runners. The extension decides which one runs a file:
*.test.mtsruns undernode --test. Use it for pure modules, and for anything with no engine.*.test.tsand*.test.tsxrun underclaude plugin test, with theclaude-code/testingkit.tests/harness.tsstands in for the engine. This runner takes a folder only, so it cannot run one file or one test by name.
What the type check misses. tsc starts from .mts files and follows their imports into hooks/, with claude-code stubbed as any in bench/stubs.d.ts. So it does not check *.test.ts files or the mods API calls. The validator and claude plugin test cover those. strict is off. Write erasable TypeScript only: no enum, no namespace, no constructor parameter properties. 33 older files start with // @ts-nocheck TODO(types). A new file must pass without that line.
Architecture
hooks/hooks.json names one module, hooks/register.ts. Its register function binds every engine event. The guardian sees each prompt first, and then the router decides the route.
| Layer | Files | Role |
| Wiring | hooks/v4.ts, hooks/guardian.ts | Own all $ engine calls and module state. They move values between the engine and the pure modules. |
| Pure logic | hooks/lib/*.ts | No $ and no I/O. Each file opens with a comment that states its contract. lib/io.ts is the one exception. |
| UI | hooks/ui/ | publish.ts writes the automode.view atom, and every drawing reads it. A drawing never calls publish. |
| Workflow | workflows/run.js, agents/*.md | The automode:run workflow and its six agents. It runs in a different runtime from the hooks and shares no imports with them. |
| User scripts | scripts/*.mjs | Run through a user's node, which may be older than 23.6. |
| Evidence | bench/ | Benchmarks and offline transcript tools. Most of bench/external/ is vendored upstream code with checksums. Do not edit it. |
| Marketing | marketing/ | Listings registry, post drafts, GitHub metrics snapshots and the Buffer client. Not shipped. The growth skill runs it. tests/marketing.test.mts checks every post's numbers against the benchmark. |
How a route is decided. v4PromptSubmit runs decideAuto under policy auto, the shipped policy. Policy legacy is the interval optimizer in lib/optimizer.ts.
- Two Haiku judges give evidence: the effortless judge (
lib/effortless.ts) and the AutoMode judge (lib/classify.ts). lib/consequence.tsandlib/risk.tsset quality floors.optimizeAutoinlib/auto.tspicks the weakest eligible model, at that model's own effort. Eligibility reads one verdict asked with a fixed anchor, so the route follows the task, not the model the session started on.resolveinlib/authority.tsapplies the order that matters: a user override or workflow role is final, the floor lifts the candidate, and the switch check inlib/policy.tsthen holds or switches on cache cost. A hold never drops below the floor.
Effort can change per prompt while the model holds, because an effort change keeps the cache where the provider shares it. v4TurnStep applies the route to each request.
How a read is delegated. delegation is off, shadow or on, and config.json ships off. readGate in v4.ts runs from the main-session branch of guarded, and gateRead in lib/delegation.ts decides.
- A full read of a text file at or above
delegationGateTokensstops once per file and task. It returns a free outline fromoutlineOf. The size comes from bytes, not lines, so a minified file counts. - A second read of that file runs, so the model can always get the exact text. The gate also passes a file edited in the task, the
debug,reconsiderandreviewstages, and any task with a risk rating. v4Extractservesmcp__automode__extract. It asks$.tool.checkabout each path, so Read deny rules hold. It makes one Haiku call with no tools.verifyEvidencekeeps a finding only if its quote is in the file at or near the cited lines. A kept claim is still Haiku's reading, so the model must read the lines before it relies on them.
Hooks record each step as kind: 'delegation', with the action gate, read-again or extract. The gate only saves money, so it fails open. Shadow registers no tool, because a tool definition changes the request.
How Haiku is reached. reachHaiku, on in config.json, makes Haiku eligible for T0 to T2 work without critical risk, unless the need names Opus. It also makes T2 with need=opus require Opus. Haiku solved 64 of 65 screen tasks, so a judge's "Sonnet" often overstates the need. bench/classify-eval.mts tests the judges and rules without running a task, and costs cents.
How routes learn. lib/learn.ts keeps a per-project memory in learned-<hash>.json. noteLearned in v4.ts files each task after every turn and after a correction. A task is bad when it moved up to Opus after an escalation, or when the person corrected or retried it. It is ok when a check passed. Once a bucket (tier and need) has 2 bad tasks and a 40% bad share in 30 days, decideAuto excludes the cheaper models for that bucket. Learning only raises a route, and a pin keeps its model.
Settings precedence, lowest first: code defaults, config.json, the plugin userConfig, then environment switches such as AUTOMODE_ROUTER and AUTOMODE_POLICY. See lib/settings.ts.
Failure behavior. Hooks fail open: a routing error falls through to next(e), and a failed judge never blocks a prompt. The tool guards fail closed, Bash included: tool.call denies the call if the guard throws before the call runs.
Pins and the ladder. lib/pins.ts reads the person's /model and /effort from command.run and from the app's value on main requests. In tests, h.step takes the app's model, not the routed one: never pass the routed model back, because the app's value never echoes a rewrite. escalation() in lib/failures.ts rates the evidence weak or strong, ladderStep in lib/bands.ts turns it into one step, and failureStep only reports and diagnoses, plus the legacy floor.
Traps
- Keep this file in
.claude/. ACLAUDE.mdat the repository root is a warning toclaude plugin validate, so--strictfails, and CI runs--strict. - Stale comments. The headers of
register.tsandv4.tsdescribe an AutoMode picker row, butactivationnow defaults toalways.hooks/options.tsnameshooks/ui/settings.tsx, which does not exist. The "external signals writer" inv4.tsis not in this repository. - Two routers in
register.ts. The classic judge router runs only with"router": "judge".config.jsonships"v4", so routing changes belong inv4.tsandlib/. - Duplicated risk patterns.
HIGH_RISKinregister.tsandRISK_RULESinworkflows/run.jshold the same S3 text regexes. A comment says a test keeps them identical, but no test does. Change both by hand. V4 routing itself useslib/risk.tsandlib/consequence.ts. - Validator rules. Write every mods API call in full (
$.fs.read(...), neverconst fs = $.fs). Pass event names and$.env.getnames as string literals. - The
calls:line. The validator cuts this line at 1,000 characters, and the mods catalogue refuses a cut line. CI fails on it too. Busy host calls therefore go through the function declarations inlib/io.ts. Keep them as declarations that pass$through. - The
directorybranch. The Anthropic directory reads$only within one file, sotools/release.mts directorypublishes the shipped files withhooks/bundled into one module. Purelib/files stay separate. Never name a local variableonornextinhooks/. The portal's Validate button acceptsowner/repo@branch, so try a change there first. lib/effortless.tsis a verbatim port.tests/effortless-fidelity.test.mtsfails if its code differs from the vendored source. Put changes inlib/effortless-adapt.ts, each with its reason and a test.- The delegated reads estimate runs low.
tokensOfuses 3.5 chars per token, and code runs denser.EXTRACT_MAX_TOKENSleaves a wide margin, because Haiku's price rises fivefold above 100k prompt tokens. tests/golden/decisions.jsonpins optimizer and switch-check behavior. When a change to the assumptions moves a case, update that case on purpose.- Benchmark tracks stay apart. The AutoMode study uses
claude -p, the neutral track usesbench/runners/neutral-agent.mts, and a report never compares across them. Never weaken a rule inbench/README.mdunder "What keeps a run honest". Safety tasks run only on a sandboxed runner. npm testglobs one level. It runsbench/*.test.mts, so a test in abench/subfolder does not run.workflows/run.jshas a top-levelreturn.tests/workflow.test.mtswraps it in anAsyncFunctionwith mocked globals. Keep it as JavaScript.
Repository rules
- Follow the
ship-changeskill for every change: one issue, one branch and one pull request per task. The branch holds the task's code, tests and benchmark. Nothing writes tomaindirectly. - Publish every result everywhere. After a benchmark run that changes a public number, before a release PR merges, and for any edit to the benchmark docs or the website, follow the
publish-resultsskill. It updates the README,docs/compare.md, every website page, the website's FAQ data, the sitemap and the report in the same PR.bench/results/headline.jsonholds the public numbers, andtests/claims.test.mtsfails CI when any page drifts from the rows or keeps a stale claim. Never deliver a result to one page and leave the rest. - Do not change
versionin.claude-plugin/plugin.json. The release PR raises it, and also updates the README. Add a line under[Unreleased]inCHANGELOG.mdwhen you change a shipped path. - CI rejects personal absolute paths anywhere in the repository, and a
"text":field inbench/results/*.json. Never save model reply text or real session transcripts. - Do not claim a cost saving without a repeatable benchmark run. Under 30 tasks, a run is a pilot. See
bench/README.md. - Add or update a test with every behavior change. Keep one task per pull request. A plan with phases is still one task: its phases are commits on one branch, never separate pull requests.
- Write comments and docs in short sentences, in active voice, with one idea per sentence.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

