@optave/codegraph 3.4.0 → 3.5.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 (453) hide show
  1. package/README.md +23 -22
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +3 -9
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
  6. package/dist/ast-analysis/rules/javascript.js +1 -0
  7. package/dist/ast-analysis/rules/javascript.js.map +1 -1
  8. package/dist/ast-analysis/shared.d.ts.map +1 -1
  9. package/dist/ast-analysis/shared.js +0 -1
  10. package/dist/ast-analysis/shared.js.map +1 -1
  11. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  12. package/dist/ast-analysis/visitors/ast-store-visitor.js +103 -35
  13. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  14. package/dist/ast-analysis/visitors/cfg-conditionals.d.ts +5 -0
  15. package/dist/ast-analysis/visitors/cfg-conditionals.d.ts.map +1 -0
  16. package/dist/ast-analysis/visitors/cfg-conditionals.js +166 -0
  17. package/dist/ast-analysis/visitors/cfg-conditionals.js.map +1 -0
  18. package/dist/ast-analysis/visitors/cfg-loops.d.ts +7 -0
  19. package/dist/ast-analysis/visitors/cfg-loops.d.ts.map +1 -0
  20. package/dist/ast-analysis/visitors/cfg-loops.js +73 -0
  21. package/dist/ast-analysis/visitors/cfg-loops.js.map +1 -0
  22. package/dist/ast-analysis/visitors/cfg-shared.d.ts +56 -0
  23. package/dist/ast-analysis/visitors/cfg-shared.d.ts.map +1 -0
  24. package/dist/ast-analysis/visitors/cfg-shared.js +107 -0
  25. package/dist/ast-analysis/visitors/cfg-shared.js.map +1 -0
  26. package/dist/ast-analysis/visitors/cfg-try-catch.d.ts +4 -0
  27. package/dist/ast-analysis/visitors/cfg-try-catch.d.ts.map +1 -0
  28. package/dist/ast-analysis/visitors/cfg-try-catch.js +100 -0
  29. package/dist/ast-analysis/visitors/cfg-try-catch.js.map +1 -0
  30. package/dist/ast-analysis/visitors/cfg-visitor.d.ts +2 -2
  31. package/dist/ast-analysis/visitors/cfg-visitor.d.ts.map +1 -1
  32. package/dist/ast-analysis/visitors/cfg-visitor.js +11 -445
  33. package/dist/ast-analysis/visitors/cfg-visitor.js.map +1 -1
  34. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  35. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  36. package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
  37. package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
  38. package/dist/cli/commands/batch.d.ts.map +1 -1
  39. package/dist/cli/commands/batch.js +4 -3
  40. package/dist/cli/commands/batch.js.map +1 -1
  41. package/dist/cli/commands/branch-compare.js +1 -1
  42. package/dist/cli/commands/branch-compare.js.map +1 -1
  43. package/dist/cli/commands/build.js +1 -1
  44. package/dist/cli/commands/build.js.map +1 -1
  45. package/dist/cli/commands/info.d.ts.map +1 -1
  46. package/dist/cli/commands/info.js +1 -2
  47. package/dist/cli/commands/info.js.map +1 -1
  48. package/dist/cli/commands/path.d.ts.map +1 -1
  49. package/dist/cli/commands/path.js +7 -2
  50. package/dist/cli/commands/path.js.map +1 -1
  51. package/dist/cli/commands/plot.d.ts.map +1 -1
  52. package/dist/cli/commands/plot.js +2 -2
  53. package/dist/cli/commands/plot.js.map +1 -1
  54. package/dist/cli/commands/watch.js +1 -1
  55. package/dist/cli/commands/watch.js.map +1 -1
  56. package/dist/cli/index.js +2 -2
  57. package/dist/cli/index.js.map +1 -1
  58. package/dist/cli/shared/open-graph.d.ts +2 -2
  59. package/dist/cli/shared/open-graph.d.ts.map +1 -1
  60. package/dist/cli/shared/open-graph.js.map +1 -1
  61. package/dist/cli/types.d.ts +1 -1
  62. package/dist/cli/types.d.ts.map +1 -1
  63. package/dist/cli.js +2 -3
  64. package/dist/cli.js.map +1 -1
  65. package/dist/db/better-sqlite3.d.ts +3 -0
  66. package/dist/db/better-sqlite3.d.ts.map +1 -0
  67. package/dist/db/better-sqlite3.js +19 -0
  68. package/dist/db/better-sqlite3.js.map +1 -0
  69. package/dist/db/connection.d.ts +30 -2
  70. package/dist/db/connection.d.ts.map +1 -1
  71. package/dist/db/connection.js +167 -4
  72. package/dist/db/connection.js.map +1 -1
  73. package/dist/db/index.d.ts +2 -2
  74. package/dist/db/index.d.ts.map +1 -1
  75. package/dist/db/index.js +1 -1
  76. package/dist/db/index.js.map +1 -1
  77. package/dist/db/migrations.d.ts.map +1 -1
  78. package/dist/db/migrations.js +9 -0
  79. package/dist/db/migrations.js.map +1 -1
  80. package/dist/db/query-builder.d.ts +5 -5
  81. package/dist/db/query-builder.d.ts.map +1 -1
  82. package/dist/db/query-builder.js +20 -4
  83. package/dist/db/query-builder.js.map +1 -1
  84. package/dist/db/repository/index.d.ts +1 -0
  85. package/dist/db/repository/index.d.ts.map +1 -1
  86. package/dist/db/repository/index.js +1 -0
  87. package/dist/db/repository/index.js.map +1 -1
  88. package/dist/db/repository/native-repository.d.ts +58 -0
  89. package/dist/db/repository/native-repository.d.ts.map +1 -0
  90. package/dist/db/repository/native-repository.js +261 -0
  91. package/dist/db/repository/native-repository.js.map +1 -0
  92. package/dist/db/repository/nodes.d.ts +4 -4
  93. package/dist/db/repository/nodes.d.ts.map +1 -1
  94. package/dist/db/repository/nodes.js +6 -6
  95. package/dist/db/repository/nodes.js.map +1 -1
  96. package/dist/domain/analysis/brief.d.ts.map +1 -1
  97. package/dist/domain/analysis/brief.js +1 -3
  98. package/dist/domain/analysis/brief.js.map +1 -1
  99. package/dist/domain/analysis/context.d.ts.map +1 -1
  100. package/dist/domain/analysis/context.js +2 -4
  101. package/dist/domain/analysis/context.js.map +1 -1
  102. package/dist/domain/analysis/dependencies.d.ts +49 -0
  103. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  104. package/dist/domain/analysis/dependencies.js +145 -0
  105. package/dist/domain/analysis/dependencies.js.map +1 -1
  106. package/dist/domain/analysis/diff-impact.d.ts +76 -0
  107. package/dist/domain/analysis/diff-impact.d.ts.map +1 -0
  108. package/dist/domain/analysis/diff-impact.js +282 -0
  109. package/dist/domain/analysis/diff-impact.js.map +1 -0
  110. package/dist/domain/analysis/exports.d.ts.map +1 -1
  111. package/dist/domain/analysis/exports.js +0 -1
  112. package/dist/domain/analysis/exports.js.map +1 -1
  113. package/dist/domain/analysis/fn-impact.d.ts +66 -0
  114. package/dist/domain/analysis/fn-impact.d.ts.map +1 -0
  115. package/dist/domain/analysis/fn-impact.js +189 -0
  116. package/dist/domain/analysis/fn-impact.js.map +1 -0
  117. package/dist/domain/analysis/impact.d.ts +8 -148
  118. package/dist/domain/analysis/impact.d.ts.map +1 -1
  119. package/dist/domain/analysis/impact.js +8 -568
  120. package/dist/domain/analysis/impact.js.map +1 -1
  121. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  122. package/dist/domain/analysis/module-map.js +1 -3
  123. package/dist/domain/analysis/module-map.js.map +1 -1
  124. package/dist/domain/graph/builder/context.d.ts +3 -3
  125. package/dist/domain/graph/builder/context.d.ts.map +1 -1
  126. package/dist/domain/graph/builder/context.js +1 -0
  127. package/dist/domain/graph/builder/context.js.map +1 -1
  128. package/dist/domain/graph/builder/helpers.d.ts +4 -5
  129. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  130. package/dist/domain/graph/builder/helpers.js +1 -2
  131. package/dist/domain/graph/builder/helpers.js.map +1 -1
  132. package/dist/domain/graph/builder/incremental.d.ts +2 -3
  133. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  134. package/dist/domain/graph/builder/incremental.js.map +1 -1
  135. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  136. package/dist/domain/graph/builder/pipeline.js +34 -6
  137. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  138. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  139. package/dist/domain/graph/builder/stages/build-edges.js +113 -15
  140. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  141. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  142. package/dist/domain/graph/builder/stages/build-structure.js +186 -62
  143. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  144. package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
  145. package/dist/domain/graph/builder/stages/collect-files.js +71 -7
  146. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  147. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  148. package/dist/domain/graph/builder/stages/detect-changes.js +42 -20
  149. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  150. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  151. package/dist/domain/graph/builder/stages/finalize.js +111 -64
  152. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  153. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  154. package/dist/domain/graph/builder/stages/insert-nodes.js +104 -9
  155. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  156. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  157. package/dist/domain/graph/builder/stages/resolve-imports.js +58 -11
  158. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  159. package/dist/domain/graph/cycles.js +2 -2
  160. package/dist/domain/graph/cycles.js.map +1 -1
  161. package/dist/domain/graph/resolve.d.ts.map +1 -1
  162. package/dist/domain/graph/resolve.js +10 -8
  163. package/dist/domain/graph/resolve.js.map +1 -1
  164. package/dist/domain/graph/watcher.d.ts.map +1 -1
  165. package/dist/domain/graph/watcher.js +1 -3
  166. package/dist/domain/graph/watcher.js.map +1 -1
  167. package/dist/domain/parser.d.ts.map +1 -1
  168. package/dist/domain/parser.js +12 -12
  169. package/dist/domain/parser.js.map +1 -1
  170. package/dist/domain/queries.d.ts +3 -2
  171. package/dist/domain/queries.d.ts.map +1 -1
  172. package/dist/domain/queries.js +3 -2
  173. package/dist/domain/queries.js.map +1 -1
  174. package/dist/domain/search/generator.d.ts.map +1 -1
  175. package/dist/domain/search/generator.js.map +1 -1
  176. package/dist/extractors/csharp.js +2 -2
  177. package/dist/extractors/csharp.js.map +1 -1
  178. package/dist/extractors/go.js +2 -2
  179. package/dist/extractors/go.js.map +1 -1
  180. package/dist/extractors/helpers.d.ts +5 -0
  181. package/dist/extractors/helpers.d.ts.map +1 -1
  182. package/dist/extractors/helpers.js +5 -0
  183. package/dist/extractors/helpers.js.map +1 -1
  184. package/dist/extractors/javascript.js +111 -98
  185. package/dist/extractors/javascript.js.map +1 -1
  186. package/dist/extractors/php.js +2 -2
  187. package/dist/extractors/php.js.map +1 -1
  188. package/dist/extractors/python.js +2 -2
  189. package/dist/extractors/python.js.map +1 -1
  190. package/dist/extractors/rust.js +4 -3
  191. package/dist/extractors/rust.js.map +1 -1
  192. package/dist/features/ast.d.ts +14 -1
  193. package/dist/features/ast.d.ts.map +1 -1
  194. package/dist/features/ast.js +38 -1
  195. package/dist/features/ast.js.map +1 -1
  196. package/dist/features/audit.d.ts.map +1 -1
  197. package/dist/features/audit.js +1 -2
  198. package/dist/features/audit.js.map +1 -1
  199. package/dist/features/branch-compare.d.ts.map +1 -1
  200. package/dist/features/branch-compare.js +5 -4
  201. package/dist/features/branch-compare.js.map +1 -1
  202. package/dist/features/cfg.d.ts.map +1 -1
  203. package/dist/features/cfg.js +2 -4
  204. package/dist/features/cfg.js.map +1 -1
  205. package/dist/features/cochange.js +4 -4
  206. package/dist/features/cochange.js.map +1 -1
  207. package/dist/features/communities.js +4 -4
  208. package/dist/features/communities.js.map +1 -1
  209. package/dist/features/complexity-query.d.ts +37 -0
  210. package/dist/features/complexity-query.d.ts.map +1 -0
  211. package/dist/features/complexity-query.js +263 -0
  212. package/dist/features/complexity-query.js.map +1 -0
  213. package/dist/features/complexity.d.ts +2 -30
  214. package/dist/features/complexity.d.ts.map +1 -1
  215. package/dist/features/complexity.js +7 -261
  216. package/dist/features/complexity.js.map +1 -1
  217. package/dist/features/dataflow.d.ts.map +1 -1
  218. package/dist/features/dataflow.js +8 -24
  219. package/dist/features/dataflow.js.map +1 -1
  220. package/dist/features/export.d.ts +7 -8
  221. package/dist/features/export.d.ts.map +1 -1
  222. package/dist/features/export.js.map +1 -1
  223. package/dist/features/flow.d.ts.map +1 -1
  224. package/dist/features/flow.js.map +1 -1
  225. package/dist/features/graph-enrichment.d.ts.map +1 -1
  226. package/dist/features/graph-enrichment.js +1 -3
  227. package/dist/features/graph-enrichment.js.map +1 -1
  228. package/dist/features/manifesto.js +8 -8
  229. package/dist/features/manifesto.js.map +1 -1
  230. package/dist/features/snapshot.js +2 -2
  231. package/dist/features/snapshot.js.map +1 -1
  232. package/dist/features/structure-query.d.ts +76 -0
  233. package/dist/features/structure-query.d.ts.map +1 -0
  234. package/dist/features/structure-query.js +245 -0
  235. package/dist/features/structure-query.js.map +1 -0
  236. package/dist/features/structure.d.ts +12 -67
  237. package/dist/features/structure.d.ts.map +1 -1
  238. package/dist/features/structure.js +188 -244
  239. package/dist/features/structure.js.map +1 -1
  240. package/dist/features/triage.js +2 -2
  241. package/dist/features/triage.js.map +1 -1
  242. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  243. package/dist/graph/algorithms/leiden/adapter.js +2 -9
  244. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  245. package/dist/graph/classifiers/roles.d.ts +5 -1
  246. package/dist/graph/classifiers/roles.d.ts.map +1 -1
  247. package/dist/graph/classifiers/roles.js +20 -12
  248. package/dist/graph/classifiers/roles.js.map +1 -1
  249. package/dist/index.d.ts +1 -0
  250. package/dist/index.d.ts.map +1 -1
  251. package/dist/index.js.map +1 -1
  252. package/dist/infrastructure/config.d.ts.map +1 -1
  253. package/dist/infrastructure/config.js +12 -11
  254. package/dist/infrastructure/config.js.map +1 -1
  255. package/dist/infrastructure/native.d.ts.map +1 -1
  256. package/dist/infrastructure/native.js +7 -3
  257. package/dist/infrastructure/native.js.map +1 -1
  258. package/dist/infrastructure/registry.d.ts.map +1 -1
  259. package/dist/infrastructure/registry.js +1 -1
  260. package/dist/infrastructure/registry.js.map +1 -1
  261. package/dist/infrastructure/update-check.js +3 -3
  262. package/dist/infrastructure/update-check.js.map +1 -1
  263. package/dist/mcp/server.d.ts.map +1 -1
  264. package/dist/mcp/server.js +4 -17
  265. package/dist/mcp/server.js.map +1 -1
  266. package/dist/mcp/tool-registry.d.ts.map +1 -1
  267. package/dist/mcp/tool-registry.js +9 -4
  268. package/dist/mcp/tool-registry.js.map +1 -1
  269. package/dist/mcp/tools/audit.js +1 -1
  270. package/dist/mcp/tools/audit.js.map +1 -1
  271. package/dist/mcp/tools/cfg.js +1 -1
  272. package/dist/mcp/tools/cfg.js.map +1 -1
  273. package/dist/mcp/tools/check.js +2 -2
  274. package/dist/mcp/tools/check.js.map +1 -1
  275. package/dist/mcp/tools/dataflow.js +2 -2
  276. package/dist/mcp/tools/dataflow.js.map +1 -1
  277. package/dist/mcp/tools/export-graph.js +1 -1
  278. package/dist/mcp/tools/export-graph.js.map +1 -1
  279. package/dist/mcp/tools/index.d.ts.map +1 -1
  280. package/dist/mcp/tools/index.js.map +1 -1
  281. package/dist/mcp/tools/path.d.ts +1 -0
  282. package/dist/mcp/tools/path.d.ts.map +1 -1
  283. package/dist/mcp/tools/path.js +9 -0
  284. package/dist/mcp/tools/path.js.map +1 -1
  285. package/dist/mcp/tools/query.js +1 -1
  286. package/dist/mcp/tools/query.js.map +1 -1
  287. package/dist/mcp/tools/semantic-search.js +1 -1
  288. package/dist/mcp/tools/semantic-search.js.map +1 -1
  289. package/dist/mcp/tools/sequence.js +1 -1
  290. package/dist/mcp/tools/sequence.js.map +1 -1
  291. package/dist/mcp/tools/symbol-children.js +1 -1
  292. package/dist/mcp/tools/symbol-children.js.map +1 -1
  293. package/dist/mcp/tools/triage.js +1 -1
  294. package/dist/mcp/tools/triage.js.map +1 -1
  295. package/dist/presentation/audit.d.ts.map +1 -1
  296. package/dist/presentation/audit.js +0 -1
  297. package/dist/presentation/audit.js.map +1 -1
  298. package/dist/presentation/diff-impact-mermaid.d.ts +11 -0
  299. package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -0
  300. package/dist/presentation/diff-impact-mermaid.js +105 -0
  301. package/dist/presentation/diff-impact-mermaid.js.map +1 -0
  302. package/dist/presentation/flow.d.ts.map +1 -1
  303. package/dist/presentation/flow.js +0 -2
  304. package/dist/presentation/flow.js.map +1 -1
  305. package/dist/presentation/manifesto.d.ts.map +1 -1
  306. package/dist/presentation/manifesto.js +0 -1
  307. package/dist/presentation/manifesto.js.map +1 -1
  308. package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
  309. package/dist/presentation/queries-cli/inspect.js.map +1 -1
  310. package/dist/presentation/queries-cli/path.d.ts.map +1 -1
  311. package/dist/presentation/queries-cli/path.js +45 -1
  312. package/dist/presentation/queries-cli/path.js.map +1 -1
  313. package/dist/presentation/result-formatter.d.ts.map +1 -1
  314. package/dist/presentation/result-formatter.js +1 -3
  315. package/dist/presentation/result-formatter.js.map +1 -1
  316. package/dist/presentation/sequence.d.ts.map +1 -1
  317. package/dist/presentation/sequence.js +0 -1
  318. package/dist/presentation/sequence.js.map +1 -1
  319. package/dist/presentation/structure.d.ts.map +1 -1
  320. package/dist/presentation/structure.js.map +1 -1
  321. package/dist/presentation/triage.d.ts.map +1 -1
  322. package/dist/presentation/triage.js +0 -1
  323. package/dist/presentation/triage.js.map +1 -1
  324. package/dist/shared/constants.d.ts +9 -3
  325. package/dist/shared/constants.d.ts.map +1 -1
  326. package/dist/shared/constants.js +6 -3
  327. package/dist/shared/constants.js.map +1 -1
  328. package/dist/shared/errors.d.ts +2 -0
  329. package/dist/shared/errors.d.ts.map +1 -1
  330. package/dist/shared/errors.js +4 -0
  331. package/dist/shared/errors.js.map +1 -1
  332. package/dist/shared/version.d.ts +2 -0
  333. package/dist/shared/version.d.ts.map +1 -0
  334. package/dist/shared/version.js +5 -0
  335. package/dist/shared/version.js.map +1 -0
  336. package/dist/types.d.ts +230 -2
  337. package/dist/types.d.ts.map +1 -1
  338. package/package.json +62 -11
  339. package/src/ast-analysis/engine.ts +3 -9
  340. package/src/ast-analysis/rules/javascript.ts +1 -0
  341. package/src/ast-analysis/shared.ts +0 -1
  342. package/src/ast-analysis/visitors/ast-store-visitor.ts +102 -33
  343. package/src/ast-analysis/visitors/cfg-conditionals.ts +227 -0
  344. package/src/ast-analysis/visitors/cfg-loops.ts +136 -0
  345. package/src/ast-analysis/visitors/cfg-shared.ts +196 -0
  346. package/src/ast-analysis/visitors/cfg-try-catch.ts +142 -0
  347. package/src/ast-analysis/visitors/cfg-visitor.ts +34 -655
  348. package/src/ast-analysis/visitors/complexity-visitor.ts +0 -1
  349. package/src/ast-analysis/visitors/dataflow-visitor.ts +0 -1
  350. package/src/cli/commands/batch.ts +4 -3
  351. package/src/cli/commands/branch-compare.ts +1 -1
  352. package/src/cli/commands/build.ts +1 -1
  353. package/src/cli/commands/info.ts +1 -2
  354. package/src/cli/commands/path.ts +7 -2
  355. package/src/cli/commands/plot.ts +2 -2
  356. package/src/cli/commands/watch.ts +1 -1
  357. package/src/cli/index.ts +2 -2
  358. package/src/cli/shared/open-graph.ts +2 -2
  359. package/src/cli/types.ts +1 -1
  360. package/src/cli.ts +2 -3
  361. package/src/db/better-sqlite3.ts +20 -0
  362. package/src/db/connection.ts +191 -16
  363. package/src/db/index.ts +5 -1
  364. package/src/db/migrations.ts +9 -0
  365. package/src/db/query-builder.ts +30 -5
  366. package/src/db/repository/index.ts +1 -0
  367. package/src/db/repository/native-repository.ts +361 -0
  368. package/src/db/repository/nodes.ts +7 -3
  369. package/src/domain/analysis/brief.ts +0 -1
  370. package/src/domain/analysis/context.ts +2 -6
  371. package/src/domain/analysis/dependencies.ts +165 -0
  372. package/src/domain/analysis/diff-impact.ts +354 -0
  373. package/src/domain/analysis/exports.ts +0 -2
  374. package/src/domain/analysis/fn-impact.ts +241 -0
  375. package/src/domain/analysis/impact.ts +8 -718
  376. package/src/domain/analysis/module-map.ts +1 -5
  377. package/src/domain/graph/builder/context.ts +4 -2
  378. package/src/domain/graph/builder/helpers.ts +14 -11
  379. package/src/domain/graph/builder/incremental.ts +33 -28
  380. package/src/domain/graph/builder/pipeline.ts +37 -5
  381. package/src/domain/graph/builder/stages/build-edges.ts +131 -20
  382. package/src/domain/graph/builder/stages/build-structure.ts +245 -80
  383. package/src/domain/graph/builder/stages/collect-files.ts +84 -7
  384. package/src/domain/graph/builder/stages/detect-changes.ts +49 -32
  385. package/src/domain/graph/builder/stages/finalize.ts +132 -84
  386. package/src/domain/graph/builder/stages/insert-nodes.ts +141 -18
  387. package/src/domain/graph/builder/stages/resolve-imports.ts +75 -10
  388. package/src/domain/graph/cycles.ts +2 -2
  389. package/src/domain/graph/resolve.ts +14 -8
  390. package/src/domain/graph/watcher.ts +2 -4
  391. package/src/domain/parser.ts +12 -13
  392. package/src/domain/queries.ts +2 -2
  393. package/src/domain/search/generator.ts +3 -4
  394. package/src/extractors/csharp.ts +2 -2
  395. package/src/extractors/go.ts +2 -2
  396. package/src/extractors/helpers.ts +6 -0
  397. package/src/extractors/javascript.ts +112 -97
  398. package/src/extractors/php.ts +2 -2
  399. package/src/extractors/python.ts +2 -2
  400. package/src/extractors/rust.ts +4 -3
  401. package/src/features/ast.ts +66 -1
  402. package/src/features/audit.ts +1 -2
  403. package/src/features/branch-compare.ts +6 -10
  404. package/src/features/cfg.ts +2 -4
  405. package/src/features/cochange.ts +4 -4
  406. package/src/features/communities.ts +4 -4
  407. package/src/features/complexity-query.ts +370 -0
  408. package/src/features/complexity.ts +6 -365
  409. package/src/features/dataflow.ts +48 -70
  410. package/src/features/export.ts +12 -16
  411. package/src/features/flow.ts +0 -1
  412. package/src/features/graph-enrichment.ts +1 -3
  413. package/src/features/manifesto.ts +8 -8
  414. package/src/features/snapshot.ts +3 -3
  415. package/src/features/structure-query.ts +387 -0
  416. package/src/features/structure.ts +231 -376
  417. package/src/features/triage.ts +2 -2
  418. package/src/graph/algorithms/leiden/adapter.ts +2 -9
  419. package/src/graph/classifiers/roles.ts +22 -13
  420. package/src/index.ts +1 -0
  421. package/src/infrastructure/config.ts +12 -13
  422. package/src/infrastructure/native.ts +7 -3
  423. package/src/infrastructure/registry.ts +1 -1
  424. package/src/infrastructure/update-check.ts +3 -3
  425. package/src/mcp/server.ts +4 -20
  426. package/src/mcp/tool-registry.ts +11 -4
  427. package/src/mcp/tools/audit.ts +1 -1
  428. package/src/mcp/tools/cfg.ts +1 -1
  429. package/src/mcp/tools/check.ts +2 -2
  430. package/src/mcp/tools/dataflow.ts +2 -2
  431. package/src/mcp/tools/export-graph.ts +1 -1
  432. package/src/mcp/tools/index.ts +0 -1
  433. package/src/mcp/tools/path.ts +10 -0
  434. package/src/mcp/tools/query.ts +1 -1
  435. package/src/mcp/tools/semantic-search.ts +1 -1
  436. package/src/mcp/tools/sequence.ts +1 -1
  437. package/src/mcp/tools/symbol-children.ts +1 -1
  438. package/src/mcp/tools/triage.ts +1 -1
  439. package/src/presentation/audit.ts +0 -1
  440. package/src/presentation/diff-impact-mermaid.ts +127 -0
  441. package/src/presentation/flow.ts +0 -2
  442. package/src/presentation/manifesto.ts +0 -1
  443. package/src/presentation/queries-cli/inspect.ts +0 -1
  444. package/src/presentation/queries-cli/path.ts +71 -1
  445. package/src/presentation/result-formatter.ts +0 -1
  446. package/src/presentation/sequence.ts +0 -1
  447. package/src/presentation/structure.ts +0 -12
  448. package/src/presentation/triage.ts +0 -1
  449. package/src/shared/constants.ts +33 -19
  450. package/src/shared/errors.ts +5 -0
  451. package/src/shared/version.ts +10 -0
  452. package/src/types.ts +277 -10
  453. package/src/vendor.d.ts +0 -39
@@ -6,7 +6,7 @@ import type {
6
6
  TreeSitterTree,
7
7
  TypeMapEntry,
8
8
  } from '../types.js';
9
- import { findChild, nodeEndLine, pythonVisibility } from './helpers.js';
9
+ import { findChild, MAX_WALK_DEPTH, nodeEndLine, pythonVisibility } from './helpers.js';
10
10
 
11
11
  /** Built-in globals that start with uppercase but are not user-defined types. */
12
12
  const BUILTIN_GLOBALS_PY: Set<string> = new Set([
@@ -365,7 +365,7 @@ function extractPythonTypeMapDepth(
365
365
  ctx: ExtractorOutput,
366
366
  depth: number,
367
367
  ): void {
368
- if (depth >= 200) return;
368
+ if (depth >= MAX_WALK_DEPTH) return;
369
369
 
370
370
  // typed_parameter: identifier : type (confidence 0.9)
371
371
  if (node.type === 'typed_parameter') {
@@ -5,7 +5,7 @@ import type {
5
5
  TreeSitterNode,
6
6
  TreeSitterTree,
7
7
  } from '../types.js';
8
- import { findChild, nodeEndLine, rustVisibility } from './helpers.js';
8
+ import { findChild, MAX_WALK_DEPTH, nodeEndLine, rustVisibility } from './helpers.js';
9
9
 
10
10
  /**
11
11
  * Extract symbols from Rust files.
@@ -227,7 +227,8 @@ function extractRustParameters(paramListNode: TreeSitterNode | null): SubDeclara
227
227
  const param = paramListNode.child(i);
228
228
  if (!param) continue;
229
229
  if (param.type === 'self_parameter') {
230
- params.push({ name: 'self', kind: 'parameter', line: param.startPosition.row + 1 });
230
+ // Skip self parameters matches native engine behaviour
231
+ continue;
231
232
  } else if (param.type === 'parameter') {
232
233
  const pattern = param.childForFieldName('pattern');
233
234
  if (pattern) {
@@ -274,7 +275,7 @@ function extractRustTypeMap(node: TreeSitterNode, ctx: ExtractorOutput): void {
274
275
  }
275
276
 
276
277
  function extractRustTypeMapDepth(node: TreeSitterNode, ctx: ExtractorOutput, depth: number): void {
277
- if (depth >= 200) return;
278
+ if (depth >= MAX_WALK_DEPTH) return;
278
279
 
279
280
  // let x: MyType = ...
280
281
  if (node.type === 'let_declaration') {
@@ -65,8 +65,73 @@ export async function buildAstNodes(
65
65
  db: BetterSqlite3Database,
66
66
  fileSymbols: Map<string, FileSymbols>,
67
67
  _rootDir: string,
68
- _engineOpts?: unknown,
68
+ engineOpts?: {
69
+ nativeDb?: {
70
+ bulkInsertAstNodes(
71
+ batches: Array<{
72
+ file: string;
73
+ nodes: Array<{
74
+ line: number;
75
+ kind: string;
76
+ name: string;
77
+ text?: string | null;
78
+ receiver?: string | null;
79
+ }>;
80
+ }>,
81
+ ): number;
82
+ };
83
+ },
69
84
  ): Promise<void> {
85
+ // ── Native bulk-insert fast path ──────────────────────────────────────
86
+ // Uses NativeDatabase persistent connection (Phase 6.15+).
87
+ // Standalone napi functions were removed in 6.17.
88
+ const nativeDb = engineOpts?.nativeDb;
89
+ if (nativeDb?.bulkInsertAstNodes) {
90
+ let needsJsFallback = false;
91
+ const batches: Array<{
92
+ file: string;
93
+ nodes: Array<{
94
+ line: number;
95
+ kind: string;
96
+ name: string;
97
+ text?: string | null;
98
+ receiver?: string | null;
99
+ }>;
100
+ }> = [];
101
+
102
+ for (const [relPath, symbols] of fileSymbols) {
103
+ if (Array.isArray(symbols.astNodes)) {
104
+ batches.push({
105
+ file: relPath,
106
+ nodes: symbols.astNodes.map((n) => ({
107
+ line: n.line,
108
+ kind: n.kind,
109
+ name: n.name,
110
+ text: n.text,
111
+ receiver: n.receiver,
112
+ })),
113
+ });
114
+ } else if (symbols.calls || symbols._tree) {
115
+ needsJsFallback = true;
116
+ break;
117
+ }
118
+ }
119
+
120
+ if (!needsJsFallback) {
121
+ const expectedNodes = batches.reduce((s, b) => s + b.nodes.length, 0);
122
+ const inserted = nativeDb.bulkInsertAstNodes(batches);
123
+ if (inserted === expectedNodes) {
124
+ debug(`AST extraction (native bulk): ${inserted} nodes stored`);
125
+ return;
126
+ }
127
+ debug(
128
+ `AST extraction (native bulk): expected ${expectedNodes}, got ${inserted} — falling back to JS`,
129
+ );
130
+ // fall through to JS path
131
+ }
132
+ }
133
+
134
+ // ── JS fallback path ──────────────────────────────────────────────────
70
135
  let insertStmt: ReturnType<BetterSqlite3Database['prepare']>;
71
136
  try {
72
137
  insertStmt = db.prepare(
@@ -30,7 +30,7 @@ function resolveThresholds(
30
30
  const repoRoot = path.resolve(dbDir, '..');
31
31
  return loadConfig(repoRoot);
32
32
  })();
33
- const userRules = (cfg as Record<string, unknown>)['manifesto'] || {};
33
+ const userRules = (cfg as Record<string, unknown>).manifesto || {};
34
34
  const resolved: Record<string, ThresholdEntry> = {};
35
35
  for (const def of FUNCTION_RULES) {
36
36
  const user = (userRules as Record<string, { warn?: number; fail?: number }>)[def.name];
@@ -163,7 +163,6 @@ export function auditData(
163
163
  const explained = explainData(target, customDbPath, { noTests, depth: 0 });
164
164
 
165
165
  // Apply --file and --kind filters for function targets
166
- // biome-ignore lint/suspicious/noExplicitAny: explainData returns a union type that varies by kind
167
166
  let results: any[] = explained.results;
168
167
  if (explained.kind === 'function') {
169
168
  if (fileFilters.length > 0)
@@ -2,17 +2,11 @@ import { execFileSync } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import Database from 'better-sqlite3';
5
+ import { getDatabase } from '../db/better-sqlite3.js';
6
6
  import { buildGraph } from '../domain/graph/builder.js';
7
7
  import { kindIcon } from '../domain/queries.js';
8
8
  import { isTestFile } from '../infrastructure/test-filter.js';
9
- import type { BetterSqlite3Database, EngineMode } from '../types.js';
10
-
11
- type DatabaseConstructor = new (
12
- path: string,
13
- opts?: Record<string, unknown>,
14
- ) => BetterSqlite3Database;
15
- const Db = Database as unknown as DatabaseConstructor;
9
+ import type { EngineMode } from '../types.js';
16
10
 
17
11
  // ─── Git Helpers ────────────────────────────────────────────────────────
18
12
 
@@ -111,7 +105,8 @@ function loadSymbolsFromDb(
111
105
  changedFiles: string[],
112
106
  noTests: boolean,
113
107
  ): Map<string, SymbolInfo> {
114
- const db = new Db(dbPath, { readonly: true });
108
+ const Database = getDatabase();
109
+ const db = new Database(dbPath, { readonly: true });
115
110
  try {
116
111
  const symbols = new Map<string, SymbolInfo>();
117
112
 
@@ -180,7 +175,8 @@ function loadCallersFromDb(
180
175
  ): CallerInfo[] {
181
176
  if (nodeIds.length === 0) return [];
182
177
 
183
- const db = new Db(dbPath, { readonly: true });
178
+ const Database = getDatabase();
179
+ const db = new Database(dbPath, { readonly: true });
184
180
  try {
185
181
  const allCallers = new Set<string>();
186
182
 
@@ -62,8 +62,7 @@ export function buildFunctionCFG(functionNode: TreeSitterNode, langId: string):
62
62
  };
63
63
 
64
64
  const results = walkWithVisitors(functionNode, [visitor], langId, walkerOpts);
65
- // biome-ignore lint/complexity/useLiteralKeys: noPropertyAccessFromIndexSignature requires bracket notation
66
- const cfgResults = (results['cfg'] || []) as Array<{
65
+ const cfgResults = (results.cfg || []) as Array<{
67
66
  funcNode: TreeSitterNode;
68
67
  blocks: CfgBuildBlock[];
69
68
  edges: CfgBuildEdge[];
@@ -214,8 +213,7 @@ function buildVisitorCfgMap(
214
213
  },
215
214
  };
216
215
  const walkResults = walkWithVisitors(tree?.rootNode, [visitor], langId, walkerOpts);
217
- // biome-ignore lint/complexity/useLiteralKeys: noPropertyAccessFromIndexSignature requires bracket notation
218
- const cfgResults = (walkResults['cfg'] || []) as VisitorCfgResult[];
216
+ const cfgResults = (walkResults.cfg || []) as VisitorCfgResult[];
219
217
  const visitorCfgByLine = new Map<number, VisitorCfgResult[]>();
220
218
  for (const r of cfgResults) {
221
219
  if (r.funcNode) {
@@ -472,10 +472,10 @@ function getCoChangeMeta(db: BetterSqlite3Database): CoChangeMeta | null {
472
472
  meta[row.key] = row.value;
473
473
  }
474
474
  return {
475
- analyzedAt: meta['analyzed_at'] || null,
476
- since: meta['since'] || null,
477
- minSupport: meta['min_support'] ? parseInt(meta['min_support'], 10) : null,
478
- lastCommit: meta['last_analyzed_commit'] || null,
475
+ analyzedAt: meta.analyzed_at || null,
476
+ since: meta.since || null,
477
+ minSupport: meta.min_support ? parseInt(meta.min_support, 10) : null,
478
+ lastCommit: meta.last_analyzed_commit || null,
479
479
  };
480
480
  } catch {
481
481
  return null;
@@ -50,12 +50,12 @@ function buildCommunityObjects(
50
50
  const memberData: CommunityMember[] = [];
51
51
  for (const key of members) {
52
52
  const attrs = graph.getNodeAttrs(key)!;
53
- const dir = getDirectory(attrs['file'] as string);
53
+ const dir = getDirectory(attrs.file as string);
54
54
  dirCounts[dir] = (dirCounts[dir] || 0) + 1;
55
55
  memberData.push({
56
- name: attrs['label'] as string,
57
- file: attrs['file'] as string,
58
- ...(attrs['kind'] ? { kind: attrs['kind'] as string } : {}),
56
+ name: attrs.label as string,
57
+ file: attrs.file as string,
58
+ ...(attrs.kind ? { kind: attrs.kind as string } : {}),
59
59
  });
60
60
  }
61
61
 
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Complexity query functions — read-only DB queries for complexity metrics.
3
+ *
4
+ * Split from complexity.ts to separate query-time concerns (DB reads, filtering,
5
+ * pagination) from compute-time concerns (AST traversal, metric algorithms).
6
+ */
7
+
8
+ import { openReadonlyOrFail } from '../db/index.js';
9
+ import { buildFileConditionSQL } from '../db/query-builder.js';
10
+ import { loadConfig } from '../infrastructure/config.js';
11
+ import { debug } from '../infrastructure/logger.js';
12
+ import { isTestFile } from '../infrastructure/test-filter.js';
13
+ import { paginateResult } from '../shared/paginate.js';
14
+ import type { CodegraphConfig } from '../types.js';
15
+
16
+ // ─── Query-Time Functions ─────────────────────────────────────────────────
17
+
18
+ interface ComplexityRow {
19
+ name: string;
20
+ kind: string;
21
+ file: string;
22
+ line: number;
23
+ end_line: number | null;
24
+ cognitive: number;
25
+ cyclomatic: number;
26
+ max_nesting: number;
27
+ loc: number;
28
+ sloc: number;
29
+ maintainability_index: number;
30
+ halstead_volume: number;
31
+ halstead_difficulty: number;
32
+ halstead_effort: number;
33
+ halstead_bugs: number;
34
+ }
35
+
36
+ export function complexityData(
37
+ customDbPath?: string,
38
+ opts: {
39
+ target?: string;
40
+ limit?: number;
41
+ sort?: string;
42
+ aboveThreshold?: boolean;
43
+ file?: string;
44
+ kind?: string;
45
+ noTests?: boolean;
46
+ config?: CodegraphConfig;
47
+ offset?: number;
48
+ } = {},
49
+ ): Record<string, unknown> {
50
+ const db = openReadonlyOrFail(customDbPath);
51
+ try {
52
+ const sort = opts.sort || 'cognitive';
53
+ const noTests = opts.noTests || false;
54
+ const aboveThreshold = opts.aboveThreshold || false;
55
+ const target = opts.target || null;
56
+ const fileFilter = opts.file || null;
57
+ const kindFilter = opts.kind || null;
58
+
59
+ // Load thresholds from config
60
+ const config = opts.config || loadConfig(process.cwd());
61
+ const thresholds: any = config.manifesto?.rules || {
62
+ cognitive: { warn: 15, fail: null },
63
+ cyclomatic: { warn: 10, fail: null },
64
+ maxNesting: { warn: 4, fail: null },
65
+ maintainabilityIndex: { warn: 20, fail: null },
66
+ };
67
+
68
+ // Build query
69
+ let where = "WHERE n.kind IN ('function','method')";
70
+ const params: unknown[] = [];
71
+
72
+ if (noTests) {
73
+ where += ` AND n.file NOT LIKE '%.test.%'
74
+ AND n.file NOT LIKE '%.spec.%'
75
+ AND n.file NOT LIKE '%__test__%'
76
+ AND n.file NOT LIKE '%__tests__%'
77
+ AND n.file NOT LIKE '%.stories.%'`;
78
+ }
79
+ if (target) {
80
+ where += ' AND n.name LIKE ?';
81
+ params.push(`%${target}%`);
82
+ }
83
+ {
84
+ const fc = buildFileConditionSQL(fileFilter as string, 'n.file');
85
+ where += fc.sql;
86
+ params.push(...fc.params);
87
+ }
88
+ if (kindFilter) {
89
+ where += ' AND n.kind = ?';
90
+ params.push(kindFilter);
91
+ }
92
+
93
+ const isValidThreshold = (v: unknown): v is number =>
94
+ typeof v === 'number' && Number.isFinite(v);
95
+
96
+ let having = '';
97
+ if (aboveThreshold) {
98
+ const conditions: string[] = [];
99
+ if (isValidThreshold(thresholds.cognitive?.warn)) {
100
+ conditions.push(`fc.cognitive >= ${thresholds.cognitive.warn}`);
101
+ }
102
+ if (isValidThreshold(thresholds.cyclomatic?.warn)) {
103
+ conditions.push(`fc.cyclomatic >= ${thresholds.cyclomatic.warn}`);
104
+ }
105
+ if (isValidThreshold(thresholds.maxNesting?.warn)) {
106
+ conditions.push(`fc.max_nesting >= ${thresholds.maxNesting.warn}`);
107
+ }
108
+ if (isValidThreshold(thresholds.maintainabilityIndex?.warn)) {
109
+ conditions.push(
110
+ `fc.maintainability_index > 0 AND fc.maintainability_index <= ${thresholds.maintainabilityIndex.warn}`,
111
+ );
112
+ }
113
+ if (conditions.length > 0) {
114
+ having = `AND (${conditions.join(' OR ')})`;
115
+ }
116
+ }
117
+
118
+ const orderMap: Record<string, string> = {
119
+ cognitive: 'fc.cognitive DESC',
120
+ cyclomatic: 'fc.cyclomatic DESC',
121
+ nesting: 'fc.max_nesting DESC',
122
+ mi: 'fc.maintainability_index ASC',
123
+ volume: 'fc.halstead_volume DESC',
124
+ effort: 'fc.halstead_effort DESC',
125
+ bugs: 'fc.halstead_bugs DESC',
126
+ loc: 'fc.loc DESC',
127
+ };
128
+ const orderBy = orderMap[sort] || 'fc.cognitive DESC';
129
+
130
+ let rows: ComplexityRow[];
131
+ try {
132
+ rows = db
133
+ .prepare<ComplexityRow>(
134
+ `SELECT n.name, n.kind, n.file, n.line, n.end_line,
135
+ fc.cognitive, fc.cyclomatic, fc.max_nesting,
136
+ fc.loc, fc.sloc, fc.maintainability_index,
137
+ fc.halstead_volume, fc.halstead_difficulty, fc.halstead_effort, fc.halstead_bugs
138
+ FROM function_complexity fc
139
+ JOIN nodes n ON fc.node_id = n.id
140
+ ${where} ${having}
141
+ ORDER BY ${orderBy}`,
142
+ )
143
+ .all(...params);
144
+ } catch (e: unknown) {
145
+ debug(`complexity query failed (table may not exist): ${(e as Error).message}`);
146
+ // Check if graph has nodes even though complexity table is missing/empty
147
+ let hasGraph = false;
148
+ try {
149
+ hasGraph = (db.prepare<{ c: number }>('SELECT COUNT(*) as c FROM nodes').get()?.c ?? 0) > 0;
150
+ } catch (e2: unknown) {
151
+ debug(`nodes table check failed: ${(e2 as Error).message}`);
152
+ }
153
+ return { functions: [], summary: null, thresholds, hasGraph };
154
+ }
155
+
156
+ // Post-filter test files if needed (belt-and-suspenders for isTestFile)
157
+ const filtered = noTests ? rows.filter((r) => !isTestFile(r.file)) : rows;
158
+
159
+ const functions = filtered.map((r) => {
160
+ const exceeds: string[] = [];
161
+ if (
162
+ isValidThreshold(thresholds.cognitive?.warn) &&
163
+ r.cognitive >= (thresholds.cognitive?.warn ?? 0)
164
+ )
165
+ exceeds.push('cognitive');
166
+ if (
167
+ isValidThreshold(thresholds.cyclomatic?.warn) &&
168
+ r.cyclomatic >= (thresholds.cyclomatic?.warn ?? 0)
169
+ )
170
+ exceeds.push('cyclomatic');
171
+ if (
172
+ isValidThreshold(thresholds.maxNesting?.warn) &&
173
+ r.max_nesting >= (thresholds.maxNesting?.warn ?? 0)
174
+ )
175
+ exceeds.push('maxNesting');
176
+ if (
177
+ isValidThreshold(thresholds.maintainabilityIndex?.warn) &&
178
+ r.maintainability_index > 0 &&
179
+ r.maintainability_index <= (thresholds.maintainabilityIndex?.warn ?? 0)
180
+ )
181
+ exceeds.push('maintainabilityIndex');
182
+
183
+ return {
184
+ name: r.name,
185
+ kind: r.kind,
186
+ file: r.file,
187
+ line: r.line,
188
+ endLine: r.end_line || null,
189
+ cognitive: r.cognitive,
190
+ cyclomatic: r.cyclomatic,
191
+ maxNesting: r.max_nesting,
192
+ loc: r.loc || 0,
193
+ sloc: r.sloc || 0,
194
+ maintainabilityIndex: r.maintainability_index || 0,
195
+ halstead: {
196
+ volume: r.halstead_volume || 0,
197
+ difficulty: r.halstead_difficulty || 0,
198
+ effort: r.halstead_effort || 0,
199
+ bugs: r.halstead_bugs || 0,
200
+ },
201
+ exceeds: exceeds.length > 0 ? exceeds : undefined,
202
+ };
203
+ });
204
+
205
+ // Summary stats
206
+ let summary: Record<string, unknown> | null = null;
207
+ try {
208
+ const allRows = db
209
+ .prepare<{
210
+ cognitive: number;
211
+ cyclomatic: number;
212
+ max_nesting: number;
213
+ maintainability_index: number;
214
+ }>(
215
+ `SELECT fc.cognitive, fc.cyclomatic, fc.max_nesting, fc.maintainability_index
216
+ FROM function_complexity fc JOIN nodes n ON fc.node_id = n.id
217
+ WHERE n.kind IN ('function','method')
218
+ ${noTests ? `AND n.file NOT LIKE '%.test.%' AND n.file NOT LIKE '%.spec.%' AND n.file NOT LIKE '%__test__%' AND n.file NOT LIKE '%__tests__%' AND n.file NOT LIKE '%.stories.%'` : ''}`,
219
+ )
220
+ .all();
221
+
222
+ if (allRows.length > 0) {
223
+ const miValues = allRows.map((r) => r.maintainability_index || 0);
224
+ summary = {
225
+ analyzed: allRows.length,
226
+ avgCognitive: +(allRows.reduce((s, r) => s + r.cognitive, 0) / allRows.length).toFixed(1),
227
+ avgCyclomatic: +(allRows.reduce((s, r) => s + r.cyclomatic, 0) / allRows.length).toFixed(
228
+ 1,
229
+ ),
230
+ maxCognitive: Math.max(...allRows.map((r) => r.cognitive)),
231
+ maxCyclomatic: Math.max(...allRows.map((r) => r.cyclomatic)),
232
+ avgMI: +(miValues.reduce((s, v) => s + v, 0) / miValues.length).toFixed(1),
233
+ minMI: +Math.min(...miValues).toFixed(1),
234
+ aboveWarn: allRows.filter(
235
+ (r) =>
236
+ (isValidThreshold(thresholds.cognitive?.warn) &&
237
+ r.cognitive >= (thresholds.cognitive?.warn ?? 0)) ||
238
+ (isValidThreshold(thresholds.cyclomatic?.warn) &&
239
+ r.cyclomatic >= (thresholds.cyclomatic?.warn ?? 0)) ||
240
+ (isValidThreshold(thresholds.maxNesting?.warn) &&
241
+ r.max_nesting >= (thresholds.maxNesting?.warn ?? 0)) ||
242
+ (isValidThreshold(thresholds.maintainabilityIndex?.warn) &&
243
+ r.maintainability_index > 0 &&
244
+ r.maintainability_index <= (thresholds.maintainabilityIndex?.warn ?? 0)),
245
+ ).length,
246
+ };
247
+ }
248
+ } catch (e: unknown) {
249
+ debug(`complexity summary query failed: ${(e as Error).message}`);
250
+ }
251
+
252
+ // When summary is null (no complexity rows), check if graph has nodes
253
+ let hasGraph = false;
254
+ if (summary === null) {
255
+ try {
256
+ hasGraph = (db.prepare<{ c: number }>('SELECT COUNT(*) as c FROM nodes').get()?.c ?? 0) > 0;
257
+ } catch (e: unknown) {
258
+ debug(`nodes table check failed: ${(e as Error).message}`);
259
+ }
260
+ }
261
+
262
+ const base = { functions, summary, thresholds, hasGraph };
263
+ return paginateResult(base, 'functions', { limit: opts.limit, offset: opts.offset });
264
+ } finally {
265
+ db.close();
266
+ }
267
+ }
268
+
269
+ interface IterComplexityRow {
270
+ name: string;
271
+ kind: string;
272
+ file: string;
273
+ line: number;
274
+ end_line: number | null;
275
+ cognitive: number;
276
+ cyclomatic: number;
277
+ max_nesting: number;
278
+ loc: number;
279
+ sloc: number;
280
+ }
281
+
282
+ export function* iterComplexity(
283
+ customDbPath?: string,
284
+ opts: {
285
+ noTests?: boolean;
286
+ file?: string;
287
+ target?: string;
288
+ kind?: string;
289
+ sort?: string;
290
+ } = {},
291
+ ): Generator<{
292
+ name: string;
293
+ kind: string;
294
+ file: string;
295
+ line: number;
296
+ endLine: number | null;
297
+ cognitive: number;
298
+ cyclomatic: number;
299
+ maxNesting: number;
300
+ loc: number;
301
+ sloc: number;
302
+ }> {
303
+ const db = openReadonlyOrFail(customDbPath);
304
+ try {
305
+ const noTests = opts.noTests || false;
306
+ const sort = opts.sort || 'cognitive';
307
+
308
+ let where = "WHERE n.kind IN ('function','method')";
309
+ const params: unknown[] = [];
310
+
311
+ if (noTests) {
312
+ where += ` AND n.file NOT LIKE '%.test.%'
313
+ AND n.file NOT LIKE '%.spec.%'
314
+ AND n.file NOT LIKE '%__test__%'
315
+ AND n.file NOT LIKE '%__tests__%'
316
+ AND n.file NOT LIKE '%.stories.%'`;
317
+ }
318
+ if (opts.target) {
319
+ where += ' AND n.name LIKE ?';
320
+ params.push(`%${opts.target}%`);
321
+ }
322
+ {
323
+ const fc = buildFileConditionSQL(opts.file as string, 'n.file');
324
+ where += fc.sql;
325
+ params.push(...fc.params);
326
+ }
327
+ if (opts.kind) {
328
+ where += ' AND n.kind = ?';
329
+ params.push(opts.kind);
330
+ }
331
+
332
+ const orderMap: Record<string, string> = {
333
+ cognitive: 'fc.cognitive DESC',
334
+ cyclomatic: 'fc.cyclomatic DESC',
335
+ nesting: 'fc.max_nesting DESC',
336
+ mi: 'fc.maintainability_index ASC',
337
+ volume: 'fc.halstead_volume DESC',
338
+ effort: 'fc.halstead_effort DESC',
339
+ bugs: 'fc.halstead_bugs DESC',
340
+ loc: 'fc.loc DESC',
341
+ };
342
+ const orderBy = orderMap[sort] || 'fc.cognitive DESC';
343
+
344
+ const stmt = db.prepare<IterComplexityRow>(
345
+ `SELECT n.name, n.kind, n.file, n.line, n.end_line,
346
+ fc.cognitive, fc.cyclomatic, fc.max_nesting, fc.loc, fc.sloc
347
+ FROM function_complexity fc
348
+ JOIN nodes n ON fc.node_id = n.id
349
+ ${where}
350
+ ORDER BY ${orderBy}`,
351
+ );
352
+ for (const r of stmt.iterate(...params)) {
353
+ if (noTests && isTestFile(r.file)) continue;
354
+ yield {
355
+ name: r.name,
356
+ kind: r.kind,
357
+ file: r.file,
358
+ line: r.line,
359
+ endLine: r.end_line || null,
360
+ cognitive: r.cognitive,
361
+ cyclomatic: r.cyclomatic,
362
+ maxNesting: r.max_nesting,
363
+ loc: r.loc || 0,
364
+ sloc: r.sloc || 0,
365
+ };
366
+ }
367
+ } finally {
368
+ db.close();
369
+ }
370
+ }