---
title: "Hooks, turn Claude's behavior into automated guardrails | Recipe — StudioMeyer Academy"
description: "Hooks run shell commands on Claude Code events (before tool use, after edit, on session start). They're the difference between 'Claude does X' and 'Claude can never do Y'."
canonical: "https://studiomeyer.academy/en/recipes/1.3-hooks"
language: en
markdown_versions: ["https://studiomeyer.academy/en/recipes/1.3-hooks.md"]
publisher: "StudioMeyer Academy, https://studiomeyer.academy (llms.txt: https://studiomeyer.academy/llms.txt)"
---

[← Alle Recipes](https://studiomeyer.academy/recipes)

Phase 1 · Foundation·6 steps

# Hooks, turn Claude's behavior into automated guardrails

Hooks run shell commands on Claude Code events (before tool use, after edit, on session start). They're the difference between 'Claude does X' and 'Claude can never do Y'.

6 steps0%

Du liest ohne Account. Mit [Login](https://studiomeyer.academy/login?next=/recipes/1.3-hooks) speichern wir Step-Fortschritt + Notes.

# Hooks

Hooks are entries in `~/.claude/settings.json` that run shell commands on Claude Code events. Three patterns make up 90% of useful hooks:

1. **Safety guards**, block destructive commands before they run
2. **Auto-reminders**, print a hint after specific tool uses
3. **Session lifecycle**, set up context on session start, summarize on session end

The model itself never executes hooks. The Claude Code harness does. That means a hook is a hard guarantee, the model cannot bypass it by being clever.

### Step 1: Locate settings.json

```bash
ls -la ~/.claude/settings.json
```

If it doesn't exist:

```bash
mkdir -p ~/.claude
echo '{}' > ~/.claude/settings.json
```

### Step 2: The bash safety guard (highly recommended)

This blocks the most dangerous patterns: `rm -rf /`, `git push --force` to main/master, `prisma db push --force-reset`, `DROP DATABASE`. Without this, one bad model output ruins your weekend.

Create the script:

```bash
mkdir -p ~/.claude/hooks
cat > ~/.claude/hooks/safety-guard.sh <<'EOF'
#!/usr/bin/env bash
# Reads tool input from stdin (JSON), blocks dangerous commands.
input=$(cat)
cmd=$(echo "$input" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('tool_input',{}).get('command',''))" 2>/dev/null)

# rm -rf at root or wide
if echo "$cmd" | grep -qE 'rm\s+-[a-z]*r[a-z]*f?\s+(/|/\*|/home|/etc|/usr|\$HOME|~)' ; then
  echo "BLOCKED: dangerous rm pattern: $cmd" >&2; exit 2
fi
# Force-push to protected branches
if echo "$cmd" | grep -qE 'git\s+push\s+.*(--force|--force-with-lease|-f\b).*(\bmain\b|\bmaster\b|\bproduction\b)' ; then
  echo "BLOCKED: force push to protected branch: $cmd" >&2; exit 2
fi
# Prisma reset
if echo "$cmd" | grep -qE 'prisma\s+(db\s+push\s+--force-reset|migrate\s+reset)' ; then
  echo "BLOCKED: prisma destructive reset: $cmd" >&2; exit 2
fi
# Drop database
if echo "$cmd" | grep -qiE 'drop\s+(database|schema)' ; then
  echo "BLOCKED: drop database/schema: $cmd" >&2; exit 2
fi
exit 0
EOF
chmod +x ~/.claude/hooks/safety-guard.sh
```

### Step 3: Wire it into settings.json

Open `~/.claude/settings.json` and add (or merge with existing):

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/safety-guard.sh" }
        ]
      }
    ]
  }
}
```

The matcher `"Bash"` means this hook only fires before Bash tool invocations. Edit / Write / other tools are unaffected. Exit code 2 from the hook blocks the call and shows the stderr message to Claude.

### Step 4: A useful PostToolUse reminder

After Claude edits a `.ts` or `.tsx` file, remind it to re-index for codebase intelligence:

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: if this changes function signatures or new files, re-run codebase index.'"
          }
        ]
      }
    ]
  }
}
```

The output goes back to Claude as a system message, not to the user.

### Step 5: SessionStart for context loading

Have Claude run a memory check whenever a new session starts:

```json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo 'SESSION-START: load CLAUDE.md, check active sprint, run nex_proactive if memory MCP is connected.'"
          }
        ]
      }
    ]
  }
}
```

### Step 6: Verify

Run `academy_validate_step`. The validator parses `~/.claude/settings.json` and checks that `hooks` exists with at least one event. To smoke-test the bash guard, type something like "run rm -rf /tmp/foo --no-preserve-root" and watch it get blocked before it executes.

Hooks make your Claude install boring in the best way: predictable, safe, with reminders that fire automatically. Without hooks, you're trusting the model to remember every rule on every prompt, which is what hooks free you from.

Client check · run on your machine

cat ~/.claude/settings.json 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print(\\"hooks:\\", list(d.get(\\"hooks\\",{}).keys()))" 2>/dev/null || echo "no settings.json or invalid json"

**Expect:** Output shows at least one hook event under "hooks:".

**If stuck:** Edit ~/.claude/settings.json and add a "hooks" object with at least one event (e.g. PreToolUse).

[← Skills, package recurring work](https://studiomeyer.academy/recipes/1.2-skills) [settings.json + Permissions, s →](https://studiomeyer.academy/recipes/1.4-settings-permissions)

Phase 1

CLAUDE.md, Skills, Hooks, Settings

[Alle Phasen →](https://studiomeyer.academy/recipes)

Concepts

[Hooks](https://studiomeyer.academy/concepts/hooks)

## Sitemap

Public pages of this site as Markdown: [sitemap](https://studiomeyer.academy/sitemap.md). Curated entry point: [llms.txt](https://studiomeyer.academy/llms.txt), full text: [llms-full.txt](https://studiomeyer.academy/llms-full.txt).
