@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
|
@@ -39,11 +39,6 @@ import {
|
|
|
39
39
|
writeGraphifyignore, describeOutdatedIgnore, graphifyignoreTracked,
|
|
40
40
|
} from '../../lib/graph/graphifyignore.js';
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* Backup user's config.json before cleaning
|
|
44
|
-
* @param {string} morphPath - Path to .morph directory
|
|
45
|
-
* @returns {Promise<object|null>} Backup of config.json or null
|
|
46
|
-
*/
|
|
47
42
|
async function backupUserConfig(morphPath) {
|
|
48
43
|
try {
|
|
49
44
|
const configPath = join(morphPath, 'config', 'config.json');
|
|
@@ -52,40 +47,18 @@ async function backupUserConfig(morphPath) {
|
|
|
52
47
|
return config;
|
|
53
48
|
}
|
|
54
49
|
} catch (error) {
|
|
55
|
-
// If backup fails, return null and continue
|
|
56
50
|
}
|
|
57
51
|
return null;
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
/**
|
|
61
|
-
* Os DEZ diretórios que `cleanFrameworkDirs` apaga por inteiro, sem condição.
|
|
62
|
-
*
|
|
63
|
-
* O número está pinado à mão em `test/commands/update-dry-run.test.js`
|
|
64
|
-
* (`DIRETORIOS_APAGADOS_POR_INTEIRO`), com os dez caminhos escritos: derivar a
|
|
65
|
-
* expectativa desta função deixaria a suíte cega a uma entrada removida.
|
|
66
|
-
*
|
|
67
|
-
* Exportado porque o relatório do `--dry-run` é obrigado a nomeá-los: "update"
|
|
68
|
-
* não sugere a ninguém que `.claude/skills/` e `.claude/agents/` são apagados
|
|
69
|
-
* inteiros antes da reinstalação.
|
|
70
|
-
*
|
|
71
|
-
* @param {string} morphPath
|
|
72
|
-
* @param {string} targetPath
|
|
73
|
-
* @returns {string[]}
|
|
74
|
-
*/
|
|
75
54
|
export function frameworkDirsToClean(morphPath, targetPath) {
|
|
76
55
|
const claudeDir = join(targetPath, '.claude');
|
|
77
56
|
return [
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// reconcileLegacyTemplatesCopy() below, which decides per-run whether a
|
|
81
|
-
// pre-existing local copy is safe to remove or must be preserved as a
|
|
82
|
-
// user override. Blindly deleting it here (the old behavior) destroyed
|
|
83
|
-
// any customization on every single `update` run, override or not.
|
|
57
|
+
// framework/templates is left out on purpose: reconcileLegacyTemplatesCopy() decides whether
|
|
58
|
+
// a local copy is a user override to preserve.
|
|
84
59
|
join(morphPath, 'framework', 'standards'),
|
|
85
60
|
join(morphPath, 'framework', 'hooks'),
|
|
86
61
|
join(morphPath, 'framework', 'schemas'),
|
|
87
|
-
// Não é mais instalado: fica na lista para apagar o de instalações antigas.
|
|
88
|
-
join(morphPath, 'framework', 'rubrics'),
|
|
89
62
|
join(morphPath, 'framework', 'evals'),
|
|
90
63
|
join(morphPath, 'config'),
|
|
91
64
|
join(claudeDir, 'commands'),
|
|
@@ -95,14 +68,6 @@ export function frameworkDirsToClean(morphPath, targetPath) {
|
|
|
95
68
|
];
|
|
96
69
|
}
|
|
97
70
|
|
|
98
|
-
/**
|
|
99
|
-
* Emite (e, fora do dry, executa) a remoção recursiva de um diretório.
|
|
100
|
-
*
|
|
101
|
-
* Um efeito por arquivo, mais um pelo diretório. É a varredura que a §5.2 do
|
|
102
|
-
* spec exige, e é dela que sai a contagem "N arquivos" do relatório.
|
|
103
|
-
*
|
|
104
|
-
* @returns {number} arquivos varridos
|
|
105
|
-
*/
|
|
106
71
|
function sweepRemove(dir, fx) {
|
|
107
72
|
let entries;
|
|
108
73
|
try {
|
|
@@ -125,23 +90,8 @@ function sweepRemove(dir, fx) {
|
|
|
125
90
|
}
|
|
126
91
|
|
|
127
92
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* **A armadilha que esta função resolve (decisions.md D6).** Ela apaga
|
|
131
|
-
* exatamente os destinos que os laços de limpeza de órfão DEPOIS varrem
|
|
132
|
-
* (`skills-installer.js`, `agents-installer.js`, `installRulesForStack`).
|
|
133
|
-
* Numa execução real esses laços sempre encontram diretório vazio e não
|
|
134
|
-
* apagam nada. Num `--dry-run` ingênuo nada foi apagado, eles encontrariam o
|
|
135
|
-
* diretório POPULADO e o relatório preveria remoções que a execução real
|
|
136
|
-
* nunca faz — errado na direção que assusta o usuário. Como o clean é
|
|
137
|
-
* incondicional e total, a previsão correta é "tudo que hoje está sob estes
|
|
138
|
-
* dez é apagado", e os laços de órfão ficam calados em dry: são provadamente
|
|
139
|
-
* subsumidos.
|
|
140
|
-
*
|
|
141
|
-
* @param {string} morphPath - Path to .morph directory
|
|
142
|
-
* @param {string} targetPath - Project root path
|
|
143
|
-
* @param {ReturnType<import('../../lib/installers/effects.js').createEffects>} fx
|
|
144
|
-
* @returns {Array<{ dir: string, files: number }>} o que foi (ou seria) apagado
|
|
93
|
+
* Os laços de órfão depois varrem estes mesmos destinos já vazios: em dry eles ficam calados,
|
|
94
|
+
* senão o relatório preveria remoções que a execução real nunca faz.
|
|
145
95
|
*/
|
|
146
96
|
function cleanFrameworkDirs(morphPath, targetPath, fx) {
|
|
147
97
|
const cleaned = [];
|
|
@@ -151,31 +101,20 @@ function cleanFrameworkDirs(morphPath, targetPath, fx) {
|
|
|
151
101
|
cleaned.push({ dir, files: sweepRemove(dir, fx) });
|
|
152
102
|
}
|
|
153
103
|
} catch (error) {
|
|
154
|
-
// Continue even if cleanup fails for one directory
|
|
155
104
|
}
|
|
156
105
|
}
|
|
157
106
|
return cleaned;
|
|
158
107
|
}
|
|
159
108
|
|
|
160
|
-
/**
|
|
161
|
-
* Restore user's config.json after reinstallation
|
|
162
|
-
* @param {string} morphPath - Path to .morph directory
|
|
163
|
-
* @param {string} targetPath - Project root path
|
|
164
|
-
* @param {object|null} configBackup - Backup of config.json
|
|
165
|
-
* @param {string} currentVersion - Current CLI version
|
|
166
|
-
* @param {ReturnType<import('../../lib/installers/effects.js').createEffects>} fx
|
|
167
|
-
*/
|
|
168
109
|
function restoreUserConfig(morphPath, targetPath, configBackup, currentVersion, fx) {
|
|
169
110
|
const configDir = join(morphPath, 'config');
|
|
170
111
|
fx.ensureDir(configDir);
|
|
171
112
|
const configPath = join(configDir, 'config.json');
|
|
172
113
|
|
|
173
114
|
if (configBackup) {
|
|
174
|
-
// Preserve user data but update framework version
|
|
175
115
|
configBackup.frameworkVersion = currentVersion;
|
|
176
116
|
fx.writeJson(configPath, configBackup);
|
|
177
117
|
} else {
|
|
178
|
-
// Create basic config if none existed
|
|
179
118
|
const dirName = targetPath.split(/[\\/]/).pop();
|
|
180
119
|
const defaultConfig = {
|
|
181
120
|
framework: 'global',
|
|
@@ -189,15 +128,7 @@ function restoreUserConfig(morphPath, targetPath, configBackup, currentVersion,
|
|
|
189
128
|
}
|
|
190
129
|
}
|
|
191
130
|
|
|
192
|
-
/**
|
|
193
|
-
* `.morph/state.json` já está no índice do git?
|
|
194
|
-
*
|
|
195
|
-
* SONDA, separada da mutação de propósito: sondar é ler, e ler acontece nos
|
|
196
|
-
* dois modos. É o que faz previsão e execução concordarem sobre este efeito.
|
|
197
|
-
*
|
|
198
|
-
* @param {string} targetPath
|
|
199
|
-
* @returns {boolean}
|
|
200
|
-
*/
|
|
131
|
+
/** Sonda separada da mutação: ler acontece nos dois modos, e é o que faz previsão e execução concordarem. */
|
|
201
132
|
export function stateJsonIsTracked(targetPath) {
|
|
202
133
|
try {
|
|
203
134
|
const isRepo = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
@@ -214,38 +145,14 @@ export function stateJsonIsTracked(targetPath) {
|
|
|
214
145
|
}
|
|
215
146
|
}
|
|
216
147
|
|
|
217
|
-
/**
|
|
218
|
-
* Untrack an already-committed `.morph/state.json` from git.
|
|
219
|
-
*
|
|
220
|
-
* Before v5's per-feature model, `.morph/state.json` was committed. Adding it to
|
|
221
|
-
* `.gitignore` does NOT stop git from tracking a file already in the index — so on
|
|
222
|
-
* exactly the repos that had the P1 sweep problem, `git commit -a` would keep
|
|
223
|
-
* staging state.json. This runs `git rm --cached` once so the next commit drops it.
|
|
224
|
-
* The working-tree file is left in place (it's rebuilt from feature.json on load).
|
|
225
|
-
*
|
|
226
|
-
* Mildly destructive (mutates the git index), but no data is lost: state.json is a
|
|
227
|
-
* derivable index. Fail-open — a non-git project or any git error is a silent no-op.
|
|
228
|
-
*
|
|
229
|
-
* @param {string} targetPath - Project root
|
|
230
|
-
* @param {ReturnType<import('../../lib/installers/effects.js').createEffects>} fx
|
|
231
|
-
* @returns {boolean} true if state.json was untracked, false otherwise
|
|
232
|
-
*/
|
|
233
148
|
export function untrackCommittedStateJson(targetPath, fx = createEffects({ dryRun: false })) {
|
|
234
|
-
if (!stateJsonIsTracked(targetPath)) return false;
|
|
149
|
+
if (!stateJsonIsTracked(targetPath)) return false;
|
|
235
150
|
return fx.gitIndex('rm --cached --quiet', '.morph/state.json', { cwd: targetPath });
|
|
236
151
|
}
|
|
237
152
|
|
|
238
153
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* `.morph/.morphversion → morphVersion` é o que o `update` LÊ para decidir.
|
|
242
|
-
* `config.json → frameworkVersion` é o que o `update` ESCREVE e nunca lê para
|
|
243
|
-
* decidir coisa alguma — e no MORPH_OS os dois divergem por um major inteiro
|
|
244
|
-
* (7.5.0 contra 6.0.0). Um freio calibrado no campo errado recusaria (ou
|
|
245
|
-
* liberaria) com base num número que ninguém mantém.
|
|
246
|
-
*
|
|
247
|
-
* @param {string} targetPath
|
|
248
|
-
* @returns {Promise<{ morphVersion: string|null, frameworkVersion: string|null, diverged: boolean }>}
|
|
154
|
+
* `.morphversion` é o que o `update` LÊ para decidir; `config.json → frameworkVersion` só é
|
|
155
|
+
* escrito e pode divergir por um major inteiro.
|
|
249
156
|
*/
|
|
250
157
|
export async function readVersionRegisters(targetPath) {
|
|
251
158
|
let morphVersion = null;
|
|
@@ -253,30 +160,17 @@ export async function readVersionRegisters(targetPath) {
|
|
|
253
160
|
try {
|
|
254
161
|
const v = await readJson(join(targetPath, '.morph', '.morphversion'));
|
|
255
162
|
morphVersion = typeof v?.morphVersion === 'string' ? v.morphVersion : null;
|
|
256
|
-
} catch {
|
|
163
|
+
} catch {}
|
|
257
164
|
try {
|
|
258
165
|
const c = await readJson(join(targetPath, '.morph', 'config', 'config.json'));
|
|
259
166
|
frameworkVersion = typeof c?.frameworkVersion === 'string' ? c.frameworkVersion : null;
|
|
260
|
-
} catch {
|
|
167
|
+
} catch {}
|
|
261
168
|
|
|
262
169
|
const diverged = Boolean(morphVersion && frameworkVersion && morphVersion !== frameworkVersion);
|
|
263
170
|
return { morphVersion, frameworkVersion, diverged };
|
|
264
171
|
}
|
|
265
172
|
|
|
266
|
-
/**
|
|
267
|
-
* As linhas da recusa do freio de major.
|
|
268
|
-
*
|
|
269
|
-
* Separada do comando para ser testável sem rodar o `update`, e porque a
|
|
270
|
-
* mensagem carrega três obrigações: a versão instalada, a versão de destino e
|
|
271
|
-
* **o registro de onde a versão instalada foi lida**. Quando os dois registros
|
|
272
|
-
* discordam, imprime OS DOIS e diz qual decidiu — escolher em silêncio é
|
|
273
|
-
* acertar metade das vezes e nunca saber qual metade.
|
|
274
|
-
*
|
|
275
|
-
* @param {{ refuse: boolean, reason: string|null, jump: number|null }} verdict
|
|
276
|
-
* @param {{ morphVersion: string|null, frameworkVersion: string|null, diverged: boolean }} registers
|
|
277
|
-
* @param {string} targetVersion
|
|
278
|
-
* @returns {string[]}
|
|
279
|
-
*/
|
|
173
|
+
/** Quando os dois registros discordam, imprime OS DOIS e diz qual decidiu. */
|
|
280
174
|
export function majorGuardLines(verdict, registers, targetVersion) {
|
|
281
175
|
const lines = [];
|
|
282
176
|
if (verdict.reason === 'unknown-installed') {
|
|
@@ -298,7 +192,6 @@ export function majorGuardLines(verdict, registers, targetVersion) {
|
|
|
298
192
|
return lines;
|
|
299
193
|
}
|
|
300
194
|
|
|
301
|
-
/** Rótulo humano de cada verbo, para agrupar o relatório por natureza do efeito. */
|
|
302
195
|
const VERB_GROUPS = {
|
|
303
196
|
remove: 'Apagar',
|
|
304
197
|
ensureDir: 'Criar diretório',
|
|
@@ -317,20 +210,8 @@ function isUnder(path, root) {
|
|
|
317
210
|
return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
|
|
318
211
|
}
|
|
319
212
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
*
|
|
323
|
-
* Para quase todo verbo é o próprio `target`. Para `spawn` NÃO é: o `target`
|
|
324
|
-
* de um spawn é o binário (`node`), e classificar por ele esconderia
|
|
325
|
-
* exatamente o efeito que a §5.3 obriga a nomear — `generate-refs.js`
|
|
326
|
-
* escrevendo `framework/hooks/shared/phase-utils.js` DENTRO do pacote da CLI.
|
|
327
|
-
*
|
|
328
|
-
* O script sai de `effect.args`, o **array**, nunca de um split do `detail`
|
|
329
|
-
* por espaço: o caminho real deste pacote é
|
|
330
|
-
* `R:\Polymorphism Tech\repos\...` — um espaço no meio, e o split devolvia
|
|
331
|
-
* `Tech\repos\...\generate-refs.js`, que não é subcaminho de nada. O efeito ia
|
|
332
|
-
* calado para o grupo errado. Medido rodando o comando de verdade.
|
|
333
|
-
*/
|
|
213
|
+
// O `target` de um spawn é o binário (`node`): classifica pelo script em `effect.args`, o array —
|
|
214
|
+
// um split do `detail` por espaço quebra em caminho com espaço.
|
|
334
215
|
function effectLocation(effect) {
|
|
335
216
|
if (effect.verb !== 'spawn') return effect.target;
|
|
336
217
|
const args = Array.isArray(effect.args) ? effect.args : [];
|
|
@@ -338,26 +219,12 @@ function effectLocation(effect) {
|
|
|
338
219
|
return script || effect.target;
|
|
339
220
|
}
|
|
340
221
|
|
|
341
|
-
/**
|
|
342
|
-
* Imprime o relatório de efeitos.
|
|
343
|
-
*
|
|
344
|
-
* Três grupos ganham nome próprio, porque o usuário não os adivinharia da
|
|
345
|
-
* palavra "update": o que é apagado por INTEIRO antes da reinstalação, o que
|
|
346
|
-
* acontece FORA do projeto, e o que é escrito DENTRO do pacote da CLI (que num
|
|
347
|
-
* `npm link` de desenvolvimento é o próprio repositório do framework).
|
|
348
|
-
*
|
|
349
|
-
* @param {Array} effects - saída de `fx.list()`
|
|
350
|
-
* @param {object} context
|
|
351
|
-
*/
|
|
352
222
|
export function renderEffectsReport(effects, context) {
|
|
353
223
|
const { targetPath, packageRoot, dryRun, cleaned = [], notices = [] } = context;
|
|
354
224
|
const globalClaudeDir = join(homedir(), '.claude');
|
|
355
225
|
|
|
356
226
|
logger.blank();
|
|
357
|
-
//
|
|
358
|
-
// `generate-refs.js` RODA, na variante `--check`, que verifica e não escreve.
|
|
359
|
-
// Prometer silêncio total e rodar um processo é mentir num detalhe pequeno —
|
|
360
|
-
// e é o tipo de detalhe pequeno que faz o usuário parar de acreditar no resto.
|
|
227
|
+
// Não diz "nem executado": em dry o spawn de `generate-refs.js --check` roda.
|
|
361
228
|
logger.info(dryRun
|
|
362
229
|
? 'Relatório do --dry-run — nada foi escrito, apagado nem removido '
|
|
363
230
|
+ '(o único processo executado é a variante `--check` do gerador, que só verifica):'
|
|
@@ -419,7 +286,7 @@ export function renderEffectsReport(effects, context) {
|
|
|
419
286
|
+ `${outside.length} fora do projeto, ${inPackage.length} dentro do pacote da CLI.`);
|
|
420
287
|
logger.blank();
|
|
421
288
|
|
|
422
|
-
// Risco conhecido, reportado em vez de escondido
|
|
289
|
+
// Risco conhecido, reportado em vez de escondido: o backup
|
|
423
290
|
// de `config.json` só existe em memória durante a execução.
|
|
424
291
|
logger.dim('Nota: durante uma execução real o backup de `.morph/config/config.json` existe apenas '
|
|
425
292
|
+ 'em memória — uma falha no meio do caminho deixa o arquivo apagado. Follow-up nomeado.');
|
|
@@ -437,11 +304,7 @@ export async function updateCommand(options = {}) {
|
|
|
437
304
|
|
|
438
305
|
logger.header(dryRun ? 'MORPH-SPEC Update (--dry-run)' : 'MORPH-SPEC Update');
|
|
439
306
|
|
|
440
|
-
//
|
|
441
|
-
// O passo de criação de diretórios ficava aqui em cima e criava `.morph/`
|
|
442
|
-
// ANTES de qualquer guarda. Recusar depois de mutar é meio-estado — o mesmo
|
|
443
|
-
// defeito que o `finish --merge` pagou uma vez na v8.24.3 ("valida a raiz
|
|
444
|
-
// ANTES de arquivar").
|
|
307
|
+
// Nenhuma mutação antes do freio: recusar depois de mutar deixa meio-estado.
|
|
445
308
|
const spinner = ora('Checking versions...').start();
|
|
446
309
|
// `cliCheck` é injetável para que o canário de igualdade dry × real não
|
|
447
310
|
// dependa do registro npm; a CLI nunca o passa.
|
|
@@ -450,7 +313,6 @@ export async function updateCommand(options = {}) {
|
|
|
450
313
|
const registers = await readVersionRegisters(targetPath);
|
|
451
314
|
spinner.stop();
|
|
452
315
|
|
|
453
|
-
// Display version info
|
|
454
316
|
logger.blank();
|
|
455
317
|
logger.info('Version Status:');
|
|
456
318
|
logger.dim(` CLI version: ${cliCheck.current}`);
|
|
@@ -467,10 +329,7 @@ export async function updateCommand(options = {}) {
|
|
|
467
329
|
}
|
|
468
330
|
logger.blank();
|
|
469
331
|
|
|
470
|
-
//
|
|
471
|
-
// O `--dry-run` PASSA POR CIMA desta saída antecipada: bloquear o relatório
|
|
472
|
-
// justamente quando o usuário está decidindo se atualiza seria inútil. O
|
|
473
|
-
// aviso vira a primeira linha do relatório.
|
|
332
|
+
// O `--dry-run` passa por cima desta saída antecipada: o aviso vira a 1ª linha do relatório.
|
|
474
333
|
if (cliCheck.isOutdated && cliCheck.latest) {
|
|
475
334
|
if (dryRun) {
|
|
476
335
|
notices.push(`A CLI local está desatualizada (${cliCheck.current} → ${cliCheck.latest}). `
|
|
@@ -500,7 +359,6 @@ export async function updateCommand(options = {}) {
|
|
|
500
359
|
}
|
|
501
360
|
}
|
|
502
361
|
|
|
503
|
-
// ── Freio de major, ANTES de qualquer mutação ────────────────────────────
|
|
504
362
|
const needsInitialization = !(await pathExists(morphPath));
|
|
505
363
|
const targetVersion = cliCheck.current ?? getInstalledCLIVersion();
|
|
506
364
|
const verdict = needsInitialization
|
|
@@ -514,8 +372,7 @@ export async function updateCommand(options = {}) {
|
|
|
514
372
|
if (force) {
|
|
515
373
|
notices.push(`Freio de major DISPENSADO por \`--force\`: ${lines[0]}`);
|
|
516
374
|
} else if (dryRun) {
|
|
517
|
-
//
|
|
518
|
-
// relatório seria esconder justamente a informação que motivou o comando.
|
|
375
|
+
// Em dry não há recusa: esconder o relatório esconderia a informação que motivou o comando.
|
|
519
376
|
notices.push(...lines);
|
|
520
377
|
} else {
|
|
521
378
|
logger.error(lines[0]);
|
|
@@ -531,7 +388,6 @@ export async function updateCommand(options = {}) {
|
|
|
531
388
|
+ `Quem decide é o primeiro.`);
|
|
532
389
|
}
|
|
533
390
|
|
|
534
|
-
// ── A partir daqui, tudo passa pelo coletor ──────────────────────────────
|
|
535
391
|
if (needsInitialization) {
|
|
536
392
|
logger.warn('MORPH not fully initialized. Creating basic structure...');
|
|
537
393
|
fx.ensureDir(join(morphPath, 'context'));
|
|
@@ -541,19 +397,16 @@ export async function updateCommand(options = {}) {
|
|
|
541
397
|
logger.dim(' ✓ Created .morph directory structure');
|
|
542
398
|
}
|
|
543
399
|
|
|
544
|
-
// Proceed with update
|
|
545
400
|
const updateSpinner = ora(dryRun ? 'Prevendo efeitos...' : 'Updating MORPH-SPEC...').start();
|
|
546
401
|
let cleaned = [];
|
|
547
402
|
|
|
548
403
|
try {
|
|
549
|
-
// Backup user config before cleaning
|
|
550
404
|
updateSpinner.text = 'Backing up user configuration...';
|
|
551
405
|
const configBackup = await backupUserConfig(morphPath);
|
|
552
406
|
if (configBackup) {
|
|
553
407
|
logger.dim(' ✓ User config backed up');
|
|
554
408
|
}
|
|
555
409
|
|
|
556
|
-
// Clean framework directories (preserve .morph/project/)
|
|
557
410
|
updateSpinner.text = 'Cleaning old framework files...';
|
|
558
411
|
cleaned = cleanFrameworkDirs(morphPath, targetPath, fx);
|
|
559
412
|
logger.dim(' ✓ Old framework files removed');
|
|
@@ -561,9 +414,6 @@ export async function updateCommand(options = {}) {
|
|
|
561
414
|
const updateTemplates = !options.standards || options.templates;
|
|
562
415
|
const updateStandards = !options.templates || options.standards;
|
|
563
416
|
|
|
564
|
-
// Templates are no longer copied to .morph/framework/templates/ (Task 2,
|
|
565
|
-
// R3 perf plan) — see reconcileLegacyTemplatesCopy()'s doc comment for
|
|
566
|
-
// why. Only reconcile a leftover copy from a pre-R3 install.
|
|
567
417
|
let templatesReconcile = 'absent';
|
|
568
418
|
if (updateTemplates) {
|
|
569
419
|
updateSpinner.text = 'Checking for a legacy templates copy...';
|
|
@@ -571,7 +421,6 @@ export async function updateCommand(options = {}) {
|
|
|
571
421
|
templatesReconcile = reconcileLegacyTemplatesCopy(templatesDest, projectCheck.current, fx);
|
|
572
422
|
}
|
|
573
423
|
|
|
574
|
-
// Update standards
|
|
575
424
|
if (updateStandards) {
|
|
576
425
|
updateSpinner.text = 'Updating standards...';
|
|
577
426
|
const standardsSrc = join(contentDir, 'framework', 'standards');
|
|
@@ -579,7 +428,6 @@ export async function updateCommand(options = {}) {
|
|
|
579
428
|
fx.copyDir(standardsSrc, standardsDest);
|
|
580
429
|
}
|
|
581
430
|
|
|
582
|
-
// Update hooks (runtime hook scripts under .morph/framework/hooks/)
|
|
583
431
|
updateSpinner.text = 'Updating hooks...';
|
|
584
432
|
const hooksSrc = join(__dirname, '..', '..', '..', 'framework', 'hooks');
|
|
585
433
|
const hooksDest = join(morphPath, 'framework', 'hooks');
|
|
@@ -587,7 +435,6 @@ export async function updateCommand(options = {}) {
|
|
|
587
435
|
fx.copyDir(hooksSrc, hooksDest);
|
|
588
436
|
}
|
|
589
437
|
|
|
590
|
-
// Update agents.json (sourced from framework/ — canonical single source of truth)
|
|
591
438
|
updateSpinner.text = 'Updating agents configuration...';
|
|
592
439
|
const agentsSrc = join(__dirname, '..', '..', '..', 'framework', 'agents.json');
|
|
593
440
|
const agentsDest = join(morphPath, 'framework', 'agents.json');
|
|
@@ -595,7 +442,6 @@ export async function updateCommand(options = {}) {
|
|
|
595
442
|
fx.copyFile(agentsSrc, agentsDest);
|
|
596
443
|
}
|
|
597
444
|
|
|
598
|
-
// Paridade com setup-infra 7c/8b/8c/8d: sem isto o charter e os evals instalados ficam velhos.
|
|
599
445
|
updateSpinner.text = 'Updating charter, schemas and evals...';
|
|
600
446
|
const canonicalFrameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
601
447
|
for (const file of ['MORPH.md', 'phases.json']) {
|
|
@@ -610,18 +456,13 @@ export async function updateCommand(options = {}) {
|
|
|
610
456
|
fx.copyDir(src, join(morphPath, 'framework', dir));
|
|
611
457
|
}
|
|
612
458
|
}
|
|
613
|
-
// CLI.md
|
|
614
|
-
// .morph/framework/CLI.md (o CLAUDE.md residente aponta para lá) —
|
|
615
|
-
// por isso não entra no loop de nomes acima.
|
|
459
|
+
// CLI.md instala achatado em .morph/framework/CLI.md, por isso fica fora do loop acima.
|
|
616
460
|
const cliMdSrc = join(canonicalFrameworkDir, 'docs', 'CLI.md');
|
|
617
461
|
if (await pathExists(cliMdSrc)) {
|
|
618
462
|
fx.copyFile(cliMdSrc, join(morphPath, 'framework', 'CLI.md'));
|
|
619
463
|
}
|
|
620
464
|
|
|
621
|
-
//
|
|
622
|
-
// Escreve DENTRO do pacote da CLI — num `npm link` de desenvolvimento, no
|
|
623
|
-
// próprio repositório do framework. Em dry a variante `--check` roda no
|
|
624
|
-
// lugar: ela verifica e não escreve.
|
|
465
|
+
// Escreve DENTRO do pacote da CLI (o repositório do framework num `npm link`); em dry roda `--check`.
|
|
625
466
|
updateSpinner.text = 'Syncing generated refs...';
|
|
626
467
|
try {
|
|
627
468
|
const generateScript = join(__dirname, '..', '..', '..', 'scripts', 'generate-refs.js');
|
|
@@ -634,24 +475,18 @@ export async function updateCommand(options = {}) {
|
|
|
634
475
|
}
|
|
635
476
|
}
|
|
636
477
|
} catch {
|
|
637
|
-
// Non-fatal: generator unavailable or files already in sync
|
|
638
478
|
}
|
|
639
479
|
|
|
640
|
-
// Update .claude commands and skills
|
|
641
|
-
// Source: framework/ (canonical for all stacks)
|
|
642
480
|
updateSpinner.text = 'Setting up Claude Code integration...';
|
|
643
481
|
const frameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
644
482
|
const claudeDest = join(targetPath, '.claude');
|
|
645
483
|
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
// above and restoreUserConfig() only runs at the end — re-reading the file here
|
|
649
|
-
// yields [] and silently downgrades the whole install to universal-only assets
|
|
650
|
-
// (the ".claude/agents/ only has morph-evaluator.md after update" bug).
|
|
484
|
+
// MUST come from the pre-clean backup: cleanFrameworkDirs() deleted .morph/config/ and re-reading
|
|
485
|
+
// it here yields [], silently downgrading the install to universal-only assets.
|
|
651
486
|
let projectTags = [];
|
|
652
487
|
try {
|
|
653
488
|
if (configBackup) projectTags = getProjectTags(configBackup);
|
|
654
|
-
} catch {
|
|
489
|
+
} catch {}
|
|
655
490
|
|
|
656
491
|
let commandsCopied = false;
|
|
657
492
|
const commandsSrc = join(frameworkDir, 'commands');
|
|
@@ -664,18 +499,13 @@ export async function updateCommand(options = {}) {
|
|
|
664
499
|
logger.warn(' ⚠ framework/commands/ source missing — commands not updated');
|
|
665
500
|
}
|
|
666
501
|
|
|
667
|
-
// Sync morph skills to .claude/skills/ for native Claude Code discovery
|
|
668
502
|
updateSpinner.text = 'Syncing morph skills to .claude/skills/...';
|
|
669
503
|
const skillsResult = await installSkills(targetPath, { projectTags, fx });
|
|
670
504
|
|
|
671
|
-
// Sync native subagents to .claude/agents/
|
|
672
505
|
updateSpinner.text = 'Syncing agents to .claude/agents/...';
|
|
673
506
|
const agentsResult = await installAgents(targetPath, frameworkDir, { projectTags, fx });
|
|
674
507
|
await installDomainAgents(targetPath, frameworkDir, { projectTags, fx });
|
|
675
508
|
|
|
676
|
-
// Sync path-scoped rules to .claude/rules/ — stack-filtered with orphan
|
|
677
|
-
// cleanup (parity with setup-infra), so `update` is a true superset of
|
|
678
|
-
// `setup-infra --reinstall` and also covers project.stack changes.
|
|
679
509
|
updateSpinner.text = 'Syncing rules to .claude/rules/...';
|
|
680
510
|
const rulesSrc = join(frameworkDir, 'rules');
|
|
681
511
|
const rulesDest = join(claudeDest, 'rules');
|
|
@@ -685,8 +515,6 @@ export async function updateCommand(options = {}) {
|
|
|
685
515
|
rulesResult = installRulesForStack(rulesSrc, rulesDest, projectTags, fx);
|
|
686
516
|
}
|
|
687
517
|
|
|
688
|
-
// Sync runtime CLAUDE.md to .claude/CLAUDE.md and derive the personas
|
|
689
|
-
// block from what installAgents actually installed (managed markers).
|
|
690
518
|
updateSpinner.text = 'Syncing .claude/CLAUDE.md...';
|
|
691
519
|
const runtimeSrc = join(frameworkDir, 'CLAUDE.md');
|
|
692
520
|
const runtimeDest = join(claudeDest, 'CLAUDE.md');
|
|
@@ -699,45 +527,34 @@ export async function updateCommand(options = {}) {
|
|
|
699
527
|
});
|
|
700
528
|
}
|
|
701
529
|
|
|
702
|
-
// Sync statusline globally to ~/.claude/
|
|
703
530
|
updateSpinner.text = 'Syncing statusline to ~/.claude/...';
|
|
704
531
|
const HOOKS_SRC = join(__dirname, '..', '..', '..', 'framework', 'hooks', 'claude-code');
|
|
705
532
|
try {
|
|
706
533
|
await installGlobalStatusline(HOOKS_SRC, null, { fx });
|
|
707
534
|
} catch {
|
|
708
|
-
// Non-critical: global dir may not be writable in all environments
|
|
709
535
|
logger.dim(' ⚠ Could not install statusline globally (non-critical)');
|
|
710
536
|
}
|
|
711
537
|
|
|
712
|
-
// Update Claude Code hooks in .claude/settings.local.json
|
|
713
538
|
updateSpinner.text = 'Updating Claude Code hooks...';
|
|
714
539
|
const hooksResult = await installClaudeHooks(targetPath, { fx });
|
|
715
540
|
|
|
716
|
-
// Ensure root CLAUDE.md has morph-spec @import (preserves user content)
|
|
717
541
|
updateSpinner.text = 'Checking CLAUDE.md import...';
|
|
718
542
|
const claudeMdDest = join(targetPath, 'CLAUDE.md');
|
|
719
543
|
await injectMorphImport(claudeMdDest, fx);
|
|
720
544
|
|
|
721
|
-
// Restore user config after framework reinstallation
|
|
722
545
|
updateSpinner.text = 'Restoring user configuration...';
|
|
723
546
|
restoreUserConfig(morphPath, targetPath, configBackup, cliCheck.current, fx);
|
|
724
547
|
logger.dim(' ✓ User config restored with updated framework version');
|
|
725
548
|
|
|
726
|
-
// Update .gitignore + .gitattributes with current morph rules
|
|
727
549
|
updateSpinner.text = 'Updating .gitignore + .gitattributes...';
|
|
728
550
|
await updateGitignore(targetPath, fx);
|
|
729
551
|
await ensureGitattributes(targetPath, fx);
|
|
730
552
|
|
|
731
553
|
updateSpinner.text = 'Editor excludes for worktrees (.vscode/settings.json)...';
|
|
732
|
-
try { applyEditorSettings(targetPath, { fx }); } catch {
|
|
733
|
-
|
|
734
|
-
//
|
|
735
|
-
// edited is
|
|
736
|
-
// and fixtures back in the graph — a header-only difference is left alone);
|
|
737
|
-
// an edited one is left alone, with the change to make. Only an existing
|
|
738
|
-
// file — the graph is opt-in, and `graph build` writes it. A tracked file
|
|
739
|
-
// reaches worktrees and teammates only once committed: each checkout reads
|
|
740
|
-
// its own.
|
|
554
|
+
try { applyEditorSettings(targetPath, { fx }); } catch {}
|
|
555
|
+
|
|
556
|
+
// Swap an unedited generated .graphifyignore when its RULES change (header-only diffs are left);
|
|
557
|
+
// an edited one is left alone with the change printed. Only an existing file: the graph is opt-in.
|
|
741
558
|
let graphIgnore = null;
|
|
742
559
|
if (existsSync(join(targetPath, '.graphifyignore'))) {
|
|
743
560
|
updateSpinner.text = 'Checking .graphifyignore...';
|
|
@@ -749,11 +566,8 @@ export async function updateCommand(options = {}) {
|
|
|
749
566
|
}
|
|
750
567
|
}
|
|
751
568
|
|
|
752
|
-
// Migration: untrack a pre-v5 committed state.json so it stops being swept
|
|
753
|
-
// into feature commits. Gitignore alone can't do this for an already-tracked file.
|
|
754
569
|
const untracked = untrackCommittedStateJson(targetPath, fx);
|
|
755
570
|
|
|
756
|
-
// Update .morphversion
|
|
757
571
|
updateSpinner.text = 'Saving version info...';
|
|
758
572
|
await saveProjectMorphVersion(targetPath, cliCheck.current, fx);
|
|
759
573
|
|
|
@@ -761,11 +575,9 @@ export async function updateCommand(options = {}) {
|
|
|
761
575
|
logger.blank();
|
|
762
576
|
|
|
763
577
|
if (!dryRun) {
|
|
764
|
-
//
|
|
765
|
-
// outside the project, so not an effect of the update itself).
|
|
578
|
+
// Outside the project, so not an effect of the update itself.
|
|
766
579
|
registerProject(targetPath, 'update');
|
|
767
580
|
|
|
768
|
-
// Show version change
|
|
769
581
|
if (projectCheck.current && projectCheck.current !== cliCheck.current) {
|
|
770
582
|
logger.success(`Updated: ${projectCheck.current} → ${cliCheck.current}`);
|
|
771
583
|
} else if (!projectCheck.current) {
|
|
@@ -786,10 +598,8 @@ export async function updateCommand(options = {}) {
|
|
|
786
598
|
}
|
|
787
599
|
}
|
|
788
600
|
|
|
789
|
-
// Counts make a silent stack-filter downgrade visible
|
|
790
|
-
//
|
|
791
|
-
// Display only compact tags: project.integrations entries are long
|
|
792
|
-
// free-text descriptions and would flood the summary line.
|
|
601
|
+
// Counts make a silent stack-filter downgrade visible (a dotnet project with "1 persona");
|
|
602
|
+
// only compact tags, since integrations are long free text.
|
|
793
603
|
const compactTags = projectTags.filter(t => !t.includes(' '));
|
|
794
604
|
const tagsLabel = compactTags.length > 0 ? compactTags.join('+') : 'universal only';
|
|
795
605
|
logger.dim(` ✓ .claude/settings.local.json (${hooksResult.installed} hooks installed)`);
|
|
@@ -811,9 +621,7 @@ export async function updateCommand(options = {}) {
|
|
|
811
621
|
logger.info('Your config.json was preserved.');
|
|
812
622
|
}
|
|
813
623
|
|
|
814
|
-
// O resumo sai de `list()` NOS DOIS MODOS
|
|
815
|
-
// ponto de emissão esquecido sumir das duas saídas ao mesmo tempo, em vez
|
|
816
|
-
// de só do `--dry-run`.
|
|
624
|
+
// O resumo sai de `list()` NOS DOIS MODOS: um ponto de emissão esquecido some das duas saídas.
|
|
817
625
|
renderEffectsReport(fx.list(), {
|
|
818
626
|
targetPath, packageRoot: contentDir, dryRun, cleaned, notices,
|
|
819
627
|
});
|