@polymorphism-tech/morph-spec 4.21.0 → 5.0.1

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 (213) hide show
  1. package/CLAUDE.md +57 -71
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +224 -491
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +55 -38
  8. package/framework/MORPH.md +244 -0
  9. package/framework/agents.json +127 -1183
  10. package/framework/commands/morph-apply.md +56 -240
  11. package/framework/commands/morph-archive.md +48 -85
  12. package/framework/commands/morph-preflight.md +7 -7
  13. package/framework/commands/morph-proposal.md +35 -191
  14. package/framework/commands/morph-status.md +19 -50
  15. package/framework/commands/morph-troubleshoot.md +2 -2
  16. package/framework/evals/rubrics/README.md +44 -0
  17. package/framework/evals/rubrics/architecture.md +46 -0
  18. package/framework/evals/rubrics/code-quality.md +50 -0
  19. package/framework/evals/rubrics/contracts.md +47 -0
  20. package/framework/evals/rubrics/test-coverage.md +50 -0
  21. package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
  22. package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
  23. package/framework/evals/suites/standard.yaml +5 -0
  24. package/framework/evals/trace-schema.md +67 -0
  25. package/framework/hooks/claude-code/core/README.md +3 -0
  26. package/framework/hooks/claude-code/core/denylist.json +14 -0
  27. package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
  28. package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
  30. package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
  31. package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
  32. package/framework/hooks/shared/phase-utils.js +20 -28
  33. package/framework/hooks/shared/state-reader.js +4 -5
  34. package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -210
  35. package/framework/phases.json +40 -114
  36. package/framework/rubrics/eval-rubric.json +66 -0
  37. package/framework/skills/README.md +39 -64
  38. package/framework/skills/core/README.md +3 -0
  39. package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
  40. package/framework/skills/core/morph-eval/SKILL.md +57 -0
  41. package/framework/skills/core/morph-implement/SKILL.md +83 -0
  42. package/framework/skills/core/morph-plan/SKILL.md +64 -0
  43. package/framework/skills/core/morph-review/SKILL.md +62 -0
  44. package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
  45. package/framework/skills/core/morph-standards/SKILL.md +49 -0
  46. package/framework/standards/STANDARDS.json +32 -0
  47. package/framework/standards/frontend/design-system/ai-image-generation.md +244 -0
  48. package/framework/standards/integration/mcp/mcp-tools.md +48 -0
  49. package/framework/templates/CLAUDE.md.core.guide.md +29 -0
  50. package/framework/templates/CLAUDE.md.core.template +22 -0
  51. package/framework/templates/REGISTRY.json +4 -4
  52. package/framework/workflows/configs/fast-track.json +5 -10
  53. package/framework/workflows/configs/full.json +4 -8
  54. package/framework/workflows/configs/quick.json +32 -0
  55. package/framework/workflows/configs/standard.json +57 -64
  56. package/package.json +7 -6
  57. package/src/commands/mcp/mcp-setup.js +58 -31
  58. package/src/commands/project/doctor.js +69 -70
  59. package/src/commands/project/init.js +26 -5
  60. package/src/commands/project/status.js +18 -19
  61. package/src/commands/state/approve.js +1 -1
  62. package/src/commands/validation/validate-feature.js +18 -112
  63. package/src/core/paths/output-schema.js +133 -151
  64. package/src/core/state/phase-state-machine.js +10 -24
  65. package/src/core/state/state-manager.js +17 -17
  66. package/src/core/workflows/workflow-detector.js +214 -31
  67. package/src/core/workflows/workflow-router.js +204 -0
  68. package/src/dashboard/app/pages/AgentMap.jsx +32 -33
  69. package/src/dashboard/app/pages/LiveState.jsx +1 -1
  70. package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
  71. package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
  72. package/src/dashboard/app/pages/Workflows.jsx +2 -2
  73. package/src/dashboard/scanner/agents-scanner.js +30 -35
  74. package/src/lib/agents/metrics-aggregator.js +1 -1
  75. package/src/lib/agents/output-collector.js +7 -8
  76. package/src/lib/context/context-builder.js +289 -0
  77. package/src/lib/detectors/claude-config-detector.js +19 -3
  78. package/src/lib/generators/context-generator.js +20 -42
  79. package/src/lib/generators/index.js +1 -0
  80. package/src/lib/generators/mandate-generator.js +512 -0
  81. package/src/lib/generators/metadata-extractor.js +1 -1
  82. package/src/lib/generators/recap-generator.js +3 -4
  83. package/src/lib/installers/mcp-installer.js +300 -92
  84. package/src/lib/phase-chain/phase-validator.js +36 -40
  85. package/src/lib/stack/stack-profile.js +74 -91
  86. package/src/lib/tasks/task-parser.js +1 -1
  87. package/src/lib/tasks/wave-resolver.js +353 -0
  88. package/src/lib/validators/checkpoint-persistence.js +2 -2
  89. package/src/lib/validators/content/cross-phase-validator.js +281 -0
  90. package/src/lib/validators/content/index.js +1 -0
  91. package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
  92. package/src/scripts/global-install.js +2 -27
  93. package/src/utils/agents-installer.js +224 -447
  94. package/src/utils/claude-settings-manager.js +57 -15
  95. package/src/utils/file-copier.js +1 -0
  96. package/src/utils/hooks-installer.js +15 -117
  97. package/src/utils/skills-installer.js +12 -18
  98. package/bin/detect-agents.js +0 -226
  99. package/bin/task-manager.js +0 -1036
  100. package/framework/agents/README.md +0 -13
  101. package/framework/agents/ai-agents/ai-system-architect.md +0 -194
  102. package/framework/agents/ai-agents/vector-search-expert.md +0 -247
  103. package/framework/agents/architecture/architecture-expert.md +0 -98
  104. package/framework/agents/architecture/morph-spec-architect.md +0 -66
  105. package/framework/agents/architecture/packages-validator.md +0 -89
  106. package/framework/agents/architecture/security-expert.md +0 -79
  107. package/framework/agents/architecture/seo-growth-hacker.md +0 -168
  108. package/framework/agents/architecture/standards-architect.md +0 -153
  109. package/framework/agents/backend/dotnet-senior.md +0 -163
  110. package/framework/agents/backend/ef-modeler.md +0 -119
  111. package/framework/agents/backend/hangfire-orchestrator.md +0 -133
  112. package/framework/agents/backend/ms-agent-expert.md +0 -55
  113. package/framework/agents/backend/neon-expert.md +0 -379
  114. package/framework/agents/frontend/css-specialist.md +0 -89
  115. package/framework/agents/frontend/design-system-validator.md +0 -100
  116. package/framework/agents/frontend/gsap-animator.md +0 -131
  117. package/framework/agents/frontend/nextjs-component-validator.md +0 -97
  118. package/framework/agents/frontend/nextjs-expert.md +0 -201
  119. package/framework/agents/frontend/ui-designer.md +0 -259
  120. package/framework/agents/infrastructure/infra-architect.md +0 -215
  121. package/framework/agents/integrations/asaas-financial.md +0 -138
  122. package/framework/agents/integrations/neon-auth.md +0 -151
  123. package/framework/agents/integrations/resend-email.md +0 -127
  124. package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
  125. package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
  126. package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
  127. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
  128. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
  129. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
  130. package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
  131. package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
  132. package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
  133. package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
  134. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
  135. package/framework/hooks/shared/compact-restore.js +0 -123
  136. package/framework/hooks/shared/dispatch-helpers.js +0 -147
  137. package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
  138. package/framework/hooks/shared/stale-task-reset.js +0 -57
  139. package/framework/hooks/shared/worktree-helpers.js +0 -75
  140. package/framework/skills/level-0-meta/README.md +0 -7
  141. package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
  142. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
  143. package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
  144. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
  145. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
  146. package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
  147. package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
  148. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
  149. package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
  150. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
  151. package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
  152. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
  153. package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
  154. package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
  155. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
  156. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -382
  157. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
  158. package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
  159. package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
  160. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
  161. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
  162. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
  163. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
  164. package/framework/skills/level-1-workflows/README.md +0 -7
  165. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
  166. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
  167. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
  168. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
  169. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
  170. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
  171. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -493
  172. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
  173. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -46
  174. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
  175. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
  176. package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
  177. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
  178. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
  179. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
  180. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
  181. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
  182. package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
  183. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -788
  184. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
  185. package/framework/squad-templates/backend-only.json +0 -47
  186. package/framework/squad-templates/frontend-only.json +0 -56
  187. package/framework/squad-templates/full-stack.json +0 -74
  188. package/src/commands/agents/agent-metrics.js +0 -174
  189. package/src/commands/agents/audit-dispatch.js +0 -82
  190. package/src/commands/agents/detect-agents.js +0 -275
  191. package/src/commands/agents/dispatch-agents.js +0 -461
  192. package/src/commands/agents/index.js +0 -13
  193. package/src/commands/agents/manage-agents.js +0 -352
  194. package/src/commands/agents/project-metrics.js +0 -74
  195. package/src/commands/phase/phase-reset.js +0 -74
  196. package/src/commands/scope/escalate.js +0 -217
  197. package/src/commands/state/advance-phase.js +0 -600
  198. package/src/commands/state/phase-runner.js +0 -215
  199. package/src/commands/state/state.js +0 -444
  200. package/src/commands/task/expand.js +0 -100
  201. package/src/commands/tasks/index.js +0 -4
  202. package/src/commands/tasks/task.js +0 -105
  203. package/src/commands/templates/index.js +0 -5
  204. package/src/commands/templates/template-render.js +0 -93
  205. package/src/commands/trust/trust.js +0 -118
  206. package/src/lib/agents/agents-manager.js +0 -349
  207. package/src/lib/build/build-runner.js +0 -99
  208. package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
  209. package/src/lib/contracts/sprint-contract-generator.js +0 -137
  210. package/src/lib/scope/impact-analyzer.js +0 -106
  211. package/src/lib/trust/trust-manager.js +0 -157
  212. package/src/utils/tier-helpers.js +0 -24
  213. /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
@@ -1,151 +0,0 @@
1
- ---
2
- name: neon-auth
3
- description: Neon Auth (Better Auth) specialist for authentication flows, session management, Next.js App Router integration, .NET JWT validation, and RLS policy integration. Use when integrating Neon Auth, implementing social login flows, managing user sessions, or protecting routes with Neon Auth middleware.
4
- allowed-tools: Read, Write, Edit, Bash, Glob, Grep
5
- stacks:
6
- - neon
7
- color: teal
8
- effort: medium
9
- ---
10
-
11
- # Neon Auth (Better Auth)
12
-
13
- Authentication with Neon Auth for Next.js and .NET. SDK: `@neondatabase/auth` (Next.js) / JWT Bearer validation (.NET).
14
-
15
- > **Ref:** `framework/standards/backend/integrations/neon-auth/neon-auth.md`
16
-
17
- ## Setup (Next.js)
18
-
19
- ```typescript
20
- // lib/auth/client.ts
21
- 'use client';
22
- import { createAuthClient } from '@neondatabase/auth/next';
23
- export const authClient = createAuthClient();
24
-
25
- // lib/auth/server.ts
26
- import { createNeonAuth } from '@neondatabase/auth/next/server';
27
- export const auth = createNeonAuth({
28
- baseUrl: process.env.NEON_AUTH_BASE_URL!,
29
- cookies: { secret: process.env.NEON_AUTH_COOKIE_SECRET! },
30
- });
31
-
32
- // app/api/auth/[...path]/route.ts
33
- import { auth } from '@/lib/auth/server';
34
- export const { GET, POST } = auth.handler();
35
- ```
36
-
37
- ## Setup (.NET)
38
-
39
- ```csharp
40
- // Program.cs
41
- builder.Services.AddAuthentication("NeonAuth")
42
- .AddJwtBearer("NeonAuth", options =>
43
- {
44
- options.Authority = builder.Configuration["NeonAuth:BaseUrl"];
45
- options.TokenValidationParameters = new()
46
- {
47
- ValidateIssuer = true,
48
- ValidateAudience = false,
49
- ValidateLifetime = true,
50
- ValidateIssuerSigningKey = true,
51
- NameClaimType = "sub"
52
- };
53
- });
54
- builder.Services.AddAuthorization();
55
- app.UseAuthentication();
56
- app.UseAuthorization();
57
- ```
58
-
59
- ## Protected Endpoints
60
-
61
- ```csharp
62
- // Minimal API
63
- app.MapGet("/api/profile", (ClaimsPrincipal user) =>
64
- {
65
- var userId = user.FindFirstValue("sub");
66
- if (userId is null) return Results.Unauthorized();
67
- return Results.Ok(new { UserId = userId });
68
- }).RequireAuthorization();
69
-
70
- // Controller: same pattern with [Authorize] + User.FindFirstValue("sub")
71
- ```
72
-
73
- ## Next.js Integration
74
-
75
- ```tsx
76
- // middleware.ts
77
- import { auth } from '@/lib/auth/server';
78
- export default auth.middleware({ loginUrl: '/auth/sign-in' });
79
- export const config = { matcher: ['/((?!.*\\..*|_next|auth).*)', '/'] };
80
-
81
- // Protected page (server)
82
- import { auth } from '@/lib/auth/server';
83
- export const dynamic = 'force-dynamic';
84
- export default async function DashboardPage() {
85
- const { data: session } = await auth.getSession();
86
- if (!session?.user) redirect('/auth/sign-in');
87
- return <Dashboard user={session.user} />;
88
- }
89
-
90
- // Client component
91
- 'use client';
92
- import { useSession } from '@/lib/auth/client';
93
- const { data } = useSession();
94
- const user = data?.user;
95
- ```
96
-
97
- ## Auth Pages
98
-
99
- ```tsx
100
- // Sign in / Sign up — single component
101
- import { AuthView } from '@neondatabase/auth/react';
102
- export default function SignInPage() {
103
- return <AuthView pathname="sign-in" />;
104
- }
105
- ```
106
-
107
- ## OAuth
108
-
109
- ```typescript
110
- await authClient.signIn.social({
111
- provider: 'google',
112
- callbackURL: '/auth/callback',
113
- });
114
- ```
115
-
116
- ## RLS Integration
117
-
118
- Neon Auth JWTs are validated by the Data API. Use `auth.user_id()` in RLS policies:
119
-
120
- ```sql
121
- CREATE POLICY "Users own rows"
122
- ON public.documents FOR ALL TO authenticated
123
- USING (user_id = auth.user_id());
124
- ```
125
-
126
- ## Database Schema
127
-
128
- Auth data lives in `neon_auth` schema (managed automatically):
129
- - `neon_auth.user` — profiles
130
- - `neon_auth.session` — sessions
131
- - `neon_auth.account` — credentials
132
-
133
- Query directly — no webhook sync needed:
134
- ```sql
135
- SELECT id, email, name FROM neon_auth.user WHERE id = auth.user_id();
136
- ```
137
-
138
- ## Checklist
139
-
140
- - [ ] `NEON_AUTH_BASE_URL` in environment variables
141
- - [ ] `NEON_AUTH_COOKIE_SECRET` (32+ chars) in environment variables
142
- - [ ] Auth API route at `app/api/auth/[...path]/route.ts`
143
- - [ ] `NeonAuthUIProvider` wrapping app in root layout
144
- - [ ] Middleware protecting private routes
145
- - [ ] `.NET` JWT Bearer configured with Neon Auth authority
146
- - [ ] RLS policies using `auth.user_id()`
147
- - [ ] `force-dynamic` on pages using `auth.getSession()`
148
-
149
- ---
150
-
151
- *MORPH-SPEC by Polymorphism Tech*
@@ -1,127 +0,0 @@
1
- ---
2
- name: resend-email
3
- description: Resend Email specialist for transactional email, HTML/React email templates, Resend API integration, and notification systems in .NET and Next.js. Use when implementing email notifications, integrating Resend API, creating transactional email templates, or configuring email delivery in MORPH-SPEC projects.
4
- allowed-tools: Read, Write, Edit, Bash, Glob, Grep
5
- stacks:
6
- - resend
7
- color: teal
8
- effort: medium
9
- ---
10
-
11
- # Resend Email
12
-
13
- Transactional email via Resend for .NET. REST API, no official SDK. Simple, developer-friendly.
14
-
15
- ## Setup
16
-
17
- ```csharp
18
- // appsettings.json
19
- { "Resend": { "BaseUrl": "https://api.resend.com", "ApiKey": "${RESEND_API_KEY}", "FromEmail": "noreply@yourdomain.com" } }
20
-
21
- // Program.cs
22
- builder.Services.Configure<ResendOptions>(builder.Configuration.GetSection("Resend"));
23
- builder.Services.AddHttpClient<IResendClient, ResendClient>((sp, client) =>
24
- {
25
- var options = sp.GetRequiredService<IOptions<ResendOptions>>().Value;
26
- client.BaseAddress = new Uri(options.BaseUrl);
27
- client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", options.ApiKey);
28
- });
29
- ```
30
-
31
- ## Client Interface
32
-
33
- ```csharp
34
- public interface IResendClient
35
- {
36
- Task<EmailResponse> SendAsync(SendEmailRequest request, CancellationToken ct = default);
37
- Task<EmailResponse> SendBatchAsync(IEnumerable<SendEmailRequest> requests, CancellationToken ct = default);
38
- Task<EmailDetails> GetEmailAsync(string emailId, CancellationToken ct = default);
39
- }
40
- ```
41
-
42
- Implementation: `HttpClient.PostAsJsonAsync("/emails")` + `ReadFromJsonAsync<EmailResponse>`. Log errors, throw `ResendException` on failure.
43
-
44
- ## DTOs
45
-
46
- ```csharp
47
- public record SendEmailRequest
48
- {
49
- [JsonPropertyName("from")] public required string From { get; init; }
50
- [JsonPropertyName("to")] public required string[] To { get; init; }
51
- [JsonPropertyName("subject")] public required string Subject { get; init; }
52
- [JsonPropertyName("html")] public string? Html { get; init; }
53
- [JsonPropertyName("text")] public string? Text { get; init; }
54
- [JsonPropertyName("reply_to")] public string? ReplyTo { get; init; }
55
- [JsonPropertyName("tags")] public Tag[]? Tags { get; init; }
56
- }
57
-
58
- public record EmailResponse
59
- {
60
- [JsonPropertyName("id")] public string Id { get; init; } = "";
61
- }
62
-
63
- public record Tag
64
- {
65
- [JsonPropertyName("name")] public required string Name { get; init; }
66
- [JsonPropertyName("value")] public required string Value { get; init; }
67
- }
68
- ```
69
-
70
- ## Webhooks
71
-
72
- ```csharp
73
- [ApiController, Route("api/webhooks/resend")]
74
- public class ResendWebhookController(IEmailTrackingService tracking, ILogger<ResendWebhookController> logger) : ControllerBase
75
- {
76
- [HttpPost]
77
- public async Task<IActionResult> Handle([FromBody] ResendWebhookPayload payload)
78
- {
79
- logger.LogInformation("Resend webhook: {Type} for {EmailId}", payload.Type, payload.Data?.EmailId);
80
- switch (payload.Type)
81
- {
82
- case "email.delivered": await tracking.MarkDeliveredAsync(payload.Data!.EmailId); break;
83
- case "email.bounced": await tracking.MarkBouncedAsync(payload.Data!.EmailId); break;
84
- case "email.complained": await tracking.MarkComplainedAsync(payload.Data!.EmailId); break;
85
- }
86
- return Ok();
87
- }
88
- }
89
- ```
90
-
91
- ## Abstraction Pattern
92
-
93
- ```csharp
94
- // Use IEmailService abstraction over IResendClient for testability
95
- public interface IEmailService
96
- {
97
- Task<string> SendTransactionalAsync(string to, string subject, string htmlBody, CancellationToken ct = default);
98
- }
99
-
100
- // ResendEmailService implements IEmailService using IResendClient
101
- // FakeEmailClient implements IEmailService for simulation mode
102
- ```
103
-
104
- ## Gotchas
105
-
106
- | Issue | Fix |
107
- |-------|-----|
108
- | Rate limit: 100 req/sec (free), 1000 (pro) | Queue emails via Hangfire for bulk sends |
109
- | Domain verification required for production | Verify in Resend dashboard, add DNS records |
110
- | `from` must match verified domain | Use `noreply@yourdomain.com` |
111
- | HTML email rendering varies | Test with Litmus/Email on Acid, use MJML |
112
- | Webhook signature validation | Verify `svix-signature` header in production |
113
-
114
- ## Checklist
115
-
116
- - [ ] API Key in Key Vault (not hardcoded)
117
- - [ ] HttpClient with `Authorization: Bearer` header
118
- - [ ] Domain verified in Resend dashboard
119
- - [ ] `IEmailService` abstraction for testability
120
- - [ ] Webhook endpoint + signature validation
121
- - [ ] Rate limiting handled (queue for bulk)
122
- - [ ] Tags used for tracking/analytics
123
- - [ ] Simulation mode (`FakeEmailClient`) for dev
124
-
125
- ---
126
-
127
- *MORPH-SPEC by Polymorphism Tech*
@@ -1,122 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * PostToolUse Hook: Agent Completion Tracker
5
- *
6
- * Event: PostToolUse | Matcher: Agent
7
- *
8
- * Detects when morph-spec subagents complete (dispatched via the Agent tool)
9
- * and injects status notifications:
10
- * - Partial: shows which agents are still pending
11
- * - Complete: instructs to run validate-feature
12
- *
13
- * Only tracks tier 1-3 agents (not validators: morph-validator-*).
14
- *
15
- * Fail-open: exits 0 on any error.
16
- */
17
-
18
- import { readStdin } from '../../shared/stdin-reader.js';
19
- import { stateExists, getActiveFeature, getFeature, getFeaturePhase } from '../../shared/state-reader.js';
20
- import { pass, injectContext } from '../../shared/hook-response.js';
21
- import { logHookActivity } from '../../shared/activity-logger.js';
22
- import { subagentTypeToAgentId } from '../../shared/dispatch-helpers.js';
23
-
24
- try {
25
- if (!stateExists()) pass();
26
-
27
- const payload = await readStdin();
28
- if (!payload) pass();
29
-
30
- const subagentType = payload?.tool_input?.subagent_type || '';
31
- if (!subagentType) pass();
32
-
33
- // Only track morph-* agents (not external subagents)
34
- if (!subagentType.startsWith('morph-')) pass();
35
-
36
- // Skip validators (tier 4) — they have their own tracking
37
- if (subagentType.startsWith('morph-validator-')) pass();
38
-
39
- const result = buildAgentCompleteContext(subagentType);
40
- if (result) {
41
- logHookActivity('agent-complete', 'PostToolUse', result.type);
42
- injectContext(result.text);
43
- }
44
-
45
- pass();
46
- } catch {
47
- // Fail-open
48
- process.exit(0);
49
- }
50
-
51
- /**
52
- * Build the agent completion notification context.
53
- *
54
- * @param {string} subagentType - e.g. 'morph-nextjs-expert'
55
- * @returns {{ text: string, type: string }|null}
56
- */
57
- function buildAgentCompleteContext(subagentType) {
58
- // Get active feature
59
- const active = getActiveFeature();
60
- if (!active) return null;
61
-
62
- const { name: featureName, feature } = active;
63
-
64
- // Only track during implement phase
65
- const phase = getFeaturePhase(featureName);
66
- if (phase !== 'implement') return null;
67
-
68
- // Get dispatch log
69
- const dispatchLog = feature.dispatchLog;
70
- if (!Array.isArray(dispatchLog) || dispatchLog.length === 0) return null;
71
-
72
- // Get the most recent dispatch entry
73
- const latestDispatch = dispatchLog[dispatchLog.length - 1];
74
- const agents = latestDispatch?.agents;
75
- if (!Array.isArray(agents) || agents.length === 0) return null;
76
-
77
- // Find the completing agent in the dispatch
78
- const agentId = subagentTypeToAgentId(subagentType);
79
- let matchedAgent = agents.find(a => a.subagentType === subagentType);
80
- if (!matchedAgent && agentId) {
81
- matchedAgent = agents.find(a => a.id === agentId);
82
- }
83
-
84
- if (!matchedAgent) return null;
85
-
86
- // Mark this agent as completed (in-memory for evaluation only)
87
- // Check which agents are still pending (no completedAt)
88
- const pendingAgents = agents.filter(a => {
89
- if (a === matchedAgent) return false; // The one just completed
90
- return !a.completedAt;
91
- });
92
-
93
- const totalAgents = agents.length;
94
- const completedCount = totalAgents - pendingAgents.length;
95
-
96
- if (pendingAgents.length === 0) {
97
- // All agents complete
98
- return {
99
- type: 'all_complete',
100
- text: [
101
- '✅ ALL MORPH-SPEC AGENTS COMPLETE',
102
- '════════════════════════════════════════',
103
- `All ${totalAgents} dispatched agents have completed their tasks.`,
104
- '',
105
- 'NEXT STEP:',
106
- ` morph-spec validate-feature ${featureName} --phase implement`,
107
- ].join('\n'),
108
- };
109
- }
110
-
111
- // Partial completion
112
- const remainingList = pendingAgents
113
- .map(a => a.title || a.id || 'unknown')
114
- .join(', ');
115
-
116
- const agentTitle = matchedAgent.title || matchedAgent.id || subagentType;
117
-
118
- return {
119
- type: 'partial_complete',
120
- text: `✅ Agent '${agentTitle}' completed (${completedCount}/${totalAgents}). Remaining: ${remainingList}`,
121
- };
122
- }