@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
package/bin/morph-spec.js
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* morph-spec CLI v5
|
|
5
|
-
*
|
|
6
|
-
* In v5 the CLI is the *installer* — Claude executes everything else
|
|
7
|
-
* autonomously based on MORPH.md and the core skills. Commands here are
|
|
8
|
-
* limited to actions that humans take or operations Claude needs help with
|
|
9
|
-
* (scaffolding, validation, context refresh).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
3
|
import { program } from 'commander';
|
|
13
4
|
import chalk from 'chalk';
|
|
14
5
|
import { fileURLToPath } from 'url';
|
|
@@ -16,14 +7,9 @@ import { dirname, join } from 'path';
|
|
|
16
7
|
import { readFileSync } from 'fs';
|
|
17
8
|
import { renderLogo, maybePrintFirstRunBanner } from '../src/utils/banner.js';
|
|
18
9
|
|
|
19
|
-
//
|
|
20
|
-
// ~700 ms on EVERY invocation (init.js alone pulls inquirer: ~550 ms), paid
|
|
21
|
-
// by each hook that shells out to the CLI and by each CLI spawn in the suite.
|
|
22
|
-
// test/bin/command-modules-import.test.js imports every path below, so a module
|
|
23
|
-
// that breaks at import time still fails the suite, not only its own command.
|
|
10
|
+
// Lazy: eager import of every command cost ~700 ms per invocation, paid by each hook that shells out.
|
|
24
11
|
const lazy = (path, name) => async (...args) => (await import(path))[name](...args);
|
|
25
12
|
|
|
26
|
-
// === Project / install ===
|
|
27
13
|
const initCommand = lazy('../src/commands/project/init.js', 'initCommand');
|
|
28
14
|
const setupInfraCommand = lazy('../src/commands/project/setup-infra-cmd.js', 'setupInfraCommand');
|
|
29
15
|
const installPluginCommand = lazy('../src/commands/project/install-plugin-cmd.js', 'installPluginCommand');
|
|
@@ -45,8 +31,9 @@ const claimCommand = lazy('../src/commands/project/claim.js', 'claimCommand');
|
|
|
45
31
|
const standardsCommand = lazy('../src/commands/project/standards.js', 'standardsCommand');
|
|
46
32
|
const reportCheckCommand = lazy('../src/commands/state/report-check.js', 'reportCheckCommand');
|
|
47
33
|
const scoutCheckCommand = lazy('../src/commands/validation/scout-check.js', 'scoutCheckCommand');
|
|
34
|
+
const planCheckCommand = lazy('../src/commands/validation/plan-check.js', 'planCheckCommand');
|
|
35
|
+
const stackScanCommand = lazy('../src/commands/project/stack-scan.js', 'stackScanCommand');
|
|
48
36
|
|
|
49
|
-
// === Approval gates (human-only) ===
|
|
50
37
|
const approveCommand = lazy('../src/commands/state/approve.js', 'approveCommand');
|
|
51
38
|
const approvalStatusCommand = lazy('../src/commands/state/approve.js', 'approvalStatusCommand');
|
|
52
39
|
const unapproveCommand = lazy('../src/commands/state/approve.js', 'unapproveCommand');
|
|
@@ -60,19 +47,12 @@ const retypeCommand = lazy('../src/commands/state/retype.js', 'retypeCommand');
|
|
|
60
47
|
const handoffCommand = lazy('../src/commands/state/handoff.js', 'handoffCommand');
|
|
61
48
|
const listCommand = lazy('../src/commands/state/list.js', 'listCommand');
|
|
62
49
|
const deleteCommand = lazy('../src/commands/state/delete.js', 'deleteCommand');
|
|
63
|
-
const usecaseReserveCommand = lazy('../src/commands/state/usecase.js', 'usecaseReserveCommand');
|
|
64
|
-
const usecaseCheckCommand = lazy('../src/commands/state/usecase.js', 'usecaseCheckCommand');
|
|
65
|
-
|
|
66
|
-
// === Tasks ===
|
|
67
|
-
const dagCommand = lazy('../src/commands/tasks/dag.js', 'dagCommand');
|
|
68
50
|
|
|
69
|
-
// === Codebase graph ===
|
|
70
51
|
const graphStatusCommand = lazy('../src/commands/graph/status.js', 'graphStatusCommand');
|
|
71
52
|
const graphBuildCommand = lazy('../src/commands/graph/build.js', 'graphBuildCommand');
|
|
72
53
|
const graphAffectedCommand = lazy('../src/commands/graph/affected.js', 'graphAffectedCommand');
|
|
73
54
|
const graphExplainCommand = lazy('../src/commands/graph/explain.js', 'graphExplainCommand');
|
|
74
55
|
|
|
75
|
-
// === Validation ===
|
|
76
56
|
const validateCommand = lazy('./validate.js', 'validateCommand');
|
|
77
57
|
const validateFeatureCommand = lazy('../src/commands/validation/validate-feature.js', 'validateFeatureCommand');
|
|
78
58
|
const verifyCommand = lazy('../src/commands/validation/verify.js', 'verifyCommand');
|
|
@@ -81,20 +61,15 @@ const e2eUpCommand = lazy('../src/commands/e2e/e2e.js', 'e2eUpCommand');
|
|
|
81
61
|
const e2eStatusCommand = lazy('../src/commands/e2e/e2e.js', 'e2eStatusCommand');
|
|
82
62
|
const e2eDownCommand = lazy('../src/commands/e2e/e2e.js', 'e2eDownCommand');
|
|
83
63
|
|
|
84
|
-
// === Scaffolding ===
|
|
85
64
|
const scaffoldDotnetCommand = lazy('../src/commands/scaffold/scaffold.js', 'scaffoldDotnetCommand');
|
|
86
65
|
const scaffoldNextjsCommand = lazy('../src/commands/scaffold/scaffold.js', 'scaffoldNextjsCommand');
|
|
87
66
|
|
|
88
|
-
// === MCP setup ===
|
|
89
67
|
const mcpSetupCommand = lazy('../src/commands/mcp/mcp-setup.js', 'mcpSetupCommand');
|
|
90
68
|
|
|
91
|
-
// === Context refresh ===
|
|
92
69
|
const contextRefreshCommand = lazy('../src/commands/context/context-refresh.js', 'contextRefreshCommand');
|
|
93
70
|
|
|
94
|
-
// === Dashboard ===
|
|
95
71
|
const dashboardCommand = lazy('../src/commands/dashboard/dashboard.js', 'dashboardCommand');
|
|
96
72
|
|
|
97
|
-
// === Observability ===
|
|
98
73
|
const telemetryCommand = lazy('../src/commands/observability/telemetry.js', 'telemetryCommand');
|
|
99
74
|
const costCommand = lazy('../src/commands/observability/cost.js', 'costCommand');
|
|
100
75
|
|
|
@@ -105,9 +80,7 @@ const banner = `${renderLogo(pkg.version)}
|
|
|
105
80
|
${chalk.dim('Spec-driven development harness for Claude Code — by Polymorphism Tech')}
|
|
106
81
|
`;
|
|
107
82
|
|
|
108
|
-
//
|
|
109
|
-
// postinstall output). Skipped for --version/--help (parsed/already-bannered)
|
|
110
|
-
// and for non-TTY output — see maybePrintFirstRunBanner.
|
|
83
|
+
// npm >=7 hides postinstall output, so the first run after an install shows the banner.
|
|
111
84
|
const argvRest = process.argv.slice(2);
|
|
112
85
|
const skipFirstRun = argvRest.length === 0 ||
|
|
113
86
|
argvRest.some(a => ['-V', '--version', '-h', '--help'].includes(a));
|
|
@@ -121,7 +94,6 @@ program
|
|
|
121
94
|
.version(pkg.version)
|
|
122
95
|
.addHelpText('beforeAll', banner);
|
|
123
96
|
|
|
124
|
-
// ─── Install ──────────────────────────────────────────────────────────────
|
|
125
97
|
program
|
|
126
98
|
.command('init')
|
|
127
99
|
.description('Initialize MORPH-SPEC in current directory')
|
|
@@ -164,7 +136,6 @@ program
|
|
|
164
136
|
.option('--reset', 'Remove morph-managed entries from .claude/settings.local.json')
|
|
165
137
|
.action(doctorCommand);
|
|
166
138
|
|
|
167
|
-
// ─── Status / inspection ──────────────────────────────────────────────────
|
|
168
139
|
program
|
|
169
140
|
.command('status <feature>')
|
|
170
141
|
.description('Show feature status (phase, gate state, task counts)')
|
|
@@ -174,22 +145,21 @@ program
|
|
|
174
145
|
|
|
175
146
|
program
|
|
176
147
|
.command('list')
|
|
177
|
-
.description('List every feature × status × gates (G1/
|
|
148
|
+
.description('List every feature × status × gates (G1 plan / Gd uiux / G3 review) × workType — the audit board')
|
|
178
149
|
.option('--all', 'Include archived features (.morph/archive)')
|
|
179
150
|
.option('--archived', 'Include archived features (alias of --all)')
|
|
180
151
|
.option('--no-branch', 'Skip reading morph/{feature} for truth — report only what is on disk in this tree')
|
|
181
152
|
.option('--json', 'Output as JSON')
|
|
182
153
|
.action((options) => listCommand(options));
|
|
183
154
|
|
|
184
|
-
// ─── Approval gates (dev-only) ────────────────────────────────────────────
|
|
185
155
|
program
|
|
186
156
|
.command('approve <feature> <gate>')
|
|
187
|
-
.description('Approve a phase gate (
|
|
157
|
+
.description('Approve a phase gate (plan, uiux, review). Enforces gate order (uiux and review require plan)')
|
|
188
158
|
.option('--approver <name>', 'Name of approver (default: current user when interactive, "auto" otherwise)')
|
|
189
159
|
.option('--mode <mode>', 'Approval mode: "user" or "auto" (default: detected from TTY)')
|
|
190
160
|
.option('--notes <notes>', 'Approval notes')
|
|
191
161
|
.option('--force', 'Approve out of order (bypass the gate-monotonicity check)')
|
|
192
|
-
.option('--self-assessed', 'Gate 3 only: approve without
|
|
162
|
+
.option('--self-assessed', 'Gate 3 only: approve without the review reports (verifier.md, reviewer.md, security.md), recording the degradation in feature.json')
|
|
193
163
|
.action((feature, gate, options) => approveCommand(feature, gate, options));
|
|
194
164
|
|
|
195
165
|
program
|
|
@@ -197,7 +167,7 @@ program
|
|
|
197
167
|
.description('Approve the current phase gate and scaffold the next phase folder (one step)')
|
|
198
168
|
.option('--approver <name>', 'Name of approver (default: current user when interactive, "auto" otherwise)')
|
|
199
169
|
.option('--mode <mode>', 'Approval mode: "user" or "auto" (default: detected from TTY)')
|
|
200
|
-
.option('--design', 'From
|
|
170
|
+
.option('--design', 'From plan, advance into the optional design (uiux) phase instead of implement')
|
|
201
171
|
.option('--notes <notes>', 'Approval notes')
|
|
202
172
|
.option('--force', 'Advance even if a prerequisite gate is unapproved (out-of-order recovery)')
|
|
203
173
|
.action((feature, options) => advanceCommand(feature, options));
|
|
@@ -251,7 +221,7 @@ program
|
|
|
251
221
|
program
|
|
252
222
|
.command('gate-check <feature> <gate>')
|
|
253
223
|
.description('Decide + record a gate\'s trust decision from disk-derived signals (shouldPauseGate) — the deterministic default; see gate-decision for the manual escape hatch')
|
|
254
|
-
.option('--signals <json>', 'Override ONLY standardsViolations/designSystemDivergence (not derivable from disk) — everything else is read from feature.json/
|
|
224
|
+
.option('--signals <json>', 'Override ONLY standardsViolations/designSystemDivergence (not derivable from disk) — everything else is read from feature.json/checks.json/git and cannot be overridden here')
|
|
255
225
|
.option('--trust <mode>', 'Trust mode to decide with (default: config.json → trust, or "auto")')
|
|
256
226
|
.option('--json', 'Output as JSON')
|
|
257
227
|
.action((feature, gateArg, options) => gateCheckCommand(feature, gateArg, options));
|
|
@@ -294,34 +264,6 @@ program
|
|
|
294
264
|
.option('--here', 'Force building/writing in the current directory, even if another tree owns this feature')
|
|
295
265
|
.action((feature, options) => handoffCommand(feature, options));
|
|
296
266
|
|
|
297
|
-
// ─── Use cases ────────────────────────────────────────────────────────────
|
|
298
|
-
const usecaseCommand = program
|
|
299
|
-
.command('usecase')
|
|
300
|
-
.description('Project-wide use cases: UC-{nn} numbering (acervo + every worktree of this repo) and the acervo lifecycle check');
|
|
301
|
-
|
|
302
|
-
usecaseCommand
|
|
303
|
-
.command('reserve')
|
|
304
|
-
.description('Atomically reserve the next N UC-{nn} numbers, shared across every worktree of this repo — replaces the old max+1 disk scan, which collides across worktrees')
|
|
305
|
-
.option('--count <n>', 'How many numbers to reserve', '1')
|
|
306
|
-
.option('--feature <name>', 'Feature to record as the reservation owner (defaults to the sole active feature, if unambiguous) — also what the promotion collision guard compares against')
|
|
307
|
-
.option('--json', 'Output as JSON')
|
|
308
|
-
.action((options) => usecaseReserveCommand(options));
|
|
309
|
-
|
|
310
|
-
usecaseCommand
|
|
311
|
-
.command('check <feature>')
|
|
312
|
-
.description('Check a feature against the acervo lifecycle: tasks citing a superseded UC, malformed supersession frontmatter, spec.md revocation rows the finish could not apply (plus the task/use-case plumbing dag checks, when tasks.json exists)')
|
|
313
|
-
.option('--json', 'Output as JSON')
|
|
314
|
-
.action((feature, options) => usecaseCheckCommand(feature, options));
|
|
315
|
-
|
|
316
|
-
// ─── Tasks ────────────────────────────────────────────────────────────────
|
|
317
|
-
program
|
|
318
|
-
.command('dag <feature>')
|
|
319
|
-
.description('Analyze the tasks.json DAG and print the sequential vs parallel dispatch decision (effort-weighted)')
|
|
320
|
-
.option('--json', 'Output as JSON')
|
|
321
|
-
.option('--mermaid', 'Emit the tasks.json DAG as a Mermaid flowchart (flowchart TD)')
|
|
322
|
-
.action((feature, options) => dagCommand(feature, options));
|
|
323
|
-
|
|
324
|
-
// ─── Codebase graph ───────────────────────────────────────────────────────
|
|
325
267
|
const graphCommand = program
|
|
326
268
|
.command('graph')
|
|
327
269
|
.description('Grafo de codebase (graphify) com saneamento determinístico — fail-open: sem grafo, tudo segue por grep');
|
|
@@ -363,7 +305,6 @@ graphCommand
|
|
|
363
305
|
.option('--json', 'Output as JSON')
|
|
364
306
|
.action((symbol, options) => graphExplainCommand(symbol, options));
|
|
365
307
|
|
|
366
|
-
// ─── Validation ───────────────────────────────────────────────────────────
|
|
367
308
|
program
|
|
368
309
|
.command('validate [validator]')
|
|
369
310
|
.description('Run project validations (all | packages | next-components | spec-tasks)')
|
|
@@ -388,24 +329,23 @@ program
|
|
|
388
329
|
.action(validateFeatureCommand);
|
|
389
330
|
|
|
390
331
|
program
|
|
391
|
-
.command('verify <feature>
|
|
332
|
+
.command('verify <feature>')
|
|
392
333
|
.description('Run the deterministic verification chain (build + tests + validators + e2e) — code before the LLM judge')
|
|
393
334
|
.option('--json', 'Output as JSON')
|
|
394
335
|
.option('--timeout <ms>', 'Timeout in ms for the build AND tests nodes (build default 120000; tests default 120000 per test project, or project.testTimeoutMs)', (v) => parseInt(v, 10))
|
|
395
336
|
.option('--filter <ids>', 'Run only the test nodes whose id matches (comma-separated; exact or substring). Unmatched nodes are reported as skip; matching nothing is a usage error (exit 2)')
|
|
396
337
|
.option('--linux', 'Run the test nodes inside a Linux container, on a COPY of the tree (the host tree is mounted read-only and copied in — a bind mount is case-insensitive and cannot reproduce case defects). No Docker = usage error, never a skip')
|
|
397
338
|
.option('--linux-image <img>', 'Image used by --linux (default: mcr.microsoft.com/dotnet/sdk:10.0)')
|
|
398
|
-
.option('--
|
|
399
|
-
.option('--record-baseline', 'Measure every test node and write expectedCount back into .morph/config/config.json (only from an all-green run at FEATURE scope; with a task id it is a usage error; never creates config.json)')
|
|
339
|
+
.option('--record-baseline', 'Measure every test node and write expectedCount back into .morph/config/config.json (only from an all-green run; never creates config.json)')
|
|
400
340
|
.option('--refresh-cache', 'Rewrite the response cache of every `runner: "evals"` node against the real provider. NEVER implicit: a cache rewrite is a paid call nobody asked for. Requires the declared envFile; no eval node declared, --skip-tests, or a --filter that excludes them all are usage errors (exit 2), refused BEFORE the cache is touched')
|
|
401
|
-
.option('--skip-tests', 'Skip the test node.
|
|
402
|
-
.option('--skip-build', 'Skip the build node.
|
|
341
|
+
.option('--skip-tests', 'Skip the test node. The run is partial, so the verification stamp is NOT written (reason "skip-tests")')
|
|
342
|
+
.option('--skip-build', 'Skip the build node. The run is partial, so the verification stamp is NOT written (reason "skip-build")')
|
|
403
343
|
.option('--skip-e2e', 'Skip the e2e node (Docker bring-up + Playwright)')
|
|
404
344
|
.option('--fresh-e2e', 'Recreate the e2e stack from scratch: down -v (named volumes included), drop the freshness stamp, rebuild, re-seed auth')
|
|
405
345
|
.option('--no-persist', 'Do not persist the result to feature.json')
|
|
406
|
-
.option('--ci', '
|
|
407
|
-
.action((feature,
|
|
408
|
-
verifyCommand(feature,
|
|
346
|
+
.option('--ci', 'Push morph/<feature> and take the proof from the project CI (creates .github/workflows/morph-verify.yml when no workflow runs verify). Same stamp, source "ci". No GitHub/gh/workflow = local verify with the reason; uncommitted code = exit 2; CI still running after 50 min = exit 3, no stamp')
|
|
347
|
+
.action((feature, options) =>
|
|
348
|
+
verifyCommand(feature, { ...options, noPersist: options.persist === false })
|
|
409
349
|
);
|
|
410
350
|
|
|
411
351
|
program
|
|
@@ -421,7 +361,6 @@ program
|
|
|
421
361
|
.option('--json', 'Output as JSON')
|
|
422
362
|
.action((feature, options) => mutateCommand(feature, options));
|
|
423
363
|
|
|
424
|
-
// ─── Scaffolding ──────────────────────────────────────────────────────────
|
|
425
364
|
const scaffoldCmd = program
|
|
426
365
|
.command('scaffold')
|
|
427
366
|
.description('Generate feature files from templates');
|
|
@@ -443,7 +382,6 @@ scaffoldCmd
|
|
|
443
382
|
.option('-v, --verbose', 'Show detailed output')
|
|
444
383
|
.action(scaffoldNextjsCommand);
|
|
445
384
|
|
|
446
|
-
// ─── MCP setup ────────────────────────────────────────────────────────────
|
|
447
385
|
const mcpCommand = program
|
|
448
386
|
.command('mcp')
|
|
449
387
|
.description('MCP server setup and configuration');
|
|
@@ -455,7 +393,6 @@ mcpCommand
|
|
|
455
393
|
.option('--auto', 'Auto-install credential-free MCPs without prompts')
|
|
456
394
|
.action((name, options) => mcpSetupCommand(name, options));
|
|
457
395
|
|
|
458
|
-
// ─── Context refresh ──────────────────────────────────────────────────────
|
|
459
396
|
const contextCommand = program
|
|
460
397
|
.command('context')
|
|
461
398
|
.description('Project context management (refresh)');
|
|
@@ -468,7 +405,6 @@ contextCommand
|
|
|
468
405
|
.option('--json', 'Output scan result as JSON')
|
|
469
406
|
.action((options) => contextRefreshCommand(options));
|
|
470
407
|
|
|
471
|
-
// ─── E2E stack (Docker bring-up for runtime verification) ─────────────────
|
|
472
408
|
const e2eCommand = program
|
|
473
409
|
.command('e2e')
|
|
474
410
|
.description("Lifecycle of the feature's verification stack (docker compose)");
|
|
@@ -493,7 +429,6 @@ e2eCommand
|
|
|
493
429
|
.option('--json', 'Output as JSON')
|
|
494
430
|
.action((feature, options) => e2eDownCommand(feature, options));
|
|
495
431
|
|
|
496
|
-
// ─── Heavy nodes (build, tests, e2e) ─────────────────────────────────────
|
|
497
432
|
const heavyCommand = program
|
|
498
433
|
.command('heavy')
|
|
499
434
|
.description('Slots por usuário para os nós pesados (build, testes, e2e), compartilhados entre as sessões');
|
|
@@ -510,7 +445,6 @@ heavyCommand
|
|
|
510
445
|
.option('--label <label>', 'Nome do que roda, mostrado a quem espera pelo slot')
|
|
511
446
|
.action((command, options) => heavyRunCommand(command, options));
|
|
512
447
|
|
|
513
|
-
// ─── Worktree (optional, isolation) ───────────────────────────────────────
|
|
514
448
|
const worktreeCommand = program
|
|
515
449
|
.command('worktree')
|
|
516
450
|
.description('Manage git worktrees for feature isolation');
|
|
@@ -522,10 +456,6 @@ worktreeCommand
|
|
|
522
456
|
.option('--json', 'Output as JSON (always JSON — accepted so the WorktreeCreate hook can call setup and provision uniformly)')
|
|
523
457
|
.action((feature, options) => worktreeSetupCommand(feature, options));
|
|
524
458
|
|
|
525
|
-
// Task isolation — the tree `claude -w`, EnterWorktree and Agent
|
|
526
|
-
// isolation:"worktree" ask for when the name is NOT a feature. Same infra as
|
|
527
|
-
// setup (junctions, port block, git-invisible manifest, primary-root landing),
|
|
528
|
-
// no feature identity. The WorktreeCreate hook is its main caller.
|
|
529
459
|
worktreeCommand
|
|
530
460
|
.command('provision <name>')
|
|
531
461
|
.description('Create a task-isolation worktree at worktrees/{name}/ on branch morph-task/{name} — full morph infra, no feature identity')
|
|
@@ -540,10 +470,8 @@ worktreeCommand
|
|
|
540
470
|
.option('--all', 'Link all shared dirs, not just node_modules')
|
|
541
471
|
.action((options) => worktreeLinkCommand(options));
|
|
542
472
|
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
// root's .claude/ and .morph/framework/, so nothing in this repo (message, doc,
|
|
546
|
-
// or skill) may point at it; they all point here.
|
|
473
|
+
// Raw `git worktree remove` follows the infra junctions and empties the primary root's .claude/
|
|
474
|
+
// and .morph/framework/: nothing in this repo may point at it, only at this command.
|
|
547
475
|
worktreeCommand
|
|
548
476
|
.command('remove [feature]')
|
|
549
477
|
.description('Safely tear down a worktree (drops infra junctions first, PROVE-or-DECLINE) — does NOT close the feature; that is `finish`')
|
|
@@ -554,9 +482,6 @@ worktreeCommand
|
|
|
554
482
|
.option('--json', 'Output as JSON (always JSON)')
|
|
555
483
|
.action((feature, options) => worktreeRemoveCommand(feature, options));
|
|
556
484
|
|
|
557
|
-
// Sub-agent trees (`agent-<hex>` on `morph-task/agent-<hex>`) outlive the agent: nothing
|
|
558
|
-
// tells the harness the agent is done with its worktree. The reaper removes only what it can prove
|
|
559
|
-
// is finished and lost-proof; the rest is listed with the reason.
|
|
560
485
|
worktreeCommand
|
|
561
486
|
.command('reap')
|
|
562
487
|
.description('Remove the worktrees and branches of sub-agents that stopped clean (nothing uncommitted, no commit only they hold); everything else is listed with the reason and left alone')
|
|
@@ -571,11 +496,6 @@ worktreeCommand
|
|
|
571
496
|
.option('--json', 'Output as JSON')
|
|
572
497
|
.action((options) => worktreeListCommand(options));
|
|
573
498
|
|
|
574
|
-
// ─── Branch housekeeping ──────────────────────────────────────────────────
|
|
575
|
-
// `git branch --merged` cannot see a squash-merge, so the only command left for
|
|
576
|
-
// a squash-merged branch is `git branch -D` — indistinguishable from discarding
|
|
577
|
-
// real work. `prune` makes the distinction itself and deletes only what it
|
|
578
|
-
// proved is already integrated.
|
|
579
499
|
const branchCommand = program
|
|
580
500
|
.command('branch')
|
|
581
501
|
.description('Local branch housekeeping');
|
|
@@ -588,7 +508,6 @@ branchCommand
|
|
|
588
508
|
.option('--json', 'Output as JSON')
|
|
589
509
|
.action((options) => branchPruneCommand(options));
|
|
590
510
|
|
|
591
|
-
// ─── Worktree env (shell-loadable port block) ─────────────────────────────
|
|
592
511
|
program
|
|
593
512
|
.command('env [feature]')
|
|
594
513
|
.description('Print the worktree\'s dedicated environment (port block + COMPOSE_PROJECT_NAME), shell-loadable: eval "$(morph-spec env)" outside Claude Code; feature defaults to the morph/{f} or morph-task/{n} branch of cwd')
|
|
@@ -596,14 +515,12 @@ program
|
|
|
596
515
|
.option('--powershell', 'Emit $env:K="V" lines instead of POSIX exports')
|
|
597
516
|
.action((feature, options) => envCommand(feature, options));
|
|
598
517
|
|
|
599
|
-
// ─── Fleet ────────────────────────────────────────────────────────────────
|
|
600
518
|
program
|
|
601
519
|
.command('fleet')
|
|
602
520
|
.description('Fleet view — every active feature across primary root and worktrees (phase, gates, tasks, ports, handoff, activity)')
|
|
603
521
|
.option('--json', 'Output as JSON')
|
|
604
522
|
.action((options) => fleetCommand(options));
|
|
605
523
|
|
|
606
|
-
// ─── Claims (advisory feature ownership) ──────────────────────────────────
|
|
607
524
|
program
|
|
608
525
|
.command('claim [feature]')
|
|
609
526
|
.description('claim [feature] acquires it (moves this tree\'s previous claim in the same step); no args shows the board (claims are advisory warnings, never locks)')
|
|
@@ -613,23 +530,22 @@ program
|
|
|
613
530
|
.option('--json', 'Output as JSON')
|
|
614
531
|
.action((feature, options) => claimCommand(feature, options));
|
|
615
532
|
|
|
616
|
-
// ─── Standards search (server-side, R2 token cut) ─────────────────────────
|
|
617
533
|
program
|
|
618
534
|
.command('standards')
|
|
619
|
-
.description('Search STANDARDS.json server-side (top 3,
|
|
535
|
+
.description('Search STANDARDS.json server-side (top 3, digest + anchors) or print one section, instead of reading the whole registry. Bare: categories + counts')
|
|
620
536
|
.option('--find <query>', 'Search query — matches name/id, tags/aliases, category, digest')
|
|
537
|
+
.option('--show <target>', '<id>: digest + anchors; <id>#<anchor>: only that section of the standard .md')
|
|
621
538
|
.option('--category <category>', 'Restrict --find to a single category')
|
|
622
539
|
.option('--tags <tags>', 'Restrict --find to standards carrying any of these comma-separated tags')
|
|
623
540
|
.option('--json', 'Output as JSON')
|
|
624
541
|
.action((options) => standardsCommand(options));
|
|
625
542
|
|
|
626
|
-
// ─── Sub-agent report protocol ────────────────────────────────────────────
|
|
627
543
|
program
|
|
628
|
-
.command('report-check <feature> [
|
|
629
|
-
.description('Audit
|
|
630
|
-
.option('--before-eval', 'Save a snapshot of the tree before dispatching the
|
|
544
|
+
.command('report-check <feature> [slices...]')
|
|
545
|
+
.description('Audit slice report files (3-implement/reports/{slice}.md) against the work the dispatch actually wrote (telemetry, keyed by the agent that wrote the report). Exits 0 only when every audited slice is intact; naming slices audits only those')
|
|
546
|
+
.option('--before-eval', 'Save a snapshot of the tree before dispatching the review agents and print their models; later runs report what changed since')
|
|
631
547
|
.option('--json', 'Output as JSON')
|
|
632
|
-
.action((feature,
|
|
548
|
+
.action((feature, slices, options) => reportCheckCommand(feature, slices, options));
|
|
633
549
|
|
|
634
550
|
program
|
|
635
551
|
.command('scout-check <map>')
|
|
@@ -638,7 +554,20 @@ program
|
|
|
638
554
|
.option('--json', 'Output as JSON')
|
|
639
555
|
.action((map, options) => scoutCheckCommand(map, options));
|
|
640
556
|
|
|
641
|
-
|
|
557
|
+
program
|
|
558
|
+
.command('plan-check <feature>')
|
|
559
|
+
.description('Parse 2-plan/plan.md (scenarios, slices, decisions, reuse) and, with no error, write 2-plan/checks.json — the only writer of checks.json. Refuses to rewrite it once the plan gate is approved and the plan changed outside ## Errata. Exits 1 on errors, 2 when plan.md is missing')
|
|
560
|
+
.option('--json', 'Output as JSON')
|
|
561
|
+
.addHelpText('after', () => '\nGramática (framework/templates/docs/plan.md):\n\n' +
|
|
562
|
+
readFileSync(join(__dirname, '..', 'framework', 'templates', 'docs', 'plan.md'), 'utf8').replace(/\{\{FEATURE_NAME\}\}/g, '<feature>'))
|
|
563
|
+
.action((feature, options) => planCheckCommand(feature, options));
|
|
564
|
+
|
|
565
|
+
program
|
|
566
|
+
.command('stack-scan [feature]')
|
|
567
|
+
.description('Audit the code against config.project.stack, judging only the configured stacks (runtime, ORM, database, Guid.NewGuid, generic repository, service layer, raw SQL, sealed, router, UI library) by severity. With a feature, write .morph/features/<f>/2-plan/stack-scan.md; without, print the report')
|
|
568
|
+
.option('--json', 'Output as JSON')
|
|
569
|
+
.action((feature, options) => stackScanCommand(feature, options));
|
|
570
|
+
|
|
642
571
|
program
|
|
643
572
|
.command('dashboard')
|
|
644
573
|
.description('Open the morph-spec inspector dashboard')
|
|
@@ -646,7 +575,6 @@ program
|
|
|
646
575
|
.option('--no-open', 'Don\'t auto-open browser')
|
|
647
576
|
.action(dashboardCommand);
|
|
648
577
|
|
|
649
|
-
// ─── Observability ────────────────────────────────────────────────────────
|
|
650
578
|
program
|
|
651
579
|
.command('telemetry')
|
|
652
580
|
.description('Summarize the harness execution path from .morph/logs/events.jsonl (skills, personas, standards read, tool mix, prompts)')
|
|
@@ -657,10 +585,10 @@ program
|
|
|
657
585
|
|
|
658
586
|
program
|
|
659
587
|
.command('cost [feature]')
|
|
660
|
-
.description('Cost-facing view over .morph/logs/events.jsonl:
|
|
661
|
-
.option('--by <mode>', 'Group by:
|
|
588
|
+
.description('Cost-facing view over .morph/logs/events.jsonl: bytes re-ingested by kind (proxy, not tokens), dispatch counts, real tokens when observed')
|
|
589
|
+
.option('--by <mode>', 'Group by: phase (default: feature-wide summary; with --all-trees, applies to the combined aggregate)')
|
|
662
590
|
.option('--all-trees', 'Aggregate across every git worktree with a log plus the logs finish saved in .git/morph-spec/finish-backup — per-tree breakdown + combined total')
|
|
663
|
-
.option('--metrics', 'Loop measurements: turns, tool calls (orchestrator x sub-agent), time per command class, identical reruns, rereads,
|
|
591
|
+
.option('--metrics', 'Loop measurements: turns, tool calls (orchestrator x sub-agent), time per command class, identical reruns, rereads, verify count per feature, git calls, reuse validator per tree')
|
|
664
592
|
.option('--top <n>', 'With --metrics: how many reread files to list (default 10)', (v) => Number.parseInt(v, 10))
|
|
665
593
|
.option('--json', 'Output the aggregate as JSON')
|
|
666
594
|
.action((feature, options) => costCommand(feature, options));
|
package/bin/validate.js
CHANGED
|
@@ -1,32 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Validation CLI
|
|
5
|
-
*
|
|
6
|
-
* Runs validators for the current project stack (detected from .morph/config/config.json).
|
|
7
|
-
*
|
|
8
|
-
* .NET stacks: packages, spec-tasks
|
|
9
|
-
* Next.js stack: next-components, spec-tasks
|
|
10
|
-
* Other stacks: spec-tasks
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* morph-spec validate [options]
|
|
14
|
-
* morph-spec validate all
|
|
15
|
-
* morph-spec validate next-components (Next.js projects)
|
|
16
|
-
* morph-spec validate packages (.NET projects)
|
|
17
|
-
*
|
|
18
|
-
* MORPH-SPEC 3.0
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
3
|
import chalk from 'chalk';
|
|
22
4
|
import { getStackProfile } from '../src/lib/stack/stack-profile.js';
|
|
23
5
|
|
|
24
|
-
/**
|
|
25
|
-
* Build the VALIDATORS registry for the current stack.
|
|
26
|
-
* .NET stacks → packages, spec-tasks
|
|
27
|
-
* nextjs stack → next-components, spec-tasks
|
|
28
|
-
* other stacks → spec-tasks only
|
|
29
|
-
*/
|
|
30
6
|
async function buildValidators(stack) {
|
|
31
7
|
const validators = {};
|
|
32
8
|
|
|
@@ -52,114 +28,9 @@ async function buildValidators(stack) {
|
|
|
52
28
|
};
|
|
53
29
|
}
|
|
54
30
|
|
|
55
|
-
validators['spec-tasks'] = {
|
|
56
|
-
name: 'Spec vs Tasks Coverage',
|
|
57
|
-
description: 'Validates that tasks.json covers all requirements from spec.md',
|
|
58
|
-
run: validateSpecVsTasks
|
|
59
|
-
};
|
|
60
|
-
|
|
61
31
|
return validators;
|
|
62
32
|
}
|
|
63
33
|
|
|
64
|
-
/**
|
|
65
|
-
* Validate spec.md requirements are covered by tasks
|
|
66
|
-
*/
|
|
67
|
-
async function validateSpecVsTasks(projectPath, options = {}) {
|
|
68
|
-
const path = await import('path');
|
|
69
|
-
const fs = await import('fs');
|
|
70
|
-
const { loadState } = await import('../src/core/state/state-manager.js');
|
|
71
|
-
|
|
72
|
-
const results = [];
|
|
73
|
-
let errors = 0;
|
|
74
|
-
let warnings = 0;
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const state = loadState(false);
|
|
78
|
-
if (!state || !state.features) {
|
|
79
|
-
return { status: 'ok', errors: 0, warnings: 0, message: 'No features to validate' };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const [featureName, feature] of Object.entries(state.features)) {
|
|
83
|
-
const outputsPath = path.join(projectPath, '.morph/features', featureName);
|
|
84
|
-
const specPath = path.join(outputsPath, 'spec.md');
|
|
85
|
-
|
|
86
|
-
if (!fs.existsSync(specPath)) {
|
|
87
|
-
continue; // No spec to validate
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Read spec.md and extract requirements
|
|
91
|
-
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
92
|
-
|
|
93
|
-
// Extract requirement patterns (## Requisitos, - [ ], numbered lists, etc.)
|
|
94
|
-
const requirementPatterns = [
|
|
95
|
-
/^#+\s*Requisitos?\s*$/gim,
|
|
96
|
-
/^-\s*\[.\]\s*(.+)$/gm,
|
|
97
|
-
/^\d+\.\s+(.+)$/gm
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
const requirements = [];
|
|
101
|
-
for (const pattern of requirementPatterns) {
|
|
102
|
-
const matches = specContent.matchAll(pattern);
|
|
103
|
-
for (const match of matches) {
|
|
104
|
-
if (match[1]) {
|
|
105
|
-
requirements.push(match[1].trim());
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Check if feature has tasks
|
|
111
|
-
if (!feature.tasks || (Array.isArray(feature.tasks) && feature.tasks.length === 0)) {
|
|
112
|
-
if (requirements.length > 0) {
|
|
113
|
-
warnings++;
|
|
114
|
-
results.push({
|
|
115
|
-
feature: featureName,
|
|
116
|
-
level: 'warning',
|
|
117
|
-
message: `Spec has ${requirements.length} requirements but no tasks defined`
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Get task titles
|
|
124
|
-
const taskTitles = Array.isArray(feature.tasks)
|
|
125
|
-
? feature.tasks.map(t => t.title?.toLowerCase() || '')
|
|
126
|
-
: [];
|
|
127
|
-
|
|
128
|
-
// Check each requirement has a matching task (fuzzy match)
|
|
129
|
-
for (const req of requirements.slice(0, 20)) { // Limit to first 20
|
|
130
|
-
const reqLower = req.toLowerCase();
|
|
131
|
-
const hasMatch = taskTitles.some(title =>
|
|
132
|
-
title.includes(reqLower.slice(0, 20)) ||
|
|
133
|
-
reqLower.includes(title.slice(0, 20))
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (!hasMatch && options.verbose) {
|
|
137
|
-
console.log(chalk.gray(` ? Requirement may not be covered: "${req.slice(0, 50)}..."`));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (options.verbose) {
|
|
142
|
-
console.log(chalk.green(` ✓ ${featureName}: ${requirements.length} requirements, ${taskTitles.length} tasks`));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
} catch (error) {
|
|
147
|
-
if (options.verbose) {
|
|
148
|
-
console.log(chalk.yellow(` ⚠ Could not validate: ${error.message}`));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
status: errors > 0 ? 'error' : warnings > 0 ? 'warning' : 'ok',
|
|
154
|
-
errors,
|
|
155
|
-
warnings,
|
|
156
|
-
results
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Main validation command
|
|
162
|
-
*/
|
|
163
34
|
export async function validateCommand(args = []) {
|
|
164
35
|
const validatorName = args[0] || 'all';
|
|
165
36
|
const options = parseOptions(args);
|
|
@@ -187,9 +58,6 @@ export async function validateCommand(args = []) {
|
|
|
187
58
|
}
|
|
188
59
|
}
|
|
189
60
|
|
|
190
|
-
/**
|
|
191
|
-
* Run all validators
|
|
192
|
-
*/
|
|
193
61
|
async function runAllValidators(VALIDATORS, options) {
|
|
194
62
|
const results = {};
|
|
195
63
|
let totalErrors = 0;
|
|
@@ -209,12 +77,10 @@ async function runAllValidators(VALIDATORS, options) {
|
|
|
209
77
|
|
|
210
78
|
results[name] = result;
|
|
211
79
|
|
|
212
|
-
// Count errors/warnings
|
|
213
80
|
if (result.errors !== undefined) {
|
|
214
81
|
totalErrors += result.errors;
|
|
215
82
|
totalWarnings += result.warnings || 0;
|
|
216
83
|
} else if (result.results) {
|
|
217
|
-
// Count issues in results array
|
|
218
84
|
const issues = result.results.flatMap(r => r.issues || []);
|
|
219
85
|
totalErrors += issues.filter(i => i.level === 'error').length;
|
|
220
86
|
totalWarnings += issues.filter(i => i.level === 'warning').length;
|
|
@@ -226,7 +92,6 @@ async function runAllValidators(VALIDATORS, options) {
|
|
|
226
92
|
}
|
|
227
93
|
}
|
|
228
94
|
|
|
229
|
-
// Summary
|
|
230
95
|
console.log(chalk.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
231
96
|
console.log(chalk.bold('📊 Validation Summary\n'));
|
|
232
97
|
|
|
@@ -243,15 +108,11 @@ async function runAllValidators(VALIDATORS, options) {
|
|
|
243
108
|
|
|
244
109
|
console.log('');
|
|
245
110
|
|
|
246
|
-
// Exit with error code if errors found
|
|
247
111
|
if (totalErrors > 0 && !options.noFail) {
|
|
248
112
|
process.exit(1);
|
|
249
113
|
}
|
|
250
114
|
}
|
|
251
115
|
|
|
252
|
-
/**
|
|
253
|
-
* Run single validator
|
|
254
|
-
*/
|
|
255
116
|
async function runValidator(VALIDATORS, name, options) {
|
|
256
117
|
const validator = VALIDATORS[name];
|
|
257
118
|
|
|
@@ -260,7 +121,7 @@ async function runValidator(VALIDATORS, name, options) {
|
|
|
260
121
|
|
|
261
122
|
try {
|
|
262
123
|
const result = await validator.run('.', {
|
|
263
|
-
verbose: true,
|
|
124
|
+
verbose: true,
|
|
264
125
|
autoFix: options.autoFix
|
|
265
126
|
});
|
|
266
127
|
|
|
@@ -282,9 +143,6 @@ async function runValidator(VALIDATORS, name, options) {
|
|
|
282
143
|
}
|
|
283
144
|
}
|
|
284
145
|
|
|
285
|
-
/**
|
|
286
|
-
* Parse command-line options
|
|
287
|
-
*/
|
|
288
146
|
function parseOptions(args) {
|
|
289
147
|
const options = {
|
|
290
148
|
verbose: false,
|
|
@@ -305,9 +163,6 @@ function parseOptions(args) {
|
|
|
305
163
|
return options;
|
|
306
164
|
}
|
|
307
165
|
|
|
308
|
-
/**
|
|
309
|
-
* Show help
|
|
310
|
-
*/
|
|
311
166
|
export function showValidateHelp() {
|
|
312
167
|
console.log(`
|
|
313
168
|
${chalk.bold.cyan('morph-spec validate')} - Run project validations
|
|
@@ -342,7 +197,6 @@ ${chalk.bold('INTEGRATION')}
|
|
|
342
197
|
`);
|
|
343
198
|
}
|
|
344
199
|
|
|
345
|
-
// Run if called directly
|
|
346
200
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
347
201
|
const args = process.argv.slice(2);
|
|
348
202
|
|
package/claude-plugin.json
CHANGED