Skip to content

Commit a9eb4fa

Browse files
committed
Add rules/ at root for Open Plugins / cursor.directory compatibility
1 parent d192d00 commit a9eb4fa

22 files changed

Lines changed: 1160 additions & 0 deletions

rules/ai-collaboration.mdc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
description: Instructions for AI agent code generation workflow
3+
globs: ["*"]
4+
---
5+
6+
# AI Collaboration Guidelines
7+
8+
The 3-stage agentic loop:
9+
10+
1. **PLAN:**
11+
"Analyze the current codebase and plan how to implement [feature]. List the files you'll modify and why. Don't write code yet."
12+
13+
2. **IMPLEMENT:**
14+
"Implement the plan. Follow all `.cursor/rules/` guidelines. If you're unsure about auth or RLS, check `supabase-auth-security.mdc`."
15+
16+
3. **REVIEW:**
17+
"Review what you just generated. Check for:
18+
- `getSession()` usage (MUST be `getUser()`)
19+
- Missing error boundaries or `loading.tsx`
20+
- Next.js 15 async layout/page params
21+
- TypeScript `any` types (use `unknown` or define interface)
22+
Report any issues before I run it."
23+
24+
Constraints:
25+
- NEVER give the AI a task longer than 200 words.
26+
- NEVER let the AI implement more than 3-4 files in one turn.
27+
- ALWAYS review the Git diff before committing.

rules/api-design.mdc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
description: API route handler design patterns
3+
globs: ["**/app/api/**", "**/route.ts"]
4+
---
5+
6+
# API Design Patterns
7+
8+
Standard response format for Route Handlers:
9+
Success: `{ data: T, error: null }`
10+
Error: `{ data: null, error: { code: string, message: string } }`
11+
12+
HTTP status codes:
13+
- 200: Success (GET, PATCH)
14+
- 201: Created (POST)
15+
- 204: No content (DELETE)
16+
- 400: Validation error
17+
- 401: Unauthenticated
18+
- 403: Unauthorized
19+
- 404: Not found
20+
- 500: Server error
21+
22+
Route handler template:
23+
```typescript
24+
import { NextResponse } from 'next/server'
25+
import { createServerClient } from '@supabase/ssr'
26+
27+
export async function GET(request: Request, { params }: { params: Promise<{ id: string }> }) {
28+
const { id } = await params
29+
const supabase = createServerClient(...)
30+
31+
const { data: { user } } = await supabase.auth.getUser()
32+
if (!user) {
33+
return NextResponse.json(
34+
{ data: null, error: { code: 'UNAUTHORIZED', message: 'Login required' } },
35+
{ status: 401 }
36+
)
37+
}
38+
39+
// ... fetch data
40+
return NextResponse.json({ data: { id, status: 'ok' }, error: null })
41+
}
42+
```

rules/caching-revalidation.mdc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
description: Next.js caching and revalidation best practices
3+
globs: ["**/app/**", "**/lib/**"]
4+
alwaysApply: false
5+
---
6+
7+
# Caching & Revalidation
8+
9+
## Default Behavior
10+
Next.js 15 has specific caching defaults. AI models often get these wrong.
11+
12+
## Rules
13+
14+
### Server Components - Data Fetching
15+
For dynamic data that changes frequently:
16+
```typescript
17+
// Force fresh data on every request
18+
const data = await fetch(url, { cache: 'no-store' })
19+
20+
// Or revalidate every 60 seconds:
21+
const data = await fetch(url, { next: { revalidate: 60 } })
22+
```
23+
24+
### Route Segment Config
25+
For pages that must always show fresh data:
26+
```typescript
27+
// At the top of page.tsx or layout.tsx:
28+
export const dynamic = 'force-dynamic' // Never cache this page
29+
export const revalidate = 0 // Same as above
30+
```
31+
32+
For static pages that rarely change:
33+
```typescript
34+
export const revalidate = 3600 // Revalidate every hour
35+
```
36+
37+
### Server Actions — Always Revalidate
38+
After mutations, ALWAYS revalidate affected paths:
39+
```typescript
40+
'use server'
41+
import { revalidatePath } from 'next/cache'
42+
43+
export async function createPost(formData: FormData) {
44+
// ... create post in database
45+
revalidatePath('/posts') // ← NEVER forget this
46+
revalidatePath('/dashboard') // ← Revalidate all affected pages
47+
}
48+
```
49+
50+
### Anti-Patterns
51+
- NEVER assume `fetch()` will always return fresh data — check your cache settings
52+
- NEVER forget `revalidatePath()` after a mutation in a Server Action
53+
- NEVER use `cache: 'force-cache'` for user-specific data
54+
- NEVER rely on client-side state to show "updated" data after a mutation — revalidate the server cache
55+
- ALWAYS use `revalidateTag()` for fine-grained cache invalidation when possible

rules/database-design.mdc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
description: Database schema design patterns for Supabase with proper types and relationships
3+
globs: ["**/*.sql", "**/supabase/**", "**/types/**"]
4+
alwaysApply: false
5+
---
6+
7+
# Database Design Patterns
8+
9+
## Table Naming
10+
- Use `snake_case` for tables and columns: `user_profiles`, `created_at`
11+
- NEVER use camelCase in SQL: `userId` → WRONG, `user_id` → CORRECT
12+
- Use plural table names: `profiles`, `posts`, `comments`
13+
14+
## Required Columns (Every Table)
15+
```sql
16+
CREATE TABLE public.example (
17+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
18+
-- your columns here
19+
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
20+
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
21+
);
22+
```
23+
ALWAYS include `id`, `created_at`, and `updated_at`.
24+
ALWAYS use UUID for primary keys (not serial/integer).
25+
ALWAYS use TIMESTAMPTZ (not TIMESTAMP) for timezone safety.
26+
27+
## Foreign Key Pattern
28+
```sql
29+
-- Always reference auth.users for user ownership
30+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL
31+
```
32+
Use `ON DELETE CASCADE` for user-owned data.
33+
Use `ON DELETE SET NULL` for optional relationships.
34+
35+
## RLS Template (Copy This Every Time)
36+
```sql
37+
ALTER TABLE public.example ENABLE ROW LEVEL SECURITY;
38+
39+
-- Users can only see their own data
40+
CREATE POLICY "Users own data" ON public.example
41+
FOR ALL USING (auth.uid() = user_id);
42+
43+
-- Or for public read + owner write:
44+
CREATE POLICY "Public read" ON public.example
45+
FOR SELECT USING (true);
46+
CREATE POLICY "Owner write" ON public.example
47+
FOR INSERT WITH CHECK (auth.uid() = user_id);
48+
CREATE POLICY "Owner update" ON public.example
49+
FOR UPDATE USING (auth.uid() = user_id);
50+
CREATE POLICY "Owner delete" ON public.example
51+
FOR DELETE USING (auth.uid() = user_id);
52+
```
53+
54+
## Type Generation
55+
After schema changes, regenerate types:
56+
```bash
57+
npx supabase gen types typescript --project-id YOUR_PROJECT_REF > src/types/database.ts
58+
```
59+
ALWAYS use generated types — NEVER manually type database schemas.
60+
61+
## Anti-Patterns
62+
- NEVER use `TEXT` for fields that should be enums — use PostgreSQL enums or CHECK constraints
63+
- NEVER store JSON blobs when structured columns work — use `JSONB` only for truly dynamic data
64+
- NEVER create tables without RLS — see `supabase-rls.mdc`
65+
- NEVER use `SERIAL` for IDs — use `UUID` for security (prevents enumeration attacks)

rules/env-management.mdc

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
description: Environment variable management and secrets handling
3+
globs: ["**/*.ts", "**/*.tsx", "**/.env*"]
4+
alwaysApply: false
5+
---
6+
7+
# Environment Variable Management
8+
9+
## Classification Rules
10+
| Prefix | Visibility | Use For |
11+
|--------|-----------|---------|
12+
| `NEXT_PUBLIC_` | Client + Server | Non-sensitive public values (Supabase URL, Stripe publishable key) |
13+
| No prefix | Server Only | ALL secrets (API keys, webhook secrets, database URLs) |
14+
15+
## NEVER Expose These to the Client
16+
```
17+
STRIPE_SECRET_KEY → Server only (NEVER NEXT_PUBLIC_)
18+
STRIPE_WEBHOOK_SECRET → Server only
19+
SUPABASE_SERVICE_ROLE_KEY → Server only (admin bypass key)
20+
RESEND_API_KEY → Server only
21+
DATABASE_URL → Server only
22+
```
23+
24+
## Safe for Client
25+
```
26+
NEXT_PUBLIC_SUPABASE_URL → Public Supabase endpoint
27+
NEXT_PUBLIC_SUPABASE_ANON_KEY → Rate-limited client key (RLS protects data)
28+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY → Stripe public key (payment form only)
29+
NEXT_PUBLIC_APP_URL → Your app's URL
30+
```
31+
32+
## Validation Pattern
33+
Use `src/lib/env.ts` to validate at startup:
34+
```typescript
35+
function getEnvVar(key: string, required = true): string {
36+
const value = process.env[key]
37+
if (!value && required) {
38+
throw new Error(`❌ Missing required env var: ${key}`)
39+
}
40+
return value ?? ''
41+
}
42+
```
43+
44+
## .env.local Structure
45+
```bash
46+
# Supabase (required)
47+
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
48+
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
49+
50+
# Stripe (optional in dev)
51+
STRIPE_SECRET_KEY=sk_test_...
52+
STRIPE_WEBHOOK_SECRET=whsec_...
53+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
54+
55+
# Email (optional in dev)
56+
RESEND_API_KEY=re_...
57+
58+
# App
59+
NEXT_PUBLIC_APP_URL=http://localhost:3000
60+
```
61+
62+
## Anti-Patterns
63+
- NEVER commit `.env.local` — it's in `.gitignore`
64+
- NEVER use `process.env.X` directly in components — use the centralized `env` config
65+
- NEVER hardcode API keys or secrets in source code
66+
- NEVER use `NEXT_PUBLIC_` for write-access API keys

rules/error-handling.mdc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
description: Error boundaries and loading states guidelines
3+
globs: ["**/app/**"]
4+
---
5+
6+
# Error Handling and Loading States
7+
8+
EVERY page in `app/` router MUST have sibling files if it does data fetching:
9+
- `loading.tsx` (Suspense boundary)
10+
- `error.tsx` (Error boundary — must be `'use client'`)
11+
12+
`loading.tsx` template:
13+
```tsx
14+
export default function Loading() {
15+
return <div className="animate-pulse">Loading...</div>
16+
}
17+
```
18+
19+
`error.tsx` template:
20+
```tsx
21+
'use client'
22+
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
23+
return (
24+
<div>
25+
<h2>Something went wrong</h2>
26+
<button onClick={reset}>Try again</button>
27+
</div>
28+
)
29+
}
30+
```
31+
32+
Server Action error pattern:
33+
```typescript
34+
export async function createItem(formData: FormData) {
35+
try {
36+
// ... operation
37+
return { success: true }
38+
} catch (error) {
39+
return { error: 'Failed to create item' }
40+
}
41+
}
42+
```

rules/file-naming.mdc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
description: File naming conventions and project organization rules
3+
globs: ["**/*.ts", "**/*.tsx"]
4+
alwaysApply: false
5+
---
6+
7+
# File Naming & Organization
8+
9+
## Naming Conventions
10+
- **Components:** PascalCase → `UserProfile.tsx`, `DashboardLayout.tsx`
11+
- **Utilities/Libs:** camelCase → `formatDate.ts`, `createClient.ts`
12+
- **Constants:** SCREAMING_SNAKE → `export const MAX_RETRIES = 3`
13+
- **Types files:** camelCase → `types/index.ts`, `types/database.ts`
14+
- **Server Actions:** camelCase in grouped files → `actions.ts` (not one file per action)
15+
- **Route Handlers:** always `route.ts` (Next.js convention)
16+
17+
## Directory Rules
18+
- Components in `src/components/` — NEVER in `src/app/`
19+
- Page-specific components in `src/app/[route]/_components/` (underscore prefix = private)
20+
- Shared types in `src/types/` — NEVER define types inline in components
21+
- Business logic in `src/lib/` — NEVER put business logic in components
22+
- Server actions in the nearest `actions.ts` to where they're used
23+
24+
## Import Order (enforce in every file)
25+
```typescript
26+
// 1. React/Next.js imports
27+
import { Suspense } from 'react'
28+
import Link from 'next/link'
29+
30+
// 2. Third-party libraries
31+
import { z } from 'zod'
32+
33+
// 3. Internal aliases (@/)
34+
import { Button } from '@/components/ui/button'
35+
import { createClient } from '@/lib/supabase/server'
36+
import type { UserProfile } from '@/types'
37+
38+
// 4. Relative imports (only for co-located files)
39+
import { columns } from './columns'
40+
```
41+
42+
## Anti-Patterns
43+
- NEVER create `utils/helpers.ts` catch-all files — name files by what they do
44+
- NEVER put more than 1 exported component per file
45+
- NEVER use default exports for utilities — use named exports
46+
- NEVER create `index.ts` barrel files that re-export everything (breaks tree-shaking)

rules/git-conventions.mdc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
description: Git workflow and commit conventions for AI-assisted development
3+
globs: ["*"]
4+
---
5+
6+
# Git Conventions for Vibe Coders
7+
8+
Commit convention (Conventional Commits):
9+
- `feat:` add user authentication
10+
- `fix:` resolve RLS policy for shared projects
11+
- `chore:` update dependencies
12+
- `docs:` add API documentation
13+
- `refactor:` extract auth helper to separate module
14+
15+
Branch naming:
16+
- `feature/auth-flow`
17+
- `fix/rls-policy-bug`
18+
- `chore/update-dependencies`
19+
20+
ALWAYS run before commit (if available):
21+
1. `npm run lint`
22+
2. `npm run type-check`
23+
3. `npm test`
24+
25+
AI session workflow:
26+
- Create a new branch before starting any AI session
27+
- Review ALL AI-generated code before committing
28+
- Break large AI generations into multiple focused commits

0 commit comments

Comments
 (0)