раздел 06 · подстраница 3

5 готовых hooks

Боевые hooks, которые работают у меня прямо сейчас. Копируйте в ~/.claude/settings.json (или проектный .claude/settings.json).

Зачем

Эти пять закрывают типовые потребности: автоформат, уведомления, защита секретов, авто-тесты, авто-коммиты. Их хватает, чтобы Claude перестал ломать стиль кода и забывать про CI.

1. Авто-формат после Edit/Write

Black для .py, prettier для .ts/.tsx/.js/.json.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "for f in $CLAUDE_FILE_PATHS; do case \"$f\" in *.py) command -v black >/dev/null && black -q \"$f\" ;; *.ts|*.tsx|*.js|*.jsx|*.json|*.md) command -v prettier >/dev/null && prettier --write --log-level error \"$f\" ;; esac; done; exit 0"
          }
        ]
      }
    ]
  }
}

Тонкости:

  • command -v ... >/dev/null - не падать, если форматтера нет.
  • exit 0 в конце - hook никогда не должен блокировать сохранение.
  • Поддержите все расширения, иначе Claude будет править стиль обратно после форматтера.

2. macOS-уведомление после Stop

Звук + всплывашка, чтобы заметить, когда Claude закончил длинную задачу.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Ping.aiff & osascript -e 'display notification \"Claude finished\" with title \"Claude Code\" sound name \"Ping\"' ; exit 0"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Tink.aiff ; exit 0"
          }
        ]
      }
    ]
  }
}

Для Linux замените osascript на notify-send "Claude" "finished".

3. Блок чтения .env через PreToolUse

Жёсткая защита: Claude никогда не прочитает файлы с секретами, даже если попросите.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "case \"$CLAUDE_FILE_PATHS$CLAUDE_TOOL_INPUT\" in *\".env\"*|*\"credentials\"*|*\"id_rsa\"*|*\".pem\"*) echo 'BLOCKED: secret file' >&2; exit 1 ;; esac; exit 0"
          }
        ]
      }
    ]
  }
}

exit 1 блокирует вызов. Claude получит ошибку и попытается обойти - попросите его не настаивать и читать .env.example.

4. Авто-pytest при изменениях в backend/

После любой правки файлов в backend/ - прогон тестов соответствующего модуля.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/auto-pytest.sh"
          }
        ]
      }
    ]
  }
}

.claude/hooks/auto-pytest.sh:

#!/usr/bin/env bash
set -e

# фильтруем только backend/*.py
files=""
for f in $CLAUDE_FILE_PATHS; do
  case "$f" in
    backend/*.py) files="$files $f" ;;
  esac
done

[ -z "$files" ] && exit 0

# запускаем pytest только для изменённых модулей
cd backend
timeout 60s pytest -x --tb=short -q $files 2>&1 | tail -30 || {
  echo "TESTS FAILED" >&2
  exit 2
}
exit 0

exit 2 - soft block: Claude увидит сообщение и пойдёт чинить тесты.

5. Авто-коммит после Stop

В feature-ветке Claude закончил - сразу git add -A && git commit с авто-сгенерированным сообщением. Используется в долгих параллельных сессиях, чтобы не терять прогресс.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/auto-commit.sh"
          }
        ]
      }
    ]
  }
}

.claude/hooks/auto-commit.sh:

#!/usr/bin/env bash
set -e

# только не на main/master
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
case "$branch" in
  main|master|"") exit 0 ;;
esac

# есть ли что коммитить
[ -z "$(git status --porcelain)" ] && exit 0

# короткий summary через первые 3 изменённых файла
files=$(git diff --name-only --cached HEAD 2>/dev/null | head -3 | tr '\n' ',' | sed 's/,$//')
[ -z "$files" ] && files=$(git status --porcelain | awk '{print $2}' | head -3 | tr '\n' ',' | sed 's/,$//')

git add -A
git commit -m "wip: auto-commit ($files)

Co-authored-by: Claude Code <noreply@anthropic.com>" --no-verify || true

exit 0

Как поставить все пять

Один файл ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Bash",
        "hooks": [{ "type": "command", "command": "case \"$CLAUDE_FILE_PATHS$CLAUDE_TOOL_INPUT\" in *\".env\"*|*\"credentials\"*) echo 'BLOCKED' >&2; exit 1 ;; esac; exit 0" }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          { "type": "command", "command": "for f in $CLAUDE_FILE_PATHS; do case \"$f\" in *.py) black -q \"$f\" 2>/dev/null ;; *.ts|*.tsx|*.js|*.json|*.md) prettier --write --log-level error \"$f\" 2>/dev/null ;; esac; done; exit 0" },
          { "type": "command", "command": ".claude/hooks/auto-pytest.sh" }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          { "type": "command", "command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"' ; exit 0" },
          { "type": "command", "command": ".claude/hooks/auto-commit.sh" }
        ]
      }
    ]
  }
}

И сделать скрипты исполняемыми:

chmod +x .claude/hooks/*.sh

Антипаттерны

  • Запускать npm test (вместо pytest -x по конкретным файлам) на каждый Edit - сессия превратится в очередь ожиданий.
  • Авто-коммит на main без условия - ломается история.
  • Уведомление на каждый Stop в чате с десятком ответов - спам, добавьте rate-limit.
  • Не использовать timeout - hook может зависнуть и заморозить Claude.

Полезные ссылки