@polymorphism-tech/morph-spec 4.7.1 → 4.7.2

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 (232) hide show
  1. package/.morph/.morphversion +5 -0
  2. package/.morph/analytics/threads-log.jsonl +5 -0
  3. package/.morph/config/config.json +8 -0
  4. package/.morph/framework/agents.json +1815 -0
  5. package/.morph/framework/hooks/README.md +205 -0
  6. package/.morph/framework/hooks/claude-code/notification/approval-reminder.js +54 -0
  7. package/.morph/framework/hooks/claude-code/post-tool-use/dispatch.js +83 -0
  8. package/.morph/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +42 -0
  9. package/.morph/framework/hooks/claude-code/pre-compact/save-morph-context.js +61 -0
  10. package/.morph/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +71 -0
  11. package/.morph/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +58 -0
  12. package/.morph/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +64 -0
  13. package/.morph/framework/hooks/claude-code/session-start/inject-morph-context.js +94 -0
  14. package/.morph/framework/hooks/claude-code/statusline.py +538 -0
  15. package/.morph/framework/hooks/claude-code/statusline.sh +7 -0
  16. package/.morph/framework/hooks/claude-code/stop/validate-completion.js +88 -0
  17. package/.morph/framework/hooks/claude-code/user-prompt/enrich-prompt.js +91 -0
  18. package/.morph/framework/hooks/git/commit-msg/conventional-commits.sh +33 -0
  19. package/.morph/framework/hooks/git/pre-commit/agents.sh +25 -0
  20. package/.morph/framework/hooks/git/pre-commit/orchestrator.sh +64 -0
  21. package/.morph/framework/hooks/git/pre-commit/specs.sh +50 -0
  22. package/.morph/framework/hooks/git/pre-push/run-tests.sh +44 -0
  23. package/.morph/framework/hooks/shared/hook-response.js +45 -0
  24. package/.morph/framework/hooks/shared/phase-utils.js +129 -0
  25. package/.morph/framework/hooks/shared/state-reader.js +138 -0
  26. package/.morph/framework/hooks/shared/stdin-reader.js +26 -0
  27. package/.morph/framework/standards/STANDARDS.json +933 -0
  28. package/.morph/framework/standards/ai-agents/blazor-ui.md +364 -0
  29. package/.morph/framework/standards/ai-agents/production.md +415 -0
  30. package/.morph/framework/standards/ai-agents/setup.md +418 -0
  31. package/.morph/framework/standards/ai-agents/team-orchestration.md +479 -0
  32. package/.morph/framework/standards/ai-agents/workflows.md +354 -0
  33. package/.morph/framework/standards/architecture/ddd/aggregates.md +120 -0
  34. package/.morph/framework/standards/architecture/ddd/bounded-contexts.md +105 -0
  35. package/.morph/framework/standards/architecture/ddd/complexity-levels.md +108 -0
  36. package/.morph/framework/standards/architecture/ddd/entities.md +99 -0
  37. package/.morph/framework/standards/architecture/ddd/ubiquitous-language.md +58 -0
  38. package/.morph/framework/standards/architecture/ddd/value-objects.md +124 -0
  39. package/.morph/framework/standards/backend/api/minimal-api.md +494 -0
  40. package/.morph/framework/standards/backend/api/rest.md +492 -0
  41. package/.morph/framework/standards/backend/api/validation.md +88 -0
  42. package/.morph/framework/standards/backend/authentication/passkeys.md +428 -0
  43. package/.morph/framework/standards/backend/database/ef-core.md +199 -0
  44. package/.morph/framework/standards/backend/database/migrations.md +393 -0
  45. package/.morph/framework/standards/backend/database/postgresql/database.md +352 -0
  46. package/.morph/framework/standards/backend/database/repository-patterns.md +528 -0
  47. package/.morph/framework/standards/backend/database/vector-search-rag.md +541 -0
  48. package/.morph/framework/standards/backend/dotnet/async.md +366 -0
  49. package/.morph/framework/standards/backend/dotnet/core.md +117 -0
  50. package/.morph/framework/standards/backend/dotnet/di.md +439 -0
  51. package/.morph/framework/standards/backend/dotnet/program-cs-checklist.md +92 -0
  52. package/.morph/framework/standards/backend/integrations/asaas/asaas-api.md +216 -0
  53. package/.morph/framework/standards/backend/integrations/clerk/clerk-auth.md +290 -0
  54. package/.morph/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +350 -0
  55. package/.morph/framework/standards/backend/integrations/resend/resend-email.md +385 -0
  56. package/.morph/framework/standards/context/analytics.md +96 -0
  57. package/.morph/framework/standards/context/bundles.md +110 -0
  58. package/.morph/framework/standards/context/priming.md +78 -0
  59. package/.morph/framework/standards/core/architecture.md +185 -0
  60. package/.morph/framework/standards/core/coding.md +214 -0
  61. package/.morph/framework/standards/core/git-branching-strategy.md +403 -0
  62. package/.morph/framework/standards/core/git.md +185 -0
  63. package/.morph/framework/standards/core/testing.md +295 -0
  64. package/.morph/framework/standards/data/nosql/blob-storage.md +102 -0
  65. package/.morph/framework/standards/data/nosql/cache/redis.md +97 -0
  66. package/.morph/framework/standards/data/nosql/cosmos-db.md +118 -0
  67. package/.morph/framework/standards/data/vector-search/azure-ai-search.md +121 -0
  68. package/.morph/framework/standards/data/vector-search/rag-chunking.md +104 -0
  69. package/.morph/framework/standards/frontend/blazor/design-checklist.md +222 -0
  70. package/.morph/framework/standards/frontend/blazor/fluent-ui-setup.md +595 -0
  71. package/.morph/framework/standards/frontend/blazor/fluent-ui.md +137 -0
  72. package/.morph/framework/standards/frontend/blazor/html-conversion.md +184 -0
  73. package/.morph/framework/standards/frontend/blazor/lifecycle.md +195 -0
  74. package/.morph/framework/standards/frontend/blazor/pitfalls.md +198 -0
  75. package/.morph/framework/standards/frontend/blazor/state.md +191 -0
  76. package/.morph/framework/standards/frontend/design-system/animations.md +151 -0
  77. package/.morph/framework/standards/frontend/design-system/naming.md +64 -0
  78. package/.morph/framework/standards/frontend/nextjs/app-router.md +123 -0
  79. package/.morph/framework/standards/frontend/nextjs/components.md +132 -0
  80. package/.morph/framework/standards/frontend/nextjs/data-fetching.md +126 -0
  81. package/.morph/framework/standards/frontend/nextjs/forms.md +128 -0
  82. package/.morph/framework/standards/frontend/nextjs/naming-conventions.md +67 -0
  83. package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +215 -0
  84. package/.morph/framework/standards/frontend/nextjs/project-structure.md +102 -0
  85. package/.morph/framework/standards/frontend/nextjs/state-management.md +72 -0
  86. package/.morph/framework/standards/frontend/nextjs/testing.md +111 -0
  87. package/.morph/framework/standards/infrastructure/azure/azure.md +624 -0
  88. package/.morph/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +422 -0
  89. package/.morph/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +516 -0
  90. package/.morph/framework/standards/infrastructure/azure/devops/local-development.md +520 -0
  91. package/.morph/framework/standards/infrastructure/azure/services/functions.md +486 -0
  92. package/.morph/framework/standards/infrastructure/azure/services/service-bus.md +459 -0
  93. package/.morph/framework/standards/infrastructure/azure/services/storage.md +407 -0
  94. package/.morph/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -0
  95. package/.morph/framework/standards/infrastructure/supabase/mcp-setup.md +252 -0
  96. package/.morph/framework/standards/infrastructure/supabase/supabase-auth.md +176 -0
  97. package/.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md +169 -0
  98. package/.morph/framework/standards/infrastructure/supabase/supabase-rls.md +184 -0
  99. package/.morph/framework/standards/infrastructure/supabase/supabase-storage.md +153 -0
  100. package/.morph/framework/standards/integration/api/graphql.md +91 -0
  101. package/.morph/framework/standards/integration/api/grpc.md +114 -0
  102. package/.morph/framework/standards/integration/api/rest-design.md +95 -0
  103. package/.morph/framework/standards/integration/event-driven/cqrs.md +101 -0
  104. package/.morph/framework/standards/integration/event-driven/event-sourcing.md +124 -0
  105. package/.morph/framework/standards/integration/event-driven/service-bus.md +95 -0
  106. package/.morph/framework/standards/integration/mcp/mcp-tools.md +384 -0
  107. package/.morph/framework/standards/observability/logging.md +131 -0
  108. package/.morph/framework/standards/observability/metrics.md +121 -0
  109. package/.morph/framework/standards/observability/monitoring.md +114 -0
  110. package/.morph/framework/standards/observability/tracing.md +132 -0
  111. package/.morph/framework/standards/workflows/parallel-execution.md +112 -0
  112. package/.morph/framework/standards/workflows/thread-management.md +113 -0
  113. package/.morph/framework/templates/.idea/morph-templates.xml +92 -0
  114. package/.morph/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  115. package/.morph/framework/templates/IDE-SNIPPETS.md +266 -0
  116. package/.morph/framework/templates/README.md +814 -0
  117. package/.morph/framework/templates/REGISTRY.json +1888 -0
  118. package/.morph/framework/templates/code/dotnet/backend/repository.cs +141 -0
  119. package/.morph/framework/templates/code/dotnet/backend/service.cs +139 -0
  120. package/.morph/framework/templates/code/dotnet/contracts/Commands.cs +74 -0
  121. package/.morph/framework/templates/code/dotnet/contracts/Entities.cs +25 -0
  122. package/.morph/framework/templates/code/dotnet/contracts/Queries.cs +74 -0
  123. package/.morph/framework/templates/code/dotnet/contracts/README.md +74 -0
  124. package/.morph/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -0
  125. package/.morph/framework/templates/code/dotnet/contracts/contracts-level1.cs +69 -0
  126. package/.morph/framework/templates/code/dotnet/contracts/contracts-level2.cs +86 -0
  127. package/.morph/framework/templates/code/dotnet/contracts/contracts-level3.cs +41 -0
  128. package/.morph/framework/templates/code/dotnet/database/migration.cs +83 -0
  129. package/.morph/framework/templates/code/dotnet/frontend/component.razor +239 -0
  130. package/.morph/framework/templates/code/dotnet/jobs/agent.cs +163 -0
  131. package/.morph/framework/templates/code/dotnet/jobs/job.cs +171 -0
  132. package/.morph/framework/templates/code/dotnet/test.cs +239 -0
  133. package/.morph/framework/templates/code/sql/rls-policy.sql +57 -0
  134. package/.morph/framework/templates/code/sql/supabase-migration.sql +100 -0
  135. package/.morph/framework/templates/code/sql/supabase-migration.template.sql +113 -0
  136. package/.morph/framework/templates/code/typescript/contracts.ts +168 -0
  137. package/.morph/framework/templates/context/CONTEXT-FEATURE.md +276 -0
  138. package/.morph/framework/templates/context/CONTEXT.md +181 -0
  139. package/.morph/framework/templates/docs/clarifications.md +253 -0
  140. package/.morph/framework/templates/docs/onboarding.md +123 -0
  141. package/.morph/framework/templates/docs/proposal.md +182 -0
  142. package/.morph/framework/templates/docs/schema-analysis.md +119 -0
  143. package/.morph/framework/templates/docs/spec.md +198 -0
  144. package/.morph/framework/templates/docs/ui-components.md +124 -0
  145. package/.morph/framework/templates/docs/ui-design-system.md +76 -0
  146. package/.morph/framework/templates/docs/ui-flows.md +167 -0
  147. package/.morph/framework/templates/docs/ui-mockups.md +98 -0
  148. package/.morph/framework/templates/docs/user-stories.md +34 -0
  149. package/.morph/framework/templates/examples/design-system-examples.md +357 -0
  150. package/.morph/framework/templates/examples/spec-examples.md +90 -0
  151. package/.morph/framework/templates/feature/decisions.md +187 -0
  152. package/.morph/framework/templates/feature/recap.md +146 -0
  153. package/.morph/framework/templates/feature/tasks.md +199 -0
  154. package/.morph/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -0
  155. package/.morph/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -0
  156. package/.morph/framework/templates/frontend/nextjs/env.mjs.hbs +32 -0
  157. package/.morph/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -0
  158. package/.morph/framework/templates/frontend/nextjs/page.tsx.hbs +22 -0
  159. package/.morph/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -0
  160. package/.morph/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -0
  161. package/.morph/framework/templates/infrastructure/azure/Dockerfile.example +82 -0
  162. package/.morph/framework/templates/infrastructure/azure/README.md +286 -0
  163. package/.morph/framework/templates/infrastructure/azure/app-insights.bicep +63 -0
  164. package/.morph/framework/templates/infrastructure/azure/app-service.bicep +164 -0
  165. package/.morph/framework/templates/infrastructure/azure/container-app-env.bicep +49 -0
  166. package/.morph/framework/templates/infrastructure/azure/container-app.bicep +156 -0
  167. package/.morph/framework/templates/infrastructure/azure/deploy-checklist.md +426 -0
  168. package/.morph/framework/templates/infrastructure/azure/deploy.ps1 +229 -0
  169. package/.morph/framework/templates/infrastructure/azure/deploy.sh +208 -0
  170. package/.morph/framework/templates/infrastructure/azure/key-vault.bicep +91 -0
  171. package/.morph/framework/templates/infrastructure/azure/main.bicep +189 -0
  172. package/.morph/framework/templates/infrastructure/azure/parameters.dev.json +29 -0
  173. package/.morph/framework/templates/infrastructure/azure/parameters.prod.json +29 -0
  174. package/.morph/framework/templates/infrastructure/azure/parameters.staging.json +29 -0
  175. package/.morph/framework/templates/infrastructure/azure/sql-database.bicep +103 -0
  176. package/.morph/framework/templates/infrastructure/azure/storage.bicep +106 -0
  177. package/.morph/framework/templates/infrastructure/docker/Dockerfile.template +58 -0
  178. package/.morph/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -0
  179. package/.morph/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -0
  180. package/.morph/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -0
  181. package/.morph/framework/templates/infrastructure/docker/easypanel.template.json +54 -0
  182. package/.morph/framework/templates/infrastructure/github/README.md +593 -0
  183. package/.morph/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +22 -0
  184. package/.morph/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -0
  185. package/.morph/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -0
  186. package/.morph/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +61 -0
  187. package/.morph/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +31 -0
  188. package/.morph/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -0
  189. package/.morph/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -0
  190. package/.morph/framework/templates/integrations/asaas-client.cs +387 -0
  191. package/.morph/framework/templates/integrations/asaas-webhook.cs +351 -0
  192. package/.morph/framework/templates/integrations/azure-identity-config.cs +288 -0
  193. package/.morph/framework/templates/integrations/clerk-config.cs +258 -0
  194. package/.morph/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -0
  195. package/.morph/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -0
  196. package/.morph/framework/templates/meta-prompts/hops/hop-retry.md +78 -0
  197. package/.morph/framework/templates/meta-prompts/hops/hop-validation.md +97 -0
  198. package/.morph/framework/templates/meta-prompts/hops/hop-wrapper.md +36 -0
  199. package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +113 -0
  200. package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +80 -0
  201. package/.morph/framework/templates/meta-prompts/squad-leaders/backend-squad.md +90 -0
  202. package/.morph/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +126 -0
  203. package/.morph/framework/templates/meta-prompts/squad-leaders/squad-leader.md +43 -0
  204. package/.morph/framework/templates/meta-prompts/validators/checkpoint-validator.md +107 -0
  205. package/.morph/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -0
  206. package/.morph/framework/templates/project-structure/dotnet-ddd.md +70 -0
  207. package/.morph/framework/templates/saas/subscription.cs +347 -0
  208. package/.morph/framework/templates/saas/tenant.cs +338 -0
  209. package/.morph/framework/templates/state.template.json +17 -0
  210. package/.morph/framework/templates/ui/FluentDesignTheme.cs +149 -0
  211. package/.morph/framework/templates/ui/MudTheme.cs +281 -0
  212. package/.morph/framework/templates/ui/design-system.css +226 -0
  213. package/.morph/logs/tool-failures.log +17 -0
  214. package/.morph/memory/pre-compact-2026-02-24T17-43-30-049Z.json +16 -0
  215. package/.morph/plans/eager-watching-bunny.md +105 -0
  216. package/.morph/plans/temporal-seeking-nebula.md +45 -0
  217. package/.morph/state.json +48 -0
  218. package/CLAUDE.md +1 -1
  219. package/README.md +2 -2
  220. package/bin/morph-spec.js +0 -9
  221. package/framework/CLAUDE.md +1 -1
  222. package/framework/hooks/README.md +10 -6
  223. package/framework/hooks/claude-code/notification/approval-reminder.js +2 -0
  224. package/framework/hooks/claude-code/post-tool-use/dispatch.js +1 -1
  225. package/framework/hooks/claude-code/stop/validate-completion.js +1 -1
  226. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +1 -1
  227. package/package.json +1 -1
  228. package/src/commands/project/init.js +15 -42
  229. package/src/commands/project/update.js +22 -37
  230. package/src/lib/installers/mcp-installer.js +18 -3
  231. package/src/utils/hooks-installer.js +5 -15
  232. package/src/commands/project/detect.js +0 -114
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Stop Hook: Validate Completion (Advisory)
5
+ *
6
+ * Event: Stop
7
+ *
8
+ * When Claude stops, checks for incomplete work:
9
+ * - In implement phase: checks uncompleted tasks
10
+ * - In spec phases: checks required outputs still created: false
11
+ * - Returns additionalContext with what remains (advisory, not blocking)
12
+ *
13
+ * Uses stop_hook_active env check to prevent infinite loops.
14
+ *
15
+ * Fail-open: exits 0 on any error.
16
+ */
17
+
18
+ import { stateExists, getActiveFeature, getMissingOutputs } from '../../shared/state-reader.js';
19
+ import { injectContext, pass } from '../../shared/hook-response.js';
20
+
21
+ try {
22
+ // Prevent infinite loop
23
+ if (process.env.MORPH_STOP_HOOK_ACTIVE === '1') pass();
24
+
25
+ if (!stateExists()) pass();
26
+
27
+ const active = getActiveFeature();
28
+ if (!active) pass();
29
+
30
+ const { name, feature } = active;
31
+ const warnings = [];
32
+
33
+ // Check for incomplete tasks during implement phase
34
+ if (feature.phase === 'implement' && feature.tasks) {
35
+ const remaining = (feature.tasks.total || 0) - (feature.tasks.completed || 0);
36
+ if (remaining > 0) {
37
+ warnings.push(`${remaining} task(s) remaining for feature '${name}'`);
38
+ if (feature.tasks.inProgress > 0) {
39
+ warnings.push(` ${feature.tasks.inProgress} task(s) still in progress`);
40
+ }
41
+ }
42
+ }
43
+
44
+ // Check for missing outputs in spec phases
45
+ if (['proposal', 'design', 'clarify', 'tasks', 'uiux'].includes(feature.phase)) {
46
+ const missing = getMissingOutputs(name);
47
+ if (missing.length > 0) {
48
+ warnings.push(`Missing outputs for '${name}' (${feature.phase} phase):`);
49
+ for (const output of missing.slice(0, 5)) {
50
+ warnings.push(` - ${output.type}: ${output.path}`);
51
+ }
52
+ if (missing.length > 5) {
53
+ warnings.push(` ... and ${missing.length - 5} more`);
54
+ }
55
+ }
56
+ }
57
+
58
+ // Check pending approval gates
59
+ if (feature.approvalGates) {
60
+ const pendingGates = Object.entries(feature.approvalGates)
61
+ .filter(([, gate]) => !gate.approved && !gate.timestamp)
62
+ .map(([name]) => name);
63
+
64
+ // Only warn about gates relevant to current/past phases
65
+ const relevantGates = pendingGates.filter(gate => {
66
+ const gatePhaseMap = { proposal: 'proposal', uiux: 'uiux', design: 'design', tasks: 'tasks' };
67
+ return gatePhaseMap[gate] !== undefined;
68
+ });
69
+
70
+ if (relevantGates.length > 0) {
71
+ warnings.push(`Pending approvals: ${relevantGates.join(', ')}`);
72
+ }
73
+ }
74
+
75
+ if (warnings.length === 0) pass();
76
+
77
+ const message = [
78
+ 'MORPH-SPEC: Incomplete work detected:',
79
+ ...warnings.map(w => ` ${w}`),
80
+ '',
81
+ 'Resume with: morph-spec status ' + name
82
+ ].join('\n');
83
+
84
+ injectContext(message);
85
+ } catch {
86
+ // Fail-open
87
+ process.exit(0);
88
+ }
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * UserPromptSubmit Hook: Enrich User Prompts with Context
5
+ *
6
+ * Event: UserPromptSubmit
7
+ *
8
+ * Scans user prompt for morph-spec keywords and injects relevant context:
9
+ * - Feature name mentioned → inject its current status/phase
10
+ * - "implement"/"code" while not in implement phase → warn about wrong phase
11
+ * - "approve"/"looks good" → inject approval command syntax
12
+ * - "next task" → inject current task list
13
+ *
14
+ * Fail-open: exits 0 on any error.
15
+ */
16
+
17
+ import { readStdin } from '../../shared/stdin-reader.js';
18
+ import { stateExists, loadState, getActiveFeature, getFeature, getPendingGates } from '../../shared/state-reader.js';
19
+ import { injectContext, pass } from '../../shared/hook-response.js';
20
+
21
+ try {
22
+ if (!stateExists()) pass();
23
+
24
+ const payload = await readStdin();
25
+ if (!payload) pass();
26
+
27
+ const userPrompt = payload?.prompt || payload?.user_prompt || payload?.content || '';
28
+ if (!userPrompt || userPrompt.length < 3) pass();
29
+
30
+ const promptLower = userPrompt.toLowerCase();
31
+ const state = loadState();
32
+ if (!state?.features) pass();
33
+
34
+ const context = [];
35
+
36
+ // Check if a feature name is mentioned
37
+ for (const [featureName, feature] of Object.entries(state.features)) {
38
+ if (promptLower.includes(featureName.toLowerCase())) {
39
+ context.push(`[morph-spec] Feature '${featureName}': phase=${feature.phase}, status=${feature.status}`);
40
+ if (feature.tasks?.total > 0) {
41
+ context.push(` Tasks: ${feature.tasks.completed || 0}/${feature.tasks.total} completed`);
42
+ }
43
+ break; // Only inject for the first matched feature
44
+ }
45
+ }
46
+
47
+ // Check for implement/code intent while not in implement phase
48
+ const active = getActiveFeature();
49
+ if (active) {
50
+ const { name, feature } = active;
51
+
52
+ const codeKeywords = ['implement', 'code', 'start coding', 'write the code', 'build it', 'let\'s build'];
53
+ const wantsToCode = codeKeywords.some(kw => promptLower.includes(kw));
54
+
55
+ if (wantsToCode && feature.phase !== 'implement' && feature.phase !== 'sync') {
56
+ context.push(
57
+ `[morph-spec] WARNING: Feature '${name}' is in '${feature.phase}' phase, not 'implement'.` +
58
+ ` Complete the current phase first or advance: morph-spec phase advance ${name}`
59
+ );
60
+ }
61
+
62
+ // Check for approval intent
63
+ const approvalKeywords = ['approve', 'approved', 'looks good', 'lgtm', 'ship it'];
64
+ const wantsToApprove = approvalKeywords.some(kw => promptLower.includes(kw));
65
+
66
+ if (wantsToApprove) {
67
+ const pending = getPendingGates(name);
68
+ if (pending.length > 0) {
69
+ context.push(
70
+ `[morph-spec] To approve, use: morph-spec approve ${name} ${pending[0]}`
71
+ );
72
+ }
73
+ }
74
+
75
+ // Check for "next task" intent
76
+ if (promptLower.includes('next task') || promptLower.includes('what\'s next')) {
77
+ if (feature.phase === 'implement') {
78
+ context.push(
79
+ `[morph-spec] Use: morph-spec task next ${name}`
80
+ );
81
+ }
82
+ }
83
+ }
84
+
85
+ if (context.length === 0) pass();
86
+
87
+ injectContext(context.join('\n'));
88
+ } catch {
89
+ // Fail-open
90
+ process.exit(0);
91
+ }
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ # Scope: universal | Validates commit message format (Conventional Commits)
3
+ # MORPH-SPEC Commit Message Hook
4
+ # Enforces Conventional Commits specification
5
+
6
+ COMMIT_MSG_FILE="$1"
7
+ COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
8
+
9
+ # Conventional Commits pattern: type(scope): description
10
+ # Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert
11
+ PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert)(\(.+\))?: .{1,100}"
12
+
13
+ if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
14
+ echo "❌ Invalid commit message format!"
15
+ echo ""
16
+ echo "Required format: <type>(<scope>): <description>"
17
+ echo ""
18
+ echo "Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert"
19
+ echo ""
20
+ echo "Examples:"
21
+ echo " feat(auth): add JWT refresh token rotation"
22
+ echo " fix(api): resolve null reference in UserService"
23
+ echo " docs(readme): update installation instructions"
24
+ echo ""
25
+ echo "Your message:"
26
+ echo " $COMMIT_MSG"
27
+ echo ""
28
+ echo "Override with: git commit --no-verify"
29
+ exit 1
30
+ fi
31
+
32
+ echo "✅ Commit message format valid"
33
+ exit 0
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+ # Scope: universal | Validates agents.json Schema
3
+ # MORPH-SPEC Pre-Commit Hook: Agent Configuration Validation
4
+ # Uses validate-agents.js to check agents.json
5
+
6
+ echo "🤖 Validating agent configuration..."
7
+
8
+ # Check if agents.json is being modified
9
+ if git diff --cached --name-only | grep -q 'agents\.json$'; then
10
+ echo "Detected changes to agents.json"
11
+
12
+ # Run validator
13
+ if ! npx morph-spec validate-agents-skills; then
14
+ echo ""
15
+ echo "❌ COMMIT BLOCKED: agents.json validation failed"
16
+ echo " Fix errors above before committing"
17
+ exit 1
18
+ fi
19
+
20
+ echo "✓ agents.json is valid"
21
+ else
22
+ echo "✓ No changes to agents.json"
23
+ fi
24
+
25
+ exit 0
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ # Scope: universal | Orchestrates framework + stack-specific pre-commit hooks
3
+ # MORPH-SPEC Master Pre-Commit Hook
4
+ # Runs universal framework hooks + stack-specific hooks in sequence
5
+
6
+ echo "╔════════════════════════════════════════════════╗"
7
+ echo "║ MORPH-SPEC PRE-COMMIT VALIDATION ║"
8
+ echo "╚════════════════════════════════════════════════╝"
9
+ echo ""
10
+
11
+ FRAMEWORK_HOOKS_DIR="$(dirname "$0")"
12
+ STACK_HOOKS_DIR=".morph/hooks/pre-commit"
13
+
14
+ HAS_FAILURES=false
15
+
16
+ # Run framework hooks (universal - all stacks)
17
+ echo "📦 Running framework hooks (universal)..."
18
+ for hook in "$FRAMEWORK_HOOKS_DIR"/*.sh; do
19
+ # Skip orchestrator itself
20
+ if [[ "$hook" == */orchestrator.sh ]]; then
21
+ continue
22
+ fi
23
+
24
+ if [[ -f "$hook" ]]; then
25
+ hook_name=$(basename "$hook")
26
+ echo "─────────────────────────────────────────────────"
27
+ if ! bash "$hook"; then
28
+ HAS_FAILURES=true
29
+ echo "❌ Framework hook failed: $hook_name"
30
+ fi
31
+ echo ""
32
+ fi
33
+ done
34
+
35
+ # Run stack-specific hooks (if exist)
36
+ if [[ -d "$STACK_HOOKS_DIR" ]]; then
37
+ echo "🎯 Running stack-specific hooks..."
38
+ for hook in "$STACK_HOOKS_DIR"/*.sh; do
39
+ if [[ -f "$hook" ]]; then
40
+ hook_name=$(basename "$hook")
41
+ echo "─────────────────────────────────────────────────"
42
+ if ! bash "$hook"; then
43
+ HAS_FAILURES=true
44
+ echo "❌ Stack hook failed: $hook_name"
45
+ fi
46
+ echo ""
47
+ fi
48
+ done
49
+ else
50
+ echo "ℹ️ No stack-specific hooks found (optional)"
51
+ echo ""
52
+ fi
53
+
54
+ echo "═════════════════════════════════════════════════"
55
+
56
+ if [[ "$HAS_FAILURES" == "true" ]]; then
57
+ echo "❌ PRE-COMMIT VALIDATION FAILED"
58
+ echo " Fix errors above before committing"
59
+ echo " Or use: git commit --no-verify to skip"
60
+ exit 1
61
+ fi
62
+
63
+ echo "✅ ALL PRE-COMMIT VALIDATIONS PASSED"
64
+ exit 0
@@ -0,0 +1,50 @@
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
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env bash
2
+ # Scope: universal | Runs test suite before push
3
+ # MORPH-SPEC Pre-Push Hook
4
+ # Ensures all tests pass before pushing to remote
5
+
6
+ echo "🧪 Running test suite before push..."
7
+ echo ""
8
+
9
+ # Detect project type and run appropriate tests
10
+ if [[ -f *.csproj ]] || find . -maxdepth 2 -name "*.csproj" 2>/dev/null | grep -q .; then
11
+ # .NET project detected
12
+ echo "📦 Detected .NET project"
13
+
14
+ if ! dotnet test --verbosity minimal --no-build --no-restore 2>/dev/null; then
15
+ echo ""
16
+ echo "❌ .NET tests failed!"
17
+ echo ""
18
+ echo "Fix failing tests before pushing."
19
+ echo "Override with: git push --no-verify"
20
+ exit 1
21
+ fi
22
+
23
+ elif [[ -f package.json ]]; then
24
+ # Node.js project detected
25
+ echo "📦 Detected Node.js project"
26
+
27
+ if ! npm test 2>/dev/null; then
28
+ echo ""
29
+ echo "❌ Node.js tests failed!"
30
+ echo ""
31
+ echo "Fix failing tests before pushing."
32
+ echo "Override with: git push --no-verify"
33
+ exit 1
34
+ fi
35
+
36
+ else
37
+ echo "⚠️ No recognized test framework found"
38
+ echo " Skipping test execution"
39
+ exit 0
40
+ fi
41
+
42
+ echo ""
43
+ echo "✅ All tests passed - safe to push"
44
+ exit 0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shared hook response builders for Claude Code hooks.
3
+ *
4
+ * Claude Code hooks communicate via JSON to stdout.
5
+ * - PreToolUse: { "decision": "block"|"approve", "reason": "..." }
6
+ * - SessionStart/Stop/etc: { "additionalContext": "..." }
7
+ */
8
+
9
+ /**
10
+ * Block a tool use with a reason.
11
+ * @param {string} reason - Human-readable reason for blocking
12
+ */
13
+ export function block(reason) {
14
+ console.log(JSON.stringify({ decision: 'block', reason }));
15
+ process.exit(0);
16
+ }
17
+
18
+ /**
19
+ * Approve a tool use, optionally injecting context.
20
+ * @param {string} [context] - Additional context to inject
21
+ */
22
+ export function approve(context) {
23
+ if (context) {
24
+ console.log(JSON.stringify({ decision: 'approve', additionalContext: context }));
25
+ }
26
+ // No output = implicit approve
27
+ process.exit(0);
28
+ }
29
+
30
+ /**
31
+ * Inject additional context (for SessionStart, Stop, UserPromptSubmit, etc.).
32
+ * @param {string} text - Context text to inject
33
+ * @param {Object} [extra] - Additional fields to include in response
34
+ */
35
+ export function injectContext(text, extra = {}) {
36
+ console.log(JSON.stringify({ additionalContext: text, ...extra }));
37
+ process.exit(0);
38
+ }
39
+
40
+ /**
41
+ * Silent exit — no output, no blocking.
42
+ */
43
+ export function pass() {
44
+ process.exit(0);
45
+ }
@@ -0,0 +1,129 @@
1
+ // GENERATED by scripts/generate-refs.js — DO NOT EDIT manually
2
+ // Source of truth: src/core/paths/output-schema.js
3
+ // Regenerate with: node scripts/generate-refs.js
4
+ /**
5
+ * Shared phase utilities for Claude Code hooks.
6
+ *
7
+ * Maps phases to directories and output types.
8
+ */
9
+
10
+ /** Phase order */
11
+ export const PHASE_ORDER = ["proposal","setup","uiux","design","clarify","tasks","implement","sync"];
12
+
13
+ /** Map phase → allowed output subdirectory */
14
+ export const PHASE_DIRS = {
15
+ proposal: '0-proposal',
16
+ setup: '0-proposal',
17
+ uiux: '2-ui',
18
+ design: '1-design',
19
+ clarify: '1-design',
20
+ tasks: '3-tasks',
21
+ implement: '4-implement',
22
+ sync: '4-implement',
23
+ };
24
+
25
+ /** Map output type (camelCase) → phase that produces it */
26
+ export const OUTPUT_PHASE_MAP = {
27
+ proposal: 'proposal',
28
+ schemaAnalysis: 'design',
29
+ spec: 'design',
30
+ clarifications: 'clarify',
31
+ contracts: 'design',
32
+ tasks: 'tasks',
33
+ uiDesignSystem: 'uiux',
34
+ uiMockups: 'uiux',
35
+ uiComponents: 'uiux',
36
+ uiFlows: 'uiux',
37
+ decisions: 'design',
38
+ recap: 'implement',
39
+ };
40
+
41
+ /** Map filename → output type (camelCase) */
42
+ export const FILENAME_TO_OUTPUT = {
43
+ 'proposal.md': 'proposal',
44
+ 'schema-analysis.md': 'schemaAnalysis',
45
+ 'spec.md': 'spec',
46
+ 'clarifications.md': 'clarifications',
47
+ 'contracts.cs': 'contracts',
48
+ 'tasks.md': 'tasks',
49
+ 'design-system.md': 'uiDesignSystem',
50
+ 'mockups.md': 'uiMockups',
51
+ 'components.md': 'uiComponents',
52
+ 'flows.md': 'uiFlows',
53
+ 'decisions.md': 'decisions',
54
+ 'recap.md': 'recap',
55
+ };
56
+
57
+ /** Protected spec files and the approval gate that locks them */
58
+ export const PROTECTED_SPEC_FILES = {
59
+ 'schema-analysis.md': 'design',
60
+ 'spec.md': 'design',
61
+ 'contracts.cs': 'design',
62
+ 'tasks.md': 'tasks',
63
+ 'design-system.md': 'uiux',
64
+ 'mockups.md': 'uiux',
65
+ 'components.md': 'uiux',
66
+ 'flows.md': 'uiux',
67
+ };
68
+
69
+ /**
70
+ * Extract feature name from a .morph/features/{feature}/... path
71
+ * @param {string} filePath - File path to analyze
72
+ * @returns {string|null} Feature name or null
73
+ */
74
+ export function extractFeatureName(filePath) {
75
+ const normalized = filePath.replace(/\\/g, '/');
76
+ const match = normalized.match(/\.morph\/features\/([^/]+)\//);
77
+ return match ? match[1] : null;
78
+ }
79
+
80
+ /**
81
+ * Extract phase subdirectory from a .morph/features/{feature}/{phaseDir}/... path
82
+ * @param {string} filePath - File path to analyze
83
+ * @returns {string|null} Phase directory (e.g., '0-proposal', '1-design') or null
84
+ */
85
+ export function extractPhaseDir(filePath) {
86
+ const normalized = filePath.replace(/\\/g, '/');
87
+ const match = normalized.match(/\.morph\/features\/[^/]+\/(\d+-[^/]+)\//);
88
+ return match ? match[1] : null;
89
+ }
90
+
91
+ /**
92
+ * Check if a file path is inside .morph/features/
93
+ * @param {string} filePath
94
+ * @returns {boolean}
95
+ */
96
+ export function isFeaturePath(filePath) {
97
+ const normalized = filePath.replace(/\\/g, '/');
98
+ return normalized.includes('.morph/features/');
99
+ }
100
+
101
+ /**
102
+ * Check if a file path is inside .morph/framework/ (readonly)
103
+ * @param {string} filePath
104
+ * @returns {boolean}
105
+ */
106
+ export function isFrameworkPath(filePath) {
107
+ const normalized = filePath.replace(/\\/g, '/');
108
+ return normalized.includes('.morph/framework/');
109
+ }
110
+
111
+ /**
112
+ * Check if a file path is state.json
113
+ * @param {string} filePath
114
+ * @returns {boolean}
115
+ */
116
+ export function isStatePath(filePath) {
117
+ const normalized = filePath.replace(/\\/g, '/');
118
+ return normalized.endsWith('.morph/state.json');
119
+ }
120
+
121
+ /**
122
+ * Get the basename of a file path
123
+ * @param {string} filePath
124
+ * @returns {string}
125
+ */
126
+ export function getBasename(filePath) {
127
+ const normalized = filePath.replace(/\\/g, '/');
128
+ return normalized.split('/').pop();
129
+ }