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,646 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PoC Scaffolder.
|
|
3
|
+
*
|
|
4
|
+
* Generates the initial proof-of-concept project structure from a
|
|
5
|
+
* `node-ts-vitest` template. Creates all required files for a working
|
|
6
|
+
* TypeScript + Vitest project.
|
|
7
|
+
*
|
|
8
|
+
* Contract: specs/002-poc-generation/contracts/poc-output.md
|
|
9
|
+
*/
|
|
10
|
+
import { writeFile, mkdir, access } from 'node:fs/promises';
|
|
11
|
+
import { join, dirname } from 'node:path';
|
|
12
|
+
import { execSync } from 'node:child_process';
|
|
13
|
+
|
|
14
|
+
import type { TechStack } from '../shared/schemas/session.js';
|
|
15
|
+
import type { WorkshopSession } from '../shared/schemas/session.js';
|
|
16
|
+
import type { TemplateEntry } from './templateRegistry.js';
|
|
17
|
+
|
|
18
|
+
// ── Types ────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export interface ScaffoldContext {
|
|
21
|
+
/** Kebab-case project name derived from idea title */
|
|
22
|
+
projectName: string;
|
|
23
|
+
/** Original idea title */
|
|
24
|
+
ideaTitle: string;
|
|
25
|
+
/** Idea description from workshop */
|
|
26
|
+
ideaDescription: string;
|
|
27
|
+
/** Tech stack to use */
|
|
28
|
+
techStack: TechStack;
|
|
29
|
+
/** Summary of the implementation plan */
|
|
30
|
+
planSummary: string;
|
|
31
|
+
/** Workshop session ID */
|
|
32
|
+
sessionId: string;
|
|
33
|
+
/** Output directory (absolute path) */
|
|
34
|
+
outputDir: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface TemplateFile {
|
|
38
|
+
/** Path relative to outputDir */
|
|
39
|
+
path: string;
|
|
40
|
+
/** File content string or generator function */
|
|
41
|
+
content: string | ((ctx: ScaffoldContext) => string);
|
|
42
|
+
/** Skip writing if file already exists (default: true) */
|
|
43
|
+
skipIfExists?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ScaffoldResult {
|
|
47
|
+
/** Files that were created */
|
|
48
|
+
createdFiles: string[];
|
|
49
|
+
/** Files that were skipped because they already existed */
|
|
50
|
+
skippedFiles: string[];
|
|
51
|
+
/** The scaffold context used */
|
|
52
|
+
context: ScaffoldContext;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Convert an idea title to a kebab-case project name.
|
|
59
|
+
*/
|
|
60
|
+
export function toKebabCase(title: string): string {
|
|
61
|
+
return title
|
|
62
|
+
.toLowerCase()
|
|
63
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
64
|
+
.replace(/^-+|-+$/g, '')
|
|
65
|
+
.substring(0, 64);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check if a file exists.
|
|
70
|
+
*/
|
|
71
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
72
|
+
try {
|
|
73
|
+
await access(filePath);
|
|
74
|
+
return true;
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── Template definitions ─────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
const NODE_TS_VITEST_TEMPLATE: TemplateFile[] = [
|
|
83
|
+
{
|
|
84
|
+
path: '.gitignore',
|
|
85
|
+
skipIfExists: false,
|
|
86
|
+
content: `node_modules/
|
|
87
|
+
dist/
|
|
88
|
+
coverage/
|
|
89
|
+
*.tsbuildinfo
|
|
90
|
+
.env
|
|
91
|
+
`,
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
{
|
|
95
|
+
path: 'package.json',
|
|
96
|
+
skipIfExists: true,
|
|
97
|
+
content: (ctx) =>
|
|
98
|
+
JSON.stringify(
|
|
99
|
+
{
|
|
100
|
+
name: ctx.projectName,
|
|
101
|
+
version: '0.1.0',
|
|
102
|
+
private: true,
|
|
103
|
+
type: 'module',
|
|
104
|
+
scripts: {
|
|
105
|
+
build: 'tsc',
|
|
106
|
+
start: 'node dist/index.js',
|
|
107
|
+
test: 'vitest run',
|
|
108
|
+
},
|
|
109
|
+
dependencies: {},
|
|
110
|
+
devDependencies: {
|
|
111
|
+
typescript: '^5.0.0',
|
|
112
|
+
vitest: '^3.0.0',
|
|
113
|
+
'@types/node': '^20.0.0',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
null,
|
|
117
|
+
2,
|
|
118
|
+
) + '\n',
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
{
|
|
122
|
+
path: 'tsconfig.json',
|
|
123
|
+
skipIfExists: true,
|
|
124
|
+
content:
|
|
125
|
+
JSON.stringify(
|
|
126
|
+
{
|
|
127
|
+
compilerOptions: {
|
|
128
|
+
target: 'ES2022',
|
|
129
|
+
module: 'Node16',
|
|
130
|
+
moduleResolution: 'Node16',
|
|
131
|
+
strict: true,
|
|
132
|
+
outDir: 'dist',
|
|
133
|
+
rootDir: 'src',
|
|
134
|
+
declaration: true,
|
|
135
|
+
esModuleInterop: true,
|
|
136
|
+
skipLibCheck: true,
|
|
137
|
+
},
|
|
138
|
+
include: ['src'],
|
|
139
|
+
},
|
|
140
|
+
null,
|
|
141
|
+
2,
|
|
142
|
+
) + '\n',
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
{
|
|
146
|
+
path: 'README.md',
|
|
147
|
+
skipIfExists: true,
|
|
148
|
+
content: (ctx) => `# ${ctx.ideaTitle}
|
|
149
|
+
|
|
150
|
+
${ctx.ideaDescription}
|
|
151
|
+
|
|
152
|
+
## Generated by
|
|
153
|
+
|
|
154
|
+
sofIA — AI Discovery Workshop CLI
|
|
155
|
+
Session: \`${ctx.sessionId}\`
|
|
156
|
+
Generated: ${new Date().toISOString()}
|
|
157
|
+
|
|
158
|
+
## Prerequisites
|
|
159
|
+
|
|
160
|
+
- Node.js 20+
|
|
161
|
+
- npm 9+
|
|
162
|
+
|
|
163
|
+
## Quick Start
|
|
164
|
+
|
|
165
|
+
\`\`\`bash
|
|
166
|
+
npm install
|
|
167
|
+
npm test
|
|
168
|
+
npm start
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
## How It Works
|
|
172
|
+
|
|
173
|
+
${ctx.planSummary}
|
|
174
|
+
|
|
175
|
+
## Technology Stack
|
|
176
|
+
|
|
177
|
+
- **Language**: ${ctx.techStack.language}
|
|
178
|
+
- **Runtime**: ${ctx.techStack.runtime}
|
|
179
|
+
- **Test Runner**: ${ctx.techStack.testRunner}
|
|
180
|
+
${ctx.techStack.framework ? `- **Framework**: ${ctx.techStack.framework}\n` : ''}
|
|
181
|
+
## Workshop Context
|
|
182
|
+
|
|
183
|
+
This project was selected and designed through a structured AI Discovery Workshop.
|
|
184
|
+
See [WORKSHOP.md](WORKSHOP.md) for the full decision history, including business
|
|
185
|
+
context, ideation, evaluation, selection rationale, and implementation plan.
|
|
186
|
+
|
|
187
|
+
Detailed per-phase documentation is in [\`docs/workshop/\`](docs/workshop/).
|
|
188
|
+
`,
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
{
|
|
192
|
+
path: 'src/index.ts',
|
|
193
|
+
skipIfExists: true,
|
|
194
|
+
content: (ctx) => `/**
|
|
195
|
+
* ${ctx.ideaTitle}
|
|
196
|
+
*
|
|
197
|
+
* ${ctx.ideaDescription}
|
|
198
|
+
*
|
|
199
|
+
* Generated by sofIA (session: ${ctx.sessionId})
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Main entry point for the ${ctx.projectName} PoC.
|
|
204
|
+
*
|
|
205
|
+
* TODO: Implement the core functionality described in the plan:
|
|
206
|
+
* ${ctx.planSummary.substring(0, 300)}
|
|
207
|
+
*
|
|
208
|
+
* The test file (tests/index.test.ts) describes the expected behavior.
|
|
209
|
+
* Implement the required functions to make the tests pass.
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
export async function main(input?: unknown): Promise<{ status: string; processed?: boolean }> {
|
|
213
|
+
// TODO: Implement based on the plan and test requirements
|
|
214
|
+
throw new Error('Not implemented yet. Ralph loop will implement this based on tests.');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Run if called directly
|
|
218
|
+
const isMain = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('index.ts');
|
|
219
|
+
if (isMain) {
|
|
220
|
+
main()
|
|
221
|
+
.then((result) => console.log('PoC result:', result))
|
|
222
|
+
.catch((err) => console.error('PoC error:', err));
|
|
223
|
+
}
|
|
224
|
+
`,
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
{
|
|
228
|
+
path: 'tests/index.test.ts',
|
|
229
|
+
skipIfExists: true,
|
|
230
|
+
content: (ctx) => `/**
|
|
231
|
+
* Tests for ${ctx.ideaTitle} PoC.
|
|
232
|
+
*
|
|
233
|
+
* These tests describe the expected behavior of the implementation.
|
|
234
|
+
* They are intentionally failing initially — the Ralph loop will
|
|
235
|
+
* iteratively fix the implementation until they pass.
|
|
236
|
+
*
|
|
237
|
+
* Generated by sofIA (session: ${ctx.sessionId})
|
|
238
|
+
* Plan Summary: ${ctx.planSummary.substring(0, 200)}
|
|
239
|
+
*/
|
|
240
|
+
import { describe, it, expect } from 'vitest';
|
|
241
|
+
|
|
242
|
+
describe('${ctx.projectName} PoC', () => {
|
|
243
|
+
describe('Core Module Exports', () => {
|
|
244
|
+
it('should export the main entry point', async () => {
|
|
245
|
+
// This test will fail until src/index.ts exports the required functions
|
|
246
|
+
const module = await import('../src/index.js');
|
|
247
|
+
expect(module).toBeDefined();
|
|
248
|
+
expect(typeof module.main).toBe('function');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should export core functionality modules', async () => {
|
|
252
|
+
// Uncomment and modify based on your architecture:
|
|
253
|
+
// const module = await import('../src/index.js');
|
|
254
|
+
// expect(module.processData).toBeDefined();
|
|
255
|
+
// expect(module.analyzeResults).toBeDefined();
|
|
256
|
+
// expect(module.generateOutput).toBeDefined();
|
|
257
|
+
|
|
258
|
+
// Placeholder: Replace with actual module exports from your plan
|
|
259
|
+
const module = await import('../src/index.js');
|
|
260
|
+
expect(module.main).toBeDefined();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
describe('Main Functionality', () => {
|
|
265
|
+
it('should execute the main workflow', async () => {
|
|
266
|
+
// This test describes the core PoC workflow from the plan
|
|
267
|
+
const { main } = await import('../src/index.js');
|
|
268
|
+
|
|
269
|
+
// TODO: Ralph loop will implement this based on the plan summary
|
|
270
|
+
const result = await main();
|
|
271
|
+
|
|
272
|
+
expect(result).toBeDefined();
|
|
273
|
+
expect(result).toHaveProperty('status');
|
|
274
|
+
expect(result.status).toBe('success');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should handle input data correctly', async () => {
|
|
278
|
+
// Test data processing as described in the plan
|
|
279
|
+
const { main } = await import('../src/index.js');
|
|
280
|
+
|
|
281
|
+
const testInput = { data: 'test' };
|
|
282
|
+
const result = await main(testInput);
|
|
283
|
+
|
|
284
|
+
expect(result).toBeDefined();
|
|
285
|
+
expect(result).toHaveProperty('processed');
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe('Error Handling', () => {
|
|
290
|
+
it('should handle invalid input gracefully', async () => {
|
|
291
|
+
const { main } = await import('../src/index.js');
|
|
292
|
+
|
|
293
|
+
// Test error cases
|
|
294
|
+
await expect(async () => {
|
|
295
|
+
await main(null);
|
|
296
|
+
}).rejects.toThrow();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
`,
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
{
|
|
304
|
+
path: '.sofia-metadata.json',
|
|
305
|
+
skipIfExists: false,
|
|
306
|
+
content: (ctx) =>
|
|
307
|
+
JSON.stringify(
|
|
308
|
+
{
|
|
309
|
+
sessionId: ctx.sessionId,
|
|
310
|
+
featureSpec: '002-poc-generation',
|
|
311
|
+
generatedAt: new Date().toISOString(),
|
|
312
|
+
ideaTitle: ctx.ideaTitle,
|
|
313
|
+
totalIterations: 0,
|
|
314
|
+
finalStatus: null,
|
|
315
|
+
terminationReason: null,
|
|
316
|
+
techStack: {
|
|
317
|
+
language: ctx.techStack.language.toLowerCase(),
|
|
318
|
+
runtime: ctx.techStack.runtime.toLowerCase(),
|
|
319
|
+
testRunner: ctx.techStack.testRunner,
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
null,
|
|
323
|
+
2,
|
|
324
|
+
) + '\n',
|
|
325
|
+
},
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
// ── PocScaffolder ────────────────────────────────────────────────────────────
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Generates a PoC project scaffold from the `node-ts-vitest` template.
|
|
332
|
+
*
|
|
333
|
+
* Accepts a ScaffoldContext derived from a workshop session and creates
|
|
334
|
+
* all required files. Files that already exist and have `skipIfExists: true`
|
|
335
|
+
* are skipped to allow resuming a partial scaffold.
|
|
336
|
+
*/
|
|
337
|
+
export class PocScaffolder {
|
|
338
|
+
private readonly template: TemplateFile[];
|
|
339
|
+
private readonly templateId: string | undefined;
|
|
340
|
+
|
|
341
|
+
constructor(templateOrFiles?: TemplateEntry | TemplateFile[]) {
|
|
342
|
+
if (templateOrFiles && 'files' in templateOrFiles && 'id' in templateOrFiles) {
|
|
343
|
+
// TemplateEntry
|
|
344
|
+
const entry = templateOrFiles as TemplateEntry;
|
|
345
|
+
this.template = entry.files;
|
|
346
|
+
this.templateId = entry.id;
|
|
347
|
+
} else {
|
|
348
|
+
this.template = (templateOrFiles as TemplateFile[] | undefined) ?? NODE_TS_VITEST_TEMPLATE;
|
|
349
|
+
this.templateId = undefined;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Build a ScaffoldContext from a workshop session.
|
|
355
|
+
*/
|
|
356
|
+
static buildContext(
|
|
357
|
+
session: WorkshopSession,
|
|
358
|
+
outputDir: string,
|
|
359
|
+
templateEntry?: TemplateEntry,
|
|
360
|
+
): ScaffoldContext {
|
|
361
|
+
const idea = session.ideas?.find((i) => i.id === session.selection?.ideaId);
|
|
362
|
+
const ideaTitle = idea?.title ?? 'AI PoC';
|
|
363
|
+
const ideaDescription = idea?.description ?? 'A proof-of-concept AI application.';
|
|
364
|
+
|
|
365
|
+
const planSummary = session.plan?.architectureNotes
|
|
366
|
+
? session.plan.architectureNotes
|
|
367
|
+
: (session.plan?.milestones?.map((m) => m.title).join(', ') ?? 'See plan for details');
|
|
368
|
+
|
|
369
|
+
const techStack: TechStack = templateEntry?.techStack
|
|
370
|
+
? { ...templateEntry.techStack }
|
|
371
|
+
: {
|
|
372
|
+
language: 'TypeScript',
|
|
373
|
+
runtime: 'Node.js 20',
|
|
374
|
+
testRunner: 'npm test',
|
|
375
|
+
buildCommand: 'npm run build',
|
|
376
|
+
framework: undefined,
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// Infer framework from plan if present
|
|
380
|
+
if (session.plan?.architectureNotes) {
|
|
381
|
+
const notes = session.plan.architectureNotes.toLowerCase();
|
|
382
|
+
if (notes.includes('express')) techStack.framework = 'Express';
|
|
383
|
+
else if (notes.includes('fastapi')) techStack.framework = 'FastAPI';
|
|
384
|
+
else if (notes.includes('next')) techStack.framework = 'Next.js';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
projectName: toKebabCase(ideaTitle),
|
|
389
|
+
ideaTitle,
|
|
390
|
+
ideaDescription,
|
|
391
|
+
techStack,
|
|
392
|
+
planSummary,
|
|
393
|
+
sessionId: session.sessionId,
|
|
394
|
+
outputDir,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Scaffold the PoC project into the output directory.
|
|
400
|
+
*
|
|
401
|
+
* Creates all template files, respecting skipIfExists.
|
|
402
|
+
* Returns lists of created and skipped files.
|
|
403
|
+
*/
|
|
404
|
+
async scaffold(context: ScaffoldContext): Promise<ScaffoldResult> {
|
|
405
|
+
await mkdir(context.outputDir, { recursive: true });
|
|
406
|
+
|
|
407
|
+
const createdFiles: string[] = [];
|
|
408
|
+
const skippedFiles: string[] = [];
|
|
409
|
+
|
|
410
|
+
for (const templateFile of this.template) {
|
|
411
|
+
const fullPath = join(context.outputDir, templateFile.path);
|
|
412
|
+
const shouldSkip = templateFile.skipIfExists !== false;
|
|
413
|
+
|
|
414
|
+
if (shouldSkip && (await fileExists(fullPath))) {
|
|
415
|
+
skippedFiles.push(templateFile.path);
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Ensure parent directory exists
|
|
420
|
+
const parentDir = dirname(fullPath);
|
|
421
|
+
await mkdir(parentDir, { recursive: true });
|
|
422
|
+
|
|
423
|
+
// Generate content
|
|
424
|
+
const content =
|
|
425
|
+
typeof templateFile.content === 'function'
|
|
426
|
+
? templateFile.content(context)
|
|
427
|
+
: templateFile.content;
|
|
428
|
+
|
|
429
|
+
await writeFile(fullPath, content, 'utf-8');
|
|
430
|
+
createdFiles.push(templateFile.path);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// FR-022: Add templateId to metadata if available
|
|
434
|
+
const metadataPath = join(context.outputDir, '.sofia-metadata.json');
|
|
435
|
+
if (await fileExists(metadataPath)) {
|
|
436
|
+
try {
|
|
437
|
+
const { readFile: rf } = await import('node:fs/promises');
|
|
438
|
+
const raw = await rf(metadataPath, 'utf-8');
|
|
439
|
+
const metadata = JSON.parse(raw);
|
|
440
|
+
if (this.templateId) {
|
|
441
|
+
metadata.templateId = this.templateId;
|
|
442
|
+
}
|
|
443
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf-8');
|
|
444
|
+
} catch {
|
|
445
|
+
// Ignore metadata update errors
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return { createdFiles, skippedFiles, context };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Scan scaffold files for TODO markers and update .sofia-metadata.json.
|
|
454
|
+
* Called after scaffolding to track initial TODO count (FR-022).
|
|
455
|
+
*/
|
|
456
|
+
static async scanAndRecordTodos(outputDir: string): Promise<{
|
|
457
|
+
totalInitial: number;
|
|
458
|
+
remaining: number;
|
|
459
|
+
markers: string[];
|
|
460
|
+
}> {
|
|
461
|
+
const { readFile: rf, readdir, stat } = await import('node:fs/promises');
|
|
462
|
+
const markers: string[] = [];
|
|
463
|
+
|
|
464
|
+
async function scanDir(dir: string, base: string): Promise<void> {
|
|
465
|
+
let entries;
|
|
466
|
+
try {
|
|
467
|
+
entries = await readdir(dir);
|
|
468
|
+
} catch {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
for (const entry of entries) {
|
|
472
|
+
if (entry === 'node_modules' || entry === '.git' || entry === 'dist') continue;
|
|
473
|
+
const full = join(dir, entry);
|
|
474
|
+
const rel = base ? `${base}/${entry}` : entry;
|
|
475
|
+
let s;
|
|
476
|
+
try {
|
|
477
|
+
s = await stat(full);
|
|
478
|
+
} catch {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (s.isDirectory()) {
|
|
482
|
+
await scanDir(full, rel);
|
|
483
|
+
} else if (s.isFile()) {
|
|
484
|
+
try {
|
|
485
|
+
const content = await rf(full, 'utf-8');
|
|
486
|
+
const lines = content.split('\n');
|
|
487
|
+
for (let i = 0; i < lines.length; i++) {
|
|
488
|
+
if (lines[i].includes('TODO:')) {
|
|
489
|
+
markers.push(`${rel}:${i + 1}: ${lines[i].trim()}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
// skip binary or unreadable files
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
await scanDir(outputDir, '');
|
|
500
|
+
|
|
501
|
+
const todos = {
|
|
502
|
+
totalInitial: markers.length,
|
|
503
|
+
remaining: markers.length,
|
|
504
|
+
markers,
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
// Update .sofia-metadata.json with TODO info
|
|
508
|
+
const metadataPath = join(outputDir, '.sofia-metadata.json');
|
|
509
|
+
try {
|
|
510
|
+
const raw = await rf(metadataPath, 'utf-8');
|
|
511
|
+
const metadata = JSON.parse(raw);
|
|
512
|
+
metadata.todos = todos;
|
|
513
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf-8');
|
|
514
|
+
} catch {
|
|
515
|
+
// Metadata file may not exist yet
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return todos;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Get the list of file paths in the template.
|
|
523
|
+
*/
|
|
524
|
+
getTemplateFiles(): string[] {
|
|
525
|
+
return this.template.map((f) => f.path);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Initialize a local git repository in the output directory.
|
|
530
|
+
* Creates an initial commit with all scaffold files.
|
|
531
|
+
*
|
|
532
|
+
* @param outputDir The directory to initialize git in
|
|
533
|
+
* @returns true if successful, false otherwise
|
|
534
|
+
*/
|
|
535
|
+
static async initializeGitRepo(outputDir: string): Promise<boolean> {
|
|
536
|
+
try {
|
|
537
|
+
// Check if git is already initialized
|
|
538
|
+
const gitDir = join(outputDir, '.git');
|
|
539
|
+
const exists = await fileExists(gitDir);
|
|
540
|
+
if (exists) {
|
|
541
|
+
return true; // Already initialized
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Initialize git repository
|
|
545
|
+
execSync('git init', { cwd: outputDir, stdio: 'ignore' });
|
|
546
|
+
|
|
547
|
+
// Stage all files
|
|
548
|
+
execSync('git add .', { cwd: outputDir, stdio: 'ignore' });
|
|
549
|
+
|
|
550
|
+
// Create initial commit
|
|
551
|
+
execSync('git commit -m "chore: initial scaffold from sofIA"', {
|
|
552
|
+
cwd: outputDir,
|
|
553
|
+
stdio: 'ignore',
|
|
554
|
+
env: {
|
|
555
|
+
...process.env,
|
|
556
|
+
GIT_AUTHOR_NAME: 'sofIA',
|
|
557
|
+
GIT_AUTHOR_EMAIL: 'sofia@workshop.local',
|
|
558
|
+
GIT_COMMITTER_NAME: 'sofIA',
|
|
559
|
+
GIT_COMMITTER_EMAIL: 'sofia@workshop.local',
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
return true;
|
|
564
|
+
} catch (_err) {
|
|
565
|
+
// Git initialization failed - not critical, just return false
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// ── Output Validator ─────────────────────────────────────────────────────────
|
|
572
|
+
|
|
573
|
+
export interface ValidationResult {
|
|
574
|
+
valid: boolean;
|
|
575
|
+
missingFiles: string[];
|
|
576
|
+
errors: string[];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Validate that a scaffold directory meets the poc-output contract requirements.
|
|
581
|
+
*/
|
|
582
|
+
export async function validatePocOutput(outputDir: string): Promise<ValidationResult> {
|
|
583
|
+
const requiredFiles = [
|
|
584
|
+
'package.json',
|
|
585
|
+
'README.md',
|
|
586
|
+
'tsconfig.json',
|
|
587
|
+
'.gitignore',
|
|
588
|
+
'.sofia-metadata.json',
|
|
589
|
+
];
|
|
590
|
+
|
|
591
|
+
const missingFiles: string[] = [];
|
|
592
|
+
const errors: string[] = [];
|
|
593
|
+
|
|
594
|
+
// Check required files
|
|
595
|
+
for (const file of requiredFiles) {
|
|
596
|
+
const exists = await fileExists(join(outputDir, file));
|
|
597
|
+
if (!exists) {
|
|
598
|
+
missingFiles.push(file);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Check package.json has test script
|
|
603
|
+
if (!missingFiles.includes('package.json')) {
|
|
604
|
+
try {
|
|
605
|
+
const { readFile } = await import('node:fs/promises');
|
|
606
|
+
const pkgContent = await readFile(join(outputDir, 'package.json'), 'utf-8');
|
|
607
|
+
const pkg = JSON.parse(pkgContent) as { scripts?: Record<string, string> };
|
|
608
|
+
if (!pkg.scripts?.test) {
|
|
609
|
+
errors.push('package.json is missing "test" script');
|
|
610
|
+
}
|
|
611
|
+
} catch {
|
|
612
|
+
errors.push('package.json is not valid JSON');
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Check for at least one .ts file in src/
|
|
617
|
+
const { readdir } = await import('node:fs/promises');
|
|
618
|
+
let hasSrcTs = false;
|
|
619
|
+
try {
|
|
620
|
+
const srcFiles = await readdir(join(outputDir, 'src'));
|
|
621
|
+
hasSrcTs = srcFiles.some((f) => f.endsWith('.ts'));
|
|
622
|
+
} catch {
|
|
623
|
+
// src/ doesn't exist
|
|
624
|
+
}
|
|
625
|
+
if (!hasSrcTs) {
|
|
626
|
+
errors.push('No TypeScript files found in src/');
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Check for at least one .test.ts file in tests/
|
|
630
|
+
let hasTestFile = false;
|
|
631
|
+
try {
|
|
632
|
+
const testFiles = await readdir(join(outputDir, 'tests'));
|
|
633
|
+
hasTestFile = testFiles.some((f) => f.endsWith('.test.ts'));
|
|
634
|
+
} catch {
|
|
635
|
+
// tests/ doesn't exist
|
|
636
|
+
}
|
|
637
|
+
if (!hasTestFile) {
|
|
638
|
+
errors.push('No test files found in tests/');
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
valid: missingFiles.length === 0 && errors.length === 0,
|
|
643
|
+
missingFiles,
|
|
644
|
+
errors,
|
|
645
|
+
};
|
|
646
|
+
}
|