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.
- package/README.md +13 -3
- package/bin/cli.mjs +2 -0
- package/dist/cli/bootstrap.js +1 -1
- package/dist/cli/bootstrap.js.map +1 -1
- package/dist/cli/bootstrap.test.js +16 -0
- package/dist/cli/bootstrap.test.js.map +1 -1
- package/dist/cli/init.test.js +38 -0
- package/dist/cli/init.test.js.map +1 -1
- package/dist/cli/stack-config-update.test.js +18 -0
- package/dist/cli/stack-config-update.test.js.map +1 -1
- package/dist/cli/stack-config.d.ts.map +1 -1
- package/dist/cli/stack-config.js +1 -0
- 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/orchestrator/plugins/index.d.ts.map +1 -1
- package/dist/orchestrator/plugins/index.js +4 -0
- package/dist/orchestrator/plugins/index.js.map +1 -1
- package/dist/orchestrator/plugins/notion/config.d.ts +3 -0
- package/dist/orchestrator/plugins/notion/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/notion/config.js +46 -0
- package/dist/orchestrator/plugins/notion/config.js.map +1 -0
- package/dist/orchestrator/plugins/trello/config.d.ts +3 -0
- package/dist/orchestrator/plugins/trello/config.d.ts.map +1 -0
- package/dist/orchestrator/plugins/trello/config.js +43 -0
- package/dist/orchestrator/plugins/trello/config.js.map +1 -0
- package/dist/orchestrator/plugins/types.d.ts +1 -1
- package/dist/orchestrator/plugins/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/bootstrap.test.ts +21 -0
- package/src/cli/bootstrap.ts +1 -1
- package/src/cli/init.test.ts +46 -0
- package/src/cli/stack-config-update.test.ts +20 -0
- package/src/cli/stack-config.ts +1 -0
- package/src/cli/types.ts +1 -1
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/orchestrator/agents/api-designer.agent.md +25 -34
- package/src/orchestrator/agents/architect.agent.md +40 -84
- package/src/orchestrator/agents/content-engineer.agent.md +29 -31
- package/src/orchestrator/agents/copywriter.agent.md +35 -60
- package/src/orchestrator/agents/data-expert.agent.md +24 -30
- package/src/orchestrator/agents/database-engineer.agent.md +26 -31
- package/src/orchestrator/agents/developer.agent.md +32 -34
- package/src/orchestrator/agents/devops-expert.agent.md +31 -26
- package/src/orchestrator/agents/documentation-writer.agent.md +29 -29
- package/src/orchestrator/agents/performance-expert.agent.md +36 -33
- package/src/orchestrator/agents/release-manager.agent.md +25 -34
- package/src/orchestrator/agents/researcher.agent.md +41 -95
- package/src/orchestrator/agents/reviewer.agent.md +24 -34
- package/src/orchestrator/agents/security-expert.agent.md +35 -39
- package/src/orchestrator/agents/seo-specialist.agent.md +25 -32
- package/src/orchestrator/agents/session-guard.agent.md +20 -79
- package/src/orchestrator/agents/team-lead.agent.md +50 -254
- package/src/orchestrator/agents/testing-expert.agent.md +37 -49
- package/src/orchestrator/agents/ui-ux-expert.agent.md +33 -39
- package/src/orchestrator/customizations/KNOWN-ISSUES.md +0 -1
- package/src/orchestrator/customizations/agents/skill-matrix.json +20 -4
- package/src/orchestrator/customizations/agents/skill-matrix.md +20 -0
- package/src/orchestrator/instructions/general.instructions.md +24 -84
- package/src/orchestrator/plugins/astro/SKILL.md +23 -179
- package/src/orchestrator/plugins/convex/SKILL.md +38 -12
- package/src/orchestrator/plugins/index.ts +4 -0
- package/src/orchestrator/plugins/netlify/SKILL.md +17 -13
- package/src/orchestrator/plugins/nextjs/SKILL.md +55 -261
- package/src/orchestrator/plugins/notion/SKILL.md +205 -0
- package/src/orchestrator/plugins/notion/config.ts +47 -0
- package/src/orchestrator/plugins/nx/SKILL.md +20 -72
- package/src/orchestrator/plugins/playwright/SKILL.md +5 -17
- package/src/orchestrator/plugins/slack/SKILL.md +28 -190
- package/src/orchestrator/plugins/teams/SKILL.md +10 -140
- package/src/orchestrator/plugins/trello/SKILL.md +151 -0
- package/src/orchestrator/plugins/trello/config.ts +44 -0
- package/src/orchestrator/plugins/types.ts +1 -1
- package/src/orchestrator/plugins/vitest/SKILL.md +2 -2
- package/src/orchestrator/prompts/bug-fix.prompt.md +25 -63
- package/src/orchestrator/prompts/implement-feature.prompt.md +29 -66
- package/src/orchestrator/prompts/quick-refinement.prompt.md +31 -66
- package/src/orchestrator/skills/accessibility-standards/SKILL.md +50 -105
- package/src/orchestrator/skills/agent-hooks/SKILL.md +60 -110
- package/src/orchestrator/skills/agent-memory/SKILL.md +44 -93
- package/src/orchestrator/skills/api-patterns/SKILL.md +20 -68
- package/src/orchestrator/skills/code-commenting/SKILL.md +49 -101
- package/src/orchestrator/skills/context-map/SKILL.md +47 -88
- package/src/orchestrator/skills/data-engineering/SKILL.md +27 -74
- package/src/orchestrator/skills/decomposition/SKILL.md +50 -98
- package/src/orchestrator/skills/deployment-infrastructure/SKILL.md +44 -107
- package/src/orchestrator/skills/documentation-standards/SKILL.md +28 -89
- package/src/orchestrator/skills/fast-review/SKILL.md +51 -276
- package/src/orchestrator/skills/frontend-design/SKILL.md +53 -163
- package/src/orchestrator/skills/git-workflow/SKILL.md +18 -54
- package/src/orchestrator/skills/memory-merger/SKILL.md +51 -88
- package/src/orchestrator/skills/observability-logging/SKILL.md +29 -75
- package/src/orchestrator/skills/orchestration-protocols/SKILL.md +58 -117
- package/src/orchestrator/skills/panel-majority-vote/SKILL.md +65 -140
- package/src/orchestrator/skills/performance-optimization/SKILL.md +21 -85
- package/src/orchestrator/skills/project-consistency/SKILL.md +62 -281
- package/src/orchestrator/skills/react-development/SKILL.md +38 -86
- package/src/orchestrator/skills/security-hardening/SKILL.md +40 -84
- package/src/orchestrator/skills/self-improvement/SKILL.md +26 -60
- package/src/orchestrator/skills/seo-patterns/SKILL.md +40 -105
- package/src/orchestrator/skills/session-checkpoints/SKILL.md +26 -68
- package/src/orchestrator/skills/team-lead-reference/SKILL.md +66 -206
- package/src/orchestrator/skills/testing-workflow/SKILL.md +42 -112
- package/src/orchestrator/skills/validation-gates/SKILL.md +39 -170
- package/src/orchestrator/snippets/base-output-contract.md +14 -0
- package/src/orchestrator/snippets/discovered-issues-policy.md +15 -0
- package/src/orchestrator/snippets/logging-mandatory.md +11 -0
- 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
|
-
│ │
|
|
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
|
|
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
|
-
|
|
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
|
-
| **
|
|
50
|
-
| **
|
|
51
|
-
| **
|
|
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
|
-
| **
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
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.**
|
|
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
|
|
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
|
|
98
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
129
|
-
|
|
130
|
-
Initiate independent fetches simultaneously — never `await` sequentially.
|
|
90
|
+
### Parallel Fetching
|
|
131
91
|
|
|
132
92
|
```tsx
|
|
133
|
-
|
|
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
|
|
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 |
|
|
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
|
-
|
|
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
|
|
170
|
-
| `layout.tsx` | Shared layout (
|
|
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
|
|
117
|
+
| `error.tsx` | Error UI (Client Component) |
|
|
174
118
|
| `not-found.tsx` | 404 UI |
|
|
175
|
-
| `route.ts` | API endpoint
|
|
119
|
+
| `route.ts` | API endpoint |
|
|
176
120
|
| `default.tsx` | Fallback for parallel routes |
|
|
177
121
|
|
|
178
122
|
### Dynamic Routes
|
|
179
123
|
|
|
180
|
-
|
|
181
|
-
app/
|
|
182
|
-
app/shop/[...slug]/page.tsx
|
|
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
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
##
|
|
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
|
|
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
|
-
##
|
|
151
|
+
## Image, Font, and Metadata
|
|
355
152
|
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
-
|
|
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
|
|
367
|
-
| Sequential `await` for independent data |
|
|
368
|
-
| `next/dynamic` with `ssr: false` in Server Components | Build/runtime crash | Extract to
|
|
369
|
-
| Fetching in `useEffect` when server fetch works | Extra
|
|
370
|
-
| Giant `layout.tsx` with all providers | Hard to test, couples
|
|
371
|
-
|
|
|
372
|
-
| Hardcoding secrets in source
|
|
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
|
-
|
|
|
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
|
+
};
|