Hunch documentation
Install Hunch, connect your assistants, and make the reasoning behind your code available when the next change begins.
Hunch stores decisions, bugs, constraints, and code relationships as a git-native graph. Every connected assistant receives the same relevant evidence. Captured memory starts advisory; blocking is always a separate, explicit human choice.
npm i -g @davesheffer/hunch → cd your-repo && hunch init → reload your assistant and ask “why is X built this way?”. Use hunch backfill --since 90d when you want to seed an existing repo from history.Install#
Hunch needs Node ≥ 22.13. Install the CLI globally — that puts a hunch binary on your PATH:
npm install -g @davesheffer/hunchOr run from source (for hacking on Hunch itself):
git clone https://github.com/davesheffer/hunch && cd hunch npm install npm run build # compile to dist/ npm link # optional: global `hunch` from source
For the editor companion, install Hunch 0.17.3 from Open VSX. It reads the same memory home as the CLI and MCP server.
Initialize a repo#
Run hunch init in the repo you want a memory for. One command scaffolds everything:
hunch init # scaffold .hunch/, index, install hooks, wire up assistants hunch backfill --since 90d # cold start: seed decisions from recent git history
hunch init does the following, idempotently (re-running is safe and merges into existing files):
- Scaffolds
.hunch/and indexes the repo into a symbol/dependency graph. - Installs a post-commit hook (the learning loop) and a pre-commit Constraint Guard.
- Writes
.mcp.json+ slash commands + an auto-maintainedCLAUDE.md, and registers a git merge driver for the graph. - Wires up every detected assistant — Claude Code, Cursor, VS Code (Copilot), Windsurf, Codex — to the same
.hunch/graph.
init writes a .mcp.json pointing at this machine’s Node + Hunch, so each teammate runs it once. In Matrix mode it also validates the committed .hunch/team.json, clones the dedicated memory repo into ignored local storage, and writes this machine’s ignored pointer.Your first query#
Ask the codebase why — from the CLI, or in your assistant once the MCP server is registered:
hunch why src/auth/session.ts # decisions / bugs / invariants behind a file hunch doctor # git, schema version, synthesis mode
…and in your assistant: “why is the session module built this way?” — the assistant consults Hunch and answers with citations (record ids + provenance).
Synthesis & billing#
When Hunch turns a commit into a structured Decision, it normally uses your coding-assistant subscription through its CLI. Hunch is provider-agnostic: it uses a subscription automatically only when exactly one supported CLI is available. If several are installed, it stays on the free deterministic fallback until you choose which subscription to use. A configured local OpenAI-compatible endpoint is also supported; every public remote requires the deliberate HUNCH_SYNTH_ALLOW_METERED=1 opt-in.
| CLI | Subscription | Detected by |
|---|---|---|
claude (Claude Code) | Claude Pro / Max | claude --version |
codex (OpenAI Codex) | ChatGPT Plus / Pro | codex --version |
cursor-agent (Cursor) | Cursor | cursor-agent --version |
Your normal choice lives in gitignored local config, not the repository, so it can never spend a teammate’s plan. hunch provider shows what Hunch sees; select once locally, or override one shell with HUNCH_SYNTH_PROVIDER:
hunch provider codex-cli # local: Claude / Codex / Cursor / openai-compat / deterministic hunch provider # see availability + why this provider is selected export HUNCH_SYNTH_PROVIDER=claude-cli # one-shell override; `auto` disables the local choice
Local & self-hosted models. No subscription CLI is required: point Hunch at an OpenAI-compatible endpoint — Ollama, vLLM, LM Studio, llama.cpp server, and anything else that speaks the chat-completions format. Select it like any other provider (hunch provider openai-compat, or the one-shell override):
export HUNCH_SYNTH_PROVIDER=openai-compat # alias: ollama export HUNCH_SYNTH_BASE_URL=http://localhost:11434/v1 export HUNCH_SYNTH_MODEL=llama3.1 export HUNCH_SYNTH_API_KEY=... # optional — omit for keyless local servers export HUNCH_SYNTH_TIMEOUT_MS=300000 # optional, default 5 min — local models can be slow export HUNCH_SYNTH_MAX_TOKENS=2048 # optional output cap export HUNCH_SYNTH_ALLOW_METERED=1 # public remotes only; deliberate billing/trust opt-in
Opt-in only: leave these unset and Hunch behaves exactly as above. Localhost, private/link-local IPs, and conventional LAN names work directly. Every public remote requires HUNCH_SYNTH_ALLOW_METERED=1 so a newly launched paid provider can never bypass a stale hostname list; set it only when the endpoint is deliberately trusted and any billing is understood.
Deep Synthesis v0.19. --deep gathers several independent takes on a change and reconciles them into one note — trusting it more when they agree. --verify then fact-checks the note against the actual commit and drops anything the change doesn't support. The result is a more trustworthy note, and it always stays advisory until you confirm it. Fans out across whichever assistants (subscription CLIs and/or a configured local model) are available; falls back to a single draft when only one is.
hunch backfill --deep # higher-quality notes across history hunch sync <sha> --deep # a single commit (fact-check on) hunch sync <sha> --verify # fact-check a single note
MCP & assistants#
The Hunch MCP server is client-agnostic: one .hunch/ graph powers every assistant. hunch init scaffolds each tool’s MCP config plus an ambient grounding file so they all consult the same memory.
| Assistant | MCP config | Grounding file |
|---|---|---|
| Claude Code | .mcp.json | CLAUDE.md + /hunch-* commands |
| Cursor | .cursor/mcp.json | .cursor/rules/hunch.mdc |
| VS Code (Copilot) | .vscode/mcp.json | .github/copilot-instructions.md |
| Windsurf | .windsurf/mcp_config.json | .windsurf/rules/hunch.md |
| Codex / any | .codex/config.toml | AGENTS.md |
The server runs over stdio and exposes the hunch_* tools. You can start it manually to verify:
hunch mcp # serves Hunch over stdio (this is what .mcp.json launches)
hunch init over a global claude mcp add. .mcp.json is registered by file path, so it’s robust and travels with the repo.Reload your assistant#
After hunch init (or after pulling new Hunch versions), the MCP server config has changed but a running assistant session still holds the old process. Reload your assistant in the repo — reload the window / restart the session — so it respawns the server and picks up the hunch_* tools.
.hunch/ and the tools are hunch_*. If the tools don’t appear after a reload, run hunch doctor.Private memory v0.17#
Open-source your code without open-sourcing your reasoning. Keep sensitive decisions/bugs/constraints in a separate private location and Hunch unions it into every query and guard locally — your MCP tools and the pre-edit hook see it — while your public .hunch/ stays clean.
One command sets it up (no env var, no shell-profile edit):
hunch private # default ./.hunch-private (gitignored) hunch private ~/work/secret-memory # or a path you choose hunch private --repo git@github.com:you/mem-private.git # or clone a private repo
It writes a gitignored .hunch/local.json pointing at the store, installs the --private post-commit hook, and that’s it — Hunch, the MCP server, and the local VS Code extension auto-detect it; hunch doctor shows private: on → …. It’s opt-in and default-off: no local.json (and no HUNCH_PRIVATE_DIR) means the overlay is completely inert.
Record sensitive items with private: true on hunch_record_decision / hunch_record_correction; they’re written to the overlay, never the committed repo. Post-commit synthesis routes its auto-captured decisions there too, so a private-memory repo’s auto-decisions never land in the public tree. HUNCH_PRIVATE_DIR overrides local.json per-shell (e.g. for portability); the git hook lives in .git/hooks/ and is never committed.
Auto-commit (on by default, every mode). Each capture is auto-committed to the store it lands in: a private/shared overlay repo is committed and pushed; the public .hunch/ in a regular repo is committed only and rides your next push — Hunch never pushes or merges your code branch. It stages only the .hunch/ dir (never your other changes) and is recursion-safe. Opt out with --no-auto-commit on hunch init / private / shared.
CLAUDE.md grounding) and the CI PR comment render public-only — a private record can’t reach a public surface. A private constraint is still enforced on your machine (pre-edit hook + local hunch check), just never posted. Keep the var unset in CI; the scaffolded guard also passes --public-only and neutralizes it as defense-in-depth.Windows note#
On Windows, a global claude mcp add writes to ~/.claude.json keyed by the raw working-directory string. Drive letters are case-insensitive (c:\ and C:\ are the same folder) but Claude Code compares the key case-sensitively — so it can create two project blocks for one directory, and a session that resolves to the other casing sees no hunch_* tools.
If you hit this, run hunch doctor — on Windows it detects the split and heals it (merging the MCP servers across both casings, after backing up ~/.claude.json). hunch init runs the same heal automatically.
The reasoning graph#
Hunch stores four kinds of record as git-tracked JSON, plus the code graph that links them:
| Record | What it captures |
|---|---|
| Decision | Why a change was made — an ADR auto-drafted from each commit (context, decision, alternatives, consequences). |
| Bug | A root cause + its history (introduced → fixed → recurred), captured from a failing test. |
| Constraint | A do-not-break rule. Recurring or severe bugs become one; corrections create one directly. |
| Code map | How your code connects — functions, files, and components — so Hunch can see the ripple effect of a change. |
It's all saved as plain, readable files in your repo, with a fast local search index built from them on demand.
Provenance & confidence#
Every record is tagged with where it came from and how sure Hunch is — so nothing is a blind assertion. Captured memory enters the graph as live advisory context automatically (auto-trust, v1.8); only what you've personally vouched for is trusted enough to block a change (see firmness). The rare decision only you can make surfaces inline via hunch escalations — there is no review queue to manage.
Time-travel#
Decisions and constraints carry a git-anchored valid-time window. Supersession closes a window instead of deleting the record, so history stays auditable. Ask the graph what was believed at any past commit or tag:
hunch why src/auth/session.ts --as-of v0.7.0 hunch timeline src/auth/session.ts # the full decision history, newest first
The hunch_why and hunch_context MCP tools take the same optional as_of ref.
Decision-grounding#
Architectural Conformance keeps the code honest to the graph; decision-grounding keeps your docs honest to it. A decision can be anchored to a topic (e.g. auth.session), and a topic always has one live answer — the current decision, its history, and what was rejected along the way.
Capture with /capture. It interviews the decision to a resolved state — topic, rationale, rejected alternatives — before anything is written, and the graph refuses to hold two live decisions on one topic. A git merge is the one thing that can create two; hunch reconcile-topics catches it for human resolution.
Markdown topic anchors — AGENTS.md as a drift surface
Anchor a section of AGENTS.md, CLAUDE.md, or any repo markdown to a topic with an HTML comment:
<!-- hunch:topic auth.session --> # grounds only <!-- hunch:topic auth.session dec_a1b2c3d4e5 --> # pinned: prose written against that decision
- Grounding: whenever an assistant edits the file, the pre-edit hook injects each declared topic's current decision and what it rejected — follow the graph, not the stale prose being edited.
- Drift: a pinned marker whose decision has been superseded fires
doc-anchor-stale; a pin to a missing decision firesdoc-anchor-dangling. Only an explicit pin can fire — never a semantic guess. Unpinned markers ground but never drift. - Heal:
hunch heallists each drifted section with the decision to reconcile toward, then you update the pin. Read-only — it never rewrites your prose.
hunch drift # ⛔ exits non-zero on any anchor-stale drift or topic collision — wire into CI hunch heal # what to reconcile, and toward which decision hunch reconcile-topics # >1 live decision on a topic (post-merge) — exits non-zero
The component wiki & specs ledger#
hunch wiki renders a component wiki from the graph — one page per component (why it's shaped this way, invariants, structure, bug history, relations) plus a specs ledger that grades every markdown doc in your repo deterministically: ✅ grounded (anchored to current decisions — safe to trust), ⚠ stale (pinned to a superseded decision, says "proposed" about shipped code, or references only deleted files), ◻ unverified (no anchors — Hunch can't vouch either way). No LLM in any grade.
Stale docs get adopted. The wiki copies a stale doc into wiki/docs/ as a wiki-managed page healed against the graph — every stale pin re-pinned to the current decision with a 🧭 correction inline quoting what changed and what was rejected. Your original file is never touched; heal it (or delete it) and the copy retires automatically. The ledger routes readers to the trustworthy version either way.
Freshness is drift, not a cron job. Each page's graph inputs are content-hashed into a manifest; when the graph moves, hunch drift names exactly which pages went stale and hunch wiki --heal regenerates only those. Hand-editing a generated page trips the same wire (the graph is the source of truth — edit records, not pages).
hunch wiki # generate/refresh wiki/ + the specs ledger hunch wiki --check # CI gate: non-zero if any page is stale (green no-op if never adopted) hunch wiki --heal # regenerate ONLY the stale pages; retire orphans hunch wiki --private # render the FULL graph (private overlay included) into the OVERLAY repo
With a private overlay, the public wiki is built from the committed store only — a private record can never reach a committed page, by construction. --private writes the full-graph wiki into the overlay repo instead; nothing lands here.
MCP tools#
The tools your assistant calls under the hood. Read tools answer “why / what-must-not-break / blast radius”; write tools persist new memory.
| Tool | What it does |
|---|---|
hunch_query | Full-text + graph search across all records, ranked with provenance. |
hunch_why | Decisions, bugs & constraints behind a file/symbol (accepts as_of). |
hunch_check_constraints | Invariants whose scope matches a path/glob — call before editing. |
hunch_get_dependents | Transitive dependents of a symbol — the blast radius. |
hunch_blast_radius | Dependent files + near-violations a change could break indirectly. |
hunch_structure | The indexed shape of the repo / a dir / a file / a symbol — orient from the graph instead of grep rounds; tells the agent exactly which file to read. |
hunch_pr_impact | The impact surface of a change (working tree / staged / branch / commit): dependent files reached, invariants direct & near, decisions concerned. Advisory — the gate stays hunch_merge_verdict. |
hunch_path | Shortest dependency chain between two symbols/files/components, either direction — "how does A reach B?". |
hunch_bug_lineage | Bugs matching a symptom/symbol with their introduced→fixed→recurred lineage. |
hunch_context | The minimal relevant slice for a task — invariants, why, bug history, blast radius. |
hunch_timeline | A target’s decision history over time, with valid-time windows. |
hunch_merge_verdict | A cited BLOCK / WARN / PASS over a working-tree, staged, branch, or commit diff (see below). |
hunch_record_decision | Write-back: persist a new Decision (ADR) with provenance. Gated by topic — refuses a second live decision for a topic that already has one (never silently two); accepts an optional capture_token. |
hunch_capture_decision | Start a capture session — returns a one-question-at-a-time grilling protocol plus a capture_token to hand back to hunch_record_decision. |
hunch_current_decision | The single in-force decision for a topic — the drift-detection anchor (with what it rejected). |
hunch_record_correction | Write-back: turn a correction into an enforced invariant (see Never Twice). |
hunch_record_finding | Write-back: persist an observation with no code change (an audit that found a gap, a measurement, an incident) — anchored to a date + evidence, it grounds future edits to the affected files. |
hunch_findings | The open-observations ledger: live findings (open / accepted-risk / scheduled) for a file, glob, or symbol — inherit past audits instead of re-discovering them. |
hunch_runbook | The proven steps for a recurring task (e.g. “add an MCP tool”) — kind-scoped retrieval over runbooks. |
hunch_compare | Rank N candidate branches/commits by architectural fit (fewest invariant hits) — the “evaluate 5 solutions” check. |
hunch_conformance | Does the code still satisfy recorded intent? (e.g. pay still reaches verifySession) — deterministic, over the graph. |
Slash commands#
For Claude Code, hunch init also writes ergonomic slash commands:
| Command | What it does |
|---|---|
/hunch-why <file|symbol> | The decisions, invariants, and bug history behind it — with citations. |
/hunch-fix <bug> | Fix a bug grounded in past root causes, blast radius, and constraints. |
/hunch-fragile | A fragility report — the riskiest code, with evidence. |
/capture | Grill you through a decision one question at a time, then persist it against its topic (via hunch_capture_decision). |
/heal | Read-only doc↔graph reconciliation — surface where a doc disagrees with the graph; never rewrites prose silently. |
Firmness levels#
Telling an assistant “consult Hunch first” in a prompt drifts. Instead, hunch init installs agent hooks so grounding is enforced by the harness. How hard it pushes is one committed knob — set it once, it applies to the whole team:
hunch firmness # print the current level hunch firmness strict # change it (takes effect on the next edit; no restart)
| Level | Before an edit |
|---|---|
off | nothing (the hook is a no-op) |
advisory default | inject the relevant Hunch slice as context |
firm | advisory + explicitly flag invariants in the file’s scope |
strict | firm + deny an edit that hits a blocking invariant (directly or via blast radius), feeding the invariant back as the refusal reason |
strict only bites once you have blocking constraints recorded.The memory loop — it runs itself#
Since v1.8 there is no review queue. Every capture enters the graph as live advisory memory the moment it lands — grounding every assistant immediately — and nothing gains blocking power until you explicitly vouch for it. The whole loop is automated, local-first, and reversible:
hunch log # the memory timeline: capture · adopt · supersede · prune · repair hunch log --diff <sha> # what one move changed hunch revert-move <sha> # undo one move (local git revert, never pushed) hunch escalations # the decisions only YOU can make — normally empty hunch repair --apply # heal bindings after a rename (sync does this automatically) hunch push # the one deliberate outward step
| Piece | What it does on its own |
|---|---|
| Auto-trust | a capture is live advisory memory instantly; migrate an old draft backlog once with hunch adopt-drafts |
| Self-repair | renames heal every exact file binding — decision files, tripwire & constraint scopes, policy selectors — from git’s own rename detection; each repair is a revertable timeline move |
| Inline escalations | only what the graph can’t settle itself asks you: a topic conflict, a candidate rule, a proposed rule with its proof, a repaired rule needing a fresh proof — each a question with its resolution verb |
| Local-first | memory auto-commits locally and rides your next push; hunch push (or the panel’s Approve-to-push) is the explicit outward step |
In VS Code, the Hunch Memory panel is the same spine as a source-control view: the timeline with click-to-diff and one-click local revert, a ⚖ Needs your decision group that exists only while something real needs you, and a 🏛 Constitution group where activate / demote / withdraw / retire are each one audited click.
Never Twice v0.13#
The most expensive failure in AI coding is being corrected and then re-corrected: you say “no, never call X here”, the agent complies once, and next session it does it again — because the feedback was stored as advisory text, not enforced. Hunch closes that loop.
When you correct the agent, it captures the rule as a first-class Constraint (provenance human_confirmed) via hunch_record_correction — and from then on the same pre-edit hook + CI guard hold every assistant to it.
You: "no — never import lodash, we ship our own utils"
Agent: hunch_record_correction({ rule: "never import lodash; use src/utils",
scope_hint_file: "src/cart.ts", severity: "blocking" })
→ con_… recorded. A later edit that adds `import _ from "lodash"` to that scope is
DENIED (strict) and the PR fails CI — in Cursor, Copilot, Windsurf, or Claude Code alike.The UserPromptSubmit hook nudges the agent to persist a rule whenever your prompt reads like a correction. Scoping is conservative by default (the file you were in); a repo-wide rule is only blocking when you pass applies_to_all, so one correction can’t silently gate the whole tree.
Causal Merge Verdict v0.14#
A diff-only reviewer sees what changed. It can’t see that the line you’re deleting is the fix for an incident, that the symbol you’re re-adding was deliberately retired, or that the helper you’re adding already exists three modules over. Hunch can — because it holds the why and the whole graph.
hunch_merge_verdict (and hunch check / the CI guard) replays a diff against the graph and returns one cited verdict — BLOCK / WARN / PASS — that shows the reasoning, not just the rule:
VERDICT: ⛔ BLOCK — this change breaks a recorded invariant or re-opens a known bug.
⛔ pay() must verify the session before charging — con_pay
🧠 why: "Charge must verify the session first" (dec_pay)
🐞 guards against: Double-charge on unverified session — pay() charged without verifying (bug_…)No model in the loop, so it’s safe as a merge gate: it blocks only on a high-confidence rule you’ve confirmed, and warns on everything softer. Call it with {} to check staged changes, or pass base: "origin/main" for a PR range.
Decision Guard (Veto) v0.15#
Decisions record not just what you chose but the alternatives you rejected. The Decision Guard flags a change that reverses a decision — re-introducing an approach an in-force decision rejected — so the agent can’t quietly walk back a deliberate call.
hunch veto # flag changes that reverse a rejected alternative
Like the other gates: it warns by default and blocks under strict only on a rejected approach you’ve explicitly vouched for — the one-click activation in the VS Code Hunch Memory panel, or hunch review --accept <id> in the terminal. Blocking power is never granted automatically.
Regression Guard#
Re-adding a symbol or dependency that an in-force decision deliberately retired gets flagged — warns always, fails a strict commit only when the retirement is tied to a blocking invariant. The agent stops undoing intentional design. The pre-edit hook also surfaces what a decision retired from a file (“don’t re-add login here — dec_017 removed it”), and the commit-time hunch check does the actual gating.
Redundancy Guard v0.16#
Coding agents work from a local context window, so they re-implement a helper that already lives three modules over, or re-add a dependency the codebase already has — sprawl a diff-only reviewer can’t see. Hunch holds the whole symbol graph, so a diff that adds a function or class already defined elsewhere is flagged with its existing location.
Possibly re-implements 1 symbol(s) that already exist (advisory — review, never blocks): ⟲ adds function `formatDate` — already defined in src/util/date.ts
It’s advisory — it never blocks a commit; it surfaces in hunch check, the CI guard, and the merge verdict. It’s tuned to stay quiet, so a refactor that just moves code around isn’t mistaken for a duplicate.
CI Constraint Guard#
Memory that can hold a merge — when you've asked it to. hunch ci scaffolds a GitHub Action that runs hunch check on every PR and comments the affected invariants and decisions (with con_/dec_ ids and the causal why). The check fails only on a direct, high-confidence, non-stale blocking invariant — a rule you confirmed yourself.
hunch ci # scaffold .github/workflows/ + the PR comment action
hunch check --staged --strict # gate staged changes hunch check --base origin/main # gate a PR range (used in CI)
CLI reference#
Run hunch <command> --help for full flags. The everyday commands:
| Command | What it does |
|---|---|
init | Scaffold .hunch/, index, install hooks, wire up assistants. |
index | Parse the repo into a symbol/dependency graph (deterministic, no LLM). |
backfill | Replay git history to seed decisions (cold-start fix). --since 90d |
sync | Capture a decision from a commit (run by the post-commit hook). |
query | Full-text + graph search. Add --semantic for embeddings recall. |
why | Explain why a file/symbol is the way it is. --as-of <ref> |
timeline | The decision history for a file/symbol over time. |
context | Assemble the minimal relevant Hunch slice for a task. |
fragile | Ranked fragility report with evidence. |
check | Flag changes that touch a do-not-break invariant (local + CI guard); also flags (advisory) symbols you add that already exist elsewhere. |
status | Enforcement readiness at a glance — what's enforcing, what's waiting for confirmation, what went stale. |
veto | Decision Guard — flag changes that reverse a rejected alternative. |
structure | Repo map / dir contents / file outline / symbol definition — from the graph, no grep. hunch structure src/auth |
impact | The impact surface of a change — dependents reached, invariants, decisions concerned. hunch impact origin/main, --commit <sha>, or staged by default. Advisory. |
path | Shortest dependency chain between two points: hunch path chargeUser verifySession. |
drift | Flag doc≠graph drift — a file anchored to a superseded decision (anchor-stale), a markdown pin to a superseded/missing decision (topic anchors), a live finding whose anchor evaporated (finding-stale), plus topic collisions. Deterministic; exits non-zero for CI. |
findings | The open-observations ledger — audited gaps/debt with no fix landed yet, anchored to a date + evidence instead of a commit. hunch findings src/procs/**, --all for history. |
reconcile-topics | Scan for >1 live decision per topic (an invariant a git merge can violate) and exit non-zero. Wire into a post-merge hook / CI. |
heal | Read-only doc↔graph reconciliation — report where prose and the graph disagree; never rewrites docs silently. |
wiki | Generate the component wiki + specs ledger from the graph. --check CI gate, --heal regenerates only stale pages, --private renders the full graph into the overlay repo. |
ci | Scaffold the CI Constraint Guard (GitHub Action). |
record-constraint | Record an invariant the codebase must not break. |
record-bug | Capture a Bug from a failing test (symptom + suspects). |
test | Run tests; capture failures as Bugs, promote recurrences to Constraints. |
supersede | Mark one decision as replaced by another (invalidate, don’t delete). |
stale | List decisions/constraints whose files changed after last verified (drift). |
review | Triage low-confidence drafts: list, accept, or reject. |
firmness | Get or set how firmly the agent hook enforces Hunch. |
embed | Generate local embeddings for semantic search (opt-in). |
compact | Prune low-value auto-captured records. |
migrate | Upgrade .hunch/ records to the current schema version. |
mcp | Start the MCP server over stdio. |
doctor | Diagnose the environment (git, synthesis provider, index freshness) + flag memory drift (dead refs, dangling supersedes, stale “proposed” docs). |
conform | Intent-conformance — prove the code still satisfies each decision’s recorded intent over the graph. --strict to fail. |
compare | Rank candidate branches/commits by architectural fit. hunch compare feat-a feat-b --base main |
runbook | Distill a reusable runbook from a commit range, or --find <query> to look one up. |
eval | Score retrieval quality (Recall@k, MRR) over a golden set; A/B the graph stream. --kind runbooks |
capture-comments | Lift inline hunch-why: / hunch-rule: comments into the graph (decisions + constraints). |
hook | The agent pre-edit hook handler (installed by init; reads the event JSON on stdin) — injects relevant memory before an edit, and at strict firmness denies edits that hit a blocking invariant. Never blocks on its own failure. |
merge-driver | (internal) git merge driver for .hunch/ JSON — resolves concurrent edits by record id, no conflict markers. Wired by init. |
Common workflows#
Copy-paste sequences for the everyday tasks. Run hunch <command> --help for every flag.
Set up a new repo
hunch init # scaffold, index, install hooks, wire up assistants hunch backfill --since 90d # seed decisions from recent history (no empty day-1) # …reload your assistant, then ask: "why is X built this way?"
Join a repo that already uses Hunch
git pull # the .hunch/ graph travels with the code hunch init # wire YOUR assistants to the shared graph (idempotent) # …reload your assistant — the team's memory is now in context
Ask why a file is the way it is
hunch why src/auth/session.ts # decisions, bugs & rules behind it hunch timeline src/auth/session.ts # how the thinking changed over time hunch why src/auth/session.ts --as-of v1.2.0 # what was believed at a past release
Turn a correction into an enforced rule
Tell your assistant the rule (it records it for you), or do it from the CLI — then confirm it so it actually blocks. Name the actual violation with --forbid-dep (or --forbid-symbol): the rule then blocks the real change across the file's whole life and stays quiet on edits that don't break it, where a scope-only rule relaxes to advisory once the file is edited again.
hunch record-constraint "never import lodash; use src/utils" \ --scope "src/**" --severity blocking --forbid-dep "lodash" hunch review --accept <id> # confirm → now held against every assistant
--forbid-dep matches the parsed import (a comment or string naming the module can't trip it; a submodule like lodash/groupBy is caught). A correction your assistant records is given the same matcher automatically. --match <regex> remains as a lint-grade textual fallback. None of these are bypass-proof — deliberate indirection can still route around any rule.
Curate the draft queue
hunch review # segmented: ready-to-confirm vs needs-scrutiny hunch review --accept-verified # batch-accept the well-grounded ones hunch compact # prune rejected / superseded / stale low-value drafts
Check a change before you merge
hunch check --staged --strict # does this break a do-not-break rule? hunch veto --staged # does it reverse a rejected decision? hunch conform --strict # does the code still honor recorded intent? # …or check a whole PR range instead of staged files: hunch check --base origin/main
Add the CI merge gate
hunch ci # scaffold the GitHub Action (runs on every PR) git add .github && git commit -m "ci: add Hunch constraint guard"
Capture a higher-quality note for an important commit
hunch sync <sha> --deep # reconcile several drafts + fact-check the note hunch review # then confirm it
Make an existing public repo's memory private
hunch private --repo git@github.com:you/mem-private.git --migrate # moves existing memory into the private store, makes this repo code-only, # and prints the one git command to commit the now-clean public repo
Work across branches & worktrees
hunch worktree ../feature-x -b feature-x # new worktree, already sharing memory hunch doctor # confirms "worktree: linked — sharing the repo's memory"
It's not working
hunch doctor # git, synthesis provider, index freshness, drift hunch index # rebuild the code map from current source hunch migrate # after an upgrade, bring records to the latest schema
Where it’s stored#
The source of truth is plain, readable JSON in a Git repository you control: under .hunch/ in the code repo by default, or in a dedicated private memory repo in Matrix mode. The code repo then carries only the credential-free .hunch/team.json; local paths and the derived SQLite search index stay ignored and can be rebuilt at any time.
Doctor & troubleshooting#
hunch doctor is the first stop for anything that looks off — it reports git status, the resolved synthesis provider, schema version, index freshness, and record counts (and on Windows, heals the ~/.claude.json casing split).
hunch doctor| Symptom | Fix |
|---|---|
Assistant shows no hunch_* tools | Reload the assistant in the repo; if still missing, hunch doctor. |
Synthesis is deterministic | Log into Claude Code, Codex, or Cursor. If more than one is available, choose with hunch provider <name>. |
| Graph looks stale | hunch index to rebuild the symbol graph from current source. |
| Schema mismatch after upgrade | hunch migrate to upgrade .hunch/ records. |
| Advertised team memory is unavailable | Check Git access and .hunch/team.json, then run hunch shared --repo <the-same-url>. Hunch refuses to fall back to another graph. |
Working as a team — the Matrix v1.9#
Matrix mode gives a project one living engineering-memory spine across teammates, branches, worktrees, agents, and operating systems. The source of truth is a separate private Git repository you control; Hunch hosts nothing and never places credentials in the code repository.
Create that repository first, grant the team normal Git access, and have one maintainer connect it:
npm i -g @davesheffer/hunch@1.9.4 hunch shared --repo git@github.com:acme/project-hunch-memory.git git add .gitignore .hunch/team.json git commit -m "chore: connect shared Hunch memory" git push
Use a credential-free HTTPS or SSH URL and leave authentication in SSH or the Git credential helper. Add --migrate only when moving existing public .hunch/ records into the dedicated store.
After the pointer commit lands, each teammate installs v1.9.4, pulls, and runs hunch init. Hunch validates .hunch/team.json, creates an ignored .hunch-private/ clone, and merges the local path and preferences into ignored .hunch/local.json. An unparseable local config is never overwritten.
Always pumping. CLI operations attempt a bounded pull at startup; long-lived MCP servers refresh at tool-request boundaries. Captures commit and synchronize the memory-only repository by default, concurrent JSON records merge by identity, and a failed push stays local for hunch shared --sync to retry.
Authority stays fail-closed. Captures become advisory memory immediately. Blocking policy authority still requires an explicit audited human action, and public receipts should use --public-only so the shared graph never appears on a public surface.
Pause or roll back without deleting memory
hunch firmness off hunch shared --repo git@github.com:acme/project-hunch-memory.git --no-auto-commit npm i -g @davesheffer/hunch@1.8.5
This preserves every shared and local memory record while disabling enforcement and automatic publication. Revert the .hunch/team.json setup commit only if new clones must stop discovering the Matrix; do not delete the memory repo or local overlay. Upgrade every client to v1.9 before resuming Matrix policy workflows, then run hunch shared --sync.
Maintenance#
Two housekeeping commands keep the graph healthy over time:
hunch compact # prune rejected/superseded/stale low-value drafts hunch stale # list records whose files changed after they were last verified
Since v1.8 there is no draft backlog to triage: captured memory auto-trusts as advisory the moment it lands, and hunch escalations asks you inline about the few things the graph can't settle itself. Migrating an older repo? One hunch adopt-drafts clears a legacy queue, and hunch log shows every memory move — each one revertable.
Release integrity#
Hunch tests release artifacts before publication credentials are available. The immutable npm tarball and VSIX are then passed to isolated publishers, which verify the public registries converged on the expected version. npm publishes with GitHub Actions provenance; one tested VSIX goes unchanged to Open VSX and is downloaded again for byte verification.
npm view @davesheffer/hunch@1.9.4 version dist.integrity dist.attestations --json git tag --list v1.9.4 vscode-v0.17.3
Inspect GitHub Actions, npm, and Open VSX for the public receipts.
Develop & contribute#
Hunch is open source under Apache-2.0. Contributions are welcome — see CONTRIBUTING.md and the repo to get started.