@spikedpunch/mast 0.1.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.
Files changed (257) hide show
  1. package/LICENSE +21 -0
  2. package/MAST_SPEC.md +2926 -0
  3. package/README.md +842 -0
  4. package/assets/skill.md +45 -0
  5. package/dist/ast/extract.d.ts +65 -0
  6. package/dist/ast/extract.d.ts.map +1 -0
  7. package/dist/ast/extract.js +147 -0
  8. package/dist/ast/extract.js.map +1 -0
  9. package/dist/ast/extractor.d.ts +85 -0
  10. package/dist/ast/extractor.d.ts.map +1 -0
  11. package/dist/ast/extractor.js +2 -0
  12. package/dist/ast/extractor.js.map +1 -0
  13. package/dist/ast/extractors/markdown.d.ts +40 -0
  14. package/dist/ast/extractors/markdown.d.ts.map +1 -0
  15. package/dist/ast/extractors/markdown.js +175 -0
  16. package/dist/ast/extractors/markdown.js.map +1 -0
  17. package/dist/ast/extractors/typescript.d.ts +179 -0
  18. package/dist/ast/extractors/typescript.d.ts.map +1 -0
  19. package/dist/ast/extractors/typescript.js +1439 -0
  20. package/dist/ast/extractors/typescript.js.map +1 -0
  21. package/dist/ast/parser.d.ts +5 -0
  22. package/dist/ast/parser.d.ts.map +1 -0
  23. package/dist/ast/parser.js +21 -0
  24. package/dist/ast/parser.js.map +1 -0
  25. package/dist/ast/types.d.ts +586 -0
  26. package/dist/ast/types.d.ts.map +1 -0
  27. package/dist/ast/types.js +3 -0
  28. package/dist/ast/types.js.map +1 -0
  29. package/dist/cli/docs-cmd.d.ts +22 -0
  30. package/dist/cli/docs-cmd.d.ts.map +1 -0
  31. package/dist/cli/docs-cmd.js +100 -0
  32. package/dist/cli/docs-cmd.js.map +1 -0
  33. package/dist/cli/index-cmd.d.ts +72 -0
  34. package/dist/cli/index-cmd.d.ts.map +1 -0
  35. package/dist/cli/index-cmd.js +263 -0
  36. package/dist/cli/index-cmd.js.map +1 -0
  37. package/dist/cli/index.d.ts +3 -0
  38. package/dist/cli/index.d.ts.map +1 -0
  39. package/dist/cli/index.js +4 -0
  40. package/dist/cli/index.js.map +1 -0
  41. package/dist/cli/init.d.ts +14 -0
  42. package/dist/cli/init.d.ts.map +1 -0
  43. package/dist/cli/init.js +64 -0
  44. package/dist/cli/init.js.map +1 -0
  45. package/dist/cli/install-hooks.d.ts +3 -0
  46. package/dist/cli/install-hooks.d.ts.map +1 -0
  47. package/dist/cli/install-hooks.js +34 -0
  48. package/dist/cli/install-hooks.js.map +1 -0
  49. package/dist/cli/metrics-cmd.d.ts +3 -0
  50. package/dist/cli/metrics-cmd.d.ts.map +1 -0
  51. package/dist/cli/metrics-cmd.js +164 -0
  52. package/dist/cli/metrics-cmd.js.map +1 -0
  53. package/dist/cli/program.d.ts +14 -0
  54. package/dist/cli/program.d.ts.map +1 -0
  55. package/dist/cli/program.js +44 -0
  56. package/dist/cli/program.js.map +1 -0
  57. package/dist/cli/query.d.ts +46 -0
  58. package/dist/cli/query.d.ts.map +1 -0
  59. package/dist/cli/query.js +153 -0
  60. package/dist/cli/query.js.map +1 -0
  61. package/dist/cli/search-cmd.d.ts +33 -0
  62. package/dist/cli/search-cmd.d.ts.map +1 -0
  63. package/dist/cli/search-cmd.js +114 -0
  64. package/dist/cli/search-cmd.js.map +1 -0
  65. package/dist/cli/serve.d.ts +3 -0
  66. package/dist/cli/serve.d.ts.map +1 -0
  67. package/dist/cli/serve.js +19 -0
  68. package/dist/cli/serve.js.map +1 -0
  69. package/dist/cli/skill-install.d.ts +39 -0
  70. package/dist/cli/skill-install.d.ts.map +1 -0
  71. package/dist/cli/skill-install.js +57 -0
  72. package/dist/cli/skill-install.js.map +1 -0
  73. package/dist/cli/status.d.ts +35 -0
  74. package/dist/cli/status.d.ts.map +1 -0
  75. package/dist/cli/status.js +117 -0
  76. package/dist/cli/status.js.map +1 -0
  77. package/dist/cli/upgrade-cmd.d.ts +48 -0
  78. package/dist/cli/upgrade-cmd.d.ts.map +1 -0
  79. package/dist/cli/upgrade-cmd.js +129 -0
  80. package/dist/cli/upgrade-cmd.js.map +1 -0
  81. package/dist/cli/version.d.ts +3 -0
  82. package/dist/cli/version.d.ts.map +1 -0
  83. package/dist/cli/version.js +19 -0
  84. package/dist/cli/version.js.map +1 -0
  85. package/dist/env.d.ts +17 -0
  86. package/dist/env.d.ts.map +1 -0
  87. package/dist/env.js +17 -0
  88. package/dist/env.js.map +1 -0
  89. package/dist/graph/checker-resolver.d.ts +156 -0
  90. package/dist/graph/checker-resolver.d.ts.map +1 -0
  91. package/dist/graph/checker-resolver.js +458 -0
  92. package/dist/graph/checker-resolver.js.map +1 -0
  93. package/dist/graph/db.d.ts +318 -0
  94. package/dist/graph/db.d.ts.map +1 -0
  95. package/dist/graph/db.js +266 -0
  96. package/dist/graph/db.js.map +1 -0
  97. package/dist/graph/index.d.ts +4 -0
  98. package/dist/graph/index.d.ts.map +1 -0
  99. package/dist/graph/index.js +5 -0
  100. package/dist/graph/index.js.map +1 -0
  101. package/dist/graph/local-type-env.d.ts +45 -0
  102. package/dist/graph/local-type-env.d.ts.map +1 -0
  103. package/dist/graph/local-type-env.js +59 -0
  104. package/dist/graph/local-type-env.js.map +1 -0
  105. package/dist/graph/path-range.d.ts +40 -0
  106. package/dist/graph/path-range.d.ts.map +1 -0
  107. package/dist/graph/path-range.js +42 -0
  108. package/dist/graph/path-range.js.map +1 -0
  109. package/dist/graph/populate.d.ts +263 -0
  110. package/dist/graph/populate.d.ts.map +1 -0
  111. package/dist/graph/populate.js +984 -0
  112. package/dist/graph/populate.js.map +1 -0
  113. package/dist/graph/queries.d.ts +114 -0
  114. package/dist/graph/queries.d.ts.map +1 -0
  115. package/dist/graph/queries.js +415 -0
  116. package/dist/graph/queries.js.map +1 -0
  117. package/dist/graph/sqliteBatch.d.ts +48 -0
  118. package/dist/graph/sqliteBatch.d.ts.map +1 -0
  119. package/dist/graph/sqliteBatch.js +93 -0
  120. package/dist/graph/sqliteBatch.js.map +1 -0
  121. package/dist/indexer/freshness.d.ts +48 -0
  122. package/dist/indexer/freshness.d.ts.map +1 -0
  123. package/dist/indexer/freshness.js +72 -0
  124. package/dist/indexer/freshness.js.map +1 -0
  125. package/dist/indexer/import-resolver.d.ts +50 -0
  126. package/dist/indexer/import-resolver.d.ts.map +1 -0
  127. package/dist/indexer/import-resolver.js +318 -0
  128. package/dist/indexer/import-resolver.js.map +1 -0
  129. package/dist/indexer/index.d.ts +249 -0
  130. package/dist/indexer/index.d.ts.map +1 -0
  131. package/dist/indexer/index.js +560 -0
  132. package/dist/indexer/index.js.map +1 -0
  133. package/dist/indexer/walker.d.ts +60 -0
  134. package/dist/indexer/walker.d.ts.map +1 -0
  135. package/dist/indexer/walker.js +128 -0
  136. package/dist/indexer/walker.js.map +1 -0
  137. package/dist/indexer/watcher.d.ts +80 -0
  138. package/dist/indexer/watcher.d.ts.map +1 -0
  139. package/dist/indexer/watcher.js +151 -0
  140. package/dist/indexer/watcher.js.map +1 -0
  141. package/dist/mcp/context.d.ts +23 -0
  142. package/dist/mcp/context.d.ts.map +1 -0
  143. package/dist/mcp/context.js +2 -0
  144. package/dist/mcp/context.js.map +1 -0
  145. package/dist/mcp/register-tools.d.ts +21 -0
  146. package/dist/mcp/register-tools.d.ts.map +1 -0
  147. package/dist/mcp/register-tools.js +42 -0
  148. package/dist/mcp/register-tools.js.map +1 -0
  149. package/dist/mcp/server.d.ts +58 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/mcp/server.js +177 -0
  152. package/dist/mcp/server.js.map +1 -0
  153. package/dist/mcp/staleness.d.ts +72 -0
  154. package/dist/mcp/staleness.d.ts.map +1 -0
  155. package/dist/mcp/staleness.js +213 -0
  156. package/dist/mcp/staleness.js.map +1 -0
  157. package/dist/mcp/startup.d.ts +40 -0
  158. package/dist/mcp/startup.d.ts.map +1 -0
  159. package/dist/mcp/startup.js +114 -0
  160. package/dist/mcp/startup.js.map +1 -0
  161. package/dist/mcp/tools/_helpers.d.ts +26 -0
  162. package/dist/mcp/tools/_helpers.d.ts.map +1 -0
  163. package/dist/mcp/tools/_helpers.js +54 -0
  164. package/dist/mcp/tools/_helpers.js.map +1 -0
  165. package/dist/mcp/tools/callers.d.ts +4 -0
  166. package/dist/mcp/tools/callers.d.ts.map +1 -0
  167. package/dist/mcp/tools/callers.js +112 -0
  168. package/dist/mcp/tools/callers.js.map +1 -0
  169. package/dist/mcp/tools/dependencies.d.ts +4 -0
  170. package/dist/mcp/tools/dependencies.d.ts.map +1 -0
  171. package/dist/mcp/tools/dependencies.js +36 -0
  172. package/dist/mcp/tools/dependencies.js.map +1 -0
  173. package/dist/mcp/tools/efficiency.d.ts +4 -0
  174. package/dist/mcp/tools/efficiency.d.ts.map +1 -0
  175. package/dist/mcp/tools/efficiency.js +39 -0
  176. package/dist/mcp/tools/efficiency.js.map +1 -0
  177. package/dist/mcp/tools/exports.d.ts +4 -0
  178. package/dist/mcp/tools/exports.d.ts.map +1 -0
  179. package/dist/mcp/tools/exports.js +56 -0
  180. package/dist/mcp/tools/exports.js.map +1 -0
  181. package/dist/mcp/tools/implementors.d.ts +4 -0
  182. package/dist/mcp/tools/implementors.d.ts.map +1 -0
  183. package/dist/mcp/tools/implementors.js +47 -0
  184. package/dist/mcp/tools/implementors.js.map +1 -0
  185. package/dist/mcp/tools/project-skeleton.d.ts +4 -0
  186. package/dist/mcp/tools/project-skeleton.d.ts.map +1 -0
  187. package/dist/mcp/tools/project-skeleton.js +61 -0
  188. package/dist/mcp/tools/project-skeleton.js.map +1 -0
  189. package/dist/mcp/tools/reindex.d.ts +14 -0
  190. package/dist/mcp/tools/reindex.d.ts.map +1 -0
  191. package/dist/mcp/tools/reindex.js +32 -0
  192. package/dist/mcp/tools/reindex.js.map +1 -0
  193. package/dist/mcp/tools/rename-impact.d.ts +4 -0
  194. package/dist/mcp/tools/rename-impact.d.ts.map +1 -0
  195. package/dist/mcp/tools/rename-impact.js +124 -0
  196. package/dist/mcp/tools/rename-impact.js.map +1 -0
  197. package/dist/mcp/tools/search.d.ts +4 -0
  198. package/dist/mcp/tools/search.d.ts.map +1 -0
  199. package/dist/mcp/tools/search.js +69 -0
  200. package/dist/mcp/tools/search.js.map +1 -0
  201. package/dist/mcp/tools/signature.d.ts +4 -0
  202. package/dist/mcp/tools/signature.d.ts.map +1 -0
  203. package/dist/mcp/tools/signature.js +127 -0
  204. package/dist/mcp/tools/signature.js.map +1 -0
  205. package/dist/mcp/tools/status.d.ts +4 -0
  206. package/dist/mcp/tools/status.d.ts.map +1 -0
  207. package/dist/mcp/tools/status.js +30 -0
  208. package/dist/mcp/tools/status.js.map +1 -0
  209. package/dist/search/declex.d.ts +109 -0
  210. package/dist/search/declex.d.ts.map +1 -0
  211. package/dist/search/declex.js +242 -0
  212. package/dist/search/declex.js.map +1 -0
  213. package/dist/search/fts.d.ts +77 -0
  214. package/dist/search/fts.d.ts.map +1 -0
  215. package/dist/search/fts.js +223 -0
  216. package/dist/search/fts.js.map +1 -0
  217. package/dist/search/fused.d.ts +110 -0
  218. package/dist/search/fused.d.ts.map +1 -0
  219. package/dist/search/fused.js +323 -0
  220. package/dist/search/fused.js.map +1 -0
  221. package/dist/search/potential-matches.d.ts +88 -0
  222. package/dist/search/potential-matches.d.ts.map +1 -0
  223. package/dist/search/potential-matches.js +83 -0
  224. package/dist/search/potential-matches.js.map +1 -0
  225. package/dist/search/scope.d.ts +66 -0
  226. package/dist/search/scope.d.ts.map +1 -0
  227. package/dist/search/scope.js +71 -0
  228. package/dist/search/scope.js.map +1 -0
  229. package/dist/store/config.d.ts +57 -0
  230. package/dist/store/config.d.ts.map +1 -0
  231. package/dist/store/config.js +208 -0
  232. package/dist/store/config.js.map +1 -0
  233. package/dist/store/lock.d.ts +101 -0
  234. package/dist/store/lock.d.ts.map +1 -0
  235. package/dist/store/lock.js +182 -0
  236. package/dist/store/lock.js.map +1 -0
  237. package/dist/store/lockMetrics.d.ts +52 -0
  238. package/dist/store/lockMetrics.d.ts.map +1 -0
  239. package/dist/store/lockMetrics.js +29 -0
  240. package/dist/store/lockMetrics.js.map +1 -0
  241. package/dist/store/sqliteChunkStore.d.ts +44 -0
  242. package/dist/store/sqliteChunkStore.d.ts.map +1 -0
  243. package/dist/store/sqliteChunkStore.js +135 -0
  244. package/dist/store/sqliteChunkStore.js.map +1 -0
  245. package/dist/telemetry/lockMetricsSummary.d.ts +42 -0
  246. package/dist/telemetry/lockMetricsSummary.d.ts.map +1 -0
  247. package/dist/telemetry/lockMetricsSummary.js +139 -0
  248. package/dist/telemetry/lockMetricsSummary.js.map +1 -0
  249. package/dist/telemetry/metrics.d.ts +140 -0
  250. package/dist/telemetry/metrics.d.ts.map +1 -0
  251. package/dist/telemetry/metrics.js +312 -0
  252. package/dist/telemetry/metrics.js.map +1 -0
  253. package/dist/telemetry/tokenizer.d.ts +125 -0
  254. package/dist/telemetry/tokenizer.d.ts.map +1 -0
  255. package/dist/telemetry/tokenizer.js +174 -0
  256. package/dist/telemetry/tokenizer.js.map +1 -0
  257. package/package.json +81 -0
@@ -0,0 +1,560 @@
1
+ import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { sql } from 'kysely';
4
+ import { CURRENT_SCHEMA_VERSION } from '../store/config.js';
5
+ import { initLockMarkers, withLock } from '../store/lock.js';
6
+ import { SqliteChunkStore } from '../store/sqliteChunkStore.js';
7
+ import { openDatabase, readPragmaValue } from '../graph/db.js';
8
+ import { populateFile, insertEdges, insertReExportFiles, removeDeletedFiles } from '../graph/populate.js';
9
+ import { extractFile } from '../ast/extract.js';
10
+ import { walkProject, buildManifest, diffManifest } from './walker.js';
11
+ import { getImportResolver } from './import-resolver.js';
12
+ /**
13
+ * Fresh best-effort mtime read (seconds), used wherever a value written to
14
+ * `files.mtime` or `file_manifest.json` must reflect disk state as observed
15
+ * at READ time rather than at walk time (F1/F12 — see the WHY-comments in
16
+ * {@link runIndex} for why the two can diverge once `structure.lock` no
17
+ * longer spans the whole run). Two call sites, two different reasons: the
18
+ * parse loop calls this IMMEDIATELY BEFORE `doExtract` so the stamp can never
19
+ * be newer than the content it accompanies (F12 invariant 1); the final
20
+ * manifest phase calls this with no accompanying content read at all, purely
21
+ * for `file_manifest.json` bookkeeping (see invariant 1's note on why that
22
+ * one doesn't need the same fix). Falls back to `fallbackSeconds` if the file
23
+ * has vanished since it was walked — mirrors `walkProject`'s own
24
+ * best-effort disappearance handling (walker.ts:63-65).
25
+ */
26
+ function statMtimeSecondsOrFallback(absPath, fallbackSeconds) {
27
+ try {
28
+ return statSync(absPath).mtimeMs / 1_000;
29
+ }
30
+ catch {
31
+ return fallbackSeconds;
32
+ }
33
+ }
34
+ /**
35
+ * Walk the project, parse changed files into chunks, populate the graph
36
+ * database and FTS indexes.
37
+ *
38
+ * F1 (GITNEXUS_COMPARISON.md §13.2, MAST_SPEC.md:822-826): `structure.lock`
39
+ * is acquired and released separately for each write phase — deleted-file
40
+ * cleanup, each `LANCE_BATCH`-sized write batch, each edge-insertion batch,
41
+ * and the final manifest/index.json write — instead of once for the whole
42
+ * run. The read-only walk and the CPU-bound parse phase never hold the lock
43
+ * at all. Before this change the lock was held for the run's full duration
44
+ * (measured max: 280,782 ms on a 1,335-file corpus — eval/baseline-locks.json),
45
+ * which starved the JIT re-parse path (mcp/staleness.ts:56, 3 retries x
46
+ * 100 ms) for minutes at a time — directly contradicting MAST_SPEC.md:822-826
47
+ * ("lock holding is per-file-parse (10-50ms), not per-tool-call").
48
+ *
49
+ * Narrowing the lock's scope means a JIT re-parse can now interleave BETWEEN
50
+ * phases of an in-flight run. Two invariants keep that safe:
51
+ *
52
+ * 1. **[F12, `GITNEXUS_COMPARISON.md` Stage 1]** The row's stamped mtime must
53
+ * never be NEWER than the content it is stamped against — i.e.
54
+ * `stamp <= mtime-at-content-read`. This is an ORDERING property, not a
55
+ * mutual-exclusion one: no lock can enforce it, because a file's editor
56
+ * never takes `structure.lock`. An earlier version of this invariant
57
+ * re-stat'd each file AFTER `doExtract` (inside the locked write phase) to
58
+ * stop `files.mtime` *regressing* below `entry.mtime` — but that opened
59
+ * the real hazard: an edit landing in the (unlocked, seconds-wide under
60
+ * §15.2's FTS growth) parse-to-write window would be stamped with a mtime
61
+ * NEWER than the content that was actually parsed, permanently disarming
62
+ * staleness detection for that file (`diskMtime <= storedMtime` reads
63
+ * "fresh" forever — the exact P0 class this stage exists to eliminate).
64
+ * The fix stats each file IMMEDIATELY BEFORE `doExtract`, in the parse
65
+ * loop below (unlocked, same as `mcp/staleness.ts`'s JIT path, which has
66
+ * always done this correctly), and carries that pre-parse stamp through to
67
+ * the write untouched — no re-stat at write time. A post-stat edit then
68
+ * leaves stamp < content, so staleness re-fires on the next check and the
69
+ * system self-heals, the same property the JIT path already had.
70
+ * 2. **Monotonic write-guard, `graph/populate.ts`'s `populateFile`**: even
71
+ * with (1), two writers (e.g. this run's batch write and a concurrent JIT
72
+ * refresh of the same file) can still race to write the SAME row with
73
+ * DIFFERENT stamps, and `structure.lock` only serializes the two writes —
74
+ * it does not guarantee the one with the fresher stamp wins. `populateFile`
75
+ * refuses to replace a row whose stored mtime already exceeds the incoming
76
+ * stamp, so whichever write carries the NEWER stamp wins regardless of
77
+ * which one reaches the lock first — fixing the reindex-vs-JIT race
78
+ * *better* than the lock alone does (the lock only serializes; the guard
79
+ * orders).
80
+ *
81
+ * Known limitation (not solved here): mtime-granularity blindness. An edit
82
+ * landing in the SAME mtime tick as the stamp compares equal, not greater,
83
+ * and reads as fresh (the git racy-lstat problem). Near-moot on APFS's
84
+ * nanosecond-resolution mtimes; real on coarse-mtime volumes (e.g. some
85
+ * shared/network filesystems), which is exactly the container-deployment
86
+ * case MAST_SPEC.md §7.6 targets. Solving it properly means content hashing,
87
+ * not mtime comparison, and is out of scope for F12.
88
+ *
89
+ * `file_manifest.json` (which drives the NEXT run's incremental diff, see
90
+ * `diffManifest`) is rebuilt from a fresh re-stat of every file under the
91
+ * FINAL lock acquisition (below), not from the run-start walk snapshot or
92
+ * from the parse-phase stamps above. This one does NOT need invariant 1's
93
+ * fix: it is a stat with no accompanying content read in this final phase, so
94
+ * there is no stamp/content pair to invert. If it ever disagrees with
95
+ * `files.mtime` for a file JIT touched concurrently, that disagreement
96
+ * self-heals on the next JIT check (`checkAndRefreshIfStale` compares disk
97
+ * mtime, not the manifest, against `files.mtime`) — the manifest only feeds
98
+ * the NEXT run's `diffManifest`, and a stale manifest entry there causes an
99
+ * extra, harmless re-parse, never a missed one.
100
+ *
101
+ * The two-pass edge-insertion structure (`insertEdges` needs every file's
102
+ * symbols already present) is preserved: ALL pass-1 batches — across the
103
+ * whole run, not just the current batch — complete before pass 2 starts, so
104
+ * cross-file edge targets are always resolvable regardless of how finely
105
+ * pass 1's own lock acquisitions are chunked.
106
+ */
107
+ export async function runIndex(config, options) {
108
+ const startMs = Date.now();
109
+ // initLockMarkers is idempotent — safe to call on every run so that
110
+ // `mast index` works even without a prior `mast init`.
111
+ initLockMarkers(config.resolved_state_dir);
112
+ const lockOptions = {
113
+ maxRetries: 5,
114
+ retryIntervalMs: 1_000,
115
+ caller: 'index-run',
116
+ sink: options.lockMetricsSink,
117
+ };
118
+ const doExtract = options.extractFileFn ?? extractFile;
119
+ // Opening the db handle is connection setup, not a graph/chunk-store
120
+ // mutation — no lock needed.
121
+ const db = openDatabase(config.resolved_state_dir, options.dbOptions ?? {});
122
+ // M1 (eval/GITNEXUS_COMPARISON.md §15.1): chunks live in graph.db's
123
+ // `chunks` table by default — `SqliteChunkStore` wraps the same `db`
124
+ // connection every write below already uses. `chunkStoreOverride` (test
125
+ // DI, §4.4) substitutes a fake for write-failure tests; see its docstring
126
+ // on `IndexOptions` and `graph/populate.ts`'s `ChunkWriter`.
127
+ const chunkStore = options.chunkStoreOverride ?? new SqliteChunkStore(db);
128
+ const chunkWriter = options.chunkStoreOverride
129
+ ? options.chunkStoreOverride.replaceChunksForFile.bind(options.chunkStoreOverride)
130
+ : undefined;
131
+ // Built with conditional spreads for the same reason `index-cmd.ts` builds
132
+ // `dbOptions` that way: `exactOptionalPropertyTypes` distinguishes an absent
133
+ // property from one explicitly set to undefined, and the production path
134
+ // depends on `spans` being genuinely absent so `populateFile` never takes a
135
+ // clock reading at all.
136
+ const populateOptions = {
137
+ ...(chunkWriter !== undefined ? { chunkWriter } : {}),
138
+ ...(options.writeSpans !== undefined ? { spans: options.writeSpans } : {}),
139
+ ...(options.unsafeSkipFtsDeletes === true ? { skipFtsDeletes: true } : {}),
140
+ };
141
+ // Load previous manifest for incremental comparison.
142
+ const manifestPath = join(config.resolved_state_dir, 'file_manifest.json');
143
+ const prevManifest = existsSync(manifestPath)
144
+ ? JSON.parse(readFileSync(manifestPath, 'utf-8'))
145
+ : {};
146
+ const currentFiles = await walkProject(config);
147
+ const { stale, added, deleted } = diffManifest(currentFiles, prevManifest);
148
+ let chunksRemoved = 0;
149
+ // Deleted-file cleanup — cascade removes symbols/edges/imports/chunks from
150
+ // the graph (one transaction, `removeDeletedFiles`). Its own short lock
151
+ // acquisition, separate from pass 1's per-batch locks.
152
+ await withLock(config.resolved_state_dir, 'structure', lockOptions, async () => {
153
+ chunksRemoved += await removeDeletedFiles(db, deleted);
154
+ // Full reindex: also purge DB entries for files no longer in the current
155
+ // walk (e.g. dist/ files left over from a previous indexing configuration
156
+ // that predates the current exclude_patterns). These are invisible to the
157
+ // manifest-diff above because they were never written to file_manifest.json
158
+ // under the current config.
159
+ if (!options.incremental) {
160
+ const currentPaths = new Set(currentFiles.map((e) => e.relativePath));
161
+ const dbRows = await db.selectFrom('files').select('path').execute();
162
+ const orphans = dbRows.map((r) => r.path).filter((p) => !currentPaths.has(p));
163
+ if (orphans.length > 0) {
164
+ chunksRemoved += await removeDeletedFiles(db, orphans);
165
+ }
166
+ }
167
+ });
168
+ // An incremental run's work set is the manifest diff PLUS any walked file
169
+ // with no `files` row. The manifest alone is not sufficient: a run that
170
+ // failed on a file before 2026-08-20 recorded it as up to date anyway
171
+ // (D034), so the diff reports it unchanged forever and the file never comes
172
+ // back. Without this, `mast_status` would correctly report such an index
173
+ // stale (`measureFreshness` reads the `files` stamps too) and every
174
+ // incremental run would do nothing about it — a state that never converges.
175
+ // On a full run every file is reindexed regardless, so this is scoped to the
176
+ // incremental path and costs one indexed `path` scan.
177
+ let toIndex;
178
+ if (options.incremental) {
179
+ const queued = new Set([...stale, ...added].map((e) => e.relativePath));
180
+ const indexedPaths = new Set((await db.selectFrom('files').select('path').execute()).map((r) => r.path));
181
+ const neverWritten = currentFiles.filter((e) => !queued.has(e.relativePath) && !indexedPaths.has(e.relativePath));
182
+ toIndex = [...stale, ...added, ...neverWritten];
183
+ }
184
+ else {
185
+ toIndex = currentFiles;
186
+ }
187
+ let filesIndexed = 0;
188
+ /**
189
+ * Files parsed and queued for a write. NOT `filesIndexed` — a queued file
190
+ * whose write then fails never lands, and conflating the two is what made
191
+ * `files: N indexed` and `files_indexed: N` overstate the index (D038).
192
+ * Kept solely so `onProgress` still advances during the parse phase, which
193
+ * is where the wall-clock is spent on a large batch.
194
+ */
195
+ let filesQueued = 0;
196
+ let parseErrors = 0;
197
+ let writeErrors = 0;
198
+ let staleWriteRejections = 0;
199
+ let chunksAdded = 0;
200
+ let filesStable = 0;
201
+ /**
202
+ * Relative paths this run attempted and did not get into the index — a parse
203
+ * that threw, or a write that failed. They are withheld from the manifest at
204
+ * finalise, so the next run's `diffManifest` sees them as new and retries
205
+ * them. Recording them as up-to-date is what made one transient failure a
206
+ * permanent hole (`docs/defects/LEDGER.md` D034).
207
+ */
208
+ const failedPaths = new Set();
209
+ // Pass 1: parse, then write (chunks + graph + FTS together via
210
+ // populateFile), processed in batches so a batch's worth of parse results
211
+ // doesn't sit in memory indefinitely. SQLite writes are sequential —
212
+ // better-sqlite3 serialises internally, and M1 (§15.1) removed the
213
+ // separate concurrent chunk-store write phase that existed only because
214
+ // the spike's chunk store was a different file/connection from graph.db.
215
+ // F1: `structure.lock` now wraps only the write half of each batch (below),
216
+ // not the parse phase and not the whole loop — see the runIndex WHY-comment.
217
+ const LANCE_BATCH = 16;
218
+ const edgeDataByFile = new Map();
219
+ // Phase accumulators. Summed across batches rather than sampled, because the parse/write
220
+ // interleave runs once per 16-file batch and a single stamp would capture only one of them.
221
+ const phase = { walk: 0, parse: 0, write: 0, edges: 0, finalise: 0 };
222
+ // Everything from `startMs` to here is the walk and manifest diff.
223
+ phase.walk = Date.now() - startMs;
224
+ for (let i = 0; i < toIndex.length; i += LANCE_BATCH) {
225
+ const batch = toIndex.slice(i, i + LANCE_BATCH);
226
+ const batchParseStart = Date.now();
227
+ // Parse phase — synchronous tree-sitter, cannot be parallelised without
228
+ // workers, and does not touch the graph db / chunk store, so it runs
229
+ // UNLOCKED. This is exactly the window in which a concurrent JIT
230
+ // re-parse of a not-yet-reached file can acquire structure.lock.
231
+ const parsed = [];
232
+ for (const entry of batch) {
233
+ try {
234
+ // F12: stat IMMEDIATELY BEFORE doExtract, not after — mirrors
235
+ // mcp/staleness.ts's JIT path (which has always stat'd before
236
+ // parsing). This is invariant 1 from the runIndex WHY-comment:
237
+ // `stamp <= mtime-at-content-read`. Stat'ing AFTER doExtract (the
238
+ // prior ordering) let a concurrent edit land in between, stamping
239
+ // this row with a mtime NEWER than the content that was actually
240
+ // parsed — permanently disarming staleness detection for the file
241
+ // (F12), and no lock can fix that ordering because editors never
242
+ // take structure.lock.
243
+ const preParseMtime = statMtimeSecondsOrFallback(entry.path, entry.mtime);
244
+ const result = doExtract(entry.path, config.resolved_project_root, config.context_lines, config.chunk_split_threshold, config.markdown_heading_depth);
245
+ // §7.1 stability skip: a file whose mtime changed but whose chunked
246
+ // content is identical (e.g. `git checkout` rewriting the same bytes)
247
+ // is re-parsed but need not be re-written. Incremental only; the
248
+ // check is conservative and bails to a full rewrite on any doubt.
249
+ if (options.incremental && await isFileUnchanged(db, chunkStore, entry.relativePath, result)) {
250
+ filesStable++;
251
+ }
252
+ else {
253
+ parsed.push({ entry, result, mtime: preParseMtime });
254
+ // `filesIndexed` / `chunksAdded` are NOT incremented here. Both are
255
+ // counted in the write loop below, once `populateFile` reports the
256
+ // write actually landed — see D038.
257
+ filesQueued++;
258
+ }
259
+ }
260
+ catch (err) {
261
+ process.stderr.write(`[mast] WARN: parse error in ${entry.path}: ${String(err)}\n`);
262
+ parseErrors++;
263
+ failedPaths.add(entry.relativePath);
264
+ }
265
+ options.onProgress?.(filesQueued + filesStable + parseErrors, toIndex.length);
266
+ }
267
+ phase.parse += Date.now() - batchParseStart;
268
+ const batchWriteStart = Date.now();
269
+ // E1-FTS `lock` span — see WriteSpansMs.lock. Acquisition is timed from
270
+ // here to the first statement of the callback, release from the callback's
271
+ // last statement to `withLock`'s return; both directly, neither derived.
272
+ const lockAcquireStart = options.writeSpans === undefined ? 0 : performance.now();
273
+ let lockBodyEnd = 0;
274
+ // Write phase — LOCKED, scoped to this batch only (F1). Bounds how long
275
+ // any caller (including a concurrent JIT re-parse) can be starved to
276
+ // roughly one batch's writes, not the whole run.
277
+ await withLock(config.resolved_state_dir, 'structure', lockOptions, async () => {
278
+ if (options.writeSpans !== undefined) {
279
+ options.writeSpans.lock += performance.now() - lockAcquireStart;
280
+ }
281
+ // populateFile now writes chunks + symbols + edges + imports + FTS in
282
+ // ONE per-file transaction (M1, §15.1) — a failure anywhere in it rolls
283
+ // back everything for that file, so there is no separate "chunk store
284
+ // write" step to run before this loop any more.
285
+ //
286
+ // A write failure is counted as `writeErrors`, NEVER `parseErrors` — the
287
+ // file parsed correctly; only its storage failed. Conflating the two
288
+ // (the original defect, GITNEXUS_COMPARISON.md §15.3 item 3) hid a
289
+ // storage failure behind a "parse error" label, which is the wrong
290
+ // diagnosis for anyone debugging it. Logged at ERROR (not WARN): the
291
+ // file's chunks/symbols/edges/imports/FTS rows are skipped entirely —
292
+ // this is not a soft warning.
293
+ //
294
+ // Deliberately NOT aborted and NOT quarantined per-chunk: one
295
+ // pathological file must not turn a monorepo index into a total outage,
296
+ // and partial-chunk recovery is out of scope for this fix. The file
297
+ // still fails to index — it just fails loudly and is correctly
298
+ // classified, instead of silently amputating the file under a WARN and
299
+ // a miscounted `parseErrors`.
300
+ for (const { entry, result, mtime } of parsed) {
301
+ // `mtime` is the PRE-parse stamp captured in the parse loop above
302
+ // (F12 invariant 1) — carried through untouched, NOT re-stat'd here.
303
+ // Re-statting at this point (the prior ordering) is exactly the bug:
304
+ // it reads disk state AFTER doExtract already read the content, so a
305
+ // concurrent edit landing in between gets stamped as newer-than-content.
306
+ try {
307
+ const { chunksRemoved: removed, written } = await populateFile(db, {
308
+ filePath: entry.relativePath,
309
+ language: result.language,
310
+ mtime,
311
+ chunks: result.chunks,
312
+ imports: result.imports,
313
+ symbols: result.symbols,
314
+ identifierRows: result.identifierRows,
315
+ }, populateOptions);
316
+ if (!written) {
317
+ // Monotonic write-guard (F12 invariant 2, graph/populate.ts)
318
+ // rejected this write: some other writer — a concurrent JIT
319
+ // refresh, or a later batch in this same run reprocessing the
320
+ // file — already stamped a NEWER mtime. The DB row is already at
321
+ // least as fresh as what this parse would have written, so this
322
+ // is correct behaviour, not a failure, but it must stay visible
323
+ // rather than look like an ordinary successful write (the
324
+ // `writeErrors` precedent).
325
+ staleWriteRejections++;
326
+ continue;
327
+ }
328
+ chunksRemoved += removed;
329
+ // Counted HERE, not in the parse loop: this is the first point at
330
+ // which the file is known to be in the index (D038). A stale-write
331
+ // rejection `continue`s above and a write failure `continue`s below,
332
+ // so neither reaches this line.
333
+ filesIndexed++;
334
+ chunksAdded += result.chunks.length;
335
+ }
336
+ catch (err) {
337
+ process.stderr.write(`[mast] ERROR: chunk store write failed for ${entry.path} — file will be absent from the index: ${String(err)}\n`);
338
+ writeErrors++;
339
+ failedPaths.add(entry.relativePath);
340
+ continue;
341
+ }
342
+ edgeDataByFile.set(entry.relativePath, result);
343
+ }
344
+ lockBodyEnd = performance.now();
345
+ });
346
+ if (options.writeSpans !== undefined) {
347
+ options.writeSpans.lock += performance.now() - lockBodyEnd;
348
+ }
349
+ phase.write += Date.now() - batchWriteStart;
350
+ }
351
+ // Pass 2: insert edges and star re-export rows now that all files' symbols
352
+ // and file rows exist (both may reference files indexed later in pass 1).
353
+ // Batched the same way as pass 1 and for the same reason (F1) — each batch
354
+ // gets its own short lock instead of one lock spanning every file's edges.
355
+ // Safe because ALL of pass 1 (every batch above) has already completed by
356
+ // the time this loop starts, so every cross-file edge target this run
357
+ // could produce already exists, independent of how pass 2's own lock
358
+ // acquisitions are chunked.
359
+ const edgeEntries = [...edgeDataByFile];
360
+ const edgesStart = Date.now();
361
+ for (let i = 0; i < edgeEntries.length; i += LANCE_BATCH) {
362
+ const batch = edgeEntries.slice(i, i + LANCE_BATCH);
363
+ await withLock(config.resolved_state_dir, 'structure', lockOptions, async () => {
364
+ for (const [filePath, data] of batch) {
365
+ await insertEdges(db, filePath, data.edges);
366
+ await insertReExportFiles(db, filePath, data.starReExports);
367
+ }
368
+ });
369
+ }
370
+ phase.edges = Date.now() - edgesStart;
371
+ const finaliseStart = Date.now();
372
+ // Final phase — manifest + index.json, under one more short lock. See
373
+ // invariant 2 in the runIndex WHY-comment for why this re-stats every file
374
+ // instead of reusing `currentFiles`' run-start walk snapshot, and why it
375
+ // needs the lock at all (to not race a JIT write that is already in flight).
376
+ await withLock(config.resolved_state_dir, 'structure', lockOptions, async () => {
377
+ // Files this run failed on are withheld: a manifest entry asserts "the
378
+ // index reflects this file at this mtime", which is false for them. Leaving
379
+ // the entry out makes the next run see the file as new and retry it, and —
380
+ // because a file with no manifest entry is also what `measureFreshness`
381
+ // counts as unindexed — keeps both status surfaces honest in the meantime.
382
+ // A file that was never attempted (unchanged on an incremental run) is not
383
+ // in `failedPaths` and keeps its entry, as it should.
384
+ const freshEntries = currentFiles
385
+ .filter((entry) => !failedPaths.has(entry.relativePath))
386
+ .map((entry) => ({
387
+ ...entry,
388
+ mtime: statMtimeSecondsOrFallback(entry.path, entry.mtime),
389
+ }));
390
+ const newManifest = buildManifest(freshEntries);
391
+ writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2));
392
+ const chunkCount = await chunkStore.chunkCount();
393
+ const meta = {
394
+ schema_version: CURRENT_SCHEMA_VERSION,
395
+ last_indexed: new Date().toISOString(),
396
+ file_count: currentFiles.length,
397
+ chunk_count: chunkCount,
398
+ parse_errors: parseErrors > 0 ? parseErrors : undefined,
399
+ write_errors: writeErrors > 0 ? writeErrors : undefined,
400
+ };
401
+ writeFileSync(join(config.resolved_state_dir, 'index.json'), JSON.stringify(meta, null, 2));
402
+ });
403
+ phase.finalise = Date.now() - finaliseStart;
404
+ // Read the tuning pragmas back BEFORE the connection closes — they are
405
+ // connection-scoped state, not file state, and are unrecoverable afterwards.
406
+ const appliedPragmas = {
407
+ cache_size: readPragmaValue('cache_size', (await sql `PRAGMA cache_size`.execute(db)).rows),
408
+ mmap_size: readPragmaValue('mmap_size', (await sql `PRAGMA mmap_size`.execute(db)).rows),
409
+ };
410
+ await db.destroy();
411
+ // SqliteChunkStore (default path) wraps this same `db` connection (§15.1) —
412
+ // no separate handle to close. `chunkStoreOverride`, when supplied, is
413
+ // owned by the caller regardless.
414
+ return {
415
+ // Files skipped = never-parsed (mtime unchanged) + parsed-but-stable.
416
+ filesIndexed,
417
+ filesSkipped: currentFiles.length - toIndex.length + filesStable,
418
+ chunksAdded,
419
+ chunksRemoved,
420
+ parseErrors,
421
+ writeErrors,
422
+ staleWriteRejections,
423
+ // Drained, not read: the resolver is cached for the process lifetime and
424
+ // serves every run, so each run must clear what it reported.
425
+ miscasedImports: getImportResolver(config.resolved_project_root).drainMiscased(),
426
+ durationMs: Date.now() - startMs,
427
+ phaseMs: phase,
428
+ appliedPragmas,
429
+ };
430
+ }
431
+ /**
432
+ * True when `result` is equivalent to what is already stored for `filePath`, so
433
+ * re-writing it would be redundant (§7.1). Conservative:
434
+ *
435
+ * - bails (returns false) if the file has any `block` chunk, whose content is
436
+ * not captured by a symbol hash and so cannot be verified here;
437
+ * - requires the exact same set of chunk ids (catches additions, removals, and
438
+ * declarations that moved to a different line);
439
+ * - requires every stored chunk's CONTENT to match byte-for-byte;
440
+ * - requires every symbol's declaration_hash AND body_hash to match the stored
441
+ * symbol of the same name+line;
442
+ * - requires the stored `imports` rows to match module-for-module.
443
+ *
444
+ * Any mismatch — or any uncertainty — falls through to a full rewrite.
445
+ *
446
+ * D030 — WHY THE LAST TWO CHECKS EXIST. This function decides that a write can
447
+ * be skipped, so its equivalence check must cover everything the write covers.
448
+ * It originally compared chunk IDs and symbol hashes only, and neither moves
449
+ * when an edit lands OUTSIDE every symbol body. An `import` statement always
450
+ * does. `populateFile` writes the `imports` table, and a skipped file is also
451
+ * never added to `edgeDataByFile`, so pass 2 never re-resolves its edges —
452
+ * which made an import-only edit invisible in both places at once. It never
453
+ * self-healed: the finalise phase stamps the manifest from a fresh re-stat of
454
+ * EVERY walked file, written or not, so the next run did not see the file as
455
+ * stale either. Measured: after moving a declaration and repointing its
456
+ * importer, `mast index --incremental` reported `0 indexed, 2 skipped`, the
457
+ * `imports` row still read `./alpha.js` after that path had stopped existing,
458
+ * and `mast_callers` returned `verified_callers: []` for a function with a
459
+ * live caller.
460
+ *
461
+ * Chunk IDs deliberately do NOT change on a content edit (`dedupeChunkIds`,
462
+ * ast/extract.ts — `vectorKey` depends on that), so ID equality can never
463
+ * stand in for content equality here.
464
+ */
465
+ async function isFileUnchanged(db, chunkStore, filePath, result) {
466
+ // `block` chunks carry no symbol hash, and `doc` chunks carry no symbols at
467
+ // all — neither can be content-verified here, so both bail to a full rewrite.
468
+ if (result.chunks.some((c) => c.chunk_type === 'block' || c.chunk_type === 'doc'))
469
+ return false;
470
+ // Structure: identical set of chunk ids, each carrying identical content.
471
+ // The content comparison is what catches an edit outside every symbol body
472
+ // — the import line, a top-level comment, a re-export (D030).
473
+ const storedChunks = await chunkStore.getChunksByFilePath(filePath);
474
+ if (storedChunks.length !== result.chunks.length)
475
+ return false;
476
+ const storedContentById = new Map(storedChunks.map((c) => [c.chunk_id, c.content]));
477
+ if (!result.chunks.every((c) => storedContentById.get(c.chunk_id) === c.content))
478
+ return false;
479
+ // Content: identical symbol hash signature.
480
+ const storedSymbols = await db
481
+ .selectFrom('symbols as s')
482
+ .innerJoin('files as f', 'f.id', 's.file_id')
483
+ .select(['s.name', 's.line', 's.declaration_hash', 's.body_hash'])
484
+ .where('f.path', '=', filePath)
485
+ .execute();
486
+ if (symbolSignature(result.symbols) !== symbolSignature(storedSymbols))
487
+ return false;
488
+ // Imports: an import statement can sit outside every chunk entirely (a file
489
+ // whose only chunks are its declarations, with `context_lines: 0`), so the
490
+ // chunk-content check above does not subsume this one.
491
+ const storedImports = await db
492
+ .selectFrom('imports as i')
493
+ .innerJoin('files as f', 'f.id', 'i.file_id')
494
+ .select(['i.module', 'i.symbols', 'i.is_external', 'i.resolved_path'])
495
+ .where('f.path', '=', filePath)
496
+ .execute();
497
+ return importSignature(result.imports) === importSignature(storedImports);
498
+ }
499
+ /**
500
+ * Order-independent signature of a file's import set, over exactly the columns
501
+ * `populateFile` writes to `imports`. The extracted and stored shapes differ
502
+ * (camelCase vs snake_case, boolean vs 0/1, array vs JSON text), so both are
503
+ * normalised to one canonical string rather than compared field by field.
504
+ */
505
+ function importSignature(imports) {
506
+ return imports
507
+ .map((i) => {
508
+ const symbols = typeof i.symbols === 'string' ? i.symbols : JSON.stringify(i.symbols ?? []);
509
+ const external = i.isExternal ?? i.is_external === 1;
510
+ const resolved = i.resolvedPath ?? i.resolved_path ?? '';
511
+ return `${i.module}|${symbols}|${external ? 1 : 0}|${resolved}`;
512
+ })
513
+ .sort()
514
+ .join('\n');
515
+ }
516
+ /** Order-independent signature of a symbol set's identity + stability hashes. */
517
+ function symbolSignature(symbols) {
518
+ return symbols
519
+ .map((s) => {
520
+ const decl = s.declarationHash ?? s.declaration_hash ?? '';
521
+ const body = s.bodyHash ?? s.body_hash ?? '';
522
+ return `${s.name}|${s.line}|${decl}|${body}`;
523
+ })
524
+ .sort()
525
+ .join('\n');
526
+ }
527
+ // ---------------------------------------------------------------------------
528
+ // Freshness diagnostics (mast_status, `mast status`)
529
+ // ---------------------------------------------------------------------------
530
+ /**
531
+ * Derive the `freshness_cause` discriminator (§9 mast_status) from the stale
532
+ * file count. Stage 7.2 (IMPLEMENTATION_PLAN.md "Stage 7: Vector-store
533
+ * deletion") dropped the second parameter this used to take
534
+ * (`pendingEmbeddings`) along with the `'embedding_backlog'`/`'both'` cases —
535
+ * the Phase 2 embedder that could produce a backlog distinct from Phase 1
536
+ * staleness was excised in Stage 7.1, so a two-cause signature asserted a
537
+ * distinction the code could no longer draw.
538
+ */
539
+ export function freshnessCause(staleFiles) {
540
+ return staleFiles > 0 ? 'phase1_stale' : null;
541
+ }
542
+ /**
543
+ * Load `index.json` from the state directory, or return null if absent.
544
+ *
545
+ * Unvalidated cast, not a zod parse — an `index.json` written before Stage
546
+ * 7.2 (IMPLEMENTATION_PLAN.md "Stage 7: Vector-store deletion") may still
547
+ * carry the removed `model` field; it rides along as an untyped extra key
548
+ * and is simply never read, so old files load without error.
549
+ */
550
+ export function loadIndexMeta(stateDir) {
551
+ const metaPath = join(stateDir, 'index.json');
552
+ if (!existsSync(metaPath))
553
+ return null;
554
+ return JSON.parse(readFileSync(metaPath, 'utf-8'));
555
+ }
556
+ /** Write `index.json` to the state directory. */
557
+ export function writeIndexMeta(stateDir, meta) {
558
+ writeFileSync(join(stateDir, 'index.json'), JSON.stringify(meta, null, 2));
559
+ }
560
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { MastConfig } from '../ast/types.js';
2
+ export interface FileEntry {
3
+ /** Absolute path to the file. */
4
+ readonly path: string;
5
+ /** Relative path from project root (used as the stored `file_path`). */
6
+ readonly relativePath: string;
7
+ /** File mtime in unix seconds. */
8
+ readonly mtime: number;
9
+ }
10
+ /**
11
+ * Convert a glob pattern to a RegExp.
12
+ * `**\/` — matches zero or more leading directories
13
+ * `**` — matches any sequence including `/`
14
+ * `*` — matches any sequence not containing `/`
15
+ * `?` — matches a single non-`/` character
16
+ *
17
+ * Lives in the walker (file-discovery domain) because both watch mode and the
18
+ * MCP tools' `file_pattern` filters need the same glob semantics — and because
19
+ * `walkProject` below hands the same patterns to fast-glob, so this function's
20
+ * output has to agree with fast-glob's for the same pattern. It is checked
21
+ * against fast-glob directly by `__tests__/glob-to-regex.test.ts`.
22
+ *
23
+ * Written as a single left-to-right scan rather than a chain of `.replace`
24
+ * calls. The chain was re-entrant: each rule rewrote the output of the ones
25
+ * before it, so `**` → `.*` left a `*` for the `*` rule to turn into
26
+ * `.[^/]*`, and `**\/` → `(.+/)?` left a `?` for the `?` rule to turn into
27
+ * `(.+/)[^/]`. Every shipped default in `exclude_patterns` (`store/config.ts`)
28
+ * contains one of those two sequences, so every one of them compiled to a
29
+ * regex that matched neither the directory it named nor its contents.
30
+ */
31
+ export declare function globToRegex(pattern: string): RegExp;
32
+ /**
33
+ * Walk the project and return all indexable files.
34
+ *
35
+ * Applies `file_extensions` allowlist and `exclude_patterns` denylist from
36
+ * config. Results are sorted lexicographically by `relativePath` (D1,
37
+ * IMPLEMENTATION_PLAN.md Stage 4): fast-glob returns filesystem order, which
38
+ * varies between identical runs — and because edge insertion order feeds the
39
+ * bare-name fallback in `insertEdges`' name resolution, two identical index
40
+ * runs produced edge sets differing by ±4/3,940 (§15.5). Sorting here makes
41
+ * every downstream consumer (index order, manifest, edge insertion)
42
+ * deterministic at the source. Callers still must not attach SEMANTIC meaning
43
+ * to the order — the guarantee is reproducibility, not priority.
44
+ */
45
+ export declare function walkProject(config: MastConfig): Promise<FileEntry[]>;
46
+ /**
47
+ * Build a `{ relativePath → mtime }` snapshot map from `walkProject` results.
48
+ * Stored as `file_manifest.json` at the end of each index run.
49
+ */
50
+ export declare function buildManifest(entries: readonly FileEntry[]): Record<string, number>;
51
+ /**
52
+ * Compare the current filesystem scan against the previous manifest.
53
+ * Returns sets of paths for changed, new, and deleted files.
54
+ */
55
+ export declare function diffManifest(current: readonly FileEntry[], previousManifest: Record<string, number>): {
56
+ stale: FileEntry[];
57
+ added: FileEntry[];
58
+ deleted: string[];
59
+ };
60
+ //# sourceMappingURL=walker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walker.d.ts","sourceRoot":"","sources":["../../src/indexer/walker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,SAAS;IACxB,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAwBnD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAgC1E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMnF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACvC;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAoBA"}