Skip to content

Хуки (Hooks): Автоматическое срабатывание триггера в нужный момент

📚 Навигация по серии: В предыдущей статье 32 Стили вывода (Output Styles) мы узнали, как сменить «личность», чтобы Claude работал с нужным вам тоном. В этой статье речь пойдет о другом виде «автоматизации» — не об изменении того, как он говорит, а о железном выполнении нужного вам действия в момент наступления определенного события: автоматическое форматирование после изменения файла, жесткая блокировка опасных команд или отправка уведомления по окончании работы. Это и есть Хуки (Hooks).

Представьте себе такую цифру: за одну неделю количество раз, когда вы вручную вводили prettier --write после того, как Claude изменил код, составило 23 раза.

23 раза. Одно и то же действие, механически повторенное 23 раза. Что еще хуже, пару раз вы забыли это сделать — в результате коммит не прошел проверку формата CI, и пришлось прогонять весь процесс заново.

В этот момент стоит задуматься: почему такое действие, которое «нужно делать каждый раз, и оно всегда одинаковое», должно зависеть от человеческой памяти или сознательности Claude? В CLAUDE.md было написано «после изменения файла не забудь запустить prettier», но в трети случаев он об этом забывал — потому что это всего лишь просьба, а не гарантия.

А если настроить Хук (Hook), одна строка конфигурации навсегда решает эту проблему: с тех пор каждый раз, когда Claude заканчивает редактировать файл, форматирование запускается автоматически. Вам больше ни разу не придется вводить prettier вручную, и CI больше не вернет ваш код. В этой статье мы подробно разберем этот механизм «автоматического триггера»: от того, что это такое, до того, как его настраивать и отлаживать.

Прочитав эту статью, вы узнаете:

  • В одном предложении: что такое Hook и в чем фундаментальное отличие от «просьбы, записанной в CLAUDE.md».
  • В какие «моменты» жизненного цикла работы Claude можно повесить хуки (PreToolUse / PostToolUse / Stop / SessionStart и т.д.).
  • В каком файле настраиваются хуки и как matcher сужает их область действия (например, «запускать только при редактировании файлов»).
  • Три реальных примера, которые можно сразу скопировать: автоматическое форматирование после правок, блокировка опасных команд, уведомление об окончании работы.
  • Как общаются хук и Claude (JSON через stdin, коды выхода, stdout) — это ключ к пониманию всего процесса.
  • Как шаг за шагом найти причину, если хук не срабатывает или выдает ошибку.

01 Сначала поймем: что такое Hook и в чем сила его «гарантии»

Сначала вывод: Hook — это «скрипт или запрос, который автоматически выполняется при наступлении определенного события». Он не зависит от того, решит ли Claude его выполнить — его запуск гарантирован. (Чаще всего используются shell-команды, но также поддерживаются HTTP-эндпоинты, инструменты MCP, промпты LLM и т.д.)

Официальное определение весьма лаконично, приведем его здесь:

Хуки — это определяемые пользователем shell-команды, которые выполняются в определенные моменты жизненного цикла Claude Code. Они обеспечивают детерминированный контроль над поведением Claude Code, гарантируя, что определенные действия всегда будут выполнены, вместо того чтобы полагаться на выбор LLM об их запуске.

Обратите внимание на два словосочетания: «детерминированный контроль» и «всегда будут выполнены». В этом и заключается суть Хуков.

Аналогия: правила автоматизации умного дома («если..., то...»). Вы наверняка настраивали такие правила: «Если кто-то открывает дверь, то включить свет», «Если я ухожу из дома, то выключить все розетки». Как только условие выполняется, действие происходит неизбежно, никто не должен об этом помнить. Hook — это такое же правило для Claude Code. Вы определяете: «Если происходит определенное событие, то выполнить эту команду», и это выполняется автоматически и безоговорочно.

Здесь нужно жестко закрепить одно важнейшее различие: написанное в CLAUDE.md — это «просьба» (он, скорее всего, её выполнит, но может и забыть); настроенное как Hook — это «гарантия» (как только срабатывает событие, действие обязательно выполняется, и это никак не зависит от памяти Claude). Официальная цитата:

Инструкции вроде «никогда не редактируй .env» в CLAUDE.md или skill — это запросы, а не гарантии. Хук PreToolUse, блокирующий редактирование, — это принудительное выполнение (enforcement).

Именно в этом причина тех 23 раз, упомянутых в начале: «запустить prettier после изменения» в CLAUDE.md — это просьба, которую он забывал каждый третий раз. А как Hook — это гарантия, которая не пропустит ни одного раза.

Вот несколько сценариев, с которыми вы наверняка столкнетесь и для которых нужна «гарантия»:

  • «Каждый раз после изменения файла автоматически запускать форматирование / линтер» — не вводите вручную и не ждите, что ИИ сделает это сам.
  • «Команды вроде rm -rf или удаление продакшн-базы жестко блокировать» — нужно гарантированно блокировать, а не просто просить не делать.
  • «Когда он закончил задачу или ждет моего ввода, отправлять уведомление на рабочий стол» — чтобы вы могли переключиться на другие дела и не пялиться в терминал.

💡 Краткий итог: Hook — это «автоматическое действие, срабатывающее по событию». Его главная ценность — превращение «просьбы» в «гарантию»: то, о чем вы просите в CLAUDE.md, может быть пропущено, а действие, привязанное к событию через Hook, будет выполнено неизбежно.


02 На какие «моменты» можно повесить хуки: знакомимся с событиями жизненного цикла

Hook нельзя прицепить в любой момент. Он должен быть привязан к определенному «моменту» в процессе работы Claude. Эти моменты официально называются событиями (events). Чтобы эффективно использовать хуки, первым делом нужно понять, «в какой момент я хочу, чтобы это произошло».

Вспомните 3-ю статью о цикле агента: работа Claude — это цикл «подумал → сделал → посмотрел». События распределены до и после каждого шага этого цикла. Официальная документация делит их по частоте срабатывания на три группы, и это очень легко запомнить:

  • Один раз за сеанс: SessionStart (сеанс начат / восстановлен), SessionEnd (сеанс завершен).
  • Один раз за раунд диалога: UserPromptSubmit (вы только что отправили промпт, Claude еще не начал обработку), Stop (Claude завершил ответ в этом раунде).
  • При каждом вызове инструмента в цикле: PreToolUse (непосредственно перед выполнением инструмента), PostToolUse (сразу после успешного выполнения инструмента).

Чтобы было не так абстрактно, взгляните на диаграмму — по ней сразу видно, где находятся эти события:

7 моментов для хуков Claude Code: SessionStart → UserPromptSubmit → Цикл инструментов Pre/Post → Stop → SessionEnd

Эта диаграмма разворачивает цикл «подумал → сделал → посмотрел»: вход в сеанс — это SessionStart, ваша реплика — UserPromptSubmit, затем цикл «использовать инструмент или нет» — перед каждым использованием инструмента есть PreToolUse, после — PostToolUse, завершение раунда — Stop, а закрытие всего сеанса — SessionEnd. В зависимости от того, на каком этапе вы хотите выполнить действие, вы привязываете его к соответствующему событию.

Эти шесть событий используются чаще всего. На самом деле официально поддерживается около тридцати (например, PreCompact/PostCompact до и после сжатия контекста, FileChanged при изменении файлов на диске, ConfigChange при изменении конфигурации, SubagentStart/SubagentStop при запуске/остановке дочерних агентов и др.). Но для новичка достаточно хорошо понять следующие четыре, они покроют 90% сценариев:

СобытиеКогда срабатываетСамое типичное использование
PreToolUseПеред выполнением инструментаБлокировка опасных команд, защита критичных файлов (может заблокировать операцию)
PostToolUseПосле успешного выполненияАвтоматическое форматирование / линтинг измененных файлов
StopКогда Claude закончил ответ в текущем раундеНапоминание «работа еще не закончена, продолжай», сканирование рабочей области
SessionStartПри запуске или восстановлении сеансаВнедрение статуса проекта в контекст (например, последние коммиты)

Есть хитрость, как запомнить эту таблицу: обращайте внимание на приставки Pre и Post. Pre означает «до», поэтому только в этом событии действие можно заблокировать до его совершения; Post означает «после», инструмент уже отработал, дело сделано, и хук может только «подчистить хвосты» (форматирование, логирование), но заблокировать уже не может. Об этой разнице подробнее поговорим в следующем разделе.

💡 Краткий итог: Hook привязывается к конкретным событиям жизненного цикла Claude. По частоте они делятся на три уровня (сеанс / раунд / вызов инструмента). Новичкам для начала нужно освоить четыре: PreToolUse (до, может блокировать), PostToolUse (после, подчищает), Stop (после ответа) и SessionStart (старт сеанса).


03 Где настраиваются хуки и как matcher сужает область их действия

Зная, к каким событиям можно привязать хуки, давайте посмотрим, как их писать. Хуки прописываются в файле настроек (settings.json) — той самой системе конфигурации, о которой мы подробно говорили в статье 31. То, в какой файл вы его запишете, определяет область его действия:

В каком файле настроеноОбласть действияМожно ли поделиться с командой
~/.claude/settings.jsonВсе ваши проектыНет, только на вашей машине
.claude/settings.json (в корне проекта)Только текущий проектДа, можно закоммитить в git
.claude/settings.local.json (в корне проекта)Только текущий проектНет, игнорируется git

Это та же логика «проектный шкаф vs ваша тумбочка», что обсуждалась в статье 31: Хуки, которые должны быть у всей команды (например, «всегда форматировать после правок»), записываются в .claude/settings.json проекта и коммитятся в git; то, что нужно только вам (например, уведомления на рабочий стол), пишется в ~/.claude/settings.json.

Как выглядит конфигурация хука

Сначала посмотрим на минимальный, но полный пример — «каждый раз после использования Edit или Write автоматически запускать prettier» (тот самый, который лечит болезнь «23 раз»). Записывается в .claude/settings.json в корне проекта:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Не пугайтесь вложенности, здесь всего три уровня. Разберем их, и все станет понятно:

  1. "PostToolUse" — к какому событию мы привязываемся (в данном случае: после использования инструмента).
  2. "matcher": "Edit|Write"сужение до определенных инструментов (здесь: сработает только после инструментов Edit или Write, но не после Bash или Read).
  3. Внутренний массив hooks — само действие, которое нужно выполнить: "type": "command" означает выполнение shell-команды, а "command" содержит саму команду.

Команда jq в этом примере — это небольшая утилита для парсинга JSON (на Mac устанавливается через brew install jq, на Ubuntu — apt-get install jq). Ее роль мы разберем в следующем разделе — проще говоря, она извлекает путь к только что измененному файлу из данных, переданных от Claude, и скармливает его prettier.

matcher: как заставить хук «срабатывать только тогда, когда нужно»

matcher — это самое важное для понимания поле в конфигурации хука. Одним словом: без него хук будет срабатывать на «каждый» триггер данного события; с ним вы можете сузить область действия.

Аналогия: правила пропуска для охранника на проходной. Хук без matcher подобен приказу охраннику «регистрировать абсолютно всех входящих», что крайне неэффективно. С matcher правило меняется на «регистрировать только курьеров, остальных пропускать без задержек». matcher именно это и делает — он «очерчивает границы срабатывания».

Для событий, связанных с инструментами (PreToolUse/PostToolUse), matcher сопоставляется с именем инструмента. Существует три способа его написания, посмотрите на таблицу:

Как вы написали matcherЗначениеПример
"Edit|Write"Точное совпадение с этими инструментами (| означает логическое ИЛИ)Срабатывает только после Edit или Write
"Bash"Точное совпадение с одним инструментомСрабатывает только при выполнении Bash-команды
"" или пропущеноСрабатывает всегдаВыполняется при каждом наступлении события

Внимание: matcher чувствителен к регистру. Написав edit, вы не поймаете инструмент Edit — это одна из самых частых причин, почему у новичков не срабатывают хуки.

И еще один нюанс, о котором часто забывают: некоторые события в принципе не поддерживают matcher (например, UserPromptSubmit, Stop), потому что у них нет «имени инструмента» для фильтрации, они просто срабатывают всегда. Если вы добавите matcher к таким событиям, он будет молча проигнорирован.

💡 Краткий итог: Хуки прописываются в settings.json (глобальные — в домашней директории, проектные — в .claude/). Конфигурация состоит из 3 уровней: событие, matcher, действие. matcher отвечает за то, чтобы хук срабатывал «только для нужных инструментов», и чувствителен к регистру.


04 Как хук общается с Claude: stdin, коды выхода, stdout

Этот раздел — ключ к пониманию всего процесса. Почему команда jq смогла получить путь к файлу? Как хук может «заблокировать» команду? Все ответы кроются в этом «механизме общения».

Сам механизм предельно прост, он состоит из трех каналов: Claude передает данные о событии через stdin в ваш скрипт → скрипт выполняет работу → скрипт использует «код выхода + stdout», чтобы сообщить Claude, что делать дальше. Разберем каждый шаг.

Вход: Claude подает вам блок JSON через stdin

Как только срабатывает событие, Claude Code передает данные, относящиеся к этому событию, в виде JSON-строки на стандартный ввод (stdin) вашей команды. Например, когда Claude собирается выполнить Bash-команду, хук PreToolUse получает примерно следующее:

json
{
  "session_id": "abc123",
  "cwd": "/Users/sarah/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

Видите? Что собирается сделать Claude, какой инструмент использует, какие передает параметры — всё находится здесь. Команда jq -r '.tool_input.file_path' из раздела 03 занимается тем, что вытаскивает из этого JSON путь к файлу (tool_input.file_path), который нужно изменить. jq — утилита специально для разбора JSON, а флаг -r заставляет ее выводить чистый текст (без кавычек).

Выход: используйте «код выхода», чтобы сообщить Claude следующий шаг

Когда скрипт завершает работу, он с помощью кода выхода (exit code) отдает команду Claude. Это самое важное соглашение для Хуков. Вам нужно запомнить только три числа:

Код выходаЗначениеРезультат
0Всё в порядке, продолжайОперация продолжается (при PreToolUse это не означает автоматическое одобрение, стандартный процесс проверки разрешений остается)
2Блокируй!Операция заблокирована; то, что вы написали в stderr, будет передано Claude как обратная связь для корректировки действий
Другие (например, 1)Произошла ошибка, но не блокироватьОперация продолжается, в терминале выводится сообщение об ошибке хука

Самое главное — exit 2. Это единственный способ «нажать на тормоз». Обратите внимание на весьма неочевидную ловушку:

Для большинства событий хука только код выхода 2 блокирует операцию. Claude Code расценивает код выхода 1 как неблокирующую ошибку и продолжает работу, несмотря на то, что 1 традиционно означает ошибку в Unix. Если ваш хук предназначен для принудительного применения политик, используйте exit 2.

Проще говоря: хотите заблокировать операцию — используйте exit 2, а не exit 1. Многие по привычке из Unix пишут exit 1, в итоге хук «выдает ошибку, но не блокирует», и команда спокойно выполняется. Это самая частая ошибка новичков при написании блокирующих хуков — скрипт корректно распознает опасную команду, печатает предупреждение, но из-за написанного по привычке exit 1 Claude как ни в чем не бывало ее выполняет, заставляя пользователя покрыться холодным потом.

И еще одна важная деталь, касающаяся «возможности блокировки»: по-настоящему блокировать операции могут только события группы Pre. Если PostToolUse вернет exit 2, это ничего не остановит — инструмент уже отработал, фарш невозможно провернуть назад. Хук сможет только показать stderr для Claude. Это возвращает нас к фразе из предыдущего раздела: «Pre может заблокировать, Post — только подчистить хвосты».

Продвинутый уровень: возврат JSON через stdout для тонкого управления

Код выхода работает только в режиме «заблокировать / не заблокировать». Если вам нужно более тонкое управление (например, при блокировке сообщить Claude конкретную причину, или внедрить информацию в его контекст), верните exit 0 и выведите JSON-объект в stdout.

Используйте код выхода 2 в паре с выводом в stderr для «блокировки», либо JSON в паре с кодом выхода 0 для «структурированного управления». Не смешивайте их: если вы возвращаете код 2, Claude Code проигнорирует ваш JSON.

Рассмотрим два самых частых случая вывода JSON:

① Вы хотите заблокировать операцию в PreToolUse и объяснить причину — используйте permissionDecision:

json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Эта команда затрагивает продакшн-базу данных, выполнение запрещено."
  }
}

У permissionDecision может быть четыре значения: "deny" (заблокировать и передать причину Claude), "ask" (стандартно показать окно подтверждения пользователю), "allow" (пропустить окно подтверждения и выполнить), "defer" (отложить выполнение, чтобы восстановить инструмент позже, полезно для асинхронных согласований в неинтерактивном режиме).

Здесь нужно четко обозначить красную линию безопасности, возвращаясь к теме разрешений из статей 20 и 21: если хук возвращает "allow", это не может обойти правила отклонения (deny), настроенные в ваших параметрах. Официальная цитата:

Возврат "allow" пропускает интерактивный запрос, но не отменяет правила разрешений. Если правило отклонения (deny) совпадает с вызовом инструмента, вызов будет заблокирован, даже если ваш хук возвращает "allow".

То есть: Hook может только «ужесточить» ограничения, он не может «ослабить» их сверх того, что разрешено правилами. Это очень важный аспект безопасности — он гарантирует, что вредоносный хук не сможет сломать ваши защитные барьеры, вернув allow. И наоборот, приоритет блокировки у хука PreToolUse максимальный: даже если вы запустили Claude с флагом --dangerously-skip-permissions (пропуск всех проверок), хук, вернувший deny, всё равно заблокирует действие. Поэтому использование хуков для обеспечения командных политик (красных линий) — это по-настоящему надежный способ защиты.

② Вы хотите внедрить информацию в контекст при SessionStart — просто выведите текст в stdout (эти события особые, их stdout будет передан Claude в качестве контекста). Например, при запуске сеанса сразу передать 5 последних коммитов:

json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "git log --oneline -5"
          }
        ]
      }
    ]
  }
}

💡 Краткий итог: Хук и Claude общаются через три канала — через stdin подается JSON, через код выхода отдаются команды, через stdout осуществляется тонкое управление. Запомните три правила: «для блокировки нужен exit 2 (не 1)», «не смешивайте код выхода 2 и JSON», «Хук может только ужесточать, но не ослаблять правила разрешений». Зная это, вы поняли 70% механизма.


05 Три реальных примера, которые можно скопировать и использовать

Достаточно теории, перейдем к трем практичным хукам, которые вы можете сразу применить. Для каждого будет указано «к какому событию привязан, как сужен matcher и в какой файл записан».

Пример 1: Автоматическое форматирование после изменения файла (PostToolUse)

Тот самый, что лечит проблему 23 раз. Самый полезный и абсолютно безопасный хук, настоятельно рекомендуется для всех проектов. Записывается в .claude/settings.json в корне проекта:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Логика: Каждый раз, когда Claude изменяет файл с помощью Edit/Write → хук вытаскивает путь к файлу из переданного через stdin JSON → передает его prettier --write для форматирования. Теперь формат всегда будет идеальным, и вам не нужно об этом думать. Вы можете заменить prettier на eslint --fix, gofmt или black — принцип останется тем же.

Пример 2: Блокировка опасных команд (PreToolUse + внешний скрипт)

Здесь применяется «блокировка через exit 2». Когда логика становится сложной, гораздо чище вынести ее в отдельный скрипт, чем пытаться втиснуть в конфигурацию JSON.

Шаг 1, сохраните скрипт в .claude/hooks/block-dangerous.sh:

bash
#!/bin/bash
# block-dangerous.sh: Блокировка опасных команд вроде rm -rf
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "rm -rf"; then
  echo "Blocked: Обнаружен rm -rf, команда заблокирована" >&2   # Вывод в stderr, вернется к Claude
  exit 2                                                         # exit 2 = заблокировать вызов инструмента
fi

exit 0   # Остальные команды пропускаем для стандартной проверки разрешений

Шаг 2, сделайте скрипт исполняемым (обязательно для Mac/Linux, иначе Claude не сможет его запустить):

bash
chmod +x .claude/hooks/block-dangerous.sh

Шаг 3, зарегистрируйте его в .claude/settings.json, привязав к инструменту Bash в событии PreToolUse:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
          }
        ]
      }
    ]
  }
}

Здесь $CLAUDE_PROJECT_DIR — это переменная окружения, предоставляемая Claude Code, которая указывает на корневую директорию проекта. Использование этой переменной для формирования пути гарантирует, что хук найдет скрипт независимо от того, в какой подпапке вы находитесь. Это гораздо надежнее, чем захардкоженные относительные пути.

⚠️ Вернемся к теме безопасности из статьи 21: Хук — это shell-скрипт, который выполняется с вашими полными правами пользователя. Он может удалить любой файл, к которому у вас есть доступ. Официальная документация постоянно подчеркивает: перед добавлением любого хука внимательно изучите его команды, особенно не стоит копировать целые скрипты из неизвестных источников прямо в настройки.

Пример 3: Уведомление на рабочий стол, когда требуется ваш ввод (Notification)

Когда Claude доходит до места, где нужно ваше одобрение, или просто закончил отвечать и ждет вашей реплики, вы, возможно, уже заняты чем-то другим. Повесьте хук на уведомления, и он сам вас позовет. Используется событие Notification (срабатывает, когда Claude отправляет уведомление).

Для macOS запишите это в ~/.claude/settings.json (такие хуки-«напоминалки» — это ваши личные предпочтения, поэтому сохраняем глобально):

json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code ждет вас\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Различия для платформ (официальные нативные команды, просто скопируйте нужную):

ПлатформаКоманда для уведомления (вписать в command)
macOSosascript -e 'display notification "..." with title "Claude Code"'
Linuxnotify-send 'Claude Code' '...'
WindowsИспользуйте MessageBox из PowerShell (в официальной документации есть полный фрагмент)

Если на macOS уведомление не появляется, скорее всего, у Script Editor нет прав на отправку уведомлений. Зайдите в «Системные настройки → Уведомления», найдите Script Editor и включите переключатель. Часто при первой настройке звук и всплывающее окно не появляются, и после долгих поисков выясняется, что проблема в отсутствии прав.

💡 Краткий итог: Три хука с возрастающим уровнем риска — форматирование (PostToolUse, риск нулевой, рекомендуется всем), блокировка команд (PreToolUse + скрипт, не забудьте chmod +x и exit 2), отправка уведомлений (Notification, команды зависят от платформы). Для путей к скриптам надежнее всего использовать $CLAUDE_PROJECT_DIR.


06 Практика: Настраиваем хук за 5 минут и проверяем его работу

Теория без практики быстро забывается. Сейчас мы настроим самый безопасный и наглядный хук — после каждой Bash-команды Claude, он будет записывать ее в лог-файл. Вы не затронете рабочий код, добавите только кусок конфигурации и сможете своими глазами увидеть результат.

Для этой практики понадобится jq. Если у вас его нет: на Mac выполните brew install jq, на Ubuntu — sudo apt-get install jq. (Установка не требует VPN или обхода блокировок).

Шаг 1: Найдите тестовую директорию и создайте в ней файл настроек проекта

Выберите пустую папку (не тренируйтесь в важных проектах) и создайте в ней .claude/settings.json. Если файл уже есть и содержит данные, добавьте ключ hooks, но не заменяйте всё остальное. Содержимое файла:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/claude-bash-log.txt"
          }
        ]
      }
    ]
  }
}

Что делает этот хук: каждый раз, когда инструмент Bash завершает работу (PostToolUse + matcher: "Bash") → извлекает команду из JSON в stdin → с помощью >> дописывает её в файл claude-bash-log.txt в домашней директории.

Шаг 2: Запустите Claude в этой директории и убедитесь, что хук зарегистрирован

bash
claude

Оказавшись внутри, введите команду /hooks:

text
/hooks

Ожидаемый результат: Появится read-only (только для чтения) обозреватель хуков, в котором перечислены все события. Найдите PostToolUse — рядом с ним должно быть указано наличие 1 хука. Выбрав его, вы увидите детали: событие, matcher (Bash), источник файла (Project — то есть .claude/settings.json проекта) и саму команду. Если он есть в списке, значит, хук успешно зарегистрирован.

Меню /hooks предназначено только для чтения — вы можете в нем искать, но не добавлять или изменять хуки. Чтобы изменить хук, отредактируйте settings.json вручную или попросите об этом Claude.

Шаг 3: Попросите Claude выполнить Bash-команду для срабатывания хука

Нажмите Esc, чтобы вернуться в чат, и попросите выполнить безобидную команду:

text
Покажи мне файлы в текущей директории с помощью команды ls

Он вызовет инструмент Bash, чтобы выполнить ls. Как только команда выполнится, хук PostToolUse должен сработать. (Когда хук выполняется успешно, он делает это «тихо», без специальных оповещений в терминале — это нормальное поведение).

Шаг 4: Убедитесь, что хук действительно отработал — проверьте лог-файл

Откройте новое окно терминала и посмотрите лог:

bash
cat ~/claude-bash-log.txt

Ожидаемый результат: В файле должна появиться команда ls (а также любые другие команды Bash, которые Claude выполнял в этом сеансе). Если команда записана — значит хук действительно автоматически сработал после выполнения Bash, и ваше «автоматическое правило» успешно внедрено.

Шаг 5: Очистка (по желанию)

Чтобы удалить тренировочный хук, просто удалите блок hooks из .claude/settings.json (специальной команды для удаления хука нет, достаточно убрать запись из конфигурации). Также удалите лог-файл: rm ~/claude-bash-log.txt.

Выполнив эти пять шагов, вы собственноручно прошли весь процесс: «написать конфигурацию → проверить регистрацию через /hooks → спровоцировать событие → проверить результат (сайд-эффект)». В будущем настройка любого хука будет следовать этому же алгоритму, изменится только событие, matcher или сама команда.

💡 Краткий итог: Для тренировки лучше всего использовать безопасный хук, вроде логирования Bash-команд: пропишите его в .claude/settings.json, проверьте через /hooks, попросите Claude выполнить команду для срабатывания, проверьте лог-файл. Увидеть результат своими глазами полезнее, чем вызубрить десяток определений.


07 Если хук не срабатывает или выдает ошибку: как это починить

Неработающий хук — это самая частая преграда для новичков. Не гадайте на кофейной гуще; следуйте этому алгоритму, и вы почти всегда найдете причину. Я переработал официальный чеклист диагностики в таблицу «Симптом → Как проверить»:

СимптомНаиболее вероятная причина / Как проверить
Хук вообще не срабатывает① Введите /hooks и посмотрите, зарегистрирован ли он; ② Учтите, что matcher чувствителен к регистру: edit не совпадет с Edit; ③ Выбрано не то событие (хотите заблокировать — нужен PreToolUse, а не PostToolUse).
В /hooks вообще нет моего хука① Ошибка в формате JSON (в JSON запрещены запятые в конце списка и комментарии); ② Файл находится не там (хук проекта должен быть в .claude/settings.json, глобальный — в ~/.claude/settings.json); ③ Если изменения не применились, перезапустите сессию.
В терминале выдается ошибка hook errorСкрипт неожиданно завершился с ненулевым кодом. Протестируйте его вручную (см. команды ниже); если пишет command not found, скорее всего, путь к скрипту указан неверно — используйте абсолютные пути или $CLAUDE_PROJECT_DIR; если ошибка jq: command not found — значит, не установлен jq.
Скрипт не запускаетсяНа Mac/Linux забыли дать скрипту права на выполнение, добавьте chmod +x.
Хотели заблокировать, но не вышлоНаверняка вы написали exit 1 вместо exit 2 (см. ловушку из раздела 04).

Два самых эффективных метода отладки, которые стоит выделить:

① Протестируйте скрипт вручную с помощью фейковых данных. Не нужно каждый раз вызывать Claude; создайте JSON-строку и передайте ее скрипту через конвейер, чтобы проверить код выхода:

bash
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /tmp/x"}}' | ./block-dangerous.sh
echo $?   # Посмотреть код выхода: скрипт блокировки должен вернуть 2

Это стандартный метод проверки блокирующих хуков — сначала отладьте скрипт отдельно, и только потом подключайте к Claude, чтобы не тратить время на тестирование в реальной сессии.

② Включите отладочные логи для подробностей. Если вы хотите узнать «какие именно хуки сработали, какой у них был код выхода, что они вывели в stdout/stderr», запустите Claude с флагом --debug, и логи будут записываться в ~/.claude/debug/<id_сеанса>.txt:

bash
claude --debug

Также режим отладки можно включить прямо в сессии, введя команду /debug. В логах появятся следующие строки, по которым сразу понятно, запускался ли хук и как он завершился:

text
[DEBUG] Executing hooks for PostToolUse:Bash
[DEBUG] Hook command completed with status 0

И еще одна полезная фича: если вам нужно временно отключить все хуки (например, вы подозреваете, что один из них все ломает), достаточно добавить строку "disableAllHooks": true в файл настроек, не нужно удалять их по одному.

💡 Краткий итог: Если хук не работает, не гадайте вслепую, а действуйте по алгоритму: «посмотреть /hooks → проверить регистр matcher'а и выбор события → вручную скормить JSON скрипту → посмотреть логи через --debug»; если хотели заблокировать, но действие выполнилось, проверьте, не написан ли случайно exit 1.


08 Итоги

В этой статье мы разобрали Хуки от понятия «что это такое» до «как настроить и отладить» — это «правила автоматизации» для Claude Code: как только срабатывает определенное событие, он безоговорочно выполняет нужную команду за вас.

Ключевые моменты для закрепления:

Что вы хотите сделатьКак реализоватьВажный момент
Понять, что такое HookАвтоматическая shell-команда, срабатывающая по событиюПревращает «просьбу» в «гарантию», не полагаясь на сознательность Claude
Выбрать момент срабатыванияОпределить событие жизненного циклаPre может блокировать, Post подчищает, Stop — по завершении ответа, SessionStart — на старте
Написать хукДобавить конфигурацию в settings.json3 уровня: Событие + matcher (чувствителен к регистру) + Действие
Наладить диалог хука с Claudestdin / коды выхода / stdoutДля блокировки нужен exit 2, код выхода и JSON-ответ не смешиваются
Заблокировать опасную командуPreToolUse + скриптНе забудьте chmod +x; Хуки могут только ужесточать, но не ослаблять права
Хук не работаетПроводить проверку по порядкуПроверить /hooks, протестировать JSON вручную, посмотреть логи --debug

Теперь вы должны уметь: Объяснить фундаментальную разницу между Хуком и «просьбой в CLAUDE.md» (гарантия против просьбы); знать, на каких этапах жизненного цикла срабатывают PreToolUse/PostToolUse/Stop/SessionStart; написать хук с использованием matcher в settings.json по шаблону; понимать, что хуки общаются с Claude через stdin/код выхода/stdout, и накрепко запомнить «блокировать можно только через exit 2»; знать, что отладку неработающего хука следует начинать с меню /hooks и флага --debug. И та проблема из начала статьи — 23 раза вводить prettier вручную — теперь может быть навсегда решена одной строкой конфигурации.

Хуки — это весьма хардкорная часть группы «Конфигурация и оптимизация системы», которая дает вам детерминированный (гарантированный) контроль над поведением Claude, вместо простого «доверия его выбору».


Следующая статья: 34 «Справочное руководство по CLI: Команды и все флаги» — на этом пути вы уже ввели множество команд, начинающихся с claude, использовали флаги вроде --debug и --dangerously-skip-permissions. Но на самом деле это лишь вершина айсберга. В следующей статье мы систематизируем все команды и флаги интерфейса командной строки claude, создав своеобразный «словарь», который всегда будет под рукой. Подумайте: сколько флагов claude вы сейчас можете назвать по памяти? Прочитав ту статью, вы обнаружите, что упускали как минимум половину полезных ключей, которые могли бы значительно упростить вам работу.


Рекомендуем к прочтению