Skip to content

Example Hook Scripts

These are ready-to-use hook configurations for claude_internal/settings.json. Each example includes the settings entry and any associated scripts. For background on how hooks work, see the Hooks Guide.

Auto-Format by File Type

A PostToolUse hook that runs the appropriate formatter whenever Claude edits a file.

claude_internal/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hook": "bash /path/to/your/project/claude_internal/hooks/auto-format.sh $CLAUDE_FILE_PATH"
      }
    ]
  }
}

claude_internal/hooks/auto-format.sh:

#!/usr/bin/env bash
set -euo pipefail

FILE="$1"
EXT="${FILE##*.}"

case "$EXT" in
  ts|tsx|js|jsx|json|css|md)
    npx prettier --write "$FILE" 2>/dev/null
    ;;
  py)
    black --quiet "$FILE" 2>/dev/null
    ;;
  rs)
    rustfmt "$FILE" 2>/dev/null
    ;;
esac

Pre-Commit Lint Gate

A PreToolUse hook that intercepts git commit commands and runs linting first. If the linter fails, the commit is blocked.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hook": "bash /path/to/your/project/claude_internal/hooks/pre-commit-lint.sh \"$CLAUDE_BASH_COMMAND\""
      }
    ]
  }
}

claude_internal/hooks/pre-commit-lint.sh:

#!/usr/bin/env bash
set -euo pipefail

COMMAND="$1"

if echo "$COMMAND" | grep -q "git commit"; then
  echo "Running lint check before commit..."
  npm run lint
  # Non-zero exit here blocks the tool use
fi

Desktop Notification on Task Complete

A Notification hook that alerts you when Claude finishes a task. Choose the variant for your OS.

macOS:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hook": "osascript -e 'display notification \"$CLAUDE_NOTIFICATION\" with title \"Claude Code\"'"
      }
    ]
  }
}

Linux:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hook": "notify-send 'Claude Code' \"$CLAUDE_NOTIFICATION\""
      }
    ]
  }
}

Windows (PowerShell toast):

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hook": "powershell -Command \"[console]::beep(600,300); Write-Host 'Claude: $env:CLAUDE_NOTIFICATION'\""
      }
    ]
  }
}

Run Tests After Edits

A PostToolUse hook that runs the corresponding test file when a source file is edited.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hook": "bash /path/to/your/project/claude_internal/hooks/run-related-test.sh $CLAUDE_FILE_PATH"
      }
    ]
  }
}

claude_internal/hooks/run-related-test.sh:

#!/usr/bin/env bash
set -euo pipefail

FILE="$1"

# Skip if the edited file is itself a test
if echo "$FILE" | grep -qE '\.(test|spec)\.(ts|js|py)$'; then
  exit 0
fi

# Derive test file path (adjust pattern to match your project)
TEST_FILE=$(echo "$FILE" | sed 's|src/|tests/|' | sed 's/\.\(ts\|js\)$/.test.\1/')

if [ -f "$TEST_FILE" ]; then
  echo "Running related test: $TEST_FILE"
  npx jest "$TEST_FILE" --no-coverage 2>&1 | tail -5
fi

Audit Log

A PostToolUse hook that logs every tool invocation with a timestamp.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "",
        "hook": "echo \"$(date -Iseconds) tool=$CLAUDE_TOOL_NAME file=$CLAUDE_FILE_PATH\" >> ~/claude_internal/audit-log.txt"
      }
    ]
  }
}

This produces output like:

2026-03-22T10:15:32+00:00 tool=Edit file=src/index.ts
2026-03-22T10:15:45+00:00 tool=Bash file=

Block Dangerous Commands

A PreToolUse hook that rejects potentially destructive commands before they execute.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hook": "bash /path/to/your/project/claude_internal/hooks/block-dangerous.sh \"$CLAUDE_BASH_COMMAND\""
      }
    ]
  }
}

claude_internal/hooks/block-dangerous.sh:

#!/usr/bin/env bash
set -euo pipefail

COMMAND="$1"

BLOCKED_PATTERNS=(
  "rm -rf /"
  "DROP TABLE"
  "DROP DATABASE"
  "git push --force.*main"
  "git push --force.*master"
  "git push -f.*main"
  "git push -f.*master"
  ":(){ :|:& };:"
)

for pattern in "${BLOCKED_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qEi "$pattern"; then
    echo "BLOCKED: Command matches dangerous pattern: $pattern" >&2
    exit 1
  fi
done

See Also