@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +11 -63
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -1
- package/framework/MORPH.md +3 -3
- package/framework/agents.json +19 -11
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +22 -31
- package/framework/evals/review-checklist.md +12 -2
- package/framework/hooks/README.md +8 -44
- 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 +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- 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 +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +12 -68
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- 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.sh +11 -11
- 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 +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- 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 +1 -42
- 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 +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- 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/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/rules/nextjs-standards.md +1 -1
- 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 +4 -4
- package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +9 -7
- package/framework/skills/core/morph-review/SKILL.md +9 -6
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +36 -14
- package/framework/standards/STANDARDS.meta.json +16 -2
- 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/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 +2 -2
- 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 +1 -1
- 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/testing.md +10 -4
- 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/frontend/nextjs/testing.md +10 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/plan.md +3 -1
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- 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 +17 -104
- 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 +5 -57
- 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.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +15 -97
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +11 -11
- package/src/commands/validation/plan-check.js +4 -2
- package/src/commands/validation/tests-audit.js +7 -0
- package/src/commands/validation/validate-feature.js +10 -97
- package/src/commands/validation/verify.js +30 -113
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- 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 +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- 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 +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- 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 +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- 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 +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/dashboard/vite.config.js +24 -24
- 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 +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +14 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/applicability.js +37 -0
- 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 +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- 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 +1 -0
- 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 +3 -15
- 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 +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/adapters.js +6 -17
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/runner.js +23 -75
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/mutation/trx.js +0 -1
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +34 -33
- package/src/lib/plan/plan-parser.js +7 -8
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +11 -26
- package/src/lib/risk-detector.js +41 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/tests-audit/audit.js +96 -0
- 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 +2 -24
- 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 +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +21 -158
- package/src/lib/validators/verify-runner.js +85 -376
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- 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 +13 -95
- 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 -147
- package/src/utils/agents-installer.js +29 -276
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
- package/framework/hooks/git/pre-commit/agents.sh +0 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
- package/framework/hooks/git/pre-push/run-tests.sh +0 -44
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -35,16 +35,10 @@ import { reconcileLegacyTemplatesCopy } from '../../lib/installers/templates-rec
|
|
|
35
35
|
import { createEffects } from '../../lib/installers/effects.js';
|
|
36
36
|
import { applyEditorSettings } from '../../lib/worktree/vscode-settings.js';
|
|
37
37
|
import { registerProject } from '../../lib/projects/registry.js';
|
|
38
|
-
import { findOldFormatFeatures, oldFormatGuardLines } from '../../lib/specs/old-format.js';
|
|
39
38
|
import {
|
|
40
39
|
writeGraphifyignore, describeOutdatedIgnore, graphifyignoreTracked,
|
|
41
40
|
} from '../../lib/graph/graphifyignore.js';
|
|
42
41
|
|
|
43
|
-
/**
|
|
44
|
-
* Backup user's config.json before cleaning
|
|
45
|
-
* @param {string} morphPath - Path to .morph directory
|
|
46
|
-
* @returns {Promise<object|null>} Backup of config.json or null
|
|
47
|
-
*/
|
|
48
42
|
async function backupUserConfig(morphPath) {
|
|
49
43
|
try {
|
|
50
44
|
const configPath = join(morphPath, 'config', 'config.json');
|
|
@@ -53,40 +47,18 @@ async function backupUserConfig(morphPath) {
|
|
|
53
47
|
return config;
|
|
54
48
|
}
|
|
55
49
|
} catch (error) {
|
|
56
|
-
// If backup fails, return null and continue
|
|
57
50
|
}
|
|
58
51
|
return null;
|
|
59
52
|
}
|
|
60
53
|
|
|
61
|
-
/**
|
|
62
|
-
* Os DEZ diretórios que `cleanFrameworkDirs` apaga por inteiro, sem condição.
|
|
63
|
-
*
|
|
64
|
-
* O número está pinado à mão em `test/commands/update-dry-run.test.js`
|
|
65
|
-
* (`DIRETORIOS_APAGADOS_POR_INTEIRO`), com os dez caminhos escritos: derivar a
|
|
66
|
-
* expectativa desta função deixaria a suíte cega a uma entrada removida.
|
|
67
|
-
*
|
|
68
|
-
* Exportado porque o relatório do `--dry-run` é obrigado a nomeá-los: "update"
|
|
69
|
-
* não sugere a ninguém que `.claude/skills/` e `.claude/agents/` são apagados
|
|
70
|
-
* inteiros antes da reinstalação.
|
|
71
|
-
*
|
|
72
|
-
* @param {string} morphPath
|
|
73
|
-
* @param {string} targetPath
|
|
74
|
-
* @returns {string[]}
|
|
75
|
-
*/
|
|
76
54
|
export function frameworkDirsToClean(morphPath, targetPath) {
|
|
77
55
|
const claudeDir = join(targetPath, '.claude');
|
|
78
56
|
return [
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
// reconcileLegacyTemplatesCopy() below, which decides per-run whether a
|
|
82
|
-
// pre-existing local copy is safe to remove or must be preserved as a
|
|
83
|
-
// user override. Blindly deleting it here (the old behavior) destroyed
|
|
84
|
-
// 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.
|
|
85
59
|
join(morphPath, 'framework', 'standards'),
|
|
86
60
|
join(morphPath, 'framework', 'hooks'),
|
|
87
61
|
join(morphPath, 'framework', 'schemas'),
|
|
88
|
-
// Não é mais instalado: fica na lista para apagar o de instalações antigas.
|
|
89
|
-
join(morphPath, 'framework', 'rubrics'),
|
|
90
62
|
join(morphPath, 'framework', 'evals'),
|
|
91
63
|
join(morphPath, 'config'),
|
|
92
64
|
join(claudeDir, 'commands'),
|
|
@@ -96,14 +68,6 @@ export function frameworkDirsToClean(morphPath, targetPath) {
|
|
|
96
68
|
];
|
|
97
69
|
}
|
|
98
70
|
|
|
99
|
-
/**
|
|
100
|
-
* Emite (e, fora do dry, executa) a remoção recursiva de um diretório.
|
|
101
|
-
*
|
|
102
|
-
* Um efeito por arquivo, mais um pelo diretório. É a varredura que a §5.2 do
|
|
103
|
-
* spec exige, e é dela que sai a contagem "N arquivos" do relatório.
|
|
104
|
-
*
|
|
105
|
-
* @returns {number} arquivos varridos
|
|
106
|
-
*/
|
|
107
71
|
function sweepRemove(dir, fx) {
|
|
108
72
|
let entries;
|
|
109
73
|
try {
|
|
@@ -126,23 +90,8 @@ function sweepRemove(dir, fx) {
|
|
|
126
90
|
}
|
|
127
91
|
|
|
128
92
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* **A armadilha que esta função resolve (decisions.md D6).** Ela apaga
|
|
132
|
-
* exatamente os destinos que os laços de limpeza de órfão DEPOIS varrem
|
|
133
|
-
* (`skills-installer.js`, `agents-installer.js`, `installRulesForStack`).
|
|
134
|
-
* Numa execução real esses laços sempre encontram diretório vazio e não
|
|
135
|
-
* apagam nada. Num `--dry-run` ingênuo nada foi apagado, eles encontrariam o
|
|
136
|
-
* diretório POPULADO e o relatório preveria remoções que a execução real
|
|
137
|
-
* nunca faz — errado na direção que assusta o usuário. Como o clean é
|
|
138
|
-
* incondicional e total, a previsão correta é "tudo que hoje está sob estes
|
|
139
|
-
* dez é apagado", e os laços de órfão ficam calados em dry: são provadamente
|
|
140
|
-
* subsumidos.
|
|
141
|
-
*
|
|
142
|
-
* @param {string} morphPath - Path to .morph directory
|
|
143
|
-
* @param {string} targetPath - Project root path
|
|
144
|
-
* @param {ReturnType<import('../../lib/installers/effects.js').createEffects>} fx
|
|
145
|
-
* @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.
|
|
146
95
|
*/
|
|
147
96
|
function cleanFrameworkDirs(morphPath, targetPath, fx) {
|
|
148
97
|
const cleaned = [];
|
|
@@ -152,31 +101,20 @@ function cleanFrameworkDirs(morphPath, targetPath, fx) {
|
|
|
152
101
|
cleaned.push({ dir, files: sweepRemove(dir, fx) });
|
|
153
102
|
}
|
|
154
103
|
} catch (error) {
|
|
155
|
-
// Continue even if cleanup fails for one directory
|
|
156
104
|
}
|
|
157
105
|
}
|
|
158
106
|
return cleaned;
|
|
159
107
|
}
|
|
160
108
|
|
|
161
|
-
/**
|
|
162
|
-
* Restore user's config.json after reinstallation
|
|
163
|
-
* @param {string} morphPath - Path to .morph directory
|
|
164
|
-
* @param {string} targetPath - Project root path
|
|
165
|
-
* @param {object|null} configBackup - Backup of config.json
|
|
166
|
-
* @param {string} currentVersion - Current CLI version
|
|
167
|
-
* @param {ReturnType<import('../../lib/installers/effects.js').createEffects>} fx
|
|
168
|
-
*/
|
|
169
109
|
function restoreUserConfig(morphPath, targetPath, configBackup, currentVersion, fx) {
|
|
170
110
|
const configDir = join(morphPath, 'config');
|
|
171
111
|
fx.ensureDir(configDir);
|
|
172
112
|
const configPath = join(configDir, 'config.json');
|
|
173
113
|
|
|
174
114
|
if (configBackup) {
|
|
175
|
-
// Preserve user data but update framework version
|
|
176
115
|
configBackup.frameworkVersion = currentVersion;
|
|
177
116
|
fx.writeJson(configPath, configBackup);
|
|
178
117
|
} else {
|
|
179
|
-
// Create basic config if none existed
|
|
180
118
|
const dirName = targetPath.split(/[\\/]/).pop();
|
|
181
119
|
const defaultConfig = {
|
|
182
120
|
framework: 'global',
|
|
@@ -190,15 +128,7 @@ function restoreUserConfig(morphPath, targetPath, configBackup, currentVersion,
|
|
|
190
128
|
}
|
|
191
129
|
}
|
|
192
130
|
|
|
193
|
-
/**
|
|
194
|
-
* `.morph/state.json` já está no índice do git?
|
|
195
|
-
*
|
|
196
|
-
* SONDA, separada da mutação de propósito: sondar é ler, e ler acontece nos
|
|
197
|
-
* dois modos. É o que faz previsão e execução concordarem sobre este efeito.
|
|
198
|
-
*
|
|
199
|
-
* @param {string} targetPath
|
|
200
|
-
* @returns {boolean}
|
|
201
|
-
*/
|
|
131
|
+
/** Sonda separada da mutação: ler acontece nos dois modos, e é o que faz previsão e execução concordarem. */
|
|
202
132
|
export function stateJsonIsTracked(targetPath) {
|
|
203
133
|
try {
|
|
204
134
|
const isRepo = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
@@ -215,38 +145,14 @@ export function stateJsonIsTracked(targetPath) {
|
|
|
215
145
|
}
|
|
216
146
|
}
|
|
217
147
|
|
|
218
|
-
/**
|
|
219
|
-
* Untrack an already-committed `.morph/state.json` from git.
|
|
220
|
-
*
|
|
221
|
-
* Before v5's per-feature model, `.morph/state.json` was committed. Adding it to
|
|
222
|
-
* `.gitignore` does NOT stop git from tracking a file already in the index — so on
|
|
223
|
-
* exactly the repos that had the P1 sweep problem, `git commit -a` would keep
|
|
224
|
-
* staging state.json. This runs `git rm --cached` once so the next commit drops it.
|
|
225
|
-
* The working-tree file is left in place (it's rebuilt from feature.json on load).
|
|
226
|
-
*
|
|
227
|
-
* Mildly destructive (mutates the git index), but no data is lost: state.json is a
|
|
228
|
-
* derivable index. Fail-open — a non-git project or any git error is a silent no-op.
|
|
229
|
-
*
|
|
230
|
-
* @param {string} targetPath - Project root
|
|
231
|
-
* @param {ReturnType<import('../../lib/installers/effects.js').createEffects>} fx
|
|
232
|
-
* @returns {boolean} true if state.json was untracked, false otherwise
|
|
233
|
-
*/
|
|
234
148
|
export function untrackCommittedStateJson(targetPath, fx = createEffects({ dryRun: false })) {
|
|
235
|
-
if (!stateJsonIsTracked(targetPath)) return false;
|
|
149
|
+
if (!stateJsonIsTracked(targetPath)) return false;
|
|
236
150
|
return fx.gitIndex('rm --cached --quiet', '.morph/state.json', { cwd: targetPath });
|
|
237
151
|
}
|
|
238
152
|
|
|
239
153
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
* `.morph/.morphversion → morphVersion` é o que o `update` LÊ para decidir.
|
|
243
|
-
* `config.json → frameworkVersion` é o que o `update` ESCREVE e nunca lê para
|
|
244
|
-
* decidir coisa alguma — e no MORPH_OS os dois divergem por um major inteiro
|
|
245
|
-
* (7.5.0 contra 6.0.0). Um freio calibrado no campo errado recusaria (ou
|
|
246
|
-
* liberaria) com base num número que ninguém mantém.
|
|
247
|
-
*
|
|
248
|
-
* @param {string} targetPath
|
|
249
|
-
* @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.
|
|
250
156
|
*/
|
|
251
157
|
export async function readVersionRegisters(targetPath) {
|
|
252
158
|
let morphVersion = null;
|
|
@@ -254,30 +160,17 @@ export async function readVersionRegisters(targetPath) {
|
|
|
254
160
|
try {
|
|
255
161
|
const v = await readJson(join(targetPath, '.morph', '.morphversion'));
|
|
256
162
|
morphVersion = typeof v?.morphVersion === 'string' ? v.morphVersion : null;
|
|
257
|
-
} catch {
|
|
163
|
+
} catch {}
|
|
258
164
|
try {
|
|
259
165
|
const c = await readJson(join(targetPath, '.morph', 'config', 'config.json'));
|
|
260
166
|
frameworkVersion = typeof c?.frameworkVersion === 'string' ? c.frameworkVersion : null;
|
|
261
|
-
} catch {
|
|
167
|
+
} catch {}
|
|
262
168
|
|
|
263
169
|
const diverged = Boolean(morphVersion && frameworkVersion && morphVersion !== frameworkVersion);
|
|
264
170
|
return { morphVersion, frameworkVersion, diverged };
|
|
265
171
|
}
|
|
266
172
|
|
|
267
|
-
/**
|
|
268
|
-
* As linhas da recusa do freio de major.
|
|
269
|
-
*
|
|
270
|
-
* Separada do comando para ser testável sem rodar o `update`, e porque a
|
|
271
|
-
* mensagem carrega três obrigações: a versão instalada, a versão de destino e
|
|
272
|
-
* **o registro de onde a versão instalada foi lida**. Quando os dois registros
|
|
273
|
-
* discordam, imprime OS DOIS e diz qual decidiu — escolher em silêncio é
|
|
274
|
-
* acertar metade das vezes e nunca saber qual metade.
|
|
275
|
-
*
|
|
276
|
-
* @param {{ refuse: boolean, reason: string|null, jump: number|null }} verdict
|
|
277
|
-
* @param {{ morphVersion: string|null, frameworkVersion: string|null, diverged: boolean }} registers
|
|
278
|
-
* @param {string} targetVersion
|
|
279
|
-
* @returns {string[]}
|
|
280
|
-
*/
|
|
173
|
+
/** Quando os dois registros discordam, imprime OS DOIS e diz qual decidiu. */
|
|
281
174
|
export function majorGuardLines(verdict, registers, targetVersion) {
|
|
282
175
|
const lines = [];
|
|
283
176
|
if (verdict.reason === 'unknown-installed') {
|
|
@@ -299,7 +192,6 @@ export function majorGuardLines(verdict, registers, targetVersion) {
|
|
|
299
192
|
return lines;
|
|
300
193
|
}
|
|
301
194
|
|
|
302
|
-
/** Rótulo humano de cada verbo, para agrupar o relatório por natureza do efeito. */
|
|
303
195
|
const VERB_GROUPS = {
|
|
304
196
|
remove: 'Apagar',
|
|
305
197
|
ensureDir: 'Criar diretório',
|
|
@@ -318,20 +210,8 @@ function isUnder(path, root) {
|
|
|
318
210
|
return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
|
|
319
211
|
}
|
|
320
212
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
*
|
|
324
|
-
* Para quase todo verbo é o próprio `target`. Para `spawn` NÃO é: o `target`
|
|
325
|
-
* de um spawn é o binário (`node`), e classificar por ele esconderia
|
|
326
|
-
* exatamente o efeito que a §5.3 obriga a nomear — `generate-refs.js`
|
|
327
|
-
* escrevendo `framework/hooks/shared/phase-utils.js` DENTRO do pacote da CLI.
|
|
328
|
-
*
|
|
329
|
-
* O script sai de `effect.args`, o **array**, nunca de um split do `detail`
|
|
330
|
-
* por espaço: o caminho real deste pacote é
|
|
331
|
-
* `R:\Polymorphism Tech\repos\...` — um espaço no meio, e o split devolvia
|
|
332
|
-
* `Tech\repos\...\generate-refs.js`, que não é subcaminho de nada. O efeito ia
|
|
333
|
-
* calado para o grupo errado. Medido rodando o comando de verdade.
|
|
334
|
-
*/
|
|
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.
|
|
335
215
|
function effectLocation(effect) {
|
|
336
216
|
if (effect.verb !== 'spawn') return effect.target;
|
|
337
217
|
const args = Array.isArray(effect.args) ? effect.args : [];
|
|
@@ -339,26 +219,12 @@ function effectLocation(effect) {
|
|
|
339
219
|
return script || effect.target;
|
|
340
220
|
}
|
|
341
221
|
|
|
342
|
-
/**
|
|
343
|
-
* Imprime o relatório de efeitos.
|
|
344
|
-
*
|
|
345
|
-
* Três grupos ganham nome próprio, porque o usuário não os adivinharia da
|
|
346
|
-
* palavra "update": o que é apagado por INTEIRO antes da reinstalação, o que
|
|
347
|
-
* acontece FORA do projeto, e o que é escrito DENTRO do pacote da CLI (que num
|
|
348
|
-
* `npm link` de desenvolvimento é o próprio repositório do framework).
|
|
349
|
-
*
|
|
350
|
-
* @param {Array} effects - saída de `fx.list()`
|
|
351
|
-
* @param {object} context
|
|
352
|
-
*/
|
|
353
222
|
export function renderEffectsReport(effects, context) {
|
|
354
223
|
const { targetPath, packageRoot, dryRun, cleaned = [], notices = [] } = context;
|
|
355
224
|
const globalClaudeDir = join(homedir(), '.claude');
|
|
356
225
|
|
|
357
226
|
logger.blank();
|
|
358
|
-
//
|
|
359
|
-
// `generate-refs.js` RODA, na variante `--check`, que verifica e não escreve.
|
|
360
|
-
// Prometer silêncio total e rodar um processo é mentir num detalhe pequeno —
|
|
361
|
-
// 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.
|
|
362
228
|
logger.info(dryRun
|
|
363
229
|
? 'Relatório do --dry-run — nada foi escrito, apagado nem removido '
|
|
364
230
|
+ '(o único processo executado é a variante `--check` do gerador, que só verifica):'
|
|
@@ -420,7 +286,7 @@ export function renderEffectsReport(effects, context) {
|
|
|
420
286
|
+ `${outside.length} fora do projeto, ${inPackage.length} dentro do pacote da CLI.`);
|
|
421
287
|
logger.blank();
|
|
422
288
|
|
|
423
|
-
// Risco conhecido, reportado em vez de escondido
|
|
289
|
+
// Risco conhecido, reportado em vez de escondido: o backup
|
|
424
290
|
// de `config.json` só existe em memória durante a execução.
|
|
425
291
|
logger.dim('Nota: durante uma execução real o backup de `.morph/config/config.json` existe apenas '
|
|
426
292
|
+ 'em memória — uma falha no meio do caminho deixa o arquivo apagado. Follow-up nomeado.');
|
|
@@ -438,11 +304,7 @@ export async function updateCommand(options = {}) {
|
|
|
438
304
|
|
|
439
305
|
logger.header(dryRun ? 'MORPH-SPEC Update (--dry-run)' : 'MORPH-SPEC Update');
|
|
440
306
|
|
|
441
|
-
//
|
|
442
|
-
// O passo de criação de diretórios ficava aqui em cima e criava `.morph/`
|
|
443
|
-
// ANTES de qualquer guarda. Recusar depois de mutar é meio-estado — o mesmo
|
|
444
|
-
// defeito que o `finish --merge` pagou uma vez na v8.24.3 ("valida a raiz
|
|
445
|
-
// ANTES de arquivar").
|
|
307
|
+
// Nenhuma mutação antes do freio: recusar depois de mutar deixa meio-estado.
|
|
446
308
|
const spinner = ora('Checking versions...').start();
|
|
447
309
|
// `cliCheck` é injetável para que o canário de igualdade dry × real não
|
|
448
310
|
// dependa do registro npm; a CLI nunca o passa.
|
|
@@ -451,7 +313,6 @@ export async function updateCommand(options = {}) {
|
|
|
451
313
|
const registers = await readVersionRegisters(targetPath);
|
|
452
314
|
spinner.stop();
|
|
453
315
|
|
|
454
|
-
// Display version info
|
|
455
316
|
logger.blank();
|
|
456
317
|
logger.info('Version Status:');
|
|
457
318
|
logger.dim(` CLI version: ${cliCheck.current}`);
|
|
@@ -468,10 +329,7 @@ export async function updateCommand(options = {}) {
|
|
|
468
329
|
}
|
|
469
330
|
logger.blank();
|
|
470
331
|
|
|
471
|
-
//
|
|
472
|
-
// O `--dry-run` PASSA POR CIMA desta saída antecipada: bloquear o relatório
|
|
473
|
-
// justamente quando o usuário está decidindo se atualiza seria inútil. O
|
|
474
|
-
// 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.
|
|
475
333
|
if (cliCheck.isOutdated && cliCheck.latest) {
|
|
476
334
|
if (dryRun) {
|
|
477
335
|
notices.push(`A CLI local está desatualizada (${cliCheck.current} → ${cliCheck.latest}). `
|
|
@@ -501,7 +359,6 @@ export async function updateCommand(options = {}) {
|
|
|
501
359
|
}
|
|
502
360
|
}
|
|
503
361
|
|
|
504
|
-
// ── Freio de major, ANTES de qualquer mutação ────────────────────────────
|
|
505
362
|
const needsInitialization = !(await pathExists(morphPath));
|
|
506
363
|
const targetVersion = cliCheck.current ?? getInstalledCLIVersion();
|
|
507
364
|
const verdict = needsInitialization
|
|
@@ -515,8 +372,7 @@ export async function updateCommand(options = {}) {
|
|
|
515
372
|
if (force) {
|
|
516
373
|
notices.push(`Freio de major DISPENSADO por \`--force\`: ${lines[0]}`);
|
|
517
374
|
} else if (dryRun) {
|
|
518
|
-
//
|
|
519
|
-
// 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.
|
|
520
376
|
notices.push(...lines);
|
|
521
377
|
} else {
|
|
522
378
|
logger.error(lines[0]);
|
|
@@ -532,22 +388,6 @@ export async function updateCommand(options = {}) {
|
|
|
532
388
|
+ `Quem decide é o primeiro.`);
|
|
533
389
|
}
|
|
534
390
|
|
|
535
|
-
// `--force` não dispensa: esta CLI não sabe conduzir essas features.
|
|
536
|
-
const oldFormat = needsInitialization ? [] : findOldFormatFeatures(targetPath);
|
|
537
|
-
if (oldFormat.length) {
|
|
538
|
-
const lines = oldFormatGuardLines(oldFormat, targetPath);
|
|
539
|
-
if (dryRun) {
|
|
540
|
-
notices.push(...lines);
|
|
541
|
-
} else {
|
|
542
|
-
logger.error(lines[0]);
|
|
543
|
-
for (const l of lines.slice(1)) logger.dim(l);
|
|
544
|
-
logger.blank();
|
|
545
|
-
process.exitCode = 1;
|
|
546
|
-
return { refused: true, reason: 'old-format-features', features: oldFormat, effects: fx.list() };
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// ── A partir daqui, tudo passa pelo coletor ──────────────────────────────
|
|
551
391
|
if (needsInitialization) {
|
|
552
392
|
logger.warn('MORPH not fully initialized. Creating basic structure...');
|
|
553
393
|
fx.ensureDir(join(morphPath, 'context'));
|
|
@@ -557,19 +397,16 @@ export async function updateCommand(options = {}) {
|
|
|
557
397
|
logger.dim(' ✓ Created .morph directory structure');
|
|
558
398
|
}
|
|
559
399
|
|
|
560
|
-
// Proceed with update
|
|
561
400
|
const updateSpinner = ora(dryRun ? 'Prevendo efeitos...' : 'Updating MORPH-SPEC...').start();
|
|
562
401
|
let cleaned = [];
|
|
563
402
|
|
|
564
403
|
try {
|
|
565
|
-
// Backup user config before cleaning
|
|
566
404
|
updateSpinner.text = 'Backing up user configuration...';
|
|
567
405
|
const configBackup = await backupUserConfig(morphPath);
|
|
568
406
|
if (configBackup) {
|
|
569
407
|
logger.dim(' ✓ User config backed up');
|
|
570
408
|
}
|
|
571
409
|
|
|
572
|
-
// Clean framework directories (preserve .morph/project/)
|
|
573
410
|
updateSpinner.text = 'Cleaning old framework files...';
|
|
574
411
|
cleaned = cleanFrameworkDirs(morphPath, targetPath, fx);
|
|
575
412
|
logger.dim(' ✓ Old framework files removed');
|
|
@@ -577,9 +414,6 @@ export async function updateCommand(options = {}) {
|
|
|
577
414
|
const updateTemplates = !options.standards || options.templates;
|
|
578
415
|
const updateStandards = !options.templates || options.standards;
|
|
579
416
|
|
|
580
|
-
// Templates are no longer copied to .morph/framework/templates/ (Task 2,
|
|
581
|
-
// R3 perf plan) — see reconcileLegacyTemplatesCopy()'s doc comment for
|
|
582
|
-
// why. Only reconcile a leftover copy from a pre-R3 install.
|
|
583
417
|
let templatesReconcile = 'absent';
|
|
584
418
|
if (updateTemplates) {
|
|
585
419
|
updateSpinner.text = 'Checking for a legacy templates copy...';
|
|
@@ -587,7 +421,6 @@ export async function updateCommand(options = {}) {
|
|
|
587
421
|
templatesReconcile = reconcileLegacyTemplatesCopy(templatesDest, projectCheck.current, fx);
|
|
588
422
|
}
|
|
589
423
|
|
|
590
|
-
// Update standards
|
|
591
424
|
if (updateStandards) {
|
|
592
425
|
updateSpinner.text = 'Updating standards...';
|
|
593
426
|
const standardsSrc = join(contentDir, 'framework', 'standards');
|
|
@@ -595,7 +428,6 @@ export async function updateCommand(options = {}) {
|
|
|
595
428
|
fx.copyDir(standardsSrc, standardsDest);
|
|
596
429
|
}
|
|
597
430
|
|
|
598
|
-
// Update hooks (runtime hook scripts under .morph/framework/hooks/)
|
|
599
431
|
updateSpinner.text = 'Updating hooks...';
|
|
600
432
|
const hooksSrc = join(__dirname, '..', '..', '..', 'framework', 'hooks');
|
|
601
433
|
const hooksDest = join(morphPath, 'framework', 'hooks');
|
|
@@ -603,7 +435,6 @@ export async function updateCommand(options = {}) {
|
|
|
603
435
|
fx.copyDir(hooksSrc, hooksDest);
|
|
604
436
|
}
|
|
605
437
|
|
|
606
|
-
// Update agents.json (sourced from framework/ — canonical single source of truth)
|
|
607
438
|
updateSpinner.text = 'Updating agents configuration...';
|
|
608
439
|
const agentsSrc = join(__dirname, '..', '..', '..', 'framework', 'agents.json');
|
|
609
440
|
const agentsDest = join(morphPath, 'framework', 'agents.json');
|
|
@@ -611,7 +442,6 @@ export async function updateCommand(options = {}) {
|
|
|
611
442
|
fx.copyFile(agentsSrc, agentsDest);
|
|
612
443
|
}
|
|
613
444
|
|
|
614
|
-
// Paridade com setup-infra 7c/8b/8c/8d: sem isto o charter e os evals instalados ficam velhos.
|
|
615
445
|
updateSpinner.text = 'Updating charter, schemas and evals...';
|
|
616
446
|
const canonicalFrameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
617
447
|
for (const file of ['MORPH.md', 'phases.json']) {
|
|
@@ -626,18 +456,13 @@ export async function updateCommand(options = {}) {
|
|
|
626
456
|
fx.copyDir(src, join(morphPath, 'framework', dir));
|
|
627
457
|
}
|
|
628
458
|
}
|
|
629
|
-
// CLI.md
|
|
630
|
-
// .morph/framework/CLI.md (o CLAUDE.md residente aponta para lá) —
|
|
631
|
-
// 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.
|
|
632
460
|
const cliMdSrc = join(canonicalFrameworkDir, 'docs', 'CLI.md');
|
|
633
461
|
if (await pathExists(cliMdSrc)) {
|
|
634
462
|
fx.copyFile(cliMdSrc, join(morphPath, 'framework', 'CLI.md'));
|
|
635
463
|
}
|
|
636
464
|
|
|
637
|
-
//
|
|
638
|
-
// Escreve DENTRO do pacote da CLI — num `npm link` de desenvolvimento, no
|
|
639
|
-
// próprio repositório do framework. Em dry a variante `--check` roda no
|
|
640
|
-
// 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`.
|
|
641
466
|
updateSpinner.text = 'Syncing generated refs...';
|
|
642
467
|
try {
|
|
643
468
|
const generateScript = join(__dirname, '..', '..', '..', 'scripts', 'generate-refs.js');
|
|
@@ -650,24 +475,18 @@ export async function updateCommand(options = {}) {
|
|
|
650
475
|
}
|
|
651
476
|
}
|
|
652
477
|
} catch {
|
|
653
|
-
// Non-fatal: generator unavailable or files already in sync
|
|
654
478
|
}
|
|
655
479
|
|
|
656
|
-
// Update .claude commands and skills
|
|
657
|
-
// Source: framework/ (canonical for all stacks)
|
|
658
480
|
updateSpinner.text = 'Setting up Claude Code integration...';
|
|
659
481
|
const frameworkDir = join(__dirname, '..', '..', '..', 'framework');
|
|
660
482
|
const claudeDest = join(targetPath, '.claude');
|
|
661
483
|
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
// above and restoreUserConfig() only runs at the end — re-reading the file here
|
|
665
|
-
// yields [] and silently downgrades the whole install to universal-only assets
|
|
666
|
-
// (the ".claude/agents/ only has morph-verifier.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.
|
|
667
486
|
let projectTags = [];
|
|
668
487
|
try {
|
|
669
488
|
if (configBackup) projectTags = getProjectTags(configBackup);
|
|
670
|
-
} catch {
|
|
489
|
+
} catch {}
|
|
671
490
|
|
|
672
491
|
let commandsCopied = false;
|
|
673
492
|
const commandsSrc = join(frameworkDir, 'commands');
|
|
@@ -680,18 +499,13 @@ export async function updateCommand(options = {}) {
|
|
|
680
499
|
logger.warn(' ⚠ framework/commands/ source missing — commands not updated');
|
|
681
500
|
}
|
|
682
501
|
|
|
683
|
-
// Sync morph skills to .claude/skills/ for native Claude Code discovery
|
|
684
502
|
updateSpinner.text = 'Syncing morph skills to .claude/skills/...';
|
|
685
503
|
const skillsResult = await installSkills(targetPath, { projectTags, fx });
|
|
686
504
|
|
|
687
|
-
// Sync native subagents to .claude/agents/
|
|
688
505
|
updateSpinner.text = 'Syncing agents to .claude/agents/...';
|
|
689
506
|
const agentsResult = await installAgents(targetPath, frameworkDir, { projectTags, fx });
|
|
690
507
|
await installDomainAgents(targetPath, frameworkDir, { projectTags, fx });
|
|
691
508
|
|
|
692
|
-
// Sync path-scoped rules to .claude/rules/ — stack-filtered with orphan
|
|
693
|
-
// cleanup (parity with setup-infra), so `update` is a true superset of
|
|
694
|
-
// `setup-infra --reinstall` and also covers project.stack changes.
|
|
695
509
|
updateSpinner.text = 'Syncing rules to .claude/rules/...';
|
|
696
510
|
const rulesSrc = join(frameworkDir, 'rules');
|
|
697
511
|
const rulesDest = join(claudeDest, 'rules');
|
|
@@ -701,8 +515,6 @@ export async function updateCommand(options = {}) {
|
|
|
701
515
|
rulesResult = installRulesForStack(rulesSrc, rulesDest, projectTags, fx);
|
|
702
516
|
}
|
|
703
517
|
|
|
704
|
-
// Sync runtime CLAUDE.md to .claude/CLAUDE.md and derive the personas
|
|
705
|
-
// block from what installAgents actually installed (managed markers).
|
|
706
518
|
updateSpinner.text = 'Syncing .claude/CLAUDE.md...';
|
|
707
519
|
const runtimeSrc = join(frameworkDir, 'CLAUDE.md');
|
|
708
520
|
const runtimeDest = join(claudeDest, 'CLAUDE.md');
|
|
@@ -715,45 +527,34 @@ export async function updateCommand(options = {}) {
|
|
|
715
527
|
});
|
|
716
528
|
}
|
|
717
529
|
|
|
718
|
-
// Sync statusline globally to ~/.claude/
|
|
719
530
|
updateSpinner.text = 'Syncing statusline to ~/.claude/...';
|
|
720
531
|
const HOOKS_SRC = join(__dirname, '..', '..', '..', 'framework', 'hooks', 'claude-code');
|
|
721
532
|
try {
|
|
722
533
|
await installGlobalStatusline(HOOKS_SRC, null, { fx });
|
|
723
534
|
} catch {
|
|
724
|
-
// Non-critical: global dir may not be writable in all environments
|
|
725
535
|
logger.dim(' ⚠ Could not install statusline globally (non-critical)');
|
|
726
536
|
}
|
|
727
537
|
|
|
728
|
-
// Update Claude Code hooks in .claude/settings.local.json
|
|
729
538
|
updateSpinner.text = 'Updating Claude Code hooks...';
|
|
730
539
|
const hooksResult = await installClaudeHooks(targetPath, { fx });
|
|
731
540
|
|
|
732
|
-
// Ensure root CLAUDE.md has morph-spec @import (preserves user content)
|
|
733
541
|
updateSpinner.text = 'Checking CLAUDE.md import...';
|
|
734
542
|
const claudeMdDest = join(targetPath, 'CLAUDE.md');
|
|
735
543
|
await injectMorphImport(claudeMdDest, fx);
|
|
736
544
|
|
|
737
|
-
// Restore user config after framework reinstallation
|
|
738
545
|
updateSpinner.text = 'Restoring user configuration...';
|
|
739
546
|
restoreUserConfig(morphPath, targetPath, configBackup, cliCheck.current, fx);
|
|
740
547
|
logger.dim(' ✓ User config restored with updated framework version');
|
|
741
548
|
|
|
742
|
-
// Update .gitignore + .gitattributes with current morph rules
|
|
743
549
|
updateSpinner.text = 'Updating .gitignore + .gitattributes...';
|
|
744
550
|
await updateGitignore(targetPath, fx);
|
|
745
551
|
await ensureGitattributes(targetPath, fx);
|
|
746
552
|
|
|
747
553
|
updateSpinner.text = 'Editor excludes for worktrees (.vscode/settings.json)...';
|
|
748
|
-
try { applyEditorSettings(targetPath, { fx }); } catch {
|
|
749
|
-
|
|
750
|
-
//
|
|
751
|
-
// edited is
|
|
752
|
-
// and fixtures back in the graph — a header-only difference is left alone);
|
|
753
|
-
// an edited one is left alone, with the change to make. Only an existing
|
|
754
|
-
// file — the graph is opt-in, and `graph build` writes it. A tracked file
|
|
755
|
-
// reaches worktrees and teammates only once committed: each checkout reads
|
|
756
|
-
// 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.
|
|
757
558
|
let graphIgnore = null;
|
|
758
559
|
if (existsSync(join(targetPath, '.graphifyignore'))) {
|
|
759
560
|
updateSpinner.text = 'Checking .graphifyignore...';
|
|
@@ -765,11 +566,8 @@ export async function updateCommand(options = {}) {
|
|
|
765
566
|
}
|
|
766
567
|
}
|
|
767
568
|
|
|
768
|
-
// Migration: untrack a pre-v5 committed state.json so it stops being swept
|
|
769
|
-
// into feature commits. Gitignore alone can't do this for an already-tracked file.
|
|
770
569
|
const untracked = untrackCommittedStateJson(targetPath, fx);
|
|
771
570
|
|
|
772
|
-
// Update .morphversion
|
|
773
571
|
updateSpinner.text = 'Saving version info...';
|
|
774
572
|
await saveProjectMorphVersion(targetPath, cliCheck.current, fx);
|
|
775
573
|
|
|
@@ -777,11 +575,9 @@ export async function updateCommand(options = {}) {
|
|
|
777
575
|
logger.blank();
|
|
778
576
|
|
|
779
577
|
if (!dryRun) {
|
|
780
|
-
//
|
|
781
|
-
// outside the project, so not an effect of the update itself).
|
|
578
|
+
// Outside the project, so not an effect of the update itself.
|
|
782
579
|
registerProject(targetPath, 'update');
|
|
783
580
|
|
|
784
|
-
// Show version change
|
|
785
581
|
if (projectCheck.current && projectCheck.current !== cliCheck.current) {
|
|
786
582
|
logger.success(`Updated: ${projectCheck.current} → ${cliCheck.current}`);
|
|
787
583
|
} else if (!projectCheck.current) {
|
|
@@ -802,10 +598,8 @@ export async function updateCommand(options = {}) {
|
|
|
802
598
|
}
|
|
803
599
|
}
|
|
804
600
|
|
|
805
|
-
// Counts make a silent stack-filter downgrade visible
|
|
806
|
-
//
|
|
807
|
-
// Display only compact tags: project.integrations entries are long
|
|
808
|
-
// 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.
|
|
809
603
|
const compactTags = projectTags.filter(t => !t.includes(' '));
|
|
810
604
|
const tagsLabel = compactTags.length > 0 ? compactTags.join('+') : 'universal only';
|
|
811
605
|
logger.dim(` ✓ .claude/settings.local.json (${hooksResult.installed} hooks installed)`);
|
|
@@ -827,9 +621,7 @@ export async function updateCommand(options = {}) {
|
|
|
827
621
|
logger.info('Your config.json was preserved.');
|
|
828
622
|
}
|
|
829
623
|
|
|
830
|
-
// O resumo sai de `list()` NOS DOIS MODOS
|
|
831
|
-
// ponto de emissão esquecido sumir das duas saídas ao mesmo tempo, em vez
|
|
832
|
-
// 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.
|
|
833
625
|
renderEffectsReport(fx.list(), {
|
|
834
626
|
targetPath, packageRoot: contentDir, dryRun, cleaned, notices,
|
|
835
627
|
});
|