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,134 @@
1
+ /**
2
+ * T064: Unit tests for statusCommand — session name display.
3
+ *
4
+ * Verifies that statusCommand displays the session name in both
5
+ * TTY table and JSON output formats.
6
+ */
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
+
9
+ import type { WorkshopSession } from '../../../src/shared/schemas/session.js';
10
+
11
+ function makeSession(overrides?: Partial<WorkshopSession>): WorkshopSession {
12
+ return {
13
+ sessionId: 'test-session-001',
14
+ schemaVersion: '1.0.0',
15
+ createdAt: '2026-01-01T00:00:00Z',
16
+ updatedAt: '2026-01-01T01:00:00Z',
17
+ phase: 'Discover',
18
+ status: 'Active',
19
+ participants: [],
20
+ artifacts: { generatedFiles: [] },
21
+ turns: [],
22
+ ...overrides,
23
+ };
24
+ }
25
+
26
+ // Mock sessionStore
27
+ const mockStore = {
28
+ load: vi.fn(),
29
+ save: vi.fn(),
30
+ exists: vi.fn(),
31
+ list: vi.fn(),
32
+ };
33
+
34
+ vi.mock('../../../src/sessions/sessionStore.js', () => ({
35
+ createDefaultStore: () => mockStore,
36
+ SessionStore: vi.fn(),
37
+ }));
38
+
39
+ describe('statusCommand', () => {
40
+ let stdoutChunks: string[];
41
+ let stderrChunks: string[];
42
+ const originalWrite = process.stdout.write;
43
+ const originalConsoleLog = console.log;
44
+ const originalConsoleError = console.error;
45
+
46
+ beforeEach(() => {
47
+ stdoutChunks = [];
48
+ stderrChunks = [];
49
+ process.stdout.write = vi.fn((chunk: string | Uint8Array) => {
50
+ stdoutChunks.push(chunk.toString());
51
+ return true;
52
+ }) as typeof process.stdout.write;
53
+ console.log = vi.fn((...args: unknown[]) => {
54
+ stdoutChunks.push(args.map(String).join(' '));
55
+ });
56
+ console.error = vi.fn((...args: unknown[]) => {
57
+ stderrChunks.push(args.map(String).join(' '));
58
+ });
59
+ process.exitCode = undefined;
60
+ vi.resetAllMocks();
61
+ });
62
+
63
+ afterEach(() => {
64
+ process.stdout.write = originalWrite;
65
+ console.log = originalConsoleLog;
66
+ console.error = originalConsoleError;
67
+ process.exitCode = undefined;
68
+ });
69
+
70
+ describe('session name display (T064)', () => {
71
+ it('displays session name in JSON output for single session', async () => {
72
+ const session = makeSession({ name: 'Logistics AI Workshop' } as Partial<WorkshopSession>);
73
+ mockStore.exists.mockResolvedValue(true);
74
+ mockStore.load.mockResolvedValue(session);
75
+
76
+ const { statusCommand } = await import('../../../src/cli/statusCommand.js');
77
+ await statusCommand({ session: 'test-session-001', json: true });
78
+
79
+ const output = stdoutChunks.join('');
80
+ const parsed = JSON.parse(output);
81
+ expect(parsed.name).toBe('Logistics AI Workshop');
82
+ });
83
+
84
+ it('omits name from JSON output when session has no name', async () => {
85
+ const session = makeSession();
86
+ mockStore.exists.mockResolvedValue(true);
87
+ mockStore.load.mockResolvedValue(session);
88
+
89
+ const { statusCommand } = await import('../../../src/cli/statusCommand.js');
90
+ await statusCommand({ session: 'test-session-001', json: true });
91
+
92
+ const output = stdoutChunks.join('');
93
+ const parsed = JSON.parse(output);
94
+ expect(parsed.name).toBeUndefined();
95
+ });
96
+
97
+ it('displays session name in TTY output for single session', async () => {
98
+ const session = makeSession({ name: 'Retail AI Insights' } as Partial<WorkshopSession>);
99
+ mockStore.exists.mockResolvedValue(true);
100
+ mockStore.load.mockResolvedValue(session);
101
+
102
+ const { statusCommand } = await import('../../../src/cli/statusCommand.js');
103
+ await statusCommand({ session: 'test-session-001', json: false });
104
+
105
+ const output = stdoutChunks.join(' ');
106
+ expect(output).toContain('Retail AI Insights');
107
+ });
108
+
109
+ it('displays session name in session list JSON output', async () => {
110
+ const session = makeSession({ name: 'Supply Chain AI' } as Partial<WorkshopSession>);
111
+ mockStore.list.mockResolvedValue(['test-session-001']);
112
+ mockStore.load.mockResolvedValue(session);
113
+
114
+ const { statusCommand } = await import('../../../src/cli/statusCommand.js');
115
+ await statusCommand({ json: true });
116
+
117
+ const output = stdoutChunks.join('');
118
+ const parsed = JSON.parse(output);
119
+ expect(parsed.sessions[0].name).toBe('Supply Chain AI');
120
+ });
121
+
122
+ it('displays session name in session list TTY table', async () => {
123
+ const session = makeSession({ name: 'HR Automation' } as Partial<WorkshopSession>);
124
+ mockStore.list.mockResolvedValue(['test-session-001']);
125
+ mockStore.load.mockResolvedValue(session);
126
+
127
+ const { statusCommand } = await import('../../../src/cli/statusCommand.js');
128
+ await statusCommand({ json: false });
129
+
130
+ const output = stdoutChunks.join(' ');
131
+ expect(output).toContain('HR Automation');
132
+ });
133
+ });
134
+ });
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Unit tests for workshopCommand client creation — silent mock fallback bug.
3
+ *
4
+ * Verifies:
5
+ * - When createCopilotClient() throws, the error is logged (not swallowed)
6
+ * - The fake client is never silently substituted in production mode
7
+ * - The user receives a clear error message when the SDK is unavailable
8
+ */
9
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
10
+
11
+ // Mock copilotClient module so createCopilotClient always rejects
12
+ vi.mock('../../../src/shared/copilotClient.js', async (importOriginal) => {
13
+ const orig = await importOriginal<typeof import('../../../src/shared/copilotClient.js')>();
14
+ return {
15
+ ...orig,
16
+ createCopilotClient: vi.fn().mockRejectedValue(
17
+ new Error('SDK not available'),
18
+ ),
19
+ };
20
+ });
21
+
22
+ // Mock the logger so we can assert error logging
23
+ vi.mock('../../../src/logging/logger.js', () => {
24
+ const fakeLogger = {
25
+ error: vi.fn(),
26
+ warn: vi.fn(),
27
+ info: vi.fn(),
28
+ debug: vi.fn(),
29
+ fatal: vi.fn(),
30
+ child: vi.fn().mockReturnThis(),
31
+ };
32
+ return {
33
+ getLogger: vi.fn(() => fakeLogger),
34
+ createLogger: vi.fn(() => fakeLogger),
35
+ initGlobalLogger: vi.fn(() => fakeLogger),
36
+ };
37
+ });
38
+
39
+ import { workshopCommand } from '../../../src/cli/workshopCommand.js';
40
+ import { getLogger } from '../../../src/logging/logger.js';
41
+
42
+ describe('workshopCommand — client creation failure', () => {
43
+ let exitCodeBefore: number | undefined;
44
+
45
+ beforeEach(() => {
46
+ exitCodeBefore = process.exitCode as number | undefined;
47
+ process.exitCode = undefined;
48
+ });
49
+
50
+ afterEach(() => {
51
+ process.exitCode = exitCodeBefore;
52
+ vi.restoreAllMocks();
53
+ });
54
+
55
+ it('logs the error when createCopilotClient fails', async () => {
56
+ // Run with --new-session --non-interactive so no menus block
57
+ await workshopCommand({
58
+ newSession: true,
59
+ nonInteractive: true,
60
+ json: true,
61
+ });
62
+
63
+ const logger = getLogger();
64
+ expect(logger.error).toHaveBeenCalledWith(
65
+ expect.objectContaining({ err: expect.any(Error) }),
66
+ expect.stringContaining('Copilot'),
67
+ );
68
+ });
69
+
70
+ it('sets non-zero exit code when SDK is unavailable', async () => {
71
+ await workshopCommand({
72
+ newSession: true,
73
+ nonInteractive: true,
74
+ json: true,
75
+ });
76
+
77
+ expect(process.exitCode).not.toBe(0);
78
+ expect(process.exitCode).toBeDefined();
79
+ });
80
+
81
+ it('does not silently fall back to fake client', async () => {
82
+ // Capture stdout to check there is no fake "Welcome" response
83
+ const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
84
+
85
+ await workshopCommand({
86
+ newSession: true,
87
+ nonInteractive: true,
88
+ json: true,
89
+ });
90
+
91
+ const allOutput = writeSpy.mock.calls.map(c => String(c[0])).join('');
92
+
93
+ // Should NOT contain the canned fake response
94
+ expect(allOutput).not.toContain('Welcome to the AI Discovery Workshop');
95
+ // Should contain an error indication
96
+ expect(allOutput).toContain('error');
97
+
98
+ writeSpy.mockRestore();
99
+ });
100
+ });
@@ -0,0 +1,378 @@
1
+ /**
2
+ * Unit tests for workshopCommand — session name display, Plan→Develop
3
+ * transition guidance (T052), and auto-transition prompt (T053).
4
+ */
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
6
+
7
+ import type { WorkshopSession } from '../../../src/shared/schemas/session.js';
8
+
9
+ // ── Mocks ───────────────────────────────────────────────────────────────────
10
+
11
+ const mockStore = {
12
+ load: vi.fn(),
13
+ save: vi.fn().mockResolvedValue(undefined),
14
+ exists: vi.fn(),
15
+ list: vi.fn(),
16
+ };
17
+
18
+ vi.mock('../../../src/sessions/sessionStore.js', () => ({
19
+ createDefaultStore: () => mockStore,
20
+ SessionStore: vi.fn(),
21
+ }));
22
+
23
+ // Minimal fake CopilotClient
24
+ const fakeClient = {
25
+ async createSession() {
26
+ return {
27
+ send: async function* () {
28
+ yield { type: 'TextDelta' as const, text: 'Hello!', timestamp: new Date().toISOString() };
29
+ },
30
+ getHistory: () => [],
31
+ };
32
+ },
33
+ };
34
+
35
+ vi.mock('../../../src/shared/copilotClient.js', async (importOriginal) => {
36
+ const orig = await importOriginal<typeof import('../../../src/shared/copilotClient.js')>();
37
+ return {
38
+ ...orig,
39
+ createCopilotClient: vi.fn().mockResolvedValue(fakeClient),
40
+ };
41
+ });
42
+
43
+ vi.mock('../../../src/logging/logger.js', () => {
44
+ const fakeLogger = {
45
+ error: vi.fn(),
46
+ warn: vi.fn(),
47
+ info: vi.fn(),
48
+ debug: vi.fn(),
49
+ fatal: vi.fn(),
50
+ child: vi.fn().mockReturnThis(),
51
+ };
52
+ return {
53
+ getLogger: vi.fn(() => fakeLogger),
54
+ createLogger: vi.fn(() => fakeLogger),
55
+ initGlobalLogger: vi.fn(() => fakeLogger),
56
+ };
57
+ });
58
+
59
+ // Track LoopIO writes — decision gate is configurable per test
60
+ let ioWrites: string[] = [];
61
+ let ioReadResponses: (string | null)[] = [];
62
+ let ioReadIndex = 0;
63
+ let ioReadInputPrompts: string[] = [];
64
+ let decisionGateResponses: Array<{ choice: string }> = [];
65
+ let decisionGateIndex = 0;
66
+
67
+ vi.mock('../../../src/cli/ioContext.js', () => ({
68
+ createLoopIO: () => ({
69
+ write: (text: string) => {
70
+ ioWrites.push(text);
71
+ },
72
+ writeActivity: () => {},
73
+ readInput: async (prompt?: string) => {
74
+ if (prompt) ioReadInputPrompts.push(prompt);
75
+ if (ioReadIndex >= ioReadResponses.length) return null;
76
+ return ioReadResponses[ioReadIndex++];
77
+ },
78
+ showDecisionGate: async () => {
79
+ if (decisionGateIndex < decisionGateResponses.length) {
80
+ return decisionGateResponses[decisionGateIndex++];
81
+ }
82
+ return { choice: 'exit' as const };
83
+ },
84
+ isJsonMode: false,
85
+ isTTY: true,
86
+ }),
87
+ }));
88
+
89
+ // Mock phaseHandlers to avoid loading prompts
90
+ vi.mock('../../../src/phases/phaseHandlers.js', async (importOriginal) => {
91
+ const orig = await importOriginal<typeof import('../../../src/phases/phaseHandlers.js')>();
92
+ return {
93
+ ...orig,
94
+ createPhaseHandler: () => ({
95
+ phase: 'Discover',
96
+ buildSystemPrompt: () => 'system prompt',
97
+ getReferences: () => [],
98
+ extractResult: () => ({}),
99
+ isComplete: () => false,
100
+ _preload: async () => {},
101
+ }),
102
+ };
103
+ });
104
+
105
+ describe('workshopCommand session name display (T064b)', () => {
106
+ beforeEach(() => {
107
+ ioWrites = [];
108
+ ioReadResponses = [];
109
+ ioReadIndex = 0;
110
+ ioReadInputPrompts = [];
111
+ decisionGateResponses = [];
112
+ decisionGateIndex = 0;
113
+ vi.clearAllMocks();
114
+ process.exitCode = undefined;
115
+ });
116
+
117
+ afterEach(() => {
118
+ process.exitCode = undefined;
119
+ });
120
+
121
+ it('displays session name on new session creation message', async () => {
122
+ // Simulate: main menu → choose "new" (option 1) → session starts → exit
123
+ ioReadResponses = ['1']; // Pick new session
124
+ mockStore.list.mockResolvedValue([]);
125
+
126
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
127
+ await workshopCommand({});
128
+
129
+ const allOutput = ioWrites.join(' ');
130
+ // New session should show session ID (timestamp format)
131
+ expect(allOutput).toContain('New session');
132
+ });
133
+
134
+ it('displays session name when resuming a named session', async () => {
135
+ const session: WorkshopSession = {
136
+ sessionId: '2026-01-01_120000',
137
+ schemaVersion: '1.0.0',
138
+ createdAt: '2026-01-01T12:00:00Z',
139
+ updatedAt: '2026-01-01T12:30:00Z',
140
+ phase: 'Discover',
141
+ status: 'Active',
142
+ participants: [],
143
+ artifacts: { generatedFiles: [] },
144
+ turns: [],
145
+ name: 'Logistics AI Workshop',
146
+ } as WorkshopSession;
147
+
148
+ mockStore.exists.mockResolvedValue(true);
149
+ mockStore.load.mockResolvedValue(session);
150
+
151
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
152
+ await workshopCommand({ session: '2026-01-01_120000' });
153
+
154
+ // The session name should appear in the output somewhere
155
+ const allOutput = ioWrites.join(' ');
156
+ expect(allOutput).toContain('Logistics AI Workshop');
157
+ });
158
+
159
+ it('displays session name in pause message when session has a name', async () => {
160
+ const session: WorkshopSession = {
161
+ sessionId: '2026-01-01_120000',
162
+ schemaVersion: '1.0.0',
163
+ createdAt: '2026-01-01T12:00:00Z',
164
+ updatedAt: '2026-01-01T12:30:00Z',
165
+ phase: 'Discover',
166
+ status: 'Active',
167
+ participants: [],
168
+ artifacts: { generatedFiles: [] },
169
+ turns: [],
170
+ name: 'Retail Insights',
171
+ } as WorkshopSession;
172
+
173
+ mockStore.exists.mockResolvedValue(true);
174
+ mockStore.load.mockResolvedValue(session);
175
+
176
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
177
+ // Direct session mode triggers run → decision gate returns exit → pauses
178
+ await workshopCommand({ session: '2026-01-01_120000' });
179
+
180
+ const allOutput = ioWrites.join(' ');
181
+ expect(allOutput).toContain('Retail Insights');
182
+ });
183
+
184
+ it('shows only session ID when no name is set', async () => {
185
+ const session: WorkshopSession = {
186
+ sessionId: '2026-01-01_120000',
187
+ schemaVersion: '1.0.0',
188
+ createdAt: '2026-01-01T12:00:00Z',
189
+ updatedAt: '2026-01-01T12:30:00Z',
190
+ phase: 'Discover',
191
+ status: 'Active',
192
+ participants: [],
193
+ artifacts: { generatedFiles: [] },
194
+ turns: [],
195
+ };
196
+
197
+ mockStore.exists.mockResolvedValue(true);
198
+ mockStore.load.mockResolvedValue(session);
199
+
200
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
201
+ await workshopCommand({ session: '2026-01-01_120000' });
202
+
203
+ const allOutput = ioWrites.join(' ');
204
+ expect(allOutput).toContain('2026-01-01_120000');
205
+ });
206
+
207
+ it('shows only named sessions in the resume list', async () => {
208
+ const namedSession: WorkshopSession = {
209
+ sessionId: '2026-02-28_165120',
210
+ schemaVersion: '1.0.0',
211
+ createdAt: '2026-02-28T16:51:20Z',
212
+ updatedAt: '2026-02-28T16:55:00Z',
213
+ phase: 'Discover',
214
+ status: 'Active',
215
+ participants: [],
216
+ artifacts: { generatedFiles: [] },
217
+ turns: [],
218
+ name: 'Inventory Insights',
219
+ } as WorkshopSession;
220
+
221
+ const unnamedSession: WorkshopSession = {
222
+ sessionId: '2026-02-28_170457',
223
+ schemaVersion: '1.0.0',
224
+ createdAt: '2026-02-28T17:04:57Z',
225
+ updatedAt: '2026-02-28T17:05:00Z',
226
+ phase: 'Discover',
227
+ status: 'Active',
228
+ participants: [],
229
+ artifacts: { generatedFiles: [] },
230
+ turns: [],
231
+ } as WorkshopSession;
232
+
233
+ ioReadResponses = ['2', '1']; // Resume flow, choose first session
234
+ mockStore.list.mockResolvedValue([namedSession.sessionId, unnamedSession.sessionId]);
235
+ mockStore.load.mockImplementation(async (id: string) => {
236
+ if (id === namedSession.sessionId) return namedSession;
237
+ if (id === unnamedSession.sessionId) return unnamedSession;
238
+ throw new Error('Unknown session');
239
+ });
240
+
241
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
242
+ await workshopCommand({});
243
+
244
+ const allOutput = ioWrites.join(' ');
245
+ expect(allOutput).toContain('Available sessions');
246
+ // Named session appears in the list
247
+ expect(allOutput).toContain('Inventory Insights');
248
+ expect(allOutput).toContain('2026-02-28_165120');
249
+ // Unnamed session is filtered out
250
+ expect(allOutput).not.toContain('2026-02-28_170457');
251
+ });
252
+ });
253
+
254
+ // ── T052: Plan→Develop transition displays "sofia dev --session {id}" ─────
255
+
256
+ describe('workshopCommand Plan→Develop transition (T052)', () => {
257
+ beforeEach(() => {
258
+ ioWrites = [];
259
+ ioReadResponses = [];
260
+ ioReadIndex = 0;
261
+ ioReadInputPrompts = [];
262
+ decisionGateResponses = [];
263
+ decisionGateIndex = 0;
264
+ vi.clearAllMocks();
265
+ process.exitCode = undefined;
266
+ });
267
+
268
+ afterEach(() => {
269
+ process.exitCode = undefined;
270
+ });
271
+
272
+ it('displays "sofia dev --session {id}" after Plan phase completes (FR-020)', async () => {
273
+ const session: WorkshopSession = {
274
+ sessionId: 'plan-test-001',
275
+ schemaVersion: '1.0.0',
276
+ createdAt: '2026-01-01T12:00:00Z',
277
+ updatedAt: '2026-01-01T12:30:00Z',
278
+ phase: 'Plan',
279
+ status: 'Active',
280
+ participants: [],
281
+ artifacts: { generatedFiles: [] },
282
+ turns: [],
283
+ };
284
+
285
+ mockStore.exists.mockResolvedValue(true);
286
+ mockStore.load.mockResolvedValue(session);
287
+
288
+ // First gate (Plan): continue → triggers transition to Develop
289
+ // Second gate (Develop): exit → stops the loop
290
+ decisionGateResponses = [{ choice: 'continue' }, { choice: 'exit' }];
291
+
292
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
293
+ await workshopCommand({ session: 'plan-test-001' });
294
+
295
+ const allOutput = ioWrites.join(' ');
296
+ // FR-020: Shows transition message for PoC generation
297
+ expect(allOutput).toContain('Starting PoC Generation');
298
+ // Session lacks selection/plan so RalphLoop throws — workshop shows recovery guidance
299
+ expect(allOutput).toContain('sofia dev --session plan-test-001');
300
+ });
301
+ });
302
+
303
+ // ── T053: Workshop auto-runs PoC generation after Plan phase ─────────────
304
+
305
+ describe('workshopCommand auto-run PoC generation (T053)', () => {
306
+ beforeEach(() => {
307
+ ioWrites = [];
308
+ ioReadResponses = [];
309
+ ioReadIndex = 0;
310
+ ioReadInputPrompts = [];
311
+ decisionGateResponses = [];
312
+ decisionGateIndex = 0;
313
+ vi.clearAllMocks();
314
+ process.exitCode = undefined;
315
+ });
316
+
317
+ afterEach(() => {
318
+ process.exitCode = undefined;
319
+ });
320
+
321
+ it('auto-runs PoC generation after Plan phase, shows warning on failure (FR-021)', async () => {
322
+ const session: WorkshopSession = {
323
+ sessionId: 'transition-test-001',
324
+ schemaVersion: '1.0.0',
325
+ createdAt: '2026-01-01T12:00:00Z',
326
+ updatedAt: '2026-01-01T12:30:00Z',
327
+ phase: 'Plan',
328
+ status: 'Active',
329
+ participants: [],
330
+ artifacts: { generatedFiles: [] },
331
+ turns: [],
332
+ };
333
+
334
+ mockStore.exists.mockResolvedValue(true);
335
+ mockStore.load.mockResolvedValue(session);
336
+
337
+ // Continue from Plan → Develop, then exit
338
+ decisionGateResponses = [{ choice: 'continue' }, { choice: 'exit' }];
339
+
340
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
341
+ await workshopCommand({ session: 'transition-test-001' });
342
+
343
+ const allOutput = ioWrites.join(' ');
344
+ // FR-021: PoC generation runs automatically after Plan phase
345
+ expect(allOutput).toContain('Starting PoC Generation');
346
+ // Session lacks selection/plan so RalphLoop throws — workshop shows recovery guidance
347
+ expect(allOutput).toContain('PoC generation failed');
348
+ expect(allOutput).toContain('sofia dev --session transition-test-001');
349
+ });
350
+
351
+ it('continues workshop after PoC generation failure (FR-021)', async () => {
352
+ const session: WorkshopSession = {
353
+ sessionId: 'auto-transition-001',
354
+ schemaVersion: '1.0.0',
355
+ createdAt: '2026-01-01T12:00:00Z',
356
+ updatedAt: '2026-01-01T12:30:00Z',
357
+ phase: 'Plan',
358
+ status: 'Active',
359
+ participants: [],
360
+ artifacts: { generatedFiles: [] },
361
+ turns: [],
362
+ };
363
+
364
+ mockStore.exists.mockResolvedValue(true);
365
+ mockStore.load.mockResolvedValue(session);
366
+
367
+ // Continue from Plan → PoC fails → continues to Develop phase → exit
368
+ decisionGateResponses = [{ choice: 'continue' }, { choice: 'exit' }];
369
+
370
+ const { workshopCommand } = await import('../../../src/cli/workshopCommand.js');
371
+ await workshopCommand({ session: 'auto-transition-001' });
372
+
373
+ const allOutput = ioWrites.join(' ');
374
+ // Workshop continues after PoC failure — doesn't block
375
+ expect(allOutput).toContain('Starting PoC Generation');
376
+ expect(allOutput).toContain('sofia dev --session auto-transition-001');
377
+ });
378
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import liveConfig from '../../../vitest.live.config';
4
+ import defaultConfig from '../../../vitest.config';
5
+
6
+ type VitestLikeConfig = {
7
+ test?: {
8
+ setupFiles?: string[];
9
+ };
10
+ };
11
+
12
+ describe('Vitest env setup', () => {
13
+ it('declares a setup file for default tests', () => {
14
+ const config = defaultConfig as VitestLikeConfig;
15
+
16
+ expect(config.test?.setupFiles).toContain('tests/setup/loadEnv.ts');
17
+ });
18
+
19
+ it('declares a setup file for live tests', () => {
20
+ const config = liveConfig as VitestLikeConfig;
21
+
22
+ expect(config.test?.setupFiles).toContain('tests/setup/loadEnv.ts');
23
+ });
24
+ });