Руководства
Hooks в Claude Code: как настроить, какие события выбрать и готовые примеры
Hooks — это пользовательские команды оболочки, которые Claude Code запускает в определённые моменты своего жизненного цикла. Выполняет hook не модель, а значит, действие происходит всегда. Эта страница — практическое руководство: куда поместить hook, как выглядит запись hook, какие события важнее всего, четыре примера, которые можно вставить, как коды выхода блокируют действие и что проверить, если hook ничего не делает. Она следует документам Anthropic Automate actions with hooks и Hooks reference, сверенным с обеими страницами 30 октября 2026 г.
- Hook — это блок
hooksв файле настроек: выберите событие, добавьте matcher для фильтрации и опишите обработчик, который запускается. Поместите его в~/.claude/settings.jsonдля всех ваших проектов или в.claude/settings.json, чтобы делиться им с репозиторием. - Введите
/hooksв Claude Code, чтобы убедиться, что hook зарегистрирован под нужным событием. - Выход 0 означает успех; выход 2 блокирует действие на событиях, которые умеют блокировать, а ваш stderr становится причиной. Любой другой код выхода — неблокирующая ошибка.
- Hooks типа command выполняют команды оболочки с вашими полными правами пользователя, поэтому проверяйте и тестируйте каждую команду hook перед добавлением.
Что такое hooks и когда их использовать
Hooks — это пользовательские команды оболочки. Claude Code запускает их в определённые моменты жизненного цикла, что даёт детерминированный контроль: некоторые действия происходят всегда, а не зависят от того, решит ли LLM их выполнить. Используйте hooks, чтобы обеспечивать соблюдение правил проекта, автоматизировать повторяющиеся задачи и интегрировать Claude Code с уже имеющимися инструментами.
Среди примеров Anthropic: форматирование файлов после правок, блокировка команд до их выполнения, уведомления, когда Claude нужен ваш ответ, и добавление контекста при старте сессии. Для решений, требующих суждения, а не детерминированных правил, руководство указывает на hooks на основе prompt и на основе агента, которые используют модель Claude для оценки условий. Для добавления контекста при каждом старте сессии руководство вместо этого предлагает CLAUDE.md. Чтобы дать Claude дополнительные инструкции и исполняемые команды, см. skills.
Безопасность: hooks типа command выполняют команды оболочки с вашими полными правами пользователя. Они могут изменять, удалять или читать любые файлы, доступные вашей учётной записи. Проверяйте и тестируйте все команды hooks, прежде чем добавлять их в конфигурацию.
Где настраиваются hooks
Hooks задаются в JSON-файлах настроек. Место, куда вы добавляете hook, определяет его область действия:
| Расположение | Область | Можно делиться |
|---|---|---|
~/.claude/settings.json | Все ваши проекты | Нет, остаётся на вашей машине |
.claude/settings.json | Один проект | Да, можно коммитить в репозиторий |
.claude/settings.local.json | Один проект | Нет, добавляется в gitignore, когда Claude Code сохраняет в него настройку |
| Managed policy settings | Вся организация | Да, управляется администратором |
Плагин hooks/hooks.json | Пока плагин включён | Да, поставляется вместе с плагином |
| Frontmatter skill | Остаток сессии после вызова skill | Да, задаётся в файле skill |
| Frontmatter subagent | Пока работает этот subagent | Да, задаётся в файле subagent |
Введите /hooks в Claude Code, чтобы открыть браузер настроенных hooks только для чтения. Список помечает каждый hook источником: пользовательские настройки, настройки проекта, локальные настройки, плагин или текущая сессия. Чтобы отключить hooks, задайте "disableAllHooks": true в файле настроек. Если вы правите файлы настроек напрямую, пока Claude Code запущен, наблюдатель за файлами обычно подхватывает изменения hooks автоматически.
В интерактивной сессии Claude Code не запускает hooks ни из одного файла настроек, включая ваш собственный ~/.claude/settings.json, пока вы не примете диалог доверия рабочему пространству для папки или для родительского каталога, доверие к которому распространяется на неё.
Структура записи hook
У конфигурации три уровня вложенности: выберите событие hook, на которое реагировать, например PreToolUse или Stop; добавьте группу matcher, чтобы фильтровать, когда оно срабатывает, например «только для инструмента Bash»; затем опишите один или несколько обработчиков hook, запускаемых при совпадении. Имя каждого события — ключ внутри единственного объекта hooks. Вот первый hook из руководства Anthropic — уведомление на рабочем столе в macOS:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}
Если в вашем файле настроек уже есть ключ hooks, добавьте новое событие как соседа существующих ключей событий, а не заменяйте весь объект.
Каждый объект во внутреннем массиве hooks — это обработчик hook. Большинство hooks используют "type": "command", который запускает команду оболочки. Доступны ещё четыре типа: "type": "http", "type": "mcp_tool", "type": "prompt" и "type": "agent". Hooks типа agent экспериментальные и могут измениться. Общие поля обработчика:
| Поле | Обязательно | Что делает |
|---|---|---|
type | да | "command", "http", "mcp_tool", "prompt" или "agent" |
if | нет | Синтаксис правил разрешений для фильтрации, когда этот hook запускается, например "Bash(git *)" или "Edit(*.ts)". Вычисляется только на событиях инструментов: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest и PermissionDenied. На других событиях hook с заданным if не запускается никогда |
timeout | нет | Секунды до отмены. По умолчанию: 600 для command, http и mcp_tool; 30 для prompt; 60 для agent |
statusMessage | нет | Собственное сообщение индикатора, показываемое, пока hook выполняется |
Matchers
Без matcher hook срабатывает при каждом появлении своего события. Поле matcher фильтрует, когда срабатывают hooks, а то, как оно вычисляется, зависит от символов в нём:
| Значение matcher | Как вычисляется | Пример |
|---|---|---|
"*", "" или опущено | Совпадает со всем | срабатывает при каждом появлении события |
Только буквы, цифры, _, -, пробелы, , и | | Точная строка или список точных строк, разделённых | или ,, с возможными пробелами вокруг | Bash совпадает только с инструментом Bash; Edit|Write и Edit, Write каждый точно совпадают с любым из двух инструментов |
| Содержит любой другой символ | Регулярное выражение JavaScript без привязки к началу | ^Notebook совпадает с любым инструментом, имя которого начинается с Notebook; mcp__memory__.* совпадает с каждым инструментом сервера memory |
Каждый тип события сопоставляется по своему полю: для PreToolUse и PostToolUse это имя инструмента, для SessionStart — способ запуска сессии (startup, resume, clear, compact, fork), а для Notification — тип уведомления. Matchers чувствительны к регистру. UserPromptSubmit, Stop и ещё несколько событий не поддерживают matcher и срабатывают при каждом появлении.
Поле if идёт дальше, чем matcher, который фильтрует на уровне группы и только по имени инструмента. Этот hook запускается, только когда Claude использует команды git, а не все команды Bash:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
}
]
}
]
}
}
Поскольку этот фильтр работает по принципу «насколько получится», руководство советует для жёсткого разрешения или запрета использовать систему разрешений, а не hook.
События, которые понадобятся чаще всего
Когда событие срабатывает, Claude Code запускает подходящие hooks параллельно. В руководстве перечислено гораздо больше событий; здесь те, на которые опираются примеры на этой странице, и ещё несколько распространённых.
| Событие | Когда срабатывает | Может блокировать? |
|---|---|---|
SessionStart | Когда сессия начинается или возобновляется | Нет. Выход 2 показывает stderr только пользователю |
UserPromptSubmit | Когда отправлен prompt, до того как Claude его обработает | Да, блокирует prompt |
PreToolUse | Перед выполнением вызова инструмента | Да, блокирует вызов инструмента |
PermissionRequest | Когда вызову инструмента нужно решение о разрешении | Нет. Код выхода 2 не учитывается; отклоняйте через объект decision |
PostToolUse | После успешного вызова инструмента | Нет. Показывает stderr Claude; инструмент уже выполнился |
Notification | Когда Claude Code отправляет уведомление | Нет. Код выхода и stderr игнорируются |
Stop | Когда Claude заканчивает отвечать | Да, не даёт Claude остановиться и продолжает разговор |
ConfigChange | Когда файл конфигурации меняется во время сессии | Да, блокирует изменение (кроме policy_settings) |
CwdChanged | Когда меняется рабочий каталог, например когда Claude выполняет команду cd | Нет. Показывает stderr только пользователю |
PreCompact | Перед сжатием контекста | Да, блокирует сжатие |
SessionEnd | Когда сессия завершается | Нет. Показывает stderr только пользователю |
Четыре примера для вставки
Каждый — готовый к использованию блок конфигурации из руководства Anthropic. Примеры на Bash используют jq для разбора JSON: установите его через brew install jq на macOS, apt-get install jq на Debian и Ubuntu или см. страницу загрузок jq.
Уведомление, когда Claude нужен ваш ответ
Этот hook использует событие Notification, которое Claude Code вызывает, когда Claude ждёт ввода или разрешения. Версия для macOS приведена в разделе выше; здесь — Linux, где используется notify-send. Добавьте в ~/.claude/settings.json:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}
Пустой matcher срабатывает на все типы уведомлений. Чтобы срабатывать только на определённые, задайте один из типов уведомлений, например permission_prompt (Claude нужно, чтобы вы одобрили использование инструмента) или idle_prompt (Claude закончил отвечать около 60 секунд назад, а вы с тех пор ничего не набирали). Если уведомление на macOS не появляется: osascript направляет уведомления через встроенное приложение Script Editor, и если у Script Editor нет разрешения на уведомления, команда молча завершается неудачей. В Linux notify-send нужен демон уведомлений рабочего стола, которого нет на серверах без графики, в сессиях SSH и в большинстве контейнеров.
Автоформатирование кода после правок
Этот hook использует событие PostToolUse с matcher Edit|Write, поэтому запускается только после инструментов правки файлов. Команда извлекает путь отредактированного файла через jq и передаёт его Prettier. Добавьте в .claude/settings.json в корне проекта:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
Чтобы проверить, попросите Claude добавить в файл JavaScript строку со строками в одинарных кавычках, а затем откройте файл: при настройках Prettier по умолчанию hook перепишет их в двойные кавычки. Когда hook успешен, Claude Code ничего не показывает в разговоре, поэтому проверьте, что отредактированный файл переформатирован. Команда Bash тоже может переписывать файлы; чтобы переформатировать конкретный файл, как бы он ни менялся, используйте вместо этого hook FileChanged.
Блокировка правок защищённых файлов
Это не даёт Claude изменять чувствительные файлы, такие как .env, package-lock.json или что-либо в .git/. Claude получает обратную связь с объяснением, почему правка заблокирована, и может скорректировать подход. Hook вызывает скрипт, который сверяет путь целевого файла со списком защищённых шаблонов и завершается с кодом 2, чтобы заблокировать правку. Сохраните скрипт в .claude/hooks/protect-files.sh:
#!/bin/bash
# protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Normalize Windows backslash separators so the patterns below match
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
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
Скрипты hooks должны быть исполняемыми, иначе Claude Code не сможет их запустить. На macOS и Linux:
chmod +x .claude/hooks/protect-files.sh
Затем добавьте в .claude/settings.json hook PreToolUse, который запускает скрипт перед любым вызовом инструмента Edit или Write:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
Чтобы проверить, попросите Claude добавить комментарий в ваш файл .env. Claude Code заблокирует правку до выполнения и передаст сообщение скрипта Blocked: Claude как обратную связь.
Повторное добавление контекста после сжатия
Когда контекстное окно Claude заполняется, сжатие пересказывает разговор, освобождая место, и важные детали при этом могут потеряться. Используйте hook SessionStart с matcher compact, чтобы после каждого сжатия заново добавлять критически важный контекст. Claude Code добавляет в контекст Claude простой текст, который ваша команда пишет в stdout. Добавьте это в .claude/settings.json в корне проекта:
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
}
]
}
]
}
}
Вместо echo можно подставить любую команду с динамическим выводом, например git log --oneline -5, чтобы показать последние коммиты.
Коды выхода и как работает блокировка
Hooks взаимодействуют с Claude Code через stdin, stdout, stderr и коды выхода. Когда событие срабатывает, Claude Code передаёт данные, специфичные для события, в виде JSON на stdin вашего скрипта. Для команды Bash hook PreToolUse получает поля вроде session_id, cwd, hook_event_name, tool_name и tool_input, где tool_input.command содержит команду оболочки. Этот скрипт блокирует команду:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "drop table"; then
echo "Blocked: dropping tables is not allowed" >&2 # stderr becomes Claude's feedback
exit 2 # exit 2 = block the action
fi
exit 0 # exit 0 = no decision; the normal permission flow applies
- Выход 0 означает успех. Если hook
PreToolUseзавершился с 0, вызов инструмента всё равно проходит обычный процесс разрешений, потому что выход 0 его не одобряет. Для hooksUserPromptSubmit,UserPromptExpansion,SessionStartиPostModelSwitchClaude Code добавляет простой текст из stdout в контекст Claude. Для большинства других событий stdout попадает в отладочный лог и не показывается в транскрипте. - Выход 2 блокирует действие на событиях, которые умеют блокировать. Запишите причину в stderr: сообщение, сопровождающее блокировку, — это stderr вашего hook. В зависимости от события Claude получает его как обратную связь, чтобы скорректироваться, его видит пользователь или сообщение не появляется вовсе. Некоторые события блокировать нельзя: для
SessionStartи других выход 2 показывает stderr пользователю, а выполнение продолжается. - Любой другой код выхода — неблокирующая ошибка: действие продолжается, кроме hooks, сбой которых блокирует действие, например hook с
onFailure, равным"block". На событиях вродеPreToolUseв транскрипте вы увидите уведомление<hook name> hook error.
Claude никогда не видит stderr hook, завершившегося с 0. Чтобы показать Claude предупреждение из hook PostToolUse, завершитесь с 2: тогда Claude увидит stderr, хотя инструмент уже выполнился. Hook PostToolUse не может отменить действие, так как инструмент уже выполнен.
Коды выхода позволяют только блокировать или молчать. Для более тонкого управления завершитесь с 0 и выведите объект JSON в stdout. Выбирайте один подход на hook: выход 2, чтобы заблокировать с сообщением в stderr, или выход 0 с JSON для структурированного управления. Например, hook PreToolUse может отклонить вызов инструмента и объяснить Claude почему:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep for better performance"
}
}
При "deny" Claude Code отменяет вызов инструмента и возвращает permissionDecisionReason Claude. Hook, возвращающий permissionDecision: "deny", блокирует инструмент даже в режиме bypassPermissions или с --dangerously-skip-permissions. Когда одному событию соответствуют несколько hooks, команда каждого hook выполняется до конца, прежде чем Claude Code объединит результаты, так что один hook, вернувший deny, не мешает выполняться соседним.
Как отладить hook
- Выполните
/hooksи убедитесь, что hook отображается под нужным событием. - Проверьте скрипт, подав в него пример JSON через pipe, затем проверьте код выхода (команды ниже).
- Нажмите
Ctrl+O, чтобы открыть просмотр транскрипта, и найдите результат работы hook. Успех ничего не показывает, если JSON hook ничего не выводит. Блокирующая ошибка показывает сообщение, сопровождающее блокировку. Неблокирующая ошибка показывает уведомление<hook name> hook errorс кратким пояснением. - Полные сведения о выполнении, включая коды выхода, stdout и stderr hooks, — в отладочном логе. Запустите Claude Code как
claude --debug-file /tmp/claude.log, чтобы писать по известному пути, а затем выполнитеtail -f /tmp/claude.logв другом терминале. Если вы запустили без этого флага, выполните/debugпосреди сессии, чтобы включить логирование и узнать путь к логу.
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $? # Check the exit code
claude --debug-file /tmp/claude.log
tail -f /tmp/claude.log
Устранение неполадок
| Симптом | Что проверить |
|---|---|
| Hook никогда не срабатывает | Выполните /hooks и убедитесь, что он отображается под нужным событием. Проверьте, что шаблон matcher в точности совпадает с именем инструмента; matchers чувствительны к регистру. Убедитесь, что вы вызываете нужный тип события: PreToolUse срабатывает до выполнения инструмента, PostToolUse — после. |
/hooks не показывает hooks | Если они не появились спустя несколько секунд, перезапустите сессию, чтобы принудительно перезагрузить. Проверьте, что JSON корректен: завершающие запятые и комментарии не допускаются. Убедитесь, что файл лежит где нужно: .claude/settings.json для hooks проекта, ~/.claude/settings.json для глобальных. |
| «PreToolUse hook error: ...» в транскрипте | Ваш скрипт неожиданно завершился с ненулевым кодом. Проверьте его вручную, подав пример JSON через pipe. Если скрипт вообще не запускается, сделайте его исполняемым: chmod +x ./my-hook.sh. |
| «command not found» | Используйте абсолютные пути или ${CLAUDE_PROJECT_DIR} для ссылок на скрипты. |
| «jq: command not found» | Установите jq или используйте Python/Node.js для разбора JSON. |
| Hook печатает JSON, но ничего не происходит | Что-то другое может писать в stdout раньше, обычно безусловный echo в профиле вашей оболочки, из-за чего вывод больше не начинается с {. Оберните эти echo так, чтобы они выполнялись только в интерактивных оболочках. Либо поле находится не на том уровне: permissionDecision относится внутрь hookSpecificOutput, а не на верхний уровень. Запустите Claude Code как claude --debug и найдите в отладочном логе Hook output does not start with { или Hook JSON output had unrecognized keys. |
| Claude продолжает работать, а предупреждение говорит, что hook Stop блокировал слишком много раз | Claude Code переопределяет hook Stop после того, как он заблокировал восемь раз подряд без вызова инструмента от Claude между ними. Разберите поле stop_hook_active из входного JSON и выходите раньше, если оно true. |
Проверка stop_hook_active выглядит так:
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Allow Claude to stop
fi
# ... rest of your hook logic
FAQ
Когда использовать hook вместо инструкции в CLAUDE.md?
Используйте hook, когда действие должно происходить всегда. Hook — это команда оболочки, которую Claude Code запускает в определённый момент, так что она не зависит от того, решит ли LLM её выполнить. Для добавления контекста при каждом старте сессии руководство Anthropic вместо этого предлагает CLAUDE.md; hook SessionStart с matcher compact — пример повторного добавления контекста после сжатия.
В какой файл настроек поместить hook?
Используйте ~/.claude/settings.json для hook, который действует во всех ваших проектах и остаётся на вашей машине. Используйте .claude/settings.json для одного проекта; его можно коммитить в репозиторий. Используйте .claude/settings.local.json для одного проекта без публикации. Записи hooks объединяются между уровнями настроек, а не заменяют друг друга.
Может ли hook что-то заблокировать даже в режиме bypass?
Да. Hook PreToolUse, возвращающий permissionDecision: "deny", блокирует инструмент даже в режиме bypassPermissions или с --dangerously-skip-permissions, потому что hooks PreToolUse срабатывают раньше любой проверки режима разрешений. Hook, возвращающий "allow", не обходит правила deny из настроек.
Источники
- Claude Code: Automate actions with hooks (первый hook, примеры, matchers, устранение неполадок; проверено 30 октября 2026 г.)
- Claude Code: Hooks reference (расположение hooks, поля обработчика, коды выхода, отладка, соображения безопасности; проверено 30 октября 2026 г.)