@davesheffer/hunch 0.12.0 β 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +379 -345
- package/dist/cli/index.js +12 -6
- package/dist/cli/invocation.js +19 -2
- package/dist/core/paths.js +8 -0
- package/dist/extractors/git.js +6 -0
- package/dist/integrations/ciAction.js +19 -6
- package/dist/integrations/scaffold.js +30 -30
- package/dist/mcp/server.js +5 -4
- package/dist/store/hunchStore.js +54 -39
- package/dist/store/schema.js +79 -79
- package/dist/synthesis/provider.js +4 -4
- package/package.json +68 -68
package/README.md
CHANGED
|
@@ -1,345 +1,379 @@
|
|
|
1
|
-
# π§ Hunch β Engineering Memory OS
|
|
2
|
-
|
|
3
|
-
> Git stores *what* the code is. **Hunch** stores ***why*** it is that way β a persistent,
|
|
4
|
-
> git-native reasoning graph over your codebase, surfaced to Claude Code at reasoning time
|
|
5
|
-
> so the AI stops re-deriving understanding and stops undoing intentional design.
|
|
6
|
-
|
|
7
|
-
## The problem
|
|
8
|
-
|
|
9
|
-
Every AI coding session starts from zero. The model re-reads your code, re-guesses the
|
|
10
|
-
intent, and happily "fixes" the thing you deliberately did last month β because the
|
|
11
|
-
*reasoning* behind the code lives in PRs, Slack, and people's heads, not in the repo.
|
|
12
|
-
|
|
13
|
-
**Hunch** captures that reasoning as a **byproduct of normal work** β commits and test
|
|
14
|
-
failures β stores it as a git-tracked graph next to your code, and feeds it back to
|
|
15
|
-
Claude Code so every session is grounded in the decisions, bugs, and invariants that
|
|
16
|
-
came before. Local-first, no documentation toil, no SaaS.
|
|
17
|
-
|
|
18
|
-
## How it works
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
commit / test failure .hunch/ (git-tracked JSON) Claude Code
|
|
22
|
-
βββββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββ
|
|
23
|
-
β post-commit hook ββββΌβββββββββΆβ Decisions (why a change) ββββββββββΆβ MCP tools β
|
|
24
|
-
β record-bug ββββΌβββββββββΆβ Bugs (root causes) β read β /hunch-* cmds β
|
|
25
|
-
β structured diff + β write β Constraints(invariants) βββββββββββ CLAUDE.md β
|
|
26
|
-
β Claude (or heuristic) β β Components / Symbols/Edges β β CLI β
|
|
27
|
-
βββββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββ
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
- **Index** (no LLM): tree-sitter parses your repo into a symbol/dependency graph β
|
|
31
|
-
functions, call edges, imports, components β plus churn and fan-in metrics.
|
|
32
|
-
- **Learn**: each commit becomes a structured **Decision** (an ADR); a failing test
|
|
33
|
-
becomes a **Bug** with a ranked suspect list; recurring or severe bugs are promoted
|
|
34
|
-
into **Constraints** (do-not-break invariants) and raise a component's *fragility*.
|
|
35
|
-
- **Ground**: Claude Code reads it through an **MCP server**, an auto-maintained
|
|
36
|
-
**`CLAUDE.md`**, and **slash commands** β every answer cites `provenance`
|
|
37
|
-
(source + confidence + evidence), so nothing is a blind assertion.
|
|
38
|
-
|
|
39
|
-
## Getting started
|
|
40
|
-
|
|
41
|
-
### 1. Install it
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm install -g @davesheffer/hunch # puts `hunch` on your PATH
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Or run from source (for hacking on Hunch itself):
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm install
|
|
51
|
-
npm run build # compiles to dist/
|
|
52
|
-
npm link # optional: puts a global `hunch` on your PATH
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Either way you then type `hunch β¦`. From a source checkout without `npm link`, use
|
|
56
|
-
`node dist/cli/index.js β¦` (or `npm run hunch -- β¦` to run via tsx). The rest of this
|
|
57
|
-
README uses `hunch` for brevity.
|
|
58
|
-
|
|
59
|
-
### 2. (Recommended) make a coding-assistant CLI available
|
|
60
|
-
|
|
61
|
-
Hunch's LLM synthesis is billed to **your subscription** through a coding-assistant
|
|
62
|
-
CLI β **never** a pay-per-token API key (the API key is stripped from the child env).
|
|
63
|
-
Hunch auto-detects the first one present, in this order:
|
|
64
|
-
|
|
65
|
-
| CLI | Subscription | Detected by |
|
|
66
|
-
|---|---|---|
|
|
67
|
-
| `claude` (Claude Code) | Claude Pro/Max | `claude --version` |
|
|
68
|
-
| `codex` (OpenAI Codex) | ChatGPT Plus/Pro | `codex --version` |
|
|
69
|
-
| `cursor-agent` (Cursor) | Cursor | `cursor-agent --version` |
|
|
70
|
-
|
|
71
|
-
If none is installed, Hunch still works using a deterministic structural heuristic
|
|
72
|
-
(lower-confidence drafts). `hunch doctor` tells you which mode you're in; force one
|
|
73
|
-
with `HUNCH_SYNTH_PROVIDER=claude-cli|codex-cli|cursor-agent|deterministic`.
|
|
74
|
-
|
|
75
|
-
### 3. Initialize the repo you want a memory for
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
hunch init # scaffold .hunch/, index, install the post-commit hook,
|
|
79
|
-
# write .mcp.json + slash commands + CLAUDE.md, register the merge driver
|
|
80
|
-
hunch backfill --since 90d # cold start: seed decisions from recent git history
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
`init` writes a `.mcp.json` pointing at *this machine's* node + Hunch β so **reload
|
|
84
|
-
Claude Code in the repo** afterward to pick up the `hunch_*` tools. (Each teammate runs
|
|
85
|
-
`hunch init` once to wire up their own clone; the captured `.hunch/` content is shared
|
|
86
|
-
via git.)
|
|
87
|
-
|
|
88
|
-
### 4. Use it
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
hunch why src/auth/session.ts # the decisions / bugs / invariants behind a file
|
|
92
|
-
hunch doctor # check git, schema version, and synthesis mode
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
β¦and in Claude Code, just ask: *"why is the session module built this way?"*
|
|
96
|
-
|
|
97
|
-
## Two ways to use it
|
|
98
|
-
|
|
99
|
-
**Through Claude Code (the point).** Once the MCP server is registered, ask questions
|
|
100
|
-
normally and Claude consults Hunch, or invoke the slash commands:
|
|
101
|
-
|
|
102
|
-
| Slash command | What it does |
|
|
103
|
-
|---|---|
|
|
104
|
-
| `/hunch-why <file\|symbol>` | the decisions, invariants, and bug history behind it β with citations |
|
|
105
|
-
| `/hunch-fix <bug>` | fix a bug grounded in past root causes, blast radius, and constraints |
|
|
106
|
-
| `/hunch-fragile` | a fragility report (the riskiest code, with evidence) |
|
|
107
|
-
|
|
108
|
-
The MCP tools Claude calls under the hood: `hunch_why`, `hunch_query`,
|
|
109
|
-
`hunch_check_constraints`, `hunch_get_dependents` (blast radius), `hunch_blast_radius`
|
|
110
|
-
(dependent files + near-violations a change could break indirectly), `hunch_bug_lineage`,
|
|
111
|
-
`hunch_context` (surgical minimal slice for a task), `hunch_timeline` (a target's decision
|
|
112
|
-
history over time), `hunch_record_decision` (write-back). `hunch_why` and `hunch_context`
|
|
113
|
-
take an optional `as_of` (commit/tag/branch) to **time-travel** the graph to a past state.
|
|
114
|
-
|
|
115
|
-
### Works with any MCP assistant
|
|
116
|
-
|
|
117
|
-
The Hunch MCP server is **client-agnostic** β one `.hunch/` graph powers every
|
|
118
|
-
assistant. `hunch init` scaffolds each tool's MCP config + ambient grounding so
|
|
119
|
-
they all consult the same memory:
|
|
120
|
-
|
|
121
|
-
| Assistant | MCP config | Grounding file |
|
|
122
|
-
|---|---|---|
|
|
123
|
-
| Claude Code | `.mcp.json` | `CLAUDE.md` + `/hunch-*` slash commands |
|
|
124
|
-
| Cursor | `.cursor/mcp.json` | `.cursor/rules/hunch.mdc` (always-applied) |
|
|
125
|
-
| VS Code (Copilot) | `.vscode/mcp.json` | `.github/copilot-instructions.md` |
|
|
126
|
-
| Codex CLI | `.codex/config.toml` | `AGENTS.md` |
|
|
127
|
-
| Windsurf | `.windsurf/mcp_config.json` | `.windsurf/rules/hunch.md` (always-on) |
|
|
128
|
-
| Anything else | β | `AGENTS.md` (cross-tool standard) |
|
|
129
|
-
|
|
130
|
-
Each writer **merges** into existing files (other MCP servers and your own prose are
|
|
131
|
-
preserved) and is idempotent. Opt out with `hunch init --no-providers`.
|
|
132
|
-
|
|
133
|
-
**Through the CLI** β the same graph, from your terminal:
|
|
134
|
-
|
|
135
|
-
| Command | What |
|
|
136
|
-
|---|---|
|
|
137
|
-
| `hunch init` | scaffold `.hunch/`, index, install hook + merge driver, auto-install the advisory pre-commit guard, install the **Claude Code agent hooks**, and wire up **every assistant** (Claude Code, Cursor, VS Code/Copilot, Codex, Windsurf, AGENTS.md). Flags: `--no-enforce`, `--enforce-strict`, `--no-providers`, `--no-agent-hooks`, `--firmness <level>` |
|
|
138
|
-
| `hunch index` | parse repo β symbols / edges / components (deterministic, no LLM) |
|
|
139
|
-
| `hunch backfill --since 90d` | replay git history β seed decisions |
|
|
140
|
-
| `hunch sync [sha]` | turn a commit into a Decision (run automatically by the hook) |
|
|
141
|
-
| `hunch record-bug --test <id> --message <m>` | capture a Bug from a failing test |
|
|
142
|
-
| `hunch record-constraint "<statement>" [--scope <globs>] [--severity advisory\|warning\|blocking] [--type β¦] [--rationale <t>] [--source-decision <id>]` | record an invariant the code must not break (what `hunch check` + the strict agent hook enforce) |
|
|
143
|
-
| `hunch firmness [off\|advisory\|firm\|strict]` | get/set how firmly the agent hook enforces Hunch before edits (no arg prints the current level) |
|
|
144
|
-
| `hunch test [cmdβ¦]` | run the suite (default `npm test`); auto-capture failures as Bugs (suspects + recurrenceβConstraints), mark passing tests' bugs fixed |
|
|
145
|
-
| `hunch why <path\|symbol> [--as-of <ref>]` | decisions / bugs / constraints explaining a target (flags `β STALE`); `--as-of` time-travels to what was believed at a commit/tag/branch |
|
|
146
|
-
| `hunch timeline <path\|symbol>` | the decision history for a target β what was believed, its valid-time window, and what superseded it |
|
|
147
|
-
| `hunch supersede <old> --by <new>` | mark one decision as replaced by another: closes the old one's valid-time window (invalidate, don't delete) |
|
|
148
|
-
| `hunch query "<q>" [--semantic]` | full-text + graph search (`--semantic` blends in local embeddings) |
|
|
149
|
-
| `hunch embed` | generate local embeddings for semantic recall (opt-in; needs `@huggingface/transformers`) |
|
|
150
|
-
| `hunch context <path\|symbol> [--as-of <ref>]` | minimal relevant slice for a task: invariants β decisions β bugs β blast radius (`--as-of` time-travels) |
|
|
151
|
-
| `hunch fragile` | ranked fragility report with evidence |
|
|
152
|
-
| `hunch check [--staged\|--commit <sha>] [--strict] [--blast]` | guardrail: flag changes touching a do-not-break invariant **directly or via blast radius** (a guarded file that depends on what you changed), **and changes that re-introduce something a decision deliberately retired** (the Regression Guard); `--blast` prints the dependency fan-out |
|
|
153
|
-
| `hunch
|
|
154
|
-
| `hunch
|
|
155
|
-
| `hunch
|
|
156
|
-
| `hunch
|
|
157
|
-
| `hunch
|
|
158
|
-
| `hunch
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
hunch firmness
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
hunch
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
> `
|
|
211
|
-
>
|
|
212
|
-
> (`
|
|
213
|
-
>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
ββ
|
|
308
|
-
ββ
|
|
309
|
-
ββ
|
|
310
|
-
ββ
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
1
|
+
# π§ Hunch β Engineering Memory OS
|
|
2
|
+
|
|
3
|
+
> Git stores *what* the code is. **Hunch** stores ***why*** it is that way β a persistent,
|
|
4
|
+
> git-native reasoning graph over your codebase, surfaced to Claude Code at reasoning time
|
|
5
|
+
> so the AI stops re-deriving understanding and stops undoing intentional design.
|
|
6
|
+
|
|
7
|
+
## The problem
|
|
8
|
+
|
|
9
|
+
Every AI coding session starts from zero. The model re-reads your code, re-guesses the
|
|
10
|
+
intent, and happily "fixes" the thing you deliberately did last month β because the
|
|
11
|
+
*reasoning* behind the code lives in PRs, Slack, and people's heads, not in the repo.
|
|
12
|
+
|
|
13
|
+
**Hunch** captures that reasoning as a **byproduct of normal work** β commits and test
|
|
14
|
+
failures β stores it as a git-tracked graph next to your code, and feeds it back to
|
|
15
|
+
Claude Code so every session is grounded in the decisions, bugs, and invariants that
|
|
16
|
+
came before. Local-first, no documentation toil, no SaaS.
|
|
17
|
+
|
|
18
|
+
## How it works
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
commit / test failure .hunch/ (git-tracked JSON) Claude Code
|
|
22
|
+
βββββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββ
|
|
23
|
+
β post-commit hook ββββΌβββββββββΆβ Decisions (why a change) ββββββββββΆβ MCP tools β
|
|
24
|
+
β record-bug ββββΌβββββββββΆβ Bugs (root causes) β read β /hunch-* cmds β
|
|
25
|
+
β structured diff + β write β Constraints(invariants) βββββββββββ CLAUDE.md β
|
|
26
|
+
β Claude (or heuristic) β β Components / Symbols/Edges β β CLI β
|
|
27
|
+
βββββββββββββββββββββββββ ββββββββββββββββββββββββββββ ββββββββββββββββ
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- **Index** (no LLM): tree-sitter parses your repo into a symbol/dependency graph β
|
|
31
|
+
functions, call edges, imports, components β plus churn and fan-in metrics.
|
|
32
|
+
- **Learn**: each commit becomes a structured **Decision** (an ADR); a failing test
|
|
33
|
+
becomes a **Bug** with a ranked suspect list; recurring or severe bugs are promoted
|
|
34
|
+
into **Constraints** (do-not-break invariants) and raise a component's *fragility*.
|
|
35
|
+
- **Ground**: Claude Code reads it through an **MCP server**, an auto-maintained
|
|
36
|
+
**`CLAUDE.md`**, and **slash commands** β every answer cites `provenance`
|
|
37
|
+
(source + confidence + evidence), so nothing is a blind assertion.
|
|
38
|
+
|
|
39
|
+
## Getting started
|
|
40
|
+
|
|
41
|
+
### 1. Install it
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g @davesheffer/hunch # puts `hunch` on your PATH
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or run from source (for hacking on Hunch itself):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install
|
|
51
|
+
npm run build # compiles to dist/
|
|
52
|
+
npm link # optional: puts a global `hunch` on your PATH
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Either way you then type `hunch β¦`. From a source checkout without `npm link`, use
|
|
56
|
+
`node dist/cli/index.js β¦` (or `npm run hunch -- β¦` to run via tsx). The rest of this
|
|
57
|
+
README uses `hunch` for brevity.
|
|
58
|
+
|
|
59
|
+
### 2. (Recommended) make a coding-assistant CLI available
|
|
60
|
+
|
|
61
|
+
Hunch's LLM synthesis is billed to **your subscription** through a coding-assistant
|
|
62
|
+
CLI β **never** a pay-per-token API key (the API key is stripped from the child env).
|
|
63
|
+
Hunch auto-detects the first one present, in this order:
|
|
64
|
+
|
|
65
|
+
| CLI | Subscription | Detected by |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `claude` (Claude Code) | Claude Pro/Max | `claude --version` |
|
|
68
|
+
| `codex` (OpenAI Codex) | ChatGPT Plus/Pro | `codex --version` |
|
|
69
|
+
| `cursor-agent` (Cursor) | Cursor | `cursor-agent --version` |
|
|
70
|
+
|
|
71
|
+
If none is installed, Hunch still works using a deterministic structural heuristic
|
|
72
|
+
(lower-confidence drafts). `hunch doctor` tells you which mode you're in; force one
|
|
73
|
+
with `HUNCH_SYNTH_PROVIDER=claude-cli|codex-cli|cursor-agent|deterministic`.
|
|
74
|
+
|
|
75
|
+
### 3. Initialize the repo you want a memory for
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
hunch init # scaffold .hunch/, index, install the post-commit hook,
|
|
79
|
+
# write .mcp.json + slash commands + CLAUDE.md, register the merge driver
|
|
80
|
+
hunch backfill --since 90d # cold start: seed decisions from recent git history
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`init` writes a `.mcp.json` pointing at *this machine's* node + Hunch β so **reload
|
|
84
|
+
Claude Code in the repo** afterward to pick up the `hunch_*` tools. (Each teammate runs
|
|
85
|
+
`hunch init` once to wire up their own clone; the captured `.hunch/` content is shared
|
|
86
|
+
via git.)
|
|
87
|
+
|
|
88
|
+
### 4. Use it
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
hunch why src/auth/session.ts # the decisions / bugs / invariants behind a file
|
|
92
|
+
hunch doctor # check git, schema version, and synthesis mode
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
β¦and in Claude Code, just ask: *"why is the session module built this way?"*
|
|
96
|
+
|
|
97
|
+
## Two ways to use it
|
|
98
|
+
|
|
99
|
+
**Through Claude Code (the point).** Once the MCP server is registered, ask questions
|
|
100
|
+
normally and Claude consults Hunch, or invoke the slash commands:
|
|
101
|
+
|
|
102
|
+
| Slash command | What it does |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `/hunch-why <file\|symbol>` | the decisions, invariants, and bug history behind it β with citations |
|
|
105
|
+
| `/hunch-fix <bug>` | fix a bug grounded in past root causes, blast radius, and constraints |
|
|
106
|
+
| `/hunch-fragile` | a fragility report (the riskiest code, with evidence) |
|
|
107
|
+
|
|
108
|
+
The MCP tools Claude calls under the hood: `hunch_why`, `hunch_query`,
|
|
109
|
+
`hunch_check_constraints`, `hunch_get_dependents` (blast radius), `hunch_blast_radius`
|
|
110
|
+
(dependent files + near-violations a change could break indirectly), `hunch_bug_lineage`,
|
|
111
|
+
`hunch_context` (surgical minimal slice for a task), `hunch_timeline` (a target's decision
|
|
112
|
+
history over time), `hunch_record_decision` (write-back). `hunch_why` and `hunch_context`
|
|
113
|
+
take an optional `as_of` (commit/tag/branch) to **time-travel** the graph to a past state.
|
|
114
|
+
|
|
115
|
+
### Works with any MCP assistant
|
|
116
|
+
|
|
117
|
+
The Hunch MCP server is **client-agnostic** β one `.hunch/` graph powers every
|
|
118
|
+
assistant. `hunch init` scaffolds each tool's MCP config + ambient grounding so
|
|
119
|
+
they all consult the same memory:
|
|
120
|
+
|
|
121
|
+
| Assistant | MCP config | Grounding file |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| Claude Code | `.mcp.json` | `CLAUDE.md` + `/hunch-*` slash commands |
|
|
124
|
+
| Cursor | `.cursor/mcp.json` | `.cursor/rules/hunch.mdc` (always-applied) |
|
|
125
|
+
| VS Code (Copilot) | `.vscode/mcp.json` | `.github/copilot-instructions.md` |
|
|
126
|
+
| Codex CLI | `.codex/config.toml` | `AGENTS.md` |
|
|
127
|
+
| Windsurf | `.windsurf/mcp_config.json` | `.windsurf/rules/hunch.md` (always-on) |
|
|
128
|
+
| Anything else | β | `AGENTS.md` (cross-tool standard) |
|
|
129
|
+
|
|
130
|
+
Each writer **merges** into existing files (other MCP servers and your own prose are
|
|
131
|
+
preserved) and is idempotent. Opt out with `hunch init --no-providers`.
|
|
132
|
+
|
|
133
|
+
**Through the CLI** β the same graph, from your terminal:
|
|
134
|
+
|
|
135
|
+
| Command | What |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `hunch init` | scaffold `.hunch/`, index, install hook + merge driver, auto-install the advisory pre-commit guard, install the **Claude Code agent hooks**, and wire up **every assistant** (Claude Code, Cursor, VS Code/Copilot, Codex, Windsurf, AGENTS.md). Flags: `--no-enforce`, `--enforce-strict`, `--no-providers`, `--no-agent-hooks`, `--firmness <level>` |
|
|
138
|
+
| `hunch index` | parse repo β symbols / edges / components (deterministic, no LLM) |
|
|
139
|
+
| `hunch backfill --since 90d` | replay git history β seed decisions |
|
|
140
|
+
| `hunch sync [sha]` | turn a commit into a Decision (run automatically by the hook) |
|
|
141
|
+
| `hunch record-bug --test <id> --message <m>` | capture a Bug from a failing test |
|
|
142
|
+
| `hunch record-constraint "<statement>" [--scope <globs>] [--severity advisory\|warning\|blocking] [--type β¦] [--rationale <t>] [--source-decision <id>]` | record an invariant the code must not break (what `hunch check` + the strict agent hook enforce) |
|
|
143
|
+
| `hunch firmness [off\|advisory\|firm\|strict]` | get/set how firmly the agent hook enforces Hunch before edits (no arg prints the current level) |
|
|
144
|
+
| `hunch test [cmdβ¦]` | run the suite (default `npm test`); auto-capture failures as Bugs (suspects + recurrenceβConstraints), mark passing tests' bugs fixed |
|
|
145
|
+
| `hunch why <path\|symbol> [--as-of <ref>]` | decisions / bugs / constraints explaining a target (flags `β STALE`); `--as-of` time-travels to what was believed at a commit/tag/branch |
|
|
146
|
+
| `hunch timeline <path\|symbol>` | the decision history for a target β what was believed, its valid-time window, and what superseded it |
|
|
147
|
+
| `hunch supersede <old> --by <new>` | mark one decision as replaced by another: closes the old one's valid-time window (invalidate, don't delete) |
|
|
148
|
+
| `hunch query "<q>" [--semantic]` | full-text + graph search (`--semantic` blends in local embeddings) |
|
|
149
|
+
| `hunch embed` | generate local embeddings for semantic recall (opt-in; needs `@huggingface/transformers`) |
|
|
150
|
+
| `hunch context <path\|symbol> [--as-of <ref>]` | minimal relevant slice for a task: invariants β decisions β bugs β blast radius (`--as-of` time-travels) |
|
|
151
|
+
| `hunch fragile` | ranked fragility report with evidence |
|
|
152
|
+
| `hunch check [--staged\|--commit <sha>\|--base <ref>] [--strict] [--format text\|markdown] [--blast]` | guardrail: flag changes touching a do-not-break invariant **directly or via blast radius** (a guarded file that depends on what you changed), **and changes that re-introduce something a decision deliberately retired** (the Regression Guard). `--base <ref>` checks a PR's diff (for CI); `--format markdown` emits a PR comment; `--strict` fails only on a direct, high-confidence, non-stale **blocking** invariant; `--blast` prints the dependency fan-out |
|
|
153
|
+
| `hunch ci` | scaffold the **CI Constraint Guard** β a GitHub Action that runs `hunch check` on every PR, comments the affected invariants/decisions, and fails on a blocking one |
|
|
154
|
+
| `hunch stale [--resync]` | drift: records whose files changed after last verification (`--resync` regenerates stale decisions from their commits) |
|
|
155
|
+
| `hunch review [--accept <id>\|--reject <id>]` | curate: triage / promote / drop low-confidence drafts |
|
|
156
|
+
| `hunch migrate` | upgrade `.hunch/` records to the current schema version |
|
|
157
|
+
| `hunch compact [--apply]` | prune low-value drafts to bound growth (dry-run by default) |
|
|
158
|
+
| `hunch doctor` | environment diagnostics (git, auth mode, schema version, counts) |
|
|
159
|
+
| `hunch mcp` | start the MCP server over stdio (Claude Code connects here) |
|
|
160
|
+
|
|
161
|
+
## Grounding the agent automatically (firmness)
|
|
162
|
+
|
|
163
|
+
Telling an assistant "consult Hunch first" in a prompt is advisory β it drifts. `hunch
|
|
164
|
+
init` instead installs two **Claude Code agent hooks** (in `.claude/settings.json`) so the
|
|
165
|
+
grounding is enforced by the harness, not by the model's memory:
|
|
166
|
+
|
|
167
|
+
- **Before every edit** (`PreToolUse` on `Edit`/`Write`/`MultiEdit`) Hunch injects the
|
|
168
|
+
relevant slice for the file being touched β its decisions, invariants, bug history, and
|
|
169
|
+
blast radius β straight into the model's context.
|
|
170
|
+
- **On every prompt** (`UserPromptSubmit`) it reminds the agent to query Hunch.
|
|
171
|
+
|
|
172
|
+
How hard it pushes is one committed knob β set it once, it applies to the whole team:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
hunch firmness # print the current level
|
|
176
|
+
hunch firmness strict # change it (takes effect on the next edit; no restart)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
| Level | Before an edit |
|
|
180
|
+
|---|---|
|
|
181
|
+
| `off` | nothing (hook is a no-op) |
|
|
182
|
+
| `advisory` *(default)* | inject the relevant Hunch slice as context |
|
|
183
|
+
| `firm` | advisory **+** explicitly flag invariants in the file's scope |
|
|
184
|
+
| `strict` | firm **+** **deny** an edit that hits a *blocking* invariant (directly or via blast radius), feeding the invariant back as the refusal reason |
|
|
185
|
+
|
|
186
|
+
Before an edit, the hook also grounds the agent in anything an in-force decision
|
|
187
|
+
**deliberately retired** from that file ("don't re-introduce `login` here β dec_017 removed
|
|
188
|
+
it"). The actual gate is at commit time: `hunch check` runs the **Regression Guard** over
|
|
189
|
+
the staged diff and, under `--strict`, fails the commit when a change re-adds a retired
|
|
190
|
+
symbol/dependency tied to a blocking invariant (otherwise it warns).
|
|
191
|
+
|
|
192
|
+
The hook never breaks your flow: any error or unrecognized input emits nothing and exits
|
|
193
|
+
0, and it stays silent on files Hunch hasn't learned yet. `strict` only bites once you have
|
|
194
|
+
**blocking** constraints recorded (`hunch record-constraint β¦ --severity blocking`) β with
|
|
195
|
+
none, every level degrades to context-only. Opt out of the hooks entirely with `hunch init
|
|
196
|
+
--no-agent-hooks`.
|
|
197
|
+
|
|
198
|
+
## Semantic search (optional)
|
|
199
|
+
|
|
200
|
+
By default `hunch query` and the `hunch_query` MCP tool use fast keyword (FTS) search β
|
|
201
|
+
zero setup, instant, offline. For recall on *paraphrases* (a question that shares no words
|
|
202
|
+
with the record it should find), opt into **local embeddings**:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm i -g @huggingface/transformers # one-time; a local model runtime
|
|
206
|
+
hunch embed # embed your records (first run downloads ~90MB)
|
|
207
|
+
hunch query --semantic "auth token expiry" # hybrid keyword + semantic recall
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
> **Install it where `hunch` runs.** The runtime is resolved from `hunch`'s own
|
|
211
|
+
> `node_modules`, so match the install scope: a globally-installed `hunch` needs the
|
|
212
|
+
> global (`-g`) install above; running from a source checkout needs it in the repo
|
|
213
|
+
> (`npm i @huggingface/transformers` there). If `hunch embed` reports the model "present
|
|
214
|
+
> but failed to load," the scopes don't match. `hunch doctor` shows the active mode.
|
|
215
|
+
|
|
216
|
+
Embeddings are **local and free** (no API β consistent with the subscription-only synthesis
|
|
217
|
+
rule) and **opt-in** (the base install stays lean). The long-lived MCP server picks them up
|
|
218
|
+
automatically once present. Vectors live in the derived SQLite index and are reconciled by
|
|
219
|
+
content hash on every `hunch index`, so they never drift from the JSON source of truth. `hunch
|
|
220
|
+
doctor` reports coverage; tune the blend with `HUNCH_RRF_W_FTS` / `HUNCH_RRF_W_SEM` / `HUNCH_RRF_K`.
|
|
221
|
+
|
|
222
|
+
## What makes the capture good (not just "changed N files")
|
|
223
|
+
|
|
224
|
+
Even with **no LLM**, the write path runs a structured **diff analysis** β added /
|
|
225
|
+
removed / changed symbols, new and dropped dependencies, and which invariants a change
|
|
226
|
+
touches β so an auto-captured decision reads like *"introduced `verifySession`,
|
|
227
|
+
`revokeSession`; removed `login`; new dep: redis; touches con_004"*, with breaking-change
|
|
228
|
+
consequences. With the `claude` CLI present it upgrades to full LLM synthesis; otherwise
|
|
229
|
+
it stays useful offline. Either way every record is **advisory and cheap to discard** β
|
|
230
|
+
`hunch review` lets you promote the good ones to human-confirmed.
|
|
231
|
+
|
|
232
|
+
## Working as a team
|
|
233
|
+
|
|
234
|
+
The `.hunch/` JSON is the **source of truth**: diffable, reviewable in PRs, and synced
|
|
235
|
+
for free over `git push` / `pull`. `hunch init` also registers a **git merge driver** so
|
|
236
|
+
concurrent edits to the graph merge **by record id** instead of throwing conflict markers
|
|
237
|
+
(human-confirmed beats auto, then higher confidence, then recency). The routing lives in a
|
|
238
|
+
committed `.gitattributes`; the per-clone driver definition is set up by each teammate's
|
|
239
|
+
`hunch init`. The graph is **OS-agnostic**: paths are stored and matched in POSIX form, and
|
|
240
|
+
an installed Hunch registers its MCP server by package name (`npx -y @davesheffer/hunch`)
|
|
241
|
+
rather than an absolute path β so a teammate on Windows, macOS, or Linux shares the same
|
|
242
|
+
`.hunch/` and the same committed config without per-machine fixups.
|
|
243
|
+
|
|
244
|
+
## Continuous learning (CI)
|
|
245
|
+
|
|
246
|
+
The decision half of the loop is automatic (the post-commit hook). Light up the **bug /
|
|
247
|
+
constraint half** by wrapping your test run with `hunch test`:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
hunch test # runs `npm test`; capture failures β Bugs, resolve fixed ones
|
|
251
|
+
hunch test -- pytest -q # any runner: pass the command after `--`
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
It parses TAP and the `node:test` spec reporter, captures each failing test as a **Bug**
|
|
255
|
+
(ranked suspects; a recurrence or substantiated high-severity failure auto-promotes a
|
|
256
|
+
do-not-break **Constraint**), and marks a previously-open bug **fixed** once its test passes
|
|
257
|
+
again. It preserves the runner's exit code, so it's a drop-in CI step:
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
# .github/workflows/ci.yml
|
|
261
|
+
- run: npm ci
|
|
262
|
+
- run: npx hunch test # exits non-zero on failure, just like the suite
|
|
263
|
+
- run: | # persist what was learned (optional)
|
|
264
|
+
git add .hunch && git commit -m "chore(hunch): capture test run" || true
|
|
265
|
+
git push || true
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Repair drift after refactors with **`hunch stale --resync`** (re-synthesizes stale decisions
|
|
269
|
+
from their commits via the LLM).
|
|
270
|
+
|
|
271
|
+
### Block a PR that breaks memory (CI Constraint Guard)
|
|
272
|
+
|
|
273
|
+
Memory that only *advises* gets ignored. `hunch ci` scaffolds a GitHub Action that turns
|
|
274
|
+
Hunch into a **merge gate**: on every pull request it runs `hunch check` over the diff,
|
|
275
|
+
posts a sticky comment citing the affected `con_`/`dec_` ids, and **fails the check** when
|
|
276
|
+
the PR breaks a *direct, high-confidence, non-stale* blocking invariant, re-adds
|
|
277
|
+
deliberately-retired code, or contradicts an in-force decision.
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
hunch ci # writes .github/workflows/hunch-guard.yml β commit it
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
It reasons over **the diff plus the constraints committed in the same git history**, so the
|
|
284
|
+
comment says exactly which decision a change violates ("breaks `con_004` β server-side
|
|
285
|
+
revocation, from `dec_017`"). Make *Hunch Guard* a required status check in branch protection
|
|
286
|
+
to enforce on merge. The hardened strict gate only blocks on high-confidence, non-stale
|
|
287
|
+
invariants β stale / low-confidence / blast-radius hits stay advisory in the comment β so
|
|
288
|
+
it's safe to require on a shared repo. Under the hood it's just
|
|
289
|
+
`hunch check --base origin/<target> --strict --format markdown`.
|
|
290
|
+
|
|
291
|
+
## Maintenance
|
|
292
|
+
|
|
293
|
+
- **`hunch doctor`** β is git healthy? are you on the subscription path or the offline
|
|
294
|
+
heuristic? what schema version is on disk? how many records?
|
|
295
|
+
- **`hunch migrate`** β after upgrading Hunch, bring old `.hunch/` records up to the
|
|
296
|
+
current schema (old records are migrated in memory on every read, so reads never break;
|
|
297
|
+
`migrate` persists the upgrade and never drops a record it can't migrate).
|
|
298
|
+
- **`hunch compact --apply`** β auto-captured drafts accumulate; compaction prunes the
|
|
299
|
+
low-value ones (rejected / superseded / stale drafts, resolved low-confidence bugs).
|
|
300
|
+
It **never** removes an accepted/human-confirmed decision, an open bug, a constraint, or
|
|
301
|
+
any record another record still references. Run without `--apply` first to preview.
|
|
302
|
+
|
|
303
|
+
## Where it's stored
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
.hunch/
|
|
307
|
+
ββ components/ one JSON file per architecture node (curated, PR-reviewable)
|
|
308
|
+
ββ decisions/ one JSON file per Decision (ADR)
|
|
309
|
+
ββ bugs/ one JSON file per Bug
|
|
310
|
+
ββ constraints/ one JSON file per Constraint (invariant)
|
|
311
|
+
ββ symbols/index.json the symbol graph (high-cardinality, single file)
|
|
312
|
+
ββ edges/index.json the dependency graph
|
|
313
|
+
ββ manifest.json on-disk schema version
|
|
314
|
+
ββ hunch.sqlite DERIVED FTS5 + graph index, rebuilt by `hunch index` (gitignored)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Low-volume entities are one file per record so they read cleanly in a PR; the
|
|
318
|
+
high-cardinality symbol/edge graphs are single id-sorted arrays to keep git noise down.
|
|
319
|
+
SQLite is a throwaway index rebuilt from the JSON β only the JSON is committed.
|
|
320
|
+
|
|
321
|
+
> Note: the on-disk directory is still `.hunch/` (and the MCP tools are still `hunch_*`)
|
|
322
|
+
> for backward compatibility with existing graphs. A future release may migrate these to
|
|
323
|
+
> `.hunch/` / `hunch_*`.
|
|
324
|
+
|
|
325
|
+
## Architecture
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
src/
|
|
329
|
+
ββ core/ types (Zod schema), ids, paths, glob, schema migration, atomic file I/O
|
|
330
|
+
ββ store/ JSON source of truth ββ SQLite/FTS5 derived index; merge driver; compaction
|
|
331
|
+
ββ extractors/ tree-sitter parse, git introspection, the indexer
|
|
332
|
+
ββ synthesis/ write path: Claude-CLI (subscription) or deterministic fallback
|
|
333
|
+
ββ mcp/ MCP stdio server (the hunch_* tools)
|
|
334
|
+
ββ integrations/ post-commit hook, CLAUDE.md writer, .mcp.json + slash commands, merge driver
|
|
335
|
+
ββ cli/ commander entrypoint
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## VS Code
|
|
339
|
+
|
|
340
|
+
A companion **[VS Code extension](vscode-extension/)** (on
|
|
341
|
+
[Open VSX](https://open-vsx.org/extension/davesheffer/hunch-vscode) β works in
|
|
342
|
+
VS Code / Cursor / Windsurf / VSCodium) brings the graph into the editor: a tree of
|
|
343
|
+
decisions / invariants / bugs / **bug-lineage** / fragility / **stale records**, a
|
|
344
|
+
**CodeLens** summary + per-symbol bug/fragility marks, **hover** with bug history,
|
|
345
|
+
invariants surfaced in the **Problems panel**, overview-ruler hotspot marks, an
|
|
346
|
+
interactive **component graph**, fuzzy **search**, and a status-bar invariant counter.
|
|
347
|
+
It reads the committed `.hunch/` JSON directly (no server, no native deps); writes
|
|
348
|
+
delegate to the `hunch` CLI.
|
|
349
|
+
|
|
350
|
+
## Notable engineering decisions
|
|
351
|
+
|
|
352
|
+
- **Subscription-billed synthesis, never the API.** The write path drives your Claude
|
|
353
|
+
subscription via the `claude` CLI; `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` are
|
|
354
|
+
stripped from the child env to force subscription auth (they outrank it in headless
|
|
355
|
+
mode). A deterministic, no-LLM fallback means the loop never hard-requires credentials.
|
|
356
|
+
- **Native `tree-sitter` (0.21.1) + `tree-sitter-typescript`, not web-tree-sitter.** The
|
|
357
|
+
prebuilt WASM grammars have an ABI incompatible with current `web-tree-sitter`; the
|
|
358
|
+
native bindings ship Node-20 prebuilds (no compiler) and a simpler synchronous API.
|
|
359
|
+
- **`better-sqlite3` pinned to `12.9.0`** β 12.10.x ships no Node-20 prebuild and would
|
|
360
|
+
force a source compile; 12.9.0 has the Node-20 (ABI 115) prebuild.
|
|
361
|
+
- **Atomic, durable writes.** All `.hunch/` writes go through a temp-file + rename, with a
|
|
362
|
+
Windows-safe fallback, so an interrupted write can't truncate the index; `put`/`delete`
|
|
363
|
+
refuse to rewrite a corrupt index rather than flatten it.
|
|
364
|
+
- **OS-agnostic by construction.** Git emits `/`-separated paths on every platform, so the
|
|
365
|
+
graph is the source of truth in POSIX form; any free-form target is canonicalized before
|
|
366
|
+
comparison, so `src\auth\x.ts` and `src/auth/x.ts` resolve to the same records. Generated
|
|
367
|
+
configs that get committed reference Hunch by package name, never a machine-local path.
|
|
368
|
+
|
|
369
|
+
## Develop
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
npm run typecheck # tsc --noEmit
|
|
373
|
+
npm test # node:test suite (store, graph, parse, indexer, synthesis, migrate, merge, compact)
|
|
374
|
+
npm run hunch -- why src/store/hunchStore.ts # run the CLI from source via tsx, no build
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
See [DESIGN.md](DESIGN.md) for the full spec. PR/CI enforcement now ships as the
|
|
378
|
+
**CI Constraint Guard** (`hunch ci`). Still deferred by design: a hosted web dashboard
|
|
379
|
+
and multi-repo support.
|