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,192 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+
3
+ vi.mock('../../utils/circuitBreaker.js', () => {
4
+ const circuitStates: Record<string, string> = {};
5
+ return {
6
+ configureCircuit: vi.fn(),
7
+ withCircuitBreaker: vi.fn(async (name: string, fn: () => unknown) => {
8
+ circuitStates[name] = 'used';
9
+ return fn();
10
+ }),
11
+ getCircuitState: vi.fn().mockReturnValue({ state: 'closed', failures: 0 }),
12
+ resetCircuit: vi.fn(),
13
+ getAllCircuitStates: vi.fn().mockReturnValue({}),
14
+ clearAllCircuits: vi.fn(),
15
+ stopCircuitCleanup: vi.fn(),
16
+ CircuitOpenError: class CircuitOpenError extends Error {
17
+ circuitName: string;
18
+ constructor(name: string) { super(`Circuit ${name} is open`); this.circuitName = name; }
19
+ },
20
+ _getUsedCircuits: () => circuitStates,
21
+ };
22
+ });
23
+
24
+ vi.mock('../../utils/retry.js', () => ({
25
+ withRetry: vi.fn(async (fn: () => unknown) => fn()),
26
+ RETRY_CONFIGS: {
27
+ github: { maxAttempts: 3, baseDelayMs: 1000, maxDelayMs: 30000 },
28
+ local: { maxAttempts: 2, baseDelayMs: 200, maxDelayMs: 1000 },
29
+ lsp: { maxAttempts: 3, baseDelayMs: 500, maxDelayMs: 5000 },
30
+ package: { maxAttempts: 3, baseDelayMs: 500, maxDelayMs: 15000 },
31
+ },
32
+ }));
33
+
34
+ vi.mock('../../utils/asyncTimeout.js', () => ({
35
+ fireAndForgetWithTimeout: vi.fn(),
36
+ withTimeout: vi.fn(async (fn: () => unknown) => fn()),
37
+ }));
38
+
39
+ import {
40
+ withGitHubResilience,
41
+ withLocalResilience,
42
+ withLspResilience,
43
+ withPackageResilience,
44
+ } from '../../utils/resilience.js';
45
+ import { withCircuitBreaker } from '../../utils/circuitBreaker.js';
46
+ import { withRetry } from '../../utils/retry.js';
47
+ import { withTimeout } from '../../utils/asyncTimeout.js';
48
+
49
+ describe('Resilience Wrappers', () => {
50
+ beforeEach(() => {
51
+ vi.clearAllMocks();
52
+ });
53
+
54
+ describe('withGitHubResilience', () => {
55
+ it('executes operation successfully', async () => {
56
+ const result = await withGitHubResilience(
57
+ async () => ({ data: 'github-result' }),
58
+ 'githubSearchCode'
59
+ );
60
+ expect(result).toEqual({ data: 'github-result' });
61
+ });
62
+
63
+ it('uses timeout wrapper', async () => {
64
+ await withGitHubResilience(async () => 'ok', 'githubSearchCode');
65
+ expect(withTimeout).toHaveBeenCalled();
66
+ });
67
+
68
+ it('uses circuit breaker', async () => {
69
+ await withGitHubResilience(async () => 'ok', 'githubSearchCode');
70
+ expect(withCircuitBreaker).toHaveBeenCalled();
71
+ });
72
+
73
+ it('uses retry', async () => {
74
+ await withGitHubResilience(async () => 'ok', 'githubSearchCode');
75
+ expect(withRetry).toHaveBeenCalled();
76
+ });
77
+
78
+ it('maps tool to correct circuit', async () => {
79
+ await withGitHubResilience(async () => 'ok', 'githubSearchCode');
80
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
81
+ 'github:search',
82
+ expect.any(Function)
83
+ );
84
+ });
85
+
86
+ it('maps content tools to github:content circuit', async () => {
87
+ await withGitHubResilience(async () => 'ok', 'githubGetFileContent');
88
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
89
+ 'github:content',
90
+ expect.any(Function)
91
+ );
92
+ });
93
+
94
+ it('maps PR tools to github:pulls circuit', async () => {
95
+ await withGitHubResilience(async () => 'ok', 'githubSearchPullRequests');
96
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
97
+ 'github:pulls',
98
+ expect.any(Function)
99
+ );
100
+ });
101
+
102
+ it('propagates errors from operation', async () => {
103
+ vi.mocked(withRetry).mockRejectedValueOnce(new Error('API error'));
104
+ await expect(
105
+ withGitHubResilience(async () => { throw new Error('API error'); }, 'githubSearchCode')
106
+ ).rejects.toThrow('API error');
107
+ });
108
+ });
109
+
110
+ describe('withLocalResilience', () => {
111
+ it('executes local operations', async () => {
112
+ const result = await withLocalResilience(
113
+ async () => ({ files: ['a.ts'] }),
114
+ 'localSearchCode'
115
+ );
116
+ expect(result).toEqual({ files: ['a.ts'] });
117
+ });
118
+
119
+ it('maps local tools to local circuit', async () => {
120
+ await withLocalResilience(async () => 'ok', 'localSearchCode');
121
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
122
+ 'local',
123
+ expect.any(Function)
124
+ );
125
+ });
126
+ });
127
+
128
+ describe('withLspResilience', () => {
129
+ it('executes LSP operations', async () => {
130
+ const result = await withLspResilience(
131
+ async () => ({ definition: 'found' }),
132
+ 'lspGotoDefinition'
133
+ );
134
+ expect(result).toEqual({ definition: 'found' });
135
+ });
136
+
137
+ it('maps navigation tools to lsp:navigation circuit', async () => {
138
+ await withLspResilience(async () => 'ok', 'lspGotoDefinition');
139
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
140
+ 'lsp:navigation',
141
+ expect.any(Function)
142
+ );
143
+ });
144
+
145
+ it('maps hierarchy tools to lsp:hierarchy circuit', async () => {
146
+ await withLspResilience(async () => 'ok', 'lspCallHierarchy');
147
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
148
+ 'lsp:hierarchy',
149
+ expect.any(Function)
150
+ );
151
+ });
152
+ });
153
+
154
+ describe('withPackageResilience', () => {
155
+ it('executes package operations', async () => {
156
+ const result = await withPackageResilience(
157
+ async () => ({ packages: [] }),
158
+ 'packageSearch'
159
+ );
160
+ expect(result).toEqual({ packages: [] });
161
+ });
162
+
163
+ it('maps to package circuit', async () => {
164
+ await withPackageResilience(async () => 'ok', 'packageSearch');
165
+ expect(withCircuitBreaker).toHaveBeenCalledWith(
166
+ 'package',
167
+ expect.any(Function)
168
+ );
169
+ });
170
+ });
171
+
172
+ describe('composition order', () => {
173
+ it('wraps in timeout -> circuit -> retry order', async () => {
174
+ const callOrder: string[] = [];
175
+ vi.mocked(withTimeout).mockImplementation(async (fn) => {
176
+ callOrder.push('timeout');
177
+ return fn();
178
+ });
179
+ vi.mocked(withCircuitBreaker).mockImplementation(async (_name, fn) => {
180
+ callOrder.push('circuit');
181
+ return fn();
182
+ });
183
+ vi.mocked(withRetry).mockImplementation(async (fn) => {
184
+ callOrder.push('retry');
185
+ return fn();
186
+ });
187
+
188
+ await withGitHubResilience(async () => 'ok', 'githubSearchCode');
189
+ expect(callOrder).toEqual(['timeout', 'circuit', 'retry']);
190
+ });
191
+ });
192
+ });
@@ -0,0 +1,172 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ safeString,
4
+ safeNumber,
5
+ safeArray,
6
+ extractMatchLocations,
7
+ transformPagination,
8
+ } from '../../utils/responseFactory.js';
9
+
10
+ describe('safeString', () => {
11
+ it('extracts string property', () => {
12
+ expect(safeString({ name: 'hello' }, 'name')).toBe('hello');
13
+ });
14
+
15
+ it('returns fallback for missing property', () => {
16
+ expect(safeString({}, 'name')).toBe('');
17
+ expect(safeString({}, 'name', 'default')).toBe('default');
18
+ });
19
+
20
+ it('returns fallback for non-string value', () => {
21
+ expect(safeString({ name: 123 }, 'name')).toBe('');
22
+ });
23
+
24
+ it('returns fallback for null input', () => {
25
+ expect(safeString(null, 'name')).toBe('');
26
+ });
27
+
28
+ it('returns fallback for undefined input', () => {
29
+ expect(safeString(undefined, 'name')).toBe('');
30
+ });
31
+
32
+ it('handles empty string value', () => {
33
+ expect(safeString({ name: '' }, 'name')).toBe('');
34
+ });
35
+ });
36
+
37
+ describe('safeNumber', () => {
38
+ it('extracts number property', () => {
39
+ expect(safeNumber({ count: 42 }, 'count')).toBe(42);
40
+ });
41
+
42
+ it('returns fallback for missing property', () => {
43
+ expect(safeNumber({}, 'count')).toBe(0);
44
+ expect(safeNumber({}, 'count', -1)).toBe(-1);
45
+ });
46
+
47
+ it('returns fallback for non-number value', () => {
48
+ expect(safeNumber({ count: 'not-a-number' }, 'count')).toBe(0);
49
+ });
50
+
51
+ it('returns fallback for null input', () => {
52
+ expect(safeNumber(null, 'count')).toBe(0);
53
+ });
54
+
55
+ it('handles zero value', () => {
56
+ expect(safeNumber({ count: 0 }, 'count')).toBe(0);
57
+ });
58
+
59
+ it('handles negative values', () => {
60
+ expect(safeNumber({ count: -5 }, 'count')).toBe(-5);
61
+ });
62
+
63
+ it('handles floating point values', () => {
64
+ expect(safeNumber({ ratio: 3.14 }, 'ratio')).toBe(3.14);
65
+ });
66
+ });
67
+
68
+ describe('safeArray', () => {
69
+ it('extracts array property', () => {
70
+ expect(safeArray({ items: [1, 2, 3] }, 'items')).toEqual([1, 2, 3]);
71
+ });
72
+
73
+ it('returns empty array for missing property', () => {
74
+ expect(safeArray({}, 'items')).toEqual([]);
75
+ });
76
+
77
+ it('returns empty array for non-array value', () => {
78
+ expect(safeArray({ items: 'not-array' }, 'items')).toEqual([]);
79
+ });
80
+
81
+ it('returns empty array for null input', () => {
82
+ expect(safeArray(null, 'items')).toEqual([]);
83
+ });
84
+
85
+ it('handles empty array', () => {
86
+ expect(safeArray({ items: [] }, 'items')).toEqual([]);
87
+ });
88
+
89
+ it('handles object arrays', () => {
90
+ const items = [{ name: 'a' }, { name: 'b' }];
91
+ expect(safeArray({ items }, 'items')).toEqual(items);
92
+ });
93
+ });
94
+
95
+ describe('extractMatchLocations', () => {
96
+ it('extracts line numbers from matches', () => {
97
+ const matches = [
98
+ { line: 10, column: 5, value: 'foo' },
99
+ { line: 20, column: 3, value: 'bar' },
100
+ ];
101
+ const result = extractMatchLocations(matches);
102
+ expect(result).toHaveLength(2);
103
+ expect(result[0].line).toBe(10);
104
+ expect(result[0].column).toBe(5);
105
+ expect(result[0].value).toBe('foo');
106
+ });
107
+
108
+ it('handles matches with byte/char offsets', () => {
109
+ const matches = [{ line: 1, byteOffset: 100, charOffset: 50 }];
110
+ const result = extractMatchLocations(matches);
111
+ expect(result[0].byteOffset).toBe(100);
112
+ expect(result[0].charOffset).toBe(50);
113
+ });
114
+
115
+ it('handles non-object matches', () => {
116
+ const result = extractMatchLocations(['not-an-object', null, undefined]);
117
+ expect(result).toHaveLength(3);
118
+ expect(result[0].line).toBe(0);
119
+ });
120
+
121
+ it('handles empty matches', () => {
122
+ expect(extractMatchLocations([])).toEqual([]);
123
+ });
124
+
125
+ it('trims value strings', () => {
126
+ const matches = [{ line: 1, value: ' hello ' }];
127
+ const result = extractMatchLocations(matches);
128
+ expect(result[0].value).toBe('hello');
129
+ });
130
+
131
+ it('omits optional fields when missing', () => {
132
+ const matches = [{ line: 5 }];
133
+ const result = extractMatchLocations(matches);
134
+ expect(result[0].line).toBe(5);
135
+ expect(result[0].column).toBeUndefined();
136
+ expect(result[0].value).toBeUndefined();
137
+ expect(result[0].byteOffset).toBeUndefined();
138
+ });
139
+ });
140
+
141
+ describe('transformPagination', () => {
142
+ it('transforms valid pagination', () => {
143
+ const result = transformPagination({
144
+ currentPage: 2,
145
+ totalPages: 5,
146
+ hasMore: true,
147
+ });
148
+ expect(result).toEqual({ page: 2, total: 5, hasMore: true });
149
+ });
150
+
151
+ it('returns undefined for non-object input', () => {
152
+ expect(transformPagination(null)).toBeUndefined();
153
+ expect(transformPagination(undefined)).toBeUndefined();
154
+ expect(transformPagination('string')).toBeUndefined();
155
+ expect(transformPagination(42)).toBeUndefined();
156
+ });
157
+
158
+ it('uses defaults for missing fields', () => {
159
+ const result = transformPagination({});
160
+ expect(result).toEqual({ page: 1, total: 1, hasMore: false });
161
+ });
162
+
163
+ it('hasMore is false when not explicitly true', () => {
164
+ const result = transformPagination({ currentPage: 1, totalPages: 1 });
165
+ expect(result?.hasMore).toBe(false);
166
+ });
167
+
168
+ it('hasMore detects boolean true value', () => {
169
+ const result = transformPagination({ hasMore: true });
170
+ expect(result?.hasMore).toBe(true);
171
+ });
172
+ });
@@ -0,0 +1,288 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseToolResponse, parseToolResponseBulk } from '../../utils/responseParser.js';
3
+
4
+ describe('parseToolResponse', () => {
5
+ describe('structuredContent path', () => {
6
+ it('returns structuredContent when available', () => {
7
+ const result = parseToolResponse({
8
+ structuredContent: { files: ['a.ts', 'b.ts'], count: 2 },
9
+ isError: false,
10
+ });
11
+ expect(result.data).toEqual({ files: ['a.ts', 'b.ts'], count: 2 });
12
+ expect(result.isError).toBe(false);
13
+ expect(result.hints).toEqual([]);
14
+ });
15
+
16
+ it('preserves isError from response', () => {
17
+ const result = parseToolResponse({
18
+ structuredContent: { error: 'something' },
19
+ isError: true,
20
+ });
21
+ expect(result.isError).toBe(true);
22
+ });
23
+ });
24
+
25
+ describe('YAML content path', () => {
26
+ it('extracts data from results[0].data', () => {
27
+ const yaml = `results:
28
+ - status: hasResults
29
+ data:
30
+ files:
31
+ - path: test.ts
32
+ totalMatches: 5`;
33
+ const result = parseToolResponse({
34
+ content: [{ type: 'text', text: yaml }],
35
+ });
36
+ expect(result.data.files).toBeDefined();
37
+ expect(result.data.totalMatches).toBe(5);
38
+ expect(result.status).toBe('hasResults');
39
+ expect(result.isError).toBe(false);
40
+ });
41
+
42
+ it('extracts hasResultsStatusHints', () => {
43
+ const yaml = `results:
44
+ - status: hasResults
45
+ data:
46
+ files: []
47
+ hasResultsStatusHints:
48
+ - Use lineHint for LSP
49
+ - Check file paths`;
50
+ const result = parseToolResponse({
51
+ content: [{ type: 'text', text: yaml }],
52
+ });
53
+ expect(result.hints).toEqual(['Use lineHint for LSP', 'Check file paths']);
54
+ });
55
+
56
+ it('extracts emptyStatusHints', () => {
57
+ const yaml = `results:
58
+ - status: empty
59
+ data: {}
60
+ emptyStatusHints:
61
+ - Try broader search`;
62
+ const result = parseToolResponse({
63
+ content: [{ type: 'text', text: yaml }],
64
+ });
65
+ expect(result.hints).toEqual(['Try broader search']);
66
+ expect(result.status).toBe('empty');
67
+ });
68
+
69
+ it('extracts errorStatusHints', () => {
70
+ const yaml = `results:
71
+ - status: error
72
+ data:
73
+ message: Rate limited
74
+ errorStatusHints:
75
+ - Retry after 60s`;
76
+ const result = parseToolResponse({
77
+ content: [{ type: 'text', text: yaml }],
78
+ });
79
+ expect(result.hints).toEqual(['Retry after 60s']);
80
+ expect(result.status).toBe('error');
81
+ expect(result.isError).toBe(true);
82
+ });
83
+
84
+ it('extracts research context', () => {
85
+ const yaml = `results:
86
+ - status: hasResults
87
+ mainResearchGoal: Find auth flow
88
+ researchGoal: Locate middleware
89
+ reasoning: Need to trace
90
+ data:
91
+ files: []`;
92
+ const result = parseToolResponse({
93
+ content: [{ type: 'text', text: yaml }],
94
+ });
95
+ expect(result.research.mainResearchGoal).toBe('Find auth flow');
96
+ expect(result.research.researchGoal).toBe('Locate middleware');
97
+ expect(result.research.reasoning).toBe('Need to trace');
98
+ });
99
+ });
100
+
101
+ describe('fallback paths', () => {
102
+ it('returns parsed YAML when no results array', () => {
103
+ const yaml = `instructions: Do something\nkey: value`;
104
+ const result = parseToolResponse({
105
+ content: [{ type: 'text', text: yaml }],
106
+ });
107
+ expect(result.data).toHaveProperty('instructions');
108
+ expect(result.data).toHaveProperty('key');
109
+ expect(result.status).toBe('unknown');
110
+ });
111
+
112
+ it('returns empty result for invalid YAML', () => {
113
+ const result = parseToolResponse({
114
+ content: [{ type: 'text', text: '{{invalid yaml::' }],
115
+ });
116
+ expect(result.isError).toBe(true);
117
+ expect(result.data).toEqual({});
118
+ });
119
+
120
+ it('returns empty result when no content', () => {
121
+ const result = parseToolResponse({});
122
+ expect(result.isError).toBe(true);
123
+ expect(result.data).toEqual({});
124
+ expect(result.hints).toEqual([]);
125
+ });
126
+
127
+ it('returns empty result for empty content array', () => {
128
+ const result = parseToolResponse({ content: [] });
129
+ expect(result.isError).toBe(true);
130
+ });
131
+
132
+ it('handles content with no text', () => {
133
+ const result = parseToolResponse({ content: [{ type: 'text' }] });
134
+ expect(result.isError).toBe(true);
135
+ });
136
+
137
+ it('handles results with non-object data', () => {
138
+ const yaml = `results:
139
+ - status: hasResults
140
+ data: just-a-string`;
141
+ const result = parseToolResponse({
142
+ content: [{ type: 'text', text: yaml }],
143
+ });
144
+ expect(result.status).toBe('unknown');
145
+ });
146
+ });
147
+ });
148
+
149
+ describe('parseToolResponseBulk', () => {
150
+ it('parses multiple results', () => {
151
+ const yaml = `results:
152
+ - id: 1
153
+ status: hasResults
154
+ data:
155
+ files: [a.ts]
156
+ - id: 2
157
+ status: empty
158
+ data: {}
159
+ - id: 3
160
+ status: error
161
+ data:
162
+ message: failed
163
+ hasResultsStatusHints:
164
+ - Got results
165
+ emptyStatusHints:
166
+ - No results
167
+ errorStatusHints:
168
+ - Error occurred
169
+ instructions: Process all results`;
170
+ const result = parseToolResponseBulk({
171
+ content: [{ type: 'text', text: yaml }],
172
+ });
173
+ expect(result.results).toHaveLength(3);
174
+ expect(result.counts).toEqual({ total: 3, hasResults: 1, empty: 1, error: 1 });
175
+ expect(result.isError).toBe(false);
176
+ expect(result.instructions).toBe('Process all results');
177
+ });
178
+
179
+ it('categorizes hints by status', () => {
180
+ const yaml = `results:
181
+ - status: hasResults
182
+ data: {}
183
+ hasResultsStatusHints:
184
+ - hint-a
185
+ emptyStatusHints:
186
+ - hint-b
187
+ errorStatusHints:
188
+ - hint-c`;
189
+ const result = parseToolResponseBulk({
190
+ content: [{ type: 'text', text: yaml }],
191
+ });
192
+ expect(result.hints.hasResults).toEqual(['hint-a']);
193
+ expect(result.hints.empty).toEqual(['hint-b']);
194
+ expect(result.hints.error).toEqual(['hint-c']);
195
+ });
196
+
197
+ it('marks isError true when all results are errors', () => {
198
+ const yaml = `results:
199
+ - id: 1
200
+ status: error
201
+ data:
202
+ message: fail1
203
+ - id: 2
204
+ status: error
205
+ data:
206
+ message: fail2`;
207
+ const result = parseToolResponseBulk({
208
+ content: [{ type: 'text', text: yaml }],
209
+ });
210
+ expect(result.isError).toBe(true);
211
+ expect(result.counts.error).toBe(2);
212
+ });
213
+
214
+ it('marks isError false when some succeed', () => {
215
+ const yaml = `results:
216
+ - status: hasResults
217
+ data: {}
218
+ - status: error
219
+ data: {}`;
220
+ const result = parseToolResponseBulk({
221
+ content: [{ type: 'text', text: yaml }],
222
+ });
223
+ expect(result.isError).toBe(false);
224
+ });
225
+
226
+ it('extracts research context per result', () => {
227
+ const yaml = `results:
228
+ - status: hasResults
229
+ mainResearchGoal: goal1
230
+ researchGoal: sub1
231
+ reasoning: reason1
232
+ data:
233
+ files: []`;
234
+ const result = parseToolResponseBulk({
235
+ content: [{ type: 'text', text: yaml }],
236
+ });
237
+ expect(result.results[0].research.mainResearchGoal).toBe('goal1');
238
+ expect(result.results[0].research.researchGoal).toBe('sub1');
239
+ });
240
+
241
+ it('returns empty for no content', () => {
242
+ const result = parseToolResponseBulk({});
243
+ expect(result.results).toEqual([]);
244
+ expect(result.isError).toBe(true);
245
+ expect(result.counts.total).toBe(0);
246
+ });
247
+
248
+ it('returns empty for invalid YAML', () => {
249
+ const result = parseToolResponseBulk({
250
+ content: [{ type: 'text', text: '{{bad yaml' }],
251
+ });
252
+ expect(result.results).toEqual([]);
253
+ expect(result.isError).toBe(true);
254
+ });
255
+
256
+ it('returns empty for missing results array', () => {
257
+ const result = parseToolResponseBulk({
258
+ content: [{ type: 'text', text: 'instructions: test' }],
259
+ });
260
+ expect(result.results).toEqual([]);
261
+ expect(result.isError).toBe(true);
262
+ });
263
+
264
+ it('auto-assigns ids when missing', () => {
265
+ const yaml = `results:
266
+ - status: hasResults
267
+ data: {}
268
+ - status: hasResults
269
+ data: {}`;
270
+ const result = parseToolResponseBulk({
271
+ content: [{ type: 'text', text: yaml }],
272
+ });
273
+ expect(result.results[0].id).toBe(1);
274
+ expect(result.results[1].id).toBe(2);
275
+ });
276
+
277
+ it('skips non-object results', () => {
278
+ const yaml = `results:
279
+ - status: hasResults
280
+ data: {}
281
+ - null
282
+ - just-a-string`;
283
+ const result = parseToolResponseBulk({
284
+ content: [{ type: 'text', text: yaml }],
285
+ });
286
+ expect(result.results).toHaveLength(1);
287
+ });
288
+ });