@specverse/engine-realize 3.5.3
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/assets/examples/09-api/ai-spec.yaml +194 -0
- package/assets/examples/09-api/converted.yaml +95 -0
- package/assets/examples/09-api/diagram-architecture.mmd +10 -0
- package/assets/examples/09-api/diagram-er.mmd +10 -0
- package/assets/examples/09-api/documentation.html +104 -0
- package/assets/examples/09-api/documentation.md +95 -0
- package/assets/examples/09-api/inferred-spec.yaml +420 -0
- package/assets/examples/09-api/openapi.json +61 -0
- package/assets/examples/10-api/README.md +216 -0
- package/assets/examples/10-api/ai-spec.yaml +194 -0
- package/assets/examples/10-api/converted.yaml +96 -0
- package/assets/examples/10-api/diagram-architecture.mmd +10 -0
- package/assets/examples/10-api/diagram-er.mmd +10 -0
- package/assets/examples/10-api/documentation.html +104 -0
- package/assets/examples/10-api/documentation.md +95 -0
- package/assets/examples/10-api/inferred-spec.yaml +7 -0
- package/assets/examples/10-api/metadata.yaml +89 -0
- package/assets/examples/10-api/openapi.json +61 -0
- package/assets/examples/10-api/package-integration-test.js +177 -0
- package/assets/examples/10-api/usage-example.js +323 -0
- package/assets/examples/10-api/usage-example.ts +363 -0
- package/assets/examples/10-api/workflow-test.js +113 -0
- package/assets/examples/manifests/01-simple-default-mappings.yaml +36 -0
- package/assets/examples/manifests/02-capability-mappings.yaml +55 -0
- package/assets/examples/manifests/03-hybrid-mappings.yaml +109 -0
- package/assets/examples/manifests/README.md +245 -0
- package/assets/examples/manifests/backend-only.yaml +43 -0
- package/assets/examples/manifests/blog-api.md +78 -0
- package/assets/examples/manifests/blog-api.specly +79 -0
- package/assets/examples/manifests/frontend-only.yaml +27 -0
- package/assets/examples/manifests/fullstack-app.yaml +44 -0
- package/assets/examples/manifests/fullstack-monorepo.yaml +62 -0
- package/assets/examples/validate-examples-with-expected-failures.cjs +328 -0
- package/assets/examples/validate-examples.cjs +225 -0
- package/assets/examples-decomposed/cloud-native-manifest.example.yaml +8 -0
- package/assets/examples-decomposed/cloud-native-manifest.md +379 -0
- package/assets/examples-decomposed/cloud-native-manifest.specly +60 -0
- package/assets/examples-decomposed/docker-compose-manifest.example.yaml +8 -0
- package/assets/examples-decomposed/docker-compose-manifest.md +326 -0
- package/assets/examples-decomposed/docker-compose-manifest.specly +40 -0
- package/assets/examples-decomposed/kubernetes-deployment-manifest.example.yaml +8 -0
- package/assets/examples-decomposed/kubernetes-deployment-manifest.md +237 -0
- package/assets/examples-decomposed/kubernetes-deployment-manifest.specly +41 -0
- package/assets/templates/README.md +559 -0
- package/assets/templates/TEMPLATE-ENHANCEMENTS-V33.md +462 -0
- package/assets/templates/backend-only/CLAUDE.md +73 -0
- package/assets/templates/backend-only/README.md +197 -0
- package/assets/templates/backend-only/deployments/README.md +149 -0
- package/assets/templates/backend-only/deployments/development.specly +53 -0
- package/assets/templates/backend-only/deployments/production.specly +87 -0
- package/assets/templates/backend-only/docs/README.md +50 -0
- package/assets/templates/backend-only/docs/api/README.md +7 -0
- package/assets/templates/backend-only/docs/diagrams/README.md +85 -0
- package/assets/templates/backend-only/docs/example-documentation-template.md +269 -0
- package/assets/templates/backend-only/docs/guides/README.md +15 -0
- package/assets/templates/backend-only/dot.env.example +18 -0
- package/assets/templates/backend-only/generated/README.md +56 -0
- package/assets/templates/backend-only/generated/code/integration-test.template.js +320 -0
- package/assets/templates/backend-only/generated/code/package.json.template +34 -0
- package/assets/templates/backend-only/generated/docs/README.md +49 -0
- package/assets/templates/backend-only/gitignore +54 -0
- package/assets/templates/backend-only/manifests/README.md +72 -0
- package/assets/templates/backend-only/manifests/docker-compose.specly +91 -0
- package/assets/templates/backend-only/manifests/implementation.yaml +100 -0
- package/assets/templates/backend-only/manifests/kubernetes.specly +140 -0
- package/assets/templates/backend-only/package.json +59 -0
- package/assets/templates/backend-only/scripts/test-all.sh +160 -0
- package/assets/templates/backend-only/scripts/test-generated-code.sh +165 -0
- package/assets/templates/backend-only/specs/main.specly +67 -0
- package/assets/templates/default/CLAUDE.md +141 -0
- package/assets/templates/default/README.md +404 -0
- package/assets/templates/default/deployments/README.md +149 -0
- package/assets/templates/default/deployments/development.specly +53 -0
- package/assets/templates/default/deployments/production.specly +87 -0
- package/assets/templates/default/docs/README.md +50 -0
- package/assets/templates/default/docs/api/README.md +7 -0
- package/assets/templates/default/docs/diagrams/README.md +85 -0
- package/assets/templates/default/docs/example-documentation-template.md +269 -0
- package/assets/templates/default/docs/guides/README.md +15 -0
- package/assets/templates/default/dot.env.example +18 -0
- package/assets/templates/default/generated/README.md +56 -0
- package/assets/templates/default/generated/code/integration-test.template.js +320 -0
- package/assets/templates/default/generated/code/package.json.template +34 -0
- package/assets/templates/default/generated/docs/README.md +49 -0
- package/assets/templates/default/gitignore +54 -0
- package/assets/templates/default/manifests/README.md +72 -0
- package/assets/templates/default/manifests/docker-compose.specly +91 -0
- package/assets/templates/default/manifests/implementation.yaml +176 -0
- package/assets/templates/default/manifests/kubernetes.specly +140 -0
- package/assets/templates/default/package.json +61 -0
- package/assets/templates/default/scripts/test-all.sh +160 -0
- package/assets/templates/default/scripts/test-generated-code.sh +165 -0
- package/assets/templates/default/specs/main.specly +67 -0
- package/assets/templates/frontend-only/CLAUDE.md +75 -0
- package/assets/templates/frontend-only/README.md +231 -0
- package/assets/templates/frontend-only/deployments/README.md +149 -0
- package/assets/templates/frontend-only/deployments/development.specly +53 -0
- package/assets/templates/frontend-only/deployments/production.specly +87 -0
- package/assets/templates/frontend-only/docs/README.md +50 -0
- package/assets/templates/frontend-only/docs/api/README.md +7 -0
- package/assets/templates/frontend-only/docs/diagrams/README.md +85 -0
- package/assets/templates/frontend-only/docs/example-documentation-template.md +269 -0
- package/assets/templates/frontend-only/docs/guides/README.md +15 -0
- package/assets/templates/frontend-only/dot.env.example +18 -0
- package/assets/templates/frontend-only/generated/README.md +56 -0
- package/assets/templates/frontend-only/generated/code/integration-test.template.js +320 -0
- package/assets/templates/frontend-only/generated/code/package.json.template +34 -0
- package/assets/templates/frontend-only/generated/docs/README.md +49 -0
- package/assets/templates/frontend-only/gitignore +54 -0
- package/assets/templates/frontend-only/manifests/README.md +72 -0
- package/assets/templates/frontend-only/manifests/docker-compose.specly +91 -0
- package/assets/templates/frontend-only/manifests/implementation.yaml +58 -0
- package/assets/templates/frontend-only/manifests/kubernetes.specly +140 -0
- package/assets/templates/frontend-only/package.json +59 -0
- package/assets/templates/frontend-only/scripts/test-all.sh +160 -0
- package/assets/templates/frontend-only/scripts/test-generated-code.sh +165 -0
- package/assets/templates/frontend-only/specs/main.specly +57 -0
- package/assets/templates/full-stack/AI-GUIDE.md +60 -0
- package/assets/templates/full-stack/CLAUDE.md +141 -0
- package/assets/templates/full-stack/README.md +382 -0
- package/assets/templates/full-stack/archive/AI-GUIDE-legacy.md +392 -0
- package/assets/templates/full-stack/deployments/README.md +149 -0
- package/assets/templates/full-stack/deployments/development.specly +53 -0
- package/assets/templates/full-stack/deployments/production.specly +87 -0
- package/assets/templates/full-stack/docs/README.md +51 -0
- package/assets/templates/full-stack/docs/api/README.md +7 -0
- package/assets/templates/full-stack/docs/diagrams/README.md +85 -0
- package/assets/templates/full-stack/docs/example-documentation-template.md +269 -0
- package/assets/templates/full-stack/docs/guides/README.md +15 -0
- package/assets/templates/full-stack/generated/README.md +56 -0
- package/assets/templates/full-stack/generated/code/integration-test.template.js +320 -0
- package/assets/templates/full-stack/generated/code/package.json.template +34 -0
- package/assets/templates/full-stack/generated/docs/README.md +49 -0
- package/assets/templates/full-stack/gitignore +54 -0
- package/assets/templates/full-stack/manifests/README.md +72 -0
- package/assets/templates/full-stack/manifests/docker-compose.specly +91 -0
- package/assets/templates/full-stack/manifests/implementation.yaml +155 -0
- package/assets/templates/full-stack/manifests/kubernetes.specly +140 -0
- package/assets/templates/full-stack/package.json +45 -0
- package/assets/templates/full-stack/scripts/test-all.sh +160 -0
- package/assets/templates/full-stack/scripts/test-generated-code.sh +165 -0
- package/assets/templates/full-stack/specs/example-v33.specly +297 -0
- package/assets/templates/full-stack/specs/main-simple.specly +73 -0
- package/assets/templates/full-stack/specs/main.specly +408 -0
- package/dist/engines/code-generator.d.ts +86 -0
- package/dist/engines/code-generator.d.ts.map +1 -0
- package/dist/engines/code-generator.js +159 -0
- package/dist/engines/code-generator.js.map +1 -0
- package/dist/engines/engine-registry.d.ts +94 -0
- package/dist/engines/engine-registry.d.ts.map +1 -0
- package/dist/engines/engine-registry.js +163 -0
- package/dist/engines/engine-registry.js.map +1 -0
- package/dist/engines/index.d.ts +10 -0
- package/dist/engines/index.d.ts.map +1 -0
- package/dist/engines/index.js +12 -0
- package/dist/engines/index.js.map +1 -0
- package/dist/engines/typescript-engine.d.ts +74 -0
- package/dist/engines/typescript-engine.d.ts.map +1 -0
- package/dist/engines/typescript-engine.js +288 -0
- package/dist/engines/typescript-engine.js.map +1 -0
- package/dist/generators/index.d.ts +11 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +11 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +434 -0
- package/dist/index.js.map +1 -0
- package/dist/library/index.d.ts +12 -0
- package/dist/library/index.d.ts.map +1 -0
- package/dist/library/index.js +15 -0
- package/dist/library/index.js.map +1 -0
- package/dist/library/library.d.ts +132 -0
- package/dist/library/library.d.ts.map +1 -0
- package/dist/library/library.js +343 -0
- package/dist/library/library.js.map +1 -0
- package/dist/library/loader.d.ts +73 -0
- package/dist/library/loader.d.ts.map +1 -0
- package/dist/library/loader.js +150 -0
- package/dist/library/loader.js.map +1 -0
- package/dist/library/resolver.d.ts +104 -0
- package/dist/library/resolver.d.ts.map +1 -0
- package/dist/library/resolver.js +299 -0
- package/dist/library/resolver.js.map +1 -0
- package/dist/library/validator.d.ts +65 -0
- package/dist/library/validator.d.ts.map +1 -0
- package/dist/library/validator.js +203 -0
- package/dist/library/validator.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/instance-factory.d.ts +289 -0
- package/dist/types/instance-factory.d.ts.map +1 -0
- package/dist/types/instance-factory.js +8 -0
- package/dist/types/instance-factory.js.map +1 -0
- package/dist/types/unified-mappings.d.ts +163 -0
- package/dist/types/unified-mappings.d.ts.map +1 -0
- package/dist/types/unified-mappings.js +110 -0
- package/dist/types/unified-mappings.js.map +1 -0
- package/dist/utils/ai-spec-loader.d.ts +77 -0
- package/dist/utils/ai-spec-loader.d.ts.map +1 -0
- package/dist/utils/ai-spec-loader.js +138 -0
- package/dist/utils/ai-spec-loader.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/manifest-loader.d.ts +107 -0
- package/dist/utils/manifest-loader.d.ts.map +1 -0
- package/dist/utils/manifest-loader.js +168 -0
- package/dist/utils/manifest-loader.js.map +1 -0
- package/dist/utils/mapping-migration.d.ts +53 -0
- package/dist/utils/mapping-migration.d.ts.map +1 -0
- package/dist/utils/mapping-migration.js +194 -0
- package/dist/utils/mapping-migration.js.map +1 -0
- package/libs/instance-factories/CURVED-INTERFACE.md +278 -0
- package/libs/instance-factories/README.md +433 -0
- package/libs/instance-factories/applications/generic-app.yaml +52 -0
- package/libs/instance-factories/applications/react-app.yaml +186 -0
- package/libs/instance-factories/applications/templates/generic/backend-env-generator.ts +31 -0
- package/libs/instance-factories/applications/templates/generic/backend-package-json-generator.ts +80 -0
- package/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.ts +69 -0
- package/libs/instance-factories/applications/templates/generic/main-generator.ts +308 -0
- package/libs/instance-factories/applications/templates/react/_view-components-source.ts +555 -0
- package/libs/instance-factories/applications/templates/react/api-client-generator.ts +436 -0
- package/libs/instance-factories/applications/templates/react/api-types-generator.ts +153 -0
- package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +94 -0
- package/libs/instance-factories/applications/templates/react/env-example-generator.ts +24 -0
- package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +106 -0
- package/libs/instance-factories/applications/templates/react/gitignore-generator.ts +38 -0
- package/libs/instance-factories/applications/templates/react/index-css-generator.ts +85 -0
- package/libs/instance-factories/applications/templates/react/index-html-generator.ts +30 -0
- package/libs/instance-factories/applications/templates/react/main-tsx-generator.ts +34 -0
- package/libs/instance-factories/applications/templates/react/package-json-generator.ts +54 -0
- package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +179 -0
- package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +1347 -0
- package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +150 -0
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +704 -0
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +84 -0
- package/libs/instance-factories/applications/templates/react/tsconfig-generator.ts +35 -0
- package/libs/instance-factories/applications/templates/react/use-api-hooks-generator.ts +121 -0
- package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +150 -0
- package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +150 -0
- package/libs/instance-factories/applications/templates/react/view-form-generator.ts +362 -0
- package/libs/instance-factories/applications/templates/react/view-list-generator.ts +98 -0
- package/libs/instance-factories/applications/templates/react/view-router-generator.ts +89 -0
- package/libs/instance-factories/applications/templates/react/vite-config-generator.ts +49 -0
- package/libs/instance-factories/archived/fastify-prisma.yaml +104 -0
- package/libs/instance-factories/cli/commander-js.yaml +55 -0
- package/libs/instance-factories/cli/templates/commander/cli-entry-generator.d.ts +12 -0
- package/libs/instance-factories/cli/templates/commander/cli-entry-generator.d.ts.map +1 -0
- package/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +115 -0
- package/libs/instance-factories/cli/templates/commander/cli-entry-generator.js.map +1 -0
- package/libs/instance-factories/cli/templates/commander/cli-entry-generator.ts +145 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.d.ts +14 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.d.ts.map +1 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.js +182 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.js.map +1 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +992 -0
- package/libs/instance-factories/communication/event-emitter.yaml +56 -0
- package/libs/instance-factories/communication/rabbitmq-events.yaml +87 -0
- package/libs/instance-factories/communication/templates/eventemitter/bus-generator.ts +93 -0
- package/libs/instance-factories/communication/templates/eventemitter/publisher-generator.ts +117 -0
- package/libs/instance-factories/communication/templates/eventemitter/subscriber-generator.ts +101 -0
- package/libs/instance-factories/controllers/fastify.yaml +127 -0
- package/libs/instance-factories/controllers/templates/fastify/meta-routes-generator.ts +103 -0
- package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +389 -0
- package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +76 -0
- package/libs/instance-factories/infrastructure/docker-k8s.yaml +61 -0
- package/libs/instance-factories/infrastructure/templates/docker-k8s/infrastructure-generator.ts +46 -0
- package/libs/instance-factories/orms/prisma.yaml +89 -0
- package/libs/instance-factories/orms/templates/prisma/schema-generator.ts +563 -0
- package/libs/instance-factories/orms/templates/prisma/services-generator.ts +408 -0
- package/libs/instance-factories/scaffolding/generic-scaffold.yaml +65 -0
- package/libs/instance-factories/scaffolding/templates/generic/env-example-generator.ts +73 -0
- package/libs/instance-factories/scaffolding/templates/generic/env-generator.ts +85 -0
- package/libs/instance-factories/scaffolding/templates/generic/gitignore-generator.ts +69 -0
- package/libs/instance-factories/scaffolding/templates/generic/package-json-generator.ts +176 -0
- package/libs/instance-factories/scaffolding/templates/generic/readme-generator.ts +207 -0
- package/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.ts +78 -0
- package/libs/instance-factories/scaffolding/templates/generic/tsconfig-react-generator.ts +41 -0
- package/libs/instance-factories/sdks/python-sdk.yaml +66 -0
- package/libs/instance-factories/sdks/templates/python/sdk-generator.ts +50 -0
- package/libs/instance-factories/sdks/templates/typescript/sdk-generator.ts +49 -0
- package/libs/instance-factories/sdks/typescript-sdk.yaml +59 -0
- package/libs/instance-factories/services/prisma-services.yaml +71 -0
- package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +303 -0
- package/libs/instance-factories/services/templates/prisma/controller-generator.ts +532 -0
- package/libs/instance-factories/services/templates/prisma/service-generator.ts +315 -0
- package/libs/instance-factories/shared/path-resolver.ts +111 -0
- package/libs/instance-factories/storage/mongodb.yaml +79 -0
- package/libs/instance-factories/storage/postgresql.yaml +75 -0
- package/libs/instance-factories/storage/redis.yaml +79 -0
- package/libs/instance-factories/storage/templates/mongodb/config-generator.ts +15 -0
- package/libs/instance-factories/storage/templates/mongodb/docker-generator.ts +18 -0
- package/libs/instance-factories/storage/templates/postgresql/config-generator.ts +54 -0
- package/libs/instance-factories/storage/templates/postgresql/docker-generator.ts +55 -0
- package/libs/instance-factories/storage/templates/redis/config-generator.ts +16 -0
- package/libs/instance-factories/storage/templates/redis/docker-generator.ts +18 -0
- package/libs/instance-factories/test-generation.ts +192 -0
- package/libs/instance-factories/testing/templates/vitest/tests-generator.ts +51 -0
- package/libs/instance-factories/testing/vitest-tests.yaml +63 -0
- package/libs/instance-factories/tools/templates/mcp/mcp-server-generator.ts +136 -0
- package/libs/instance-factories/tools/templates/mcp/static/docs/DEPLOYMENT_GUIDE.md +630 -0
- package/libs/instance-factories/tools/templates/mcp/static/docs/HYBRID_RESOURCE_SYSTEM.md +330 -0
- package/libs/instance-factories/tools/templates/mcp/static/docs/deployments/EXTENSION_DEPLOYMENT.md +552 -0
- package/libs/instance-factories/tools/templates/mcp/static/docs/deployments/LOCAL_DEPLOYMENT.md +164 -0
- package/libs/instance-factories/tools/templates/mcp/static/docs/deployments/WEB_DEPLOYMENT.md +247 -0
- package/libs/instance-factories/tools/templates/mcp/static/package.json +92 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/build-enterprise.js +284 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/build-extension.js +139 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/build-local.js +74 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/build-web.js +156 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/copy-canonical-files.js +41 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/test-deployments.js +259 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/test-hybrid-resources.js +231 -0
- package/libs/instance-factories/tools/templates/mcp/static/scripts/test-hybrid-simple.js +196 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.ts +293 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.ts +90 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/index.ts +24 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.ts +15 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.ts +106 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.ts +75 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.ts +239 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.ts +1501 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.ts +211 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.ts +308 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.ts +210 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.ts +356 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.ts +524 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.ts +530 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.ts +594 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.ts +170 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/tests/unit/CLIProxyService.init.test.ts +544 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/tests/unit/CLIProxyService.test.ts +189 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/tests/unit/ResourcesProviderService.test.ts +89 -0
- package/libs/instance-factories/tools/templates/mcp/static/src/types/index.ts +110 -0
- package/libs/instance-factories/tools/templates/mcp/static/tsconfig.json +28 -0
- package/libs/instance-factories/tools/templates/vscode/static/extension.ts +1195 -0
- package/libs/instance-factories/tools/templates/vscode/static/language-configuration.json +34 -0
- package/libs/instance-factories/tools/templates/vscode/static/schemas/specverse-v3-schema.json +4279 -0
- package/libs/instance-factories/tools/templates/vscode/static/syntaxes/specverse.tmLanguage.json +138 -0
- package/libs/instance-factories/tools/templates/vscode/static/themes/README.md +74 -0
- package/libs/instance-factories/tools/templates/vscode/static/themes/complete-specverse-colors.json +122 -0
- package/libs/instance-factories/tools/templates/vscode/static/themes/specverse-basic-theme.json +65 -0
- package/libs/instance-factories/tools/templates/vscode/static/themes/specverse-complete-theme.json +123 -0
- package/libs/instance-factories/tools/templates/vscode/static/themes/specverse-theme-colors.json +64 -0
- package/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.ts +214 -0
- package/libs/instance-factories/validation/templates/zod/validation-generator.ts +46 -0
- package/libs/instance-factories/validation/zod.yaml +56 -0
- package/libs/instance-factories/views/index.d.ts +13 -0
- package/libs/instance-factories/views/index.d.ts.map +1 -0
- package/libs/instance-factories/views/index.js +18 -0
- package/libs/instance-factories/views/index.js.map +1 -0
- package/libs/instance-factories/views/index.ts +45 -0
- package/libs/instance-factories/views/react-components.yaml +129 -0
- package/libs/instance-factories/views/templates/ARCHITECTURE.md +198 -0
- package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +869 -0
- package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +953 -0
- package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +806 -0
- package/libs/instance-factories/views/templates/react/app-generator.ts +55 -0
- package/libs/instance-factories/views/templates/react/components-generator.ts +391 -0
- package/libs/instance-factories/views/templates/react/forms-generator.ts +343 -0
- package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +54 -0
- package/libs/instance-factories/views/templates/react/hooks-generator.ts +122 -0
- package/libs/instance-factories/views/templates/react/index-css-generator.ts +209 -0
- package/libs/instance-factories/views/templates/react/index-html-generator.ts +34 -0
- package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +29 -0
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +152 -0
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/react/react-component-generator.js +398 -0
- package/libs/instance-factories/views/templates/react/react-component-generator.js.map +1 -0
- package/libs/instance-factories/views/templates/react/react-component-generator.ts +533 -0
- package/libs/instance-factories/views/templates/react/router-generator.ts +197 -0
- package/libs/instance-factories/views/templates/react/router-generic-generator.ts +103 -0
- package/libs/instance-factories/views/templates/react/spec-json-generator.ts +17 -0
- package/libs/instance-factories/views/templates/react/types-generator.ts +76 -0
- package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +42 -0
- package/libs/instance-factories/views/templates/react/vite-config-generator.ts +38 -0
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +1 -0
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +474 -0
- package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +242 -0
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +77 -0
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/shared/adapter-types.js +47 -0
- package/libs/instance-factories/views/templates/shared/adapter-types.js.map +1 -0
- package/libs/instance-factories/views/templates/shared/adapter-types.ts +142 -0
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +63 -0
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +822 -0
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +1 -0
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +908 -0
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts +247 -0
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/shared/base-generator.js +363 -0
- package/libs/instance-factories/views/templates/shared/base-generator.js.map +1 -0
- package/libs/instance-factories/views/templates/shared/base-generator.ts +608 -0
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +254 -0
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/shared/component-metadata.js +602 -0
- package/libs/instance-factories/views/templates/shared/component-metadata.js.map +1 -0
- package/libs/instance-factories/views/templates/shared/component-metadata.ts +803 -0
- package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +250 -0
- package/libs/instance-factories/views/templates/shared/composite-patterns.ts +535 -0
- package/libs/instance-factories/views/templates/shared/index.ts +68 -0
- package/libs/instance-factories/views/templates/shared/pattern-validator.ts +279 -0
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +149 -0
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/shared/property-mapper.js +580 -0
- package/libs/instance-factories/views/templates/shared/property-mapper.js.map +1 -0
- package/libs/instance-factories/views/templates/shared/property-mapper.ts +700 -0
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +143 -0
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +1 -0
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js +420 -0
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +1 -0
- package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +539 -0
- package/package.json +42 -0
- package/schema/SPECVERSE-SCHEMA.json +4274 -0
|
@@ -0,0 +1,1501 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLIProxyService
|
|
3
|
+
* Dynamic CLI integration service for MCP server
|
|
4
|
+
*
|
|
5
|
+
* This service uses the shared CLI discovery utility to dynamically discover
|
|
6
|
+
* and execute CLI commands with --json output, ensuring the MCP server
|
|
7
|
+
* always stays in sync with CLI capabilities.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Runtime import resolution for development vs published package
|
|
11
|
+
async function loadSpecVerseAPI() {
|
|
12
|
+
const { createRequire } = await import('module');
|
|
13
|
+
const { fileURLToPath } = await import('url');
|
|
14
|
+
const { join, dirname } = await import('path');
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
|
|
17
|
+
// Construct import paths dynamically to avoid TypeScript compile-time resolution
|
|
18
|
+
const publishedPackage = '@' + 'specverse/lang';
|
|
19
|
+
|
|
20
|
+
// Get current file location and calculate path to dist/index.js
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
|
|
24
|
+
// Determine if we're in src/ (tests) or dist/ (runtime)
|
|
25
|
+
// Runtime: tools/specverse-mcp/dist/local/services/ -> go up 5 to root
|
|
26
|
+
// Tests: tools/specverse-mcp/src/services/ -> go up 4 to root
|
|
27
|
+
const isInDist = __dirname.includes('/dist/');
|
|
28
|
+
const levelsUp = isInDist ? 5 : 4;
|
|
29
|
+
const pathParts = Array(levelsUp).fill('..').concat(['dist', 'index.js']);
|
|
30
|
+
const localPath = join(__dirname, ...pathParts);
|
|
31
|
+
|
|
32
|
+
// In development, prefer local build over published package
|
|
33
|
+
// Use import directly - if it fails, we'll fall back to published
|
|
34
|
+
try {
|
|
35
|
+
return await import(localPath);
|
|
36
|
+
} catch (localError) {
|
|
37
|
+
// Local build doesn't exist or failed to load
|
|
38
|
+
try {
|
|
39
|
+
// Try to resolve the published package
|
|
40
|
+
require.resolve(publishedPackage);
|
|
41
|
+
return await import(publishedPackage);
|
|
42
|
+
} catch (publishedError) {
|
|
43
|
+
// Neither worked - throw original error
|
|
44
|
+
throw new Error(`Failed to load SpecVerse API: local (${localError.message}) and published (${publishedError.message})`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// This will be initialized in the class constructor
|
|
50
|
+
let specverseAPI: any = null;
|
|
51
|
+
|
|
52
|
+
type CLICapabilities = any;
|
|
53
|
+
type CLICommand = any;
|
|
54
|
+
type GroupedCommand = any;
|
|
55
|
+
import { writeFileSync, unlinkSync, mkdtempSync } from 'fs';
|
|
56
|
+
import { join } from 'path';
|
|
57
|
+
import { tmpdir } from 'os';
|
|
58
|
+
import type { MCPToolResult } from '../types/index.js';
|
|
59
|
+
|
|
60
|
+
export interface MCPTool {
|
|
61
|
+
name: string;
|
|
62
|
+
description: string;
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: 'object';
|
|
65
|
+
properties: Record<string, any>;
|
|
66
|
+
required?: string[];
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class CLIProxyService {
|
|
71
|
+
private capabilities: CLICapabilities | null = null;
|
|
72
|
+
private cliPath: string | null = null;
|
|
73
|
+
private lastDiscovery: number = 0;
|
|
74
|
+
private readonly DISCOVERY_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
75
|
+
private initialized = false;
|
|
76
|
+
|
|
77
|
+
constructor(private workingDirectory?: string) {
|
|
78
|
+
// API will be loaded lazily in init()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initialize the API - called once before first use
|
|
83
|
+
*/
|
|
84
|
+
private async init() {
|
|
85
|
+
if (this.initialized) return;
|
|
86
|
+
|
|
87
|
+
specverseAPI = await loadSpecVerseAPI();
|
|
88
|
+
this.cliPath = specverseAPI.getCliPath(this.workingDirectory);
|
|
89
|
+
|
|
90
|
+
if (!this.cliPath && process.env.MCP_DEBUG) {
|
|
91
|
+
// Only warn to stderr in debug mode - stdout must be clean for MCP protocol
|
|
92
|
+
console.error('⚠️ SpecVerse CLI not found. CLI proxy functionality will be limited.');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.initialized = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Discover CLI capabilities (cached for performance)
|
|
100
|
+
*/
|
|
101
|
+
async discoverCapabilities(): Promise<CLICapabilities> {
|
|
102
|
+
await this.init();
|
|
103
|
+
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
|
|
106
|
+
// Return cached capabilities if still fresh
|
|
107
|
+
if (this.capabilities && (now - this.lastDiscovery) < this.DISCOVERY_CACHE_TTL) {
|
|
108
|
+
return this.capabilities;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Discover fresh capabilities
|
|
112
|
+
try {
|
|
113
|
+
this.capabilities = specverseAPI.getAllCliCapabilities(this.cliPath || undefined);
|
|
114
|
+
this.lastDiscovery = now;
|
|
115
|
+
|
|
116
|
+
// Don't log to console in MCP mode - it breaks the JSON protocol
|
|
117
|
+
// Only log if explicitly in debug mode
|
|
118
|
+
if (process.env.MCP_DEBUG) {
|
|
119
|
+
console.error(`🔍 Discovered ${this.capabilities.coreCommands.length} core commands, ` +
|
|
120
|
+
`${this.capabilities.groupedCommands.length} grouped commands, ` +
|
|
121
|
+
`${this.capabilities.aiCommands.length} AI commands`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return this.capabilities;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// Log errors to stderr, not stdout
|
|
127
|
+
if (process.env.MCP_DEBUG) {
|
|
128
|
+
console.error('❌ Failed to discover CLI capabilities:', error);
|
|
129
|
+
}
|
|
130
|
+
// Return empty capabilities as fallback
|
|
131
|
+
return {
|
|
132
|
+
coreCommands: [],
|
|
133
|
+
groupedCommands: [],
|
|
134
|
+
aiCommands: []
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate MCP tool definitions from CLI capabilities
|
|
141
|
+
*/
|
|
142
|
+
async generateMCPTools(): Promise<MCPTool[]> {
|
|
143
|
+
const capabilities = await this.discoverCapabilities();
|
|
144
|
+
const tools: MCPTool[] = [];
|
|
145
|
+
|
|
146
|
+
// Add MCP-specific debug tool first
|
|
147
|
+
tools.push({
|
|
148
|
+
name: 'specverse_mcp_debug',
|
|
149
|
+
description: 'Show MCP server diagnostic information including SpecVerse installation details, path resolution, and environment analysis',
|
|
150
|
+
inputSchema: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
properties: {
|
|
153
|
+
verbose: {
|
|
154
|
+
type: 'boolean',
|
|
155
|
+
description: 'Show detailed path resolution information'
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
required: []
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Core commands (validate, infer, init)
|
|
163
|
+
for (const cmd of capabilities.coreCommands) {
|
|
164
|
+
tools.push(this.createMCPToolFromCommand(cmd));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Grouped commands (gen yaml, dev format, test cycle, etc.)
|
|
168
|
+
for (const cmd of capabilities.groupedCommands) {
|
|
169
|
+
tools.push(this.createMCPToolFromGroupedCommand(cmd));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// AI commands are handled specially since they have complex arguments
|
|
173
|
+
for (const cmd of capabilities.aiCommands) {
|
|
174
|
+
tools.push(...this.createAIMCPTools());
|
|
175
|
+
break; // Only need to process AI commands once
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Add content-based tools for direct LLM interaction
|
|
179
|
+
tools.push(...this.createContentBasedTools());
|
|
180
|
+
|
|
181
|
+
return tools;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Execute a CLI command via the proxy
|
|
186
|
+
*/
|
|
187
|
+
async executeCommand(toolName: string, args: Record<string, any>): Promise<MCPToolResult> {
|
|
188
|
+
try {
|
|
189
|
+
// Initialize if not already done
|
|
190
|
+
await this.init();
|
|
191
|
+
|
|
192
|
+
// Handle MCP-specific debug tool
|
|
193
|
+
if (toolName === 'specverse_mcp_debug') {
|
|
194
|
+
return await this.executeMCPDebugTool(args.verbose || false);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!this.cliPath) {
|
|
198
|
+
return {
|
|
199
|
+
content: [{
|
|
200
|
+
type: 'text',
|
|
201
|
+
text: 'SpecVerse CLI not found. Please ensure SpecVerse is installed and accessible.'
|
|
202
|
+
}],
|
|
203
|
+
isError: true
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Handle content-based tools by creating temporary files
|
|
208
|
+
if (toolName.endsWith('_content')) {
|
|
209
|
+
return await this.handleContentBasedCommand(toolName, args);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Map MCP tool name back to CLI command
|
|
213
|
+
const baseCommand = this.mapToolNameToCommand(toolName);
|
|
214
|
+
|
|
215
|
+
// Handle commands with positional arguments
|
|
216
|
+
let command = baseCommand;
|
|
217
|
+
let commandArgs = { ...args };
|
|
218
|
+
|
|
219
|
+
// Handle AI commands - append operation as positional argument
|
|
220
|
+
if (baseCommand.startsWith('ai ') && args.operation) {
|
|
221
|
+
command = `${baseCommand} ${args.operation}`;
|
|
222
|
+
const { operation, ...remainingArgs } = args;
|
|
223
|
+
commandArgs = remainingArgs;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Handle lib commands with positional arguments
|
|
227
|
+
else if (baseCommand.startsWith('lib ')) {
|
|
228
|
+
if (baseCommand === 'lib search' && args.query) {
|
|
229
|
+
command = `${baseCommand} "${args.query}"`;
|
|
230
|
+
const { query, ...remainingArgs } = args;
|
|
231
|
+
commandArgs = remainingArgs;
|
|
232
|
+
} else if (baseCommand === 'lib info' && args.name) {
|
|
233
|
+
command = `${baseCommand} "${args.name}"`;
|
|
234
|
+
const { name, ...remainingArgs } = args;
|
|
235
|
+
commandArgs = remainingArgs;
|
|
236
|
+
}
|
|
237
|
+
// lib list, lib tags, lib cache don't have positional arguments
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Handle init command - append name as positional argument
|
|
241
|
+
else if (baseCommand === 'init') {
|
|
242
|
+
// Init command special handling
|
|
243
|
+
console.error(`MCP Debug: Received args.name: "${args.name}"`);
|
|
244
|
+
console.error(`MCP Debug: Full args object:`, JSON.stringify(args, null, 2));
|
|
245
|
+
if (args.name) {
|
|
246
|
+
// Pass the full path to executeInitCommand in args, use basename for CLI
|
|
247
|
+
const fullPath = args.name;
|
|
248
|
+
const projectName = args.name.includes('/') ? args.name.split('/').pop() : args.name;
|
|
249
|
+
command = `${baseCommand} "${projectName}"`;
|
|
250
|
+
const { name, ...remainingArgs } = args;
|
|
251
|
+
// Preserve the full path in commandArgs for proper directory handling
|
|
252
|
+
commandArgs = { ...remainingArgs, fullPath };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Init doesn't support --json, so we need to handle its output specially
|
|
256
|
+
try {
|
|
257
|
+
const result = await this.executeInitCommand(command, commandArgs);
|
|
258
|
+
return {
|
|
259
|
+
content: [{
|
|
260
|
+
type: 'text',
|
|
261
|
+
text: JSON.stringify(result, null, 2)
|
|
262
|
+
}]
|
|
263
|
+
};
|
|
264
|
+
} catch (error) {
|
|
265
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
266
|
+
return {
|
|
267
|
+
content: [{
|
|
268
|
+
type: 'text',
|
|
269
|
+
text: JSON.stringify({
|
|
270
|
+
status: 'error',
|
|
271
|
+
message: 'Init command failed',
|
|
272
|
+
details: errorMessage,
|
|
273
|
+
timestamp: new Date().toISOString()
|
|
274
|
+
}, null, 2)
|
|
275
|
+
}],
|
|
276
|
+
isError: true
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Handle other commands with positional file arguments
|
|
282
|
+
else if (args.file) {
|
|
283
|
+
command = `${baseCommand} "${args.file}"`;
|
|
284
|
+
const { file, ...remainingArgs } = args;
|
|
285
|
+
commandArgs = remainingArgs;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Execute with JSON output
|
|
289
|
+
const result = await specverseAPI.executeCliCommand(command, commandArgs, {
|
|
290
|
+
cliPath: this.cliPath,
|
|
291
|
+
timeout: 60000 // 1 minute timeout
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
content: [{
|
|
296
|
+
type: 'text',
|
|
297
|
+
text: JSON.stringify(result, null, 2)
|
|
298
|
+
}]
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
} catch (error) {
|
|
302
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
303
|
+
|
|
304
|
+
// Try to parse error as JSON (CLI returns structured errors)
|
|
305
|
+
try {
|
|
306
|
+
const parsedError = JSON.parse(errorMessage);
|
|
307
|
+
return {
|
|
308
|
+
content: [{
|
|
309
|
+
type: 'text',
|
|
310
|
+
text: JSON.stringify(parsedError, null, 2)
|
|
311
|
+
}],
|
|
312
|
+
isError: true
|
|
313
|
+
};
|
|
314
|
+
} catch {
|
|
315
|
+
return {
|
|
316
|
+
content: [{
|
|
317
|
+
type: 'text',
|
|
318
|
+
text: JSON.stringify({
|
|
319
|
+
status: 'error',
|
|
320
|
+
message: 'CLI command execution failed',
|
|
321
|
+
details: errorMessage,
|
|
322
|
+
timestamp: new Date().toISOString()
|
|
323
|
+
}, null, 2)
|
|
324
|
+
}],
|
|
325
|
+
isError: true
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Create MCP tool definition from core CLI command
|
|
333
|
+
*/
|
|
334
|
+
private createMCPToolFromCommand(cmd: CLICommand): MCPTool {
|
|
335
|
+
const properties: Record<string, any> = {};
|
|
336
|
+
const required: string[] = [];
|
|
337
|
+
|
|
338
|
+
// Parse arguments and options
|
|
339
|
+
if (cmd.args) {
|
|
340
|
+
if (cmd.args.includes('<file>')) {
|
|
341
|
+
properties.file = {
|
|
342
|
+
type: 'string',
|
|
343
|
+
description: 'SpecVerse specification file path'
|
|
344
|
+
};
|
|
345
|
+
required.push('file');
|
|
346
|
+
}
|
|
347
|
+
if (cmd.args.includes('[name]')) {
|
|
348
|
+
properties.name = {
|
|
349
|
+
type: 'string',
|
|
350
|
+
description: 'Project name (optional)'
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Special handling for init command based on known CLI structure
|
|
356
|
+
if (cmd.command === 'init') {
|
|
357
|
+
properties.name = {
|
|
358
|
+
type: 'string',
|
|
359
|
+
description: 'Project name'
|
|
360
|
+
};
|
|
361
|
+
// Add template option that init supports
|
|
362
|
+
properties.template = {
|
|
363
|
+
type: 'string',
|
|
364
|
+
description: 'Template to use (default: "default")'
|
|
365
|
+
};
|
|
366
|
+
// Add MCP-specific flags
|
|
367
|
+
properties.zip = {
|
|
368
|
+
type: 'boolean',
|
|
369
|
+
description: 'Return project as ZIP file'
|
|
370
|
+
};
|
|
371
|
+
properties.json = {
|
|
372
|
+
type: 'boolean',
|
|
373
|
+
description: 'Return project as JSON file structure'
|
|
374
|
+
};
|
|
375
|
+
// Don't add verbose for init - it doesn't support it
|
|
376
|
+
} else if (cmd.command === 'validate' || cmd.command === 'infer') {
|
|
377
|
+
// These commands support verbose
|
|
378
|
+
properties.verbose = {
|
|
379
|
+
type: 'boolean',
|
|
380
|
+
description: 'Show detailed output'
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Add zip parameter for file-generating commands
|
|
385
|
+
if (this.isFileGeneratingCommand(cmd.command)) {
|
|
386
|
+
properties.zip = {
|
|
387
|
+
type: 'boolean',
|
|
388
|
+
description: 'Return files as ZIP package instead of creating locally (useful for remote servers or consistent delivery)'
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
name: `specverse_${cmd.command}`,
|
|
394
|
+
description: cmd.description,
|
|
395
|
+
inputSchema: {
|
|
396
|
+
type: 'object',
|
|
397
|
+
properties,
|
|
398
|
+
required
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Check if a command generates files that could benefit from ZIP packaging
|
|
405
|
+
*/
|
|
406
|
+
private isFileGeneratingCommand(command: string): boolean {
|
|
407
|
+
return ['init'].includes(command);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Check if a grouped command generates files that could benefit from ZIP packaging
|
|
412
|
+
*/
|
|
413
|
+
private isFileGeneratingGroupedCommand(cmd: any): boolean {
|
|
414
|
+
// All gen commands generate files
|
|
415
|
+
if (cmd.group === 'gen') {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
// Other commands that generate files can be added here
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Create MCP tool definition from grouped command
|
|
424
|
+
*/
|
|
425
|
+
private createMCPToolFromGroupedCommand(cmd: GroupedCommand): MCPTool {
|
|
426
|
+
const properties: Record<string, any> = {};
|
|
427
|
+
const required: string[] = [];
|
|
428
|
+
|
|
429
|
+
// Most grouped commands need a file
|
|
430
|
+
if (cmd.args?.includes('<file>')) {
|
|
431
|
+
properties.file = {
|
|
432
|
+
type: 'string',
|
|
433
|
+
description: 'SpecVerse specification file path'
|
|
434
|
+
};
|
|
435
|
+
required.push('file');
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Add group-specific options
|
|
439
|
+
if (cmd.group === 'gen') {
|
|
440
|
+
properties.output = {
|
|
441
|
+
type: 'string',
|
|
442
|
+
description: 'Output file or directory path'
|
|
443
|
+
};
|
|
444
|
+
} else if (cmd.group === 'dev' && cmd.subcommand === 'watch') {
|
|
445
|
+
properties.directory = {
|
|
446
|
+
type: 'string',
|
|
447
|
+
description: 'Directory to watch for changes'
|
|
448
|
+
};
|
|
449
|
+
} else if (cmd.group === 'lib') {
|
|
450
|
+
// Add lib-specific options based on subcommand
|
|
451
|
+
if (cmd.subcommand === 'search') {
|
|
452
|
+
if (cmd.args?.includes('[query]')) {
|
|
453
|
+
properties.query = {
|
|
454
|
+
type: 'string',
|
|
455
|
+
description: 'Search term (name, description, tags)'
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
properties.type = {
|
|
459
|
+
type: 'string',
|
|
460
|
+
enum: ['component', 'deployment', 'manifest'],
|
|
461
|
+
description: 'Filter by type'
|
|
462
|
+
};
|
|
463
|
+
properties.category = {
|
|
464
|
+
type: 'string',
|
|
465
|
+
description: 'Filter by category'
|
|
466
|
+
};
|
|
467
|
+
properties.tags = {
|
|
468
|
+
type: 'string',
|
|
469
|
+
description: 'Filter by tags (comma-separated: auth,jwt,session)'
|
|
470
|
+
};
|
|
471
|
+
properties.limit = {
|
|
472
|
+
type: 'number',
|
|
473
|
+
description: 'Results limit (default: 20)'
|
|
474
|
+
};
|
|
475
|
+
properties.format = {
|
|
476
|
+
type: 'string',
|
|
477
|
+
enum: ['table', 'json'],
|
|
478
|
+
description: 'Output format (default: table)'
|
|
479
|
+
};
|
|
480
|
+
} else if (cmd.subcommand === 'info') {
|
|
481
|
+
if (cmd.args?.includes('<name>')) {
|
|
482
|
+
properties.name = {
|
|
483
|
+
type: 'string',
|
|
484
|
+
description: 'Library name'
|
|
485
|
+
};
|
|
486
|
+
required.push('name');
|
|
487
|
+
}
|
|
488
|
+
properties.version = {
|
|
489
|
+
type: 'string',
|
|
490
|
+
description: 'Specific version (default: latest)'
|
|
491
|
+
};
|
|
492
|
+
properties.format = {
|
|
493
|
+
type: 'string',
|
|
494
|
+
enum: ['table', 'json'],
|
|
495
|
+
description: 'Output format (default: table)'
|
|
496
|
+
};
|
|
497
|
+
properties.content = {
|
|
498
|
+
type: 'boolean',
|
|
499
|
+
description: 'Show library content'
|
|
500
|
+
};
|
|
501
|
+
} else if (cmd.subcommand === 'list') {
|
|
502
|
+
properties.cached = {
|
|
503
|
+
type: 'boolean',
|
|
504
|
+
description: 'Show locally cached libraries'
|
|
505
|
+
};
|
|
506
|
+
properties.used = {
|
|
507
|
+
type: 'boolean',
|
|
508
|
+
description: 'Show libraries used in current project'
|
|
509
|
+
};
|
|
510
|
+
properties.format = {
|
|
511
|
+
type: 'string',
|
|
512
|
+
enum: ['table', 'json'],
|
|
513
|
+
description: 'Output format (default: table)'
|
|
514
|
+
};
|
|
515
|
+
} else if (cmd.subcommand === 'tags') {
|
|
516
|
+
properties.format = {
|
|
517
|
+
type: 'string',
|
|
518
|
+
enum: ['table', 'json'],
|
|
519
|
+
description: 'Output format (default: table)'
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Add zip parameter for file-generating grouped commands
|
|
525
|
+
if (this.isFileGeneratingGroupedCommand(cmd)) {
|
|
526
|
+
properties.zip = {
|
|
527
|
+
type: 'boolean',
|
|
528
|
+
description: 'Return files as ZIP package instead of creating locally (useful for remote servers or consistent delivery)'
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return {
|
|
533
|
+
name: `specverse_${cmd.command.replace(/\s+/g, '_')}`,
|
|
534
|
+
description: cmd.description,
|
|
535
|
+
inputSchema: {
|
|
536
|
+
type: 'object',
|
|
537
|
+
properties,
|
|
538
|
+
required
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Create MCP tools for AI commands
|
|
545
|
+
*/
|
|
546
|
+
private createAIMCPTools(): MCPTool[] {
|
|
547
|
+
return [
|
|
548
|
+
{
|
|
549
|
+
name: 'specverse_ai_template',
|
|
550
|
+
description: 'Get AI prompt templates for SpecVerse operations',
|
|
551
|
+
inputSchema: {
|
|
552
|
+
type: 'object',
|
|
553
|
+
properties: {
|
|
554
|
+
operation: {
|
|
555
|
+
type: 'string',
|
|
556
|
+
enum: ['analyse', 'create', 'materialise', 'realize'],
|
|
557
|
+
description: 'AI operation type'
|
|
558
|
+
},
|
|
559
|
+
pver: {
|
|
560
|
+
type: 'string',
|
|
561
|
+
description: 'Prompt version (v1|v2|v3|v4|v5|v6|v7) (default: v1)'
|
|
562
|
+
},
|
|
563
|
+
output: {
|
|
564
|
+
type: 'string',
|
|
565
|
+
description: 'Output file path'
|
|
566
|
+
},
|
|
567
|
+
copy: {
|
|
568
|
+
type: 'boolean',
|
|
569
|
+
description: 'Copy result to clipboard'
|
|
570
|
+
}
|
|
571
|
+
},
|
|
572
|
+
required: ['operation']
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
name: 'specverse_ai_fill',
|
|
577
|
+
description: 'Fill AI prompt templates with requirements',
|
|
578
|
+
inputSchema: {
|
|
579
|
+
type: 'object',
|
|
580
|
+
properties: {
|
|
581
|
+
operation: {
|
|
582
|
+
type: 'string',
|
|
583
|
+
enum: ['analyse', 'create', 'materialise', 'realize'],
|
|
584
|
+
description: 'AI operation type'
|
|
585
|
+
},
|
|
586
|
+
requirements: {
|
|
587
|
+
type: 'string',
|
|
588
|
+
description: 'Project requirements'
|
|
589
|
+
},
|
|
590
|
+
scale: {
|
|
591
|
+
type: 'string',
|
|
592
|
+
enum: ['personal', 'business', 'enterprise'],
|
|
593
|
+
description: 'Project scale (default: business)'
|
|
594
|
+
},
|
|
595
|
+
framework: {
|
|
596
|
+
type: 'string',
|
|
597
|
+
description: 'Framework preference'
|
|
598
|
+
},
|
|
599
|
+
domain: {
|
|
600
|
+
type: 'string',
|
|
601
|
+
description: 'Project domain'
|
|
602
|
+
},
|
|
603
|
+
compliance: {
|
|
604
|
+
type: 'string',
|
|
605
|
+
description: 'Compliance requirements (comma-separated)'
|
|
606
|
+
},
|
|
607
|
+
tech: {
|
|
608
|
+
type: 'string',
|
|
609
|
+
description: 'Technology preferences (comma-separated)'
|
|
610
|
+
},
|
|
611
|
+
pver: {
|
|
612
|
+
type: 'string',
|
|
613
|
+
description: 'Prompt version (v1|v2|v3|v4|v5|v6|v7) (default: v1)'
|
|
614
|
+
},
|
|
615
|
+
output: {
|
|
616
|
+
type: 'string',
|
|
617
|
+
description: 'Output file path'
|
|
618
|
+
},
|
|
619
|
+
copy: {
|
|
620
|
+
type: 'boolean',
|
|
621
|
+
description: 'Copy result to clipboard'
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
required: ['operation', 'requirements']
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
name: 'specverse_ai_suggest',
|
|
629
|
+
description: 'Get AI library suggestions for project requirements',
|
|
630
|
+
inputSchema: {
|
|
631
|
+
type: 'object',
|
|
632
|
+
properties: {
|
|
633
|
+
requirements: {
|
|
634
|
+
type: 'string',
|
|
635
|
+
description: 'Project requirements'
|
|
636
|
+
},
|
|
637
|
+
domain: {
|
|
638
|
+
type: 'string',
|
|
639
|
+
description: 'Project domain'
|
|
640
|
+
},
|
|
641
|
+
scale: {
|
|
642
|
+
type: 'string',
|
|
643
|
+
enum: ['personal', 'business', 'enterprise'],
|
|
644
|
+
default: 'business',
|
|
645
|
+
description: 'Project scale'
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
required: ['requirements']
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: 'specverse_ai_enhance',
|
|
653
|
+
description: 'Get enhanced AI prompts with library context (BEST)',
|
|
654
|
+
inputSchema: {
|
|
655
|
+
type: 'object',
|
|
656
|
+
properties: {
|
|
657
|
+
operation: {
|
|
658
|
+
type: 'string',
|
|
659
|
+
enum: ['analyse', 'create', 'materialise', 'realize'],
|
|
660
|
+
description: 'AI operation type'
|
|
661
|
+
},
|
|
662
|
+
requirements: {
|
|
663
|
+
type: 'string',
|
|
664
|
+
description: 'Project requirements'
|
|
665
|
+
},
|
|
666
|
+
scale: {
|
|
667
|
+
type: 'string',
|
|
668
|
+
enum: ['personal', 'business', 'enterprise'],
|
|
669
|
+
description: 'Project scale (default: business)'
|
|
670
|
+
},
|
|
671
|
+
framework: {
|
|
672
|
+
type: 'string',
|
|
673
|
+
description: 'Framework preference'
|
|
674
|
+
},
|
|
675
|
+
domain: {
|
|
676
|
+
type: 'string',
|
|
677
|
+
description: 'Project domain'
|
|
678
|
+
},
|
|
679
|
+
compliance: {
|
|
680
|
+
type: 'string',
|
|
681
|
+
description: 'Compliance requirements (comma-separated)'
|
|
682
|
+
},
|
|
683
|
+
tech: {
|
|
684
|
+
type: 'string',
|
|
685
|
+
description: 'Technology preferences (comma-separated)'
|
|
686
|
+
},
|
|
687
|
+
pver: {
|
|
688
|
+
type: 'string',
|
|
689
|
+
description: 'Prompt version (v1|v2|v3|v4|v5|v6|v7) (default: v1)'
|
|
690
|
+
},
|
|
691
|
+
output: {
|
|
692
|
+
type: 'string',
|
|
693
|
+
description: 'Output file path'
|
|
694
|
+
},
|
|
695
|
+
copy: {
|
|
696
|
+
type: 'boolean',
|
|
697
|
+
description: 'Copy result to clipboard'
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
required: ['operation', 'requirements']
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
];
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Create content-based tools for direct LLM interaction
|
|
708
|
+
*/
|
|
709
|
+
private createContentBasedTools(): MCPTool[] {
|
|
710
|
+
return [
|
|
711
|
+
{
|
|
712
|
+
name: 'specverse_validate_content',
|
|
713
|
+
description: 'Validate SpecVerse specification content directly (no file required)',
|
|
714
|
+
inputSchema: {
|
|
715
|
+
type: 'object',
|
|
716
|
+
properties: {
|
|
717
|
+
content: {
|
|
718
|
+
type: 'string',
|
|
719
|
+
description: 'SpecVerse specification content (.specly format)'
|
|
720
|
+
},
|
|
721
|
+
filename: {
|
|
722
|
+
type: 'string',
|
|
723
|
+
description: 'Optional filename for error reporting (default: temp.specly)'
|
|
724
|
+
},
|
|
725
|
+
verbose: {
|
|
726
|
+
type: 'boolean',
|
|
727
|
+
description: 'Show detailed validation results'
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
required: ['content']
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: 'specverse_infer_content',
|
|
735
|
+
description: 'Generate complete specification from content using AI inference',
|
|
736
|
+
inputSchema: {
|
|
737
|
+
type: 'object',
|
|
738
|
+
properties: {
|
|
739
|
+
content: {
|
|
740
|
+
type: 'string',
|
|
741
|
+
description: 'Minimal SpecVerse specification content (.specly format)'
|
|
742
|
+
},
|
|
743
|
+
filename: {
|
|
744
|
+
type: 'string',
|
|
745
|
+
description: 'Optional filename (default: temp.specly)'
|
|
746
|
+
},
|
|
747
|
+
controllers: {
|
|
748
|
+
type: 'boolean',
|
|
749
|
+
description: 'Generate controllers (default: true)'
|
|
750
|
+
},
|
|
751
|
+
services: {
|
|
752
|
+
type: 'boolean',
|
|
753
|
+
description: 'Generate services (default: true)'
|
|
754
|
+
},
|
|
755
|
+
events: {
|
|
756
|
+
type: 'boolean',
|
|
757
|
+
description: 'Generate events (default: true)'
|
|
758
|
+
},
|
|
759
|
+
views: {
|
|
760
|
+
type: 'boolean',
|
|
761
|
+
description: 'Generate views (default: true)'
|
|
762
|
+
},
|
|
763
|
+
deployment: {
|
|
764
|
+
type: 'boolean',
|
|
765
|
+
description: 'Generate deployment specification (default: false)'
|
|
766
|
+
},
|
|
767
|
+
environment: {
|
|
768
|
+
type: 'string',
|
|
769
|
+
enum: ['development', 'staging', 'production'],
|
|
770
|
+
description: 'Target environment for deployment'
|
|
771
|
+
},
|
|
772
|
+
verbose: {
|
|
773
|
+
type: 'boolean',
|
|
774
|
+
description: 'Show detailed inference process'
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
required: ['content']
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
name: 'specverse_gen_yaml_content',
|
|
782
|
+
description: 'Generate YAML from SpecVerse specification content',
|
|
783
|
+
inputSchema: {
|
|
784
|
+
type: 'object',
|
|
785
|
+
properties: {
|
|
786
|
+
content: {
|
|
787
|
+
type: 'string',
|
|
788
|
+
description: 'SpecVerse specification content (.specly format)'
|
|
789
|
+
},
|
|
790
|
+
filename: {
|
|
791
|
+
type: 'string',
|
|
792
|
+
description: 'Optional filename (default: temp.specly)'
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
required: ['content']
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
];
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Handle content-based commands by creating temporary files
|
|
803
|
+
*/
|
|
804
|
+
private async handleContentBasedCommand(toolName: string, args: Record<string, any>): Promise<MCPToolResult> {
|
|
805
|
+
const { content, filename = 'temp.specly', ...otherArgs } = args;
|
|
806
|
+
|
|
807
|
+
if (!content) {
|
|
808
|
+
return {
|
|
809
|
+
content: [{
|
|
810
|
+
type: 'text',
|
|
811
|
+
text: JSON.stringify({
|
|
812
|
+
status: 'error',
|
|
813
|
+
message: 'Content is required for content-based commands',
|
|
814
|
+
timestamp: new Date().toISOString()
|
|
815
|
+
}, null, 2)
|
|
816
|
+
}],
|
|
817
|
+
isError: true
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Create temporary directory and file
|
|
822
|
+
let tempDir: string;
|
|
823
|
+
let tempFile: string;
|
|
824
|
+
|
|
825
|
+
try {
|
|
826
|
+
tempDir = mkdtempSync(join(tmpdir(), 'specverse-mcp-'));
|
|
827
|
+
tempFile = join(tempDir, filename);
|
|
828
|
+
writeFileSync(tempFile, content, 'utf8');
|
|
829
|
+
|
|
830
|
+
// Map content tool name to CLI command
|
|
831
|
+
const cliCommand = this.mapContentToolToCommand(toolName);
|
|
832
|
+
|
|
833
|
+
// Execute CLI command with temporary file
|
|
834
|
+
// Note: Most commands expect file as positional argument, not --file flag
|
|
835
|
+
const result = await specverseAPI.executeCliCommand(`${cliCommand} "${tempFile}"`, otherArgs, {
|
|
836
|
+
cliPath: this.cliPath,
|
|
837
|
+
timeout: 60000
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
return {
|
|
841
|
+
content: [{
|
|
842
|
+
type: 'text',
|
|
843
|
+
text: JSON.stringify(result, null, 2)
|
|
844
|
+
}]
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
} catch (error) {
|
|
848
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
const parsedError = JSON.parse(errorMessage);
|
|
852
|
+
return {
|
|
853
|
+
content: [{
|
|
854
|
+
type: 'text',
|
|
855
|
+
text: JSON.stringify(parsedError, null, 2)
|
|
856
|
+
}],
|
|
857
|
+
isError: true
|
|
858
|
+
};
|
|
859
|
+
} catch {
|
|
860
|
+
return {
|
|
861
|
+
content: [{
|
|
862
|
+
type: 'text',
|
|
863
|
+
text: JSON.stringify({
|
|
864
|
+
status: 'error',
|
|
865
|
+
message: 'Content-based command execution failed',
|
|
866
|
+
details: errorMessage,
|
|
867
|
+
timestamp: new Date().toISOString()
|
|
868
|
+
}, null, 2)
|
|
869
|
+
}],
|
|
870
|
+
isError: true
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
} finally {
|
|
874
|
+
// Clean up temporary file
|
|
875
|
+
try {
|
|
876
|
+
if (tempFile) unlinkSync(tempFile);
|
|
877
|
+
} catch {
|
|
878
|
+
// Ignore cleanup errors
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Map content-based tool names to CLI commands
|
|
885
|
+
*/
|
|
886
|
+
private mapContentToolToCommand(toolName: string): string {
|
|
887
|
+
const mapping: Record<string, string> = {
|
|
888
|
+
'specverse_validate_content': 'validate',
|
|
889
|
+
'specverse_infer_content': 'infer',
|
|
890
|
+
'specverse_gen_yaml_content': 'gen yaml'
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
return mapping[toolName] || toolName.replace(/^specverse_/, '').replace(/_content$/, '').replace(/_/g, ' ');
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Check if we're running in a remote environment where local file creation won't work
|
|
898
|
+
*/
|
|
899
|
+
private async isRemoteEnvironment(): Promise<boolean> {
|
|
900
|
+
// Allow explicit override to force local mode
|
|
901
|
+
if (process.env.MCP_FORCE_LOCAL === 'true') {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Check for common remote environment indicators
|
|
906
|
+
return (
|
|
907
|
+
// Vercel/Netlify/similar serverless
|
|
908
|
+
process.env.VERCEL === '1' ||
|
|
909
|
+
process.env.NETLIFY === 'true' ||
|
|
910
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined ||
|
|
911
|
+
|
|
912
|
+
// Web deployment mode indicators
|
|
913
|
+
process.env.NODE_ENV === 'production' && (
|
|
914
|
+
process.env.PORT !== undefined ||
|
|
915
|
+
process.env.HOST !== undefined
|
|
916
|
+
) ||
|
|
917
|
+
|
|
918
|
+
// Environment explicitly marked as remote
|
|
919
|
+
process.env.MCP_REMOTE === 'true' ||
|
|
920
|
+
|
|
921
|
+
// Detect if we can't write to current directory (more reliable than just checking root)
|
|
922
|
+
await this.isDirectoryReadOnly()
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Check if we should use remote environment detection specifically for root directory
|
|
928
|
+
* Only triggers if running from root AND no full paths are provided
|
|
929
|
+
*/
|
|
930
|
+
private isProblematicRootEnvironment(args: Record<string, any>): boolean {
|
|
931
|
+
if (process.cwd() !== '/') {
|
|
932
|
+
return false; // Not running from root, so no problem
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// If user provided full paths, it should work fine from root
|
|
936
|
+
const hasFullPaths = (
|
|
937
|
+
(args.name && args.name.startsWith('/')) || // /full/path/project
|
|
938
|
+
(args.file && args.file.startsWith('/')) || // /full/path/file.specly
|
|
939
|
+
(args.output && args.output.startsWith('/')) || // /full/path/output
|
|
940
|
+
(args.directory && args.directory.startsWith('/')) // /full/path/directory
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
if (hasFullPaths) {
|
|
944
|
+
return false; // Full paths work fine from root
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Only problematic if we have relative names that would create files in root
|
|
948
|
+
const hasRelativeName = args.name && !args.name.startsWith('/');
|
|
949
|
+
|
|
950
|
+
// Return true only for the actually problematic case
|
|
951
|
+
return hasRelativeName;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Check if current directory is read-only
|
|
956
|
+
*/
|
|
957
|
+
private async isDirectoryReadOnly(): Promise<boolean> {
|
|
958
|
+
try {
|
|
959
|
+
const { writeFileSync, unlinkSync } = await import('fs');
|
|
960
|
+
const testFile = '.mcp-write-test-' + Date.now();
|
|
961
|
+
writeFileSync(testFile, 'test');
|
|
962
|
+
unlinkSync(testFile);
|
|
963
|
+
return false;
|
|
964
|
+
} catch {
|
|
965
|
+
return true;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Read project files into structured format for direct delivery
|
|
971
|
+
*/
|
|
972
|
+
private async readProjectFilesStructured(projectPath: string): Promise<{files: any[], summary: any}> {
|
|
973
|
+
const { readdir, readFile, stat } = await import('fs/promises');
|
|
974
|
+
const { join, relative, extname } = await import('path');
|
|
975
|
+
|
|
976
|
+
const files: any[] = [];
|
|
977
|
+
let totalSize = 0;
|
|
978
|
+
const directories: string[] = [];
|
|
979
|
+
|
|
980
|
+
const readDirectory = async (dirPath: string) => {
|
|
981
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
982
|
+
|
|
983
|
+
for (const entry of entries) {
|
|
984
|
+
const fullPath = join(dirPath, entry.name);
|
|
985
|
+
const relativePath = relative(projectPath, fullPath);
|
|
986
|
+
|
|
987
|
+
if (entry.isDirectory()) {
|
|
988
|
+
directories.push(relativePath + '/');
|
|
989
|
+
await readDirectory(fullPath);
|
|
990
|
+
} else if (entry.isFile()) {
|
|
991
|
+
try {
|
|
992
|
+
const content = await readFile(fullPath, 'utf8');
|
|
993
|
+
const stats = await stat(fullPath);
|
|
994
|
+
const extension = extname(entry.name).toLowerCase();
|
|
995
|
+
|
|
996
|
+
// Determine file type from extension
|
|
997
|
+
let fileType = 'text';
|
|
998
|
+
if (extension === '.js' || extension === '.ts') fileType = 'javascript';
|
|
999
|
+
else if (extension === '.py') fileType = 'python';
|
|
1000
|
+
else if (extension === '.md') fileType = 'markdown';
|
|
1001
|
+
else if (extension === '.json') fileType = 'json';
|
|
1002
|
+
else if (extension === '.yaml' || extension === '.yml') fileType = 'yaml';
|
|
1003
|
+
else if (extension === '.specly') fileType = 'specverse';
|
|
1004
|
+
else if (extension === '.sh') fileType = 'shell';
|
|
1005
|
+
else if (extension === '.html') fileType = 'html';
|
|
1006
|
+
else if (extension === '.css') fileType = 'css';
|
|
1007
|
+
|
|
1008
|
+
files.push({
|
|
1009
|
+
path: relativePath,
|
|
1010
|
+
content: content,
|
|
1011
|
+
type: fileType,
|
|
1012
|
+
size: stats.size,
|
|
1013
|
+
encoding: 'utf-8'
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
totalSize += stats.size;
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
console.error(`Failed to read file ${fullPath}:`, error);
|
|
1019
|
+
// Skip files that can't be read
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
await readDirectory(projectPath);
|
|
1026
|
+
|
|
1027
|
+
return {
|
|
1028
|
+
files,
|
|
1029
|
+
summary: {
|
|
1030
|
+
total_files: files.length,
|
|
1031
|
+
total_size: totalSize < 1024 ? `${totalSize}B` :
|
|
1032
|
+
totalSize < 1024 * 1024 ? `${(totalSize / 1024).toFixed(1)}KB` :
|
|
1033
|
+
`${(totalSize / 1024 / 1024).toFixed(2)}MB`,
|
|
1034
|
+
directories: [...new Set(directories)].sort(),
|
|
1035
|
+
file_types: [...new Set(files.map(f => f.type))].sort()
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Package project files into ZIP for remote delivery (DEPRECATED - keeping for fallback)
|
|
1042
|
+
*/
|
|
1043
|
+
private async packageProjectFiles(projectPath: string): Promise<{zipData: string, fileName: string, size: number}> {
|
|
1044
|
+
const { execSync } = await import('child_process');
|
|
1045
|
+
const { readFile, unlink } = await import('fs/promises');
|
|
1046
|
+
const { basename, dirname, join } = await import('path');
|
|
1047
|
+
const { tmpdir } = await import('os');
|
|
1048
|
+
|
|
1049
|
+
const projectName = basename(projectPath);
|
|
1050
|
+
const parentDir = dirname(projectPath);
|
|
1051
|
+
const tempZipPath = join(tmpdir(), `${projectName}-${Date.now()}.zip`);
|
|
1052
|
+
|
|
1053
|
+
console.error(`MCP Packaging Debug: Project path: ${projectPath}`);
|
|
1054
|
+
console.error(`MCP Packaging Debug: Project name: ${projectName}`);
|
|
1055
|
+
console.error(`MCP Packaging Debug: Parent dir: ${parentDir}`);
|
|
1056
|
+
console.error(`MCP Packaging Debug: Temp ZIP path: ${tempZipPath}`);
|
|
1057
|
+
|
|
1058
|
+
try {
|
|
1059
|
+
// Create ZIP file using system zip command (available on most systems)
|
|
1060
|
+
const zipCommand = `cd "${parentDir}" && zip -r "${tempZipPath}" "${projectName}"`;
|
|
1061
|
+
console.error(`MCP Packaging Debug: Running command: ${zipCommand}`);
|
|
1062
|
+
|
|
1063
|
+
execSync(zipCommand, {
|
|
1064
|
+
encoding: 'utf8',
|
|
1065
|
+
stdio: 'pipe'
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
console.error(`MCP Packaging Debug: ZIP file created successfully`);
|
|
1069
|
+
|
|
1070
|
+
// Read ZIP file as base64
|
|
1071
|
+
const zipBuffer = await readFile(tempZipPath);
|
|
1072
|
+
console.error(`MCP Packaging Debug: ZIP buffer size: ${zipBuffer.length} bytes`);
|
|
1073
|
+
console.error(`MCP Packaging Debug: ZIP buffer size: ${(zipBuffer.length / 1024 / 1024).toFixed(2)} MB`);
|
|
1074
|
+
|
|
1075
|
+
const zipData = zipBuffer.toString('base64');
|
|
1076
|
+
console.error(`MCP Packaging Debug: Base64 string length: ${zipData.length} chars`);
|
|
1077
|
+
|
|
1078
|
+
// Clean up temp ZIP file
|
|
1079
|
+
await unlink(tempZipPath);
|
|
1080
|
+
console.error(`MCP Packaging Debug: Temp file cleaned up`);
|
|
1081
|
+
|
|
1082
|
+
return {
|
|
1083
|
+
zipData,
|
|
1084
|
+
fileName: `${projectName}.zip`,
|
|
1085
|
+
size: zipBuffer.length
|
|
1086
|
+
};
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
console.error(`MCP Packaging Debug: ZIP command failed, trying fallback:`, error);
|
|
1089
|
+
// Fallback: try creating ZIP with Node.js if zip command fails
|
|
1090
|
+
return await this.createZipFallback(projectPath);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Fallback ZIP creation using pure Node.js
|
|
1096
|
+
*/
|
|
1097
|
+
private async createZipFallback(projectPath: string): Promise<{zipData: string, fileName: string, size: number}> {
|
|
1098
|
+
// Simple ZIP implementation fallback
|
|
1099
|
+
// For now, let's use tar.gz which is more universally available
|
|
1100
|
+
const { execSync } = await import('child_process');
|
|
1101
|
+
const { readFile, unlink } = await import('fs/promises');
|
|
1102
|
+
const { basename, dirname, join } = await import('path');
|
|
1103
|
+
const { tmpdir } = await import('os');
|
|
1104
|
+
|
|
1105
|
+
const projectName = basename(projectPath);
|
|
1106
|
+
const parentDir = dirname(projectPath);
|
|
1107
|
+
const tempTarPath = join(tmpdir(), `${projectName}-${Date.now()}.tar.gz`);
|
|
1108
|
+
|
|
1109
|
+
try {
|
|
1110
|
+
// Create tar.gz file (more universally available than zip)
|
|
1111
|
+
execSync(`cd "${parentDir}" && tar -czf "${tempTarPath}" "${projectName}"`, {
|
|
1112
|
+
encoding: 'utf8',
|
|
1113
|
+
stdio: 'pipe'
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
// Read tar.gz file as base64
|
|
1117
|
+
const tarBuffer = await readFile(tempTarPath);
|
|
1118
|
+
const tarData = tarBuffer.toString('base64');
|
|
1119
|
+
|
|
1120
|
+
// Clean up temp tar file
|
|
1121
|
+
await unlink(tempTarPath);
|
|
1122
|
+
|
|
1123
|
+
return {
|
|
1124
|
+
zipData: tarData,
|
|
1125
|
+
fileName: `${projectName}.tar.gz`,
|
|
1126
|
+
size: tarBuffer.length
|
|
1127
|
+
};
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
throw new Error(`Failed to create project archive: ${error}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* Execute init command without --json flag (not supported)
|
|
1135
|
+
* For remote deployments, returns project files as JSON structure
|
|
1136
|
+
*/
|
|
1137
|
+
private async executeInitCommand(command: string, args: Record<string, any>): Promise<any> {
|
|
1138
|
+
const { execSync } = await import('child_process');
|
|
1139
|
+
|
|
1140
|
+
// Build command with arguments (excluding --json, --zip, --local, and fullPath which are MCP-only)
|
|
1141
|
+
const argString = Object.entries(args)
|
|
1142
|
+
.filter(([key, value]) =>
|
|
1143
|
+
value !== undefined &&
|
|
1144
|
+
value !== null &&
|
|
1145
|
+
key !== 'zip' && // ZIP is handled by MCP service, not CLI
|
|
1146
|
+
key !== 'local' && // Local is MCP server flag, not CLI flag
|
|
1147
|
+
key !== 'fullPath' && // fullPath is used internally for directory handling
|
|
1148
|
+
key !== 'name' // name is passed as positional argument, not flag
|
|
1149
|
+
)
|
|
1150
|
+
.map(([key, value]) => {
|
|
1151
|
+
if (typeof value === 'boolean') {
|
|
1152
|
+
return value ? `--${key}` : '';
|
|
1153
|
+
} else {
|
|
1154
|
+
return `--${key} "${value}"`;
|
|
1155
|
+
}
|
|
1156
|
+
})
|
|
1157
|
+
.filter(Boolean)
|
|
1158
|
+
.join(' ');
|
|
1159
|
+
|
|
1160
|
+
const baseCommand = this.cliPath === 'specverse' ? 'specverse' : `node ${this.cliPath}`;
|
|
1161
|
+
const fullCommand = `${baseCommand} ${command} ${argString}`.trim();
|
|
1162
|
+
|
|
1163
|
+
// Use fullPath from args if available (contains the complete path), otherwise extract from command
|
|
1164
|
+
const rawName = args.fullPath || command.match(/"([^"]+)"/)?.[1] || args.name || 'project';
|
|
1165
|
+
|
|
1166
|
+
// Handle full path vs relative path
|
|
1167
|
+
let projectName: string;
|
|
1168
|
+
let targetWorkingDir: string;
|
|
1169
|
+
|
|
1170
|
+
if (rawName.startsWith('/')) {
|
|
1171
|
+
// Full absolute path - extract parent directory and project name
|
|
1172
|
+
const { dirname, basename } = await import('path');
|
|
1173
|
+
projectName = basename(rawName);
|
|
1174
|
+
targetWorkingDir = dirname(rawName);
|
|
1175
|
+
} else {
|
|
1176
|
+
// Relative path - use current directory
|
|
1177
|
+
projectName = rawName;
|
|
1178
|
+
targetWorkingDir = process.cwd();
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const forceZip = args.zip === true;
|
|
1182
|
+
const forceJson = args.json === true;
|
|
1183
|
+
|
|
1184
|
+
console.error(`MCP Init Debug: Working directory: ${process.cwd()}`);
|
|
1185
|
+
console.error(`MCP Init Debug: Target working directory: ${targetWorkingDir}`);
|
|
1186
|
+
console.error(`MCP Init Debug: Raw name: ${rawName}`);
|
|
1187
|
+
console.error(`MCP Init Debug: Project name: ${projectName}`);
|
|
1188
|
+
console.error(`MCP Init Debug: Force ZIP: ${forceZip}`);
|
|
1189
|
+
console.error(`MCP Init Debug: Force JSON: ${forceJson}`);
|
|
1190
|
+
console.error(`MCP Init Debug: Executing command: ${fullCommand}`);
|
|
1191
|
+
|
|
1192
|
+
// Simple decision logic
|
|
1193
|
+
if (forceZip) {
|
|
1194
|
+
console.error(`MCP Init Debug: ZIP requested - will return ZIP file`);
|
|
1195
|
+
return await this.executeInitWithZip(fullCommand, projectName, args);
|
|
1196
|
+
} else if (forceJson) {
|
|
1197
|
+
console.error(`MCP Init Debug: JSON requested - will return JSON structure`);
|
|
1198
|
+
return await this.executeInitWithJson(fullCommand, projectName, args);
|
|
1199
|
+
} else if (await this.canCreateLocalFiles(projectName, targetWorkingDir)) {
|
|
1200
|
+
console.error(`MCP Init Debug: Local filesystem - will create files locally`);
|
|
1201
|
+
return await this.executeInitLocal(fullCommand, projectName, targetWorkingDir);
|
|
1202
|
+
} else {
|
|
1203
|
+
console.error(`MCP Init Debug: Cannot create local files - will return JSON structure`);
|
|
1204
|
+
return await this.executeInitWithJson(fullCommand, projectName, args);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
private async canCreateLocalFiles(projectName: string, targetWorkingDir?: string): Promise<boolean> {
|
|
1209
|
+
try {
|
|
1210
|
+
// Use provided target directory, or fall back to old logic for compatibility
|
|
1211
|
+
const targetDir = targetWorkingDir || await (async () => {
|
|
1212
|
+
const cwd = process.cwd();
|
|
1213
|
+
|
|
1214
|
+
if (projectName.startsWith('/')) {
|
|
1215
|
+
// Absolute path - extract parent directory
|
|
1216
|
+
const { dirname } = await import('path');
|
|
1217
|
+
return dirname(projectName);
|
|
1218
|
+
} else {
|
|
1219
|
+
// Relative path - check if current directory is problematic
|
|
1220
|
+
if (cwd === '/') {
|
|
1221
|
+
return null; // Don't create relative paths in root
|
|
1222
|
+
}
|
|
1223
|
+
return cwd;
|
|
1224
|
+
}
|
|
1225
|
+
})();
|
|
1226
|
+
|
|
1227
|
+
// If we couldn't determine a valid target directory, return false
|
|
1228
|
+
if (!targetDir) {
|
|
1229
|
+
return false;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// Try to write a test file in the target directory
|
|
1233
|
+
const { writeFileSync, unlinkSync } = await import('fs');
|
|
1234
|
+
const { join } = await import('path');
|
|
1235
|
+
const testFile = join(targetDir, '.mcp-write-test-' + Date.now());
|
|
1236
|
+
writeFileSync(testFile, 'test');
|
|
1237
|
+
unlinkSync(testFile);
|
|
1238
|
+
return true;
|
|
1239
|
+
} catch {
|
|
1240
|
+
return false;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
private async executeInitLocal(fullCommand: string, projectName: string, targetWorkingDir?: string): Promise<any> {
|
|
1245
|
+
const { execSync } = await import('child_process');
|
|
1246
|
+
const { join } = await import('path');
|
|
1247
|
+
|
|
1248
|
+
// Use provided target directory or fallback to current directory
|
|
1249
|
+
const workingDir = targetWorkingDir || process.cwd();
|
|
1250
|
+
|
|
1251
|
+
try {
|
|
1252
|
+
const result = execSync(fullCommand, {
|
|
1253
|
+
encoding: 'utf8',
|
|
1254
|
+
timeout: 30000,
|
|
1255
|
+
stdio: 'pipe',
|
|
1256
|
+
cwd: workingDir
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
return {
|
|
1260
|
+
status: 'success',
|
|
1261
|
+
message: `Project '${projectName}' created successfully`,
|
|
1262
|
+
project_path: join(workingDir, projectName),
|
|
1263
|
+
delivery_method: 'local_filesystem',
|
|
1264
|
+
created: true
|
|
1265
|
+
};
|
|
1266
|
+
} catch (error: any) {
|
|
1267
|
+
const errorOutput = error.stdout || error.stderr || error.message;
|
|
1268
|
+
throw new Error(`Failed to create project '${projectName}': ${errorOutput}`);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
private async executeInitWithZip(fullCommand: string, projectName: string, args: Record<string, any>): Promise<any> {
|
|
1273
|
+
const { execSync } = await import('child_process');
|
|
1274
|
+
const { mkdtemp, rm } = await import('fs/promises');
|
|
1275
|
+
const { tmpdir } = await import('os');
|
|
1276
|
+
const { join } = await import('path');
|
|
1277
|
+
|
|
1278
|
+
// Create in temp directory
|
|
1279
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'specverse-zip-'));
|
|
1280
|
+
|
|
1281
|
+
try {
|
|
1282
|
+
const result = execSync(fullCommand, {
|
|
1283
|
+
encoding: 'utf8',
|
|
1284
|
+
timeout: 30000,
|
|
1285
|
+
stdio: 'pipe',
|
|
1286
|
+
cwd: tempDir
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
// Package as ZIP
|
|
1290
|
+
const projectPath = join(tempDir, projectName);
|
|
1291
|
+
const zipData = await this.packageProjectFiles(projectPath);
|
|
1292
|
+
|
|
1293
|
+
// Cleanup
|
|
1294
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
1295
|
+
|
|
1296
|
+
return {
|
|
1297
|
+
status: 'success',
|
|
1298
|
+
message: `Project '${projectName}' packaged as ZIP`,
|
|
1299
|
+
delivery_method: 'zip_file',
|
|
1300
|
+
zip_data: zipData.zipData,
|
|
1301
|
+
file_name: zipData.fileName,
|
|
1302
|
+
size: zipData.size
|
|
1303
|
+
};
|
|
1304
|
+
} catch (error: any) {
|
|
1305
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
1306
|
+
const errorOutput = error.stdout || error.stderr || error.message;
|
|
1307
|
+
throw new Error(`Failed to create project '${projectName}': ${errorOutput}`);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
private async executeInitWithJson(fullCommand: string, projectName: string, args: Record<string, any>): Promise<any> {
|
|
1312
|
+
const { execSync } = await import('child_process');
|
|
1313
|
+
const { mkdtemp, rm } = await import('fs/promises');
|
|
1314
|
+
const { tmpdir } = await import('os');
|
|
1315
|
+
const { join } = await import('path');
|
|
1316
|
+
|
|
1317
|
+
// Create in temp directory
|
|
1318
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'specverse-json-'));
|
|
1319
|
+
|
|
1320
|
+
try {
|
|
1321
|
+
const result = execSync(fullCommand, {
|
|
1322
|
+
encoding: 'utf8',
|
|
1323
|
+
timeout: 30000,
|
|
1324
|
+
stdio: 'pipe',
|
|
1325
|
+
cwd: tempDir
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
// Read files as JSON structure
|
|
1329
|
+
const projectPath = join(tempDir, projectName);
|
|
1330
|
+
const structuredFiles = await this.readProjectFilesStructured(projectPath);
|
|
1331
|
+
|
|
1332
|
+
// Cleanup
|
|
1333
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
1334
|
+
|
|
1335
|
+
return {
|
|
1336
|
+
status: 'success',
|
|
1337
|
+
instruction: 'create_file_attachments_for_each_file_described_in_files',
|
|
1338
|
+
message: `Project '${projectName}' created successfully`,
|
|
1339
|
+
project: projectName,
|
|
1340
|
+
files: structuredFiles.files,
|
|
1341
|
+
summary: structuredFiles.summary,
|
|
1342
|
+
delivery_method: 'json_structure'
|
|
1343
|
+
};
|
|
1344
|
+
} catch (error: any) {
|
|
1345
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
1346
|
+
const errorOutput = error.stdout || error.stderr || error.message;
|
|
1347
|
+
throw new Error(`Failed to create project '${projectName}': ${errorOutput}`);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* Map MCP tool name back to CLI command
|
|
1353
|
+
*/
|
|
1354
|
+
private mapToolNameToCommand(toolName: string): string {
|
|
1355
|
+
// Remove 'specverse_' prefix and convert underscores back to spaces
|
|
1356
|
+
const command = toolName.replace(/^specverse_/, '').replace(/_/g, ' ');
|
|
1357
|
+
|
|
1358
|
+
// Handle special cases
|
|
1359
|
+
if (command.startsWith('ai ')) {
|
|
1360
|
+
return command; // ai commands are already correct
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
return command;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Get MCP package version using Node.js built-in package resolution
|
|
1368
|
+
*/
|
|
1369
|
+
private async getMCPVersion(): Promise<string> {
|
|
1370
|
+
try {
|
|
1371
|
+
// Method 1: Development mode - look for local package.json first (most reliable)
|
|
1372
|
+
const { readFile } = await import('fs/promises');
|
|
1373
|
+
const { join, dirname } = await import('path');
|
|
1374
|
+
const { fileURLToPath } = await import('url');
|
|
1375
|
+
|
|
1376
|
+
// Get current file location and find project root
|
|
1377
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1378
|
+
const __dirname = dirname(__filename);
|
|
1379
|
+
|
|
1380
|
+
// In development, package.json should be 2-3 levels up from dist/services/
|
|
1381
|
+
const packagePaths = [
|
|
1382
|
+
join(__dirname, '..', '..', 'package.json'), // dist/services -> root
|
|
1383
|
+
join(__dirname, '..', '..', '..', 'package.json') // dist/target/services -> root
|
|
1384
|
+
];
|
|
1385
|
+
|
|
1386
|
+
for (const packagePath of packagePaths) {
|
|
1387
|
+
try {
|
|
1388
|
+
const content = await readFile(packagePath, 'utf8');
|
|
1389
|
+
const packageInfo = JSON.parse(content);
|
|
1390
|
+
// Verify this is the MCP package
|
|
1391
|
+
if (packageInfo.name === '@specverse/mcp') {
|
|
1392
|
+
return packageInfo.version;
|
|
1393
|
+
}
|
|
1394
|
+
} catch {
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// Method 2: Try npm installed package resolution
|
|
1400
|
+
try {
|
|
1401
|
+
const { createRequire } = await import('module');
|
|
1402
|
+
const require = createRequire(import.meta.url);
|
|
1403
|
+
const packageInfo = require('@specverse/mcp/package.json');
|
|
1404
|
+
return packageInfo.version;
|
|
1405
|
+
} catch {
|
|
1406
|
+
// Method 3: Try import.meta.resolve (newer Node versions)
|
|
1407
|
+
try {
|
|
1408
|
+
const packageJsonUrl = import.meta.resolve('@specverse/mcp/package.json');
|
|
1409
|
+
const content = await readFile(new URL(packageJsonUrl), 'utf8');
|
|
1410
|
+
const packageInfo = JSON.parse(content);
|
|
1411
|
+
return packageInfo.version;
|
|
1412
|
+
} catch {
|
|
1413
|
+
// Fallback to environment variable or unknown
|
|
1414
|
+
return process.env.MCP_VERSION || 'unknown';
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
} catch {
|
|
1418
|
+
// Final fallback
|
|
1419
|
+
return process.env.MCP_VERSION || 'unknown';
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* Execute MCP-specific debug tool
|
|
1425
|
+
*/
|
|
1426
|
+
private async executeMCPDebugTool(verbose: boolean): Promise<MCPToolResult> {
|
|
1427
|
+
try {
|
|
1428
|
+
const mcpVersion = await this.getMCPVersion();
|
|
1429
|
+
|
|
1430
|
+
const diagnostics: any = {
|
|
1431
|
+
timestamp: new Date().toISOString(),
|
|
1432
|
+
mcp_server: {
|
|
1433
|
+
version: mcpVersion,
|
|
1434
|
+
mode: 'diagnostic'
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
// Get SpecVerse installation info
|
|
1439
|
+
try {
|
|
1440
|
+
const { resolvePackageRoot } = specverseAPI;
|
|
1441
|
+
const packageRoot = resolvePackageRoot();
|
|
1442
|
+
|
|
1443
|
+
// Read package.json for version info
|
|
1444
|
+
const { readFile } = await import('fs/promises');
|
|
1445
|
+
const { join } = await import('path');
|
|
1446
|
+
const packageJsonPath = join(packageRoot, 'package.json');
|
|
1447
|
+
const packageContent = await readFile(packageJsonPath, 'utf8');
|
|
1448
|
+
const packageData = JSON.parse(packageContent);
|
|
1449
|
+
|
|
1450
|
+
diagnostics.specverse_installation = {
|
|
1451
|
+
type: 'npm_package',
|
|
1452
|
+
version: packageData.version,
|
|
1453
|
+
location: packageRoot,
|
|
1454
|
+
package_name: packageData.name,
|
|
1455
|
+
status: 'found'
|
|
1456
|
+
};
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
diagnostics.specverse_installation = {
|
|
1459
|
+
status: 'error',
|
|
1460
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// CLI detection
|
|
1465
|
+
diagnostics.cli_detection = {
|
|
1466
|
+
cli_path: this.cliPath,
|
|
1467
|
+
working_directory: this.workingDirectory || process.cwd(),
|
|
1468
|
+
status: this.cliPath ? 'found' : 'not_found'
|
|
1469
|
+
};
|
|
1470
|
+
|
|
1471
|
+
// Health check
|
|
1472
|
+
diagnostics.health_check = {
|
|
1473
|
+
can_import_specverse: !!diagnostics.specverse_installation.version,
|
|
1474
|
+
cli_accessible: !!this.cliPath,
|
|
1475
|
+
capabilities_loaded: !!this.capabilities,
|
|
1476
|
+
overall_status: (!!diagnostics.specverse_installation.version && !!this.cliPath) ? 'healthy' : 'degraded'
|
|
1477
|
+
};
|
|
1478
|
+
|
|
1479
|
+
return {
|
|
1480
|
+
content: [{
|
|
1481
|
+
type: 'text',
|
|
1482
|
+
text: JSON.stringify(diagnostics, null, 2)
|
|
1483
|
+
}]
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
} catch (error) {
|
|
1487
|
+
return {
|
|
1488
|
+
content: [{
|
|
1489
|
+
type: 'text',
|
|
1490
|
+
text: JSON.stringify({
|
|
1491
|
+
status: 'error',
|
|
1492
|
+
message: 'MCP debug tool execution failed',
|
|
1493
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1494
|
+
timestamp: new Date().toISOString()
|
|
1495
|
+
}, null, 2)
|
|
1496
|
+
}],
|
|
1497
|
+
isError: true
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|