@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.
Files changed (304) hide show
  1. package/CLAUDE.md +53 -44
  2. package/README.md +3 -4
  3. package/bin/morph-spec.js +38 -55
  4. package/bin/validate.js +5 -134
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -2
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +75 -18
  11. package/framework/commands/create-umbraco-lp.md +2 -0
  12. package/framework/commands/migrate-lp-to-umbraco.md +2 -0
  13. package/framework/commands/morph-apply.md +5 -28
  14. package/framework/commands/morph-archive.md +2 -2
  15. package/framework/commands/morph-hotfix.md +8 -8
  16. package/framework/commands/morph-infra.md +17 -203
  17. package/framework/commands/morph-media.md +3 -0
  18. package/framework/commands/morph-preflight.md +7 -31
  19. package/framework/commands/morph-proposal.md +2 -50
  20. package/framework/commands/morph-status.md +3 -3
  21. package/framework/commands/morph-troubleshoot.md +2 -14
  22. package/framework/docs/CLI.md +585 -286
  23. package/framework/evals/review-checklist.md +82 -41
  24. package/framework/hooks/README.md +10 -11
  25. package/framework/hooks/claude-code/core/comment-guard.js +1 -1
  26. package/framework/hooks/claude-code/core/denylist.json +33 -3
  27. package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
  28. package/framework/hooks/claude-code/core/gate-guard.js +2 -11
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
  30. package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
  31. package/framework/hooks/claude-code/core/state-sync.js +17 -19
  32. package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
  33. package/framework/hooks/claude-code/core/validate-completion.js +33 -97
  34. package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
  35. package/framework/hooks/claude-code/core/worktree-context.js +5 -4
  36. package/framework/hooks/claude-code/statusline.py +55 -41
  37. package/framework/hooks/package.json +3 -0
  38. package/framework/hooks/shared/activity-logger.js +1 -1
  39. package/framework/hooks/shared/build-check.js +10 -6
  40. package/framework/hooks/shared/command-tokens.js +169 -0
  41. package/framework/hooks/shared/comment-rules.js +1 -1
  42. package/framework/hooks/shared/comment-scanner.js +10 -23
  43. package/framework/hooks/shared/comment-stripper.js +1 -1
  44. package/framework/hooks/shared/e2e-check.js +67 -31
  45. package/framework/hooks/shared/errata.js +72 -0
  46. package/framework/hooks/shared/feature-locator.js +1 -1
  47. package/framework/hooks/shared/frontend-locations.js +6 -0
  48. package/framework/hooks/shared/gate-question.js +18 -0
  49. package/framework/hooks/shared/phase-utils.js +15 -32
  50. package/framework/hooks/shared/slice-progress.js +118 -0
  51. package/framework/hooks/shared/spawn-command.js +23 -0
  52. package/framework/hooks/shared/state-reader.js +61 -87
  53. package/framework/hooks/shared/state-writer.js +3 -3
  54. package/framework/mcp/registry.json +3 -4
  55. package/framework/phases.json +23 -39
  56. package/framework/rules/ai-agents-standards.md +18 -16
  57. package/framework/rules/asaas-standards.md +3 -1
  58. package/framework/rules/csharp-standards.md +10 -8
  59. package/framework/rules/data-standards.md +5 -5
  60. package/framework/rules/dotnet-templates.md +1 -1
  61. package/framework/rules/frontend-standards.md +4 -2
  62. package/framework/rules/gsap-standards.md +10 -5
  63. package/framework/rules/hangfire-standards.md +3 -1
  64. package/framework/rules/infrastructure-standards.md +4 -2
  65. package/framework/rules/neon-auth-standards.md +3 -1
  66. package/framework/rules/neon-standards.md +6 -4
  67. package/framework/rules/nextjs-standards.md +13 -11
  68. package/framework/rules/nextjs-templates.md +1 -1
  69. package/framework/rules/resend-standards.md +3 -1
  70. package/framework/schemas/config.schema.json +14 -9
  71. package/framework/schemas/feature.schema.json +15 -52
  72. package/framework/skills/README.md +4 -5
  73. package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
  74. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
  75. package/framework/skills/core/morph-apply/SKILL.md +150 -223
  76. package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
  77. package/framework/skills/core/morph-machine/SKILL.md +4 -4
  78. package/framework/skills/core/morph-media/SKILL.md +3 -3
  79. package/framework/skills/core/morph-plan/SKILL.md +138 -300
  80. package/framework/skills/core/morph-review/SKILL.md +112 -137
  81. package/framework/skills/core/morph-standards/SKILL.md +29 -41
  82. package/framework/skills/core/morph-uiux/SKILL.md +78 -153
  83. package/framework/standards/STANDARDS.json +90 -1219
  84. package/framework/standards/STANDARDS.meta.json +1305 -0
  85. package/framework/standards/ai-agents/evals-with-cache.md +2 -3
  86. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  87. package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
  88. package/framework/standards/ai-agents/prompt-sources.md +6 -4
  89. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  90. package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
  91. package/framework/standards/ai-agents/setup.md +2 -2
  92. package/framework/standards/ai-agents/structured-output.md +1 -1
  93. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  94. package/framework/standards/architecture/code-comments.md +8 -8
  95. package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
  96. package/framework/standards/backend/api/validation.md +135 -47
  97. package/framework/standards/backend/database/postgresql/database.md +11 -7
  98. package/framework/standards/backend/database/vector-search-rag.md +207 -324
  99. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
  100. package/framework/standards/backend/dotnet/result-pattern.md +40 -6
  101. package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
  102. package/framework/standards/backend/dotnet/scrutor.md +1 -1
  103. package/framework/standards/backend/dotnet/testing.md +1 -1
  104. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
  105. package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
  106. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
  107. package/framework/standards/data/nosql/cache/redis.md +120 -44
  108. package/framework/standards/data/nosql/cosmos-db.md +114 -71
  109. package/framework/standards/frontend/design-system/animations.md +1 -1
  110. package/framework/standards/frontend/design-system/naming.md +1 -1
  111. package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
  112. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
  113. package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
  114. package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
  115. package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
  116. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  117. package/framework/templates/README.md +2 -3
  118. package/framework/templates/REGISTRY.json +6 -170
  119. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  120. package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
  121. package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
  122. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
  123. package/framework/templates/docs/plan.md +72 -0
  124. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  125. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
  126. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  127. package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
  128. package/framework/templates/infrastructure/github/README.md +2 -1
  129. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
  130. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  131. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  132. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  133. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  134. package/package.json +1 -1
  135. package/src/commands/e2e/e2e.js +2 -2
  136. package/src/commands/observability/cost.js +18 -60
  137. package/src/commands/observability/telemetry.js +3 -5
  138. package/src/commands/project/doctor-procs.js +1 -1
  139. package/src/commands/project/doctor.js +9 -9
  140. package/src/commands/project/fleet.js +4 -4
  141. package/src/commands/project/heavy.js +3 -3
  142. package/src/commands/project/init.js +6 -1
  143. package/src/commands/project/setup-infra-cmd.js +2 -1
  144. package/src/commands/project/stack-scan.js +58 -0
  145. package/src/commands/project/standards.js +79 -25
  146. package/src/commands/project/status.js +29 -69
  147. package/src/commands/project/update.js +33 -19
  148. package/src/commands/project/worktree.js +1 -1
  149. package/src/commands/state/advance.js +62 -46
  150. package/src/commands/state/approve.js +95 -138
  151. package/src/commands/state/archive.js +20 -84
  152. package/src/commands/state/auto-approval.js +68 -0
  153. package/src/commands/state/create.js +28 -1
  154. package/src/commands/state/delete.js +1 -1
  155. package/src/commands/state/finish.js +11 -23
  156. package/src/commands/state/gate-check.js +86 -95
  157. package/src/commands/state/gate-decision.js +6 -4
  158. package/src/commands/state/handoff.js +1 -1
  159. package/src/commands/state/list.js +9 -9
  160. package/src/commands/state/report-check.js +157 -311
  161. package/src/commands/state/retype.js +9 -3
  162. package/src/commands/validation/mutate.js +77 -14
  163. package/src/commands/validation/plan-check.js +81 -0
  164. package/src/commands/validation/validate-feature.js +60 -135
  165. package/src/commands/validation/verify.js +41 -82
  166. package/src/core/paths/output-schema.js +20 -67
  167. package/src/core/state/gate-order.js +5 -6
  168. package/src/core/state/lifecycle-audit.js +2 -2
  169. package/src/core/state/state-manager.js +17 -98
  170. package/src/core/templates/template-registry.js +2 -2
  171. package/src/core/templates/template-seed.js +22 -0
  172. package/src/core/work-types.js +10 -10
  173. package/src/dashboard/app/App.jsx +0 -2
  174. package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
  175. package/src/dashboard/app/components/CommandPalette.jsx +7 -8
  176. package/src/dashboard/app/components/EntityLink.jsx +1 -2
  177. package/src/dashboard/app/components/Markdown.jsx +1 -1
  178. package/src/dashboard/app/components/Sidebar.jsx +1 -2
  179. package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
  180. package/src/dashboard/app/lib/phases.js +2 -4
  181. package/src/dashboard/app/pages/Activity.jsx +1 -1
  182. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
  183. package/src/dashboard/app/pages/Features.jsx +9 -9
  184. package/src/dashboard/app/pages/Guide.jsx +2 -2
  185. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
  186. package/src/dashboard/app/pages/RulesMap.jsx +3 -3
  187. package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
  188. package/src/dashboard/app/pages/Standards.jsx +1 -1
  189. package/src/dashboard/scanner/activity-scanner.js +6 -16
  190. package/src/dashboard/scanner/agents-scanner.js +1 -1
  191. package/src/dashboard/scanner/docs-scanner.js +5 -23
  192. package/src/dashboard/scanner/features-scanner.js +95 -60
  193. package/src/dashboard/scanner/index.js +0 -2
  194. package/src/dashboard/scanner/project-api.js +1 -1
  195. package/src/dashboard/scanner/rules-scanner.js +15 -10
  196. package/src/dashboard/scanner/skills-scanner.js +2 -3
  197. package/src/dashboard/scanner/standards-scanner.js +2 -1
  198. package/src/lib/ci/ci-verify.js +17 -1
  199. package/src/lib/ci/github-ci.js +1 -1
  200. package/src/lib/detectors/index.js +3 -8
  201. package/src/lib/generators/handoff-generator.js +34 -65
  202. package/src/lib/generators/settings-generator.js +11 -89
  203. package/src/lib/git/evidence-staleness.js +1 -1
  204. package/src/lib/git/tree-fingerprint.js +19 -7
  205. package/src/lib/git/tree-state.js +2 -2
  206. package/src/lib/heavy/cleanup.js +1 -5
  207. package/src/lib/installers/memory-installer.js +6 -8
  208. package/src/lib/mutation/commands.js +3 -2
  209. package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
  210. package/src/lib/mutation/plan.js +14 -6
  211. package/src/lib/mutation/runner.js +1 -1
  212. package/src/lib/mutation/subset.js +46 -9
  213. package/src/lib/plan/artifact-vocabulary.js +60 -0
  214. package/src/lib/plan/checks.js +234 -0
  215. package/src/lib/plan/plan-parser.js +209 -0
  216. package/src/lib/plan/plan-template.js +18 -0
  217. package/src/lib/review-reports.js +222 -0
  218. package/src/lib/risk-detector.js +134 -135
  219. package/src/lib/specs/adr-index.js +50 -69
  220. package/src/lib/specs/design-seed.js +18 -0
  221. package/src/lib/specs/frontmatter.js +44 -0
  222. package/src/lib/specs/gfm-table.js +17 -0
  223. package/src/lib/specs/old-format.js +68 -0
  224. package/src/lib/specs/revocations.js +37 -95
  225. package/src/lib/specs/screen-checklist.js +18 -17
  226. package/src/lib/stack/dotnet-build-ignores.js +47 -0
  227. package/src/lib/stack/stack-profile.js +1 -1
  228. package/src/lib/stack/stack-scan.js +267 -0
  229. package/src/lib/standards/registry.js +43 -0
  230. package/src/lib/standards/sections.js +58 -0
  231. package/src/lib/tasks/eval-node.js +4 -12
  232. package/src/lib/tasks/test-nodes.js +1 -74
  233. package/src/lib/tasks/test-runner.js +75 -117
  234. package/src/lib/telemetry/metrics.js +8 -69
  235. package/src/lib/telemetry/telemetry-reader.js +5 -118
  236. package/src/lib/telemetry/verify-event.js +5 -7
  237. package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
  238. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
  239. package/src/lib/validators/reuse/reuse-validator.js +24 -48
  240. package/src/lib/validators/reuse/symbol-scanner.js +2 -4
  241. package/src/lib/validators/traceability-check.js +85 -129
  242. package/src/lib/validators/validation-runner.js +8 -88
  243. package/src/lib/validators/verify-runner.js +134 -301
  244. package/src/lib/worktree/docker-audit.js +1 -2
  245. package/src/lib/worktree/fleet-scanner.js +25 -38
  246. package/src/scripts/setup-infra.js +42 -22
  247. package/src/utils/agents-installer.js +92 -13
  248. package/src/utils/claude-settings-manager.js +0 -35
  249. package/src/utils/file-copier.js +34 -6
  250. package/src/utils/hooks-installer.js +16 -9
  251. package/src/utils/skills-installer.js +1 -2
  252. package/framework/commands/commit.md +0 -166
  253. package/framework/evals/trace-schema.md +0 -67
  254. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  255. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  256. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  257. package/framework/hooks/shared/tasks-validator.js +0 -111
  258. package/framework/rubrics/backend.json +0 -41
  259. package/framework/rubrics/default.json +0 -36
  260. package/framework/rubrics/frontend.json +0 -36
  261. package/framework/rules/morph-task-tracking.md +0 -54
  262. package/framework/rules/morph-workflow.md +0 -244
  263. package/framework/schemas/tasks.schema.json +0 -79
  264. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  265. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  266. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  267. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  268. package/framework/templates/docs/proposal.md +0 -192
  269. package/framework/templates/docs/spec.md +0 -57
  270. package/framework/templates/docs/ui-components.md +0 -52
  271. package/framework/templates/docs/ui-design-system.md +0 -76
  272. package/framework/templates/docs/ui-flows.md +0 -167
  273. package/framework/templates/examples/spec-examples.md +0 -91
  274. package/framework/templates/feature/decisions.md +0 -124
  275. package/framework/templates/feature/recap.md +0 -145
  276. package/framework/templates/project/validate.js +0 -97
  277. package/framework/templates/tasks.template.json +0 -17
  278. package/src/commands/state/usecase.js +0 -143
  279. package/src/commands/tasks/dag.js +0 -181
  280. package/src/dashboard/app/pages/Rubrics.jsx +0 -97
  281. package/src/dashboard/scanner/rubrics-scanner.js +0 -12
  282. package/src/lib/agents/output-collector.js +0 -76
  283. package/src/lib/evaluator-report.js +0 -91
  284. package/src/lib/specs/acervo-format.js +0 -70
  285. package/src/lib/specs/acervo-lifecycle.js +0 -264
  286. package/src/lib/specs/done-criteria-check.js +0 -200
  287. package/src/lib/specs/usecase-check.js +0 -301
  288. package/src/lib/specs/usecase-promoter.js +0 -301
  289. package/src/lib/specs/usecase-registry.js +0 -237
  290. package/src/lib/tasks/dispatch-planner.js +0 -319
  291. package/src/lib/tasks/mermaid-renderer.js +0 -132
  292. package/src/lib/tasks/task-classifier.js +0 -30
  293. package/src/lib/tasks/task-parser.js +0 -209
  294. package/src/lib/tasks/tasks-json-parser.js +0 -296
  295. package/src/lib/tasks/test-scope.js +0 -597
  296. package/src/lib/validators/architecture/architecture-validator.js +0 -270
  297. package/src/lib/validators/architecture/index.js +0 -7
  298. package/src/lib/validators/css/css-validator.js +0 -340
  299. package/src/lib/validators/css/index.js +0 -7
  300. package/src/lib/validators/design-system/design-system-validator.js +0 -237
  301. package/src/lib/validators/design-system/index.js +0 -7
  302. package/src/lib/validators/score-aggregator.js +0 -244
  303. package/src/lib/validators/ui/index.js +0 -7
  304. 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