hunch docs
Documentation

Hunch documentation

A shared record for AI agents: what was decided, what happened, and what still needs doing.

Hunch keeps decisions, rules, action records and commitments with their sources. A new agent can check that record instead of rebuilding the story from scratch. Engineering memory explains the reasons behind code; the served state layer extends the same approach to repository, user, team and organization records. Both ship today.

The vision is for agents working across different tools to share one current record. Wider organizational impact is still being tested in a pilot. Hunch checks defined rules about records and evidence; it does not make every submitted statement true or guarantee that an agent uses what it receives.

⚡Fast path: 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:

terminal
npm install -g @davesheffer/hunch

If the shell reports that hunch is not found, no global binary is required. From the repository directory, run npx -y @davesheffer/hunch@latest init.

Or run from source (for hacking on Hunch itself):

terminal
git clone https://github.com/davesheffer/hunch && cd hunch
npm install
npm run build        # compile to dist/
npm link             # optional: global `hunch` from source

The optional Hunch editor companion is available from Open VSX. It reads the same memory home as the CLI and MCP server; its version is separate from the CLI version.

Initialize a repo#

Run hunch init in the repo you want a memory for. One command scaffolds everything:

terminal
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-maintained CLAUDE.md, and registers a git merge driver for the graph.
  • Wires up every detected assistant — Claude Code, Cursor, VS Code (Copilot), Windsurf, Codex, Antigravity — to the same .hunch/ graph.
ℹInstalled releases write portable MCP launchers pinned to an exact Hunch npm version. Each teammate still runs init for local hooks and setup. In Matrix mode it also validates .hunch/team.json, clones the dedicated memory repo into ignored local storage, and writes this machine’s ignored pointer. Source-checkout installs may use local executable paths.

Update Hunch#

Ask an agent to “update Hunch”, or run hunch update from the repository root. If the shell reports that hunch is not found, run npx -y @davesheffer/hunch@latest update instead. It installs the latest release and aligns all configured harness pins while preserving existing settings.

For standalone npm projects, the command keeps Hunch in its current dependency section and saves an exact version. Without a repository dependency it updates the global CLI. Add --global to update both, or --dry-run to preview the commands. Other package managers and workspaces should update their dependency explicitly, then run hunch integrations repair-pins.

The update also repairs known legacy hook commands while preserving unrelated settings and intentionally disabled hooks. Restart or reconnect active assistant sessions afterward. In Codex, open /hooks to review and trust changed commands, then start a new session. Changing the pinned version changes the command and requires renewed trust; Hunch does not approve it for you. Each invocation updates the current repository; repeat it in other repositories as needed.

Installed interactive CLI commands may show a cached update notice. At most once every 24 hours, a detached worker asks npm for the package's public latest version. Hooks, MCP, CI, servers, the updater, non-interactive commands, and source checkouts skip that request. Set HUNCH_NO_UPDATE_CHECK=1 or NO_UPDATE_NOTIFIER=1 to disable it.

verify the connection
hunch integrations check --harness codex --probe --require mcp
hunch integrations check --harness codex --require context

The first command checks a fresh MCP process. Run the second after trusted hooks have run in a new Codex session: it checks recorded host delivery on the expected Hunch version. Configuration, delivery, and the agent's use of memory are different evidence. Requiring edit-blocking additionally needs strict firmness and a delivered pre-edit event.

Your first query#

Ask the codebase why — from the CLI, or in your assistant once the MCP server is registered:

terminal
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#

Optional synthesis drafts memory from commits and failures. Agent-initiated work stays with that agent's provider and its CLI authentication; an unavailable or unknown origin uses the deterministic local fallback. Human terminal invocations can use an explicitly selected provider. Installing a CLI alone does not select it. See agent origin and provider routing.

CLISubscriptionDetected by
claude (Claude Code)CLI's configured authenticationclaude --version
codex (OpenAI Codex)CLI's configured authenticationcodex --version
cursor-agent (Cursor)Cursorcursor-agent --version

A terminal provider preference lives in gitignored local config. It does not override a known initiating agent with another provider. hunch provider shows availability and why a provider was selected:

terminal
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):

terminal
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. --deep takes repeated samples through the bound provider and reconciles them. --verify checks the draft against the commit. These are model-assisted drafting steps, not independent proof; the result stays advisory. They do not fan out to another installed provider.

terminal
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 lets supported clients query the same memory. hunch init writes MCP configuration and guidance for supported assistants. Automatic context delivery and edit blocking depend on the host's lifecycle hooks, settings and trust; an MCP connection alone proves neither.

AssistantMCP configGrounding file
Claude Code.mcp.jsonCLAUDE.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.codex/config.tomlAGENTS.md + .codex/hooks.json (review with /hooks)
Antigravity.agents/mcp_config.jsonAGENTS.md + .agents/hooks.json

The server runs over stdio and exposes memory tools under hunch_*. State tools under nuryel_* are exposed when enabled for the configuration or state root, not by default in every plain repository. Use hunch mcp --root <partition-dir> for a served partition. Other MCP clients can register a launcher manually; starting it is not a connectivity test:

terminal
hunch mcp   # serves Hunch over stdio (this is what .mcp.json launches)
★Prefer 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 an update, a running assistant may still hold the old MCP process. Reload or reconnect your assistant in the repo so it loads the current tools. Codex hooks also require command trust: review changed commands with /hooks, then start a new session.

ℹThe on-disk directory is .hunch/ and the tools are hunch_*. If the tools don’t appear after a reload, run hunch doctor.

Private memory v0.17#

Keep sensitive decisions, bugs and constraints in a separate private overlay. Authorized local queries and checks can include that overlay; public-only outputs use the public store. Choose the destination when capturing private information and control access to the overlay through your local and Git permissions.

One command sets it up (no env var, no shell-profile edit):

terminal
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 is on by default. Captures attempt to commit to their actual memory home. A private/shared Git store can also be pushed; public memory in an application repository rides your next code-branch push. Managed grounding documents can be refreshed alongside memory. Failed or disabled synchronization can leave a record local, so inspect the saved durability result. Opt out with --no-auto-commit on hunch init, private or shared.

ℹSeparate public outputs from private inspection. Generated public guidance and scaffolded CI comments use public-only memory. Local queries and reports can contain private records, so review an artifact before sharing it. Keep HUNCH_PRIVATE_DIR unset in public CI; the scaffolded guard also uses --public-only. Private rules still need the same human authority and enforcement conditions as public ones.

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.

Shared state for agents#

Suppose one agent records a customer commitment and another agent ships the fix. Hunch can link the commitment, engineering decision and action receipt so either agent can check what is still open and what evidence supports completion. Agents continue to own their source tools and permissions.

Deterministic state means defined rules check record identity, repeated writes, access, conflicting current decisions and changes to supporting evidence. Hunch preserves the difference between an observation and a verified action record. It does not independently verify a CRM or email action merely because an agent records it.

hunch serve provides HTTP access to repository, user, team and organization partitions. Each token identifies a principal and its allowed partitions. To try one locally:

local state service
hunch serve init --config ./hunch-serve.json --partition user:demo --root ./demo-state --principal assistant@demo
hunch serve --config ./hunch-serve.json

Initialization prints the agent token once; keep it private. The server listens on 127.0.0.1. Use a Git repository for the partition when you need committed history; write results distinguish local, committed and pushed records. Read and write through HTTP, MCP, hunch state, or the typed TypeScript and repository-built Python clients. The Python package is not yet published to PyPI.

Open /operator on your server to inspect current records, completed work, commitments and writer-supplied field citations. The browser view is read-only. Optional per-record audiences limit which authorized principals can read or write a record. Optional key-bound credentials add proof from the configured private key on each request. Explicit user, team and organization conventions stay advisory.

Before enabling record visibility or key-bound authentication, upgrade and restart every serving process. Protected partitions refuse older state readers. Read the 1.33 upgrade guide, state CLI reference and Python client guide.

Engineering memory and this service ship today. Broader organizational results remain a pilot, with emulated runs and limited live use reported separately. Hunch is not a managed CRM, mail or chat connector service.

Read the product direction, state contract and setup reference, and dated pilot status.

Memory and Project DNA#

Memory records what matters: a decision and its reason, a rejected approach, a rule, a fixed bug or an open finding. Project DNA describes observed conventions: the repository's terminology, contribution habits and review expectations. It helps an agent fit the project without turning common habits into mandatory rules.

inspect the project
hunch why src/auth/session.ts
hunch dna inspect
hunch dna context

The code graph connects symbols and dependencies. Engineering Landscape records connect systems, services, owners and operational resources. hunch prove origin/main --public-only can bind an exact committed change to these records and its checks. A proof describes the evidence and gaps; it does not authorize a merge or deployment.

Read the Project DNA contract and change-proof guide.

See Hunch's contribution#

A task completion card and local report show what memory reached the agent, what the agent says it used, and which checks actually ran. A remembered lesson, an agent's attribution and a passing test are separate evidence; none alone proves that Hunch caused a better outcome.

inspect a task
hunch report
hunch report <task-id> --html

The agent reuses a task ID supplied by a trusted prompt hook, or starts one through hunch_task. It carries that ID through context and captures, runs relevant checks through the returned verification launcher, then finishes the task. Missing instrumentation stays unverified.

The HTML report is a saved local file and may contain private memory. Read task reports for evidence grades, privacy controls and the agent workflow.

The reasoning graph#

These core engineering records explain a project; the code graph connects them to files and symbols. Action records, commitments and other state extend the same store:

RecordWhat it captures
DecisionWhy a change was made — an ADR auto-drafted from each commit (context, decision, alternatives, consequences).
BugA root cause + its history (introduced → fixed → recurred), captured from a failing test.
ConstraintA do-not-break rule. Recurring or severe bugs become one; corrections create one directly.
FindingAn observed gap, incident or measurement with dated evidence, which may remain open.
Code mapHow your code connects — functions, files, and components — so Hunch can see the ripple effect of a change.

Durable memory is kept in readable files. Code and search indexes are derived data that Hunch can rebuild from the repository and its records.

Provenance & confidence#

Records carry provenance and confidence so you can distinguish an agent's observation from human-confirmed intent. A source label does not independently prove the statement. Captured memory starts as advisory context; blocking needs human authority and a supported enforcement path. hunch escalations surfaces conflicts and decisions that still need human review.

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:

terminal
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:

AGENTS.md
<!-- 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 fires doc-anchor-dangling. Only an explicit pin can fire — never a semantic guess. Unpinned markers ground but never drift.
  • Heal: hunch heal lists each drifted section with the decision to reconcile toward, then you update the pin. Read-only — it never rewrites your prose.
terminal
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).

terminal
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.

ToolWhat it does
hunch_queryFull-text + graph search across all records, ranked with provenance.
hunch_whyDecisions, bugs & constraints behind a file/symbol (accepts as_of).
hunch_check_constraintsInvariants whose scope matches a path/glob — call before editing.
hunch_get_dependentsTransitive dependents of a symbol — the blast radius.
hunch_blast_radiusDependent files + near-violations a change could break indirectly.
hunch_structureThe 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_impactThe 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_pathShortest dependency chain between two symbols/files/components, either direction — "how does A reach B?".
hunch_bug_lineageBugs matching a symptom/symbol with their introduced→fixed→recurred lineage.
hunch_contextThe minimal relevant slice for a task — invariants, why, bug history, blast radius.
hunch_timelineA target’s decision history over time, with valid-time windows.
hunch_merge_verdictA cited BLOCK / WARN / PASS over a working-tree, staged, branch, or commit diff (see below).
hunch_record_decisionWrite-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_decisionStart 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_decisionThe single in-force decision for a topic — the drift-detection anchor (with what it rejected).
hunch_record_correctionSave a scoped correction. Unconfirmed agent captures remain advisory; blocking needs human confirmation and strict enforcement (see Never Twice).
hunch_record_findingWrite-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_findingsThe open-observations ledger: live findings (open / accepted-risk / scheduled) for a file, glob, or symbol — inherit past audits instead of re-discovering them.
hunch_runbookThe proven steps for a recurring task (e.g. “add an MCP tool”) — kind-scoped retrieval over runbooks.
hunch_compareRank N candidate branches/commits by architectural fit (fewest invariant hits) — the “evaluate 5 solutions” check.
hunch_conformanceDoes 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:

CommandWhat 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-fragileA fragility report — the riskiest code, with evidence.
/captureGrill you through a decision one question at a time, then persist it against its topic (via hunch_capture_decision).
/healRead-only doc↔graph reconciliation — surface where a doc disagrees with the graph; never rewrites prose silently.

Firmness levels#

hunch init installs supported lifecycle hooks and guidance for the assistant. Where the host loads and trusts those hooks, they can deliver relevant memory before work. Firmness controls how Hunch responds; use integration checks to see which capabilities were actually observed in your host.

terminal
hunch firmness          # print the current level
hunch firmness strict   # change it (takes effect on the next edit; no restart)
LevelBefore an edit
offnothing (the hook is a no-op)
advisory defaultinject the relevant Hunch slice as context
firmadvisory + explicitly flag invariants in the file’s scope
strictfirm + deny a supported direct violation of a human-confirmed blocking constraint; indirect blast-radius warnings stay advisory
ℹUnsupported hook input or an internal hook error does not establish a successful check. Blocking requires a supported rule, human authority, strict firmness and host enforcement. Configuration alone cannot verify that chain.

The memory loop — it runs itself#

Captured memory becomes available as advisory context without routine manual triage. Genuine conflicts and authority decisions can still need review. Blocking power requires human confirmation. Configured hooks and synchronization handle much of the loop; task reports show what was observed:

terminal
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
PieceWhat it does on its own
Auto-trusta capture is live advisory memory instantly; migrate an old draft backlog once with hunch adopt-drafts
Self-repairrenames 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 escalationsonly 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-firstmemory 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.

ℹThe rule of the loop: advisory is automatic; blocking is always one explicit human act — and a rule whose bindings were auto-repaired asks once for a fresh proof before it can block again.

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.

The agent can save your correction as a scoped Constraint through hunch_record_correction. A capture without a verified human interview token remains agent-reported testimony, capped at warning severity. Human confirmation can give a supported rule blocking authority; strict firmness and a working host hook or CI guard are also required.

in your assistant
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 as advisory testimony without a capture token.
  Confirm the rule through the supported human review flow before relying on a deny.

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:

hunch_merge_verdict
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.

terminal
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.

hunch check
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.

terminal
hunch ci    # scaffold .github/workflows/ + the PR comment action
run a check locally
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:

CommandWhat it does
initScaffold .hunch/, index, install hooks, wire up assistants.
update [--global] [--dry-run]Install the latest Hunch release and repair every configured harness pin in the current repository.
indexParse the repo into a symbol/dependency graph (deterministic, no LLM).
backfillReplay git history to seed decisions (cold-start fix). --since 90d
syncCapture a decision from a commit (run by the post-commit hook).
queryFull-text + graph search. Add --semantic for embeddings recall.
whyExplain why a file/symbol is the way it is. --as-of <ref>
timelineThe decision history for a file/symbol over time.
contextAssemble the minimal relevant Hunch slice for a task.
fragileRanked fragility report with evidence.
checkFlag changes that touch a do-not-break invariant (local + CI guard); also flags (advisory) symbols you add that already exist elsewhere.
statusEnforcement readiness at a glance — what's enforcing, what's waiting for confirmation, what went stale.
vetoDecision Guard — flag changes that reverse a rejected alternative.
structureRepo map / dir contents / file outline / symbol definition — from the graph, no grep. hunch structure src/auth
impactThe impact surface of a change — dependents reached, invariants, decisions concerned. hunch impact origin/main, --commit <sha>, or staged by default. Advisory.
pathShortest dependency chain between two points: hunch path chargeUser verifySession.
driftFlag 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.
findingsThe 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-topicsScan for >1 live decision per topic (an invariant a git merge can violate) and exit non-zero. Wire into a post-merge hook / CI.
healRead-only doc↔graph reconciliation — report where prose and the graph disagree; never rewrites docs silently.
wikiGenerate 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.
ciScaffold the CI Constraint Guard (GitHub Action).
record-constraintRecord an invariant the codebase must not break.
record-bugCapture a Bug from a failing test (symptom + suspects).
testRun tests; capture failures as Bugs, promote recurrences to Constraints.
supersedeMark one decision as replaced by another (invalidate, don’t delete).
staleList decisions/constraints whose files changed after last verified (drift).
reviewTriage low-confidence drafts: list, accept, or reject.
firmnessGet or set how firmly the agent hook enforces Hunch.
embedGenerate local embeddings for semantic search (opt-in).
compactPrune low-value auto-captured records.
migrateUpgrade .hunch/ records to the current schema version.
mcpStart the MCP server over stdio.
doctorDiagnose the environment (git, synthesis provider, index freshness) + flag memory drift (dead refs, dangling supersedes, stale “proposed” docs).
conformIntent-conformance — prove the code still satisfies each decision’s recorded intent over the graph. --strict to fail.
compareRank candidate branches/commits by architectural fit. hunch compare feat-a feat-b --base main
runbookDistill a reusable runbook from a commit range, or --find <query> to look one up.
evalScore retrieval quality (Recall@k, MRR) over a golden set; A/B the graph stream. --kind runbooks
capture-commentsLift inline hunch-why: / hunch-rule: comments into the graph (decisions + constraints).
hookThe 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

terminal
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

terminal
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

terminal
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.

terminal
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

terminal
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

terminal
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

terminal
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

terminal
hunch sync <sha> --deep             # reconcile several drafts + fact-check the note
hunch review                        # then confirm it

Make an existing public repo's memory private

terminal
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

terminal
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

terminal
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).

terminal
hunch doctor
SymptomFix
Assistant shows no hunch_* toolsReload the assistant in the repo; if still missing, hunch doctor.
Synthesis is deterministicLog into Claude Code, Codex, or Cursor. If more than one is available, choose with hunch provider <name>.
Graph looks stalehunch index to rebuild the symbol graph from current source.
Schema mismatch after upgradehunch migrate to upgrade .hunch/ records.
Advertised team memory is unavailableCheck 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; You provide the Git hosting; the committed pointer contains no credentials.

Create that repository first, grant the team normal Git access, and have one maintainer connect it:

maintainer
npm i -g @davesheffer/hunch
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 the same Hunch version, 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

terminal
hunch firmness off
hunch shared --repo git@github.com:acme/project-hunch-memory.git --no-auto-commit
# Keep the installed version; inspect the problem before choosing a rollback.

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. Keep every client on the same supported Hunch version before resuming shared policy workflows, then run hunch shared --sync.

Maintenance#

Two housekeeping commands keep the graph healthy over time:

terminal
hunch compact   # prune rejected/superseded/stale low-value drafts
hunch stale     # list records whose files changed after they were last verified

Routine captures are available as advisory memory, 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.

audit
npm view @davesheffer/hunch version dist.integrity dist.attestations --json
git tag --list 'v*' 'vscode-v*'

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.