@davesheffer/hunch 1.12.1 → 1.13.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/NOTICE +1 -1
- package/README.md +14 -16
- package/dist/cli/index.js +59 -37
- package/dist/core/delivery.js +322 -0
- package/dist/core/drift.js +11 -1
- package/dist/core/format.js +3 -50
- package/dist/core/glob.js +7 -0
- package/dist/core/publication.js +196 -0
- package/dist/core/served.js +42 -5
- package/dist/extractors/languages.js +51 -41
- package/dist/extractors/parse.js +43 -1
- package/dist/integrations/providers.js +31 -11
- package/dist/mcp/server.js +28 -7
- package/dist/store/hunchStore.js +7 -10
- package/dist/synthesis/synthesize.js +10 -2
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/store/hunchStore.js
CHANGED
|
@@ -19,7 +19,7 @@ import { RESET_SQL, embedHash } from "./schema.js";
|
|
|
19
19
|
import { selectEmbedder } from "./embedder.js";
|
|
20
20
|
import { JsonStore } from "./jsonStore.js";
|
|
21
21
|
import { gitCommonDir, gitWorktreeRoot, sameGitPublication } from "../extractors/git.js";
|
|
22
|
-
import { pathMatchesGlob } from "../core/glob.js";
|
|
22
|
+
import { pathMatchesGlob, pathsRelated } from "../core/glob.js";
|
|
23
23
|
import { currentForTopic, isInForce } from "../core/topics.js";
|
|
24
24
|
import { edgeId } from "../core/ids.js";
|
|
25
25
|
import { isStrictBlocker, isVetoBlocker } from "../core/strictgate.js";
|
|
@@ -819,14 +819,14 @@ export class HunchStore {
|
|
|
819
819
|
const symbols = this.recs("symbols");
|
|
820
820
|
const components = this.recs("components");
|
|
821
821
|
const asOf = opts.asOf;
|
|
822
|
-
//
|
|
822
|
+
// pathsRelated, not bare endsWith: "scenario.ts".endsWith("io.ts") is true,
|
|
823
823
|
// so an unanchored suffix pulled unrelated files' records into why()/the
|
|
824
824
|
// pre-edit grounding block (issue #32). Segment-anchored matching only.
|
|
825
|
-
const matchedSymbols = symbols.filter((s) => s.file === target || s.name === target || s.id === target ||
|
|
825
|
+
const matchedSymbols = symbols.filter((s) => s.file === target || s.name === target || s.id === target || pathsRelated(s.file, target));
|
|
826
826
|
const symIds = new Set(matchedSymbols.map((s) => s.id));
|
|
827
827
|
const fileSet = new Set(matchedSymbols.map((s) => s.file));
|
|
828
828
|
const isPath = target.includes("/") || target.includes(".");
|
|
829
|
-
const fileMatch = (files) => files.some((f) => f === target || (isPath &&
|
|
829
|
+
const fileMatch = (files) => files.some((f) => f === target || (isPath && pathsRelated(f, target)) || fileSet.has(f));
|
|
830
830
|
return {
|
|
831
831
|
target,
|
|
832
832
|
decisions: decisions.filter((d) => (fileMatch(d.related_files) || d.related_components.some((c) => components.find((x) => x.id === c && fileMatch(x.paths))))
|
|
@@ -1057,7 +1057,7 @@ export class HunchStore {
|
|
|
1057
1057
|
const live = (f) => f.triage === "open" || f.triage === "accepted-risk" || f.triage === "scheduled";
|
|
1058
1058
|
return this.recs("findings")
|
|
1059
1059
|
.filter(live)
|
|
1060
|
-
.filter((f) => f.affected_files.some((af) => pathMatchesGlob(t, af) || pathMatchesGlob(af, t) ||
|
|
1060
|
+
.filter((f) => f.affected_files.some((af) => pathMatchesGlob(t, af) || pathMatchesGlob(af, t) || pathsRelated(toPosixTarget(af), t))
|
|
1061
1061
|
|| f.affected_symbols.some((s) => s === scope))
|
|
1062
1062
|
.sort((a, b) => (SEV_FINDING[b.severity] ?? 0) - (SEV_FINDING[a.severity] ?? 0) || a.id.localeCompare(b.id));
|
|
1063
1063
|
}
|
|
@@ -1294,7 +1294,7 @@ export class HunchStore {
|
|
|
1294
1294
|
const addedDeps = new Set(added.deps);
|
|
1295
1295
|
if (!addedSyms.size && !addedDeps.size)
|
|
1296
1296
|
return [];
|
|
1297
|
-
const fileRelevant = (related) => related.some((f) => files.some((x) =>
|
|
1297
|
+
const fileRelevant = (related) => related.some((f) => files.some((x) => pathsRelated(x, f)));
|
|
1298
1298
|
const decisions = this.recs("decisions");
|
|
1299
1299
|
// decisions tied to an active blocking constraint via source_decision
|
|
1300
1300
|
const blockingDec = new Set(this.recs("constraints")
|
|
@@ -1416,7 +1416,7 @@ export class HunchStore {
|
|
|
1416
1416
|
continue;
|
|
1417
1417
|
if (!d.retired.symbols.length && !d.retired.deps.length)
|
|
1418
1418
|
continue;
|
|
1419
|
-
if (!d.related_files.some((f) =>
|
|
1419
|
+
if (!d.related_files.some((f) => pathsRelated(f, file)))
|
|
1420
1420
|
continue;
|
|
1421
1421
|
out.push({ decision: d.id, title: d.title, symbols: d.retired.symbols, deps: d.retired.deps });
|
|
1422
1422
|
}
|
|
@@ -1558,9 +1558,6 @@ const SEV_FINDING = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
|
1558
1558
|
/** Do two repo paths refer to the same file? Exact match, or one is a trailing
|
|
1559
1559
|
* path-SEGMENT suffix of the other (e.g. "x.ts" vs "src/x.ts") — anchored at a
|
|
1560
1560
|
* "/" boundary so "re.ts" never matches "store.ts" (the bare-endsWith hazard). */
|
|
1561
|
-
function pathRelated(a, b) {
|
|
1562
|
-
return a === b || a.endsWith("/" + b) || b.endsWith("/" + a);
|
|
1563
|
-
}
|
|
1564
1561
|
function inWindow(valid_from, valid_to, asOf) {
|
|
1565
1562
|
if (!asOf)
|
|
1566
1563
|
return true;
|
|
@@ -201,8 +201,16 @@ export async function syncCommit(store, root, sha, opts = {}) {
|
|
|
201
201
|
},
|
|
202
202
|
date: meta.date, // the commit date
|
|
203
203
|
};
|
|
204
|
-
// Route to the
|
|
205
|
-
// ("shared") mode; else the public store.
|
|
204
|
+
// Route to the resolved home: the overlay when asked (--private / opts.home) or in
|
|
205
|
+
// unified ("shared") mode; else the public store.
|
|
206
|
+
//
|
|
207
|
+
// DELIBERATELY home-scoped, NOT putCapture. Synthesis keeps the public and private
|
|
208
|
+
// spines separate: a public failure must never reach through and rewrite a same-id
|
|
209
|
+
// private record, which putCapture's cross-home collision guard would either throw on
|
|
210
|
+
// or (via putWhereItLives) silently redirect. test/private-capture.test.ts pins that
|
|
211
|
+
// behaviour — "public post-promotion rewrite cannot overwrite the private collision".
|
|
212
|
+
// An earlier comment here claimed the "same contract as every other capture path",
|
|
213
|
+
// which read as an accidental bypass and invited exactly that wrong fix.
|
|
206
214
|
if (home === "private")
|
|
207
215
|
store.putPrivate("decisions", decision);
|
|
208
216
|
else
|
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.
|
|
10
|
+
"version": "1.13.0",
|
|
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.13.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"packageArguments": [
|
|
19
19
|
{
|