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.
Как попробовать
-
Установи jq — почти все hook-скрипты используют его для парсинга JSON:
brew install jq(macOS) илиapt-get install jq(Linux) -
Начни с уведомлений — набери
/hooksв Claude Code, выбериNotification, вставьosascriptкоманду. Проверь, что уведомление приходит -
Добавь авто-формат — скопируй JSON из раздела #1 в
.claude/settings.jsonпроекта. Убедись, что Prettier установлен:npm install --save-dev prettier -
Добавь защиту файлов — создай скрипт из раздела #2, сделай
chmod +x, зарегистрируй в settings.json -
Включи отладку — нажми
Ctrl+Oдля verbose mode, чтобы видеть вывод hooks в транскрипте. Для полной диагностики:claude --debug
Полная документация: code.claude.com/docs/en/hooks-guide | Справочник всех событий: code.claude.com/docs/en/hooks