opencastle 0.9.2 → 0.10.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/README.md +12 -69
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +13 -7
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/init.test.d.ts +17 -0
- package/dist/cli/init.test.d.ts.map +1 -0
- package/dist/cli/init.test.js +881 -0
- package/dist/cli/init.test.js.map +1 -0
- package/dist/cli/mcp.d.ts +9 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +56 -0
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/run/adapters/copilot.d.ts +10 -2
- package/dist/cli/run/adapters/copilot.d.ts.map +1 -1
- package/dist/cli/run/adapters/copilot.js +83 -56
- package/dist/cli/run/adapters/copilot.js.map +1 -1
- package/dist/cli/run.js +2 -2
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stack-config-update.test.d.ts +2 -0
- package/dist/cli/stack-config-update.test.d.ts.map +1 -0
- package/dist/cli/stack-config-update.test.js +185 -0
- package/dist/cli/stack-config-update.test.js.map +1 -0
- package/dist/cli/stack-config.d.ts +27 -0
- package/dist/cli/stack-config.d.ts.map +1 -1
- package/dist/cli/stack-config.js +80 -27
- package/dist/cli/stack-config.js.map +1 -1
- package/dist/cli/types.d.ts +1 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +184 -17
- package/dist/cli/update.js.map +1 -1
- package/dist/orchestrator/plugins/astro/config.d.ts +3 -0
- package/dist/orchestrator/plugins/astro/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/astro/config.js +27 -0
- package/dist/orchestrator/plugins/astro/config.js.map +1 -0
- package/dist/orchestrator/plugins/chrome-devtools/config.js +2 -2
- package/dist/orchestrator/plugins/chrome-devtools/config.js.map +1 -1
- package/dist/orchestrator/plugins/contentful/config.js +1 -1
- package/dist/orchestrator/plugins/contentful/config.js.map +1 -1
- package/dist/orchestrator/plugins/convex/config.js +1 -1
- package/dist/orchestrator/plugins/convex/config.js.map +1 -1
- package/dist/orchestrator/plugins/cypress/config.d.ts +3 -0
- package/dist/orchestrator/plugins/cypress/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/cypress/config.js +15 -0
- package/dist/orchestrator/plugins/cypress/config.js.map +1 -0
- package/dist/orchestrator/plugins/figma/config.d.ts +3 -0
- package/dist/orchestrator/plugins/figma/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/figma/config.js +33 -0
- package/dist/orchestrator/plugins/figma/config.js.map +1 -0
- package/dist/orchestrator/plugins/index.d.ts.map +1 -1
- package/dist/orchestrator/plugins/index.js +20 -0
- package/dist/orchestrator/plugins/index.js.map +1 -1
- package/dist/orchestrator/plugins/jira/config.d.ts.map +1 -1
- package/dist/orchestrator/plugins/jira/config.js +2 -3
- package/dist/orchestrator/plugins/jira/config.js.map +1 -1
- package/dist/orchestrator/plugins/linear/config.js +2 -2
- package/dist/orchestrator/plugins/linear/config.js.map +1 -1
- package/dist/orchestrator/plugins/netlify/config.d.ts +3 -0
- package/dist/orchestrator/plugins/netlify/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/netlify/config.js +30 -0
- package/dist/orchestrator/plugins/netlify/config.js.map +1 -0
- package/dist/orchestrator/plugins/nextjs/config.d.ts +3 -0
- package/dist/orchestrator/plugins/nextjs/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/nextjs/config.js +35 -0
- package/dist/orchestrator/plugins/nextjs/config.js.map +1 -0
- package/dist/orchestrator/plugins/nx/config.d.ts.map +1 -1
- package/dist/orchestrator/plugins/nx/config.js +2 -3
- package/dist/orchestrator/plugins/nx/config.js.map +1 -1
- package/dist/orchestrator/plugins/playwright/config.d.ts +3 -0
- package/dist/orchestrator/plugins/playwright/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/playwright/config.js +25 -0
- package/dist/orchestrator/plugins/playwright/config.js.map +1 -0
- package/dist/orchestrator/plugins/prisma/config.d.ts +3 -0
- package/dist/orchestrator/plugins/prisma/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/prisma/config.js +25 -0
- package/dist/orchestrator/plugins/prisma/config.js.map +1 -0
- package/dist/orchestrator/plugins/resend/config.d.ts +3 -0
- package/dist/orchestrator/plugins/resend/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/resend/config.js +46 -0
- package/dist/orchestrator/plugins/resend/config.js.map +1 -0
- package/dist/orchestrator/plugins/sanity/config.d.ts.map +1 -1
- package/dist/orchestrator/plugins/sanity/config.js +1 -2
- package/dist/orchestrator/plugins/sanity/config.js.map +1 -1
- package/dist/orchestrator/plugins/slack/config.js +1 -1
- package/dist/orchestrator/plugins/slack/config.js.map +1 -1
- package/dist/orchestrator/plugins/strapi/config.js +1 -1
- package/dist/orchestrator/plugins/strapi/config.js.map +1 -1
- package/dist/orchestrator/plugins/supabase/config.d.ts.map +1 -1
- package/dist/orchestrator/plugins/supabase/config.js +1 -2
- package/dist/orchestrator/plugins/supabase/config.js.map +1 -1
- package/dist/orchestrator/plugins/teams/config.d.ts.map +1 -1
- package/dist/orchestrator/plugins/teams/config.js +1 -2
- package/dist/orchestrator/plugins/teams/config.js.map +1 -1
- package/dist/orchestrator/plugins/turborepo/config.d.ts +3 -0
- package/dist/orchestrator/plugins/turborepo/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/turborepo/config.js +15 -0
- package/dist/orchestrator/plugins/turborepo/config.js.map +1 -0
- package/dist/orchestrator/plugins/types.d.ts +7 -7
- package/dist/orchestrator/plugins/types.d.ts.map +1 -1
- package/dist/orchestrator/plugins/vercel/config.d.ts.map +1 -1
- package/dist/orchestrator/plugins/vercel/config.js +2 -3
- package/dist/orchestrator/plugins/vercel/config.js.map +1 -1
- package/dist/orchestrator/plugins/vitest/config.d.ts +3 -0
- package/dist/orchestrator/plugins/vitest/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/vitest/config.js +15 -0
- package/dist/orchestrator/plugins/vitest/config.js.map +1 -0
- package/package.json +2 -1
- package/src/cli/doctor.ts +14 -7
- package/src/cli/init.test.ts +1141 -0
- package/src/cli/init.ts +2 -1
- package/src/cli/mcp.ts +77 -1
- package/src/cli/run/adapters/copilot.ts +86 -58
- package/src/cli/run.ts +2 -2
- package/src/cli/stack-config-update.test.ts +210 -0
- package/src/cli/stack-config.ts +110 -37
- package/src/cli/types.ts +1 -1
- package/src/cli/update.ts +230 -23
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/orchestrator/agents/api-designer.agent.md +1 -11
- package/src/orchestrator/agents/architect.agent.md +1 -9
- package/src/orchestrator/agents/content-engineer.agent.md +1 -5
- package/src/orchestrator/agents/copywriter.agent.md +1 -9
- package/src/orchestrator/agents/data-expert.agent.md +2 -6
- package/src/orchestrator/agents/database-engineer.agent.md +1 -6
- package/src/orchestrator/agents/developer.agent.md +2 -12
- package/src/orchestrator/agents/devops-expert.agent.md +1 -5
- package/src/orchestrator/agents/documentation-writer.agent.md +1 -4
- package/src/orchestrator/agents/performance-expert.agent.md +1 -5
- package/src/orchestrator/agents/release-manager.agent.md +1 -11
- package/src/orchestrator/agents/researcher.agent.md +1 -4
- package/src/orchestrator/agents/security-expert.agent.md +2 -7
- package/src/orchestrator/agents/seo-specialist.agent.md +1 -10
- package/src/orchestrator/agents/testing-expert.agent.md +2 -11
- package/src/orchestrator/agents/ui-ux-expert.agent.md +3 -10
- package/src/orchestrator/customizations/README.md +2 -1
- package/src/orchestrator/customizations/agents/skill-matrix.json +106 -0
- package/src/orchestrator/customizations/agents/skill-matrix.md +58 -121
- package/src/orchestrator/instructions/general.instructions.md +1 -1
- package/src/orchestrator/plugins/astro/SKILL.md +288 -0
- package/src/orchestrator/plugins/astro/config.ts +28 -0
- package/src/orchestrator/plugins/chrome-devtools/config.ts +2 -2
- package/src/orchestrator/plugins/contentful/config.ts +1 -1
- package/src/orchestrator/plugins/convex/config.ts +1 -1
- package/src/orchestrator/plugins/cypress/SKILL.md +145 -0
- package/src/orchestrator/plugins/cypress/config.ts +16 -0
- package/src/orchestrator/plugins/figma/SKILL.md +85 -0
- package/src/orchestrator/plugins/figma/config.ts +34 -0
- package/src/orchestrator/plugins/index.ts +20 -0
- package/src/orchestrator/plugins/jira/config.ts +2 -3
- package/src/orchestrator/plugins/linear/config.ts +2 -2
- package/src/orchestrator/plugins/netlify/SKILL.md +134 -0
- package/src/orchestrator/plugins/netlify/config.ts +31 -0
- package/src/orchestrator/plugins/nextjs/SKILL.md +376 -0
- package/src/orchestrator/plugins/nextjs/config.ts +36 -0
- package/src/orchestrator/plugins/nx/config.ts +2 -3
- package/src/orchestrator/plugins/playwright/SKILL.md +191 -0
- package/src/orchestrator/plugins/playwright/config.ts +26 -0
- package/src/orchestrator/plugins/prisma/SKILL.md +137 -0
- package/src/orchestrator/plugins/prisma/config.ts +26 -0
- package/src/orchestrator/plugins/resend/SKILL.md +187 -0
- package/src/orchestrator/plugins/resend/config.ts +47 -0
- package/src/orchestrator/plugins/sanity/config.ts +1 -2
- package/src/orchestrator/plugins/slack/config.ts +1 -1
- package/src/orchestrator/plugins/strapi/config.ts +1 -1
- package/src/orchestrator/plugins/supabase/config.ts +1 -2
- package/src/orchestrator/plugins/teams/config.ts +1 -2
- package/src/orchestrator/plugins/turborepo/SKILL.md +121 -0
- package/src/orchestrator/plugins/turborepo/config.ts +16 -0
- package/src/orchestrator/plugins/types.ts +7 -7
- package/src/orchestrator/plugins/vercel/SKILL.md +99 -0
- package/src/orchestrator/plugins/vercel/config.ts +2 -3
- package/src/orchestrator/plugins/vitest/SKILL.md +166 -0
- package/src/orchestrator/plugins/vitest/config.ts +16 -0
- package/src/orchestrator/prompts/bootstrap-customizations.prompt.md +6 -4
- package/src/orchestrator/prompts/create-skill.prompt.md +6 -7
- package/src/orchestrator/prompts/generate-task-spec.prompt.md +1 -1
- package/src/orchestrator/skills/agent-hooks/SKILL.md +2 -2
- package/src/orchestrator/skills/memory-merger/SKILL.md +1 -1
- package/src/orchestrator/skills/nextjs-patterns/SKILL.md +0 -200
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prisma-database
|
|
3
|
+
description: "Prisma ORM schema design, migrations, client generation, and query patterns. Use when designing database schemas, writing migrations, querying data, or managing Prisma Client."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .github/customizations/ directory instead. -->
|
|
7
|
+
|
|
8
|
+
# Prisma Database
|
|
9
|
+
|
|
10
|
+
Prisma-specific schema design, migration, and query patterns. For project-specific database schema and connection details, see [database-config.md](../../customizations/stack/database-config.md).
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx prisma init # Initialize Prisma in the project
|
|
16
|
+
npx prisma generate # Generate Prisma Client from schema
|
|
17
|
+
npx prisma migrate dev # Create and apply migration (dev)
|
|
18
|
+
npx prisma migrate deploy # Apply pending migrations (production)
|
|
19
|
+
npx prisma migrate reset # Reset database and apply all migrations
|
|
20
|
+
npx prisma db push # Push schema changes without migration
|
|
21
|
+
npx prisma db pull # Introspect database into schema
|
|
22
|
+
npx prisma db seed # Run seed script
|
|
23
|
+
npx prisma studio # Open visual database editor
|
|
24
|
+
npx prisma format # Format schema file
|
|
25
|
+
npx prisma validate # Validate schema syntax
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Schema Design
|
|
29
|
+
|
|
30
|
+
```prisma
|
|
31
|
+
// prisma/schema.prisma
|
|
32
|
+
generator client {
|
|
33
|
+
provider = "prisma-client-js"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
datasource db {
|
|
37
|
+
provider = "postgresql"
|
|
38
|
+
url = env("DATABASE_URL")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
model User {
|
|
42
|
+
id String @id @default(cuid())
|
|
43
|
+
email String @unique
|
|
44
|
+
name String?
|
|
45
|
+
posts Post[]
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
updatedAt DateTime @updatedAt
|
|
48
|
+
|
|
49
|
+
@@index([email])
|
|
50
|
+
@@map("users")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
model Post {
|
|
54
|
+
id String @id @default(cuid())
|
|
55
|
+
title String
|
|
56
|
+
content String?
|
|
57
|
+
published Boolean @default(false)
|
|
58
|
+
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
|
59
|
+
authorId String
|
|
60
|
+
|
|
61
|
+
@@index([authorId])
|
|
62
|
+
@@map("posts")
|
|
63
|
+
}
|
|
64
|
+
```
|
|
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';
|
|
121
|
+
|
|
122
|
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
|
|
123
|
+
|
|
124
|
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
|
|
125
|
+
|
|
126
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
127
|
+
globalForPrisma.prisma = prisma;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Best Practices
|
|
132
|
+
|
|
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
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PluginConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const config: PluginConfig = {
|
|
4
|
+
id: 'prisma',
|
|
5
|
+
name: 'Prisma',
|
|
6
|
+
category: 'tech',
|
|
7
|
+
subCategory: 'database',
|
|
8
|
+
label: 'Prisma',
|
|
9
|
+
hint: 'Type-safe ORM, migrations, schema management',
|
|
10
|
+
skillName: 'prisma-database',
|
|
11
|
+
mcpServerKey: 'Prisma',
|
|
12
|
+
mcpConfig: {
|
|
13
|
+
type: 'stdio',
|
|
14
|
+
command: 'npx',
|
|
15
|
+
args: ['-y', '@anthropic/prisma-mcp@latest'],
|
|
16
|
+
},
|
|
17
|
+
authType: 'none',
|
|
18
|
+
envVars: [],
|
|
19
|
+
agentToolMap: {
|
|
20
|
+
'database-engineer': ['prisma/*'],
|
|
21
|
+
'developer': ['prisma/*'],
|
|
22
|
+
},
|
|
23
|
+
docsUrl: 'https://www.opencastle.dev/docs/plugins#prisma',
|
|
24
|
+
officialDocs: 'https://www.prisma.io/docs',
|
|
25
|
+
mcpPackage: '@anthropic/prisma-mcp',
|
|
26
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: resend-email
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .github/customizations/ directory instead. -->
|
|
7
|
+
|
|
8
|
+
# Resend Email
|
|
9
|
+
|
|
10
|
+
Resend-specific email sending patterns and React Email template conventions.
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install resend
|
|
16
|
+
npm install @react-email/components # For React Email templates
|
|
17
|
+
```
|
|
18
|
+
|
|
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
|
+
|
|
29
|
+
export const resend = new Resend(process.env.RESEND_API_KEY);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Sending Emails
|
|
33
|
+
|
|
34
|
+
### Basic Send
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { resend } from '@/lib/resend';
|
|
38
|
+
|
|
39
|
+
await resend.emails.send({
|
|
40
|
+
from: 'App <no-reply@yourdomain.com>',
|
|
41
|
+
to: ['user@example.com'],
|
|
42
|
+
subject: 'Welcome to our app',
|
|
43
|
+
html: '<p>Welcome! Your account is ready.</p>',
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### With React Email Template
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { resend } from '@/lib/resend';
|
|
51
|
+
import { WelcomeEmail } from '@/emails/welcome';
|
|
52
|
+
|
|
53
|
+
await resend.emails.send({
|
|
54
|
+
from: 'App <no-reply@yourdomain.com>',
|
|
55
|
+
to: ['user@example.com'],
|
|
56
|
+
subject: 'Welcome to our app',
|
|
57
|
+
react: WelcomeEmail({ name: 'Alice' }),
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## React Email Templates
|
|
62
|
+
|
|
63
|
+
### Template Structure
|
|
64
|
+
|
|
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
|
+
```
|
|
131
|
+
|
|
132
|
+
### Preview Templates
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx email dev # Start React Email dev server at localhost:3000
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Domain Configuration
|
|
139
|
+
|
|
140
|
+
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)
|
|
143
|
+
4. Use `from: 'Name <no-reply@yourdomain.com>'` in sends
|
|
144
|
+
|
|
145
|
+
## Webhook Handling
|
|
146
|
+
|
|
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
|
+
```
|
|
177
|
+
|
|
178
|
+
## Best Practices
|
|
179
|
+
|
|
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
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PluginConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const config: PluginConfig = {
|
|
4
|
+
id: 'resend',
|
|
5
|
+
name: 'Resend',
|
|
6
|
+
category: 'tech',
|
|
7
|
+
subCategory: 'email',
|
|
8
|
+
label: 'Resend',
|
|
9
|
+
hint: 'Transactional email API with React templates',
|
|
10
|
+
skillName: 'resend-email',
|
|
11
|
+
mcpServerKey: 'Resend',
|
|
12
|
+
mcpConfig: {
|
|
13
|
+
type: 'stdio',
|
|
14
|
+
command: 'npx',
|
|
15
|
+
args: ['-y', 'resend-mcp'],
|
|
16
|
+
envFile: '${workspaceFolder}/.env',
|
|
17
|
+
},
|
|
18
|
+
authType: 'env-token',
|
|
19
|
+
envVars: [
|
|
20
|
+
{
|
|
21
|
+
name: 'RESEND_API_KEY',
|
|
22
|
+
hint: 'Generate at resend.com → API Keys',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
agentToolMap: {
|
|
26
|
+
'developer': [
|
|
27
|
+
'resend_send_email', 'resend_batch_send_email', 'resend_get_email',
|
|
28
|
+
'resend_list_emails', 'resend_cancel_email', 'resend_update_email',
|
|
29
|
+
],
|
|
30
|
+
'devops-expert': [
|
|
31
|
+
'resend_create_domain', 'resend_list_domains', 'resend_get_domain',
|
|
32
|
+
'resend_verify_domain', 'resend_update_domain', 'resend_remove_domain',
|
|
33
|
+
'resend_create_api_key', 'resend_list_api_keys', 'resend_remove_api_key',
|
|
34
|
+
'resend_create_webhook', 'resend_list_webhooks', 'resend_get_webhook',
|
|
35
|
+
'resend_update_webhook', 'resend_remove_webhook',
|
|
36
|
+
],
|
|
37
|
+
'data-expert': [
|
|
38
|
+
'resend_create_contact', 'resend_list_contacts', 'resend_get_contact',
|
|
39
|
+
'resend_update_contact', 'resend_remove_contact',
|
|
40
|
+
'resend_create_broadcast', 'resend_list_broadcasts', 'resend_get_broadcast',
|
|
41
|
+
'resend_send_broadcast',
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
docsUrl: 'https://www.opencastle.dev/docs/plugins#resend',
|
|
45
|
+
officialDocs: 'https://resend.com/docs',
|
|
46
|
+
mcpPackage: 'resend-mcp',
|
|
47
|
+
};
|
|
@@ -29,7 +29,7 @@ export const config: PluginConfig = {
|
|
|
29
29
|
'team-lead': ['slack/*'],
|
|
30
30
|
'release-manager': ['slack/*'],
|
|
31
31
|
},
|
|
32
|
-
docsUrl: '/
|
|
32
|
+
docsUrl: 'https://www.opencastle.dev/docs/plugins#slack',
|
|
33
33
|
officialDocs: 'https://api.slack.com/docs',
|
|
34
34
|
mcpPackage: '@kazuph/mcp-slack',
|
|
35
35
|
};
|
|
@@ -35,7 +35,7 @@ export const config: PluginConfig = {
|
|
|
35
35
|
'strapi/get_entry', 'strapi/create_entry', 'strapi/update_entry',
|
|
36
36
|
],
|
|
37
37
|
},
|
|
38
|
-
docsUrl:
|
|
38
|
+
docsUrl: 'https://www.opencastle.dev/docs/plugins#strapi',
|
|
39
39
|
officialDocs: 'https://docs.strapi.io/',
|
|
40
40
|
mcpPackage: 'strapi-mcp',
|
|
41
41
|
};
|
|
@@ -31,9 +31,8 @@ export const config: PluginConfig = {
|
|
|
31
31
|
'Teams/mcp_graph_teams_listChannelMessages',
|
|
32
32
|
],
|
|
33
33
|
},
|
|
34
|
-
docsUrl:
|
|
34
|
+
docsUrl: 'https://www.opencastle.dev/docs/plugins#teams',
|
|
35
35
|
officialDocs: 'https://learn.microsoft.com/en-us/microsoftteams/',
|
|
36
|
-
mcpPackage: null,
|
|
37
36
|
mcpInputs: [
|
|
38
37
|
{
|
|
39
38
|
id: 'tenant_id',
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: turborepo-monorepo
|
|
3
|
+
description: "Turborepo monorepo commands, pipeline configuration, caching strategies, and task orchestration. Use when running builds, tests, linting, or any development commands in a Turborepo monorepo."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .github/customizations/ directory instead. -->
|
|
7
|
+
|
|
8
|
+
# Turborepo Monorepo
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
### Running Tasks
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
turbo run build # Build all packages
|
|
16
|
+
turbo run test # Test all packages
|
|
17
|
+
turbo run lint # Lint all packages
|
|
18
|
+
turbo run build --filter=web # Build specific package
|
|
19
|
+
turbo run build --filter=./apps/* # Build all apps
|
|
20
|
+
turbo run build --filter=...[HEAD~1] # Only affected since last commit
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Common Patterns
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
turbo run build test lint # Run multiple tasks
|
|
27
|
+
turbo run build --dry-run # Preview what would run
|
|
28
|
+
turbo run build --graph # Visualize task graph
|
|
29
|
+
turbo run build --force # Ignore cache, rebuild all
|
|
30
|
+
turbo run build --concurrency=4 # Limit parallelism
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Forbidden Commands
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# NEVER use these directly — always go through turbo:
|
|
37
|
+
npm run build # Skips caching and parallelism
|
|
38
|
+
cd apps/web && npm test # Skips dependency resolution
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Pipeline Configuration (turbo.json)
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"$schema": "https://turbo.build/schema.json",
|
|
46
|
+
"tasks": {
|
|
47
|
+
"build": {
|
|
48
|
+
"dependsOn": ["^build"],
|
|
49
|
+
"outputs": ["dist/**", ".next/**"]
|
|
50
|
+
},
|
|
51
|
+
"test": {
|
|
52
|
+
"dependsOn": ["build"],
|
|
53
|
+
"inputs": ["src/**", "test/**"]
|
|
54
|
+
},
|
|
55
|
+
"lint": {
|
|
56
|
+
"dependsOn": ["^build"]
|
|
57
|
+
},
|
|
58
|
+
"dev": {
|
|
59
|
+
"cache": false,
|
|
60
|
+
"persistent": true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Key Concepts
|
|
67
|
+
|
|
68
|
+
- `^build` — run `build` in dependencies first (topological)
|
|
69
|
+
- `dependsOn` — declare task dependencies
|
|
70
|
+
- `outputs` — files to cache (miss = rebuild)
|
|
71
|
+
- `inputs` — files to hash for cache key (default: all tracked files)
|
|
72
|
+
- `cache: false` — never cache (use for `dev`, `start`)
|
|
73
|
+
- `persistent: true` — long-running tasks (dev servers)
|
|
74
|
+
|
|
75
|
+
## Caching
|
|
76
|
+
|
|
77
|
+
### Local Cache
|
|
78
|
+
|
|
79
|
+
Turborepo caches task outputs automatically in `node_modules/.cache/turbo`. Cache keys are computed from:
|
|
80
|
+
|
|
81
|
+
1. Task inputs (source files)
|
|
82
|
+
2. Environment variables
|
|
83
|
+
3. Dependencies' build outputs
|
|
84
|
+
4. `turbo.json` configuration
|
|
85
|
+
|
|
86
|
+
### Remote Cache
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
turbo login # Authenticate
|
|
90
|
+
turbo link # Link project to remote cache
|
|
91
|
+
turbo run build --remote-only # Force remote cache usage
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- Shares cache across CI and team members
|
|
95
|
+
- Vercel Remote Cache or self-hosted (Ducktape, TurboCache)
|
|
96
|
+
- Set `TURBO_TOKEN` and `TURBO_TEAM` in CI environment
|
|
97
|
+
|
|
98
|
+
## Package Workspace Structure
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
monorepo/
|
|
102
|
+
├── turbo.json
|
|
103
|
+
├── package.json # Root workspace config
|
|
104
|
+
├── apps/
|
|
105
|
+
│ ├── web/ # Next.js app
|
|
106
|
+
│ └── docs/ # Documentation site
|
|
107
|
+
├── packages/
|
|
108
|
+
│ ├── ui/ # Shared UI components
|
|
109
|
+
│ ├── config/ # Shared config (ESLint, TS)
|
|
110
|
+
│ └── utils/ # Shared utilities
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Best Practices
|
|
114
|
+
|
|
115
|
+
- Always use `turbo run` instead of directly invoking package scripts
|
|
116
|
+
- Define `outputs` for every cacheable task — missing outputs mean missing cache
|
|
117
|
+
- Use `--filter` to scope commands to affected packages
|
|
118
|
+
- Set `inputs` to narrow cache keys and avoid unnecessary rebuilds
|
|
119
|
+
- Use `--dry-run` to debug pipeline configuration
|
|
120
|
+
- Add `TURBO_TOKEN` and `TURBO_TEAM` to CI for remote caching
|
|
121
|
+
- Never commit `.turbo/` or `node_modules/.cache/turbo`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PluginConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const config: PluginConfig = {
|
|
4
|
+
id: 'turborepo',
|
|
5
|
+
name: 'Turborepo',
|
|
6
|
+
category: 'tech',
|
|
7
|
+
subCategory: 'codebase-tool',
|
|
8
|
+
label: 'Turborepo',
|
|
9
|
+
hint: 'Monorepo build system with remote caching',
|
|
10
|
+
skillName: 'turborepo-monorepo',
|
|
11
|
+
authType: 'none',
|
|
12
|
+
envVars: [],
|
|
13
|
+
agentToolMap: {},
|
|
14
|
+
docsUrl: 'https://www.opencastle.dev/docs/plugins#turborepo',
|
|
15
|
+
officialDocs: 'https://turbo.build/repo/docs',
|
|
16
|
+
};
|
|
@@ -13,7 +13,7 @@ export interface PluginConfig {
|
|
|
13
13
|
category: 'tech' | 'team';
|
|
14
14
|
|
|
15
15
|
/** Sub-category for grouping */
|
|
16
|
-
subCategory: 'cms' | 'database' | 'deployment' | '
|
|
16
|
+
subCategory: 'cms' | 'database' | 'deployment' | 'framework' | 'codebase-tool' | 'task-management' | 'notifications' | 'testing' | 'e2e-testing' | 'design' | 'email';
|
|
17
17
|
|
|
18
18
|
/** Label shown in the `npx opencastle init` multiselect */
|
|
19
19
|
label: string;
|
|
@@ -24,11 +24,11 @@ export interface PluginConfig {
|
|
|
24
24
|
/** Skill directory name (matches the old skills/ dirname). null if no skill. */
|
|
25
25
|
skillName: string | null;
|
|
26
26
|
|
|
27
|
-
/** MCP server key used in the generated MCP config.
|
|
28
|
-
mcpServerKey
|
|
27
|
+
/** MCP server key used in the generated MCP config. Omit if no MCP server. */
|
|
28
|
+
mcpServerKey?: string;
|
|
29
29
|
|
|
30
|
-
/** Raw MCP server config */
|
|
31
|
-
mcpConfig
|
|
30
|
+
/** Raw MCP server config (required when mcpServerKey is set) */
|
|
31
|
+
mcpConfig?: McpServerConfig;
|
|
32
32
|
|
|
33
33
|
/** Authentication type */
|
|
34
34
|
authType: 'oauth' | 'env-token' | 'none';
|
|
@@ -46,8 +46,8 @@ export interface PluginConfig {
|
|
|
46
46
|
/** Official product documentation URL */
|
|
47
47
|
officialDocs: string;
|
|
48
48
|
|
|
49
|
-
/** NPM package for the MCP server (
|
|
50
|
-
mcpPackage
|
|
49
|
+
/** NPM package for the MCP server (omit for HTTP/OAuth servers or plugins without MCP) */
|
|
50
|
+
mcpPackage?: string;
|
|
51
51
|
|
|
52
52
|
/** Whether this plugin should be preselected in the init prompt */
|
|
53
53
|
preselected?: boolean;
|