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,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T050: E2E failure/recovery test.
|
|
3
|
+
*
|
|
4
|
+
* Verifies graceful termination; verifies `finalStatus` is "failed" or "partial"
|
|
5
|
+
* in session state; verifies `terminationReason: "max-iterations"`;
|
|
6
|
+
* verifies user-facing output includes recovery guidance (Constitution VI compliance).
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
9
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
|
|
14
|
+
import { RalphLoop } from '../../src/develop/ralphLoop.js';
|
|
15
|
+
import { PocScaffolder } from '../../src/develop/pocScaffolder.js';
|
|
16
|
+
import { TestRunner } from '../../src/develop/testRunner.js';
|
|
17
|
+
import type { WorkshopSession } from '../../src/shared/schemas/session.js';
|
|
18
|
+
import type { LoopIO } from '../../src/loop/conversationLoop.js';
|
|
19
|
+
import type { CopilotClient } from '../../src/shared/copilotClient.js';
|
|
20
|
+
import type { TestResults } from '../../src/shared/schemas/session.js';
|
|
21
|
+
|
|
22
|
+
vi.mock('node:child_process', async (importOriginal) => {
|
|
23
|
+
const actual = await importOriginal<typeof import('node:child_process')>();
|
|
24
|
+
return {
|
|
25
|
+
...actual,
|
|
26
|
+
spawn: vi.fn((cmd: string, args: string[]) => {
|
|
27
|
+
if (cmd === 'npm' && args.includes('install')) {
|
|
28
|
+
return {
|
|
29
|
+
stdout: { on: vi.fn() },
|
|
30
|
+
stderr: { on: vi.fn() },
|
|
31
|
+
on: vi.fn((event: string, cb: (code: number) => void) => {
|
|
32
|
+
if (event === 'close') cb(0);
|
|
33
|
+
}),
|
|
34
|
+
kill: vi.fn(),
|
|
35
|
+
killed: false,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return actual.spawn(cmd, args);
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const require = createRequire(import.meta.url);
|
|
44
|
+
const fixtureSession: WorkshopSession = require('../fixtures/completedSession.json') as WorkshopSession;
|
|
45
|
+
|
|
46
|
+
describe('E2E: failure/recovery (T050)', () => {
|
|
47
|
+
let tmpDir: string;
|
|
48
|
+
let originalExitCode: number | undefined;
|
|
49
|
+
|
|
50
|
+
beforeEach(async () => {
|
|
51
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-e2e-failure-'));
|
|
52
|
+
originalExitCode = process.exitCode as number | undefined;
|
|
53
|
+
process.exitCode = undefined;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(async () => {
|
|
57
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
58
|
+
process.exitCode = originalExitCode;
|
|
59
|
+
vi.restoreAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function makeIo(): LoopIO & { writtenLines: string[]; activityLines: string[] } {
|
|
63
|
+
const writtenLines: string[] = [];
|
|
64
|
+
const activityLines: string[] = [];
|
|
65
|
+
return {
|
|
66
|
+
writtenLines,
|
|
67
|
+
activityLines,
|
|
68
|
+
write: vi.fn((text: string) => { writtenLines.push(text); }),
|
|
69
|
+
writeActivity: vi.fn((text: string) => { activityLines.push(text); }),
|
|
70
|
+
writeToolSummary: vi.fn(),
|
|
71
|
+
readInput: vi.fn().mockResolvedValue(null),
|
|
72
|
+
showDecisionGate: vi.fn(),
|
|
73
|
+
isJsonMode: false,
|
|
74
|
+
isTTY: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function makeFakeScaffolder(outputDir: string): PocScaffolder {
|
|
79
|
+
return {
|
|
80
|
+
scaffold: vi.fn().mockImplementation(async () => {
|
|
81
|
+
const { writeFile, mkdir } = await import('node:fs/promises');
|
|
82
|
+
await mkdir(join(outputDir, 'src'), { recursive: true });
|
|
83
|
+
await writeFile(join(outputDir, 'package.json'), JSON.stringify({
|
|
84
|
+
name: 'test-poc',
|
|
85
|
+
scripts: { test: 'vitest run' },
|
|
86
|
+
dependencies: {},
|
|
87
|
+
devDependencies: {},
|
|
88
|
+
}), 'utf-8');
|
|
89
|
+
await writeFile(join(outputDir, 'src', 'index.ts'), 'export function main() {}', 'utf-8');
|
|
90
|
+
return {
|
|
91
|
+
createdFiles: ['package.json', 'src/index.ts'],
|
|
92
|
+
skippedFiles: [],
|
|
93
|
+
context: {
|
|
94
|
+
projectName: 'test-poc',
|
|
95
|
+
ideaTitle: 'Test',
|
|
96
|
+
ideaDescription: 'Test',
|
|
97
|
+
techStack: { language: 'TypeScript', runtime: 'Node.js 20', testRunner: 'npm test' },
|
|
98
|
+
planSummary: 'Test',
|
|
99
|
+
sessionId: fixtureSession.sessionId,
|
|
100
|
+
outputDir,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}),
|
|
104
|
+
getTemplateFiles: () => [],
|
|
105
|
+
} as unknown as PocScaffolder;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function makeAlwaysFailingClient(): CopilotClient {
|
|
109
|
+
return {
|
|
110
|
+
createSession: vi.fn().mockResolvedValue({
|
|
111
|
+
send: vi.fn().mockReturnValue({
|
|
112
|
+
async *[Symbol.asyncIterator]() {
|
|
113
|
+
yield { type: 'TextDelta', text: '', timestamp: '' };
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
getHistory: () => [],
|
|
117
|
+
}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function makeAlwaysFailingTestRunner(): TestRunner {
|
|
122
|
+
return {
|
|
123
|
+
run: vi.fn().mockResolvedValue({
|
|
124
|
+
passed: 0,
|
|
125
|
+
failed: 1,
|
|
126
|
+
skipped: 0,
|
|
127
|
+
total: 1,
|
|
128
|
+
durationMs: 400,
|
|
129
|
+
failures: [{ testName: 'test', message: 'always fails' }],
|
|
130
|
+
rawOutput: '',
|
|
131
|
+
} satisfies TestResults),
|
|
132
|
+
} as unknown as TestRunner;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
it('terminates with max-iterations when all tests keep failing', async () => {
|
|
136
|
+
const io = makeIo();
|
|
137
|
+
const scaffolder = makeFakeScaffolder(tmpDir);
|
|
138
|
+
const client = makeAlwaysFailingClient();
|
|
139
|
+
const testRunner = makeAlwaysFailingTestRunner();
|
|
140
|
+
|
|
141
|
+
const ralph = new RalphLoop({
|
|
142
|
+
client,
|
|
143
|
+
io,
|
|
144
|
+
session: fixtureSession,
|
|
145
|
+
outputDir: tmpDir,
|
|
146
|
+
maxIterations: 2,
|
|
147
|
+
testRunner,
|
|
148
|
+
scaffolder,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const result = await ralph.run();
|
|
152
|
+
|
|
153
|
+
expect(result.terminationReason).toBe('max-iterations');
|
|
154
|
+
expect(['failed', 'partial']).toContain(result.finalStatus);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('verifies terminationReason=max-iterations in session state', async () => {
|
|
158
|
+
const io = makeIo();
|
|
159
|
+
const scaffolder = makeFakeScaffolder(tmpDir);
|
|
160
|
+
const client = makeAlwaysFailingClient();
|
|
161
|
+
const testRunner = makeAlwaysFailingTestRunner();
|
|
162
|
+
|
|
163
|
+
const ralph = new RalphLoop({
|
|
164
|
+
client,
|
|
165
|
+
io,
|
|
166
|
+
session: fixtureSession,
|
|
167
|
+
outputDir: tmpDir,
|
|
168
|
+
maxIterations: 2,
|
|
169
|
+
testRunner,
|
|
170
|
+
scaffolder,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const result = await ralph.run();
|
|
174
|
+
expect(result.session.poc?.terminationReason).toBe('max-iterations');
|
|
175
|
+
expect(result.session.poc?.finalStatus).toBeDefined();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('session has iteration history after failed loop', async () => {
|
|
179
|
+
const io = makeIo();
|
|
180
|
+
const scaffolder = makeFakeScaffolder(tmpDir);
|
|
181
|
+
const client = makeAlwaysFailingClient();
|
|
182
|
+
const testRunner = makeAlwaysFailingTestRunner();
|
|
183
|
+
|
|
184
|
+
const ralph = new RalphLoop({
|
|
185
|
+
client,
|
|
186
|
+
io,
|
|
187
|
+
session: fixtureSession,
|
|
188
|
+
outputDir: tmpDir,
|
|
189
|
+
maxIterations: 2,
|
|
190
|
+
testRunner,
|
|
191
|
+
scaffolder,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const result = await ralph.run();
|
|
195
|
+
|
|
196
|
+
// Should have at least scaffold iteration
|
|
197
|
+
expect(result.session.poc?.iterations.length).toBeGreaterThan(0);
|
|
198
|
+
expect(result.session.poc?.iterations[0].outcome).toBe('scaffold');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('shows recovery guidance in non-JSON output for failed status (Constitution VI)', async () => {
|
|
202
|
+
const { developCommand } = await import('../../src/cli/developCommand.js');
|
|
203
|
+
|
|
204
|
+
const devIo = makeIo();
|
|
205
|
+
const client = makeAlwaysFailingClient();
|
|
206
|
+
|
|
207
|
+
const store = {
|
|
208
|
+
load: vi.fn().mockResolvedValue(fixtureSession),
|
|
209
|
+
save: vi.fn().mockResolvedValue(undefined),
|
|
210
|
+
list: vi.fn().mockResolvedValue([fixtureSession.sessionId]),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Mock RalphLoop.prototype.run to return failed immediately
|
|
214
|
+
const originalRun = RalphLoop.prototype.run;
|
|
215
|
+
const sessionWithFailedPoc: WorkshopSession = {
|
|
216
|
+
...fixtureSession,
|
|
217
|
+
poc: {
|
|
218
|
+
repoSource: 'local' as const,
|
|
219
|
+
repoPath: tmpDir,
|
|
220
|
+
iterations: [],
|
|
221
|
+
finalStatus: 'failed' as const,
|
|
222
|
+
terminationReason: 'max-iterations' as const,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
RalphLoop.prototype.run = vi.fn().mockResolvedValue({
|
|
226
|
+
session: sessionWithFailedPoc,
|
|
227
|
+
finalStatus: 'failed' as const,
|
|
228
|
+
terminationReason: 'max-iterations' as const,
|
|
229
|
+
iterationsCompleted: 2,
|
|
230
|
+
outputDir: tmpDir,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
await developCommand(
|
|
235
|
+
{ session: fixtureSession.sessionId, maxIterations: 1, output: tmpDir },
|
|
236
|
+
{ store, io: devIo, client },
|
|
237
|
+
);
|
|
238
|
+
} finally {
|
|
239
|
+
RalphLoop.prototype.run = originalRun;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const allOutput = devIo.writtenLines.join('\n');
|
|
243
|
+
// developCommand should show recovery guidance for non-success status
|
|
244
|
+
expect(allOutput).toMatch(/resume|retry|force|more.*iter/i);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('sets process.exitCode=1 when loop terminates with failed status', async () => {
|
|
248
|
+
const { developCommand } = await import('../../src/cli/developCommand.js');
|
|
249
|
+
const devIo = makeIo();
|
|
250
|
+
const client = makeAlwaysFailingClient();
|
|
251
|
+
|
|
252
|
+
const store = {
|
|
253
|
+
load: vi.fn().mockResolvedValue(fixtureSession),
|
|
254
|
+
save: vi.fn().mockResolvedValue(undefined),
|
|
255
|
+
list: vi.fn().mockResolvedValue([fixtureSession.sessionId]),
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const sessionWithFailedPoc: WorkshopSession = {
|
|
259
|
+
...fixtureSession,
|
|
260
|
+
poc: {
|
|
261
|
+
repoSource: 'local' as const,
|
|
262
|
+
repoPath: tmpDir,
|
|
263
|
+
iterations: [],
|
|
264
|
+
finalStatus: 'failed' as const,
|
|
265
|
+
terminationReason: 'max-iterations' as const,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const originalRun = RalphLoop.prototype.run;
|
|
270
|
+
RalphLoop.prototype.run = vi.fn().mockResolvedValue({
|
|
271
|
+
session: sessionWithFailedPoc,
|
|
272
|
+
finalStatus: 'failed' as const,
|
|
273
|
+
terminationReason: 'max-iterations' as const,
|
|
274
|
+
iterationsCompleted: 2,
|
|
275
|
+
outputDir: tmpDir,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await developCommand(
|
|
280
|
+
{ session: fixtureSession.sessionId },
|
|
281
|
+
{ store, io: devIo, client },
|
|
282
|
+
);
|
|
283
|
+
} finally {
|
|
284
|
+
RalphLoop.prototype.run = originalRun;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
expect(process.exitCode).toBe(1);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T049-T051: PTY-based interactive E2E tests for `sofia dev`.
|
|
3
|
+
*
|
|
4
|
+
* Validates Ctrl+C handling, progress output, and clean exit behavior.
|
|
5
|
+
* Gracefully skips if node-pty allocation fails (e.g., CI without TTY).
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
|
|
9
|
+
// ── PTY availability guard (T051) ────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
let pty: typeof import('node-pty') | undefined;
|
|
12
|
+
let ptyAvailable = false;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
pty = await import('node-pty');
|
|
16
|
+
// Attempt a minimal allocation to verify PTY works
|
|
17
|
+
const testProc = pty.spawn('echo', ['test'], { cols: 80, rows: 24 });
|
|
18
|
+
testProc.kill();
|
|
19
|
+
ptyAvailable = true;
|
|
20
|
+
} catch {
|
|
21
|
+
ptyAvailable = false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('PTY-based E2E: sofia dev', () => {
|
|
25
|
+
// T051: Skip gracefully if node-pty allocation fails
|
|
26
|
+
const itPty = ptyAvailable ? it : it.skip;
|
|
27
|
+
|
|
28
|
+
itPty('help output appears in PTY buffer (T050)', async () => {
|
|
29
|
+
if (!pty) return;
|
|
30
|
+
|
|
31
|
+
const proc = pty.spawn('npx', ['tsx', 'src/cli/index.ts', 'dev', '--help'], {
|
|
32
|
+
cols: 120,
|
|
33
|
+
rows: 40,
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
env: { ...process.env },
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
let output = '';
|
|
39
|
+
proc.onData((data: string) => {
|
|
40
|
+
output += data;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const exitCode = await new Promise<number>((resolve) => {
|
|
44
|
+
proc.onExit(({ exitCode: code }) => {
|
|
45
|
+
resolve(code);
|
|
46
|
+
});
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
proc.kill();
|
|
49
|
+
resolve(-1);
|
|
50
|
+
}, 15_000);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// --help should produce usage output containing 'dev'
|
|
54
|
+
expect(output).toContain('dev');
|
|
55
|
+
expect(exitCode).toBe(0);
|
|
56
|
+
}, 20_000);
|
|
57
|
+
|
|
58
|
+
itPty('Ctrl+C sends signal to running process (T049)', async () => {
|
|
59
|
+
if (!pty) return;
|
|
60
|
+
|
|
61
|
+
// Use a simple process that sleeps, then send Ctrl+C
|
|
62
|
+
const proc = pty.spawn('sleep', ['30'], {
|
|
63
|
+
cols: 80,
|
|
64
|
+
rows: 24,
|
|
65
|
+
cwd: process.cwd(),
|
|
66
|
+
env: { ...process.env },
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Wait briefly then send Ctrl+C
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
71
|
+
proc.write('\x03'); // Ctrl+C
|
|
72
|
+
|
|
73
|
+
const exitCode = await new Promise<number>((resolve) => {
|
|
74
|
+
proc.onExit(({ exitCode: code }) => {
|
|
75
|
+
resolve(code);
|
|
76
|
+
});
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
proc.kill();
|
|
79
|
+
resolve(-999);
|
|
80
|
+
}, 5_000);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Process should have been interrupted (not timed out)
|
|
84
|
+
expect(exitCode).not.toBe(-999);
|
|
85
|
+
}, 10_000);
|
|
86
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T041: Discovery web search enrichment relevance validation (SC-003-005).
|
|
3
|
+
*
|
|
4
|
+
* Validates that discovery web search enrichment retrieves keyword-relevant
|
|
5
|
+
* context for at least 3 out of 5 test company descriptions.
|
|
6
|
+
* Gated behind SOFIA_LIVE_MCP_TESTS=true because it requires real web search.
|
|
7
|
+
*
|
|
8
|
+
* Acceptance criteria:
|
|
9
|
+
* - Run enrichFromWebSearch() for 5 different company descriptions
|
|
10
|
+
* - At least 3/5 must return results with keyword-relevant content
|
|
11
|
+
* - "Keyword-relevant" = at least one result snippet contains a word from the company/industry
|
|
12
|
+
*/
|
|
13
|
+
import { describe, it, expect } from 'vitest';
|
|
14
|
+
|
|
15
|
+
const LIVE = process.env.SOFIA_LIVE_MCP_TESTS === 'true';
|
|
16
|
+
|
|
17
|
+
const TEST_COMPANIES = [
|
|
18
|
+
{
|
|
19
|
+
summary: '"Nestlé" is a global food and beverage company headquartered in Switzerland.',
|
|
20
|
+
keywords: ['food', 'beverage', 'switzerland', 'global', 'company'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
summary: '"Zara" is a global retail company headquartered in Spain.',
|
|
24
|
+
keywords: ['retail', 'fashion', 'spain', 'global', 'company'],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
summary:
|
|
28
|
+
'"Microsoft Corporation" is a global technology company headquartered in Redmond, Washington.',
|
|
29
|
+
keywords: ['technology', 'software', 'hardware', 'cloud', 'global'],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
summary:
|
|
33
|
+
'"Maersk" is a global shipping and logistics company headquartered in Copenhagen, Denmark.',
|
|
34
|
+
keywords: ['shipping', 'logistics', 'denmark', 'global', 'company'],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
summary:
|
|
38
|
+
'"Hasbro" is a global toy and entertainment company headquartered in Pawtucket, Rhode Island.',
|
|
39
|
+
keywords: ['toy', 'entertainment', 'rhode island', 'global', 'company'],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
describe.skipIf(!LIVE)('Discovery web search relevance validation (T041 / SC-003-005)', () => {
|
|
44
|
+
it('at least 3/5 company descriptions return keyword-relevant results', async () => {
|
|
45
|
+
const { DiscoveryEnricher } = await import('../../src/phases/discoveryEnricher.js');
|
|
46
|
+
const { createWebSearchTool } = await import('../../src/mcp/webSearch.js');
|
|
47
|
+
|
|
48
|
+
const webSearchFn = createWebSearchTool({
|
|
49
|
+
projectEndpoint: process.env.FOUNDRY_PROJECT_ENDPOINT!,
|
|
50
|
+
modelDeploymentName: process.env.FOUNDRY_MODEL_DEPLOYMENT_NAME!,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const webSearchClient = {
|
|
54
|
+
search: async (query: string) => webSearchFn(query),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const enricher = new DiscoveryEnricher();
|
|
58
|
+
let relevantCount = 0;
|
|
59
|
+
|
|
60
|
+
const results: Array<{ company: string; relevant: boolean; snippetCount: number }> = [];
|
|
61
|
+
|
|
62
|
+
for (const company of TEST_COMPANIES) {
|
|
63
|
+
const enrichment = await enricher.enrichFromWebSearch(company.summary, webSearchClient);
|
|
64
|
+
|
|
65
|
+
// Collect all result strings from enrichment
|
|
66
|
+
const allText = [
|
|
67
|
+
...(enrichment.companyNews ?? []),
|
|
68
|
+
...(enrichment.competitorInfo ?? []),
|
|
69
|
+
...(enrichment.industryTrends ?? []),
|
|
70
|
+
]
|
|
71
|
+
.join(' ')
|
|
72
|
+
.toLowerCase();
|
|
73
|
+
|
|
74
|
+
const snippetCount =
|
|
75
|
+
(enrichment.companyNews?.length ?? 0) +
|
|
76
|
+
(enrichment.competitorInfo?.length ?? 0) +
|
|
77
|
+
(enrichment.industryTrends?.length ?? 0);
|
|
78
|
+
|
|
79
|
+
// Check if any keyword appears in the results
|
|
80
|
+
const hasRelevantKeyword = company.keywords.some((kw) => allText.includes(kw.toLowerCase()));
|
|
81
|
+
|
|
82
|
+
if (hasRelevantKeyword && snippetCount > 0) {
|
|
83
|
+
relevantCount++;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
results.push({
|
|
87
|
+
company: company.summary.split('"')[1] || company.summary.slice(0, 30),
|
|
88
|
+
relevant: hasRelevantKeyword && snippetCount > 0,
|
|
89
|
+
snippetCount,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Log outcomes for manual review
|
|
94
|
+
console.log('=== T041 Web Search Relevance Validation ===');
|
|
95
|
+
for (const r of results) {
|
|
96
|
+
console.log(` ${r.relevant ? '✓' : '✗'} ${r.company}: ${r.snippetCount} snippets`);
|
|
97
|
+
}
|
|
98
|
+
console.log(`Result: ${relevantCount}/5 companies have relevant results`);
|
|
99
|
+
|
|
100
|
+
// Acceptance: at least 3 out of 5
|
|
101
|
+
expect(relevantCount).toBeGreaterThanOrEqual(3);
|
|
102
|
+
}, 120_000); // 2 minute timeout for multiple web searches
|
|
103
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E test harness skeleton.
|
|
3
|
+
*
|
|
4
|
+
* Uses node-pty to drive the sofIA CLI interactively, simulating
|
|
5
|
+
* user input and verifying streaming output. This is a skeleton —
|
|
6
|
+
* actual E2E test scenarios will be added in US1 (T021).
|
|
7
|
+
*
|
|
8
|
+
* Requirements:
|
|
9
|
+
* - node-pty must be installed (`npm install node-pty`)
|
|
10
|
+
* - Tests run under the `test:e2e` npm script
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { spawn } from 'node:child_process';
|
|
14
|
+
import { join, dirname } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const PROJECT_ROOT = join(__dirname, '..', '..');
|
|
19
|
+
const CLI_ENTRY = join(PROJECT_ROOT, 'src', 'cli', 'index.ts');
|
|
20
|
+
|
|
21
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
interface CliResult {
|
|
24
|
+
stdout: string;
|
|
25
|
+
stderr: string;
|
|
26
|
+
exitCode: number | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Run the sofIA CLI with given arguments and return output.
|
|
31
|
+
* Uses tsx to run TypeScript directly.
|
|
32
|
+
*/
|
|
33
|
+
function runCli(args: string[], timeoutMs = 10000): Promise<CliResult> {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const child = spawn('npx', ['tsx', CLI_ENTRY, ...args], {
|
|
36
|
+
cwd: PROJECT_ROOT,
|
|
37
|
+
env: { ...process.env, NODE_ENV: 'test' },
|
|
38
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const stdout: Buffer[] = [];
|
|
42
|
+
const stderr: Buffer[] = [];
|
|
43
|
+
|
|
44
|
+
child.stdout.on('data', (chunk) => stdout.push(chunk));
|
|
45
|
+
child.stderr.on('data', (chunk) => stderr.push(chunk));
|
|
46
|
+
|
|
47
|
+
const timer = setTimeout(() => {
|
|
48
|
+
child.kill('SIGTERM');
|
|
49
|
+
reject(new Error(`CLI timed out after ${timeoutMs}ms`));
|
|
50
|
+
}, timeoutMs);
|
|
51
|
+
|
|
52
|
+
child.on('close', (code) => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
resolve({
|
|
55
|
+
stdout: Buffer.concat(stdout).toString('utf-8'),
|
|
56
|
+
stderr: Buffer.concat(stderr).toString('utf-8'),
|
|
57
|
+
exitCode: code,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
child.on('error', (err) => {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
reject(err);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
describe('E2E Harness', () => {
|
|
71
|
+
it('displays help when invoked with --help', async () => {
|
|
72
|
+
const result = await runCli(['--help']);
|
|
73
|
+
expect(result.exitCode).toBe(0);
|
|
74
|
+
expect(result.stdout).toContain('sofIA');
|
|
75
|
+
expect(result.stdout).toContain('workshop');
|
|
76
|
+
expect(result.stdout).toContain('status');
|
|
77
|
+
expect(result.stdout).toContain('export');
|
|
78
|
+
}, 15_000);
|
|
79
|
+
|
|
80
|
+
it('displays version when invoked with --version', async () => {
|
|
81
|
+
const result = await runCli(['--version']);
|
|
82
|
+
expect(result.exitCode).toBe(0);
|
|
83
|
+
expect(result.stdout.trim()).toMatch(/^\d+\.\d+\.\d+$/);
|
|
84
|
+
}, 15_000);
|
|
85
|
+
|
|
86
|
+
it('shows workshop help', async () => {
|
|
87
|
+
const result = await runCli(['workshop', '--help']);
|
|
88
|
+
expect(result.exitCode).toBe(0);
|
|
89
|
+
expect(result.stdout).toContain('workshop');
|
|
90
|
+
}, 15_000);
|
|
91
|
+
|
|
92
|
+
it('lists sessions or reports none when status invoked without session', async () => {
|
|
93
|
+
const result = await runCli(['status', '--json']);
|
|
94
|
+
// Either lists sessions or reports no sessions found — both valid
|
|
95
|
+
const parsed = JSON.parse(result.stdout);
|
|
96
|
+
expect(parsed).toBeDefined();
|
|
97
|
+
expect('sessions' in parsed || 'error' in parsed).toBe(true);
|
|
98
|
+
}, 15_000);
|
|
99
|
+
|
|
100
|
+
it('returns error for export without session', async () => {
|
|
101
|
+
const result = await runCli(['export', '--json']);
|
|
102
|
+
expect(result.stdout).toContain('No session specified');
|
|
103
|
+
}, 15_000);
|
|
104
|
+
});
|