octocode-cli 1.2.8 → 1.2.9

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 (282) hide show
  1. package/README.md +42 -35
  2. package/out/octocode-cli.js +36 -11767
  3. package/package.json +36 -36
  4. package/skills/README.md +42 -114
  5. package/skills/{octocode-code-engineer → octocode-engineer}/.claude/settings.local.json +2 -1
  6. package/skills/octocode-engineer/README.md +99 -0
  7. package/skills/octocode-engineer/SKILL.md +499 -0
  8. package/skills/octocode-engineer/build.mjs +29 -0
  9. package/skills/{octocode-code-engineer → octocode-engineer}/eslint.config.mjs +3 -13
  10. package/skills/{octocode-code-engineer → octocode-engineer}/package.json +28 -27
  11. package/skills/octocode-engineer/references/ast-reference.md +166 -0
  12. package/skills/{octocode-code-engineer → octocode-engineer}/references/cli-reference.md +80 -6
  13. package/skills/octocode-engineer/references/externals.md +86 -0
  14. package/skills/{octocode-code-engineer → octocode-engineer}/references/output-files.md +46 -6
  15. package/skills/octocode-engineer/references/quality-indicators.md +202 -0
  16. package/skills/octocode-engineer/references/tool-workflows.md +298 -0
  17. package/skills/octocode-engineer/references/validation-playbooks.md +99 -0
  18. package/skills/octocode-engineer/scripts/ast/search.js +45 -0
  19. package/skills/octocode-engineer/scripts/ast/tree-search.js +27 -0
  20. package/skills/octocode-engineer/scripts/index.js +173 -0
  21. package/skills/octocode-engineer/scripts/run.js +179 -0
  22. package/skills/octocode-engineer/src/analysis/dependencies.ts +378 -0
  23. package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/discovery.test.ts +57 -0
  24. package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/discovery.ts +43 -0
  25. package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/search.test.ts +113 -0
  26. package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/search.ts +64 -1
  27. package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-sitter.test.ts +118 -2
  28. package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-sitter.ts +65 -3
  29. package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/ts-analyzer.test.ts +281 -1
  30. package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/ts-analyzer.ts +173 -3
  31. package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/security.test.ts +73 -0
  32. package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/security.ts +62 -4
  33. package/skills/octocode-engineer/src/detector-gating.test.ts +59 -0
  34. package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/code-quality.ts +342 -0
  35. package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/index.ts +8 -0
  36. package/skills/{octocode-code-engineer → octocode-engineer}/src/index.test.ts +565 -11
  37. package/skills/octocode-engineer/src/index.ts +468 -0
  38. package/skills/octocode-engineer/src/pipeline/affected.test.ts +147 -0
  39. package/skills/octocode-engineer/src/pipeline/affected.ts +68 -0
  40. package/skills/octocode-engineer/src/pipeline/baseline.test.ts +276 -0
  41. package/skills/octocode-engineer/src/pipeline/baseline.ts +76 -0
  42. package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cli.test.ts +300 -53
  43. package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cli.ts +180 -36
  44. package/skills/octocode-engineer/src/pipeline/config-loader.test.ts +264 -0
  45. package/skills/octocode-engineer/src/pipeline/config-loader.ts +109 -0
  46. package/skills/octocode-engineer/src/pipeline/create-options.ts +55 -0
  47. package/skills/octocode-engineer/src/pipeline/health-score.test.ts +65 -0
  48. package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/main.ts +130 -17
  49. package/skills/octocode-engineer/src/pipeline/progress.ts +51 -0
  50. package/skills/octocode-engineer/src/pipeline/reporters.test.ts +155 -0
  51. package/skills/octocode-engineer/src/pipeline/reporters.ts +64 -0
  52. package/skills/octocode-engineer/src/reporting/graph-features.test.ts +279 -0
  53. package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/output-contract.test.ts +6 -0
  54. package/skills/octocode-engineer/src/reporting/summary-md.test.ts +1066 -0
  55. package/skills/octocode-engineer/src/reporting/summary-md.ts +1604 -0
  56. package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/writer.ts +136 -13
  57. package/skills/octocode-engineer/src/run.ts +78 -0
  58. package/skills/{octocode-code-engineer → octocode-engineer}/src/sanity.test.ts +1 -1
  59. package/skills/octocode-engineer/src/types/analysis.ts +25 -0
  60. package/skills/octocode-engineer/src/types/collectors.ts +134 -0
  61. package/skills/{octocode-code-engineer → octocode-engineer}/src/types/constants.ts +75 -41
  62. package/skills/octocode-engineer/src/types/core.ts +203 -0
  63. package/skills/octocode-engineer/src/types/dependency.ts +215 -0
  64. package/skills/octocode-engineer/src/types/file-entry.ts +108 -0
  65. package/skills/octocode-engineer/src/types/findings.ts +105 -0
  66. package/skills/{octocode-code-engineer → octocode-engineer}/src/types/index.ts +60 -30
  67. package/skills/octocode-engineer/src/types/tree-sitter.ts +38 -0
  68. package/skills/{octocode-code-engineer → octocode-engineer}/tsconfig.json +1 -0
  69. package/skills/octocode-research/.octocode/scan/.cache/analysis-cache.json +1 -0
  70. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/architecture.json +1 -0
  71. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/ast-trees.txt +5566 -0
  72. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/code-quality.json +1 -0
  73. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/dead-code.json +1 -0
  74. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/file-inventory.json +1 -0
  75. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/findings.json +1 -0
  76. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/graph.md +189 -0
  77. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/security.json +1 -0
  78. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/summary.json +1 -0
  79. package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/summary.md +265 -0
  80. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/architecture.json +1 -0
  81. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/ast-trees.txt +5555 -0
  82. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/code-quality.json +1 -0
  83. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/dead-code.json +1 -0
  84. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/file-inventory.json +1 -0
  85. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/findings.json +1 -0
  86. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/graph.md +190 -0
  87. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/security.json +1 -0
  88. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/summary.json +1 -0
  89. package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/summary.md +265 -0
  90. package/skills/octocode-research/CHANGELOG.md +60 -0
  91. package/skills/octocode-research/README.md +102 -388
  92. package/skills/octocode-research/SKILL.md +169 -498
  93. package/skills/octocode-research/package.json +19 -31
  94. package/skills/octocode-research/references/PARALLEL_AGENT_PROTOCOL.md +19 -0
  95. package/skills/octocode-research/references/SESSION_MANAGEMENT.md +38 -0
  96. package/skills/octocode-research/scripts/server-init.js +1 -1
  97. package/skills/octocode-research/scripts/server.d.ts +2 -1
  98. package/skills/octocode-research/scripts/server.js +329 -233
  99. package/skills/octocode-research/src/__tests__/integration/promptsRoutes.test.ts +180 -0
  100. package/skills/octocode-research/src/__tests__/integration/serverHttp.test.ts +221 -0
  101. package/skills/octocode-research/src/__tests__/integration/serverLifecycle.test.ts +194 -0
  102. package/skills/octocode-research/src/__tests__/integration/toolsRoutes.test.ts +501 -0
  103. package/skills/octocode-research/src/__tests__/unit/readiness.test.ts +61 -0
  104. package/skills/octocode-research/src/__tests__/unit/resilience.test.ts +192 -0
  105. package/skills/octocode-research/src/__tests__/unit/responseFactory.test.ts +172 -0
  106. package/skills/octocode-research/src/__tests__/unit/responseParser.test.ts +288 -0
  107. package/skills/octocode-research/src/__tests__/unit/schemas.test.ts +509 -0
  108. package/skills/octocode-research/src/index.ts +4 -124
  109. package/skills/octocode-research/src/middleware/queryParser.ts +0 -26
  110. package/skills/octocode-research/src/routes/lsp.ts +58 -59
  111. package/skills/octocode-research/src/routes/package.ts +35 -65
  112. package/skills/octocode-research/src/routes/prompts.ts +3 -3
  113. package/skills/octocode-research/src/routes/tools.ts +8 -20
  114. package/skills/octocode-research/src/server-init.ts +30 -237
  115. package/skills/octocode-research/src/server.ts +50 -23
  116. package/skills/octocode-research/src/types/errorGuards.ts +9 -80
  117. package/skills/octocode-research/src/types/guards.ts +0 -28
  118. package/skills/octocode-research/src/types/mcp.ts +11 -66
  119. package/skills/octocode-research/src/types/responses.ts +11 -129
  120. package/skills/octocode-research/src/utils/circuitBreaker.ts +0 -21
  121. package/skills/octocode-research/src/utils/logger.ts +1 -97
  122. package/skills/octocode-research/src/utils/resilience.ts +2 -12
  123. package/skills/octocode-research/src/utils/responseFactory.ts +0 -42
  124. package/skills/octocode-research/src/utils/responseParser.ts +3 -25
  125. package/skills/octocode-research/src/utils/retry.ts +0 -63
  126. package/skills/octocode-research/src/utils/routeFactory.ts +1 -1
  127. package/skills/octocode-research/src/validation/httpPreprocess.ts +0 -3
  128. package/skills/octocode-research/src/validation/index.ts +0 -1
  129. package/skills/octocode-research/src/validation/schemas.ts +0 -63
  130. package/skills/octocode-research/src/validation/toolCallSchema.ts +3 -3
  131. package/skills/octocode-research/tsdown.config.ts +4 -0
  132. package/skills/octocode-research/vitest.config.ts +3 -0
  133. package/skills/octocode-code-engineer/.plan/VALIDATED_PLAN.md +0 -223
  134. package/skills/octocode-code-engineer/README.md +0 -178
  135. package/skills/octocode-code-engineer/SKILL.md +0 -418
  136. package/skills/octocode-code-engineer/minify-scripts.mjs +0 -32
  137. package/skills/octocode-code-engineer/references/agent-ast-reading-rfc.md +0 -95
  138. package/skills/octocode-code-engineer/references/architecture-techniques.md +0 -121
  139. package/skills/octocode-code-engineer/references/ast-search.md +0 -210
  140. package/skills/octocode-code-engineer/references/ast-tree-search.md +0 -151
  141. package/skills/octocode-code-engineer/references/concepts.md +0 -107
  142. package/skills/octocode-code-engineer/references/finding-categories.md +0 -128
  143. package/skills/octocode-code-engineer/references/improvement-roadmap.md +0 -304
  144. package/skills/octocode-code-engineer/references/playbooks.md +0 -204
  145. package/skills/octocode-code-engineer/references/present-results.md +0 -136
  146. package/skills/octocode-code-engineer/references/tool-workflows.md +0 -566
  147. package/skills/octocode-code-engineer/references/validate-investigate.md +0 -225
  148. package/skills/octocode-code-engineer/scripts/analysis/dependencies.js +0 -1
  149. package/skills/octocode-code-engineer/scripts/analysis/dependency-summary.js +0 -1
  150. package/skills/octocode-code-engineer/scripts/analysis/discovery.js +0 -1
  151. package/skills/octocode-code-engineer/scripts/analysis/graph-analytics.js +0 -1
  152. package/skills/octocode-code-engineer/scripts/analysis/semantic.js +0 -1
  153. package/skills/octocode-code-engineer/scripts/ast/helpers.js +0 -1
  154. package/skills/octocode-code-engineer/scripts/ast/metrics.js +0 -1
  155. package/skills/octocode-code-engineer/scripts/ast/search.js +0 -2
  156. package/skills/octocode-code-engineer/scripts/ast/tree-search.js +0 -2
  157. package/skills/octocode-code-engineer/scripts/ast/tree-sitter.js +0 -1
  158. package/skills/octocode-code-engineer/scripts/ast/ts-analyzer.js +0 -1
  159. package/skills/octocode-code-engineer/scripts/collectors/chains.js +0 -1
  160. package/skills/octocode-code-engineer/scripts/collectors/effects.js +0 -1
  161. package/skills/octocode-code-engineer/scripts/collectors/input-sources.js +0 -1
  162. package/skills/octocode-code-engineer/scripts/collectors/performance.js +0 -1
  163. package/skills/octocode-code-engineer/scripts/collectors/prototype-pollution.js +0 -1
  164. package/skills/octocode-code-engineer/scripts/collectors/security.js +0 -1
  165. package/skills/octocode-code-engineer/scripts/collectors/test-profile.js +0 -1
  166. package/skills/octocode-code-engineer/scripts/common/is-direct-run.js +0 -1
  167. package/skills/octocode-code-engineer/scripts/common/utils.js +0 -1
  168. package/skills/octocode-code-engineer/scripts/detectors/code-quality.js +0 -1
  169. package/skills/octocode-code-engineer/scripts/detectors/cohesion.js +0 -1
  170. package/skills/octocode-code-engineer/scripts/detectors/coupling.js +0 -1
  171. package/skills/octocode-code-engineer/scripts/detectors/cycle.js +0 -1
  172. package/skills/octocode-code-engineer/scripts/detectors/dead-code.js +0 -1
  173. package/skills/octocode-code-engineer/scripts/detectors/import-style.js +0 -1
  174. package/skills/octocode-code-engineer/scripts/detectors/index.js +0 -1
  175. package/skills/octocode-code-engineer/scripts/detectors/security.js +0 -1
  176. package/skills/octocode-code-engineer/scripts/detectors/semantic.js +0 -1
  177. package/skills/octocode-code-engineer/scripts/detectors/shared.js +0 -1
  178. package/skills/octocode-code-engineer/scripts/detectors/test-quality.js +0 -1
  179. package/skills/octocode-code-engineer/scripts/index.js +0 -1
  180. package/skills/octocode-code-engineer/scripts/pipeline/cache.js +0 -1
  181. package/skills/octocode-code-engineer/scripts/pipeline/cli.js +0 -1
  182. package/skills/octocode-code-engineer/scripts/pipeline/main.js +0 -2
  183. package/skills/octocode-code-engineer/scripts/reporting/analysis.js +0 -1
  184. package/skills/octocode-code-engineer/scripts/reporting/summary-md.js +0 -1
  185. package/skills/octocode-code-engineer/scripts/reporting/writer.js +0 -1
  186. package/skills/octocode-code-engineer/scripts/types/constants.js +0 -1
  187. package/skills/octocode-code-engineer/scripts/types/index.js +0 -1
  188. package/skills/octocode-code-engineer/scripts/types/interfaces.js +0 -1
  189. package/skills/octocode-code-engineer/src/analysis/dependencies.ts +0 -406
  190. package/skills/octocode-code-engineer/src/index.ts +0 -403
  191. package/skills/octocode-code-engineer/src/reporting/summary-md.test.ts +0 -421
  192. package/skills/octocode-code-engineer/src/reporting/summary-md.ts +0 -714
  193. package/skills/octocode-code-engineer/src/types/interfaces.ts +0 -682
  194. package/skills/octocode-research/src/types/toolTypes.ts +0 -33
  195. package/skills/octocode-research/src/utils/logEmoji.ts +0 -103
  196. /package/skills/{octocode-code-engineer → octocode-engineer}/.octocode/rfc/RFC-code-engineer-weakness-fixes.md +0 -0
  197. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/architecture.ts.html +0 -0
  198. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ast-helpers.ts.html +0 -0
  199. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ast-search.ts.html +0 -0
  200. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/base.css +0 -0
  201. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/block-navigation.js +0 -0
  202. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/cache.ts.html +0 -0
  203. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/cli.ts.html +0 -0
  204. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/clover.xml +0 -0
  205. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-effects.ts.html +0 -0
  206. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-input-sources.ts.html +0 -0
  207. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-performance.ts.html +0 -0
  208. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-prototype-pollution.ts.html +0 -0
  209. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-security.ts.html +0 -0
  210. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-test-profile.ts.html +0 -0
  211. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/coverage-final.json +0 -0
  212. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/dependencies.ts.html +0 -0
  213. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/dependency-summary.ts.html +0 -0
  214. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/discovery.ts.html +0 -0
  215. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/favicon.png +0 -0
  216. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/graph-analytics.ts.html +0 -0
  217. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/index.html +0 -0
  218. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/index.ts.html +0 -0
  219. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/metrics.ts.html +0 -0
  220. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/pipeline.ts.html +0 -0
  221. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/prettify.css +0 -0
  222. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/prettify.js +0 -0
  223. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/report-analysis.ts.html +0 -0
  224. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/report-writer.ts.html +0 -0
  225. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/security-detectors.ts.html +0 -0
  226. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/semantic-detectors.ts.html +0 -0
  227. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/semantic.ts.html +0 -0
  228. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/sort-arrow-sprite.png +0 -0
  229. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/sorter.js +0 -0
  230. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/summary-md.ts.html +0 -0
  231. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/test-quality-detectors.ts.html +0 -0
  232. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/tree-sitter-analyzer.ts.html +0 -0
  233. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ts-analyzer.ts.html +0 -0
  234. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/types.ts.html +0 -0
  235. /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/utils.ts.html +0 -0
  236. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependencies.test.ts +0 -0
  237. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependency-summary.test.ts +0 -0
  238. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependency-summary.ts +0 -0
  239. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/graph-analytics.test.ts +0 -0
  240. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/graph-analytics.ts +0 -0
  241. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/semantic.test.ts +0 -0
  242. /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/semantic.ts +0 -0
  243. /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/helpers.test.ts +0 -0
  244. /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/helpers.ts +0 -0
  245. /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/metrics.test.ts +0 -0
  246. /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/metrics.ts +0 -0
  247. /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-search.test.ts +0 -0
  248. /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-search.ts +0 -0
  249. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/chains.ts +0 -0
  250. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/effects.test.ts +0 -0
  251. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/effects.ts +0 -0
  252. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/input-sources.test.ts +0 -0
  253. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/input-sources.ts +0 -0
  254. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/performance.test.ts +0 -0
  255. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/performance.ts +0 -0
  256. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/prototype-pollution.test.ts +0 -0
  257. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/prototype-pollution.ts +0 -0
  258. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/test-profile.test.ts +0 -0
  259. /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/test-profile.ts +0 -0
  260. /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/is-direct-run.test.ts +0 -0
  261. /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/is-direct-run.ts +0 -0
  262. /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/utils.test.ts +0 -0
  263. /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/utils.ts +0 -0
  264. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/cohesion.ts +0 -0
  265. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/coupling.ts +0 -0
  266. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/cycle.ts +0 -0
  267. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/dead-code.ts +0 -0
  268. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/import-style.ts +0 -0
  269. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/index.test.ts +0 -0
  270. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/security.test.ts +0 -0
  271. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/security.ts +0 -0
  272. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/semantic.ts +0 -0
  273. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/shared.ts +0 -0
  274. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/test-quality.test.ts +0 -0
  275. /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/test-quality.ts +0 -0
  276. /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cache.test.ts +0 -0
  277. /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cache.ts +0 -0
  278. /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/main.test.ts +0 -0
  279. /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline.test.ts +0 -0
  280. /package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/analysis.test.ts +0 -0
  281. /package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/analysis.ts +0 -0
  282. /package/skills/{octocode-code-engineer → octocode-engineer}/vitest.config.ts +0 -0
@@ -1153,7 +1153,7 @@ describe('category group constants', () => {
1153
1153
  });
1154
1154
 
1155
1155
  it('code quality group has expected categories', () => {
1156
- expect(CODE_QUALITY_CATEGORIES.size).toBe(26);
1156
+ expect(CODE_QUALITY_CATEGORIES.size).toBe(34);
1157
1157
  });
1158
1158
 
1159
1159
  it('dead code group has 11 categories', () => {
@@ -2094,13 +2094,18 @@ describe('end-to-end output validation', () => {
2094
2094
  it('produces valid summary.md with all sections', async () => {
2095
2095
  const { execSync } = await import('node:child_process');
2096
2096
  const dir = '/tmp/cq-test-' + Date.now();
2097
- const scriptPath = path.join(process.cwd(), 'scripts', 'index.js');
2097
+ const scriptPath = path.join(process.cwd(), 'scripts', 'run.js');
2098
2098
  const monorepoRoot = path.join(process.cwd(), '..', '..');
2099
2099
  try {
2100
- execSync(
2101
- `node "${scriptPath}" --root "${monorepoRoot}" --out "${dir}" --no-tree`,
2102
- { cwd: process.cwd(), encoding: 'utf8', timeout: 30000 }
2103
- );
2100
+ try {
2101
+ execSync(
2102
+ `node "${scriptPath}" --root "${monorepoRoot}" --out "${dir}" --no-tree`,
2103
+ { cwd: process.cwd(), encoding: 'utf8', timeout: 30000 }
2104
+ );
2105
+ } catch (execErr: unknown) {
2106
+ const e = execErr as { status?: number };
2107
+ if (e.status !== 1) throw execErr;
2108
+ }
2104
2109
 
2105
2110
  expect(fs.existsSync(`${dir}/summary.md`)).toBe(true);
2106
2111
  expect(fs.existsSync(`${dir}/summary.json`)).toBe(true);
@@ -3336,9 +3341,7 @@ describe('buildIssueCatalog detector paths', () => {
3336
3341
  ...DEFAULT_OPTS,
3337
3342
  root: '/repo',
3338
3343
  findingsLimit: 500,
3339
- anyThreshold: 5,
3340
- halsteadEffortThreshold: 500_000,
3341
- maintainabilityIndexThreshold: 20,
3344
+ thresholds: { ...DEFAULT_OPTS.thresholds, anyThreshold: 5, halsteadEffortThreshold: 500_000, maintainabilityIndexThreshold: 20 },
3342
3345
  };
3343
3346
 
3344
3347
  it('detects dead exports via declaredExportsByFile without consumedFromModule', () => {
@@ -4394,7 +4397,7 @@ describe('buildIssueCatalog detector paths via buildIssueCatalog', () => {
4394
4397
  ],
4395
4398
  },
4396
4399
  ];
4397
- const optsWithFlow = { ...opts, flowDupThreshold: 3 };
4400
+ const optsWithFlow = { ...opts, thresholds: { ...opts.thresholds, flowDupThreshold: 3 } };
4398
4401
  const { findings } = buildIssueCatalog(
4399
4402
  [],
4400
4403
  controlDuplicates,
@@ -4411,7 +4414,7 @@ describe('buildIssueCatalog detector paths via buildIssueCatalog', () => {
4411
4414
  it('detectLayerViolations: layerOrder triggers when lower layer imports from upper', () => {
4412
4415
  const state = emptyState();
4413
4416
  addEdge(state, 'src/repository/db.ts', 'src/service/handler.ts');
4414
- const optsWithLayers = { ...opts, layerOrder: ['service', 'repository'] };
4417
+ const optsWithLayers = { ...opts, thresholds: { ...opts.thresholds, layerOrder: ['service', 'repository'] } };
4415
4418
  const { findings } = buildIssueCatalog(
4416
4419
  [],
4417
4420
  [],
@@ -4423,3 +4426,554 @@ describe('buildIssueCatalog detector paths via buildIssueCatalog', () => {
4423
4426
  expect(findings.some(f => f.category === 'layer-violation')).toBe(true);
4424
4427
  });
4425
4428
  });
4429
+
4430
+ describe('new v2 quality detectors via buildIssueCatalog', () => {
4431
+ const testOpts2 = { ...DEFAULT_OPTS, findingsLimit: 500, includeTests: false };
4432
+
4433
+ function makeEntry2(
4434
+ file: string,
4435
+ overrides: Partial<FileEntry> = {}
4436
+ ): FileEntry {
4437
+ return {
4438
+ package: 'test-pkg',
4439
+ file,
4440
+ parseEngine: 'typescript',
4441
+ nodeCount: 0,
4442
+ kindCounts: {},
4443
+ functions: [],
4444
+ flows: [],
4445
+ dependencyProfile: {
4446
+ internalDependencies: [],
4447
+ externalDependencies: [],
4448
+ unresolvedDependencies: [],
4449
+ declaredExports: [],
4450
+ importedSymbols: [],
4451
+ reExports: [],
4452
+ },
4453
+ ...overrides,
4454
+ };
4455
+ }
4456
+
4457
+ describe('detectDeepNesting', () => {
4458
+ it('triggers when function has branch depth >= threshold', () => {
4459
+ const entry = makeEntry2('src/deep.ts', {
4460
+ functions: [
4461
+ makeFn({
4462
+ name: 'deepFn',
4463
+ file: 'src/deep.ts',
4464
+ maxBranchDepth: 6,
4465
+ maxLoopDepth: 0,
4466
+ statementCount: 20,
4467
+ }),
4468
+ ],
4469
+ });
4470
+ const { findings } = buildIssueCatalog(
4471
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4472
+ );
4473
+ const deep = findings.filter(f => f.category === 'deep-nesting');
4474
+ expect(deep.length).toBe(1);
4475
+ expect(deep[0].title).toContain('6');
4476
+ expect(deep[0].title).toContain('deepFn');
4477
+ });
4478
+
4479
+ it('triggers when function has loop depth >= threshold', () => {
4480
+ const entry = makeEntry2('src/loops.ts', {
4481
+ functions: [
4482
+ makeFn({
4483
+ name: 'loopFn',
4484
+ file: 'src/loops.ts',
4485
+ maxBranchDepth: 1,
4486
+ maxLoopDepth: 7,
4487
+ statementCount: 15,
4488
+ }),
4489
+ ],
4490
+ });
4491
+ const { findings } = buildIssueCatalog(
4492
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4493
+ );
4494
+ expect(findings.some(f => f.category === 'deep-nesting')).toBe(true);
4495
+ });
4496
+
4497
+ it('does not trigger when depth is below threshold', () => {
4498
+ const entry = makeEntry2('src/shallow.ts', {
4499
+ functions: [
4500
+ makeFn({ maxBranchDepth: 2, maxLoopDepth: 1, statementCount: 10 }),
4501
+ ],
4502
+ });
4503
+ const { findings } = buildIssueCatalog(
4504
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4505
+ );
4506
+ expect(findings.some(f => f.category === 'deep-nesting')).toBe(false);
4507
+ });
4508
+
4509
+ it('skips test files', () => {
4510
+ const entry = makeEntry2('src/__tests__/deep.test.ts', {
4511
+ functions: [
4512
+ makeFn({ maxBranchDepth: 10, maxLoopDepth: 10, statementCount: 50 }),
4513
+ ],
4514
+ });
4515
+ const { findings } = buildIssueCatalog(
4516
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4517
+ );
4518
+ expect(findings.some(f => f.category === 'deep-nesting')).toBe(false);
4519
+ });
4520
+
4521
+ it('severity scales with nesting depth', () => {
4522
+ const low = makeEntry2('src/low.ts', {
4523
+ functions: [makeFn({ name: 'fn', file: 'src/low.ts', maxBranchDepth: 5, statementCount: 10 })],
4524
+ });
4525
+ const high = makeEntry2('src/high.ts', {
4526
+ functions: [makeFn({ name: 'fn', file: 'src/high.ts', maxBranchDepth: 9, statementCount: 10 })],
4527
+ });
4528
+ const { findings: lowF } = buildIssueCatalog(
4529
+ [], [], [low], minimalDepSummary(), emptyState(), testOpts2
4530
+ );
4531
+ const { findings: highF } = buildIssueCatalog(
4532
+ [], [], [high], minimalDepSummary(), emptyState(), testOpts2
4533
+ );
4534
+ const lowSev = lowF.find(f => f.category === 'deep-nesting')?.severity;
4535
+ const highSev = highF.find(f => f.category === 'deep-nesting')?.severity;
4536
+ expect(lowSev).toBe('low');
4537
+ expect(highSev).toBe('high');
4538
+ });
4539
+ });
4540
+
4541
+ describe('detectMultipleReturnPaths', () => {
4542
+ it('triggers when function has returns >= threshold', () => {
4543
+ const entry = makeEntry2('src/multi.ts', {
4544
+ functions: [
4545
+ makeFn({
4546
+ name: 'multiFn',
4547
+ file: 'src/multi.ts',
4548
+ returns: 7,
4549
+ statementCount: 20,
4550
+ }),
4551
+ ],
4552
+ });
4553
+ const { findings } = buildIssueCatalog(
4554
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4555
+ );
4556
+ const multi = findings.filter(f => f.category === 'multiple-return-paths');
4557
+ expect(multi.length).toBe(1);
4558
+ expect(multi[0].title).toContain('7');
4559
+ });
4560
+
4561
+ it('does not trigger below threshold', () => {
4562
+ const entry = makeEntry2('src/few.ts', {
4563
+ functions: [makeFn({ returns: 3, statementCount: 10 })],
4564
+ });
4565
+ const { findings } = buildIssueCatalog(
4566
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4567
+ );
4568
+ expect(findings.some(f => f.category === 'multiple-return-paths')).toBe(false);
4569
+ });
4570
+
4571
+ it('skips test files', () => {
4572
+ const entry = makeEntry2('src/x.test.ts', {
4573
+ functions: [makeFn({ returns: 20, statementCount: 50 })],
4574
+ });
4575
+ const { findings } = buildIssueCatalog(
4576
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4577
+ );
4578
+ expect(findings.some(f => f.category === 'multiple-return-paths')).toBe(false);
4579
+ });
4580
+ });
4581
+
4582
+ describe('detectCatchRethrow', () => {
4583
+ it('triggers from pre-collected catchRethrows data', () => {
4584
+ const entry = makeEntry2('src/rethrow.ts', {
4585
+ catchRethrows: [
4586
+ { file: 'src/rethrow.ts', lineStart: 5, lineEnd: 8 },
4587
+ ],
4588
+ });
4589
+ const { findings } = buildIssueCatalog(
4590
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4591
+ );
4592
+ const cr = findings.filter(f => f.category === 'catch-rethrow');
4593
+ expect(cr.length).toBe(1);
4594
+ expect(cr[0].severity).toBe('low');
4595
+ expect(cr[0].title).toContain('Catch-rethrow');
4596
+ });
4597
+
4598
+ it('does not trigger when no catchRethrows', () => {
4599
+ const entry = makeEntry2('src/clean.ts');
4600
+ const { findings } = buildIssueCatalog(
4601
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4602
+ );
4603
+ expect(findings.some(f => f.category === 'catch-rethrow')).toBe(false);
4604
+ });
4605
+
4606
+ it('skips test files', () => {
4607
+ const entry = makeEntry2('src/__tests__/rethrow.test.ts', {
4608
+ catchRethrows: [
4609
+ { file: 'src/__tests__/rethrow.test.ts', lineStart: 5, lineEnd: 8 },
4610
+ ],
4611
+ });
4612
+ const { findings } = buildIssueCatalog(
4613
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4614
+ );
4615
+ expect(findings.some(f => f.category === 'catch-rethrow')).toBe(false);
4616
+ });
4617
+ });
4618
+
4619
+ describe('detectMagicStrings', () => {
4620
+ it('triggers when string appears >= minOccurrences across files', () => {
4621
+ const e1 = makeEntry2('src/a.ts', {
4622
+ magicStrings: [
4623
+ { file: 'src/a.ts', lineStart: 1, lineEnd: 1, value: 'active' },
4624
+ { file: 'src/a.ts', lineStart: 5, lineEnd: 5, value: 'active' },
4625
+ ],
4626
+ });
4627
+ const e2 = makeEntry2('src/b.ts', {
4628
+ magicStrings: [
4629
+ { file: 'src/b.ts', lineStart: 3, lineEnd: 3, value: 'active' },
4630
+ ],
4631
+ });
4632
+ const { findings } = buildIssueCatalog(
4633
+ [], [], [e1, e2], minimalDepSummary(), emptyState(), testOpts2
4634
+ );
4635
+ const ms = findings.filter(f => f.category === 'magic-string');
4636
+ expect(ms.length).toBe(1);
4637
+ expect(ms[0].title).toContain('active');
4638
+ expect(ms[0].title).toContain('3');
4639
+ });
4640
+
4641
+ it('does not trigger below minOccurrences', () => {
4642
+ const entry = makeEntry2('src/single.ts', {
4643
+ magicStrings: [
4644
+ { file: 'src/single.ts', lineStart: 1, lineEnd: 1, value: 'rare' },
4645
+ { file: 'src/single.ts', lineStart: 3, lineEnd: 3, value: 'rare' },
4646
+ ],
4647
+ });
4648
+ const { findings } = buildIssueCatalog(
4649
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4650
+ );
4651
+ expect(findings.some(f => f.category === 'magic-string')).toBe(false);
4652
+ });
4653
+
4654
+ it('severity scales with occurrence count', () => {
4655
+ const entries = Array.from({ length: 1 }, () =>
4656
+ makeEntry2('src/many.ts', {
4657
+ magicStrings: Array.from({ length: 9 }, (_, i) => ({
4658
+ file: 'src/many.ts',
4659
+ lineStart: i + 1,
4660
+ lineEnd: i + 1,
4661
+ value: 'status',
4662
+ })),
4663
+ })
4664
+ );
4665
+ const { findings } = buildIssueCatalog(
4666
+ [], [], entries, minimalDepSummary(), emptyState(), testOpts2
4667
+ );
4668
+ const ms = findings.filter(f => f.category === 'magic-string');
4669
+ expect(ms.length).toBe(1);
4670
+ expect(ms[0].severity).toBe('high');
4671
+ });
4672
+ });
4673
+
4674
+ describe('detectBooleanParameterCluster', () => {
4675
+ it('triggers from pre-collected booleanParamClusters', () => {
4676
+ const entry = makeEntry2('src/flags.ts', {
4677
+ booleanParamClusters: [
4678
+ {
4679
+ name: 'configure',
4680
+ booleanCount: 3,
4681
+ totalParams: 4,
4682
+ lineStart: 1,
4683
+ lineEnd: 5,
4684
+ },
4685
+ ],
4686
+ });
4687
+ const { findings } = buildIssueCatalog(
4688
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4689
+ );
4690
+ const bp = findings.filter(f => f.category === 'boolean-parameter-cluster');
4691
+ expect(bp.length).toBe(1);
4692
+ expect(bp[0].severity).toBe('medium');
4693
+ expect(bp[0].title).toContain('3');
4694
+ expect(bp[0].title).toContain('configure');
4695
+ });
4696
+
4697
+ it('does not trigger when below threshold', () => {
4698
+ const entry = makeEntry2('src/few.ts', {
4699
+ booleanParamClusters: [
4700
+ { name: 'fn', booleanCount: 2, totalParams: 3, lineStart: 1, lineEnd: 3 },
4701
+ ],
4702
+ });
4703
+ const { findings } = buildIssueCatalog(
4704
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4705
+ );
4706
+ expect(findings.some(f => f.category === 'boolean-parameter-cluster')).toBe(false);
4707
+ });
4708
+
4709
+ it('skips test files', () => {
4710
+ const entry = makeEntry2('src/flags.test.ts', {
4711
+ booleanParamClusters: [
4712
+ { name: 'testFn', booleanCount: 4, totalParams: 4, lineStart: 1, lineEnd: 5 },
4713
+ ],
4714
+ });
4715
+ const { findings } = buildIssueCatalog(
4716
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4717
+ );
4718
+ expect(findings.some(f => f.category === 'boolean-parameter-cluster')).toBe(false);
4719
+ });
4720
+ });
4721
+
4722
+ describe('detectPromiseAllUnhandled', () => {
4723
+ it('triggers from pre-collected promiseAllUnhandled', () => {
4724
+ const entry = makeEntry2('src/fetch.ts', {
4725
+ promiseAllUnhandled: [
4726
+ { file: 'src/fetch.ts', lineStart: 10, lineEnd: 10, kind: 'Promise.all' },
4727
+ ],
4728
+ });
4729
+ const { findings } = buildIssueCatalog(
4730
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4731
+ );
4732
+ const pa = findings.filter(f => f.category === 'promise-all-unhandled');
4733
+ expect(pa.length).toBe(1);
4734
+ expect(pa[0].severity).toBe('medium');
4735
+ expect(pa[0].title).toContain('Promise.all');
4736
+ });
4737
+
4738
+ it('detects multiple kinds (race, any)', () => {
4739
+ const entry = makeEntry2('src/multi.ts', {
4740
+ promiseAllUnhandled: [
4741
+ { file: 'src/multi.ts', lineStart: 1, lineEnd: 1, kind: 'Promise.race' },
4742
+ { file: 'src/multi.ts', lineStart: 5, lineEnd: 5, kind: 'Promise.any' },
4743
+ ],
4744
+ });
4745
+ const { findings } = buildIssueCatalog(
4746
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4747
+ );
4748
+ const pa = findings.filter(f => f.category === 'promise-all-unhandled');
4749
+ expect(pa.length).toBe(2);
4750
+ expect(pa.some(f => f.title.includes('Promise.race'))).toBe(true);
4751
+ expect(pa.some(f => f.title.includes('Promise.any'))).toBe(true);
4752
+ });
4753
+
4754
+ it('skips test files', () => {
4755
+ const entry = makeEntry2('src/fetch.spec.ts', {
4756
+ promiseAllUnhandled: [
4757
+ { file: 'src/fetch.spec.ts', lineStart: 1, lineEnd: 1, kind: 'Promise.all' },
4758
+ ],
4759
+ });
4760
+ const { findings } = buildIssueCatalog(
4761
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4762
+ );
4763
+ expect(findings.some(f => f.category === 'promise-all-unhandled')).toBe(false);
4764
+ });
4765
+ });
4766
+
4767
+ describe('detectExportSurfaceDensity', () => {
4768
+ it('triggers when export ratio >= 50%', () => {
4769
+ const fns = Array.from({ length: 5 }, (_, i) =>
4770
+ makeFn({
4771
+ name: `fn${i}`,
4772
+ file: 'src/dense.ts',
4773
+ statementCount: 5,
4774
+ })
4775
+ );
4776
+ const entry = makeEntry2('src/dense.ts', {
4777
+ functions: fns,
4778
+ dependencyProfile: {
4779
+ internalDependencies: [],
4780
+ externalDependencies: [],
4781
+ unresolvedDependencies: [],
4782
+ declaredExports: Array.from({ length: 15 }, (_, i) => ({
4783
+ name: `export${i}`,
4784
+ kind: 'function' as const,
4785
+ isType: false,
4786
+ isDefault: false,
4787
+ })),
4788
+ importedSymbols: [],
4789
+ reExports: [],
4790
+ },
4791
+ });
4792
+ const { findings } = buildIssueCatalog(
4793
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4794
+ );
4795
+ const esd = findings.filter(f => f.category === 'export-surface-density');
4796
+ expect(esd.length).toBe(1);
4797
+ expect(esd[0].title).toContain('%');
4798
+ });
4799
+
4800
+ it('does not trigger when ratio < 50%', () => {
4801
+ const fns = Array.from({ length: 10 }, (_, i) =>
4802
+ makeFn({ name: `fn${i}`, file: 'src/normal.ts', statementCount: 10 })
4803
+ );
4804
+ const entry = makeEntry2('src/normal.ts', {
4805
+ functions: fns,
4806
+ dependencyProfile: {
4807
+ internalDependencies: [],
4808
+ externalDependencies: [],
4809
+ unresolvedDependencies: [],
4810
+ declaredExports: [
4811
+ { name: 'main', kind: 'function' as const, isType: false, isDefault: false },
4812
+ ],
4813
+ importedSymbols: [],
4814
+ reExports: [],
4815
+ },
4816
+ });
4817
+ const { findings } = buildIssueCatalog(
4818
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4819
+ );
4820
+ expect(findings.some(f => f.category === 'export-surface-density')).toBe(false);
4821
+ });
4822
+
4823
+ it('does not trigger for small files (< 20 statements)', () => {
4824
+ const entry = makeEntry2('src/small.ts', {
4825
+ functions: [makeFn({ statementCount: 5, file: 'src/small.ts' })],
4826
+ dependencyProfile: {
4827
+ internalDependencies: [],
4828
+ externalDependencies: [],
4829
+ unresolvedDependencies: [],
4830
+ declaredExports: Array.from({ length: 5 }, (_, i) => ({
4831
+ name: `e${i}`,
4832
+ kind: 'function' as const,
4833
+ isType: false,
4834
+ isDefault: false,
4835
+ })),
4836
+ importedSymbols: [],
4837
+ reExports: [],
4838
+ },
4839
+ });
4840
+ const { findings } = buildIssueCatalog(
4841
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4842
+ );
4843
+ expect(findings.some(f => f.category === 'export-surface-density')).toBe(false);
4844
+ });
4845
+ });
4846
+
4847
+ describe('detectChangeRisk', () => {
4848
+ it('triggers when multiple quality signals overlap', () => {
4849
+ const entry = makeEntry2('src/risky.ts', {
4850
+ functions: [
4851
+ makeFn({
4852
+ name: 'complexFn',
4853
+ file: 'src/risky.ts',
4854
+ complexity: 25,
4855
+ cognitiveComplexity: 30,
4856
+ maintainabilityIndex: 10,
4857
+ statementCount: 50,
4858
+ }),
4859
+ makeFn({
4860
+ name: 'anotherFn',
4861
+ file: 'src/risky.ts',
4862
+ complexity: 20,
4863
+ cognitiveComplexity: 25,
4864
+ maintainabilityIndex: 15,
4865
+ statementCount: 30,
4866
+ }),
4867
+ ],
4868
+ emptyCatches: [{ file: 'src/risky.ts', lineStart: 10, lineEnd: 12 }],
4869
+ promiseAllUnhandled: [
4870
+ { file: 'src/risky.ts', lineStart: 20, lineEnd: 20, kind: 'Promise.all' as const },
4871
+ ],
4872
+ });
4873
+ const { findings } = buildIssueCatalog(
4874
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4875
+ );
4876
+ const cr = findings.filter(f => f.category === 'change-risk');
4877
+ expect(cr.length).toBe(1);
4878
+ expect(['medium', 'high', 'critical']).toContain(cr[0].severity);
4879
+ expect(cr[0].title).toContain('Change-risk score');
4880
+ });
4881
+
4882
+ it('does not trigger when risk score < 4', () => {
4883
+ const entry = makeEntry2('src/clean.ts', {
4884
+ functions: [
4885
+ makeFn({ complexity: 3, cognitiveComplexity: 2, statementCount: 10 }),
4886
+ ],
4887
+ });
4888
+ const { findings } = buildIssueCatalog(
4889
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4890
+ );
4891
+ expect(findings.some(f => f.category === 'change-risk')).toBe(false);
4892
+ });
4893
+
4894
+ it('skips test files', () => {
4895
+ const entry = makeEntry2('src/__tests__/risky.test.ts', {
4896
+ functions: [
4897
+ makeFn({ complexity: 30, cognitiveComplexity: 30, maintainabilityIndex: 5, statementCount: 100 }),
4898
+ ],
4899
+ emptyCatches: [{ file: 'src/__tests__/risky.test.ts', lineStart: 1, lineEnd: 2 }],
4900
+ });
4901
+ const { findings } = buildIssueCatalog(
4902
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4903
+ );
4904
+ expect(findings.some(f => f.category === 'change-risk')).toBe(false);
4905
+ });
4906
+
4907
+ it('severity critical when score >= 8', () => {
4908
+ const entry = makeEntry2('src/terrible.ts', {
4909
+ functions: [
4910
+ makeFn({
4911
+ name: 'fn1',
4912
+ file: 'src/terrible.ts',
4913
+ complexity: 40,
4914
+ cognitiveComplexity: 50,
4915
+ maintainabilityIndex: 5,
4916
+ statementCount: 80,
4917
+ }),
4918
+ makeFn({
4919
+ name: 'fn2',
4920
+ file: 'src/terrible.ts',
4921
+ complexity: 35,
4922
+ cognitiveComplexity: 40,
4923
+ maintainabilityIndex: 8,
4924
+ statementCount: 60,
4925
+ }),
4926
+ makeFn({
4927
+ name: 'fn3',
4928
+ file: 'src/terrible.ts',
4929
+ complexity: 30,
4930
+ cognitiveComplexity: 25,
4931
+ maintainabilityIndex: 12,
4932
+ statementCount: 40,
4933
+ }),
4934
+ ],
4935
+ emptyCatches: [{ file: 'src/terrible.ts', lineStart: 1, lineEnd: 2 }],
4936
+ promiseAllUnhandled: [
4937
+ { file: 'src/terrible.ts', lineStart: 5, lineEnd: 5, kind: 'Promise.all' as const },
4938
+ ],
4939
+ dependencyProfile: {
4940
+ internalDependencies: [],
4941
+ externalDependencies: [],
4942
+ unresolvedDependencies: [],
4943
+ declaredExports: Array.from({ length: 20 }, (_, i) => ({
4944
+ name: `e${i}`,
4945
+ kind: 'function' as const,
4946
+ isType: false,
4947
+ isDefault: false,
4948
+ })),
4949
+ importedSymbols: [],
4950
+ reExports: [],
4951
+ },
4952
+ });
4953
+ const { findings } = buildIssueCatalog(
4954
+ [], [], [entry], minimalDepSummary(), emptyState(), testOpts2
4955
+ );
4956
+ const cr = findings.filter(f => f.category === 'change-risk');
4957
+ expect(cr.length).toBe(1);
4958
+ expect(cr[0].severity).toBe('critical');
4959
+ });
4960
+ });
4961
+
4962
+ describe('new categories registered in PILLAR_CATEGORIES', () => {
4963
+ it('all new categories exist in code-quality pillar', () => {
4964
+ const newCategories = [
4965
+ 'deep-nesting',
4966
+ 'multiple-return-paths',
4967
+ 'catch-rethrow',
4968
+ 'magic-string',
4969
+ 'boolean-parameter-cluster',
4970
+ 'promise-all-unhandled',
4971
+ 'export-surface-density',
4972
+ 'change-risk',
4973
+ ];
4974
+ for (const cat of newCategories) {
4975
+ expect(PILLAR_CATEGORIES['code-quality']).toContain(cat);
4976
+ }
4977
+ });
4978
+ });
4979
+ });