@optave/codegraph 3.7.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +28 -16
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +195 -30
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/metrics.d.ts +0 -3
  6. package/dist/ast-analysis/metrics.d.ts.map +1 -1
  7. package/dist/ast-analysis/metrics.js +30 -13
  8. package/dist/ast-analysis/metrics.js.map +1 -1
  9. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  10. package/dist/ast-analysis/rules/javascript.js +0 -1
  11. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  12. package/dist/ast-analysis/shared.d.ts.map +1 -1
  13. package/dist/ast-analysis/shared.js +24 -19
  14. package/dist/ast-analysis/shared.js.map +1 -1
  15. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  16. package/dist/ast-analysis/visitor-utils.js +55 -39
  17. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  18. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  19. package/dist/ast-analysis/visitor.js +91 -70
  20. package/dist/ast-analysis/visitor.js.map +1 -1
  21. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  22. package/dist/ast-analysis/visitors/ast-store-visitor.js +52 -129
  23. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  24. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  25. package/dist/ast-analysis/visitors/complexity-visitor.js +32 -39
  26. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  27. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  28. package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
  29. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  30. package/dist/cli/commands/ast.js +2 -2
  31. package/dist/cli/commands/ast.js.map +1 -1
  32. package/dist/cli/commands/watch.d.ts.map +1 -1
  33. package/dist/cli/commands/watch.js +16 -2
  34. package/dist/cli/commands/watch.js.map +1 -1
  35. package/dist/db/connection.d.ts.map +1 -1
  36. package/dist/db/connection.js +29 -26
  37. package/dist/db/connection.js.map +1 -1
  38. package/dist/db/query-builder.d.ts.map +1 -1
  39. package/dist/db/query-builder.js +16 -5
  40. package/dist/db/query-builder.js.map +1 -1
  41. package/dist/db/repository/base.d.ts +10 -0
  42. package/dist/db/repository/base.d.ts.map +1 -1
  43. package/dist/db/repository/base.js +17 -0
  44. package/dist/db/repository/base.js.map +1 -1
  45. package/dist/db/repository/native-repository.d.ts +6 -1
  46. package/dist/db/repository/native-repository.d.ts.map +1 -1
  47. package/dist/db/repository/native-repository.js +77 -1
  48. package/dist/db/repository/native-repository.js.map +1 -1
  49. package/dist/db/repository/nodes.d.ts.map +1 -1
  50. package/dist/db/repository/nodes.js +8 -4
  51. package/dist/db/repository/nodes.js.map +1 -1
  52. package/dist/db/repository/sqlite-repository.d.ts +3 -0
  53. package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
  54. package/dist/db/repository/sqlite-repository.js +26 -0
  55. package/dist/db/repository/sqlite-repository.js.map +1 -1
  56. package/dist/domain/analysis/brief.d.ts.map +1 -1
  57. package/dist/domain/analysis/brief.js +13 -17
  58. package/dist/domain/analysis/brief.js.map +1 -1
  59. package/dist/domain/analysis/context.d.ts.map +1 -1
  60. package/dist/domain/analysis/context.js +14 -11
  61. package/dist/domain/analysis/context.js.map +1 -1
  62. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  63. package/dist/domain/analysis/dependencies.js +53 -52
  64. package/dist/domain/analysis/dependencies.js.map +1 -1
  65. package/dist/domain/analysis/fn-impact.d.ts +2 -7
  66. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  67. package/dist/domain/analysis/fn-impact.js +33 -31
  68. package/dist/domain/analysis/fn-impact.js.map +1 -1
  69. package/dist/domain/analysis/implementations.d.ts.map +1 -1
  70. package/dist/domain/analysis/implementations.js +11 -19
  71. package/dist/domain/analysis/implementations.js.map +1 -1
  72. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  73. package/dist/domain/analysis/module-map.js +55 -76
  74. package/dist/domain/analysis/module-map.js.map +1 -1
  75. package/dist/domain/analysis/query-helpers.d.ts +7 -0
  76. package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
  77. package/dist/domain/analysis/query-helpers.js +15 -1
  78. package/dist/domain/analysis/query-helpers.js.map +1 -1
  79. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  80. package/dist/domain/graph/builder/pipeline.js +315 -43
  81. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  82. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  83. package/dist/domain/graph/builder/stages/build-edges.js +106 -1
  84. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  85. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  86. package/dist/domain/graph/builder/stages/collect-files.js +17 -5
  87. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  88. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  89. package/dist/domain/graph/builder/stages/detect-changes.js +99 -51
  90. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  91. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  92. package/dist/domain/graph/builder/stages/finalize.js +34 -7
  93. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  94. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  95. package/dist/domain/graph/builder/stages/insert-nodes.js +50 -26
  96. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  97. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  98. package/dist/domain/graph/builder/stages/resolve-imports.js +95 -84
  99. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  100. package/dist/domain/graph/cycles.d.ts +6 -0
  101. package/dist/domain/graph/cycles.d.ts.map +1 -1
  102. package/dist/domain/graph/cycles.js +114 -22
  103. package/dist/domain/graph/cycles.js.map +1 -1
  104. package/dist/domain/graph/resolve.js +1 -1
  105. package/dist/domain/graph/resolve.js.map +1 -1
  106. package/dist/domain/graph/watcher.d.ts +2 -0
  107. package/dist/domain/graph/watcher.d.ts.map +1 -1
  108. package/dist/domain/graph/watcher.js +170 -75
  109. package/dist/domain/graph/watcher.js.map +1 -1
  110. package/dist/domain/parser.d.ts +1 -6
  111. package/dist/domain/parser.d.ts.map +1 -1
  112. package/dist/domain/parser.js +101 -32
  113. package/dist/domain/parser.js.map +1 -1
  114. package/dist/domain/search/generator.js +1 -1
  115. package/dist/domain/search/generator.js.map +1 -1
  116. package/dist/domain/search/models.d.ts +4 -3
  117. package/dist/domain/search/models.d.ts.map +1 -1
  118. package/dist/domain/search/models.js +18 -5
  119. package/dist/domain/search/models.js.map +1 -1
  120. package/dist/domain/search/search/hybrid.d.ts.map +1 -1
  121. package/dist/domain/search/search/hybrid.js +29 -18
  122. package/dist/domain/search/search/hybrid.js.map +1 -1
  123. package/dist/extractors/clojure.d.ts +12 -0
  124. package/dist/extractors/clojure.d.ts.map +1 -0
  125. package/dist/extractors/clojure.js +245 -0
  126. package/dist/extractors/clojure.js.map +1 -0
  127. package/dist/extractors/cuda.d.ts +11 -0
  128. package/dist/extractors/cuda.d.ts.map +1 -0
  129. package/dist/extractors/cuda.js +302 -0
  130. package/dist/extractors/cuda.js.map +1 -0
  131. package/dist/extractors/erlang.d.ts +14 -0
  132. package/dist/extractors/erlang.d.ts.map +1 -0
  133. package/dist/extractors/erlang.js +239 -0
  134. package/dist/extractors/erlang.js.map +1 -0
  135. package/dist/extractors/fsharp.d.ts +13 -0
  136. package/dist/extractors/fsharp.d.ts.map +1 -0
  137. package/dist/extractors/fsharp.js +218 -0
  138. package/dist/extractors/fsharp.js.map +1 -0
  139. package/dist/extractors/gleam.d.ts +14 -0
  140. package/dist/extractors/gleam.d.ts.map +1 -0
  141. package/dist/extractors/gleam.js +229 -0
  142. package/dist/extractors/gleam.js.map +1 -0
  143. package/dist/extractors/go.js +36 -33
  144. package/dist/extractors/go.js.map +1 -1
  145. package/dist/extractors/groovy.d.ts +10 -0
  146. package/dist/extractors/groovy.d.ts.map +1 -0
  147. package/dist/extractors/groovy.js +304 -0
  148. package/dist/extractors/groovy.js.map +1 -0
  149. package/dist/extractors/helpers.d.ts.map +1 -1
  150. package/dist/extractors/helpers.js +40 -29
  151. package/dist/extractors/helpers.js.map +1 -1
  152. package/dist/extractors/index.d.ts +11 -0
  153. package/dist/extractors/index.d.ts.map +1 -1
  154. package/dist/extractors/index.js +11 -0
  155. package/dist/extractors/index.js.map +1 -1
  156. package/dist/extractors/java.js +58 -46
  157. package/dist/extractors/java.js.map +1 -1
  158. package/dist/extractors/javascript.js +46 -45
  159. package/dist/extractors/javascript.js.map +1 -1
  160. package/dist/extractors/julia.d.ts +16 -0
  161. package/dist/extractors/julia.d.ts.map +1 -0
  162. package/dist/extractors/julia.js +287 -0
  163. package/dist/extractors/julia.js.map +1 -0
  164. package/dist/extractors/kotlin.js +84 -78
  165. package/dist/extractors/kotlin.js.map +1 -1
  166. package/dist/extractors/objc.d.ts +9 -0
  167. package/dist/extractors/objc.d.ts.map +1 -0
  168. package/dist/extractors/objc.js +406 -0
  169. package/dist/extractors/objc.js.map +1 -0
  170. package/dist/extractors/ocaml.js +74 -0
  171. package/dist/extractors/ocaml.js.map +1 -1
  172. package/dist/extractors/python.js +29 -24
  173. package/dist/extractors/python.js.map +1 -1
  174. package/dist/extractors/r.d.ts +13 -0
  175. package/dist/extractors/r.d.ts.map +1 -0
  176. package/dist/extractors/r.js +251 -0
  177. package/dist/extractors/r.js.map +1 -0
  178. package/dist/extractors/rust.js +41 -32
  179. package/dist/extractors/rust.js.map +1 -1
  180. package/dist/extractors/solidity.d.ts +9 -0
  181. package/dist/extractors/solidity.d.ts.map +1 -0
  182. package/dist/extractors/solidity.js +365 -0
  183. package/dist/extractors/solidity.js.map +1 -0
  184. package/dist/extractors/swift.js +83 -81
  185. package/dist/extractors/swift.js.map +1 -1
  186. package/dist/extractors/verilog.d.ts +9 -0
  187. package/dist/extractors/verilog.d.ts.map +1 -0
  188. package/dist/extractors/verilog.js +286 -0
  189. package/dist/extractors/verilog.js.map +1 -0
  190. package/dist/extractors/zig.js +58 -60
  191. package/dist/extractors/zig.js.map +1 -1
  192. package/dist/features/ast.d.ts +16 -14
  193. package/dist/features/ast.d.ts.map +1 -1
  194. package/dist/features/ast.js +84 -83
  195. package/dist/features/ast.js.map +1 -1
  196. package/dist/features/audit.d.ts.map +1 -1
  197. package/dist/features/audit.js +8 -6
  198. package/dist/features/audit.js.map +1 -1
  199. package/dist/features/branch-compare.d.ts.map +1 -1
  200. package/dist/features/branch-compare.js +69 -72
  201. package/dist/features/branch-compare.js.map +1 -1
  202. package/dist/features/communities.d.ts.map +1 -1
  203. package/dist/features/communities.js +19 -7
  204. package/dist/features/communities.js.map +1 -1
  205. package/dist/features/complexity.d.ts.map +1 -1
  206. package/dist/features/complexity.js +120 -125
  207. package/dist/features/complexity.js.map +1 -1
  208. package/dist/features/dataflow.d.ts.map +1 -1
  209. package/dist/features/dataflow.js +136 -137
  210. package/dist/features/dataflow.js.map +1 -1
  211. package/dist/features/flow.d.ts.map +1 -1
  212. package/dist/features/flow.js +84 -79
  213. package/dist/features/flow.js.map +1 -1
  214. package/dist/features/structure-query.d.ts.map +1 -1
  215. package/dist/features/structure-query.js +69 -65
  216. package/dist/features/structure-query.js.map +1 -1
  217. package/dist/graph/algorithms/bfs.d.ts +2 -0
  218. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  219. package/dist/graph/algorithms/bfs.js +27 -0
  220. package/dist/graph/algorithms/bfs.js.map +1 -1
  221. package/dist/graph/algorithms/centrality.d.ts +2 -0
  222. package/dist/graph/algorithms/centrality.d.ts.map +1 -1
  223. package/dist/graph/algorithms/centrality.js +28 -0
  224. package/dist/graph/algorithms/centrality.js.map +1 -1
  225. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  226. package/dist/graph/algorithms/leiden/optimiser.js +70 -55
  227. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  228. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  229. package/dist/graph/algorithms/leiden/partition.js +288 -266
  230. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  231. package/dist/graph/algorithms/louvain.d.ts +3 -4
  232. package/dist/graph/algorithms/louvain.d.ts.map +1 -1
  233. package/dist/graph/algorithms/louvain.js +29 -0
  234. package/dist/graph/algorithms/louvain.js.map +1 -1
  235. package/dist/graph/algorithms/shortest-path.d.ts +2 -0
  236. package/dist/graph/algorithms/shortest-path.d.ts.map +1 -1
  237. package/dist/graph/algorithms/shortest-path.js +18 -1
  238. package/dist/graph/algorithms/shortest-path.js.map +1 -1
  239. package/dist/graph/model.d.ts.map +1 -1
  240. package/dist/graph/model.js +5 -1
  241. package/dist/graph/model.js.map +1 -1
  242. package/dist/infrastructure/config.d.ts.map +1 -1
  243. package/dist/infrastructure/config.js +6 -4
  244. package/dist/infrastructure/config.js.map +1 -1
  245. package/dist/infrastructure/suppress.d.ts +25 -0
  246. package/dist/infrastructure/suppress.d.ts.map +1 -0
  247. package/dist/infrastructure/suppress.js +43 -0
  248. package/dist/infrastructure/suppress.js.map +1 -0
  249. package/dist/mcp/server.d.ts.map +1 -1
  250. package/dist/mcp/server.js +29 -24
  251. package/dist/mcp/server.js.map +1 -1
  252. package/dist/presentation/dataflow.d.ts.map +1 -1
  253. package/dist/presentation/dataflow.js +47 -38
  254. package/dist/presentation/dataflow.js.map +1 -1
  255. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
  256. package/dist/presentation/diff-impact-mermaid.js +60 -51
  257. package/dist/presentation/diff-impact-mermaid.js.map +1 -1
  258. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  259. package/dist/presentation/queries-cli/exports.js +20 -14
  260. package/dist/presentation/queries-cli/exports.js.map +1 -1
  261. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  262. package/dist/presentation/queries-cli/impact.js +15 -13
  263. package/dist/presentation/queries-cli/impact.js.map +1 -1
  264. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  265. package/dist/presentation/queries-cli/inspect.js +101 -79
  266. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  267. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  268. package/dist/presentation/queries-cli/overview.js +25 -16
  269. package/dist/presentation/queries-cli/overview.js.map +1 -1
  270. package/dist/presentation/queries-cli/path.js +26 -20
  271. package/dist/presentation/queries-cli/path.js.map +1 -1
  272. package/dist/presentation/result-formatter.d.ts +10 -0
  273. package/dist/presentation/result-formatter.d.ts.map +1 -1
  274. package/dist/presentation/result-formatter.js +16 -1
  275. package/dist/presentation/result-formatter.js.map +1 -1
  276. package/dist/presentation/viewer.d.ts.map +1 -1
  277. package/dist/presentation/viewer.js +18 -12
  278. package/dist/presentation/viewer.js.map +1 -1
  279. package/dist/shared/errors.d.ts +5 -0
  280. package/dist/shared/errors.d.ts.map +1 -1
  281. package/dist/shared/errors.js +5 -0
  282. package/dist/shared/errors.js.map +1 -1
  283. package/dist/shared/hierarchy.d.ts +8 -2
  284. package/dist/shared/hierarchy.d.ts.map +1 -1
  285. package/dist/shared/hierarchy.js +42 -1
  286. package/dist/shared/hierarchy.js.map +1 -1
  287. package/dist/shared/normalize.d.ts +6 -1
  288. package/dist/shared/normalize.d.ts.map +1 -1
  289. package/dist/shared/normalize.js +20 -12
  290. package/dist/shared/normalize.js.map +1 -1
  291. package/dist/shared/paginate.d.ts +0 -9
  292. package/dist/shared/paginate.d.ts.map +1 -1
  293. package/dist/shared/paginate.js +0 -15
  294. package/dist/shared/paginate.js.map +1 -1
  295. package/dist/types.d.ts +129 -3
  296. package/dist/types.d.ts.map +1 -1
  297. package/grammars/tree-sitter-clojure.wasm +0 -0
  298. package/grammars/tree-sitter-cuda.wasm +0 -0
  299. package/grammars/tree-sitter-erlang.wasm +0 -0
  300. package/grammars/tree-sitter-fsharp.wasm +0 -0
  301. package/grammars/tree-sitter-gleam.wasm +0 -0
  302. package/grammars/tree-sitter-groovy.wasm +0 -0
  303. package/grammars/tree-sitter-julia.wasm +0 -0
  304. package/grammars/tree-sitter-objc.wasm +0 -0
  305. package/grammars/tree-sitter-ocaml_interface.wasm +0 -0
  306. package/grammars/tree-sitter-r.wasm +0 -0
  307. package/grammars/tree-sitter-solidity.wasm +0 -0
  308. package/grammars/tree-sitter-verilog.wasm +0 -0
  309. package/package.json +18 -7
  310. package/src/ast-analysis/engine.ts +245 -42
  311. package/src/ast-analysis/metrics.ts +33 -11
  312. package/src/ast-analysis/rules/javascript.ts +0 -1
  313. package/src/ast-analysis/shared.ts +33 -24
  314. package/src/ast-analysis/visitor-utils.ts +52 -32
  315. package/src/ast-analysis/visitor.ts +132 -71
  316. package/src/ast-analysis/visitors/ast-store-visitor.ts +49 -119
  317. package/src/ast-analysis/visitors/complexity-visitor.ts +35 -40
  318. package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
  319. package/src/cli/commands/ast.ts +2 -2
  320. package/src/cli/commands/watch.ts +16 -2
  321. package/src/db/connection.ts +29 -28
  322. package/src/db/query-builder.ts +15 -3
  323. package/src/db/repository/base.ts +20 -0
  324. package/src/db/repository/native-repository.ts +79 -1
  325. package/src/db/repository/nodes.ts +13 -8
  326. package/src/db/repository/sqlite-repository.ts +29 -0
  327. package/src/domain/analysis/brief.ts +15 -25
  328. package/src/domain/analysis/context.ts +17 -10
  329. package/src/domain/analysis/dependencies.ts +67 -76
  330. package/src/domain/analysis/fn-impact.ts +36 -43
  331. package/src/domain/analysis/implementations.ts +11 -17
  332. package/src/domain/analysis/module-map.ts +58 -92
  333. package/src/domain/analysis/query-helpers.ts +18 -1
  334. package/src/domain/graph/builder/pipeline.ts +366 -41
  335. package/src/domain/graph/builder/stages/build-edges.ts +162 -1
  336. package/src/domain/graph/builder/stages/collect-files.ts +18 -7
  337. package/src/domain/graph/builder/stages/detect-changes.ts +110 -56
  338. package/src/domain/graph/builder/stages/finalize.ts +41 -11
  339. package/src/domain/graph/builder/stages/insert-nodes.ts +75 -39
  340. package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
  341. package/src/domain/graph/cycles.ts +110 -23
  342. package/src/domain/graph/resolve.ts +1 -1
  343. package/src/domain/graph/watcher.ts +202 -96
  344. package/src/domain/parser.ts +122 -28
  345. package/src/domain/search/generator.ts +1 -1
  346. package/src/domain/search/models.ts +17 -4
  347. package/src/domain/search/search/hybrid.ts +69 -51
  348. package/src/extractors/clojure.ts +273 -0
  349. package/src/extractors/cuda.ts +316 -0
  350. package/src/extractors/erlang.ts +252 -0
  351. package/src/extractors/fsharp.ts +253 -0
  352. package/src/extractors/gleam.ts +246 -0
  353. package/src/extractors/go.ts +43 -33
  354. package/src/extractors/groovy.ts +332 -0
  355. package/src/extractors/helpers.ts +37 -23
  356. package/src/extractors/index.ts +11 -0
  357. package/src/extractors/java.ts +66 -47
  358. package/src/extractors/javascript.ts +45 -46
  359. package/src/extractors/julia.ts +318 -0
  360. package/src/extractors/kotlin.ts +84 -77
  361. package/src/extractors/objc.ts +431 -0
  362. package/src/extractors/ocaml.ts +78 -0
  363. package/src/extractors/python.ts +31 -25
  364. package/src/extractors/r.ts +253 -0
  365. package/src/extractors/rust.ts +37 -29
  366. package/src/extractors/solidity.ts +394 -0
  367. package/src/extractors/swift.ts +81 -80
  368. package/src/extractors/verilog.ts +315 -0
  369. package/src/extractors/zig.ts +58 -61
  370. package/src/features/ast.ts +131 -112
  371. package/src/features/audit.ts +8 -6
  372. package/src/features/branch-compare.ts +105 -79
  373. package/src/features/communities.ts +25 -10
  374. package/src/features/complexity.ts +171 -134
  375. package/src/features/dataflow.ts +165 -175
  376. package/src/features/flow.ts +129 -92
  377. package/src/features/structure-query.ts +79 -64
  378. package/src/graph/algorithms/bfs.ts +34 -0
  379. package/src/graph/algorithms/centrality.ts +30 -0
  380. package/src/graph/algorithms/leiden/optimiser.ts +99 -55
  381. package/src/graph/algorithms/leiden/partition.ts +359 -294
  382. package/src/graph/algorithms/louvain.ts +31 -4
  383. package/src/graph/algorithms/shortest-path.ts +20 -1
  384. package/src/graph/model.ts +6 -1
  385. package/src/infrastructure/config.ts +6 -4
  386. package/src/infrastructure/suppress.ts +47 -0
  387. package/src/mcp/server.ts +53 -37
  388. package/src/presentation/dataflow.ts +50 -44
  389. package/src/presentation/diff-impact-mermaid.ts +104 -62
  390. package/src/presentation/queries-cli/exports.ts +21 -13
  391. package/src/presentation/queries-cli/impact.ts +15 -13
  392. package/src/presentation/queries-cli/inspect.ts +100 -81
  393. package/src/presentation/queries-cli/overview.ts +26 -16
  394. package/src/presentation/queries-cli/path.ts +33 -25
  395. package/src/presentation/result-formatter.ts +19 -1
  396. package/src/presentation/viewer.ts +42 -14
  397. package/src/shared/errors.ts +6 -0
  398. package/src/shared/hierarchy.ts +50 -2
  399. package/src/shared/normalize.ts +31 -12
  400. package/src/shared/paginate.ts +0 -17
  401. package/src/types.ts +138 -3
@@ -18,29 +18,8 @@ function isTrackedExt(filePath: string): boolean {
18
18
  return EXTENSIONS.has(path.extname(filePath));
19
19
  }
20
20
 
21
- export async function watchProject(rootDir: string, opts: { engine?: string } = {}): Promise<void> {
22
- const dbPath = path.join(rootDir, '.codegraph', 'graph.db');
23
- if (!fs.existsSync(dbPath)) {
24
- throw new DbError('No graph.db found. Run `codegraph build` first.', { file: dbPath });
25
- }
26
-
27
- const db = openDb(dbPath);
28
- initSchema(db);
29
- const engineOpts: import('../../types.js').EngineOpts = {
30
- engine: (opts.engine || 'auto') as import('../../types.js').EngineMode,
31
- dataflow: false,
32
- ast: false,
33
- };
34
- const { name: engineName, version: engineVersion } = getActiveEngine(engineOpts);
35
- info(`Watch mode using ${engineName} engine${engineVersion ? ` (v${engineVersion})` : ''}`);
36
-
37
- const cache = createParseTreeCache();
38
- info(
39
- cache
40
- ? 'Incremental parsing enabled (native tree cache)'
41
- : 'Incremental parsing unavailable (full re-parse)',
42
- );
43
-
21
+ /** Prepare all SQL statements needed by the watcher's incremental rebuild. */
22
+ function prepareWatcherStatements(db: ReturnType<typeof openDb>): IncrementalStmts {
44
23
  const stmts = {
45
24
  insertNode: db.prepare(
46
25
  'INSERT OR IGNORE INTO nodes (name, kind, file, line, end_line) VALUES (?, ?, ?, ?, ?)',
@@ -67,7 +46,6 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
67
46
  listSymbols: db.prepare("SELECT name, kind, line FROM nodes WHERE file = ? AND kind != 'file'"),
68
47
  };
69
48
 
70
- // Use named params for statements needing the same value twice
71
49
  const origDeleteEdges = db.prepare(
72
50
  `DELETE FROM edges WHERE source_id IN (SELECT id FROM nodes WHERE file = @f) OR target_id IN (SELECT id FROM nodes WHERE file = @f)`,
73
51
  );
@@ -79,96 +57,224 @@ export async function watchProject(rootDir: string, opts: { engine?: string } =
79
57
  get: (f: string) => origCountEdges.get({ f }) as { c: number } | undefined,
80
58
  };
81
59
 
60
+ return stmts as IncrementalStmts;
61
+ }
62
+
63
+ /** Rebuild result shape from rebuildFile. */
64
+ interface RebuildResult {
65
+ file: string;
66
+ deleted?: boolean;
67
+ event: string;
68
+ symbolDiff: unknown;
69
+ nodesBefore: number;
70
+ nodesAfter: number;
71
+ nodesAdded: number;
72
+ nodesRemoved: number;
73
+ edgesAdded: number;
74
+ }
75
+
76
+ /** Process a batch of pending file changes: rebuild, journal, and log. */
77
+ async function processPendingFiles(
78
+ files: string[],
79
+ db: ReturnType<typeof openDb>,
80
+ rootDir: string,
81
+ stmts: IncrementalStmts,
82
+ engineOpts: import('../../types.js').EngineOpts,
83
+ cache: ReturnType<typeof createParseTreeCache>,
84
+ ): Promise<void> {
85
+ const results: RebuildResult[] = [];
86
+ for (const filePath of files) {
87
+ const result = (await rebuildFile(db, rootDir, filePath, stmts, engineOpts, cache, {
88
+ diffSymbols: diffSymbols as (old: unknown[], new_: unknown[]) => unknown,
89
+ })) as RebuildResult | null;
90
+ if (result) results.push(result);
91
+ }
92
+
93
+ if (results.length > 0) {
94
+ writeJournalAndChangeEvents(rootDir, results);
95
+ }
96
+
97
+ logRebuildResults(results);
98
+ }
99
+
100
+ /** Write journal entries and change events for processed files. */
101
+ function writeJournalAndChangeEvents(rootDir: string, updates: RebuildResult[]): void {
102
+ const entries = updates.map((r) => ({
103
+ file: r.file,
104
+ deleted: r.deleted || false,
105
+ }));
106
+ try {
107
+ appendJournalEntries(rootDir, entries);
108
+ } catch (e: unknown) {
109
+ debug(`Journal write failed (non-fatal): ${(e as Error).message}`);
110
+ }
111
+
112
+ const changeEvents = updates.map((r) =>
113
+ buildChangeEvent(r.file, r.event, r.symbolDiff, {
114
+ nodesBefore: r.nodesBefore,
115
+ nodesAfter: r.nodesAfter,
116
+ edgesAdded: r.edgesAdded,
117
+ }),
118
+ );
119
+ try {
120
+ appendChangeEvents(rootDir, changeEvents);
121
+ } catch (e: unknown) {
122
+ debug(`Change event write failed (non-fatal): ${(e as Error).message}`);
123
+ }
124
+ }
125
+
126
+ /** Log rebuild results to the user. */
127
+ function logRebuildResults(updates: RebuildResult[]): void {
128
+ for (const r of updates) {
129
+ const nodeDelta = r.nodesAdded - r.nodesRemoved;
130
+ const nodeStr = nodeDelta >= 0 ? `+${nodeDelta}` : `${nodeDelta}`;
131
+ if (r.deleted) {
132
+ info(`Removed: ${r.file} (-${r.nodesRemoved} nodes)`);
133
+ } else {
134
+ info(`Updated: ${r.file} (${nodeStr} nodes, +${r.edgesAdded} edges)`);
135
+ }
136
+ }
137
+ }
138
+
139
+ /** Recursively collect tracked source files for stat-based polling. */
140
+ function collectTrackedFiles(dir: string, result: string[]): void {
141
+ let entries: fs.Dirent[];
142
+ try {
143
+ entries = fs.readdirSync(dir, { withFileTypes: true });
144
+ } catch {
145
+ return;
146
+ }
147
+ for (const entry of entries) {
148
+ if (IGNORE_DIRS.has(entry.name) || entry.name.startsWith('.')) continue;
149
+ const full = path.join(dir, entry.name);
150
+ if (entry.isDirectory()) {
151
+ collectTrackedFiles(full, result);
152
+ } else if (EXTENSIONS.has(path.extname(entry.name))) {
153
+ result.push(full);
154
+ }
155
+ }
156
+ }
157
+
158
+ export async function watchProject(
159
+ rootDir: string,
160
+ opts: { engine?: string; poll?: boolean; pollInterval?: number } = {},
161
+ ): Promise<void> {
162
+ const dbPath = path.join(rootDir, '.codegraph', 'graph.db');
163
+ if (!fs.existsSync(dbPath)) {
164
+ throw new DbError('No graph.db found. Run `codegraph build` first.', { file: dbPath });
165
+ }
166
+
167
+ const db = openDb(dbPath);
168
+ initSchema(db);
169
+ const engineOpts: import('../../types.js').EngineOpts = {
170
+ engine: (opts.engine || 'auto') as import('../../types.js').EngineMode,
171
+ dataflow: false,
172
+ ast: false,
173
+ };
174
+ const { name: engineName, version: engineVersion } = getActiveEngine(engineOpts);
175
+ info(`Watch mode using ${engineName} engine${engineVersion ? ` (v${engineVersion})` : ''}`);
176
+
177
+ const cache = createParseTreeCache();
178
+ info(
179
+ cache
180
+ ? 'Incremental parsing enabled (native tree cache)'
181
+ : 'Incremental parsing unavailable (full re-parse)',
182
+ );
183
+
184
+ const stmts = prepareWatcherStatements(db);
185
+
82
186
  const pending = new Set<string>();
83
187
  let timer: ReturnType<typeof setTimeout> | null = null;
84
188
  const DEBOUNCE_MS = 300;
85
189
 
86
- async function processPending(): Promise<void> {
87
- const files = [...pending];
88
- pending.clear();
89
-
90
- const results: Array<{
91
- file: string;
92
- deleted?: boolean;
93
- event: string;
94
- symbolDiff: unknown;
95
- nodesBefore: number;
96
- nodesAfter: number;
97
- nodesAdded: number;
98
- nodesRemoved: number;
99
- edgesAdded: number;
100
- }> = [];
101
- for (const filePath of files) {
102
- const result = (await rebuildFile(
103
- db,
104
- rootDir,
105
- filePath,
106
- stmts as IncrementalStmts,
107
- engineOpts,
108
- cache,
109
- {
110
- diffSymbols: diffSymbols as (old: unknown[], new_: unknown[]) => unknown,
111
- },
112
- )) as (typeof results)[number] | null;
113
- if (result) results.push(result);
114
- }
115
- const updates = results;
190
+ const usePoll = opts.poll ?? process.platform === 'win32';
191
+ const POLL_INTERVAL_MS = opts.pollInterval ?? 2000;
116
192
 
117
- // Append processed files to journal for Tier 0 detection on next build
118
- if (updates.length > 0) {
119
- const entries = updates.map((r) => ({
120
- file: r.file,
121
- deleted: r.deleted || false,
122
- }));
123
- try {
124
- appendJournalEntries(rootDir, entries);
125
- } catch (e: unknown) {
126
- debug(`Journal write failed (non-fatal): ${(e as Error).message}`);
127
- }
193
+ info(`Watching ${rootDir} for changes${usePoll ? ' (polling mode)' : ''}...`);
194
+ info('Press Ctrl+C to stop.');
195
+
196
+ let cleanup: () => void;
197
+
198
+ if (usePoll) {
199
+ // Polling mode: avoids native OS file watchers (NtNotifyChangeDirectoryFileEx)
200
+ // which can crash ReFS drivers on Windows Dev Drives.
201
+ const mtimeMap = new Map<string, number>();
128
202
 
129
- const changeEvents = updates.map((r) =>
130
- buildChangeEvent(r.file, r.event, r.symbolDiff, {
131
- nodesBefore: r.nodesBefore,
132
- nodesAfter: r.nodesAfter,
133
- edgesAdded: r.edgesAdded,
134
- }),
135
- );
203
+ // Seed initial mtimes
204
+ const initial: string[] = [];
205
+ collectTrackedFiles(rootDir, initial);
206
+ for (const f of initial) {
136
207
  try {
137
- appendChangeEvents(rootDir, changeEvents);
138
- } catch (e: unknown) {
139
- debug(`Change event write failed (non-fatal): ${(e as Error).message}`);
208
+ mtimeMap.set(f, fs.statSync(f).mtimeMs);
209
+ } catch {
210
+ /* deleted between collect and stat */
140
211
  }
141
212
  }
213
+ info(`Polling ${initial.length} tracked files every ${POLL_INTERVAL_MS}ms`);
214
+
215
+ const pollTimer = setInterval(() => {
216
+ const current: string[] = [];
217
+ collectTrackedFiles(rootDir, current);
218
+ const currentSet = new Set(current);
142
219
 
143
- for (const r of updates) {
144
- const nodeDelta = r.nodesAdded - r.nodesRemoved;
145
- const nodeStr = nodeDelta >= 0 ? `+${nodeDelta}` : `${nodeDelta}`;
146
- if (r.deleted) {
147
- info(`Removed: ${r.file} (-${r.nodesRemoved} nodes)`);
148
- } else {
149
- info(`Updated: ${r.file} (${nodeStr} nodes, +${r.edgesAdded} edges)`);
220
+ // Detect modified or new files
221
+ for (const f of current) {
222
+ try {
223
+ const mtime = fs.statSync(f).mtimeMs;
224
+ const prev = mtimeMap.get(f);
225
+ if (prev === undefined || mtime !== prev) {
226
+ mtimeMap.set(f, mtime);
227
+ pending.add(f);
228
+ }
229
+ } catch {
230
+ /* deleted between collect and stat */
231
+ }
150
232
  }
151
- }
152
- }
153
233
 
154
- info(`Watching ${rootDir} for changes...`);
155
- info('Press Ctrl+C to stop.');
234
+ // Detect deleted files
235
+ for (const f of mtimeMap.keys()) {
236
+ if (!currentSet.has(f)) {
237
+ mtimeMap.delete(f);
238
+ pending.add(f);
239
+ }
240
+ }
156
241
 
157
- const watcher = fs.watch(rootDir, { recursive: true }, (_eventType, filename) => {
158
- if (!filename) return;
159
- if (shouldIgnore(filename)) return;
160
- if (!isTrackedExt(filename)) return;
242
+ if (pending.size > 0) {
243
+ if (timer) clearTimeout(timer);
244
+ timer = setTimeout(async () => {
245
+ const files = [...pending];
246
+ pending.clear();
247
+ await processPendingFiles(files, db, rootDir, stmts, engineOpts, cache);
248
+ }, DEBOUNCE_MS);
249
+ }
250
+ }, POLL_INTERVAL_MS);
161
251
 
162
- const fullPath = path.join(rootDir, filename);
163
- pending.add(fullPath);
252
+ cleanup = () => clearInterval(pollTimer);
253
+ } else {
254
+ // Native OS watcher — efficient but can trigger ReFS crashes on Windows Dev Drives.
255
+ // Use --poll if you experience BSOD/HYPERVISOR_ERROR on ReFS volumes.
256
+ const watcher = fs.watch(rootDir, { recursive: true }, (_eventType, filename) => {
257
+ if (!filename) return;
258
+ if (shouldIgnore(filename)) return;
259
+ if (!isTrackedExt(filename)) return;
164
260
 
165
- if (timer) clearTimeout(timer);
166
- timer = setTimeout(processPending, DEBOUNCE_MS);
167
- });
261
+ const fullPath = path.join(rootDir, filename);
262
+ pending.add(fullPath);
263
+
264
+ if (timer) clearTimeout(timer);
265
+ timer = setTimeout(async () => {
266
+ const files = [...pending];
267
+ pending.clear();
268
+ await processPendingFiles(files, db, rootDir, stmts, engineOpts, cache);
269
+ }, DEBOUNCE_MS);
270
+ });
271
+
272
+ cleanup = () => watcher.close();
273
+ }
168
274
 
169
275
  process.on('SIGINT', () => {
170
276
  info('Stopping watcher...');
171
- watcher.close();
277
+ cleanup();
172
278
  // Flush any pending file paths to journal before exit
173
279
  if (pending.size > 0) {
174
280
  const entries = [...pending].map((filePath) => ({
@@ -17,49 +17,71 @@ import type {
17
17
  // Re-export all extractors for backward compatibility
18
18
  export {
19
19
  extractBashSymbols,
20
+ extractClojureSymbols,
20
21
  extractCppSymbols,
21
22
  extractCSharpSymbols,
22
23
  extractCSymbols,
24
+ extractCudaSymbols,
23
25
  extractDartSymbols,
24
26
  extractElixirSymbols,
27
+ extractErlangSymbols,
28
+ extractFSharpSymbols,
29
+ extractGleamSymbols,
25
30
  extractGoSymbols,
31
+ extractGroovySymbols,
26
32
  extractHaskellSymbols,
27
33
  extractHCLSymbols,
28
34
  extractJavaSymbols,
35
+ extractJuliaSymbols,
29
36
  extractKotlinSymbols,
30
37
  extractLuaSymbols,
38
+ extractObjCSymbols,
31
39
  extractOCamlSymbols,
32
40
  extractPHPSymbols,
33
41
  extractPythonSymbols,
42
+ extractRSymbols,
34
43
  extractRubySymbols,
35
44
  extractRustSymbols,
36
45
  extractScalaSymbols,
46
+ extractSoliditySymbols,
37
47
  extractSwiftSymbols,
38
48
  extractSymbols,
49
+ extractVerilogSymbols,
39
50
  extractZigSymbols,
40
51
  } from '../extractors/index.js';
41
52
 
42
53
  import {
43
54
  extractBashSymbols,
55
+ extractClojureSymbols,
44
56
  extractCppSymbols,
45
57
  extractCSharpSymbols,
46
58
  extractCSymbols,
59
+ extractCudaSymbols,
47
60
  extractDartSymbols,
48
61
  extractElixirSymbols,
62
+ extractErlangSymbols,
63
+ extractFSharpSymbols,
64
+ extractGleamSymbols,
49
65
  extractGoSymbols,
66
+ extractGroovySymbols,
50
67
  extractHaskellSymbols,
51
68
  extractHCLSymbols,
52
69
  extractJavaSymbols,
70
+ extractJuliaSymbols,
53
71
  extractKotlinSymbols,
54
72
  extractLuaSymbols,
73
+ extractObjCSymbols,
55
74
  extractOCamlSymbols,
56
75
  extractPHPSymbols,
57
76
  extractPythonSymbols,
77
+ extractRSymbols,
58
78
  extractRubySymbols,
59
79
  extractRustSymbols,
60
80
  extractScalaSymbols,
81
+ extractSoliditySymbols,
61
82
  extractSwiftSymbols,
62
83
  extractSymbols,
84
+ extractVerilogSymbols,
63
85
  extractZigSymbols,
64
86
  } from '../extractors/index.js';
65
87
 
@@ -171,39 +193,27 @@ export async function createParsers(): Promise<Map<string, Parser | null>> {
171
193
  * Call this between repeated builds in the same process (e.g. benchmarks)
172
194
  * to prevent memory accumulation that can cause segfaults.
173
195
  */
174
- export function disposeParsers(): void {
175
- if (_cachedParsers) {
176
- for (const [id, parser] of _cachedParsers) {
177
- if (parser && typeof parser.delete === 'function') {
178
- try {
179
- parser.delete();
180
- } catch (e: unknown) {
181
- debug(`Failed to dispose parser ${id}: ${(e as Error).message}`);
182
- }
183
- }
184
- }
185
- _cachedParsers = null;
186
- }
187
- for (const [id, query] of _queryCache) {
188
- if (query && typeof query.delete === 'function') {
196
+ function disposeMapEntries(entries: Iterable<[string, any]>, label: string): void {
197
+ for (const [id, item] of entries) {
198
+ if (item && typeof item.delete === 'function') {
189
199
  try {
190
- query.delete();
200
+ item.delete();
191
201
  } catch (e: unknown) {
192
- debug(`Failed to dispose query ${id}: ${(e as Error).message}`);
202
+ debug(`Failed to dispose ${label} ${id}: ${(e as Error).message}`);
193
203
  }
194
204
  }
195
205
  }
206
+ }
207
+
208
+ export function disposeParsers(): void {
209
+ if (_cachedParsers) {
210
+ disposeMapEntries(_cachedParsers, 'parser');
211
+ _cachedParsers = null;
212
+ }
213
+ disposeMapEntries(_queryCache, 'query');
196
214
  _queryCache.clear();
197
215
  if (_cachedLanguages) {
198
- for (const [id, lang] of _cachedLanguages) {
199
- if (lang && typeof (lang as any).delete === 'function') {
200
- try {
201
- (lang as any).delete();
202
- } catch (e: unknown) {
203
- debug(`Failed to dispose language ${id}: ${(e as Error).message}`);
204
- }
205
- }
206
- }
216
+ disposeMapEntries(_cachedLanguages, 'language');
207
217
  _cachedLanguages = null;
208
218
  }
209
219
  _initialized = false;
@@ -529,11 +539,95 @@ export const LANGUAGE_REGISTRY: LanguageRegistryEntry[] = [
529
539
  },
530
540
  {
531
541
  id: 'ocaml',
532
- extensions: ['.ml', '.mli'],
542
+ extensions: ['.ml'],
533
543
  grammarFile: 'tree-sitter-ocaml.wasm',
534
544
  extractor: extractOCamlSymbols,
535
545
  required: false,
536
546
  },
547
+ {
548
+ id: 'ocaml-interface',
549
+ extensions: ['.mli'],
550
+ grammarFile: 'tree-sitter-ocaml_interface.wasm',
551
+ extractor: extractOCamlSymbols,
552
+ required: false,
553
+ },
554
+ {
555
+ id: 'fsharp',
556
+ extensions: ['.fs', '.fsx', '.fsi'],
557
+ grammarFile: 'tree-sitter-fsharp.wasm',
558
+ extractor: extractFSharpSymbols,
559
+ required: false,
560
+ },
561
+ {
562
+ id: 'gleam',
563
+ extensions: ['.gleam'],
564
+ grammarFile: 'tree-sitter-gleam.wasm',
565
+ extractor: extractGleamSymbols,
566
+ required: false,
567
+ },
568
+ {
569
+ id: 'clojure',
570
+ extensions: ['.clj', '.cljs', '.cljc'],
571
+ grammarFile: 'tree-sitter-clojure.wasm',
572
+ extractor: extractClojureSymbols,
573
+ required: false,
574
+ },
575
+ {
576
+ id: 'julia',
577
+ extensions: ['.jl'],
578
+ grammarFile: 'tree-sitter-julia.wasm',
579
+ extractor: extractJuliaSymbols,
580
+ required: false,
581
+ },
582
+ {
583
+ id: 'r',
584
+ extensions: ['.r', '.R'],
585
+ grammarFile: 'tree-sitter-r.wasm',
586
+ extractor: extractRSymbols,
587
+ required: false,
588
+ },
589
+ {
590
+ id: 'erlang',
591
+ extensions: ['.erl', '.hrl'],
592
+ grammarFile: 'tree-sitter-erlang.wasm',
593
+ extractor: extractErlangSymbols,
594
+ required: false,
595
+ },
596
+ {
597
+ id: 'solidity',
598
+ extensions: ['.sol'],
599
+ grammarFile: 'tree-sitter-solidity.wasm',
600
+ extractor: extractSoliditySymbols,
601
+ required: false,
602
+ },
603
+ {
604
+ id: 'objc',
605
+ extensions: ['.m'],
606
+ grammarFile: 'tree-sitter-objc.wasm',
607
+ extractor: extractObjCSymbols,
608
+ required: false,
609
+ },
610
+ {
611
+ id: 'cuda',
612
+ extensions: ['.cu', '.cuh'],
613
+ grammarFile: 'tree-sitter-cuda.wasm',
614
+ extractor: extractCudaSymbols,
615
+ required: false,
616
+ },
617
+ {
618
+ id: 'groovy',
619
+ extensions: ['.groovy', '.gvy'],
620
+ grammarFile: 'tree-sitter-groovy.wasm',
621
+ extractor: extractGroovySymbols,
622
+ required: false,
623
+ },
624
+ {
625
+ id: 'verilog',
626
+ extensions: ['.v', '.sv'],
627
+ grammarFile: 'tree-sitter-verilog.wasm',
628
+ extractor: extractVerilogSymbols,
629
+ required: false,
630
+ },
537
631
  ];
538
632
 
539
633
  const _extToLang: Map<string, LanguageRegistryEntry> = new Map();
@@ -766,7 +860,7 @@ export function getActiveEngine(opts: ParseEngineOpts = {}): {
766
860
  */
767
861
  export function createParseTreeCache(): any {
768
862
  const native = loadNative();
769
- if (!native || !native.ParseTreeCache) return null;
863
+ if (!native?.ParseTreeCache) return null;
770
864
  return new native.ParseTreeCache();
771
865
  }
772
866
 
@@ -100,7 +100,7 @@ export async function buildEmbeddings(
100
100
  let overflowCount = 0;
101
101
 
102
102
  for (const [file, fileNodes] of byFile) {
103
- const fullPath = path.join(rootDir, file);
103
+ const fullPath = path.isAbsolute(file) ? file : path.join(rootDir, file);
104
104
  let lines: string[];
105
105
  try {
106
106
  lines = fs.readFileSync(fullPath, 'utf-8').split('\n');
@@ -96,13 +96,26 @@ export function getModelConfig(modelKey?: string): ModelConfig {
96
96
  }
97
97
 
98
98
  /**
99
- * Prompt the user to install a missing package interactively.
99
+ * Attempt to install a missing package.
100
+ * In TTY environments, prompts the user for confirmation first.
101
+ * In non-TTY environments (CI, piped stdin), installs automatically with a log message.
100
102
  * Returns true if the package was installed, false otherwise.
101
- * Skips the prompt entirely in non-TTY environments (CI, piped stdin).
102
103
  * @internal Not part of the public barrel.
103
104
  */
104
105
  export function promptInstall(packageName: string): Promise<boolean> {
105
- if (!process.stdin.isTTY) return Promise.resolve(false);
106
+ if (!process.stdin.isTTY) {
107
+ info(`Installing ${packageName} (optional dependency for semantic search)…`);
108
+ try {
109
+ execFileSync('npm', ['install', '--no-save', packageName], {
110
+ stdio: 'inherit',
111
+ timeout: 300_000,
112
+ });
113
+ return Promise.resolve(true);
114
+ } catch (err) {
115
+ info(`Auto-install failed: ${err instanceof Error ? err.message : String(err)}`);
116
+ return Promise.resolve(false);
117
+ }
118
+ }
106
119
 
107
120
  return new Promise((resolve) => {
108
121
  const rl = createInterface({ input: process.stdin, output: process.stderr });
@@ -128,7 +141,7 @@ export function promptInstall(packageName: string): Promise<boolean> {
128
141
  /**
129
142
  * Lazy-load @huggingface/transformers.
130
143
  * If the package is missing, prompts the user to install it interactively.
131
- * In non-TTY environments, prints an error and exits.
144
+ * In non-TTY environments, attempts automatic installation.
132
145
  * @internal Not part of the public barrel.
133
146
  */
134
147
  export async function loadTransformers(): Promise<unknown> {