@polymorphism-tech/morph-spec 3.0.1 → 3.2.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 (316) hide show
  1. package/CLAUDE.md +561 -63
  2. package/LICENSE +72 -72
  3. package/README.md +275 -79
  4. package/bin/detect-agents.js +3 -1
  5. package/bin/morph-spec.js +60 -1
  6. package/bin/render-template.js +61 -14
  7. package/bin/semantic-detect-agents.js +2 -1
  8. package/bin/{task-manager.js → task-manager.cjs} +113 -8
  9. package/bin/validate-agents-skills.js +10 -4
  10. package/bin/validate-agents.js +4 -3
  11. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
  12. package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
  13. package/docs/api/scripts/collapse.js +38 -38
  14. package/docs/api/scripts/commonNav.js +28 -28
  15. package/docs/api/scripts/linenumber.js +25 -25
  16. package/docs/api/scripts/nav.js +12 -12
  17. package/docs/api/scripts/polyfill.js +3 -3
  18. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
  19. package/docs/api/scripts/prettify/lang-css.js +2 -2
  20. package/docs/api/scripts/prettify/prettify.js +28 -28
  21. package/docs/api/scripts/search.js +98 -98
  22. package/docs/api/styles/jsdoc.css +776 -776
  23. package/docs/api/styles/prettify.css +80 -80
  24. package/docs/cli-auto-detection.md +219 -0
  25. package/docs/examples.md +328 -328
  26. package/docs/getting-started.md +3 -3
  27. package/docs/llm-interaction-config.md +735 -0
  28. package/docs/templates.md +418 -418
  29. package/docs/troubleshooting.md +269 -0
  30. package/package.json +7 -3
  31. package/scripts/postinstall.js +132 -132
  32. package/scripts/reorganize-skills.cjs +1 -1
  33. package/scripts/validate-agents-structure.cjs +1 -1
  34. package/scripts/validate-skills.cjs +2 -2
  35. package/src/commands/advance-phase.js +93 -2
  36. package/src/commands/analyze-blazor-concurrency.js +193 -193
  37. package/src/commands/approve.js +221 -0
  38. package/src/commands/capture-pattern.js +121 -0
  39. package/src/commands/create-story.js +5 -2
  40. package/src/commands/deploy.js +780 -780
  41. package/src/commands/detect-agents.js +4 -2
  42. package/src/commands/generate.js +276 -149
  43. package/src/commands/init.js +37 -0
  44. package/src/commands/lint-fluent.js +352 -352
  45. package/src/commands/migrate-state.js +158 -0
  46. package/src/commands/rollback-phase.js +185 -185
  47. package/src/commands/search-patterns.js +126 -0
  48. package/src/commands/session-summary.js +291 -291
  49. package/src/commands/shard-spec.js +224 -224
  50. package/src/commands/spawn-team.js +172 -0
  51. package/src/commands/sprint-status.js +250 -250
  52. package/src/commands/task.js +3 -3
  53. package/src/commands/troubleshoot.js +222 -222
  54. package/src/commands/update.js +36 -0
  55. package/src/commands/upgrade.js +346 -0
  56. package/src/commands/validate-blazor-state.js +210 -210
  57. package/src/commands/validate-blazor.js +156 -156
  58. package/src/commands/validate-css.js +84 -84
  59. package/src/commands/validate-phase.js +221 -221
  60. package/src/generator/.gitkeep +0 -0
  61. package/src/generator/config-generator.js +206 -0
  62. package/src/generator/templates/config.json.template +40 -0
  63. package/src/generator/templates/project.md.template +67 -0
  64. package/src/lib/blazor-concurrency-analyzer.js +288 -288
  65. package/src/lib/blazor-state-validator.js +291 -291
  66. package/src/lib/blazor-validator.js +374 -374
  67. package/src/lib/checkpoint-hooks.js +258 -0
  68. package/src/lib/context-generator.js +7 -4
  69. package/src/lib/css-validator.js +352 -352
  70. package/src/lib/design-system-generator.js +298 -298
  71. package/src/lib/hook-executor.js +2 -1
  72. package/src/lib/learning-system.js +520 -520
  73. package/src/lib/metadata-extractor.js +380 -0
  74. package/src/lib/mockup-generator.js +366 -366
  75. package/src/lib/phase-state-machine.js +214 -0
  76. package/src/lib/stack-resolver.js +148 -0
  77. package/src/lib/standards-context-injector.js +4 -3
  78. package/src/lib/state-manager.js +120 -0
  79. package/src/lib/team-orchestrator.js +2 -1
  80. package/src/lib/template-data-sources.js +325 -0
  81. package/src/lib/troubleshoot-grep.js +204 -194
  82. package/src/lib/troubleshoot-index.js +144 -144
  83. package/src/lib/ui-detector.js +350 -350
  84. package/src/lib/validation-runner.js +2 -1
  85. package/src/lib/validators/architecture-validator.js +387 -387
  86. package/src/lib/validators/content-validator.js +351 -0
  87. package/src/lib/validators/package-validator.js +360 -360
  88. package/src/lib/validators/ui-contrast-validator.js +422 -422
  89. package/src/llm/.gitkeep +0 -0
  90. package/src/llm/analyzer.js +215 -0
  91. package/src/llm/environment-detector.js +43 -0
  92. package/src/llm/few-shot-examples.js +216 -0
  93. package/src/llm/project-config-schema.json +188 -0
  94. package/src/llm/prompt-builder.js +96 -0
  95. package/src/llm/schema-validator.js +121 -0
  96. package/src/orchestrator.js +206 -0
  97. package/src/sanitizer/.gitkeep +0 -0
  98. package/src/sanitizer/context-sanitizer.js +221 -0
  99. package/src/sanitizer/patterns.js +163 -0
  100. package/src/scanner/.gitkeep +0 -0
  101. package/src/scanner/project-scanner.js +242 -0
  102. package/src/types/index.js +477 -0
  103. package/src/ui/.gitkeep +0 -0
  104. package/src/ui/diff-display.js +91 -0
  105. package/src/ui/interactive-wizard.js +96 -0
  106. package/src/ui/user-review.js +211 -0
  107. package/src/ui/wizard-questions.js +190 -0
  108. package/src/utils/file-copier.js +3 -1
  109. package/src/utils/logger.js +32 -32
  110. package/src/utils/version-checker.js +175 -175
  111. package/src/writer/.gitkeep +0 -0
  112. package/src/writer/file-writer.js +86 -0
  113. package/{content → stacks/blazor-azure}/.azure/README.md +2 -2
  114. package/{content → stacks/blazor-azure}/.azure/pipelines/pipeline-variables.yml +1 -1
  115. package/{content → stacks/blazor-azure}/.azure/pipelines/prod-pipeline.yml +1 -1
  116. package/{content → stacks/blazor-azure}/.azure/pipelines/staging-pipeline.yml +1 -1
  117. package/{content → stacks/blazor-azure}/.claude/commands/morph-preflight.md +227 -227
  118. package/{content → stacks/blazor-azure}/.claude/commands/morph-troubleshoot.md +122 -122
  119. package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-setup.md +1 -1
  120. package/{content → stacks/blazor-azure}/.morph/docs/workflows/enforcement-pipeline.md +3 -3
  121. package/{content → stacks/blazor-azure}/.morph/hooks/README.md +12 -12
  122. package/{content → stacks/blazor-azure}/.morph/standards/agent-teams-workflow.md +2 -2
  123. package/{content → stacks/blazor-azure}/.morph/standards/migration-guide.md +2 -2
  124. package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy-checklist.md +426 -426
  125. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +244 -0
  126. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +335 -0
  127. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +189 -0
  128. package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +170 -0
  129. package/stacks/nextjs-supabase/.morph/config/agents.json +345 -0
  130. package/stacks/nextjs-supabase/.morph/config/config.template.json +92 -0
  131. package/stacks/nextjs-supabase/.morph/docs/easypanel-setup.md +169 -0
  132. package/stacks/nextjs-supabase/.morph/docs/supabase-mcp-setup.md +247 -0
  133. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/README.md +697 -0
  134. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/spec.md +85 -0
  135. package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/tasks.md +86 -0
  136. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/README.md +498 -0
  137. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/decisions.md +121 -0
  138. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/spec.md +138 -0
  139. package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/tasks.md +162 -0
  140. package/stacks/nextjs-supabase/.morph/project.md +168 -0
  141. package/stacks/nextjs-supabase/.morph/standards/easypanel-deploy.md +191 -0
  142. package/stacks/nextjs-supabase/.morph/standards/nextjs-patterns.md +193 -0
  143. package/stacks/nextjs-supabase/.morph/standards/supabase-auth.md +171 -0
  144. package/stacks/nextjs-supabase/.morph/standards/supabase-pgvector.md +164 -0
  145. package/stacks/nextjs-supabase/.morph/standards/supabase-rls.md +179 -0
  146. package/stacks/nextjs-supabase/.morph/standards/supabase-storage.md +148 -0
  147. package/stacks/nextjs-supabase/.morph/templates/contracts.cs +173 -0
  148. package/stacks/nextjs-supabase/.morph/templates/contracts.ts +168 -0
  149. package/stacks/nextjs-supabase/.morph/templates/decisions.md +115 -0
  150. package/stacks/nextjs-supabase/.morph/templates/dockerfile-api.dockerfile +38 -0
  151. package/stacks/nextjs-supabase/.morph/templates/dockerfile-web.dockerfile +48 -0
  152. package/stacks/nextjs-supabase/.morph/templates/proposal.md +145 -0
  153. package/stacks/nextjs-supabase/.morph/templates/recap.md +134 -0
  154. package/stacks/nextjs-supabase/.morph/templates/rls-policy.sql +57 -0
  155. package/stacks/nextjs-supabase/.morph/templates/spec.md +231 -0
  156. package/stacks/nextjs-supabase/.morph/templates/supabase-migration.sql +100 -0
  157. package/stacks/nextjs-supabase/.morph/templates/tasks.md +257 -0
  158. package/stacks/nextjs-supabase/CLAUDE.md +149 -0
  159. package/stacks/nextjs-supabase/README.md +112 -0
  160. /package/{content → stacks/blazor-azure}/.azure/docs/azure-devops-setup.md +0 -0
  161. /package/{content → stacks/blazor-azure}/.azure/docs/branch-strategy.md +0 -0
  162. /package/{content → stacks/blazor-azure}/.azure/docs/local-development.md +0 -0
  163. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/build-dotnet.yml +0 -0
  164. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/deploy-app-service.yml +0 -0
  165. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/deploy-container-app.yml +0 -0
  166. /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/infra-deploy.yml +0 -0
  167. /package/{content → stacks/blazor-azure}/.claude/commands/morph-apply.md +0 -0
  168. /package/{content → stacks/blazor-azure}/.claude/commands/morph-archive.md +0 -0
  169. /package/{content → stacks/blazor-azure}/.claude/commands/morph-deploy.md +0 -0
  170. /package/{content → stacks/blazor-azure}/.claude/commands/morph-infra.md +0 -0
  171. /package/{content → stacks/blazor-azure}/.claude/commands/morph-proposal.md +0 -0
  172. /package/{content → stacks/blazor-azure}/.claude/commands/morph-status.md +0 -0
  173. /package/{content → stacks/blazor-azure}/.claude/settings.local.json +0 -0
  174. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/README.md +0 -0
  175. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/code-review.md +0 -0
  176. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/morph-checklist.md +0 -0
  177. /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/simulation-checklist.md +0 -0
  178. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/README.md +0 -0
  179. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/morph-replicate.md +0 -0
  180. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-clarify.md +0 -0
  181. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-design.md +0 -0
  182. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-tasks.md +0 -0
  183. /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-uiux.md +0 -0
  184. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/README.md +0 -0
  185. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -0
  186. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -0
  187. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -0
  188. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -0
  189. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -0
  190. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -0
  191. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -0
  192. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -0
  193. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -0
  194. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -0
  195. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -0
  196. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -0
  197. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -0
  198. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -0
  199. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -0
  200. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -0
  201. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -0
  202. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -0
  203. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -0
  204. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -0
  205. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/resend-email.md +0 -0
  206. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -0
  207. /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -0
  208. /package/{content → stacks/blazor-azure}/.claude/skills/level-3-technologies/README.md +0 -0
  209. /package/{content → stacks/blazor-azure}/.claude/skills/level-4-patterns/README.md +0 -0
  210. /package/{content → stacks/blazor-azure}/.morph/.morphversion +0 -0
  211. /package/{content → stacks/blazor-azure}/.morph/archive/.gitkeep +0 -0
  212. /package/{content → stacks/blazor-azure}/.morph/config/agents.json +0 -0
  213. /package/{content → stacks/blazor-azure}/.morph/config/config.template.json +0 -0
  214. /package/{content → stacks/blazor-azure}/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -0
  215. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/design-impl.md +0 -0
  216. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/fast-track.md +0 -0
  217. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/full-morph.md +0 -0
  218. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/standard.md +0 -0
  219. /package/{content → stacks/blazor-azure}/.morph/docs/workflows/ui-refresh.md +0 -0
  220. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/README.md +0 -0
  221. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/contracts.ts +0 -0
  222. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/spec.md +0 -0
  223. /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/tasks.md +0 -0
  224. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/README.md +0 -0
  225. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/contracts.cs +0 -0
  226. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/decisions.md +0 -0
  227. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/spec.md +0 -0
  228. /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/tasks.md +0 -0
  229. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/README.md +0 -0
  230. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/contracts.cs +0 -0
  231. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/spec.md +0 -0
  232. /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/tasks.md +0 -0
  233. /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/decisions.md +0 -0
  234. /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/proposal.md +0 -0
  235. /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/spec.md +0 -0
  236. /package/{content → stacks/blazor-azure}/.morph/examples/state-v3.json +0 -0
  237. /package/{content → stacks/blazor-azure}/.morph/features/.gitkeep +0 -0
  238. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-agents.sh +0 -0
  239. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-all.sh +0 -0
  240. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-specs.sh +0 -0
  241. /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-tests.sh +0 -0
  242. /package/{content → stacks/blazor-azure}/.morph/hooks/task-completed.js +0 -0
  243. /package/{content → stacks/blazor-azure}/.morph/hooks/teammate-idle.js +0 -0
  244. /package/{content → stacks/blazor-azure}/.morph/project.md +0 -0
  245. /package/{content → stacks/blazor-azure}/.morph/schemas/agent.schema.json +0 -0
  246. /package/{content → stacks/blazor-azure}/.morph/schemas/tasks.schema.json +0 -0
  247. /package/{content → stacks/blazor-azure}/.morph/specs/.gitkeep +0 -0
  248. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-blazor-ui.md +0 -0
  249. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-production.md +0 -0
  250. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-setup.md +0 -0
  251. /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-workflows.md +0 -0
  252. /package/{content → stacks/blazor-azure}/.morph/standards/architecture.md +0 -0
  253. /package/{content → stacks/blazor-azure}/.morph/standards/azure.md +0 -0
  254. /package/{content → stacks/blazor-azure}/.morph/standards/coding.md +0 -0
  255. /package/{content → stacks/blazor-azure}/.morph/standards/dotnet10-migration.md +0 -0
  256. /package/{content → stacks/blazor-azure}/.morph/standards/fluent-ui-setup.md +0 -0
  257. /package/{content → stacks/blazor-azure}/.morph/standards/passkeys-auth.md +0 -0
  258. /package/{content → stacks/blazor-azure}/.morph/standards/vector-search-rag.md +0 -0
  259. /package/{content → stacks/blazor-azure}/.morph/state.json +0 -0
  260. /package/{content → stacks/blazor-azure}/.morph/templates/CONTEXT-FEATURE.md +0 -0
  261. /package/{content → stacks/blazor-azure}/.morph/templates/CONTEXT.md +0 -0
  262. /package/{content → stacks/blazor-azure}/.morph/templates/FluentDesignTheme.cs +0 -0
  263. /package/{content → stacks/blazor-azure}/.morph/templates/MudTheme.cs +0 -0
  264. /package/{content → stacks/blazor-azure}/.morph/templates/agent.cs +0 -0
  265. /package/{content → stacks/blazor-azure}/.morph/templates/clarify-questions.md +0 -0
  266. /package/{content → stacks/blazor-azure}/.morph/templates/component.razor +0 -0
  267. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Commands.cs +0 -0
  268. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Entities.cs +0 -0
  269. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Queries.cs +0 -0
  270. /package/{content → stacks/blazor-azure}/.morph/templates/contracts/README.md +0 -0
  271. /package/{content → stacks/blazor-azure}/.morph/templates/contracts.cs +0 -0
  272. /package/{content → stacks/blazor-azure}/.morph/templates/decisions.md +0 -0
  273. /package/{content → stacks/blazor-azure}/.morph/templates/design-system.css +0 -0
  274. /package/{content → stacks/blazor-azure}/.morph/templates/infra/.dockerignore.example +0 -0
  275. /package/{content → stacks/blazor-azure}/.morph/templates/infra/Dockerfile.example +0 -0
  276. /package/{content → stacks/blazor-azure}/.morph/templates/infra/README.md +0 -0
  277. /package/{content → stacks/blazor-azure}/.morph/templates/infra/app-insights.bicep +0 -0
  278. /package/{content → stacks/blazor-azure}/.morph/templates/infra/app-service.bicep +0 -0
  279. /package/{content → stacks/blazor-azure}/.morph/templates/infra/azure-pipelines-deploy.yml +0 -0
  280. /package/{content → stacks/blazor-azure}/.morph/templates/infra/container-app-env.bicep +0 -0
  281. /package/{content → stacks/blazor-azure}/.morph/templates/infra/container-app.bicep +0 -0
  282. /package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy.ps1 +0 -0
  283. /package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy.sh +0 -0
  284. /package/{content → stacks/blazor-azure}/.morph/templates/infra/key-vault.bicep +0 -0
  285. /package/{content → stacks/blazor-azure}/.morph/templates/infra/main.bicep +0 -0
  286. /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.dev.json +0 -0
  287. /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.prod.json +0 -0
  288. /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.staging.json +0 -0
  289. /package/{content → stacks/blazor-azure}/.morph/templates/infra/sql-database.bicep +0 -0
  290. /package/{content → stacks/blazor-azure}/.morph/templates/infra/storage.bicep +0 -0
  291. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/asaas-client.cs +0 -0
  292. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/asaas-webhook.cs +0 -0
  293. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/azure-identity-config.cs +0 -0
  294. /package/{content → stacks/blazor-azure}/.morph/templates/integrations/clerk-config.cs +0 -0
  295. /package/{content → stacks/blazor-azure}/.morph/templates/job.cs +0 -0
  296. /package/{content → stacks/blazor-azure}/.morph/templates/migration.cs +0 -0
  297. /package/{content → stacks/blazor-azure}/.morph/templates/proposal.md +0 -0
  298. /package/{content → stacks/blazor-azure}/.morph/templates/recap.md +0 -0
  299. /package/{content → stacks/blazor-azure}/.morph/templates/repository.cs +0 -0
  300. /package/{content → stacks/blazor-azure}/.morph/templates/saas/subscription.cs +0 -0
  301. /package/{content → stacks/blazor-azure}/.morph/templates/saas/tenant.cs +0 -0
  302. /package/{content → stacks/blazor-azure}/.morph/templates/service.cs +0 -0
  303. /package/{content → stacks/blazor-azure}/.morph/templates/simulation.md +0 -0
  304. /package/{content → stacks/blazor-azure}/.morph/templates/spec.md +0 -0
  305. /package/{content → stacks/blazor-azure}/.morph/templates/sprint-status.yaml +0 -0
  306. /package/{content → stacks/blazor-azure}/.morph/templates/state.template.json +0 -0
  307. /package/{content → stacks/blazor-azure}/.morph/templates/story.md +0 -0
  308. /package/{content → stacks/blazor-azure}/.morph/templates/tasks.md +0 -0
  309. /package/{content → stacks/blazor-azure}/.morph/templates/test.cs +0 -0
  310. /package/{content → stacks/blazor-azure}/.morph/templates/ui-components.md +0 -0
  311. /package/{content → stacks/blazor-azure}/.morph/templates/ui-design-system.md +0 -0
  312. /package/{content → stacks/blazor-azure}/.morph/templates/ui-flows.md +0 -0
  313. /package/{content → stacks/blazor-azure}/.morph/templates/ui-mockups.md +0 -0
  314. /package/{content → stacks/blazor-azure}/.morph/test-infra/example.bicep +0 -0
  315. /package/{content → stacks/blazor-azure}/CLAUDE.md +0 -0
  316. /package/{content → stacks/blazor-azure}/README.md +0 -0
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Phase State Machine - Enforces valid phase transitions
3
+ *
4
+ * Prevents invalid workflow jumps and ensures sequential phase progression.
5
+ * Based on the MORPH 5-phase workflow.
6
+ */
7
+
8
+ /**
9
+ * Valid phase transitions map
10
+ * Each phase can only transition to specific next phases
11
+ */
12
+ const VALID_TRANSITIONS = {
13
+ 'proposal': ['setup'],
14
+ 'setup': ['uiux', 'design'], // Can skip UI/UX if no frontend
15
+ 'uiux': ['design'],
16
+ 'design': ['clarify'],
17
+ 'clarify': ['tasks'],
18
+ 'tasks': ['implement'],
19
+ 'implement': ['sync', 'archived'], // Can skip sync
20
+ 'sync': ['archived']
21
+ };
22
+
23
+ /**
24
+ * Phase display names for error messages
25
+ */
26
+ const PHASE_NAMES = {
27
+ 'proposal': 'Proposal (Phase 0)',
28
+ 'setup': 'Setup (Phase 1)',
29
+ 'uiux': 'UI/UX Design (Phase 1.5)',
30
+ 'design': 'Design (Phase 2)',
31
+ 'clarify': 'Clarify (Phase 3)',
32
+ 'tasks': 'Tasks (Phase 4)',
33
+ 'implement': 'Implement (Phase 5)',
34
+ 'sync': 'Sync (Phase 6)',
35
+ 'archived': 'Archived'
36
+ };
37
+
38
+ /**
39
+ * Optional phases that can be skipped
40
+ */
41
+ const OPTIONAL_PHASES = ['uiux', 'sync'];
42
+
43
+ /**
44
+ * Validate if a phase transition is allowed
45
+ * @param {string} fromPhase - Current phase
46
+ * @param {string} toPhase - Target phase
47
+ * @returns {boolean} True if transition is valid
48
+ */
49
+ export function isValidTransition(fromPhase, toPhase) {
50
+ if (!fromPhase || !toPhase) {
51
+ return false;
52
+ }
53
+
54
+ const validNextPhases = VALID_TRANSITIONS[fromPhase];
55
+ return validNextPhases ? validNextPhases.includes(toPhase) : false;
56
+ }
57
+
58
+ /**
59
+ * Get list of valid next phases for a given phase
60
+ * @param {string} currentPhase - Current phase
61
+ * @returns {string[]} Array of valid next phase names
62
+ */
63
+ export function getValidNextPhases(currentPhase) {
64
+ return VALID_TRANSITIONS[currentPhase] || [];
65
+ }
66
+
67
+ /**
68
+ * Get display name for a phase
69
+ * @param {string} phase - Phase identifier
70
+ * @returns {string} Human-readable phase name
71
+ */
72
+ export function getPhaseDisplayName(phase) {
73
+ return PHASE_NAMES[phase] || phase;
74
+ }
75
+
76
+ /**
77
+ * Check if a phase is optional
78
+ * @param {string} phase - Phase identifier
79
+ * @returns {boolean} True if phase is optional
80
+ */
81
+ export function isOptionalPhase(phase) {
82
+ return OPTIONAL_PHASES.includes(phase);
83
+ }
84
+
85
+ /**
86
+ * Validate transition and throw error if invalid
87
+ * @param {string} fromPhase - Current phase
88
+ * @param {string} toPhase - Target phase
89
+ * @throws {Error} If transition is invalid
90
+ */
91
+ export function validateTransition(fromPhase, toPhase) {
92
+ if (!fromPhase) {
93
+ throw new Error('Current phase is undefined. Cannot validate transition.');
94
+ }
95
+
96
+ if (!toPhase) {
97
+ throw new Error('Target phase is undefined. Cannot validate transition.');
98
+ }
99
+
100
+ if (!isValidTransition(fromPhase, toPhase)) {
101
+ const validPhases = getValidNextPhases(fromPhase);
102
+ const fromDisplay = getPhaseDisplayName(fromPhase);
103
+ const toDisplay = getPhaseDisplayName(toPhase);
104
+
105
+ throw new Error(
106
+ `Invalid phase transition: ${fromDisplay} → ${toDisplay}\n\n` +
107
+ `Valid next phases from ${fromDisplay}:\n` +
108
+ validPhases.map(p => ` • ${getPhaseDisplayName(p)}`).join('\n') +
109
+ '\n\n' +
110
+ 'You cannot skip phases in the MORPH workflow.\n' +
111
+ 'To override this check, use the --force flag (not recommended).'
112
+ );
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Get the sequential order of phases
118
+ * @returns {string[]} Ordered list of phases
119
+ */
120
+ export function getPhaseSequence() {
121
+ return [
122
+ 'proposal',
123
+ 'setup',
124
+ 'uiux', // optional
125
+ 'design',
126
+ 'clarify',
127
+ 'tasks',
128
+ 'implement',
129
+ 'sync', // optional
130
+ 'archived'
131
+ ];
132
+ }
133
+
134
+ /**
135
+ * Get phase order index (for comparison)
136
+ * @param {string} phase - Phase identifier
137
+ * @returns {number} Index in sequence (-1 if not found)
138
+ */
139
+ export function getPhaseOrder(phase) {
140
+ return getPhaseSequence().indexOf(phase);
141
+ }
142
+
143
+ /**
144
+ * Check if target phase is ahead of current phase
145
+ * @param {string} currentPhase - Current phase
146
+ * @param {string} targetPhase - Target phase
147
+ * @returns {boolean} True if target is ahead
148
+ */
149
+ export function isPhaseAhead(currentPhase, targetPhase) {
150
+ return getPhaseOrder(targetPhase) > getPhaseOrder(currentPhase);
151
+ }
152
+
153
+ /**
154
+ * Check if target phase is behind current phase (rollback scenario)
155
+ * @param {string} currentPhase - Current phase
156
+ * @param {string} targetPhase - Target phase
157
+ * @returns {boolean} True if target is behind
158
+ */
159
+ export function isPhaseRollback(currentPhase, targetPhase) {
160
+ return getPhaseOrder(targetPhase) < getPhaseOrder(currentPhase);
161
+ }
162
+
163
+ /**
164
+ * Get all skipped phases between two phases
165
+ * @param {string} fromPhase - Starting phase
166
+ * @param {string} toPhase - Ending phase
167
+ * @returns {string[]} Array of skipped phases
168
+ */
169
+ export function getSkippedPhases(fromPhase, toPhase) {
170
+ const sequence = getPhaseSequence();
171
+ const fromIndex = sequence.indexOf(fromPhase);
172
+ const toIndex = sequence.indexOf(toPhase);
173
+
174
+ if (fromIndex === -1 || toIndex === -1 || toIndex <= fromIndex) {
175
+ return [];
176
+ }
177
+
178
+ return sequence.slice(fromIndex + 1, toIndex);
179
+ }
180
+
181
+ /**
182
+ * Validate that no required phases were skipped
183
+ * @param {string} fromPhase - Starting phase
184
+ * @param {string} toPhase - Ending phase
185
+ * @throws {Error} If required phases were skipped
186
+ */
187
+ export function validateNoRequiredPhasesSkipped(fromPhase, toPhase) {
188
+ const skipped = getSkippedPhases(fromPhase, toPhase);
189
+ const requiredSkipped = skipped.filter(p => !isOptionalPhase(p));
190
+
191
+ if (requiredSkipped.length > 0) {
192
+ throw new Error(
193
+ `Cannot skip required phases:\n` +
194
+ requiredSkipped.map(p => ` • ${getPhaseDisplayName(p)}`).join('\n') +
195
+ '\n\nYou must complete each required phase in sequence.'
196
+ );
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Get comprehensive phase information
202
+ * @param {string} phase - Phase identifier
203
+ * @returns {Object} Phase metadata
204
+ */
205
+ export function getPhaseInfo(phase) {
206
+ return {
207
+ id: phase,
208
+ displayName: getPhaseDisplayName(phase),
209
+ order: getPhaseOrder(phase),
210
+ isOptional: isOptionalPhase(phase),
211
+ validNextPhases: getValidNextPhases(phase),
212
+ canTransitionTo: (targetPhase) => isValidTransition(phase, targetPhase)
213
+ };
214
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Stack Resolver - Centralized stack path resolution for multi-stack support.
3
+ *
4
+ * Detects the active stack and resolves all stack-relative paths.
5
+ * Resolution order: explicit > config.json > state.json > auto-detect > fallback
6
+ *
7
+ * @module stack-resolver
8
+ */
9
+
10
+ import { readFileSync, existsSync, readdirSync } from 'fs';
11
+ import { join } from 'path';
12
+
13
+ const STACK_TYPE_TO_DIR = {
14
+ 'blazor-server': 'blazor-azure',
15
+ 'blazor-azure': 'blazor-azure',
16
+ 'nextjs-supabase': 'nextjs-supabase',
17
+ };
18
+
19
+ // Module-level override set by CLI --stack flag via setGlobalStack()
20
+ let _globalStackOverride = null;
21
+
22
+ /**
23
+ * Set a global stack override (called by CLI --stack flag).
24
+ * Once set, all resolveStackId() calls use this value unless
25
+ * an explicit parameter is provided.
26
+ * @param {string} stackId - Stack identifier
27
+ */
28
+ export function setGlobalStack(stackId) {
29
+ _globalStackOverride = stackId;
30
+ }
31
+
32
+ /**
33
+ * Detect which stack directory to use.
34
+ *
35
+ * Resolution order:
36
+ * 1. explicitStack parameter (from CLI --stack flag)
37
+ * 2. .morph/config/config.json → project.type
38
+ * 3. .morph/state.json → project.type
39
+ * 4. Auto-detect: scan stacks/ for existing directories
40
+ * 5. Fallback: 'blazor-azure'
41
+ *
42
+ * @param {string} projectPath - Absolute path to project root
43
+ * @param {string} [explicitStack] - Override from CLI --stack flag
44
+ * @returns {string} Stack directory name (e.g., 'blazor-azure', 'nextjs-supabase')
45
+ */
46
+ export function resolveStackId(projectPath, explicitStack) {
47
+ // 1. Explicit parameter override
48
+ if (explicitStack) {
49
+ return STACK_TYPE_TO_DIR[explicitStack] || explicitStack;
50
+ }
51
+
52
+ // 1b. Global CLI --stack override
53
+ if (_globalStackOverride) {
54
+ return STACK_TYPE_TO_DIR[_globalStackOverride] || _globalStackOverride;
55
+ }
56
+
57
+ // 2. config.json → project.type
58
+ try {
59
+ const configPath = join(projectPath, '.morph/config/config.json');
60
+ if (existsSync(configPath)) {
61
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
62
+ const projectType = config?.project?.type;
63
+ if (projectType && STACK_TYPE_TO_DIR[projectType]) {
64
+ return STACK_TYPE_TO_DIR[projectType];
65
+ }
66
+ }
67
+ } catch { /* continue to next source */ }
68
+
69
+ // 3. state.json → project.type
70
+ try {
71
+ const statePath = join(projectPath, '.morph/state.json');
72
+ if (existsSync(statePath)) {
73
+ const state = JSON.parse(readFileSync(statePath, 'utf8'));
74
+ const projectType = state?.project?.type;
75
+ if (projectType && STACK_TYPE_TO_DIR[projectType]) {
76
+ return STACK_TYPE_TO_DIR[projectType];
77
+ }
78
+ }
79
+ } catch { /* continue to next source */ }
80
+
81
+ // 4. Auto-detect: first directory in stacks/
82
+ try {
83
+ const stacksDir = join(projectPath, 'stacks');
84
+ if (existsSync(stacksDir)) {
85
+ const dirs = readdirSync(stacksDir, { withFileTypes: true })
86
+ .filter(d => d.isDirectory())
87
+ .map(d => d.name);
88
+ if (dirs.length === 1) {
89
+ return dirs[0];
90
+ }
91
+ }
92
+ } catch { /* continue to fallback */ }
93
+
94
+ // 5. Fallback
95
+ return 'blazor-azure';
96
+ }
97
+
98
+ /**
99
+ * Get absolute path to the active stack's root directory.
100
+ * @param {string} projectPath
101
+ * @param {string} [explicitStack]
102
+ * @returns {string}
103
+ */
104
+ export function resolveStackPath(projectPath, explicitStack) {
105
+ return join(projectPath, 'stacks', resolveStackId(projectPath, explicitStack));
106
+ }
107
+
108
+ /**
109
+ * Get absolute path to the active stack's agents.json.
110
+ * @param {string} projectPath
111
+ * @param {string} [explicitStack]
112
+ * @returns {string}
113
+ */
114
+ export function resolveAgentsConfigPath(projectPath, explicitStack) {
115
+ return join(resolveStackPath(projectPath, explicitStack), '.morph/config/agents.json');
116
+ }
117
+
118
+ /**
119
+ * Get absolute path to the active stack's standards directory.
120
+ * @param {string} projectPath
121
+ * @param {string} [explicitStack]
122
+ * @returns {string}
123
+ */
124
+ export function resolveStandardsDir(projectPath, explicitStack) {
125
+ return join(resolveStackPath(projectPath, explicitStack), '.morph/standards');
126
+ }
127
+
128
+ /**
129
+ * Get absolute path to the active stack's templates directory.
130
+ * @param {string} projectPath
131
+ * @param {string} [explicitStack]
132
+ * @returns {string}
133
+ */
134
+ export function resolveTemplatesDir(projectPath, explicitStack) {
135
+ return join(resolveStackPath(projectPath, explicitStack), '.morph/templates');
136
+ }
137
+
138
+ /**
139
+ * Get absolute path to the active stack's config directory.
140
+ * @param {string} projectPath
141
+ * @param {string} [explicitStack]
142
+ * @returns {string}
143
+ */
144
+ export function resolveConfigDir(projectPath, explicitStack) {
145
+ return join(resolveStackPath(projectPath, explicitStack), '.morph/config');
146
+ }
147
+
148
+ export { STACK_TYPE_TO_DIR };
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { readFileSync, existsSync } from 'fs';
11
11
  import { join } from 'path';
12
+ import { resolveStandardsDir } from './stack-resolver.js';
12
13
 
13
14
  /**
14
15
  * Agent → Standards mapping
@@ -191,8 +192,8 @@ function loadStandard(standardName, projectPath) {
191
192
  const locations = [
192
193
  // 1. Project override
193
194
  join(projectPath, '.morph/project/standards', `${standardName}.md`),
194
- // 2. Content standards (AI/Agent Framework, Azure, etc.)
195
- join(projectPath, 'content/.morph/standards', `${standardName}.md`),
195
+ // 2. Stack standards (resolved dynamically based on project type)
196
+ join(resolveStandardsDir(projectPath), `${standardName}.md`),
196
197
  // 3. Framework standards (Blazor, CSS, .NET)
197
198
  join(projectPath, 'framework/standards', `${standardName}.md`)
198
199
  ];
@@ -273,7 +274,7 @@ export function getStandardsListForAgent(agentId) {
273
274
  export function checkStandardExists(standardName, projectPath = '.') {
274
275
  const locations = [
275
276
  { type: 'project', path: join(projectPath, '.morph/project/standards', `${standardName}.md`) },
276
- { type: 'content', path: join(projectPath, 'content/.morph/standards', `${standardName}.md`) },
277
+ { type: 'stack', path: join(resolveStandardsDir(projectPath), `${standardName}.md`) },
277
278
  { type: 'framework', path: join(projectPath, 'framework/standards', `${standardName}.md`) }
278
279
  ];
279
280
 
@@ -139,6 +139,12 @@ function ensureFeature(featureName) {
139
139
  createdAt: new Date().toISOString(),
140
140
  updatedAt: new Date().toISOString(),
141
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
+ },
142
148
  outputs: {
143
149
  proposal: { created: false, path: `.morph/project/outputs/${featureName}/proposal.md` },
144
150
  spec: { created: false, path: `.morph/project/outputs/${featureName}/spec.md` },
@@ -412,3 +418,117 @@ export function getSummary() {
412
418
  featuresCount: Object.keys(state.features).length
413
419
  };
414
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
+ }
@@ -13,6 +13,7 @@
13
13
  import fs from 'fs/promises';
14
14
  import path from 'path';
15
15
  import { fileURLToPath } from 'url';
16
+ import { resolveAgentsConfigPath } from './stack-resolver.js';
16
17
 
17
18
  const __filename = fileURLToPath(import.meta.url);
18
19
  const __dirname = path.dirname(__filename);
@@ -23,7 +24,7 @@ const __dirname = path.dirname(__filename);
23
24
  * @returns {Promise<Object>} Parsed agents configuration
24
25
  */
25
26
  async function loadAgentsConfig(projectPath) {
26
- const agentsPath = path.join(projectPath, 'content/.morph/config/agents.json');
27
+ const agentsPath = resolveAgentsConfigPath(projectPath);
27
28
  const content = await fs.readFile(agentsPath, 'utf8');
28
29
  return JSON.parse(content);
29
30
  }