Жизненный цикл hook
Claude Code запускает hooks в определённых точках во время сеанса. Когда событие срабатывает и совпадает с фильтром, Claude Code передаёт JSON-контекст события вашему обработчику hook. Для command hooks входные данные поступают на stdin. Для HTTP hooks они поступают как тело POST-запроса. Ваш обработчик может затем проверить входные данные, выполнить действие и опционально вернуть решение. События срабатывают в трёх ритмах:- один раз за сеанс:
SessionStartиSessionEnd - один раз за ход:
UserPromptSubmit,StopиStopFailure - при каждом вызове инструмента внутри агентного цикла:
PreToolUseиPostToolUse, за исключением вызововEndConversation, которые пропускают оба
Как разрешается hook
Чтобы увидеть, как событие, фильтр и обработчик работают вместе, рассмотрим этот hookPreToolUse, который блокирует деструктивные команды оболочки.
- macOS/Linux
- Windows (PowerShell)
matcher сужает область до вызовов инструмента Bash и команд Monitor, а условие if сужает её дальше до подкоманд Bash, совпадающих с rm *, поэтому block-rm.sh запускается, только когда оба фильтра совпадают:permissionDecision со значением "deny", если она содержит rm -rf. Сохраните его в .claude/hooks/block-rm.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x .claude/hooks/block-rm.sh, чтобы Claude Code мог его запустить:jq, поэтому установите jq и убедитесь, что он находится в вашем PATH, перед попыткой их использования.Bash "rm -rf /tmp/build" с конфигурацией macOS/Linux. Вот что происходит:
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 причину.
Конфигурация
Хуки определяются в JSON-файлах настроек. Конфигурация имеет три уровня вложенности:- Выберите событие хука, на которое нужно реагировать, например
PreToolUseилиStop - Добавьте группу matcher, чтобы отфильтровать, когда он срабатывает, например «для инструмента Bash»
- Определите один или несколько обработчиков хука, которые будут запускаться при совпадении
На этой странице для каждого уровня используются определённые термины: событие хука — для точки жизненного цикла, группа matcher — для фильтра и обработчик хука — для запускаемой shell-команды, HTTP-эндпоинта, MCP-инструмента, промпта или агента. Слово «хук» само по себе обозначает функцию в целом.
Расположение хуков
Место, где вы определяете хук, определяет его область действия:
Облачные сессии не читают ваш локальный
~/.claude/settings.json. В самостоятельно размещённом окружении Claude Code также запускает хуки, которые оператор заранее поместил в ~/.claude/ хоста раннера, и запускает хуки из файла управляемых настроек образа раннера, когда этот файл входит в число управляемых источников, которые применяет Claude Code, что по умолчанию означает: только когда ни настройки, управляемые сервером, ни политика Claude Code, доставленная через MDM, не предоставляют управляемый уровень. О том, какие файлы настроек и плагины, а значит и какие хуки, попадают в облачную сессию, см. что переносится из вашей настройки.
Подробнее о разрешении файлов настроек см. настройки.
Хуки из файлов настроек, настроек управляемой политики и плагинов также запускаются внутри субагентов. Когда субагент вызывает инструмент, события инструментов, такие как PreToolUse и PostToolUse, запускают те же настроенные хуки, что и в основном диалоге, а входные данные содержат общие поля входных данных agent_id и agent_type, которые идентифицируют субагента.
Администраторы могут использовать allowManagedHooksOnly в управляемых настройках, чтобы ограничить, какие хуки запускаются:
- Ваши пользовательские, проектные, локальные хуки и хуки плагинов блокируются. Исключение составляют хуки из плагинов, принудительно включённых в
enabledPluginsуправляемых настроек - Claude Code также ограничивает ваши настройки
statusLineиfileSuggestionуправляемыми настройками - Claude Code также отключает плагины с источником
command, включая плагины, принудительно включённые вenabledPluginsуправляемых настроек, если толькоdisableCommandPluginSourcesявно не установлен вfalse. Источникиcommandтребуют Claude Code v2.1.229 или новее - Claude Code также блокирует команды
headersHelperмаркетплейсов, если толькоdisableCommandPluginSourcesявно не установлен вfalse, за исключением маркетплейса, который объявлен в самих управляемых настройках
allowManagedHooksOnly.
Записи хуков объединяются между уровнями настроек, а не заменяют друг друга: пользовательские, проектные и локальные настройки добавляют собственные хуки, не удаляя управляемые, а настройка disableAllHooks не может отключить управляемые хуки, если задана вне управляемых настроек.
Списки разрешённых URL для HTTP-хуков применяются к хукам из любого источника, включая настройки управляемой политики:
allowedHttpHookUrls: если задан на любом уровне настроек, Claude Code запускает обработчик HTTP-хука, только если его URL соответствует объединённому списку разрешённыхhttpHookAllowedEnvVars: если задан, Claude Code подставляет в заголовки хуков только переменные окружения из этого списка
Шаблоны matcher
Полеmatcher фильтрует, когда срабатывают хуки. То, как вычисляется matcher, зависит от содержащихся в нём символов:
Matcher, вычисляемый как регулярное выражение, проверяется с помощью
RegExp.prototype.test в JavaScript, который успешно срабатывает при совпадении в любом месте значения. Edit.* соответствует и Edit, и NotebookEdit; оберните шаблон в ^ и $, как в ^Edit$, если вам нужно совпадение со всей строкой.
FileChanged и StopFailure используют более узкий набор символов для точного совпадения: только буквы, цифры, _ и |. Дефис, пробел или запятая в matcher для этих двух событий оставляют его на пути регулярного выражения, и альтернативы разделяет только |. Все остальные события с поддержкой matcher в следующей таблице принимают | или ,.
Событие FileChanged не следует этим правилам при построении списка отслеживаемых файлов. См. FileChanged.
Каждый тип события сопоставляется по своему полю:
Сопоставление
StopFailure по cloud_credential_error требует Claude Code v2.1.267 или новее — первой версии, которая сообщает об ошибках загрузки учётных данных с этим значением, а не с server_error или unknown.
Для большинства событий Claude Code вычисляет matcher по полю из входных данных JSON, которые он отправляет вашему хуку через stdin. Для событий инструментов это поле tool_name. Для PreModelSwitch и PostModelSwitch Claude Code вычисляет matcher по каноническому имени, которое он выводит из to_model, как описано в разделе PreModelSwitch. В разделе каждого события хука перечислен полный набор значений matcher и схема входных данных для этого события.
Этот пример запускает скрипт линтинга только тогда, когда Claude записывает или редактирует файл:
matcher к событию без поддержки matcher, оно будет молча проигнорировано.
Для событий инструментов можно фильтровать точнее, задав поле if в отдельных обработчиках хуков. if использует синтаксис правил разрешений для сопоставления имени инструмента и аргументов вместе, поэтому "Bash(git *)" запускается, когда любая подкоманда во входных данных Bash соответствует git *, а "Edit(*.ts)" запускается только для файлов TypeScript.
Сопоставление MCP-инструментов
Инструменты MCP-серверов отображаются как обычные инструменты в событиях инструментов (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), поэтому их можно сопоставлять так же, как любое другое имя инструмента.
MCP-инструменты следуют шаблону именования mcp__<server>__<tool>, например:
mcp__memory__create_entities: инструмент создания сущностей сервера Memorymcp__filesystem__read_file: инструмент чтения файла сервера Filesystemmcp__github__search_repositories: инструмент поиска сервера GitHub
.* к префиксу сервера. .* обязателен: matcher вроде mcp__memory или mcp__brave-search содержит только символы точного совпадения, поэтому он сравнивается как точная строка и не соответствует ни одному инструменту.
mcp__memory__.*соответствует всем инструментам сервераmemorymcp__brave-search__.*соответствует всем инструментам сервера, имя которого содержит дефисmcp__.*__write.*соответствует любому инструменту любого сервера, имя которого начинается сwrite
mcp__plugin_<plugin-name>_<server-name>__<tool>. Matcher, написанный для голого ключа сервера, никогда не срабатывает для этих инструментов. Для плагина с именем my-plugin, который поставляет сервер под ключом db, инструмент query отображается как mcp__plugin_my-plugin_db__query, поэтому matcher для всех инструментов этого сервера — mcp__plugin_my-plugin_db__.*. Используйте то же имя инструмента с областью в поле if обработчика. О том, как строится имя с областью, см. MCP-серверы, предоставляемые плагинами.
Этот пример записывает в лог все операции сервера memory и проверяет операции записи от любого MCP-сервера:
Сопоставление команд Monitor
ХукPreToolUse или PermissionRequest с matcher Bash также запускается для shell-команд, которые Claude запускает с помощью инструмента Monitor, но его allow и updatedInput на них не действуют. Требуется Claude Code v2.1.314 или новее. Matcher, такие как Bash|PowerShell, ^Bash$ и Bash.*, тоже являются matcher Bash. Хук с matcher Bash не запускается для наблюдения за WebSocket, а хук PostToolUse с таким matcher не запускается для Monitor.
Хук, который запускается для команды Monitor, может отклонить её или заставить Claude Code спросить вас о ней. Чтобы разрешить или переписать команду, замените matcher Bash на тот, который включает Monitor:
- Matcher, включающий Monitor: с matcher, например
Bash|Monitorили.*, или без matcher хук получает собственные входные данные вызова Monitor, иallowиupdatedInputприменяются. Вifукажите Monitor, как вMonitor(tail *), который сопоставляется по тем же правилам, что иBash(tail *). Если в егоifуказан Bash, хук всё равно получает входные данные вызова Monitor, ноallowиupdatedInputне действуют - Matcher
Bash: хук получает вызов Bash. Вifукажите Bash, как вBash(tail *). Хук не запускается, если в егоifуказан Monitor
Поля обработчика хука
Каждый объект во внутреннем массивеhooks — это обработчик хука: shell-команда, HTTP-эндпоинт, MCP-инструмент, промпт LLM или агент, который запускается при совпадении matcher. Существует пять типов:
- Командные хуки (
type: "command"): запускают shell-команду. Ваш скрипт получает входные данные JSON события через stdin и сообщает результаты через коды выхода и stdout. - HTTP-хуки (
type: "http"): отправляют входные данные JSON события как HTTP POST-запрос на URL. Эндпоинт сообщает результаты через тело ответа, используя тот же формат вывода JSON, что и командные хуки. - Хуки MCP-инструментов (
type: "mcp_tool"): вызывают инструмент на настроенном MCP-сервере. Текстовый вывод инструмента обрабатывается так же, как stdout командного хука. - Хуки с промптом (
type: "prompt"): отправляют промпт модели Claude для однократной оценки. Модель возвращает своё решение в виде JSON. См. Хуки на основе промптов. - Хуки агентов (
type: "agent"): порождают субагента, который может использовать такие инструменты, как Read, Grep и Glob, чтобы проверить условия перед возвратом решения. Хуки агентов являются экспериментальными и могут измениться. См. Хуки на основе агентов.
timeout каждого ожидающего хука начинает отсчёт, когда этот хук запускается.
Если вы определите один и тот же обработчик в нескольких файлах настроек, он запустится один раз. Копия того же обработчика в плагине или скилле остаётся отдельной.
Обработчики запускаются в текущем каталоге с окружением Claude Code. Если текущий каталог больше не существует, например worktree или временный каталог, который другая оболочка удалила посреди сессии, Claude Code запускает командные хуки из первого из следующих каталогов, который ещё существует: каталог, в котором началась сессия, корень проекта, ваш домашний каталог или системный временный каталог. Claude Code записывает предупреждение с названием резервного каталога в лог отладки. О сессии в worktree, которую вы запускаете из десктопного приложения, см. Что worktree разделяют с основной рабочей копией.
Переменная окружения $CLAUDE_CODE_REMOTE имеет значение "true" в удалённых веб-окружениях и не задана в локальном CLI. Claude Code v2.1.199 и новее устанавливает $CLAUDE_CODE_BRIDGE_SESSION_ID в идентификатор сессии Remote Control, пока у локальной сессии есть активное подключение Remote Control.
Общие поля
Эти поля применяются ко всем типам хуков:
Поле
if содержит ровно одно правило разрешений. Синтаксиса &&, || или списка для объединения правил нет; чтобы применить несколько условий, определите отдельный обработчик хука для каждого.
В условии if для файлового инструмента шаблон каталога из одного сегмента, например "Edit(src/**)", соответствует только каталогу src в рабочем каталоге и файлам внутри него. Чтобы сопоставить каталог с именем src на любой глубине, напишите "Edit(**/src/**)".
Как шаблоны if сопоставляются с командами Bash
Для шаблонов Bash в поле if то, запустится ли команда вашего хука, зависит от формы шаблона и команды Bash, которую вызывает Claude. Начальные присваивания VAR=value удаляются перед сопоставлением.
Когда Claude Code не может определить, какие команды запускает входная команда Bash, он запускает ваш хук независимо от шаблона. Поскольку фильтр
if работает по принципу «насколько возможно», для строгого разрешения или запрета используйте систему разрешений, а не хук.
Поля командного хука
Помимо общих полей, командные хуки принимают следующие поля:Форма exec и форма shell
Командный хук выполняется в форме exec, если заданargs, и в форме shell, если args опущен. Задавайте args всякий раз, когда хук ссылается на плейсхолдер пути, поскольку каждый элемент передаётся как один аргумент без экранирования кавычками. Опускайте args, если вам нужны возможности оболочки, такие как конвейеры или &&, или если ни одно из этих соображений не применимо.
Форма exec используется, когда args присутствует. Claude Code разрешает command как исполняемый файл в PATH и запускает его напрямую с args в качестве вектора аргументов. Оболочки нет, поэтому каждый элемент args — это ровно один аргумент в том виде, в котором он написан, а плейсхолдеры путей, такие как ${CLAUDE_PLUGIN_ROOT}, подставляются в command и в каждый элемент args как обычные строки. Специальные символы, такие как апострофы, $ и обратные кавычки, передаются как есть, поскольку нет оболочки, которая бы их интерпретировала. Токенизация оболочкой не выполняется ни на одной платформе.
Форма shell используется, когда args отсутствует. Строка command передаётся оболочке: sh -c в macOS и Linux, Git Bash в Windows или PowerShell, если Git Bash не установлен. Задайте поле shell, чтобы выбрать явно. Оболочка токенизирует строку, раскрывает переменные и интерпретирует конвейеры, &&, перенаправления и glob-шаблоны.
В Windows форма exec требует, чтобы
command разрешался в настоящий исполняемый файл, например .exe. Прослойки .cmd и .bat, которые npm, npx, eslint и другие инструменты устанавливают в node_modules/.bin, не являются исполняемыми файлами и не могут быть запущены без оболочки. Чтобы запустить их в форме exec, вызовите базовый скрипт напрямую через node, например "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Шаблон «node плюс путь к скрипту» работает на любой платформе, поскольку node.exe — настоящий двоичный файл. Чтобы запустить прослойку .cmd или .bat по имени, используйте форму shell.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT и CLAUDE_PLUGIN_DATA в запущенный процесс, поэтому скрипт может читать process.env.CLAUDE_PLUGIN_ROOT независимо от того, как он был запущен.
Хуки плагинов дополнительно подставляют значения ${user_config.*}, только в форме exec: значение подставляется в command и в каждый элемент args как обычная строка, поэтому никакая оболочка не разбирает его повторно.
Хук плагина в форме shell, у которого command ссылается на ${user_config.*}, завершается ошибкой вместо запуска. Чтобы использовать значение параметра из хука в форме shell, читайте переменную окружения $CLAUDE_PLUGIN_OPTION_<KEY>, например $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL для параметра webhook_url, или задайте args, чтобы переключить хук в форму exec. До v2.1.207 команды хуков плагинов в форме shell также подставляли ${user_config.*}.
В форме exec
command — это только имя или путь исполняемого файла. Если command — голое имя без разделителя пути и содержит пробелы при наличии args, Claude Code записывает в лог предупреждение, поскольку запуск завершится неудачей: исполняемого файла с именем node script.js не существует. Перенесите лишние токены в args. Абсолютные пути с пробелами, такие как C:\Program Files\nodejs\node.exe, являются одним допустимым исполняемым файлом и не вызывают предупреждения.Поля HTTP-хука
Помимо общих полей, HTTP-хуки принимают следующие поля:
Claude Code отправляет входные данные JSON хука как тело POST-запроса с
Content-Type: application/json. Тело ответа использует тот же формат вывода JSON, что и командные хуки.
Обработка ошибок отличается от командных хуков; см. Обработка HTTP-ответов.
Этот пример отправляет события PreToolUse в локальный сервис проверки, аутентифицируясь токеном из переменной окружения MY_TOKEN:
Поля хука MCP-инструмента
Помимо общих полей, хуки MCP-инструментов принимают следующие поля:
Этот пример вызывает инструмент
security_scan на MCP-сервере my_server после каждого Write или Edit, передавая путь к отредактированному файлу:
Как читается результат инструмента
Claude Code читает текстовое содержимое инструмента так же, как stdout командного хука, следуя правилу разбора для кода выхода 0. Если инструмент возвращаетisError: true, хук выдаёт неблокирующую ошибку, и выполнение продолжается.
Если ошибка хука содержит одно из этих сообщений, Claude Code не смог прочитать ответ инструмента, поэтому хук не вынес вердикта:
PreToolUse Claude Code отклоняет вызов инструмента, который проверял хук. В других событиях, таких как UserPromptSubmit, PostToolUse и Stop, ошибка является неблокирующей.
Если ошибка продолжает появляться, возвращайте из инструмента меньше данных или измените tool или input хука, чтобы ответ был меньше.
Когда сервер ещё подключается
В событиях, где хук может заблокировать или изменить результат, таких какPreToolUse или Stop, Claude Code ожидает подключающийся сервер, прежде чем вызвать инструмент, не дольше MCP_TIMEOUT и в пределах собственного timeout хука. В наблюдательных событиях, таких как Notification или SessionEnd, он не ждёт.
Сервер со статусом cached подключается, когда хук вызывает его инструмент. Если в этот момент сервер не подключён, хук выдаёт неблокирующую ошибку, и выполнение продолжается. Хук никогда не запускает процесс OAuth, поэтому сначала аутентифицируйте сервер через /mcp.
События, которые срабатывают до того, как MCP-серверы становятся доступны
SessionStart при запуске, в том числе с --continue или --resume, и каждое событие Setup срабатывают до того, как MCP-серверы сессии становятся доступны хукам. Claude Code пропускает их хуки mcp_tool, не вызывая инструмент, а в лог отладки записывается mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context) или то же сообщение с упоминанием Setup. Когда SessionStart срабатывает снова позже в сессии, после /clear или сжатия контекста, его хуки mcp_tool запускаются. Для всего, что нужно сессии при запуске, используйте вместо этого хук type: "command" в SessionStart.
Поля хуков с промптом и хуков агентов
Помимо общих полей, хуки с промптом и хуки агентов принимают следующие поля:Ссылки на скрипты по пути
Используйте эти плейсхолдеры, чтобы ссылаться на скрипты хуков относительно корня проекта или плагина, независимо от рабочего каталога в момент запуска хука:${CLAUDE_PROJECT_DIR}: корень проекта, в котором началась сессия. Claude Code также задаёт эту переменную в окружении stdio MCP-серверов и LSP-серверов плагинов.${CLAUDE_PLUGIN_ROOT}: каталог установки плагина, для скриптов, поставляемых с плагином. О том, как ведёт себя путь при обновлениях, см. переменные окружения плагинов.${CLAUDE_PLUGIN_DATA}: каталог постоянных данных плагина, для зависимостей и состояния, которые должны сохраняться при обновлениях плагина.
С worktree всё иначе. Если Claude входит в worktree во время сессии, Claude Code оставляет
${CLAUDE_PROJECT_DIR} там, где он был, и передаёт путь worktree вашим хукам другим способом:${CLAUDE_PROJECT_DIR}остаётся на месте: он по-прежнему указывает на корень проекта, в котором началась сессия, поэтому команда вроде${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.shпо-прежнему запускает скрипт в основной рабочей копии.cwdследует за Claude: полеcwdво входных данных JSON хука — это корень worktree после того, как Claude входит в worktree, и новый каталог после того, как Claude выполняетcd. Читайте его, когда хуку нужно знать, в каком каталоге работает Claude.
${CLAUDE_PROJECT_DIR}, см. Что worktree разделяют с основной рабочей копией.- Скрипты проекта
- Скрипты плагинов
Этот пример использует
${CLAUDE_PROJECT_DIR} для запуска средства проверки стиля из каталога .claude/hooks/ проекта после любого вызова инструмента Write или Edit:Хуки в скиллах и агентах
Помимо файлов настроек и плагинов, хуки можно определять непосредственно в скиллах и субагентах с помощью frontmatter, в том же формате конфигурации, что и хуки на основе настроек. То, как долго Claude Code держит их зарегистрированными, зависит от компонента:- Хуки субагентов: Claude Code запускает их только во время работы этого субагента и удаляет, когда он завершается. Claude Code преобразует хук
Stopздесь вSubagentStop— событие, которое срабатывает при завершении субагента. - Хуки скиллов: Claude Code регистрирует их, когда вы или Claude вызываете скилл, и продолжает запускать их до конца сессии, в том числе на ходах после собственного хода скилла. Чтобы Claude Code вместо этого удалял хук после его первого успешного запуска, задайте для него
once: true.
PreToolUse, который запускает скрипт проверки безопасности перед каждой командой Bash:
-p в папке, которой вы не доверяете.
Хуки во frontmatter проектного субагента запускаются только после того, как вы примете диалоговое окно доверия к рабочему пространству для папки, из которой взят файл агента. Сессия -p не считается его принятием. В разделе Что запускается до того, как вы доверитесь папке это сравнивается с правилом для файлов настроек, а на странице субагентов перечислено, какие области действия освобождены.
Ограничения хуков скиллов
Если хуки во frontmatter скилла не срабатывают после того, как вы или Claude вызвали скилл, выполните/hooks в этой сессии. Если они там перечислены, см. Хук не срабатывает. Если ни один из них не перечислен, скилл может находиться под блокировкой «только плагины» или превышать лимит хуков и работает без каких-либо своих хуков из frontmatter. Проверьте каждый случай:
- Блокировка «только плагины»: администратор вашей организации может ограничить хуки плагинами и управляемыми настройками с помощью управляемой настройки
strictPluginOnlyCustomization. При такой блокировке скилл сохраняет свои хуки из frontmatter, только если это скилл плагина, встроенный скилл или скилл в каталоге управляемых настроек. Хуки в ваших собственных файлах настроек тоже не запускаются.- Проверка: введите
/statusв сессии Claude Code. Если строкаSetting sourcesсодержитEnterprise managed settings, спросите администратора, блокирует лиstrictPluginOnlyCustomizationхуки. - Исправление: перенесите скилл в плагин, поскольку скиллы плагинов сохраняют свои хуки из frontmatter. Добавьте плагин в маркетплейс, а затем установите его оттуда. Если Claude Code отклоняет маркетплейс с сообщением
is blocked by enterprise policy, попросите администратора разрешить этот маркетплейс.
- Проверка: введите
- Лимит хуков: в Claude Code v2.1.304 или новее у каждого источника скиллов есть собственный лимит на сессию. Ваш проект, ваша папка личных скиллов и каждый плагин считаются отдельными источниками. Скиллы из одного источника могут зарегистрировать не более 1 024 хуков. Matcher этих хуков в сумме могут содержать не более 16 384 символов. Matcher, под которым находится несколько хуков, учитывается один раз для каждого хука.
- Проверка: повторите сессию, в которой хук не сработал, с включённым логированием отладки:
- Запустите новую сессию из оболочки с помощью
claude --debug. Если вы не запускаете сессии из оболочки, вместо этого сначала введите/debugв новой сессии. - Вызовите те же скиллы в том же порядке, включая те, которые вызывал Claude, поскольку оба итоговых значения накапливаются скилл за скиллом. Попросите Claude вызвать любой скилл, который вы не можете ввести как команду.
- Найдите в логе отладки
registers none of its hooks. Строка с этим текстом называет скилл, который превысил лимит.
- Запустите новую сессию из оболочки с помощью
- Исправление: объявите меньше хуков или более короткие matcher в этом скилле или в других скиллах из его источника, затем начните новую сессию. Если эти скиллы не ваши и вы не можете их редактировать, попросите их автора внести это изменение. До тех пор начинайте новую сессию и вызывайте нужный скилл раньше любого другого скилла из его источника.
- Проверка: повторите сессию, в которой хук не сработал, с включённым логированием отладки:
Меню /hooks
Введите /hooks в Claude Code, чтобы открыть браузер настроенных хуков только для чтения. В списке каждый хук помечен источником, например пользовательские настройки, проектные настройки, локальные настройки, плагин или текущая сессия.
Выберите хук, чтобы увидеть полный текст того, что он запускает, и место, где он определён, например путь к его файлу настроек или имя его плагина.
Чтобы просмотреть все события хуков, включая те, для которых хуки не настроены, выберите All events в конце списка.
Отключение или удаление хуков
Чтобы удалить хук, определённый в файле настроек, удалите его запись из этого файла. Чтобы временно отключить все хуки, не удаляя их, задайте"disableAllHooks": true в файле настроек. Claude Code читает значение, оставшееся после применения приоритета настроек, поэтому "disableAllHooks": false в .claude/settings.json проекта переопределяет true в ваших пользовательских настройках. Чтобы отключить хуки для одного запуска независимо от настроек проекта, передайте --settings '{"disableAllHooks": true}', что имеет приоритет над проектными и локальными настройками. Способа отключить отдельный хук, сохранив его в конфигурации, нет.
Настройка disableAllHooks учитывает иерархию управляемых настроек. Если администратор настроил хуки через настройки управляемой политики, disableAllHooks, заданный в пользовательских, проектных или локальных настройках, не может отключить эти управляемые хуки. Только disableAllHooks, заданный на уровне управляемых настроек, может отключить управляемые хуки. О полном охвате каждого уровня см. disableAllHooks.
Прямые изменения хуков в файлах настроек обычно подхватываются автоматически средством отслеживания файлов.
Входные и выходные данные хуков
Командные хуки получают данные JSON через stdin и сообщают результаты через коды выхода, stdout и stderr. HTTP-хуки получают тот же JSON в теле POST-запроса и сообщают результаты через тело HTTP-ответа. В этом разделе описаны поля и поведение, общие для всех событий. Раздел каждого события в Событиях хуков содержит его собственную схему входных данных и параметры управления решениями. В macOS и Linux командные хуки выполняются в собственной сессии без управляющего терминала. Процесс хука и любые дочерние процессы не могут открыть/dev/tty или отправлять escape-последовательности напрямую в интерфейс Claude Code. В Windows /dev/tty отсутствует.
Чтобы показать сообщение пользователю на любой платформе, верните systemMessage в выводе JSON. Некоторые события отбрасывают его или доставляют в другое место, и раздел каждого события об этом сообщает. Чтобы вызвать уведомление на рабочем столе, задать заголовок окна или подать звуковой сигнал, верните вместо этого terminalSequence.
Общие поля входных данных
События хуков получают эти поля в формате JSON в дополнение к специфичным для события полям, описанным в разделе каждого события хука. Для командных хуков этот JSON поступает через stdin. Для HTTP-хуков он поступает в теле POST-запроса.agent_id и agent_type сообщают вашему скрипту, в каком агенте сработал хук, например в субагенте, участнике команды, работающем в том же процессе, или агенте, выбранном с помощью --agent:
Только хуки
SessionStart могут получать поле model, и Claude Code не всегда его включает. Хуки PreModelSwitch и PostModelSwitch вместо этого получают from_model и to_model, поэтому используйте хук PostModelSwitch, чтобы отслеживать модель при её смене в ходе сессии.
Переменной окружения $CLAUDE_MODEL не существует. Хук может прочитать $ANTHROPIC_MODEL, если вы задали её в своей оболочке, но это значение не меняется, когда вы переключаете модели с помощью /model в ходе сессии.
Процесс хука наследует родительское окружение, за исключением переменных экспортёра OTEL_*, которые Claude Code удаляет из каждого запускаемого им подпроцесса, и, если CLAUDE_CODE_SUBPROCESS_ENV_SCRUB установлена в 1, переменных, которые он при этом удаляет. В сессии, которая получает конфигурацию HIPAA, Claude Code также удаляет учётные данные Anthropic из окружения хука.
Например, хук PreToolUse для команды Bash получает через stdin следующее:
tool_name, tool_input и tool_use_id специфичны для события. Раздел каждого события хука описывает дополнительные поля для этого события.
Вывод через код выхода
Когда выполнение вашего хука завершается, Claude Code по его коду выхода решает, продолжать ли действие, которое вызвало хук, например вызов инструмента или промпт:- Успех: ваш хук завершается с кодом 0. Claude Code применяет все поля вывода JSON, которые вывел ваш хук, и действие выполняется, если эти поля не блокируют и не отклоняют его. Исключение описано в разделе Хуки, запускающие фоновый процесс.
- Блокирующая ошибка: ваш хук завершается с кодом 2. Для событий, которые могут блокировать, Claude Code останавливает действие.
- Неблокирующая ошибка: ваш хук завершается с любым другим кодом или завершается сбоем иным образом, например не запускается или выводит некорректный JSON. Действие выполняется, за исключением хуков, сбой которых блокирует действие, например хука с
onFailure, установленным в"block". Для таких событий, какPreToolUse, в транскрипте отображается уведомление<hook name> hook error.
PreToolUse завершается с кодом 1, но выводит JSON, прошедший проверку, этот JSON всё равно может определить, что произойдёт. Чтобы узнать результат вашего хука для такого события, как PreToolUse, найдите в первом столбце то, что он вывел в stdout, а в верхней строке — его код выхода:
У некоторых событий собственные правила:
WorktreeCreate: любой ненулевой код выхода приводит к ошибке создания worktree, что бы ни содержал ваш JSON.WorktreeRemove: любой ненулевой код выхода приводит к ошибке удаления worktree, если каталог после этого всё ещё существует.Stop,SubagentStop,TaskCompletedи хукUserPromptSubmitплагина: если ваш хук завершается с кодом 2, ничего не выводит в stdout, а его stderr сообщает об отсутствии файла, напримерNo such file or directory, Claude Code обрабатывает запуск как неблокирующую ошибку.ElicitationиElicitationResult: Claude Code применяет вашhookSpecificOutput, когда хук завершается с кодом 0, и игнорирует его при любом другом коде выхода.- События, отбрасывающие вывод хука, такие как
StopFailure: Claude Code игнорирует ваш JSON при любом коде выхода, за исключением полей с побочными эффектами, таких какterminalSequence, которые всё равно срабатывают.
Код выхода 0
Выход с кодом 0 означает успех и является предполагаемым кодом выхода, когда вы выводите JSON для структурированного управления. Если ваш хук завершается с кодом 0, но выводит JSON, который не удаётся разобрать или который не проходит проверку схемы, действие выполняется, если хук не относится к тем, сбой которых блокирует действие. Вместо успеха с применёнными полями запуск считается неблокирующей ошибкой. Чтобы исправить вывод, воспользуйтесь сообщением о разборе или проверке, которое содержит уведомление<hook name> hook error. Для хука, который не является асинхронным, ваш stdout считается выводом JSON, только если вы выводите один объект JSON на одной или нескольких строках, вокруг которого нет ничего, кроме пробельных символов. Любой другой stdout Claude Code воспринимает как обычный текст или как ошибку разбора:
- Вывод, который не начинается с
{или начинается с{, но не заканчивается}: обычный текст. По этому правилу массив JSON и строка JSON в кавычках считаются обычным текстом. - Две или более строк, каждая из которых по отдельности разбирается как JSON, причём первая начинается с
{, а последняя заканчивается}: обычный текст, если ни одна строка не является объектом вывода JSON, задающим какое-либо поле. Если такая строка есть, вывод считается ошибкой разбора, поэтому выводите этот объект, не добавляя в stdout ничего другого. - Всё остальное, что начинается с
{и заканчивается}, но не является корректным JSON: ошибка разбора.
UserPromptSubmit,UserPromptExpansion,SessionStartиPostModelSwitch: Claude Code добавляет обычный текст из stdout как контекст, который Claude может видеть и учитывать. Stdout, который ему не удалось разобрать, Claude Code не добавляет.PreCompact: ваш stdout становится инструкциями для сводки сжатия контекста
PreToolUse, включите отладочное логирование. Чтобы передать предупреждение Claude из хука PostToolUse или PostToolUseFailure, вместо этого выйдите с кодом 2, чтобы Claude увидел stderr, хотя инструмент уже выполнился.
Код выхода 2
Завершитесь с кодом 2, чтобы заблокировать действие. Для событий, которые могут блокировать, Claude Code останавливает действие: например, хукPreToolUse блокирует вызов инструмента, а хук UserPromptSubmit отклоняет промпт.
Сообщением, сопровождающим блокировку, служит stderr вашего хука. Если ваш хук также вывел JSON с блокирующим решением, Claude Code вместо этого использует причину из этого решения.
Выход с кодом 2 блокирует, даже если ваш хук выводит JSON:
- JSON, прошедший проверку схемы: Claude Code по-прежнему читает поля вывода JSON, но они не могут переопределить блокировку. Даже
permissionDecisionсо значением"allow"не пропускает действие. ДляElicitationиElicitationResultполеhookSpecificOutputхука, завершившегося с кодом 2, игнорируется. - JSON, не прошедший проверку схемы: хук всё равно блокирует. Claude Code использует ваш stderr как причину блокировки и записывает ошибку проверки в отладочный лог.
rm, завершаясь с кодом 2, и оставляет все остальные команды обычному процессу разрешений:
PreToolUse для Bash, команда, начинающаяся с rm, блокируется, а Claude получает stderr хука как ошибку инструмента с префиксом из имени события, имени инструмента и команды хука:
Другие коды выхода
Уведомление<hook name> hook error с Failed with non-blocking status code: означает, что код выхода вашего хука не был равен 0 или 2, а его stdout содержал обычный текст или был пустым. Если хук не относится к тем, сбой которых блокирует действие, это неблокирующая ошибка, поэтому ваш хук не остановил действие. Чтобы получить полный stderr вашего хука, а не только первую строку, которую показывает уведомление, включите отладочное логирование.
Например, если хук PreToolUse для Bash выводит something broke в stderr и завершается с кодом 1, уведомление PreToolUse:Bash hook error содержит такую строку:
No such file or directory, как в следующей строке, хук так и не был запущен, например потому что путь к его скрипту не существует:
settings.json означает, что хук никогда не выполнится. Чтобы блокировать действие, когда хук не удаётся запустить, установите onFailure: "block".
Таймауты
За исключением командного хука, запущенного сasync: true, Claude Code отменяет хук command, http или mcp_tool, достигший своего timeout, и отбрасывает вывод хука, поэтому для большинства событий хук, превысивший таймаут, не выносит никакого решения. Для PreModelSwitch хук, отменённый по таймауту, блокирует переключение модели.
Для PreToolUse хук command, http или mcp_tool, превысивший таймаут, не блокирует вызов инструмента, за исключением хуков, сбой которых блокирует действие. Вызов продолжается через обычный процесс разрешений, поэтому не рассчитывайте, что зависший хук сработает как защитный барьер. Чтобы блокировать вызов при превышении таймаута хуком command или http, установите onFailure: "block". Callback-хук Agent SDK, превысивший свой таймаут, блокирует вызов инструмента.
Хуки, запускающие фоновый процесс
Хук может запустить долго работающую программу, например сервер разработки, и оставить её работать, пока сессия продолжается. В скрипте хука или в команде в форме оболочки:- Запустите программу в фоновом режиме
- Перенаправьте её stdout и stderr. Например, команда Bash
npm run dev > dev-server.log 2>&1 &направляет оба потока в файл. Если вы их не перенаправите, хуку, который не является асинхронным, требуется до 10 секунд, чтобы завершиться после выхода собственного процесса.
stdio went quiet и refusing to honor the suspect capture.
Этот скрипт хука PreToolUse выводит решение с помощью policy-check.sh, а затем загружает метрики в фоновом режиме:
|| exit $?: делает код выхода проверки собственным кодом выхода хука, поэтому проверка, завершающаяся с кодом 2 для отказа, по-прежнему блокирует. Иначе хук завершается с кодом последней строки, то есть 0.>/dev/null 2>&1 &: запускаетupload-metrics.shв фоновом режиме с перенаправленным выводом, чтобы он не удерживал вывод хука открытым.
policy-check.sh завершался с кодом 2, а затем вызовите инструмент, которому соответствует хук. Вызов инструмента будет заблокирован.
Блокировка действия при сбое хука
Для большинства событий, когда хук завершается сбоем или превышает таймаут, Claude Code всё равно выполняет действие. В результате хук политики с неверным путём или падающим скриптом пропускает всё. Чтобы вместо этого блокировать действие, установите"onFailure": "block" для хука command или http. Требуется Claude Code v2.1.295 или новее. О callback-хуках Agent SDK смотрите Блокировка действия при сбое callback.
В Windows без Git Bash командный хук
PreToolUse или PermissionRequest в форме оболочки, завершившийся сбоем при незаданном onFailure, может вместо этого заблокировать вызов инструмента.PreToolUse в .claude/settings.json запускает скрипт проекта перед каждой командой Bash и блокирует команду, если скрипт завершается сбоем:
check-command.js и попросите Claude выполнить команду Bash, например ls. Claude Code блокирует вызов, и ошибка содержит failed; blocking because onFailure is "block", за которым следует собственный вывод ошибки node, сокращённый здесь до одной строки:
timed out вместо failed. Без заданного onFailure тот же отсутствующий скрипт является неблокирующей ошибкой, и ls выполняется.
Каждый из следующих случаев считается сбоем:
- Не удаётся запустить: командный хук не запускается, например потому что скрипт или исполняемый файл не существует
- Код выхода, отличный от 0 или 2: учитывается для командного хука, даже если он вывел JSON, разрешающий действие, например
permissionDecision: "allow". Чтобы вернуть решение в JSON, завершитесь с кодом 0 - Ошибка HTTP: соединение HTTP-хука не удаётся установить, или статус ответа не 2xx
- Таймаут: хук достигает своего
timeout - Некорректный вывод: вывод JSON не удаётся разобрать или он не проходит проверку схемы. Для HTTP-хука сбоем также считается тело ответа 2xx, которое не является ни пустым, ни объектом JSON. Обычный текст в stdout командного хука сбоем не считается
"block" сбой действует так же, как код выхода 2 для этого события, за исключением PermissionRequest, где он отклоняет запрос. Например, сбой PreToolUse блокирует вызов инструмента, а сбой UserPromptSubmit блокирует промпт.
Поле не действует для следующих хуков:
- Хуки
Stop,SubagentStop,TaskCompletedиTeammateIdle: код выхода 2 для этих событий отправляет Claude продолжать работу, а Claude не может исправить хук, который не запускается - Фоновые командные хуки: командные хуки, в которых задано
asyncилиasyncRewake
Сбой хуков в Windows без Git Bash
Если ошибка заблокированного вызова инструмента содержит этот текст, командный хукPreToolUse или PermissionRequest завершился сбоем или превысил таймаут и заблокировал вызов:
PowerShell started no program, начните с исправлений для command, состоящего только из строк в кавычках, которые PowerShell выводит вместо того, чтобы выполнять.
Без Git Bash любой код выхода этого хука, отличный от 0 или 2, считается сбоем, даже если хук выводит JSON, который не отклоняет вызов. Чтобы узнать причину сбоя хука, проверьте отладочный лог. Затем выберите исправление, подходящее для вашего хука:
- Действие должно выполняться при сбое хука: добавьте в хук
"onFailure": "continue" - Хук завершается с кодом 1 для предупреждения, но должен блокировать при падении: с
"onFailure": "continue"вызов выполняется при коде выхода 1 — как при предупреждении, так и при падении. Вместо этого добавьте"onFailure": "block"и завершайте скрипт с кодом 0 там, где он выдаёт предупреждение, чтобы такие вызовы выполнялись. Чтобы по-прежнему показывать предупреждение дляPreToolUse, выведите его в stdout в полеsystemMessageодного объекта JSON. - Хук написан для Bash: без Git Bash хук в форме оболочки без заданного
shellвыполняется в PowerShell, где он может завершиться сбоем. Установите Git for Windows или задайтеCLAUDE_CODE_GIT_BASH_PATH, если Git Bash установлен там, где Claude Code его не находит. Затем начните новую сессию. - Хук запускает один файл
.exe, а не файл.cmdили.bat: укажите программу вcommand, а её аргументы вargs, чтобы использовать форму exec, и добавьте"onFailure": "block". Тогда Claude Code запускает программу без оболочки. Без"onFailure": "block"вызов может выполниться, если программа завершается сбоем, отсутствует или превышает таймаут - Защитный хук PowerShell, который должен блокировать при сбое: при незаданном
onFailureон перестаёт это делать, как только установлен Git for Windows и Claude Code находит Git Bash. Добавьте"onFailure": "block", чтобы сбой или таймаут блокировал вызов как с Git Bash, так и без него. Также задайте"shell": "powershell", чтобы хук и дальше выполнялся в PowerShell, а не в Git Bash
blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.
PowerShell started no program
Хук с этой ошибкой выполнялся в PowerShell, который выводитcommand, состоящий только из строк в кавычках, например "command": "\"C:\\Tools\\guard.exe\"", вместо того чтобы его выполнить.
- Чтобы вывести текст, например JSON: задайте
"shell": "powershell", и хук завершится с кодом 0 - Чтобы запустить файл
.ps1,.cmdили.batлибо.exe, путь к которому содержит переменную: задайте"shell": "powershell"и поставьте&перед путём в кавычках. Оставьте$HOMEкак есть, а переменные окружения записывайте так, как примерcheck.ps1записывает$env:CLAUDE_PROJECT_DIR - Чтобы запустить один
.exeпо пути без$: удалите внутренние кавычки изcommand, затем примените исправление для хука, запускающего один.exe, указав"args": [], если программа не принимает аргументов - Чтобы запустить скрипт, написанный для Bash: примените исправление для хука, написанного для Bash
Поведение кода выхода 2 для каждого события
Код выхода 2 — это способ, которым хук сигнализирует «стоп, не делай этого». Эффект зависит от события, потому что одни события представляют действия, которые можно заблокировать (например, вызов инструмента, который ещё не произошёл), а другие — то, что уже произошло или не может быть предотвращено.
Для
SessionStart, SubagentStart и PostModelSwitch Claude Code отображает stderr при коде выхода 2 в транскрипте как уведомление <hook name> hook error — так же, как отображает неблокирующую ошибку. Claude его не видит, и сессия или субагент продолжают работу. Для SubagentStart уведомление появляется в собственном транскрипте субагента, а не в родительском диалоге.
Обработка HTTP-ответов
HTTP-хуки используют коды состояния HTTP и тела ответов вместо кодов выхода и stdout. Чтобы блокировать действие, когда запрос завершается сбоем или возвращает статус не 2xx, установитеonFailure: "block". Чтобы заблокировать вызов инструмента или отклонить разрешение, верните ответ 2xx с телом JSON, содержащим соответствующие поля решения.
После неблокирующей ошибки действие выполняется, если хук не относится к тем, сбой которых блокирует действие. За исключением событий с собственным контрактом обработки сбоев в таблице по событиям, таких как WorktreeCreate, для каждого ответа или сбоя вы получаете следующие результаты:
- 2xx с пустым телом: успех, эквивалентно коду выхода 0 без вывода
- 2xx с телом в виде объекта JSON: разбирается по той же схеме вывода JSON, что и для командных хуков. Тело, не прошедшее проверку схемы, является неблокирующей ошибкой
- 2xx с любым другим телом, например обычным текстом: неблокирующая ошибка, обрабатывается так же, как статус не 2xx. Claude Code не добавляет текст в контекст Claude
- Статус не 2xx: неблокирующая ошибка
- Сбой соединения: неблокирующая ошибка
- Таймаут: хук отменяется, как описано в разделе Таймауты
Вывод JSON
Коды выхода позволяют только блокировать или ничего не сообщать, а вывод JSON даёт более тонкое управление. Вместо выхода с кодом 2 для блокировки завершитесь с кодом 0 и выведите объект JSON в stdout. Claude Code читает определённые поля из этого JSON для управления поведением, включая управление решениями для блокировки, разрешения или передачи решения пользователю.Выберите один подход для каждого хука: либо используйте только коды выхода для сигнализации, либо завершайтесь с кодом 0 и выводите JSON для структурированного управления. Если вы их смешиваете, выход с кодом 2 сохраняет свой блокирующий эффект, а Claude Code по-прежнему читает поля JSON, с единственным исключением для elicitation, отмеченным в разделе Код выхода 2.
additionalContext, systemMessage и initialUserMessage хука, а также его обычный stdout ограничены 10 000 символов:
- Область действия: Claude Code измеряет каждую строку отдельно, даже если для одного события выполняется несколько хуков. Для вывода JSON каждое поле измеряется отдельно; обычный stdout измеряется целиком.
- Превышение лимита: за исключением stdout хука PreCompact, Claude Code сохраняет вывод в файл в каталоге сессии. Он заменяет вывод путём к файлу и превью, содержащим до 2 000 первых символов. Большой корректный результат Bash обрабатывается так же, как описано в разделе Ограничения вывода. В отличие от этого ограничения Bash, у данного лимита нет настройки или переменной окружения, позволяющей его увеличить.
- Чтение файла: Claude Code не просит Claude прочитать файл, поэтому держите всё, что Claude должен видеть всегда, в пределах лимита.
- Универсальные поля, такие как
continue, перечислены в таблице ниже. Их принимает каждое событие, но некоторые события отбрасывают их или доставляютsystemMessageне в транскрипт, а в другое место. Раздел каждого события об этом сообщает.terminalSequenceтоже работает для этих событий, с исключениями, перечисленными в разделе Вывод уведомлений терминала. decisionиreasonверхнего уровня используются некоторыми событиями для блокировки или предоставления обратной связи.hookSpecificOutput— вложенный объект для событий, которым требуется более развитое управление. Он требует полеhookEventName, установленное в имя события.
Чтобы полностью остановить Claude:
PreToolUse и PostToolUse остановка применяется, даже если вызов инструмента завершается ошибкой или выполняется, пока Claude ещё передаёт ответ в потоковом режиме.
Вывод уведомлений терминала
Хуки выполняются без управляющего терминала, поэтому запись escape-последовательностей напрямую в/dev/tty не работает. Вместо этого верните escape-последовательность в поле terminalSequence, и Claude Code выведет её за вас через собственный путь записи в терминал. Это исключает состояние гонки и работает в 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 - Одиночный BEL
systemMessage и continue, таких как Notification и StopFailure. У него есть некоторые ограничения:
- Claude Code записывает последовательность только в интерактивной сессии и только пока его интерфейс находится на экране. В неинтерактивном режиме с флагом
-pи в Agent SDK он игнорирует это поле. - Поле работает только в хуке, который не выполняется в фоновом режиме, поэтому не включайте
asyncиasyncRewake. - Командный хук
WorktreeCreateне может возвращать JSON, потому что Claude Code читает его stdout как путь к worktree. HTTP-хукWorktreeCreateвозвращает JSON и может включать это поле.
Notification вызывает уведомление на рабочем столе в терминале, который читает OSC 777, например Ghostty или Warp. Вызов jq -nc --arg в скрипте формирует вывод JSON и экранирует все кавычки, обратные слеши и переводы строк в тексте уведомления:
{ "terminalSequence": "..." } одинакова для любой оболочки или языка.
Получение уведомлений терминала внутри tmux или GNU screen
Когда Claude Code работает внутри tmux или GNU screen, для того чтобы последовательности OSC доходили до вашего терминального приложения, необходимо следующее:- tmux 3.3 или новее: добавьте
set -g allow-passthrough onв~/.tmux.conf, затем выполнитеtmux source-file ~/.tmux.confиз оболочки. Параметр применяется ко всем программам в этих панелях, а не только к Claude Code. Со значениемonотправлять их могут только панели, которые tmux показывает на экране. Чтобы их могли отправлять и другие панели в сессии, к которой вы подключены, используйтеallвместоonв tmux 3.4 или новее. - GNU screen: держите окно Claude Code отображаемым в screen, потому что последовательности из фонового окна не доходят до вашего терминального приложения
Добавление контекста для Claude
ПолеadditionalContext передаёт строку из вашего хука в контекстное окно Claude. Claude Code оборачивает строку в системное напоминание и вставляет его в диалог в точке срабатывания хука. Claude читает напоминание при следующем запросе к модели, но оно не отображается в интерфейсе как сообщение чата.
Возвращайте additionalContext внутри hookSpecificOutput вместе с именем события:
- SessionStart и SubagentStart: в начале диалога, перед первым промптом
- UserPromptSubmit и UserPromptExpansion: вместе с отправленным промптом
- PreToolUse, PostToolUse, PostToolUseFailure и PostToolBatch: рядом с результатом инструмента
- Stop и SubagentStop: в конце хода. Диалог продолжается, чтобы Claude мог отреагировать на обратную связь. Смотрите Управление решениями Stop
- PostModelSwitch: со следующим запросом после переключения. Смотрите Управление решениями PostModelSwitch, чтобы узнать о времени доставки
additionalContext для одного события, Claude получает все значения.
Если ваша строка содержит тег <system-reminder> или </system-reminder>, Claude получает строку, в которой < этого тега заменён на <.
Если значение превышает 10 000 символов, Claude Code записывает текст в файл в каталоге сессии и вместо этого передаёт Claude путь к файлу с превью, содержащим до 2 000 первых символов. Claude может прочитать файл, но Claude Code не просит его об этом.
Используйте additionalContext для информации, которую Claude следует знать о текущем состоянии вашего окружения или об только что выполненной операции:
- Состояние окружения: текущая ветка, цель развёртывания или активные флаги функций
- Условные правила проекта: какая команда тестирования применима к только что отредактированному файлу, какие каталоги доступны только для чтения в этом worktree
- Внешние данные: открытые задачи, назначенные вам, недавние результаты CI, содержимое, полученное из внутреннего сервиса
bun test» воспринимаются как информация о проекте. Текст, оформленный как внеполосные системные команды, может активировать защиту Claude от промпт-инъекций, из-за чего Claude покажет этот текст вам вместо того, чтобы воспринять его как контекст.
Claude Code сохраняет внедрённый текст в транскрипте сессии. Для событий в середине сессии, таких как PostToolUse или UserPromptSubmit, при возобновлении с --continue или --resume Claude Code воспроизводит сохранённый текст, а не запускает хук повторно для прошлых ходов, поэтому такие значения, как временные метки или SHA коммитов, устаревают. Хуки SessionStart при возобновлении запускаются снова с source, установленным в "resume", или в "fork", если вы добавили --fork-session, поэтому они могут обновить свой контекст.
Управление решениями
Не каждое событие поддерживает блокировку или управление поведением через JSON. События, которые это поддерживают, используют разные наборы полей для выражения решения. Используйте эту таблицу как краткий справочник перед написанием хука:
Некоторые события также могут переписывать содержимое, а не только разрешать или блокировать его:
PreToolUse:updatedInputнепосредственно вhookSpecificOutputзаменяет аргументы инструмента перед его запуском. Смотрите Управление решениями PreToolUsePermissionRequest:updatedInputвнутри объектаdecision. Смотрите Управление решениями PermissionRequestPostToolUse:updatedToolOutputзаменяет результат инструмента. В облачной сессии Cowork Claude Code игнорируетupdatedToolOutput, если инструментом являетсяAskUserQuestionилиExitPlanMode, и Claude читает исходный ответ или план. Смотрите Управление решениями PostToolUseUserPromptSubmit: не может заменить промпт; он только внедряетadditionalContextрядом с ним
PreToolUse, а входящие результаты инструментов — в PostToolUse.
Вот примеры каждого шаблона в действии:
- decision верхнего уровня
- PreToolUse
- PermissionRequest
Единственное значение для
decision — "block". Чтобы разрешить продолжение действия, не указывайте decision в вашем JSON или завершитесь с кодом 0 вообще без JSON:События хуков
Каждое событие соответствует точке в жизненном цикле Claude Code, в которой могут выполняться хуки. Разделы ниже упорядочены в соответствии с жизненным циклом: от настройки сессии через агентный цикл до завершения сессии. В каждом разделе описано, когда срабатывает событие, какие значения matcher оно поддерживает, какие входные данные JSON получает и как управлять поведением через вывод.SessionStart
Выполняется, когда Claude Code начинает новую сессию или возобновляет существующую. Полезно для загрузки контекста разработки, например существующих issues или недавних изменений в вашей кодовой базе, или для настройки переменных окружения. Для статического контекста, который не требует скрипта, используйте вместо этого CLAUDE.md. SessionStart выполняется в каждой сессии, поэтому такие хуки должны работать быстро. Поддерживаются только хукиtype: "command" и type: "mcp_tool". О том, когда выполняются хуки mcp_tool, см. Поля хука MCP-инструмента.
Значение matcher соответствует тому, как была запущена сессия:
Когда вы начинаете интерактивную сессию, возобновляете диалог при запуске с
--continue или --resume или выполняете /clear, хуки SessionStart выполняются в фоне. Вы можете сразу начать вводить текст, а возобновлённый диалог появляется, не дожидаясь хуков. Первый ответ Claude всё равно ждёт завершения хуков, чтобы их контекст дошёл до Claude.
Когда вы переключаете диалоги с помощью /resume внутри сессии, переключение, напротив, ждёт завершения хуков. Если вы выполните /clear или переключитесь на другой диалог, пока фоновые хуки ещё выполняются, ничто из того, что они вернут, не применится к сессии.
То же ожидание действует и при запуске, включая возобновлённую сессию: промпт, который вы отправляете, пока хуки SessionStart ещё выполняются, не дойдёт до Claude, пока они не завершатся.
Во время любого из этих ожиданий нажмите Esc, чтобы вернуть промпт в поле ввода, не отправляя его. Хуки продолжат выполняться.
Входные данные SessionStart
Помимо общих входных полей, хуки SessionStart получаютsource и, необязательно, model, agent_type и session_title:
Сессия, которой вы не дали имя, всё равно может иметь сгенерированное название. Такое название не является пользовательским и не появляется в
session_title.
Когда source равен "resume" или "fork" и транскрипт содержит хотя бы один ответ Claude, хуки SessionStart также получают четыре поля ниже. Ваш хук может использовать их, чтобы до первого запроса сообщить, во что обойдётся возобновление устаревшего диалога, например в systemMessage. Для этих полей требуется Claude Code v2.1.251 или новее.
В этом примере показаны входные данные для сессии, возобновлённой через 90 минут после её последнего ответа:
Управление решениями SessionStart
Хук SessionStart может добавить контекст для Claude, задать первое сообщение пользователя, задать название сессии, отслеживать файлы и перезагружать скиллы. Для каждого из этих действий верните соответствующее поле в дополнение к полям вывода JSON, доступным всем хукам:
Этот вывод добавляет контекст и даёт сессии имя:
initialUserMessage или sessionTitle, установите плагин до начала сессии. Claude Code игнорирует оба поля от плагина, установка которого завершается после того, как хуки SessionStart уже выполнились.
Перезагрузка скиллов, которые устанавливает хук
Чтобы скиллы, которые устанавливает хук SessionStart, стали доступны в той же сессии, вернитеreloadSkills. Обнаружение скиллов обычно выполняется до завершения хуков SessionStart, поэтому без этого поля файлы, которые хук записывает в ~/.claude/skills/ или .claude/skills/, могут отсутствовать при выполнении первого промпта.
Этот пример синхронизирует общий репозиторий скиллов и запрашивает повторное сканирование:
Сохранение переменных окружения
Хуки SessionStart имеют доступ к переменной окруженияCLAUDE_ENV_FILE, которая указывает путь к файлу, где можно сохранить переменные окружения для shell-команд, которые Claude выполняет позже в сессии.
Чтобы задать отдельные переменные окружения, запишите операторы export в CLAUDE_ENV_FILE. Используйте дозапись (>>), чтобы сохранить переменные, заданные другими хуками:
$PATH в export PATH="$PATH:./node_modules/.bin".
Сохранённые переменные в командах PowerShell
Команды PowerShell тоже получают переменные изCLAUDE_ENV_FILE в Claude Code v2.1.296 или новее, но PowerShell никогда не выполняет этот файл. Вместо этого Claude Code считывает из него присваивания и копирует их в окружение команды PowerShell. Это происходит, только если каждая строка относится к одному из следующих видов — во всём, что записали все хуки в этой сессии, и в любом скрипте, на который вы установили CLAUDE_ENV_FILE до запуска:
- Пустая строка или комментарий
# - Одно присваивание в начале строки, записанное как
export NAME=value,declare -x NAME=valueилиNAME=value, со значением, которое Bash использовал бы в точности как написано, составленным из любого сочетания следующих частей: текст без кавычек, использующий только буквы, цифры и символы_ @ % + = : , . / -, текст в одинарных кавычках и текст в двойных кавычках, где любые$, обратная кавычка или"внутри экранированы обратной косой чертой
export PATH="$PATH:./node_modules/.bin" с неэкранированным $PATH, команда source или строки $'...', которые печатает direnv export bash, команды PowerShell не получают ни одной переменной, а claude --debug записывает в лог Session environment is not all plain assignments. Команды Bash по-прежнему получают их все. В Windows команды PowerShell также не получают переменную, значение которой содержит / или \, поскольку Git Bash и Windows записывают пути по-разному. Команда PowerShell, выполняемая в песочнице, не получает ни одной переменной.
CLAUDE_ENV_FILE доступна для хуков SessionStart, Setup, CwdChanged и FileChanged. Другие события хуков не имеют доступа к этой переменной, как и хук, выполняемый в PowerShell, будь то через "shell": "powershell" или по умолчанию в Windows без Git Bash.Setup
Срабатывает, только когда вы запускаете Claude Code с--init-only либо с --init или --maintenance в неинтерактивном режиме с флагом -p. При обычном запуске не срабатывает. Используйте его для однократной установки зависимостей или плановой очистки, которую вы явно запускаете из CI или скриптов, отдельно от обычного начала сессии. Для инициализации на уровне каждой сессии используйте вместо этого SessionStart.
Значение matcher соответствует флагу CLI, который запустил хук:
Когда вы выполняете
claude --init-only, Claude Code запускает хуки Setup и хуки SessionStart с matcher startup, а затем завершает работу, не начиная диалог.
Когда вы начинаете или продолжаете диалог с -p, необходимо также передать промпт — как аргумент или через stdin. Промпт можно не передавать, если хук SessionStart задаёт initialUserMessage или если вы возобновляете сессию с отложенным вызовом инструмента.
При успешном выполнении --init-only ничего не выводит в терминал. Чтобы убедиться, что хуки выполнились, запустите claude --debug-file <path> --init-only, заменив <path> расположением файла лога, и проверьте в логе записи хуков Setup и SessionStart.
Поскольку Setup срабатывает не при каждом запуске, плагин, которому нужна установленная зависимость, не может полагаться только на Setup. Практичный подход — проверять наличие зависимости при первом использовании и устанавливать её при отсутствии, например хук или скилл, который проверяет наличие ${CLAUDE_PLUGIN_DATA}/node_modules и выполняет npm install, если её нет. О том, где хранить установленные зависимости, см. каталог постоянных данных. Если вы распространяете плагин через маркетплейс, этот подход может не понадобиться: Claude Code автоматически устанавливает подходящие зависимости пакетов Node.js при кэшировании плагина.
Входные данные Setup
Помимо общих входных полей, хуки Setup получают полеtrigger со значением "init" или "maintenance":
Управление решениями Setup
Хуки Setup не могут блокировать; выполнение продолжается при любом коде выхода. При любом коде выхода Claude Code отбрасывает поля вывода JSON хука Setup, такие какsystemMessage, continue и hookSpecificOutput.additionalContext. С -p stdout, stderr и код выхода хука Setup появляются в выводе запуска только как события hook_response, если вы запускаете с --output-format stream-json --verbose.
Хуки Setup имеют доступ к CLAUDE_ENV_FILE. Если ваш хук записывает переменные в этот файл, они сохраняются для последующих команд Bash в сессии, как и в хуках SessionStart. На Setup запускаются только хуки type: "command". Хук type: "mcp_tool" на Setup всегда пропускается, как описано в разделе Поля хуков MCP-инструментов.
InstructionsLoaded
Срабатывает, когда файлCLAUDE.md или .claude/rules/*.md загружается в контекст. Это событие срабатывает в начале сессии для файлов, загружаемых сразу, и позже при отложенной загрузке файлов, например когда Claude обращается к подкаталогу, содержащему вложенный CLAUDE.md, или когда срабатывают условные правила с frontmatter paths:. Хук не поддерживает блокировку или управление решениями. Он выполняется асинхронно для целей наблюдаемости.
Это событие не срабатывает, когда Claude читает AGENTS.md напрямую через настройку Project instructions. Оно срабатывает, когда CLAUDE.md импортирует ваш AGENTS.md — с load_reason, равным include, как для любого другого импортированного файла, — и когда CLAUDE.md является символической ссылкой на него — как обычная загрузка CLAUDE.md.
Matcher сопоставляется с load_reason. Например, используйте "matcher": "session_start", чтобы срабатывать только для файлов, загружаемых в начале сессии, или "matcher": "path_glob_match|nested_traversal", чтобы срабатывать только при отложенных загрузках.
Входные данные InstructionsLoaded
Помимо общих входных полей, хуки InstructionsLoaded получают следующие поля:Управление решениями InstructionsLoaded
Хуки InstructionsLoaded не имеют управления решениями. Они не могут блокировать или изменять загрузку инструкций. Claude Code отбрасывает их поля вывода JSON, такие какsystemMessage и continue. Используйте это событие для журнала аудита, отслеживания соответствия требованиям или наблюдаемости.
UserPromptSubmit
Выполняется при отправке промпта, до того как Claude его обработает. Это позволяет добавлять дополнительный контекст на основе промпта/диалога, проверять промпты или блокировать определённые типы промптов. ХукиUserPromptSubmit срабатывают не только на промпты, которые вы вводите. Claude Code также запускает их, когда:
- срабатывает запланированная задача на уровне сессии, включая итерацию
/loop - Фоновый субагент отчитывается перед сессией, которая его запустила
- Другая сессия отправляет сообщение в ваш основной диалог
UserPromptSubmit имеют таймаут по умолчанию 30 секунд для типов command, http и mcp_tool — меньше, чем 600 секунд по умолчанию для этих типов в большинстве других событий. Поскольку этот хук выполняется перед каждым промптом и блокирует обработку моделью до своего завершения, зависший хук останавливает сессию. Если вашему хуку нужно больше времени, задайте поле timeout в записи хука.
За исключением командного хука, запускаемого с async: true, хук UserPromptSubmit типа command, HTTP или MCP-инструмент, достигший таймаута, отменяется, а его вывод, включая любой additionalContext, отбрасывается. Промпт всё равно доходит до Claude, но без этого контекста. Чтобы вместо этого заблокировать промпт, задайте onFailure: "block" для командного или HTTP-хука. В транскрипте отображается уведомление с именем хука, сработавшим таймаутом и сообщением о том, что вывод был отброшен.
Callback-хук Agent SDK на UserPromptSubmit, достигший таймаута, блокирует промпт с сообщением, в котором указаны хук и таймаут, потому что callback в этом месте может выполнять роль политики, которая не должна пропускать промпт при сбое. Сессия продолжается.
Esc во время проверки промпта
Если вы нажмёте Esc или прервёте сессию, пока хуки ещё проверяют промпт, проверка останавливается, и промпт не доходит до Claude. Текст, который вы ввели в терминале, возвращается в поле ввода промпта, и хуки проверяют его снова, когда вы отправляете его повторно.
Хук может заблокировать промпт в тот же момент, когда вы прерываете сессию. В этом случае промпт остаётся заблокированным: вы видите сообщение о блокировке этого хука, а текст не возвращается в поле ввода.
Входные данные UserPromptSubmit
Помимо общих входных полей, хуки UserPromptSubmit получают полеprompt, содержащее отправленный текст. Вставленное содержимое, свёрнутое в заглушку [Pasted text #N], поступает развёрнутым на своём месте. В сессиях, где Claude Code помечает вставленный текст для Claude, это развёрнутое содержимое находится между строкой <pasted_content id="…"> и строкой </pasted_content id="…">, поэтому учитывайте эти строки, если ваш хук разбирает промпт.
Хуки UserPromptSubmit также получают session_title, когда у сессии есть пользовательское название, с тем же значением, что и поле session_title в SessionStart.
/deploy staging, хук получает эту строку в том виде, в каком вы её ввели, а не развёрнутое содержимое скилла. Если хук блокирует промпт, команда не выполняется, поэтому shell-команды, которые скилл вставляет с помощью !`<command>`, тоже не выполняются. Чтобы вместо этого сопоставлять имя или аргументы команды, используйте UserPromptExpansion.
Управление решениями UserPromptSubmit
ХукиUserPromptSubmit могут управлять тем, обрабатывается ли отправленный промпт, и добавлять контекст. Доступны все поля вывода JSON.
Есть два способа добавить контекст в диалог при коде выхода 0:
- Обычный текстовый stdout: Claude Code добавляет stdout, который он считает обычным текстом, в контекст Claude
- JSON с
additionalContext: используйте формат JSON ниже для большего контроля. ПолеadditionalContextдобавляется как контекст
additionalContext внедряются каждое как системное напоминание, начинающееся с имени хука; Claude читает оба. Чтобы подтвердить доставку, проверьте лог отладки.
Чтобы заблокировать промпт, верните объект JSON с decision, равным "block":
Хук, который блокирует через выход с кодом 2, обрабатывается так же, как
reason: сообщение о блокировке показывает пользователю текст stderr, и он не добавляется в контекст.
Что остаётся после заблокированного промпта
Заблокированный промпт никогда не доходит до Claude, но его текст удаляется не везде. По умолчанию сообщение о блокировке, показываемое пользователю, заканчивается строкойOriginal prompt:, за которой следует отправленный текст, и Claude Code записывает это сообщение в файл транскрипта сессии на диске. Чтобы исключить текст из сообщения, выведите JSON с "suppressOriginalPrompt": true внутри hookSpecificOutput. Это работает независимо от того, блокирует ли хук через decision: "block" или через выход с кодом 2.
suppressOriginalPrompt изменяет только сообщение о блокировке. Отправленный текст всё равно может оказаться в локальных файлах, таких как транскрипт сессии и история ваших промптов, поэтому блокирующий хук не является способом не допустить попадания секрета на диск. Чтобы ограничить или удалить эти файлы, см. Хранение в открытом виде и Очистка локальных данных.
UserPromptExpansion
Выполняется, когда введённая пользователем команда разворачивается в промпт до того, как дойти до Claude. Используйте это, чтобы блокировать прямой вызов определённых команд, внедрять контекст для конкретного скилла или логировать, какие команды вызывают пользователи. Например, хук с matcherdeploy может блокировать /deploy, если нет файла подтверждения, а хук, соответствующий скиллу ревью, может добавлять чек-лист ревью команды в качестве additionalContext.
Это событие покрывает путь, который не покрывает PreToolUse: хук PreToolUse, соответствующий инструменту Skill, срабатывает только когда Claude вызывает этот инструмент, а прямой ввод /skillname обходит PreToolUse. UserPromptExpansion срабатывает на этом прямом пути.
Сопоставляется с command_name. Оставьте matcher пустым, чтобы срабатывать на каждую команду типа prompt.
Входные данные UserPromptExpansion
Помимо общих входных полей, хуки UserPromptExpansion получаютexpansion_type, command_name, command_args, command_source и исходную строку prompt. Поле expansion_type равно slash_command для скиллов и пользовательских команд или mcp_prompt для промптов MCP-сервера.
Управление решениями UserPromptExpansion
ХукиUserPromptExpansion могут блокировать развёртывание или добавлять контекст. Доступны все поля вывода JSON.
Хук, который блокирует через выход с кодом 2, обрабатывается так же, как
reason: сообщение о блокировке показывает пользователю текст stderr.
MessageDisplay
Выполняется, пока сообщение ассистента выводится на экран в потоковом режиме. Claude Code отображает сообщение порциями: каждый раз, когда готова к отрисовке партия только что завершённых строк, хук выполняется один раз с этими строками, и Claude Code отрисовывает на их месте текст замены от хука. Длинное сообщение приводит к нескольким вызовам; короткое может привести только к одному. Используйте MessageDisplay, чтобы:- убирать markdown для минималистичного отображения
- преобразовывать текст, который приложение на Agent SDK показывает своим пользователям
- скрывать API-ключи или внутренние имена хостов из ответов Claude
timeout в записи хука.
MessageDisplay влияет только на отображение: текст замены меняет только то, что отрисовывается на экране. В транскрипте и в том, что видит Claude, сохраняется исходный текст, поэтому Claude никогда не видит замену, а подробный режим показывает оригинал. Хук получает только текст сообщений ассистента, поэтому результаты инструментов и вводимый вами текст отображаются без изменений.
MessageDisplay не поддерживает matcher и срабатывает для каждого сообщения ассистента, которое передаёт текст в потоке; сообщения без текста, например ответы, состоящие только из вызовов инструментов, его не вызывают.
В неинтерактивных запусках, включая запросы Agent SDK и claude -p, MessageDisplay выполняется один раз на сообщение ассистента, а не один раз на партию строк. Единственный вызов поступает после завершения сообщения и содержит полный текст сообщения: index равен 0, final равен true, а delta содержит всё сообщение. Хук, собирающий текст delta для каждого сообщения, получает в обоих режимах одинаковый итоговый текст.
Входные данные MessageDisplay
Помимо общих входных полей, хуки MessageDisplay получают идентификаторы хода и сообщения, позицию этого вызова в сообщении и новый текст вdelta. Границы партий зависят от того, как передаётся текст в потоке, поэтому используйте index и final для отслеживания продвижения по сообщению, а не рассчитывайте на определённую группировку строк.
Вывод MessageDisplay
Помимо полей вывода JSON, доступных всем хукам, хуки MessageDisplay могут вернутьdisplayContent, чтобы заменить delta на экране:
Хуки MessageDisplay не имеют управления решениями. Они не могут блокировать сообщение или изменять то, что сохраняется в транскрипте или отправляется Claude. Claude Code учитывает
displayContent из их вывода JSON и отбрасывает systemMessage и continue.
Этот пример убирает форматирование markdown из ответов Claude для отображения обычным текстом. Скрипт считывает каждую партию из stdin, удаляет из delta маркеры полужирного шрифта и обратные кавычки встроенного кода и возвращает результат как displayContent.
- macOS/Linux
- Windows (PowerShell)
Зарегистрируйте командный хук для события в файле настроек:Сохраните этот скрипт в
.claude/hooks/plain-display.sh в своём проекте и сделайте его исполняемым с помощью chmod +x:jq, Claude Code отображает исходный текст и отмечает сбой только в выводе отладки, а не в сессии.
PreToolUse
Выполняется после того, как Claude сформирует параметры инструмента, и до обработки вызова инструмента. Сопоставляется с любым именем инструмента, кромеEndConversation: встроенными инструментами, такими как Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion и ExitPlanMode, а также любыми именами MCP-инструментов.
Чтобы запускать хук при изменении конкретного файла на диске, независимо от того, что его записало, используйте FileChanged вместо сопоставления инструментов редактирования файлов по имени. В отличие от PreToolUse, Claude Code запускает хуки FileChanged после изменения, и у них нет управления решениями, поэтому они не могут заблокировать запись.
Используйте управление решениями PreToolUse, чтобы разрешить, запретить, запросить подтверждение или отложить вызов инструмента.
Хук обратного вызова Agent SDK в PreToolUse, превысивший свой таймаут, блокирует вызов инструмента.
Входные данные PreToolUse
Помимо общих входных полей, хуки PreToolUse получаютtool_name, tool_input и tool_use_id.
Для MCP-инструмента входные данные также содержат mcp_server — объект с name сервера и source, указывающим, откуда взято определение сервера. Значения source включают plugin, sdk и области действия конфигурации, такие как user и project. McpServerProvenance в справочнике Agent SDK перечисляет их все и описывает, как обрабатывать нераспознанное значение. Основывайте решения о доверии на source, а не на name или префиксе имени инструмента mcp__<server>__. Для поля mcp_server требуется Claude Code v2.1.274 или новее.
Для файловых инструментов Write, Edit и Read значение tool_input.file_path всегда абсолютное:
- Claude Code раскрывает
~и относительные пути до выполнения хуков, поэтому хук, сопоставляющий пути, нельзя обойти через~или относительную запись того же пути - В Windows путь поступает с обратными косыми чертами в качестве разделителей, даже если ваш хук выполняется в Git Bash, где
$PWDвыглядит как/c/project - Сравнение, записанное с прямыми косыми чертами, например проверка
/src/, никогда не совпадёт с путём с обратными косыми чертами, и вызов инструмента продолжится так, будто хуку нечего было блокировать - Нормализуйте разделители перед сравнением:
FILE_PATH="${FILE_PATH//\\//}"в Bash илиfile_path.replace("\\", "/")в Python, а затем сопоставляйте сегмент пути, например/src/, а не привязывайтесь к началу с помощью^, поскольку путь абсолютный
Write в Windows передаёт:
tool_input зависят от инструмента:
Bash
Выполняет shell-команды.
Когда команда Bash изменяет файлы в репозитории Git, Claude Code может записывать, что изменилось. Он записывает изменения в любом режиме разрешений, когда это включено настройкой
bashEditDiffEnabled; в описании этой настройки указано, какие файлы могут её задавать. В противном случае он записывает их только в авторежиме и режиме bypassPermissions, и только когда Claude Code направляет Claude редактировать файлы через Bash. Задайте bashEditDiffEnabled равным false, чтобы отключить запись. Фоновые команды и команды только для чтения не содержат diff.
Затем ваш хук PostToolUse получает изменённые файлы в tool_response.bashEditDiff. Список охватывает то, что изменилось в репозитории во время выполнения команды. Файлы, которые игнорирует Git, и файлы в подмодулях не перечисляются. Требуется Claude Code v2.1.269 или новее.
Список формируется по принципу «насколько возможно» и доступен в публичной бета-версии. Claude Code может пропустить изменение, включить файл, который одновременно изменил другой процесс, или остановиться на своих ограничениях размера. Структура поля может измениться. Используйте список, чтобы найти, что проверить, а не для принудительного применения политики.
changedFiles и files перечисляют то, что изменила команда; остальные поля указывают, насколько полон и надёжен этот список.
PowerShell
Выполняет команды PowerShell. Доступность по платформам см. в описании инструмента PowerShell. Поля совпадают с инструментом Bash, строка команды находится вcommand:
В хуках, проверяющих shell-команды, используйте matcher
Bash|PowerShell, чтобы они охватывали оба инструмента:
- В Windows везде, где включён инструмент PowerShell, Claude считает PowerShell основной оболочкой и направляет shell-команды через него.
- В Windows без Git Bash инструмент включается автоматически, и Claude Code вообще не регистрирует инструмент Bash.
- Хук, соответствующий только
Bash, там никогда не срабатывает.
Write
Создаёт или перезаписывает файл.Edit
Заменяет строку в существующем файле.Read
Читает содержимое файла.Glob
Находит файлы, соответствующие glob-шаблону.Grep
Ищет в содержимом файлов с помощью регулярных выражений.WebFetch
Загружает и обрабатывает веб-содержимое.WebSearch
Выполняет поиск в интернете.Agent
Запускает субагента.
Когда вызов Agent на переднем плане завершается, ваш хук PostToolUse получает результат субагента и телеметрию запуска в
tool_response. Читайте эти поля, чтобы проанализировать запуск; для сводных данных по токенам и стоимости по всем субагентам используйте счётчики токенов и стоимости, отфильтрованные по query_source "subagent", поскольку totalTokens и usage охватывают только последний запрос:
В Claude Code v2.1.271 или новее субагент, работающий с инструментом
SubagentHandback, который Claude Code предоставляет в авторежиме, передаёт свой отчёт через этот инструмент, а не возвращает его как текст. Тогда поле content его результата completed содержит краткую заметку об этой передаче, а не сам отчёт. Чтобы прочитать отчёт, настройте хук PreToolUse или PostToolUse с matcher SubagentHandback и читайте tool_input.message.
Для фоновых субагентов инструмент возвращает результат, когда задача переходит в фон, поэтому tool_response не содержит полей использования: фоновый запуск возвращается сразу, а задача на переднем плане, которую Claude Code переводит в фон во время выполнения, возвращается в момент этого перехода. Он содержит status: "async_launched", agentId, description, prompt, outputFile и resolvedModel.
В ответе completed поле resolvedModel указывает модель, на которой запустился субагент; она может отличаться от значения model в tool_input, например когда применяется availableModels или другое переопределение. В ответе async_launched поле resolvedModel указывает модель, использовавшуюся в момент перехода агента в фон, поэтому смена модели, произошедшая до перехода в фон, отражается там.
AskUserQuestion
Задаёт пользователю от одного до четырёх вопросов с вариантами ответа.ExitPlanMode
Представляет план и просит пользователя утвердить его, прежде чем Claude выйдет из режима планирования. Claude записывает план в файл на диске перед вызовом инструмента, поэтому буквальныйtool_input от модели обычно пуст. Claude Code внедряет содержимое плана и путь к файлу перед передачей входных данных хукам.
В
PostToolUse tool_response — это объект с полями plan и filePath, содержащими утверждённый план, а также внутренними флагами статуса. Читайте содержимое плана из tool_response.plan, а не перечитывайте файл с диска.
Управление решениями PreToolUse
ХукиPreToolUse могут управлять тем, будет ли выполнен вызов инструмента. В отличие от других хуков, использующих поле decision верхнего уровня, PreToolUse возвращает своё решение внутри объекта hookSpecificOutput. Это даёт более богатые возможности управления: четыре исхода (разрешить, запретить, запросить подтверждение или отложить), а также возможность изменить входные данные инструмента перед выполнением. О том, как разрешить или переписать команду Monitor, см. Сопоставление команд Monitor.
Когда несколько хуков PreToolUse возвращают разные решения, приоритет таков:
deny > defer > ask > allow.
Хук, который блокирует через выход с кодом 2, обрабатывается так же, как "deny": Claude видит сообщение stderr как причину запрета.
Когда хук возвращает "ask", запрос разрешения, показываемый пользователю, содержит метку, указывающую, откуда взят хук: [settings] для хука из любого файла настроек или из frontmatter агента, [plugin:<name>] для хука плагина или [skill] для хука из frontmatter скилла. Это помогает пользователям понять, какой источник конфигурации запрашивает подтверждение.
Ранее PreToolUse использовал поля
decision и reason верхнего уровня, но для этого события они объявлены устаревшими. Используйте вместо них hookSpecificOutput.permissionDecision и hookSpecificOutput.permissionDecisionReason. Устаревшие значения "approve" и "block" соответствуют "allow" и "deny". Другие события, такие как PostToolUse и Stop, по-прежнему используют decision и reason верхнего уровня в качестве текущего формата.updatedInput в PreToolUse в авторежиме
Когда хук возвращает updatedInput без permissionDecision в авторежиме, Claude Code может отклонить изменённый вызов. Сервер проверил ввод, который написал Claude, а не ввод, который вернул хук. В интерактивных сессиях терминала и сессиях VS Code с прямым подключением к Anthropic API отказ содержит такой текст:
- Он может отклонить вызов или отправить его на запрос разрешения
- Любой возвращённый им
updatedInputотбрасывается, поэтому выполняется то, что проверил сервер
command-хук в настройках или в файле хуков плагина может изменять вызовы Bash, PowerShell, Monitor, Edit, Write и NotebookEdit без этого отказа. Отказ всё же может появиться, например когда какой-либо хук изменил более ранний вызов в том же ответе Claude или команда Bash запрашивает выполнение вне песочницы.
Решения ask в PreToolUse в авторежиме
В авторежиме "ask" хука PreToolUse обычно отправляет вызов инструмента в запрос разрешения, и классификатор по нему не решает. Некоторые вызовы идут другим путём:
- Вызовы, которые также помечают собственные проверки Claude Code: к ним относятся запись в защищённые пути и команды, запрашивающие выполнение вне песочницы. Классификатор может отклонить такой вызов до появления какого-либо запроса. Если он одобряет, вызов обычно всё равно попадает в запрос.
- Вызовы
SubagentHandback: этот инструмент передаёт отчёт субагента. Для него запрос не появляется, и вызов выполняется, даже если классификатор возражает.
SubagentHandback, верните "deny". Тогда Claude Code сообщает субагенту — до трёх раз, — что его отчёт не был доставлен и что нужно вызвать инструмент снова. Если ваш хук продолжает отклонять, субагент завершается, не доставив отчёт через этот инструмент, а ожидающий его диалог получает примечание об этом.
Инструменты, требующие взаимодействия с пользователем
AskUserQuestion и ExitPlanMode требуют взаимодействия с пользователем. В неинтерактивном режиме с флагом -p Claude Code предлагает их, только если у запуска есть хост разрешений, принимающий запрос, например callback canUseTool в Agent SDK.
Хук PreToolUse удовлетворяет этому требованию, если он делает следующее:
- Считывает входные данные инструмента из stdin
- Собирает ответ через ваш собственный интерфейс
- Возвращает
permissionDecision: "allow"вместе сupdatedInput, содержащим ответ, чтобы инструмент выполнился без запроса
"allow" для этих инструментов недостаточно.
Для AskUserQuestion верните исходный массив questions и добавьте объект answers, сопоставляющий текст каждого вопроса с выбранным ответом. Этот вывод отвечает на один вопрос вариантом React:
_meta["anthropic/requiresUserInteraction"], обрабатывается строже: хук не может пропустить его запрос подтверждения с помощью "allow", с updatedInput или без, поскольку Claude Code не может убедиться, что хук собрал взаимодействие, необходимое инструменту.
Отложить вызов инструмента на потом
"defer" предназначен для интеграций, которые запускают claude -p как подпроцесс и читают его вывод в формате JSON, например для приложения на Agent SDK или собственного интерфейса, построенного поверх Claude Code. Он позволяет вызывающему процессу приостановить Claude на вызове инструмента, собрать ввод через собственный интерфейс и продолжить с того места, где работа была прервана.
Claude Code учитывает "defer" только в неинтерактивном режиме с флагом -p и только когда Claude делает единственный вызов инструмента за ход:
- В интерактивных сессиях: записывается предупреждение, а результат хука игнорируется
- Несколько вызовов инструментов одновременно:
"defer"игнорируется с предупреждением, и инструмент проходит обычный процесс проверки разрешений
AskUserQuestion: Claude хочет что-то спросить у пользователя, но терминала для ответа нет. Запуск -p предлагает AskUserQuestion только при наличии хоста разрешений, например MCP-инструмента, который вы передаёте через --permission-prompt-tool, поэтому начинайте запуск с ним. Цикл работает так:
- Claude вызывает
AskUserQuestion. Срабатывает хукPreToolUse. - Хук возвращает
permissionDecision: "defer". Инструмент не выполняется. Ход завершается результатом сstop_reason: "tool_deferred", а ожидающий вызов инструмента остаётся в транскрипте. - Вызывающий процесс читает
deferred_tool_useиз результата SDK, показывает вопрос в своём интерфейсе и ждёт ответа. - Вызывающий процесс выполняет
claude -p --resume <session-id>с тем же хостом разрешений. Тот же вызов инструмента снова вызываетPreToolUse. - Хук возвращает
permissionDecision: "allow"с ответом вupdatedInput. Инструмент выполняется, и Claude продолжает работу.
deferred_tool_use содержит id, name и input инструмента:
cleanupPeriodDays, по умолчанию через 30 дней.
Если ответ ещё не готов к моменту возобновления, хук может снова вернуть "defer". Если при этом возобновлении вы отправили промпт, Claude ответит на него без результата инструмента и не выполнит вызов. Возобновляйте без промпта, пока хук не вернёт "allow" или "deny".
Чтобы возобновить отложенную сессию в режиме планирования, передайте
--permission-prompt-tool вместе с --resume, чтобы Claude Code мог представить план на утверждение. Прочие условия см. в разделе Возобновление в режиме планирования с -p. Требуется Claude Code v2.1.246 или новее.При возобновлении с -p Claude Code не восстанавливает никакой другой сохранённый режим разрешений. Запуск начинается в том режиме разрешений, в котором начался бы новый запуск claude -p, поэтому снова передайте --permission-mode или --dangerously-skip-permissions, если отложенная сессия их использовала.Возобновление, когда отложенный инструмент недоступен
Результат сstop_reason: "tool_deferred_unavailable" означает, что инструмент отложенного вызова недоступен в возобновлённой сессии, например потому что его MCP-сервер не подключён. Ваш хук PreToolUse не срабатывает, а поле deferred_tool_use результата указывает инструмент.
Пока вызов ожидает, любой отправленный вами промпт не доходит до Claude, а запуск завершается с кодом выхода 1. Поле result начинается с <tool> isn't available, so its deferred call didn't run. Выполните вызов или отбросьте его, чтобы он не выполнялся:
- Чтобы выполнить вызов: сделайте инструмент доступным и возобновите сессию снова.
- Чтобы отбросить вызов: возобновите сессию с промптом, установив
CLAUDE_CODE_DROP_UNAVAILABLE_DEFERRED_TOOLв1. Claude получит ошибку в качестве результата вызова и ответит на ваш промпт. При следующем возобновлении ожидающих вызовов не будет.
tool_deferred_unavailable с пустым полем result, а затем ответ Claude во втором результате. Он завершается с кодом выхода 0, если только этот ответ не является ошибкой. Если вы установите переменную и не отправите промпт, запуск всё равно отбросит вызов и завершится с кодом выхода 1.
До v2.1.319 Claude Code отвечал на промпт без результата инструмента и отбрасывал вызов. В этих версиях отправьте новый промпт, когда инструмент станет доступен.
PermissionRequest
Выполняется, когда Claude Code собирается запросить у вас разрешение на использование инструмента. В сессиях, которые не могут показать запрос, например у фоновых субагентов в неинтерактивном режиме, Claude Code всё равно запускает эти хуки, и если ни один хук не вернёт решение, он запрещает вызов инструмента. Для вызова, который доходит до--permission-prompt-tool или callback canUseTool в Agent SDK, хуки выполняются параллельно с вашим хостом, и применяется решение того, кто примет его первым.
Используйте управление решениями PermissionRequest, чтобы разрешать или запрещать от имени пользователя.
Используйте это событие, когда вам нужен сигнал в тот момент, когда Claude запрашивает разрешение на использование инструмента. Claude Code запускает хук Notification с типом permission_prompt только после того, как запрос прождёт около шести секунд.
Claude Code не запускает хуки PermissionRequest для сетевого запроса команды, выполняемой в песочнице. Чтобы получить сигнал для такого запроса, используйте тип уведомления permission_prompt.
Сопоставляется с именем инструмента, с теми же значениями, что и PreToolUse.
Входные данные PermissionRequest
Хуки PermissionRequest получают поляtool_name и tool_input, как и хуки PreToolUse, но без tool_use_id. Для инструмента MCP они также получают объект mcp_server. Необязательный массив permission_suggestions содержит обновления разрешений, которые Claude Code предлагает для этого запроса, например добавление разрешающего правила или изменение режима разрешений.
Массив permission_suggestions не является точным списком вариантов, которые вы видите, поскольку каждое диалоговое окно разрешения формирует собственные варианты. Некоторые диалоговые окна, например окно для правок файлов, вообще не читают этот массив и получают варианты из самого запроса. Диалоговое окно, которое читает массив, всё равно может скрыть вариант, предложение для которого остаётся в массиве, например когда allowManagedPermissionRulesOnly скрывает варианты сохранения правил. Оно также может предлагать варианты, для которых нет записи в предложениях, например Yes, and switch to auto mode, который меняет режим разрешений напрямую, а не через обновление разрешений.
Хуки PreToolUse запускаются перед каждым вызовом инструмента, независимо от того, требуется ли для него разрешение. Хуки PermissionRequest запускаются только тогда, когда Claude Code собирается запросить у вас разрешение, или когда он в противном случае автоматически отклонил бы вызов, для которого невозможно показать запрос. Ни одно из этих событий не срабатывает для EndConversation.
Управление решениями PermissionRequest
ХукиPermissionRequest могут разрешать или отклонять запросы разрешений. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может вернуть объект decision со следующими полями, специфичными для события:
Чтобы разрешить или переписать команду Monitor, см. раздел Сопоставление команд Monitor.
Хук, завершающийся с кодом 2 без объекта
decision, оставляет процесс обработки разрешения без изменений, а его stderr отбрасывается. Чтобы предоставить или отклонить запрос, верните объект decision.
Записи обновления разрешений
Поле выводаupdatedPermissions и поле ввода permission_suggestions используют один и тот же массив объектов-записей. Каждая запись имеет поле type, определяющее её остальные поля, и поле destination, которое управляет тем, куда записывается изменение.
В
rules для правила разрешений, записанного как Tool(specifier), toolName — это имя инструмента, а ruleContent — спецификатор:
Bash(npm test):{ "toolName": "Bash", "ruleContent": "npm test" }BashилиBash(*):{ "toolName": "Bash" }, потому что оба соответствуют всему инструментуmcp__github__get_issue:{ "toolName": "mcp__github__get_issue" }
ruleContent в разрешающем правиле для MCP-инструмента. Если его добавить, правило не будет соответствовать ни одному вызову.
setMode с bypassPermissions вступает в силу, только если вы запустили сессию, когда режим обхода уже был доступен: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions или permissions.defaultMode: "bypassPermissions" в пользовательских настройках, --settings или управляемых настройках. В противном случае обновление ничего не делает. Обновление также ничего не делает, когда permissions.disableBypassPermissionsMode отключает этот режим или когда сессия запускается в ограниченном режиме.bypassPermissions никогда не сохраняется как defaultMode, независимо от destination.destination в каждой записи определяет, останется ли изменение в памяти или будет сохранено в файл настроек.
Хук может вернуть одно из полученных
permission_suggestions в качестве собственного вывода updatedPermissions.
PostToolUse
Запускается сразу после успешного завершения инструмента. Сопоставляется по имени инструмента, с теми же значениями, что и PreToolUse. Используйте более широкое сопоставление, когда имя инструмента не подходит в качестве фильтра:- Чтобы запускать хук после успешного завершения любого инструмента, опустите
matcherили установите его в"*". Тогда ваш хук может сам определить, что изменилось, например выполнивgit status --porcelain, который также перечисляет неотслеживаемые файлы, пропускаемыеgit diff. Для вызовов инструментов, завершившихся с ошибкой, добавьте тот же хук в PostToolUseFailure. - Чтобы запускать хук при изменении определённого файла на диске, независимо от того, кто его записал, используйте FileChanged. Claude Code не запускает хук
PostToolUseс matcherEdit|Write, когда тот же файл перезаписывает командаBashили процесс вне Claude Code.
PostToolUse после прерывания
Если хукPostToolUse останавливается раньше времени после того, как вы отправили сообщение или прервали работу во время вызова инструмента, проверьте, нажали ли вы Ctrl+Enter, Enter или Esc:
Ctrl+Enter: если нажатие завершает ход во время работы инструмента и инструмент завершается, ваши хукиPostToolUseвыполняются на его результате, и у них есть 10 секунд на завершение. Клавиша действует какEsc, если вы поставили в очередь команду!перед своими сообщениями или фоновые задачи отключены, например с помощьюCLAUDE_CODE_DISABLE_BACKGROUND_TASKS.Enter: сообщение, поставленное в очередь с помощьюEnter, не прерывает ход, поэтому хуки сохраняют свой обычныйtimeout.Esc: вы сразу отменяете хуки, поэтому отправляйте сообщение с помощьюEnterилиCtrl+Enter, когда хуки должны выполниться.
timeout. Чтобы позволить более долгому хуку команды завершиться, запускайте его с async: true; это означает, что хук не сможет заблокировать или переписать результат.
До v2.1.319 хуки не запускались после того, как вы прерывали ход сообщением, а работающий хук останавливался.
Входные данные PostToolUse
ХукиPostToolUse срабатывают после того, как инструмент уже успешно выполнился. Входные данные включают как tool_input — аргументы, переданные инструменту, так и tool_response — возвращённый им результат. Точная схема обоих полей зависит от инструмента. Пути в tool_input файловых инструментов приходят в том же формате, что и для PreToolUse: всегда абсолютные, с нативными разделителями платформы, то есть с обратными слешами в Windows. Для инструмента MCP входные данные также содержат объект mcp_server.
Управление решениями PostToolUse
ХукиPostToolUse могут предоставлять Claude обратную связь после выполнения инструмента. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может вернуть следующие поля, специфичные для события:
Пример ниже заменяет вывод вызова
Bash. Значение замены соответствует форме вывода инструмента Bash:
Аннотирование результата для классификатора авторежима
ВернитеclassifierContext, чтобы отправить короткую заметку о результате вызова инструмента классификатору авторежима, а не Claude. Заметка предназначена для проверки классификатором последующих действий. Поле требует Claude Code v2.1.236 или новее.
Пример ниже сообщает классификатору, откуда получен вывод запроса:
- Хуки, настроенные в Claude Code: для хуков из файлов настроек, плагинов, скиллов и frontmatter агентов классификатор рассматривает примечание как непроверенный контекст, предоставленный приложением. Примечание никогда не устанавливает намерение пользователя, и если оно утверждает, что вы что-то одобрили или запросили, классификатор сверяет это утверждение с вашими собственными сообщениями в диалоге
- Внутрипроцессные обратные вызовы Agent SDK: когда приложение, встраивающее Claude Code, регистрирует хук как обратный вызов TypeScript SDK и возвращает примечание во время активной сессии, классификатор может учитывать переданное в примечании утверждение пользователя как намерение пользователя. Такое утверждение может удовлетворить требование согласия, которое классификатор принял бы от отправленного вами сообщения, но оно никогда не снимает блокировку, которую не смогло бы снять и ваше собственное сообщение. После возобновления сессии Claude Code рассматривает восстановленные примечания как непроверенный контекст. Когда хуки из обеих групп аннотируют один и тот же вызов, классификатор рассматривает объединённое примечание как непроверенное
- Длина: Claude Code ограничивает примечания для одного вызова инструмента 2000 символами и обрезает остальное. Ограничение общее для всех хуков, отвечающих на этот вызов
- Только синхронные ответы: Claude Code игнорирует это поле в ответе хука, который выполняется в фоне, поскольку такой ответ приходит после того, как Claude Code записал результат инструмента
- Вызовы, которые классификатор не записывает: транскрипт классификатора не включает операции только для чтения, такие как чтение файлов и поиск. Claude Code отбрасывает примечание, прикреплённое к одному из таких вызовов
- Взаимодействие с перезаписью: когда примечание описывает вывод, который вы заменяете с помощью
updatedToolOutput, возвращайте оба поля в одном ответе хука. Claude Code отбрасывает примечание, если эта перезапись отклонена или её заменяет перезапись другого хука. Claude Code доставляет примечание, возвращённое без перезаписи, даже если другой хук перезаписывает вывод
PostToolUseFailure
Запускается, когда инструмент, начавший выполнение, завершается с ошибкой: инструмент выбросил ошибку или инструмент MCP вернул результат с ошибкой. Используйте это для логирования сбоев, отправки оповещений или предоставления Claude корректирующей обратной связи. Сопоставляется по имени инструмента, с теми же значениями, что и PreToolUse.Это событие не срабатывает для вызовов инструментов, отклонённых до выполнения: неизвестное имя инструмента, входные данные, не прошедшие проверку схемы или специфичную для инструмента валидацию, или отказ в разрешении. Отклонения при валидации возвращаются как результаты
tool_use_error и происходят до запуска хуков, поэтому не вызывают ни PreToolUse, ни PostToolUseFailure. Отказы в разрешении вызывают PreToolUse, но не это событие; см. PermissionDenied.Входные данные PostToolUseFailure
Хуки PostToolUseFailure получают те же поляtool_name и tool_input, что и PostToolUse, а также информацию об ошибке в виде полей верхнего уровня. Для инструмента MCP они также получают объект mcp_server. Например, неудавшаяся команда npm test может передать:
Строка
error обычно совпадает с текстом, который Claude получает как результат неудавшегося инструмента. Её формат зависит от инструмента и сбоя. Ориентируйте свой хук на tool_name, is_interrupt и первую строку Exit code N; остальную часть строки рассматривайте как текст для отображения, а не как стабильный формат.
- Для Bash и PowerShell команда, которая выполнилась и завершилась, даёт первую строку
Exit code N, а затем любой вывод команды одним блоком, где stdout и stderr перемежаются - Данные события также могут содержать просто сообщение о сбое без строки с кодом выхода, когда Claude Code не смог запустить сам процесс оболочки
- Claude Code обрезает длинные строки посередине вокруг маркера
... [N characters truncated] ...и может вставлять собственные строки, напримерCommand timed out after 2m 0s
Управление решениями PostToolUseFailure
ХукиPostToolUseFailure могут предоставлять Claude контекст после сбоя инструмента. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может вернуть следующие поля, специфичные для события:
PostToolBatch
Запускается один раз после того, как все вызовы инструментов в пакете завершены, до того как Claude Code отправит следующий запрос модели.PostToolUse срабатывает один раз для каждого инструмента, а значит, срабатывает параллельно, когда Claude делает параллельные вызовы инструментов. PostToolBatch срабатывает ровно один раз со всем пакетом, поэтому это подходящее место для внедрения контекста, который зависит от набора выполненных инструментов, а не от какого-либо одного инструмента. Для этого события нет matcher.
Входные данные PostToolBatch
Помимо общих полей входных данных, хуки PostToolBatch получаютtool_calls — массив, описывающий каждый вызов инструмента в пакете:
tool_response содержит то же содержимое, которое модель получает в соответствующем блоке tool_result. Значение — это сериализованная строка или массив блоков содержимого, ровно в том виде, в каком его выдал инструмент. Для Read это означает текст с префиксами номеров строк, а не необработанное содержимое файла. Ответы могут быть большими, поэтому разбирайте только нужные поля.
Форма
tool_response отличается от формы в PostToolUse. PostToolUse передаёт структурированный объект Output инструмента, например {filePath: "...", type: "create"} для Write; PostToolBatch передаёт сериализованное содержимое tool_result, которое видит модель.Управление решениями PostToolBatch
ХукиPostToolBatch могут внедрять контекст для Claude. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может вернуть следующие поля, специфичные для события:
decision: "block" или continue: false останавливает агентный цикл перед следующим вызовом модели. Сообщение о блокировке берётся из JSON-поля reason или stopReason либо из stderr при коде выхода 2. Вы видите его как предупреждение в транскрипте, и оно остаётся в диалоге, поэтому Claude видит его при продолжении диалога.
PermissionDenied
Запускается, когда авторежим отклоняет вызов инструмента, в том числе когда он отклоняет его без вердикта классификатора, потому что проверка безопасности, отдельная от авторежима, отклонила собственный запрос классификатора или его ответ не удалось разобрать. Этот хук срабатывает только в авторежиме: он не запускается, когда вы вручную отклоняете диалоговое окно разрешения, когда хукPreToolUse блокирует вызов или когда срабатывает правило deny. Используйте его для логирования отказов, корректировки конфигурации или для того, чтобы сообщить модели, что она может повторить попытку вызова инструмента.
Сопоставляется по имени инструмента, с теми же значениями, что и PreToolUse.
Входные данные PermissionDenied
Помимо общих полей входных данных, хуки PermissionDenied получаютtool_name, tool_input, tool_use_id и reason. Для инструмента MCP они также получают объект mcp_server.
Управление решениями PermissionDenied
Хуки PermissionDenied могут сообщить модели, что она может повторить попытку отклонённого вызова инструмента. Верните JSON-объект сhookSpecificOutput.retry, установленным в true:
retry равно true, Claude Code добавляет в диалог сообщение, сообщающее модели, что она может повторить попытку вызова инструмента. Сам отказ Claude Code не отменяет. Если ваш хук не возвращает JSON или возвращает retry: false, отказ остаётся в силе, и модель получает исходное сообщение об отклонении.
Claude Code игнорирует retry: true, когда классификатор не вынес вердикта по действию: его ответ не удалось разобрать или проверка безопасности, отдельная от авторежима, отклонила собственный запрос классификатора. Для таких отказов Claude Code уже сообщает модели в сообщении об отклонении, следует ли повторить попытку позже или двигаться дальше.
Notification
Запускается, когда Claude Code отправляет уведомления. Сопоставляется по типу уведомления. Опустите matcher, чтобы запускать хуки для всех типов уведомлений. Вы получаете эти события хуков даже при отключённых уведомлениях на рабочем столе: настройкаpreferredNotifChannel, включая notifications_disabled, меняет только способ оповещения, но не то, запускается ли ваш хук.
Типы
quota_auto_resume_fired, quota_auto_resume_stale и quota_auto_resume_disabled требуют Claude Code v2.1.234 или новее. Тип auth_storage_failure требует v2.1.288 или новее.
В терминальных сессиях для permission_prompt при сетевом запросе команды в песочнице требуется Claude Code v2.1.246 или новее.
Для agent_needs_input при вопросе участника команды о настройке терминала требуется Claude Code v2.1.248 или новее.
Типы
permission_prompt, idle_prompt, elicitation_dialog и elicitation_url_dialog используют то же время срабатывания, что и уведомления на рабочем столе, поэтому в терминальных сессиях вы видите их, только когда кажется, что вы отошли от терминала:- Ожидайте
permission_prompt, когда вы ничего не вводили около шести секунд. Таймер запускается при появлении запроса разрешения, и каждое нажатие клавиши откладывает его. Чтобы запускать хук сразу, когда Claude запрашивает разрешение на использование инструмента, используйте вместо этого PermissionRequest. - Ожидайте
idle_promptпримерно через 60 секунд после того, как Claude закончит отвечать, и только если вы с тех пор ничего не вводили и ни один фоновый агент, например фоновый субагент, всё ещё не выполняется. Claude Code не отправляетidle_prompt, пока ожидает сброса лимита использования claude.ai. Когда ожидание завершается само по себе, вместо этого срабатывает один из типовquota_auto_resume_*. - Ожидайте
elicitation_dialogдля формы запроса данных илиelicitation_url_dialogдля запроса URL в браузере, когда вы ничего не вводили около шести секунд. Оба используют тот же шестисекундный порог, что иpermission_prompt: таймер запускается при появлении диалогового окна, и каждое нажатие клавиши откладывает его.
permission_prompt в сессиях, где он отправляет запросы разрешений в обратный вызов canUseTool Agent SDK — именно так Claude Desktop и расширение VS Code размещают Claude Code:
- Ожидайте
permission_promptпримерно через шесть секунд после того, как Claude запросит разрешение. Claude Code не откладывает его, пока вы вводите текст. - Если вы или хук PermissionRequest ответите раньше, Claude Code не запускает
permission_prompt. - Установите
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSв1, чтобы отключитьpermission_promptв этих сессиях.
permission_prompt в этих сессиях не срабатывал.
Используйте отдельные matcher, чтобы запускать разные обработчики в зависимости от типа уведомления. Эта конфигурация запускает скрипт оповещения, специфичный для разрешений, когда Claude нужно подтверждение разрешения, и другое уведомление, когда Claude бездействует:
Входные данные Notification
Помимо общих полей входных данных, хуки Notification получаютmessage с текстом уведомления, необязательный title и notification_type, указывающий, какой тип сработал.
systemMessage и continue, но по-прежнему выводит terminalSequence, на что и опирается пример уведомления на рабочем столе. Хуки Notification предназначены для побочных эффектов, например пересылки уведомления во внешний сервис.
SubagentStart
Запускается, когда Claude порождает субагента с помощью инструмента Agent, когда Claude возобновляет субагента, а также каждый раз, когда внутрипроцессный участник команды агентов обрабатывает новое сообщение. Поддерживает matcher для фильтрации по имени типа агента. Для встроенных агентов это имя агента, напримерgeneral-purpose, Explore или Plan. Для пользовательских субагентов это поле name из frontmatter агента, а не имя файла.
Для субагентов, поставляемых плагином, тип агента — это идентификатор в области плагина, например my-plugin:reviewer, а не просто имя из frontmatter. Двоеточие переводит имя в области плагина на путь регулярных выражений, поэтому для точного совпадения привяжите matcher с помощью ^ и $: ^my-plugin:reviewer$.
Входные данные SubagentStart
Помимо общих полей входных данных, хуки SubagentStart получаютagent_id с уникальным идентификатором субагента и agent_type с именем агента, по которому фильтрует matcher.
SubagentStop
Запускается, когда субагент Claude Code закончил отвечать. Сопоставляется по типу агента, с теми же значениями, что и SubagentStart.Входные данные SubagentStop
Помимо общих полей входных данных, хуки SubagentStop получаютstop_hook_active, agent_id, agent_type, agent_transcript_path и last_assistant_message. Поле agent_type — это значение, используемое для фильтрации через matcher. transcript_path — это транскрипт основной сессии, а agent_transcript_path — собственный транскрипт субагента, хранящийся во вложенной папке subagents/. Поле last_assistant_message содержит текстовое содержимое финального ответа субагента, поэтому хуки могут получить к нему доступ без разбора файла транскрипта.
Не каждое событие SubagentStop исходит от субагента, порождённого Claude. Claude Code также запускает внутренних агентов для некоторых своих функций, таких как предложения промптов и побочные вопросы /btw, и SubagentStop срабатывает и тогда, когда один из них завершается. Для таких событий agent_type — это имя агента, под которым выполняется сама сессия, например заданное с помощью --agent или настройки agent, и пустая строка, когда сессия выполняется без него.
matcher, в котором указаны типы агентов, не совпадает с пустым agent_type. Хук, у которого matcher опущен, равен "" или "*" либо является регулярным выражением, совпадающим с пустой строкой, запускается и для событий с пустым agent_type.
В Claude Code v2.1.271 или новее субагент, работающий с инструментом SubagentHandback, передаёт свой отчёт через этот инструмент перед остановкой. Тогда поле last_assistant_message содержит заключительный текст субагента, если он есть, который не является переданным отчётом. Отчёт — это входной параметр message того вызова, который хук PreToolUse или PostToolUse с matcher на SubagentHandback получает как tool_input.message.
Хуки SubagentStop также получают массивы background_tasks и session_crons, описанные в разделе Входные данные Stop. Оба массива относятся к родительской сессии, а не к субагенту.
hookSpecificOutput.additionalContext с hookEventName, установленным в "SubagentStop", для обратной связи, не являющейся ошибкой, которая продолжает работу субагента. Возврат decision: "block" с reason продолжает работу субагента и передаёт reason субагенту как его следующую инструкцию. Хук, блокирующий с кодом выхода 2, передаёт своё сообщение из stderr тем же способом. Чтобы внедрить контекст в родительскую сессию после возврата субагента, используйте вместо этого хук PostToolUse на инструменте Agent.
TaskCreated
Запускается, когда задача создаётся с помощью инструментаTaskCreate. Используйте это для соблюдения соглашений об именовании, требования описаний задач или предотвращения создания определённых задач. В сессии без инструментов Task это событие не срабатывает.
Хуки TaskCreated не поддерживают matcher и срабатывают при каждом возникновении события.
Входные данные TaskCreated
Помимо общих полей входных данных, хуки TaskCreated получаютtask_id, task_subject и, при наличии, task_description, teammate_name и team_name.
Управление решениями TaskCreated
Хук TaskCreated может заблокировать создание с помощью кода выхода 2 или решения в JSON. В любом случае Claude Code удаляет задачу и возвращает ваше сообщение Claude как ошибку инструмента. Claude Code игнорируетcontinue: false от этого события, и Claude продолжает работу.
- Код выхода 2: Claude Code возвращает текст из stderr в качестве сообщения.
- JSON
{"decision": "block", "reason": "..."}: Claude Code возвращаетreasonв качестве сообщения.
TaskCompleted
Запускается, когда задача помечается как завершённая. Это происходит в двух ситуациях: когда любой агент явно помечает задачу как завершённую через инструмент TaskUpdate или когда участник команды агентов заканчивает свой ход с задачами в процессе выполнения. Используйте это для соблюдения критериев завершения, таких как прохождение тестов или проверок линтера, прежде чем задачу можно будет закрыть. Хуки TaskCompleted не поддерживают matcher и срабатывают при каждом возникновении события.Входные данные TaskCompleted
Помимо общих полей входных данных, хуки TaskCompleted получаютtask_id, task_subject и, при наличии, task_description, teammate_name и team_name.
Управление решениями TaskCompleted
Хуки TaskCompleted поддерживают два способа управления завершением задачи:- Код выхода 2: задача не помечается как завершённая, а сообщение из stderr передаётся модели в качестве обратной связи.
- JSON
{"continue": false, "stopReason": "..."}: когда событие вызвано завершением хода участника команды, полностью останавливает участника команды, аналогично поведению хукаStop.stopReasonпоказывается пользователю. Когда событие вызвано инструментомTaskUpdate, Claude Code игнорируетcontinue: false; код выхода 2 по-прежнему блокирует завершение.
Stop
Запускается, когда основной агент Claude Code закончил отвечать. Не запускается, если остановка произошла из-за прерывания пользователем. Ошибки API вместо этого вызывают StopFailure.Входные данные Stop
Помимо общих полей входных данных, хуки Stop получаютstop_hook_active, last_assistant_message, background_tasks и session_crons. Поле stop_hook_active равно true, когда Claude Code уже продолжает работу в результате хука остановки. Проверяйте это значение или обрабатывайте транскрипт, чтобы избежать блокировки по условию, которое никогда не выполнится.
Claude Code применяет ограничение в 8 последовательных продолжений: после того как хуки остановки продолжили ход восемь раз подряд, Claude Code переопределяет следующую блокировку и завершает ход. Счётчик последовательных продолжений сбрасывается каждый раз, когда Claude вызывает инструмент. Чтобы повысить ограничение, установите CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Поле last_assistant_message содержит текстовое содержимое финального ответа Claude, поэтому хуки могут получить к нему доступ без разбора файла транскрипта. Для хуков, которые работают с только что завершённым ходом, например хуков чтения вслух или уведомлений, используйте это поле, а не чтение transcript_path: не во всех версиях гарантируется, что файл транскрипта содержит финальное сообщение в момент Stop.
Массивы background_tasks и session_crons позволяют хукам отличать «сессия завершена» от «сессия приостановлена в ожидании фоновой работы, которая её разбудит». Оба массива присутствуют, когда реестр задач доступен, и пусты, когда ничего не выполняется и не запланировано.
Каждая запись в background_tasks описывает одну выполняющуюся задачу и использует следующие поля:
Каждая запись в
session_crons описывает одно запланированное пробуждение в рамках сессии, созданное через CronCreate, ScheduleWakeup и /loop:
Этот пример показывает входные данные Stop с одной выполняющейся задачей оболочки и одним повторяющимся cron:
Управление решениями Stop
ХукиStop и SubagentStop могут управлять тем, продолжит ли Claude работу. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может вернуть следующие поля, специфичные для события:
Хук, блокирующий с кодом выхода 2, обрабатывается так же, как
reason: Claude получает сообщение из stderr в качестве объяснения, почему он должен продолжить.
additionalContext, когда хук работает так, как задумано, и даёт Claude указания, например «запусти набор тестов перед завершением». Он продолжает диалог с теми же защитами от зацикливания, что и decision: "block", а именно входным полем stop_hook_active и ограничением в 8 последовательных продолжений, но транскрипт помечает его как Stop hook feedback, и уведомление об ошибке хука не показывается:
StopFailure
Запускается вместо Stop, когда ход завершается из-за ошибки API. Claude Code игнорирует вывод и код выхода хука, за исключениемterminalSequence. Используйте это для логирования сбоев, отправки оповещений или выполнения действий по восстановлению, когда Claude не может завершить ответ из-за ограничений частоты запросов, проблем с аутентификацией или других ошибок API.
Входные данные StopFailure
Помимо общих полей входных данных, хуки StopFailure получаютerror, необязательное error_details и необязательное last_assistant_message. Поле error определяет тип ошибки и используется для фильтрации через matcher.
TeammateIdle
Запускается, когда участник команды агентов собирается перейти в режим простоя после завершения своего хода. Используйте это для применения контрольных проверок качества до того, как участник команды прекратит работу, например для требования прохождения проверок линтера или проверки наличия выходных файлов. Хуки TeammateIdle не поддерживают matcher и срабатывают при каждом возникновении события.Входные данные TeammateIdle
Помимо общих полей входных данных, хуки TeammateIdle получаютteammate_name и team_name.
Управление решениями TeammateIdle
Хуки TeammateIdle поддерживают два способа управления поведением участника команды:- Код выхода 2: участник команды получает сообщение из stderr в качестве обратной связи и продолжает работу вместо перехода в режим простоя.
- JSON
{"continue": false, "stopReason": "..."}: полностью останавливает участника команды, аналогично поведению хукаStop.stopReasonпоказывается пользователю.
ConfigChange
Запускается, когда файл конфигурации изменяется во время сессии. Используйте это для аудита изменений настроек, применения политик безопасности или блокировки несанкционированных изменений файлов конфигурации. Claude Code запускает хуки ConfigChange, когда изменяется файл настроек, файл управляемой политики или файл скилла. Для управляемой политики он запускает их, только когда изменяетсяmanaged-settings.json или файл в managed-settings.d/.
Matcher фильтрует по источнику конфигурации:
Изменения, подобные перечисленным ниже, вступают в силу без запуска хуков:
- Правила и настройки, которые вы сохраняете через Yes, and don’t ask again,
/permissionsили/model, в той сессии, из которой вы их сохранили. Если другая открытая сессия загружает тот же файл, хуки запускаются в ней. - Обновления настроек, управляемых сервером, управляемых настроек macOS или политик реестра Windows
- В WSL с
wslInheritsWindowsSettings— изменённый файл управляемых настроек на стороне Windows, который Claude Code подхватывает при опросе политик
Входные данные ConfigChange
Помимо общих полей входных данных, хуки ConfigChange получаютsource и, при наличии, file_path. Поле source указывает, какой тип конфигурации изменился, а file_path содержит путь к конкретному изменённому файлу.
Управление решениями ConfigChange
Хуки ConfigChange могут блокировать вступление изменений конфигурации в силу. Используйте код выхода 2 или JSON-полеdecision, чтобы предотвратить изменение. При блокировке новые настройки не применяются к работающей сессии.
policy_settings нельзя заблокировать. Хуки по-прежнему срабатывают для источников policy_settings, когда изменяется файл управляемых настроек на машине, поэтому вы можете использовать их для записи этих правок в лог, но любое решение о блокировке игнорируется. Это гарантирует, что корпоративные управляемые настройки всегда вступают в силу. Claude Code не запускает хуки ConfigChange, когда настройки, управляемые сервером, поступают или обновляются.
Claude Code выполняет решение о блокировке из JSON-вывода хука ConfigChange и отбрасывает systemMessage и continue. Заблокированное изменение не выводит никакого сообщения ни вам, ни Claude, независимо от того, блокируете ли вы его с помощью reason или через stderr с кодом выхода 2. Claude Code лишь записывает строку в отладочный лог.
CwdChanged
Запускается, когда shell-команда в основном диалоге изменяет рабочий каталог, например когда Claude выполняет командуcd. Используйте это для реакции на смену каталога: перезагрузки переменных окружения, активации специфичных для проекта наборов инструментов или автоматического запуска скриптов настройки. Работает в паре с FileChanged для таких инструментов, как direnv, которые управляют окружением для каждого каталога.
Хуки CwdChanged имеют доступ к CLAUDE_ENV_FILE. Если ваш хук записывает переменные в этот файл, они сохраняются в последующих командах Bash до следующего события CwdChanged, когда Claude Code их очищает.
CwdChanged не поддерживает matcher и срабатывает при каждом возникновении события.
Входные данные CwdChanged
Помимо общих полей входных данных, хуки CwdChanged получаютold_cwd и new_cwd.
Вывод CwdChanged
Помимо полей вывода JSON, доступных всем хукам, хуки CwdChanged могут вернутьwatchPaths, чтобы динамически задать, за какими путями к файлам следит FileChanged:
У хуков CwdChanged нет управления решениями. Они не могут заблокировать смену каталога.
Claude Code читает
watchPaths и systemMessage из их JSON-вывода и отбрасывает continue. В интерактивных сессиях он показывает systemMessage как короткое уведомление в терминале. Сообщение не попадает в поток сообщений SDK.
DirectoryAdded
Запускается после того, как вы добавляете рабочий каталог посреди сессии с помощью команды/add-dir, или после того, как SDK-клиент добавляет его с помощью управляющего запроса register_repo_root. Используйте это для подготовки только что добавленного репозитория, например для установки его зависимостей.
Claude Code не вызывает это событие, когда:
- Вы передаёте каталог с флагом запуска
--add-dir; эти каталоги охватывает SessionStart - Вы добавляете каталог на вкладке Workspace в
/permissions - Вы добавляете каталог, который уже является рабочим каталогом или находится внутри него
Входные данные DirectoryAdded
Помимо общих полей входных данных, хуки DirectoryAdded получаютdirectory и source.
continue из их JSON-вывода, а остальное обрабатывает по-разному в зависимости от источника:
slash_command: Claude Code передаётsystemMessageхука Claude как контекст на следующем ходе диалога, а не показывает его вам. В транскрипте появляется количество хуков, завершившихся с ошибкой. Полный вывод ошибок записывается в отладочный логregister_repo_root: Claude Code записывает выводsystemMessageи вывод ошибок только в отладочный лог
FileChanged
Запускается, когда отслеживаемый файл изменяется на диске. Claude Code обнаруживает изменения с помощью наблюдателя файловой системы, а не путём анализа вызовов инструментов, поэтому запускает хук независимо от того, что изменило файл: вызов инструментаEdit или Write, скрипт, который Claude запускает через Bash, или процесс полностью вне Claude Code. Типичный сценарий использования — перезагрузка переменных окружения при изменении файлов конфигурации проекта.
matcher для этого события выполняет две роли:
- Формирование списка отслеживания: значение разбивается по
|, и каждый сегмент регистрируется как буквальное имя файла в рабочем каталоге, поэтому".envrc|.env"отслеживает ровно эти два файла. Регулярные выражения здесь бесполезны: значение вроде^\.envбудет отслеживать файл с буквальным именем^\.env. - Фильтрация запускаемых хуков: когда отслеживаемый файл изменяется, то же значение фильтрует, какие группы хуков запускаются, по стандартным правилам matcher применительно к базовому имени изменённого файла.
data.csv после любого изменения, включая перезапись файла командой Bash или внешним скриптом:
file_path входных данных JSON на stdin. Его проверка grep ищет то же, что удаляет perl, — символ CR в конце строки, поэтому запуск после нормализации завершается, не затрагивая файл. Более мягкая проверка приводит к бесконечному циклу, потому что perl -i перезаписывает файл, даже если ничего не заменяет, а Claude Code запускает хук снова после каждой перезаписи. Сохраните этот скрипт по пути /path/to/normalize-line-endings.sh и сделайте его исполняемым:
data.csv командой Bash. Claude Code запустит хук, и в файле останутся окончания LF.
Чтобы отслеживать файлы, имена которых нельзя указать заранее, возвращайте watchPaths из хука для динамического обновления списка отслеживания. Claude Code запускает наблюдатель только тогда, когда что-то указывает файл для отслеживания, поэтому заполните список группой FileChanged, matcher которой называет хотя бы один файл, или хуком SessionStart или CwdChanged, возвращающим watchPaths. Matcher по-прежнему фильтрует, какие группы хуков запускаются при изменении отслеживаемого файла, поэтому для группы, обрабатывающей динамические пути, не указывайте matcher: тогда он соответствует каждому отслеживаемому файлу и ничего не добавляет в список отслеживания. Matcher "*" тоже соответствует каждому файлу, но Claude Code регистрирует его в списке отслеживания как любое другое значение — как файл с буквальным именем *.
Хуки FileChanged имеют доступ к CLAUDE_ENV_FILE. Переменные, записанные в этот файл, сохраняются для последующих команд Bash до следующего события CwdChanged, когда Claude Code их очищает.
Входные данные FileChanged
Помимо общих входных полей, хуки FileChanged получаютfile_path и event.
Выходные данные FileChanged
Помимо полей вывода JSON, доступных всем хукам, хуки FileChanged могут возвращатьwatchPaths для динамического обновления отслеживаемых путей файлов:
Хуки FileChanged не управляют решениями. Они не могут предотвратить изменение файла.
Claude Code считывает
watchPaths и systemMessage из их вывода JSON и отбрасывает continue. В интерактивных сессиях systemMessage показывается как краткое уведомление в терминале. Сообщение не попадает в поток сообщений SDK.
WorktreeCreate
Запускается, когда Claude Code создаёт worktree, например в таких случаях:- Вы запускаете сессию командой
claude --worktree - Claude создаёт worktree во время сессии и переключается в него с помощью инструмента
EnterWorktree - Субагент использует
isolation: "worktree" - Рабочий процесс запускает агента в собственном worktree
- Claude Code изолирует фоновую сессию в собственном worktree
claude remote-controlв режимеworktreeзапускает сессию в собственном worktree
--worktree и --tmux:
git worktree addчерез Bash: чтобы проверить или заблокировать эту команду, добавьте хукPreToolUseс matcherBash.--worktreeс--tmux: Claude Code создаёт worktree до загрузки плагинов, поэтому хук WorktreeCreate из плагина не запускается. Чтобы ваш хук запускался с обоими флагами, определите его в файле настроек. До v2.1.300 хук WorktreeCreate плагина не запускался и с одним только--worktree.
git worktree. Настройка хука WorktreeCreate заменяет это стандартное поведение git, позволяя использовать другую систему контроля версий, например SVN, Perforce или Mercurial.
Поскольку хук полностью заменяет стандартное поведение, .worktreeinclude не обрабатывается. Если нужно скопировать локальные файлы конфигурации, такие как .env, в новый worktree, делайте это в скрипте хука.
Хук должен вернуть путь к созданному каталогу worktree. Claude Code использует этот путь как рабочий каталог для изолированной сессии. О том, как каждый тип хука возвращает путь, см. Выходные данные WorktreeCreate.
Claude Code учитывает успешность хука и возвращённый путь и отбрасывает systemMessage и continue.
Этот пример создаёт рабочую копию SVN и выводит путь для использования в Claude Code. Замените URL репозитория своим:
name worktree из входных данных JSON на stdin, извлекает свежую копию в новый каталог и выводит путь к каталогу. echo в последней строке — это то, что Claude Code считывает как путь к worktree. Перенаправляйте любой другой вывод в stderr, чтобы он не мешал пути.
Входные данные WorktreeCreate
Помимо общих входных полей, хуки WorktreeCreate получают полеname. Это slug-идентификатор нового worktree, заданный пользователем или сгенерированный автоматически, например bold-oak-a3f2.
Выходные данные WorktreeCreate
Хуки WorktreeCreate не используют стандартную модель решений разрешения/блокировки. Вместо этого результат определяется успехом или неудачей хука. Хук должен вернуть путь к созданному каталогу worktree:- Хуки команд (
type: "command"): выведите путь последней непустой строкой stdout. Claude Code удаляет escape-коды ANSI перед чтением этой строки, поэтому баннеры запуска оболочки, выведенные до вашегоecho, игнорируются. Перенаправляйте любой другой вывод хука в stderr. - HTTP-хуки (
type: "http"): верните{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }в теле ответа.
. или ... Если полученный путь не является каталогом, в который Claude Code может перейти, сессия выводит ошибку с указанием пути и завершается с кодом 1.
Claude Code отклоняет абсолютный путь, содержащий сегменты . или .., а также любой путь, проходящий через символическую ссылку ниже корня репозитория. В ошибке указывается отклонённый компонент. Символическая ссылка, закоммиченная в репозиторий, могла бы перенаправить worktree за его пределы. Возвращайте нормализованный путь, не проходящий через символическую ссылку внутри репозитория.
WorktreeRemove
Запускается, когда Claude Code очищает worktree, созданный вашим хукомWorktreeCreate. Событие срабатывает, когда:
- Вы выходите из интерактивной сессии worktree и выбираете удаление worktree в ответ на запрос Claude Code
- Вы выходите из интерактивной сессии worktree, которой не дали имя, Claude Code не находит изменённых или неотслеживаемых файлов и удаляет worktree без запроса
- Вы удаляете фоновую сессию, работающую в worktree
- Вы просите Claude выйти и удалить worktree во время сессии, и Claude удаляет его инструментом
ExitWorktree - Сессия, которую
claude remote-controlзапустил в режимеworktree, завершается без сбоя, и Claude Code не находит в её worktree изменённых или неотслеживаемых файлов и новых коммитов - Вы останавливаете
claude remote-control, пока запущенные им в режимеworktreeсессии ещё работают, и Claude Code не находит в их worktree изменённых или неотслеживаемых файлов и новых коммитов
ExitWorktree или очистки claude remote-control. ExitWorktree отказывается удалять созданный хуком worktree, если вызов не передаёт discard_changes: true, что делает ваш хук последней проверкой на этом пути.
Claude Code использует git для поиска изменённых или неотслеживаемых файлов, поэтому не находит их в worktree, который не является git-checkout и не находится внутри него, даже если в каталоге есть незакоммиченная работа. Проверяйте наличие такой работы в хуке WorktreeRemove, прежде чем он что-либо удалит.
Для worktree на основе git Claude Code автоматически выполняет очистку с помощью git worktree remove. Если вы настроили хук WorktreeCreate, дополните его хуком WorktreeRemove, чтобы управлять очисткой:
- Нет хука WorktreeRemove: Claude Code использует git как резервный вариант при удалении на выходе или через
ExitWorktreeи сохраняет worktree при очисткеclaude remote-control:- Вы выходите из сессии worktree или Claude вызывает
ExitWorktree: Claude Code удаляет путь, возвращённый хуком WorktreeCreate, так же, как это сделал быgit worktree remove --force, поэтому worktree, распознаваемый git, удаляется. Worktree, который git не распознаёт, например созданный хуком с системой контроля версий, отличной от git, остаётся на диске. claude remote-controlочищает worktree сессии: worktree остаётся на диске, а терминал, в котором работаетclaude remote-control, показываетworktree removal failed, kept: <path>.- Вы удаляете фоновую сессию: см. Что удаляется при удалении сессии.
- Вы выходите из сессии worktree или Claude вызывает
- Хук завершается с кодом 0: worktree считается удалённым. Claude Code больше ничего не считывает из хука, поэтому убедитесь, что хук удалил каталог.
- Хук завершается с ненулевым кодом: удаление завершается неудачей, если каталог по пути
worktree_pathпосле этого всё ещё существует, и worktree остаётся на диске без резервного варианта через git. Хук, удаливший каталог перед завершением с ненулевым кодом, считается выполнившим удаление. О том, как сообщается о неудаче, см. Входные данные WorktreeRemove.
systemMessage и continue.
При удалении фоновой сессии Claude Code проверяет сохранённый путь к worktree перед запуском хука и отклоняет путь, который является символической ссылкой или проходит через неё ниже корня репозитория. На этом пути хук запускается для worktree, в котором ещё есть файлы, только если вы подтверждаете удаление в представлении агентов. Для такого worktree claude rm вместо этого сохраняет сессию и worktree.
Claude Code передаёт путь, возвращённый WorktreeCreate, как worktree_path во входных данных хука. Этот пример считывает этот путь и удаляет каталог:
Входные данные WorktreeRemove
Помимо общих входных полей, хуки WorktreeRemove получают полеworktree_path — абсолютный путь к удаляемому worktree.
worktree_path после этого всё ещё существует, удаление завершается неудачей:
- Worktree остаётся на диске, а команда хука и его stderr попадают в отладочный лог.
- Если вы удаляли фоновую сессию, сессия тоже сохраняется. Сообщение об отказе в представлении агентов сообщает, как завершился хук, например
exited 1, цитирует начало его stderr и указывает, удалит ли повторное удаление сессии каталог в любом случае. - Если
claude remote-controlочищал worktree сессии, терминал, в котором он работает, показываетworktree removal failed, kept: <path>.
PreCompact
Запускается перед тем, как Claude Code выполнит операцию сжатия контекста. Значение matcher указывает, было ли сжатие запущено вручную или автоматически:
Завершитесь с кодом 2, чтобы заблокировать сжатие. Для ручного
/compact сообщение из stderr показывается пользователю. Также можно заблокировать сжатие, вернув JSON с "decision": "block".
Блокировка автоматического сжатия имеет разные последствия в зависимости от момента срабатывания. Если сжатие было запущено заблаговременно, до достижения лимита контекста, Claude Code пропускает его, и диалог продолжается без сжатия. Если сжатие было запущено для восстановления после ошибки лимита контекста, уже возвращённой API, исходная ошибка выводится, и текущий запрос завершается неудачей.
Claude Code отбрасывает поля systemMessage и continue хука PreCompact.
Входные данные PreCompact
Помимо общих входных полей, хуки PreCompact получаютtrigger и custom_instructions. Для manual поле custom_instructions содержит то, что пользователь передаёт в /compact, и равно null, если ничего не передано. Для auto поле custom_instructions равно null.
Выходные данные PreCompact
Когда хук PreCompact завершается с кодом 0 и не блокирует сжатие, Claude Code использует его stdout как инструкции для сводки сжатия. Ограничивайте этот stdout 10 000 символов на хук. Более длинный вывод Claude Code обрезает на этом пределе и добавляет маркер.PostCompact
Запускается после того, как Claude Code завершает операцию сжатия контекста. Используйте это событие, чтобы реагировать на новое сжатое состояние, например чтобы записать в лог сгенерированную сводку или обновить внешнее состояние. Claude Code отбрасывает поляsystemMessage и continue хука PostCompact.
Применяются те же значения matcher, что и для PreCompact:
Входные данные PostCompact
Помимо общих входных полей, хуки PostCompact получаютtrigger и compact_summary. Поле compact_summary содержит сводку диалога, сгенерированную операцией сжатия.
PreModelSwitch
Запускается перед тем, как Claude Code применит переключение модели, запрошенное вами или клиентом. Используйте его, чтобы заблокировать переключение, потребовать подтверждения или показать, во что обойдётся переключение, до того как оно произойдёт. PreModelSwitch требует Claude Code v2.1.251 или новее. Claude Code запускает его для следующих запросов:/model <name>и окно выбора/model- Окно выбора модели
Option+PилиAlt+P - Настройка Model в
/config - Включение быстрого режима, если это меняет модель сессии
- Запрос
set_modelили изменение модели в запросеapply_flag_settingsот хоста Agent SDK или Remote Control
[1m]. Псевдоним, например opus, идентификатор модели с датой и идентификатор конкретного провайдера, например идентификатор модели Amazon Bedrock, соответствуют одному каноническому имени, в которое они разрешаются, поэтому claude-opus-5 охватывает все варианты написания Opus 5.
Если Claude Code не может определить каноническое имя целевой модели, например для пользовательского идентификатора модели, известного только вашему LLM-шлюзу, он запускает каждый хук PreModelSwitch независимо от matcher. Поэтому блокирующий хук должен проверять to_model из входных данных, а не полагаться только на matcher.
Записывайте matcher как точное имя, список через |, например claude-opus-4-6|claude-opus-5, или регулярное выражение, например .*opus.*. Этот пример использует matcher с точным именем и дополнительно проверяет to_model из входных данных хука, поэтому он отклоняет переключение на Opus 4.6, завершаясь с кодом 2, и пропускает любую другую целевую модель:
- macOS/Linux
- Windows (PowerShell)
Команда проверяет
to_model с помощью jq:/model claude-opus-4-6 в сессии с другой моделью. Claude Code сохранит текущую модель и сообщит, что хук PreModelSwitch заблокировал переключение, указав ваше сообщение в качестве причины.
Входные данные PreModelSwitch
Помимо общих входных полей, хуки PreModelSwitch получают поля из этой таблицы. Последние пять описывают стоимость повторной отправки диалога новой модели, чтобы хук мог показать эту цифру до переключения.
Этот пример показывает входные данные для
/model opus в сессии с Sonnet 5:
Управление решениями PreModelSwitch
ХукиPreModelSwitch могут отменить переключение, попросить пользователя подтвердить его или разрешить его выполнение. Код выхода 2 или decision: "block" на верхнем уровне отменяет переключение.
Для более тонкого управления возвращайте permissionDecision и permissionDecisionReason в объекте hookSpecificOutput, как в PreToolUse. PreModelSwitch принимает "allow", "deny" и "ask". Он не принимает "defer", updatedInput или additionalContext. В таблице ниже описаны оба поля:
Только
/model в интерактивной сессии может показать запрос "ask". Во всех остальных интерфейсах, включая неинтерактивный режим с флагом -p, /config и запросы set_model, Claude Code рассматривает "ask" как отказ.
Этот пример просит пользователя подтвердить переключение и приводит количество токенов из context_tokens:
deny > ask > allow.
Claude Code показывает пользователю любое systemMessage, возвращённое хуком, независимо от решения, поэтому хук для отчёта о стоимости может вернуть {"systemMessage": "..."} и завершиться с кодом 0.
Таймауты PreModelSwitch и другие коды выхода
Хук PreModelSwitch, не ответивший до истечения таймаута, блокирует переключение. Таймаут по умолчанию для этого события — 30 секунд. Код выхода, отличный от 0 или 2, без корректного вывода JSON является неблокирующей ошибкой, поэтому переключение выполняется, за исключением хуков, сбой которых его блокирует.PostModelSwitch
Запускается после изменения модели сессии. Используйте его, чтобы дать Claude указания для конкретной модели без редактирования каждого CLAUDE.md, например инструкцию уровня организации, применимую к определённым моделям. PostModelSwitch требует Claude Code v2.1.251 или новее. Он не может блокировать, потому что модель уже изменилась. Claude Code запускает хуки PostModelSwitch после любого из следующих изменений:- Переключение, запрошенное вами или клиентом
- Автоматическое переключение на резервную модель, которое меняет модель сессии
- Вход в режим планирования или выход из него при настройке, такой как
opusplan - Восстановление модели Claude Code при возобновлении сессии
/model opus в сессии Sonnet, а затем спросите Claude, какие у него есть указания относительно текущей модели.
Входные данные PostModelSwitch
Хуки PostModelSwitch получают те же поля, что и PreModelSwitch, при этомhook_event_name равно "PostModelSwitch", и есть ещё два значения source: "auto" для автоматического переключения на резервную модель или другого изменения, сделанного Claude Code самостоятельно, и "resume" для модели, восстановленной при возобновлении сессии.
requested_model равно null, когда source равно "auto". Когда source равно "resume", это сохранённая настройка модели, которую восстановил Claude Code.
Управление решениями PostModelSwitch
Claude Code берёт простой текст stdout вашего хука при коде выхода 0 илиadditionalContext из вывода JSON и передаёт его Claude со следующим запросом после переключения. Помимо полей вывода JSON, доступных всем хукам, можно вернуть:
Если хук не завершился в течение пяти секунд после отправки следующего промпта, Claude Code отправляет этот запрос без вывода и прикрепляет его к следующему за ним запросу. Если модель меняется несколько раз до следующего запроса, Claude Code передаёт только вывод для целевой модели последнего переключения.
SessionEnd
Запускается при завершении сессии Claude Code. Полезен для задач очистки, записи статистики сессии в лог или сохранения состояния сессии. Поддерживает matcher для фильтрации по причине выхода. Полеreason во входных данных хука указывает, почему сессия завершилась:
Входные данные SessionEnd
Помимо общих входных полей, хуки SessionEnd получают полеreason, указывающее, почему сессия завершилась. Все значения см. в таблице причин выше.
systemMessage.
Таймаут по умолчанию для хуков SessionEnd — 1,5 секунды. Он действует, когда вы выходите, выполняете /clear или переключаете сессии через интерактивную /resume. Дать хуку больше времени можно двумя способами:
timeoutдля отдельного хука: задайтеtimeoutв конфигурации этого хука. Общий лимит времени автоматически увеличивается до наибольшего значенияtimeoutотдельного хука в ваших файлах настроек, но не более 60 секунд. Если вы увеличиваете лимит таким образом, хук без собственногоtimeoutпо-прежнему сохраняет значение по умолчанию. Таймауты, заданные для хуков из плагинов, не увеличивают лимит.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: задайте эту переменную окружения в миллисекундах, чтобы явно переопределить лимит. Заданное значение также становится таймаутом для каждого хука без собственногоtimeout.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS увеличивала только общий лимит, а хук без собственного timeout всё равно отменялся через 1,5 секунды.
Elicitation
Запускается, когда MCP-сервер запрашивает ввод пользователя посреди задачи. По умолчанию Claude Code показывает интерактивное диалоговое окно для ответа пользователя. Хуки могут перехватить этот запрос и ответить программно, полностью пропуская диалоговое окно. Полный хук с записью в настройках и скриптом см. в разделе Ответ на запрос формы из скрипта. Поле matcher сопоставляется с именем MCP-сервера.Входные данные Elicitation
Помимо общих входных полей, хуки Elicitation получаютmcp_server_name, message и необязательные поля mode, url, elicitation_id и requested_schema.
Для elicitation в режиме формы, наиболее распространённого случая:
Выходные данные Elicitation
Хук Elicitation может ответить на запрос вместо пользователя, отклонить или отменить его либо оставить его диалоговому окну. Чтобы ответить, отклонить или отменить, завершитесь с кодом 0 и выведите объектhookSpecificOutput с action. Сервер получит ваш ответ, и диалоговое окно не появится. Каждая строка этой таблицы показывает, что вернуть для одного исхода и что получит MCP-сервер:
Этот вывод отвечает на запрос в режиме формы, показанный в разделе Входные данные Elicitation. Ключи в
content — это имена свойств из requested_schema этого запроса:
decline, а нажатие Esc отправляет cancel, поэтому возвращайте тот вариант, который должен увидеть сервер.
Для запроса в режиме URL хук, возвращающий accept, пропускает диалоговое окно, поэтому URL так и не открывается.
Claude Code отбрасывает reason, systemMessage и continue из вывода JSON хука Elicitation, какой бы action вы ни вернули.
Другие способы отклонить elicitation
Хук также может отклонить запрос следующими способами. Сервер получит тот жеdecline, что и для "action": "decline":
- Завершение с кодом 2: Claude Code игнорирует
hookSpecificOutput, выведенный тем же хуком - Вывод
"decision": "block"на верхнем уровне: блокировка переопределяетactionв том же выводе
accept или cancel от другого.
Этот скрипт отклоняет запросы в режиме URL и оставляет запросы формы диалоговому окну:
reason.
Claude Code игнорировал decision верхнего уровня от хуков Elicitation и ElicitationResult, начиная с v2.1.105 и до исправления в v2.1.284.
Ответ на запрос формы из скрипта
Этот пример отвечает на один повторяющийся вопрос вместо пользователя. MCP-сервер с именемissue-tracker запрашивает ключ проекта в форме, и хук подставляет DOCS. Скрипт принимает запрос, когда project_key — единственное поле формы. Для любого другого запроса он ничего не выводит, поэтому появляется диалоговое окно.
- macOS/Linux
- Windows (PowerShell)
Зарегистрируйте хук команды для этого события в файле настроек, указав имя сервера в качестве matcher:Сохраните этот скрипт в
.claude/hooks/answer-project-key.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x:claude --debug и дайте Claude задачу, при которой сервер запросит ключ проекта. Диалоговое окно не появится, а в отладочном логе будет строка, заканчивающаяся на Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}.
ElicitationResult
Запускается после того, как пользователь отвечает на elicitation MCP. Хуки могут просматривать, изменять или блокировать ответ до его отправки обратно MCP-серверу. Когда хук Elicitation отвечает на запрос, Claude Code отправляет этот ответ серверу, не запуская хуки ElicitationResult. Поле matcher сопоставляется с именем MCP-сервера.Входные данные ElicitationResult
Помимо общих входных полей, хуки ElicitationResult получаютmcp_server_name, action и необязательные поля mode, elicitation_id и content.
Выходные данные ElicitationResult
Хук ElicitationResult может пропустить ответ пользователя, изменить его значения или заблокировать его. Чтобы изменить или заблокировать ответ, завершитесь с кодом 0 и выведите объектhookSpecificOutput с action. Каждая строка этой таблицы показывает, что вернуть для одного исхода и что получит MCP-сервер:
Этот вывод изменяет ответ, показанный в разделе Входные данные ElicitationResult, так что сервер получает
alice@example.com вместо отправленного пользователем alice:
content заменяет весь объект content пользователя, поэтому включайте и поля, которые не изменяете. Возвращайте вместе с ним action, потому что Claude Code игнорирует hookSpecificOutput без action.
Хуки ElicitationResult также запускаются, когда пользователь отклоняет или отменяет запрос, и ваш action заменяет его действие. Прежде чем возвращать accept, проверяйте, что action во входных данных равен accept, иначе хук превратит отклонённый запрос в принятый. Этот скрипт вносит то же изменение, если пользователь принял запрос, сохраняет остальные поля и в противном случае ничего не выводит:
"decision": "block" на верхнем уровне также блокируют ответ. В разделе Другие способы отклонить elicitation описано, какой из вариантов действует, когда хук их сочетает, что видит пользователь и какие версии игнорировали decision.
Claude Code отбрасывает reason, systemMessage и continue из вывода JSON хука ElicitationResult, какой бы action вы ни вернули.
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):
PermissionDeniedPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
PermissionRequest поддерживает command, http, mcp_tool и prompt hooks, но не agent hooks. Если вы настроите agent hook на этом событии, Claude Code пропустит его и поток разрешений продолжится без изменений. Чтобы разрешить или отклонить из hook, верните объект решения из command или HTTP hook.
События, которые поддерживают command, http и mcp_tool hooks, но не prompt или agent:
ConfigChangeCwdChangedDirectoryAddedElicitationElicitationResultFileChangedInstructionsLoadedMessageDisplayNotificationPostCompactPostModelSwitchPreCompactPreModelSwitchSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart и Setup поддерживают command и mcp_tool hooks, и MCP tool hook fields описывает, когда их mcp_tool hooks запускаются. Они не поддерживают http, prompt или agent hooks.
How prompt-based hooks work
Вместо выполнения команды Bash, prompt-based hooks:- Отправляют входные данные хука и ваш промпт модели Claude, по умолчанию той, которую Claude Code использует для фоновой функциональности
- LLM отвечает структурированным JSON, содержащим решение
- Claude Code автоматически обрабатывает решение
Prompt hook configuration
Установитеtype на "prompt" и предоставьте строку prompt вместо command. Используйте заполнитель $ARGUMENTS для внедрения данных JSON входа hook в текст вашей подсказки.
В prompt или agent хуке вы можете написать prompt как правило о том, что блокировать или разрешать, например «Блокировать любую команду Bash, которая читает файлы .env», или как условие, которое должно выполняться, например «Все модульные тесты проходят».
Этот hook Stop просит LLM оценить, должен ли Claude остановиться перед разрешением Claude закончить:
Response schema
LLM должен ответить JSON, содержащим:
Что происходит при
ok: false, зависит от события:
StopиSubagentStop: причина передаётся обратно Claude как его следующая инструкция и ход продолжается, если только ответ также не устанавливаетimpossible: true, в этом случае Claude Code позволяет остановке и ход заканчиваетсяPreToolUse: вызов инструмента отклоняется; по умолчанию ход заканчивается и причина отказа появляется в чате как строка предупреждения. УстановитеcontinueOnBlock: trueдля возврата причины Claude как ошибки инструмента, чтобы он мог скорректировать и продолжить, эквивалентноpermissionDecision: "deny"из command hookPostToolUse: по умолчанию ход заканчивается и причина появляется в чате как строка предупреждения. УстановитеcontinueOnBlock: trueдля передачи причины обратно Claude и продолжения хода вместо этогоPostToolBatch,UserPromptSubmitиUserPromptExpansion: ход заканчивается и причина появляется как строка предупреждения. Эти события заканчивают ход наdecision: "block"независимо отcontinuePostToolUseFailureиTaskCreated: причина возвращается Claude как ошибка инструмента и ход продолжается, независимо отcontinueOnBlockTaskCompleted: когда он срабатывает, потому что задача отмечена как завершённая во время хода, причина возвращается Claude как ошибка инструмента и ход продолжается, независимо отcontinueOnBlock. Когда он срабатывает, потому что товарищ по команде останавливается, он ведёт себя какTeammateIdleи останавливает товарища по команде по умолчаниюTeammateIdle: по умолчанию товарищ по команде останавливается и причина появляется как строка предупреждения. Установите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, за исключением PermissionRequest.
How agent hooks work
Когда срабатывает agent hook:- Claude Code порождает subagent с вашей подсказкой и JSON входом hook
- Subagent может использовать инструменты, такие как Read, Grep и Glob, для исследования
- После до 50 оборотов subagent возвращает структурированное решение
{ "ok": true/false } - Claude Code разрешает действие, если
okимеет значениеtrue. Еслиokимеет значениеfalse, Claude Code обрабатывает блокировку так же, как prompt hook сcontinueOnBlock: trueна этом событии, как указано в разделе Response schema
Agent hook configuration
Установитеtype на "agent" и предоставьте строку prompt, используя $ARGUMENTS как заполнитель для JSON входа hook. Поля конфигурации те же, что и prompt hooks, за исключением того, что agent hooks имеют более длинный таймаут по умолчанию в 60 секунд и не имеют поля continueOnBlock.
Схема ответа — это { "ok": true } для разрешения или { "ok": false, "reason": "..." } для блокировки. При ok: false, Claude Code обрабатывает agent hook так же, как он обрабатывает prompt hook с continueOnBlock: true на том же событии; agent hooks не имеют поля continueOnBlock и не поддерживают поле impossible из prompt hook.
Этот 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 выполняется. Когда скрипт завершается, его выход доставляется на следующий ход разговора:
timeout к нему. Claude Code по-прежнему применяет timeout к hook, который вы запускаете с asyncRewake.
Claude Code доставляет результаты асинхронного hook только во время работы сеанса:
- В неинтерактивном режиме с флагом
-pClaude Code завершает любой асинхронный hook, который всё ещё работает при завершении, и завершает его с результатомcancelled - Если работа вашего hook должна пережить сеанс
claude -p, запустите полностью отделённый процесс из него
Как выполняются асинхронные hooks
Когда срабатывает асинхронный hook, Claude Code запускает процесс hook и немедленно продолжает без ожидания его завершения. Hook получает те же JSON входные данные через stdin, что и синхронный hook. После выхода фонового процесса Claude Code доставляет поляadditionalContext и systemMessage из JSON-ответа хука к Claude на следующем ходу диалога, за исключением повторного вывода SessionStart. В отличие от systemMessage синхронного хука, ни одно из этих полей не показывается вам.
Выводите JSON-ответ в stdout либо единственным содержимым, либо на отдельной строке:
- Единственным содержимым stdout: когда ответ — единственный текст в stdout, он может занимать несколько строк, например отформатированный вывод
jq. Для ответа на нескольких строках требуется Claude Code v2.1.295 или новее. - На отдельной строке: асинхронный хук может выводить в stdout другой текст, если ответ целиком умещается на отдельной строке, например при использовании
jq -c.
systemMessage, который не является строкой, вместо его доставки. Запустите с --debug для просмотра предупреждения, называющего каждое отброшенное поле.
Уведомления о завершении асинхронного 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:- Выход hook доставляется на следующий ход разговора. Если сеанс неактивен, ответ ждёт до следующего взаимодействия пользователя. Исключение: hook
asyncRewake, который выходит с кодом 2, пробуждает Claude немедленно даже когда сеанс неактивен. - Каждое выполнение создаёт отдельный фоновый процесс.
- Асинхронный хук
SessionStartзапускается каждый раз, когда срабатывает событие. Claude Code пропускает вывод хука, если текстadditionalContextиsystemMessageсовпадает с тем, что Claude последним получил от этого хука, если только синхронный хукSessionStartс тех пор не добавил другой текст. Claude Code доставляет текст, когда он меняется, а также в следующий раз, когда хук запускается после сжатия контекста или/clear. До v2.1.295 Claude Code добавлял один и тот же текст в диалог заново при каждом возобновлении.
Соображения безопасности
Отказ от ответственности
Доверие рабочей области
Claude Code проверяет доверие рабочей области перед запуском любого hook из файла параметров. Что считается доверенным, зависит от типа сеанса:- Интерактивный сеанс: Claude Code удерживает hooks из каждого файла параметров, включая ваш собственный
~/.claude/settings.json, пока вы не примете диалог доверия рабочей области для папки или для родительского каталога, чьё доверие распространяется на неё - Сеанс
-pили SDK: Claude Code никогда не показывает диалог и рассматривает папку как доверенную, поэтому hooks, зафиксированные в.claude/settings.jsonрепозитория, запускаются в папке, которой вы никогда не доверяли
claude -p над репозиторием, который вы не писали, проверьте его файлы параметров .claude/, начните с --bare или отключите hooks для этого запуска с помощью --settings '{"disableAllHooks": true}'. Frontmatter hooks в проектном подагенте следуют более строгому правилу, чем hooks файлов параметров. Что запускается перед доверием папке перечисляет каждый вид содержимого репозитория по типу сеанса.
Лучшие практики безопасности
Помните об этих практиках при написании hooks:- Проверяйте и санитизируйте входные данные: никогда не доверяйте входным данным вслепую
- Всегда заключайте переменные оболочки в кавычки: используйте
"$VAR"не$VAR - Блокируйте обход пути: проверяйте наличие
..в путях файлов - Используйте абсолютные пути: указывайте полные пути для скриптов. В форме exec используйте
${CLAUDE_PROJECT_DIR}и путь не требует кавычек. В форме shell оберните его в двойные кавычки - Пропускайте чувствительные файлы: избегайте
.env,.git/, ключей и т. д.
Windows PowerShell tool
На Windows вы можете запустить отдельные hooks в PowerShell, установив"shell": "powershell" на command hook. Claude Code автоматически обнаруживает pwsh.exe, исполняемый файл PowerShell 7 и более поздних версий, и переходит на powershell.exe для Windows PowerShell 5.1.
Ссылки на скрипты по пути в хуках PowerShell
Чтобы ссылаться на корневой каталог проекта из команды PowerShell в форме shell, напишите${CLAUDE_PROJECT_DIR} или $env:CLAUDE_PROJECT_DIR. Claude Code переписывает заполнители ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} и ${CLAUDE_PLUGIN_DATA} в команде PowerShell в форме shell в форму ${env:NAME} PowerShell, независимо от того, определён ли хук в settings.json, плагине или скилле. PowerShell затем разрешает значение из экспортированного окружения после анализа, поэтому заполнитель работает внутри строк в двойных кавычках, но не внутри строк в одинарных кавычках, где PowerShell никогда не расширяет переменные.
Не пишите простое написание $CLAUDE_PROJECT_DIR в hook PowerShell. PowerShell анализирует его как неопределённую локальную переменную и разрешает её в $null, что оставляет путь скрипта без префикса корневого каталога проекта. Claude Code не переписывает эту форму; вместо этого она регистрирует предупреждение в debug log.
Пример ниже показывает хук в settings.json, который запускает скрипт проекта с формой $env::
Debug hooks
Детали выполнения hooks записываются в файл отладочного журнала. Запустите Claude Code сclaude --debug-file <path> для записи журнала в известное расположение, или запустите claude --debug и прочитайте журнал в ~/.claude/debug/<session-id>.txt. Флаг --debug не выводит на терминал.
Например, hook PostToolUse на Write, чья команда выводит hook-ran, создаёт записи вроде:
Hooks:, которые заканчиваются длительностью. В Claude Code v2.1.296 или новее каждый командный хук на событии инструмента, UserPromptSubmit, SessionStart, Stop и ряде других событий оставляет такую строку по завершении, независимо от того, что он вывел. Строка содержит имя события, соединённое двоеточием с именем инструмента или другим значением, по которому сработал хук, затем команду хука в квадратных скобках, плагин, из которого он получен (если есть), то, чем завершился запуск, и его длительность, например Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms). Запуск также может завершиться как timed out after <N>ms, cancelled, moved to the background или failed to start. На некоторых событиях, таких как Notification, SessionEnd и PreCompact, командный хук вместо этого оставляет строку completed with status без длительности.
Для более детальной информации о совпадении hooks установите CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose для просмотра дополнительных строк логирования, таких как количество совпадений фильтра hook и совпадение запроса.
Для устранения неполадок распространённых проблем, таких как hooks, которые не срабатывают, Stop hooks, которые продолжают блокировать, или ошибки конфигурации, см. Limitations and troubleshooting в руководстве. Для более широкого диагностического пошагового руководства, охватывающего /context, /doctor и приоритет параметров, см. Debug your config.