Cline rules are Markdown-based instruction files that guide Cline's AI assistant to follow your project's coding standards, architectural patterns, and development workflows. Unlike other AI coding tools that use YAML or JSON schemas, Cline embraces simplicity with plain Markdown.
Why Rules Matter
Without rules, Cline generates generic code based on its training data:
- Inconsistent coding styles
- Wrong framework versions
- Outdated patterns
- Generic error handling
With rules, Cline becomes project-aware:
- Follows your tech stack conventions
- Uses your preferred patterns
- Matches your codebase style
- Avoids known pitfalls
Core Philosophy
Cline's approach:
- ✅ Human-readable: Write rules in plain Markdown
- ✅ No schema: No YAML frontmatter or JSON validation
- ✅ Git-friendly: Easy to version control and review
- ✅ Progressive: Start simple, add complexity as needed
2. File Locations & Hierarchy
Project-Level Rules
Cline checks the project root for rules in this order:
Option 1: Single File (Simpler)
my-project/
├── .clinerules ← Cline looks here first
├── src/
└── package.json
Option 2: Directory (Recommended for complex projects)
my-project/
├── .clinerules/ ← Cline merges all .md files here
│ ├── 01-core.md
│ ├── 02-frontend.md
│ └── 03-backend.md
├── src/
└── package.json
Automatic Loading Behavior
| File/Directory | Loading Method | Priority |
|---|---|---|
.clinerules (file) |
Entire file loaded | Exclusive (if exists, directory ignored) |
.clinerules/ (directory) |
All .md files merged alphabetically |
Inclusive (all files combined) |
Important: If both exist, Cline uses only the file and ignores the directory.
Fallback Compatibility
If no .clinerules exists, Cline checks for:
AGENTS.md ← Universal standard (priority 1)
.cursorrules ← Cursor compatibility (priority 2)
.windsurfrules ← Windsurf compatibility (priority 3)
3. Single-File Configuration
When to Use Single-File
✅ Best for:
- Small projects (< 5,000 lines of code)
- Solo developers
- Prototypes and MVPs
- Simple tech stacks
❌ Avoid for:
- Multi-language projects
- Large teams
- Complex architecture
- Microservices
Creating .clinerules
# Create in project root
touch .clinerules
# Or copy from template
curl -o .clinerules https://raw.githubusercontent.com/examples/cline-templates/main/basic.md
Minimal Viable Rules (Starter Template)
# Project Rules
## Tech Stack
- **Language**: TypeScript 5.4
- **Framework**: Next.js 15 (App Router)
- **Database**: PostgreSQL 16
- **ORM**: Drizzle ORM
- **Testing**: Vitest
## Coding Standards
### TypeScript
- Strict mode enabled
- Explicit return types required
- No `any` types (use `unknown` + type guards)
### React
- Functional components only
- Server Components by default
- Add `'use client'` only when necessary
### Error Handling
typescript
type Result =
| { success: true; data: T }
| { success: false; error: string };
async function operation(): Promise> {
try {
const data = await fetch();
return { success: true, data };
} catch (error) {
logger.error('Operation failed', { error });
return { success: false, error: 'User message' };
}
}
## Testing
- 80% coverage minimum
- Unit tests with Vitest
- Test files: `*.test.ts`
## Important Notes
- Use `pnpm` (NOT npm/yarn)
- Never commit secrets (use .env.local)
- All PRs require 1 approval
File Size Recommendations
| Project Complexity | Target Length | Max Length |
|---|---|---|
| Simple | 100-200 lines | 300 lines |
| Moderate | 200-400 lines | 600 lines |
| Complex | 400-600 lines | 1,000 lines |
| Very Complex | Use directory-based | N/A |
Why size matters: Files over 1,000 lines become hard to maintain and consume excessive AI context tokens.
4. Directory-Based Configuration
When to Use Directory-Based
✅ Best for:
- Multi-language projects (TypeScript + Python)
- Large codebases (> 10,000 lines)
- Teams with different specializations
- Microservices architecture
- Complex monorepos
Creating .clinerules/ Directory
# Create directory structure
mkdir .clinerules
# Create initial files
touch .clinerules/01-project-overview.md
touch .clinerules/02-coding-standards.md
touch .clinerules/03-testing.md
touch .clinerules/04-deployment.md
File Naming Convention
Pattern: [number]-[description].md
.clinerules/
├── 01-project-overview.md # Always loaded first
├── 02-architecture.md # Structural patterns
├── 03-typescript-rules.md # Language-specific
├── 04-react-patterns.md # Framework-specific
├── 05-api-conventions.md # Backend standards
├── 06-database-patterns.md # Data layer
├── 07-testing-guide.md # QA standards
├── 08-security-checklist.md # Security requirements
└── 09-deployment-process.md # DevOps workflow
Numbering scheme:
-
01-09: Core rules (always relevant) -
10-19: Language/framework rules -
20-29: Advanced patterns -
30-39: Team-specific conventions
Loading Order
Cline merges files alphabetically (case-insensitive):
01-project-overview.md → Loaded 1st
02-architecture.md → Loaded 2nd
03-typescript-rules.md → Loaded 3rd
...
09-deployment-process.md → Loaded 9th
Total merged content is presented to AI as single context.
Modular Structure Example
01-project-overview.md (200 lines)
# Project Overview
## Core Information
- **Name**: E-Commerce Platform
- **Type**: Full-stack web application
- **Stack**: Next.js 15, TypeScript, PostgreSQL
- **Architecture**: Monorepo (Turborepo)
## Team Structure
- Frontend: 3 engineers
- Backend: 2 engineers
- Full-stack: 2 engineers
## Current Sprint Focus
- Shopping cart implementation
- Payment gateway integration (Stripe)
- Order management dashboard
## Quick Links
- Architecture diagrams: `docs/architecture/`
- API documentation: `docs/api/`
- Database schema: `packages/database/schema.ts`
03-typescript-rules.md (300 lines)
# TypeScript Coding Standards
## Type Annotations
### Function Return Types
**REQUIRED**: All functions must have explicit return types.
typescript
// ✅ CORRECT
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ WRONG - Missing return type
export function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
### Type vs Interface
Use **interfaces** for object shapes, **types** for unions/intersections.
typescript
// ✅ CORRECT - Interface for objects
interface User {
id: string;
name: string;
email: string;
}
// ✅ CORRECT - Type for unions
type Result =
| { success: true; data: T }
| { success: false; error: string };
// ❌ WRONG - Type for simple objects
type User = {
id: string;
name: string;
};
## Avoiding `any`
### Never Use `any`
typescript
// ❌ FORBIDDEN
function processData(data: any) {
return data.someProperty;
}
// ✅ CORRECT - Use unknown + type guard
function processData(data: unknown) {
if (isValidData(data)) {
return data.someProperty; // Type-safe
}
throw new Error('Invalid data');
}
function isValidData(data: unknown): data is { someProperty: string } {
return (
typeof data === 'object' &&
data !== null &&
'someProperty' in data &&
typeof (data as any).someProperty === 'string'
);
}
## Async Patterns
### Always Use async/await
typescript
// ✅ CORRECT
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' };
}
}
// ❌ WRONG - Using .then() chains
function fetchUser(id: string) {
return db.query.users.findFirst({ where: eq(users.id, id) })
.then(user => user)
.catch(error => console.log(error));
}
[Continue with more TypeScript rules...]
04-react-patterns.md (400 lines)
# React Coding Patterns
## Server vs Client Components
### Default: Server Components
tsx
// ✅ Server Component (no directive needed)
async function UserProfilePage({ params }: { params: { id: string } }) {
// Can fetch data directly
const user = await db.query.users.findFirst({
where: eq(users.id, params.id)
});
return (
{user.name}
);
}
### When to Use Client Components
Add `'use client'` directive ONLY when:
1. **Using React hooks**
tsx
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return setCount(count + 1)}>{count};
}
2. **Event handlers**
tsx
'use client';
export function Form() {
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
// Handle submission
};
return
...;}
3. **Browser APIs**
tsx
'use client';
import { useEffect } from 'react';
export function ThemeDetector() {
useEffect(() => {
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
// Use isDark
}, []);
return
Theme detector;}
## Component Structure
### Size Limits
- **Maximum 200 lines per component**
- **If exceeded**: Extract sub-components
- **Use composition** over props drilling
tsx
// ✅ CORRECT - Composition
User Profile
// ❌ WRONG - Props drilling
title="User Profile"
avatar={}
details={}
footer={}
headerClassName="..."
contentClassName="..."
footerClassName="..."
/>
## State Management
| Use Case | Solution | When to Use |
|----------|----------|-------------|
| Server data | TanStack Query | Fetching from APIs |
| Form state | React Hook Form + Zod | Complex forms with validation |
| Local UI state | useState | Component-specific (modals, toggles) |
| Global client state | Zustand | App-wide state (user, theme) |
| URL state | useSearchParams | Shareable state (filters, pagination) |
[Continue with more React patterns...]
Directory Structure Benefits
✅ Separation of concerns: Each file focuses on one topic
✅ Parallel editing: Multiple team members can update different files
✅ Selective loading: Comment out files temporarily (rename to .bak)
✅ Clear ownership: Assign files to team members
✅ Easier reviews: PRs show which specific area changed
5. Global Rules Setup
Purpose of Global Rules
Global rules apply to ALL projects on your machine. Use for:
- Personal coding style preferences
- Universal security practices
- Cross-project conventions
- Organization-wide standards (if shared drive)
Location by Operating System
| OS | Path |
|---|---|
| Windows | %USERPROFILE%\Documents\Cline\Rules\ |
| macOS | ~/Documents/Cline/Rules/ |
| Linux |
~/Documents/Cline/Rules/ (fallback: ~/Cline/Rules/) |
Creating Global Rules
Windows (PowerShell):
# Create directory
New-Item -ItemType Directory -Path "$env:USERPROFILE\Documents\Cline\Rules" -Force
# Create rule file
New-Item -ItemType File -Path "$env:USERPROFILE\Documents\Cline\Rules\my-preferences.md"
# Edit in Notepad
notepad "$env:USERPROFILE\Documents\Cline\Rules\my-preferences.md"
macOS/Linux (Bash):
# Create directory
mkdir -p ~/Documents/Cline/Rules
# Create rule file
touch ~/Documents/Cline/Rules/my-preferences.md
# Edit in default editor
${EDITOR:-nano} ~/Documents/Cline/Rules/my-preferences.md
Global Rules Template
~/Documents/Cline/Rules/my-preferences.md
# My Personal Coding Preferences
## Response Style
- Provide concise answers
- Show code examples with explanations
- Highlight potential issues or gotchas
- Suggest alternative approaches when relevant
## Code Style
- Use modern ES2022+ JavaScript/TypeScript syntax
- Prefer `const` over `let` (never `var`)
- Use destructuring where it improves readability
- Prefer explicit over clever
## Testing Philosophy
- Write tests AFTER implementation (not strict TDD)
- Focus on integration tests over unit tests
- Mock external APIs, use real database (test DB)
- Aim for practical coverage, not 100%
## Git Practices
- Conventional Commits format: `feat:`, `fix:`, `refactor:`, etc.
- Descriptive commit messages (not "fixed stuff")
- Squash commits before merging
## Documentation
- JSDoc comments for public APIs
- README updates when adding features
- Architecture decision records (ADRs) for major changes
## Things I Dislike
- Overly clever code (prefer explicit)
- Deep nesting (max 3 levels)
- Long functions (>50 lines)
- Generic variable names (`data`, `obj`, `temp`)
Precedence: Global vs Project
When both exist:
Global Rules (~/Documents/Cline/Rules/*.md)
↓ (loaded first, baseline)
Project Rules (.clinerules or .clinerules/)
↓ (loaded second, overrides)
Final Context (merged, project takes precedence)
Example:
- Global rule: "Use single quotes for strings"
- Project rule: "Use double quotes (Prettier config)"
- Cline follows: Double quotes (project overrides global)
Sharing Global Rules (Organizations)
Option 1: Symlink to Shared Drive
# Windows
mklink /D "%USERPROFILE%\Documents\Cline\Rules" "Z:\CompanyStandards\ClineRules"
# macOS/Linux
ln -s /Volumes/CompanyStandards/ClineRules ~/Documents/Cline/Rules
Option 2: Git Repository
# Clone company rules
git clone https://github.com/company/cline-global-rules.git ~/Documents/Cline/Rules
# Update periodically
cd ~/Documents/Cline/Rules && git pull
Option 3: Sync Script
#!/bin/bash
# sync-global-rules.sh
# Download from internal server
curl -o ~/Documents/Cline/Rules/company-standards.md \
https://internal.company.com/cline/standards.md
echo "✅ Global rules synced"
Add to cron (macOS/Linux) or Task Scheduler (Windows) for automatic updates.
6. Rule Structure & Syntax
Markdown Hierarchy
Cline parses standard Markdown with semantic meaning:
# H1 - Major Section (Project Overview, Tech Stack, etc.)
## H2 - Category (TypeScript Rules, React Patterns, etc.)
### H3 - Specific Rule (Type Annotations, Error Handling, etc.)
- Bullet point details
- Implementation notes
**Bold** - Emphasis for critical requirements
`code` - Inline code references
language
Code blocks for examples
Recommended Structure Template
# [Project Name] - Coding Rules
Version: [X.Y.Z]
Last Updated: [YYYY-MM-DD]
---
## 1. PROJECT CONTEXT
[Brief overview - who, what, why]
## 2. TECHNOLOGY STACK
[Languages, frameworks, tools with versions]
## 3. ARCHITECTURE
[Structure, patterns, design decisions]
## 4. CODING STANDARDS
### 4.1 [Language] Rules
### 4.2 [Framework] Patterns
### 4.3 Error Handling
### 4.4 Testing
## 5. COMMON COMMANDS
[Development, testing, deployment commands]
## 6. KNOWN ISSUES
[Gotchas, workarounds, limitations]
## 7. IMPORTANT NOTES
[Security, performance, deployment considerations]
Semantic Elements
1. Headers for Navigation
## Error Handling Standards
### Result<T> Pattern
[Details]
### Logging Requirements
[Details]
### Retry Logic
[Details]
Why it works: Cline can quickly scan headers to find relevant sections.
2. Code Fences for Examples
## API Response Format
ALL endpoints must return this structure:
```
typescript
interface ApiResponse<T> {
success: boolean;
data: T | null;
error: {
code: string;
message: string;
} | null;
}
```
Example usage:
```
typescript
// Success case
return {
success: true,
data: { id: '123', name: 'John' },
error: null
};
// Error case
return {
success: false,
data: null,
error: {
code: 'USER_NOT_FOUND',
message: 'User with ID 123 not found'
}
};
```
```
`
**Why it works**: Syntax highlighting helps Cline distinguish code from prose.
---
**3. Tables for Mappings**
```markdown
## 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 | Local | useState | `const [open, setOpen]` |
| Global client | Memory | App-wide | Zustand | `useUserStore()` |
```
**Why it works**: Dense information, easy to scan for specific scenario.
---
**4. Lists for Sequential Rules**
```markdown
## Code Review Checklist
Before submitting PR:
1. Run tests: `pnpm test` (all must pass)
2. Run linter: `pnpm lint --max-warnings 0`
3. Type check: `pnpm type-check`
4. Build succeeds: `pnpm build`
5. Remove debug statements (console.log, debugger)
6. Update documentation if API changed
7. Add tests for new functionality
```
**Why it works**: Clear sequence, easy to verify completion.
---
**5. Blockquotes for Warnings**
```markdown
## Database Migrations
> **⚠️ CRITICAL**: Never edit migration files manually after they've been run in production.
> Always create a new migration for schema changes.
> **💡 TIP**: Test migrations on staging environment before applying to production.
```
**Why it works**: Visually distinct, draws attention to important information.
---
**6. Bold/Italics for Emphasis**
```markdown
## Security Requirements
**NEVER** commit API keys or secrets to git.
*Always* validate user inputs before processing.
**REQUIRED**: All endpoints must have rate limiting.
```
**Why it works**: Emphasis signals priority to AI.
---
### **Anti-Patterns to Avoid**
**❌ Don't: Vague Language**
```markdown
## Bad Example
- Try to write clean code
- Be consistent
- Follow best practices
```
**✅ Do: Specific Directives**
```markdown
## Good Example
- Maximum 50 lines per function
- Use camelCase for variables, PascalCase for components
- Follow Airbnb JavaScript Style Guide (linked in ESLint config)
```
---
**❌ Don't: Prose-Heavy Explanations**
```markdown
## Bad Example
When you're working with asynchronous operations in this codebase,
it's really important that you remember to always use the async/await
syntax instead of using promise chains with .then() and .catch() because
we've found that it makes the code much more readable and easier to debug.
```
**✅ Do: Code-First Examples**
```markdown
## Good Example
### Async Patterns
```typescript
// ✅ CORRECT
async function fetchData() {
try {
const response = await api.get('/data');
return response.data;
} catch (error) {
logger.error('Fetch failed', { error });
throw error;
}
}
// ❌ WRONG
function fetchData() {
return api.get('/data')
.then(response => response.data)
.catch(error => console.log(error));
}
```
```
---
**❌ Don't: Generic Advice**
```markdown
## Bad Example
- Write good tests
- Handle errors properly
- Make code maintainable
```
**✅ Do: Concrete Patterns**
```markdown
## Good Example
### Testing Standards
Every feature requires:
1. **Unit test** (business logic)
```typescript
// File: src/lib/calculate.ts
export function calculateDiscount(price: number): number {
return price * 0.1;
}
// File: src/lib/calculate.test.ts
describe('calculateDiscount', () => {
it('calculates 10% discount', () => {
expect(calculateDiscount(100)).toBe(10);
});
});
```
2. **Integration test** (API endpoints)
3. **Minimum 80% coverage** (check with `pnpm test:coverage`)
```
---
<a name="7-effective-patterns"></a>
## **7. Effective Rule Patterns**
### **Pattern 1: Good/Bad Code Pairs**
**Formula**: Show anti-pattern → Explain why it's bad → Show correct pattern
```markdown
## Component Props
### ❌ WRONG: Inline Types
```tsx
function UserCard({ user, onEdit }: {
user: any;
onEdit: Function;
}) {
return <div>{user.name}</div>;
}
```
**Problems**:
- `any` type defeats TypeScript's purpose
- `Function` type is too generic
- No reusability of types
### ✅ CORRECT: Explicit Interface
```tsx
interface UserCardProps {
user: User; // Defined User type
onEdit: (id: string) => void; // Explicit function signature
}
function UserCard({ user, onEdit }: UserCardProps) {
return (
<div onClick={() => onEdit(user.id)}>
{user.name}
</div>
);
}
```
**Benefits**:
- Full type safety
- Reusable `UserCardProps` interface
- IDE autocomplete works perfectly
```
---
### **Pattern 2: Decision Trees**
**Formula**: Present scenario → Provide clear selection criteria
```markdown
## When to Use Each State Solution
### Is it server data (from API/database)?
→ **YES**: Use TanStack Query
```typescript
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers
});
```
→ **NO**: Continue to next question
### Does it need to persist across page navigation?
→ **YES**: Use URL state (useSearchParams)
```typescript
const [searchParams, setSearchParams] = useSearchParams();
const filter = searchParams.get('filter') || 'all';
```
→ **NO**: Continue to next question
### Is it needed across multiple components?
→ **YES**: Use Zustand (global state)
```typescript
const user = useUserStore(state => state.user);
```
→ **NO**: Use useState (local state)
```typescript
const [isOpen, setIsOpen] = useState(false);
```
```
---
### **Pattern 3: Checklist Format**
**Formula**: Verification steps with clear pass/fail criteria
```markdown
## Pre-Commit Checklist
Before committing code, verify:
- [ ] **Tests pass**: Run `pnpm test`
- Expected: All green, 0 failures
- If fails: Fix broken tests before committing
- [ ] **Linter passes**: Run `pnpm lint`
- Expected: 0 errors, 0 warnings
- If fails: Run `pnpm lint --fix` then check remaining issues
- [ ] **TypeScript compiles**: Run `pnpm type-check`
- Expected: 0 errors
- If fails: Fix type errors (no `@ts-ignore` shortcuts)
- [ ] **No debug code**: Search for `console.log`, `debugger`
- Expected: 0 occurrences in `/src`
- If found: Remove all debug statements
- [ ] **Build succeeds**: Run `pnpm build`
- Expected: Completes without errors
- If fails: Check build logs for issues
```
---
### **Pattern 4: Quick Reference Tables**
**Formula**: Compact lookup for common scenarios
```markdown
## Import Organization Reference
| Import Type | Order | Example |
|-------------|-------|---------|
| React/External | 1st | `import { useState } from 'react';` |
| Internal packages | 2nd | `import { Button } from '@repo/ui';` |
| Absolute imports | 3rd | `import { api } from '@/lib/api';` |
| Relative imports | 4th | `import { UserCard } from './UserCard';` |
| Type imports | 5th | `import type { User } from './types';` |
| Styles | Last | `import './styles.css';` |
**Template**:
```typescript
// 1. React & external
import { useState, useEffect } from 'react';
import { z } from 'zod';
// 2. Internal packages
import { Button } from '@repo/ui';
// 3. Absolute imports
import { trpc } from '@/lib/trpc';
// 4. Relative imports
import { UserCard } from './UserCard';
// 5. Types
import type { UserCardProps } from './types';
// 6. Styles
import './page.css';
```
```
---
### **Pattern 5: Progressive Detail Levels**
**Formula**: High-level principle → Pattern → Detailed example
```markdown
## Error Handling (Three Levels)
### Level 1: Principle
ALL async operations must handle errors explicitly and never fail silently.
### Level 2: Pattern
Use `Result<T>` type for predictable error handling:
```typescript
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
```
### Level 3: Complete Implementation
```typescript
// Define Result type
type Result<T> =
| { success: true; data: T }
| { success: false; error: string };
// Use in async function
async function fetchUser(id: string): Promise<Result<User>> {
try {
// 1. Attempt operation
const user = await db.query.users.findFirst({
where: eq(users.id, id)
});
// 2. Handle "not found" gracefully
if (!user) {
return {
success: false,
error: 'User not found'
};
}
// 3. Return success
return {
success: true,
data: user
};
} catch (error) {
// 4. Log for debugging
logger.error('fetchUser failed', {
userId: id,
error
});
// 5. Return user-friendly error
return {
success: false,
error: 'Failed to fetch user. Please try again.'
};
}
}
// Usage in calling code
async function handleGetUser(id: string) {
const result = await fetchUser(id);
if (!result.success) {
// Handle error
toast.error(result.error);
return;
}
// Use data safely (TypeScript knows it exists)
const user = result.data;
console.log(user.name);
}
```
```
---
<a name="8-language-specific"></a>
## **8. Language-Specific Rules**
### **TypeScript Rules Template**
```markdown
# TypeScript Standards
## Strict Mode Configuration
Ensure `tsconfig.json` has:
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true
}
}
```
## Type Annotations
### Function Signatures
```typescript
// ✅ CORRECT - Explicit return type
function add(a: number, b: number): number {
return a + b;
}
// ✅ CORRECT - Async function
async function fetchData(url: string): Promise<Response> {
return await fetch(url);
}
// ❌ WRONG - No return type
function add(a: number, b: number) {
return a + b;
}
```
### Object Types
```typescript
// ✅ PREFER - Interface for objects
interface User {
id: string;
name: string;
email: string;
}
// ✅ PREFER - Type for unions/intersections
type Status = 'active' | 'inactive' | 'pending';
type UserWithStatus = User & { status: Status };
// ❌ AVOID - Type for simple objects
type User = {
id: string;
name: string;
};
```
## Null/Undefined Handling
```typescript
// ✅ CORRECT - Optional chaining
const userName = user?.profile?.name ?? 'Anonymous';
// ✅ CORRECT - Nullish coalescing
const port = config.port ?? 3000;
// ❌ WRONG - Loose OR operator (treats 0, '', false as missing)
const port = config.port || 3000;
```
## Generic Types
```typescript
// ✅ CORRECT - Descriptive generic names
interface ApiResponse<TData, TError = Error> {
data: TData | null;
error: TError | null;
}
// ✅ CORRECT - Constrained generics
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
//...
}
// ❌ WRONG - Single letter without context
interface Response<T> {
data: T;
}
```
## Type Guards
```typescript
// ✅ CORRECT - Type predicate
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
typeof (value as User).id === 'string' &&
typeof (value as User).name === 'string'
);
}
// Usage
function processValue(value: unknown) {
if (isUser(value)) {
console.log(value.name); // Type-safe!
}
}
```
## Utility Types
Common patterns:
```typescript
// Partial - Make all properties optional
type PartialUser = Partial<User>;
// Pick - Select specific properties
type UserSummary = Pick<User, 'id' | 'name'>;
// Omit - Exclude specific properties
type UserWithoutEmail = Omit<User, 'email'>;
// Record - Object with specific key/value types
type UserMap = Record<string, User>;
// ReturnType - Extract function return type
type FetchResult = ReturnType<typeof fetchUser>;
```
```
---
### **Python Rules Template**
```markdown
# Python Standards
## Python Version
- Minimum: Python 3.11
- Target: Python 3.12
## Type Hints
### Function Annotations
```python
# ✅ CORRECT - Type hints on all functions
def calculate_total(items: list[Item]) -> float:
return sum(item.price for item in items)
async def fetch_user(user_id: str) -> User | None:
user = await db.query(User).where(User.id == user_id).first()
return user
# ❌ WRONG - No type hints
def calculate_total(items):
return sum(item.price for item in items)
```
### Variable Annotations
```python
# ✅ CORRECT - When type isn't obvious
users: list[User] = []
config: dict[str, Any] = load_config()
# ✅ CORRECT - No annotation when obvious
name = "John" # Obviously str
count = 0 # Obviously int
```
## Dataclasses Over Dicts
```python
# ✅ CORRECT - Dataclass for structure
from dataclasses import dataclass
@dataclass
class User:
id: str
name: str
email: str
is_active: bool = True
user = User(id="123", name="John", email="john@example.com")
print(user.name) # Type-safe attribute access
# ❌ WRONG - Dict without structure
user = {
"id": "123",
"name": "John",
"email": "john@example.com"
}
print(user["name"]) # No type safety
```
## Error Handling
```python
# ✅ CORRECT - Specific exceptions
try:
result = await api.fetch_data()
except HTTPError as e:
logger.error(f"API error: {e.status_code}", exc_info=True)
raise APIException(f"Failed to fetch: {e.message}")
except TimeoutError:
logger.warning("Request timed out, retrying...")
result = await retry_with_backoff(api.fetch_data)
# ❌ WRONG - Bare except
try:
result = await api.fetch_data()
except: # Catches everything, even KeyboardInterrupt!
pass
```
## Async Patterns
```python
# ✅ CORRECT - Context manager for connections
async def fetch_users() -> list[User]:
async with get_db_connection() as conn:
result = await conn.query("SELECT * FROM users")
return [User(**row) for row in result]
# ✅ CORRECT - Gather for concurrent operations
async def fetch_all_data(user_ids: list[str]) -> list[User]:
tasks = [fetch_user(uid) for uid in user_ids]
return await asyncio.gather(*tasks)
# ❌ WRONG - Sequential when could be parallel
async def fetch_all_data(user_ids: list[str]) -> list[User]:
users = []
for uid in user_ids:
user = await fetch_user(uid) # Waits for each!
users.append(user)
return users
```
## String Formatting
```python
# ✅ PREFER - f-strings
name = "John"
age = 30
message = f"User {name} is {age} years old"
# ✅ ACCEPTABLE - format() for templates
template = "User {name} is {age} years old"
message = template.format(name=name, age=age)
# ❌ AVOID - % formatting (legacy)
message = "User %s is %d years old" % (name, age)
# ❌ AVOID - String concatenation
message = "User " + name + " is " + str(age) + " years old"
```
```
---
### **JavaScript/Node.js Rules Template**
```markdown
# JavaScript/Node.js Standards
## Modern Syntax (ES2022+)
### Prefer `const` over `let`
```javascript
// ✅ CORRECT
const users = await fetchUsers();
const user = users.find(u => u.id === userId);
// ❌ WRONG - Unnecessary `let`
let users = await fetchUsers(); // Never reassigned
let user = users.find(u => u.id === userId); // Never reassigned
```
### Optional Chaining & Nullish Coalescing
```javascript
// ✅ CORRECT - Safe property access
const userName = user?.profile?.name ?? 'Anonymous';
const port = process.env.PORT ?? 3000;
// ❌ WRONG - Verbose null checks
const userName = user && user.profile && user.profile.name || 'Anonymous';
const port = process.env.PORT || 3000; // Treats 0 as falsy!
```
### Destructuring
```javascript
// ✅ CORRECT - Destructure parameters
function createUser({ name, email, role = 'user' }) {
return { name, email, role, createdAt: Date.now() };
}
// ✅ CORRECT - Destructure imports
const { readFile, writeFile } = require('fs/promises');
// ❌ WRONG - Accessing object repeatedly
function createUser(params) {
return {
name: params.name,
email: params.email,
role: params.role || 'user',
createdAt: Date.now()
};
}
```
## Async Patterns
### Always async/await
```javascript
// ✅ CORRECT
async function fetchUserData(userId) {
try {
const user = await db.query.users.findFirst({
where: { id: userId }
});
if (!user) {
throw new Error('User not found');
}
return user;
} catch (error) {
logger.error('fetchUserData failed', { userId, error });
throw error;
}
}
// ❌ WRONG - Promise chains
function fetchUserData(userId) {
return db.query.users.findFirst({ where: { id: userId } })
.then(user => {
if (!user) throw new Error('User not found');
return user;
})
.catch(error => {
console.log(error); // Also wrong - use logger
throw error;
});
}
```
## Module System
### Use ES Modules (ESM)
```javascript
// ✅ CORRECT - ESM
import { readFile } from 'fs/promises';
import { User } from './models/User.js';
export async function loadUser(id) {
//...
}
// ❌ WRONG - CommonJS (use only for legacy compatibility)
const { readFile } = require('fs/promises');
const { User } = require('./models/User');
module.exports = { loadUser };
```
## Error Handling
### Custom Error Classes
```javascript
// ✅ CORRECT - Typed errors
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
class NotFoundError extends Error {
constructor(resource, id) {
super(`${resource} with ID ${id} not found`);
this.name = 'NotFoundError';
this.resource = resource;
this.id = id;
}
}
// Usage
async function getUser(id) {
const user = await db.users.findById(id);
if (!user) {
throw new NotFoundError('User', id);
}
return user;
}
```
```
---
<a name="9-framework-guides"></a>
## **9. Framework Integration Guides**
### **Next.js 15 (App Router) Guide**
**Complete `.clinerules` for Next.js projects:**
```markdown
# Next.js 15 Project Rules
## Framework Version
- Next.js 15.1 (App Router - NOT Pages Router)
- React 19
- TypeScript 5.4
## Directory Structure
```
app/
├── (auth)/ # Route group (doesn't affect URL)
│ ├── login/
│ └── register/
├── (dashboard)/
│ ├── layout.tsx # Shared layout
│ └── page.tsx
├── api/ # API routes (use sparingly, prefer tRPC)
│ └── webhooks/
├── layout.tsx # Root layout
└── page.tsx # Home page
components/
├── ui/ # shadcn/ui primitives
└── features/ # Feature-specific components
lib/
├── api/ # tRPC routers
├── db/ # Database client & schema
└── utils/ # Utility functions
```
## Server vs Client Components
### Default: Server Components
```tsx
// ✅ Server Component (no directive, can be async)
async function UserProfilePage({ params }: { params: { id: string } }) {
// Direct data fetching
const user = await db.query.users.findFirst({
where: eq(users.id, params.id)
});
if (!user) {
notFound(); // Next.js helper
}
return (
<div>
<h1>{user.name}</h1>
<UserPosts userId={user.id} />
</div>
);
}
export default UserProfilePage;
```
### Client Components (when necessary)
```tsx
'use client';
import { useState } from 'react';
// Add 'use client' ONLY when using:
// - React hooks (useState, useEffect, useContext, etc.)
// - Event handlers (onClick, onChange, onSubmit, etc.)
// - Browser APIs (window, localStorage, navigator, etc.)
// - useSearchParams, usePathname, useRouter (client hooks)
export function InteractiveCounter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
```
## Data Fetching
### Server Component Data Fetching
```tsx
// ✅ CORRECT - Direct async in Server Component
async function ProductsPage() {
const products = await db.query.products.findMany({
orderBy: desc(products.createdAt)
});
return <ProductList products={products} />;
}
// ❌ WRONG - useEffect in Server Component
function ProductsPage() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products').then(...);
}, []);
return <ProductList products={products} />;
}
```
### Client Component Data Fetching
**Use TanStack Query (or tRPC) for client-side data.**
```tsx
// ✅ CORRECT - TanStack Query
'use client';
function ProductList() {
const { data, isLoading } = useQuery({
queryKey: ['products'],
queryFn: fetchProducts
});
if (isLoading) return <Spinner />;
return <div>{data.map(p => <Product key={p.id} product={p} />)}</div>;
}
// ❌ WRONG - useEffect boilerplate
'use client';
function ProductList() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchProducts()
.then(setData)
.finally(() => setLoading(false));
}, []);
//...
}
```
## Server Actions
### Usage
Use Server Actions for mutations (form submissions, updates).
```tsx
// ✅ CORRECT - Server Action
async function createPost(formData: FormData) {
'use server';
const title = formData.get('title');
if (!title) return { error: 'Title required' };
await db.insert(posts).values({ title });
revalidatePath('/posts');
}
// Component using it
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
```
## Metadata
### Static Metadata
```tsx
// layout.tsx or page.tsx
export const metadata: Metadata = {
title: 'My App',
description: 'The best app ever',
};
```
### Dynamic Metadata
```tsx
// app/products/[id]/page.tsx
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const product = await getProduct(params.id);
return {
title: product.title,
description: product.description,
};
}
```
```
---
### **React + Vite (SPA) Guide**
**Complete `.clinerules` for Vite projects:**
```markdown
# React + Vite Project Rules
## Stack
- **Build**: Vite 5
- **Framework**: React 18 (Client-Side Rendering)
- **Language**: TypeScript 5
- **State**: Zustand + TanStack Query
- **Router**: React Router 6
## Project Structure
```
src/
├── assets/ # Static images
├── components/ # Shared components
├── features/ # Feature modules
│ ├── auth/
│ │ ├── api/ # API calls for feature
│ │ ├── components/ # Feature-specific components
│ │ ├── hooks/ # Custom hooks
│ │ └── routes/ # Route definitions
│ └── products/
├── hooks/ # Global hooks
├── lib/ # Axios, QueryClient config
├── routes/ # Router setup
├── stores/ # Global Zustand stores
├── types/ # Global types
└── utils/ # Helper functions
```
## Component Patterns
### Functional Components
```tsx
// ✅ CORRECT - Typed props
interface Props {
title: string;
isActive?: boolean;
}
export const Header = ({ title, isActive = false }: Props) => {
return (
<header className={isActive ? 'active' : ''}>
<h1>{title}</h1>
</header>
);
};
// ❌ WRONG - React.FC (deprecated pattern)
export const Header: React.FC<Props> = ({ title }) => {
return <header>{title}</header>;
};
```
### Custom Hooks
Extract logic into custom hooks when:
1. Logic is reused
2. Logic is complex
3. Component exceeds 150 lines
```tsx
// ✅ CORRECT - Custom Hook
function useProductSearch() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
const { data } = useQuery({
queryKey: ['search', debouncedQuery],
queryFn: () => api.search(debouncedQuery),
enabled: !!debouncedQuery
});
return { query, setQuery, results: data };
}
// Component usage
function SearchBar() {
const { query, setQuery, results } = useProductSearch();
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}
```
## Environment Variables
Vite uses `import.meta.env`, NOT `process.env`.
```typescript
// ✅ CORRECT
const apiUrl = import.meta.env.VITE_API_URL;
// ❌ WRONG
const apiUrl = process.env.VITE_API_URL;
```
- Must be prefixed with `VITE_` to be exposed to client
- Define types in `src/vite-env.d.ts`
```typescript
// src/vite-env.d.ts
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_AUTH_DOMAIN: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
```
```
---
### **FastAPI (Python) Guide**
**Complete `.clinerules` for FastAPI projects:**
```markdown
# FastAPI Project Rules
## Stack
- Python 3.12
- FastAPI 0.110
- Pydantic 2.0
- SQLAlchemy 2.0 (Async)
- Alembic (Migrations)
## Project Structure
```
app/
├── api/
│ ├── deps.py # Dependency injection
│ ├── api_v1/
│ │ └── endpoints/ # Route handlers
├── core/
│ ├── config.py # Settings
│ └── security.py # Auth utils
├── crud/ # Database operations (Create, Read, Update, Delete)
├── db/
│ ├── base.py # Base class
│ └── session.py # DB session
├── models/ # SQLAlchemy models
├── schemas/ # Pydantic schemas
└── main.py # App entry point
```
## API Development
### Router Structure
```python
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from app import crud, schemas
from app.api import deps
router = APIRouter()
@router.get("/{item_id}", response_model=schemas.Item)
async def read_item(
item_id: int,
db: AsyncSession = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Get item by ID.
"""
item = await crud.item.get(db=db, id=item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id):
raise HTTPException(status_code=400, detail="Not enough permissions")
return item
```
### Pydantic Models
Use separate schemas for Create, Update, and Response.
```python
# schemas/item.py
from pydantic import BaseModel
# Shared properties
class ItemBase(BaseModel):
title: str | None = None
description: str | None = None
# Properties to receive on item creation
class ItemCreate(ItemBase):
title: str # Required
# Properties to receive on item update
class ItemUpdate(ItemBase):
pass
# Properties shared by models stored in DB
class ItemInDBBase(ItemBase):
id: int
owner_id: int
class Config:
from_attributes = True
# Properties to return to client
class Item(ItemInDBBase):
pass
```
## Database
### Async SQLAlchemy
Always use async session and await queries.
```python
# ✅ CORRECT
result = await db.execute(select(User).where(User.email == email))
user = result.scalars().first()
# ❌ WRONG (Synchronous style)
user = db.query(User).filter(User.email == email).first()
```
### Alembic Migrations
- NEVER modify migration files manually after generation
- Generate: `alembic revision --autogenerate -m "Add user table"`
- Apply: `alembic upgrade head`
## Testing
- Use `pytest` and `httpx` (AsyncClient)
- Use `conftest.py` for fixtures
```python
# tests/api/test_items.py
async def test_create_item(
client: AsyncClient,superuser_token_headers: dict[str, str]
) -> None:
data = {"title": "Foo", "description": "Fighters"}
response = await client.post(
f"{settings.API_V1_STR}/items/",
headers=superuser_token_headers,
json=data,
)
assert response.status_code == 200
content = response.json()
assert content["title"] == data["title"]
assert "id" in content
```
```
---
<a name="10-rules-bank"></a>
## **10. Rules Bank Strategy**
For developers working on diverse projects, maintaining a "Rules Bank" allows quick context switching.
### **Directory Structure**
```
~/Documents/Cline/
├── Rules/ # Global rules (always active)
│ └── my-preferences.md
│
└── RulesBank/ # Repository of inactive rules
├── frameworks/
│ ├── nextjs-app-router.md
│ ├── react-vite.md
│ ├── fastapi.md
│ └── express.md
│
├── languages/
│ ├── typescript.md
│ ├── python.md
│ ├── rust.md
│ └── go.md
│
├── project-types/
│ ├── microservice.md
│ ├── cli-tool.md
│ ├── library.md
│ └── documentation.md
│
└── clients/
├── acme-corp.md
└── globex.md
```
### **Workflow: Starting a New Project**
1. **Identify needs**: "I'm building a Next.js SaaS for Client X"
2. **Initialize project rules**:
```bash
# In project root
mkdir .clinerules
# Copy from bank
cp ~/Documents/Cline/RulesBank/frameworks/nextjs-app-router.md .clinerules/01-framework.md
cp ~/Documents/Cline/RulesBank/languages/typescript.md .clinerules/02-language.md
cp ~/Documents/Cline/RulesBank/project-types/saas.md .clinerules/03-type.md
```
### **Workflow: Updating the Bank**
When you discover a better pattern in a project:
1. Update the project's `.clinerules`
2. Copy the improvement back to `~/Documents/Cline/RulesBank`
3. Commit the bank to a personal Git repo for backup
### **Creating Reusable Modules**
Design bank files to be composable (avoid overlapping concerns).
**`languages/typescript.md`**: Only TS syntax, types, linting.
**`frameworks/react.md`**: Only component patterns, hooks.
**`testing/vitest.md`**: Only testing syntax.
**Composition**:
```
Project = TypeScript + React + Vitest
```
This prevents contradictions like `languages/typescript.md` saying "Use Jest" while `frameworks/react.md` says "Use Vitest".
---
<a name="11-testing"></a>
## **11. Testing & Validation**
How do you know your rules are working?
### **The "Gold Standard" Test**
1. **Select a file**: Pick a well-written existing file (e.g., `src/utils/format.ts`).
2. **Delete it**: Remove the file contents.
3. **Prompt Cline**: "Regenerate `src/utils/format.ts` based on project rules."
4. **Compare**: Diff the result with the original.
**Scoring:**
- **100% Match**: Perfect rules.
- **Logic correct, style wrong**: Add style rules (imports, naming).
- **Structure wrong**: Add architectural rules.
- **Libraries wrong**: Update Tech Stack section.
### **The "Violation" Prompt**
Ask Cline to critique your rules:
> "Review `.clinerules`. Identify any contradictions, vague instructions, or missing critical information based on the current codebase."
### **Validation Checklist**
Before committing rules:
- [ ] **Tech Stack**: Are all versions specified? (e.g., Next.js 15 vs 14)
- [ ] **Constraints**: Are "NEVER" rules explicit?
- [ ] **Examples**: Are there code blocks for complex patterns?
- [ ] **Consistency**: Do framework rules match language rules?
- [ ] **Secrets**: Are there any API keys (remove them!)?
---
<a name="12-maintenance"></a>
## **12. Maintenance & Updates**
### **When to Update Rules**
1. **Stack Upgrade**: Upgrading from Next.js 14 to 15? Update rules immediately.
2. **New Pattern**: Team decided to switch from Redux to Zustand? Update rules.
3. **Recurring Bug**: AI keeps making the same mistake? Add a "NEVER" rule.
4. **New Hire**: Onboarding questions reveal missing documentation.
### **Change Management**
Treat `.clinerules` like code:
- **Branching**: Create `docs/update-rules` branch.
- **PR Review**: Team reviews rule changes.
- **Commit Messages**: `docs: update typescript rules for stricter typing`.
### **Deprecation Strategy**
When changing patterns (e.g., replacing `axios` with `fetch`):
1. **Mark Deprecated**:
```markdown
### ⚠️ DEPRECATED: Axios
Do not use `axios` for new code. Existing code will be migrated.
```
2. **Define New Standard**:
```markdown
### ✅ CURRENT: Fetch API
Use native `fetch` with the `client` wrapper in `src/lib/api`.
```
3. **Cleanup**: Remove deprecated rule once migration is complete.
---
<a name="13-examples"></a>
## **13. Real-World Examples**
### **Example 1: The "Strict" CTO**
For teams requiring high discipline and uniformity.
```markdown
# Engineering Standards
## Strict Compliance
- **0 Warnings Policy**: CI fails on any linter/TS warning.
- **100% Type Safety**: Usage of `any` requires `// eslint-disable-next-line` with explanation.
- **Immutable Data**: Use `readonly` arrays and properties by default.
## Review Process
- AI must self-review before outputting code.
- Format: "I have verified this code against the strict compliance rules."
## Architecture
- **Hexagonal Architecture**: Domain > Application > Infrastructure.
- **Dependency Rule**: Inner layers never depend on outer layers.
```
### **Example 2: The "Speed" Startup**
For MVPs and rapid prototyping.
```markdown
# MVP Rules
## Philosophy
- **Speed > Perfection**: Ship working code now, optimize later.
- **YAGNI**: Don't build generic solutions for hypothetical future problems.
- **Simplicity**: Choose the simplest solution that works.
## Tech Stack
- Next.js, Tailwind, Supabase (BaaS)
## Shortcuts Allowed
- Inline styles for one-off adjustments.
- `any` allowed for complex external API responses (add TODO).
- Co-location: Put components, styles, and utils in same file if small.
```
### **Example 3: The "Legacy" Migration**
For moving from old stack to new stack.
```markdown
# Migration Rules
## Context
Migrating from Express/JavaScript to NestJS/TypeScript.
## Rules
1. **New Features**: Must use NestJS/TypeScript.
2. **Bug Fixes**:
- If trivial, fix in place (Express).
- If complex, refactor to NestJS first.
3. **Interop**: Use the Adapter Pattern in `src/adapters` to bridge systems.
## TypeScript Migration
- Enable `allowJs: true` in tsconfig.
- Migrate files one by one.
- Use `JSDoc` types for JS files pending migration.
```
---
<a name="14-troubleshooting"></a>
## **14. Troubleshooting**
### **Problem: Cline Ignores Rules**
**Cause 1**: File location incorrect.
- **Fix**: Ensure `.clinerules` is in the *root* of the workspace opened in VS Code.
**Cause 2**: Formatting errors.
- **Fix**: Check Markdown syntax. Headers (`#`) need a space after hash. Code blocks need closing backticks.
**Cause 3**: Context window overflow.
- **Fix**: Rule file is too large. Split into directory-based rules or remove verbose prose.
### **Problem: Hallucinations (Inventing Libraries)**
**Cause**: Tech stack not specific enough.
- **Fix**: Add explicit versions. "Use React Query" → "Use TanStack Query v5".
**Cause**: Conflicting instructions.
- **Fix**: Check for contradictions (e.g., "Use Redux" and "Use Zustand").
### **Problem: "I can't do that"**
**Cause**: Safety filters triggered by "hacky" looking instructions.
- **Fix**: Rephrase as standard engineering patterns.
---
<a name="15-migration"></a>
## **15. Migration from Other Tools**
### **From Cursor (`.cursorrules`)**
1. **Rename**: `.cursorrules` → `.clinerules`
2. **Cleanup**: Remove `@file` syntax (Cline uses context differently).
3. **Review**: Cursor rules often rely on "Composer" features; adapt for Cline's agentic workflow.
### **From Windsurf (`.windsurfrules`)**
1. **Extract**: Copy Markdown content (ignore proprietary XML tags if used).
2. **Paste**: Put into `.clinerules`.
3. **Simplify**: Remove Windsurf-specific "memories" or "cascade" instructions.
### **From Copilot (`.github/copilot-instructions.md`)**
1. **Copy**: `cp .github/copilot-instructions.md .clinerules`
2. **Enhance**: Copilot rules are usually brief. Cline can handle (and benefits from) more detailed architectural context.
Top comments (0)