@polymorphism-tech/morph-spec 4.22.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.
- package/CLAUDE.md +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /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
|
-
}
|