Skip to main content
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

The folder must not exist. Recipes: 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

Writes the hunch skill to .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

Needs the 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

Writes two files beside the results file (see below), named after the spec or folder: <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

test exits 1 both when a check fails and when --max-cost stops it from asking (for example, order_stability needs new answers). Read the last line of output to tell them apart.

Reading the output

compile

run

  • First line: answers cached and asked, requests, input tokens, actual cost.
  • rows below act: confidence under act; review shows 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

When test 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:
Columns: kind, row id, question, model answer and confidence, answer key (- 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.