You wrote the perfect CLAUDE.md. Claude still ignores it.
Here's why: CLAUDE.md files are suggestions—not commands.
Claude can and will override your CLAUDE.md when:
- Context windows get tight
- Your rules conflict with its training
- It thinks it knows better
This post shows you how to turn those suggestions into laws.
The Problem
You specify PascalCase components, Claude uses camelCase.
You say "use async/await," Claude writes callbacks.
You document your testing workflow, Claude skips it.
Every session, you repeat the same explanations.
The Root Cause
CLAUDE.md files are passive. Claude reads them once at session start, then forgets. The rules don't fire on every operation.
The Solution: Hooks
Hooks are enforcement—they run before and after every Claude operation.
Here are 5 hooks that actually enforce your rules:
1. Session Start Memory Hook
Forces Claude to read your memory file at session start.
#!/bin/bash
# .claude/hooks/session-start.sh
MEMORY_FILE=".claude/memory.md"
if [ -f "$MEMORY_FILE" ]; then
cat "$MEMORY_FILE"
fi
2. Session End Memory Hook
Prompts Claude to write learnings to memory at session end.
#!/bin/bash
# .claude/hooks/session-end.sh
MEMORY_FILE=".claude/memory.md"
echo "## Session Learning - $(date)" >> "$MEMORY_FILE"
echo "What did you learn this session?" >> "$MEMORY_FILE"
3. Test-First Gate Hook
Blocks Write operations if no test file exists.
#!/bin/bash
# .claude/hooks/pre-write.sh
FILE="$1"
TEST_DIR="tests"
# Check if test file exists
if [[ "$FILE" == *".ts" || "$FILE" == *".py" || "$FILE" == *".js" ]]; then
BASENAME=$(basename "$FILE")
TEST_FILE="$TEST_DIR/${BASENAME%.*}.test.*"
if [ ! -f "$TEST_FILE" ]; then
echo "Error: No test file found. Write the test first."
exit 1
fi
fi
4. Code Style Gate Hook
Runs ESLint/Prettier/ruff before any Write fires.
#!/bin/bash
# .claude/hooks/pre-write.sh
FILE="$1"
# Run linter before write
if [[ "$FILE" == *".ts" || "$FILE" == *".js" ]]; then
npm run lint "$FILE"
if [ $? -ne 0 ]; then
echo "Error: Linter failed. Fix linting before writing."
exit 1
fi
fi
if [[ "$FILE" == *".py" ]]; then
ruff check "$FILE"
if [ $? -ne 0 ]; then
echo "Error: Ruff check failed. Fix linting before writing."
exit 1
fi
fi
5. Context Injector Hook
Injects project context on every tool call.
#!/bin/bash
# .claude/hooks/pre-any.sh
CONTEXT_FILE=".claude/context.txt"
if [ -f "$CONTEXT_FILE" ]; then
cat "$CONTEXT_FILE"
fi
Why Hooks Matter
CLAUDE.md: "Please write tests first" → Claude: "Maybe"
Hooks: "Cannot Write without test file" → Claude: "Okay, writing test first"
Hooks turn suggestions into laws.
How to Install
- Create
.claude/hooks/directory - Add the hook files (make them executable:
chmod +x .claude/hooks/*.sh) - Configure Claude Code to use the hooks directory
- Done
Total time: 5 minutes.
Complete CLAUDE.md Rules Pack
This post shows you 5 hooks. My CLAUDE.md Rules Pack includes:
- 10 battle-tested CLAUDE.md templates (drop in and use)
- 5 enforcement hooks (the secret sauce)
- Anti-Patterns Guide (why your CLAUDE.md gets ignored)
- Quick Start Guide (15-minute setup)
Get the complete CLAUDE.md Rules Pack
Price: $27
30-day money-back guarantee.
TL;DR
CLAUDE.md files are suggestions. Hooks are laws.
Install 2 hooks, and Claude follows your rules—every time.
Top comments (0)