@nanonets/graft 0.7.1 → 0.8.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.
Files changed (101) hide show
  1. package/README.md +170 -70
  2. package/dist/ask/ask.d.ts +14 -0
  3. package/dist/ask/ask.d.ts.map +1 -1
  4. package/dist/ask/ask.js +24 -5
  5. package/dist/ask/ask.js.map +1 -1
  6. package/dist/claude/format.d.ts +1 -1
  7. package/dist/claude/format.d.ts.map +1 -1
  8. package/dist/claude/format.js +6 -3
  9. package/dist/claude/format.js.map +1 -1
  10. package/dist/claude/hooks.d.ts +15 -0
  11. package/dist/claude/hooks.d.ts.map +1 -1
  12. package/dist/claude/hooks.js +55 -4
  13. package/dist/claude/hooks.js.map +1 -1
  14. package/dist/claude/init.d.ts +17 -0
  15. package/dist/claude/init.d.ts.map +1 -1
  16. package/dist/claude/init.js +45 -19
  17. package/dist/claude/init.js.map +1 -1
  18. package/dist/claude/settings-merge.d.ts.map +1 -1
  19. package/dist/claude/settings-merge.js +9 -1
  20. package/dist/claude/settings-merge.js.map +1 -1
  21. package/dist/claude/skill-template.d.ts.map +1 -1
  22. package/dist/claude/skill-template.js +31 -8
  23. package/dist/claude/skill-template.js.map +1 -1
  24. package/dist/claude/state.d.ts +2 -20
  25. package/dist/claude/state.d.ts.map +1 -1
  26. package/dist/claude/state.js +9 -72
  27. package/dist/claude/state.js.map +1 -1
  28. package/dist/cli-picker.d.ts +60 -0
  29. package/dist/cli-picker.d.ts.map +1 -0
  30. package/dist/cli-picker.js +268 -0
  31. package/dist/cli-picker.js.map +1 -0
  32. package/dist/cli.js +104 -13
  33. package/dist/cli.js.map +1 -1
  34. package/dist/context/check.d.ts +21 -0
  35. package/dist/context/check.d.ts.map +1 -1
  36. package/dist/context/check.js +32 -1
  37. package/dist/context/check.js.map +1 -1
  38. package/dist/engine.d.ts +2 -0
  39. package/dist/engine.d.ts.map +1 -1
  40. package/dist/engine.js +1 -0
  41. package/dist/engine.js.map +1 -1
  42. package/dist/graph/build.d.ts +13 -2
  43. package/dist/graph/build.d.ts.map +1 -1
  44. package/dist/graph/build.js +93 -19
  45. package/dist/graph/build.js.map +1 -1
  46. package/dist/graph/extract-cache.d.ts +96 -0
  47. package/dist/graph/extract-cache.d.ts.map +1 -0
  48. package/dist/graph/extract-cache.js +199 -0
  49. package/dist/graph/extract-cache.js.map +1 -0
  50. package/dist/graph/fingerprint.d.ts +70 -0
  51. package/dist/graph/fingerprint.d.ts.map +1 -0
  52. package/dist/graph/fingerprint.js +147 -0
  53. package/dist/graph/fingerprint.js.map +1 -0
  54. package/dist/graph/load.d.ts +11 -0
  55. package/dist/graph/load.d.ts.map +1 -1
  56. package/dist/graph/load.js +14 -0
  57. package/dist/graph/load.js.map +1 -1
  58. package/dist/graph/refresh.d.ts +49 -0
  59. package/dist/graph/refresh.d.ts.map +1 -0
  60. package/dist/graph/refresh.js +202 -0
  61. package/dist/graph/refresh.js.map +1 -0
  62. package/dist/graph/source-files.d.ts +18 -0
  63. package/dist/graph/source-files.d.ts.map +1 -0
  64. package/dist/graph/source-files.js +36 -0
  65. package/dist/graph/source-files.js.map +1 -0
  66. package/dist/graph/traverse-cli.d.ts.map +1 -1
  67. package/dist/graph/traverse-cli.js +16 -6
  68. package/dist/graph/traverse-cli.js.map +1 -1
  69. package/dist/hosts/codex-hooks.d.ts +8 -0
  70. package/dist/hosts/codex-hooks.d.ts.map +1 -1
  71. package/dist/hosts/codex-hooks.js +27 -4
  72. package/dist/hosts/codex-hooks.js.map +1 -1
  73. package/dist/hosts/init.d.ts +2 -0
  74. package/dist/hosts/init.d.ts.map +1 -1
  75. package/dist/hosts/init.js +5 -2
  76. package/dist/hosts/init.js.map +1 -1
  77. package/dist/hosts/mcp-config.d.ts +18 -0
  78. package/dist/hosts/mcp-config.d.ts.map +1 -1
  79. package/dist/hosts/mcp-config.js +31 -7
  80. package/dist/hosts/mcp-config.js.map +1 -1
  81. package/dist/hosts/plan.d.ts +32 -0
  82. package/dist/hosts/plan.d.ts.map +1 -0
  83. package/dist/hosts/plan.js +67 -0
  84. package/dist/hosts/plan.js.map +1 -0
  85. package/dist/hosts/registry.d.ts.map +1 -1
  86. package/dist/hosts/registry.js +9 -0
  87. package/dist/hosts/registry.js.map +1 -1
  88. package/dist/mcp/server.d.ts.map +1 -1
  89. package/dist/mcp/server.js +5 -2
  90. package/dist/mcp/server.js.map +1 -1
  91. package/dist/mcp/tools.d.ts +2 -2
  92. package/dist/mcp/tools.d.ts.map +1 -1
  93. package/dist/mcp/tools.js +108 -88
  94. package/dist/mcp/tools.js.map +1 -1
  95. package/dist/search/grep-cli.js +1 -1
  96. package/dist/search/grep-cli.js.map +1 -1
  97. package/dist/util/state.d.ts +45 -0
  98. package/dist/util/state.d.ts.map +1 -0
  99. package/dist/util/state.js +121 -0
  100. package/dist/util/state.js.map +1 -0
  101. package/package.json +1 -1
@@ -6,18 +6,29 @@
6
6
  * 2. Parse each with tree-sitter and emit one NodeV1 per definition.
7
7
  * 3. Write a sorted graph.json.
8
8
  * Edges (M2) and LLM summary/crux (M3) layer onto this without changing it.
9
+ *
10
+ * Step 2 is memoized per file (`extract-cache.ts`): a file whose bytes haven't
11
+ * moved replays its last parse instead of re-running tree-sitter, so a rebuild
12
+ * costs ~the files that changed. Everything after extraction still runs over the
13
+ * whole node set, so an incremental build's output is byte-identical to a cold
14
+ * one — the invariant `test/graph-incremental.test.ts` pins down.
9
15
  */
10
16
  import { readFileSync } from "node:fs";
11
17
  import { basename, dirname, relative, resolve, sep } from "node:path";
12
18
  import { walkDir } from "../ingest/fs.js";
13
19
  import { contextDirFor, ensureGitignored } from "../context/node-file.js";
14
20
  import { extractFile, languageOf } from "./extract.js";
21
+ import { contentHash } from "../util/id.js";
22
+ import { emptyExtractCache, readExtractCache, writeExtractCache, } from "./extract-cache.js";
23
+ import { writeFingerprint } from "./fingerprint.js";
24
+ import { listSourceStats } from "./source-files.js";
15
25
  import { resolveEdges } from "./resolve.js";
16
26
  import { enrichGraph } from "./enrich.js";
17
27
  import { readGraph, writeGraph, wiringPath } from "./write.js";
18
28
  import { writeCards, writeIndex, writeCovers } from "./cards.js";
19
29
  import { writeAskIndex } from "../ask/index-file.js";
20
30
  import { discoverScopes, scopeOf } from "./scopes.js";
31
+ export { listSourceFiles } from "./source-files.js";
21
32
  /** Minimum non-file node count for a discovered sub-scope to stand on its own
22
33
  * (over-split guard 3). A scope with fewer nodes than this is folded into the
23
34
  * root scope — node counts aren't known until after the graph is assembled,
@@ -45,10 +56,6 @@ function applyMinSubstanceGuard(scopes, nodes) {
45
56
  }
46
57
  return kept;
47
58
  }
48
- /** The source files a graph build parses: supported languages, minus the output dir. */
49
- export function listSourceFiles(root, outDir) {
50
- return walkDir(root).filter((f) => !f.startsWith(outDir) && languageOf(f) !== null);
51
- }
52
59
  /** Every Go module in the repo: each `go.mod`'s declared `module` path and the repo
53
60
  * directory it lives in (posix, `.` for the root). Found anywhere in the tree, so a
54
61
  * monorepo whose module is in a subdir (e.g. `backend/go.mod`) resolves too. Lets edge
@@ -74,36 +81,85 @@ function readGoModules(root) {
74
81
  export async function buildGraph(dir, opts = {}) {
75
82
  const root = resolve(dir);
76
83
  const outDir = contextDirFor(root, opts.contextDir);
77
- const files = listSourceFiles(root, outDir);
84
+ const files = listSourceStats(root, outDir);
78
85
  const discoveredScopes = discoverScopes(root);
79
86
  const nodes = [];
80
87
  const rawEdges = [];
81
88
  const sources = new Map();
82
89
  const langs = new Set();
83
90
  const errors = [];
84
- files.forEach((file, i) => {
85
- const rel = relative(root, file);
91
+ // Tier-1 memo: unchanged files replay their last parse. `entries` is rebuilt
92
+ // from scratch each run and keyed only by files currently on disk, so deletions
93
+ // fall out of both the cache and the fingerprint with no separate pruning pass.
94
+ const priorExtract = opts.reuse === false ? emptyExtractCache() : readExtractCache(outDir);
95
+ const entries = {};
96
+ let parsed = 0;
97
+ let reused = 0;
98
+ files.forEach((f, i) => {
99
+ const rel = f.rel;
86
100
  opts.onProgress?.({ phase: "parse", index: i, total: files.length, file: rel });
87
- const lang = languageOf(file);
101
+ const lang = languageOf(f.abs);
102
+ const cached = priorExtract.files[rel];
103
+ // Every file is read and hashed, every build — only the *parse* is memoized.
104
+ // The tempting optimization is to trust the probe's `(size, mtimeMs)` and skip
105
+ // the read too, but then `graft build` inherits the probe's blind spot: on a
106
+ // filesystem with coarse mtime granularity, a same-length edit inside the same
107
+ // second is invisible, so `graft check` reports drift (it always re-hashes) and
108
+ // the `graft build` it tells you to run refuses to repair it — forever. A stat
109
+ // may decide whether a *query* bothers rebuilding; it may not decide what the
110
+ // rebuild itself looks at. Reading is ~0.05ms/file against the ~4.6ms parse
111
+ // this still skips.
88
112
  let source;
89
113
  try {
90
- source = readFileSync(file, "utf8");
114
+ source = readFileSync(f.abs, "utf8");
91
115
  }
92
116
  catch (err) {
93
- errors.push(`${rel}: ${err instanceof Error ? err.message : String(err)}`);
117
+ const message = `${rel}: ${err instanceof Error ? err.message : String(err)}`;
118
+ errors.push(message);
119
+ // Record it anyway (with the stat we do have) so the freshness probe's
120
+ // fast path doesn't report this file as new on every single query.
121
+ entries[rel] = { size: f.size, mtimeMs: f.mtimeMs, hash: "", nodes: [], rawEdges: [], error: message };
94
122
  return;
95
123
  }
124
+ const hash = contentHash(source);
125
+ if (cached && hash === cached.hash) {
126
+ entries[rel] = { ...cached, size: f.size, mtimeMs: f.mtimeMs };
127
+ sources.set(rel, source);
128
+ reused++;
129
+ if (cached.error) {
130
+ errors.push(cached.error); // this file failed to parse last time too
131
+ return;
132
+ }
133
+ nodes.push(...cached.nodes);
134
+ rawEdges.push(...cached.rawEdges);
135
+ langs.add(lang);
136
+ return;
137
+ }
138
+ parsed++;
96
139
  try {
97
140
  const { nodes: fileNodes, rawEdges: fileEdges } = extractFile(rel, source, lang);
98
141
  nodes.push(...fileNodes);
99
142
  rawEdges.push(...fileEdges);
100
143
  sources.set(rel, source);
101
144
  langs.add(lang);
145
+ entries[rel] = { size: f.size, mtimeMs: f.mtimeMs, hash, nodes: fileNodes, rawEdges: fileEdges };
102
146
  }
103
147
  catch (err) {
104
- errors.push(`${rel}: parse failed — ${err instanceof Error ? err.message : String(err)}`);
148
+ const message = `${rel}: parse failed — ${err instanceof Error ? err.message : String(err)}`;
149
+ errors.push(message);
150
+ entries[rel] = { size: f.size, mtimeMs: f.mtimeMs, hash, nodes: [], rawEdges: [], error: message };
105
151
  }
106
152
  });
153
+ // Persist the memo BEFORE enrichment, because `enrichGraph` mutates these very
154
+ // node objects (summary/crux/summary_state) and the cache must only ever hold
155
+ // pristine Tier-1 output — otherwise a replayed node would arrive pre-enriched
156
+ // and a cold build and an incremental build could disagree. The meaning layer
157
+ // has its own cache (wiring.json itself, keyed on body_hash); this one is
158
+ // strictly about not re-parsing.
159
+ writeExtractCache(outDir, {
160
+ ...emptyExtractCache(),
161
+ files: entries,
162
+ });
107
163
  const edges = resolveEdges(nodes, rawEdges, { goModules: readGoModules(root) });
108
164
  // graph.json is its own Tier-2 cache: fold in the prior meaning layer so an
109
165
  // unchanged body is never re-summarized (and a Tier-1-only run never wipes it).
@@ -130,9 +186,6 @@ export async function buildGraph(dir, opts = {}) {
130
186
  edges,
131
187
  };
132
188
  const graphPath = writeGraph(graph, outDir);
133
- // The graph is a local, regenerable cache — make sure git ignores it. Runs on
134
- // every build (cheap, idempotent), so a fresh clone's first build self-ignores.
135
- ensureGitignored(root, outDir);
136
189
  // `ask`'s token/IDF sidecar — moves per-query corpus tokenization to build
137
190
  // time (~45% of query time on a 32k-node graph, profiled). Lives in the
138
191
  // cache dir; `ask` falls back to live tokenization when it's absent/stale.
@@ -150,13 +203,32 @@ export async function buildGraph(dir, opts = {}) {
150
203
  catch (err) {
151
204
  errors.push(`ask-index: ${err instanceof Error ? err.message : String(err)}`);
152
205
  }
206
+ // The fingerprint claims exactly one thing: "the graph on disk was built from
207
+ // these source bytes." Nothing about the projections below — which is why it is
208
+ // safe to write here, and why `graphOnly` builds (the query path, which stops
209
+ // right after this line) are still recorded as fresh.
210
+ writeFingerprint(outDir, entries);
153
211
  // Tier-2 passive surface: project the nodes into per-file markdown cards, and
154
212
  // refresh the INDEX roster. Pure projection — no LLM, no network.
155
- const cardStats = writeCards(graph, outDir);
156
- writeIndex(outDir, cardStats.files);
157
- // Backfill concept nodes with their `covers:` symbol/file:line list (the
158
- // OKF↔Wiring link). No-op when there are no concept nodes (a $0 build).
159
- writeCovers(graph, outDir);
213
+ //
214
+ // Skipped entirely on the query path (`graphOnly`). Retrieval answers from
215
+ // wiring.json and the ask sidecar; the markdown surface is for humans and for
216
+ // the agent's own greps, and it is rebuilt by an explicit `graft build` — which
217
+ // is what the Claude Code `Stop` hook already runs at the end of a turn. Keeping
218
+ // it off the query path is what makes a refresh cheap, leaves the repo untouched
219
+ // (`ensureGitignored` writes `.gitignore`, which a read has no business doing),
220
+ // and means a failure here can never be triggered by a query.
221
+ let cardStats = { written: 0, pruned: 0, files: [] };
222
+ if (!opts.graphOnly) {
223
+ // The graph is a local, regenerable cache — make sure git ignores it. Cheap and
224
+ // idempotent, so a fresh clone's first build self-ignores.
225
+ ensureGitignored(root, outDir);
226
+ cardStats = writeCards(graph, outDir);
227
+ writeIndex(outDir, cardStats.files);
228
+ // Backfill concept nodes with their `covers:` symbol/file:line list (the
229
+ // OKF↔Wiring link). No-op when there are no concept nodes (a $0 build).
230
+ writeCovers(graph, outDir);
231
+ }
160
232
  const byKind = {};
161
233
  for (const n of nodes)
162
234
  byKind[n.kind] = (byKind[n.kind] ?? 0) + 1;
@@ -168,6 +240,8 @@ export async function buildGraph(dir, opts = {}) {
168
240
  graphPath,
169
241
  cards: cardStats.written,
170
242
  files: files.length,
243
+ parsed,
244
+ reused,
171
245
  nodes: nodes.length,
172
246
  edges: edges.length,
173
247
  byKind,
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/graph/build.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/graph/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import type { RawEdge } from "./extract.js";
2
+ import type { NodeV1 } from "./types.js";
3
+ export interface ExtractEntry {
4
+ size: number;
5
+ mtimeMs: number;
6
+ /** sha256 of the file's bytes — the same value as its `kind:"file"` node's `body_hash`. */
7
+ hash: string;
8
+ nodes: NodeV1[];
9
+ rawEdges: RawEdge[];
10
+ /** Set when this file couldn't be read or parsed. The entry exists anyway so the
11
+ * freshness probe doesn't flag the file as new on every query; replaying it
12
+ * re-reports the same error and contributes no nodes, exactly as a cold build
13
+ * would. */
14
+ error?: string;
15
+ }
16
+ export interface ExtractCache {
17
+ version: number;
18
+ /** Identity of the extractor that produced these entries. */
19
+ extractor: string;
20
+ /** repo-relative source path → its last parse. */
21
+ files: Record<string, ExtractEntry>;
22
+ }
23
+ /**
24
+ * Where this graft's memo lives: `<outDir>/.cache/extract.<stamp>.json`.
25
+ *
26
+ * The stamp is in the *filename*, not just inside the file, so two grafts working
27
+ * on one repo keep separate memos instead of evicting each other. That is the
28
+ * default install, not an exotic case: `graft init` wires the MCP server as
29
+ * `npx -y @nanonets/graft` (which resolves the latest published version) while the
30
+ * Claude Code hooks run the locally installed one. The moment those two versions
31
+ * differ, a single shared file means the prompt hook and every MCP retrieval take
32
+ * turns rejecting each other's entries and cold-re-parsing the whole repo — the memo
33
+ * would never help anyone.
34
+ *
35
+ * Null stamp → null path → no memo at all. See {@link extractorStamp}.
36
+ */
37
+ export declare function extractCachePath(outDir: string): string | null;
38
+ /** Keep `.cache/` from growing a file per version forever: after writing, drop all
39
+ * but the newest `keep` files sharing a prefix. Best-effort and never fatal — this
40
+ * is a cache directory, and a failure here costs disk, not correctness. */
41
+ export declare function pruneSidecars(cacheDir: string, prefix: string, keep?: number): void;
42
+ /**
43
+ * Identity of the code that produces the cached parses. Both sidecars key on it,
44
+ * so *anything* that can change extraction output has to change this string.
45
+ *
46
+ * Content-hashed over every sibling module in this directory, plus the package
47
+ * version. Two earlier instincts are deliberately rejected:
48
+ *
49
+ * - **Not a timestamp.** This was `mtime:size` of `extract.js` alone, which is
50
+ * wrong in both directions. Too loose: the parse of `db.count()` depends on
51
+ * `bindings.ts` deciding that `db` is a `UserRepo` (that answer is stored *in*
52
+ * the cached entry, as an edge's `recvType`), and fixing a binding bug leaves
53
+ * `extract.js`'s emitted bytes untouched — so any build that skips rewriting
54
+ * unchanged output keeps the old stamp and silently replays pre-fix edges. Too
55
+ * tight: a rebuild that rewrites identical content moves the mtime and throws
56
+ * away the whole memo for nothing.
57
+ * - **Not a hand-kept list of modules.** A list is correct exactly until someone
58
+ * moves parse logic into a module nobody added to it, and the failure is silent.
59
+ *
60
+ * Hashing the directory over-invalidates a little — editing any `graph/` module
61
+ * costs one cold rebuild — which is no more often than a version bump already
62
+ * costs, and always in the safe direction. The package version is folded in so a
63
+ * tree-sitter grammar upgrade (which changes parse output without changing any of
64
+ * graft's own files) invalidates too.
65
+ *
66
+ * Measured at ~0.5ms for 21 files / 556KB, paid once per process.
67
+ *
68
+ * **Null when no identity can be established at all**, and that is deliberately not
69
+ * a string. It used to return `"unknown"` on failure — but `"unknown"` was then
70
+ * *written into the sidecars as a real identity*, and every later run compared equal
71
+ * to it, so any environment where stamping fails (a `pkg`/`bun-compile` single-file
72
+ * build, an asar-style read, a directory that can't be listed) permanently lost the
73
+ * ability to notice an extractor change. A sentinel that doubles as a valid value
74
+ * silently disables the whole mechanism. Null forces callers to decide instead, and
75
+ * {@link extractCachePath} decides not to have a memo.
76
+ */
77
+ export declare function extractorStamp(): string | null;
78
+ /**
79
+ * Hash every `ext` file in `dir`, plus `version`. Null when the directory holds no
80
+ * such file. Separated from {@link extractorStamp} so a test can prove the property
81
+ * that matters: a change to *any* module in the directory moves the stamp, not just
82
+ * the one the old implementation happened to watch.
83
+ */
84
+ export declare function stampDir(dir: string, ext: string, version?: string): string | null;
85
+ export declare function emptyExtractCache(): ExtractCache;
86
+ /** The cache for `outDir`, or an empty one when it's absent, unparseable, written by
87
+ * a different cache version, or when this graft has no identity to key on (then
88
+ * every build is cold, which is slow but never wrong). The stamp is in the filename,
89
+ * so the `extractor` field is a second check rather than the only one. */
90
+ export declare function readExtractCache(outDir: string): ExtractCache;
91
+ /** Best-effort write — a full graph is already on disk by the time this runs, so an
92
+ * unwritable cache dir must never fail the build (it only costs the next build its
93
+ * reuse). Returns false when nothing was written, including the deliberate case of
94
+ * having no extractor identity: a parse we can't attribute must never be replayed. */
95
+ export declare function writeExtractCache(outDir: string, cache: ExtractCache): boolean;
96
+ //# sourceMappingURL=extract-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-cache.d.ts","sourceRoot":"","sources":["../../src/graph/extract-cache.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAMzC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB;;;gBAGY;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACrC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED;;2EAE2E;AAC3E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,IAAI,CAa9E;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAG9C;AAoBD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,SAAK,GAAG,MAAM,GAAG,IAAI,CAU9E;AAYD,wBAAgB,iBAAiB,IAAI,YAAY,CAEhD;AAED;;;0EAG0E;AAC1E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAS7D;AAED;;;sFAGsF;AACtF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAU9E"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Per-file memo for Tier-1 extraction — `<outDir>/.cache/extract.json`.
3
+ *
4
+ * `extractFile` is pure and file-local (`rel`, `source`, `lang` → `{nodes,
5
+ * rawEdges}`), which is the whole trick: an unchanged file's parse result can be
6
+ * replayed from disk instead of re-running tree-sitter. That turns
7
+ * {@link buildGraph} from "always re-parse the repo" (~4.6ms/file) into "re-parse
8
+ * only what moved", which is what makes a rebuild cheap enough to run *before*
9
+ * every query (see `graph/refresh.ts`).
10
+ *
11
+ * Everything downstream of extraction (edge resolution, enrichment, the graph
12
+ * write, the ask sidecar, cards) still runs over the whole merged node set, so no
13
+ * other invariant changes — an incremental build must produce a byte-identical
14
+ * `wiring.json` to a cold one.
15
+ *
16
+ * Crucially the cached nodes keep `body_text`: `writeGraph` strips it before
17
+ * serializing, so `wiring.json` can never be the reuse source — the ask sidecar
18
+ * needs a body for every node, including the ones we didn't re-parse.
19
+ *
20
+ * Lives under `.cache/` (gitignored, regenerate-anytime) next to `ask-index.json`
21
+ * and `summaries.json`. Two things invalidate the whole file: a bump of
22
+ * {@link CACHE_VERSION} for a change of on-disk shape, and a change to the
23
+ * extraction code itself, caught by content-hashing it — see
24
+ * {@link extractorStamp}. Neither asks anyone to remember to bump anything.
25
+ */
26
+ import { readFileSync, readdirSync, rmSync, statSync } from "node:fs";
27
+ import { createHash } from "node:crypto";
28
+ import { fileURLToPath } from "node:url";
29
+ import { dirname, extname, join } from "node:path";
30
+ import { CACHE_DIR } from "../context/node-file.js";
31
+ import { readJson, writeJsonAtomic } from "../util/state.js";
32
+ /** Bump when the on-disk shape below changes. */
33
+ const CACHE_VERSION = 1;
34
+ const EXTRACT_CACHE_PREFIX = "extract";
35
+ /**
36
+ * Where this graft's memo lives: `<outDir>/.cache/extract.<stamp>.json`.
37
+ *
38
+ * The stamp is in the *filename*, not just inside the file, so two grafts working
39
+ * on one repo keep separate memos instead of evicting each other. That is the
40
+ * default install, not an exotic case: `graft init` wires the MCP server as
41
+ * `npx -y @nanonets/graft` (which resolves the latest published version) while the
42
+ * Claude Code hooks run the locally installed one. The moment those two versions
43
+ * differ, a single shared file means the prompt hook and every MCP retrieval take
44
+ * turns rejecting each other's entries and cold-re-parsing the whole repo — the memo
45
+ * would never help anyone.
46
+ *
47
+ * Null stamp → null path → no memo at all. See {@link extractorStamp}.
48
+ */
49
+ export function extractCachePath(outDir) {
50
+ const stamp = extractorStamp();
51
+ return stamp === null ? null : join(outDir, CACHE_DIR, `${EXTRACT_CACHE_PREFIX}.${stamp}.json`);
52
+ }
53
+ /** Keep `.cache/` from growing a file per version forever: after writing, drop all
54
+ * but the newest `keep` files sharing a prefix. Best-effort and never fatal — this
55
+ * is a cache directory, and a failure here costs disk, not correctness. */
56
+ export function pruneSidecars(cacheDir, prefix, keep = 2) {
57
+ try {
58
+ const mine = readdirSync(cacheDir)
59
+ .filter((f) => f.startsWith(`${prefix}.`) && f.endsWith(".json"))
60
+ .map((f) => {
61
+ const full = join(cacheDir, f);
62
+ return { full, mtimeMs: statSync(full).mtimeMs };
63
+ })
64
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
65
+ for (const f of mine.slice(keep))
66
+ rmSync(f.full, { force: true });
67
+ }
68
+ catch {
69
+ /* nothing here is load-bearing */
70
+ }
71
+ }
72
+ /** Computed once per process — this can't change under a running process without
73
+ * the module graph itself being swapped, and a long-lived MCP server holding the
74
+ * identity of the code it actually loaded is the correct answer anyway.
75
+ * `undefined` = not computed yet; `null` = computed, and there is no identity. */
76
+ let memoizedStamp;
77
+ /**
78
+ * Identity of the code that produces the cached parses. Both sidecars key on it,
79
+ * so *anything* that can change extraction output has to change this string.
80
+ *
81
+ * Content-hashed over every sibling module in this directory, plus the package
82
+ * version. Two earlier instincts are deliberately rejected:
83
+ *
84
+ * - **Not a timestamp.** This was `mtime:size` of `extract.js` alone, which is
85
+ * wrong in both directions. Too loose: the parse of `db.count()` depends on
86
+ * `bindings.ts` deciding that `db` is a `UserRepo` (that answer is stored *in*
87
+ * the cached entry, as an edge's `recvType`), and fixing a binding bug leaves
88
+ * `extract.js`'s emitted bytes untouched — so any build that skips rewriting
89
+ * unchanged output keeps the old stamp and silently replays pre-fix edges. Too
90
+ * tight: a rebuild that rewrites identical content moves the mtime and throws
91
+ * away the whole memo for nothing.
92
+ * - **Not a hand-kept list of modules.** A list is correct exactly until someone
93
+ * moves parse logic into a module nobody added to it, and the failure is silent.
94
+ *
95
+ * Hashing the directory over-invalidates a little — editing any `graph/` module
96
+ * costs one cold rebuild — which is no more often than a version bump already
97
+ * costs, and always in the safe direction. The package version is folded in so a
98
+ * tree-sitter grammar upgrade (which changes parse output without changing any of
99
+ * graft's own files) invalidates too.
100
+ *
101
+ * Measured at ~0.5ms for 21 files / 556KB, paid once per process.
102
+ *
103
+ * **Null when no identity can be established at all**, and that is deliberately not
104
+ * a string. It used to return `"unknown"` on failure — but `"unknown"` was then
105
+ * *written into the sidecars as a real identity*, and every later run compared equal
106
+ * to it, so any environment where stamping fails (a `pkg`/`bun-compile` single-file
107
+ * build, an asar-style read, a directory that can't be listed) permanently lost the
108
+ * ability to notice an extractor change. A sentinel that doubles as a valid value
109
+ * silently disables the whole mechanism. Null forces callers to decide instead, and
110
+ * {@link extractCachePath} decides not to have a memo.
111
+ */
112
+ export function extractorStamp() {
113
+ if (memoizedStamp === undefined)
114
+ memoizedStamp = computeStamp();
115
+ return memoizedStamp;
116
+ }
117
+ function computeStamp() {
118
+ try {
119
+ const self = fileURLToPath(import.meta.url);
120
+ // `.js` when running from `dist/`, `.ts` under tsx — take the extension from
121
+ // our own filename rather than guessing which layout we're in.
122
+ const dir = dirname(self);
123
+ const hashed = stampDir(dir, extname(self), packageVersion(dir) ?? "");
124
+ if (hashed)
125
+ return hashed;
126
+ // Couldn't read the modules (bundled into one file, say). The version alone is
127
+ // a weaker identity — it can't see a local edit — but it still turns over on
128
+ // every upgrade, which is the case that ships broken parses to users.
129
+ const v = packageVersion(dir);
130
+ return v ? `v${v}` : null;
131
+ }
132
+ catch {
133
+ return null;
134
+ }
135
+ }
136
+ /**
137
+ * Hash every `ext` file in `dir`, plus `version`. Null when the directory holds no
138
+ * such file. Separated from {@link extractorStamp} so a test can prove the property
139
+ * that matters: a change to *any* module in the directory moves the stamp, not just
140
+ * the one the old implementation happened to watch.
141
+ */
142
+ export function stampDir(dir, ext, version = "") {
143
+ const files = readdirSync(dir).filter((f) => f.endsWith(ext)).sort();
144
+ if (!files.length)
145
+ return null;
146
+ const h = createHash("sha256");
147
+ h.update(version);
148
+ for (const f of files) {
149
+ h.update(f); // a rename is a change, even at identical content
150
+ h.update(readFileSync(join(dir, f)));
151
+ }
152
+ return h.digest("hex").slice(0, 16);
153
+ }
154
+ /** graft's own version, or null when it can't be read. */
155
+ function packageVersion(graphDir) {
156
+ try {
157
+ const pkg = JSON.parse(readFileSync(join(graphDir, "..", "..", "package.json"), "utf8"));
158
+ return pkg.version ?? null;
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ }
164
+ export function emptyExtractCache() {
165
+ return { version: CACHE_VERSION, extractor: extractorStamp() ?? "", files: {} };
166
+ }
167
+ /** The cache for `outDir`, or an empty one when it's absent, unparseable, written by
168
+ * a different cache version, or when this graft has no identity to key on (then
169
+ * every build is cold, which is slow but never wrong). The stamp is in the filename,
170
+ * so the `extractor` field is a second check rather than the only one. */
171
+ export function readExtractCache(outDir) {
172
+ const path = extractCachePath(outDir);
173
+ const stamp = extractorStamp();
174
+ if (path === null || stamp === null)
175
+ return emptyExtractCache();
176
+ const c = readJson(path);
177
+ if (!c || c.version !== CACHE_VERSION || c.extractor !== stamp || typeof c.files !== "object") {
178
+ return emptyExtractCache();
179
+ }
180
+ return { version: c.version, extractor: c.extractor, files: c.files ?? {} };
181
+ }
182
+ /** Best-effort write — a full graph is already on disk by the time this runs, so an
183
+ * unwritable cache dir must never fail the build (it only costs the next build its
184
+ * reuse). Returns false when nothing was written, including the deliberate case of
185
+ * having no extractor identity: a parse we can't attribute must never be replayed. */
186
+ export function writeExtractCache(outDir, cache) {
187
+ const path = extractCachePath(outDir);
188
+ if (path === null)
189
+ return false;
190
+ try {
191
+ writeJsonAtomic(path, cache, true);
192
+ pruneSidecars(join(outDir, CACHE_DIR), EXTRACT_CACHE_PREFIX);
193
+ return true;
194
+ }
195
+ catch {
196
+ return false;
197
+ }
198
+ }
199
+ //# sourceMappingURL=extract-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-cache.js","sourceRoot":"","sources":["../../src/graph/extract-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { type ExtractEntry } from "./extract-cache.js";
2
+ /** `[size, mtimeMs, hash]` — positional to keep the file small. */
3
+ type Print = [number, number, string];
4
+ export interface Fingerprint {
5
+ version: number;
6
+ /** The extractor that produced the graph these prints describe — the same stamp
7
+ * `extract.json` carries. Without it the two sidecars can disagree about whether
8
+ * the graph is current: an extractor change correctly drops every memo entry,
9
+ * yet the prints still match the tree byte-for-byte, so the probe would report
10
+ * clean and queries would keep answering from nodes the old extractor built. */
11
+ extractor: string;
12
+ files: Record<string, Print>;
13
+ }
14
+ /** What moved since the last build. Empty in all three arrays = nothing to do. */
15
+ export interface Drift {
16
+ /** Recorded files whose bytes differ now. */
17
+ changed: string[];
18
+ /** Source files with no record — new, or never indexed. */
19
+ added: string[];
20
+ /** Recorded files that are gone from disk. */
21
+ removed: string[];
22
+ }
23
+ /** `<outDir>/.cache/fingerprint.<stamp>.json` — keyed by extractor identity for the
24
+ * same reason the memo is (see {@link extractCachePath}): two grafts on one repo,
25
+ * typically an `npx` MCP server and a locally installed hook binary, must not keep
26
+ * invalidating each other's prints and forcing a cold rebuild on every call. */
27
+ export declare function fingerprintPath(outDir: string): string;
28
+ export declare function readFingerprint(outDir: string): Fingerprint | null;
29
+ /** Project the extraction cache's entries into the probe sidecar. Best-effort:
30
+ * the graph is already on disk when this runs, so a failed write costs the next
31
+ * probe its fast path and nothing more. */
32
+ export declare function writeFingerprint(outDir: string, entries: Record<string, ExtractEntry>): boolean;
33
+ /** `GRAFT_REFRESH=hash` — never trust a stat, confirm every file by its bytes. */
34
+ export declare function alwaysHash(): boolean;
35
+ /**
36
+ * May a recorded `(size, mtimeMs, hash)` be trusted for the file `f` as it is on
37
+ * disk now, without reading it?
38
+ *
39
+ * **The probe's rule only.** `buildGraph` deliberately does not use this: it reads
40
+ * and hashes every file, every time. A stat may decide whether a query bothers
41
+ * rebuilding; it may not decide what the rebuild itself looks at — otherwise
42
+ * `graft check` (which always re-hashes) can report drift that the `graft build` it
43
+ * recommends then refuses to repair. `GRAFT_REFRESH=hash` is the escape hatch for
44
+ * the probe's blind spot: a same-length edit inside one mtime tick.
45
+ *
46
+ * An empty `hash` means the last build never got the bytes — always re-read.
47
+ * A *parse* failure keeps its real hash, so it stays on the fast path: re-reading
48
+ * bytes that failed to parse yesterday just fails to parse again.
49
+ */
50
+ export declare function statUnchanged(rec: {
51
+ size: number;
52
+ mtimeMs: number;
53
+ hash: string;
54
+ }, f: {
55
+ size: number;
56
+ mtimeMs: number;
57
+ }): boolean;
58
+ export declare function isClean(d: Drift): boolean;
59
+ export declare function driftCount(d: Drift): number;
60
+ /**
61
+ * Diff the working tree against the last build's fingerprint. Returns null when
62
+ * there is no fingerprint to compare against (never built, or built by a version
63
+ * that didn't write one) — callers should treat that as "unknown", not "clean".
64
+ *
65
+ * `GRAFT_REFRESH=hash` skips the stat fast path and hashes every file, for the
66
+ * rare tooling that rewrites content while preserving size and mtime.
67
+ */
68
+ export declare function probeDrift(root: string, outDir: string): Drift | null;
69
+ export {};
70
+ //# sourceMappingURL=fingerprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/graph/fingerprint.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAiC,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AActF,mEAAmE;AACnE,KAAK,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEtC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;oFAIgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC9B;AAED,kFAAkF;AAClF,MAAM,WAAW,KAAK;IACpB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;gFAGgF;AAChF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAKlE;AAED;;2CAE2C;AAC3C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,OAAO,CAU/F;AAED,kFAAkF;AAClF,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACpD,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnC,OAAO,CAIT;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAEzC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAqCrE"}