@davesheffer/hunch 1.22.3 → 1.23.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 +7 -4
- package/contracts/change-proof/hunch.change-proof.v1.example.json +96 -0
- package/contracts/change-proof/hunch.change-proof.v1.schema.json +215 -0
- package/dist/changeProof.d.ts +1 -0
- package/dist/changeProof.js +2 -0
- package/dist/cli/index.js +37 -0
- package/dist/core/changeProof.js +512 -0
- package/dist/core/changeProofContract.d.ts +164 -0
- package/dist/core/changeProofContract.js +253 -0
- package/dist/core/projectDnaOutcomeExperience.d.ts +3 -3
- package/dist/mcp/server.js +28 -0
- package/dist/synthesis/provider.js +1 -1
- package/package.json +10 -2
- package/server.json +3 -3
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { compareCodeUnits } from "./canonicalOrder.js";
|
|
4
|
+
export const CHANGE_PROOF_SCHEMA_VERSION = "hunch.change-proof/1";
|
|
5
|
+
export const CHANGE_PROOF_ALGORITHM = "hunch-change-proof-sha256/1";
|
|
6
|
+
export const CHANGE_PROOF_VERDICTS = ["fail", "pass", "unknown"];
|
|
7
|
+
export const CHANGE_PROOF_RELEVANCE = ["blast_radius", "changed_path", "conformance", "guard"];
|
|
8
|
+
const GIT_OBJECT = /^[a-f0-9]{40,64}$/;
|
|
9
|
+
const SHA1 = /^sha1:[a-f0-9]{40}$/;
|
|
10
|
+
const SHA256 = /^sha256:[a-f0-9]{64}$/;
|
|
11
|
+
const CHANGE_ID = /^hchg_[a-f0-9]{24}$/;
|
|
12
|
+
const PROOF_ID = /^hproof_[a-f0-9]{24}$/;
|
|
13
|
+
const PROFILE_ID = /^pdna_[a-f0-9]{24}$/;
|
|
14
|
+
const REPOSITORY_ID = /^pdnar_[a-f0-9]{24}$/;
|
|
15
|
+
const RepoPathSchema = z.string().min(1).max(4_096).refine((path) => {
|
|
16
|
+
if (path.includes("\0") || path.includes("\\") || path.startsWith("/") || /^[A-Za-z]:/.test(path))
|
|
17
|
+
return false;
|
|
18
|
+
return !path.split("/").some((segment) => segment === "" || segment === "." || segment === "..");
|
|
19
|
+
}, "repository path must be canonical and relative");
|
|
20
|
+
const HashGapSchema = z.object({
|
|
21
|
+
code: z.string().regex(/^[a-z][a-z0-9_.-]{2,127}$/),
|
|
22
|
+
count: z.number().int().positive().max(1_000_000),
|
|
23
|
+
evidence_hash: z.string().regex(SHA256),
|
|
24
|
+
}).strict();
|
|
25
|
+
const ChangeIdentitySchema = z.object({
|
|
26
|
+
schema: z.literal("hunch.change-identity/1"),
|
|
27
|
+
algorithm: z.literal("git-raw-tree-delta-sha256/1"),
|
|
28
|
+
change_id: z.string().regex(CHANGE_ID),
|
|
29
|
+
base_revision: z.string().regex(GIT_OBJECT),
|
|
30
|
+
head_revision: z.string().regex(GIT_OBJECT),
|
|
31
|
+
base_tree: z.string().regex(GIT_OBJECT),
|
|
32
|
+
head_tree: z.string().regex(GIT_OBJECT),
|
|
33
|
+
delta_hash: z.string().regex(SHA256),
|
|
34
|
+
patch_id: z.string().regex(GIT_OBJECT).nullable(),
|
|
35
|
+
file_count: z.number().int().positive().max(16_384),
|
|
36
|
+
paths_hash: z.string().regex(SHA256),
|
|
37
|
+
content_hash: z.string().regex(SHA256),
|
|
38
|
+
}).strict();
|
|
39
|
+
const GraphSealSchema = z.object({
|
|
40
|
+
source: z.literal("commit"),
|
|
41
|
+
revision: z.string().regex(GIT_OBJECT),
|
|
42
|
+
source_hash: z.string().regex(SHA1),
|
|
43
|
+
topology_hash: z.string().regex(SHA256),
|
|
44
|
+
files: z.number().int().nonnegative().max(1_000_000),
|
|
45
|
+
symbols: z.number().int().nonnegative().max(10_000_000),
|
|
46
|
+
edges: z.number().int().nonnegative().max(50_000_000),
|
|
47
|
+
components: z.number().int().nonnegative().max(1_000_000),
|
|
48
|
+
issue_count: z.number().int().nonnegative().max(1_000_000),
|
|
49
|
+
}).strict();
|
|
50
|
+
const BlastEntrySchema = z.object({
|
|
51
|
+
source_path: RepoPathSchema,
|
|
52
|
+
dependent_path: RepoPathSchema,
|
|
53
|
+
depth: z.number().int().min(1).max(4),
|
|
54
|
+
graphs: z.array(z.enum(["base", "result"])).min(1).max(2),
|
|
55
|
+
}).strict();
|
|
56
|
+
const DecisionRefSchema = z.object({
|
|
57
|
+
id: z.string().regex(/^dec_[A-Za-z0-9_.-]{3,}$/),
|
|
58
|
+
record_hash: z.string().regex(SHA256),
|
|
59
|
+
relevance: z.array(z.enum(CHANGE_PROOF_RELEVANCE)).min(1).max(4),
|
|
60
|
+
paths: z.array(RepoPathSchema).max(128),
|
|
61
|
+
path_count: z.number().int().nonnegative().max(1_000_000),
|
|
62
|
+
paths_hash: z.string().regex(SHA256),
|
|
63
|
+
}).strict();
|
|
64
|
+
const ConstraintRefSchema = z.object({
|
|
65
|
+
id: z.string().regex(/^con_[A-Za-z0-9_.-]{3,}$/),
|
|
66
|
+
record_hash: z.string().regex(SHA256),
|
|
67
|
+
severity: z.enum(["advisory", "warning", "blocking"]),
|
|
68
|
+
relevance: z.array(z.enum(["blast_radius", "changed_path"])).min(1).max(2),
|
|
69
|
+
paths: z.array(RepoPathSchema).max(128),
|
|
70
|
+
path_count: z.number().int().nonnegative().max(1_000_000),
|
|
71
|
+
paths_hash: z.string().regex(SHA256),
|
|
72
|
+
}).strict();
|
|
73
|
+
const ConformanceReceiptSchema = z.object({
|
|
74
|
+
decision_id: z.string().regex(/^dec_[A-Za-z0-9_.-]{3,}$/),
|
|
75
|
+
predicate_index: z.number().int().nonnegative().max(1_000_000),
|
|
76
|
+
predicate_hash: z.string().regex(SHA256),
|
|
77
|
+
satisfied: z.boolean(),
|
|
78
|
+
detail_hash: z.string().regex(SHA256),
|
|
79
|
+
}).strict();
|
|
80
|
+
export const ChangeProofSchema = z.object({
|
|
81
|
+
schema: z.literal(CHANGE_PROOF_SCHEMA_VERSION),
|
|
82
|
+
algorithm: z.literal(CHANGE_PROOF_ALGORITHM),
|
|
83
|
+
proof_id: z.string().regex(PROOF_ID),
|
|
84
|
+
engine: z.object({
|
|
85
|
+
package: z.literal("@davesheffer/hunch"),
|
|
86
|
+
version: z.string().min(1).max(64),
|
|
87
|
+
}).strict(),
|
|
88
|
+
repository: z.object({
|
|
89
|
+
repository_id: z.string().regex(REPOSITORY_ID),
|
|
90
|
+
base_revision: z.string().regex(GIT_OBJECT),
|
|
91
|
+
result_revision: z.string().regex(GIT_OBJECT),
|
|
92
|
+
}).strict(),
|
|
93
|
+
change: ChangeIdentitySchema,
|
|
94
|
+
project_dna: z.object({
|
|
95
|
+
schema: z.literal("hunch.project-dna/1"),
|
|
96
|
+
profile_id: z.string().regex(PROFILE_ID),
|
|
97
|
+
repository_id: z.string().regex(REPOSITORY_ID),
|
|
98
|
+
repository_revision: z.string().regex(GIT_OBJECT),
|
|
99
|
+
content_hash: z.string().regex(SHA256),
|
|
100
|
+
trait_ids: z.array(z.string().regex(/^pdnat_[a-f0-9]{20}$/)).max(64),
|
|
101
|
+
}).strict(),
|
|
102
|
+
graph: z.object({
|
|
103
|
+
base: GraphSealSchema,
|
|
104
|
+
result: GraphSealSchema,
|
|
105
|
+
}).strict(),
|
|
106
|
+
changed_files: z.array(RepoPathSchema).max(2_048),
|
|
107
|
+
changed_file_count: z.number().int().positive().max(16_384),
|
|
108
|
+
blast_radius: z.array(BlastEntrySchema).max(4_096),
|
|
109
|
+
blast_radius_count: z.number().int().nonnegative().max(10_000_000),
|
|
110
|
+
decisions: z.array(DecisionRefSchema).max(1_024),
|
|
111
|
+
decision_count: z.number().int().nonnegative().max(1_000_000),
|
|
112
|
+
constraints: z.array(ConstraintRefSchema).max(1_024),
|
|
113
|
+
constraint_count: z.number().int().nonnegative().max(1_000_000),
|
|
114
|
+
conformance: z.array(ConformanceReceiptSchema).max(1_024),
|
|
115
|
+
conformance_count: z.number().int().nonnegative().max(1_000_000),
|
|
116
|
+
guard: z.object({
|
|
117
|
+
verdict: z.enum(["pass", "fail"]),
|
|
118
|
+
strict_blocker_ids: z.array(z.string().min(1).max(256)).max(2_048),
|
|
119
|
+
regression_decision_ids: z.array(z.string().regex(/^dec_[A-Za-z0-9_.-]{3,}$/)).max(1_024),
|
|
120
|
+
veto_decision_ids: z.array(z.string().regex(/^dec_[A-Za-z0-9_.-]{3,}$/)).max(1_024),
|
|
121
|
+
report_hash: z.string().regex(SHA256),
|
|
122
|
+
}).strict(),
|
|
123
|
+
memory: z.object({
|
|
124
|
+
scope: z.enum(["public", "union"]),
|
|
125
|
+
records_hash: z.string().regex(SHA256),
|
|
126
|
+
}).strict(),
|
|
127
|
+
omissions: z.array(HashGapSchema).max(64),
|
|
128
|
+
unknowns: z.array(HashGapSchema).max(64),
|
|
129
|
+
verdict: z.enum(CHANGE_PROOF_VERDICTS),
|
|
130
|
+
authority: z.object({
|
|
131
|
+
execution: z.literal(false),
|
|
132
|
+
ci: z.literal(false),
|
|
133
|
+
deployment: z.literal(false),
|
|
134
|
+
merge: z.literal(false),
|
|
135
|
+
ranking: z.literal(false),
|
|
136
|
+
promotion: z.literal(false),
|
|
137
|
+
policy: z.literal(false),
|
|
138
|
+
}).strict(),
|
|
139
|
+
content_hash: z.string().regex(SHA256),
|
|
140
|
+
}).strict();
|
|
141
|
+
export function canonicalChangeProofJson(value) {
|
|
142
|
+
if (Array.isArray(value))
|
|
143
|
+
return `[${value.map(canonicalChangeProofJson).join(",")}]`;
|
|
144
|
+
if (value && typeof value === "object") {
|
|
145
|
+
return `{${Object.entries(value)
|
|
146
|
+
.filter(([, child]) => child !== undefined)
|
|
147
|
+
.sort(([left], [right]) => compareCodeUnits(left, right))
|
|
148
|
+
.map(([key, child]) => `${JSON.stringify(key)}:${canonicalChangeProofJson(child)}`)
|
|
149
|
+
.join(",")}}`;
|
|
150
|
+
}
|
|
151
|
+
return JSON.stringify(value) ?? "null";
|
|
152
|
+
}
|
|
153
|
+
export function changeProofHash(value) {
|
|
154
|
+
return `sha256:${createHash("sha256").update(canonicalChangeProofJson(value)).digest("hex")}`;
|
|
155
|
+
}
|
|
156
|
+
function canonicalArray(values) {
|
|
157
|
+
const rendered = values.map(canonicalChangeProofJson);
|
|
158
|
+
return rendered.every((value, index) => index === 0 || compareCodeUnits(rendered[index - 1], value) < 0);
|
|
159
|
+
}
|
|
160
|
+
function expectedChangeId(change) {
|
|
161
|
+
return `hchg_${changeProofHash({ algorithm: change.algorithm, delta_hash: change.delta_hash })
|
|
162
|
+
.slice("sha256:".length, "sha256:".length + 24)}`;
|
|
163
|
+
}
|
|
164
|
+
export function sealChangeProof(unsigned) {
|
|
165
|
+
const proofId = `hproof_${changeProofHash(unsigned).slice("sha256:".length, "sha256:".length + 24)}`;
|
|
166
|
+
const sealed = { ...unsigned, proof_id: proofId };
|
|
167
|
+
const proof = { ...sealed, content_hash: changeProofHash(sealed) };
|
|
168
|
+
assertChangeProof(proof);
|
|
169
|
+
return proof;
|
|
170
|
+
}
|
|
171
|
+
export function assertChangeProof(value) {
|
|
172
|
+
const proof = ChangeProofSchema.parse(value);
|
|
173
|
+
const sortedArrays = [
|
|
174
|
+
proof.changed_files,
|
|
175
|
+
proof.blast_radius,
|
|
176
|
+
proof.decisions,
|
|
177
|
+
proof.constraints,
|
|
178
|
+
proof.conformance,
|
|
179
|
+
proof.guard.strict_blocker_ids,
|
|
180
|
+
proof.guard.regression_decision_ids,
|
|
181
|
+
proof.guard.veto_decision_ids,
|
|
182
|
+
proof.omissions,
|
|
183
|
+
proof.unknowns,
|
|
184
|
+
proof.project_dna.trait_ids,
|
|
185
|
+
];
|
|
186
|
+
if (sortedArrays.some((items) => !canonicalArray(items)))
|
|
187
|
+
throw new Error("change proof collections must be unique and canonically ordered");
|
|
188
|
+
for (const decision of proof.decisions) {
|
|
189
|
+
if (!canonicalArray(decision.relevance) || !canonicalArray(decision.paths)
|
|
190
|
+
|| decision.path_count < decision.paths.length
|
|
191
|
+
|| (decision.path_count === decision.paths.length && decision.paths_hash !== changeProofHash(decision.paths))) {
|
|
192
|
+
throw new Error("change proof decision reference is non-canonical");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
for (const constraint of proof.constraints) {
|
|
196
|
+
if (!canonicalArray(constraint.relevance) || !canonicalArray(constraint.paths)
|
|
197
|
+
|| constraint.path_count < constraint.paths.length
|
|
198
|
+
|| (constraint.path_count === constraint.paths.length && constraint.paths_hash !== changeProofHash(constraint.paths))) {
|
|
199
|
+
throw new Error("change proof constraint reference is non-canonical");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
for (const blast of proof.blast_radius) {
|
|
203
|
+
if (!canonicalArray(blast.graphs) || blast.source_path === blast.dependent_path) {
|
|
204
|
+
throw new Error("change proof blast radius is non-canonical");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const conformanceKeys = new Set();
|
|
208
|
+
for (const receipt of proof.conformance) {
|
|
209
|
+
const key = `${receipt.decision_id}\0${receipt.predicate_index}`;
|
|
210
|
+
if (conformanceKeys.has(key))
|
|
211
|
+
throw new Error("change proof conformance predicate identity is duplicated");
|
|
212
|
+
conformanceKeys.add(key);
|
|
213
|
+
}
|
|
214
|
+
const changeUnsigned = (({ content_hash: _contentHash, ...rest }) => rest)(proof.change);
|
|
215
|
+
if (proof.change.change_id !== expectedChangeId(proof.change)
|
|
216
|
+
|| proof.change.content_hash !== changeProofHash(changeUnsigned)) {
|
|
217
|
+
throw new Error("change proof change identity seal is invalid");
|
|
218
|
+
}
|
|
219
|
+
if (proof.repository.base_revision !== proof.change.base_revision
|
|
220
|
+
|| proof.repository.result_revision !== proof.change.head_revision
|
|
221
|
+
|| proof.project_dna.repository_id !== proof.repository.repository_id
|
|
222
|
+
|| proof.project_dna.repository_revision !== proof.repository.result_revision
|
|
223
|
+
|| proof.graph.base.revision !== proof.repository.base_revision
|
|
224
|
+
|| proof.graph.result.revision !== proof.repository.result_revision
|
|
225
|
+
|| proof.changed_file_count !== proof.change.file_count
|
|
226
|
+
|| proof.changed_file_count < proof.changed_files.length
|
|
227
|
+
|| proof.blast_radius_count < proof.blast_radius.length
|
|
228
|
+
|| proof.decision_count < proof.decisions.length
|
|
229
|
+
|| proof.constraint_count < proof.constraints.length
|
|
230
|
+
|| proof.conformance_count < proof.conformance.length) {
|
|
231
|
+
throw new Error("change proof exact-revision or count binding is invalid");
|
|
232
|
+
}
|
|
233
|
+
const recordsHash = changeProofHash({ decisions: proof.decisions, constraints: proof.constraints });
|
|
234
|
+
if (proof.memory.records_hash !== recordsHash)
|
|
235
|
+
throw new Error("change proof memory seal is invalid");
|
|
236
|
+
const guardFails = proof.guard.strict_blocker_ids.length > 0;
|
|
237
|
+
if (proof.guard.verdict !== (guardFails ? "fail" : "pass"))
|
|
238
|
+
throw new Error("change proof guard verdict is invalid");
|
|
239
|
+
const expectedVerdict = guardFails || proof.conformance.some((receipt) => !receipt.satisfied)
|
|
240
|
+
? "fail"
|
|
241
|
+
: proof.omissions.length || proof.unknowns.length
|
|
242
|
+
? "unknown"
|
|
243
|
+
: "pass";
|
|
244
|
+
if (proof.verdict !== expectedVerdict)
|
|
245
|
+
throw new Error("change proof verdict is invalid");
|
|
246
|
+
const { proof_id: _proofId, content_hash: _proofHash, ...unsigned } = proof;
|
|
247
|
+
const expectedProofId = `hproof_${changeProofHash(unsigned).slice("sha256:".length, "sha256:".length + 24)}`;
|
|
248
|
+
const sealed = { ...unsigned, proof_id: proof.proof_id };
|
|
249
|
+
if (proof.proof_id !== expectedProofId || proof.content_hash !== changeProofHash(sealed)) {
|
|
250
|
+
throw new Error("change proof seal is invalid");
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=changeProofContract.js.map
|
|
@@ -13,8 +13,8 @@ export declare const ProjectDnaUsefulnessObservationSchema: z.ZodObject<{
|
|
|
13
13
|
episodeHash: z.ZodString;
|
|
14
14
|
terminalAt: z.ZodString;
|
|
15
15
|
result: z.ZodEnum<{
|
|
16
|
-
pass: "pass";
|
|
17
16
|
fail: "fail";
|
|
17
|
+
pass: "pass";
|
|
18
18
|
uncertain: "uncertain";
|
|
19
19
|
abandoned: "abandoned";
|
|
20
20
|
rolled_back: "rolled_back";
|
|
@@ -44,10 +44,10 @@ export declare const ProjectDnaUsefulnessObservationSchema: z.ZodObject<{
|
|
|
44
44
|
}, z.core.$strict>;
|
|
45
45
|
artifact: z.ZodObject<{
|
|
46
46
|
kind: z.ZodEnum<{
|
|
47
|
-
|
|
47
|
+
message: "message";
|
|
48
48
|
commit: "commit";
|
|
49
|
+
pull_request: "pull_request";
|
|
49
50
|
issue: "issue";
|
|
50
|
-
message: "message";
|
|
51
51
|
}>;
|
|
52
52
|
ref: z.ZodString;
|
|
53
53
|
contentHash: z.ZodString;
|
package/dist/mcp/server.js
CHANGED
|
@@ -27,6 +27,8 @@ import { compileVerifiedEvidenceMap, EvidenceExecutionSchema, EvidenceInterventi
|
|
|
27
27
|
import { collectCorrectionStageSources } from "../extractors/correctionSources.js";
|
|
28
28
|
import { buildDeliveryEnvelope, DELIVERY_PROFILE_POLICY_VERSION, DELIVERY_PROFILES, } from "../core/delivery.js";
|
|
29
29
|
import { CHANGE_IDENTITY_ALGORITHM, CHANGE_IDENTITY_SCHEMA_VERSION, deriveChangeIdentity, } from "../core/changeIdentity.js";
|
|
30
|
+
import { deriveChangeProof } from "../core/changeProof.js";
|
|
31
|
+
import { ChangeProofSchema } from "../core/changeProofContract.js";
|
|
30
32
|
import { PROJECT_DNA_CATEGORIES, PROJECT_DNA_MATCH_SCHEMA_VERSION, PROJECT_DNA_SCHEMA_VERSION, discoverProjectDna, evaluateProjectDnaMatch, } from "../core/projectDna.js";
|
|
31
33
|
import { PROJECT_DNA_DELTA_SCHEMA_VERSION, diffProjectDna } from "../core/projectDnaDelta.js";
|
|
32
34
|
import { projectDnaDeliverySupplement } from "../core/projectDnaDelivery.js";
|
|
@@ -905,6 +907,32 @@ export function buildServerWithRootControl(initialRoot) {
|
|
|
905
907
|
return err(error.message);
|
|
906
908
|
}
|
|
907
909
|
});
|
|
910
|
+
// -- hunch_change_proof (exact-revision semantic evidence) ----------------
|
|
911
|
+
server.registerTool("hunch_change_proof", {
|
|
912
|
+
title: "Derive a sealed semantic proof for an exact change",
|
|
913
|
+
description: "Bind an exact committed Git transition to its change identity, Project DNA, base/result semantic graphs, current decisions and constraints, blast radius, conformance, guard verdict, and explicit gaps. Read-only and deterministic; grants no execution, CI, deployment, merge, ranking, promotion, or policy authority.",
|
|
914
|
+
inputSchema: {
|
|
915
|
+
base_ref: z.string().min(1).max(1_024).describe("Base commit or ref for the exact tree transition."),
|
|
916
|
+
result_ref: z.string().min(1).max(1_024).optional().describe("Result commit or ref (default HEAD)."),
|
|
917
|
+
public_only: z.boolean().optional().describe("Exclude the configured private-memory overlay. Required before publishing a proof."),
|
|
918
|
+
cwd: cwdHintField,
|
|
919
|
+
},
|
|
920
|
+
outputSchema: ChangeProofSchema,
|
|
921
|
+
}, async ({ base_ref, result_ref, public_only }) => {
|
|
922
|
+
try {
|
|
923
|
+
const proof = ChangeProofSchema.parse(deriveChangeProof(root, store, base_ref, result_ref ?? "HEAD", { publicOnly: public_only }));
|
|
924
|
+
return {
|
|
925
|
+
content: [{
|
|
926
|
+
type: "text",
|
|
927
|
+
text: `${proof.proof_id} — ${proof.verdict.toUpperCase()}; ${proof.changed_file_count} exact file delta(s), ${proof.blast_radius_count} dependent path(s), ${proof.omissions.length + proof.unknowns.length} explicit gap(s); sealed ${proof.content_hash}. Evidence only; no execution or merge authority.`,
|
|
928
|
+
}],
|
|
929
|
+
structuredContent: proof,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
catch (error) {
|
|
933
|
+
return err(error.message);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
908
936
|
// -- hunch_project_dna ----------------------------------------------------
|
|
909
937
|
server.registerTool("hunch_project_dna", {
|
|
910
938
|
title: "Inspect this repository's evidence-backed Project DNA",
|
|
@@ -596,7 +596,7 @@ export async function probeOllamaNumCtx(baseUrl, model) {
|
|
|
596
596
|
return null;
|
|
597
597
|
if (/^num_ctx\s+\d+/m.test(body.parameters))
|
|
598
598
|
return null; // already configured — nothing to warn about
|
|
599
|
-
return "⚠ This Ollama model does not pin num_ctx; its effective context depends on server/VRAM defaults. For stable large-diff synthesis, see https://
|
|
599
|
+
return "⚠ This Ollama model does not pin num_ctx; its effective context depends on server/VRAM defaults. For stable large-diff synthesis, see https://www.hunchmemory.com/cookbook and pin num_ctx via a custom Modelfile.";
|
|
600
600
|
}
|
|
601
601
|
catch {
|
|
602
602
|
return null; // not Ollama, unreachable, or an unexpected response shape — advisory only, never throw
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@davesheffer/hunch",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.2",
|
|
4
4
|
"mcpName": "io.github.davesheffer/hunch",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Dave Sheffer <dave.sheffer1@gmail.com>",
|
|
7
7
|
"description": "Engineering memory and a deterministic Change Gate for AI-assisted codebases: decisions, rejected approaches, constraints, and bug lineage become portable context and opt-in enforcement for every MCP assistant.",
|
|
8
|
-
"homepage": "https://
|
|
8
|
+
"homepage": "https://www.hunchmemory.com",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/davesheffer/hunch.git"
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"hunch": "dist/cli/index.js"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
21
|
+
"./change-proof": {
|
|
22
|
+
"types": "./dist/changeProof.d.ts",
|
|
23
|
+
"default": "./dist/changeProof.js"
|
|
24
|
+
},
|
|
21
25
|
"./project-dna": {
|
|
22
26
|
"types": "./dist/projectDna.d.ts",
|
|
23
27
|
"default": "./dist/projectDna.js"
|
|
@@ -27,8 +31,11 @@
|
|
|
27
31
|
},
|
|
28
32
|
"files": [
|
|
29
33
|
"dist/**/*.js",
|
|
34
|
+
"dist/changeProof.d.ts",
|
|
35
|
+
"dist/core/changeProofContract.d.ts",
|
|
30
36
|
"dist/projectDna.d.ts",
|
|
31
37
|
"dist/core/projectDna*.d.ts",
|
|
38
|
+
"contracts/change-proof/*.json",
|
|
32
39
|
"server.json",
|
|
33
40
|
"bench/constitution-exp03-v1.json",
|
|
34
41
|
"tooling/competitive-watch.mjs",
|
|
@@ -76,6 +83,7 @@
|
|
|
76
83
|
"rehearse:constitution": "npm run build && node tooling/constitution-clean-rehearsal.mjs",
|
|
77
84
|
"gate:release": "node tooling/release-gate.mjs",
|
|
78
85
|
"site:proof": "npm run build && node tooling/generate-public-proof.mjs",
|
|
86
|
+
"site:sitemap": "node tooling/generate-sitemap.mjs",
|
|
79
87
|
"bench:md1": "node tooling/md1-benchmark.mjs",
|
|
80
88
|
"research:competitors": "node tooling/competitive-watch.mjs",
|
|
81
89
|
"outreach": "node tooling/outreach-pipeline.mjs",
|
package/server.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
"url": "https://github.com/davesheffer/hunch",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"websiteUrl": "https://
|
|
10
|
-
"version": "1.
|
|
9
|
+
"websiteUrl": "https://www.hunchmemory.com",
|
|
10
|
+
"version": "1.23.2",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@davesheffer/hunch",
|
|
16
|
-
"version": "1.
|
|
16
|
+
"version": "1.23.2",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|