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,185 @@
1
+ /**
2
+ * Integration test: Direct command TTY mode (T045)
3
+ *
4
+ * Tests direct command entry with --session and --phase flags
5
+ * in a TTY environment where interactive prompts are available.
6
+ *
7
+ * Verifies:
8
+ * - `--session <id> --phase <phase>` jumps to the requested phase
9
+ * - Session is loaded and used for the conversation loop
10
+ * - TTY mode prompts for missing inputs
11
+ * - Decision gates work in direct TTY mode
12
+ * - Session state is correctly updated after direct phase run
13
+ */
14
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
15
+ import { mkdtemp, rm } from 'node:fs/promises';
16
+ import { join } from 'node:path';
17
+ import { tmpdir } from 'node:os';
18
+
19
+ import type { LoopIO, DecisionGateResult } from '../../src/loop/conversationLoop.js';
20
+ import { createFakeCopilotClient } from '../../src/shared/copilotClient.js';
21
+ import type { WorkshopSession, PhaseValue } from '../../src/shared/schemas/session.js';
22
+ import { SessionStore } from '../../src/sessions/sessionStore.js';
23
+ import {
24
+ runDirectCommand,
25
+ } from '../../src/cli/directCommands.js';
26
+
27
+ // ── Helpers ──────────────────────────────────────────────────────────────────
28
+
29
+ function createTestSession(overrides?: Partial<WorkshopSession>): WorkshopSession {
30
+ const now = new Date().toISOString();
31
+ return {
32
+ sessionId: 'test-direct-tty',
33
+ schemaVersion: '1.0.0',
34
+ createdAt: now,
35
+ updatedAt: now,
36
+ phase: 'Discover',
37
+ status: 'Active',
38
+ participants: [],
39
+ artifacts: { generatedFiles: [] },
40
+ turns: [],
41
+ ...overrides,
42
+ };
43
+ }
44
+
45
+ function createScriptedIO(
46
+ inputs: (string | null)[],
47
+ decisionGateChoice: DecisionGateResult = { choice: 'exit' },
48
+ ): LoopIO & { output: string[]; activityLog: string[] } {
49
+ let inputIdx = 0;
50
+ const output: string[] = [];
51
+ const activityLog: string[] = [];
52
+
53
+ return {
54
+ write(text: string) { output.push(text); },
55
+ writeActivity(text: string) { activityLog.push(text); },
56
+ writeToolSummary(_toolName: string, _summary: string) {},
57
+ async readInput(_prompt?: string): Promise<string | null> {
58
+ if (inputIdx >= inputs.length) return null;
59
+ return inputs[inputIdx++];
60
+ },
61
+ async showDecisionGate(_phase: PhaseValue): Promise<DecisionGateResult> {
62
+ return decisionGateChoice;
63
+ },
64
+ isJsonMode: false,
65
+ isTTY: true,
66
+ output,
67
+ activityLog,
68
+ };
69
+ }
70
+
71
+ // ── Tests ────────────────────────────────────────────────────────────────────
72
+
73
+ describe('Direct command TTY mode', () => {
74
+ let tmpDir: string;
75
+ let store: SessionStore;
76
+
77
+ beforeEach(async () => {
78
+ tmpDir = await mkdtemp(join(tmpdir(), 'sofia-direct-tty-'));
79
+ store = new SessionStore(tmpDir);
80
+ });
81
+
82
+ afterEach(async () => {
83
+ await rm(tmpDir, { recursive: true, force: true });
84
+ });
85
+
86
+ it('runs a specific phase with --session and --phase', async () => {
87
+ const session = createTestSession({ phase: 'Ideate' });
88
+ await store.save(session);
89
+
90
+ const io = createScriptedIO(['Here are some ideas about AI automation'], { choice: 'exit' });
91
+ const client = createFakeCopilotClient([
92
+ { role: 'assistant', content: '{"ideas": [{"title": "AI Helper", "description": "An AI assistant"}]}' },
93
+ ]);
94
+
95
+ const result = await runDirectCommand({
96
+ sessionId: session.sessionId,
97
+ phase: 'Ideate',
98
+ store,
99
+ client,
100
+ io,
101
+ });
102
+
103
+ expect(result.exitCode).toBe(0);
104
+ const loaded = await store.load(session.sessionId);
105
+ expect(loaded.phase).toBe('Ideate');
106
+ });
107
+
108
+ it('prompts for missing input in TTY mode', async () => {
109
+ const session = createTestSession();
110
+ await store.save(session);
111
+
112
+ const io = createScriptedIO(['My business is a retail company'], { choice: 'exit' });
113
+ const client = createFakeCopilotClient([
114
+ { role: 'assistant', content: 'Thank you. Let me analyze your business context.' },
115
+ ]);
116
+
117
+ const result = await runDirectCommand({
118
+ sessionId: session.sessionId,
119
+ phase: 'Discover',
120
+ store,
121
+ client,
122
+ io,
123
+ });
124
+
125
+ // TTY mode should succeed — it can prompt
126
+ expect(result.exitCode).toBe(0);
127
+ });
128
+
129
+ it('returns error when session not found', async () => {
130
+ const io = createScriptedIO([]);
131
+ const client = createFakeCopilotClient([]);
132
+
133
+ const result = await runDirectCommand({
134
+ sessionId: 'nonexistent-session',
135
+ phase: 'Discover',
136
+ store,
137
+ client,
138
+ io,
139
+ });
140
+
141
+ expect(result.exitCode).toBe(1);
142
+ expect(result.error).toContain('not found');
143
+ });
144
+
145
+ it('returns error for invalid phase', async () => {
146
+ const session = createTestSession();
147
+ await store.save(session);
148
+
149
+ const io = createScriptedIO([]);
150
+ const client = createFakeCopilotClient([]);
151
+
152
+ const result = await runDirectCommand({
153
+ sessionId: session.sessionId,
154
+ phase: 'InvalidPhase' as PhaseValue,
155
+ store,
156
+ client,
157
+ io,
158
+ });
159
+
160
+ expect(result.exitCode).toBe(1);
161
+ expect(result.error).toContain('Invalid phase');
162
+ });
163
+
164
+ it('persists session after running a phase', async () => {
165
+ const session = createTestSession({ phase: 'Discover' });
166
+ await store.save(session);
167
+
168
+ const io = createScriptedIO(['We are a tech company'], { choice: 'exit' });
169
+ const client = createFakeCopilotClient([
170
+ { role: 'assistant', content: 'Great, I understand your business context.' },
171
+ ]);
172
+
173
+ await runDirectCommand({
174
+ sessionId: session.sessionId,
175
+ phase: 'Discover',
176
+ store,
177
+ client,
178
+ io,
179
+ });
180
+
181
+ const loaded = await store.load(session.sessionId);
182
+ expect(loaded.updatedAt).not.toBe(session.updatedAt);
183
+ expect(loaded.turns!.length).toBeGreaterThan(0);
184
+ });
185
+ });
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Integration test for Discovery Phase Enrichment Flow (T029).
3
+ *
4
+ * Simulates Step 1 completion in the discover handler and verifies:
5
+ * - The user is offered web search enrichment when available
6
+ * - DiscoveryEnricher.enrichFromWebSearch() is called when web search provided
7
+ * - Session is updated with discovery.enrichment
8
+ * - Enrichment data is included in the Ideate phase system prompt
9
+ */
10
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
11
+
12
+ import { createPhaseHandler } from '../../src/phases/phaseHandlers.js';
13
+ import type { WebSearchClient } from '../../src/phases/discoveryEnricher.js';
14
+ import type { LoopIO } from '../../src/loop/conversationLoop.js';
15
+ import type { McpManager } from '../../src/mcp/mcpManager.js';
16
+ import type { WorkshopSession } from '../../src/shared/schemas/session.js';
17
+
18
+ // ── Helpers ──────────────────────────────────────────────────────────────────
19
+
20
+ function makeSession(overrides?: Partial<WorkshopSession>): WorkshopSession {
21
+ return {
22
+ sessionId: 'test-session',
23
+ schemaVersion: '0.3.0',
24
+ createdAt: new Date().toISOString(),
25
+ updatedAt: new Date().toISOString(),
26
+ phase: 'Discover' as const,
27
+ status: 'Active' as const,
28
+ participants: [],
29
+ artifacts: { generatedFiles: [] },
30
+ ...overrides,
31
+ };
32
+ }
33
+
34
+ function makeLoopIO(): LoopIO {
35
+ return {
36
+ write: vi.fn(),
37
+ writeActivity: vi.fn(),
38
+ writeToolSummary: vi.fn(),
39
+ readInput: vi.fn().mockResolvedValue('n'),
40
+ showDecisionGate: vi.fn().mockResolvedValue({ choice: 'continue' }),
41
+ isJsonMode: false,
42
+ isTTY: true,
43
+ };
44
+ }
45
+
46
+ function makeMcpManager(): McpManager {
47
+ return {
48
+ isAvailable: vi.fn().mockReturnValue(false),
49
+ callTool: vi.fn().mockResolvedValue({}),
50
+ markConnected: vi.fn(),
51
+ markDisconnected: vi.fn(),
52
+ disconnectAll: vi.fn().mockResolvedValue(undefined),
53
+ } as unknown as McpManager;
54
+ }
55
+
56
+ // ── Tests ────────────────────────────────────────────────────────────────────
57
+
58
+ describe('Discovery enrichment integration flow (T029)', () => {
59
+ let io: LoopIO;
60
+ let mcpManager: McpManager;
61
+ let webSearchClient: WebSearchClient;
62
+
63
+ beforeEach(() => {
64
+ io = makeLoopIO();
65
+ mcpManager = makeMcpManager();
66
+ webSearchClient = {
67
+ search: vi.fn().mockResolvedValue({
68
+ results: [
69
+ {
70
+ title: 'Acme launches AI',
71
+ url: 'https://news.com/1',
72
+ snippet: 'Acme Corp announced a new AI product',
73
+ },
74
+ ],
75
+ degraded: false,
76
+ }),
77
+ };
78
+ });
79
+
80
+ it('runs enrichment via postExtract when businessContext is available and webSearchClient provided', async () => {
81
+ const handler = createPhaseHandler('Discover', {
82
+ discover: { io, mcpManager, webSearchClient },
83
+ });
84
+ await handler._preload();
85
+
86
+ const session = makeSession({
87
+ businessContext: {
88
+ businessDescription: 'Acme Corp manufactures widgets and is exploring AI',
89
+ challenges: ['efficiency', 'quality control'],
90
+ },
91
+ });
92
+
93
+ // postExtract should trigger enrichment
94
+ const updates = await handler.postExtract!(session);
95
+
96
+ expect(updates.discovery).toBeDefined();
97
+ expect(updates.discovery!.enrichment).toBeDefined();
98
+ expect(updates.discovery!.enrichment!.sourcesUsed).toContain('websearch');
99
+ expect(updates.discovery!.enrichment!.companyNews).toBeDefined();
100
+ expect(updates.discovery!.enrichment!.companyNews!.length).toBeGreaterThan(0);
101
+ expect(webSearchClient.search).toHaveBeenCalled();
102
+ });
103
+
104
+ it('does not run enrichment when businessContext is missing', async () => {
105
+ const handler = createPhaseHandler('Discover', {
106
+ discover: { io, mcpManager, webSearchClient },
107
+ });
108
+ await handler._preload();
109
+
110
+ const session = makeSession(); // no businessContext
111
+
112
+ const updates = await handler.postExtract!(session);
113
+
114
+ expect(updates.discovery).toBeUndefined();
115
+ expect(webSearchClient.search).not.toHaveBeenCalled();
116
+ });
117
+
118
+ it('runs enrichment only once (first extraction)', async () => {
119
+ const handler = createPhaseHandler('Discover', {
120
+ discover: { io, mcpManager, webSearchClient },
121
+ });
122
+ await handler._preload();
123
+
124
+ const session = makeSession({
125
+ businessContext: {
126
+ businessDescription: 'Acme Corp',
127
+ challenges: ['challenge'],
128
+ },
129
+ });
130
+
131
+ // First call: triggers enrichment
132
+ const updates1 = await handler.postExtract!(session);
133
+ expect(updates1.discovery).toBeDefined();
134
+
135
+ // Second call: should not re-trigger
136
+ const updates2 = await handler.postExtract!(session);
137
+ expect(updates2).toEqual({});
138
+ // search is called once (first successful query) in the first call only, not in second
139
+ expect(webSearchClient.search).toHaveBeenCalledTimes(1);
140
+ });
141
+
142
+ it('does not run enrichment when no webSearchClient or WorkIQ available', async () => {
143
+ const handler = createPhaseHandler('Discover', {
144
+ discover: { io, mcpManager },
145
+ });
146
+ await handler._preload();
147
+
148
+ const session = makeSession({
149
+ businessContext: {
150
+ businessDescription: 'Acme Corp',
151
+ challenges: ['challenge'],
152
+ },
153
+ });
154
+
155
+ const updates = await handler.postExtract!(session);
156
+ expect(updates).toEqual({});
157
+ });
158
+
159
+ it('enrichment result stored in session.discovery.enrichment is picked up by Ideate prompt', async () => {
160
+ // Create a session with enrichment already stored
161
+ const session = makeSession({
162
+ phase: 'Ideate',
163
+ businessContext: {
164
+ businessDescription: 'Acme Corp',
165
+ challenges: ['efficiency'],
166
+ },
167
+ discovery: {
168
+ enrichment: {
169
+ companyNews: ['Acme raises $10M Series B'],
170
+ competitorInfo: ['Widget Inc growing fast'],
171
+ industryTrends: ['AI in manufacturing trending'],
172
+ enrichedAt: new Date().toISOString(),
173
+ sourcesUsed: ['websearch'],
174
+ },
175
+ },
176
+ });
177
+
178
+ const ideateHandler = createPhaseHandler('Ideate');
179
+ await ideateHandler._preload();
180
+
181
+ const prompt = ideateHandler.buildSystemPrompt(session);
182
+ // The ideate handler includes businessContext in its prompt
183
+ expect(prompt).toContain('Acme Corp');
184
+ // Note: Enrichment injection into ideation prompt is a downstream task,
185
+ // this test verifies the data is available for injection
186
+ });
187
+
188
+ it('enrichment does not interfere with normal extractResult', async () => {
189
+ const handler = createPhaseHandler('Discover', {
190
+ discover: { io, mcpManager, webSearchClient },
191
+ });
192
+ await handler._preload();
193
+
194
+ const session = makeSession();
195
+
196
+ // Normal extractResult should still work
197
+ const response = `## Business Context
198
+ \`\`\`json
199
+ {
200
+ "businessDescription": "Acme Corp",
201
+ "challenges": ["efficiency"]
202
+ }
203
+ \`\`\``;
204
+
205
+ const updates = handler.extractResult(session, response);
206
+ // extractResult should not contain discovery (that's postExtract's job)
207
+ expect(updates.businessContext).toBeDefined();
208
+ });
209
+ });
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Integration test: Export Artifacts (T037)
3
+ *
4
+ * Tests the export pipeline from session to Markdown + summary.json:
5
+ * - Export a complete session with all phases populated
6
+ * - Verify per-phase Markdown files are generated
7
+ * - Verify summary.json matches contract structure
8
+ * - Verify export handles partial sessions (only some phases done)
9
+ * - Verify export paths and directory creation
10
+ */
11
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
12
+ import { mkdtemp, rm, readFile, readdir } from 'node:fs/promises';
13
+ import { join } from 'node:path';
14
+ import { tmpdir } from 'node:os';
15
+
16
+ import type { WorkshopSession } from '../../src/shared/schemas/session.js';
17
+ import { exportSession } from '../../src/sessions/exportWriter.js';
18
+
19
+ // ── Helpers ──────────────────────────────────────────────────────────────────
20
+
21
+ function createFullSession(): WorkshopSession {
22
+ return {
23
+ sessionId: 'test-export-session',
24
+ schemaVersion: '1.0.0',
25
+ createdAt: '2025-01-01T00:00:00Z',
26
+ updatedAt: '2025-06-15T12:00:00Z',
27
+ phase: 'Complete',
28
+ status: 'Completed',
29
+ participants: [
30
+ { id: 'p1', displayName: 'Alice', role: 'Facilitator' },
31
+ ],
32
+ businessContext: {
33
+ businessDescription: 'Logistics company specializing in last-mile delivery',
34
+ challenges: ['Route optimization is manual', 'High fuel costs'],
35
+ constraints: ['Budget limited to $50k'],
36
+ successMetrics: [{ name: 'Delivery time', value: '30', unit: 'minutes' }],
37
+ },
38
+ workflow: {
39
+ activities: [
40
+ { id: 'a1', name: 'Receive Order', description: 'Customer places order' },
41
+ { id: 'a2', name: 'Plan Route', description: 'Manual route planning' },
42
+ { id: 'a3', name: 'Deliver', description: 'Driver delivers package' },
43
+ ],
44
+ edges: [
45
+ { fromStepId: 'a1', toStepId: 'a2' },
46
+ { fromStepId: 'a2', toStepId: 'a3' },
47
+ ],
48
+ },
49
+ ideas: [
50
+ {
51
+ id: 'i1',
52
+ title: 'AI Route Optimizer',
53
+ description: 'Use ML to find optimal delivery routes',
54
+ workflowStepIds: ['a2'],
55
+ aspirationalScope: 'Cover all urban routes',
56
+ },
57
+ {
58
+ id: 'i2',
59
+ title: 'Predictive Demand',
60
+ description: 'Forecast delivery demand to pre-position drivers',
61
+ workflowStepIds: ['a1', 'a2'],
62
+ },
63
+ ],
64
+ evaluation: {
65
+ method: 'feasibility-value-matrix',
66
+ ideas: [
67
+ { ideaId: 'i1', feasibility: 4, value: 5, risks: ['Data quality'] },
68
+ { ideaId: 'i2', feasibility: 3, value: 4 },
69
+ ],
70
+ },
71
+ selection: {
72
+ ideaId: 'i1',
73
+ selectionRationale: 'Highest combined feasibility + value score',
74
+ confirmedByUser: true,
75
+ confirmedAt: '2025-06-15T11:00:00Z',
76
+ },
77
+ plan: {
78
+ milestones: [
79
+ { id: 'm1', title: 'Data Collection', items: ['Set up telemetry', 'Collect 3 months of delivery data'] },
80
+ { id: 'm2', title: 'Model Training', items: ['Train route model', 'Validate on historical data'] },
81
+ { id: 'm3', title: 'Integration', items: ['API endpoint', 'Driver app integration'] },
82
+ ],
83
+ architectureNotes: 'Microservices with Azure Maps API',
84
+ dependencies: ['Azure subscription', 'Historical delivery data'],
85
+ },
86
+ poc: {
87
+ repoSource: 'local' as const,
88
+ iterations: [
89
+ {
90
+ iteration: 1,
91
+ startedAt: '2025-06-15T11:30:00Z',
92
+ outcome: 'scaffold' as const,
93
+ filesChanged: ['package.json', 'src/index.ts'],
94
+ changesSummary: 'Initial scaffold with simple greedy routing',
95
+ },
96
+ ],
97
+ },
98
+ artifacts: { generatedFiles: [] },
99
+ turns: [
100
+ { phase: 'Discover', sequence: 1, role: 'user', content: 'We are a logistics company', timestamp: '2025-01-01T00:01:00Z' },
101
+ { phase: 'Discover', sequence: 2, role: 'assistant', content: 'Understood, let me document that.', timestamp: '2025-01-01T00:02:00Z' },
102
+ { phase: 'Ideate', sequence: 3, role: 'user', content: 'Generate ideas', timestamp: '2025-01-01T00:03:00Z' },
103
+ ],
104
+ };
105
+ }
106
+
107
+ function createPartialSession(): WorkshopSession {
108
+ return {
109
+ sessionId: 'test-partial-session',
110
+ schemaVersion: '1.0.0',
111
+ createdAt: '2025-01-01T00:00:00Z',
112
+ updatedAt: '2025-01-01T01:00:00Z',
113
+ phase: 'Ideate',
114
+ status: 'Active',
115
+ participants: [],
116
+ businessContext: {
117
+ businessDescription: 'A SaaS platform',
118
+ challenges: ['Customer churn'],
119
+ },
120
+ artifacts: { generatedFiles: [] },
121
+ turns: [],
122
+ };
123
+ }
124
+
125
+ // ── Tests ────────────────────────────────────────────────────────────────────
126
+
127
+ describe('Export Artifacts Flow', () => {
128
+ let tmpDir: string;
129
+
130
+ beforeEach(async () => {
131
+ tmpDir = await mkdtemp(join(tmpdir(), 'sofia-export-'));
132
+ });
133
+
134
+ afterEach(async () => {
135
+ await rm(tmpDir, { recursive: true, force: true });
136
+ });
137
+
138
+ it('exports a complete session with all phase Markdown files', async () => {
139
+ const session = createFullSession();
140
+ const exportDir = join(tmpDir, session.sessionId);
141
+ const result = await exportSession(session, exportDir);
142
+
143
+ expect(result.files.length).toBeGreaterThanOrEqual(6); // discover + ideate + design + select + plan + develop + summary
144
+
145
+ const files = await readdir(exportDir);
146
+ expect(files).toContain('summary.json');
147
+ });
148
+
149
+ it('generates valid summary.json matching contract', async () => {
150
+ const session = createFullSession();
151
+ const exportDir = join(tmpDir, session.sessionId);
152
+ await exportSession(session, exportDir);
153
+
154
+ const summaryRaw = await readFile(join(exportDir, 'summary.json'), 'utf-8');
155
+ const summary = JSON.parse(summaryRaw);
156
+
157
+ expect(summary.sessionId).toBe('test-export-session');
158
+ expect(summary.phase).toBe('Complete');
159
+ expect(summary.status).toBe('Completed');
160
+ expect(summary.exportedAt).toBeDefined();
161
+ expect(Array.isArray(summary.files)).toBe(true);
162
+ expect(summary.files.length).toBeGreaterThan(0);
163
+ });
164
+
165
+ it('exports Discover markdown with business context', async () => {
166
+ const session = createFullSession();
167
+ const exportDir = join(tmpDir, session.sessionId);
168
+ await exportSession(session, exportDir);
169
+
170
+ const files = await readdir(exportDir);
171
+ const discoverFile = files.find((f) => f.includes('discover'));
172
+ expect(discoverFile).toBeDefined();
173
+
174
+ const content = await readFile(join(exportDir, discoverFile!), 'utf-8');
175
+ expect(content).toContain('Logistics company');
176
+ expect(content).toContain('Route optimization');
177
+ });
178
+
179
+ it('exports Ideate markdown with ideas', async () => {
180
+ const session = createFullSession();
181
+ const exportDir = join(tmpDir, session.sessionId);
182
+ await exportSession(session, exportDir);
183
+
184
+ const files = await readdir(exportDir);
185
+ const ideateFile = files.find((f) => f.includes('ideate'));
186
+ expect(ideateFile).toBeDefined();
187
+
188
+ const content = await readFile(join(exportDir, ideateFile!), 'utf-8');
189
+ expect(content).toContain('AI Route Optimizer');
190
+ });
191
+
192
+ it('exports Plan markdown with milestones', async () => {
193
+ const session = createFullSession();
194
+ const exportDir = join(tmpDir, session.sessionId);
195
+ await exportSession(session, exportDir);
196
+
197
+ const files = await readdir(exportDir);
198
+ const planFile = files.find((f) => f.includes('plan'));
199
+ expect(planFile).toBeDefined();
200
+
201
+ const content = await readFile(join(exportDir, planFile!), 'utf-8');
202
+ expect(content).toContain('Data Collection');
203
+ expect(content).toContain('Model Training');
204
+ });
205
+
206
+ it('handles partial session with only Discover data', async () => {
207
+ const session = createPartialSession();
208
+ const exportDir = join(tmpDir, session.sessionId);
209
+ const result = await exportSession(session, exportDir);
210
+
211
+ // Should still have at least discover markdown + summary.json
212
+ expect(result.files.length).toBeGreaterThanOrEqual(2);
213
+
214
+ const files = await readdir(exportDir);
215
+ expect(files).toContain('summary.json');
216
+ const discoverFile = files.find((f) => f.includes('discover'));
217
+ expect(discoverFile).toBeDefined();
218
+ });
219
+
220
+ it('summary file list contains relative paths', async () => {
221
+ const session = createFullSession();
222
+ const exportDir = join(tmpDir, session.sessionId);
223
+ await exportSession(session, exportDir);
224
+
225
+ const summaryRaw = await readFile(join(exportDir, 'summary.json'), 'utf-8');
226
+ const summary = JSON.parse(summaryRaw);
227
+
228
+ for (const file of summary.files) {
229
+ expect(file).not.toContain(tmpDir); // Should be relative, not absolute
230
+ }
231
+ });
232
+ });