@optave/codegraph 3.13.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/README.md +35 -34
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +38 -40
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/b2.d.ts +7 -0
  6. package/dist/ast-analysis/rules/b2.d.ts.map +1 -0
  7. package/dist/ast-analysis/rules/b2.js +240 -0
  8. package/dist/ast-analysis/rules/b2.js.map +1 -0
  9. package/dist/ast-analysis/rules/b3.d.ts +6 -0
  10. package/dist/ast-analysis/rules/b3.d.ts.map +1 -0
  11. package/dist/ast-analysis/rules/b3.js +105 -0
  12. package/dist/ast-analysis/rules/b3.js.map +1 -0
  13. package/dist/ast-analysis/rules/b4.d.ts +9 -0
  14. package/dist/ast-analysis/rules/b4.d.ts.map +1 -0
  15. package/dist/ast-analysis/rules/b4.js +361 -0
  16. package/dist/ast-analysis/rules/b4.js.map +1 -0
  17. package/dist/ast-analysis/rules/b5.d.ts +4 -0
  18. package/dist/ast-analysis/rules/b5.d.ts.map +1 -0
  19. package/dist/ast-analysis/rules/b5.js +52 -0
  20. package/dist/ast-analysis/rules/b5.js.map +1 -0
  21. package/dist/ast-analysis/rules/c.d.ts +4 -0
  22. package/dist/ast-analysis/rules/c.d.ts.map +1 -0
  23. package/dist/ast-analysis/rules/c.js +143 -0
  24. package/dist/ast-analysis/rules/c.js.map +1 -0
  25. package/dist/ast-analysis/rules/index.d.ts.map +1 -1
  26. package/dist/ast-analysis/rules/index.js +34 -0
  27. package/dist/ast-analysis/rules/index.js.map +1 -1
  28. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  29. package/dist/ast-analysis/rules/javascript.js +3 -0
  30. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  31. package/dist/ast-analysis/shared.d.ts.map +1 -1
  32. package/dist/ast-analysis/shared.js +2 -0
  33. package/dist/ast-analysis/shared.js.map +1 -1
  34. package/dist/ast-analysis/visitor-utils.d.ts +1 -0
  35. package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
  36. package/dist/ast-analysis/visitor-utils.js +5 -0
  37. package/dist/ast-analysis/visitor-utils.js.map +1 -1
  38. package/dist/ast-analysis/visitor.d.ts.map +1 -1
  39. package/dist/ast-analysis/visitor.js +60 -47
  40. package/dist/ast-analysis/visitor.js.map +1 -1
  41. package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
  42. package/dist/ast-analysis/visitors/cfg-visitor.js +126 -76
  43. package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -1
  44. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  45. package/dist/ast-analysis/visitors/complexity-visitor.js +27 -15
  46. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  47. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  48. package/dist/ast-analysis/visitors/dataflow-visitor.js +54 -21
  49. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  50. package/dist/cli/commands/config.d.ts.map +1 -1
  51. package/dist/cli/commands/config.js +137 -134
  52. package/dist/cli/commands/config.js.map +1 -1
  53. package/dist/cli/commands/roles.d.ts.map +1 -1
  54. package/dist/cli/commands/roles.js +6 -1
  55. package/dist/cli/commands/roles.js.map +1 -1
  56. package/dist/db/better-sqlite3.d.ts +2 -1
  57. package/dist/db/better-sqlite3.d.ts.map +1 -1
  58. package/dist/db/better-sqlite3.js.map +1 -1
  59. package/dist/db/connection.d.ts +7 -1
  60. package/dist/db/connection.d.ts.map +1 -1
  61. package/dist/db/connection.js +20 -5
  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 +68 -0
  69. package/dist/db/migrations.js.map +1 -1
  70. package/dist/db/repository/build-stmts.d.ts.map +1 -1
  71. package/dist/db/repository/build-stmts.js +18 -0
  72. package/dist/db/repository/build-stmts.js.map +1 -1
  73. package/dist/db/repository/dataflow.d.ts +5 -0
  74. package/dist/db/repository/dataflow.d.ts.map +1 -1
  75. package/dist/db/repository/dataflow.js +14 -0
  76. package/dist/db/repository/dataflow.js.map +1 -1
  77. package/dist/db/repository/index.d.ts +1 -1
  78. package/dist/db/repository/index.d.ts.map +1 -1
  79. package/dist/db/repository/index.js +1 -1
  80. package/dist/db/repository/index.js.map +1 -1
  81. package/dist/db/repository/native-repository.d.ts.map +1 -1
  82. package/dist/db/repository/native-repository.js +47 -34
  83. package/dist/db/repository/native-repository.js.map +1 -1
  84. package/dist/domain/analysis/context.d.ts +2 -2
  85. package/dist/domain/analysis/dependencies.d.ts +2 -2
  86. package/dist/domain/analysis/diff-impact.d.ts +2 -2
  87. package/dist/domain/analysis/fn-impact.d.ts +3 -1
  88. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  89. package/dist/domain/analysis/fn-impact.js +4 -0
  90. package/dist/domain/analysis/fn-impact.js.map +1 -1
  91. package/dist/domain/analysis/implementations.d.ts +2 -2
  92. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  93. package/dist/domain/analysis/module-map.js +32 -5
  94. package/dist/domain/analysis/module-map.js.map +1 -1
  95. package/dist/domain/analysis/roles.d.ts +7 -1
  96. package/dist/domain/analysis/roles.d.ts.map +1 -1
  97. package/dist/domain/analysis/roles.js +16 -0
  98. package/dist/domain/analysis/roles.js.map +1 -1
  99. package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
  100. package/dist/domain/graph/builder/call-resolver.d.ts +17 -5
  101. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
  102. package/dist/domain/graph/builder/call-resolver.js +85 -220
  103. package/dist/domain/graph/builder/call-resolver.js.map +1 -1
  104. package/dist/domain/graph/builder/context.d.ts +1 -0
  105. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  106. package/dist/domain/graph/builder/context.js.map +1 -1
  107. package/dist/domain/graph/builder/helpers.d.ts +16 -1
  108. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  109. package/dist/domain/graph/builder/helpers.js +162 -72
  110. package/dist/domain/graph/builder/helpers.js.map +1 -1
  111. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  112. package/dist/domain/graph/builder/incremental.js +166 -97
  113. package/dist/domain/graph/builder/incremental.js.map +1 -1
  114. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  115. package/dist/domain/graph/builder/pipeline.js +10 -4
  116. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  117. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  118. package/dist/domain/graph/builder/stages/build-edges.js +496 -250
  119. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  120. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  121. package/dist/domain/graph/builder/stages/collect-files.js +10 -7
  122. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  123. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  124. package/dist/domain/graph/builder/stages/detect-changes.js +2 -1
  125. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  126. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  127. package/dist/domain/graph/builder/stages/native-orchestrator.js +895 -545
  128. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  129. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
  130. package/dist/domain/graph/resolver/points-to.js +105 -57
  131. package/dist/domain/graph/resolver/points-to.js.map +1 -1
  132. package/dist/domain/graph/resolver/strategy.d.ts +61 -0
  133. package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
  134. package/dist/domain/graph/resolver/strategy.js +222 -0
  135. package/dist/domain/graph/resolver/strategy.js.map +1 -0
  136. package/dist/domain/graph/watcher.d.ts.map +1 -1
  137. package/dist/domain/graph/watcher.js +16 -9
  138. package/dist/domain/graph/watcher.js.map +1 -1
  139. package/dist/domain/parser.d.ts +12 -0
  140. package/dist/domain/parser.d.ts.map +1 -1
  141. package/dist/domain/parser.js +12 -2
  142. package/dist/domain/parser.js.map +1 -1
  143. package/dist/domain/queries.d.ts +1 -1
  144. package/dist/domain/queries.d.ts.map +1 -1
  145. package/dist/domain/queries.js +1 -1
  146. package/dist/domain/queries.js.map +1 -1
  147. package/dist/domain/wasm-worker-entry.js +3 -0
  148. package/dist/domain/wasm-worker-entry.js.map +1 -1
  149. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  150. package/dist/domain/wasm-worker-pool.js +24 -5
  151. package/dist/domain/wasm-worker-pool.js.map +1 -1
  152. package/dist/domain/wasm-worker-protocol.d.ts +7 -0
  153. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  154. package/dist/extractors/dart.js +48 -3
  155. package/dist/extractors/dart.js.map +1 -1
  156. package/dist/extractors/groovy.js +62 -3
  157. package/dist/extractors/groovy.js.map +1 -1
  158. package/dist/extractors/helpers.d.ts +4 -2
  159. package/dist/extractors/helpers.d.ts.map +1 -1
  160. package/dist/extractors/helpers.js +5 -1
  161. package/dist/extractors/helpers.js.map +1 -1
  162. package/dist/extractors/java.js +77 -1
  163. package/dist/extractors/java.js.map +1 -1
  164. package/dist/extractors/javascript.d.ts.map +1 -1
  165. package/dist/extractors/javascript.js +549 -163
  166. package/dist/extractors/javascript.js.map +1 -1
  167. package/dist/extractors/kotlin.js +58 -3
  168. package/dist/extractors/kotlin.js.map +1 -1
  169. package/dist/extractors/objc.js +25 -2
  170. package/dist/extractors/objc.js.map +1 -1
  171. package/dist/extractors/scala.js +62 -2
  172. package/dist/extractors/scala.js.map +1 -1
  173. package/dist/extractors/swift.js +52 -3
  174. package/dist/extractors/swift.js.map +1 -1
  175. package/dist/features/audit.js +26 -23
  176. package/dist/features/audit.js.map +1 -1
  177. package/dist/features/boundaries.d.ts.map +1 -1
  178. package/dist/features/boundaries.js +12 -9
  179. package/dist/features/boundaries.js.map +1 -1
  180. package/dist/features/cfg.d.ts.map +1 -1
  181. package/dist/features/cfg.js +25 -18
  182. package/dist/features/cfg.js.map +1 -1
  183. package/dist/features/check.d.ts.map +1 -1
  184. package/dist/features/check.js +18 -5
  185. package/dist/features/check.js.map +1 -1
  186. package/dist/features/communities.d.ts +4 -2
  187. package/dist/features/communities.d.ts.map +1 -1
  188. package/dist/features/communities.js +6 -4
  189. package/dist/features/communities.js.map +1 -1
  190. package/dist/features/dataflow.d.ts +60 -0
  191. package/dist/features/dataflow.d.ts.map +1 -1
  192. package/dist/features/dataflow.js +530 -6
  193. package/dist/features/dataflow.js.map +1 -1
  194. package/dist/features/manifesto.d.ts.map +1 -1
  195. package/dist/features/manifesto.js +59 -72
  196. package/dist/features/manifesto.js.map +1 -1
  197. package/dist/features/sequence.d.ts.map +1 -1
  198. package/dist/features/sequence.js +27 -22
  199. package/dist/features/sequence.js.map +1 -1
  200. package/dist/features/snapshot.d.ts.map +1 -1
  201. package/dist/features/snapshot.js +36 -28
  202. package/dist/features/snapshot.js.map +1 -1
  203. package/dist/features/structure.d.ts.map +1 -1
  204. package/dist/features/structure.js +150 -62
  205. package/dist/features/structure.js.map +1 -1
  206. package/dist/features/triage.d.ts.map +1 -1
  207. package/dist/features/triage.js +18 -11
  208. package/dist/features/triage.js.map +1 -1
  209. package/dist/graph/algorithms/bfs.d.ts +1 -1
  210. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  211. package/dist/graph/algorithms/bfs.js +14 -13
  212. package/dist/graph/algorithms/bfs.js.map +1 -1
  213. package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
  214. package/dist/graph/algorithms/tarjan.js +5 -0
  215. package/dist/graph/algorithms/tarjan.js.map +1 -1
  216. package/dist/graph/builders/dependency.js +28 -22
  217. package/dist/graph/builders/dependency.js.map +1 -1
  218. package/dist/graph/classifiers/roles.d.ts +10 -1
  219. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  220. package/dist/graph/classifiers/roles.js +60 -6
  221. package/dist/graph/classifiers/roles.js.map +1 -1
  222. package/dist/infrastructure/config.d.ts +10 -0
  223. package/dist/infrastructure/config.d.ts.map +1 -1
  224. package/dist/infrastructure/config.js +31 -3
  225. package/dist/infrastructure/config.js.map +1 -1
  226. package/dist/infrastructure/registry.d.ts +0 -7
  227. package/dist/infrastructure/registry.d.ts.map +1 -1
  228. package/dist/infrastructure/registry.js +29 -13
  229. package/dist/infrastructure/registry.js.map +1 -1
  230. package/dist/infrastructure/update-check.d.ts.map +1 -1
  231. package/dist/infrastructure/update-check.js +49 -31
  232. package/dist/infrastructure/update-check.js.map +1 -1
  233. package/dist/mcp/server.d.ts +2 -10
  234. package/dist/mcp/server.d.ts.map +1 -1
  235. package/dist/mcp/server.js.map +1 -1
  236. package/dist/mcp/tools/ast-query.d.ts +1 -1
  237. package/dist/mcp/tools/ast-query.d.ts.map +1 -1
  238. package/dist/mcp/tools/audit.d.ts +1 -1
  239. package/dist/mcp/tools/audit.d.ts.map +1 -1
  240. package/dist/mcp/tools/batch-query.d.ts +1 -1
  241. package/dist/mcp/tools/batch-query.d.ts.map +1 -1
  242. package/dist/mcp/tools/branch-compare.d.ts +1 -1
  243. package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
  244. package/dist/mcp/tools/brief.d.ts +1 -1
  245. package/dist/mcp/tools/brief.d.ts.map +1 -1
  246. package/dist/mcp/tools/cfg.d.ts +1 -1
  247. package/dist/mcp/tools/cfg.d.ts.map +1 -1
  248. package/dist/mcp/tools/check.d.ts +1 -1
  249. package/dist/mcp/tools/check.d.ts.map +1 -1
  250. package/dist/mcp/tools/co-changes.d.ts +1 -1
  251. package/dist/mcp/tools/co-changes.d.ts.map +1 -1
  252. package/dist/mcp/tools/code-owners.d.ts +1 -1
  253. package/dist/mcp/tools/code-owners.d.ts.map +1 -1
  254. package/dist/mcp/tools/communities.d.ts +1 -1
  255. package/dist/mcp/tools/communities.d.ts.map +1 -1
  256. package/dist/mcp/tools/complexity.d.ts +1 -1
  257. package/dist/mcp/tools/complexity.d.ts.map +1 -1
  258. package/dist/mcp/tools/context.d.ts +1 -1
  259. package/dist/mcp/tools/context.d.ts.map +1 -1
  260. package/dist/mcp/tools/dataflow.d.ts +1 -1
  261. package/dist/mcp/tools/dataflow.d.ts.map +1 -1
  262. package/dist/mcp/tools/diff-impact.d.ts +1 -1
  263. package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
  264. package/dist/mcp/tools/execution-flow.d.ts +1 -1
  265. package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
  266. package/dist/mcp/tools/export-graph.d.ts +1 -1
  267. package/dist/mcp/tools/export-graph.d.ts.map +1 -1
  268. package/dist/mcp/tools/file-deps.d.ts +1 -1
  269. package/dist/mcp/tools/file-deps.d.ts.map +1 -1
  270. package/dist/mcp/tools/file-exports.d.ts +1 -1
  271. package/dist/mcp/tools/file-exports.d.ts.map +1 -1
  272. package/dist/mcp/tools/find-cycles.d.ts +1 -1
  273. package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
  274. package/dist/mcp/tools/fn-impact.d.ts +1 -1
  275. package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
  276. package/dist/mcp/tools/impact-analysis.d.ts +1 -1
  277. package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
  278. package/dist/mcp/tools/implementations.d.ts +1 -1
  279. package/dist/mcp/tools/implementations.d.ts.map +1 -1
  280. package/dist/mcp/tools/index.d.ts +2 -5
  281. package/dist/mcp/tools/index.d.ts.map +1 -1
  282. package/dist/mcp/tools/index.js.map +1 -1
  283. package/dist/mcp/tools/interfaces.d.ts +1 -1
  284. package/dist/mcp/tools/interfaces.d.ts.map +1 -1
  285. package/dist/mcp/tools/list-functions.d.ts +1 -1
  286. package/dist/mcp/tools/list-functions.d.ts.map +1 -1
  287. package/dist/mcp/tools/list-repos.d.ts +1 -1
  288. package/dist/mcp/tools/list-repos.d.ts.map +1 -1
  289. package/dist/mcp/tools/module-map.d.ts +1 -1
  290. package/dist/mcp/tools/module-map.d.ts.map +1 -1
  291. package/dist/mcp/tools/node-roles.d.ts +1 -1
  292. package/dist/mcp/tools/node-roles.d.ts.map +1 -1
  293. package/dist/mcp/tools/path.d.ts +1 -1
  294. package/dist/mcp/tools/path.d.ts.map +1 -1
  295. package/dist/mcp/tools/query.d.ts +1 -1
  296. package/dist/mcp/tools/query.d.ts.map +1 -1
  297. package/dist/mcp/tools/semantic-search.d.ts +1 -1
  298. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  299. package/dist/mcp/tools/sequence.d.ts +1 -1
  300. package/dist/mcp/tools/sequence.d.ts.map +1 -1
  301. package/dist/mcp/tools/structure.d.ts +1 -1
  302. package/dist/mcp/tools/structure.d.ts.map +1 -1
  303. package/dist/mcp/tools/symbol-children.d.ts +1 -1
  304. package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
  305. package/dist/mcp/tools/triage.d.ts +1 -1
  306. package/dist/mcp/tools/triage.d.ts.map +1 -1
  307. package/dist/mcp/tools/where.d.ts +1 -1
  308. package/dist/mcp/tools/where.d.ts.map +1 -1
  309. package/dist/mcp/types.d.ts +19 -0
  310. package/dist/mcp/types.d.ts.map +1 -0
  311. package/dist/mcp/types.js +6 -0
  312. package/dist/mcp/types.js.map +1 -0
  313. package/dist/presentation/queries-cli/index.d.ts +1 -1
  314. package/dist/presentation/queries-cli/index.d.ts.map +1 -1
  315. package/dist/presentation/queries-cli/index.js +1 -1
  316. package/dist/presentation/queries-cli/index.js.map +1 -1
  317. package/dist/presentation/queries-cli/overview.d.ts +1 -0
  318. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  319. package/dist/presentation/queries-cli/overview.js +20 -1
  320. package/dist/presentation/queries-cli/overview.js.map +1 -1
  321. package/dist/presentation/queries-cli.d.ts +1 -1
  322. package/dist/presentation/queries-cli.d.ts.map +1 -1
  323. package/dist/presentation/queries-cli.js +1 -1
  324. package/dist/presentation/queries-cli.js.map +1 -1
  325. package/dist/presentation/viewer.d.ts.map +1 -1
  326. package/dist/presentation/viewer.js +45 -32
  327. package/dist/presentation/viewer.js.map +1 -1
  328. package/dist/shared/constants.d.ts +21 -0
  329. package/dist/shared/constants.d.ts.map +1 -1
  330. package/dist/shared/constants.js +25 -0
  331. package/dist/shared/constants.js.map +1 -1
  332. package/dist/shared/normalize.d.ts.map +1 -1
  333. package/dist/shared/normalize.js +12 -22
  334. package/dist/shared/normalize.js.map +1 -1
  335. package/dist/shared/paginate.d.ts +4 -17
  336. package/dist/shared/paginate.d.ts.map +1 -1
  337. package/dist/shared/paginate.js.map +1 -1
  338. package/dist/types.d.ts +76 -1
  339. package/dist/types.d.ts.map +1 -1
  340. package/grammars/tree-sitter-erlang.wasm +0 -0
  341. package/package.json +7 -7
  342. package/src/ast-analysis/engine.ts +43 -63
  343. package/src/ast-analysis/rules/b2.ts +263 -0
  344. package/src/ast-analysis/rules/b3.ts +127 -0
  345. package/src/ast-analysis/rules/b4.ts +378 -0
  346. package/src/ast-analysis/rules/b5.ts +65 -0
  347. package/src/ast-analysis/rules/c.ts +157 -0
  348. package/src/ast-analysis/rules/index.ts +34 -0
  349. package/src/ast-analysis/rules/javascript.ts +3 -0
  350. package/src/ast-analysis/shared.ts +2 -0
  351. package/src/ast-analysis/visitor-utils.ts +5 -0
  352. package/src/ast-analysis/visitor.ts +82 -52
  353. package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
  354. package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
  355. package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
  356. package/src/cli/commands/config.ts +184 -184
  357. package/src/cli/commands/roles.ts +6 -1
  358. package/src/db/better-sqlite3.ts +5 -4
  359. package/src/db/connection.ts +23 -5
  360. package/src/db/index.ts +1 -0
  361. package/src/db/migrations.ts +68 -0
  362. package/src/db/repository/build-stmts.ts +30 -0
  363. package/src/db/repository/dataflow.ts +16 -0
  364. package/src/db/repository/index.ts +1 -1
  365. package/src/db/repository/native-repository.ts +56 -40
  366. package/src/domain/analysis/fn-impact.ts +4 -0
  367. package/src/domain/analysis/module-map.ts +38 -6
  368. package/src/domain/analysis/roles.ts +23 -0
  369. package/src/domain/graph/builder/call-resolver.ts +112 -232
  370. package/src/domain/graph/builder/context.ts +1 -0
  371. package/src/domain/graph/builder/helpers.ts +190 -72
  372. package/src/domain/graph/builder/incremental.ts +249 -120
  373. package/src/domain/graph/builder/pipeline.ts +11 -5
  374. package/src/domain/graph/builder/stages/build-edges.ts +696 -296
  375. package/src/domain/graph/builder/stages/collect-files.ts +12 -6
  376. package/src/domain/graph/builder/stages/detect-changes.ts +3 -1
  377. package/src/domain/graph/builder/stages/native-orchestrator.ts +1102 -590
  378. package/src/domain/graph/resolver/points-to.ts +182 -59
  379. package/src/domain/graph/resolver/strategy.ts +265 -0
  380. package/src/domain/graph/watcher.ts +19 -9
  381. package/src/domain/parser.ts +12 -2
  382. package/src/domain/queries.ts +1 -1
  383. package/src/domain/wasm-worker-entry.ts +3 -0
  384. package/src/domain/wasm-worker-pool.ts +28 -4
  385. package/src/domain/wasm-worker-protocol.ts +4 -0
  386. package/src/extractors/dart.ts +48 -3
  387. package/src/extractors/groovy.ts +62 -2
  388. package/src/extractors/helpers.ts +5 -2
  389. package/src/extractors/java.ts +80 -1
  390. package/src/extractors/javascript.ts +566 -161
  391. package/src/extractors/kotlin.ts +57 -3
  392. package/src/extractors/objc.ts +25 -1
  393. package/src/extractors/scala.ts +63 -1
  394. package/src/extractors/swift.ts +46 -3
  395. package/src/features/audit.ts +43 -34
  396. package/src/features/boundaries.ts +17 -9
  397. package/src/features/cfg.ts +31 -22
  398. package/src/features/check.ts +21 -5
  399. package/src/features/communities.ts +28 -19
  400. package/src/features/dataflow.ts +755 -6
  401. package/src/features/manifesto.ts +76 -75
  402. package/src/features/sequence.ts +29 -23
  403. package/src/features/snapshot.ts +36 -25
  404. package/src/features/structure.ts +185 -55
  405. package/src/features/triage.ts +28 -15
  406. package/src/graph/algorithms/bfs.ts +13 -12
  407. package/src/graph/algorithms/tarjan.ts +5 -0
  408. package/src/graph/builders/dependency.ts +35 -23
  409. package/src/graph/classifiers/roles.ts +74 -7
  410. package/src/infrastructure/config.ts +32 -3
  411. package/src/infrastructure/registry.ts +44 -20
  412. package/src/infrastructure/update-check.ts +55 -33
  413. package/src/mcp/server.ts +2 -8
  414. package/src/mcp/tools/ast-query.ts +1 -1
  415. package/src/mcp/tools/audit.ts +1 -1
  416. package/src/mcp/tools/batch-query.ts +1 -1
  417. package/src/mcp/tools/branch-compare.ts +1 -1
  418. package/src/mcp/tools/brief.ts +1 -1
  419. package/src/mcp/tools/cfg.ts +1 -1
  420. package/src/mcp/tools/check.ts +1 -1
  421. package/src/mcp/tools/co-changes.ts +1 -1
  422. package/src/mcp/tools/code-owners.ts +1 -1
  423. package/src/mcp/tools/communities.ts +1 -1
  424. package/src/mcp/tools/complexity.ts +1 -1
  425. package/src/mcp/tools/context.ts +1 -1
  426. package/src/mcp/tools/dataflow.ts +1 -1
  427. package/src/mcp/tools/diff-impact.ts +1 -1
  428. package/src/mcp/tools/execution-flow.ts +1 -1
  429. package/src/mcp/tools/export-graph.ts +1 -1
  430. package/src/mcp/tools/file-deps.ts +1 -1
  431. package/src/mcp/tools/file-exports.ts +1 -1
  432. package/src/mcp/tools/find-cycles.ts +1 -1
  433. package/src/mcp/tools/fn-impact.ts +1 -1
  434. package/src/mcp/tools/impact-analysis.ts +1 -1
  435. package/src/mcp/tools/implementations.ts +1 -1
  436. package/src/mcp/tools/index.ts +2 -5
  437. package/src/mcp/tools/interfaces.ts +1 -1
  438. package/src/mcp/tools/list-functions.ts +1 -1
  439. package/src/mcp/tools/list-repos.ts +1 -1
  440. package/src/mcp/tools/module-map.ts +1 -1
  441. package/src/mcp/tools/node-roles.ts +1 -1
  442. package/src/mcp/tools/path.ts +1 -1
  443. package/src/mcp/tools/query.ts +1 -1
  444. package/src/mcp/tools/semantic-search.ts +1 -1
  445. package/src/mcp/tools/sequence.ts +1 -1
  446. package/src/mcp/tools/structure.ts +1 -1
  447. package/src/mcp/tools/symbol-children.ts +1 -1
  448. package/src/mcp/tools/triage.ts +1 -1
  449. package/src/mcp/tools/where.ts +1 -1
  450. package/src/mcp/types.ts +21 -0
  451. package/src/presentation/queries-cli/index.ts +1 -1
  452. package/src/presentation/queries-cli/overview.ts +35 -1
  453. package/src/presentation/queries-cli.ts +1 -0
  454. package/src/presentation/viewer.ts +98 -87
  455. package/src/shared/constants.ts +26 -0
  456. package/src/shared/normalize.ts +13 -22
  457. package/src/shared/paginate.ts +4 -18
  458. package/src/types.ts +86 -1
@@ -116,6 +116,33 @@ interface TriageDataOpts {
116
116
  repo?: Repository;
117
117
  }
118
118
 
119
+ interface ResolvedRiskConfig {
120
+ weights: RiskWeights;
121
+ riskOpts: { roleWeights?: Record<string, number>; defaultRoleWeight?: number };
122
+ }
123
+
124
+ /** Resolve risk weights and role-weight options from config + opts overrides. */
125
+ function resolveRiskConfig(opts: TriageDataOpts): ResolvedRiskConfig {
126
+ const config = opts.config || loadConfig();
127
+ const riskConfig = ((config as unknown as Record<string, unknown>).risk || {}) as {
128
+ weights?: Partial<RiskWeights>;
129
+ roleWeights?: Record<string, number>;
130
+ defaultRoleWeight?: number;
131
+ };
132
+ const weights: RiskWeights = {
133
+ ...DEFAULT_WEIGHTS,
134
+ ...(riskConfig.weights || {}),
135
+ ...(opts.weights || {}),
136
+ };
137
+ return {
138
+ weights,
139
+ riskOpts: {
140
+ roleWeights: riskConfig.roleWeights,
141
+ defaultRoleWeight: riskConfig.defaultRoleWeight,
142
+ },
143
+ };
144
+ }
145
+
119
146
  export function triageData(
120
147
  customDbPath?: string,
121
148
  opts: TriageDataOpts = {},
@@ -125,21 +152,7 @@ export function triageData(
125
152
  const noTests = opts.noTests || false;
126
153
  const minScore = opts.minScore != null ? Number(opts.minScore) : null;
127
154
  const sort = opts.sort || 'risk';
128
- const config = opts.config || loadConfig();
129
- const riskConfig = ((config as unknown as Record<string, unknown>).risk || {}) as {
130
- weights?: Partial<RiskWeights>;
131
- roleWeights?: Record<string, number>;
132
- defaultRoleWeight?: number;
133
- };
134
- const weights: RiskWeights = {
135
- ...DEFAULT_WEIGHTS,
136
- ...(riskConfig.weights || {}),
137
- ...(opts.weights || {}),
138
- };
139
- const riskOpts = {
140
- roleWeights: riskConfig.roleWeights,
141
- defaultRoleWeight: riskConfig.defaultRoleWeight,
142
- };
155
+ const { weights, riskOpts } = resolveRiskConfig(opts);
143
156
 
144
157
  let rows: TriageNodeRow[];
145
158
  try {
@@ -6,10 +6,17 @@ export interface BfsOpts {
6
6
  direction?: 'forward' | 'backward' | 'both';
7
7
  }
8
8
 
9
+ /** Resolve the neighbor list for a node given traversal direction. */
10
+ function getNeighbors(graph: CodeGraph, node: string, direction: string): string[] {
11
+ if (direction === 'forward') return graph.successors(node);
12
+ if (direction === 'backward') return graph.predecessors(node);
13
+ return graph.neighbors(node);
14
+ }
15
+
9
16
  /**
10
17
  * Breadth-first traversal on a CodeGraph.
11
18
  *
12
- * Tries the native Rust implementation first, falls back to JS.
19
+ * Tries the native Rust implementation first, falls back to a pure-JS queue.
13
20
  *
14
21
  * @returns nodeId → depth from nearest start node
15
22
  */
@@ -46,7 +53,10 @@ export function bfs(
46
53
  return bfsJS(graph, starts, maxDepth, direction);
47
54
  }
48
55
 
49
- /** Pure JS fallback for BFS (used when native addon is unavailable). */
56
+ /**
57
+ * Pure-JS BFS queue (used when native addon is unavailable).
58
+ * Separated from bfs() to keep each function's complexity within thresholds.
59
+ */
50
60
  function bfsJS(
51
61
  graph: CodeGraph,
52
62
  starts: string[],
@@ -70,16 +80,7 @@ function bfsJS(
70
80
  const depth = depths.get(current)!;
71
81
  if (depth >= maxDepth) continue;
72
82
 
73
- let neighbors: string[];
74
- if (direction === 'forward') {
75
- neighbors = graph.successors(current);
76
- } else if (direction === 'backward') {
77
- neighbors = graph.predecessors(current);
78
- } else {
79
- neighbors = graph.neighbors(current);
80
- }
81
-
82
- for (const n of neighbors) {
83
+ for (const n of getNeighbors(graph, current, direction)) {
83
84
  if (!depths.has(n)) {
84
85
  depths.set(n, depth + 1);
85
86
  queue.push(n);
@@ -15,6 +15,7 @@ export function tarjan(graph: CodeGraph): string[][] {
15
15
  const sccs: string[][] = [];
16
16
 
17
17
  function strongconnect(v: string): void {
18
+ // Assign the next discovery index and initialise lowlink to self
18
19
  indices.set(v, index);
19
20
  lowlinks.set(v, index);
20
21
  index++;
@@ -23,13 +24,16 @@ export function tarjan(graph: CodeGraph): string[][] {
23
24
 
24
25
  for (const w of graph.successors(v)) {
25
26
  if (!indices.has(w)) {
27
+ // Tree edge: recurse then propagate lowlink upward
26
28
  strongconnect(w);
27
29
  lowlinks.set(v, Math.min(lowlinks.get(v)!, lowlinks.get(w)!));
28
30
  } else if (onStack.has(w)) {
31
+ // Back/cross edge to a node still on the stack: update lowlink via index
29
32
  lowlinks.set(v, Math.min(lowlinks.get(v)!, indices.get(w)!));
30
33
  }
31
34
  }
32
35
 
36
+ // v is the root of an SCC when its lowlink equals its own discovery index
33
37
  if (lowlinks.get(v) === indices.get(v)) {
34
38
  const scc: string[] = [];
35
39
  let w: string | undefined;
@@ -38,6 +42,7 @@ export function tarjan(graph: CodeGraph): string[][] {
38
42
  onStack.delete(w);
39
43
  scc.push(w);
40
44
  } while (w !== v);
45
+ // Only report non-trivial SCCs (length > 1 = a real cycle)
41
46
  if (scc.length > 1) sccs.push(scc);
42
47
  }
43
48
  }
@@ -78,6 +78,37 @@ interface MinConfidenceEdgeRow {
78
78
  target_id: number;
79
79
  }
80
80
 
81
+ /**
82
+ * Fetch call edges from `dbOrRepo`, optionally filtered by a minimum confidence
83
+ * threshold. When `minConfidence` is unset, all call edges are returned.
84
+ */
85
+ function resolveCallEdges(
86
+ dbOrRepo: BetterSqlite3Database | Repository,
87
+ isRepo: boolean,
88
+ minConfidence?: number,
89
+ ): CallEdgeRow[] | MinConfidenceEdgeRow[] {
90
+ if (minConfidence == null) {
91
+ return isRepo
92
+ ? (dbOrRepo as Repository).getCallEdges()
93
+ : getCallEdges(dbOrRepo as BetterSqlite3Database);
94
+ }
95
+ if (isRepo) {
96
+ // Trade-off: Repository.getCallEdges() returns all call edges, so we
97
+ // filter in JS. This is O(all call edges) rather than the SQL path's
98
+ // indexed WHERE clause. Acceptable for current data sizes; a dedicated
99
+ // getCallEdgesByMinConfidence(threshold) method on the Repository
100
+ // interface would be the proper fix if this becomes a bottleneck.
101
+ return (dbOrRepo as Repository)
102
+ .getCallEdges()
103
+ .filter((e) => e.confidence != null && e.confidence >= minConfidence);
104
+ }
105
+ return (dbOrRepo as BetterSqlite3Database)
106
+ .prepare<MinConfidenceEdgeRow>(
107
+ "SELECT source_id, target_id FROM edges WHERE kind = 'calls' AND confidence >= ?",
108
+ )
109
+ .all(minConfidence);
110
+ }
111
+
81
112
  function buildFunctionLevelGraph(
82
113
  dbOrRepo: BetterSqlite3Database | Repository,
83
114
  noTests: boolean,
@@ -86,7 +117,9 @@ function buildFunctionLevelGraph(
86
117
  const graph = new CodeGraph();
87
118
  const isRepo = dbOrRepo instanceof Repository;
88
119
 
89
- let nodes: CallableNodeRow[] = isRepo ? dbOrRepo.getCallableNodes() : getCallableNodes(dbOrRepo);
120
+ let nodes: CallableNodeRow[] = isRepo
121
+ ? (dbOrRepo as Repository).getCallableNodes()
122
+ : getCallableNodes(dbOrRepo as BetterSqlite3Database);
90
123
  if (noTests) nodes = nodes.filter((n) => !isTestFile(n.file));
91
124
 
92
125
  const nodeIds = new Set<number>();
@@ -100,28 +133,7 @@ function buildFunctionLevelGraph(
100
133
  nodeIds.add(n.id);
101
134
  }
102
135
 
103
- let edges: CallEdgeRow[] | MinConfidenceEdgeRow[];
104
- if (minConfidence != null) {
105
- if (isRepo) {
106
- // Trade-off: Repository.getCallEdges() returns all call edges, so we
107
- // filter in JS. This is O(all call edges) rather than the SQL path's
108
- // indexed WHERE clause. Acceptable for current data sizes; a dedicated
109
- // getCallEdgesByMinConfidence(threshold) method on the Repository
110
- // interface would be the proper fix if this becomes a bottleneck.
111
- edges = dbOrRepo
112
- .getCallEdges()
113
- .filter((e) => e.confidence != null && e.confidence >= minConfidence);
114
- } else {
115
- edges = (dbOrRepo as BetterSqlite3Database)
116
- .prepare<MinConfidenceEdgeRow>(
117
- "SELECT source_id, target_id FROM edges WHERE kind = 'calls' AND confidence >= ?",
118
- )
119
- .all(minConfidence);
120
- }
121
- } else {
122
- edges = isRepo ? dbOrRepo.getCallEdges() : getCallEdges(dbOrRepo);
123
- }
124
-
136
+ const edges = resolveCallEdges(dbOrRepo, isRepo, minConfidence);
125
137
  for (const e of edges) {
126
138
  if (!nodeIds.has(e.source_id) || !nodeIds.has(e.target_id)) continue;
127
139
  const src = String(e.source_id);
@@ -18,6 +18,14 @@ export const FRAMEWORK_ENTRY_PREFIXES: readonly string[] = ['route:', 'event:',
18
18
 
19
19
  const LEAF_KINDS = new Set(['parameter', 'property', 'constant']);
20
20
 
21
+ /**
22
+ * Type definition kinds that are consumed via type annotations rather than calls.
23
+ * These have no inbound call edges by design — they are "used" by type references,
24
+ * struct literals, and generic parameters, none of which produce call edges.
25
+ * If the same file has active callables, type definitions are almost certainly live.
26
+ */
27
+ const TYPE_DEF_KINDS = new Set(['struct', 'enum', 'trait', 'type', 'interface', 'record']);
28
+
21
29
  const FFI_EXTENSIONS = new Set(['.rs', '.c', '.cpp', '.h', '.go', '.java', '.cs']);
22
30
 
23
31
  /** Path patterns indicating framework-dispatched entry points. */
@@ -29,6 +37,17 @@ const ENTRY_PATH_PATTERNS: readonly RegExp[] = [
29
37
  /middleware[/\\]/,
30
38
  ];
31
39
 
40
+ /**
41
+ * Well-known Commander.js dispatch method names.
42
+ * When a method with one of these names lives in a file that matches
43
+ * ENTRY_PATH_PATTERNS, it is the actual framework entry point — not merely a
44
+ * candidate — so it must be classified as `entry` rather than `dead-entry`.
45
+ *
46
+ * `execute` — the action callback invoked by Commander on `program.action()`.
47
+ * `validate` — a pre-execution argument/option validator called before `execute`.
48
+ */
49
+ const COMMANDER_DISPATCH_NAMES = new Set(['execute', 'validate']);
50
+
32
51
  export interface ClassifiableNode {
33
52
  kind?: string;
34
53
  file?: string;
@@ -74,7 +93,16 @@ export interface RoleClassificationNode {
74
93
  isExported: boolean;
75
94
  testOnlyFanIn?: number;
76
95
  productionFanIn?: number;
77
- /** True when the same file contains at least one non-constant callable connected to the graph (fanIn > 0 or fanOut > 0). */
96
+ /**
97
+ * True when the same file contains at least one callable connected to the graph
98
+ * (fanIn > 0 or fanOut > 0) that is not itself an annotation-only kind.
99
+ * Annotation-only kinds are `constant` and all members of `TYPE_DEF_KINDS`
100
+ * (struct, enum, trait, type, interface, record) — these are excluded because
101
+ * they are consumed via references/type-annotations rather than call edges and
102
+ * would otherwise produce a circular dependency in the active-file heuristic.
103
+ * Populated only for `constant` and `TYPE_DEF_KINDS` nodes; `undefined` for
104
+ * regular callables (functions, methods, classes, etc.) which don't need it.
105
+ */
78
106
  hasActiveFileSiblings?: boolean;
79
107
  }
80
108
 
@@ -99,11 +127,37 @@ function computeFanMedians(nodes: RoleClassificationNode[]): { fanIn: number; fa
99
127
  * Covers framework-active constants, test-only callables, and the dead-* family.
100
128
  */
101
129
  function classifyUnreferencedNode(node: RoleClassificationNode): Role {
102
- if (node.kind === 'constant' && node.hasActiveFileSiblings) {
103
- // Constants consumed via identifier reference (not calls) have no
104
- // inbound call edges. If the same file has active callables, the
105
- // constant is almost certainly used locally classify as leaf.
106
- return 'leaf';
130
+ if (node.hasActiveFileSiblings) {
131
+ if (node.kind === 'constant') {
132
+ // Constants consumed via identifier reference (not calls) have no
133
+ // inbound call edges. If the same file has active callables, the
134
+ // constant is almost certainly used locally — classify as leaf.
135
+ return 'leaf';
136
+ }
137
+ if (node.kind && TYPE_DEF_KINDS.has(node.kind)) {
138
+ // Type definitions (struct, enum, trait, type, interface, record) are
139
+ // consumed via type annotations and struct literals — not calls — so they
140
+ // never get inbound call edges. If the same file has active callables,
141
+ // these types are almost certainly live — classify as leaf.
142
+ return 'leaf';
143
+ }
144
+ if (node.kind === 'method' && node.fanOut > 0) {
145
+ // Methods implementing interfaces are dispatched via conditional property
146
+ // access e.g. `if (v.enterFunction) v.enterFunction(...)`. Codegraph
147
+ // resolves the call to the property accessor rather than to the concrete
148
+ // method implementation, so the method has no inbound call edge. We
149
+ // require `fanOut > 0` as evidence of non-triviality, mirroring the
150
+ // function case — trivially-inert dead helper methods remain visible.
151
+ return 'leaf';
152
+ }
153
+ if (node.kind === 'function' && node.fanOut > 0) {
154
+ // Functions referenced as logical-or fallback defaults — e.g.
155
+ // `const fn = options._fetchLatest || fetchLatestVersion` — appear as
156
+ // value references, not call sites, so no call edge is produced. We
157
+ // require `fanOut > 0` as evidence that the function is non-trivial
158
+ // (i.e. it calls something), ruling out truly inert dead helpers.
159
+ return 'leaf';
160
+ }
107
161
  }
108
162
  if (node.testOnlyFanIn != null && node.testOnlyFanIn > 0) return 'test-only';
109
163
  return classifyDeadSubRole(node);
@@ -129,7 +183,20 @@ function classifyNodeRole(node: RoleClassificationNode, medFanIn: number, medFan
129
183
  if (FRAMEWORK_ENTRY_PREFIXES.some((p) => node.name.startsWith(p))) return 'entry';
130
184
 
131
185
  if (node.fanIn === 0) {
132
- return node.isExported ? 'entry' : classifyUnreferencedNode(node);
186
+ if (!node.isExported) {
187
+ // Well-known Commander.js dispatch methods (execute, validate) in framework
188
+ // directories are confirmed entry points, not candidates. Promote them to
189
+ // `entry` directly so they don't appear in `--role dead` output.
190
+ if (
191
+ node.file &&
192
+ COMMANDER_DISPATCH_NAMES.has(node.name) &&
193
+ ENTRY_PATH_PATTERNS.some((p) => p.test(node.file!))
194
+ ) {
195
+ return 'entry';
196
+ }
197
+ return classifyUnreferencedNode(node);
198
+ }
199
+ return 'entry';
133
200
  }
134
201
 
135
202
  const hasProdFanIn = typeof node.productionFanIn === 'number';
@@ -21,6 +21,7 @@ export const DEFAULTS = {
21
21
  include: [] as string[],
22
22
  exclude: [] as string[],
23
23
  ignoreDirs: [] as string[],
24
+ ignoreAdditionalDirs: [] as string[],
24
25
  extensions: [] as string[],
25
26
  aliases: {} as Record<string, string>,
26
27
  build: {
@@ -29,6 +30,8 @@ export const DEFAULTS = {
29
30
  driftThreshold: 0.2,
30
31
  smallFilesThreshold: 5,
31
32
  typescriptResolver: true,
33
+ engine: 'auto' as 'auto' | 'native' | 'wasm',
34
+ fastSkipDiag: false,
32
35
  },
33
36
  query: {
34
37
  defaultDepth: 3,
@@ -87,9 +90,16 @@ export const DEFAULTS = {
87
90
  // TODO(Phase 8.3): wire these into the points-to solver and type-propagation path
88
91
  // once config is threaded through to extractSymbols / buildPointsToMap. Currently
89
92
  // controlled by hardcoded constants in src/extractors/javascript.ts
90
- // (MAX_PROPAGATION_DEPTH, PROPAGATION_HOP_PENALTY) and in
93
+ // (MAX_PROPAGATION_DEPTH, PROPAGATION_HOP_PENALTY, INFERRED_RETURN_TYPE_CONFIDENCE) and in
91
94
  // src/domain/graph/resolver/points-to.ts (MAX_SOLVER_ITERATIONS).
92
95
  typePropagationDepth: 3,
96
+ /**
97
+ * Confidence score assigned to a return type inferred from `return new Constructor()`
98
+ * when no explicit TypeScript annotation is present.
99
+ * Mirrors `INFERRED_RETURN_TYPE_CONFIDENCE` in `src/extractors/javascript.ts`.
100
+ * @reserved — not yet wired; see TODO above.
101
+ */
102
+ typeInferenceConfidence: 0.85,
93
103
  /**
94
104
  * Maximum fixed-point iterations for the Phase 8.3 points-to solver.
95
105
  * @reserved — currently not wired to either the WASM solver
@@ -443,6 +453,7 @@ const BUILD_HASH_KEYS: ReadonlyArray<keyof CodegraphConfig> = [
443
453
  'include',
444
454
  'exclude',
445
455
  'ignoreDirs',
456
+ 'ignoreAdditionalDirs',
446
457
  'extensions',
447
458
  'aliases',
448
459
  'build',
@@ -658,8 +669,8 @@ export function loadConfigWithProvenance(
658
669
  const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as Record<string, unknown>;
659
670
  for (const k of Object.keys(raw)) provenance[k] = 'project';
660
671
  break;
661
- } catch {
662
- // ignore
672
+ } catch (err) {
673
+ debug(`loadConfigWithProvenance: failed to parse ${filePath}: ${toErrorMessage(err)}`);
663
674
  }
664
675
  }
665
676
  }
@@ -686,6 +697,24 @@ export function applyEnvOverrides(config: CodegraphConfig): CodegraphConfig {
686
697
  process.env[envKey as keyof NodeJS.ProcessEnv];
687
698
  }
688
699
  }
700
+ // Engine selection: CODEGRAPH_ENGINE env always wins over config-file value.
701
+ if (process.env.CODEGRAPH_ENGINE !== undefined) {
702
+ const raw = process.env.CODEGRAPH_ENGINE;
703
+ const valid = ['auto', 'native', 'wasm'] as const;
704
+ if ((valid as readonly string[]).includes(raw)) {
705
+ (config.build as Record<string, unknown>).engine = raw as 'auto' | 'native' | 'wasm';
706
+ } else {
707
+ warn(
708
+ `CODEGRAPH_ENGINE="${raw}" is not a valid engine value (expected auto|native|wasm). Falling back to "auto".`,
709
+ );
710
+ (config.build as Record<string, unknown>).engine = 'auto';
711
+ }
712
+ }
713
+ // Fast-skip diagnostic flag.
714
+ if (process.env.CODEGRAPH_FAST_SKIP_DIAG !== undefined) {
715
+ (config.build as Record<string, unknown>).fastSkipDiag =
716
+ process.env.CODEGRAPH_FAST_SKIP_DIAG === '1';
717
+ }
689
718
  return config;
690
719
  }
691
720
 
@@ -242,19 +242,18 @@ interface PrunedEntry {
242
242
  *
243
243
  * When `dryRun` is true, entries are identified but not removed from disk.
244
244
  */
245
- export function pruneRegistry(
246
- registryPath: string = REGISTRY_PATH,
247
- ttlDays: number = DEFAULT_TTL_DAYS,
248
- excludeNames: string[] = [],
249
- dryRun = false,
245
+ /**
246
+ * Walk `registry.repos`, identify entries that are missing on disk or have
247
+ * exceeded the TTL, and optionally delete them (when `dryRun` is false).
248
+ * Returns the list of entries that were (or would be) pruned.
249
+ */
250
+ function pruneRepoEntries(
251
+ registry: Registry,
252
+ cutoff: number,
253
+ excludeSet: Set<string>,
254
+ dryRun: boolean,
250
255
  ): PrunedEntry[] {
251
- const registry = loadRegistry(registryPath);
252
256
  const pruned: PrunedEntry[] = [];
253
- const cutoff = Date.now() - ttlDays * 24 * 60 * 60 * 1000;
254
- const excludeSet = new Set(
255
- excludeNames.filter((n) => typeof n === 'string' && n.trim().length > 0),
256
- );
257
-
258
257
  for (const [name, entry] of Object.entries(registry.repos)) {
259
258
  if (excludeSet.has(name)) continue;
260
259
  if (!fs.existsSync(entry.path)) {
@@ -268,18 +267,43 @@ export function pruneRegistry(
268
267
  if (!dryRun) delete registry.repos[name];
269
268
  }
270
269
  }
270
+ return pruned;
271
+ }
271
272
 
272
- // Prune consent entries whose repo paths no longer exist on disk.
273
- // Consent entries are TTL-exempt only the missing-path rule applies.
274
- let consentChanged = false;
275
- if (!dryRun && registry.userConfig?.consent) {
276
- for (const p of Object.keys(registry.userConfig.consent)) {
277
- if (!fs.existsSync(p)) {
278
- delete registry.userConfig.consent[p];
279
- consentChanged = true;
280
- }
273
+ /**
274
+ * Prune consent entries whose repo paths no longer exist on disk.
275
+ * Consent entries are TTL-exempt — only the missing-path rule applies.
276
+ * Returns true when at least one entry was removed.
277
+ */
278
+ function pruneConsentEntries(consent: Record<string, ConsentDecision>): boolean {
279
+ let changed = false;
280
+ for (const p of Object.keys(consent)) {
281
+ if (!fs.existsSync(p)) {
282
+ delete consent[p];
283
+ changed = true;
281
284
  }
282
285
  }
286
+ return changed;
287
+ }
288
+
289
+ export function pruneRegistry(
290
+ registryPath: string = REGISTRY_PATH,
291
+ ttlDays: number = DEFAULT_TTL_DAYS,
292
+ excludeNames: string[] = [],
293
+ dryRun = false,
294
+ ): PrunedEntry[] {
295
+ const registry = loadRegistry(registryPath);
296
+ const cutoff = Date.now() - ttlDays * 24 * 60 * 60 * 1000;
297
+ const excludeSet = new Set(
298
+ excludeNames.filter((n) => typeof n === 'string' && n.trim().length > 0),
299
+ );
300
+
301
+ const pruned = pruneRepoEntries(registry, cutoff, excludeSet, dryRun);
302
+
303
+ const consentChanged =
304
+ !dryRun && registry.userConfig?.consent
305
+ ? pruneConsentEntries(registry.userConfig.consent)
306
+ : false;
283
307
 
284
308
  if (!dryRun && (pruned.length > 0 || consentChanged)) {
285
309
  saveRegistry(registry, registryPath);
@@ -61,6 +61,33 @@ function saveCache(cache: UpdateCache, cachePath: string = CACHE_PATH): void {
61
61
  fs.renameSync(tmp, cachePath);
62
62
  }
63
63
 
64
+ /**
65
+ * Collect the full response body from an IncomingMessage stream.
66
+ * Resolves with the body string, or null on parse/status error.
67
+ */
68
+ function collectResponseBody(res: import('node:http').IncomingMessage): Promise<string | null> {
69
+ return new Promise((resolve) => {
70
+ if (res.statusCode !== 200) {
71
+ res.resume();
72
+ resolve(null);
73
+ return;
74
+ }
75
+ let body = '';
76
+ res.setEncoding('utf-8');
77
+ res.on('data', (chunk: string) => {
78
+ body += chunk;
79
+ });
80
+ res.on('end', () => {
81
+ try {
82
+ const data = JSON.parse(body);
83
+ resolve(typeof data.version === 'string' ? data.version : null);
84
+ } catch {
85
+ resolve(null);
86
+ }
87
+ });
88
+ });
89
+ }
90
+
64
91
  /**
65
92
  * Fetch the latest version string from the npm registry.
66
93
  * Returns the version string or null on failure.
@@ -71,24 +98,7 @@ function fetchLatestVersion(): Promise<string | null> {
71
98
  REGISTRY_URL,
72
99
  { timeout: FETCH_TIMEOUT_MS, headers: { Accept: 'application/json' } },
73
100
  (res) => {
74
- if (res.statusCode !== 200) {
75
- res.resume();
76
- resolve(null);
77
- return;
78
- }
79
- let body = '';
80
- res.setEncoding('utf-8');
81
- res.on('data', (chunk: string) => {
82
- body += chunk;
83
- });
84
- res.on('end', () => {
85
- try {
86
- const data = JSON.parse(body);
87
- resolve(typeof data.version === 'string' ? data.version : null);
88
- } catch {
89
- resolve(null);
90
- }
91
- });
101
+ collectResponseBody(res).then(resolve);
92
102
  },
93
103
  );
94
104
  req.on('error', () => resolve(null));
@@ -109,6 +119,32 @@ interface CheckForUpdatesOptions {
109
119
  _fetchLatest?: () => Promise<string | null>;
110
120
  }
111
121
 
122
+ /**
123
+ * Return the latest version from cache if fresh, or fetch it from the registry.
124
+ * Persists the fetched version to the cache only when a network fetch occurs
125
+ * (i.e. the cache is stale or missing). Returns the cached value directly
126
+ * without updating the cache when the cache is still within `CACHE_TTL_MS`.
127
+ * Returns null when the network fetch fails or the cache is corrupt.
128
+ */
129
+ async function resolveLatestVersion(
130
+ cachePath: string,
131
+ fetchFn: () => Promise<string | null>,
132
+ ): Promise<string | null> {
133
+ const cache = loadCache(cachePath);
134
+
135
+ if (cache && Date.now() - cache.lastCheckedAt < CACHE_TTL_MS) {
136
+ // Cache is fresh — use stored value directly
137
+ return cache.latestVersion;
138
+ }
139
+
140
+ // Cache is stale or missing — fetch from registry
141
+ const latest = await fetchFn();
142
+ if (!latest) return null;
143
+
144
+ saveCache({ lastCheckedAt: Date.now(), latestVersion: latest }, cachePath);
145
+ return latest;
146
+ }
147
+
112
148
  /**
113
149
  * Check whether a newer version of codegraph is available.
114
150
  *
@@ -129,23 +165,9 @@ export async function checkForUpdates(
129
165
  const fetchFn = options._fetchLatest || fetchLatestVersion;
130
166
 
131
167
  try {
132
- const cache = loadCache(cachePath);
133
-
134
- // Cache is fresh — use it
135
- if (cache && Date.now() - cache.lastCheckedAt < CACHE_TTL_MS) {
136
- if (semverCompare(currentVersion, cache.latestVersion) < 0) {
137
- return { current: currentVersion, latest: cache.latestVersion };
138
- }
139
- return null;
140
- }
141
-
142
- // Cache is stale or missing — fetch
143
- const latest = await fetchFn();
168
+ const latest = await resolveLatestVersion(cachePath, fetchFn);
144
169
  if (!latest) return null;
145
170
 
146
- // Update cache regardless of result
147
- saveCache({ lastCheckedAt: Date.now(), latestVersion: latest }, cachePath);
148
-
149
171
  if (semverCompare(currentVersion, latest) < 0) {
150
172
  return { current: currentVersion, latest };
151
173
  }
package/src/mcp/server.ts CHANGED
@@ -20,6 +20,7 @@ import type { CodegraphConfig, MCPServerOptions } from '../types.js';
20
20
  import { initMcpDefaults } from './middleware.js';
21
21
  import { buildToolList } from './tool-registry.js';
22
22
  import { TOOL_HANDLERS } from './tools/index.js';
23
+ import type { McpToolContext } from './types.js';
23
24
 
24
25
  /**
25
26
  * Module-level guard to register shutdown handlers only once per process.
@@ -28,14 +29,7 @@ import { TOOL_HANDLERS } from './tools/index.js';
28
29
  */
29
30
  let _activeServer: any = null;
30
31
 
31
- export interface McpToolContext {
32
- dbPath: string | undefined;
33
- getQueries(): Promise<any>;
34
- getDatabase(): any;
35
- findDbPath: typeof findDbPath;
36
- allowedRepos: string[] | undefined;
37
- MCP_MAX_LIMIT: number;
38
- }
32
+ export type { McpToolContext };
39
33
 
40
34
  interface MCPServerOptionsInternal extends MCPServerOptions {
41
35
  config?: CodegraphConfig;
@@ -1,6 +1,6 @@
1
1
  import type { ASTNodeKind } from '../../types.js';
2
2
  import { effectiveLimit, effectiveOffset } from '../middleware.js';
3
- import type { McpToolContext } from '../server.js';
3
+ import type { McpToolContext } from '../types.js';
4
4
 
5
5
  export const name = 'ast_query';
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { effectiveOffset, MCP_DEFAULTS, MCP_MAX_LIMIT } from '../middleware.js';
2
- import type { McpToolContext } from '../server.js';
2
+ import type { McpToolContext } from '../types.js';
3
3
 
4
4
  export const name = 'audit';
5
5
 
@@ -1,4 +1,4 @@
1
- import type { McpToolContext } from '../server.js';
1
+ import type { McpToolContext } from '../types.js';
2
2
 
3
3
  export const name = 'batch_query';
4
4
 
@@ -1,4 +1,4 @@
1
- import type { McpToolContext } from '../server.js';
1
+ import type { McpToolContext } from '../types.js';
2
2
 
3
3
  export const name = 'branch_compare';
4
4
 
@@ -1,4 +1,4 @@
1
- import type { McpToolContext } from '../server.js';
1
+ import type { McpToolContext } from '../types.js';
2
2
 
3
3
  export const name = 'brief';
4
4