sofia-cli 0.1.1

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 (435) hide show
  1. package/.github/agents/copilot-instructions.md +39 -0
  2. package/.github/agents/speckit.analyze.agent.md +184 -0
  3. package/.github/agents/speckit.checklist.agent.md +294 -0
  4. package/.github/agents/speckit.clarify.agent.md +181 -0
  5. package/.github/agents/speckit.constitution.agent.md +84 -0
  6. package/.github/agents/speckit.implement.agent.md +135 -0
  7. package/.github/agents/speckit.plan.agent.md +90 -0
  8. package/.github/agents/speckit.specify.agent.md +258 -0
  9. package/.github/agents/speckit.tasks.agent.md +137 -0
  10. package/.github/agents/speckit.taskstoissues.agent.md +30 -0
  11. package/.github/copilot-instructions.md +257 -0
  12. package/.github/prompts/speckit.analyze.prompt.md +3 -0
  13. package/.github/prompts/speckit.checklist.prompt.md +3 -0
  14. package/.github/prompts/speckit.clarify.prompt.md +3 -0
  15. package/.github/prompts/speckit.constitution.prompt.md +3 -0
  16. package/.github/prompts/speckit.implement.prompt.md +3 -0
  17. package/.github/prompts/speckit.plan.prompt.md +3 -0
  18. package/.github/prompts/speckit.specify.prompt.md +3 -0
  19. package/.github/prompts/speckit.tasks.prompt.md +3 -0
  20. package/.github/prompts/speckit.taskstoissues.prompt.md +3 -0
  21. package/.github/workflows/ci.yml +38 -0
  22. package/.prettierrc +6 -0
  23. package/.specify/memory/constitution.md +181 -0
  24. package/.specify/scripts/bash/check-prerequisites.sh +166 -0
  25. package/.specify/scripts/bash/common.sh +156 -0
  26. package/.specify/scripts/bash/create-new-feature.sh +297 -0
  27. package/.specify/scripts/bash/setup-plan.sh +61 -0
  28. package/.specify/scripts/bash/update-agent-context.sh +810 -0
  29. package/.specify/templates/agent-file-template.md +28 -0
  30. package/.specify/templates/checklist-template.md +40 -0
  31. package/.specify/templates/constitution-template.md +50 -0
  32. package/.specify/templates/plan-template.md +113 -0
  33. package/.specify/templates/spec-template.md +115 -0
  34. package/.specify/templates/tasks-template.md +251 -0
  35. package/.vscode/mcp.json +42 -0
  36. package/.vscode/settings.json +19 -0
  37. package/CODE_OF_CONDUCT.md +128 -0
  38. package/LICENSE +21 -0
  39. package/README.md +213 -0
  40. package/dist/src/cli/developCommand.js +240 -0
  41. package/dist/src/cli/directCommands.js +143 -0
  42. package/dist/src/cli/envLoader.js +16 -0
  43. package/dist/src/cli/exportCommand.js +53 -0
  44. package/dist/src/cli/index.js +203 -0
  45. package/dist/src/cli/ioContext.js +109 -0
  46. package/dist/src/cli/preflight.js +57 -0
  47. package/dist/src/cli/statusCommand.js +110 -0
  48. package/dist/src/cli/workshopCommand.js +400 -0
  49. package/dist/src/develop/checkpointState.js +86 -0
  50. package/dist/src/develop/codeGenerator.js +319 -0
  51. package/dist/src/develop/dynamicScaffolder.js +226 -0
  52. package/dist/src/develop/githubMcpAdapter.js +122 -0
  53. package/dist/src/develop/index.js +15 -0
  54. package/dist/src/develop/mcpContextEnricher.js +195 -0
  55. package/dist/src/develop/pocScaffolder.js +542 -0
  56. package/dist/src/develop/ralphLoop.js +659 -0
  57. package/dist/src/develop/templateRegistry.js +364 -0
  58. package/dist/src/develop/testRunner.js +202 -0
  59. package/dist/src/logging/logger.js +58 -0
  60. package/dist/src/loop/conversationLoop.js +227 -0
  61. package/dist/src/loop/phaseSummarizer.js +87 -0
  62. package/dist/src/mcp/mcpManager.js +267 -0
  63. package/dist/src/mcp/mcpTransport.js +391 -0
  64. package/dist/src/mcp/retryPolicy.js +47 -0
  65. package/dist/src/mcp/webSearch.js +254 -0
  66. package/dist/src/phases/contextSummarizer.js +101 -0
  67. package/dist/src/phases/discoveryEnricher.js +156 -0
  68. package/dist/src/phases/phaseExtractors.js +222 -0
  69. package/dist/src/phases/phaseHandlers.js +328 -0
  70. package/dist/src/prompts/design.md +51 -0
  71. package/dist/src/prompts/develop-boundary.md +51 -0
  72. package/dist/src/prompts/develop.md +111 -0
  73. package/dist/src/prompts/discover.md +58 -0
  74. package/dist/src/prompts/ideate.md +56 -0
  75. package/dist/src/prompts/plan.md +51 -0
  76. package/dist/src/prompts/promptLoader.js +167 -0
  77. package/dist/src/prompts/promptLoader.ts +198 -0
  78. package/dist/src/prompts/select.md +47 -0
  79. package/dist/src/prompts/summarize/README.md +8 -0
  80. package/dist/src/prompts/summarize/design-summary.md +37 -0
  81. package/dist/src/prompts/summarize/develop-summary.md +25 -0
  82. package/dist/src/prompts/summarize/ideate-summary.md +27 -0
  83. package/dist/src/prompts/summarize/plan-summary.md +27 -0
  84. package/dist/src/prompts/summarize/select-summary.md +21 -0
  85. package/dist/src/prompts/system.md +28 -0
  86. package/dist/src/sessions/exportPaths.js +22 -0
  87. package/dist/src/sessions/exportWriter.js +406 -0
  88. package/dist/src/sessions/sessionManager.js +81 -0
  89. package/dist/src/sessions/sessionStore.js +65 -0
  90. package/dist/src/shared/activitySpinner.js +91 -0
  91. package/dist/src/shared/copilotClient.js +129 -0
  92. package/dist/src/shared/data/cards.json +1249 -0
  93. package/dist/src/shared/data/cardsLoader.js +51 -0
  94. package/dist/src/shared/errorClassifier.js +120 -0
  95. package/dist/src/shared/events.js +28 -0
  96. package/dist/src/shared/markdownRenderer.js +34 -0
  97. package/dist/src/shared/schemas/session.js +265 -0
  98. package/dist/src/shared/tableRenderer.js +20 -0
  99. package/dist/src/vendor/chalk.js +2 -0
  100. package/dist/src/vendor/cli-table3.js +3 -0
  101. package/dist/src/vendor/commander.js +2 -0
  102. package/dist/src/vendor/marked-terminal.js +3 -0
  103. package/dist/src/vendor/marked.js +2 -0
  104. package/dist/src/vendor/ora.js +2 -0
  105. package/dist/src/vendor/pino.js +2 -0
  106. package/dist/src/vendor/zod.js +2 -0
  107. package/dist/tests/e2e/developE2e.spec.js +126 -0
  108. package/dist/tests/e2e/developFailureE2e.spec.js +247 -0
  109. package/dist/tests/e2e/developPty.spec.js +75 -0
  110. package/dist/tests/e2e/discoveryWebSearchRelevance.spec.js +84 -0
  111. package/dist/tests/e2e/harness.spec.js +83 -0
  112. package/dist/tests/e2e/mcpLive.spec.js +120 -0
  113. package/dist/tests/e2e/newSession.e2e.spec.js +177 -0
  114. package/dist/tests/e2e/ralphLoopEnrichmentComparison.spec.js +62 -0
  115. package/dist/tests/e2e/workiqEnrichment.spec.js +56 -0
  116. package/dist/tests/e2e/zavaSimulation.spec.js +452 -0
  117. package/dist/tests/fixtures/test-fixture-project/src/add.js +3 -0
  118. package/dist/tests/fixtures/test-fixture-project/tests/failing.test.js +6 -0
  119. package/dist/tests/fixtures/test-fixture-project/tests/hanging.test.js +8 -0
  120. package/dist/tests/fixtures/test-fixture-project/tests/passing.test.js +10 -0
  121. package/dist/tests/fixtures/test-fixture-project/vitest.config.js +6 -0
  122. package/dist/tests/integration/autoStartConversation.spec.js +138 -0
  123. package/dist/tests/integration/defaultCommand.spec.js +147 -0
  124. package/dist/tests/integration/directCommandNonTty.spec.js +224 -0
  125. package/dist/tests/integration/directCommandTty.spec.js +151 -0
  126. package/dist/tests/integration/discoveryEnrichmentFlow.spec.js +175 -0
  127. package/dist/tests/integration/exportArtifacts.spec.js +202 -0
  128. package/dist/tests/integration/exportFallbackFlow.spec.js +99 -0
  129. package/dist/tests/integration/mcpDegradationFlow.spec.js +190 -0
  130. package/dist/tests/integration/mcpTransportFlow.spec.js +139 -0
  131. package/dist/tests/integration/newSessionFlow.spec.js +343 -0
  132. package/dist/tests/integration/pocGithubMcp.spec.js +186 -0
  133. package/dist/tests/integration/pocLocalFallback.spec.js +171 -0
  134. package/dist/tests/integration/pocScaffold.spec.js +163 -0
  135. package/dist/tests/integration/ralphLoopFlow.spec.js +359 -0
  136. package/dist/tests/integration/ralphLoopPartial.spec.js +368 -0
  137. package/dist/tests/integration/resumeAndBacktrack.spec.js +247 -0
  138. package/dist/tests/integration/spinnerLifecycle.spec.js +220 -0
  139. package/dist/tests/integration/summarizationFlow.spec.js +115 -0
  140. package/dist/tests/integration/testRunnerReal.spec.js +52 -0
  141. package/dist/tests/integration/webSearchAgent.spec.js +128 -0
  142. package/dist/tests/live/copilotSdkLive.spec.js +107 -0
  143. package/dist/tests/live/zavaFullWorkshop.spec.js +392 -0
  144. package/dist/tests/setup/loadEnv.js +3 -0
  145. package/dist/tests/unit/cli/developCommand.spec.js +567 -0
  146. package/dist/tests/unit/cli/directCommands.spec.js +279 -0
  147. package/dist/tests/unit/cli/envLoader.spec.js +58 -0
  148. package/dist/tests/unit/cli/ioContext.spec.js +119 -0
  149. package/dist/tests/unit/cli/preflight.spec.js +108 -0
  150. package/dist/tests/unit/cli/statusCommand.spec.js +111 -0
  151. package/dist/tests/unit/cli/workshopClientFallback.spec.js +80 -0
  152. package/dist/tests/unit/cli/workshopCommand.spec.js +329 -0
  153. package/dist/tests/unit/config/vitestEnvSetup.spec.js +13 -0
  154. package/dist/tests/unit/develop/checkpointState.spec.js +315 -0
  155. package/dist/tests/unit/develop/codeGenerator.spec.js +355 -0
  156. package/dist/tests/unit/develop/githubMcpAdapter.spec.js +231 -0
  157. package/dist/tests/unit/develop/mcpContextEnricher.spec.js +433 -0
  158. package/dist/tests/unit/develop/outputValidator.spec.js +119 -0
  159. package/dist/tests/unit/develop/pocScaffolder.spec.js +353 -0
  160. package/dist/tests/unit/develop/ralphLoop.spec.js +1248 -0
  161. package/dist/tests/unit/develop/templateRegistry.spec.js +85 -0
  162. package/dist/tests/unit/develop/testRunner.spec.js +249 -0
  163. package/dist/tests/unit/infraBicep.spec.js +92 -0
  164. package/dist/tests/unit/infraDeploy.spec.js +82 -0
  165. package/dist/tests/unit/infraTeardown.spec.js +63 -0
  166. package/dist/tests/unit/logging/logger.spec.js +43 -0
  167. package/dist/tests/unit/loop/conversationLoop.spec.js +592 -0
  168. package/dist/tests/unit/loop/phaseSummarizer.spec.js +141 -0
  169. package/dist/tests/unit/loop/streamingMarkdown.spec.js +147 -0
  170. package/dist/tests/unit/mcp/mcpManager.spec.js +279 -0
  171. package/dist/tests/unit/mcp/mcpTransport.spec.js +529 -0
  172. package/dist/tests/unit/mcp/retryPolicy.spec.js +218 -0
  173. package/dist/tests/unit/mcp/timeoutValidation.spec.js +46 -0
  174. package/dist/tests/unit/mcp/webSearch.spec.js +567 -0
  175. package/dist/tests/unit/phases/contextSummarizer.spec.js +140 -0
  176. package/dist/tests/unit/phases/discoveryEnricher.repeatCalls.spec.js +93 -0
  177. package/dist/tests/unit/phases/discoveryEnricher.spec.js +411 -0
  178. package/dist/tests/unit/phases/phaseExtractors.spec.js +352 -0
  179. package/dist/tests/unit/phases/phaseHandlers.spec.js +425 -0
  180. package/dist/tests/unit/prompts/promptLoader.spec.js +118 -0
  181. package/dist/tests/unit/schemas/pocSchemas.spec.js +412 -0
  182. package/dist/tests/unit/schemas/session.spec.js +257 -0
  183. package/dist/tests/unit/sessions/exportPaths.spec.js +31 -0
  184. package/dist/tests/unit/sessions/exportWriter.spec.js +655 -0
  185. package/dist/tests/unit/sessions/sessionManager.spec.js +151 -0
  186. package/dist/tests/unit/sessions/sessionStore.spec.js +116 -0
  187. package/dist/tests/unit/shared/activitySpinner.spec.js +175 -0
  188. package/dist/tests/unit/shared/cardsLoader.spec.js +76 -0
  189. package/dist/tests/unit/shared/copilotClient.spec.js +155 -0
  190. package/dist/tests/unit/shared/errorClassifier.spec.js +131 -0
  191. package/dist/tests/unit/shared/events.spec.js +55 -0
  192. package/dist/tests/unit/shared/markdownRenderer.spec.js +35 -0
  193. package/dist/tests/unit/shared/markdownRendererChunks.spec.js +70 -0
  194. package/dist/tests/unit/shared/tableRenderer.spec.js +34 -0
  195. package/dist/vitest.config.js +14 -0
  196. package/dist/vitest.live.config.js +18 -0
  197. package/docs/README.md +35 -0
  198. package/docs/architecture.md +169 -0
  199. package/docs/cli-usage.md +207 -0
  200. package/docs/environment.md +66 -0
  201. package/docs/export-format.md +146 -0
  202. package/docs/session-model.md +113 -0
  203. package/eslint.config.js +35 -0
  204. package/infra/deploy.sh +193 -0
  205. package/infra/gather-env.sh +211 -0
  206. package/infra/main.bicep +90 -0
  207. package/infra/main.bicepparam +18 -0
  208. package/infra/resources.bicep +134 -0
  209. package/infra/teardown.sh +114 -0
  210. package/package.json +63 -0
  211. package/specs/001-cli-workshop-rebuild/checklists/requirements.md +35 -0
  212. package/specs/001-cli-workshop-rebuild/contracts/cli.md +59 -0
  213. package/specs/001-cli-workshop-rebuild/contracts/export-summary-json.md +23 -0
  214. package/specs/001-cli-workshop-rebuild/contracts/session-json.md +30 -0
  215. package/specs/001-cli-workshop-rebuild/data-model.md +210 -0
  216. package/specs/001-cli-workshop-rebuild/plan.md +361 -0
  217. package/specs/001-cli-workshop-rebuild/quickstart.md +83 -0
  218. package/specs/001-cli-workshop-rebuild/research.md +116 -0
  219. package/specs/001-cli-workshop-rebuild/spec.md +240 -0
  220. package/specs/001-cli-workshop-rebuild/tasks.md +476 -0
  221. package/specs/002-poc-generation/contracts/poc-output.md +172 -0
  222. package/specs/002-poc-generation/contracts/ralph-loop.md +113 -0
  223. package/specs/002-poc-generation/data-model.md +172 -0
  224. package/specs/002-poc-generation/plan.md +109 -0
  225. package/specs/002-poc-generation/quickstart.md +97 -0
  226. package/specs/002-poc-generation/research.md +786 -0
  227. package/specs/002-poc-generation/spec.md +81 -0
  228. package/specs/002-poc-generation/tasks-fix.md +198 -0
  229. package/specs/002-poc-generation/tasks.md +252 -0
  230. package/specs/003-mcp-transport-integration/checklists/requirements.md +37 -0
  231. package/specs/003-mcp-transport-integration/contracts/context-enricher.md +220 -0
  232. package/specs/003-mcp-transport-integration/contracts/discovery-enricher.md +267 -0
  233. package/specs/003-mcp-transport-integration/contracts/github-adapter.md +149 -0
  234. package/specs/003-mcp-transport-integration/contracts/mcp-transport.md +288 -0
  235. package/specs/003-mcp-transport-integration/data-model.md +326 -0
  236. package/specs/003-mcp-transport-integration/plan.md +114 -0
  237. package/specs/003-mcp-transport-integration/quickstart.md +311 -0
  238. package/specs/003-mcp-transport-integration/research.md +395 -0
  239. package/specs/003-mcp-transport-integration/spec.md +234 -0
  240. package/specs/003-mcp-transport-integration/tasks.md +324 -0
  241. package/specs/003-next-spec-gaps.md +150 -0
  242. package/specs/004-dev-resume-hardening/checklists/requirements.md +37 -0
  243. package/specs/004-dev-resume-hardening/contracts/cli.md +160 -0
  244. package/specs/004-dev-resume-hardening/data-model.md +321 -0
  245. package/specs/004-dev-resume-hardening/plan.md +107 -0
  246. package/specs/004-dev-resume-hardening/quickstart.md +115 -0
  247. package/specs/004-dev-resume-hardening/research.md +142 -0
  248. package/specs/004-dev-resume-hardening/spec.md +221 -0
  249. package/specs/004-dev-resume-hardening/tasks.md +333 -0
  250. package/specs/005-ai-search-deploy/checklists/requirements.md +39 -0
  251. package/specs/005-ai-search-deploy/contracts/web-search-tool.md +241 -0
  252. package/specs/005-ai-search-deploy/data-model.md +130 -0
  253. package/specs/005-ai-search-deploy/plan.md +93 -0
  254. package/specs/005-ai-search-deploy/quickstart.md +96 -0
  255. package/specs/005-ai-search-deploy/research.md +187 -0
  256. package/specs/005-ai-search-deploy/spec.md +143 -0
  257. package/specs/005-ai-search-deploy/tasks.md +284 -0
  258. package/specs/006-workshop-extraction-fixes/checklists/requirements.md +61 -0
  259. package/specs/006-workshop-extraction-fixes/contracts/summarization-and-export.md +131 -0
  260. package/specs/006-workshop-extraction-fixes/data-model.md +149 -0
  261. package/specs/006-workshop-extraction-fixes/plan.md +123 -0
  262. package/specs/006-workshop-extraction-fixes/quickstart.md +101 -0
  263. package/specs/006-workshop-extraction-fixes/research.md +143 -0
  264. package/specs/006-workshop-extraction-fixes/spec.md +210 -0
  265. package/specs/006-workshop-extraction-fixes/tasks.md +316 -0
  266. package/src/cli/developCommand.ts +308 -0
  267. package/src/cli/directCommands.ts +195 -0
  268. package/src/cli/envLoader.ts +17 -0
  269. package/src/cli/exportCommand.ts +65 -0
  270. package/src/cli/index.ts +249 -0
  271. package/src/cli/ioContext.ts +139 -0
  272. package/src/cli/preflight.ts +86 -0
  273. package/src/cli/statusCommand.ts +118 -0
  274. package/src/cli/workshopCommand.ts +496 -0
  275. package/src/develop/checkpointState.ts +121 -0
  276. package/src/develop/codeGenerator.ts +402 -0
  277. package/src/develop/dynamicScaffolder.ts +284 -0
  278. package/src/develop/githubMcpAdapter.ts +199 -0
  279. package/src/develop/index.ts +34 -0
  280. package/src/develop/mcpContextEnricher.ts +279 -0
  281. package/src/develop/pocScaffolder.ts +646 -0
  282. package/src/develop/ralphLoop.ts +1044 -0
  283. package/src/develop/templateRegistry.ts +427 -0
  284. package/src/develop/testRunner.ts +276 -0
  285. package/src/logging/logger.ts +73 -0
  286. package/src/loop/conversationLoop.ts +355 -0
  287. package/src/loop/phaseSummarizer.ts +114 -0
  288. package/src/mcp/mcpManager.ts +365 -0
  289. package/src/mcp/mcpTransport.ts +562 -0
  290. package/src/mcp/retryPolicy.ts +87 -0
  291. package/src/mcp/webSearch.ts +388 -0
  292. package/src/originalPrompts/design_thinking.md +178 -0
  293. package/src/originalPrompts/design_thinking_persona.md +76 -0
  294. package/src/originalPrompts/document_generator_example.md +77 -0
  295. package/src/originalPrompts/document_generator_persona.md +47 -0
  296. package/src/originalPrompts/facilitator_persona.md +125 -0
  297. package/src/originalPrompts/guardrails.md +47 -0
  298. package/src/phases/contextSummarizer.ts +154 -0
  299. package/src/phases/discoveryEnricher.ts +223 -0
  300. package/src/phases/phaseExtractors.ts +247 -0
  301. package/src/phases/phaseHandlers.ts +450 -0
  302. package/src/prompts/design.md +51 -0
  303. package/src/prompts/develop-boundary.md +51 -0
  304. package/src/prompts/develop.md +111 -0
  305. package/src/prompts/discover.md +58 -0
  306. package/src/prompts/ideate.md +56 -0
  307. package/src/prompts/plan.md +51 -0
  308. package/src/prompts/promptLoader.ts +198 -0
  309. package/src/prompts/select.md +47 -0
  310. package/src/prompts/summarize/README.md +8 -0
  311. package/src/prompts/summarize/design-summary.md +37 -0
  312. package/src/prompts/summarize/develop-summary.md +25 -0
  313. package/src/prompts/summarize/ideate-summary.md +27 -0
  314. package/src/prompts/summarize/plan-summary.md +27 -0
  315. package/src/prompts/summarize/select-summary.md +21 -0
  316. package/src/prompts/system.md +28 -0
  317. package/src/sessions/exportPaths.ts +28 -0
  318. package/src/sessions/exportWriter.ts +490 -0
  319. package/src/sessions/sessionManager.ts +119 -0
  320. package/src/sessions/sessionStore.ts +69 -0
  321. package/src/shared/activitySpinner.ts +108 -0
  322. package/src/shared/copilotClient.ts +291 -0
  323. package/src/shared/data/cards.json +1249 -0
  324. package/src/shared/data/cardsLoader.ts +70 -0
  325. package/src/shared/errorClassifier.ts +160 -0
  326. package/src/shared/events.ts +103 -0
  327. package/src/shared/markdownRenderer.ts +44 -0
  328. package/src/shared/schemas/session.ts +346 -0
  329. package/src/shared/tableRenderer.ts +28 -0
  330. package/src/types/marked-terminal.d.ts +5 -0
  331. package/src/vendor/chalk.ts +2 -0
  332. package/src/vendor/cli-table3.ts +3 -0
  333. package/src/vendor/commander.ts +2 -0
  334. package/src/vendor/marked-terminal.ts +3 -0
  335. package/src/vendor/marked.ts +2 -0
  336. package/src/vendor/ora.ts +2 -0
  337. package/src/vendor/pino.ts +3 -0
  338. package/src/vendor/zod.ts +3 -0
  339. package/tests/e2e/developE2e.spec.ts +152 -0
  340. package/tests/e2e/developFailureE2e.spec.ts +289 -0
  341. package/tests/e2e/developPty.spec.ts +86 -0
  342. package/tests/e2e/discoveryWebSearchRelevance.spec.ts +103 -0
  343. package/tests/e2e/harness.spec.ts +104 -0
  344. package/tests/e2e/mcpLive.spec.ts +149 -0
  345. package/tests/e2e/newSession.e2e.spec.ts +245 -0
  346. package/tests/e2e/ralphLoopEnrichmentComparison.spec.ts +70 -0
  347. package/tests/e2e/workiqEnrichment.spec.ts +72 -0
  348. package/tests/e2e/zava-assessment/agent-interaction-script.md +258 -0
  349. package/tests/e2e/zava-assessment/company-profile.md +98 -0
  350. package/tests/e2e/zava-assessment/expected-results-checklist.md +454 -0
  351. package/tests/e2e/zavaSimulation.spec.ts +511 -0
  352. package/tests/fixtures/completedSession.json +141 -0
  353. package/tests/fixtures/test-fixture-project/package-lock.json +1585 -0
  354. package/tests/fixtures/test-fixture-project/package.json +12 -0
  355. package/tests/fixtures/test-fixture-project/src/add.ts +3 -0
  356. package/tests/fixtures/test-fixture-project/tests/failing.test.ts +7 -0
  357. package/tests/fixtures/test-fixture-project/tests/hanging.test.ts +9 -0
  358. package/tests/fixtures/test-fixture-project/tests/passing.test.ts +13 -0
  359. package/tests/fixtures/test-fixture-project/vitest.config.ts +7 -0
  360. package/tests/integration/autoStartConversation.spec.ts +168 -0
  361. package/tests/integration/defaultCommand.spec.ts +179 -0
  362. package/tests/integration/directCommandNonTty.spec.ts +260 -0
  363. package/tests/integration/directCommandTty.spec.ts +185 -0
  364. package/tests/integration/discoveryEnrichmentFlow.spec.ts +209 -0
  365. package/tests/integration/exportArtifacts.spec.ts +232 -0
  366. package/tests/integration/exportFallbackFlow.spec.ts +115 -0
  367. package/tests/integration/mcpDegradationFlow.spec.ts +231 -0
  368. package/tests/integration/mcpTransportFlow.spec.ts +178 -0
  369. package/tests/integration/newSessionFlow.spec.ts +406 -0
  370. package/tests/integration/pocGithubMcp.spec.ts +224 -0
  371. package/tests/integration/pocLocalFallback.spec.ts +205 -0
  372. package/tests/integration/pocScaffold.spec.ts +220 -0
  373. package/tests/integration/ralphLoopFlow.spec.ts +430 -0
  374. package/tests/integration/ralphLoopPartial.spec.ts +416 -0
  375. package/tests/integration/resumeAndBacktrack.spec.ts +278 -0
  376. package/tests/integration/spinnerLifecycle.spec.ts +270 -0
  377. package/tests/integration/summarizationFlow.spec.ts +135 -0
  378. package/tests/integration/testRunnerReal.spec.ts +63 -0
  379. package/tests/integration/webSearchAgent.spec.ts +155 -0
  380. package/tests/live/copilotSdkLive.spec.ts +149 -0
  381. package/tests/live/zavaFullWorkshop.spec.ts +515 -0
  382. package/tests/setup/loadEnv.ts +5 -0
  383. package/tests/unit/cli/developCommand.spec.ts +679 -0
  384. package/tests/unit/cli/directCommands.spec.ts +325 -0
  385. package/tests/unit/cli/envLoader.spec.ts +73 -0
  386. package/tests/unit/cli/ioContext.spec.ts +148 -0
  387. package/tests/unit/cli/preflight.spec.ts +125 -0
  388. package/tests/unit/cli/statusCommand.spec.ts +134 -0
  389. package/tests/unit/cli/workshopClientFallback.spec.ts +100 -0
  390. package/tests/unit/cli/workshopCommand.spec.ts +378 -0
  391. package/tests/unit/config/vitestEnvSetup.spec.ts +24 -0
  392. package/tests/unit/develop/checkpointState.spec.ts +378 -0
  393. package/tests/unit/develop/codeGenerator.spec.ts +447 -0
  394. package/tests/unit/develop/githubMcpAdapter.spec.ts +283 -0
  395. package/tests/unit/develop/mcpContextEnricher.spec.ts +564 -0
  396. package/tests/unit/develop/outputValidator.spec.ts +134 -0
  397. package/tests/unit/develop/pocScaffolder.spec.ts +451 -0
  398. package/tests/unit/develop/ralphLoop.spec.ts +1439 -0
  399. package/tests/unit/develop/templateRegistry.spec.ts +106 -0
  400. package/tests/unit/develop/testRunner.spec.ts +294 -0
  401. package/tests/unit/infraBicep.spec.ts +116 -0
  402. package/tests/unit/infraDeploy.spec.ts +102 -0
  403. package/tests/unit/infraTeardown.spec.ts +77 -0
  404. package/tests/unit/logging/logger.spec.ts +50 -0
  405. package/tests/unit/loop/conversationLoop.spec.ts +719 -0
  406. package/tests/unit/loop/phaseSummarizer.spec.ts +169 -0
  407. package/tests/unit/loop/streamingMarkdown.spec.ts +180 -0
  408. package/tests/unit/mcp/mcpManager.spec.ts +336 -0
  409. package/tests/unit/mcp/mcpTransport.spec.ts +689 -0
  410. package/tests/unit/mcp/retryPolicy.spec.ts +278 -0
  411. package/tests/unit/mcp/timeoutValidation.spec.ts +55 -0
  412. package/tests/unit/mcp/webSearch.spec.ts +718 -0
  413. package/tests/unit/phases/contextSummarizer.spec.ts +158 -0
  414. package/tests/unit/phases/discoveryEnricher.repeatCalls.spec.ts +125 -0
  415. package/tests/unit/phases/discoveryEnricher.spec.ts +512 -0
  416. package/tests/unit/phases/phaseExtractors.spec.ts +406 -0
  417. package/tests/unit/phases/phaseHandlers.spec.ts +483 -0
  418. package/tests/unit/prompts/promptLoader.spec.ts +144 -0
  419. package/tests/unit/schemas/pocSchemas.spec.ts +457 -0
  420. package/tests/unit/schemas/session.spec.ts +328 -0
  421. package/tests/unit/sessions/exportPaths.spec.ts +38 -0
  422. package/tests/unit/sessions/exportWriter.spec.ts +737 -0
  423. package/tests/unit/sessions/sessionManager.spec.ts +174 -0
  424. package/tests/unit/sessions/sessionStore.spec.ts +136 -0
  425. package/tests/unit/shared/activitySpinner.spec.ts +211 -0
  426. package/tests/unit/shared/cardsLoader.spec.ts +89 -0
  427. package/tests/unit/shared/copilotClient.spec.ts +185 -0
  428. package/tests/unit/shared/errorClassifier.spec.ts +152 -0
  429. package/tests/unit/shared/events.spec.ts +71 -0
  430. package/tests/unit/shared/markdownRenderer.spec.ts +42 -0
  431. package/tests/unit/shared/markdownRendererChunks.spec.ts +83 -0
  432. package/tests/unit/shared/tableRenderer.spec.ts +38 -0
  433. package/tsconfig.json +20 -0
  434. package/vitest.config.ts +15 -0
  435. package/vitest.live.config.ts +19 -0
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Phase summarizer tests.
3
+ *
4
+ * Tests for the post-phase summarization fallback that extracts
5
+ * structured data from conversation transcripts when the conversation
6
+ * loop's inline extraction fails.
7
+ */
8
+ import { describe, it, expect, vi } from 'vitest';
9
+ import { needsSummarization, buildPhaseTranscript, phaseSummarize, } from '../../../src/loop/phaseSummarizer.js';
10
+ function emptySession(overrides) {
11
+ return {
12
+ sessionId: 'test-1',
13
+ schemaVersion: '1.0.0',
14
+ createdAt: '2025-01-01T00:00:00Z',
15
+ updatedAt: '2025-01-01T00:00:00Z',
16
+ phase: 'Discover',
17
+ status: 'Active',
18
+ participants: [],
19
+ artifacts: { generatedFiles: [] },
20
+ turns: [],
21
+ ...overrides,
22
+ };
23
+ }
24
+ describe('needsSummarization', () => {
25
+ it('returns true when Ideate session field is null', () => {
26
+ const session = emptySession();
27
+ expect(needsSummarization('Ideate', session)).toBe(true);
28
+ });
29
+ it('returns false when Ideate session field is populated', () => {
30
+ const session = emptySession({
31
+ ideas: [{ id: 'idea-1', title: 'Test', description: 'Desc', workflowStepIds: [] }],
32
+ });
33
+ expect(needsSummarization('Ideate', session)).toBe(false);
34
+ });
35
+ it('returns true when Design session field is null', () => {
36
+ expect(needsSummarization('Design', emptySession())).toBe(true);
37
+ });
38
+ it('returns false when Design session field is populated', () => {
39
+ const session = emptySession({
40
+ evaluation: { method: 'feasibility-value-matrix', ideas: [] },
41
+ });
42
+ expect(needsSummarization('Design', session)).toBe(false);
43
+ });
44
+ it('returns false for unknown phase', () => {
45
+ expect(needsSummarization('Complete', emptySession())).toBe(false);
46
+ });
47
+ });
48
+ describe('buildPhaseTranscript', () => {
49
+ it('returns empty string when no turns for phase', () => {
50
+ const session = emptySession();
51
+ expect(buildPhaseTranscript('Ideate', session)).toBe('');
52
+ });
53
+ it('concatenates turns for the specified phase', () => {
54
+ const session = emptySession({
55
+ turns: [
56
+ { phase: 'Ideate', sequence: 1, role: 'user', content: 'Hello', timestamp: '2025-01-01T00:00:00Z' },
57
+ { phase: 'Ideate', sequence: 2, role: 'assistant', content: 'Hi there', timestamp: '2025-01-01T00:00:00Z' },
58
+ { phase: 'Design', sequence: 3, role: 'user', content: 'Other phase', timestamp: '2025-01-01T00:00:00Z' },
59
+ ],
60
+ });
61
+ const transcript = buildPhaseTranscript('Ideate', session);
62
+ expect(transcript).toContain('[user]: Hello');
63
+ expect(transcript).toContain('[assistant]: Hi there');
64
+ expect(transcript).not.toContain('Other phase');
65
+ });
66
+ });
67
+ describe('phaseSummarize', () => {
68
+ function createFakeClient(responseText) {
69
+ return {
70
+ createSession: vi.fn().mockResolvedValue({
71
+ send: vi.fn().mockImplementation(async function* () {
72
+ yield { type: 'TextDelta', text: responseText };
73
+ }),
74
+ }),
75
+ };
76
+ }
77
+ function createHandler(extractReturn = {}) {
78
+ return {
79
+ phase: 'Ideate',
80
+ buildSystemPrompt: () => 'test prompt',
81
+ extractResult: vi.fn().mockReturnValue(extractReturn),
82
+ };
83
+ }
84
+ it('returns empty object when session field already populated (no-op)', async () => {
85
+ const session = emptySession({
86
+ ideas: [{ id: 'idea-1', title: 'Test', description: 'Desc', workflowStepIds: [] }],
87
+ });
88
+ const client = createFakeClient('');
89
+ const handler = createHandler();
90
+ const result = await phaseSummarize(client, 'Ideate', session, handler);
91
+ expect(result).toEqual({});
92
+ expect(client.createSession).not.toHaveBeenCalled();
93
+ });
94
+ it('returns empty object when no transcript turns exist', async () => {
95
+ const session = emptySession();
96
+ const client = createFakeClient('');
97
+ const handler = createHandler();
98
+ const result = await phaseSummarize(client, 'Ideate', session, handler);
99
+ expect(result).toEqual({});
100
+ });
101
+ it('extracts IdeaCard[] from LLM summary response', async () => {
102
+ const ideas = [{ id: 'idea-1', title: 'AI Assistant', description: 'Automate tasks', workflowStepIds: ['s1'] }];
103
+ const responseJson = '```json\n' + JSON.stringify(ideas) + '\n```';
104
+ const client = createFakeClient(responseJson);
105
+ const handler = createHandler({ ideas });
106
+ const session = emptySession({
107
+ turns: [
108
+ { phase: 'Ideate', sequence: 1, role: 'user', content: 'Give me ideas', timestamp: '2025-01-01T00:00:00Z' },
109
+ { phase: 'Ideate', sequence: 2, role: 'assistant', content: 'Here are ideas', timestamp: '2025-01-01T00:00:00Z' },
110
+ ],
111
+ });
112
+ const result = await phaseSummarize(client, 'Ideate', session, handler);
113
+ expect(result).toEqual({ ideas });
114
+ expect(handler.extractResult).toHaveBeenCalledWith(session, responseJson);
115
+ });
116
+ it('returns empty object when LLM returns invalid response (no crash)', async () => {
117
+ const client = createFakeClient('This is not JSON at all');
118
+ const handler = createHandler({});
119
+ const session = emptySession({
120
+ turns: [
121
+ { phase: 'Ideate', sequence: 1, role: 'user', content: 'Give me ideas', timestamp: '2025-01-01T00:00:00Z' },
122
+ { phase: 'Ideate', sequence: 2, role: 'assistant', content: 'Here are ideas', timestamp: '2025-01-01T00:00:00Z' },
123
+ ],
124
+ });
125
+ const result = await phaseSummarize(client, 'Ideate', session, handler);
126
+ expect(result).toEqual({});
127
+ });
128
+ it('does not throw when client throws', async () => {
129
+ const client = {
130
+ createSession: vi.fn().mockRejectedValue(new Error('Network error')),
131
+ };
132
+ const handler = createHandler();
133
+ const session = emptySession({
134
+ turns: [
135
+ { phase: 'Ideate', sequence: 1, role: 'user', content: 'Hello', timestamp: '2025-01-01T00:00:00Z' },
136
+ ],
137
+ });
138
+ const result = await phaseSummarize(client, 'Ideate', session, handler);
139
+ expect(result).toEqual({});
140
+ });
141
+ });
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Tests for incremental markdown rendering during streaming (T080).
3
+ *
4
+ * Verifies that TextDelta chunks are rendered through markdownRenderer
5
+ * in TTY mode, raw markdown in non-TTY/JSON mode, and that turn history
6
+ * stores raw markdown (not ANSI).
7
+ */
8
+ import { describe, it, expect, vi } from 'vitest';
9
+ import { ConversationLoop, } from '../../../src/loop/conversationLoop.js';
10
+ import { createFakeCopilotClient } from '../../../src/shared/copilotClient.js';
11
+ import * as markdownRenderer from '../../../src/shared/markdownRenderer.js';
12
+ import { createNoOpSpinner } from '../../../src/shared/activitySpinner.js';
13
+ // ── Helpers ─────────────────────────────────────────────────────────────────
14
+ function makeSession(overrides) {
15
+ return {
16
+ sessionId: 'test-md-stream',
17
+ schemaVersion: '1.0.0',
18
+ createdAt: '2025-01-01T00:00:00Z',
19
+ updatedAt: '2025-01-01T00:00:00Z',
20
+ phase: 'Discover',
21
+ status: 'Active',
22
+ participants: [],
23
+ artifacts: { generatedFiles: [] },
24
+ ...overrides,
25
+ };
26
+ }
27
+ function makeIO(inputs, opts) {
28
+ let inputIndex = 0;
29
+ const written = [];
30
+ const activities = [];
31
+ return {
32
+ write(text) {
33
+ written.push(text);
34
+ },
35
+ writeActivity(text) {
36
+ activities.push(text);
37
+ },
38
+ writeToolSummary(_toolName, _summary) {
39
+ // no-op for these tests
40
+ },
41
+ async readInput() {
42
+ if (inputIndex >= inputs.length)
43
+ return null;
44
+ return inputs[inputIndex++] ?? null;
45
+ },
46
+ async showDecisionGate() {
47
+ return { choice: 'continue' };
48
+ },
49
+ isJsonMode: opts?.json ?? false,
50
+ isTTY: opts?.tty ?? true,
51
+ get _written() {
52
+ return written;
53
+ },
54
+ get _activities() {
55
+ return activities;
56
+ },
57
+ };
58
+ }
59
+ function makePhaseHandler(overrides) {
60
+ return {
61
+ phase: 'Discover',
62
+ buildSystemPrompt: () => 'System prompt',
63
+ extractResult: () => ({}),
64
+ ...overrides,
65
+ };
66
+ }
67
+ // ── Tests ────────────────────────────────────────────────────────────────────
68
+ describe('Incremental streaming markdown rendering (T080)', () => {
69
+ it('renders TextDelta chunks through renderMarkdown in TTY mode', async () => {
70
+ const renderSpy = vi.spyOn(markdownRenderer, 'renderMarkdown');
71
+ const client = createFakeCopilotClient([
72
+ { role: 'assistant', content: '## Hello World\n\nSome **bold** text.' },
73
+ ]);
74
+ const io = makeIO(['test input'], { tty: true, json: false });
75
+ const loop = new ConversationLoop({
76
+ client,
77
+ io,
78
+ session: makeSession(),
79
+ phaseHandler: makePhaseHandler(),
80
+ spinner: createNoOpSpinner(),
81
+ });
82
+ await loop.run();
83
+ // renderMarkdown should have been called for the TextDelta chunk
84
+ expect(renderSpy).toHaveBeenCalled();
85
+ const callArgs = renderSpy.mock.calls;
86
+ // At least one call should have the LLM content
87
+ const contentCalls = callArgs.filter(([text]) => text.includes('Hello World'));
88
+ expect(contentCalls.length).toBeGreaterThanOrEqual(1);
89
+ renderSpy.mockRestore();
90
+ });
91
+ it('writes raw text in non-TTY mode without markdown rendering', async () => {
92
+ const client = createFakeCopilotClient([
93
+ { role: 'assistant', content: '## Raw heading' },
94
+ ]);
95
+ const io = makeIO(['test'], { tty: false, json: false });
96
+ const loop = new ConversationLoop({
97
+ client,
98
+ io,
99
+ session: makeSession(),
100
+ phaseHandler: makePhaseHandler(),
101
+ spinner: createNoOpSpinner(),
102
+ });
103
+ await loop.run();
104
+ // In non-TTY mode, raw text should be written
105
+ const allOutput = io._written.join('');
106
+ expect(allOutput).toContain('## Raw heading');
107
+ });
108
+ it('preserves raw markdown in JSON mode output', async () => {
109
+ const client = createFakeCopilotClient([
110
+ { role: 'assistant', content: '**Bold** text' },
111
+ ]);
112
+ const io = makeIO(['test'], { tty: false, json: true });
113
+ const loop = new ConversationLoop({
114
+ client,
115
+ io,
116
+ session: makeSession(),
117
+ phaseHandler: makePhaseHandler(),
118
+ spinner: createNoOpSpinner(),
119
+ });
120
+ await loop.run();
121
+ const jsonOutputs = io._written.filter((w) => w.startsWith('{'));
122
+ expect(jsonOutputs.length).toBeGreaterThanOrEqual(1);
123
+ const parsed = JSON.parse(jsonOutputs[0]);
124
+ expect(parsed.content).toBe('**Bold** text');
125
+ });
126
+ it('stores raw markdown in turn history (not ANSI)', async () => {
127
+ const client = createFakeCopilotClient([
128
+ { role: 'assistant', content: '## Phase Output\n\nContent here.' },
129
+ ]);
130
+ const io = makeIO(['input'], { tty: true, json: false });
131
+ const loop = new ConversationLoop({
132
+ client,
133
+ io,
134
+ session: makeSession(),
135
+ phaseHandler: makePhaseHandler(),
136
+ spinner: createNoOpSpinner(),
137
+ });
138
+ const result = await loop.run();
139
+ // Turn history should contain raw markdown, not ANSI escape codes
140
+ const assistantTurn = result.turns?.find(t => t.role === 'assistant');
141
+ expect(assistantTurn).toBeDefined();
142
+ expect(assistantTurn.content).toBe('## Phase Output\n\nContent here.');
143
+ // Should NOT contain ANSI escape sequences
144
+ // eslint-disable-next-line no-control-regex
145
+ expect(assistantTurn.content).not.toMatch(/\u001b\[/);
146
+ });
147
+ });
@@ -0,0 +1,279 @@
1
+ /**
2
+ * MCP Manager tests.
3
+ *
4
+ * The McpManager loads .vscode/mcp.json, manages connections to MCP servers,
5
+ * lists available tools, and classifies errors.
6
+ *
7
+ * T007: Tests for callTool() real dispatch (lazy transport, retry, normalization)
8
+ * T049: Tests for toSdkMcpServers() conversion
9
+ */
10
+ import { describe, it, expect } from 'vitest';
11
+ import { McpManager, loadMcpConfig, classifyMcpError, toSdkMcpServers, } from '../../../src/mcp/mcpManager.js';
12
+ // ── Tests ────────────────────────────────────────────────────────────────────
13
+ describe('McpManager', () => {
14
+ describe('loadMcpConfig', () => {
15
+ it('loads and parses .vscode/mcp.json from given path', async () => {
16
+ const config = await loadMcpConfig(new URL('../../../.vscode/mcp.json', import.meta.url).pathname);
17
+ expect(config).toBeDefined();
18
+ expect(config.servers).toBeDefined();
19
+ expect(Object.keys(config.servers).length).toBeGreaterThan(0);
20
+ });
21
+ it('returns empty servers when file does not exist', async () => {
22
+ const config = await loadMcpConfig('/nonexistent/mcp.json');
23
+ expect(config.servers).toEqual({});
24
+ });
25
+ it('identifies server types correctly (stdio vs http)', async () => {
26
+ const config = await loadMcpConfig(new URL('../../../.vscode/mcp.json', import.meta.url).pathname);
27
+ // workiq has command → stdio type
28
+ const workiq = config.servers['workiq'];
29
+ expect(workiq).toBeDefined();
30
+ expect(workiq.type).toBe('stdio');
31
+ // github has url → http type
32
+ const github = config.servers['github'];
33
+ expect(github).toBeDefined();
34
+ expect(github.type).toBe('http');
35
+ });
36
+ });
37
+ describe('McpManager instance', () => {
38
+ it('can be created with a config', () => {
39
+ const config = {
40
+ servers: {
41
+ testServer: {
42
+ name: 'testServer',
43
+ type: 'stdio',
44
+ command: 'echo',
45
+ args: ['hello'],
46
+ },
47
+ },
48
+ };
49
+ const manager = new McpManager(config);
50
+ expect(manager).toBeDefined();
51
+ });
52
+ it('listServers returns configured server names', () => {
53
+ const config = {
54
+ servers: {
55
+ s1: { name: 's1', type: 'stdio', command: 'echo', args: [] },
56
+ s2: { name: 's2', type: 'http', url: 'http://example.com' },
57
+ },
58
+ };
59
+ const manager = new McpManager(config);
60
+ const names = manager.listServers();
61
+ expect(names).toEqual(['s1', 's2']);
62
+ });
63
+ it('getServerConfig returns config for a known server', () => {
64
+ const config = {
65
+ servers: {
66
+ myServer: {
67
+ name: 'myServer',
68
+ type: 'stdio',
69
+ command: 'npx',
70
+ args: ['-y', 'my-tool'],
71
+ },
72
+ },
73
+ };
74
+ const manager = new McpManager(config);
75
+ const sc = manager.getServerConfig('myServer');
76
+ expect(sc).toBeDefined();
77
+ expect(sc.type).toBe('stdio');
78
+ expect(sc.command).toBe('npx');
79
+ });
80
+ it('getServerConfig returns undefined for unknown server', () => {
81
+ const manager = new McpManager({ servers: {} });
82
+ expect(manager.getServerConfig('nope')).toBeUndefined();
83
+ });
84
+ it('isAvailable returns false when not connected', () => {
85
+ const config = {
86
+ servers: {
87
+ s1: { name: 's1', type: 'stdio', command: 'echo', args: [] },
88
+ },
89
+ };
90
+ const manager = new McpManager(config);
91
+ expect(manager.isAvailable('s1')).toBe(false);
92
+ });
93
+ });
94
+ describe('classifyMcpError', () => {
95
+ it('classifies ECONNREFUSED as connection-refused', () => {
96
+ const err = new Error('connect ECONNREFUSED 127.0.0.1:3000');
97
+ err.code = 'ECONNREFUSED';
98
+ expect(classifyMcpError(err)).toBe('connection-refused');
99
+ });
100
+ it('classifies ENOTFOUND as dns-failure', () => {
101
+ const err = new Error('getaddrinfo ENOTFOUND example.com');
102
+ err.code = 'ENOTFOUND';
103
+ expect(classifyMcpError(err)).toBe('dns-failure');
104
+ });
105
+ it('classifies ETIMEDOUT as timeout', () => {
106
+ const err = new Error('connect ETIMEDOUT');
107
+ err.code = 'ETIMEDOUT';
108
+ expect(classifyMcpError(err)).toBe('timeout');
109
+ });
110
+ it('classifies unknown errors as unknown', () => {
111
+ expect(classifyMcpError(new Error('something weird'))).toBe('unknown');
112
+ });
113
+ it('classifies non-Error values as unknown', () => {
114
+ expect(classifyMcpError('string error')).toBe('unknown');
115
+ });
116
+ });
117
+ // ── T007: callTool() real dispatch tests ─────────────────────────────────
118
+ describe('callTool() real dispatch', () => {
119
+ it('throws when server is not in config', async () => {
120
+ const manager = new McpManager({ servers: {} });
121
+ await expect(manager.callTool('nonexistent', 'tool', {})).rejects.toThrow(/[Uu]nknown.*nonexistent|not available/);
122
+ });
123
+ it('returns unwrapped content from ToolCallResponse', async () => {
124
+ // This test will fail until T016 implements real dispatch.
125
+ // Once implemented, McpManager.callTool should return the
126
+ // unwrapped content from the transport's ToolCallResponse.
127
+ const config = {
128
+ servers: {
129
+ testserver: {
130
+ name: 'testserver',
131
+ type: 'http',
132
+ url: 'https://test.example.com/mcp',
133
+ },
134
+ },
135
+ };
136
+ const manager = new McpManager(config);
137
+ manager.markConnected('testserver');
138
+ // After T016: This should dispatch to transport and return parsed content.
139
+ // For now, we just verify the method exists and can be called
140
+ // (it currently throws "not yet wired to transport").
141
+ try {
142
+ await manager.callTool('testserver', 'search', { query: 'test' });
143
+ }
144
+ catch (err) {
145
+ // Expected to throw "not yet wired" until T016
146
+ expect(err).toBeInstanceOf(Error);
147
+ }
148
+ });
149
+ it('throws when calling unavailable server', async () => {
150
+ const config = {
151
+ servers: {
152
+ myserver: {
153
+ name: 'myserver',
154
+ type: 'stdio',
155
+ command: 'echo',
156
+ args: [],
157
+ },
158
+ },
159
+ };
160
+ const manager = new McpManager(config);
161
+ // Don't mark connected
162
+ await expect(manager.callTool('myserver', 'tool', {})).rejects.toThrow(/not available/);
163
+ });
164
+ });
165
+ // ── T049: toSdkMcpServers() tests ───────────────────────────────────────
166
+ describe('toSdkMcpServers()', () => {
167
+ it('converts StdioServerConfig to SDK format', () => {
168
+ const config = {
169
+ servers: {
170
+ context7: {
171
+ name: 'context7',
172
+ type: 'stdio',
173
+ command: 'npx',
174
+ args: ['-y', '@upstash/context7-mcp'],
175
+ env: { NODE_ENV: 'production' },
176
+ cwd: '/tmp',
177
+ tools: ['resolve-library-id', 'query-docs'],
178
+ timeout: 15000,
179
+ },
180
+ },
181
+ };
182
+ const result = toSdkMcpServers(config);
183
+ expect(result).toEqual({
184
+ context7: {
185
+ type: 'stdio',
186
+ command: 'npx',
187
+ args: ['-y', '@upstash/context7-mcp'],
188
+ tools: ['resolve-library-id', 'query-docs'],
189
+ env: { NODE_ENV: 'production' },
190
+ cwd: '/tmp',
191
+ timeout: 15000,
192
+ },
193
+ });
194
+ });
195
+ it('converts HttpServerConfig to SDK format', () => {
196
+ const config = {
197
+ servers: {
198
+ github: {
199
+ name: 'github',
200
+ type: 'http',
201
+ url: 'https://api.githubcopilot.com/mcp/',
202
+ headers: { 'X-Custom': 'value' },
203
+ tools: ['create_repository'],
204
+ timeout: 60000,
205
+ },
206
+ },
207
+ };
208
+ const result = toSdkMcpServers(config);
209
+ expect(result).toEqual({
210
+ github: {
211
+ type: 'http',
212
+ url: 'https://api.githubcopilot.com/mcp/',
213
+ tools: ['create_repository'],
214
+ headers: { 'X-Custom': 'value' },
215
+ timeout: 60000,
216
+ },
217
+ });
218
+ });
219
+ it('returns empty object for empty servers', () => {
220
+ const config = { servers: {} };
221
+ const result = toSdkMcpServers(config);
222
+ expect(result).toEqual({});
223
+ });
224
+ it('defaults tools to ["*"] when not specified', () => {
225
+ const config = {
226
+ servers: {
227
+ simple: {
228
+ name: 'simple',
229
+ type: 'stdio',
230
+ command: 'echo',
231
+ args: [],
232
+ },
233
+ },
234
+ };
235
+ const result = toSdkMcpServers(config);
236
+ expect(result.simple.tools).toEqual(['*']);
237
+ });
238
+ it('omits optional fields when not in source config', () => {
239
+ const config = {
240
+ servers: {
241
+ minimal: {
242
+ name: 'minimal',
243
+ type: 'http',
244
+ url: 'https://example.com/mcp',
245
+ },
246
+ },
247
+ };
248
+ const result = toSdkMcpServers(config);
249
+ expect(result.minimal).toEqual({
250
+ type: 'http',
251
+ url: 'https://example.com/mcp',
252
+ tools: ['*'],
253
+ });
254
+ expect(result.minimal).not.toHaveProperty('headers');
255
+ expect(result.minimal).not.toHaveProperty('timeout');
256
+ });
257
+ it('converts mixed stdio and http configs', () => {
258
+ const config = {
259
+ servers: {
260
+ local: {
261
+ name: 'local',
262
+ type: 'stdio',
263
+ command: 'node',
264
+ args: ['server.js'],
265
+ },
266
+ remote: {
267
+ name: 'remote',
268
+ type: 'http',
269
+ url: 'https://api.example.com',
270
+ },
271
+ },
272
+ };
273
+ const result = toSdkMcpServers(config);
274
+ expect(Object.keys(result)).toEqual(['local', 'remote']);
275
+ expect(result.local.type).toBe('stdio');
276
+ expect(result.remote.type).toBe('http');
277
+ });
278
+ });
279
+ });