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.
- package/.github/agents/copilot-instructions.md +39 -0
- package/.github/agents/speckit.analyze.agent.md +184 -0
- package/.github/agents/speckit.checklist.agent.md +294 -0
- package/.github/agents/speckit.clarify.agent.md +181 -0
- package/.github/agents/speckit.constitution.agent.md +84 -0
- package/.github/agents/speckit.implement.agent.md +135 -0
- package/.github/agents/speckit.plan.agent.md +90 -0
- package/.github/agents/speckit.specify.agent.md +258 -0
- package/.github/agents/speckit.tasks.agent.md +137 -0
- package/.github/agents/speckit.taskstoissues.agent.md +30 -0
- package/.github/copilot-instructions.md +257 -0
- package/.github/prompts/speckit.analyze.prompt.md +3 -0
- package/.github/prompts/speckit.checklist.prompt.md +3 -0
- package/.github/prompts/speckit.clarify.prompt.md +3 -0
- package/.github/prompts/speckit.constitution.prompt.md +3 -0
- package/.github/prompts/speckit.implement.prompt.md +3 -0
- package/.github/prompts/speckit.plan.prompt.md +3 -0
- package/.github/prompts/speckit.specify.prompt.md +3 -0
- package/.github/prompts/speckit.tasks.prompt.md +3 -0
- package/.github/prompts/speckit.taskstoissues.prompt.md +3 -0
- package/.github/workflows/ci.yml +38 -0
- package/.prettierrc +6 -0
- package/.specify/memory/constitution.md +181 -0
- package/.specify/scripts/bash/check-prerequisites.sh +166 -0
- package/.specify/scripts/bash/common.sh +156 -0
- package/.specify/scripts/bash/create-new-feature.sh +297 -0
- package/.specify/scripts/bash/setup-plan.sh +61 -0
- package/.specify/scripts/bash/update-agent-context.sh +810 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/constitution-template.md +50 -0
- package/.specify/templates/plan-template.md +113 -0
- package/.specify/templates/spec-template.md +115 -0
- package/.specify/templates/tasks-template.md +251 -0
- package/.vscode/mcp.json +42 -0
- package/.vscode/settings.json +19 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/LICENSE +21 -0
- package/README.md +213 -0
- package/dist/src/cli/developCommand.js +240 -0
- package/dist/src/cli/directCommands.js +143 -0
- package/dist/src/cli/envLoader.js +16 -0
- package/dist/src/cli/exportCommand.js +53 -0
- package/dist/src/cli/index.js +203 -0
- package/dist/src/cli/ioContext.js +109 -0
- package/dist/src/cli/preflight.js +57 -0
- package/dist/src/cli/statusCommand.js +110 -0
- package/dist/src/cli/workshopCommand.js +400 -0
- package/dist/src/develop/checkpointState.js +86 -0
- package/dist/src/develop/codeGenerator.js +319 -0
- package/dist/src/develop/dynamicScaffolder.js +226 -0
- package/dist/src/develop/githubMcpAdapter.js +122 -0
- package/dist/src/develop/index.js +15 -0
- package/dist/src/develop/mcpContextEnricher.js +195 -0
- package/dist/src/develop/pocScaffolder.js +542 -0
- package/dist/src/develop/ralphLoop.js +659 -0
- package/dist/src/develop/templateRegistry.js +364 -0
- package/dist/src/develop/testRunner.js +202 -0
- package/dist/src/logging/logger.js +58 -0
- package/dist/src/loop/conversationLoop.js +227 -0
- package/dist/src/loop/phaseSummarizer.js +87 -0
- package/dist/src/mcp/mcpManager.js +267 -0
- package/dist/src/mcp/mcpTransport.js +391 -0
- package/dist/src/mcp/retryPolicy.js +47 -0
- package/dist/src/mcp/webSearch.js +254 -0
- package/dist/src/phases/contextSummarizer.js +101 -0
- package/dist/src/phases/discoveryEnricher.js +156 -0
- package/dist/src/phases/phaseExtractors.js +222 -0
- package/dist/src/phases/phaseHandlers.js +328 -0
- package/dist/src/prompts/design.md +51 -0
- package/dist/src/prompts/develop-boundary.md +51 -0
- package/dist/src/prompts/develop.md +111 -0
- package/dist/src/prompts/discover.md +58 -0
- package/dist/src/prompts/ideate.md +56 -0
- package/dist/src/prompts/plan.md +51 -0
- package/dist/src/prompts/promptLoader.js +167 -0
- package/dist/src/prompts/promptLoader.ts +198 -0
- package/dist/src/prompts/select.md +47 -0
- package/dist/src/prompts/summarize/README.md +8 -0
- package/dist/src/prompts/summarize/design-summary.md +37 -0
- package/dist/src/prompts/summarize/develop-summary.md +25 -0
- package/dist/src/prompts/summarize/ideate-summary.md +27 -0
- package/dist/src/prompts/summarize/plan-summary.md +27 -0
- package/dist/src/prompts/summarize/select-summary.md +21 -0
- package/dist/src/prompts/system.md +28 -0
- package/dist/src/sessions/exportPaths.js +22 -0
- package/dist/src/sessions/exportWriter.js +406 -0
- package/dist/src/sessions/sessionManager.js +81 -0
- package/dist/src/sessions/sessionStore.js +65 -0
- package/dist/src/shared/activitySpinner.js +91 -0
- package/dist/src/shared/copilotClient.js +129 -0
- package/dist/src/shared/data/cards.json +1249 -0
- package/dist/src/shared/data/cardsLoader.js +51 -0
- package/dist/src/shared/errorClassifier.js +120 -0
- package/dist/src/shared/events.js +28 -0
- package/dist/src/shared/markdownRenderer.js +34 -0
- package/dist/src/shared/schemas/session.js +265 -0
- package/dist/src/shared/tableRenderer.js +20 -0
- package/dist/src/vendor/chalk.js +2 -0
- package/dist/src/vendor/cli-table3.js +3 -0
- package/dist/src/vendor/commander.js +2 -0
- package/dist/src/vendor/marked-terminal.js +3 -0
- package/dist/src/vendor/marked.js +2 -0
- package/dist/src/vendor/ora.js +2 -0
- package/dist/src/vendor/pino.js +2 -0
- package/dist/src/vendor/zod.js +2 -0
- package/dist/tests/e2e/developE2e.spec.js +126 -0
- package/dist/tests/e2e/developFailureE2e.spec.js +247 -0
- package/dist/tests/e2e/developPty.spec.js +75 -0
- package/dist/tests/e2e/discoveryWebSearchRelevance.spec.js +84 -0
- package/dist/tests/e2e/harness.spec.js +83 -0
- package/dist/tests/e2e/mcpLive.spec.js +120 -0
- package/dist/tests/e2e/newSession.e2e.spec.js +177 -0
- package/dist/tests/e2e/ralphLoopEnrichmentComparison.spec.js +62 -0
- package/dist/tests/e2e/workiqEnrichment.spec.js +56 -0
- package/dist/tests/e2e/zavaSimulation.spec.js +452 -0
- package/dist/tests/fixtures/test-fixture-project/src/add.js +3 -0
- package/dist/tests/fixtures/test-fixture-project/tests/failing.test.js +6 -0
- package/dist/tests/fixtures/test-fixture-project/tests/hanging.test.js +8 -0
- package/dist/tests/fixtures/test-fixture-project/tests/passing.test.js +10 -0
- package/dist/tests/fixtures/test-fixture-project/vitest.config.js +6 -0
- package/dist/tests/integration/autoStartConversation.spec.js +138 -0
- package/dist/tests/integration/defaultCommand.spec.js +147 -0
- package/dist/tests/integration/directCommandNonTty.spec.js +224 -0
- package/dist/tests/integration/directCommandTty.spec.js +151 -0
- package/dist/tests/integration/discoveryEnrichmentFlow.spec.js +175 -0
- package/dist/tests/integration/exportArtifacts.spec.js +202 -0
- package/dist/tests/integration/exportFallbackFlow.spec.js +99 -0
- package/dist/tests/integration/mcpDegradationFlow.spec.js +190 -0
- package/dist/tests/integration/mcpTransportFlow.spec.js +139 -0
- package/dist/tests/integration/newSessionFlow.spec.js +343 -0
- package/dist/tests/integration/pocGithubMcp.spec.js +186 -0
- package/dist/tests/integration/pocLocalFallback.spec.js +171 -0
- package/dist/tests/integration/pocScaffold.spec.js +163 -0
- package/dist/tests/integration/ralphLoopFlow.spec.js +359 -0
- package/dist/tests/integration/ralphLoopPartial.spec.js +368 -0
- package/dist/tests/integration/resumeAndBacktrack.spec.js +247 -0
- package/dist/tests/integration/spinnerLifecycle.spec.js +220 -0
- package/dist/tests/integration/summarizationFlow.spec.js +115 -0
- package/dist/tests/integration/testRunnerReal.spec.js +52 -0
- package/dist/tests/integration/webSearchAgent.spec.js +128 -0
- package/dist/tests/live/copilotSdkLive.spec.js +107 -0
- package/dist/tests/live/zavaFullWorkshop.spec.js +392 -0
- package/dist/tests/setup/loadEnv.js +3 -0
- package/dist/tests/unit/cli/developCommand.spec.js +567 -0
- package/dist/tests/unit/cli/directCommands.spec.js +279 -0
- package/dist/tests/unit/cli/envLoader.spec.js +58 -0
- package/dist/tests/unit/cli/ioContext.spec.js +119 -0
- package/dist/tests/unit/cli/preflight.spec.js +108 -0
- package/dist/tests/unit/cli/statusCommand.spec.js +111 -0
- package/dist/tests/unit/cli/workshopClientFallback.spec.js +80 -0
- package/dist/tests/unit/cli/workshopCommand.spec.js +329 -0
- package/dist/tests/unit/config/vitestEnvSetup.spec.js +13 -0
- package/dist/tests/unit/develop/checkpointState.spec.js +315 -0
- package/dist/tests/unit/develop/codeGenerator.spec.js +355 -0
- package/dist/tests/unit/develop/githubMcpAdapter.spec.js +231 -0
- package/dist/tests/unit/develop/mcpContextEnricher.spec.js +433 -0
- package/dist/tests/unit/develop/outputValidator.spec.js +119 -0
- package/dist/tests/unit/develop/pocScaffolder.spec.js +353 -0
- package/dist/tests/unit/develop/ralphLoop.spec.js +1248 -0
- package/dist/tests/unit/develop/templateRegistry.spec.js +85 -0
- package/dist/tests/unit/develop/testRunner.spec.js +249 -0
- package/dist/tests/unit/infraBicep.spec.js +92 -0
- package/dist/tests/unit/infraDeploy.spec.js +82 -0
- package/dist/tests/unit/infraTeardown.spec.js +63 -0
- package/dist/tests/unit/logging/logger.spec.js +43 -0
- package/dist/tests/unit/loop/conversationLoop.spec.js +592 -0
- package/dist/tests/unit/loop/phaseSummarizer.spec.js +141 -0
- package/dist/tests/unit/loop/streamingMarkdown.spec.js +147 -0
- package/dist/tests/unit/mcp/mcpManager.spec.js +279 -0
- package/dist/tests/unit/mcp/mcpTransport.spec.js +529 -0
- package/dist/tests/unit/mcp/retryPolicy.spec.js +218 -0
- package/dist/tests/unit/mcp/timeoutValidation.spec.js +46 -0
- package/dist/tests/unit/mcp/webSearch.spec.js +567 -0
- package/dist/tests/unit/phases/contextSummarizer.spec.js +140 -0
- package/dist/tests/unit/phases/discoveryEnricher.repeatCalls.spec.js +93 -0
- package/dist/tests/unit/phases/discoveryEnricher.spec.js +411 -0
- package/dist/tests/unit/phases/phaseExtractors.spec.js +352 -0
- package/dist/tests/unit/phases/phaseHandlers.spec.js +425 -0
- package/dist/tests/unit/prompts/promptLoader.spec.js +118 -0
- package/dist/tests/unit/schemas/pocSchemas.spec.js +412 -0
- package/dist/tests/unit/schemas/session.spec.js +257 -0
- package/dist/tests/unit/sessions/exportPaths.spec.js +31 -0
- package/dist/tests/unit/sessions/exportWriter.spec.js +655 -0
- package/dist/tests/unit/sessions/sessionManager.spec.js +151 -0
- package/dist/tests/unit/sessions/sessionStore.spec.js +116 -0
- package/dist/tests/unit/shared/activitySpinner.spec.js +175 -0
- package/dist/tests/unit/shared/cardsLoader.spec.js +76 -0
- package/dist/tests/unit/shared/copilotClient.spec.js +155 -0
- package/dist/tests/unit/shared/errorClassifier.spec.js +131 -0
- package/dist/tests/unit/shared/events.spec.js +55 -0
- package/dist/tests/unit/shared/markdownRenderer.spec.js +35 -0
- package/dist/tests/unit/shared/markdownRendererChunks.spec.js +70 -0
- package/dist/tests/unit/shared/tableRenderer.spec.js +34 -0
- package/dist/vitest.config.js +14 -0
- package/dist/vitest.live.config.js +18 -0
- package/docs/README.md +35 -0
- package/docs/architecture.md +169 -0
- package/docs/cli-usage.md +207 -0
- package/docs/environment.md +66 -0
- package/docs/export-format.md +146 -0
- package/docs/session-model.md +113 -0
- package/eslint.config.js +35 -0
- package/infra/deploy.sh +193 -0
- package/infra/gather-env.sh +211 -0
- package/infra/main.bicep +90 -0
- package/infra/main.bicepparam +18 -0
- package/infra/resources.bicep +134 -0
- package/infra/teardown.sh +114 -0
- package/package.json +63 -0
- package/specs/001-cli-workshop-rebuild/checklists/requirements.md +35 -0
- package/specs/001-cli-workshop-rebuild/contracts/cli.md +59 -0
- package/specs/001-cli-workshop-rebuild/contracts/export-summary-json.md +23 -0
- package/specs/001-cli-workshop-rebuild/contracts/session-json.md +30 -0
- package/specs/001-cli-workshop-rebuild/data-model.md +210 -0
- package/specs/001-cli-workshop-rebuild/plan.md +361 -0
- package/specs/001-cli-workshop-rebuild/quickstart.md +83 -0
- package/specs/001-cli-workshop-rebuild/research.md +116 -0
- package/specs/001-cli-workshop-rebuild/spec.md +240 -0
- package/specs/001-cli-workshop-rebuild/tasks.md +476 -0
- package/specs/002-poc-generation/contracts/poc-output.md +172 -0
- package/specs/002-poc-generation/contracts/ralph-loop.md +113 -0
- package/specs/002-poc-generation/data-model.md +172 -0
- package/specs/002-poc-generation/plan.md +109 -0
- package/specs/002-poc-generation/quickstart.md +97 -0
- package/specs/002-poc-generation/research.md +786 -0
- package/specs/002-poc-generation/spec.md +81 -0
- package/specs/002-poc-generation/tasks-fix.md +198 -0
- package/specs/002-poc-generation/tasks.md +252 -0
- package/specs/003-mcp-transport-integration/checklists/requirements.md +37 -0
- package/specs/003-mcp-transport-integration/contracts/context-enricher.md +220 -0
- package/specs/003-mcp-transport-integration/contracts/discovery-enricher.md +267 -0
- package/specs/003-mcp-transport-integration/contracts/github-adapter.md +149 -0
- package/specs/003-mcp-transport-integration/contracts/mcp-transport.md +288 -0
- package/specs/003-mcp-transport-integration/data-model.md +326 -0
- package/specs/003-mcp-transport-integration/plan.md +114 -0
- package/specs/003-mcp-transport-integration/quickstart.md +311 -0
- package/specs/003-mcp-transport-integration/research.md +395 -0
- package/specs/003-mcp-transport-integration/spec.md +234 -0
- package/specs/003-mcp-transport-integration/tasks.md +324 -0
- package/specs/003-next-spec-gaps.md +150 -0
- package/specs/004-dev-resume-hardening/checklists/requirements.md +37 -0
- package/specs/004-dev-resume-hardening/contracts/cli.md +160 -0
- package/specs/004-dev-resume-hardening/data-model.md +321 -0
- package/specs/004-dev-resume-hardening/plan.md +107 -0
- package/specs/004-dev-resume-hardening/quickstart.md +115 -0
- package/specs/004-dev-resume-hardening/research.md +142 -0
- package/specs/004-dev-resume-hardening/spec.md +221 -0
- package/specs/004-dev-resume-hardening/tasks.md +333 -0
- package/specs/005-ai-search-deploy/checklists/requirements.md +39 -0
- package/specs/005-ai-search-deploy/contracts/web-search-tool.md +241 -0
- package/specs/005-ai-search-deploy/data-model.md +130 -0
- package/specs/005-ai-search-deploy/plan.md +93 -0
- package/specs/005-ai-search-deploy/quickstart.md +96 -0
- package/specs/005-ai-search-deploy/research.md +187 -0
- package/specs/005-ai-search-deploy/spec.md +143 -0
- package/specs/005-ai-search-deploy/tasks.md +284 -0
- package/specs/006-workshop-extraction-fixes/checklists/requirements.md +61 -0
- package/specs/006-workshop-extraction-fixes/contracts/summarization-and-export.md +131 -0
- package/specs/006-workshop-extraction-fixes/data-model.md +149 -0
- package/specs/006-workshop-extraction-fixes/plan.md +123 -0
- package/specs/006-workshop-extraction-fixes/quickstart.md +101 -0
- package/specs/006-workshop-extraction-fixes/research.md +143 -0
- package/specs/006-workshop-extraction-fixes/spec.md +210 -0
- package/specs/006-workshop-extraction-fixes/tasks.md +316 -0
- package/src/cli/developCommand.ts +308 -0
- package/src/cli/directCommands.ts +195 -0
- package/src/cli/envLoader.ts +17 -0
- package/src/cli/exportCommand.ts +65 -0
- package/src/cli/index.ts +249 -0
- package/src/cli/ioContext.ts +139 -0
- package/src/cli/preflight.ts +86 -0
- package/src/cli/statusCommand.ts +118 -0
- package/src/cli/workshopCommand.ts +496 -0
- package/src/develop/checkpointState.ts +121 -0
- package/src/develop/codeGenerator.ts +402 -0
- package/src/develop/dynamicScaffolder.ts +284 -0
- package/src/develop/githubMcpAdapter.ts +199 -0
- package/src/develop/index.ts +34 -0
- package/src/develop/mcpContextEnricher.ts +279 -0
- package/src/develop/pocScaffolder.ts +646 -0
- package/src/develop/ralphLoop.ts +1044 -0
- package/src/develop/templateRegistry.ts +427 -0
- package/src/develop/testRunner.ts +276 -0
- package/src/logging/logger.ts +73 -0
- package/src/loop/conversationLoop.ts +355 -0
- package/src/loop/phaseSummarizer.ts +114 -0
- package/src/mcp/mcpManager.ts +365 -0
- package/src/mcp/mcpTransport.ts +562 -0
- package/src/mcp/retryPolicy.ts +87 -0
- package/src/mcp/webSearch.ts +388 -0
- package/src/originalPrompts/design_thinking.md +178 -0
- package/src/originalPrompts/design_thinking_persona.md +76 -0
- package/src/originalPrompts/document_generator_example.md +77 -0
- package/src/originalPrompts/document_generator_persona.md +47 -0
- package/src/originalPrompts/facilitator_persona.md +125 -0
- package/src/originalPrompts/guardrails.md +47 -0
- package/src/phases/contextSummarizer.ts +154 -0
- package/src/phases/discoveryEnricher.ts +223 -0
- package/src/phases/phaseExtractors.ts +247 -0
- package/src/phases/phaseHandlers.ts +450 -0
- package/src/prompts/design.md +51 -0
- package/src/prompts/develop-boundary.md +51 -0
- package/src/prompts/develop.md +111 -0
- package/src/prompts/discover.md +58 -0
- package/src/prompts/ideate.md +56 -0
- package/src/prompts/plan.md +51 -0
- package/src/prompts/promptLoader.ts +198 -0
- package/src/prompts/select.md +47 -0
- package/src/prompts/summarize/README.md +8 -0
- package/src/prompts/summarize/design-summary.md +37 -0
- package/src/prompts/summarize/develop-summary.md +25 -0
- package/src/prompts/summarize/ideate-summary.md +27 -0
- package/src/prompts/summarize/plan-summary.md +27 -0
- package/src/prompts/summarize/select-summary.md +21 -0
- package/src/prompts/system.md +28 -0
- package/src/sessions/exportPaths.ts +28 -0
- package/src/sessions/exportWriter.ts +490 -0
- package/src/sessions/sessionManager.ts +119 -0
- package/src/sessions/sessionStore.ts +69 -0
- package/src/shared/activitySpinner.ts +108 -0
- package/src/shared/copilotClient.ts +291 -0
- package/src/shared/data/cards.json +1249 -0
- package/src/shared/data/cardsLoader.ts +70 -0
- package/src/shared/errorClassifier.ts +160 -0
- package/src/shared/events.ts +103 -0
- package/src/shared/markdownRenderer.ts +44 -0
- package/src/shared/schemas/session.ts +346 -0
- package/src/shared/tableRenderer.ts +28 -0
- package/src/types/marked-terminal.d.ts +5 -0
- package/src/vendor/chalk.ts +2 -0
- package/src/vendor/cli-table3.ts +3 -0
- package/src/vendor/commander.ts +2 -0
- package/src/vendor/marked-terminal.ts +3 -0
- package/src/vendor/marked.ts +2 -0
- package/src/vendor/ora.ts +2 -0
- package/src/vendor/pino.ts +3 -0
- package/src/vendor/zod.ts +3 -0
- package/tests/e2e/developE2e.spec.ts +152 -0
- package/tests/e2e/developFailureE2e.spec.ts +289 -0
- package/tests/e2e/developPty.spec.ts +86 -0
- package/tests/e2e/discoveryWebSearchRelevance.spec.ts +103 -0
- package/tests/e2e/harness.spec.ts +104 -0
- package/tests/e2e/mcpLive.spec.ts +149 -0
- package/tests/e2e/newSession.e2e.spec.ts +245 -0
- package/tests/e2e/ralphLoopEnrichmentComparison.spec.ts +70 -0
- package/tests/e2e/workiqEnrichment.spec.ts +72 -0
- package/tests/e2e/zava-assessment/agent-interaction-script.md +258 -0
- package/tests/e2e/zava-assessment/company-profile.md +98 -0
- package/tests/e2e/zava-assessment/expected-results-checklist.md +454 -0
- package/tests/e2e/zavaSimulation.spec.ts +511 -0
- package/tests/fixtures/completedSession.json +141 -0
- package/tests/fixtures/test-fixture-project/package-lock.json +1585 -0
- package/tests/fixtures/test-fixture-project/package.json +12 -0
- package/tests/fixtures/test-fixture-project/src/add.ts +3 -0
- package/tests/fixtures/test-fixture-project/tests/failing.test.ts +7 -0
- package/tests/fixtures/test-fixture-project/tests/hanging.test.ts +9 -0
- package/tests/fixtures/test-fixture-project/tests/passing.test.ts +13 -0
- package/tests/fixtures/test-fixture-project/vitest.config.ts +7 -0
- package/tests/integration/autoStartConversation.spec.ts +168 -0
- package/tests/integration/defaultCommand.spec.ts +179 -0
- package/tests/integration/directCommandNonTty.spec.ts +260 -0
- package/tests/integration/directCommandTty.spec.ts +185 -0
- package/tests/integration/discoveryEnrichmentFlow.spec.ts +209 -0
- package/tests/integration/exportArtifacts.spec.ts +232 -0
- package/tests/integration/exportFallbackFlow.spec.ts +115 -0
- package/tests/integration/mcpDegradationFlow.spec.ts +231 -0
- package/tests/integration/mcpTransportFlow.spec.ts +178 -0
- package/tests/integration/newSessionFlow.spec.ts +406 -0
- package/tests/integration/pocGithubMcp.spec.ts +224 -0
- package/tests/integration/pocLocalFallback.spec.ts +205 -0
- package/tests/integration/pocScaffold.spec.ts +220 -0
- package/tests/integration/ralphLoopFlow.spec.ts +430 -0
- package/tests/integration/ralphLoopPartial.spec.ts +416 -0
- package/tests/integration/resumeAndBacktrack.spec.ts +278 -0
- package/tests/integration/spinnerLifecycle.spec.ts +270 -0
- package/tests/integration/summarizationFlow.spec.ts +135 -0
- package/tests/integration/testRunnerReal.spec.ts +63 -0
- package/tests/integration/webSearchAgent.spec.ts +155 -0
- package/tests/live/copilotSdkLive.spec.ts +149 -0
- package/tests/live/zavaFullWorkshop.spec.ts +515 -0
- package/tests/setup/loadEnv.ts +5 -0
- package/tests/unit/cli/developCommand.spec.ts +679 -0
- package/tests/unit/cli/directCommands.spec.ts +325 -0
- package/tests/unit/cli/envLoader.spec.ts +73 -0
- package/tests/unit/cli/ioContext.spec.ts +148 -0
- package/tests/unit/cli/preflight.spec.ts +125 -0
- package/tests/unit/cli/statusCommand.spec.ts +134 -0
- package/tests/unit/cli/workshopClientFallback.spec.ts +100 -0
- package/tests/unit/cli/workshopCommand.spec.ts +378 -0
- package/tests/unit/config/vitestEnvSetup.spec.ts +24 -0
- package/tests/unit/develop/checkpointState.spec.ts +378 -0
- package/tests/unit/develop/codeGenerator.spec.ts +447 -0
- package/tests/unit/develop/githubMcpAdapter.spec.ts +283 -0
- package/tests/unit/develop/mcpContextEnricher.spec.ts +564 -0
- package/tests/unit/develop/outputValidator.spec.ts +134 -0
- package/tests/unit/develop/pocScaffolder.spec.ts +451 -0
- package/tests/unit/develop/ralphLoop.spec.ts +1439 -0
- package/tests/unit/develop/templateRegistry.spec.ts +106 -0
- package/tests/unit/develop/testRunner.spec.ts +294 -0
- package/tests/unit/infraBicep.spec.ts +116 -0
- package/tests/unit/infraDeploy.spec.ts +102 -0
- package/tests/unit/infraTeardown.spec.ts +77 -0
- package/tests/unit/logging/logger.spec.ts +50 -0
- package/tests/unit/loop/conversationLoop.spec.ts +719 -0
- package/tests/unit/loop/phaseSummarizer.spec.ts +169 -0
- package/tests/unit/loop/streamingMarkdown.spec.ts +180 -0
- package/tests/unit/mcp/mcpManager.spec.ts +336 -0
- package/tests/unit/mcp/mcpTransport.spec.ts +689 -0
- package/tests/unit/mcp/retryPolicy.spec.ts +278 -0
- package/tests/unit/mcp/timeoutValidation.spec.ts +55 -0
- package/tests/unit/mcp/webSearch.spec.ts +718 -0
- package/tests/unit/phases/contextSummarizer.spec.ts +158 -0
- package/tests/unit/phases/discoveryEnricher.repeatCalls.spec.ts +125 -0
- package/tests/unit/phases/discoveryEnricher.spec.ts +512 -0
- package/tests/unit/phases/phaseExtractors.spec.ts +406 -0
- package/tests/unit/phases/phaseHandlers.spec.ts +483 -0
- package/tests/unit/prompts/promptLoader.spec.ts +144 -0
- package/tests/unit/schemas/pocSchemas.spec.ts +457 -0
- package/tests/unit/schemas/session.spec.ts +328 -0
- package/tests/unit/sessions/exportPaths.spec.ts +38 -0
- package/tests/unit/sessions/exportWriter.spec.ts +737 -0
- package/tests/unit/sessions/sessionManager.spec.ts +174 -0
- package/tests/unit/sessions/sessionStore.spec.ts +136 -0
- package/tests/unit/shared/activitySpinner.spec.ts +211 -0
- package/tests/unit/shared/cardsLoader.spec.ts +89 -0
- package/tests/unit/shared/copilotClient.spec.ts +185 -0
- package/tests/unit/shared/errorClassifier.spec.ts +152 -0
- package/tests/unit/shared/events.spec.ts +71 -0
- package/tests/unit/shared/markdownRenderer.spec.ts +42 -0
- package/tests/unit/shared/markdownRendererChunks.spec.ts +83 -0
- package/tests/unit/shared/tableRenderer.spec.ts +38 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +15 -0
- package/vitest.live.config.ts +19 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for export path helpers.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
import { mkdtemp, rm, stat } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { getExportDir, ensureExportDir } from '../../../src/sessions/exportPaths.js';
|
|
9
|
+
describe('exportPaths', () => {
|
|
10
|
+
let tmpDir;
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-export-test-'));
|
|
13
|
+
});
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
it('getExportDir returns correct path', () => {
|
|
18
|
+
const dir = getExportDir('sess-42', tmpDir);
|
|
19
|
+
expect(dir).toBe(join(tmpDir, 'sess-42'));
|
|
20
|
+
});
|
|
21
|
+
it('ensureExportDir creates the directory', async () => {
|
|
22
|
+
const dir = await ensureExportDir('sess-42', tmpDir);
|
|
23
|
+
const stats = await stat(dir);
|
|
24
|
+
expect(stats.isDirectory()).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it('ensureExportDir is idempotent', async () => {
|
|
27
|
+
await ensureExportDir('sess-42', tmpDir);
|
|
28
|
+
const dir = await ensureExportDir('sess-42', tmpDir);
|
|
29
|
+
expect(dir).toBe(join(tmpDir, 'sess-42'));
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export writer tests (T042, T044).
|
|
3
|
+
*
|
|
4
|
+
* Validates that the export writer:
|
|
5
|
+
* - Generates summary.json per contract
|
|
6
|
+
* - Creates Markdown files for each completed phase
|
|
7
|
+
* - Tracks generated files in the artifact index
|
|
8
|
+
* - Handles sessions with varying completion levels
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
11
|
+
import { mkdtemp, rm, readFile, readdir } from 'node:fs/promises';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { tmpdir } from 'node:os';
|
|
14
|
+
import { exportSession, exportWorkshopDocs } from '../../../src/sessions/exportWriter.js';
|
|
15
|
+
function createFullSession(overrides) {
|
|
16
|
+
const now = new Date().toISOString();
|
|
17
|
+
return {
|
|
18
|
+
sessionId: 'export-test-session',
|
|
19
|
+
schemaVersion: '1.0.0',
|
|
20
|
+
createdAt: now,
|
|
21
|
+
updatedAt: now,
|
|
22
|
+
phase: 'Complete',
|
|
23
|
+
status: 'Completed',
|
|
24
|
+
participants: [{ id: 'p1', displayName: 'Alice', role: 'Facilitator' }],
|
|
25
|
+
artifacts: { generatedFiles: [] },
|
|
26
|
+
turns: [
|
|
27
|
+
{ phase: 'Discover', sequence: 1, role: 'user', content: 'We sell rockets', timestamp: now },
|
|
28
|
+
{
|
|
29
|
+
phase: 'Discover',
|
|
30
|
+
sequence: 2,
|
|
31
|
+
role: 'assistant',
|
|
32
|
+
content: 'Interesting! Tell me more.',
|
|
33
|
+
timestamp: now,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
phase: 'Ideate',
|
|
37
|
+
sequence: 3,
|
|
38
|
+
role: 'user',
|
|
39
|
+
content: 'We need better delivery',
|
|
40
|
+
timestamp: now,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
phase: 'Ideate',
|
|
44
|
+
sequence: 4,
|
|
45
|
+
role: 'assistant',
|
|
46
|
+
content: 'Here are some ideas.',
|
|
47
|
+
timestamp: now,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
businessContext: {
|
|
51
|
+
businessDescription: 'ACME Rockets Inc.',
|
|
52
|
+
challenges: ['Supply chain delays', 'Customer retention'],
|
|
53
|
+
},
|
|
54
|
+
workflow: {
|
|
55
|
+
activities: [
|
|
56
|
+
{ id: 'a1', name: 'Order Processing' },
|
|
57
|
+
{ id: 'a2', name: 'Delivery Tracking' },
|
|
58
|
+
],
|
|
59
|
+
edges: [{ fromStepId: 'a1', toStepId: 'a2' }],
|
|
60
|
+
},
|
|
61
|
+
ideas: [
|
|
62
|
+
{
|
|
63
|
+
id: 'idea-1',
|
|
64
|
+
title: 'AI-Powered Delivery Tracking',
|
|
65
|
+
description: 'Use AI to predict delivery times',
|
|
66
|
+
workflowStepIds: ['a2'],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'idea-2',
|
|
70
|
+
title: 'Smart Inventory Management',
|
|
71
|
+
description: 'ML-based inventory optimization',
|
|
72
|
+
workflowStepIds: ['a1'],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
selection: {
|
|
76
|
+
ideaId: 'idea-1',
|
|
77
|
+
selectionRationale: 'Highest feasibility score',
|
|
78
|
+
confirmedByUser: true,
|
|
79
|
+
confirmedAt: now,
|
|
80
|
+
},
|
|
81
|
+
plan: {
|
|
82
|
+
milestones: [
|
|
83
|
+
{ id: 'm1', title: 'Data Pipeline Setup', items: ['Set up delivery data pipeline'] },
|
|
84
|
+
{ id: 'm2', title: 'Model Training', items: ['Train prediction model'] },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
...overrides,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
describe('exportWriter', () => {
|
|
91
|
+
let tmpDir;
|
|
92
|
+
beforeEach(async () => {
|
|
93
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-export-'));
|
|
94
|
+
});
|
|
95
|
+
afterEach(async () => {
|
|
96
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
97
|
+
});
|
|
98
|
+
it('creates summary.json with required fields', async () => {
|
|
99
|
+
const session = createFullSession();
|
|
100
|
+
const result = await exportSession(session, tmpDir);
|
|
101
|
+
const summaryPath = join(tmpDir, 'summary.json');
|
|
102
|
+
const raw = await readFile(summaryPath, 'utf-8');
|
|
103
|
+
const summary = JSON.parse(raw);
|
|
104
|
+
expect(summary.sessionId).toBe('export-test-session');
|
|
105
|
+
expect(summary.exportedAt).toBeDefined();
|
|
106
|
+
expect(summary.phase).toBe('Complete');
|
|
107
|
+
expect(summary.status).toBe('Completed');
|
|
108
|
+
expect(summary.files).toBeInstanceOf(Array);
|
|
109
|
+
expect(summary.files.length).toBeGreaterThan(0);
|
|
110
|
+
expect(result.files.length).toBeGreaterThan(0);
|
|
111
|
+
});
|
|
112
|
+
it('creates Markdown files for completed phases', async () => {
|
|
113
|
+
const session = createFullSession();
|
|
114
|
+
await exportSession(session, tmpDir);
|
|
115
|
+
const files = await readdir(tmpDir);
|
|
116
|
+
expect(files).toContain('summary.json');
|
|
117
|
+
expect(files).toContain('discover.md');
|
|
118
|
+
// Should have at least discover and ideate based on turns
|
|
119
|
+
});
|
|
120
|
+
it('includes business context in discover.md', async () => {
|
|
121
|
+
const session = createFullSession();
|
|
122
|
+
await exportSession(session, tmpDir);
|
|
123
|
+
const content = await readFile(join(tmpDir, 'discover.md'), 'utf-8');
|
|
124
|
+
expect(content).toContain('ACME Rockets Inc.');
|
|
125
|
+
expect(content).toContain('Supply chain delays');
|
|
126
|
+
});
|
|
127
|
+
it('includes ideas in ideate.md', async () => {
|
|
128
|
+
const session = createFullSession();
|
|
129
|
+
await exportSession(session, tmpDir);
|
|
130
|
+
const content = await readFile(join(tmpDir, 'ideate.md'), 'utf-8');
|
|
131
|
+
expect(content).toContain('AI-Powered Delivery Tracking');
|
|
132
|
+
expect(content).toContain('Smart Inventory Management');
|
|
133
|
+
});
|
|
134
|
+
it('includes selection details in select.md', async () => {
|
|
135
|
+
const session = createFullSession();
|
|
136
|
+
await exportSession(session, tmpDir);
|
|
137
|
+
const content = await readFile(join(tmpDir, 'select.md'), 'utf-8');
|
|
138
|
+
expect(content).toContain('idea-1');
|
|
139
|
+
expect(content).toContain('Highest feasibility score');
|
|
140
|
+
});
|
|
141
|
+
it('includes plan milestones in plan.md', async () => {
|
|
142
|
+
const session = createFullSession();
|
|
143
|
+
await exportSession(session, tmpDir);
|
|
144
|
+
const content = await readFile(join(tmpDir, 'plan.md'), 'utf-8');
|
|
145
|
+
expect(content).toContain('Data Pipeline Setup');
|
|
146
|
+
expect(content).toContain('Model Training');
|
|
147
|
+
});
|
|
148
|
+
it('handles session with only Discover phase', async () => {
|
|
149
|
+
const session = createFullSession({
|
|
150
|
+
phase: 'Discover',
|
|
151
|
+
status: 'Active',
|
|
152
|
+
ideas: undefined,
|
|
153
|
+
selection: undefined,
|
|
154
|
+
plan: undefined,
|
|
155
|
+
turns: [
|
|
156
|
+
{
|
|
157
|
+
phase: 'Discover',
|
|
158
|
+
sequence: 1,
|
|
159
|
+
role: 'user',
|
|
160
|
+
content: 'We sell rockets',
|
|
161
|
+
timestamp: new Date().toISOString(),
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
const result = await exportSession(session, tmpDir);
|
|
166
|
+
const files = await readdir(tmpDir);
|
|
167
|
+
expect(files).toContain('summary.json');
|
|
168
|
+
expect(files).toContain('discover.md');
|
|
169
|
+
// Should not have plan.md since there's no plan data
|
|
170
|
+
expect(files).not.toContain('plan.md');
|
|
171
|
+
expect(result.files.length).toBeGreaterThan(0);
|
|
172
|
+
});
|
|
173
|
+
it('returns ExportResult with file list', async () => {
|
|
174
|
+
const session = createFullSession();
|
|
175
|
+
const result = await exportSession(session, tmpDir);
|
|
176
|
+
expect(result.exportDir).toBe(tmpDir);
|
|
177
|
+
expect(result.files.length).toBeGreaterThan(0);
|
|
178
|
+
expect(result.files.every((f) => f.path && f.type)).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
it('summary.json files paths are relative', async () => {
|
|
181
|
+
const session = createFullSession();
|
|
182
|
+
await exportSession(session, tmpDir);
|
|
183
|
+
const raw = await readFile(join(tmpDir, 'summary.json'), 'utf-8');
|
|
184
|
+
const summary = JSON.parse(raw);
|
|
185
|
+
for (const file of summary.files) {
|
|
186
|
+
expect(file.path).not.toContain('/');
|
|
187
|
+
expect(file.path).not.toContain('\\');
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
it('does not include secrets in exported files', async () => {
|
|
191
|
+
const session = createFullSession();
|
|
192
|
+
await exportSession(session, tmpDir);
|
|
193
|
+
const files = await readdir(tmpDir);
|
|
194
|
+
for (const file of files) {
|
|
195
|
+
const content = await readFile(join(tmpDir, file), 'utf-8');
|
|
196
|
+
// Should not contain typical secret patterns
|
|
197
|
+
expect(content).not.toMatch(/api[_-]?key/i);
|
|
198
|
+
expect(content).not.toMatch(/secret/i);
|
|
199
|
+
expect(content).not.toMatch(/token/i);
|
|
200
|
+
expect(content).not.toMatch(/password/i);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
// ── T010: Tests for enriched generateDevelopMarkdown (Feature 002) ───────────
|
|
205
|
+
describe('generateDevelopMarkdown (Feature 002 enrichment)', () => {
|
|
206
|
+
let tmpDir;
|
|
207
|
+
beforeEach(async () => {
|
|
208
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-develop-md-test-'));
|
|
209
|
+
});
|
|
210
|
+
afterEach(async () => {
|
|
211
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
212
|
+
});
|
|
213
|
+
function createSessionWithPoc(overrides) {
|
|
214
|
+
const base = createFullSession();
|
|
215
|
+
return {
|
|
216
|
+
...base,
|
|
217
|
+
poc: {
|
|
218
|
+
repoSource: 'local',
|
|
219
|
+
repoPath: './poc/test-session',
|
|
220
|
+
iterations: [],
|
|
221
|
+
...(overrides?.poc ?? {}),
|
|
222
|
+
},
|
|
223
|
+
...overrides,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
it('generates develop.md when poc is present', async () => {
|
|
227
|
+
const session = createSessionWithPoc();
|
|
228
|
+
const result = await exportSession(session, tmpDir);
|
|
229
|
+
const files = await readdir(tmpDir);
|
|
230
|
+
expect(files).toContain('develop.md');
|
|
231
|
+
const fileEntry = result.files.find((f) => f.path === 'develop.md');
|
|
232
|
+
expect(fileEntry).toBeDefined();
|
|
233
|
+
});
|
|
234
|
+
it('does not generate develop.md when poc is absent', async () => {
|
|
235
|
+
const session = createFullSession();
|
|
236
|
+
// no poc field
|
|
237
|
+
await exportSession(session, tmpDir);
|
|
238
|
+
const files = await readdir(tmpDir);
|
|
239
|
+
expect(files).not.toContain('develop.md');
|
|
240
|
+
});
|
|
241
|
+
it('includes repo path when repoSource is local', async () => {
|
|
242
|
+
const session = createSessionWithPoc({
|
|
243
|
+
poc: {
|
|
244
|
+
repoSource: 'local',
|
|
245
|
+
repoPath: './poc/test-session-001',
|
|
246
|
+
iterations: [],
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
await exportSession(session, tmpDir);
|
|
250
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
251
|
+
expect(content).toContain('./poc/test-session-001');
|
|
252
|
+
expect(content).toContain('local');
|
|
253
|
+
});
|
|
254
|
+
it('includes repo URL when manually set on local repoSource', async () => {
|
|
255
|
+
const session = createSessionWithPoc({
|
|
256
|
+
poc: {
|
|
257
|
+
repoSource: 'local',
|
|
258
|
+
repoUrl: 'https://github.com/acme/poc-route-optimizer',
|
|
259
|
+
iterations: [],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
await exportSession(session, tmpDir);
|
|
263
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
264
|
+
expect(content).toContain('https://github.com/acme/poc-route-optimizer');
|
|
265
|
+
expect(content).toContain('local');
|
|
266
|
+
});
|
|
267
|
+
it('includes tech stack summary', async () => {
|
|
268
|
+
const session = createSessionWithPoc({
|
|
269
|
+
poc: {
|
|
270
|
+
repoSource: 'local',
|
|
271
|
+
iterations: [],
|
|
272
|
+
techStack: {
|
|
273
|
+
language: 'TypeScript',
|
|
274
|
+
framework: 'Express',
|
|
275
|
+
testRunner: 'npm test',
|
|
276
|
+
buildCommand: 'npm run build',
|
|
277
|
+
runtime: 'Node.js 20',
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
await exportSession(session, tmpDir);
|
|
282
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
283
|
+
expect(content).toContain('TypeScript');
|
|
284
|
+
expect(content).toContain('Express');
|
|
285
|
+
expect(content).toContain('Node.js 20');
|
|
286
|
+
});
|
|
287
|
+
it('includes iteration timeline with outcomes', async () => {
|
|
288
|
+
const session = createSessionWithPoc({
|
|
289
|
+
poc: {
|
|
290
|
+
repoSource: 'local',
|
|
291
|
+
iterations: [
|
|
292
|
+
{
|
|
293
|
+
iteration: 1,
|
|
294
|
+
startedAt: '2026-01-15T10:00:00Z',
|
|
295
|
+
endedAt: '2026-01-15T10:01:00Z',
|
|
296
|
+
outcome: 'scaffold',
|
|
297
|
+
filesChanged: ['package.json', 'src/index.ts'],
|
|
298
|
+
changesSummary: 'Initial scaffold generated',
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
iteration: 2,
|
|
302
|
+
startedAt: '2026-01-15T10:01:00Z',
|
|
303
|
+
endedAt: '2026-01-15T10:02:00Z',
|
|
304
|
+
outcome: 'tests-passing',
|
|
305
|
+
filesChanged: ['src/optimizer.ts'],
|
|
306
|
+
testResults: {
|
|
307
|
+
passed: 3,
|
|
308
|
+
failed: 0,
|
|
309
|
+
skipped: 0,
|
|
310
|
+
total: 3,
|
|
311
|
+
durationMs: 450,
|
|
312
|
+
failures: [],
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
await exportSession(session, tmpDir);
|
|
319
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
320
|
+
expect(content).toContain('Iteration 1');
|
|
321
|
+
expect(content).toContain('scaffold');
|
|
322
|
+
expect(content).toContain('Iteration 2');
|
|
323
|
+
expect(content).toContain('tests-passing');
|
|
324
|
+
expect(content).toContain('package.json');
|
|
325
|
+
expect(content).toContain('3 passed');
|
|
326
|
+
});
|
|
327
|
+
it('includes final test results', async () => {
|
|
328
|
+
const session = createSessionWithPoc({
|
|
329
|
+
poc: {
|
|
330
|
+
repoSource: 'local',
|
|
331
|
+
iterations: [],
|
|
332
|
+
finalStatus: 'success',
|
|
333
|
+
terminationReason: 'tests-passing',
|
|
334
|
+
totalDurationMs: 60000,
|
|
335
|
+
finalTestResults: {
|
|
336
|
+
passed: 5,
|
|
337
|
+
failed: 0,
|
|
338
|
+
skipped: 1,
|
|
339
|
+
total: 6,
|
|
340
|
+
durationMs: 800,
|
|
341
|
+
failures: [],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
await exportSession(session, tmpDir);
|
|
346
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
347
|
+
expect(content).toContain('success');
|
|
348
|
+
expect(content).toContain('tests-passing');
|
|
349
|
+
expect(content).toContain('5'); // passed count
|
|
350
|
+
expect(content).toContain('60.0s'); // total duration
|
|
351
|
+
});
|
|
352
|
+
it('includes failure details in final test results', async () => {
|
|
353
|
+
const session = createSessionWithPoc({
|
|
354
|
+
poc: {
|
|
355
|
+
repoSource: 'local',
|
|
356
|
+
iterations: [],
|
|
357
|
+
finalStatus: 'failed',
|
|
358
|
+
terminationReason: 'max-iterations',
|
|
359
|
+
finalTestResults: {
|
|
360
|
+
passed: 0,
|
|
361
|
+
failed: 1,
|
|
362
|
+
skipped: 0,
|
|
363
|
+
total: 1,
|
|
364
|
+
durationMs: 500,
|
|
365
|
+
failures: [{ testName: 'route optimizer test', message: 'Expected 3 but got 5' }],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
await exportSession(session, tmpDir);
|
|
370
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
371
|
+
expect(content).toContain('route optimizer test');
|
|
372
|
+
expect(content).toContain('Expected 3 but got 5');
|
|
373
|
+
});
|
|
374
|
+
it('includes termination reason', async () => {
|
|
375
|
+
const session = createSessionWithPoc({
|
|
376
|
+
poc: {
|
|
377
|
+
repoSource: 'local',
|
|
378
|
+
iterations: [],
|
|
379
|
+
finalStatus: 'partial',
|
|
380
|
+
terminationReason: 'max-iterations',
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
await exportSession(session, tmpDir);
|
|
384
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
385
|
+
expect(content).toContain('partial');
|
|
386
|
+
expect(content).toContain('max-iterations');
|
|
387
|
+
});
|
|
388
|
+
it('handles error iteration with errorMessage', async () => {
|
|
389
|
+
const session = createSessionWithPoc({
|
|
390
|
+
poc: {
|
|
391
|
+
repoSource: 'local',
|
|
392
|
+
iterations: [
|
|
393
|
+
{
|
|
394
|
+
iteration: 2,
|
|
395
|
+
startedAt: '2026-01-15T10:01:00Z',
|
|
396
|
+
outcome: 'error',
|
|
397
|
+
filesChanged: [],
|
|
398
|
+
errorMessage: 'npm install failed: ENOTFOUND registry.npmjs.org',
|
|
399
|
+
},
|
|
400
|
+
],
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
await exportSession(session, tmpDir);
|
|
404
|
+
const content = await readFile(join(tmpDir, 'develop.md'), 'utf-8');
|
|
405
|
+
expect(content).toContain('error');
|
|
406
|
+
expect(content).toContain('npm install failed');
|
|
407
|
+
});
|
|
408
|
+
it('includes PoC highlights in summary.json (F030)', async () => {
|
|
409
|
+
const session = createSessionWithPoc({
|
|
410
|
+
poc: {
|
|
411
|
+
repoSource: 'local',
|
|
412
|
+
iterations: [
|
|
413
|
+
{
|
|
414
|
+
iteration: 1,
|
|
415
|
+
startedAt: '2026-01-15T10:00:00Z',
|
|
416
|
+
outcome: 'tests-passing',
|
|
417
|
+
filesChanged: ['src/index.ts'],
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
iteration: 2,
|
|
421
|
+
startedAt: '2026-01-15T10:01:00Z',
|
|
422
|
+
outcome: 'tests-passing',
|
|
423
|
+
filesChanged: ['src/routes.ts'],
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
finalStatus: 'success',
|
|
427
|
+
terminationReason: 'tests-passing',
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
await exportSession(session, tmpDir);
|
|
431
|
+
const summaryRaw = await readFile(join(tmpDir, 'summary.json'), 'utf-8');
|
|
432
|
+
const summary = JSON.parse(summaryRaw);
|
|
433
|
+
expect(summary.highlights).toBeDefined();
|
|
434
|
+
expect(summary.highlights).toEqual(expect.arrayContaining([
|
|
435
|
+
expect.stringContaining('PoC status: success'),
|
|
436
|
+
expect.stringContaining('PoC iterations: 2'),
|
|
437
|
+
expect.stringContaining('PoC termination: tests-passing'),
|
|
438
|
+
]));
|
|
439
|
+
});
|
|
440
|
+
// ── Conversation Fallback Export Tests (T072-T076) ───────────────────────
|
|
441
|
+
it('generates ideate.md with conversation turns when ideas are null', async () => {
|
|
442
|
+
const now = new Date().toISOString();
|
|
443
|
+
const session = createFullSession({
|
|
444
|
+
ideas: undefined,
|
|
445
|
+
turns: [
|
|
446
|
+
{ phase: 'Discover', sequence: 1, role: 'user', content: 'hello', timestamp: now },
|
|
447
|
+
{
|
|
448
|
+
phase: 'Ideate',
|
|
449
|
+
sequence: 2,
|
|
450
|
+
role: 'user',
|
|
451
|
+
content: 'brainstorm AI ideas',
|
|
452
|
+
timestamp: now,
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
phase: 'Ideate',
|
|
456
|
+
sequence: 3,
|
|
457
|
+
role: 'assistant',
|
|
458
|
+
content: 'Here are some ideas for AI-powered solutions.',
|
|
459
|
+
timestamp: now,
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
});
|
|
463
|
+
await exportSession(session, tmpDir);
|
|
464
|
+
const files = await readdir(tmpDir);
|
|
465
|
+
expect(files).toContain('ideate.md');
|
|
466
|
+
const content = await readFile(join(tmpDir, 'ideate.md'), 'utf-8');
|
|
467
|
+
expect(content).toContain('# Ideate Phase');
|
|
468
|
+
expect(content).toContain('## Conversation');
|
|
469
|
+
expect(content).toContain('brainstorm AI ideas');
|
|
470
|
+
});
|
|
471
|
+
it('generates design.md with conversation turns when evaluation is null', async () => {
|
|
472
|
+
const now = new Date().toISOString();
|
|
473
|
+
const session = createFullSession({
|
|
474
|
+
evaluation: undefined,
|
|
475
|
+
turns: [
|
|
476
|
+
{ phase: 'Design', sequence: 1, role: 'user', content: 'evaluate ideas', timestamp: now },
|
|
477
|
+
{
|
|
478
|
+
phase: 'Design',
|
|
479
|
+
sequence: 2,
|
|
480
|
+
role: 'assistant',
|
|
481
|
+
content: 'Let me evaluate.',
|
|
482
|
+
timestamp: now,
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
});
|
|
486
|
+
await exportSession(session, tmpDir);
|
|
487
|
+
const files = await readdir(tmpDir);
|
|
488
|
+
expect(files).toContain('design.md');
|
|
489
|
+
const content = await readFile(join(tmpDir, 'design.md'), 'utf-8');
|
|
490
|
+
expect(content).toContain('# Design Phase');
|
|
491
|
+
expect(content).toContain('## Conversation');
|
|
492
|
+
expect(content).toContain('evaluate ideas');
|
|
493
|
+
});
|
|
494
|
+
it('renders structured data first then conversation when both exist', async () => {
|
|
495
|
+
const now = new Date().toISOString();
|
|
496
|
+
const session = createFullSession({
|
|
497
|
+
turns: [
|
|
498
|
+
{ phase: 'Ideate', sequence: 1, role: 'user', content: 'give ideas', timestamp: now },
|
|
499
|
+
{
|
|
500
|
+
phase: 'Ideate',
|
|
501
|
+
sequence: 2,
|
|
502
|
+
role: 'assistant',
|
|
503
|
+
content: 'here are ideas',
|
|
504
|
+
timestamp: now,
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
});
|
|
508
|
+
// session already has ideas from createFullSession
|
|
509
|
+
await exportSession(session, tmpDir);
|
|
510
|
+
const content = await readFile(join(tmpDir, 'ideate.md'), 'utf-8');
|
|
511
|
+
// Structured data (Ideas section) should come before Conversation
|
|
512
|
+
const ideasIdx = content.indexOf('## Ideas');
|
|
513
|
+
const convIdx = content.indexOf('## Conversation');
|
|
514
|
+
expect(ideasIdx).toBeGreaterThan(-1);
|
|
515
|
+
expect(convIdx).toBeGreaterThan(-1);
|
|
516
|
+
expect(ideasIdx).toBeLessThan(convIdx);
|
|
517
|
+
});
|
|
518
|
+
it('summary.json lists all 6 phase files when all phases have turns', async () => {
|
|
519
|
+
const now = new Date().toISOString();
|
|
520
|
+
const phases = ['Discover', 'Ideate', 'Design', 'Select', 'Plan', 'Develop'];
|
|
521
|
+
const turns = phases.flatMap((phase, i) => [
|
|
522
|
+
{
|
|
523
|
+
phase,
|
|
524
|
+
sequence: i * 2 + 1,
|
|
525
|
+
role: 'user',
|
|
526
|
+
content: `${phase} input`,
|
|
527
|
+
timestamp: now,
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
phase,
|
|
531
|
+
sequence: i * 2 + 2,
|
|
532
|
+
role: 'assistant',
|
|
533
|
+
content: `${phase} response`,
|
|
534
|
+
timestamp: now,
|
|
535
|
+
},
|
|
536
|
+
]);
|
|
537
|
+
const session = createFullSession({ turns });
|
|
538
|
+
await exportSession(session, tmpDir);
|
|
539
|
+
const summaryRaw = await readFile(join(tmpDir, 'summary.json'), 'utf-8');
|
|
540
|
+
const summary = JSON.parse(summaryRaw);
|
|
541
|
+
const mdFiles = summary.files.filter((f) => f.path.endsWith('.md'));
|
|
542
|
+
expect(mdFiles.length).toBeGreaterThanOrEqual(6);
|
|
543
|
+
});
|
|
544
|
+
it('summary.json highlights include one entry per phase with turns', async () => {
|
|
545
|
+
const now = new Date().toISOString();
|
|
546
|
+
const session = createFullSession({
|
|
547
|
+
ideas: undefined,
|
|
548
|
+
evaluation: undefined,
|
|
549
|
+
selection: undefined,
|
|
550
|
+
plan: undefined,
|
|
551
|
+
poc: undefined,
|
|
552
|
+
turns: [
|
|
553
|
+
{ phase: 'Ideate', sequence: 1, role: 'user', content: 'ideas please', timestamp: now },
|
|
554
|
+
{
|
|
555
|
+
phase: 'Ideate',
|
|
556
|
+
sequence: 2,
|
|
557
|
+
role: 'assistant',
|
|
558
|
+
content: 'Here are creative ideas.',
|
|
559
|
+
timestamp: now,
|
|
560
|
+
},
|
|
561
|
+
{ phase: 'Design', sequence: 3, role: 'user', content: 'evaluate', timestamp: now },
|
|
562
|
+
{
|
|
563
|
+
phase: 'Design',
|
|
564
|
+
sequence: 4,
|
|
565
|
+
role: 'assistant',
|
|
566
|
+
content: 'Evaluation results.',
|
|
567
|
+
timestamp: now,
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
});
|
|
571
|
+
await exportSession(session, tmpDir);
|
|
572
|
+
const summaryRaw = await readFile(join(tmpDir, 'summary.json'), 'utf-8');
|
|
573
|
+
const summary = JSON.parse(summaryRaw);
|
|
574
|
+
expect(summary.highlights).toBeDefined();
|
|
575
|
+
// Should have highlights for phases with turns (fallback to first assistant turn)
|
|
576
|
+
expect(summary.highlights.some((h) => h.includes('Ideate'))).toBe(true);
|
|
577
|
+
expect(summary.highlights.some((h) => h.includes('Design'))).toBe(true);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
// ── exportWorkshopDocs — workshop docs for PoC repos ─────────────────────────
|
|
581
|
+
describe('exportWorkshopDocs', () => {
|
|
582
|
+
let tmpDir;
|
|
583
|
+
beforeEach(async () => {
|
|
584
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-wsdocs-'));
|
|
585
|
+
});
|
|
586
|
+
afterEach(async () => {
|
|
587
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
588
|
+
});
|
|
589
|
+
it('creates docs/workshop/ directory with phase markdown files', async () => {
|
|
590
|
+
const session = createFullSession();
|
|
591
|
+
const result = await exportWorkshopDocs(session, tmpDir);
|
|
592
|
+
const workshopDir = join(tmpDir, 'docs', 'workshop');
|
|
593
|
+
const files = await readdir(workshopDir);
|
|
594
|
+
expect(files).toContain('discover.md');
|
|
595
|
+
expect(files).toContain('ideate.md');
|
|
596
|
+
expect(files).toContain('select.md');
|
|
597
|
+
expect(files).toContain('plan.md');
|
|
598
|
+
expect(result.createdFiles.length).toBeGreaterThan(0);
|
|
599
|
+
});
|
|
600
|
+
it('creates a WORKSHOP.md index at the repo root', async () => {
|
|
601
|
+
const session = createFullSession();
|
|
602
|
+
await exportWorkshopDocs(session, tmpDir);
|
|
603
|
+
const content = await readFile(join(tmpDir, 'WORKSHOP.md'), 'utf-8');
|
|
604
|
+
expect(content).toContain('Workshop Summary');
|
|
605
|
+
expect(content).toContain('discover.md');
|
|
606
|
+
expect(content).toContain(session.sessionId);
|
|
607
|
+
});
|
|
608
|
+
it('excludes develop.md since the repo IS the PoC', async () => {
|
|
609
|
+
const session = createFullSession({
|
|
610
|
+
poc: {
|
|
611
|
+
repoSource: 'local',
|
|
612
|
+
repoPath: '/tmp/poc',
|
|
613
|
+
iterations: [],
|
|
614
|
+
},
|
|
615
|
+
});
|
|
616
|
+
await exportWorkshopDocs(session, tmpDir);
|
|
617
|
+
const workshopDir = join(tmpDir, 'docs', 'workshop');
|
|
618
|
+
const files = await readdir(workshopDir);
|
|
619
|
+
expect(files).not.toContain('develop.md');
|
|
620
|
+
});
|
|
621
|
+
it('includes business context in discover.md', async () => {
|
|
622
|
+
const session = createFullSession();
|
|
623
|
+
await exportWorkshopDocs(session, tmpDir);
|
|
624
|
+
const content = await readFile(join(tmpDir, 'docs', 'workshop', 'discover.md'), 'utf-8');
|
|
625
|
+
expect(content).toContain('ACME Rockets Inc.');
|
|
626
|
+
});
|
|
627
|
+
it('includes selection rationale in select.md', async () => {
|
|
628
|
+
const session = createFullSession();
|
|
629
|
+
await exportWorkshopDocs(session, tmpDir);
|
|
630
|
+
const content = await readFile(join(tmpDir, 'docs', 'workshop', 'select.md'), 'utf-8');
|
|
631
|
+
expect(content).toContain('Highest feasibility score');
|
|
632
|
+
});
|
|
633
|
+
it('returns list of created files relative to repoDir', async () => {
|
|
634
|
+
const session = createFullSession();
|
|
635
|
+
const result = await exportWorkshopDocs(session, tmpDir);
|
|
636
|
+
expect(result.createdFiles).toContain('WORKSHOP.md');
|
|
637
|
+
expect(result.createdFiles.some((f) => f.startsWith('docs/workshop/'))).toBe(true);
|
|
638
|
+
});
|
|
639
|
+
it('handles session with only Discover data', async () => {
|
|
640
|
+
const session = createFullSession({
|
|
641
|
+
phase: 'Discover',
|
|
642
|
+
status: 'Active',
|
|
643
|
+
ideas: undefined,
|
|
644
|
+
selection: undefined,
|
|
645
|
+
plan: undefined,
|
|
646
|
+
evaluation: undefined,
|
|
647
|
+
});
|
|
648
|
+
const result = await exportWorkshopDocs(session, tmpDir);
|
|
649
|
+
const workshopDir = join(tmpDir, 'docs', 'workshop');
|
|
650
|
+
const files = await readdir(workshopDir);
|
|
651
|
+
expect(files).toContain('discover.md');
|
|
652
|
+
expect(files).not.toContain('plan.md');
|
|
653
|
+
expect(result.createdFiles.length).toBeGreaterThan(0);
|
|
654
|
+
});
|
|
655
|
+
});
|