@polymorphism-tech/morph-spec 4.11.1 → 4.13.0

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 (317) hide show
  1. package/README.md +21 -29
  2. package/bin/morph-spec.js +41 -24
  3. package/bin/task-manager.js +962 -1341
  4. package/bin/validate.js +1 -1
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +10 -11
  7. package/docs/QUICKSTART.md +15 -15
  8. package/framework/CLAUDE.md +5 -2
  9. package/framework/agents/README.md +3 -4
  10. package/framework/agents/ai-agents/ai-system-architect.md +6 -10
  11. package/framework/agents/architecture/seo-growth-hacker.md +166 -326
  12. package/framework/agents/architecture/standards-architect.md +21 -31
  13. package/framework/agents/backend/dotnet-senior.md +160 -86
  14. package/framework/agents/backend/ef-modeler.md +2 -6
  15. package/framework/agents/backend/hangfire-orchestrator.md +8 -9
  16. package/framework/agents/backend/ms-agent-expert.md +1 -1
  17. package/framework/agents/frontend/nextjs-expert.md +198 -133
  18. package/framework/agents/frontend/ui-ux-designer.md +226 -267
  19. package/framework/agents/infrastructure/infra-architect.md +191 -27
  20. package/framework/agents/integrations/clerk-auth.md +19 -26
  21. package/framework/agents/integrations/hangfire-integration.md +1 -4
  22. package/framework/agents.json +226 -1454
  23. package/framework/commands/morph-apply.md +4 -4
  24. package/framework/commands/morph-preflight.md +4 -26
  25. package/framework/commands/morph-proposal.md +19 -19
  26. package/framework/commands/morph-status.md +2 -2
  27. package/framework/commands/morph-troubleshoot.md +6 -13
  28. package/framework/hooks/README.md +1 -6
  29. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +122 -0
  30. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +4 -6
  31. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +12 -7
  32. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +2 -2
  33. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +2 -2
  34. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +5 -10
  35. package/framework/hooks/claude-code/session-start/inject-morph-context.js +63 -30
  36. package/framework/hooks/claude-code/session-start/resume-context.js +77 -0
  37. package/framework/hooks/claude-code/statusline.py +4 -5
  38. package/framework/hooks/claude-code/stop/validate-completion.js +4 -4
  39. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +8 -8
  40. package/framework/hooks/shared/compact-restore.js +28 -5
  41. package/framework/hooks/shared/dispatch-helpers.js +17 -0
  42. package/framework/hooks/shared/phase-utils.js +4 -8
  43. package/framework/hooks/shared/state-reader.js +34 -3
  44. package/framework/phases.json +7 -45
  45. package/framework/rules/ai-agents-standards.md +15 -0
  46. package/framework/rules/asaas-standards.md +13 -0
  47. package/framework/rules/clerk-standards.md +13 -0
  48. package/framework/rules/csharp-standards.md +8 -4
  49. package/framework/rules/data-standards.md +18 -0
  50. package/framework/rules/dotnet-templates.md +87 -0
  51. package/framework/rules/frontend-standards.md +13 -13
  52. package/framework/rules/hangfire-standards.md +13 -0
  53. package/framework/rules/infrastructure-standards.md +27 -16
  54. package/framework/rules/morph-workflow.md +73 -13
  55. package/framework/rules/nextjs-standards.md +1 -0
  56. package/framework/rules/nextjs-templates.md +40 -0
  57. package/framework/rules/resend-standards.md +12 -0
  58. package/framework/rules/supabase-standards.md +29 -0
  59. package/framework/skills/README.md +6 -8
  60. package/framework/skills/level-0-meta/mcp-registry.json +21 -99
  61. package/framework/{standards/ai-agents/team-orchestration.md → skills/level-0-meta/morph-agent-teams/SKILL.md} +472 -479
  62. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +28 -1
  63. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  64. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +1 -5
  65. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +1 -1
  66. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +23 -41
  67. package/framework/skills/level-0-meta/morph-init/SKILL.md +18 -14
  68. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +225 -0
  69. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +20 -0
  70. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +4 -8
  71. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +15 -59
  72. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +7 -19
  73. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +8 -8
  74. package/framework/skills/level-1-workflows/README.md +1 -1
  75. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +49 -50
  76. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +58 -124
  77. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +5 -50
  78. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +1 -1
  79. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +127 -159
  80. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -3
  81. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +2 -2
  82. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +1 -1
  83. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +17 -13
  84. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +22 -40
  85. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -2
  86. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +5 -5
  87. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +275 -61
  88. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  89. package/framework/squad-templates/backend-only.json +6 -6
  90. package/framework/squad-templates/frontend-only.json +17 -8
  91. package/framework/squad-templates/full-stack.json +29 -24
  92. package/framework/standards/STANDARDS.json +1021 -2338
  93. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +200 -0
  94. package/framework/standards/backend/dotnet/result-pattern.md +213 -0
  95. package/framework/standards/backend/dotnet/scalar-ui.md +172 -0
  96. package/framework/standards/backend/dotnet/scrutor.md +158 -0
  97. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -0
  98. package/framework/standards/frontend/nextjs/motion-patterns.md +1 -1
  99. package/framework/standards/infrastructure/supabase/mcp-setup.md +83 -193
  100. package/framework/standards/integration/mcp/mcp-tools.md +91 -77
  101. package/framework/templates/IDE-SNIPPETS.md +2 -6
  102. package/framework/templates/README.md +709 -814
  103. package/framework/templates/REGISTRY.json +223 -762
  104. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -0
  105. package/framework/templates/code/dotnet/abstractions/error.cs +60 -0
  106. package/framework/templates/code/dotnet/abstractions/handler.cs +12 -0
  107. package/framework/templates/code/dotnet/abstractions/result.cs +74 -0
  108. package/framework/templates/code/dotnet/backend/repository.cs +148 -141
  109. package/framework/templates/code/dotnet/constants/api-tags.cs +6 -0
  110. package/framework/templates/code/dotnet/exceptions/exception-handler.cs +32 -0
  111. package/framework/templates/code/dotnet/extensions/database-extensions.cs +20 -0
  112. package/framework/templates/code/dotnet/extensions/handler-registration.cs +38 -0
  113. package/framework/templates/code/dotnet/extensions/health-checks.cs +24 -0
  114. package/framework/templates/code/dotnet/extensions/map-endpoints.cs +35 -0
  115. package/framework/templates/code/dotnet/extensions/result-extensions.cs +29 -0
  116. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -0
  117. package/framework/templates/code/dotnet/feature/entity.cs.hbs +8 -0
  118. package/framework/templates/code/dotnet/feature/errors.cs.hbs +8 -0
  119. package/framework/templates/code/dotnet/feature/handler.cs.hbs +24 -0
  120. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -0
  121. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -0
  122. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -0
  123. package/framework/templates/code/dotnet/jobs/job.cs +21 -17
  124. package/framework/templates/code/dotnet/pipelines/logging-decorator.cs +31 -0
  125. package/framework/templates/code/dotnet/pipelines/validation-decorator.cs +97 -0
  126. package/framework/templates/code/dotnet/test.cs +192 -106
  127. package/framework/templates/context/ARCHITECTURE.md +89 -0
  128. package/framework/templates/context/CONTEXT-FEATURE.md +2 -2
  129. package/framework/templates/context/CONTEXT.md +180 -181
  130. package/framework/templates/context/design-system.md +342 -211
  131. package/framework/templates/docs/onboarding.md +2 -3
  132. package/framework/templates/docs/proposal.md +171 -182
  133. package/framework/templates/docs/spec.md +1 -3
  134. package/framework/templates/docs/ui-components.md +0 -72
  135. package/framework/templates/examples/design-system-examples.md +4 -4
  136. package/framework/templates/feature/decisions.md +0 -66
  137. package/framework/templates/feature/recap.md +1 -2
  138. package/framework/templates/infrastructure/github/README.md +56 -593
  139. package/framework/templates/integrations/clerk-config.cs +0 -41
  140. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +2 -2
  141. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -2
  142. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +83 -126
  143. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +2 -2
  144. package/framework/templates/project/validate.js +2 -4
  145. package/framework/templates/ui/design-system.css +1 -1
  146. package/framework/workflows/README.md +8 -8
  147. package/framework/workflows/configs/fast-track.json +51 -0
  148. package/framework/workflows/configs/full.json +68 -0
  149. package/framework/workflows/configs/standard.json +7 -3
  150. package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +1 -1
  151. package/framework/workflows/docs/enforcement-pipeline.md +18 -19
  152. package/framework/workflows/docs/full-morph.md +69 -76
  153. package/framework/workflows/docs/standard.md +49 -44
  154. package/framework/workflows/docs/ui-refresh.md +1 -1
  155. package/package.json +3 -3
  156. package/src/commands/agents/dispatch-agents.js +55 -116
  157. package/src/commands/context/context-refresh.js +79 -0
  158. package/src/commands/mcp/mcp-setup.js +28 -6
  159. package/src/commands/phase/phase-reset.js +1 -1
  160. package/src/commands/project/doctor.js +10 -32
  161. package/src/commands/project/init.js +32 -1
  162. package/src/commands/project/status.js +6 -12
  163. package/src/commands/scaffold/scaffold.js +131 -0
  164. package/src/commands/scope/escalate.js +1 -1
  165. package/src/commands/state/advance-phase.js +15 -29
  166. package/src/commands/state/state.js +20 -5
  167. package/src/commands/tasks/task.js +0 -30
  168. package/src/commands/validation/validate-feature.js +3 -10
  169. package/src/core/paths/output-schema.js +12 -13
  170. package/src/core/state/phase-state-machine.js +211 -217
  171. package/src/core/state/state-manager.js +9 -9
  172. package/src/lib/agents/agents-manager.js +2 -2
  173. package/src/lib/agents/output-collector.js +1 -2
  174. package/src/lib/detectors/design-system-detector.js +4 -4
  175. package/src/lib/detectors/index.js +1 -11
  176. package/src/lib/generators/architecture-scanner.js +640 -0
  177. package/src/lib/generators/context-generator.js +3 -3
  178. package/src/lib/generators/design-system-generator.js +508 -298
  179. package/src/lib/generators/index.js +12 -10
  180. package/src/lib/generators/metadata-extractor.js +386 -387
  181. package/src/lib/generators/recap-generator.js +4 -4
  182. package/src/lib/generators/scaffold-generator.js +317 -0
  183. package/src/lib/generators/settings-generator.js +0 -1
  184. package/src/lib/phase-chain/phase-validator.js +64 -42
  185. package/src/lib/stack/stack-profile.js +2 -2
  186. package/src/lib/standards/digest-builder.js +72 -285
  187. package/src/lib/tasks/task-parser.js +11 -5
  188. package/src/lib/validators/architecture/architecture-validator.js +259 -387
  189. package/src/lib/validators/content/content-validator.js +287 -287
  190. package/src/lib/validators/css/css-validator.js +340 -352
  191. package/src/lib/validators/design-system/design-system-validator.js +11 -5
  192. package/src/lib/validators/packages/package-validator.js +347 -360
  193. package/src/lib/validators/shared/issue-counter.js +14 -18
  194. package/src/lib/validators/validation-runner.js +3 -19
  195. package/src/scripts/setup-infra.js +1 -1
  196. package/src/utils/agents-installer.js +24 -3
  197. package/src/utils/file-copier.js +1 -0
  198. package/src/utils/hooks-installer.js +21 -19
  199. package/src/utils/skills-installer.js +178 -178
  200. package/framework/agents/architecture/prompt-engineer.md +0 -195
  201. package/framework/agents/backend/api-designer.md +0 -106
  202. package/framework/agents/frontend/blazor-builder.md +0 -229
  203. package/framework/agents/frontend/nextjs-ui-specialist.md +0 -218
  204. package/framework/agents/infrastructure/azure-architect.md +0 -150
  205. package/framework/agents/infrastructure/azure-deploy-specialist.md +0 -707
  206. package/framework/agents/infrastructure/bicep-architect.md +0 -132
  207. package/framework/agents/infrastructure/container-specialist.md +0 -137
  208. package/framework/agents/infrastructure/devops-engineer.md +0 -126
  209. package/framework/agents/integrations/azure-identity.md +0 -148
  210. package/framework/agents/quality/code-analyzer.md +0 -239
  211. package/framework/agents/quality/testing-specialist.md +0 -130
  212. package/framework/hooks/claude-code/notification/approval-reminder.js +0 -57
  213. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +0 -109
  214. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +0 -78
  215. package/framework/index/troubleshooting-index.json +0 -184
  216. package/framework/rules/testing-standards.md +0 -14
  217. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +0 -230
  218. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +0 -70
  219. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +0 -256
  220. package/framework/skills/level-1-workflows/morph-phase-clarify/references/clarifications-example.md +0 -117
  221. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +0 -269
  222. package/framework/standards/ai-agents/blazor-ui.md +0 -364
  223. package/framework/standards/architecture/ddd/aggregates.md +0 -120
  224. package/framework/standards/architecture/ddd/bounded-contexts.md +0 -105
  225. package/framework/standards/architecture/ddd/complexity-levels.md +0 -108
  226. package/framework/standards/architecture/ddd/entities.md +0 -99
  227. package/framework/standards/architecture/ddd/ubiquitous-language.md +0 -58
  228. package/framework/standards/architecture/ddd/value-objects.md +0 -124
  229. package/framework/standards/backend/api/minimal-api.md +0 -494
  230. package/framework/standards/backend/api/rest.md +0 -492
  231. package/framework/standards/backend/authentication/passkeys.md +0 -428
  232. package/framework/standards/backend/database/ef-core.md +0 -199
  233. package/framework/standards/backend/database/migrations.md +0 -393
  234. package/framework/standards/backend/database/repository-patterns.md +0 -528
  235. package/framework/standards/backend/dotnet/async.md +0 -366
  236. package/framework/standards/backend/dotnet/core.md +0 -117
  237. package/framework/standards/backend/dotnet/di.md +0 -439
  238. package/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
  239. package/framework/standards/context/analytics.md +0 -96
  240. package/framework/standards/context/bundles.md +0 -110
  241. package/framework/standards/context/priming.md +0 -78
  242. package/framework/standards/core/architecture.md +0 -185
  243. package/framework/standards/core/coding.md +0 -214
  244. package/framework/standards/core/git-branching-strategy.md +0 -403
  245. package/framework/standards/core/git.md +0 -185
  246. package/framework/standards/core/testing.md +0 -295
  247. package/framework/standards/data/nosql/blob-storage.md +0 -102
  248. package/framework/standards/data/vector-search/azure-ai-search.md +0 -121
  249. package/framework/standards/frontend/blazor/design-checklist.md +0 -222
  250. package/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
  251. package/framework/standards/frontend/blazor/fluent-ui.md +0 -137
  252. package/framework/standards/frontend/blazor/html-conversion.md +0 -184
  253. package/framework/standards/frontend/blazor/lifecycle.md +0 -195
  254. package/framework/standards/frontend/blazor/pitfalls.md +0 -198
  255. package/framework/standards/frontend/blazor/state.md +0 -191
  256. package/framework/standards/frontend/design-system/aesthetic-direction.md +0 -213
  257. package/framework/standards/infrastructure/azure/azure.md +0 -624
  258. package/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
  259. package/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
  260. package/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
  261. package/framework/standards/infrastructure/azure/services/functions.md +0 -486
  262. package/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
  263. package/framework/standards/infrastructure/azure/services/storage.md +0 -407
  264. package/framework/standards/infrastructure/vercel/vercel-database.md +0 -106
  265. package/framework/standards/integration/api/graphql.md +0 -91
  266. package/framework/standards/integration/api/grpc.md +0 -114
  267. package/framework/standards/integration/api/rest-design.md +0 -95
  268. package/framework/standards/integration/event-driven/cqrs.md +0 -101
  269. package/framework/standards/integration/event-driven/event-sourcing.md +0 -124
  270. package/framework/standards/integration/event-driven/service-bus.md +0 -95
  271. package/framework/standards/observability/logging.md +0 -131
  272. package/framework/standards/observability/metrics.md +0 -121
  273. package/framework/standards/observability/monitoring.md +0 -114
  274. package/framework/standards/observability/tracing.md +0 -132
  275. package/framework/standards/workflows/parallel-execution.md +0 -112
  276. package/framework/standards/workflows/thread-management.md +0 -113
  277. package/framework/templates/code/dotnet/backend/service.cs +0 -139
  278. package/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
  279. package/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
  280. package/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
  281. package/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
  282. package/framework/templates/code/dotnet/contracts/contracts-level1.cs +0 -69
  283. package/framework/templates/code/dotnet/contracts/contracts-level2.cs +0 -86
  284. package/framework/templates/code/dotnet/contracts/contracts-level3.cs +0 -41
  285. package/framework/templates/code/dotnet/frontend/component.razor +0 -239
  286. package/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
  287. package/framework/templates/infrastructure/azure/README.md +0 -286
  288. package/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
  289. package/framework/templates/infrastructure/azure/app-service.bicep +0 -164
  290. package/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
  291. package/framework/templates/infrastructure/azure/container-app.bicep +0 -156
  292. package/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
  293. package/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
  294. package/framework/templates/infrastructure/azure/deploy.sh +0 -208
  295. package/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
  296. package/framework/templates/infrastructure/azure/main.bicep +0 -189
  297. package/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
  298. package/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
  299. package/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
  300. package/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
  301. package/framework/templates/infrastructure/azure/storage.bicep +0 -106
  302. package/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
  303. package/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
  304. package/framework/templates/integrations/azure-identity-config.cs +0 -288
  305. package/framework/templates/project-structure/dotnet-ddd.md +0 -70
  306. package/framework/templates/saas/subscription.cs +0 -347
  307. package/framework/templates/saas/tenant.cs +0 -338
  308. package/framework/templates/ui/FluentDesignTheme.cs +0 -149
  309. package/framework/templates/ui/MudTheme.cs +0 -281
  310. package/framework/workflows/configs/nodejs-cli.json +0 -40
  311. package/src/commands/standards/context-budget.js +0 -89
  312. package/src/lib/standards/index.js +0 -7
  313. package/src/lib/standards/standards-context-injector.js +0 -302
  314. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +0 -316
  315. package/src/lib/validators/blazor/blazor-state-validator.js +0 -279
  316. package/src/lib/validators/blazor/blazor-validator.js +0 -369
  317. package/src/lib/validators/blazor/index.js +0 -9
@@ -1,298 +1,508 @@
1
- /**
2
- * MORPH-SPEC Design System Generator Library
3
- *
4
- * Reads ui-design-system.md and generates:
5
- * - wwwroot/css/design-system.css (CSS variables)
6
- * - Themes/FluentDesignTheme.cs (Fluent UI theme)
7
- * - Themes/MudTheme.cs (MudBlazor theme)
8
- *
9
- * Used both by CLI commands and internal automation.
10
- */
11
-
12
- import { readFileSync, existsSync } from 'fs';
13
-
14
- // ============================================================================
15
- // Parsing Functions
16
- // ============================================================================
17
-
18
- /**
19
- * Parse colors from markdown
20
- * @param {string} markdown - Markdown content
21
- * @returns {Object} Colors object with primary, secondary, neutral, semantic
22
- */
23
- export function parseColors(markdown) {
24
- const colors = { primary: {}, secondary: {}, neutral: {}, semantic: {} };
25
-
26
- // Match color definitions like: - **Primary 500** (`#...`) - ...
27
- const colorRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^\(]*\(`(#[0-9A-Fa-f]{6})`\)/gm;
28
-
29
- let match;
30
- while ((match = colorRegex.exec(markdown)) !== null) {
31
- const [, name, hex] = match;
32
- const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
33
-
34
- if (name.includes('Primary')) {
35
- const variant = name.match(/\d+/)?.[0] || 'default';
36
- colors.primary[variant] = hex;
37
- } else if (name.includes('Secondary')) {
38
- const variant = name.match(/\d+/)?.[0] || 'default';
39
- colors.secondary[variant] = hex;
40
- } else if (name.includes('Neutral') || name.includes('Gray')) {
41
- const variant = name.match(/\d+/)?.[0] || 'default';
42
- colors.neutral[variant] = hex;
43
- } else if (name.includes('Success') || name.includes('Error') || name.includes('Warning') || name.includes('Info')) {
44
- const type = name.toLowerCase().replace(/\s+/g, '-');
45
- colors.semantic[type] = hex;
46
- }
47
- }
48
-
49
- return colors;
50
- }
51
-
52
- /**
53
- * Parse typography from markdown
54
- * @param {string} markdown - Markdown content
55
- * @returns {Object} Typography object with fontFamily, fontSize, fontWeight, lineHeight
56
- */
57
- export function parseTypography(markdown) {
58
- const typography = { fontFamily: {}, fontSize: {}, fontWeight: {}, lineHeight: {} };
59
-
60
- // Match font family
61
- const fontFamilyRegex = /(?:font[- ]?family|typeface)[:\s]+([^\n]+)/gi;
62
- const fontMatch = fontFamilyRegex.exec(markdown);
63
- if (fontMatch) {
64
- typography.fontFamily.primary = fontMatch[1].trim();
65
- }
66
-
67
- // Match font sizes (but exclude spacing-related)
68
- const fontSizeRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^:]*:\s*(\d+(?:\.\d+)?(?:rem|px|em))/gm;
69
- let match;
70
- while ((match = fontSizeRegex.exec(markdown)) !== null) {
71
- const [, name, size] = match;
72
- const lowerName = name.toLowerCase();
73
- // Skip if it's spacing-related
74
- if (lowerName.includes('spacing') || lowerName.includes('gap') || lowerName.includes('margin') || lowerName.includes('padding')) {
75
- continue;
76
- }
77
- const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
78
- typography.fontSize[cleanName] = size;
79
- }
80
-
81
- return typography;
82
- }
83
-
84
- /**
85
- * Parse spacing from markdown
86
- * @param {string} markdown - Markdown content
87
- * @returns {Object} Spacing object with spacing values
88
- */
89
- export function parseSpacing(markdown) {
90
- const spacing = {};
91
-
92
- // Match spacing definitions
93
- const spacingRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^:]*:\s*(\d+(?:\.\d+)?(?:rem|px|em))/gm;
94
- let match;
95
- while ((match = spacingRegex.exec(markdown)) !== null) {
96
- const [, name, value] = match;
97
- if (name.toLowerCase().includes('spacing') || name.toLowerCase().includes('gap') || name.toLowerCase().includes('margin') || name.toLowerCase().includes('padding')) {
98
- const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
99
- spacing[cleanName] = value;
100
- }
101
- }
102
-
103
- return spacing;
104
- }
105
-
106
- /**
107
- * Parse all design system properties from markdown
108
- * @param {string} markdownPath - Path to ui-design-system.md file
109
- * @returns {Object} Parsed design system with colors, typography, spacing
110
- */
111
- export function parseDesignSystem(markdownPath) {
112
- if (!existsSync(markdownPath)) {
113
- throw new Error(`File not found: ${markdownPath}`);
114
- }
115
-
116
- const markdown = readFileSync(markdownPath, 'utf-8');
117
-
118
- return {
119
- colors: parseColors(markdown),
120
- typography: parseTypography(markdown),
121
- spacing: parseSpacing(markdown)
122
- };
123
- }
124
-
125
- // ============================================================================
126
- // Generation Functions
127
- // ============================================================================
128
-
129
- /**
130
- * Generate CSS variables file
131
- * @param {Object} colors - Colors object
132
- * @param {Object} typography - Typography object
133
- * @param {Object} spacing - Spacing object
134
- * @returns {string} CSS content
135
- */
136
- export function generateCSS(colors, typography, spacing) {
137
- let css = `/* Auto-generated by MORPH-SPEC Design System Generator */\n`;
138
- css += `/* Do not edit manually - regenerate from ui-design-system.md */\n\n`;
139
- css += `:root {\n`;
140
-
141
- // Colors
142
- if (Object.keys(colors.primary).length > 0) {
143
- css += ` /* Primary Colors */\n`;
144
- Object.entries(colors.primary).forEach(([variant, hex]) => {
145
- css += ` --color-primary-${variant}: ${hex};\n`;
146
- });
147
- css += `\n`;
148
- }
149
-
150
- if (Object.keys(colors.secondary).length > 0) {
151
- css += ` /* Secondary Colors */\n`;
152
- Object.entries(colors.secondary).forEach(([variant, hex]) => {
153
- css += ` --color-secondary-${variant}: ${hex};\n`;
154
- });
155
- css += `\n`;
156
- }
157
-
158
- if (Object.keys(colors.neutral).length > 0) {
159
- css += ` /* Neutral Colors */\n`;
160
- Object.entries(colors.neutral).forEach(([variant, hex]) => {
161
- css += ` --color-neutral-${variant}: ${hex};\n`;
162
- });
163
- css += `\n`;
164
- }
165
-
166
- if (Object.keys(colors.semantic).length > 0) {
167
- css += ` /* Semantic Colors */\n`;
168
- Object.entries(colors.semantic).forEach(([type, hex]) => {
169
- css += ` --color-${type}: ${hex};\n`;
170
- });
171
- css += `\n`;
172
- }
173
-
174
- // Typography
175
- if (typography.fontFamily.primary) {
176
- css += ` /* Typography */\n`;
177
- css += ` --font-family-primary: ${typography.fontFamily.primary};\n`;
178
- css += `\n`;
179
- }
180
-
181
- if (Object.keys(typography.fontSize).length > 0) {
182
- css += ` /* Font Sizes */\n`;
183
- Object.entries(typography.fontSize).forEach(([name, size]) => {
184
- css += ` --font-size-${name}: ${size};\n`;
185
- });
186
- css += `\n`;
187
- }
188
-
189
- // Spacing
190
- if (Object.keys(spacing).length > 0) {
191
- css += ` /* Spacing */\n`;
192
- Object.entries(spacing).forEach(([name, value]) => {
193
- css += ` --${name}: ${value};\n`;
194
- });
195
- }
196
-
197
- css += `}\n`;
198
-
199
- return css;
200
- }
201
-
202
- /**
203
- * Generate Fluent UI theme (C#)
204
- * @param {Object} colors - Colors object
205
- * @param {Object} typography - Typography object
206
- * @param {string} namespace - C# namespace (default: YourProject.Themes)
207
- * @returns {string} C# code
208
- */
209
- export function generateFluentTheme(colors, typography, namespace = 'YourProject.Themes') {
210
- const primary500 = colors.primary['500'] || '#0078d4';
211
-
212
- let csharp = `// Auto-generated by MORPH-SPEC Design System Generator\n`;
213
- csharp += `// Do not edit manually - regenerate from ui-design-system.md\n\n`;
214
- csharp += `using Microsoft.FluentUI.AspNetCore.Components;\n\n`;
215
- csharp += `namespace ${namespace};\n\n`;
216
- csharp += `public static class FluentDesignTheme\n{\n`;
217
- csharp += ` public static DesignThemePalette GetPalette()\n`;
218
- csharp += ` {\n`;
219
- csharp += ` return new DesignThemePalette\n`;
220
- csharp += ` {\n`;
221
- csharp += ` Accent = "${primary500}",\n`;
222
- csharp += ` // Add more theme properties as needed\n`;
223
- csharp += ` };\n`;
224
- csharp += ` }\n`;
225
- csharp += `}\n`;
226
-
227
- return csharp;
228
- }
229
-
230
- /**
231
- * Generate MudBlazor theme (C#)
232
- * @param {Object} colors - Colors object
233
- * @param {Object} typography - Typography object
234
- * @param {string} namespace - C# namespace (default: YourProject.Themes)
235
- * @returns {string} C# code
236
- */
237
- export function generateMudTheme(colors, typography, namespace = 'YourProject.Themes') {
238
- const primary500 = colors.primary['500'] || '#594ae2';
239
- const secondary500 = colors.secondary['500'] || '#ff4081';
240
-
241
- let csharp = `// Auto-generated by MORPH-SPEC Design System Generator\n`;
242
- csharp += `// Do not edit manually - regenerate from ui-design-system.md\n\n`;
243
- csharp += `using MudBlazor;\n\n`;
244
- csharp += `namespace ${namespace};\n\n`;
245
- csharp += `public static class MudDesignTheme\n{\n`;
246
- csharp += ` public static MudTheme GetTheme()\n`;
247
- csharp += ` {\n`;
248
- csharp += ` return new MudTheme\n`;
249
- csharp += ` {\n`;
250
- csharp += ` Palette = new PaletteLight\n`;
251
- csharp += ` {\n`;
252
- csharp += ` Primary = "${primary500}",\n`;
253
- csharp += ` Secondary = "${secondary500}",\n`;
254
- csharp += ` // Add more palette colors as needed\n`;
255
- csharp += ` }\n`;
256
- csharp += ` };\n`;
257
- csharp += ` }\n`;
258
- csharp += `}\n`;
259
-
260
- return csharp;
261
- }
262
-
263
- /**
264
- * Generate all design system files
265
- * @param {string} designSystemPath - Path to ui-design-system.md
266
- * @param {Object} options - Generation options
267
- * @param {string} options.mode - Generation mode: 'fluent', 'mud', or 'both'
268
- * @param {string} options.namespace - C# namespace
269
- * @returns {Object} Generated files content
270
- */
271
- export function generateDesignSystem(designSystemPath, options = {}) {
272
- const { mode = 'both', namespace = 'YourProject.Themes' } = options;
273
-
274
- // Parse design system
275
- const { colors, typography, spacing } = parseDesignSystem(designSystemPath);
276
-
277
- const generated = {
278
- css: generateCSS(colors, typography, spacing),
279
- stats: {
280
- primaryColors: Object.keys(colors.primary).length,
281
- neutralColors: Object.keys(colors.neutral).length,
282
- semanticColors: Object.keys(colors.semantic).length,
283
- fontSizes: Object.keys(typography.fontSize).length,
284
- spacingValues: Object.keys(spacing).length
285
- }
286
- };
287
-
288
- // Generate themes based on mode
289
- if (mode === 'fluent' || mode === 'both') {
290
- generated.fluentTheme = generateFluentTheme(colors, typography, namespace);
291
- }
292
-
293
- if (mode === 'mud' || mode === 'both') {
294
- generated.mudTheme = generateMudTheme(colors, typography, namespace);
295
- }
296
-
297
- return generated;
298
- }
1
+ /**
2
+ * MORPH-SPEC Design System Generator Library
3
+ *
4
+ * Reads design-system.md and generates:
5
+ * - CSS variables (design-system.css)
6
+ *
7
+ * Supports BOTH:
8
+ * - New CSS :root format (design-system.md template v2)
9
+ * - Old markdown list format (backward compat)
10
+ *
11
+ * Used both by CLI commands and internal automation.
12
+ */
13
+
14
+ import { readFileSync, existsSync } from 'fs';
15
+
16
+ // ============================================================================
17
+ // CSS :root Helpers
18
+ // ============================================================================
19
+
20
+ /**
21
+ * Extract all CSS :root blocks from markdown (inside ```css fences)
22
+ * @param {string} markdown
23
+ * @returns {string} Combined CSS content from all :root blocks
24
+ */
25
+ function extractCSSRootBlocks(markdown) {
26
+ const cssBlocks = [];
27
+ const fenceRegex = /```css\s*\n([\s\S]*?)```/g;
28
+ let match;
29
+ while ((match = fenceRegex.exec(markdown)) !== null) {
30
+ cssBlocks.push(match[1]);
31
+ }
32
+ return cssBlocks.join('\n');
33
+ }
34
+
35
+ /**
36
+ * Extract CSS custom properties from :root blocks
37
+ * Returns array of { name, value } for all --xxx: yyy; declarations
38
+ */
39
+ function extractCSSProperties(cssContent) {
40
+ const props = [];
41
+ const propRegex = /(--[\w-]+)\s*:\s*([^;]+);/g;
42
+ let match;
43
+ while ((match = propRegex.exec(cssContent)) !== null) {
44
+ props.push({ name: match[1].trim(), value: match[2].trim() });
45
+ }
46
+ return props;
47
+ }
48
+
49
+ // ============================================================================
50
+ // Parsing Functions
51
+ // ============================================================================
52
+
53
+ /**
54
+ * Parse colors from markdown — supports CSS :root format and old markdown format
55
+ * @param {string} markdown - Markdown content
56
+ * @returns {Object} Colors object with primary, accent, secondary, neutral, semantic, surface, text, other
57
+ */
58
+ export function parseColors(markdown) {
59
+ const colors = {
60
+ primary: {},
61
+ accent: {},
62
+ secondary: {},
63
+ neutral: {},
64
+ semantic: {},
65
+ surface: {},
66
+ text: {},
67
+ other: {}
68
+ };
69
+
70
+ // Try CSS :root format first
71
+ const cssContent = extractCSSRootBlocks(markdown);
72
+ const props = extractCSSProperties(cssContent);
73
+
74
+ let cssColorCount = 0;
75
+
76
+ for (const { name, value } of props) {
77
+ if (!name.startsWith('--color-')) continue;
78
+
79
+ const suffix = name.slice('--color-'.length); // e.g. "primary", "primary-light", "neutral-500"
80
+
81
+ if (suffix.startsWith('primary-rgb')) {
82
+ // Store RGB metadata as-is
83
+ colors.primary['rgb'] = value;
84
+ cssColorCount++;
85
+ } else if (suffix.startsWith('primary')) {
86
+ const variant = suffix.slice('primary'.length).replace(/^-/, '') || 'default';
87
+ colors.primary[variant] = value;
88
+ cssColorCount++;
89
+ } else if (suffix.startsWith('accent')) {
90
+ const variant = suffix.slice('accent'.length).replace(/^-/, '') || 'default';
91
+ colors.accent[variant] = value;
92
+ cssColorCount++;
93
+ } else if (suffix.startsWith('neutral-')) {
94
+ const variant = suffix.slice('neutral-'.length);
95
+ colors.neutral[variant] = value;
96
+ cssColorCount++;
97
+ } else if (suffix.startsWith('surface-')) {
98
+ const variant = suffix.slice('surface-'.length);
99
+ colors.surface[variant] = value;
100
+ cssColorCount++;
101
+ } else if (suffix.startsWith('text-')) {
102
+ const variant = suffix.slice('text-'.length);
103
+ colors.text[variant] = value;
104
+ cssColorCount++;
105
+ } else if (suffix === 'success' || suffix === 'warning' || suffix === 'error' || suffix === 'info') {
106
+ colors.semantic[suffix] = value;
107
+ cssColorCount++;
108
+ } else if (suffix === 'border') {
109
+ colors.other['border'] = value;
110
+ cssColorCount++;
111
+ } else {
112
+ // Any other --color-xxx token
113
+ colors.other[suffix] = value;
114
+ cssColorCount++;
115
+ }
116
+ }
117
+
118
+ // If CSS format found colors, return them
119
+ if (cssColorCount > 0) {
120
+ return colors;
121
+ }
122
+
123
+ // FALL BACK to old markdown regex format
124
+ const colorRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^\(]*\(`(#[0-9A-Fa-f]{6})`\)/gm;
125
+
126
+ let match;
127
+ while ((match = colorRegex.exec(markdown)) !== null) {
128
+ const [, name, hex] = match;
129
+
130
+ if (name.includes('Primary')) {
131
+ const variant = name.match(/\d+/)?.[0] || 'default';
132
+ colors.primary[variant] = hex;
133
+ } else if (name.includes('Secondary')) {
134
+ const variant = name.match(/\d+/)?.[0] || 'default';
135
+ colors.secondary[variant] = hex;
136
+ } else if (name.includes('Neutral') || name.includes('Gray')) {
137
+ const variant = name.match(/\d+/)?.[0] || 'default';
138
+ colors.neutral[variant] = hex;
139
+ } else if (name.includes('Success') || name.includes('Error') || name.includes('Warning') || name.includes('Info')) {
140
+ const type = name.toLowerCase().replace(/\s+/g, '-');
141
+ colors.semantic[type] = hex;
142
+ }
143
+ }
144
+
145
+ return colors;
146
+ }
147
+
148
+ /**
149
+ * Parse typography from markdown — supports CSS :root format and old markdown format
150
+ * @param {string} markdown - Markdown content
151
+ * @returns {Object} Typography with fontFamily, fontSize, fontWeight, lineHeight
152
+ */
153
+ export function parseTypography(markdown) {
154
+ const typography = {
155
+ fontFamily: {},
156
+ fontSize: {},
157
+ fontWeight: {},
158
+ lineHeight: {}
159
+ };
160
+
161
+ // Try CSS :root format first
162
+ const cssContent = extractCSSRootBlocks(markdown);
163
+ const props = extractCSSProperties(cssContent);
164
+
165
+ let cssTypoCount = 0;
166
+
167
+ for (const { name, value } of props) {
168
+ if (name === '--font-display') {
169
+ typography.fontFamily.display = value;
170
+ cssTypoCount++;
171
+ } else if (name === '--font-body') {
172
+ typography.fontFamily.body = value;
173
+ cssTypoCount++;
174
+ } else if (name === '--font-mono') {
175
+ typography.fontFamily.mono = value;
176
+ cssTypoCount++;
177
+ } else if (name.startsWith('--text-')) {
178
+ // Font sizes: --text-xs, --text-sm, --text-base, etc.
179
+ const sizeName = name.slice('--text-'.length);
180
+ typography.fontSize[sizeName] = value;
181
+ cssTypoCount++;
182
+ } else if (name.startsWith('--font-weight-')) {
183
+ const weightName = name.slice('--font-weight-'.length);
184
+ typography.fontWeight[weightName] = value;
185
+ cssTypoCount++;
186
+ } else if (name.startsWith('--leading-')) {
187
+ const leadingName = name.slice('--leading-'.length);
188
+ typography.lineHeight[leadingName] = value;
189
+ cssTypoCount++;
190
+ }
191
+ }
192
+
193
+ // If CSS format found typography, return it
194
+ if (cssTypoCount > 0) {
195
+ return typography;
196
+ }
197
+
198
+ // FALL BACK to old markdown format
199
+ // Match font family
200
+ const fontFamilyRegex = /(?:font[- ]?family|typeface)[:\s]+([^\n]+)/gi;
201
+ const fontMatch = fontFamilyRegex.exec(markdown);
202
+ if (fontMatch) {
203
+ typography.fontFamily.primary = fontMatch[1].trim();
204
+ }
205
+
206
+ // Match font sizes (but exclude spacing-related)
207
+ const fontSizeRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^:]*:\s*(\d+(?:\.\d+)?(?:rem|px|em))/gm;
208
+ let match;
209
+ while ((match = fontSizeRegex.exec(markdown)) !== null) {
210
+ const [, name, size] = match;
211
+ const lowerName = name.toLowerCase();
212
+ // Skip if it's spacing-related
213
+ if (lowerName.includes('spacing') || lowerName.includes('gap') || lowerName.includes('margin') || lowerName.includes('padding')) {
214
+ continue;
215
+ }
216
+ const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
217
+ typography.fontSize[cleanName] = size;
218
+ }
219
+
220
+ return typography;
221
+ }
222
+
223
+ /**
224
+ * Parse spacing from markdown — supports CSS :root format and old markdown format
225
+ * @param {string} markdown - Markdown content
226
+ * @returns {Object} Spacing values
227
+ */
228
+ export function parseSpacing(markdown) {
229
+ const spacing = {};
230
+
231
+ // Try CSS :root format first
232
+ const cssContent = extractCSSRootBlocks(markdown);
233
+ const props = extractCSSProperties(cssContent);
234
+
235
+ let cssSpaceCount = 0;
236
+
237
+ for (const { name, value } of props) {
238
+ if (name.startsWith('--space-')) {
239
+ const spaceName = name.slice('--space-'.length);
240
+ spacing[spaceName] = value;
241
+ cssSpaceCount++;
242
+ }
243
+ }
244
+
245
+ // If CSS format found spacing, return it
246
+ if (cssSpaceCount > 0) {
247
+ return spacing;
248
+ }
249
+
250
+ // FALL BACK to old markdown format
251
+ const spacingRegex = /(?:^|\n)[-*]\s+\*\*([^*]+)\*\*[^:]*:\s*(\d+(?:\.\d+)?(?:rem|px|em))/gm;
252
+ let match;
253
+ while ((match = spacingRegex.exec(markdown)) !== null) {
254
+ const [, name, value] = match;
255
+ if (name.toLowerCase().includes('spacing') || name.toLowerCase().includes('gap') || name.toLowerCase().includes('margin') || name.toLowerCase().includes('padding')) {
256
+ const cleanName = name.trim().toLowerCase().replace(/\s+/g, '-');
257
+ spacing[cleanName] = value;
258
+ }
259
+ }
260
+
261
+ return spacing;
262
+ }
263
+
264
+ /**
265
+ * Parse motion tokens from markdown (CSS :root format only)
266
+ * @param {string} markdown - Markdown content
267
+ * @returns {Object} { durations, easings, scroll }
268
+ */
269
+ export function parseMotion(markdown) {
270
+ const motion = {
271
+ durations: {},
272
+ easings: {},
273
+ scroll: {}
274
+ };
275
+
276
+ const cssContent = extractCSSRootBlocks(markdown);
277
+ const props = extractCSSProperties(cssContent);
278
+
279
+ for (const { name, value } of props) {
280
+ if (name.startsWith('--duration-')) {
281
+ const key = name.slice('--duration-'.length);
282
+ motion.durations[key] = value;
283
+ } else if (name.startsWith('--easing-')) {
284
+ const key = name.slice('--easing-'.length);
285
+ motion.easings[key] = value;
286
+ } else if (name.startsWith('--scroll-')) {
287
+ const key = name.slice('--scroll-'.length);
288
+ motion.scroll[key] = value;
289
+ }
290
+ }
291
+
292
+ return motion;
293
+ }
294
+
295
+ /**
296
+ * Parse advanced effects tokens from markdown (CSS :root format only)
297
+ * @param {string} markdown - Markdown content
298
+ * @returns {Object} { glass, aurora, perspective, container, viewTransition, noise }
299
+ */
300
+ export function parseAdvancedEffects(markdown) {
301
+ const effects = {
302
+ glass: {},
303
+ aurora: {},
304
+ perspective: {},
305
+ container: {},
306
+ viewTransition: {},
307
+ noise: {}
308
+ };
309
+
310
+ const cssContent = extractCSSRootBlocks(markdown);
311
+ const props = extractCSSProperties(cssContent);
312
+
313
+ for (const { name, value } of props) {
314
+ if (name.startsWith('--glass-')) {
315
+ const key = name.slice('--glass-'.length);
316
+ effects.glass[key] = value;
317
+ } else if (name.startsWith('--aurora-')) {
318
+ const key = name.slice('--aurora-'.length);
319
+ effects.aurora[key] = value;
320
+ } else if (name.startsWith('--noise-')) {
321
+ const key = name.slice('--noise-'.length);
322
+ effects.noise[key] = value;
323
+ } else if (name.startsWith('--perspective-')) {
324
+ const key = name.slice('--perspective-'.length);
325
+ effects.perspective[key] = value;
326
+ } else if (name.startsWith('--container-')) {
327
+ const key = name.slice('--container-'.length);
328
+ effects.container[key] = value;
329
+ } else if (name.startsWith('--view-transition-')) {
330
+ const key = name.slice('--view-transition-'.length);
331
+ effects.viewTransition[key] = value;
332
+ }
333
+ }
334
+
335
+ return effects;
336
+ }
337
+
338
+ /**
339
+ * Parse all design system properties from markdown
340
+ * @param {string} markdownPath - Path to design-system.md file
341
+ * @returns {Object} Parsed design system with colors, typography, spacing, motion, effects
342
+ */
343
+ export function parseDesignSystem(markdownPath) {
344
+ if (!existsSync(markdownPath)) {
345
+ throw new Error(`File not found: ${markdownPath}`);
346
+ }
347
+
348
+ const markdown = readFileSync(markdownPath, 'utf-8');
349
+
350
+ return {
351
+ colors: parseColors(markdown),
352
+ typography: parseTypography(markdown),
353
+ spacing: parseSpacing(markdown),
354
+ motion: parseMotion(markdown),
355
+ effects: parseAdvancedEffects(markdown)
356
+ };
357
+ }
358
+
359
+ // ============================================================================
360
+ // Generation Functions
361
+ // ============================================================================
362
+
363
+ /**
364
+ * Helper to emit CSS variable entries for a color category
365
+ */
366
+ function emitColorCategory(colors, prefix, label) {
367
+ if (Object.keys(colors).length === 0) return '';
368
+ let css = ` /* ${label} */\n`;
369
+ Object.entries(colors).forEach(([variant, value]) => {
370
+ if (variant === 'rgb') {
371
+ css += ` --color-${prefix}-rgb: ${value};\n`;
372
+ } else if (variant === 'default') {
373
+ css += ` --color-${prefix}: ${value};\n`;
374
+ } else {
375
+ css += ` --color-${prefix}-${variant}: ${value};\n`;
376
+ }
377
+ });
378
+ css += `\n`;
379
+ return css;
380
+ }
381
+
382
+ /**
383
+ * Generate CSS variables file
384
+ * @param {Object} colors - Colors object
385
+ * @param {Object} typography - Typography object
386
+ * @param {Object} spacing - Spacing object
387
+ * @returns {string} CSS content
388
+ */
389
+ export function generateCSS(colors, typography, spacing) {
390
+ let css = `/* Auto-generated by MORPH-SPEC Design System Generator */\n`;
391
+ css += `/* Do not edit manually - regenerate from design-system.md */\n\n`;
392
+ css += `:root {\n`;
393
+
394
+ // Colors — handle expanded structure
395
+ css += emitColorCategory(colors.primary, 'primary', 'Primary Colors');
396
+
397
+ // Support both accent (new) and secondary (old)
398
+ if (Object.keys(colors.accent || {}).length > 0) {
399
+ css += emitColorCategory(colors.accent, 'accent', 'Accent Colors');
400
+ }
401
+ if (Object.keys(colors.secondary || {}).length > 0) {
402
+ css += emitColorCategory(colors.secondary, 'secondary', 'Secondary Colors');
403
+ }
404
+
405
+ css += emitColorCategory(colors.neutral || {}, 'neutral', 'Neutral Colors');
406
+
407
+ if (Object.keys(colors.surface || {}).length > 0) {
408
+ css += emitColorCategory(colors.surface, 'surface', 'Surface Colors');
409
+ }
410
+
411
+ if (Object.keys(colors.text || {}).length > 0) {
412
+ css += emitColorCategory(colors.text, 'text', 'Text Colors');
413
+ }
414
+
415
+ if (Object.keys(colors.semantic || {}).length > 0) {
416
+ css += ` /* Semantic Colors */\n`;
417
+ Object.entries(colors.semantic).forEach(([type, hex]) => {
418
+ css += ` --color-${type}: ${hex};\n`;
419
+ });
420
+ css += `\n`;
421
+ }
422
+
423
+ if (Object.keys(colors.other || {}).length > 0) {
424
+ css += ` /* Other Colors */\n`;
425
+ Object.entries(colors.other).forEach(([key, value]) => {
426
+ css += ` --color-${key}: ${value};\n`;
427
+ });
428
+ css += `\n`;
429
+ }
430
+
431
+ // Typography
432
+ const fontFamilies = typography.fontFamily || {};
433
+ if (fontFamilies.display || fontFamilies.body || fontFamilies.mono || fontFamilies.primary) {
434
+ css += ` /* Typography */\n`;
435
+ if (fontFamilies.display) css += ` --font-display: ${fontFamilies.display};\n`;
436
+ if (fontFamilies.body) css += ` --font-body: ${fontFamilies.body};\n`;
437
+ if (fontFamilies.mono) css += ` --font-mono: ${fontFamilies.mono};\n`;
438
+ if (fontFamilies.primary) css += ` --font-family-primary: ${fontFamilies.primary};\n`;
439
+ css += `\n`;
440
+ }
441
+
442
+ if (Object.keys(typography.fontSize).length > 0) {
443
+ css += ` /* Font Sizes */\n`;
444
+ Object.entries(typography.fontSize).forEach(([name, size]) => {
445
+ css += ` --font-size-${name}: ${size};\n`;
446
+ });
447
+ css += `\n`;
448
+ }
449
+
450
+ // Spacing
451
+ if (Object.keys(spacing).length > 0) {
452
+ css += ` /* Spacing */\n`;
453
+ Object.entries(spacing).forEach(([name, value]) => {
454
+ // If key is numeric (from --space-N), use --space-N format
455
+ if (/^\d+$/.test(name)) {
456
+ css += ` --space-${name}: ${value};\n`;
457
+ } else {
458
+ css += ` --${name}: ${value};\n`;
459
+ }
460
+ });
461
+ }
462
+
463
+ css += `}\n`;
464
+
465
+ return css;
466
+ }
467
+
468
+ /**
469
+ * Count non-empty keys in an object
470
+ */
471
+ function countTokens(obj) {
472
+ return Object.keys(obj || {}).length;
473
+ }
474
+
475
+ /**
476
+ * Generate all design system files
477
+ * @param {string} designSystemPath - Path to design-system.md
478
+ * @param {Object} options - Generation options
479
+ * @returns {Object} Generated files content
480
+ */
481
+ export function generateDesignSystem(designSystemPath, options = {}) {
482
+ // Parse design system
483
+ const { colors, typography, spacing, motion, effects } = parseDesignSystem(designSystemPath);
484
+
485
+ const generated = {
486
+ css: generateCSS(colors, typography, spacing),
487
+ stats: {
488
+ primaryColors: countTokens(colors.primary),
489
+ accentColors: countTokens(colors.accent),
490
+ secondaryColors: countTokens(colors.secondary),
491
+ neutralColors: countTokens(colors.neutral),
492
+ semanticColors: countTokens(colors.semantic),
493
+ surfaceColors: countTokens(colors.surface),
494
+ textColors: countTokens(colors.text),
495
+ fontSizes: countTokens(typography.fontSize),
496
+ fontWeights: countTokens(typography.fontWeight),
497
+ lineHeights: countTokens(typography.lineHeight),
498
+ spacingValues: countTokens(spacing),
499
+ motionDurations: countTokens(motion.durations),
500
+ motionEasings: countTokens(motion.easings),
501
+ effectsGlass: countTokens(effects.glass),
502
+ effectsAurora: countTokens(effects.aurora),
503
+ effectsPerspective: countTokens(effects.perspective)
504
+ }
505
+ };
506
+
507
+ return generated;
508
+ }