Жизненный цикл hook
Hooks срабатывают в определённых точках во время сеанса Claude Code. Когда событие срабатывает и совпадает с фильтром, Claude Code передаёт JSON-контекст события вашему обработчику hook. Для command hooks входные данные поступают на stdin. Для HTTP hooks они поступают как тело POST-запроса. Ваш обработчик может затем проверить входные данные, выполнить действие и опционально вернуть решение. События срабатывают в трёх ритмах:- один раз за сеанс:
SessionStartиSessionEnd - один раз за ход:
UserPromptSubmit,StopиStopFailure - при каждом вызове инструмента внутри агентного цикла:
PreToolUseиPostToolUse
Как разрешается hook
Чтобы увидеть, как эти части работают вместе, рассмотрим этот hookPreToolUse, который блокирует деструктивные команды оболочки. Фильтр matcher сужает область до вызовов инструмента Bash, а условие if сужает её дальше до команд Bash, совпадающих с rm *, поэтому block-rm.sh запускается только когда оба фильтра совпадают:
permissionDecision со значением "deny", если она содержит rm -rf:
Bash "rm -rf /tmp/build". Вот что происходит:
1
Событие срабатывает
Событие
PreToolUse срабатывает. Claude Code отправляет входные данные инструмента как JSON на stdin hook:2
Фильтр проверяет
Фильтр
"Bash" совпадает с именем инструмента, поэтому эта группа hook активируется. Если вы опустите фильтр или используете "*", группа активируется при каждом возникновении события.3
Условие if проверяет
Условие
if "Bash(rm *)" совпадает, потому что rm -rf /tmp/build — это подкоманда, совпадающая с rm *, поэтому этот обработчик запускается. Если бы команда была npm test, проверка if не удалась бы и block-rm.sh никогда не запустился бы, избегая затрат на порождение процесса. Поле if опционально; без него каждый обработчик в совпадающей группе запускается.4
Обработчик hook запускается
Скрипт проверяет полную команду и находит Если бы команда была более безопасным вариантом
rm -rf, поэтому выводит решение на stdout:rm, таким как rm file.txt, скрипт выполнил бы exit 0 вместо этого. Код выхода 0 без вывода означает, что hook не имеет решения для отчёта, поэтому вызов инструмента продолжается через нормальный поток разрешений. Hook может отклонить вызов, но молчание не одобряет его.5
Claude Code действует на основе результата
Claude Code читает JSON решение, блокирует вызов инструмента и показывает Claude причину.
Конфигурация
Hooks определяются в JSON файлах настроек. Конфигурация имеет три уровня вложенности:- Выберите hook event для ответа, например
PreToolUseилиStop - Добавьте matcher group для фильтрации срабатывания, например “только для инструмента Bash”
- Определите один или несколько hook handlers для запуска при совпадении
На этой странице используются специальные термины для каждого уровня: hook event для точки жизненного цикла, matcher group для фильтра и hook handler для команды оболочки, конечной точки HTTP, инструмента MCP, подсказки или агента, который запускается. “Hook” сам по себе относится к общей функции.
Расположение hook
Место, где вы определяете hook, определяет его область действия:
Для получения подробной информации о разрешении файлов настроек см. settings. Администраторы предприятия могут использовать
allowManagedHooksOnly для блокировки пользовательских, проектных и плагинных hooks. Hooks из плагинов, принудительно включённых в управляемых параметрах enabledPlugins, исключены, поэтому администраторы могут распространять проверенные hooks через организационный marketplace. См. Hook configuration.
Matcher patterns
Полеmatcher фильтрует срабатывание hooks. Способ оценки фильтра зависит от содержащихся в нём символов:
Фильтр на пути регулярного выражения проверяется с помощью
RegExp.prototype.test JavaScript, который успешно совпадает в любом месте значения. Edit.* совпадает как с Edit, так и с NotebookEdit; оберните шаблон в ^ и $, как в ^Edit$, когда вам нужно совпадение всей строки.
Разделители запятых и допуск окружающего пробела требуют Claude Code v2.1.191 или позже.
Дефисы в наборе точного совпадения требуют Claude Code v2.1.195 или позже. На более ранних версиях дефисное имя, такое как code-reviewer, оценивается как регулярное выражение без привязки, поэтому оно также срабатывает для senior-code-reviewer; закрепите его как ^code-reviewer$ на этих версиях, чтобы совпадать только с этим именем.
FileChanged и StopFailure используют более узкий набор точного совпадения только букв, цифр, _ и |. Дефис, пробел или запятая в фильтре для этих двух событий держит его на пути регулярного выражения, и только | разделяет альтернативы. Каждое другое событие с поддержкой фильтра в таблице ниже принимает | или ,.
Событие FileChanged не следует этим правилам при построении своего списка наблюдения. См. FileChanged.
Каждый тип события совпадает с другим полем:
Фильтр запускается против поля из JSON входа, который Claude Code отправляет вашему hook на stdin. Для событий инструмента это поле —
tool_name. Каждый раздел hook event перечисляет полный набор значений фильтра и схему входа для этого события.
Этот пример запускает скрипт линтинга только когда Claude пишет или редактирует файл:
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay и CwdChanged не поддерживают фильтры и всегда срабатывают при каждом вхождении. Если вы добавите поле matcher к этим событиям, оно будет молча проигнорировано.
Для событий инструмента вы можете фильтровать более узко, установив поле if на отдельных обработчиках hook. if использует синтаксис правила разрешения для совпадения с именем инструмента и аргументами вместе, поэтому "Bash(git *)" запускается когда любая подкоманда входа Bash совпадает с git * и "Edit(*.ts)" запускается только для файлов TypeScript.
Match MCP tools
MCP server инструменты отображаются как обычные инструменты в событиях инструментов (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), поэтому вы можете совпадать с ними так же, как с любым другим именем инструмента.
MCP инструменты следуют шаблону именования mcp__<server>__<tool>, например:
mcp__memory__create_entities: инструмент create entities сервера Memorymcp__filesystem__read_file: инструмент read file сервера Filesystemmcp__github__search_repositories: инструмент поиска сервера GitHub
.* к префиксу сервера. .* требуется: фильтр, такой как mcp__memory или mcp__brave-search, содержит только символы точного совпадения, поэтому он сравнивается как точная строка и не совпадает ни с одним инструментом.
mcp__memory__.*совпадает со всеми инструментами сервераmemorymcp__brave-search__.*совпадает со всеми инструментами с сервера, чьё имя содержит дефисmcp__.*__write.*совпадает с любым инструментом, чьё имя начинается сwriteиз любого сервера
mcp__brave-search, оценивается как регулярное выражение без привязки и совпадает с каждым инструментом с этого сервера. Форма mcp__brave-search__.* работает на каждой версии.
Инструменты из plugin-bundled MCP server используют сегмент сервера с областью, который включает имя плагина: mcp__plugin_<plugin-name>_<server-name>__<tool>. Фильтр, написанный против голого ключа сервера, никогда не срабатывает для этих инструментов. Для плагина с именем my-plugin, который объединяет сервер под ключом db, инструмент query отображается как mcp__plugin_my-plugin_db__query, поэтому фильтр для каждого инструмента с этого сервера — mcp__plugin_my-plugin_db__.*. Используйте то же имя инструмента с областью в поле if обработчика. См. Plugin-provided MCP servers для того, как строится имя с областью.
Этот пример логирует все операции сервера memory и проверяет операции записи из любого MCP сервера:
Hook handler fields
Каждый объект во внутреннем массивеhooks — это hook handler: команда оболочки, конечная точка HTTP, инструмент MCP, подсказка LLM или агент, который запускается при совпадении фильтра. Есть пять типов:
- Command hooks (
type: "command"): запускают команду оболочки. Ваш скрипт получает JSON входные данные события на stdin и передаёт результаты обратно через коды выхода и stdout. - HTTP hooks (
type: "http"): отправляют JSON входные данные события как HTTP POST запрос на URL. Конечная точка передаёт результаты обратно через тело ответа, используя тот же JSON формат выхода, что и command hooks. - MCP tool hooks (
type: "mcp_tool"): вызывают инструмент на уже подключённом MCP сервере. Текстовый вывод инструмента обрабатывается как stdout command hook. - Prompt hooks (
type: "prompt"): отправляют подсказку модели Claude для однооборотной оценки. Модель возвращает решение да/нет как JSON. См. Prompt-based hooks. - Agent hooks (
type: "agent"): порождают subagent, который может использовать инструменты, такие как Read, Grep и Glob, для проверки условий перед возвратом решения. Agent hooks являются экспериментальными и могут измениться. См. Agent-based hooks.
args, а HTTP hooks дедублируются по URL.
Обработчики запускаются в текущем каталоге с окружением Claude Code. Переменная окружения $CLAUDE_CODE_REMOTE устанавливается на "true" в удалённых веб-окружениях и не устанавливается в локальном CLI. Начиная с v2.1.199, $CLAUDE_CODE_BRIDGE_SESSION_ID устанавливается на ID сеанса Remote Control пока локальный сеанс имеет активное соединение Remote Control.
Common fields
Эти поля применяются ко всем типам hooks:
Поле
if содержит ровно одно правило разрешения. Нет синтаксиса &&, || или списка для объединения правил; чтобы применить несколько условий, определите отдельный обработчик hook для каждого.
Для Bash шаблонов, запускается ли ваша команда hook зависит от формы шаблона и команды Bash, которую вызывает Claude. Ведущие присваивания VAR=value удаляются перед совпадением.
Фильтр также открывается с ошибкой, запуская ваш hook независимо от шаблона, когда команда Bash не может быть проанализирована. Поскольку фильтр
if является лучшим усилием, используйте систему разрешений вместо hook для обеспечения жёсткого разрешения или отказа.
Command hook fields
В дополнение к общим полям, command hooks принимают эти поля:
Command hook запускается в exec form когда установлен
args, и в shell form когда args опущен. Установите args всякий раз, когда hook ссылается на path placeholder, так как каждый элемент передаётся как один аргумент без кавычек. Опустите args когда вам нужны функции оболочки, такие как pipes или &&, или когда ни одна из этих проблем не применяется.
Exec form запускается когда присутствует args. Claude Code разрешает command как исполняемый файл на PATH и запускает его напрямую с args как вектор аргументов. Нет оболочки, поэтому каждый элемент args — это ровно один аргумент, написанный как есть, и path placeholders, такие как ${CLAUDE_PLUGIN_ROOT}, подставляются в command и в каждый элемент args как простые строки. Специальные символы, такие как апострофы, $ и обратные кавычки, проходят дословно, потому что нет оболочки для их интерпретации. На любой платформе не происходит никакой токенизации оболочки.
Shell form запускается когда args отсутствует. Строка command передаётся в оболочку: sh -c на macOS и Linux, Git Bash на Windows, или PowerShell когда Git Bash не установлен. Установите поле shell для явного выбора. Оболочка токенизирует строку, расширяет переменные и интерпретирует pipes, &&, redirects и globs.
На Windows, exec form требует, чтобы
command разрешался в реальный исполняемый файл, такой как .exe. Shims .cmd и .bat, которые npm, npx, eslint и другие инструменты устанавливают в node_modules/.bin, не являются исполняемыми файлами и не могут быть запущены без оболочки. Чтобы запустить их в exec form, вызовите базовый скрипт с node напрямую, например "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Паттерн node плюс script-path работает на каждой платформе, потому что node.exe — это реальный бинарный файл. Чтобы запустить shim .cmd или .bat по имени, используйте shell form.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT и CLAUDE_PLUGIN_DATA на порождённом процессе, поэтому скрипт может читать process.env.CLAUDE_PLUGIN_ROOT независимо от того, как он был запущен.
Plugin hooks дополнительно подставляют значения ${user_config.*}, только в exec form: значение подставляется в command и в каждый элемент args как простая строка, поэтому оболочка не переанализирует его.
Shell-form plugin hook, чей command ссылается на ${user_config.*}, завершается с ошибкой вместо запуска. Чтобы использовать значение опции из shell-form hook, прочитайте переменную окружения $CLAUDE_PLUGIN_OPTION_<KEY>, такую как $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL для опции webhook_url, или установите args для переключения hook на exec form. До v2.1.207, shell-form plugin hook команды также подставляли ${user_config.*}.
В exec form,
command — это только имя исполняемого файла или путь. Если command — это голое имя без разделителя пути и содержит пробелы рядом с args, Claude Code логирует предупреждение, потому что spawn не удастся: нет исполняемого файла с именем node script.js. Переместите дополнительные токены в args. Абсолютные пути с пробелами, такие как C:\Program Files\nodejs\node.exe, — это один действительный исполняемый файл и не вызывают предупреждение.HTTP hook fields
В дополнение к общим полям, HTTP hooks принимают эти поля:
Claude Code отправляет JSON входные данные hook как тело POST запроса с
Content-Type: application/json. Тело ответа использует тот же JSON формат выхода, что и command hooks.
Обработка ошибок отличается от command hooks: ответы не 2xx, сбои соединения и таймауты все производят неблокирующие ошибки, которые позволяют выполнению продолжаться. Чтобы заблокировать вызов инструмента или отклонить разрешение, верните ответ 2xx с JSON телом, содержащим decision: "block" или hookSpecificOutput с permissionDecision: "deny".
Этот пример отправляет события PreToolUse на локальный сервис валидации, аутентифицируясь с токеном из переменной окружения MY_TOKEN:
MCP tool hook fields
В дополнение к общим полям, MCP tool hooks принимают эти поля:
Текстовое содержимое инструмента обрабатывается как stdout command hook: если оно анализируется как действительный JSON выход, оно обрабатывается как решение, в противном случае оно показывается как простой текст. Если названный сервер не подключён или инструмент возвращает
isError: true, hook производит неблокирующую ошибку и выполнение продолжается.
MCP tool hooks доступны на каждом hook событии после того, как Claude Code подключился к вашим MCP серверам. SessionStart и Setup обычно срабатывают до завершения подключения серверов, поэтому hooks на этих событиях должны ожидать ошибку “не подключено” при первом запуске.
Этот пример вызывает инструмент security_scan на MCP сервере my_server после каждого Write или Edit, передавая путь отредактированного файла:
Prompt and agent hook fields
В дополнение к общим полям, prompt и agent hooks принимают эти поля:Reference scripts by path
Используйте эти заполнители для ссылки на скрипты hook относительно корня проекта или плагина, независимо от рабочего каталога при запуске hook:${CLAUDE_PROJECT_DIR}: корень проекта. Claude Code также устанавливает эту переменную в окружении stdio MCP серверов и plugin LSP серверов.${CLAUDE_PLUGIN_ROOT}: каталог установки плагина, для скриптов, поставляемых с плагином. Изменяется при каждом обновлении плагина.${CLAUDE_PLUGIN_DATA}: каталог постоянных данных плагина, для зависимостей и состояния, которые должны пережить обновления плагина.
args как один аргумент без токенизации оболочки, поэтому пути с пробелами или специальными символами не нуждаются в кавычках. В shell form оберните каждый заполнитель в двойные кавычки.
- Project scripts
- Plugin scripts
Этот пример использует
${CLAUDE_PROJECT_DIR} для запуска проверки стиля из каталога .claude/hooks/ проекта после любого вызова инструмента Write или Edit:Hooks in skills and agents
В дополнение к файлам настроек и плагинам, hooks могут быть определены непосредственно в skills и subagents с использованием frontmatter. Эти hooks ограничены жизненным циклом компонента и запускаются только когда этот компонент активен. Поддерживаются все hook события. Для subagents,Stop hooks автоматически преобразуются в SubagentStop, так как это событие, которое срабатывает при завершении subagent.
Hooks используют тот же формат конфигурации, что и hooks на основе настроек, но ограничены жизненным циклом компонента и очищаются при его завершении.
Этот skill определяет hook PreToolUse, который запускает скрипт проверки безопасности перед каждой командой Bash:
Меню /hooks
Введите /hooks в Claude Code, чтобы открыть браузер только для чтения ваших настроенных hooks. Меню показывает каждое hook событие с количеством настроенных hooks, позволяет вам углубиться в фильтры и показывает полные детали каждого hook обработчика. Используйте его для проверки конфигурации, проверки того, из какого файла настроек пришёл hook, или проверки команды, подсказки или URL hook.
Меню отображает все пять типов hook: command, prompt, agent, http и mcp_tool. Каждый hook помечен префиксом [type] и источником, указывающим, где он был определён:
User: из~/.claude/settings.jsonProject: из.claude/settings.jsonLocal: из.claude/settings.local.jsonPlugin: изhooks/hooks.jsonплагинаSession: зарегистрирован в памяти для текущего сеансаBuilt-in: зарегистрирован внутри Claude Code
Отключение или удаление hooks
Чтобы удалить hook, удалите его запись из JSON файла настроек. Чтобы временно отключить все hooks без их удаления, установите"disableAllHooks": true в файле настроек. Нет способа отключить отдельный hook, сохраняя его в конфигурации.
Параметр disableAllHooks соблюдает иерархию управляемых настроек. Если администратор настроил hooks через управляемые параметры политики, disableAllHooks, установленный в пользовательских, проектных или локальных настройках, не может отключить эти управляемые hooks. Только disableAllHooks, установленный на уровне управляемых настроек, может отключить управляемые hooks.
Прямые редактирования hooks в файлах настроек обычно захватываются автоматически наблюдателем файлов.
Входные и выходные данные Hook
Command hooks получают JSON данные через stdin и передают результаты через коды выхода, stdout и stderr. HTTP hooks получают тот же JSON как тело POST запроса и передают результаты через тело HTTP ответа. Этот раздел охватывает поля и поведение, общие для всех событий. Каждый раздел события под Hook events включает его специфическую схему входа и параметры управления решением. На macOS и Linux command hooks запускаются в своём собственном сеансе без управляющего терминала начиная с v2.1.139. Процесс hook и любые дочерние процессы не могут открыть/dev/tty или отправлять escape последовательности непосредственно в интерфейс Claude Code. Windows не имеет /dev/tty. Чтобы вывести сообщение пользователю на любой платформе, верните systemMessage в JSON выходе. Чтобы вызвать уведомление рабочего стола, установить заголовок окна или издать звуковой сигнал, верните terminalSequence вместо этого.
Общие входные поля
Hook события получают эти поля как JSON, в дополнение к полям, специфичным для события, документированным в каждом разделе hook event. Для command hooks этот JSON поступает через stdin. Для HTTP hooks он поступает как тело POST запроса.
При запуске с
--agent или внутри subagent включаются два дополнительных поля:
Только hooks
SessionStart могут получать поле model, и его присутствие не гарантировано. Нет переменной окружения $CLAUDE_MODEL. Процесс hook наследует родительское окружение, поэтому он может читать $ANTHROPIC_MODEL, если вы установили её в вашей оболочке, но это значение не меняется при переключении моделей с /model во время сеанса. Один набор переменных не наследуется: Claude Code удаляет переменные экспортера OTEL_* из каждого подпроцесса, который он порождает, включая hooks.
Например, hook PreToolUse для команды Bash получает это на stdin:
tool_name и tool_input специфичны для события. Каждый раздел hook event документирует дополнительные поля для этого события.
Выходные коды выхода
Код выхода из вашей команды hook говорит Claude Code, должно ли действие продолжаться, быть заблокировано или быть проигнорировано. Exit 0 означает успех. Claude Code анализирует stdout для JSON полей выхода. JSON выход обрабатывается только при exit 0. Для большинства событий stdout записывается в журнал отладки, но не показывается в транскрипте. Исключения —UserPromptSubmit, UserPromptExpansion и SessionStart, где stdout добавляется как контекст, который Claude может видеть и действовать.
Exit 2 означает блокирующую ошибку. Claude Code игнорирует stdout и любой JSON в нём. Вместо этого текст stderr передаётся обратно Claude как сообщение об ошибке. Эффект зависит от события: PreToolUse блокирует вызов инструмента, UserPromptSubmit отклоняет подсказку и так далее. См. exit code 2 behavior для полного списка.
Любой другой код выхода — это неблокирующая ошибка для большинства событий hook. Транскрипт показывает уведомление об ошибке <hook name> hook error, за которым следует первая строка stderr, поэтому вы можете определить причину без --debug. Выполнение продолжается и полный stderr записывается в журнал отладки.
Например, скрипт команды hook, который блокирует опасные команды Bash:
Поведение exit code 2 для каждого события
Exit code 2 — это способ hook сигнализировать “стоп, не делай этого”. Эффект зависит от события, потому что некоторые события представляют действия, которые могут быть заблокированы (например, вызов инструмента, который ещё не произошёл), а другие представляют вещи, которые уже произошли или не могут быть предотвращены.
Для
SessionStart, Setup и SubagentStart stderr exit code 2 отображается в транскрипте как уведомление об ошибке <hook name> hook error, так же как неблокирующая ошибка. Claude не видит это, и сеанс или subagent продолжается. Для SubagentStart уведомление появляется в собственном транскрипте subagent, а не в родительском разговоре.
Начиная с Claude Code v2.1.199, SessionStart, Setup и SubagentStart показывают stderr exit code 2 в транскрипте. Более ранние версии записывали это только в журнал отладки.
Обработка HTTP ответа
HTTP hooks используют коды статуса HTTP и тела ответов вместо кодов выхода и stdout:- 2xx с пустым телом: успех, эквивалентно exit code 0 без выхода
- 2xx с телом простого текста: успех, текст добавляется как контекст
- 2xx с JSON телом: успех, анализируется с использованием той же JSON выхода схемы, что и command hooks
- Статус не 2xx: неблокирующая ошибка, выполнение продолжается
- Сбой соединения или таймаут: неблокирующая ошибка, выполнение продолжается
JSON выход
Коды выхода позволяют вам разрешить или заблокировать, но JSON выход даёт вам более точное управление. Вместо выхода с кодом 2 для блокировки, выйдите с 0 и выведите JSON объект на stdout. Claude Code читает специфические поля из этого JSON для управления поведением, включая decision control для блокировки, разрешения или эскалации пользователю.Вы должны выбрать один подход на hook, не оба: либо используйте коды выхода отдельно для сигнализации, либо выйдите с 0 и выведите JSON для структурированного управления. Claude Code обрабатывает JSON только при exit 0. Если вы выйдете с 2, любой JSON игнорируется.
additionalContext, systemMessage и простой stdout, ограничены 10 000 символами. Выход, превышающий этот лимит, сохраняется в файл и заменяется предпросмотром и путём к файлу, так же как обрабатываются большие результаты инструментов.
JSON объект поддерживает три вида полей:
- Универсальные поля как
continueработают во всех событиях. Они перечислены в таблице ниже. - Верхнеуровневые
decisionиreasonиспользуются некоторыми событиями для блокировки или предоставления обратной связи. hookSpecificOutput— это вложенный объект для событий, которым нужно более богатое управление. Он требует полеhookEventName, установленное на имя события.
Чтобы полностью остановить Claude независимо от типа события:
Выдача уведомлений терминала
ПолеterminalSequence требует Claude Code v2.1.141 или позже.
Hooks запускаются без управляющего терминала, поэтому запись escape последовательностей непосредственно в /dev/tty не удаётся. Вместо этого верните escape последовательность в поле terminalSequence и Claude Code выдаст её от вашего имени через собственный путь записи терминала. Это свободно от гонок, работает внутри tmux и GNU screen, и работает на Windows, где нет /dev/tty.
Поле принимает строку из одной или нескольких разрешённых escape последовательностей:
- OSC
0,1,2: заголовки окна и значков - OSC
9: уведомления iTerm2, ConEmu, Windows Terminal и WezTerm, включая9;4прогресс панели задач - OSC
99: уведомления Kitty - OSC
777: уведомления urxvt, Ghostty и Warp - Bare BEL
Notification. Escape последовательность строится с printf восьмеричными экранами, поэтому управляющие байты никогда не появляются в командной строке оболочки, и jq -n --arg строит JSON выход, поэтому кавычки, обратные слэши и новые строки в сообщении уведомления правильно экранируются:
{ "terminalSequence": "..." } одинакова из любой оболочки или языка. На Windows постройте escape строку в PowerShell или скрипте и выдайте тот же JSON объект.
terminalSequence — это поддерживаемая замена для hooks, которые ранее писали escape последовательности непосредственно в /dev/tty. Список разрешённых ограничен последовательностями, которые не могут перемещать курсор или изменять цвета, поэтому hook никогда не может повредить подсказку на экране.Добавить контекст для Claude
ПолеadditionalContext передаёт строку из вашего hook в контекстное окно Claude. Claude Code оборачивает строку в системное напоминание и вставляет её в разговор в точке, где сработал hook. Claude читает напоминание при следующем запросе модели, но оно не появляется как сообщение чата в интерфейсе.
Верните additionalContext внутри hookSpecificOutput рядом с именем события:
- SessionStart, Setup и SubagentStart: в начале разговора, перед первой подсказкой
- UserPromptSubmit и UserPromptExpansion: рядом с отправленной подсказкой
- PreToolUse, PostToolUse, PostToolUseFailure и PostToolBatch: рядом с результатом инструмента
- Stop и SubagentStop: в конце хода. Разговор продолжается, поэтому Claude может действовать на основе обратной связи. См. Stop decision control
additionalContext для одного события, Claude получает все значения. Если значение превышает 10 000 символов, Claude Code записывает полный текст в файл в каталоге сеанса и передаёт Claude путь к файлу с кратким предпросмотром вместо этого.
Используйте additionalContext для информации, которую Claude должен знать о текущем состоянии вашей среды или операции, которая только что запустилась:
- Состояние среды: текущая ветка, цель развёртывания или активные флаги функций
- Условные правила проекта: какая команда тестирования применяется к только что отредактированному файлу, какие каталоги доступны только для чтения в этом worktree
- Внешние данные: открытые проблемы, назначенные вам, недавние результаты CI, содержимое, полученное из внутреннего сервиса
bun test” читаются как информация о проекте. Текст, сформулированный как внеполосные системные команды, может активировать защиту Claude от внедрения подсказок, что заставляет Claude вывести текст вам вместо того, чтобы рассматривать его как контекст.
После внедрения текст сохраняется в транскрипте сеанса. Для событий в середине сеанса, таких как PostToolUse или UserPromptSubmit, возобновление с --continue или --resume воспроизводит сохранённый текст вместо повторного запуска hook для прошлых ходов, поэтому значения, такие как временные метки или SHA коммитов, становятся устаревшими при возобновлении. Hooks SessionStart запускаются снова при возобновлении с source установленным на "resume", поэтому они могут обновить свой контекст.
Управление решением
Не каждое событие поддерживает блокировку или управление поведением через JSON. События, которые это делают, каждое использует другой набор полей для выражения этого решения. Используйте эту таблицу как быструю ссылку перед написанием hook:
Несколько событий также могут переписывать содержимое, а не только разрешать или блокировать его:
PreToolUse:updatedInputнепосредственно подhookSpecificOutputзаменяет аргументы инструмента перед его запуском. См. PreToolUse decision controlPermissionRequest:updatedInputвнутри объектаdecision. См. PermissionRequest decision controlPostToolUse:updatedToolOutputзаменяет результат инструмента. См. PostToolUse decision controlUserPromptSubmit: не может заменить подсказку; только внедряетadditionalContextрядом с ней
PreToolUse для исходящих входных данных инструмента и PostToolUse для входящих результатов инструмента.
Вот примеры каждого шаблона в действии:
- Top-level decision
- PreToolUse
- PermissionRequest
Используется
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange и PreCompact. Единственное значение — "block". Чтобы разрешить действию продолжаться, опустите decision из вашего JSON или выйдите с 0 без какого-либо JSON вообще:Hook events
Каждое событие соответствует точке в жизненном цикле Claude Code, где могут запускаться hooks. Разделы ниже упорядочены в соответствии с жизненным циклом: от настройки сеанса через агентный цикл к концу сеанса. Каждый раздел описывает, когда срабатывает событие, какие фильтры оно поддерживает, JSON входные данные, которые оно получает, и как управлять поведением через выход.SessionStart
Запускается при запуске Claude Code нового сеанса или возобновлении существующего сеанса. Полезно для загрузки контекста разработки, такого как существующие проблемы или недавние изменения в вашей кодовой базе, или установки переменных окружения. Для статического контекста, который не требует скрипта, используйте CLAUDE.md вместо этого. SessionStart запускается при каждом сеансе, поэтому держите эти hooks быстрыми. Поддерживаются только hookstype: "command" и type: "mcp_tool".
Значение фильтра соответствует тому, как был инициирован сеанс:
SessionStart input
В дополнение к общим полям входа, SessionStart hooks получаютsource и опционально model, agent_type и session_title:
SessionStart decision control
Любой текст, который ваш скрипт hook выводит на stdout, добавляется как контекст для Claude. В дополнение к JSON полям выхода, доступным для всех hooks, вы можете вернуть эти поля, специфичные для события:suppressOutput или sessionTitle.
Используйте reloadSkills, когда hook SessionStart устанавливает или обновляет skills. Обнаружение skills обычно запускается перед завершением SessionStart hooks, поэтому файлы, которые hook записывает в ~/.claude/skills/ или .claude/skills/, в противном случае появились бы только в следующем сеансе. Этот пример синхронизирует репозиторий общих skills и запрашивает повторное сканирование:
Persist environment variables
SessionStart hooks имеют доступ к переменной окруженияCLAUDE_ENV_FILE, которая предоставляет путь к файлу, где вы можете сохранять переменные окружения для последующих команд Bash.
Чтобы установить отдельные переменные окружения, напишите операторы export в CLAUDE_ENV_FILE. Используйте добавление (>>) для сохранения переменных, установленных другими hooks:
CLAUDE_ENV_FILE доступен для SessionStart, Setup, CwdChanged и FileChanged hooks. Другие типы hooks не имеют доступа к этой переменной.Setup
Срабатывает только при запуске Claude Code с--init-only или с --init или --maintenance в неинтерактивном режиме с флагом -p. Не срабатывает при нормальном запуске. Используйте это для одноразовой установки зависимостей или запланированной очистки, которую вы запускаете явно из CI или скриптов, отдельно от нормального запуска сеанса. Для инициализации для каждого сеанса используйте SessionStart вместо этого.
Значение фильтра соответствует флагу CLI, который запустил hook:
--init-only запускает Setup hooks и SessionStart hooks с фильтром startup, затем выходит без запуска разговора. --init и --maintenance срабатывают Setup hooks только при объединении с -p; в интерактивном сеансе эти два флага в настоящее время не срабатывают Setup hooks.
Поскольку Setup не срабатывает при каждом запуске, плагин, которому нужна установленная зависимость, не может полагаться только на Setup. Практический паттерн — проверить зависимость при первом использовании и установить при отсутствии, например hook или skill, который проверяет ${CLAUDE_PLUGIN_DATA}/node_modules и запускает npm install при отсутствии. См. persistent data directory для того, где хранить установленные зависимости.
Setup input
В дополнение к общим полям входа, Setup hooks получают полеtrigger, установленное на "init" или "maintenance":
Setup decision control
Setup hooks не могут блокировать. При exit code 2 stderr показывается пользователю как уведомление об ошибке hook, и выполнение продолжается. В неинтерактивном режиме выход hook появляется только при запуске с--verbose. Чтобы передать информацию в контекст Claude, верните additionalContext в JSON выходе; простой stdout записывается только в журнал отладки. В дополнение к JSON полям выхода, доступным для всех hooks, вы можете вернуть эти поля, специфичные для события:
CLAUDE_ENV_FILE. Переменные, написанные в этот файл, сохраняются в последующих командах Bash для сеанса, как и в SessionStart hooks. Поддерживаются только hooks type: "command" и type: "mcp_tool".
InstructionsLoaded
Срабатывает при загрузке файлаCLAUDE.md или .claude/rules/*.md в контекст. Это событие срабатывает при запуске сеанса для нетерпеливо загруженных файлов и снова позже при ленивой загрузке, например когда Claude получает доступ к подкаталогу, содержащему вложенный CLAUDE.md, или когда условные правила с frontmatter paths: совпадают. Hook не поддерживает блокировку или управление решением. Он запускается асинхронно в целях наблюдаемости.
Фильтр запускается против load_reason. Например, используйте "matcher": "session_start" для срабатывания только для файлов, загруженных при запуске сеанса, или "matcher": "path_glob_match|nested_traversal" для срабатывания только для ленивых загрузок.
InstructionsLoaded input
В дополнение к общим полям входа, InstructionsLoaded hooks получают эти поля:InstructionsLoaded decision control
InstructionsLoaded hooks не имеют управления решением. Они не могут блокировать или изменять загрузку инструкций. Используйте это событие для аудита логирования, отслеживания соответствия или наблюдаемости.UserPromptSubmit
Запускается при отправке пользователем подсказки, перед обработкой Claude. Это позволяет вам добавить дополнительный контекст на основе подсказки/разговора, проверить подсказки или заблокировать определённые типы подсказок. HooksUserPromptSubmit имеют таймаут по умолчанию 30 секунд для типов command, http и mcp_tool, что короче, чем таймаут по умолчанию 600 секунд для этих типов на других событиях. Поскольку этот hook запускается перед каждой подсказкой и блокирует обработку модели до его завершения, застрявший hook замораживает сеанс. Если вашему hook нужно больше времени, установите поле timeout в записи hook.
Hook UserPromptSubmit, который достигает своего таймаута, отменяется и его выход, включая любой additionalContext, отбрасывается. Подсказка всё ещё достигает Claude без этого контекста. Начиная с v2.1.196, транскрипт показывает уведомление, называющее hook, таймаут, который сработал, и что выход был отброшен. Более ранние версии отменяют hook без уведомления.
Hook Agent SDK callback на UserPromptSubmit, который достигает своего таймаута, блокирует подсказку с сообщением, называющим hook и таймаут, потому что callback там может действовать как политический шлюз, который не должен отказать открыто. Сеанс продолжается. До v2.1.208 таймаут callback на этом событии заканчивал ход с ошибкой выполнения.
UserPromptSubmit input
В дополнение к общим полям входа, UserPromptSubmit hooks получают полеprompt, содержащее текст, отправленный пользователем.
UserPromptSubmit decision control
HooksUserPromptSubmit могут управлять тем, обрабатывается ли подсказка пользователя, и добавлять контекст. Доступны все JSON поля выхода.
Есть два способа добавить контекст в разговор при exit code 0:
- Простой текст stdout: любой текст, не являющийся JSON, написанный на stdout, добавляется как контекст
- JSON с
additionalContext: используйте формат JSON ниже для большего управления. ПолеadditionalContextдобавляется как контекст
additionalContext внедряется как системное напоминание, которое Claude читает без видимой записи в транскрипте.
Чтобы заблокировать подсказку, верните JSON объект с decision, установленным на "block":
UserPromptExpansion
Запускается, когда пользователь вводит slash command, который расширяется в подсказку перед достижением Claude. Используйте это для блокировки определённых команд от прямого вызова, внедрения контекста для определённого skill или логирования, какие команды вызывают пользователи. Например, hook, соответствующийdeploy, может заблокировать /deploy, если файл одобрения отсутствует, или hook, соответствующий skill проверки, может добавить контрольный список проверки команды как additionalContext.
Это событие охватывает путь, который PreToolUse не охватывает: hook PreToolUse, соответствующий инструменту Skill, срабатывает только когда Claude вызывает инструмент, но ввод /skillname напрямую обходит PreToolUse. UserPromptExpansion срабатывает на этом прямом пути.
Совпадает с command_name. Оставьте фильтр пустым для срабатывания на каждой подсказке-типе slash command.
UserPromptExpansion input
В дополнение к общим полям входа, UserPromptExpansion hooks получаютexpansion_type, command_name, command_args, command_source и исходную строку prompt. Поле expansion_type равно slash_command для skill и пользовательских команд или mcp_prompt для подсказок MCP сервера.
UserPromptExpansion decision control
HooksUserPromptExpansion могут блокировать расширение или добавлять контекст. Доступны все JSON поля выхода.
MessageDisplay
Запускается во время потоковой передачи сообщения помощника на экран. Claude Code отображает сообщение порциями: каждый раз, когда пакет новых завершённых строк готов к отрисовке, hook запускается один раз с этими строками, и Claude Code отображает текст замены hook вместо них. Длинное сообщение производит несколько вызовов; короткое сообщение может произвести только один. Используйте MessageDisplay для:- удаления markdown для минимального отображения
- преобразования текста, который приложение Agent SDK показывает своим пользователям
- редактирования API ключей или внутренних имён хостов из ответов Claude
timeout в записи hook.
MessageDisplay предназначен только для отображения: текст замены изменяет только то, что отрисовывается на экране. Транскрипт и то, что видит Claude, сохраняют исходный текст, поэтому Claude никогда не видит замену, и подробный режим показывает исходный. Hook получает только текст сообщения помощника, поэтому результаты инструментов и текст, который вы вводите, отрисовываются без изменений.
MessageDisplay не поддерживает фильтры и срабатывает для каждого сообщения помощника, которое потоком передаёт текст; сообщения без текста, такие как ответы только с вызовом инструмента, не запускают его.
В неинтерактивных запусках, включая запросы Agent SDK и claude -p, MessageDisplay запускается один раз за сообщение помощника вместо один раз за пакет строк. Единственный вызов приходит после завершения сообщения и содержит полный текст сообщения: index равен 0, final равен true, и delta содержит всё сообщение. Hook, который собирает текст delta для каждого сообщения, получает одинаковый общий текст в обоих режимах.
MessageDisplay input
В дополнение к общим полям входа, MessageDisplay hooks получают идентификаторы для хода и сообщения, позицию этого вызова в сообщении и новый текст вdelta. Границы пакетов зависят от того, как текст потоком передаётся, поэтому используйте index и final для отслеживания прогресса через сообщение, а не ожидайте, что строки будут сгруппированы определённым образом.
MessageDisplay output
В дополнение к JSON полям выхода, доступным для всех hooks, MessageDisplay hooks могут вернутьdisplayContent для замены дельты на экране:
MessageDisplay hooks не имеют управления решением. Они не могут блокировать сообщение или изменять то, что хранится в транскрипте или отправляется Claude.
Этот пример удаляет форматирование markdown из ответов Claude для отображения в виде простого текста. Скрипт читает каждый пакет из stdin, удаляет маркеры жирного шрифта и обратные кавычки встроенного кода из
delta и возвращает результат как displayContent.
- macOS/Linux
- Windows (PowerShell)
Зарегистрируйте command hook для события в файле настроек:Сохраните этот скрипт в Скрипту нужен
.claude/hooks/plain-display.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x:jq в вашем PATH.jq отсутствует, Claude Code отображает исходный текст и отмечает сбой только в debug output, а не в сеансе.
PreToolUse
Запускается после того, как Claude создаёт параметры инструмента и перед обработкой вызова инструмента. Совпадает с именем инструмента:Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode и любые имена MCP инструментов.
Используйте PreToolUse decision control для разрешения, отклонения, запроса или отложения вызова инструмента.
PreToolUse input
В дополнение к общим полям входа, PreToolUse hooks получаютtool_name, tool_input и tool_use_id. Поля tool_input зависят от инструмента:
Выполняет команды оболочки.
Создаёт или перезаписывает файл.
Заменяет строку в существующем файле.
Читает содержимое файла.
Находит файлы, соответствующие шаблону glob.
Ищет содержимое файла с регулярными выражениями.
Получает и обрабатывает веб-содержимое.
Ищет в веб.
Порождает subagent.
В
PostToolUse, tool_response для завершённого вызова Agent содержит финальный текст subagent вместе с телеметрией использования. Читайте эти поля для записи затрат для каждого subagent из hook:
Для фоновых subagents инструмент возвращается сразу после запуска, поэтому
tool_response не содержит полей использования. Он имеет status: "async_launched", agentId, description, prompt, outputFile и resolvedModel вместо этого.
Поле resolvedModel называет модель, на которой subagent фактически запустился, что может отличаться от значения model в tool_input. Оно требует Claude Code v2.1.174 или позже.
Задаёт пользователю один-четыре вопроса с множественным выбором.
Представляет план и просит пользователя одобрить его перед тем, как Claude покинет plan mode. Claude записывает план в файл на диск перед вызовом инструмента, поэтому буквальный
tool_input от модели обычно пуст. Claude Code внедряет содержимое плана и путь файла перед передачей входных данных в hooks.
В
PostToolUse, tool_response — это объект с полями plan и filePath, содержащими одобренный план, плюс внутренние флаги статуса. Читайте tool_response.plan для содержимого плана, а не перечитывайте файл с диска.
PreToolUse decision control
HooksPreToolUse могут управлять тем, продолжается ли вызов инструмента. В отличие от других hooks, которые используют верхнеуровневое поле decision, PreToolUse возвращает своё решение внутри объекта hookSpecificOutput. Это даёт ему более богатое управление: четыре результата (разрешить, отклонить, спросить или отложить) плюс возможность изменить входные данные инструмента перед выполнением.
Когда несколько PreToolUse hooks возвращают разные решения, приоритет —
deny > defer > ask > allow.
Когда hook возвращает "ask", диалог разрешения, отображаемый пользователю, включает метку, идентифицирующую источник hook: например, [User], [Project], [Plugin] или [Local]. Это помогает пользователям понять, какой источник конфигурации запрашивает подтверждение.
AskUserQuestion и ExitPlanMode требуют взаимодействия пользователя и обычно блокируют в неинтерактивном режиме с флагом -p. Возврат permissionDecision: "allow" вместе с updatedInput удовлетворяет этому требованию: hook читает входные данные инструмента из stdin, собирает ответ через ваш собственный UI и возвращает его в updatedInput, чтобы инструмент запустился без запроса. Возврат только "allow" недостаточен для этих инструментов. Для AskUserQuestion повторите исходный массив questions и добавьте объект answers, соответствующий тексту каждого вопроса выбранному ответу.
Инструменты соединителя ваша организация установила на ask запрашивают даже когда hook возвращает "allow".
Начиная с v2.1.199, инструмент MCP, чей сервер помечает его с помощью _meta["anthropic/requiresUserInteraction"], более строг: hook не может пропустить его диалог одобрения с помощью "allow", с updatedInput или без, потому что Claude Code не может подтвердить, что hook собрал взаимодействие, которое нужно инструменту.
PreToolUse ранее использовал верхнеуровневые поля
decision и reason, но они устарели для этого события. Используйте hookSpecificOutput.permissionDecision и hookSpecificOutput.permissionDecisionReason вместо этого. Устаревшие значения "approve" и "block" соответствуют "allow" и "deny" соответственно. Другие события, такие как PostToolUse и Stop, продолжают использовать верхнеуровневые decision и reason как их текущий формат.Defer a tool call for later
"defer" предназначен для интеграций, которые запускают claude -p как подпроцесс и читают его JSON выход, таких как приложение Agent SDK или пользовательский UI, построенный на основе Claude Code. Это позволяет этому вызывающему процессу приостановить Claude при вызове инструмента, собрать входные данные через его собственный интерфейс и возобновить с того же места. Claude Code соблюдает это значение только в неинтерактивном режиме с флагом -p. В интерактивных сеансах он логирует предупреждение и игнорирует результат hook.
Инструмент AskUserQuestion — это типичный случай: Claude хочет что-то спросить у пользователя, но нет терминала для ответа. Круговой путь работает так:
- Claude вызывает
AskUserQuestion. Срабатывает hookPreToolUse. - Hook возвращает
permissionDecision: "defer". Инструмент не выполняется. Процесс выходит сstop_reason: "tool_deferred"и отложенный вызов инструмента сохраняется в транскрипте. - Вызывающий процесс читает
deferred_tool_useиз результата SDK, выводит вопрос в своём UI и ждёт ответа. - Вызывающий процесс запускает
claude -p --resume <session-id>. Тот же вызов инструмента срабатываетPreToolUseснова. - Hook возвращает
permissionDecision: "allow"с ответом вupdatedInput. Инструмент выполняется и Claude продолжает.
deferred_tool_use содержит id, name и input инструмента. input — это параметры, которые Claude сгенерировал для вызова инструмента, захваченные перед выполнением:
cleanupPeriodDays, которая удаляет файлы сеанса через 30 дней по умолчанию. Если ответ не готов при возобновлении, hook может вернуть "defer" снова и процесс выходит так же. Вызывающий процесс управляет тем, когда разорвать цикл, в конечном итоге возвращая "allow" или "deny" из hook.
"defer" работает только когда Claude делает один вызов инструмента в ходе. Если Claude делает несколько вызовов инструментов одновременно, "defer" игнорируется с предупреждением и инструмент проходит через обычный поток разрешений. Ограничение существует потому что возобновление может только повторно запустить один инструмент: нет способа отложить один вызов из пакета без оставления других неразрешённых.
Если отложенный инструмент больше не доступен при возобновлении, процесс выходит с stop_reason: "tool_deferred_unavailable" и is_error: true перед срабатыванием hook. Это происходит когда MCP сервер, который предоставил инструмент, не подключен для возобновлённого сеанса. Полезная нагрузка deferred_tool_use всё ещё включена, чтобы вы могли идентифицировать, какой инструмент исчез.
--resume восстанавливает режим разрешения, который был активен при отложении инструмента, поэтому вам не нужно передавать --permission-mode снова. Исключения — это plan и bypassPermissions, которые никогда не переносятся. Передача --permission-mode явно при возобновлении переопределяет восстановленное значение.PermissionRequest
Запускается при показе пользователю диалога разрешения. Используйте PermissionRequest decision control для разрешения или отклонения от имени пользователя. Совпадает с именем инструмента, те же значения, что и PreToolUse.PermissionRequest input
PermissionRequest hooks получают поляtool_name и tool_input как PreToolUse hooks, но без tool_use_id. Опциональный массив permission_suggestions содержит параметры “всегда разрешить”, которые пользователь обычно видит в диалоге разрешения. Разница в том, когда срабатывает hook: PermissionRequest hooks запускаются при показе диалога разрешения пользователю, в то время как PreToolUse hooks запускаются перед выполнением инструмента независимо от статуса разрешения.
PermissionRequest decision control
HooksPermissionRequest могут разрешить или отклонить запросы разрешения. В дополнение к JSON полям выхода, доступным для всех hooks, ваш скрипт hook может вернуть объект decision с этими полями, специфичными для события:
Permission update entries
Поле выходаupdatedPermissions и поле входа permission_suggestions оба используют один и тот же массив объектов записей. Каждая запись имеет type, который определяет её другие поля, и destination, который управляет тем, где применяется изменение.
setMode с bypassPermissions только вступает в силу, если сеанс был запущен с режимом обхода, уже доступным: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions или permissions.defaultMode: "bypassPermissions" в настройках, и режим не отключен permissions.disableBypassPermissionsMode. В противном случае обновление — это no-op. bypassPermissions никогда не сохраняется как defaultMode независимо от destination.destination на каждой записи определяет, остаётся ли изменение в памяти или сохраняется в файл настроек.
Hook может вывести одно из
permission_suggestions, которые он получил, как свой собственный выход updatedPermissions, что эквивалентно выбору пользователем этого параметра “всегда разрешить” в диалоге.
PostToolUse
Запускается сразу после успешного завершения инструмента. Совпадает с именем инструмента, те же значения, что и PreToolUse.PostToolUse input
HooksPostToolUse срабатывают после того, как инструмент уже выполнился успешно. Входные данные включают как tool_input, аргументы, отправленные инструменту, так и tool_response, результат, который он вернул. Точная схема для обоих зависит от инструмента.
PostToolUse decision control
HooksPostToolUse могут предоставить обратную связь Claude после выполнения инструмента. В дополнение к JSON полям выхода, доступным для всех hooks, ваш скрипт hook может вернуть эти поля, специфичные для события:
Пример ниже заменяет выход вызова
Bash. Значение замены соответствует форме выхода инструмента Bash:
PostToolUseFailure
Запускается при сбое выполнения инструмента: инструмент выбросил ошибку или инструмент MCP вернул результат ошибки. Используйте это для логирования сбоев, отправки оповещений или предоставления исправляющей обратной связи Claude. Совпадает с именем инструмента, те же значения, что и PreToolUse.Это событие не срабатывает для вызовов инструментов, отклонённых перед выполнением: неизвестное имя инструмента, входные данные, которые не проходят проверку схемы или инструмента, или отклонение разрешения. Отклонения проверки возвращаются как результаты
tool_use_error и происходят перед запуском hooks, поэтому они не срабатывают ни PreToolUse ни этим событием. Отклонения разрешения срабатывают PreToolUse, но не этим событием; см. PermissionDenied.PostToolUseFailure input
PostToolUseFailure hooks получают те же поляtool_name и tool_input, что и PostToolUse, вместе с информацией об ошибке как верхнеуровневые поля:
PostToolUseFailure decision control
HooksPostToolUseFailure могут предоставить контекст Claude после сбоя инструмента. В дополнение к JSON полям выхода, доступным для всех hooks, ваш скрипт hook может вернуть эти поля, специфичные для события:
PostToolBatch
Запускается один раз после разрешения каждого вызова инструмента в пакете, перед отправкой Claude Code следующего запроса модели.PostToolUse срабатывает один раз за инструмент, что означает, что он срабатывает одновременно, когда Claude делает параллельные вызовы инструментов. PostToolBatch срабатывает ровно один раз со всем пакетом, поэтому это правильное место для внедрения контекста, который зависит от набора инструментов, которые запустились, а не от какого-либо одного инструмента. Нет фильтра для этого события.
PostToolBatch input
В дополнение к общим полям входа, PostToolBatch hooks получаютtool_calls, массив, описывающий каждый вызов инструмента в пакете:
tool_response содержит то же содержимое, которое модель получает в соответствующем блоке tool_result. Значение — это сериализованная строка или массив блоков содержимого, ровно как инструмент его выдал. Для Read это означает текст с префиксом номера строки, а не необработанное содержимое файла. Ответы могут быть большими, поэтому анализируйте только нужные вам поля.
Форма
tool_response отличается от PostToolUse. PostToolUse передаёт структурированный объект Output инструмента, такой как {filePath: "...", success: true} для Write; PostToolBatch передаёт сериализованное содержимое tool_result, которое видит модель.PostToolBatch decision control
HooksPostToolBatch могут внедрить контекст для Claude. В дополнение к JSON полям выхода, доступным для всех hooks, ваш скрипт hook может вернуть эти поля, специфичные для события:
decision: "block" или continue: false останавливает агентный цикл перед следующим вызовом модели.
PermissionDenied
Запускается когда классификатор auto mode отклоняет вызов инструмента. Этот hook срабатывает только в auto mode: он не запускается когда вы вручную отклоняете диалог разрешения, когда hookPreToolUse блокирует вызов или когда совпадает правило deny. Используйте это для логирования отказов классификатора, корректировки конфигурации или сообщения модели, что она может повторить попытку вызова инструмента.
Совпадает с именем инструмента, те же значения, что и PreToolUse.
PermissionDenied input
В дополнение к общим полям входа, PermissionDenied hooks получаютtool_name, tool_input, tool_use_id и reason.
PermissionDenied decision control
PermissionDenied hooks могут сообщить модели, что она может повторить попытку отклонённого вызова инструмента. Верните JSON объект сhookSpecificOutput.retry, установленным на true:
retry равно true, Claude Code добавляет сообщение в разговор, говорящее модели, что она может повторить попытку вызова инструмента. Отказ сам по себе не отменяется. Если ваш hook не возвращает JSON или возвращает retry: false, отказ остаётся и модель получает исходное сообщение об отклонении.
Notification
Запускается при отправке Claude Code уведомлений. Совпадает с типом уведомления. Опустите фильтр для запуска hooks для всех типов уведомлений.
Типы
agent_needs_input и agent_completed требуют Claude Code v2.1.198 или позже.
Используйте отдельные фильтры для запуска разных обработчиков в зависимости от типа уведомления. Эта конфигурация запускает скрипт оповещения, специфичный для разрешения, когда Claude нуждается в одобрении разрешения, и другое уведомление, когда Claude был неактивен:
Notification input
В дополнение к общим полям входа, Notification hooks получаютmessage с текстом уведомления, опциональный title и notification_type, указывающий, какой тип сработал.
systemMessage применяются.
SubagentStart
Запускается при порождении Claude Code subagent через инструмент Agent. Поддерживает фильтры для фильтрации по имени типа агента. Для встроенных агентов это имя агента, такое какgeneral-purpose, Explore или Plan. Для пользовательских subagents, это поле name из frontmatter агента, а не имя файла.
Для subagents, поставляемых плагином, тип агента — это идентификатор, ограниченный плагином, такой как my-plugin:reviewer, а не простое имя frontmatter. Двоеточие помещает имя, ограниченное плагином, на путь регулярного выражения, поэтому закрепите фильтр с ^ и $ для точного совпадения: ^my-plugin:reviewer$.
SubagentStart input
В дополнение к общим полям входа, SubagentStart hooks получаютagent_id с уникальным идентификатором для subagent и agent_type с именем агента, которое фильтр использует для фильтрации.
SubagentStop
Запускается при завершении ответа Claude Code subagent. Совпадает с типом агента, те же значения, что и SubagentStart.SubagentStop input
В дополнение к общим полям входа, SubagentStop hooks получаютstop_hook_active, agent_id, agent_type, agent_transcript_path и last_assistant_message. Поле agent_type — это значение, используемое для фильтрации фильтра. transcript_path — это транскрипт основного сеанса, в то время как agent_transcript_path — это собственный транскрипт subagent, хранящийся в вложенной папке subagents/. Поле last_assistant_message содержит текстовое содержимое финального ответа subagent, поэтому hooks могут получить к нему доступ без анализа файла транскрипта.
SubagentStop hooks также получают массивы background_tasks и session_crons, описанные в разделе Stop input, доступные в Claude Code v2.1.145 или позже. Оба массива ограничены родительским сеансом, а не subagent.
hookSpecificOutput.additionalContext с hookEventName, установленным на "SubagentStop", для ненаправленной обратной связи, которая держит subagent работающим. Возврат decision: "block" с reason держит subagent работающим и доставляет reason subagent как его следующую инструкцию. Чтобы внедрить контекст в родительский сеанс после возврата subagent, используйте hook PostToolUse на инструменте Agent вместо этого.
TaskCreated
Запускается при создании задачи через инструментTaskCreate. Используйте это для обеспечения соглашений об именовании, требования описаний задач или предотвращения создания определённых задач.
Когда hook TaskCreated выходит с кодом 2, задача не создаётся и сообщение stderr передаётся обратно модели как обратная связь. Чтобы полностью остановить товарища вместо его повторного запуска, верните JSON с {"continue": false, "stopReason": "..."}. TaskCreated hooks не поддерживают фильтры и срабатывают при каждом вхождении.
TaskCreated input
В дополнение к общим полям входа, TaskCreated hooks получаютtask_id, task_subject и опционально task_description, teammate_name и team_name.
TaskCreated decision control
TaskCreated hooks поддерживают два способа управления созданием задачи:- Exit code 2: задача не создаётся и сообщение stderr передаётся обратно модели как обратная связь.
- JSON
{"continue": false, "stopReason": "..."}: полностью останавливает товарища, соответствуя поведению hookStop.stopReasonпоказывается пользователю.
TaskCompleted
Запускается при отметке задачи как завершённой. Это срабатывает в двух ситуациях: когда любой агент явно отмечает задачу как завершённую через инструмент TaskUpdate, или когда товарищ agent team завершает свой ход с незавершёнными задачами. Используйте это для обеспечения критериев завершения, таких как прохождение тестов или проверок линтинга перед закрытием задачи. Когда hookTaskCompleted выходит с кодом 2, задача не отмечается как завершённая и сообщение stderr передаётся обратно модели как обратная связь. Чтобы полностью остановить товарища вместо его повторного запуска, верните JSON с {"continue": false, "stopReason": "..."}. TaskCompleted hooks не поддерживают фильтры и срабатывают при каждом вхождении.
TaskCompleted input
В дополнение к общим полям входа, TaskCompleted hooks получаютtask_id, task_subject и опционально task_description, teammate_name и team_name.
TaskCompleted decision control
TaskCompleted hooks поддерживают два способа управления завершением задачи:- Exit code 2: задача не отмечается как завершённая и сообщение stderr передаётся обратно модели как обратная связь.
- JSON
{"continue": false, "stopReason": "..."}: полностью останавливает товарища, соответствуя поведению hookStop.stopReasonпоказывается пользователю.
Stop
Запускается при завершении ответа основного агента Claude Code. Не запускается, если остановка произошла из-за прерывания пользователя. Ошибки API срабатывают StopFailure вместо этого.Stop input
В дополнение к общим полям входа, Stop hooks получаютstop_hook_active, last_assistant_message, background_tasks и session_crons. Поле stop_hook_active равно true, когда Claude Code уже продолжает в результате stop hook. Проверьте это значение или обработайте транскрипт, чтобы предотвратить блокировку на условии, которое никогда не разрешится. Claude Code переопределяет hook и заканчивает ход после 8 последовательных блокировок.
Поле last_assistant_message содержит текстовое содержимое финального ответа Claude, поэтому hooks могут получить к нему доступ без анализа файла транскрипта. Для hooks, которые действуют на только что завершённый ход, такие как hooks для чтения вслух или уведомления, используйте это поле, а не читайте transcript_path: файл транскрипта не гарантирует включение финального сообщения в момент Stop на всех версиях.
Массивы background_tasks и session_crons, доступные в Claude Code v2.1.145 или позже, позволяют hooks различать “сеанс завершён” от “сеанс приостановлен в ожидании фоновой работы для его пробуждения”. Оба массива присутствуют, когда реестр задач доступен, и пусты, когда ничего не выполняется или не запланировано.
Каждая запись в background_tasks описывает одну выполняемую задачу и использует эти поля:
Каждая запись в
session_crons описывает одно запланированное пробуждение, ограниченное сеансом, полученное из CronCreate, ScheduleWakeup и /loop:
Этот пример показывает Stop input с одной выполняемой shell задачей и одним повторяющимся cron:
Stop decision control
HooksStop и SubagentStop могут управлять тем, продолжает ли Claude. В дополнение к JSON полям выхода, доступным для всех hooks, ваш скрипт hook может вернуть эти поля, специфичные для события:
additionalContext, когда hook работает как задумано и даёт Claude руководство, такое как “запустите набор тестов перед завершением”. Это держит разговор идущим через те же защиты цикла, что и decision: "block", а именно вход stop_hook_active и ограничение 8 последовательных продолжений, но транскрипт помечает его как Stop hook feedback и уведомление об ошибке hook не показывается:
StopFailure
Запускается вместо Stop когда ход заканчивается из-за ошибки API. Выход и код выхода игнорируются. Используйте это для логирования сбоев, отправки оповещений или принятия действий восстановления, когда Claude не может завершить ответ из-за ограничений скорости, проблем аутентификации или других ошибок API.StopFailure input
В дополнение к общим полям входа, StopFailure hooks получаютerror, опциональные error_details и last_assistant_message. Поле error определяет тип ошибки и используется для фильтрации фильтра.
TeammateIdle
Запускается когда товарищ agent team собирается перейти в режим ожидания после завершения своего хода. Используйте это для обеспечения качественных ворот перед остановкой работы товарища, такие как требование прохождения проверок линтинга или проверка существования выходных файлов. Когда hookTeammateIdle выходит с кодом 2, товарищ получает сообщение stderr как обратную связь и продолжает работать вместо перехода в режим ожидания. Чтобы полностью остановить товарища вместо его повторного запуска, верните JSON с {"continue": false, "stopReason": "..."}. TeammateIdle hooks не поддерживают фильтры и срабатывают при каждом вхождении.
TeammateIdle input
В дополнение к общим полям входа, TeammateIdle hooks получаютteammate_name и team_name.
TeammateIdle decision control
TeammateIdle hooks поддерживают два способа управления поведением товарища:- Exit code 2: товарищ получает сообщение stderr как обратную связь и продолжает работать вместо перехода в режим ожидания.
- JSON
{"continue": false, "stopReason": "..."}: полностью останавливает товарища, соответствуя поведению hookStop.stopReasonпоказывается пользователю.
ConfigChange
Запускается при изменении файла конфигурации во время сеанса. Используйте это для аудита изменений настроек, обеспечения политик безопасности или блокировки несанкционированных изменений файлов конфигурации. ConfigChange hooks срабатывают для изменений файлов настроек, управляемых параметров политики и файлов skills. Полеsource во входных данных говорит вам, какой тип конфигурации изменился, и опциональное поле file_path предоставляет путь к изменённому файлу.
Фильтр фильтрует по источнику конфигурации:
Этот пример логирует все изменения конфигурации для аудита безопасности:
ConfigChange input
В дополнение к общим полям входа, ConfigChange hooks получаютsource и опционально file_path. Поле source указывает, какой тип конфигурации изменился, и file_path предоставляет путь к конкретному изменённому файлу.
ConfigChange decision control
ConfigChange hooks могут блокировать применение изменений конфигурации. Используйте exit code 2 или JSONdecision для предотвращения изменения. При блокировке новые параметры не применяются к запущенному сеансу.
policy_settings не могут быть заблокированы. Hooks всё ещё срабатывают для источников policy_settings, поэтому вы можете использовать их для аудита логирования, но любое решение блокировки игнорируется. Это гарантирует, что управляемые предприятием параметры всегда вступают в силу.
CwdChanged
Запускается при изменении рабочего каталога во время сеанса, например когда Claude выполняет командуcd. Используйте это для реакции на изменения каталога: перезагрузка переменных окружения, активация специфичных для проекта цепочек инструментов или автоматический запуск скриптов настройки. Объединяется с FileChanged для инструментов, таких как direnv, которые управляют окружением для каждого каталога.
CwdChanged hooks имеют доступ к CLAUDE_ENV_FILE. Переменные, написанные в этот файл, сохраняются в последующих командах Bash для сеанса, как и в SessionStart hooks.
CwdChanged не поддерживает фильтры и срабатывает при каждом изменении каталога.
CwdChanged input
В дополнение к общим полям входа, CwdChanged hooks получаютold_cwd и new_cwd.
CwdChanged output
В дополнение к JSON полям выхода, доступным для всех hooks, CwdChanged hooks могут вернутьwatchPaths для динамической установки, какие пути файлов FileChanged отслеживает:
CwdChanged hooks не имеют управления решением. Они не могут блокировать изменение каталога.
FileChanged
Запускается при изменении отслеживаемого файла на диске. Полезно для перезагрузки переменных окружения при изменении файлов конфигурации проекта. Полеmatcher для этого события служит двум целям:
- Построение списка наблюдения: значение разделяется на
|и каждый сегмент регистрируется как буквальное имя файла в рабочем каталоге, поэтому".envrc|.env"отслеживает ровно эти два файла. Regex шаблоны здесь не полезны: значение, такое как^\.env, отслеживало бы файл буквально названный^\.env. - Фильтрация, какие hooks запускаются: когда отслеживаемый файл изменяется, то же значение фильтрует, какие группы hook запускаются, используя стандартные правила фильтра против базового имени изменённого файла.
CLAUDE_ENV_FILE. Переменные, написанные в этот файл, сохраняются в последующих командах Bash для сеанса, как и в SessionStart hooks.
FileChanged input
В дополнение к общим полям входа, FileChanged hooks получаютfile_path и event.
FileChanged output
В дополнение к JSON полям выхода, доступным для всех hooks, FileChanged hooks могут вернутьwatchPaths для динамического обновления, какие пути файлов отслеживаются:
FileChanged hooks не имеют управления решением. Они не могут блокировать изменение файла от возникновения.
WorktreeCreate
Запускается при создании worktree, либо изclaude --worktree, либо из subagent, использующего isolation: "worktree". По умолчанию Claude Code создаёт изолированную рабочую копию с помощью git worktree. Настройка hook WorktreeCreate заменяет это поведение git по умолчанию, позволяя вам использовать другую систему контроля версий, такую как SVN, Perforce или Mercurial.
Потому что hook заменяет поведение по умолчанию полностью, .worktreeinclude не обрабатывается. Если вам нужно скопировать локальные файлы конфигурации, такие как .env, в новый worktree, сделайте это внутри вашего скрипта hook.
Hook должен вернуть путь к созданному worktree каталогу. Claude Code использует этот путь как рабочий каталог для изолированного сеанса. См. WorktreeCreate output для того, как каждый тип hook возвращает путь.
Этот пример создаёт рабочую копию SVN и выводит путь для использования Claude Code. Замените URL репозитория на свой:
name из JSON входа на stdin, проверяет свежую копию в новый каталог и выводит путь каталога. echo на последней строке — это то, что Claude Code читает как путь worktree. Перенаправьте любой другой выход на stderr, чтобы он не мешал пути.
WorktreeCreate input
В дополнение к общим полям входа, WorktreeCreate hooks получают полеname. Это идентификатор slug для нового worktree, либо указанный пользователем, либо автоматически сгенерированный, например bold-oak-a3f2.
WorktreeCreate output
WorktreeCreate hooks не используют стандартную модель решения разрешить/заблокировать. Вместо этого успех или сбой hook определяет результат. Hook должен вернуть путь к созданному worktree каталогу:- Command hooks (
type: "command"): выводят путь как последнюю непустую строку stdout. Claude Code удаляет коды ANSI перед чтением этой строки, поэтому баннеры запуска оболочки, выведенные перед вашимecho, игнорируются. Перенаправьте любой другой выход hook на stderr. - HTTP hooks (
type: "http"): возвращают{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }в теле ответа.
-p он зависал примерно на 30 секунд перед выходом с кодом 0.
WorktreeRemove
Запускается при удалении worktree, либо при выходе из сеанса--worktree и выборе его удаления, либо при завершении subagent с isolation: "worktree". Это аналог очистки для WorktreeCreate.
Для git-based worktrees Claude Code обрабатывает очистку автоматически с помощью git worktree remove. Если вы настроили hook WorktreeCreate для системы контроля версий, не основанной на git, объедините его с hook WorktreeRemove для обработки очистки. Без него каталог worktree остаётся на диске.
Claude Code передаёт путь, который WorktreeCreate вывел на stdout, как worktree_path во входных данных hook. Этот пример читает этот путь и удаляет каталог:
WorktreeRemove input
В дополнение к общим полям входа, WorktreeRemove hooks получают полеworktree_path, которое является абсолютным путём к удаляемому worktree.
PreCompact
Запускается перед тем, как Claude Code собирается запустить операцию компактирования. Значение фильтра указывает, было ли компактирование запущено вручную или автоматически:
Exit code 2 блокирует компактирование. Для ручного
/compact сообщение stderr показывается пользователю. Вы также можете заблокировать, возвращая JSON с "decision": "block".
Блокировка автоматического компактирования имеет разные эффекты в зависимости от того, когда оно срабатывает. Если компактирование было запущено проактивно перед лимитом контекста, Claude Code пропускает его и разговор продолжается некомпактированным. Если компактирование было запущено для восстановления от ошибки лимита контекста, уже возвращённой API, основная ошибка выходит на поверхность и текущий запрос не удаётся.
PreCompact input
В дополнение к общим полям входа, PreCompact hooks получаютtrigger и custom_instructions. Для manual, custom_instructions содержит то, что пользователь передаёт в /compact. Для auto, custom_instructions пусто.
PostCompact
Запускается после завершения Claude Code операции компактирования. Используйте это событие для реакции на новое компактирован состояние, например для логирования сгенерированного резюме или обновления внешнего состояния. Те же значения фильтра применяются как дляPreCompact:
PostCompact input
В дополнение к общим полям входа, PostCompact hooks получаютtrigger и compact_summary. Поле compact_summary содержит резюме разговора, сгенерированное операцией компактирования.
SessionEnd
Запускается при завершении сеанса Claude Code. Полезно для задач очистки, логирования статистики сеанса или сохранения состояния сеанса. Поддерживает фильтры для фильтрации по причине выхода. Полеreason во входных данных hook указывает, почему сеанс закончился:
SessionEnd input
В дополнение к общим полям входа, SessionEnd hooks получают полеreason, указывающее, почему сеанс закончился. См. таблицу выше для всех значений.
/clear и переключению сеансов через интерактивный /resume. Если hook нуждается в большем времени, установите поле timeout в конфигурации hook. Общий бюджет автоматически повышается до наибольшего per-hook таймаута, настроенного в файлах настроек, до 60 секунд. Таймауты, установленные на hooks, предоставленные плагинами, не повышают бюджет. Чтобы явно переопределить бюджет, установите переменную окружения CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS в миллисекундах.
Elicitation
Запускается, когда MCP сервер запрашивает ввод пользователя во время выполнения задачи. По умолчанию Claude Code показывает интерактивный диалог для ответа пользователя. Hooks могут перехватить этот запрос и ответить программно, полностью пропустив диалог. Поле фильтра совпадает с именем MCP сервера.Elicitation input
В дополнение к общим полям входа, Elicitation hooks получаютmcp_server_name, message и опциональные mode, url, elicitation_id и requested_schema поля.
Для form-mode elicitation (наиболее распространённый случай):
Elicitation output
Чтобы ответить программно без показа диалога, верните JSON объект сhookSpecificOutput:
Exit code 2 отклоняет elicitation и показывает stderr пользователю.
ElicitationResult
Запускается после ответа пользователя на MCP elicitation. Hooks могут наблюдать, изменять или блокировать ответ перед его отправкой обратно на MCP сервер. Поле фильтра совпадает с именем MCP сервера.ElicitationResult input
В дополнение к общим полям входа, ElicitationResult hooks получаютmcp_server_name, action и опциональные mode, elicitation_id и content поля.
ElicitationResult output
Чтобы переопределить ответ пользователя, верните JSON объект сhookSpecificOutput:
Exit code 2 блокирует ответ, изменяя эффективное действие на
decline.
Prompt-based hooks
В дополнение к command, HTTP и MCP tool hooks, Claude Code поддерживает prompt-based hooks (type: "prompt"), которые используют LLM для оценки разрешения или блокировки действия, и agent hooks (type: "agent"), которые порождают агентного верификатора с доступом к инструментам. Не все события поддерживают каждый тип hook.
События, которые поддерживают все пять типов hook (command, http, mcp_tool, prompt и agent):
PermissionDeniedPermissionRequestPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
command, http и mcp_tool hooks, но не prompt или agent:
ConfigChangeCwdChangedElicitationElicitationResultFileChangedInstructionsLoadedNotificationPostCompactPreCompactSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart и Setup поддерживают command и mcp_tool hooks. Они не поддерживают http, prompt или agent hooks.
How prompt-based hooks work
Вместо выполнения команды Bash, prompt-based hooks:- Отправляют входные данные hook и вашу подсказку модели Claude, Haiku по умолчанию
- LLM отвечает структурированным JSON, содержащим решение
- Claude Code автоматически обрабатывает решение
Prompt hook configuration
Установитеtype на "prompt" и предоставьте строку prompt вместо command. Используйте заполнитель $ARGUMENTS для внедрения данных JSON входа hook в текст вашей подсказки. Claude Code отправляет объединённую подсказку и входные данные быстрой модели Claude, которая возвращает JSON решение.
Этот hook Stop просит LLM оценить, должен ли Claude остановиться перед разрешением Claude закончить:
Response schema
LLM должен ответить JSON, содержащим:
Что происходит при
ok: false, зависит от события:
StopиSubagentStop: причина передаётся обратно Claude как его следующая инструкция и ход продолжаетсяPreToolUse: вызов инструмента отклоняется и причина возвращается Claude как ошибка инструмента, эквивалентноpermissionDecision: "deny"из command hookPostToolUse: по умолчанию ход заканчивается и причина появляется в чате как строка предупреждения. УстановитеcontinueOnBlock: trueдля передачи причины обратно Claude и продолжения хода вместо этогоPostToolBatch,UserPromptSubmitиUserPromptExpansion: ход заканчивается и причина появляется как строка предупреждения. Эти события заканчивают ход наdecision: "block"независимо отcontinuePostToolUseFailure,TaskCreatedиTaskCompleted: причина возвращается Claude как ошибка инструмента, аналогичноPreToolUseTeammateIdle: по умолчанию товарищ по команде останавливается и причина появляется как строка предупреждения. УстановитеcontinueOnBlock: trueдля передачи причины обратно товарищу по команде и продолжения его работы вместо этогоPermissionRequest:ok: falseне имеет эффекта. Чтобы отклонить одобрение из hook, используйте command hook, возвращающийhookSpecificOutput.decision.behavior: "deny"PermissionDenied:ok: falseне имеет эффекта, потому что отказ уже произошёл. Единственный результат, который это событие читает, этоhookSpecificOutput.retry, который prompt и agent hooks не могут установить. Они запускаются на этом событии, но их результат отбрасывается. Используйте command hook для возвратаretry
Check multiple conditions before stopping
Этот hookStop использует подробную подсказку для проверки трёх условий перед разрешением Claude остановиться. Hooks SubagentStop используют тот же формат для оценки, должен ли subagent остановиться. Если "ok" равно false, Claude продолжает работать с предоставленной причиной как своей следующей инструкцией:
Agent-based hooks
Agent-based hooks (type: "agent") похожи на prompt-based hooks, но с многооборотным доступом к инструментам. Вместо одного вызова LLM, agent hook порождает subagent, который может читать файлы, искать код и проверять кодовую базу для проверки условий. Agent hooks поддерживают те же события, что и prompt-based hooks.
How agent hooks work
Когда срабатывает agent hook:- Claude Code порождает subagent с вашей подсказкой и JSON входом hook
- Subagent может использовать инструменты, такие как Read, Grep и Glob, для исследования
- После до 50 оборотов subagent возвращает структурированное решение
{ "ok": true/false } - Claude Code обрабатывает решение так же, как prompt hook
Agent hook configuration
Установитеtype на "agent" и предоставьте строку prompt. Поля конфигурации те же, что и prompt hooks, с более длинным таймаутом по умолчанию:
Схема ответа та же, что и prompt hooks:
{ "ok": true } для разрешения или { "ok": false, "reason": "..." } для блокировки.
Этот hook Stop проверяет, что все модульные тесты проходят перед разрешением Claude закончить:
Запуск hooks в фоне
По умолчанию hooks блокируют выполнение Claude до их завершения. Для долгоживущих задач, таких как развёртывания, наборы тестов или вызовы внешних API, установите"async": true для запуска hook в фоне, пока Claude продолжает работать. Асинхронные hooks не могут блокировать или управлять поведением Claude: поля ответа, такие как decision, permissionDecision и continue, не имеют эффекта, потому что действие, которое они контролировали, уже завершено.
Настройка асинхронного hook
Добавьте"async": true к конфигурации command hook для запуска его в фоне без блокировки Claude. Это поле доступно только на hooks type: "command".
Этот hook запускает скрипт тестирования после каждого вызова инструмента Write. Claude продолжает работать немедленно, пока run-tests.sh выполняется до 120 секунд. Когда скрипт завершается, его выход доставляется на следующий ход разговора:
timeout устанавливает максимальное время в секундах для фонового процесса. Если не указано, асинхронные hooks используют тот же 10-минутный таймаут по умолчанию, что и синхронные hooks.
Как выполняются асинхронные hooks
Когда срабатывает асинхронный hook, Claude Code запускает процесс hook и немедленно продолжает без ожидания его завершения. Hook получает те же JSON входные данные через stdin, что и синхронный hook. После выхода фонового процесса, если hook произвёл JSON ответ с полемadditionalContext, это содержимое доставляется Claude как контекст на следующем ходу разговора. Поле systemMessage показывается вам, а не Claude.
Claude Code проверяет, что JSON ответ соответствует той же схеме выходных данных, что и синхронные hooks, и отбрасывает любое поле, значение которого имеет неправильный тип, например systemMessage, который не является строкой, вместо его доставки. Запустите с --debug для просмотра предупреждения, называющего каждое отброшенное поле. До версии v2.1.202 неправильно сформированный JSON выход из асинхронного hook мог привести к сбою сеанса, и сбой повторялся каждый раз при возобновлении сеанса.
Уведомления о завершении асинхронного hook подавляются по умолчанию. Чтобы их увидеть, включите подробный режим с помощью Ctrl+O или запустите Claude Code с --verbose.
Запуск тестов после изменения файлов
Этот hook запускает набор тестов в фоне всякий раз, когда Claude пишет файл, затем сообщает результаты обратно Claude при завершении тестов. Сохраните этот скрипт в.claude/hooks/run-tests-async.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x:
.claude/settings.json в корне вашего проекта. Флаг async: true позволяет Claude продолжать работу, пока тесты запускаются:
Ограничения
Асинхронные hooks имеют несколько ограничений по сравнению с синхронными hooks:- Только hooks
type: "command"поддерживаютasync. Prompt-based hooks не могут запускаться асинхронно. - Асинхронные hooks не могут блокировать вызовы инструментов или возвращать решения. К моменту завершения hook действие, вызвавшее его, уже произошло.
- Выход hook доставляется на следующий ход разговора. Если сеанс неактивен, ответ ждёт до следующего взаимодействия пользователя. Исключение: hook
asyncRewake, который выходит с кодом 2, пробуждает Claude немедленно даже когда сеанс неактивен. - Каждое выполнение создаёт отдельный фоновый процесс. Нет дедупликации между несколькими срабатываниями одного и того же асинхронного hook.
Соображения безопасности
Отказ от ответственности
Command hooks запускаются с полными разрешениями системного пользователя.Лучшие практики безопасности
Помните об этих практиках при написании hooks:- Проверяйте и санитизируйте входные данные: никогда не доверяйте входным данным вслепую
- Всегда заключайте переменные оболочки в кавычки: используйте
"$VAR"не$VAR - Блокируйте обход пути: проверяйте наличие
..в путях файлов - Используйте абсолютные пути: указывайте полные пути для скриптов. В форме exec используйте
${CLAUDE_PROJECT_DIR}и путь не требует кавычек. В форме shell оберните его в двойные кавычки - Пропускайте чувствительные файлы: избегайте
.env,.git/, ключей и т. д.
Windows PowerShell tool
На Windows вы можете запустить отдельные hooks в PowerShell, установив"shell": "powershell" на command hook. Hooks порождают PowerShell напрямую, поэтому это работает независимо от того, установлен ли CLAUDE_CODE_USE_POWERSHELL_TOOL. Claude Code автоматически обнаруживает pwsh.exe, исполняемый файл PowerShell 7 и более поздних версий, и переходит на powershell.exe для Windows PowerShell 5.1.
${CLAUDE_PROJECT_DIR} или $env:CLAUDE_PROJECT_DIR. Начиная с версии 2.1.198, Claude Code переписывает заполнители ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} и ${CLAUDE_PLUGIN_DATA} в команде PowerShell в форме shell в форму ${env:NAME} PowerShell, независимо от того, определён ли hook в settings.json, плагине или навыке. PowerShell затем разрешает значение из экспортированного окружения после анализа, поэтому заполнитель работает внутри строк в двойных кавычках, но не внутри строк в одинарных кавычках, где PowerShell никогда не расширяет переменные.
До версии 2.1.198 эта переписка применялась только к plugin hooks. В более ранних версиях hook в settings.json требует формы $env: или exec form, где ${CLAUDE_PROJECT_DIR} подставляется в каждый элемент args независимо от того, где определён hook.
Не пишите простое написание $CLAUDE_PROJECT_DIR в hook PowerShell. PowerShell анализирует его как неопределённую локальную переменную и разрешает её в $null, что оставляет путь скрипта без префикса корневого каталога проекта. Claude Code не переписывает эту форму; вместо этого она регистрирует предупреждение в debug log.
Пример ниже показывает hook в settings.json, который запускает скрипт проекта с формой $env:, которая работает на каждой версии:
Debug hooks
Детали выполнения hooks, включая информацию о том, какие hooks совпали, их коды выхода и полный stdout и stderr, записываются в файл отладочного журнала. Запустите Claude Code сclaude --debug-file <path> для записи журнала в известное расположение, или запустите claude --debug и прочитайте журнал в ~/.claude/debug/<session-id>.txt. Флаг --debug не выводит на терминал.
CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose для просмотра дополнительных строк логирования, таких как количество совпадений фильтра hook и совпадение запроса.
Для устранения неполадок распространённых проблем, таких как hooks, которые не срабатывают, бесконечные циклы Stop hook или ошибки конфигурации, см. Limitations and troubleshooting в руководстве. Для более широкого диагностического пошагового руководства, охватывающего /context, /doctor и приоритет параметров, см. Debug your config.