@optave/codegraph 3.4.0 → 3.4.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 (410) hide show
  1. package/README.md +7 -7
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +3 -9
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/shared.d.ts.map +1 -1
  6. package/dist/ast-analysis/shared.js +0 -1
  7. package/dist/ast-analysis/shared.js.map +1 -1
  8. package/dist/ast-analysis/visitors/cfg-conditionals.d.ts +5 -0
  9. package/dist/ast-analysis/visitors/cfg-conditionals.d.ts.map +1 -0
  10. package/dist/ast-analysis/visitors/cfg-conditionals.js +166 -0
  11. package/dist/ast-analysis/visitors/cfg-conditionals.js.map +1 -0
  12. package/dist/ast-analysis/visitors/cfg-loops.d.ts +7 -0
  13. package/dist/ast-analysis/visitors/cfg-loops.d.ts.map +1 -0
  14. package/dist/ast-analysis/visitors/cfg-loops.js +73 -0
  15. package/dist/ast-analysis/visitors/cfg-loops.js.map +1 -0
  16. package/dist/ast-analysis/visitors/cfg-shared.d.ts +56 -0
  17. package/dist/ast-analysis/visitors/cfg-shared.d.ts.map +1 -0
  18. package/dist/ast-analysis/visitors/cfg-shared.js +107 -0
  19. package/dist/ast-analysis/visitors/cfg-shared.js.map +1 -0
  20. package/dist/ast-analysis/visitors/cfg-try-catch.d.ts +4 -0
  21. package/dist/ast-analysis/visitors/cfg-try-catch.d.ts.map +1 -0
  22. package/dist/ast-analysis/visitors/cfg-try-catch.js +100 -0
  23. package/dist/ast-analysis/visitors/cfg-try-catch.js.map +1 -0
  24. package/dist/ast-analysis/visitors/cfg-visitor.d.ts +2 -2
  25. package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
  26. package/dist/ast-analysis/visitors/cfg-visitor.js +11 -445
  27. package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -1
  28. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  29. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  30. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  31. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  32. package/dist/cli/commands/batch.d.ts.map +1 -1
  33. package/dist/cli/commands/batch.js +4 -3
  34. package/dist/cli/commands/batch.js.map +1 -1
  35. package/dist/cli/commands/branch-compare.js +1 -1
  36. package/dist/cli/commands/branch-compare.js.map +1 -1
  37. package/dist/cli/commands/build.js +1 -1
  38. package/dist/cli/commands/build.js.map +1 -1
  39. package/dist/cli/commands/info.d.ts.map +1 -1
  40. package/dist/cli/commands/info.js +1 -2
  41. package/dist/cli/commands/info.js.map +1 -1
  42. package/dist/cli/commands/path.d.ts.map +1 -1
  43. package/dist/cli/commands/path.js +7 -2
  44. package/dist/cli/commands/path.js.map +1 -1
  45. package/dist/cli/commands/plot.d.ts.map +1 -1
  46. package/dist/cli/commands/plot.js +2 -2
  47. package/dist/cli/commands/plot.js.map +1 -1
  48. package/dist/cli/commands/watch.js +1 -1
  49. package/dist/cli/commands/watch.js.map +1 -1
  50. package/dist/cli/index.js +2 -2
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/cli/shared/open-graph.d.ts +2 -2
  53. package/dist/cli/shared/open-graph.d.ts.map +1 -1
  54. package/dist/cli/shared/open-graph.js.map +1 -1
  55. package/dist/cli/types.d.ts +1 -1
  56. package/dist/cli/types.d.ts.map +1 -1
  57. package/dist/cli.js +2 -3
  58. package/dist/cli.js.map +1 -1
  59. package/dist/db/connection.d.ts +17 -0
  60. package/dist/db/connection.d.ts.map +1 -1
  61. package/dist/db/connection.js +91 -2
  62. package/dist/db/connection.js.map +1 -1
  63. package/dist/db/index.d.ts +1 -1
  64. package/dist/db/index.d.ts.map +1 -1
  65. package/dist/db/index.js +1 -1
  66. package/dist/db/index.js.map +1 -1
  67. package/dist/db/migrations.d.ts.map +1 -1
  68. package/dist/db/migrations.js +7 -0
  69. package/dist/db/migrations.js.map +1 -1
  70. package/dist/domain/analysis/brief.d.ts.map +1 -1
  71. package/dist/domain/analysis/brief.js +1 -3
  72. package/dist/domain/analysis/brief.js.map +1 -1
  73. package/dist/domain/analysis/context.d.ts.map +1 -1
  74. package/dist/domain/analysis/context.js +2 -4
  75. package/dist/domain/analysis/context.js.map +1 -1
  76. package/dist/domain/analysis/dependencies.d.ts +49 -0
  77. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  78. package/dist/domain/analysis/dependencies.js +145 -0
  79. package/dist/domain/analysis/dependencies.js.map +1 -1
  80. package/dist/domain/analysis/diff-impact.d.ts +76 -0
  81. package/dist/domain/analysis/diff-impact.d.ts.map +1 -0
  82. package/dist/domain/analysis/diff-impact.js +282 -0
  83. package/dist/domain/analysis/diff-impact.js.map +1 -0
  84. package/dist/domain/analysis/exports.d.ts.map +1 -1
  85. package/dist/domain/analysis/exports.js +0 -1
  86. package/dist/domain/analysis/exports.js.map +1 -1
  87. package/dist/domain/analysis/fn-impact.d.ts +66 -0
  88. package/dist/domain/analysis/fn-impact.d.ts.map +1 -0
  89. package/dist/domain/analysis/fn-impact.js +189 -0
  90. package/dist/domain/analysis/fn-impact.js.map +1 -0
  91. package/dist/domain/analysis/impact.d.ts +8 -148
  92. package/dist/domain/analysis/impact.d.ts.map +1 -1
  93. package/dist/domain/analysis/impact.js +8 -568
  94. package/dist/domain/analysis/impact.js.map +1 -1
  95. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  96. package/dist/domain/analysis/module-map.js +1 -3
  97. package/dist/domain/analysis/module-map.js.map +1 -1
  98. package/dist/domain/graph/builder/context.d.ts +2 -3
  99. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  100. package/dist/domain/graph/builder/context.js.map +1 -1
  101. package/dist/domain/graph/builder/helpers.d.ts +4 -5
  102. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  103. package/dist/domain/graph/builder/helpers.js +1 -2
  104. package/dist/domain/graph/builder/helpers.js.map +1 -1
  105. package/dist/domain/graph/builder/incremental.d.ts +2 -3
  106. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  107. package/dist/domain/graph/builder/incremental.js.map +1 -1
  108. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  109. package/dist/domain/graph/builder/pipeline.js +6 -0
  110. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  111. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  112. package/dist/domain/graph/builder/stages/build-edges.js +12 -2
  113. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  114. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  115. package/dist/domain/graph/builder/stages/build-structure.js +155 -59
  116. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  117. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  118. package/dist/domain/graph/builder/stages/detect-changes.js +6 -6
  119. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  120. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  121. package/dist/domain/graph/builder/stages/finalize.js +85 -61
  122. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  123. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  124. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  125. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  126. package/dist/domain/graph/builder/stages/resolve-imports.js +58 -11
  127. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  128. package/dist/domain/graph/cycles.js +2 -2
  129. package/dist/domain/graph/cycles.js.map +1 -1
  130. package/dist/domain/graph/resolve.d.ts.map +1 -1
  131. package/dist/domain/graph/resolve.js +10 -8
  132. package/dist/domain/graph/resolve.js.map +1 -1
  133. package/dist/domain/graph/watcher.d.ts.map +1 -1
  134. package/dist/domain/graph/watcher.js +1 -3
  135. package/dist/domain/graph/watcher.js.map +1 -1
  136. package/dist/domain/parser.d.ts.map +1 -1
  137. package/dist/domain/parser.js +11 -12
  138. package/dist/domain/parser.js.map +1 -1
  139. package/dist/domain/queries.d.ts +3 -2
  140. package/dist/domain/queries.d.ts.map +1 -1
  141. package/dist/domain/queries.js +3 -2
  142. package/dist/domain/queries.js.map +1 -1
  143. package/dist/domain/search/generator.d.ts.map +1 -1
  144. package/dist/domain/search/generator.js.map +1 -1
  145. package/dist/extractors/csharp.js +2 -2
  146. package/dist/extractors/csharp.js.map +1 -1
  147. package/dist/extractors/go.js +2 -2
  148. package/dist/extractors/go.js.map +1 -1
  149. package/dist/extractors/helpers.d.ts +5 -0
  150. package/dist/extractors/helpers.d.ts.map +1 -1
  151. package/dist/extractors/helpers.js +5 -0
  152. package/dist/extractors/helpers.js.map +1 -1
  153. package/dist/extractors/javascript.js +58 -60
  154. package/dist/extractors/javascript.js.map +1 -1
  155. package/dist/extractors/php.js +2 -2
  156. package/dist/extractors/php.js.map +1 -1
  157. package/dist/extractors/python.js +2 -2
  158. package/dist/extractors/python.js.map +1 -1
  159. package/dist/extractors/rust.js +2 -2
  160. package/dist/extractors/rust.js.map +1 -1
  161. package/dist/features/audit.d.ts.map +1 -1
  162. package/dist/features/audit.js +1 -2
  163. package/dist/features/audit.js.map +1 -1
  164. package/dist/features/branch-compare.d.ts.map +1 -1
  165. package/dist/features/branch-compare.js +2 -3
  166. package/dist/features/branch-compare.js.map +1 -1
  167. package/dist/features/cfg.d.ts.map +1 -1
  168. package/dist/features/cfg.js +2 -4
  169. package/dist/features/cfg.js.map +1 -1
  170. package/dist/features/cochange.js +4 -4
  171. package/dist/features/cochange.js.map +1 -1
  172. package/dist/features/communities.js +4 -4
  173. package/dist/features/communities.js.map +1 -1
  174. package/dist/features/complexity-query.d.ts +37 -0
  175. package/dist/features/complexity-query.d.ts.map +1 -0
  176. package/dist/features/complexity-query.js +263 -0
  177. package/dist/features/complexity-query.js.map +1 -0
  178. package/dist/features/complexity.d.ts +2 -30
  179. package/dist/features/complexity.d.ts.map +1 -1
  180. package/dist/features/complexity.js +7 -261
  181. package/dist/features/complexity.js.map +1 -1
  182. package/dist/features/dataflow.d.ts.map +1 -1
  183. package/dist/features/dataflow.js +8 -24
  184. package/dist/features/dataflow.js.map +1 -1
  185. package/dist/features/export.d.ts +7 -8
  186. package/dist/features/export.d.ts.map +1 -1
  187. package/dist/features/export.js.map +1 -1
  188. package/dist/features/flow.d.ts.map +1 -1
  189. package/dist/features/flow.js.map +1 -1
  190. package/dist/features/graph-enrichment.d.ts.map +1 -1
  191. package/dist/features/graph-enrichment.js +1 -3
  192. package/dist/features/graph-enrichment.js.map +1 -1
  193. package/dist/features/manifesto.js +8 -8
  194. package/dist/features/manifesto.js.map +1 -1
  195. package/dist/features/snapshot.d.ts.map +1 -1
  196. package/dist/features/snapshot.js +0 -1
  197. package/dist/features/snapshot.js.map +1 -1
  198. package/dist/features/structure-query.d.ts +76 -0
  199. package/dist/features/structure-query.d.ts.map +1 -0
  200. package/dist/features/structure-query.js +245 -0
  201. package/dist/features/structure-query.js.map +1 -0
  202. package/dist/features/structure.d.ts +12 -67
  203. package/dist/features/structure.d.ts.map +1 -1
  204. package/dist/features/structure.js +188 -244
  205. package/dist/features/structure.js.map +1 -1
  206. package/dist/features/triage.js +2 -2
  207. package/dist/features/triage.js.map +1 -1
  208. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  209. package/dist/graph/algorithms/leiden/adapter.js +2 -9
  210. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  211. package/dist/graph/classifiers/roles.d.ts +5 -1
  212. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  213. package/dist/graph/classifiers/roles.js +20 -12
  214. package/dist/graph/classifiers/roles.js.map +1 -1
  215. package/dist/index.d.ts +1 -0
  216. package/dist/index.d.ts.map +1 -1
  217. package/dist/index.js.map +1 -1
  218. package/dist/infrastructure/config.d.ts.map +1 -1
  219. package/dist/infrastructure/config.js +12 -11
  220. package/dist/infrastructure/config.js.map +1 -1
  221. package/dist/infrastructure/native.d.ts.map +1 -1
  222. package/dist/infrastructure/native.js +7 -3
  223. package/dist/infrastructure/native.js.map +1 -1
  224. package/dist/infrastructure/registry.d.ts.map +1 -1
  225. package/dist/infrastructure/registry.js +1 -1
  226. package/dist/infrastructure/registry.js.map +1 -1
  227. package/dist/infrastructure/update-check.js +3 -3
  228. package/dist/infrastructure/update-check.js.map +1 -1
  229. package/dist/mcp/server.d.ts.map +1 -1
  230. package/dist/mcp/server.js +2 -8
  231. package/dist/mcp/server.js.map +1 -1
  232. package/dist/mcp/tool-registry.d.ts.map +1 -1
  233. package/dist/mcp/tool-registry.js +9 -4
  234. package/dist/mcp/tool-registry.js.map +1 -1
  235. package/dist/mcp/tools/audit.js +1 -1
  236. package/dist/mcp/tools/audit.js.map +1 -1
  237. package/dist/mcp/tools/cfg.js +1 -1
  238. package/dist/mcp/tools/cfg.js.map +1 -1
  239. package/dist/mcp/tools/check.js +2 -2
  240. package/dist/mcp/tools/check.js.map +1 -1
  241. package/dist/mcp/tools/dataflow.js +2 -2
  242. package/dist/mcp/tools/dataflow.js.map +1 -1
  243. package/dist/mcp/tools/export-graph.js +1 -1
  244. package/dist/mcp/tools/export-graph.js.map +1 -1
  245. package/dist/mcp/tools/index.d.ts.map +1 -1
  246. package/dist/mcp/tools/index.js.map +1 -1
  247. package/dist/mcp/tools/path.d.ts +1 -0
  248. package/dist/mcp/tools/path.d.ts.map +1 -1
  249. package/dist/mcp/tools/path.js +9 -0
  250. package/dist/mcp/tools/path.js.map +1 -1
  251. package/dist/mcp/tools/query.js +1 -1
  252. package/dist/mcp/tools/query.js.map +1 -1
  253. package/dist/mcp/tools/semantic-search.js +1 -1
  254. package/dist/mcp/tools/semantic-search.js.map +1 -1
  255. package/dist/mcp/tools/sequence.js +1 -1
  256. package/dist/mcp/tools/sequence.js.map +1 -1
  257. package/dist/mcp/tools/symbol-children.js +1 -1
  258. package/dist/mcp/tools/symbol-children.js.map +1 -1
  259. package/dist/mcp/tools/triage.js +1 -1
  260. package/dist/mcp/tools/triage.js.map +1 -1
  261. package/dist/presentation/audit.d.ts.map +1 -1
  262. package/dist/presentation/audit.js +0 -1
  263. package/dist/presentation/audit.js.map +1 -1
  264. package/dist/presentation/diff-impact-mermaid.d.ts +11 -0
  265. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -0
  266. package/dist/presentation/diff-impact-mermaid.js +105 -0
  267. package/dist/presentation/diff-impact-mermaid.js.map +1 -0
  268. package/dist/presentation/flow.d.ts.map +1 -1
  269. package/dist/presentation/flow.js +0 -2
  270. package/dist/presentation/flow.js.map +1 -1
  271. package/dist/presentation/manifesto.d.ts.map +1 -1
  272. package/dist/presentation/manifesto.js +0 -1
  273. package/dist/presentation/manifesto.js.map +1 -1
  274. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  275. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  276. package/dist/presentation/queries-cli/path.d.ts.map +1 -1
  277. package/dist/presentation/queries-cli/path.js +45 -1
  278. package/dist/presentation/queries-cli/path.js.map +1 -1
  279. package/dist/presentation/result-formatter.d.ts.map +1 -1
  280. package/dist/presentation/result-formatter.js +1 -3
  281. package/dist/presentation/result-formatter.js.map +1 -1
  282. package/dist/presentation/sequence.d.ts.map +1 -1
  283. package/dist/presentation/sequence.js +0 -1
  284. package/dist/presentation/sequence.js.map +1 -1
  285. package/dist/presentation/structure.d.ts.map +1 -1
  286. package/dist/presentation/structure.js.map +1 -1
  287. package/dist/presentation/triage.d.ts.map +1 -1
  288. package/dist/presentation/triage.js +0 -1
  289. package/dist/presentation/triage.js.map +1 -1
  290. package/dist/shared/constants.d.ts +9 -3
  291. package/dist/shared/constants.d.ts.map +1 -1
  292. package/dist/shared/constants.js +6 -3
  293. package/dist/shared/constants.js.map +1 -1
  294. package/dist/shared/errors.d.ts +2 -0
  295. package/dist/shared/errors.d.ts.map +1 -1
  296. package/dist/shared/errors.js +4 -0
  297. package/dist/shared/errors.js.map +1 -1
  298. package/dist/shared/version.d.ts +2 -0
  299. package/dist/shared/version.d.ts.map +1 -0
  300. package/dist/shared/version.js +5 -0
  301. package/dist/shared/version.js.map +1 -0
  302. package/dist/types.d.ts +2 -2
  303. package/dist/types.d.ts.map +1 -1
  304. package/package.json +8 -7
  305. package/src/ast-analysis/engine.ts +3 -9
  306. package/src/ast-analysis/shared.ts +0 -1
  307. package/src/ast-analysis/visitors/cfg-conditionals.ts +227 -0
  308. package/src/ast-analysis/visitors/cfg-loops.ts +136 -0
  309. package/src/ast-analysis/visitors/cfg-shared.ts +196 -0
  310. package/src/ast-analysis/visitors/cfg-try-catch.ts +142 -0
  311. package/src/ast-analysis/visitors/cfg-visitor.ts +34 -655
  312. package/src/ast-analysis/visitors/complexity-visitor.ts +0 -1
  313. package/src/ast-analysis/visitors/dataflow-visitor.ts +0 -1
  314. package/src/cli/commands/batch.ts +4 -3
  315. package/src/cli/commands/branch-compare.ts +1 -1
  316. package/src/cli/commands/build.ts +1 -1
  317. package/src/cli/commands/info.ts +1 -2
  318. package/src/cli/commands/path.ts +7 -2
  319. package/src/cli/commands/plot.ts +2 -2
  320. package/src/cli/commands/watch.ts +1 -1
  321. package/src/cli/index.ts +2 -2
  322. package/src/cli/shared/open-graph.ts +2 -2
  323. package/src/cli/types.ts +1 -1
  324. package/src/cli.ts +2 -3
  325. package/src/db/connection.ts +97 -13
  326. package/src/db/index.ts +2 -0
  327. package/src/db/migrations.ts +7 -0
  328. package/src/domain/analysis/brief.ts +0 -1
  329. package/src/domain/analysis/context.ts +2 -6
  330. package/src/domain/analysis/dependencies.ts +165 -0
  331. package/src/domain/analysis/diff-impact.ts +354 -0
  332. package/src/domain/analysis/exports.ts +0 -2
  333. package/src/domain/analysis/fn-impact.ts +241 -0
  334. package/src/domain/analysis/impact.ts +8 -718
  335. package/src/domain/analysis/module-map.ts +1 -5
  336. package/src/domain/graph/builder/context.ts +2 -2
  337. package/src/domain/graph/builder/helpers.ts +14 -11
  338. package/src/domain/graph/builder/incremental.ts +33 -28
  339. package/src/domain/graph/builder/pipeline.ts +8 -0
  340. package/src/domain/graph/builder/stages/build-edges.ts +17 -4
  341. package/src/domain/graph/builder/stages/build-structure.ts +205 -76
  342. package/src/domain/graph/builder/stages/detect-changes.ts +11 -12
  343. package/src/domain/graph/builder/stages/finalize.ts +100 -81
  344. package/src/domain/graph/builder/stages/insert-nodes.ts +12 -8
  345. package/src/domain/graph/builder/stages/resolve-imports.ts +75 -10
  346. package/src/domain/graph/cycles.ts +2 -2
  347. package/src/domain/graph/resolve.ts +14 -8
  348. package/src/domain/graph/watcher.ts +2 -4
  349. package/src/domain/parser.ts +11 -13
  350. package/src/domain/queries.ts +2 -2
  351. package/src/domain/search/generator.ts +3 -4
  352. package/src/extractors/csharp.ts +2 -2
  353. package/src/extractors/go.ts +2 -2
  354. package/src/extractors/helpers.ts +6 -0
  355. package/src/extractors/javascript.ts +58 -61
  356. package/src/extractors/php.ts +2 -2
  357. package/src/extractors/python.ts +2 -2
  358. package/src/extractors/rust.ts +2 -2
  359. package/src/features/audit.ts +1 -2
  360. package/src/features/branch-compare.ts +3 -9
  361. package/src/features/cfg.ts +2 -4
  362. package/src/features/cochange.ts +4 -4
  363. package/src/features/communities.ts +4 -4
  364. package/src/features/complexity-query.ts +370 -0
  365. package/src/features/complexity.ts +6 -365
  366. package/src/features/dataflow.ts +48 -70
  367. package/src/features/export.ts +12 -16
  368. package/src/features/flow.ts +0 -1
  369. package/src/features/graph-enrichment.ts +1 -3
  370. package/src/features/manifesto.ts +8 -8
  371. package/src/features/snapshot.ts +1 -2
  372. package/src/features/structure-query.ts +387 -0
  373. package/src/features/structure.ts +231 -376
  374. package/src/features/triage.ts +2 -2
  375. package/src/graph/algorithms/leiden/adapter.ts +2 -9
  376. package/src/graph/classifiers/roles.ts +22 -13
  377. package/src/index.ts +1 -0
  378. package/src/infrastructure/config.ts +12 -13
  379. package/src/infrastructure/native.ts +7 -3
  380. package/src/infrastructure/registry.ts +1 -1
  381. package/src/infrastructure/update-check.ts +3 -3
  382. package/src/mcp/server.ts +2 -10
  383. package/src/mcp/tool-registry.ts +11 -4
  384. package/src/mcp/tools/audit.ts +1 -1
  385. package/src/mcp/tools/cfg.ts +1 -1
  386. package/src/mcp/tools/check.ts +2 -2
  387. package/src/mcp/tools/dataflow.ts +2 -2
  388. package/src/mcp/tools/export-graph.ts +1 -1
  389. package/src/mcp/tools/index.ts +0 -1
  390. package/src/mcp/tools/path.ts +10 -0
  391. package/src/mcp/tools/query.ts +1 -1
  392. package/src/mcp/tools/semantic-search.ts +1 -1
  393. package/src/mcp/tools/sequence.ts +1 -1
  394. package/src/mcp/tools/symbol-children.ts +1 -1
  395. package/src/mcp/tools/triage.ts +1 -1
  396. package/src/presentation/audit.ts +0 -1
  397. package/src/presentation/diff-impact-mermaid.ts +127 -0
  398. package/src/presentation/flow.ts +0 -2
  399. package/src/presentation/manifesto.ts +0 -1
  400. package/src/presentation/queries-cli/inspect.ts +0 -1
  401. package/src/presentation/queries-cli/path.ts +71 -1
  402. package/src/presentation/result-formatter.ts +0 -1
  403. package/src/presentation/sequence.ts +0 -1
  404. package/src/presentation/structure.ts +0 -12
  405. package/src/presentation/triage.ts +0 -1
  406. package/src/shared/constants.ts +33 -19
  407. package/src/shared/errors.ts +5 -0
  408. package/src/shared/version.ts +10 -0
  409. package/src/types.ts +4 -10
  410. package/src/vendor.d.ts +0 -39
@@ -5,7 +5,6 @@ import {
5
5
  computeMaintainabilityIndex,
6
6
  } from '../metrics.js';
7
7
 
8
- // biome-ignore lint/suspicious/noExplicitAny: complexity/halstead rules are opaque language-specific objects
9
8
  type AnyRules = any;
10
9
 
11
10
  interface ComplexityAcc {
@@ -10,7 +10,6 @@ import {
10
10
  truncate,
11
11
  } from '../visitor-utils.js';
12
12
 
13
- // biome-ignore lint/suspicious/noExplicitAny: dataflow rules are opaque language-specific objects
14
13
  type AnyRules = any;
15
14
 
16
15
  interface ScopeEntry {
@@ -3,7 +3,7 @@ import { collectFile } from '../../db/query-builder.js';
3
3
  import { EVERY_SYMBOL_KIND } from '../../domain/queries.js';
4
4
  import { BATCH_COMMANDS, multiBatchData, splitTargets } from '../../features/batch.js';
5
5
  import { batch } from '../../presentation/batch.js';
6
- import { ConfigError } from '../../shared/errors.js';
6
+ import { ConfigError, toErrorMessage } from '../../shared/errors.js';
7
7
  import type { CommandDefinition } from '../types.js';
8
8
 
9
9
  interface MultiBatchItem {
@@ -58,8 +58,9 @@ export const command: CommandDefinition = {
58
58
  targets = splitTargets(positionalTargets as unknown as string[]);
59
59
  }
60
60
  } catch (err: unknown) {
61
- const message = err instanceof Error ? err.message : String(err);
62
- throw new ConfigError(`Failed to parse targets: ${message}`, { cause: err as Error });
61
+ throw new ConfigError(`Failed to parse targets: ${toErrorMessage(err)}`, {
62
+ cause: err as Error,
63
+ });
63
64
  }
64
65
 
65
66
  if (!targets || targets.length === 0) {
@@ -13,7 +13,7 @@ export const command: CommandDefinition = {
13
13
  async execute([base, target], opts, ctx) {
14
14
  const { branchCompare } = await import('../../presentation/branch-compare.js');
15
15
  await branchCompare(base!, target!, {
16
- engine: ctx.program.opts()['engine'],
16
+ engine: ctx.program.opts().engine,
17
17
  depth: parseInt(opts.depth as string, 10),
18
18
  noTests: ctx.resolveNoTests(opts),
19
19
  json: opts.json,
@@ -15,7 +15,7 @@ export const command: CommandDefinition = {
15
15
  ],
16
16
  async execute([dir], opts, ctx) {
17
17
  const root = path.resolve(dir || '.');
18
- const engine = ctx.program.opts()['engine'];
18
+ const engine = ctx.program.opts().engine;
19
19
  await buildGraph(root, {
20
20
  incremental: opts.incremental as boolean,
21
21
  ast: opts.ast as boolean,
@@ -9,7 +9,7 @@ export const command: CommandDefinition = {
9
9
  );
10
10
  const { getActiveEngine } = await import('../../domain/parser.js');
11
11
 
12
- const engine = ctx.program.opts()['engine'];
12
+ const engine = ctx.program.opts().engine;
13
13
  const { name: activeName, version: activeVersion } = getActiveEngine({ engine });
14
14
  const nativeAvailable = isNativeAvailable();
15
15
 
@@ -43,7 +43,6 @@ export const command: CommandDefinition = {
43
43
  const dbPath = findDbPath();
44
44
  const fs = await import('node:fs');
45
45
  if (fs.existsSync(dbPath)) {
46
- // @ts-expect-error -- better-sqlite3 default export typing
47
46
  const db = new Database(dbPath, { readonly: true });
48
47
  const buildEngine = getBuildMeta(db, 'engine');
49
48
  const buildVersion = getBuildMeta(db, 'codegraph_version');
@@ -4,11 +4,15 @@ import type { CommandDefinition } from '../types.js';
4
4
 
5
5
  export const command: CommandDefinition = {
6
6
  name: 'path <from> <to>',
7
- description: 'Find shortest path between two symbols',
7
+ description: 'Find shortest path between two symbols (or files with --file)',
8
8
  options: [
9
9
  ['-d, --db <path>', 'Path to graph.db'],
10
+ ['-f, --file', 'Treat <from> and <to> as file paths instead of symbol names'],
10
11
  ['--reverse', 'Follow edges backward'],
11
- ['--kinds <kinds>', 'Comma-separated edge kinds to follow (default: calls)'],
12
+ [
13
+ '--kinds <kinds>',
14
+ 'Comma-separated edge kinds to follow (default: calls; file mode: imports,imports-type)',
15
+ ],
12
16
  ['--from-file <path>', 'Disambiguate source symbol by file'],
13
17
  ['--to-file <path>', 'Disambiguate target symbol by file'],
14
18
  ['--depth <n>', 'Max traversal depth', '10'],
@@ -32,6 +36,7 @@ export const command: CommandDefinition = {
32
36
  kind: opts.kind,
33
37
  noTests: ctx.resolveNoTests(opts),
34
38
  json: opts.json,
39
+ file: opts.file,
35
40
  });
36
41
  },
37
42
  };
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { toErrorMessage } from '../../shared/errors.js';
3
4
  import { openGraph } from '../shared/open-graph.js';
4
5
  import type { CommandDefinition } from '../types.js';
5
6
 
@@ -51,8 +52,7 @@ export const command: CommandDefinition = {
51
52
  try {
52
53
  plotCfg = JSON.parse(fs.readFileSync(opts.config as string, 'utf-8')) as PlotConfig;
53
54
  } catch (e: unknown) {
54
- const message = e instanceof Error ? e.message : String(e);
55
- console.error(`Failed to load config: ${message}`);
55
+ console.error(`Failed to load config: ${toErrorMessage(e)}`);
56
56
  process.exitCode = 1;
57
57
  return;
58
58
  }
@@ -7,7 +7,7 @@ export const command: CommandDefinition = {
7
7
  description: 'Watch project for file changes and incrementally update the graph',
8
8
  async execute([dir], _opts, ctx) {
9
9
  const root = path.resolve(dir || '.');
10
- const engine = ctx.program.opts()['engine'];
10
+ const engine = ctx.program.opts().engine;
11
11
  await watchProject(root, { engine });
12
12
  },
13
13
  };
package/src/cli/index.ts CHANGED
@@ -27,12 +27,12 @@ program
27
27
  .option('--engine <engine>', 'Parser engine: native, wasm, or auto (default: auto)', 'auto')
28
28
  .hook('preAction', (thisCommand) => {
29
29
  const opts = thisCommand.opts();
30
- if (opts['verbose']) setVerbose(true);
30
+ if (opts.verbose) setVerbose(true);
31
31
  })
32
32
  .hook('postAction', async (_thisCommand, actionCommand) => {
33
33
  const name = actionCommand.name();
34
34
  if (name === 'mcp' || name === 'watch') return;
35
- if (actionCommand.opts()['json']) return;
35
+ if (actionCommand.opts().json) return;
36
36
  try {
37
37
  const result = await checkForUpdates(pkg.version);
38
38
  if (result) printUpdateNotification(result.current, result.latest);
@@ -1,11 +1,11 @@
1
- import type Database from 'better-sqlite3';
2
1
  import { openReadonlyOrFail } from '../../db/index.js';
2
+ import type { BetterSqlite3Database } from '../../types.js';
3
3
 
4
4
  /**
5
5
  * Open the graph database in readonly mode with a clean close() handle.
6
6
  */
7
7
  export function openGraph(opts: { db?: string } = {}): {
8
- db: Database.Database;
8
+ db: BetterSqlite3Database;
9
9
  close: () => void;
10
10
  } {
11
11
  const db = openReadonlyOrFail(opts.db);
package/src/cli/types.ts CHANGED
@@ -27,7 +27,7 @@ export interface CommandDefinition {
27
27
  description: string;
28
28
  queryOpts?: boolean;
29
29
  options?: Array<[string, string, ...unknown[]]>;
30
- validate?(args: string[], opts: CommandOpts, ctx: CliContext): string | void;
30
+ validate?(args: string[], opts: CommandOpts, ctx: CliContext): string | undefined;
31
31
  execute?(args: string[], opts: CommandOpts, ctx: CliContext): void | Promise<void>;
32
32
  subcommands?: CommandDefinition[];
33
33
  }
package/src/cli.ts CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { run } from './cli/index.js';
4
- import { CodegraphError } from './shared/errors.js';
4
+ import { CodegraphError, toErrorMessage } from './shared/errors.js';
5
5
 
6
6
  run().catch((err: unknown) => {
7
7
  if (err instanceof CodegraphError) {
8
8
  console.error(`codegraph [${err.code}]: ${err.message}`);
9
9
  if (err.file) console.error(` file: ${err.file}`);
10
10
  } else {
11
- const message = err instanceof Error ? err.message : String(err);
12
- console.error(`codegraph: fatal error — ${message}`);
11
+ console.error(`codegraph: fatal error ${toErrorMessage(err)}`);
13
12
  }
14
13
  process.exit(1);
15
14
  });
@@ -1,6 +1,7 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
4
5
  import Database from 'better-sqlite3';
5
6
  import { debug, warn } from '../infrastructure/logger.js';
6
7
  import { DbError } from '../shared/errors.js';
@@ -8,6 +9,24 @@ import type { BetterSqlite3Database } from '../types.js';
8
9
  import { Repository } from './repository/base.js';
9
10
  import { SqliteRepository } from './repository/sqlite-repository.js';
10
11
 
12
+ /** Lazy-loaded package version (read once from package.json). */
13
+ let _packageVersion: string | undefined;
14
+ function getPackageVersion(): string {
15
+ if (_packageVersion !== undefined) return _packageVersion;
16
+ try {
17
+ const connDir = path.dirname(fileURLToPath(import.meta.url));
18
+ const pkgPath = path.join(connDir, '..', '..', 'package.json');
19
+ _packageVersion = (JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { version: string })
20
+ .version;
21
+ } catch {
22
+ _packageVersion = '';
23
+ }
24
+ return _packageVersion;
25
+ }
26
+
27
+ /** Warn once per process when DB version mismatches the running codegraph version. */
28
+ let _versionWarned = false;
29
+
11
30
  /** DB instance with optional advisory lock path. */
12
31
  export type LockedDatabase = BetterSqlite3Database & { __lockPath?: string };
13
32
 
@@ -60,6 +79,11 @@ export function _resetRepoRootCache(): void {
60
79
  _cachedRepoRootCwd = undefined;
61
80
  }
62
81
 
82
+ /** Reset the version warning flag (for testing). */
83
+ export function _resetVersionWarning(): void {
84
+ _versionWarned = false;
85
+ }
86
+
63
87
  function isProcessAlive(pid: number): boolean {
64
88
  try {
65
89
  process.kill(pid, 0);
@@ -119,16 +143,12 @@ function isSameDirectory(a: string, b: string): boolean {
119
143
  }
120
144
 
121
145
  export function openDb(dbPath: string): LockedDatabase {
146
+ // Flush any deferred DB close from a previous build (avoids WAL contention)
147
+ flushDeferredClose();
122
148
  const dir = path.dirname(dbPath);
123
149
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
124
150
  acquireAdvisoryLock(dbPath);
125
- // vendor.d.ts declares Database as a callable; cast through unknown for construct usage
126
- const db = new (
127
- Database as unknown as new (
128
- path: string,
129
- opts?: Record<string, unknown>,
130
- ) => LockedDatabase
131
- )(dbPath);
151
+ const db = new Database(dbPath) as unknown as LockedDatabase;
132
152
  db.pragma('journal_mode = WAL');
133
153
  db.pragma('busy_timeout = 5000');
134
154
  db.__lockPath = `${dbPath}.lock`;
@@ -140,6 +160,54 @@ export function closeDb(db: LockedDatabase): void {
140
160
  if (db.__lockPath) releaseAdvisoryLock(db.__lockPath);
141
161
  }
142
162
 
163
+ /** Pending deferred-close DB handles (not yet closed). */
164
+ const _deferredDbs: LockedDatabase[] = [];
165
+
166
+ /**
167
+ * Synchronously close any DB handles queued by `closeDbDeferred()`.
168
+ * Call before deleting DB files or in test teardown to avoid EBUSY on Windows.
169
+ */
170
+ export function flushDeferredClose(): void {
171
+ while (_deferredDbs.length > 0) {
172
+ const db = _deferredDbs.pop()!;
173
+ try {
174
+ db.close();
175
+ } catch {
176
+ /* ignore — handle may already be closed */
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Schedule DB close on the next event loop tick. Useful for incremental
183
+ * builds where the WAL checkpoint in db.close() is expensive (~250ms on
184
+ * Windows) and doesn't need to block the caller.
185
+ *
186
+ * The advisory lock is released immediately so subsequent opens succeed.
187
+ * The actual handle close (+ WAL checkpoint) happens asynchronously.
188
+ * Call `flushDeferredClose()` before deleting the DB file.
189
+ */
190
+ export function closeDbDeferred(db: LockedDatabase): void {
191
+ // Release the advisory lock immediately so the next open can proceed
192
+ if (db.__lockPath) {
193
+ releaseAdvisoryLock(db.__lockPath);
194
+ db.__lockPath = undefined;
195
+ }
196
+ _deferredDbs.push(db);
197
+ // Defer the expensive WAL checkpoint to after the caller returns
198
+ setImmediate(() => {
199
+ const idx = _deferredDbs.indexOf(db);
200
+ if (idx !== -1) {
201
+ _deferredDbs.splice(idx, 1);
202
+ try {
203
+ db.close();
204
+ } catch {
205
+ /* ignore — handle may already be closed by flush */
206
+ }
207
+ }
208
+ });
209
+ }
210
+
143
211
  export function findDbPath(customPath?: string): string {
144
212
  if (customPath) return path.resolve(customPath);
145
213
  const rawCeiling = findRepoRoot();
@@ -190,12 +258,28 @@ export function openReadonlyOrFail(customPath?: string): BetterSqlite3Database {
190
258
  { file: dbPath },
191
259
  );
192
260
  }
193
- return new (
194
- Database as unknown as new (
195
- path: string,
196
- opts?: Record<string, unknown>,
197
- ) => BetterSqlite3Database
198
- )(dbPath, { readonly: true });
261
+ const db = new Database(dbPath, { readonly: true }) as unknown as BetterSqlite3Database;
262
+
263
+ // Warn once per process if the DB was built with a different codegraph version
264
+ if (!_versionWarned) {
265
+ try {
266
+ const row = db
267
+ .prepare<{ value: string }>('SELECT value FROM build_meta WHERE key = ?')
268
+ .get('codegraph_version');
269
+ const buildVersion = row?.value;
270
+ const currentVersion = getPackageVersion();
271
+ if (buildVersion && currentVersion && buildVersion !== currentVersion) {
272
+ warn(
273
+ `DB was built with codegraph v${buildVersion}, running v${currentVersion}. Consider: codegraph build --no-incremental`,
274
+ );
275
+ }
276
+ } catch {
277
+ // build_meta table may not exist in older DBs — silently ignore
278
+ }
279
+ _versionWarned = true;
280
+ }
281
+
282
+ return db;
199
283
  }
200
284
 
201
285
  /**
package/src/db/index.ts CHANGED
@@ -3,8 +3,10 @@
3
3
  export type { LockedDatabase } from './connection.js';
4
4
  export {
5
5
  closeDb,
6
+ closeDbDeferred,
6
7
  findDbPath,
7
8
  findRepoRoot,
9
+ flushDeferredClose,
8
10
  openDb,
9
11
  openReadonlyOrFail,
10
12
  openRepo,
@@ -247,6 +247,13 @@ export const MIGRATIONS: Migration[] = [
247
247
  CREATE INDEX IF NOT EXISTS idx_nodes_scope ON nodes(scope);
248
248
  `,
249
249
  },
250
+ {
251
+ version: 16,
252
+ up: `
253
+ CREATE INDEX IF NOT EXISTS idx_edges_kind_target ON edges(kind, target_id);
254
+ CREATE INDEX IF NOT EXISTS idx_edges_kind_source ON edges(kind, source_id);
255
+ `,
256
+ },
250
257
  ];
251
258
 
252
259
  interface PragmaColumnInfo {
@@ -113,7 +113,6 @@ function countTransitiveImporters(
113
113
  export function briefData(
114
114
  file: string,
115
115
  customDbPath: string,
116
- // biome-ignore lint/suspicious/noExplicitAny: config shape is dynamic
117
116
  opts: { noTests?: boolean; config?: any } = {},
118
117
  ) {
119
118
  const db = openReadonlyOrFail(customDbPath);
@@ -379,7 +379,6 @@ function explainFunctionImpl(
379
379
  });
380
380
  }
381
381
 
382
- // biome-ignore lint/suspicious/noExplicitAny: explainFunctionImpl results have dynamic shape with _depth
383
382
  function explainCallees(
384
383
  parentResults: any[],
385
384
  currentDepth: number,
@@ -405,8 +404,8 @@ function explainCallees(
405
404
  );
406
405
  const exact = calleeResults.find((cr) => cr.file === callee.file && cr.line === callee.line);
407
406
  if (exact) {
408
- (exact as Record<string, unknown>)['_depth'] =
409
- (((r as Record<string, unknown>)['_depth'] as number) || 0) + 1;
407
+ (exact as Record<string, unknown>)._depth =
408
+ (((r as Record<string, unknown>)._depth as number) || 0) + 1;
410
409
  newCallees.push(exact);
411
410
  }
412
411
  }
@@ -431,7 +430,6 @@ export function contextData(
431
430
  kind?: string;
432
431
  limit?: number;
433
432
  offset?: number;
434
- // biome-ignore lint/suspicious/noExplicitAny: config shape is dynamic
435
433
  config?: any;
436
434
  } = {},
437
435
  ) {
@@ -509,7 +507,6 @@ export function explainData(
509
507
  depth?: number;
510
508
  limit?: number;
511
509
  offset?: number;
512
- // biome-ignore lint/suspicious/noExplicitAny: config shape is dynamic
513
510
  config?: any;
514
511
  } = {},
515
512
  ) {
@@ -533,7 +530,6 @@ export function explainData(
533
530
  : explainFunctionImpl(db, target, noTests, getFileLines, displayOpts);
534
531
 
535
532
  if (kind === 'function' && depth > 0 && results.length > 0) {
536
- // biome-ignore lint/suspicious/noExplicitAny: results are function results when kind === 'function'
537
533
  const visited = new Set(results.map((r: any) => `${r.name}:${r.file}:${r.line ?? ''}`));
538
534
  explainCallees(results, depth, visited, db, noTests, getFileLines, displayOpts);
539
535
  }
@@ -481,3 +481,168 @@ export function pathData(
481
481
  db.close();
482
482
  }
483
483
  }
484
+
485
+ // ── File-level shortest path ────────────────────────────────────────────
486
+
487
+ /**
488
+ * BFS at the file level: find shortest import/edge path between two files.
489
+ * Adjacency: file A → file B if any symbol in A has an edge to any symbol in B.
490
+ */
491
+ export function filePathData(
492
+ from: string,
493
+ to: string,
494
+ customDbPath: string,
495
+ opts: {
496
+ noTests?: boolean;
497
+ maxDepth?: number;
498
+ edgeKinds?: string[];
499
+ reverse?: boolean;
500
+ } = {},
501
+ ) {
502
+ const db = openReadonlyOrFail(customDbPath);
503
+ try {
504
+ const noTests = opts.noTests || false;
505
+ const maxDepth = opts.maxDepth || 10;
506
+ const edgeKinds = opts.edgeKinds || ['imports', 'imports-type'];
507
+ const reverse = opts.reverse || false;
508
+
509
+ // Resolve from/to as file paths (LIKE match)
510
+ const fromFiles = findFileNodes(db, `%${from}%`) as NodeRow[];
511
+ if (fromFiles.length === 0) {
512
+ return {
513
+ from,
514
+ to,
515
+ found: false,
516
+ error: `No file matching "${from}"`,
517
+ path: [],
518
+ fromCandidates: [],
519
+ toCandidates: [],
520
+ };
521
+ }
522
+ const toFiles = findFileNodes(db, `%${to}%`) as NodeRow[];
523
+ if (toFiles.length === 0) {
524
+ return {
525
+ from,
526
+ to,
527
+ found: false,
528
+ error: `No file matching "${to}"`,
529
+ path: [],
530
+ fromCandidates: fromFiles.slice(0, 5).map((f) => f.file),
531
+ toCandidates: [],
532
+ };
533
+ }
534
+
535
+ const sourceFile = fromFiles[0]!.file;
536
+ const targetFile = toFiles[0]!.file;
537
+
538
+ const fromCandidates = fromFiles.slice(0, 5).map((f) => f.file);
539
+ const toCandidates = toFiles.slice(0, 5).map((f) => f.file);
540
+
541
+ if (sourceFile === targetFile) {
542
+ return {
543
+ from,
544
+ to,
545
+ fromCandidates,
546
+ toCandidates,
547
+ found: true,
548
+ hops: 0,
549
+ path: [sourceFile],
550
+ alternateCount: 0,
551
+ edgeKinds,
552
+ reverse,
553
+ maxDepth,
554
+ };
555
+ }
556
+
557
+ // Build neighbor query: find all distinct files adjacent to a given file via edges
558
+ const kindPlaceholders = edgeKinds.map(() => '?').join(', ');
559
+ const neighborQuery = reverse
560
+ ? `SELECT DISTINCT n_src.file AS neighbor_file
561
+ FROM nodes n_tgt
562
+ JOIN edges e ON e.target_id = n_tgt.id
563
+ JOIN nodes n_src ON e.source_id = n_src.id
564
+ WHERE n_tgt.file = ? AND e.kind IN (${kindPlaceholders}) AND n_src.file != n_tgt.file`
565
+ : `SELECT DISTINCT n_tgt.file AS neighbor_file
566
+ FROM nodes n_src
567
+ JOIN edges e ON e.source_id = n_src.id
568
+ JOIN nodes n_tgt ON e.target_id = n_tgt.id
569
+ WHERE n_src.file = ? AND e.kind IN (${kindPlaceholders}) AND n_tgt.file != n_src.file`;
570
+ const neighborStmt = db.prepare(neighborQuery);
571
+
572
+ // BFS
573
+ const visited = new Set([sourceFile]);
574
+ const parentMap = new Map<string, string>();
575
+ let queue = [sourceFile];
576
+ let found = false;
577
+ let alternateCount = 0;
578
+
579
+ for (let depth = 1; depth <= maxDepth; depth++) {
580
+ const nextQueue: string[] = [];
581
+ for (const currentFile of queue) {
582
+ const neighbors = neighborStmt.all(currentFile, ...edgeKinds) as Array<{
583
+ neighbor_file: string;
584
+ }>;
585
+ for (const n of neighbors) {
586
+ if (noTests && isTestFile(n.neighbor_file)) continue;
587
+ if (n.neighbor_file === targetFile) {
588
+ if (!found) {
589
+ found = true;
590
+ parentMap.set(n.neighbor_file, currentFile);
591
+ }
592
+ alternateCount++;
593
+ continue;
594
+ }
595
+ if (!visited.has(n.neighbor_file)) {
596
+ visited.add(n.neighbor_file);
597
+ parentMap.set(n.neighbor_file, currentFile);
598
+ nextQueue.push(n.neighbor_file);
599
+ }
600
+ }
601
+ }
602
+ if (found) break;
603
+ queue = nextQueue;
604
+ if (queue.length === 0) break;
605
+ }
606
+
607
+ if (!found) {
608
+ return {
609
+ from,
610
+ to,
611
+ fromCandidates,
612
+ toCandidates,
613
+ found: false,
614
+ hops: null,
615
+ path: [],
616
+ alternateCount: 0,
617
+ edgeKinds,
618
+ reverse,
619
+ maxDepth,
620
+ };
621
+ }
622
+
623
+ // Reconstruct path
624
+ const filePath: string[] = [targetFile];
625
+ let cur = targetFile;
626
+ while (cur !== sourceFile) {
627
+ cur = parentMap.get(cur)!;
628
+ filePath.push(cur);
629
+ }
630
+ filePath.reverse();
631
+
632
+ return {
633
+ from,
634
+ to,
635
+ fromCandidates,
636
+ toCandidates,
637
+ found: true,
638
+ hops: filePath.length - 1,
639
+ path: filePath,
640
+ alternateCount: Math.max(0, alternateCount - 1),
641
+ edgeKinds,
642
+ reverse,
643
+ maxDepth,
644
+ };
645
+ } finally {
646
+ db.close();
647
+ }
648
+ }