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,501 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
4
+ import express from 'express';
5
+ import request from 'supertest';
6
+ import { errorHandler } from '../../middleware/errorHandler.js';
7
+
8
+ vi.mock('../../mcpCache.js', () => ({
9
+ getMcpContent: vi.fn().mockReturnValue({
10
+ tools: {
11
+ localSearchCode: {
12
+ name: 'localSearchCode',
13
+ description: 'Search local code',
14
+ schema: { type: 'object', properties: { pattern: { type: 'string' } } },
15
+ hints: { hasResults: ['Use lineHint'], empty: ['Try broader search'] },
16
+ },
17
+ githubSearchCode: {
18
+ name: 'githubSearchCode',
19
+ description: 'Search GitHub code',
20
+ schema: { type: 'object', properties: { keywordsToSearch: { type: 'string' } } },
21
+ hints: { hasResults: ['Check results'], empty: ['Try other keywords'] },
22
+ },
23
+ },
24
+ prompts: {
25
+ research: { name: 'Research', description: 'Research', args: [], content: 'test' },
26
+ plan: { name: 'Plan', description: 'Plan', args: [], content: 'test' },
27
+ },
28
+ instructions: 'System instructions for agent behavior',
29
+ baseHints: ['Always follow hints'],
30
+ genericErrorHints: ['Check inputs'],
31
+ baseSchema: { type: 'object' },
32
+ }),
33
+ initializeMcpContent: vi.fn().mockResolvedValue({}),
34
+ isMcpInitialized: vi.fn().mockReturnValue(true),
35
+ }));
36
+
37
+ vi.mock('../../index.js', () => ({
38
+ localSearchCode: vi.fn().mockResolvedValue({
39
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n files: []\n totalMatches: 0' }],
40
+ }),
41
+ localGetFileContent: vi.fn().mockResolvedValue({
42
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n path: test.ts\n content: "hello"' }],
43
+ }),
44
+ localFindFiles: vi.fn().mockResolvedValue({
45
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n files: []' }],
46
+ }),
47
+ localViewStructure: vi.fn().mockResolvedValue({
48
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n structuredOutput: ""' }],
49
+ }),
50
+ githubSearchCode: vi.fn().mockResolvedValue({
51
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n files: []' }],
52
+ }),
53
+ githubGetFileContent: vi.fn().mockResolvedValue({
54
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n content: "test"' }],
55
+ }),
56
+ githubSearchRepositories: vi.fn().mockResolvedValue({
57
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n repositories: []' }],
58
+ }),
59
+ githubViewRepoStructure: vi.fn().mockResolvedValue({
60
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n structure: {}' }],
61
+ }),
62
+ githubSearchPullRequests: vi.fn().mockResolvedValue({
63
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n pull_requests: []' }],
64
+ }),
65
+ lspGotoDefinition: vi.fn().mockResolvedValue({
66
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n definition: null' }],
67
+ }),
68
+ lspFindReferences: vi.fn().mockResolvedValue({
69
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n references: []' }],
70
+ }),
71
+ lspCallHierarchy: vi.fn().mockResolvedValue({
72
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n calls: []' }],
73
+ }),
74
+ packageSearch: vi.fn().mockResolvedValue({
75
+ content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n packages: []' }],
76
+ }),
77
+ logToolCall: vi.fn().mockResolvedValue(undefined),
78
+ logPromptCall: vi.fn().mockResolvedValue(undefined),
79
+ }));
80
+
81
+ vi.mock('../../utils/resilience.js', () => ({
82
+ withGitHubResilience: vi.fn(async (fn: () => any) => fn()),
83
+ withLocalResilience: vi.fn(async (fn: () => any) => fn()),
84
+ withLspResilience: vi.fn(async (fn: () => any) => fn()),
85
+ withPackageResilience: vi.fn(async (fn: () => any) => fn()),
86
+ }));
87
+
88
+ vi.mock('../../utils/asyncTimeout.js', () => ({
89
+ fireAndForgetWithTimeout: vi.fn(),
90
+ withTimeout: vi.fn(async (fn: () => any) => fn()),
91
+ }));
92
+
93
+ import { toolsRoutes } from '../../routes/tools.js';
94
+
95
+ function createApp(): any {
96
+ const app = express();
97
+ app.use(express.json());
98
+ app.use('/tools', toolsRoutes);
99
+ app.use(errorHandler);
100
+ return app;
101
+ }
102
+
103
+ describe('Tools Routes', () => {
104
+ let app: any;
105
+
106
+ beforeEach(() => {
107
+ app = createApp();
108
+ vi.clearAllMocks();
109
+ });
110
+
111
+ // =========================================================================
112
+ // GET /tools/list
113
+ // =========================================================================
114
+ describe('GET /tools/list', () => {
115
+ it('returns 200 with success true', async () => {
116
+ const res = await request(app).get('/tools/list');
117
+ expect(res.status).toBe(200);
118
+ expect(res.body.success).toBe(true);
119
+ });
120
+
121
+ it('returns all 13 tools', async () => {
122
+ const res = await request(app).get('/tools/list');
123
+ expect(res.body.data.tools).toHaveLength(13);
124
+ });
125
+
126
+ it('each tool has name and description', async () => {
127
+ const res = await request(app).get('/tools/list');
128
+ for (const tool of res.body.data.tools) {
129
+ expect(tool).toHaveProperty('name');
130
+ expect(tool).toHaveProperty('description');
131
+ expect(typeof tool.name).toBe('string');
132
+ expect(typeof tool.description).toBe('string');
133
+ }
134
+ });
135
+
136
+ it('includes all tool categories', async () => {
137
+ const res = await request(app).get('/tools/list');
138
+ const names = res.body.data.tools.map((t: any) => t.name);
139
+ expect(names).toContain('localSearchCode');
140
+ expect(names).toContain('githubSearchCode');
141
+ expect(names).toContain('lspGotoDefinition');
142
+ expect(names).toContain('packageSearch');
143
+ });
144
+
145
+ it('includes hints', async () => {
146
+ const res = await request(app).get('/tools/list');
147
+ expect(res.body.hints).toBeDefined();
148
+ expect(Array.isArray(res.body.hints)).toBe(true);
149
+ });
150
+ });
151
+
152
+ // =========================================================================
153
+ // GET /tools/info
154
+ // =========================================================================
155
+ describe('GET /tools/info', () => {
156
+ it('returns all tools from MCP content', async () => {
157
+ const res = await request(app).get('/tools/info');
158
+ expect(res.status).toBe(200);
159
+ expect(res.body.success).toBe(true);
160
+ expect(res.body.data.toolNames).toContain('localSearchCode');
161
+ });
162
+
163
+ it('includes total count', async () => {
164
+ const res = await request(app).get('/tools/info');
165
+ expect(res.body.data.totalTools).toBeGreaterThan(0);
166
+ });
167
+
168
+ it('excludes schema by default', async () => {
169
+ const res = await request(app).get('/tools/info');
170
+ for (const tool of res.body.data.tools) {
171
+ expect(tool).not.toHaveProperty('schema');
172
+ }
173
+ });
174
+
175
+ it('includes schema when schema=true', async () => {
176
+ const res = await request(app).get('/tools/info').query({ schema: 'true' });
177
+ for (const tool of res.body.data.tools) {
178
+ expect(tool).toHaveProperty('schema');
179
+ }
180
+ });
181
+
182
+ it('includes hints when hints=true', async () => {
183
+ const res = await request(app).get('/tools/info').query({ hints: 'true' });
184
+ expect(res.body.data.baseHints).toBeDefined();
185
+ for (const tool of res.body.data.tools) {
186
+ expect(tool).toHaveProperty('hints');
187
+ }
188
+ });
189
+ });
190
+
191
+ // =========================================================================
192
+ // GET /tools/info/:toolName
193
+ // =========================================================================
194
+ describe('GET /tools/info/:toolName', () => {
195
+ it('returns tool info for valid tool', async () => {
196
+ const res = await request(app).get('/tools/info/localSearchCode');
197
+ expect(res.status).toBe(200);
198
+ expect(res.body.success).toBe(true);
199
+ expect(res.body.data.name).toBe('localSearchCode');
200
+ });
201
+
202
+ it('includes inputSchema by default', async () => {
203
+ const res = await request(app).get('/tools/info/localSearchCode');
204
+ expect(res.body.data).toHaveProperty('inputSchema');
205
+ });
206
+
207
+ it('includes schema source indicator', async () => {
208
+ const res = await request(app).get('/tools/info/localSearchCode');
209
+ expect(res.body.data._schemaSource).toBeDefined();
210
+ });
211
+
212
+ it('includes tool hints by default', async () => {
213
+ const res = await request(app).get('/tools/info/localSearchCode');
214
+ expect(res.body.data).toHaveProperty('toolHints');
215
+ });
216
+
217
+ it('excludes schema when schema=false', async () => {
218
+ const res = await request(app).get('/tools/info/localSearchCode').query({ schema: 'false' });
219
+ expect(res.body.data).not.toHaveProperty('inputSchema');
220
+ });
221
+
222
+ it('excludes hints when hints=false', async () => {
223
+ const res = await request(app).get('/tools/info/localSearchCode').query({ hints: 'false' });
224
+ expect(res.body.data).not.toHaveProperty('toolHints');
225
+ });
226
+
227
+ it('returns 404 for unknown tool', async () => {
228
+ const res = await request(app).get('/tools/info/nonExistentTool');
229
+ expect(res.status).toBe(404);
230
+ expect(res.body.success).toBe(false);
231
+ });
232
+
233
+ it('provides helpful hints for unknown tool', async () => {
234
+ const res = await request(app).get('/tools/info/badTool');
235
+ expect(res.body.hints).toBeDefined();
236
+ expect(res.body.hints.some((h: string) => h.includes('badTool'))).toBe(true);
237
+ expect(res.body.hints.some((h: string) => h.includes('Available tools'))).toBe(true);
238
+ });
239
+ });
240
+
241
+ // =========================================================================
242
+ // GET /tools/metadata
243
+ // =========================================================================
244
+ describe('GET /tools/metadata', () => {
245
+ it('returns metadata summary', async () => {
246
+ const res = await request(app).get('/tools/metadata');
247
+ expect(res.status).toBe(200);
248
+ expect(res.body.success).toBe(true);
249
+ expect(res.body.data).toHaveProperty('instructions');
250
+ expect(res.body.data).toHaveProperty('toolCount');
251
+ expect(res.body.data).toHaveProperty('promptCount');
252
+ expect(res.body.data).toHaveProperty('hasBaseSchema');
253
+ });
254
+
255
+ it('returns correct counts', async () => {
256
+ const res = await request(app).get('/tools/metadata');
257
+ expect(res.body.data.toolCount).toBe(2);
258
+ expect(res.body.data.promptCount).toBe(2);
259
+ });
260
+ });
261
+
262
+ // =========================================================================
263
+ // GET /tools/schemas
264
+ // =========================================================================
265
+ describe('GET /tools/schemas', () => {
266
+ it('returns all tool schemas', async () => {
267
+ const res = await request(app).get('/tools/schemas');
268
+ expect(res.status).toBe(200);
269
+ expect(res.body.success).toBe(true);
270
+ expect(res.body.data).toHaveProperty('totalTools');
271
+ expect(res.body.data).toHaveProperty('schemas');
272
+ });
273
+
274
+ it('has 13 schemas (one per tool)', async () => {
275
+ const res = await request(app).get('/tools/schemas');
276
+ expect(res.body.data.totalTools).toBe(13);
277
+ });
278
+
279
+ it('each schema is a valid JSON Schema (has properties or anyOf)', async () => {
280
+ const res = await request(app).get('/tools/schemas');
281
+ const schemas = res.body.data.schemas;
282
+ for (const [, schema] of Object.entries(schemas) as [string, any][]) {
283
+ expect(schema).toHaveProperty('$schema');
284
+ const hasProperties = schema.properties && Object.keys(schema.properties).length > 0;
285
+ const hasAnyOf = Array.isArray(schema.anyOf) && schema.anyOf.length > 0;
286
+ expect(hasProperties || hasAnyOf).toBe(true);
287
+ }
288
+ });
289
+ });
290
+
291
+ // =========================================================================
292
+ // GET /tools/system
293
+ // =========================================================================
294
+ describe('GET /tools/system', () => {
295
+ it('returns system instructions', async () => {
296
+ const res = await request(app).get('/tools/system');
297
+ expect(res.status).toBe(200);
298
+ expect(res.body.success).toBe(true);
299
+ expect(res.body.data.instructions).toBe('System instructions for agent behavior');
300
+ });
301
+
302
+ it('includes char count and version', async () => {
303
+ const res = await request(app).get('/tools/system');
304
+ expect(res.body.data).toHaveProperty('charCount');
305
+ expect(res.body.data).toHaveProperty('version');
306
+ expect(res.body.data.charCount).toBe('System instructions for agent behavior'.length);
307
+ });
308
+ });
309
+
310
+ // =========================================================================
311
+ // GET /tools/initContext
312
+ // =========================================================================
313
+ describe('GET /tools/initContext', () => {
314
+ it('returns combined system prompt and schemas', async () => {
315
+ const res = await request(app).get('/tools/initContext');
316
+ expect(res.status).toBe(200);
317
+ expect(res.body.success).toBe(true);
318
+ expect(res.body).toHaveProperty('system_prompt');
319
+ expect(res.body).toHaveProperty('tools_schema');
320
+ expect(res.body).toHaveProperty('_meta');
321
+ });
322
+
323
+ it('system_prompt matches /tools/system content', async () => {
324
+ const res = await request(app).get('/tools/initContext');
325
+ expect(res.body.system_prompt).toBe('System instructions for agent behavior');
326
+ });
327
+
328
+ it('tools_schema has all tool schemas', async () => {
329
+ const res = await request(app).get('/tools/initContext');
330
+ const schemaNames = Object.keys(res.body.tools_schema);
331
+ expect(schemaNames.length).toBe(13);
332
+ });
333
+
334
+ it('_meta includes prompt char count and tools count', async () => {
335
+ const res = await request(app).get('/tools/initContext');
336
+ expect(res.body._meta.promptCharCount).toBeGreaterThan(0);
337
+ expect(res.body._meta.toolsCount).toBe(13);
338
+ expect(res.body._meta).toHaveProperty('version');
339
+ });
340
+ });
341
+
342
+ // =========================================================================
343
+ // POST /tools/call/:toolName
344
+ // =========================================================================
345
+ describe('POST /tools/call/:toolName', () => {
346
+ const validLocalQuery = {
347
+ queries: [{
348
+ id: 'q1',
349
+ researchGoal: 'test search',
350
+ reasoning: 'testing',
351
+ pattern: 'hello',
352
+ path: '/test/project',
353
+ }],
354
+ };
355
+
356
+ describe('success path', () => {
357
+ it('executes tool and returns parsed result', async () => {
358
+ const res = await request(app)
359
+ .post('/tools/call/localSearchCode')
360
+ .send(validLocalQuery);
361
+ expect(res.status).toBe(200);
362
+ expect(res.body.tool).toBe('localSearchCode');
363
+ expect(res.body.success).toBe(true);
364
+ expect(res.body).toHaveProperty('data');
365
+ expect(res.body).toHaveProperty('hints');
366
+ });
367
+
368
+ it('returns research context from query', async () => {
369
+ const res = await request(app)
370
+ .post('/tools/call/localSearchCode')
371
+ .send(validLocalQuery);
372
+ expect(res.body).toHaveProperty('research');
373
+ });
374
+ });
375
+
376
+ describe('error handling', () => {
377
+ it('returns 404 for unknown tool', async () => {
378
+ const res = await request(app)
379
+ .post('/tools/call/nonExistentTool')
380
+ .send(validLocalQuery);
381
+ expect(res.status).toBe(404);
382
+ expect(res.body.success).toBe(false);
383
+ expect(res.body.hints.some((h: string) => h.includes('nonExistentTool'))).toBe(true);
384
+ });
385
+
386
+ it('returns 400 for missing body', async () => {
387
+ const res = await request(app)
388
+ .post('/tools/call/localSearchCode')
389
+ .send({});
390
+ expect(res.status).toBe(400);
391
+ expect(res.body.success).toBe(false);
392
+ });
393
+
394
+ it('returns 400 for empty queries array', async () => {
395
+ const res = await request(app)
396
+ .post('/tools/call/localSearchCode')
397
+ .send({ queries: [] });
398
+ expect(res.status).toBe(400);
399
+ expect(res.body.success).toBe(false);
400
+ });
401
+
402
+ it('returns 400 for invalid queries format', async () => {
403
+ const res = await request(app)
404
+ .post('/tools/call/localSearchCode')
405
+ .send({ queries: 'not-an-array' });
406
+ expect(res.status).toBe(400);
407
+ });
408
+
409
+ it('returns 400 for too many queries', async () => {
410
+ const res = await request(app)
411
+ .post('/tools/call/localSearchCode')
412
+ .send({
413
+ queries: [
414
+ { id: 'q1', researchGoal: 't', reasoning: 't', pattern: 'a', path: '/p' },
415
+ { id: 'q2', researchGoal: 't', reasoning: 't', pattern: 'b', path: '/p' },
416
+ { id: 'q3', researchGoal: 't', reasoning: 't', pattern: 'c', path: '/p' },
417
+ { id: 'q4', researchGoal: 't', reasoning: 't', pattern: 'd', path: '/p' },
418
+ ],
419
+ });
420
+ expect(res.status).toBe(400);
421
+ });
422
+
423
+ it('lists available tools in 404 response', async () => {
424
+ const res = await request(app)
425
+ .post('/tools/call/badTool')
426
+ .send(validLocalQuery);
427
+ expect(res.body.hints.some((h: string) => h.includes('localSearchCode'))).toBe(true);
428
+ });
429
+ });
430
+
431
+ describe('bulk queries', () => {
432
+ it('returns bulk format for multiple queries', async () => {
433
+ const res = await request(app)
434
+ .post('/tools/call/localSearchCode')
435
+ .send({
436
+ queries: [
437
+ { id: 'q1', researchGoal: 't', reasoning: 't', pattern: 'a', path: '/p' },
438
+ { id: 'q2', researchGoal: 't', reasoning: 't', pattern: 'b', path: '/p' },
439
+ ],
440
+ });
441
+ expect(res.status).toBe(200);
442
+ expect(res.body.bulk).toBe(true);
443
+ expect(res.body).toHaveProperty('results');
444
+ expect(res.body).toHaveProperty('counts');
445
+ expect(res.body).toHaveProperty('hints');
446
+ });
447
+ });
448
+
449
+ describe('error response from tool', () => {
450
+ it('returns 500 when tool reports error status', async () => {
451
+ const { localSearchCode } = await import('../../index.js');
452
+ vi.mocked(localSearchCode).mockResolvedValueOnce({
453
+ content: [{ type: 'text', text: 'results:\n - status: error\n data:\n message: "Rate limited"' }],
454
+ });
455
+ const res = await request(app)
456
+ .post('/tools/call/localSearchCode')
457
+ .send(validLocalQuery);
458
+ expect(res.status).toBe(500);
459
+ expect(res.body.success).toBe(false);
460
+ });
461
+ });
462
+ });
463
+
464
+ // =========================================================================
465
+ // Readiness (503 when not initialized)
466
+ // =========================================================================
467
+ describe('Readiness gate', () => {
468
+ it('returns 503 for /tools/list when not initialized', async () => {
469
+ const { isMcpInitialized } = await import('../../mcpCache.js');
470
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
471
+
472
+ const res = await request(app).get('/tools/list');
473
+ expect(res.status).toBe(503);
474
+ expect(res.body.error.code).toBe('SERVER_INITIALIZING');
475
+
476
+ vi.mocked(isMcpInitialized).mockReturnValue(true);
477
+ });
478
+
479
+ it('returns 503 for POST /tools/call when not initialized', async () => {
480
+ const { isMcpInitialized } = await import('../../mcpCache.js');
481
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
482
+
483
+ const res = await request(app)
484
+ .post('/tools/call/localSearchCode')
485
+ .send({ queries: [{ id: 'q1', researchGoal: 't', reasoning: 't', pattern: 'a', path: '/p' }] });
486
+ expect(res.status).toBe(503);
487
+
488
+ vi.mocked(isMcpInitialized).mockReturnValue(true);
489
+ });
490
+
491
+ it('returns 503 for /tools/schemas when not initialized', async () => {
492
+ const { isMcpInitialized } = await import('../../mcpCache.js');
493
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
494
+
495
+ const res = await request(app).get('/tools/schemas');
496
+ expect(res.status).toBe(503);
497
+
498
+ vi.mocked(isMcpInitialized).mockReturnValue(true);
499
+ });
500
+ });
501
+ });
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import express from 'express';
3
+ import request from 'supertest';
4
+
5
+ vi.mock('../../mcpCache.js', () => ({
6
+ isMcpInitialized: vi.fn().mockReturnValue(true),
7
+ }));
8
+
9
+ import { checkReadiness } from '../../middleware/readiness.js';
10
+ import { isMcpInitialized } from '../../mcpCache.js';
11
+
12
+ function createApp() {
13
+ const app = express();
14
+ app.use(checkReadiness);
15
+ app.get('/test', (_req, res) => res.json({ ok: true }));
16
+ return app;
17
+ }
18
+
19
+ describe('checkReadiness middleware', () => {
20
+ beforeEach(() => {
21
+ vi.clearAllMocks();
22
+ });
23
+
24
+ it('calls next() when MCP is initialized', async () => {
25
+ vi.mocked(isMcpInitialized).mockReturnValue(true);
26
+ const res = await request(createApp()).get('/test');
27
+ expect(res.status).toBe(200);
28
+ expect(res.body.ok).toBe(true);
29
+ });
30
+
31
+ it('returns 503 when MCP is not initialized', async () => {
32
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
33
+ const res = await request(createApp()).get('/test');
34
+ expect(res.status).toBe(503);
35
+ });
36
+
37
+ it('returns SERVER_INITIALIZING error code', async () => {
38
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
39
+ const res = await request(createApp()).get('/test');
40
+ expect(res.body.error.code).toBe('SERVER_INITIALIZING');
41
+ });
42
+
43
+ it('returns success: false', async () => {
44
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
45
+ const res = await request(createApp()).get('/test');
46
+ expect(res.body.success).toBe(false);
47
+ });
48
+
49
+ it('includes retry hint', async () => {
50
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
51
+ const res = await request(createApp()).get('/test');
52
+ expect(res.body.error.hint).toContain('retry');
53
+ });
54
+
55
+ it('does not call next() when returning 503', async () => {
56
+ vi.mocked(isMcpInitialized).mockReturnValue(false);
57
+ const res = await request(createApp()).get('/test');
58
+ expect(res.status).toBe(503);
59
+ expect(res.body).not.toHaveProperty('ok');
60
+ });
61
+ });