Boost Claude.md Compliance to 100% with 8 Essential Hooks

The article explains why Claude.md only obeys about 80% of instructions, introduces Claude Code Hooks as a deterministic alternative, and provides eight ready‑to‑copy hook configurations—including auto‑formatting, dangerous‑command blocking, file protection, test enforcement, linting, command logging, and automatic commits—to reliably enforce code‑quality policies.

ShiZhen AI
ShiZhen AI
ShiZhen AI
Boost Claude.md Compliance to 100% with 8 Essential Hooks

Why Hooks Matter

Claude.md is a guideline file that Claude reads and tries to follow, but its compliance rate is roughly 80% due to context length, task complexity, and other random factors. Hooks are a separate mechanism that run at specific moments (PreToolUse, PostToolUse, or Stop) and execute commands directly, guaranteeing enforcement regardless of Claude’s language understanding.

Hook Placement

Hooks are configured in .claude/settings.json and can be stored at three levels:

.claude/settings.json         // project‑level, shared via git
~/.claude/settings.json       // user‑level, applies to all projects
.claude/settings.local.json   // local private, not committed

Official documentation: https://code.claude.com/docs/en/hooks

Eight Ready‑to‑Use Hooks

1. Auto‑formatting (Prettier / Black / gofmt)

Problem: Code runs but the formatting is messy, requiring extra review cycles.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0"
          }
        ]
      }
    ]
  }
}

Replace the command with black for Python projects or gofmt for Go projects; the logic remains identical.

2. Dangerous‑Command Interception

Problem: Claude can execute risky commands such as rm -rf, git reset --hard, or DROP TABLE, and “it should not” is insufficient.

#!/usr/bin/env bash
set -euopipefail
cmd=$(jq -r '.tool_input.command // ""')

dangerous_patterns=(
  "rm -rf"
  "git reset --hard"
  "git push.*--force"
  "DROP TABLE"
  "DROP DATABASE"
  "curl.*|.*sh"
  "wget.*|.*bash"
)

for pattern in "${dangerous_patterns[@]}"; do
  if echo "$cmd" | grep -qiE "$pattern"; then
    echo "Blocked: '$cmd' matches dangerous pattern '$pattern'. Propose a safer alternative." >&2
    exit 2
  fi
done
exit 0

Register the script in .claude/settings.json under PreToolUse with matcher "Bash". Exit code 2 signals rejection, while 0 allows the operation.

3. Sensitive‑File Protection

Problem: Files such as .env, *.pem, or other secret files should never be edited by Claude.

#!/usr/bin/env bash
set -euopipefail
file=$(jq -r '.tool_input.file_path // .tool_input.path // ""')

protected=(
  ".env*"
  ".git/*"
  "package-lock.json"
  "yarn.lock"
  "*.pem"
  "*.key"
  "secrets/*"
)

for pattern in "${protected[@]}"; do
  if echo "$file" | grep -qiE "^${pattern//\*/\\*}.*$"; then
    echo "Blocked: '$file' is protected. Explain why this edit is necessary." >&2
    exit 2
  fi
done
exit 0

Register with matcher "Edit|Write" under PreToolUse.

4. Run Tests After Each Edit

Problem: Claude reports success, but the commit fails because tests break.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run test --silent 2>&1 | tail -5; exit 0"
          }
        ]
      }
    ]
  }
}

The tail -5 limits output to the last five lines, preventing the test log from overwhelming Claude’s context. Boris Cherny notes this feedback loop can improve Claude’s code quality by 2‑3×.

5. Enforce Green Tests Before PR Creation

Problem: PRs are opened with failing CI, causing reviewers to reject them.

#!/usr/bin/env bash
set -euopipefail
if npm run test --silent; then
  exit 0
else
  echo "Tests are failing. Fix all test failures before creating a PR." >&2
  exit 2
fi

Register under PreToolUse with matcher mcp__github__create_pull_request. A failing test aborts PR creation.

6. Automatic Linting and Error Feedback

Problem: Code passes formatting but fails ESLint rules, leading to repeated review rejections.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0"
          }
        ]
      }
    ]
  }
}

This can be combined with the Prettier hook so that formatting and linting are both applied before Claude returns the file.

7. Log Every Claude Command

Problem: It’s unclear what commands Claude executed days ago, making debugging difficult.

#!/usr/bin/env bash
set -euopipefail
cmd=$(jq -r '.tool_input.command // ""')
printf '%s %s
' "$(date -Is)" "$cmd" >> .claude/command-log.txt
exit 0

Register with matcher "Bash" under PreToolUse. Remember to add .claude/command-log.txt to .gitignore so the log isn’t committed.

8. Auto‑Commit After Each Task

Problem: Multiple edits are collapsed into a single vague commit, losing granularity.

#!/usr/bin/env bash
set -euopipefail
git add -A
if ! git diff --cached --quiet; then
  git commit -m "chore(ai): apply Claude edit"
fi
exit 0

Register under the Stop event so that each Claude reply triggers a separate commit, keeping the Git history clean.

Combined settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/block-dangerous.sh" },
          { "type": "command", "command": ".claude/hooks/log-commands.sh" }
        ]
      },
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/protect-files.sh" }
        ]
      },
      {
        "matcher": "mcp__github__create_pull_request",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/require-tests-for-pr.sh" }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null; exit 0" },
          { "type": "command", "command": "npx eslint --fix $(jq -r '.tool_input.file_path') 2>&1 | tail -10; exit 0" },
          { "type": "command", "command": "npm run test --silent 2>&1 | tail -5; exit 0" }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": ".claude/hooks/auto-commit.sh" }
        ]
      }
    ]
  }
}

Start by enabling Hook 1 (auto‑formatting) and Hook 2 (dangerous‑command blocking); they provide the highest immediate benefit.

AutomationGithooksshellClaudeESLintPrettier
ShiZhen AI
Written by

ShiZhen AI

Tech blogger with over 10 years of experience at leading tech firms, AI efficiency and delivery expert focusing on AI productivity. Covers tech gadgets, AI-driven efficiency, and leisure— AI leisure community. 🛰 szzdzhp001

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.