@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,36 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec standards --find "<query>" [--category <c>] [--tags <a,b>] [--json]`
|
|
3
|
-
*
|
|
4
|
-
* Server-side replacement for "read the whole STANDARDS.json to pick 1-3
|
|
5
|
-
* standards" (R2 of the performance plan — token cut per task). Today
|
|
6
|
-
* morph-implement loads the full 65 KB / 60-entry registry (with a `digest`
|
|
7
|
-
* per entry) just to choose a couple of relevant standards: ~16k tokens per
|
|
8
|
-
* task. This command searches server-side and returns only the TOP 3 hits —
|
|
9
|
-
* `id · path · digest` — where the digest is the FULL text: it is what
|
|
10
|
-
* substitutes for actually reading the standard's .md file.
|
|
11
|
-
*
|
|
12
|
-
* Without `--find` (a bare call): lists categories + counts — a cheap
|
|
13
|
-
* orientation view, no scoring.
|
|
14
|
-
*
|
|
15
|
-
* A query with no hit exits 0 (this is advisory tooling, not a gate) with a
|
|
16
|
-
* human-readable warning + category suggestions on stderr, and — under
|
|
17
|
-
* `--json` — a stable `{ query, results: [], suggestion }` shape on stdout so
|
|
18
|
-
* a JSON consumer never has to special-case "no results" as a parse error.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
1
|
import { existsSync, readFileSync } from 'fs';
|
|
22
|
-
import { join } from 'path';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
23
3
|
import chalk from 'chalk';
|
|
4
|
+
import { loadStandardsRegistry } from '../../lib/standards/registry.js';
|
|
5
|
+
import { markdownHeadings, sectionBySlug } from '../../lib/standards/sections.js';
|
|
24
6
|
|
|
25
|
-
/**
|
|
26
|
-
* Resolve STANDARDS.json the same way doctor.js resolves the standards root:
|
|
27
|
-
* an installed project first (`.morph/framework/standards/`), falling back
|
|
28
|
-
* to this package's own bundled copy (`framework/standards/`) — the framework
|
|
29
|
-
* repo in dev, or the npm package's own copy before `setup-infra` has run.
|
|
30
|
-
*
|
|
31
|
-
* @param {string} targetPath - project root to look for `.morph/` under
|
|
32
|
-
* @returns {string|null} absolute path to STANDARDS.json, or null if neither exists
|
|
33
|
-
*/
|
|
7
|
+
/** Installed project first, else this package's bundled copy (dev repo, or before `setup-infra` ran). */
|
|
34
8
|
export function resolveStandardsJsonPath(targetPath) {
|
|
35
9
|
const installed = join(targetPath, '.morph', 'framework', 'standards', 'STANDARDS.json');
|
|
36
10
|
if (existsSync(installed)) return installed;
|
|
@@ -41,17 +15,62 @@ export function resolveStandardsJsonPath(targetPath) {
|
|
|
41
15
|
return null;
|
|
42
16
|
}
|
|
43
17
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
18
|
+
export function resolveStandardSection(standard, mdText, anchor) {
|
|
19
|
+
if (mdText === null) return { status: 'unreadable' };
|
|
20
|
+
const anchors = standard.anchors || {};
|
|
21
|
+
const slug = String(anchors[anchor] ?? anchor).replace(/^#/, '');
|
|
22
|
+
const section = sectionBySlug(mdText, slug);
|
|
23
|
+
if (section) return { status: 'ok', section, slug };
|
|
24
|
+
return { status: 'unknown-anchor', anchors, headings: markdownHeadings(mdText).map((h) => h.slug) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readStandardMd(standardsJsonPath, standard) {
|
|
28
|
+
try {
|
|
29
|
+
return readFileSync(join(dirname(standardsJsonPath), standard.path), 'utf8');
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function showStandard(standards, standardsJsonPath, target, json) {
|
|
36
|
+
const hash = String(target).indexOf('#');
|
|
37
|
+
const id = hash === -1 ? String(target) : String(target).slice(0, hash);
|
|
38
|
+
const anchor = hash === -1 ? '' : String(target).slice(hash + 1).replace(/^#+/, '');
|
|
39
|
+
const standard = standards.find((s) => s.id === id);
|
|
40
|
+
if (!standard) {
|
|
41
|
+
const near = findStandards(standards, id.replace(/-/g, ' ')).map((s) => s.id);
|
|
42
|
+
if (json) console.log(JSON.stringify({ id, error: 'unknown-id', suggestions: near }));
|
|
43
|
+
else console.error(chalk.red(`\n❌ Standard "${id}" não existe.`) + (near.length ? chalk.gray(` Talvez: ${near.join(', ')}`) : '') + '\n');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const anchors = standard.anchors || {};
|
|
47
|
+
|
|
48
|
+
if (!anchor) {
|
|
49
|
+
if (json) {
|
|
50
|
+
console.log(JSON.stringify({ id: standard.id, path: standard.path, digest: standard.digest, anchors }));
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
console.log(chalk.cyan(`\n${standard.id}`) + chalk.gray(` (${standard.path})`));
|
|
54
|
+
console.log(`\n${standard.digest || '(sem digest)'}\n`);
|
|
55
|
+
console.log(chalk.bold('Âncoras') + chalk.gray(` — morph-spec standards --show ${standard.id}#<âncora>`));
|
|
56
|
+
for (const [key, slug] of Object.entries(anchors)) console.log(` ${key.padEnd(16)}${slug}`);
|
|
57
|
+
console.log('');
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result = resolveStandardSection(standard, readStandardMd(standardsJsonPath, standard), anchor);
|
|
62
|
+
if (result.status === 'ok') {
|
|
63
|
+
if (json) console.log(JSON.stringify({ id: standard.id, anchor, slug: result.slug, section: result.section }));
|
|
64
|
+
else process.stdout.write(result.section);
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
if (json) console.log(JSON.stringify({ id: standard.id, anchor, error: result.status, anchors }));
|
|
68
|
+
else if (result.status === 'unreadable') console.error(chalk.red(`\n❌ ${standard.path} não pôde ser lido.\n`));
|
|
69
|
+
else console.error(chalk.red(`\n❌ "${anchor}" não é âncora nem heading de ${standard.id}.`)
|
|
70
|
+
+ chalk.gray(` Âncoras: ${Object.keys(anchors).join(', ') || '(nenhuma)'}\n`));
|
|
71
|
+
process.exit(1);
|
|
48
72
|
}
|
|
49
73
|
|
|
50
|
-
/**
|
|
51
|
-
* category → count, sorted by count desc then name asc.
|
|
52
|
-
* @param {object[]} standards
|
|
53
|
-
* @returns {{ category: string, count: number }[]}
|
|
54
|
-
*/
|
|
55
74
|
export function categoryCounts(standards) {
|
|
56
75
|
const counts = {};
|
|
57
76
|
for (const std of standards) {
|
|
@@ -63,16 +82,6 @@ export function categoryCounts(standards) {
|
|
|
63
82
|
.sort((a, b) => b.count - a.count || a.category.localeCompare(b.category));
|
|
64
83
|
}
|
|
65
84
|
|
|
66
|
-
/**
|
|
67
|
-
* Simple weighted relevance score for one standard against a query.
|
|
68
|
-
* Tiers, as specified: name/id > tags(+aliases) > digest. category/subcategory
|
|
69
|
-
* ride alongside the tags tier (lighter). A token-level fallback keeps
|
|
70
|
-
* multi-word queries findable even without a verbatim phrase match anywhere.
|
|
71
|
-
*
|
|
72
|
-
* @param {object} std - one STANDARDS.json entry
|
|
73
|
-
* @param {string} query - raw user query (already trimmed, non-empty)
|
|
74
|
-
* @returns {number} score, 0 = no match
|
|
75
|
-
*/
|
|
76
85
|
export function scoreStandard(std, query) {
|
|
77
86
|
const q = query.toLowerCase();
|
|
78
87
|
const tokens = q.split(/\s+/).filter((t) => t.length >= 3);
|
|
@@ -87,19 +96,15 @@ export function scoreStandard(std, query) {
|
|
|
87
96
|
|
|
88
97
|
let score = 0;
|
|
89
98
|
|
|
90
|
-
// Tier 1: name / id — the strongest signal.
|
|
91
99
|
if (id === q || name === q) score += 20;
|
|
92
100
|
else if (id.includes(q) || name.includes(q)) score += 12;
|
|
93
101
|
|
|
94
|
-
// Tier 2: tags / aliases (curated search terms) + category/subcategory.
|
|
95
102
|
if (tags.includes(q) || aliases.includes(q)) score += 8;
|
|
96
103
|
else if (tags.some((t) => t.includes(q)) || aliases.some((a) => a.includes(q))) score += 5;
|
|
97
104
|
if (category === q || subcategory === q) score += 4;
|
|
98
105
|
|
|
99
|
-
// Tier 3: digest — weakest signal, full-phrase substring only.
|
|
100
106
|
if (digest.includes(q)) score += 2;
|
|
101
107
|
|
|
102
|
-
// Token-level fallback for multi-word queries with no verbatim match above.
|
|
103
108
|
for (const tok of tokens) {
|
|
104
109
|
if (name.includes(tok) || id.includes(tok)) score += 2;
|
|
105
110
|
if (tags.some((t) => t.includes(tok)) || aliases.some((a) => a.includes(tok))) score += 1;
|
|
@@ -109,15 +114,6 @@ export function scoreStandard(std, query) {
|
|
|
109
114
|
return score;
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
/**
|
|
113
|
-
* Rank standards against a query, applying optional category/tags filters
|
|
114
|
-
* first. Returns the TOP 3 with score > 0, sorted desc (ties broken by id).
|
|
115
|
-
*
|
|
116
|
-
* @param {object[]} standards
|
|
117
|
-
* @param {string} query
|
|
118
|
-
* @param {{ category?: string, tags?: string[] }} [filters]
|
|
119
|
-
* @returns {Array<object & { score: number }>}
|
|
120
|
-
*/
|
|
121
117
|
export function findStandards(standards, query, filters = {}) {
|
|
122
118
|
const category = filters.category?.toLowerCase();
|
|
123
119
|
const tags = (filters.tags || []).map((t) => t.toLowerCase());
|
|
@@ -138,15 +134,11 @@ export function findStandards(standards, query, filters = {}) {
|
|
|
138
134
|
.slice(0, 3);
|
|
139
135
|
}
|
|
140
136
|
|
|
141
|
-
/** Parse `--tags a,b,c` into a trimmed, non-empty array. */
|
|
142
137
|
function parseTagsOption(raw) {
|
|
143
138
|
if (!raw) return [];
|
|
144
139
|
return String(raw).split(',').map((t) => t.trim()).filter(Boolean);
|
|
145
140
|
}
|
|
146
141
|
|
|
147
|
-
/**
|
|
148
|
-
* @param {{ find?: string, category?: string, tags?: string, json?: boolean, cwd?: string }} [options]
|
|
149
|
-
*/
|
|
150
142
|
export function standardsCommand(options = {}) {
|
|
151
143
|
const cwd = options.cwd || process.cwd();
|
|
152
144
|
const standardsJsonPath = resolveStandardsJsonPath(cwd);
|
|
@@ -159,16 +151,16 @@ export function standardsCommand(options = {}) {
|
|
|
159
151
|
|
|
160
152
|
let registry;
|
|
161
153
|
try {
|
|
162
|
-
registry =
|
|
154
|
+
registry = loadStandardsRegistry(standardsJsonPath);
|
|
163
155
|
} catch (err) {
|
|
164
156
|
console.error(chalk.red(`\n❌ STANDARDS.json inválido em ${standardsJsonPath}: ${err.message}\n`));
|
|
165
157
|
process.exit(1);
|
|
166
158
|
}
|
|
167
159
|
|
|
168
|
-
const standards =
|
|
160
|
+
const standards = registry.standards;
|
|
161
|
+
if (options.show) showStandard(standards, standardsJsonPath, options.show, options.json);
|
|
169
162
|
const query = (options.find || '').trim();
|
|
170
163
|
|
|
171
|
-
// ── Bare call (no --find): categories + counts, cheap orientation ────────
|
|
172
164
|
if (!query) {
|
|
173
165
|
const counts = categoryCounts(standards);
|
|
174
166
|
if (options.json) {
|
|
@@ -180,11 +172,10 @@ export function standardsCommand(options = {}) {
|
|
|
180
172
|
for (const { category, count } of counts) {
|
|
181
173
|
console.log(` ${col(category, 20)}${count}`);
|
|
182
174
|
}
|
|
183
|
-
console.log(chalk.gray('\nUse --find "<query>" para buscar (top 3, com digest
|
|
175
|
+
console.log(chalk.gray('\nUse --find "<query>" para buscar (top 3, com digest e âncoras) e --show <id>#<âncora> para ler uma seção.\n'));
|
|
184
176
|
process.exit(0);
|
|
185
177
|
}
|
|
186
178
|
|
|
187
|
-
// ── --find: score + filter ────────────────────────────────────────────
|
|
188
179
|
const filters = { category: options.category, tags: parseTagsOption(options.tags) };
|
|
189
180
|
const results = findStandards(standards, query, filters);
|
|
190
181
|
|
|
@@ -201,7 +192,8 @@ export function standardsCommand(options = {}) {
|
|
|
201
192
|
}
|
|
202
193
|
|
|
203
194
|
const shaped = results.map((r) => ({
|
|
204
|
-
id: r.id, name: r.name, path: r.path, category: r.category, digest: r.digest,
|
|
195
|
+
id: r.id, name: r.name, path: r.path, category: r.category, digest: r.digest,
|
|
196
|
+
anchors: Object.keys(r.anchors || {}), score: r.score,
|
|
205
197
|
}));
|
|
206
198
|
|
|
207
199
|
if (options.json) {
|
|
@@ -214,6 +206,7 @@ export function standardsCommand(options = {}) {
|
|
|
214
206
|
console.log(chalk.cyan(`${i + 1}. ${r.id}`) + chalk.gray(` (${r.category})`));
|
|
215
207
|
console.log(` ${r.path}`);
|
|
216
208
|
console.log(chalk.gray(` ${r.digest}`));
|
|
209
|
+
if (r.anchors.length) console.log(` âncoras: ${r.anchors.join(', ')}` + chalk.gray(` → morph-spec standards --show ${r.id}#<âncora>`));
|
|
217
210
|
console.log('');
|
|
218
211
|
});
|
|
219
212
|
process.exit(0);
|
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MORPH-SPEC Feature Status Command
|
|
3
|
-
*
|
|
4
|
-
* Displays a comprehensive dashboard showing feature progress, phase status,
|
|
5
|
-
* outputs, tasks, approvals, and next steps.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* morph-spec status <feature-name>
|
|
9
|
-
* morph-spec status <feature-name> --json
|
|
10
|
-
*
|
|
11
|
-
* Example:
|
|
12
|
-
* morph-spec status user-portal
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
1
|
import chalk from 'chalk';
|
|
16
2
|
import { loadState, getFeature, derivePhase, deriveOutputs } from '../../core/state/state-manager.js';
|
|
3
|
+
import { sliceProgress } from '../../../framework/hooks/shared/slice-progress.js';
|
|
17
4
|
|
|
18
|
-
/**
|
|
19
|
-
* Phase symbols for visual representation
|
|
20
|
-
*/
|
|
21
5
|
const PHASE_SYMBOLS = {
|
|
22
6
|
completed: '✅',
|
|
23
7
|
current: '⏸️',
|
|
@@ -25,29 +9,27 @@ const PHASE_SYMBOLS = {
|
|
|
25
9
|
skipped: '⊘'
|
|
26
10
|
};
|
|
27
11
|
|
|
28
|
-
/**
|
|
29
|
-
* Phase definitions with order
|
|
30
|
-
*/
|
|
31
12
|
const PHASE_ORDER = [
|
|
32
|
-
{ key: '
|
|
33
|
-
{ key: 'uiux', name: 'FASE
|
|
34
|
-
{ key: '
|
|
35
|
-
{ key: '
|
|
36
|
-
{ key: 'review', name: 'FASE 4: REVIEW', order: 4 }
|
|
13
|
+
{ key: 'plan', name: 'FASE 1: PLAN', order: 0 },
|
|
14
|
+
{ key: 'uiux', name: 'FASE 2: UI/UX', order: 1, optional: true },
|
|
15
|
+
{ key: 'implement', name: 'FASE 3: IMPLEMENT', order: 2 },
|
|
16
|
+
{ key: 'review', name: 'FASE 4: REVIEW', order: 3 }
|
|
37
17
|
];
|
|
38
18
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
const PHASE_OUTPUTS = {
|
|
20
|
+
plan: ['plan', 'checks'],
|
|
21
|
+
uiux: ['uiDesignSystem', 'uiMockups', 'uiComponents', 'uiFlows'],
|
|
22
|
+
implement: [],
|
|
23
|
+
review: ['verifierReport', 'reviewerReport']
|
|
24
|
+
};
|
|
25
|
+
|
|
42
26
|
function getPhaseStatus(phaseKey, currentPhase, feature) {
|
|
43
27
|
const phase = PHASE_ORDER.find(p => p.key === phaseKey);
|
|
44
28
|
const current = PHASE_ORDER.find(p => p.key === currentPhase);
|
|
45
29
|
|
|
46
30
|
if (!phase || !current) return 'pending';
|
|
47
31
|
|
|
48
|
-
// Check if phase was skipped (optional phases)
|
|
49
32
|
if (phase.optional && phase.order < current.order) {
|
|
50
|
-
// Check if any outputs were created for this phase
|
|
51
33
|
const hasOutputs = checkPhaseOutputs(phaseKey, feature);
|
|
52
34
|
if (!hasOutputs) return 'skipped';
|
|
53
35
|
}
|
|
@@ -57,25 +39,11 @@ function getPhaseStatus(phaseKey, currentPhase, feature) {
|
|
|
57
39
|
return 'pending';
|
|
58
40
|
}
|
|
59
41
|
|
|
60
|
-
/**
|
|
61
|
-
* Check if phase has any outputs created
|
|
62
|
-
*/
|
|
63
42
|
function checkPhaseOutputs(phaseKey, feature) {
|
|
64
|
-
const
|
|
65
|
-
proposal: ['proposal', 'stackScan'],
|
|
66
|
-
uiux: ['uiDesignSystem', 'uiMockups', 'uiComponents', 'uiFlows'],
|
|
67
|
-
plan: ['spec', 'tasks', 'decisions'],
|
|
68
|
-
implement: ['recap'],
|
|
69
|
-
review: ['reviewReport']
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const outputs = phaseOutputMap[phaseKey] || [];
|
|
43
|
+
const outputs = PHASE_OUTPUTS[phaseKey] || [];
|
|
73
44
|
return outputs.some(output => feature.outputs[output]?.created);
|
|
74
45
|
}
|
|
75
46
|
|
|
76
|
-
/**
|
|
77
|
-
* Format phase display with symbol and status
|
|
78
|
-
*/
|
|
79
47
|
function formatPhase(phaseKey, currentPhase, feature, options = {}) {
|
|
80
48
|
const phase = PHASE_ORDER.find(p => p.key === phaseKey);
|
|
81
49
|
const status = getPhaseStatus(phaseKey, currentPhase, feature);
|
|
@@ -97,17 +65,8 @@ function formatPhase(phaseKey, currentPhase, feature, options = {}) {
|
|
|
97
65
|
display = chalk.gray(display);
|
|
98
66
|
}
|
|
99
67
|
|
|
100
|
-
// Add output files if verbose
|
|
101
68
|
if (options.verbose && status !== 'pending' && status !== 'skipped') {
|
|
102
|
-
const
|
|
103
|
-
proposal: ['proposal', 'stackScan'],
|
|
104
|
-
uiux: ['uiDesignSystem', 'uiMockups', 'uiComponents', 'uiFlows'],
|
|
105
|
-
plan: ['spec', 'tasks', 'decisions'],
|
|
106
|
-
implement: ['recap'],
|
|
107
|
-
review: ['reviewReport']
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const outputs = phaseOutputMap[phaseKey] || [];
|
|
69
|
+
const outputs = PHASE_OUTPUTS[phaseKey] || [];
|
|
111
70
|
const created = outputs.filter(o => feature.outputs[o]?.created);
|
|
112
71
|
if (created.length > 0) {
|
|
113
72
|
display += chalk.gray(`\n Files: ${created.length}/${outputs.length}`);
|
|
@@ -117,11 +76,8 @@ function formatPhase(phaseKey, currentPhase, feature, options = {}) {
|
|
|
117
76
|
return display;
|
|
118
77
|
}
|
|
119
78
|
|
|
120
|
-
/**
|
|
121
|
-
* Calculate completion percentage
|
|
122
|
-
*/
|
|
123
79
|
function calculateCompletionPercentage(feature) {
|
|
124
|
-
const totalPhases = PHASE_ORDER.filter(p => !p.optional).length;
|
|
80
|
+
const totalPhases = PHASE_ORDER.filter(p => !p.optional).length;
|
|
125
81
|
const completedPhases = PHASE_ORDER.filter(p => {
|
|
126
82
|
if (p.optional) return false;
|
|
127
83
|
const status = getPhaseStatus(p.key, feature.phase, feature);
|
|
@@ -131,17 +87,12 @@ function calculateCompletionPercentage(feature) {
|
|
|
131
87
|
return Math.round((completedPhases / totalPhases) * 100);
|
|
132
88
|
}
|
|
133
89
|
|
|
134
|
-
/**
|
|
135
|
-
* Get next steps based on current phase
|
|
136
|
-
*/
|
|
137
90
|
function getNextSteps(feature) {
|
|
138
91
|
const steps = [];
|
|
139
92
|
|
|
140
|
-
// Check for pending approvals relevant to current phase
|
|
141
93
|
const phaseApprovalMap = {
|
|
142
|
-
proposal: 'proposal',
|
|
143
|
-
uiux: 'uiux',
|
|
144
94
|
plan: 'plan',
|
|
95
|
+
uiux: 'uiux',
|
|
145
96
|
implement: 'plan',
|
|
146
97
|
review: 'review'
|
|
147
98
|
};
|
|
@@ -157,15 +108,7 @@ function getNextSteps(feature) {
|
|
|
157
108
|
});
|
|
158
109
|
}
|
|
159
110
|
|
|
160
|
-
|
|
161
|
-
const phaseOutputMap = {
|
|
162
|
-
proposal: ['proposal'],
|
|
163
|
-
plan: ['spec', 'tasks'],
|
|
164
|
-
implement: ['recap'],
|
|
165
|
-
review: ['reviewReport']
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const requiredOutputs = phaseOutputMap[feature.phase] || [];
|
|
111
|
+
const requiredOutputs = feature.phase === 'uiux' ? [] : PHASE_OUTPUTS[feature.phase] || [];
|
|
169
112
|
const missingOutputs = requiredOutputs.filter(o => !feature.outputs[o]?.created);
|
|
170
113
|
|
|
171
114
|
if (missingOutputs.length > 0) {
|
|
@@ -176,7 +119,6 @@ function getNextSteps(feature) {
|
|
|
176
119
|
});
|
|
177
120
|
}
|
|
178
121
|
|
|
179
|
-
// Suggest next phase if current is complete
|
|
180
122
|
const hasApprovalPending = relevantGate && approvalGates[relevantGate] && !approvalGates[relevantGate].approved;
|
|
181
123
|
if (missingOutputs.length === 0 && !hasApprovalPending) {
|
|
182
124
|
const currentPhaseIndex = PHASE_ORDER.findIndex(p => p.key === feature.phase);
|
|
@@ -199,28 +141,6 @@ function getNextSteps(feature) {
|
|
|
199
141
|
return steps;
|
|
200
142
|
}
|
|
201
143
|
|
|
202
|
-
/**
|
|
203
|
-
* Estimate remaining time based on tasks
|
|
204
|
-
*/
|
|
205
|
-
function estimateRemainingTime(feature) {
|
|
206
|
-
if (!feature.tasks || feature.tasks.total === 0) {
|
|
207
|
-
return 'Not estimated';
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const remaining = feature.tasks.total - feature.tasks.completed;
|
|
211
|
-
const avgHoursPerTask = 2; // Configurable estimate
|
|
212
|
-
const hours = remaining * avgHoursPerTask;
|
|
213
|
-
|
|
214
|
-
if (hours < 1) return '< 1 hour';
|
|
215
|
-
if (hours === 1) return '1 hour';
|
|
216
|
-
if (hours < 8) return `${hours} hours`;
|
|
217
|
-
const days = Math.ceil(hours / 8);
|
|
218
|
-
return `~${days} day${days > 1 ? 's' : ''} (${hours}h)`;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Main status command
|
|
223
|
-
*/
|
|
224
144
|
export async function statusCommand(featureName, options = {}) {
|
|
225
145
|
if (!featureName) {
|
|
226
146
|
console.error(chalk.red('✗ Error: Feature name required'));
|
|
@@ -242,7 +162,6 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
242
162
|
process.exit(1);
|
|
243
163
|
}
|
|
244
164
|
|
|
245
|
-
// Derive phase and outputs from filesystem if not stored in state
|
|
246
165
|
const { join } = await import('path');
|
|
247
166
|
const featurePath = join(process.cwd(), '.morph', 'features', featureName);
|
|
248
167
|
if (!feature.phase) {
|
|
@@ -251,8 +170,8 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
251
170
|
if (!feature.outputs) {
|
|
252
171
|
feature.outputs = deriveOutputs(featureName, process.cwd());
|
|
253
172
|
}
|
|
173
|
+
const slices = sliceProgress(featurePath);
|
|
254
174
|
|
|
255
|
-
// JSON output mode
|
|
256
175
|
if (options.json) {
|
|
257
176
|
const status = {
|
|
258
177
|
feature: featureName,
|
|
@@ -270,28 +189,24 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
270
189
|
created: output.created,
|
|
271
190
|
path: output.path
|
|
272
191
|
})),
|
|
273
|
-
|
|
192
|
+
slices,
|
|
274
193
|
approvals: feature.approvalGates || {},
|
|
275
|
-
nextSteps: getNextSteps(feature)
|
|
276
|
-
estimatedTimeRemaining: estimateRemainingTime(feature)
|
|
194
|
+
nextSteps: getNextSteps(feature)
|
|
277
195
|
};
|
|
278
196
|
|
|
279
197
|
console.log(JSON.stringify(status, null, 2));
|
|
280
198
|
return;
|
|
281
199
|
}
|
|
282
200
|
|
|
283
|
-
// Visual dashboard output
|
|
284
201
|
console.log(chalk.cyan('\n┌────────────────────────────────────────────────────────────┐'));
|
|
285
202
|
console.log(chalk.cyan('│ MORPH-SPEC STATUS │'));
|
|
286
203
|
console.log(chalk.cyan('└────────────────────────────────────────────────────────────┘\n'));
|
|
287
204
|
|
|
288
|
-
// Feature info
|
|
289
205
|
console.log(chalk.white.bold(`Feature: ${featureName}`));
|
|
290
206
|
console.log(chalk.gray(`Status: ${feature.status || 'unknown'}`));
|
|
291
207
|
console.log(chalk.gray(`Completion: ${calculateCompletionPercentage(feature)}%`));
|
|
292
208
|
console.log('');
|
|
293
209
|
|
|
294
|
-
// Phase progression
|
|
295
210
|
console.log(chalk.white.bold('Phases:'));
|
|
296
211
|
console.log(chalk.cyan('├─────────────────────────────────────────────────────────────┤'));
|
|
297
212
|
PHASE_ORDER.forEach(phase => {
|
|
@@ -300,7 +215,6 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
300
215
|
console.log(chalk.cyan('└─────────────────────────────────────────────────────────────┘'));
|
|
301
216
|
console.log('');
|
|
302
217
|
|
|
303
|
-
// Outputs summary
|
|
304
218
|
const totalOutputs = Object.keys(feature.outputs).length;
|
|
305
219
|
const createdOutputs = Object.values(feature.outputs).filter(o => o.created).length;
|
|
306
220
|
console.log(chalk.white.bold(`Outputs: ${createdOutputs}/${totalOutputs} complete`));
|
|
@@ -315,21 +229,16 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
315
229
|
}
|
|
316
230
|
console.log('');
|
|
317
231
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
console.log(`
|
|
325
|
-
console.log(chalk.green(` ✓ Completed: ${completed} (${percentage}%)`));
|
|
326
|
-
if (inProgress > 0) console.log(chalk.cyan(` ▶ In Progress: ${inProgress}`));
|
|
327
|
-
if (pending > 0) console.log(chalk.gray(` ⬜ Pending: ${pending}`));
|
|
328
|
-
console.log(` Estimated time remaining: ${estimateRemainingTime(feature)}`);
|
|
232
|
+
if (slices && slices.total > 0) {
|
|
233
|
+
console.log(chalk.white.bold('Fatias:'));
|
|
234
|
+
const percentage = Math.round((slices.done / slices.total) * 100);
|
|
235
|
+
console.log(` Total: ${slices.total}`);
|
|
236
|
+
console.log(chalk.green(` ✓ Feitas: ${slices.done} (${percentage}%)`));
|
|
237
|
+
if (slices.partial > 0) console.log(chalk.cyan(` ▶ Parciais: ${slices.partial}`));
|
|
238
|
+
if (slices.pending > 0) console.log(chalk.gray(` ⬜ Pendentes: ${slices.pending}`));
|
|
329
239
|
console.log('');
|
|
330
240
|
}
|
|
331
241
|
|
|
332
|
-
// Approval gates
|
|
333
242
|
if (feature.approvalGates) {
|
|
334
243
|
const gates = Object.entries(feature.approvalGates);
|
|
335
244
|
const approved = gates.filter(([_, g]) => g.approved).length;
|
|
@@ -349,7 +258,6 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
349
258
|
console.log('');
|
|
350
259
|
}
|
|
351
260
|
|
|
352
|
-
// Next steps
|
|
353
261
|
const nextSteps = getNextSteps(feature);
|
|
354
262
|
if (nextSteps.length > 0) {
|
|
355
263
|
console.log(chalk.white.bold('Next Steps:'));
|
|
@@ -361,10 +269,9 @@ export async function statusCommand(featureName, options = {}) {
|
|
|
361
269
|
});
|
|
362
270
|
console.log('');
|
|
363
271
|
} else {
|
|
364
|
-
console.log(chalk.green('✓
|
|
272
|
+
console.log(chalk.green('✓ Nada pendente.\n'));
|
|
365
273
|
}
|
|
366
274
|
|
|
367
|
-
// Footer
|
|
368
275
|
console.log(chalk.gray('─'.repeat(60)));
|
|
369
276
|
console.log(chalk.gray('Run with --verbose for detailed output'));
|
|
370
277
|
console.log(chalk.gray('Run with --json for JSON output\n'));
|