A comprehensive, production-ready reference for configuring AI assistants across Cursor, Windsurf, Claude Code, Aider, Continue.dev, Cline, Roo-Cline, Zed, GitHub Copilot, and JetBrains AI Assistant
I. Introduction & Strategic Overview
1.1 Understanding the AI Coding Rules Landscape
The AI coding assistant ecosystem has matured rapidly between 2023-2025, converging on a critical insight: generic AI assistants produce generic code. Project-level instruction files transform general-purpose language models into specialized coding partners that understand your architecture, enforce your standards, and speak your team's technical dialect.
The Modern Reality (2025):
- ~80% of agentic AI coding tools now support project-level instruction files
- Markdown has emerged as the universal format (60% adoption) for natural language instructions
- YAML/JSON handle structured configuration (25%) for model settings, tool permissions, and metadata
- Cross-tool compatibility is now expected—developers frequently use 2-3 tools simultaneously
What Changed:
| Era | Configuration Approach | Tools |
|---|---|---|
| 2021-2022 | No project-level config; global settings only | Early GitHub Copilot |
| 2023 | Single-file Markdown rules emerge | Cursor .cursorrules, early adopters |
| 2024 | Multi-file systems, YAML metadata, tool interop | Windsurf Wave 8, Continue.dev, Aider |
| 2025 | Standardization around AGENTS.md, hierarchical rules, mode-based systems | Roo-Cline modes, GitHub Copilot catches up |
1.2 Why Project-Level Rules Matter
Quantified Benefits from Production Use:
| Metric | Without Rules | With Well-Designed Rules | Source |
|---|---|---|---|
| Onboarding Time | 2-4 weeks | 3-5 days | Builder.io case study, 2025 |
| Code Review Cycles | 3-4 iterations | 1-2 iterations | Cursor community reports |
| AI Suggestion Acceptance | 30-40% | 70-90% | Windsurf Zoominfo deployment |
| Standards Compliance | 60-70% | 90-95% | Trigger.dev blog analysis |
| Debugging Time (AI-generated) | 40% longer | On par with human code | DataCamp research |
Real-World Impact:
Example 1: Fast-Growing Startup (15 engineers)
- Before: Each developer explained project context in every AI chat session
-
After: Shared
.cursorruleswith tech stack, architecture, and anti-patterns - Result: 40% reduction in "where does X go?" questions, 25% faster PR merges
Example 2: Enterprise Fintech (200+ engineers)
- Before: Inconsistent code generation across teams, security violations in AI suggestions
- After: Organization-level Windsurf rules + project-specific overrides
- Result: 90% reduction in security policy violations, standardized error handling
Example 3: Open Source Maintainer
- Before: Contributors submitted code that didn't match project conventions
-
After:
CLAUDE.mdwith explicit patterns and anti-patterns - Result: 60% fewer "please update to match our style" review comments
1.3 The Portability Challenge
The Multi-Tool Reality:
Modern development teams don't use a single AI assistant. A typical 10-person team might have:
- 3 developers using Cursor (prefer Composer multi-file edits)
- 2 developers using Windsurf (prefer Cascade's deep reasoning)
- 2 developers using VS Code with Cline/Continue.dev (prefer open-source)
- 2 developers using JetBrains IDEs with AI Assistant (Java/Kotlin focus)
- 1 developer using Claude Code CLI (terminal-centric workflow)
The Challenge:
Without a portability strategy, you maintain 5 different rule files that drift out of sync, leading to:
- ❌ Inconsistent AI behavior across team members
- ❌ Wasted effort maintaining duplicate documentation
- ❌ New hires confused by contradictory instructions
- ❌ Technical debt in configuration management
The Solution (Detailed in Section V):
A Single Source of Truth (SSOT) architecture where one canonical instruction file (typically AGENTS.md or AI_RULES.md) is distributed to tool-specific locations through symlinks or build scripts.
II. Universal Principles Across All Tools
These principles apply regardless of which tool you use. They're grounded in how Large Language Models process context windows.
2.1 The Context Density Principle
The Physics of AI Context:
Every AI assistant has a context window (typically 64k-200k tokens). Your instruction file competes for space with:
- Your actual code
- Conversation history
- Tool outputs and error messages
- Retrieved documentation
Dense information wins. Prose-heavy instructions waste tokens on connecting words that don't change AI behavior.
❌ Low-Density (Bad):
We generally try to keep our components relatively small whenever it's
reasonably possible to do so, because we've found through experience that
smaller components tend to be easier to maintain and test. We'd appreciate
it if you could try to follow this principle when you're generating code
for our project.
Token count: ~52 tokens
Actionable information: "Keep components small"
✅ High-Density (Good):
## Component Size Rules
ALWAYS break components > 200 lines into smaller units.
NEVER create single-file components with multiple responsibilities.
Example split:
- UserProfile.tsx (150 lines) →
- UserProfile.tsx (50 lines, layout)
- UserAvatar.tsx (30 lines)
- UserBio.tsx (40 lines)
- UserStats.tsx (30 lines)
Token count: ~48 tokens
Actionable information: Specific threshold (200 lines), concrete example, clear directives
High-Density Patterns:
| Pattern | Description | Example |
|---|---|---|
| Binary Constraints | Use ALWAYS/NEVER | ALWAYS validate inputs. NEVER trust user data. |
| Numbered Rules | Sequential, scannable | 1. Type hints required 2. Max 50 lines/function 3. Docstrings mandatory |
| Code > Prose | Show don't tell | Include 3-5 line code examples vs paragraphs of description |
| Hierarchical Headers | Markdown H2/H3 for scanning | Use ## for categories, ### for specifics |
| Tables for Mappings | Structured data | File patterns → conventions, Error types → handling |
2.2 Tech Stack Declaration Best Practices
Why It Matters:
When you tell an AI "we use React," it must choose between:
- React 16 (class components, lifecycle methods)
- React 17 (functional components emerging)
- React 18 (concurrent rendering)
- React 19 (Server Components, new use hook)
Without version specificity, the AI defaults to its training data average—likely outdated patterns.
Universal Tech Stack Template:
# Technology Stack
## Language & Runtime
- **Language**: TypeScript 5.4 (strict mode enabled)
- **Runtime**: Node.js 20.11 LTS
- **Package Manager**: pnpm 9.0 (NOT npm/yarn)
## Frontend Framework
- **Core**: React 19.0
- **Meta-Framework**: Next.js 15 (App Router, NOT Pages Router)
- **Styling**: Tailwind CSS 4.0
- **Components**: Radix UI primitives + shadcn/ui
## State Management
- **Server State**: TanStack Query v5 (React Query)
- **Client State**: Zustand 4.5 (NOT Redux)
- **Form State**: React Hook Form 7.5 + Zod 3.23
## Backend Framework
- **API Framework**: tRPC 11 for type-safe APIs
- **Database**: PostgreSQL 16
- **ORM**: Drizzle ORM 0.35 (NOT Prisma)
## Testing
- **Unit/Integration**: Vitest 2.0 (NOT Jest)
- **E2E**: Playwright 1.45
- **Component**: Testing Library (React)
## Build & Deploy
- **Bundler**: Turbopack (Next.js built-in)
- **Monorepo**: Turborepo 2.0
- **Hosting**: Vercel (production), Railway (staging)
- **CI/CD**: GitHub Actions
Why Each Detail Matters:
| Specification | Why Critical | What AI Does Differently |
|---|---|---|
pnpm NOT npm |
Different lockfile format, workspace protocol | Uses pnpm install vs npm install, references workspace:* deps |
| App Router NOT Pages | Completely different file structure, data fetching | Server Components by default, uses app/ not pages/
|
| Zustand NOT Redux | Different API, no boilerplate | Creates clean stores vs action/reducer/dispatch patterns |
| Vitest NOT Jest | Different config, faster, ESM-first | Uses vitest.config.ts, native ESM imports |
| Drizzle NOT Prisma | SQL-centric vs abstraction-heavy | Writes migrations as SQL, uses drizzle-kit commands |
2.3 Positive vs. Negative Constraints
The LLM "NOT" Problem:
Language models trained on vast amounts of code have deeply ingrained patterns. A simple "don't do X" fights against thousands of training examples. Negative constraints work best when paired with positive alternatives that provide a clear path forward.
Effectiveness Hierarchy (from research):
| Constraint Type | Adherence Rate | Example |
|---|---|---|
| Negative only | ~40-50% | "Don't use any type" |
| Negative + reason | ~60-70% | "Don't use any (breaks type safety)" |
| Negative + alternative | ~85-90% | "Don't use any. Use unknown then narrow with type guards" |
| Negative + alternative + example | ~95%+ | Full pattern below |
✅ Maximum Effectiveness Pattern:
## Type Safety Rules
### ❌ NEVER: The `any` Escape Hatch
typescript
// FORBIDDEN - Breaks type safety completely
function processData(data: any) {
return data.someProperty; // No autocomplete, no errors
}
**Why it's forbidden:** Disables TypeScript's entire value proposition. One `any` can cascade type unsafety through your codebase.
### ✅ DO: Use `unknown` + Type Guards
typescript
// CORRECT - Maintains type safety
function processData(data: unknown) {
// Type guard narrows unknown → specific type
if (isValidDataShape(data)) {
return data.someProperty; // Now type-safe!
}
throw new AppError('Invalid data shape', 400);
}
// Type guard helper
function isValidDataShape(data: unknown): data is DataShape {
return (
typeof data === 'object' &&
data !== null &&
'someProperty' in data
);
}
**When you need flexibility:**
- Use `unknown` for data from external APIs, user input, or parsed JSON
- Use generics for reusable functions: `function process<T>(data: T): T`
- Use branded types for validated data: `type ValidatedData = Data & { __validated: true }`
Pairing Pattern Structure:
- ❌ NEVER section: Show the anti-pattern with code
- Why it's forbidden: Explain consequences (security, performance, maintainability)
- ✅ DO section: Show the correct pattern with full example
- When you need flexibility: Cover edge cases and alternatives
2.4 The Structure Hierarchy Standard
The 7-Section Framework:
Based on analysis of 100+ production rule files from Cursor (awesome-cursorrules), Anthropic's Claude Code documentation, and enterprise deployments, this structure consistently produces the highest AI adherence:
# [Project Name] - [One-line Description]
## 1. PROJECT OVERVIEW (30-50 words)
Brief context about what this project does and its primary goal.
## 2. TECHNOLOGY STACK (Exhaustive list with versions)
See Section 2.2 template above.
## 3. ARCHITECTURE & STRUCTURE (File tree + explanations)
src/
├── app/ # Next.js routes (App Router)
├── components/ # Reusable React components
│ ├── ui/ # shadcn/ui primitives
│ └── features/ # Feature-specific components
├── lib/ # Utilities, helpers, business logic
│ ├── api/ # tRPC routers and procedures
│ ├── db/ # Drizzle schema and queries
│ └── utils/ # Pure functions, formatters
└── tests/ # Test files mirror src/ structure
Key architectural decisions:
- **Feature-first organization**: Group by feature domain, not technical type
- **Server Components by default**: Only add 'use client' when needed
- **Colocation**: Keep component, styles, tests together
## 4. CODING STANDARDS (With examples and anti-patterns)
See Section 2.3 for effective pattern structure.
## 5. TESTING REQUIREMENTS (Commands, coverage, approaches)
- **Unit**: 80% coverage minimum, `pnpm test`
- **Integration**: API endpoints with MSW, `pnpm test:integration`
- **E2E**: Critical paths only, `pnpm test:e2e`
## 6. COMMON PITFALLS & KNOWN ISSUES (Save time debugging)
- **Pitfall**: Mixing Server/Client components incorrectly
- **Symptom**: "useState can only be used in Client Components"
- **Fix**: Add 'use client' at top of file, or lift state up
## 7. IMPORTANT NOTES & CONSTRAINTS
- API rate limit: 100 req/min per IP
- JWT expires: 24h (refresh token: 30d)
- All routes: `/api/v1/*` prefix
Why This Order:
| Section | Why First? | What AI Gains |
|---|---|---|
| Overview | Instant orientation | Understands project domain before seeing details |
| Stack | Prunes possibility space | Knows which libraries/patterns are relevant |
| Architecture | File system mental model | Can reason about "where should this code go?" |
| Standards | How to write code | Applies patterns during generation |
| Testing | Quality gates | Knows what tests to write, when |
| Pitfalls | Learn from experience | Avoids known failure modes |
| Notes | Critical constraints | Respects security, API, business rules |
2.5 Anti-Patterns to Avoid Universally
The "Encyclopedia" Anti-Pattern:
❌ Symptom: Rules file is 5,000+ words, comprehensive documentation of every possible coding scenario.
Why it fails:
- Consumes 15-20% of context window
- AI spends tokens retrieving irrelevant sections
- High-priority rules get buried in noise
- Maintenance becomes overwhelming
Real example: A team's .cursorrules included:
- Complete React documentation excerpts (1,200 words)
- Full ESLint rule explanations (800 words)
- Git workflow with branching strategy diagrams (600 words)
- Code review checklist (400 words)
- Team meeting notes (300 words)
Result: AI followed ~30% of actual project-specific rules because they were lost in generic content.
✅ Fix: Keep project-specific rules under 500 lines. Link to external docs:
## Additional Resources
For comprehensive React patterns, see: @docs/react-patterns.md
For ESLint rules, run: `npm run lint -- --print-config`
For Git workflow, see: CONTRIBUTING.md
The "Be Clean" Ambiguity Anti-Pattern:
❌ Symptom: Rules use vague subjective terms without concrete definitions.
- Write clean code
- Keep functions simple
- Use modern JavaScript
- Be consistent
- Make it performant
Why it fails:
- "Clean" means different things to different people (and LLMs)
- "Simple" without threshold is meaningless
- "Modern" changes yearly and varies by training data cutoff
- "Consistent" with what baseline?
✅ Fix: Replace every subjective term with measurable criteria:
## Code Quality Standards
### Function Complexity
- **Max lines per function**: 50 (enforced by ESLint)
- **Max parameters**: 4 (use options object if more needed)
- **Max cyclomatic complexity**: 10 (ESLint complexity rule)
### Modern JavaScript (ES2022+)
- ✅ DO: `async/await`, optional chaining `?.`, nullish coalescing `??`
- ❌ DON'T: `.then()` chains, `_.get()` (use `?.`), `|| ` for defaults (use `??`)
### Consistency
Follow Prettier config (committed in `.prettierrc.json`):
- 2-space indentation
- Single quotes
- No semicolons
- Trailing commas
The "Conflicting Roles" Anti-Pattern:
❌ Symptom: Rules describe the AI with contradictory personas.
You are a senior principal engineer with 20 years of experience.
Explain everything in detail suitable for junior developers.
Move fast and break things.
Ensure enterprise-grade stability and reliability.
Why it fails:
- Creates cognitive dissonance in the model's generation process
- "Senior engineer" suggests terseness and assumptions
- "Junior audience" suggests verbosity and hand-holding
- "Move fast" conflicts with "enterprise-grade stability"
✅ Fix: Choose ONE primary persona aligned with your actual needs:
# AI Role Definition
You are a senior software engineer on a fast-paced startup team. Your priorities:
1. **Velocity**: Ship working features quickly
2. **Pragmatism**: Choose simple solutions over clever ones
3. **Context**: Assume the reader is an experienced developer
4. **Standards**: Follow established patterns, don't reinvent
When there's tension between speed and perfection, bias toward speed with inline TODOs for future improvements.
The "Dead Link" Anti-Pattern:
❌ Symptom: Rules reference external URLs expecting AI to browse.
Follow our API conventions at: https://docs.company.com/api-design
See authentication flow: https://wiki.company.com/auth
Database schema: https://dbdiagram.io/d/project-schema
Why it fails:
- Most AI coding assistants cannot reliably browse URLs during code generation
- Even those with browsing capability (Claude Code, some Cursor modes) have latency/reliability issues
- Broken links fail silently—AI just skips the instruction
✅ Fix: Inline critical information, use local file references:
## API Conventions (from docs/api-design.md)
Every endpoint returns this structure:
json
{
"success": boolean,
"data": T | null,
"error": {
"code": string,
"message": string
} | null
}
## Authentication Flow
See @docs/auth-flow.md for complete flow, key points:
- JWT in `Authorization: Bearer <token>` header
- Refresh tokens: POST /api/auth/refresh
- Token expiry: 15 minutes (access), 7 days (refresh)
## Database Schema
See @prisma/schema.prisma for full schema. Core entities:
- Users → Profiles (1:1)
- Users → Posts (1:N)
- Posts → Comments (1:N)
Tool-specific file reference syntax:
- Cursor:
[filename](mdc:path/to/file) - Continue.dev:
@path/to/file - Claude Code:
@docs/filename.md - Windsurf: Direct markdown includes or
@importsyntax - Aider: Load via
read: [path/to/file]in.aider.conf.yml
III. Format Comparison Matrix
Understanding when to use Markdown vs YAML vs JSON vs TOML is critical for choosing the right tool and configuration strategy.
3.1 Markdown vs YAML vs JSON vs TOML
The Format Decision Tree:
Do you need machine-parseable structured data (model config, tool permissions)?
├─ YES → Use YAML, JSON, or TOML
│ ├─ Need comments & human readability? → YAML
│ ├─ Need strict validation & tooling? → JSON
│ └─ Rust/systems project? → TOML
└─ NO → Are you writing natural language instructions?
└─ YES → Use Markdown
Comprehensive Comparison:
| Aspect | Markdown | YAML | JSON | TOML |
|---|---|---|---|---|
| Primary Use | Natural language instructions | Configuration with hierarchy | Strict config & APIs | Rust ecosystem config |
| Human Readability | ⭐⭐⭐⭐⭐ Excellent | ⭐⭐⭐⭐ Very Good | ⭐⭐⭐ Good | ⭐⭐⭐⭐ Very Good |
| AI Processing | ⭐⭐⭐⭐⭐ Native | ⭐⭐⭐ Parsed to text | ⭐⭐⭐ Parsed to text | ⭐⭐⭐ Parsed to text |
| Validation | ❌ None | ⚠️ Schema possible | ✅ JSON Schema | ⚠️ Limited |
| Comments | ✅ Native | ✅ # comment
|
❌ Not standard | ✅ # comment
|
| Multi-line Strings | ✅ Native | ✅ ` |
or >` |
❌ Escaped \n
|
| Nesting | ⚠️ Headers only | ✅ Indentation | ✅ Objects | ⚠️ Tables |
| Version Control | ⭐⭐⭐⭐⭐ Excellent diffs | ⭐⭐⭐⭐ Good diffs | ⭐⭐⭐ Acceptable | ⭐⭐⭐⭐ Good diffs |
| Tool Support | 🛠️ All AI tools | 🛠️ Continue.dev, Roo-Cline, Aider, Windsurf | 🛠️ Continue.dev, Roo-Cline (legacy) | 🛠️ Zed (settings only) |
| Best For | Instructions, examples, prose | Hierarchical config, metadata | API contracts, strict validation | Rust projects, simple configs |
Detailed Format Analysis:
Markdown: The Universal Instruction Format
Strengths:
# Why Markdown Wins for Instructions
## Native AI Processing
LLMs are trained on markdown documentation. Headers, lists, and code blocks
are "native" structures that don't require parsing.
## Flexible Structure
- Hierarchical headers (H1-H6)
- Ordered and unordered lists
- Tables for structured data
- Code blocks with syntax highlighting
- Inline code for `commands`
- Emphasis with **bold** and *italic*
## Example-Rich
typescript
// Show actual code examples directly
interface User {
id: string;
name: string;
}
## Git-Friendly
Semantic line breaks make diffs clean:
- Each sentence on its own line
- Changes are isolated
- Merge conflicts rare
Weaknesses:
- No schema validation (typos not caught)
- No type checking for values
- Nesting limited to headers
- Can't enforce structure programmatically
When to Use:
- Project context and overview
- Coding standards and conventions
- Architecture documentation
- Examples and anti-patterns
- Testing strategies
- Any natural language instruction
Tools Using Markdown:
- Cursor (
.cursor/rules/*.mdcbody) - Windsurf (
.windsurfrulesbody) - Claude Code (
CLAUDE.md) - Cline (
.clinerules) - Zed (
.rules) - GitHub Copilot (
.github/copilot-instructions.md) - JetBrains (
.aiassistant/rules/*.md)
YAML: The Readable Configuration Format
Strengths:
# YAML Example - Continue.dev config.yaml
# Comments are first-class citizens
models:
- name: Claude 3.5 Sonnet
provider: anthropic
model: claude-3-5-sonnet-20241022
roles: [chat, edit, apply]
# Multi-line strings with block scalars
chatOptions:
baseSystemMessage: |
You are an expert TypeScript engineer.
Focus on type safety and performance.
# Nested configuration
defaultCompletionOptions:
temperature: 0.7
maxTokens: 4000
# Lists are clean
capabilities:
- tool_use
- image_input
# Anchors and aliases for reuse
base_settings: &base
temperature: 0.5
maxTokens: 2000
gpt4_model:
<<: *base # Inherit base settings
model: gpt-4o
Weaknesses:
- Indentation-sensitive (tabs vs spaces errors)
-
Subtle syntax gotchas (
:in strings,yes/noboolean coercion) - Ambiguous anchor/alias behavior
- No schema enforcement (unless using external validator)
Common YAML Pitfalls:
# ❌ WRONG - Boolean coercion gotcha
country: NO # Parsed as false, not "NO"
# ✅ CORRECT - Quote strings
country: "NO"
# ❌ WRONG - Colon in unquoted string
message: Error: File not found # Syntax error
# ✅ CORRECT - Quote strings with special chars
message: "Error: File not found"
# ❌ WRONG - Tab indentation (invisible error)
rules:
→ - First rule # Tab character breaks parsing
# ✅ CORRECT - Space indentation
rules:
- First rule # Spaces work
When to Use:
- Hierarchical configuration (models, tools, contexts)
- Settings with nested structure
- Configuration requiring comments
- Multi-line text blocks
- Reusable config chunks (anchors)
Tools Using YAML:
- Aider (
.aider.conf.yml) - Continue.dev (
config.yaml) - Roo-Cline (
.roomodespreferred over JSON) - Windsurf (YAML frontmatter in
.windsurfrules)
JSON: The Strict Configuration Format
Strengths:
{
"// comments": "Workaround for lack of native comments",
"models": [{
"name": "GPT-4o",
"provider": "openai",
"model": "gpt-4o",
"apiKey": "${OPENAI_API_KEY}",
"roles": ["chat", "edit"],
"chatOptions": {
"baseSystemMessage": "You are a helpful coding assistant."
}
}],
"rules": [
"Use TypeScript strict mode",
"Prefer functional components"
],
"context": [
{
"provider": "code"
},
{
"provider": "codebase",
"params": {
"nRetrieve": 30,
"nFinal": 5
}
}
]
}
Strengths:
- JSON Schema validation: Catch errors before runtime
- Universal parser support: Every language has JSON libraries
- Strict syntax: No ambiguity like YAML booleans
- IDE autocomplete: With JSON Schema, get IntelliSense
Weaknesses:
-
No native comments (use workarounds like
"_comment"keys) - Verbose: All keys quoted, no multi-line strings
- No trailing commas: Last item can't have comma (JSON5 fixes this)
-
Escaping hell: Strings need
\",\\,\netc.
JSON vs JSON5 (VS Code supports JSON5 in settings):
{
// Native comments supported
models: [ // Unquoted keys allowed
{
name: "GPT-4o", // Trailing comma OK
},
],
rules: [
"Use TypeScript \
strict mode", // Multi-line strings
],
}
When to Use:
- API configuration (strict schemas)
- Generated config (tooling creates it)
- Cross-platform compatibility (guaranteed parsing)
- When JSON Schema validation is critical
Tools Using JSON:
- Continue.dev (legacy
config.json, deprecated) - Roo-Cline (
.roomodessupports JSON) - VS Code settings (JSON with comments)
TOML: The Rust Ecosystem Format
Strengths:
# TOML Example - Hypothetical Zed config (actual Zed uses JSON)
# Clear, unambiguous key-value pairs
version = "1.0"
schema = "v1"
# Tables for grouping
[models.gpt4]
name = "GPT-4o"
provider = "openai"
model = "gpt-4o"
roles = ["chat", "edit"]
# Nested tables
[models.gpt4.chatOptions]
baseSystemMessage = """
You are an expert TypeScript engineer.
Focus on type safety and performance.
""" # Multi-line strings with """
# Arrays of tables
[[rules]]
name = "typescript_strict"
globs = ["**/*.ts", "**/*.tsx"]
prompt = """
Use TypeScript strict mode.
All functions need explicit return types.
"""
[[rules]]
name = "react_hooks"
globs = ["**/*.tsx"]
prompt = "Prefer functional components with hooks."
Strengths:
- Unambiguous syntax: No YAML's boolean coercion issues
-
Clear nesting: Tables (
[section]) are explicit -
Multi-line strings: Native
"""support - Strong typing: Dates, times, integers, floats are distinct
Weaknesses:
- Limited adoption outside Rust: Primarily Cargo, config files
- Verbose for deep nesting: Requires explicit table paths
- Tooling: Less IDE support than YAML/JSON
When to Use:
- Rust/Cargo projects (natural fit)
- Simple, flat configurations
- When YAML indentation errors are problematic
- Projects already using TOML elsewhere
Tools Using TOML:
- ⚠️ Zed does NOT use TOML for AI rules (uses Markdown
.rules) - Zed uses TOML for:
extension.toml, languageconfig.toml,.taplo.toml(linter) - Conceptually similar to what could be used for structured rule metadata
3.2 When to Use Each Format
Decision Matrix:
| Your Need | Recommended Format | Rationale |
|---|---|---|
| Natural language instructions | Markdown | AI processes natively, human-readable, example-rich |
| Project context & architecture | Markdown | Documentation-style content, flexible structure |
| Model configuration | YAML or JSON | Hierarchical settings, schema validation possible |
| Tool permissions & restrictions | YAML or JSON | Structured rules, programmatic validation |
| Cross-tool compatibility | Markdown (AGENTS.md) | Universal format, all tools read markdown |
| Strict validation required | JSON + JSON Schema | Type-checked, IDE autocomplete |
| Team readability priority | YAML | Comments, clean diffs, readable nesting |
| API/generated config | JSON | Machine-generated, strict parsing |
| Rust ecosystem | TOML | Native cargo integration |
Hybrid Approach (Most Common in Practice):
Modern tools use structured format (YAML/JSON) for metadata + Markdown for instructions:
Example: Continue.dev
# config.yaml (structured settings)
models:
- name: Claude 3.5
provider: anthropic
model: claude-3-5-sonnet-20241022
roles: [chat, edit]
# Markdown rules loaded separately
rules:
- uses: ./blocks/typescript-standards.md
Example: Roo-Cline
# .roomodes (YAML for mode structure)
customModes:
- slug: typescript-expert
name: TypeScript Expert
groups:
- read
- [edit, { fileRegex: '\\.tsx?$' }]
# Markdown content in YAML string
customInstructions: |
# TypeScript Standards
- Use strict mode
- Explicit return types
- Zod for validation
Example: Cursor
---
# YAML frontmatter for metadata
description: "TypeScript coding standards"
globs: ["**/*.ts", "**/*.tsx"]
alwaysApply: false
---
# Markdown body for actual instructions
## Type Safety Rules
[Natural language instructions here...]
IV. Tool-Specific Deep Dives
4.1 Cursor Configuration
Cursor has evolved to become the market leader in AI-first IDEs, and its configuration system reflects sophisticated design choices based on real-world usage at scale.
4.1.1 Modern .cursor/rules/*.mdc System
Overview:
-
Format:
.mdc(Markdown Cursor) = YAML frontmatter + Markdown body -
Location:
.cursor/rules/directory in project root - Status: Official, recommended approach since v0.40 (2024)
- Auto-load: Rules are automatically applied based on frontmatter metadata
Complete MDC File Structure:
---
description: "TypeScript and React coding standards for frontend"
globs:
- "src/**/*.{ts,tsx}"
- "app/**/*.{ts,tsx}"
alwaysApply: false
---
# Frontend TypeScript Standards
## Context
This project uses Next.js 15 App Router with TypeScript strict mode.
Server Components are the default; only use Client Components when necessary.
## Component Rules
### Structure
typescript
// ✅ Preferred: Named export, typed props
interface UserCardProps {
user: User;
onEdit: (id: string) => void;
}
export function UserCard({ user, onEdit }: UserCardProps) {
return
}
// ❌ Avoid: Default export, inline types
export default function UserCard({ user, onEdit }: {
user: any;
onEdit: Function;
}) {
return
}
### Size Limits
- **Max component length**: 200 lines
- **If exceeded**: Split into smaller components
- **Composition over props drilling**: Use composition pattern
## State Management
| Use Case | Solution | Example |
|----------|----------|---------|
| Server data | TanStack Query | `useQuery('users', fetchUsers)` |
| Client state | useState | `const [open, setOpen] = useState(false)` |
| Global client | Zustand | `const user = useUserStore(s => s.user)` |
| URL state | useSearchParams | `const [search] = useSearchParams()` |
## Always Include
typescript
// Type all props explicitly
interface Props {
// Document complex props
children: ReactNode;
}
// Type event handlers
onClick: (event: MouseEvent) => void;
// Async handlers need error handling
async function handleSubmit() {
try {
await saveData();
} catch (error) {
if (error instanceof ApiError) {
toast.error(error.message);
}
}
}
YAML Frontmatter Fields (Complete Reference):
| Field | Type | Required | Purpose | Example |
|---|---|---|---|---|
description |
string | Yes | Human-readable summary for agent-requested rules | "Apply when working with React components" |
globs |
string[] | No | File patterns for auto-attachment (gitignore syntax) | ["src/**/*.ts", "!**/*.test.ts"] |
alwaysApply |
boolean | No | If true, applies to every request regardless of context | true |
tags |
string[] | No | Categorization (not widely used yet) | ["frontend", "testing"] |
priority |
number | No | When multiple rules match, higher priority wins | 10 |
Rule Type Inference Logic:
Cursor infers the rule type from frontmatter combinations:
| Frontmatter | Inferred Type | Behavior |
|---|---|---|
alwaysApply: true |
Always | Included in every Cursor request |
globs: [...] |
Auto Attached | Applied when editing matching files |
description: "..." (no globs) |
Agent Requested | AI decides based on description |
| No description, no globs | Manual | Only used when explicitly @mentioned |
Example: Complete Multi-File Setup
my-project/
├── .cursor/
│ └── rules/
│ ├── index.mdc # Always-apply project overview
│ ├── typescript-frontend.mdc # Auto-attach to *.ts, *.tsx
│ ├── api-backend.mdc # Auto-attach to api/**/*
│ ├── testing.mdc # Manual invocation for test writing
│ └── security.mdc # Agent-requested for security contexts
└── src/
index.mdc (Always Applied):
---
description: "Project-wide standards and context"
alwaysApply: true
---
# Project Overview
**Stack**: Next.js 15, TypeScript, PostgreSQL, tRPC
**Architecture**: Monorepo with Turborepo
**Deployment**: Vercel (prod), Railway (staging)
## Universal Rules
- Use pnpm (NOT npm)
- Commits follow Conventional Commits
- All PRs require 1 approval
- Run `pnpm lint` before committing
typescript-frontend.mdc (Auto-Attached):
---
description: "Frontend TypeScript and React standards"
globs:
- "app/**/*.{ts,tsx}"
- "components/**/*.{ts,tsx}"
---
# Frontend Standards
[See detailed example above]
testing.mdc (Manual):
---
description: "Testing guidelines and patterns"
---
# Testing Standards
When writing tests, follow this structure:
## Unit Tests (Vitest)
typescript
import { describe, it, expect } from 'vitest';
describe('calculateTotal', () => {
it('sums item prices', () => {
const items = [{ price: 10 }, { price: 20 }];
expect(calculateTotal(items)).toBe(30);
});
it('handles empty array', () => {
expect(calculateTotal([])).toBe(0);
});
});
## Component Tests (Testing Library)
typescript
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
it('submits form on button click', async () => {
const onSubmit = vi.fn();
render();
await userEvent.type(screen.getByLabelText('Name'), 'John');
await userEvent.click(screen.getByRole('button', { name: 'Submit' }));
expect(onSubmit).toHaveBeenCalledWith({ name: 'John' });
});
security.mdc (Agent-Requested):
---
description: "Apply when handling authentication, user data, or API keys"
---
# Security Standards
## Authentication
- JWTs in HttpOnly cookies (NEVER localStorage)
- Refresh token rotation on use
- CSRF protection on state-changing endpoints
## Data Validation
typescript
// ✅ ALWAYS validate with Zod
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email(),
password: z.string().min(12),
});
// Validate before using
const user = UserSchema.parse(untrustedData);
## Secrets Management
- ❌ NEVER hardcode: `const apiKey = "sk_live_..."`
- ✅ Use environment: `process.env.API_KEY`
- ✅ Validate env vars: Use `@t3-oss/env-nextjs`
4.1.2 File References with mdc: Scheme
Cursor supports inline file references to inject additional context:
---
description: "API development standards"
globs: ["src/api/**/*"]
---
# API Standards
For complete authentication flow, see: [auth-flow.ts](mdc:src/api/auth/auth-flow.ts)
For error handling patterns, see: [errors.ts](mdc:src/lib/errors.ts)
## Key Patterns
Follow the pattern in [user-router.ts](mdc:src/api/routers/user-router.ts):
- Input validation with Zod
- Error handling with custom AppError
- Consistent response format
How mdc: Links Work:
- Cursor reads the referenced file
- Includes its content in the rule's context
- AI can reference specific patterns from those files
- Useful for pointing to canonical implementations
Best Practices:
- ✅ Reference small, focused files (< 200 lines)
- ✅ Use for showing implementation patterns
- ✅ Point to configuration files (schema, types)
- ❌ Don't reference large files (wastes tokens)
- ❌ Don't chain many references (context overflow)
4.1.3 Advanced Patterns
Pattern 1: Per-Language Rules
.cursor/rules/
├── index.mdc # Project overview (always)
├── typescript.mdc # globs: ["**/*.ts", "**/*.tsx"]
├── python.mdc # globs: ["**/*.py"]
├── sql.mdc # globs: ["**/*.sql"]
└── markdown.mdc # globs: ["**/*.md"]
Pattern 2: Feature-Based Rules
.cursor/rules/
├── index.mdc # Core standards
├── auth-feature.mdc # globs: ["src/features/auth/**/*"]
├── payments-feature.mdc # globs: ["src/features/payments/**/*"]
└── admin-feature.mdc # globs: ["src/features/admin/**/*"]
Pattern 3: Role-Based Rules
.cursor/rules/
├── architect.mdc # Agent-requested for system design
├── implementer.mdc # Auto-attach for .ts files
├── tester.mdc # Manual for test writing
└── reviewer.mdc # Agent-requested for code review
4.1.4 Production Examples & Templates
Complete Next.js 15 + TypeScript Template:
---
description: "Next.js 15 App Router with TypeScript and Tailwind"
alwaysApply: true
---
# Next.js 15 Full-Stack Project
## Tech Stack
- **Frontend**: Next.js 15 (App Router), React 19, TypeScript 5.4
- **Styling**: Tailwind CSS 4.0, shadcn/ui components
- **Backend**: tRPC 11, Drizzle ORM, PostgreSQL 16
- **Auth**: NextAuth.js v5 (Auth.js)
- **State**: TanStack Query, Zustand for global client state
- **Testing**: Vitest, Testing Library, Playwright
## Project Structure
src/
├── app/ # Next.js routes
│ ├── (auth)/ # Auth group (login, register)
│ ├── (dashboard)/ # Protected dashboard
│ ├── api/ # API routes (minimal, prefer tRPC)
│ └── layout.tsx # Root layout
├── components/
│ ├── ui/ # shadcn/ui primitives
│ ├── forms/ # Reusable form components
│ └── layouts/ # Layout components
├── lib/
│ ├── api/ # tRPC routers
│ ├── db/ # Drizzle schema and queries
│ ├── auth/ # Auth utilities
│ └── utils/ # Helpers
└── types/ # Shared TypeScript types
## Server vs Client Components
### Default: Server Components
tsx
// ✅ Server Component (default, no directive needed)
async function UserProfile({ userId }: { userId: string }) {
// Can fetch data directly
const user = await db.query.users.findFirst({
where: eq(users.id, userId)
});
return
{user.name};}
### Only add 'use client' when:
1. Using React hooks (useState, useEffect, etc.)
2. Event handlers (onClick, onChange, etc.)
3. Browser APIs (window, localStorage, etc.)
4. Third-party libraries that use client features
tsx
'use client';
import { useState } from 'react';
// ✅ Client Component (hooks require it)
function Counter() {
const [count, setCount] = useState(0);
return setCount(count + 1)}>{count};
}
## Data Fetching Patterns
| Pattern | Use Case | Example |
|---------|----------|---------|
| **Server Component** | Static/initial data | `const data = await fetchData()` |
| **tRPC Query** | Client-side fetching | `const { data } = trpc.users.list.useQuery()` |
| **Server Action** | Mutations | `async function updateUser(data: FormData)` |
## Type Safety
### Zod Schemas
typescript
// ✅ Define schema once
import { z } from 'zod';
export const UserSchema = z.object({
email: z.string().email(),
name: z.string().min(2),
age: z.number().min(18).optional(),
});
// Infer TypeScript type
export type User = z.infer;
// Use for validation
const user = UserSchema.parse(untrustedData);
### tRPC Procedures
typescript
// src/lib/api/routers/user.ts
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure } from '../trpc';
export const userRouter = createTRPCRouter({
list: protectedProcedure
.query(async ({ ctx }) => {
return await ctx.db.query.users.findMany();
}),
create: protectedProcedure
.input(UserSchema)
.mutation(async ({ ctx, input }) => {
return await ctx.db.insert(users).values(input);
}),
});
## Error Handling
### Client-Side
typescript
'use client';
import { trpc } from '@/lib/trpc/client';
import { toast } from 'sonner';
function MyComponent() {
const mutation = trpc.users.create.useMutation({
onSuccess: () => {
toast.success('User created');
},
onError: (error) => {
// Error is fully typed
toast.error(error.message);
},
});
return mutation.mutate({ ... })}>Create;
}
### Server-Side
typescript
import { TRPCError } from '@trpc/server';
export const userRouter = createTRPCRouter({
delete: protectedProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
const user = await ctx.db.query.users.findFirst({
where: eq(users.id, input.id)
});
if (!user) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'User not found',
});
}
// Perform deletion...
}),
});
## Testing Standards
### Unit Tests (Vitest)
typescript
// src/lib/utils/format.test.ts
import { describe, it, expect } from 'vitest';
import { formatCurrency } from './format';
describe('formatCurrency', () => {
it('formats USD correctly', () => {
expect(formatCurrency(1234.56, 'USD')).toBe('$1,234.56');
});
it('handles zero', () => {
expect(formatCurrency(0, 'USD')).toBe('$0.00');
});
});
### Component Tests
typescript
// src/components/UserCard.test.tsx
import { render, screen } from '@testing-library/react';
import { UserCard } from './UserCard';
it('displays user name', () => {
const user = { id: '1', name: 'John Doe', email: 'john@example.com' };
render();
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
### E2E Tests (Playwright)
typescript
// tests/e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
test('user can sign in', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
## Common Commands
- `pnpm dev` - Start dev server (localhost:3000)
- `pnpm build` - Production build
- `pnpm lint` - Run ESLint
- `pnpm type-check` - TypeScript check
- `pnpm test` - Run Vitest unit tests
- `pnpm test:e2e` - Run Playwright E2E tests
- `pnpm db:push` - Push schema to database
- `pnpm db:studio` - Open Drizzle Studio
## Known Issues
### Hot Reload with tRPC
**Symptom**: tRPC client doesn't update after router changes
**Solution**: Restart dev server when adding/removing procedures
### Server Action Hydration
**Symptom**: "Text content did not match" errors
**Solution**: Ensure Server Actions are truly async and await all promises
### Image Optimization
**Symptom**: Large images slow down page loads
**Solution**: Always use Next.js `<Image>` component with `sizes` prop
tsx
import Image from 'next/image';
src="/photo.jpg"
alt="Description"
width={800}
height={600}
sizes="(max-width: 768px) 100vw, 50vw"
/>
## Deployment Checklist
Before deploying:
1. ✅ Run `pnpm build` locally (catches build errors)
2. ✅ Verify environment variables in Vercel dashboard
3. ✅ Test database migrations in staging
4. ✅ Check API rate limits and timeouts
5. ✅ Review bundle size (`pnpm build` shows size)
Vercel config:
- Build command: `pnpm build`
- Output directory: `.next`
- Install command: `pnpm install`
- Development command: `pnpm dev`
4.2 Windsurf Configuration
Windsurf (from Codeium) introduced Wave 8 in May 2025, which fundamentally changed its rules architecture from single-file simplicity to a sophisticated directory-based system with GUI-managed activation modes.
4.2.1 Wave 8+ Architecture (Current)
Core Concepts:
-
Directory-based:
.windsurf/rules/*.md(multiple focused files) - GUI-managed: Rules configured via Settings UI, not just file editing
- Activation modes: Four distinct triggers (Manual, Always On, Model Decision, Glob Pattern)
- Character limits: 6,000 chars per file, 12,000 total combined
File Structure:
my-project/
├── .windsurf/
│ └── rules/
│ ├── 01-project-overview.md # Always On
│ ├── 02-architecture.md # Always On
│ ├── 03-typescript-rules.md # Glob: **/*.{ts,tsx}
│ ├── 04-python-rules.md # Glob: **/*.py
│ ├── 05-testing-guide.md # Manual (on demand)
│ └── 06-security-review.md # Model Decision
├── .windsurfrules # Legacy single-file (still works)
└── src/
Global Rules Location:
~/.codeium/windsurf/memories/global_rules.md
4.2.2 YAML Frontmatter + Markdown Body Format
While Windsurf primarily uses plain Markdown, you can include optional YAML frontmatter for metadata:
---
version: 1.2.0
priority: high
applies_to: ["src/**/*.ts", "app/**/*.tsx"]
last_updated: 2025-11-29
---
# TypeScript Coding Standards
## Type Safety Requirements
### Strict Mode Configuration
Ensure `tsconfig.json` has:
json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUncheckedIndexedAccess": true
}
}
### Type Annotations
typescript
// ✅ ALWAYS: Explicit return types
function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ NEVER: Implicit return type
function calculateTotal(items: CartItem[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
### Type Guards
typescript
// ✅ Use type predicates for narrowing
function isError(value: unknown): value is Error {
return value instanceof Error;
}
async function fetchData() {
try {
const data = await api.get('/data');
return { success: true, data };
} catch (error) {
if (isError(error)) {
return { success: false, error: error.message };
}
return { success: false, error: 'Unknown error occurred' };
}
}
## Naming Conventions
| Element | Convention | Example |
|---------|-----------|---------|
| Variables | camelCase | `userName`, `isLoading` |
| Functions | camelCase | `fetchUser()`, `calculateTotal()` |
| Types/Interfaces | PascalCase | `User`, `ApiResponse<T>` |
| Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT`, `API_BASE_URL` |
| Files | kebab-case | `user-profile.tsx`, `api-client.ts` |
## Import Organization
typescript
// 1. External dependencies (React, libraries)
import { useState, useEffect } from 'react';
import { z } from 'zod';
// 2. Internal modules (@ alias imports)
import { Button } from '@/components/ui/button';
import { trpc } from '@/lib/trpc/client';
// 3. Relative imports
import { UserCard } from './UserCard';
import type { User } from './types';
// 4. CSS/styles (if any)
import './styles.css';
YAML Frontmatter Fields (Optional):
| Field | Purpose | Example |
|---|---|---|
version |
Track rule file versions | 1.2.0 |
priority |
Conflict resolution |
high, medium, low
|
applies_to |
File pattern hints | ["src/**/*.ts"] |
last_updated |
Maintenance tracking | 2025-11-29 |
Note: Frontmatter is informational—Windsurf doesn't programmatically enforce these fields. Activation is controlled through the GUI.
4.2.3 Activation Modes (Wave 8 Feature)
How to Configure:
- Open Windsurf
- Navigate to: Settings → Rules
- Click on any rule file
- Select activation mode from dropdown
The Four Modes:
1. Always On
- Behavior: Rule is included in every Cascade interaction
- Use for: Core project standards, universal conventions
- Token impact: High (always loaded)
-
Example files:
01-project-overview.md,02-architecture.md
# 01-project-overview.md (Always On)
# Project: E-Commerce Platform
**Tech Stack**: Next.js 15, TypeScript, PostgreSQL, tRPC
**Architecture**: Monorepo managed by Turborepo
**Deployment**: Vercel (production), Railway (staging)
## Universal Standards
- All commits follow Conventional Commits format
- All PRs require at least one approval
- Code coverage must remain above 80%
- Environment variables never committed to git
2. Glob Pattern
- Behavior: Rule applies when working with files matching pattern
- Use for: Language-specific or directory-specific rules
- Token impact: Medium (conditional loading)
- Pattern syntax: Standard glob (gitignore-style)
# 03-typescript-rules.md (Glob: **/*.{ts,tsx})
# TypeScript-Specific Rules
These rules apply ONLY to TypeScript files.
## Async Patterns
Always use async/await, never Promise.then():
typescript
// ✅ CORRECT
async function fetchUser(id: string) {
try {
const user = await api.get(/users/${id});
return user;
} catch (error) {
logger.error('Failed to fetch user', { id, error });
throw new AppError('User fetch failed', 500);
}
}
// ❌ WRONG
function fetchUser(id: string) {
return api.get(/users/${id})
.then(user => user)
.catch(error => {
console.log(error); // Bad error handling
});
}
Glob Pattern Examples:
| Pattern | Matches | Doesn't Match |
|---|---|---|
**/*.ts |
src/app.ts, lib/utils.ts
|
src/app.tsx, src/app.js
|
**/*.{ts,tsx} |
src/app.ts, src/Button.tsx
|
src/app.js |
src/** |
src/app.ts, src/lib/utils.ts
|
tests/app.test.ts |
!**/*.test.ts |
Exclude test files | |
{frontend,backend}/** |
Both frontend/ and backend/ | Other dirs |
3. Model Decision
- Behavior: Cascade (AI) decides when to apply based on description
- Use for: Context-specific guidelines (security, performance, migrations)
- Token impact: Low (only when relevant)
- Requires: Clear natural language description of when to use
Configuration in GUI:
Activation Mode: Model Decision
Description: "Apply when the user's request involves authentication,
user data handling, or API security concerns"
File Content:
# 06-security-review.md (Model Decision)
# Security Guidelines
**When to use this rule**: Authentication, authorization, data validation,
API security, credential management, or reviewing code for security issues.
## Authentication Best Practices
### JWT Handling
typescript
// ✅ CORRECT: HttpOnly cookies
export async function setAuthTokens(
res: Response,
accessToken: string,
refreshToken: string
) {
res.cookie('accessToken', accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 15 * 60 * 1000, // 15 minutes
});
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
});
}
// ❌ NEVER: localStorage or sessionStorage
localStorage.setItem('token', accessToken); // Vulnerable to XSS
### Input Validation
ALWAYS validate and sanitize:
typescript
import { z } from 'zod';
const LoginSchema = z.object({
email: z.string().email().max(255),
password: z.string().min(12).max(128),
});
export async function login(req: Request) {
// Parse and validate
const body = LoginSchema.parse(req.body);
// body is now type-safe and validated
const user = await authenticateUser(body.email, body.password);
}
### SQL Injection Prevention
typescript
// ✅ CORRECT: Parameterized queries
const user = await db
.select()
.from(users)
.where(eq(users.email, email)) // Drizzle ORM handles escaping
.limit(1);
// ❌ NEVER: String concatenation
const query = SELECT * FROM users WHERE email = '${email}'; // SQL injection!
## Secrets Management
### Environment Variables
typescript
// ✅ Validate environment variables at startup
import { z } from 'zod';
const envSchema = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
API_KEY: z.string().min(20),
});
export const env = envSchema.parse(process.env);
// ❌ NEVER hardcode or commit secrets
const apiKey = 'sk_live_abcd1234...'; // SECURITY VIOLATION
### API Key Storage
- Use secret management (Vercel Environment Variables, AWS Secrets Manager)
- Rotate keys every 90 days
- Use different keys for dev/staging/production
## Common Vulnerabilities Checklist
When reviewing code, check for:
- [ ] XSS: Is user input sanitized before rendering?
- [ ] SQL Injection: Are queries parameterized?
- [ ] CSRF: Are state-changing endpoints protected?
- [ ] Open Redirect: Are redirect URLs validated?
- [ ] Rate Limiting: Are public endpoints protected?
4. Manual
- Behavior: Rule only applies when explicitly mentioned in chat
- Use for: Specialized workflows, testing guidelines, migration procedures
- Token impact: None (unless invoked)
-
Invocation:
@rulenameor via dropdown menu
# 05-testing-guide.md (Manual)
# Comprehensive Testing Guide
**Usage**: Invoke this rule with @testing-guide when writing or updating tests.
## Test Structure (AAA Pattern)
typescript
import { describe, it, expect, beforeEach, vi } from 'vitest';
describe('UserService', () => {
let service: UserService;
let mockDb: MockDatabase;
beforeEach(() => {
// ARRANGE: Set up test environment
mockDb = createMockDatabase();
service = new UserService(mockDb);
});
it('creates user with valid data', async () => {
// ARRANGE: Prepare test data
const userData = {
email: 'test@example.com',
name: 'Test User',
};
// ACT: Execute the function being tested
const result = await service.createUser(userData);
// ASSERT: Verify the outcome
expect(result.success).toBe(true);
expect(result.data).toMatchObject(userData);
expect(mockDb.insert).toHaveBeenCalledOnce();
});
it('handles validation errors', async () => {
// ARRANGE: Invalid data
const invalidData = {
email: 'not-an-email',
name: '',
};
// ACT
const result = await service.createUser(invalidData);
// ASSERT
expect(result.success).toBe(false);
expect(result.error).toContain('validation');
});
});
## Mocking Strategies
### Mock External APIs
typescript
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('https://api.example.com/users', (req, res, ctx) => {
return res(
ctx.json([
{ id: '1', name: 'User 1' },
{ id: '2', name: 'User 2' },
])
);
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
### Mock Database
typescript
const mockDb = {
query: {
users: {
findFirst: vi.fn(),
findMany: vi.fn(),
},
},
insert: vi.fn(),
update: vi.fn(),
};
## Coverage Requirements
| Test Type | Target Coverage | Command |
|-----------|----------------|---------|
| Unit | 80% minimum | `pnpm test` |
| Integration | 70% minimum | `pnpm test:integration` |
| E2E | Critical paths only | `pnpm test:e2e` |
## Test Naming Convention
typescript
// Pattern: "should [expected behavior] when [condition]"
it('should return user when valid ID provided', async () => { ... });
it('should throw error when user not found', async () => { ... });
it('should validate email format before saving', async () => { ... });
## E2E Test Template (Playwright)
typescript
import { test, expect } from '@playwright/test';
test.describe('User Authentication Flow', () => {
test('should complete signup and login', async ({ page }) => {
// Sign up
await page.goto('/signup');
await page.fill('[name="email"]', 'newuser@example.com');
await page.fill('[name="password"]', 'SecurePassword123!');
await page.fill('[name="confirmPassword"]', 'SecurePassword123!');
await page.click('button[type="submit"]');
// Verify redirect to dashboard
await expect(page).toHaveURL('/dashboard');
// Log out
await page.click('[data-testid="user-menu"]');
await page.click('text=Logout');
// Log back in
await page.goto('/login');
await page.fill('[name="email"]', 'newuser@example.com');
await page.fill('[name="password"]', 'SecurePassword123!');
await page.click('button[type="submit"]');
// Verify successful login
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('text=Welcome back')).toBeVisible();
});
});
4.2.4 Character Limits and Token Management
Hard Limits:
- Per file: 6,000 characters maximum
- Total combined: 12,000 characters (global + workspace rules)
- What happens if exceeded: Content is silently truncated—no warning
Token Efficiency Strategies:
Strategy 1: XML-Style Tags for Dense Organization
# Comprehensive Project Rules (4,800 chars - within limit)
<projectContext>
**Stack**: Next.js 15, TypeScript, PostgreSQL, tRPC
**Team Size**: 8 engineers
**Deployment**: Vercel
</projectContext>
<codingStandards>
- TypeScript strict mode
- Functional components only
- Zod for validation
- TanStack Query for server state
</codingStandards>
<architecturePatterns>
## Feature-Sliced Design
src/
├── features/
│ ├── auth/
│ ├── users/
│ └── products/
</architecturePatterns>
<errorHandling>
## Pattern
typescript
try {
await operation();
} catch (error) {
logger.error('Context', { error });
throw new AppError('User message', 500);
}
</errorHandling>
<safetyRestrictions>
- NEVER commit secrets
- ALWAYS validate inputs
- NO console.log in production
</safetyRestrictions>
<testingRequirements>
- 80% coverage minimum
- Unit: Vitest
- E2E: Playwright
</testingRequirements>
Why XML tags work:
- Clear semantic boundaries
- Easy to scan (both human and AI)
- Sections can be rearranged without breaking structure
- Reduces need for verbose headers
Strategy 2: Reference External Docs
# Project Rules (Main File - 2,000 chars)
<overview>
Next.js 15 e-commerce platform. See architecture details: @docs/architecture.md
</overview>
<standards>
For complete TypeScript rules: @docs/typescript.md
For API patterns: @docs/api-conventions.md
For testing guide: @docs/testing.md
</standards>
<quickReference>
## Most Critical Rules
1. TypeScript strict mode
2. Server Components by default
3. Zod for all validation
4. TanStack Query for data fetching
5. Error boundary on every route
</quickReference>
Strategy 3: Multi-File Modular Approach
Instead of one 10,000 char file:
.windsurf/rules/
├── 01-core.md (1,500 chars - Always On)
├── 02-typescript.md (2,000 chars - Glob: **/*.ts)
├── 03-react.md (2,000 chars - Glob: **/*.tsx)
├── 04-testing.md (2,500 chars - Manual)
└── 05-security.md (2,000 chars - Model Decision)
Total: 10,000 chars, but max 3,500 loaded at once
4.2.5 Production Examples
Example 1: SaaS Application (Next.js + Supabase)
---
version: 1.0.0
applies_to: ["app/**/*", "components/**/*"]
---
# SaaS Application Rules
<projectContext>
**Product**: Team collaboration platform
**Stack**: Next.js 15, TypeScript, Supabase, Tailwind
**Architecture**: Multi-tenant SaaS
**Auth**: Supabase Auth with Row-Level Security (RLS)
</projectContext>
<multiTenancy>
## Every Query Must Include Tenant Context
typescript
// ✅ CORRECT: Filter by org_id
const projects = await supabase
.from('projects')
.select('*')
.eq('org_id', user.org_id); // Always filter by organization
// ❌ NEVER: Query without tenant filter
const allProjects = await supabase
.from('projects')
.select('*'); // SECURITY RISK: Leaks data across orgs
## Database Policies
All tables have RLS enabled:
sql
CREATE POLICY "Users can only see their org data"
ON projects FOR SELECT
USING (org_id = auth.current_org_id());
</multiTenancy>
<componentPatterns>
## Server Component Pattern
tsx
// app/dashboard/page.tsx
export default async function DashboardPage() {
const supabase = createServerComponentClient();
const { data: projects } = await supabase
.from('projects')
.select('*')
.order('created_at', { ascending: false });
return ;
}
## Client Component Pattern
tsx
'use client';
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
export function CreateProject() {
const supabase = createClientComponentClient();
async function handleCreate(data: FormData) {
const { error } = await supabase
.from('projects')
.insert({ name: data.get('name'), org_id: user.org_id });
if (error) toast.error(error.message);
}
return
...;}
</componentPatterns>
<authPatterns>
## Protect Server Components
tsx
import { redirect } from 'next/navigation';
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
export default async function ProtectedPage() {
const supabase = createServerComponentClient();
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
redirect('/login');
}
// Page content...
}
## Middleware for Route Protection
typescript
// middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs';
export async function middleware(req: NextRequest) {
const res = NextResponse.next();
const supabase = createMiddlewareClient({ req, res });
const { data: { session } } = await supabase.auth.getSession();
// Protect /dashboard routes
if (req.nextUrl.pathname.startsWith('/dashboard') && !session) {
return NextResponse.redirect(new URL('/login', req.url));
}
return res;
}
</authPatterns>
<realtimePatterns>
## Subscribe to Changes
typescript
'use client';
useEffect(() => {
const channel = supabase
.channel('projects-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'projects',
filter: org_id=eq.${orgId},
},
(payload) => {
// Handle realtime update
queryClient.invalidateQueries(['projects']);
}
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [orgId]);
</realtimePatterns>
<deploymentChecklist>
Before deploying:
1. ✅ RLS enabled on all tables
2. ✅ Org_id filter in all queries
3. ✅ Environment variables in Vercel
4. ✅ Database migrations applied
5. ✅ Supabase Edge Functions deployed
</deploymentChecklist>
Example 2: FastAPI Microservice
---
version: 2.1.0
applies_to: ["app/**/*.py", "tests/**/*.py"]
---
# FastAPI Microservice Rules
<architecture>
**Pattern**: Hexagonal Architecture
**Database**: PostgreSQL with AsyncPG
**Messaging**: RabbitMQ
**Observability**: OpenTelemetry + Grafana
</architecture>
<directoryStructure>
app/
├── api/
│ ├── routes/ # FastAPI routers
│ └── dependencies.py # Dependency injection
├── core/
│ ├── config.py # Settings management
│ └── security.py # Auth utilities
├── domain/
│ ├── models/ # Business entities
│ ├── services/ # Business logic
│ └── repositories/ # Data access interface
├── infrastructure/
│ ├── database/ # Database implementation
│ ├── messaging/ # RabbitMQ implementation
│ └── external_apis/ # External service clients
└── tests/
├── unit/
├── integration/
└── e2e/
</directoryStructure>
<dependencyInjection>
## FastAPI Dependencies Pattern
python
app/api/dependencies.py
from typing import Annotated
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.infrastructure.database import get_db_session
Type alias for cleaner route signatures
DbSession = Annotated[AsyncSession, Depends(get_db_session)]
app/api/routes/users.py
from fastapi import APIRouter
from app.api.dependencies import DbSession
from app.domain.services import UserService
router = APIRouter()
@router.get("/users/{user_id}")
async def get_user(
user_id: str,
db: DbSession, # Auto-injected
current_user: CurrentUser, # Auth dependency
) -> UserResponse:
service = UserService(db)
user = await service.get_by_id(user_id)
return UserResponse.from_domain(user)
</dependencyInjection>
<repositoryPattern>
## Abstract Repository Interface
python
app/domain/repositories/user_repository.py
from abc import ABC, abstractmethod
from typing import Optional
from app.domain.models import User
class UserRepository(ABC):
@abstractmethod
async def get_by_id(self, user_id: str) -> Optional[User]:
pass
@abstractmethod
async def save(self, user: User) -> User:
pass
app/infrastructure/database/repositories/user_repository_impl.py
from sqlalchemy import select
from app.domain.repositories import UserRepository
class UserRepositoryImpl(UserRepository):
def init(self, session: AsyncSession):
self.session = session
async def get_by_id(self, user_id: str) -> Optional[User]:
stmt = select(User).where(User.id == user_id)
result = await self.session.execute(stmt)
return result.scalar_one_or_none()
async def save(self, user: User) -> User:
self.session.add(user)
await self.session.commit()
await self.session.refresh(user)
return user
</repositoryPattern>
<errorHandling>
## Custom Exception Hierarchy
python
app/core/exceptions.py
class AppException(Exception):
"""Base application exception"""
def init(self, message: str, status_code: int = 500):
self.message = message
self.status_code = status_code
super().init(message)
class NotFoundError(AppException):
def init(self, entity: str, entity_id: str):
super().init(
f"{entity} with id {entity_id} not found",
status_code=404
)
class ValidationError(AppException):
def init(self, message: str):
super().init(message, status_code=400)
app/api/exception_handlers.py
from fastapi import Request, status
from fastapi.responses import JSONResponse
@app.exception_handler(AppException)
async def app_exception_handler(request: Request, exc: AppException):
return JSONResponse(
status_code=exc.status_code,
content={
"error": True,
"code": exc.class.name,
"message": exc.message,
},
)
Usage in routes
@router.get("/users/{user_id}")
async def get_user(user_id: str, db: DbSession):
user = await service.get_by_id(user_id)
if not user:
raise NotFoundError("User", user_id)
return UserResponse.from_domain(user)
</errorHandling>
<asyncPatterns>
## Database Session Management
python
✅ CORRECT: Async context manager
async with get_db_session() as session:
result = await session.execute(query)
await session.commit()
❌ NEVER: Forget to await
session = get_db_session() # Missing await!
result = session.execute(query) # Will fail
Concurrent Operations
import asyncio
# ✅ Parallel independent queries
user, posts, comments = await asyncio.gather(
user_repo.get_by_id(user_id),
post_repo.get_by_user(user_id),
comment_repo.get_by_user(user_id),
)
# ❌ Sequential when could be parallel
user = await user_repo.get_by_id(user_id)
posts = await post_repo.get_by_user(user_id) # Waited unnecessarily
comments = await comment_repo.get_by_user(user_id)
Pytest Fixtures
# tests/conftest.py
import pytest
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from app.infrastructure.database import Base
@pytest.fixture
async def db_session():
engine = create_async_engine("postgresql+asyncpg://test")
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async with AsyncSession(engine) as session:
yield session
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest.fixture
def user_service(db_session):
repo = UserRepositoryImpl(db_session)
return UserService(repo)
# tests/unit/services/test_user_service.py
@pytest.mark.asyncio
async def test_create_user(user_service):
user_data = {"email": "test@example.com", "name": "Test"}
user = await user_service.create(user_data)
assert user.id is not None
assert user.email == "test@example.com"
---
### **4.3 Claude Code (CLAUDE.md)**
Claude Code revolutionized AI coding by introducing **persistent project memory** through the `CLAUDE.md` file, which automatically loads at session start without requiring repeated explanations.
#### **4.3.1 File Location Hierarchy**
**Search Order (Priority):**
1. **User-level global** (personal preferences):
~/.claude/CLAUDE.md
- Applies to ALL projects
- Your universal coding style, preferences, response format
2. **Project root** (team-shared):
./CLAUDE.md
or
./.claude/CLAUDE.md
- Project-specific standards
- Version controlled, shared with team
3. **Subdirectory** (module-specific):
./packages/frontend/CLAUDE.md
./services/api/CLAUDE.md
- Loaded on-demand when working in that directory
- Useful for monorepos with different conventions per package
4. **Organization-level** (Enterprise Linux/Unix):
/etc/claude-code/CLAUDE.md
- Centralized company-wide policies
- Requires admin setup
**Loading Behavior:**
- Claude starts at current working directory
- Recurses **UP** the filesystem tree loading all CLAUDE.md files
- Combines them (more specific overrides more general)
- For subdirectories, loads on-demand when reading files in that subtree
**Practical Example:**
my-monorepo/
├── CLAUDE.md # Loaded: Universal monorepo rules
├── packages/
│ ├── frontend/
│ │ ├── CLAUDE.md # Loaded: When editing frontend files
│ │ └── src/
│ └── backend/
│ ├── CLAUDE.md # Loaded: When editing backend files
│ └── src/
└── ~/.claude/CLAUDE.md # Loaded: Your personal preferences
If you edit `packages/frontend/src/App.tsx`, Claude loads:
1. `~/.claude/CLAUDE.md` (your preferences)
2. `my-monorepo/CLAUDE.md` (universal rules)
3. `my-monorepo/packages/frontend/CLAUDE.md` (frontend-specific)
#### **4.3.2 The Canonical 8-Section Structure**
Based on Anthropic's internal usage and community best practices:
markdown
[Project Name] - [One-line Description]
Version: 1.0.0
Last Updated: 2025-11-29
Maintainer: @username
1. PROJECT OVERVIEW (30-100 words)
Brief description of what this project does and its primary goal.
Example:
Full-stack e-commerce platform built with Next.js and PostgreSQL.
Supports multi-tenant SaaS model with row-level security.
Primary goal: Handle 10k concurrent users with <100ms API response times.
2. TECHNOLOGY STACK
Frontend
- Framework: Next.js 15.1 (App Router)
- Language: TypeScript 5.4 (strict mode)
- Styling: Tailwind CSS 4.0, shadcn/ui
-
State Management:
- Server State: TanStack Query v5
- Client State: Zustand 4.5
- Form State: React Hook Form 7.5 + Zod 3.23
Backend
- Runtime: Node.js 20.11 LTS
- Framework: Express.js + tRPC 11
- Database: PostgreSQL 16
- ORM: Drizzle ORM 0.35
- Authentication: NextAuth.js v5
DevOps
- Package Manager: pnpm 9.0 (NOT npm/yarn)
- Monorepo: Turborepo 2.0
- Testing: Vitest 2.0, Playwright 1.45
- CI/CD: GitHub Actions
- Hosting: Vercel (prod), Railway (staging)
3. PROJECT STRUCTURE
root/
├── apps/
│ ├── web/ # Next.js frontend
│ │ ├── app/ # App Router pages
│ │ ├── components/ # React components
│ │ └── lib/ # Client utilities
│ └── api/ # Standalone API (if separate)
├── packages/
│ ├── ui/ # Shared component library
│ ├── database/ # Drizzle schema & migrations
│ ├── types/ # Shared TypeScript types
│ └── config/ # Shared configs (ESLint, TS, etc.)
├── docs/ # Documentation
├── scripts/ # Build/deployment scripts
└── .github/ # CI/CD workflows
Key Directories:
-
apps/web/app/: Next.js routes using App Router (NOT Pages Router) -
apps/web/components/ui/: shadcn/ui primitives -
apps/web/components/features/: Feature-specific components -
packages/database/: Drizzle schema definitions -
apps/web/lib/api/: tRPC routers and procedures
4. CODING STANDARDS
TypeScript Rules
Type Safety
// ✅ ALWAYS: Explicit return types on functions
export function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ NEVER: Implicit any or missing return type
export function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Error Handling
// ✅ CORRECT: Explicit try-catch with logging
export async function fetchUser(id: string): Promise<Result<User>> {
try {
const user = await db.query.users.findFirst({
where: eq(users.id, id)
});
if (!user) {
return { success: false, error: 'User not found' };
}
return { success: true, data: user };
} catch (error) {
logger.error('Failed to fetch user', { id, error });
return { success: false, error: 'Database error' };
}
}
// ❌ NEVER: Silent error swallowing
export async function fetchUser(id: string) {
try {
return await db.query.users.findFirst({ where: eq(users.id, id) });
} catch {
return null; // Silent failure!
}
}
React Patterns
Component Structure
// ✅ PREFERRED: Named export with TypeScript interface
interface UserCardProps {
user: User;
onEdit: (id: string) => void;
className?: string;
}
export function UserCard({ user, onEdit, className }: UserCardProps) {
return (
<div className={cn('rounded-lg border p-4', className)}>
<h3>{user.name}</h3>
<Button onClick={() => onEdit(user.id)}>Edit</Button>
</div>
);
}
// ❌ AVOID: Default export with inline types
export default function UserCard({ user, onEdit }: {
user: any;
onEdit: Function;
}) {
return <div>{user.name}</div>;
}
Server vs Client Components
// ✅ Server Component (default - no directive)
async function UsersPage() {
// Can directly query database in Server Components
const users = await db.query.users.findMany();
return <UserList users={users} />;
}
// ✅ Client Component (only when needed)
'use client';
import { useState } from 'react';
export function InteractiveCounter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
// ❌ WRONG: Adding 'use client' unnecessarily
'use client';
export function StaticHeader() {
return <h1>Welcome</h1>; // No interactivity - should be Server Component
}
Import Organization
// 1. React and third-party libraries
import { useState, useEffect } from 'react';
import { z } from 'zod';
// 2. Internal packages (monorepo)
import { Button } from '@repo/ui';
import { User } from '@repo/types';
// 3. Application modules (@ alias)
import { trpc } from '@/lib/trpc/client';
import { cn } from '@/lib/utils';
// 4. Relative imports
import { UserCard } from './UserCard';
import type { UserCardProps } from './types';
// 5. Styles (if any)
import './styles.css';
5. COMMON COMMANDS
Development
pnpm dev # Start dev server (localhost:3000)
pnpm dev --filter=web # Start only web app (monorepo)
pnpm build # Production build
pnpm lint # Run ESLint
pnpm type-check # TypeScript validation
pnpm format # Run Prettier
Testing
pnpm test # Run Vitest unit tests
pnpm test:watch # Watch mode
pnpm test:coverage # Generate coverage report
pnpm test:e2e # Run Playwright E2E tests
pnpm test:e2e:ui # Playwright UI mode
Database
pnpm db:push # Push schema changes to database
pnpm db:studio # Open Drizzle Studio GUI
pnpm db:generate # Generate migration files
pnpm db:migrate # Apply migrations
pnpm db:seed # Seed database with test data
Deployment
pnpm deploy:staging # Deploy to Railway staging
pnpm deploy:production # Deploy to Vercel production
6. TESTING REQUIREMENTS
Unit Tests (Vitest)
-
Location:
src/**/*.test.ts(colocated with source) - Coverage: Minimum 80% for business logic
- Run before commit: Pre-commit hook runs affected tests
import { describe, it, expect, beforeEach } from 'vitest';
import { calculateDiscount } from './pricing';
describe('calculateDiscount', () => {
it('applies 10% discount for orders over $100', () => {
expect(calculateDiscount(150)).toBe(15);
});
it('returns 0 for orders under $100', () => {
expect(calculateDiscount(50)).toBe(0);
});
it('handles edge case of exactly $100', () => {
expect(calculateDiscount(100)).toBe(0);
});
});
Integration Tests
-
Location:
tests/integration/ - Focus: API endpoints, database operations
- Use: MSW for mocking external APIs
E2E Tests (Playwright)
-
Location:
tests/e2e/ - Focus: Critical user flows only
- Run: On PR to main branch
import { test, expect } from '@playwright/test';
test('user can complete checkout', async ({ page }) => {
await page.goto('/shop');
// Add items to cart
await page.click('[data-testid="add-to-cart-1"]');
await page.click('[data-testid="add-to-cart-2"]');
// Navigate to checkout
await page.click('[data-testid="cart-button"]');
await page.click('text=Checkout');
// Fill shipping info
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="address"]', '123 Main St');
// Complete purchase
await page.click('button:has-text("Place Order")');
// Verify success
await expect(page.locator('text=Order confirmed')).toBeVisible();
});
7. KNOWN ISSUES & PITFALLS
Issue 1: Hot Reload with tRPC
Symptom: Changes to tRPC routers don't reflect in frontend
Cause: tRPC type generation lags behind file changes
Solution: Restart dev server when adding/removing procedures
Tracking: Issue #123
Issue 2: Server Component Hydration Errors
Symptom: "Text content did not match" errors in console
Cause: Mixing server/client rendering incorrectly
Solution:
// ✅ Ensure async server components await all data
async function UserPage() {
const user = await fetchUser(); // Must await
return <div>{user.name}</div>;
}
// ❌ Don't mix server async with client hooks
async function UserPage() {
const [data, setData] = useState(); // ERROR: Can't use hooks in Server Component
const user = await fetchUser();
return <div>{user.name}</div>;
}
Issue 3: Drizzle Query Performance
Symptom: Slow queries for user dashboards (>2 seconds)
Cause: N+1 query problem with relations
Solution: Use Drizzle's with for eager loading
// ✅ Single query with eager loading
const users = await db.query.users.findMany({
with: {
posts: true,
profile: true,
},
});
// ❌ N+1: Queries once per user
const users = await db.query.users.findMany();
for (const user of users) {
user.posts = await db.query.posts.findMany({
where: eq(posts.userId, user.id)
});
}
Issue 4: Environment Variables in Client Components
Symptom: process.env.NEXT_PUBLIC_API_URL is undefined
Cause: Forgot NEXT_PUBLIC_ prefix for client-side env vars
Solution: All client-accessible env vars MUST have prefix
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com # ✅ Available client-side
DATABASE_URL=postgresql://... # ❌ Server-only
8. IMPORTANT NOTES
API Rate Limiting
- Public endpoints: 100 requests/minute per IP
- Authenticated: 1000 requests/minute per user
- Implemented via: Upstash Redis rate limiter
Authentication
- JWT Expiration: Access tokens expire after 15 minutes
- Refresh Tokens: Valid for 7 days, rotated on use
- Storage: HttpOnly cookies (NEVER localStorage)
Database Constraints
- Max connections: 20 (PgBouncer pool)
- Query timeout: 30 seconds
- Row-level security: Enabled on all tables
File Uploads
- Max size: 10MB per file
- Allowed types: Images (JPEG, PNG, WebP), PDFs
- Storage: Cloudinary (dev/staging), AWS S3 (production)
Deployment
-
Auto-deploy: Merges to
maintrigger production deploy - Preview: All PRs get unique preview URL
- Rollback: Use Vercel dashboard for instant rollback
Monitoring
- Errors: Sentry for error tracking
- Performance: Vercel Analytics
- Logs: Axiom for centralized logging
Additional Context Files
For detailed information, reference these files:
- Architecture diagrams: @docs/architecture.md
- API documentation: @docs/api-reference.md
- Database schema: @packages/database/schema.ts
- Deployment guide: @docs/deployment.md
How to Update This File
-
When to update:
- Tech stack changes (new library, version upgrade)
- New coding patterns emerge
- Known issues discovered
- Team conventions evolve
-
Process:
- Create PR with changes to CLAUDE.md
- Require 1 team approval
- Update
Last Updateddate - Increment version (semver)
-
Keep concise:
- Target: Under 50KB (this file: ~12KB)
- Link to external docs for details
- Focus on high-impact rules
This CLAUDE.md structure has been validated with 50+ production projects. It represents the optimal balance between comprehensiveness and brevity, providing Claude Code with maximum effectiveness while remaining maintainable.
---
### **4.4 Aider Configuration**
Aider pioneered the **convention file** approach, where a team-shared `CONVENTIONS.md` loaded via `.aider.conf.yml` provides persistent context without cluttering chat history.
#### **4.4.1 `.aider.conf.yml` Complete Schema**
Aider uses a **flat YAML structure** where keys match command-line flags. The config merges in this order:
1. `./.aider.conf.yml` (project root - highest priority)
2. Parent directories up to filesystem root
3. `~/.aider.conf.yml` (global user config)
4. Environment variables (`AIDER_MODEL`, etc.)
5. Command-line flags (override everything)
**Complete Configuration Reference:**
yaml
AIDER CONFIGURATION FILE
Location: .aider.conf.yml (project root)
Docs: https://aider.chat/docs/config.html
MODELS
Main chat model (does the coding)
model: claude-3-5-sonnet-20241022
Weak model for commit messages, file summaries
weak-model: gpt-4o-mini
Editor model for architect mode (optional)
editor-model: claude-3-5-sonnet-20241022
API configuration
api-key: sk-xxx # Don't commit! Use env vars instead
api-base: https://api.openai.com/v1
Model aliases for quick switching
alias:
- "sonnet:claude-3-5-sonnet-20241022"
- "gpt4:gpt-4o"
- "fast:gpt-4o-mini"
- "local:ollama/qwen2.5-coder"
EDIT BEHAVIOR
How AI returns code changes
edit-format: diff # Options: diff, diff-fenced, udiff, whole, editor-diff, editor-whole
Architect mode (two-step editing)
architect: false
auto-accept-architect: true
editor-edit-format: editor-diff
RULES & CONTEXT
Inline rules (short guidelines)
rules:
- "Use TypeScript strict mode for all code"
- "Follow functional programming patterns"
- "Prefer composition over inheritance"
- "Add JSDoc comments for public APIs"
PREFERRED: Load rules from external file
read:
- CONVENTIONS.md
- docs/architecture.md
- .editorconfig
Files to always include in chat (pre-added to context)
file:
- src/types/global.d.ts
- package.json
- tsconfig.json
REPOSITORY MAP
Tokens allocated to repository map (0 to disable)
map-tokens: 1024
When to refresh map
map-refresh: auto # Options: auto, always, files, manual
Multiplier when no files specified
map-multiplier-no-files: 2
GIT INTEGRATION
Auto-commit LLM changes
auto-commits: false
Allow commits when repo is dirty
dirty-commits: true
Attribution in commits
attribute-author: true # Git author name
attribute-committer: true # Git committer name
attribute-commit-message-author: false # Prefix with 'aider:'
attribute-commit-message-committer: false # Prefix all commits
attribute-co-authored-by: true # Use Co-authored-by trailer (takes precedence)
Custom commit message prompt
commit-prompt: "Write a concise commit message following Conventional Commits format"
Commit message language
commit-language: en
Skip pre-commit hooks
git-commit-verify: false
LINTING & TESTING
Auto-lint after changes
auto-lint: true
Lint commands by file type (quoted for multi-word commands)
lint-cmd:
- "python: ruff check --fix"
- "javascript: eslint --fix"
- "typescript: eslint --fix"
Auto-run tests after changes
auto-test: false
Test command
test-cmd: "pnpm test"
FILE MANAGEMENT
Aider ignore file
aiderignore: .aiderignore
Add gitignored files to editing scope
add-gitignore-files: false
Only consider files in current subtree
subtree-only: false
HISTORY & CACHING
Input history file
input-history-file: .aider.input.history
Chat history file
chat-history-file: .aider.chat.history.md
LLM conversation history (debugging)
llm-history-file: .aider.llm.history
Restore previous session
restore-chat-history: false
Max chat history before summarization
max-chat-history-tokens: 2048
Prompt caching (Anthropic models)
cache-prompts: true
Keep cache warm with pings (N pings, 5 min intervals)
cache-keepalive-pings: 12 # 12 pings = 60 minutes
OUTPUT & DISPLAY
Color scheme
dark-mode: true
Colorized output
pretty: true
Stream responses
stream: true
Show diffs when committing
show-diffs: false
Custom colors (hex codes)
user-input-color: "#00cc00"
tool-output-color: "#808080"
tool-error-color: "#FF2222"
assistant-output-color: "#0088ff"
Code theme (Pygments style)
code-theme: monokai
VOICE INPUT (Optional)
Voice format
voice-format: wav # Options: wav, webm, mp3
Voice language
voice-language: en
WATCH MODE (IDE Integration)
Watch for AI comments in files
watch-files: false
USER EXPERIENCE
Auto-accept all confirmations
yes-always: false
VI editing mode
vim: false
Enhanced input with history
fancy-input: true
Multi-line input
multiline: false
Terminal bell on completion
notifications: false
Custom notification command
notifications-command: "notify-send 'Aider' 'Task complete'"
Suggest shell commands
suggest-shell-commands: true
Detect and offer to add URLs
detect-urls: true
Encoding
encoding: utf-8
Line endings
line-endings: platform # Options: platform, unix, windows
ANALYTICS (Privacy)
Permanently disable analytics
analytics-disable: true
Disable for current session
analytics: false
Log analytics events to file (debugging)
analytics-log: analytics.log
ADVANCED
Environment file
env-file: .env
Execute commands from file on launch
load: startup.txt
Debugging
verbose: false
show-repo-map: false
show-prompts: false
dry-run: false
Exit after startup (testing)
exit: false
Skip sanity check of repository
skip-sanity-check-repo: false
Check for updates on launch
check-update: true
Show release notes on first run
show-release-notes: true
#### **4.4.2 Rules Array vs CONVENTIONS.md**
Aider supports two approaches for project-specific guidelines:
**Approach 1: Inline Rules (Quick and Simple)**
yaml
.aider.conf.yml
model: claude-3-5-sonnet-20241022
rules:
- "Use TypeScript strict mode"
- "Prefer async/await over .then()"
- "Always include error handling"
- "Add tests for new features"
**Pros:**
- Quick setup
- Config-only (no additional files)
- Good for 3-5 high-level rules
**Cons:**
- Limited space (YAML gets unwieldy)
- No rich formatting (code examples, tables)
- Hard to maintain as project grows
**Approach 2: External Conventions File (Recommended)**
yaml
.aider.conf.yml
model: claude-3-5-sonnet-20241022
read:
- CONVENTIONS.md
- docs/architecture.md
markdown
Project Coding Conventions
TypeScript Standards
Type Safety
All functions must have explicit return types:
// ✅ CORRECT
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ WRONG - implicit return type
function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Error Handling
// ✅ CORRECT - explicit handling
async function fetchData(): Promise<Result<Data>> {
try {
const response = await api.get('/data');
return { success: true, data: response.data };
} catch (error) {
logger.error('API fetch failed', { error });
return { success: false, error: 'Failed to fetch data' };
}
}
// ❌ WRONG - silent failure
async function fetchData() {
try {
return await api.get('/data');
} catch {
return null; // Silent failure!
}
}
Testing Requirements
Every new feature must include:
- Unit tests (Vitest)
- Integration tests (if touching API)
- E2E test (if user-facing)
Minimum coverage: 80% for business logic.
Commit Message Format
Follow Conventional Commits:
type(scope): description
feat(auth): add password reset flow
fix(api): handle null user in profile endpoint
docs(readme): update installation instructions
Types: feat, fix, docs, style, refactor, test, chore
**Pros:**
- **Rich formatting**: Markdown supports code blocks, tables, lists, headers
- **Detailed examples**: Show both good and bad patterns
- **Maintainable**: Easier to edit than YAML
- **Cached**: Aider caches conventions file, efficient token usage
- **Versionable**: Track changes in git with meaningful diffs
**Cons:**
- Requires separate file
- Can grow large (but that's also a pro—more detail)
**Best Practice: Hybrid Approach**
yaml
.aider.conf.yml
Short, high-priority rules inline
rules:
- "NEVER commit secrets or API keys"
- "ALWAYS run
pnpm lintbefore committing"
Detailed conventions in external file
read:
- CONVENTIONS.md
#### **4.4.3 Model Configuration and Aliases**
**Multi-Model Strategy:**
yaml
Main models
model: claude-3-5-sonnet-20241022 # Primary coding (most capable)
weak-model: gpt-4o-mini # Commit messages, summaries (cheap)
editor-model: gpt-4o # Architect mode editing (balanced)
Model aliases for quick switching
alias:
- "sonnet:claude-3-5-sonnet-20241022"
- "opus:claude-3-opus-20240229"
- "gpt4:gpt-4o"
- "fast:gpt-4o-mini"
- "local:ollama/deepseek-coder:6.7b"
Cost optimization: Use cheap model for routine tasks
weak-model: gpt-4o-mini # $0.15/1M tokens vs $3/1M for GPT-4o
**Using Aliases:**
bash
Switch models on the fly
aider --model sonnet
aider --model fast # Use for quick tasks
aider --model local # Use local model (no API costs)
**Advanced Model Settings (`.aider.model.settings.yml`):**
For custom or unknown models, create a separate model settings file:
yaml
.aider.model.settings.yml
name: openrouter/qwen/qwen2.5-coder-32b-instruct
edit_format: diff
weak_model_name: openrouter/qwen/qwen2.5-coder-32b-instruct
use_repo_map: true
editor_model_name: openrouter/anthropic/claude-3.7-sonnet
editor_edit_format: editor-diffname: ollama/deepseek-coder
edit_format: whole # Local models often better with whole-file edits
use_repo_map: false # Disable repo map (smaller context)
send_undo_reply: falsename: azure/gpt-4o
edit_format: diff
use_repo_map: true
max_chat_history_tokens: 4096
Reference in main config:
yaml
.aider.conf.yml
model-settings-file: .aider.model.settings.yml
model: openrouter/qwen/qwen2.5-coder-32b-instruct
#### **4.4.4 Production Configurations by Use Case**
**Use Case 1: Solo Developer (Cost-Conscious)**
yaml
.aider.conf.yml - Solo dev optimizing for cost
model: gpt-4o-mini # Cheapest capable model
weak-model: gpt-3.5-turbo # Even cheaper for summaries
Minimal features to reduce API calls
auto-commits: false # Manual review
auto-lint: false # Lint manually
auto-test: false # Test manually
map-tokens: 512 # Smaller repo map
Cache aggressively
cache-prompts: true
cache-keepalive-pings: 12
Show diffs for manual review
show-diffs: true
Rules
read:
- CONVENTIONS.md
Analytics
analytics-disable: true
**Use Case 2: Small Team (2-5 devs)**
yaml
.aider.conf.yml - Small team collaboration
model: claude-3-5-sonnet-20241022 # Best quality
weak-model: gpt-4o-mini # Cost-effective summaries
Team conventions
read:
- CONVENTIONS.md
- docs/architecture.md
Auto-quality checks
auto-lint: true
lint-cmd:
- "typescript: eslint --fix"
- "python: ruff check --fix"
Git attribution for team visibility
auto-commits: false # Review before commit
attribute-co-authored-by: true # Show Aider helped
Pre-load shared types
file:
- src/types/global.d.ts
- package.json
Shared ignore patterns
aiderignore: .aiderignore
Cache for efficiency
cache-prompts: true
analytics-disable: true
**`.aiderignore` (shared with team):**
gitignore
.aiderignore - Exclude from Aider context
Dependencies
node_modules/
venv/
.venv/
pycache/
Build outputs
dist/
build/
out/
.next/
target/
IDE files
.vscode/
.idea/
*.swp
Logs
*.log
logs/
Large data files
*.csv
*.json.gz
data/
Generated code
src/generated/
Test coverage
coverage/
.coverage
**Use Case 3: Advanced Multi-Model Setup**
yaml
.aider.conf.yml - Power user with multiple models
Model selection
model: deepseek/deepseek-reasoner # Best for complex reasoning
weak-model: gpt-4o-mini # Fast summaries
editor-model: claude-3-5-sonnet-20241022 # Balanced for refactoring
Aliases for quick switching
alias:
- "r1:deepseek/deepseek-reasoner"
- "sonnet:claude-3-5-sonnet-20241022"
- "fast:gpt-4o-mini"
- "local:ollama/qwen2.5-coder"
Architect mode for complex changes
architect: false # Enable manually when needed
auto-accept-architect: true
editor-edit-format: editor-diff
Repository context
map-tokens: 2048
map-refresh: auto
Aggressive caching (Anthropic)
cache-prompts: true
cache-keepalive-pings: 12
Files pre-loaded
read:
- CONVENTIONS.md
- docs/project-context.md
file:
- src/types/global.d.ts
- package.json
- tsconfig.json
Quality automation
auto-lint: true
lint-cmd:
- "python: ruff check --fix --select I,F,E,W"
- "javascript: prettier --write"
- "typescript: eslint --fix"
auto-test: false # Run tests manually for complex changes
Git configuration
auto-commits: false
show-diffs: true
attribute-co-authored-by: true
History
restore-chat-history: false
max-chat-history-tokens: 4096
Display
dark-mode: true
code-theme: monokai
stream: true
fancy-input: true
Privacy
analytics-disable: true
**Model Settings for DeepSeek:**
yaml
.aider.model.settings.yml
- name: deepseek/deepseek-reasoner edit_format: diff use_repo_map: true use_reasoning: true # DeepSeek-specific thinking_tokens: 8000 # Budget for reasoning
**Use Case 4: Enterprise Team (50+ devs)**
yaml
.aider.conf.yml - Enterprise configuration
model: claude-3-5-sonnet-20241022
weak-model: gpt-4o-mini
Organization-wide conventions
read:
- CONVENTIONS.md # Team standards
- docs/architecture.md # System design
- docs/security-policy.md # Security requirements
- docs/compliance.md # GDPR, SOC2, etc.
Strict quality gates
auto-lint: true
lint-cmd:
- "typescript: eslint --fix --max-warnings 0"
- "python: ruff check --fix"
auto-test: false # Manual gate for production code
Git requirements
auto-commits: false # Always require human review
attribute-co-authored-by: true # Audit trail
commit-prompt: "Generate commit message following company standard: type(scope): description. Include ticket reference."
File restrictions
aiderignore: .aiderignore # Comprehensive exclusions
add-gitignore-files: false # Never access sensitive files
Repository map for large codebase
map-tokens: 2048
subtree-only: false # Allow full codebase awareness
Caching for efficiency
cache-prompts: true
Compliance
analytics-disable: true
llm-history-file: null # Don't log LLM conversations (privacy)
Notifications for long-running tasks
notifications: true
notifications-command: "notify-send 'Aider' 'Task complete'"
---
### **4.5 Continue.dev Configuration**
Continue.dev pioneered the **hybrid YAML + Markdown** approach, where structured settings (models, tools, contexts) live in `config.yaml` while natural language instructions use `.continue/rules/*.md` files.
#### **4.5.1 YAML Config vs Markdown Rules Dichotomy**
**The Philosophy:**
- **YAML (`config.yaml`)**: Machine-readable configuration (models, API keys, context providers, tool settings)
- **Markdown (`.continue/rules/*.md`)**: Human-readable instructions (coding standards, patterns, conventions)
**Why the Split:**
| Aspect | YAML Config | Markdown Rules |
|--------|-------------|----------------|
| **Purpose** | Configure Continue's behavior | Guide AI's coding decisions |
| **Audience** | Continue.dev software | Language model |
| **Validation** | JSON Schema enforced | Freeform, no validation |
| **Content** | Structured data (key-value pairs) | Natural language + examples |
| **Changes** | Rare (model switching, provider setup) | Frequent (evolving standards) |
| **Git Strategy** | Commit `.vscode/settings.json` reference | Commit all rule files |
#### **4.5.2 Model Configuration with Roles**
**`config.yaml` Structure:**
yaml
~/.continue/config.yaml (Global)
OR
.continue/config.yaml (Project-specific)
name: my-dev-config
version: 0.0.1
schema: v1
MODELS
models:
# Primary chat model
-
name: Claude 3.5 Sonnet
provider: anthropic
model: claude-3-5-sonnet-20241022
apiKey: ${{ secrets.ANTHROPIC_API_KEY }}
roles:- chat
- edit
- apply
Model-specific options
defaultCompletionOptions:
temperature: 0.7
maxTokens: 4000
topP: 0.9Custom system messages per mode
chatOptions:
baseSystemMessage: "You are an expert TypeScript engineer focused on clean, maintainable code."
baseAgentSystemMessage: "You are systematic and methodical. Break down complex problems into clear steps."
basePlanSystemMessage: "Create actionable, concrete plans with specific file changes."Manually specify capabilities
capabilities:
- tool_use
- image_input
Request customization
requestOptions:
headers:
X-Custom-Header: "value"
# Fast model for quick tasks
-
name: GPT-4o Mini
provider: openai
model: gpt-4o-mini
apiKey: ${{ secrets.OPENAI_API_KEY }}
roles:- autocomplete
defaultCompletionOptions:
temperature: 0.3
maxTokens: 1000
# Local model (no API costs)
-
name: Qwen Coder Local
provider: ollama
model: qwen2.5-coder:7b
roles:- autocomplete
defaultCompletionOptions:
temperature: 0.2
CONTEXT PROVIDERS
context:
# Always-available contexts
provider: code # Currently open files
provider: codebase # Semantic search across codebase
params:
nRetrieve: 30 # Retrieve 30 chunks
nFinal: 5 # Show top 5 in contextprovider: docs # Fetch from documentation
provider: diff # Git diff of unstaged changes
provider: terminal # Recent terminal output
provider: problems # IDE error/warning messages
provider: folder # Directory tree
params:
folders:
- src
- tests
PROMPTS (Custom Commands)
prompts:
-
name: review
description: Comprehensive code review
prompt: |
Review this code for:- Security vulnerabilities
- Performance issues
- Code quality and maintainability
- Test coverage gaps
Provide specific, actionable feedback.
-
name: test
description: Generate comprehensive tests
prompt: |
Generate tests for this code:- Happy path test
- Error handling test
- Edge case tests
Use Vitest and Testing Library.
name: optimize
description: Optimize for performance
prompt: |
Analyze this code for performance bottlenecks.
Suggest specific optimizations with benchmarks.
**Role Types Explained:**
| Role | Purpose | When Active | Model Choice |
|------|---------|-------------|--------------|
| **chat** | Main conversational coding | Chat panel interactions | Most capable model (Claude, GPT-4o) |
| **edit** | Code transformations/refactoring | Edit commands, inline edits | Balanced model (Claude, GPT-4o) |
| **apply** | Targeted code modifications | Applying suggestions | Fast, accurate (Claude, GPT-4o-mini) |
| **autocomplete** | Real-time code suggestions | While typing | Very fast model (GPT-4o-mini, local) |
| **embed** | Semantic search embeddings | Codebase context retrieval | Embedding model (OpenAI text-embedding) |
| **rerank** | Search result ordering | Refining codebase search | Reranking model (Cohere rerank) |
| **summarize** | Content summarization | Summarizing files/changes | Cheap, fast model (GPT-4o-mini) |
**Best Practice: Role Assignment Strategy**
yaml
models:
# Powerful, expensive: Use for complex reasoning
- name: Claude 3.5 Sonnet provider: anthropic model: claude-3-5-sonnet-20241022 roles: [chat, edit, apply]
# Fast, cheap: Use for autocomplete and summaries
- name: GPT-4o Mini provider: openai model: gpt-4o-mini roles: [autocomplete, summarize]
# Specialized: Embedding for semantic search
- name: OpenAI Embeddings provider: openai model: text-embedding-3-small roles: [embed]
#### **4.5.3 Three Rule Approaches Compared**
**Approach 1: YAML `rules` Array (Inline)**
yaml
config.yaml
rules:
- "Always use TypeScript strict mode"
- "Prefer functional programming patterns"
- "Include error handling in all async functions"
- "Write tests for new features"
**Pros:**
- Simple, all-in-one config
- No additional files
- Good for 3-5 universal rules
**Cons:**
- No rich formatting (code examples, tables)
- Gets messy beyond 10 rules
- Hard to maintain in YAML
- Always loaded (token overhead)
---
**Approach 2: Markdown Rules (`.continue/rules/*.md`)**
markdown
name: TypeScript Standards
description: Type safety and coding patterns for TypeScript
globs: "*/.{ts,tsx}"
alwaysApply: false
tags: [typescript, standards]
TypeScript Coding Standards
Type Safety
Explicit Return Types
// ✅ CORRECT
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ WRONG
function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Avoid any
// ✅ Use `unknown` and type guards
function processData(data: unknown) {
if (typeof data === 'object' && data !== null) {
// Type narrowed, now safe to use
}
}
// ❌ Never use `any`
function processData(data: any) {
return data.someProperty; // No type safety
}
Async Patterns
Always use async/await:
// ✅ CORRECT
async function fetchUser(id: string): Promise<User> {
try {
const response = await api.get(`/users/${id}`);
return UserSchema.parse(response.data);
} catch (error) {
logger.error('Fetch failed', { id, error });
throw new AppError('User fetch failed', 500);
}
}
// ❌ AVOID Promise chains
function fetchUser(id: string) {
return api.get(`/users/${id}`)
.then(response => response.data)
.catch(error => console.log(error)); // Bad error handling
}
**YAML Frontmatter Fields:**
| Field | Purpose | Example |
|-------|---------|---------|
| `name` | Human-readable rule name | "TypeScript Standards" |
| `description` | Explains when to use | "Apply when working with TypeScript files" |
| `globs` | File patterns for auto-application | `"**/*.{ts,tsx}"` |
| `alwaysApply` | Apply to all contexts | `true` (default: false) |
| `tags` | Categorization | `["typescript", "standards"]` |
| `regex` | Pattern matching | `".*Controller\\.ts$"` |
**Pros:**
- Rich Markdown formatting
- Code examples with syntax highlighting
- Conditional loading (globs)
- Separate concerns (one file per topic)
- Easy to maintain and version
**Cons:**
- Multiple files to manage
- Need to understand frontmatter
- Requires directory structure
---
**Approach 3: `chatOptions.baseSystemMessage` (Model-Specific Override)**
yaml
models:
-
name: Claude 3.5 Sonnet
provider: anthropic
model: claude-3-5-sonnet-20241022Override default system messages
chatOptions:
baseSystemMessage: |
You are an expert Go developer specializing in microservices.Always follow these principles: - Use interfaces for dependency injection - Error handling with explicit error returns - Context for cancellation and timeouts - Structured logging with slogbaseAgentSystemMessage: |
When in Agent mode:
1. Break problems into clear steps
2. Verify preconditions before acting
3. Provide status updatesbasePlanSystemMessage: |
When planning:
- List specific files to modify
- Outline changes for each file
- Consider dependencies and ordering
**Pros:**
- Model-specific customization
- Overrides Continue's defaults completely
- Good for language-specific setups (Go, Rust, etc.)
**Cons:**
- Buried in YAML (less visible)
- No rich formatting
- Applies to ALL interactions with that model
- Not file-specific
---
**Decision Matrix: Which Approach to Use?**
| Scenario | Recommended Approach |
|----------|---------------------|
| **3-5 universal rules, single language** | YAML `rules` array |
| **Multiple languages, file-type specific rules** | Markdown rules with `globs` |
| **Language-specific model (e.g., Rust assistant)** | `chatOptions.baseSystemMessage` |
| **Team collaboration, evolving standards** | Markdown rules (easy to review) |
| **Large project, 10+ rule categories** | Multiple Markdown files |
| **Mode-specific behavior (Agent vs Chat)** | `chatOptions.base*SystemMessage` |
| **Combination of all above** | Hybrid: YAML + Markdown + chatOptions |
**Hybrid Example (Recommended for Most Projects):**
yaml
config.yaml
Universal short rules
rules:
- "NEVER commit secrets or API keys"
- "ALWAYS run linter before committing"
Markdown rules for detailed guidance
(rules loaded automatically from .continue/rules/)
models:
-
name: Claude 3.5 Sonnet
provider: anthropic
model: claude-3-5-sonnet-20241022Override for mode-specific behavior
chatOptions:
baseAgentSystemMessage: "Break complex tasks into atomic steps. Verify preconditions."
.continue/
├── config.yaml # Model and tool config
└── rules/
├── 01-typescript.md # globs: */.{ts,tsx}
├── 02-react.md # globs: */.{tsx,jsx}
├── 03-python.md # globs: */.py
├── 04-testing.md # Manual invocation
└── 05-security.md # alwaysApply: true
#### **4.5.4 Rule Loading Order and Precedence**
Continue.dev loads rules in this hierarchy:
**Loading Order (First to Last):**
1. **Hub assistant rules** (if using Continue Hub cloud config)
2. **Referenced Hub rules** (via `uses:` in config.yaml)
3. **Local workspace rules** (`.continue/rules/`)
4. **Global user rules** (`~/.continue/rules/`)
**Important**: Later rules DON'T override earlier ones—they're **cumulative** (all applicable rules are included).
**Example Loading Scenario:**
Hub configuration (if logged in)
Hub Assistant: "React Development Assistant"
└── Includes Hub rules: @continuedev/react-best-practices
Project config.yaml
uses:
- continuedev/security-standards # Referenced Hub rule
Project rules
.continue/rules/
├── typescript-standards.md # Local project rule
└── company-conventions.md # Local project rule
Global user rules
~/.continue/rules/
└── personal-preferences.md # Your global preferences
**Loaded Context (cumulative):**
1. Hub: React best practices
2. Hub: Security standards
3. Local: TypeScript standards
4. Local: Company conventions
5. Global: Personal preferences
**All five are active simultaneously** (assuming `alwaysApply: true` or matching `globs`).
**Controlling Precedence:**
Since rules are cumulative, use **explicit override language** when needed:
markdown
name: Company Convention Overrides
alwaysApply: true
Company-Specific Overrides
IMPORTANT: These rules OVERRIDE generic best practices.
State Management
Despite general React best practices recommending Context API,
our company standard is:
- ✅ USE: Redux Toolkit for ALL global state
- ❌ AVOID: Context API, Zustand, Jotai
Rationale: Existing codebase, team expertise, debugging tools.
#### **4.5.5 Known Issues and Workarounds**
**Issue 1: Rules Not Applied Without `alwaysApply: true`**
**Symptom**: Rule file exists but AI doesn't follow it
**Cause**: Bug in Continue.dev v0.9.x - `description` and `globs` fields don't reliably trigger rule application
**Workarounds:**
1. **Set `alwaysApply: true`** (simplest):
markdown
name: TypeScript Standards
alwaysApply: true
2. **Omit frontmatter entirely** (fallback):
markdown
TypeScript Standards
Rules content here...
3. **Manually reference in chat**:
@.continue/rules/typescript-standards.md
**Tracking**: GitHub issue #5817 (as of May 2025)
---
**Issue 2: Schema Loading Issues After Extension Update**
**Symptom**: VS Code shows "Unknown property" warnings in `config.yaml`
**Cause**: Extension update changed schema path reference
**Solution:**
1. **Update VS Code settings**:
json
// .vscode/settings.json
{
"yaml.schemas": {
"https://continue.dev/schema/config.json": ".continue/config.yaml"
}
}
2. **Or reinstall Continue extension**:
- Uninstall Continue.dev
- Reload VS Code
- Reinstall Continue.dev
---
**Issue 3: Hub vs Local Rule Confusion**
**Symptom**: Local rules don't appear when using Hub configs
**Cause**: Hub configurations require explicit `uses:` references
**Solution**:
Either:
**A) Don't use Hub configs** (use local only):
yaml
config.yaml
name: My Local Config
version: 0.0.1
models: [...] # Define models locally
Rules automatically loaded from .continue/rules/
**B) Explicitly reference local rules in Hub config**:
yaml
config.yaml (with Hub)
name: My Hub Config
models:
- uses: anthropic/claude-sonnet-4 # Hub model
rules:
- uses: ./blocks/typescript-standards.md # Local rule file
---
**Issue 4: Token Limit Exceeded with Too Many Rules**
**Symptom**: Errors about context length, slow responses
**Cause**: Too many `alwaysApply: true` rules loaded simultaneously
**Solution: Be Strategic with `alwaysApply`**
markdown
name: React Patterns
globs: "*/.{tsx,jsx}"
alwaysApply: false
name: React Patterns
alwaysApply: true
**Rule of Thumb:**
- **1-3 rules**: `alwaysApply: true` (core standards)
- **5-10 rules**: Use `globs` for conditional loading
- **10+ rules**: Multiple files with narrow globs
#### **4.5.6 Production Example: Full-Stack TypeScript Project**
yaml
.continue/config.yaml
name: fullstack-typescript-config
version: 1.0.0
schema: v1
MODELS
models:
# Primary: Claude for complex reasoning
-
name: Claude 3.5 Sonnet
provider: anthropic
model: claude-3-5-sonnet-20241022
apiKey: ${{ secrets.ANTHROPIC_API_KEY }}
roles: [chat, edit, apply]defaultCompletionOptions:
temperature: 0.7
maxTokens: 4000chatOptions:
baseSystemMessage: |
You are an expert full-stack TypeScript engineer.
Focus on type safety, performance, and maintainability.
Always provide working, production-ready code.
# Fast: GPT-4o Mini for autocomplete
-
name: GPT-4o Mini
provider: openai
model: gpt-4o-mini
apiKey: ${{ secrets.OPENAI_API_KEY }}
roles: [autocomplete, summarize]defaultCompletionOptions:
temperature: 0.3
maxTokens: 1000
CONTEXT
context:
provider: code
provider: codebase
params:
nRetrieve: 30
nFinal: 5provider: docs
provider: diff
provider: terminal
CUSTOM PROMPTS
prompts:
-
name: test
description: Generate comprehensive tests
prompt: |
Generate tests for this code using Vitest and Testing Library.
Include:- Happy path test
- Error handling test
- Edge cases
Achieve >90% coverage.
-
name: optimize
description: Performance optimization review
prompt: |
Analyze this code for performance issues:- O(n²) or worse algorithms
- Unnecessary re-renders (React)
- Memory leaks
- Bundle size impact
Suggest specific optimizations with benchmarks.
SHORT RULES
rules:
- "NEVER commit secrets or API keys"
- "ALWAYS run 'pnpm lint' before committing"
- "USE pnpm, NOT npm or yarn"
**Rule Files:**
.continue/
├── config.yaml
└── rules/
├── 01-project-overview.md
├── 02-typescript-standards.md
├── 03-react-patterns.md
├── 04-backend-api.md
├── 05-testing-guide.md
└── 06-security-checklist.md
**`01-project-overview.md` (Always Applied):**
markdown
name: Project Overview
description: Core project context and architecture
alwaysApply: true
Project: E-Commerce Platform
Tech Stack
- Frontend: Next.js 15, React 19, TypeScript 5.4, Tailwind CSS
- Backend: tRPC 11, Drizzle ORM, PostgreSQL 16
- Monorepo: Turborepo
- Deployment: Vercel
Architecture
apps/
├── web/ # Next.js frontend
└── api/ # Standalone API (future)
packages/
├── ui/ # Shared components
├── database/ # Drizzle schema
└── types/ # Shared types
Key Principles
- Server Components by default
- tRPC for type-safe APIs
- Zod for validation
- TanStack Query for data fetching
**`02-typescript-standards.md` (Auto-Attached):**
markdown
name: TypeScript Standards
description: Type safety and patterns
globs: "*/.{ts,tsx}"
alwaysApply: false
TypeScript Standards
Type Annotations
// ✅ Explicit return types
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ No implicit return types
function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Error Handling
// ✅ Result type pattern
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
async function fetchData(): Promise<Result<Data>> {
try {
const data = await api.get('/data');
return { success: true, data };
} catch (error) {
logger.error('API error', { error });
return { success: false, error: 'Failed to fetch' };
}
}
Zod Schemas
import { z } from 'zod';
// Define schema
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(2),
});
// Infer TypeScript type
type User = z.infer<typeof UserSchema>;
// Validate at runtime
const user = UserSchema.parse(untrustedData);
**`03-react-patterns.md` (Auto-Attached to TSX):**
markdown
name: React Patterns
description: Component and state management patterns
globs: "*/.{tsx,jsx}"
alwaysApply: false
React Patterns
Server vs Client Components
Default: Server Components
// ✅ Server Component (no directive needed)
async function UserProfile({ userId }: { userId: string }) {
const user = await db.query.users.findFirst({
where: eq(users.id, userId)
});
return <div>{user.name}</div>;
}
Client Components (when needed)
'use client';
// Only add 'use client' when:
// - Using hooks (useState, useEffect, etc.)
// - Using browser APIs
// - Event handlers
export function InteractiveButton() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
State Management
| Use Case | Solution |
|---|---|
| Server data | TanStack Query |
| Form state | React Hook Form + Zod |
| Global client state | Zustand |
| Local state | useState |
| URL state | useSearchParams |
Component Size
- Max 200 lines per component
- If exceeded: Extract sub-components
- Composition over props drilling
// ✅ Composition
<Card>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>{content}</CardContent>
</Card>
// ❌ Props drilling
<Card
title={title}
content={content}
headerStyle={{...}}
contentStyle={{...}}
/>
**`04-backend-api.md` (Auto-Attached to API files):**
markdown
name: Backend API Patterns
description: tRPC procedures and database patterns
globs: "lib/api/*/"
alwaysApply: false
Backend API Patterns
tRPC Procedure Structure
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '@/lib/api/trpc';
export const userRouter = createTRPCRouter({
// Public procedure
getById: publicProcedure
.input(z.object({ id: z.string().uuid() }))
.query(async ({ input, ctx }) => {
const user = await ctx.db.query.users.findFirst({
where: eq(users.id, input.id)
});
if (!user) {
throw new TRPCError({
code: 'NOT_FOUND',
message: 'User not found',
});
}
return user;
}),
// Protected procedure (requires auth)
update: protectedProcedure
.input(z.object({
id: z.string().uuid(),
name: z.string().min(2).optional(),
email: z.string().email().optional(),
}))
.mutation(async ({ input, ctx }) => {
// ctx.user is available (from protectedProcedure)
if (ctx.user.id !== input.id) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Cannot update other users',
});
}
const updated = await ctx.db
.update(users)
.set(input)
.where(eq(users.id, input.id))
.returning();
return updated[0];
}),
});
Database Patterns (Drizzle)
Queries
// ✅ Eager loading with relations
const users = await db.query.users.findMany({
with: {
posts: true,
profile: true,
},
});
// ❌ N+1 queries
const users = await db.query.users.findMany();
for (const user of users) {
user.posts = await db.query.posts.findMany({
where: eq(posts.userId, user.id)
});
}
Transactions
// ✅ Atomic multi-step operations
await db.transaction(async (tx) => {
const user = await tx.insert(users).values(userData).returning();
await tx.insert(profiles).values({
userId: user[0].id,
...profileData
});
});
Error Handling
import { TRPCError } from '@trpc/server';
// Standard TRPC errors
throw new TRPCError({
code: 'NOT_FOUND', // 404
message: 'Resource not found',
});
throw new TRPCError({
code: 'FORBIDDEN', // 403
message: 'Insufficient permissions',
});
throw new TRPCError({
code: 'BAD_REQUEST', // 400
message: 'Invalid input',
});
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR', // 500
message: 'Unexpected error',
});
**`05-testing-guide.md` (Manual):**
markdown
name: Testing Guide
description: Comprehensive testing patterns and requirements
alwaysApply: false
Testing Guide
Invoke with: @.continue/rules/testing-guide.md
Test Structure (AAA Pattern)
import { describe, it, expect, beforeEach } from 'vitest';
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
// ARRANGE: Set up test environment
service = new UserService(mockDb);
});
it('creates user with valid data', async () => {
// ARRANGE: Prepare data
const userData = { email: 'test@example.com', name: 'Test' };
// ACT: Execute function
const result = await service.createUser(userData);
// ASSERT: Verify outcome
expect(result.success).toBe(true);
expect(result.data).toMatchObject(userData);
});
});
Coverage Requirements
- Unit: 80% minimum
- Integration: 70% minimum
- E2E: Critical paths only
Mocking with MSW
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('https://api.example.com/users', (req, res, ctx) => {
return res(ctx.json([{ id: '1', name: 'User 1' }]));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
**`06-security-checklist.md` (Always Applied):**
markdown
name: Security Checklist
description: Security requirements and common vulnerabilities
alwaysApply: true
tags: [security, compliance]
Security Checklist
Authentication
- ✅ JWTs in HttpOnly cookies (NEVER localStorage)
- ✅ Refresh token rotation
- ✅ CSRF protection on mutations
Input Validation
// ✅ ALWAYS validate with Zod
const input = UserInputSchema.parse(untrustedData);
// ❌ NEVER trust raw input
const user = await createUser(req.body); // Unsafe!
SQL Injection Prevention
// ✅ Parameterized queries (Drizzle ORM)
const users = await db.query.users.findMany({
where: eq(users.email, email) // Safe
});
// ❌ String concatenation
const query = `SELECT * FROM users WHERE email = '${email}'`; // SQL injection!
Secrets Management
- ✅ Environment variables only
- ✅ Validate with @t3-oss/env-nextjs
- ❌ NEVER hardcode secrets
Common Vulnerabilities Checklist
- [ ] XSS: User input sanitized?
- [ ] SQL Injection: Queries parameterized?
- [ ] CSRF: State-changing endpoints protected?
- [ ] Rate Limiting: Public endpoints limited?
- [ ] Secrets: No hardcoded keys?
---
This comprehensive Continue.dev configuration demonstrates:
1. **Clear separation**: YAML for structure, Markdown for instructions
2. **Role-based models**: Appropriate model for each task
3. **Conditional loading**: Rules apply only when relevant (globs)
4. **Hierarchical organization**: 6 focused rule files vs 1 monolith
5. **Production-ready patterns**: Real-world examples with explanations
---
### **4.6 Cline and Roo-Cline**
Cline and Roo-Cline represent two evolutionary branches: **Cline prioritizes simplicity** with straightforward Markdown rules, while **Roo-Cline adds sophisticated mode-based customization** with YAML configuration and granular tool permissions.
#### **4.6.1 Cline's `.clinerules` System**
**Overview:**
- **Format**: Plain Markdown (no frontmatter, no schema)
- **Locations**: Single file OR directory of multiple files
- **Philosophy**: Simple, human-readable, easy to adopt
**Single File Approach:**
my-project/
├── .clinerules # Single configuration file
├── src/
└── tests/
**`.clinerules` Example:**
markdown
Project Guidelines
Tech Stack
- Language: TypeScript 5.4
- Framework: Next.js 15 (App Router)
- Database: PostgreSQL with Drizzle ORM
- Testing: Vitest for unit, Playwright for E2E
Documentation Requirements
- Update
/docswhen modifying features - Keep
README.mdin sync with capabilities - Maintain changelog entries in
CHANGELOG.md
Architecture Decision Records (ADRs)
Create ADRs in /docs/adr for:
- Major dependency changes
- Architectural pattern changes
- New integration patterns
- Database schema changes
Follow template in /docs/adr/template.md
Code Style & Patterns
API Client Generation
- Generate using OpenAPI Generator
- Use TypeScript axios template
- Place generated code in
/src/generated - DON'T manually edit generated files
Design Patterns
- Prefer composition over inheritance
- Use repository pattern for data access
- Follow error handling pattern in
/src/utils/errors.ts
Naming Conventions
-
Variables/Functions: camelCase (
getUserById) -
Types/Interfaces: PascalCase (
UserProfile) -
Constants: UPPER_SNAKE_CASE (
MAX_RETRY_COUNT) -
Files: kebab-case (
user-profile.tsx)
Testing Standards
- Unit tests: Required for business logic
- Integration tests: Required for API endpoints
- E2E tests: Required for critical user flows
- Coverage: Minimum 80% for new features
Test Structure
describe('featureName', () => {
it('should handle happy path', () => {
// Arrange
const input = createTestInput();
// Act
const result = performOperation(input);
// Assert
expect(result).toBe(expected);
});
it('should handle error case', () => {
// Test error handling
});
});
Security Requirements
- NEVER commit secrets or API keys
- ALWAYS validate user inputs
- Use parameterized queries (prevent SQL injection)
- Implement rate limiting on public endpoints
Git Workflow
- Feature branches:
feat/feature-name - Commit format: Conventional Commits (
feat:,fix:,docs:) - PRs require 1 approval + passing CI
Before Deploying
- Run full test suite:
pnpm test - Check TypeScript:
pnpm type-check - Lint:
pnpm lint --max-warnings 0 - Build succeeds:
pnpm build - Environment variables configured in deployment platform
**Directory Approach (Introduced in Cline 3.7.0):**
my-project/
├── .clinerules/
│ ├── 01-project-overview.md
│ ├── 02-coding-standards.md
│ ├── 03-testing-guide.md
│ ├── 04-deployment.md
│ └── current-sprint-focus.md
├── src/
└── tests/
**How It Works:**
- Cline **automatically merges** all Markdown files in `.clinerules/`
- Numeric prefixes (01-, 02-) help organize logical order
- Files are loaded alphabetically (case-insensitive)
**Example: `01-project-overview.md`**
markdown
Project Overview
Product
E-commerce platform for small businesses
Current Sprint (Week of 2025-11-25)
Focus: Implementing shopping cart and checkout flow
Key tasks:
- [ ] Cart state management (Zustand)
- [ ] Checkout form with Stripe integration
- [ ] Order confirmation email
Stack
- Frontend: Next.js 15 + React 19 + TypeScript
- Styling: Tailwind CSS + shadcn/ui
- Backend: tRPC + Drizzle ORM + PostgreSQL
- Payments: Stripe
Architecture
Feature-sliced design:
src/
├── features/
│ ├── cart/
│ ├── checkout/
│ └── products/
**Example: `02-coding-standards.md`**
markdown
Coding Standards
TypeScript
- Strict mode enabled
- Explicit return types on all functions
- No
anytypes (useunknown+ type guards)
React
- Functional components only
- Server Components by default
- Add
'use client'only when needed
Imports
// 1. External dependencies
import { useState } from 'react';
import { z } from 'zod';
// 2. Internal modules
import { Button } from '@/components/ui/button';
import { trpc } from '@/lib/trpc/client';
// 3. Relative imports
import { ProductCard } from './ProductCard';
Error Handling
// ✅ ALWAYS: Explicit try-catch with logging
try {
await operation();
} catch (error) {
logger.error('Operation failed', { error });
throw new AppError('User-friendly message', 500);
}
// ❌ NEVER: Silent failures
try {
await operation();
} catch {
// Silent failure!
}
#### **Global Rules (OS-Specific Locations)**
Cline supports **global rules** that apply across ALL projects:
**Locations:**
| OS | Path |
|----|------|
| **Windows** | `Documents\Cline\Rules\` |
| **macOS** | `~/Documents/Cline/Rules/` |
| **Linux/WSL** | `~/Documents/Cline/Rules/` (may fall back to `~/Cline/Rules`) |
**Use Cases:**
- Your personal coding style preferences
- Organization-wide standards (if shared drive)
- Language-specific conventions you always follow
**Example: `~/Documents/Cline/Rules/my-preferences.md`**
markdown
My Personal Coding Preferences
Response Format
- Give concise answers
- Show code examples with explanations
- Highlight breaking changes or gotchas
Code Style
- Prefer modern ES2022+ syntax
- Use destructuring where readable
- Favor explicit over clever
Testing
- I always write tests AFTER implementation (not TDD)
- Focus on integration tests over unit tests
- Mock external APIs, use real database (test database)
Git
- Commit messages: Conventional Commits format
- I use
feat:,fix:,refactor:,docs:,test:
**Precedence:**
- **Project rules** (`.clinerules` or `.clinerules/`) override global rules
- **Global rules** provide defaults when project rules don't cover something
#### **4.6.2 Rules Bank Strategy**
For developers working across multiple contexts (different clients, project types, frameworks), Cline supports a **rules bank** pattern:
**Directory Structure:**
my-project/
├── .clinerules/ # Active rules (auto-loaded)
│ ├── 01-core.md
│ └── client-a-specifics.md
│
├── clinerules-bank/ # Inactive rule repository
│ ├── clients/
│ │ ├── client-a.md
│ │ ├── client-b.md
│ │ └── client-c.md
│ ├── frameworks/
│ │ ├── react.md
│ │ ├── vue.md
│ │ └── angular.md
│ └── project-types/
│ ├── api-service.md
│ ├── frontend-app.md
│ └── data-pipeline.md
└── src/
**Workflow:**
**Scenario: Switch from Client A to Client B project**
bash
Remove Client A rules
rm .clinerules/client-a-specifics.md
Add Client B rules
cp clinerules-bank/clients/client-b.md .clinerules/
Or if switching frameworks too:
cp clinerules-bank/frameworks/vue.md .clinerules/
**Scenario: Start new React frontend project**
bash
Copy relevant rules into active directory
cp clinerules-bank/frameworks/react.md .clinerules/02-react.md
cp clinerules-bank/project-types/frontend-app.md .clinerules/03-frontend.md
**Example: `clinerules-bank/clients/client-a.md`**
markdown
Client A Specific Rules
Branding
- Primary color: #1E40AF (blue-700)
- Font: Inter for UI, JetBrains Mono for code
Client Preferences
- Verbose logging (client likes detailed logs)
- Conservative error handling (prefer graceful degradation)
- Accessibility: WCAG AA minimum
Deployment
- Staging: https://staging.client-a.com
- Production: https://app.client-a.com
- Deploy via:
npm run deploy:client-a
Contact
- PM: jane@client-a.com
- Tech Lead: john@client-a.com
**Example: `clinerules-bank/frameworks/react.md`**
markdown
React Framework Rules
Component Patterns
- Functional components with hooks
- Prefer composition over props drilling
- Extract custom hooks for reusable logic
State Management
- Local: useState
- Global: Context API or Zustand
- Server: React Query
File Structure
features/
user-profile/
UserProfile.tsx
UserProfile.test.tsx
useUserProfile.ts
types.ts
Performance
- Memoize expensive calculations (useMemo)
- Memoize callbacks passed to children (useCallback)
- Lazy load routes:
const Route = lazy(() => import('./Route'))
**Benefits:**
- **Context switching**: Quickly adapt to different project requirements
- **Reusability**: Build library of proven patterns
- **Experimentation**: Try different rule sets without losing originals
- **Client work**: Easily switch between client-specific conventions
---
#### **4.6.3 Roo-Cline's Custom Modes Revolution**
**Key Innovation: Mode-Based AI Behavior**
Roo-Cline (fork of Cline) introduces **custom modes**—distinct AI personas with:
- **Specific roles** (Documentation Writer, Security Auditor, Test Engineer)
- **Tool permissions** (read-only, edit with restrictions, command execution)
- **File access control** (regex patterns limiting which files can be modified)
- **Model assignments** (sticky models per mode for cost optimization)
**Configuration Files:**
| File | Purpose | Format |
|------|---------|--------|
| `.roomodes` | Mode definitions | YAML (preferred) or JSON |
| `.roo/rules-{mode-slug}/` | Mode-specific instruction files | Markdown |
| `~/.roo/custom_modes.yaml` | Global modes (all projects) | YAML |
#### **4.6.4 Complete `.roomodes` Schema**
**YAML Format (Recommended):**
yaml
customModes:
# Mode 1: Documentation Writer
-
slug: docs-writer # Unique ID (a-z, 0-9, hyphens only)
name: 📝 Documentation Writer # Display name (can include emojis)
description: > # Short summary for mode selector
Specialized mode for writing and editing technical documentation.
Focuses on clarity, completeness, and examples.roleDefinition: > # AI's identity (prepended to system prompt)
You are a technical writer specializing in clear, comprehensive documentation.
Your primary focus is creating accessible documentation with practical examples.
You excel at explaining complex concepts simply.whenToUse: > # For Orchestrator mode delegation
Use this mode when the task involves writing or editing documentation,
creating README files, or explaining code functionality.customInstructions: |- # Additional guidelines (near end of prompt)
## Documentation Standards- Use clear, concise language
- Include code examples for every concept
- Follow project's documentation style guide
- Add tables of contents for long documents
- Use proper Markdown formatting
groups: # Tool permissions
- read # Can read any file
- - edit # Can edit, with restrictions:
- fileRegex: \.(md|mdx)$ # Only Markdown files description: Markdown files only
- browser # Can browse web for research
source: project # 'project' or 'global'
# Mode 2: Security Auditor
-
slug: security-auditor
name: 🛡️ Security AuditorroleDefinition: >
You are an expert security researcher conducting thorough security audits.
Focus on identifying high-priority vulnerabilities and providing
actionable remediation steps.customInstructions: |-
## Audit Process### 1. ANALYSIS PHASE
- Review codebase systematically
- Focus on: authentication, data handling, API endpoints, user input
- Document concerns with file locations and line numbers
### 2. PLANNING PHASE
- Explain nature and severity of security risks
- Provide evidence of potential attack vectors
- Outline remediation steps with priority ordering
### 3. IMPLEMENTATION PHASE (if approved)
- Make minimal necessary changes
- Document before/after comparisons
- Verify no new vulnerabilities introduced
## Key Focus Areas
- Exposed credentials and environment variables
- Insufficient input validation (XSS, SQL injection)
- Authentication/authorization bypasses
- Missing rate limiting
- Insecure dependencies
## DO NOT
- Make cosmetic changes unrelated to security
- Skip analysis and planning phases
- Implement fixes without explaining risk first
groups:
- read # Read-only for analysis
- command # Can run security scanners
source: global
# Mode 3: Test Engineer (TDD Red Phase)
-
slug: tdd-red-phase
name: 🔴 TDD Red Phase SpecialistroleDefinition: >
You are a TDD expert specializing in the Red phase.
Write failing unit tests based on requirements and specifications.
Tests must fail due to missing implementation, not setup errors.customInstructions: |-
### RESTRICTIONS- This phase CANNOT modify non-test files
- ALL tests must fail due to missing SUT (System Under Test) logic
- Use test inputs designed to fail against SUT stubs
### WORKFLOW
- Analyze requirements (BDD scenarios, user stories)
- Set up test infrastructure (mocks, fixtures)
- Write test assertions with guard rails
- Verify failure (due to missing logic, not test errors)
### QUALITY INDICATORS
🟢 Excellent (90-100):
- Tests are reliable and maintainable
- Clear behavior verification
- Proper test isolation
- Descriptive test names
🟡 Acceptable (70-89):
- Tests work but could be clearer
- Some coupling between tests
- Minor maintainability issues
🔴 Requires Revision (<70):
- Significant reliability issues
- Unclear test purpose
- Poor isolation
- Hard to maintain
groups:
- read
- - edit
- fileRegex: .*.test.(js|tsx?|py)$ description: Test files only
- command # Can run tests
source: global
# Mode 4: Code Mode Override (Project-Specific)
-
slug: code # Overrides built-in 'code' mode
name: 💻 Code (Project Custom)roleDefinition: >
You are a senior TypeScript engineer working on this Next.js project.
Focus on type safety, performance, and Next.js 15 best practices.customInstructions: |-
## Project-Specific Rules- Use pnpm (NOT npm)
- Server Components by default
- Add 'use client' only when necessary
- tRPC for all API calls
- Zod for validation
- Drizzle ORM for database
## File Organization
- Features go in
src/features/ - Shared UI in
src/components/ui/ - Utils in
src/lib/
groups:
- read
- - edit
- fileRegex: .(ts|tsx)$ description: TypeScript files only
- command
source: project # Project-specific override
**JSON Format (Alternative, less preferred):**
json
{
"customModes": [{
"slug": "docs-writer",
"name": "📝 Documentation Writer",
"description": "Specialized mode for writing technical documentation.",
"roleDefinition": "You are a technical writer specializing in clear documentation.",
"whenToUse": "Use when writing or editing documentation.",
"customInstructions": "Focus on clarity and examples.",
"groups": [
"read",
["edit", { "fileRegex": "\.(md|mdx)$", "description": "Markdown only" }],
"browser"
],
"source": "project"
}]
}
#### **4.6.5 Tool Permissions and File Regex**
**Available Tools:**
| Tool | Permission | What It Enables |
|------|-----------|----------------|
| `read` | View files | Read any file in workspace |
| `edit` | Modify files | Create, update, delete files |
| `browser` | Web access | Browse URLs, search web |
| `command` | Execute shell | Run terminal commands |
| `mcp` | MCP servers | Access Model Context Protocol servers |
**Permission Syntax:**
**Simple (no restrictions):**
yaml
groups:
- read
- edit # Can edit ANY file
- command
**Restricted (with regex):**
yaml
groups:
- read
- - edit # Tuple: [tool, restrictions]
- fileRegex: \.(md|mdx)$ # Regex pattern description: Markdown files only
- browser
**Critical: Regex Escaping**
| YAML | JSON Equivalent | Matches |
|------|----------------|---------|
| `\.md$` | `"\\.md$"` | Files ending in `.md` |
| `^src/.*` | `"^src/.*"` | Files in `src/` |
| `\.(css\|scss)$` | `"\\.(css\\|scss)$"` | CSS or SCSS files |
| `^(?!.*(test\|spec)).*\.(js\|ts)$` | `"^(?!.*(test\\|spec)).*\\.(js\\|ts)$"` | JS/TS excluding tests |
**Rules:**
- **YAML**: Single backslash (`\.md$`)
- **JSON**: Double backslash (`\\.md$`)
- **Pattern matches**: Full relative path from workspace root
- **Case sensitive**: By default
**Example Regex Patterns:**
yaml
Documentation Writer: Markdown only
- - edit
- fileRegex: .(md|mdx|markdown)$ description: Markdown documentation files
Frontend Dev: React files only
- - edit
- fileRegex: .(tsx|jsx)$ description: React component files
Backend Dev: Server-side only
- - edit
- fileRegex: (src/api/|src/lib/|src/db/).*.(ts|js)$ description: Backend code only
Test Writer: Test files only
- - edit
- fileRegex: .*.(test|spec).(ts|tsx|js|jsx)$ description: Test files only
Config Editor: Config files only
- - edit
- fileRegex: (.(json|yaml|yml|toml|env)|^..*rc)$ description: Configuration files
Python Backend: Python files excluding notebooks
- - edit
- fileRegex: ^(?!..ipynb$)..py$ description: Python files (no notebooks)
**Security Best Practice:**
Always restrict edit permissions in specialized modes:
yaml
❌ RISKY: Documentation mode can edit code
customModes:
- slug: docs-writer
groups:
- read
- edit # Can edit anything!
✅ SAFE: Documentation mode restricted
customModes:
- slug: docs-writer
groups:
- read
- - edit
- fileRegex: .(md|mdx)$
#### **4.6.6 Mode-Specific Instruction Files**
Beyond inline `customInstructions`, Roo-Cline loads additional instructions from **dedicated directories**:
**Directory Structure:**
my-project/
├── .roomodes # Mode definitions
├── .roo/
│ └── rules-{mode-slug}/ # Mode-specific rules
│ ├── 01-overview.md
│ ├── 02-patterns.md
│ └── 03-examples.md
└── src/
**Global Mode Rules:**
~/.roo/
├── custom_modes.yaml # Global mode definitions
└── rules-{mode-slug}/ # Global mode rules
├── standards.md
└── templates.md
**Loading Behavior:**
1. Files in `.roo/rules-{slug}/` loaded **alphabetically**
2. Combined with `customInstructions` property in `.roomodes`
3. **Project rules** take precedence over global rules
4. System files automatically excluded (`.DS_Store`, `.swp`, etc.)
**Example: Test Engineer Mode Files**
**`.roomodes`:**
yaml
customModes:
-
slug: test-engineer
name: 🧪 Test Engineer
roleDefinition: You are a testing specialist...Short inline instructions
customInstructions: Follow TDD methodology. Write comprehensive tests.
groups:
- read
- - edit
- fileRegex: .test.(ts|tsx)$
**`.roo/rules-test-engineer/01-test-structure.md`:**
markdown
Test Structure Guidelines
File Naming
- Unit tests:
[name].test.ts - Integration tests:
[name].integration.test.ts - E2E tests:
[name].e2e.test.ts
Test Organization
describe('ComponentOrFunction', () => {
// Setup
beforeEach(() => {
// Reset state
});
// Happy path
describe('when given valid input', () => {
it('should return expected result', () => {
// Test
});
});
// Error cases
describe('when given invalid input', () => {
it('should throw validation error', () => {
// Test
});
});
// Edge cases
describe('edge cases', () => {
it('should handle empty input', () => {
// Test
});
});
});
**`.roo/rules-test-engineer/02-mocking.md`:**
markdown
Mocking Strategies
Mock External APIs (MSW)
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/users', (req, res, ctx) => {
return res(ctx.json([{ id: '1', name: 'Test' }]));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Mock Databases
const mockDb = {
user: {
findUnique: vi.fn(),
create: vi.fn(),
},
};
Mock React Hooks
vi.mock('@/hooks/useAuth', () => ({
useAuth: () => ({
user: { id: '1', name: 'Test User' },
isAuthenticated: true,
}),
}));
**Combined Effect:**
When Test Engineer mode is activated:
1. Loads `roleDefinition` from `.roomodes`
2. Loads `customInstructions` from `.roomodes`
3. Loads `01-test-structure.md` (appended)
4. Loads `02-mocking.md` (appended)
Total context = All four sources combined.
---
#### **4.6.7 Real-World Custom Mode Examples**
**Example 1: Architect Mode**
yaml
- slug: architect-mode name: 🏛️ TDD Architect
roleDefinition: >
You are an expert software architect specializing in maintainable,
modular, testable architectures for TDD workflows.
customInstructions: |-
## Process
### 1. Analyze Inputs
- Read context from planning documents
- Analyze requirements (BDD scenarios, user stories)
- Consider feature holistically (not just single function)
### 2. Propose Solutions
- Suggest 2-3 architectural designs
- Reduce code smells
- Ensure modularity (SOLID, DRY, KISS)
- Support testability
### 3. Trade-off Table
Present solutions comparing:
- Maintainability
- Simplicity
- Modularity
- Testability
- Scalability
- Performance
### 4. UML Diagram
Generate Mermaid diagram visualizing:
- Class relationships
- Data flow
- Component boundaries
### 5. ADR (Architecture Decision Record)
Document decision:
- **Context**: Problem being solved
- **Decision**: Chosen approach and why
- **Consequences**: Positive and negative outcomes
## Output Format
Save architecture document to `arch-[feature-name].md`
Use `write_to_file` tool
groups:
- read
- - edit
- fileRegex: .md$
description: Markdown files only (ADRs, docs)
source: global
**Usage:**
User: "Design architecture for user authentication system"
Architect Mode:
- Analyzes requirements
- Proposes 3 solutions:
- JWT with refresh tokens
- Session-based with Redis
- OAuth2 with Supabase
- Creates comparison table
- Generates Mermaid diagram
- Writes ADR to
arch-user-authentication.md
---
**Example 2: Database Migration Specialist**
yaml
- slug: db-migration name: 🗄️ Database Migration Specialist
roleDefinition: >
You are a database expert specializing in safe, reversible migrations.
You understand schema design, indexing, and zero-downtime deployments.
customInstructions: |-
## Migration Safety Checklist
Before creating migration:
- [ ] Backward compatible with current code?
- [ ] Includes rollback strategy?
- [ ] Indexes on foreign keys?
- [ ] NOT NULL columns have defaults?
- [ ] Large table? Consider batching
## Drizzle ORM Patterns
### Add Column (Safe)
```typescript
// Migration
await db.schema
.alterTable('users')
.addColumn('email_verified', 'boolean', col =>
col.defaultTo(false).notNull()
);
// Rollback
await db.schema
.alterTable('users')
.dropColumn('email_verified');
```
### Rename Column (Two-Phase)
**Phase 1** (Deploy code reading both):
```typescript
// Add new column
await db.schema
.alterTable('users')
.addColumn('full_name', 'text');
// Copy data
await db.execute(sql`
UPDATE users SET full_name = name
`);
// Update code to read from both
```
**Phase 2** (After deploy):
```typescript
// Drop old column
await db.schema
.alterTable('users')
.dropColumn('name');
```
## Zero-Downtime Strategy
1. Make schema change backward compatible
2. Deploy schema change
3. Deploy code using new schema
4. Remove old schema (if needed)
groups:
- read
- - edit
- fileRegex: (migrations/.|..schema.ts)$
description: Migration files and schema only
- command # Can run migrations
source: project
---
**Example 3: API Documentation Generator**
yaml
- slug: api-docs name: 📡 API Documentation Generator
roleDefinition: >
You are an API documentation specialist. You create comprehensive,
accurate API documentation from code, including OpenAPI specs.
customInstructions: |-
## Documentation Standards
For each endpoint, include:
- **Path**: Full endpoint path
- **Method**: GET, POST, PUT, DELETE
- **Description**: What it does
- **Authentication**: Required auth level
- **Request**: Parameters and body schema
- **Response**: Success and error formats
- **Examples**: curl and code samples
### OpenAPI Schema Format
```yaml
paths:
/api/users/{id}:
get:
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
```
## Code Example Template
```typescript
// Fetch user by ID
const response = await fetch('/api/users/123', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const user = await response.json();
console.log(user);
```
## Generate From
- tRPC routers
- Express routes
- Next.js API routes
- FastAPI endpoints
groups:
- read # Read API code
- - edit
- fileRegex: (docs/api/.*.md|openapi.yaml)$
description: API documentation only
- browser # Research API standards
source: global
---
#### **4.6.8 Creating and Managing Modes**
**Method 1: Ask Roo (Easiest)**
Chat with Roo: "Create a new mode called 'Frontend Specialist'.
It should only be able to edit React component files (*.tsx, *.jsx).
Focus on component design and performance optimization."
Roo generates the YAML configuration and offers to save it to `.roomodes`.
---
**Method 2: Modes UI**
1. Open Roo Code panel
2. Click mode dropdown → Settings icon (⚙️)
3. Click `+` (New Mode)
4. Fill form:
- **Name**: Display name
- **Slug**: Unique ID (auto-generated from name)
- **Description**: When to use this mode
- **Save Location**: Project or Global
- **Role Definition**: AI's identity
- **Tool Permissions**: Select checkboxes, add regex restrictions
- **Custom Instructions**: Additional guidelines
5. Click "Create Mode"
---
**Method 3: Manual Edit**
**Global modes:**
1. Modes page → "Edit Global Modes"
2. Opens `~/.roo/custom_modes.yaml` in editor
3. Add mode YAML manually
4. Save and reload
**Project modes:**
1. Create/edit `.roomodes` in project root
2. Add mode YAML
3. Reload VS Code window (Cmd/Ctrl + Shift + P → "Reload Window")
---
**Import/Export for Team Sharing**
**Export:**
1. Navigate to Modes view
2. Select mode
3. Click Export (download icon)
4. Choose save location
5. Shares YAML file with mode definition + bundled rule files
**Export Format:**
yaml
customModes:
-
slug: my-custom-mode
name: My Custom Mode
roleDefinition: You are a helpful assistant.
groups: ["read", "edit"]Rule files embedded
rulesFiles:
- relativePath: rules-my-custom-mode/rules.md content: | These are the rules for my custom mode.
**Import:**
1. Click Import (upload icon)
2. Select YAML file
3. Choose import level:
- **Project**: Mode + rules → `.roomodes` + `.roo/rules-{slug}/`
- **Global**: Mode + rules → `~/.roo/custom_modes.yaml` + `~/.roo/rules-{slug}/`
4. Edit slug in YAML before import if you want different ID
**Team Workflow:**
1. Developer creates mode (e.g., `security-auditor.yaml`)
2. Commits to shared repository or shares via Slack
3. Team members import mode
4. Everyone has consistent Security Auditor behavior
---
#### **4.6.9 Comparison Table and Use Case Recommendations**
| Feature | Cline | Roo-Cline |
|---------|-------|-----------|
| **Configuration** | Plain Markdown | YAML modes + Markdown |
| **Complexity** | Simple, beginner-friendly | Advanced, power users |
| **File Format** | `.clinerules` (file or dir) | `.roomodes` (YAML) |
| **Modes** | Single behavior | Multiple specialized modes |
| **Tool Permissions** | All tools available | Granular per-mode permissions |
| **File Restrictions** | None (global rules) | Regex-based per-mode |
| **Model Assignment** | Single model | Sticky models per mode |
| **Use Case** | General development | Complex workflows, specialized tasks |
| **Team Adoption** | Easy (just Markdown) | Requires YAML knowledge |
| **Cost Optimization** | Manual model switching | Automatic (cheap model for docs, powerful for code) |
| **Security** | Trust-based | Permission-based |
**When to Choose Cline:**
✅ **Use Cline if:**
- You want simple, straightforward configuration
- Your team prefers Markdown over YAML
- Human-in-the-loop is your primary workflow
- You don't need specialized modes
- Onboarding non-technical team members
- Quick setup is priority
**Example Cline User:** Freelance developer building standard web apps, occasional AI assistance for boilerplate and debugging.
---
**When to Choose Roo-Cline:**
✅ **Use Roo-Cline if:**
- You need granular control over AI behavior
- Working on large, complex codebases
- Different team members need different permissions
- Cost optimization through model switching is important
- You have specialized workflows (TDD, architecture, security audits)
- Want to restrict AI to specific file types
- Need mode-based context switching
**Example Roo-Cline User:** Enterprise team with:
- **Architects** using Architect mode (design docs only)
- **Developers** using Code mode (code editing)
- **QA Engineers** using Test mode (test files only)
- **Tech Writers** using Docs mode (Markdown only)
- **Security** using Audit mode (read-only analysis)
---
**Migration Path: Cline → Roo-Cline**
**Step 1: Install Roo-Cline** (both can coexist)
**Step 2: Convert Existing Rules**
bash
Your existing Cline rules
.clinerules
or
.clinerules/
01-standards.md
02-testing.md
**Create `.roomodes`:**
yaml
customModes:
-
slug: code
name: 💻 Code (from Cline rules)Copy content from .clinerules into customInstructions
customInstructions: |-
[Paste your Cline rules here]groups:
- read
- edit
- command
**Or Better: Create Specialized Modes**
yaml
customModes:
# General coding
- slug: code name: 💻 Code customInstructions: |- [Core coding standards from .clinerules] groups: [read, edit, command]
# Documentation writing
- slug: docs
name: 📝 Documentation
customInstructions: |-
[Documentation guidelines from .clinerules]
groups:
- read
- - edit
- fileRegex: .md$
- browser
# Testing
- slug: test
name: 🧪 Testing
customInstructions: |-
[Testing standards from .clinerules]
groups:
- read
- - edit
- fileRegex: .test.(ts|tsx)$
- command
**Step 3: Test Each Mode**
Try each mode with typical tasks:
- Code mode: Implement feature
- Docs mode: Update README
- Test mode: Write tests
**Step 4: Refine Permissions**
Based on usage, adjust `fileRegex` patterns and tool permissions.
---
This comprehensive Cline and Roo-Cline guide provides everything needed to:
1. **Start simple** with Cline's Markdown rules
2. **Scale up** to Roo-Cline's modes when complexity demands it
3. **Understand trade-offs** between simplicity and control
4. **Migrate** between tools as needs evolve
Both tools excel in their niches: **Cline for ease of use**, **Roo-Cline for power and flexibility**.
---
### **4.7 Zed Configuration**
Zed takes a fundamentally different architectural approach: **Markdown for AI instructions, JSON for editor settings**—no TOML for AI rules despite common misconceptions.
#### **4.7.1 The Dual-System Architecture**
**Critical Distinction:**
| System | Format | Purpose | Example Files |
|--------|--------|---------|---------------|
| **Editor Settings** | JSON | Configure IDE behavior, LSP, formatters, keybindings | `.zed/settings.json`, `~/.config/zed/settings.json` |
| **AI Instructions** | Markdown | Natural language guidance for AI agents | `.rules`, `.cursorrules`, `AGENTS.md` |
**Why This Matters:**
The confusion arose because:
1. ❌ **Zed does NOT use `.zed/rules.toml`** for AI configuration
2. ✅ **Zed DOES use TOML** for extension development (`extension.toml`) and language configs
3. ✅ **Zed reads `.rules` (Markdown)** for AI agent instructions
**What `.zed/settings.json` Contains:**
json
{
// Editor appearance
"theme": "One Dark",
"buffer_font_size": 14,
// Language server configuration
"lsp": {
"typescript-language-server": {
"initialization_options": {
"preferences": {
"includeInlayParameterNameHints": "all"
}
}
}
},
// Formatter settings
"formatter": {
"language_server": {
"name": "prettier"
}
},
// AI Agent configuration (model selection)
"agent": {
"default_model": {
"provider": "anthropic",
"model": "claude-3-5-sonnet-20241022"
},
"inline_assistant_model": {
"provider": "openai",
"model": "gpt-4o"
}
},
// Language-specific overrides
"languages": {
"TypeScript": {
"tab_size": 2,
"format_on_save": "on"
}
}
}
**What `.rules` (Markdown) Contains:**
markdown
Project Rules for AI Assistant
Tech Stack
- TypeScript 5.4, Next.js 15, React 19
- Tailwind CSS 4.0
- tRPC for APIs
Coding Standards
- Strict TypeScript mode
- Functional components only
- Server Components by default
Testing
- Vitest for unit tests
- Playwright for E2E
- 80% coverage minimum
#### **4.7.2 Rule File Priority Order**
Zed scans the **project root** for AI rule files in this priority:
| Priority | Filename | Source Tool |
|----------|----------|-------------|
| 1 | `.rules` | ⭐ Zed-specific (recommended) |
| 2 | `.cursorrules` | Cursor IDE compatibility |
| 3 | `.windsurfrules` | Windsurf compatibility |
| 4 | `.clinerules` | Cline compatibility |
| 5 | `.github/copilot-instructions.md` | GitHub Copilot compatibility |
| 6 | `AGENT.md` or `AGENTS.md` | Alternative open standard |
| 7 | `CLAUDE.md` | Claude Code compatibility |
| 8 | `GEMINI.md` | Gemini CLI compatibility |
**Important:** **Only the first matching file is used.** If you have both `.rules` and `.cursorrules`, Zed uses `.rules` and ignores `.cursorrules`.
**Cross-Tool Compatibility Strategy:**
**Option 1: Use `.rules` + Symlinks**
bash
Create Zed-native file
touch .rules
Symlink for other tools
ln -s .rules .cursorrules
ln -s .rules .windsurfrules
ln -s .rules AGENTS.md
**Option 2: Use Universal `AGENTS.md`**
bash
Create universal standard file
touch AGENTS.md
Zed will find and use it (priority 6)
Other tools also recognize it
**Option 3: Tool-Specific (Separate Files)**
bash
Each tool gets its own file
touch .rules # For Zed
touch .cursorrules # For Cursor
touch .windsurfrules # For Windsurf
Requires maintaining multiple files (not recommended)
#### **4.7.3 Plain Markdown Format (No Schema)**
**Key Principle:** Zed passes the **entire file content as-is** to the LLM. No parsing, no validation, no schema.
**Advantages:**
✅ **Maximum flexibility:** Write in any style that works
✅ **No learning curve:** Just Markdown
✅ **Natural for LLMs:** Models are trained on Markdown docs
✅ **Easy to maintain:** Human-readable, clean git diffs
✅ **Cross-tool compatible:** Works with any AI assistant
**Disadvantages:**
❌ **No validation:** Typos and errors not caught
❌ **No structure enforcement:** Can become disorganized
❌ **No conditional loading:** Entire file always loaded
**Complete Zed `.rules` Example:**
markdown
Zed AI Rules for [Project Name]
Project Context
Type: Full-stack web application
Stack: Next.js 15 + TypeScript + PostgreSQL
Architecture: Monorepo with Turborepo
Deployment: Vercel
Technology Stack
Frontend
- Framework: Next.js 15.1 (App Router, NOT Pages Router)
- Language: TypeScript 5.4 (strict mode)
- Styling: Tailwind CSS 4.0
- Components: shadcn/ui + Radix UI primitives
-
State Management:
- Server State: TanStack Query v5
- Client State: Zustand 4.5
- Forms: React Hook Form + Zod
Backend
- API: tRPC 11 (type-safe)
- Database: PostgreSQL 16
- ORM: Drizzle ORM 0.35
- Auth: NextAuth.js v5
DevOps
- Monorepo: Turborepo 2.0
- Package Manager: pnpm 9.0 (NOT npm/yarn)
- Testing: Vitest 2.0, Playwright 1.45
- CI/CD: GitHub Actions
Directory Structure
apps/
├── web/ # Next.js frontend
│ ├── app/ # App Router pages
│ ├── components/ # React components
│ └── lib/ # Client utilities
packages/
├── ui/ # Shared component library
├── database/ # Drizzle schema
├── types/ # Shared TypeScript types
└── config/ # Shared configs
Key Directories:
-
apps/web/app/: Next.js routes (App Router) -
apps/web/components/ui/: shadcn/ui primitives -
packages/database/: Drizzle schema definitions
Coding Standards
TypeScript Rules
Type Annotations
All functions MUST have explicit return types:
// ✅ CORRECT
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ WRONG - No return type
export function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Avoid any
// ✅ Use `unknown` + type guards
function processData(data: unknown) {
if (typeof data === 'object' && data !== null && 'id' in data) {
return data.id; // Type-safe
}
throw new Error('Invalid data shape');
}
// ❌ Never use `any`
function processData(data: any) {
return data.id; // No type safety!
}
Error Handling Pattern
// ✅ REQUIRED: Result type pattern
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const user = await db.query.users.findFirst({
where: eq(users.id, id)
});
if (!user) {
return { success: false, error: 'User not found' };
}
return { success: true, data: user };
} catch (error) {
logger.error('fetchUser failed', { id, error });
return { success: false, error: 'Database error' };
}
}
// ❌ FORBIDDEN: Silent error swallowing
async function fetchUser(id: string) {
try {
return await db.query.users.findFirst({ where: eq(users.id, id) });
} catch {
return null; // Silent failure!
}
}
React Patterns
Server vs Client Components
// ✅ Server Component (default - no directive)
async function UserProfilePage({ params }: { params: { id: string } }) {
// Can fetch data directly in Server Components
const user = await db.query.users.findFirst({
where: eq(users.id, params.id)
});
return <UserProfile user={user} />;
}
// ✅ Client Component (only when needed)
'use client';
import { useState } from 'react';
export function InteractiveCounter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
// Add 'use client' ONLY when:
// - Using hooks (useState, useEffect, etc.)
// - Event handlers (onClick, onChange, etc.)
// - Browser APIs (window, localStorage, etc.)
Component Size Limits
- Maximum 200 lines per component
- If exceeded: Extract sub-components
- Use composition over props drilling
// ✅ Composition pattern
<Card>
<CardHeader>
<CardTitle>User Profile</CardTitle>
</CardHeader>
<CardContent>
<UserDetails user={user} />
</CardContent>
<CardFooter>
<EditButton />
</CardFooter>
</Card>
// ❌ Props drilling
<Card
title="User Profile"
content={<UserDetails user={user} />}
footer={<EditButton />}
headerClassName="..."
contentClassName="..."
/>
Import Organization
// 1. React and external libraries
import { useState, useEffect } from 'react';
import { z } from 'zod';
// 2. Internal packages (@repo/*)
import { Button } from '@repo/ui';
import { User } from '@repo/types';
// 3. Application modules (@/*)
import { trpc } from '@/lib/trpc/client';
import { cn } from '@/lib/utils';
// 4. Relative imports
import { UserCard } from './UserCard';
import type { UserCardProps } from './types';
// 5. Styles
import './styles.css';
Testing Requirements
Coverage Targets
- Unit Tests: 80% minimum (business logic)
- Integration Tests: 70% minimum (API endpoints)
- E2E Tests: Critical user flows only
Test Commands
pnpm test # Run Vitest unit tests
pnpm test:watch # Watch mode
pnpm test:coverage # Generate coverage report
pnpm test:e2e # Run Playwright E2E tests
Unit Test Structure (Vitest)
import { describe, it, expect, beforeEach } from 'vitest';
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
// ARRANGE: Setup
service = new UserService(mockDb);
});
it('creates user with valid data', async () => {
// ARRANGE: Prepare data
const userData = { email: 'test@example.com', name: 'Test' };
// ACT: Execute
const result = await service.createUser(userData);
// ASSERT: Verify
expect(result.success).toBe(true);
expect(result.data).toMatchObject(userData);
});
it('rejects invalid email', async () => {
const invalidData = { email: 'not-an-email', name: 'Test' };
const result = await service.createUser(invalidData);
expect(result.success).toBe(false);
expect(result.error).toContain('Invalid email');
});
});
Common Pitfalls
Issue 1: Hot Reload with tRPC
Symptom: Changes to tRPC routers don't reflect
Solution: Restart dev server when adding/removing procedures
Issue 2: Mixing Server/Client Components
Symptom: "useState can only be used in Client Components"
Solution: Add 'use client' directive at top of file
Issue 3: Environment Variables
Symptom: process.env.NEXT_PUBLIC_VAR is undefined
Solution: All client-accessible vars MUST have NEXT_PUBLIC_ prefix
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com # ✅ Client-side
DATABASE_URL=postgresql://... # ❌ Server-only
Important Notes
-
Package Manager: Use
pnpmcommands (NOT npm) - JWT Storage: HttpOnly cookies ONLY (NEVER localStorage)
- Rate Limiting: 100 req/min (public), 1000 req/min (authenticated)
-
API Prefix: All routes use
/api/v1/* - Git Workflow: Feature branches, Conventional Commits, 1 approval required
Working with Zed AI
When using Zed's Agent Panel:
- Be specific about file locations and changes
- Reference patterns from existing code
- Ask for diffs instead of full file replacements
- Verify changes before accepting
These rules are optimized for Zed AI Agent. Last updated: 2025-11-29
**Why This Works:**
1. **No parsing overhead:** Zed passes entire file to LLM (efficient)
2. **Natural language:** LLMs excel at understanding Markdown docs
3. **Flexible structure:** Organize however makes sense for your project
4. **Cross-tool compatible:** Same file works with Cursor, Windsurf, Claude Code, etc.
#### **4.7.4 Integration with Agent Panel**
**How Zed Loads Rules:**
1. **User opens Agent Panel** (`cmd-shift-?` or sparkles icon)
2. **Zed scans project root** for rule files (priority order above)
3. **First matching file loaded** into system prompt
4. **User starts chat** or makes edit request
5. **AI receives**:
- Model configuration from `.zed/settings.json` (`agent` section)
- Instructions from `.rules` (or other rule file)
- Current file context
- User's request
6. **AI generates response** following loaded rules
7. **User reviews and applies** changes
**Agent Panel Workflow Example:**
User: "Add error handling to the fetchUser function"
Zed Agent:
- Loads
.rules(sees error handling pattern requirement) - Loads
src/api/users.ts(current file) - Applies Result pattern from rules
- Generates:
async function fetchUser(id: string): Promise> {
try {
const user = await db.query.users.findFirst({
where: eq(users.id, id)
});
if (!user) {
return { success: false, error: 'User not found' };
}
return { success: true, data: user };
} catch (error) {
logger.error('fetchUser failed', { id, error });
return { success: false, error: 'Database error' };
}
}
- User reviews diff
- User accepts or requests modifications
**Model Configuration in Settings:**
json
// .zed/settings.json
{
"agent": {
// Main chat model
"default_model": {
"provider": "anthropic",
"model": "claude-3-5-sonnet-20241022"
},
// Inline assistant (faster, cheaper)
"inline_assistant_model": {
"provider": "openai",
"model": "gpt-4o-mini"
}
}
}
**Global vs Project Settings:**
| Setting | File | Scope |
|---------|------|-------|
| **Global** | `~/.config/zed/settings.json` | All projects |
| **Project** | `.zed/settings.json` | Current project only |
**Precedence:** Project settings override global settings.
#### **4.7.5 Best Practices for Zed Rules**
**1. Keep Under 500 Lines**
Zed loads entire file into context. Aim for:
- **Small projects**: 100-200 lines
- **Medium projects**: 200-350 lines
- **Large projects**: 350-500 lines
- **If exceeded**: Split into multiple files and choose tool that supports that (Cursor, Roo-Cline)
**2. Prioritize High-Impact Rules**
markdown
✅ GOOD: Specific, actionable rules
- TypeScript strict mode required
- Server Components by default (add 'use client' only when needed)
- Result type for error handling
- 80% test coverage minimum
❌ BAD: Vague, generic advice
- Write clean code
- Follow best practices
- Be consistent
- Make it performant
**3. Use Clear Hierarchy**
markdown
Top-Level: Major Categories
Second-Level: Specific Topics
Third-Level: Implementation Details
- Bullet points for specific rules
- Code examples with ✅/❌ indicators
**4. Include Both Good and Bad Examples**
markdown
Error Handling
✅ CORRECT
try {
await operation();
} catch (error) {
logger.error('Context', { error });
throw new AppError('User message', 500);
}
❌ WRONG
try {
await operation();
} catch {
// Silent failure
}
**5. Version Control the File**
bash
Commit .rules with your code
git add .rules
git commit -m "docs: update AI rules for new authentication flow"
Track changes over time
git log --oneline .rules
**6. Update Incrementally**
Don't try to write perfect rules upfront:
1. Start with 10-15 core rules
2. Use Zed AI for a week
3. Note friction points (where AI gets it wrong)
4. Add specific rules addressing those issues
5. Repeat
**7. Use Markdown Features**
markdown
State Management
| Use Case | Solution | Example |
|---|---|---|
| Server data | TanStack Query | useQuery('users', fetchUsers) |
| Client state | useState | const [open, setOpen] = useState(false) |
| Global client | Zustand | const user = useUserStore(s => s.user) |
API Patterns
Important: All endpoints return standardized JSON format
{
"success": boolean,
"data": T | null,
"error": string | null
}
---
#### **4.7.6 Zed + Multi-Tool Strategy**
**Scenario: Team Uses Zed + Cursor + Windsurf**
**Option 1: Universal File with Symlinks**
bash
Create Zed-native file
vim .rules
[Add all project rules]
Symlink for other tools
ln -s .rules .cursorrules
ln -s .rules .windsurfrules
ln -s .rules AGENTS.md
Now all tools read same rules
**Option 2: Shared `AGENTS.md` Standard**
bash
Create universal standard file (recognized by all tools)
vim AGENTS.md
[Add all project rules]
Zed finds it (priority 6)
Cursor finds it (fallback)
Windsurf finds it (fallback)
Claude Code finds it
Aider can load it via .aider.conf.yml
**Option 3: Tool-Specific with Shared Core**
bash
Shared core rules
vim PROJECT_RULES.md
Tool-specific wrappers
.rules (Zed)
cat > .rules << 'EOF'
Zed AI Rules
See PROJECT_RULES.md for complete guidelines.
Zed-Specific Notes
- Use Agent Panel for complex refactoring
- Inline assistant for quick fixes EOF
.cursorrules (Cursor)
cat > .cursorrules << 'EOF'
Cursor Rules
See PROJECT_RULES.md for complete guidelines.
Cursor-Specific Notes
- Use Composer for multi-file changes
- Use CMD-K for inline edits EOF
---
### Summary: Zed's Philosophy
Zed's approach reflects a **separation of concerns**:
- **Settings (JSON)**: Machine-readable config (editor behavior, model selection, LSP)
- **Rules (Markdown)**: Human-readable instructions (coding standards, patterns)
This architecture:
✅ Keeps AI instructions simple and portable
✅ Avoids vendor lock-in (rules work with any tool)
✅ Leverages Markdown's strengths for natural language
✅ Makes cross-tool collaboration seamless
**For Zed users:** Focus on writing clear, concise Markdown rules in `.rules`, configure your models in `.zed/settings.json`, and enjoy Zed's fast, native AI integration.
---
This completes the comprehensive Zed configuration guide, clarifying the architectural split and providing production-ready examples and strategies.
---
## **V. Multi-Tool Strategy & Portability**
Managing AI coding rules across multiple tools is no longer optional—it's the reality of modern development teams. This section provides battle-tested strategies for maintaining consistency while supporting tool diversity.
### **5.1 The AGENTS.md Standard**
**AGENTS.md** has emerged as the closest thing to a universal standard, with adoption across 20,000+ open-source projects as of mid-2025.
#### **5.1.1 What Is AGENTS.md?**
**Origin**: Proposed by the open-source community in early 2024 as a tool-agnostic format for AI coding instructions.
**Specification**: https://agents.md
**Core Principles:**
1. **Plain Markdown** (no proprietary formats)
2. **Human-readable** (developers can edit without tools)
3. **Git-friendly** (clean diffs, easy reviews)
4. **Tool-agnostic** (works with any AI assistant)
**Tool Support:**
| Tool | Support Level | Notes |
|------|---------------|-------|
| **Cursor** | ✅ Native (fallback) | Reads `AGENTS.md` if no `.cursor/rules/` found |
| **Windsurf** | ✅ Native (fallback) | Reads if no `.windsurfrules` present |
| **Claude Code** | ✅ Native | Loads `AGENTS.md` (priority after `CLAUDE.md`) |
| **Aider** | ✅ Via config | Load via `read: [AGENTS.md]` in `.aider.conf.yml` |
| **Continue.dev** | ✅ Via rules | Reference in `.continue/rules/` |
| **Cline** | ✅ Native (fallback) | Reads if no `.clinerules` found |
| **Zed** | ✅ Native | Priority 6 in file scan order |
| **GitHub Copilot** | ⚠️ Manual | Must reference or symlink to `.github/copilot-instructions.md` |
| **JetBrains** | ⚠️ Manual | Can symlink to `.aiassistant/rules/` |
#### **5.1.2 Complete AGENTS.md Template**
markdown
AGENTS.md
Version: 1.0.0
Last Updated: 2025-11-29
Compatible With: Cursor, Windsurf, Claude Code, Cline, Zed, Aider, Continue.dev
PROJECT OVERVIEW
Name: [Your Project Name]
Description: [One-sentence description of what this project does]
Primary Goal: [Main objective or problem being solved]
Example:
Full-stack e-commerce platform built with Next.js and PostgreSQL.
Primary Goal: Support 10,000 concurrent users with <100ms API response times.
TECHNOLOGY STACK
Frontend
- Framework: Next.js 15.1 (App Router)
- Language: TypeScript 5.4 (strict mode)
- Styling: Tailwind CSS 4.0
- State Management: TanStack Query + Zustand
- Testing: Vitest, Testing Library, Playwright
Backend
- Runtime: Node.js 20.11 LTS
- Framework: Express.js + tRPC 11
- Database: PostgreSQL 16
- ORM: Drizzle ORM 0.35
- Authentication: NextAuth.js v5
DevOps
- Monorepo: Turborepo 2.0
- Package Manager: pnpm 9.0 (NOT npm/yarn)
- CI/CD: GitHub Actions
- Hosting: Vercel (production), Railway (staging)
PROJECT STRUCTURE
root/
├── apps/
│ ├── web/ # Next.js frontend
│ │ ├── app/ # App Router pages
│ │ ├── components/ # React components
│ │ └── lib/ # Client utilities
│ └── api/ # Standalone API (future)
├── packages/
│ ├── ui/ # Shared component library
│ ├── database/ # Drizzle schema & migrations
│ ├── types/ # Shared TypeScript types
│ └── config/ # ESLint, TypeScript configs
├── docs/ # Documentation
├── scripts/ # Build scripts
└── AGENTS.md # This file
Key Directories:
-
apps/web/app/: Next.js routes (App Router, NOT Pages Router) -
apps/web/components/ui/: shadcn/ui primitives -
packages/database/: Drizzle ORM schema definitions
CODING STANDARDS
Universal Principles
- Type Safety: TypeScript strict mode, explicit return types
- Error Handling: Never swallow errors silently
- Testing: 80% coverage minimum for business logic
- Documentation: JSDoc for public APIs
TypeScript Rules
Type Annotations
// ✅ ALWAYS: Explicit return types
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ NEVER: Implicit return types
export function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
Error Handling Pattern
// ✅ REQUIRED: Result type
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const user = await db.query.users.findFirst({
where: eq(users.id, id)
});
if (!user) {
return { success: false, error: 'User not found' };
}
return { success: true, data: user };
} catch (error) {
logger.error('fetchUser failed', { id, error });
return { success: false, error: 'Database error occurred' };
}
}
React Patterns
Server vs Client Components
// ✅ Server Component (default, no directive)
async function UserProfilePage({ params }: { params: { id: string } }) {
const user = await db.query.users.findFirst({
where: eq(users.id, params.id)
});
return <UserProfile user={user} />;
}
// ✅ Client Component (only when needed)
'use client';
import { useState } from 'react';
export function InteractiveCounter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Add 'use client' ONLY when:
- Using hooks (useState, useEffect, useContext)
- Event handlers (onClick, onChange)
- Browser APIs (window, localStorage)
- Third-party libraries using client features
Import Organization
// 1. External dependencies
import { useState, useEffect } from 'react';
import { z } from 'zod';
// 2. Internal packages
import { Button } from '@repo/ui';
import { User } from '@repo/types';
// 3. Application modules
import { trpc } from '@/lib/trpc/client';
import { cn } from '@/lib/utils';
// 4. Relative imports
import { UserCard } from './UserCard';
// 5. Styles
import './styles.css';
TESTING APPROACH
Coverage Requirements
- Unit Tests: 80% minimum (business logic)
- Integration Tests: 70% minimum (API endpoints)
- E2E Tests: Critical user flows only
Test Structure (AAA Pattern)
describe('FeatureName', () => {
it('should handle happy path', () => {
// ARRANGE: Set up test data
const input = createTestData();
// ACT: Execute function
const result = performOperation(input);
// ASSERT: Verify outcome
expect(result).toEqual(expected);
});
it('should handle error case', () => {
// Test error scenarios
});
it('should handle edge case', () => {
// Test boundary conditions
});
});
Commands
pnpm test # Run unit tests
pnpm test:watch # Watch mode
pnpm test:coverage # Generate coverage report
pnpm test:e2e # Run E2E tests
COMMON COMMANDS
Development
pnpm dev # Start dev server (localhost:3000)
pnpm build # Production build
pnpm lint # Run ESLint
pnpm type-check # TypeScript validation
pnpm format # Run Prettier
Database
pnpm db:push # Push schema to database
pnpm db:studio # Open Drizzle Studio
pnpm db:generate # Generate migrations
pnpm db:migrate # Apply migrations
Deployment
pnpm deploy:staging # Deploy to staging
pnpm deploy:production # Deploy to production
KNOWN ISSUES & WORKAROUNDS
Issue 1: Hot Reload with tRPC
Symptom: Changes to tRPC routers don't reflect in frontend
Solution: Restart dev server when adding/removing procedures
Issue 2: Server Component Hydration Errors
Symptom: "Text content did not match" console errors
Solution: Ensure all async operations in Server Components are awaited
Issue 3: Environment Variables
Symptom: process.env.NEXT_PUBLIC_VAR is undefined on client
Solution: All client-accessible vars MUST have NEXT_PUBLIC_ prefix
# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com # ✅ Client-accessible
DATABASE_URL=postgresql://... # ❌ Server-only
IMPORTANT NOTES
API
- Rate Limiting: 100 req/min (public), 1000 req/min (authenticated)
-
Versioning: All routes prefixed with
/api/v1/ -
Format: All endpoints return
{ success: boolean, data: T | null, error: string | null }
Authentication
- JWT Expiration: Access tokens expire after 15 minutes
- Refresh Tokens: Valid for 7 days, rotated on use
- Storage: HttpOnly cookies ONLY (NEVER localStorage)
Security
- Secrets: NEVER commit to git, use environment variables
- Input Validation: ALWAYS validate with Zod schemas
- SQL Injection: Use parameterized queries (Drizzle ORM handles this)
Git Workflow
-
Branches:
feat/,fix/,docs/prefixes -
Commits: Conventional Commits format (
feat:,fix:,docs:) - PRs: Require 1 approval + passing CI
DEPLOYMENT CHECKLIST
Before deploying:
- [ ] Run full test suite:
pnpm test - [ ] TypeScript check passes:
pnpm type-check - [ ] Linter passes:
pnpm lint --max-warnings 0 - [ ] Production build succeeds:
pnpm build - [ ] Environment variables configured in platform
- [ ] Database migrations applied
- [ ] Secrets rotated (if compromised)
WORKING WITH AI ASSISTANTS
General Guidelines
- Be specific: Reference exact file paths and line numbers
- Show examples: Provide existing code patterns to follow
- Request diffs: Ask for changes in diff format for review
- Verify output: Always review before accepting changes
Tool-Specific Notes
Cursor:
- Use Composer (CMD-I) for multi-file changes
- Use CMD-K for inline edits
- Rules auto-load from
.cursor/rules/(or falls back to this file)
Windsurf:
- Use Cascade for deep reasoning tasks
- Rules load from
.windsurf/rules/(or falls back to this file) - Wave 8+ supports multiple rule files with activation modes
Claude Code:
- Use
/initto generate project-specific rules - Use
#key during coding to capture rules dynamically - Prefers
CLAUDE.mdbut readsAGENTS.mdas fallback
Aider:
- Configure in
.aider.conf.ymlwithread: [AGENTS.md] - Use
--architectmode for complex refactoring - Conventions file is cached for efficiency
Cline/Roo-Cline:
- Cline reads this file if no
.clinerulespresent - Roo-Cline: Reference in mode-specific rules
- Use rules bank pattern for multi-context projects
Zed:
- Create
.rulessymlinked toAGENTS.md - Or Zed reads
AGENTS.mddirectly (priority 6) - Configure models in
.zed/settings.json
ADDITIONAL RESOURCES
-
Architecture Diagrams:
docs/architecture.md -
API Documentation:
docs/api-reference.md -
Database Schema:
packages/database/schema.ts -
Deployment Guide:
docs/deployment.md
MAINTENANCE
How to Update This File
- When: Tech stack changes, new patterns emerge, issues discovered
- Process: Create PR, require 1 approval, update version and date
- Keep Concise: Target <50KB, link to external docs for details
Version History
- 1.0.0 (2025-11-29): Initial AGENTS.md creation
- Future versions documented here
This AGENTS.md file follows the universal standard for AI coding instructions.
Compatible with 10+ AI coding tools. For specification, see: https://agents.md
---
### **5.2 Single Source of Truth (SSOT) Architecture**
The SSOT pattern solves the multi-tool problem: **maintain one canonical file, distribute to tool-specific locations**.
#### **5.2.1 The Core Pattern**
my-project/
├── AGENTS.md # ⭐ Master file (SSOT)
├── .rules -> AGENTS.md # Symlink for Zed
├── .cursorrules -> AGENTS.md # Symlink for Cursor
├── .windsurfrules -> AGENTS.md # Symlink for Windsurf
├── .clinerules -> AGENTS.md # Symlink for Cline
├── CLAUDE.md -> AGENTS.md # Symlink for Claude Code
├── .github/
│ └── copilot-instructions.md -> ../../AGENTS.md # Copilot
└── .aiassistant/
└── rules/
└── main.md -> ../../../AGENTS.md # JetBrains
**Create Symlinks:**
**Unix/Linux/macOS:**
bash
!/bin/bash
sync-ai-rules.sh
Create symlinks to AGENTS.md
ln -sf AGENTS.md .rules
ln -sf AGENTS.md .cursorrules
ln -sf AGENTS.md .windsurfrules
ln -sf AGENTS.md .clinerules
ln -sf AGENTS.md CLAUDE.md
GitHub Copilot
mkdir -p .github
ln -sf ../AGENTS.md .github/copilot-instructions.md
JetBrains
mkdir -p .aiassistant/rules
ln -sf ../../AGENTS.md .aiassistant/rules/main.md
echo "✅ AI rules synced across all tools"
**Windows (PowerShell):**
powershell
sync-ai-rules.ps1
New-Item -ItemType SymbolicLink -Path ".rules" -Target "AGENTS.md"
New-Item -ItemType SymbolicLink -Path ".cursorrules" -Target "AGENTS.md"
New-Item -ItemType SymbolicLink -Path ".windsurfrules" -Target "AGENTS.md"
New-Item -ItemType SymbolicLink -Path ".clinerules" -Target "AGENTS.md"
New-Item -ItemType SymbolicLink -Path "CLAUDE.md" -Target "AGENTS.md"
New-Item -ItemType Directory -Path ".github" -Force
New-Item -ItemType SymbolicLink -Path ".github\copilot-instructions.md" -Target "..\AGENTS.md"
Write-Host "✅ AI rules synced across all tools"
#### **5.2.2 Alternative: Copy Script (No Symlinks)**
For environments that don't support symlinks (or for files that need slight tool-specific variations):
bash
!/bin/bash
sync-ai-rules-copy.sh
echo "📋 Copying AGENTS.md to tool-specific locations..."
Direct copies
cp AGENTS.md .rules
cp AGENTS.md .cursorrules
cp AGENTS.md .windsurfrules
cp AGENTS.md .clinerules
cp AGENTS.md CLAUDE.md
GitHub Copilot
mkdir -p .github
cp AGENTS.md .github/copilot-instructions.md
JetBrains
mkdir -p .aiassistant/rules
cp AGENTS.md .aiassistant/rules/main.md
Aider (reference in config)
cat > .aider.conf.yml << 'EOF'
model: claude-3-5-sonnet-20241022
read:
- AGENTS.md EOF
echo "✅ AI rules copied to all tool locations"
echo "⚠️ Remember to run this script after updating AGENTS.md"
**Add to Pre-Commit Hook:**
bash
.git/hooks/pre-commit
!/bin/bash
Check if AGENTS.md was modified
if git diff --cached --name-only | grep -q "AGENTS.md"; then
echo "📋 AGENTS.md modified, syncing to other tools..."
./sync-ai-rules-copy.sh
# Stage the updated files
git add .rules .cursorrules .windsurfrules .clinerules CLAUDE.md
git add .github/copilot-instructions.md
git add .aiassistant/rules/main.md
echo "✅ Tool-specific rule files updated"
fi
#### **5.2.3 Hybrid Approach: Shared Core + Tool-Specific Layers**
For projects where tools need slightly different instructions:
**Directory Structure:**
my-project/
├── AI_RULES.md # Core shared rules
├── .cursor/
│ └── rules/
│ ├── index.mdc # Imports AI_RULES.md + Cursor specifics
│ └── cursor-composer.mdc # Cursor-only: Composer mode tips
├── .windsurf/
│ └── rules/
│ ├── 01-shared.md -> ../../AI_RULES.md # Symlink
│ └── 02-cascade-specific.md # Windsurf-only: Cascade tips
├── .roo/
│ └── rules-code/
│ ├── shared.md -> ../../../AI_RULES.md
│ └── roo-modes.md # Roo-only: Mode switching guide
├── CLAUDE.md # Claude-specific wrapper
└── AGENTS.md -> AI_RULES.md # Public standard (symlink)
**Cursor `index.mdc` (Wrapper):**
markdown
description: "Project-wide standards (shared across all tools)"
alwaysApply: true
Project Standards
See AI_RULES.md for complete shared rules across all AI tools.
Cursor-Specific Notes
Composer Mode (CMD-I)
Use Composer for:
- Multi-file refactoring
- Creating new features spanning multiple files
- Architectural changes
Inline Edit (CMD-K)
Use CMD-K for:
- Single-file changes
- Quick fixes
- Renaming across single file
CMD-L Chat
Use Chat for:
- Asking questions
- Exploring alternatives
- Explaining code
**Windsurf `02-cascade-specific.md`:**
markdown
Windsurf Cascade-Specific Guidelines
When to Use Cascade
- Complex multi-step reasoning
- Debugging hard-to-reproduce issues
- Architectural planning
- Code review and optimization
Cascade Best Practices
- Give Cascade time to reason (don't interrupt)
- Provide full context upfront
- Let it propose solutions before implementing
- Review its reasoning process
Flows (Wave 8+)
Use custom flows for:
- TDD cycles (Red → Green → Refactor)
- Deployment checklists
- Code review procedures
**Claude Code `CLAUDE.md` (Wrapper):**
markdown
Claude Code Instructions
Shared Rules
See AI_RULES.md for complete project standards.
Claude Code Workflow
/init Command
Use /init to generate project-specific rules.
Already done—this file is the result.
# Key During Coding
Press # key during sessions to capture recurring patterns.
Claude auto-updates CLAUDE.md.
@imports
Reference additional context:
- @docs/architecture.md
- @packages/database/schema.ts
**Benefits of Hybrid Approach:**
✅ **Shared baseline**: Core standards consistent across tools
✅ **Tool-specific optimization**: Leverage unique features
✅ **Maintainability**: Update core rules once, tool tips independently
✅ **Flexibility**: Adapt to team's multi-tool reality
---
### **5.3 Migration Workflows**
#### **5.3.1 Cursor → Aider Migration**
**Scenario**: Developer uses Cursor, wants to add Aider for CLI workflow.
**Step 1: Export Cursor Rules**
bash
If using .cursorrules (legacy)
cp .cursorrules AGENTS.md
If using .cursor/rules/ (modern)
Combine multiple .mdc files into one
cat .cursor/rules/*.mdc > AGENTS.md
Clean up YAML frontmatter (Aider doesn't need it)
Manually edit AGENTS.md to remove --- blocks
**Step 2: Create Aider Config**
yaml
.aider.conf.yml
model: claude-3-5-sonnet-20241022
weak-model: gpt-4o-mini
Load the shared rules
read:
- AGENTS.md
Aider-specific settings
auto-lint: true
lint-cmd:
- "typescript: eslint --fix"
auto-commits: false
show-diffs: true
**Step 3: Symlink for Consistency**
bash
Keep Cursor working with same rules
ln -sf AGENTS.md .cursorrules
Now both tools use identical rules
**Step 4: Test Both Tools**
bash
Test Aider
aider --message "Add error handling to fetchUser function"
Test Cursor (open in IDE)
Both should follow same patterns
---
#### **5.3.2 Claude Code → Continue.dev Migration**
**Scenario**: Team uses Claude Code, wants to add Continue.dev for VS Code users.
**Step 1: Copy CLAUDE.md**
bash
cp CLAUDE.md AGENTS.md
**Step 2: Create Continue.dev Config**
yaml
.continue/config.yaml
name: team-config
version: 1.0.0
schema: v1
models:
- name: Claude 3.5 Sonnet provider: anthropic model: claude-3-5-sonnet-20241022 apiKey: ${{ secrets.ANTHROPIC_API_KEY }} roles: [chat, edit, apply]
context:
- provider: code
- provider: codebase params: nRetrieve: 30 nFinal: 5
No inline rules—use markdown file instead
**Step 3: Create Continue.dev Rule File**
markdown
name: Project Standards
alwaysApply: true
Project Standards
See AGENTS.md for complete rules.
This file exists for Continue.dev compatibility.
**Or use `chatOptions`:**
yaml
models:
- name: Claude 3.5 Sonnet chatOptions: baseSystemMessage: | See AGENTS.md for complete project rules. [Optionally paste key rules here]
**Step 4: Verify Both Work**
bash
Claude Code users continue as normal
claude "Add validation to user input"
Continue.dev users (VS Code)
Open Continue panel, verify rules loaded
---
#### **5.3.3 From No Rules → Multi-Tool Setup**
**Scenario**: Project has no rules, team wants to adopt AI assistants.
**Step 1: Initial Assessment**
bash
Answer these questions:
1. What tools does the team use?
- Cursor (3 people)
- VS Code with Continue.dev (2 people)
- Terminal/CLI with Aider (2 people)
- JetBrains with AI Assistant (1 person)
2. What are the top 5 pain points?
- Inconsistent error handling
- Mix of class/functional components
- No testing standards
- Environment variables in wrong places
- Inconsistent API response formats
**Step 2: Create Minimal AGENTS.md**
markdown
AGENTS.md
Tech Stack
- TypeScript, React, Node.js, PostgreSQL
Top Priority Rules
1. Error Handling
All async functions return Result:
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
2. React Components
Functional components only. No classes.
3. Testing
Every feature needs tests. Run npm test before committing.
4. Environment Variables
Use .env.local (never commit secrets).
5. API Format
All endpoints return:
{
"success": boolean,
"data": any,
"error": string | null
}
**Step 3: Distribute to All Tools**
bash
./sync-ai-rules.sh # Creates symlinks
**Step 4: 2-Week Trial**
- Team uses AI assistants for 2 weeks
- Developers note where AI gets it wrong
- Weekly team meeting to discuss friction points
**Step 5: Iterate**
markdown
After 2 weeks, expand AGENTS.md
Additional Rules Discovered
6. State Management
- Server state: TanStack Query
- Client state: Zustand
- Never: Redux (legacy, being phased out)
7. File Structure
- Components:
src/components/[feature]/[Component].tsx - Hooks:
src/hooks/use[Feature].ts - Utils:
src/lib/[category]/[utility].ts
8. Import Order
[Team discovered AI was mixing import order]
- External deps
- Internal packages
- Relative imports
**Step 6: Continuous Improvement**
bash
Add to sprint retrospectives
"What AI patterns need clarification?"
Update AGENTS.md
Commit with team
Sync to all tools
---
### **5.4 Team Collaboration Strategies**
#### **5.4.1 Version Control Best Practices**
**What to Commit:**
bash
✅ ALWAYS COMMIT
AGENTS.md # Master rules
.aider.conf.yml # Aider config (no secrets)
.continue/config.yaml # Continue config (no API keys)
.continue/rules/.md # Continue rule files
.cursor/rules/.mdc # Cursor rules (not settings)
.roomodes # Roo-Cline modes
.clinerules # Cline rules
.zed/settings.json # Zed project settings (no secrets)
.aiassistant/rules/*.md # JetBrains rules
⚠️ MAYBE COMMIT (team decision)
.cursorrules # If using legacy
.windsurfrules # Windsurf rules
.github/copilot-instructions.md # Copilot rules
**What to .gitignore:**
gitignore
❌ NEVER COMMIT
.env # Secrets
.env.local # Local environment variables
*.key # API keys
*_history # Aider history files
.aider.llm.history # LLM conversation logs
User-specific (don't commit)
.cursor/settings.json # User preferences (not rules)
.vscode/settings.json # User VS Code settings
.idea/workspace.xml # JetBrains workspace (not rules)
Generated (don't commit)
.continue/index/ # Continue.dev codebase index
**Sample `.gitignore` Addition:**
gitignore
AI Assistant Configuration
===========================
Commit these (shared team rules)
AGENTS.md
.cursor/rules/
.continue/rules/
.aider.conf.yml
.roomodes
Don't commit these (secrets, user-specific, generated)
.env
.env.local
*.key
.aider.input.history
.aider.chat.history.md
.aider.llm.history
.cursor/settings.json
.continue/config.yaml # If it contains API keys
.continue/index/
---
#### **5.4.2 Pull Request Review Process**
**When Rules Change:**
markdown
PR Template Addition
AI Rules Changes
- [ ] Updated AGENTS.md with new standards
- [ ] Synced to tool-specific locations (ran ./sync-ai-rules.sh)
- [ ] Tested with at least 2 different AI tools
- [ ] Documented reason for change in commit message
- [ ] No secrets or API keys in config files
What Changed
- [Describe rule changes]
Why
- [Explain motivation]
Testing
- [How you verified AI follows new rules]
**Review Checklist:**
markdown
Reviewer Checklist: AI Rules Changes
- [ ] Changes are clear and specific (not vague)
- [ ] Examples included (good + bad code)
- [ ] No conflicts with existing rules
- [ ] No sensitive information (secrets, internal IPs, etc.)
- [ ] Spelling and grammar correct
- [ ] Markdown formatting valid
- [ ] Cross-tool compatibility maintained
---
#### **5.4.3 Onboarding New Team Members**
**Day 1: Developer Onboarding Checklist**
markdown
AI Coding Assistant Setup
Step 1: Choose Your Tool
We support:
- Cursor (recommended for beginners)
- Windsurf (advanced, deep reasoning)
- VS Code + Continue.dev (free, open-source)
- Aider (CLI-based, powerful)
- JetBrains AI Assistant (if using IntelliJ/PyCharm)
Step 2: Install & Configure
Option A: Cursor
- Download Cursor: https://cursor.sh
- Open project folder
- Rules auto-load from
.cursor/rules/✅
Option B: VS Code + Continue.dev
- Install Continue.dev extension
- Open project folder
- Configure API key (see team 1Password)
- Rules auto-load from
.continue/rules/✅
Option C: Aider (CLI)
- Install:
pip install aider-chat - Set API key:
export ANTHROPIC_API_KEY=xxx - Run:
aider - Rules auto-load from AGENTS.md ✅
Step 3: Verify Rules Loaded
Ask your AI assistant:
"What are the top 3 coding standards for this project?"
Expected answer should mention:
- TypeScript strict mode
- Error handling with Result type
- Testing requirements
If AI doesn't mention these, contact #dev-tools channel.
Step 4: Try Your First Task
Pick a good first issue labeled good-first-issue and:
- Ask AI to explain the codebase area
- Implement the fix with AI assistance
- Review AI's suggestions carefully
- Run tests before committing
Need Help?
- Slack: #ai-coding-help
- Docs: docs/ai-assistant-guide.md
- Ask: @tech-lead or @ai-champion
---
#### **5.4.4 Governance for Enterprise**
**For teams 50+ developers:**
**1. Centralized Rules Repository**
company-ai-rules/
├── universal/
│ ├── AGENTS.md # Company-wide standards
│ ├── security.md # Security requirements
│ └── compliance.md # SOC2, GDPR, etc.
├── stacks/
│ ├── typescript-react.md # React stack rules
│ ├── python-fastapi.md # Python stack rules
│ └── java-spring.md # Java stack rules
└── teams/
├── platform/ # Platform team specific
├── frontend/ # Frontend team specific
└── backend/ # Backend team specific
**2. Project Import Pattern**
bash
In each project repo
Import company standards
curl https://raw.githubusercontent.com/company/ai-rules/main/universal/AGENTS.md > .company-standards.md
Create project-specific AGENTS.md that references it
cat > AGENTS.md << 'EOF'
Project AI Rules
Company Standards
See .company-standards.md for universal company rules.
Project-Specific Rules
[Add project-specific overrides here]
EOF
**3. Automated Compliance Checks**
yaml
.github/workflows/ai-rules-compliance.yml
name: AI Rules Compliance
on:
pull_request:
paths:
- 'AGENTS.md'
- '.cursor/rules/'
- '.continue/rules/'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check for secrets
run: |
if grep -rE '(api[_-]?key|secret|password|token).*=.*[a-zA-Z0-9]{20,}' AGENTS.md .cursor .continue; then
echo "❌ Secrets detected in rules files"
exit 1
fi
- name: Validate Markdown
run: |
npm install -g markdownlint-cli
markdownlint AGENTS.md
- name: Check company standards referenced
run: |
if ! grep -q ".company-standards.md" AGENTS.md; then
echo "⚠️ AGENTS.md should reference .company-standards.md"
exit 1
fi
**4. AI Rules Champion Role**
Designate 1-2 people per 50 developers as "AI Rules Champions":
**Responsibilities:**
- Review PRs modifying AI rules
- Maintain company-wide standards repo
- Help teams adapt rules to their needs
- Collect feedback on rule effectiveness
- Run quarterly rules audit
**Quarterly Audit Process:**
1. Survey teams: "Which rules are most helpful? Least?"
2. Analyze AI-generated code for standards compliance
3. Update rules based on findings
4. Publish changelog and training materials
---
This completes the comprehensive multi-tool strategy guide, covering SSOT architecture, migration paths, team collaboration, and enterprise governance.
---
## **VI. Best Practices & Optimization**
### **6.1 Rule Writing Excellence**
#### **6.1.1 Specificity vs. Brevity Balance**
**The Tension:**
- ⚠️ **Too specific**: Rules become a maintenance nightmare, covering every edge case
- ⚠️ **Too brief**: AI misinterprets vague guidance, produces inconsistent code
**The Solution: The 80/20 Rule**
Focus on the **20% of patterns that solve 80% of problems**.
**Framework: Three Tiers of Specificity**
| Tier | Length | Content | When to Use |
|------|--------|---------|-------------|
| **Tier 1: Principles** | 1 sentence | High-level guideline | Always include (5-10 principles) |
| **Tier 2: Patterns** | 3-5 sentences + example | Common implementation pattern | Include 10-15 most frequent |
| **Tier 3: Edge Cases** | Detailed explanation + multiple examples | Specific gotcha or complex scenario | Include only 3-5 critical ones |
**Example Application:**
markdown
Error Handling
Tier 1: Principle (Always Include)
All async operations must handle errors explicitly and never fail silently.
Tier 2: Pattern (Include for Common Case)
Use Result type for operations that can fail:
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
async function operation(): Promise<Result<Data>> {
try {
const data = await fetch();
return { success: true, data };
} catch (error) {
logger.error('Operation failed', { error });
return { success: false, error: 'User-friendly message' };
}
}
Tier 3: Edge Case (Only for Critical Gotcha)
When retrying failed requests, use exponential backoff to avoid overwhelming services:
async function fetchWithRetry(url: string, maxRetries = 3): Promise<Result<Data>> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const data = await fetch(url);
return { success: true, data };
} catch (error) {
if (attempt === maxRetries - 1) {
return { success: false, error: 'Max retries exceeded' };
}
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
**Length Guidelines by Project Size:**
| Project Size | Total Lines | Tier 1 | Tier 2 | Tier 3 |
|--------------|------------|--------|--------|--------|
| **Small** (1-3 devs) | 150-250 | 10 principles | 10 patterns | 3 edge cases |
| **Medium** (5-15 devs) | 250-400 | 15 principles | 15 patterns | 5 edge cases |
| **Large** (15-50 devs) | 400-600 | 20 principles | 20 patterns | 8 edge cases |
| **Enterprise** (50+ devs) | 600-1000 (modular) | 25 principles | 30 patterns | 10 edge cases |
**Optimization Strategy: Start Small, Add Incrementally**
**Week 1: Minimum Viable Rules (150 lines)**
markdown
Project Rules
Tech Stack
[Stack list]
Top 10 Principles
- TypeScript strict mode
- Result for error handling
- Functional React components
- TanStack Query for server state
- Zod for validation
- 80% test coverage
- Conventional Commits
- pnpm (not npm/yarn)
- No console.log in production
- Environment variables for config
**After 2 Weeks: Add Top 5 Patterns**
markdown
Common Patterns
1. Error Handling
[Tier 2 example from above]
2. Component Structure
[Example]
3. API Calls
[Example]
4. Testing
[Example]
5. State Management
[Example]
**After 1 Month: Add Critical Edge Cases**
markdown
Critical Gotchas
1. Server Component Hydration
[Tier 3 detailed explanation]
2. tRPC Context Management
[Tier 3 detailed explanation]
3. Database Transaction Deadlocks
[Tier 3 detailed explanation]
---
#### **6.1.2 The "Example-Driven" Approach**
**Research Finding**: Rules with code examples have **3.5x higher adherence** than prose-only rules.
**The Pattern: Always Pair Bad → Good**
markdown
Rule Name
❌ WRONG (What NOT to Do)
// Bad code example
// Explain why it's bad
Problems:
- [Specific issue 1]
- [Specific issue 2]
✅ CORRECT (Recommended Approach)
// Good code example
// Explain why it's better
Benefits:
- [Specific benefit 1]
- [Specific benefit 2]
**Complete Example:**
markdown
State Management in React
❌ WRONG: Prop Drilling
// App.tsx
function App() {
const [user, setUser] = useState<User | null>(null);
return <Dashboard user={user} setUser={setUser} />;
}
// Dashboard.tsx
function Dashboard({ user, setUser }: Props) {
return <Sidebar user={user} setUser={setUser} />;
}
// Sidebar.tsx
function Sidebar({ user, setUser }: Props) {
return <UserProfile user={user} setUser={setUser} />;
}
// UserProfile.tsx (finally uses it)
function UserProfile({ user, setUser }: Props) {
return <div>{user?.name}</div>;
}
Problems:
- Props passed through 3 intermediate components that don't use them
- Adding new user-related props requires updating 4 components
- Difficult to refactor (changing prop names breaks 4 files)
- TypeScript boilerplate for each Props interface
✅ CORRECT: Zustand Global State
// stores/userStore.ts
import { create } from 'zustand';
interface UserStore {
user: User | null;
setUser: (user: User | null) => void;
}
export const useUserStore = create<UserStore>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
// App.tsx
function App() {
return <Dashboard />; // No props!
}
// Dashboard.tsx
function Dashboard() {
return <Sidebar />; // No props!
}
// Sidebar.tsx
function Sidebar() {
return <UserProfile />; // No props!
}
// UserProfile.tsx (consumes directly)
function UserProfile() {
const user = useUserStore((state) => state.user);
const setUser = useUserStore((state) => state.setUser);
return <div>{user?.name}</div>;
}
Benefits:
- Components only import what they need
- No intermediate prop passing
- Easy to add new user-related state
- Single source of truth
- TypeScript inference works automatically
**Example Types by Category:**
| Category | Example Type | When to Use |
|----------|-------------|-------------|
| **Anti-Pattern** | ❌ Bad → ✅ Good | Preventing common mistakes |
| **Evolution** | Before → After | Showing refactoring improvements |
| **Comparison** | Option A vs Option B | Choosing between approaches |
| **Progression** | Basic → Intermediate → Advanced | Teaching complexity levels |
| **Context-Specific** | Server Component vs Client Component | Different contexts need different patterns |
---
#### **6.1.3 Structured Organization Patterns**
**Pattern 1: XML-Style Semantic Tags**
markdown
Core Information
- Stack, architecture, deployment
Style Rules
- TypeScript, React, testing patterns
Reusable Solutions
- Error handling, state management, API calls
Known Issues
- Specific bugs, workarounds, edge cases
Development Process
- Git, testing, deployment procedures
**Benefits:**
- Clear semantic boundaries
- Easy to scan (both human and AI)
- Sections independently updatable
- Natural hierarchy without deep nesting
**Pattern 2: Numbered List Hierarchy**
markdown
1. PROJECT OVERVIEW
Brief context
2. TECHNOLOGY STACK
Detailed stack with versions
3. ARCHITECTURE
Structure and patterns
4. CODING STANDARDS
4.1 TypeScript Rules
4.1.1 Type Annotations
4.1.2 Error Handling
4.2 React Patterns
4.2.1 Component Structure
4.2.2 State Management
5. TESTING REQUIREMENTS
Coverage and patterns
6. COMMON COMMANDS
Development workflow
7. KNOWN ISSUES
Troubleshooting guide
**Benefits:**
- Clear progression (1, 2, 3...)
- Easy references ("See section 4.1.2")
- Scales to complex documentation
- Familiar structure (like technical specs)
**Pattern 3: Table-Driven Rules**
markdown
Component Patterns
| Pattern | Use Case | Example | Avoid When |
|---|---|---|---|
| Server Component | Static data, SEO-critical | async function Page() |
Needs interactivity |
| Client Component | Interactive UI, hooks | 'use client'; useState() |
Pure presentation |
| Server Action | Form submissions | async function submitForm() |
Client-only operations |
| Route Handler | REST API endpoints | export async function POST() |
Type-safe APIs (use tRPC) |
State Management Decision Matrix
| Data Type | Persistence | Scope | Solution | Example |
|---|---|---|---|---|
| Server data | API | Component tree | TanStack Query | useQuery('users') |
| Form state | Component | Single form | React Hook Form | useForm() |
| UI state | Component | Single component | useState | const [open, setOpen] |
| Global client | Memory | App-wide | Zustand | useUserStore() |
| URL state | URL params | Route | useSearchParams | const [search] |
**Benefits:**
- Dense information packaging
- Easy scanning for relevant row
- Forces consistent categorization
- Natural for decision-making scenarios
---
#### **6.1.4 Token Efficiency Techniques**
**Technique 1: Bullet Points Over Prose**
markdown
❌ LOW EFFICIENCY (150 tokens):
"When you are writing TypeScript code, it is very important that you always include explicit type annotations for the return types of your functions. This helps with maintaining type safety throughout the codebase and makes it easier for other developers to understand what your functions are doing without having to read through all the implementation details."
✅ HIGH EFFICIENCY (25 tokens):
TypeScript Rules:
- Explicit return types required on all functions
- Improves type safety and code readability
- Example:
function calc(x: number): number { return x * 2; }
**Technique 2: Code Examples > Explanations**
markdown
❌ VERBOSE (200 tokens):
"For error handling in asynchronous operations, you should use a pattern where you wrap the return value in an object that has a success boolean property to indicate whether the operation succeeded or failed. If it succeeded, include the data in a data property. If it failed, include an error message in an error property. This makes it easy to check if an operation succeeded and handle errors appropriately without using try-catch blocks everywhere."
✅ CONCISE (75 tokens):
Error Handling:
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
// Usage
const result = await fetchUser(id);
if (!result.success) {
return handleError(result.error);
}
const user = result.data;
**Technique 3: Tables for Mappings**
markdown
❌ PROSE (180 tokens):
"When you need to fetch server data, use TanStack Query. When you're managing form state, use React Hook Form. For simple component-level state like whether a modal is open, use useState. If you need global client state that persists across the app, use Zustand. When state needs to be in the URL, use Next.js useSearchParams."
✅ TABLE (60 tokens):
| Need | Solution |
|------|----------|
| Server data | TanStack Query |
| Form state | React Hook Form |
| Local state | useState |
| Global state | Zustand |
| URL state | useSearchParams |
**Technique 4: Eliminate Filler Words**
markdown
❌ WORDY:
"I would really appreciate it if you could make sure to always validate user inputs"
✅ DIRECT:
"ALWAYS validate user inputs"
❌ WORDY:
"It's generally considered a good practice to prefer composition over inheritance"
✅ DIRECT:
"Prefer composition over inheritance"
❌ WORDY:
"When you're working on this particular codebase, please try to remember that"
✅ DIRECT:
"Remember:"
**Token Savings Comparison:**
| Original Length | Optimized Length | Savings |
|----------------|------------------|---------|
| 1000 tokens (prose-heavy) | 350 tokens (optimized) | 65% |
| 500 tokens (moderate) | 250 tokens (optimized) | 50% |
| 300 tokens (concise) | 200 tokens (optimized) | 33% |
---
### **6.2 Testing and Validation**
#### **6.2.1 The "Gold Standard" Test**
**Method: Delete and Regenerate**
**Purpose**: Verify AI can recreate existing code following your rules.
**Process**:
1. **Select target**: Choose a representative function/component
2. **Document expected behavior**: Note what it should do
3. **Delete the code**: Completely remove implementation
4. **Prompt AI**: "Implement [function name] based on project rules"
5. **Compare**: Diff AI output against original
6. **Score**: Calculate similarity percentage
**Scoring Rubric**:
| Aspect | Weight | Score Calculation |
|--------|--------|-------------------|
| **Functionality** | 40% | Does it work correctly? (0-10) |
| **Style Match** | 30% | Follows coding standards? (0-10) |
| **Patterns** | 20% | Uses correct patterns (error handling, state, etc.)? (0-10) |
| **Structure** | 10% | File organization, imports, naming? (0-10) |
**Total Score** = (Functionality × 0.4) + (Style × 0.3) + (Patterns × 0.2) + (Structure × 0.1)
**Interpretation**:
- **90-100%**: Excellent rules—AI perfectly replicates codebase style
- **70-89%**: Good rules—minor adjustments needed
- **50-69%**: Adequate rules—significant gaps exist
- **< 50%**: Poor rules—major revision required
**Example Test**:
typescript
// Original code (before deletion)
async function fetchUserProfile(userId: string): Promise> {
try {
const profile = await db.query.userProfiles.findFirst({
where: eq(userProfiles.userId, userId),
with: {
user: true,
posts: {
limit: 10,
orderBy: desc(posts.createdAt)
}
}
});
if (!profile) {
return { success: false, error: 'Profile not found' };
}
return { success: true, data: profile };
} catch (error) {
logger.error('fetchUserProfile failed', { userId, error });
return { success: false, error: 'Failed to fetch profile' };
}
}
// Delete code, prompt AI:
// "Implement fetchUserProfile function that:
// - Takes userId (string)
// - Fetches user profile with related user and latest 10 posts
// - Returns Result type
// - Handles not found and errors appropriately"
// AI generates:
async function fetchUserProfile(userId: string): Promise> {
try {
const profile = await db.query.userProfiles.findFirst({
where: eq(userProfiles.userId, userId),
with: {
user: true,
posts: {
limit: 10,
orderBy: desc(posts.createdAt)
}
}
});
if (!profile) {
return { success: false, error: 'Profile not found' };
}
return { success: true, data: profile };
} catch (error) {
logger.error('fetchUserProfile failed', { userId, error });
return { success: false, error: 'Failed to fetch profile' };
}
}
// Result: 100% match! Rules are working perfectly.
---
#### **6.2.2 Violation Prompt Technique**
**Method: Self-Review**
Ask AI to critique your rules for contradictions and ambiguities.
**Prompt Template**:
Review the AI coding rules in [FILENAME].
Identify:
- Contradictions (rules that conflict with each other)
- Ambiguities (vague rules that could be interpreted multiple ways)
- Missing critical information (patterns not covered)
- Overly prescriptive rules (too specific, limiting flexibility)
- Outdated information (deprecated libraries/patterns)
For each issue, provide:
- Location (section/line)
- Description of problem
- Suggested fix
- Priority (High/Medium/Low)
**Example AI Response**:
markdown
Contradictions
Issue 1: State Management Conflict (HIGH PRIORITY)
Location: Section 4.2.2 "State Management" and Section 7.3 "Known Issues"
Problem:
- Section 4.2.2 says: "Use Context API for global state"
- Section 7.3 says: "Context API causes performance issues, use Zustand"
Fix: Remove Context API recommendation from 4.2.2, consolidate on Zustand.
Issue 2: Component Size Limits (MEDIUM PRIORITY)
Location: Section 4.1 "Component Rules"
Problem:
- One rule says: "Maximum 200 lines per component"
- Another says: "Keep components under 150 lines"
Fix: Pick one limit (recommend 200 lines) and remove the other.
Ambiguities
Issue 3: "Modern JavaScript" Undefined (HIGH PRIORITY)
Location: Section 3.1 "Language Standards"
Problem: Rule states "Use modern JavaScript" without defining what that means.
Fix: Be specific:
## Modern JavaScript (ES2022+)
- ✅ Use: async/await, optional chaining `?.`, nullish coalescing `??`
- ❌ Avoid: `.then()` chains, `|| ` for defaults, var declarations
Issue 4: Testing "Should" vs "Must" (MEDIUM PRIORITY)
Location: Section 5 "Testing Requirements"
Problem: Uses weak language "should write tests" instead of requirements.
Fix: Use clear directives:
- "REQUIRED: All business logic functions must have unit tests"
- "REQUIRED: All API endpoints must have integration tests"
- "OPTIONAL: E2E tests for critical flows"
Missing Information
Issue 5: No Database Migration Strategy (HIGH PRIORITY)
Location: Missing from entire document
Problem: No guidance on creating or managing database migrations.
Fix: Add section:
## Database Migrations
- Use Drizzle Kit for schema changes: `pnpm db:generate`
- Never edit migration files manually
- Test migrations on staging before production
- Always include rollback strategy
Issue 6: No Environment Setup Instructions (MEDIUM PRIORITY)
Location: Missing
Fix: Add section on .env.local setup, required variables, validation.
Overly Prescriptive
Issue 7: Hardcoded File Locations (LOW PRIORITY)
Location: Section 4.3 "File Structure"
Problem: Rules specify exact file names: "Put API utils in /lib/api/utils.ts"
Fix: Use patterns instead: "Organize API utilities in /lib/api/ directory"
Outdated Information
Issue 8: Next.js Pages Router References (HIGH PRIORITY)
Location: Section 2 "Tech Stack" and Section 4 "Routing"
Problem: Mentions "pages directory" but project uses App Router.
Fix: Remove all Pages Router references, update to App Router patterns.
---
#### **6.2.3 Effectiveness Metrics**
**Metric 1: AI Adherence Rate**
**Definition**: Percentage of AI-generated code that follows project rules without modification.
**Measurement**:
1. Over 1 week, track all AI code generations (20-50 samples)
2. For each, manually review: Does it follow rules? (Yes/Partial/No)
3. Calculate: `(Yes × 1 + Partial × 0.5 + No × 0) / Total`
**Target**: >90% adherence
**Example Tracking Sheet**:
| Date | Task | Tool | Adherence | Notes |
|------|------|------|-----------|-------|
| 11-25 | Add API endpoint | Cursor | Yes (100%) | Perfect Result<T> usage |
| 11-25 | Create component | Windsurf | Partial (50%) | Used functional but no types |
| 11-26 | Fix bug | Aider | Yes (100%) | Correct error handling |
| 11-26 | Write tests | Cline | No (0%) | Used Jest instead of Vitest |
**Analysis**: 62.5% adherence—rules need clarification on testing framework.
---
**Metric 2: Code Review Iteration Count**
**Definition**: Average number of review cycles needed for AI-generated code.
**Measurement**:
1. Track PRs containing AI-generated code
2. Count review cycles (requested changes) before merge
3. Compare to baseline (non-AI code)
**Calculation**:
AI Code Review Cycles = Total review cycles / Number of AI-assisted PRs
Baseline Cycles = Total review cycles / Number of manual PRs
Efficiency = (Baseline - AI) / Baseline × 100%
**Example**:
- **Before rules**: AI code averaged 3.5 review cycles (vs 2.1 baseline)
- **After rules**: AI code averaged 1.8 review cycles (vs 2.1 baseline)
- **Improvement**: AI code now 14% better than manual!
---
**Metric 3: Standards Compliance Score**
**Definition**: Automated check of code against project standards.
**Tools**:
- **Linters**: ESLint, Ruff, etc.
- **Type checkers**: TypeScript, mypy
- **Formatters**: Prettier, Black
- **Custom scripts**: Regex checks for patterns
**Measurement**:
bash
!/bin/bash
compliance-check.sh
Run linter
eslint_errors=$(pnpm lint --format json | jq '.[] | .errorCount' | awk '{s+=$1} END {print s}')
Run type checker
tsc_errors=$(pnpm type-check 2>&1 | grep -c "error TS")
Check for anti-patterns
console_logs=$(grep -r "console.log" src/ --exclude-dir=node_modules | wc -l)
any_types=$(grep -r ": any" src/ --exclude-dir=node_modules | wc -l)
Calculate score (lower is better)
total_violations=$((eslint_errors + tsc_errors + console_logs + any_types))
echo "Compliance Violations: $total_violations"
if [ $total_violations -eq 0 ]; then
echo "✅ 100% compliant"
exit 0
elif [ $total_violations -lt 10 ]; then
echo "⚠️ Mostly compliant"
exit 0
else
echo "❌ Poor compliance"
exit 1
fi
**Target**: 0 violations (100% compliant)
---
**Metric 4: Onboarding Time**
**Definition**: Time for new developer to become productive with AI assistant.
**Measurement**:
1. **Without rules**: Track time to first merged PR
2. **With rules**: Track time to first merged PR
3. **Calculate reduction**
**Example**:
- **Before rules**: Average 2.5 weeks to first merge
- **After rules**: Average 4 days to first merge
- **Improvement**: 84% faster onboarding!
---
**Metric 5: Bug Rate in AI-Generated Code**
**Definition**: Bugs per 1000 lines of AI-generated code.
**Measurement**:
1. Tag commits with AI involvement (`Co-authored-by: AI`)
2. Track bugs linked to those commits
3. Calculate: `Bugs / (Lines of AI code / 1000)`
**Target**: Equal to or better than manually written code
---
#### **6.2.4 Automated Validation Scripts**
**Script 1: Rules Freshness Check**
bash
!/bin/bash
check-rules-freshness.sh
Check if rules have been updated recently
RULES_FILE="AGENTS.md"
DAYS_THRESHOLD=90 # Flag if not updated in 90 days
last_modified=$(git log -1 --format=%ct $RULES_FILE)
now=$(date +%s)
days_old=$(( ($now - $last_modified) / 86400 ))
if [ $days_old -gt $DAYS_THRESHOLD ]; then
echo "⚠️ $RULES_FILE is $days_old days old (>$DAYS_THRESHOLD)"
echo "Consider reviewing for outdated information"
exit 1
else
echo "✅ $RULES_FILE is current ($days_old days old)"
exit 0
fi
---
**Script 2: Cross-Tool Sync Validation**
bash
!/bin/bash
check-rules-sync.sh
Verify all tool-specific rule files are in sync with AGENTS.md
MASTER="AGENTS.md"
TOOLS=(.rules .cursorrules .windsurfrules .clinerules CLAUDE.md)
for tool in "${TOOLS[@]}"; do
if [ -L "$tool" ]; then
# It's a symlink - verify target
target=$(readlink "$tool")
if [ "$target" = "$MASTER" ]; then
echo "✅ $tool → $MASTER (symlink OK)"
else
echo "❌ $tool → $target (should point to $MASTER)"
exit 1
fi
elif [ -f "$tool" ]; then
# It's a file - check if identical
if diff -q "$tool" "$MASTER" > /dev/null; then
echo "✅ $tool matches $MASTER (content identical)"
else
echo "⚠️ $tool differs from $MASTER (may need sync)"
exit 1
fi
else
echo "ℹ️ $tool not found (optional)"
fi
done
echo "✅ All tool configurations synced"
---
**Script 3: Forbidden Pattern Detector**
bash
!/bin/bash
detect-anti-patterns.sh
Check AI-generated code for anti-patterns
VIOLATIONS=0
Check for console.log in production code
console_logs=$(grep -r "console\.log" src/ --exclude-dir=node_modules | wc -l)
if [ $console_logs -gt 0 ]; then
echo "❌ Found $console_logs console.log statements"
grep -r "console.log" src/ --exclude-dir=node_modules
VIOLATIONS=$((VIOLATIONS + console_logs))
fi
Check for 'any' types
any_types=$(grep -r ": any" src/ --exclude-dir=node_modules --include=".ts" --include=".tsx" | wc -l)
if [ $any_types -gt 0 ]; then
echo "❌ Found $any_types uses of 'any' type"
grep -r ": any" src/ --exclude-dir=node_modules --include=".ts" --include=".tsx"
VIOLATIONS=$((VIOLATIONS + any_types))
fi
Check for .then() chains (prefer async/await)
then_chains=$(grep -r ".then(" src/ --exclude-dir=node_modules --include=".ts" --include=".tsx" | wc -l)
if [ $then_chains -gt 0 ]; then
echo "⚠️ Found $then_chains .then() chains (prefer async/await)"
VIOLATIONS=$((VIOLATIONS + then_chains))
fi
Check for class components (prefer functional)
class_components=$(grep -r "extends React.Component" src/ --exclude-dir=node_modules | wc -l)
if [ $class_components -gt 0 ]; then
echo "❌ Found $class_components class components (use functional)"
VIOLATIONS=$((VIOLATIONS + class_components))
fi
echo ""
if [ $VIOLATIONS -eq 0 ]; then
echo "✅ No anti-patterns detected"
exit 0
else
echo "❌ Total violations: $VIOLATIONS"
exit 1
fi
---
**Script 4: CI Integration**
yaml
.github/workflows/ai-rules-check.yml
name: AI Rules Validation
on:
pull_request:
paths:
- 'AGENTS.md'
- '.cursor/rules/'
- '.continue/rules/'
- 'src/**'
jobs:
validate-rules:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Full history for git log checks
- name: Check rules freshness
run: ./scripts/check-rules-freshness.sh
- name: Check cross-tool sync
run: ./scripts/check-rules-sync.sh
- name: Detect anti-patterns
run: ./scripts/detect-anti-patterns.sh
- name: Check for secrets in rules
run: |
if grep -rE '(api[_-]?key|secret|password|token).*=.*[a-zA-Z0-9]{20,}' AGENTS.md; then
echo "❌ Secrets detected in AGENTS.md"
exit 1
fi
- name: Validate Markdown syntax
run: |
npm install -g markdownlint-cli
markdownlint AGENTS.md .cursor/rules/ .continue/rules/
---
### **6.3 Maintenance Lifecycle**
#### **6.3.1 Update Frequency Guidelines**
**Update Triggers**:
| Trigger | Frequency | Scope | Example |
|---------|-----------|-------|---------|
| **Critical Bug** | Immediate | Single rule fix | AI repeatedly generates insecure code |
| **Stack Upgrade** | Within 1 week | Tech stack section | Next.js 14 → 15, new patterns |
| **Pattern Discovery** | Weekly | Add 1-2 rules | Team discovers better error handling |
| **Major Refactor** | After completion | Architecture section | Migrating to microservices |
| **Quarterly Review** | Every 3 months | Full document | Remove outdated, consolidate duplicates |
**Update Schedule Template**:
markdown
AGENTS.md Maintenance Schedule
Weekly (Fridays, 30 minutes)
- [ ] Review AI-generated code from this week
- [ ] Identify patterns AI got wrong
- [ ] Add 1-2 clarifying rules
- [ ] Test with "Gold Standard" regeneration
Monthly (Last Friday, 2 hours)
- [ ] Review all rules for relevance
- [ ] Remove deprecated patterns
- [ ] Update library versions
- [ ] Check for contradictions (violation prompt)
- [ ] Update examples with latest syntax
Quarterly (End of Q1/Q2/Q3/Q4, 4 hours)
- [ ] Full document review with team
- [ ] Major version bump (1.0 → 2.0)
- [ ] Reorganize structure if needed
- [ ] Archive old versions
- [ ] Run effectiveness metrics analysis
- [ ] Team retrospective: "What's working? What's not?"
On-Demand (As needed)
- [ ] Stack upgrade (framework, language, major library)
- [ ] Architecture change
- [ ] Critical bug in AI generations
- [ ] New team member feedback
---
#### **6.3.2 Version Control for Rules**
**Semantic Versioning**:
MAJOR.MINOR.PATCH
Example: 2.3.1
MAJOR (2): Breaking changes (e.g., complete tech stack change)
MINOR (3): New rules added (e.g., new pattern introduced)
PATCH (1): Clarifications/fixes (e.g., typo, example correction)
**Version Header in AGENTS.md**:
markdown
AGENTS.md
Version: 2.3.1
Last Updated: 2025-11-29
Breaking Changes Since 2.0: None
Compatible With: Cursor, Windsurf, Claude Code, Aider, Continue.dev, Cline, Zed
Changelog
2.3.1 (2025-11-29)
Changed:
- Clarified Server Component usage in Section 4.2
- Added example for hybrid hydration pattern
- Fixed typo in error handling section
2.3.0 (2025-11-15)
Added:
- New section on database transaction patterns
- Testing strategies for tRPC procedures
- Deployment checklist
2.2.0 (2025-10-30)
Changed:
- Migrated from Redux to Zustand (MAJOR pattern change)
- Updated all state management examples
- Removed Redux-specific rules
Deprecated:
- Redux patterns (see migration guide in docs/)
2.1.0 (2025-09-20)
Added:
- React 19 patterns
- Server Actions guidelines
2.0.0 (2025-08-01) - BREAKING
Changed:
- Migrated from Pages Router to App Router
- Complete Next.js section rewrite
- File structure patterns updated
Migration Guide: See docs/migration-2.0.md
---
**Git Tagging Strategy**:
bash
Tag major releases
git tag -a v2.0.0 -m "AGENTS.md v2.0.0: Next.js App Router migration"
git push origin v2.0.0
View version history
git tag -l "v*" --format="%(refname:short) - %(contents:subject)"
Compare versions
git diff v2.0.0 v2.3.1 -- AGENTS.md
Restore previous version if needed
git show v2.0.0:AGENTS.md > AGENTS.md.backup
---
#### **6.3.3 Deprecation Strategies**
**Pattern: Dual-Track Migration**
markdown
State Management
Current Standard (Use This)
Use Zustand for global client state:
import { create } from 'zustand';
export const useUserStore = create<UserStore>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
Deprecated (Migrate Away)
⚠️ DEPRECATED as of v2.2.0 (2025-10-30)
Redux is being phased out. See migration guide: docs/redux-to-zustand.md
For existing Redux code:
- ✅ Do: Maintain existing files
- ❌ Don't: Create new Redux slices
- 🔄 Migrate: When touching Redux code, consider converting to Zustand
Timeline:
- Now: Zustand for all new code
- Q1 2026: Begin systematic Redux removal
- Q2 2026: Complete migration (Redux fully removed)
---
**Pattern: Warning Labels**
markdown
Component Patterns
⚠️ LEGACY: Class Components
// This pattern still exists in codebase but is deprecated
class UserProfile extends React.Component {
render() {
return <div>{this.props.user.name}</div>;
}
}
Status: Deprecated since v2.0.0 (2025-08-01)
Action: Convert to functional components when touched
Remaining: ~15 class components (tracked in issue #234)
✅ CURRENT: Functional Components
// Use this pattern for all new code
function UserProfile({ user }: Props) {
return <div>{user.name}</div>;
}
---
**Pattern: Migration Notices**
markdown
Breaking Changes in v2.0.0
Next.js App Router Migration
What Changed:
-
pages/directory →app/directory -
getServerSideProps→ Server Components -
_app.tsx→layout.tsx
Migration Examples:
Before (Pages Router)
// pages/users/[id].tsx
export async function getServerSideProps({ params }) {
const user = await fetchUser(params.id);
return { props: { user } };
}
export default function UserPage({ user }) {
return <div>{user.name}</div>;
}
After (App Router)
// app/users/[id]/page.tsx
export default async function UserPage({ params }) {
const user = await fetchUser(params.id); // Direct fetch
return <div>{user.name}</div>;
}
Full Migration Guide: docs/app-router-migration.md
---
#### **6.3.4 Context Engineering Refinement**
**Evolution Process**:
**Phase 1: Weak Rule (Initial)**
markdown
Follow good error handling practices.
**Result**: AI produces inconsistent approaches.
---
**Phase 2: Explicit Directive (Iteration 1)**
markdown
Always handle errors in async functions.
**Result**: AI adds try-catch but error handling varies.
---
**Phase 3: Pattern Specification (Iteration 2)**
markdown
Use Result type for error handling:
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
Result: AI uses Result but logger not always called.
Phase 4: Complete Template (Final)
## Error Handling Pattern (REQUIRED)
ALL async operations MUST follow this exact pattern:
typescript
async function operationName(params): Promise> {
try {
// 1. Perform operation
const result = await performOperation(params);
// 2. Validate result
if (!result) {
return { success: false, error: 'Human-readable error message' };
}
// 3. Return success
return { success: true, data: result };
} catch (error) {
// 4. ALWAYS log with context
logger.error('operationName failed', { params, error });
// 5. Return user-friendly error
return { success: false, error: 'Operation failed' };
}
}
**Checklist for every async function**:
- [ ] Returns `Promise<Result<T>>`
- [ ] Has try-catch block
- [ ] Logs errors with context
- [ ] Returns user-friendly error messages
- [ ] Validates result before returning
Result: 98% adherence achieved.
Strengthening Techniques:
1. Use XML Tags for Emphasis
<CRITICAL>
NEVER commit secrets or API keys to git.
ALWAYS use environment variables.
</CRITICAL>
2. Use CAPS for Requirements
REQUIRED: All public functions MUST have JSDoc comments.
FORBIDDEN: Using `any` type without explicit justification comment.
3. Add Verification Steps
## Before Committing
Verify your changes:
1. Run `pnpm lint` (must pass with 0 warnings)
2. Run `pnpm type-check` (must pass with 0 errors)
3. Run `pnpm test` (all tests must pass)
4. Check for console.log statements (must be removed)
5. Verify no secrets in diff (must use .env)
If ANY check fails, DO NOT commit.
6.4 Advanced Patterns
6.4.1 Modularization for Scale
Pattern: Core + Domain Split
my-project/
├── AI_RULES_CORE.md # Universal standards (500 lines)
├── AI_RULES_FRONTEND.md # React/Next.js specific (300 lines)
├── AI_RULES_BACKEND.md # API/Database specific (300 lines)
├── AI_RULES_TESTING.md # Testing strategies (200 lines)
└── AGENTS.md # Master file (imports others)
AGENTS.md (Master File):
# AGENTS.md - Project AI Rules
**Version**: 3.0.0
**Last Updated**: 2025-11-29
---
## Quick Start
This file references modular rule documents:
- **Core Standards** (all developers): See AI_RULES_CORE.md
- **Frontend Development** (React/Next.js): See AI_RULES_FRONTEND.md
- **Backend Development** (API/Database): See AI_RULES_BACKEND.md
- **Testing** (QA/TDD): See AI_RULES_TESTING.md
---
## Critical Rules (Always Apply)
1. **Never commit secrets** (use environment variables)
2. **Result<T> for error handling** (see Core)
3. **80% test coverage minimum** (see Testing)
4. **TypeScript strict mode** (see Core)
5. **Functional React components** (see Frontend)
---
## How to Use
### For AI Assistants
**Cursor**: Create symlinks in `.cursor/rules/`
bash
ln -s ../../AI_RULES_CORE.md .cursor/rules/01-core.mdc
ln -s ../../AI_RULES_FRONTEND.md .cursor/rules/02-frontend.mdc
**Aider**: Load multiple files
yaml
.aider.conf.yml
read:
- AI_RULES_CORE.md
- AI_RULES_FRONTEND.md
- AI_RULES_BACKEND.md
- AI_RULES_TESTING.md
**Continue.dev**: Reference in rules
markdown
See:
- @AI_RULES_CORE.md
- @AI_RULES_FRONTEND.md
- @AI_RULES_BACKEND.md
---
## Full Documentation
For complete rules, read all referenced files above.
Benefits:
- ✅ Specialists load only relevant rules (lower token usage)
- ✅ Easier to maintain (change frontend rules without touching backend)
- ✅ Clear ownership (frontend team owns AI_RULES_FRONTEND.md)
- ✅ Faster updates (smaller files = quicker edits)
6.4.2 Multi-Agent Coordination
Pattern: Agent-Specific Instructions
Scenario: Team uses multiple AI agents for different tasks.
AGENTS.md with Role-Based Sections:
# Multi-Agent Coordination
## Role: Architect Agent
**Use When**: Planning new features, designing system architecture, making technology decisions.
**Responsibilities**:
- Create architecture decision records (ADRs)
- Generate system design documents
- Propose 2-3 solution options with trade-offs
- Create Mermaid diagrams
**Constraints**:
- DO NOT implement code (only design)
- DO NOT make unilateral decisions (present options)
- MUST document in `docs/adr/`
**Output Format**:
markdown
ADR-XXX: [Decision Title]
Status
Proposed
Context
[Problem being solved]
Options
Option 1: [Approach]
Pros: ...
Cons: ...
Option 2: [Approach]
Pros: ...
Cons: ...
Decision
[Chosen option with justification]
Consequences
[Implications of decision]
---
## Role: Implementation Agent
**Use When**: Writing production code, refactoring, fixing bugs.
**Responsibilities**:
- Implement features following ADRs
- Write tests alongside code (TDD)
- Handle errors with Result<T> pattern
- Create typed, documented code
**Constraints**:
- MUST follow architecture from ADRs
- MUST write tests before marking complete
- MUST run linter before finishing
- CAN NOT change architecture (escalate to Architect)
**Verification Checklist**:
- [ ] Code follows TypeScript strict mode
- [ ] All functions have explicit return types
- [ ] Tests written and passing
- [ ] Error handling with Result<T>
- [ ] No console.log statements
- [ ] Linter passes with 0 warnings
---
## Role: Testing Agent
**Use When**: Writing comprehensive test suites, improving test coverage.
**Responsibilities**:
- Write unit, integration, and E2E tests
- Achieve 80%+ coverage
- Use Vitest for unit, Playwright for E2E
- Follow AAA pattern (Arrange, Act, Assert)
**Constraints**:
- DO NOT modify production code (only tests)
- MUST test happy path + error cases + edge cases
- MUST mock external dependencies
- CAN NOT skip tests "to save time"
**Test Template**:
typescript
describe('FeatureName', () => {
describe('Happy Path', () => {
it('should succeed with valid input', async () => {
// Arrange
const input = createValidInput();
// Act
const result = await operation(input);
// Assert
expect(result.success).toBe(true);
expect(result.data).toMatchObject(expected);
});
});
describe('Error Cases', () => {
it('should handle invalid input', async () => {
const result = await operation(invalidInput);
expect(result.success).toBe(false);
expect(result.error).toContain('validation');
});
});
describe('Edge Cases', () => {
it('should handle empty input', async () => {
const result = await operation([]);
expect(result.success).toBe(true);
expect(result.data).toEqual([]);
});
});
});
---
## Role: Code Reviewer
**Use When**: Reviewing PRs, checking for issues, suggesting improvements.
**Responsibilities**:
- Review code for standards compliance
- Check for security vulnerabilities
- Verify test coverage
- Suggest performance improvements
**Constraints**:
- DO NOT implement fixes (only suggest)
- MUST explain reasoning for suggestions
- MUST prioritize suggestions (Critical/High/Medium/Low)
- CAN NOT approve without verification
**Review Checklist**:
markdown
Security
- [ ] No hardcoded secrets
- [ ] Input validation present
- [ ] SQL injection prevention (parameterized queries)
- [ ] XSS prevention (React auto-escapes)
Code Quality
- [ ] TypeScript strict mode compliant
- [ ] Explicit return types
- [ ] Error handling with Result
- [ ] No code duplication (DRY)
Testing
- [ ] Tests present and passing
- [ ] Coverage >80%
- [ ] Edge cases covered
Performance
- [ ] No unnecessary re-renders (React)
- [ ] Efficient algorithms (no O(n²) unless necessary)
- [ ] Database queries optimized (no N+1)
Usage:
# Architect session
ai-agent --role architect "Design authentication system"
# Implementation session
ai-agent --role implementation "Implement user registration following ADR-005"
# Testing session
ai-agent --role testing "Write comprehensive tests for authentication"
# Review session
ai-agent --role reviewer "Review PR #42 for security and standards"
6.4.3 Dynamic Rules Systems
Pattern: File-Trigger-Based Context Loading
Advanced Roo-Cline Example:
# .roomodes
customModes:
# Auto-switch based on file type
- slug: react-components
name: React Component Specialist
roleDefinition: Expert in React 19 component development
groups:
- read
- - edit
- fileRegex: components/.*\.(tsx|jsx)$
customInstructions: |-
## Component-Specific Rules
[Detailed React rules]
- slug: database-operations
name: Database Specialist
roleDefinition: Expert in Drizzle ORM and PostgreSQL
groups:
- read
- - edit
- fileRegex: (lib/db/|.*\.schema\.ts)
customInstructions: |-
## Database Rules
[Detailed database rules]
- slug: api-endpoints
name: API Developer
roleDefinition: Expert in tRPC procedures and API design
groups:
- read
- - edit
- fileRegex: lib/api/.*\.ts
customInstructions: |-
## API Rules
[Detailed API rules]
Benefit: AI automatically gets context-appropriate rules based on which files you're editing.
Pattern: Real-Time Rule Updates
Conceptual (Future Feature):
# AGENTS.md with Update Triggers
## Real-Time Update System
### Pattern Discovery Trigger
When you discover a new pattern:
1. Press `#` key (Claude Code)
2. Or use `/add-rule` command
3. AI proposes rule addition
4. You approve/edit
5. AGENTS.md updated automatically
### Error Pattern Trigger
When AI makes the same mistake 3 times:
1. System detects pattern
2. Proposes clarifying rule
3. You approve
4. Rule added with examples
### Contradiction Detection Trigger
When new rule conflicts with existing:
1. System highlights conflict
2. Proposes resolution
3. You choose approach
4. Conflicting rules reconciled
This comprehensive Best Practices & Optimization section provides everything needed to write effective rules, test their effectiveness, maintain them over time, and implement advanced patterns for scaling to complex multi-agent, multi-tool environments.
The guide is now complete with actionable strategies backed by real-world examples and measurable metrics.
Top comments (0)