path is a spec file or a folder of specs. Every command lints the whole project first: warnings go to stderr; an error exits 2.
Commands
Commands ask only for answers the store lacks.
--max-cost 0 guarantees no spend: if anything is missing, the command stops before asking. For suggest, the cap also covers each writer call at its worst case.
hunch init
agent-commands (a guard for 38 real coding-agent shell commands, the quickstart, about $0.002), agent-eval (check a coding agent’s work from its trace), tickets (40 made-up support tickets: a choice, a yes/no and a score).
hunch skill
.claude/skills/hunch/SKILL.md (Claude Code) and .agents/skills/hunch/SKILL.md (Codex; Cursor reads both). It tells the agent how to run the loop (lint, compile, run with a cost cap, test, diff) and what not to do: spend without --max-cost, write *.reviews.csv, or edit .hunch/. Each file prints installed, updated or up to date; an existing copy is replaced, so re-run after upgrading hunch and commit the result.
hunch hook
install copies the command guard (spec, 38 labelled commands, NOTICE) into hunch/, keeping files already there, adds .hunch/ to .gitignore, and adds a PreToolUse hook with matcher Bash and a 30-second timeout to .claude/settings.json and .codex/hooks.json, keeping the rest of each file. It writes nothing if a settings file can’t be read. The hook command finds the spec through $CLAUDE_PROJECT_DIR (Claude Code) or the git root (Codex), and ends in || echo a warning, so a missing or older hunch lets commands through instead of blocking them.
run reads the agent’s event, judges the command with request (the last message the person typed, from a Claude Code transcript; empty otherwise), cwd, description and command, and logs the row, redacted by the spec’s rules, for --traffic. When any answer is yes or routes to review, it replies ask, or deny with --deny (Codex, which cannot ask yet), with the reasons; otherwise it prints nothing. It never replies allow. On any error it prints a systemMessage warning and lets the command through. HUNCH_MAX_COST defaults to 0.01 per command. See Guard your coding agent.
hunch distill
distill extra. Asks nothing: it reads the store’s answers for --node (default: the last judgment) and writes distilled/<judgment>@<hash>/ next to the spec (meta.json and weights.npz). Per question it trains a linear layer on a frozen MiniLM encoder, class-balanced, on the row’s gold where there is some and the engine’s answer otherwise, holding out half the gold rows by a hash of their text. It prints each question’s training size and rarest answer, warning under 30 examples, and the lines to measure and use it. See Distill a decision.
hunch docs
<name>.html, one self-contained page with no server or network needed, and <name>.manifest.json. It reads the specs, the results of the last test of the same path and the store’s run log, and never asks the engine. See Share what your judgments decide.
The manifest
Flags
--sample picks rows by a hash of the key, so a repeated sample comes from the store and a later full run asks only the rest. run --sample caches its answers but does not replace any table, since downstream readers would take the sample for the whole.
Exit codes
Reading the output
compile
run
- First line: answers cached and asked, requests, input tokens, actual cost.
rows below act: confidence underact;reviewshows them as “model unsure”.- The run id is stored on every table row (Store).
- stderr shows progress while asking:
asking 529 answers in 529 requests (~$0.0168)and the request rate.
test
Real output, BANKING77 holdout (hunch test intent.yml --source banking77_holdout.csv), trimmed:
The results file
Whentest finishes, it also writes what it printed as JSON, for CI, dashboards and agents: .hunch/target/<tested path>.json, beside the store, with the path of the tested spec or folder relative to the store’s folder (hunch test examples/tickets/triage.yml writes .hunch/target/examples/tickets/triage.json). With --model, the engine’s suffix is added, as for its tables (triage__deepseek_deepseek_flash.json), so another engine’s results never replace the spec’s own. test removes the file when it starts, so a run stopped by --max-cost leaves none rather than an old one.
diff
Real output, BANKING77 (hunch diff intent.yml --against intent_bare.yml), trimmed:
Both sides use today’s reviews.
review
See Review for the screen and keys. --list prints one row per item:
- if none), start of the row.
Verdicts saved to <judgment>.reviews.csv (columns qid,row_id,state_hash,verdict,label,reviewer,at,kind):
kind says why the row was reviewed: disputed, audit (a random spot check), uncertain, shadow, or any other name you write when importing verdicts (for example flagged). Only audit rows feed the accuracy estimate; every verdict counts as gold. test also marks a row same_text when it takes its gold from another row with identical text: gold, but not a spot check.