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
@@ -0,0 +1,378 @@
1
+ import path from 'node:path';
2
+
3
+ import * as ts from 'typescript';
4
+
5
+ import {
6
+ addToMapSet,
7
+ isRelativeImport,
8
+ isTestFile,
9
+ normalizeDependencyValue,
10
+ resolveImportTarget,
11
+ toRepoPath,
12
+ } from '../common/utils.js';
13
+
14
+ import type {
15
+ AnalysisOptions,
16
+ DependencyProfile,
17
+ DependencyRecord,
18
+ DependencyState,
19
+ ExportSymbol,
20
+ ImportedSymbolRef,
21
+ ReExportRef,
22
+ } from '../types/index.js';
23
+
24
+ function nodeLineRange(
25
+ sourceFile: ts.SourceFile,
26
+ node: ts.Node
27
+ ): { lineStart: number; lineEnd: number } {
28
+ return {
29
+ lineStart:
30
+ sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
31
+ .line + 1,
32
+ lineEnd:
33
+ sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line + 1,
34
+ };
35
+ }
36
+
37
+ function hasExportModifier(node: ts.Node): boolean {
38
+ if (!ts.canHaveModifiers(node)) return false;
39
+ return Boolean(
40
+ ts
41
+ .getModifiers(node)
42
+ ?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)
43
+ );
44
+ }
45
+
46
+ interface DependencyCollectorContext {
47
+ sourceFile: ts.SourceFile;
48
+ importedSymbols: ImportedSymbolRef[];
49
+ reExports: ReExportRef[];
50
+ pushDeclaredExport: (item: ExportSymbol) => void;
51
+ resolveSpecifier: (specifier: string | undefined) => string | null;
52
+ }
53
+
54
+ function collectImportSymbols(
55
+ node: ts.ImportDeclaration,
56
+ ctx: DependencyCollectorContext
57
+ ): void {
58
+ if (!node.moduleSpecifier || !ts.isStringLiteral(node.moduleSpecifier))
59
+ return;
60
+ const sourceModule = node.moduleSpecifier.text;
61
+ const resolvedModule = ctx.resolveSpecifier(sourceModule) ?? undefined;
62
+ const loc = nodeLineRange(ctx.sourceFile, node);
63
+ const clause = node.importClause;
64
+ if (!clause) return;
65
+ if (clause.name) {
66
+ ctx.importedSymbols.push({
67
+ sourceModule,
68
+ resolvedModule,
69
+ importedName: 'default',
70
+ localName: clause.name.text,
71
+ isTypeOnly: clause.isTypeOnly,
72
+ ...loc,
73
+ });
74
+ }
75
+ if (!clause.namedBindings) return;
76
+ if (ts.isNamespaceImport(clause.namedBindings)) {
77
+ ctx.importedSymbols.push({
78
+ sourceModule,
79
+ resolvedModule,
80
+ importedName: '*',
81
+ localName: clause.namedBindings.name.text,
82
+ isTypeOnly: clause.isTypeOnly,
83
+ ...loc,
84
+ });
85
+ } else {
86
+ for (const element of clause.namedBindings.elements) {
87
+ ctx.importedSymbols.push({
88
+ sourceModule,
89
+ resolvedModule,
90
+ importedName: element.propertyName?.text ?? element.name.text,
91
+ localName: element.name.text,
92
+ isTypeOnly: clause.isTypeOnly || element.isTypeOnly,
93
+ ...loc,
94
+ });
95
+ }
96
+ }
97
+ }
98
+
99
+ function collectReExportDeclaration(
100
+ node: ts.ExportDeclaration,
101
+ ctx: DependencyCollectorContext
102
+ ): void {
103
+ if (!node.moduleSpecifier || !ts.isStringLiteral(node.moduleSpecifier))
104
+ return;
105
+ const sourceModule = node.moduleSpecifier.text;
106
+ const resolvedModule = ctx.resolveSpecifier(sourceModule) ?? undefined;
107
+ if (node.exportClause && ts.isNamedExports(node.exportClause)) {
108
+ for (const element of node.exportClause.elements) {
109
+ ctx.reExports.push({
110
+ sourceModule,
111
+ resolvedModule,
112
+ exportedAs: element.name.text,
113
+ importedName: element.propertyName?.text ?? element.name.text,
114
+ isStar: false,
115
+ isTypeOnly: node.isTypeOnly || element.isTypeOnly,
116
+ ...nodeLineRange(ctx.sourceFile, element),
117
+ });
118
+ }
119
+ } else {
120
+ ctx.reExports.push({
121
+ sourceModule,
122
+ resolvedModule,
123
+ exportedAs: '*',
124
+ importedName: '*',
125
+ isStar: true,
126
+ isTypeOnly: node.isTypeOnly,
127
+ ...nodeLineRange(ctx.sourceFile, node),
128
+ });
129
+ }
130
+ }
131
+
132
+ function collectDeclaredExportFromNode(
133
+ node: ts.Node,
134
+ ctx: DependencyCollectorContext
135
+ ): void {
136
+ const loc = nodeLineRange(ctx.sourceFile, node);
137
+
138
+ if (ts.isExportAssignment(node)) {
139
+ ctx.pushDeclaredExport({ name: 'default', kind: 'value', isDefault: true, ...loc });
140
+ return;
141
+ }
142
+
143
+ if (
144
+ (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) &&
145
+ hasExportModifier(node)
146
+ ) {
147
+ ctx.pushDeclaredExport({
148
+ name: node.name?.text || 'default',
149
+ kind: 'value',
150
+ isDefault: !node.name,
151
+ ...loc,
152
+ });
153
+ return;
154
+ }
155
+
156
+ if (ts.isEnumDeclaration(node) && hasExportModifier(node)) {
157
+ ctx.pushDeclaredExport({ name: node.name.text, kind: 'value', ...loc });
158
+ return;
159
+ }
160
+
161
+ if (
162
+ (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) &&
163
+ hasExportModifier(node)
164
+ ) {
165
+ ctx.pushDeclaredExport({ name: node.name.text, kind: 'type', ...loc });
166
+ return;
167
+ }
168
+
169
+ if (ts.isVariableStatement(node) && hasExportModifier(node)) {
170
+ for (const decl of node.declarationList.declarations) {
171
+ if (ts.isIdentifier(decl.name)) {
172
+ ctx.pushDeclaredExport({
173
+ name: decl.name.text,
174
+ kind: 'value',
175
+ ...nodeLineRange(ctx.sourceFile, decl),
176
+ });
177
+ }
178
+ }
179
+ return;
180
+ }
181
+
182
+ if (
183
+ ts.isExportDeclaration(node) &&
184
+ !node.moduleSpecifier &&
185
+ node.exportClause &&
186
+ ts.isNamedExports(node.exportClause)
187
+ ) {
188
+ for (const element of node.exportClause.elements) {
189
+ ctx.pushDeclaredExport({
190
+ name: element.name.text,
191
+ kind: element.isTypeOnly ? 'type' : 'unknown',
192
+ ...nodeLineRange(ctx.sourceFile, element),
193
+ });
194
+ }
195
+ }
196
+ }
197
+
198
+ function collectRequireCall(
199
+ node: ts.CallExpression,
200
+ ctx: DependencyCollectorContext
201
+ ): void {
202
+ if (
203
+ !ts.isIdentifier(node.expression) ||
204
+ node.expression.text !== 'require' ||
205
+ node.arguments.length !== 1 ||
206
+ !ts.isStringLiteral(node.arguments[0])
207
+ )
208
+ return;
209
+ const sourceModule = node.arguments[0].text;
210
+ const resolvedModule = ctx.resolveSpecifier(sourceModule) ?? undefined;
211
+ ctx.importedSymbols.push({
212
+ sourceModule,
213
+ resolvedModule,
214
+ importedName: '*',
215
+ localName: 'require',
216
+ isTypeOnly: false,
217
+ ...nodeLineRange(ctx.sourceFile, node),
218
+ });
219
+ }
220
+
221
+ export function collectModuleDependencies(
222
+ sourceFile: ts.SourceFile,
223
+ filePath: string,
224
+ repoRoot: string
225
+ ): DependencyProfile {
226
+ const currentDirectory = path.dirname(filePath);
227
+ const internal = new Set<string>();
228
+ const external = new Set<string>();
229
+ const unresolved = new Set<string>();
230
+ const declaredExports: ExportSymbol[] = [];
231
+
232
+ const pushDeclaredExport = (item: ExportSymbol): void => {
233
+ if (
234
+ declaredExports.some(
235
+ entry => entry.name === item.name && entry.kind === item.kind
236
+ )
237
+ )
238
+ return;
239
+ declaredExports.push(item);
240
+ };
241
+
242
+ const resolveSpecifier = (specifier: string | undefined): string | null => {
243
+ if (!specifier || typeof specifier !== 'string') return null;
244
+ if (!isRelativeImport(specifier)) {
245
+ external.add(specifier);
246
+ return null;
247
+ }
248
+ const resolved = resolveImportTarget(currentDirectory, specifier);
249
+ if (!resolved) {
250
+ unresolved.add(specifier);
251
+ return null;
252
+ }
253
+ if (!resolved.startsWith(repoRoot)) {
254
+ external.add(specifier);
255
+ return null;
256
+ }
257
+ const relativeResolved = normalizeDependencyValue(
258
+ path.relative(repoRoot, resolved)
259
+ );
260
+ internal.add(relativeResolved);
261
+ return relativeResolved;
262
+ };
263
+
264
+ const ctx: DependencyCollectorContext = {
265
+ sourceFile,
266
+ importedSymbols: [],
267
+ reExports: [],
268
+ pushDeclaredExport,
269
+ resolveSpecifier,
270
+ };
271
+
272
+ const visit = (node: ts.Node): void => {
273
+ if (ts.isImportDeclaration(node)) collectImportSymbols(node, ctx);
274
+ if (ts.isExportDeclaration(node) && node.moduleSpecifier) collectReExportDeclaration(node, ctx);
275
+ collectDeclaredExportFromNode(node, ctx);
276
+ if (ts.isCallExpression(node)) collectRequireCall(node, ctx);
277
+ ts.forEachChild(node, visit);
278
+ };
279
+
280
+ visit(sourceFile);
281
+ return {
282
+ internalDependencies: [...internal].sort(),
283
+ externalDependencies: [...external].sort(),
284
+ unresolvedDependencies: [...unresolved].sort(),
285
+ declaredExports,
286
+ importedSymbols: ctx.importedSymbols,
287
+ reExports: ctx.reExports,
288
+ };
289
+ }
290
+
291
+ export function trackDependencyEdge(
292
+ dependencyState: DependencyState,
293
+ fromFile: string,
294
+ toFile: string,
295
+ importerIsTest: boolean
296
+ ): void {
297
+ addToMapSet(dependencyState.outgoing, fromFile, toFile);
298
+ addToMapSet(dependencyState.incoming, toFile, fromFile);
299
+ if (importerIsTest) {
300
+ addToMapSet(dependencyState.incomingFromTests, toFile, fromFile);
301
+ } else {
302
+ addToMapSet(dependencyState.incomingFromProduction, toFile, fromFile);
303
+ }
304
+ }
305
+
306
+ export function collectDependencyProfile(
307
+ sourceFile: ts.SourceFile,
308
+ filePath: string,
309
+ packageName: string,
310
+ options: AnalysisOptions,
311
+ dependencyState: DependencyState
312
+ ): DependencyProfile {
313
+ const fileRelative = toRepoPath(filePath, options.root);
314
+ dependencyState.files.add(fileRelative);
315
+
316
+ const deps = collectModuleDependencies(sourceFile, filePath, options.root);
317
+ const importerIsTest = isTestFile(filePath);
318
+
319
+ for (const internalDependency of deps.internalDependencies) {
320
+ const normalizedDep = normalizeDependencyValue(internalDependency);
321
+ trackDependencyEdge(
322
+ dependencyState,
323
+ fileRelative,
324
+ normalizedDep,
325
+ importerIsTest
326
+ );
327
+ }
328
+
329
+ if (deps.externalDependencies.length > 0) {
330
+ dependencyState.externalCounts.set(
331
+ fileRelative,
332
+ new Set(deps.externalDependencies)
333
+ );
334
+ }
335
+
336
+ if (deps.unresolvedDependencies.length > 0) {
337
+ dependencyState.unresolvedCounts.set(
338
+ fileRelative,
339
+ new Set(deps.unresolvedDependencies)
340
+ );
341
+ }
342
+
343
+ dependencyState.declaredExportsByFile.set(fileRelative, deps.declaredExports);
344
+ dependencyState.importedSymbolsByFile.set(fileRelative, deps.importedSymbols);
345
+ dependencyState.reExportsByFile.set(fileRelative, deps.reExports);
346
+
347
+ return {
348
+ ...deps,
349
+ package: packageName,
350
+ file: fileRelative,
351
+ };
352
+ }
353
+
354
+ export function dependencyProfileToRecord(
355
+ fileRelative: string,
356
+ dependencyState: DependencyState
357
+ ): DependencyRecord {
358
+ const outbound = dependencyState.outgoing.get(fileRelative) || new Set();
359
+ const inbound = dependencyState.incoming.get(fileRelative) || new Set();
360
+ const prodIn =
361
+ dependencyState.incomingFromProduction.get(fileRelative) || new Set();
362
+ const testIn =
363
+ dependencyState.incomingFromTests.get(fileRelative) || new Set();
364
+ const external =
365
+ dependencyState.externalCounts.get(fileRelative) || new Set();
366
+ const unresolvedSet =
367
+ dependencyState.unresolvedCounts.get(fileRelative) || new Set();
368
+
369
+ return {
370
+ file: fileRelative,
371
+ outboundCount: outbound.size,
372
+ inboundCount: inbound.size,
373
+ inboundFromProduction: prodIn.size,
374
+ inboundFromTests: testIn.size,
375
+ externalDependencyCount: external.size,
376
+ unresolvedDependencyCount: unresolvedSet.size,
377
+ };
378
+ }
@@ -190,6 +190,63 @@ describe('discovery', () => {
190
190
  [...basenames].sort((a, b) => a.localeCompare(b))
191
191
  );
192
192
  });
193
+
194
+ it('skips minified js artifacts', () => {
195
+ fs.writeFileSync(path.join(tmpDir, 'app.js'), '', 'utf8');
196
+ fs.writeFileSync(path.join(tmpDir, 'app.min.js'), '', 'utf8');
197
+
198
+ const opts: AnalysisOptions = { ...DEFAULT_OPTS, root: tmpDir };
199
+ const files = collectFiles(tmpDir, opts);
200
+
201
+ expect(files.some(f => f.endsWith('app.js'))).toBe(true);
202
+ expect(files.some(f => f.endsWith('app.min.js'))).toBe(false);
203
+ });
204
+
205
+ it('skips scripts files when mirrored src file exists', () => {
206
+ fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
207
+ fs.mkdirSync(path.join(tmpDir, 'scripts'), { recursive: true });
208
+ fs.writeFileSync(path.join(tmpDir, 'src', 'index.ts'), '', 'utf8');
209
+ fs.writeFileSync(path.join(tmpDir, 'scripts', 'index.js'), '', 'utf8');
210
+
211
+ const opts: AnalysisOptions = { ...DEFAULT_OPTS, root: tmpDir };
212
+ const files = collectFiles(tmpDir, opts);
213
+
214
+ expect(files.some(f => f.endsWith(path.join('src', 'index.ts')))).toBe(
215
+ true
216
+ );
217
+ expect(
218
+ files.some(f => f.endsWith(path.join('scripts', 'index.js')))
219
+ ).toBe(false);
220
+ });
221
+
222
+ it('keeps scripts files when no src mirror exists', () => {
223
+ fs.mkdirSync(path.join(tmpDir, 'scripts'), { recursive: true });
224
+ fs.writeFileSync(path.join(tmpDir, 'scripts', 'bootstrap.js'), '', 'utf8');
225
+
226
+ const opts: AnalysisOptions = { ...DEFAULT_OPTS, root: tmpDir };
227
+ const files = collectFiles(tmpDir, opts);
228
+
229
+ expect(
230
+ files.some(f => f.endsWith(path.join('scripts', 'bootstrap.js')))
231
+ ).toBe(true);
232
+ });
233
+
234
+ it('keeps scoped scripts file even when mirrored src file exists', () => {
235
+ fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
236
+ fs.mkdirSync(path.join(tmpDir, 'scripts'), { recursive: true });
237
+ const scriptPath = path.join(tmpDir, 'scripts', 'index.js');
238
+ fs.writeFileSync(path.join(tmpDir, 'src', 'index.ts'), '', 'utf8');
239
+ fs.writeFileSync(scriptPath, '', 'utf8');
240
+
241
+ const opts: AnalysisOptions = {
242
+ ...DEFAULT_OPTS,
243
+ root: tmpDir,
244
+ scope: [scriptPath],
245
+ };
246
+ const files = collectFiles(tmpDir, opts);
247
+
248
+ expect(files.some(f => f === scriptPath)).toBe(true);
249
+ });
193
250
  });
194
251
 
195
252
  describe('safeRead', () => {
@@ -6,6 +6,42 @@ import { ALLOWED_EXTS } from '../types/index.js';
6
6
 
7
7
  import type { AnalysisOptions, FileEntry, PackageInfo } from '../types/index.js';
8
8
 
9
+ const SOURCE_MIRROR_EXTS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
10
+
11
+ function isExplicitlyScoped(
12
+ filePath: string,
13
+ scope: AnalysisOptions['scope']
14
+ ): boolean {
15
+ if (!scope || scope.length === 0) return false;
16
+ const normalizedFile = path.normalize(filePath);
17
+ return scope.some(scopeEntry => {
18
+ const normalizedScope = path.normalize(scopeEntry);
19
+ return (
20
+ normalizedFile === normalizedScope ||
21
+ normalizedFile.startsWith(normalizedScope + path.sep)
22
+ );
23
+ });
24
+ }
25
+
26
+ function isMinifiedArtifact(fileName: string): boolean {
27
+ return /\.min\.(?:js|mjs|cjs)$/i.test(fileName);
28
+ }
29
+
30
+ function hasSrcMirror(filePath: string, rootDir: string): boolean {
31
+ const rel = path.relative(rootDir, filePath);
32
+ if (!rel || rel.startsWith('..')) return false;
33
+ const parts = rel.split(path.sep);
34
+ if (parts[0] !== 'scripts' || parts.length < 2) return false;
35
+
36
+ const ext = path.extname(filePath);
37
+ const relWithoutExt = parts.slice(1).join(path.sep).slice(0, -ext.length);
38
+ for (const candidateExt of SOURCE_MIRROR_EXTS) {
39
+ const candidate = path.join(rootDir, 'src', `${relWithoutExt}${candidateExt}`);
40
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return true;
41
+ }
42
+ return false;
43
+ }
44
+
9
45
  export function collectFiles(rootDir: string, opts: AnalysisOptions): string[] {
10
46
  const files: string[] = [];
11
47
  const walk = (dir: string): void => {
@@ -25,6 +61,13 @@ export function collectFiles(rootDir: string, opts: AnalysisOptions): string[] {
25
61
  if (!entry.isFile()) continue;
26
62
  if (entry.name.endsWith('.d.ts')) continue;
27
63
 
64
+ if (
65
+ !isExplicitlyScoped(next, opts.scope) &&
66
+ (isMinifiedArtifact(entry.name) || hasSrcMirror(next, rootDir))
67
+ ) {
68
+ continue;
69
+ }
70
+
28
71
  const ext = path.extname(entry.name);
29
72
  if (!ALLOWED_EXTS.has(ext)) continue;
30
73
  if (!opts.includeTests && isTestFile(next)) continue;
@@ -645,3 +645,116 @@ describe('formatTextOutput with context', () => {
645
645
  expect(output).not.toContain('undefined');
646
646
  });
647
647
  });
648
+
649
+ describe('new AST search presets', () => {
650
+ it('catch-rethrow: finds catch blocks that re-throw', () => {
651
+ const source = `
652
+ try { doStuff(); } catch (e) { throw e; }
653
+ try { doOther(); } catch (e) { console.error(e); throw e; }
654
+ try { ok(); } catch (e) { handleError(e); }
655
+ `;
656
+ const preset = PRESETS['catch-rethrow'];
657
+ const matches = searchFile('test.ts', source, preset, null, 100);
658
+ expect(matches.length).toBeGreaterThanOrEqual(1);
659
+ });
660
+
661
+ it('catch-rethrow: no match when catch handles error', () => {
662
+ const source = `try { doStuff(); } catch (e) { handleError(e); }`;
663
+ const preset = PRESETS['catch-rethrow'];
664
+ const matches = searchFile('test.ts', source, preset, null, 100);
665
+ expect(matches.length).toBe(0);
666
+ });
667
+
668
+ it('promise-all: finds Promise.all calls', () => {
669
+ const source = `
670
+ const results = await Promise.all([fetch('/a'), fetch('/b')]);
671
+ const single = await fetch('/c');
672
+ `;
673
+ const preset = PRESETS['promise-all'];
674
+ const matches = searchFile('test.ts', source, preset, null, 100);
675
+ expect(matches.length).toBe(1);
676
+ });
677
+
678
+ it('promise-all: no match without Promise.all', () => {
679
+ const source = `const x = await fetch('/a');`;
680
+ const preset = PRESETS['promise-all'];
681
+ const matches = searchFile('test.ts', source, preset, null, 100);
682
+ expect(matches.length).toBe(0);
683
+ });
684
+
685
+ it('boolean-param: finds boolean-typed parameters', () => {
686
+ const source = `function toggle(visible: boolean, active: boolean) { return visible && active; }`;
687
+ const preset = PRESETS['boolean-param'];
688
+ const matches = searchFile('test.ts', source, preset, null, 100);
689
+ expect(matches.length).toBeGreaterThanOrEqual(1);
690
+ });
691
+
692
+ it('boolean-param: no match for non-boolean params', () => {
693
+ const source = `function add(a: number, b: number) { return a + b; }`;
694
+ const preset = PRESETS['boolean-param'];
695
+ const matches = searchFile('test.ts', source, preset, null, 100);
696
+ expect(matches.length).toBe(0);
697
+ });
698
+
699
+ it('magic-number: finds numeric literals excluding 0 and 1', () => {
700
+ const source = `
701
+ const x = 42;
702
+ const y = 0;
703
+ const z = 1;
704
+ const w = 100;
705
+ `;
706
+ const preset = PRESETS['magic-number'];
707
+ const matches = searchFile('test.ts', source, preset, null, 100);
708
+ expect(matches.length).toBe(2);
709
+ expect(matches.some(m => m.text === '42')).toBe(true);
710
+ expect(matches.some(m => m.text === '100')).toBe(true);
711
+ });
712
+
713
+ it('magic-number: no match for 0 and 1', () => {
714
+ const source = `const x = 0; const y = 1;`;
715
+ const preset = PRESETS['magic-number'];
716
+ const matches = searchFile('test.ts', source, preset, null, 100);
717
+ expect(matches.length).toBe(0);
718
+ });
719
+
720
+ it('deep-callback: finds 3+ nested arrow functions', () => {
721
+ const source = `
722
+ const f = () => {
723
+ return () => {
724
+ return () => {
725
+ return 42;
726
+ };
727
+ };
728
+ };
729
+ `;
730
+ const preset = PRESETS['deep-callback'];
731
+ const matches = searchFile('test.ts', source, preset, null, 100);
732
+ expect(matches.length).toBeGreaterThanOrEqual(1);
733
+ });
734
+
735
+ it('deep-callback: no match for 2 levels', () => {
736
+ const source = `const f = () => { return () => { return 1; }; };`;
737
+ const preset = PRESETS['deep-callback'];
738
+ const matches = searchFile('test.ts', source, preset, null, 100);
739
+ expect(matches.length).toBe(0);
740
+ });
741
+
742
+ it('unused-var: finds variable declarations without call expressions', () => {
743
+ const source = `
744
+ const x = 42;
745
+ const y = "hello";
746
+ `;
747
+ const preset = PRESETS['unused-var'];
748
+ const matches = searchFile('test.ts', source, preset, null, 100);
749
+ expect(matches.length).toBeGreaterThanOrEqual(2);
750
+ });
751
+
752
+ it('all new presets are defined in PRESETS object', () => {
753
+ const newPresets = ['catch-rethrow', 'promise-all', 'boolean-param', 'magic-number', 'deep-callback', 'unused-var'];
754
+ for (const name of newPresets) {
755
+ expect(PRESETS[name]).toBeDefined();
756
+ expect(PRESETS[name].description).toBeTruthy();
757
+ expect(PRESETS[name].rule).toBeDefined();
758
+ }
759
+ });
760
+ });
@@ -181,6 +181,69 @@ export const PRESETS: Record<string, PresetRule> = {
181
181
  },
182
182
  description: 'Namespace imports (import * as X)',
183
183
  },
184
+ 'catch-rethrow': {
185
+ rule: {
186
+ kind: 'catch_clause',
187
+ has: {
188
+ kind: 'statement_block',
189
+ has: {
190
+ kind: 'throw_statement',
191
+ },
192
+ },
193
+ },
194
+ description: 'Catch blocks that only re-throw the caught error',
195
+ },
196
+ 'promise-all': {
197
+ rule: {
198
+ pattern: 'Promise.all($$$ARGS)',
199
+ },
200
+ description: 'Promise.all calls (check for missing error handling)',
201
+ },
202
+ 'boolean-param': {
203
+ rule: {
204
+ kind: 'type_annotation',
205
+ has: {
206
+ kind: 'predefined_type',
207
+ regex: '^boolean$',
208
+ },
209
+ },
210
+ description: 'Function parameters typed as boolean',
211
+ },
212
+ 'magic-number': {
213
+ rule: {
214
+ kind: 'number',
215
+ not: {
216
+ regex: '^[01]$',
217
+ },
218
+ },
219
+ description: 'Numeric literals (excluding 0 and 1) — potential magic numbers',
220
+ },
221
+ 'deep-callback': {
222
+ rule: {
223
+ kind: 'arrow_function',
224
+ inside: {
225
+ kind: 'arrow_function',
226
+ inside: {
227
+ kind: 'arrow_function',
228
+ stopBy: 'end',
229
+ },
230
+ stopBy: 'end',
231
+ },
232
+ },
233
+ description: 'Deeply nested arrow function callbacks (3+ levels)',
234
+ },
235
+ 'unused-var': {
236
+ rule: {
237
+ kind: 'variable_declarator',
238
+ not: {
239
+ has: {
240
+ kind: 'call_expression',
241
+ stopBy: 'end',
242
+ },
243
+ },
244
+ },
245
+ description: 'Variable declarations without call expressions (candidates for dead code)',
246
+ },
184
247
  };
185
248
 
186
249
  function isTestFile(filePath: string): boolean {
@@ -232,7 +295,7 @@ function parserForExt(ext: string): AstParser {
232
295
  case '.tsx':
233
296
  return astTsx;
234
297
  case '.jsx':
235
- return astJs;
298
+ return astTsx;
236
299
  case '.js':
237
300
  case '.mjs':
238
301
  case '.cjs':