Skip to content

.claude/rules/ Path-Based Rules

When using AI agents, once CLAUDE.md exceeds 200 lines, Claude starts ignoring rules. The key tool to prevent this is the .claude/rules/ directory.


⚠️ When Do You Need Rules?

If you're seeing these symptoms, it's time to split into Rules:

  • Claude is violating a rule you just mentioned
  • Even saying "I told you earlier" doesn't help
  • When you run /context, the System Prompt takes up a large portion

Choosing a solution:

If Claude is ignoring rules because they're too long → Split into Rules. Keep only the core rules in CLAUDE.md and move folder-specific detailed rules to .claude/rules/.

If it's a repetitive task (lint before commit, format on file save) → Automate with Hooks. Repeated instructions like "always run npm run lint before committing" shouldn't be in CLAUDE.md — make them a pre-commit Hook instead.

If it's a rule that's always followed → Consider converting to a Hook. Rules that Claude follows 100% of the time are better automated as Hooks to save tokens.

💡 Official recommendation: "Automate what Claude does well with Hooks, and explicitly define what it doesn't with Rules."


Basic Structure

your-project/
├── .claude/
│   ├── CLAUDE.md              ← Core rules (50-100 lines)
│   └── rules/
│       ├── code-style.md      ← Global style (no paths)
│       ├── api.md             ← Only for API work (with paths)
│       └── components.md      ← Only for component work

Important: All .md files inside .claude/rules/ are automatically recognized.


Two Loading Methods

Always Load (no paths)

If there is no YAML frontmatter or no paths field, the file is loaded unconditionally at session start. It operates at the same priority as CLAUDE.md.

markdown
# Coding Style Rules

- 2-space indentation
- ESLint compliance

Conditional Load (with paths)

If the YAML frontmatter has a paths field, the file is only loaded when working with matching files. API rules only enter the context when modifying files under src/api/.

markdown
---
paths:
  - "src/api/**/*.ts"
---

# API Rules

- Zod validation required
- rate limiting required

Syntax: YAML Frontmatter

markdown
---
paths:
  - "path/pattern/**"
  - "another/pattern/*.ts"
---

# Rule Content

Notes:

  • paths: (plural — not path)
  • Must start with --- and end with ---
  • Each path is a YAML array in - "path" format

Path Patterns

PatternMatched Files
"src/api/**/*.ts"src/api/users/route.ts
"src/components/*.tsx"src/components/Button.tsx (excludes subdirectories)
"**/*.test.ts"All test files

Glob pattern rules:

  • * = one level (does not cross slashes)
  • ** = all levels (recursive)

Real-World Examples

API Rules (conditional load)

.claude/rules/api.md

markdown
---
paths:
  - "src/api/**/*.ts"
---

# API Rules

- Zod validation required
- rate limiting default 100 req/min
- Error response format: { error: "message", code: "CODE" }
- JWT verification via Supabase

Component Rules (conditional load)

.claude/rules/components.md

markdown
---
paths:
  - "src/components/**/*.tsx"
---

# React Component Rules

## Props Naming
- Booleans: `isLoading`, `hasError`
- Handlers: `onSubmit`, `onClick`

## Styling
- Tailwind CSS only
- No inline styles

💡 Practical Tips

1. Splitting an Existing CLAUDE.md

A step-by-step strategy for migrating a long CLAUDE.md to Rules.

1. Back up CLAUDE.md (make a copy)
2. Group by folder (API/components/DB...)
3. Create Rules files + set paths
4. Delete those sections from CLAUDE.md
5. Test one at a time (don't split everything at once)

2. Measured Token Savings

Based on the Typingroom project:

BEFORE: CLAUDE.md 280 lines → always loaded

AFTER: CLAUDE.md 50 lines + rules 230 lines (split by paths)

Results:
- During API work: only 150 lines loaded (46% savings)
- During component work: only 140 lines loaded (50% savings)

3. Finding paths Patterns

You can check file patterns you frequently work with using git log.

bash
# Check frequently worked-on files
git log --name-only --pretty=format: | sort | uniq -c | sort -rn | head -20

# Example output:
# 42 src/api/users/route.ts
# 38 src/components/Button.tsx
# 25 src/hooks/useAuth.ts

 Split rules separately for src/api/**, src/components/**, src/hooks/**

4. Checking for Conflicts

"Find any conflicting rules between CLAUDE.md and rules"

Ask Claude and it will find them for you.


❌ Never Do This (Common Mistakes)

Mistake 1: Using the singular path

This is the syntax mistake that trips people up 8 out of 10 times.

markdown
# ❌ Wrong
path: "src/api/**"

# ✅ Correct
---
paths:
  - "src/api/**/*.ts"
---

Mistake 2: Thinking it's conditional without paths

Without a paths frontmatter, the file is always loaded unconditionally.

markdown
# .claude/rules/api.md

# API Rules
- rate limiting required

→ "This will only load during API work" ← This is wrong. Without paths, it always loads.

Mistake 3: Conflicting with CLAUDE.md

markdown
# CLAUDE.md
- Use Express for API

# .claude/rules/api.md
- Use Fastify for API

→ Claude gets confused and picks randomly.

Fix: Keep only the stack overview in CLAUDE.md, and put specific implementation rules in Rules.

Mistake 4: Making 10 files with 10 lines each

❌ Bad example:
.claude/rules/
  ├── api-users.md (10 lines)
  ├── api-auth.md (8 lines)
  ├── api-posts.md (12 lines)
  ...

✅ Good example:
.claude/rules/
  └── api.md (40 lines, all consolidated)

Rule of thumb: Only split when a single file exceeds 50 lines and is clearly independent.

Mistake 5: Continuing to work when rules aren't being applied

When you've created Rules but Claude isn't following them.

Checklist:

1. Is the YAML syntax correct? (---, paths:)
2. Does the path pattern match the actual files?
3. Have you restarted Claude Code?

How to verify:

"Show me the rule files currently loaded"

Ask Claude and it will show you the list of currently loaded rules.


Usage Patterns

Basic 3-tier Structure

CLAUDE.md (50 lines)
  ← Project stack, core prohibitions

.claude/rules/
  ├── code-style.md (no paths, 50 lines)
  │   ← Global coding style
  ├── api.md (with paths, 50 lines)
  │   ← Loaded only during src/api/** work
  └── components.md (with paths, 50 lines)
      ← Loaded only during src/components/** work

Result:

  • Total rules are 200 lines but only 150 lines are loaded at a time
  • 25% token savings

Troubleshooting

When Rules Aren't Being Applied

1. Check YAML frontmatter syntax (---, paths:)
2. Verify that the path pattern matches the actual files
3. Restart Claude Code

When a File with paths Always Loads

→ The glob pattern is too broad (like **/* matching everything)
→ Double-check the paths syntax

How to Verify Loading

bash
# Ask Claude directly
"Show me the rule files currently loaded"

# Or use Hook for log output
# .claude/settings.json
{
  "hooks": {
    "InstructionsLoaded": [{
      "hooks": [{
        "type": "command",
        "command": "echo 'Loaded: $HOOK_FILE_PATH'"
      }]
    }]
  }
}

→ Logs will appear in the terminal showing which files were loaded and when.


PRs welcome ✦