AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Router for Claude Code: a local hook (not a proxy) that recommends Ollama, Haiku, Sonnet or Opus per prompt. Local-first, MIT. Audited numbers in the README.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Operate like a master without studying every day — real multi-agent projects in VS Code. Local-first.README 전문 보기 ↗
What it is, in one paragraph. Mooter is a hook for Claude Code that classifies each prompt on your machine and recommends the cheapest tier that can do the job: local Ollama, Haiku, Sonnet or Opus. It is not a proxy: requests go from Claude Code to the model provider directly. Honest state, audited 2026-08-23: 101 of 123 classified prompts were routed to a local or cheap tier (82.1%), yet 1 of 3,225 recorded executions ran locally, so today Mooter mostly advises. No savings figure is published; see Honest numbers.
Mooter sets up, watches, and pilots your project with total visibility: foundation-gap alerts, best practices applied automatically, and the magic visible (Live Preview). The moat is trust: an auditable receipt and adversarial verification (critic ≠ author) on work a non-dev can check. The engine is table stakes; the cockpit is where the proof shows. Five experiences: Resume · Plan · Route (invisible) · Watch · Review.
Two-axis routing — complexity → model · domain → tools (Moo Packs). Zero-proxy · Doctrine-based · Self-tuning · GPU-aware.
⬆ main·a1b2 │ 🐮 mooter v1.0 · 🐮 Moo · CrazyMoo · LazyMoo │ [T3] ops arch 2.5s │ qwen 61% · hku 22% · son 13% · ops 4% │ 💻 RTX 4090 61% │ ●●◐○○○
Your statusline after a day of work: git · 🐮 brand + mode trio · current tier + classify · tier distribution · GPU · provider availability. No router. No proxy. No extra bill.
No savings figure appears above, and none appears anywhere in this repository. Since 2026-08-24 this project publishes no "% saved" or "$ saved" until a telemetry file records tokens. See Honest numbers.
Tier emojis — 🏠 T0 (local) · 🌸 T1 (Haiku) · 🎵 T2 (Sonnet) · 💎 T3 (Opus)
Public as of Wave 1 (2026-06-03). mooter is MIT-licensed — clone it, fork it, modify it. It's still early (single-user, opinionated, validated mostly on my own solo-founder workflow), so expect rough edges. Issues and PRs welcome.
The strategy and the full Mooter (axis-2) build log live in docs/strategy/MOOTER_PLAYBOOK.md — the single source of truth for the two-axis design. See NOTICE.md for commercial intent.
You use Claude Code. You love it. Then you look at the bill and realise you've been burning Opus tokens on tasks a sed one-liner could have handled. Renaming a variable cost you $0.12. A commit message cost you $0.08. A typo fix cost you $0.15.
Claude Code gives you the smartest model in the world by default — but smartest ≠ cheapest for every task. You wouldn't drive a Ferrari to buy groceries.
Existing "routers" for LLMs solve this with proxies: they sit between your client and Anthropic, intercepting requests and forwarding them to different providers. This creates:
mooter takes a different approach.
mooter doesn't intercept anything. It teaches Claude Code itself when to reach for Opus and when to delegate to Ollama, Haiku, or Sonnet. It does this through three mechanisms that work together:
A classifier hook (inject_context.js + classify.js) runs on every prompt before Claude Code processes it. Pure regex, <50 ms, zero LLM cost. It emits a <router-hint> with a recommended tier (T0/T1/T2/T3) and confidence score.
A mediator doctrine (~/.claude/CLAUDE.md) is a 165-line set of rules Claude Code reads at session start. It teaches the session how to interpret the hint, when to escalate, when to refuse, and which subagent to spawn for each tier.
Subagents for each tier — local-summarizer (Ollama), cheap-triage (Haiku), model-reasoner (Sonnet), model-architect (Opus), final-reviewer (Opus). Claude Code spawns them natively via the Agent tool. No ports. No external processes. No lock-in.
The result: Claude Code itself decides, on every prompt, which model is the cheapest capable tool for the job. The decision is explainable (the classifier returns a reasoning field), reversible (edit one file to override), and auditable (every decision is logged to decisions.log).
And because it's doctrine and not a proxy, if mooter dies, Claude Code still works. It falls back to default behaviour. Zero blast radius.
Most "routers" answer one question: which model is cheap enough for this prompt? That's axis 1 — complexity → tier (T0–T3). mooter shipped that first.
But the cheapest capable answer often needs more than a model — it needs the right tools: a skill, an MCP server, a project scaffold, the canonical library for that domain. So mooter adds axis 2 — domain → Moo Pack. The same regex hook that classifies complexity now also classifies domain, in parallel, and emits a second hint.
flowchart TD
P["User prompt"] --> H["UserPromptSubmit hook<br/>(pure regex, p99 < 4ms)"]
H --> A1["Axis 1 — classify_complexity()<br/>→ tier T0…T3"]
H --> A2["Axis 2 — classify_domain()<br/>→ Moo Pack"]
A1 --> RH["<router-hint><br/><b>which MODEL</b>"]
A2 --> PH["<pack-hint><br/><b>which TOOLS</b>"]
RH --> D{"Claude Code<br/>decides"}
PH --> D
D --> M["model:<br/>Ollama · Haiku · Sonnet · Opus"]
D --> T["tools:<br/>skills + MCPs + scaffold"]
Both hints are advisory and explainable (each carries a numeric confidence and a reason). The pack hint only fires above a confidence floor; below it, mooter stays out of your way (GENERAL) or asks (AMBIGUOUS). Backward-compatible by design — axis 2 never replaces the <router-hint>, it sits beside it.
Wave 1 validation: 20 real prompts, recall 20/20 (100%), combined hook p99 3.74 ms (budget: 60 ms). Full report: docs/wave1-validation.md.
A Moo Pack is a small, versioned YAML manifest that captures how to do a class of task well: the domain signals that identify it, the skills/MCPs it needs, the model floor it shouldn't drop below, the canonical libraries, and a smoke test. When classify_domain() matches a prompt to a pack, the <pack-hint> tells the session which tools to reach for — and mooter pack install <name> wires up any missing MCPs in one shot.
Three seed packs ship with Wave 1:
| Pack | Domain | Model floor | What it wires up |
|---|---|---|---|
animation-web | Web/UI animation (motion, GSAP, Lottie, parallax) | T2 | web-artifacts-builder skill · Vercel MCP · motion.dev/GSAP refs |
code-audit | Security/dependency/coherence audits | T3 | accessibility-review skill · Snyk MCP · audit scaffold |
diagram-systems | Diagrams & architecture (Mermaid, C4, ER, D2) | T1 | canvas-design skill · Mermaid/D2 scaffold |
Schema: packs/pack.schema.yaml. Strategy & roadmap (7 packs by end of Wave 1): docs/strategy/MOOTER_PLAYBOOK.md.
mooter now learns from its own decisions. Every night at 02:00, a scheduled task runs:
decisions.log ──► backtest.js ──► router-tuning.json ──► update-router.js ──► classify.js
│ │ │ │ │
│ analyzes 24h proposes new patches the reads TUNED
│ of routing regex patterns classifier block at
│ decisions + threshold idempotently runtime
│
└─────────────────────── grows smarter without human input ─────────────────►
What the backtest detects:
"task notification") that cost Opus tokens 3+ times for zero value.confidence < 0.6 landing on high tiers without evidence of risk.What it emits:
{
"complexity_threshold": 0.25,
"promote_to_t0_patterns": ["/\\bok\\s+vamos\\b/i"],
"demote_from_t3_patterns": ["/\\bdecompõe\\s+o\\s+sprint\\b/i"],
"notes": [
"Analysed 60 prompts.",
"Short prompts on high tier: 8 (13.3%).",
"Estimated additional savings if patterns demoted: $0.0490."
]
}
What the runtime does with it: classify.js reads the TUNED_DEMOTE_T3, TUNED_PROMOTE_T0, and TUNED_COMPLEXITY_THRESHOLD constants from an auto-generated block. Two passes (demote then promote) check the current prompt against the tuned regex list. A hard guardrail (high === 0) ensures no prompt with HIGH_RISK signals (push, deploy, migration, secret, architect, merge, CI...) is ever downgraded, regardless of what the backtest learned. The guardrail is dual-enforced — both at runtime in classify.js and upstream in backtest.js so bad patterns never even enter the tuning file.
Slash command: Paulo can force a retune at any time with /update-router — Claude Code runs the backtest and applies the patch in one step.
| Metric | Value |
|---|---|
| Prompts validated (v0.3) | 1,370 real prompts |
| T0 routing (free/local) | 83.9% |
| T2 routing (Sonnet) | 12.4% |
| T3 routing (Opus) | 3.6% |
| Hook p50 (v0.7) | 113ms (was ~3s pre-v0.7) |
| Classifier latency | 1ms cached (n=264) · 134ms p50 when a process spawns (n=498) — measured 2026-09-03 |
| Low-confidence rate | 2.0% |
| Savings vs naive Opus | not measured — see Honest numbers |
| Cost model | token-estimated, see docs/archive/2026-04-reports/COST_MODEL.md |
| Unit tests (loop + cost model) | 59/59 passing (node:test) |
| Projects validated on | marleyliving (CRM), cloude-home, misc |
This README used to carry five different savings figures — 65–82%, ~78-90%, 90%, 90.2% and a badge — and two of them were opposite readings of the same pair: a cost of 65–82% of baseline means a saving of 18–35%, not 65–82%.
On 2026-08-23 an audit traced every savings number this project publishes. None survived.
| number | where it lived | why it died |
|---|---|---|
| 0% | statusline | denominator was Bash calls, not prompts (26 calls per prompt) |
| 49.9% | backtest | no event filter; the same prompt counted up to 3× |
| 62.7% | decisions_v2 | that is the share of T0 lines, not a cost |
| 83.2% | stats.js | well-formed — but it measures the recommendation, and 101 prompts routed to local produced 1 local execution |
| 89.9% | project memory | April corpus, hand-written, no code produces it |
| the ‘658 calls’ figure | earlier launch posts (Wave 33.5); still quoted by AI assistants in Sep 2026 | same root cause, no token telemetry. Withdrawn 2026-09-28; the methodology page explains why |
The root cause is structural and worth stating plainly: no telemetry file in this project records tokens. Zero of 4,534 lines. Without tokens there is no measured cost, and without a measured cost there is no measured saving — in any unit.
In 123 classified prompts (2026-08-20 → 2026-08-23), the classifier routed 101 (82.1%) to a local or cheap tier. In the same sessions, of 3,225 recorded executions, 3,193 ran on Opus and 1 ran locally.
That gap — recommendation vs execution — is the honest state of the project, and closing it is the current work. Publishing a savings percentage on top of it would describe a product that does not exist yet.
guaranteed_saved (Option-A hits, where a local answer demonstrably replaced a paid one) is measured per session. It is the only dollar figure here with a denominator.
An audit of the live hook log (762 classifications) found two claims in this README, in CLAUDE.md and in AGENTS.md that described the minority case:
| number | what it actually is |
|---|---|
<50ms | true for 34.6% of sizings. The cache path is 1.0ms p50 (n=264); the spawn path is 134ms p50, 326ms max, and 0% under 50ms (n=498). The regex rule really does cost ~2ms — starting the process does not. |
$0 to classify | true of the rule, not of the shipped hook. When confidence is low it asks a cloud arbiter: 98 paid calls in the same log. |
Both were fixed in place rather than quietly deleted, because the sentence they lived in is the one every agent reads at startup.
| Persona | Why mooter helps |
|---|---|
| Solo founders burning Claude Code credits on trivial work | Trivial work stops reaching Opus — the saving is real but we do not yet measure it (no token telemetry) |
| Small teams sharing a company Anthropic account | Consistent routing across developers, visible spend per tier |
| Open-source maintainers using Claude Code in CI | Cap runaway agents at T2 automatically via the budget guardrail |
| Researchers & tinkerers who want a transparent, regex-based router | Every decision is explainable in <10 lines of code |
| Budget-conscious devs on the Anthropic 5-hour OAuth plan | Live statusline shows your budget burn in real time |
It is not for: production AI workloads where latency matters more than cost, or teams that need a hosted multi-tenant router. mooter is single-user, local, and opinionated.
┌─────────────────────────────────────────────────────────────────────────┐
│ Claude Code session │
│ │
│ user prompt │
│ │ │
│ ▼ │
│ UserPromptSubmit hook ──► inject_context.js ──► classify.js │
│ │ │
│ │ 1-134ms, regex │
│ ▼ │
│ <router-hint> injected │
│ │ │
│ ▼ │
│ Claude Code reads ~/.claude/CLAUDE.md (doctrine) │
│ │ │
│ ▼ │
│ Session decides: inline, or spawn which subagent? │
│ │ │
│ ├─► local-summarizer (Ollama, T0) │
│ ├─► cheap-triage (Haiku, T1) │
│ ├─► model-reasoner (Sonnet, T2) │
│ ├─► model-architect (Opus, T3) │
│ └─► final-reviewer (Opus, pre-merge gate) │
│ │
│ Every decision → decisions.log │
│ Every tool call → execution.log (exec-logger PostToolUse hook) │
└───────────────────────┬─────────────────────────────────────────────────┘
│
│ 02:00 daily (Windows Task Scheduler)
▼
backtest.js → router-tuning.json → update-router.js
│ │
│ │
└──── next prompt uses new rules ─┘
For a deeper walkthrough see ARCHITECTURE.md.
| Tier | Provider | Cost/prompt | When it's used |
|---|---|---|---|
| T0-general | Ollama (qwen3:30b) | Free | Summaries, brainstorm, translations, docs |
| T0-code | Ollama (qwen2.5-coder:7b) | Free | Formatting, renaming, commit messages, trivial edits |
| T0-math | Ollama (qwen2-math:7b) | Free | Calculations, data transforms, regex |
| T1 | Claude Haiku | ~$0.001 | Light code, explanations, translations, regex |
| T2 | Claude Sonnet, Codex CLI | ~$0.01 | Features, debugging, refactors, planning |
| T3 | Claude Opus | ~$0.05 | Architecture, multi-file refactors, final review, anything touching secrets/prod |
Want to swap providers? See docs/MODEL_MAPPING.md.
mooter reads your Anthropic OAuth usage and caps the maximum tier dynamically:
| 5-hour usage | Max tier | Effective ceiling |
|---|---|---|
| 0–50% | T3 | Full routing (Opus allowed) |
| 50–70% | T2 | Sonnet max, Opus blocked |
| 70–85% | T1 | Haiku max |
| 85–95% | T0 | Local / free only |
| >95% | T0 | Hard block on all paid tiers |
Runs every 2 hours in the background, displayed in the statusline as 5h:37% ↺2h14m.
TL;DR:
# macOS / Linux
curl -fsSL https://mooter.ai/install.sh | bash
# Windows (PowerShell)
irm https://mooter.ai/install.ps1 | iex
Or, from a cloned repo:
git clone [email protected]:pauloloureiroshp-ship-it/mooter.git
cd mooter
bash install.sh # macOS / Linux
.\install.ps1 # Windows
Requirements:
Mooter reached v1.0.0 by converging two version timelines that ran in parallel inside this repo. Both sets of git tags are preserved (never deleted) for provenance — so git tag shows more than the v1.0 headline, and this section explains why.
Timeline 1 — router engine (origin: "frugal", rebranded → "mooter" 2026-04-14)
v0.7.0 … v0.9.4 tags, 2026-04-08→10 landing app + beta (frugal)
→ v0.8.0 (arbiter) → v0.11.0 routing engine maturity
(tools/router/)
Timeline 2 — Mooter product waves
v0.1.0-pastor-wave1 … v0.6.6 tags, 2026-05-27→31
Pastor foundations → reveal/rebrand → safety/activation/sync →
auth/dashboard → Adapter Forge → statusline V2 → onboarding/install →
admin (privacy + charts + feedback) (packages/cli/ + landing/)
⬇ converge ⬇
v1.0.0 — single version, all three packages
(tools/router/ · packages/cli/ · landing/)
The same numbers (e.g. v0.6.x) meant different things on each timeline; v1.0.0 ends that ambiguity. Legacy tags stay for history; the going-forward version is v1.0.0.
See ROADMAP.md for the full version timeline, completed work, and deferred items.
| Version | Status | Highlights |
|---|---|---|
| v0.1.0 | ✅ Released | classify.js v1, 6 subagents, routing docs |
| v0.2.0 | ✅ Released | Mediator doctrine, stats.js, benchmark.sh |
| v0.3.0 | ✅ Released | replay.js, 1,370-prompt validation (the 90.2% quoted at the time is retracted — see Honest numbers) |
| v0.4.0 | ✅ Released | Statusline OAuth, budget guardrail, multi-provider |
| v0.5.0 | ✅ Released | Auto-learning loop: backtest, TUNED wire-up, 11 tests, pct_by_model |
| v0.6.0 | ✅ Released | Honest numbers: token cost model, BRL/EUR/GBP, guaranteed vs advisory savings |
| v0.6.1 | ✅ Released | In-prompt user override (usa o opus, @sonnet, force ollama, sem opus) + HIGH_RISK guardrail on downgrades, 25 tests |
| v0.7.0 | ✅ Released | Hook p50 3s→113ms (classify cache + async budget + Ollama warmup), quality-intent detection, T0 sub-tier specialists |
| v0.7.1 | ✅ Released | Provider availability indicator in statusline (Claude/Ollama/Gemini/GPT live/dim dots) |
| v0.7.2 | ✅ Released | Turn-latency measurement via Stop hook + ~Opus baseline estimate in statusline + dispatcher architecture analysis |
| v0.8.0 | ✅ Released | Haiku arbiter for ambiguous prompts (~17% long tail) — semantic understanding, HIGH_RISK dual-enforced, 56 tests, ~$0.27/mo extra cost for ~95% decision quality |
| v0.9.0 | 🟡 Planned | Parallel decomposition execution (arbiter already returns decomposition array, wire it to parallel subagent spawns) + arbiter metrics in statusline |
| v1.0.0 | ✅ Released (Convergence) | Unifies the router-engine timeline (v0.7→v0.11) and the Mooter wave timeline (v0.1.0-pastor → v0.6.6) into one version. See Genealogy. Zero production code changed — version + docs only. |
| v1.1+ |
| Command | What it does |
|---|---|
/mooter-status | Health check: hook active, Ollama live, hub reachable, last 5 decisions |
/mooter-savings | Economic report: session savings, projected annual, tier distribution |
/mooter-route <task> | Classify any task description before running it |
/mooter-summary | What the router decided this session (full breakdown) |
/mooter-update | Pull from GitHub + sync classifier (idempotent) |
/mooter-beast | Beast Mode: forces T3 (Opus) on everything until reset |
/mooter-zen | Zen Mode: caps at T1 (Haiku/Ollama) — great for focused writing |
/mooter-auto | Resets to intelligent auto-routing |
/mooter-hello | Onboarding walkthrough for first-time users |
/mooter-doctor | Full system diagnostic: hooks, models, log permissions, --fix mode |
/mooter-<model>Beyond the fixed commands above, mooter generates one slash command per model you can actually reach, discovered from your detected subscriptions. Run mooter doctor (or mooter init) to (re)generate them; they update automatically when a subscription appears or is revoked.
For an Anthropic subscription you get:
| Command | Pins | Tier | Subagent |
|---|---|---|---|
/mooter-opus-4-7 | Opus 4.7 | T3 | model-architect |
/mooter-opus-4-6 | Opus 4.6 | T3 | model-architect |
/mooter-sonnet-4-6 | Sonnet 4.6 | T2 | model-reasoner |
/mooter-haiku-4-5 | Haiku 4.5 | T1 | cheap-triage |
Type /mooter-sonnet-4-6 <your prompt> to pin Sonnet 4.6 for that one message — the next message returns to automatic routing. A pin that would downgrade a high-risk prompt (deploy / migration / secret) below its safety floor is refused, same as the router's own guardrail.
If mooter detects a ChatGPT subscription (Codex CLI), an OPENAI_API_KEY, or a local Ollama install, it also generates pins for those — e.g. /mooter-codex, /mooter-openai-gpt-5-4, /mooter-qwen3-30b (one per installed Ollama chat model).
⚠️ Honest UX caveat. Claude Code's session host is Claude, not the pinned model. A non-Anthropic pin therefore runs
router-execute.jsunder the hood and the reply comes back as a tool result the agent relays — not as a native answer from the model. It works, but it reads as quoted output rather than the model "speaking" directly. (Anthropic pins above don't have this — they dispatch to a native subagent.)
Single-message scope, same as Anthropic pins.
No silent fallback — if the pinned provider has no quota or fails, you get the error, never a quiet switch to a different model.
See per-provider cost / quota any time:
cat ~/.claude/tools/router/quota-state.json | jq .providers.openai_codex_cli
mooter-doctor.js is a cross-platform diagnostic tool that runs 12 health checks and can auto-fix common issues:
node ~/.claude/hooks/mooter-doctor.js # diagnose
node ~/.claude/hooks/mooter-doctor.js --fix # diagnose + repair
Checks: hook registration in settings.json, Ollama reachability, GPU probe, decisions.log write permission, exec-logger presence, statusline segments, CLAUDE.md doctrine version, hub connectivity, and more.
| Doc | Purpose |
|---|---|
| docs/strategy/MOOTER_PLAYBOOK.md | Two-axis routing SSoT — Mooter (axis 2 / Moo Packs) strategy & build log |
| docs/strategy/GLOSSARY.md | Canonical vocabulary — Mooter, Moos, to pastor, Moo card, Pack |
| docs/wave1-validation.md | Wave 1 live validation report (recall, latency, signals for Wave 2) |
| ARCHITECTURE.md | Deep dive into the technical design and rationale |
| ROADMAP.md | Version history and what's next |
| Setup | Install, configure, verify |
| REQUEST_ACCESS.md | Legacy access note (repo is now public) |
| CONTRIBUTING.md | For approved contributors |
| SECURITY.md | Vulnerability disclosure policy |
| CHANGELOG.md | Full version history |
Landing page: landing-five-azure-16.vercel.app
MIT — see LICENSE. See NOTICE.md para as marcas e os avisos de terceiros.
| 🔵 Planned |
| Learned classifier (BERT/DeBERTa-small) if corpus justifies it; Adapter Forge Docker auto-training (Wave 5 D3); plugin marketplace; MCP integration |
| NOTICE.md | Legal + commercial intent |
| docs/ROUTING_POLICY.md | Complete tier routing rules |
| docs/HOW_IT_WORKS.md | Request lifecycle diagram |
| docs/LIMITATIONS.md | What mooter does not do |
| docs/archive/2026-04-reports/REAL_CORPUS_VALIDATION.md | The 1,370-prompt benchmark |
| docs/MODEL_MAPPING.md | How to swap providers |
| docs/archive/2026-04-reports/COST_MODEL.md | How v0.6 measures savings (token-estimated) |
| PRIVACY.md | What data is collected and what stays local |
| ONBOARDING_GUIDE.md | 5-minute install guide for Mac and Windows |