@polymorphism-tech/morph-spec 3.2.0 → 4.3.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 (386) hide show
  1. package/README.md +1 -14
  2. package/bin/detect-agents.js +1 -1
  3. package/bin/morph-spec.js +403 -40
  4. package/bin/validate.js +5 -5
  5. package/docs/getting-started.md +0 -5
  6. package/docs/next-generation/AGENTS.md +521 -0
  7. package/docs/next-generation/ANALYSIS.md +555 -0
  8. package/docs/next-generation/ARCHITECTURE.md +436 -0
  9. package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
  10. package/docs/next-generation/EXECUTION-FLOW.md +274 -0
  11. package/docs/next-generation/FEATURES.md +688 -0
  12. package/docs/next-generation/META-PROMPTS.md +235 -0
  13. package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
  14. package/docs/next-generation/README.md +231 -0
  15. package/docs/next-generation/ROADMAP.md +801 -0
  16. package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
  17. package/docs/validation-checklist.md +0 -1
  18. package/package.json +5 -5
  19. package/src/commands/agents/agents-fuse.js +96 -0
  20. package/src/commands/agents/index.js +4 -0
  21. package/src/commands/agents/micro-agent.js +112 -0
  22. package/src/commands/{spawn-team.js → agents/spawn-team.js} +237 -172
  23. package/src/commands/agents/squad-template.js +146 -0
  24. package/src/commands/analytics/analytics.js +176 -0
  25. package/src/commands/context/context-prime.js +63 -0
  26. package/src/commands/context/core-four.js +54 -0
  27. package/src/commands/{create-story.js → feature/create-story.js} +357 -354
  28. package/src/commands/feature/index.js +6 -0
  29. package/src/commands/{shard-spec.js → feature/shard-spec.js} +2 -2
  30. package/src/commands/{sprint-status.js → feature/sprint-status.js} +1 -1
  31. package/src/commands/{generate-context.js → generation/generate-context.js} +40 -40
  32. package/src/commands/{generate.js → generation/generate.js} +4 -4
  33. package/src/commands/generation/index.js +5 -0
  34. package/src/commands/index.js +16 -0
  35. package/src/commands/{capture-pattern.js → learning/capture-pattern.js} +121 -121
  36. package/src/commands/learning/index.js +5 -0
  37. package/src/commands/mcp/mcp.js +102 -0
  38. package/src/commands/{detect-agents.js → project/detect-agents.js} +178 -178
  39. package/src/commands/project/detect-workflow.js +174 -0
  40. package/src/commands/{detect.js → project/detect.js} +104 -104
  41. package/src/commands/{doctor.js → project/doctor.js} +221 -4
  42. package/src/commands/project/index.js +10 -0
  43. package/src/commands/{init.js → project/init.js} +305 -295
  44. package/src/commands/{sync.js → project/sync.js} +167 -167
  45. package/src/commands/{update.js → project/update.js} +240 -240
  46. package/src/commands/{advance-phase.js → state/advance-phase.js} +101 -25
  47. package/src/commands/{approve.js → state/approve.js} +221 -221
  48. package/src/commands/state/index.js +8 -0
  49. package/src/commands/{rollback-phase.js → state/rollback-phase.js} +185 -185
  50. package/src/commands/{state.js → state/state.js} +334 -334
  51. package/src/commands/{validate-phase.js → state/validate-phase.js} +221 -221
  52. package/src/commands/tasks/index.js +4 -0
  53. package/src/commands/{task.js → tasks/task.js} +78 -78
  54. package/src/commands/templates/index.js +8 -0
  55. package/src/commands/templates/template-customize.js +101 -0
  56. package/src/commands/templates/template-list.js +128 -0
  57. package/src/commands/templates/template-render.js +174 -0
  58. package/src/commands/templates/template-show.js +131 -0
  59. package/src/commands/templates/template-validate.js +91 -0
  60. package/src/commands/threads/thread-template.js +103 -0
  61. package/src/commands/threads/threads.js +261 -0
  62. package/src/commands/trust/trust.js +205 -0
  63. package/src/commands/utils/index.js +7 -0
  64. package/src/commands/{session-summary.js → utils/session-summary.js} +291 -291
  65. package/src/commands/{troubleshoot.js → utils/troubleshoot.js} +222 -222
  66. package/src/commands/{analyze-blazor-concurrency.js → validation/analyze-blazor-concurrency.js} +193 -193
  67. package/src/commands/validation/index.js +8 -0
  68. package/src/commands/{lint-fluent.js → validation/lint-fluent.js} +352 -352
  69. package/src/commands/{validate-blazor-state.js → validation/validate-blazor-state.js} +210 -210
  70. package/src/commands/{validate-blazor.js → validation/validate-blazor.js} +156 -156
  71. package/src/commands/{validate-css.js → validation/validate-css.js} +84 -84
  72. package/src/core/index.js +10 -0
  73. package/src/{orchestrator.js → core/orchestrator.js} +8 -8
  74. package/src/core/registry/command-registry.js +302 -0
  75. package/src/core/registry/index.js +8 -0
  76. package/src/core/registry/validator-registry.js +204 -0
  77. package/src/core/state/index.js +8 -0
  78. package/src/{lib → core/state}/phase-state-machine.js +214 -214
  79. package/src/{lib → core/state}/state-manager.js +588 -534
  80. package/src/core/templates/index.js +9 -0
  81. package/src/core/templates/template-registry.js +335 -0
  82. package/src/core/templates/template-renderer.js +477 -0
  83. package/src/core/templates/template-validator.js +296 -0
  84. package/src/core/workflows/index.js +7 -0
  85. package/src/core/workflows/workflow-detector.js +452 -0
  86. package/src/lib/agents/micro-agent-factory.js +161 -0
  87. package/src/lib/{complexity-analyzer.js → analysis/complexity-analyzer.js} +441 -441
  88. package/src/lib/analysis/index.js +7 -0
  89. package/src/lib/analytics/analytics-engine.js +345 -0
  90. package/src/lib/{checkpoint-hooks.js → checkpoints/checkpoint-hooks.js} +35 -0
  91. package/src/lib/checkpoints/index.js +7 -0
  92. package/src/lib/context/context-bundler.js +240 -0
  93. package/src/lib/context/context-optimizer.js +212 -0
  94. package/src/lib/context/context-tracker.js +273 -0
  95. package/src/lib/context/core-four-tracker.js +201 -0
  96. package/src/lib/context/mcp-optimizer.js +200 -0
  97. package/src/lib/detectors/config-detector.js +223 -223
  98. package/src/lib/detectors/conversation-analyzer.js +163 -163
  99. package/src/lib/{design-system-detector.js → detectors/design-system-detector.js} +187 -187
  100. package/src/lib/detectors/index.js +87 -84
  101. package/src/lib/detectors/standards-generator.js +275 -275
  102. package/src/lib/detectors/structure-detector.js +245 -245
  103. package/src/lib/execution/fusion-executor.js +304 -0
  104. package/src/lib/execution/parallel-executor.js +270 -0
  105. package/src/lib/{context-generator.js → generators/context-generator.js} +526 -516
  106. package/src/lib/generators/index.js +10 -0
  107. package/src/lib/{metadata-extractor.js → generators/metadata-extractor.js} +387 -380
  108. package/src/lib/{recap-generator.js → generators/recap-generator.js} +205 -205
  109. package/src/lib/hooks/hook-executor.js +169 -0
  110. package/src/lib/hooks/stop-hook-executor.js +286 -0
  111. package/src/lib/hops/hop-composer.js +221 -0
  112. package/src/lib/learning/index.js +7 -0
  113. package/src/lib/orchestration/index.js +7 -0
  114. package/src/lib/{team-orchestrator.js → orchestration/team-orchestrator.js} +323 -323
  115. package/src/lib/stacks/index.js +7 -0
  116. package/src/lib/{stack-resolver.js → stacks/stack-resolver.js} +180 -148
  117. package/src/lib/standards/index.js +7 -0
  118. package/src/lib/{standards-context-injector.js → standards/standards-context-injector.js} +298 -288
  119. package/src/lib/threads/thread-coordinator.js +238 -0
  120. package/src/lib/threads/thread-manager.js +317 -0
  121. package/src/lib/tracking/artifact-trail.js +202 -0
  122. package/src/lib/troubleshooting/index.js +8 -0
  123. package/src/lib/{troubleshoot-grep.js → troubleshooting/troubleshoot-grep.js} +204 -204
  124. package/src/lib/{troubleshoot-index.js → troubleshooting/troubleshoot-index.js} +144 -144
  125. package/src/lib/trust/trust-manager.js +269 -0
  126. package/src/lib/validators/{architecture-validator.js → architecture/architecture-validator.js} +8 -8
  127. package/src/lib/validators/architecture/index.js +7 -0
  128. package/src/lib/{blazor-concurrency-analyzer.js → validators/blazor/blazor-concurrency-analyzer.js} +277 -288
  129. package/src/lib/{blazor-state-validator.js → validators/blazor/blazor-state-validator.js} +279 -291
  130. package/src/lib/{blazor-validator.js → validators/blazor/blazor-validator.js} +369 -374
  131. package/src/lib/validators/blazor/index.js +9 -0
  132. package/src/lib/validators/{content-validator.js → content/content-validator.js} +351 -351
  133. package/src/lib/validators/content/index.js +7 -0
  134. package/src/lib/validators/{contract-compliance-validator.js → contracts/contract-compliance-validator.js} +273 -273
  135. package/src/lib/validators/contracts/index.js +7 -0
  136. package/src/lib/{css-validator.js → validators/css/css-validator.js} +352 -352
  137. package/src/lib/validators/css/index.js +7 -0
  138. package/src/lib/validators/{design-system-validator.js → design-system/design-system-validator.js} +231 -231
  139. package/src/lib/validators/design-system/index.js +7 -0
  140. package/src/lib/validators/packages/index.js +7 -0
  141. package/src/lib/validators/shared/index.js +12 -0
  142. package/src/lib/validators/shared/issue-counter.js +18 -0
  143. package/src/lib/validators/shared/result-formatter.js +124 -0
  144. package/src/lib/{spec-validator.js → validators/spec-validator.js} +258 -258
  145. package/src/lib/validators/ui/index.js +7 -0
  146. package/src/lib/{validation-runner.js → validators/validation-runner.js} +286 -284
  147. package/src/ui/wizard-questions.js +0 -2
  148. package/src/utils/color-utils.js +70 -0
  149. package/src/utils/file-copier.js +188 -189
  150. package/src/utils/process-handler.js +97 -0
  151. package/stacks/blazor-azure/.morph/config/agents.json +948 -764
  152. package/stacks/blazor-azure/.morph/hooks/{pre-commit-tests.sh → pre-commit/tests-csharp.sh} +3 -2
  153. package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/cd-prod.yml.hbs +41 -0
  154. package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/cd-staging.yml.hbs +24 -0
  155. package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/ci-build.yml.hbs +23 -0
  156. package/stacks/nextjs-supabase/.morph/config/agents.json +345 -345
  157. package/stacks/nextjs-supabase/.morph/hooks/pre-commit/tests-typescript.sh +61 -0
  158. package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/cd-prod.yml.hbs +22 -0
  159. package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/cd-staging.yml.hbs +22 -0
  160. package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/ci-build.yml.hbs +35 -0
  161. package/stacks/nextjs-supabase/README.md +6 -15
  162. package/CLAUDE.md +0 -648
  163. package/bin/render-template.js +0 -349
  164. package/bin/semantic-detect-agents.js +0 -247
  165. package/bin/validate-agents-skills.js +0 -257
  166. package/bin/validate-agents.js +0 -70
  167. package/bin/validate-phase.js +0 -263
  168. package/docs/examples.md +0 -328
  169. package/docs/llm-interaction-config.md +0 -735
  170. package/scripts/reorganize-skills.cjs +0 -175
  171. package/scripts/validate-agents-structure.cjs +0 -52
  172. package/scripts/validate-skills.cjs +0 -180
  173. package/src/commands/deploy.js +0 -780
  174. package/src/commands/migrate-state.js +0 -158
  175. package/src/commands/upgrade.js +0 -346
  176. package/src/lib/continuous-validator.js +0 -421
  177. package/src/lib/decision-constraint-loader.js +0 -109
  178. package/src/lib/design-system-scaffolder.js +0 -299
  179. package/src/lib/hook-executor.js +0 -257
  180. package/src/lib/mockup-generator.js +0 -366
  181. package/src/lib/ui-detector.js +0 -350
  182. package/src/llm/schema-validator.js +0 -121
  183. package/src/sanitizer/.gitkeep +0 -0
  184. package/src/scanner/.gitkeep +0 -0
  185. package/src/types/index.js +0 -477
  186. package/src/ui/.gitkeep +0 -0
  187. package/src/writer/.gitkeep +0 -0
  188. package/stacks/blazor-azure/.azure/README.md +0 -293
  189. package/stacks/blazor-azure/.azure/docs/azure-devops-setup.md +0 -454
  190. package/stacks/blazor-azure/.azure/docs/branch-strategy.md +0 -398
  191. package/stacks/blazor-azure/.azure/docs/local-development.md +0 -515
  192. package/stacks/blazor-azure/.azure/pipelines/pipeline-variables.yml +0 -34
  193. package/stacks/blazor-azure/.azure/pipelines/prod-pipeline.yml +0 -319
  194. package/stacks/blazor-azure/.azure/pipelines/staging-pipeline.yml +0 -234
  195. package/stacks/blazor-azure/.azure/pipelines/templates/build-dotnet.yml +0 -75
  196. package/stacks/blazor-azure/.azure/pipelines/templates/deploy-app-service.yml +0 -94
  197. package/stacks/blazor-azure/.azure/pipelines/templates/deploy-container-app.yml +0 -120
  198. package/stacks/blazor-azure/.azure/pipelines/templates/infra-deploy.yml +0 -90
  199. package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
  200. package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
  201. package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
  202. package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
  203. package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
  204. package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
  205. package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
  206. package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
  207. package/stacks/blazor-azure/.claude/settings.local.json +0 -15
  208. package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
  209. package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
  210. package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
  211. package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
  212. package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
  213. package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
  214. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
  215. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
  216. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
  217. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
  218. package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
  219. package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
  220. package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
  221. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
  222. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
  223. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
  224. package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
  225. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -287
  226. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -113
  227. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
  228. package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -109
  229. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
  230. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
  231. package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
  232. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
  233. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
  234. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
  235. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
  236. package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
  237. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
  238. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
  239. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
  240. package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
  241. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
  242. package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
  243. package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
  244. package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
  245. package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
  246. package/stacks/blazor-azure/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -392
  247. package/stacks/blazor-azure/.morph/docs/workflows/design-impl.md +0 -37
  248. package/stacks/blazor-azure/.morph/docs/workflows/enforcement-pipeline.md +0 -668
  249. package/stacks/blazor-azure/.morph/docs/workflows/fast-track.md +0 -29
  250. package/stacks/blazor-azure/.morph/docs/workflows/full-morph.md +0 -76
  251. package/stacks/blazor-azure/.morph/docs/workflows/standard.md +0 -44
  252. package/stacks/blazor-azure/.morph/docs/workflows/ui-refresh.md +0 -39
  253. package/stacks/blazor-azure/.morph/examples/api-nextjs/README.md +0 -241
  254. package/stacks/blazor-azure/.morph/examples/api-nextjs/contracts.ts +0 -307
  255. package/stacks/blazor-azure/.morph/examples/api-nextjs/spec.md +0 -399
  256. package/stacks/blazor-azure/.morph/examples/api-nextjs/tasks.md +0 -168
  257. package/stacks/blazor-azure/.morph/examples/micro-saas/README.md +0 -125
  258. package/stacks/blazor-azure/.morph/examples/micro-saas/contracts.cs +0 -358
  259. package/stacks/blazor-azure/.morph/examples/micro-saas/decisions.md +0 -246
  260. package/stacks/blazor-azure/.morph/examples/micro-saas/spec.md +0 -236
  261. package/stacks/blazor-azure/.morph/examples/micro-saas/tasks.md +0 -150
  262. package/stacks/blazor-azure/.morph/examples/multi-agent/README.md +0 -309
  263. package/stacks/blazor-azure/.morph/examples/multi-agent/contracts.cs +0 -433
  264. package/stacks/blazor-azure/.morph/examples/multi-agent/spec.md +0 -479
  265. package/stacks/blazor-azure/.morph/examples/multi-agent/tasks.md +0 -185
  266. package/stacks/blazor-azure/.morph/examples/scheduled-reports/decisions.md +0 -158
  267. package/stacks/blazor-azure/.morph/examples/scheduled-reports/proposal.md +0 -95
  268. package/stacks/blazor-azure/.morph/examples/scheduled-reports/spec.md +0 -267
  269. package/stacks/blazor-azure/.morph/examples/state-v3.json +0 -188
  270. package/stacks/blazor-azure/.morph/features/.gitkeep +0 -25
  271. package/stacks/blazor-azure/.morph/hooks/README.md +0 -348
  272. package/stacks/blazor-azure/.morph/hooks/pre-commit-agents.sh +0 -24
  273. package/stacks/blazor-azure/.morph/hooks/pre-commit-all.sh +0 -48
  274. package/stacks/blazor-azure/.morph/hooks/pre-commit-specs.sh +0 -49
  275. package/stacks/blazor-azure/.morph/hooks/task-completed.js +0 -73
  276. package/stacks/blazor-azure/.morph/hooks/teammate-idle.js +0 -68
  277. package/stacks/blazor-azure/.morph/schemas/agent.schema.json +0 -296
  278. package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
  279. package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
  280. package/stacks/blazor-azure/.morph/standards/agent-framework-blazor-ui.md +0 -359
  281. package/stacks/blazor-azure/.morph/standards/agent-framework-production.md +0 -410
  282. package/stacks/blazor-azure/.morph/standards/agent-framework-setup.md +0 -413
  283. package/stacks/blazor-azure/.morph/standards/agent-framework-workflows.md +0 -349
  284. package/stacks/blazor-azure/.morph/standards/agent-teams-workflow.md +0 -474
  285. package/stacks/blazor-azure/.morph/standards/architecture.md +0 -325
  286. package/stacks/blazor-azure/.morph/standards/azure.md +0 -605
  287. package/stacks/blazor-azure/.morph/standards/coding.md +0 -377
  288. package/stacks/blazor-azure/.morph/standards/dotnet10-migration.md +0 -520
  289. package/stacks/blazor-azure/.morph/standards/fluent-ui-setup.md +0 -590
  290. package/stacks/blazor-azure/.morph/standards/migration-guide.md +0 -514
  291. package/stacks/blazor-azure/.morph/standards/passkeys-auth.md +0 -423
  292. package/stacks/blazor-azure/.morph/standards/vector-search-rag.md +0 -536
  293. package/stacks/blazor-azure/.morph/templates/CONTEXT-FEATURE.md +0 -276
  294. package/stacks/blazor-azure/.morph/templates/CONTEXT.md +0 -170
  295. package/stacks/blazor-azure/.morph/templates/FluentDesignTheme.cs +0 -149
  296. package/stacks/blazor-azure/.morph/templates/MudTheme.cs +0 -281
  297. package/stacks/blazor-azure/.morph/templates/agent.cs +0 -163
  298. package/stacks/blazor-azure/.morph/templates/clarify-questions.md +0 -159
  299. package/stacks/blazor-azure/.morph/templates/component.razor +0 -239
  300. package/stacks/blazor-azure/.morph/templates/contracts/Commands.cs +0 -74
  301. package/stacks/blazor-azure/.morph/templates/contracts/Entities.cs +0 -25
  302. package/stacks/blazor-azure/.morph/templates/contracts/Queries.cs +0 -74
  303. package/stacks/blazor-azure/.morph/templates/contracts/README.md +0 -74
  304. package/stacks/blazor-azure/.morph/templates/contracts.cs +0 -217
  305. package/stacks/blazor-azure/.morph/templates/decisions.md +0 -123
  306. package/stacks/blazor-azure/.morph/templates/design-system.css +0 -226
  307. package/stacks/blazor-azure/.morph/templates/infra/.dockerignore.example +0 -89
  308. package/stacks/blazor-azure/.morph/templates/infra/Dockerfile.example +0 -82
  309. package/stacks/blazor-azure/.morph/templates/infra/README.md +0 -286
  310. package/stacks/blazor-azure/.morph/templates/infra/app-insights.bicep +0 -63
  311. package/stacks/blazor-azure/.morph/templates/infra/app-service.bicep +0 -164
  312. package/stacks/blazor-azure/.morph/templates/infra/azure-pipelines-deploy.yml +0 -480
  313. package/stacks/blazor-azure/.morph/templates/infra/container-app-env.bicep +0 -49
  314. package/stacks/blazor-azure/.morph/templates/infra/container-app.bicep +0 -156
  315. package/stacks/blazor-azure/.morph/templates/infra/deploy-checklist.md +0 -426
  316. package/stacks/blazor-azure/.morph/templates/infra/deploy.ps1 +0 -229
  317. package/stacks/blazor-azure/.morph/templates/infra/deploy.sh +0 -208
  318. package/stacks/blazor-azure/.morph/templates/infra/key-vault.bicep +0 -91
  319. package/stacks/blazor-azure/.morph/templates/infra/main.bicep +0 -189
  320. package/stacks/blazor-azure/.morph/templates/infra/parameters.dev.json +0 -29
  321. package/stacks/blazor-azure/.morph/templates/infra/parameters.prod.json +0 -29
  322. package/stacks/blazor-azure/.morph/templates/infra/parameters.staging.json +0 -29
  323. package/stacks/blazor-azure/.morph/templates/infra/sql-database.bicep +0 -103
  324. package/stacks/blazor-azure/.morph/templates/infra/storage.bicep +0 -106
  325. package/stacks/blazor-azure/.morph/templates/integrations/asaas-client.cs +0 -387
  326. package/stacks/blazor-azure/.morph/templates/integrations/asaas-webhook.cs +0 -351
  327. package/stacks/blazor-azure/.morph/templates/integrations/azure-identity-config.cs +0 -288
  328. package/stacks/blazor-azure/.morph/templates/integrations/clerk-config.cs +0 -258
  329. package/stacks/blazor-azure/.morph/templates/job.cs +0 -171
  330. package/stacks/blazor-azure/.morph/templates/migration.cs +0 -83
  331. package/stacks/blazor-azure/.morph/templates/proposal.md +0 -141
  332. package/stacks/blazor-azure/.morph/templates/recap.md +0 -94
  333. package/stacks/blazor-azure/.morph/templates/repository.cs +0 -141
  334. package/stacks/blazor-azure/.morph/templates/saas/subscription.cs +0 -347
  335. package/stacks/blazor-azure/.morph/templates/saas/tenant.cs +0 -338
  336. package/stacks/blazor-azure/.morph/templates/service.cs +0 -139
  337. package/stacks/blazor-azure/.morph/templates/simulation.md +0 -353
  338. package/stacks/blazor-azure/.morph/templates/spec.md +0 -149
  339. package/stacks/blazor-azure/.morph/templates/sprint-status.yaml +0 -68
  340. package/stacks/blazor-azure/.morph/templates/state.template.json +0 -222
  341. package/stacks/blazor-azure/.morph/templates/story.md +0 -143
  342. package/stacks/blazor-azure/.morph/templates/tasks.md +0 -257
  343. package/stacks/blazor-azure/.morph/templates/test.cs +0 -239
  344. package/stacks/blazor-azure/.morph/templates/ui-components.md +0 -362
  345. package/stacks/blazor-azure/.morph/templates/ui-design-system.md +0 -286
  346. package/stacks/blazor-azure/.morph/templates/ui-flows.md +0 -336
  347. package/stacks/blazor-azure/.morph/templates/ui-mockups.md +0 -133
  348. package/stacks/blazor-azure/.morph/test-infra/example.bicep +0 -59
  349. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
  350. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
  351. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
  352. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -170
  353. package/stacks/nextjs-supabase/.morph/docs/easypanel-setup.md +0 -169
  354. package/stacks/nextjs-supabase/.morph/docs/supabase-mcp-setup.md +0 -247
  355. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/README.md +0 -697
  356. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/spec.md +0 -85
  357. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/tasks.md +0 -86
  358. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/README.md +0 -498
  359. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/decisions.md +0 -121
  360. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/spec.md +0 -138
  361. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/tasks.md +0 -162
  362. package/stacks/nextjs-supabase/.morph/standards/easypanel-deploy.md +0 -191
  363. package/stacks/nextjs-supabase/.morph/standards/nextjs-patterns.md +0 -193
  364. package/stacks/nextjs-supabase/.morph/standards/supabase-auth.md +0 -171
  365. package/stacks/nextjs-supabase/.morph/standards/supabase-pgvector.md +0 -164
  366. package/stacks/nextjs-supabase/.morph/standards/supabase-rls.md +0 -179
  367. package/stacks/nextjs-supabase/.morph/standards/supabase-storage.md +0 -148
  368. package/stacks/nextjs-supabase/.morph/templates/contracts.cs +0 -173
  369. package/stacks/nextjs-supabase/.morph/templates/contracts.ts +0 -168
  370. package/stacks/nextjs-supabase/.morph/templates/decisions.md +0 -115
  371. package/stacks/nextjs-supabase/.morph/templates/dockerfile-api.dockerfile +0 -38
  372. package/stacks/nextjs-supabase/.morph/templates/dockerfile-web.dockerfile +0 -48
  373. package/stacks/nextjs-supabase/.morph/templates/proposal.md +0 -145
  374. package/stacks/nextjs-supabase/.morph/templates/recap.md +0 -134
  375. package/stacks/nextjs-supabase/.morph/templates/rls-policy.sql +0 -57
  376. package/stacks/nextjs-supabase/.morph/templates/spec.md +0 -231
  377. package/stacks/nextjs-supabase/.morph/templates/supabase-migration.sql +0 -100
  378. package/stacks/nextjs-supabase/.morph/templates/tasks.md +0 -257
  379. /package/src/commands/{search-patterns.js → learning/search-patterns.js} +0 -0
  380. /package/src/{lib → core/templates}/template-data-sources.js +0 -0
  381. /package/src/lib/{design-system-generator.js → generators/design-system-generator.js} +0 -0
  382. /package/src/lib/{learning-system.js → learning/learning-system.js} +0 -0
  383. /package/src/lib/validators/{package-validator.js → packages/package-validator.js} +0 -0
  384. /package/src/lib/validators/{ui-contrast-validator.js → ui/ui-contrast-validator.js} +0 -0
  385. /package/{src/generator → stacks/blazor-azure/.morph/templates}/.gitkeep +0 -0
  386. /package/{src/llm → stacks/nextjs-supabase/.morph/templates}/.gitkeep +0 -0
@@ -1,534 +1,588 @@
1
- /**
2
- * MORPH-SPEC State Manager Library
3
- *
4
- * Manages state.json for tracking features, progress, agents, and checkpoints.
5
- * Used both by CLI commands and internal automation.
6
- */
7
-
8
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
9
- import { join, dirname } from 'path';
10
-
11
- const STATE_FILE_NAME = '.morph/state.json';
12
-
13
- // ============================================================================
14
- // Core Functions
15
- // ============================================================================
16
-
17
- /**
18
- * Get the state file path (looks in current working directory)
19
- */
20
- export function getStatePath() {
21
- return join(process.cwd(), STATE_FILE_NAME);
22
- }
23
-
24
- /**
25
- * Check if state file exists
26
- */
27
- export function stateExists() {
28
- return existsSync(getStatePath());
29
- }
30
-
31
- /**
32
- * Load state from disk
33
- * @param {boolean} throwOnError - If false, returns null instead of throwing
34
- * @returns {Object|null} State object or null
35
- */
36
- export function loadState(throwOnError = true) {
37
- const statePath = getStatePath();
38
-
39
- if (!existsSync(statePath)) {
40
- if (throwOnError) {
41
- throw new Error(`State file not found: ${statePath}\nRun 'morph-spec state init' first.`);
42
- }
43
- return null;
44
- }
45
-
46
- try {
47
- const content = readFileSync(statePath, 'utf8');
48
- return JSON.parse(content);
49
- } catch (err) {
50
- if (throwOnError) {
51
- throw new Error(`Failed to parse state.json: ${err.message}`);
52
- }
53
- return null;
54
- }
55
- }
56
-
57
- /**
58
- * Save state to disk
59
- * @param {Object} state - State object to save
60
- */
61
- export function saveState(state) {
62
- state.metadata = state.metadata || {};
63
- state.metadata.lastUpdated = new Date().toISOString();
64
-
65
- const statePath = getStatePath();
66
- const stateDir = dirname(statePath);
67
-
68
- // Ensure .morph directory exists
69
- if (!existsSync(stateDir)) {
70
- mkdirSync(stateDir, { recursive: true });
71
- }
72
-
73
- writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
74
- }
75
-
76
- /**
77
- * Initialize new state file
78
- * @param {Object} options - Options
79
- * @param {boolean} options.force - Overwrite existing file
80
- * @param {string} options.projectName - Project name
81
- * @param {string} options.projectType - Project type (e.g., 'blazor-server')
82
- * @returns {Object} Initial state
83
- */
84
- export function initState(options = {}) {
85
- const { force = false, projectName = '{PROJECT_NAME}', projectType = 'blazor-server' } = options;
86
-
87
- if (stateExists() && !force) {
88
- throw new Error('State file already exists. Use force=true to overwrite.');
89
- }
90
-
91
- const initialState = {
92
- version: "2.1.1",
93
- project: {
94
- name: projectName,
95
- type: projectType,
96
- createdAt: new Date().toISOString(),
97
- updatedAt: new Date().toISOString()
98
- },
99
- features: {},
100
- metadata: {
101
- totalFeatures: 0,
102
- completedFeatures: 0,
103
- totalTimeSpent: 0,
104
- lastUpdated: new Date().toISOString()
105
- }
106
- };
107
-
108
- saveState(initialState);
109
- return initialState;
110
- }
111
-
112
- // ============================================================================
113
- // Feature Operations
114
- // ============================================================================
115
-
116
- /**
117
- * Get feature from state
118
- * @param {string} featureName - Feature name
119
- * @returns {Object|null} Feature object or null
120
- */
121
- export function getFeature(featureName) {
122
- const state = loadState();
123
- return state.features[featureName] || null;
124
- }
125
-
126
- /**
127
- * Create or get feature with default structure
128
- * @param {string} featureName - Feature name
129
- * @returns {Object} Feature object
130
- */
131
- function ensureFeature(featureName) {
132
- const state = loadState();
133
-
134
- if (!state.features[featureName]) {
135
- state.features[featureName] = {
136
- status: "draft",
137
- phase: "proposal",
138
- workflow: "auto", // auto | fast-track | standard | full-morph
139
- createdAt: new Date().toISOString(),
140
- updatedAt: new Date().toISOString(),
141
- activeAgents: [],
142
- approvalGates: {
143
- proposal: { approved: false, timestamp: null, approvedBy: null },
144
- uiux: { approved: false, timestamp: null, approvedBy: null },
145
- design: { approved: false, timestamp: null, approvedBy: null },
146
- tasks: { approved: false, timestamp: null, approvedBy: null }
147
- },
148
- outputs: {
149
- proposal: { created: false, path: `.morph/project/outputs/${featureName}/proposal.md` },
150
- spec: { created: false, path: `.morph/project/outputs/${featureName}/spec.md` },
151
- contracts: { created: false, path: `.morph/project/outputs/${featureName}/contracts.cs` },
152
- tasks: { created: false, path: `.morph/project/outputs/${featureName}/tasks.json` },
153
- uiDesignSystem: { created: false, path: `.morph/project/outputs/${featureName}/ui-design-system.md` },
154
- uiMockups: { created: false, path: `.morph/project/outputs/${featureName}/ui-mockups.md` },
155
- uiComponents: { created: false, path: `.morph/project/outputs/${featureName}/ui-components.md` },
156
- uiFlows: { created: false, path: `.morph/project/outputs/${featureName}/ui-flows.md` },
157
- decisions: { created: false, path: `.morph/project/outputs/${featureName}/decisions.md` },
158
- recap: { created: false, path: `.morph/project/outputs/${featureName}/recap.md` }
159
- },
160
- tasks: {
161
- total: 0,
162
- completed: 0,
163
- inProgress: 0,
164
- pending: 0
165
- },
166
- checkpoints: []
167
- };
168
-
169
- state.metadata.totalFeatures++;
170
- saveState(state);
171
- }
172
-
173
- return state.features[featureName];
174
- }
175
-
176
- /**
177
- * Update feature property (supports nested keys like "tasks.completed")
178
- * @param {string} featureName - Feature name
179
- * @param {string} key - Property key (supports dot notation)
180
- * @param {any} value - Value to set
181
- */
182
- export function updateFeature(featureName, key, value) {
183
- ensureFeature(featureName);
184
- const state = loadState(); // Load AFTER ensuring feature exists
185
-
186
- const keys = key.split('.');
187
- let target = state.features[featureName];
188
-
189
- for (let i = 0; i < keys.length - 1; i++) {
190
- if (!target[keys[i]]) {
191
- target[keys[i]] = {};
192
- }
193
- target = target[keys[i]];
194
- }
195
-
196
- const finalKey = keys[keys.length - 1];
197
- target[finalKey] = value;
198
- state.features[featureName].updatedAt = new Date().toISOString();
199
-
200
- saveState(state);
201
- }
202
-
203
- /**
204
- * Update multiple feature properties at once
205
- * @param {string} featureName - Feature name
206
- * @param {Object} updates - Object with key-value pairs to update
207
- */
208
- export function updateFeatureMultiple(featureName, updates) {
209
- ensureFeature(featureName);
210
- const state = loadState(); // Load AFTER ensuring feature exists
211
-
212
- for (const [key, value] of Object.entries(updates)) {
213
- const keys = key.split('.');
214
- let target = state.features[featureName];
215
-
216
- for (let i = 0; i < keys.length - 1; i++) {
217
- if (!target[keys[i]]) {
218
- target[keys[i]] = {};
219
- }
220
- target = target[keys[i]];
221
- }
222
-
223
- const finalKey = keys[keys.length - 1];
224
- target[finalKey] = value;
225
- }
226
-
227
- state.features[featureName].updatedAt = new Date().toISOString();
228
- saveState(state);
229
- }
230
-
231
- /**
232
- * Add checkpoint to feature
233
- * @param {string} featureName - Feature name
234
- * @param {string} note - Checkpoint note
235
- * @returns {Object} Checkpoint object
236
- */
237
- export function addCheckpoint(featureName, note) {
238
- ensureFeature(featureName);
239
- const state = loadState();
240
- const feature = state.features[featureName];
241
-
242
- const checkpoint = {
243
- timestamp: new Date().toISOString(),
244
- phase: feature.phase,
245
- completedTasks: feature.tasks.completed,
246
- note: note
247
- };
248
-
249
- feature.checkpoints.push(checkpoint);
250
- feature.updatedAt = new Date().toISOString();
251
-
252
- saveState(state);
253
- return checkpoint;
254
- }
255
-
256
- /**
257
- * Add agent to feature
258
- * @param {string} featureName - Feature name
259
- * @param {string} agentId - Agent ID
260
- * @returns {boolean} True if added, false if already exists
261
- */
262
- export function addAgent(featureName, agentId) {
263
- ensureFeature(featureName);
264
- const state = loadState(); // Load AFTER ensuring feature exists
265
-
266
- if (!state.features[featureName].activeAgents.includes(agentId)) {
267
- state.features[featureName].activeAgents.push(agentId);
268
- state.features[featureName].updatedAt = new Date().toISOString();
269
- saveState(state);
270
- return true;
271
- }
272
-
273
- return false;
274
- }
275
-
276
- /**
277
- * Remove agent from feature
278
- * @param {string} featureName - Feature name
279
- * @param {string} agentId - Agent ID
280
- * @returns {boolean} True if removed, false if not found
281
- */
282
- export function removeAgent(featureName, agentId) {
283
- const state = loadState();
284
-
285
- if (!state.features[featureName]) {
286
- throw new Error(`Feature '${featureName}' not found.`);
287
- }
288
-
289
- const index = state.features[featureName].activeAgents.indexOf(agentId);
290
- if (index > -1) {
291
- state.features[featureName].activeAgents.splice(index, 1);
292
- state.features[featureName].updatedAt = new Date().toISOString();
293
- saveState(state);
294
- return true;
295
- }
296
-
297
- return false;
298
- }
299
-
300
- /**
301
- * Normalize output type from kebab-case to camelCase for UI types (BUG #12 fix)
302
- * @param {string} type - Output type (e.g., 'ui-design-system' or 'uiDesignSystem')
303
- * @returns {string} Normalized type in camelCase
304
- */
305
- function normalizeOutputType(type) {
306
- const kebabMap = {
307
- 'ui-design-system': 'uiDesignSystem',
308
- 'ui-mockups': 'uiMockups',
309
- 'ui-components': 'uiComponents',
310
- 'ui-flows': 'uiFlows'
311
- };
312
- return kebabMap[type] || type;
313
- }
314
-
315
- /**
316
- * Mark output as created
317
- * @param {string} featureName - Feature name
318
- * @param {string} outputType - Output type (proposal, spec, contracts, etc.)
319
- */
320
- export function markOutput(featureName, outputType) {
321
- ensureFeature(featureName);
322
- const state = loadState();
323
-
324
- const normalized = normalizeOutputType(outputType);
325
-
326
- if (!state.features[featureName].outputs[normalized]) {
327
- throw new Error(`Output type '${outputType}' not valid. Valid types: proposal, spec, contracts, tasks, uiDesignSystem (or ui-design-system), uiMockups (or ui-mockups), uiComponents (or ui-components), uiFlows (or ui-flows), decisions, recap`);
328
- }
329
-
330
- state.features[featureName].outputs[normalized].created = true;
331
- state.features[featureName].updatedAt = new Date().toISOString();
332
-
333
- // If marking tasks output, try to sync task count from state tasks array
334
- if (normalized === 'tasks') {
335
- syncTasksCount(state.features[featureName]);
336
- }
337
-
338
- saveState(state);
339
- }
340
-
341
- /**
342
- * Sync tasks count from tasks array (if exists)
343
- * @param {Object} feature - Feature object
344
- */
345
- function syncTasksCount(feature) {
346
- // If feature has tasks array (schema 3.0), count them
347
- if (feature.tasks && Array.isArray(feature.tasks)) {
348
- const tasks = feature.tasks;
349
- feature.progress = {
350
- total: tasks.length,
351
- completed: tasks.filter(t => t.status === 'completed').length,
352
- inProgress: tasks.filter(t => t.status === 'in_progress').length,
353
- pending: tasks.filter(t => t.status === 'pending').length,
354
- percentage: tasks.length > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / tasks.length) * 100) : 0
355
- };
356
- }
357
- }
358
-
359
- /**
360
- * Skip a phase and record it
361
- * @param {string} featureName - Feature name
362
- * @param {string} phase - Phase to skip
363
- * @param {string} reason - Reason for skipping
364
- */
365
- export function skipPhase(featureName, phase, reason = '') {
366
- ensureFeature(featureName);
367
- const state = loadState();
368
-
369
- if (!state.features[featureName].skippedPhases) {
370
- state.features[featureName].skippedPhases = [];
371
- }
372
-
373
- // Don't add duplicates
374
- const existing = state.features[featureName].skippedPhases.find(s => s.phase === phase);
375
- if (!existing) {
376
- state.features[featureName].skippedPhases.push({
377
- phase,
378
- reason,
379
- timestamp: new Date().toISOString()
380
- });
381
- }
382
-
383
- state.features[featureName].updatedAt = new Date().toISOString();
384
- saveState(state);
385
- }
386
-
387
- /**
388
- * Get skipped phases for a feature
389
- * @param {string} featureName - Feature name
390
- * @returns {Array} Array of skipped phases
391
- */
392
- export function getSkippedPhases(featureName) {
393
- const state = loadState();
394
- if (!state.features[featureName]) {
395
- return [];
396
- }
397
- return state.features[featureName].skippedPhases || [];
398
- }
399
-
400
- /**
401
- * List all features
402
- * @returns {Array} Array of [featureName, featureObject] tuples
403
- */
404
- export function listFeatures() {
405
- const state = loadState();
406
- return Object.entries(state.features);
407
- }
408
-
409
- /**
410
- * Get project summary
411
- * @returns {Object} Summary with metadata
412
- */
413
- export function getSummary() {
414
- const state = loadState();
415
- return {
416
- project: state.project,
417
- metadata: state.metadata,
418
- featuresCount: Object.keys(state.features).length
419
- };
420
- }
421
-
422
- // ============================================================================
423
- // Approval Gates Operations
424
- // ============================================================================
425
-
426
- /**
427
- * Set approval gate status
428
- * @param {string} featureName - Feature name
429
- * @param {string} gate - Gate name (proposal, uiux, design, tasks)
430
- * @param {boolean} approved - Approval status
431
- * @param {Object} metadata - Additional metadata (approvedBy, reason, etc.)
432
- */
433
- export function setApprovalGate(featureName, gate, approved, metadata = {}) {
434
- const state = loadState();
435
- const feature = ensureFeature(featureName);
436
-
437
- if (!feature.approvalGates) {
438
- feature.approvalGates = {
439
- proposal: { approved: false, timestamp: null, approvedBy: null },
440
- uiux: { approved: false, timestamp: null, approvedBy: null },
441
- design: { approved: false, timestamp: null, approvedBy: null },
442
- tasks: { approved: false, timestamp: null, approvedBy: null }
443
- };
444
- }
445
-
446
- feature.approvalGates[gate] = {
447
- approved,
448
- timestamp: metadata.approvedAt || metadata.rejectedAt || new Date().toISOString(),
449
- approvedBy: metadata.approvedBy || metadata.rejectedBy || null,
450
- ...metadata
451
- };
452
-
453
- state.features[featureName] = feature;
454
- saveState(state);
455
- }
456
-
457
- /**
458
- * Get approval gate status
459
- * @param {string} featureName - Feature name
460
- * @param {string} gate - Gate name
461
- * @returns {Object|null} Gate object or null
462
- */
463
- export function getApprovalGate(featureName, gate) {
464
- const state = loadState();
465
- const feature = state.features[featureName];
466
-
467
- if (!feature || !feature.approvalGates) {
468
- return null;
469
- }
470
-
471
- return feature.approvalGates[gate] || null;
472
- }
473
-
474
- /**
475
- * Check if feature is pending approval
476
- * @param {string} featureName - Feature name
477
- * @returns {boolean} True if any gate is pending approval
478
- */
479
- export function isPendingApproval(featureName) {
480
- const state = loadState();
481
- const feature = state.features[featureName];
482
-
483
- if (!feature || !feature.approvalGates) {
484
- return false;
485
- }
486
-
487
- const currentPhase = feature.phase;
488
-
489
- // Check if current phase has an approval gate
490
- const phaseGateMap = {
491
- 'design': 'design',
492
- 'tasks': 'tasks',
493
- 'uiux': 'uiux'
494
- };
495
-
496
- const relevantGate = phaseGateMap[currentPhase];
497
- if (!relevantGate) {
498
- return false;
499
- }
500
-
501
- const gate = feature.approvalGates[relevantGate];
502
- return gate && !gate.approved;
503
- }
504
-
505
- /**
506
- * Get approval history for a feature
507
- * @param {string} featureName - Feature name
508
- * @returns {Array} Array of approval events
509
- */
510
- export function getApprovalHistory(featureName) {
511
- const state = loadState();
512
- const feature = state.features[featureName];
513
-
514
- if (!feature || !feature.approvalGates) {
515
- return [];
516
- }
517
-
518
- const history = [];
519
-
520
- Object.entries(feature.approvalGates).forEach(([gate, data]) => {
521
- if (data.timestamp) {
522
- history.push({
523
- gate,
524
- approved: data.approved,
525
- timestamp: data.timestamp,
526
- approvedBy: data.approvedBy || data.rejectedBy,
527
- reason: data.reason
528
- });
529
- }
530
- });
531
-
532
- // Sort by timestamp
533
- return history.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
534
- }
1
+ /**
2
+ * MORPH-SPEC State Manager Library
3
+ *
4
+ * Manages state.json for tracking features, progress, agents, and checkpoints.
5
+ * Used both by CLI commands and internal automation.
6
+ */
7
+
8
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
9
+ import { join, dirname } from 'path';
10
+ import { detectWorkflow } from '../workflows/workflow-detector.js';
11
+
12
+ const STATE_FILE_NAME = '.morph/state.json';
13
+
14
+ // ============================================================================
15
+ // Core Functions
16
+ // ============================================================================
17
+
18
+ /**
19
+ * Get the state file path (looks in current working directory)
20
+ */
21
+ export function getStatePath() {
22
+ return join(process.cwd(), STATE_FILE_NAME);
23
+ }
24
+
25
+ /**
26
+ * Check if state file exists
27
+ */
28
+ export function stateExists() {
29
+ return existsSync(getStatePath());
30
+ }
31
+
32
+ /**
33
+ * Load state from disk
34
+ * @param {boolean} throwOnError - If false, returns null instead of throwing
35
+ * @returns {Object|null} State object or null
36
+ */
37
+ export function loadState(throwOnError = true) {
38
+ const statePath = getStatePath();
39
+
40
+ if (!existsSync(statePath)) {
41
+ if (throwOnError) {
42
+ throw new Error(`State file not found: ${statePath}\nRun 'morph-spec state init' first.`);
43
+ }
44
+ return null;
45
+ }
46
+
47
+ try {
48
+ const content = readFileSync(statePath, 'utf8');
49
+ return JSON.parse(content);
50
+ } catch (err) {
51
+ if (throwOnError) {
52
+ throw new Error(`Failed to parse state.json: ${err.message}`);
53
+ }
54
+ return null;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Save state to disk
60
+ * @param {Object} state - State object to save
61
+ */
62
+ export function saveState(state) {
63
+ state.metadata = state.metadata || {};
64
+ state.metadata.lastUpdated = new Date().toISOString();
65
+
66
+ const statePath = getStatePath();
67
+ const stateDir = dirname(statePath);
68
+
69
+ // Ensure .morph directory exists
70
+ if (!existsSync(stateDir)) {
71
+ mkdirSync(stateDir, { recursive: true });
72
+ }
73
+
74
+ writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
75
+ }
76
+
77
+ /**
78
+ * Initialize new state file
79
+ * @param {Object} options - Options
80
+ * @param {boolean} options.force - Overwrite existing file
81
+ * @param {string} options.projectName - Project name
82
+ * @param {string} options.projectType - Project type (e.g., 'blazor-server')
83
+ * @returns {Object} Initial state
84
+ */
85
+ export function initState(options = {}) {
86
+ const { force = false, projectName = '{PROJECT_NAME}', projectType = 'blazor-server' } = options;
87
+
88
+ if (stateExists() && !force) {
89
+ throw new Error('State file already exists. Use force=true to overwrite.');
90
+ }
91
+
92
+ const initialState = {
93
+ version: "3.0.0",
94
+ project: {
95
+ name: projectName,
96
+ type: projectType,
97
+ createdAt: new Date().toISOString(),
98
+ updatedAt: new Date().toISOString()
99
+ },
100
+ features: {},
101
+ threads: {},
102
+ metadata: {
103
+ totalFeatures: 0,
104
+ completedFeatures: 0,
105
+ totalTimeSpent: 0,
106
+ lastUpdated: new Date().toISOString()
107
+ }
108
+ };
109
+
110
+ saveState(initialState);
111
+ return initialState;
112
+ }
113
+
114
+ // ============================================================================
115
+ // Feature Operations
116
+ // ============================================================================
117
+
118
+ /**
119
+ * Get feature from state
120
+ * @param {string} featureName - Feature name
121
+ * @returns {Object|null} Feature object or null
122
+ */
123
+ export function getFeature(featureName) {
124
+ const state = loadState();
125
+ return state.features[featureName] || null;
126
+ }
127
+
128
+ /**
129
+ * Create or get feature with default structure
130
+ * @param {string} featureName - Feature name
131
+ * @param {Object} options - Options
132
+ * @param {string} options.userRequest - User's feature request for workflow detection
133
+ * @param {string} options.workflow - Manual workflow override
134
+ * @param {string} options.projectPath - Project path
135
+ * @returns {Promise<Object>} Feature object
136
+ */
137
+ async function ensureFeature(featureName, options = {}) {
138
+ const state = loadState();
139
+
140
+ if (!state.features[featureName]) {
141
+ // Detect workflow if userRequest provided
142
+ let workflowId = 'auto';
143
+ let workflowDetection = {
144
+ auto: false,
145
+ confidence: 0,
146
+ userOverride: false
147
+ };
148
+
149
+ if (options.workflow) {
150
+ // Manual override
151
+ workflowId = options.workflow;
152
+ workflowDetection.userOverride = true;
153
+ } else if (options.userRequest) {
154
+ // Auto-detect
155
+ try {
156
+ const detection = await detectWorkflow({
157
+ userRequest: options.userRequest,
158
+ projectPath: options.projectPath || '.',
159
+ featureName
160
+ });
161
+ workflowId = detection.workflowId;
162
+ workflowDetection.auto = true;
163
+ workflowDetection.confidence = detection.confidence;
164
+ workflowDetection.matchedKeywords = detection.matchedKeywords;
165
+ workflowDetection.estimatedComplexity = detection.estimatedComplexity;
166
+ workflowDetection.reasoning = detection.reasoning;
167
+ } catch (err) {
168
+ // If detection fails, fall back to auto
169
+ console.warn('Workflow detection failed:', err.message);
170
+ }
171
+ }
172
+
173
+ state.features[featureName] = {
174
+ status: "draft",
175
+ phase: "proposal",
176
+ workflow: workflowId, // auto | fast-track | standard | full-morph | design-impl | ui-refresh
177
+ workflowDetection,
178
+ createdAt: new Date().toISOString(),
179
+ updatedAt: new Date().toISOString(),
180
+ activeAgents: [],
181
+ approvalGates: {
182
+ proposal: { approved: false, timestamp: null, approvedBy: null },
183
+ uiux: { approved: false, timestamp: null, approvedBy: null },
184
+ design: { approved: false, timestamp: null, approvedBy: null },
185
+ tasks: { approved: false, timestamp: null, approvedBy: null }
186
+ },
187
+ outputs: {
188
+ proposal: { created: false, path: `.morph/project/outputs/${featureName}/proposal.md` },
189
+ spec: { created: false, path: `.morph/project/outputs/${featureName}/spec.md` },
190
+ contracts: { created: false, path: `.morph/project/outputs/${featureName}/contracts.cs` },
191
+ tasks: { created: false, path: `.morph/project/outputs/${featureName}/tasks.json` },
192
+ uiDesignSystem: { created: false, path: `.morph/project/outputs/${featureName}/ui-design-system.md` },
193
+ uiMockups: { created: false, path: `.morph/project/outputs/${featureName}/ui-mockups.md` },
194
+ uiComponents: { created: false, path: `.morph/project/outputs/${featureName}/ui-components.md` },
195
+ uiFlows: { created: false, path: `.morph/project/outputs/${featureName}/ui-flows.md` },
196
+ decisions: { created: false, path: `.morph/project/outputs/${featureName}/decisions.md` },
197
+ recap: { created: false, path: `.morph/project/outputs/${featureName}/recap.md` }
198
+ },
199
+ tasks: {
200
+ total: 0,
201
+ completed: 0,
202
+ inProgress: 0,
203
+ pending: 0
204
+ },
205
+ checkpoints: [],
206
+ threadMetrics: {
207
+ totalThreads: 0,
208
+ parallelPeak: 0,
209
+ avgDuration: 0,
210
+ checkpointPassRate: 100
211
+ },
212
+ trustConfig: {
213
+ level: 'low',
214
+ history: [],
215
+ autoApprove: {
216
+ design: false,
217
+ tasks: false
218
+ }
219
+ },
220
+ contextBundles: []
221
+ };
222
+
223
+ state.metadata.totalFeatures++;
224
+ saveState(state);
225
+ }
226
+
227
+ return state.features[featureName];
228
+ }
229
+
230
+ /**
231
+ * Update feature property (supports nested keys like "tasks.completed")
232
+ * @param {string} featureName - Feature name
233
+ * @param {string} key - Property key (supports dot notation)
234
+ * @param {any} value - Value to set
235
+ */
236
+ export async function updateFeature(featureName, key, value) {
237
+ await ensureFeature(featureName);
238
+ const state = loadState(); // Load AFTER ensuring feature exists
239
+
240
+ const keys = key.split('.');
241
+ let target = state.features[featureName];
242
+
243
+ for (let i = 0; i < keys.length - 1; i++) {
244
+ if (!target[keys[i]]) {
245
+ target[keys[i]] = {};
246
+ }
247
+ target = target[keys[i]];
248
+ }
249
+
250
+ const finalKey = keys[keys.length - 1];
251
+ target[finalKey] = value;
252
+ state.features[featureName].updatedAt = new Date().toISOString();
253
+
254
+ saveState(state);
255
+ }
256
+
257
+ /**
258
+ * Update multiple feature properties at once
259
+ * @param {string} featureName - Feature name
260
+ * @param {Object} updates - Object with key-value pairs to update
261
+ */
262
+ export async function updateFeatureMultiple(featureName, updates) {
263
+ await ensureFeature(featureName);
264
+ const state = loadState(); // Load AFTER ensuring feature exists
265
+
266
+ for (const [key, value] of Object.entries(updates)) {
267
+ const keys = key.split('.');
268
+ let target = state.features[featureName];
269
+
270
+ for (let i = 0; i < keys.length - 1; i++) {
271
+ if (!target[keys[i]]) {
272
+ target[keys[i]] = {};
273
+ }
274
+ target = target[keys[i]];
275
+ }
276
+
277
+ const finalKey = keys[keys.length - 1];
278
+ target[finalKey] = value;
279
+ }
280
+
281
+ state.features[featureName].updatedAt = new Date().toISOString();
282
+ saveState(state);
283
+ }
284
+
285
+ /**
286
+ * Add checkpoint to feature
287
+ * @param {string} featureName - Feature name
288
+ * @param {string} note - Checkpoint note
289
+ * @returns {Object} Checkpoint object
290
+ */
291
+ export async function addCheckpoint(featureName, note) {
292
+ await ensureFeature(featureName);
293
+ const state = loadState();
294
+ const feature = state.features[featureName];
295
+
296
+ const checkpoint = {
297
+ timestamp: new Date().toISOString(),
298
+ phase: feature.phase,
299
+ completedTasks: feature.tasks.completed,
300
+ note: note
301
+ };
302
+
303
+ feature.checkpoints.push(checkpoint);
304
+ feature.updatedAt = new Date().toISOString();
305
+
306
+ saveState(state);
307
+ return checkpoint;
308
+ }
309
+
310
+ /**
311
+ * Add agent to feature
312
+ * @param {string} featureName - Feature name
313
+ * @param {string} agentId - Agent ID
314
+ * @returns {boolean} True if added, false if already exists
315
+ */
316
+ export async function addAgent(featureName, agentId) {
317
+ await ensureFeature(featureName);
318
+ const state = loadState(); // Load AFTER ensuring feature exists
319
+
320
+ if (!state.features[featureName].activeAgents.includes(agentId)) {
321
+ state.features[featureName].activeAgents.push(agentId);
322
+ state.features[featureName].updatedAt = new Date().toISOString();
323
+ saveState(state);
324
+ return true;
325
+ }
326
+
327
+ return false;
328
+ }
329
+
330
+ /**
331
+ * Remove agent from feature
332
+ * @param {string} featureName - Feature name
333
+ * @param {string} agentId - Agent ID
334
+ * @returns {boolean} True if removed, false if not found
335
+ */
336
+ export function removeAgent(featureName, agentId) {
337
+ const state = loadState();
338
+
339
+ if (!state.features[featureName]) {
340
+ throw new Error(`Feature '${featureName}' not found.`);
341
+ }
342
+
343
+ const index = state.features[featureName].activeAgents.indexOf(agentId);
344
+ if (index > -1) {
345
+ state.features[featureName].activeAgents.splice(index, 1);
346
+ state.features[featureName].updatedAt = new Date().toISOString();
347
+ saveState(state);
348
+ return true;
349
+ }
350
+
351
+ return false;
352
+ }
353
+
354
+ /**
355
+ * Normalize output type from kebab-case to camelCase for UI types (BUG #12 fix)
356
+ * @param {string} type - Output type (e.g., 'ui-design-system' or 'uiDesignSystem')
357
+ * @returns {string} Normalized type in camelCase
358
+ */
359
+ function normalizeOutputType(type) {
360
+ const kebabMap = {
361
+ 'ui-design-system': 'uiDesignSystem',
362
+ 'ui-mockups': 'uiMockups',
363
+ 'ui-components': 'uiComponents',
364
+ 'ui-flows': 'uiFlows'
365
+ };
366
+ return kebabMap[type] || type;
367
+ }
368
+
369
+ /**
370
+ * Mark output as created
371
+ * @param {string} featureName - Feature name
372
+ * @param {string} outputType - Output type (proposal, spec, contracts, etc.)
373
+ */
374
+ export async function markOutput(featureName, outputType) {
375
+ await ensureFeature(featureName);
376
+ const state = loadState();
377
+
378
+ const normalized = normalizeOutputType(outputType);
379
+
380
+ if (!state.features[featureName].outputs[normalized]) {
381
+ throw new Error(`Output type '${outputType}' not valid. Valid types: proposal, spec, contracts, tasks, uiDesignSystem (or ui-design-system), uiMockups (or ui-mockups), uiComponents (or ui-components), uiFlows (or ui-flows), decisions, recap`);
382
+ }
383
+
384
+ state.features[featureName].outputs[normalized].created = true;
385
+ state.features[featureName].updatedAt = new Date().toISOString();
386
+
387
+ // If marking tasks output, try to sync task count from state tasks array
388
+ if (normalized === 'tasks') {
389
+ syncTasksCount(state.features[featureName]);
390
+ }
391
+
392
+ saveState(state);
393
+ }
394
+
395
+ /**
396
+ * Sync tasks count from tasks array (if exists)
397
+ * @param {Object} feature - Feature object
398
+ */
399
+ function syncTasksCount(feature) {
400
+ // If feature has tasks array (schema 3.0), count them
401
+ if (feature.tasks && Array.isArray(feature.tasks)) {
402
+ const tasks = feature.tasks;
403
+ feature.progress = {
404
+ total: tasks.length,
405
+ completed: tasks.filter(t => t.status === 'completed').length,
406
+ inProgress: tasks.filter(t => t.status === 'in_progress').length,
407
+ pending: tasks.filter(t => t.status === 'pending').length,
408
+ percentage: tasks.length > 0 ? Math.round((tasks.filter(t => t.status === 'completed').length / tasks.length) * 100) : 0
409
+ };
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Skip a phase and record it
415
+ * @param {string} featureName - Feature name
416
+ * @param {string} phase - Phase to skip
417
+ * @param {string} reason - Reason for skipping
418
+ */
419
+ export async function skipPhase(featureName, phase, reason = '') {
420
+ await ensureFeature(featureName);
421
+ const state = loadState();
422
+
423
+ if (!state.features[featureName].skippedPhases) {
424
+ state.features[featureName].skippedPhases = [];
425
+ }
426
+
427
+ // Don't add duplicates
428
+ const existing = state.features[featureName].skippedPhases.find(s => s.phase === phase);
429
+ if (!existing) {
430
+ state.features[featureName].skippedPhases.push({
431
+ phase,
432
+ reason,
433
+ timestamp: new Date().toISOString()
434
+ });
435
+ }
436
+
437
+ state.features[featureName].updatedAt = new Date().toISOString();
438
+ saveState(state);
439
+ }
440
+
441
+ /**
442
+ * Get skipped phases for a feature
443
+ * @param {string} featureName - Feature name
444
+ * @returns {Array} Array of skipped phases
445
+ */
446
+ export function getSkippedPhases(featureName) {
447
+ const state = loadState();
448
+ if (!state.features[featureName]) {
449
+ return [];
450
+ }
451
+ return state.features[featureName].skippedPhases || [];
452
+ }
453
+
454
+ /**
455
+ * List all features
456
+ * @returns {Array} Array of [featureName, featureObject] tuples
457
+ */
458
+ export function listFeatures() {
459
+ const state = loadState();
460
+ return Object.entries(state.features);
461
+ }
462
+
463
+ /**
464
+ * Get project summary
465
+ * @returns {Object} Summary with metadata
466
+ */
467
+ export function getSummary() {
468
+ const state = loadState();
469
+ return {
470
+ project: state.project,
471
+ metadata: state.metadata,
472
+ featuresCount: Object.keys(state.features).length
473
+ };
474
+ }
475
+
476
+ // ============================================================================
477
+ // Approval Gates Operations
478
+ // ============================================================================
479
+
480
+ /**
481
+ * Set approval gate status
482
+ * @param {string} featureName - Feature name
483
+ * @param {string} gate - Gate name (proposal, uiux, design, tasks)
484
+ * @param {boolean} approved - Approval status
485
+ * @param {Object} metadata - Additional metadata (approvedBy, reason, etc.)
486
+ */
487
+ export async function setApprovalGate(featureName, gate, approved, metadata = {}) {
488
+ const state = loadState();
489
+ const feature = await ensureFeature(featureName);
490
+
491
+ if (!feature.approvalGates) {
492
+ feature.approvalGates = {
493
+ proposal: { approved: false, timestamp: null, approvedBy: null },
494
+ uiux: { approved: false, timestamp: null, approvedBy: null },
495
+ design: { approved: false, timestamp: null, approvedBy: null },
496
+ tasks: { approved: false, timestamp: null, approvedBy: null }
497
+ };
498
+ }
499
+
500
+ feature.approvalGates[gate] = {
501
+ approved,
502
+ timestamp: metadata.approvedAt || metadata.rejectedAt || new Date().toISOString(),
503
+ approvedBy: metadata.approvedBy || metadata.rejectedBy || null,
504
+ ...metadata
505
+ };
506
+
507
+ state.features[featureName] = feature;
508
+ saveState(state);
509
+ }
510
+
511
+ /**
512
+ * Get approval gate status
513
+ * @param {string} featureName - Feature name
514
+ * @param {string} gate - Gate name
515
+ * @returns {Object|null} Gate object or null
516
+ */
517
+ export function getApprovalGate(featureName, gate) {
518
+ const state = loadState();
519
+ const feature = state.features[featureName];
520
+
521
+ if (!feature || !feature.approvalGates) {
522
+ return null;
523
+ }
524
+
525
+ return feature.approvalGates[gate] || null;
526
+ }
527
+
528
+ /**
529
+ * Check if feature is pending approval
530
+ * @param {string} featureName - Feature name
531
+ * @returns {boolean} True if any gate is pending approval
532
+ */
533
+ export function isPendingApproval(featureName) {
534
+ const state = loadState();
535
+ const feature = state.features[featureName];
536
+
537
+ if (!feature || !feature.approvalGates) {
538
+ return false;
539
+ }
540
+
541
+ const currentPhase = feature.phase;
542
+
543
+ // Check if current phase has an approval gate
544
+ const phaseGateMap = {
545
+ 'design': 'design',
546
+ 'tasks': 'tasks',
547
+ 'uiux': 'uiux'
548
+ };
549
+
550
+ const relevantGate = phaseGateMap[currentPhase];
551
+ if (!relevantGate) {
552
+ return false;
553
+ }
554
+
555
+ const gate = feature.approvalGates[relevantGate];
556
+ return gate && !gate.approved;
557
+ }
558
+
559
+ /**
560
+ * Get approval history for a feature
561
+ * @param {string} featureName - Feature name
562
+ * @returns {Array} Array of approval events
563
+ */
564
+ export function getApprovalHistory(featureName) {
565
+ const state = loadState();
566
+ const feature = state.features[featureName];
567
+
568
+ if (!feature || !feature.approvalGates) {
569
+ return [];
570
+ }
571
+
572
+ const history = [];
573
+
574
+ Object.entries(feature.approvalGates).forEach(([gate, data]) => {
575
+ if (data.timestamp) {
576
+ history.push({
577
+ gate,
578
+ approved: data.approved,
579
+ timestamp: data.timestamp,
580
+ approvedBy: data.approvedBy || data.rejectedBy,
581
+ reason: data.reason
582
+ });
583
+ }
584
+ });
585
+
586
+ // Sort by timestamp
587
+ return history.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
588
+ }