@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,14 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Hooks run inside the user's project and cannot import the CLI's
|
|
5
|
-
* src/core/state-manager.js. This module is the hook-side mirror of the core
|
|
6
|
-
* writers: it persists a feature to its authoritative `feature.json` (committed)
|
|
7
|
-
* and refreshes the thin, gitignored root index (`.morph/state.json`).
|
|
8
|
-
*
|
|
9
|
-
* Fail-open by contract: every function swallows errors and returns a boolean —
|
|
10
|
-
* a hook must never break the agent over a state write.
|
|
11
|
-
*/
|
|
1
|
+
// Hook-side mirror of the CLI state writers (hooks cannot import `src/`).
|
|
2
|
+
// Fail-open: every function returns a boolean, never throws.
|
|
12
3
|
|
|
13
4
|
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync, mkdirSync } from 'fs';
|
|
14
5
|
import { join, dirname } from 'path';
|
|
@@ -17,12 +8,10 @@ import { clearStateCache } from './state-reader.js';
|
|
|
17
8
|
const STATE_FILE = '.morph/state.json';
|
|
18
9
|
const STATE_VERSION = '5.0.0';
|
|
19
10
|
|
|
20
|
-
/** Absolute path to a feature's authoritative feature.json. */
|
|
21
11
|
export function featureJsonPath(featureName, projectPath) {
|
|
22
12
|
return join(projectPath || process.cwd(), '.morph', 'features', featureName, 'feature.json');
|
|
23
13
|
}
|
|
24
14
|
|
|
25
|
-
/** Read a feature's feature.json, or null if missing/corrupt. */
|
|
26
15
|
export function readFeatureFile(featureName, projectPath) {
|
|
27
16
|
const p = featureJsonPath(featureName, projectPath);
|
|
28
17
|
if (!existsSync(p)) return null;
|
|
@@ -33,7 +22,7 @@ export function readFeatureFile(featureName, projectPath) {
|
|
|
33
22
|
}
|
|
34
23
|
}
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
// EPERM retry: Windows rename fails while another process holds the target.
|
|
37
26
|
function atomicWriteJson(targetPath, data) {
|
|
38
27
|
const dir = dirname(targetPath);
|
|
39
28
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
@@ -54,11 +43,6 @@ function atomicWriteJson(targetPath, data) {
|
|
|
54
43
|
}
|
|
55
44
|
}
|
|
56
45
|
|
|
57
|
-
/**
|
|
58
|
-
* Persist a feature to its authoritative feature.json and refresh the thin root
|
|
59
|
-
* index entry (status + updatedAt) + aggregate metadata. Returns true on
|
|
60
|
-
* success, false fail-open.
|
|
61
|
-
*/
|
|
62
46
|
export function persistFeature(featureName, feature, projectPath) {
|
|
63
47
|
try {
|
|
64
48
|
atomicWriteJson(featureJsonPath(featureName, projectPath), feature);
|
|
@@ -75,19 +59,8 @@ export function persistFeature(featureName, feature, projectPath) {
|
|
|
75
59
|
root.metadata.totalFeatures = Object.keys(root.features).length;
|
|
76
60
|
atomicWriteJson(statePath, root);
|
|
77
61
|
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// process (README history), but core/chain.js now runs multiple hooks
|
|
81
|
-
// — e.g. state-sync then gate-guard — IN ONE process. Without this,
|
|
82
|
-
// a loadState() call earlier in that same process (state-sync's
|
|
83
|
-
// handleWriteEdit reads via loadState before this write) would leave a
|
|
84
|
-
// stale snapshot cached, and gate-guard's loadState() right after this
|
|
85
|
-
// write would silently read the PRE-write state instead of what was
|
|
86
|
-
// just persisted — inverting the "gate-guard runs after state-sync so it
|
|
87
|
-
// sees the gate state-sync just recorded" ordering guarantee the
|
|
88
|
-
// registry comment documents. Invalidating here (the one place
|
|
89
|
-
// feature.json/state.json are actually written) covers every current
|
|
90
|
-
// and future caller, not just this one chain.
|
|
62
|
+
// chain.js runs several hooks in one process: the next link must not read
|
|
63
|
+
// the memoized pre-write snapshot.
|
|
91
64
|
clearStateCache();
|
|
92
65
|
return true;
|
|
93
66
|
} catch {
|
|
@@ -95,16 +68,7 @@ export function persistFeature(featureName, feature, projectPath) {
|
|
|
95
68
|
}
|
|
96
69
|
}
|
|
97
70
|
|
|
98
|
-
/**
|
|
99
|
-
* Canonical gate-approval writer (hook-side mirror of CLI setApprovalGate).
|
|
100
|
-
* Stamps approvalGates[gate] = { approved, timestamp, approvedBy } — the single
|
|
101
|
-
* canonical shape — onto the feature and persists it. Returns true on success.
|
|
102
|
-
*
|
|
103
|
-
* @param {string} featureName
|
|
104
|
-
* @param {string} gate - 'proposal'|'uiux'|'plan'|'review'
|
|
105
|
-
* @param {{ approvedBy?: string, timestamp?: string }} [meta]
|
|
106
|
-
* @param {string} [projectPath]
|
|
107
|
-
*/
|
|
71
|
+
/** @param {{ approvedBy?: string, timestamp?: string, selfAssessed?: boolean }} [meta] */
|
|
108
72
|
export function setGateApproved(featureName, gate, meta = {}, projectPath) {
|
|
109
73
|
try {
|
|
110
74
|
const feature = readFeatureFile(featureName, projectPath) || createDefaultFeature(featureName);
|
|
@@ -123,16 +87,8 @@ export function setGateApproved(featureName, gate, meta = {}, projectPath) {
|
|
|
123
87
|
}
|
|
124
88
|
}
|
|
125
89
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
* Stamps identity — name (the slug), workType, createdAt — so the implicit
|
|
129
|
-
* birth path (state-sync registering a feature on the first Write) never
|
|
130
|
-
* produces an anonymous {status,updatedAt} shell. `description` stays absent
|
|
131
|
-
* (a hook can't invent it); `list`/`doctor` flag its absence. Mirrors the CLI
|
|
132
|
-
* createFeature's stamping.
|
|
133
|
-
*
|
|
134
|
-
* @param {string} [featureName] - the feature slug, stamped as `name`
|
|
135
|
-
*/
|
|
90
|
+
// Stamps identity so a feature born implicitly by a hook is never an anonymous
|
|
91
|
+
// shell; `description` stays absent because a hook cannot invent it.
|
|
136
92
|
export function createDefaultFeature(featureName) {
|
|
137
93
|
const now = new Date().toISOString();
|
|
138
94
|
return {
|
|
@@ -142,9 +98,8 @@ export function createDefaultFeature(featureName) {
|
|
|
142
98
|
createdAt: now,
|
|
143
99
|
updatedAt: now,
|
|
144
100
|
approvalGates: {
|
|
145
|
-
proposal: { approved: false, timestamp: null, approvedBy: null },
|
|
146
|
-
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
147
101
|
plan: { approved: false, timestamp: null, approvedBy: null },
|
|
102
|
+
uiux: { approved: false, timestamp: null, approvedBy: null },
|
|
148
103
|
review: { approved: false, timestamp: null, approvedBy: null },
|
|
149
104
|
},
|
|
150
105
|
outputs: {},
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared stdin reader for Claude Code hooks.
|
|
3
|
-
*
|
|
4
|
-
* Reads JSON payload from stdin (provided by Claude Code to hooks).
|
|
5
|
-
* Returns parsed object or null on failure.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Read and parse JSON from stdin
|
|
10
|
-
* @returns {Promise<Object|null>} Parsed JSON or null
|
|
11
|
-
*/
|
|
1
|
+
/** @returns {Promise<Object|null>} the hook payload, or null when stdin is not JSON */
|
|
12
2
|
export function readStdin() {
|
|
13
3
|
return new Promise((resolve) => {
|
|
14
4
|
let input = '';
|
|
@@ -1,20 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Writes one JSON line per event to .morph/logs/events.jsonl. This is the sink
|
|
5
|
-
* for the `telemetry-log` hook, which fires on every tool call (PostToolUse,
|
|
6
|
-
* all tools) and on every user prompt (UserPromptSubmit) — the only vantage
|
|
7
|
-
* points an NLH exposes. The whole execution path (skills, sub-agent dispatch,
|
|
8
|
-
* knowledge-base reads, prompts) is reconstructable by reading this file.
|
|
9
|
-
*
|
|
10
|
-
* Design constraints (same discipline as activity-logger, plus one):
|
|
11
|
-
* - Synchronous only (hooks run in process, no async overhead)
|
|
12
|
-
* - Fail-silent (never throws — hooks must not be blocked by the logger)
|
|
13
|
-
* - APPEND-ONLY: one `appendFileSync` line per event, O(1) per call. The
|
|
14
|
-
* read-modify-write of a whole JSON doc (activity-logger) is O(n²) across a
|
|
15
|
-
* session and blows the ≤5ms budget once tool calls number in the hundreds.
|
|
16
|
-
* - Windows-safe paths via path.join
|
|
17
|
-
*/
|
|
1
|
+
// Append-only on purpose: rewriting a whole JSON doc per event is O(n²) across
|
|
2
|
+
// a session, and this runs on every tool call. Fail-silent: never blocks a hook.
|
|
18
3
|
|
|
19
4
|
import {
|
|
20
5
|
appendFileSync, readFileSync, mkdirSync, existsSync, statSync, renameSync,
|
|
@@ -26,13 +11,7 @@ import { redactSecrets } from './redact-secrets.js';
|
|
|
26
11
|
const LOGS_DIR = '.morph/logs';
|
|
27
12
|
export const EVENTS_FILE = join(LOGS_DIR, 'events.jsonl');
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
// this size, alongside the pre-existing activity.jsonl rotation — same
|
|
31
|
-
// call site, same "between sessions" checkpoint. events.jsonl has no natural
|
|
32
|
-
// per-turn cap (prompts/commands are truncated at the write site, but the
|
|
33
|
-
// LINE COUNT itself grows unbounded across a long-lived project), so without
|
|
34
|
-
// this it grows forever the way activity.jsonl used to before R1.2.
|
|
35
|
-
export const ROTATE_THRESHOLD_BYTES = 5 * 1024 * 1024; // 5 MB
|
|
14
|
+
export const ROTATE_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
36
15
|
|
|
37
16
|
// Bumped when a field changes meaning. Readers key legacy handling off its
|
|
38
17
|
// absence (v1 events carry no schemaVersion and no eventId).
|
|
@@ -49,15 +28,7 @@ function getEventsPath(projectPath) {
|
|
|
49
28
|
return join(projectPath || process.cwd(), EVENTS_FILE);
|
|
50
29
|
}
|
|
51
30
|
|
|
52
|
-
|
|
53
|
-
* Append a single telemetry event as one JSON line.
|
|
54
|
-
* Stamps `ts` (unless given), `schemaVersion` and a unique `eventId` — the
|
|
55
|
-
* dedupe key when the same line is read back from a rotated archive or a
|
|
56
|
-
* finish backup — and redacts secrets in the free-text fields.
|
|
57
|
-
*
|
|
58
|
-
* @param {Object} event - Event payload (tool, skill/persona/target/prompt, feature, phase…)
|
|
59
|
-
* @param {string} [projectPath] - Project root (defaults to cwd)
|
|
60
|
-
*/
|
|
31
|
+
// `eventId` dedupes the same line read back from a rotated archive or a finish backup.
|
|
61
32
|
export function appendTelemetryEvent(event, projectPath) {
|
|
62
33
|
try {
|
|
63
34
|
const eventsPath = getEventsPath(projectPath);
|
|
@@ -75,17 +46,9 @@ export function appendTelemetryEvent(event, projectPath) {
|
|
|
75
46
|
}
|
|
76
47
|
appendFileSync(eventsPath, JSON.stringify(stamped) + '\n', 'utf-8');
|
|
77
48
|
} catch {
|
|
78
|
-
// Fail-silent — never block hooks
|
|
79
49
|
}
|
|
80
50
|
}
|
|
81
51
|
|
|
82
|
-
/**
|
|
83
|
-
* Read the full event stream back into an array. A corrupt/truncated line
|
|
84
|
-
* (e.g. a killed process mid-write) is skipped, not fatal.
|
|
85
|
-
*
|
|
86
|
-
* @param {string} [projectPath]
|
|
87
|
-
* @returns {Object[]} events in write order (may be empty)
|
|
88
|
-
*/
|
|
89
52
|
export function readTelemetryEvents(projectPath) {
|
|
90
53
|
try {
|
|
91
54
|
const eventsPath = getEventsPath(projectPath);
|
|
@@ -98,7 +61,7 @@ export function readTelemetryEvents(projectPath) {
|
|
|
98
61
|
try {
|
|
99
62
|
events.push(JSON.parse(trimmed));
|
|
100
63
|
} catch {
|
|
101
|
-
//
|
|
64
|
+
// a process killed mid-write leaves a truncated line
|
|
102
65
|
}
|
|
103
66
|
}
|
|
104
67
|
return events;
|
|
@@ -107,19 +70,7 @@ export function readTelemetryEvents(projectPath) {
|
|
|
107
70
|
}
|
|
108
71
|
}
|
|
109
72
|
|
|
110
|
-
|
|
111
|
-
* Archive the current events.jsonl to `.morph/logs/sessions/`, mirroring
|
|
112
|
-
* activity-logger.js's `archiveSession()` — same directory, same rename-not-
|
|
113
|
-
* rewrite mechanics (O(1) even at multi-MB sizes). Named with an `events-`
|
|
114
|
-
* prefix (activity-logger's archives carry no prefix) so the two streams
|
|
115
|
-
* never collide inside the shared `sessions/` directory and stay
|
|
116
|
-
* distinguishable at a glance.
|
|
117
|
-
*
|
|
118
|
-
* Fail-silent — called only from `rotateEventsLogIfLarge` below, which has
|
|
119
|
-
* already confirmed the file exists.
|
|
120
|
-
*
|
|
121
|
-
* @param {string} [projectPath]
|
|
122
|
-
*/
|
|
73
|
+
// `events-` prefix: activity-logger archives share `sessions/` without one.
|
|
123
74
|
function archiveEvents(projectPath) {
|
|
124
75
|
try {
|
|
125
76
|
const eventsPath = getEventsPath(projectPath);
|
|
@@ -132,21 +83,10 @@ function archiveEvents(projectPath) {
|
|
|
132
83
|
const archivePath = join(sessionsDir, `events-${stamp}.jsonl`);
|
|
133
84
|
renameSync(eventsPath, archivePath);
|
|
134
85
|
} catch {
|
|
135
|
-
// Fail-silent
|
|
136
86
|
}
|
|
137
87
|
}
|
|
138
88
|
|
|
139
|
-
/**
|
|
140
|
-
* Rotate events.jsonl when it crosses `thresholdBytes`. Called from
|
|
141
|
-
* worktree-context.js on SessionStart, right next to the existing
|
|
142
|
-
* `rotateActivityLogIfLarge()` call — the only reliable "between sessions"
|
|
143
|
-
* checkpoint the harness gets. `thresholdBytes` is injectable so a test can
|
|
144
|
-
* force rotation without writing multiple megabytes.
|
|
145
|
-
*
|
|
146
|
-
* @param {string} [projectPath] - Project root (defaults to cwd)
|
|
147
|
-
* @param {number} [thresholdBytes] - Rotate once the log is at least this large
|
|
148
|
-
* @returns {boolean} true if a rotation was performed
|
|
149
|
-
*/
|
|
89
|
+
/** @returns {boolean} true if a rotation was performed */
|
|
150
90
|
export function rotateEventsLogIfLarge(projectPath, thresholdBytes = ROTATE_THRESHOLD_BYTES) {
|
|
151
91
|
try {
|
|
152
92
|
const eventsPath = getEventsPath(projectPath);
|
|
@@ -1,28 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Stop hook (the main session's transcript) and SubagentStop (each
|
|
4
|
-
* sub-agent's own transcript, which the main one does not include).
|
|
5
|
-
*/
|
|
1
|
+
// Shared by Stop and SubagentStop: the main transcript does not include the
|
|
2
|
+
// sub-agents' usage.
|
|
6
3
|
|
|
7
4
|
import { readFileSync, existsSync, statSync } from 'fs';
|
|
8
5
|
|
|
9
|
-
//
|
|
10
|
-
// than risk an unbounded read inside a hook.
|
|
6
|
+
// Skip rather than risk an unbounded read inside a hook.
|
|
11
7
|
const MAX_TRANSCRIPT_BYTES = 50 * 1024 * 1024;
|
|
12
8
|
|
|
13
9
|
export const USAGE_KEYS = ['inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheCreationTokens'];
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*
|
|
18
|
-
* One turn is written as several lines (one per content block), each repeating
|
|
19
|
-
* the same usage under the same `message.id`: summing lines double-counts, so
|
|
20
|
-
* the sum is deduplicated by message id.
|
|
21
|
-
*
|
|
22
|
-
* @param {string} [transcriptPath]
|
|
23
|
-
* @returns {{inputTokens:number,outputTokens:number,cacheReadTokens:number,cacheCreationTokens:number}|null}
|
|
24
|
-
* null when there is nothing to report (no/oversized/unreadable file, no usage)
|
|
25
|
-
*/
|
|
11
|
+
// One turn spans several lines repeating the same usage under the same
|
|
12
|
+
// `message.id`: summing lines double-counts, so the sum dedups by id.
|
|
26
13
|
export function readTranscriptUsage(transcriptPath) {
|
|
27
14
|
if (!transcriptPath || typeof transcriptPath !== 'string') return null;
|
|
28
15
|
try {
|
|
@@ -61,17 +48,8 @@ export function readTranscriptUsage(transcriptPath) {
|
|
|
61
48
|
}
|
|
62
49
|
}
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
* A counter going DOWN means the transcript restarted (a new file under the
|
|
68
|
-
* same key), so the whole current total is new usage — subtracting would
|
|
69
|
-
* produce negative spend.
|
|
70
|
-
*
|
|
71
|
-
* @param {object|null|undefined} previous - last recorded totals for this key
|
|
72
|
-
* @param {object} current - totals just read
|
|
73
|
-
* @returns {{delta: object, changed: boolean}}
|
|
74
|
-
*/
|
|
51
|
+
// A counter going DOWN means the transcript restarted: the whole current
|
|
52
|
+
// total is new usage, and subtracting would produce negative spend.
|
|
75
53
|
export function usageDelta(previous, current) {
|
|
76
54
|
const reset = !previous || USAGE_KEYS.some((k) => (current[k] ?? 0) < (previous[k] ?? 0));
|
|
77
55
|
const delta = {};
|
|
@@ -1,29 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Padrão: `<raiz>/worktrees/<nome>` (mais o legado `.worktrees/`). Com `worktree.root` na
|
|
5
|
-
* config (ou `MORPH_WORKTREE_ROOT`), as novas nascem em `<root>/<chave-do-repo>/<nome>`, FORA da
|
|
6
|
-
* árvore aberta no editor. A chave é `basename-<hash da raiz>`: só o basename colidiria entre dois
|
|
7
|
-
* repositórios de mesmo nome que dividem o mesmo root.
|
|
8
|
-
*
|
|
9
|
-
* Um root DENTRO da raiz é recusado: toda lista que se orienta pelo nome do container
|
|
10
|
-
* (.gitignore, graphifyignore, descoberta de testes, tar do --linux, globs do editor) deixaria de
|
|
11
|
-
* enxergá-lo, em silêncio. Recusado, vale o padrão e o motivo volta em `warning`.
|
|
12
|
-
*
|
|
13
|
-
* O predicado "está no container" é a UNIÃO do layout configurado com o padrão e o legado: uma
|
|
14
|
-
* worktree que já existe continua reconhecida depois que a chave muda. Mora em `hooks/shared`
|
|
15
|
-
* porque hook não importa `src/`; o `src/` importa daqui.
|
|
16
|
-
*
|
|
17
|
-
* @module worktree-root
|
|
18
|
-
*/
|
|
1
|
+
// "Está no container" é a UNIÃO do layout configurado com o padrão e o legado:
|
|
2
|
+
// uma worktree antiga segue reconhecida. Root dentro da raiz é recusado, porque as
|
|
3
|
+
// listas orientadas pelo nome `worktrees/` deixariam de enxergá-lo.
|
|
19
4
|
|
|
20
5
|
import { createHash } from 'crypto';
|
|
21
6
|
import { readFileSync } from 'fs';
|
|
22
7
|
import { homedir } from 'os';
|
|
23
8
|
import { basename, isAbsolute, join, relative, resolve } from 'path';
|
|
24
9
|
|
|
25
|
-
// No leading dot
|
|
26
|
-
// frontend suite report 0 tests without an error. `.worktrees/` is the pre-C1 name, still read everywhere.
|
|
10
|
+
// No leading dot: Jest escapes it inside an absolute `rootDir` glob and reports 0 tests.
|
|
27
11
|
export const WORKTREES_DIR = 'worktrees';
|
|
28
12
|
export const LEGACY_WORKTREES_DIR = '.worktrees';
|
|
29
13
|
export const WORKTREE_DIR_NAMES = [WORKTREES_DIR, LEGACY_WORKTREES_DIR];
|
|
@@ -31,7 +15,7 @@ export const WORKTREE_DIR_NAMES = [WORKTREES_DIR, LEGACY_WORKTREES_DIR];
|
|
|
31
15
|
const fwd = (p) => String(p).replace(/\\/g, '/');
|
|
32
16
|
const foldCase = (p) => (process.platform === 'win32' ? p.toLowerCase() : p);
|
|
33
17
|
|
|
34
|
-
|
|
18
|
+
// Só o basename colidiria entre repositórios; o hash é estável entre `R:` e `r:`.
|
|
35
19
|
export function repoKey(primaryRoot) {
|
|
36
20
|
const root = fwd(resolve(primaryRoot));
|
|
37
21
|
const hash = createHash('sha1').update(foldCase(root)).digest('hex').slice(0, 8);
|
|
@@ -54,11 +38,7 @@ function isInside(root, path) {
|
|
|
54
38
|
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
55
39
|
}
|
|
56
40
|
|
|
57
|
-
/**
|
|
58
|
-
* @param {string} primaryRoot
|
|
59
|
-
* @param {{ env?: NodeJS.ProcessEnv }} [opts]
|
|
60
|
-
* @returns {{ dir: string|null, base?: string, warning?: string }} `dir` = `<root>/<repoKey>`
|
|
61
|
-
*/
|
|
41
|
+
/** @returns {{ dir: string|null, base?: string, warning?: string }} `dir` = `<root>/<repoKey>` */
|
|
62
42
|
export function externalContainer(primaryRoot, { env = process.env } = {}) {
|
|
63
43
|
const configured = configuredRoot(primaryRoot, env);
|
|
64
44
|
if (!configured) return { dir: null };
|
|
@@ -70,19 +50,17 @@ export function externalContainer(primaryRoot, { env = process.env } = {}) {
|
|
|
70
50
|
return { dir: fwd(join(base, repoKey(primaryRoot))), base: fwd(base) };
|
|
71
51
|
}
|
|
72
52
|
|
|
73
|
-
/** Where a NEW worktree lands. */
|
|
74
53
|
export function newWorktreesDir(primaryRoot, opts) {
|
|
75
54
|
return externalContainer(primaryRoot, opts).dir ?? `${fwd(resolve(primaryRoot))}/${WORKTREES_DIR}`;
|
|
76
55
|
}
|
|
77
56
|
|
|
78
|
-
/** Every directory that counts as "the container", external first. Forward slashes. */
|
|
79
57
|
export function containerDirs(primaryRoot, opts) {
|
|
80
58
|
const root = fwd(resolve(primaryRoot));
|
|
81
59
|
const external = externalContainer(primaryRoot, opts).dir;
|
|
82
60
|
return [...(external ? [external] : []), ...WORKTREE_DIR_NAMES.map((name) => `${root}/${name}`)];
|
|
83
61
|
}
|
|
84
62
|
|
|
85
|
-
|
|
63
|
+
// Ask `containerDirs` once per audit, not once per path.
|
|
86
64
|
export function isUnder(dirs, path) {
|
|
87
65
|
const target = foldCase(fwd(resolve(path)));
|
|
88
66
|
return dirs.some((dir) => target.startsWith(`${foldCase(dir)}/`));
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Centralized MCP registry with phase relevance and install configuration for morph-spec workflows",
|
|
4
4
|
"mcps": {
|
|
5
5
|
"context7": {
|
|
6
|
-
"phases": ["
|
|
6
|
+
"phases": ["plan", "implement"],
|
|
7
7
|
"tools": ["resolve-library-id", "query-docs"],
|
|
8
8
|
"serverNames": ["context7", "plugin_context7_context7"],
|
|
9
9
|
"detection": "enabledPlugins includes context7 or mcpServers includes context7",
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
}
|
|
217
217
|
},
|
|
218
218
|
"graphify": {
|
|
219
|
-
"phases": ["
|
|
219
|
+
"phases": ["plan", "implement", "review"],
|
|
220
220
|
"tools": ["query_graph", "get_node", "get_neighbors", "get_community", "god_nodes", "graph_stats", "shortest_path", "list_prs", "get_pr_impact", "triage_prs"],
|
|
221
221
|
"detection": "graphify-out/graph.json exists in the project root (written by morph-spec graph build, or by the graphify post-commit hook)",
|
|
222
222
|
"fallback": "Comportamento atual: Grep/Glob/Read. O grafo é um subconjunto estrito do grep — no eval não encontrou um único arquivo que o grep perderia — então cair para grep não perde recall, só devolve mais ruído",
|
|
@@ -281,9 +281,8 @@
|
|
|
281
281
|
},
|
|
282
282
|
"_phaseMatrixNote": "Keys must be the v5 phases from framework/phases.json. The v4 keys 'design' and 'tasks' both collapsed into 'plan'; 'review' was absent entirely, so a project in review resolved to undefined. Stack-scoped MCPs (asaas, neon) stay out of this matrix on purpose — only universal ones are listed.",
|
|
283
283
|
"phaseMatrix": {
|
|
284
|
-
"proposal": ["context7", "graphify"],
|
|
285
|
-
"uiux": ["playwright", "context7", "stitch", "morph-assets"],
|
|
286
284
|
"plan": ["context7", "graphify"],
|
|
285
|
+
"uiux": ["playwright", "context7", "stitch", "morph-assets"],
|
|
287
286
|
"implement": ["context7", "playwright", "vercel", "morph-assets", "graphify"],
|
|
288
287
|
"review": ["playwright", "context7", "graphify"]
|
|
289
288
|
}
|
package/framework/phases.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "5.0.0",
|
|
3
3
|
"phases": {
|
|
4
|
-
"
|
|
5
|
-
"id": "
|
|
4
|
+
"plan": {
|
|
5
|
+
"id": "plan",
|
|
6
6
|
"index": 0,
|
|
7
|
-
"folder": "
|
|
8
|
-
"displayName": "
|
|
7
|
+
"folder": "2-plan",
|
|
8
|
+
"displayName": "Plan",
|
|
9
9
|
"goals": [
|
|
10
|
-
"Understand the business problem and
|
|
11
|
-
"
|
|
12
|
-
"
|
|
10
|
+
"Understand the business problem and scan the stack (brownfield) or assume the default (greenfield)",
|
|
11
|
+
"Write plan.md — scenarios with proof, decisions, reuse and slices",
|
|
12
|
+
"Run plan-check to produce checks.json and reach the plan gate"
|
|
13
13
|
],
|
|
14
|
-
"requiredOutputs": ["
|
|
15
|
-
"optionalOutputs": [
|
|
14
|
+
"requiredOutputs": ["plan", "checks"],
|
|
15
|
+
"optionalOutputs": [],
|
|
16
16
|
"pausePoints": [
|
|
17
|
-
{ "id": "
|
|
17
|
+
{ "id": "plan", "label": "Gate do plano — Approve plan.md + checks.json" }
|
|
18
18
|
],
|
|
19
|
-
"skill": "morph-
|
|
19
|
+
"skill": "morph-plan"
|
|
20
20
|
},
|
|
21
21
|
"uiux": {
|
|
22
22
|
"id": "uiux",
|
|
@@ -35,50 +35,34 @@
|
|
|
35
35
|
],
|
|
36
36
|
"skill": "morph-uiux"
|
|
37
37
|
},
|
|
38
|
-
"plan": {
|
|
39
|
-
"id": "plan",
|
|
40
|
-
"index": 2,
|
|
41
|
-
"folder": "2-plan",
|
|
42
|
-
"displayName": "Plan",
|
|
43
|
-
"goals": [
|
|
44
|
-
"Write spec.md (technical spec)",
|
|
45
|
-
"Write tasks.json (ordered task breakdown)"
|
|
46
|
-
],
|
|
47
|
-
"requiredOutputs": ["spec", "tasks"],
|
|
48
|
-
"optionalOutputs": ["decisions"],
|
|
49
|
-
"pausePoints": [
|
|
50
|
-
{ "id": "plan", "label": "Gate 2 — Approve spec + tasks" }
|
|
51
|
-
],
|
|
52
|
-
"skill": "morph-plan"
|
|
53
|
-
},
|
|
54
38
|
"implement": {
|
|
55
39
|
"id": "implement",
|
|
56
|
-
"index":
|
|
40
|
+
"index": 2,
|
|
57
41
|
"folder": "3-implement",
|
|
58
42
|
"displayName": "Implementation",
|
|
59
43
|
"goals": [
|
|
60
|
-
"Execute
|
|
61
|
-
"
|
|
62
|
-
"
|
|
44
|
+
"Execute the slices of plan.md, in parallel waves when plan-check allows",
|
|
45
|
+
"Close a slice only on verify green and its scenarios proven",
|
|
46
|
+
"One report per slice (3-implement/reports/F{n}.md, with **Arquivos:** and **Standards:**) — the implementation record"
|
|
63
47
|
],
|
|
64
|
-
"requiredOutputs": [
|
|
48
|
+
"requiredOutputs": [],
|
|
65
49
|
"optionalOutputs": ["evidencias"],
|
|
66
50
|
"pausePoints": [],
|
|
67
|
-
"skill": "morph-
|
|
51
|
+
"skill": "morph-apply"
|
|
68
52
|
},
|
|
69
53
|
"review": {
|
|
70
54
|
"id": "review",
|
|
71
|
-
"index":
|
|
55
|
+
"index": 3,
|
|
72
56
|
"folder": "4-review",
|
|
73
57
|
"displayName": "Review",
|
|
74
58
|
"goals": [
|
|
75
|
-
"
|
|
76
|
-
"
|
|
59
|
+
"Verifier proves every scenario; reviewer (and security, when required) judge the diff at a fixed SHA",
|
|
60
|
+
"verifier.md + reviewer.md (+ security.md) with SHIP/FIX and evidence-typed blockers, then Gate 3"
|
|
77
61
|
],
|
|
78
|
-
"requiredOutputs": ["
|
|
79
|
-
"optionalOutputs": [],
|
|
62
|
+
"requiredOutputs": ["verifierReport", "reviewerReport"],
|
|
63
|
+
"optionalOutputs": ["securityReport"],
|
|
80
64
|
"pausePoints": [
|
|
81
|
-
{ "id": "review", "label": "Gate 3 — Approve review
|
|
65
|
+
{ "id": "review", "label": "Gate 3 — Approve the review reports" }
|
|
82
66
|
],
|
|
83
67
|
"skill": "morph-review"
|
|
84
68
|
}
|
|
@@ -9,7 +9,7 @@ stacks:
|
|
|
9
9
|
# C# and .NET VSA Standards
|
|
10
10
|
|
|
11
11
|
- **architecture-vertical-slice-vertical-slice** (`architecture/vertical-slice/vertical-slice.md`) — Uma slice por operação em `Features/{Entity}Feature/{Verb}{Entity}/`: Request/Response `sealed record`, Handler, Validator e Endpoint `IApiEndpoint` `internal sealed`. Decorators Scrutor: Logging → Validation → Handler. PROIBIDO: MediatR/`IRequest`, AggregateRoot, DomainEvent, CQRS formal, camada Application Service, DTO compartilhado entre features, `IRepository<T>`/`IUnitOfWork`.
|
|
12
|
-
- **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `
|
|
12
|
+
- **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `plan.md` (`## Decisões`).
|
|
13
13
|
- **backend-dotnet-result-pattern** (`backend/dotnet/result-pattern.md`) — Falha esperada é `Result`/`Result<T>`, nunca exceção. Endpoint: `result.Match(value => Results.Ok(value), error => error.ToHttpResult())`; nunca leia `.Value` fora do `Match`. Status pelo `ErrorType` (Validation 400, NotFound 404, Conflict 409, Unauthorized 401, Forbidden 403, Unexpected 500), nunca BadRequest para tudo. PROIBIDO: try/catch no handler, retornar null, erro string sem código.
|
|
14
14
|
- **backend-dotnet-fluent-validation-vsa** (`backend/dotnet/fluent-validation-vsa.md`) — Um `sealed AbstractValidator<TRequest>` (FluentValidation 12) por request, na pasta da slice; o `ValidationDecorator` roda antes do handler e devolve `Result.Failure(ValidationError)`. `AddValidatorsFromAssembly()` ANTES de `AddHandlersFromAssembly()`. Sempre `.WithMessage()`; tempo via `TimeProvider` lido DENTRO da regra. Nunca lance `ValidationException`.
|
|
15
15
|
- **backend-dotnet-scrutor** (`backend/dotnet/scrutor.md`) — `AddHandlersFromAssembly` registra cada `IHandler<,>` como Scoped (filtro `!t.ContainsGenericParameters`) e depois `Decorate` Validation e então Logging (o último registrado é o mais externo). Endpoints `IApiEndpoint` via `TryAddEnumerable` Transient + `MapApiEndpoints`. Nunca registre handler um a um.
|
|
@@ -11,5 +11,6 @@ stacks:
|
|
|
11
11
|
|
|
12
12
|
- **frontend-design-system-naming** (`frontend/design-system/naming.md`) — BEM para componente reutilizável (`.card`, `.card__header`, `.card--featured`); prefixo de página para elemento específico (`.home-hero`). Custom properties em `:root` com namespace `--app-`. Estado por sufixo fixo: `--active`, `--disabled`, `--loading`, `--error`.
|
|
13
13
|
- **frontend-design-system-animations** (`frontend/design-system/animations.md`) — Classes `.animate-*`, `.duration-fast/normal/slow`, `.stagger-1..10`. Lista escalonada exige `animation-delay` E `animation-fill-mode: backwards`. Sempre o bloco `prefers-reduced-motion: reduce`. Além do CSS: Motion (`motion/react`) para enter/exit/layout; GSAP só para ScrollTrigger, SVG morph e timelines.
|
|
14
|
+
- **frontend-design-system-art-direction** (`frontend/design-system/art-direction.md`) — Tela nova ou alterada segue a direção escrita em `1-design/direcao.md` e o protótipo de `1-design/prototipos/`: celular composto (folha inferior, alvos de 44px), movimento só em `transform`/`opacity` com versão estática em `prefers-reduced-motion`, nenhum número inventado em cena ilustrativa.
|
|
14
15
|
|
|
15
16
|
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -10,6 +10,6 @@ stacks:
|
|
|
10
10
|
|
|
11
11
|
# Hangfire Background Jobs Standards
|
|
12
12
|
|
|
13
|
-
- **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `
|
|
13
|
+
- **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `plan.md` (`## Decisões`); o que cabe no request fica no handler. Storage `Hangfire.PostgreSql` 1.21.1 com `UseNpgsqlConnection` no endpoint DIRETO do Neon; referencie `Newtonsoft.Json` 13.0.4. Job é classe fina e idempotente que chama o handler; `[AutomaticRetry]`, sem try/catch nem retry manual. Dashboard exige Admin autenticado.
|
|
14
14
|
|
|
15
15
|
> Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
|
|
@@ -11,7 +11,7 @@ stacks:
|
|
|
11
11
|
# Neon PostgreSQL Standards
|
|
12
12
|
|
|
13
13
|
- **infrastructure-neon-setup** (`infrastructure/neon/neon-setup.md`) — Duas connection strings: `DefaultConnection` (pooled `-pooler`) para a app e `MigrationConnection` (direta) para migrations e advisory locks; nunca commitadas (user-secrets / env do Coolify). Formato keyword (`Host=...;SSL Mode=Require`), não URI. `EnableRetryOnFailure(5, 10s)` por causa do scale-to-zero. Branch sempre com `--expires-at`.
|
|
14
|
-
- **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `
|
|
14
|
+
- **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `plan.md` (`## Decisões`), default `gen_random_uuid()`.
|
|
15
15
|
- **infrastructure-neon-pgrag** (`infrastructure/neon/neon-pgrag.md`) — A extensão `rag` é EXPERIMENTAL: protótipo e RAG só-SQL, não produção sem teste. Nunca misture modelos de embedding numa coluna (bge-small 384 × text-embedding-3-small 1536). Índice HNSW depois da carga inicial. Produção .NET com lógica própria: tool MAF tipada sobre pgvector.
|
|
16
16
|
- **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
|
|
17
17
|
|
|
@@ -9,6 +9,18 @@
|
|
|
9
9
|
"type": "object",
|
|
10
10
|
"additionalProperties": true,
|
|
11
11
|
"properties": {
|
|
12
|
+
"stack": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Stack tags joined by `+` (e.g. `dotnet+nextjs`). Decides the build node's commands, which assets install, and that bin/ and obj/ go into .gitignore."
|
|
15
|
+
},
|
|
16
|
+
"frontendPath": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Folder of the frontend, relative to the project root: where the build node runs `tsc`. Absent: the root."
|
|
19
|
+
},
|
|
20
|
+
"backendPath": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Folder of the .NET solution, relative to the project root: where the build node runs `dotnet build`. Absent: the root, or the repository's single .sln/.slnx (else single .csproj) when the root has none."
|
|
23
|
+
},
|
|
12
24
|
"ci": {
|
|
13
25
|
"type": "boolean",
|
|
14
26
|
"description": "false turns `verify {f} --ci` into the local verify (with the reason printed). Absent or true: the Gate 3 proof comes from the GitHub Actions workflow that runs `morph-spec verify` (created on first use), whenever origin is GitHub and gh is authenticated."
|
|
@@ -60,20 +72,13 @@
|
|
|
60
72
|
"e2e": {
|
|
61
73
|
"type": "boolean",
|
|
62
74
|
"default": false,
|
|
63
|
-
"description": "Requires the application stack to be up.
|
|
75
|
+
"description": "Requires the application stack to be up."
|
|
64
76
|
},
|
|
65
77
|
"linuxImage": {
|
|
66
78
|
"type": "string",
|
|
67
79
|
"minLength": 1,
|
|
68
80
|
"description": "Overrides the container image used by `verify --linux` for this node."
|
|
69
81
|
},
|
|
70
|
-
"groups": {
|
|
71
|
-
"type": "array",
|
|
72
|
-
"minItems": 1,
|
|
73
|
-
"uniqueItems": true,
|
|
74
|
-
"items": { "type": "string", "enum": ["backend", "frontend", "tests", "infra", "docs"] },
|
|
75
|
-
"description": "Which task group(s) (tasks.json `group`) this node verifies. Absent means the runner's own default applies (RUNNER_DEFAULT_GROUPS in src/lib/tasks/test-nodes.js) — `none`/`evals` have no default and are unscoped unless declared here."
|
|
76
|
-
},
|
|
77
82
|
"evals": {
|
|
78
83
|
"type": "object",
|
|
79
84
|
"additionalProperties": false,
|
|
@@ -191,7 +196,7 @@
|
|
|
191
196
|
"shutdownBuildServer": {
|
|
192
197
|
"type": "boolean",
|
|
193
198
|
"default": true,
|
|
194
|
-
"description": "Run `dotnet build-server shutdown` at the end of a
|
|
199
|
+
"description": "Run `dotnet build-server shutdown` at the end of a `verify` and of `e2e down`, only when no slot is held and no dotnet build/test is alive."
|
|
195
200
|
},
|
|
196
201
|
"disableNodeReuse": {
|
|
197
202
|
"type": "boolean",
|