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,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test: Export fallback flow.
|
|
3
|
+
*
|
|
4
|
+
* Tests the full export pipeline with null structured data but present
|
|
5
|
+
* conversation turns — verifies that all phase files are generated.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import { mkdtemp, rm, readFile, readdir } from 'node:fs/promises';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
|
|
12
|
+
import { exportSession } from '../../src/sessions/exportWriter.js';
|
|
13
|
+
import type { WorkshopSession } from '../../src/shared/schemas/session.js';
|
|
14
|
+
|
|
15
|
+
let tmpDir: string;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'sofia-export-fallback-'));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('export fallback flow', () => {
|
|
26
|
+
it('generates 6 markdown files when all phases have turns but no structured data', async () => {
|
|
27
|
+
const now = new Date().toISOString();
|
|
28
|
+
const phases = ['Discover', 'Ideate', 'Design', 'Select', 'Plan', 'Develop'] as const;
|
|
29
|
+
|
|
30
|
+
const turns = phases.flatMap((phase, i) => [
|
|
31
|
+
{
|
|
32
|
+
phase,
|
|
33
|
+
sequence: i * 2 + 1,
|
|
34
|
+
role: 'user' as const,
|
|
35
|
+
content: `Tell me about the ${phase} phase`,
|
|
36
|
+
timestamp: now,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
phase,
|
|
40
|
+
sequence: i * 2 + 2,
|
|
41
|
+
role: 'assistant' as const,
|
|
42
|
+
content: `Here is information about the ${phase} phase.`,
|
|
43
|
+
timestamp: now,
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const session: WorkshopSession = {
|
|
48
|
+
sessionId: 'fallback-test',
|
|
49
|
+
schemaVersion: '1.0.0',
|
|
50
|
+
createdAt: now,
|
|
51
|
+
updatedAt: now,
|
|
52
|
+
phase: 'Complete',
|
|
53
|
+
status: 'Completed',
|
|
54
|
+
participants: [],
|
|
55
|
+
artifacts: { generatedFiles: [] },
|
|
56
|
+
turns,
|
|
57
|
+
// Discover has conversation fallback built-in
|
|
58
|
+
businessContext: {
|
|
59
|
+
businessDescription: 'Test Company',
|
|
60
|
+
challenges: ['Testing'],
|
|
61
|
+
},
|
|
62
|
+
// All other structured fields are null/undefined
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
await exportSession(session, tmpDir);
|
|
66
|
+
|
|
67
|
+
const files = await readdir(tmpDir);
|
|
68
|
+
expect(files).toContain('discover.md');
|
|
69
|
+
expect(files).toContain('ideate.md');
|
|
70
|
+
expect(files).toContain('design.md');
|
|
71
|
+
expect(files).toContain('select.md');
|
|
72
|
+
expect(files).toContain('plan.md');
|
|
73
|
+
expect(files).toContain('develop.md');
|
|
74
|
+
expect(files).toContain('summary.json');
|
|
75
|
+
|
|
76
|
+
// Verify each file has conversation content
|
|
77
|
+
for (const phase of phases) {
|
|
78
|
+
const content = await readFile(join(tmpDir, `${phase.toLowerCase()}.md`), 'utf-8');
|
|
79
|
+
expect(content).toContain('## Conversation');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Verify summary.json lists all files
|
|
83
|
+
const summaryRaw = await readFile(join(tmpDir, 'summary.json'), 'utf-8');
|
|
84
|
+
const summary = JSON.parse(summaryRaw) as { files: Array<{ path: string }>; highlights?: string[] };
|
|
85
|
+
const mdFiles = summary.files.filter((f) => f.path.endsWith('.md'));
|
|
86
|
+
expect(mdFiles.length).toBe(6);
|
|
87
|
+
|
|
88
|
+
// Verify highlights exist
|
|
89
|
+
expect(summary.highlights).toBeDefined();
|
|
90
|
+
expect(summary.highlights!.length).toBeGreaterThan(0);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns null for phase with neither structured data nor turns', async () => {
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
const session: WorkshopSession = {
|
|
96
|
+
sessionId: 'empty-test',
|
|
97
|
+
schemaVersion: '1.0.0',
|
|
98
|
+
createdAt: now,
|
|
99
|
+
updatedAt: now,
|
|
100
|
+
phase: 'Discover',
|
|
101
|
+
status: 'Active',
|
|
102
|
+
participants: [],
|
|
103
|
+
artifacts: { generatedFiles: [] },
|
|
104
|
+
turns: [],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
await exportSession(session, tmpDir);
|
|
108
|
+
const files = await readdir(tmpDir);
|
|
109
|
+
|
|
110
|
+
// Only summary.json should be generated — no phase files
|
|
111
|
+
expect(files).toContain('summary.json');
|
|
112
|
+
const mdFiles = files.filter((f) => f.endsWith('.md'));
|
|
113
|
+
expect(mdFiles).toHaveLength(0);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T042: Integration test for MCP degradation flow.
|
|
3
|
+
*
|
|
4
|
+
* Configures McpManager with servers marked unavailable (or stub transports
|
|
5
|
+
* that throw), runs GitHubMcpAdapter + McpContextEnricher calls, and asserts
|
|
6
|
+
* graceful degradation (no throws; adapters return { available: false, reason }
|
|
7
|
+
* or fallback context) per US1 Acceptance Scenario 4 and FR-013.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
|
+
|
|
11
|
+
import { McpManager } from '../../src/mcp/mcpManager.js';
|
|
12
|
+
import type { McpConfig } from '../../src/mcp/mcpManager.js';
|
|
13
|
+
import { GitHubMcpAdapter } from '../../src/develop/githubMcpAdapter.js';
|
|
14
|
+
import { McpContextEnricher } from '../../src/develop/mcpContextEnricher.js';
|
|
15
|
+
|
|
16
|
+
vi.mock('../../src/mcp/webSearch.js', () => ({
|
|
17
|
+
isWebSearchConfigured: vi.fn(() => false),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
function makeEmptyConfig(): McpConfig {
|
|
23
|
+
return { servers: {} };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function makeConfigWithServers(): McpConfig {
|
|
27
|
+
return {
|
|
28
|
+
servers: {
|
|
29
|
+
github: {
|
|
30
|
+
name: 'github',
|
|
31
|
+
type: 'http' as const,
|
|
32
|
+
url: 'https://api.github.com/mcp',
|
|
33
|
+
},
|
|
34
|
+
context7: {
|
|
35
|
+
name: 'context7',
|
|
36
|
+
type: 'stdio' as const,
|
|
37
|
+
command: 'npx',
|
|
38
|
+
args: ['-y', '@upstash/context7-mcp'],
|
|
39
|
+
},
|
|
40
|
+
azure: {
|
|
41
|
+
name: 'azure',
|
|
42
|
+
type: 'stdio' as const,
|
|
43
|
+
command: 'npx',
|
|
44
|
+
args: ['-y', '@azure/mcp'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
describe('MCP Degradation Flow (integration)', () => {
|
|
53
|
+
let originalGithubToken: string | undefined;
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
originalGithubToken = process.env.GITHUB_TOKEN;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(() => {
|
|
60
|
+
if (originalGithubToken !== undefined) {
|
|
61
|
+
process.env.GITHUB_TOKEN = originalGithubToken;
|
|
62
|
+
} else {
|
|
63
|
+
delete process.env.GITHUB_TOKEN;
|
|
64
|
+
}
|
|
65
|
+
vi.restoreAllMocks();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('GitHubMcpAdapter graceful degradation', () => {
|
|
69
|
+
it('returns available:false when GitHub MCP is not configured', async () => {
|
|
70
|
+
const manager = new McpManager(makeEmptyConfig());
|
|
71
|
+
const adapter = new GitHubMcpAdapter(manager);
|
|
72
|
+
|
|
73
|
+
expect(adapter.isAvailable()).toBe(false);
|
|
74
|
+
|
|
75
|
+
const createResult = await adapter.createRepository({ name: 'test-poc' });
|
|
76
|
+
expect(createResult.available).toBe(false);
|
|
77
|
+
if (!createResult.available) {
|
|
78
|
+
expect(createResult.reason).toBeDefined();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const pushResult = await adapter.pushFiles({
|
|
82
|
+
repoUrl: 'https://github.com/acme/test',
|
|
83
|
+
files: [{ path: 'index.ts', content: 'hello' }],
|
|
84
|
+
commitMessage: 'init',
|
|
85
|
+
});
|
|
86
|
+
expect(pushResult.available).toBe(false);
|
|
87
|
+
if (!pushResult.available) {
|
|
88
|
+
expect(pushResult.reason).toBeDefined();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('returns available:false when GitHub is configured but not connected', async () => {
|
|
93
|
+
const manager = new McpManager(makeConfigWithServers());
|
|
94
|
+
// Do NOT call markConnected — server is configured but unavailable
|
|
95
|
+
const adapter = new GitHubMcpAdapter(manager);
|
|
96
|
+
|
|
97
|
+
expect(adapter.isAvailable()).toBe(false);
|
|
98
|
+
|
|
99
|
+
const createResult = await adapter.createRepository({ name: 'test-poc' });
|
|
100
|
+
expect(createResult.available).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('never throws from createRepository or pushFiles', async () => {
|
|
104
|
+
const manager = new McpManager(makeConfigWithServers());
|
|
105
|
+
manager.markConnected('github');
|
|
106
|
+
|
|
107
|
+
// Stub callTool to throw
|
|
108
|
+
vi.spyOn(manager, 'callTool').mockRejectedValue(new Error('network crash'));
|
|
109
|
+
|
|
110
|
+
const adapter = new GitHubMcpAdapter(manager);
|
|
111
|
+
|
|
112
|
+
// Should not throw
|
|
113
|
+
const createResult = await adapter.createRepository({ name: 'test-poc' });
|
|
114
|
+
expect(createResult.available).toBe(false);
|
|
115
|
+
if (!createResult.available) {
|
|
116
|
+
expect(createResult.reason).toBeDefined();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const pushResult = await adapter.pushFiles({
|
|
120
|
+
repoUrl: 'https://github.com/acme/test',
|
|
121
|
+
files: [{ path: 'index.ts', content: 'hello' }],
|
|
122
|
+
commitMessage: 'init',
|
|
123
|
+
});
|
|
124
|
+
expect(pushResult.available).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('McpContextEnricher graceful degradation', () => {
|
|
129
|
+
it('returns empty context when all services unavailable', async () => {
|
|
130
|
+
const manager = new McpManager(makeEmptyConfig());
|
|
131
|
+
const enricher = new McpContextEnricher(manager);
|
|
132
|
+
|
|
133
|
+
const result = await enricher.enrich({
|
|
134
|
+
dependencies: ['express', 'zod'],
|
|
135
|
+
architectureNotes: 'Use Azure Cosmos DB',
|
|
136
|
+
stuckIterations: 3,
|
|
137
|
+
failingTests: ['test fails with TypeError'],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(result.combined).toBe('');
|
|
141
|
+
expect(result.libraryDocs).toBeUndefined();
|
|
142
|
+
expect(result.azureGuidance).toBeUndefined();
|
|
143
|
+
expect(result.webSearchResults).toBeUndefined();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('returns empty context when servers configured but not connected', async () => {
|
|
147
|
+
const manager = new McpManager(makeConfigWithServers());
|
|
148
|
+
// Not marking any server as connected
|
|
149
|
+
const enricher = new McpContextEnricher(manager);
|
|
150
|
+
|
|
151
|
+
const result = await enricher.enrich({
|
|
152
|
+
dependencies: ['express'],
|
|
153
|
+
architectureNotes: 'Use Azure Functions',
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(result.combined).toBe('');
|
|
157
|
+
expect(result.libraryDocs).toBeUndefined();
|
|
158
|
+
expect(result.azureGuidance).toBeUndefined();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('returns fallback context when callTool throws for all services', async () => {
|
|
162
|
+
const manager = new McpManager(makeConfigWithServers());
|
|
163
|
+
manager.markConnected('context7');
|
|
164
|
+
manager.markConnected('azure');
|
|
165
|
+
|
|
166
|
+
// Stub callTool to throw for all calls
|
|
167
|
+
vi.spyOn(manager, 'callTool').mockRejectedValue(new Error('transport broken'));
|
|
168
|
+
|
|
169
|
+
const enricher = new McpContextEnricher(manager);
|
|
170
|
+
|
|
171
|
+
const result = await enricher.enrich({
|
|
172
|
+
dependencies: ['express'],
|
|
173
|
+
architectureNotes: 'Use Azure Cosmos DB for data',
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Context7 should fall back to npm links
|
|
177
|
+
if (result.libraryDocs) {
|
|
178
|
+
expect(result.libraryDocs).toContain('npmjs.com');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Azure should fall back to static guidance
|
|
182
|
+
if (result.azureGuidance) {
|
|
183
|
+
expect(result.azureGuidance).toContain('DefaultAzureCredential');
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('never throws from enrich()', async () => {
|
|
188
|
+
const manager = new McpManager(makeConfigWithServers());
|
|
189
|
+
manager.markConnected('context7');
|
|
190
|
+
manager.markConnected('azure');
|
|
191
|
+
|
|
192
|
+
vi.spyOn(manager, 'callTool').mockRejectedValue(new Error('catastrophic'));
|
|
193
|
+
|
|
194
|
+
const enricher = new McpContextEnricher(manager);
|
|
195
|
+
|
|
196
|
+
// Should not throw
|
|
197
|
+
const result = await enricher.enrich({
|
|
198
|
+
dependencies: ['express', 'zod'],
|
|
199
|
+
architectureNotes: 'Use Azure Cosmos DB',
|
|
200
|
+
stuckIterations: 5,
|
|
201
|
+
failingTests: ['test fails'],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Should still return a valid EnrichedContext
|
|
205
|
+
expect(result).toHaveProperty('combined');
|
|
206
|
+
expect(typeof result.combined).toBe('string');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('Combined adapter + enricher degradation', () => {
|
|
211
|
+
it('full workflow with all MCP unavailable returns graceful defaults', async () => {
|
|
212
|
+
const manager = new McpManager(makeEmptyConfig());
|
|
213
|
+
const adapter = new GitHubMcpAdapter(manager);
|
|
214
|
+
const enricher = new McpContextEnricher(manager);
|
|
215
|
+
|
|
216
|
+
// Adapter: not available
|
|
217
|
+
expect(adapter.isAvailable()).toBe(false);
|
|
218
|
+
const repoResult = await adapter.createRepository({ name: 'test' });
|
|
219
|
+
expect(repoResult.available).toBe(false);
|
|
220
|
+
|
|
221
|
+
// Enricher: empty context
|
|
222
|
+
const contextResult = await enricher.enrich({
|
|
223
|
+
dependencies: ['express'],
|
|
224
|
+
architectureNotes: 'Use Azure',
|
|
225
|
+
stuckIterations: 3,
|
|
226
|
+
failingTests: ['test fails'],
|
|
227
|
+
});
|
|
228
|
+
expect(contextResult.combined).toBe('');
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T011: Integration test for MCP transport flow.
|
|
3
|
+
*
|
|
4
|
+
* Spawns a minimal JSON-RPC echo server as a child process, verifies that
|
|
5
|
+
* StdioMcpTransport can connect and round-trip a `tools/call` request,
|
|
6
|
+
* and verifies McpManager.callTool() dispatches through StdioMcpTransport
|
|
7
|
+
* for stdio config.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
10
|
+
import { writeFile, mkdtemp, rm } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import pino from 'pino';
|
|
14
|
+
|
|
15
|
+
import { StdioMcpTransport } from '../../src/mcp/mcpTransport.js';
|
|
16
|
+
import { McpManager } from '../../src/mcp/mcpManager.js';
|
|
17
|
+
import type { McpConfig, StdioServerConfig } from '../../src/mcp/mcpManager.js';
|
|
18
|
+
|
|
19
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const silentLogger = pino({ level: 'silent' });
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Minimal JSON-RPC echo server script.
|
|
25
|
+
* Handles `initialize` handshake and echoes `tools/call` params back.
|
|
26
|
+
*/
|
|
27
|
+
const ECHO_SERVER_SCRIPT = `
|
|
28
|
+
const readline = require('readline');
|
|
29
|
+
const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
30
|
+
|
|
31
|
+
rl.on('line', (line) => {
|
|
32
|
+
let msg;
|
|
33
|
+
try { msg = JSON.parse(line); } catch { return; }
|
|
34
|
+
|
|
35
|
+
if (msg.method === 'initialize') {
|
|
36
|
+
const response = {
|
|
37
|
+
jsonrpc: '2.0',
|
|
38
|
+
id: msg.id,
|
|
39
|
+
result: {
|
|
40
|
+
protocolVersion: '2024-11-05',
|
|
41
|
+
capabilities: { tools: {} },
|
|
42
|
+
serverInfo: { name: 'echo-server', version: '0.1.0' },
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
process.stdout.write(JSON.stringify(response) + '\\n');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (msg.method === 'tools/call') {
|
|
50
|
+
const result = {
|
|
51
|
+
jsonrpc: '2.0',
|
|
52
|
+
id: msg.id,
|
|
53
|
+
result: {
|
|
54
|
+
content: [{ type: 'text', text: JSON.stringify(msg.params) }],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
process.stdout.write(JSON.stringify(result) + '\\n');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
let tmpDir: string | undefined;
|
|
64
|
+
|
|
65
|
+
async function createEchoServer(): Promise<string> {
|
|
66
|
+
tmpDir = await mkdtemp(join(tmpdir(), 'mcp-echo-'));
|
|
67
|
+
const scriptPath = join(tmpDir, 'echo-server.js');
|
|
68
|
+
await writeFile(scriptPath, ECHO_SERVER_SCRIPT, 'utf-8');
|
|
69
|
+
return scriptPath;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function makeStdioConfig(name: string, scriptPath: string): StdioServerConfig {
|
|
73
|
+
return {
|
|
74
|
+
name,
|
|
75
|
+
type: 'stdio' as const,
|
|
76
|
+
command: 'node',
|
|
77
|
+
args: [scriptPath],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Tests ────────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
describe('MCP Transport Flow (integration)', () => {
|
|
84
|
+
let transport: StdioMcpTransport | undefined;
|
|
85
|
+
let manager: McpManager | undefined;
|
|
86
|
+
|
|
87
|
+
afterEach(async () => {
|
|
88
|
+
if (transport?.isConnected()) {
|
|
89
|
+
await transport.disconnect().catch(() => {});
|
|
90
|
+
}
|
|
91
|
+
transport = undefined;
|
|
92
|
+
|
|
93
|
+
if (manager) {
|
|
94
|
+
await manager.disconnectAll().catch(() => {});
|
|
95
|
+
}
|
|
96
|
+
manager = undefined;
|
|
97
|
+
|
|
98
|
+
if (tmpDir) {
|
|
99
|
+
await rm(tmpDir, { recursive: true, force: true }).catch(() => {});
|
|
100
|
+
tmpDir = undefined;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('StdioMcpTransport connects and round-trips a tools/call request', async () => {
|
|
105
|
+
const scriptPath = await createEchoServer();
|
|
106
|
+
|
|
107
|
+
transport = new StdioMcpTransport(makeStdioConfig('echo-test', scriptPath), silentLogger);
|
|
108
|
+
|
|
109
|
+
await transport.connect();
|
|
110
|
+
expect(transport.isConnected()).toBe(true);
|
|
111
|
+
|
|
112
|
+
const response = await transport.callTool('test-tool', { greeting: 'hello' }, 10_000);
|
|
113
|
+
|
|
114
|
+
expect(response.content).toBeDefined();
|
|
115
|
+
|
|
116
|
+
const content =
|
|
117
|
+
typeof response.content === 'string'
|
|
118
|
+
? (JSON.parse(response.content) as Record<string, unknown>)
|
|
119
|
+
: response.content;
|
|
120
|
+
|
|
121
|
+
expect(content).toHaveProperty('name', 'test-tool');
|
|
122
|
+
expect(content).toHaveProperty('arguments');
|
|
123
|
+
const args = content.arguments as Record<string, unknown>;
|
|
124
|
+
expect(args).toHaveProperty('greeting', 'hello');
|
|
125
|
+
|
|
126
|
+
await transport.disconnect();
|
|
127
|
+
expect(transport.isConnected()).toBe(false);
|
|
128
|
+
}, 15_000);
|
|
129
|
+
|
|
130
|
+
it('McpManager.callTool() dispatches through StdioMcpTransport for stdio config', async () => {
|
|
131
|
+
const scriptPath = await createEchoServer();
|
|
132
|
+
|
|
133
|
+
const config: McpConfig = {
|
|
134
|
+
servers: {
|
|
135
|
+
'echo-server': makeStdioConfig('echo-server', scriptPath),
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
manager = new McpManager(config, silentLogger);
|
|
139
|
+
manager.markConnected('echo-server');
|
|
140
|
+
|
|
141
|
+
const result = await manager.callTool(
|
|
142
|
+
'echo-server',
|
|
143
|
+
'ping-tool',
|
|
144
|
+
{ message: 'pong' },
|
|
145
|
+
{ timeoutMs: 10_000 },
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result).toBeDefined();
|
|
149
|
+
expect(result).toHaveProperty('name', 'ping-tool');
|
|
150
|
+
expect(result).toHaveProperty('arguments');
|
|
151
|
+
const args = result.arguments as Record<string, unknown>;
|
|
152
|
+
expect(args).toHaveProperty('message', 'pong');
|
|
153
|
+
}, 15_000);
|
|
154
|
+
|
|
155
|
+
it('echo server round-trips multiple sequential calls', async () => {
|
|
156
|
+
const scriptPath = await createEchoServer();
|
|
157
|
+
|
|
158
|
+
transport = new StdioMcpTransport(makeStdioConfig('echo-multi', scriptPath), silentLogger);
|
|
159
|
+
|
|
160
|
+
await transport.connect();
|
|
161
|
+
|
|
162
|
+
const r1 = await transport.callTool('tool-a', { n: 1 }, 10_000);
|
|
163
|
+
const c1 =
|
|
164
|
+
typeof r1.content === 'string'
|
|
165
|
+
? (JSON.parse(r1.content) as Record<string, unknown>)
|
|
166
|
+
: r1.content;
|
|
167
|
+
expect((c1.arguments as Record<string, unknown>).n).toBe(1);
|
|
168
|
+
|
|
169
|
+
const r2 = await transport.callTool('tool-b', { n: 2 }, 10_000);
|
|
170
|
+
const c2 =
|
|
171
|
+
typeof r2.content === 'string'
|
|
172
|
+
? (JSON.parse(r2.content) as Record<string, unknown>)
|
|
173
|
+
: r2.content;
|
|
174
|
+
expect((c2.arguments as Record<string, unknown>).n).toBe(2);
|
|
175
|
+
|
|
176
|
+
await transport.disconnect();
|
|
177
|
+
}, 15_000);
|
|
178
|
+
});
|