@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.
Files changed (420) hide show
  1. package/assets/examples/09-api/ai-spec.yaml +194 -0
  2. package/assets/examples/09-api/converted.yaml +95 -0
  3. package/assets/examples/09-api/diagram-architecture.mmd +10 -0
  4. package/assets/examples/09-api/diagram-er.mmd +10 -0
  5. package/assets/examples/09-api/documentation.html +104 -0
  6. package/assets/examples/09-api/documentation.md +95 -0
  7. package/assets/examples/09-api/inferred-spec.yaml +420 -0
  8. package/assets/examples/09-api/openapi.json +61 -0
  9. package/assets/examples/10-api/README.md +216 -0
  10. package/assets/examples/10-api/ai-spec.yaml +194 -0
  11. package/assets/examples/10-api/converted.yaml +96 -0
  12. package/assets/examples/10-api/diagram-architecture.mmd +10 -0
  13. package/assets/examples/10-api/diagram-er.mmd +10 -0
  14. package/assets/examples/10-api/documentation.html +104 -0
  15. package/assets/examples/10-api/documentation.md +95 -0
  16. package/assets/examples/10-api/inferred-spec.yaml +7 -0
  17. package/assets/examples/10-api/metadata.yaml +89 -0
  18. package/assets/examples/10-api/openapi.json +61 -0
  19. package/assets/examples/10-api/package-integration-test.js +177 -0
  20. package/assets/examples/10-api/usage-example.js +323 -0
  21. package/assets/examples/10-api/usage-example.ts +363 -0
  22. package/assets/examples/10-api/workflow-test.js +113 -0
  23. package/assets/examples/manifests/01-simple-default-mappings.yaml +36 -0
  24. package/assets/examples/manifests/02-capability-mappings.yaml +55 -0
  25. package/assets/examples/manifests/03-hybrid-mappings.yaml +109 -0
  26. package/assets/examples/manifests/README.md +245 -0
  27. package/assets/examples/manifests/backend-only.yaml +43 -0
  28. package/assets/examples/manifests/blog-api.md +78 -0
  29. package/assets/examples/manifests/blog-api.specly +79 -0
  30. package/assets/examples/manifests/frontend-only.yaml +27 -0
  31. package/assets/examples/manifests/fullstack-app.yaml +44 -0
  32. package/assets/examples/manifests/fullstack-monorepo.yaml +62 -0
  33. package/assets/examples/validate-examples-with-expected-failures.cjs +328 -0
  34. package/assets/examples/validate-examples.cjs +225 -0
  35. package/assets/examples-decomposed/cloud-native-manifest.example.yaml +8 -0
  36. package/assets/examples-decomposed/cloud-native-manifest.md +379 -0
  37. package/assets/examples-decomposed/cloud-native-manifest.specly +60 -0
  38. package/assets/examples-decomposed/docker-compose-manifest.example.yaml +8 -0
  39. package/assets/examples-decomposed/docker-compose-manifest.md +326 -0
  40. package/assets/examples-decomposed/docker-compose-manifest.specly +40 -0
  41. package/assets/examples-decomposed/kubernetes-deployment-manifest.example.yaml +8 -0
  42. package/assets/examples-decomposed/kubernetes-deployment-manifest.md +237 -0
  43. package/assets/examples-decomposed/kubernetes-deployment-manifest.specly +41 -0
  44. package/assets/templates/README.md +559 -0
  45. package/assets/templates/TEMPLATE-ENHANCEMENTS-V33.md +462 -0
  46. package/assets/templates/backend-only/CLAUDE.md +73 -0
  47. package/assets/templates/backend-only/README.md +197 -0
  48. package/assets/templates/backend-only/deployments/README.md +149 -0
  49. package/assets/templates/backend-only/deployments/development.specly +53 -0
  50. package/assets/templates/backend-only/deployments/production.specly +87 -0
  51. package/assets/templates/backend-only/docs/README.md +50 -0
  52. package/assets/templates/backend-only/docs/api/README.md +7 -0
  53. package/assets/templates/backend-only/docs/diagrams/README.md +85 -0
  54. package/assets/templates/backend-only/docs/example-documentation-template.md +269 -0
  55. package/assets/templates/backend-only/docs/guides/README.md +15 -0
  56. package/assets/templates/backend-only/dot.env.example +18 -0
  57. package/assets/templates/backend-only/generated/README.md +56 -0
  58. package/assets/templates/backend-only/generated/code/integration-test.template.js +320 -0
  59. package/assets/templates/backend-only/generated/code/package.json.template +34 -0
  60. package/assets/templates/backend-only/generated/docs/README.md +49 -0
  61. package/assets/templates/backend-only/gitignore +54 -0
  62. package/assets/templates/backend-only/manifests/README.md +72 -0
  63. package/assets/templates/backend-only/manifests/docker-compose.specly +91 -0
  64. package/assets/templates/backend-only/manifests/implementation.yaml +100 -0
  65. package/assets/templates/backend-only/manifests/kubernetes.specly +140 -0
  66. package/assets/templates/backend-only/package.json +59 -0
  67. package/assets/templates/backend-only/scripts/test-all.sh +160 -0
  68. package/assets/templates/backend-only/scripts/test-generated-code.sh +165 -0
  69. package/assets/templates/backend-only/specs/main.specly +67 -0
  70. package/assets/templates/default/CLAUDE.md +141 -0
  71. package/assets/templates/default/README.md +404 -0
  72. package/assets/templates/default/deployments/README.md +149 -0
  73. package/assets/templates/default/deployments/development.specly +53 -0
  74. package/assets/templates/default/deployments/production.specly +87 -0
  75. package/assets/templates/default/docs/README.md +50 -0
  76. package/assets/templates/default/docs/api/README.md +7 -0
  77. package/assets/templates/default/docs/diagrams/README.md +85 -0
  78. package/assets/templates/default/docs/example-documentation-template.md +269 -0
  79. package/assets/templates/default/docs/guides/README.md +15 -0
  80. package/assets/templates/default/dot.env.example +18 -0
  81. package/assets/templates/default/generated/README.md +56 -0
  82. package/assets/templates/default/generated/code/integration-test.template.js +320 -0
  83. package/assets/templates/default/generated/code/package.json.template +34 -0
  84. package/assets/templates/default/generated/docs/README.md +49 -0
  85. package/assets/templates/default/gitignore +54 -0
  86. package/assets/templates/default/manifests/README.md +72 -0
  87. package/assets/templates/default/manifests/docker-compose.specly +91 -0
  88. package/assets/templates/default/manifests/implementation.yaml +176 -0
  89. package/assets/templates/default/manifests/kubernetes.specly +140 -0
  90. package/assets/templates/default/package.json +61 -0
  91. package/assets/templates/default/scripts/test-all.sh +160 -0
  92. package/assets/templates/default/scripts/test-generated-code.sh +165 -0
  93. package/assets/templates/default/specs/main.specly +67 -0
  94. package/assets/templates/frontend-only/CLAUDE.md +75 -0
  95. package/assets/templates/frontend-only/README.md +231 -0
  96. package/assets/templates/frontend-only/deployments/README.md +149 -0
  97. package/assets/templates/frontend-only/deployments/development.specly +53 -0
  98. package/assets/templates/frontend-only/deployments/production.specly +87 -0
  99. package/assets/templates/frontend-only/docs/README.md +50 -0
  100. package/assets/templates/frontend-only/docs/api/README.md +7 -0
  101. package/assets/templates/frontend-only/docs/diagrams/README.md +85 -0
  102. package/assets/templates/frontend-only/docs/example-documentation-template.md +269 -0
  103. package/assets/templates/frontend-only/docs/guides/README.md +15 -0
  104. package/assets/templates/frontend-only/dot.env.example +18 -0
  105. package/assets/templates/frontend-only/generated/README.md +56 -0
  106. package/assets/templates/frontend-only/generated/code/integration-test.template.js +320 -0
  107. package/assets/templates/frontend-only/generated/code/package.json.template +34 -0
  108. package/assets/templates/frontend-only/generated/docs/README.md +49 -0
  109. package/assets/templates/frontend-only/gitignore +54 -0
  110. package/assets/templates/frontend-only/manifests/README.md +72 -0
  111. package/assets/templates/frontend-only/manifests/docker-compose.specly +91 -0
  112. package/assets/templates/frontend-only/manifests/implementation.yaml +58 -0
  113. package/assets/templates/frontend-only/manifests/kubernetes.specly +140 -0
  114. package/assets/templates/frontend-only/package.json +59 -0
  115. package/assets/templates/frontend-only/scripts/test-all.sh +160 -0
  116. package/assets/templates/frontend-only/scripts/test-generated-code.sh +165 -0
  117. package/assets/templates/frontend-only/specs/main.specly +57 -0
  118. package/assets/templates/full-stack/AI-GUIDE.md +60 -0
  119. package/assets/templates/full-stack/CLAUDE.md +141 -0
  120. package/assets/templates/full-stack/README.md +382 -0
  121. package/assets/templates/full-stack/archive/AI-GUIDE-legacy.md +392 -0
  122. package/assets/templates/full-stack/deployments/README.md +149 -0
  123. package/assets/templates/full-stack/deployments/development.specly +53 -0
  124. package/assets/templates/full-stack/deployments/production.specly +87 -0
  125. package/assets/templates/full-stack/docs/README.md +51 -0
  126. package/assets/templates/full-stack/docs/api/README.md +7 -0
  127. package/assets/templates/full-stack/docs/diagrams/README.md +85 -0
  128. package/assets/templates/full-stack/docs/example-documentation-template.md +269 -0
  129. package/assets/templates/full-stack/docs/guides/README.md +15 -0
  130. package/assets/templates/full-stack/generated/README.md +56 -0
  131. package/assets/templates/full-stack/generated/code/integration-test.template.js +320 -0
  132. package/assets/templates/full-stack/generated/code/package.json.template +34 -0
  133. package/assets/templates/full-stack/generated/docs/README.md +49 -0
  134. package/assets/templates/full-stack/gitignore +54 -0
  135. package/assets/templates/full-stack/manifests/README.md +72 -0
  136. package/assets/templates/full-stack/manifests/docker-compose.specly +91 -0
  137. package/assets/templates/full-stack/manifests/implementation.yaml +155 -0
  138. package/assets/templates/full-stack/manifests/kubernetes.specly +140 -0
  139. package/assets/templates/full-stack/package.json +45 -0
  140. package/assets/templates/full-stack/scripts/test-all.sh +160 -0
  141. package/assets/templates/full-stack/scripts/test-generated-code.sh +165 -0
  142. package/assets/templates/full-stack/specs/example-v33.specly +297 -0
  143. package/assets/templates/full-stack/specs/main-simple.specly +73 -0
  144. package/assets/templates/full-stack/specs/main.specly +408 -0
  145. package/dist/engines/code-generator.d.ts +86 -0
  146. package/dist/engines/code-generator.d.ts.map +1 -0
  147. package/dist/engines/code-generator.js +159 -0
  148. package/dist/engines/code-generator.js.map +1 -0
  149. package/dist/engines/engine-registry.d.ts +94 -0
  150. package/dist/engines/engine-registry.d.ts.map +1 -0
  151. package/dist/engines/engine-registry.js +163 -0
  152. package/dist/engines/engine-registry.js.map +1 -0
  153. package/dist/engines/index.d.ts +10 -0
  154. package/dist/engines/index.d.ts.map +1 -0
  155. package/dist/engines/index.js +12 -0
  156. package/dist/engines/index.js.map +1 -0
  157. package/dist/engines/typescript-engine.d.ts +74 -0
  158. package/dist/engines/typescript-engine.d.ts.map +1 -0
  159. package/dist/engines/typescript-engine.js +288 -0
  160. package/dist/engines/typescript-engine.js.map +1 -0
  161. package/dist/generators/index.d.ts +11 -0
  162. package/dist/generators/index.d.ts.map +1 -0
  163. package/dist/generators/index.js +11 -0
  164. package/dist/generators/index.js.map +1 -0
  165. package/dist/index.d.ts +48 -0
  166. package/dist/index.d.ts.map +1 -0
  167. package/dist/index.js +434 -0
  168. package/dist/index.js.map +1 -0
  169. package/dist/library/index.d.ts +12 -0
  170. package/dist/library/index.d.ts.map +1 -0
  171. package/dist/library/index.js +15 -0
  172. package/dist/library/index.js.map +1 -0
  173. package/dist/library/library.d.ts +132 -0
  174. package/dist/library/library.d.ts.map +1 -0
  175. package/dist/library/library.js +343 -0
  176. package/dist/library/library.js.map +1 -0
  177. package/dist/library/loader.d.ts +73 -0
  178. package/dist/library/loader.d.ts.map +1 -0
  179. package/dist/library/loader.js +150 -0
  180. package/dist/library/loader.js.map +1 -0
  181. package/dist/library/resolver.d.ts +104 -0
  182. package/dist/library/resolver.d.ts.map +1 -0
  183. package/dist/library/resolver.js +299 -0
  184. package/dist/library/resolver.js.map +1 -0
  185. package/dist/library/validator.d.ts +65 -0
  186. package/dist/library/validator.d.ts.map +1 -0
  187. package/dist/library/validator.js +203 -0
  188. package/dist/library/validator.js.map +1 -0
  189. package/dist/types/index.d.ts +7 -0
  190. package/dist/types/index.d.ts.map +1 -0
  191. package/dist/types/index.js +7 -0
  192. package/dist/types/index.js.map +1 -0
  193. package/dist/types/instance-factory.d.ts +289 -0
  194. package/dist/types/instance-factory.d.ts.map +1 -0
  195. package/dist/types/instance-factory.js +8 -0
  196. package/dist/types/instance-factory.js.map +1 -0
  197. package/dist/types/unified-mappings.d.ts +163 -0
  198. package/dist/types/unified-mappings.d.ts.map +1 -0
  199. package/dist/types/unified-mappings.js +110 -0
  200. package/dist/types/unified-mappings.js.map +1 -0
  201. package/dist/utils/ai-spec-loader.d.ts +77 -0
  202. package/dist/utils/ai-spec-loader.d.ts.map +1 -0
  203. package/dist/utils/ai-spec-loader.js +138 -0
  204. package/dist/utils/ai-spec-loader.js.map +1 -0
  205. package/dist/utils/index.d.ts +9 -0
  206. package/dist/utils/index.d.ts.map +1 -0
  207. package/dist/utils/index.js +9 -0
  208. package/dist/utils/index.js.map +1 -0
  209. package/dist/utils/manifest-loader.d.ts +107 -0
  210. package/dist/utils/manifest-loader.d.ts.map +1 -0
  211. package/dist/utils/manifest-loader.js +168 -0
  212. package/dist/utils/manifest-loader.js.map +1 -0
  213. package/dist/utils/mapping-migration.d.ts +53 -0
  214. package/dist/utils/mapping-migration.d.ts.map +1 -0
  215. package/dist/utils/mapping-migration.js +194 -0
  216. package/dist/utils/mapping-migration.js.map +1 -0
  217. package/libs/instance-factories/CURVED-INTERFACE.md +278 -0
  218. package/libs/instance-factories/README.md +433 -0
  219. package/libs/instance-factories/applications/generic-app.yaml +52 -0
  220. package/libs/instance-factories/applications/react-app.yaml +186 -0
  221. package/libs/instance-factories/applications/templates/generic/backend-env-generator.ts +31 -0
  222. package/libs/instance-factories/applications/templates/generic/backend-package-json-generator.ts +80 -0
  223. package/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.ts +69 -0
  224. package/libs/instance-factories/applications/templates/generic/main-generator.ts +308 -0
  225. package/libs/instance-factories/applications/templates/react/_view-components-source.ts +555 -0
  226. package/libs/instance-factories/applications/templates/react/api-client-generator.ts +436 -0
  227. package/libs/instance-factories/applications/templates/react/api-types-generator.ts +153 -0
  228. package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +94 -0
  229. package/libs/instance-factories/applications/templates/react/env-example-generator.ts +24 -0
  230. package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +106 -0
  231. package/libs/instance-factories/applications/templates/react/gitignore-generator.ts +38 -0
  232. package/libs/instance-factories/applications/templates/react/index-css-generator.ts +85 -0
  233. package/libs/instance-factories/applications/templates/react/index-html-generator.ts +30 -0
  234. package/libs/instance-factories/applications/templates/react/main-tsx-generator.ts +34 -0
  235. package/libs/instance-factories/applications/templates/react/package-json-generator.ts +54 -0
  236. package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +179 -0
  237. package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +1347 -0
  238. package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +150 -0
  239. package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +704 -0
  240. package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +84 -0
  241. package/libs/instance-factories/applications/templates/react/tsconfig-generator.ts +35 -0
  242. package/libs/instance-factories/applications/templates/react/use-api-hooks-generator.ts +121 -0
  243. package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +150 -0
  244. package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +150 -0
  245. package/libs/instance-factories/applications/templates/react/view-form-generator.ts +362 -0
  246. package/libs/instance-factories/applications/templates/react/view-list-generator.ts +98 -0
  247. package/libs/instance-factories/applications/templates/react/view-router-generator.ts +89 -0
  248. package/libs/instance-factories/applications/templates/react/vite-config-generator.ts +49 -0
  249. package/libs/instance-factories/archived/fastify-prisma.yaml +104 -0
  250. package/libs/instance-factories/cli/commander-js.yaml +55 -0
  251. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.d.ts +12 -0
  252. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.d.ts.map +1 -0
  253. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.js +115 -0
  254. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.js.map +1 -0
  255. package/libs/instance-factories/cli/templates/commander/cli-entry-generator.ts +145 -0
  256. package/libs/instance-factories/cli/templates/commander/command-generator.d.ts +14 -0
  257. package/libs/instance-factories/cli/templates/commander/command-generator.d.ts.map +1 -0
  258. package/libs/instance-factories/cli/templates/commander/command-generator.js +182 -0
  259. package/libs/instance-factories/cli/templates/commander/command-generator.js.map +1 -0
  260. package/libs/instance-factories/cli/templates/commander/command-generator.ts +992 -0
  261. package/libs/instance-factories/communication/event-emitter.yaml +56 -0
  262. package/libs/instance-factories/communication/rabbitmq-events.yaml +87 -0
  263. package/libs/instance-factories/communication/templates/eventemitter/bus-generator.ts +93 -0
  264. package/libs/instance-factories/communication/templates/eventemitter/publisher-generator.ts +117 -0
  265. package/libs/instance-factories/communication/templates/eventemitter/subscriber-generator.ts +101 -0
  266. package/libs/instance-factories/controllers/fastify.yaml +127 -0
  267. package/libs/instance-factories/controllers/templates/fastify/meta-routes-generator.ts +103 -0
  268. package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +389 -0
  269. package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +76 -0
  270. package/libs/instance-factories/infrastructure/docker-k8s.yaml +61 -0
  271. package/libs/instance-factories/infrastructure/templates/docker-k8s/infrastructure-generator.ts +46 -0
  272. package/libs/instance-factories/orms/prisma.yaml +89 -0
  273. package/libs/instance-factories/orms/templates/prisma/schema-generator.ts +563 -0
  274. package/libs/instance-factories/orms/templates/prisma/services-generator.ts +408 -0
  275. package/libs/instance-factories/scaffolding/generic-scaffold.yaml +65 -0
  276. package/libs/instance-factories/scaffolding/templates/generic/env-example-generator.ts +73 -0
  277. package/libs/instance-factories/scaffolding/templates/generic/env-generator.ts +85 -0
  278. package/libs/instance-factories/scaffolding/templates/generic/gitignore-generator.ts +69 -0
  279. package/libs/instance-factories/scaffolding/templates/generic/package-json-generator.ts +176 -0
  280. package/libs/instance-factories/scaffolding/templates/generic/readme-generator.ts +207 -0
  281. package/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.ts +78 -0
  282. package/libs/instance-factories/scaffolding/templates/generic/tsconfig-react-generator.ts +41 -0
  283. package/libs/instance-factories/sdks/python-sdk.yaml +66 -0
  284. package/libs/instance-factories/sdks/templates/python/sdk-generator.ts +50 -0
  285. package/libs/instance-factories/sdks/templates/typescript/sdk-generator.ts +49 -0
  286. package/libs/instance-factories/sdks/typescript-sdk.yaml +59 -0
  287. package/libs/instance-factories/services/prisma-services.yaml +71 -0
  288. package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +303 -0
  289. package/libs/instance-factories/services/templates/prisma/controller-generator.ts +532 -0
  290. package/libs/instance-factories/services/templates/prisma/service-generator.ts +315 -0
  291. package/libs/instance-factories/shared/path-resolver.ts +111 -0
  292. package/libs/instance-factories/storage/mongodb.yaml +79 -0
  293. package/libs/instance-factories/storage/postgresql.yaml +75 -0
  294. package/libs/instance-factories/storage/redis.yaml +79 -0
  295. package/libs/instance-factories/storage/templates/mongodb/config-generator.ts +15 -0
  296. package/libs/instance-factories/storage/templates/mongodb/docker-generator.ts +18 -0
  297. package/libs/instance-factories/storage/templates/postgresql/config-generator.ts +54 -0
  298. package/libs/instance-factories/storage/templates/postgresql/docker-generator.ts +55 -0
  299. package/libs/instance-factories/storage/templates/redis/config-generator.ts +16 -0
  300. package/libs/instance-factories/storage/templates/redis/docker-generator.ts +18 -0
  301. package/libs/instance-factories/test-generation.ts +192 -0
  302. package/libs/instance-factories/testing/templates/vitest/tests-generator.ts +51 -0
  303. package/libs/instance-factories/testing/vitest-tests.yaml +63 -0
  304. package/libs/instance-factories/tools/templates/mcp/mcp-server-generator.ts +136 -0
  305. package/libs/instance-factories/tools/templates/mcp/static/docs/DEPLOYMENT_GUIDE.md +630 -0
  306. package/libs/instance-factories/tools/templates/mcp/static/docs/HYBRID_RESOURCE_SYSTEM.md +330 -0
  307. package/libs/instance-factories/tools/templates/mcp/static/docs/deployments/EXTENSION_DEPLOYMENT.md +552 -0
  308. package/libs/instance-factories/tools/templates/mcp/static/docs/deployments/LOCAL_DEPLOYMENT.md +164 -0
  309. package/libs/instance-factories/tools/templates/mcp/static/docs/deployments/WEB_DEPLOYMENT.md +247 -0
  310. package/libs/instance-factories/tools/templates/mcp/static/package.json +92 -0
  311. package/libs/instance-factories/tools/templates/mcp/static/scripts/build-enterprise.js +284 -0
  312. package/libs/instance-factories/tools/templates/mcp/static/scripts/build-extension.js +139 -0
  313. package/libs/instance-factories/tools/templates/mcp/static/scripts/build-local.js +74 -0
  314. package/libs/instance-factories/tools/templates/mcp/static/scripts/build-web.js +156 -0
  315. package/libs/instance-factories/tools/templates/mcp/static/scripts/copy-canonical-files.js +41 -0
  316. package/libs/instance-factories/tools/templates/mcp/static/scripts/test-deployments.js +259 -0
  317. package/libs/instance-factories/tools/templates/mcp/static/scripts/test-hybrid-resources.js +231 -0
  318. package/libs/instance-factories/tools/templates/mcp/static/scripts/test-hybrid-simple.js +196 -0
  319. package/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.ts +293 -0
  320. package/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.ts +90 -0
  321. package/libs/instance-factories/tools/templates/mcp/static/src/index.ts +24 -0
  322. package/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.ts +15 -0
  323. package/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.ts +106 -0
  324. package/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.ts +75 -0
  325. package/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.ts +239 -0
  326. package/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.ts +1501 -0
  327. package/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.ts +211 -0
  328. package/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.ts +308 -0
  329. package/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.ts +210 -0
  330. package/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.ts +356 -0
  331. package/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.ts +524 -0
  332. package/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.ts +530 -0
  333. package/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.ts +594 -0
  334. package/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.ts +170 -0
  335. package/libs/instance-factories/tools/templates/mcp/static/src/tests/unit/CLIProxyService.init.test.ts +544 -0
  336. package/libs/instance-factories/tools/templates/mcp/static/src/tests/unit/CLIProxyService.test.ts +189 -0
  337. package/libs/instance-factories/tools/templates/mcp/static/src/tests/unit/ResourcesProviderService.test.ts +89 -0
  338. package/libs/instance-factories/tools/templates/mcp/static/src/types/index.ts +110 -0
  339. package/libs/instance-factories/tools/templates/mcp/static/tsconfig.json +28 -0
  340. package/libs/instance-factories/tools/templates/vscode/static/extension.ts +1195 -0
  341. package/libs/instance-factories/tools/templates/vscode/static/language-configuration.json +34 -0
  342. package/libs/instance-factories/tools/templates/vscode/static/schemas/specverse-v3-schema.json +4279 -0
  343. package/libs/instance-factories/tools/templates/vscode/static/syntaxes/specverse.tmLanguage.json +138 -0
  344. package/libs/instance-factories/tools/templates/vscode/static/themes/README.md +74 -0
  345. package/libs/instance-factories/tools/templates/vscode/static/themes/complete-specverse-colors.json +122 -0
  346. package/libs/instance-factories/tools/templates/vscode/static/themes/specverse-basic-theme.json +65 -0
  347. package/libs/instance-factories/tools/templates/vscode/static/themes/specverse-complete-theme.json +123 -0
  348. package/libs/instance-factories/tools/templates/vscode/static/themes/specverse-theme-colors.json +64 -0
  349. package/libs/instance-factories/tools/templates/vscode/vscode-extension-generator.ts +214 -0
  350. package/libs/instance-factories/validation/templates/zod/validation-generator.ts +46 -0
  351. package/libs/instance-factories/validation/zod.yaml +56 -0
  352. package/libs/instance-factories/views/index.d.ts +13 -0
  353. package/libs/instance-factories/views/index.d.ts.map +1 -0
  354. package/libs/instance-factories/views/index.js +18 -0
  355. package/libs/instance-factories/views/index.js.map +1 -0
  356. package/libs/instance-factories/views/index.ts +45 -0
  357. package/libs/instance-factories/views/react-components.yaml +129 -0
  358. package/libs/instance-factories/views/templates/ARCHITECTURE.md +198 -0
  359. package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +869 -0
  360. package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +953 -0
  361. package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +806 -0
  362. package/libs/instance-factories/views/templates/react/app-generator.ts +55 -0
  363. package/libs/instance-factories/views/templates/react/components-generator.ts +391 -0
  364. package/libs/instance-factories/views/templates/react/forms-generator.ts +343 -0
  365. package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +54 -0
  366. package/libs/instance-factories/views/templates/react/hooks-generator.ts +122 -0
  367. package/libs/instance-factories/views/templates/react/index-css-generator.ts +209 -0
  368. package/libs/instance-factories/views/templates/react/index-html-generator.ts +34 -0
  369. package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +29 -0
  370. package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +152 -0
  371. package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +1 -0
  372. package/libs/instance-factories/views/templates/react/react-component-generator.js +398 -0
  373. package/libs/instance-factories/views/templates/react/react-component-generator.js.map +1 -0
  374. package/libs/instance-factories/views/templates/react/react-component-generator.ts +533 -0
  375. package/libs/instance-factories/views/templates/react/router-generator.ts +197 -0
  376. package/libs/instance-factories/views/templates/react/router-generic-generator.ts +103 -0
  377. package/libs/instance-factories/views/templates/react/spec-json-generator.ts +17 -0
  378. package/libs/instance-factories/views/templates/react/types-generator.ts +76 -0
  379. package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +42 -0
  380. package/libs/instance-factories/views/templates/react/vite-config-generator.ts +38 -0
  381. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +1 -0
  382. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +1 -0
  383. package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +474 -0
  384. package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +242 -0
  385. package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +77 -0
  386. package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +1 -0
  387. package/libs/instance-factories/views/templates/shared/adapter-types.js +47 -0
  388. package/libs/instance-factories/views/templates/shared/adapter-types.js.map +1 -0
  389. package/libs/instance-factories/views/templates/shared/adapter-types.ts +142 -0
  390. package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +63 -0
  391. package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +1 -0
  392. package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +822 -0
  393. package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +1 -0
  394. package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +908 -0
  395. package/libs/instance-factories/views/templates/shared/base-generator.d.ts +247 -0
  396. package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +1 -0
  397. package/libs/instance-factories/views/templates/shared/base-generator.js +363 -0
  398. package/libs/instance-factories/views/templates/shared/base-generator.js.map +1 -0
  399. package/libs/instance-factories/views/templates/shared/base-generator.ts +608 -0
  400. package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +254 -0
  401. package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +1 -0
  402. package/libs/instance-factories/views/templates/shared/component-metadata.js +602 -0
  403. package/libs/instance-factories/views/templates/shared/component-metadata.js.map +1 -0
  404. package/libs/instance-factories/views/templates/shared/component-metadata.ts +803 -0
  405. package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +250 -0
  406. package/libs/instance-factories/views/templates/shared/composite-patterns.ts +535 -0
  407. package/libs/instance-factories/views/templates/shared/index.ts +68 -0
  408. package/libs/instance-factories/views/templates/shared/pattern-validator.ts +279 -0
  409. package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +149 -0
  410. package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +1 -0
  411. package/libs/instance-factories/views/templates/shared/property-mapper.js +580 -0
  412. package/libs/instance-factories/views/templates/shared/property-mapper.js.map +1 -0
  413. package/libs/instance-factories/views/templates/shared/property-mapper.ts +700 -0
  414. package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +143 -0
  415. package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +1 -0
  416. package/libs/instance-factories/views/templates/shared/syntax-mapper.js +420 -0
  417. package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +1 -0
  418. package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +539 -0
  419. package/package.json +42 -0
  420. package/schema/SPECVERSE-SCHEMA.json +4274 -0
@@ -0,0 +1,992 @@
1
+ /**
2
+ * Command Generator
3
+ *
4
+ * Generates individual command files from command specifications.
5
+ * Each command registers itself on a Commander program and wires
6
+ * to its corresponding service for business logic delegation.
7
+ */
8
+
9
+ import type { TemplateContext } from '@specverse/engine-realize';
10
+
11
+ /**
12
+ * Generate a single command file.
13
+ * Called once per command in the spec.
14
+ */
15
+ export default function generateCommand(context: TemplateContext): string {
16
+ const { command } = context;
17
+
18
+ if (!command) {
19
+ throw new Error('Command is required in template context');
20
+ }
21
+
22
+ const name = command.name;
23
+ const description = command.description || '';
24
+ const args = command.arguments || {};
25
+ const flags = command.flags || {};
26
+ const exitCodes = command.exitCodes || {};
27
+ const subcommands = command.subcommands || {};
28
+ const serviceRef = command.serviceRef;
29
+
30
+ // Build positional argument string for Commander
31
+ const positionalArgs = Object.entries(args)
32
+ .filter(([_, arg]: [string, any]) => arg.positional)
33
+ .sort((a: any, b: any) => (a[1].position || 0) - (b[1].position || 0))
34
+ .map(([argName, arg]: [string, any]) => {
35
+ const required = arg.required;
36
+ return required ? `<${argName}>` : `[${argName}]`;
37
+ })
38
+ .join(' ');
39
+
40
+ const commandStr = positionalArgs ? `${name} ${positionalArgs}` : name;
41
+
42
+ // Build options
43
+ const optionDefs = Object.entries(flags).map(([flagName, flag]: [string, any]) => {
44
+ const alias = flag.alias ? `${flag.alias}, ` : '';
45
+ const flagType = flag.type?.toLowerCase();
46
+ const valuePart = flagType === 'boolean' ? '' : ` <${flagName.replace(/^--/, '')}>`;
47
+ const defaultVal = flag.default !== undefined ? `, ${JSON.stringify(flag.default)}` : '';
48
+ const desc = flag.description || `${flagName} option`;
49
+ return ` .option('${alias}${flagName}${valuePart}', '${desc}'${defaultVal})`;
50
+ });
51
+
52
+ // Build type interface for options
53
+ const optionTypes = Object.entries(flags).map(([flagName, flag]: [string, any]) => {
54
+ const tsType = mapFlagTypeToTS(flag.type);
55
+ const key = flagName.replace(/^--/, '').replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
56
+ return ` ${key}${flag.required ? '' : '?'}: ${tsType};`;
57
+ });
58
+
59
+ // Build positional arg types
60
+ const argTypes = Object.entries(args)
61
+ .filter(([_, arg]: [string, any]) => arg.positional)
62
+ .map(([argName, arg]: [string, any]) => {
63
+ const tsType = mapArgTypeToTS(arg.type);
64
+ return `${argName}: ${tsType}`;
65
+ });
66
+
67
+ // Generate action handler
68
+ const actionParams = argTypes.length > 0
69
+ ? argTypes.join(', ') + ', options: CommandOptions'
70
+ : 'options: CommandOptions';
71
+
72
+ // Generate exit code comments
73
+ const exitCodeComments = Object.entries(exitCodes).length > 0
74
+ ? Object.entries(exitCodes).map(([code, meaning]) =>
75
+ ` // ${code}: ${meaning}`
76
+ ).join('\n')
77
+ : '';
78
+
79
+ // Handle subcommands
80
+ const hasSubcommands = Object.keys(subcommands).length > 0;
81
+ const subcommandRegistrations = hasSubcommands
82
+ ? generateSubcommandRegistrations(name, subcommands)
83
+ : '';
84
+
85
+ // Service import
86
+ const serviceImport = serviceRef
87
+ ? `import { ${serviceRef} } from '../services/${serviceRef}.js';`
88
+ : '';
89
+
90
+ // Collect engine imports — deduplicate across parent and subcommand handlers
91
+ const allImportSets: string[] = [];
92
+ if (ENGINE_HANDLERS[name]?.imports) allImportSets.push(ENGINE_HANDLERS[name].imports);
93
+ if (hasSubcommands) {
94
+ for (const subName of Object.keys(subcommands)) {
95
+ const key = `${name}.${subName}`;
96
+ if (ENGINE_HANDLERS[key]?.imports) allImportSets.push(ENGINE_HANDLERS[key].imports);
97
+ }
98
+ }
99
+ const engineImports = deduplicateImports(allImportSets);
100
+
101
+ return `/**
102
+ * ${name} command
103
+ * ${description}
104
+ * Generated from SpecVerse specification
105
+ */
106
+
107
+ import { Command } from 'commander';
108
+ ${serviceImport}
109
+ ${engineImports}
110
+
111
+ interface CommandOptions {
112
+ ${optionTypes.length > 0 ? optionTypes.join('\n') : ' [key: string]: any;'}
113
+ }
114
+
115
+ ${exitCodeComments ? `/**\n * Exit codes:\n${exitCodeComments}\n */` : ''}
116
+
117
+ /**
118
+ * Register the ${name} command on the program.
119
+ */
120
+ export function register${capitalize(name)}Command(program: Command): void {
121
+ ${hasSubcommands ? generateCommandWithSubcommands(name, description, subcommands, optionDefs) : generateLeafCommand(name, description, commandStr, optionDefs, actionParams, serviceRef, exitCodes)}
122
+ }
123
+ ${subcommandRegistrations}
124
+ `;
125
+ }
126
+
127
+ /**
128
+ * Engine-wired command handlers for known SpecVerse commands.
129
+ * Each generates the action body that calls the actual engine.
130
+ * Unknown commands get a stub.
131
+ */
132
+ const ENGINE_HANDLERS: Record<string, { imports: string; handler: string }> = {
133
+ validate: {
134
+ imports: `import { readFileSync, existsSync } from 'fs';
135
+ import { EngineRegistry } from '@specverse/engine-entities';
136
+ import type { ParserEngine } from '@specverse/types';`,
137
+ handler: `if (!existsSync(file)) {
138
+ console.error('File not found:', file);
139
+ process.exit(1);
140
+ }
141
+
142
+ // Discover and initialize parser engine
143
+ const registry = new EngineRegistry();
144
+ await registry.discover();
145
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
146
+ if (!parser) {
147
+ console.error('No parser engine found. Install @specverse/engine-parser.');
148
+ process.exit(1);
149
+ }
150
+ await parser.initialize();
151
+
152
+ const content = readFileSync(file, 'utf8');
153
+ const result = parser.parseContent(content, file);
154
+
155
+ if (result.errors.length > 0) {
156
+ console.error('Validation failed');
157
+ result.errors.forEach((e: string) => console.error(' ', e));
158
+ if (result.warnings && result.warnings.length > 0) {
159
+ console.warn('Warnings:');
160
+ result.warnings.forEach((w: string) => console.warn(' ', w));
161
+ }
162
+ process.exit(1);
163
+ }
164
+
165
+ if (options.json) {
166
+ console.log(JSON.stringify({ valid: true, warnings: result.warnings }, null, 2));
167
+ } else {
168
+ console.log('Validation successful');
169
+ if (result.warnings && result.warnings.length > 0) {
170
+ console.warn('Warnings:');
171
+ result.warnings.forEach((w: string) => console.warn(' ', w));
172
+ }
173
+ }`
174
+ },
175
+ infer: {
176
+ imports: `import { readFileSync, writeFileSync, existsSync } from 'fs';
177
+ import { EngineRegistry } from '@specverse/engine-entities';
178
+ import type { ParserEngine, InferenceEngine } from '@specverse/types';`,
179
+ handler: `if (!existsSync(file)) {
180
+ console.error('File not found:', file);
181
+ process.exit(1);
182
+ }
183
+
184
+ console.log('Running inference on', file, '...');
185
+
186
+ // Discover engines
187
+ const registry = new EngineRegistry();
188
+ await registry.discover();
189
+
190
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
191
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
192
+ await parser.initialize();
193
+
194
+ const inferEngine = registry.getEngineForCapability('infer') as InferenceEngine;
195
+ if (!inferEngine) { console.error('No inference engine found.'); process.exit(1); }
196
+ await inferEngine.initialize({ options: { verbose: options.verbose } });
197
+
198
+ const content = readFileSync(file, 'utf8');
199
+ const parseResult = parser.parseContent(content, file);
200
+
201
+ if (parseResult.errors.length > 0) {
202
+ console.error('Cannot infer from invalid specification:');
203
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
204
+ process.exit(1);
205
+ }
206
+
207
+ const ast = parseResult.ast!;
208
+ const inferResult = await inferEngine.infer(ast, {
209
+ generateControllers: true,
210
+ generateServices: true,
211
+ generateEvents: true,
212
+ generateViews: true,
213
+ generateDeployment: options.deployment || false,
214
+ verbose: options.verbose || false,
215
+ });
216
+
217
+ const outputFile = options.output || file.replace(/\\.specly$/, '-inferred.specly');
218
+ writeFileSync(outputFile, inferResult.yaml, 'utf8');
219
+ console.log('Inferred specification written to:', outputFile);`
220
+ },
221
+ realize: {
222
+ imports: `import { readFileSync, existsSync } from 'fs';
223
+ import { resolve } from 'path';
224
+ import { EngineRegistry } from '@specverse/engine-entities';
225
+ import type { ParserEngine, InferenceEngine, RealizeEngine } from '@specverse/types';`,
226
+ handler: `if (!existsSync(file)) {
227
+ console.error('File not found:', file);
228
+ process.exit(1);
229
+ }
230
+
231
+ // Discover engines
232
+ const registry = new EngineRegistry();
233
+ await registry.discover();
234
+
235
+ // Parse — let the parser engine handle its own schema
236
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
237
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
238
+ await parser.initialize();
239
+
240
+ const content = readFileSync(file, 'utf8');
241
+ const parseResult = parser.parseContent(content, file);
242
+ if (parseResult.errors.length > 0) {
243
+ console.error('Invalid spec:');
244
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
245
+ process.exit(1);
246
+ }
247
+
248
+ // Infer — let the inference engine handle its own rules
249
+ const inferEngine = registry.getEngineForCapability('infer') as InferenceEngine;
250
+ if (!inferEngine) { console.error('No inference engine found.'); process.exit(1); }
251
+ await inferEngine.initialize();
252
+ const inferResult = await inferEngine.infer(parseResult.ast!, {
253
+ generateControllers: true, generateServices: true,
254
+ generateEvents: true, generateViews: true,
255
+ });
256
+
257
+ // Load inferred YAML and flatten component data for realize
258
+ const yaml = await import('js-yaml');
259
+ const inferredYaml = yaml.load(inferResult.yaml) as any;
260
+ // Inference output: { components: { Name: { models, controllers, ... } } }
261
+ // realizeAll expects: { models: {...}, controllers: {...}, ... } (flat component data)
262
+ const componentName = Object.keys(inferredYaml?.components || {})[0];
263
+ const componentData = componentName ? inferredYaml.components[componentName] : {};
264
+
265
+ // Merge original spec's services/events/views that inference didn't generate
266
+ // (inference generates from models; explicit services in the spec are preserved here)
267
+ const origComponent = parseResult.ast!.components?.[0] || {};
268
+ for (const section of ['services', 'events', 'views']) {
269
+ const origData = (origComponent as any)[section];
270
+ if (origData && (!componentData[section] || JSON.stringify(componentData[section]) === '{}')) {
271
+ // Convert array form (from parser) to object form (for realize)
272
+ if (Array.isArray(origData)) {
273
+ componentData[section] = {};
274
+ for (const item of origData) {
275
+ componentData[section][item.name] = item;
276
+ }
277
+ } else {
278
+ componentData[section] = origData;
279
+ }
280
+ }
281
+ }
282
+
283
+ // Inject key as name into entity maps, and expand convention-format attributes
284
+ // Iterate all object-valued sections (not hardcoded — covers any entity type)
285
+ const entitySections = Object.keys(componentData).filter(k =>
286
+ componentData[k] && typeof componentData[k] === 'object' && !Array.isArray(componentData[k])
287
+ && !['version', 'description', 'name', 'componentName', 'commonDefinitions'].includes(k)
288
+ );
289
+ for (const section of entitySections) {
290
+ if (componentData[section] && typeof componentData[section] === 'object' && !Array.isArray(componentData[section])) {
291
+ for (const [key, value] of Object.entries(componentData[section])) {
292
+ if (value && typeof value === 'object') {
293
+ (value as any).name = key;
294
+ // Expand convention-format attributes: { attrName: "Type modifiers" } → [{ name, type, ... }]
295
+ if ((value as any).attributes && typeof (value as any).attributes === 'object' && !Array.isArray((value as any).attributes)) {
296
+ (value as any).attributes = Object.entries((value as any).attributes).map(([attrName, attrDef]: [string, any]) => {
297
+ if (typeof attrDef === 'string') {
298
+ const parts = attrDef.split(' ');
299
+ return { name: attrName, type: parts[0], required: parts.includes('required'), unique: parts.includes('unique'), auto: parts.find((p: string) => p.startsWith('auto='))?.split('=')[1] };
300
+ }
301
+ return { name: attrName, ...(typeof attrDef === 'object' ? attrDef : {}) };
302
+ });
303
+ }
304
+ // Expand convention-format relationships: { relName: { name, type, target } } → array
305
+ if ((value as any).relationships && typeof (value as any).relationships === 'object' && !Array.isArray((value as any).relationships)) {
306
+ (value as any).relationships = Object.entries((value as any).relationships).map(([relName, relDef]: [string, any]) => {
307
+ if (typeof relDef === 'object') return { name: relName, ...relDef };
308
+ return { name: relName };
309
+ });
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ const inferredSpec = { ...componentData, componentName };
316
+
317
+ // Realize — let the realize engine handle its own library
318
+ const manifestPath = options.manifest || resolve(process.cwd(), 'manifests/implementation.yaml');
319
+ if (!existsSync(manifestPath)) {
320
+ console.error('Manifest not found:', manifestPath);
321
+ process.exit(1);
322
+ }
323
+ const realizeEngine = registry.getEngineForCapability('realize') as RealizeEngine;
324
+ if (!realizeEngine) { console.error('No realize engine found.'); process.exit(1); }
325
+ await realizeEngine.initialize({ manifestPath, workingDir: process.cwd() });
326
+
327
+ const outputDir = options.output || resolve(process.cwd(), 'generated/code');
328
+ console.log('Realizing ' + type + ' from ' + file + '...');
329
+ await (realizeEngine as any).realizeAll(inferredSpec, outputDir);`
330
+ },
331
+ init: {
332
+ imports: `import { existsSync, mkdirSync, readdirSync, statSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
333
+ import { resolve, join, dirname } from 'path';
334
+ import { fileURLToPath } from 'url';`,
335
+ handler: `const __fn = fileURLToPath(import.meta.url);
336
+ const __dn = dirname(__fn);
337
+
338
+ // Resolve templates from engine package assets
339
+ const templateCandidates = [
340
+ resolve(__dn, '../../../templates'), // Shipped via realize
341
+ resolve(__dn, '../../templates'), // Alternative layout
342
+ ];
343
+ // Also try node_modules
344
+ for (let i = 2; i <= 5; i++) {
345
+ const up = Array(i).fill('..').join('/');
346
+ templateCandidates.push(resolve(__dn, up, 'node_modules/@specverse/engine-realize/assets/templates'));
347
+ }
348
+
349
+ let templatesDir = null;
350
+ for (const candidate of templateCandidates) {
351
+ if (existsSync(candidate) && existsSync(join(candidate, 'default'))) {
352
+ templatesDir = candidate;
353
+ break;
354
+ }
355
+ }
356
+
357
+ if (!templatesDir) {
358
+ console.error('Templates not found. Run specverse realize all first, or install @specverse/engine-realize.');
359
+ process.exit(1);
360
+ }
361
+
362
+ if (options.list) {
363
+ const templates = readdirSync(templatesDir).filter(
364
+ d => statSync(join(templatesDir!, d)).isDirectory()
365
+ );
366
+ console.log('Available templates:');
367
+ templates.forEach(t => console.log(' ' + t));
368
+ return;
369
+ }
370
+
371
+ const projectName = name || 'my-project';
372
+ const templateName = options.template || 'default';
373
+ const templateDir = join(templatesDir, templateName);
374
+
375
+ if (!existsSync(templateDir)) {
376
+ console.error('Template not found: ' + templateName);
377
+ console.error('Available: ' + readdirSync(templatesDir).filter(
378
+ d => statSync(join(templatesDir!, d)).isDirectory()
379
+ ).join(', '));
380
+ process.exit(1);
381
+ }
382
+
383
+ const destDir = resolve(process.cwd(), projectName);
384
+ if (existsSync(destDir)) {
385
+ console.error('Directory already exists: ' + destDir);
386
+ process.exit(1);
387
+ }
388
+
389
+ // Copy template with variable substitution
390
+ const kebab = projectName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
391
+ const component = projectName.charAt(0).toUpperCase() + projectName.slice(1);
392
+ const vars: Record<string, string> = {
393
+ '{{PROJECT_NAME}}': projectName,
394
+ '{{projectName}}': projectName,
395
+ '{{projectNameKebab}}': kebab,
396
+ '{{componentName}}': component,
397
+ '{{DB_USER}}': 'postgres',
398
+ '{{DB_PASSWORD}}': 'postgres',
399
+ };
400
+
401
+ function copyDir(src: string, dest: string) {
402
+ mkdirSync(dest, { recursive: true });
403
+ for (const item of readdirSync(src)) {
404
+ const srcPath = join(src, item);
405
+ let destName = item;
406
+ if (item === 'gitignore') destName = '.gitignore';
407
+ if (item === 'dot.env.example') destName = '.env.example';
408
+ const destPath = join(dest, destName);
409
+
410
+ if (statSync(srcPath).isDirectory()) {
411
+ copyDir(srcPath, destPath);
412
+ } else {
413
+ let content = readFileSync(srcPath, 'utf8');
414
+ for (const [key, val] of Object.entries(vars)) {
415
+ content = content.split(key).join(val);
416
+ }
417
+ writeFileSync(destPath, content);
418
+ }
419
+ }
420
+ }
421
+
422
+ copyDir(templateDir, destDir);
423
+ console.log('Project created: ' + destDir);
424
+ console.log('Template: ' + templateName);
425
+ console.log('');
426
+ console.log('Next steps:');
427
+ console.log(' cd ' + projectName);
428
+ console.log(' npm install');
429
+ console.log(' specverse validate specs/main.specly');`
430
+ },
431
+ // === gen subcommands ===
432
+ 'gen.diagrams': {
433
+ imports: `import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
434
+ import { resolve, dirname, basename, join } from 'path';
435
+ import { fileURLToPath } from 'url';
436
+ import { EngineRegistry } from '@specverse/engine-entities';
437
+ import type { ParserEngine } from '@specverse/types';`,
438
+ handler: `const registry = new EngineRegistry();
439
+ await registry.discover();
440
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
441
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
442
+ await parser.initialize();
443
+
444
+ const content = readFileSync(file, 'utf8');
445
+ const parseResult = parser.parseContent(content, file);
446
+ if (parseResult.errors.length > 0) {
447
+ console.error('Invalid spec:');
448
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
449
+ process.exit(1);
450
+ }
451
+
452
+ const gen = registry.getEngineForCapability('generate-diagrams') as any;
453
+ if (!gen) { console.error('No generators engine found.'); process.exit(1); }
454
+ await gen.initialize();
455
+
456
+ const diagrams = await gen.generateDiagrams(parseResult.ast!, { type: options.type || 'all' });
457
+ const outputDir = options.output || basename(file, '.specly') + '-diagrams';
458
+ if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
459
+ for (const [diagramType, diagramContent] of diagrams.entries()) {
460
+ writeFileSync(join(outputDir, diagramType + '.mmd'), diagramContent);
461
+ console.log(' ' + diagramType);
462
+ }
463
+ console.log('Generated ' + diagrams.size + ' diagrams in: ' + outputDir);`
464
+ },
465
+ 'gen.docs': {
466
+ imports: `import { readFileSync, writeFileSync, existsSync } from 'fs';
467
+ import { resolve, dirname, basename } from 'path';
468
+ import { fileURLToPath } from 'url';
469
+ import { EngineRegistry } from '@specverse/engine-entities';
470
+ import type { ParserEngine } from '@specverse/types';`,
471
+ handler: `const registry = new EngineRegistry();
472
+ await registry.discover();
473
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
474
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
475
+ await parser.initialize();
476
+
477
+ const content = readFileSync(file, 'utf8');
478
+ const parseResult = parser.parseContent(content, file);
479
+ if (parseResult.errors.length > 0) {
480
+ console.error('Invalid spec:');
481
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
482
+ process.exit(1);
483
+ }
484
+
485
+ const gen = registry.getEngineForCapability('generate-docs') as any;
486
+ if (!gen) { console.error('No generators engine found.'); process.exit(1); }
487
+ await gen.initialize();
488
+
489
+ const docs = await gen.generateDocs(parseResult.ast!, { format: options.format || 'markdown' });
490
+ const ext = options.format === 'html' ? '.html' : '.md';
491
+ const outputFile = options.output || basename(file, '.specly') + '-docs' + ext;
492
+ writeFileSync(outputFile, docs);
493
+ console.log('Documentation generated: ' + outputFile);`
494
+ },
495
+ 'gen.uml': {
496
+ imports: `import { readFileSync, writeFileSync, existsSync } from 'fs';
497
+ import { resolve, dirname, basename } from 'path';
498
+ import { fileURLToPath } from 'url';
499
+ import { EngineRegistry } from '@specverse/engine-entities';
500
+ import type { ParserEngine } from '@specverse/types';`,
501
+ handler: `const registry = new EngineRegistry();
502
+ await registry.discover();
503
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
504
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
505
+ await parser.initialize();
506
+
507
+ const content = readFileSync(file, 'utf8');
508
+ const parseResult = parser.parseContent(content, file);
509
+ if (parseResult.errors.length > 0) {
510
+ console.error('Invalid spec:');
511
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
512
+ process.exit(1);
513
+ }
514
+
515
+ const gen = registry.getEngineForCapability('generate-uml') as any;
516
+ if (!gen) { console.error('No generators engine found.'); process.exit(1); }
517
+ await gen.initialize();
518
+
519
+ const uml = await gen.generateUML(parseResult.ast!, { type: options.type || 'all' });
520
+ const outputFile = basename(file, '.specly') + '-uml.puml';
521
+ writeFileSync(outputFile, uml);
522
+ console.log('UML generated: ' + outputFile);`
523
+ },
524
+ // === dev subcommands ===
525
+ 'dev.format': {
526
+ imports: `import { readFileSync, writeFileSync, existsSync } from 'fs';
527
+ import { resolve, dirname } from 'path';
528
+ import { fileURLToPath } from 'url';
529
+ import { EngineRegistry } from '@specverse/engine-entities';
530
+ import type { ParserEngine } from '@specverse/types';`,
531
+ handler: `const registry = new EngineRegistry();
532
+ await registry.discover();
533
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
534
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
535
+ await parser.initialize();
536
+
537
+ const content = readFileSync(file, 'utf8');
538
+ const result = parser.parseContent(content, file);
539
+ if (result.errors.length > 0) {
540
+ console.error('Cannot format invalid spec:');
541
+ result.errors.forEach((e: string) => console.error(' ', e));
542
+ process.exit(1);
543
+ }
544
+ const yaml = await import('js-yaml');
545
+ const formatted = yaml.dump(yaml.load(content), { lineWidth: 120, noRefs: true });
546
+ if (options.write) {
547
+ writeFileSync(file, formatted);
548
+ console.log('Formatted and saved: ' + file);
549
+ } else {
550
+ console.log(formatted);
551
+ }`
552
+ },
553
+ 'dev.watch': {
554
+ imports: `import { readFileSync, existsSync, watch } from 'fs';
555
+ import { resolve, dirname } from 'path';
556
+ import { fileURLToPath } from 'url';
557
+ import { EngineRegistry } from '@specverse/engine-entities';
558
+ import type { ParserEngine } from '@specverse/types';`,
559
+ handler: `const registry = new EngineRegistry();
560
+ await registry.discover();
561
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
562
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
563
+ await parser.initialize();
564
+
565
+ console.log('Watching ' + file + ' for changes...');
566
+ const doValidate = () => {
567
+ try {
568
+ const c = readFileSync(file, 'utf8');
569
+ const r = parser.parseContent(c, file);
570
+ if (r.errors.length > 0) {
571
+ console.log('[' + new Date().toLocaleTimeString() + '] FAILED');
572
+ r.errors.forEach((e: string) => console.error(' ', e));
573
+ } else {
574
+ console.log('[' + new Date().toLocaleTimeString() + '] Valid');
575
+ }
576
+ } catch (e: any) { console.error('Watch error:', e.message); }
577
+ };
578
+ doValidate();
579
+ watch(file, doValidate);
580
+ await new Promise(() => {});`
581
+ },
582
+ 'dev.quick': {
583
+ imports: `import { readFileSync, existsSync } from 'fs';
584
+ import { resolve, dirname } from 'path';
585
+ import { fileURLToPath } from 'url';
586
+ import { EngineRegistry } from '@specverse/engine-entities';
587
+ import type { ParserEngine } from '@specverse/types';`,
588
+ handler: `const registry = new EngineRegistry();
589
+ await registry.discover();
590
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
591
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
592
+ await parser.initialize();
593
+
594
+ const content = readFileSync(file, 'utf8');
595
+ const result = parser.parseContent(content, file);
596
+ if (result.errors.length > 0) {
597
+ console.error('Quick check: FAILED');
598
+ result.errors.forEach((e: string) => console.error(' ', e));
599
+ process.exit(1);
600
+ }
601
+ console.log('Quick check: OK');`
602
+ },
603
+ // === cache command (leaf, not subcommand) ===
604
+ cache: {
605
+ imports: `import { EngineRegistry } from '@specverse/engine-entities';
606
+ import type { ParserEngine } from '@specverse/types';`,
607
+ handler: `const registry = new EngineRegistry();
608
+ await registry.discover();
609
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
610
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
611
+ await parser.initialize();
612
+
613
+ // Access ImportResolver cache via parser
614
+ const resolverModule = await import('@specverse/engine-parser');
615
+ const resolver = new resolverModule.ImportResolver({ basePath: process.cwd() });
616
+
617
+ const cacheDir = (resolver as any).getCacheDir ? (resolver as any).getCacheDir() : null;
618
+ if (options.stats) {
619
+ console.log('Cache directory:', cacheDir || 'default');
620
+ } else if (options.list) {
621
+ if (cacheDir) {
622
+ const fs = await import('fs');
623
+ if (fs.existsSync(cacheDir)) {
624
+ const items = fs.readdirSync(cacheDir);
625
+ if (items.length === 0) { console.log('Cache is empty'); }
626
+ else { items.forEach((item: string) => console.log(' ', item)); }
627
+ } else { console.log('Cache directory does not exist'); }
628
+ } else { console.log('No cache directory configured'); }
629
+ } else if (options.clear) {
630
+ if (resolver.clearCache) { resolver.clearCache(); }
631
+ console.log('Cache cleared');
632
+ } else {
633
+ console.log('Use --stats, --list, or --clear');
634
+ }`
635
+ },
636
+ // === ai subcommands ===
637
+ 'ai.docs': {
638
+ imports: `import { readFileSync, writeFileSync, existsSync } from 'fs';
639
+ import { resolve, dirname, basename } from 'path';
640
+ import { fileURLToPath } from 'url';
641
+ import { EngineRegistry } from '@specverse/engine-entities';
642
+ import type { ParserEngine } from '@specverse/types';`,
643
+ handler: `const registry = new EngineRegistry();
644
+ await registry.discover();
645
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
646
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
647
+ await parser.initialize();
648
+
649
+ const content = readFileSync(file, 'utf8');
650
+ const parseResult = parser.parseContent(content, file);
651
+ if (parseResult.errors.length > 0) {
652
+ console.error('Invalid spec:');
653
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
654
+ process.exit(1);
655
+ }
656
+
657
+ const aiEngine = registry.getEngineForCapability('ai-prompts') as any;
658
+ if (!aiEngine) {
659
+ console.error('AI engine not available. Install @specverse/engine-ai.');
660
+ process.exit(1);
661
+ }
662
+ await aiEngine.initialize({ provider: options.provider });
663
+ const prompt = await aiEngine.generatePrompt(parseResult.ast!, { type: 'docs' });
664
+ const outputFile = options.output || basename(file, '.specly') + '-ai-docs.md';
665
+ writeFileSync(outputFile, prompt);
666
+ console.log('AI documentation prompt generated: ' + outputFile);`
667
+ },
668
+ 'ai.suggest': {
669
+ imports: `import { readFileSync, existsSync } from 'fs';
670
+ import { resolve, dirname } from 'path';
671
+ import { fileURLToPath } from 'url';
672
+ import { EngineRegistry } from '@specverse/engine-entities';
673
+ import type { ParserEngine } from '@specverse/types';`,
674
+ handler: `const registry = new EngineRegistry();
675
+ await registry.discover();
676
+ const parser = registry.getEngineForCapability('parse') as ParserEngine;
677
+ if (!parser) { console.error('No parser engine found.'); process.exit(1); }
678
+ await parser.initialize();
679
+
680
+ const content = readFileSync(file, 'utf8');
681
+ const parseResult = parser.parseContent(content, file);
682
+ if (parseResult.errors.length > 0) {
683
+ console.error('Invalid spec:');
684
+ parseResult.errors.forEach((e: string) => console.error(' ', e));
685
+ process.exit(1);
686
+ }
687
+
688
+ const aiEngine = registry.getEngineForCapability('ai-suggestions') as any;
689
+ if (!aiEngine) {
690
+ console.error('AI engine not available. Install @specverse/engine-ai.');
691
+ process.exit(1);
692
+ }
693
+ await aiEngine.initialize();
694
+ const suggestions = await aiEngine.suggest(parseResult.ast!);
695
+ if (suggestions.length === 0) {
696
+ console.log('No suggestions — spec looks good!');
697
+ } else {
698
+ const warnings = suggestions.filter((s: any) => s.severity === 'warning');
699
+ const improvements = suggestions.filter((s: any) => s.severity === 'improvement');
700
+ const info = suggestions.filter((s: any) => s.severity === 'info');
701
+ if (warnings.length > 0) {
702
+ console.log('\\nWarnings:');
703
+ warnings.forEach((s: any) => console.log(' [' + s.target + '] ' + s.description));
704
+ }
705
+ if (improvements.length > 0) {
706
+ console.log('\\nSuggested improvements:');
707
+ improvements.forEach((s: any) => console.log(' [' + s.target + '] ' + s.description));
708
+ }
709
+ if (info.length > 0) {
710
+ console.log('\\nInfo:');
711
+ info.forEach((s: any) => console.log(' [' + s.target + '] ' + s.description));
712
+ }
713
+ console.log('\\n' + suggestions.length + ' suggestion(s): ' + warnings.length + ' warning, ' + improvements.length + ' improvement, ' + info.length + ' info');
714
+ }`
715
+ },
716
+ 'ai.template': {
717
+ imports: `import { writeFileSync } from 'fs';
718
+ import { EngineRegistry } from '@specverse/engine-entities';`,
719
+ handler: `const registry = new EngineRegistry();
720
+ await registry.discover();
721
+ const aiEngine = registry.getEngineForCapability('ai-templates') as any;
722
+ if (!aiEngine) {
723
+ console.error('AI engine not available. Install @specverse/engine-ai.');
724
+ process.exit(1);
725
+ }
726
+ await aiEngine.initialize();
727
+ const template = await aiEngine.template(operation, { config: options.config });
728
+ if (options.output) {
729
+ writeFileSync(options.output, template);
730
+ console.log('Template written to: ' + options.output);
731
+ } else {
732
+ console.log(template);
733
+ }`
734
+ },
735
+ // === session subcommands ===
736
+ 'session.create': {
737
+ imports: `import { EngineRegistry } from '@specverse/engine-entities';`,
738
+ handler: `const registry = new EngineRegistry();
739
+ await registry.discover();
740
+ const aiEngine = registry.getEngineForCapability('ai-prompts') as any;
741
+ if (!aiEngine) { console.error('AI engine not available.'); process.exit(1); }
742
+ await aiEngine.initialize();
743
+
744
+ const { SessionManager } = await import('@specverse/engine-ai');
745
+ const manager = new SessionManager();
746
+ const session = await manager.create({ name: options.name, pver: options.pver });
747
+
748
+ if (options.json) {
749
+ console.log(JSON.stringify(session, null, 2));
750
+ } else {
751
+ console.log('Session created: ' + session.sessionId);
752
+ console.log('Status: ' + session.status);
753
+ if (session.name) console.log('Name: ' + session.name);
754
+ console.log('Prompt version: ' + session.pver);
755
+ }`
756
+ },
757
+ 'session.list': {
758
+ imports: ``,
759
+ handler: `const { SessionManager } = await import('@specverse/engine-ai');
760
+ const manager = new SessionManager();
761
+ const sessions = await manager.list({ all: options.all });
762
+
763
+ if (options.json) {
764
+ console.log(JSON.stringify(sessions, null, 2));
765
+ } else if (sessions.length === 0) {
766
+ console.log('No sessions found. Create one with: specverse session create');
767
+ } else {
768
+ console.log('Sessions (' + sessions.length + '):');
769
+ for (const s of sessions) {
770
+ console.log(' ' + (s.status === 'active' ? '*' : ' ') + ' ' + s.sessionId + (s.name ? ' (' + s.name + ')' : '') + ' — ' + s.status + ', ' + s.jobsProcessed + ' jobs');
771
+ }
772
+ }`
773
+ },
774
+ 'session.delete': {
775
+ imports: ``,
776
+ handler: `const { SessionManager } = await import('@specverse/engine-ai');
777
+ const manager = new SessionManager();
778
+ await manager.delete(sessionId, { force: options.force });
779
+ console.log('Session deleted: ' + sessionId);`
780
+ },
781
+ 'session.submit': {
782
+ imports: ``,
783
+ handler: `const { SessionManager } = await import('@specverse/engine-ai');
784
+ const manager = new SessionManager();
785
+ const job = await manager.submit(sessionId, requirements, {
786
+ jobId: options.jobId,
787
+ outputPath: options.output,
788
+ operation: options.operation,
789
+ });
790
+ console.log('Job submitted: ' + job.jobId);
791
+ console.log('Status: ' + job.status);
792
+ if (options.output) console.log('Output: ' + options.output);`
793
+ },
794
+ 'session.status': {
795
+ imports: ``,
796
+ handler: `const { SessionManager } = await import('@specverse/engine-ai');
797
+ const manager = new SessionManager();
798
+ const status = await manager.status(id);
799
+ if (options.json) {
800
+ console.log(JSON.stringify(status, null, 2));
801
+ } else {
802
+ console.log('ID: ' + (status.sessionId || status.jobId));
803
+ console.log('Status: ' + status.status);
804
+ if (status.created) console.log('Created: ' + new Date(status.created).toLocaleString());
805
+ if (status.jobsProcessed !== undefined) console.log('Jobs: ' + status.jobsProcessed);
806
+ }`
807
+ },
808
+ 'session.process': {
809
+ imports: ``,
810
+ handler: `const { SessionManager } = await import('@specverse/engine-ai');
811
+ const manager = new SessionManager();
812
+ console.log('Processing job: ' + jobId);
813
+ await manager.process(jobId);
814
+ console.log('Job processed successfully');`
815
+ },
816
+ };
817
+
818
+ function generateLeafCommand(
819
+ name: string,
820
+ description: string,
821
+ commandStr: string,
822
+ optionDefs: string[],
823
+ actionParams: string,
824
+ serviceRef: string | undefined,
825
+ exitCodes: Record<string, string>
826
+ ): string {
827
+ // Check for engine-wired handler
828
+ const engineHandler = ENGINE_HANDLERS[name];
829
+ const handler = engineHandler
830
+ ? engineHandler.handler
831
+ : serviceRef
832
+ ? `const service = new ${serviceRef}();
833
+ const result = await service.execute(${actionParams.includes(':') ? '{ ' + actionParams.split(',').map(p => p.trim().split(':')[0].trim()).join(', ') + ', ...options }' : 'options'});
834
+ console.log(result);`
835
+ : `console.log('Executing ${name}...');
836
+ // TODO: Wire to service`;
837
+
838
+ return `const cmd = program
839
+ .command('${commandStr}')
840
+ .description('${description}')
841
+ ${optionDefs.join('\n')}
842
+ .action(async (${actionParams}) => {
843
+ try {
844
+ ${handler}
845
+ } catch (error: any) {
846
+ console.error('Error:', error.message);
847
+ process.exit(${Object.keys(exitCodes).find(k => k !== '0') || '1'});
848
+ }
849
+ });`;
850
+ }
851
+
852
+ function generateCommandWithSubcommands(
853
+ name: string,
854
+ description: string,
855
+ subcommands: Record<string, any>,
856
+ optionDefs: string[]
857
+ ): string {
858
+ const subcmdRegistrations = Object.entries(subcommands).map(([subName, subDef]) => {
859
+ const subDesc = subDef.description || '';
860
+ const subArgs = subDef.arguments || {};
861
+ const subFlags = subDef.flags || {};
862
+
863
+ const positionalStr = Object.entries(subArgs)
864
+ .filter(([_, a]: [string, any]) => a.positional)
865
+ .map(([n, a]: [string, any]) => a.required ? `<${n}>` : `[${n}]`)
866
+ .join(' ');
867
+
868
+ const subCmdStr = positionalStr ? `${subName} ${positionalStr}` : subName;
869
+
870
+ const subOptionDefs = Object.entries(subFlags).map(([flagName, flag]: [string, any]) => {
871
+ const alias = flag.alias ? `${flag.alias}, ` : '';
872
+ const flagType = flag.type?.toLowerCase();
873
+ const valuePart = flagType === 'boolean' ? '' : ` <${flagName.replace(/^--/, '')}>`;
874
+ const defaultVal = flag.default !== undefined ? `, ${JSON.stringify(flag.default)}` : '';
875
+ return ` .option('${alias}${flagName}${valuePart}', '${flag.description || flagName}'${defaultVal})`;
876
+ });
877
+
878
+ // Look up engine handler for this subcommand
879
+ const handlerKey = `${name}.${subName}`;
880
+ const engineHandler = ENGINE_HANDLERS[handlerKey];
881
+
882
+ // Build action parameters from subcommand args
883
+ const subArgTypes = Object.entries(subArgs)
884
+ .filter(([_, a]: [string, any]) => a.positional)
885
+ .map(([n, a]: [string, any]) => `${n}: ${mapArgTypeToTS(a.type)}`);
886
+ const subActionParams = subArgTypes.length > 0
887
+ ? subArgTypes.join(', ') + ', options: any'
888
+ : 'options: any';
889
+
890
+ const handler = engineHandler
891
+ ? engineHandler.handler
892
+ : `console.log('${name} ${subName}: not yet implemented via engine');`;
893
+
894
+ return `
895
+ cmd
896
+ .command('${subCmdStr}')
897
+ .description('${subDesc}')
898
+ ${subOptionDefs.join('\n')}
899
+ .action(async (${subActionParams}) => {
900
+ try {
901
+ ${handler}
902
+ } catch (error: any) {
903
+ console.error('Error:', error.message);
904
+ process.exit(1);
905
+ }
906
+ });`;
907
+ });
908
+
909
+ return `const cmd = program
910
+ .command('${name}')
911
+ .description('${description}');
912
+ ${subcmdRegistrations.join('\n')}`;
913
+ }
914
+
915
+ function generateSubcommandRegistrations(
916
+ _parentName: string,
917
+ _subcommands: Record<string, any>
918
+ ): string {
919
+ return ''; // Subcommands are registered inline
920
+ }
921
+
922
+ function mapFlagTypeToTS(type: string): string {
923
+ if (!type) return 'string';
924
+ const lower = type.toLowerCase();
925
+ if (lower === 'boolean') return 'boolean';
926
+ if (lower === 'number' || lower === 'integer') return 'number';
927
+ return 'string';
928
+ }
929
+
930
+ function mapArgTypeToTS(type: string): string {
931
+ if (!type) return 'string';
932
+ const lower = type.toLowerCase();
933
+ if (lower === 'filepath' || lower === 'string') return 'string';
934
+ if (lower === 'number' || lower === 'integer') return 'number';
935
+ if (lower === 'boolean') return 'boolean';
936
+ return 'string';
937
+ }
938
+
939
+ function capitalize(str: string): string {
940
+ return str.charAt(0).toUpperCase() + str.slice(1);
941
+ }
942
+
943
+ /**
944
+ * Deduplicate import statements across multiple handler import blocks.
945
+ * Merges named imports from the same module and removes exact duplicates.
946
+ */
947
+ function deduplicateImports(importBlocks: string[]): string {
948
+ const seen = new Map<string, Set<string>>(); // module -> set of named imports
949
+ const typeImports = new Map<string, Set<string>>(); // module -> set of type imports
950
+ const rawLines = new Set<string>(); // non-mergeable lines
951
+
952
+ for (const block of importBlocks) {
953
+ for (const line of block.split('\n').map(l => l.trim()).filter(l => l)) {
954
+ // Match: import { X, Y } from 'module';
955
+ const namedMatch = line.match(/^import\s+\{\s*(.+?)\s*\}\s+from\s+'(.+?)';?$/);
956
+ if (namedMatch) {
957
+ const names = namedMatch[1].split(',').map(n => n.trim());
958
+ const mod = namedMatch[2];
959
+ if (!seen.has(mod)) seen.set(mod, new Set());
960
+ names.forEach(n => seen.get(mod)!.add(n));
961
+ continue;
962
+ }
963
+ // Match: import type { X } from 'module';
964
+ const typeMatch = line.match(/^import\s+type\s+\{\s*(.+?)\s*\}\s+from\s+'(.+?)';?$/);
965
+ if (typeMatch) {
966
+ const names = typeMatch[1].split(',').map(n => n.trim());
967
+ const mod = typeMatch[2];
968
+ if (!typeImports.has(mod)) typeImports.set(mod, new Set());
969
+ names.forEach(n => typeImports.get(mod)!.add(n));
970
+ continue;
971
+ }
972
+ rawLines.add(line);
973
+ }
974
+ }
975
+
976
+ const result: string[] = [];
977
+ for (const [mod, names] of seen.entries()) {
978
+ result.push(`import { ${[...names].join(', ')} } from '${mod}';`);
979
+ }
980
+ for (const [mod, names] of typeImports.entries()) {
981
+ // Remove type imports that are already in regular imports
982
+ const regularNames = seen.get(mod) || new Set();
983
+ const typeOnly = [...names].filter(n => !regularNames.has(n));
984
+ if (typeOnly.length > 0) {
985
+ result.push(`import type { ${typeOnly.join(', ')} } from '${mod}';`);
986
+ }
987
+ }
988
+ for (const line of rawLines) {
989
+ result.push(line);
990
+ }
991
+ return result.join('\n');
992
+ }