@polymorphism-tech/morph-spec 8.46.0 → 9.0.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 +53 -44
- package/README.md +3 -4
- package/bin/morph-spec.js +38 -55
- package/bin/validate.js +5 -134
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +75 -18
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +2 -2
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +7 -31
- package/framework/commands/morph-proposal.md +2 -50
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +585 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/comment-guard.js +1 -1
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
- package/framework/hooks/claude-code/core/gate-guard.js +2 -11
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
- package/framework/hooks/claude-code/core/state-sync.js +17 -19
- package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
- package/framework/hooks/claude-code/core/validate-completion.js +33 -97
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
- package/framework/hooks/claude-code/core/worktree-context.js +5 -4
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +1 -1
- package/framework/hooks/shared/build-check.js +10 -6
- package/framework/hooks/shared/command-tokens.js +169 -0
- package/framework/hooks/shared/comment-rules.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +10 -23
- package/framework/hooks/shared/comment-stripper.js +1 -1
- package/framework/hooks/shared/e2e-check.js +67 -31
- package/framework/hooks/shared/errata.js +72 -0
- package/framework/hooks/shared/feature-locator.js +1 -1
- package/framework/hooks/shared/frontend-locations.js +6 -0
- package/framework/hooks/shared/gate-question.js +18 -0
- package/framework/hooks/shared/phase-utils.js +15 -32
- package/framework/hooks/shared/slice-progress.js +118 -0
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +61 -87
- package/framework/hooks/shared/state-writer.js +3 -3
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -52
- package/framework/skills/README.md +4 -5
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +150 -223
- package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
- package/framework/skills/core/morph-machine/SKILL.md +4 -4
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +138 -300
- package/framework/skills/core/morph-review/SKILL.md +112 -137
- package/framework/skills/core/morph-standards/SKILL.md +29 -41
- package/framework/skills/core/morph-uiux/SKILL.md +78 -153
- package/framework/standards/STANDARDS.json +90 -1219
- package/framework/standards/STANDARDS.meta.json +1305 -0
- package/framework/standards/ai-agents/evals-with-cache.md +2 -3
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +8 -8
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +6 -170
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- 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/e2e/e2e.js +2 -2
- package/src/commands/observability/cost.js +18 -60
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +9 -9
- package/src/commands/project/fleet.js +4 -4
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +6 -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 +79 -25
- package/src/commands/project/status.js +29 -69
- package/src/commands/project/update.js +33 -19
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/advance.js +62 -46
- package/src/commands/state/approve.js +95 -138
- package/src/commands/state/archive.js +20 -84
- package/src/commands/state/auto-approval.js +68 -0
- package/src/commands/state/create.js +28 -1
- package/src/commands/state/delete.js +1 -1
- package/src/commands/state/finish.js +11 -23
- package/src/commands/state/gate-check.js +86 -95
- package/src/commands/state/gate-decision.js +6 -4
- package/src/commands/state/handoff.js +1 -1
- package/src/commands/state/list.js +9 -9
- package/src/commands/state/report-check.js +157 -311
- package/src/commands/state/retype.js +9 -3
- package/src/commands/validation/mutate.js +77 -14
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +60 -135
- package/src/commands/validation/verify.js +41 -82
- package/src/core/paths/output-schema.js +20 -67
- package/src/core/state/gate-order.js +5 -6
- package/src/core/state/lifecycle-audit.js +2 -2
- package/src/core/state/state-manager.js +17 -98
- package/src/core/templates/template-registry.js +2 -2
- package/src/core/templates/template-seed.js +22 -0
- package/src/core/work-types.js +10 -10
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
- package/src/dashboard/app/components/CommandPalette.jsx +7 -8
- package/src/dashboard/app/components/EntityLink.jsx +1 -2
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
- package/src/dashboard/app/lib/phases.js +2 -4
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
- package/src/dashboard/app/pages/Features.jsx +9 -9
- package/src/dashboard/app/pages/Guide.jsx +2 -2
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/app/pages/Standards.jsx +1 -1
- package/src/dashboard/scanner/activity-scanner.js +6 -16
- package/src/dashboard/scanner/agents-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +95 -60
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/project-api.js +1 -1
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/dashboard/scanner/standards-scanner.js +2 -1
- package/src/lib/ci/ci-verify.js +17 -1
- package/src/lib/ci/github-ci.js +1 -1
- package/src/lib/detectors/index.js +3 -8
- package/src/lib/generators/handoff-generator.js +34 -65
- package/src/lib/generators/settings-generator.js +11 -89
- package/src/lib/git/evidence-staleness.js +1 -1
- package/src/lib/git/tree-fingerprint.js +19 -7
- package/src/lib/git/tree-state.js +2 -2
- package/src/lib/heavy/cleanup.js +1 -5
- package/src/lib/installers/memory-installer.js +6 -8
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +46 -9
- package/src/lib/plan/artifact-vocabulary.js +60 -0
- package/src/lib/plan/checks.js +234 -0
- package/src/lib/plan/plan-parser.js +209 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/review-reports.js +222 -0
- package/src/lib/risk-detector.js +134 -135
- package/src/lib/specs/adr-index.js +50 -69
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +44 -0
- package/src/lib/specs/gfm-table.js +17 -0
- package/src/lib/specs/old-format.js +68 -0
- package/src/lib/specs/revocations.js +37 -95
- package/src/lib/specs/screen-checklist.js +18 -17
- package/src/lib/stack/dotnet-build-ignores.js +47 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/stack/stack-scan.js +267 -0
- package/src/lib/standards/registry.js +43 -0
- package/src/lib/standards/sections.js +58 -0
- package/src/lib/tasks/eval-node.js +4 -12
- package/src/lib/tasks/test-nodes.js +1 -74
- package/src/lib/tasks/test-runner.js +75 -117
- package/src/lib/telemetry/metrics.js +8 -69
- package/src/lib/telemetry/telemetry-reader.js +5 -118
- package/src/lib/telemetry/verify-event.js +5 -7
- package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
- package/src/lib/validators/reuse/reuse-validator.js +24 -48
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/traceability-check.js +85 -129
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +134 -301
- package/src/lib/worktree/docker-audit.js +1 -2
- package/src/lib/worktree/fleet-scanner.js +25 -38
- package/src/scripts/setup-infra.js +42 -22
- package/src/utils/agents-installer.js +92 -13
- package/src/utils/claude-settings-manager.js +0 -35
- package/src/utils/file-copier.js +34 -6
- package/src/utils/hooks-installer.js +16 -9
- package/src/utils/skills-installer.js +1 -2
- package/framework/commands/commit.md +0 -166
- 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/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- 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/project/validate.js +0 -97
- 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/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/evaluator-report.js +0 -91
- 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/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/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
allowed-tools: Bash(git add:*), Bash(git status:*), Bash(git commit:*), Bash(git diff:*), Bash(git log:*)
|
|
3
|
-
argument-hint: [message] | --no-verify | --amend
|
|
4
|
-
description: Create well-formatted commits with conventional commit format and emoji
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Smart Git Commit
|
|
8
|
-
|
|
9
|
-
Create well-formatted commit: $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
## Current Repository State
|
|
12
|
-
|
|
13
|
-
- Git status: !git status --porcelain
|
|
14
|
-
- Current branch: !git branch --show-current
|
|
15
|
-
- Staged changes: !git diff --cached --stat
|
|
16
|
-
- Unstaged changes: !git diff --stat
|
|
17
|
-
- Recent commits: !git log --oneline -5
|
|
18
|
-
|
|
19
|
-
## What This Command Does
|
|
20
|
-
|
|
21
|
-
1. Unless specified with --no-verify, automatically runs pre-commit checks:
|
|
22
|
-
- pnpm lint to ensure code quality
|
|
23
|
-
- pnpm build to verify the build succeeds
|
|
24
|
-
- pnpm generate:docs to update documentation
|
|
25
|
-
2. Checks which files are staged with git status
|
|
26
|
-
3. If 0 files are staged, automatically adds all modified and new files with git add
|
|
27
|
-
4. Performs a git diff to understand what changes are being committed
|
|
28
|
-
5. Analyzes the diff to determine if multiple distinct logical changes are present
|
|
29
|
-
6. If multiple distinct changes are detected, suggests breaking the commit into multiple smaller commits
|
|
30
|
-
7. For each commit (or the single commit if not split), creates a commit message using emoji conventional commit format
|
|
31
|
-
|
|
32
|
-
## Best Practices for Commits
|
|
33
|
-
|
|
34
|
-
- **Verify before committing**: Ensure code is linted, builds correctly, and documentation is updated
|
|
35
|
-
- **Atomic commits**: Each commit should contain related changes that serve a single purpose
|
|
36
|
-
- **Split large changes**: If changes touch multiple concerns, split them into separate commits
|
|
37
|
-
- **Conventional commit format**: Use the format <type>: <description> where type is one of:
|
|
38
|
-
- feat: A new feature
|
|
39
|
-
- fix: A bug fix
|
|
40
|
-
- docs: Documentation changes
|
|
41
|
-
- style: Code style changes (formatting, etc)
|
|
42
|
-
- refactor: Code changes that neither fix bugs nor add features
|
|
43
|
-
- perf: Performance improvements
|
|
44
|
-
- test: Adding or fixing tests
|
|
45
|
-
- chore: Changes to the build process, tools, etc.
|
|
46
|
-
- **Present tense, imperative mood**: Write commit messages as commands (e.g., "add feature" not "added feature")
|
|
47
|
-
- **Concise first line**: Keep the first line under 72 characters
|
|
48
|
-
- **Emoji**: Each commit type is paired with an appropriate emoji:
|
|
49
|
-
- ✨ feat: New feature
|
|
50
|
-
- 🐛 fix: Bug fix
|
|
51
|
-
- 📝 docs: Documentation
|
|
52
|
-
- 💄 style: Formatting/style
|
|
53
|
-
- ♻️ refactor: Code refactoring
|
|
54
|
-
- ⚡️ perf: Performance improvements
|
|
55
|
-
- ✅ test: Tests
|
|
56
|
-
- 🔧 chore: Tooling, configuration
|
|
57
|
-
- 🚀 ci: CI/CD improvements
|
|
58
|
-
- 🗑️ revert: Reverting changes
|
|
59
|
-
- 🧪 test: Add a failing test
|
|
60
|
-
- 🚨 fix: Fix compiler/linter warnings
|
|
61
|
-
- 🔒️ fix: Fix security issues
|
|
62
|
-
- 👥 chore: Add or update contributors
|
|
63
|
-
- 🚚 refactor: Move or rename resources
|
|
64
|
-
- 🏗️ refactor: Make architectural changes
|
|
65
|
-
- 🔀 chore: Merge branches
|
|
66
|
-
- 📦️ chore: Add or update compiled files or packages
|
|
67
|
-
- ➕ chore: Add a dependency
|
|
68
|
-
- ➖ chore: Remove a dependency
|
|
69
|
-
- 🌱 chore: Add or update seed files
|
|
70
|
-
- 🧑💻 chore: Improve developer experience
|
|
71
|
-
- 🧵 feat: Add or update code related to multithreading or concurrency
|
|
72
|
-
- 🔍️ feat: Improve SEO
|
|
73
|
-
- 🏷️ feat: Add or update types
|
|
74
|
-
- 💬 feat: Add or update text and literals
|
|
75
|
-
- 🌐 feat: Internationalization and localization
|
|
76
|
-
- 👔 feat: Add or update business logic
|
|
77
|
-
- 📱 feat: Work on responsive design
|
|
78
|
-
- 🚸 feat: Improve user experience / usability
|
|
79
|
-
- 🩹 fix: Simple fix for a non-critical issue
|
|
80
|
-
- 🥅 fix: Catch errors
|
|
81
|
-
- 👽️ fix: Update code due to external API changes
|
|
82
|
-
- 🔥 fix: Remove code or files
|
|
83
|
-
- 🎨 style: Improve structure/format of the code
|
|
84
|
-
- 🚑️ fix: Critical hotfix
|
|
85
|
-
- 🎉 chore: Begin a project
|
|
86
|
-
- 🔖 chore: Release/Version tags
|
|
87
|
-
- 🚧 wip: Work in progress
|
|
88
|
-
- 💚 fix: Fix CI build
|
|
89
|
-
- 📌 chore: Pin dependencies to specific versions
|
|
90
|
-
- 👷 ci: Add or update CI build system
|
|
91
|
-
- 📈 feat: Add or update analytics or tracking code
|
|
92
|
-
- ✏️ fix: Fix typos
|
|
93
|
-
- ⏪️ revert: Revert changes
|
|
94
|
-
- 📄 chore: Add or update license
|
|
95
|
-
- 💥 feat: Introduce breaking changes
|
|
96
|
-
- 🍱 assets: Add or update assets
|
|
97
|
-
- ♿️ feat: Improve accessibility
|
|
98
|
-
- 💡 docs: Add or update comments in source code
|
|
99
|
-
- 🗃️ db: Perform database related changes
|
|
100
|
-
- 🔊 feat: Add or update logs
|
|
101
|
-
- 🔇 fix: Remove logs
|
|
102
|
-
- 🤡 test: Mock things
|
|
103
|
-
- 🥚 feat: Add or update an easter egg
|
|
104
|
-
- 🙈 chore: Add or update .gitignore file
|
|
105
|
-
- 📸 test: Add or update snapshots
|
|
106
|
-
- ⚗️ experiment: Perform experiments
|
|
107
|
-
- 🚩 feat: Add, update, or remove feature flags
|
|
108
|
-
- 💫 ui: Add or update animations and transitions
|
|
109
|
-
- ⚰️ refactor: Remove dead code
|
|
110
|
-
- 🦺 feat: Add or update code related to validation
|
|
111
|
-
- ✈️ feat: Improve offline support
|
|
112
|
-
|
|
113
|
-
## Guidelines for Splitting Commits
|
|
114
|
-
|
|
115
|
-
When analyzing the diff, consider splitting commits based on these criteria:
|
|
116
|
-
|
|
117
|
-
1. **Different concerns**: Changes to unrelated parts of the codebase
|
|
118
|
-
2. **Different types of changes**: Mixing features, fixes, refactoring, etc.
|
|
119
|
-
3. **File patterns**: Changes to different types of files (e.g., source code vs documentation)
|
|
120
|
-
4. **Logical grouping**: Changes that would be easier to understand or review separately
|
|
121
|
-
5. **Size**: Very large changes that would be clearer if broken down
|
|
122
|
-
|
|
123
|
-
## Examples
|
|
124
|
-
|
|
125
|
-
Good commit messages:
|
|
126
|
-
- ✨ feat: add user authentication system
|
|
127
|
-
- 🐛 fix: resolve memory leak in rendering process
|
|
128
|
-
- 📝 docs: update API documentation with new endpoints
|
|
129
|
-
- ♻️ refactor: simplify error handling logic in parser
|
|
130
|
-
- 🚨 fix: resolve linter warnings in component files
|
|
131
|
-
- 🧑💻 chore: improve developer tooling setup process
|
|
132
|
-
- 👔 feat: implement business logic for transaction validation
|
|
133
|
-
- 🩹 fix: address minor styling inconsistency in header
|
|
134
|
-
- 🚑️ fix: patch critical security vulnerability in auth flow
|
|
135
|
-
- 🎨 style: reorganize component structure for better readability
|
|
136
|
-
- 🔥 fix: remove deprecated legacy code
|
|
137
|
-
- 🦺 feat: add input validation for user registration form
|
|
138
|
-
- 💚 fix: resolve failing CI pipeline tests
|
|
139
|
-
- 📈 feat: implement analytics tracking for user engagement
|
|
140
|
-
- 🔒️ fix: strengthen authentication password requirements
|
|
141
|
-
- ♿️ feat: improve form accessibility for screen readers
|
|
142
|
-
|
|
143
|
-
Example of splitting commits:
|
|
144
|
-
- First commit: ✨ feat: add new solc version type definitions
|
|
145
|
-
- Second commit: 📝 docs: update documentation for new solc versions
|
|
146
|
-
- Third commit: 🔧 chore: update package.json dependencies
|
|
147
|
-
- Fourth commit: 🏷️ feat: add type definitions for new API endpoints
|
|
148
|
-
- Fifth commit: 🧵 feat: improve concurrency handling in worker threads
|
|
149
|
-
- Sixth commit: 🚨 fix: resolve linting issues in new code
|
|
150
|
-
- Seventh commit: ✅ test: add unit tests for new solc version features
|
|
151
|
-
- Eighth commit: 🔒️ fix: update dependencies with security vulnerabilities
|
|
152
|
-
|
|
153
|
-
## Command Options
|
|
154
|
-
|
|
155
|
-
- --no-verify: Skip running the pre-commit checks (lint, build, generate:docs)
|
|
156
|
-
|
|
157
|
-
## Important Notes
|
|
158
|
-
|
|
159
|
-
- By default, pre-commit checks (pnpm lint, pnpm build, pnpm generate:docs) will run to ensure code quality
|
|
160
|
-
- If these checks fail, you'll be asked if you want to proceed with the commit anyway or fix the issues first
|
|
161
|
-
- If specific files are already staged, the command will only commit those files
|
|
162
|
-
- If no files are staged, it will automatically stage all modified and new files
|
|
163
|
-
- The commit message will be constructed based on the changes detected
|
|
164
|
-
- Before committing, the command will review the diff to identify if multiple commits would be more appropriate
|
|
165
|
-
- If suggesting multiple commits, it will help you stage and commit the changes separately
|
|
166
|
-
- Always reviews the commit diff to ensure the message matches the changes
|
|
@@ -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);
|
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* trace-autogen.js — PostToolUse hook (Write|Edit)
|
|
4
|
-
*
|
|
5
|
-
* Auto-generates the execution trace
|
|
6
|
-
* `.morph/traces/YYYY-MM-DD-{feature}-implement.json` from tasks.json +
|
|
7
|
-
* recap.md whenever tasks.json is written during
|
|
8
|
-
* the implement phase.
|
|
9
|
-
*
|
|
10
|
-
* This removes the manual trace-writing step from the skills (friction item
|
|
11
|
-
* observed in MORPH_GHLBrain 2026-07-11: 5 writes per task). The tasks.json
|
|
12
|
-
* status flip — the last write of every task's ceremony — now produces the
|
|
13
|
-
* trace for free: deterministic, idempotent, always in sync with the real
|
|
14
|
-
* on-disk state. The skills must NOT write traces by hand anymore.
|
|
15
|
-
*
|
|
16
|
-
* Sources:
|
|
17
|
-
* - tasks.json → task set, status (done→completed, skipped→skipped,
|
|
18
|
-
* blocked→failed; pending/in_progress stay out), outputs
|
|
19
|
-
* - recap.md → per-task `**Standard:**` id and `**Persona:**` line
|
|
20
|
-
*
|
|
21
|
-
* The most recent existing `*-{feature}-implement.json` is updated in place
|
|
22
|
-
* (session_id/started_at preserved); a new dated file is created otherwise.
|
|
23
|
-
* `completed_at` + phase_summary are stamped when no task is left
|
|
24
|
-
* pending/in_progress. Fail-open: any error exits 0 silently.
|
|
25
|
-
*
|
|
26
|
-
* This is also the vantage point for TaskStart/TaskEnd telemetry: a
|
|
27
|
-
* pending→in_progress flip and a flip to done/skipped are the only two task
|
|
28
|
-
* transitions the harness can observe (tasks.json is the single place task
|
|
29
|
-
* status is written), so this hook diffs each task's previous status against
|
|
30
|
-
* `.morph/logs/task-times.json` and appends the matching event via the shared
|
|
31
|
-
* telemetry logger (see `updateTaskTimes` below). The same start/end pair
|
|
32
|
-
* feeds `duration_seconds` in the trace entry.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
36
|
-
import { join } from 'path';
|
|
37
|
-
import { runStandalone } from '../../shared/hook-response.js';
|
|
38
|
-
import { stateExists } from '../../shared/state-reader.js';
|
|
39
|
-
import { getFilePath, resolveMorphRoot } from '../../shared/payload-utils.js';
|
|
40
|
-
import { appendTelemetryEvent } from '../../shared/telemetry-logger.js';
|
|
41
|
-
|
|
42
|
-
function projectDir() {
|
|
43
|
-
return process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function extractFeature(filePath) {
|
|
47
|
-
const normalized = String(filePath).replace(/\\/g, '/');
|
|
48
|
-
const match = normalized.match(/\.morph\/features\/([^/]+)\/2-plan\/tasks\.json$/);
|
|
49
|
-
return match ? match[1] : null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Parse recap.md task blocks (`## T{N} — ...`) into
|
|
54
|
-
* { [id]: { standard, persona } }. Best-effort: missing fields stay null.
|
|
55
|
-
*/
|
|
56
|
-
function parseRecap(raw) {
|
|
57
|
-
const map = {};
|
|
58
|
-
const blocks = String(raw).split(/^## (?=T\d+)/m).slice(1);
|
|
59
|
-
for (const block of blocks) {
|
|
60
|
-
const id = (block.match(/^(T\d+)/) ?? [])[1];
|
|
61
|
-
if (!id) continue;
|
|
62
|
-
const standard = (block.match(/\*\*Standard:\*\*\s*([^\s(]+)/) ?? [])[1] ?? null;
|
|
63
|
-
const persona = (block.match(/\*\*Persona:\*\*\s*(.+)$/m) ?? [])[1]?.trim() ?? null;
|
|
64
|
-
map[id] = { standard, persona };
|
|
65
|
-
}
|
|
66
|
-
return map;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** session_id in the trace-schema format YYYY-MM-DD-HH-MM-SS. */
|
|
70
|
-
function sessionId(d) {
|
|
71
|
-
const p = (n) => String(n).padStart(2, '0');
|
|
72
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}-${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** tasks.json status → trace-schema status. Unsettled statuses are omitted. */
|
|
76
|
-
const STATUS_MAP = { done: 'completed', skipped: 'skipped', blocked: 'failed' };
|
|
77
|
-
|
|
78
|
-
const TASK_TIMES_PATH = join('.morph', 'logs', 'task-times.json');
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Read `.morph/logs/task-times.json`: `{ [feature]: { [taskId]: { status,
|
|
82
|
-
* start?, end? } } }`. Nested by feature — not flat by task id, as a literal
|
|
83
|
-
* reading of "keyed by taskId" might suggest — because task ids (T1, T2…)
|
|
84
|
-
* are only unique WITHIN a feature; a flat map would let two sibling
|
|
85
|
-
* features in the same tree (see worktree-state-resolution.test.js for that
|
|
86
|
-
* shape) clobber each other's start/end timestamps.
|
|
87
|
-
* Fail-open: a missing/corrupt file reads as {}.
|
|
88
|
-
*/
|
|
89
|
-
function readTaskTimes(root) {
|
|
90
|
-
const p = join(root, TASK_TIMES_PATH);
|
|
91
|
-
if (!existsSync(p)) return {};
|
|
92
|
-
try {
|
|
93
|
-
return JSON.parse(readFileSync(p, 'utf8'));
|
|
94
|
-
} catch {
|
|
95
|
-
return {};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function writeTaskTimes(root, allTimes) {
|
|
100
|
-
mkdirSync(join(root, '.morph', 'logs'), { recursive: true });
|
|
101
|
-
writeFileSync(join(root, TASK_TIMES_PATH), JSON.stringify(allTimes, null, 2), 'utf8');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Diff each task's PREVIOUS status (from `times`, this feature's slice of
|
|
106
|
-
* task-times.json) against its status in the tasks.json that was just
|
|
107
|
-
* written, and append the matching telemetry event via the shared logger —
|
|
108
|
-
* same shape/mechanism as every other event in events.jsonl, just a
|
|
109
|
-
* different `event` value.
|
|
110
|
-
*
|
|
111
|
-
* - pending → in_progress ⇒ TaskStart, stamp `start`
|
|
112
|
-
* - anything → done/skipped ⇒ TaskEnd (carries the new status), stamp `end`
|
|
113
|
-
* - first sight of a task id (no prior entry) only seeds the baseline,
|
|
114
|
-
* silently — there is no prior status to diff, and emitting an event here
|
|
115
|
-
* would fabricate a transition that never happened (e.g. a task whose
|
|
116
|
-
* first-ever tasks.json write already shows it `done`, from historical
|
|
117
|
-
* data predating this file).
|
|
118
|
-
*
|
|
119
|
-
* Mutates `times` in place and returns whether anything changed, so the
|
|
120
|
-
* caller only rewrites task-times.json when needed and the entries built
|
|
121
|
-
* right after this call see the freshly-stamped start/end for
|
|
122
|
-
* `duration_seconds`.
|
|
123
|
-
*/
|
|
124
|
-
function updateTaskTimes(times, tasks, feature, root, nowIso) {
|
|
125
|
-
let changed = false;
|
|
126
|
-
for (const t of tasks) {
|
|
127
|
-
const prev = times[t.id];
|
|
128
|
-
if (!prev) {
|
|
129
|
-
times[t.id] = { status: t.status };
|
|
130
|
-
changed = true;
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (prev.status === t.status) continue;
|
|
134
|
-
|
|
135
|
-
if (prev.status === 'pending' && t.status === 'in_progress') {
|
|
136
|
-
appendTelemetryEvent({ event: 'TaskStart', feature, task: t.id, phase: 'implement' }, root);
|
|
137
|
-
times[t.id] = { ...prev, status: t.status, start: nowIso };
|
|
138
|
-
} else if (t.status === 'done' || t.status === 'skipped') {
|
|
139
|
-
appendTelemetryEvent({ event: 'TaskEnd', feature, task: t.id, status: t.status, phase: 'implement' }, root);
|
|
140
|
-
times[t.id] = { ...prev, status: t.status, end: nowIso };
|
|
141
|
-
} else {
|
|
142
|
-
times[t.id] = { ...prev, status: t.status };
|
|
143
|
-
}
|
|
144
|
-
changed = true;
|
|
145
|
-
}
|
|
146
|
-
return changed;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Seconds between recorded start/end for a task, or null when either is missing. */
|
|
150
|
-
function taskDuration(times, taskId) {
|
|
151
|
-
const rec = times[taskId];
|
|
152
|
-
if (!rec?.start || !rec?.end) return null;
|
|
153
|
-
const secs = (new Date(rec.end).getTime() - new Date(rec.start).getTime()) / 1000;
|
|
154
|
-
return Number.isFinite(secs) ? Math.round(secs * 10) / 10 : null;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @param {object} payload - parsed PostToolUse stdin payload (Write|Edit)
|
|
159
|
-
* @returns {Promise<null>} always null — this hook is pure side-effect (trace
|
|
160
|
-
* file + task-times.json), never prints/injects context.
|
|
161
|
-
*/
|
|
162
|
-
export async function run(payload) {
|
|
163
|
-
try {
|
|
164
|
-
await generateTrace(payload);
|
|
165
|
-
} catch {
|
|
166
|
-
// Fail-open
|
|
167
|
-
}
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async function generateTrace(payload) {
|
|
172
|
-
if (!payload) return;
|
|
173
|
-
|
|
174
|
-
const filePath = getFilePath(payload);
|
|
175
|
-
const feature = filePath ? extractFeature(filePath) : null;
|
|
176
|
-
if (!feature) return;
|
|
177
|
-
|
|
178
|
-
// Read the sources — and write the trace — in the tree that owns the written
|
|
179
|
-
// tasks.json. Resolving from the session instead made this hook INERT for
|
|
180
|
-
// every worktree feature: the root's copy of the feature has no
|
|
181
|
-
// `2-plan/tasks.json`, so the `existsSync` guard below returned silently and
|
|
182
|
-
// no trace was ever produced for work done in a worktree.
|
|
183
|
-
const cwd = resolveMorphRoot(filePath, projectDir());
|
|
184
|
-
if (!stateExists(cwd)) return;
|
|
185
|
-
|
|
186
|
-
const featureDir = join(cwd, '.morph', 'features', feature);
|
|
187
|
-
const tasksPath = join(featureDir, '2-plan', 'tasks.json');
|
|
188
|
-
if (!existsSync(tasksPath)) return;
|
|
189
|
-
const tasksDoc = JSON.parse(readFileSync(tasksPath, 'utf8'));
|
|
190
|
-
const tasks = Array.isArray(tasksDoc.tasks) ? tasksDoc.tasks : [];
|
|
191
|
-
|
|
192
|
-
const now = new Date();
|
|
193
|
-
|
|
194
|
-
// TaskStart/TaskEnd telemetry + duration_seconds source: diff each task's
|
|
195
|
-
// previous status (persisted below) against the status just written.
|
|
196
|
-
const allTimes = readTaskTimes(cwd);
|
|
197
|
-
const times = allTimes[feature] ?? (allTimes[feature] = {});
|
|
198
|
-
if (updateTaskTimes(times, tasks, feature, cwd, now.toISOString())) {
|
|
199
|
-
writeTaskTimes(cwd, allTimes);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
let recap = {};
|
|
203
|
-
const recapPath = join(featureDir, '3-implement', 'recap.md');
|
|
204
|
-
if (existsSync(recapPath)) {
|
|
205
|
-
try { recap = parseRecap(readFileSync(recapPath, 'utf8')); } catch { recap = {}; }
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const tracesDir = join(cwd, '.morph', 'traces');
|
|
209
|
-
mkdirSync(tracesDir, { recursive: true });
|
|
210
|
-
|
|
211
|
-
// Multi-session phases update the same file incrementally (trace-schema):
|
|
212
|
-
// reuse the most recent trace for this feature+phase if one exists.
|
|
213
|
-
const suffix = `-${feature}-implement.json`;
|
|
214
|
-
const existing = readdirSync(tracesDir)
|
|
215
|
-
.filter(f => /^\d{4}-\d{2}-\d{2}-/.test(f) && f.endsWith(suffix))
|
|
216
|
-
.sort()
|
|
217
|
-
.pop();
|
|
218
|
-
|
|
219
|
-
let previous = null;
|
|
220
|
-
if (existing) {
|
|
221
|
-
try { previous = JSON.parse(readFileSync(join(tracesDir, existing), 'utf8')); } catch { previous = null; }
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const fileName = existing ?? `${now.toISOString().slice(0, 10)}${suffix}`;
|
|
225
|
-
|
|
226
|
-
const entries = tasks
|
|
227
|
-
.filter(t => STATUS_MAP[t.status])
|
|
228
|
-
.map(t => {
|
|
229
|
-
const recapEntry = recap[t.id] ?? {};
|
|
230
|
-
return {
|
|
231
|
-
id: t.id,
|
|
232
|
-
title: t.title ?? null,
|
|
233
|
-
status: STATUS_MAP[t.status],
|
|
234
|
-
standards_consulted: recapEntry.standard ? [recapEntry.standard] : [],
|
|
235
|
-
agent_persona: recapEntry.persona ?? null,
|
|
236
|
-
sub_agent_dispatched: recapEntry.persona
|
|
237
|
-
? /^morph-|general-purpose/.test(recapEntry.persona)
|
|
238
|
-
: false,
|
|
239
|
-
files_modified: Array.isArray(t.outputs) ? t.outputs.filter(o => typeof o === 'string') : [],
|
|
240
|
-
duration_seconds: taskDuration(times, t.id),
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
const allSettled = tasks.length > 0 &&
|
|
245
|
-
tasks.every(t => t.status !== 'pending' && t.status !== 'in_progress');
|
|
246
|
-
|
|
247
|
-
const standardCounts = {};
|
|
248
|
-
for (const entry of entries) {
|
|
249
|
-
for (const std of entry.standards_consulted) {
|
|
250
|
-
standardCounts[std] = (standardCounts[std] ?? 0) + 1;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
const mostUsed = Object.entries(standardCounts)
|
|
254
|
-
.sort((a, b) => b[1] - a[1])
|
|
255
|
-
.slice(0, 3)
|
|
256
|
-
.map(([std]) => std);
|
|
257
|
-
|
|
258
|
-
const trace = {
|
|
259
|
-
schema_version: '1.0',
|
|
260
|
-
session_id: previous?.session_id ?? sessionId(now),
|
|
261
|
-
feature,
|
|
262
|
-
phase: 'implement',
|
|
263
|
-
started_at: previous?.started_at ?? now.toISOString(),
|
|
264
|
-
completed_at: allSettled ? (previous?.completed_at ?? now.toISOString()) : null,
|
|
265
|
-
tasks: entries,
|
|
266
|
-
phase_summary: {
|
|
267
|
-
tasks_completed: entries.filter(e => e.status === 'completed').length,
|
|
268
|
-
tasks_failed: entries.filter(e => e.status === 'failed').length,
|
|
269
|
-
standards_most_used: mostUsed,
|
|
270
|
-
sub_agents_dispatched: entries.filter(e => e.sub_agent_dispatched).length,
|
|
271
|
-
},
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
writeFileSync(join(tracesDir, fileName), JSON.stringify(trace, null, 2), 'utf8');
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
await runStandalone(run);
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Scope: universal | Validates spec.md required sections
|
|
3
|
-
# MORPH-SPEC Pre-Commit Hook: Spec Validation
|
|
4
|
-
# Validates that spec.md files have required sections
|
|
5
|
-
|
|
6
|
-
echo "🔍 Validating spec files..."
|
|
7
|
-
|
|
8
|
-
# Find modified spec.md files
|
|
9
|
-
SPEC_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep 'spec\.md$')
|
|
10
|
-
|
|
11
|
-
if [ -z "$SPEC_FILES" ]; then
|
|
12
|
-
echo "✓ No spec files modified"
|
|
13
|
-
exit 0
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
HAS_ERRORS=false
|
|
17
|
-
|
|
18
|
-
for spec_file in $SPEC_FILES; do
|
|
19
|
-
echo "Checking: $spec_file"
|
|
20
|
-
|
|
21
|
-
# Required sections
|
|
22
|
-
REQUIRED_SECTIONS=(
|
|
23
|
-
"## 📋 Metadata"
|
|
24
|
-
"## 🎯 Overview"
|
|
25
|
-
"## 🏗️ Technical Design"
|
|
26
|
-
"## ✅ Acceptance Criteria"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
for section in "${REQUIRED_SECTIONS[@]}"; do
|
|
30
|
-
if ! grep -q "$section" "$spec_file"; then
|
|
31
|
-
echo " ❌ Missing section: $section"
|
|
32
|
-
HAS_ERRORS=true
|
|
33
|
-
fi
|
|
34
|
-
done
|
|
35
|
-
|
|
36
|
-
# Check if has at least one user story or requirement
|
|
37
|
-
if ! grep -qi "user story\|requirement\|acceptance criteria" "$spec_file"; then
|
|
38
|
-
echo " ⚠️ Warning: No user stories or requirements found"
|
|
39
|
-
fi
|
|
40
|
-
done
|
|
41
|
-
|
|
42
|
-
if [ "$HAS_ERRORS" = true ]; then
|
|
43
|
-
echo ""
|
|
44
|
-
echo "❌ COMMIT BLOCKED: spec.md files are incomplete"
|
|
45
|
-
echo " Add missing sections before committing"
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
echo "✓ All spec files are valid"
|
|
50
|
-
exit 0
|