Module 7 Β· Intermediate Β· ~45 min

πŸ€– Hooks β€” Automating Your Workflow

Hooks let you set up automatic actions that run when Claude does something β€” like a spell check every time a file is saved, or a git commit after every task.

What are hooks?

πŸ“–
Plain English: what is a hook? A hook is a "if this happens, then run that" rule you set up once. For example: "Every time Claude saves a do file, automatically run dofile_linter to check for errors." The hook runs in the background β€” you don't have to remember to do it. It's like setting up an automatic alarm: configure it once, it triggers automatically.

Hooks are configured in Claude Code's settings file (settings.json) and run as shell commands in response to events in your session.

Types of hooks

Hook typeTriggers when...Research use case
PreToolUse Claude is about to use a tool (read/edit/run) Validate a file before Claude edits it; block risky operations
PostToolUse Claude just used a tool successfully Auto-commit after every file edit; run linter after saving code
Stop Claude finishes a task (stops generating) Send a notification; run tests; log what was done
SubagentStop A sub-agent finishes Aggregate results from parallel tasks

Configuring hooks

Hooks are set in ~/.claude/settings.json (global) or .claude/settings.json (project-level). Here's the structure:

// ~/.claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'File saved: $CLAUDE_FILE_PATH'"
          }
        ]
      }
    ]
  }
}

The matcher field specifies which tool triggers the hook (e.g., "Write", "Edit", "Bash"). You can also match on specific file patterns.

Research-specific hook examples

Auto-commit after every file edit

Never lose work by automatically committing every change Claude makes:

{
  "PostToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "git add -A && git commit -m 'Claude: auto-save $(date +%H:%M:%S)' 2>/dev/null || true"
      }]
    }
  ]
}

Run Stata's dofile linter on save

{
  "PostToolUse": [
    {
      "matcher": "Write",
      "hooks": [{
        "type": "command",
        "command": "if [[ '$CLAUDE_FILE_PATH' == *.do ]]; then echo 'Stata file saved: $CLAUDE_FILE_PATH'; fi"
      }]
    }
  ]
}

Desktop notification when Claude finishes a long task

{
  "Stop": [
    {
      "hooks": [{
        "type": "command",
        "command": "osascript -e 'display notification \"Claude finished!\" with title \"Claude Code\"'"
      }]
    }
  ]
}

(Mac only β€” for Linux use notify-send, for Windows use PowerShell's New-BurntToastNotification)

Log all tasks to a research journal

{
  "Stop": [
    {
      "hooks": [{
        "type": "command",
        "command": "echo \"$(date): Claude session ended\" >> ~/research-log.txt"
      }]
    }
  ]
}

Safety and caution

⚠️
Hooks run shell commands automatically β€” treat them with care
  • A hook that deletes files or overwrites data will run every time the trigger fires
  • Test hooks with echo commands before using real operations
  • Never put sensitive information (API keys, passwords) in hook commands
  • Start simple β€” one hook at a time, with logging so you can see what's happening
🎯

Module 7 Exercise: Set up your first hook

Set up a simple, safe hook that shows you what's happening:

  1. Open (or create) ~/.claude/settings.json
  2. Add a Stop hook that logs "Claude finished at [time]" to a file called ~/claude-activity.log
  3. Start a Claude Code session, give it a small task, and verify the log file was updated when Claude stopped
  4. As a bonus: add a Mac/Linux desktop notification hook for long tasks

Hint: Ask Claude Code to help you write the settings.json syntax β€” "Add a Stop hook to my settings.json that logs each session end to ~/claude-activity.log"

TerminalJSON~25 min