@polymorphism-tech/morph-spec 8.47.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skills Installer
|
|
3
|
-
*
|
|
4
|
-
* Copies framework skills to .claude/skills/ during morph-spec init.
|
|
5
|
-
* Installs only the `core/` skill set — the canonical NLH skills referenced by
|
|
6
|
-
* MORPH.md (morph-brainstorming, morph-stack-scan, morph-plan, morph-implement,
|
|
7
|
-
* morph-review, morph-standards). The 3-tier system was collapsed
|
|
8
|
-
* into a single source of truth in framework/skills/core/.
|
|
9
|
-
*
|
|
10
|
-
* Source skills are directory-based: framework/skills/core/{skill-name}/SKILL.md
|
|
11
|
-
* (plus optional scripts/, references/, assets/ subdirectories).
|
|
12
|
-
*
|
|
13
|
-
* Directory-based skills have their entire directory copied so Claude Code can
|
|
14
|
-
* execute scripts and load references on demand.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
1
|
import { existsSync, readdirSync, statSync, readFileSync } from 'fs';
|
|
18
2
|
import { basename, join } from 'path';
|
|
19
3
|
import { fileURLToPath } from 'url';
|
|
@@ -23,59 +7,16 @@ import { realEffects } from '../lib/installers/effects.js';
|
|
|
23
7
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
24
8
|
const FRAMEWORK_SKILLS_DIR = join(__dirname, '..', '..', 'framework', 'skills');
|
|
25
9
|
|
|
26
|
-
/**
|
|
27
|
-
* Skill levels to install in .claude/skills/.
|
|
28
|
-
* Only `core/` survives — single source of truth aligned with MORPH.md.
|
|
29
|
-
*/
|
|
30
10
|
const SKILL_LEVELS_TO_INSTALL = ['core'];
|
|
31
11
|
|
|
32
|
-
/**
|
|
33
|
-
* Check if a skill should be installed based on its stacks: frontmatter.
|
|
34
|
-
* @param {string} skillContent - Raw SKILL.md content
|
|
35
|
-
* @param {string[]} projectTags - Stack tags from config
|
|
36
|
-
* @returns {boolean}
|
|
37
|
-
*/
|
|
38
12
|
export function shouldInstallSkill(skillContent, projectTags) {
|
|
39
13
|
return shouldInstall(parseStacks(skillContent), projectTags);
|
|
40
14
|
}
|
|
41
15
|
|
|
42
|
-
/**
|
|
43
|
-
* Copy a skill directory tree from src to dest, skipping README.md files.
|
|
44
|
-
*
|
|
45
|
-
* UM efeito por skill (não um por arquivo): é assim que o instalador pensa
|
|
46
|
-
* ("instalar a skill X"), e é o que o relatório do `--dry-run` precisa dizer.
|
|
47
|
-
*
|
|
48
|
-
* @param {string} src - Source directory path
|
|
49
|
-
* @param {string} dest - Destination directory path
|
|
50
|
-
* @param {ReturnType<import('../lib/installers/effects.js').createEffects>} fx
|
|
51
|
-
*/
|
|
52
16
|
function copySkillDirectory(src, dest, fx) {
|
|
53
17
|
fx.copyDir(src, dest, { filter: (from) => basename(from) !== 'README.md' });
|
|
54
18
|
}
|
|
55
19
|
|
|
56
|
-
/**
|
|
57
|
-
* Walk srcDir and install skills into destDir.
|
|
58
|
-
*
|
|
59
|
-
* Handles two source formats:
|
|
60
|
-
* 1. Directory-based skill: srcDir/{skill-name}/SKILL.md
|
|
61
|
-
* → copies entire directory (SKILL.md + scripts/ + references/ + assets/)
|
|
62
|
-
* → destDir/{skill-name}/
|
|
63
|
-
*
|
|
64
|
-
* 2. Flat .md skill (legacy): srcDir/{skill-name}.md
|
|
65
|
-
* → destDir/{skill-name}/SKILL.md
|
|
66
|
-
*
|
|
67
|
-
* The `core/` level directory is traversed recursively. A directory is
|
|
68
|
-
* recognized as a skill directory when it contains a SKILL.md file.
|
|
69
|
-
*
|
|
70
|
-
* @param {string} srcDir - Source directory to walk
|
|
71
|
-
* @param {string} destDir - Destination base directory (.claude/skills/)
|
|
72
|
-
* @param {string[]} projectTags - Stack tags from config
|
|
73
|
-
* @param {Set<string>} installedNow - Accumulator: skill names this call
|
|
74
|
-
* actually (re)installed. NOT derived by re-scanning destDir afterwards —
|
|
75
|
-
* a disk scan can't distinguish "installed this run" from "leftover from a
|
|
76
|
-
* previous run with different projectTags", which is exactly the stale
|
|
77
|
-
* entry a stack change needs cleaned up.
|
|
78
|
-
*/
|
|
79
20
|
function installSkillsFromDir(srcDir, destDir, projectTags, installedNow, fx) {
|
|
80
21
|
const entries = readdirSync(srcDir);
|
|
81
22
|
for (const entry of entries) {
|
|
@@ -85,20 +26,15 @@ function installSkillsFromDir(srcDir, destDir, projectTags, installedNow, fx) {
|
|
|
85
26
|
if (stat.isDirectory()) {
|
|
86
27
|
const skillMdPath = join(srcPath, 'SKILL.md');
|
|
87
28
|
if (existsSync(skillMdPath)) {
|
|
88
|
-
// Directory-based skill — check stack filter before copying.
|
|
89
|
-
// O filtro é PURO e continua sendo o único: previsão e execução
|
|
90
|
-
// percorrem esta mesma linha.
|
|
91
29
|
const content = readFileSync(skillMdPath, 'utf-8');
|
|
92
30
|
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
93
31
|
const skillDestDir = join(destDir, entry);
|
|
94
32
|
copySkillDirectory(srcPath, skillDestDir, fx);
|
|
95
33
|
installedNow.add(entry);
|
|
96
34
|
} else {
|
|
97
|
-
// Level/category directory — recurse
|
|
98
35
|
installSkillsFromDir(srcPath, destDir, projectTags, installedNow, fx);
|
|
99
36
|
}
|
|
100
37
|
} else if (entry.endsWith('.md') && entry !== 'README.md') {
|
|
101
|
-
// Legacy flat .md skill — check stack filter before copying
|
|
102
38
|
const content = readFileSync(srcPath, 'utf-8');
|
|
103
39
|
if (!shouldInstallSkill(content, projectTags)) continue;
|
|
104
40
|
const skillName = entry.slice(0, -3);
|
|
@@ -132,32 +68,12 @@ function collectNamesFromDir(dir, names) {
|
|
|
132
68
|
}
|
|
133
69
|
}
|
|
134
70
|
|
|
135
|
-
/**
|
|
136
|
-
* Install morph framework skills to .claude/skills/ in the target project.
|
|
137
|
-
* Skills are copied into subdirectories as SKILL.md files so Claude Code can
|
|
138
|
-
* discover them natively via /<skill-name> syntax (e.g., /phase-design,
|
|
139
|
-
* /nextjs-expert). Directory-based skills also get their scripts/,
|
|
140
|
-
* references/, and assets/ subdirectories copied for on-demand loading.
|
|
141
|
-
*
|
|
142
|
-
* Stack filtering: if projectTags is provided, only skills whose `stacks:`
|
|
143
|
-
* frontmatter matches the project tags are installed. Skills without a
|
|
144
|
-
* `stacks:` field are always installed (universal).
|
|
145
|
-
*
|
|
146
|
-
* @param {string} projectDir - Target project root directory
|
|
147
|
-
* @param {Object} [options]
|
|
148
|
-
* @param {string[]} [options.projectTags=[]] - Stack tags from config
|
|
149
|
-
* @param {ReturnType<import('../lib/installers/effects.js').createEffects>} [options.fx]
|
|
150
|
-
* Coletor de efeitos; o default executa e registra numa lista descartada.
|
|
151
|
-
*/
|
|
152
71
|
export async function installSkills(projectDir, { projectTags = [], fx = realEffects() } = {}) {
|
|
153
72
|
const claudeSkillsDir = join(projectDir, '.claude', 'skills');
|
|
154
73
|
fx.ensureDir(claudeSkillsDir);
|
|
155
74
|
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
// can't tell "installed this run" apart from "leftover from a previous
|
|
159
|
-
// run with different projectTags", which is exactly the stale entry a
|
|
160
|
-
// stack change (or a re-run that narrows the stack) needs cleaned up.
|
|
75
|
+
// Tracked as we go: a disk scan can't tell this run's installs from leftovers
|
|
76
|
+
// of a run with other projectTags, which a stack change must clean up.
|
|
161
77
|
const installedNow = new Set();
|
|
162
78
|
for (const level of SKILL_LEVELS_TO_INSTALL) {
|
|
163
79
|
const levelDir = join(FRAMEWORK_SKILLS_DIR, level);
|
|
@@ -165,22 +81,8 @@ export async function installSkills(projectDir, { projectTags = [], fx = realEff
|
|
|
165
81
|
installSkillsFromDir(levelDir, claudeSkillsDir, projectTags, installedNow, fx);
|
|
166
82
|
}
|
|
167
83
|
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
// run — i.e. it was filtered out by a `stacks:` mismatch (project.stack
|
|
171
|
-
// changed, or a re-run narrowed the tags). Previously scoped to `morph-`
|
|
172
|
-
// prefixed names only, which silently exempted every non-`morph-` skill
|
|
173
|
-
// (the Umbraco pair) from ever being cleaned up: install once with
|
|
174
|
-
// `umbraco` in the stack, drop the stack later, and they'd survive every
|
|
175
|
-
// reinstall/update forever. Names outside allSourceSkills (user-added,
|
|
176
|
-
// non-framework skills) are never touched.
|
|
177
|
-
//
|
|
178
|
-
// Em `dryRun` este laço NÃO emite nada, e a razão está na D6: numa execução
|
|
179
|
-
// real do `update` ele sempre vê um diretório VAZIO, porque
|
|
180
|
-
// `cleanFrameworkDirs()` acabou de apagar `.claude/skills/` inteiro. Num dry
|
|
181
|
-
// ingênuo nada foi apagado, ele veria o diretório POPULADO, e o relatório
|
|
182
|
-
// preveria remoções que a execução real nunca faz — errado na direção que
|
|
183
|
-
// assusta o usuário. O clean subsome este laço, provadamente.
|
|
84
|
+
// Removes known framework skills not reinstalled this run; user skills are never touched.
|
|
85
|
+
// Silent in dryRun: the real update's cleanFrameworkDirs() already emptied the dir.
|
|
184
86
|
if (!fx.dryRun) {
|
|
185
87
|
const allSourceSkills = collectSourceSkillNames();
|
|
186
88
|
for (const entry of readdirSync(claudeSkillsDir)) {
|
|
@@ -6,10 +6,6 @@ import { realEffects } from '../lib/installers/effects.js';
|
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Get the current CLI version from package.json
|
|
11
|
-
* @returns {string} Version string (e.g., "1.0.4")
|
|
12
|
-
*/
|
|
13
9
|
export function getInstalledCLIVersion() {
|
|
14
10
|
try {
|
|
15
11
|
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
@@ -20,11 +16,6 @@ export function getInstalledCLIVersion() {
|
|
|
20
16
|
}
|
|
21
17
|
}
|
|
22
18
|
|
|
23
|
-
/**
|
|
24
|
-
* Get the latest version from npm registry
|
|
25
|
-
* @param {string} packageName - The package name (default: @polymorphism-tech/morph-spec)
|
|
26
|
-
* @returns {Promise<string|null>} Latest version or null if failed
|
|
27
|
-
*/
|
|
28
19
|
export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morph-spec') {
|
|
29
20
|
try {
|
|
30
21
|
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
|
|
@@ -36,11 +27,6 @@ export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morp
|
|
|
36
27
|
}
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
/**
|
|
40
|
-
* Get the MORPH version installed in the project
|
|
41
|
-
* @param {string} projectPath - Path to the project
|
|
42
|
-
* @returns {Promise<object|null>} Version info object or null if not found
|
|
43
|
-
*/
|
|
44
30
|
export async function getProjectMorphVersion(projectPath) {
|
|
45
31
|
try {
|
|
46
32
|
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
@@ -53,12 +39,6 @@ export async function getProjectMorphVersion(projectPath) {
|
|
|
53
39
|
}
|
|
54
40
|
}
|
|
55
41
|
|
|
56
|
-
/**
|
|
57
|
-
* Save the MORPH version to the project
|
|
58
|
-
* @param {string} projectPath - Path to the project
|
|
59
|
-
* @param {string} version - Version to save
|
|
60
|
-
* @returns {Promise<void>}
|
|
61
|
-
*/
|
|
62
42
|
export async function saveProjectMorphVersion(projectPath, version, fx = realEffects()) {
|
|
63
43
|
const versionPath = join(projectPath, '.morph', '.morphversion');
|
|
64
44
|
const versionInfo = {
|
|
@@ -69,12 +49,7 @@ export async function saveProjectMorphVersion(projectPath, version, fx = realEff
|
|
|
69
49
|
fx.writeJson(versionPath, versionInfo);
|
|
70
50
|
}
|
|
71
51
|
|
|
72
|
-
/**
|
|
73
|
-
* Compare two semver versions
|
|
74
|
-
* @param {string} v1 - First version
|
|
75
|
-
* @param {string} v2 - Second version
|
|
76
|
-
* @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
77
|
-
*/
|
|
52
|
+
/** @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2 */
|
|
78
53
|
export function compareVersions(v1, v2) {
|
|
79
54
|
if (!v1 || !v2) return 0;
|
|
80
55
|
|
|
@@ -92,49 +67,17 @@ export function compareVersions(v1, v2) {
|
|
|
92
67
|
return 0;
|
|
93
68
|
}
|
|
94
69
|
|
|
95
|
-
/**
|
|
96
|
-
* Major de uma versão, ou `null` quando a string não permite decidir.
|
|
97
|
-
*
|
|
98
|
-
* Vive aqui, ao lado de `compareVersions`, de propósito: uma segunda
|
|
99
|
-
* aritmética de versão noutro arquivo é exatamente como duas verdades sobre a
|
|
100
|
-
* mesma versão nascem.
|
|
101
|
-
*
|
|
102
|
-
* @param {string|null|undefined} version
|
|
103
|
-
* @returns {number|null}
|
|
104
|
-
*/
|
|
70
|
+
/** @returns {number|null} `null` when the string does not determine a major */
|
|
105
71
|
export function majorOf(version) {
|
|
106
72
|
if (typeof version !== 'string') return null;
|
|
107
73
|
const m = /^\s*v?(\d+)\./.exec(version) ?? /^\s*v?(\d+)\s*$/.exec(version);
|
|
108
74
|
return m ? Number(m[1]) : null;
|
|
109
75
|
}
|
|
110
76
|
|
|
111
|
-
/**
|
|
112
|
-
* Limiar do freio: um salto de DOIS majors ou mais recusa sem `--force`.
|
|
113
|
-
*
|
|
114
|
-
* O limiar não é "quantos majors dá para pular com segurança" — é "a partir de
|
|
115
|
-
* onde o usuário merece ler o relatório antes". Medido: um projeto em 7.x indo
|
|
116
|
-
* para 9.x recusa; um em 8.x atualiza sem atrito.
|
|
117
|
-
*/
|
|
118
77
|
export const MAJOR_JUMP_LIMIT = 2;
|
|
119
78
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
*
|
|
123
|
-
* Três resultados, e a diferença entre eles é o que a mensagem tem de dizer:
|
|
124
|
-
*
|
|
125
|
-
* - `reason: 'unknown-installed'` — a versão instalada é indeterminável.
|
|
126
|
-
* Tratada como salto grande, com motivo PRÓPRIO: "não sei quão velho isto é"
|
|
127
|
-
* é precisamente quando sobrescrever às cegas é mais perigoso, e tratá-la
|
|
128
|
-
* como zero seria a única leitura que libera o pior caso.
|
|
129
|
-
* - `reason: 'major-jump'` — salto medido de `jump` majors, >= ao limiar.
|
|
130
|
-
* - `reason: null` — sem freio (mesmo major, um só major de diferença, ou
|
|
131
|
-
* destino não superior à instalada).
|
|
132
|
-
*
|
|
133
|
-
* @param {string|null} installedVersion
|
|
134
|
-
* @param {string|null} targetVersion
|
|
135
|
-
* @param {{ limit?: number }} [options]
|
|
136
|
-
* @returns {{ refuse: boolean, reason: 'unknown-installed'|'major-jump'|null, jump: number|null }}
|
|
137
|
-
*/
|
|
79
|
+
// An unreadable installed version refuses with its own reason: not knowing how old
|
|
80
|
+
// the project is is when a blind overwrite is most dangerous.
|
|
138
81
|
export function majorJump(installedVersion, targetVersion, { limit = MAJOR_JUMP_LIMIT } = {}) {
|
|
139
82
|
const installedMajor = majorOf(installedVersion);
|
|
140
83
|
const targetMajor = majorOf(targetVersion);
|
|
@@ -143,8 +86,6 @@ export function majorJump(installedVersion, targetVersion, { limit = MAJOR_JUMP_
|
|
|
143
86
|
return { refuse: true, reason: 'unknown-installed', jump: null };
|
|
144
87
|
}
|
|
145
88
|
if (targetMajor === null) {
|
|
146
|
-
// Sem destino legível não há salto a medir — e inventar um seria decidir
|
|
147
|
-
// por um número que ninguém mantém.
|
|
148
89
|
return { refuse: false, reason: null, jump: null };
|
|
149
90
|
}
|
|
150
91
|
if (compareVersions(installedVersion, targetVersion) >= 0) {
|
|
@@ -156,17 +97,11 @@ export function majorJump(installedVersion, targetVersion, { limit = MAJOR_JUMP_
|
|
|
156
97
|
return { refuse: false, reason: null, jump };
|
|
157
98
|
}
|
|
158
99
|
|
|
159
|
-
/**
|
|
160
|
-
* Detect how the CLI was installed
|
|
161
|
-
* @returns {'global'|'npx'|'unknown'}
|
|
162
|
-
*/
|
|
163
100
|
export function detectInstallMethod() {
|
|
164
|
-
// Check if running via npx
|
|
165
101
|
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
|
|
166
102
|
return 'npx';
|
|
167
103
|
}
|
|
168
104
|
|
|
169
|
-
// Check if installed globally
|
|
170
105
|
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
171
106
|
process.execPath.includes('node_modules');
|
|
172
107
|
|
|
@@ -177,11 +112,6 @@ export function detectInstallMethod() {
|
|
|
177
112
|
return 'unknown';
|
|
178
113
|
}
|
|
179
114
|
|
|
180
|
-
/**
|
|
181
|
-
* Get update instructions based on install method
|
|
182
|
-
* @param {string} method - Install method ('global', 'npx', 'unknown')
|
|
183
|
-
* @returns {string[]} Array of instruction strings
|
|
184
|
-
*/
|
|
185
115
|
export function getUpdateInstructions(method) {
|
|
186
116
|
switch (method) {
|
|
187
117
|
case 'global':
|
|
@@ -198,10 +128,6 @@ export function getUpdateInstructions(method) {
|
|
|
198
128
|
}
|
|
199
129
|
}
|
|
200
130
|
|
|
201
|
-
/**
|
|
202
|
-
* Check if CLI version is outdated
|
|
203
|
-
* @returns {Promise<{isOutdated: boolean, current: string, latest: string|null}>}
|
|
204
|
-
*/
|
|
205
131
|
export async function checkCLIOutdated() {
|
|
206
132
|
const current = getInstalledCLIVersion();
|
|
207
133
|
const latest = await getLatestNPMVersion();
|
|
@@ -215,11 +141,6 @@ export async function checkCLIOutdated() {
|
|
|
215
141
|
return { isOutdated, current, latest };
|
|
216
142
|
}
|
|
217
143
|
|
|
218
|
-
/**
|
|
219
|
-
* Check if project MORPH version is outdated
|
|
220
|
-
* @param {string} projectPath - Path to the project
|
|
221
|
-
* @returns {Promise<{isOutdated: boolean, current: string|null, cliVersion: string}>}
|
|
222
|
-
*/
|
|
223
144
|
export async function checkProjectOutdated(projectPath) {
|
|
224
145
|
const cliVersion = getInstalledCLIVersion();
|
|
225
146
|
const projectVersion = await getProjectMorphVersion(projectPath);
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Inicia uma nova feature MORPH-SPEC. Roda morph-brainstorming para entender negócio + escanear stack, depois morph-plan para produzir use cases, spec e tasks. Pausa nos Gates 1 e 2.
|
|
3
|
-
argument-hint: <feature-name>
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent, AskUserQuestion, Skill
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# /morph-proposal — NLH Pipeline
|
|
8
|
-
|
|
9
|
-
Conduz a feature do request inicial até o Gate 2 (plano aprovado), sem intervenção do usuário entre os gates.
|
|
10
|
-
|
|
11
|
-
**Implementação:** este slash command é um atalho. O agente deve invocar `Skill(morph-proposal)` com o nome da feature como argumento. A skill encadeia automaticamente `morph-brainstorming` → Gate 1 → `morph-plan` → Gate 2. Em `trust: "auto"` (default), gates auto-passam sem sinais de risco.
|
|
12
|
-
|
|
13
|
-
## Argumentos
|
|
14
|
-
|
|
15
|
-
`{feature-name}` — nome em kebab-case da feature.
|
|
16
|
-
|
|
17
|
-
## Fluxo
|
|
18
|
-
|
|
19
|
-
1. **Detecta contexto:** verifica se `.morph/features/{feature-name}/` já existe.
|
|
20
|
-
- Se existe e tem `0-proposal/proposal.md`: pular para passo 3.
|
|
21
|
-
- Se não existe: criar a estrutura e seguir o passo 2.
|
|
22
|
-
|
|
23
|
-
2. **Invocar `morph-brainstorming`** (skill em `.claude/skills/morph-brainstorming/`):
|
|
24
|
-
- Varre o ambiente **antes** de qualquer pergunta (stack, contexto do projeto, acervo de use cases).
|
|
25
|
-
- Monta a árvore de decisões abertas — só o que a varredura não respondeu.
|
|
26
|
-
- Abre com a escolha de **modo de descoberta** (entrevista ou automático), recomendando um.
|
|
27
|
-
- Produz `proposal.md` em `.morph/features/{feature-name}/0-proposal/`.
|
|
28
|
-
- Apresenta o **Gate 1** (que pausa sempre no modo entrevista).
|
|
29
|
-
|
|
30
|
-
3. **Aguardar aprovação do Gate 1.**
|
|
31
|
-
- Resposta válida (o que o hook `state-sync` reconhece): começa com "aprovado" ou contém "pode avançar", "pode implementar" ou "pode prosseguir". Outra frase não grava o gate.
|
|
32
|
-
- Se ajustes solicitados: aplicar e reapresentar o gate.
|
|
33
|
-
|
|
34
|
-
4. **Após Gate 1, invocar `morph-plan`** (skill em `.claude/skills/morph-plan/`):
|
|
35
|
-
- Lê `proposal.md`.
|
|
36
|
-
- Produz use cases, `spec.md` e `tasks.json` em `.morph/features/{feature-name}/2-plan/`.
|
|
37
|
-
- Apresenta o **Gate 2** com template fixo (ver SKILL.md passo 5).
|
|
38
|
-
|
|
39
|
-
5. **Aguardar aprovação do Gate 2.**
|
|
40
|
-
- Resposta válida: as mesmas do Gate 1 (ex.: "aprovado", "pode implementar").
|
|
41
|
-
- Se ajustes solicitados: aplicar e reapresentar o gate.
|
|
42
|
-
|
|
43
|
-
6. **Após Gate 2:** sinalizar ao usuário que `/morph-apply {feature-name}` está pronto para iniciar a implementação.
|
|
44
|
-
|
|
45
|
-
## Anti-padrões
|
|
46
|
-
|
|
47
|
-
- Não pular o Gate 1 mesmo que o usuário pareça apressado — proposal.md é o contrato de escopo.
|
|
48
|
-
- Não escrever código nesta fase. Apenas artefatos de planejamento.
|
|
49
|
-
- Não perguntar sobre stack — o stack padrão é VSA + .NET 10 + MAF + Next.js + Neon.
|
|
50
|
-
|
|
51
|
-
## Referências
|
|
52
|
-
|
|
53
|
-
- `.morph/framework/MORPH.md` — charter completo
|
|
54
|
-
- `.claude/skills/morph-brainstorming/SKILL.md`
|
|
55
|
-
- `.claude/skills/morph-plan/SKILL.md`
|
|
56
|
-
|
|
57
|
-
**Feature:** $ARGUMENTS
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Trace Schema — morph-spec NLH
|
|
2
|
-
|
|
3
|
-
## Local de armazenamento
|
|
4
|
-
|
|
5
|
-
`.morph/traces/YYYY-MM-DD-{feature}-{phase}.json`
|
|
6
|
-
|
|
7
|
-
Um arquivo por fase. Se a fase for executada em múltiplas sessões, o mesmo arquivo é atualizado incrementalmente.
|
|
8
|
-
|
|
9
|
-
## Formato JSON
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"schema_version": "1.0",
|
|
14
|
-
"session_id": "YYYY-MM-DD-HH-MM-SS",
|
|
15
|
-
"feature": "nome-da-feature",
|
|
16
|
-
"phase": "implement | plan | review",
|
|
17
|
-
"started_at": "ISO 8601",
|
|
18
|
-
"completed_at": "ISO 8601",
|
|
19
|
-
"tasks": [
|
|
20
|
-
{
|
|
21
|
-
"id": "T1",
|
|
22
|
-
"title": "descrição da task",
|
|
23
|
-
"status": "completed | failed | skipped",
|
|
24
|
-
"standards_consulted": ["id-do-standard-1"],
|
|
25
|
-
"agent_persona": "dotnet-senior | nextjs-expert | etc",
|
|
26
|
-
"sub_agent_dispatched": false,
|
|
27
|
-
"files_modified": ["path/relativo/arquivo.cs"],
|
|
28
|
-
"duration_seconds": null
|
|
29
|
-
}
|
|
30
|
-
],
|
|
31
|
-
"phase_summary": {
|
|
32
|
-
"tasks_completed": 3,
|
|
33
|
-
"tasks_failed": 0,
|
|
34
|
-
"standards_most_used": ["architecture-vertical-slice"],
|
|
35
|
-
"sub_agents_dispatched": 0
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Como o trace é gerado (automático — não escreva à mão)
|
|
41
|
-
|
|
42
|
-
O trace é **gerado pelo hook `trace-autogen`** (PostToolUse em Write|Edit) a cada
|
|
43
|
-
write de `2-plan/tasks.json` — o último write da cerimônia de cada task. As skills
|
|
44
|
-
(`morph-implement`, `morph-review`) **não escrevem o trace**.
|
|
45
|
-
|
|
46
|
-
| Fonte | O que alimenta |
|
|
47
|
-
|-------|----------------|
|
|
48
|
-
| `tasks.json` | conjunto de tasks, `status` (done→completed, skipped→skipped, blocked→failed; pending/in_progress ficam fora), `outputs` → `files_modified` |
|
|
49
|
-
| `recap.md` | `**Standard:**` → `standards_consulted`; `**Persona:**` → `agent_persona` + `sub_agent_dispatched` (`morph-{id}`/`general-purpose` = true) |
|
|
50
|
-
|
|
51
|
-
`completed_at` e `phase_summary` são preenchidos quando nenhuma task resta
|
|
52
|
-
`pending`/`in_progress`. O arquivo existente da mesma feature+fase é atualizado
|
|
53
|
-
in place (`session_id`/`started_at` preservados entre sessões).
|
|
54
|
-
|
|
55
|
-
**Se o trace parecer incompleto, corrija a FONTE** (um recap sem o bloco `## T{N}`), nunca o trace.
|
|
56
|
-
|
|
57
|
-
## Regras de geração
|
|
58
|
-
|
|
59
|
-
- Se um campo não tiver valor disponível, o hook usa `null` — nunca omite o campo.
|
|
60
|
-
- Estrutura completa mantida para compatibilidade com análise de meta-harness.
|
|
61
|
-
- `session_id` usa o formato `YYYY-MM-DD-HH-MM-SS` da primeira geração.
|
|
62
|
-
- `standards_consulted` contém os `id`s do STANDARDS.json, não os paths.
|
|
63
|
-
- `files_modified` usa paths relativos à raiz do repositório (vem de `outputs`).
|
|
64
|
-
|
|
65
|
-
## Motivação
|
|
66
|
-
|
|
67
|
-
Raw traces preservam o sinal completo de execução. Summaries comprimem informação e perdem sinais que o meta-harness usa para otimização. Regra: logar o trace completo — nunca substituir por resumo.
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* tasks-json-guard.js — PostToolUse hook (validate-and-warn, never block)
|
|
4
|
-
*
|
|
5
|
-
* Event: PostToolUse | Matcher: Write|Edit
|
|
6
|
-
*
|
|
7
|
-
* After any Write/Edit that touches a tasks.json under .morph/features/,
|
|
8
|
-
* re-reads the file from disk and validates the schema (status enum, effort
|
|
9
|
-
* enum). Invalid → injects a warning so the LLM fixes the file immediately;
|
|
10
|
-
* valid → logs one activity line and stays silent.
|
|
11
|
-
*
|
|
12
|
-
* The production failure this guards: a leaf sub-agent wrote
|
|
13
|
-
* status:"completed" (not in the enum — the schema accepts "done") into
|
|
14
|
-
* tasks.json and nothing flagged it. The blocking pre-write validation for
|
|
15
|
-
* post-Gate-2 edits lives in protect-spec-files.js (PreToolUse); this
|
|
16
|
-
* PostToolUse pass also covers pre-gate authoring (morph-plan) and anything
|
|
17
|
-
* that landed on disk regardless.
|
|
18
|
-
*
|
|
19
|
-
* Fail-open: exits 0 on any error.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { readFileSync } from 'fs';
|
|
23
|
-
import { isAbsolute, join } from 'path';
|
|
24
|
-
import { stateExists } from '../../shared/state-reader.js';
|
|
25
|
-
import { contextResult, runStandalone } from '../../shared/hook-response.js';
|
|
26
|
-
import { logHookActivity } from '../../shared/activity-logger.js';
|
|
27
|
-
import { getFilePath } from '../../shared/payload-utils.js';
|
|
28
|
-
import { isFeaturePath, getBasename } from '../../shared/phase-utils.js';
|
|
29
|
-
import { VALID_TASK_STATUS, validateTasksDoc } from '../../shared/tasks-validator.js';
|
|
30
|
-
|
|
31
|
-
function projectDir() {
|
|
32
|
-
return process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @param {object} payload - parsed PostToolUse stdin payload (Write|Edit)
|
|
37
|
-
* @returns {Promise<object|null>} a hookSpecificOutput warning when tasks.json
|
|
38
|
-
* is unparseable or schema-invalid, or null (pass/silent).
|
|
39
|
-
*/
|
|
40
|
-
export async function run(payload) {
|
|
41
|
-
try {
|
|
42
|
-
const cwd = projectDir();
|
|
43
|
-
if (!stateExists(cwd)) return null;
|
|
44
|
-
|
|
45
|
-
const filePath = getFilePath(payload);
|
|
46
|
-
if (!filePath) return null;
|
|
47
|
-
if (!isFeaturePath(filePath)) return null;
|
|
48
|
-
if (getBasename(filePath) !== 'tasks.json') return null;
|
|
49
|
-
|
|
50
|
-
const absPath = isAbsolute(filePath) ? filePath : join(cwd, filePath);
|
|
51
|
-
|
|
52
|
-
let doc;
|
|
53
|
-
try {
|
|
54
|
-
doc = JSON.parse(readFileSync(absPath, 'utf-8'));
|
|
55
|
-
} catch {
|
|
56
|
-
logHookActivity('tasks-json-guard', 'PostToolUse', 'invalid', cwd);
|
|
57
|
-
return contextResult(
|
|
58
|
-
'⚠️ [morph-spec] tasks.json INVÁLIDO — o arquivo gravado não parseia como JSON. ' +
|
|
59
|
-
'Corrija o arquivo antes de continuar.',
|
|
60
|
-
'PostToolUse'
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const { valid, errors } = validateTasksDoc(doc);
|
|
65
|
-
if (!valid) {
|
|
66
|
-
logHookActivity('tasks-json-guard', 'PostToolUse', 'invalid', cwd);
|
|
67
|
-
return contextResult(
|
|
68
|
-
'⚠️ [morph-spec] tasks.json com schema inválido após o write:\n' +
|
|
69
|
-
errors.map(e => ` • ${e}`).join('\n') +
|
|
70
|
-
`\nStatus válidos: ${VALID_TASK_STATUS.join(', ')}. Corrija antes de marcar a task como done.`,
|
|
71
|
-
'PostToolUse'
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
logHookActivity('tasks-json-guard', 'PostToolUse', 'ok', cwd);
|
|
76
|
-
return null;
|
|
77
|
-
} catch {
|
|
78
|
-
// Fail-open
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
await runStandalone(run);
|