@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,43 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* mobile, abas) escritas por tela, na fase uiux.
|
|
5
|
-
*
|
|
6
|
-
* Existe porque o que o design não pergunta não vira `doneCriteria`, e o que
|
|
7
|
-
* não é critério ninguém entrega: telas de admin saíam listando tudo, sem
|
|
8
|
-
* paginar nem ordenar, com o design aprovado. Os leitores do MESMO parser:
|
|
9
|
-
*
|
|
10
|
-
* - `gate-check uiux` pausa com os problemas nomeados;
|
|
11
|
-
* - `approve`/`advance` com assinatura `auto` recusam a mesma pausa sem uma
|
|
12
|
-
* decisão gravada (`uiuxAutoApprovalBlock`) — a regra é a do risk-detector;
|
|
13
|
-
* - `gate-check review` exige que o avaliador registre a conferência de cada
|
|
14
|
-
* item decidido contra a tela entregue (`checklistCoverage`).
|
|
15
|
-
*
|
|
16
|
-
* Mockup no formato ANTERIOR ao checklist (telas `## S001: …`, sem nenhuma
|
|
17
|
-
* `## Tela:`) é legado e isento, com a isenção gravada no sinal.
|
|
18
|
-
*
|
|
19
|
-
* O formato é o que a `morph-uiux` ensina: cada tela é um `## Tela: <nome>`, e
|
|
20
|
-
* dentro dela um `### Checklist de tela` com uma linha por item — tabela
|
|
21
|
-
* (`| Paginação | servidor, 25/página |`) ou lista (`- **Paginação:** …`).
|
|
22
|
-
* "Não se aplica" é resposta válida; vazio, "?" ou "a definir" não são.
|
|
23
|
-
*/
|
|
1
|
+
// Cada tela é `## Tela: <nome>` com um `### Checklist de tela`, uma linha por item (tabela ou lista).
|
|
2
|
+
// "Não se aplica" é resposta válida; vazio, "?" ou "a definir" não são. Telas `## S001: …` sem
|
|
3
|
+
// nenhuma `## Tela:` são o formato legado, isento.
|
|
24
4
|
|
|
25
5
|
import { createHash } from 'crypto';
|
|
26
6
|
import { existsSync, readFileSync } from 'fs';
|
|
27
|
-
import { join } from 'path';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
28
8
|
import { fileURLToPath } from 'url';
|
|
29
9
|
import { getAbsoluteOutputPath } from '../../core/paths/output-schema.js';
|
|
30
|
-
import {
|
|
10
|
+
import { verifierScreenChecklist } from '../review-reports.js';
|
|
31
11
|
import { LEGACY_MOCKUP_EXEMPTION, uiuxChecklistPause } from '../risk-detector.js';
|
|
32
12
|
|
|
33
13
|
const PACKAGE_AGENTS = fileURLToPath(new URL('../../../framework/agents.json', import.meta.url));
|
|
34
14
|
|
|
35
|
-
/**
|
|
36
|
-
* Os itens, na ordem do template. `match` roda sobre o rótulo NORMALIZADO
|
|
37
|
-
* (minúsculas, sem acento): "Estado de erro", "Erro" e "**Erro:**" são o mesmo
|
|
38
|
-
* item. Um rótulo que casa com vários ("Estados (vazio/carregando/erro)")
|
|
39
|
-
* responde a todos eles.
|
|
40
|
-
*/
|
|
15
|
+
/** `match` roda sobre o rótulo normalizado; um rótulo que casa com vários itens responde a todos. */
|
|
41
16
|
export const SCREEN_CHECKLIST_ITEMS = [
|
|
42
17
|
{ id: 'superficie', label: 'Superfície', match: /\bsuperficie\b/ },
|
|
43
18
|
{ id: 'volume', label: 'Volume esperado', match: /\bvolume\b/ },
|
|
@@ -51,26 +26,24 @@ export const SCREEN_CHECKLIST_ITEMS = [
|
|
|
51
26
|
{ id: 'abas', label: 'Abas', match: /\babas\b/ },
|
|
52
27
|
];
|
|
53
28
|
|
|
54
|
-
|
|
55
|
-
* Itens que o avaliador não confere contra a tela: a superfície decide QUAIS
|
|
56
|
-
* standards a tela lê, não um comportamento que a tela entregue exiba.
|
|
57
|
-
*/
|
|
29
|
+
// A superfície decide quais standards a tela lê, não um comportamento que a tela entregue exiba.
|
|
58
30
|
const NOT_VERIFIED_AT_REVIEW = new Set(['superficie']);
|
|
59
31
|
|
|
60
|
-
|
|
61
|
-
* Respostas que ocupam a célula sem decidir nada: as exatas, e as que COMEÇAM
|
|
62
|
-
* adiando a decisão ("a definir depois", "TBD.", "? talvez").
|
|
63
|
-
*/
|
|
32
|
+
// As exatas, e as que começam adiando a decisão ("a definir depois", "TBD.").
|
|
64
33
|
const PLACEHOLDERS = new Set(['', '-', '--', '—', '–', '...', 'x', 'todo', 'definir', 'pendente']);
|
|
65
34
|
const PLACEHOLDER_PREFIX_RE = /^(a definir|tbd|\?|…)/;
|
|
66
35
|
|
|
67
|
-
/** O formato de mockup anterior ao checklist: telas `## S001: <nome>`. */
|
|
68
36
|
const LEGACY_SCREEN_HEADING_RE = /^##\s+S\d+\s*:/i;
|
|
69
37
|
|
|
70
38
|
const NOT_APPLICABLE_RE = /^(nao se aplica|n\/a)\b/;
|
|
71
39
|
|
|
72
40
|
const SCREEN_HEADING_RE = /^##\s+tela\s*[:—–-]\s*(.+?)\s*#*\s*$/i;
|
|
73
41
|
const CHECKLIST_HEADING_RE = /^(#{3,6})\s+checklist\b/i;
|
|
42
|
+
const PROTOTYPE_LINE_RE = /prot[oó]tipo/i;
|
|
43
|
+
const PROTOTYPE_PATH_RE = /(?:1-design\/)?(prototipos\/[^\s`|)\]]+\.html)/i;
|
|
44
|
+
|
|
45
|
+
/** As larguras dos prints que o laço de autocrítica deixa ao lado do protótipo. */
|
|
46
|
+
export const PROTOTYPE_WIDTHS = [1280, 390];
|
|
74
47
|
|
|
75
48
|
/** Minúsculas, sem acento, sem marcação inline, espaços colapsados. */
|
|
76
49
|
export function normalizeLabel(text) {
|
|
@@ -83,21 +56,15 @@ export function normalizeLabel(text) {
|
|
|
83
56
|
.trim();
|
|
84
57
|
}
|
|
85
58
|
|
|
86
|
-
/** Nome de tela comparável entre o mockup e o relatório do avaliador. */
|
|
87
59
|
function screenKey(name) {
|
|
88
60
|
return normalizeLabel(name).replace(/^tela\s*[:—–-]\s*/, '').replace(/[.:;,]+$/, '');
|
|
89
61
|
}
|
|
90
62
|
|
|
91
|
-
/** Os itens que um rótulo responde. */
|
|
92
63
|
function itemsOf(label) {
|
|
93
64
|
const norm = normalizeLabel(label);
|
|
94
65
|
return SCREEN_CHECKLIST_ITEMS.filter((item) => item.match.test(norm)).map((item) => item.id);
|
|
95
66
|
}
|
|
96
67
|
|
|
97
|
-
/**
|
|
98
|
-
* Uma linha de checklist como `[rótulo, resposta]`, ou null: linha de tabela
|
|
99
|
-
* (separador `|---|` fora) ou item de lista `- Rótulo: resposta`.
|
|
100
|
-
*/
|
|
101
68
|
function checklistRow(line) {
|
|
102
69
|
const trimmed = line.trim();
|
|
103
70
|
if (trimmed.startsWith('|')) {
|
|
@@ -112,13 +79,12 @@ function checklistRow(line) {
|
|
|
112
79
|
return [bullet[1].slice(0, colon), bullet[1].slice(colon + 1).trim()];
|
|
113
80
|
}
|
|
114
81
|
|
|
115
|
-
/** As células de uma linha de tabela markdown, sem as bordas. */
|
|
116
82
|
export function tableCells(line) {
|
|
117
83
|
const inner = line.trim().replace(/^\|/, '').replace(/\|$/, '');
|
|
118
84
|
return inner.split(/(?<!\\)\|/).map((cell) => cell.trim());
|
|
119
85
|
}
|
|
120
86
|
|
|
121
|
-
|
|
87
|
+
// Um exemplo dentro de ``` não é uma tela.
|
|
122
88
|
function proseLines(markdown) {
|
|
123
89
|
const out = [];
|
|
124
90
|
let fenced = false;
|
|
@@ -129,12 +95,6 @@ function proseLines(markdown) {
|
|
|
129
95
|
return out;
|
|
130
96
|
}
|
|
131
97
|
|
|
132
|
-
/**
|
|
133
|
-
* As telas do mockup e o que o checklist de cada uma responde.
|
|
134
|
-
*
|
|
135
|
-
* @param {string} markdown - conteúdo do `1-design/mockups.md`
|
|
136
|
-
* @returns {Array<{ name: string, hasChecklist: boolean, answers: Map<string, string> }>}
|
|
137
|
-
*/
|
|
138
98
|
export function parseScreens(markdown) {
|
|
139
99
|
const screens = [];
|
|
140
100
|
let screen = null;
|
|
@@ -146,7 +106,7 @@ export function parseScreens(markdown) {
|
|
|
146
106
|
const level = heading[1].length;
|
|
147
107
|
const named = SCREEN_HEADING_RE.exec(line);
|
|
148
108
|
if (level <= 2) {
|
|
149
|
-
screen = named && named[1].trim() ? { name: named[1].trim(), hasChecklist: false, answers: new Map() } : null;
|
|
109
|
+
screen = named && named[1].trim() ? { name: named[1].trim(), hasChecklist: false, answers: new Map(), prototype: null } : null;
|
|
150
110
|
if (screen) screens.push(screen);
|
|
151
111
|
checklistLevel = 0;
|
|
152
112
|
continue;
|
|
@@ -160,6 +120,9 @@ export function parseScreens(markdown) {
|
|
|
160
120
|
}
|
|
161
121
|
continue;
|
|
162
122
|
}
|
|
123
|
+
if (screen && !screen.prototype && PROTOTYPE_LINE_RE.test(line)) {
|
|
124
|
+
screen.prototype = PROTOTYPE_PATH_RE.exec(line)?.[1] ?? null;
|
|
125
|
+
}
|
|
163
126
|
if (!screen || !checklistLevel) continue;
|
|
164
127
|
const row = checklistRow(line);
|
|
165
128
|
if (!row) continue;
|
|
@@ -178,47 +141,29 @@ const isNotApplicable = (answer) => NOT_APPLICABLE_RE.test(normalizeLabel(answer
|
|
|
178
141
|
|
|
179
142
|
const labelOf = (id) => SCREEN_CHECKLIST_ITEMS.find((item) => item.id === id)?.label ?? id;
|
|
180
143
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
* vocabulário válido do item `Superfície`, que o `morph-plan` §5a usa para
|
|
184
|
-
* somar os standards da tela em `tasks[].standards`. Lê a cópia instalada do
|
|
185
|
-
* projeto; sem ela (este repositório), a do pacote. Persona sem `surfaces`
|
|
186
|
-
* (instalação anterior ao campo) devolve null: o valor não é validado.
|
|
187
|
-
*
|
|
188
|
-
* @param {string} cwd
|
|
189
|
-
* @returns {string[]|null}
|
|
190
|
-
*/
|
|
144
|
+
// O vocabulário válido de `Superfície`: as `surfaces` da `ui-designer`, da cópia instalada ou do pacote.
|
|
145
|
+
// null quando a persona não declara o campo, e então o valor não é validado.
|
|
191
146
|
export function knownSurfaces(cwd) {
|
|
147
|
+
const surfaces = readAgentsJson(cwd)?.agents?.['ui-designer']?.surfaces;
|
|
148
|
+
return surfaces && typeof surfaces === 'object' && !Array.isArray(surfaces) ? Object.keys(surfaces) : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The project's installed `agents.json`, else the package's; null when unreadable. */
|
|
152
|
+
export function readAgentsJson(cwd) {
|
|
192
153
|
for (const path of [join(cwd, '.morph', 'framework', 'agents.json'), PACKAGE_AGENTS]) {
|
|
193
154
|
if (!existsSync(path)) continue;
|
|
194
|
-
try {
|
|
195
|
-
const surfaces = JSON.parse(readFileSync(path, 'utf8'))?.agents?.['ui-designer']?.surfaces;
|
|
196
|
-
return surfaces && typeof surfaces === 'object' && !Array.isArray(surfaces) ? Object.keys(surfaces) : null;
|
|
197
|
-
} catch {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
155
|
+
try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
|
|
200
156
|
}
|
|
201
157
|
return null;
|
|
202
158
|
}
|
|
203
159
|
|
|
204
|
-
/** A resposta de `Superfície` nomeia uma das superfícies conhecidas? */
|
|
205
160
|
function namesSurface(answer, surfaces) {
|
|
206
161
|
const norm = normalizeLabel(answer);
|
|
207
162
|
return surfaces.some((key) => new RegExp(`(^|[^a-z0-9-])${key}([^a-z0-9-]|$)`).test(norm));
|
|
208
163
|
}
|
|
209
164
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
* resposta escrita — e, quando `surfaces` é dado, a `Superfície` é uma delas.
|
|
213
|
-
*
|
|
214
|
-
* Sem nenhuma `## Tela:`, o mockup é `legacy` (isento) só quando declara telas
|
|
215
|
-
* no formato antigo; qualquer outro formato é o problema "nenhuma tela
|
|
216
|
-
* declarada" — senão trocar o heading seria o atalho para pular o checklist.
|
|
217
|
-
*
|
|
218
|
-
* @param {string} markdown
|
|
219
|
-
* @returns {{ legacy: boolean, screens: Array<{ name: string, hasChecklist: boolean, missing: string[], unanswered: string[] }>,
|
|
220
|
-
* problems: string[] }} `problems` vazio ⇔ checklist completo (ou isento)
|
|
221
|
-
*/
|
|
165
|
+
// Sem nenhuma `## Tela:`, só o formato antigo é legado; qualquer outro heading é "nenhuma tela
|
|
166
|
+
// declarada", senão trocar o heading pularia o checklist.
|
|
222
167
|
export function auditScreenChecklist(markdown, { surfaces = null } = {}) {
|
|
223
168
|
const parsed = parseScreens(markdown);
|
|
224
169
|
const problems = [];
|
|
@@ -246,69 +191,55 @@ export function auditScreenChecklist(markdown, { surfaces = null } = {}) {
|
|
|
246
191
|
problems.push(`tela "${screen.name}": Superfície "${surface}" não é uma das superfícies da ui-designer (${surfaces.join(', ')})`);
|
|
247
192
|
}
|
|
248
193
|
}
|
|
249
|
-
return { name: screen.name, hasChecklist: screen.hasChecklist, missing, unanswered };
|
|
194
|
+
return { name: screen.name, hasChecklist: screen.hasChecklist, missing, unanswered, prototype: screen.prototype };
|
|
250
195
|
});
|
|
251
196
|
return { legacy: false, screens, problems };
|
|
252
197
|
}
|
|
253
198
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
199
|
+
// Linha **Protótipo:** da tela + o HTML + os prints do laço de autocrítica ao lado dele.
|
|
200
|
+
export function auditPrototypes(screens, designDir) {
|
|
201
|
+
const problems = [];
|
|
202
|
+
for (const screen of screens) {
|
|
203
|
+
if (!screen.prototype) {
|
|
204
|
+
problems.push(`tela "${screen.name}" sem a linha \`**Protótipo:** 1-design/prototipos/<tela>.html\``);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const base = screen.prototype.replace(/\.html$/i, '');
|
|
208
|
+
const expected = [screen.prototype, ...PROTOTYPE_WIDTHS.map((width) => `${base}-${width}.png`)];
|
|
209
|
+
const missing = expected.filter((rel) => !existsSync(join(designDir, rel)));
|
|
210
|
+
if (missing.length) problems.push(`tela "${screen.name}": falta ${missing.map((rel) => `1-design/${rel}`).join(', ')}`);
|
|
211
|
+
}
|
|
212
|
+
return problems;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Coleta, nunca decide: quem pausa é o risk-detector. No review cobra-se a conferência (`uncovered`), não o design.
|
|
268
216
|
export function collectScreenChecklist(cwd, featureName, gateName) {
|
|
269
217
|
const mockups = readMockups(cwd, featureName);
|
|
270
218
|
if (mockups === null) return { mockups: false, legacy: false, screens: [], problems: [] };
|
|
271
219
|
const audit = auditScreenChecklist(mockups, { surfaces: knownSurfaces(cwd) });
|
|
272
220
|
const signal = { mockups: true, legacy: audit.legacy, screens: audit.screens.map((s) => s.name), problems: audit.problems };
|
|
273
221
|
if (audit.legacy) signal.exempt = LEGACY_MOCKUP_EXEMPTION;
|
|
222
|
+
if (gateName === 'uiux' && !audit.legacy) {
|
|
223
|
+
const designDir = dirname(getAbsoluteOutputPath(cwd, featureName, 'uiMockups'));
|
|
224
|
+
signal.prototypes = auditPrototypes(audit.screens, designDir);
|
|
225
|
+
}
|
|
274
226
|
if (gateName === 'review') {
|
|
275
|
-
const report =
|
|
227
|
+
const report = verifierScreenChecklist(cwd, featureName);
|
|
276
228
|
signal.section = report.section;
|
|
277
229
|
signal.uncovered = checklistCoverage(mockups, report.section ? report.body : '');
|
|
278
230
|
}
|
|
279
231
|
return signal;
|
|
280
232
|
}
|
|
281
233
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
* que o `gate-check uiux` daria pelo checklist —, ou null. Lido por `approve`
|
|
285
|
-
* e `advance`, para que uma aprovação automática nunca passe onde o gate-check
|
|
286
|
-
* pausaria. Uma decisão gravada com `pause: false` (o `gate-decision` manual,
|
|
287
|
-
* ou um gate-check que não pausou) é a resposta a essa pausa e destrava — só
|
|
288
|
-
* para o mockup que ela viu: a decisão guarda `mockupsHash`, e um mockup
|
|
289
|
-
* escrito ou mudado depois dela volta a ser conferido.
|
|
290
|
-
*
|
|
291
|
-
* @param {string} cwd
|
|
292
|
-
* @param {string} featureName
|
|
293
|
-
* @param {object} feature - o feature.json (lê `gateDecisions.uiux`)
|
|
294
|
-
* @returns {string|null}
|
|
295
|
-
*/
|
|
234
|
+
// Lido por approve e advance para que o `auto` nunca passe onde o gate-check pausaria. Uma decisão com
|
|
235
|
+
// `pause: false` destrava só o design que ela viu: guarda `mockupsHash`, e mudar o arquivo volta a conferir.
|
|
296
236
|
export function uiuxAutoApprovalBlock(cwd, featureName, feature) {
|
|
297
237
|
const decision = feature?.gateDecisions?.uiux;
|
|
298
238
|
if (decision?.pause === false && decision.mockupsHash === mockupsHash(cwd, featureName)) return null;
|
|
299
239
|
return uiuxChecklistPause(collectScreenChecklist(cwd, featureName, 'uiux'));
|
|
300
240
|
}
|
|
301
241
|
|
|
302
|
-
|
|
303
|
-
* O que o avaliador deixou de conferir: por tela do mockup, os itens com
|
|
304
|
-
* decisão (nem "não se aplica", nem a superfície) sem linha na seção
|
|
305
|
-
* `## Checklist de tela` do relatório. Uma linha que o avaliador marca como
|
|
306
|
-
* não verificável conta como conferida — o que pausa é a AUSÊNCIA da linha.
|
|
307
|
-
*
|
|
308
|
-
* @param {string} designMarkdown - o `1-design/mockups.md`
|
|
309
|
-
* @param {string} reportSection - o corpo da seção do `evaluator-report.md`
|
|
310
|
-
* @returns {string[]} `"<tela>: <item>, <item>"` por tela com lacuna
|
|
311
|
-
*/
|
|
242
|
+
// Linha marcada como não verificável conta como conferida: o que pausa é a ausência da linha.
|
|
312
243
|
export function checklistCoverage(designMarkdown, reportSection) {
|
|
313
244
|
const covered = new Map();
|
|
314
245
|
for (const line of proseLines(reportSection)) {
|
|
@@ -332,14 +263,7 @@ export function checklistCoverage(designMarkdown, reportSection) {
|
|
|
332
263
|
return gaps;
|
|
333
264
|
}
|
|
334
265
|
|
|
335
|
-
|
|
336
|
-
* O `1-design/mockups.md` por conteúdo: BOM e CRLF não contam, para que o
|
|
337
|
-
* checkout de outra máquina seja o mesmo mockup.
|
|
338
|
-
*
|
|
339
|
-
* @param {string} cwd
|
|
340
|
-
* @param {string} featureName
|
|
341
|
-
* @returns {string|null} null sem mockup
|
|
342
|
-
*/
|
|
266
|
+
// BOM e CRLF não contam: o checkout de outra máquina é o mesmo arquivo.
|
|
343
267
|
export function mockupsHash(cwd, featureName) {
|
|
344
268
|
const text = readMockups(cwd, featureName);
|
|
345
269
|
if (text === null) return null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { ensureDotnetBuildIgnores } from '../../utils/file-copier.js';
|
|
4
|
+
import { getProjectTags } from '../stack-filter.js';
|
|
5
|
+
import { readMorphConfig } from '../tasks/test-runner.js';
|
|
6
|
+
import { readPlanChecks } from '../plan/checks.js';
|
|
7
|
+
import { WORKTREE_DIR_NAMES } from '../worktree/dirs.js';
|
|
8
|
+
|
|
9
|
+
export const DOTNET_IMPLEMENTERS = ['dotnet-senior', 'ef-modeler', 'maf-expert'];
|
|
10
|
+
|
|
11
|
+
const SKIPPED_DIRS = new Set([...WORKTREE_DIR_NAMES, 'node_modules', 'bin', 'obj']);
|
|
12
|
+
const DOTNET_PROJECT_FILE = /\.(?:csproj|slnx?)$/i;
|
|
13
|
+
const MAX_DEPTH = 6;
|
|
14
|
+
|
|
15
|
+
export function hasDotnetProjectFile(root, depth = 0) {
|
|
16
|
+
let entries;
|
|
17
|
+
try { entries = readdirSync(root, { withFileTypes: true }); } catch { return false; }
|
|
18
|
+
if (entries.some((e) => e.isFile() && DOTNET_PROJECT_FILE.test(e.name))) return true;
|
|
19
|
+
if (depth >= MAX_DEPTH) return false;
|
|
20
|
+
return entries.some((e) => e.isDirectory() && !e.name.startsWith('.') && !SKIPPED_DIRS.has(e.name)
|
|
21
|
+
&& hasDotnetProjectFile(join(root, e.name), depth + 1));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function planHasDotnetSlice(tree, featureName) {
|
|
25
|
+
if (!featureName) return false;
|
|
26
|
+
const { checks } = readPlanChecks(tree, featureName);
|
|
27
|
+
return (checks?.slices || []).some((slice) => DOTNET_IMPLEMENTERS.includes(String(slice.persona || '').replace(/^morph-/, '')));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// The plan's slices are checked because a greenfield tree has no stack or .csproj yet.
|
|
31
|
+
export function isDotnetProject(tree, { featureName, configRoot = tree } = {}) {
|
|
32
|
+
if (getProjectTags(readMorphConfig(configRoot)).includes('dotnet')) return true;
|
|
33
|
+
return planHasDotnetSlice(tree, featureName) || hasDotnetProjectFile(tree);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Returns the `.gitignore` entries added; `[]` when not .NET or on any error. */
|
|
37
|
+
export function seedDotnetBuildIgnores(tree, options = {}) {
|
|
38
|
+
try {
|
|
39
|
+
return isDotnetProject(tree, options) ? ensureDotnetBuildIgnores(tree) : [];
|
|
40
|
+
} catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
|
|
4
|
-
/** @typedef {'nextjs'|'dotnet'|'nodejs-cli'|'unknown'} StackName */
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} StackProfile
|
|
8
|
-
* @property {StackName} stack
|
|
9
|
-
* @property {boolean} isDotnet
|
|
10
|
-
* @property {boolean} isNextjs
|
|
11
|
-
* @property {string[]} defaultValidators
|
|
12
|
-
* @property {string|null} buildCommand
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
4
|
const DOTNET_STACKS = new Set(['dotnet', 'aspnet']);
|
|
16
5
|
const NEXTJS_STACKS = new Set(['nextjs', 'next', 'next.js']);
|
|
17
6
|
const NO_VALIDATOR_STACKS = new Set(['nodejs-cli', 'node-cli', 'cli']);
|
|
@@ -34,9 +23,6 @@ const PROFILES = {
|
|
|
34
23
|
},
|
|
35
24
|
};
|
|
36
25
|
|
|
37
|
-
/**
|
|
38
|
-
* Read project stack from .morph/config/config.json.
|
|
39
|
-
*/
|
|
40
26
|
function readStack(cwd) {
|
|
41
27
|
const configPath = join(cwd, '.morph', 'config', 'config.json');
|
|
42
28
|
if (!existsSync(configPath)) return null;
|
|
@@ -47,7 +33,6 @@ function readStack(cwd) {
|
|
|
47
33
|
}
|
|
48
34
|
}
|
|
49
35
|
|
|
50
|
-
/** Merge two or more profiles' validator lists (deduped) and boolean flags. */
|
|
51
36
|
function mergeProfiles(profiles) {
|
|
52
37
|
return {
|
|
53
38
|
isDotnet: profiles.some((p) => p.isDotnet),
|
|
@@ -57,16 +42,7 @@ function mergeProfiles(profiles) {
|
|
|
57
42
|
};
|
|
58
43
|
}
|
|
59
44
|
|
|
60
|
-
/**
|
|
61
|
-
* Get the StackProfile for the current project.
|
|
62
|
-
*
|
|
63
|
-
* `stack` may be a compound value (e.g. "dotnet-nextjs", "dotnet+nextjs") —
|
|
64
|
-
* the framework's own convention for monorepos (see stack-filter.js's
|
|
65
|
-
* getProjectTags, which splits project.stack the same way). Each segment is
|
|
66
|
-
* resolved independently and matching profiles are merged, instead of the
|
|
67
|
-
* whole compound string failing every exact-match check and silently
|
|
68
|
-
* collapsing to the empty profile (0 default validators).
|
|
69
|
-
*/
|
|
45
|
+
/** A compound stack ("dotnet-nextjs", "dotnet+nextjs") merges the profile of each segment. */
|
|
70
46
|
export function getStackProfile(cwd, stackOverride) {
|
|
71
47
|
const root = cwd ?? process.cwd();
|
|
72
48
|
const raw = stackOverride ?? readStack(root);
|
|
@@ -74,8 +50,6 @@ export function getStackProfile(cwd, stackOverride) {
|
|
|
74
50
|
|
|
75
51
|
const segments = stack.split(/[-+]/).filter(Boolean);
|
|
76
52
|
|
|
77
|
-
// Single-segment stack (the overwhelming common case): resolve exactly as
|
|
78
|
-
// before, byte-for-byte, including the 'unknown' → dotnet fallback.
|
|
79
53
|
if (segments.length <= 1) {
|
|
80
54
|
let profile;
|
|
81
55
|
if (NEXTJS_STACKS.has(stack)) {
|
|
@@ -90,7 +64,6 @@ export function getStackProfile(cwd, stackOverride) {
|
|
|
90
64
|
return { stack, ...profile };
|
|
91
65
|
}
|
|
92
66
|
|
|
93
|
-
// Compound stack: resolve each recognized segment and merge.
|
|
94
67
|
const matched = [];
|
|
95
68
|
if (segments.some((s) => DOTNET_STACKS.has(s))) matched.push(PROFILES.dotnet);
|
|
96
69
|
if (segments.some((s) => NEXTJS_STACKS.has(s))) matched.push(PROFILES.nextjs);
|