Skip to main content
Hook — это обработчик события: функция, которую Claude Code запускает при возникновении именованного события. Claude Code генерирует событие в каждой точке, где он собирается действовать, например, когда он запускает инструмент, отправляет подсказку, отправляет запрос к модели или начинает или завершает сеанс. Ваш hook запускается перед тем, как Claude Code действует, поэтому он может наблюдать событие, переписать его или ответить на него вместо Claude Code. Вы регистрируете hook с помощью on(eventName, handler). Создайте свой первый мод перед тем, как начать здесь. Для каждого события и его точных полей см. справочник или прочитайте типы для вашей сборки.

Как hook обрабатывает событие

Hook находится между событием и тем, что Claude Code сделает с ним, поэтому он может наблюдать событие, переписать его или ответить на него сам. Он получает три аргумента: mods API как $, событие как e и следующий обработчик как next. Обработчики события образуют цепочку middleware. next(e) вызывает следующий обработчик, который является hook другого мода или, в конце цепочки, собственным поведением Claude Code, и разрешается в результат. То, что ваш hook делает с next, определяет, какой из трёх вариантов он выполняет.

Наблюдать событие

Чтобы наблюдать событие без его изменения, выполните свою работу и верните next(e). Этот hook регистрирует каждый инструмент, который Claude собирается использовать:
Перед запуском каждого инструмента в расшифровке появляется тусклая строка, такая как ● my-mod: Claude is about to use Bash, где my-mod — это имя вашего плагина. Инструмент запускается так же, как без мода. Чтобы действовать после события, await next(e), выполните свою работу и верните результат. Этот hook регистрирует каждый инструмент после его запуска:
Строка теперь появляется после завершения каждого инструмента. Claude читает один и тот же результат в любом случае, потому что hook возвращает то, в чём разрешился next(e).

Переписать событие

Чтобы изменить то, на что действует Claude Code, например текст подсказки, вызовите next с изменённой копией события. Само событие неизменяемо: оно заморожено на каждой глубине, и присваивание полю выбрасывает исключение. Этот hook обрезает каждую подсказку перед её отправкой:
Более поздние обработчики и Claude Code получают обрезанную подсказку и никогда не видят оригинал. Вы также можете изменить результат: await next(e), затем верните копию результата с заменённым полем.

Ответить на событие

Чтобы обработать событие самостоятельно, верните результат без вызова next. Это короткозамыкает цепочку, поэтому более поздние моды и собственное поведение Claude Code не запускаются. Этот hook отказывает в каждой команде Bash:
Когда Claude пытается выполнить команду Bash, команда не запускается, и Claude читает текст deny как результат инструмента. Каждое событие имеет свою форму результата, которую справочник событий перечисляет.

Фильтровать события, которые обрабатывает hook

Чтобы запустить hook только для некоторых событий, передайте фильтр в качестве второго аргумента on. Claude Code называет фильтр matcher. Это объект, чьи поля сравниваются с полями события, и hook запускается только когда каждое поле совпадает. Поле может быть значением, массивом допустимых значений или регулярным выражением. Каждая строка в этом примере регистрирует одну и ту же функцию, hook, для более узкого набора вызовов инструментов:
hook запускается один раз для вызова Bash, Edit или Write и один раз для вызова инструмента, имя которого начинается с mcp__github__. Вызов любого другого инструмента, такого как Read, не совпадает ни с одним из трёх, поэтому hook не запускается для него. Имя события может быть подстановочным символом. 'classic.*' совпадает с каждым событием hook настроек. '*' совпадает с каждым событием, кроме событий телеметрии, которые вы подключаете по имени или как 'telemetry.*'. Регистрируйте каждое событие один раз для каждого matcher. Если вы вызовете on дважды для session.start без matcher, модуль не загружается с ошибкой on("session.start") is registered twice without a matcher. Поместите всё, что ваш мод делает при запуске сеанса, в один hook.

Подключить то, что делает Claude

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

Охранять или изменять вызов инструмента

Hook tool.call видит каждый инструмент, который Claude собирается использовать, поэтому он может отказать в вызове, изменить его аргументы или пропустить его. tool.call срабатывает, когда Claude Code собирается запустить инструмент, включая вызовы, которые делает подагент, и вызовы инструментов MCP. e.tool — это имя инструмента, а аргументы инструмента — это поля e, такие как e.command для Bash. Когда вы вызываете next(e), Claude Code запускает проверку разрешений, а затем инструмент. Этот hook отказывает в команде Bash, которая выполняет force-push, и объясняет Claude почему:
Когда Claude пытается выполнить git push --force, команда не запускается и не появляется запрос разрешения, потому что hook никогда не вызывает next. Claude читает текст deny как результат инструмента, поэтому напишите его как инструкцию, на которую Claude может действовать. Каждая другая команда Bash запускается так же, как без мода. Чтобы действовать после запуска инструмента, await next(e), выполните свою работу и верните то, что дал вам next. Этот hook регистрирует каждый файл .mdx, который Claude изменяет, с помощью $.ui.log, который добавляет тусклую строку в расшифровку, которую Claude не читает:
После того как Claude редактирует или записывает файл .mdx, тусклая строка в расшифровке называет файл. Ничего не регистрируется для другого вида файла или для вызова, который был отказан или не удался. Представление Claude о вызове не меняется, потому что hook возвращает результат, который он получил. Чтобы изменить вызов, передайте изменённые аргументы next. Чтобы повторить вызов, вызовите next(e) снова: hook, который видит isError на первом результате, может запустить инструмент второй раз и вернуть этот результат. Чтобы ответить на вызов самостоятельно, верните объект с полем result, такой как { result: 'Skipped by my-mod' }, без вызова next. Когда вы это делаете, запрос разрешения не появляется и инструмент не запускается, поэтому результат, который вы возвращаете, — это всё, что Claude узнает о том, что произошло. Hooks в управляемых настройках вашей организации запускаются перед любым hook tool.call мода, и блокировка от одного из них является окончательной.

Удерживать вызов инструмента до тех пор, пока пользователь не решит

Hook может приостановить вызов инструмента и спросить пользователя, что делать, прежде чем он продолжится. Hook tool.call может await перед вызовом next или возвратом, и вызов инструмента остаётся в ожидании до тех пор. Чтобы задать вопрос пользователю, вызовите $.ui.ask. Он показывает ваш вопрос над пронумерованным списком ваших вариантов в диалоге, который Claude использует, чтобы спросить вас что-то, и разрешается в метку, которую выбрал пользователь. После ваших вариантов диалог добавляет строку для ввода другого ответа и строку Chat about this. Шаблон RISKY в этом примере совпадает с rm -r, rm -rf, git reset --hard и git push с --force, и он пропускает другие написания, такие как git push -f. Этот модуль спрашивает перед запуском команды Bash, которая совпадает с шаблоном:
Когда Claude пытается выполнить команду, такую как rm -rf build, вопрос появляется с командой в нём, и команда ждёт ответа:
  • Пользователь выбирает Run it: hook вызывает next(e), и обычная проверка разрешений всё ещё запускается после неё
  • Пользователь выбирает Refuse: команда не запускается, и Claude читает текст deny
  • Пользователь вводит ответ: $.ui.ask разрешается в введённый текст. Hook сравнивает его с Run it, поэтому любой другой текст отказывает в команде.
  • Никто не отвечает: $.ui.ask отклоняется, когда пользователь отклоняет вопрос или выбирает Chat about this, и в запуске claude -p, поэтому блок catch оставляет ответ на Refuse
Держите ожидание внутри вызова mods API, такого как $.ui.ask, потому что это время не учитывается в 10-секундном ограничении времени hook. Время, потраченное на ожидание собственного обещания, учитывается. Claude Code пропускает hook, который истекает по времени, поэтому удерживаемая команда запустится.

Переписать или добавить к подсказке

Hook prompt.submit видит каждую подсказку перед началом хода, поэтому он может переписать текст или добавить к нему. e.text — это то, что было введено. Этот hook добавляет имя текущей ветки для Claude всякий раз, когда подсказка упоминает pull request:
Когда вы отправляете подсказку, такую как open a PR for this change, ваше сообщение выглядит одинаково в расшифровке, и Claude также читает строку, такую как Current branch: feature/auth после неё. Подсказка, которая не упоминает pull request, проходит без изменений, и git не запускается. Другие события охватывают остальное, что читает Claude: prompt.section для каждого раздела системной подсказки, prompt.context для контекста, отправленного с первым сообщением, и skill.prompt для текста навыка. Текст из этих hooks, который меняется между запросами, делает кэш подсказок недействительным.

Следить за ходом

Ход — это всё, что Claude делает в ответ на одну подсказку. Подключите turn.start, turn.step и turn.complete, чтобы следить за одним: Напишите hook turn.step как асинхронный генератор, потому что событие потоковое. yield* next(e) пересылает ответ по мере его потока и вычисляется в завершённый результат. Этот hook регистрирует, сколько каждого запроса Claude API обслужил из кэша подсказок:
Ответ Claude потоком идёт на экран так же, как без мода. После завершения каждого запроса тусклая строка в расшифровке даёт количество токенов, прочитанных из кэша, и количество записанных в него. Ход с вызовами инструментов имеет несколько запросов, поэтому он добавляет несколько строк. result.usage содержит четыре количества токенов, которые сообщает Claude API для запроса, плюс model, который ответил: input_tokens, output_tokens, cache_read_input_tokens и cache_creation_input_tokens. Hook запускается и для запросов подагентов, поэтому проверьте e.agentId, когда вам нужна только основная беседа.

Подключить события hook настроек

Hooks настроек — это command, HTTP, prompt и agent hooks, которые вы настраиваете в файлах настроек. Каждое событие hook настроек, такое как Stop, SessionEnd или PostToolUse, также является событием с именем classic. с последующим именем события hook настроек, такое как classic.Stop. e — это JSON, который получает hook настроек на stdin, включая transcript_path. Этот hook использует Stop, который срабатывает, когда Claude заканчивает отвечать, чтобы зарегистрировать, где сохраняется расшифровка сеанса:
Каждый раз, когда Claude заканчивает отвечать, тусклая строка в расшифровке даёт путь файла расшифровки.

Запускаться рядом с другими модами

Несколько модов могут подключить одно и то же событие, и любой из них может не удаться. Если ваш мод блокирует вызовы инструментов, проверьте его позицию в цепочке и что происходит, когда его hook не удаётся.

Порядок, в котором запускаются моды

Hooks на одно и то же событие образуют одну цепочку middleware. Каждый next мода вызывает hook следующего мода, и последний next достигает собственного поведения Claude Code. Первый мод является самым внешним: он видит событие перед другими и результат после них, и он решает, запускаются ли другие вообще. Более поздний мод не может остановить более ранний от просмотра события. Claude Code упорядочивает цепочку по тому, откуда берётся каждый мод:
  1. Встроенная защита sec-default@builtin, мод, встроенный в Claude Code, который /plugin перечисляет как cc-plugin-sec-default, где он загружается, моды, которые ваша организация перечисляет в prependPlugins, а затем любой другой мод, который считается модом вашей организации и не находится в appendPlugins
  2. Моды, которые вы устанавливаете
  3. Моды, которые ваша организация перечисляет в appendPlugins
  4. Другие моды, встроенные в Claude Code
Среди модов, которые вы устанавливаете, мод запускается перед модами, которые он перечисляет в dependencies в своём манифесте. В одном модуле hooks запускаются в порядке, в котором register вызвал on.

Где hooks настроек запускаются в порядке

Hooks PreToolUse, настроенные в файлах настроек, также запускаются во время вызова инструмента в фиксированных точках цепочки модов:
  • Hooks PreToolUse из управляемых настроек: запускаются перед hook tool.call первого мода, и блокировка от одного из них является окончательной, поэтому ни один мод не видит вызов.
  • Hooks PreToolUse из каждого другого файла настроек и из hooks/hooks.json плагинов: запускаются после того, как последний мод вызовет next, как часть собственного поведения Claude Code. Мод, который отвечает на tool.call без вызова next, не позволяет им запускаться, и мод, который вызывает next, видит их решение в результате, который он возвращает.
tool.check — это событие, где Claude Code решает, может ли вызов инструмента запуститься. Оно срабатывает после этих hooks и правил разрешений, и next(e) разрешается в их решение. Hook на tool.check может вернуть другое решение, такое как { decision: 'allow' }, поэтому он может одобрить вызов, который hook из второй группы заблокировал. Расширить разрешения с помощью hooks перечисляет, какие решения имеют приоритет над модом.

Обработать hook, который не удаётся

Hook, который не удаётся, не нарушает сеанс, и вы можете решить, что происходит вместо этого. Когда hook без обработчика .catch выбрасывает исключение, истекает по времени или возвращает результат неправильной формы, что происходит дальше, зависит от того, вызвал ли он next:
  • Он не удался перед вызовом next: Claude Code пропускает его, и следующий обработчик запускается на его месте
  • Он не удался после разрешения next: этот результат стоит, и ничего не запускается второй раз
Одна строка называет мод, событие и причину, такую как my-mod: tool.call hook skipped: threw Error: boom. Где вы это читаете, зависит от сеанса, как Узнать, почему мод ничего не делает перечисляет. Hook ui.render, чей рисунок не проходит валидацию, сообщается иначе, как Построить дерево из элементов описывает. Чтобы сделать hook, который блокирует вызовы, не удаётся закрыто, добавьте обработчик ошибок .catch, который отвечает на его месте. Здесь guard — это ваша функция hook:
Пока guard работает, обработчик никогда не запускается. Когда guard выбрасывает исключение или истекает по времени при вызове Bash, Claude Code вызывает обработчик с тем же событием. Обработчик возвращает { deny }, поэтому команда не запускается, и Claude читает текст с throw или timeout в конце. Без обработчика Claude Code пропустил бы guard и запустил бы команду. Обработчик имеет одну секунду для ответа.

Следующие шаги