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,106 @@
1
+ /**
2
+ * Unit tests for templateRegistry.
3
+ *
4
+ * T033: selectTemplate returns python-pytest for Python plans
5
+ * T034: selectTemplate returns node-ts-vitest for TypeScript plans
6
+ * T035: selectTemplate returns default for ambiguous plans
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+
10
+ import {
11
+ selectTemplate,
12
+ createDefaultRegistry,
13
+ NODE_TS_VITEST_TEMPLATE,
14
+ PYTHON_PYTEST_TEMPLATE,
15
+ } from '../../../src/develop/templateRegistry.js';
16
+
17
+ describe('selectTemplate', () => {
18
+ const registry = createDefaultRegistry();
19
+
20
+ it('returns python-pytest for plans mentioning "Python"', () => {
21
+ const result = selectTemplate(registry, 'Python 3.11 + FastAPI backend');
22
+ expect(result.id).toBe('python-pytest');
23
+ });
24
+
25
+ it('returns python-pytest for plans mentioning "FastAPI"', () => {
26
+ const result = selectTemplate(registry, 'A FastAPI-based REST API');
27
+ expect(result.id).toBe('python-pytest');
28
+ });
29
+
30
+ it('returns python-pytest for plans with "flask" in dependencies', () => {
31
+ const result = selectTemplate(registry, undefined, ['flask', 'redis']);
32
+ expect(result.id).toBe('python-pytest');
33
+ });
34
+
35
+ it('returns python-pytest for plans with "django" dependency', () => {
36
+ const result = selectTemplate(registry, undefined, ['django']);
37
+ expect(result.id).toBe('python-pytest');
38
+ });
39
+
40
+ it('returns node-ts-vitest for plans mentioning "TypeScript"', () => {
41
+ const result = selectTemplate(registry, 'TypeScript + Express backend');
42
+ expect(result.id).toBe('node-ts-vitest');
43
+ });
44
+
45
+ it('returns node-ts-vitest for plans mentioning "Node"', () => {
46
+ const result = selectTemplate(registry, 'Node.js microservice');
47
+ expect(result.id).toBe('node-ts-vitest');
48
+ });
49
+
50
+ it('returns node-ts-vitest for plans with no architecture notes (default)', () => {
51
+ const result = selectTemplate(registry);
52
+ expect(result.id).toBe('node-ts-vitest');
53
+ });
54
+
55
+ it('returns default node-ts-vitest for ambiguous plans', () => {
56
+ const result = selectTemplate(registry, 'A machine learning pipeline using GPT');
57
+ expect(result.id).toBe('node-ts-vitest');
58
+ });
59
+
60
+ it('performs case-insensitive matching', () => {
61
+ const result = selectTemplate(registry, 'PYTHON and FASTAPI');
62
+ expect(result.id).toBe('python-pytest');
63
+ });
64
+ });
65
+
66
+ describe('NODE_TS_VITEST_TEMPLATE', () => {
67
+ it('has correct id and match patterns', () => {
68
+ expect(NODE_TS_VITEST_TEMPLATE.id).toBe('node-ts-vitest');
69
+ expect(NODE_TS_VITEST_TEMPLATE.installCommand).toBe('npm install');
70
+ expect(NODE_TS_VITEST_TEMPLATE.testCommand).toBe('npm test -- --reporter=json');
71
+ expect(NODE_TS_VITEST_TEMPLATE.matchPatterns).toContain('typescript');
72
+ });
73
+
74
+ it('includes .sofia-metadata.json in files', () => {
75
+ const paths = NODE_TS_VITEST_TEMPLATE.files.map((f) => f.path);
76
+ expect(paths).toContain('.sofia-metadata.json');
77
+ });
78
+ });
79
+
80
+ describe('PYTHON_PYTEST_TEMPLATE', () => {
81
+ it('has correct id and match patterns', () => {
82
+ expect(PYTHON_PYTEST_TEMPLATE.id).toBe('python-pytest');
83
+ expect(PYTHON_PYTEST_TEMPLATE.installCommand).toBe('pip install -r requirements.txt');
84
+ expect(PYTHON_PYTEST_TEMPLATE.matchPatterns).toContain('python');
85
+ expect(PYTHON_PYTEST_TEMPLATE.matchPatterns).toContain('fastapi');
86
+ });
87
+
88
+ it('includes required Python files', () => {
89
+ const paths = PYTHON_PYTEST_TEMPLATE.files.map((f) => f.path);
90
+ expect(paths).toContain('requirements.txt');
91
+ expect(paths).toContain('pytest.ini');
92
+ expect(paths).toContain('src/__init__.py');
93
+ expect(paths).toContain('src/main.py');
94
+ expect(paths).toContain('tests/test_main.py');
95
+ expect(paths).toContain('.sofia-metadata.json');
96
+ });
97
+ });
98
+
99
+ describe('createDefaultRegistry', () => {
100
+ it('contains both built-in templates', () => {
101
+ const registry = createDefaultRegistry();
102
+ expect(registry.size).toBe(2);
103
+ expect(registry.has('node-ts-vitest')).toBe(true);
104
+ expect(registry.has('python-pytest')).toBe(true);
105
+ });
106
+ });
@@ -0,0 +1,294 @@
1
+ /**
2
+ * T012: Unit tests for TestRunner.
3
+ *
4
+ * Verifies test runner spawns npm test with --reporter=json,
5
+ * parses JSON output into TestResults schema, handles timeout,
6
+ * handles non-zero exit code, truncates rawOutput.
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+
10
+ import { TestRunner } from '../../../src/develop/testRunner.js';
11
+
12
+ // ── Fake test runner that overrides spawnTests ────────────────────────────────
13
+
14
+ class FakeTestRunner extends TestRunner {
15
+ private fakeOutput: string;
16
+ private fakeTimedOut: boolean;
17
+ private fakeError: Error | null;
18
+
19
+ constructor(
20
+ fakeOutput: string,
21
+ options?: { timedOut?: boolean; error?: Error; timeoutMs?: number },
22
+ ) {
23
+ super({ timeoutMs: options?.timeoutMs ?? 60_000 });
24
+ this.fakeOutput = fakeOutput;
25
+ this.fakeTimedOut = options?.timedOut ?? false;
26
+ this.fakeError = options?.error ?? null;
27
+ }
28
+
29
+ // Override the run method to inject fake output
30
+ async run(_outputDir: string): Promise<import('../../../src/shared/schemas/session.js').TestResults> {
31
+ if (this.fakeError) throw this.fakeError;
32
+
33
+ if (this.fakeTimedOut) {
34
+ return {
35
+ passed: 0,
36
+ failed: 0,
37
+ skipped: 0,
38
+ total: 0,
39
+ durationMs: 60000,
40
+ failures: [],
41
+ rawOutput: `Test runner timed out after 60000ms`,
42
+ };
43
+ }
44
+
45
+ // Use the public run interface but with fake underlying behavior
46
+ // We'll test the parsing logic directly
47
+ const parsed = this.parseOutputPublic(this.fakeOutput, Date.now() - 100, this.fakeOutput);
48
+ return parsed;
49
+ }
50
+
51
+ // Expose internal parsing for testing (parseOutput is protected in TestRunner)
52
+ parseOutputPublic(
53
+ output: string,
54
+ startTime: number,
55
+ rawOutput: string,
56
+ ): import('../../../src/shared/schemas/session.js').TestResults {
57
+ return this.parseOutput(output, startTime, rawOutput);
58
+ }
59
+ }
60
+
61
+ // ── Vitest JSON output fixtures ───────────────────────────────────────────────
62
+
63
+ const PASSING_JSON = JSON.stringify({
64
+ numPassedTests: 3,
65
+ numFailedTests: 0,
66
+ numPendingTests: 0,
67
+ numTotalTests: 3,
68
+ success: true,
69
+ startTime: Date.now() - 500,
70
+ endTime: Date.now(),
71
+ testResults: [
72
+ {
73
+ testFilePath: 'tests/index.test.ts',
74
+ status: 'passed',
75
+ assertionResults: [
76
+ { title: 'should work', fullName: 'suite > should work', status: 'passed', duration: 10 },
77
+ { title: 'test 2', fullName: 'suite > test 2', status: 'passed', duration: 5 },
78
+ { title: 'test 3', fullName: 'suite > test 3', status: 'passed', duration: 8 },
79
+ ],
80
+ },
81
+ ],
82
+ });
83
+
84
+ const FAILING_JSON = JSON.stringify({
85
+ numPassedTests: 1,
86
+ numFailedTests: 2,
87
+ numPendingTests: 0,
88
+ numTotalTests: 3,
89
+ success: false,
90
+ testResults: [
91
+ {
92
+ testFilePath: 'tests/index.test.ts',
93
+ status: 'failed',
94
+ assertionResults: [
95
+ { title: 'should work', fullName: 'suite > should work', status: 'passed', duration: 10 },
96
+ {
97
+ title: 'test 2',
98
+ fullName: 'suite > test 2',
99
+ status: 'failed',
100
+ duration: 5,
101
+ failureMessages: ['Expected 3 but got 5'],
102
+ },
103
+ {
104
+ title: 'test 3',
105
+ fullName: 'suite > test 3',
106
+ status: 'failed',
107
+ duration: 8,
108
+ failureMessages: ['AssertionError: undefined is not a function'],
109
+ },
110
+ ],
111
+ },
112
+ ],
113
+ });
114
+
115
+ const INVALID_OUTPUT = 'This is not JSON output from vitest\nSome stderr text\n';
116
+
117
+ describe('TestRunner', () => {
118
+ describe('JSON output parsing via FakeTestRunner', () => {
119
+ it('parses passing test results', async () => {
120
+ const runner = new FakeTestRunner(PASSING_JSON);
121
+ const result = await runner.run('/fake/dir');
122
+
123
+ expect(result.passed).toBe(3);
124
+ expect(result.failed).toBe(0);
125
+ expect(result.skipped).toBe(0);
126
+ expect(result.total).toBe(3);
127
+ expect(result.failures).toHaveLength(0);
128
+ });
129
+
130
+ it('parses failing test results with failure details', async () => {
131
+ const runner = new FakeTestRunner(FAILING_JSON);
132
+ const result = await runner.run('/fake/dir');
133
+
134
+ expect(result.passed).toBe(1);
135
+ expect(result.failed).toBe(2);
136
+ expect(result.total).toBe(3);
137
+ expect(result.failures).toHaveLength(2);
138
+
139
+ const firstFailure = result.failures[0];
140
+ expect(firstFailure.testName).toContain('test 2');
141
+ expect(firstFailure.message).toContain('Expected 3 but got 5');
142
+ expect(firstFailure.file).toBe('tests/index.test.ts');
143
+ });
144
+
145
+ it('handles non-JSON output gracefully', async () => {
146
+ const runner = new FakeTestRunner(INVALID_OUTPUT);
147
+ const result = await runner.run('/fake/dir');
148
+
149
+ expect(result.passed).toBe(0);
150
+ expect(result.failed).toBe(0);
151
+ expect(result.total).toBe(0);
152
+ expect(result.failures).toHaveLength(0);
153
+ });
154
+
155
+ it('handles timeout by returning zero counts', async () => {
156
+ const runner = new FakeTestRunner('', { timedOut: true, timeoutMs: 60_000 });
157
+ const result = await runner.run('/fake/dir');
158
+
159
+ expect(result.passed).toBe(0);
160
+ expect(result.failed).toBe(0);
161
+ expect(result.rawOutput).toContain('timed out');
162
+ });
163
+
164
+ it('truncates rawOutput to 2000 chars', async () => {
165
+ const longOutput = PASSING_JSON + '\n' + 'x'.repeat(5000);
166
+ const runner = new FakeTestRunner(longOutput);
167
+ const result = await runner.run('/fake/dir');
168
+
169
+ expect(result.rawOutput).toBeDefined();
170
+ expect(result.rawOutput!.length).toBeLessThanOrEqual(2000);
171
+ });
172
+
173
+ it('limits failures to 10 max', async () => {
174
+ // Create a JSON with 15 failures
175
+ const manyFailures = {
176
+ numPassedTests: 0,
177
+ numFailedTests: 15,
178
+ numPendingTests: 0,
179
+ numTotalTests: 15,
180
+ success: false,
181
+ testResults: [
182
+ {
183
+ testFilePath: 'tests/index.test.ts',
184
+ status: 'failed',
185
+ assertionResults: Array.from({ length: 15 }, (_, i) => ({
186
+ title: `test ${i}`,
187
+ fullName: `suite > test ${i}`,
188
+ status: 'failed',
189
+ failureMessages: [`Error in test ${i}`],
190
+ })),
191
+ },
192
+ ],
193
+ };
194
+
195
+ const runner = new FakeTestRunner(JSON.stringify(manyFailures));
196
+ const result = await runner.run('/fake/dir');
197
+
198
+ expect(result.failures.length).toBeLessThanOrEqual(10);
199
+ });
200
+
201
+ it('handles non-zero exit code with partial JSON', async () => {
202
+ // Mix of stderr text and JSON (vitest may output to stderr)
203
+ const mixedOutput = `Error: Test failed\n${FAILING_JSON}\n`;
204
+ const runner = new FakeTestRunner(mixedOutput);
205
+ const result = await runner.run('/fake/dir');
206
+
207
+ // Should find JSON in the mixed output
208
+ expect(result.total).toBe(3);
209
+ });
210
+ });
211
+
212
+ describe('TestRunner constructor', () => {
213
+ it('uses default timeout of 60000ms', () => {
214
+ const runner = new TestRunner();
215
+ // The timeout is used internally; we can't directly inspect it,
216
+ // but we verify the runner is instantiated correctly
217
+ expect(runner).toBeDefined();
218
+ });
219
+
220
+ it('accepts custom timeout', () => {
221
+ const runner = new TestRunner({ timeoutMs: 30_000 });
222
+ expect(runner).toBeDefined();
223
+ });
224
+
225
+ it('accepts custom testCommand', () => {
226
+ const runner = new TestRunner({ testCommand: 'pytest --tb=short' });
227
+ expect(runner).toBeDefined();
228
+ });
229
+ });
230
+
231
+ // ── T045: extractJson fallback path ─────────────────────────────────────
232
+
233
+ describe('extractJson fallback path (T045)', () => {
234
+ it('extracts JSON from mixed console + JSON output', () => {
235
+ const mixed = `
236
+ Some console output here
237
+ Warning: something happened
238
+ ${PASSING_JSON}
239
+ More console output after
240
+ `;
241
+ const runner = new FakeTestRunner(mixed);
242
+ const result = runner.parseOutputPublic(mixed, Date.now() - 100, mixed);
243
+ expect(result.passed).toBe(3);
244
+ expect(result.failed).toBe(0);
245
+ expect(result.total).toBe(3);
246
+ });
247
+
248
+ it('uses first-brace to last-brace fallback when JSON spans mixed output', () => {
249
+ // Construct output where JSON is embedded in non-JSON text
250
+ const embedded = `npm warn something\n{"numPassedTests":1,"numFailedTests":0,"numPendingTests":0,"numTotalTests":1,"success":true,"testResults":[]}\nDone`;
251
+ const runner = new FakeTestRunner(embedded);
252
+ const result = runner.parseOutputPublic(embedded, Date.now() - 100, embedded);
253
+ expect(result.passed).toBe(1);
254
+ expect(result.total).toBe(1);
255
+ });
256
+ });
257
+
258
+ // ── T046: extractJson returns null for no valid JSON ────────────────────
259
+
260
+ describe('extractJson null return (T046)', () => {
261
+ it('returns zero-count result for output with no valid JSON', () => {
262
+ const noJson = 'This is just plain text output\nNo JSON here at all\nERROR: something failed';
263
+ const runner = new FakeTestRunner(noJson);
264
+ const result = runner.parseOutputPublic(noJson, Date.now() - 100, noJson);
265
+ expect(result.passed).toBe(0);
266
+ expect(result.failed).toBe(0);
267
+ expect(result.total).toBe(0);
268
+ });
269
+ });
270
+
271
+ // ── T047: buildErrorResult ──────────────────────────────────────────────
272
+
273
+ describe('buildErrorResult (T047)', () => {
274
+ it('produces correct zero-count result with error message on timeout', () => {
275
+ const runner = new FakeTestRunner('', { timedOut: true });
276
+ // Run returns timeout result
277
+ const resultPromise = runner.run('/tmp/fake');
278
+ return resultPromise.then((result) => {
279
+ expect(result.passed).toBe(0);
280
+ expect(result.failed).toBe(0);
281
+ expect(result.skipped).toBe(0);
282
+ expect(result.total).toBe(0);
283
+ expect(result.rawOutput).toContain('timed out');
284
+ });
285
+ });
286
+
287
+ it('produces correct zero-count result with error message on spawn error', () => {
288
+ const runner = new FakeTestRunner('', { error: new Error('ENOENT: npm not found') });
289
+ return runner.run('/tmp/fake').catch((err: Error) => {
290
+ expect(err.message).toContain('ENOENT');
291
+ });
292
+ });
293
+ });
294
+ });
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Bicep template structure tests (T008, T013, T014).
3
+ *
4
+ * Validates the Bicep template:
5
+ * - Contains all 5 expected resource types
6
+ * - All parameters have @description() decorators (T013)
7
+ * - Parameters have correct defaults per data-model.md (T014)
8
+ */
9
+ import { describe, it, expect } from 'vitest';
10
+ import { readFileSync } from 'node:fs';
11
+ import { resolve } from 'node:path';
12
+
13
+ const BICEP_PATH = resolve(__dirname, '../../infra/main.bicep');
14
+ const RESOURCES_BICEP_PATH = resolve(__dirname, '../../infra/resources.bicep');
15
+ const BICEPPARAM_PATH = resolve(__dirname, '../../infra/main.bicepparam');
16
+
17
+ describe('Bicep template structure (T008)', () => {
18
+ const bicep = readFileSync(BICEP_PATH, 'utf-8');
19
+ const resourcesBicep = readFileSync(RESOURCES_BICEP_PATH, 'utf-8');
20
+ const allBicep = bicep + '\n' + resourcesBicep;
21
+
22
+ describe('required resource types', () => {
23
+ it('defines a Cognitive Services account (AIServices)', () => {
24
+ expect(allBicep).toContain("Microsoft.CognitiveServices/accounts");
25
+ expect(allBicep).toMatch(/kind:\s*'AIServices'/);
26
+ });
27
+
28
+ it('defines a model deployment', () => {
29
+ expect(allBicep).toContain("Microsoft.CognitiveServices/accounts/deployments");
30
+ });
31
+
32
+ it('defines a project', () => {
33
+ expect(allBicep).toContain("Microsoft.CognitiveServices/accounts/projects");
34
+ });
35
+
36
+ it('defines an account-level capability host', () => {
37
+ expect(allBicep).toContain("Microsoft.CognitiveServices/accounts/capabilityHosts");
38
+ });
39
+
40
+ it('defines a project-level capability host', () => {
41
+ expect(allBicep).toContain("Microsoft.CognitiveServices/accounts/projects/capabilityHosts");
42
+ });
43
+
44
+ it('uses subscription target scope', () => {
45
+ expect(allBicep).toMatch(/targetScope\s*=\s*'subscription'/);
46
+ });
47
+ });
48
+
49
+ describe('resource configuration', () => {
50
+ it('enables project management on the account', () => {
51
+ expect(allBicep).toContain('allowProjectManagement');
52
+ });
53
+
54
+ it('configures custom subdomain name', () => {
55
+ expect(allBicep).toContain('customSubDomainName');
56
+ });
57
+
58
+ it('uses GlobalStandard SKU for model deployment', () => {
59
+ expect(allBicep).toContain('GlobalStandard');
60
+ });
61
+
62
+ it('configures Agents capability kind', () => {
63
+ expect(allBicep).toContain("'Agents'");
64
+ });
65
+ });
66
+ });
67
+
68
+ describe('Bicep parameter descriptions (T013)', () => {
69
+ const bicep = readFileSync(BICEP_PATH, 'utf-8');
70
+
71
+ it('every param in main.bicep has a preceding @description() decorator', () => {
72
+ // Extract all param declarations
73
+ const paramLines = bicep.split('\n');
74
+ const paramIndices = paramLines
75
+ .map((line, idx) => ({ line: line.trim(), idx }))
76
+ .filter(({ line }) => line.startsWith('param '));
77
+
78
+ expect(paramIndices.length).toBeGreaterThan(0);
79
+
80
+ for (const { line, idx } of paramIndices) {
81
+ // Check that at least one of the preceding lines (within 5 lines) has @description
82
+ const precedingLines = paramLines.slice(Math.max(0, idx - 5), idx).join('\n');
83
+ const paramName = line.split(/\s+/)[1];
84
+ expect(
85
+ precedingLines,
86
+ `Parameter '${paramName}' at line ${idx + 1} should have a preceding @description() decorator`,
87
+ ).toMatch(/@description\(/);
88
+ }
89
+ });
90
+ });
91
+
92
+ describe('Bicep parameter defaults (T014)', () => {
93
+ const bicep = readFileSync(BICEP_PATH, 'utf-8');
94
+ const bicepparam = readFileSync(BICEPPARAM_PATH, 'utf-8');
95
+
96
+ it('defaults location to swedencentral', () => {
97
+ // Check either in bicep or bicepparam
98
+ const combined = bicep + bicepparam;
99
+ expect(combined).toContain('swedencentral');
100
+ });
101
+
102
+ it('defaults model deployment name to gpt-4.1-mini', () => {
103
+ const combined = bicep + bicepparam;
104
+ expect(combined).toContain('gpt-4.1-mini');
105
+ });
106
+
107
+ it('defaults model version to 2025-04-14', () => {
108
+ const combined = bicep + bicepparam;
109
+ expect(combined).toContain('2025-04-14');
110
+ });
111
+
112
+ it('defaults model SKU to GlobalStandard', () => {
113
+ const combined = bicep + bicepparam;
114
+ expect(combined).toContain('GlobalStandard');
115
+ });
116
+ });
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Deploy script tests (T007).
3
+ *
4
+ * Tests for infra/deploy.sh:
5
+ * - Parameter parsing (subscription, resource-group, location, account-name, model)
6
+ * - Default values for optional parameters
7
+ * - Missing required arguments
8
+ * - Script file properties (executable, shebang, error handling)
9
+ */
10
+ import { describe, it, expect } from 'vitest';
11
+ import { readFileSync, accessSync, constants } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+
14
+ const DEPLOY_SCRIPT_PATH = resolve(__dirname, '../../infra/deploy.sh');
15
+
16
+ describe('deploy.sh (T007)', () => {
17
+ const script = readFileSync(DEPLOY_SCRIPT_PATH, 'utf-8');
18
+
19
+ describe('script file properties', () => {
20
+ it('has bash shebang', () => {
21
+ expect(script.startsWith('#!/usr/bin/env bash')).toBe(true);
22
+ });
23
+
24
+ it('is executable', () => {
25
+ expect(() => accessSync(DEPLOY_SCRIPT_PATH, constants.X_OK)).not.toThrow();
26
+ });
27
+
28
+ it('uses strict error handling (set -euo pipefail)', () => {
29
+ expect(script).toContain('set -euo pipefail');
30
+ });
31
+ });
32
+
33
+ describe('parameter parsing', () => {
34
+ it('supports --subscription / -s flag', () => {
35
+ expect(script).toMatch(/--subscription\b|-s\b/);
36
+ });
37
+
38
+ it('supports --resource-group / -g flag', () => {
39
+ expect(script).toMatch(/--resource-group\b|-g\b/);
40
+ });
41
+
42
+ it('supports --location / -l flag', () => {
43
+ expect(script).toMatch(/--location\b|-l\b/);
44
+ });
45
+
46
+ it('supports --account-name / -n flag', () => {
47
+ expect(script).toMatch(/--account-name\b|-n\b/);
48
+ });
49
+
50
+ it('supports --model / -m flag', () => {
51
+ expect(script).toMatch(/--model\b|-m\b/);
52
+ });
53
+ });
54
+
55
+ describe('default values', () => {
56
+ it('defaults location to swedencentral', () => {
57
+ expect(script).toContain('swedencentral');
58
+ });
59
+
60
+ it('defaults account name to sofia-foundry', () => {
61
+ expect(script).toContain('sofia-foundry');
62
+ });
63
+
64
+ it('defaults model to gpt-4.1-mini', () => {
65
+ expect(script).toContain('gpt-4.1-mini');
66
+ });
67
+ });
68
+
69
+ describe('prerequisite validation', () => {
70
+ it('checks for az CLI installation', () => {
71
+ expect(script).toMatch(/command -v az|which az/);
72
+ });
73
+
74
+ it('checks for Azure login status', () => {
75
+ expect(script).toContain('az account show');
76
+ });
77
+ });
78
+
79
+ describe('exit codes', () => {
80
+ it('uses exit code 1 for prerequisite failures', () => {
81
+ expect(script).toContain('exit 1');
82
+ });
83
+
84
+ it('uses exit code 2 for deployment failures', () => {
85
+ expect(script).toContain('exit 2');
86
+ });
87
+ });
88
+
89
+ describe('output', () => {
90
+ it('outputs FOUNDRY_PROJECT_ENDPOINT env var instruction', () => {
91
+ expect(script).toContain('FOUNDRY_PROJECT_ENDPOINT');
92
+ });
93
+
94
+ it('outputs FOUNDRY_MODEL_DEPLOYMENT_NAME env var instruction', () => {
95
+ expect(script).toContain('FOUNDRY_MODEL_DEPLOYMENT_NAME');
96
+ });
97
+
98
+ it('references teardown script', () => {
99
+ expect(script).toContain('teardown.sh');
100
+ });
101
+ });
102
+ });
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Teardown script tests (T032).
3
+ *
4
+ * Tests for infra/teardown.sh:
5
+ * - Required --resource-group flag
6
+ * - Script file properties (executable, shebang, error handling)
7
+ * - Exit codes (0=success/not-found, 1=prereq fail, 2=deletion fail)
8
+ * - Handles non-existent resource group gracefully
9
+ * - Supports --yes flag for non-interactive confirmation
10
+ */
11
+ import { describe, it, expect } from 'vitest';
12
+ import { readFileSync, accessSync, constants } from 'node:fs';
13
+ import { resolve } from 'node:path';
14
+
15
+ const TEARDOWN_SCRIPT_PATH = resolve(__dirname, '../../infra/teardown.sh');
16
+
17
+ describe('teardown.sh (T032)', () => {
18
+ const script = readFileSync(TEARDOWN_SCRIPT_PATH, 'utf-8');
19
+
20
+ describe('script file properties', () => {
21
+ it('has bash shebang', () => {
22
+ expect(script.startsWith('#!/usr/bin/env bash')).toBe(true);
23
+ });
24
+
25
+ it('is executable', () => {
26
+ expect(() => accessSync(TEARDOWN_SCRIPT_PATH, constants.X_OK)).not.toThrow();
27
+ });
28
+
29
+ it('uses strict error handling (set -euo pipefail)', () => {
30
+ expect(script).toContain('set -euo pipefail');
31
+ });
32
+ });
33
+
34
+ describe('parameter parsing', () => {
35
+ it('supports --resource-group / -g flag', () => {
36
+ expect(script).toMatch(/--resource-group\b|-g\b/);
37
+ });
38
+
39
+ it('supports --yes flag for non-interactive confirmation', () => {
40
+ expect(script).toMatch(/--yes\b/);
41
+ });
42
+ });
43
+
44
+ describe('prerequisite validation', () => {
45
+ it('checks for az CLI installation', () => {
46
+ expect(script).toMatch(/command -v az|which az/);
47
+ });
48
+ });
49
+
50
+ describe('exit codes', () => {
51
+ it('uses exit code 0 for success or not-found', () => {
52
+ expect(script).toContain('exit 0');
53
+ });
54
+
55
+ it('uses exit code 1 for prerequisite failures', () => {
56
+ expect(script).toContain('exit 1');
57
+ });
58
+
59
+ it('uses exit code 2 for deletion failures', () => {
60
+ expect(script).toContain('exit 2');
61
+ });
62
+ });
63
+
64
+ describe('resource group handling', () => {
65
+ it('checks if resource group exists', () => {
66
+ expect(script).toContain('az group exists');
67
+ });
68
+
69
+ it('uses az group delete for deletion', () => {
70
+ expect(script).toContain('az group delete');
71
+ });
72
+
73
+ it('uses --no-wait for non-blocking deletion', () => {
74
+ expect(script).toContain('--no-wait');
75
+ });
76
+ });
77
+ });