@davesheffer/hunch 1.22.0 → 1.22.1

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 CHANGED
@@ -1,37 +1,37 @@
1
1
  # Hunch
2
2
 
3
- ## Give your AI coding assistant the missing story behind your code.
3
+ ## Your repo remembers why and teaches every coding agent how the project works.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@davesheffer/hunch?color=2742ff&label=npm)](https://www.npmjs.com/package/@davesheffer/hunch)
6
6
  [![GitHub stars](https://img.shields.io/github/stars/davesheffer/hunch?color=2742ff&label=%E2%98%85%20star)](https://github.com/davesheffer/hunch)
7
7
  [![license](https://img.shields.io/npm/l/@davesheffer/hunch?color=2742ff)](LICENSE)
8
8
 
9
- Git remembers what changed. Chat history disappears. A future Claude, Cursor, Codex, or Copilot
10
- session can read your code, but it usually does not know why your team chose this design, which
11
- alternative failed, or which old bug a strange-looking line prevents.
9
+ Every new AI coding session can read your code. It cannot automatically see why your team chose
10
+ this design, which alternative already failed, what an odd-looking line protects, or how this
11
+ repository expects work to be explained and reviewed.
12
12
 
13
- **Hunch is project memory for the AI coding tools you already use.** It saves decisions, bug fixes,
14
- rejected approaches, and important rules. Before an assistant changes code, Hunch brings back the
15
- relevant reasons. After the change, it can check whether the work conflicts with a rule your team
16
- explicitly trusts.
13
+ That is how settled decisions get reopened, fixed bugs return, and technically plausible changes
14
+ arrive feeling foreign to the project.
17
15
 
18
- ### A simple example
16
+ **Hunch is evidence-backed project intelligence for the AI coding tools you already use.** It gives
17
+ Claude, Codex, Cursor, Copilot, Windsurf, Antigravity, and other MCP clients the same durable
18
+ understanding of your codebase:
19
19
 
20
- Your team once moved login sessions to the server so stolen tokens could be disabled immediately.
21
- Months later, an AI assistant sees the extra code and proposes a “simpler” token-only design. Hunch
22
- shows the assistant why the server-side design exists and which security bug it prevents—before the
23
- edit happens.
20
+ - why the code is shaped this way;
21
+ - how the repository communicates, reviews, and builds;
22
+ - what depends on the code about to change; and
23
+ - which trusted decisions, fixes, and architectural boundaries the result must preserve.
24
24
 
25
- That means less time repeating old explanations, fewer old mistakes returning, and the same project
26
- context across every coding assistant.
25
+ For precise rules your team has explicitly trusted, the promise is **Never Twice**: an agent may
26
+ propose a different direction, but it cannot quietly re-make a decided decision or re-introduce a
27
+ fixed failure without Hunch surfacing the conflict and its evidence.
27
28
 
28
- Hunch is **not another AI model** and it does not replace your coding assistant. It is the memory and
29
- safety layer behind it. Memory is advisory by default; nothing blocks work unless you deliberately
30
- turn on strict checks for a precise rule.
29
+ Memory starts advisory. Nothing blocks until a human deliberately trusts a precise rule and opts
30
+ into strict enforcement.
31
31
 
32
32
  ## Start in five minutes
33
33
 
34
- Requires Node 22.13+ and a git repository.
34
+ Requires Node 22.13+ and a Git repository.
35
35
 
36
36
  ```bash
37
37
  npm i -g @davesheffer/hunch
@@ -44,254 +44,190 @@ Reload your coding assistant, then ask a normal question:
44
44
 
45
45
  > Why is this built this way?
46
46
 
47
- Hunch answers from your project's saved history and shows where the answer came from. `hunch init`
48
- indexes the repo, installs local hooks, and connects supported assistants without replacing their
49
- existing configuration.
50
-
51
- ## What Hunch gives you
52
-
53
- - **A memory that outlives chat** — decisions and corrections are still available next week, next
54
- year, and in a different assistant.
55
- - **One shared story** — Claude Code, Cursor, Copilot, Windsurf, Antigravity, Codex, and any MCP
56
- client get the same project context.
57
- - **Warnings with reasons** — review a change against trusted project rules and see exactly why it
58
- passes, needs attention, or should be blocked.
59
- - **Past bugs stay useful** — see which old incident a piece of code fixed before accidentally
60
- undoing it.
61
- - **Fresh context without lost history** — decisions whose code anchors are still current rank
62
- ahead of ones whose files changed after verification; older evidence remains visible and keeps
63
- its existing authority.
64
- - **Understands how code connects** — for TypeScript, JavaScript, Python, Go, PHP, YAML, and Helm, Hunch
65
- can see what calls or depends on the code you are about to change. Its memory works with any
66
- language.
67
- - **Works with existing decision documents** — import your architecture decision records into
68
- Hunch, or export Hunch decisions back to a standard format other tools can read. Imported ADRs
69
- start as useful advisory memory; during normal work your assistant asks you to approve or decline
70
- one exact ADR at a time. Silence never grants authority, and changed ADR text is asked again.
71
-
72
- The source of truth is readable JSON in `.hunch/`. A local SQLite index makes retrieval fast but
73
- is always rebuildable.
74
-
75
- ## What improved in v1.19
76
-
77
- Hunch now gives developers a better, shorter list of code to inspect when they describe a problem.
78
- In a 12-problem test on unfamiliar code, it found the changed piece of code in 6 cases instead of 3
79
- and found the correct file in 10 cases instead of 8. In a separate test, it kept the same five
80
- successful finds while reducing the average number of named code items to inspect—such as functions
81
- or classes—from 18.9 to 11.
82
-
83
- | What the test measured | Before | v1.19 | Result |
84
- | --- | ---: | ---: | ---: |
85
- | Problems where Hunch found the changed code | 3/12 | 6/12 | 2× as many in this test |
86
- | Problems where Hunch found the correct file | 8/12 | 10/12 | 2 more correct files |
87
- | Pieces of code inspected for the same five finds | 18.9 average | 11 average | 41.9% less to inspect |
88
-
89
- These are small, controlled tests—not a promise that Hunch is twice as accurate everywhere. Hunch
90
- also refuses to pretend it knows the exact fix when the evidence only shows where to investigate.
91
- The detailed receipts live in [`bench/external/results`](bench/external/results).
92
-
93
- See the public [roadmap](ROADMAP.md) for what is next and what is deliberately out of scope.
94
-
95
- ## Common tasks
96
-
97
- Most memory work happens automatically after commits. These commands cover the common manual paths:
98
-
99
- | Command | Use it for |
100
- | --- | --- |
101
- | `hunch why <file>` | Explain why a file is built this way and what could be affected by changing it |
102
- | `hunch query "<question>"` | Search project memory |
103
- | `hunch context "<task>" --profile reviewer` | Get a bounded builder, reviewer, or architect view without changing enforcement |
104
- | `hunch change-id <base> [head]` | Bind a branch and its exact squash merge to the same content-based change ID |
105
- | `hunch check --working` | Check current changes against the decisions and rules your team trusts |
106
- | `hunch log` | See what Hunch remembered and undo a memory change if needed |
107
- | `hunch escalations` | See the rare questions that need a human answer |
108
- | `hunch review` | Answer the current imported-ADR approve/decline question from the terminal |
109
- | `hunch doctor` | Diagnose setup problems |
47
+ `hunch init` indexes the repository, installs local lifecycle hooks, and connects supported
48
+ assistants without replacing their existing configuration. The next session receives the relevant
49
+ story with its sources, not a giant transcript or a generic prompt wall.
50
+
51
+ ## One evidence loop, not another model
52
+
53
+ ```text
54
+ Git history + ADRs + corrections + tests + repository conventions
55
+
56
+
57
+ Hunch's evidence graph
58
+ / │ \
59
+ engineering memory Project DNA reviewed landscape
60
+ \ │ /
61
+
62
+ role-shaped, budgeted context delivery
63
+
64
+
65
+ Claude / Codex / Cursor / any MCP agent
66
+
67
+
68
+ deterministic change receipt
69
+ ```
110
70
 
111
- <details>
112
- <summary><strong>Advanced: problem shortlisting and evidence receipts</strong></summary>
71
+ Hunch is not an agent, orchestrator, or hosted knowledge service. It is the durable reasoning and
72
+ validation layer behind the tools that write the code.
113
73
 
114
- These tools help researchers and maintainers investigate where a described behavior may live. They
115
- show uncertainty instead of claiming to know the exact fix.
74
+ ## What Hunch understands
116
75
 
117
- | Command | Use it for |
76
+ | Layer | What it adds |
118
77
  | --- | --- |
119
- | `hunch shortlist --issue "..."` | Build a bounded list of files and named code items to inspect |
120
- | `hunch evidence-map receipt.json` | Add observed execution evidence without guessing the exact owner |
121
-
122
- `hunch evidence-map` accepts a bounded JSON receipt containing a red target, a distinct green
123
- control, optional execution counts, and optional intervention outcomes. It reports target-only and
124
- shared execution plus behavior-sensitive files. It does not run the probes, edit the repository, or
125
- claim that behaviorally influential code owns the correction. Use `--json` for the machine-readable
126
- map; MCP clients can submit the same receipt through `hunch_evidence_map`.
127
-
128
- `hunch shortlist --evidence` attaches authenticated observations to the relevant candidates but does
129
- not reorder them. Three fresh transfer experiments failed to prove that execution or intervention
130
- influence identifies the correction owner, so the production path converts that result into a hard
131
- safeguard: no candidate is promoted or displaced by evidence. JSON output still includes a
132
- deterministic receipt and the explicit `exact_owner_enabled: false` policy.
133
-
134
- Every shortlist also preserves its flat top five and adds a deterministic hierarchical inspection
135
- view anchored to those files: at most five files, two semantic declaration families per file, and
136
- three declarations per family. On a preregistered 12-case fresh transfer, the preserved union found
137
- 6/12 changed declarations versus 3/12 for the flat top five (**+25 percentage points**, three
138
- rescues), while correct-file coverage improved from 8/12 to 10/12. The view averaged 18.8 unique
139
- declarations and never exceeded 24. This promotes the clusters as a supplemental diagnostic, not as
140
- a top-five accuracy claim; exact-owner output and per-case confidence remain disabled. JSON output
141
- includes the deterministic cluster receipt and the transfer calibration.
142
-
143
- The default output also turns those clusters into a progressive inspection queue. It preserves the
144
- flat shortlist, adds only the strongest members of already-selected semantic families, stops at ten
145
- when the behavior is explained, and permits one final fallback declaration before reporting
146
- uncertainty. Development replay retained all 21/36 combined hits from the full cluster view while
147
- reducing the hard inspection ceiling to 11 from an average of 19.8 declarations (44% less). On a
148
- separate preregistered 12-case ArkType transfer it retained all 5 full-cluster hits with zero losses
149
- and reduced mean inspection from 18.9 declarations to 11 (41.9% less). It found no additional fresh
150
- hit, so the queue is retained as an efficiency advisory rather than promoted as an accuracy gain.
151
-
152
- Follow-up optimization attempts stay out of production. Replacing cluster slots with same-file
153
- declarations produced four development rescues but also three losses. Appending two same-file slots
154
- removed those development losses, but a second blind 12-case ArkType transfer produced 3/12 hits for
155
- both the existing and expanded plans, with zero rescues. Product-source filtering lost one prior hit,
156
- one-hop relationship expansion added none, and evidence/causal rerankers also failed their frozen
157
- transfer gates. The receipts remain in `bench/external/results`; rejected mechanisms cannot silently
158
- change the production ordering.
78
+ | **Engineering Memory** | Decisions, rejected alternatives, corrections, bug lineage, findings, and the rationale a future session would otherwise miss. |
79
+ | **Code Graph** | Symbols, calls, imports, dependencies, components, blast radius, and architectural reachability across TypeScript, JavaScript, Python, Go, PHP, YAML, and Helm. Memory itself works with any language. |
80
+ | **Project DNA** | Revision-specific, evidence-backed observations about how a repository communicates and works: vocabulary, contribution habits, review expectations, engineering conventions, and culture. |
81
+ | **Engineering Landscape** | Human-reviewed links from product and capability to system, repository, service, interface, data, delivery resources, runbooks, ownership, dashboards, and SLOs. |
82
+ | **Validated Delivery** | The smallest relevant evidence for the current builder, reviewer, or architect, with provenance, currentness, omissions, authority, and a content-addressed receipt. |
83
+ | **Change Gate + Constitution** | Deterministic checks for trusted constraints and architectural intent. Policies are compiled, proved, inspected, and explicitly activated by a human—never promoted by an agent in the background. |
159
84
 
160
- </details>
85
+ Readable JSON in `.hunch/` is the source of truth. SQLite is a fast, rebuildable projection. Git
86
+ keeps the memory portable, reviewable, and reversible.
161
87
 
162
- Corrections can become scoped rules, but captured memory cannot hard-block on its own. Enforcement is
163
- deterministic and opt-in:
88
+ ## Project DNA: help the agent work like it belongs here
164
89
 
165
- ```bash
166
- hunch firmness strict
167
- hunch check --staged --strict
168
- ```
90
+ Project DNA is Hunch's evidence-bound model of **how a repository communicates and works**. It is
91
+ not a persona, does not impersonate a maintainer, and does not turn frequent behavior into policy.
169
92
 
170
- ## Share the same memory with your team
93
+ The deterministic baseline reads an exact Git revision, bounded commit history, and committed
94
+ convention files. The current release can also accept bounded, caller-authorized pull-request and
95
+ review evidence. Every evidence batch is validated and sealed; raw collaboration text does not
96
+ enter the profile.
171
97
 
172
- For a team, Hunch can keep everyone’s decisions, corrections, and rules in one private Git
173
- repository, separate from the code repository. Hunch does not host it. Create a private repository
174
- that every teammate can access, install Hunch on team machines and CI, then have one maintainer run:
98
+ Each trait keeps its category, confidence, freshness, repository revision, and evidence hash. Hunch
99
+ can then include only the relevant DNA in normal context, explain how well a commit, PR, issue, or
100
+ message matches repository conventions, and show how the profile changed between two revisions.
175
101
 
176
102
  ```bash
177
- npm i -g @davesheffer/hunch@1.22.0
178
- hunch shared --repo git@github.com:acme/project-hunch-memory.git
179
- git add .gitignore .hunch/team.json
180
- git commit -m "chore: connect shared Hunch memory"
181
- git push
103
+ hunch dna inspect
104
+ hunch dna context
105
+ hunch dna diff <older-ref> <newer-ref>
182
106
  ```
183
107
 
184
- Use a credential-free URL in the command; keep tokens in your Git credential helper or use SSH.
185
- If this project already publishes memory in `.hunch/` and you want to move it into the dedicated
186
- repo, add `--migrate`, review the reported untrack/ignore changes, and follow the commit instructions
187
- printed by Hunch. Omit `--migrate` for a new setup.
108
+ DNA may shape orientation, terminology, and advisory Project Match checks. It cannot create or
109
+ override a decision, constraint, finding, conformance rule, policy, or permission.
188
110
 
189
- After the pointer commit lands, teammates need Hunch installed and Git access to the memory repo:
111
+ Read the [Project DNA contract](docs/project-dna.md) and the broader
112
+ [Project DNA vision](docs/project-dna-engine.md).
190
113
 
191
- ```bash
192
- npm i -g @davesheffer/hunch@1.22.0
193
- git pull
194
- hunch init
195
- hunch doctor
196
- ```
197
-
198
- `hunch init` validates and connects an ignored local clone of the memory repo. Memory-reading and
199
- writing CLI operations attempt a bounded refresh at startup; connected MCP sessions check for new
200
- team memory at each tool-request boundary and rebuild their local index only when the JSON changed.
201
- New captures route to that repo and are committed and synchronized automatically by default. If a
202
- push cannot complete, a later capture or `hunch shared --sync` retries it.
203
-
204
- The committed `.hunch/team.json` contains only the credential-free memory-repo locator and canonical
205
- branch. The ignored `.hunch/local.json` contains local paths and preferences, not credentials;
206
- authentication stays in SSH or the normal Git credential helper. Shared memory records,
207
- `.hunch/local.json`, and `.hunch-private/` stay out of code history. Use
208
- `hunch check --base origin/main --strict --public-only --format markdown` for output that may be
209
- posted publicly; omit `--public-only` for an internal check that should enforce team memory.
210
- `HUNCH_PRIVATE_DIR` remains an explicit process-level override for CI and portability. When it
211
- redirects a repo away from `.hunch/local.json` or bypasses an advertised team store, CLI and MCP
212
- startup warn on stderr and `hunch doctor` labels the effective source.
213
-
214
- For a correction that Hunch can express as a deterministic policy, create and inspect its
215
- proof-backed proposal:
114
+ ## Day-to-day
216
115
 
217
- ```bash
218
- hunch policy upgrade-correction con_...
219
- hunch policy card pol_...
220
- ```
116
+ Most capture happens around normal commits and test failures. These commands cover the common
117
+ manual paths:
221
118
 
222
- The upgrade creates evidence, a plan, and a proof but leaves the policy proposed with
223
- `authority: none`. A proved policy still requires explicit, audited human acceptance before it can
224
- become advisory or blocking; Hunch never grants that authority automatically.
225
-
226
- Need to pause or roll back without deleting memory?
119
+ | Command | Use it for |
120
+ | --- | --- |
121
+ | `hunch context "<task>" --profile builder` | Get a bounded builder, reviewer, or architect brief before work starts |
122
+ | `hunch why <file-or-symbol>` | See the decisions, bugs, constraints, and blast radius behind code |
123
+ | `hunch structure [target]` | Inspect the indexed repository shape without repeated search rounds |
124
+ | `hunch findings [scope]` | Inherit known-but-unfixed gaps instead of rediscovering them |
125
+ | `hunch check --working` | Review the current tree against trusted project rules |
126
+ | `hunch conform` | Prove the code still satisfies recorded architectural intent |
127
+ | `hunch impact origin/main` | See the dependency and memory surface of a branch |
128
+ | `hunch compare branch-a branch-b` | Rank candidate changes by the fewest invariant and decision conflicts |
129
+ | `hunch landscape review` | Inspect a hash-bound repository landscape without writing authority |
130
+ | `hunch now` | See recent memory and the live decision-backed roadmap |
131
+ | `hunch escalations` | See the rare questions that genuinely require a human answer |
132
+ | `hunch doctor` | Diagnose setup, provider, index, or overlay problems |
133
+
134
+ When you are ready for deterministic enforcement:
227
135
 
228
136
  ```bash
229
- hunch firmness off
230
- hunch shared --repo git@github.com:acme/project-hunch-memory.git --no-auto-commit
231
- # Later, publish any pending local memory explicitly:
232
- hunch shared --sync
137
+ hunch firmness strict
138
+ hunch check --staged --strict
233
139
  ```
234
140
 
235
- The first command turns off agent-hook enforcement; the second keeps shared reads and local captures
236
- but stops automatic memory commits and pushes. As a team-coordinated rollback, revert the setup
237
- commit to stop discovery after teammates pull the revert. Existing machines retain their ignored
238
- local overlay until they are deliberately disconnected; do not delete the memory repo as part of a
239
- rollback. For this rollout, reinstall the previous published package with
240
- `npm i -g @davesheffer/hunch@1.20.3`; the release receipt resolves and records the verified rollback
241
- target from the npm registry instead of trusting Git tags. Pause enforcement first as shown above,
242
- and keep every team client on the same release before resuming Matrix policy workflows.
141
+ That is the one moment of teeth. Captured memory cannot silently hard-block on its own.
243
142
 
244
- ## Synthesis without surprise billing
143
+ ## What changed after v1.19
245
144
 
246
- Hunch can draft structured memory through:
145
+ The v1.19 correction-search benchmark is still useful evidence, but it no longer describes the
146
+ whole product.
247
147
 
248
- - a selected Claude Code, Codex, or Cursor subscription CLI;
249
- - an opt-in OpenAI-compatible local endpoint such as Ollama, vLLM, LM Studio, or llama.cpp; or
250
- - the built-in deterministic fallback when no model is available.
148
+ - **v1.20 one validated path from reason to result.** Role-shaped context, reviewed Engineering
149
+ Landscape fragments, exact change identity, PHP graph support, and hash-bound ADR review moved
150
+ source, provenance, currentness, omissions, and human authority through one delivery contract.
151
+ - **v1.21 — Project DNA.** Hunch gained deterministic, revision-specific repository profiles,
152
+ bounded DNA context delivery, explainable Project Match checks, and auditable profile deltas.
153
+ - **v1.22 — authorized collaboration evidence.** Hosts can contribute bounded PR and review
154
+ evidence to Project DNA through a typed, sealed contract without storing raw collaboration text
155
+ or changing policy authority.
251
156
 
252
- When several subscription CLIs are installed, Hunch does not guess which plan to use:
157
+ See the [changelog](CHANGELOG.md) for the release-by-release detail and the
158
+ [roadmap](ROADMAP.md) for what is next and deliberately out of scope.
253
159
 
254
- ```bash
255
- hunch provider codex-cli
256
- ```
160
+ <details>
161
+ <summary><strong>The scoped v1.19 benchmark</strong></summary>
257
162
 
258
- Local and private-network endpoints work without a billing flag. Every public remote requires the
259
- explicit `HUNCH_SYNTH_ALLOW_METERED=1` opt-in, because Hunch cannot infer cost from a hostname.
260
- See [Synthesis & billing](https://hunch-pi.vercel.app/docs#synthesis) for setup details.
163
+ On a preregistered 12-problem transfer, the supplemental inspection view found the changed
164
+ declaration in 6 cases instead of 3 and the correct file in 10 cases instead of 8. On a separate
165
+ 12-case transfer, its progressive queue retained the same five successful finds while reducing the
166
+ average declarations to inspect from 18.9 to 11 (41.9% less).
261
167
 
262
- ## Local-first and portable
168
+ These are bounded diagnostic results, not a claim that Hunch is universally twice as accurate.
169
+ Failed evidence and causal rerankers remain disabled; evidence can annotate the shortlist but does
170
+ not reorder it or claim an exact correction owner. The detailed receipts live in
171
+ [`bench/external/results`](bench/external/results).
263
172
 
264
- Hunch has no hosted memory service or telemetry. Your graph travels with git and speaks MCP, so it
265
- is not tied to one editor or model provider.
173
+ </details>
266
174
 
267
- Sensitive reasoning can live in a separate private overlay:
175
+ ## Share one living memory with your team
176
+
177
+ Hunch can keep a team's memory in a dedicated private Git repository, separate from the code. Hunch
178
+ does not host that repository. Give teammates and CI normal Git access, keep credentials in SSH or
179
+ the Git credential helper, and have one maintainer connect it:
268
180
 
269
181
  ```bash
270
- hunch private --repo git@github.com:you/project-memory.git
182
+ npm i -g @davesheffer/hunch@1.22.1
183
+ hunch shared --repo git@github.com:acme/project-hunch-memory.git
184
+ git add .gitignore .hunch/team.json
185
+ git commit -m "chore: connect shared Hunch memory"
186
+ git push
271
187
  ```
272
188
 
273
- Local tools see the combined graph; public CI and committed documentation stay public-only.
189
+ Teammates then install the same version and run:
274
190
 
275
- ## Releases you can trace to source
276
-
277
- Hunch releases are built and tested without publication credentials. The resulting npm tarball or
278
- VSIX is content-addressed, carried unchanged into a minimal publisher, and checked again against the
279
- registry after publication. The npm path also runs native, atomic-write, and Matrix safety checks on
280
- Windows and macOS and verifies provenance back to the exact source tag.
191
+ ```bash
192
+ npm i -g @davesheffer/hunch@1.22.1
193
+ git pull
194
+ hunch init
195
+ hunch doctor
196
+ ```
281
197
 
282
- The editor companion is published from an exact `vscode-v*` tag to
283
- [Open VSX](https://open-vsx.org/extension/davesheffer/hunch-vscode). The workflow verifies the
284
- downloaded public VSIX has the same digest as the credential-free release candidate.
198
+ The committed pointer contains a credential-free repository locator and branch. The local clone,
199
+ paths, preferences, and private overlays stay ignored. MCP sessions refresh shared memory at tool
200
+ boundaries, and failed pushes are retried by a later capture or `hunch shared --sync`.
201
+
202
+ Use `hunch firmness off` to pause hook enforcement without deleting history. Use
203
+ `hunch shared --repo <url> --no-auto-commit` when captures should remain local until an explicit
204
+ `hunch shared --sync`.
205
+
206
+ ## Trust boundaries that stay visible
207
+
208
+ - **Local-first.** Hunch has no hosted memory service or telemetry. The graph travels with Git and
209
+ speaks MCP instead of belonging to one editor or model provider.
210
+ - **Private when needed.** `hunch private --repo <url>` keeps sensitive reasoning in a separate
211
+ overlay. Local tools see the union; public CI and documentation remain public-only.
212
+ - **Human authority.** Observations, generated drafts, imported ADRs, discovered landscape records,
213
+ and proved policy candidates do not silently become trusted truth.
214
+ - **Deterministic core.** Indexing, retrieval receipts, currentness, conformance, checks, Project
215
+ DNA discovery, and policy evaluation do not require a model.
216
+ - **No surprise synthesis bill.** Optional drafting can use a selected Claude Code, Codex, or
217
+ Cursor subscription CLI, a local OpenAI-compatible endpoint, or the deterministic fallback.
218
+ Public remote endpoints require explicit `HUNCH_SYNTH_ALLOW_METERED=1` opt-in.
219
+ - **Traceable releases.** npm packages and the VS Code extension are content-addressed, verified
220
+ against their public registries, and tied back to exact source tags.
285
221
 
286
222
  ## Learn more
287
223
 
288
224
  - [Full documentation](https://hunch-pi.vercel.app/docs)
289
225
  - [Copy-paste cookbook](https://hunch-pi.vercel.app/cookbook)
290
- - [VS Code extension guide](vscode-extension/README.md)
291
- - [Contributing](CONTRIBUTING.md)
226
+ - [Project DNA](docs/project-dna.md)
227
+ - [Engineering Landscape Graph](docs/engineering-landscape.md)
228
+ - [Hunch roadmap](ROADMAP.md)
229
+ - [VS Code extension](vscode-extension/README.md)
292
230
  - [Architecture benchmark](bench/architectural-conformance.md)
293
- - [Engineering Landscape Graph and ORC boundary](docs/engineering-landscape.md)
294
- - [ORC outcome/experience protocol](docs/outcome-experience-protocol.md)
295
- - [Competitive landscape (dated; re-verify before quoting)](docs/competitive-landscape.md)
231
+ - [Contributing](CONTRIBUTING.md)
296
232
 
297
233
  Apache-2.0
@@ -10,6 +10,8 @@ import { languageFor } from "./languages.js";
10
10
  const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", ".hunch", "coverage", ".next", "out"]);
11
11
  const ORDINARY_BLOB_MODES = new Set(["100644", "100755"]);
12
12
  const GIT_MAX_LISTING_BYTES = 64 * 1024 * 1024;
13
+ const GIT_MAX_BLOB_BATCH_BYTES = 64 * 1024 * 1024;
14
+ const GIT_MAX_BLOB_BATCH_ITEMS = 128;
13
15
  function rawContentHash(bytes) {
14
16
  return `sha1:${createHash("sha1").update(bytes).digest("hex")}`;
15
17
  }
@@ -52,6 +54,16 @@ function gitBuffer(root, args, maxBuffer = GIT_MAX_LISTING_BYTES, preserveInvoca
52
54
  timeout: 15_000,
53
55
  });
54
56
  }
57
+ function gitBufferInput(root, args, input, maxBuffer) {
58
+ return execFileSync("git", ["-C", root, ...args], {
59
+ encoding: "buffer",
60
+ env: gitEnv(),
61
+ input: Buffer.from(input, "ascii"),
62
+ maxBuffer,
63
+ stdio: ["pipe", "pipe", "ignore"],
64
+ timeout: 15_000,
65
+ });
66
+ }
55
67
  function gitText(root, args) {
56
68
  return gitBuffer(root, args, 1024 * 1024).toString("utf8").trim();
57
69
  }
@@ -171,6 +183,122 @@ function gitBlobEntry(root, path, mode, oid) {
171
183
  },
172
184
  };
173
185
  }
186
+ /** Resolve blob type/size for a complete immutable tree in one Git process.
187
+ * Oversized blobs are rejected before hydration, preserving the source byte
188
+ * limit without paying one `cat-file -t` and `cat-file -s` spawn per path. */
189
+ function checkedGitBlobs(root, candidates) {
190
+ if (candidates.length === 0)
191
+ return { readable: [], rejected: [] };
192
+ const raw = gitBufferInput(root, ["cat-file", "--batch-check=%(objectname) %(objecttype) %(objectsize)"], `${candidates.map((candidate) => candidate.oid).join("\n")}\n`, GIT_MAX_LISTING_BYTES).toString("ascii");
193
+ const lines = raw.endsWith("\n") ? raw.slice(0, -1).split("\n") : raw.split("\n");
194
+ if (lines.length !== candidates.length)
195
+ throw new Error("Git blob batch check returned an incomplete result");
196
+ const readable = [];
197
+ const rejected = [];
198
+ for (const [index, candidate] of candidates.entries()) {
199
+ const line = lines[index] ?? "";
200
+ const match = line.match(/^([0-9a-f]{40,64}) ([a-z]+) ([0-9]+)$/i);
201
+ const size = match ? Number(match[3]) : Number.NaN;
202
+ if (!match || match[1].toLowerCase() !== candidate.oid || match[2] !== "blob"
203
+ || !Number.isSafeInteger(size) || size < 0) {
204
+ rejected.push(issueEntry(candidate.path, candidate.mode, "read_failed", `${candidate.path} does not resolve to an ordinary blob`));
205
+ }
206
+ else if (size > MAX_REPO_SOURCE_FILE_BYTES) {
207
+ rejected.push(issueEntry(candidate.path, candidate.mode, "oversized", `${candidate.path} exceeds the ${MAX_REPO_SOURCE_FILE_BYTES}-byte source limit`));
208
+ }
209
+ else {
210
+ readable.push({ ...candidate, size });
211
+ }
212
+ }
213
+ return { readable, rejected };
214
+ }
215
+ function readGitBlobBatch(root, batch) {
216
+ try {
217
+ const expectedBytes = batch.reduce((total, candidate) => total + candidate.size + 160, 0);
218
+ const raw = gitBufferInput(root, ["cat-file", "--batch"], `${batch.map((candidate) => candidate.oid).join("\n")}\n`, Math.max(1024 * 1024, expectedBytes));
219
+ const results = [];
220
+ let cursor = 0;
221
+ for (const candidate of batch) {
222
+ const headerEnd = raw.indexOf(0x0a, cursor);
223
+ if (headerEnd < 0)
224
+ throw new Error("Git blob batch omitted an object header");
225
+ const header = raw.subarray(cursor, headerEnd).toString("ascii");
226
+ const match = header.match(/^([0-9a-f]{40,64}) blob ([0-9]+)$/i);
227
+ const size = match ? Number(match[2]) : Number.NaN;
228
+ if (!match || match[1].toLowerCase() !== candidate.oid || size !== candidate.size) {
229
+ throw new Error("Git blob batch returned a different object than requested");
230
+ }
231
+ const contentStart = headerEnd + 1;
232
+ const contentEnd = contentStart + size;
233
+ if (contentEnd >= raw.length || raw[contentEnd] !== 0x0a) {
234
+ throw new Error("Git blob batch returned a truncated object");
235
+ }
236
+ results.push(decodedSource(candidate.path, candidate.mode, raw.subarray(contentStart, contentEnd)));
237
+ cursor = contentEnd + 1;
238
+ }
239
+ if (cursor !== raw.length)
240
+ throw new Error("Git blob batch returned trailing bytes");
241
+ return results;
242
+ }
243
+ catch {
244
+ return batch.map((candidate) => ({
245
+ source: null,
246
+ mode: candidate.mode,
247
+ issue: {
248
+ path: candidate.path,
249
+ code: "read_failed",
250
+ detail: `${candidate.path} blob could not be read`,
251
+ },
252
+ }));
253
+ }
254
+ }
255
+ /** Hydrate exact-tree blobs through bounded `git cat-file --batch` groups.
256
+ * Normal scan consumers read each entry once; a repeated read retains the old
257
+ * entry contract by falling back to the single-blob path. Completed batches
258
+ * release their decoded payloads so a large repo never accumulates every source
259
+ * body in memory at once. */
260
+ function batchedGitBlobEntries(root, candidates) {
261
+ const groups = [];
262
+ let group = [];
263
+ let groupBytes = 0;
264
+ for (const candidate of candidates) {
265
+ if (group.length > 0 && (group.length >= GIT_MAX_BLOB_BATCH_ITEMS
266
+ || groupBytes + candidate.size > GIT_MAX_BLOB_BATCH_BYTES)) {
267
+ groups.push(group);
268
+ group = [];
269
+ groupBytes = 0;
270
+ }
271
+ group.push(candidate);
272
+ groupBytes += candidate.size;
273
+ }
274
+ if (group.length > 0)
275
+ groups.push(group);
276
+ return groups.flatMap((batch) => {
277
+ let loaded = null;
278
+ let unread = batch.length;
279
+ return batch.map((candidate, index) => {
280
+ let firstRead = true;
281
+ return {
282
+ path: candidate.path,
283
+ mode: candidate.mode,
284
+ read: () => {
285
+ if (!firstRead)
286
+ return gitBlobEntry(root, candidate.path, candidate.mode, candidate.oid).read();
287
+ firstRead = false;
288
+ loaded ??= readGitBlobBatch(root, batch);
289
+ const result = loaded[index];
290
+ if (!result)
291
+ throw new Error(`Git blob batch lost ${candidate.path}`);
292
+ loaded[index] = undefined;
293
+ unread--;
294
+ if (unread === 0)
295
+ loaded = null;
296
+ return result;
297
+ },
298
+ };
299
+ });
300
+ });
301
+ }
174
302
  function indexRows(root, preserveInvocationIndex = false) {
175
303
  const raw = gitBuffer(root, ["ls-files", "--cached", "--stage", "-z"], GIT_MAX_LISTING_BYTES, preserveInvocationIndex);
176
304
  const rows = [];
@@ -253,6 +381,7 @@ function treeInventory(root, kind, ref) {
253
381
  const revision = exactCommit(root, ref);
254
382
  const raw = gitBuffer(root, ["ls-tree", "--full-tree", "-r", "-z", revision]);
255
383
  const entries = [];
384
+ const blobCandidates = [];
256
385
  for (const record of nulRecords(raw)) {
257
386
  const tab = record.indexOf(0x09);
258
387
  if (tab < 0)
@@ -284,9 +413,11 @@ function treeInventory(root, kind, ref) {
284
413
  entries.push(issueEntry(path, mode, "unsafe_mode", `${path} uses unsupported Git ${type} mode ${mode}`));
285
414
  }
286
415
  else {
287
- entries.push(gitBlobEntry(root, path, mode, oid));
416
+ blobCandidates.push({ path, mode, oid });
288
417
  }
289
418
  }
419
+ const checked = checkedGitBlobs(root, blobCandidates);
420
+ entries.push(...checked.rejected, ...batchedGitBlobEntries(root, checked.readable));
290
421
  entries.sort((left, right) => compareCodeUnits(left.path, right.path));
291
422
  return { identity: { kind, revision }, entries };
292
423
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davesheffer/hunch",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "mcpName": "io.github.davesheffer/hunch",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dave Sheffer <dave.sheffer1@gmail.com>",
package/server.json CHANGED
@@ -7,13 +7,13 @@
7
7
  "source": "github"
8
8
  },
9
9
  "websiteUrl": "https://hunch-pi.vercel.app",
10
- "version": "1.22.0",
10
+ "version": "1.22.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "registryBaseUrl": "https://registry.npmjs.org",
15
15
  "identifier": "@davesheffer/hunch",
16
- "version": "1.22.0",
16
+ "version": "1.22.1",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {