opencastle 0.32.12 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -93
  2. package/README.md +5 -3
  3. package/package.json +2 -2
  4. package/src/dashboard/dist/data/convoys/demo-api-v2.json +3 -3
  5. package/src/dashboard/dist/data/convoys/demo-auth-revamp.json +4 -4
  6. package/src/dashboard/dist/data/convoys/demo-dashboard-ui.json +12 -12
  7. package/src/dashboard/dist/data/convoys/demo-data-pipeline.json +3 -3
  8. package/src/dashboard/dist/data/convoys/demo-deploy-ci.json +1 -1
  9. package/src/dashboard/dist/data/convoys/demo-docs-update.json +7 -7
  10. package/src/dashboard/dist/data/convoys/demo-perf-opt.json +4 -4
  11. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  12. package/src/dashboard/public/data/convoys/demo-api-v2.json +3 -3
  13. package/src/dashboard/public/data/convoys/demo-auth-revamp.json +4 -4
  14. package/src/dashboard/public/data/convoys/demo-dashboard-ui.json +12 -12
  15. package/src/dashboard/public/data/convoys/demo-data-pipeline.json +3 -3
  16. package/src/dashboard/public/data/convoys/demo-deploy-ci.json +1 -1
  17. package/src/dashboard/public/data/convoys/demo-docs-update.json +7 -7
  18. package/src/dashboard/public/data/convoys/demo-perf-opt.json +4 -4
  19. package/src/orchestrator/customizations/stack/sanity-config.md +43 -0
  20. package/src/orchestrator/customizations/stack/supabase-config.md +53 -0
  21. package/src/orchestrator/plugins/astro/REFERENCE.md +5 -0
  22. package/src/orchestrator/plugins/astro/SKILL.md +22 -29
  23. package/src/orchestrator/plugins/chrome-devtools/REFERENCE.md +9 -0
  24. package/src/orchestrator/plugins/chrome-devtools/SKILL.md +10 -55
  25. package/src/orchestrator/plugins/contentful/REFERENCE.md +16 -0
  26. package/src/orchestrator/plugins/contentful/SKILL.md +69 -29
  27. package/src/orchestrator/plugins/convex/REFERENCE.md +9 -0
  28. package/src/orchestrator/plugins/convex/SKILL.md +13 -1
  29. package/src/orchestrator/plugins/cypress/REFERENCE.md +5 -0
  30. package/src/orchestrator/plugins/cypress/SKILL.md +29 -93
  31. package/src/orchestrator/plugins/figma/REFERENCE.md +18 -0
  32. package/src/orchestrator/plugins/figma/SKILL.md +41 -66
  33. package/src/orchestrator/plugins/jira/REFERENCE.md +9 -0
  34. package/src/orchestrator/plugins/jira/SKILL.md +26 -114
  35. package/src/orchestrator/plugins/linear/SKILL.md +42 -109
  36. package/src/orchestrator/plugins/netlify/REFERENCE.md +33 -0
  37. package/src/orchestrator/plugins/netlify/SKILL.md +34 -64
  38. package/src/orchestrator/plugins/nextjs/REFERENCE.md +73 -0
  39. package/src/orchestrator/plugins/nextjs/SKILL.md +49 -138
  40. package/src/orchestrator/plugins/notion/SKILL.md +26 -168
  41. package/src/orchestrator/plugins/notion/TEMPLATES.md +88 -0
  42. package/src/orchestrator/plugins/nx/REFERENCE.md +10 -0
  43. package/src/orchestrator/plugins/nx/SKILL.md +12 -12
  44. package/src/orchestrator/plugins/playwright/REFERENCE.md +12 -0
  45. package/src/orchestrator/plugins/playwright/SKILL.md +33 -98
  46. package/src/orchestrator/plugins/prisma/REFERENCE.md +42 -0
  47. package/src/orchestrator/plugins/prisma/SKILL.md +18 -68
  48. package/src/orchestrator/plugins/resend/REFERENCE.md +61 -0
  49. package/src/orchestrator/plugins/resend/SKILL.md +23 -137
  50. package/src/orchestrator/plugins/sanity/SKILL.md +50 -3
  51. package/src/orchestrator/plugins/slack/REFERENCE.md +24 -0
  52. package/src/orchestrator/plugins/slack/SKILL.md +36 -111
  53. package/src/orchestrator/plugins/strapi/REFERENCE.md +35 -0
  54. package/src/orchestrator/plugins/strapi/SKILL.md +60 -24
  55. package/src/orchestrator/plugins/supabase/REFERENCE.md +9 -0
  56. package/src/orchestrator/plugins/supabase/SKILL.md +44 -16
  57. package/src/orchestrator/plugins/teams/REFERENCE.md +36 -0
  58. package/src/orchestrator/plugins/teams/SKILL.md +35 -85
  59. package/src/orchestrator/plugins/trello/REFERENCE.md +9 -0
  60. package/src/orchestrator/plugins/trello/SKILL.md +25 -97
  61. package/src/orchestrator/plugins/turborepo/REFERENCE.md +9 -0
  62. package/src/orchestrator/plugins/turborepo/SKILL.md +13 -1
  63. package/src/orchestrator/plugins/vercel/SKILL.md +45 -52
  64. package/src/orchestrator/plugins/vitest/SKILL.md +10 -14
  65. package/src/orchestrator/prompts/create-skill.prompt.md +62 -20
  66. package/src/orchestrator/prompts/generate-convoy.prompt.md +6 -0
  67. package/src/orchestrator/prompts/generate-prd.prompt.md +4 -0
  68. package/src/orchestrator/skills/accessibility-standards/REFERENCE.md +34 -0
  69. package/src/orchestrator/skills/accessibility-standards/SKILL.md +6 -3
  70. package/src/orchestrator/skills/agent-hooks/HOOKS-REFERENCE.md +48 -0
  71. package/src/orchestrator/skills/agent-hooks/SKILL.md +41 -65
  72. package/src/orchestrator/skills/agent-memory/KNOWLEDGE-GRAPH.md +49 -0
  73. package/src/orchestrator/skills/agent-memory/SKILL.md +30 -67
  74. package/src/orchestrator/skills/api-patterns/SKILL.md +29 -1
  75. package/src/orchestrator/skills/backbone-scaffolding/EXAMPLES.md +16 -0
  76. package/src/orchestrator/skills/backbone-scaffolding/SKILL.md +99 -0
  77. package/src/orchestrator/skills/code-commenting/SKILL.md +1 -1
  78. package/src/orchestrator/skills/context-map/REFERENCE.md +70 -0
  79. package/src/orchestrator/skills/context-map/SKILL.md +28 -55
  80. package/src/orchestrator/skills/data-engineering/REFERENCE.md +55 -0
  81. package/src/orchestrator/skills/data-engineering/SKILL.md +40 -34
  82. package/src/orchestrator/skills/decomposition/REFERENCE.md +28 -0
  83. package/src/orchestrator/skills/decomposition/SKILL.md +15 -30
  84. package/src/orchestrator/skills/deployment-infrastructure/SKILL.md +31 -65
  85. package/src/orchestrator/skills/documentation-standards/SKILL.md +31 -50
  86. package/src/orchestrator/skills/documentation-standards/WRITING-GUIDE.md +39 -0
  87. package/src/orchestrator/skills/fast-review/REFERENCE.md +30 -0
  88. package/src/orchestrator/skills/fast-review/SKILL.md +11 -31
  89. package/src/orchestrator/skills/frontend-design/COMPONENTS.md +113 -0
  90. package/src/orchestrator/skills/frontend-design/REFERENCE.md +36 -0
  91. package/src/orchestrator/skills/frontend-design/SKILL.md +36 -85
  92. package/src/orchestrator/skills/git-workflow/SKILL.md +13 -2
  93. package/src/orchestrator/skills/memory-merger/REFERENCE.md +20 -0
  94. package/src/orchestrator/skills/memory-merger/SKILL.md +29 -38
  95. package/src/orchestrator/skills/observability-logging/SKILL.md +5 -12
  96. package/src/orchestrator/skills/orchestration-protocols/REFERENCE.md +42 -0
  97. package/src/orchestrator/skills/orchestration-protocols/SKILL.md +54 -41
  98. package/src/orchestrator/skills/panel-majority-vote/REFERENCE.md +55 -0
  99. package/src/orchestrator/skills/panel-majority-vote/SKILL.md +30 -75
  100. package/src/orchestrator/skills/performance-optimization/SKILL.md +41 -1
  101. package/src/orchestrator/skills/project-consistency/SKILL.md +50 -89
  102. package/src/orchestrator/skills/project-consistency/TEMPLATES.md +39 -0
  103. package/src/orchestrator/skills/react-development/REFERENCE.md +7 -0
  104. package/src/orchestrator/skills/react-development/SKILL.md +50 -42
  105. package/src/orchestrator/skills/security-hardening/SKILL.md +88 -1
  106. package/src/orchestrator/skills/self-improvement/LESSON-CATEGORIES.md +36 -0
  107. package/src/orchestrator/skills/self-improvement/SKILL.md +19 -25
  108. package/src/orchestrator/skills/seo-patterns/REFERENCE.md +54 -0
  109. package/src/orchestrator/skills/seo-patterns/SKILL.md +20 -88
  110. package/src/orchestrator/skills/session-checkpoints/CHECKPOINT-TEMPLATE.md +58 -0
  111. package/src/orchestrator/skills/session-checkpoints/SKILL.md +34 -58
  112. package/src/orchestrator/skills/team-lead-reference/SKILL.md +37 -30
  113. package/src/orchestrator/skills/testing-workflow/SKILL.md +55 -2
  114. package/src/orchestrator/skills/validation-gates/REFERENCE.md +50 -0
  115. package/src/orchestrator/skills/validation-gates/SKILL.md +39 -35
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: playwright-testing
3
- description: "Playwright E2E testing patterns, cross-browser configuration, page objects, and CI setup. Use when writing E2E tests, visual regression tests, or configuring Playwright in CI pipelines."
3
+ description: "Playwright E2E testing patterns, cross-browser configuration, page objects, and CI setup. Use when creating E2E specs, visual regression suites, or configuring Playwright in CI. Trigger terms: playwright, e2e, trace, page object, cross-browser"
4
4
  ---
5
5
 
6
6
  <!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .opencastle/ directory instead. -->
7
7
 
8
8
  # Playwright Testing
9
9
 
10
- Playwright-specific E2E testing patterns. For project-specific test configuration and breakpoints, see [testing-config.md](../../.opencastle/stack/testing-config.md).
10
+ For project-specific test configuration and breakpoints, see [testing-config.md](../../.opencastle/stack/testing-config.md).
11
11
 
12
12
  ## Commands
13
13
 
@@ -24,85 +24,29 @@ npx playwright show-report # View HTML test report
24
24
  npx playwright install # Install browsers
25
25
  ```
26
26
 
27
- ## Test Structure
27
+ ## File Conventions
28
28
 
29
- ```
30
- tests/
31
- ├── e2e/
32
- │ ├── auth/
33
- │ │ ├── login.spec.ts
34
- │ │ └── signup.spec.ts
35
- │ └── dashboard/
36
- │ └── overview.spec.ts
37
- ├── fixtures/
38
- │ └── auth.fixture.ts # Custom test fixtures
39
- └── pages/
40
- ├── login.page.ts # Page object
41
- └── dashboard.page.ts
42
- ```
29
+ Tests: `tests/e2e/{feature}/`, page objects: `tests/pages/`, fixtures: `tests/fixtures/`.
43
30
 
44
31
  ## Writing Tests
45
32
 
46
33
  ### Basic Test Pattern
47
34
 
48
- ```typescript
35
+ ```ts
49
36
  import { test, expect } from '@playwright/test';
37
+ import { LoginPage } from '../pages/login-page';
50
38
 
51
39
  test.describe('Login', () => {
52
- test.beforeEach(async ({ page }) => {
53
- await page.goto('/login');
54
- });
55
-
56
- test('should log in with valid credentials', async ({ page }) => {
57
- await page.getByTestId('email-input').fill('user@example.com');
58
- await page.getByTestId('password-input').fill('password123');
59
- await page.getByTestId('login-button').click();
60
-
61
- await expect(page).toHaveURL(/.*dashboard/);
62
- await expect(page.getByTestId('user-menu')).toBeVisible();
63
- });
64
-
65
- test('should show error for invalid credentials', async ({ page }) => {
66
- await page.getByTestId('email-input').fill('wrong@example.com');
67
- await page.getByTestId('password-input').fill('wrong');
68
- await page.getByTestId('login-button').click();
69
-
70
- await expect(page.getByTestId('error-message')).toContainText('Invalid credentials');
40
+ test('submits valid credentials', async ({ page }) => {
41
+ const login = new LoginPage(page);
42
+ await login.goto();
43
+ await login.fill('user@example.com', 'password123');
44
+ await page.getByRole('button', { name: 'Sign in' }).click();
45
+ await expect(page.getByTestId('dashboard')).toBeVisible();
71
46
  });
72
47
  });
73
48
  ```
74
49
 
75
- ### Page Object Model
76
-
77
- ```typescript
78
- // tests/pages/login.page.ts
79
- import { type Page, type Locator } from '@playwright/test';
80
-
81
- export class LoginPage {
82
- readonly emailInput: Locator;
83
- readonly passwordInput: Locator;
84
- readonly submitButton: Locator;
85
- readonly errorMessage: Locator;
86
-
87
- constructor(private readonly page: Page) {
88
- this.emailInput = page.getByTestId('email-input');
89
- this.passwordInput = page.getByTestId('password-input');
90
- this.submitButton = page.getByTestId('login-button');
91
- this.errorMessage = page.getByTestId('error-message');
92
- }
93
-
94
- async goto() {
95
- await this.page.goto('/login');
96
- }
97
-
98
- async login(email: string, password: string) {
99
- await this.emailInput.fill(email);
100
- await this.passwordInput.fill(password);
101
- await this.submitButton.click();
102
- }
103
- }
104
- ```
105
-
106
50
  ## API Mocking
107
51
 
108
52
  ```typescript
@@ -114,13 +58,22 @@ await page.route('/api/login', (route) => {
114
58
 
115
59
  ## Locator Strategy
116
60
 
117
- Use Playwright's built-in locators in priority order:
61
+ Prefer semantic locators; avoid brittle CSS selectors:
62
+
63
+ ```ts
64
+ page.getByRole('button', { name: 'Sign in' }) // preferred: ARIA role + accessible name
65
+ page.getByLabel('Email address') // form inputs by label
66
+ page.getByTestId('login-form') // test-id for complex containers
67
+ page.getByText('Welcome back') // visible text
68
+ page.locator('[data-state="open"]') // CSS only when no semantic alternative
69
+ ```
118
70
 
119
- 1. `page.getByTestId()` most resilient
120
- 2. `page.getByRole()` accessible, meaningful
121
- 3. `page.getByLabel()` for form elements
122
- 4. `page.getByText()` for unique visible text
123
- 5. `page.locator()` with CSS last resort
71
+ ## Quick Workflow: Write → Run → Debug → Verify
72
+ 1. Write a focused spec using page objects and `test.describe`.
73
+ 2. Run locally with `npx playwright test --ui` to iterate interactively.
74
+ 3. On failure: collect a trace (`trace: 'on-first-retry'`), save an HTML report, and inspect via `npx playwright show-report`.
75
+ 4. Validation checkpoint: confirm `npx playwright show-report` shows 0 failures and that saved traces contain the failing trace id.
76
+ 5. Fix the test or app; re-run the spec and verify the HTML report is green.
124
77
 
125
78
  ## Configuration (playwright.config.ts)
126
79
 
@@ -153,27 +106,9 @@ export default defineConfig({
153
106
  });
154
107
  ```
155
108
 
156
- ## MCP Tools
157
-
158
- The Playwright MCP server enables AI agents to interact with browsers directly:
159
-
160
- | Tool | Purpose |
161
- |------|---------|
162
- | `playwright/navigate` | Navigate to a URL |
163
- | `playwright/screenshot` | Take page screenshots |
164
- | `playwright/click` | Click elements |
165
- | `playwright/fill` | Fill form inputs |
166
- | `playwright/evaluate` | Execute JavaScript in browser |
167
- | `playwright/expect` | Assert page state |
168
-
169
- ## Best Practices
170
-
171
- - Use `test.describe` to group related tests
172
- - Use `test.beforeEach` for common setup — keep tests independent
173
- - Prefer `getByTestId` and `getByRole` over CSS selectors
174
- - Use `expect(locator).toBeVisible()` before interacting
175
- - Use `page.waitForURL()` or `page.waitForResponse()` instead of arbitrary waits
176
- - Run tests in parallel (`fullyParallel: true`) for speed
177
- - Use `trace: 'on-first-retry'` to debug flaky tests
178
- - Use `codegen` to bootstrap tests, then refactor into page objects
179
- - Use `page.route()` to mock API responses — create isolated, deterministic tests without backend dependencies
109
+ See REFERENCE.md for MCP tools and advanced config (moved to a referenced file).
110
+ ## Best Practices (non-obvious)
111
+
112
+ - Use `trace: 'on-first-retry'` selectively for flaky suites; attach trace IDs to failure tickets
113
+ - Favor page-object reuse across specs but avoid global singletons that leak state between parallel workers
114
+ - For CI visual diffs, snapshot only stable regions and mask dynamic content before comparison
@@ -0,0 +1,42 @@
1
+ > Parent: [SKILL.md](./SKILL.md)
2
+
3
+ ## Prisma Reference: Queries & Patterns
4
+
5
+ ### Basic CRUD
6
+
7
+ ```typescript
8
+ import { PrismaClient } from '@prisma/client';
9
+ const prisma = new PrismaClient();
10
+
11
+ // Create
12
+ await prisma.user.create({ data: { email: 'user@example.com', name: 'Alice' } });
13
+
14
+ // Read (with relations)
15
+ await prisma.user.findUnique({ where: { id: userId }, include: { posts: true } });
16
+
17
+ // Update
18
+ await prisma.user.update({ where: { id: userId }, data: { name: 'Updated Name' } });
19
+
20
+ // Delete
21
+ await prisma.user.delete({ where: { id: userId } });
22
+ ```
23
+
24
+ ### Singleton client pattern
25
+
26
+ ```typescript
27
+ import { PrismaClient } from '@prisma/client';
28
+ const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
29
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient();
30
+ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
31
+ ```
32
+
33
+ ### Best-practice reminders
34
+ - Use `select` for narrow reads; prefer transactions (`prisma.$transaction`) for multi-step updates.
35
+ - Catch `P2002` for unique constraint violations and handle gracefully.
36
+ Last Updated: 2026-03-31
37
+
38
+ Reference: Prisma migration & production checklist
39
+
40
+ - Inspection checklist for generated SQL and destructive changes
41
+ - CI pipeline snippet for `prisma migrate deploy` and `prisma generate`
42
+ - Handling `P2002` unique constraint errors and remediation patterns
@@ -7,7 +7,7 @@ description: "Prisma ORM schema design, migrations, client generation, and query
7
7
 
8
8
  # Prisma Database
9
9
 
10
- Prisma-specific schema design, migration, and query patterns. For project-specific database schema and connection details, see [database-config.md](../../.opencastle/stack/database-config.md).
10
+ For project-specific database schema and connection details, see [database-config.md](../../.opencastle/stack/database-config.md).
11
11
 
12
12
  ## Commands
13
13
 
@@ -63,75 +63,25 @@ model Post {
63
63
  }
64
64
  ```
65
65
 
66
- ### Schema Best Practices
67
-
68
- - Use `cuid()` or `uuid()` for IDs — never auto-increment in distributed systems
69
- - Always add `createdAt` and `updatedAt` timestamps
70
- - Use `@map` and `@@map` to control database table/column names
71
- - Add `@@index` for frequently queried columns
72
- - Use `onDelete: Cascade` where appropriate
73
- - Define relations explicitly with `@relation`
74
- - Use enums for constrained string values
75
-
76
- ## Migration Rules
77
-
78
- 1. Always use `prisma migrate dev` in development — never `db push` for schema changes that need history
79
- 2. Name migrations descriptively: `npx prisma migrate dev --name add_reviews_table`
80
- 3. Review generated SQL before applying — Prisma auto-generates but may need manual adjustments
81
- 4. Test migrations locally before deploying
82
- 5. Use `prisma migrate deploy` in CI/CD — never `migrate dev` in production
83
- 6. Write seed scripts for development data in `prisma/seed.ts`
84
- 7. Never edit applied migration files — create new migrations instead
85
- 8. Run `prisma generate` after every schema change to update the client
86
-
87
- ## Query Patterns
88
-
89
- ### Basic CRUD
90
-
91
- ```typescript
92
- import { PrismaClient } from '@prisma/client';
93
- const prisma = new PrismaClient();
94
-
95
- // Create
96
- const user = await prisma.user.create({
97
- data: { email: 'user@example.com', name: 'Alice' },
98
- });
99
-
100
- // Read (with relations)
101
- const userWithPosts = await prisma.user.findUnique({
102
- where: { id: userId },
103
- include: { posts: true },
104
- });
105
-
106
- // Update
107
- const updated = await prisma.user.update({
108
- where: { id: userId },
109
- data: { name: 'Updated Name' },
110
- });
111
-
112
- // Delete
113
- await prisma.user.delete({ where: { id: userId } });
114
- ```
115
-
116
- ### Singleton Pattern
117
-
118
- ```typescript
119
- // lib/prisma.ts
120
- import { PrismaClient } from '@prisma/client';
66
+ ### Schema Best Practices (Prisma gotchas)
121
67
 
122
- const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
68
+ - Use `cuid()` or `uuid()` for IDs in distributed systems (avoid serial increments).
69
+ - Always include `createdAt` and `updatedAt` timestamps for auditability and migrations.
70
+ - Prefer explicit `@relation` definitions and add `@@index` on frequently queried columns.
71
+ - Review `@map`/`@@map` uses when renaming to prevent accidental column/table mismatches.
72
+ - Avoid editing applied migration files; create corrective migrations instead.
123
73
 
124
- export const prisma = globalForPrisma.prisma ?? new PrismaClient();
74
+ ## Migration Rules (Prisma-specific)
125
75
 
126
- if (process.env.NODE_ENV !== 'production') {
127
- globalForPrisma.prisma = prisma;
128
- }
129
- ```
76
+ 1. Use `npx prisma migrate dev` during development to generate migrations; inspect the generated SQL before applying.
77
+ 2. Use `npx prisma migrate deploy` in CI/CD; never run `migrate dev` in production.
78
+ 3. Name migrations descriptively and include backfill steps for destructive changes.
79
+ 4. Regenerate the client after schema changes: `npx prisma generate`.
130
80
 
131
- ### Best Practices
81
+ ## Migration Workflow: validate → fix → retry (Prisma-focused)
82
+ 1. Run `npx prisma migrate dev --name <desc>` locally to generate SQL.
83
+ 2. Inspect `prisma/migrations/<timestamp>/migration.sql` for destructive operations and add backfills as needed.
84
+ 3. Apply to a local/ephemeral DB and run tests; revert and adjust if failures occur.
85
+ 4. Push migration to CI with `npx prisma migrate deploy` and assert clean application.
132
86
 
133
- - Always use the singleton pattern to avoid connection pool exhaustion
134
- - Use `select` instead of `include` when you only need specific fields
135
- - Use transactions (`prisma.$transaction`) for multi-step operations
136
- - Paginate large result sets with `skip` and `take`
137
- - Handle unique constraint violations with try/catch on `P2002` error code
87
+ For query patterns, singleton client pattern, and runnable CRUD examples see REFERENCE.md in this directory.
@@ -0,0 +1,61 @@
1
+ > Parent: [SKILL.md](./SKILL.md)
2
+
3
+ ## Resend Reference: Templates & Webhook Handler
4
+
5
+ ### React Email template (compact example)
6
+
7
+ ```tsx
8
+ // emails/welcome.tsx
9
+ import { Html, Head, Body, Container, Heading, Text, Button } from '@react-email/components';
10
+
11
+ export function WelcomeEmail({ name, loginUrl = 'https://app.example.com/login' }: { name: string; loginUrl?: string }) {
12
+ return (
13
+ <Html>
14
+ <Head />
15
+ <Body>
16
+ <Container>
17
+ <Heading>Welcome, {name}!</Heading>
18
+ <Text>Your account is ready.</Text>
19
+ <Button href={loginUrl}>Get started</Button>
20
+ </Container>
21
+ </Body>
22
+ </Html>
23
+ );
24
+ }
25
+ ```
26
+
27
+ ### Webhook handler (verify signature)
28
+
29
+ ```typescript
30
+ // app/api/webhooks/resend/route.ts
31
+ import { Webhook } from 'resend';
32
+
33
+ export async function POST(request: Request) {
34
+ const body = await request.text();
35
+ const signature = request.headers.get('svix-signature');
36
+ if (!signature || !process.env.RESEND_WEBHOOK_SECRET) return new Response('Missing signature', { status: 400 });
37
+
38
+ try {
39
+ const webhook = new Webhook(process.env.RESEND_WEBHOOK_SECRET);
40
+ const event = webhook.verify(body, {
41
+ 'svix-id': request.headers.get('svix-id')!,
42
+ 'svix-timestamp': request.headers.get('svix-timestamp')!,
43
+ 'svix-signature': signature,
44
+ });
45
+
46
+ // handle event.type (delivered, bounced, complained)
47
+
48
+ return new Response('OK', { status: 200 });
49
+ } catch (err) {
50
+ console.error('resend webhook verify failed', (err as Error).message);
51
+ return new Response('Invalid signature', { status: 400 });
52
+ }
53
+ }
54
+ ```
55
+ Last Updated: 2026-03-31
56
+
57
+ Reference: Resend verification & webhook troubleshooting
58
+
59
+ - DNS verification commands (dig examples) and expected DKIM/SPF headers
60
+ - Sample webhook test payloads and replay instructions
61
+ - Email header inspection checklist for deliverability debugging
@@ -3,185 +3,71 @@ name: resend-email
3
3
  description: "Resend transactional email patterns, React Email templates, domain configuration, and webhook handling. Use when sending emails, building email templates, or configuring email delivery."
4
4
  ---
5
5
 
6
- <!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .opencastle/ directory instead. -->
7
-
8
6
  # Resend Email
9
7
 
10
- Resend-specific email sending patterns and React Email template conventions.
11
8
 
12
9
  ## Setup
13
10
 
14
11
  ```bash
15
12
  npm install resend
16
- npm install @react-email/components # For React Email templates
13
+ npm install @react-email/components
17
14
  ```
18
15
 
19
- ### Client Initialization
20
-
21
- ```typescript
22
- // lib/resend.ts
23
- import { Resend } from 'resend';
24
-
25
- if (!process.env.RESEND_API_KEY) {
26
- throw new Error('RESEND_API_KEY is required');
27
- }
28
16
 
29
- export const resend = new Resend(process.env.RESEND_API_KEY);
30
- ```
31
17
 
32
18
  ## Sending Emails
33
19
 
34
- ### Basic Send
35
-
36
20
  ```typescript
37
- import { resend } from '@/lib/resend';
21
+ import { Resend } from 'resend';
22
+ import { WelcomeEmail } from '@/emails/welcome';
38
23
 
24
+ const resend = new Resend(process.env.RESEND_API_KEY);
25
+
26
+ // Plain text
39
27
  await resend.emails.send({
40
28
  from: 'App <no-reply@yourdomain.com>',
41
29
  to: ['user@example.com'],
42
- subject: 'Welcome to our app',
43
- html: '<p>Welcome! Your account is ready.</p>',
30
+ subject: 'Welcome',
31
+ html: '<p>Your account is ready.</p>',
44
32
  });
45
- ```
46
-
47
- ### With React Email Template
48
-
49
- ```typescript
50
- import { resend } from '@/lib/resend';
51
- import { WelcomeEmail } from '@/emails/welcome';
52
33
 
34
+ // With React Email template
53
35
  await resend.emails.send({
54
36
  from: 'App <no-reply@yourdomain.com>',
55
37
  to: ['user@example.com'],
56
- subject: 'Welcome to our app',
38
+ subject: 'Welcome',
57
39
  react: WelcomeEmail({ name: 'Alice' }),
58
40
  });
59
41
  ```
60
42
 
61
43
  ## React Email Templates
62
44
 
63
- ### Template Structure
64
45
 
65
- ```
66
- emails/
67
- ├── welcome.tsx
68
- ├── password-reset.tsx
69
- ├── invoice.tsx
70
- └── components/
71
- ├── header.tsx
72
- ├── footer.tsx
73
- └── button.tsx
74
- ```
75
-
76
- ### Template Pattern
77
-
78
- ```tsx
79
- // emails/welcome.tsx
80
- import {
81
- Html, Head, Body, Container, Section,
82
- Heading, Text, Button, Img, Hr,
83
- } from '@react-email/components';
84
-
85
- interface WelcomeEmailProps {
86
- name: string;
87
- loginUrl?: string;
88
- }
89
-
90
- export function WelcomeEmail({
91
- name,
92
- loginUrl = 'https://app.example.com/login',
93
- }: WelcomeEmailProps) {
94
- return (
95
- <Html lang="en">
96
- <Head />
97
- <Body style={bodyStyle}>
98
- <Container style={containerStyle}>
99
- <Heading style={headingStyle}>Welcome, {name}!</Heading>
100
- <Text style={textStyle}>
101
- Your account has been created successfully.
102
- </Text>
103
- <Section style={{ textAlign: 'center' as const }}>
104
- <Button href={loginUrl} style={buttonStyle}>
105
- Get Started
106
- </Button>
107
- </Section>
108
- <Hr style={hrStyle} />
109
- <Text style={footerStyle}>
110
- © 2026 Your App. All rights reserved.
111
- </Text>
112
- </Container>
113
- </Body>
114
- </Html>
115
- );
116
- }
117
-
118
- const bodyStyle = { backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' };
119
- const containerStyle = { margin: '0 auto', padding: '40px 20px', maxWidth: '580px' };
120
- const headingStyle = { fontSize: '24px', color: '#1a1a1a' };
121
- const textStyle = { fontSize: '16px', color: '#4a4a4a', lineHeight: '26px' };
122
- const buttonStyle = {
123
- backgroundColor: '#3b82f6', color: '#fff', fontSize: '16px',
124
- padding: '12px 24px', borderRadius: '6px', textDecoration: 'none',
125
- };
126
- const hrStyle = { borderColor: '#e6e6e6', margin: '32px 0' };
127
- const footerStyle = { fontSize: '12px', color: '#999' };
128
-
129
- export default WelcomeEmail;
130
- ```
46
+ Template pattern and webhook handler examples: see [REFERENCE.md](./REFERENCE.md).
131
47
 
132
48
  ### Preview Templates
133
49
 
134
50
  ```bash
135
- npx email dev # Start React Email dev server at localhost:3000
51
+ npx email dev
136
52
  ```
137
53
 
138
54
  ## Domain Configuration
139
55
 
140
56
  1. Add your domain at resend.com → Domains
141
- 2. Configure DNS records (SPF, DKIM, DMARC) as instructed
142
- 3. Wait for domain verification (usually < 1 hour)
57
+ 2. Configure DNS records (SPF, DKIM, DMARC) and await verification (<1h)
58
+ 3. Post-verify: send one canned test to `postmaster@` and confirm headers (SPF/DKIM pass).
143
59
  4. Use `from: 'Name <no-reply@yourdomain.com>'` in sends
144
60
 
145
61
  ## Webhook Handling
146
62
 
147
- ```typescript
148
- // app/api/webhooks/resend/route.ts
149
- import { Webhook } from 'resend';
150
-
151
- export async function POST(request: Request) {
152
- const body = await request.text();
153
- const signature = request.headers.get('svix-signature');
154
-
155
- const webhook = new Webhook(process.env.RESEND_WEBHOOK_SECRET!);
156
- const event = webhook.verify(body, {
157
- 'svix-id': request.headers.get('svix-id')!,
158
- 'svix-timestamp': request.headers.get('svix-timestamp')!,
159
- 'svix-signature': signature!,
160
- });
161
-
162
- switch (event.type) {
163
- case 'email.delivered':
164
- // Handle successful delivery
165
- break;
166
- case 'email.bounced':
167
- // Handle bounce — remove from mailing list
168
- break;
169
- case 'email.complained':
170
- // Handle spam complaint — unsubscribe immediately
171
- break;
172
- }
173
-
174
- return new Response('OK', { status: 200 });
175
- }
176
- ```
63
+ Webhook handling patterns and a safe, verified handler example are in REFERENCE.md (this directory).
64
+
65
+ ## Quick Verification Workflow
66
+ 1. Add domain and copy DNS records.
67
+ 2. Verify DNS propagation: `dig TXT yourdomain.com` — confirm SPF/DKIM records appear.
68
+ 3. Send a test email and confirm SPF/DKIM pass in headers.
69
+ 4. Deploy webhook handler; test with: `curl -X POST -H 'Content-Type: application/json' -d '{"type":"email.delivered"}' https://yourapp.com/api/webhooks/resend` — assert 200.
70
+ 5. If verification fails: re-check DNS, API keys, and webhook secret.
177
71
 
178
- ## Best Practices
72
+ See REFERENCE.md for detailed verification commands and troubleshooting.
179
73
 
180
- - Always use a verified custom domain — never send from `onboarding@resend.dev` in production
181
- - Use React Email templates for complex emails — plain HTML for simple transactional messages
182
- - Handle bounces and complaints via webhooks — remove invalid addresses promptly
183
- - Use `RESEND_API_KEY` as an environment variable — never commit it
184
- - Test emails locally with React Email dev server before deploying
185
- - Set appropriate `from` addresses: `no-reply@` for transactional, named sender for marketing
186
- - Include unsubscribe links where legally required (CAN-SPAM, GDPR)
187
- - Keep email templates responsive — test in major email clients
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sanity-cms
3
- description: "Sanity CMS development rules, GROQ query patterns, and content management best practices. Use when working with Sanity schemas, writing GROQ queries, modifying content models, or managing CMS configuration."
3
+ description: "Manages Sanity CMS schemas, GROQ queries, dataset exports/imports, and Studio configuration. Use when updating Sanity schemas, running GROQ or Vision queries, exporting datasets, modifying content models, or configuring a headless CMS with Sanity.io."
4
4
  ---
5
5
 
6
6
  <!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .opencastle/ directory instead. -->
@@ -13,8 +13,55 @@ Generic Sanity CMS development methodology. For project-specific configuration,
13
13
 
14
14
  1. **Always check the schema before querying** — use `get_schema` to understand document types and field structures before writing GROQ queries
15
15
  2. **Array vs single reference** — always verify whether a field is an array of references or a single reference; using the wrong query operator causes silent failures
16
- 3. **Local schema files are source of truth** — the Studio schema directory takes precedence over deployed schemas; deploying schemas from a local Studio context creates drift
16
+ 3. **Local schema files are source of truth** — never edit deployed schemas directly; always change local files and redeploy
17
17
  4. **Follow `defineType` and `defineField` patterns** — always use Sanity helpers for type safety and consistency
18
18
  5. **Test GROQ queries in Vision** — validate queries against real data in the Vision plugin before deploying
19
- 6. **Handle draft/publish workflow** — remember drafts have `drafts.` prefix; mutations create drafts, not published documents
19
+ 6. **Handle draft/publish workflow** — always account for the `drafts.` prefix when querying or mutating documents
20
20
  7. **Keep queries in the shared library** — queries belong in a shared queries library, never inline in components
21
+
22
+ ## Quick GROQ examples
23
+
24
+ Fetch published articles with authors populated:
25
+
26
+ ```groq
27
+ *[_type == "article" && defined(publishedAt)] | order(publishedAt desc) {
28
+ _id,
29
+ title,
30
+ "author": author-> { name, _id },
31
+ body
32
+ }
33
+ ```
34
+
35
+ Filter by tag and return first 10:
36
+
37
+ ```groq
38
+ *[_type == "article" && "tech" in tags[]] | order(_createdAt desc)[0..9] { title, slug }
39
+ ```
40
+
41
+ ## Example schema (executable)
42
+
43
+ ```js
44
+ // schemas/article.js
45
+ import { defineType, defineField } from 'sanity'
46
+
47
+ export default defineType({
48
+ name: 'article',
49
+ title: 'Article',
50
+ type: 'document',
51
+ fields: [
52
+ defineField({ name: 'title', type: 'string', title: 'Title' }),
53
+ defineField({ name: 'slug', type: 'slug', options: { source: 'title' } }),
54
+ defineField({ name: 'author', type: 'reference', to: [{ type: 'author' }] }),
55
+ defineField({ name: 'body', type: 'array', of: [{ type: 'block' }] }),
56
+ ],
57
+ })
58
+ ```
59
+
60
+ ## Workflow: change schema → validate → deploy
61
+
62
+ 1. Update local schema files and run `sanity start` to surface schema errors locally.
63
+ 2. Run Vision queries to validate GROQ results against local data: execute representative queries (see examples above).
64
+ 3. Create a migration or dataset export if needed and test changes in a staging project/dataset.
65
+ - Validation checkpoint: run `sanity dataset export` and `sanity dataset import` into a temporary dataset and run queries.
66
+ 4. Deploy the schema to Studio and run end-to-end site builds to confirm runtime behavior.
67
+ 5. If validation fails at any step, revert schema changes locally, fix, and repeat from step 1.