Stop paying a premium model to find a missing import.
Token Clinic is a pre-flight gate for coding agents. It runs cheap, deterministic checks on your machine first, fixes for free what it can, sends only the genuinely-hard problems to a model — at the right price tier — and prints a receipt showing what you saved.
Most tokens in agentic coding are wasted having an expensive model rediscover what a cheap local tool already knows. Token Clinic does the cheap part locally and only pays for the rest.
- Install
- Quick start
- Commands
- Use it in Claude Code
- Configuration
- How it works
- Development
- Background & design
Token Clinic runs on Bun (v1+).
npm install -g tokenclinic # or: bun add -g tokenclinicThen run it anywhere:
tokenclinic scan ./my-projectNo install? Use npx:
npx tokenclinic scan ./my-projectWhat needs an API key: scan (including --json) and audit are free and run offline. Only scan --apply and learn actually call a model — those need ANTHROPIC_API_KEY.
Scope today: the live analyzers cover TypeScript/JavaScript (via the TypeScript compiler) plus any promoted ast-grep rules. Analyzers are a registry — adding Python/Rust/etc. is adding one entry (a
detect+ arun), the rest of the pipeline is language-agnostic.auditis already language-agnostic (it reads logs, not code).
# 1. See what's wrong in a TypeScript project — free, nothing leaves your machine
tokenclinic scan ./my-project
# 2. Let it fix the real problems with the right-priced model
export ANTHROPIC_API_KEY=sk-ant-...
tokenclinic scan ./my-project --applyscan prints findings and a bill:
🩺 Token Clinic — my-project
node project · 14 deps · 5 findings · prices: llm-intel
● TS2322 [semantic→sonnet-4-6] Type 'number' is not assignable to type 'string'.
src/index.ts:4
● TS6133 [local] 'unused' is declared but its value is never read.
src/index.ts:5
● TS2304 [mechanical→haiku-4-5] Cannot find name 'radius'.
src/index.ts:8
● TS2339 [semantic→sonnet-4-6] Property 'email' does not exist on type 'User'.
src/index.ts:16
Explanation of Benefits (estimated — LLM step stubbed)
5 findings
1 fixed on-device · $0.00
4 escalated to a model
→ claude-sonnet-4-6 2× $0.0070
→ claude-haiku-4-5 2× $0.0023
clinic spend $0.0093
naive cost $0.04 (dump each file at the top model)
saved ~$0.03 (77% cheaper)
Read it like this:
[local]findings are fixed on your machine for $0 — a model never sees them.[mechanical→haiku-4-5]/[semantic→sonnet-4-6]are escalated to the cheapest model that can handle that difficulty.- The clinic spend vs. naive cost line is the point: what you'd pay with Token Clinic vs. throwing whole files at a top model.
Read-only. Runs the type checker plus any local rules, sorts findings into a local ($0) lane and a model lane, and estimates the cost. Calls nothing, changes nothing.
tokenclinic scan ./my-projectUse it to preview the work and the savings before spending anything.
The live loop. For each escalated finding it sends a tight packet (the relevant lines, not the whole repo) to the routed model, gets a corrected snippet back, writes it, then re-runs the type checker to confirm the error is gone. A patch that makes things worse is automatically reverted. Costs shown are exact (from the API's usage).
export ANTHROPIC_API_KEY=sk-ant-...
tokenclinic scan ./my-project --applyRefuses cleanly if no key is set — run plain scan for the free estimate instead.
Point it at a log of your past LLM calls and it prints the bill backwards: how much you spent, and how much of it was "eliminable" — work a local tool could have done for $0. Runs entirely on the exported logs; no code leaves your machine.
tokenclinic audit ./my-llm-calls.jsonl🩺 Token Clinic — retroactive audit · my-llm-calls.jsonl
12 calls · $0.20 spent · prices: llm-intel
● eliminable 6 calls $0.09 42% of spend · killed on-device → $0
● routable 3 calls $0.05 24% of spend · re-priced to cheapest tier
● essential 3 calls $0.07 34% of spend · real reasoning → unchanged
eliminable-class fraction 42% (clearly large — build it)
projected spend $0.08 under the clinic loop
would have saved ~$0.12 (59% cheaper)
Log format — one JSON object per line (JSONL):
model, inputTokens, outputTokens are required. category (e.g. import, lint, refactor, design) is authoritative when present; otherwise the call is bucketed heuristically from task and the audit is flagged estimated.
When the same kind of problem keeps getting escalated, learn spends one model call to write a deterministic ast-grep rule that catches it — then validates that rule against generated test fixtures before trusting it. Once promoted, that rule runs locally in every future scan for $0. Pay once, run free.
export ANTHROPIC_API_KEY=sk-ant-...
tokenclinic learn ./my-projectPromoted rules are written to .tokenclinic/rules/ (commit them — they're a shared asset). Rules that fail their fixtures are quarantined, never run.
Same read-only scan, emitted as a machine-readable report instead of a pretty table. No model call. A host agent (see below) reads the advice and does the fixes with its own model.
tokenclinic scan ./my-project --json{
"eob": { "fixedLocally": 1, "escalated": 4, "saved": 0.032, ... },
"advice": {
"autoApply": ["<ids of free local-lane findings>"],
"escalate": [{ "id": "...", "file": "src/x.ts", "line": 4, "recommendedModel": "claude-sonnet-4-6" }]
},
"findings": [ { "rule": "TS2322", "lane": "model", "context": { "snippet": "...", "startLine": 1 }, ... } ]
}Inside a coding agent like Claude Code, the harness already owns the model, the key, and the bill — so Token Clinic shouldn't make its own calls there. Instead it runs as an advisory pre-flight gate: it does the free local elimination and hands the agent a tight packet for each remaining problem, and the agent fixes them with its own model.
A ready-to-use skill ships in skill/token-clinic/SKILL.md. Drop it into your Claude Code skills directory and the agent will run tokenclinic scan --json before any fix pass and act on the advice — fixing real problems from the packets instead of crawling your repo to rediscover them.
Only scan --apply and learn call a model. They accept either provider:
OPENROUTER_API_KEY— recommended. One key routes to any provider/model (OpenAI, Google, Anthropic, Llama, …) through OpenRouter, using the same ids the pricing catalog uses.ANTHROPIC_API_KEY— used directly forclaude-*models when OpenRouter isn't set.
Everything else (scan, scan --json, audit) works with no key at all.
Prices come from llm-intel (the OpenRouter catalog — every provider) at startup, with a built-in offline snapshot so scan never requires network. The footer shows which was used: prices: llm-intel or prices: snapshot. An unknown model is flagged, never priced with a made-up number.
By default, fixes route by difficulty to Anthropic models (mechanical → Haiku, semantic → Sonnet, architectural → Opus). Override per class with any model id, including other providers:
{
"mechanical": "claude-haiku-4-5",
"semantic": "openai/gpt-4o",
"architectural": "claude-opus-4-8"
}Pricing and the live calls resolve whatever you configure: with OPENROUTER_API_KEY set, --apply/learn route any model through OpenRouter; otherwise claude-* models go through the Anthropic SDK directly.
Each run writes a .tokenclinic/ directory into the scanned repo:
profile.json— detected deps + analyzershistory.jsonl— findings, spend, and savings over timerules/— promoted local rules (commit these)quarantine/— generated rules that failed validation
Every run reads it back, so the more you use Token Clinic on a repo, the cheaper and sharper it gets. Add the throwaway parts to your .gitignore if you like, but keep rules/ — that's the compounding asset:
.tokenclinic/history.jsonl
.tokenclinic/profile.json
.tokenclinic/quarantine/Four stages, one record (Finding) flowing through each:
| Stage | What it does |
|---|---|
| Triage | Run on-device analyzers (the TypeScript compiler + your promoted rules), normalize everything into one finding list. Most findings die here, for $0. |
| Diagnose | Split findings into the local ($0) lane and the model lane; for the model lane, assemble a tight context packet — the relevant lines, not the whole repo. |
| Treat | Route each escalation to the cheapest model that can handle its difficulty, apply the fix, then re-run the checker to verify. Revert anything that makes it worse. |
| Bill | Print the receipt: cost per fix and savings vs. dumping whole files at a top model. |
git clone https://github.com/mrdulasolutions/TokenClinic.git
cd TokenClinic
bun install
bun test # run the test suite
bun run typecheck # tsc --noEmit
bun run demo # scan the sample project
bun run demo:auditContributions welcome — see CONTRIBUTING.md. Security issues: see SECURITY.md.
Token Clinic was built command-by-command in a deliberate sequence — measure demand with audit before building the live loop, then add the amortization moat. The full product reasoning lives in docs/design-token-clinic.md. Changelog: CHANGELOG.md.
MIT © mrdulasolutions
{ "model": "claude-opus-4-8", "inputTokens": 1500, "outputTokens": 250, "task": "add missing import", "category": "import" }