Перейти к основному содержанию
Для краткого руководства с примерами см. Автоматизация рабочих процессов с помощью hooks.
Hooks — это определяемые пользователем команды оболочки, конечные точки HTTP, вызовы инструментов MCP, подсказки LLM или подагенты, которые выполняются автоматически в определённых точках жизненного цикла Claude Code. Claude Code запускает одни и те же события hook везде, где он работает: сеансы в терминале, расширения IDE, приложение Desktop и облачные сеансы. Используйте этот справочник для поиска схем событий, параметров конфигурации, форматов JSON входа/выхода и расширенных функций, таких как асинхронные hooks, HTTP hooks и MCP tool hooks. Плагин также может регистрировать hooks как функции JavaScript, которые Claude Code вызывает в своём собственном процессе, которые могут отображаться в интерфейсе, а также реагировать на события. Плагин, который это делает, — это mod, и эти функциональные hooks рассматриваются в разделе Реагирование на события, а не здесь. Hooks на этой странице продолжают работать наряду с модами.

Жизненный цикл hook

Claude Code запускает hooks в определённых точках во время сеанса. Когда событие срабатывает и совпадает с фильтром, Claude Code передаёт JSON-контекст события вашему обработчику hook. Для command hooks входные данные поступают на stdin. Для HTTP hooks они поступают как тело POST-запроса. Ваш обработчик может затем проверить входные данные, выполнить действие и опционально вернуть решение. События срабатывают в трёх ритмах:
  • один раз за сеанс: SessionStart и SessionEnd
  • один раз за ход: UserPromptSubmit, Stop и StopFailure
  • при каждом вызове инструмента внутри агентного цикла: PreToolUse и PostToolUse, за исключением вызовов EndConversation, которые пропускают оба
Диаграмма жизненного цикла hook, показывающая опциональный Setup, переходящий в SessionStart, затем цикл за ход, содержащий UserPromptSubmit, UserPromptExpansion для slash commands, вложенный агентный цикл (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted) и Stop или StopFailure, за которым следуют TeammateIdle, PreCompact, PostCompact и SessionEnd, с Elicitation и ElicitationResult вложенными внутри выполнения MCP tool, PermissionDenied как боковая ветвь от PermissionRequest для автоматических отказов, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged и DirectoryAdded как отдельные асинхронные события, PreModelSwitch как отдельное последовательное событие, которое запускается перед запрошенным переключением модели, PostModelSwitch как отдельное асинхронное событие, которое запускается после изменения модели сеанса, и MessageDisplay как событие только для отображения, которое запускается во время потоковой передачи текста сообщения помощникаДиаграмма жизненного цикла hook, показывающая опциональный Setup, переходящий в SessionStart, затем цикл за ход, содержащий UserPromptSubmit, UserPromptExpansion для slash commands, вложенный агентный цикл (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted) и Stop или StopFailure, за которым следуют TeammateIdle, PreCompact, PostCompact и SessionEnd, с Elicitation и ElicitationResult вложенными внутри выполнения MCP tool, PermissionDenied как боковая ветвь от PermissionRequest для автоматических отказов, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged и DirectoryAdded как отдельные асинхронные события, PreModelSwitch как отдельное последовательное событие, которое запускается перед запрошенным переключением модели, PostModelSwitch как отдельное асинхронное событие, которое запускается после изменения модели сеанса, и MessageDisplay как событие только для отображения, которое запускается во время потоковой передачи текста сообщения помощника
Таблица ниже суммирует, когда срабатывает каждое событие. Раздел Hook events документирует полную схему входа и параметры управления решением для каждого события.

Как разрешается hook

Чтобы увидеть, как событие, фильтр и обработчик работают вместе, рассмотрим этот hook PreToolUse, который блокирует деструктивные команды оболочки.
matcher сужает область до вызовов инструмента Bash и команд Monitor, а условие if сужает её дальше до подкоманд Bash, совпадающих с rm *, поэтому block-rm.sh запускается, только когда оба фильтра совпадают:
Скрипт читает JSON входные данные из stdin, извлекает команду и возвращает permissionDecision со значением "deny", если она содержит rm -rf. Сохраните его в .claude/hooks/block-rm.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x .claude/hooks/block-rm.sh, чтобы Claude Code мог его запустить:
Этот скрипт, как и другие примеры Bash на этой странице, которые анализируют JSON входные данные, использует jq, поэтому установите jq и убедитесь, что он находится в вашем PATH, перед попыткой их использования.
Теперь предположим, что Claude Code решает запустить Bash "rm -rf /tmp/build" с конфигурацией macOS/Linux. Вот что происходит:
Диаграмма разрешения hook: срабатывает событие PreToolUse, фильтр проверяет совпадение Bash, затем условие if проверяет совпадение Bash(rm *). Если оба совпадают, команда hook запускается и возвращает permissionDecision deny, поэтому вызов инструмента блокируется и Claude Code продолжает работу. Если одна из проверок не совпадает, hook пропускается и вызов инструмента может продолжить работу.Диаграмма разрешения hook: срабатывает событие PreToolUse, фильтр проверяет совпадение Bash, затем условие if проверяет совпадение Bash(rm *). Если оба совпадают, команда hook запускается и возвращает permissionDecision deny, поэтому вызов инструмента блокируется и Claude Code продолжает работу. Если одна из проверок не совпадает, hook пропускается и вызов инструмента может продолжить работу.
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 причину.
Раздел Configuration ниже документирует полную схему, и каждый раздел hook event документирует, какой входной JSON получает ваша команда и какой выход она может вернуть.

Конфигурация

Хуки определяются в JSON-файлах настроек. Конфигурация имеет три уровня вложенности:
  1. Выберите событие хука, на которое нужно реагировать, например PreToolUse или Stop
  2. Добавьте группу matcher, чтобы отфильтровать, когда он срабатывает, например «для инструмента Bash»
  3. Определите один или несколько обработчиков хука, которые будут запускаться при совпадении
Полный разбор с аннотированным примером см. выше в разделе Как разрешается хук.
На этой странице для каждого уровня используются определённые термины: событие хука — для точки жизненного цикла, группа 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: инструмент создания сущностей сервера Memory
  • mcp__filesystem__read_file: инструмент чтения файла сервера Filesystem
  • mcp__github__search_repositories: инструмент поиска сервера GitHub
Чтобы сопоставить все инструменты сервера, добавьте .* к префиксу сервера. .* обязателен: matcher вроде mcp__memory или mcp__brave-search содержит только символы точного совпадения, поэтому он сравнивается как точная строка и не соответствует ни одному инструменту.
  • mcp__memory__.* соответствует всем инструментам сервера memory
  • mcp__brave-search__.* соответствует всем инструментам сервера, имя которого содержит дефис
  • mcp__.*__write.* соответствует любому инструменту любого сервера, имя которого начинается с write
Инструменты MCP-сервера, поставляемого с плагином, используют сегмент сервера с областью, включающий имя плагина: 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, чтобы проверить условия перед возвратом решения. Хуки агентов являются экспериментальными и могут измениться. См. Хуки на основе агентов.
Когда событие срабатывает, соответствующие ему хуки запускаются параллельно. Если более 12 из них являются хуками с промптом или хуками агентов, 12 из них запускаются одновременно, а остальные запускаются по мере завершения работающих. 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.
Этот пример запускает Node-скрипт, поставляемый с плагином. Форма exec передаёт разрешённый путь к скрипту как один аргумент без экранирования кавычками:
Эквивалентная форма 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.
О сессии в worktree, которую вы запускаете из десктопного приложения, и о том, куда указывает ${CLAUDE_PROJECT_DIR}, см. Что worktree разделяют с основной рабочей копией.
Для любого хука, ссылающегося на плейсхолдер пути, предпочитайте форму exec. В форме shell заключайте каждый плейсхолдер в двойные кавычки.
Этот пример использует ${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:
Субагенты используют тот же формат в своём YAML frontmatter. Хуки во frontmatter проектного скилла следуют тому же правилу доверия к рабочему пространству, что и хуки в файлах настроек. Claude Code регистрирует их, когда вы или Claude вызываете скилл, в том числе при запуске с -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, под которым находится несколько хуков, учитывается один раз для каждого хука.
    • Проверка: повторите сессию, в которой хук не сработал, с включённым логированием отладки:
      1. Запустите новую сессию из оболочки с помощью claude --debug. Если вы не запускаете сессии из оболочки, вместо этого сначала введите /debug в новой сессии.
      2. Вызовите те же скиллы в том же порядке, включая те, которые вызывал Claude, поскольку оба итоговых значения накапливаются скилл за скиллом. Попросите Claude вызвать любой скилл, который вы не можете ввести как команду.
      3. Найдите в логе отладки registers none of its hooks. Строка с этим текстом называет скилл, который превысил лимит.
    • Исправление: объявите меньше хуков или более короткие matcher в этом скилле или в других скиллах из его источника, затем начните новую сессию. Если эти скиллы не ваши и вы не можете их редактировать, попросите их автора внести это изменение. До тех пор начинайте новую сессию и вызывайте нужный скилл раньше любого другого скилла из его источника.
Если ни один из случаев не ваш, проверьте, нет ли frontmatter, который не удалось разобрать.

Меню /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.
То, что ваш хук выводит в stdout, может изменить результат. Например, если хук 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, которые всё равно срабатывают.
Чтобы узнать, что делает код выхода 2 для вашего события, смотрите Поведение кода выхода 2 для каждого события. Чтобы узнать, какие поля решений оно учитывает, смотрите Управление решениями.

Код выхода 0

Выход с кодом 0 означает успех и является предполагаемым кодом выхода, когда вы выводите JSON для структурированного управления. Если ваш хук завершается с кодом 0, но выводит JSON, который не удаётся разобрать или который не проходит проверку схемы, действие выполняется, если хук не относится к тем, сбой которых блокирует действие. Вместо успеха с применёнными полями запуск считается неблокирующей ошибкой. Чтобы исправить вывод, воспользуйтесь сообщением о разборе или проверке, которое содержит уведомление <hook name> hook error. Для хука, который не является асинхронным, ваш stdout считается выводом JSON, только если вы выводите один объект JSON на одной или нескольких строках, вокруг которого нет ничего, кроме пробельных символов. Любой другой stdout Claude Code воспринимает как обычный текст или как ошибку разбора:
  • Вывод, который не начинается с { или начинается с {, но не заканчивается }: обычный текст. По этому правилу массив JSON и строка JSON в кавычках считаются обычным текстом.
  • Две или более строк, каждая из которых по отдельности разбирается как JSON, причём первая начинается с {, а последняя заканчивается }: обычный текст, если ни одна строка не является объектом вывода JSON, задающим какое-либо поле. Если такая строка есть, вывод считается ошибкой разбора, поэтому выводите этот объект, не добавляя в stdout ничего другого.
  • Всё остальное, что начинается с { и заканчивается }, но не является корректным JSON: ошибка разбора.
Для большинства событий Claude Code записывает stdout в отладочный лог и не показывает его в транскрипте. Для следующих событий ваш stdout доходит до Claude или до сводки сжатия контекста:
  • UserPromptSubmit, UserPromptExpansion, SessionStart и PostModelSwitch: Claude Code добавляет обычный текст из stdout как контекст, который Claude может видеть и учитывать. Stdout, который ему не удалось разобрать, Claude Code не добавляет.
  • PreCompact: ваш stdout становится инструкциями для сводки сжатия контекста
Claude никогда не видит stderr хука, завершившегося с кодом 0. Чтобы прочитать его самостоятельно для таких событий, как 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, как в следующей строке, хук так и не был запущен, например потому что путь к его скрипту не существует:
Если путь неверен, исправьте его там, где настроен хук. В форме оболочки, если путь к скрипту не существует или файл не является исполняемым, оболочка завершается с кодом вроде 127, и уведомление содержит сообщение интерпретатора. Если хук не относится к тем, сбой которых блокирует действие, хук, который не удаётся запустить, тоже считается неблокирующей ошибкой. Настраивая хук для политики, следите за этим уведомлением при его первом запуске, потому что опечатка в пути в settings.json означает, что хук никогда не выполнится. Чтобы блокировать действие, когда хук не удаётся запустить, установите onFailure: "block".
Код выхода 1 без корректного вывода JSON является неблокирующей ошибкой, если хук не относится к тем, сбой которых блокирует действие, хотя 1 — общепринятый код ошибки в Unix. Если ваш хук предназначен для соблюдения политики, используйте exit 2.

Таймауты

За исключением командного хука, запущенного с 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 секунд, чтобы завершиться после выхода собственного процесса.
Если ваш командный хук принимает решение в фоновом процессе и завершается, то отказ, который этот процесс выводит после того, как Claude Code перестал читать вывод, не блокирует вызов инструмента. Для хука, который не является асинхронным, Claude Code заканчивает чтение вывода хука вскоре после завершения собственного процесса хука. Выводите решение до запуска фонового процесса и перенаправляйте stdout и stderr этого процесса. Если ваш скрипт выводит разрешение, а затем запускает фоновый процесс, не перенаправив его stdout и stderr, Claude Code блокирует вызов инструмента. Ошибка хука содержит 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
До версии v2.1.309 сообщение выглядело так: 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.
Выводите в stdout только объект JSON. Для хука, который не является асинхронным, любой другой текст там, например строка, которую профиль вашей оболочки выводит при запуске, мешает Claude Code прочитать объект как JSON; в разделе JSON хука не действует показано, как найти и убрать этот текст. Строки additionalContext, systemMessage и initialUserMessage хука, а также его обычный stdout ограничены 10 000 символов:
  • Область действия: Claude Code измеряет каждую строку отдельно, даже если для одного события выполняется несколько хуков. Для вывода JSON каждое поле измеряется отдельно; обычный stdout измеряется целиком.
  • Превышение лимита: за исключением stdout хука PreCompact, Claude Code сохраняет вывод в файл в каталоге сессии. Он заменяет вывод путём к файлу и превью, содержащим до 2 000 первых символов. Большой корректный результат Bash обрабатывается так же, как описано в разделе Ограничения вывода. В отличие от этого ограничения Bash, у данного лимита нет настройки или переменной окружения, позволяющей его увеличить.
  • Чтение файла: Claude Code не просит Claude прочитать файл, поэтому держите всё, что Claude должен видеть всегда, в пределах лимита.
Объект JSON поддерживает три вида полей:
  • Универсальные поля, такие как 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
Последовательности могут завершаться символом BEL или ST. Всё, что не входит в список разрешённых последовательностей, включая последовательности CSI для курсора и цвета, последовательности OSC для палитры, гиперссылки OSC 8, запись в буфер обмена OSC 52 и OSC 1337, отклоняется, а поле игнорируется. Claude Code сам записывает последовательность при обработке вывода вашего хука, поэтому поле работает для событий, которые отбрасывают systemMessage и continue, таких как Notification и StopFailure. У него есть некоторые ограничения:
  • Claude Code записывает последовательность только в интерактивной сессии и только пока его интерфейс находится на экране. В неинтерактивном режиме с флагом -p и в Agent SDK он игнорирует это поле.
  • Поле работает только в хуке, который не выполняется в фоновом режиме, поэтому не включайте async и asyncRewake.
  • Командный хук WorktreeCreate не может возвращать JSON, потому что Claude Code читает его stdout как путь к worktree. HTTP-хук WorktreeCreate возвращает JSON и может включать это поле.
Если вы используете tmux или GNU screen, сначала выполните их настройку. В примере ниже хук 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 вместе с именем события:
Место появления напоминания зависит от события: Когда несколько хуков возвращают additionalContext для одного события, Claude получает все значения. Если ваша строка содержит тег <system-reminder> или </system-reminder>, Claude получает строку, в которой < этого тега заменён на &lt;. Если значение превышает 10 000 символов, Claude Code записывает текст в файл в каталоге сессии и вместо этого передаёт Claude путь к файлу с превью, содержащим до 2 000 первых символов. Claude может прочитать файл, но Claude Code не просит его об этом. Используйте additionalContext для информации, которую Claude следует знать о текущем состоянии вашего окружения или об только что выполненной операции:
  • Состояние окружения: текущая ветка, цель развёртывания или активные флаги функций
  • Условные правила проекта: какая команда тестирования применима к только что отредактированному файлу, какие каталоги доступны только для чтения в этом worktree
  • Внешние данные: открытые задачи, назначенные вам, недавние результаты CI, содержимое, полученное из внутреннего сервиса
Для инструкций, которые никогда не меняются, предпочтительнее CLAUDE.md. Он загружается без запуска скрипта и является стандартным местом для статических соглашений проекта. Пишите текст в виде фактических утверждений, а не императивных системных инструкций. Формулировки вроде «The deployment target is production» или «This repo uses bun test» воспринимаются как информация о проекте. Текст, оформленный как внеполосные системные команды, может активировать защиту Claude от промпт-инъекций, из-за чего Claude покажет этот текст вам вместо того, чтобы воспринять его как контекст. Claude Code сохраняет внедрённый текст в транскрипте сессии. Для событий в середине сессии, таких как PostToolUse или UserPromptSubmit, при возобновлении с --continue или --resume Claude Code воспроизводит сохранённый текст, а не запускает хук повторно для прошлых ходов, поэтому такие значения, как временные метки или SHA коммитов, устаревают. Хуки SessionStart при возобновлении запускаются снова с source, установленным в "resume", или в "fork", если вы добавили --fork-session, поэтому они могут обновить свой контекст.

Управление решениями

Не каждое событие поддерживает блокировку или управление поведением через JSON. События, которые это поддерживают, используют разные наборы полей для выражения решения. Используйте эту таблицу как краткий справочник перед написанием хука: Некоторые события также могут переписывать содержимое, а не только разрешать или блокировать его:
  • PreToolUse: updatedInput непосредственно в hookSpecificOutput заменяет аргументы инструмента перед его запуском. Смотрите Управление решениями PreToolUse
  • PermissionRequest: updatedInput внутри объекта decision. Смотрите Управление решениями PermissionRequest
  • PostToolUse: updatedToolOutput заменяет результат инструмента. В облачной сессии Cowork Claude Code игнорирует updatedToolOutput, если инструментом является AskUserQuestion или ExitPlanMode, и Claude читает исходный ответ или план. Смотрите Управление решениями PostToolUse
  • UserPromptSubmit: не может заменить промпт; он только внедряет additionalContext рядом с ним
Для сценариев редактирования или преобразования данных перехватывайте исходящие входные данные инструментов в PreToolUse, а входящие результаты инструментов — в PostToolUse. Вот примеры каждого шаблона в действии:
Единственное значение для decision — "block". Чтобы разрешить продолжение действия, не указывайте decision в вашем JSON или завершитесь с кодом 0 вообще без JSON:
Расширенные примеры, включая проверку команд Bash, фильтрацию промптов и скрипты автоматического одобрения, смотрите в разделе Что можно автоматизировать руководства и в эталонной реализации валидатора команд Bash.

События хуков

Каждое событие соответствует точке в жизненном цикле 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, доступным всем хукам: Этот вывод добавляет контекст и даёт сессии имя:
Хук, который только добавляет контекст, может просто напечатать его, не формируя JSON, поскольку Claude Code добавляет обычный текстовый stdout хука SessionStart в контекст Claude. Если хук SessionStart вашего плагина задаёт initialUserMessage или sessionTitle, установите плагин до начала сессии. Claude Code игнорирует оба поля от плагина, установка которого завершается после того, как хуки SessionStart уже выполнились.

Перезагрузка скиллов, которые устанавливает хук

Чтобы скиллы, которые устанавливает хук SessionStart, стали доступны в той же сессии, верните reloadSkills. Обнаружение скиллов обычно выполняется до завершения хуков SessionStart, поэтому без этого поля файлы, которые хук записывает в ~/.claude/skills/ или .claude/skills/, могут отсутствовать при выполнении первого промпта. Этот пример синхронизирует общий репозиторий скиллов и запрашивает повторное сканирование:
URL репозитория — это заглушка. Замените его на свой репозиторий скиллов.

Сохранение переменных окружения

Хуки SessionStart имеют доступ к переменной окружения CLAUDE_ENV_FILE, которая указывает путь к файлу, где можно сохранить переменные окружения для shell-команд, которые Claude выполняет позже в сессии. Чтобы задать отдельные переменные окружения, запишите операторы export в CLAUDE_ENV_FILE. Используйте дозапись (>>), чтобы сохранить переменные, заданные другими хуками:
Чтобы зафиксировать все изменения окружения от команд настройки, сравните экспортированные переменные до и после:
Каждая команда Bash выполняет содержимое файла как shell-код перед самой командой, поэтому строка в нём может использовать всё, что вычисляет Bash, например ссылку на $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 также запускает их, когда: Хуки 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 добавляется как контекст
Ни один из каналов не создаёт видимой записи в транскрипте. Обычный stdout и значение additionalContext внедряются каждое как системное напоминание, начинающееся с имени хука; Claude читает оба. Чтобы подтвердить доставку, проверьте лог отладки. Чтобы заблокировать промпт, верните объект JSON с decision, равным "block": Хук, который блокирует через выход с кодом 2, обрабатывается так же, как reason: сообщение о блокировке показывает пользователю текст stderr, и он не добавляется в контекст.

Что остаётся после заблокированного промпта

Заблокированный промпт никогда не доходит до Claude, но его текст удаляется не везде. По умолчанию сообщение о блокировке, показываемое пользователю, заканчивается строкой Original prompt:, за которой следует отправленный текст, и Claude Code записывает это сообщение в файл транскрипта сессии на диске. Чтобы исключить текст из сообщения, выведите JSON с "suppressOriginalPrompt": true внутри hookSpecificOutput. Это работает независимо от того, блокирует ли хук через decision: "block" или через выход с кодом 2. suppressOriginalPrompt изменяет только сообщение о блокировке. Отправленный текст всё равно может оказаться в локальных файлах, таких как транскрипт сессии и история ваших промптов, поэтому блокирующий хук не является способом не допустить попадания секрета на диск. Чтобы ограничить или удалить эти файлы, см. Хранение в открытом виде и Очистка локальных данных.

UserPromptExpansion

Выполняется, когда введённая пользователем команда разворачивается в промпт до того, как дойти до Claude. Используйте это, чтобы блокировать прямой вызов определённых команд, внедрять контекст для конкретного скилла или логировать, какие команды вызывают пользователи. Например, хук с matcher deploy может блокировать /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
Claude Code удерживает каждую партию, пока ваш хук не вернёт результат, поэтому хук должен работать быстро. Если хук завершается с ошибкой или по таймауту, Claude Code отображает исходный текст. Таймаут по умолчанию для этого события — 10 секунд; если вашему хуку нужно больше времени, задайте поле 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.
Зарегистрируйте командный хук для события в файле настроек:
Сохраните этот скрипт в .claude/hooks/plain-display.sh в своём проекте и сделайте его исполняемым с помощью chmod +x:
Партии без markdown проходят без изменений. Если скрипт завершается с ошибкой, например из-за отсутствия 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 выполняется только тогда, когда Claude вызывает инструмент. По умолчанию файлы, на которые вы ссылаетесь с помощью @ в промпте, добавляются без какого-либо вызова инструмента. Claude Code вставляет их содержимое при формировании промпта, поэтому для них не срабатывает ни один хук PreToolUse, включая хуки, соответствующие Read. PreToolUse также не срабатывает для EndConversation.Вы можете блокировать определённые пути в ссылках @ или отключить вставку:
  • Блокировка определённых путей: добавьте для них правило запрета Read
  • Отключение вставки: задайте CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 в своей оболочке, пользовательских настройках или управляемых настройках. Тогда Claude получает упомянутые пути с пометкой прочитать их с помощью своих файловых инструментов. Если он это делает, выполняются соответствующие хуки PreToolUse. Для этой переменной требуется Claude Code v2.1.299 или новее, и Claude Code игнорирует её в настройках проекта и локальных настройках.
Используйте управление решениями 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 отказ содержит такой текст:
После отказа с этим текстом Claude может снова выполнить изменённый вызов, чтобы сервер его проверил. Если Claude в следующем ответе в точности повторяет изменённый ввод, ваш хук всё равно выполняется:
  • Он может отклонить вызов или отправить его на запрос разрешения
  • Любой возвращённый им updatedInput отбрасывается, поэтому выполняется то, что проверил сервер
Менять хук из-за этого отказа не нужно, если только Claude не сообщит в ответе, что авторежим не смог проверить вызов. В сессиях, где показывается этот текст, 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 удовлетворяет этому требованию, если он делает следующее:
  1. Считывает входные данные инструмента из stdin
  2. Собирает ответ через ваш собственный интерфейс
  3. Возвращает permissionDecision: "allow" вместе с updatedInput, содержащим ответ, чтобы инструмент выполнился без запроса
Одного возврата "allow" для этих инструментов недостаточно. Для AskUserQuestion верните исходный массив questions и добавьте объект answers, сопоставляющий текст каждого вопроса с выбранным ответом. Этот вывод отвечает на один вопрос вариантом React:
MCP-инструмент, который сервер помечает с помощью _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, поэтому начинайте запуск с ним. Цикл работает так:
  1. Claude вызывает AskUserQuestion. Срабатывает хук PreToolUse.
  2. Хук возвращает permissionDecision: "defer". Инструмент не выполняется. Ход завершается результатом с stop_reason: "tool_deferred", а ожидающий вызов инструмента остаётся в транскрипте.
  3. Вызывающий процесс читает deferred_tool_use из результата SDK, показывает вопрос в своём интерфейсе и ждёт ответа.
  4. Вызывающий процесс выполняет claude -p --resume <session-id> с тем же хостом разрешений. Тот же вызов инструмента снова вызывает PreToolUse.
  5. Хук возвращает 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 с matcher Edit|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, когда хуки должны выполниться.
Когда 10 секунд истекают, Claude Code завершает всё ещё работающий хук команды, независимо от его 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:
updatedToolOutput изменяет только то, что видит Claude. К моменту срабатывания хука инструмент уже выполнился, поэтому все записанные файлы, выполненные команды или отправленные сетевые запросы уже вступили в силу. Телеметрия, такая как спаны инструментов OpenTelemetry и аналитические события, также фиксирует исходный вывод до запуска хука. Чтобы предотвратить или изменить вызов инструмента до его выполнения, используйте вместо этого хук PreToolUse.Значение замены должно соответствовать форме вывода инструмента. Встроенные инструменты возвращают структурированные объекты, а не простые строки. Например, Bash возвращает объект с полями stdout, stderr, interrupted и isImage. Для встроенных инструментов значение, не соответствующее схеме вывода инструмента, игнорируется, и используется исходный вывод. Вывод инструментов MCP передаётся без проверки схемы. Удаление сведений об ошибках, которые нужны Claude, может привести к тому, что он продолжит работу на основе ложного предположения.

Аннотирование результата для классификатора авторежима

Верните classifierContext, чтобы отправить короткую заметку о результате вызова инструмента классификатору авторежима, а не Claude. Заметка предназначена для проверки классификатором последующих действий. Поле требует Claude Code v2.1.236 или новее. Пример ниже сообщает классификатору, откуда получен вывод запроса:
Какой вес классификатор придаёт примечанию, зависит от того, где вы настроили хук:
  • Хуки, настроенные в Claude Code: для хуков из файлов настроек, плагинов, скиллов и frontmatter агентов классификатор рассматривает примечание как непроверенный контекст, предоставленный приложением. Примечание никогда не устанавливает намерение пользователя, и если оно утверждает, что вы что-то одобрили или запросили, классификатор сверяет это утверждение с вашими собственными сообщениями в диалоге
  • Внутрипроцессные обратные вызовы Agent SDK: когда приложение, встраивающее Claude Code, регистрирует хук как обратный вызов TypeScript SDK и возвращает примечание во время активной сессии, классификатор может учитывать переданное в примечании утверждение пользователя как намерение пользователя. Такое утверждение может удовлетворить требование согласия, которое классификатор принял бы от отправленного вами сообщения, но оно никогда не снимает блокировку, которую не смогло бы снять и ваше собственное сообщение. После возобновления сессии Claude Code рассматривает восстановленные примечания как непроверенный контекст. Когда хуки из обеих групп аннотируют один и тот же вызов, классификатор рассматривает объединённое примечание как непроверенное
При доставке примечания Claude Code применяет следующие ограничения:
  • Длина: Claude Code ограничивает примечания для одного вызова инструмента 2000 символами и обрезает остальное. Ограничение общее для всех хуков, отвечающих на этот вызов
  • Только синхронные ответы: Claude Code игнорирует это поле в ответе хука, который выполняется в фоне, поскольку такой ответ приходит после того, как Claude Code записал результат инструмента
  • Вызовы, которые классификатор не записывает: транскрипт классификатора не включает операции только для чтения, такие как чтение файлов и поиск. Claude Code отбрасывает примечание, прикреплённое к одному из таких вызовов
  • Взаимодействие с перезаписью: когда примечание описывает вывод, который вы заменяете с помощью updatedToolOutput, возвращайте оба поля в одном ответе хука. Claude Code отбрасывает примечание, если эта перезапись отклонена или её заменяет перезапись другого хука. Claude Code доставляет примечание, возвращённое без перезаписи, даже если другой хук перезаписывает вывод
Классификатор читает содержимое, помещённое вами в classifierContext, как информацию от приложения, в котором размещена сессия, поэтому не копируйте в него ненадёжный вывод инструментов или сторонний текст. Ограничивайте примечание кратким утверждением об этом одном вызове, например фактом о его происхождении или утверждением пользователя о нём; не используйте это поле для доставки посторонних сообщений или потока событий.

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: таймер запускается при появлении диалогового окна, и каждое нажатие клавиши откладывает его.
Запрос разрешения или запрос данных, поступивший, пока на экране открыто другое диалоговое окно, сохраняет тот же шестисекундный порог, отсчитываемый от момента поступления запроса. Его уведомление может дойти до вас, пока запрос ещё ожидает за открытым диалоговым окном.
Claude Code иначе определяет время срабатывания 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 в этих сессиях.
До v2.1.233 permission_prompt в этих сессиях не срабатывал. Используйте отдельные matcher, чтобы запускать разные обработчики в зависимости от типа уведомления. Эта конфигурация запускает скрипт оповещения, специфичный для разрешений, когда Claude нужно подтверждение разрешения, и другое уведомление, когда Claude бездействует:

Входные данные Notification

Помимо общих полей входных данных, хуки Notification получают message с текстом уведомления, необязательный title и notification_type, указывающий, какой тип сработал.
Хуки Notification не могут блокировать или изменять уведомления. Claude Code отбрасывает их поля 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.
Хуки SubagentStart не могут блокировать создание субагента, но могут внедрять контекст в субагента. Помимо полей вывода JSON, доступных всем хукам, вы можете вернуть:
Когда хук снова запускается для того же субагента, Claude Code внедряет возвращённый контекст, только если контекст субагента ещё не содержит копию из предыдущего запуска. Копия, внедрённая при запуске, остаётся на месте, сохраняя кэш промптов субагента нетронутым. После того как автосжатие отбросит эту копию, Claude Code снова внедряет контекст следующего запуска.

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. Оба массива относятся к родительской сессии, а не к субагенту.
Хуки SubagentStop используют тот же формат управления решениями, что и хуки 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.
Команда /goal — это встроенный быстрый способ создать хук Stop на основе промпта в рамках сессии. Используйте её, когда хотите, чтобы Claude продолжал работать до достижения условия, не создавая конфигурацию хука.

Входные данные 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.
У хуков StopFailure нет управления решениями. Они выполняются только для уведомлений и логирования.

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
  • Вы добавляете каталог, который уже является рабочим каталогом или находится внутри него
Claude Code вызывает DirectoryAdded после обновления состояния песочницы и разрешений, поэтому инструменты в песочнице уже видят новый каталог, когда запускается ваш хук. Сами команды хуков выполняются вне песочницы. Claude Code не ждёт хук: добавление завершается немедленно, а хук выполняется в фоне с таймаутом по умолчанию 600 секунд. Matcher фильтрует по способу добавления каталога:

Входные данные DirectoryAdded

Помимо общих полей входных данных, хуки DirectoryAdded получают directory и source.
У хуков DirectoryAdded нет управления решениями. Они не могут заблокировать добавление, которое уже завершено к моменту запуска хука. Claude Code отбрасывает поле 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 и сделайте его исполняемым:
Чтобы убедиться, что хук работает, попросите Claude добавить строку с CRLF в 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, например в таких случаях: Хук WorktreeCreate не запускается для worktree, который Claude добавляет через Bash, а хук плагина не запускается для worktree, создаваемого при запуске с --worktree и --tmux:
  • git worktree add через Bash: чтобы проверить или заблокировать эту команду, добавьте хук PreToolUse с matcher Bash.
  • --worktree с --tmux: Claude Code создаёт worktree до загрузки плагинов, поэтому хук WorktreeCreate из плагина не запускается. Чтобы ваш хук запускался с обоими флагами, определите его в файле настроек. До v2.1.300 хук WorktreeCreate плагина не запускался и с одним только --worktree.
По умолчанию Claude Code создаёт изолированную рабочую копию с помощью 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" } } в теле ответа.
Если хук завершается неудачей или не выдаёт путь, создание worktree завершается с ошибкой. Claude Code разрешает относительный путь относительно каталога, в котором выполнялся хук, сворачивая в нём любые сегменты . или ... Если полученный путь не является каталогом, в который 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 изменённых или неотслеживаемых файлов и новых коммитов
Когда субагент или агент рабочего процесса завершает работу, Claude Code сохраняет worktree, созданный для него вашим хуком WorktreeCreate, и не запускает хук WorktreeRemove. Удаляйте такие worktree самостоятельно, когда работа завершена. Claude Code не запрашивает подтверждение перед запуском хука для вызова 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>.
    • Вы удаляете фоновую сессию: см. Что удаляется при удалении сессии.
  • Хук завершается с кодом 0: worktree считается удалённым. Claude Code больше ничего не считывает из хука, поэтому убедитесь, что хук удалил каталог.
  • Хук завершается с ненулевым кодом: удаление завершается неудачей, если каталог по пути worktree_path после этого всё ещё существует, и worktree остаётся на диске без резервного варианта через git. Хук, удаливший каталог перед завершением с ненулевым кодом, считается выполнившим удаление. О том, как сообщается о неудаче, см. Входные данные WorktreeRemove.
Claude Code никогда не удаляет ветку, принадлежащую worktree, созданному хуком, поскольку знает только путь, возвращённый вашим хуком WorktreeCreate. Если ваш хук WorktreeCreate создаёт ветку, удаляйте её в хуке WorktreeRemove. Claude Code отбрасывает поля вывода JSON хука WorktreeRemove, такие как systemMessage и continue. При удалении фоновой сессии Claude Code проверяет сохранённый путь к worktree перед запуском хука и отклоняет путь, который является символической ссылкой или проходит через неё ниже корня репозитория. На этом пути хук запускается для worktree, в котором ещё есть файлы, только если вы подтверждаете удаление в представлении агентов. Для такого worktree claude rm вместо этого сохраняет сессию и worktree. Claude Code передаёт путь, возвращённый WorktreeCreate, как worktree_path во входных данных хука. Этот пример считывает этот путь и удаляет каталог:

Входные данные WorktreeRemove

Помимо общих входных полей, хуки WorktreeRemove получают поле worktree_path — абсолютный путь к удаляемому worktree.
Результат определяется кодом выхода хука WorktreeRemove. Если хук завершается с ненулевым кодом, а каталог по пути 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 содержит сводку диалога, сгенерированную операцией сжатия.
Хуки PostCompact не управляют решениями. Они не могут повлиять на результат сжатия, но могут выполнять последующие задачи.

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
Claude Code не запускает хуки PreModelSwitch для переключений, которые он выполняет сам, например при автоматическом переключении на резервную модель или восстановлении модели при возобновлении сессии. Такие изменения доходят только до PostModelSwitch. Claude Code сравнивает matcher с каноническим именем модели, на которую переключается сессия, игнорируя суффикс [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, и пропускает любую другую целевую модель:
Команда проверяет 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:
Когда несколько хуков PreModelSwitch возвращают разные решения, приоритет такой: 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 после любого из следующих изменений: Claude Code не запускает хуки PostModelSwitch, когда ход обслуживает модель из цепочки резервных моделей, потому что такая замена длится один ход и не меняет модель сессии. Matcher подчиняется тем же правилам, что и в PreModelSwitch: Claude Code сравнивает его с каноническим именем модели, на которую переключилась сессия. Этот пример добавляет указания всякий раз, когда модель сессии меняется на любую модель Opus:
Чтобы убедиться, что хук работает, переключитесь на модель Opus в сессии с другой моделью, например выполните /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, указывающее, почему сессия завершилась. Все значения см. в таблице причин выше.
Хуки SessionEnd не управляют решениями. Они не могут заблокировать завершение сессии, но могут выполнять задачи очистки. Claude Code отбрасывает их поля вывода JSON, такие как systemMessage. Таймаут по умолчанию для хуков SessionEnd — 1,5 секунды. Он действует, когда вы выходите, выполняете /clear или переключаете сессии через интерактивную /resume. Дать хуку больше времени можно двумя способами:
  • timeout для отдельного хука: задайте timeout в конфигурации этого хука. Общий лимит времени автоматически увеличивается до наибольшего значения timeout отдельного хука в ваших файлах настроек, но не более 60 секунд. Если вы увеличиваете лимит таким образом, хук без собственного timeout по-прежнему сохраняет значение по умолчанию. Таймауты, заданные для хуков из плагинов, не увеличивают лимит.
  • CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: задайте эту переменную окружения в миллисекундах, чтобы явно переопределить лимит. Заданное значение также становится таймаутом для каждого хука без собственного timeout.
Этот пример устанавливает лимит в 5 секунд:
До v2.1.268 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 в режиме URL, используемого для аутентификации через браузер:

Выходные данные Elicitation

Хук Elicitation может ответить на запрос вместо пользователя, отклонить или отменить его либо оставить его диалоговому окну. Чтобы ответить, отклонить или отменить, завершитесь с кодом 0 и выведите объект hookSpecificOutput с action. Сервер получит ваш ответ, и диалоговое окно не появится. Каждая строка этой таблицы показывает, что вернуть для одного исхода и что получит MCP-сервер: Этот вывод отвечает на запрос в режиме формы, показанный в разделе Входные данные Elicitation. Ключи в content — это имена свойств из requested_schema этого запроса:
Этот вывод отклоняет запрос:
В диалоговом окне выбор Decline отправляет 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 и оставляет запросы формы диалоговому окну:
Ни пользователь, ни сервер не видят, почему хук отклонил запрос, потому что Claude Code не показывает ваш stderr или ваш reason. Claude Code игнорировал decision верхнего уровня от хуков Elicitation и ElicitationResult, начиная с v2.1.105 и до исправления в v2.1.284.

Ответ на запрос формы из скрипта

Этот пример отвечает на один повторяющийся вопрос вместо пользователя. MCP-сервер с именем issue-tracker запрашивает ключ проекта в форме, и хук подставляет DOCS. Скрипт принимает запрос, когда project_key — единственное поле формы. Для любого другого запроса он ничего не выводит, поэтому появляется диалоговое окно.
Зарегистрируйте хук команды для этого события в файле настроек, указав имя сервера в качестве matcher:
Сохраните этот скрипт в .claude/hooks/answer-project-key.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x:
Чтобы убедиться, что хук работает, запустите Claude Code командой 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, иначе хук превратит отклонённый запрос в принятый. Этот скрипт вносит то же изменение, если пользователь принял запрос, сохраняет остальные поля и в противном случае ничего не выводит:
Этот вывод блокирует ответ:
Код выхода 2 и "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):
  • PermissionDenied
  • PostToolBatch
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • TeammateIdle
  • UserPromptExpansion
  • UserPromptSubmit
PermissionRequest поддерживает command, http, mcp_tool и prompt hooks, но не agent hooks. Если вы настроите agent hook на этом событии, Claude Code пропустит его и поток разрешений продолжится без изменений. Чтобы разрешить или отклонить из hook, верните объект решения из command или HTTP hook. События, которые поддерживают command, http и mcp_tool hooks, но не prompt или agent:
  • ConfigChange
  • CwdChanged
  • DirectoryAdded
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • MessageDisplay
  • Notification
  • PostCompact
  • PostModelSwitch
  • PreCompact
  • PreModelSwitch
  • SessionEnd
  • StopFailure
  • SubagentStart
  • WorktreeCreate
  • WorktreeRemove
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:
  1. Отправляют входные данные хука и ваш промпт модели Claude, по умолчанию той, которую Claude Code использует для фоновой функциональности
  2. LLM отвечает структурированным JSON, содержащим решение
  3. 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 hook
  • PostToolUse: по умолчанию ход заканчивается и причина появляется в чате как строка предупреждения. Установите continueOnBlock: true для передачи причины обратно Claude и продолжения хода вместо этого
  • PostToolBatch, UserPromptSubmit и UserPromptExpansion: ход заканчивается и причина появляется как строка предупреждения. Эти события заканчивают ход на decision: "block" независимо от continue
  • PostToolUseFailure и TaskCreated: причина возвращается Claude как ошибка инструмента и ход продолжается, независимо от continueOnBlock
  • TaskCompleted: когда он срабатывает, потому что задача отмечена как завершённая во время хода, причина возвращается 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
Если вам нужен более точный контроль над любым событием, используйте command hook с полями для каждого события, описанными в Decision control.

Check multiple conditions before stopping

Этот hook Stop использует подробную подсказку для проверки трёх условий перед разрешением Claude остановиться. Hooks SubagentStop используют тот же формат для оценки, должен ли subagent остановиться. Если модель возвращает "ok": false, потому что условие ещё не выполнено, Claude продолжает работать с предоставленной причиной как своей следующей инструкцией:

Agent-based hooks

Agent hooks являются экспериментальными. Поведение и конфигурация могут измениться в будущих выпусках. Для рабочих процессов в производстве предпочитайте command 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:
  1. Claude Code порождает subagent с вашей подсказкой и JSON входом hook
  2. Subagent может использовать инструменты, такие как Read, Grep и Glob, для исследования
  3. После до 50 оборотов subagent возвращает структурированное решение { "ok": true/false }
  4. Claude Code разрешает действие, если ok имеет значение true. Если ok имеет значение false, Claude Code обрабатывает блокировку так же, как prompt hook с continueOnBlock: true на этом событии, как указано в разделе Response schema
Agent hooks полезны, когда проверка требует проверки фактических файлов или выхода тестов, а не только оценки данных входа hook.

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 выполняется. Когда скрипт завершается, его выход доставляется на следующий ход разговора:
Как только асинхронный hook запущен в фоне, Claude Code не применяет timeout к нему. Claude Code по-прежнему применяет timeout к hook, который вы запускаете с asyncRewake. Claude Code доставляет результаты асинхронного hook только во время работы сеанса:
  • В неинтерактивном режиме с флагом -p Claude 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.
Claude Code проверяет, что JSON-ответ соответствует той же схеме выходных данных, что и у синхронных хуков, и отбрасывает любое поле, значение которого имеет неправильный тип, например 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 добавлял один и тот же текст в диалог заново при каждом возобновлении.

Соображения безопасности

Отказ от ответственности

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

Доверие рабочей области

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.