@davesheffer/hunch 1.13.0 → 1.13.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 +4 -4
- package/dist/mcp/server.js +89 -11
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ strict enforcement.
|
|
|
16
16
|
**Memory is the input. The product boundary is the receipt:** relevant evidence before an edit,
|
|
17
17
|
then a deterministic check of the change against the rules your team has explicitly trusted.
|
|
18
18
|
|
|
19
|
-
> **New in v1.13.
|
|
20
|
-
>
|
|
19
|
+
> **New in v1.13.1:** `hunch_context` exposes that provenance-checked, hard-budgeted delivery
|
|
20
|
+
> envelope as MCP structured output and records exactly which returned items reached the client.
|
|
21
21
|
|
|
22
22
|
See the public [roadmap](ROADMAP.md) for what is next and what is deliberately out of scope.
|
|
23
23
|
|
|
@@ -82,7 +82,7 @@ Git repo that every teammate can access, install the Matrix release on team mach
|
|
|
82
82
|
have one maintainer run:
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
npm i -g @davesheffer/hunch@1.13.
|
|
85
|
+
npm i -g @davesheffer/hunch@1.13.1
|
|
86
86
|
hunch shared --repo git@github.com:acme/project-hunch-memory.git
|
|
87
87
|
git add .gitignore .hunch/team.json
|
|
88
88
|
git commit -m "chore: connect shared Hunch memory"
|
|
@@ -97,7 +97,7 @@ printed by Hunch. Omit `--migrate` for a new setup.
|
|
|
97
97
|
After the pointer commit lands, teammates need Hunch installed and Git access to the memory repo:
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
npm i -g @davesheffer/hunch@1.13.
|
|
100
|
+
npm i -g @davesheffer/hunch@1.13.1
|
|
101
101
|
git pull
|
|
102
102
|
hunch init
|
|
103
103
|
hunch doctor
|
package/dist/mcp/server.js
CHANGED
|
@@ -21,7 +21,9 @@ import { refreshExistingGrounding } from "../integrations/providers.js";
|
|
|
21
21
|
import { revParse, asOfDate, revExists, lastChangeDate, rangeFiles, rangeDiff, commitFiles, commitDiff, stagedFiles, stagedDiff, workingFiles, workingDiff, pullHunchStatus, sameRemoteUrl } from "../extractors/git.js";
|
|
22
22
|
import { flushCapture, flushMemoryHome, pinSharedRemote } from "../integrations/sync.js";
|
|
23
23
|
import { advertisedTeamRemoteContract, ensureTeamOverlay, overlayMatchesTeamRemote, readTeamConfig, teamRemoteContract, teamSharedRef } from "../integrations/team.js";
|
|
24
|
-
import {
|
|
24
|
+
import { formatStructure } from "../core/format.js";
|
|
25
|
+
import { buildDeliveryEnvelope } from "../core/delivery.js";
|
|
26
|
+
import { recordServed } from "../core/served.js";
|
|
25
27
|
import { compareCandidates } from "../core/compare.js";
|
|
26
28
|
import { checkConformance } from "../core/conformance.js";
|
|
27
29
|
import { ConstitutionService, policyEvaluationEnvelope } from "../constitution/service.js";
|
|
@@ -82,6 +84,61 @@ const FINDINGS_CAP = 12; // hunch_findings listing
|
|
|
82
84
|
const SEV_CONSTRAINT = { blocking: 3, warning: 2, advisory: 1 };
|
|
83
85
|
const SEV_BUG = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
84
86
|
const more = (total, cap, hint = "") => total > cap ? `\n …(+${total - cap} more${hint ? ` — ${hint}` : ""})` : "";
|
|
87
|
+
/** Public MCP shape for the canonical delivery envelope. Keeping the schema on
|
|
88
|
+
* the tool means orchestrators can consume receipt facts without scraping the
|
|
89
|
+
* backward-compatible text block. */
|
|
90
|
+
const DELIVERY_OUTPUT_SCHEMA = z.object({
|
|
91
|
+
text: z.string(),
|
|
92
|
+
delivered: z.array(z.object({
|
|
93
|
+
kind: z.enum(["constraints", "decisions", "bugs", "findings"]),
|
|
94
|
+
record_id: z.string(),
|
|
95
|
+
rank: z.number().int().positive(),
|
|
96
|
+
delivery_reason: z.enum(["ranked", "blocking-reserved"]),
|
|
97
|
+
provenance_status: z.enum(["current", "unverified", "stale"]),
|
|
98
|
+
token_cost: z.number().int().nonnegative(),
|
|
99
|
+
})),
|
|
100
|
+
supplements: z.array(z.object({
|
|
101
|
+
id: z.string(),
|
|
102
|
+
kind: z.string(),
|
|
103
|
+
delivered: z.boolean(),
|
|
104
|
+
reason: z.enum(["supplemental", "budget", "empty"]),
|
|
105
|
+
rank: z.number().int().positive(),
|
|
106
|
+
token_cost: z.number().int().nonnegative(),
|
|
107
|
+
})),
|
|
108
|
+
omitted: z.array(z.object({
|
|
109
|
+
kind: z.enum(["constraints", "decisions", "bugs", "findings"]),
|
|
110
|
+
record_id: z.string(),
|
|
111
|
+
reason: z.enum(["budget", "stale-provenance", "retired"]),
|
|
112
|
+
detail: z.string(),
|
|
113
|
+
})),
|
|
114
|
+
budget_tokens: z.number().int().nonnegative(),
|
|
115
|
+
used_chars: z.number().int().nonnegative(),
|
|
116
|
+
blocking_overflow: z.boolean(),
|
|
117
|
+
});
|
|
118
|
+
/** Return the same human-readable brief older clients consume plus the exact
|
|
119
|
+
* machine-readable envelope. Receipt recording is deliberately best-effort:
|
|
120
|
+
* recordServed never throws, so telemetry can never cost a delivery. */
|
|
121
|
+
function deliveredContext(root, target, envelope, sessionId) {
|
|
122
|
+
// Validate before recording: if a future envelope change drifts from the
|
|
123
|
+
// advertised MCP contract, the SDK will reject the call and the local ledger
|
|
124
|
+
// must not claim that response was served.
|
|
125
|
+
const structuredContent = DELIVERY_OUTPUT_SCHEMA.parse(envelope);
|
|
126
|
+
recordServed(root, structuredContent.delivered.map((item) => ({
|
|
127
|
+
event: "served",
|
|
128
|
+
kind: item.kind,
|
|
129
|
+
record_id: item.record_id,
|
|
130
|
+
target,
|
|
131
|
+
session_id: sessionId,
|
|
132
|
+
rank: item.rank,
|
|
133
|
+
delivery_reason: item.delivery_reason,
|
|
134
|
+
provenance_status: item.provenance_status,
|
|
135
|
+
token_cost: item.token_cost,
|
|
136
|
+
})));
|
|
137
|
+
return {
|
|
138
|
+
content: [{ type: "text", text: structuredContent.text }],
|
|
139
|
+
structuredContent,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
85
142
|
// Capture-session tokens live in src/core/capturetoken.ts (pure + testable). These
|
|
86
143
|
// thin wrappers bind the process clock and id source at the call site (§5 Stage 1).
|
|
87
144
|
const issueCaptureToken = () => issueToken(randomUUID, Date.now());
|
|
@@ -589,11 +646,19 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
589
646
|
budget_tokens: z.number().optional().describe("Rough token budget for the brief (default 1500)."),
|
|
590
647
|
as_of: z.string().optional().describe("Time-travel ref (commit/tag/branch): assemble the slice as it stood then."),
|
|
591
648
|
},
|
|
592
|
-
|
|
649
|
+
outputSchema: DELIVERY_OUTPUT_SCHEMA,
|
|
650
|
+
}, async ({ target, budget_tokens, as_of }, extra) => {
|
|
593
651
|
const asOf = as_of ? asOfDate(as_of, root) : undefined;
|
|
594
652
|
if (as_of && !asOf)
|
|
595
653
|
return err(`Could not resolve as_of "${as_of}" to a commit.`);
|
|
596
654
|
const ctx = store.assembleContext(target, budget_tokens ?? 1500, { asOf });
|
|
655
|
+
const options = {
|
|
656
|
+
root,
|
|
657
|
+
symbols: store.recs("symbols"),
|
|
658
|
+
components: store.recs("components"),
|
|
659
|
+
decisionCorpus: store.recs("decisions"),
|
|
660
|
+
historical: !!asOf,
|
|
661
|
+
};
|
|
597
662
|
// Task-phrase input ("improve retrieval ranking") resolves no file/symbol and
|
|
598
663
|
// used to return an empty brief while the graph held the answer — fall back to
|
|
599
664
|
// FTS so the assistant always leaves with the closest matches, not a shrug.
|
|
@@ -601,17 +666,30 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
601
666
|
if (empty && !asOf) {
|
|
602
667
|
const hits = store.search(target, 8);
|
|
603
668
|
if (hits.length) {
|
|
604
|
-
const
|
|
605
|
-
|
|
669
|
+
const resolved = hits.map((hit) => ({ hit, record: store.resolve(hit.ref)?.record }));
|
|
670
|
+
const fallback = {
|
|
671
|
+
...ctx,
|
|
672
|
+
constraints: resolved.filter(({ hit, record }) => hit.kind === "constraints" && !!record).map(({ record }) => record),
|
|
673
|
+
decisions: resolved.filter(({ hit, record }) => hit.kind === "decisions" && !!record).map(({ record }) => record),
|
|
674
|
+
bugs: resolved.filter(({ hit, record }) => hit.kind === "bugs" && !!record).map(({ record }) => record),
|
|
675
|
+
findings: resolved.filter(({ hit, record }) => hit.kind === "findings" && !!record).map(({ record }) => record),
|
|
676
|
+
};
|
|
677
|
+
const envelope = buildDeliveryEnvelope(fallback, {
|
|
678
|
+
...options,
|
|
679
|
+
supplements: hits
|
|
680
|
+
.filter((hit) => !["constraints", "decisions", "bugs", "findings"].includes(hit.kind))
|
|
681
|
+
.map((hit, index) => ({
|
|
682
|
+
id: hit.ref,
|
|
683
|
+
kind: `search-${hit.kind}`,
|
|
684
|
+
text: `${hit.ref} — ${hit.title}: ${hit.snippet}`,
|
|
685
|
+
priority: 100 - index,
|
|
686
|
+
})),
|
|
687
|
+
});
|
|
688
|
+
return deliveredContext(root, target, envelope, extra.sessionId);
|
|
606
689
|
}
|
|
607
690
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
symbols: store.recs("symbols"),
|
|
611
|
-
components: store.recs("components"),
|
|
612
|
-
decisionCorpus: store.recs("decisions"),
|
|
613
|
-
historical: !!asOf,
|
|
614
|
-
}));
|
|
691
|
+
const envelope = buildDeliveryEnvelope(ctx, options);
|
|
692
|
+
return deliveredContext(root, as_of ? `${target} (as_of:${as_of})` : target, envelope, extra.sessionId);
|
|
615
693
|
});
|
|
616
694
|
// -- hunch_now (the hot view: recent activity + roadmap) --------------------
|
|
617
695
|
// PUBLIC store only, per dec_29eff08c69's jurisdiction rule: an assistant may
|
package/package.json
CHANGED
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.13.
|
|
10
|
+
"version": "1.13.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.13.
|
|
16
|
+
"version": "1.13.1",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|