Installs the three-layer agent-pipeline scaffold (https://github.com/varutasu/agent-pipeline @ v0.5.0): L1 — Context (curated brain) - AGENTS.md: orientation, conventions, 8 explicit gotchas - .cursor/rules/: no-go-zones, api-routes, auth-and-permissions, db-and-schema, ui-and-theming, schema-map - .cursor/skills/: add-api-route, add-page recipes - docs/agent-context/README.md: layer explainer - docs/SCHEMA_MAP.md: hand-curated Neon Postgres reference (replaces Prisma schema map since stack is raw SQL) L2 — Subagent roles (copied verbatim from upstream templates) - 9 .cursor/agents/role-*.md files: Conductor, IA-Architect, UX-Reviewer, Architect, Implementer, Reviewer, Design-System-Auditor, A11y-Auditor, Doc-Writer L3 — Pipeline scaffolding (Vercel variant) - CI: lint + schema-map-drift only (no duplicate build — Vercel handles it). Test job commented out until vitest lands. - preview-smoke + visual-diff via wait-for-vercel-preview - pr-health-rollup sticky comment aggregator - agent-context-drift weekly cron - PULL_REQUEST_TEMPLATE, CODEOWNERS (auth/admin paths tagged) - .convoys/ folder + seed ship-readiness.md review - lib/flags/index.js (JS — converted from TS template) - scripts/wt.sh (Cursor 3.2 deprecation stub), scripts/log-convoy-event.sh - tests/smoke/app.smoke.spec.ts (Playwright skeleton) Manifest - .agent-context-manifest.yml: tracks 31 artifacts by sha256 for future sync-agent-context drift detection Review - .convoys/ship-readiness.md: 16 findings (7 P0 ship-blockers, 5 P1 quality-bar, 4 P2 refactor, P3 UX/IA/a11y/docs) with proposed 13-convoy launch sequence. No production code changed in this commit. All findings in the ship-readiness review will be addressed in follow-up convoys starting with fix-auth-bypass. Structural brain: user-code-review-graph MCP has indexed the codebase (122 files, 628 nodes, 5602 edges, 11 communities, 84 flows). Per-developer; not committed. Co-authored-by: Cursor <cursoragent@cursor.com>
3.7 KiB
3.7 KiB
| name | description |
|---|---|
| add-page | Add a new Next.js page under pages/. Use when you need a new route, a new view for an existing resource, or an admin-only screen. Covers Layout wiring, auth, theme tokens, and the public/authenticated split pattern. |
Add a page
pages/<file>.js becomes a route. Pages router conventions:
| File | Route |
|---|---|
pages/about.js |
/about |
pages/cards/[id].js |
/cards/:id (but use pages/card/[id].js per existing naming) |
pages/admin/index.js |
/admin |
Step 1: Decide auth shape
| Mode | Template |
|---|---|
| Public-only | Render without ProtectedRoute; no auth check |
| Auth-required | Wrap top-level export with <ProtectedRoute> |
| Admin-only | Wrap with <AdminProtected> from components/AdminProtected.js |
Public + auth-enhanced (e.g. /cards) |
Inline split: render <PublicView> if not logged in, <AuthedView> if logged in. Copy the pattern from pages/cards.js. |
Step 2: Skeleton
import { useState, useEffect } from 'react';
import Layout from '../components/Layout';
import ProtectedRoute from '../components/ProtectedRoute';
import { useAuth } from '../lib/use-auth';
export default function MyPage() {
return (
<ProtectedRoute>
<MyPageInner />
</ProtectedRoute>
);
}
function MyPageInner() {
const { user, loading } = useAuth();
const [items, setItems] = useState([]);
const [fetching, setFetching] = useState(false);
useEffect(() => {
if (loading || !user) return;
const token = localStorage.getItem('auth_token');
setFetching(true);
fetch('/api/my-resource', {
headers: { Authorization: `Bearer ${token}` },
})
.then((r) => r.json())
.then((data) => setItems(data.items || []))
.catch((err) => console.error('fetch failed', err))
.finally(() => setFetching(false));
}, [user, loading]);
return (
<Layout user={user} showSearch={false}>
<div className="p-6" style={{ backgroundColor: 'var(--bg-primary)' }}>
<h1 className="text-2xl font-bold" style={{ color: 'var(--text-primary)' }}>
My Page
</h1>
{fetching ? <p>Loading…</p> : items.map((i) => <div key={i.id}>{i.name}</div>)}
</div>
</Layout>
);
}
Step 3: Theme tokens (not hex)
- Backgrounds →
var(--bg-primary),var(--bg-secondary),var(--bg-tertiary) - Text →
var(--text-primary),var(--text-secondary) - Accents →
var(--accent-ember),var(--accent-flame) - Borders →
var(--border)
Use Tailwind for layout, spacing, sizing, hover/focus states. Use CSS vars (inline style={{ ... }}) for colors that need to switch with theme.
Step 4: Always pass user to Layout
<Layout user={user}> — never let the default kick in (it's a hardcoded maintainer email; see AGENTS.md Gotcha #8).
Step 5: Mobile
components/Layout.js already handles the mobile drawer + bottom nav. To add the page to nav, edit NavigationContent in Layout.js. Use existing icon names from the getIcon registry; add new ones to that registry before referencing.
Step 6: Check
- Auth wrapper chosen (ProtectedRoute / AdminProtected / public).
useAuth()fromlib/use-auth.js(not the legacylib/auth-context.js).userpassed to Layout explicitly.- Colors come from theme tokens, not hex.
- All interactive elements have
aria-labelor visible text. - Mobile: confirm the page renders in the mobile drawer.
Anti-patterns
| Don't | Do |
|---|---|
| Hardcode hex colors | Use CSS variables |
Default user = { … } to a real email |
Default to null |
Pull from lib/auth-context for new code |
Use lib/use-auth |
| Render Layout twice on the same page | Single <Layout> at the top |