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,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T070: Integration tests for default command behavior (FR-004).
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* - `sofia` with no subcommand enters the workshop flow (default action)
|
|
6
|
+
* - `sofia workshop` still works as an alias
|
|
7
|
+
* - `--help` shows workshop options (--new-session, --phase, --retry) at top level
|
|
8
|
+
* - `sofia status` and `sofia export` subcommands continue to work after restructure
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
11
|
+
// Mock all heavy dependencies so we only test CLI argument routing
|
|
12
|
+
vi.mock('../../src/sessions/sessionStore.js', () => ({
|
|
13
|
+
createDefaultStore: vi.fn(() => ({
|
|
14
|
+
list: vi.fn(async () => []),
|
|
15
|
+
exists: vi.fn(async () => false),
|
|
16
|
+
save: vi.fn(async () => { }),
|
|
17
|
+
load: vi.fn(async () => ({})),
|
|
18
|
+
})),
|
|
19
|
+
SessionStore: vi.fn(),
|
|
20
|
+
}));
|
|
21
|
+
vi.mock('../../src/shared/copilotClient.js', () => ({
|
|
22
|
+
createCopilotClient: vi.fn(async () => ({
|
|
23
|
+
createSession: vi.fn(async () => ({
|
|
24
|
+
send: vi.fn(async function* () {
|
|
25
|
+
yield { type: 'TextDelta', text: 'hello' };
|
|
26
|
+
}),
|
|
27
|
+
})),
|
|
28
|
+
})),
|
|
29
|
+
createFakeCopilotClient: vi.fn(),
|
|
30
|
+
}));
|
|
31
|
+
vi.mock('../../src/logging/logger.js', () => ({
|
|
32
|
+
getLogger: vi.fn(() => ({
|
|
33
|
+
info: vi.fn(),
|
|
34
|
+
warn: vi.fn(),
|
|
35
|
+
error: vi.fn(),
|
|
36
|
+
debug: vi.fn(),
|
|
37
|
+
})),
|
|
38
|
+
}));
|
|
39
|
+
vi.mock('../../src/cli/ioContext.js', () => ({
|
|
40
|
+
createLoopIO: vi.fn(() => ({
|
|
41
|
+
write: vi.fn(),
|
|
42
|
+
writeActivity: vi.fn(),
|
|
43
|
+
readInput: vi.fn(async () => null),
|
|
44
|
+
showDecisionGate: vi.fn(async () => ({ choice: 'exit' })),
|
|
45
|
+
isJsonMode: false,
|
|
46
|
+
isTTY: false,
|
|
47
|
+
})),
|
|
48
|
+
}));
|
|
49
|
+
describe('Default command behavior (T070)', () => {
|
|
50
|
+
let workshopSpy;
|
|
51
|
+
let statusSpy;
|
|
52
|
+
let exportSpy;
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
workshopSpy = vi.fn(async () => { });
|
|
55
|
+
statusSpy = vi.fn(async () => { });
|
|
56
|
+
exportSpy = vi.fn(async () => { });
|
|
57
|
+
});
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
vi.restoreAllMocks();
|
|
60
|
+
});
|
|
61
|
+
it('sofia with no subcommand invokes the workshop handler (default action)', async () => {
|
|
62
|
+
// Import buildCli which should expose the program without auto-parsing
|
|
63
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
64
|
+
const program = buildCli({
|
|
65
|
+
workshopHandler: workshopSpy,
|
|
66
|
+
statusHandler: statusSpy,
|
|
67
|
+
exportHandler: exportSpy,
|
|
68
|
+
});
|
|
69
|
+
// Parse with no subcommand: `sofia`
|
|
70
|
+
await program.parseAsync(['node', 'sofia']);
|
|
71
|
+
expect(workshopSpy).toHaveBeenCalledTimes(1);
|
|
72
|
+
expect(statusSpy).not.toHaveBeenCalled();
|
|
73
|
+
expect(exportSpy).not.toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
it('sofia workshop still works as alias', async () => {
|
|
76
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
77
|
+
const program = buildCli({
|
|
78
|
+
workshopHandler: workshopSpy,
|
|
79
|
+
statusHandler: statusSpy,
|
|
80
|
+
exportHandler: exportSpy,
|
|
81
|
+
});
|
|
82
|
+
await program.parseAsync(['node', 'sofia', 'workshop']);
|
|
83
|
+
expect(workshopSpy).toHaveBeenCalledTimes(1);
|
|
84
|
+
expect(statusSpy).not.toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
it('--help shows workshop options (--new-session, --phase, --retry) at top level', async () => {
|
|
87
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
88
|
+
const program = buildCli({
|
|
89
|
+
workshopHandler: workshopSpy,
|
|
90
|
+
statusHandler: statusSpy,
|
|
91
|
+
exportHandler: exportSpy,
|
|
92
|
+
});
|
|
93
|
+
const helpText = program.helpInformation();
|
|
94
|
+
expect(helpText).toContain('--new-session');
|
|
95
|
+
expect(helpText).toContain('--phase');
|
|
96
|
+
expect(helpText).toContain('--retry');
|
|
97
|
+
});
|
|
98
|
+
it('sofia status routes to status handler', async () => {
|
|
99
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
100
|
+
const program = buildCli({
|
|
101
|
+
workshopHandler: workshopSpy,
|
|
102
|
+
statusHandler: statusSpy,
|
|
103
|
+
exportHandler: exportSpy,
|
|
104
|
+
});
|
|
105
|
+
await program.parseAsync(['node', 'sofia', 'status']);
|
|
106
|
+
expect(statusSpy).toHaveBeenCalledTimes(1);
|
|
107
|
+
expect(workshopSpy).not.toHaveBeenCalled();
|
|
108
|
+
expect(exportSpy).not.toHaveBeenCalled();
|
|
109
|
+
});
|
|
110
|
+
it('sofia export routes to export handler', async () => {
|
|
111
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
112
|
+
const program = buildCli({
|
|
113
|
+
workshopHandler: workshopSpy,
|
|
114
|
+
statusHandler: statusSpy,
|
|
115
|
+
exportHandler: exportSpy,
|
|
116
|
+
});
|
|
117
|
+
await program.parseAsync(['node', 'sofia', 'export']);
|
|
118
|
+
expect(exportSpy).toHaveBeenCalledTimes(1);
|
|
119
|
+
expect(workshopSpy).not.toHaveBeenCalled();
|
|
120
|
+
expect(statusSpy).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
it('top-level --new-session is passed through to workshop handler', async () => {
|
|
123
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
124
|
+
const program = buildCli({
|
|
125
|
+
workshopHandler: workshopSpy,
|
|
126
|
+
statusHandler: statusSpy,
|
|
127
|
+
exportHandler: exportSpy,
|
|
128
|
+
});
|
|
129
|
+
await program.parseAsync(['node', 'sofia', '--new-session']);
|
|
130
|
+
expect(workshopSpy).toHaveBeenCalledTimes(1);
|
|
131
|
+
const mergedOpts = workshopSpy.mock.calls[0][0];
|
|
132
|
+
expect(mergedOpts.newSession).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
it('top-level --session and --phase triggers direct command mode options', async () => {
|
|
135
|
+
const { buildCli } = await import('../../src/cli/index.js');
|
|
136
|
+
const program = buildCli({
|
|
137
|
+
workshopHandler: workshopSpy,
|
|
138
|
+
statusHandler: statusSpy,
|
|
139
|
+
exportHandler: exportSpy,
|
|
140
|
+
});
|
|
141
|
+
await program.parseAsync(['node', 'sofia', '--session', 's123', '--phase', 'Ideate']);
|
|
142
|
+
expect(workshopSpy).toHaveBeenCalledTimes(1);
|
|
143
|
+
const opts = workshopSpy.mock.calls[0][0];
|
|
144
|
+
expect(opts.session).toBe('s123');
|
|
145
|
+
expect(opts.phase).toBe('Ideate');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: Direct command non-TTY mode (T046)
|
|
3
|
+
*
|
|
4
|
+
* Tests the direct command entrypoint in non-TTY / automation contexts.
|
|
5
|
+
*
|
|
6
|
+
* Verifies:
|
|
7
|
+
* - Fails fast with non-zero exit code when --session is missing
|
|
8
|
+
* - Fails fast when --phase is missing in non-interactive mode
|
|
9
|
+
* - JSON-only stdout when --json specified (no human text leaks)
|
|
10
|
+
* - Activity/telemetry goes to the activity log (stderr equivalent), not stdout
|
|
11
|
+
* - Retry flag retries transient failures the specified number of times
|
|
12
|
+
* - Actionable error messages in JSON format
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { tmpdir } from 'node:os';
|
|
18
|
+
import { createFakeCopilotClient } from '../../src/shared/copilotClient.js';
|
|
19
|
+
import { SessionStore } from '../../src/sessions/sessionStore.js';
|
|
20
|
+
import { runDirectCommand, } from '../../src/cli/directCommands.js';
|
|
21
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
22
|
+
function createTestSession(overrides) {
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
return {
|
|
25
|
+
sessionId: 'test-direct-nontty',
|
|
26
|
+
schemaVersion: '1.0.0',
|
|
27
|
+
createdAt: now,
|
|
28
|
+
updatedAt: now,
|
|
29
|
+
phase: 'Discover',
|
|
30
|
+
status: 'Active',
|
|
31
|
+
participants: [],
|
|
32
|
+
artifacts: { generatedFiles: [] },
|
|
33
|
+
turns: [],
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function createNonTtyIO(decisionGateChoice = { choice: 'continue' }, inputs = [null]) {
|
|
38
|
+
let inputIdx = 0;
|
|
39
|
+
const output = [];
|
|
40
|
+
const activityLog = [];
|
|
41
|
+
return {
|
|
42
|
+
write(text) { output.push(text); },
|
|
43
|
+
writeActivity(text) { activityLog.push(text); },
|
|
44
|
+
writeToolSummary(_toolName, _summary) { },
|
|
45
|
+
async readInput(_prompt) {
|
|
46
|
+
if (inputIdx >= inputs.length)
|
|
47
|
+
return null;
|
|
48
|
+
return inputs[inputIdx++];
|
|
49
|
+
},
|
|
50
|
+
async showDecisionGate(_phase) {
|
|
51
|
+
return decisionGateChoice;
|
|
52
|
+
},
|
|
53
|
+
isJsonMode: true,
|
|
54
|
+
isTTY: false,
|
|
55
|
+
output,
|
|
56
|
+
activityLog,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
60
|
+
describe('Direct command non-TTY mode', () => {
|
|
61
|
+
let tmpDir;
|
|
62
|
+
let store;
|
|
63
|
+
beforeEach(async () => {
|
|
64
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-direct-nontty-'));
|
|
65
|
+
store = new SessionStore(tmpDir);
|
|
66
|
+
});
|
|
67
|
+
afterEach(async () => {
|
|
68
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
69
|
+
});
|
|
70
|
+
it('fails with exit code 1 when session is missing', async () => {
|
|
71
|
+
const io = createNonTtyIO();
|
|
72
|
+
const client = createFakeCopilotClient([]);
|
|
73
|
+
const result = await runDirectCommand({
|
|
74
|
+
sessionId: undefined,
|
|
75
|
+
phase: 'Discover',
|
|
76
|
+
store,
|
|
77
|
+
client,
|
|
78
|
+
io,
|
|
79
|
+
nonInteractive: true,
|
|
80
|
+
});
|
|
81
|
+
expect(result.exitCode).toBe(1);
|
|
82
|
+
expect(result.error).toContain('session');
|
|
83
|
+
});
|
|
84
|
+
it('fails with exit code 1 when phase is missing in non-interactive mode', async () => {
|
|
85
|
+
const session = createTestSession();
|
|
86
|
+
await store.save(session);
|
|
87
|
+
const io = createNonTtyIO();
|
|
88
|
+
const client = createFakeCopilotClient([]);
|
|
89
|
+
const result = await runDirectCommand({
|
|
90
|
+
sessionId: session.sessionId,
|
|
91
|
+
phase: undefined,
|
|
92
|
+
store,
|
|
93
|
+
client,
|
|
94
|
+
io,
|
|
95
|
+
nonInteractive: true,
|
|
96
|
+
});
|
|
97
|
+
expect(result.exitCode).toBe(1);
|
|
98
|
+
expect(result.error).toContain('phase');
|
|
99
|
+
});
|
|
100
|
+
it('outputs JSON-only on stdout with --json', async () => {
|
|
101
|
+
const session = createTestSession({ phase: 'Discover' });
|
|
102
|
+
await store.save(session);
|
|
103
|
+
const io = createNonTtyIO({ choice: 'exit' });
|
|
104
|
+
const client = createFakeCopilotClient([
|
|
105
|
+
{ role: 'assistant', content: '{"businessContext": {"company": "Test Corp"}}' },
|
|
106
|
+
]);
|
|
107
|
+
await runDirectCommand({
|
|
108
|
+
sessionId: session.sessionId,
|
|
109
|
+
phase: 'Discover',
|
|
110
|
+
store,
|
|
111
|
+
client,
|
|
112
|
+
io,
|
|
113
|
+
json: true,
|
|
114
|
+
});
|
|
115
|
+
// All stdout output should be valid JSON lines
|
|
116
|
+
for (const line of io.output.filter(l => l.trim())) {
|
|
117
|
+
expect(() => JSON.parse(line)).not.toThrow();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
it('sends activity to activityLog, not stdout', async () => {
|
|
121
|
+
const session = createTestSession({ phase: 'Discover' });
|
|
122
|
+
await store.save(session);
|
|
123
|
+
const io = createNonTtyIO({ choice: 'exit' });
|
|
124
|
+
const client = createFakeCopilotClient([
|
|
125
|
+
{ role: 'assistant', content: 'Analyzing your business context.' },
|
|
126
|
+
]);
|
|
127
|
+
await runDirectCommand({
|
|
128
|
+
sessionId: session.sessionId,
|
|
129
|
+
phase: 'Discover',
|
|
130
|
+
store,
|
|
131
|
+
client,
|
|
132
|
+
io,
|
|
133
|
+
json: true,
|
|
134
|
+
debug: true,
|
|
135
|
+
});
|
|
136
|
+
// Output should not contain activity markers
|
|
137
|
+
const stdoutText = io.output.join('');
|
|
138
|
+
expect(stdoutText).not.toContain('[activity]');
|
|
139
|
+
});
|
|
140
|
+
it('returns actionable error as JSON', async () => {
|
|
141
|
+
const io = createNonTtyIO();
|
|
142
|
+
const client = createFakeCopilotClient([]);
|
|
143
|
+
const result = await runDirectCommand({
|
|
144
|
+
sessionId: 'missing-session',
|
|
145
|
+
phase: 'Discover',
|
|
146
|
+
store,
|
|
147
|
+
client,
|
|
148
|
+
io,
|
|
149
|
+
json: true,
|
|
150
|
+
nonInteractive: true,
|
|
151
|
+
});
|
|
152
|
+
expect(result.exitCode).toBe(1);
|
|
153
|
+
expect(result.error).toBeDefined();
|
|
154
|
+
// When json mode, the IO output should have JSON error
|
|
155
|
+
const jsonErrors = io.output.filter(l => {
|
|
156
|
+
try {
|
|
157
|
+
const o = JSON.parse(l);
|
|
158
|
+
return o.error;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
expect(jsonErrors.length).toBeGreaterThan(0);
|
|
165
|
+
});
|
|
166
|
+
it('retries transient failures when --retry specified', async () => {
|
|
167
|
+
const session = createTestSession({ phase: 'Discover' });
|
|
168
|
+
await store.save(session);
|
|
169
|
+
let callCount = 0;
|
|
170
|
+
const client = createFakeCopilotClient([], {
|
|
171
|
+
onChat: async () => {
|
|
172
|
+
callCount++;
|
|
173
|
+
if (callCount <= 2) {
|
|
174
|
+
throw Object.assign(new Error('Connection refused'), { code: 'ECONNREFUSED' });
|
|
175
|
+
}
|
|
176
|
+
return { role: 'assistant', content: 'Business context captured.' };
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
// Each retry attempt consumes one input (the error thrown by onChat breaks the
|
|
180
|
+
// loop before readInput is called again). Provide one input per attempt, then
|
|
181
|
+
// null to end the successful run.
|
|
182
|
+
const io = createNonTtyIO({ choice: 'exit' }, [
|
|
183
|
+
'attempt 1', // consumed by attempt 1 (fails)
|
|
184
|
+
'attempt 2', // consumed by attempt 2 (fails)
|
|
185
|
+
'attempt 3', // consumed by attempt 3 (succeeds, then loop reads again)
|
|
186
|
+
null, // ends the successful loop iteration
|
|
187
|
+
]);
|
|
188
|
+
await runDirectCommand({
|
|
189
|
+
sessionId: session.sessionId,
|
|
190
|
+
phase: 'Discover',
|
|
191
|
+
store,
|
|
192
|
+
client,
|
|
193
|
+
io,
|
|
194
|
+
retry: 3,
|
|
195
|
+
});
|
|
196
|
+
// Should have retried and eventually succeeded
|
|
197
|
+
expect(callCount).toBeGreaterThan(1);
|
|
198
|
+
});
|
|
199
|
+
it('fails after exhausting retries', async () => {
|
|
200
|
+
const session = createTestSession({ phase: 'Discover' });
|
|
201
|
+
await store.save(session);
|
|
202
|
+
const client = createFakeCopilotClient([], {
|
|
203
|
+
onChat: async () => {
|
|
204
|
+
throw Object.assign(new Error('Connection refused'), { code: 'ECONNREFUSED' });
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
// Each retry attempt consumes one input. retry=2 means 3 total attempts.
|
|
208
|
+
const io = createNonTtyIO({ choice: 'exit' }, [
|
|
209
|
+
'attempt 1', // attempt 1 (fails)
|
|
210
|
+
'attempt 2', // attempt 2/retry 1 (fails)
|
|
211
|
+
'attempt 3', // attempt 3/retry 2 (fails, exhausted)
|
|
212
|
+
]);
|
|
213
|
+
const result = await runDirectCommand({
|
|
214
|
+
sessionId: session.sessionId,
|
|
215
|
+
phase: 'Discover',
|
|
216
|
+
store,
|
|
217
|
+
client,
|
|
218
|
+
io,
|
|
219
|
+
retry: 2,
|
|
220
|
+
});
|
|
221
|
+
expect(result.exitCode).toBe(1);
|
|
222
|
+
expect(result.error).toContain('Connection refused');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: Direct command TTY mode (T045)
|
|
3
|
+
*
|
|
4
|
+
* Tests direct command entry with --session and --phase flags
|
|
5
|
+
* in a TTY environment where interactive prompts are available.
|
|
6
|
+
*
|
|
7
|
+
* Verifies:
|
|
8
|
+
* - `--session <id> --phase <phase>` jumps to the requested phase
|
|
9
|
+
* - Session is loaded and used for the conversation loop
|
|
10
|
+
* - TTY mode prompts for missing inputs
|
|
11
|
+
* - Decision gates work in direct TTY mode
|
|
12
|
+
* - Session state is correctly updated after direct phase run
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { tmpdir } from 'node:os';
|
|
18
|
+
import { createFakeCopilotClient } from '../../src/shared/copilotClient.js';
|
|
19
|
+
import { SessionStore } from '../../src/sessions/sessionStore.js';
|
|
20
|
+
import { runDirectCommand, } from '../../src/cli/directCommands.js';
|
|
21
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
22
|
+
function createTestSession(overrides) {
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
return {
|
|
25
|
+
sessionId: 'test-direct-tty',
|
|
26
|
+
schemaVersion: '1.0.0',
|
|
27
|
+
createdAt: now,
|
|
28
|
+
updatedAt: now,
|
|
29
|
+
phase: 'Discover',
|
|
30
|
+
status: 'Active',
|
|
31
|
+
participants: [],
|
|
32
|
+
artifacts: { generatedFiles: [] },
|
|
33
|
+
turns: [],
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function createScriptedIO(inputs, decisionGateChoice = { choice: 'exit' }) {
|
|
38
|
+
let inputIdx = 0;
|
|
39
|
+
const output = [];
|
|
40
|
+
const activityLog = [];
|
|
41
|
+
return {
|
|
42
|
+
write(text) { output.push(text); },
|
|
43
|
+
writeActivity(text) { activityLog.push(text); },
|
|
44
|
+
writeToolSummary(_toolName, _summary) { },
|
|
45
|
+
async readInput(_prompt) {
|
|
46
|
+
if (inputIdx >= inputs.length)
|
|
47
|
+
return null;
|
|
48
|
+
return inputs[inputIdx++];
|
|
49
|
+
},
|
|
50
|
+
async showDecisionGate(_phase) {
|
|
51
|
+
return decisionGateChoice;
|
|
52
|
+
},
|
|
53
|
+
isJsonMode: false,
|
|
54
|
+
isTTY: true,
|
|
55
|
+
output,
|
|
56
|
+
activityLog,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
60
|
+
describe('Direct command TTY mode', () => {
|
|
61
|
+
let tmpDir;
|
|
62
|
+
let store;
|
|
63
|
+
beforeEach(async () => {
|
|
64
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-direct-tty-'));
|
|
65
|
+
store = new SessionStore(tmpDir);
|
|
66
|
+
});
|
|
67
|
+
afterEach(async () => {
|
|
68
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
69
|
+
});
|
|
70
|
+
it('runs a specific phase with --session and --phase', async () => {
|
|
71
|
+
const session = createTestSession({ phase: 'Ideate' });
|
|
72
|
+
await store.save(session);
|
|
73
|
+
const io = createScriptedIO(['Here are some ideas about AI automation'], { choice: 'exit' });
|
|
74
|
+
const client = createFakeCopilotClient([
|
|
75
|
+
{ role: 'assistant', content: '{"ideas": [{"title": "AI Helper", "description": "An AI assistant"}]}' },
|
|
76
|
+
]);
|
|
77
|
+
const result = await runDirectCommand({
|
|
78
|
+
sessionId: session.sessionId,
|
|
79
|
+
phase: 'Ideate',
|
|
80
|
+
store,
|
|
81
|
+
client,
|
|
82
|
+
io,
|
|
83
|
+
});
|
|
84
|
+
expect(result.exitCode).toBe(0);
|
|
85
|
+
const loaded = await store.load(session.sessionId);
|
|
86
|
+
expect(loaded.phase).toBe('Ideate');
|
|
87
|
+
});
|
|
88
|
+
it('prompts for missing input in TTY mode', async () => {
|
|
89
|
+
const session = createTestSession();
|
|
90
|
+
await store.save(session);
|
|
91
|
+
const io = createScriptedIO(['My business is a retail company'], { choice: 'exit' });
|
|
92
|
+
const client = createFakeCopilotClient([
|
|
93
|
+
{ role: 'assistant', content: 'Thank you. Let me analyze your business context.' },
|
|
94
|
+
]);
|
|
95
|
+
const result = await runDirectCommand({
|
|
96
|
+
sessionId: session.sessionId,
|
|
97
|
+
phase: 'Discover',
|
|
98
|
+
store,
|
|
99
|
+
client,
|
|
100
|
+
io,
|
|
101
|
+
});
|
|
102
|
+
// TTY mode should succeed — it can prompt
|
|
103
|
+
expect(result.exitCode).toBe(0);
|
|
104
|
+
});
|
|
105
|
+
it('returns error when session not found', async () => {
|
|
106
|
+
const io = createScriptedIO([]);
|
|
107
|
+
const client = createFakeCopilotClient([]);
|
|
108
|
+
const result = await runDirectCommand({
|
|
109
|
+
sessionId: 'nonexistent-session',
|
|
110
|
+
phase: 'Discover',
|
|
111
|
+
store,
|
|
112
|
+
client,
|
|
113
|
+
io,
|
|
114
|
+
});
|
|
115
|
+
expect(result.exitCode).toBe(1);
|
|
116
|
+
expect(result.error).toContain('not found');
|
|
117
|
+
});
|
|
118
|
+
it('returns error for invalid phase', async () => {
|
|
119
|
+
const session = createTestSession();
|
|
120
|
+
await store.save(session);
|
|
121
|
+
const io = createScriptedIO([]);
|
|
122
|
+
const client = createFakeCopilotClient([]);
|
|
123
|
+
const result = await runDirectCommand({
|
|
124
|
+
sessionId: session.sessionId,
|
|
125
|
+
phase: 'InvalidPhase',
|
|
126
|
+
store,
|
|
127
|
+
client,
|
|
128
|
+
io,
|
|
129
|
+
});
|
|
130
|
+
expect(result.exitCode).toBe(1);
|
|
131
|
+
expect(result.error).toContain('Invalid phase');
|
|
132
|
+
});
|
|
133
|
+
it('persists session after running a phase', async () => {
|
|
134
|
+
const session = createTestSession({ phase: 'Discover' });
|
|
135
|
+
await store.save(session);
|
|
136
|
+
const io = createScriptedIO(['We are a tech company'], { choice: 'exit' });
|
|
137
|
+
const client = createFakeCopilotClient([
|
|
138
|
+
{ role: 'assistant', content: 'Great, I understand your business context.' },
|
|
139
|
+
]);
|
|
140
|
+
await runDirectCommand({
|
|
141
|
+
sessionId: session.sessionId,
|
|
142
|
+
phase: 'Discover',
|
|
143
|
+
store,
|
|
144
|
+
client,
|
|
145
|
+
io,
|
|
146
|
+
});
|
|
147
|
+
const loaded = await store.load(session.sessionId);
|
|
148
|
+
expect(loaded.updatedAt).not.toBe(session.updatedAt);
|
|
149
|
+
expect(loaded.turns.length).toBeGreaterThan(0);
|
|
150
|
+
});
|
|
151
|
+
});
|