octocode-cli 1.2.6 → 1.2.8

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 (303) hide show
  1. package/LICENSE +21 -63
  2. package/README.md +85 -142
  3. package/out/octocode-cli.js +7063 -6934
  4. package/package.json +8 -6
  5. package/skills/README.md +97 -120
  6. package/skills/octocode-code-engineer/.claude/settings.local.json +18 -0
  7. package/skills/octocode-code-engineer/.octocode/rfc/RFC-code-engineer-weakness-fixes.md +255 -0
  8. package/skills/octocode-code-engineer/.plan/VALIDATED_PLAN.md +223 -0
  9. package/skills/octocode-code-engineer/README.md +178 -0
  10. package/skills/octocode-code-engineer/SKILL.md +418 -0
  11. package/skills/octocode-code-engineer/coverage/architecture.ts.html +7828 -0
  12. package/skills/octocode-code-engineer/coverage/ast-helpers.ts.html +211 -0
  13. package/skills/octocode-code-engineer/coverage/ast-search.ts.html +1795 -0
  14. package/skills/octocode-code-engineer/coverage/base.css +224 -0
  15. package/skills/octocode-code-engineer/coverage/block-navigation.js +87 -0
  16. package/skills/octocode-code-engineer/coverage/cache.ts.html +376 -0
  17. package/skills/octocode-code-engineer/coverage/cli.ts.html +982 -0
  18. package/skills/octocode-code-engineer/coverage/clover.xml +3217 -0
  19. package/skills/octocode-code-engineer/coverage/collect-effects.ts.html +664 -0
  20. package/skills/octocode-code-engineer/coverage/collect-input-sources.ts.html +577 -0
  21. package/skills/octocode-code-engineer/coverage/collect-performance.ts.html +331 -0
  22. package/skills/octocode-code-engineer/coverage/collect-prototype-pollution.ts.html +421 -0
  23. package/skills/octocode-code-engineer/coverage/collect-security.ts.html +604 -0
  24. package/skills/octocode-code-engineer/coverage/collect-test-profile.ts.html +589 -0
  25. package/skills/octocode-code-engineer/coverage/coverage-final.json +30 -0
  26. package/skills/octocode-code-engineer/coverage/dependencies.ts.html +997 -0
  27. package/skills/octocode-code-engineer/coverage/dependency-summary.ts.html +688 -0
  28. package/skills/octocode-code-engineer/coverage/discovery.ts.html +322 -0
  29. package/skills/octocode-code-engineer/coverage/favicon.png +0 -0
  30. package/skills/octocode-code-engineer/coverage/graph-analytics.ts.html +1510 -0
  31. package/skills/octocode-code-engineer/coverage/index.html +536 -0
  32. package/skills/octocode-code-engineer/coverage/index.ts.html +826 -0
  33. package/skills/octocode-code-engineer/coverage/metrics.ts.html +553 -0
  34. package/skills/octocode-code-engineer/coverage/pipeline.ts.html +2044 -0
  35. package/skills/octocode-code-engineer/coverage/prettify.css +1 -0
  36. package/skills/octocode-code-engineer/coverage/prettify.js +2 -0
  37. package/skills/octocode-code-engineer/coverage/report-analysis.ts.html +1570 -0
  38. package/skills/octocode-code-engineer/coverage/report-writer.ts.html +1102 -0
  39. package/skills/octocode-code-engineer/coverage/security-detectors.ts.html +1747 -0
  40. package/skills/octocode-code-engineer/coverage/semantic-detectors.ts.html +2152 -0
  41. package/skills/octocode-code-engineer/coverage/semantic.ts.html +1897 -0
  42. package/skills/octocode-code-engineer/coverage/sort-arrow-sprite.png +0 -0
  43. package/skills/octocode-code-engineer/coverage/sorter.js +210 -0
  44. package/skills/octocode-code-engineer/coverage/summary-md.ts.html +1222 -0
  45. package/skills/octocode-code-engineer/coverage/test-quality-detectors.ts.html +1039 -0
  46. package/skills/octocode-code-engineer/coverage/tree-sitter-analyzer.ts.html +955 -0
  47. package/skills/octocode-code-engineer/coverage/ts-analyzer.ts.html +1213 -0
  48. package/skills/octocode-code-engineer/coverage/types.ts.html +2473 -0
  49. package/skills/octocode-code-engineer/coverage/utils.ts.html +820 -0
  50. package/skills/octocode-code-engineer/eslint.config.mjs +54 -0
  51. package/skills/octocode-code-engineer/minify-scripts.mjs +32 -0
  52. package/skills/octocode-code-engineer/package.json +54 -0
  53. package/skills/octocode-code-engineer/references/agent-ast-reading-rfc.md +95 -0
  54. package/skills/octocode-code-engineer/references/architecture-techniques.md +121 -0
  55. package/skills/octocode-code-engineer/references/ast-search.md +210 -0
  56. package/skills/octocode-code-engineer/references/ast-tree-search.md +151 -0
  57. package/skills/octocode-code-engineer/references/cli-reference.md +167 -0
  58. package/skills/octocode-code-engineer/references/concepts.md +107 -0
  59. package/skills/octocode-code-engineer/references/finding-categories.md +128 -0
  60. package/skills/octocode-code-engineer/references/improvement-roadmap.md +304 -0
  61. package/skills/octocode-code-engineer/references/output-files.md +144 -0
  62. package/skills/octocode-code-engineer/references/playbooks.md +204 -0
  63. package/skills/octocode-code-engineer/references/present-results.md +136 -0
  64. package/skills/octocode-code-engineer/references/tool-workflows.md +566 -0
  65. package/skills/octocode-code-engineer/references/validate-investigate.md +225 -0
  66. package/skills/octocode-code-engineer/scripts/analysis/dependencies.js +1 -0
  67. package/skills/octocode-code-engineer/scripts/analysis/dependency-summary.js +1 -0
  68. package/skills/octocode-code-engineer/scripts/analysis/discovery.js +1 -0
  69. package/skills/octocode-code-engineer/scripts/analysis/graph-analytics.js +1 -0
  70. package/skills/octocode-code-engineer/scripts/analysis/semantic.js +1 -0
  71. package/skills/octocode-code-engineer/scripts/ast/helpers.js +1 -0
  72. package/skills/octocode-code-engineer/scripts/ast/metrics.js +1 -0
  73. package/skills/octocode-code-engineer/scripts/ast/search.js +2 -0
  74. package/skills/octocode-code-engineer/scripts/ast/tree-search.js +2 -0
  75. package/skills/octocode-code-engineer/scripts/ast/tree-sitter.js +1 -0
  76. package/skills/octocode-code-engineer/scripts/ast/ts-analyzer.js +1 -0
  77. package/skills/octocode-code-engineer/scripts/collectors/chains.js +1 -0
  78. package/skills/octocode-code-engineer/scripts/collectors/effects.js +1 -0
  79. package/skills/octocode-code-engineer/scripts/collectors/input-sources.js +1 -0
  80. package/skills/octocode-code-engineer/scripts/collectors/performance.js +1 -0
  81. package/skills/octocode-code-engineer/scripts/collectors/prototype-pollution.js +1 -0
  82. package/skills/octocode-code-engineer/scripts/collectors/security.js +1 -0
  83. package/skills/octocode-code-engineer/scripts/collectors/test-profile.js +1 -0
  84. package/skills/octocode-code-engineer/scripts/common/is-direct-run.js +1 -0
  85. package/skills/octocode-code-engineer/scripts/common/utils.js +1 -0
  86. package/skills/octocode-code-engineer/scripts/detectors/code-quality.js +1 -0
  87. package/skills/octocode-code-engineer/scripts/detectors/cohesion.js +1 -0
  88. package/skills/octocode-code-engineer/scripts/detectors/coupling.js +1 -0
  89. package/skills/octocode-code-engineer/scripts/detectors/cycle.js +1 -0
  90. package/skills/octocode-code-engineer/scripts/detectors/dead-code.js +1 -0
  91. package/skills/octocode-code-engineer/scripts/detectors/import-style.js +1 -0
  92. package/skills/octocode-code-engineer/scripts/detectors/index.js +1 -0
  93. package/skills/octocode-code-engineer/scripts/detectors/security.js +1 -0
  94. package/skills/octocode-code-engineer/scripts/detectors/semantic.js +1 -0
  95. package/skills/octocode-code-engineer/scripts/detectors/shared.js +1 -0
  96. package/skills/octocode-code-engineer/scripts/detectors/test-quality.js +1 -0
  97. package/skills/octocode-code-engineer/scripts/index.js +1 -0
  98. package/skills/octocode-code-engineer/scripts/pipeline/cache.js +1 -0
  99. package/skills/octocode-code-engineer/scripts/pipeline/cli.js +1 -0
  100. package/skills/octocode-code-engineer/scripts/pipeline/main.js +2 -0
  101. package/skills/octocode-code-engineer/scripts/reporting/analysis.js +1 -0
  102. package/skills/octocode-code-engineer/scripts/reporting/summary-md.js +1 -0
  103. package/skills/octocode-code-engineer/scripts/reporting/writer.js +1 -0
  104. package/skills/octocode-code-engineer/scripts/types/constants.js +1 -0
  105. package/skills/octocode-code-engineer/scripts/types/index.js +1 -0
  106. package/skills/octocode-code-engineer/scripts/types/interfaces.js +1 -0
  107. package/skills/octocode-code-engineer/src/analysis/dependencies.test.ts +545 -0
  108. package/skills/octocode-code-engineer/src/analysis/dependencies.ts +406 -0
  109. package/skills/octocode-code-engineer/src/analysis/dependency-summary.test.ts +566 -0
  110. package/skills/octocode-code-engineer/src/analysis/dependency-summary.ts +257 -0
  111. package/skills/octocode-code-engineer/src/analysis/discovery.test.ts +420 -0
  112. package/skills/octocode-code-engineer/src/analysis/discovery.ts +87 -0
  113. package/skills/octocode-code-engineer/src/analysis/graph-analytics.test.ts +449 -0
  114. package/skills/octocode-code-engineer/src/analysis/graph-analytics.ts +534 -0
  115. package/skills/octocode-code-engineer/src/analysis/semantic.test.ts +1533 -0
  116. package/skills/octocode-code-engineer/src/analysis/semantic.ts +830 -0
  117. package/skills/octocode-code-engineer/src/ast/helpers.test.ts +185 -0
  118. package/skills/octocode-code-engineer/src/ast/helpers.ts +62 -0
  119. package/skills/octocode-code-engineer/src/ast/metrics.test.ts +304 -0
  120. package/skills/octocode-code-engineer/src/ast/metrics.ts +204 -0
  121. package/skills/octocode-code-engineer/src/ast/search.test.ts +647 -0
  122. package/skills/octocode-code-engineer/src/ast/search.ts +648 -0
  123. package/skills/octocode-code-engineer/src/ast/tree-search.test.ts +199 -0
  124. package/skills/octocode-code-engineer/src/ast/tree-search.ts +392 -0
  125. package/skills/octocode-code-engineer/src/ast/tree-sitter.test.ts +407 -0
  126. package/skills/octocode-code-engineer/src/ast/tree-sitter.ts +402 -0
  127. package/skills/octocode-code-engineer/src/ast/ts-analyzer.test.ts +1864 -0
  128. package/skills/octocode-code-engineer/src/ast/ts-analyzer.ts +509 -0
  129. package/skills/octocode-code-engineer/src/collectors/chains.ts +74 -0
  130. package/skills/octocode-code-engineer/src/collectors/effects.test.ts +490 -0
  131. package/skills/octocode-code-engineer/src/collectors/effects.ts +332 -0
  132. package/skills/octocode-code-engineer/src/collectors/input-sources.test.ts +144 -0
  133. package/skills/octocode-code-engineer/src/collectors/input-sources.ts +196 -0
  134. package/skills/octocode-code-engineer/src/collectors/performance.test.ts +82 -0
  135. package/skills/octocode-code-engineer/src/collectors/performance.ts +141 -0
  136. package/skills/octocode-code-engineer/src/collectors/prototype-pollution.test.ts +55 -0
  137. package/skills/octocode-code-engineer/src/collectors/prototype-pollution.ts +162 -0
  138. package/skills/octocode-code-engineer/src/collectors/security.test.ts +124 -0
  139. package/skills/octocode-code-engineer/src/collectors/security.ts +309 -0
  140. package/skills/octocode-code-engineer/src/collectors/test-profile.test.ts +97 -0
  141. package/skills/octocode-code-engineer/src/collectors/test-profile.ts +269 -0
  142. package/skills/octocode-code-engineer/src/common/is-direct-run.test.ts +32 -0
  143. package/skills/octocode-code-engineer/src/common/is-direct-run.ts +13 -0
  144. package/skills/octocode-code-engineer/src/common/utils.test.ts +463 -0
  145. package/skills/octocode-code-engineer/src/common/utils.ts +304 -0
  146. package/skills/octocode-code-engineer/src/detectors/code-quality.ts +966 -0
  147. package/skills/octocode-code-engineer/src/detectors/cohesion.ts +539 -0
  148. package/skills/octocode-code-engineer/src/detectors/coupling.ts +323 -0
  149. package/skills/octocode-code-engineer/src/detectors/cycle.ts +349 -0
  150. package/skills/octocode-code-engineer/src/detectors/dead-code.ts +320 -0
  151. package/skills/octocode-code-engineer/src/detectors/import-style.ts +376 -0
  152. package/skills/octocode-code-engineer/src/detectors/index.test.ts +3061 -0
  153. package/skills/octocode-code-engineer/src/detectors/index.ts +88 -0
  154. package/skills/octocode-code-engineer/src/detectors/security.test.ts +882 -0
  155. package/skills/octocode-code-engineer/src/detectors/security.ts +821 -0
  156. package/skills/octocode-code-engineer/src/detectors/semantic.ts +758 -0
  157. package/skills/octocode-code-engineer/src/detectors/shared.ts +49 -0
  158. package/skills/octocode-code-engineer/src/detectors/test-quality.test.ts +388 -0
  159. package/skills/octocode-code-engineer/src/detectors/test-quality.ts +367 -0
  160. package/skills/octocode-code-engineer/src/index.test.ts +4425 -0
  161. package/skills/octocode-code-engineer/src/index.ts +403 -0
  162. package/skills/octocode-code-engineer/src/pipeline/cache.test.ts +199 -0
  163. package/skills/octocode-code-engineer/src/pipeline/cache.ts +130 -0
  164. package/skills/octocode-code-engineer/src/pipeline/cli.test.ts +493 -0
  165. package/skills/octocode-code-engineer/src/pipeline/cli.ts +344 -0
  166. package/skills/octocode-code-engineer/src/pipeline/main.test.ts +174 -0
  167. package/skills/octocode-code-engineer/src/pipeline/main.ts +1074 -0
  168. package/skills/octocode-code-engineer/src/pipeline.test.ts +84 -0
  169. package/skills/octocode-code-engineer/src/reporting/analysis.test.ts +782 -0
  170. package/skills/octocode-code-engineer/src/reporting/analysis.ts +688 -0
  171. package/skills/octocode-code-engineer/src/reporting/output-contract.test.ts +463 -0
  172. package/skills/octocode-code-engineer/src/reporting/summary-md.test.ts +421 -0
  173. package/skills/octocode-code-engineer/src/reporting/summary-md.ts +714 -0
  174. package/skills/octocode-code-engineer/src/reporting/writer.ts +430 -0
  175. package/skills/octocode-code-engineer/src/sanity.test.ts +47 -0
  176. package/skills/octocode-code-engineer/src/types/constants.ts +248 -0
  177. package/skills/octocode-code-engineer/src/types/index.ts +80 -0
  178. package/skills/octocode-code-engineer/src/types/interfaces.ts +682 -0
  179. package/skills/octocode-code-engineer/tsconfig.json +17 -0
  180. package/skills/octocode-code-engineer/vitest.config.ts +8 -0
  181. package/skills/octocode-documentation-writer/README.md +113 -0
  182. package/skills/octocode-documentation-writer/SKILL.md +886 -0
  183. package/skills/octocode-documentation-writer/references/agent-discovery-analysis.md +453 -0
  184. package/skills/octocode-documentation-writer/references/agent-documentation-writer.md +255 -0
  185. package/skills/octocode-documentation-writer/references/agent-engineer-questions.md +247 -0
  186. package/skills/octocode-documentation-writer/references/agent-orchestrator.md +370 -0
  187. package/skills/octocode-documentation-writer/references/agent-qa-validator.md +227 -0
  188. package/skills/octocode-documentation-writer/references/agent-researcher.md +250 -0
  189. package/skills/octocode-documentation-writer/schemas/analysis-schema.json +886 -0
  190. package/skills/octocode-documentation-writer/schemas/discovery-tasks.json +96 -0
  191. package/skills/octocode-documentation-writer/schemas/documentation-structure.json +373 -0
  192. package/skills/octocode-documentation-writer/schemas/partial-discovery-schema.json +102 -0
  193. package/skills/octocode-documentation-writer/schemas/partial-research-schema.json +98 -0
  194. package/skills/octocode-documentation-writer/schemas/qa-results-schema.json +113 -0
  195. package/skills/octocode-documentation-writer/schemas/questions-schema.json +228 -0
  196. package/skills/octocode-documentation-writer/schemas/research-schema.json +104 -0
  197. package/skills/octocode-documentation-writer/schemas/state-schema.json +222 -0
  198. package/skills/octocode-documentation-writer/schemas/work-assignments-schema.json +74 -0
  199. package/skills/octocode-plan/SKILL.md +122 -116
  200. package/skills/octocode-prompt-optimizer/SKILL.md +617 -0
  201. package/skills/octocode-pull-request-reviewer/README.md +249 -0
  202. package/skills/octocode-pull-request-reviewer/SKILL.md +479 -0
  203. package/skills/octocode-pull-request-reviewer/references/dependency-check.md +74 -0
  204. package/skills/octocode-pull-request-reviewer/references/domain-reviewers.md +24 -0
  205. package/skills/octocode-pull-request-reviewer/references/execution-lifecycle.md +441 -0
  206. package/skills/octocode-pull-request-reviewer/references/flow-analysis-protocol.md +64 -0
  207. package/skills/octocode-pull-request-reviewer/references/output-template.md +174 -0
  208. package/skills/octocode-pull-request-reviewer/references/parallel-agent-protocol.md +182 -0
  209. package/skills/octocode-pull-request-reviewer/references/review-guidelines.md +26 -0
  210. package/skills/octocode-pull-request-reviewer/references/verification-checklist.md +40 -0
  211. package/skills/octocode-research/.claude/settings.local.json +46 -0
  212. package/skills/octocode-research/.octocode/plan/code-review-fixes/plan.md +312 -0
  213. package/skills/octocode-research/.octocode/plan/code-review-fixes/research.md +212 -0
  214. package/skills/octocode-research/.octocode/plans/NODE_SERVER_START_PLAN.md +755 -0
  215. package/skills/octocode-research/.octocode/research/code-review/research.md +371 -0
  216. package/skills/octocode-research/.octocode/review/IMPROVEMENTS.md +391 -0
  217. package/skills/octocode-research/.octocode/review/REVIEW_PLAN.md +289 -0
  218. package/skills/octocode-research/.octocode/review/REVIEW_REPORT.md +356 -0
  219. package/skills/octocode-research/AGENTS.md +349 -0
  220. package/skills/octocode-research/README.md +494 -0
  221. package/skills/octocode-research/SKILL.md +652 -274
  222. package/skills/octocode-research/docs/API_REFERENCE.md +562 -0
  223. package/skills/octocode-research/docs/ARCHITECTURE.md +554 -0
  224. package/skills/octocode-research/docs/FLOWS.md +577 -0
  225. package/skills/octocode-research/docs/OVERVIEW.md +564 -0
  226. package/skills/octocode-research/docs/SERVER_FLOWS.md +631 -0
  227. package/skills/octocode-research/ecosystem.config.cjs +88 -0
  228. package/skills/octocode-research/eslint.config.mjs +27 -0
  229. package/skills/octocode-research/package.json +84 -0
  230. package/skills/octocode-research/references/GUARDRAILS.md +40 -0
  231. package/skills/octocode-research/references/PARALLEL_AGENT_PROTOCOL.md +178 -0
  232. package/skills/octocode-research/references/roast-prompt.md +149 -0
  233. package/skills/octocode-research/scripts/server-init.d.ts +2 -0
  234. package/skills/octocode-research/scripts/server-init.js +2 -0
  235. package/skills/octocode-research/scripts/server.d.ts +8 -0
  236. package/skills/octocode-research/scripts/server.js +445 -0
  237. package/skills/octocode-research/src/__tests__/integration/circuitBreaker.test.ts +205 -0
  238. package/skills/octocode-research/src/__tests__/integration/routes.test.ts +374 -0
  239. package/skills/octocode-research/src/__tests__/unit/circuitBreaker.test.ts +245 -0
  240. package/skills/octocode-research/src/__tests__/unit/errorHandler.test.ts +183 -0
  241. package/skills/octocode-research/src/__tests__/unit/httpPreprocess.test.ts +157 -0
  242. package/skills/octocode-research/src/__tests__/unit/logger.test.ts +143 -0
  243. package/skills/octocode-research/src/__tests__/unit/queryParser.test.ts +130 -0
  244. package/skills/octocode-research/src/__tests__/unit/responseBuilder.test.ts +469 -0
  245. package/skills/octocode-research/src/__tests__/unit/retry.test.ts +205 -0
  246. package/skills/octocode-research/src/index.ts +186 -0
  247. package/skills/octocode-research/src/mcpCache.ts +49 -0
  248. package/skills/octocode-research/src/middleware/errorHandler.ts +65 -0
  249. package/skills/octocode-research/src/middleware/logger.ts +61 -0
  250. package/skills/octocode-research/src/middleware/queryParser.ts +115 -0
  251. package/skills/octocode-research/src/middleware/readiness.ts +17 -0
  252. package/skills/octocode-research/src/routes/github.ts +197 -0
  253. package/skills/octocode-research/src/routes/local.ts +175 -0
  254. package/skills/octocode-research/src/routes/lsp.ts +177 -0
  255. package/skills/octocode-research/src/routes/package.ts +127 -0
  256. package/skills/octocode-research/src/routes/prompts.ts +138 -0
  257. package/skills/octocode-research/src/routes/tools.ts +677 -0
  258. package/skills/octocode-research/src/server-init.ts +363 -0
  259. package/skills/octocode-research/src/server.ts +285 -0
  260. package/skills/octocode-research/src/types/errorGuards.ts +151 -0
  261. package/skills/octocode-research/src/types/express.d.ts +76 -0
  262. package/skills/octocode-research/src/types/guards.ts +98 -0
  263. package/skills/octocode-research/src/types/mcp.ts +119 -0
  264. package/skills/octocode-research/src/types/responses.ts +199 -0
  265. package/skills/octocode-research/src/types/toolTypes.ts +33 -0
  266. package/skills/octocode-research/src/utils/asyncTimeout.ts +116 -0
  267. package/skills/octocode-research/src/utils/circuitBreaker.ts +492 -0
  268. package/skills/octocode-research/src/utils/colors.ts +53 -0
  269. package/skills/octocode-research/src/utils/errorQueue.ts +71 -0
  270. package/skills/octocode-research/src/utils/logEmoji.ts +103 -0
  271. package/skills/octocode-research/src/utils/logger.ts +413 -0
  272. package/skills/octocode-research/src/utils/resilience.ts +169 -0
  273. package/skills/octocode-research/src/utils/responseBuilder.ts +495 -0
  274. package/skills/octocode-research/src/utils/responseFactory.ts +100 -0
  275. package/skills/octocode-research/src/utils/responseParser.ts +272 -0
  276. package/skills/octocode-research/src/utils/retry.ts +280 -0
  277. package/skills/octocode-research/src/utils/routeFactory.ts +117 -0
  278. package/skills/octocode-research/src/utils/url.ts +20 -0
  279. package/skills/octocode-research/src/validation/httpPreprocess.ts +155 -0
  280. package/skills/octocode-research/src/validation/index.ts +2 -0
  281. package/skills/octocode-research/src/validation/schemas.ts +578 -0
  282. package/skills/octocode-research/src/validation/toolCallSchema.ts +132 -0
  283. package/skills/octocode-research/tsconfig.json +21 -0
  284. package/skills/octocode-research/tsdown.config.ts +42 -0
  285. package/skills/octocode-research/vitest.config.ts +20 -0
  286. package/skills/octocode-researcher/SKILL.md +461 -0
  287. package/skills/octocode-researcher/references/fallbacks.md +120 -0
  288. package/skills/{octocode-local-search → octocode-researcher}/references/tool-reference.md +132 -49
  289. package/skills/{octocode-local-search → octocode-researcher}/references/workflow-patterns.md +204 -4
  290. package/skills/octocode-rfc-generator/SKILL.md +223 -0
  291. package/skills/octocode-rfc-generator/references/rfc-template.md +193 -0
  292. package/skills/octocode-roast/SKILL.md +63 -21
  293. package/skills/octocode-implement/SKILL.md +0 -293
  294. package/skills/octocode-implement/references/execution-phases.md +0 -317
  295. package/skills/octocode-implement/references/tool-reference.md +0 -403
  296. package/skills/octocode-implement/references/workflow-patterns.md +0 -385
  297. package/skills/octocode-local-search/SKILL.md +0 -449
  298. package/skills/octocode-pr-review/SKILL.md +0 -391
  299. package/skills/octocode-pr-review/references/domain-reviewers.md +0 -105
  300. package/skills/octocode-pr-review/references/execution-lifecycle.md +0 -116
  301. package/skills/octocode-pr-review/references/research-flows.md +0 -75
  302. package/skills/octocode-research/references/tool-reference.md +0 -304
  303. package/skills/octocode-research/references/workflow-patterns.md +0 -325
@@ -0,0 +1,1074 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ import * as ts from 'typescript';
6
+
7
+ import {
8
+ clearCache,
9
+ createEmptyCache,
10
+ garbageCollect,
11
+ getCachedResult,
12
+ isCacheHit,
13
+ loadCache,
14
+ saveCache,
15
+ setCacheEntry,
16
+ } from './cache.js';
17
+ import { parseArgs } from './cli.js';
18
+ import { collectDependencyProfile } from '../analysis/dependencies.js';
19
+ import { buildDependencySummary } from '../analysis/dependency-summary.js';
20
+ import {
21
+ collectFiles,
22
+ fileSummaryWithFindings,
23
+ listWorkspacePackages,
24
+ safeRead,
25
+ } from '../analysis/discovery.js';
26
+ import {
27
+ buildAdvancedGraphFindings,
28
+ computeGraphAnalytics,
29
+ } from '../analysis/graph-analytics.js';
30
+ import {
31
+ analyzeSemanticProfile,
32
+ collectAllAbsoluteFiles,
33
+ createSemanticContext,
34
+ } from '../analysis/semantic.js';
35
+ import {
36
+ analyzeTreeSitterFile,
37
+ resolveTreeSitter,
38
+ } from '../ast/tree-sitter.js';
39
+ import {
40
+ analyzeSourceFile,
41
+ buildDependencyCriticality,
42
+ } from '../ast/ts-analyzer.js';
43
+ import { isDirectRun } from '../common/is-direct-run.js';
44
+ import { canonicalScriptKind, increment } from '../common/utils.js';
45
+ import { computeHotFiles } from '../detectors/index.js';
46
+ import { runSemanticDetectors } from '../detectors/semantic.js';
47
+ import { applyFindingsLimit, assignFindingIds, buildIssueCatalog } from '../index.js';
48
+ import {
49
+ computeReportAnalysisSummary,
50
+ enrichFileInventoryEntries,
51
+ enrichFindings,
52
+ } from '../reporting/analysis.js';
53
+ import { diverseTopRecommendations } from '../reporting/summary-md.js';
54
+ import { generateMermaidGraph, writeMultiFileReport } from '../reporting/writer.js';
55
+ import { PILLAR_CATEGORIES, SEMANTIC_CATEGORIES } from '../types/index.js';
56
+
57
+ import type { SemanticProfile } from '../analysis/semantic.js';
58
+ import type {
59
+ AnalysisOptions,
60
+ ControlMapEntry,
61
+ DependencyState,
62
+ DependencySummary,
63
+ DuplicateFlowHint,
64
+ DuplicateGroup,
65
+ FileCriticality,
66
+ FileEntry,
67
+ Finding,
68
+ FlowMapEntry,
69
+ PackageFileSummary,
70
+ PackageInfo,
71
+ RedundantFlowGroup,
72
+ TreeEntry,
73
+ } from '../types/index.js';
74
+
75
+ function discoverPackages(
76
+ root: string,
77
+ packageRoot: string
78
+ ): PackageInfo[] {
79
+ let packages = listWorkspacePackages(root, packageRoot);
80
+ if (!packages.length) {
81
+ const rootManifest = path.join(root, 'package.json');
82
+ if (fs.existsSync(rootManifest)) {
83
+ try {
84
+ const json = JSON.parse(fs.readFileSync(rootManifest, 'utf8'));
85
+ const name =
86
+ typeof json.name === 'string' ? json.name : path.basename(root);
87
+ packages = [{ name, dir: root, folder: path.basename(root) }];
88
+ } catch {
89
+ console.error(
90
+ `No packages found in ${packageRoot} and root package.json is unreadable`
91
+ );
92
+ process.exit(1);
93
+ }
94
+ } else {
95
+ console.error(
96
+ `No packages found in ${packageRoot} and no package.json in root`
97
+ );
98
+ process.exit(1);
99
+ }
100
+ }
101
+ return packages;
102
+ }
103
+
104
+ function groupDuplicates(
105
+ flowMap: Map<string, FlowMapEntry[]>,
106
+ controlMap: Map<string, ControlMapEntry[]>,
107
+ flowDupThreshold: number
108
+ ): {
109
+ duplicateFunctions: DuplicateGroup[];
110
+ redundantFlows: RedundantFlowGroup[];
111
+ duplicateFlowHints: DuplicateFlowHint[];
112
+ } {
113
+ const duplicateFunctions: DuplicateGroup[] = [...flowMap.entries()]
114
+ .map(([key, locations]) => {
115
+ if (locations.length < 2) return null;
116
+ const [first] = locations;
117
+ const [hash] = key.split('|');
118
+ const signatureName = first.name || first.kind || '<flow>';
119
+ const files = [...new Set(locations.map(item => item.file))];
120
+ return {
121
+ hash,
122
+ signature: signatureName,
123
+ kind: first.kind,
124
+ occurrences: locations.length,
125
+ filesCount: files.length,
126
+ locations: locations.slice(0, 20),
127
+ };
128
+ })
129
+ .filter((item): item is DuplicateGroup => item !== null)
130
+ .sort((a, b) => b.occurrences - a.occurrences);
131
+
132
+ const redundantFlows: RedundantFlowGroup[] = [...controlMap.entries()]
133
+ .map(([key, locations]) => {
134
+ if (locations.length <= 1) return null;
135
+ const [, kind] = key.split('|');
136
+ const files = [...new Set(locations.map(item => item.file))];
137
+ return {
138
+ kind,
139
+ occurrences: locations.length,
140
+ filesCount: files.length,
141
+ locations: locations.slice(0, 20),
142
+ };
143
+ })
144
+ .filter((item): item is RedundantFlowGroup => item !== null)
145
+ .sort((a, b) => b.occurrences - a.occurrences);
146
+
147
+ const duplicateFlowHints: DuplicateFlowHint[] = [];
148
+ for (const fn of duplicateFunctions.slice(0, 200)) {
149
+ if (fn.occurrences >= 2) {
150
+ duplicateFlowHints.push({
151
+ type: 'duplicate-function-body',
152
+ message: `Identical function body for ${fn.signature}`,
153
+ file: fn.locations[0]?.file,
154
+ lineStart: fn.locations[0]?.lineStart,
155
+ lineEnd: fn.locations[0]?.lineEnd,
156
+ details: `Occurs ${fn.occurrences} times in ${fn.filesCount} files.`,
157
+ });
158
+ }
159
+ }
160
+
161
+ for (const [index, flow] of redundantFlows.slice(0, 100).entries()) {
162
+ if (flow.occurrences >= flowDupThreshold) {
163
+ duplicateFlowHints.push({
164
+ type: 'repeated-flow',
165
+ message: `Repeated ${flow.kind} control structure`,
166
+ file: flow.locations[0]?.file,
167
+ lineStart: flow.locations[0]?.lineStart,
168
+ lineEnd: flow.locations[0]?.lineEnd,
169
+ details: `Structure appears ${flow.occurrences} times across ${flow.filesCount} file(s).`,
170
+ });
171
+ }
172
+ if (index > 100) break;
173
+ }
174
+
175
+ return { duplicateFunctions, redundantFlows, duplicateFlowHints };
176
+ }
177
+
178
+ function runSemanticPhase(
179
+ fileSummaries: FileEntry[],
180
+ dependencyState: DependencyState,
181
+ options: AnalysisOptions,
182
+ parseErrors: { file: string; message: string }[]
183
+ ): Array<Omit<Finding, 'id'>> {
184
+ if (!options.semantic) return [];
185
+ const wantsAnySemantic =
186
+ !options.features ||
187
+ [...SEMANTIC_CATEGORIES].some(c => options.features!.has(c));
188
+ if (!wantsAnySemantic) return [];
189
+
190
+ try {
191
+ const allAbsFiles = collectAllAbsoluteFiles(
192
+ fileSummaries,
193
+ dependencyState,
194
+ options.root
195
+ );
196
+ const semanticCtx = createSemanticContext(allAbsFiles, options.root);
197
+ const profiles: SemanticProfile[] = [];
198
+ for (const entry of fileSummaries) {
199
+ const absPath = path.resolve(options.root, entry.file);
200
+ try {
201
+ profiles.push(
202
+ analyzeSemanticProfile(
203
+ semanticCtx,
204
+ absPath,
205
+ entry,
206
+ options.includeTests
207
+ )
208
+ );
209
+ } catch {
210
+ void 0;
211
+ }
212
+ }
213
+ return runSemanticDetectors(semanticCtx, profiles, {
214
+ overrideChainThreshold: options.overrideChainThreshold,
215
+ shotgunThreshold: options.shotgunThreshold,
216
+ });
217
+ } catch (err: unknown) {
218
+ parseErrors.push({
219
+ file: '<semantic>',
220
+ message: `Semantic analysis failed: ${String((err as Error)?.message || err)}`,
221
+ });
222
+ return [];
223
+ }
224
+ }
225
+
226
+ function applyScopeFilter(
227
+ scopedFindings: Array<Omit<Finding, 'id'>>,
228
+ options: AnalysisOptions,
229
+ fileSummaries: FileEntry[]
230
+ ): Array<Omit<Finding, 'id'>> {
231
+ if (!options.scope) return scopedFindings;
232
+
233
+ const scopeMatchesRel = (file: string): boolean => {
234
+ const absPath = path.resolve(options.root, file);
235
+ return options.scope!.some(s => {
236
+ const normScope = path.normalize(s);
237
+ const normPath = path.normalize(absPath);
238
+ return (
239
+ normPath === normScope || normPath.startsWith(normScope + path.sep)
240
+ );
241
+ });
242
+ };
243
+ let filtered = scopedFindings.filter(
244
+ f => scopeMatchesRel(f.file) || (f.files?.some(scopeMatchesRel) ?? false)
245
+ );
246
+
247
+ if (options.scopeSymbols && options.scopeSymbols.size > 0) {
248
+ const symbolRanges: Array<{
249
+ file: string;
250
+ lineStart: number;
251
+ lineEnd: number;
252
+ name: string;
253
+ }> = [];
254
+ const unresolvedSymbols: string[] = [];
255
+ for (const [absFile, symbolNames] of options.scopeSymbols) {
256
+ const relFile = path.relative(options.root, absFile);
257
+ const entry = fileSummaries.find(e => e.file === relFile);
258
+ if (!entry) {
259
+ for (const sym of symbolNames) unresolvedSymbols.push(`${relFile}:${sym}`);
260
+ continue;
261
+ }
262
+ for (const sym of symbolNames) {
263
+ const fn = entry.functions.find(f => f.name === sym);
264
+ if (fn) {
265
+ symbolRanges.push({
266
+ file: relFile,
267
+ lineStart: fn.lineStart,
268
+ lineEnd: fn.lineEnd,
269
+ name: sym,
270
+ });
271
+ continue;
272
+ }
273
+ const exp = entry.dependencyProfile?.declaredExports?.find(
274
+ e => e.name === sym && e.lineStart != null && e.lineEnd != null
275
+ );
276
+ if (exp) {
277
+ symbolRanges.push({
278
+ file: relFile,
279
+ lineStart: exp.lineStart!,
280
+ lineEnd: exp.lineEnd!,
281
+ name: sym,
282
+ });
283
+ } else {
284
+ unresolvedSymbols.push(`${relFile}:${sym}`);
285
+ }
286
+ }
287
+ }
288
+ if (unresolvedSymbols.length > 0) {
289
+ console.warn(
290
+ `Warning: symbol scope could not resolve: ${unresolvedSymbols.join(', ')}. Falling back to file-level scope for those entries.`
291
+ );
292
+ }
293
+ if (symbolRanges.length > 0) {
294
+ const overlaps = (
295
+ fLineStart: number,
296
+ fLineEnd: number,
297
+ rLineStart: number,
298
+ rLineEnd: number
299
+ ): boolean => fLineStart <= rLineEnd && fLineEnd >= rLineStart;
300
+ filtered = filtered.filter(f =>
301
+ symbolRanges.some(
302
+ r =>
303
+ f.file === r.file &&
304
+ overlaps(f.lineStart, f.lineEnd, r.lineStart, r.lineEnd)
305
+ )
306
+ );
307
+ }
308
+ }
309
+ return filtered;
310
+ }
311
+
312
+ function printConsoleResults(
313
+ summary: { totalFiles: number; totalFunctions: number; totalFlows: number; totalDependencyFiles: number },
314
+ duplicateFunctions: DuplicateGroup[],
315
+ redundantFlows: RedundantFlowGroup[],
316
+ dependencySummary: DependencySummary,
317
+ findings: Finding[],
318
+ parseErrors: { file: string; message: string }[],
319
+ options: AnalysisOptions,
320
+ parserEffective: string
321
+ ): void {
322
+ console.log(
323
+ `AST analysis complete: ${summary.totalFiles} files, ${summary.totalFunctions} functions, ${summary.totalFlows} flow nodes`
324
+ );
325
+ if (summary.totalDependencyFiles !== summary.totalFiles) {
326
+ console.log(
327
+ `Dependency scan analyzed ${summary.totalDependencyFiles} files (including tests where present).`
328
+ );
329
+ }
330
+ console.log(`Duplicate function bodies: ${duplicateFunctions.length}`);
331
+ for (const item of duplicateFunctions.slice(0, 20)) {
332
+ console.log(
333
+ `- ${item.kind} "${item.signature}" occurs ${item.occurrences}x in ${item.filesCount} file(s)`
334
+ );
335
+ }
336
+
337
+ console.log(`\nRepeated control-flow structures: ${redundantFlows.length}`);
338
+ for (const item of redundantFlows.slice(0, 20)) {
339
+ console.log(
340
+ `- ${item.kind} appears ${item.occurrences}x across ${item.filesCount} file(s)`
341
+ );
342
+ }
343
+
344
+ console.log(
345
+ `\nDependency graph: ${dependencySummary.totalModules} modules, ${dependencySummary.totalEdges} import edges`
346
+ );
347
+ if (dependencySummary.totalModules > 0) {
348
+ console.log(
349
+ `- Critical chains: ${dependencySummary.criticalPaths.length} (showing top ${Math.min(options.deepLinkTopN, dependencySummary.criticalPaths.length)})`
350
+ );
351
+ console.log(
352
+ `- Root modules: ${dependencySummary.rootsCount}, Leaf modules: ${dependencySummary.leavesCount}`
353
+ );
354
+ console.log(
355
+ `- Test-only modules: ${dependencySummary.testOnlyModules.length}`
356
+ );
357
+ console.log(`- Cycles: ${dependencySummary.cycles.length}`);
358
+ }
359
+
360
+ console.log(`\nAgent Findings: ${findings.length}`);
361
+ for (const item of findings.slice(0, 20)) {
362
+ console.log(`- [${item.severity.toUpperCase()}] ${item.title}`);
363
+ console.log(` - ${item.reason}`);
364
+ console.log(` - fix: ${item.suggestedFix.strategy}`);
365
+ }
366
+
367
+ if (parseErrors.length > 0) {
368
+ console.log(`\nParse errors: ${parseErrors.length}`);
369
+ parseErrors.slice(0, 10).forEach(error => {
370
+ console.log(`- ${error.file}: ${error.message}`);
371
+ });
372
+ }
373
+
374
+ console.log(`\nParser engine used: ${parserEffective}`);
375
+ }
376
+
377
+ interface ScanState {
378
+ options: AnalysisOptions;
379
+ packages: PackageInfo[];
380
+ effectiveParser: string;
381
+ useTreeSitter: boolean;
382
+ summary: {
383
+ totalPackages: number;
384
+ totalFiles: number;
385
+ totalNodes: number;
386
+ totalFunctions: number;
387
+ totalFlows: number;
388
+ totalDependencyFiles: number;
389
+ byPackage: Record<
390
+ string,
391
+ {
392
+ files: number;
393
+ nodes: number;
394
+ functions: number;
395
+ flows: number;
396
+ topKinds: [string, number][];
397
+ rootPath: string;
398
+ }
399
+ >;
400
+ };
401
+ flowMap: Map<string, FlowMapEntry[]>;
402
+ controlMap: Map<string, ControlMapEntry[]>;
403
+ trees: TreeEntry[];
404
+ fileSummaries: FileEntry[];
405
+ parseErrors: { file: string; message: string }[];
406
+ dependencyState: DependencyState;
407
+ packageFileStats: Record<string, PackageFileSummary>;
408
+ allPkgJsonDeps: Record<string, string>;
409
+ allPkgJsonDevDeps: Record<string, string>;
410
+ cacheHits: number;
411
+ isLegacyMode: boolean;
412
+ outputDir: string | null;
413
+ outputPath: string | null;
414
+ treeSitterAvailable: boolean;
415
+ treeSitterError: string | null;
416
+ }
417
+
418
+ async function initScanState(): Promise<ScanState | null> {
419
+ const options = parseArgs(process.argv.slice(2));
420
+
421
+ if (options.clearCache) {
422
+ clearCache(options.root);
423
+ console.error('Cache cleared.');
424
+ return null;
425
+ }
426
+
427
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
428
+ const isLegacyMode = options.out?.endsWith('.json') ?? false;
429
+ const outputDir = isLegacyMode
430
+ ? null
431
+ : options.out || path.join(options.root, '.octocode', 'scan', timestamp);
432
+ const outputPath = isLegacyMode ? (options.out ?? null) : null;
433
+
434
+ const packages = discoverPackages(options.root, options.packageRoot);
435
+
436
+ let effectiveParser = options.parser as string;
437
+ const parserProbe =
438
+ options.parser === 'tree-sitter' || options.parser === 'auto'
439
+ ? await resolveTreeSitter()
440
+ : { available: false, error: null, parserTs: null, parserTsx: null };
441
+ const useTreeSitter =
442
+ (options.parser === 'tree-sitter' || options.parser === 'auto') &&
443
+ Boolean(parserProbe?.available);
444
+
445
+ if (options.parser === 'tree-sitter' && !parserProbe?.available) {
446
+ console.warn(
447
+ `Tree-sitter requested but unavailable: ${parserProbe?.error || 'missing parser modules'}`
448
+ );
449
+ console.warn('Falling back to TypeScript parser for duplicate detection.');
450
+ effectiveParser = 'typescript';
451
+ }
452
+
453
+ if (options.parser === 'tree-sitter' && parserProbe?.available) {
454
+ effectiveParser = 'tree-sitter (primary) + typescript (dependencies)';
455
+ }
456
+
457
+ if (options.parser === 'auto' && parserProbe?.available) {
458
+ effectiveParser = 'typescript (primary) + tree-sitter (node count)';
459
+ }
460
+
461
+ const allPkgJsonDeps: Record<string, string> = {};
462
+ const allPkgJsonDevDeps: Record<string, string> = {};
463
+ for (const pkg of packages) {
464
+ try {
465
+ const manifest = JSON.parse(
466
+ fs.readFileSync(path.join(pkg.dir, 'package.json'), 'utf8')
467
+ );
468
+ Object.assign(allPkgJsonDeps, manifest.dependencies || {});
469
+ Object.assign(allPkgJsonDevDeps, manifest.devDependencies || {});
470
+ } catch {
471
+ void 0;
472
+ }
473
+ }
474
+
475
+ return {
476
+ options,
477
+ packages,
478
+ effectiveParser,
479
+ useTreeSitter,
480
+ summary: {
481
+ totalPackages: packages.length,
482
+ totalFiles: 0,
483
+ totalNodes: 0,
484
+ totalFunctions: 0,
485
+ totalFlows: 0,
486
+ totalDependencyFiles: 0,
487
+ byPackage: {},
488
+ },
489
+ flowMap: new Map(),
490
+ controlMap: new Map(),
491
+ trees: [],
492
+ fileSummaries: [],
493
+ parseErrors: [],
494
+ dependencyState: {
495
+ files: new Set(),
496
+ outgoing: new Map(),
497
+ incoming: new Map(),
498
+ incomingFromTests: new Map(),
499
+ incomingFromProduction: new Map(),
500
+ externalCounts: new Map(),
501
+ unresolvedCounts: new Map(),
502
+ declaredExportsByFile: new Map(),
503
+ importedSymbolsByFile: new Map(),
504
+ reExportsByFile: new Map(),
505
+ },
506
+ packageFileStats: Object.fromEntries(
507
+ packages.map(pkg => [
508
+ pkg.name,
509
+ {
510
+ fileCount: 0,
511
+ nodeCount: 0,
512
+ functionCount: 0,
513
+ flowCount: 0,
514
+ kindCounts: {},
515
+ functions: [],
516
+ flows: [],
517
+ },
518
+ ])
519
+ ),
520
+ allPkgJsonDeps,
521
+ allPkgJsonDevDeps,
522
+ cacheHits: 0,
523
+ isLegacyMode,
524
+ outputDir,
525
+ outputPath,
526
+ treeSitterAvailable: useTreeSitter,
527
+ treeSitterError: parserProbe?.available ? null : (parserProbe?.error || null),
528
+ };
529
+ }
530
+
531
+ type CachedResult = {
532
+ fileEntry: FileEntry;
533
+ flowMapEntries: [string, FlowMapEntry[]][];
534
+ controlMapEntries: [string, ControlMapEntry[]][];
535
+ treeEntry?: TreeEntry;
536
+ };
537
+
538
+ function collectFileData(state: ScanState): void {
539
+ const { options, packages, useTreeSitter, summary, flowMap, controlMap, trees, fileSummaries, parseErrors, dependencyState, packageFileStats } = state;
540
+ const cache = options.noCache ? null : loadCache(options.root);
541
+ const newCache = createEmptyCache(options.root);
542
+
543
+ for (const pkg of packages) {
544
+ let packageStats = packageFileStats[pkg.name];
545
+ if (!packageStats) {
546
+ packageStats = {
547
+ fileCount: 0,
548
+ nodeCount: 0,
549
+ functionCount: 0,
550
+ flowCount: 0,
551
+ kindCounts: {},
552
+ functions: [],
553
+ flows: [],
554
+ };
555
+ packageFileStats[pkg.name] = packageStats;
556
+ }
557
+ const packageFiles = collectFiles(pkg.dir, options);
558
+ const dependencyFiles = collectFiles(pkg.dir, {
559
+ ...options,
560
+ includeTests: true,
561
+ });
562
+ const scopeMatchesPath = (absPath: string): boolean =>
563
+ options.scope != null &&
564
+ options.scope.some(s => {
565
+ const normScope = path.normalize(s);
566
+ const normPath = path.normalize(absPath);
567
+ return (
568
+ normPath === normScope || normPath.startsWith(normScope + path.sep)
569
+ );
570
+ });
571
+ const scopedPackageFiles = options.scope
572
+ ? packageFiles.filter(f => scopeMatchesPath(f))
573
+ : packageFiles;
574
+ const analysisFileSet = new Set(scopedPackageFiles);
575
+
576
+ for (const filePath of dependencyFiles) {
577
+ const text = safeRead(filePath);
578
+ if (text === null) {
579
+ parseErrors.push({
580
+ file: path.relative(options.root, filePath),
581
+ message: 'Failed to read file',
582
+ });
583
+ continue;
584
+ }
585
+
586
+ const ext = path.extname(filePath);
587
+ const source = ts.createSourceFile(
588
+ filePath,
589
+ text,
590
+ ts.ScriptTarget.ESNext,
591
+ true,
592
+ canonicalScriptKind(ext)
593
+ );
594
+
595
+ try {
596
+ const dependencyProfile = collectDependencyProfile(
597
+ source,
598
+ filePath,
599
+ pkg.name,
600
+ options,
601
+ dependencyState
602
+ );
603
+ if (!analysisFileSet.has(filePath)) continue;
604
+
605
+ const relPath = path.relative(options.root, filePath);
606
+ const stat = fs.statSync(filePath);
607
+ const statKey = { mtimeMs: stat.mtimeMs, size: stat.size };
608
+
609
+ if (cache && isCacheHit(cache, relPath, statKey)) {
610
+ const raw = getCachedResult(cache, relPath) as
611
+ | CachedResult
612
+ | undefined;
613
+ if (raw?.fileEntry) {
614
+ for (const [key, entries] of raw.flowMapEntries ?? []) {
615
+ for (const entry of entries) increment(flowMap, key, entry);
616
+ }
617
+ for (const [key, entries] of raw.controlMapEntries ?? []) {
618
+ for (const entry of entries) increment(controlMap, key, entry);
619
+ }
620
+ const fileSummary: FileEntry = {
621
+ ...raw.fileEntry,
622
+ dependencyProfile,
623
+ };
624
+ packageStats.fileCount += 1;
625
+ packageStats.nodeCount += fileSummary.nodeCount;
626
+ packageStats.functionCount += fileSummary.functions.length;
627
+ packageStats.flowCount += fileSummary.flows.length;
628
+ for (const [k, count] of Object.entries(fileSummary.kindCounts)) {
629
+ packageStats.kindCounts[k] =
630
+ (packageStats.kindCounts[k] || 0) + count;
631
+ }
632
+ for (const fn of fileSummary.functions)
633
+ packageStats.functions.push(fn);
634
+ if (raw.treeEntry) trees.push(raw.treeEntry);
635
+
636
+ summary.totalFiles += 1;
637
+ summary.totalNodes += fileSummary.nodeCount;
638
+ summary.totalFunctions += fileSummary.functions.length;
639
+ summary.totalFlows += fileSummary.flows.length;
640
+ fileSummaries.push(fileSummary);
641
+
642
+ setCacheEntry(newCache, relPath, statKey, raw);
643
+ state.cacheHits++;
644
+ continue;
645
+ }
646
+ }
647
+
648
+ const fileFlowMap = new Map<string, FlowMapEntry[]>();
649
+ const fileControlMap = new Map<string, ControlMapEntry[]>();
650
+
651
+ const treeSitterPrimary =
652
+ useTreeSitter && options.parser === 'tree-sitter';
653
+
654
+ let fileSummary: FileEntry;
655
+
656
+ if (treeSitterPrimary) {
657
+ const treeSitterEntry = analyzeTreeSitterFile(
658
+ filePath,
659
+ text,
660
+ options,
661
+ pkg.name,
662
+ { flowMap: fileFlowMap, controlMap: fileControlMap }
663
+ );
664
+ if (!treeSitterEntry) {
665
+ const fallback = analyzeSourceFile(
666
+ source,
667
+ pkg.name,
668
+ packageStats,
669
+ options,
670
+ { flowMap: fileFlowMap, controlMap: fileControlMap },
671
+ trees,
672
+ dependencyProfile
673
+ );
674
+ fallback.parserFallback = 'typescript (tree-sitter failed)';
675
+ fileSummary = fallback;
676
+ } else {
677
+ const fileRelative = path.relative(options.root, filePath);
678
+ fileSummary = {
679
+ package: pkg.name,
680
+ file: fileRelative,
681
+ parseEngine: 'tree-sitter',
682
+ nodeCount: treeSitterEntry.nodeCount,
683
+ kindCounts: {},
684
+ functions: treeSitterEntry.functions,
685
+ flows: treeSitterEntry.flows,
686
+ dependencyProfile,
687
+ };
688
+ if (treeSitterEntry.tree && options.emitTree) {
689
+ trees.push({
690
+ package: pkg.name,
691
+ file: fileRelative,
692
+ tree: treeSitterEntry.tree,
693
+ });
694
+ }
695
+ packageStats.fileCount += 1;
696
+ packageStats.nodeCount += treeSitterEntry.nodeCount;
697
+ packageStats.functionCount += treeSitterEntry.functions.length;
698
+ packageStats.flowCount += treeSitterEntry.flows.length;
699
+ for (const fn of treeSitterEntry.functions) {
700
+ packageStats.functions.push(fn);
701
+ }
702
+ }
703
+ } else {
704
+ fileSummary = analyzeSourceFile(
705
+ source,
706
+ pkg.name,
707
+ packageStats,
708
+ options,
709
+ { flowMap: fileFlowMap, controlMap: fileControlMap },
710
+ trees,
711
+ dependencyProfile
712
+ );
713
+
714
+ if (useTreeSitter) {
715
+ try {
716
+ const treeSitterEntry = analyzeTreeSitterFile(
717
+ filePath,
718
+ text,
719
+ options,
720
+ pkg.name,
721
+ null
722
+ );
723
+ if (treeSitterEntry) {
724
+ fileSummary.treeSitterNodeCount = treeSitterEntry.nodeCount;
725
+ }
726
+ } catch (error: unknown) {
727
+ fileSummary.treeSitterError = String(
728
+ (error as Error)?.message || error
729
+ );
730
+ }
731
+ }
732
+ }
733
+
734
+ for (const [key, entries] of fileFlowMap) {
735
+ for (const entry of entries) increment(flowMap, key, entry);
736
+ }
737
+ for (const [key, entries] of fileControlMap) {
738
+ for (const entry of entries) increment(controlMap, key, entry);
739
+ }
740
+
741
+ const treeEntry = options.emitTree
742
+ ? trees.find(t => t.file === relPath)
743
+ : undefined;
744
+ const toCache: CachedResult = {
745
+ fileEntry: fileSummary,
746
+ flowMapEntries: [...fileFlowMap.entries()],
747
+ controlMapEntries: [...fileControlMap.entries()],
748
+ ...(treeEntry && { treeEntry }),
749
+ };
750
+ setCacheEntry(newCache, relPath, statKey, toCache);
751
+
752
+ summary.totalFiles += 1;
753
+ summary.totalNodes += fileSummary.nodeCount;
754
+ summary.totalFunctions += fileSummary.functions.length;
755
+ summary.totalFlows += fileSummary.flows.length;
756
+ fileSummaries.push(fileSummary);
757
+ } catch (error: unknown) {
758
+ parseErrors.push({
759
+ file: path.relative(options.root, filePath),
760
+ message: String((error as Error)?.message || error),
761
+ });
762
+ }
763
+ }
764
+
765
+ summary.byPackage[pkg.name] = {
766
+ files: packageStats.fileCount,
767
+ nodes: packageStats.nodeCount,
768
+ functions: packageStats.functionCount,
769
+ flows: packageStats.flowCount,
770
+ topKinds: Object.entries(packageStats.kindCounts)
771
+ .sort((a, b) => b[1] - a[1])
772
+ .slice(0, 8),
773
+ rootPath: pkg.folder,
774
+ };
775
+ }
776
+
777
+ if (!options.noCache) {
778
+ garbageCollect(newCache);
779
+ saveCache(options.root, newCache);
780
+ }
781
+ if (state.cacheHits > 0 && !options.json) {
782
+ console.error(
783
+ `Cache: ${state.cacheHits} hits, ${fileSummaries.length - state.cacheHits} misses`
784
+ );
785
+ }
786
+
787
+ summary.totalDependencyFiles = dependencyState.files.size;
788
+ }
789
+
790
+ function analyzeAndReport(state: ScanState): void {
791
+ const { options, effectiveParser, summary, flowMap, controlMap, trees, fileSummaries, parseErrors, dependencyState, allPkgJsonDeps, allPkgJsonDevDeps, isLegacyMode, outputDir, outputPath, treeSitterAvailable, treeSitterError } = state;
792
+
793
+ const { duplicateFunctions, redundantFlows, duplicateFlowHints } =
794
+ groupDuplicates(flowMap, controlMap, options.flowDupThreshold);
795
+
796
+ const fileCriticalityByPath = new Map<string, FileCriticality>(
797
+ fileSummaries.map(item => [
798
+ item.file,
799
+ buildDependencyCriticality(item, options),
800
+ ])
801
+ );
802
+ const dependencySummary = buildDependencySummary(
803
+ dependencyState,
804
+ fileCriticalityByPath,
805
+ options
806
+ );
807
+ const graphAnalytics = computeGraphAnalytics(
808
+ dependencyState,
809
+ dependencySummary,
810
+ fileCriticalityByPath
811
+ );
812
+ const advancedGraphFindings = options.graphAdvanced
813
+ ? buildAdvancedGraphFindings(graphAnalytics, dependencyState, fileSummaries)
814
+ : [];
815
+
816
+ const semanticFindings = runSemanticPhase(
817
+ fileSummaries,
818
+ dependencyState,
819
+ options,
820
+ parseErrors
821
+ );
822
+
823
+ const catalog = buildIssueCatalog(
824
+ duplicateFunctions,
825
+ redundantFlows,
826
+ fileSummaries,
827
+ dependencySummary,
828
+ dependencyState,
829
+ options,
830
+ allPkgJsonDeps,
831
+ allPkgJsonDevDeps,
832
+ fileCriticalityByPath,
833
+ semanticFindings,
834
+ flowMap,
835
+ advancedGraphFindings
836
+ );
837
+ let scopedFindings = catalog.allFindings;
838
+
839
+ scopedFindings = applyScopeFilter(scopedFindings, options, fileSummaries);
840
+
841
+ const {
842
+ findings: limitedFindings,
843
+ totalBeforeTruncation,
844
+ droppedCategories,
845
+ } = applyFindingsLimit(scopedFindings, options);
846
+ const assigned = assignFindingIds(limitedFindings);
847
+ let findings = assigned.findings;
848
+ const byFile = assigned.byFile;
849
+ const findingStats = buildFindingStats(scopedFindings);
850
+
851
+ const enrichedFileSummaries = enrichFileInventoryEntries(fileSummaries, {
852
+ flowEnabled: !!options.flow,
853
+ });
854
+ const hotFiles = computeHotFiles(
855
+ dependencyState,
856
+ dependencySummary,
857
+ fileCriticalityByPath
858
+ );
859
+ findings = enrichFindings(
860
+ findings,
861
+ enrichedFileSummaries,
862
+ hotFiles,
863
+ graphAnalytics,
864
+ { flowEnabled: !!options.flow }
865
+ );
866
+ const reportAnalysis = computeReportAnalysisSummary(
867
+ findings,
868
+ enrichedFileSummaries,
869
+ hotFiles,
870
+ graphAnalytics
871
+ );
872
+ const enhancedFileSummaries = fileSummaryWithFindings(
873
+ enrichedFileSummaries,
874
+ byFile
875
+ );
876
+
877
+ const report = {
878
+ generatedAt: new Date().toISOString(),
879
+ repoRoot: options.root,
880
+ options: {
881
+ ...options,
882
+ ignoreDirs: [...options.ignoreDirs],
883
+ },
884
+ parser: {
885
+ requested: options.parser,
886
+ effective: effectiveParser,
887
+ treeSitterAvailable,
888
+ treeSitterError,
889
+ },
890
+ summary,
891
+ fileInventory: enhancedFileSummaries,
892
+ duplicateFlows: {
893
+ duplicatedFunctions: duplicateFunctions.slice(0, 200),
894
+ duplicatedControlFlow: redundantFlows.slice(0, 200),
895
+ totalFunctionGroups: duplicateFunctions.length,
896
+ totalFlowGroups: redundantFlows.length,
897
+ },
898
+ dependencyGraph: dependencySummary,
899
+ dependencyFindings: findings.filter(item =>
900
+ item.category?.startsWith('dependency')
901
+ ),
902
+ agentOutput: {
903
+ totalFindings: findings.length,
904
+ totalBeforeTruncation,
905
+ droppedCategories,
906
+ findingStats,
907
+ analysisSummary: {
908
+ strongestGraphSignal: reportAnalysis.strongestGraphSignal,
909
+ strongestAstSignal: reportAnalysis.strongestAstSignal,
910
+ combinedSignals: reportAnalysis.combinedSignals,
911
+ recommendedValidation: reportAnalysis.recommendedValidation,
912
+ },
913
+ highPriority: findings.filter(
914
+ f => f.severity === 'high' || f.severity === 'critical'
915
+ ).length,
916
+ mediumPriority: findings.filter(f => f.severity === 'medium').length,
917
+ lowPriority: findings.filter(
918
+ f => f.severity === 'low' || f.severity === 'info'
919
+ ).length,
920
+ topRecommendations: diverseTopRecommendations(
921
+ findings,
922
+ 20,
923
+ options.maxRecsPerCategory
924
+ ).map(f => ({
925
+ id: f.id,
926
+ file: f.file,
927
+ severity: f.severity,
928
+ category: f.category,
929
+ title: f.title,
930
+ reason: f.reason,
931
+ suggestedFix: f.suggestedFix,
932
+ })),
933
+ filesWithIssues: [...byFile.entries()].map(([file, ids]) => ({
934
+ file,
935
+ issueCount: ids.length,
936
+ issueIds: ids,
937
+ })),
938
+ },
939
+ optimizationOpportunities: duplicateFlowHints,
940
+ optimizationFindings: findings,
941
+ parseErrors,
942
+ astTrees: undefined as TreeEntry[] | undefined,
943
+ graphAnalytics,
944
+ reportAnalysis,
945
+ };
946
+
947
+ if (options.emitTree) {
948
+ report.astTrees = trees;
949
+ }
950
+
951
+ if (options.json) {
952
+ console.log(JSON.stringify(report));
953
+ } else {
954
+ printConsoleResults(
955
+ summary,
956
+ duplicateFunctions,
957
+ redundantFlows,
958
+ dependencySummary,
959
+ findings,
960
+ parseErrors,
961
+ options,
962
+ report.parser.effective
963
+ );
964
+ }
965
+
966
+ if (isLegacyMode && outputPath) {
967
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
968
+ fs.writeFileSync(outputPath, JSON.stringify(report), 'utf8');
969
+ if (!options.json) {
970
+ console.log(
971
+ `\nFull report written to ${path.relative(options.root, outputPath)}`
972
+ );
973
+ }
974
+ if (options.graph) {
975
+ const graphMd = generateMermaidGraph(
976
+ dependencyState,
977
+ dependencySummary,
978
+ fileCriticalityByPath
979
+ );
980
+ const graphPath = outputPath.replace(/\.json$/, '-graph.md');
981
+ fs.writeFileSync(graphPath, graphMd, 'utf8');
982
+ if (!options.json) {
983
+ console.log(
984
+ `Dependency graph written to ${path.relative(options.root, graphPath)}`
985
+ );
986
+ }
987
+ }
988
+ } else if (outputDir) {
989
+ const outputFiles = writeMultiFileReport(
990
+ outputDir,
991
+ report,
992
+ options,
993
+ dependencyState,
994
+ dependencySummary,
995
+ fileCriticalityByPath
996
+ );
997
+ if (!options.json) {
998
+ const relDir = path.relative(options.root, outputDir);
999
+ console.log(`\nReport written to ${relDir}/`);
1000
+ for (const [key, file] of Object.entries(outputFiles)) {
1001
+ console.log(` ${key}: ${file}`);
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+
1007
+ async function main(): Promise<void> {
1008
+ const state = await initScanState();
1009
+ if (!state) return;
1010
+ collectFileData(state);
1011
+ analyzeAndReport(state);
1012
+ }
1013
+
1014
+ export { main };
1015
+
1016
+ function buildFindingStats(
1017
+ findings: Array<Omit<Finding, 'id'>>
1018
+ ): {
1019
+ overall: {
1020
+ totalFindings: number;
1021
+ severityBreakdown: Record<string, number>;
1022
+ };
1023
+ pillars: Record<
1024
+ string,
1025
+ {
1026
+ totalFindings: number;
1027
+ severityBreakdown: Record<string, number>;
1028
+ }
1029
+ >;
1030
+ } {
1031
+ const makeSeverityBreakdown = (
1032
+ entries: Array<Pick<Finding, 'severity'>>
1033
+ ): Record<string, number> => {
1034
+ const counts: Record<string, number> = {
1035
+ critical: 0,
1036
+ high: 0,
1037
+ medium: 0,
1038
+ low: 0,
1039
+ info: 0,
1040
+ };
1041
+ for (const entry of entries) {
1042
+ counts[entry.severity] = (counts[entry.severity] || 0) + 1;
1043
+ }
1044
+ return counts;
1045
+ };
1046
+
1047
+ const overall = {
1048
+ totalFindings: findings.length,
1049
+ severityBreakdown: makeSeverityBreakdown(findings),
1050
+ };
1051
+
1052
+ const pillars = Object.fromEntries(
1053
+ Object.entries(PILLAR_CATEGORIES).map(([pillar, categories]) => {
1054
+ const categorySet = new Set(categories);
1055
+ const pillarFindings = findings.filter(f => categorySet.has(f.category));
1056
+ return [
1057
+ pillar,
1058
+ {
1059
+ totalFindings: pillarFindings.length,
1060
+ severityBreakdown: makeSeverityBreakdown(pillarFindings),
1061
+ },
1062
+ ];
1063
+ })
1064
+ );
1065
+
1066
+ return { overall, pillars };
1067
+ }
1068
+
1069
+ if (isDirectRun(import.meta.url)) {
1070
+ main().catch((error: unknown) => {
1071
+ console.error(error);
1072
+ process.exit(1);
1073
+ });
1074
+ }