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,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Generator.
|
|
3
|
+
*
|
|
4
|
+
* Builds LLM iteration prompts from test failures and PoC context,
|
|
5
|
+
* parses fenced code block responses from the LLM, writes generated
|
|
6
|
+
* files to the output directory, and detects new package.json dependencies.
|
|
7
|
+
*
|
|
8
|
+
* Contract: specs/002-poc-generation/contracts/ralph-loop.md (Code change format)
|
|
9
|
+
*/
|
|
10
|
+
import { writeFile, mkdir, readFile } from 'node:fs/promises';
|
|
11
|
+
import { join, dirname, resolve, sep, isAbsolute } from 'node:path';
|
|
12
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
13
|
+
// ── Code block parsing ────────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Parse fenced code blocks with `file=path` annotations from LLM response.
|
|
16
|
+
*
|
|
17
|
+
* Recognizes:
|
|
18
|
+
* ```typescript file=src/index.ts
|
|
19
|
+
* // content
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ```ts file=tests/index.test.ts
|
|
23
|
+
* // content
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function parseFencedCodeBlocks(response) {
|
|
27
|
+
const results = [];
|
|
28
|
+
// Match ``` optionally followed by language, then `file=<path>`, then content, then ```
|
|
29
|
+
// Allow optional spaces between language and file=
|
|
30
|
+
// Match fenced code blocks with `file=<path>` annotation.
|
|
31
|
+
//
|
|
32
|
+
// Pattern breakdown:
|
|
33
|
+
// ^``` - opening fence at line start
|
|
34
|
+
// (\w*) - optional language tag (e.g., typescript, ts, json)
|
|
35
|
+
// \s+ - whitespace separator
|
|
36
|
+
// (?:file=(\S+) - "file=" followed by path (group 2), OR
|
|
37
|
+
// |.*?file=(\S+))- any prefix then "file=" (group 3, handles "lang file=path")
|
|
38
|
+
// \s*\n - optional trailing whitespace + newline
|
|
39
|
+
// ([\s\S]*?) - multi-line file content (group 4, non-greedy)
|
|
40
|
+
// ^``` - closing fence at line start
|
|
41
|
+
//
|
|
42
|
+
// The `gm` flags enable global matching and treat ^ as start-of-line.
|
|
43
|
+
const fencePattern = /^```(\w*)\s+(?:file=(\S+)|.*?file=(\S+))\s*\n([\s\S]*?)^```/gm;
|
|
44
|
+
let match;
|
|
45
|
+
while ((match = fencePattern.exec(response)) !== null) {
|
|
46
|
+
const language = match[1] || 'text';
|
|
47
|
+
const filePath = match[2] || match[3];
|
|
48
|
+
const content = match[4];
|
|
49
|
+
if (filePath && content !== undefined) {
|
|
50
|
+
// Normalize path (remove leading ./ if present)
|
|
51
|
+
const normalizedPath = filePath.replace(/^\.\//, '');
|
|
52
|
+
results.push({
|
|
53
|
+
path: normalizedPath,
|
|
54
|
+
content,
|
|
55
|
+
language,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
// ── Directory tree helper ─────────────────────────────────────────────────────
|
|
62
|
+
/**
|
|
63
|
+
* Build a simple file tree listing for a directory.
|
|
64
|
+
* Excludes node_modules/, dist/, coverage/, .git/.
|
|
65
|
+
*/
|
|
66
|
+
export function buildFileTree(dir, prefix = '') {
|
|
67
|
+
const EXCLUDED = new Set(['node_modules', 'dist', 'coverage', '.git', '.sofia-metadata.json']);
|
|
68
|
+
const lines = [];
|
|
69
|
+
if (!existsSync(dir))
|
|
70
|
+
return lines;
|
|
71
|
+
let entries;
|
|
72
|
+
try {
|
|
73
|
+
entries = readdirSync(dir).sort();
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return lines;
|
|
77
|
+
}
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
if (EXCLUDED.has(entry))
|
|
80
|
+
continue;
|
|
81
|
+
const fullPath = join(dir, entry);
|
|
82
|
+
let isDir = false;
|
|
83
|
+
try {
|
|
84
|
+
isDir = statSync(fullPath).isDirectory();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (isDir) {
|
|
90
|
+
lines.push(`${prefix}${entry}/`);
|
|
91
|
+
lines.push(...buildFileTree(fullPath, `${prefix} `));
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.push(`${prefix}${entry}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return lines;
|
|
98
|
+
}
|
|
99
|
+
// ── Path safety ───────────────────────────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Returns true when `filePath` resolves to a location inside `baseDir`.
|
|
102
|
+
*
|
|
103
|
+
* Works cross-platform: on a POSIX host, `resolve()` is OS-native and correctly
|
|
104
|
+
* handles POSIX absolute paths. For Windows-style paths produced by an LLM on a
|
|
105
|
+
* POSIX host (`C:\...`, `\\server\...`, `//server/...`), we also apply explicit
|
|
106
|
+
* pattern matching so they are caught regardless of which OS the server runs on.
|
|
107
|
+
*/
|
|
108
|
+
export function isPathWithinDirectory(filePath, baseDir) {
|
|
109
|
+
const resolvedBase = resolve(baseDir);
|
|
110
|
+
const prefix = resolvedBase.endsWith(sep) ? resolvedBase : resolvedBase + sep;
|
|
111
|
+
const resolvedFull = resolve(baseDir, filePath);
|
|
112
|
+
return resolvedFull.startsWith(prefix);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns true when `filePath` must be rejected before any path joining.
|
|
116
|
+
*
|
|
117
|
+
* Catches:
|
|
118
|
+
* - POSIX absolute paths (`/etc/passwd`) via `isAbsolute()`.
|
|
119
|
+
* - Windows drive-letter paths (`C:\...` or `C:/...`) — unsafe on Windows hosts;
|
|
120
|
+
* `isAbsolute()` only detects these correctly on Windows, so we also apply an
|
|
121
|
+
* explicit pattern that works from any host OS.
|
|
122
|
+
* - Windows/POSIX UNC paths (`\\server\share` or `//server/share`).
|
|
123
|
+
* - Path traversal segments (`..`).
|
|
124
|
+
*/
|
|
125
|
+
export function isUnsafePath(filePath) {
|
|
126
|
+
if (isAbsolute(filePath))
|
|
127
|
+
return true;
|
|
128
|
+
if (filePath.includes('..'))
|
|
129
|
+
return true;
|
|
130
|
+
// Windows drive-letter absolute paths (C:\ or C:/)
|
|
131
|
+
if (/^[a-zA-Z]:[\\/]/.test(filePath))
|
|
132
|
+
return true;
|
|
133
|
+
// UNC paths with backslash or forward slash (\\server or //server)
|
|
134
|
+
if (/^[/\\]{2}/.test(filePath))
|
|
135
|
+
return true;
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// ── CodeGenerator ────────────────────────────────────────────────────────────
|
|
139
|
+
export class CodeGenerator {
|
|
140
|
+
outputDir;
|
|
141
|
+
constructor(outputDir) {
|
|
142
|
+
this.outputDir = outputDir;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Build the iteration prompt to send to the LLM.
|
|
146
|
+
*
|
|
147
|
+
* Follows the template from the ralph-loop contract.
|
|
148
|
+
*/
|
|
149
|
+
buildIterationPrompt(options) {
|
|
150
|
+
const { iteration, maxIterations, previousOutcome, testResults, filesInPoc, mcpContext } = options;
|
|
151
|
+
const lines = [];
|
|
152
|
+
lines.push('## Current State');
|
|
153
|
+
lines.push(`- Iteration: ${iteration} of ${maxIterations}`);
|
|
154
|
+
lines.push(`- Previous outcome: ${previousOutcome}`);
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('## Test Results');
|
|
157
|
+
lines.push(`- Passed: ${testResults.passed}, Failed: ${testResults.failed}, Skipped: ${testResults.skipped}`);
|
|
158
|
+
lines.push(`- Duration: ${testResults.durationMs}ms`);
|
|
159
|
+
lines.push('');
|
|
160
|
+
if (testResults.failures.length > 0) {
|
|
161
|
+
lines.push('### Failures');
|
|
162
|
+
testResults.failures.forEach((f, i) => {
|
|
163
|
+
lines.push(`${i + 1}. **${f.testName}**: ${f.message}`);
|
|
164
|
+
if (f.expected !== undefined)
|
|
165
|
+
lines.push(` Expected: ${f.expected}`);
|
|
166
|
+
if (f.actual !== undefined)
|
|
167
|
+
lines.push(` Actual: ${f.actual}`);
|
|
168
|
+
if (f.file)
|
|
169
|
+
lines.push(` At: ${f.file}${f.line ? `:${f.line}` : ''}`);
|
|
170
|
+
});
|
|
171
|
+
lines.push('');
|
|
172
|
+
}
|
|
173
|
+
if (filesInPoc.length > 0) {
|
|
174
|
+
lines.push('## Files in PoC');
|
|
175
|
+
for (const f of filesInPoc) {
|
|
176
|
+
lines.push(`- ${f}`);
|
|
177
|
+
}
|
|
178
|
+
lines.push('');
|
|
179
|
+
}
|
|
180
|
+
if (options.fileContents && options.fileContents.length > 0) {
|
|
181
|
+
lines.push('## Current Code');
|
|
182
|
+
lines.push('');
|
|
183
|
+
for (const file of options.fileContents) {
|
|
184
|
+
const lang = file.path.endsWith('.ts')
|
|
185
|
+
? 'typescript'
|
|
186
|
+
: file.path.endsWith('.json')
|
|
187
|
+
? 'json'
|
|
188
|
+
: 'text';
|
|
189
|
+
lines.push(`### ${file.path}`);
|
|
190
|
+
lines.push(`\`\`\`${lang} file=${file.path}`);
|
|
191
|
+
lines.push(file.content);
|
|
192
|
+
lines.push('```');
|
|
193
|
+
lines.push('');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (mcpContext) {
|
|
197
|
+
lines.push('## MCP Context');
|
|
198
|
+
lines.push(mcpContext);
|
|
199
|
+
lines.push('');
|
|
200
|
+
}
|
|
201
|
+
if (testResults.rawOutput) {
|
|
202
|
+
lines.push('## Raw Test Output (tail)');
|
|
203
|
+
lines.push('```');
|
|
204
|
+
lines.push(testResults.rawOutput);
|
|
205
|
+
lines.push('```');
|
|
206
|
+
lines.push('');
|
|
207
|
+
}
|
|
208
|
+
lines.push('## Task');
|
|
209
|
+
if (testResults.failed > 0) {
|
|
210
|
+
lines.push('Fix the failing tests and any underlying issues. For every file you modify or create, respond with its complete contents using fenced code blocks in this format:');
|
|
211
|
+
lines.push('');
|
|
212
|
+
lines.push('```ts file=relative/path/to/file.ts');
|
|
213
|
+
lines.push('// full file content here');
|
|
214
|
+
lines.push('```');
|
|
215
|
+
lines.push('');
|
|
216
|
+
lines.push('Only include complete files in these fenced code blocks; do not include partial snippets for changed files.');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
lines.push('The test run reported 0 failing tests. Review the code, tests, and test output for quality, completeness, and any hidden issues (such as parsing errors or missing tests).');
|
|
220
|
+
lines.push('If you determine that any code or tests should be improved or fixed, respond with the complete contents of each file you need to modify or create, using fenced code blocks in the same ` ```lang file=relative/path/to/file.ext` format as above.');
|
|
221
|
+
lines.push('If you are confident that no further changes are required, respond with a concise summary explaining why no changes are needed and do not include any fenced code blocks.');
|
|
222
|
+
}
|
|
223
|
+
return lines.join('\n');
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get the audit summary string for llmPromptContext field.
|
|
227
|
+
* Contains key context info, not the full prompt.
|
|
228
|
+
*/
|
|
229
|
+
buildPromptContextSummary(options) {
|
|
230
|
+
const { iteration, maxIterations, testResults, filesInPoc } = options;
|
|
231
|
+
return [
|
|
232
|
+
`Iteration ${iteration} of ${maxIterations}`,
|
|
233
|
+
`${testResults.failed} failures`,
|
|
234
|
+
`files: ${filesInPoc.slice(0, 10).join(', ')}${filesInPoc.length > 10 ? '...' : ''}`,
|
|
235
|
+
].join(', ');
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Apply LLM-generated code changes to the output directory.
|
|
239
|
+
*
|
|
240
|
+
* Parses fenced code blocks from the LLM response and writes them to disk.
|
|
241
|
+
* Returns information about what changed.
|
|
242
|
+
*/
|
|
243
|
+
async applyChanges(llmResponse) {
|
|
244
|
+
if (!llmResponse || llmResponse.trim().length === 0) {
|
|
245
|
+
return {
|
|
246
|
+
writtenFiles: [],
|
|
247
|
+
dependenciesChanged: false,
|
|
248
|
+
previousDependencies: {},
|
|
249
|
+
newDependencies: {},
|
|
250
|
+
llmPromptContext: '',
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
// Read current package.json dependencies before applying
|
|
254
|
+
const previousDeps = await this.readPackageJsonDeps();
|
|
255
|
+
// Parse fenced code blocks
|
|
256
|
+
const parsedFiles = parseFencedCodeBlocks(llmResponse);
|
|
257
|
+
const writtenFiles = [];
|
|
258
|
+
for (const file of parsedFiles) {
|
|
259
|
+
// Reject unsafe paths before any filesystem access.
|
|
260
|
+
if (isUnsafePath(file.path) || !isPathWithinDirectory(file.path, this.outputDir)) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const fullPath = join(this.outputDir, file.path);
|
|
264
|
+
const parentDir = dirname(fullPath);
|
|
265
|
+
await mkdir(parentDir, { recursive: true });
|
|
266
|
+
await writeFile(fullPath, file.content, 'utf-8');
|
|
267
|
+
writtenFiles.push(file.path);
|
|
268
|
+
}
|
|
269
|
+
// Check if package.json dependencies changed
|
|
270
|
+
const newDeps = await this.readPackageJsonDeps();
|
|
271
|
+
const dependenciesChanged = this.depsChanged(previousDeps, newDeps);
|
|
272
|
+
return {
|
|
273
|
+
writtenFiles,
|
|
274
|
+
dependenciesChanged,
|
|
275
|
+
previousDependencies: previousDeps,
|
|
276
|
+
newDependencies: newDeps,
|
|
277
|
+
llmPromptContext: '',
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Read package.json dependencies (both deps and devDeps).
|
|
282
|
+
*/
|
|
283
|
+
async readPackageJsonDeps() {
|
|
284
|
+
try {
|
|
285
|
+
const pkgPath = join(this.outputDir, 'package.json');
|
|
286
|
+
const content = await readFile(pkgPath, 'utf-8');
|
|
287
|
+
const pkg = JSON.parse(content);
|
|
288
|
+
return {
|
|
289
|
+
...(pkg.dependencies ?? {}),
|
|
290
|
+
...(pkg.devDependencies ?? {}),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return {};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Check if dependencies changed between two snapshots.
|
|
299
|
+
*/
|
|
300
|
+
depsChanged(prev, next) {
|
|
301
|
+
const prevKeys = Object.keys(prev).sort();
|
|
302
|
+
const nextKeys = Object.keys(next).sort();
|
|
303
|
+
if (prevKeys.length !== nextKeys.length)
|
|
304
|
+
return true;
|
|
305
|
+
if (prevKeys.join(',') !== nextKeys.join(','))
|
|
306
|
+
return true;
|
|
307
|
+
for (const key of prevKeys) {
|
|
308
|
+
if (prev[key] !== next[key])
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Get the current files in the PoC directory (relative paths).
|
|
315
|
+
*/
|
|
316
|
+
getFilesInPoc() {
|
|
317
|
+
return buildFileTree(this.outputDir);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic PoC Scaffold Generator.
|
|
3
|
+
*
|
|
4
|
+
* Generates initial project structure using LLM based on workshop context,
|
|
5
|
+
* replacing the fixed template approach. Creates meaningful failing tests
|
|
6
|
+
* that drive real implementation aligned with the workshop plan.
|
|
7
|
+
*
|
|
8
|
+
* Contract: Uses Copilot SDK to generate project files dynamically
|
|
9
|
+
*/
|
|
10
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
11
|
+
import { join, dirname } from 'node:path';
|
|
12
|
+
import { exportWorkshopDocs } from '../sessions/exportWriter.js';
|
|
13
|
+
// ── Helper Functions ─────────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Build a comprehensive prompt for scaffold generation.
|
|
16
|
+
*/
|
|
17
|
+
function buildScaffoldPrompt(session) {
|
|
18
|
+
const idea = session.ideas?.find((i) => i.id === session.selection?.ideaId);
|
|
19
|
+
const ideaTitle = idea?.title ?? session.selection?.ideaId ?? 'AI Solution';
|
|
20
|
+
const ideaDescription = idea?.description ?? 'AI-powered solution from workshop';
|
|
21
|
+
const businessContext = session.businessContext
|
|
22
|
+
? `## Business Context\n\n` +
|
|
23
|
+
`Company: ${session.businessContext.businessDescription}\n\n` +
|
|
24
|
+
`Key Challenges:\n${session.businessContext.challenges?.map((c) => `- ${c}`).join('\n') ?? 'N/A'}\n\n`
|
|
25
|
+
: '';
|
|
26
|
+
const planContext = session.plan
|
|
27
|
+
? `## Implementation Plan\n\n` +
|
|
28
|
+
`Architecture Notes:\n${session.plan.architectureNotes ?? 'Not specified'}\n\n` +
|
|
29
|
+
`Dependencies:\n${session.plan.dependencies?.map((d) => `- ${d}`).join('\n') ?? 'None specified'}\n\n` +
|
|
30
|
+
`Milestones:\n${session.plan.milestones?.map((m) => `- ${m.title}\n ${m.items.map((i) => ` • ${i}`).join('\n')}`).join('\n') ?? 'Not specified'}\n\n`
|
|
31
|
+
: '';
|
|
32
|
+
const selectionRationale = session.selection?.selectionRationale
|
|
33
|
+
? `## Why This Idea Was Selected\n\n${session.selection.selectionRationale}\n\n`
|
|
34
|
+
: '';
|
|
35
|
+
return `# Generate Proof-of-Concept Project Structure
|
|
36
|
+
|
|
37
|
+
You are generating the initial project structure for a proof-of-concept that was designed through an AI Discovery Workshop.
|
|
38
|
+
|
|
39
|
+
${businessContext}
|
|
40
|
+
## Selected Idea
|
|
41
|
+
|
|
42
|
+
**Title**: ${ideaTitle}
|
|
43
|
+
|
|
44
|
+
**Description**: ${ideaDescription}
|
|
45
|
+
|
|
46
|
+
${selectionRationale}
|
|
47
|
+
${planContext}
|
|
48
|
+
## Your Task
|
|
49
|
+
|
|
50
|
+
Generate a complete, working project structure for this PoC. The project should:
|
|
51
|
+
|
|
52
|
+
1. **Use the technology stack** described in the plan (infer if not explicitly stated)
|
|
53
|
+
2. **Create failing tests** that describe the core functionality from the plan
|
|
54
|
+
3. **Include proper project setup** (package.json, tsconfig, .gitignore, README)
|
|
55
|
+
4. **Scaffold minimal implementation** that makes tests compile but fail
|
|
56
|
+
|
|
57
|
+
**CRITICAL**: The tests should describe REAL functionality from the plan, not trivial checks.
|
|
58
|
+
Tests should fail initially because the implementation doesn't exist yet.
|
|
59
|
+
|
|
60
|
+
## Output Format
|
|
61
|
+
|
|
62
|
+
Respond with fenced code blocks containing complete file contents, using the \`file=\` attribute:
|
|
63
|
+
|
|
64
|
+
\`\`\`json file=package.json
|
|
65
|
+
{
|
|
66
|
+
"name": "my-poc",
|
|
67
|
+
...
|
|
68
|
+
}
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
\`\`\`typescript file=src/index.ts
|
|
72
|
+
// implementation stub
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
\`\`\`typescript file=tests/core.test.ts
|
|
76
|
+
// tests describing real functionality
|
|
77
|
+
\`\`\`
|
|
78
|
+
|
|
79
|
+
Generate all necessary files for a working project:
|
|
80
|
+
- package.json (with proper dependencies)
|
|
81
|
+
- Configuration files (tsconfig.json, .gitignore, etc.)
|
|
82
|
+
- Source files (src/) with stubs that throw "Not implemented"
|
|
83
|
+
- Test files (tests/) with meaningful assertions about expected behavior
|
|
84
|
+
- README.md explaining the PoC and how to run it
|
|
85
|
+
|
|
86
|
+
Focus on **quality over quantity** - generate files that matter for demonstrating the core idea.`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse code blocks from LLM response.
|
|
90
|
+
*/
|
|
91
|
+
function parseCodeBlocks(response) {
|
|
92
|
+
const blocks = [];
|
|
93
|
+
// Match fenced code blocks with file= attribute
|
|
94
|
+
const regex = /```(?:\w+)?\s+file=([^\s]+)\n([\s\S]*?)```/g;
|
|
95
|
+
let match;
|
|
96
|
+
while ((match = regex.exec(response)) !== null) {
|
|
97
|
+
const file = match[1].trim();
|
|
98
|
+
const content = match[2];
|
|
99
|
+
blocks.push({ file, content });
|
|
100
|
+
}
|
|
101
|
+
return blocks;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Infer tech stack from generated files.
|
|
105
|
+
*/
|
|
106
|
+
function inferTechStack(files) {
|
|
107
|
+
const hasPackageJson = files.some((f) => f.file === 'package.json');
|
|
108
|
+
const hasTypeScript = files.some((f) => f.file.endsWith('.ts') || f.file === 'tsconfig.json');
|
|
109
|
+
const hasPython = files.some((f) => f.file.endsWith('.py') || f.file === 'requirements.txt' || f.file === 'pyproject.toml');
|
|
110
|
+
// Try to parse package.json for more details
|
|
111
|
+
let testRunner = 'npm test';
|
|
112
|
+
let framework;
|
|
113
|
+
let buildCommand;
|
|
114
|
+
const packageJson = files.find((f) => f.file === 'package.json');
|
|
115
|
+
if (packageJson) {
|
|
116
|
+
try {
|
|
117
|
+
const pkg = JSON.parse(packageJson.content);
|
|
118
|
+
testRunner = pkg.scripts?.test ?? 'npm test';
|
|
119
|
+
buildCommand = pkg.scripts?.build;
|
|
120
|
+
// Detect framework from dependencies
|
|
121
|
+
if (pkg.dependencies?.['express'] || pkg.devDependencies?.['express'])
|
|
122
|
+
framework = 'Express';
|
|
123
|
+
if (pkg.dependencies?.['react'] || pkg.devDependencies?.['react'])
|
|
124
|
+
framework = 'React';
|
|
125
|
+
if (pkg.dependencies?.['fastify'] || pkg.devDependencies?.['fastify'])
|
|
126
|
+
framework = 'Fastify';
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Ignore parse errors
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (hasTypeScript) {
|
|
133
|
+
return {
|
|
134
|
+
language: 'TypeScript',
|
|
135
|
+
runtime: 'Node.js 20',
|
|
136
|
+
framework,
|
|
137
|
+
testRunner,
|
|
138
|
+
buildCommand,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (hasPython) {
|
|
142
|
+
return {
|
|
143
|
+
language: 'Python',
|
|
144
|
+
runtime: 'Python 3.11+',
|
|
145
|
+
testRunner: 'pytest',
|
|
146
|
+
buildCommand: undefined,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// Default to Node.js
|
|
150
|
+
return {
|
|
151
|
+
language: hasPackageJson ? 'JavaScript' : 'TypeScript',
|
|
152
|
+
runtime: 'Node.js 20',
|
|
153
|
+
testRunner,
|
|
154
|
+
buildCommand,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// ── Main Generator ───────────────────────────────────────────────────────────
|
|
158
|
+
/**
|
|
159
|
+
* Generate initial PoC scaffold using LLM.
|
|
160
|
+
*
|
|
161
|
+
* Replaces the template-based PocScaffolder with dynamic generation
|
|
162
|
+
* that's grounded in the workshop session context.
|
|
163
|
+
*/
|
|
164
|
+
export async function generateDynamicScaffold(context) {
|
|
165
|
+
const { session, outputDir, client } = context;
|
|
166
|
+
// Build comprehensive prompt from session
|
|
167
|
+
const prompt = buildScaffoldPrompt(session);
|
|
168
|
+
// Create conversation session with system prompt
|
|
169
|
+
const conversationSession = await client.createSession({
|
|
170
|
+
systemPrompt: 'You are a senior software architect generating proof-of-concept project structures for AI solutions.',
|
|
171
|
+
});
|
|
172
|
+
// Send user prompt and collect response
|
|
173
|
+
let fullResponse = '';
|
|
174
|
+
const stream = conversationSession.send({
|
|
175
|
+
role: 'user',
|
|
176
|
+
content: prompt,
|
|
177
|
+
});
|
|
178
|
+
for await (const event of stream) {
|
|
179
|
+
if (event.type === 'TextDelta') {
|
|
180
|
+
fullResponse += event.text;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Parse file blocks from response
|
|
184
|
+
const files = parseCodeBlocks(fullResponse);
|
|
185
|
+
if (files.length === 0) {
|
|
186
|
+
throw new Error('LLM did not generate any files. Response: ' + fullResponse.substring(0, 500));
|
|
187
|
+
}
|
|
188
|
+
// Write files to disk
|
|
189
|
+
const createdFiles = [];
|
|
190
|
+
for (const { file, content } of files) {
|
|
191
|
+
const fullPath = join(outputDir, file);
|
|
192
|
+
// Ensure directory exists
|
|
193
|
+
await mkdir(dirname(fullPath), { recursive: true });
|
|
194
|
+
// Write file
|
|
195
|
+
await writeFile(fullPath, content, 'utf-8');
|
|
196
|
+
createdFiles.push(file);
|
|
197
|
+
}
|
|
198
|
+
// Export workshop documentation
|
|
199
|
+
try {
|
|
200
|
+
const workshopResult = await exportWorkshopDocs(session, outputDir);
|
|
201
|
+
createdFiles.push(...workshopResult.createdFiles);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Non-fatal - PoC is still usable without workshop docs
|
|
205
|
+
}
|
|
206
|
+
// Create .sofia-metadata.json
|
|
207
|
+
const metadata = {
|
|
208
|
+
sessionId: session.sessionId,
|
|
209
|
+
featureSpec: '002-poc-generation',
|
|
210
|
+
generatedAt: new Date().toISOString(),
|
|
211
|
+
ideaTitle: session.ideas?.find((i) => i.id === session.selection?.ideaId)?.title ?? 'AI Solution',
|
|
212
|
+
totalIterations: 0,
|
|
213
|
+
finalStatus: null,
|
|
214
|
+
terminationReason: null,
|
|
215
|
+
generatedBy: 'dynamic-scaffold',
|
|
216
|
+
};
|
|
217
|
+
const metadataPath = join(outputDir, '.sofia-metadata.json');
|
|
218
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf-8');
|
|
219
|
+
createdFiles.push('.sofia-metadata.json');
|
|
220
|
+
// Infer tech stack from generated files
|
|
221
|
+
const techStack = inferTechStack(files);
|
|
222
|
+
return {
|
|
223
|
+
createdFiles,
|
|
224
|
+
techStack,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ── GitHubMcpAdapter ─────────────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Adapter for GitHub MCP repository operations.
|
|
4
|
+
*
|
|
5
|
+
* All methods check isAvailable() before attempting tool calls.
|
|
6
|
+
* On failure or unavailability, returns `{ available: false }` without throwing.
|
|
7
|
+
*/
|
|
8
|
+
export class GitHubMcpAdapter {
|
|
9
|
+
mcpManager;
|
|
10
|
+
_repoUrl;
|
|
11
|
+
constructor(mcpManager) {
|
|
12
|
+
this.mcpManager = mcpManager;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check if GitHub MCP is available.
|
|
16
|
+
*/
|
|
17
|
+
isAvailable() {
|
|
18
|
+
return this.mcpManager.isAvailable('github');
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a GitHub repository via MCP.
|
|
22
|
+
*
|
|
23
|
+
* Calls `mcpManager.callTool('github', 'create_repository', ...)` and parses
|
|
24
|
+
* the response for `html_url`. Returns `{ available: false }` if GitHub MCP
|
|
25
|
+
* is unavailable or if creation fails.
|
|
26
|
+
*/
|
|
27
|
+
async createRepository(options) {
|
|
28
|
+
if (!this.isAvailable()) {
|
|
29
|
+
return { available: false, reason: 'GitHub MCP not available' };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const response = await this.mcpManager.callTool('github', 'create_repository', {
|
|
33
|
+
name: options.name,
|
|
34
|
+
description: options.description ?? '',
|
|
35
|
+
private: options.private ?? true,
|
|
36
|
+
}, { timeoutMs: 60_000 });
|
|
37
|
+
const repoUrl = response.html_url ?? response.url ?? response.clone_url;
|
|
38
|
+
const repoName = response.name ?? response.full_name ?? options.name;
|
|
39
|
+
if (!repoUrl) {
|
|
40
|
+
return { available: false, reason: 'MCP response missing repository URL' };
|
|
41
|
+
}
|
|
42
|
+
this._repoUrl = repoUrl;
|
|
43
|
+
return {
|
|
44
|
+
available: true,
|
|
45
|
+
repoUrl,
|
|
46
|
+
repoName,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const reason = err instanceof Error ? err.message : 'Unknown error';
|
|
51
|
+
return { available: false, reason };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Push files to a GitHub repository via MCP.
|
|
56
|
+
*
|
|
57
|
+
* Calls `mcpManager.callTool('github', 'push_files', ...)` with file paths
|
|
58
|
+
* and contents. Returns `{ available: false }` if GitHub MCP is unavailable
|
|
59
|
+
* or if push fails.
|
|
60
|
+
*/
|
|
61
|
+
async pushFiles(options) {
|
|
62
|
+
if (!this.isAvailable()) {
|
|
63
|
+
return { available: false, reason: 'GitHub MCP not available' };
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
// Extract owner and repo from repoUrl
|
|
67
|
+
const { owner, repo } = extractOwnerRepo(options.repoUrl);
|
|
68
|
+
const response = await this.mcpManager.callTool('github', 'push_files', {
|
|
69
|
+
owner,
|
|
70
|
+
repo,
|
|
71
|
+
files: options.files,
|
|
72
|
+
message: options.commitMessage,
|
|
73
|
+
branch: options.branch ?? 'main',
|
|
74
|
+
}, { timeoutMs: 60_000 });
|
|
75
|
+
const commitSha = response.sha ??
|
|
76
|
+
response.commit?.sha;
|
|
77
|
+
return {
|
|
78
|
+
available: true,
|
|
79
|
+
commitSha,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
const reason = err instanceof Error ? err.message : 'Unknown error';
|
|
84
|
+
return { available: false, reason };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get the repository URL from the last successful createRepository call.
|
|
89
|
+
*/
|
|
90
|
+
getRepoUrl() {
|
|
91
|
+
return this._repoUrl;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
95
|
+
/**
|
|
96
|
+
* Extract owner and repo from a GitHub repository URL.
|
|
97
|
+
* Supports formats like:
|
|
98
|
+
* https://github.com/owner/repo
|
|
99
|
+
* https://github.com/owner/repo.git
|
|
100
|
+
* https://api.github.com/repos/owner/repo
|
|
101
|
+
*/
|
|
102
|
+
function extractOwnerRepo(repoUrl) {
|
|
103
|
+
// Try github.com/{owner}/{repo} pattern
|
|
104
|
+
const ghMatch = repoUrl.match(/github\.com\/([^/]+)\/([^/.]+)/);
|
|
105
|
+
if (ghMatch) {
|
|
106
|
+
return { owner: ghMatch[1], repo: ghMatch[2] };
|
|
107
|
+
}
|
|
108
|
+
// Fallback: try api.github.com/repos/{owner}/{repo}
|
|
109
|
+
const apiMatch = repoUrl.match(/api\.github\.com\/repos\/([^/]+)\/([^/.]+)/);
|
|
110
|
+
if (apiMatch) {
|
|
111
|
+
return { owner: apiMatch[1], repo: apiMatch[2] };
|
|
112
|
+
}
|
|
113
|
+
// Last resort: try extracting last two path segments
|
|
114
|
+
const segments = new URL(repoUrl).pathname.split('/').filter(Boolean);
|
|
115
|
+
if (segments.length >= 2) {
|
|
116
|
+
return {
|
|
117
|
+
owner: segments[segments.length - 2],
|
|
118
|
+
repo: segments[segments.length - 1].replace('.git', ''),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`Cannot extract owner/repo from URL: ${repoUrl}`);
|
|
122
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Develop phase module.
|
|
3
|
+
*
|
|
4
|
+
* Exports all components of the PoC Generation & Ralph Loop feature (Feature 002).
|
|
5
|
+
* Orchestrates: scaffold → install → iterate (test → fix → repeat) → complete.
|
|
6
|
+
*/
|
|
7
|
+
export { PocScaffolder } from './pocScaffolder.js';
|
|
8
|
+
export { generateDynamicScaffold } from './dynamicScaffolder.js';
|
|
9
|
+
export { TestRunner } from './testRunner.js';
|
|
10
|
+
export { CodeGenerator } from './codeGenerator.js';
|
|
11
|
+
export { McpContextEnricher } from './mcpContextEnricher.js';
|
|
12
|
+
export { GitHubMcpAdapter } from './githubMcpAdapter.js';
|
|
13
|
+
export { RalphLoop } from './ralphLoop.js';
|
|
14
|
+
export { deriveCheckpointState } from './checkpointState.js';
|
|
15
|
+
export { selectTemplate, createDefaultRegistry, NODE_TS_VITEST_TEMPLATE, PYTHON_PYTEST_TEMPLATE, } from './templateRegistry.js';
|