@cstart/coldstart 2.0.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 (360) hide show
  1. package/README.md +236 -0
  2. package/dist/cache/disk-cache.d.ts +14 -0
  3. package/dist/cache/disk-cache.d.ts.map +1 -0
  4. package/dist/cache/disk-cache.js +371 -0
  5. package/dist/cache/disk-cache.js.map +1 -0
  6. package/dist/cli.d.ts +18 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +206 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/constants.d.ts +8 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/dist/constants.js +111 -0
  13. package/dist/constants.js.map +1 -0
  14. package/dist/daemon-lock.d.ts +63 -0
  15. package/dist/daemon-lock.d.ts.map +1 -0
  16. package/dist/daemon-lock.js +218 -0
  17. package/dist/daemon-lock.js.map +1 -0
  18. package/dist/daemon-log.d.ts +26 -0
  19. package/dist/daemon-log.d.ts.map +1 -0
  20. package/dist/daemon-log.js +110 -0
  21. package/dist/daemon-log.js.map +1 -0
  22. package/dist/index-manager.d.ts +103 -0
  23. package/dist/index-manager.d.ts.map +1 -0
  24. package/dist/index-manager.js +361 -0
  25. package/dist/index-manager.js.map +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +724 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/indexer/content-tokens.d.ts +74 -0
  31. package/dist/indexer/content-tokens.d.ts.map +1 -0
  32. package/dist/indexer/content-tokens.js +406 -0
  33. package/dist/indexer/content-tokens.js.map +1 -0
  34. package/dist/indexer/cpp-include-roots.d.ts +7 -0
  35. package/dist/indexer/cpp-include-roots.d.ts.map +1 -0
  36. package/dist/indexer/cpp-include-roots.js +166 -0
  37. package/dist/indexer/cpp-include-roots.js.map +1 -0
  38. package/dist/indexer/csharp-synthetic.d.ts +9 -0
  39. package/dist/indexer/csharp-synthetic.d.ts.map +1 -0
  40. package/dist/indexer/csharp-synthetic.js +51 -0
  41. package/dist/indexer/csharp-synthetic.js.map +1 -0
  42. package/dist/indexer/django-synthetic.d.ts +12 -0
  43. package/dist/indexer/django-synthetic.d.ts.map +1 -0
  44. package/dist/indexer/django-synthetic.js +49 -0
  45. package/dist/indexer/django-synthetic.js.map +1 -0
  46. package/dist/indexer/extractors/angularjs.d.ts +16 -0
  47. package/dist/indexer/extractors/angularjs.d.ts.map +1 -0
  48. package/dist/indexer/extractors/angularjs.js +49 -0
  49. package/dist/indexer/extractors/angularjs.js.map +1 -0
  50. package/dist/indexer/extractors/cpp.d.ts +9 -0
  51. package/dist/indexer/extractors/cpp.d.ts.map +1 -0
  52. package/dist/indexer/extractors/cpp.js +254 -0
  53. package/dist/indexer/extractors/cpp.js.map +1 -0
  54. package/dist/indexer/extractors/csharp.d.ts +19 -0
  55. package/dist/indexer/extractors/csharp.d.ts.map +1 -0
  56. package/dist/indexer/extractors/csharp.js +327 -0
  57. package/dist/indexer/extractors/csharp.js.map +1 -0
  58. package/dist/indexer/extractors/env.d.ts +9 -0
  59. package/dist/indexer/extractors/env.d.ts.map +1 -0
  60. package/dist/indexer/extractors/env.js +43 -0
  61. package/dist/indexer/extractors/env.js.map +1 -0
  62. package/dist/indexer/extractors/go.d.ts +9 -0
  63. package/dist/indexer/extractors/go.d.ts.map +1 -0
  64. package/dist/indexer/extractors/go.js +227 -0
  65. package/dist/indexer/extractors/go.js.map +1 -0
  66. package/dist/indexer/extractors/graphql.d.ts +22 -0
  67. package/dist/indexer/extractors/graphql.d.ts.map +1 -0
  68. package/dist/indexer/extractors/graphql.js +88 -0
  69. package/dist/indexer/extractors/graphql.js.map +1 -0
  70. package/dist/indexer/extractors/groovy.d.ts +9 -0
  71. package/dist/indexer/extractors/groovy.d.ts.map +1 -0
  72. package/dist/indexer/extractors/groovy.js +356 -0
  73. package/dist/indexer/extractors/groovy.js.map +1 -0
  74. package/dist/indexer/extractors/java.d.ts +10 -0
  75. package/dist/indexer/extractors/java.d.ts.map +1 -0
  76. package/dist/indexer/extractors/java.js +586 -0
  77. package/dist/indexer/extractors/java.js.map +1 -0
  78. package/dist/indexer/extractors/kotlin.d.ts +10 -0
  79. package/dist/indexer/extractors/kotlin.d.ts.map +1 -0
  80. package/dist/indexer/extractors/kotlin.js +398 -0
  81. package/dist/indexer/extractors/kotlin.js.map +1 -0
  82. package/dist/indexer/extractors/node-helpers.d.ts +13 -0
  83. package/dist/indexer/extractors/node-helpers.d.ts.map +1 -0
  84. package/dist/indexer/extractors/node-helpers.js +17 -0
  85. package/dist/indexer/extractors/node-helpers.js.map +1 -0
  86. package/dist/indexer/extractors/parser-factory.d.ts +4 -0
  87. package/dist/indexer/extractors/parser-factory.d.ts.map +1 -0
  88. package/dist/indexer/extractors/parser-factory.js +28 -0
  89. package/dist/indexer/extractors/parser-factory.js.map +1 -0
  90. package/dist/indexer/extractors/php.d.ts +17 -0
  91. package/dist/indexer/extractors/php.d.ts.map +1 -0
  92. package/dist/indexer/extractors/php.js +363 -0
  93. package/dist/indexer/extractors/php.js.map +1 -0
  94. package/dist/indexer/extractors/python.d.ts +14 -0
  95. package/dist/indexer/extractors/python.d.ts.map +1 -0
  96. package/dist/indexer/extractors/python.js +449 -0
  97. package/dist/indexer/extractors/python.js.map +1 -0
  98. package/dist/indexer/extractors/ruby.d.ts +10 -0
  99. package/dist/indexer/extractors/ruby.d.ts.map +1 -0
  100. package/dist/indexer/extractors/ruby.js +996 -0
  101. package/dist/indexer/extractors/ruby.js.map +1 -0
  102. package/dist/indexer/extractors/rust.d.ts +9 -0
  103. package/dist/indexer/extractors/rust.d.ts.map +1 -0
  104. package/dist/indexer/extractors/rust.js +322 -0
  105. package/dist/indexer/extractors/rust.js.map +1 -0
  106. package/dist/indexer/extractors/toml.d.ts +9 -0
  107. package/dist/indexer/extractors/toml.d.ts.map +1 -0
  108. package/dist/indexer/extractors/toml.js +84 -0
  109. package/dist/indexer/extractors/toml.js.map +1 -0
  110. package/dist/indexer/extractors/xml.d.ts +9 -0
  111. package/dist/indexer/extractors/xml.d.ts.map +1 -0
  112. package/dist/indexer/extractors/xml.js +163 -0
  113. package/dist/indexer/extractors/xml.js.map +1 -0
  114. package/dist/indexer/extractors/yaml.d.ts +9 -0
  115. package/dist/indexer/extractors/yaml.d.ts.map +1 -0
  116. package/dist/indexer/extractors/yaml.js +117 -0
  117. package/dist/indexer/extractors/yaml.js.map +1 -0
  118. package/dist/indexer/git.d.ts +19 -0
  119. package/dist/indexer/git.d.ts.map +1 -0
  120. package/dist/indexer/git.js +55 -0
  121. package/dist/indexer/git.js.map +1 -0
  122. package/dist/indexer/graph.d.ts +7 -0
  123. package/dist/indexer/graph.d.ts.map +1 -0
  124. package/dist/indexer/graph.js +20 -0
  125. package/dist/indexer/graph.js.map +1 -0
  126. package/dist/indexer/indexed-file.d.ts +15 -0
  127. package/dist/indexer/indexed-file.d.ts.map +1 -0
  128. package/dist/indexer/indexed-file.js +50 -0
  129. package/dist/indexer/indexed-file.js.map +1 -0
  130. package/dist/indexer/invariants.d.ts +15 -0
  131. package/dist/indexer/invariants.d.ts.map +1 -0
  132. package/dist/indexer/invariants.js +96 -0
  133. package/dist/indexer/invariants.js.map +1 -0
  134. package/dist/indexer/laravel-synthetic.d.ts +10 -0
  135. package/dist/indexer/laravel-synthetic.d.ts.map +1 -0
  136. package/dist/indexer/laravel-synthetic.js +78 -0
  137. package/dist/indexer/laravel-synthetic.js.map +1 -0
  138. package/dist/indexer/parser.d.ts +4 -0
  139. package/dist/indexer/parser.d.ts.map +1 -0
  140. package/dist/indexer/parser.js +499 -0
  141. package/dist/indexer/parser.js.map +1 -0
  142. package/dist/indexer/patch.d.ts +3 -0
  143. package/dist/indexer/patch.d.ts.map +1 -0
  144. package/dist/indexer/patch.js +291 -0
  145. package/dist/indexer/patch.js.map +1 -0
  146. package/dist/indexer/rails-synthetic.d.ts +11 -0
  147. package/dist/indexer/rails-synthetic.d.ts.map +1 -0
  148. package/dist/indexer/rails-synthetic.js +80 -0
  149. package/dist/indexer/rails-synthetic.js.map +1 -0
  150. package/dist/indexer/reconcile.d.ts +9 -0
  151. package/dist/indexer/reconcile.d.ts.map +1 -0
  152. package/dist/indexer/reconcile.js +96 -0
  153. package/dist/indexer/reconcile.js.map +1 -0
  154. package/dist/indexer/resolvers/cpp.d.ts +15 -0
  155. package/dist/indexer/resolvers/cpp.d.ts.map +1 -0
  156. package/dist/indexer/resolvers/cpp.js +35 -0
  157. package/dist/indexer/resolvers/cpp.js.map +1 -0
  158. package/dist/indexer/resolvers/csharp.d.ts +2 -0
  159. package/dist/indexer/resolvers/csharp.d.ts.map +1 -0
  160. package/dist/indexer/resolvers/csharp.js +89 -0
  161. package/dist/indexer/resolvers/csharp.js.map +1 -0
  162. package/dist/indexer/resolvers/generic.d.ts +10 -0
  163. package/dist/indexer/resolvers/generic.d.ts.map +1 -0
  164. package/dist/indexer/resolvers/generic.js +35 -0
  165. package/dist/indexer/resolvers/generic.js.map +1 -0
  166. package/dist/indexer/resolvers/go.d.ts +2 -0
  167. package/dist/indexer/resolvers/go.d.ts.map +1 -0
  168. package/dist/indexer/resolvers/go.js +171 -0
  169. package/dist/indexer/resolvers/go.js.map +1 -0
  170. package/dist/indexer/resolvers/index.d.ts +26 -0
  171. package/dist/indexer/resolvers/index.d.ts.map +1 -0
  172. package/dist/indexer/resolvers/index.js +301 -0
  173. package/dist/indexer/resolvers/index.js.map +1 -0
  174. package/dist/indexer/resolvers/java.d.ts +17 -0
  175. package/dist/indexer/resolvers/java.d.ts.map +1 -0
  176. package/dist/indexer/resolvers/java.js +104 -0
  177. package/dist/indexer/resolvers/java.js.map +1 -0
  178. package/dist/indexer/resolvers/php.d.ts +2 -0
  179. package/dist/indexer/resolvers/php.d.ts.map +1 -0
  180. package/dist/indexer/resolvers/php.js +108 -0
  181. package/dist/indexer/resolvers/php.js.map +1 -0
  182. package/dist/indexer/resolvers/python.d.ts +17 -0
  183. package/dist/indexer/resolvers/python.d.ts.map +1 -0
  184. package/dist/indexer/resolvers/python.js +63 -0
  185. package/dist/indexer/resolvers/python.js.map +1 -0
  186. package/dist/indexer/resolvers/ruby.d.ts +13 -0
  187. package/dist/indexer/resolvers/ruby.d.ts.map +1 -0
  188. package/dist/indexer/resolvers/ruby.js +199 -0
  189. package/dist/indexer/resolvers/ruby.js.map +1 -0
  190. package/dist/indexer/resolvers/rust.d.ts +12 -0
  191. package/dist/indexer/resolvers/rust.d.ts.map +1 -0
  192. package/dist/indexer/resolvers/rust.js +61 -0
  193. package/dist/indexer/resolvers/rust.js.map +1 -0
  194. package/dist/indexer/resolvers/shared.d.ts +18 -0
  195. package/dist/indexer/resolvers/shared.d.ts.map +1 -0
  196. package/dist/indexer/resolvers/shared.js +71 -0
  197. package/dist/indexer/resolvers/shared.js.map +1 -0
  198. package/dist/indexer/rust-workspace.d.ts +25 -0
  199. package/dist/indexer/rust-workspace.d.ts.map +1 -0
  200. package/dist/indexer/rust-workspace.js +155 -0
  201. package/dist/indexer/rust-workspace.js.map +1 -0
  202. package/dist/indexer/symbol-edges.d.ts +19 -0
  203. package/dist/indexer/symbol-edges.d.ts.map +1 -0
  204. package/dist/indexer/symbol-edges.js +117 -0
  205. package/dist/indexer/symbol-edges.js.map +1 -0
  206. package/dist/indexer/tokenize.d.ts +26 -0
  207. package/dist/indexer/tokenize.d.ts.map +1 -0
  208. package/dist/indexer/tokenize.js +200 -0
  209. package/dist/indexer/tokenize.js.map +1 -0
  210. package/dist/indexer/ts-parser.d.ts +10 -0
  211. package/dist/indexer/ts-parser.d.ts.map +1 -0
  212. package/dist/indexer/ts-parser.js +605 -0
  213. package/dist/indexer/ts-parser.js.map +1 -0
  214. package/dist/indexer/walker.d.ts +9 -0
  215. package/dist/indexer/walker.d.ts.map +1 -0
  216. package/dist/indexer/walker.js +91 -0
  217. package/dist/indexer/walker.js.map +1 -0
  218. package/dist/init.d.ts +111 -0
  219. package/dist/init.d.ts.map +1 -0
  220. package/dist/init.js +787 -0
  221. package/dist/init.js.map +1 -0
  222. package/dist/kb/cli.d.ts +2 -0
  223. package/dist/kb/cli.d.ts.map +1 -0
  224. package/dist/kb/cli.js +377 -0
  225. package/dist/kb/cli.js.map +1 -0
  226. package/dist/kb/commit.d.ts +6 -0
  227. package/dist/kb/commit.d.ts.map +1 -0
  228. package/dist/kb/commit.js +84 -0
  229. package/dist/kb/commit.js.map +1 -0
  230. package/dist/kb/fold.d.ts +42 -0
  231. package/dist/kb/fold.d.ts.map +1 -0
  232. package/dist/kb/fold.js +285 -0
  233. package/dist/kb/fold.js.map +1 -0
  234. package/dist/kb/freshness.d.ts +9 -0
  235. package/dist/kb/freshness.d.ts.map +1 -0
  236. package/dist/kb/freshness.js +52 -0
  237. package/dist/kb/freshness.js.map +1 -0
  238. package/dist/kb/git.d.ts +4 -0
  239. package/dist/kb/git.d.ts.map +1 -0
  240. package/dist/kb/git.js +28 -0
  241. package/dist/kb/git.js.map +1 -0
  242. package/dist/kb/ids.d.ts +8 -0
  243. package/dist/kb/ids.d.ts.map +1 -0
  244. package/dist/kb/ids.js +42 -0
  245. package/dist/kb/ids.js.map +1 -0
  246. package/dist/kb/lint.d.ts +23 -0
  247. package/dist/kb/lint.d.ts.map +1 -0
  248. package/dist/kb/lint.js +102 -0
  249. package/dist/kb/lint.js.map +1 -0
  250. package/dist/kb/lookup.d.ts +55 -0
  251. package/dist/kb/lookup.d.ts.map +1 -0
  252. package/dist/kb/lookup.js +92 -0
  253. package/dist/kb/lookup.js.map +1 -0
  254. package/dist/kb/notes-index.d.ts +31 -0
  255. package/dist/kb/notes-index.d.ts.map +1 -0
  256. package/dist/kb/notes-index.js +0 -0
  257. package/dist/kb/notes-index.js.map +1 -0
  258. package/dist/kb/raw-log.d.ts +34 -0
  259. package/dist/kb/raw-log.d.ts.map +1 -0
  260. package/dist/kb/raw-log.js +106 -0
  261. package/dist/kb/raw-log.js.map +1 -0
  262. package/dist/kb/render.d.ts +17 -0
  263. package/dist/kb/render.d.ts.map +1 -0
  264. package/dist/kb/render.js +101 -0
  265. package/dist/kb/render.js.map +1 -0
  266. package/dist/kb/search.d.ts +110 -0
  267. package/dist/kb/search.d.ts.map +1 -0
  268. package/dist/kb/search.js +453 -0
  269. package/dist/kb/search.js.map +1 -0
  270. package/dist/kb/store.d.ts +35 -0
  271. package/dist/kb/store.d.ts.map +1 -0
  272. package/dist/kb/store.js +160 -0
  273. package/dist/kb/store.js.map +1 -0
  274. package/dist/kb/types.d.ts +142 -0
  275. package/dist/kb/types.d.ts.map +1 -0
  276. package/dist/kb/types.js +13 -0
  277. package/dist/kb/types.js.map +1 -0
  278. package/dist/kb/view-template.d.ts +8 -0
  279. package/dist/kb/view-template.d.ts.map +1 -0
  280. package/dist/kb/view-template.js +8 -0
  281. package/dist/kb/view-template.js.map +1 -0
  282. package/dist/kb/view.d.ts +42 -0
  283. package/dist/kb/view.d.ts.map +1 -0
  284. package/dist/kb/view.js +118 -0
  285. package/dist/kb/view.js.map +1 -0
  286. package/dist/kb/write.d.ts +72 -0
  287. package/dist/kb/write.d.ts.map +1 -0
  288. package/dist/kb/write.js +345 -0
  289. package/dist/kb/write.js.map +1 -0
  290. package/dist/keeper-state.d.ts +29 -0
  291. package/dist/keeper-state.d.ts.map +1 -0
  292. package/dist/keeper-state.js +89 -0
  293. package/dist/keeper-state.js.map +1 -0
  294. package/dist/keeper.d.ts +23 -0
  295. package/dist/keeper.d.ts.map +1 -0
  296. package/dist/keeper.js +105 -0
  297. package/dist/keeper.js.map +1 -0
  298. package/dist/migrate.d.ts +9 -0
  299. package/dist/migrate.d.ts.map +1 -0
  300. package/dist/migrate.js +102 -0
  301. package/dist/migrate.js.map +1 -0
  302. package/dist/restart.d.ts +15 -0
  303. package/dist/restart.d.ts.map +1 -0
  304. package/dist/restart.js +48 -0
  305. package/dist/restart.js.map +1 -0
  306. package/dist/server/find.d.ts +39 -0
  307. package/dist/server/find.d.ts.map +1 -0
  308. package/dist/server/find.js +979 -0
  309. package/dist/server/find.js.map +1 -0
  310. package/dist/server/mcp.d.ts +117 -0
  311. package/dist/server/mcp.d.ts.map +1 -0
  312. package/dist/server/mcp.js +301 -0
  313. package/dist/server/mcp.js.map +1 -0
  314. package/dist/server/searcher.d.ts +14 -0
  315. package/dist/server/searcher.d.ts.map +1 -0
  316. package/dist/server/searcher.js +146 -0
  317. package/dist/server/searcher.js.map +1 -0
  318. package/dist/server/tools.d.ts +17 -0
  319. package/dist/server/tools.d.ts.map +1 -0
  320. package/dist/server/tools.js +875 -0
  321. package/dist/server/tools.js.map +1 -0
  322. package/dist/status.d.ts +12 -0
  323. package/dist/status.d.ts.map +1 -0
  324. package/dist/status.js +157 -0
  325. package/dist/status.js.map +1 -0
  326. package/dist/types.d.ts +138 -0
  327. package/dist/types.d.ts.map +1 -0
  328. package/dist/types.js +3 -0
  329. package/dist/types.js.map +1 -0
  330. package/dist/watcher.d.ts +11 -0
  331. package/dist/watcher.d.ts.map +1 -0
  332. package/dist/watcher.js +67 -0
  333. package/dist/watcher.js.map +1 -0
  334. package/hooks/canonical-find-key.mjs +68 -0
  335. package/hooks/codex-find-nudge.mjs +15 -0
  336. package/hooks/codex-find-preguard.mjs +14 -0
  337. package/hooks/codex-kb-elicit.mjs +329 -0
  338. package/hooks/codex-kb-recall.mjs +151 -0
  339. package/hooks/codex-nudge-handler.mjs +370 -0
  340. package/hooks/codex-preguard-handler.mjs +75 -0
  341. package/hooks/codex-run-hook.mjs +132 -0
  342. package/hooks/coldstart-call.mjs +58 -0
  343. package/hooks/cursor-find-nudge.mjs +15 -0
  344. package/hooks/cursor-find-preguard.mjs +14 -0
  345. package/hooks/cursor-input.mjs +72 -0
  346. package/hooks/cursor-kb-elicit.mjs +335 -0
  347. package/hooks/cursor-kb-recall.mjs +138 -0
  348. package/hooks/cursor-nudge-handler.mjs +33 -0
  349. package/hooks/cursor-preguard-handler.mjs +39 -0
  350. package/hooks/cursor-run-hook.mjs +129 -0
  351. package/hooks/find-nudge.mjs +15 -0
  352. package/hooks/find-preguard.mjs +14 -0
  353. package/hooks/kb-elicit.mjs +334 -0
  354. package/hooks/kb-recall.mjs +151 -0
  355. package/hooks/nudge-handler.mjs +370 -0
  356. package/hooks/preguard-handler.mjs +75 -0
  357. package/hooks/run-hook.mjs +132 -0
  358. package/package.json +69 -0
  359. package/templates/coldstart.cli.md +60 -0
  360. package/templates/coldstart.mcp.md +59 -0
@@ -0,0 +1,291 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { relative, extname } from 'node:path';
3
+ import { EXTENSION_TO_LANGUAGE, DEFAULT_EXCLUDES } from '../constants.js';
4
+ import { parseFile, buildFileId } from './parser.js';
5
+ import { buildFileDomains, isTestPath } from './tokenize.js';
6
+ import { resolveImportsForFiles, buildPackageIndex } from './resolvers/index.js';
7
+ import { buildSymbolEdges } from './symbol-edges.js';
8
+ import { buildContentTokenPostings } from './content-tokens.js';
9
+ import { baseIndexedFile } from './indexed-file.js';
10
+ import { addRailsSyntheticEdges } from './rails-synthetic.js';
11
+ import { addLaravelSyntheticEdges } from './laravel-synthetic.js';
12
+ import { addCSharpSyntheticEdges } from './csharp-synthetic.js';
13
+ import { addDjangoSyntheticEdges } from './django-synthetic.js';
14
+ export async function patchIndex(index, changedAbsPaths, rootDir) {
15
+ // Current fileIdSet — used for import resolution of patched files
16
+ const fileIdSet = new Set(index.files.keys());
17
+ // Track which file IDs were touched (for importedByCount recomputation)
18
+ const affectedIds = new Set();
19
+ // -------------------------------------------------------------------------
20
+ // Phase 0: Pre-plan — decide delete/update/skip for every file in the batch
21
+ // WITHOUT touching the index yet. This ensures state is only
22
+ // stripped when we know we can successfully replace it.
23
+ // -------------------------------------------------------------------------
24
+ const plans = new Map();
25
+ for (const absPath of changedAbsPaths) {
26
+ const ext = extname(absPath).toLowerCase();
27
+ const lang = EXTENSION_TO_LANGUAGE[ext];
28
+ if (!lang)
29
+ continue;
30
+ const relPath = relative(rootDir, absPath).replace(/\\/g, '/');
31
+ // Mirror the walker's directory rules: it never descends into hidden or
32
+ // excluded dirs, so a patch must not index files from them either. Both
33
+ // batch producers leak such paths otherwise — the watcher (live edit to
34
+ // .claude/settings.json) and reconcile (porcelain lists it as untracked).
35
+ // Hidden FILES at the root (.rubocop.yml) are walked, so only dir
36
+ // segments are filtered.
37
+ const dirSegments = relPath.split('/').slice(0, -1);
38
+ if (dirSegments.some((s) => s.startsWith('.') || DEFAULT_EXCLUDES.has(s)))
39
+ continue;
40
+ const fileId = buildFileId(relPath);
41
+ // Check existence first (covers deletions)
42
+ let liveStat = null;
43
+ try {
44
+ liveStat = await stat(absPath);
45
+ }
46
+ catch { /* deleted */ }
47
+ if (!liveStat) {
48
+ plans.set(fileId, { absPath, fileId, plan: { action: 'delete' } });
49
+ continue;
50
+ }
51
+ const parsed = await parseFile(absPath, lang, fileId);
52
+ if (!parsed) {
53
+ // Parse failed — skip this file entirely. Index state is preserved.
54
+ plans.set(fileId, { absPath, fileId, plan: { action: 'skip' } });
55
+ continue;
56
+ }
57
+ // Hash check: if content is identical, this was a false-positive watch event
58
+ const oldFile = index.files.get(fileId);
59
+ if (oldFile && oldFile.hash === parsed.hash) {
60
+ // Refresh the fingerprint anyway: a touch-without-change moved mtime,
61
+ // and a stale stamp would make reconcile/audit re-flag this file forever.
62
+ oldFile.mtimeMs = liveStat.mtimeMs;
63
+ oldFile.sizeBytes = liveStat.size;
64
+ plans.set(fileId, { absPath, fileId, plan: { action: 'skip' } });
65
+ continue;
66
+ }
67
+ plans.set(fileId, { absPath, fileId, plan: { action: 'update', parsed, lang, relPath } });
68
+ }
69
+ // -------------------------------------------------------------------------
70
+ // Phase 1: Strip old data — only for files planned as 'delete' or 'update'
71
+ // -------------------------------------------------------------------------
72
+ for (const { fileId, plan } of plans.values()) {
73
+ if (plan.action === 'skip')
74
+ continue;
75
+ affectedIds.add(fileId);
76
+ const oldFile = index.files.get(fileId);
77
+ if (!oldFile)
78
+ continue; // new file — nothing old to remove
79
+ // Remove outgoing flat edges (from === fileId)
80
+ index.edges = index.edges.filter(e => e.from !== fileId);
81
+ // Remove fileId from each import target's inEdges
82
+ const oldOutgoing = index.outEdges.get(fileId) ?? [];
83
+ for (const targetId of oldOutgoing) {
84
+ const importers = index.inEdges.get(targetId);
85
+ if (importers) {
86
+ const i = importers.indexOf(fileId);
87
+ if (i !== -1)
88
+ importers.splice(i, 1);
89
+ }
90
+ }
91
+ index.outEdges.set(fileId, []);
92
+ // Decrement tokenDocFreq for old file's tokens (skip barrel files)
93
+ if (!oldFile.isBarrel) {
94
+ for (const token of Object.keys(oldFile.domainMap)) {
95
+ const count = index.tokenDocFreq.get(token) ?? 0;
96
+ if (count <= 1)
97
+ index.tokenDocFreq.delete(token);
98
+ else
99
+ index.tokenDocFreq.set(token, count - 1);
100
+ }
101
+ }
102
+ // Remove symbolEdges originating from this file or its symbols
103
+ const prefix = fileId + '#';
104
+ index.symbolEdges = index.symbolEdges.filter(se => se.from !== fileId && !se.from.startsWith(prefix));
105
+ }
106
+ // -------------------------------------------------------------------------
107
+ // Phase 2: Apply deletions — clean up all graph pointers to deleted files
108
+ // -------------------------------------------------------------------------
109
+ for (const { fileId, plan } of plans.values()) {
110
+ if (plan.action !== 'delete')
111
+ continue;
112
+ // Files that imported the deleted file
113
+ const deletedImporters = index.inEdges.get(fileId) ?? [];
114
+ // Remove ALL flat edges pointing to this deleted file (incoming edges)
115
+ index.edges = index.edges.filter(e => e.to !== fileId);
116
+ // Remove symbolEdges pointing INTO the deleted file (other files' calls /
117
+ // extends / re-exports of its symbols). Phase 1 only strips edges FROM
118
+ // changed files; the callers aren't in the change set, and a dangling
119
+ // se.to fails the invariant lint and forces a full rebuild on every
120
+ // delete of a referenced file.
121
+ const deletedPrefix = fileId + '#';
122
+ index.symbolEdges = index.symbolEdges.filter(se => se.to !== fileId && !se.to.startsWith(deletedPrefix));
123
+ // Remove fileId from each importer's outEdges array
124
+ for (const importerId of deletedImporters) {
125
+ const importerOut = index.outEdges.get(importerId);
126
+ if (importerOut) {
127
+ const i = importerOut.indexOf(fileId);
128
+ if (i !== -1)
129
+ importerOut.splice(i, 1);
130
+ }
131
+ affectedIds.add(importerId);
132
+ }
133
+ index.files.delete(fileId);
134
+ index.outEdges.delete(fileId);
135
+ index.inEdges.delete(fileId);
136
+ fileIdSet.delete(fileId);
137
+ }
138
+ // -------------------------------------------------------------------------
139
+ // Phase 3: Apply updates — build new index entries
140
+ // -------------------------------------------------------------------------
141
+ const newFiles = [];
142
+ for (const { absPath, fileId, plan } of plans.values()) {
143
+ if (plan.action !== 'update')
144
+ continue;
145
+ const { parsed, lang, relPath } = plan;
146
+ const oldFile = index.files.get(fileId);
147
+ const newFile = {
148
+ ...baseIndexedFile(fileId, absPath, relPath, lang, parsed),
149
+ domainMap: buildFileDomains(relPath, parsed.exports),
150
+ importedByCount: oldFile?.importedByCount ?? 0,
151
+ transitiveImportedByCount: oldFile?.transitiveImportedByCount ?? 0,
152
+ isBarrel: false,
153
+ isTestFile: isTestPath(relPath),
154
+ };
155
+ index.files.set(fileId, newFile);
156
+ fileIdSet.add(fileId);
157
+ if (!index.inEdges.has(fileId))
158
+ index.inEdges.set(fileId, []);
159
+ index.outEdges.set(fileId, []);
160
+ newFiles.push(newFile);
161
+ }
162
+ if (newFiles.length === 0 && affectedIds.size === 0)
163
+ return; // nothing actionable
164
+ // -------------------------------------------------------------------------
165
+ // Phase 4: Resolve imports for updated files, rebuild edges + inEdges
166
+ // -------------------------------------------------------------------------
167
+ if (newFiles.length > 0) {
168
+ // Build the JVM package index from the FULL set (not just newFiles) so a
169
+ // changed Java/Kotlin file resolves against unchanged ones by declared package.
170
+ const pkgById = buildPackageIndex([...index.files.values()]);
171
+ const { edges: newEdges } = await resolveImportsForFiles(newFiles, fileIdSet, rootDir, pkgById);
172
+ for (const edge of newEdges) {
173
+ index.edges.push(edge);
174
+ // outEdges
175
+ const out = index.outEdges.get(edge.from) ?? [];
176
+ if (!out.includes(edge.to))
177
+ out.push(edge.to);
178
+ index.outEdges.set(edge.from, out);
179
+ // inEdges
180
+ if (!index.inEdges.has(edge.to))
181
+ index.inEdges.set(edge.to, []);
182
+ const inn = index.inEdges.get(edge.to);
183
+ if (!inn.includes(edge.from))
184
+ inn.push(edge.from);
185
+ }
186
+ // -----------------------------------------------------------------------
187
+ // Phase 5: Barrel detection + tokenDocFreq + symbolEdges for new files
188
+ // -----------------------------------------------------------------------
189
+ for (const file of newFiles) {
190
+ if (file.language === 'typescript' || file.language === 'javascript') {
191
+ file.isBarrel = ((file.reexportRatio ?? 0) > 0.5 &&
192
+ (index.inEdges.get(file.id)?.length ?? 0) > 1 &&
193
+ file.exports.length > 0);
194
+ if (file.isBarrel) {
195
+ for (const [token, ev] of Object.entries(file.domainMap)) {
196
+ if (ev.filename === 0 && ev.path === 0) {
197
+ delete file.domainMap[token];
198
+ }
199
+ else {
200
+ file.domainMap[token] = { ...ev, symbol: 0 };
201
+ }
202
+ }
203
+ }
204
+ }
205
+ if (!file.isBarrel) {
206
+ for (const token of Object.keys(file.domainMap)) {
207
+ index.tokenDocFreq.set(token, (index.tokenDocFreq.get(token) ?? 0) + 1);
208
+ }
209
+ }
210
+ for (const edge of buildSymbolEdges([file], index.outEdges, index.files)) {
211
+ index.symbolEdges.push(edge);
212
+ }
213
+ }
214
+ // -----------------------------------------------------------------------
215
+ // Phase 5.5: Refresh synthetic (convention) edges. Phase 1 stripped the
216
+ // outgoing edges of every changed file, INCLUDING its synthetic
217
+ // Rails/Django/Laravel/C# convention edges — so without this, editing a
218
+ // convention file deletes its association/view/route edges until the next
219
+ // full rebuild. The synthetic passes are idempotent (each seeds a `seen`
220
+ // set from the existing edges and only adds what's missing), so re-running
221
+ // them over the full file set re-creates exactly the stripped edges with no
222
+ // duplication. Gate on the languages that actually changed so a
223
+ // non-convention edit (e.g. a TS file in a Rails repo) pays nothing.
224
+ const changedLangs = new Set(newFiles.map(f => f.language));
225
+ if (changedLangs.has('ruby') || changedLangs.has('php') ||
226
+ changedLangs.has('csharp') || changedLangs.has('python')) {
227
+ const allFiles = [...index.files.values()];
228
+ const before = index.edges.length;
229
+ if (changedLangs.has('ruby'))
230
+ await addRailsSyntheticEdges(allFiles, index.edges, fileIdSet, rootDir);
231
+ if (changedLangs.has('php'))
232
+ await addLaravelSyntheticEdges(allFiles, index.edges, fileIdSet, rootDir);
233
+ if (changedLangs.has('csharp'))
234
+ await addCSharpSyntheticEdges(allFiles, index.edges, fileIdSet, rootDir);
235
+ if (changedLangs.has('python'))
236
+ await addDjangoSyntheticEdges(allFiles, index.edges, fileIdSet, rootDir);
237
+ // Sync the newly-added synthetic edges into the graph maps + recompute set.
238
+ for (let i = before; i < index.edges.length; i++) {
239
+ const e = index.edges[i];
240
+ const out = index.outEdges.get(e.from) ?? [];
241
+ if (!out.includes(e.to))
242
+ out.push(e.to);
243
+ index.outEdges.set(e.from, out);
244
+ if (!index.inEdges.has(e.to))
245
+ index.inEdges.set(e.to, []);
246
+ const inn = index.inEdges.get(e.to);
247
+ if (!inn.includes(e.from))
248
+ inn.push(e.from);
249
+ affectedIds.add(e.from);
250
+ affectedIds.add(e.to);
251
+ }
252
+ }
253
+ }
254
+ // -------------------------------------------------------------------------
255
+ // Phase 6: Recompute importedByCount for affected files + their neighbours
256
+ // -------------------------------------------------------------------------
257
+ const recomputeSet = new Set(affectedIds);
258
+ for (const fileId of affectedIds) {
259
+ for (const id of index.inEdges.get(fileId) ?? [])
260
+ recomputeSet.add(id);
261
+ for (const id of index.outEdges.get(fileId) ?? [])
262
+ recomputeSet.add(id);
263
+ }
264
+ for (const fileId of recomputeSet) {
265
+ const file = index.files.get(fileId);
266
+ if (file)
267
+ file.importedByCount = index.inEdges.get(fileId)?.length ?? 0;
268
+ }
269
+ // -------------------------------------------------------------------------
270
+ // Phase 7: Recompute transitiveImportedByCount (cheap full pass)
271
+ // -------------------------------------------------------------------------
272
+ for (const file of index.files.values()) {
273
+ file.transitiveImportedByCount = file.importedByCount;
274
+ }
275
+ for (const file of index.files.values()) {
276
+ if (!file.isBarrel)
277
+ continue;
278
+ for (const childId of index.outEdges.get(file.id) ?? []) {
279
+ const child = index.files.get(childId);
280
+ if (child)
281
+ child.transitiveImportedByCount += file.importedByCount;
282
+ }
283
+ }
284
+ // -------------------------------------------------------------------------
285
+ // Phase 8: Rebuild content-token postings (cheap full pass — df window 2–5
286
+ // means almost any edit can move tokens in or out of the postings)
287
+ // -------------------------------------------------------------------------
288
+ index.contentTokenPostings = buildContentTokenPostings(index.files.values());
289
+ index.indexedAt = Date.now();
290
+ }
291
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../../src/indexer/patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Edge, IndexedFile } from '../types.js';
2
+ /**
3
+ * Rails synthetic edges:
4
+ * - Constant references (Zeitwerk autoload): emitted per-AST-reference, resolved
5
+ * against the FQCN index built once from app/<category>/{,concerns/}/*.rb.
6
+ * - Controller/mailer ↔ view folder-pairing: pure path-regex matching the Rails
7
+ * convention. No AST, no render-call parsing. Captures relevance neighborhood
8
+ * (controller + all files in its view folder).
9
+ */
10
+ export declare function addRailsSyntheticEdges(indexedFiles: IndexedFile[], edges: Edge[], fullFileIdSet: Set<string>, rootDir: string): Promise<void>;
11
+ //# sourceMappingURL=rails-synthetic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rails-synthetic.d.ts","sourceRoot":"","sources":["../../src/indexer/rails-synthetic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,WAAW,EAAE,EAC3B,KAAK,EAAE,IAAI,EAAE,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,EAC1B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAyDf"}
@@ -0,0 +1,80 @@
1
+ import { buildRailsFqcnIndex, resolveRailsConstantCandidates } from './resolvers/ruby.js';
2
+ /**
3
+ * Rails synthetic edges:
4
+ * - Constant references (Zeitwerk autoload): emitted per-AST-reference, resolved
5
+ * against the FQCN index built once from app/<category>/{,concerns/}/*.rb.
6
+ * - Controller/mailer ↔ view folder-pairing: pure path-regex matching the Rails
7
+ * convention. No AST, no render-call parsing. Captures relevance neighborhood
8
+ * (controller + all files in its view folder).
9
+ */
10
+ export async function addRailsSyntheticEdges(indexedFiles, edges, fullFileIdSet, rootDir) {
11
+ const rubyFiles = indexedFiles.filter(f => f.language === 'ruby');
12
+ if (rubyFiles.length === 0)
13
+ return;
14
+ let appRoot = null;
15
+ for (const f of rubyFiles) {
16
+ const idx = f.path.lastIndexOf('/app/');
17
+ if (idx >= 0) {
18
+ appRoot = f.path.substring(0, idx);
19
+ break;
20
+ }
21
+ }
22
+ if (!appRoot)
23
+ return;
24
+ const fqcnIndex = await buildRailsFqcnIndex(appRoot, fullFileIdSet, rootDir);
25
+ const seen = new Set();
26
+ for (const e of edges)
27
+ seen.add(`${e.from}|${e.to}`);
28
+ for (const f of rubyFiles) {
29
+ if (!f.constantReferences?.length)
30
+ continue;
31
+ for (const candidates of f.constantReferences) {
32
+ const hit = resolveRailsConstantCandidates(candidates, fqcnIndex);
33
+ if (!hit || hit.fileId === f.id)
34
+ continue;
35
+ const key = `${f.id}|${hit.fileId}`;
36
+ if (seen.has(key))
37
+ continue;
38
+ seen.add(key);
39
+ edges.push({ from: f.id, to: hit.fileId, type: 'import', specifier: `const:${hit.fqcn}` });
40
+ }
41
+ }
42
+ // Folder-pairing: controller/mailer ↔ views in convention folder.
43
+ // app/controllers/<dir>/<name>_controller.rb → app/views/<dir>/<name>/*
44
+ // app/mailers/<name>_mailer.rb → app/views/<name>_mailer/*
45
+ const viewsByDir = new Map();
46
+ for (const id of fullFileIdSet) {
47
+ const m = id.match(/^(.*?)app\/views\/(.+)\/[^/]+$/);
48
+ if (!m)
49
+ continue;
50
+ const key = `${m[1]}::${m[2]}`;
51
+ let arr = viewsByDir.get(key);
52
+ if (!arr) {
53
+ arr = [];
54
+ viewsByDir.set(key, arr);
55
+ }
56
+ arr.push(id);
57
+ }
58
+ const folderRules = [
59
+ { re: /^(.*?)app\/controllers\/(.+)_controller\.rb$/, viewSuffix: '' },
60
+ { re: /^(.*?)app\/mailers\/(.+)_mailer\.rb$/, viewSuffix: '_mailer' },
61
+ ];
62
+ for (const f of indexedFiles) {
63
+ for (const rule of folderRules) {
64
+ const m = f.id.match(rule.re);
65
+ if (!m)
66
+ continue;
67
+ const targets = viewsByDir.get(`${m[1]}::${m[2]}${rule.viewSuffix}`);
68
+ if (!targets)
69
+ continue;
70
+ for (const targetId of targets) {
71
+ const key = `${f.id}|${targetId}`;
72
+ if (seen.has(key))
73
+ continue;
74
+ seen.add(key);
75
+ edges.push({ from: f.id, to: targetId, type: 'import', specifier: 'convention:views' });
76
+ }
77
+ }
78
+ }
79
+ }
80
+ //# sourceMappingURL=rails-synthetic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rails-synthetic.js","sourceRoot":"","sources":["../../src/indexer/rails-synthetic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { CodebaseIndex } from '../types.js';
2
+ export interface ReconcileResult {
3
+ /** Absolute paths — feed straight to patchIndex (it plans delete/update/skip itself). */
4
+ changed: Set<string>;
5
+ /** One line for the keeper log / repair log / status. */
6
+ reason: string;
7
+ }
8
+ export declare function reconcileChanges(index: CodebaseIndex, rootDir: string, excludes?: string[], includes?: string[]): Promise<ReconcileResult | null>;
9
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/indexer/reconcile.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,MAAM,WAAW,eAAe;IAC9B,yFAAyF;IACzF,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;CAChB;AAaD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,EAAO,EACvB,QAAQ,GAAE,MAAM,EAAO,GACtB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAkDjC"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Startup reconcile — answers "which files changed while no keeper was
3
+ * watching?" so a loaded cache can be PATCHED instead of discarded. This is
4
+ * the freshness mechanism that replaced the deleted cache TTL: validity is
5
+ * version + this reconcile + the keeper's live watcher.
6
+ *
7
+ * Two detectors, both always relevant:
8
+ *
9
+ * stale/deleted — a stat-walk over every indexed file, comparing live
10
+ * [mtimeMs, size] against the fingerprint stamped at parse time. Runs
11
+ * unconditionally (~100-200ms for 16k files): git alone would miss files
12
+ * that were indexed dirty and then reverted (clean tree, stale index),
13
+ * and gitignored-but-indexed files.
14
+ *
15
+ * new files — a stat-walk can't see files the index has never met. git
16
+ * (diff against the stored HEAD + porcelain for untracked) supplies them
17
+ * cheaply; non-git repos or git failure fall back to a directory walk.
18
+ *
19
+ * Known blind spot (accepted): an edit that preserves both mtime and size is
20
+ * invisible to the fingerprint, and a NEW gitignored file in a git repo is
21
+ * invisible to porcelain. The live watcher catches both from spawn onward.
22
+ *
23
+ * Returns null only when even the walk fails — caller does a full rebuild.
24
+ */
25
+ import { stat } from 'node:fs/promises';
26
+ import { join } from 'node:path';
27
+ import { getGitHead, getGitChangedFiles, getGitDirtyFiles } from './git.js';
28
+ import { walkDirectory } from './walker.js';
29
+ import { DEFAULT_EXCLUDES } from '../constants.js';
30
+ /** Live stat vs stored fingerprint. true = index no longer reflects this file. */
31
+ async function isStale(absPath, mtimeMs, sizeBytes) {
32
+ try {
33
+ const st = await stat(absPath);
34
+ if (mtimeMs === undefined || sizeBytes === undefined)
35
+ return true; // never stamped — re-parse to be safe
36
+ return st.mtimeMs !== mtimeMs || st.size !== sizeBytes;
37
+ }
38
+ catch {
39
+ return true; // gone — patch plans a delete
40
+ }
41
+ }
42
+ export async function reconcileChanges(index, rootDir, excludes = [], includes = []) {
43
+ const changed = new Set();
44
+ // ---- stale/deleted: fingerprint stat-walk over the indexed files ---------
45
+ for (const f of index.files.values()) {
46
+ if (await isStale(f.path, f.mtimeMs, f.sizeBytes))
47
+ changed.add(f.path);
48
+ }
49
+ const staleCount = changed.size;
50
+ // ---- new files: git fast path, directory-walk fallback -------------------
51
+ const indexed = new Set();
52
+ for (const f of index.files.values())
53
+ indexed.add(f.path);
54
+ let newSource;
55
+ const head = await getGitHead(rootDir);
56
+ const committed = head && index.gitHead && head !== index.gitHead
57
+ ? await getGitChangedFiles(rootDir, index.gitHead)
58
+ : head ? [] : null;
59
+ const dirty = head ? await getGitDirtyFiles(rootDir) : null;
60
+ if (committed !== null && dirty !== null) {
61
+ newSource = head === index.gitHead ? 'git-status' : 'git-diff+status';
62
+ for (const rel of [...committed, ...dirty]) {
63
+ // The walker never descends into hidden/excluded dirs — porcelain does
64
+ // (.claude/settings.json is a real untracked file). Without this filter
65
+ // every keeper start "discovers" it and pays a no-op patch + re-save.
66
+ const dirSegments = rel.split('/').slice(0, -1);
67
+ if (dirSegments.some((s) => s.startsWith('.') || DEFAULT_EXCLUDES.has(s) || excludes.includes(s)))
68
+ continue;
69
+ const abs = join(rootDir, rel);
70
+ if (indexed.has(abs) || changed.has(abs))
71
+ continue; // known file — stat-walk already judged it
72
+ try {
73
+ await stat(abs);
74
+ changed.add(abs); // exists but never indexed → new (patch filters non-indexable extensions)
75
+ }
76
+ catch { /* listed but gone and never indexed — nothing to do */ }
77
+ }
78
+ }
79
+ else {
80
+ newSource = 'fs-walk';
81
+ try {
82
+ const walked = await walkDirectory({ rootDir, excludes, includes });
83
+ for (const wf of walked) {
84
+ if (!indexed.has(wf.absolutePath))
85
+ changed.add(wf.absolutePath);
86
+ }
87
+ }
88
+ catch {
89
+ // Can't even enumerate the repo — no safe patch set exists.
90
+ return null;
91
+ }
92
+ }
93
+ const reason = `reconcile(${newSource}): ${staleCount} stale/deleted + ${changed.size - staleCount} new`;
94
+ return { changed, reason };
95
+ }
96
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/indexer/reconcile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * C++ resolver: handles both #include "path/to/file.h" and #include <path/to/file.h>.
3
+ *
4
+ * The extractor passes both quoted and angle-bracket includes (after filtering
5
+ * out stdlib/third-party), since modern CMake projects use angle-brackets for
6
+ * their own internal headers when they set up include roots via
7
+ * include_directories() / target_include_directories().
8
+ *
9
+ * Resolution order:
10
+ * 1. Relative to the including file's directory (for quoted includes)
11
+ * 2. For each include root discovered from CMakeLists.txt (walk-up from file)
12
+ * 3. Fallback to rootDir-relative
13
+ */
14
+ export declare function resolveCpp(specifier: string, fromFile: string, fileIdSet: Set<string>, rootDir: string, _aliasMap: Map<string, string[]>): Promise<string | null>;
15
+ //# sourceMappingURL=cpp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpp.d.ts","sourceRoot":"","sources":["../../../src/indexer/resolvers/cpp.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EACtB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB"}
@@ -0,0 +1,35 @@
1
+ import { dirname, join, resolve } from 'node:path';
2
+ import { tryResolveBase } from './shared.js';
3
+ import { getCppIncludeRoots } from '../cpp-include-roots.js';
4
+ /**
5
+ * C++ resolver: handles both #include "path/to/file.h" and #include <path/to/file.h>.
6
+ *
7
+ * The extractor passes both quoted and angle-bracket includes (after filtering
8
+ * out stdlib/third-party), since modern CMake projects use angle-brackets for
9
+ * their own internal headers when they set up include roots via
10
+ * include_directories() / target_include_directories().
11
+ *
12
+ * Resolution order:
13
+ * 1. Relative to the including file's directory (for quoted includes)
14
+ * 2. For each include root discovered from CMakeLists.txt (walk-up from file)
15
+ * 3. Fallback to rootDir-relative
16
+ */
17
+ export async function resolveCpp(specifier, fromFile, fileIdSet, rootDir, _aliasMap) {
18
+ // Try relative to the including file first
19
+ const fromDir = resolve(dirname(fromFile), specifier);
20
+ const result = tryResolveBase(fromDir, fileIdSet, rootDir);
21
+ if (result)
22
+ return result;
23
+ // Try each include root from CMakeLists.txt
24
+ const includeRoots = await getCppIncludeRoots(dirname(fromFile), rootDir);
25
+ for (const root of includeRoots) {
26
+ const candidate = join(root, specifier);
27
+ const resolved = tryResolveBase(candidate, fileIdSet, rootDir);
28
+ if (resolved)
29
+ return resolved;
30
+ }
31
+ // Fall back to rootDir-relative (project-wide include root)
32
+ const fromRoot = join(rootDir, specifier);
33
+ return tryResolveBase(fromRoot, fileIdSet, rootDir);
34
+ }
35
+ //# sourceMappingURL=cpp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpp.js","sourceRoot":"","sources":["../../../src/indexer/resolvers/cpp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,SAAiB,EACjB,QAAgB,EAChB,SAAsB,EACtB,OAAe,EACf,SAAgC;IAEhC,2CAA2C;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3D,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,4CAA4C;IAC5C,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1E,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;IAChC,CAAC;IAED,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1C,OAAO,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function resolveCSharp(specifier: string, _fromFile: string, fileIdSet: Set<string>, _rootDir: string, _aliasMap: Map<string, string[]>, pkgById?: Map<string, string>): Promise<string | null>;
2
+ //# sourceMappingURL=csharp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csharp.d.ts","sourceRoot":"","sources":["../../../src/indexer/resolvers/csharp.ts"],"names":[],"mappings":"AA4EA,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EACtB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAChC,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuBxB"}