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,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct command mode (US3).
|
|
3
|
+
*
|
|
4
|
+
* Provides non-interactive and automation-friendly entry points:
|
|
5
|
+
* - `sofia workshop --session <id> --phase <phase>` jumps to a specific phase
|
|
6
|
+
* - Enforces required inputs in non-interactive mode
|
|
7
|
+
* - JSON-only stdout when --json
|
|
8
|
+
* - Retry transient failures when --retry specified
|
|
9
|
+
*/
|
|
10
|
+
import { ConversationLoop } from '../loop/conversationLoop.js';
|
|
11
|
+
import { createPhaseHandler, getPhaseOrder } from '../phases/phaseHandlers.js';
|
|
12
|
+
import { classifyError, toUserMessage } from '../shared/errorClassifier.js';
|
|
13
|
+
// ── Validation ───────────────────────────────────────────────────────────────
|
|
14
|
+
const VALID_PHASES = getPhaseOrder();
|
|
15
|
+
function validateInputs(opts) {
|
|
16
|
+
if (!opts.sessionId) {
|
|
17
|
+
const error = 'Required: --session <id>. Provide a session ID to target.';
|
|
18
|
+
emitError(opts, error);
|
|
19
|
+
return { exitCode: 1, error };
|
|
20
|
+
}
|
|
21
|
+
if (opts.nonInteractive && !opts.phase) {
|
|
22
|
+
const error = 'Required: --phase <phase>. In non-interactive mode, a phase must be specified.';
|
|
23
|
+
emitError(opts, error);
|
|
24
|
+
return { exitCode: 1, error };
|
|
25
|
+
}
|
|
26
|
+
if (opts.phase && !VALID_PHASES.includes(opts.phase)) {
|
|
27
|
+
const error = `Invalid phase "${opts.phase}". Valid phases: ${VALID_PHASES.join(', ')}`;
|
|
28
|
+
emitError(opts, error);
|
|
29
|
+
return { exitCode: 1, error };
|
|
30
|
+
}
|
|
31
|
+
return null; // validation passed
|
|
32
|
+
}
|
|
33
|
+
function emitError(opts, message) {
|
|
34
|
+
if (opts.json || opts.io.isJsonMode) {
|
|
35
|
+
// JSON mode: follow the standard `{ error: "..." }` shape used by other commands
|
|
36
|
+
opts.io.write(JSON.stringify({ error: message }) + '\n');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// Non-JSON mode: emit a human-readable error message so failures are visible
|
|
40
|
+
opts.io.write(`Error: ${message}\n`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ── Core execution ──────────────────────────────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Run a direct command: load session, execute phase, persist results.
|
|
46
|
+
*
|
|
47
|
+
* Supports retry for transient failures (connection, timeout, etc.).
|
|
48
|
+
*/
|
|
49
|
+
export async function runDirectCommand(opts) {
|
|
50
|
+
// 1. Validate inputs
|
|
51
|
+
const validationError = validateInputs(opts);
|
|
52
|
+
if (validationError)
|
|
53
|
+
return validationError;
|
|
54
|
+
// 2. Load session
|
|
55
|
+
let session;
|
|
56
|
+
try {
|
|
57
|
+
if (!(await opts.store.exists(opts.sessionId))) {
|
|
58
|
+
const error = `Session "${opts.sessionId}" not found.`;
|
|
59
|
+
emitError(opts, error);
|
|
60
|
+
return { exitCode: 1, error };
|
|
61
|
+
}
|
|
62
|
+
session = await opts.store.load(opts.sessionId);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const error = err instanceof Error ? err.message : 'Failed to load session';
|
|
66
|
+
emitError(opts, error);
|
|
67
|
+
return { exitCode: 1, error };
|
|
68
|
+
}
|
|
69
|
+
// 3. Set phase
|
|
70
|
+
if (opts.phase) {
|
|
71
|
+
session.phase = opts.phase;
|
|
72
|
+
session.updatedAt = new Date().toISOString();
|
|
73
|
+
await opts.store.save(session);
|
|
74
|
+
}
|
|
75
|
+
// 4. Run phase with retry
|
|
76
|
+
const maxAttempts = (opts.retry ?? 0) + 1;
|
|
77
|
+
let lastError;
|
|
78
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
79
|
+
try {
|
|
80
|
+
session = await runPhase(session, opts);
|
|
81
|
+
return { exitCode: 0 };
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
85
|
+
const classification = classifyError(lastError);
|
|
86
|
+
if (!classification.recoverable || attempt >= maxAttempts) {
|
|
87
|
+
const error = lastError.message;
|
|
88
|
+
emitError(opts, error);
|
|
89
|
+
return { exitCode: 1, error };
|
|
90
|
+
}
|
|
91
|
+
// Log retry attempt
|
|
92
|
+
opts.io.writeActivity(`Attempt ${attempt}/${maxAttempts} failed (${classification.category}): ${toUserMessage(classification)}. Retrying...`);
|
|
93
|
+
// Brief backoff before retry
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * attempt));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Should not reach here, but safety net
|
|
98
|
+
const error = lastError?.message ?? 'Unknown error';
|
|
99
|
+
return { exitCode: 1, error };
|
|
100
|
+
}
|
|
101
|
+
// ── Phase runner ────────────────────────────────────────────────────────────
|
|
102
|
+
async function runPhase(session, opts) {
|
|
103
|
+
const phase = session.phase;
|
|
104
|
+
const handler = createPhaseHandler(phase);
|
|
105
|
+
await handler._preload();
|
|
106
|
+
const events = [];
|
|
107
|
+
const loop = new ConversationLoop({
|
|
108
|
+
client: opts.client,
|
|
109
|
+
io: opts.io,
|
|
110
|
+
session,
|
|
111
|
+
phaseHandler: handler,
|
|
112
|
+
onEvent: (e) => {
|
|
113
|
+
events.push(e);
|
|
114
|
+
if (opts.debug && e.type === 'Activity') {
|
|
115
|
+
opts.io.writeActivity(e.message);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
onSessionUpdate: async (updatedSession) => {
|
|
119
|
+
session = updatedSession;
|
|
120
|
+
await opts.store.save(session);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
session = await loop.run();
|
|
124
|
+
session.updatedAt = new Date().toISOString();
|
|
125
|
+
await opts.store.save(session);
|
|
126
|
+
// In JSON mode, emit result summary
|
|
127
|
+
if (opts.json || opts.io.isJsonMode) {
|
|
128
|
+
opts.io.write(JSON.stringify({
|
|
129
|
+
sessionId: session.sessionId,
|
|
130
|
+
phase: session.phase,
|
|
131
|
+
status: session.status,
|
|
132
|
+
updatedAt: session.updatedAt,
|
|
133
|
+
}) + '\n');
|
|
134
|
+
}
|
|
135
|
+
// Decision gate
|
|
136
|
+
const gateResult = await opts.io.showDecisionGate(phase);
|
|
137
|
+
if (gateResult.choice === 'exit') {
|
|
138
|
+
session.status = 'Paused';
|
|
139
|
+
session.updatedAt = new Date().toISOString();
|
|
140
|
+
await opts.store.save(session);
|
|
141
|
+
}
|
|
142
|
+
return session;
|
|
143
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads a `.env` file into `process.env` without overwriting existing values.
|
|
3
|
+
*
|
|
4
|
+
* Called at CLI startup so that variables written by `infra/deploy.sh`
|
|
5
|
+
* (e.g. `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL_DEPLOYMENT_NAME`) are
|
|
6
|
+
* available to the application automatically.
|
|
7
|
+
*/
|
|
8
|
+
import { config } from 'dotenv';
|
|
9
|
+
/**
|
|
10
|
+
* Load environment variables from the given `.env` file path.
|
|
11
|
+
* Variables already present in `process.env` are **not** overwritten.
|
|
12
|
+
* If the file does not exist the call is a silent no-op.
|
|
13
|
+
*/
|
|
14
|
+
export function loadEnvFile(envPath) {
|
|
15
|
+
config({ path: envPath, override: false, quiet: true });
|
|
16
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export command handler.
|
|
3
|
+
*
|
|
4
|
+
* Implements `sofia export` — exports workshop artifacts for a session.
|
|
5
|
+
* Generates Markdown files per phase and a summary.json.
|
|
6
|
+
*/
|
|
7
|
+
import { createDefaultStore } from '../sessions/sessionStore.js';
|
|
8
|
+
import { exportSession } from '../sessions/exportWriter.js';
|
|
9
|
+
import { ensureExportDir } from '../sessions/exportPaths.js';
|
|
10
|
+
export async function exportCommand(opts) {
|
|
11
|
+
if (!opts.session) {
|
|
12
|
+
if (opts.json) {
|
|
13
|
+
process.stdout.write(JSON.stringify({ error: 'No session specified. Use --session <id>.' }) + '\n');
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
console.error('Error: No session specified. Use --session <id>.');
|
|
17
|
+
}
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const store = createDefaultStore();
|
|
22
|
+
if (!(await store.exists(opts.session))) {
|
|
23
|
+
const msg = `Session "${opts.session}" not found.`;
|
|
24
|
+
if (opts.json) {
|
|
25
|
+
process.stdout.write(JSON.stringify({ error: msg }) + '\n');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.error(`Error: ${msg}`);
|
|
29
|
+
}
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const session = await store.load(opts.session);
|
|
34
|
+
const exportDir = opts.output
|
|
35
|
+
? opts.output
|
|
36
|
+
: await ensureExportDir(opts.session);
|
|
37
|
+
const result = await exportSession(session, exportDir);
|
|
38
|
+
if (opts.json) {
|
|
39
|
+
process.stdout.write(JSON.stringify({
|
|
40
|
+
sessionId: session.sessionId,
|
|
41
|
+
exported: true,
|
|
42
|
+
exportDir: result.exportDir,
|
|
43
|
+
files: result.files,
|
|
44
|
+
}) + '\n');
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.log(`Exported session "${session.sessionId}" to ${result.exportDir}`);
|
|
48
|
+
console.log(`Generated ${result.files.length} file(s):`);
|
|
49
|
+
for (const file of result.files) {
|
|
50
|
+
console.log(` - ${file.path} (${file.type})`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sofIA CLI entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* Provides the main `sofia` command. Running `sofia` with no subcommand
|
|
6
|
+
* starts the workshop flow (default). Explicit subcommands:
|
|
7
|
+
* - workshop: alias for the default workshop flow
|
|
8
|
+
* - status: display session status
|
|
9
|
+
* - export: export session artifacts
|
|
10
|
+
*/
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { loadEnvFile } from './envLoader.js';
|
|
15
|
+
// ── Load .env from workspace root ──────────────────────────────────────────
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
loadEnvFile(path.resolve(__dirname, '../../.env'));
|
|
18
|
+
// ── Build CLI ──────────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Build the Commander program. Accepts optional handler overrides for testing.
|
|
21
|
+
* When handlers are not provided, they dynamically import the real modules.
|
|
22
|
+
*/
|
|
23
|
+
export function buildCli(handlers) {
|
|
24
|
+
const program = new Command();
|
|
25
|
+
program.name('sofia').description('sofIA — AI Discovery Workshop CLI').version('0.1.0');
|
|
26
|
+
// ── Global options ──────────────────────────────────────────────────────
|
|
27
|
+
program
|
|
28
|
+
.option('--session <id>', 'Target an existing session')
|
|
29
|
+
.option('--json', 'Emit machine-readable JSON only on stdout')
|
|
30
|
+
.option('--debug', 'Enable debug telemetry')
|
|
31
|
+
.option('--log-file <path>', 'Write structured logs to file')
|
|
32
|
+
.option('--non-interactive', 'Disallow prompts; fail with actionable error if input is missing');
|
|
33
|
+
// ── Workshop options promoted to top level (FR-004) ──────────────────────
|
|
34
|
+
program
|
|
35
|
+
.option('--new-session', 'Start a new session')
|
|
36
|
+
.option('--phase <phase>', 'Jump to a specific phase')
|
|
37
|
+
.option('--retry <count>', 'Retry transient failures N times', parseInt);
|
|
38
|
+
// ── Workshop handler logic ──────────────────────────────────────────────
|
|
39
|
+
async function invokeWorkshopHandler(programOpts, cmdOpts) {
|
|
40
|
+
const merged = { ...programOpts, ...cmdOpts };
|
|
41
|
+
if (handlers?.workshopHandler) {
|
|
42
|
+
await handlers.workshopHandler(merged);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Direct command mode: if --session and --phase are both specified
|
|
46
|
+
if (merged.session && merged.phase) {
|
|
47
|
+
const { runDirectCommand } = await import('./directCommands.js');
|
|
48
|
+
const { createLoopIO } = await import('./ioContext.js');
|
|
49
|
+
const { createDefaultStore } = await import('../sessions/sessionStore.js');
|
|
50
|
+
const { createCopilotClient } = await import('../shared/copilotClient.js');
|
|
51
|
+
const { getLogger } = await import('../logging/logger.js');
|
|
52
|
+
const store = createDefaultStore();
|
|
53
|
+
const io = createLoopIO({
|
|
54
|
+
json: merged.json,
|
|
55
|
+
nonInteractive: merged.nonInteractive,
|
|
56
|
+
});
|
|
57
|
+
let client;
|
|
58
|
+
try {
|
|
59
|
+
client = await createCopilotClient();
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const logger = getLogger();
|
|
63
|
+
logger.error({ err }, 'Failed to create Copilot client — cannot run direct command');
|
|
64
|
+
const msg = err instanceof Error ? err.message : 'Unknown error creating Copilot client';
|
|
65
|
+
if (merged.json) {
|
|
66
|
+
process.stdout.write(JSON.stringify({ error: msg }) + '\n');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.error(`Error: ${msg}`);
|
|
70
|
+
}
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const result = await runDirectCommand({
|
|
75
|
+
sessionId: merged.session,
|
|
76
|
+
phase: merged.phase,
|
|
77
|
+
store,
|
|
78
|
+
client,
|
|
79
|
+
io,
|
|
80
|
+
nonInteractive: merged.nonInteractive,
|
|
81
|
+
json: merged.json,
|
|
82
|
+
debug: merged.debug,
|
|
83
|
+
retry: merged.retry,
|
|
84
|
+
});
|
|
85
|
+
process.exitCode = result.exitCode;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { workshopCommand } = await import('./workshopCommand.js');
|
|
89
|
+
await workshopCommand(merged);
|
|
90
|
+
}
|
|
91
|
+
// ── Workshop command (default + alias) ─────────────────────────────────
|
|
92
|
+
program
|
|
93
|
+
.command('workshop', { isDefault: true })
|
|
94
|
+
.description('Start or resume a governed AI Discovery Workshop session')
|
|
95
|
+
.action(async () => {
|
|
96
|
+
const opts = program.opts();
|
|
97
|
+
await invokeWorkshopHandler(opts);
|
|
98
|
+
});
|
|
99
|
+
// ── Status command ────────────────────────────────────────────────────
|
|
100
|
+
program
|
|
101
|
+
.command('status')
|
|
102
|
+
.description('Display session status and next expected action')
|
|
103
|
+
.action(async () => {
|
|
104
|
+
if (handlers?.statusHandler) {
|
|
105
|
+
await handlers.statusHandler(program.opts());
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const { statusCommand } = await import('./statusCommand.js');
|
|
109
|
+
const globalOpts = program.opts();
|
|
110
|
+
await statusCommand(globalOpts);
|
|
111
|
+
});
|
|
112
|
+
// ── Export command ────────────────────────────────────────────────────
|
|
113
|
+
program
|
|
114
|
+
.command('export')
|
|
115
|
+
.description('Export workshop artifacts for a session')
|
|
116
|
+
.option('--output <dir>', 'Output directory (default: ./exports/<sessionId>/)')
|
|
117
|
+
.action(async (opts) => {
|
|
118
|
+
if (handlers?.exportHandler) {
|
|
119
|
+
await handlers.exportHandler({ ...program.opts(), ...opts });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const { exportCommand } = await import('./exportCommand.js');
|
|
123
|
+
const globalOpts = program.opts();
|
|
124
|
+
await exportCommand({ ...globalOpts, ...opts });
|
|
125
|
+
});
|
|
126
|
+
// ── Dev command ───────────────────────────────────────────────────────
|
|
127
|
+
program
|
|
128
|
+
.command('dev')
|
|
129
|
+
.description('Generate a proof-of-concept repository for a completed workshop session')
|
|
130
|
+
.option('--max-iterations <n>', 'Maximum Ralph loop iterations (default: 20)', parseInt)
|
|
131
|
+
.option('--output <dir>', 'Output directory for the PoC (default: ./poc/<sessionId>/)')
|
|
132
|
+
.option('--force', 'Overwrite existing output directory and start fresh')
|
|
133
|
+
.action(async (opts) => {
|
|
134
|
+
const { developCommand } = await import('./developCommand.js');
|
|
135
|
+
const { createDefaultStore } = await import('../sessions/sessionStore.js');
|
|
136
|
+
const { createLoopIO } = await import('./ioContext.js');
|
|
137
|
+
const { createCopilotClient } = await import('../shared/copilotClient.js');
|
|
138
|
+
const { getLogger } = await import('../logging/logger.js');
|
|
139
|
+
const { loadMcpConfig, McpManager } = await import('../mcp/mcpManager.js');
|
|
140
|
+
const { join: pathJoin } = await import('node:path');
|
|
141
|
+
const globalOpts = program.opts();
|
|
142
|
+
const merged = { ...globalOpts, ...opts };
|
|
143
|
+
const store = createDefaultStore();
|
|
144
|
+
const io = createLoopIO({
|
|
145
|
+
json: merged.json,
|
|
146
|
+
nonInteractive: merged.nonInteractive,
|
|
147
|
+
});
|
|
148
|
+
let client;
|
|
149
|
+
try {
|
|
150
|
+
client = await createCopilotClient();
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const logger = getLogger();
|
|
154
|
+
logger.error({ err }, 'Failed to create Copilot client');
|
|
155
|
+
const msg = err instanceof Error ? err.message : 'Unknown error creating Copilot client';
|
|
156
|
+
if (merged.json) {
|
|
157
|
+
process.stdout.write(JSON.stringify({ error: msg }) + '\n');
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
process.stderr.write(`Error: ${msg}\n`);
|
|
161
|
+
}
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// Load MCP config and wire adapters when MCP servers are configured
|
|
166
|
+
const mcpConfigPath = pathJoin(process.cwd(), '.vscode', 'mcp.json');
|
|
167
|
+
let mcpManager;
|
|
168
|
+
try {
|
|
169
|
+
const mcpConfig = await loadMcpConfig(mcpConfigPath);
|
|
170
|
+
if (Object.keys(mcpConfig.servers).length > 0) {
|
|
171
|
+
mcpManager = new McpManager(mcpConfig);
|
|
172
|
+
// Mark all configured servers as connected (optimistic: actual MCP failures
|
|
173
|
+
// degrade gracefully inside GitHubMcpAdapter / McpContextEnricher)
|
|
174
|
+
for (const name of mcpManager.listServers()) {
|
|
175
|
+
mcpManager.markConnected(name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// MCP config is optional — proceed without it
|
|
181
|
+
}
|
|
182
|
+
await developCommand(merged, {
|
|
183
|
+
store,
|
|
184
|
+
io,
|
|
185
|
+
client,
|
|
186
|
+
mcpManager,
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
return program;
|
|
190
|
+
}
|
|
191
|
+
// ── Auto-parse when run as CLI entrypoint ─────────────────────────────────
|
|
192
|
+
// Only auto-parse when running as the main CLI entrypoint, not when imported for testing
|
|
193
|
+
const isMainModule = process.argv[1] &&
|
|
194
|
+
(process.argv[1].endsWith('/cli/index.js') ||
|
|
195
|
+
process.argv[1].endsWith('/cli/index.ts') ||
|
|
196
|
+
process.argv[1].endsWith('sofia'));
|
|
197
|
+
if (isMainModule) {
|
|
198
|
+
const program = buildCli();
|
|
199
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
200
|
+
console.error(err instanceof Error ? err.message : 'Unknown error');
|
|
201
|
+
process.exit(1);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IO Context.
|
|
3
|
+
*
|
|
4
|
+
* Detects TTY vs non-TTY, JSON mode, and provides the LoopIO
|
|
5
|
+
* implementation for the ConversationLoop based on CLI options.
|
|
6
|
+
*/
|
|
7
|
+
import * as readline from 'node:readline';
|
|
8
|
+
import { renderMarkdown } from '../shared/markdownRenderer.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create a LoopIO implementation based on CLI context.
|
|
11
|
+
*/
|
|
12
|
+
export function createLoopIO(options = {}) {
|
|
13
|
+
const input = options.input ?? process.stdin;
|
|
14
|
+
const output = options.output ?? process.stdout;
|
|
15
|
+
const errorOutput = options.errorOutput ?? process.stderr;
|
|
16
|
+
const isTTY = !options.nonInteractive && Boolean(input.isTTY);
|
|
17
|
+
const isJsonMode = options.json ?? false;
|
|
18
|
+
const isDebug = options.debug ?? false;
|
|
19
|
+
let rl = null;
|
|
20
|
+
let rlClosed = false;
|
|
21
|
+
function getReadline() {
|
|
22
|
+
if (!rl || rlClosed) {
|
|
23
|
+
rlClosed = false;
|
|
24
|
+
rl = readline.createInterface({
|
|
25
|
+
input: input,
|
|
26
|
+
output: isTTY ? output : undefined,
|
|
27
|
+
terminal: isTTY,
|
|
28
|
+
});
|
|
29
|
+
rl.on('close', () => { rlClosed = true; });
|
|
30
|
+
}
|
|
31
|
+
return rl;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
write(text) {
|
|
35
|
+
output.write(text);
|
|
36
|
+
},
|
|
37
|
+
writeActivity(text) {
|
|
38
|
+
if (!isJsonMode) {
|
|
39
|
+
errorOutput.write(`[activity] ${text}\n`);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
writeToolSummary(toolName, summary, details) {
|
|
43
|
+
if (isJsonMode || options.nonInteractive)
|
|
44
|
+
return;
|
|
45
|
+
errorOutput.write(`✓ ${toolName}: ${summary}\n`);
|
|
46
|
+
if (isDebug && details) {
|
|
47
|
+
if (details.args) {
|
|
48
|
+
errorOutput.write(` args: ${JSON.stringify(details.args, null, 2)}\n`);
|
|
49
|
+
}
|
|
50
|
+
if (details.result !== undefined) {
|
|
51
|
+
errorOutput.write(` result: ${JSON.stringify(details.result, null, 2)}\n`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
async readInput(prompt) {
|
|
56
|
+
if (options.nonInteractive) {
|
|
57
|
+
return null; // Non-interactive mode: no input
|
|
58
|
+
}
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
const r = getReadline();
|
|
61
|
+
r.question(prompt ?? '> ', (answer) => {
|
|
62
|
+
resolve(answer);
|
|
63
|
+
});
|
|
64
|
+
r.once('close', () => resolve(null));
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
async showDecisionGate(phase) {
|
|
68
|
+
if (isJsonMode || options.nonInteractive) {
|
|
69
|
+
return { choice: 'continue' };
|
|
70
|
+
}
|
|
71
|
+
const phaseOrder = [
|
|
72
|
+
'Discover', 'Ideate', 'Design', 'Select', 'Plan', 'Develop', 'Complete',
|
|
73
|
+
];
|
|
74
|
+
const currentIdx = phaseOrder.indexOf(phase);
|
|
75
|
+
const nextPhase = currentIdx < phaseOrder.length - 1 ? phaseOrder[currentIdx + 1] : null;
|
|
76
|
+
const rendered = renderMarkdown(`\n---\n\n**Phase "${phase}" complete.**\n\n` +
|
|
77
|
+
`Options:\n` +
|
|
78
|
+
` 1. Continue to ${nextPhase ?? 'Complete'}\n` +
|
|
79
|
+
` 2. Refine current phase\n` +
|
|
80
|
+
` 3. Return to main menu\n` +
|
|
81
|
+
` 4. Exit\n`, { isTTY });
|
|
82
|
+
output.write(rendered);
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const r = getReadline();
|
|
85
|
+
r.question('Choose [1-4]: ', (answer) => {
|
|
86
|
+
const choiceMap = {
|
|
87
|
+
'1': 'continue',
|
|
88
|
+
'2': 'refine',
|
|
89
|
+
'3': 'menu',
|
|
90
|
+
'4': 'exit',
|
|
91
|
+
};
|
|
92
|
+
const choice = choiceMap[answer.trim()] ?? 'continue';
|
|
93
|
+
resolve({
|
|
94
|
+
choice,
|
|
95
|
+
targetPhase: choice === 'continue' ? (nextPhase ?? undefined) : undefined,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
isJsonMode,
|
|
101
|
+
isTTY,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Close any open readline interfaces.
|
|
106
|
+
*/
|
|
107
|
+
export function closeIO(_io) {
|
|
108
|
+
// The IO might hold a readline interface — no-op if not applicable
|
|
109
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight checks — validates environment readiness before starting a workshop.
|
|
3
|
+
*
|
|
4
|
+
* Each check returns a PreflightCheck result. runPreflightChecks() collects all
|
|
5
|
+
* results, never short-circuits, and returns an aggregate PreflightResult.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Run all preflight checks in parallel and aggregate results.
|
|
9
|
+
*
|
|
10
|
+
* - Checks that throw are caught and reported as fail with `required: true`.
|
|
11
|
+
* - Overall pass requires that no check with `required !== false` has status 'fail'.
|
|
12
|
+
*/
|
|
13
|
+
export async function runPreflightChecks(checks) {
|
|
14
|
+
const entries = Object.entries(checks);
|
|
15
|
+
const results = await Promise.all(entries.map(async ([key, fn]) => {
|
|
16
|
+
try {
|
|
17
|
+
return await fn();
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
21
|
+
return {
|
|
22
|
+
name: key.replace(/^check/, '').replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, ''),
|
|
23
|
+
status: 'fail',
|
|
24
|
+
message,
|
|
25
|
+
required: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
const passed = results.every((c) => c.status !== 'fail' || c.required === false);
|
|
30
|
+
return { passed, checks: results };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check for legacy web search env vars (FR-016).
|
|
34
|
+
*
|
|
35
|
+
* If `SOFIA_FOUNDRY_AGENT_ENDPOINT` or `SOFIA_FOUNDRY_AGENT_KEY` are set,
|
|
36
|
+
* returns a fail result with migration instructions.
|
|
37
|
+
*/
|
|
38
|
+
export async function checkLegacyWebSearchEnvVars() {
|
|
39
|
+
const legacyEndpoint = process.env.SOFIA_FOUNDRY_AGENT_ENDPOINT;
|
|
40
|
+
const legacyKey = process.env.SOFIA_FOUNDRY_AGENT_KEY;
|
|
41
|
+
if (legacyEndpoint || legacyKey) {
|
|
42
|
+
return {
|
|
43
|
+
name: 'legacy-web-search-env',
|
|
44
|
+
status: 'fail',
|
|
45
|
+
message: 'Legacy web search env vars detected. ' +
|
|
46
|
+
'Migrate: replace SOFIA_FOUNDRY_AGENT_ENDPOINT with FOUNDRY_PROJECT_ENDPOINT ' +
|
|
47
|
+
'and remove SOFIA_FOUNDRY_AGENT_KEY (API key auth is no longer used). ' +
|
|
48
|
+
'See docs/environment.md',
|
|
49
|
+
required: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
name: 'legacy-web-search-env',
|
|
54
|
+
status: 'pass',
|
|
55
|
+
message: 'No legacy web search env vars detected',
|
|
56
|
+
};
|
|
57
|
+
}
|