DEV Community

Olivia Craft
Olivia Craft

Posted on • Originally published at oliviacraftlat.gumroad.com

You Wrote the Perfect CLAUDE.md. Claude Still Ignores It.

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

  1. Create .claude/hooks/ directory
  2. Add the hook files (make them executable: chmod +x .claude/hooks/*.sh)
  3. Configure Claude Code to use the hooks directory
  4. 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.

Get the pack

Top comments (0)