@davesheffer/hunch 1.24.0 → 1.26.0
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 +125 -133
- package/dist/cli/index.js +2 -0
- package/dist/cli/serve.js +64 -0
- package/dist/client/state.js +48 -0
- package/dist/core/provenance.js +43 -0
- package/dist/core/stateContract.js +250 -0
- package/dist/core/stateRecords.js +150 -0
- package/dist/core/types.js +16 -29
- package/dist/integrations/gitignore.js +8 -0
- package/dist/mcp/server.js +75 -0
- package/dist/serve/app.js +186 -0
- package/dist/serve/config.js +121 -0
- package/dist/serve/writelock.js +116 -0
- package/dist/store/changeLedger.js +96 -0
- package/dist/store/jsonStore.js +5 -0
- package/dist/store/stateBinding.js +398 -0
- package/package.json +2 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -6,28 +6,20 @@
|
|
|
6
6
|
[](https://github.com/davesheffer/hunch)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
|
|
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.
|
|
9
|
+
Every new AI coding session can read your code. It cannot automatically see why your team chose this design, which alternative already failed, what an odd-looking line protects, or how this repository expects work to be explained and reviewed.
|
|
12
10
|
|
|
13
|
-
That is how settled decisions get reopened, fixed bugs return, and technically plausible changes
|
|
14
|
-
arrive feeling foreign to the project.
|
|
11
|
+
That is how settled decisions get reopened, fixed bugs return, and technically plausible changes arrive feeling foreign to the project.
|
|
15
12
|
|
|
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:
|
|
13
|
+
**Hunch is evidence-backed deterministic project intelligence for the AI coding tools you already use.** It gives Claude, Codex, Cursor, Copilot, Windsurf, Antigravity and other MCP clients the same durable understanding of your codebase:
|
|
19
14
|
|
|
20
15
|
- why the code is shaped this way;
|
|
21
|
-
- how the repository communicates, reviews
|
|
16
|
+
- how the repository communicates, reviews and builds;
|
|
22
17
|
- what depends on the code about to change; and
|
|
23
|
-
- which trusted decisions, fixes
|
|
18
|
+
- which trusted decisions, fixes and architectural boundaries the result must preserve.
|
|
24
19
|
|
|
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.
|
|
20
|
+
For precise rules your team has explicitly trusted, the promise is **Never Twice**: an agent may propose a different direction, but it cannot quietly re-make a decided decision or re-introduce a fixed failure without Hunch surfacing the conflict and its evidence.
|
|
28
21
|
|
|
29
|
-
Memory starts advisory. Nothing blocks until a human deliberately trusts a precise rule and opts
|
|
30
|
-
into strict enforcement.
|
|
22
|
+
Memory starts advisory. Nothing blocks until a human deliberately trusts a precise rule and opts into strict enforcement.
|
|
31
23
|
|
|
32
24
|
## Start in five minutes
|
|
33
25
|
|
|
@@ -44,30 +36,19 @@ Reload your coding assistant, then ask a normal question:
|
|
|
44
36
|
|
|
45
37
|
> Why is this built this way?
|
|
46
38
|
|
|
47
|
-
`hunch init` indexes the repository, installs local lifecycle hooks
|
|
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. Lifecycle coverage
|
|
50
|
-
depends on the harness; MCP connectivity alone does not establish automatic grounding or enforcement.
|
|
39
|
+
`hunch init` indexes the repository, installs local lifecycle hooks and connects supported assistants without replacing their existing configuration. The next session receives the relevant story with its sources, not a giant transcript or generic prompt wall.
|
|
51
40
|
|
|
52
|
-
|
|
41
|
+
Lifecycle coverage depends on the harness; MCP connectivity alone does not establish automatic grounding or enforcement.
|
|
42
|
+
|
|
43
|
+
To update Hunch and configured harness pins for the current repository:
|
|
53
44
|
|
|
54
45
|
```sh
|
|
55
46
|
hunch update
|
|
56
47
|
```
|
|
57
48
|
|
|
58
|
-
Agents receive an instruction to run this when you ask **“update Hunch”** in
|
|
59
|
-
Hunch guidance. The command resolves npm's latest release, updates an existing dependency
|
|
60
|
-
to an exact version in its current dependency section, then runs the new version's pin
|
|
61
|
-
repair and integration check. Without a repository dependency it updates the global CLI.
|
|
62
|
-
Use `--global` to also update the global CLI when a local dependency exists, or `--dry-run`
|
|
63
|
-
to preview the commands. Restart active harnesses afterward. This applies to the current
|
|
64
|
-
repository, not every project on your machine. Automatic dependency installation currently
|
|
65
|
-
supports standalone npm projects; other package managers should update their dependency
|
|
66
|
-
explicitly and then use `hunch integrations repair-pins`. Existing hook settings are preserved.
|
|
67
|
-
An installation or check failure stops the command with a nonzero exit; completed npm
|
|
68
|
-
changes are not rolled back. Fix the reported issue and rerun the command.
|
|
49
|
+
Agents receive an instruction to run this when you ask **“update Hunch”** in generated Hunch guidance. Restart active harnesses afterward.
|
|
69
50
|
|
|
70
|
-
Check
|
|
51
|
+
Check integrations after upgrading Hunch or switching assistants:
|
|
71
52
|
|
|
72
53
|
```sh
|
|
73
54
|
hunch integrations check
|
|
@@ -76,25 +57,11 @@ hunch integrations check --harness claude --probe --require mcp
|
|
|
76
57
|
hunch integrations check --harness codex --require context,edit-blocking
|
|
77
58
|
```
|
|
78
59
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Capabilities are reported as **verified**, **advisory-only**, **unsupported**, or **untested**.
|
|
86
|
-
`--require` fails unless every named capability is verified. The Codex example currently fails:
|
|
87
|
-
Hunch's Codex integration supplies MCP and instructions, with no native lifecycle adapter.
|
|
88
|
-
The opt-in `--probe` starts the selected generated npm launcher, checks the server version,
|
|
89
|
-
and reads memory; it may download the pinned package. It verifies a fresh MCP process only,
|
|
90
|
-
not the existing host session, hook delivery, or whether a model follows the memory.
|
|
91
|
-
Custom launchers and environment overrides require host-side verification.
|
|
92
|
-
|
|
93
|
-
`doctor` includes this report, and session hooks with a context channel surface configuration
|
|
94
|
-
problems. Checks cover repository-local configurations; global/managed overrides remain
|
|
95
|
-
outside this inspection. Use `hunch integrations check` in CI to prevent pin drift; add
|
|
96
|
-
`--require` for capabilities your workflow cannot operate without. Hook failure remains
|
|
97
|
-
non-blocking; this explicit CI/preflight gate fails closed on unmet requirements.
|
|
60
|
+
Capabilities are reported as **verified**, **advisory-only**, **unsupported** or **untested**. `--require` fails unless every named capability is verified.
|
|
61
|
+
|
|
62
|
+
The Codex integration currently supplies MCP and instructions, with no native lifecycle adapter. The opt-in `--probe` verifies a fresh MCP process, not whether an existing host session or model actually followed the memory.
|
|
63
|
+
|
|
64
|
+
Use `hunch integrations check` in CI to prevent pin drift; add `--require` for capabilities your workflow cannot operate without.
|
|
98
65
|
|
|
99
66
|
## One evidence loop, not another model
|
|
100
67
|
|
|
@@ -116,37 +83,29 @@ Git history + ADRs + corrections + tests + repository conventions
|
|
|
116
83
|
deterministic change receipt
|
|
117
84
|
```
|
|
118
85
|
|
|
119
|
-
Hunch is not
|
|
120
|
-
validation layer behind the tools that write the code.
|
|
86
|
+
Hunch is not the agent and is not the workflow engine. The model thinks; Hunch holds deterministic, evidence-backed state about the project and validates what must remain true.
|
|
121
87
|
|
|
122
88
|
## What Hunch understands
|
|
123
89
|
|
|
124
90
|
| Layer | What it adds |
|
|
125
91
|
| --- | --- |
|
|
126
|
-
| **Engineering Memory** | Decisions, rejected alternatives, corrections, bug lineage, findings
|
|
127
|
-
| **Code Graph** | Symbols, calls, imports, dependencies, components, blast radius
|
|
128
|
-
| **Project DNA** | Revision-specific, evidence-backed observations about how a repository communicates and works: vocabulary, contribution habits, review expectations, engineering conventions
|
|
129
|
-
| **Engineering Landscape** |
|
|
130
|
-
| **Validated Delivery** | The smallest relevant evidence for the current builder
|
|
131
|
-
| **Native Change Proof** | A sealed exact-change artifact binding revisions, DNA, base/result graphs, memory, blast radius, conformance, guard verdict
|
|
132
|
-
| **Change Gate + Constitution** | Deterministic checks for trusted constraints and architectural intent. Policies are compiled, proved, inspected
|
|
92
|
+
| **Engineering Memory** | Decisions, rejected alternatives, corrections, bug lineage, findings and rationale a future session would otherwise miss. |
|
|
93
|
+
| **Code Graph** | Symbols, calls, imports, dependencies, components, blast radius and architectural reachability across supported languages/configuration. Memory itself works with any language. |
|
|
94
|
+
| **Project DNA** | Revision-specific, evidence-backed observations about how a repository communicates and works: vocabulary, contribution habits, review expectations, engineering conventions and culture. |
|
|
95
|
+
| **Engineering Landscape** | Durable links from product/capability to system, repository, service, interface, data, delivery resources, runbooks, ownership, dashboards and SLOs. |
|
|
96
|
+
| **Validated Delivery** | The smallest relevant evidence for the current builder/reviewer/architect, with provenance, currentness, omissions, authority and a content-addressed receipt. |
|
|
97
|
+
| **Native Change Proof** | A sealed exact-change artifact binding revisions, DNA, base/result graphs, memory, blast radius, conformance, guard verdict and explicit gaps without granting workflow authority. |
|
|
98
|
+
| **Change Gate + Constitution** | Deterministic checks for trusted constraints and architectural intent. Policies are compiled, proved, inspected and explicitly activated by a human. |
|
|
133
99
|
|
|
134
|
-
Readable JSON in `.hunch/` is the source of truth. SQLite is a fast, rebuildable projection. Git
|
|
135
|
-
keeps the memory portable, reviewable, and reversible.
|
|
100
|
+
Readable JSON in `.hunch/` is the repository-scoped source of truth. SQLite is a fast, rebuildable projection. Git keeps the state portable, reviewable and reversible.
|
|
136
101
|
|
|
137
102
|
## Project DNA: help the agent work like it belongs here
|
|
138
103
|
|
|
139
|
-
Project DNA is Hunch's evidence-bound model of **how a repository communicates and works**. It is
|
|
140
|
-
not a persona, does not impersonate a maintainer, and does not turn frequent behavior into policy.
|
|
104
|
+
Project DNA is Hunch's evidence-bound model of **how a repository communicates and works**. It is not a persona, does not impersonate a maintainer and does not turn frequent behavior into policy.
|
|
141
105
|
|
|
142
|
-
The deterministic baseline reads an exact Git revision, bounded commit history, and
|
|
143
|
-
convention files. The current release can also accept bounded, caller-authorized pull-request and
|
|
144
|
-
review evidence. Every evidence batch is validated and sealed; raw collaboration text does not
|
|
145
|
-
enter the profile.
|
|
106
|
+
The deterministic baseline reads an exact Git revision, bounded commit history and committed convention files. The current release can also accept bounded, caller-authorized pull-request/review evidence. Every evidence batch is validated and sealed; raw collaboration text does not enter the profile.
|
|
146
107
|
|
|
147
|
-
Each trait keeps its category, confidence, freshness, repository revision
|
|
148
|
-
can then include only the relevant DNA in normal context, explain how well a commit, PR, issue, or
|
|
149
|
-
message matches repository conventions, and show how the profile changed between two revisions.
|
|
108
|
+
Each trait keeps its category, confidence, freshness, repository revision and evidence hash. Hunch can include only the relevant DNA in normal context, explain how an artifact matches repository conventions and show profile change between revisions.
|
|
150
109
|
|
|
151
110
|
```bash
|
|
152
111
|
hunch dna inspect
|
|
@@ -154,32 +113,27 @@ hunch dna context
|
|
|
154
113
|
hunch dna diff <older-ref> <newer-ref>
|
|
155
114
|
```
|
|
156
115
|
|
|
157
|
-
DNA may shape orientation, terminology
|
|
158
|
-
override a decision, constraint, finding, conformance rule, policy, or permission.
|
|
116
|
+
DNA may shape orientation, terminology and advisory Project Match checks. It cannot create or override a decision, constraint, finding, conformance rule, policy or permission.
|
|
159
117
|
|
|
160
|
-
Read the [Project DNA contract](docs/project-dna.md) and
|
|
161
|
-
[Project DNA vision](docs/project-dna-engine.md).
|
|
118
|
+
Read the [Project DNA contract](docs/project-dna.md) and broader [Project DNA vision](docs/project-dna-engine.md).
|
|
162
119
|
|
|
163
120
|
## Day-to-day
|
|
164
121
|
|
|
165
|
-
Most capture happens around normal commits and test failures. These commands cover the common
|
|
166
|
-
manual paths:
|
|
167
|
-
|
|
168
122
|
| Command | Use it for |
|
|
169
123
|
| --- | --- |
|
|
170
|
-
| `hunch context "<task>" --profile builder` | Get a bounded builder
|
|
171
|
-
| `hunch why <file-or-symbol>` | See
|
|
172
|
-
| `hunch structure [target]` | Inspect
|
|
124
|
+
| `hunch context "<task>" --profile builder` | Get a bounded builder/reviewer/architect brief before work starts |
|
|
125
|
+
| `hunch why <file-or-symbol>` | See decisions, bugs, constraints and blast radius behind code |
|
|
126
|
+
| `hunch structure [target]` | Inspect indexed repository shape without repeated search rounds |
|
|
173
127
|
| `hunch findings [scope]` | Inherit known-but-unfixed gaps instead of rediscovering them |
|
|
174
128
|
| `hunch check --working` | Review the current tree against trusted project rules |
|
|
175
129
|
| `hunch conform` | Prove the code still satisfies recorded architectural intent |
|
|
176
130
|
| `hunch impact origin/main` | See the dependency and memory surface of a branch |
|
|
177
|
-
| `hunch compare branch-a branch-b` | Rank candidate changes by
|
|
131
|
+
| `hunch compare branch-a branch-b` | Rank candidate changes by invariant/decision conflicts |
|
|
178
132
|
| `hunch prove origin/main --public-only` | Produce a publication-safe `hunch.change-proof/1` artifact for an exact committed change |
|
|
179
133
|
| `hunch landscape review` | Inspect a hash-bound repository landscape without writing authority |
|
|
180
|
-
| `hunch now` | See recent memory and
|
|
181
|
-
| `hunch escalations` | See
|
|
182
|
-
| `hunch doctor` | Diagnose setup, provider, index
|
|
134
|
+
| `hunch now` | See recent memory and live decision-backed roadmap |
|
|
135
|
+
| `hunch escalations` | See questions that genuinely require a human answer |
|
|
136
|
+
| `hunch doctor` | Diagnose setup, provider, index or overlay problems |
|
|
183
137
|
|
|
184
138
|
When you are ready for deterministic enforcement:
|
|
185
139
|
|
|
@@ -188,45 +142,78 @@ hunch firmness strict
|
|
|
188
142
|
hunch check --staged --strict
|
|
189
143
|
```
|
|
190
144
|
|
|
191
|
-
|
|
145
|
+
Captured memory cannot silently hard-block on its own.
|
|
192
146
|
|
|
193
|
-
##
|
|
147
|
+
## Deterministic organizational state — next product direction
|
|
148
|
+
|
|
149
|
+
Repository memory solves one version of a larger problem.
|
|
150
|
+
|
|
151
|
+
As organizations give every employee an agent that can work across CRM, email, messaging, repositories and other tools, the agents become probabilistic writers/readers of the same organization. If each one independently reconstructs what was decided, what was already done or what is still owed, the organization gets multiple conflicting realities.
|
|
152
|
+
|
|
153
|
+
The active roadmap asks whether Hunch can become the deterministic state layer between those agents and the organization:
|
|
154
|
+
|
|
155
|
+
> **Agents are probabilistic. Organizations need deterministic state. Hunch is the state layer between them.**
|
|
156
|
+
|
|
157
|
+
The target is **one product, one authorized state graph and one versioned state contract** across repository, user, team and organization scopes.
|
|
158
|
+
|
|
159
|
+
Planned state includes:
|
|
160
|
+
|
|
161
|
+
- decisions currently in force;
|
|
162
|
+
- verified action receipts / what was done;
|
|
163
|
+
- commitments and due-state;
|
|
164
|
+
- entities and relationships;
|
|
165
|
+
- code/system changes with proof;
|
|
166
|
+
- repository/user/team/org DNA;
|
|
167
|
+
- derived current state with exact dependencies/invalidation.
|
|
168
|
+
|
|
169
|
+
Agents continue to own live connector mechanics. Hunch must **not** become a managed proxy that fetches Gmail, CRM, WhatsApp or GitHub on an agent's behalf.
|
|
194
170
|
|
|
195
|
-
|
|
196
|
-
whole product.
|
|
171
|
+
Instead:
|
|
197
172
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
173
|
+
```text
|
|
174
|
+
Hunch deterministic state
|
|
175
|
+
↑ ↓
|
|
176
|
+
agent -> deterministic action gate -> connector -> source system
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Hunch may hold durable state **about** external work with credential-free provenance pointers, but should not mirror raw source-system contents into a universal cache.
|
|
180
|
+
|
|
181
|
+
The first real-world pilot is **Sofia**, a working operations agent over CRM, Gmail and WhatsApp. Sofia's approved actions, follow-ups, customer/source relationships and cited summaries map naturally to action receipts, commitments, entities/relationships and dependency-bound state.
|
|
182
|
+
|
|
183
|
+
The pilot measures whether Sofia and a second, different agent stop re-deriving contradictory state when the deterministic state is delivered before they answer or act.
|
|
184
|
+
|
|
185
|
+
### The state contract, shipped
|
|
186
|
+
|
|
187
|
+
As of 1.25.0 the contract exists as code: `nuryel.state/1` — three verbs (`read` under the
|
|
188
|
+
delivery envelope's receipt, `write` with provenance and an idempotency key, `subscribe` to a
|
|
189
|
+
strictly ordered change stream) over five new record facets: action receipts, commitments,
|
|
190
|
+
derived state that names what it rests on, external entities and their relationships. Ids are
|
|
191
|
+
derived from a record's facts, a replay returns the original, a second live decision on a topic is
|
|
192
|
+
refused with the incumbent named, and derived state without dependencies is not state. Each scope
|
|
193
|
+
keeps a git-native change ledger under `.hunch/changes/`; organization, team and user partitions
|
|
194
|
+
are homed in an overlay, never in a repository. The MCP server binds it as `nuryel_capabilities`,
|
|
195
|
+
`nuryel_read`, `nuryel_write` and `nuryel_subscribe`; every other transport will call the same
|
|
196
|
+
store binding. Contract and evidence: [docs/nuryel-state-contract.md](docs/nuryel-state-contract.md).
|
|
206
197
|
|
|
207
|
-
|
|
208
|
-
|
|
198
|
+
As of 1.26.0 the state layer is also **served**: `hunch serve --config <file>` hosts organization,
|
|
199
|
+
team, user and repository partitions over HTTP on loopback with the same three verbs. A served
|
|
200
|
+
partition is a directory whose `.hunch/partition.json` names the scope it is; the bearer token
|
|
201
|
+
resolves the principal and grants come from the config only. `hunch serve init --partition
|
|
202
|
+
user:david --root <dir> --principal sofia@david` declares a partition and mints a token. The typed
|
|
203
|
+
client is `import { createStateClient } from "@davesheffer/hunch/state"`. This folds the separate
|
|
204
|
+
Hunch Memory service into Hunch.
|
|
209
205
|
|
|
210
|
-
|
|
211
|
-
<summary><strong>The scoped v1.19 benchmark</strong></summary>
|
|
206
|
+
Read [Deterministic organizational state](docs/deterministic-state.md) and the [roadmap](ROADMAP.md).
|
|
212
207
|
|
|
213
|
-
|
|
214
|
-
declaration in 6 cases instead of 3 and the correct file in 10 cases instead of 8. On a separate
|
|
215
|
-
12-case transfer, its progressive queue retained the same five successful finds while reducing the
|
|
216
|
-
average declarations to inspect from 18.9 to 11 (41.9% less).
|
|
208
|
+
### Naming
|
|
217
209
|
|
|
218
|
-
|
|
219
|
-
Failed evidence and causal rerankers remain disabled; evidence can annotate the shortlist but does
|
|
220
|
-
not reorder it or claim an exact correction owner. The detailed receipts live in
|
|
221
|
-
[`bench/external/results`](bench/external/results).
|
|
210
|
+
The product is still **Hunch**. A possible hosted-platform name, **Nuryel**, is intentionally deferred until the state contract and Sofia pilot have evidence. Rename work is not the deliverable.
|
|
222
211
|
|
|
223
|
-
|
|
212
|
+
## Share one living repository memory with your team
|
|
224
213
|
|
|
225
|
-
|
|
214
|
+
The current release can keep a team's **repository-scoped** memory in a dedicated private Git repository, separate from the code.
|
|
226
215
|
|
|
227
|
-
Hunch
|
|
228
|
-
does not host that repository. Give teammates and CI normal Git access, keep credentials in SSH or
|
|
229
|
-
the Git credential helper, and have one maintainer connect it:
|
|
216
|
+
Today Hunch does not host that shared Git repository; teammates/CI use normal Git access and one maintainer connects it:
|
|
230
217
|
|
|
231
218
|
```bash
|
|
232
219
|
npm i -g @davesheffer/hunch@1.23.3
|
|
@@ -245,34 +232,39 @@ hunch init
|
|
|
245
232
|
hunch doctor
|
|
246
233
|
```
|
|
247
234
|
|
|
248
|
-
The committed pointer contains a credential-free repository locator and branch.
|
|
249
|
-
paths, preferences, and private overlays stay ignored. MCP sessions refresh shared memory at tool
|
|
250
|
-
boundaries, and failed pushes are retried by a later capture or `hunch shared --sync`.
|
|
235
|
+
The committed pointer contains a credential-free repository locator and branch. Local clone paths, preferences and private overlays stay ignored. MCP sessions refresh shared memory at tool boundaries; failed pushes can be retried by later capture or `hunch shared --sync`.
|
|
251
236
|
|
|
252
|
-
Use `hunch firmness off` to pause hook enforcement without deleting history. Use
|
|
253
|
-
|
|
254
|
-
|
|
237
|
+
Use `hunch firmness off` to pause hook enforcement without deleting history. Use `hunch shared --repo <url> --no-auto-commit` when captures should remain local until explicit `hunch shared --sync`.
|
|
238
|
+
|
|
239
|
+
This existing Git-sharing feature is not the same thing as the planned organization/team/user state service. The roadmap extends the git-native model rather than declaring today's shared-memory repository to be an organization control plane.
|
|
255
240
|
|
|
256
241
|
## Trust boundaries that stay visible
|
|
257
242
|
|
|
258
|
-
- **Local-first.** Hunch
|
|
259
|
-
|
|
260
|
-
- **Private when needed.** `hunch private --repo <url>` keeps sensitive reasoning in a separate
|
|
261
|
-
|
|
262
|
-
- **
|
|
263
|
-
|
|
264
|
-
- **
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
-
|
|
270
|
-
|
|
243
|
+
- **Local-first today.** Repository Hunch works without a hosted service or telemetry. Git remains the repository-scoped authority.
|
|
244
|
+
- **Hosted state is a roadmap extension, not a shipped claim.** Organization/team/user state will require authenticated scope/visibility, idempotency and durability while preserving git-native truth.
|
|
245
|
+
- **Private when needed.** `hunch private --repo <url>` keeps sensitive repository reasoning in a separate overlay; public CI/documentation remain public-only.
|
|
246
|
+
- **Human authority.** Observations, generated drafts, imported ADRs, discovered landscape records and proved policy candidates do not silently become trusted truth.
|
|
247
|
+
- **Deterministic core.** Indexing, retrieval receipts, currentness, conformance, Project DNA discovery and policy evaluation do not require a model.
|
|
248
|
+
- **Agents own execution.** Hunch state does not silently grant connector permissions, send messages or become a source-system proxy.
|
|
249
|
+
- **No surprise synthesis bill.** Optional drafting can use a selected Claude Code, Codex or Cursor subscription CLI, a local OpenAI-compatible endpoint or deterministic fallback. Public remote endpoints require explicit opt-in.
|
|
250
|
+
- **Traceable releases.** npm packages and the VS Code extension are content-addressed, verified against public registries and tied back to exact source tags.
|
|
251
|
+
|
|
252
|
+
## What changed after v1.19
|
|
253
|
+
|
|
254
|
+
The v1.19 correction-search benchmark remains scoped evidence, but it no longer describes the whole product.
|
|
255
|
+
|
|
256
|
+
- **v1.20 — one validated path from reason to result.** Role-shaped context, reviewed Engineering Landscape fragments, exact change identity, PHP graph support and hash-bound ADR review moved source, provenance, currentness, omissions and human authority through one delivery contract.
|
|
257
|
+
- **v1.21 — Project DNA.** Hunch gained deterministic, revision-specific repository profiles, bounded DNA context delivery, explainable Project Match checks and auditable profile deltas.
|
|
258
|
+
- **v1.22 — authorized collaboration evidence.** Hosts can contribute bounded PR/review evidence to Project DNA through a typed, sealed contract without raw collaboration persistence or policy-authority change.
|
|
259
|
+
- **v1.23 — native change proof and proof-carrying evidence work.** Exact Git change identity, graph before/after, decisions/constraints, blast radius and Change Gate result can be bound into a sealed evidence artifact without granting authority.
|
|
260
|
+
|
|
261
|
+
See the [changelog](CHANGELOG.md) for release detail and the [roadmap](ROADMAP.md) for active work.
|
|
271
262
|
|
|
272
263
|
## Learn more
|
|
273
264
|
|
|
274
265
|
- [Full documentation](https://www.hunchmemory.com/docs)
|
|
275
266
|
- [Copy-paste cookbook](https://www.hunchmemory.com/cookbook)
|
|
267
|
+
- [Deterministic organizational state](docs/deterministic-state.md)
|
|
276
268
|
- [Project DNA](docs/project-dna.md)
|
|
277
269
|
- [Native change proof](docs/change-proof.md)
|
|
278
270
|
- [Engineering Landscape Graph](docs/engineering-landscape.md)
|
package/dist/cli/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import { writeFileAtomic } from "../core/io.js";
|
|
|
25
25
|
import { looksLikeCorrection, CORRECTION_NUDGE } from "../core/correction.js";
|
|
26
26
|
import { HUNCH_VERSION } from "../core/version.js";
|
|
27
27
|
import { registerIntegrationCommands } from "./integrations.js";
|
|
28
|
+
import { registerServeCommands } from "./serve.js";
|
|
28
29
|
import { registerUpdateCommand } from "./update.js";
|
|
29
30
|
import { inspectIntegrations, formatIntegrationHealth, integrationHealthFails, integrationSessionWarning } from "../integrations/health.js";
|
|
30
31
|
import { HunchStore } from "../store/hunchStore.js";
|
|
@@ -113,6 +114,7 @@ import { resolveInvocation, dim, synthesisStatusLines, maybeWarnOllamaContext }
|
|
|
113
114
|
const program = new Command();
|
|
114
115
|
program.name("hunch").description("Hunch — engineering memory and a deterministic Change Gate for AI-assisted codebases.").version(HUNCH_VERSION);
|
|
115
116
|
registerIntegrationCommands(program);
|
|
117
|
+
registerServeCommands(program);
|
|
116
118
|
registerUpdateCommand(program);
|
|
117
119
|
let openStore = null;
|
|
118
120
|
function openTeamStore(root, opts = {}) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { createServeApp } from "../serve/app.js";
|
|
3
|
+
import { initServeConfig, readServeConfig } from "../serve/config.js";
|
|
4
|
+
import { ScopeSchema, scopePath } from "../core/stateContract.js";
|
|
5
|
+
import { HUNCH_VERSION } from "../core/version.js";
|
|
6
|
+
function parseScopeArg(value) {
|
|
7
|
+
const m = /^([a-z]+):(.+)$/.exec(value.trim());
|
|
8
|
+
const parsed = m ? ScopeSchema.safeParse({ kind: m[1], id: m[2] }) : null;
|
|
9
|
+
if (!parsed?.success)
|
|
10
|
+
throw new Error(`partition must be kind:id (organization|team|user|repository), got "${value}"`);
|
|
11
|
+
return parsed.data;
|
|
12
|
+
}
|
|
13
|
+
export function registerServeCommands(program) {
|
|
14
|
+
const serve = program.command("serve")
|
|
15
|
+
.description("Serve nuryel.state/1 over HTTP for organization / team / user / repository partitions (binds 127.0.0.1; put it behind SSH or a reverse proxy)")
|
|
16
|
+
.option("--config <file>", "serve config (nuryel.serve-config/1)", "hunch-serve.json")
|
|
17
|
+
.option("--port <n>", "override the configured port")
|
|
18
|
+
.action((opts) => {
|
|
19
|
+
const config = readServeConfig(resolve(opts.config));
|
|
20
|
+
const port = opts.port ? Number(opts.port) : config.port;
|
|
21
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
22
|
+
throw new Error(`invalid port ${opts.port}`);
|
|
23
|
+
const app = createServeApp(config, { version: HUNCH_VERSION });
|
|
24
|
+
// Bind loopback, never expose a port: the orchestrator reaches it over an SSH hop or a
|
|
25
|
+
// reverse proxy that terminates TLS and auth of its own. Folded-in decision from Hunch Memory.
|
|
26
|
+
app.listen(port, "127.0.0.1", () => {
|
|
27
|
+
console.log(`hunch ${HUNCH_VERSION} serving nuryel.state/1 on http://127.0.0.1:${port} — ${config.partitions.map((p) => scopePath(p.scope)).join(", ")} (${config.principals.length} principal(s))`);
|
|
28
|
+
});
|
|
29
|
+
const stop = () => { app.close(() => { app.closeStores(); process.exit(0); }); };
|
|
30
|
+
process.on("SIGINT", stop);
|
|
31
|
+
process.on("SIGTERM", stop);
|
|
32
|
+
});
|
|
33
|
+
serve.command("init")
|
|
34
|
+
.description("Declare a partition directory and mint a principal token (printed once; only its hash is stored)")
|
|
35
|
+
.requiredOption("--partition <kind:id>", "the scope this directory IS, e.g. user:david or organization:ylm")
|
|
36
|
+
.requiredOption("--root <dir>", "directory whose .hunch/ holds the partition (created if missing)")
|
|
37
|
+
.option("--config <file>", "serve config to create or extend", "hunch-serve.json")
|
|
38
|
+
.option("--principal <id>", "principal to add or rotate, granted this partition")
|
|
39
|
+
.option("--kind <kind>", "principal kind: human | agent | service", "agent")
|
|
40
|
+
.option("--grant <kind:id...>", "additional partitions to grant the principal (must be served by this config)")
|
|
41
|
+
.option("--port <n>", "port to record in a new config")
|
|
42
|
+
.option("--json", "machine-readable output")
|
|
43
|
+
.action((opts) => {
|
|
44
|
+
if (!["human", "agent", "service"].includes(opts.kind))
|
|
45
|
+
throw new Error("--kind must be human, agent or service");
|
|
46
|
+
const scope = parseScopeArg(opts.partition);
|
|
47
|
+
const grants = [scope, ...(opts.grant ?? []).map(parseScopeArg)];
|
|
48
|
+
const result = initServeConfig({
|
|
49
|
+
file: resolve(opts.config), scope, root: resolve(opts.root),
|
|
50
|
+
...(opts.principal ? { principal: { id: opts.principal, kind: opts.kind, grants } } : {}),
|
|
51
|
+
...(opts.port ? { port: Number(opts.port) } : {}),
|
|
52
|
+
});
|
|
53
|
+
if (opts.json) {
|
|
54
|
+
console.log(JSON.stringify({ config: resolve(opts.config), partition: result.partition, token: result.token }));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
console.log(`partition ${scopePath(scope)} → ${result.partition.root}`);
|
|
58
|
+
console.log(`config: ${resolve(opts.config)} (${result.config.partitions.length} partition(s), ${result.config.principals.length} principal(s))`);
|
|
59
|
+
if (result.token)
|
|
60
|
+
console.log(`token for ${opts.principal} (shown once — only its sha256 is stored): ${result.token}`);
|
|
61
|
+
console.log(`start: hunch serve --config ${opts.config}`);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** A typed refusal from the server: the problem+json body, with `code` = its title. */
|
|
2
|
+
export class StateClientError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
problem;
|
|
6
|
+
constructor(status, code, problem) {
|
|
7
|
+
super(`${code}: ${problem.detail}`);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.problem = problem;
|
|
11
|
+
this.name = "StateClientError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function createStateClient(opts) {
|
|
15
|
+
const base = opts.baseUrl.replace(/\/+$/, "");
|
|
16
|
+
const doFetch = opts.fetch ?? fetch;
|
|
17
|
+
const timeoutMs = opts.timeoutMs ?? 15_000;
|
|
18
|
+
async function call(method, path, body) {
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
21
|
+
try {
|
|
22
|
+
const response = await doFetch(`${base}${path}`, {
|
|
23
|
+
method,
|
|
24
|
+
headers: { authorization: `Bearer ${opts.token}`, ...(body !== undefined ? { "content-type": "application/json" } : {}) },
|
|
25
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
26
|
+
signal: controller.signal,
|
|
27
|
+
});
|
|
28
|
+
const text = await response.text();
|
|
29
|
+
const parsed = text ? JSON.parse(text) : {};
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const p = parsed;
|
|
32
|
+
throw new StateClientError(response.status, p.title ?? String(response.status), p);
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
capabilities: (scope) => call("GET", `/nuryel/v1/capabilities${scope ? `?scope=${encodeURIComponent(`${scope.kind}:${scope.id}`)}` : ""}`),
|
|
42
|
+
read: (request) => call("POST", "/nuryel/v1/read", request),
|
|
43
|
+
write: (request) => call("POST", "/nuryel/v1/write", request),
|
|
44
|
+
subscribe: (request) => call("POST", "/nuryel/v1/subscribe", request),
|
|
45
|
+
health: () => call("GET", "/nuryel/v1/health"),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provenance and credential-free text — a LEAF module (zod only) so that record
|
|
3
|
+
* schemas registered in the store can import it without pulling in types.ts,
|
|
4
|
+
* which itself imports the store's kind registry. types.ts re-exports everything
|
|
5
|
+
* here, so existing imports keep working unchanged.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
/** Where a fact came from and how much to trust it. Confidence tiers (DESIGN §4):
|
|
9
|
+
* inferred < extracted < llm_draft < llm_draft+human_confirmed/derived. */
|
|
10
|
+
export const ProvenanceSchema = z.object({
|
|
11
|
+
source: z.string().describe("e.g. extracted | inferred | llm_draft | human_confirmed | test_failure+llm | derived"),
|
|
12
|
+
confidence: z.number().min(0).max(1),
|
|
13
|
+
evidence: z.array(z.string()).default([]).describe("file paths, commit ids, test ids backing the claim"),
|
|
14
|
+
last_verified: z.string().optional().describe("ISO timestamp of last re-validation"),
|
|
15
|
+
});
|
|
16
|
+
export const SENSITIVE_METADATA_KEY = /(^|[_-])(authorization|bearer|credential|password|passwd|private[_-]?key|secret|token|api[_-]?key)($|[_-])/i;
|
|
17
|
+
const SENSITIVE_ASSIGNMENT = /\b(authorization|password|passwd|private[_-]?key|secret|access[_-]?token|refresh[_-]?token|api[_-]?key)\s*[:=]\s*[^\s,;]{4,}/i;
|
|
18
|
+
const PRIVATE_KEY_BLOCK = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/i;
|
|
19
|
+
const BEARER_VALUE = /\bBearer\s+[A-Za-z0-9._~+\/-]{12,}/i;
|
|
20
|
+
/** Reject credential material while allowing ordinary architecture prose such as
|
|
21
|
+
* "authentication service" or "secrets are managed externally". */
|
|
22
|
+
export function isCredentialFreeText(value) {
|
|
23
|
+
if (PRIVATE_KEY_BLOCK.test(value) || BEARER_VALUE.test(value) || SENSITIVE_ASSIGNMENT.test(value))
|
|
24
|
+
return false;
|
|
25
|
+
try {
|
|
26
|
+
const url = new URL(value);
|
|
27
|
+
if (url.username || url.password)
|
|
28
|
+
return false;
|
|
29
|
+
for (const [key] of url.searchParams)
|
|
30
|
+
if (SENSITIVE_METADATA_KEY.test(key))
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
catch { /* credential-free canonical locators need not be absolute URLs */ }
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/** Bare secret shapes the general detector does not cover: it looks for assignments, bearer
|
|
37
|
+
* values, private-key blocks and URL userinfo; a token pasted on its own into an object key or
|
|
38
|
+
* locator would pass. These prefixes are the common ones. */
|
|
39
|
+
const BARE_SECRET = /(?:^|[^A-Za-z0-9])(?:ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9_-]{16,}|xox[abpr]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16}|eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})/;
|
|
40
|
+
export function isCredentialFreeValue(value) {
|
|
41
|
+
return isCredentialFreeText(value) && !BARE_SECRET.test(value);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=provenance.js.map
|