> · 8 мин

Claude Code hooks — 10 автоматизаций, после которых ручная рутина кажется дикостью

Claude Code hooks — 10 автоматизаций, после которых ручная рутина кажется дикостью

Claude Code hooks — 10 автоматизаций, после которых ручная рутина кажется дикостью

Пишешь в CLAUDE.md «всегда форматируй код через Prettier». Claude читает. Кивает. И в половине случаев забывает. Потому что CLAUDE.md — это пожелание. Вежливая просьба к LLM. А hooks — это приказ операционной системе.

TL;DR: Hooks — shell-команды, которые Claude Code запускает автоматически на каждом этапе работы. Авто-формат, защита .env, уведомления, блокировка опасных команд — 10 готовых конфигов, которые копируешь в settings.json и забываешь про ручной контроль.

Что такое hooks и зачем они нужны

Hook — это команда, которая срабатывает в конкретный момент жизненного цикла Claude Code. Не «может сработать», не «сработает, если LLM захочет». Сработает всегда. По документации Claude Code, hooks обеспечивают детерминированный контроль — в отличие от промптов и CLAUDE.md, которые работают вероятностно.

Простое правило: если это пожелание — пиши в CLAUDE.md. Если это требование — делай hook.

Hooks настраиваются в JSON через три уровня:

  • ~/.claude/settings.json — для всех проектов на машине
  • .claude/settings.json — для конкретного проекта (можно коммитить в git)
  • .claude/settings.local.json — для проекта, но в .gitignore

Самый быстрый способ создать первый hook — набрать /hooks прямо в Claude Code CLI. Откроется интерактивное меню со всеми доступными событиями.

1. Авто-формат после каждой правки

Самый популярный hook — и самый полезный. Каждый раз, когда Claude редактирует файл через Edit или Write, Prettier автоматически приводит код в порядок. Никаких «забыл отформатировать» в диффах.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null"
          }
        ]
      }
    ]
  }
}

PostToolUse срабатывает после выполнения инструмента. Matcher Edit|Write — это regex, который фильтрует по имени инструмента. Hook получает JSON на stdin, jq вытаскивает путь к файлу, Prettier форматирует. Для Python-проектов замени npx prettier --write на black.

2. Защита чувствительных файлов

Claude иногда порывается поправить .env, package-lock.json или что-нибудь в .git/. Этот hook блокирует любые попытки записи в защищённые файлы — и объясняет Claude, почему.

Создай скрипт .claude/hooks/protect-files.sh:

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

PROTECTED_PATTERNS=(".env" "package-lock.json" "yarn.lock" ".git/" "secrets/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0

Зарегистрируй в .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

Ключевой момент: exit 2 — это блокировка. Claude получает сообщение из stderr и адаптируется. exit 0 — действие разрешено. Любой другой код — ошибка, но действие всё равно пройдёт. И не забудь chmod +x .claude/hooks/protect-files.sh.

3. Уведомления — перестаёшь пялиться в терминал

Claude работает, ты занимаешься другими делами. Когда ему нужен твой ввод или разрешение — десктопное уведомление.

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

На macOS — osascript, на Linux — notify-send 'Claude Code' 'Needs attention'. Можно пойти дальше и отправлять webhook в Slack или Telegram — это обычный shell-скрипт, ограничений нет.

4. Инъекция контекста после compaction

Когда контекстное окно заполняется, Claude Code сжимает разговор. При этом теряются детали: какой пакетный менеджер использовать, какой спринт сейчас идёт, какие решения приняты. Hook SessionStart с matcher compact решает проблему — после каждой компактизации вбрасывает критический контекст обратно.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.' && git log --oneline -5"
          }
        ]
      }
    ]
  }
}

Всё, что скрипт выводит в stdout, добавляется в контекст Claude. Можно подтягивать git log, текущую ветку, содержимое TODO-файла — что угодно.

5. Блокировка опасных bash-команд

Claude иногда генерирует rm -rf, DROP TABLE или git push --force. PreToolUse hook на Bash перехватывает команду до выполнения:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

DANGEROUS_PATTERNS=("rm -rf /" "DROP TABLE" "DROP DATABASE" "--force" "git push -f")

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qi "$pattern"; then
    echo "Blocked: dangerous pattern '$pattern' detected in command" >&2
    exit 2
  fi
done

exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
          }
        ]
      }
    ]
  }
}

6. Аудит всех bash-команд

Хочешь знать, что именно Claude натворил за сессию? Логируй каждую bash-команду:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}

Простой append в файл. Полезно для ревью сессий, особенно когда Claude работает в автономном режиме.

7. Авто-запуск тестов при изменении тест-файлов

Когда Claude меняет файл с суффиксом .test. или .spec., автоматически прогоняем тесты:

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE_PATH" == *.test.* ]] || [[ "$FILE_PATH" == *.spec.* ]]; then
  npx vitest run "$FILE_PATH" --reporter=verbose 2>&1
fi

exit 0

8. Prompt-based hooks — когда нужно суждение, а не правило

Иногда «блокировать или пропустить» — слишком грубо. Нужен анализ. type: "prompt" отправляет контекст hook'а в Claude Haiku (по умолчанию) и получает yes/no решение.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks the user requested are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

Если Haiku отвечает "ok": false — Claude продолжает работать, используя reason как следующую инструкцию. Это как автоматический менеджер, который не даёт агенту уйти, пока задача не закрыта.

9. Agent-based hooks — верификация с доступом к файлам

Ещё мощнее. type: "agent" запускает полноценного субагента, который может читать файлы, запускать команды и принимать решения на основе реального состояния кодовой базы.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Run the test suite and verify all tests pass. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Субагент получает до 50 tool-вызовов и дефолтный таймаут 60 секунд. Если тесты красные — Claude не остановится, пока не починит.

10. HTTP hooks — интеграция с внешними сервисами

Когда нужно отправить данные на сервер, а не запускать локальный скрипт:

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

Сервер получает тот же JSON, что и command-hook на stdin. Ответ — в том же формате. Подходит для централизованного аудита, CI/CD интеграций и корпоративных дашбордов.

Подводные камни

Stop hook → бесконечный цикл. Самая частая ошибка. Stop hook блокирует остановку (exit 2), Claude пытается остановиться снова, hook снова блокирует — и так навечно. Согласно GitHub issue #10205, это ломает даже GitHub Actions. Решение — проверять флаг stop_hook_active:

INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Позволяем Claude остановиться
fi

Shell-профиль ломает JSON. Если .zshrc или .bashrc выводит что-то при старте (приветствие, conda activation), этот текст попадает перед JSON-ответом hook'а, и парсинг ломается. Оберни вывод в проверку if [[ $- == *i* ]]; then echo "Welcome"; fi — это выполнится только в интерактивном шелле, а hooks работают в неинтерактивном.

Matchers чувствительны к регистру. bash не сматчит инструмент Bash. Имена инструментов в Claude Code — PascalCase: Bash, Edit, Write, Read, Glob, Grep. MCP-инструменты следуют формату mcp__server__tool.

exit 2 + JSON на stdout = JSON проигнорирован. Если хочешь вернуть структурированный ответ — используй exit 0 с JSON. Если хочешь просто заблокировать — exit 2 и причину в stderr. Смешивать нельзя.

PostToolUse не отменяет действие. Hook срабатывает после выполнения инструмента. Если Claude уже записал файл — поздно блокировать. Для предотвращения используй PreToolUse.

Ручные правки settings.json не подхватываются на лету. Если редактируешь файл конфигурации во время работы Claude Code, нужно либо зайти в /hooks меню, либо перезапустить сессию. Hooks, добавленные через /hooks CLI, подхватываются мгновенно.

Вердикт

Из 10 автоматизаций три дают максимальный эффект при минимальных усилиях: авто-формат (убирает 100% споров о стиле кода), защита файлов (ни одного случайного изменения .env за всю сессию) и уведомления (не надо сидеть и ждать Claude). Это 15 минут настройки, которые окупаются в первый же день.

Prompt-based и agent-based hooks — мощное оружие, но с оговорками: они тратят дополнительные токены и могут замедлить работу. Имеет смысл включать их на CI/CD или для длинных автономных сессий, где цена ошибки высока.

Stop hook с проверкой тестов звучит красиво в теории, но на практике — главный источник бесконечных циклов. Используй осторожно и всегда проверяй stop_hook_active.

Как попробовать

  1. Установи jq — почти все hook-скрипты используют его для парсинга JSON: brew install jq (macOS) или apt-get install jq (Linux)

  2. Начни с уведомлений — набери /hooks в Claude Code, выбери Notification, вставь osascript команду. Проверь, что уведомление приходит

  3. Добавь авто-формат — скопируй JSON из раздела #1 в .claude/settings.json проекта. Убедись, что Prettier установлен: npm install --save-dev prettier

  4. Добавь защиту файлов — создай скрипт из раздела #2, сделай chmod +x, зарегистрируй в settings.json

  5. Включи отладку — нажми Ctrl+O для verbose mode, чтобы видеть вывод hooks в транскрипте. Для полной диагностики: claude --debug

Полная документация: code.claude.com/docs/en/hooks-guide | Справочник всех событий: code.claude.com/docs/en/hooks

$ ls ./related/

Похожие статьи

subscribe.sh

$ cat /dev/blog/updates

> Свежие заметки о программировании,

> DevOps и AI — прямо в мессенджер

./subscribe