Хуки Claude Code: запуск скриптов до и после действий агента
Хуки запускают ваши скрипты на событиях Claude Code: форматируют код после правок, блокируют опасные команды, шлют уведомления. Разбираем схему настройки по официальной документации.
6 мин · · разработка · автоматизация
Хуки Claude Code нужны, когда агенту мало просьбы «не забудь прогнать форматтер». Хук запускается сам на заданном событии, например перед вызовом инструмента или после правки файла. Ниже разобраны схема настройки, коды выхода и три рабочих примера. Всё сверено с официальной документацией Claude Code (страницы Hooks reference и Automate actions with hooks).
Что такое хуки Claude Code
Хук — это обработчик, который Claude Code вызывает в определённой точке жизненного цикла сессии. Обработчик получает JSON с описанием события и может ничего не делать, выполнить побочное действие (отформатировать файл, отправить уведомление) или вернуть решение, например запретить вызов инструмента.
Основные события:
| Событие | Когда срабатывает |
|---|---|
PreToolUse | перед вызовом инструмента, может его заблокировать |
PostToolUse | после успешного вызова инструмента |
PostToolUseFailure | после неудачного вызова инструмента |
PermissionRequest | когда вызову нужно решение о разрешении |
UserPromptSubmit | когда вы отправили промпт, до его обработки |
Stop | когда Claude закончил ответ |
SubagentStart / SubagentStop | запуск и завершение субагента |
Notification | когда Claude Code шлёт уведомление |
SessionStart / SessionEnd | начало и конец сессии |
PreCompact / PostCompact | до и после сжатия контекста |
Отдельных событий «до правки файла» или «перед командой терминала» нет. Правка файла — это вызов инструмента Edit или Write, команда — вызов Bash (на Windows ещё PowerShell). Нужный инструмент выбирают через matcher в событиях PreToolUse и PostToolUse. Полный список событий (их больше трёх десятков) есть в справочнике хуков.
Где хранятся хуки и как устроен конфиг
Хуки описываются в JSON-файлах настроек:
~/.claude/settings.json— для всех ваших проектов (на Windows~означает%USERPROFILE%);.claude/settings.jsonв проекте — общий для команды, можно коммитить;.claude/settings.local.json— локальные настройки проекта, не для git.
В конфиге три уровня вложенности: событие, группа с matcher и список обработчиков:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}matcher — имя инструмента (Bash), список через | (Edit|Write) или регулярное выражение (mcp__.* для всех MCP-инструментов). Если матчер пустой, "*" или его нет, группа срабатывает на каждое событие этого типа.
Типы обработчиков (type):
command— shell-команда. JSON события приходит на stdin;http— POST-запрос с JSON события в теле;mcp_tool— вызов инструмента настроенного MCP-сервера;prompt— одноразовая проверка моделью Claude;agent— субагент, который может читать файлы перед решением (помечен как экспериментальный).
Общие необязательные поля: timeout в секундах (для command и http по умолчанию 600), statusMessage — текст спиннера, if — фильтр в синтаксисе правил разрешений, например "Bash(rm *)".
Что получает хук и как отвечает
Для PreToolUse с инструментом Bash на stdin приходит примерно такой JSON:
{
"session_id": "abc123",
"cwd": "/home/user/my-project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" }
}Для Edit и Write в tool_input есть file_path. Специальных переменных окружения с путём файла или текстом команды нет. Данные берут из JSON, обычно через jq. Скрипт может использовать переменную CLAUDE_PROJECT_DIR — корень проекта.
Коды выхода:
0— успех. Stdout в большинстве событий уходит в debug-лог, но если он начинается с{и заканчивается}, его разбирают как JSON с решением;2— блокирующая ошибка. ВPreToolUseвызов инструмента отменяется, текст из stderr получает Claude. ВPostToolUseдействие уже выполнено, stderr просто показывается Claude;- любой другой код (включая
1) — неблокирующая ошибка: действие продолжается, в транскрипте появляется уведомление об ошибке хука.
Это частая ловушка: exit 1 ничего не запрещает. Для запрета используйте exit 2 или JSON-решение.
Пример: форматирование после каждой правки
Конфиг выше — пример из официального гайда. После каждого Edit или Write команда достаёт путь файла из JSON и передаёт его Prettier. Для Python замените команду, например, на jq -r '.tool_input.file_path' | xargs ruff format. Нужны установленные jq и сам форматтер.
Пример: блокировка rm -rf
Сохраните скрипт в .claude/hooks/block-rm.sh и сделайте его исполняемым (chmod +x):
#!/bin/bash
command=$(jq -r '.tool_input.command')
if echo "$command" | grep -q 'rm -rf'; then
echo "Blocked: rm -rf is not allowed in this project" >&2
exit 2
fi
exit 0Подключите его:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm.sh" }
]
}
]
}
}Вместо exit 2 можно напечатать JSON с hookSpecificOutput.permissionDecision: "deny" и причиной в permissionDecisionReason. Документация подчёркивает: фильтрация в хуке работает по принципу «best effort». Для жёстких запретов используйте правила разрешений (permissions.deny), а хук — как дополнительный слой.
Пример: HTTP-хук
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/post-tool-use",
"timeout": 30,
"headers": { "Authorization": "Bearer $MY_TOKEN" },
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}Claude Code отправит JSON события POST-запросом. Переменные в заголовках подставляются только из списка allowedEnvVars. Так токен не приходится хранить в файле настроек.
Хуки и расход токенов при работе через Zukko
Хуки типа command и http выполняются локально и к модели не обращаются. Токены тратят только prompt и agent: они вызывают модель через тот же шлюз, что и основная сессия. Ещё одна косвенная статья расхода — тексты, которые хук возвращает Claude (stderr при exit 2, additionalContext). Они попадают в контекст, поэтому пишите их коротко.
Сам Claude Code подключается к Zukko переменными окружения. Их можно задать в shell или в блоке env того же settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.zukko.pro",
"ANTHROPIC_AUTH_TOKEN": "fetch_ваш_ключ",
"ANTHROPIC_MODEL": "claude-sonnet-5"
}
}Адрес указывается без /v1, Claude Code добавляет его сам. Цена claude-sonnet-5 в Zukko — $0.3 за 1M входных и $0.3 за 1M выходных токенов. Учтите оговорку про кэш: у вендора чтение из кэша промптов стоит около 10% входной цены, а в Zukko кэшированные токены оплачиваются по обычной входной цене. Для агента с большим повторяющимся контекстом реальная разница с официальным прайсом меньше, чем при прямом сравнении цен. Актуальный список моделей — GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете. Пошаговая настройка — в инструкции по Claude Code.
Отладка
/hooksвнутри Claude Code открывает список настроенных хуков (только для чтения) с указанием, из какого файла каждый взят.Ctrl+Oоткрывает транскрипт: там видны блокировки и ошибки хуков.claude --debug-file /tmp/claude.logпишет подробный лог: какие хуки совпали, коды выхода, stdout и stderr.- Временно отключить все хуки можно настройкой
"disableAllHooks": true.
Частые вопросы
Почему мой хук с exit 1 не блокирует команду?
Код 1 считается неблокирующей ошибкой: действие продолжается. Блокирует exit 2 или JSON с permissionDecision: "deny" в PreToolUse.
Как запустить хук только для определённых файлов?
Сузьте matcher до Edit|Write и проверяйте путь в скрипте: jq -r '.tool_input.file_path', затем условие на расширение. Ещё один вариант — поле if, например "Edit(*.ts)".
Работают ли хуки внутри субагентов?
Да. Хуки из файлов настроек срабатывают и на вызовы инструментов субагентов. Во входном JSON тогда есть поля agent_id и agent_type.
Сколько ждёт Claude Code, пока хук работает?
По умолчанию до 600 секунд для command и http, 30 секунд для prompt. Значение меняется полем timeout. Если хук PreToolUse превысил таймаут, вызов не блокируется и идёт по обычной схеме разрешений.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Перевод субтитров нейросетью: SRT без сбитых таймкодовГлавный риск при переводе SRT нейросетью — сломанная структура файла. Показываем схему, где таймкоды не попадают к модели, и скрипт на Python с проверкой.
- Редактирование текстов нейросетью: корректура, стиль и сокращениеРазные виды редактуры требуют разных промптов. Показываем формулировки для корректуры, стиля и сокращения, как получать правки с объяснением и не терять авторский голос.
- Нейросеть для написания скриптов: Bash, PowerShell и PythonМодель быстро пишет скрипты для рутины, но запускать их вслепую нельзя. Показываем, как ставить задачу, требовать безопасный режим dry-run и проверять результат до запуска.
- Dify в Docker: установка, первичная настройка и подключение моделейСтавим Dify через Docker Compose, создаём администратора и подключаем модели Claude, GPT и DeepSeek по одному ключу. Отдельно — что делать, если контейнеры не стартуют.