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 регистрирует каждый инструмент после его запуска:
next(e).
Переписать событие
Чтобы изменить то, на что действует Claude Code, например текст подсказки, вызовитеnext с изменённой копией события. Само событие неизменяемо: оно заморожено на каждой глубине, и присваивание полю выбрасывает исключение. Этот hook обрезает каждую подсказку перед её отправкой:
await next(e), затем верните копию результата с заменённым полем.
Ответить на событие
Чтобы обработать событие самостоятельно, верните результат без вызоваnext. Это короткозамыкает цепочку, поэтому более поздние моды и собственное поведение Claude Code не запускаются. Этот hook отказывает в каждой команде Bash:
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, см. справочник событий.Охранять или изменять вызов инструмента
Hooktool.call видит каждый инструмент, который Claude собирается использовать, поэтому он может отказать в вызове, изменить его аргументы или пропустить его. tool.call срабатывает, когда Claude Code собирается запустить инструмент, включая вызовы, которые делает подагент, и вызовы инструментов MCP. e.tool — это имя инструмента, а аргументы инструмента — это поля e, такие как e.command для Bash. Когда вы вызываете next(e), Claude Code запускает проверку разрешений, а затем инструмент.
Этот hook отказывает в команде Bash, которая выполняет force-push, и объясняет Claude почему:
git push --force, команда не запускается и не появляется запрос разрешения, потому что hook никогда не вызывает next. Claude читает текст deny как результат инструмента, поэтому напишите его как инструкцию, на которую Claude может действовать. Каждая другая команда Bash запускается так же, как без мода.
Чтобы действовать после запуска инструмента, await next(e), выполните свою работу и верните то, что дал вам next. Этот hook регистрирует каждый файл .mdx, который Claude изменяет, с помощью $.ui.log, который добавляет тусклую строку в расшифровку, которую Claude не читает:
.mdx, тусклая строка в расшифровке называет файл. Ничего не регистрируется для другого вида файла или для вызова, который был отказан или не удался. Представление Claude о вызове не меняется, потому что hook возвращает результат, который он получил.
Чтобы изменить вызов, передайте изменённые аргументы next. Чтобы повторить вызов, вызовите next(e) снова: hook, который видит isError на первом результате, может запустить инструмент второй раз и вернуть этот результат. Чтобы ответить на вызов самостоятельно, верните объект с полем result, такой как { result: 'Skipped by my-mod' }, без вызова next. Когда вы это делаете, запрос разрешения не появляется и инструмент не запускается, поэтому результат, который вы возвращаете, — это всё, что Claude узнает о том, что произошло.
Hooks в управляемых настройках вашей организации запускаются перед любым hook tool.call мода, и блокировка от одного из них является окончательной.
Удерживать вызов инструмента до тех пор, пока пользователь не решит
Hook может приостановить вызов инструмента и спросить пользователя, что делать, прежде чем он продолжится. Hooktool.call может await перед вызовом next или возвратом, и вызов инструмента остаётся в ожидании до тех пор. Чтобы задать вопрос пользователю, вызовите $.ui.ask. Он показывает ваш вопрос над пронумерованным списком ваших вариантов в диалоге, который Claude использует, чтобы спросить вас что-то, и разрешается в метку, которую выбрал пользователь. После ваших вариантов диалог добавляет строку для ввода другого ответа и строку Chat about this.
Шаблон RISKY в этом примере совпадает с rm -r, rm -rf, git reset --hard и git push с --force, и он пропускает другие написания, такие как git push -f. Этот модуль спрашивает перед запуском команды Bash, которая совпадает с шаблоном:
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
$.ui.ask, потому что это время не учитывается в 10-секундном ограничении времени hook. Время, потраченное на ожидание собственного обещания, учитывается. Claude Code пропускает hook, который истекает по времени, поэтому удерживаемая команда запустится.
Переписать или добавить к подсказке
Hookprompt.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 обслужил из кэша подсказок:
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 заканчивает отвечать, чтобы зарегистрировать, где сохраняется расшифровка сеанса:
Запускаться рядом с другими модами
Несколько модов могут подключить одно и то же событие, и любой из них может не удаться. Если ваш мод блокирует вызовы инструментов, проверьте его позицию в цепочке и что происходит, когда его hook не удаётся.Порядок, в котором запускаются моды
Hooks на одно и то же событие образуют одну цепочку middleware. Каждыйnext мода вызывает hook следующего мода, и последний next достигает собственного поведения Claude Code. Первый мод является самым внешним: он видит событие перед другими и результат после них, и он решает, запускаются ли другие вообще. Более поздний мод не может остановить более ранний от просмотра события.
Claude Code упорядочивает цепочку по тому, откуда берётся каждый мод:
- Встроенная защита
sec-default@builtin, мод, встроенный в Claude Code, который/pluginперечисляет какcc-plugin-sec-default, где он загружается, моды, которые ваша организация перечисляет вprependPlugins, а затем любой другой мод, который считается модом вашей организации и не находится вappendPlugins - Моды, которые вы устанавливаете
- Моды, которые ваша организация перечисляет в
appendPlugins - Другие моды, встроенные в Claude Code
dependencies в своём манифесте. В одном модуле hooks запускаются в порядке, в котором register вызвал on.
Где hooks настроек запускаются в порядке
HooksPreToolUse, настроенные в файлах настроек, также запускаются во время вызова инструмента в фиксированных точках цепочки модов:
- Hooks
PreToolUseиз управляемых настроек: запускаются перед hooktool.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 и запустил бы команду. Обработчик имеет одну секунду для ответа.
Следующие шаги
- Использовать mods API: добавлять команды и инструменты, вызывать модель и запускать работу по таймеру
- Рисовать в интерфейсе: показывать то, что собирают ваши hooks, в панели или над подсказкой
- Тестировать мод: вызывать любое из этих событий из теста
- Справочник модов: каждое событие, каждый метод mods API и ограничения