@polymorphism-tech/morph-spec 8.47.0 → 9.1.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 +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scaffold Command - Generate feature files from templates.
|
|
3
|
-
*
|
|
4
|
-
* Sub-commands:
|
|
5
|
-
* - scaffold dotnet <feature> <slice-name> — VSA feature slice (.NET)
|
|
6
|
-
* - scaffold nextjs <feature> — Feature page + components (Next.js)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
import chalk from 'chalk';
|
|
10
2
|
import { relative } from 'path';
|
|
11
3
|
import { logger } from '../../utils/logger.js';
|
|
@@ -17,11 +9,6 @@ import {
|
|
|
17
9
|
|
|
18
10
|
const KEBAB_REGEX = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
19
11
|
|
|
20
|
-
/**
|
|
21
|
-
* Validate that a name is valid kebab-case.
|
|
22
|
-
* @param {string} name
|
|
23
|
-
* @param {string} label
|
|
24
|
-
*/
|
|
25
12
|
function validateKebabCase(name, label) {
|
|
26
13
|
if (!KEBAB_REGEX.test(name)) {
|
|
27
14
|
logger.error(`Invalid ${label}: "${name}"`);
|
|
@@ -30,11 +17,6 @@ function validateKebabCase(name, label) {
|
|
|
30
17
|
}
|
|
31
18
|
}
|
|
32
19
|
|
|
33
|
-
/**
|
|
34
|
-
* Log the result of a scaffold operation.
|
|
35
|
-
* @param {Array<{path: string, skipped: boolean}>} files
|
|
36
|
-
* @param {string} outputDir
|
|
37
|
-
*/
|
|
38
20
|
function logResult(files, outputDir) {
|
|
39
21
|
let created = 0;
|
|
40
22
|
let skipped = 0;
|
|
@@ -56,16 +38,12 @@ function logResult(files, outputDir) {
|
|
|
56
38
|
logger.info(parts.join(', '));
|
|
57
39
|
}
|
|
58
40
|
|
|
59
|
-
/**
|
|
60
|
-
* Scaffold .NET VSA feature slice.
|
|
61
|
-
*/
|
|
62
41
|
export async function scaffoldDotnetCommand(feature, sliceName, options) {
|
|
63
42
|
validateKebabCase(feature, 'feature name');
|
|
64
43
|
validateKebabCase(sliceName, 'slice name');
|
|
65
44
|
|
|
66
45
|
const outputDir = options.outputDir || process.cwd();
|
|
67
46
|
|
|
68
|
-
// Detect or use provided namespace
|
|
69
47
|
const namespace = options.namespace || detectNamespace(outputDir);
|
|
70
48
|
if (!namespace) {
|
|
71
49
|
logger.error('Could not detect namespace');
|
|
@@ -100,9 +78,6 @@ export async function scaffoldDotnetCommand(feature, sliceName, options) {
|
|
|
100
78
|
}
|
|
101
79
|
}
|
|
102
80
|
|
|
103
|
-
/**
|
|
104
|
-
* Scaffold Next.js feature page with components and hooks.
|
|
105
|
-
*/
|
|
106
81
|
export async function scaffoldNextjsCommand(feature, options) {
|
|
107
82
|
validateKebabCase(feature, 'feature name');
|
|
108
83
|
|
|
@@ -1,54 +1,30 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { join, relative } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { getFeature, setApprovalGate, derivePhase } from '../../core/state/state-manager.js';
|
|
6
6
|
import { unmetPrereqs } from '../../core/state/gate-order.js';
|
|
7
7
|
import { OUTPUT_SCHEMA } from '../../core/paths/output-schema.js';
|
|
8
8
|
import { assertOwnerTree, ownerTreeRefusalLines } from '../../lib/worktree/feature-truth.js';
|
|
9
9
|
import { uiuxAutoApprovalBlock } from '../../lib/specs/screen-checklist.js';
|
|
10
|
+
import { seedDesignArtifacts } from '../../lib/specs/design-seed.js';
|
|
11
|
+
import { sliceProgress } from '../../../framework/hooks/shared/slice-progress.js';
|
|
12
|
+
import { autoApprovalRefusal, printRefusal } from './auto-approval.js';
|
|
13
|
+
import { seedDotnetBuildIgnores } from '../../lib/stack/dotnet-build-ignores.js';
|
|
10
14
|
|
|
11
15
|
const FRAMEWORK_DIR = fileURLToPath(new URL('../../../framework/', import.meta.url));
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* the next phase's folder in one step. This is the sanctioned, one-command way
|
|
16
|
-
* to move a feature forward: it removes the need to hand-edit state.json (gate)
|
|
17
|
-
* AND the need to `mkdir` the next phase folder by hand. Once the gate is
|
|
18
|
-
* approved, the enforce-phase-writes hook permits writing the next phase's first
|
|
19
|
-
* canonical artifact (which then advances the derived phase).
|
|
20
|
-
*/
|
|
17
|
+
// `implement` has no gate: advancing from it only scaffolds the review folder.
|
|
18
|
+
const PHASE_GATE = { plan: 'plan', uiux: 'uiux', review: 'review' };
|
|
21
19
|
|
|
22
|
-
// The gate approved when *completing* a given phase. `implement` has no gate
|
|
23
|
-
// (it ends when all tasks are done), so advancing from implement just scaffolds
|
|
24
|
-
// the review folder.
|
|
25
|
-
const PHASE_GATE = { proposal: 'proposal', uiux: 'uiux', plan: 'plan', review: 'review' };
|
|
26
|
-
|
|
27
|
-
// The next phase folder to scaffold when advancing FROM a given phase. The
|
|
28
|
-
// default forward path skips the optional design phase (proposal → plan); pass
|
|
29
|
-
// --design to take the uiux branch (proposal → 1-design).
|
|
30
20
|
const NEXT_PHASE_DIR = {
|
|
31
|
-
proposal: '2-plan',
|
|
32
|
-
uiux: '2-plan',
|
|
33
21
|
plan: '3-implement',
|
|
22
|
+
uiux: '3-implement',
|
|
34
23
|
implement: '4-review',
|
|
35
24
|
};
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* `framework/phases.json → phases[phase].requiredOutputs`, resolved to real
|
|
40
|
-
* filenames through OUTPUT_SCHEMA.
|
|
41
|
-
*
|
|
42
|
-
* Data-driven on purpose: adding an artifact to a phase must not need a code
|
|
43
|
-
* change here, and the list must never drift from the phase definition.
|
|
44
|
-
* Fail-open — an unreadable phases.json/OUTPUT_SCHEMA yields an empty list, so
|
|
45
|
-
* a broken install degrades to today's behaviour instead of blocking every
|
|
46
|
-
* advance.
|
|
47
|
-
*
|
|
48
|
-
* @param {string} phase
|
|
49
|
-
* @param {string} featurePath
|
|
50
|
-
* @returns {string[]} relative paths of the MISSING required artifacts
|
|
51
|
-
*/
|
|
26
|
+
// Data-driven from phases.json so the list never drifts from the phase definition. Fail-open:
|
|
27
|
+
// an unreadable phases.json yields an empty list instead of blocking every advance.
|
|
52
28
|
function missingPhaseArtifacts(phase, featurePath) {
|
|
53
29
|
try {
|
|
54
30
|
const phasesPath = join(FRAMEWORK_DIR, 'phases.json');
|
|
@@ -56,33 +32,27 @@ function missingPhaseArtifacts(phase, featurePath) {
|
|
|
56
32
|
const required = phases[phase]?.requiredOutputs || [];
|
|
57
33
|
const missing = [];
|
|
58
34
|
for (const outputType of required) {
|
|
59
|
-
//
|
|
60
|
-
// unknown key is skipped rather than guessed at.
|
|
35
|
+
// An unknown key is skipped rather than guessed at.
|
|
61
36
|
const entry = OUTPUT_SCHEMA[outputType];
|
|
62
37
|
if (!entry?.filename || !entry?.phaseDir) continue;
|
|
63
38
|
const rel = `${entry.phaseDir}/${entry.filename}`;
|
|
64
39
|
if (!existsSync(join(featurePath, entry.phaseDir, entry.filename))) missing.push(rel);
|
|
65
40
|
}
|
|
41
|
+
if (phase === 'implement') missing.push(...unfinishedSliceReports(featurePath));
|
|
66
42
|
return missing;
|
|
67
43
|
} catch {
|
|
68
44
|
return [];
|
|
69
45
|
}
|
|
70
46
|
}
|
|
71
47
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
* trees. It replaces a misleading error with an accurate one.
|
|
81
|
-
*
|
|
82
|
-
* Fail-open — no git, no worktree, or any error yields null (today's message).
|
|
83
|
-
*
|
|
84
|
-
* @returns {Promise<{path: string, branch: string|null, present: string[]}|null>}
|
|
85
|
-
*/
|
|
48
|
+
function unfinishedSliceReports(featurePath) {
|
|
49
|
+
const progress = sliceProgress(featurePath);
|
|
50
|
+
if (!progress) return ['2-plan/checks.json'];
|
|
51
|
+
return progress.slices.filter((s) => s.status !== 'feito').map((s) => `3-implement/reports/${s.id}.md`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Message-only and fail-open: names the worktree that holds the artifacts instead of demanding
|
|
55
|
+
// a file that exists one directory over. Never writes across trees.
|
|
86
56
|
async function artifactsLiveInWorktree(featureName, missing) {
|
|
87
57
|
try {
|
|
88
58
|
const { findWorktreeForFeature } = await import('../project/worktree.js');
|
|
@@ -97,7 +67,6 @@ async function artifactsLiveInWorktree(featureName, missing) {
|
|
|
97
67
|
}
|
|
98
68
|
}
|
|
99
69
|
|
|
100
|
-
/** Resolve who approved, honestly (mirrors approve.js — no fake human signatures). */
|
|
101
70
|
function resolveApprovedBy(options) {
|
|
102
71
|
if (options.approver) return options.approver;
|
|
103
72
|
if (options.mode === 'auto') return 'auto';
|
|
@@ -105,6 +74,16 @@ function resolveApprovedBy(options) {
|
|
|
105
74
|
return 'auto';
|
|
106
75
|
}
|
|
107
76
|
|
|
77
|
+
export function reportDotnetIgnores(added) {
|
|
78
|
+
if (added.length) console.log(chalk.gray(` .gitignore: ${added.join(' ')} adicionados (projeto .NET) — commite antes do primeiro build.`));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function reportDesignSeed({ mockups, designSystem }) {
|
|
82
|
+
const rel = (p) => relative(process.cwd(), p).replace(/\\/g, '/');
|
|
83
|
+
if (mockups.written) console.log(chalk.gray(` Escrito ${rel(mockups.path)} a partir do template (checklist de tela por \`## Tela:\`).`));
|
|
84
|
+
if (designSystem.written) console.log(chalk.gray(` Escrito ${rel(designSystem.path)} a partir do template (o projeto não tinha design system).`));
|
|
85
|
+
}
|
|
86
|
+
|
|
108
87
|
export async function advanceCommand(featureName, options = {}) {
|
|
109
88
|
try {
|
|
110
89
|
const feature = getFeature(featureName);
|
|
@@ -113,9 +92,7 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
113
92
|
process.exit(1);
|
|
114
93
|
}
|
|
115
94
|
|
|
116
|
-
//
|
|
117
|
-
// never stamp a gate onto the root's birth-instant skeleton (branch-drift
|
|
118
|
-
// created by this command, not merely left behind by one).
|
|
95
|
+
// A live worktree elsewhere owns the state: never stamp a gate onto the root skeleton.
|
|
119
96
|
const owner = assertOwnerTree(featureName, process.cwd());
|
|
120
97
|
if (owner.blocked) {
|
|
121
98
|
for (const { tone, text } of ownerTreeRefusalLines(featureName, owner.tree, owner.branch, `morph-spec advance ${featureName}`)) {
|
|
@@ -127,6 +104,21 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
127
104
|
const featurePath = join(process.cwd(), '.morph', 'features', featureName);
|
|
128
105
|
const phase = derivePhase(featurePath);
|
|
129
106
|
|
|
107
|
+
// The plan gate is often closed by the human's answer to the gate question
|
|
108
|
+
// (state-sync), which already moves the derived phase to implement: taking
|
|
109
|
+
// the design branch then is opening 1-design/, not closing a phase.
|
|
110
|
+
if (options.design && phase === 'implement' && feature.approvalGates?.plan?.approved === true) {
|
|
111
|
+
mkdirSync(join(featurePath, '1-design'), { recursive: true });
|
|
112
|
+
reportDesignSeed(seedDesignArtifacts(featureName, process.cwd()));
|
|
113
|
+
reportDotnetIgnores(seedDotnetBuildIgnores(process.cwd(), { featureName }));
|
|
114
|
+
try {
|
|
115
|
+
const { writeHandoff } = await import('../../lib/generators/handoff-generator.js');
|
|
116
|
+
writeHandoff(featureName, process.cwd());
|
|
117
|
+
} catch { /* fail-open */ }
|
|
118
|
+
console.log(chalk.green(`\n✅ "${featureName}": gate plan já aprovado — ramo de design aberto (1-design/).\n`));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
130
122
|
if (phase === 'review') {
|
|
131
123
|
console.log(chalk.yellow(`\n⚠️ Feature "${featureName}" is already at the final phase (review).`));
|
|
132
124
|
console.log(chalk.gray(`Approve Gate 3 with: morph-spec approve ${featureName} review`));
|
|
@@ -134,19 +126,10 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
134
126
|
return;
|
|
135
127
|
}
|
|
136
128
|
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// with NO 3-implement/recap.md — the file was written later, by hand, so
|
|
140
|
-
// the Gate 3 review had nothing to review. `implement` has no gate, which
|
|
141
|
-
// is precisely why nothing stopped it.
|
|
142
|
-
//
|
|
143
|
-
// `--force` bypasses (same deliberate-recovery role it already plays for
|
|
144
|
-
// gate order) but still names what was skipped — a silent bypass would be
|
|
145
|
-
// the defect again.
|
|
129
|
+
// `implement` has no gate, so this is what keeps unfinished slice reports out of review.
|
|
130
|
+
// `--force` bypasses it but still names what was skipped.
|
|
146
131
|
const missingArtifacts = missingPhaseArtifacts(phase, featurePath);
|
|
147
132
|
if (missingArtifacts.length && !options.force) {
|
|
148
|
-
// Divergence, not absence: name the tree that actually holds the work
|
|
149
|
-
// instead of asking for a file that exists in it.
|
|
150
133
|
const elsewhere = await artifactsLiveInWorktree(featureName, missingArtifacts);
|
|
151
134
|
if (elsewhere) {
|
|
152
135
|
console.error(chalk.red(`\n❌ A fase "${phase}" de "${featureName}" não pode ser encerrada A PARTIR DESTA ÁRVORE.`));
|
|
@@ -160,36 +143,16 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
160
143
|
process.exit(1);
|
|
161
144
|
}
|
|
162
145
|
|
|
163
|
-
console.error(chalk.red(`\n❌ A fase "${phase}" não pode ser encerrada — artefato(s) obrigatório(s) ausente(s):`));
|
|
146
|
+
console.error(chalk.red(`\n❌ A fase "${phase}" não pode ser encerrada — artefato(s) obrigatório(s) ausente(s) ou PARCIAL:`));
|
|
164
147
|
for (const rel of missingArtifacts) console.error(chalk.gray(` • ${rel}`));
|
|
165
148
|
console.error(chalk.gray('\n Produza o artefato e rode de novo, ou passe --force para avançar assim mesmo.\n'));
|
|
166
149
|
process.exit(1);
|
|
167
150
|
}
|
|
168
151
|
|
|
169
|
-
// 1) Approve the gate that completes the current phase.
|
|
170
152
|
const gate = PHASE_GATE[phase];
|
|
171
153
|
const approvedBy = resolveApprovedBy(options);
|
|
172
154
|
|
|
173
|
-
//
|
|
174
|
-
// is approve.js's too: an "auto" signature never closes a gate the
|
|
175
|
-
// gate-check would pause, and a recorded decision or a human signature
|
|
176
|
-
// (--approver) is the answer to that pause; --force is not, as in approve.
|
|
177
|
-
const checklistBlock = phase === 'uiux' ? uiuxAutoApprovalBlock(process.cwd(), featureName, feature) : null;
|
|
178
|
-
if (checklistBlock && approvedBy === 'auto') {
|
|
179
|
-
console.error(chalk.red(`\n❌ A fase "uiux" não pode ser encerrada com assinatura automática — ${checklistBlock}.`));
|
|
180
|
-
console.error(chalk.gray('\n Complete o checklist (ver a skill morph-uiux) e rode de novo. Quem decidiu aprovar assim'));
|
|
181
|
-
console.error(chalk.gray(` mesmo grava a decisão (morph-spec gate-decision ${featureName} uiux --pause false --reason "...")`));
|
|
182
|
-
console.error(chalk.gray(' ou assina com --approver <nome>. O --force não dispensa o checklist.\n'));
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Gate monotonicity (mirrors approve.js): advance is a gate write-path too,
|
|
187
|
-
// so it must not stamp gate N while a predecessor is pending. In the
|
|
188
|
-
// sanctioned flow advance is always in-order (it approves G1 before the plan
|
|
189
|
-
// artifact that flips the phase to plan exists), but an anomalous filesystem
|
|
190
|
-
// state — e.g. 2-plan/spec.md present without Gate 1, the lead-termometro
|
|
191
|
-
// shape — would otherwise let advance deepen the violation by stamping G2.
|
|
192
|
-
// Escape via --force for deliberate recovery.
|
|
155
|
+
// Gate monotonicity: never stamp gate N while a predecessor is pending (`--force` to recover).
|
|
193
156
|
if (gate) {
|
|
194
157
|
const unmet = unmetPrereqs(gate, feature.approvalGates);
|
|
195
158
|
if (unmet.length && !options.force) {
|
|
@@ -199,35 +162,19 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
199
162
|
}
|
|
200
163
|
}
|
|
201
164
|
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
// gate==='review' approval, but guard the approval itself too so this
|
|
205
|
-
// stays true even if the early-return's shape changes later. Gate on the
|
|
206
|
-
// RESOLVED approvedBy — headless contexts have no TTY and resolve to
|
|
207
|
-
// "auto" regardless of --mode, so checking the raw --mode string alone
|
|
208
|
-
// would let them slip through (same bug the approve.js backstop had).
|
|
209
|
-
if (gate === 'review' && feature.workType === 'hotfix' && approvedBy === 'auto') {
|
|
210
|
-
console.error(chalk.red('\n❌ Hotfix exige aprovação humana no Gate 3 — trust:auto não se aplica.'));
|
|
211
|
-
console.error(chalk.gray(' Aprove com --approver <nome>, num terminal interativo, ou pela resposta humana "aprovado" (state-sync) ao review-report via AskUserQuestion.\n'));
|
|
212
|
-
process.exit(1);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Gate idempotency (F15): `setApprovalGate` REPLACES the gate object whole
|
|
216
|
-
// (state-manager.js), so re-writing an already-approved gate does not
|
|
217
|
-
// refresh it — it destroys `approvedBy`, `timestamp`, `notes` and
|
|
218
|
-
// `selfAssessed` in one assignment. And `resolveApprovedBy` above answers
|
|
219
|
-
// "auto" in every headless context, so `approve f proposal --approver
|
|
220
|
-
// lucas` followed by `advance f` used to rewrite a human's signature as
|
|
221
|
-
// "auto" — the one stamp the charter forbids on a human-approved gate.
|
|
222
|
-
// The state-sync hook already carries exactly this guard; `advance`, the
|
|
223
|
-
// other gate write-path, simply never got it.
|
|
224
|
-
//
|
|
225
|
-
// It skips ONLY the write. The scaffold and the handoff below still run,
|
|
226
|
-
// so re-running `advance` after a failed scaffold keeps working — the
|
|
227
|
-
// reason this is a guard and not an abort.
|
|
165
|
+
// `setApprovalGate` replaces the gate whole: rewriting an approved gate would turn a human
|
|
166
|
+
// signature into "auto". Skips ONLY the write, so the scaffold and handoff below still run.
|
|
228
167
|
const priorGate = gate ? feature.approvalGates?.[gate] : null;
|
|
229
168
|
const alreadyApproved = priorGate?.approved === true;
|
|
230
169
|
|
|
170
|
+
const refusal = alreadyApproved ? null
|
|
171
|
+
: autoApprovalRefusal({ cwd: process.cwd(), featureName, feature, gate, approvedBy });
|
|
172
|
+
if (refusal) {
|
|
173
|
+
printRefusal(chalk, refusal);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
const checklistBlock = phase === 'uiux' && !alreadyApproved ? uiuxAutoApprovalBlock(process.cwd(), featureName, feature) : null;
|
|
177
|
+
|
|
231
178
|
if (gate && !alreadyApproved) {
|
|
232
179
|
await setApprovalGate(featureName, gate, true, {
|
|
233
180
|
approvedBy,
|
|
@@ -236,17 +183,16 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
236
183
|
});
|
|
237
184
|
}
|
|
238
185
|
|
|
239
|
-
// 2) Scaffold the next phase folder so the agent can write its first artifact.
|
|
240
186
|
let nextDir = NEXT_PHASE_DIR[phase];
|
|
241
|
-
if (phase === '
|
|
187
|
+
if (phase === 'plan' && options.design) nextDir = '1-design';
|
|
242
188
|
if (nextDir) {
|
|
243
189
|
const nextPath = join(featurePath, nextDir);
|
|
244
190
|
if (!existsSync(nextPath)) mkdirSync(nextPath, { recursive: true });
|
|
245
191
|
}
|
|
192
|
+
if (nextDir === '1-design') reportDesignSeed(seedDesignArtifacts(featureName, process.cwd()));
|
|
193
|
+
if (phase === 'plan') reportDotnetIgnores(seedDotnetBuildIgnores(process.cwd(), { featureName }));
|
|
246
194
|
|
|
247
|
-
//
|
|
248
|
-
// feature over (to a new instance, or to itself after /compact) — refresh
|
|
249
|
-
// the derived handoff.md so the next reader starts from the new phase.
|
|
195
|
+
// A phase transition is when a session hands the feature over: refresh handoff.md.
|
|
250
196
|
try {
|
|
251
197
|
const { writeHandoff } = await import('../../lib/generators/handoff-generator.js');
|
|
252
198
|
writeHandoff(featureName, process.cwd());
|
|
@@ -260,8 +206,7 @@ export async function advanceCommand(featureName, options = {}) {
|
|
|
260
206
|
const by = options.force ? '--force' : `aprovado por ${approvedBy}`;
|
|
261
207
|
console.log(chalk.yellow(` ⚠️ ${by}: avançou com o ${checklistBlock}.`));
|
|
262
208
|
}
|
|
263
|
-
//
|
|
264
|
-
// was written is how the destroyed signature stayed invisible.
|
|
209
|
+
// Never claim "approved (auto)" when nothing was written.
|
|
265
210
|
if (gate && alreadyApproved) {
|
|
266
211
|
console.log(chalk.gray(` Gate "${gate}" was already approved (${priorGate.approvedBy || 'unknown'}) — approval preserved, nothing re-written.`));
|
|
267
212
|
} else if (gate) {
|