CoreCodeБлог

Руководства

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

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

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 из настроек.

Источники