@optave/codegraph 3.12.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 (524) hide show
  1. package/README.md +83 -46
  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/audit.d.ts.map +1 -1
  51. package/dist/cli/commands/audit.js +2 -1
  52. package/dist/cli/commands/audit.js.map +1 -1
  53. package/dist/cli/commands/batch.d.ts.map +1 -1
  54. package/dist/cli/commands/batch.js +1 -0
  55. package/dist/cli/commands/batch.js.map +1 -1
  56. package/dist/cli/commands/build.d.ts.map +1 -1
  57. package/dist/cli/commands/build.js +6 -1
  58. package/dist/cli/commands/build.js.map +1 -1
  59. package/dist/cli/commands/config.d.ts +3 -0
  60. package/dist/cli/commands/config.d.ts.map +1 -0
  61. package/dist/cli/commands/config.js +275 -0
  62. package/dist/cli/commands/config.js.map +1 -0
  63. package/dist/cli/commands/roles.d.ts.map +1 -1
  64. package/dist/cli/commands/roles.js +6 -1
  65. package/dist/cli/commands/roles.js.map +1 -1
  66. package/dist/cli/commands/triage.js +1 -1
  67. package/dist/cli/commands/triage.js.map +1 -1
  68. package/dist/cli/index.d.ts.map +1 -1
  69. package/dist/cli/index.js +10 -0
  70. package/dist/cli/index.js.map +1 -1
  71. package/dist/cli/shared/options.d.ts +2 -1
  72. package/dist/cli/shared/options.d.ts.map +1 -1
  73. package/dist/cli/shared/options.js +11 -1
  74. package/dist/cli/shared/options.js.map +1 -1
  75. package/dist/cli/types.d.ts +2 -0
  76. package/dist/cli/types.d.ts.map +1 -1
  77. package/dist/db/better-sqlite3.d.ts +2 -1
  78. package/dist/db/better-sqlite3.d.ts.map +1 -1
  79. package/dist/db/better-sqlite3.js.map +1 -1
  80. package/dist/db/connection.d.ts +7 -1
  81. package/dist/db/connection.d.ts.map +1 -1
  82. package/dist/db/connection.js +20 -5
  83. package/dist/db/connection.js.map +1 -1
  84. package/dist/db/index.d.ts +1 -1
  85. package/dist/db/index.d.ts.map +1 -1
  86. package/dist/db/index.js +1 -1
  87. package/dist/db/index.js.map +1 -1
  88. package/dist/db/migrations.d.ts.map +1 -1
  89. package/dist/db/migrations.js +69 -1
  90. package/dist/db/migrations.js.map +1 -1
  91. package/dist/db/repository/build-stmts.d.ts.map +1 -1
  92. package/dist/db/repository/build-stmts.js +18 -0
  93. package/dist/db/repository/build-stmts.js.map +1 -1
  94. package/dist/db/repository/dataflow.d.ts +5 -0
  95. package/dist/db/repository/dataflow.d.ts.map +1 -1
  96. package/dist/db/repository/dataflow.js +14 -0
  97. package/dist/db/repository/dataflow.js.map +1 -1
  98. package/dist/db/repository/index.d.ts +1 -1
  99. package/dist/db/repository/index.d.ts.map +1 -1
  100. package/dist/db/repository/index.js +1 -1
  101. package/dist/db/repository/index.js.map +1 -1
  102. package/dist/db/repository/native-repository.d.ts.map +1 -1
  103. package/dist/db/repository/native-repository.js +47 -34
  104. package/dist/db/repository/native-repository.js.map +1 -1
  105. package/dist/domain/analysis/context.d.ts +2 -2
  106. package/dist/domain/analysis/dependencies.d.ts +2 -2
  107. package/dist/domain/analysis/diff-impact.d.ts +2 -2
  108. package/dist/domain/analysis/fn-impact.d.ts +3 -1
  109. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  110. package/dist/domain/analysis/fn-impact.js +4 -0
  111. package/dist/domain/analysis/fn-impact.js.map +1 -1
  112. package/dist/domain/analysis/implementations.d.ts +2 -2
  113. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  114. package/dist/domain/analysis/module-map.js +32 -5
  115. package/dist/domain/analysis/module-map.js.map +1 -1
  116. package/dist/domain/analysis/roles.d.ts +7 -1
  117. package/dist/domain/analysis/roles.d.ts.map +1 -1
  118. package/dist/domain/analysis/roles.js +16 -0
  119. package/dist/domain/analysis/roles.js.map +1 -1
  120. package/dist/domain/analysis/symbol-lookup.d.ts +4 -4
  121. package/dist/domain/graph/builder/call-resolver.d.ts +29 -13
  122. package/dist/domain/graph/builder/call-resolver.d.ts.map +1 -1
  123. package/dist/domain/graph/builder/call-resolver.js +125 -205
  124. package/dist/domain/graph/builder/call-resolver.js.map +1 -1
  125. package/dist/domain/graph/builder/cha.d.ts +9 -1
  126. package/dist/domain/graph/builder/cha.d.ts.map +1 -1
  127. package/dist/domain/graph/builder/cha.js +17 -2
  128. package/dist/domain/graph/builder/cha.js.map +1 -1
  129. package/dist/domain/graph/builder/context.d.ts +1 -0
  130. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  131. package/dist/domain/graph/builder/context.js.map +1 -1
  132. package/dist/domain/graph/builder/helpers.d.ts +24 -1
  133. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  134. package/dist/domain/graph/builder/helpers.js +174 -65
  135. package/dist/domain/graph/builder/helpers.js.map +1 -1
  136. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  137. package/dist/domain/graph/builder/incremental.js +166 -97
  138. package/dist/domain/graph/builder/incremental.js.map +1 -1
  139. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  140. package/dist/domain/graph/builder/pipeline.js +46 -5
  141. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  142. package/dist/domain/graph/builder/stages/build-edges.d.ts +0 -2
  143. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  144. package/dist/domain/graph/builder/stages/build-edges.js +554 -538
  145. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  146. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  147. package/dist/domain/graph/builder/stages/collect-files.js +10 -7
  148. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  149. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  150. package/dist/domain/graph/builder/stages/detect-changes.js +3 -2
  151. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  152. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  153. package/dist/domain/graph/builder/stages/finalize.js +4 -0
  154. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  155. package/dist/domain/graph/builder/stages/native-orchestrator.d.ts.map +1 -1
  156. package/dist/domain/graph/builder/stages/native-orchestrator.js +952 -343
  157. package/dist/domain/graph/builder/stages/native-orchestrator.js.map +1 -1
  158. package/dist/domain/graph/builder/stages/resolve-imports.js +1 -1
  159. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  160. package/dist/domain/graph/resolver/points-to.d.ts.map +1 -1
  161. package/dist/domain/graph/resolver/points-to.js +105 -57
  162. package/dist/domain/graph/resolver/points-to.js.map +1 -1
  163. package/dist/domain/graph/resolver/strategy.d.ts +61 -0
  164. package/dist/domain/graph/resolver/strategy.d.ts.map +1 -0
  165. package/dist/domain/graph/resolver/strategy.js +222 -0
  166. package/dist/domain/graph/resolver/strategy.js.map +1 -0
  167. package/dist/domain/graph/watcher.d.ts.map +1 -1
  168. package/dist/domain/graph/watcher.js +16 -9
  169. package/dist/domain/graph/watcher.js.map +1 -1
  170. package/dist/domain/parser.d.ts +16 -5
  171. package/dist/domain/parser.d.ts.map +1 -1
  172. package/dist/domain/parser.js +58 -17
  173. package/dist/domain/parser.js.map +1 -1
  174. package/dist/domain/queries.d.ts +1 -1
  175. package/dist/domain/queries.d.ts.map +1 -1
  176. package/dist/domain/queries.js +1 -1
  177. package/dist/domain/queries.js.map +1 -1
  178. package/dist/domain/wasm-worker-entry.js +13 -2
  179. package/dist/domain/wasm-worker-entry.js.map +1 -1
  180. package/dist/domain/wasm-worker-pool.d.ts.map +1 -1
  181. package/dist/domain/wasm-worker-pool.js +26 -5
  182. package/dist/domain/wasm-worker-pool.js.map +1 -1
  183. package/dist/domain/wasm-worker-protocol.d.ts +8 -0
  184. package/dist/domain/wasm-worker-protocol.d.ts.map +1 -1
  185. package/dist/extractors/cpp.d.ts.map +1 -1
  186. package/dist/extractors/cpp.js +42 -1
  187. package/dist/extractors/cpp.js.map +1 -1
  188. package/dist/extractors/cuda.d.ts.map +1 -1
  189. package/dist/extractors/cuda.js +42 -1
  190. package/dist/extractors/cuda.js.map +1 -1
  191. package/dist/extractors/dart.js +48 -3
  192. package/dist/extractors/dart.js.map +1 -1
  193. package/dist/extractors/groovy.js +62 -3
  194. package/dist/extractors/groovy.js.map +1 -1
  195. package/dist/extractors/helpers.d.ts +15 -2
  196. package/dist/extractors/helpers.d.ts.map +1 -1
  197. package/dist/extractors/helpers.js +45 -1
  198. package/dist/extractors/helpers.js.map +1 -1
  199. package/dist/extractors/java.d.ts.map +1 -1
  200. package/dist/extractors/java.js +85 -8
  201. package/dist/extractors/java.js.map +1 -1
  202. package/dist/extractors/javascript.d.ts.map +1 -1
  203. package/dist/extractors/javascript.js +686 -169
  204. package/dist/extractors/javascript.js.map +1 -1
  205. package/dist/extractors/kotlin.js +58 -3
  206. package/dist/extractors/kotlin.js.map +1 -1
  207. package/dist/extractors/objc.js +25 -2
  208. package/dist/extractors/objc.js.map +1 -1
  209. package/dist/extractors/scala.js +62 -2
  210. package/dist/extractors/scala.js.map +1 -1
  211. package/dist/extractors/swift.js +52 -3
  212. package/dist/extractors/swift.js.map +1 -1
  213. package/dist/features/audit.js +26 -23
  214. package/dist/features/audit.js.map +1 -1
  215. package/dist/features/boundaries.d.ts.map +1 -1
  216. package/dist/features/boundaries.js +12 -9
  217. package/dist/features/boundaries.js.map +1 -1
  218. package/dist/features/cfg.d.ts.map +1 -1
  219. package/dist/features/cfg.js +25 -18
  220. package/dist/features/cfg.js.map +1 -1
  221. package/dist/features/check.d.ts.map +1 -1
  222. package/dist/features/check.js +18 -5
  223. package/dist/features/check.js.map +1 -1
  224. package/dist/features/communities.d.ts +4 -2
  225. package/dist/features/communities.d.ts.map +1 -1
  226. package/dist/features/communities.js +6 -4
  227. package/dist/features/communities.js.map +1 -1
  228. package/dist/features/dataflow.d.ts +60 -0
  229. package/dist/features/dataflow.d.ts.map +1 -1
  230. package/dist/features/dataflow.js +530 -6
  231. package/dist/features/dataflow.js.map +1 -1
  232. package/dist/features/manifesto.d.ts.map +1 -1
  233. package/dist/features/manifesto.js +59 -72
  234. package/dist/features/manifesto.js.map +1 -1
  235. package/dist/features/sequence.d.ts.map +1 -1
  236. package/dist/features/sequence.js +27 -22
  237. package/dist/features/sequence.js.map +1 -1
  238. package/dist/features/snapshot.d.ts.map +1 -1
  239. package/dist/features/snapshot.js +36 -28
  240. package/dist/features/snapshot.js.map +1 -1
  241. package/dist/features/structure-query.d.ts +1 -1
  242. package/dist/features/structure-query.d.ts.map +1 -1
  243. package/dist/features/structure-query.js +6 -6
  244. package/dist/features/structure-query.js.map +1 -1
  245. package/dist/features/structure.d.ts.map +1 -1
  246. package/dist/features/structure.js +150 -62
  247. package/dist/features/structure.js.map +1 -1
  248. package/dist/features/triage.d.ts.map +1 -1
  249. package/dist/features/triage.js +18 -11
  250. package/dist/features/triage.js.map +1 -1
  251. package/dist/graph/algorithms/bfs.d.ts +1 -1
  252. package/dist/graph/algorithms/bfs.d.ts.map +1 -1
  253. package/dist/graph/algorithms/bfs.js +14 -13
  254. package/dist/graph/algorithms/bfs.js.map +1 -1
  255. package/dist/graph/algorithms/tarjan.d.ts.map +1 -1
  256. package/dist/graph/algorithms/tarjan.js +5 -0
  257. package/dist/graph/algorithms/tarjan.js.map +1 -1
  258. package/dist/graph/builders/dependency.js +28 -22
  259. package/dist/graph/builders/dependency.js.map +1 -1
  260. package/dist/graph/classifiers/roles.d.ts +10 -1
  261. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  262. package/dist/graph/classifiers/roles.js +60 -6
  263. package/dist/graph/classifiers/roles.js.map +1 -1
  264. package/dist/index.d.ts +1 -1
  265. package/dist/index.d.ts.map +1 -1
  266. package/dist/index.js +1 -1
  267. package/dist/index.js.map +1 -1
  268. package/dist/infrastructure/config.d.ts +87 -4
  269. package/dist/infrastructure/config.d.ts.map +1 -1
  270. package/dist/infrastructure/config.js +424 -22
  271. package/dist/infrastructure/config.js.map +1 -1
  272. package/dist/infrastructure/registry.d.ts +27 -7
  273. package/dist/infrastructure/registry.d.ts.map +1 -1
  274. package/dist/infrastructure/registry.js +79 -5
  275. package/dist/infrastructure/registry.js.map +1 -1
  276. package/dist/infrastructure/update-check.d.ts.map +1 -1
  277. package/dist/infrastructure/update-check.js +49 -31
  278. package/dist/infrastructure/update-check.js.map +1 -1
  279. package/dist/mcp/server.d.ts +2 -10
  280. package/dist/mcp/server.d.ts.map +1 -1
  281. package/dist/mcp/server.js.map +1 -1
  282. package/dist/mcp/tools/ast-query.d.ts +1 -1
  283. package/dist/mcp/tools/ast-query.d.ts.map +1 -1
  284. package/dist/mcp/tools/audit.d.ts +1 -1
  285. package/dist/mcp/tools/audit.d.ts.map +1 -1
  286. package/dist/mcp/tools/batch-query.d.ts +1 -1
  287. package/dist/mcp/tools/batch-query.d.ts.map +1 -1
  288. package/dist/mcp/tools/branch-compare.d.ts +1 -1
  289. package/dist/mcp/tools/branch-compare.d.ts.map +1 -1
  290. package/dist/mcp/tools/brief.d.ts +1 -1
  291. package/dist/mcp/tools/brief.d.ts.map +1 -1
  292. package/dist/mcp/tools/cfg.d.ts +1 -1
  293. package/dist/mcp/tools/cfg.d.ts.map +1 -1
  294. package/dist/mcp/tools/check.d.ts +1 -1
  295. package/dist/mcp/tools/check.d.ts.map +1 -1
  296. package/dist/mcp/tools/co-changes.d.ts +1 -1
  297. package/dist/mcp/tools/co-changes.d.ts.map +1 -1
  298. package/dist/mcp/tools/code-owners.d.ts +1 -1
  299. package/dist/mcp/tools/code-owners.d.ts.map +1 -1
  300. package/dist/mcp/tools/communities.d.ts +1 -1
  301. package/dist/mcp/tools/communities.d.ts.map +1 -1
  302. package/dist/mcp/tools/complexity.d.ts +1 -1
  303. package/dist/mcp/tools/complexity.d.ts.map +1 -1
  304. package/dist/mcp/tools/context.d.ts +1 -1
  305. package/dist/mcp/tools/context.d.ts.map +1 -1
  306. package/dist/mcp/tools/dataflow.d.ts +1 -1
  307. package/dist/mcp/tools/dataflow.d.ts.map +1 -1
  308. package/dist/mcp/tools/diff-impact.d.ts +1 -1
  309. package/dist/mcp/tools/diff-impact.d.ts.map +1 -1
  310. package/dist/mcp/tools/execution-flow.d.ts +1 -1
  311. package/dist/mcp/tools/execution-flow.d.ts.map +1 -1
  312. package/dist/mcp/tools/export-graph.d.ts +1 -1
  313. package/dist/mcp/tools/export-graph.d.ts.map +1 -1
  314. package/dist/mcp/tools/file-deps.d.ts +1 -1
  315. package/dist/mcp/tools/file-deps.d.ts.map +1 -1
  316. package/dist/mcp/tools/file-exports.d.ts +1 -1
  317. package/dist/mcp/tools/file-exports.d.ts.map +1 -1
  318. package/dist/mcp/tools/find-cycles.d.ts +1 -1
  319. package/dist/mcp/tools/find-cycles.d.ts.map +1 -1
  320. package/dist/mcp/tools/fn-impact.d.ts +1 -1
  321. package/dist/mcp/tools/fn-impact.d.ts.map +1 -1
  322. package/dist/mcp/tools/impact-analysis.d.ts +1 -1
  323. package/dist/mcp/tools/impact-analysis.d.ts.map +1 -1
  324. package/dist/mcp/tools/implementations.d.ts +1 -1
  325. package/dist/mcp/tools/implementations.d.ts.map +1 -1
  326. package/dist/mcp/tools/index.d.ts +2 -5
  327. package/dist/mcp/tools/index.d.ts.map +1 -1
  328. package/dist/mcp/tools/index.js.map +1 -1
  329. package/dist/mcp/tools/interfaces.d.ts +1 -1
  330. package/dist/mcp/tools/interfaces.d.ts.map +1 -1
  331. package/dist/mcp/tools/list-functions.d.ts +1 -1
  332. package/dist/mcp/tools/list-functions.d.ts.map +1 -1
  333. package/dist/mcp/tools/list-repos.d.ts +1 -1
  334. package/dist/mcp/tools/list-repos.d.ts.map +1 -1
  335. package/dist/mcp/tools/module-map.d.ts +1 -1
  336. package/dist/mcp/tools/module-map.d.ts.map +1 -1
  337. package/dist/mcp/tools/node-roles.d.ts +1 -1
  338. package/dist/mcp/tools/node-roles.d.ts.map +1 -1
  339. package/dist/mcp/tools/path.d.ts +1 -1
  340. package/dist/mcp/tools/path.d.ts.map +1 -1
  341. package/dist/mcp/tools/query.d.ts +1 -1
  342. package/dist/mcp/tools/query.d.ts.map +1 -1
  343. package/dist/mcp/tools/semantic-search.d.ts +1 -1
  344. package/dist/mcp/tools/semantic-search.d.ts.map +1 -1
  345. package/dist/mcp/tools/sequence.d.ts +1 -1
  346. package/dist/mcp/tools/sequence.d.ts.map +1 -1
  347. package/dist/mcp/tools/structure.d.ts +1 -1
  348. package/dist/mcp/tools/structure.d.ts.map +1 -1
  349. package/dist/mcp/tools/symbol-children.d.ts +1 -1
  350. package/dist/mcp/tools/symbol-children.d.ts.map +1 -1
  351. package/dist/mcp/tools/triage.d.ts +1 -1
  352. package/dist/mcp/tools/triage.d.ts.map +1 -1
  353. package/dist/mcp/tools/where.d.ts +1 -1
  354. package/dist/mcp/tools/where.d.ts.map +1 -1
  355. package/dist/mcp/types.d.ts +19 -0
  356. package/dist/mcp/types.d.ts.map +1 -0
  357. package/dist/mcp/types.js +6 -0
  358. package/dist/mcp/types.js.map +1 -0
  359. package/dist/presentation/queries-cli/index.d.ts +1 -1
  360. package/dist/presentation/queries-cli/index.d.ts.map +1 -1
  361. package/dist/presentation/queries-cli/index.js +1 -1
  362. package/dist/presentation/queries-cli/index.js.map +1 -1
  363. package/dist/presentation/queries-cli/overview.d.ts +1 -0
  364. package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
  365. package/dist/presentation/queries-cli/overview.js +20 -1
  366. package/dist/presentation/queries-cli/overview.js.map +1 -1
  367. package/dist/presentation/queries-cli.d.ts +1 -1
  368. package/dist/presentation/queries-cli.d.ts.map +1 -1
  369. package/dist/presentation/queries-cli.js +1 -1
  370. package/dist/presentation/queries-cli.js.map +1 -1
  371. package/dist/presentation/structure.d.ts +1 -1
  372. package/dist/presentation/structure.d.ts.map +1 -1
  373. package/dist/presentation/structure.js +2 -2
  374. package/dist/presentation/structure.js.map +1 -1
  375. package/dist/presentation/viewer.d.ts.map +1 -1
  376. package/dist/presentation/viewer.js +45 -32
  377. package/dist/presentation/viewer.js.map +1 -1
  378. package/dist/shared/constants.d.ts +21 -0
  379. package/dist/shared/constants.d.ts.map +1 -1
  380. package/dist/shared/constants.js +25 -0
  381. package/dist/shared/constants.js.map +1 -1
  382. package/dist/shared/normalize.d.ts.map +1 -1
  383. package/dist/shared/normalize.js +12 -22
  384. package/dist/shared/normalize.js.map +1 -1
  385. package/dist/shared/paginate.d.ts +4 -17
  386. package/dist/shared/paginate.d.ts.map +1 -1
  387. package/dist/shared/paginate.js.map +1 -1
  388. package/dist/types.d.ts +113 -1
  389. package/dist/types.d.ts.map +1 -1
  390. package/grammars/tree-sitter-erlang.wasm +0 -0
  391. package/grammars/tree-sitter-gleam.wasm +0 -0
  392. package/package.json +7 -8
  393. package/src/ast-analysis/engine.ts +43 -63
  394. package/src/ast-analysis/rules/b2.ts +263 -0
  395. package/src/ast-analysis/rules/b3.ts +127 -0
  396. package/src/ast-analysis/rules/b4.ts +378 -0
  397. package/src/ast-analysis/rules/b5.ts +65 -0
  398. package/src/ast-analysis/rules/c.ts +157 -0
  399. package/src/ast-analysis/rules/index.ts +34 -0
  400. package/src/ast-analysis/rules/javascript.ts +3 -0
  401. package/src/ast-analysis/shared.ts +2 -0
  402. package/src/ast-analysis/visitor-utils.ts +5 -0
  403. package/src/ast-analysis/visitor.ts +82 -52
  404. package/src/ast-analysis/visitors/cfg-visitor.ts +198 -84
  405. package/src/ast-analysis/visitors/complexity-visitor.ts +44 -16
  406. package/src/ast-analysis/visitors/dataflow-visitor.ts +68 -29
  407. package/src/cli/commands/audit.ts +2 -1
  408. package/src/cli/commands/batch.ts +1 -0
  409. package/src/cli/commands/build.ts +6 -1
  410. package/src/cli/commands/config.ts +353 -0
  411. package/src/cli/commands/roles.ts +6 -1
  412. package/src/cli/commands/triage.ts +1 -1
  413. package/src/cli/index.ts +10 -0
  414. package/src/cli/shared/options.ts +11 -1
  415. package/src/cli/types.ts +2 -0
  416. package/src/db/better-sqlite3.ts +5 -4
  417. package/src/db/connection.ts +23 -5
  418. package/src/db/index.ts +1 -0
  419. package/src/db/migrations.ts +69 -1
  420. package/src/db/repository/build-stmts.ts +30 -0
  421. package/src/db/repository/dataflow.ts +16 -0
  422. package/src/db/repository/index.ts +1 -1
  423. package/src/db/repository/native-repository.ts +56 -40
  424. package/src/domain/analysis/fn-impact.ts +4 -0
  425. package/src/domain/analysis/module-map.ts +38 -6
  426. package/src/domain/analysis/roles.ts +23 -0
  427. package/src/domain/graph/builder/call-resolver.ts +156 -218
  428. package/src/domain/graph/builder/cha.ts +18 -1
  429. package/src/domain/graph/builder/context.ts +1 -0
  430. package/src/domain/graph/builder/helpers.ts +205 -67
  431. package/src/domain/graph/builder/incremental.ts +249 -119
  432. package/src/domain/graph/builder/pipeline.ts +59 -6
  433. package/src/domain/graph/builder/stages/build-edges.ts +783 -652
  434. package/src/domain/graph/builder/stages/collect-files.ts +12 -6
  435. package/src/domain/graph/builder/stages/detect-changes.ts +4 -2
  436. package/src/domain/graph/builder/stages/finalize.ts +4 -0
  437. package/src/domain/graph/builder/stages/native-orchestrator.ts +1214 -398
  438. package/src/domain/graph/builder/stages/resolve-imports.ts +1 -1
  439. package/src/domain/graph/resolver/points-to.ts +182 -59
  440. package/src/domain/graph/resolver/strategy.ts +265 -0
  441. package/src/domain/graph/watcher.ts +19 -9
  442. package/src/domain/parser.ts +57 -16
  443. package/src/domain/queries.ts +1 -1
  444. package/src/domain/wasm-worker-entry.ts +13 -2
  445. package/src/domain/wasm-worker-pool.ts +29 -4
  446. package/src/domain/wasm-worker-protocol.ts +5 -0
  447. package/src/extractors/cpp.ts +44 -1
  448. package/src/extractors/cuda.ts +44 -1
  449. package/src/extractors/dart.ts +48 -3
  450. package/src/extractors/groovy.ts +62 -2
  451. package/src/extractors/helpers.ts +48 -2
  452. package/src/extractors/java.ts +88 -8
  453. package/src/extractors/javascript.ts +693 -167
  454. package/src/extractors/kotlin.ts +57 -3
  455. package/src/extractors/objc.ts +25 -1
  456. package/src/extractors/scala.ts +63 -1
  457. package/src/extractors/swift.ts +46 -3
  458. package/src/features/audit.ts +43 -34
  459. package/src/features/boundaries.ts +17 -9
  460. package/src/features/cfg.ts +31 -22
  461. package/src/features/check.ts +21 -5
  462. package/src/features/communities.ts +28 -19
  463. package/src/features/dataflow.ts +755 -6
  464. package/src/features/manifesto.ts +76 -75
  465. package/src/features/sequence.ts +29 -23
  466. package/src/features/snapshot.ts +36 -25
  467. package/src/features/structure-query.ts +7 -7
  468. package/src/features/structure.ts +185 -55
  469. package/src/features/triage.ts +28 -15
  470. package/src/graph/algorithms/bfs.ts +13 -12
  471. package/src/graph/algorithms/tarjan.ts +5 -0
  472. package/src/graph/builders/dependency.ts +35 -23
  473. package/src/graph/classifiers/roles.ts +74 -7
  474. package/src/index.ts +5 -1
  475. package/src/infrastructure/config.ts +511 -23
  476. package/src/infrastructure/registry.ts +117 -12
  477. package/src/infrastructure/update-check.ts +55 -33
  478. package/src/mcp/server.ts +2 -8
  479. package/src/mcp/tools/ast-query.ts +1 -1
  480. package/src/mcp/tools/audit.ts +1 -1
  481. package/src/mcp/tools/batch-query.ts +1 -1
  482. package/src/mcp/tools/branch-compare.ts +1 -1
  483. package/src/mcp/tools/brief.ts +1 -1
  484. package/src/mcp/tools/cfg.ts +1 -1
  485. package/src/mcp/tools/check.ts +1 -1
  486. package/src/mcp/tools/co-changes.ts +1 -1
  487. package/src/mcp/tools/code-owners.ts +1 -1
  488. package/src/mcp/tools/communities.ts +1 -1
  489. package/src/mcp/tools/complexity.ts +1 -1
  490. package/src/mcp/tools/context.ts +1 -1
  491. package/src/mcp/tools/dataflow.ts +1 -1
  492. package/src/mcp/tools/diff-impact.ts +1 -1
  493. package/src/mcp/tools/execution-flow.ts +1 -1
  494. package/src/mcp/tools/export-graph.ts +1 -1
  495. package/src/mcp/tools/file-deps.ts +1 -1
  496. package/src/mcp/tools/file-exports.ts +1 -1
  497. package/src/mcp/tools/find-cycles.ts +1 -1
  498. package/src/mcp/tools/fn-impact.ts +1 -1
  499. package/src/mcp/tools/impact-analysis.ts +1 -1
  500. package/src/mcp/tools/implementations.ts +1 -1
  501. package/src/mcp/tools/index.ts +2 -5
  502. package/src/mcp/tools/interfaces.ts +1 -1
  503. package/src/mcp/tools/list-functions.ts +1 -1
  504. package/src/mcp/tools/list-repos.ts +1 -1
  505. package/src/mcp/tools/module-map.ts +1 -1
  506. package/src/mcp/tools/node-roles.ts +1 -1
  507. package/src/mcp/tools/path.ts +1 -1
  508. package/src/mcp/tools/query.ts +1 -1
  509. package/src/mcp/tools/semantic-search.ts +1 -1
  510. package/src/mcp/tools/sequence.ts +1 -1
  511. package/src/mcp/tools/structure.ts +1 -1
  512. package/src/mcp/tools/symbol-children.ts +1 -1
  513. package/src/mcp/tools/triage.ts +1 -1
  514. package/src/mcp/tools/where.ts +1 -1
  515. package/src/mcp/types.ts +21 -0
  516. package/src/presentation/queries-cli/index.ts +1 -1
  517. package/src/presentation/queries-cli/overview.ts +35 -1
  518. package/src/presentation/queries-cli.ts +1 -0
  519. package/src/presentation/structure.ts +3 -3
  520. package/src/presentation/viewer.ts +98 -87
  521. package/src/shared/constants.ts +26 -0
  522. package/src/shared/normalize.ts +13 -22
  523. package/src/shared/paginate.ts +4 -18
  524. package/src/types.ts +127 -1
@@ -90,6 +90,44 @@ interface SqliteStatement {
90
90
  run(...params: unknown[]): unknown;
91
91
  }
92
92
 
93
+ /** Insert file→parent-directory contains edges (incremental-aware). */
94
+ function insertFileToParentEdges(
95
+ insertEdge: SqliteStatement,
96
+ getNodeIdStmt: NodeIdStmt,
97
+ fileSymbols: Map<string, FileSymbolData>,
98
+ affectedDirs: Set<string> | null,
99
+ ): void {
100
+ for (const relPath of fileSymbols.keys()) {
101
+ const dir = normalizePath(path.dirname(relPath));
102
+ if (!dir || dir === '.') continue;
103
+ if (affectedDirs && !affectedDirs.has(dir)) continue;
104
+ const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
105
+ const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
106
+ if (dirRow && fileRow) {
107
+ insertEdge.run(dirRow.id, fileRow.id, 'contains', 1.0, 0);
108
+ }
109
+ }
110
+ }
111
+
112
+ /** Insert child-directory→parent-directory contains edges (incremental-aware). */
113
+ function insertDirToParentEdges(
114
+ insertEdge: SqliteStatement,
115
+ getNodeIdStmt: NodeIdStmt,
116
+ allDirs: Set<string>,
117
+ affectedDirs: Set<string> | null,
118
+ ): void {
119
+ for (const dir of allDirs) {
120
+ const parent = normalizePath(path.dirname(dir));
121
+ if (!parent || parent === '.' || parent === dir) continue;
122
+ if (affectedDirs && !affectedDirs.has(parent)) continue;
123
+ const parentRow = getNodeIdStmt.get(parent, 'directory', parent, 0);
124
+ const childRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
125
+ if (parentRow && childRow) {
126
+ insertEdge.run(parentRow.id, childRow.id, 'contains', 1.0, 0);
127
+ }
128
+ }
129
+ }
130
+
93
131
  function insertContainsEdges(
94
132
  db: BetterSqlite3Database,
95
133
  insertEdge: SqliteStatement,
@@ -102,26 +140,8 @@ function insertContainsEdges(
102
140
  const affectedDirs = isIncremental ? getAncestorDirs(changedFiles ?? []) : null;
103
141
 
104
142
  db.transaction(() => {
105
- for (const relPath of fileSymbols.keys()) {
106
- const dir = normalizePath(path.dirname(relPath));
107
- if (!dir || dir === '.') continue;
108
- if (affectedDirs && !affectedDirs.has(dir)) continue;
109
- const dirRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
110
- const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0);
111
- if (dirRow && fileRow) {
112
- insertEdge.run(dirRow.id, fileRow.id, 'contains', 1.0, 0);
113
- }
114
- }
115
- for (const dir of allDirs) {
116
- const parent = normalizePath(path.dirname(dir));
117
- if (!parent || parent === '.' || parent === dir) continue;
118
- if (affectedDirs && !affectedDirs.has(parent)) continue;
119
- const parentRow = getNodeIdStmt.get(parent, 'directory', parent, 0);
120
- const childRow = getNodeIdStmt.get(dir, 'directory', dir, 0);
121
- if (parentRow && childRow) {
122
- insertEdge.run(parentRow.id, childRow.id, 'contains', 1.0, 0);
123
- }
124
- }
143
+ insertFileToParentEdges(insertEdge, getNodeIdStmt, fileSymbols, affectedDirs);
144
+ insertDirToParentEdges(insertEdge, getNodeIdStmt, allDirs, affectedDirs);
125
145
  })();
126
146
  }
127
147
 
@@ -249,40 +269,59 @@ function buildFileToAncestorDirs(dirFiles: Map<string, string[]>): Map<string, S
249
269
  return fileToAncestorDirs;
250
270
  }
251
271
 
272
+ /** Initialise a zero-count map for all known directories. */
273
+ function initDirEdgeCounts(
274
+ allDirs: Set<string>,
275
+ ): Map<string, { intra: number; fanIn: number; fanOut: number }> {
276
+ const m = new Map<string, { intra: number; fanIn: number; fanOut: number }>();
277
+ for (const dir of allDirs) m.set(dir, { intra: 0, fanIn: 0, fanOut: 0 });
278
+ return m;
279
+ }
280
+
281
+ /** Accumulate source-side (intra / fanOut) counts for one import edge. */
282
+ function accumulateSrcDirCounts(
283
+ srcDirs: Set<string>,
284
+ tgtDirs: Set<string> | undefined,
285
+ dirEdgeCounts: Map<string, { intra: number; fanIn: number; fanOut: number }>,
286
+ ): void {
287
+ for (const dir of srcDirs) {
288
+ const counts = dirEdgeCounts.get(dir);
289
+ if (!counts) continue;
290
+ if (tgtDirs?.has(dir)) {
291
+ counts.intra++;
292
+ } else {
293
+ counts.fanOut++;
294
+ }
295
+ }
296
+ }
297
+
298
+ /** Accumulate target-side (fanIn) counts for one import edge. */
299
+ function accumulateTgtDirCounts(
300
+ tgtDirs: Set<string>,
301
+ srcDirs: Set<string> | undefined,
302
+ dirEdgeCounts: Map<string, { intra: number; fanIn: number; fanOut: number }>,
303
+ ): void {
304
+ for (const dir of tgtDirs) {
305
+ if (srcDirs?.has(dir)) continue;
306
+ const counts = dirEdgeCounts.get(dir);
307
+ if (!counts) continue;
308
+ counts.fanIn++;
309
+ }
310
+ }
311
+
252
312
  /** Count intra-directory, fan-in, and fan-out edges per directory. */
253
313
  function countDirectoryEdges(
254
314
  allDirs: Set<string>,
255
315
  importEdges: ImportEdge[],
256
316
  fileToAncestorDirs: Map<string, Set<string>>,
257
317
  ): Map<string, { intra: number; fanIn: number; fanOut: number }> {
258
- const dirEdgeCounts = new Map<string, { intra: number; fanIn: number; fanOut: number }>();
259
- for (const dir of allDirs) {
260
- dirEdgeCounts.set(dir, { intra: 0, fanIn: 0, fanOut: 0 });
261
- }
318
+ const dirEdgeCounts = initDirEdgeCounts(allDirs);
262
319
  for (const { source_file, target_file } of importEdges) {
263
320
  const srcDirs = fileToAncestorDirs.get(source_file);
264
321
  const tgtDirs = fileToAncestorDirs.get(target_file);
265
322
  if (!srcDirs && !tgtDirs) continue;
266
-
267
- if (srcDirs) {
268
- for (const dir of srcDirs) {
269
- const counts = dirEdgeCounts.get(dir);
270
- if (!counts) continue;
271
- if (tgtDirs?.has(dir)) {
272
- counts.intra++;
273
- } else {
274
- counts.fanOut++;
275
- }
276
- }
277
- }
278
- if (tgtDirs) {
279
- for (const dir of tgtDirs) {
280
- if (srcDirs?.has(dir)) continue;
281
- const counts = dirEdgeCounts.get(dir);
282
- if (!counts) continue;
283
- counts.fanIn++;
284
- }
285
- }
323
+ if (srcDirs) accumulateSrcDirCounts(srcDirs, tgtDirs, dirEdgeCounts);
324
+ if (tgtDirs) accumulateTgtDirCounts(tgtDirs, srcDirs, dirEdgeCounts);
286
325
  }
287
326
  return dirEdgeCounts;
288
327
  }
@@ -541,15 +580,50 @@ interface CallableNodeRow {
541
580
  fan_out: number;
542
581
  }
543
582
 
544
- /** Build the activeFiles set: files with at least one callable connected to the graph. */
545
- function buildActiveFilesSet(rows: CallableNodeRow[]): Set<string> {
583
+ /**
584
+ * Kinds that are consumed via annotations/references rather than calls.
585
+ * These do not count as "active callables" for the hasActiveFileSiblings heuristic.
586
+ */
587
+ const ANNOTATION_ONLY_KINDS = new Set([
588
+ 'constant',
589
+ 'struct',
590
+ 'enum',
591
+ 'trait',
592
+ 'type',
593
+ 'interface',
594
+ 'record',
595
+ ]);
596
+
597
+ /**
598
+ * Build two active-files sets from callable rows:
599
+ *
600
+ * - `activeFiles`: files with at least one non-annotation-only callable with
601
+ * `fan_in > 0 || fan_out > 0`. Used for annotation-only kinds (constants,
602
+ * type defs) which have no callers by design.
603
+ *
604
+ * - `calledActiveFiles`: files with at least one non-annotation-only callable
605
+ * with `fan_in > 0` (strictly called). Used for method/function kinds to
606
+ * prevent a self-sibling loop: a function with `fanIn=0, fanOut>0` as the
607
+ * only callable in its file must NOT count itself as an "active sibling" and
608
+ * thus promote itself to `leaf`.
609
+ */
610
+ function buildActiveFilesSet(rows: CallableNodeRow[]): {
611
+ activeFiles: Set<string>;
612
+ calledActiveFiles: Set<string>;
613
+ } {
546
614
  const activeFiles = new Set<string>();
615
+ const calledActiveFiles = new Set<string>();
547
616
  for (const r of rows) {
548
- if ((r.fan_in > 0 || r.fan_out > 0) && r.kind !== 'constant') {
549
- activeFiles.add(r.file);
617
+ if (!ANNOTATION_ONLY_KINDS.has(r.kind)) {
618
+ if (r.fan_in > 0 || r.fan_out > 0) {
619
+ activeFiles.add(r.file);
620
+ }
621
+ if (r.fan_in > 0) {
622
+ calledActiveFiles.add(r.file);
623
+ }
550
624
  }
551
625
  }
552
- return activeFiles;
626
+ return { activeFiles, calledActiveFiles };
553
627
  }
554
628
 
555
629
  /** Map callable rows to classifier input objects, attaching exported/prod-fan-in/active-file metadata. */
@@ -558,6 +632,7 @@ function buildClassifierInput(
558
632
  exportedIds: Set<number>,
559
633
  prodFanInMap: Map<number, number>,
560
634
  activeFiles: Set<string>,
635
+ calledActiveFiles: Set<string>,
561
636
  ): Array<{
562
637
  id: string;
563
638
  name: string;
@@ -578,7 +653,20 @@ function buildClassifierInput(
578
653
  fanOut: r.fan_out,
579
654
  isExported: exportedIds.has(r.id),
580
655
  productionFanIn: prodFanInMap.get(r.id) || 0,
581
- hasActiveFileSiblings: r.kind === 'constant' ? activeFiles.has(r.file) : undefined,
656
+ // Set hasActiveFileSiblings for annotation-only kinds (constants, type defs)
657
+ // AND for method/function — the latter two can have fanIn === 0 due to
658
+ // untraced call-site patterns (interface dispatch, logical-or defaults).
659
+ // The classifier interprets this field differently per kind (see classifyUnreferencedNode).
660
+ //
661
+ // IMPORTANT: method/function use calledActiveFiles (fan_in > 0 only) to
662
+ // prevent a self-sibling false negative: a function with fanIn=0, fanOut>0
663
+ // as the sole callable in its file must NOT see its own file as "active"
664
+ // and promote itself to leaf.
665
+ hasActiveFileSiblings: ANNOTATION_ONLY_KINDS.has(r.kind)
666
+ ? activeFiles.has(r.file)
667
+ : r.kind === 'method' || r.kind === 'function'
668
+ ? calledActiveFiles.has(r.file)
669
+ : undefined,
582
670
  }));
583
671
  }
584
672
 
@@ -660,7 +748,8 @@ function readCachedMedians(db: BetterSqlite3Database): { fanIn: number; fanOut:
660
748
  )
661
749
  return null;
662
750
  return { fanIn: cached.fanIn, fanOut: cached.fanOut };
663
- } catch {
751
+ } catch (e) {
752
+ debug(`readCachedMedians: failed to parse cached medians — ${e}`);
664
753
  return null;
665
754
  }
666
755
  }
@@ -761,6 +850,20 @@ function classifyNodeRolesFull(db: BetterSqlite3Database, emptySummary: RoleSumm
761
850
  .all() as { id: number }[];
762
851
  for (const r of reexportExported) exportedIds.add(r.id);
763
852
 
853
+ // Mark symbols with exported=1 as exported — the extractor sets this flag when the
854
+ // author writes `export interface Foo { }` / `export type Bar = ...` / `export function`.
855
+ // Cross-file edge inference misses these when the symbol is only used as a type annotation
856
+ // within the same file (no calls/imports-type edge is produced for same-file type usage).
857
+ // This fixes false dead-unresolved classification for exported interfaces with no external callers (#1583).
858
+ const explicitlyExported = db
859
+ .prepare(
860
+ `SELECT id FROM nodes
861
+ WHERE exported = 1
862
+ AND kind NOT IN ('file', 'directory', 'parameter', 'property')`,
863
+ )
864
+ .all() as { id: number }[];
865
+ for (const r of explicitlyExported) exportedIds.add(r.id);
866
+
764
867
  // Compute production fan-in (excluding callers in test files)
765
868
  const prodFanInMap = new Map<number, number>();
766
869
  const prodRows = db
@@ -781,8 +884,14 @@ function classifyNodeRolesFull(db: BetterSqlite3Database, emptySummary: RoleSumm
781
884
  // Compute medians from the already-loaded rows (no extra DB round-trip),
782
885
  // pass them as overrides to avoid recomputing inside classifyRoles,
783
886
  // and cache them for subsequent incremental builds.
784
- const activeFiles = buildActiveFilesSet(rows);
785
- const classifierInput = buildClassifierInput(rows, exportedIds, prodFanInMap, activeFiles);
887
+ const { activeFiles, calledActiveFiles } = buildActiveFilesSet(rows);
888
+ const classifierInput = buildClassifierInput(
889
+ rows,
890
+ exportedIds,
891
+ prodFanInMap,
892
+ activeFiles,
893
+ calledActiveFiles,
894
+ );
786
895
  const nonZeroFanIn = classifierInput
787
896
  .filter((n) => n.fanIn > 0)
788
897
  .map((n) => n.fanIn)
@@ -928,6 +1037,21 @@ function classifyNodeRolesIncremental(
928
1037
  .all(...allAffectedFiles) as { id: number }[];
929
1038
  for (const r of reexportExported) exportedIds.add(r.id);
930
1039
 
1040
+ // 3c. Mark symbols with exported=1 as exported — the extractor sets this flag when the
1041
+ // author writes `export interface Foo { }` / `export type Bar = ...` / `export function`.
1042
+ // Cross-file edge inference misses these when the symbol is only used as a type annotation
1043
+ // within the same file (no calls/imports-type edge is produced for same-file type usage).
1044
+ // Scoped to affected files only for the incremental path (#1583).
1045
+ const explicitlyExported = db
1046
+ .prepare(
1047
+ `SELECT id FROM nodes
1048
+ WHERE exported = 1
1049
+ AND kind NOT IN ('file', 'directory', 'parameter', 'property')
1050
+ AND file IN (${placeholders})`,
1051
+ )
1052
+ .all(...allAffectedFiles) as { id: number }[];
1053
+ for (const r of explicitlyExported) exportedIds.add(r.id);
1054
+
931
1055
  // 4. Production fan-in for affected nodes only
932
1056
  const prodFanInMap = new Map<number, number>();
933
1057
  const prodRows = db
@@ -947,8 +1071,14 @@ function classifyNodeRolesIncremental(
947
1071
  }
948
1072
 
949
1073
  // 5. Classify affected nodes using global medians
950
- const activeFiles = buildActiveFilesSet(rows);
951
- const classifierInput = buildClassifierInput(rows, exportedIds, prodFanInMap, activeFiles);
1074
+ const { activeFiles, calledActiveFiles } = buildActiveFilesSet(rows);
1075
+ const classifierInput = buildClassifierInput(
1076
+ rows,
1077
+ exportedIds,
1078
+ prodFanInMap,
1079
+ activeFiles,
1080
+ calledActiveFiles,
1081
+ );
952
1082
  const roleMap = classifyRoles(classifierInput, globalMedians);
953
1083
 
954
1084
  // 6. Build summary (only for affected nodes) and update only those nodes
@@ -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';
package/src/index.ts CHANGED
@@ -52,7 +52,11 @@ export { ownersData } from './features/owners.js';
52
52
  export { sequenceData } from './features/sequence.js';
53
53
  export { hotspotsData, moduleBoundariesData, structureData } from './features/structure.js';
54
54
  export { triageData } from './features/triage.js';
55
- export { loadConfig } from './infrastructure/config.js';
55
+ export {
56
+ loadConfig,
57
+ loadConfigWithProvenance,
58
+ resolveUserConfigPath,
59
+ } from './infrastructure/config.js';
56
60
  export type { ArrayCompatSet } from './shared/constants.js';
57
61
  export { EXTENSIONS, IGNORE_DIRS } from './shared/constants.js';
58
62
  export {