Charly
·3 min read

My 5 Claude Code hooks that change everything

Claude Code hooks run shell commands on session start, file writes, and tool calls. Here are the 5 I use daily and how to configure them.

Most people discover Claude Code for the AI. They miss the hooks.

Hooks are shell commands that execute automatically when something happens in your Claude Code session: a session starts, a tool runs, Claude stops. You define them in .claude/settings.json. They run silently in the background.

Here are the 5 I use every day.

What hooks look like

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": "echo 'Today is $(date)'" }
        ]
      }
    ]
  }
}

The available events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop.


Hook 1 — Auto-inject date on every prompt

Event: UserPromptSubmit

echo "Context: today is $(date '+%A %B %d, %Y')."

Claude doesn't have real-time awareness. Every prompt gets the current date injected automatically. Useful for anything time-sensitive: changelogs, deadlines, scheduling logic.


Hook 2 — Push notification when Claude stops

Event: Stop

osascript -e 'display notification "Claude is done" with title "Claude Code"'

Long tasks (refactors, test runs, analysis) can take 2–5 minutes. Without a notification I'd either stare at the screen or miss the result. This fires whenever Claude finishes a task, whether it succeeded or hit a limit.

On macOS you get a native notification. On Linux, swap for notify-send.


Hook 3 — Safety check before Bash runs

Event: PreToolUse (Bash)

command=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.command')
if echo "$command" | grep -qE '(rm -rf|drop table|DELETE FROM|reset --hard)'; then
  echo "BLOCKED: destructive command detected. Confirm manually." >&2
  exit 1
fi

This intercepts every Bash tool call and blocks patterns I've flagged as dangerous. Exit code 1 stops the execution and surfaces the error in Claude's context. You can adjust the regex to whatever you care about.


Hook 4 — Auto-lint after every file write

Event: PostToolUse (Write/Edit)

file=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path')
ext="${file##*.}"
if [[ "$ext" == "ts" || "$ext" == "tsx" ]]; then
  npx eslint --fix "$file" --quiet 2>/dev/null || true
fi

Every TypeScript file Claude writes gets linted immediately. No more discovering 10 lint errors after a big refactor. The || true keeps things from blocking if the file has unfixable issues — Claude sees those in the next tool call.


Hook 5 — Load project briefing on session start

Event: SessionStart

cat .claude/briefing.md 2>/dev/null && echo "---"
git log --oneline -5 2>/dev/null

My .claude/briefing.md has: current sprint goal, in-progress work, known blockers. Combined with the last 5 commits, Claude starts each session with the project's actual state — not just what's in CLAUDE.md.

I update the briefing file whenever priorities shift. Takes 30 seconds.


Where to put them

Global hooks go in ~/.claude/settings.json (apply to all projects). Project hooks go in .claude/settings.json at the repo root (checked in, shared with the team).

For hooks that reference project-specific paths or tools, use the project-level file.


The compounding effect

None of these hooks is dramatic on its own. Together they remove the small frictions that break flow: manually noting the date, checking if Claude is done, catching destructive commands late, fixing lint in a second pass, re-explaining context.

That's where the time actually goes. Hooks are how you get it back.

The Claude Code Skills Starter Kit includes pre-built hooks for auto-formatting, secret scanning, and blocking destructive commands — ready to drop into ~/.claude/.

Get the next post in your inbox

Practical tips for building with AI. One email per post.

Related posts