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,364 @@
|
|
|
1
|
+
// ── Template Definitions ─────────────────────────────────────────────────────
|
|
2
|
+
export const NODE_TS_VITEST_TEMPLATE = {
|
|
3
|
+
id: 'node-ts-vitest',
|
|
4
|
+
displayName: 'TypeScript + Node.js + Vitest',
|
|
5
|
+
techStack: {
|
|
6
|
+
language: 'TypeScript',
|
|
7
|
+
runtime: 'Node.js 20',
|
|
8
|
+
testRunner: 'npm test',
|
|
9
|
+
buildCommand: 'npm run build',
|
|
10
|
+
},
|
|
11
|
+
installCommand: 'npm install',
|
|
12
|
+
testCommand: 'npm test -- --reporter=json',
|
|
13
|
+
matchPatterns: ['typescript', 'node', 'vitest', 'ts'],
|
|
14
|
+
files: [
|
|
15
|
+
{
|
|
16
|
+
path: '.gitignore',
|
|
17
|
+
skipIfExists: false,
|
|
18
|
+
content: `node_modules/
|
|
19
|
+
dist/
|
|
20
|
+
coverage/
|
|
21
|
+
*.tsbuildinfo
|
|
22
|
+
.env
|
|
23
|
+
`,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
path: 'package.json',
|
|
27
|
+
skipIfExists: true,
|
|
28
|
+
content: (ctx) => JSON.stringify({
|
|
29
|
+
name: ctx.projectName,
|
|
30
|
+
version: '0.1.0',
|
|
31
|
+
private: true,
|
|
32
|
+
type: 'module',
|
|
33
|
+
scripts: {
|
|
34
|
+
build: 'tsc',
|
|
35
|
+
start: 'node dist/index.js',
|
|
36
|
+
test: 'vitest run',
|
|
37
|
+
},
|
|
38
|
+
dependencies: {},
|
|
39
|
+
devDependencies: {
|
|
40
|
+
typescript: '^5.0.0',
|
|
41
|
+
vitest: '^3.0.0',
|
|
42
|
+
'@types/node': '^20.0.0',
|
|
43
|
+
},
|
|
44
|
+
}, null, 2) + '\n',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
path: 'tsconfig.json',
|
|
48
|
+
skipIfExists: true,
|
|
49
|
+
content: JSON.stringify({
|
|
50
|
+
compilerOptions: {
|
|
51
|
+
target: 'ES2022',
|
|
52
|
+
module: 'Node16',
|
|
53
|
+
moduleResolution: 'Node16',
|
|
54
|
+
strict: true,
|
|
55
|
+
outDir: 'dist',
|
|
56
|
+
rootDir: 'src',
|
|
57
|
+
declaration: true,
|
|
58
|
+
esModuleInterop: true,
|
|
59
|
+
skipLibCheck: true,
|
|
60
|
+
},
|
|
61
|
+
include: ['src'],
|
|
62
|
+
}, null, 2) + '\n',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
path: 'README.md',
|
|
66
|
+
skipIfExists: true,
|
|
67
|
+
content: (ctx) => `# ${ctx.ideaTitle}
|
|
68
|
+
|
|
69
|
+
${ctx.ideaDescription}
|
|
70
|
+
|
|
71
|
+
## Generated by
|
|
72
|
+
|
|
73
|
+
sofIA — AI Discovery Workshop CLI
|
|
74
|
+
Session: \`${ctx.sessionId}\`
|
|
75
|
+
Generated: ${new Date().toISOString()}
|
|
76
|
+
|
|
77
|
+
## Prerequisites
|
|
78
|
+
|
|
79
|
+
- Node.js 20+
|
|
80
|
+
- npm 9+
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
\`\`\`bash
|
|
85
|
+
npm install
|
|
86
|
+
npm test
|
|
87
|
+
npm start
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
## How It Works
|
|
91
|
+
|
|
92
|
+
${ctx.planSummary}
|
|
93
|
+
|
|
94
|
+
## Technology Stack
|
|
95
|
+
|
|
96
|
+
- **Language**: ${ctx.techStack.language}
|
|
97
|
+
- **Runtime**: ${ctx.techStack.runtime}
|
|
98
|
+
- **Test Runner**: ${ctx.techStack.testRunner}
|
|
99
|
+
${ctx.techStack.framework ? `- **Framework**: ${ctx.techStack.framework}\n` : ''}
|
|
100
|
+
`,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
path: 'src/index.ts',
|
|
104
|
+
skipIfExists: true,
|
|
105
|
+
content: (ctx) => `/**
|
|
106
|
+
* ${ctx.ideaTitle}
|
|
107
|
+
*
|
|
108
|
+
* ${ctx.ideaDescription}
|
|
109
|
+
*
|
|
110
|
+
* Generated by sofIA (session: ${ctx.sessionId})
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Main entry point for the ${ctx.projectName} PoC.
|
|
115
|
+
*
|
|
116
|
+
* TODO: Implement the core functionality described in the plan.
|
|
117
|
+
* The test file (tests/index.test.ts) describes the expected behavior.
|
|
118
|
+
*/
|
|
119
|
+
export function main(): string {
|
|
120
|
+
return 'Hello from ${ctx.projectName}!';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Run if called directly
|
|
124
|
+
const isMain = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('index.ts');
|
|
125
|
+
if (isMain) {
|
|
126
|
+
console.log(main());
|
|
127
|
+
}
|
|
128
|
+
`,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
path: 'tests/index.test.ts',
|
|
132
|
+
skipIfExists: true,
|
|
133
|
+
content: (ctx) => `/**
|
|
134
|
+
* Tests for ${ctx.ideaTitle} PoC.
|
|
135
|
+
*
|
|
136
|
+
* These tests describe the expected behavior of the implementation.
|
|
137
|
+
* They are intentionally failing initially — the Ralph loop will
|
|
138
|
+
* iteratively fix the implementation until they pass.
|
|
139
|
+
*
|
|
140
|
+
* Generated by sofIA (session: ${ctx.sessionId})
|
|
141
|
+
*/
|
|
142
|
+
import { describe, it, expect } from 'vitest';
|
|
143
|
+
import { main } from '../src/index.js';
|
|
144
|
+
|
|
145
|
+
describe('${ctx.projectName}', () => {
|
|
146
|
+
it('should initialize successfully', () => {
|
|
147
|
+
const result = main();
|
|
148
|
+
expect(result).toBeDefined();
|
|
149
|
+
expect(typeof result).toBe('string');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should implement core functionality', () => {
|
|
153
|
+
// TODO: sofIA Ralph loop will refine this test to match the actual implementation
|
|
154
|
+
// Plan: ${ctx.planSummary.substring(0, 200)}
|
|
155
|
+
const result = main();
|
|
156
|
+
expect(result).toContain('${ctx.projectName}');
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
`,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
path: '.sofia-metadata.json',
|
|
163
|
+
skipIfExists: false,
|
|
164
|
+
content: (ctx) => JSON.stringify({
|
|
165
|
+
sessionId: ctx.sessionId,
|
|
166
|
+
featureSpec: '002-poc-generation',
|
|
167
|
+
generatedAt: new Date().toISOString(),
|
|
168
|
+
ideaTitle: ctx.ideaTitle,
|
|
169
|
+
totalIterations: 0,
|
|
170
|
+
finalStatus: null,
|
|
171
|
+
terminationReason: null,
|
|
172
|
+
techStack: {
|
|
173
|
+
language: ctx.techStack.language.toLowerCase(),
|
|
174
|
+
runtime: ctx.techStack.runtime.toLowerCase(),
|
|
175
|
+
testRunner: ctx.techStack.testRunner,
|
|
176
|
+
},
|
|
177
|
+
}, null, 2) + '\n',
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
export const PYTHON_PYTEST_TEMPLATE = {
|
|
182
|
+
id: 'python-pytest',
|
|
183
|
+
displayName: 'Python + pytest',
|
|
184
|
+
techStack: {
|
|
185
|
+
language: 'Python',
|
|
186
|
+
runtime: 'Python 3.11',
|
|
187
|
+
testRunner: 'pytest',
|
|
188
|
+
},
|
|
189
|
+
installCommand: 'pip install -r requirements.txt',
|
|
190
|
+
testCommand: 'pytest --tb=short -q --json-report --json-report-file=-',
|
|
191
|
+
matchPatterns: ['python', 'fastapi', 'flask', 'django', 'pytest'],
|
|
192
|
+
files: [
|
|
193
|
+
{
|
|
194
|
+
path: '.gitignore',
|
|
195
|
+
skipIfExists: false,
|
|
196
|
+
content: `__pycache__/
|
|
197
|
+
*.pyc
|
|
198
|
+
.venv/
|
|
199
|
+
venv/
|
|
200
|
+
dist/
|
|
201
|
+
*.egg-info/
|
|
202
|
+
.env
|
|
203
|
+
.pytest_cache/
|
|
204
|
+
`,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
path: 'requirements.txt',
|
|
208
|
+
skipIfExists: true,
|
|
209
|
+
content: (ctx) => `# ${ctx.projectName} dependencies
|
|
210
|
+
pytest>=7.0.0
|
|
211
|
+
pytest-json-report>=1.5.0
|
|
212
|
+
`,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
path: 'pytest.ini',
|
|
216
|
+
skipIfExists: true,
|
|
217
|
+
content: `[pytest]
|
|
218
|
+
testpaths = tests
|
|
219
|
+
python_files = test_*.py
|
|
220
|
+
python_classes = Test*
|
|
221
|
+
python_functions = test_*
|
|
222
|
+
`,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
path: 'README.md',
|
|
226
|
+
skipIfExists: true,
|
|
227
|
+
content: (ctx) => `# ${ctx.ideaTitle}
|
|
228
|
+
|
|
229
|
+
${ctx.ideaDescription}
|
|
230
|
+
|
|
231
|
+
## Generated by
|
|
232
|
+
|
|
233
|
+
sofIA — AI Discovery Workshop CLI
|
|
234
|
+
Session: \`${ctx.sessionId}\`
|
|
235
|
+
Generated: ${new Date().toISOString()}
|
|
236
|
+
|
|
237
|
+
## Prerequisites
|
|
238
|
+
|
|
239
|
+
- Python 3.11+
|
|
240
|
+
- pip
|
|
241
|
+
|
|
242
|
+
## Quick Start
|
|
243
|
+
|
|
244
|
+
\`\`\`bash
|
|
245
|
+
pip install -r requirements.txt
|
|
246
|
+
pytest
|
|
247
|
+
python src/main.py
|
|
248
|
+
\`\`\`
|
|
249
|
+
|
|
250
|
+
## How It Works
|
|
251
|
+
|
|
252
|
+
${ctx.planSummary}
|
|
253
|
+
|
|
254
|
+
## Technology Stack
|
|
255
|
+
|
|
256
|
+
- **Language**: ${ctx.techStack.language}
|
|
257
|
+
- **Runtime**: ${ctx.techStack.runtime}
|
|
258
|
+
- **Test Runner**: ${ctx.techStack.testRunner}
|
|
259
|
+
${ctx.techStack.framework ? `- **Framework**: ${ctx.techStack.framework}\n` : ''}
|
|
260
|
+
`,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
path: 'src/__init__.py',
|
|
264
|
+
skipIfExists: true,
|
|
265
|
+
content: '',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
path: 'src/main.py',
|
|
269
|
+
skipIfExists: true,
|
|
270
|
+
content: (ctx) => `"""
|
|
271
|
+
${ctx.ideaTitle}
|
|
272
|
+
|
|
273
|
+
${ctx.ideaDescription}
|
|
274
|
+
|
|
275
|
+
Generated by sofIA (session: ${ctx.sessionId})
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def main() -> str:
|
|
280
|
+
"""Main entry point for the ${ctx.projectName} PoC.
|
|
281
|
+
|
|
282
|
+
TODO: Implement the core functionality described in the plan.
|
|
283
|
+
The test file (tests/test_main.py) describes the expected behavior.
|
|
284
|
+
"""
|
|
285
|
+
return "Hello from ${ctx.projectName}!"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if __name__ == "__main__":
|
|
289
|
+
print(main())
|
|
290
|
+
`,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
path: 'tests/test_main.py',
|
|
294
|
+
skipIfExists: true,
|
|
295
|
+
content: (ctx) => `"""
|
|
296
|
+
Tests for ${ctx.ideaTitle} PoC.
|
|
297
|
+
|
|
298
|
+
These tests describe the expected behavior of the implementation.
|
|
299
|
+
They are intentionally failing initially — the Ralph loop will
|
|
300
|
+
iteratively fix the implementation until they pass.
|
|
301
|
+
|
|
302
|
+
Generated by sofIA (session: ${ctx.sessionId})
|
|
303
|
+
"""
|
|
304
|
+
from src.main import main
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_main_returns_string():
|
|
308
|
+
result = main()
|
|
309
|
+
assert isinstance(result, str)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_main_contains_project_name():
|
|
313
|
+
result = main()
|
|
314
|
+
assert "${ctx.projectName}" in result
|
|
315
|
+
`,
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
path: '.sofia-metadata.json',
|
|
319
|
+
skipIfExists: false,
|
|
320
|
+
content: (ctx) => JSON.stringify({
|
|
321
|
+
sessionId: ctx.sessionId,
|
|
322
|
+
featureSpec: '002-poc-generation',
|
|
323
|
+
generatedAt: new Date().toISOString(),
|
|
324
|
+
ideaTitle: ctx.ideaTitle,
|
|
325
|
+
totalIterations: 0,
|
|
326
|
+
finalStatus: null,
|
|
327
|
+
terminationReason: null,
|
|
328
|
+
techStack: {
|
|
329
|
+
language: ctx.techStack.language.toLowerCase(),
|
|
330
|
+
runtime: ctx.techStack.runtime.toLowerCase(),
|
|
331
|
+
testRunner: ctx.techStack.testRunner,
|
|
332
|
+
},
|
|
333
|
+
}, null, 2) + '\n',
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
};
|
|
337
|
+
// ── Default Registry ─────────────────────────────────────────────────────────
|
|
338
|
+
/**
|
|
339
|
+
* Create the default template registry with built-in templates.
|
|
340
|
+
*/
|
|
341
|
+
export function createDefaultRegistry() {
|
|
342
|
+
const registry = new Map();
|
|
343
|
+
registry.set(NODE_TS_VITEST_TEMPLATE.id, NODE_TS_VITEST_TEMPLATE);
|
|
344
|
+
registry.set(PYTHON_PYTEST_TEMPLATE.id, PYTHON_PYTEST_TEMPLATE);
|
|
345
|
+
return registry;
|
|
346
|
+
}
|
|
347
|
+
// ── Selection ────────────────────────────────────────────────────────────────
|
|
348
|
+
/**
|
|
349
|
+
* Select a template from the registry based on plan architecture notes and dependencies.
|
|
350
|
+
*
|
|
351
|
+
* Uses first-match-wins logic: concatenates architectureNotes + dependencies into a
|
|
352
|
+
* search string, then checks each template's matchPatterns (case-insensitive).
|
|
353
|
+
* Falls back to `node-ts-vitest` if no match is found.
|
|
354
|
+
*/
|
|
355
|
+
export function selectTemplate(registry, architectureNotes, dependencies) {
|
|
356
|
+
const searchText = [architectureNotes ?? '', ...(dependencies ?? [])].join(' ').toLowerCase();
|
|
357
|
+
for (const entry of registry.values()) {
|
|
358
|
+
if (entry.matchPatterns.some((p) => searchText.includes(p.toLowerCase()))) {
|
|
359
|
+
return entry;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// Default fallback
|
|
363
|
+
return registry.get('node-ts-vitest');
|
|
364
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Runner.
|
|
3
|
+
*
|
|
4
|
+
* Spawns the PoC's test suite via `npm test -- --reporter=json` in the
|
|
5
|
+
* output directory, parses Vitest JSON reporter output into a structured
|
|
6
|
+
* TestResults object, and enforces a 60-second timeout.
|
|
7
|
+
*
|
|
8
|
+
* Contract: specs/002-poc-generation/contracts/ralph-loop.md
|
|
9
|
+
*/
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
13
|
+
const MAX_RAW_OUTPUT_CHARS = 2_000;
|
|
14
|
+
export class TestRunner {
|
|
15
|
+
timeoutMs;
|
|
16
|
+
testCommand;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
19
|
+
this.testCommand = options.testCommand ?? 'npm test -- --reporter=json';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Run the test suite in the given output directory.
|
|
23
|
+
*
|
|
24
|
+
* Spawns `npm test -- --reporter=json` and parses the JSON output.
|
|
25
|
+
* On timeout, returns an error result.
|
|
26
|
+
*/
|
|
27
|
+
async run(outputDir) {
|
|
28
|
+
const startTime = Date.now();
|
|
29
|
+
let rawOutput = '';
|
|
30
|
+
let timedOut = false;
|
|
31
|
+
try {
|
|
32
|
+
rawOutput = await this.spawnTests(outputDir, (timed) => {
|
|
33
|
+
timedOut = timed;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
38
|
+
return this.buildErrorResult(startTime, `Test runner error: ${msg}`);
|
|
39
|
+
}
|
|
40
|
+
if (timedOut) {
|
|
41
|
+
return this.buildErrorResult(startTime, `Test runner timed out after ${this.timeoutMs}ms`);
|
|
42
|
+
}
|
|
43
|
+
// Truncate rawOutput to max chars from the end
|
|
44
|
+
const truncatedOutput = rawOutput.length > MAX_RAW_OUTPUT_CHARS
|
|
45
|
+
? rawOutput.slice(-MAX_RAW_OUTPUT_CHARS)
|
|
46
|
+
: rawOutput;
|
|
47
|
+
return this.parseOutput(truncatedOutput, startTime, rawOutput);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Spawn test process and collect output.
|
|
51
|
+
*/
|
|
52
|
+
spawnTests(outputDir, onTimeout) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const stdoutChunks = [];
|
|
55
|
+
const stderrChunks = [];
|
|
56
|
+
const parts = this.testCommand.split(/\s+/);
|
|
57
|
+
const cmd = parts[0];
|
|
58
|
+
const args = parts.slice(1);
|
|
59
|
+
const child = spawn(cmd, args, {
|
|
60
|
+
cwd: outputDir,
|
|
61
|
+
shell: false,
|
|
62
|
+
env: {
|
|
63
|
+
...process.env,
|
|
64
|
+
// Disable color output for reliable JSON parsing
|
|
65
|
+
NO_COLOR: '1',
|
|
66
|
+
FORCE_COLOR: '0',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
child.stdout.on('data', (chunk) => {
|
|
70
|
+
stdoutChunks.push(chunk);
|
|
71
|
+
});
|
|
72
|
+
child.stderr.on('data', (chunk) => {
|
|
73
|
+
stderrChunks.push(chunk);
|
|
74
|
+
});
|
|
75
|
+
const timer = setTimeout(() => {
|
|
76
|
+
onTimeout(true);
|
|
77
|
+
child.kill('SIGTERM');
|
|
78
|
+
// Force kill after 5 more seconds
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
if (!child.killed)
|
|
81
|
+
child.kill('SIGKILL');
|
|
82
|
+
}, 5_000);
|
|
83
|
+
}, this.timeoutMs);
|
|
84
|
+
child.on('close', () => {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
// Combine stdout and stderr for parsing (Vitest may output to stderr)
|
|
87
|
+
const combined = [
|
|
88
|
+
Buffer.concat(stdoutChunks).toString('utf-8'),
|
|
89
|
+
Buffer.concat(stderrChunks).toString('utf-8'),
|
|
90
|
+
]
|
|
91
|
+
.filter(Boolean)
|
|
92
|
+
.join('\n');
|
|
93
|
+
resolve(combined);
|
|
94
|
+
});
|
|
95
|
+
child.on('error', (err) => {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
reject(err);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Parse Vitest JSON reporter output into TestResults.
|
|
103
|
+
* Exposed as protected for unit testing purposes.
|
|
104
|
+
*/
|
|
105
|
+
parseOutput(output, startTime, rawOutput) {
|
|
106
|
+
const durationMs = Date.now() - startTime;
|
|
107
|
+
// Try to extract JSON from the output (Vitest may mix JSON with other output)
|
|
108
|
+
const json = this.extractJson(output);
|
|
109
|
+
if (!json) {
|
|
110
|
+
// Could not parse JSON — return raw output as error info
|
|
111
|
+
return {
|
|
112
|
+
passed: 0,
|
|
113
|
+
failed: 0,
|
|
114
|
+
skipped: 0,
|
|
115
|
+
total: 0,
|
|
116
|
+
durationMs,
|
|
117
|
+
failures: [],
|
|
118
|
+
rawOutput: rawOutput.slice(-MAX_RAW_OUTPUT_CHARS),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const passed = json.numPassedTests ?? 0;
|
|
122
|
+
const failed = json.numFailedTests ?? 0;
|
|
123
|
+
const skipped = json.numPendingTests ?? 0;
|
|
124
|
+
const total = json.numTotalTests ?? passed + failed + skipped;
|
|
125
|
+
const failures = this.extractFailures(json);
|
|
126
|
+
return {
|
|
127
|
+
passed,
|
|
128
|
+
failed,
|
|
129
|
+
skipped,
|
|
130
|
+
total,
|
|
131
|
+
durationMs,
|
|
132
|
+
failures: failures.slice(0, 10), // max 10 failures
|
|
133
|
+
rawOutput: rawOutput.slice(-MAX_RAW_OUTPUT_CHARS),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Extract JSON object from potentially mixed output.
|
|
138
|
+
* Protected for subclass testing.
|
|
139
|
+
*/
|
|
140
|
+
extractJson(output) {
|
|
141
|
+
// Try to find a JSON object/array in the output
|
|
142
|
+
const lines = output.split('\n');
|
|
143
|
+
// Try each line (Vitest may output JSON on a single line)
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
const trimmed = line.trim();
|
|
146
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(trimmed);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Not valid JSON on this line
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Try the entire output
|
|
156
|
+
const start = output.indexOf('{');
|
|
157
|
+
const end = output.lastIndexOf('}');
|
|
158
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(output.slice(start, end + 1));
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Not valid JSON
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Extract TestFailure objects from Vitest JSON output.
|
|
170
|
+
*/
|
|
171
|
+
extractFailures(json) {
|
|
172
|
+
const failures = [];
|
|
173
|
+
for (const testResult of json.testResults ?? []) {
|
|
174
|
+
for (const assertion of testResult.assertionResults ?? []) {
|
|
175
|
+
if (assertion.status === 'failed') {
|
|
176
|
+
const message = assertion.failureMessages?.join('\n') ?? 'Test failed';
|
|
177
|
+
failures.push({
|
|
178
|
+
testName: assertion.fullName || assertion.title,
|
|
179
|
+
message: message.substring(0, 500), // Truncate individual messages
|
|
180
|
+
file: testResult.testFilePath,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return failures;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Build an error result (timeout or spawn failure).
|
|
189
|
+
* Protected for subclass testing.
|
|
190
|
+
*/
|
|
191
|
+
buildErrorResult(startTime, errorMessage) {
|
|
192
|
+
return {
|
|
193
|
+
passed: 0,
|
|
194
|
+
failed: 0,
|
|
195
|
+
skipped: 0,
|
|
196
|
+
total: 0,
|
|
197
|
+
durationMs: Date.now() - startTime,
|
|
198
|
+
failures: [],
|
|
199
|
+
rawOutput: errorMessage,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger setup with pino.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - File logging when --log-file is provided
|
|
6
|
+
* - Stderr logging by default
|
|
7
|
+
* - Automatic redaction of secrets/PII (password, token, secret, key, auth)
|
|
8
|
+
*/
|
|
9
|
+
import pino from 'pino';
|
|
10
|
+
import { mkdirSync } from 'node:fs';
|
|
11
|
+
import { dirname } from 'node:path';
|
|
12
|
+
const REDACT_PATHS = [
|
|
13
|
+
'password',
|
|
14
|
+
'token',
|
|
15
|
+
'secret',
|
|
16
|
+
'apiKey',
|
|
17
|
+
'api_key',
|
|
18
|
+
'authorization',
|
|
19
|
+
'auth',
|
|
20
|
+
'credential',
|
|
21
|
+
'credentials',
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Create a configured pino logger.
|
|
25
|
+
*
|
|
26
|
+
* If logFile is specified, logs go to the file.
|
|
27
|
+
* Otherwise, logs go to stderr (to keep stdout clean for CLI output).
|
|
28
|
+
*/
|
|
29
|
+
export function createLogger(options = {}) {
|
|
30
|
+
const { logFile, level = 'info', name = 'sofia' } = options;
|
|
31
|
+
const pinoOpts = {
|
|
32
|
+
name,
|
|
33
|
+
level,
|
|
34
|
+
redact: {
|
|
35
|
+
paths: REDACT_PATHS,
|
|
36
|
+
censor: '[REDACTED]',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
if (logFile) {
|
|
40
|
+
mkdirSync(dirname(logFile), { recursive: true });
|
|
41
|
+
const dest = pino.destination({ dest: logFile, sync: false });
|
|
42
|
+
return pino(pinoOpts, dest);
|
|
43
|
+
}
|
|
44
|
+
// Default: write to stderr to keep stdout clean
|
|
45
|
+
return pino(pinoOpts, pino.destination({ dest: 2, sync: false }));
|
|
46
|
+
}
|
|
47
|
+
/** Global logger instance; call initGlobalLogger() to configure it. */
|
|
48
|
+
let globalLogger;
|
|
49
|
+
export function initGlobalLogger(options = {}) {
|
|
50
|
+
globalLogger = createLogger(options);
|
|
51
|
+
return globalLogger;
|
|
52
|
+
}
|
|
53
|
+
export function getLogger() {
|
|
54
|
+
if (!globalLogger) {
|
|
55
|
+
globalLogger = createLogger();
|
|
56
|
+
}
|
|
57
|
+
return globalLogger;
|
|
58
|
+
}
|