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,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity spinner module.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `ora` to provide unified visual feedback during LLM processing,
|
|
5
|
+
* tool calls, and internal operations (FR-043a, FR-043c).
|
|
6
|
+
*
|
|
7
|
+
* Spinner lifecycle:
|
|
8
|
+
* User input → startThinking() → "Thinking..."
|
|
9
|
+
* → ToolCall event → startToolCall(name) → "⠋ <name>..."
|
|
10
|
+
* → ToolResult event → completeToolCall(name, summary) → "✓ <name>: <summary>"
|
|
11
|
+
* → startThinking() (if more processing expected)
|
|
12
|
+
* → TextDelta event → stop() → stream text
|
|
13
|
+
*
|
|
14
|
+
* All operations are no-ops when non-TTY or JSON mode.
|
|
15
|
+
*/
|
|
16
|
+
import ora from 'ora';
|
|
17
|
+
import type { Ora } from 'ora';
|
|
18
|
+
|
|
19
|
+
export interface ActivitySpinnerOptions {
|
|
20
|
+
isTTY: boolean;
|
|
21
|
+
isJsonMode: boolean;
|
|
22
|
+
debugMode?: boolean;
|
|
23
|
+
/** Override stream for testing (default: process.stderr). */
|
|
24
|
+
stream?: NodeJS.WritableStream;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class ActivitySpinner {
|
|
28
|
+
private spinner: Ora | null = null;
|
|
29
|
+
private _active = false;
|
|
30
|
+
private readonly enabled: boolean;
|
|
31
|
+
private readonly debug: boolean;
|
|
32
|
+
private readonly stream: NodeJS.WritableStream;
|
|
33
|
+
|
|
34
|
+
constructor(options: ActivitySpinnerOptions) {
|
|
35
|
+
this.enabled = options.isTTY && !options.isJsonMode;
|
|
36
|
+
this.debug = options.debugMode ?? false;
|
|
37
|
+
this.stream = options.stream ?? process.stderr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Display "Thinking..." spinner during silent gaps. */
|
|
41
|
+
startThinking(): void {
|
|
42
|
+
if (!this.enabled) return;
|
|
43
|
+
|
|
44
|
+
if (this._active && this.spinner) {
|
|
45
|
+
this.spinner.text = 'Thinking...';
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.spinner = ora({
|
|
50
|
+
text: 'Thinking...',
|
|
51
|
+
stream: this.stream,
|
|
52
|
+
discardStdin: false,
|
|
53
|
+
}).start();
|
|
54
|
+
this._active = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Transition spinner to show tool-specific status. */
|
|
58
|
+
startToolCall(toolName: string): void {
|
|
59
|
+
if (!this.enabled) return;
|
|
60
|
+
|
|
61
|
+
if (this._active && this.spinner) {
|
|
62
|
+
this.spinner.text = `${toolName}...`;
|
|
63
|
+
} else {
|
|
64
|
+
this.spinner = ora({
|
|
65
|
+
text: `${toolName}...`,
|
|
66
|
+
stream: this.stream,
|
|
67
|
+
discardStdin: false,
|
|
68
|
+
}).start();
|
|
69
|
+
this._active = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Stop spinner and print a one-line tool completion summary.
|
|
75
|
+
* The summary line remains visible in the output stream.
|
|
76
|
+
*/
|
|
77
|
+
completeToolCall(_toolName: string, _summary: string): void {
|
|
78
|
+
if (!this.enabled) return;
|
|
79
|
+
|
|
80
|
+
if (this._active && this.spinner) {
|
|
81
|
+
this.spinner.stop();
|
|
82
|
+
}
|
|
83
|
+
this._active = false;
|
|
84
|
+
// Summary output is handled by io.writeToolSummary() to respect
|
|
85
|
+
// JSON/debug mode — no duplicate write here.
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Stop any active spinner. */
|
|
89
|
+
stop(): void {
|
|
90
|
+
if (this.spinner) {
|
|
91
|
+
this.spinner.stop();
|
|
92
|
+
}
|
|
93
|
+
this.spinner = null;
|
|
94
|
+
this._active = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Check if a spinner is currently active. */
|
|
98
|
+
isActive(): boolean {
|
|
99
|
+
return this._active;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a no-op spinner for tests or non-TTY environments.
|
|
105
|
+
*/
|
|
106
|
+
export function createNoOpSpinner(): ActivitySpinner {
|
|
107
|
+
return new ActivitySpinner({ isTTY: false, isJsonMode: true });
|
|
108
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copilot client abstraction.
|
|
3
|
+
*
|
|
4
|
+
* Wraps Copilot SDK interactions behind a small interface so that
|
|
5
|
+
* tests can use deterministic fakes instead of live LLM calls.
|
|
6
|
+
*/
|
|
7
|
+
import type { SofiaEvent } from './events.js';
|
|
8
|
+
import { createTextDeltaEvent } from './events.js';
|
|
9
|
+
|
|
10
|
+
// ── Types ────────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
export interface CopilotMessage {
|
|
13
|
+
role: 'user' | 'assistant' | 'system';
|
|
14
|
+
content: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ToolDefinition {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
parameters?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SessionOptions {
|
|
24
|
+
systemPrompt: string;
|
|
25
|
+
tools?: ToolDefinition[];
|
|
26
|
+
references?: string[];
|
|
27
|
+
/**
|
|
28
|
+
* MCP server configurations to forward to the Copilot SDK's
|
|
29
|
+
* `createSession()`. The SDK manages server lifecycle (spawn/connect,
|
|
30
|
+
* JSON-RPC, tool dispatch) for LLM-initiated tool calls.
|
|
31
|
+
*
|
|
32
|
+
* Format: `Record<string, MCPServerConfig>` matching the SDK's expected
|
|
33
|
+
* shape (MCPLocalServerConfig or MCPRemoteServerConfig).
|
|
34
|
+
* Uses `import('@github/copilot-sdk').MCPServerConfig` when SDK is available.
|
|
35
|
+
*/
|
|
36
|
+
mcpServers?: Record<
|
|
37
|
+
string,
|
|
38
|
+
{
|
|
39
|
+
type?: string;
|
|
40
|
+
tools: string[];
|
|
41
|
+
timeout?: number;
|
|
42
|
+
command?: string;
|
|
43
|
+
args?: string[];
|
|
44
|
+
env?: Record<string, string>;
|
|
45
|
+
cwd?: string;
|
|
46
|
+
url?: string;
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
>;
|
|
50
|
+
/**
|
|
51
|
+
* Optional hooks for SDK tool-call visibility (FR-021, FR-022).
|
|
52
|
+
* Forwarded to SDK `createSession({ hooks })` so tool-call activity
|
|
53
|
+
* is emitted to the CLI spinner via the activity event system.
|
|
54
|
+
*
|
|
55
|
+
* Shapes match the SDK's `SessionHooks` signatures:
|
|
56
|
+
* - `onPreToolUse(input: { toolName, toolArgs }, invocation)`
|
|
57
|
+
* - `onPostToolUse(input: { toolName, toolArgs, toolResult }, invocation)`
|
|
58
|
+
* - `onErrorOccurred(input: { error }, invocation)`
|
|
59
|
+
*/
|
|
60
|
+
hooks?: {
|
|
61
|
+
onPreToolUse?: (
|
|
62
|
+
input: { toolName: string; toolArgs: unknown },
|
|
63
|
+
invocation: { sessionId: string },
|
|
64
|
+
) => Promise<{ permissionDecision?: 'allow' | 'deny' | 'ask' } | void> | void;
|
|
65
|
+
onPostToolUse?: (
|
|
66
|
+
input: { toolName: string; toolArgs: unknown; toolResult: unknown },
|
|
67
|
+
invocation: { sessionId: string },
|
|
68
|
+
) => Promise<void> | void;
|
|
69
|
+
onErrorOccurred?: (
|
|
70
|
+
input: { error: string; errorContext?: string; recoverable?: boolean },
|
|
71
|
+
invocation: { sessionId: string },
|
|
72
|
+
) => Promise<void> | void;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Infinite session configuration for persistent workspaces and automatic
|
|
76
|
+
* context compaction. When enabled, sessions automatically manage context
|
|
77
|
+
* limits and persist state. Passed through to SDK `createSession()`.
|
|
78
|
+
*
|
|
79
|
+
* Recommended for Ralph Loop sessions to prevent context window exhaustion
|
|
80
|
+
* during extended multi-iteration conversations (FR-023).
|
|
81
|
+
*/
|
|
82
|
+
infiniteSessions?: {
|
|
83
|
+
enabled?: boolean;
|
|
84
|
+
backgroundCompactionThreshold?: number;
|
|
85
|
+
bufferExhaustionThreshold?: number;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Callback invoked when the SDK emits `assistant.usage` events (FR-024).
|
|
89
|
+
* Called with token usage data from each model interaction.
|
|
90
|
+
* Consumers can use this for debug logging or cumulative tracking.
|
|
91
|
+
*/
|
|
92
|
+
onUsage?: (usage: {
|
|
93
|
+
model: string;
|
|
94
|
+
inputTokens?: number;
|
|
95
|
+
outputTokens?: number;
|
|
96
|
+
cacheReadTokens?: number;
|
|
97
|
+
cacheWriteTokens?: number;
|
|
98
|
+
}) => void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* A conversation session with the LLM.
|
|
103
|
+
* Supports multi-turn conversations with streaming.
|
|
104
|
+
*/
|
|
105
|
+
export interface ConversationSession {
|
|
106
|
+
/** Send a message and receive streaming events. */
|
|
107
|
+
send(message: CopilotMessage): AsyncIterable<SofiaEvent>;
|
|
108
|
+
/** Get the full conversation history. */
|
|
109
|
+
getHistory(): CopilotMessage[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Client interface for creating conversation sessions.
|
|
114
|
+
* Live implementations wrap the Copilot SDK; fakes provide deterministic behavior.
|
|
115
|
+
*/
|
|
116
|
+
export interface CopilotClient {
|
|
117
|
+
createSession(options: SessionOptions): Promise<ConversationSession>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── Fake implementation for tests ────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
interface FakeClientOptions {
|
|
123
|
+
tools?: ToolDefinition[];
|
|
124
|
+
/** Override the send behavior entirely for custom test scenarios (e.g., transient failures). */
|
|
125
|
+
onChat?: (message: CopilotMessage) => Promise<CopilotMessage>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a fake CopilotClient that returns predetermined responses.
|
|
130
|
+
* Used in unit and integration tests for deterministic behavior.
|
|
131
|
+
*/
|
|
132
|
+
export function createFakeCopilotClient(
|
|
133
|
+
responses: CopilotMessage[],
|
|
134
|
+
_options?: FakeClientOptions,
|
|
135
|
+
): CopilotClient {
|
|
136
|
+
let responseIndex = 0;
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
async createSession(_sessionOptions: SessionOptions): Promise<ConversationSession> {
|
|
140
|
+
const history: CopilotMessage[] = [];
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
send(message: CopilotMessage): AsyncIterable<SofiaEvent> {
|
|
144
|
+
history.push(message);
|
|
145
|
+
|
|
146
|
+
// If onChat override is provided, use it (allows throwing for retry tests)
|
|
147
|
+
if (_options?.onChat) {
|
|
148
|
+
const chatFn = _options.onChat;
|
|
149
|
+
return {
|
|
150
|
+
async *[Symbol.asyncIterator]() {
|
|
151
|
+
const response = await chatFn(message);
|
|
152
|
+
history.push(response);
|
|
153
|
+
yield createTextDeltaEvent(response.content);
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const response = responses[responseIndex] ?? {
|
|
159
|
+
role: 'assistant' as const,
|
|
160
|
+
content: '[No more responses configured]',
|
|
161
|
+
};
|
|
162
|
+
responseIndex++;
|
|
163
|
+
history.push(response);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
async *[Symbol.asyncIterator]() {
|
|
167
|
+
// Simulate streaming by yielding the full content as a single TextDelta
|
|
168
|
+
yield createTextDeltaEvent(response.content);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
getHistory(): CopilotMessage[] {
|
|
173
|
+
return [...history];
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Live Copilot SDK client (created when SDK is available) ──────────────────
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Create a real CopilotClient using the GitHub Copilot SDK.
|
|
184
|
+
* Wraps the SDK's CopilotClient + CopilotSession behind our
|
|
185
|
+
* CopilotClient / ConversationSession interfaces so the rest of
|
|
186
|
+
* the codebase (ConversationLoop, phaseHandlers) stays decoupled.
|
|
187
|
+
*
|
|
188
|
+
* Throws a clear error if the SDK is not installed.
|
|
189
|
+
*/
|
|
190
|
+
export async function createCopilotClient(): Promise<CopilotClient> {
|
|
191
|
+
// Dynamic import — SDK is listed in dependencies but may fail in
|
|
192
|
+
// environments where the native add-on cannot build.
|
|
193
|
+
let sdk: typeof import('@github/copilot-sdk');
|
|
194
|
+
try {
|
|
195
|
+
sdk = await import('@github/copilot-sdk');
|
|
196
|
+
} catch (err: unknown) {
|
|
197
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
198
|
+
throw new Error(`GitHub Copilot SDK (@github/copilot-sdk) is not available: ${detail}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { CopilotClient: SdkClient, approveAll } = sdk;
|
|
202
|
+
|
|
203
|
+
// A single SDK client instance is shared across sessions.
|
|
204
|
+
// autoStart: true (default) means `start()` is called lazily on
|
|
205
|
+
// the first `createSession`.
|
|
206
|
+
const sdkClient = new SdkClient();
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
async createSession(options: SessionOptions): Promise<ConversationSession> {
|
|
210
|
+
const sessionConfig = {
|
|
211
|
+
onPermissionRequest: approveAll,
|
|
212
|
+
systemMessage: {
|
|
213
|
+
mode: 'replace' as const,
|
|
214
|
+
content: options.systemPrompt,
|
|
215
|
+
},
|
|
216
|
+
// Forward MCP server configs to the SDK so it manages server lifecycle
|
|
217
|
+
// (spawn, connect, JSON-RPC dispatch) for LLM-initiated tool calls.
|
|
218
|
+
// Cast needed because our SessionOptions uses a portable shape that
|
|
219
|
+
// doesn't carry the SDK's discriminated-union literal types.
|
|
220
|
+
...(options.mcpServers && Object.keys(options.mcpServers).length > 0
|
|
221
|
+
? {
|
|
222
|
+
mcpServers: options.mcpServers as Record<
|
|
223
|
+
string,
|
|
224
|
+
import('@github/copilot-sdk').MCPServerConfig
|
|
225
|
+
>,
|
|
226
|
+
}
|
|
227
|
+
: {}),
|
|
228
|
+
// Forward SDK hooks for tool-call visibility (FR-021, FR-022).
|
|
229
|
+
...(options.hooks ? { hooks: options.hooks } : {}),
|
|
230
|
+
// Forward infinite sessions config for context management (FR-023).
|
|
231
|
+
...(options.infiniteSessions ? { infiniteSessions: options.infiniteSessions } : {}),
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const sdkSession = await sdkClient.createSession(sessionConfig);
|
|
235
|
+
|
|
236
|
+
// Subscribe to assistant.usage events for token usage tracking (FR-024).
|
|
237
|
+
if (options.onUsage) {
|
|
238
|
+
const usageCb = options.onUsage;
|
|
239
|
+
sdkSession.on(
|
|
240
|
+
'assistant.usage',
|
|
241
|
+
(event: {
|
|
242
|
+
data: {
|
|
243
|
+
model: string;
|
|
244
|
+
inputTokens?: number;
|
|
245
|
+
outputTokens?: number;
|
|
246
|
+
cacheReadTokens?: number;
|
|
247
|
+
cacheWriteTokens?: number;
|
|
248
|
+
};
|
|
249
|
+
}) => {
|
|
250
|
+
usageCb(event.data);
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const history: CopilotMessage[] = [];
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
send(message: CopilotMessage): AsyncIterable<SofiaEvent> {
|
|
259
|
+
history.push(message);
|
|
260
|
+
|
|
261
|
+
// Return an AsyncIterable that bridges SDK events → SofiaEvents.
|
|
262
|
+
// We use `sendAndWait` which blocks until the assistant is idle
|
|
263
|
+
// and then yield the complete response as a single TextDelta,
|
|
264
|
+
// keeping the same contract the fake client provides.
|
|
265
|
+
return {
|
|
266
|
+
async *[Symbol.asyncIterator]() {
|
|
267
|
+
const response = await sdkSession.sendAndWait(
|
|
268
|
+
{ prompt: message.content },
|
|
269
|
+
120_000, // 2-minute timeout
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const content = response?.data.content ?? '';
|
|
273
|
+
if (content) {
|
|
274
|
+
const assistantMsg: CopilotMessage = {
|
|
275
|
+
role: 'assistant',
|
|
276
|
+
content,
|
|
277
|
+
};
|
|
278
|
+
history.push(assistantMsg);
|
|
279
|
+
yield createTextDeltaEvent(content);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
getHistory(): CopilotMessage[] {
|
|
286
|
+
return [...history];
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|