@colbymchenry/codegraph 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/README.md +23 -7
  2. package/npm-shim.js +43 -0
  3. package/package.json +13 -51
  4. package/LICENSE +0 -21
  5. package/dist/bin/codegraph.d.ts +0 -21
  6. package/dist/bin/codegraph.d.ts.map +0 -1
  7. package/dist/bin/codegraph.js +0 -1257
  8. package/dist/bin/codegraph.js.map +0 -1
  9. package/dist/bin/node-version-check.d.ts +0 -20
  10. package/dist/bin/node-version-check.d.ts.map +0 -1
  11. package/dist/bin/node-version-check.js +0 -42
  12. package/dist/bin/node-version-check.js.map +0 -1
  13. package/dist/bin/uninstall.d.ts +0 -14
  14. package/dist/bin/uninstall.d.ts.map +0 -1
  15. package/dist/bin/uninstall.js +0 -36
  16. package/dist/bin/uninstall.js.map +0 -1
  17. package/dist/config.d.ts +0 -51
  18. package/dist/config.d.ts.map +0 -1
  19. package/dist/config.js +0 -321
  20. package/dist/config.js.map +0 -1
  21. package/dist/context/formatter.d.ts +0 -30
  22. package/dist/context/formatter.d.ts.map +0 -1
  23. package/dist/context/formatter.js +0 -244
  24. package/dist/context/formatter.js.map +0 -1
  25. package/dist/context/index.d.ts +0 -97
  26. package/dist/context/index.d.ts.map +0 -1
  27. package/dist/context/index.js +0 -1050
  28. package/dist/context/index.js.map +0 -1
  29. package/dist/db/index.d.ts +0 -72
  30. package/dist/db/index.d.ts.map +0 -1
  31. package/dist/db/index.js +0 -200
  32. package/dist/db/index.js.map +0 -1
  33. package/dist/db/migrations.d.ts +0 -44
  34. package/dist/db/migrations.d.ts.map +0 -1
  35. package/dist/db/migrations.js +0 -131
  36. package/dist/db/migrations.js.map +0 -1
  37. package/dist/db/queries.d.ts +0 -253
  38. package/dist/db/queries.d.ts.map +0 -1
  39. package/dist/db/queries.js +0 -1207
  40. package/dist/db/queries.js.map +0 -1
  41. package/dist/db/schema.sql +0 -151
  42. package/dist/db/sqlite-adapter.d.ts +0 -52
  43. package/dist/db/sqlite-adapter.d.ts.map +0 -1
  44. package/dist/db/sqlite-adapter.js +0 -237
  45. package/dist/db/sqlite-adapter.js.map +0 -1
  46. package/dist/directory.d.ts +0 -57
  47. package/dist/directory.d.ts.map +0 -1
  48. package/dist/directory.js +0 -264
  49. package/dist/directory.js.map +0 -1
  50. package/dist/errors.d.ts +0 -136
  51. package/dist/errors.d.ts.map +0 -1
  52. package/dist/errors.js +0 -219
  53. package/dist/errors.js.map +0 -1
  54. package/dist/extraction/dfm-extractor.d.ts +0 -31
  55. package/dist/extraction/dfm-extractor.d.ts.map +0 -1
  56. package/dist/extraction/dfm-extractor.js +0 -151
  57. package/dist/extraction/dfm-extractor.js.map +0 -1
  58. package/dist/extraction/grammars.d.ts +0 -78
  59. package/dist/extraction/grammars.d.ts.map +0 -1
  60. package/dist/extraction/grammars.js +0 -322
  61. package/dist/extraction/grammars.js.map +0 -1
  62. package/dist/extraction/index.d.ts +0 -130
  63. package/dist/extraction/index.d.ts.map +0 -1
  64. package/dist/extraction/index.js +0 -1305
  65. package/dist/extraction/index.js.map +0 -1
  66. package/dist/extraction/languages/c-cpp.d.ts +0 -4
  67. package/dist/extraction/languages/c-cpp.d.ts.map +0 -1
  68. package/dist/extraction/languages/c-cpp.js +0 -126
  69. package/dist/extraction/languages/c-cpp.js.map +0 -1
  70. package/dist/extraction/languages/csharp.d.ts +0 -3
  71. package/dist/extraction/languages/csharp.d.ts.map +0 -1
  72. package/dist/extraction/languages/csharp.js +0 -72
  73. package/dist/extraction/languages/csharp.js.map +0 -1
  74. package/dist/extraction/languages/dart.d.ts +0 -3
  75. package/dist/extraction/languages/dart.d.ts.map +0 -1
  76. package/dist/extraction/languages/dart.js +0 -192
  77. package/dist/extraction/languages/dart.js.map +0 -1
  78. package/dist/extraction/languages/go.d.ts +0 -3
  79. package/dist/extraction/languages/go.d.ts.map +0 -1
  80. package/dist/extraction/languages/go.js +0 -58
  81. package/dist/extraction/languages/go.js.map +0 -1
  82. package/dist/extraction/languages/index.d.ts +0 -10
  83. package/dist/extraction/languages/index.d.ts.map +0 -1
  84. package/dist/extraction/languages/index.js +0 -45
  85. package/dist/extraction/languages/index.js.map +0 -1
  86. package/dist/extraction/languages/java.d.ts +0 -3
  87. package/dist/extraction/languages/java.d.ts.map +0 -1
  88. package/dist/extraction/languages/java.js +0 -64
  89. package/dist/extraction/languages/java.js.map +0 -1
  90. package/dist/extraction/languages/javascript.d.ts +0 -3
  91. package/dist/extraction/languages/javascript.d.ts.map +0 -1
  92. package/dist/extraction/languages/javascript.js +0 -90
  93. package/dist/extraction/languages/javascript.js.map +0 -1
  94. package/dist/extraction/languages/kotlin.d.ts +0 -3
  95. package/dist/extraction/languages/kotlin.d.ts.map +0 -1
  96. package/dist/extraction/languages/kotlin.js +0 -253
  97. package/dist/extraction/languages/kotlin.js.map +0 -1
  98. package/dist/extraction/languages/pascal.d.ts +0 -3
  99. package/dist/extraction/languages/pascal.d.ts.map +0 -1
  100. package/dist/extraction/languages/pascal.js +0 -66
  101. package/dist/extraction/languages/pascal.js.map +0 -1
  102. package/dist/extraction/languages/php.d.ts +0 -3
  103. package/dist/extraction/languages/php.d.ts.map +0 -1
  104. package/dist/extraction/languages/php.js +0 -107
  105. package/dist/extraction/languages/php.js.map +0 -1
  106. package/dist/extraction/languages/python.d.ts +0 -3
  107. package/dist/extraction/languages/python.d.ts.map +0 -1
  108. package/dist/extraction/languages/python.js +0 -56
  109. package/dist/extraction/languages/python.js.map +0 -1
  110. package/dist/extraction/languages/ruby.d.ts +0 -3
  111. package/dist/extraction/languages/ruby.d.ts.map +0 -1
  112. package/dist/extraction/languages/ruby.js +0 -114
  113. package/dist/extraction/languages/ruby.js.map +0 -1
  114. package/dist/extraction/languages/rust.d.ts +0 -3
  115. package/dist/extraction/languages/rust.d.ts.map +0 -1
  116. package/dist/extraction/languages/rust.js +0 -109
  117. package/dist/extraction/languages/rust.js.map +0 -1
  118. package/dist/extraction/languages/scala.d.ts +0 -3
  119. package/dist/extraction/languages/scala.d.ts.map +0 -1
  120. package/dist/extraction/languages/scala.js +0 -139
  121. package/dist/extraction/languages/scala.js.map +0 -1
  122. package/dist/extraction/languages/swift.d.ts +0 -3
  123. package/dist/extraction/languages/swift.d.ts.map +0 -1
  124. package/dist/extraction/languages/swift.js +0 -91
  125. package/dist/extraction/languages/swift.js.map +0 -1
  126. package/dist/extraction/languages/typescript.d.ts +0 -3
  127. package/dist/extraction/languages/typescript.d.ts.map +0 -1
  128. package/dist/extraction/languages/typescript.js +0 -129
  129. package/dist/extraction/languages/typescript.js.map +0 -1
  130. package/dist/extraction/liquid-extractor.d.ts +0 -52
  131. package/dist/extraction/liquid-extractor.d.ts.map +0 -1
  132. package/dist/extraction/liquid-extractor.js +0 -313
  133. package/dist/extraction/liquid-extractor.js.map +0 -1
  134. package/dist/extraction/parse-worker.d.ts +0 -8
  135. package/dist/extraction/parse-worker.d.ts.map +0 -1
  136. package/dist/extraction/parse-worker.js +0 -94
  137. package/dist/extraction/parse-worker.js.map +0 -1
  138. package/dist/extraction/svelte-extractor.d.ts +0 -56
  139. package/dist/extraction/svelte-extractor.d.ts.map +0 -1
  140. package/dist/extraction/svelte-extractor.js +0 -272
  141. package/dist/extraction/svelte-extractor.js.map +0 -1
  142. package/dist/extraction/tree-sitter-helpers.d.ts +0 -28
  143. package/dist/extraction/tree-sitter-helpers.d.ts.map +0 -1
  144. package/dist/extraction/tree-sitter-helpers.js +0 -103
  145. package/dist/extraction/tree-sitter-helpers.js.map +0 -1
  146. package/dist/extraction/tree-sitter-types.d.ts +0 -179
  147. package/dist/extraction/tree-sitter-types.d.ts.map +0 -1
  148. package/dist/extraction/tree-sitter-types.js +0 -10
  149. package/dist/extraction/tree-sitter-types.js.map +0 -1
  150. package/dist/extraction/tree-sitter.d.ts +0 -233
  151. package/dist/extraction/tree-sitter.d.ts.map +0 -1
  152. package/dist/extraction/tree-sitter.js +0 -2393
  153. package/dist/extraction/tree-sitter.js.map +0 -1
  154. package/dist/extraction/vue-extractor.d.ts +0 -36
  155. package/dist/extraction/vue-extractor.d.ts.map +0 -1
  156. package/dist/extraction/vue-extractor.js +0 -163
  157. package/dist/extraction/vue-extractor.js.map +0 -1
  158. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  159. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  160. package/dist/graph/index.d.ts +0 -8
  161. package/dist/graph/index.d.ts.map +0 -1
  162. package/dist/graph/index.js +0 -13
  163. package/dist/graph/index.js.map +0 -1
  164. package/dist/graph/queries.d.ts +0 -106
  165. package/dist/graph/queries.d.ts.map +0 -1
  166. package/dist/graph/queries.js +0 -366
  167. package/dist/graph/queries.js.map +0 -1
  168. package/dist/graph/traversal.d.ts +0 -127
  169. package/dist/graph/traversal.d.ts.map +0 -1
  170. package/dist/graph/traversal.js +0 -493
  171. package/dist/graph/traversal.js.map +0 -1
  172. package/dist/index.d.ts +0 -447
  173. package/dist/index.d.ts.map +0 -1
  174. package/dist/index.js +0 -825
  175. package/dist/index.js.map +0 -1
  176. package/dist/installer/claude-md-template.d.ts +0 -14
  177. package/dist/installer/claude-md-template.d.ts.map +0 -1
  178. package/dist/installer/claude-md-template.js +0 -21
  179. package/dist/installer/claude-md-template.js.map +0 -1
  180. package/dist/installer/config-writer.d.ts +0 -29
  181. package/dist/installer/config-writer.d.ts.map +0 -1
  182. package/dist/installer/config-writer.js +0 -111
  183. package/dist/installer/config-writer.js.map +0 -1
  184. package/dist/installer/index.d.ts +0 -65
  185. package/dist/installer/index.d.ts.map +0 -1
  186. package/dist/installer/index.js +0 -406
  187. package/dist/installer/index.js.map +0 -1
  188. package/dist/installer/instructions-template.d.ts +0 -28
  189. package/dist/installer/instructions-template.d.ts.map +0 -1
  190. package/dist/installer/instructions-template.js +0 -64
  191. package/dist/installer/instructions-template.js.map +0 -1
  192. package/dist/installer/targets/claude.d.ts +0 -31
  193. package/dist/installer/targets/claude.d.ts.map +0 -1
  194. package/dist/installer/targets/claude.js +0 -308
  195. package/dist/installer/targets/claude.js.map +0 -1
  196. package/dist/installer/targets/codex.d.ts +0 -18
  197. package/dist/installer/targets/codex.d.ts.map +0 -1
  198. package/dist/installer/targets/codex.js +0 -185
  199. package/dist/installer/targets/codex.js.map +0 -1
  200. package/dist/installer/targets/cursor.d.ts +0 -35
  201. package/dist/installer/targets/cursor.d.ts.map +0 -1
  202. package/dist/installer/targets/cursor.js +0 -229
  203. package/dist/installer/targets/cursor.js.map +0 -1
  204. package/dist/installer/targets/opencode.d.ts +0 -30
  205. package/dist/installer/targets/opencode.d.ts.map +0 -1
  206. package/dist/installer/targets/opencode.js +0 -235
  207. package/dist/installer/targets/opencode.js.map +0 -1
  208. package/dist/installer/targets/registry.d.ts +0 -35
  209. package/dist/installer/targets/registry.d.ts.map +0 -1
  210. package/dist/installer/targets/registry.js +0 -83
  211. package/dist/installer/targets/registry.js.map +0 -1
  212. package/dist/installer/targets/shared.d.ts +0 -77
  213. package/dist/installer/targets/shared.d.ts.map +0 -1
  214. package/dist/installer/targets/shared.js +0 -246
  215. package/dist/installer/targets/shared.js.map +0 -1
  216. package/dist/installer/targets/toml.d.ts +0 -52
  217. package/dist/installer/targets/toml.d.ts.map +0 -1
  218. package/dist/installer/targets/toml.js +0 -147
  219. package/dist/installer/targets/toml.js.map +0 -1
  220. package/dist/installer/targets/types.d.ts +0 -116
  221. package/dist/installer/targets/types.d.ts.map +0 -1
  222. package/dist/installer/targets/types.js +0 -16
  223. package/dist/installer/targets/types.js.map +0 -1
  224. package/dist/mcp/index.d.ts +0 -94
  225. package/dist/mcp/index.d.ts.map +0 -1
  226. package/dist/mcp/index.js +0 -453
  227. package/dist/mcp/index.js.map +0 -1
  228. package/dist/mcp/server-instructions.d.ts +0 -19
  229. package/dist/mcp/server-instructions.d.ts.map +0 -1
  230. package/dist/mcp/server-instructions.js +0 -71
  231. package/dist/mcp/server-instructions.js.map +0 -1
  232. package/dist/mcp/tools.d.ts +0 -257
  233. package/dist/mcp/tools.d.ts.map +0 -1
  234. package/dist/mcp/tools.js +0 -1633
  235. package/dist/mcp/tools.js.map +0 -1
  236. package/dist/mcp/transport.d.ts +0 -106
  237. package/dist/mcp/transport.d.ts.map +0 -1
  238. package/dist/mcp/transport.js +0 -233
  239. package/dist/mcp/transport.js.map +0 -1
  240. package/dist/resolution/frameworks/cargo-workspace.d.ts +0 -18
  241. package/dist/resolution/frameworks/cargo-workspace.d.ts.map +0 -1
  242. package/dist/resolution/frameworks/cargo-workspace.js +0 -225
  243. package/dist/resolution/frameworks/cargo-workspace.js.map +0 -1
  244. package/dist/resolution/frameworks/csharp.d.ts +0 -8
  245. package/dist/resolution/frameworks/csharp.d.ts.map +0 -1
  246. package/dist/resolution/frameworks/csharp.js +0 -213
  247. package/dist/resolution/frameworks/csharp.js.map +0 -1
  248. package/dist/resolution/frameworks/express.d.ts +0 -8
  249. package/dist/resolution/frameworks/express.d.ts.map +0 -1
  250. package/dist/resolution/frameworks/express.js +0 -225
  251. package/dist/resolution/frameworks/express.js.map +0 -1
  252. package/dist/resolution/frameworks/go.d.ts +0 -8
  253. package/dist/resolution/frameworks/go.d.ts.map +0 -1
  254. package/dist/resolution/frameworks/go.js +0 -158
  255. package/dist/resolution/frameworks/go.js.map +0 -1
  256. package/dist/resolution/frameworks/index.d.ts +0 -42
  257. package/dist/resolution/frameworks/index.d.ts.map +0 -1
  258. package/dist/resolution/frameworks/index.js +0 -133
  259. package/dist/resolution/frameworks/index.js.map +0 -1
  260. package/dist/resolution/frameworks/java.d.ts +0 -8
  261. package/dist/resolution/frameworks/java.d.ts.map +0 -1
  262. package/dist/resolution/frameworks/java.js +0 -177
  263. package/dist/resolution/frameworks/java.js.map +0 -1
  264. package/dist/resolution/frameworks/laravel.d.ts +0 -13
  265. package/dist/resolution/frameworks/laravel.d.ts.map +0 -1
  266. package/dist/resolution/frameworks/laravel.js +0 -248
  267. package/dist/resolution/frameworks/laravel.js.map +0 -1
  268. package/dist/resolution/frameworks/nestjs.d.ts +0 -26
  269. package/dist/resolution/frameworks/nestjs.d.ts.map +0 -1
  270. package/dist/resolution/frameworks/nestjs.js +0 -374
  271. package/dist/resolution/frameworks/nestjs.js.map +0 -1
  272. package/dist/resolution/frameworks/python.d.ts +0 -10
  273. package/dist/resolution/frameworks/python.d.ts.map +0 -1
  274. package/dist/resolution/frameworks/python.js +0 -278
  275. package/dist/resolution/frameworks/python.js.map +0 -1
  276. package/dist/resolution/frameworks/react.d.ts +0 -8
  277. package/dist/resolution/frameworks/react.d.ts.map +0 -1
  278. package/dist/resolution/frameworks/react.js +0 -272
  279. package/dist/resolution/frameworks/react.js.map +0 -1
  280. package/dist/resolution/frameworks/ruby.d.ts +0 -8
  281. package/dist/resolution/frameworks/ruby.d.ts.map +0 -1
  282. package/dist/resolution/frameworks/ruby.js +0 -198
  283. package/dist/resolution/frameworks/ruby.js.map +0 -1
  284. package/dist/resolution/frameworks/rust.d.ts +0 -8
  285. package/dist/resolution/frameworks/rust.d.ts.map +0 -1
  286. package/dist/resolution/frameworks/rust.js +0 -207
  287. package/dist/resolution/frameworks/rust.js.map +0 -1
  288. package/dist/resolution/frameworks/svelte.d.ts +0 -9
  289. package/dist/resolution/frameworks/svelte.d.ts.map +0 -1
  290. package/dist/resolution/frameworks/svelte.js +0 -249
  291. package/dist/resolution/frameworks/svelte.js.map +0 -1
  292. package/dist/resolution/frameworks/swift.d.ts +0 -10
  293. package/dist/resolution/frameworks/swift.d.ts.map +0 -1
  294. package/dist/resolution/frameworks/swift.js +0 -376
  295. package/dist/resolution/frameworks/swift.js.map +0 -1
  296. package/dist/resolution/frameworks/vue.d.ts +0 -9
  297. package/dist/resolution/frameworks/vue.d.ts.map +0 -1
  298. package/dist/resolution/frameworks/vue.js +0 -306
  299. package/dist/resolution/frameworks/vue.js.map +0 -1
  300. package/dist/resolution/import-resolver.d.ts +0 -40
  301. package/dist/resolution/import-resolver.d.ts.map +0 -1
  302. package/dist/resolution/import-resolver.js +0 -663
  303. package/dist/resolution/import-resolver.js.map +0 -1
  304. package/dist/resolution/index.d.ts +0 -106
  305. package/dist/resolution/index.d.ts.map +0 -1
  306. package/dist/resolution/index.js +0 -709
  307. package/dist/resolution/index.js.map +0 -1
  308. package/dist/resolution/name-matcher.d.ts +0 -32
  309. package/dist/resolution/name-matcher.d.ts.map +0 -1
  310. package/dist/resolution/name-matcher.js +0 -384
  311. package/dist/resolution/name-matcher.js.map +0 -1
  312. package/dist/resolution/path-aliases.d.ts +0 -68
  313. package/dist/resolution/path-aliases.d.ts.map +0 -1
  314. package/dist/resolution/path-aliases.js +0 -238
  315. package/dist/resolution/path-aliases.js.map +0 -1
  316. package/dist/resolution/strip-comments.d.ts +0 -27
  317. package/dist/resolution/strip-comments.d.ts.map +0 -1
  318. package/dist/resolution/strip-comments.js +0 -441
  319. package/dist/resolution/strip-comments.js.map +0 -1
  320. package/dist/resolution/types.d.ts +0 -172
  321. package/dist/resolution/types.d.ts.map +0 -1
  322. package/dist/resolution/types.js +0 -8
  323. package/dist/resolution/types.js.map +0 -1
  324. package/dist/search/query-parser.d.ts +0 -57
  325. package/dist/search/query-parser.d.ts.map +0 -1
  326. package/dist/search/query-parser.js +0 -177
  327. package/dist/search/query-parser.js.map +0 -1
  328. package/dist/search/query-utils.d.ts +0 -53
  329. package/dist/search/query-utils.d.ts.map +0 -1
  330. package/dist/search/query-utils.js +0 -350
  331. package/dist/search/query-utils.js.map +0 -1
  332. package/dist/sync/git-hooks.d.ts +0 -45
  333. package/dist/sync/git-hooks.d.ts.map +0 -1
  334. package/dist/sync/git-hooks.js +0 -223
  335. package/dist/sync/git-hooks.js.map +0 -1
  336. package/dist/sync/index.d.ts +0 -17
  337. package/dist/sync/index.d.ts.map +0 -1
  338. package/dist/sync/index.js +0 -28
  339. package/dist/sync/index.js.map +0 -1
  340. package/dist/sync/watch-policy.d.ts +0 -48
  341. package/dist/sync/watch-policy.d.ts.map +0 -1
  342. package/dist/sync/watch-policy.js +0 -124
  343. package/dist/sync/watch-policy.js.map +0 -1
  344. package/dist/sync/watcher.d.ts +0 -81
  345. package/dist/sync/watcher.d.ts.map +0 -1
  346. package/dist/sync/watcher.js +0 -194
  347. package/dist/sync/watcher.js.map +0 -1
  348. package/dist/types.d.ts +0 -423
  349. package/dist/types.d.ts.map +0 -1
  350. package/dist/types.js +0 -256
  351. package/dist/types.js.map +0 -1
  352. package/dist/ui/glyphs.d.ts +0 -42
  353. package/dist/ui/glyphs.d.ts.map +0 -1
  354. package/dist/ui/glyphs.js +0 -78
  355. package/dist/ui/glyphs.js.map +0 -1
  356. package/dist/ui/shimmer-progress.d.ts +0 -11
  357. package/dist/ui/shimmer-progress.d.ts.map +0 -1
  358. package/dist/ui/shimmer-progress.js +0 -90
  359. package/dist/ui/shimmer-progress.js.map +0 -1
  360. package/dist/ui/shimmer-worker.d.ts +0 -2
  361. package/dist/ui/shimmer-worker.d.ts.map +0 -1
  362. package/dist/ui/shimmer-worker.js +0 -118
  363. package/dist/ui/shimmer-worker.js.map +0 -1
  364. package/dist/ui/types.d.ts +0 -17
  365. package/dist/ui/types.d.ts.map +0 -1
  366. package/dist/ui/types.js +0 -3
  367. package/dist/ui/types.js.map +0 -1
  368. package/dist/utils.d.ts +0 -205
  369. package/dist/utils.d.ts.map +0 -1
  370. package/dist/utils.js +0 -549
  371. package/dist/utils.js.map +0 -1
  372. package/scripts/agent-eval/audit.sh +0 -68
  373. package/scripts/agent-eval/itrun.sh +0 -107
  374. package/scripts/agent-eval/parse-run.mjs +0 -45
  375. package/scripts/agent-eval/parse-session.mjs +0 -93
  376. package/scripts/agent-eval/run-agent.sh +0 -34
  377. package/scripts/agent-eval/run-all.sh +0 -67
  378. package/scripts/extract-release-notes.mjs +0 -130
  379. package/scripts/local-install.sh +0 -41
  380. package/scripts/patch-tree-sitter-dart.js +0 -112
  381. package/scripts/release.sh +0 -68
@@ -1,1305 +0,0 @@
1
- "use strict";
2
- /**
3
- * Extraction Orchestrator
4
- *
5
- * Coordinates file scanning, parsing, and database storage.
6
- */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
- var __importDefault = (this && this.__importDefault) || function (mod) {
41
- return (mod && mod.__esModule) ? mod : { "default": mod };
42
- };
43
- Object.defineProperty(exports, "__esModule", { value: true });
44
- exports.loadAllGrammars = exports.loadGrammarsForLanguages = exports.initGrammars = exports.getSupportedLanguages = exports.isGrammarLoaded = exports.isLanguageSupported = exports.detectLanguage = exports.extractFromSource = exports.ExtractionOrchestrator = void 0;
45
- exports.hashContent = hashContent;
46
- exports.shouldIncludeFile = shouldIncludeFile;
47
- exports.scanDirectory = scanDirectory;
48
- exports.scanDirectoryAsync = scanDirectoryAsync;
49
- const fs = __importStar(require("fs"));
50
- const fsp = __importStar(require("fs/promises"));
51
- const path = __importStar(require("path"));
52
- const crypto = __importStar(require("crypto"));
53
- const child_process_1 = require("child_process");
54
- const tree_sitter_1 = require("./tree-sitter");
55
- const grammars_1 = require("./grammars");
56
- const errors_1 = require("../errors");
57
- const utils_1 = require("../utils");
58
- const picomatch_1 = __importDefault(require("picomatch"));
59
- const frameworks_1 = require("../resolution/frameworks");
60
- /**
61
- * Number of files to read in parallel during indexing.
62
- * File reads are I/O-bound; batching overlaps I/O wait with CPU parse work.
63
- */
64
- const FILE_IO_BATCH_SIZE = 10;
65
- // PARSER_RESET_INTERVAL moved to parse-worker.ts (runs in worker thread)
66
- /**
67
- * Maximum time (ms) to wait for a single file to parse in the worker thread.
68
- * If tree-sitter hangs or WASM runs out of memory, this prevents the entire
69
- * indexing run from freezing. The worker is restarted after a timeout.
70
- */
71
- const PARSE_TIMEOUT_MS = 10_000;
72
- /**
73
- * Number of files to parse before recycling the worker thread.
74
- * WASM linear memory can grow but NEVER shrink (WebAssembly spec limitation).
75
- * The only way to reclaim tree-sitter's WASM heap is to destroy the entire
76
- * V8 isolate by terminating the worker thread and spawning a fresh one.
77
- * This interval balances memory usage against the cost of reloading grammars.
78
- */
79
- const WORKER_RECYCLE_INTERVAL = 250;
80
- /**
81
- * Calculate SHA256 hash of file contents
82
- */
83
- function hashContent(content) {
84
- return crypto.createHash('sha256').update(content).digest('hex');
85
- }
86
- /**
87
- * Check if a path matches any glob pattern (simplified)
88
- */
89
- function matchesGlob(filePath, pattern) {
90
- filePath = (0, utils_1.normalizePath)(filePath);
91
- return picomatch_1.default.isMatch(filePath, pattern, { dot: true });
92
- }
93
- /**
94
- * Check if a file should be included based on config
95
- */
96
- function shouldIncludeFile(filePath, config) {
97
- // Check exclude patterns first
98
- for (const pattern of config.exclude) {
99
- if (matchesGlob(filePath, pattern)) {
100
- return false;
101
- }
102
- }
103
- // Check include patterns
104
- for (const pattern of config.include) {
105
- if (matchesGlob(filePath, pattern)) {
106
- return true;
107
- }
108
- }
109
- return false;
110
- }
111
- /**
112
- * Collect git-visible files (tracked + untracked, .gitignore-respected) from the
113
- * git repository rooted at `repoDir`, adding each to `files` with `prefix`
114
- * prepended so paths stay relative to the original scan root.
115
- *
116
- * Recurses into embedded git repositories — nested repos that are NOT submodules
117
- * (independent clones living inside the workspace, common in CMake "super-repo"
118
- * layouts). The parent repo's `git ls-files` cannot see into them: tracked output
119
- * skips them entirely, and untracked output reports them only as an opaque
120
- * "subdir/" entry (trailing slash) rather than expanding their files. Each
121
- * embedded repo is its own git boundary, so we re-run `git ls-files` inside it.
122
- * (See issue #193.)
123
- */
124
- function collectGitFiles(repoDir, prefix, files) {
125
- const gitOpts = { cwd: repoDir, encoding: 'utf-8', timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] };
126
- // Tracked files. --recurse-submodules pulls in files from active submodules,
127
- // which the index would otherwise represent only as a commit pointer.
128
- // Without this, monorepos using submodules index 0 files. (See issue #147.)
129
- // Note: --recurse-submodules only supports -c/--cached and --stage modes — it
130
- // can't be combined with -o, so untracked files are gathered separately below.
131
- const tracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-c', '--recurse-submodules'], gitOpts);
132
- for (const line of tracked.split('\n')) {
133
- const trimmed = line.trim();
134
- if (trimmed) {
135
- files.add((0, utils_1.normalizePath)(prefix + trimmed));
136
- }
137
- }
138
- // Untracked files (submodules manage their own untracked state). Embedded git
139
- // repos surface here as a single "subdir/" entry that git refuses to descend
140
- // into — recurse into those as their own repos so their source gets indexed.
141
- const untracked = (0, child_process_1.execFileSync)('git', ['ls-files', '-o', '--exclude-standard'], gitOpts);
142
- for (const line of untracked.split('\n')) {
143
- const trimmed = line.trim();
144
- if (!trimmed)
145
- continue;
146
- if (trimmed.endsWith('/')) {
147
- // git only emits a trailing-slash directory entry for an embedded repo.
148
- // Guard with a .git check anyway, and skip anything else exactly as git
149
- // itself skips it (we never descend into a non-repo opaque dir).
150
- const childDir = path.join(repoDir, trimmed);
151
- if (fs.existsSync(path.join(childDir, '.git'))) {
152
- collectGitFiles(childDir, prefix + trimmed, files);
153
- }
154
- continue;
155
- }
156
- files.add((0, utils_1.normalizePath)(prefix + trimmed));
157
- }
158
- }
159
- /**
160
- * Get all files visible to git (tracked + untracked but not ignored).
161
- * Respects .gitignore at all levels (root, subdirectories) and descends into
162
- * embedded (nested, non-submodule) git repos. Returns null on failure
163
- * (non-git project) so callers can fall back to a filesystem walk.
164
- */
165
- function getGitVisibleFiles(rootDir) {
166
- try {
167
- // Check if the project directory is gitignored by a parent repo.
168
- // When rootDir lives inside a parent git repo that ignores it,
169
- // `git ls-files` returns nothing — fall back to filesystem walk.
170
- const gitRoot = (0, child_process_1.execFileSync)('git', ['rev-parse', '--show-toplevel'], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
171
- if (path.resolve(gitRoot) !== path.resolve(rootDir)) {
172
- try {
173
- // git check-ignore exits 0 if the path IS ignored, 1 if not
174
- (0, child_process_1.execFileSync)('git', ['check-ignore', '-q', path.resolve(rootDir)], { cwd: rootDir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
175
- // Directory is gitignored by parent repo — fall back to filesystem walk
176
- return null;
177
- }
178
- catch {
179
- // Not ignored — safe to use git ls-files
180
- }
181
- }
182
- const files = new Set();
183
- collectGitFiles(rootDir, '', files);
184
- return files;
185
- }
186
- catch {
187
- return null;
188
- }
189
- }
190
- /**
191
- * Use `git status` to detect changed files instead of scanning every file.
192
- * Returns null on failure so callers fall back to full scan.
193
- */
194
- function getGitChangedFiles(rootDir, config) {
195
- try {
196
- const output = (0, child_process_1.execFileSync)('git', ['status', '--porcelain', '--no-renames'], { cwd: rootDir, encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] });
197
- const modified = [];
198
- const added = [];
199
- const deleted = [];
200
- for (const line of output.split('\n')) {
201
- if (line.length < 4)
202
- continue; // Minimum: "XY file"
203
- const statusCode = line.substring(0, 2);
204
- const filePath = (0, utils_1.normalizePath)(line.substring(3));
205
- // Skip files that don't match include/exclude config
206
- if (!shouldIncludeFile(filePath, config))
207
- continue;
208
- if (statusCode === '??') {
209
- added.push(filePath);
210
- }
211
- else if (statusCode.includes('D')) {
212
- deleted.push(filePath);
213
- }
214
- else {
215
- // M, MM, AM, A (staged), etc. — treat as modified
216
- modified.push(filePath);
217
- }
218
- }
219
- return { modified, added, deleted };
220
- }
221
- catch {
222
- return null;
223
- }
224
- }
225
- /**
226
- * Marker file name that indicates a directory (and all children) should be skipped
227
- */
228
- const CODEGRAPH_IGNORE_MARKER = '.codegraphignore';
229
- /**
230
- * Recursively scan directory for source files.
231
- *
232
- * In git repos, uses `git ls-files` to get the file list (inherently
233
- * respects .gitignore at all levels), then filters by config include patterns.
234
- * Falls back to filesystem walk for non-git projects.
235
- */
236
- function scanDirectory(rootDir, config, onProgress) {
237
- // Fast path: use git to get all visible files (respects .gitignore everywhere)
238
- const gitFiles = getGitVisibleFiles(rootDir);
239
- if (gitFiles) {
240
- const files = [];
241
- let count = 0;
242
- for (const filePath of gitFiles) {
243
- if (shouldIncludeFile(filePath, config)) {
244
- files.push(filePath);
245
- count++;
246
- onProgress?.(count, filePath);
247
- }
248
- }
249
- return files;
250
- }
251
- // Fallback: walk filesystem for non-git projects
252
- return scanDirectoryWalk(rootDir, config, onProgress);
253
- }
254
- /**
255
- * Async variant of scanDirectory that yields to the event loop periodically,
256
- * allowing worker threads to receive and render progress messages.
257
- */
258
- async function scanDirectoryAsync(rootDir, config, onProgress) {
259
- const gitFiles = getGitVisibleFiles(rootDir);
260
- if (gitFiles) {
261
- const files = [];
262
- let count = 0;
263
- for (const filePath of gitFiles) {
264
- if (shouldIncludeFile(filePath, config)) {
265
- files.push(filePath);
266
- count++;
267
- onProgress?.(count, filePath);
268
- // Yield every 100 files so worker threads can render progress
269
- if (count % 100 === 0) {
270
- await new Promise(r => setImmediate(r));
271
- }
272
- }
273
- }
274
- return files;
275
- }
276
- return scanDirectoryWalk(rootDir, config, onProgress);
277
- }
278
- /**
279
- * Filesystem walk fallback for non-git projects.
280
- */
281
- function scanDirectoryWalk(rootDir, config, onProgress) {
282
- const files = [];
283
- let count = 0;
284
- const visitedDirs = new Set();
285
- function walk(dir) {
286
- let realDir;
287
- try {
288
- realDir = fs.realpathSync(dir);
289
- }
290
- catch {
291
- (0, errors_1.logDebug)('Skipping unresolvable directory', { dir });
292
- return;
293
- }
294
- if (visitedDirs.has(realDir)) {
295
- (0, errors_1.logDebug)('Skipping already-visited directory (symlink cycle)', { dir, realDir });
296
- return;
297
- }
298
- visitedDirs.add(realDir);
299
- // Check for .codegraphignore marker file
300
- const ignoreMarker = path.join(dir, CODEGRAPH_IGNORE_MARKER);
301
- if (fs.existsSync(ignoreMarker)) {
302
- (0, errors_1.logDebug)('Skipping directory due to .codegraphignore marker', { dir });
303
- return;
304
- }
305
- let entries;
306
- try {
307
- entries = fs.readdirSync(dir, { withFileTypes: true });
308
- }
309
- catch (error) {
310
- (0, errors_1.logDebug)('Skipping unreadable directory', { dir, error: String(error) });
311
- return;
312
- }
313
- for (const entry of entries) {
314
- const fullPath = path.join(dir, entry.name);
315
- const relativePath = (0, utils_1.normalizePath)(path.relative(rootDir, fullPath));
316
- if (entry.isSymbolicLink()) {
317
- try {
318
- const realTarget = fs.realpathSync(fullPath);
319
- const stat = fs.statSync(realTarget);
320
- if (stat.isDirectory()) {
321
- const dirPattern = relativePath + '/';
322
- let excluded = false;
323
- for (const pattern of config.exclude) {
324
- if (matchesGlob(dirPattern, pattern) || matchesGlob(relativePath, pattern)) {
325
- excluded = true;
326
- break;
327
- }
328
- }
329
- if (!excluded) {
330
- walk(fullPath);
331
- }
332
- }
333
- else if (stat.isFile()) {
334
- if (shouldIncludeFile(relativePath, config)) {
335
- files.push(relativePath);
336
- count++;
337
- onProgress?.(count, relativePath);
338
- }
339
- }
340
- }
341
- catch {
342
- (0, errors_1.logDebug)('Skipping broken symlink', { path: fullPath });
343
- }
344
- continue;
345
- }
346
- if (entry.isDirectory()) {
347
- const dirPattern = relativePath + '/';
348
- let excluded = false;
349
- for (const pattern of config.exclude) {
350
- if (matchesGlob(dirPattern, pattern) || matchesGlob(relativePath, pattern)) {
351
- excluded = true;
352
- break;
353
- }
354
- }
355
- if (!excluded) {
356
- walk(fullPath);
357
- }
358
- }
359
- else if (entry.isFile()) {
360
- if (shouldIncludeFile(relativePath, config)) {
361
- files.push(relativePath);
362
- count++;
363
- onProgress?.(count, relativePath);
364
- }
365
- }
366
- }
367
- }
368
- walk(rootDir);
369
- return files;
370
- }
371
- /**
372
- * Extraction orchestrator
373
- */
374
- class ExtractionOrchestrator {
375
- rootDir;
376
- config;
377
- queries;
378
- /**
379
- * Names of frameworks detected for this project, populated by indexAll().
380
- * Passed to extractFromSource so framework-specific extractors (route nodes,
381
- * middleware, etc.) run after the tree-sitter pass. Cleared if detection
382
- * hasn't run yet so single-file re-index paths can detect on the spot.
383
- */
384
- detectedFrameworkNames = null;
385
- constructor(rootDir, config, queries) {
386
- this.rootDir = rootDir;
387
- this.config = config;
388
- this.queries = queries;
389
- }
390
- /**
391
- * Build a filesystem-backed ResolutionContext sufficient for framework
392
- * detection. Graph-query methods (getNodesByName etc.) return empty because
393
- * the DB hasn't been populated yet, but detect() only uses readFile,
394
- * fileExists, and getAllFiles, so that's fine.
395
- */
396
- buildDetectionContext(files) {
397
- const rootDir = this.rootDir;
398
- return {
399
- getNodesInFile: () => [],
400
- getNodesByName: () => [],
401
- getNodesByQualifiedName: () => [],
402
- getNodesByKind: () => [],
403
- getNodesByLowerName: () => [],
404
- getImportMappings: () => [],
405
- getAllFiles: () => files,
406
- getProjectRoot: () => rootDir,
407
- fileExists: (relativePath) => {
408
- const full = (0, utils_1.validatePathWithinRoot)(rootDir, relativePath);
409
- if (!full)
410
- return false;
411
- try {
412
- return fs.existsSync(full);
413
- }
414
- catch {
415
- return false;
416
- }
417
- },
418
- readFile: (relativePath) => {
419
- const full = (0, utils_1.validatePathWithinRoot)(rootDir, relativePath);
420
- if (!full)
421
- return null;
422
- try {
423
- return fs.readFileSync(full, 'utf-8');
424
- }
425
- catch {
426
- return null;
427
- }
428
- },
429
- };
430
- }
431
- /**
432
- * Detect frameworks on demand using the current scanned files (or a fresh
433
- * scan if none are provided). Cached on the orchestrator so repeat calls
434
- * inside a single run don't re-scan.
435
- */
436
- ensureDetectedFrameworks(files) {
437
- if (this.detectedFrameworkNames !== null)
438
- return this.detectedFrameworkNames;
439
- const fileList = files ?? scanDirectory(this.rootDir, this.config);
440
- const context = this.buildDetectionContext(fileList);
441
- this.detectedFrameworkNames = (0, frameworks_1.detectFrameworks)(context).map((r) => r.name);
442
- return this.detectedFrameworkNames;
443
- }
444
- /**
445
- * Index all files in the project
446
- */
447
- async indexAll(onProgress, signal, verbose) {
448
- await (0, grammars_1.initGrammars)();
449
- const startTime = Date.now();
450
- const errors = [];
451
- let filesIndexed = 0;
452
- let filesSkipped = 0;
453
- let filesErrored = 0;
454
- let totalNodes = 0;
455
- let totalEdges = 0;
456
- const log = verbose
457
- ? (msg) => { console.log(`[worker] ${msg}`); }
458
- : (_msg) => { };
459
- // Phase 1: Scan for files
460
- onProgress?.({
461
- phase: 'scanning',
462
- current: 0,
463
- total: 0,
464
- });
465
- const files = await scanDirectoryAsync(this.rootDir, this.config, (current, file) => {
466
- onProgress?.({
467
- phase: 'scanning',
468
- current,
469
- total: 0,
470
- currentFile: file,
471
- });
472
- });
473
- // Detect frameworks once per indexAll run using the scanned file list.
474
- // Names are passed to each parse call so framework-specific extractors
475
- // (route nodes, middleware, etc.) run after the tree-sitter pass.
476
- // Framework detection is reset each run so adding e.g. requirements.txt
477
- // between runs is picked up without restarting the process.
478
- this.detectedFrameworkNames = null;
479
- const frameworkNames = this.ensureDetectedFrameworks(files);
480
- if (signal?.aborted) {
481
- return {
482
- success: false,
483
- filesIndexed: 0,
484
- filesSkipped: 0,
485
- filesErrored: 0,
486
- nodesCreated: 0,
487
- edgesCreated: 0,
488
- errors: [{ message: 'Aborted', severity: 'error' }],
489
- durationMs: Date.now() - startTime,
490
- };
491
- }
492
- // Phase 2: Parse files in a worker thread (keeps main thread unblocked for UI)
493
- const total = files.length;
494
- let processed = 0;
495
- // Emit parsing phase immediately so the progress bar appears during worker setup.
496
- // The yield lets the shimmer worker flush the phase transition to stdout before
497
- // the main thread starts synchronous grammar detection work.
498
- onProgress?.({
499
- phase: 'parsing',
500
- current: 0,
501
- total,
502
- });
503
- await new Promise(resolve => setImmediate(resolve));
504
- // Detect needed languages and load grammars in the parse worker
505
- const neededLanguages = [...new Set(files.map((f) => (0, grammars_1.detectLanguage)(f)))];
506
- // .h files default to 'c' but may be C++ — ensure cpp grammar is loaded when c is needed
507
- if (neededLanguages.includes('c') && !neededLanguages.includes('cpp')) {
508
- neededLanguages.push('cpp');
509
- }
510
- // Try to use a worker thread for parsing (keeps main thread unblocked for UI).
511
- // Falls back to in-process parsing if the compiled worker is unavailable (e.g. tests).
512
- const parseWorkerPath = path.join(__dirname, 'parse-worker.js');
513
- const useWorker = fs.existsSync(parseWorkerPath);
514
- let WorkerClass = null;
515
- if (useWorker) {
516
- const { Worker } = await Promise.resolve().then(() => __importStar(require('worker_threads')));
517
- WorkerClass = Worker;
518
- }
519
- else {
520
- // In-process fallback: load grammars locally
521
- await (0, grammars_1.loadGrammarsForLanguages)(neededLanguages);
522
- }
523
- // --- Worker lifecycle management ---
524
- // The worker can crash (OOM in WASM) or hang on pathological files.
525
- // We track pending parse promises and handle both cases:
526
- // - Timeout: terminate + restart the worker, reject the timed-out request
527
- // - Crash: reject all pending promises, restart for remaining files
528
- let parseWorker = null;
529
- let nextId = 0;
530
- let workerParseCount = 0;
531
- const pendingParses = new Map();
532
- function rejectAllPending(reason) {
533
- for (const [id, pending] of pendingParses) {
534
- clearTimeout(pending.timer);
535
- pendingParses.delete(id);
536
- pending.reject(new Error(reason));
537
- }
538
- }
539
- function attachWorkerHandlers(w) {
540
- w.on('message', (msg) => {
541
- if (msg.type === 'parse-result' && msg.id !== undefined) {
542
- const pending = pendingParses.get(msg.id);
543
- if (pending) {
544
- clearTimeout(pending.timer);
545
- pendingParses.delete(msg.id);
546
- pending.resolve(msg.result);
547
- }
548
- }
549
- });
550
- w.on('error', (err) => {
551
- (0, errors_1.logWarn)('Parse worker error', { error: err.message });
552
- rejectAllPending(`Worker error: ${err.message}`);
553
- });
554
- w.on('exit', (code) => {
555
- if (code !== 0 && pendingParses.size > 0) {
556
- (0, errors_1.logWarn)('Parse worker exited unexpectedly', { code });
557
- rejectAllPending(`Worker exited with code ${code}`);
558
- }
559
- // Clear reference so we know to respawn, reset count so
560
- // the fresh worker gets a full cycle before recycling.
561
- if (parseWorker === w) {
562
- parseWorker = null;
563
- workerParseCount = 0;
564
- }
565
- });
566
- }
567
- async function ensureWorker() {
568
- if (parseWorker)
569
- return parseWorker;
570
- log('Spawning new parse worker...');
571
- parseWorker = new WorkerClass(parseWorkerPath);
572
- attachWorkerHandlers(parseWorker);
573
- // Load grammars in the new worker
574
- await new Promise((resolve, reject) => {
575
- parseWorker.once('message', (msg) => {
576
- if (msg.type === 'grammars-loaded')
577
- resolve();
578
- else
579
- reject(new Error(`Unexpected message: ${msg.type}`));
580
- });
581
- parseWorker.postMessage({ type: 'load-grammars', languages: neededLanguages });
582
- });
583
- return parseWorker;
584
- }
585
- if (WorkerClass) {
586
- await ensureWorker();
587
- }
588
- /**
589
- * Recycle the worker thread to reclaim WASM memory.
590
- * Terminates the current worker and clears the reference so
591
- * ensureWorker() will spawn a fresh one on the next call.
592
- */
593
- function recycleWorker() {
594
- if (!parseWorker)
595
- return;
596
- log(`Recycling worker after ${workerParseCount} parses (heap: ${Math.round(process.memoryUsage().rss / 1024 / 1024)}MB RSS)`);
597
- const w = parseWorker;
598
- parseWorker = null;
599
- workerParseCount = 0;
600
- // Fire-and-forget: worker.terminate() can hang if WASM is stuck
601
- w.terminate().catch(() => { });
602
- }
603
- async function requestParse(filePath, content) {
604
- if (!WorkerClass) {
605
- // In-process fallback
606
- return (0, tree_sitter_1.extractFromSource)(filePath, content, (0, grammars_1.detectLanguage)(filePath, content), frameworkNames);
607
- }
608
- // Recycle the worker before the next parse if we've hit the threshold.
609
- // This destroys the WASM linear memory (which can grow but never shrink)
610
- // and starts a fresh worker with a clean heap.
611
- if (workerParseCount >= WORKER_RECYCLE_INTERVAL) {
612
- await recycleWorker();
613
- }
614
- const worker = await ensureWorker();
615
- const id = nextId++;
616
- workerParseCount++;
617
- // Scale timeout for large files: base 10s + 10s per 100KB
618
- const timeoutMs = PARSE_TIMEOUT_MS + Math.floor(content.length / 100_000) * 10_000;
619
- return new Promise((resolve, reject) => {
620
- const timer = setTimeout(() => {
621
- pendingParses.delete(id);
622
- log(`TIMEOUT: ${filePath} exceeded ${timeoutMs}ms — killing worker`);
623
- // Reject FIRST — worker.terminate() can hang if WASM is stuck
624
- parseWorker = null;
625
- workerParseCount = 0;
626
- reject(new Error(`Parse timed out after ${timeoutMs}ms`));
627
- // Fire-and-forget: kill the stuck worker in the background
628
- worker.terminate().catch(() => { });
629
- }, timeoutMs);
630
- pendingParses.set(id, { resolve, reject, timer });
631
- worker.postMessage({ type: 'parse', id, filePath, content, frameworkNames });
632
- });
633
- }
634
- for (let i = 0; i < files.length; i += FILE_IO_BATCH_SIZE) {
635
- if (signal?.aborted) {
636
- if (parseWorker)
637
- parseWorker.terminate().catch(() => { });
638
- return {
639
- success: false,
640
- filesIndexed,
641
- filesSkipped,
642
- filesErrored,
643
- nodesCreated: totalNodes,
644
- edgesCreated: totalEdges,
645
- errors: [{ message: 'Aborted', severity: 'error' }, ...errors],
646
- durationMs: Date.now() - startTime,
647
- };
648
- }
649
- const batch = files.slice(i, i + FILE_IO_BATCH_SIZE);
650
- // Read files in parallel (with path validation before any I/O)
651
- const fileContents = await Promise.all(batch.map(async (fp) => {
652
- try {
653
- const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, fp);
654
- if (!fullPath) {
655
- (0, errors_1.logWarn)('Path traversal blocked in batch reader', { filePath: fp });
656
- return { filePath: fp, content: null, stats: null, error: new Error('Path traversal blocked') };
657
- }
658
- const content = await fsp.readFile(fullPath, 'utf-8');
659
- const stats = await fsp.stat(fullPath);
660
- return { filePath: fp, content, stats, error: null };
661
- }
662
- catch (err) {
663
- return { filePath: fp, content: null, stats: null, error: err };
664
- }
665
- }));
666
- // Send to worker for parsing, store results on main thread
667
- for (const { filePath, content, stats, error } of fileContents) {
668
- if (signal?.aborted) {
669
- if (parseWorker)
670
- parseWorker.terminate().catch(() => { });
671
- return {
672
- success: false,
673
- filesIndexed,
674
- filesSkipped,
675
- filesErrored,
676
- nodesCreated: totalNodes,
677
- edgesCreated: totalEdges,
678
- errors: [{ message: 'Aborted', severity: 'error' }, ...errors],
679
- durationMs: Date.now() - startTime,
680
- };
681
- }
682
- // Report progress before parsing (show current file being worked on)
683
- onProgress?.({
684
- phase: 'parsing',
685
- current: processed,
686
- total,
687
- currentFile: filePath,
688
- });
689
- if (error || content === null || stats === null) {
690
- processed++;
691
- filesErrored++;
692
- errors.push({
693
- message: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
694
- filePath,
695
- severity: 'error',
696
- code: 'read_error',
697
- });
698
- continue;
699
- }
700
- // Honour config.maxFileSize. Without this check, vendored
701
- // generated headers, minified bundles, and other multi-MB
702
- // files get indexed despite the user setting a size cap —
703
- // wasting WASM heap and the worker recycle budget on inputs
704
- // the user explicitly opted out of. The single-file extractFile
705
- // path already enforces this; the bulk path used to silently
706
- // skip the check.
707
- if (stats.size > this.config.maxFileSize) {
708
- processed++;
709
- filesSkipped++;
710
- errors.push({
711
- message: `File exceeds max size (${stats.size} > ${this.config.maxFileSize})`,
712
- filePath,
713
- severity: 'warning',
714
- code: 'size_exceeded',
715
- });
716
- onProgress?.({ phase: 'parsing', current: processed, total });
717
- continue;
718
- }
719
- // Parse in worker thread (main thread stays unblocked).
720
- // Wrapped in try/catch to handle worker timeouts and crashes gracefully.
721
- let result;
722
- try {
723
- result = await requestParse(filePath, content);
724
- }
725
- catch (parseErr) {
726
- processed++;
727
- filesErrored++;
728
- errors.push({
729
- message: parseErr instanceof Error ? parseErr.message : String(parseErr),
730
- filePath,
731
- severity: 'error',
732
- code: 'parse_error',
733
- });
734
- continue;
735
- }
736
- processed++;
737
- // Store in database on main thread (SQLite is not thread-safe)
738
- if (result.nodes.length > 0 || result.errors.length === 0) {
739
- const language = (0, grammars_1.detectLanguage)(filePath, content);
740
- this.storeExtractionResult(filePath, content, language, stats, result);
741
- }
742
- if (result.errors.length > 0) {
743
- for (const err of result.errors) {
744
- if (!err.filePath)
745
- err.filePath = filePath;
746
- }
747
- errors.push(...result.errors);
748
- }
749
- if (result.nodes.length > 0) {
750
- filesIndexed++;
751
- totalNodes += result.nodes.length;
752
- totalEdges += result.edges.length;
753
- }
754
- else if (result.errors.some((e) => e.severity === 'error')) {
755
- filesErrored++;
756
- }
757
- else {
758
- filesSkipped++;
759
- }
760
- }
761
- }
762
- // Report 100% so the progress bar doesn't hang at 99%
763
- onProgress?.({
764
- phase: 'parsing',
765
- current: total,
766
- total,
767
- });
768
- // Yield so the shimmer worker's buffered stdout writes can flush.
769
- // Worker thread stdout is proxied through the main thread's event loop,
770
- // so synchronous work here blocks the animation from rendering.
771
- await new Promise(resolve => setImmediate(resolve));
772
- // Retry pass: files that failed due to WASM memory corruption may succeed
773
- // on a fresh worker with a clean heap. Recycle before each attempt so
774
- // every file gets the absolute cleanest WASM state possible.
775
- const retryableErrors = errors.filter((e) => e.code === 'parse_error' && e.filePath &&
776
- (e.message.includes('Worker exited') || e.message.includes('memory access out of bounds')));
777
- if (retryableErrors.length > 0 && WorkerClass) {
778
- log(`Retrying ${retryableErrors.length} files that failed due to WASM memory errors...`);
779
- const stillFailing = [];
780
- for (const errEntry of retryableErrors) {
781
- const filePath = errEntry.filePath;
782
- if (signal?.aborted)
783
- break;
784
- // Fresh worker for every retry — maximum WASM headroom
785
- recycleWorker();
786
- let content;
787
- try {
788
- const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, filePath);
789
- if (!fullPath)
790
- continue;
791
- content = await fsp.readFile(fullPath, 'utf-8');
792
- }
793
- catch {
794
- continue;
795
- }
796
- let result;
797
- try {
798
- result = await requestParse(filePath, content);
799
- }
800
- catch {
801
- stillFailing.push(errEntry);
802
- continue;
803
- }
804
- if (result.nodes.length > 0 || result.errors.length === 0) {
805
- const language = (0, grammars_1.detectLanguage)(filePath, content);
806
- const stats = await fsp.stat(path.join(this.rootDir, filePath));
807
- this.storeExtractionResult(filePath, content, language, stats, result);
808
- const idx = errors.indexOf(errEntry);
809
- if (idx >= 0)
810
- errors.splice(idx, 1);
811
- filesErrored--;
812
- filesIndexed++;
813
- totalNodes += result.nodes.length;
814
- totalEdges += result.edges.length;
815
- log(`Retry OK: ${filePath} (${result.nodes.length} nodes)`);
816
- }
817
- }
818
- // Last resort: for files that still crash on a clean worker, strip
819
- // comment-only lines to reduce WASM memory pressure. Many compiler
820
- // test files are 90%+ comments (CHECK directives) that don't contribute
821
- // code nodes but consume parser memory.
822
- if (stillFailing.length > 0) {
823
- log(`${stillFailing.length} files still failing — retrying with comments stripped...`);
824
- for (const errEntry of stillFailing) {
825
- const filePath = errEntry.filePath;
826
- if (signal?.aborted)
827
- break;
828
- recycleWorker();
829
- let fullContent;
830
- try {
831
- const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, filePath);
832
- if (!fullPath)
833
- continue;
834
- fullContent = await fsp.readFile(fullPath, 'utf-8');
835
- }
836
- catch {
837
- continue;
838
- }
839
- // Strip lines that are entirely comments (preserving line numbers
840
- // by replacing with empty lines so node positions stay correct)
841
- const stripped = fullContent
842
- .split('\n')
843
- .map(line => /^\s*\/\//.test(line) ? '' : line)
844
- .join('\n');
845
- let result;
846
- try {
847
- result = await requestParse(filePath, stripped);
848
- }
849
- catch {
850
- continue;
851
- }
852
- if (result.nodes.length > 0 || result.errors.length === 0) {
853
- const language = (0, grammars_1.detectLanguage)(filePath, fullContent);
854
- const stats = await fsp.stat(path.join(this.rootDir, filePath));
855
- this.storeExtractionResult(filePath, fullContent, language, stats, result);
856
- const idx = errors.indexOf(errEntry);
857
- if (idx >= 0)
858
- errors.splice(idx, 1);
859
- filesErrored--;
860
- filesIndexed++;
861
- totalNodes += result.nodes.length;
862
- totalEdges += result.edges.length;
863
- log(`Retry (stripped) OK: ${filePath} (${result.nodes.length} nodes)`);
864
- }
865
- }
866
- }
867
- }
868
- // Shut down parse worker and clear any pending timers
869
- rejectAllPending('Indexing complete');
870
- if (parseWorker) {
871
- parseWorker.terminate().catch(() => { });
872
- }
873
- return {
874
- success: filesIndexed > 0 || errors.filter((e) => e.severity === 'error').length === 0,
875
- filesIndexed,
876
- filesSkipped,
877
- filesErrored,
878
- nodesCreated: totalNodes,
879
- edgesCreated: totalEdges,
880
- errors,
881
- durationMs: Date.now() - startTime,
882
- };
883
- }
884
- /**
885
- * Index specific files
886
- */
887
- async indexFiles(filePaths) {
888
- const startTime = Date.now();
889
- const errors = [];
890
- let filesIndexed = 0;
891
- let filesSkipped = 0;
892
- let filesErrored = 0;
893
- let totalNodes = 0;
894
- let totalEdges = 0;
895
- for (const filePath of filePaths) {
896
- const result = await this.indexFile(filePath);
897
- if (result.errors.length > 0) {
898
- errors.push(...result.errors);
899
- }
900
- if (result.nodes.length > 0) {
901
- filesIndexed++;
902
- totalNodes += result.nodes.length;
903
- totalEdges += result.edges.length;
904
- }
905
- else if (result.errors.some((e) => e.severity === 'error')) {
906
- filesErrored++;
907
- }
908
- else {
909
- filesSkipped++;
910
- }
911
- }
912
- return {
913
- success: filesIndexed > 0 || errors.filter((e) => e.severity === 'error').length === 0,
914
- filesIndexed,
915
- filesSkipped,
916
- filesErrored,
917
- nodesCreated: totalNodes,
918
- edgesCreated: totalEdges,
919
- errors,
920
- durationMs: Date.now() - startTime,
921
- };
922
- }
923
- /**
924
- * Index a single file
925
- */
926
- async indexFile(relativePath) {
927
- const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, relativePath);
928
- if (!fullPath) {
929
- return {
930
- nodes: [],
931
- edges: [],
932
- unresolvedReferences: [],
933
- errors: [{ message: `Path traversal blocked: ${relativePath}`, filePath: relativePath, severity: 'error', code: 'path_traversal' }],
934
- durationMs: 0,
935
- };
936
- }
937
- // Read file content and stats
938
- let content;
939
- let stats;
940
- try {
941
- stats = await fsp.stat(fullPath);
942
- content = await fsp.readFile(fullPath, 'utf-8');
943
- }
944
- catch (error) {
945
- return {
946
- nodes: [],
947
- edges: [],
948
- unresolvedReferences: [],
949
- errors: [
950
- {
951
- message: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`,
952
- filePath: relativePath,
953
- severity: 'error',
954
- code: 'read_error',
955
- },
956
- ],
957
- durationMs: 0,
958
- };
959
- }
960
- return this.indexFileWithContent(relativePath, content, stats);
961
- }
962
- /**
963
- * Index a single file with pre-read content and stats.
964
- * Used by the parallel batch reader to avoid redundant file I/O.
965
- */
966
- async indexFileWithContent(relativePath, content, stats) {
967
- // Prevent path traversal
968
- const fullPath = (0, utils_1.validatePathWithinRoot)(this.rootDir, relativePath);
969
- if (!fullPath) {
970
- (0, errors_1.logWarn)('Path traversal blocked in indexFileWithContent', { relativePath });
971
- return {
972
- nodes: [],
973
- edges: [],
974
- unresolvedReferences: [],
975
- errors: [{ message: 'Path traversal blocked', filePath: relativePath, severity: 'error', code: 'path_traversal' }],
976
- durationMs: 0,
977
- };
978
- }
979
- // Check file size
980
- if (stats.size > this.config.maxFileSize) {
981
- return {
982
- nodes: [],
983
- edges: [],
984
- unresolvedReferences: [],
985
- errors: [
986
- {
987
- message: `File exceeds max size (${stats.size} > ${this.config.maxFileSize})`,
988
- filePath: relativePath,
989
- severity: 'warning',
990
- code: 'size_exceeded',
991
- },
992
- ],
993
- durationMs: 0,
994
- };
995
- }
996
- // Detect language
997
- const language = (0, grammars_1.detectLanguage)(relativePath, content);
998
- if (!(0, grammars_1.isLanguageSupported)(language)) {
999
- return {
1000
- nodes: [],
1001
- edges: [],
1002
- unresolvedReferences: [],
1003
- errors: [],
1004
- durationMs: 0,
1005
- };
1006
- }
1007
- // Extract from source. Use cached framework names if indexAll has run,
1008
- // otherwise detect on the spot so single-file re-index paths still emit
1009
- // route nodes / middleware / etc.
1010
- const frameworkNames = this.ensureDetectedFrameworks();
1011
- const result = (0, tree_sitter_1.extractFromSource)(relativePath, content, language, frameworkNames);
1012
- // Store in database
1013
- if (result.nodes.length > 0 || result.errors.length === 0) {
1014
- this.storeExtractionResult(relativePath, content, language, stats, result);
1015
- }
1016
- return result;
1017
- }
1018
- /**
1019
- * Store extraction result in database
1020
- */
1021
- storeExtractionResult(filePath, content, language, stats, result) {
1022
- const contentHash = hashContent(content);
1023
- // Check if file already exists and hasn't changed
1024
- const existingFile = this.queries.getFileByPath(filePath);
1025
- if (existingFile && existingFile.contentHash === contentHash) {
1026
- return; // No changes
1027
- }
1028
- // Delete existing data for this file
1029
- if (existingFile) {
1030
- this.queries.deleteFile(filePath);
1031
- }
1032
- // Filter out nodes with missing required fields before insertion.
1033
- // This prevents FK violations when edges reference nodes that would
1034
- // be silently skipped by insertNode() (see issue #42).
1035
- const validNodes = result.nodes.filter((n) => n.id && n.kind && n.name && n.filePath && n.language);
1036
- // Insert nodes
1037
- if (validNodes.length > 0) {
1038
- this.queries.insertNodes(validNodes);
1039
- }
1040
- // Filter edges to only reference nodes that were actually inserted
1041
- if (result.edges.length > 0) {
1042
- const insertedIds = new Set(validNodes.map((n) => n.id));
1043
- const validEdges = result.edges.filter((e) => insertedIds.has(e.source) && insertedIds.has(e.target));
1044
- if (validEdges.length > 0) {
1045
- this.queries.insertEdges(validEdges);
1046
- }
1047
- }
1048
- // Insert unresolved references in batch with denormalized filePath/language
1049
- if (result.unresolvedReferences.length > 0) {
1050
- const insertedIds = new Set(validNodes.map((n) => n.id));
1051
- const refsWithContext = result.unresolvedReferences
1052
- .filter((ref) => insertedIds.has(ref.fromNodeId))
1053
- .map((ref) => ({
1054
- ...ref,
1055
- filePath: ref.filePath ?? filePath,
1056
- language: ref.language ?? language,
1057
- }));
1058
- if (refsWithContext.length > 0) {
1059
- this.queries.insertUnresolvedRefsBatch(refsWithContext);
1060
- }
1061
- }
1062
- // Insert file record
1063
- const fileRecord = {
1064
- path: filePath,
1065
- contentHash,
1066
- language,
1067
- size: stats.size,
1068
- modifiedAt: stats.mtimeMs,
1069
- indexedAt: Date.now(),
1070
- nodeCount: result.nodes.length,
1071
- errors: result.errors.length > 0 ? result.errors : undefined,
1072
- };
1073
- this.queries.upsertFile(fileRecord);
1074
- }
1075
- /**
1076
- * Sync with current file state.
1077
- * Uses git status as a fast path when available, falling back to full scan.
1078
- */
1079
- async sync(onProgress) {
1080
- await (0, grammars_1.initGrammars)(); // Initialize WASM runtime (grammars loaded lazily below)
1081
- const startTime = Date.now();
1082
- let filesChecked = 0;
1083
- let filesAdded = 0;
1084
- let filesModified = 0;
1085
- let filesRemoved = 0;
1086
- let nodesUpdated = 0;
1087
- const changedFilePaths = [];
1088
- onProgress?.({
1089
- phase: 'scanning',
1090
- current: 0,
1091
- total: 0,
1092
- });
1093
- const filesToIndex = [];
1094
- const gitChanges = getGitChangedFiles(this.rootDir, this.config);
1095
- if (gitChanges) {
1096
- // === Git fast path ===
1097
- // Only inspect the files git reports as changed instead of scanning everything.
1098
- filesChecked = gitChanges.modified.length + gitChanges.added.length + gitChanges.deleted.length;
1099
- // Handle deleted files
1100
- for (const filePath of gitChanges.deleted) {
1101
- const tracked = this.queries.getFileByPath(filePath);
1102
- if (tracked) {
1103
- this.queries.deleteFile(filePath);
1104
- filesRemoved++;
1105
- }
1106
- }
1107
- // Handle modified + added files — read + hash only these. Untracked
1108
- // (`??`) files stay untracked in git even after we index them, so they
1109
- // can't be trusted as "new": re-hash and compare against the DB exactly
1110
- // like modified files. Otherwise every sync re-indexes them and status
1111
- // reports them as pending forever. (See issue #206.)
1112
- for (const filePath of [...gitChanges.modified, ...gitChanges.added]) {
1113
- const fullPath = path.join(this.rootDir, filePath);
1114
- let content;
1115
- try {
1116
- content = fs.readFileSync(fullPath, 'utf-8');
1117
- }
1118
- catch (error) {
1119
- (0, errors_1.logDebug)('Skipping unreadable file during sync', { filePath, error: String(error) });
1120
- continue;
1121
- }
1122
- const contentHash = hashContent(content);
1123
- const tracked = this.queries.getFileByPath(filePath);
1124
- if (!tracked) {
1125
- filesToIndex.push(filePath);
1126
- changedFilePaths.push(filePath);
1127
- filesAdded++;
1128
- }
1129
- else if (tracked.contentHash !== contentHash) {
1130
- filesToIndex.push(filePath);
1131
- changedFilePaths.push(filePath);
1132
- filesModified++;
1133
- }
1134
- }
1135
- }
1136
- else {
1137
- // === Fallback: full scan (non-git project or git failure) ===
1138
- const currentFiles = new Set(scanDirectory(this.rootDir, this.config));
1139
- filesChecked = currentFiles.size;
1140
- // Build Map for O(1) lookups instead of .find() per file
1141
- const trackedFiles = this.queries.getAllFiles();
1142
- const trackedMap = new Map();
1143
- for (const f of trackedFiles) {
1144
- trackedMap.set(f.path, f);
1145
- }
1146
- // Find files to remove (in DB but not on disk)
1147
- for (const tracked of trackedFiles) {
1148
- if (!currentFiles.has(tracked.path)) {
1149
- this.queries.deleteFile(tracked.path);
1150
- filesRemoved++;
1151
- }
1152
- }
1153
- // Find files to add or update
1154
- for (const filePath of currentFiles) {
1155
- const fullPath = path.join(this.rootDir, filePath);
1156
- let content;
1157
- try {
1158
- content = fs.readFileSync(fullPath, 'utf-8');
1159
- }
1160
- catch (error) {
1161
- (0, errors_1.logDebug)('Skipping unreadable file during sync', { filePath, error: String(error) });
1162
- continue;
1163
- }
1164
- const contentHash = hashContent(content);
1165
- const tracked = trackedMap.get(filePath);
1166
- if (!tracked) {
1167
- filesToIndex.push(filePath);
1168
- changedFilePaths.push(filePath);
1169
- filesAdded++;
1170
- }
1171
- else if (tracked.contentHash !== contentHash) {
1172
- filesToIndex.push(filePath);
1173
- changedFilePaths.push(filePath);
1174
- filesModified++;
1175
- }
1176
- }
1177
- }
1178
- // Load only grammars needed for changed files
1179
- if (filesToIndex.length > 0) {
1180
- const neededLanguages = [...new Set(filesToIndex.map((f) => (0, grammars_1.detectLanguage)(f)))];
1181
- // .h files default to 'c' but may be C++ — ensure cpp grammar is loaded
1182
- if (neededLanguages.includes('c') && !neededLanguages.includes('cpp')) {
1183
- neededLanguages.push('cpp');
1184
- }
1185
- await (0, grammars_1.loadGrammarsForLanguages)(neededLanguages);
1186
- }
1187
- // Index changed files
1188
- const total = filesToIndex.length;
1189
- for (let i = 0; i < filesToIndex.length; i++) {
1190
- const filePath = filesToIndex[i];
1191
- onProgress?.({
1192
- phase: 'parsing',
1193
- current: i + 1,
1194
- total,
1195
- currentFile: filePath,
1196
- });
1197
- const result = await this.indexFile(filePath);
1198
- nodesUpdated += result.nodes.length;
1199
- }
1200
- return {
1201
- filesChecked,
1202
- filesAdded,
1203
- filesModified,
1204
- filesRemoved,
1205
- nodesUpdated,
1206
- durationMs: Date.now() - startTime,
1207
- changedFilePaths: changedFilePaths.length > 0 ? changedFilePaths : undefined,
1208
- };
1209
- }
1210
- /**
1211
- * Get files that have changed since last index.
1212
- * Uses git status as a fast path when available, falling back to full scan.
1213
- */
1214
- getChangedFiles() {
1215
- const gitChanges = getGitChangedFiles(this.rootDir, this.config);
1216
- if (gitChanges) {
1217
- // === Git fast path ===
1218
- const added = [];
1219
- const modified = [];
1220
- const removed = [];
1221
- // Deleted files — only report if tracked in DB
1222
- for (const filePath of gitChanges.deleted) {
1223
- const tracked = this.queries.getFileByPath(filePath);
1224
- if (tracked) {
1225
- removed.push(filePath);
1226
- }
1227
- }
1228
- // Modified + added files — read + hash, compare with DB. Untracked (`??`)
1229
- // files stay untracked in git even after indexing, so they must be
1230
- // hash-compared like modified files instead of always counting as added —
1231
- // otherwise status reports them as pending forever. (See issue #206.)
1232
- for (const filePath of [...gitChanges.modified, ...gitChanges.added]) {
1233
- const fullPath = path.join(this.rootDir, filePath);
1234
- let content;
1235
- try {
1236
- content = fs.readFileSync(fullPath, 'utf-8');
1237
- }
1238
- catch (error) {
1239
- (0, errors_1.logDebug)('Skipping unreadable file while detecting changes', { filePath, error: String(error) });
1240
- continue;
1241
- }
1242
- const contentHash = hashContent(content);
1243
- const tracked = this.queries.getFileByPath(filePath);
1244
- if (!tracked) {
1245
- added.push(filePath);
1246
- }
1247
- else if (tracked.contentHash !== contentHash) {
1248
- modified.push(filePath);
1249
- }
1250
- }
1251
- return { added, modified, removed };
1252
- }
1253
- // === Fallback: full scan (non-git project or git failure) ===
1254
- const currentFiles = new Set(scanDirectory(this.rootDir, this.config));
1255
- const trackedFiles = this.queries.getAllFiles();
1256
- // Build Map for O(1) lookups
1257
- const trackedMap = new Map();
1258
- for (const f of trackedFiles) {
1259
- trackedMap.set(f.path, f);
1260
- }
1261
- const added = [];
1262
- const modified = [];
1263
- const removed = [];
1264
- // Find removed files
1265
- for (const tracked of trackedFiles) {
1266
- if (!currentFiles.has(tracked.path)) {
1267
- removed.push(tracked.path);
1268
- }
1269
- }
1270
- // Find added and modified files
1271
- for (const filePath of currentFiles) {
1272
- const fullPath = path.join(this.rootDir, filePath);
1273
- let content;
1274
- try {
1275
- content = fs.readFileSync(fullPath, 'utf-8');
1276
- }
1277
- catch (error) {
1278
- (0, errors_1.logDebug)('Skipping unreadable file while detecting changes', { filePath, error: String(error) });
1279
- continue;
1280
- }
1281
- const contentHash = hashContent(content);
1282
- const tracked = trackedMap.get(filePath);
1283
- if (!tracked) {
1284
- added.push(filePath);
1285
- }
1286
- else if (tracked.contentHash !== contentHash) {
1287
- modified.push(filePath);
1288
- }
1289
- }
1290
- return { added, modified, removed };
1291
- }
1292
- }
1293
- exports.ExtractionOrchestrator = ExtractionOrchestrator;
1294
- // Re-export useful types and functions
1295
- var tree_sitter_2 = require("./tree-sitter");
1296
- Object.defineProperty(exports, "extractFromSource", { enumerable: true, get: function () { return tree_sitter_2.extractFromSource; } });
1297
- var grammars_2 = require("./grammars");
1298
- Object.defineProperty(exports, "detectLanguage", { enumerable: true, get: function () { return grammars_2.detectLanguage; } });
1299
- Object.defineProperty(exports, "isLanguageSupported", { enumerable: true, get: function () { return grammars_2.isLanguageSupported; } });
1300
- Object.defineProperty(exports, "isGrammarLoaded", { enumerable: true, get: function () { return grammars_2.isGrammarLoaded; } });
1301
- Object.defineProperty(exports, "getSupportedLanguages", { enumerable: true, get: function () { return grammars_2.getSupportedLanguages; } });
1302
- Object.defineProperty(exports, "initGrammars", { enumerable: true, get: function () { return grammars_2.initGrammars; } });
1303
- Object.defineProperty(exports, "loadGrammarsForLanguages", { enumerable: true, get: function () { return grammars_2.loadGrammarsForLanguages; } });
1304
- Object.defineProperty(exports, "loadAllGrammars", { enumerable: true, get: function () { return grammars_2.loadAllGrammars; } });
1305
- //# sourceMappingURL=index.js.map