@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
@@ -44,6 +44,9 @@ function walkKotlinNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
44
44
  case 'navigation_expression':
45
45
  handleKotlinNavExpression(node, ctx);
46
46
  break;
47
+ case 'callable_reference':
48
+ handleKotlinCallableRef(node, ctx);
49
+ break;
47
50
  }
48
51
 
49
52
  for (let i = 0; i < node.childCount; i++) {
@@ -270,7 +273,10 @@ function handleKotlinCallExpression(node: TreeSitterNode, ctx: ExtractorOutput):
270
273
  const funcNode = node.child(0);
271
274
  if (!funcNode) return;
272
275
  if (funcNode.type === 'simple_identifier') {
273
- ctx.calls.push({ name: funcNode.text, line: node.startPosition.row + 1 });
276
+ const name = funcNode.text;
277
+ // Bare invoke() with no receiver is a resolvable operator fun invoke() self-call —
278
+ // only flag as unresolved-dynamic when called on a receiver (handled in handleKotlinNavExpression).
279
+ ctx.calls.push({ name, line: node.startPosition.row + 1 });
274
280
  }
275
281
  }
276
282
 
@@ -280,12 +286,60 @@ function handleKotlinNavExpression(node: TreeSitterNode, ctx: ExtractorOutput):
280
286
  const lastChild = node.child(node.childCount - 1);
281
287
  const firstChild = node.child(0);
282
288
  if (lastChild && lastChild.type === 'simple_identifier' && firstChild) {
283
- const call: Call = { name: lastChild.text, line: node.startPosition.row + 1 };
284
- call.receiver = firstChild.text;
289
+ const methodName = lastChild.text;
290
+ const receiver = firstChild.text;
291
+ // fn.invoke(args) on a Kotlin callable — unresolvable without type info
292
+ if (methodName === 'invoke') {
293
+ ctx.calls.push({
294
+ name: '<dynamic:unresolved>',
295
+ line: node.startPosition.row + 1,
296
+ dynamic: true,
297
+ dynamicKind: 'unresolved-dynamic',
298
+ receiver,
299
+ });
300
+ return;
301
+ }
302
+ const call: Call = { name: methodName, line: node.startPosition.row + 1 };
303
+ call.receiver = receiver;
285
304
  ctx.calls.push(call);
286
305
  }
287
306
  }
288
307
 
308
+ /**
309
+ * Handle Kotlin callable references: `::greet` and `obj::method`.
310
+ * Emits a reflection-kind call with the referenced member name.
311
+ */
312
+ function handleKotlinCallableRef(node: TreeSitterNode, ctx: ExtractorOutput): void {
313
+ // callable_reference: [qualifier '::'] simple_identifier
314
+ // Find the simple_identifier after '::'
315
+ let foundDoubleColon = false;
316
+ let qualifier: string | undefined;
317
+ for (let i = 0; i < node.childCount; i++) {
318
+ const child = node.child(i);
319
+ if (!child) continue;
320
+ if (child.type === '::') {
321
+ foundDoubleColon = true;
322
+ continue;
323
+ }
324
+ if (!foundDoubleColon) {
325
+ // Everything before '::' is the qualifier
326
+ if (child.type === 'simple_identifier' || child.type === 'type_identifier') {
327
+ qualifier = child.text;
328
+ }
329
+ } else if (child.type === 'simple_identifier') {
330
+ // The member name
331
+ ctx.calls.push({
332
+ name: child.text,
333
+ line: node.startPosition.row + 1,
334
+ dynamic: true,
335
+ dynamicKind: 'reflection',
336
+ receiver: qualifier,
337
+ });
338
+ return;
339
+ }
340
+ }
341
+ }
342
+
289
343
  // ── Child extraction helpers ────────────────────────────────────────────────
290
344
 
291
345
  function extractKotlinParameters(funcNode: TreeSitterNode): SubDeclaration[] {
@@ -336,7 +336,19 @@ function handleCCallExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
336
336
  } else {
337
337
  call.name = funcNode.text;
338
338
  }
339
- if (call.name) ctx.calls.push(call);
339
+ if (!call.name) return;
340
+ // objc_msgSend(obj, sel, ...) — raw ObjC runtime call; selector is a SEL value
341
+ if (call.name === 'objc_msgSend') {
342
+ ctx.calls.push({
343
+ name: '<dynamic:unresolved>',
344
+ line: call.line,
345
+ dynamic: true,
346
+ dynamicKind: 'unresolved-dynamic',
347
+ receiver: call.receiver,
348
+ });
349
+ return;
350
+ }
351
+ ctx.calls.push(call);
340
352
  }
341
353
 
342
354
  function handleMessageExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
@@ -368,6 +380,18 @@ function handleMessageExpr(node: TreeSitterNode, ctx: ExtractorOutput): void {
368
380
  name = selector.text;
369
381
  }
370
382
 
383
+ // performSelector: / performSelector:withObject: — SEL dispatch; not statically resolvable
384
+ if (name === 'performSelector:' || name.startsWith('performSelector:withObject')) {
385
+ ctx.calls.push({
386
+ name: '<dynamic:unresolved>',
387
+ line: node.startPosition.row + 1,
388
+ dynamic: true,
389
+ dynamicKind: 'unresolved-dynamic',
390
+ receiver: receiver?.text,
391
+ });
392
+ return;
393
+ }
394
+
371
395
  const call: Call = { name, line: node.startPosition.row + 1 };
372
396
  if (receiver) call.receiver = receiver.text;
373
397
  ctx.calls.push(call);
@@ -142,6 +142,23 @@ function handleScalaImportDecl(node: TreeSitterNode, ctx: ExtractorOutput): void
142
142
  });
143
143
  }
144
144
 
145
+ /** Extract the first string literal argument from a Scala call_expression. */
146
+ function getFirstStringArgScala(node: TreeSitterNode): string | null {
147
+ const args = node.childForFieldName('arguments') || findChild(node, 'arguments');
148
+ if (!args) return null;
149
+ for (let i = 0; i < args.childCount; i++) {
150
+ const child = args.child(i);
151
+ if (!child) continue;
152
+ const t = child.type;
153
+ if (t === '(' || t === ')' || t === ',') continue;
154
+ if (t === 'string' || t === 'string_literal') {
155
+ return child.text.replace(/^["']|["']$/g, '');
156
+ }
157
+ break;
158
+ }
159
+ return null;
160
+ }
161
+
145
162
  function handleScalaCallExpression(node: TreeSitterNode, ctx: ExtractorOutput): void {
146
163
  const funcNode = node.childForFieldName('function');
147
164
  if (!funcNode) return;
@@ -154,7 +171,52 @@ function handleScalaCallExpression(node: TreeSitterNode, ctx: ExtractorOutput):
154
171
  } else {
155
172
  call.name = funcNode.text;
156
173
  }
157
- if (call.name) ctx.calls.push(call);
174
+ if (!call.name) return;
175
+
176
+ // method.invoke(target, args) — Java/Scala reflection; target unknown statically.
177
+ // Require a non-null receiver to avoid false positives on user-defined `invoke` methods.
178
+ if (call.name === 'invoke' && call.receiver !== undefined) {
179
+ ctx.calls.push({
180
+ name: '<dynamic:unresolved>',
181
+ line: call.line,
182
+ dynamic: true,
183
+ dynamicKind: 'unresolved-dynamic',
184
+ receiver: call.receiver,
185
+ });
186
+ return;
187
+ }
188
+
189
+ // clazz.getMethod("name") / getDeclaredMethod("name") — resolvable if literal.
190
+ // Require a non-null receiver to avoid false positives on gRPC ServiceDescriptor.getMethod(),
191
+ // Spring AnnotationUtils.getDeclaredMethod(), proto-generated descriptors, and any other API
192
+ // that exposes a method by this name unrelated to java.lang.Class reflection.
193
+ if (
194
+ (call.name === 'getMethod' || call.name === 'getDeclaredMethod') &&
195
+ call.receiver !== undefined
196
+ ) {
197
+ const literal = getFirstStringArgScala(node);
198
+ if (literal) {
199
+ ctx.calls.push({
200
+ name: literal,
201
+ line: call.line,
202
+ dynamic: true,
203
+ dynamicKind: 'reflection',
204
+ keyExpr: literal,
205
+ receiver: call.receiver,
206
+ });
207
+ } else {
208
+ ctx.calls.push({
209
+ name: '<dynamic:computed-key>',
210
+ line: call.line,
211
+ dynamic: true,
212
+ dynamicKind: 'computed-key',
213
+ receiver: call.receiver,
214
+ });
215
+ }
216
+ return;
217
+ }
218
+
219
+ ctx.calls.push(call);
158
220
  }
159
221
 
160
222
  function handleScalaValVarDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
@@ -42,6 +42,7 @@ function walkSwiftNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
42
42
  handleSwiftCallExpression(node, ctx);
43
43
  break;
44
44
  case 'property_declaration':
45
+ seedSwiftPropertyTypeMap(node, ctx);
45
46
  handleSwiftPropertyDecl(node, ctx);
46
47
  break;
47
48
  }
@@ -250,11 +251,26 @@ function handleSwiftCallExpression(node: TreeSitterNode, ctx: ExtractorOutput):
250
251
  if (!funcNode) return;
251
252
  const call: Call = { name: '', line: node.startPosition.row + 1 };
252
253
  if (funcNode.type === 'navigation_expression') {
253
- // obj.method(...)
254
+ // obj.method(...) — Swift's tree-sitter grammar wraps the suffix in a
255
+ // `navigation_suffix` node: navigation_expression > [simple_identifier, navigation_suffix].
256
+ // We must descend into navigation_suffix to get the bare method name (e.g. "save" not ".save").
257
+ // Mirrors Rust match_swift_node which also descends into navigation_suffix via find_child
258
+ // to extract the inner simple_identifier, with a trim_start_matches('.') fallback.
254
259
  const lastChild = funcNode.child(funcNode.childCount - 1);
255
260
  const firstChild = funcNode.child(0);
256
- if (lastChild && lastChild.type === 'simple_identifier' && firstChild) {
257
- call.name = lastChild.text;
261
+ if (lastChild && firstChild) {
262
+ // Resolve the method name: descend into navigation_suffix to find the
263
+ // simple_identifier, or fall back to stripping the leading dot from the text.
264
+ let methodName: string;
265
+ if (lastChild.type === 'simple_identifier') {
266
+ methodName = lastChild.text;
267
+ } else if (lastChild.type === 'navigation_suffix') {
268
+ const inner = findChild(lastChild, 'simple_identifier');
269
+ methodName = inner ? inner.text : lastChild.text.replace(/^\./, '');
270
+ } else {
271
+ methodName = lastChild.text;
272
+ }
273
+ call.name = methodName;
258
274
  call.receiver = firstChild.text;
259
275
  }
260
276
  } else if (funcNode.type === 'simple_identifier') {
@@ -265,6 +281,33 @@ function handleSwiftCallExpression(node: TreeSitterNode, ctx: ExtractorOutput):
265
281
  if (call.name) ctx.calls.push(call);
266
282
  }
267
283
 
284
+ /**
285
+ * Seed the typeMap for a property_declaration with a type annotation.
286
+ * This runs for ALL property_declaration nodes (including class-body ones)
287
+ * so that `repo.method()` calls can be resolved to the correct class.
288
+ * Mirrors Rust match_swift_type_map which walks all nodes unconditionally.
289
+ */
290
+ function seedSwiftPropertyTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void {
291
+ const typeAnn = findChild(node, 'type_annotation');
292
+ if (!typeAnn) return;
293
+ // type_annotation: ":" <user_type | simple_identifier | ...>
294
+ // The last child is the actual type node.
295
+ const lastChild = typeAnn.child(typeAnn.childCount - 1);
296
+ if (!lastChild) return;
297
+ // For "user_type > type_identifier", grab the inner identifier text;
298
+ // for a plain simple_identifier, use it directly.
299
+ const typeNode =
300
+ lastChild.type === 'user_type' ? findChild(lastChild, 'type_identifier') : lastChild;
301
+ if (!typeNode) return;
302
+ const typeName = typeNode.text;
303
+ if (!typeName) return;
304
+ const pattern = findChild(node, 'pattern');
305
+ if (!pattern) return;
306
+ const varName = findChild(pattern, 'simple_identifier')?.text ?? pattern.text;
307
+ if (!varName) return;
308
+ ctx.typeMap.set(varName, { type: typeName, confidence: 0.9 });
309
+ }
310
+
268
311
  function handleSwiftPropertyDecl(node: TreeSitterNode, ctx: ExtractorOutput): void {
269
312
  // Only handle top-level properties (class properties are handled inline)
270
313
  if (
@@ -289,6 +289,46 @@ interface FileSymbol {
289
289
  signature?: string | null;
290
290
  }
291
291
 
292
+ /** Query callees, callers, and related test files for a node. */
293
+ function querySymbolEdges(
294
+ db: BetterSqlite3Database,
295
+ nodeId: number,
296
+ noTests: boolean,
297
+ ): { callees: SymbolRef[]; callers: SymbolRef[]; relatedTests: { file: string }[] } {
298
+ const callees = (
299
+ db
300
+ .prepare(
301
+ `SELECT n.name, n.kind, n.file, n.line
302
+ FROM edges e JOIN nodes n ON e.target_id = n.id
303
+ WHERE e.source_id = ? AND e.kind = 'calls'`,
304
+ )
305
+ .all(nodeId) as SymbolRef[]
306
+ ).map(toSymbolRef);
307
+
308
+ let callers = (
309
+ db
310
+ .prepare(
311
+ `SELECT n.name, n.kind, n.file, n.line
312
+ FROM edges e JOIN nodes n ON e.source_id = n.id
313
+ WHERE e.target_id = ? AND e.kind = 'calls'`,
314
+ )
315
+ .all(nodeId) as SymbolRef[]
316
+ ).map(toSymbolRef);
317
+ if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
318
+
319
+ const testCallerRows = db
320
+ .prepare(
321
+ `SELECT DISTINCT n.file FROM edges e JOIN nodes n ON e.source_id = n.id
322
+ WHERE e.target_id = ? AND e.kind = 'calls'`,
323
+ )
324
+ .all(nodeId) as { file: string }[];
325
+ const relatedTests = testCallerRows
326
+ .filter((r) => isTestFile(r.file))
327
+ .map((r) => ({ file: r.file }));
328
+
329
+ return { callees, callers, relatedTests };
330
+ }
331
+
292
332
  function enrichSymbol(
293
333
  db: BetterSqlite3Database,
294
334
  sym: FileSymbol,
@@ -305,40 +345,9 @@ function enrichSymbol(
305
345
  const endLine = nodeRow?.end_line || null;
306
346
  const lineCount = endLine ? endLine - sym.line + 1 : null;
307
347
 
308
- // Get callers/callees for this symbol
309
- let callees: SymbolRef[] = [];
310
- let callers: SymbolRef[] = [];
311
- let relatedTests: { file: string }[] = [];
312
- if (nodeId) {
313
- callees = (
314
- db
315
- .prepare(
316
- `SELECT n.name, n.kind, n.file, n.line
317
- FROM edges e JOIN nodes n ON e.target_id = n.id
318
- WHERE e.source_id = ? AND e.kind = 'calls'`,
319
- )
320
- .all(nodeId) as SymbolRef[]
321
- ).map(toSymbolRef);
322
-
323
- callers = (
324
- db
325
- .prepare(
326
- `SELECT n.name, n.kind, n.file, n.line
327
- FROM edges e JOIN nodes n ON e.source_id = n.id
328
- WHERE e.target_id = ? AND e.kind = 'calls'`,
329
- )
330
- .all(nodeId) as SymbolRef[]
331
- ).map(toSymbolRef);
332
- if (noTests) callers = callers.filter((c) => !isTestFile(c.file));
333
-
334
- const testCallerRows = db
335
- .prepare(
336
- `SELECT DISTINCT n.file FROM edges e JOIN nodes n ON e.source_id = n.id
337
- WHERE e.target_id = ? AND e.kind = 'calls'`,
338
- )
339
- .all(nodeId) as { file: string }[];
340
- relatedTests = testCallerRows.filter((r) => isTestFile(r.file)).map((r) => ({ file: r.file }));
341
- }
348
+ const { callees, callers, relatedTests } = nodeId
349
+ ? querySymbolEdges(db, nodeId, noTests)
350
+ : { callees: [], callers: [], relatedTests: [] };
342
351
 
343
352
  const health = nodeId ? buildHealth(db, nodeId, thresholds) : defaultHealth();
344
353
  const impact = nodeId
@@ -174,6 +174,21 @@ export function validateBoundaryConfig(config: unknown): { valid: boolean; error
174
174
 
175
175
  // ─── Preset Rule Generation ─────────────────────────────────────────
176
176
 
177
+ /** Collect the names of all modules assigned to layers outer than `layerIdx`. */
178
+ function collectOuterModules(
179
+ modulesByLayer: Map<string, string[]>,
180
+ layerIndex: Map<string, number>,
181
+ layerIdx: number,
182
+ ): string[] {
183
+ const outer: string[] = [];
184
+ for (const [otherLayer, otherModNames] of modulesByLayer) {
185
+ if (layerIndex.get(otherLayer)! > layerIdx) {
186
+ outer.push(...otherModNames);
187
+ }
188
+ }
189
+ return outer;
190
+ }
191
+
177
192
  function generatePresetRules(
178
193
  modules: Map<string, ResolvedModule>,
179
194
  presetName: string,
@@ -181,8 +196,7 @@ function generatePresetRules(
181
196
  const preset = PRESETS[presetName];
182
197
  if (!preset) return [];
183
198
 
184
- const layers = preset.layers;
185
- const layerIndex = new Map<string, number>(layers.map((l, i) => [l, i]));
199
+ const layerIndex = new Map<string, number>(preset.layers.map((l, i) => [l, i]));
186
200
 
187
201
  const modulesByLayer = new Map<string, string[]>();
188
202
  for (const [name, mod] of modules) {
@@ -194,13 +208,7 @@ function generatePresetRules(
194
208
 
195
209
  const rules: BoundaryRule[] = [];
196
210
  for (const [layer, modNames] of modulesByLayer) {
197
- const idx = layerIndex.get(layer)!;
198
- const outerModules: string[] = [];
199
- for (const [otherLayer, otherModNames] of modulesByLayer) {
200
- if (layerIndex.get(otherLayer)! > idx) {
201
- outerModules.push(...otherModNames);
202
- }
203
- }
211
+ const outerModules = collectOuterModules(modulesByLayer, layerIndex, layerIndex.get(layer)!);
204
212
  if (outerModules.length > 0) {
205
213
  for (const from of modNames) {
206
214
  rules.push({ from, notTo: outerModules });
@@ -136,6 +136,35 @@ async function initCfgParsers(
136
136
  return { parsers, getParserFn };
137
137
  }
138
138
 
139
+ /** Parse source via WASM when no native CFG data is available.
140
+ * Returns the parsed tree or undefined on any read/parse failure. */
141
+ function parseTreeForCfg(
142
+ relPath: string,
143
+ rootDir: string,
144
+ _langId: string,
145
+ parsers: unknown,
146
+ getParserFn: unknown,
147
+ ): { rootNode: TreeSitterNode } | undefined {
148
+ const absPath = path.join(rootDir, relPath);
149
+ let code: string;
150
+ try {
151
+ code = fs.readFileSync(absPath, 'utf-8');
152
+ } catch (e) {
153
+ debug(`cfg: cannot read ${relPath}: ${(e as Error).message}`);
154
+ return undefined;
155
+ }
156
+
157
+ const parser = (getParserFn as (parsers: unknown, absPath: string) => unknown)(parsers, absPath);
158
+ if (!parser) return undefined;
159
+
160
+ try {
161
+ return (parser as { parse: (code: string) => { rootNode: TreeSitterNode } }).parse(code);
162
+ } catch (e) {
163
+ debug(`cfg: parse failed for ${relPath}: ${(e as Error).message}`);
164
+ return undefined;
165
+ }
166
+ }
167
+
139
168
  function getTreeAndLang(
140
169
  symbols: FileSymbols,
141
170
  relPath: string,
@@ -152,28 +181,8 @@ function getTreeAndLang(
152
181
  if (!getParserFn) return null;
153
182
  langId = extToLang.get(ext);
154
183
  if (!langId || !CFG_RULES.has(langId)) return null;
155
-
156
- const absPath = path.join(rootDir, relPath);
157
- let code: string;
158
- try {
159
- code = fs.readFileSync(absPath, 'utf-8');
160
- } catch (e) {
161
- debug(`cfg: cannot read ${relPath}: ${(e as Error).message}`);
162
- return null;
163
- }
164
-
165
- const parser = (getParserFn as (parsers: unknown, absPath: string) => unknown)(
166
- parsers,
167
- absPath,
168
- );
169
- if (!parser) return null;
170
-
171
- try {
172
- tree = (parser as { parse: (code: string) => { rootNode: TreeSitterNode } }).parse(code);
173
- } catch (e) {
174
- debug(`cfg: parse failed for ${relPath}: ${(e as Error).message}`);
175
- return null;
176
- }
184
+ tree = parseTreeForCfg(relPath, rootDir, langId, parsers, getParserFn);
185
+ if (!tree) return null;
177
186
  }
178
187
 
179
188
  if (!langId) {
@@ -25,6 +25,22 @@ interface ParsedDiff {
25
25
  const HUNK_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
26
26
  const NEW_FILE_RE = /^\+\+\+ b\/(.+)/;
27
27
 
28
+ /** Returns true if the diff line marks the old file as /dev/null (new-file creation). */
29
+ function isDevNullSourceLine(line: string): boolean {
30
+ return line.startsWith('--- /dev/null');
31
+ }
32
+
33
+ /** Returns true if the diff line is a `---` source file header (not /dev/null). */
34
+ function isSourceFileHeaderLine(line: string): boolean {
35
+ return line.startsWith('--- ');
36
+ }
37
+
38
+ /** Extracts the new filename from a `+++ b/<file>` diff header, or null. */
39
+ function extractNewFileName(line: string): string | null {
40
+ const m = line.match(NEW_FILE_RE);
41
+ return m ? m[1]! : null;
42
+ }
43
+
28
44
  function pushHunkRanges(
29
45
  line: string,
30
46
  currentFile: string,
@@ -53,17 +69,17 @@ export function parseDiffOutput(diffOutput: string): ParsedDiff {
53
69
  let prevIsDevNull = false;
54
70
 
55
71
  for (const line of diffOutput.split('\n')) {
56
- if (line.startsWith('--- /dev/null')) {
72
+ if (isDevNullSourceLine(line)) {
57
73
  prevIsDevNull = true;
58
74
  continue;
59
75
  }
60
- if (line.startsWith('--- ')) {
76
+ if (isSourceFileHeaderLine(line)) {
61
77
  prevIsDevNull = false;
62
78
  continue;
63
79
  }
64
- const fileMatch = line.match(NEW_FILE_RE);
65
- if (fileMatch) {
66
- currentFile = fileMatch[1]!;
80
+ const newFile = extractNewFileName(line);
81
+ if (newFile) {
82
+ currentFile = newFile;
67
83
  if (!changedRanges.has(currentFile)) changedRanges.set(currentFile, []);
68
84
  if (!oldRanges.has(currentFile)) oldRanges.set(currentFile, []);
69
85
  if (prevIsDevNull) newFiles.add(currentFile);
@@ -146,33 +146,42 @@ function analyzeDrift(
146
146
 
147
147
  // ─── Core Analysis ────────────────────────────────────────────────────
148
148
 
149
- export function communitiesData(
150
- customDbPath?: string,
151
- opts: {
152
- functions?: boolean;
153
- resolution?: number;
154
- noTests?: boolean;
155
- drift?: boolean;
156
- json?: boolean;
157
- config?: CodegraphConfig;
158
- maxLevels?: number;
159
- maxLocalPasses?: number;
160
- refinementTheta?: number;
161
- limit?: number;
162
- offset?: number;
163
- repo?: Repository;
164
- } = {},
165
- ): Record<string, unknown> {
149
+ type CommunitiesDataOpts = {
150
+ functions?: boolean;
151
+ resolution?: number;
152
+ noTests?: boolean;
153
+ drift?: boolean;
154
+ json?: boolean;
155
+ config?: CodegraphConfig;
156
+ maxLevels?: number;
157
+ maxLocalPasses?: number;
158
+ refinementTheta?: number;
159
+ limit?: number;
160
+ offset?: number;
161
+ repo?: Repository;
162
+ };
163
+
164
+ /** Load dependency graph from the repo, then close the DB connection. */
165
+ function loadCommunityGraph(
166
+ customDbPath: string | undefined,
167
+ opts: CommunitiesDataOpts,
168
+ ): CodeGraph {
166
169
  const { repo, close } = openRepo(customDbPath, opts);
167
- let graph: CodeGraph;
168
170
  try {
169
- graph = buildDependencyGraph(repo, {
171
+ return buildDependencyGraph(repo, {
170
172
  fileLevel: !opts.functions,
171
173
  noTests: opts.noTests,
172
174
  });
173
175
  } finally {
174
176
  close();
175
177
  }
178
+ }
179
+
180
+ export function communitiesData(
181
+ customDbPath?: string,
182
+ opts: CommunitiesDataOpts = {},
183
+ ): Record<string, unknown> {
184
+ const graph = loadCommunityGraph(customDbPath, opts);
176
185
 
177
186
  if (graph.nodeCount === 0 || graph.edgeCount === 0) {
178
187
  return {