Хуки: форматирование, запреты, уведомления
CLAUDE.md — это пожелания. Модель обычно им следует, но не всегда. Когда действие должно происходить всегда, без исключений — форматирование после каждой правки, запрет на опасную команду, уведомление, когда агент ждёт вас, — нужен механизм, который не зависит от решения модели. Это хуки: команды оболочки, привязанные к событиям жизненного цикла Claude Code.
Что такое хук
Хук — это запись в файле настроек: событие, фильтр и команда. Когда событие наступает, Claude Code запускает команду, передаёт ей JSON с подробностями на стандартный ввод и смотрит на код выхода и вывод. В зависимости от них он продолжает, блокирует действие или передаёт модели дополнительный контекст.
Ключевое свойство — детерминированность. Документация формулирует так: инструкции в CLAUDE.md консультативны, хуки гарантируют, что действие произойдёт. Отсюда правило выбора: если что-то должно случиться в конкретный момент и без исключений — это хук.
События
Полный список событий длинный, для начала достаточно нескольких.
PreToolUse— перед вызовом инструмента. Единственное из этих событий, которое может заблокировать действие.PostToolUse— после успешного вызова инструмента. Инструмент уже отработал, блокировать нечего, но можно, например, отформатировать файл.Notification— когда Claude Code отправляет уведомление: ждёт разрешения или ввода.Stop— когда Claude закончил ответ.SubagentStop— когда закончил субагент.UserPromptSubmit— когда вы отправили сообщение, до того как его увидела модель.SessionStart,SessionEnd— начало и конец сессии;PreCompact— перед сжатием контекста.
Для событий инструментов фильтр (matcher) — имя инструмента: Bash, Edit|Write, mcp__github__.*. Пустой фильтр или * — на все вызовы. Для Notification фильтр — тип уведомления, для SessionStart — способ старта (startup, resume, clear, compact).
Где хранятся
Хуки живут в hooks внутри файлов настроек, и место определяет область:
~/.claude/settings.json— все ваши проекты..claude/settings.json— этот проект, в git, для команды..claude/settings.local.json— этот проект, только вы.
Команда /hooks открывает список настроенных хуков по событиям с указанием источника. Список только для чтения: править нужно JSON. Можно попросить Claude: «напиши хук, который запускает eslint после каждой правки файла» — он умеет.
Структура записи трёхуровневая: событие → список фильтров → список команд:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}Пример 1: форматирование после правки
Именно этот хук выше и решает задачу. Событие PostToolUse, фильтр Edit|Write — только после правок файлов. Команда читает JSON со стандартного ввода, достаёт tool_input.file_path через jq и передаёт файл Prettier. Каждый файл, который агент тронул, форматируется сразу, и в diff не попадает шум от отступов.
Для Python-проекта команда будет jq -r '.tool_input.file_path' | xargs uv run ruff format, для Flutter — xargs dart format, для PHP в проекте на Bitrix — xargs vendor/bin/php-cs-fixer fix. Если форматтер должен применяться только к определённым файлам, добавьте условие: поле if с правилом вида "Edit(*.ts)" ограничит хук нужными расширениями.
Хук стоит положить в .claude/settings.json проекта: тогда форматирование одинаково у всей команды.
Пример 2: запрет опасных команд
PreToolUse может остановить действие двумя способами. Первый — код выхода 2: действие блокируется, а текст из стандартного потока ошибок передаётся модели как объяснение. Второй — JSON на стандартном выводе с решением deny, allow или ask. Скрипт из документации, блокирующий rm -rf:
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0
fiСохраните его как .claude/hooks/block-rm.sh, сделайте исполняемым (chmod +x) и подключите:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
}
]
}
]
}
}${CLAUDE_PROJECT_DIR} — корень проекта, откуда запущена сессия; так путь не зависит от текущей папки. Код выхода 0 без JSON означает «решения нет, действует обычная проверка прав».
Тот же приём защищает файлы. Скрипт из документации сравнивает tool_input.file_path со списком (.env, package-lock.json, .git/), пишет причину в поток ошибок и выходит с кодом 2 — правка блокируется, а агент получает объяснение и меняет подход. Для наших проектов в список просятся папки миграций и legacy/, которые нельзя трогать без согласования.
Важно: хук не обходит правила прав. Если в permissions.deny есть запрет, он сработает независимо от хука; хук, вернувший allow, не отменит правило ask. И наоборот: для простых запретов вроде «не читать .env» правило deny короче хука. Хук нужен, когда условие сложнее, чем шаблон команды.
Пример 3: уведомление, когда агент ждёт
Длинная задача идёт минут десять, вы переключились на другое окно, а агент три минуты назад остановился с вопросом. Хук на Notification решает это. Для macOS:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}На Linux вместо osascript — notify-send 'Claude Code' 'Claude Code needs your attention'. На macOS есть тонкость: osascript шлёт уведомления через Script Editor, и если ему не разрешены уведомления, команда молча ничего не покажет. Один раз запустите её в терминале, потом включите уведомления для Script Editor в системных настройках.
Этот хук уместен в ~/.claude/settings.json: он про ваше рабочее место, а не про проект. Аналогично можно повесить уведомление на Stop — «агент закончил».
Что получает и возвращает хук
На стандартный ввод приходит JSON с общими полями — session_id, cwd, hook_event_name, permission_mode — и полями события. Для инструментов это tool_name и tool_input (для Bash — command, для Edit — file_path).
Коды выхода:
- 0 — успех. Если на выводе JSON, он разбирается; для большинства событий обычный текст уходит только в отладочный лог.
- 2 — блокирующая ошибка. Действие останавливается на событиях, которые это поддерживают (
PreToolUse,UserPromptSubmit,Stopи других), текст из потока ошибок передаётся модели. НаPostToolUseблокировать нечего, но текст модель всё равно увидит. - Другие — неблокирующая ошибка: действие продолжается, первая строка ошибки показывается в разговоре.
По умолчанию хук ждут 600 секунд, затем отменяют; для отдельных событий таймаут короче. Поле timeout задаёт свой предел. Все подходящие хуки одного события запускаются параллельно; из нескольких решений PreToolUse побеждает самое строгое.
Что ещё можно
Хук на Stop может запускать вашу проверку и блокировать завершение хода, пока она не пройдёт: агент вынужден дорабатывать, пока тесты не позеленеют (после восьми блокировок подряд Claude Code всё же завершит ход). Хук на PreToolUse для Bash может переписать команду: документация приводит пример, который добавляет к npm test фильтр, оставляющий в выводе только падения, — экономия контекста в разы. Хук на SessionStart с фильтром compact может заново подложить важный контекст после сжатия. Кроме команд оболочки есть хуки типа prompt, где условие оценивает модель, и HTTP-хуки. Всё это описано в справочнике.
Отключить все хуки на время — "disableAllHooks": true в настройках. Хуки из проектных настроек срабатывают только после того, как вы подтвердили доверие к папке, — репозиторий не может запустить у вас команду без спроса.
Попробуйте сами
10–15 мин на рабочем месте- Настройте хук уведомления для своей системы в
~/.claude/settings.json. Проверьте через/hooks, что он зарегистрирован, затем дайте агенту задачу в ручном режиме и переключитесь в другое окно. - Добавьте в проект хук форматирования под ваш стек. Попросите агента внести правку и убедитесь по
git diff, что файл отформатирован. - Напишите скрипт защиты файлов для папок, которые в вашем проекте нельзя трогать без согласования. Попросите агента что-нибудь туда добавить и посмотрите, как он отреагирует на блокировку.
Коротко
- Хук — команда оболочки на событие цикла; срабатывает всегда, в отличие от инструкций в CLAUDE.md.
- Основные события:
PreToolUse(может блокировать),PostToolUse,Notification,Stop,SessionStart. - Формат в
settings.json: событие →matcher→ список команд;/hooksпоказывает настроенное. - Код выхода 2 или JSON с
permissionDecision: "deny"блокирует действие и объясняет модели причину. - Три базовых сценария: форматирование после
Edit|Write, запрет команд наPreToolUse, уведомление наNotification. - Хуки не обходят правила прав; для простых запретов правило
denyкороче.
Видеоверсия
Сценарий озвучки · 612 слов, ≈ 5 мин
Файл клод-эм-дэ — это пожелания. Модель обычно им следует, но не всегда. Когда действие должно происходить всегда — форматирование после каждой правки, запрет опасной команды, уведомление, когда агент ждёт вас, — нужен механизм, который не зависит от решения модели. Это хуки.
Хук — это запись в файле настроек: событие, фильтр и команда. Когда событие наступает, Claude Code запускает команду, передаёт ей джейсон с подробностями и смотрит на код выхода и вывод. В зависимости от них он продолжает, блокирует действие или передаёт модели дополнительный контекст. Главное свойство — детерминированность. Инструкции в файле консультативны, хук гарантирует.
Событий много, для начала хватит нескольких. «Пре-тул-юз» — перед вызовом инструмента; единственное из базовых, которое может заблокировать действие. «Пост-тул-юз» — после успешного вызова. «Нотификейшн» — когда Claude Code ждёт разрешения или ввода. «Стоп» — когда Claude закончил ответ. И «сешн-старт» — начало сессии. Фильтр для событий инструментов — имя инструмента: «баш», «эдит», «райт».
Хуки хранятся в файлах настроек. В домашней папке — для всех ваших проектов. В папке точка-клод проекта — для команды, через git. В локальном файле — только для вас. Команда «хукс» в сессии показывает всё настроенное с указанием источника. Править нужно джейсон, но можно попросить Claude — он умеет писать хуки.
Первый пример — форматирование после правки. Событие «пост-тул-юз», фильтр — правка или запись файла. Команда читает джейсон со стандартного ввода, достаёт путь к файлу через утилиту jq и передаёт его Prettier. Каждый файл, который агент тронул, форматируется сразу, и в дифф не попадает шум от отступов. Для Python вместо Prettier — ruff, для Flutter — dart format, для PHP — php-cs-fixer. Такой хук стоит положить в настройки проекта, чтобы форматирование было одинаковым у всей команды.
Второй пример — запрет опасных команд. Хук на «пре-тул-юз» может остановить действие двумя способами: кодом выхода два, тогда текст из потока ошибок уходит модели как объяснение, или джейсоном с решением «запретить». Скрипт из документации проверяет команду на «rm -rf» и, если нашёл, возвращает запрет с причиной. Скрипт лежит в папке хуков проекта, путь к нему задаётся через переменную корня проекта. Тот же приём защищает файлы: скрипт сравнивает путь со списком — файл с секретами, lock-файл, папка git — и выходит с кодом два. Агент получает объяснение и меняет подход. Для наших проектов в список просятся миграции и папка legacy.
Важно: хук не обходит правила прав. Запрет в правах сработает независимо от хука. И для простых запретов правило короче: «не читать файл с секретами» — это одна строка в правах, а не скрипт. Хук нужен, когда условие сложнее шаблона команды.
Третий пример — уведомление. Задача идёт десять минут, вы ушли в другое окно, а агент три минуты назад остановился с вопросом. Хук на «нотификейшн» с пустым фильтром запускает системное уведомление: на Mac через osascript, на Linux через notify-send. На Mac есть тонкость: уведомления идут через Script Editor, и если ему не разрешены уведомления, ничего не появится. Один раз запустите команду в терминале и включите уведомления в системных настройках. Этот хук — в личные настройки: он про ваше рабочее место.
Что получает хук? Джейсон с идентификатором сессии, рабочей папкой, именем события и полями события: для инструментов — имя инструмента и его вход. Что возвращает? Код ноль — успех. Код два — блокировка с объяснением. Другие коды — неблокирующая ошибка, действие продолжается. По умолчанию хук ждут десять минут. Все подходящие хуки запускаются параллельно, и из нескольких решений побеждает самое строгое.
Что ещё можно? Хук на «стоп» может запускать вашу проверку и не отпускать агента, пока тесты не позеленеют. Хук перед командой может переписать её: например, оставить в выводе тестов только падения — экономия контекста в разы. Хук на старт сессии после сжатия может подложить важный контекст заново. Есть хуки, где условие оценивает модель, и хуки по эйч-ти-ти-пи. И важная защита: хуки из настроек проекта срабатывают только после того, как вы подтвердили доверие к папке. Репозиторий не запустит у вас команду без спроса.
В последнем уроке подведём итоги курса и наметим, куда двигаться дальше.
