@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.
- package/CLAUDE.md +561 -63
- package/LICENSE +72 -72
- package/README.md +275 -79
- package/bin/detect-agents.js +3 -1
- package/bin/morph-spec.js +60 -1
- package/bin/render-template.js +61 -14
- package/bin/semantic-detect-agents.js +2 -1
- package/bin/{task-manager.js → task-manager.cjs} +113 -8
- package/bin/validate-agents-skills.js +10 -4
- package/bin/validate-agents.js +4 -3
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +977 -977
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1048 -1048
- package/docs/api/scripts/collapse.js +38 -38
- package/docs/api/scripts/commonNav.js +28 -28
- package/docs/api/scripts/linenumber.js +25 -25
- package/docs/api/scripts/nav.js +12 -12
- package/docs/api/scripts/polyfill.js +3 -3
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -202
- package/docs/api/scripts/prettify/lang-css.js +2 -2
- package/docs/api/scripts/prettify/prettify.js +28 -28
- package/docs/api/scripts/search.js +98 -98
- package/docs/api/styles/jsdoc.css +776 -776
- package/docs/api/styles/prettify.css +80 -80
- package/docs/cli-auto-detection.md +219 -0
- package/docs/examples.md +328 -328
- package/docs/getting-started.md +3 -3
- package/docs/llm-interaction-config.md +735 -0
- package/docs/templates.md +418 -418
- package/docs/troubleshooting.md +269 -0
- package/package.json +7 -3
- package/scripts/postinstall.js +132 -132
- package/scripts/reorganize-skills.cjs +1 -1
- package/scripts/validate-agents-structure.cjs +1 -1
- package/scripts/validate-skills.cjs +2 -2
- package/src/commands/advance-phase.js +93 -2
- package/src/commands/analyze-blazor-concurrency.js +193 -193
- package/src/commands/approve.js +221 -0
- package/src/commands/capture-pattern.js +121 -0
- package/src/commands/create-story.js +5 -2
- package/src/commands/deploy.js +780 -780
- package/src/commands/detect-agents.js +4 -2
- package/src/commands/generate.js +276 -149
- package/src/commands/init.js +37 -0
- package/src/commands/lint-fluent.js +352 -352
- package/src/commands/migrate-state.js +158 -0
- package/src/commands/rollback-phase.js +185 -185
- package/src/commands/search-patterns.js +126 -0
- package/src/commands/session-summary.js +291 -291
- package/src/commands/shard-spec.js +224 -224
- package/src/commands/spawn-team.js +172 -0
- package/src/commands/sprint-status.js +250 -250
- package/src/commands/task.js +3 -3
- package/src/commands/troubleshoot.js +222 -222
- package/src/commands/update.js +36 -0
- package/src/commands/upgrade.js +346 -0
- package/src/commands/validate-blazor-state.js +210 -210
- package/src/commands/validate-blazor.js +156 -156
- package/src/commands/validate-css.js +84 -84
- package/src/commands/validate-phase.js +221 -221
- package/src/generator/.gitkeep +0 -0
- package/src/generator/config-generator.js +206 -0
- package/src/generator/templates/config.json.template +40 -0
- package/src/generator/templates/project.md.template +67 -0
- package/src/lib/blazor-concurrency-analyzer.js +288 -288
- package/src/lib/blazor-state-validator.js +291 -291
- package/src/lib/blazor-validator.js +374 -374
- package/src/lib/checkpoint-hooks.js +258 -0
- package/src/lib/context-generator.js +7 -4
- package/src/lib/css-validator.js +352 -352
- package/src/lib/design-system-generator.js +298 -298
- package/src/lib/hook-executor.js +2 -1
- package/src/lib/learning-system.js +520 -520
- package/src/lib/metadata-extractor.js +380 -0
- package/src/lib/mockup-generator.js +366 -366
- package/src/lib/phase-state-machine.js +214 -0
- package/src/lib/stack-resolver.js +148 -0
- package/src/lib/standards-context-injector.js +4 -3
- package/src/lib/state-manager.js +120 -0
- package/src/lib/team-orchestrator.js +2 -1
- package/src/lib/template-data-sources.js +325 -0
- package/src/lib/troubleshoot-grep.js +204 -194
- package/src/lib/troubleshoot-index.js +144 -144
- package/src/lib/ui-detector.js +350 -350
- package/src/lib/validation-runner.js +2 -1
- package/src/lib/validators/architecture-validator.js +387 -387
- package/src/lib/validators/content-validator.js +351 -0
- package/src/lib/validators/package-validator.js +360 -360
- package/src/lib/validators/ui-contrast-validator.js +422 -422
- package/src/llm/.gitkeep +0 -0
- package/src/llm/analyzer.js +215 -0
- package/src/llm/environment-detector.js +43 -0
- package/src/llm/few-shot-examples.js +216 -0
- package/src/llm/project-config-schema.json +188 -0
- package/src/llm/prompt-builder.js +96 -0
- package/src/llm/schema-validator.js +121 -0
- package/src/orchestrator.js +206 -0
- package/src/sanitizer/.gitkeep +0 -0
- package/src/sanitizer/context-sanitizer.js +221 -0
- package/src/sanitizer/patterns.js +163 -0
- package/src/scanner/.gitkeep +0 -0
- package/src/scanner/project-scanner.js +242 -0
- package/src/types/index.js +477 -0
- package/src/ui/.gitkeep +0 -0
- package/src/ui/diff-display.js +91 -0
- package/src/ui/interactive-wizard.js +96 -0
- package/src/ui/user-review.js +211 -0
- package/src/ui/wizard-questions.js +190 -0
- package/src/utils/file-copier.js +3 -1
- package/src/utils/logger.js +32 -32
- package/src/utils/version-checker.js +175 -175
- package/src/writer/.gitkeep +0 -0
- package/src/writer/file-writer.js +86 -0
- package/{content → stacks/blazor-azure}/.azure/README.md +2 -2
- package/{content → stacks/blazor-azure}/.azure/pipelines/pipeline-variables.yml +1 -1
- package/{content → stacks/blazor-azure}/.azure/pipelines/prod-pipeline.yml +1 -1
- package/{content → stacks/blazor-azure}/.azure/pipelines/staging-pipeline.yml +1 -1
- package/{content → stacks/blazor-azure}/.claude/commands/morph-preflight.md +227 -227
- package/{content → stacks/blazor-azure}/.claude/commands/morph-troubleshoot.md +122 -122
- package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-setup.md +1 -1
- package/{content → stacks/blazor-azure}/.morph/docs/workflows/enforcement-pipeline.md +3 -3
- package/{content → stacks/blazor-azure}/.morph/hooks/README.md +12 -12
- package/{content → stacks/blazor-azure}/.morph/standards/agent-teams-workflow.md +2 -2
- package/{content → stacks/blazor-azure}/.morph/standards/migration-guide.md +2 -2
- package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy-checklist.md +426 -426
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +244 -0
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +335 -0
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +189 -0
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +170 -0
- package/stacks/nextjs-supabase/.morph/config/agents.json +345 -0
- package/stacks/nextjs-supabase/.morph/config/config.template.json +92 -0
- package/stacks/nextjs-supabase/.morph/docs/easypanel-setup.md +169 -0
- package/stacks/nextjs-supabase/.morph/docs/supabase-mcp-setup.md +247 -0
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/README.md +697 -0
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/spec.md +85 -0
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/tasks.md +86 -0
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/README.md +498 -0
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/decisions.md +121 -0
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/spec.md +138 -0
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/tasks.md +162 -0
- package/stacks/nextjs-supabase/.morph/project.md +168 -0
- package/stacks/nextjs-supabase/.morph/standards/easypanel-deploy.md +191 -0
- package/stacks/nextjs-supabase/.morph/standards/nextjs-patterns.md +193 -0
- package/stacks/nextjs-supabase/.morph/standards/supabase-auth.md +171 -0
- package/stacks/nextjs-supabase/.morph/standards/supabase-pgvector.md +164 -0
- package/stacks/nextjs-supabase/.morph/standards/supabase-rls.md +179 -0
- package/stacks/nextjs-supabase/.morph/standards/supabase-storage.md +148 -0
- package/stacks/nextjs-supabase/.morph/templates/contracts.cs +173 -0
- package/stacks/nextjs-supabase/.morph/templates/contracts.ts +168 -0
- package/stacks/nextjs-supabase/.morph/templates/decisions.md +115 -0
- package/stacks/nextjs-supabase/.morph/templates/dockerfile-api.dockerfile +38 -0
- package/stacks/nextjs-supabase/.morph/templates/dockerfile-web.dockerfile +48 -0
- package/stacks/nextjs-supabase/.morph/templates/proposal.md +145 -0
- package/stacks/nextjs-supabase/.morph/templates/recap.md +134 -0
- package/stacks/nextjs-supabase/.morph/templates/rls-policy.sql +57 -0
- package/stacks/nextjs-supabase/.morph/templates/spec.md +231 -0
- package/stacks/nextjs-supabase/.morph/templates/supabase-migration.sql +100 -0
- package/stacks/nextjs-supabase/.morph/templates/tasks.md +257 -0
- package/stacks/nextjs-supabase/CLAUDE.md +149 -0
- package/stacks/nextjs-supabase/README.md +112 -0
- /package/{content → stacks/blazor-azure}/.azure/docs/azure-devops-setup.md +0 -0
- /package/{content → stacks/blazor-azure}/.azure/docs/branch-strategy.md +0 -0
- /package/{content → stacks/blazor-azure}/.azure/docs/local-development.md +0 -0
- /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/build-dotnet.yml +0 -0
- /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/deploy-app-service.yml +0 -0
- /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/deploy-container-app.yml +0 -0
- /package/{content → stacks/blazor-azure}/.azure/pipelines/templates/infra-deploy.yml +0 -0
- /package/{content → stacks/blazor-azure}/.claude/commands/morph-apply.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/commands/morph-archive.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/commands/morph-deploy.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/commands/morph-infra.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/commands/morph-proposal.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/commands/morph-status.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/settings.local.json +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/code-review.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/morph-checklist.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-0-meta/simulation-checklist.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/morph-replicate.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-clarify.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-design.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-tasks.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-1-workflows/phase-uiux.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/integrations/resend-email.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-3-technologies/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.claude/skills/level-4-patterns/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/.morphversion +0 -0
- /package/{content → stacks/blazor-azure}/.morph/archive/.gitkeep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/config/agents.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/config/config.template.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/docs/workflows/design-impl.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/docs/workflows/fast-track.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/docs/workflows/full-morph.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/docs/workflows/standard.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/docs/workflows/ui-refresh.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/contracts.ts +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/spec.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/api-nextjs/tasks.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/contracts.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/decisions.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/spec.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/micro-saas/tasks.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/contracts.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/spec.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/multi-agent/tasks.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/decisions.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/proposal.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/scheduled-reports/spec.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/examples/state-v3.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/features/.gitkeep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-agents.sh +0 -0
- /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-all.sh +0 -0
- /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-specs.sh +0 -0
- /package/{content → stacks/blazor-azure}/.morph/hooks/pre-commit-tests.sh +0 -0
- /package/{content → stacks/blazor-azure}/.morph/hooks/task-completed.js +0 -0
- /package/{content → stacks/blazor-azure}/.morph/hooks/teammate-idle.js +0 -0
- /package/{content → stacks/blazor-azure}/.morph/project.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/schemas/agent.schema.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/schemas/tasks.schema.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/specs/.gitkeep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-blazor-ui.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-production.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-setup.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/agent-framework-workflows.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/architecture.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/azure.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/coding.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/dotnet10-migration.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/fluent-ui-setup.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/passkeys-auth.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/standards/vector-search-rag.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/state.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/CONTEXT-FEATURE.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/CONTEXT.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/FluentDesignTheme.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/MudTheme.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/agent.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/clarify-questions.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/component.razor +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Commands.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Entities.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/contracts/Queries.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/contracts/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/contracts.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/decisions.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/design-system.css +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/.dockerignore.example +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/Dockerfile.example +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/README.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/app-insights.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/app-service.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/azure-pipelines-deploy.yml +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/container-app-env.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/container-app.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy.ps1 +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/deploy.sh +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/key-vault.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/main.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.dev.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.prod.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/parameters.staging.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/sql-database.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/infra/storage.bicep +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/integrations/asaas-client.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/integrations/asaas-webhook.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/integrations/azure-identity-config.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/integrations/clerk-config.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/job.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/migration.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/proposal.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/recap.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/repository.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/saas/subscription.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/saas/tenant.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/service.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/simulation.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/spec.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/sprint-status.yaml +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/state.template.json +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/story.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/tasks.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/test.cs +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/ui-components.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/ui-design-system.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/ui-flows.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/templates/ui-mockups.md +0 -0
- /package/{content → stacks/blazor-azure}/.morph/test-infra/example.bicep +0 -0
- /package/{content → stacks/blazor-azure}/CLAUDE.md +0 -0
- /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.
|
|
195
|
-
join(projectPath,
|
|
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: '
|
|
277
|
+
{ type: 'stack', path: join(resolveStandardsDir(projectPath), `${standardName}.md`) },
|
|
277
278
|
{ type: 'framework', path: join(projectPath, 'framework/standards', `${standardName}.md`) }
|
|
278
279
|
];
|
|
279
280
|
|
package/src/lib/state-manager.js
CHANGED
|
@@ -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 =
|
|
27
|
+
const agentsPath = resolveAgentsConfigPath(projectPath);
|
|
27
28
|
const content = await fs.readFile(agentsPath, 'utf8');
|
|
28
29
|
return JSON.parse(content);
|
|
29
30
|
}
|