opencastle 0.32.4 → 0.32.6

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 (108) hide show
  1. package/README.md +13 -3
  2. package/bin/cli.mjs +2 -0
  3. package/dist/cli/bootstrap.js +1 -1
  4. package/dist/cli/bootstrap.js.map +1 -1
  5. package/dist/cli/bootstrap.test.js +16 -0
  6. package/dist/cli/bootstrap.test.js.map +1 -1
  7. package/dist/cli/init.test.js +38 -0
  8. package/dist/cli/init.test.js.map +1 -1
  9. package/dist/cli/stack-config-update.test.js +18 -0
  10. package/dist/cli/stack-config-update.test.js.map +1 -1
  11. package/dist/cli/stack-config.d.ts.map +1 -1
  12. package/dist/cli/stack-config.js +1 -0
  13. package/dist/cli/stack-config.js.map +1 -1
  14. package/dist/cli/types.d.ts +1 -1
  15. package/dist/cli/types.d.ts.map +1 -1
  16. package/dist/orchestrator/plugins/index.d.ts.map +1 -1
  17. package/dist/orchestrator/plugins/index.js +4 -0
  18. package/dist/orchestrator/plugins/index.js.map +1 -1
  19. package/dist/orchestrator/plugins/notion/config.d.ts +3 -0
  20. package/dist/orchestrator/plugins/notion/config.d.ts.map +1 -0
  21. package/dist/orchestrator/plugins/notion/config.js +46 -0
  22. package/dist/orchestrator/plugins/notion/config.js.map +1 -0
  23. package/dist/orchestrator/plugins/trello/config.d.ts +3 -0
  24. package/dist/orchestrator/plugins/trello/config.d.ts.map +1 -0
  25. package/dist/orchestrator/plugins/trello/config.js +43 -0
  26. package/dist/orchestrator/plugins/trello/config.js.map +1 -0
  27. package/dist/orchestrator/plugins/types.d.ts +1 -1
  28. package/dist/orchestrator/plugins/types.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/cli/bootstrap.test.ts +21 -0
  31. package/src/cli/bootstrap.ts +1 -1
  32. package/src/cli/init.test.ts +46 -0
  33. package/src/cli/stack-config-update.test.ts +20 -0
  34. package/src/cli/stack-config.ts +1 -0
  35. package/src/cli/types.ts +1 -1
  36. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  37. package/src/orchestrator/agents/api-designer.agent.md +25 -34
  38. package/src/orchestrator/agents/architect.agent.md +40 -84
  39. package/src/orchestrator/agents/content-engineer.agent.md +29 -31
  40. package/src/orchestrator/agents/copywriter.agent.md +35 -60
  41. package/src/orchestrator/agents/data-expert.agent.md +24 -30
  42. package/src/orchestrator/agents/database-engineer.agent.md +26 -31
  43. package/src/orchestrator/agents/developer.agent.md +32 -34
  44. package/src/orchestrator/agents/devops-expert.agent.md +31 -26
  45. package/src/orchestrator/agents/documentation-writer.agent.md +29 -29
  46. package/src/orchestrator/agents/performance-expert.agent.md +36 -33
  47. package/src/orchestrator/agents/release-manager.agent.md +25 -34
  48. package/src/orchestrator/agents/researcher.agent.md +41 -95
  49. package/src/orchestrator/agents/reviewer.agent.md +24 -34
  50. package/src/orchestrator/agents/security-expert.agent.md +35 -39
  51. package/src/orchestrator/agents/seo-specialist.agent.md +25 -32
  52. package/src/orchestrator/agents/session-guard.agent.md +20 -79
  53. package/src/orchestrator/agents/team-lead.agent.md +50 -254
  54. package/src/orchestrator/agents/testing-expert.agent.md +37 -49
  55. package/src/orchestrator/agents/ui-ux-expert.agent.md +33 -39
  56. package/src/orchestrator/customizations/KNOWN-ISSUES.md +0 -1
  57. package/src/orchestrator/customizations/agents/skill-matrix.json +20 -4
  58. package/src/orchestrator/customizations/agents/skill-matrix.md +20 -0
  59. package/src/orchestrator/instructions/general.instructions.md +24 -84
  60. package/src/orchestrator/plugins/astro/SKILL.md +23 -179
  61. package/src/orchestrator/plugins/convex/SKILL.md +38 -12
  62. package/src/orchestrator/plugins/index.ts +4 -0
  63. package/src/orchestrator/plugins/netlify/SKILL.md +17 -13
  64. package/src/orchestrator/plugins/nextjs/SKILL.md +55 -261
  65. package/src/orchestrator/plugins/notion/SKILL.md +205 -0
  66. package/src/orchestrator/plugins/notion/config.ts +47 -0
  67. package/src/orchestrator/plugins/nx/SKILL.md +20 -72
  68. package/src/orchestrator/plugins/playwright/SKILL.md +5 -17
  69. package/src/orchestrator/plugins/slack/SKILL.md +28 -190
  70. package/src/orchestrator/plugins/teams/SKILL.md +10 -140
  71. package/src/orchestrator/plugins/trello/SKILL.md +151 -0
  72. package/src/orchestrator/plugins/trello/config.ts +44 -0
  73. package/src/orchestrator/plugins/types.ts +1 -1
  74. package/src/orchestrator/plugins/vitest/SKILL.md +2 -2
  75. package/src/orchestrator/prompts/bug-fix.prompt.md +25 -63
  76. package/src/orchestrator/prompts/implement-feature.prompt.md +29 -66
  77. package/src/orchestrator/prompts/quick-refinement.prompt.md +31 -66
  78. package/src/orchestrator/skills/accessibility-standards/SKILL.md +50 -105
  79. package/src/orchestrator/skills/agent-hooks/SKILL.md +60 -110
  80. package/src/orchestrator/skills/agent-memory/SKILL.md +44 -93
  81. package/src/orchestrator/skills/api-patterns/SKILL.md +20 -68
  82. package/src/orchestrator/skills/code-commenting/SKILL.md +49 -101
  83. package/src/orchestrator/skills/context-map/SKILL.md +47 -88
  84. package/src/orchestrator/skills/data-engineering/SKILL.md +27 -74
  85. package/src/orchestrator/skills/decomposition/SKILL.md +50 -98
  86. package/src/orchestrator/skills/deployment-infrastructure/SKILL.md +44 -107
  87. package/src/orchestrator/skills/documentation-standards/SKILL.md +28 -89
  88. package/src/orchestrator/skills/fast-review/SKILL.md +51 -276
  89. package/src/orchestrator/skills/frontend-design/SKILL.md +53 -163
  90. package/src/orchestrator/skills/git-workflow/SKILL.md +18 -54
  91. package/src/orchestrator/skills/memory-merger/SKILL.md +51 -88
  92. package/src/orchestrator/skills/observability-logging/SKILL.md +29 -75
  93. package/src/orchestrator/skills/orchestration-protocols/SKILL.md +58 -117
  94. package/src/orchestrator/skills/panel-majority-vote/SKILL.md +65 -140
  95. package/src/orchestrator/skills/performance-optimization/SKILL.md +21 -85
  96. package/src/orchestrator/skills/project-consistency/SKILL.md +62 -281
  97. package/src/orchestrator/skills/react-development/SKILL.md +38 -86
  98. package/src/orchestrator/skills/security-hardening/SKILL.md +40 -84
  99. package/src/orchestrator/skills/self-improvement/SKILL.md +26 -60
  100. package/src/orchestrator/skills/seo-patterns/SKILL.md +40 -105
  101. package/src/orchestrator/skills/session-checkpoints/SKILL.md +26 -68
  102. package/src/orchestrator/skills/team-lead-reference/SKILL.md +66 -206
  103. package/src/orchestrator/skills/testing-workflow/SKILL.md +42 -112
  104. package/src/orchestrator/skills/validation-gates/SKILL.md +39 -170
  105. package/src/orchestrator/snippets/base-output-contract.md +14 -0
  106. package/src/orchestrator/snippets/discovered-issues-policy.md +15 -0
  107. package/src/orchestrator/snippets/logging-mandatory.md +11 -0
  108. package/src/orchestrator/snippets/never-expose-secrets.md +22 -0
@@ -16,149 +16,93 @@ description: "Next.js framework best practices covering App Router, server/clien
16
16
  │ ├── loading.tsx # Loading UI (Suspense boundary)
17
17
  │ ├── error.tsx # Error boundary (Client Component)
18
18
  │ ├── not-found.tsx # 404 page
19
- │ ├── global-error.tsx # Root error boundary
20
19
  │ ├── (marketing)/ # Route group (no URL segment)
21
- │ │ ├── about/page.tsx # → /about
22
- │ │ └── blog/page.tsx # → /blog
20
+ │ │ └── about/page.tsx # → /about
23
21
  │ ├── dashboard/
24
22
  │ │ ├── layout.tsx # Nested layout
25
- │ │ ├── page.tsx # → /dashboard
26
23
  │ │ └── [id]/page.tsx # → /dashboard/:id
27
24
  │ └── api/
28
25
  │ └── users/route.ts # API route handler
29
26
  ├── components/ # Shared React components
30
27
  ├── lib/ # Utilities, helpers, server logic
31
- ├── public/ # Static assets (served as-is)
28
+ ├── public/ # Static assets
32
29
  ├── next.config.ts # Next.js configuration
33
30
  ├── middleware.ts # Edge middleware
34
31
  └── .env.local # Environment variables (not committed)
35
32
  ```
36
33
 
37
- - **Route Groups** `(name)` organize routes without affecting the URL.
38
- - **Private Folders** `_internal` — opt out of routing entirely.
39
- - **Parallel Routes** `@modal` — render multiple pages in the same layout.
40
- - **Intercepting Routes** `(.)photo` — intercept navigation to show modals.
34
+ Route Groups `(name)` organize routes without affecting the URL. Private Folders `_internal` opt out of routing. Parallel Routes `@modal` render multiple pages in the same layout.
41
35
 
42
36
  ## Rendering Strategies
43
37
 
44
- Next.js supports multiple rendering strategies per route:
45
-
46
38
  | Strategy | When | How |
47
39
  |----------|------|-----|
48
40
  | **Static (SSG)** | Build time | Default for pages with no dynamic data |
49
- | **Incremental Static Regeneration (ISR)** | Build + revalidation | `fetch` with `next: { revalidate: N }` or route segment config |
50
- | **Server-Side Rendering (SSR)** | Every request | `export const dynamic = 'force-dynamic'` or dynamic functions (`cookies()`, `headers()`) |
51
- | **Client-Side Rendering (CSR)** | Browser | `'use client'` components with `useEffect`/SWR |
41
+ | **ISR** | Build + revalidation | `fetch` with `next: { revalidate: N }` or route segment config |
42
+ | **SSR** | Every request | `export const dynamic = 'force-dynamic'` or dynamic functions |
43
+ | **CSR** | Browser | `'use client'` components with `useEffect`/SWR |
52
44
  | **Streaming** | Progressive | `<Suspense>` boundaries + `loading.tsx` |
53
- | **Partial Prerendering (PPR)** | Build + streaming | Static shell with dynamic holes via `<Suspense>` |
54
-
55
- ### Route Segment Config
56
-
57
- Control per-route rendering behavior:
45
+ | **PPR** | Build + streaming | Static shell with dynamic holes via `<Suspense>` |
58
46
 
59
- ```tsx
60
- // app/dashboard/page.tsx
61
- export const dynamic = 'force-dynamic'; // SSR every request
62
- export const revalidate = 60; // ISR: revalidate every 60s
63
- export const fetchCache = 'default-cache'; // Cache fetch requests
64
- export const runtime = 'nodejs'; // 'nodejs' | 'edge'
65
- ```
47
+ Route segment config: `export const dynamic = 'force-dynamic'`, `export const revalidate = 60`, `export const runtime = 'edge'`.
66
48
 
67
49
  ## Server and Client Components
68
50
 
69
- **Default: Server Components** — data fetching, heavy logic, non-interactive UI.
70
-
51
+ **Default: Server Components** — data fetching, heavy logic, non-interactive UI.
71
52
  **Client Components** — add `'use client'` at top. Use for interactivity, state, browser APIs.
72
53
 
73
- ### Decision Table
74
-
75
- | Need | Component Type | Why |
76
- |------|---------------|-----|
77
- | Fetch data at request time | Server | Direct DB/API access, no client waterfall |
78
- | Read cookies/headers | Server | Available only on the server |
79
- | Interactive UI (clicks, inputs) | Client | Requires event handlers |
80
- | Use `useState` / `useEffect` | Client | React hooks need client runtime |
81
- | Access browser APIs (localStorage, geolocation) | Client | Not available on server |
82
- | Render static/non-interactive content | Server | Smaller bundle, faster paint |
83
- | Show loading spinners for async children | Server (with `<Suspense>`) | Streams HTML progressively |
54
+ | Need | Component Type |
55
+ |------|---------------|
56
+ | Fetch data / read cookies/headers | Server |
57
+ | Interactive UI (clicks, inputs) | Client |
58
+ | `useState` / `useEffect` / browser APIs | Client |
59
+ | Static/non-interactive content | Server |
60
+ | Async children with loading state | Server + `<Suspense>` |
84
61
 
85
62
  ### Critical Rule
86
63
 
87
- **Never use `next/dynamic` with `{ ssr: false }` inside a Server Component.** Move client-only logic into a dedicated `'use client'` component, then import it normally.
64
+ **Never use `next/dynamic` with `{ ssr: false }` inside a Server Component.** Extract to a dedicated `'use client'` component and import it normally.
88
65
 
89
66
  ## Data Fetching
90
67
 
91
- ### Server-Side Fetching
92
-
93
- Fetch directly in `async` Server Components. Next.js deduplicates identical `fetch` calls.
68
+ ### Server-Side
94
69
 
95
70
  ```tsx
96
71
  export default async function ProjectsPage() {
97
- const projects = await fetch('https://api.example.com/projects', {
98
- next: { revalidate: 60 },
99
- }).then((res) => res.json());
100
- return <ul>{projects.map((p: { id: string; name: string }) => <li key={p.id}>{p.name}</li>)}</ul>;
72
+ const data = await fetch('/api/projects', { next: { revalidate: 60 } }).then((r) => r.json());
73
+ return <ul>{data.map((p: { id: string; name: string }) => <li key={p.id}>{p.name}</li>)}</ul>;
101
74
  }
102
75
  ```
103
76
 
104
- ### Server Actions (Mutations)
105
-
106
- Define with `'use server'`. Call from Client Components via `action` or `startTransition`.
77
+ ### Server Actions
107
78
 
108
79
  ```tsx
109
- // lib/actions.ts
110
80
  'use server';
111
81
  import { revalidatePath } from 'next/cache';
112
82
  export async function createItem(formData: FormData) {
113
- const name = formData.get('name') as string;
114
- await db.items.create({ data: { name } });
83
+ await db.items.create({ data: { name: formData.get('name') as string } });
115
84
  revalidatePath('/items');
116
85
  }
117
86
  ```
118
87
 
119
- ```tsx
120
- // components/CreateItemForm.tsx
121
- 'use client';
122
- import { createItem } from '@/lib/actions';
123
- export default function CreateItemForm() {
124
- return <form action={createItem}><input name="name" required /><button type="submit">Add</button></form>;
125
- }
126
- ```
88
+ Use from a Client Component: `<form action={createItem}>...</form>`
127
89
 
128
- ### Parallel Data Fetching
129
-
130
- Initiate independent fetches simultaneously — never `await` sequentially.
90
+ ### Parallel Fetching
131
91
 
132
92
  ```tsx
133
- export default async function DashboardPage() {
134
- const [metrics, activity] = await Promise.all([getMetrics(), getRecentActivity()]);
135
- return <><MetricsPanel data={metrics} /><ActivityFeed items={activity} /></>;
136
- }
93
+ const [metrics, activity] = await Promise.all([getMetrics(), getRecentActivity()]);
137
94
  ```
138
95
 
139
- ## Caching and Revalidation
96
+ ## Caching
140
97
 
141
98
  | Mechanism | Scope | How to Use |
142
99
  |-----------|-------|------------|
143
100
  | **Request Memoization** | Per-request | Automatic deduplication of identical `fetch` calls |
144
- | **Data Cache** | Cross-request | `fetch` results cached by default; opt out with `cache: 'no-store'` |
101
+ | **Data Cache** | Cross-request | Cached by default; opt out with `cache: 'no-store'` |
145
102
  | **Full Route Cache** | Build time | Static routes cached as HTML + RSC payload |
146
103
  | **Router Cache** | Client-side | Prefetched and visited routes cached in browser |
147
104
 
148
- ### Revalidation
149
-
150
- ```tsx
151
- // Time-based — revalidate every 60 seconds
152
- fetch(url, { next: { revalidate: 60 } });
153
-
154
- // On-demand — revalidate by path or tag
155
- import { revalidatePath, revalidateTag } from 'next/cache';
156
- revalidatePath('/blog');
157
- revalidateTag('posts');
158
-
159
- // Tag a fetch for on-demand revalidation
160
- fetch(url, { next: { tags: ['posts'] } });
161
- ```
105
+ On-demand revalidation: `revalidatePath('/blog')` or `revalidateTag('posts')`. Tag a fetch: `fetch(url, { next: { tags: ['posts'] } })`.
162
106
 
163
107
  ## Routing
164
108
 
@@ -166,211 +110,61 @@ fetch(url, { next: { tags: ['posts'] } });
166
110
 
167
111
  | File | Purpose |
168
112
  |------|---------|
169
- | `page.tsx` | Route UI (makes segment publicly accessible) |
170
- | `layout.tsx` | Shared layout (wraps children, persists across navigation) |
113
+ | `page.tsx` | Route UI |
114
+ | `layout.tsx` | Shared layout (persists across navigation) |
171
115
  | `template.tsx` | Like layout but re-mounts on navigation |
172
116
  | `loading.tsx` | Loading UI (automatic Suspense boundary) |
173
- | `error.tsx` | Error UI (Client Component, automatic error boundary) |
117
+ | `error.tsx` | Error UI (Client Component) |
174
118
  | `not-found.tsx` | 404 UI |
175
- | `route.ts` | API endpoint (no UI) |
119
+ | `route.ts` | API endpoint |
176
120
  | `default.tsx` | Fallback for parallel routes |
177
121
 
178
122
  ### Dynamic Routes
179
123
 
180
- ```
181
- app/blog/[slug]/page.tsx /blog/:slug
182
- app/shop/[...slug]/page.tsx /shop/:slug+ (catch-all)
183
- app/shop/[[...slug]]/page.tsx → /shop or /shop/:slug+ (optional catch-all)
184
- ```
185
-
186
- ### Route Handlers (API Routes)
187
-
188
- ```ts
189
- // app/api/users/route.ts
190
- import { NextRequest, NextResponse } from 'next/server';
191
-
192
- export async function GET(request: NextRequest) {
193
- const { searchParams } = request.nextUrl;
194
- const query = searchParams.get('q');
195
- const users = await findUsers(query);
196
- return NextResponse.json(users);
197
- }
198
-
199
- export async function POST(request: NextRequest) {
200
- const body = await request.json();
201
- const user = await createUser(body);
202
- return NextResponse.json(user, { status: 201 });
203
- }
204
- ```
205
-
206
- ## Error Handling
207
-
208
- ```tsx
209
- // app/dashboard/error.tsx — must be a Client Component
210
- 'use client';
211
- export default function DashboardError({ error, reset }: { error: Error; reset: () => void }) {
212
- return <div role="alert"><h2>Something went wrong</h2><button onClick={reset}>Try again</button></div>;
213
- }
214
- ```
215
-
216
- ```tsx
217
- // app/projects/[id]/page.tsx — trigger not-found boundary
218
- import { notFound } from 'next/navigation';
219
- export default async function ProjectPage({ params }: { params: { id: string } }) {
220
- const project = await getProject(params.id);
221
- if (!project) notFound();
222
- return <h1>{project.name}</h1>;
223
- }
224
- ```
124
+ - `app/blog/[slug]/page.tsx` → `/blog/:slug`
125
+ - `app/shop/[...slug]/page.tsx` `/shop/:slug+` (catch-all)
126
+ - `app/shop/[[...slug]]/page.tsx` `/shop` or `/shop/:slug+` (optional)
225
127
 
226
128
  ## Middleware
227
129
 
228
- Runs at the Edge before every matched request. Use for auth, redirects, rewrites, headers.
130
+ Runs at the Edge before every matched request. Use for auth, redirects, rewrites.
229
131
 
230
132
  ```ts
231
133
  import { NextResponse, type NextRequest } from 'next/server';
232
-
233
- export function middleware(request: NextRequest) {
234
- const token = request.cookies.get('session')?.value;
235
- if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
236
- return NextResponse.redirect(new URL('/login', request.url));
237
- }
238
- // Add custom headers
239
- const response = NextResponse.next();
240
- response.headers.set('x-request-id', crypto.randomUUID());
241
- return response;
134
+ export function middleware(req: NextRequest) {
135
+ if (!req.cookies.get('session') && req.nextUrl.pathname.startsWith('/dashboard'))
136
+ return NextResponse.redirect(new URL('/login', req.url));
137
+ return NextResponse.next();
242
138
  }
243
-
244
- export const config = { matcher: ['/dashboard/:path*', '/settings/:path*'] };
139
+ export const config = { matcher: ['/dashboard/:path*'] };
245
140
  ```
246
141
 
247
- ## Configuration
248
-
249
- ### `next.config.ts`
250
-
251
- ```ts
252
- import type { NextConfig } from 'next';
253
-
254
- const nextConfig: NextConfig = {
255
- reactStrictMode: true,
256
- images: {
257
- remotePatterns: [
258
- { protocol: 'https', hostname: 'cdn.example.com' },
259
- ],
260
- },
261
- experimental: {
262
- ppr: true, // Partial Prerendering
263
- typedRoutes: true, // Type-safe <Link> hrefs
264
- },
265
- // Redirects, rewrites, headers
266
- async redirects() {
267
- return [{ source: '/old-path', destination: '/new-path', permanent: true }];
268
- },
269
- };
270
-
271
- export default nextConfig;
272
- ```
273
-
274
- ### Environment Variables
142
+ ## Environment Variables
275
143
 
276
144
  | Prefix | Available in | Use Case |
277
145
  |--------|-------------|----------|
278
146
  | `NEXT_PUBLIC_` | Server + Client | Public values (API base URLs, feature flags) |
279
147
  | No prefix | Server only | Secrets (DB URLs, API keys, tokens) |
280
148
 
281
- Files loaded (in priority order): `.env.local`, `.env.development` / `.env.production`, `.env`.
282
-
283
- ## Image and Font Optimization
284
-
285
- ### Images
286
-
287
- ```tsx
288
- import Image from 'next/image';
289
- import heroImg from '@/public/hero.jpg';
290
-
291
- // Local image — auto width/height from import
292
- <Image src={heroImg} alt="Hero" priority />
293
-
294
- // Remote image — must specify dimensions
295
- <Image src="https://cdn.example.com/photo.jpg" alt="Photo" width={800} height={600} />
296
- ```
297
-
298
- ### Fonts
299
-
300
- ```tsx
301
- // app/layout.tsx
302
- import { Inter } from 'next/font/google';
303
- const inter = Inter({ subsets: ['latin'], display: 'swap' });
304
-
305
- export default function RootLayout({ children }: { children: React.ReactNode }) {
306
- return <html lang="en" className={inter.className}><body>{children}</body></html>;
307
- }
308
- ```
309
-
310
- ## Metadata and SEO
311
-
312
- ```tsx
313
- // app/layout.tsx — static metadata
314
- import type { Metadata } from 'next';
315
-
316
- export const metadata: Metadata = {
317
- title: { default: 'My App', template: '%s | My App' },
318
- description: 'App description',
319
- openGraph: { title: 'My App', description: 'App description', type: 'website' },
320
- };
321
- ```
322
-
323
- ```tsx
324
- // app/blog/[slug]/page.tsx — dynamic metadata
325
- import type { Metadata } from 'next';
326
-
327
- export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
328
- const post = await getPost(params.slug);
329
- return { title: post.title, description: post.excerpt };
330
- }
331
- ```
332
-
333
- ## Performance Patterns
334
-
335
- - **Default to Server Components** — smaller client bundles, faster paint.
336
- - **`<Suspense>` + `loading.tsx`** — stream content progressively; never block the whole page.
337
- - **`Promise.all()`** — parallel data fetching for independent data.
338
- - **Dynamic imports** — lazy-load heavy Client Components with `next/dynamic`.
339
- - **`<Image>`** — automatic lazy loading, responsive sizing, format conversion.
340
- - **`next/font`** — zero layout shift, self-hosted fonts.
341
- - **Route segment config** — fine-tune caching and rendering per route.
342
-
343
- ## Deployment
344
-
345
- Next.js deploys to multiple targets:
346
-
347
- | Target | Config | Notes |
348
- |--------|--------|-------|
349
- | **Vercel** | Zero-config | Full feature support including Edge, ISR, Middleware |
350
- | **Node.js server** | `output: 'standalone'` | Minimal `standalone/` folder with server |
351
- | **Docker** | `output: 'standalone'` | Copy `.next/standalone` + `.next/static` + `public` |
352
- | **Static export** | `output: 'export'` | No server features (no SSR, API routes, middleware) |
149
+ Files (priority order): `.env.local`, `.env.development` / `.env.production`, `.env`.
353
150
 
354
- ## Component Practices & Naming
151
+ ## Image, Font, and Metadata
355
152
 
356
- - PascalCase for component files/exports. camelCase for hooks.
357
- - Shared components in `components/`. Route-specific components co-located in route folder.
358
- - TypeScript interfaces for props. Explicit types and defaults.
359
- - Co-locate tests with components.
360
- - Folders: `kebab-case`. Types/Interfaces: `PascalCase`. Constants: `UPPER_SNAKE_CASE`.
153
+ - **Images**: Use `<Image>` from `next/image` — auto lazy loading, responsive sizing, format conversion. Configure `images.remotePatterns` in `next.config.ts` for remote URLs.
154
+ - **Fonts**: Use `next/font/google` zero layout shift, self-hosted, no external network request.
155
+ - **Metadata**: Export `metadata` (static) or `generateMetadata` (dynamic) per page/layout for SEO and social previews.
361
156
 
362
157
  ## Anti-Patterns
363
158
 
364
159
  | Anti-Pattern | Why It's Wrong | Do This Instead |
365
160
  |-------------|---------------|-----------------|
366
- | `'use client'` on every component | Bloats JS bundle, defeats RSC benefits | Default to Server Components; add `'use client'` only when needed |
367
- | Sequential `await` for independent data | Creates a waterfall, slows page load | Use `Promise.all()` for parallel fetches |
368
- | `next/dynamic` with `ssr: false` in Server Components | Build/runtime crash | Extract to a Client Component, import normally |
369
- | Fetching in `useEffect` when server fetch works | Extra client roundtrip, loading flash | Fetch in the Server Component or use Server Actions |
370
- | Giant `layout.tsx` with all providers | Hard to test, couples unrelated concerns | Split providers into a `Providers` Client Component |
371
- | Catching errors without `error.tsx` | Unhandled errors crash the page | Add `error.tsx` per route segment |
372
- | Hardcoding secrets in source files | Security risk, leaks in version control | Use `.env.local` and `process.env` |
161
+ | `'use client'` on every component | Bloats JS bundle, defeats RSC benefits | Default to Server Components |
162
+ | Sequential `await` for independent data | Waterfall, slows page load | Use `Promise.all()` |
163
+ | `next/dynamic` with `ssr: false` in Server Components | Build/runtime crash | Extract to Client Component |
164
+ | Fetching in `useEffect` when server fetch works | Extra roundtrip, loading flash | Fetch in Server Component or Server Actions |
165
+ | Giant `layout.tsx` with all providers | Hard to test, couples concerns | Split into a `Providers` Client Component |
166
+ | No `error.tsx` per segment | Unhandled errors crash the page | Add `error.tsx` per route segment |
167
+ | Hardcoding secrets in source | Security risk, version control leak | Use `.env.local` and `process.env` |
373
168
  | Skipping `loading.tsx` / `<Suspense>` | Blank screen while data loads | Add `loading.tsx` or wrap in `<Suspense>` |
374
- | Using `getServerSideProps` / `getStaticProps` | Legacy Pages Router patterns | Use App Router with `async` Server Components |
375
- | Ignoring `next.config.ts` for images | Remote images blocked by default | Configure `images.remotePatterns` |
169
+ | `getServerSideProps` / `getStaticProps` | Legacy Pages Router | Use App Router with async Server Components |
376
170
  | Missing `metadata` exports | Poor SEO, no social previews | Export `metadata` or `generateMetadata` per page |
@@ -0,0 +1,205 @@
1
+ ---
2
+ name: notion-knowledge-management
3
+ description: "Notion workspace patterns for knowledge capture, research documentation, architectural decisions, and spec management. Use when capturing research findings, writing specs, documenting decisions, or managing a team knowledge base."
4
+ ---
5
+
6
+ <!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .opencastle/ directory instead. -->
7
+
8
+ # Knowledge Management with Notion
9
+
10
+ Conventions for working with the team's Notion workspace via the official Notion MCP server. Covers page and database operations, research capture, decision documentation, and permission-aware workflows.
11
+
12
+ ## MCP Server
13
+
14
+ | Field | Value |
15
+ |-------|-------|
16
+ | **Endpoint** | `https://mcp.notion.com/mcp` (HTTP, remote) |
17
+ | **Auth** | OAuth — users authenticate via Notion account when the MCP connection is established |
18
+ | **Type** | HTTP MCP (no local process to spawn) |
19
+
20
+ ### Authentication
21
+
22
+ The Notion MCP server uses OAuth. When the MCP connection is first opened in your IDE, you will be prompted to authorise access to your Notion workspace. No API key or token is required in `.env`.
23
+
24
+ > **Scope:** The integration is granted access only to pages and databases explicitly shared with it. Before using MCP tools, ensure the relevant pages/databases are shared with the OpenCastle integration in Notion.
25
+
26
+ ## Available MCP Tools
27
+
28
+ | Tool | Description |
29
+ |------|-------------|
30
+ | `search` | Search pages and databases across the workspace by keyword |
31
+ | `create_page` | Create a new page (standalone or inside a parent page/database) |
32
+ | `update_page` | Update page properties or archive a page |
33
+ | `append_block_children` | Append content blocks (paragraphs, headings, bullets, code) to a page |
34
+ | `query_database` | Query a Notion database with filters and sorts |
35
+
36
+ ## Working with Pages and Databases
37
+
38
+ ### Page Hierarchy Hygiene
39
+
40
+ Keep the workspace navigable by placing new pages in the right location:
41
+
42
+ ```
43
+ Workspace root
44
+ ├── Engineering/
45
+ │ ├── Architecture Decisions/ ← ADRs go here
46
+ │ ├── Specs/ ← Feature specs go here
47
+ │ └── Research/ ← Research notes go here
48
+ ├── Team/
49
+ │ ├── Meeting Notes/ ← Meeting intelligence
50
+ │ └── Decisions Log/ ← Key team decisions
51
+ └── Project: <name>/ ← Per-project space
52
+ ├── Roadmap
53
+ ├── Known Issues
54
+ └── Release Notes/
55
+ ```
56
+
57
+ - Always use `search` first to check if a page already exists before creating a new one.
58
+ - Create pages as children of the appropriate parent — never at the workspace root unless explicitly requested.
59
+ - Use databases (not flat pages) for collections that need filtering, sorting, or status tracking (e.g., ADRs, specs).
60
+
61
+ ### Page Creation Pattern
62
+
63
+ When creating a page:
64
+
65
+ 1. `search` for an existing page with a similar title to avoid duplicates
66
+ 2. `create_page` with `parent` set to the correct parent page or database
67
+ 3. `append_block_children` to add structured content
68
+
69
+ ```json
70
+ // Example: Create a spec page
71
+ {
72
+ "parent": { "page_id": "<Engineering/Specs parent ID>" },
73
+ "properties": {
74
+ "title": [{ "type": "text", "text": { "content": "[Spec] Price Range Filter" } }]
75
+ }
76
+ }
77
+ ```
78
+
79
+ ## Capturing Research and Decisions
80
+
81
+ ### Research Note Structure
82
+
83
+ Use this outline when capturing research findings:
84
+
85
+ ```
86
+ # [Research] <Topic>
87
+
88
+ ## Summary
89
+ One-paragraph overview of findings.
90
+
91
+ ## Sources
92
+ - <URL or reference> — <why it is relevant>
93
+
94
+ ## Key Findings
95
+ - Finding 1
96
+ - Finding 2
97
+
98
+ ## Implications
99
+ How these findings affect the current task or architecture.
100
+
101
+ ## Open Questions
102
+ - Question 1
103
+ ```
104
+
105
+ ### Architectural Decision Record (ADR) Structure
106
+
107
+ ```
108
+ # ADR-NNN: <Short title>
109
+
110
+ **Status:** Proposed | Accepted | Deprecated | Superseded
111
+ **Date:** YYYY-MM-DD
112
+
113
+ ## Context
114
+ What is the problem or decision to be made?
115
+
116
+ ## Decision
117
+ What was decided?
118
+
119
+ ## Consequences
120
+ What tradeoffs or follow-on work does this create?
121
+
122
+ ## Alternatives Considered
123
+ - Option A — why rejected
124
+ - Option B — why rejected
125
+ ```
126
+
127
+ ### Spec-to-Implementation Link
128
+
129
+ When a spec page drives implementation, add an **Implementation** section at the bottom:
130
+
131
+ ```
132
+ ## Implementation
133
+ - **Branch:** `feat/price-range-filter`
134
+ - **PR:** <link>
135
+ - **Tracker:** <Linear/Jira/Trello card link>
136
+ - **Status:** In Progress / Done
137
+ ```
138
+
139
+ This closes the loop between the knowledge base and the task tracker.
140
+
141
+ ## Meeting Intelligence
142
+
143
+ When capturing meeting notes, use the following structure:
144
+
145
+ ```
146
+ # Meeting: <Title> — YYYY-MM-DD
147
+
148
+ **Attendees:** Name1, Name2
149
+ **Type:** Planning / Review / Retrospective / Decision
150
+
151
+ ## Summary
152
+
153
+ ## Decisions Made
154
+ - Decision 1 (owner: Name)
155
+
156
+ ## Action Items
157
+ - [ ] Action item (owner: Name, due: YYYY-MM-DD)
158
+
159
+ ## Context / Discussion Notes
160
+ ```
161
+
162
+ After the meeting, add action items to the task tracker.
163
+
164
+ ## Permission-Aware Workflows
165
+
166
+ Notion access is page-scoped. Follow these rules to avoid permission errors:
167
+
168
+ 1. **Before writing** — run `search` to verify you can see the target page. If it does not appear, the integration has not been granted access.
169
+ 2. **Sharing** — ask the user to share the relevant page or database with the OpenCastle integration before running MCP tools against it.
170
+ 3. **Databases vs pages** — use `query_database` only on pages that are databases. Use `append_block_children` to add content to regular pages.
171
+ 4. **Archived pages** — `search` does not return archived pages. If a page is missing, it may have been archived. Ask the user to restore it.
172
+
173
+ ## Database Query Patterns
174
+
175
+ ### Filter by Status
176
+
177
+ ```json
178
+ {
179
+ "filter": {
180
+ "property": "Status",
181
+ "select": { "equals": "In Progress" }
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### Sort by Last Edited
187
+
188
+ ```json
189
+ {
190
+ "sorts": [
191
+ { "timestamp": "last_edited_time", "direction": "descending" }
192
+ ]
193
+ }
194
+ ```
195
+
196
+ ## Agent Usage Guidelines
197
+
198
+ | Agent | Primary Use |
199
+ |-------|-------------|
200
+ | **Team Lead** | Create spec pages, capture decisions, link tracker issues to specs |
201
+ | **Researcher** | Capture research notes, query databases for prior art, document findings |
202
+ | **Documentation Writer** | Write and update documentation pages, maintain page hierarchy |
203
+ | **Architect** | Write ADRs, create technical specs, link specs to implementation PRs |
204
+
205
+ **Never** delete pages via MCP — use `update_page` with `archived: true` if a page needs to be removed, and confirm with the user first.
@@ -0,0 +1,47 @@
1
+ import type { PluginConfig } from '../types.js';
2
+
3
+ export const config: PluginConfig = {
4
+ id: 'notion',
5
+ name: 'Notion',
6
+ category: 'team',
7
+ subCategory: 'knowledge-management',
8
+ label: 'Notion',
9
+ hint: 'Workspace knowledge base and documentation hub',
10
+ skillName: 'notion-knowledge-management',
11
+ mcpServerKey: 'Notion',
12
+ mcpConfig: {
13
+ type: 'http',
14
+ url: 'https://mcp.notion.com/mcp',
15
+ },
16
+ authType: 'oauth',
17
+ envVars: [],
18
+ agentToolMap: {
19
+ 'team-lead': [
20
+ 'Notion/search',
21
+ 'Notion/create_page',
22
+ 'Notion/update_page',
23
+ 'Notion/query_database',
24
+ 'Notion/append_block_children',
25
+ ],
26
+ 'researcher': [
27
+ 'Notion/search',
28
+ 'Notion/create_page',
29
+ 'Notion/append_block_children',
30
+ 'Notion/query_database',
31
+ ],
32
+ 'documentation-writer': [
33
+ 'Notion/search',
34
+ 'Notion/create_page',
35
+ 'Notion/update_page',
36
+ 'Notion/append_block_children',
37
+ ],
38
+ 'architect': [
39
+ 'Notion/search',
40
+ 'Notion/create_page',
41
+ 'Notion/update_page',
42
+ 'Notion/query_database',
43
+ ],
44
+ },
45
+ docsUrl: 'https://www.opencastle.dev/docs/plugins#notion',
46
+ officialDocs: 'https://developers.notion.com/docs/mcp',
47
+ };