SpyBara
Go Premium

hooks.md 2026-10-02 22:59 UTC to 2026-10-03 09:58 UTC

This page contains 618 additions and 607 deletions.

2026
Thu 1 23:59 Fri 2 22:59 Sat 3 11:02

Справочник по hooks

Справочник по событиям hook Claude Code, схеме конфигурации, форматам JSON входа/выхода, кодам выхода, асинхронным hooks, HTTP hooks, prompt hooks и MCP tool 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 как событие только для отображения, которое запускается во время потоковой передачи текста сообщения помощника
<img src="https://mintcdn.com/claude-code/x7pO8l4XcvAXCoVc/images/hooks-lifecycle-dark.svg?fit=max&auto=format&n=x7pO8l4XcvAXCoVc&q=85&s=c9b3d88487335f58cce0b52e2f9e7531" className="hidden dark:block" alt="Диаграмма жизненного цикла 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 как событие только для отображения, которое запускается во время потоковой передачи текста сообщения помощника" width="520" height="1336" data-path="images/hooks-lifecycle-dark.svg" />

Таблица ниже суммирует, когда срабатывает каждое событие. Раздел Hook events документирует полную схему входа и параметры управления решением для каждого события.

Событие Когда оно срабатывает
SessionStart Когда сеанс начинается или возобновляется
Setup Когда вы запускаете Claude Code с --init-only, или с --init или --maintenance в режиме -p. Для одноразовой подготовки в CI или скриптах
UserPromptSubmit Когда отправляется промпт, прежде чем Claude его обработает. Также срабатывает для ходов, которые Claude Code начинает самостоятельно
UserPromptExpansion Когда команда, введённая пользователем, расширяется в запрос, прежде чем она достигнет Claude. Может заблокировать расширение
PreToolUse Перед выполнением вызова инструмента. Может заблокировать его
PermissionRequest Когда вызов инструмента требует решения о разрешении
PermissionDenied Когда автоматический режим отклоняет вызов инструмента, включая отклонения без вердикта классификатора. Используйте JSON hookSpecificOutput.retry: true, чтобы сообщить модели, что она может повторить попытку отклонённого вызова инструмента. Claude Code игнорирует retry, когда классификатор не выдал вердикт
PostToolUse После успешного выполнения вызова инструмента
PostToolUseFailure После неудачного выполнения вызова инструмента
PostToolBatch После разрешения полного пакета параллельных вызовов инструментов, перед следующим вызовом модели
Notification Когда Claude Code отправляет уведомление
MessageDisplay Во время отображения текста сообщения помощника
SubagentStart Когда порождается подагент
SubagentStop Когда подагент завершает работу
TaskCreated Когда задача создаётся через TaskCreate
TaskCompleted Когда задача отмечается как завершённая
Stop Когда Claude завершает ответ
StopFailure Когда ход завершается из-за ошибки API
TeammateIdle Когда товарищ по команде команды агентов собирается перейти в режим ожидания
InstructionsLoaded Когда файл CLAUDE.md или .claude/rules/*.md загружается в контекст. Срабатывает при запуске сеанса и когда файлы ленивой загрузки загружаются во время сеанса
ConfigChange Когда файл конфигурации изменяется во время сеанса
CwdChanged Когда рабочий каталог изменяется, например когда Claude выполняет команду cd. Полезно для реактивного управления окружением с помощью инструментов, таких как direnv
DirectoryAdded Когда рабочий каталог добавляется в середине сеанса через /add-dir или запрос управления SDK register_repo_root
FileChanged Когда наблюдаемый файл изменяется на диске. Поле matcher указывает, какие имена файлов отслеживать
WorktreeCreate Когда worktree создаётся через --worktree, isolation: "worktree", или для фонового сеанса. Заменяет поведение git по умолчанию
WorktreeRemove Когда worktree удаляется при выходе из сеанса, когда подагент завершает работу, или когда вы удаляете фоновый сеанс
PreCompact Перед компактизацией контекста
PostCompact После завершения компактизации контекста
PreModelSwitch Перед тем как Claude Code применяет переключение модели, которое вы или клиент запросили. Может заблокировать переключение
PostModelSwitch После изменения модели сеанса, включая изменения, которые Claude Code делает самостоятельно, такие как восстановление модели при возобновлении сеанса
Elicitation Когда сервер MCP запрашивает ввод пользователя во время вызова инструмента
ElicitationResult После того как пользователь отвечает на запрос MCP, перед отправкой ответа обратно на сервер
SessionEnd Когда сеанс завершается

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

Чтобы увидеть, как событие, фильтр и обработчик работают вместе, рассмотрим этот hook PreToolUse, который блокирует деструктивные команды оболочки.

Фильтр matcher сужает область до вызовов инструмента Bash, а условие if сужает её дальше до команд Bash, совпадающих с rm *, поэтому block-rm.sh запускается только когда оба фильтра совпадают:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}

Скрипт читает JSON входные данные из stdin, извлекает команду и возвращает permissionDecision со значением "deny", если она содержит rm -rf. Сохраните его в .claude/hooks/block-rm.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x .claude/hooks/block-rm.sh, чтобы Claude Code мог его запустить:

#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0  # no decision; normal permission flow applies
fi

Этот скрипт, как и другие примеры 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:

{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
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:

{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}

Если бы команда была более безопасным вариантом rm, таким как rm file.txt, скрипт выполнил бы exit 0 вместо этого. Код выхода 0 без вывода означает, что hook не имеет решения для отчёта, поэтому вызов инструмента продолжается через нормальный поток разрешений. Hook может отклонить вызов, но молчание не одобряет его.

5

Claude Code действует на основе результата

Claude Code читает JSON решение, блокирует вызов инструмента и показывает Claude причину.

Раздел Configuration ниже документирует полную схему, и каждый раздел hook event документирует, какой входной JSON получает ваша команда и какой выход она может вернуть.

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

Hooks определяются в JSON файлах настроек. Конфигурация имеет три уровня вложенности:

  1. Выберите hook event для ответа, например PreToolUse или Stop
  2. Добавьте matcher group для фильтрации срабатывания, например "только для инструмента Bash"
  3. Определите один или несколько hook handlers для запуска при совпадении

См. Как разрешается hook выше для полного пошагового руководства с аннотированным примером.

Расположение hook

Место, где вы определяете hook, определяет его область действия:

Расположение Область действия Общий доступ
~/.claude/settings.json Все ваши проекты Нет, локально на вашей машине
.claude/settings.json Один проект Да, можно зафиксировать в репозитории
.claude/settings.local.json Один проект Нет, игнорируется git когда Claude Code сохраняет параметр в него
Управляемые параметры политики Организация Да, контролируется администратором
Plugin hooks/hooks.json Когда плагин включен Да, поставляется с плагином
Skill frontmatter Остаток сеанса после вызова skill. См. Hooks in skills and agents Да, определено в файле skill
Subagent frontmatter Пока этот subagent работает Да, определено в файле subagent

Облачные сеансы на Claude Code в веб-версии не читают ваш локальный ~/.claude/settings.json. В самостоятельно размещённой среде, Claude Code также запускает hooks, которые оператор инициализировал из ~/.claude/ хоста runner, и запускает hooks в файле управляемых параметров образа runner, когда этот файл находится среди управляемых источников, которые применяет Claude Code, что по умолчанию означает только когда ни управляемые на сервере параметры, ни доставленная MDM политика Claude Code не предоставляют управляемый уровень. См. что переносится из вашей установки для того, какие файлы настроек и плагины, и таким образом какие hooks, достигают облачного сеанса.

Для получения подробной информации о разрешении файлов настроек см. settings.

Hooks из файлов настроек, управляемых параметров политики и плагинов также запускаются внутри subagents. Когда subagent вызывает инструмент, события инструмента, такие как PreToolUse и PostToolUse, запускают те же настроенные hooks, что и в основном разговоре, и входные данные содержат поля agent_id и agent_type общих входных полей, которые идентифицируют subagent.

Администраторы могут использовать allowManagedHooksOnly в управляемых параметрах для ограничения того, какие hooks запускаются:

  • Ваши пользовательские, проектные, локальные и плагинные hooks блокируются. Hooks из плагинов, принудительно включённых в управляемых параметрах enabledPlugins, исключены
  • Claude Code также сужает ваши параметры statusLine, fileSuggestion и subagentStatusLine до управляемых параметров
  • Claude Code также отключает плагины с источником command, включая плагины, принудительно включённые в управляемых параметрах enabledPlugins, если только disableCommandPluginSources явно не установлен на false. Источники command требуют Claude Code v2.1.229 или позже
  • Claude Code также блокирует команды marketplace headersHelper если только disableCommandPluginSources явно не установлен на false, за исключением marketplace, которые сами управляемые параметры объявляют

См. что запускается под allowManagedHooksOnly.

Hook записи объединяются на уровнях параметров, а не заменяют друг друга: пользовательские, проектные и локальные параметры добавляют свои собственные hooks без удаления управляемых, и параметр disableAllHooks не может отключить управляемые hooks извне управляемых параметров.

HTTP hook allowlists применяются к hooks из каждого источника, включая управляемые параметры политики:

  • allowedHttpHookUrls: когда определено на любом уровне параметров, Claude Code запускает обработчик HTTP hook только если его URL совпадает с объединённым allowlist
  • httpHookAllowedEnvVars: когда определено, Claude Code интерполирует только переменные окружения из этого списка в заголовки hook

Matcher patterns

Поле matcher фильтрует срабатывание hooks. Способ оценки фильтра зависит от содержащихся в нём символов:

Значение фильтра Оценивается как Пример
"*", "" или опущено Совпадение со всеми срабатывает при каждом возникновении события
Только буквы, цифры, _, -, пробелы, , и | Точная строка или список точных строк, разделённых | или , с опциональным окружающим пробелом Bash совпадает только с инструментом Bash; Edit|Write и Edit, Write каждый совпадает с любым инструментом точно; code-reviewer совпадает только с этим типом агента
Содержит любой другой символ Регулярное выражение JavaScript, без привязки ^Notebook совпадает с любым инструментом, начинающимся с Notebook; mcp__memory__.* совпадает с каждым инструментом с сервера memory

Фильтр на пути регулярного выражения проверяется с помощью RegExp.prototype.test JavaScript, который успешно совпадает в любом месте значения. Edit.* совпадает как с Edit, так и с NotebookEdit; оберните шаблон в ^ и $, как в ^Edit$, когда вам нужно совпадение всей строки.

FileChanged и StopFailure используют более узкий набор точного совпадения только букв, цифр, _ и |. Дефис, пробел или запятая в фильтре для этих двух событий держит его на пути регулярного выражения, и только | разделяет альтернативы. Каждое другое событие с поддержкой фильтра в таблице ниже принимает | или ,.

Событие FileChanged не следует этим правилам при построении своего списка наблюдения. См. FileChanged.

Каждый тип события совпадает с другим полем:

Событие На что фильтр влияет Примеры значений фильтра
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied имя инструмента Bash, Edit|Write, mcp__.*
SessionStart как сеанс начался startup, resume, clear, compact, fork
Setup какой флаг CLI запустил setup init, maintenance
SessionEnd почему сеанс закончился clear, resume, logout, prompt_input_exit, other
Notification тип уведомления permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_url_dialog, elicitation_complete, elicitation_response, agent_needs_input, agent_completed, quota_auto_resume_fired, quota_auto_resume_stale, quota_auto_resume_disabled
SubagentStart тип агента general-purpose, Explore, Plan, пользовательские имена агентов или имена с областью плагина, такие как ^my-plugin:reviewer$
PreCompact, PostCompact что вызвало компактирование manual, auto
PreModelSwitch, PostModelSwitch каноническое имя модели, на которую переключается сеанс, как описано в PreModelSwitch claude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.*
SubagentStop тип агента те же значения, что и SubagentStart
ConfigChange источник конфигурации user_settings, project_settings, local_settings, policy_settings, skills
CwdChanged поддержка фильтра отсутствует всегда срабатывает при каждом возникновении
DirectoryAdded как был добавлен каталог slash_command, register_repo_root
FileChanged буквальные имена файлов для наблюдения (см. FileChanged) .envrc|.env
StopFailure тип ошибки rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error, unknown
InstructionsLoaded причина загрузки session_start, nested_traversal, path_glob_match, include, compact
UserPromptExpansion имя команды ваши имена skill или команд
Elicitation имя MCP сервера ваши настроенные имена MCP серверов
ElicitationResult имя MCP сервера те же значения, что и Elicitation
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay поддержка фильтра отсутствует всегда срабатывает при каждом вхождении

Совпадение StopFailure на cloud_credential_error требует Claude Code v2.1.267 или позже, первой версии, которая сообщает об ошибках загрузки учётных данных под этим значением вместо server_error или unknown.

Для большинства событий Claude Code оценивает фильтр против поля из JSON входа, который он отправляет вашему hook на stdin. Для событий инструмента это поле — tool_name. Для PreModelSwitch и PostModelSwitch, Claude Code оценивает фильтр против канонического имени, которое он выводит из to_model, как описано в PreModelSwitch. Каждый раздел hook event перечисляет полный набор значений фильтра и схему входа для этого события.

Этот пример запускает скрипт линтинга только когда Claude пишет или редактирует файл:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}

Если вы добавите поле matcher к событию без поддержки фильтра, оно будет молча проигнорировано.

Для событий инструмента вы можете фильтровать более узко, установив поле if на отдельных обработчиках hook. if использует синтаксис правила разрешения для совпадения с именем инструмента и аргументами вместе, поэтому "Bash(git *)" запускается когда любая подкоманда входа Bash совпадает с git * и "Edit(*.ts)" запускается только для файлов TypeScript.

Match MCP tools

MCP server инструменты отображаются как обычные инструменты в событиях инструментов (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), поэтому вы можете совпадать с ними так же, как с любым другим именем инструмента.

MCP инструменты следуют шаблону именования mcp__<server>__<tool>, например:

  • mcp__memory__create_entities: инструмент create entities сервера Memory
  • mcp__filesystem__read_file: инструмент read file сервера Filesystem
  • mcp__github__search_repositories: инструмент поиска сервера GitHub

Чтобы совпадать с каждым инструментом с сервера, добавьте .* к префиксу сервера. .* требуется: фильтр, такой как mcp__memory или mcp__brave-search, содержит только символы точного совпадения, поэтому он сравнивается как точная строка и не совпадает ни с одним инструментом.

  • mcp__memory__.* совпадает со всеми инструментами сервера memory
  • mcp__brave-search__.* совпадает со всеми инструментами с сервера, чьё имя содержит дефис
  • mcp__.*__write.* совпадает с любым инструментом, чьё имя начинается с write из любого сервера

Инструменты из plugin-bundled MCP server используют сегмент сервера с областью, который включает имя плагина: mcp__plugin_<plugin-name>_<server-name>__<tool>. Фильтр, написанный против голого ключа сервера, никогда не срабатывает для этих инструментов. Для плагина с именем my-plugin, который объединяет сервер под ключом db, инструмент query отображается как mcp__plugin_my-plugin_db__query, поэтому фильтр для каждого инструмента с этого сервера — mcp__plugin_my-plugin_db__.*. Используйте то же имя инструмента с областью в поле if обработчика. См. Plugin-provided MCP servers для того, как строится имя с областью.

Этот пример логирует все операции сервера memory и проверяет операции записи из любого MCP сервера:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Hook handler fields

Каждый объект во внутреннем массиве hooks — это hook handler: команда оболочки, конечная точка HTTP, инструмент MCP, подсказка LLM или агент, который запускается при совпадении фильтра. Есть пять типов:

  • Command hooks (type: "command"): запускают команду оболочки. Ваш скрипт получает JSON входные данные события на stdin и передаёт результаты обратно через коды выхода и stdout.
  • HTTP hooks (type: "http"): отправляют JSON входные данные события как HTTP POST запрос на URL. Конечная точка передаёт результаты обратно через тело ответа, используя тот же JSON формат выхода, что и command hooks.
  • MCP tool hooks (type: "mcp_tool"): вызывают инструмент на уже подключённом MCP сервере. Текстовый вывод инструмента обрабатывается как stdout command hook.
  • Prompt hooks (type: "prompt"): отправляют подсказку модели Claude для однооборотной оценки. Модель возвращает решение как JSON. См. Prompt-based hooks.
  • Agent hooks (type: "agent"): порождают subagent, который может использовать инструменты, такие как Read, Grep и Glob, для проверки условий перед возвратом решения. Agent hooks являются экспериментальными и могут измениться. См. Agent-based hooks.

Все совпадающие hooks запускаются параллельно. Если вы определите один и тот же обработчик в более чем одном файле настроек, он запускается один раз. Копия плагина или skill одного и того же обработчика остаётся отдельной.

Обработчики запускаются в текущем каталоге с окружением Claude Code. Если текущий каталог больше не существует, например worktree или временный каталог, который другая оболочка удалила в середине сеанса, Claude Code запускает command hooks из первого из них, который всё ещё существует: каталог, в котором сеанс начался, корень проекта, ваш домашний каталог или системный временный каталог. Claude Code записывает предупреждение, называющее резервный каталог, в debug log.

Переменная окружения $CLAUDE_CODE_REMOTE устанавливается на "true" в удалённых веб-окружениях и не устанавливается в локальном CLI. Claude Code v2.1.199 и позже устанавливает $CLAUDE_CODE_BRIDGE_SESSION_ID на ID сеанса Remote Control пока локальный сеанс имеет активное соединение Remote Control.

Common fields

Эти поля применяются ко всем типам hooks:

Поле Обязательно Описание
type да "command", "http", "mcp_tool", "prompt" или "agent"
if нет Синтаксис правила разрешения для фильтрации срабатывания этого hook, такой как "Bash(git *)" или "Edit(*.ts)". Hook запускается только если вызов инструмента совпадает с шаблоном. См. таблицу Bash matching table ниже для того, как Bash шаблоны оцениваются против подкоманд, $() и обратных кавычек. Оценивается только на событиях инструмента: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest и PermissionDenied. На других событиях hook с установленным if никогда не запускается. Использует тот же синтаксис, что и правила разрешения
timeout нет Секунды перед отменой. Claude Code не применяет его на command hook, который вы запускаете с async: true. Значения по умолчанию: 600 для command, http и mcp_tool; 30 для prompt; 60 для agent. Claude Code снижает значение по умолчанию для command, http и mcp_tool до 30 на UserPromptSubmit, PreModelSwitch и PostModelSwitch, и до 10 на MessageDisplay. Hooks SessionEnd делят бюджет 1.5 секунды; если ваши параметры устанавливают более длительный timeout для каждого hook, Claude Code повышает бюджет, чтобы совпадать, до 60 секунд
statusMessage нет Пользовательское сообщение спиннера, отображаемое во время выполнения hook
once нет Если true, Claude Code удаляет hook после его первого успешного запуска. Запуск, который не удаётся, блокирует с кодом выхода 2 или истекает по времени, оставляет hook на месте, поэтому он запускается снова при следующем совпадающем событии. Только для hooks, объявленных в skill frontmatter; игнорируется в файлах настроек и agent frontmatter

Поле if содержит ровно одно правило разрешения. Нет синтаксиса &&, || или списка для объединения правил; чтобы применить несколько условий, определите отдельный обработчик hook для каждого.

В условии if для инструмента файла, шаблон каталога с одним сегментом, такой как "Edit(src/**)", совпадает только с каталогом src в рабочем каталоге и файлами под ним. Чтобы совпадать с каталогом с именем src на любой глубине, напишите "Edit(**/src/**)". До v2.1.214, "Edit(src/**)" совпадал с каталогом с именем src на любой глубине под рабочим каталогом.

Для Bash шаблонов, запускается ли ваша команда hook зависит от формы шаблона и команды Bash, которую вызывает Claude. Ведущие присваивания VAR=value удаляются перед совпадением.

if шаблон Bash команда Hook запускается? Почему
Bash(git *) FOO=bar git push да ведущие присваивания удаляются; git push совпадает
Bash(git *) npm test && git push да каждая подкоманда проверяется; git push совпадает
Bash(rm *) echo $(rm -rf /) да команды внутри $() и обратных кавычек проверяются; rm -rf / совпадает
Bash(rm *) echo $(date) нет ни одна подкоманда не совпадает с rm *
Bash(git push *) echo $(date) да шаблоны, которые указывают больше чем имя команды, запускают hook в любом случае на $(), обратных кавычках или $VAR

Когда Claude Code не может определить, какие команды запускает входные данные Bash, он запускает ваш hook независимо от шаблона. Поскольку фильтр if является лучшим усилием, используйте систему разрешений вместо hook для обеспечения жёсткого разрешения или отказа.

Command hook fields

В дополнение к общим полям, command hooks принимают эти поля:

Поле Обязательно Описание
command да Команда оболочки для выполнения. С args, исполняемый файл для прямого запуска. См. Exec form and shell form
args нет Список аргументов. Когда присутствует, command разрешается как исполняемый файл и запускается напрямую с args как вектор аргументов, без участия оболочки. См. Exec form and shell form
async нет Если true, запускается в фоне без блокировки. См. Run hooks in the background
asyncRewake нет Если true, запускается в фоне и пробуждает Claude при коде выхода 2. Hook stderr или stdout, если stderr пусто, показывается Claude как системное напоминание чтобы он мог реагировать на долгоживущий фоновый сбой
shell нет Оболочка для использования для этого hook. Принимает "bash" или "powershell". По умолчанию "bash", или "powershell" на Windows когда Git Bash не установлен. Установка "powershell" запускает команду через PowerShell на Windows. Не требует CLAUDE_CODE_USE_POWERSHELL_TOOL, так как hooks порождают PowerShell напрямую. Игнорируется когда установлен args
Exec form and shell form

Command hook запускается в exec form когда установлен args, и в shell form когда args опущен. Установите args всякий раз, когда hook ссылается на path placeholder, так как каждый элемент передаётся как один аргумент без кавычек. Опустите args когда вам нужны функции оболочки, такие как pipes или &&, или когда ни одна из этих проблем не применяется.

Exec form запускается когда присутствует args. Claude Code разрешает command как исполняемый файл на PATH и запускает его напрямую с args как вектор аргументов. Нет оболочки, поэтому каждый элемент args — это ровно один аргумент, написанный как есть, и path placeholders, такие как ${CLAUDE_PLUGIN_ROOT}, подставляются в command и в каждый элемент args как простые строки. Специальные символы, такие как апострофы, $ и обратные кавычки, проходят дословно, потому что нет оболочки для их интерпретации. На любой платформе не происходит никакой токенизации оболочки.

Shell form запускается когда args отсутствует. Строка command передаётся в оболочку: sh -c на macOS и Linux, Git Bash на Windows, или PowerShell когда Git Bash не установлен. Установите поле shell для явного выбора. Оболочка токенизирует строку, расширяет переменные и интерпретирует pipes, &&, redirects и globs.

Этот пример запускает Node скрипт, поставляемый с плагином. Exec form передаёт разрешённый путь скрипта как один аргумент без кавычек:

{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]
}

Эквивалентная shell form нуждается в кавычках для обработки путей с пробелами или специальными символами:

{
  "type": "command",
  "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"
}

Обе формы поддерживают одни и те же path placeholders, и обе экспортируют их как переменные окружения CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT и CLAUDE_PLUGIN_DATA на порождённом процессе, поэтому скрипт может читать process.env.CLAUDE_PLUGIN_ROOT независимо от того, как он был запущен.

Plugin hooks дополнительно подставляют значения ${user_config.*}, только в exec form: значение подставляется в command и в каждый элемент args как простая строка, поэтому оболочка не переанализирует его.

Shell-form plugin hook, чей command ссылается на ${user_config.*}, завершается с ошибкой вместо запуска. Чтобы использовать значение опции из shell-form hook, прочитайте переменную окружения $CLAUDE_PLUGIN_OPTION_<KEY>, такую как $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL для опции webhook_url, или установите args для переключения hook на exec form. До v2.1.207, shell-form plugin hook команды также подставляли ${user_config.*}.

HTTP hook fields

В дополнение к общим полям, HTTP hooks принимают эти поля:

Поле Обязательно Описание
url да URL для отправки POST запроса
headers нет Дополнительные HTTP заголовки как пары ключ-значение. Значения поддерживают интерполяцию переменных окружения с использованием синтаксиса $VAR_NAME или ${VAR_NAME}. Разрешены только переменные, указанные в allowedEnvVars
allowedEnvVars нет Список имён переменных окружения, которые могут быть интерполированы в значения заголовков. Ссылки на неуказанные переменные заменяются пустыми строками. Требуется для любой интерполяции переменных окружения

Claude Code отправляет JSON входные данные hook как тело POST запроса с Content-Type: application/json. Тело ответа использует тот же JSON формат выхода, что и command hooks.

Обработка ошибок отличается от command hooks; см. HTTP response handling.

Этот пример отправляет события PreToolUse на локальный сервис валидации, аутентифицируясь с токеном из переменной окружения MY_TOKEN:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/pre-tool-use",
            "timeout": 30,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

MCP tool hook fields

В дополнение к общим полям, MCP tool hooks принимают эти поля:

Поле Обязательно Описание
server да Имя настроенного MCP сервера. Для plugin-bundled server, это имя с областью plugin:<plugin-name>:<server-name>, такое как plugin:my-plugin:db, не голый ключ сервера
tool да Имя инструмента для вызова на этом сервере
input нет Аргументы, передаваемые инструменту. Строковые значения поддерживают подстановку ${path} из JSON входа hook, такую как "${tool_input.file_path}"

Этот пример вызывает инструмент security_scan на MCP сервере my_server после каждого Write или Edit, передавая путь отредактированного файла:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "my_server",
            "tool": "security_scan",
            "input": { "file_path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}
How the tool's result is read

Claude Code читает текстовое содержимое инструмента так же, как читает command-hook stdout, следуя правилу разбора под кодом выхода 0. Если инструмент возвращает isError: true, hook производит неблокирующую ошибку и выполнение продолжается.

When the server is still connecting

На событиях, где hook может блокировать или изменять результат, такие как PreToolUse или Stop, Claude Code ждёт подключения сервера перед вызовом инструмента, максимум MCP_TIMEOUT и в пределах собственного timeout hook. На наблюдательных событиях, таких как Notification или SessionEnd, он не ждёт.

Сервер, показывающий статус cached, подключается, когда hook вызывает его инструмент. Если сервер не подключён в этот момент, hook производит неблокирующую ошибку и выполнение продолжается. Hook никогда не запускает поток OAuth, поэтому аутентифицируйте сервер из /mcp сначала.

Events that fire before MCP servers are available

SessionStart при запуске, включая с --continue или --resume, и каждое событие Setup срабатывают перед доступностью MCP серверов сеанса для hooks. Claude Code пропускает их mcp_tool hooks без вызова инструмента, и debug log записывает mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), или то же сообщение, называющее Setup. Когда SessionStart срабатывает снова позже в сеансе, после /clear или компактирования, его mcp_tool hooks запускаются. Для всего, что сеансу нужно при запуске, используйте type: "command" hook на SessionStart вместо этого.

Prompt and agent hook fields

В дополнение к общим полям, prompt и agent hooks принимают эти поля:

Поле Обязательно Описание
prompt да Текст подсказки для отправки модели. Используйте $ARGUMENTS как заполнитель для JSON входа hook. Экранируйте обратной косой чертой для включения буквального текста: \$1.00 отображается как $1.00
model нет Модель для использования при оценке. По умолчанию модель, которую Claude Code использует для фоновой функциональности

Reference scripts by path

Используйте эти заполнители для ссылки на скрипты hook относительно корня проекта или плагина, независимо от рабочего каталога при запуске hook:

  • ${CLAUDE_PROJECT_DIR}: корень проекта, где сеанс начался. Claude Code также устанавливает эту переменную в окружении stdio MCP серверов и plugin LSP серверов.
  • ${CLAUDE_PLUGIN_ROOT}: каталог установки плагина, для скриптов, поставляемых с плагином. См. plugin environment variables для того, как путь ведёт себя при обновлениях.
  • ${CLAUDE_PLUGIN_DATA}: каталог постоянных данных плагина, для зависимостей и состояния, которые должны пережить обновления плагина.

Предпочитайте exec form для любого hook, который ссылается на path placeholder. В shell form оберните каждый заполнитель в двойные кавычки.

Этот пример использует ${CLAUDE_PROJECT_DIR} для запуска проверки стиля из каталога .claude/hooks/ проекта после любого вызова инструмента Write или Edit:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",
"args": []
}
]
}
]
}
}

Hooks in skills and agents

В дополнение к файлам настроек и плагинам, hooks могут быть определены непосредственно в skills и subagents с использованием frontmatter, в том же формате конфигурации, что и hooks на основе настроек. Как долго Claude Code их регистрирует, зависит от компонента:

  • Subagent hooks: Claude Code запускает их только пока этот subagent работает и удаляет их, когда он завершается. Claude Code преобразует hook Stop здесь в SubagentStop, событие, которое срабатывает при завершении subagent.
  • Skill hooks: Claude Code регистрирует их, когда вы или Claude вызываете skill, и продолжает запускать их для остатка сеанса, на ходах после собственного хода skill. Чтобы Claude Code удалил hook после его первого успешного запуска вместо этого, установите once: true на нём.

Этот skill определяет hook PreToolUse, который запускает скрипт проверки безопасности перед каждой командой Bash:

---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

Subagents используют тот же формат в своём YAML frontmatter.

Frontmatter hooks в project skill следуют тому же правилу доверия рабочей области, что и hooks в файлах настроек. Claude Code регистрирует их, когда вы или Claude вызываете skill, включая в запуск -p в папке, которую вы не доверяли.

Frontmatter hooks в project subagent запускаются только после того, как вы примете диалог доверия рабочей области для папки, из которой пришёл файл агента. Сеанс -p не считается принятием. Что запускается перед тем, как вы доверяете папке сравнивает это с правилом файла настроек, и страница subagents перечисляет какие области исключены. До v2.1.218, эти hooks могли запускаться из папок, которым вы не доверяли.

Меню `/hooks`

Введите /hooks в Claude Code, чтобы открыть браузер настроенных хуков, доступный только для чтения. В списке для каждого хука указано, откуда он взят: пользовательские настройки, настройки проекта, локальные настройки, плагин или текущая сессия.

Выберите хук, чтобы увидеть полный текст того, что он запускает, и где он определён, например путь к его файлу настроек или имя его плагина.

Чтобы просмотреть все события хуков, включая те, для которых хуки не настроены, выберите All events в конце списка.

Отключение или удаление hooks

Чтобы удалить хук, определённый в файле настроек, удалите его запись из этого файла.

Чтобы временно отключить все hooks без их удаления, установите "disableAllHooks": true в файле настроек. Claude Code читает значение, оставшееся после применения приоритета параметров, поэтому "disableAllHooks": false в .claude/settings.json проекта переопределяет true в ваших пользовательских параметрах. Чтобы отключить hooks для одного запуска, независимо от того, что говорят параметры проекта, передайте --settings '{"disableAllHooks": true}', что имеет приоритет над параметрами проекта и локальными параметрами. Нет способа отключить отдельный hook, сохраняя его в конфигурации.

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

Прямые редактирования hooks в файлах настроек обычно захватываются автоматически наблюдателем файлов.

Входные и выходные данные Hook

Hooks команд получают JSON-данные через stdin и передают результаты через коды выхода, stdout и stderr. HTTP hooks получают тот же JSON, что и тело POST-запроса, и передают результаты через тело HTTP-ответа. В этом разделе рассматриваются поля и поведение, общие для всех событий. Каждый раздел события в разделе Hook events включает его конкретную схему входных данных и параметры управления решением.

На macOS и Linux hooks команд запускаются в собственном сеансе без управляющего терминала. Процесс hook и любые дочерние процессы не могут открыть /dev/tty или отправлять последовательности escape непосредственно в интерфейс Claude Code. Windows не имеет /dev/tty.

Чтобы вывести сообщение пользователю на любой платформе, верните systemMessage в JSON-выводе. Некоторые события игнорируют его или доставляют его в другое место, и в каждом разделе события указано, как это происходит. Чтобы вызвать уведомление рабочего стола, установить заголовок окна или издать звуковой сигнал, верните terminalSequence вместо этого.

Общие входные поля

Hook события получают эти поля в виде JSON в дополнение к полям, специфичным для события, задокументированным в каждом разделе hook event. Для hooks команд этот JSON поступает через stdin. Для HTTP hooks он поступает как тело POST-запроса.

Поле Описание
session_id Текущий идентификатор сеанса
prompt_id UUID, идентифицирующий пользовательский запрос, который в настоящее время обрабатывается. Совпадает с атрибутом prompt.id на событиях OpenTelemetry, поэтому вы можете коррелировать выход hook с телеметрией для одного запроса. Отсутствует до первого ввода пользователя. Требуется Claude Code v2.1.196 или позже
transcript_path Путь к файлу JSON разговора. Файл транскрипта записывается асинхронно и может отставать от разговора в памяти, поэтому он может еще не включать самые последние сообщения текущего хода, когда срабатывает hook. Hooks, которым нужен финальный текст ассистента текущего хода, должны использовать last_assistant_message на Stop и SubagentStop вместо чтения транскрипта
cwd Текущий рабочий каталог при вызове hook
scratchpad_dir Путь к каталогу scratchpad сеанса, где Claude хранит временные рабочие файлы. Отсутствует, когда сеанс не имеет scratchpad или временный каталог недоступен. Требуется Claude Code v2.1.257 или позже
permission_mode Текущий режим разрешений: "default", "plan", "acceptEdits", "auto", "dontAsk" или "bypassPermissions". Режим, обозначенный как Manual, поступает как "default", никогда не как "manual", поэтому скрипты, которые совпадают с "default", продолжают работать. Не все события получают это поле. Проверьте пример JSON в каждом разделе hook event
effort Объект с полем level, содержащим уровень усилий, действующий при запуске hook: "low", "medium", "high", "xhigh" или "max". Если вы установите уровень, который активная модель не поддерживает, level сообщает уровень, который вместо этого запустил Claude Code; Adjust effort level говорит, как он выбирает этот уровень. Объект совпадает с полем effort строки состояния. Присутствует для событий, которые срабатывают в контексте использования инструмента, таких как PreToolUse, PostToolUse, Stop и SubagentStop, когда текущая модель поддерживает параметр усилий. Уровень также доступен для команд hook и инструмента Bash как переменная окружения $CLAUDE_EFFORT.
hook_event_name Имя события, которое сработало

При запуске с --agent или внутри subagent включаются два дополнительных поля:

Поле Описание
agent_id Уникальный идентификатор для subagent. Присутствует только когда hook срабатывает внутри вызова subagent. Используйте это для различения вызовов hook subagent от вызовов основного потока.
agent_type Имя агента (например, "Explore" или "security-reviewer"). Присутствует, когда сеанс использует --agent или hook срабатывает внутри subagent. Для subagents тип subagent имеет приоритет над значением --agent сеанса. См. SubagentStart для значений, которые сообщают пользовательские и plugin subagents, и как написать matcher для имени с областью plugin.

Только hooks SessionStart могут получить поле model, и Claude Code не всегда его включает. Hooks PreModelSwitch и PostModelSwitch получают from_model и to_model вместо этого, поэтому используйте hook PostModelSwitch для отслеживания модели по мере ее изменения во время сеанса.

Нет переменной окружения $CLAUDE_MODEL. Hook может читать $ANTHROPIC_MODEL, если вы установили ее в своей оболочке, но это значение не изменяется при переключении моделей с помощью /model во время сеанса.

Процесс hook наследует родительское окружение, за исключением переменных экспортера OTEL_*, которые Claude Code удаляет из каждого подпроцесса, который он порождает, и, когда установлена CLAUDE_CODE_SUBPROCESS_ENV_SCRUB в 1, переменные, которые он удаляет.

Например, hook PreToolUse для команды Bash получает это на stdin:

{
  "session_id": "abc123",
  "prompt_id": "550e8400-e29b-41d4-a716-446655440000",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "scratchpad_dir": "/tmp/claude-1000/-home-user-my-project/abc123/scratchpad",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite",
    "timeout": 120000,
    "run_in_background": false
  },
  "tool_use_id": "toolu_01ABC123..."
}

Поля tool_name, tool_input и tool_use_id специфичны для события. Каждый раздел hook event документирует дополнительные поля для этого события.

Выходные коды выхода

Код выхода из вашей команды hook сообщает Claude Code, должно ли действие продолжаться, быть заблокировано или игнорироваться. Код выхода не действует в одиночку. Claude Code читает поля JSON output из stdout при каждом коде выхода, не только 0, и для событий, которые используют стандартную модель решения, проанализированный объект, который проходит валидацию схемы, вступает в силу наряду с кодом. Блокировка Exit 2 — это единственный результат, который JSON не может переопределить.

Две таблицы владеют исключениями для каждого события: Exit code 2 behavior per event говорит, что коды выхода делают для каждого события, и Decision control говорит, какие поля решения каждое событие учитывает. Универсальные поля, такие как systemMessage, работают на большинстве событий и перечислены в таблице JSON output.

Exit code 0

Exit 0 означает успех и является предполагаемым кодом выхода, когда вы печатаете JSON для структурированного управления.

Для большинства событий Claude Code записывает stdout в журнал отладки и не показывает его в транскрипте. Исключения — это UserPromptSubmit, UserPromptExpansion, SessionStart и PostModelSwitch, где Claude Code добавляет простой текст stdout как контекст, который Claude может видеть и действовать.

Читает ли Claude Code ваш stdout как JSON output или как простой текст, зависит от того, как он начинается и заканчивается, игнорируя окружающие пробелы:

  • Начинается с { и заканчивается на }: Claude Code анализирует его как JSON. Когда выход состоит из двух или более строк, которые каждая анализируются как JSON самостоятельно, и ни одна строка не является объектом JSON output, который устанавливает поле, Claude Code рассматривает весь выход как простой текст. Когда одна из этих строк устанавливает поле, весь выход является ошибкой анализа, описанной ниже.
  • Начинается с { но не заканчивается на }: Claude Code рассматривает это как простой текст.
  • Начинается с чего-либо еще: Claude Code рассматривает это как простой текст, JSON массив или включенную строку JSON в кавычках.

Для событий, которые используют стандартную модель решения, exit 0 с проанализированным объектом, который не проходит валидацию схемы, является неблокирующей ошибкой: действие продолжается, и транскрипт показывает уведомление об ошибке <hook name> hook error с сообщением валидации. То же самое происходит при любом коде выхода, отличном от 2, в то время как exit 2 все еще блокирует.

Для событий, которые используют стандартную модель решения, когда Claude Code пытается анализировать ваш stdout как JSON и не может, он сообщает о неблокирующей ошибке при каждом коде выхода, отличном от 2. Транскрипт показывает уведомление об ошибке <hook name> hook error с сообщением анализа. На событиях, которые добавляют простой текст stdout как контекст, Claude Code не добавляет текст. До v2.1.248 Claude Code рассматривал этот stdout как простой текст.

Stderr из hook, который выходит с 0, идет только в журнал отладки, никогда в транскрипт, и Claude его не видит. Чтобы прочитать его самостоятельно, включите debug logging. Чтобы вывести предупреждение Claude из hook PostToolUse или PostToolUseFailure, выйдите с 2 вместо этого, чтобы Claude видел stderr, даже если инструмент уже запустился.

Exit code 2

Exit 2 означает блокирующую ошибку. На событиях, которые могут блокировать, exit 2 блокирует независимо от того, печатаете ли вы JSON: даже JSON permissionDecision из "allow" не может его переопределить. Claude Code все еще читает любой действительный JSON output на stdout. На Elicitation и ElicitationResult, hookSpecificOutput hook с exit-2 игнорируется.

Сообщение блокировки — это причина из решения блокировки вашего JSON, когда оно его делает, и ваш текст stderr в противном случае. Что делает блокировка, варьируется в зависимости от события: PreToolUse блокирует вызов инструмента, UserPromptSubmit отклоняет запрос и так далее. Exit code 2 behavior per event перечисляет эффект для каждого события, и каждый раздел события говорит, куда идет сообщение.

Hook, который выходит с 2 при печати JSON, который не проходит валидацию схемы JSON output, все еще блокирует: Claude Code использует stderr как причину блокировки и записывает ошибку валидации в журнал отладки. До v2.1.214 Claude Code рассматривал эту комбинацию как неблокирующую ошибку и действие продолжалось.

Этот скрипт блокирует команды rm, выходя с 2 и оставляет каждую другую команду нормальному потоку разрешений:

#!/bin/bash
# Reads JSON input from stdin, checks the command
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")

if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2  # Blocking error: tool call is prevented
fi

exit 0  # No decision: the normal permission flow applies

Другие коды выхода

Любой другой код выхода не блокирует сам по себе для большинства hook событий. Что происходит, зависит от вашего stdout:

  • С проанализированным объектом, который проходит валидацию схемы, для событий, которые используют стандартную модель решения, Claude Code игнорирует код выхода и только JSON решает результат:
    • Каждое поле, которое событие поддерживает, учитывается, включая permissionDecision, additionalContext, updatedInput и systemMessage, и hook не сообщается как ошибка.
    • Decision control перечисляет поля решения для каждого события; универсальные поля, такие как systemMessage, следуют таблице JSON output.
  • С проанализированным объектом, который не проходит валидацию схемы, для событий, которые используют стандартную модель решения, это то же самое неблокирующее ошибка, что и на exit 0: действие продолжается, и уведомление <hook name> hook error содержит сообщение валидации.
  • С stdout, который Claude Code пытается анализировать как JSON и не может, Claude Code сообщает о той же неблокирующей ошибке, что и на exit 0 для событий, которые используют стандартную модель решения. Действие продолжается, и уведомление содержит сообщение анализа.
  • С stdout, который Claude Code рассматривает как простой текст, или с пустым stdout, это неблокирующая ошибка для большинства hook событий: действие продолжается, и транскрипт показывает уведомление об ошибке <hook name> hook error, за которым следует первая строка stderr, с префиксом Failed with non-blocking status code:. Чтобы захватить полный stderr, включите debug logging.

События вне стандартной модели решения сохраняют свои собственные строки в таблице для каждого события: WorktreeCreate не создает при любом ненулевом выходе, независимо от того, что говорит ваш JSON, и события, которые полностью игнорируют выход hook, такие как StopFailure, игнорируют ваш JSON при каждом коде выхода, кроме полей побочных эффектов, таких как terminalSequence, которые все еще срабатывают.

Hook, который не может запуститься, попадает в ту же неблокирующую корзину. Когда путь скрипта не существует или не исполняемый, оболочка выходит с кодом, например 127, и вы видите то же уведомление с сообщением интерпретатора, например Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory. Для большинства hook событий действие продолжается. Когда вы устанавливаете hook политики, следите за этим уведомлением при его первом запуске: неправильно введенный путь в settings.json оставляет ворота молча отключенными.

Timeouts

Кроме hook команды, который вы запускаете с async: true, Claude Code отменяет hook command, http или mcp_tool, который достигает своего timeout, отбрасывая выход hook, поэтому на большинстве событий истекший по времени hook не отображает решение.

На PreModelSwitch, hook, отмененный при его timeout, блокирует переключение модели. На PreToolUse две семьи hook отличаются:

Exit code 2 behavior per event

Exit code 2 — это способ, которым hook сигнализирует "стоп, не делай этого". Эффект зависит от события, потому что некоторые события представляют действия, которые могут быть заблокированы (например, вызов инструмента, который еще не произошел), а другие представляют вещи, которые уже произошли или не могут быть предотвращены.

Hook event Может блокировать? Что происходит на exit 2
PreToolUse Да Блокирует вызов инструмента
PermissionRequest Нет Exit code 2 не учитывается для этого события и поток разрешений продолжается без изменений. Отклоните через объект decision вместо этого
UserPromptSubmit Да Блокирует обработку запроса. См. What a blocked prompt leaves behind
UserPromptExpansion Да Блокирует расширение
Stop Да Предотвращает остановку Claude, продолжает разговор
SubagentStop Да Предотвращает остановку subagent
TeammateIdle Да Предотвращает переход товарища в режим ожидания, поэтому он продолжает работать
TaskCreated Да Откатывает создание задачи
TaskCompleted Да Предотвращает отметку задачи как завершенной
ConfigChange Да Блокирует вступление изменения конфигурации в силу (кроме policy_settings)
StopFailure Нет Выход и код выхода игнорируются, кроме terminalSequence
PostToolUse Нет Показывает stderr Claude; инструмент уже запустился
PostToolUseFailure Нет Показывает stderr Claude; инструмент уже не удался
PostToolBatch Да Останавливает агентный цикл перед следующим вызовом модели
PermissionDenied Нет Выход и stderr игнорируются, потому что отказ уже произошел. Используйте JSON hookSpecificOutput.retry: true, чтобы сказать модели, что она может повторить попытку; Claude Code игнорирует retry: true для отказов без вердикта
Notification Нет Выход и stderr игнорируются
SubagentStart Нет Показывает stderr только пользователю
SessionStart Нет Показывает stderr только пользователю
Setup Нет Выход и stderr игнорируются
SessionEnd Нет Показывает stderr только пользователю
CwdChanged Нет Показывает stderr только пользователю
DirectoryAdded Нет Stderr идет в журнал отладки; каталог уже добавлен
FileChanged Нет Показывает stderr только пользователю
PreCompact Да Блокирует компактирование
PostCompact Нет Показывает stderr только пользователю
PreModelSwitch Да Блокирует переключение модели и показывает stderr пользователю
PostModelSwitch Нет Показывает stderr только пользователю; модель уже переключилась
Elicitation Да Отклоняет запрос информации
ElicitationResult Да Блокирует ответ (действие становится отклонением)
WorktreeCreate Да Любой ненулевой код выхода вызывает ошибку создания worktree
WorktreeRemove Да Любой ненулевой код выхода вызывает ошибку удаления worktree, если каталог все еще существует после этого. См. WorktreeRemove для того, что происходит с каталогом
InstructionsLoaded Нет Код выхода игнорируется
MessageDisplay Нет Отображается исходный текст

Для SessionStart, SubagentStart и PostModelSwitch, Claude Code отображает stderr exit code 2 в транскрипте как уведомление об ошибке <hook name> hook error, так же как оно отображает неблокирующую ошибку. Claude его не видит, и сеанс или subagent продолжается. Для SubagentStart уведомление появляется в собственном транскрипте subagent, а не в родительском разговоре.

HTTP response handling

HTTP hooks используют коды состояния HTTP и тела ответов вместо кодов выхода и stdout. Результаты ниже применяются к большинству событий; событие с его собственным контрактом отказа в таблице для каждого события, такое как WorktreeCreate, применяет этот контракт к неудачному HTTP hook также:

  • 2xx с пустым телом: успех, эквивалентно exit code 0 без выхода
  • 2xx с телом объекта JSON: анализируется с использованием той же схемы JSON output, что и hooks команд. Тело, которое не проходит валидацию схемы, является неблокирующей ошибкой
  • 2xx с любым другим телом, таким как простой текст: неблокирующая ошибка, обрабатывается так же, как статус non-2xx. Claude Code не добавляет текст в контекст Claude
  • Статус non-2xx: неблокирующая ошибка, выполнение продолжается
  • Ошибка соединения: неблокирующая ошибка, выполнение продолжается
  • Timeout: hook отменяется, как описано в разделе Timeouts

В отличие от hooks команд, HTTP hooks не могут сигнализировать блокирующую ошибку только через коды состояния. Чтобы заблокировать вызов инструмента или отклонить разрешение, верните ответ 2xx с телом JSON, содержащим соответствующие поля решения.

JSON output

Коды выхода позволяют вам только блокировать или молчать, но JSON output дает вам более точное управление. Вместо выхода с кодом 2 для блокировки, выйдите с 0 и напечатайте объект JSON на stdout. Claude Code читает конкретные поля из этого JSON для управления поведением, включая decision control для блокировки, разрешения или эскалации пользователю.

Stdout вашего hook должен содержать только объект JSON. Если ваш профиль оболочки печатает текст при запуске, это может помешать анализу JSON. См. Hook JSON has no effect в руководстве по устранению неполадок.

Строки additionalContext, systemMessage и initialUserMessage hook, а также его простой stdout, ограничены 10 000 символов:

  • Область: Claude Code измеряет каждую строку отдельно, даже когда несколько hooks запускаются для одного события. Для JSON output каждое поле измеряется отдельно; простой stdout измеряется целиком.
  • Превышение лимита: Claude Code сохраняет выход в файл в каталоге сеанса и заменяет его путем к файлу и предпросмотром до первых 2000 символов. Большой действительный результат Bash обрабатывается так же, как описано в разделе Output limits. В отличие от этого потолка Bash, эта крышка не имеет параметра или переменной окружения для ее повышения.
  • Чтение файла: Claude Code не просит Claude прочитать файл, поэтому держите все, что Claude должен всегда видеть, в пределах крышки.

Объект JSON поддерживает три вида полей:

  • Универсальные поля, такие как continue, перечислены в таблице ниже. Каждое событие их принимает, но некоторые события игнорируют их или доставляют systemMessage в другое место, чем транскрипт. Каждый раздел события говорит об этом. terminalSequence работает на этих событиях также, с исключениями, перечисленными в разделе Emit terminal notifications.
  • Top-level decision и reason используются некоторыми событиями для блокировки или предоставления обратной связи.
  • hookSpecificOutput — это вложенный объект для событий, которым нужно более богатое управление. Он требует поле hookEventName, установленное на имя события.
Поле По умолчанию Описание
continue true Если false, Claude полностью прекращает обработку после запуска hook. Имеет приоритет над любыми полями решения, специфичными для события
stopReason нет Сообщение, показанное пользователю, когда continue равно false. Оно остается в разговоре, поэтому Claude видит его, если разговор продолжается
suppressOutput false Не имеет эффекта: Claude Code принимает поле, но не действует на него. Stdout успешного hook никогда не показывается в транскрипте и записывается в журнал отладки
systemMessage нет Предупреждающее сообщение, показанное пользователю. В выводе Agent SDK и --output-format stream-json, оно может поступить как SDKInformationalMessage
terminalSequence нет Последовательность escape терминала для Claude Code для выпуска от вашего имени, такая как уведомление рабочего стола, заголовок окна или звонок. Ограничено OSC 0/1/2/9/99/777 и BEL. Если значение содержит что-либо вне списка разрешений, поле игнорируется. Используйте это вместо записи в /dev/tty, которая недоступна для hooks

Чтобы полностью остановить Claude:

{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

Для hooks PreToolUse и PostToolUse остановка применяется даже когда вызов инструмента не удается или завершается, пока Claude все еще потоком ответ.

Emit terminal notifications

Hooks запускаются без управляющего терминала, поэтому запись последовательностей escape непосредственно в /dev/tty не удается. Вместо этого верните последовательность escape в поле terminalSequence и Claude Code выпустит ее от вашего имени через свой собственный путь записи терминала. Это свободно от гонок, работает внутри tmux и GNU screen, и работает на Windows, где нет /dev/tty.

Поле принимает строку из одной или нескольких последовательностей escape в списке разрешений:

  • OSC 0, 1, 2: заголовки окна и значков
  • OSC 9: уведомления iTerm2, ConEmu, Windows Terminal и WezTerm, включая прогресс панели задач 9;4
  • OSC 99: уведомления Kitty
  • OSC 777: уведомления urxvt, Ghostty и Warp
  • Bare BEL

Последовательности могут быть завершены BEL или ST. Все, что находится вне списка разрешений, включая последовательности курсора CSI и цвета, последовательности палитры OSC, гиперссылки OSC 8, записи буфера обмена OSC 52 и OSC 1337, отклоняется и поле игнорируется.

Claude Code выпускает саму последовательность, когда обрабатывает выход вашего hook, поэтому поле работает на событиях, которые игнорируют systemMessage и continue, такие как Notification и StopFailure. Оно имеет два ограничения:

  • Claude Code выпускает последовательность только в интерактивном сеансе и только пока его интерфейс находится на экране. В неинтерактивном режиме с флагом -p и в Agent SDK он игнорирует поле.
  • Hook команды WorktreeCreate не может вернуть JSON, потому что Claude Code читает его stdout как путь worktree. HTTP hook WorktreeCreate возвращает JSON и может включать поле.

Пример ниже срабатывает уведомление рабочего стола из hook Notification. Последовательность escape строится с помощью printf восьмеричных escape, поэтому управляющие байты никогда не появляются в командной строке оболочки, и jq -n --arg строит выход JSON, поэтому кавычки, обратные слэши и новые строки в сообщении уведомления правильно экранируются:

#!/bin/bash
# Notification hook: ping the desktop when Claude Code needs attention.
input=$(cat)
title="Claude Code"
body=$(jq -r '.message // "Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'

Форма { "terminalSequence": "..." } одинакова из любой оболочки или языка.

Add context for Claude

Поле additionalContext передает строку из вашего hook в контекстное окно Claude. Claude Code оборачивает строку в напоминание системы и вставляет ее в разговор в точке, где сработал hook. Claude читает напоминание при следующем запросе модели, но оно не появляется как сообщение чата в интерфейсе.

Верните additionalContext внутри hookSpecificOutput рядом с именем события:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
  }
}

Где появляется напоминание, зависит от события:

Когда несколько hooks возвращают additionalContext для одного события, Claude получает все значения.

Если значение превышает 10 000 символов, Claude Code записывает текст в файл в каталоге сеанса и передает Claude путь к файлу с предпросмотром до первых 2000 символов вместо этого. Claude может прочитать файл, но Claude Code не просит его.

Используйте additionalContext для информации, которую Claude должен знать о текущем состоянии вашей среды или операции, которая только что запустилась:

  • Состояние среды: текущая ветвь, цель развертывания или активные флаги функций
  • Условные правила проекта: какая команда теста применяется к только что отредактированному файлу, какие каталоги доступны только для чтения в этом worktree
  • Внешние данные: открытые проблемы, назначенные вам, недавние результаты CI, контент, полученный из внутреннего сервиса

Для инструкций, которые никогда не изменяются, предпочитайте CLAUDE.md. Он загружается без запуска скрипта и является стандартным местом для статических соглашений проекта.

Напишите текст как фактические утверждения, а не как императивные системные инструкции. Фразировка, такая как "Цель развертывания — production" или "Этот репо использует bun test", читается как информация о проекте. Текст, сформулированный как внеполосные системные команды, может вызвать защиту Claude от инъекций подсказок, что заставляет Claude вывести текст вам вместо того, чтобы рассматривать его как контекст.

Claude Code сохраняет введенный текст в транскрипте сеанса. Для событий середины сеанса, таких как PostToolUse или UserPromptSubmit, когда вы возобновляете с --continue или --resume, Claude Code воспроизводит сохраненный текст, а не повторно запускает hook для прошлых ходов, поэтому значения, такие как временные метки или SHA коммитов, становятся устаревшими. Hooks SessionStart запускаются снова при возобновлении с source, установленным на "resume", или "fork", если вы добавили --fork-session, поэтому они могут обновить свой контекст.

Decision control

Не каждое событие поддерживает блокировку или управление поведением через JSON. События, которые это делают, каждое использует другой набор полей для выражения этого решения. Используйте эту таблицу как быструю ссылку перед написанием hook:

События Паттерн решения Ключевые поля
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact Top-level decision decision: "block", reason. Stop и SubagentStop также принимают hookSpecificOutput.additionalContext для неошибочной обратной связи, которая продолжает разговор
TeammateIdle, TaskCompleted Exit code или continue: false Exit code 2 блокирует действие с обратной связью stderr. JSON {"continue": false, "stopReason": "..."} также полностью останавливает товарища, совпадая с поведением hook Stop; TaskCompleted игнорирует это, когда инструмент TaskUpdate вызвал событие
TaskCreated Exit code или top-level decision Exit code 2 или decision: "block" отменяет задачу и возвращает сообщение Claude. continue: false игнорируется
PreToolUse hookSpecificOutput permissionDecision (allow/deny/ask/defer), permissionDecisionReason
PreModelSwitch hookSpecificOutput или top-level decision permissionDecision (allow/deny/ask), permissionDecisionReason. decision: "block" также отменяет переключение
PermissionRequest hookSpecificOutput decision.behavior (allow/deny)
PermissionDenied hookSpecificOutput retry: true сообщает модели, что она может повторить попытку отклоненного вызова инструмента; Claude Code игнорирует это для отказов без вердикта
WorktreeCreate path return Hook команды печатает путь на stdout; HTTP hook возвращает hookSpecificOutput.worktreePath. Ошибка hook или отсутствующий путь не создает
WorktreeRemove Exit code Любой ненулевой код выхода делает удаление неудачным, если каталог все еще существует после этого. Выход JSON отбрасывается
Elicitation hookSpecificOutput action (accept/decline/cancel), content (значения полей формы для accept)
ElicitationResult hookSpecificOutput action (accept/decline/cancel), content (значения полей формы переопределяют)
MessageDisplay hookSpecificOutput displayContent заменяет отображаемый текст на экране. Только отображение: транскрипт и то, что видит Claude, сохраняют оригинал
SessionStart, SubagentStart, PostModelSwitch Только контекст hookSpecificOutput.additionalContext добавляет контекст для Claude. SessionStart также принимает initialUserMessage, watchPaths, sessionTitle и reloadSkills. Нет блокировки или управления решением
Setup, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged Нет Нет управления решением. Используется для побочных эффектов, таких как логирование или очистка

Несколько событий также могут переписывать контент, а не только разрешать или блокировать его:

  • PreToolUse: updatedInput непосредственно под hookSpecificOutput заменяет аргументы инструмента перед его запуском. См. PreToolUse decision control
  • PermissionRequest: updatedInput внутри объекта decision. См. PermissionRequest decision control
  • PostToolUse: updatedToolOutput заменяет результат инструмента. См. PostToolUse decision control
  • UserPromptSubmit: не может переписать запрос; он только вводит additionalContext рядом с ним

Для случаев использования редакции или трансформации перехватите на PreToolUse для исходящих входов инструмента и PostToolUse для входящих результатов инструмента.

Вот примеры каждого паттерна в действии:

Единственное значение для decision — это "block". Чтобы разрешить действию продолжаться, опустите decision из вашего JSON или выйдите с 0 без какого-либо JSON вообще:

{
"decision": "block",
"reason": "Test suite must pass before proceeding"
}

Для расширенных примеров, включая валидацию команд Bash, фильтрацию запросов и скрипты автоматического одобрения, см. What you can automate в руководстве и Bash command validator reference implementation.

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

Каждое событие соответствует точке жизненного цикла Claude Code, в которой могут выполняться хуки. Разделы ниже упорядочены в соответствии с жизненным циклом: от настройки сессии через агентный цикл до завершения сессии. В каждом разделе описано, когда срабатывает событие, какие значения matcher оно поддерживает, какие входные данные JSON получает и как управлять поведением через вывод.

SessionStart

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

SessionStart выполняется в каждой сессии, поэтому такие хуки должны работать быстро. Поддерживаются только хуки type: "command" и type: "mcp_tool". О том, когда выполняются хуки mcp_tool, см. Поля хуков MCP-инструментов.

Значение matcher соответствует тому, как была инициирована сессия:

Matcher Когда срабатывает
startup Новая сессия
resume --resume, --continue или /resume
clear /clear
compact Автоматическое или ручное сжатие контекста
fork Новая сессия, ответвлённая от существующей: --fork-session вместе с --resume или --continue, фоновая копия /fork, /branch или диалог, который вы перевели в фон

До v2.1.214 ответвлённые сессии сообщали источник "resume".

Когда вы запускаете интерактивную сессию, возобновляете диалог при запуске с помощью --continue или --resume или выполняете /clear, хуки SessionStart выполняются в фоне. Вы можете сразу начать вводить текст, а возобновлённый диалог появляется, не дожидаясь хуков. Первый ответ Claude всё же ожидает завершения хуков, чтобы их контекст дошёл до Claude.

Когда вы переключаете диалоги с помощью /resume внутри сессии, переключение, напротив, ожидает завершения хуков. Если вы выполните /clear или переключитесь на другой диалог, пока фоновые хуки ещё работают, ничто из возвращённого ими к сессии не применяется.

То же ожидание действует при запуске, в том числе для возобновлённой сессии: промпт, отправленный, пока хуки SessionStart ещё выполняются, не дойдёт до Claude, пока они не завершатся.

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

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

Помимо общих входных полей, хуки SessionStart получают source и, необязательно, model, agent_type и session_title:

Поле Описание
source Как началась сессия: "startup" для новых сессий, "resume" для возобновлённых, "clear" после /clear, "compact" после сжатия контекста или "fork" для новой сессии, ответвлённой от существующей
model Идентификатор активной модели. Может отсутствовать, например после /clear или когда сессия восстановлена через восстановление диалога, поэтому проверяйте наличие поля перед чтением
agent_type Имя агента; присутствует, когда вы запускаете Claude Code командой claude --agent <name>
session_title Пользовательское название сессии; присутствует, если оно задано, например через --name, /rename, вывод хука sessionTitle или renameSession() в Agent SDK. Хук, выдающий sessionTitle, может сначала проверить это поле, чтобы не перезаписать существующее пользовательское название

У сессии, которую вы не назвали, всё равно может быть сгенерированное название. Такое название не является пользовательским и не появляется в session_title.

Когда source равно "resume" или "fork" и транскрипт содержит хотя бы один ответ от Claude, хуки SessionStart также получают четыре поля ниже. Ваш хук может использовать их, чтобы до первого запроса сообщить, во что обойдётся возобновление устаревшего диалога, например в systemMessage. Для этих полей требуется Claude Code v2.1.251 или новее.

Поле Описание
seconds_since_last_response Реальное время в секундах с момента последнего ответа в возобновлённом транскрипте
context_tokens Токены, которые первый запрос возобновлённой сессии повторно отправляет в качестве промпта
prompt_cache_likely_expired true, когда последний ответ старше времени жизни кэша промптов сессии или более позднее сжатие контекста заменило кэшированный диалог
estimated_cache_write_usd Оценочная стоимость в долларах США записи context_tokens в кэш промптов на модели сессии, без учёта ответа

В этом примере показаны входные данные для сессии, возобновлённой через 90 минут после последнего ответа:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionStart",
  "source": "resume",
  "model": "claude-opus-5",
  "seconds_since_last_response": 5400,
  "context_tokens": 182340,
  "prompt_cache_likely_expired": true,
  "estimated_cache_write_usd": 1.1396
}

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

Claude Code добавляет в контекст Claude stdout, который он обрабатывает как обычный текст. Помимо полей вывода JSON, доступных всем хукам, вы можете возвращать следующие поля, специфичные для события:

Поле Описание
additionalContext Строка, добавляемая в контекст Claude в начале диалога, перед первым промптом. О том, как доставляется текст и что в него включать, см. Добавление контекста для Claude
initialUserMessage Строка, используемая как первое пользовательское сообщение сессии. Применяется в неинтерактивном режиме с флагом -p, где она становится первым ходом, даже если промпт не передан. Если промпт передан, он следует как следующий ход. В отличие от additionalContext, который прикрепляется к существующему ходу, это поле создаёт ход
sessionTitle Задаёт название сессии с тем же эффектом, что и /rename. Используйте для автоматического именования сессий по каталогу запуска, ветке git или имени worktree. Применяется, когда source равно "startup", "resume" или "fork"; игнорируется при "clear" и "compact"
watchPaths Массив абсолютных путей для отслеживания событий FileChanged в этой сессии
reloadSkills Логическое значение. При true Claude Code повторно сканирует каталоги скиллов и команд после завершения хуков SessionStart, так что установленные хуком скиллы доступны в той же сессии, начиная с первого промпта
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
    "sessionTitle": "auth-refactor"
  }
}

Поскольку для этого события обычный stdout и так доходит до Claude, хук, который только загружает контекст, может выводить данные прямо в stdout, не формируя JSON. Используйте форму JSON, когда нужно совместить контекст с другими полями, например sessionTitle.

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

#!/bin/bash

git -C ~/.claude/skills/team-skills pull --quiet 2>/dev/null || \
  git clone --quiet https://git.example.com/your-org/team-skills.git ~/.claude/skills/team-skills

echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'

URL репозитория — это заполнитель; замените его на собственный репозиторий скиллов. С заполнителем клонирование завершится ошибкой и выведет сообщение fatal: в stderr. Stderr хука SessionStart, завершившегося с кодом 0, носит лишь информационный характер, поэтому запрос reloadSkills всё равно применяется.

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

Хукам SessionStart доступна переменная окружения CLAUDE_ENV_FILE, содержащая путь к файлу, в котором можно сохранять переменные окружения для последующих команд Bash.

Чтобы задать отдельные переменные окружения, запишите инструкции export в CLAUDE_ENV_FILE. Используйте дозапись (>>), чтобы сохранить переменные, заданные другими хуками:

#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0

Чтобы зафиксировать все изменения окружения, внесённые командами настройки, сравните экспортированные переменные до и после:

#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0

Setup

Срабатывает, только когда вы запускаете Claude Code с --init-only либо с --init или --maintenance в неинтерактивном режиме с флагом -p. При обычном запуске не срабатывает. Используйте его для однократной установки зависимостей или плановой очистки, которую вы явно запускаете из CI или скриптов, отдельно от обычного запуска сессии. Для инициализации каждой сессии используйте вместо этого SessionStart.

Значение matcher соответствует флагу CLI, вызвавшему хук:

Matcher Когда срабатывает
init claude --init-only или claude -p --init
maintenance claude -p --maintenance

Когда вы выполняете 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":

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Setup",
  "trigger": "init"
}

Управление решениями 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 получают следующие поля:

Поле Описание
file_path Абсолютный путь к загруженному файлу инструкций
memory_type Область действия файла: "User", "Project", "Local" или "Managed"
load_reason Почему файл был загружен: "session_start", "nested_traversal", "path_glob_match", "include" или "compact". Значение "compact" срабатывает, когда файлы инструкций повторно загружаются после сжатия контекста
globs Glob-шаблоны путей из frontmatter paths: файла, если есть. Присутствует только для загрузок path_glob_match
trigger_file_path Путь к файлу, обращение к которому вызвало эту загрузку, для отложенных загрузок
parent_file_path Путь к родительскому файлу инструкций, который включил этот, для загрузок include
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "InstructionsLoaded",
  "file_path": "/Users/my-project/CLAUDE.md",
  "memory_type": "Project",
  "load_reason": "session_start"
}

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

У хуков InstructionsLoaded нет управления решениями. Они не могут блокировать или изменять загрузку инструкций. Claude Code отбрасывает их поля вывода JSON, такие как systemMessage и continue. Используйте это событие для журнала аудита, отслеживания соответствия требованиям или наблюдаемости.

UserPromptSubmit

Запускается при отправке промпта, до того как Claude его обработает. Это позволяет добавлять дополнительный контекст на основе промпта/диалога, проверять промпты или блокировать определённые типы промптов.

Хуки UserPromptSubmit срабатывают не только на промпты, которые вы вводите. Claude Code также запускает их, когда:

У хуков UserPromptSubmit таймаут по умолчанию составляет 30 секунд для типов command, http и mcp_tool — меньше, чем 600 секунд по умолчанию для этих типов в большинстве других событий. Поскольку этот хук выполняется перед каждым промптом и блокирует обработку моделью до своего завершения, зависший хук останавливает сессию. Если вашему хуку нужно больше времени, задайте поле timeout в записи хука.

За исключением command-хука, запущенного с async: true, command-, HTTP- или MCP-хук UserPromptSubmit, достигший таймаута, отменяется, а его вывод, включая любой additionalContext, отбрасывается. Промпт всё равно доходит до Claude, но без этого контекста. В транскрипте отображается уведомление с именем хука, сработавшим таймаутом и указанием на то, что вывод был отброшен.

Callback-хук Agent SDK на UserPromptSubmit, достигший таймаута, блокирует промпт с сообщением, в котором указаны хук и таймаут, поскольку callback в этом месте может выступать шлюзом политики, который не должен при сбое пропускать запросы. Сессия продолжается. До v2.1.208 таймаут callback для этого события завершал ход с ошибкой выполнения.

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

Помимо общих входных полей, хуки UserPromptSubmit получают поле prompt, содержащее отправленный текст. Вставленное содержимое, свёрнутое в заглушку [Pasted text #N], приходит развёрнутым на своём месте. В сессиях, где Claude Code помечает вставленный текст для Claude, это развёрнутое содержимое находится между строкой <pasted_content id="…"> и строкой </pasted_content id="…">, поэтому учитывайте эти строки, если ваш хук разбирает промпт.

Хуки UserPromptSubmit также получают session_title, когда у сессии есть пользовательское название, с тем же значением, что и поле session_title в SessionStart.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

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

Хуки UserPromptSubmit могут управлять тем, обрабатывается ли отправленный промпт, и добавлять контекст. Доступны все поля вывода JSON.

Есть два способа добавить контекст в диалог при коде выхода 0:

  • stdout в виде обычного текста: Claude Code добавляет в контекст Claude stdout, который он обрабатывает как обычный текст
  • JSON с additionalContext: используйте формат JSON ниже для большего контроля. Поле additionalContext добавляется как контекст

Ни один из каналов не создаёт видимой записи в транскрипте. Обычный stdout и значение additionalContext внедряются каждое как системное напоминание, начинающееся с имени хука; Claude читает оба. Чтобы подтвердить доставку, проверьте отладочный лог.

Чтобы заблокировать промпт, верните объект JSON с decision, равным "block":

Поле Описание
decision "block" останавливает промпт до того, как он дойдёт до Claude. Не указывайте, чтобы разрешить промпту пройти
reason Показывается пользователю, когда decision равно "block". Не добавляется в контекст
additionalContext Строка, добавляемая в контекст Claude вместе с отправленным промптом. См. Добавление контекста для Claude
sessionTitle Задаёт название сессии. Используйте для автоматического именования сессий на основе содержимого промпта
suppressOriginalPrompt Если true, когда хук блокирует промпт, текст промпта не включается в сообщение о блокировке. См. Что остаётся после заблокированного промпта

Хук, блокирующий с кодом выхода 2, обрабатывается так же, как reason: сообщение о блокировке показывает пользователю текст из stderr, и он не добавляется в контекст.

{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here",
    "sessionTitle": "My session title",
    "suppressOriginalPrompt": true
  }
}

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

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

suppressOriginalPrompt изменяет только сообщение о блокировке. Отправленный текст всё равно может появиться в локальных файлах, таких как транскрипт сессии и история промптов, поэтому блокирующий хук не является способом не допустить попадания секрета на диск. Чтобы ограничить или удалить эти файлы, см. Хранение в открытом виде и Очистка локальных данных.

UserPromptExpansion

Выполняется, когда введённая пользователем команда разворачивается в промпт до того, как дойти до Claude. Используйте его, чтобы запретить прямой вызов определённых команд, внедрить контекст для конкретного скилла или логировать, какие команды вызывают пользователи. Например, хук с matcher deploy может блокировать /deploy, если нет файла подтверждения, а хук, соответствующий скиллу ревью, может добавлять чек-лист ревью команды как additionalContext.

Это событие покрывает путь, который не покрывает PreToolUse: хук PreToolUse, соответствующий инструменту Skill, срабатывает только когда Claude вызывает этот инструмент, но прямой ввод /skillname обходит PreToolUse. UserPromptExpansion срабатывает на этом прямом пути.

Сопоставляется по command_name. Оставьте matcher пустым, чтобы срабатывать для каждой команды, разворачивающейся в промпт.

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

Помимо общих входных полей, хуки UserPromptExpansion получают expansion_type, command_name, command_args, command_source и исходную строку prompt. Поле expansion_type равно slash_command для скиллов и пользовательских команд или mcp_prompt для промптов MCP-серверов.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../00893aaf.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptExpansion",
  "expansion_type": "slash_command",
  "command_name": "example-skill",
  "command_args": "arg1 arg2",
  "command_source": "plugin",
  "prompt": "/example-skill arg1 arg2"
}

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

Хуки UserPromptExpansion могут блокировать развёртывание или добавлять контекст. Доступны все поля вывода JSON.

Поле Описание
decision "block" не даёт команде развернуться. Не указывайте, чтобы разрешить продолжение
reason Показывается пользователю, когда decision равно "block"
additionalContext Строка, добавляемая в контекст Claude вместе с развёрнутым промптом. См. Добавление контекста для Claude

Хук, блокирующий с кодом выхода 2, обрабатывается так же, как reason: сообщение о блокировке показывает пользователю текст из stderr.

{
  "decision": "block",
  "reason": "This slash command is not available",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptExpansion",
    "additionalContext": "Additional context for this expansion"
  }
}

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 для отслеживания прогресса по сообщению, а не рассчитывайте на определённую группировку строк.

Поле Описание
turn_id UUID текущего хода
message_id UUID отображаемого сообщения ассистента. Неизменен во всех пакетах одного сообщения. Это не идентификатор API msg_…, поэтому его нельзя сопоставить с идентификаторами сообщений в транскрипте
index Индекс этого пакета в сообщении, начиная с нуля
final true для последнего пакета сообщения. У каждого сообщения ровно один последний пакет
delta Строки, завершённые после предыдущего пакета, включая завершающие символы новой строки. Всегда целые строки, кроме последнего пакета, который может закончиться посреди строки. В интерактивных запусках delta последнего пакета пуста, если сообщение заканчивается символом новой строки, поэтому считайте сигналом конца сообщения final, а не непустую delta. В запусках Agent SDK и claude -p единственный вызов содержит всё сообщение
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "MessageDisplay",
  "turn_id": "0c9e6a2f-7d41-4f4e-9a15-3f4f7c2b8d10",
  "message_id": "5b2a9c8e-1f63-4d8a-b7c4-9e0d2a6f1c3b",
  "index": 0,
  "final": false,
  "delta": "Here is the plan:\n"
}

Вывод MessageDisplay

Помимо полей вывода JSON, доступных всем хукам, хуки MessageDisplay могут возвращать displayContent, чтобы заменить delta на экране:

Поле Описание
displayContent Текст, отображаемый вместо delta. Не указывайте, чтобы отобразить оригинал

У хуков MessageDisplay нет управления решениями. Они не могут блокировать сообщение или изменять то, что сохраняется в транскрипте или отправляется Claude. Claude Code учитывает displayContent из их вывода JSON и отбрасывает systemMessage и continue.

В этом примере из ответов Claude удаляется форматирование markdown для отображения в виде обычного текста. Скрипт читает каждый пакет из stdin, удаляет маркеры жирного шрифта и обратные кавычки встроенного кода из delta и возвращает результат как displayContent.

Зарегистрируйте command-хук для события в файле настроек:

{
"hooks": {
"MessageDisplay": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh",
"args": []
}
]
}
]
}
}

Сохраните этот скрипт в .claude/hooks/plain-display.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x:

#!/bin/bash
jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'

Пакеты без 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, чтобы разрешить, запретить, запросить подтверждение или отложить вызов инструмента.

Callback-хук Agent SDK на PreToolUse, превысивший таймаут, блокирует вызов инструмента, и Claude получает результат с ошибкой, указывающей на таймаут. Явный запрет, возвращённый другим хуком, по-прежнему имеет приоритет.

Входные данные 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 передаёт:

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "C:\\project\\src\\index.ts",
    "content": "..."
  },
  ...
}

Поля tool_input зависят от инструмента:

Bash

Выполняет shell-команды.

Поле Тип Пример Описание
command string "npm test" Shell-команда для выполнения
description string "Run test suite" Необязательное описание того, что делает команда
timeout number 120000 Необязательный таймаут в миллисекундах. Значения выше максимума уменьшаются до максимума, а не отклоняются
run_in_background boolean false Выполнять ли команду в фоне

Когда команда Bash изменяет файлы в репозитории Git, Claude Code может записывать, что изменилось. Изменения записываются во всех режимах разрешений, когда настройка bashEditDiffEnabled включает запись; в описании этой настройки указано, в каких файлах её можно задать. В противном случае изменения записываются только в авторежиме и режиме bypassPermissions, и только когда Claude Code поручает Claude редактировать файлы через Bash. Установите bashEditDiffEnabled в false, чтобы отключить запись. Фоновые команды и команды только для чтения не содержат diff.

Затем ваш хук PostToolUse получает изменённые файлы в tool_response.bashEditDiff. Список охватывает то, что изменилось в репозитории за время выполнения команды. Файлы, которые Git игнорирует, и файлы в подмодулях не включаются. Требуется Claude Code v2.1.269 или новее.

changedFiles и files перечисляют, что изменила команда; остальные поля показывают, насколько этот список полон и надёжен.

Поле Тип Пример Описание
changedFiles array ["/path/to/src/app.ts"] Абсолютные пути файлов, изменённых командой, не более 200. Присутствует, когда files содержит diff или moreFiles больше нуля
files array [{"filePath": "/path/to/src/app.ts", "hunks": [...]}] Diff до 5 изменённых файлов для отображения. created или deleted равно true для файла, который команда добавила или удалила
moreFiles number 2 Количество изменённых файлов без diff в files
unavailable boolean true Устанавливается, когда diff неполон или его не удалось получить
skipped boolean true Устанавливается для команды Git, перемещающей рабочее дерево, например git checkout или git stash, поэтому Claude Code не получает diff
shared boolean true Устанавливается, когда другой вызов инструмента Bash, например субагента, выполнялся в том же репозитории в то же время, поэтому некоторые перечисленные изменения могут принадлежать той команде
PowerShell

Выполняет команды PowerShell. Доступность по платформам см. в разделе об инструменте PowerShell.

Поля совпадают с инструментом Bash, строка команды — в command:

Поле Тип Пример Описание
command string "Get-ChildItem -Recurse" Команда PowerShell для выполнения
description string "List files recursively" Необязательное описание того, что делает команда
timeout number 120000 Необязательный таймаут в миллисекундах
run_in_background boolean false Выполнять ли команду в фоне

В хуках, проверяющих shell-команды, используйте matcher Bash|PowerShell, чтобы охватить оба инструмента:

  • В Windows, где бы ни был включён инструмент PowerShell, Claude считает PowerShell основной оболочкой и направляет через неё shell-команды.
  • В Windows без Git Bash инструмент включается автоматически, а Claude Code вообще не регистрирует инструмент Bash.
  • Хук, соответствующий только Bash, там никогда не срабатывает.
Write

Создаёт или перезаписывает файл.

Поле Тип Пример Описание
file_path string "/path/to/file.txt" Абсолютный путь к файлу для записи
content string "file content" Содержимое для записи в файл
Edit

Заменяет строку в существующем файле.

Поле Тип Пример Описание
file_path string "/path/to/file.txt" Абсолютный путь к файлу для редактирования
old_string string "original text" Текст для поиска и замены
new_string string "replacement text" Текст замены
replace_all boolean false Заменять ли все вхождения
Read

Читает содержимое файла.

Поле Тип Пример Описание
file_path string "/path/to/file.txt" Абсолютный путь к файлу для чтения
offset number 10 Необязательный номер строки, с которой начать чтение
limit number 50 Необязательное количество строк для чтения
Glob

Находит файлы, соответствующие glob-шаблону.

Поле Тип Пример Описание
pattern string "**/*.ts" Glob-шаблон для сопоставления файлов
path string "/path/to/dir" Необязательный каталог для поиска. По умолчанию — текущий рабочий каталог
Grep

Ищет по содержимому файлов с помощью регулярных выражений.

Поле Тип Пример Описание
pattern string "TODO.*fix" Шаблон регулярного выражения для поиска
path string "/path/to/dir" Необязательный файл или каталог для поиска
glob string "*.ts" Необязательный glob-шаблон для фильтрации файлов
output_mode string "content" "content", "files_with_matches" или "count". По умолчанию "files_with_matches"
-i boolean true Поиск без учёта регистра
multiline boolean false Включить многострочное сопоставление
WebFetch

Загружает и обрабатывает веб-контент.

Поле Тип Пример Описание
url string "https://example.com/api" URL, с которого загружается контент
prompt string "Extract the API endpoints" Промпт, применяемый к загруженному контенту
WebSearch

Выполняет поиск в интернете.

Поле Тип Пример Описание
query string "react hooks best practices" Поисковый запрос
allowed_domains array ["docs.example.com"] Необязательно: включать результаты только с этих доменов
blocked_domains array ["spam.example.com"] Необязательно: исключать результаты с этих доменов
Agent

Запускает субагента.

Поле Тип Пример Описание
prompt string "Find all API endpoints" Задача, которую должен выполнить агент
description string "Find API endpoints" Краткое описание задачи
subagent_type string "Explore" Тип используемого специализированного агента
model string "sonnet" Необязательный псевдоним модели для переопределения модели по умолчанию

Когда вызов Agent на переднем плане завершается, ваш хук PostToolUse получает результат субагента и телеметрию запуска в tool_response. Читайте эти поля для анализа запуска; для сводных данных по токенам и стоимости по всем субагентам используйте счётчики токенов и стоимости с фильтром query_source "subagent", поскольку totalTokens и usage охватывают только последний запрос:

Поле Тип Пример Описание
status string "completed" "completed" для субагентов на переднем плане, "async_launched" для фоновых субагентов. По умолчанию субагенты выполняются в фоне, поэтому вызов Agent без run_in_background также даёт "async_launched"
agentId string "a4d2c8f1e0b3a297" Идентификатор запуска субагента
content array [{"type": "text", "text": "Found 12 endpoints..."}] Итоговые текстовые блоки субагента или, для субагента, чей отчёт передаётся через SubagentHandback, вместо них краткая заметка об этой передаче
resolvedModel string "claude-sonnet-4-5" Модель, с которой субагент начал работу; может отличаться от запрошенной
modelsUsed array ["claude-sonnet-4-5", "claude-haiku-4-5"] Использованные модели по порядку, с объединением последовательных повторов; задаётся только если модель была заменена во время запуска. Требуется Claude Code v2.1.212 или новее
totalTokens number 12450 Количество токенов последнего запроса API субагента: входные, выходные и токены кэша вместе. Это не итог за весь запуск
totalDurationMs number 48211 Реальная длительность запуска субагента
totalToolUseCount number 7 Количество вызовов инструментов, сделанных субагентом
usage object {"input_tokens": 8320, ...} Разбивка токенов последнего запроса API по типам: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens

В 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 указывает модель, использовавшуюся в момент перехода агента в фон, поэтому замена, произошедшая до перевода в фон, в нём отражается. Для modelsUsed и поведения resolvedModel на момент перевода в фон требуется Claude Code v2.1.212 или новее.

AskUserQuestion

Задаёт пользователю от одного до четырёх вопросов с вариантами ответа.

Поле Тип Пример Описание
questions array [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}] Вопросы для показа, каждый со строкой question, коротким header, массивом options и необязательным флагом multiSelect
answers object {"Which framework?": "React"} Необязательно. Сопоставляет текст вопроса с меткой выбранного варианта. В ответах с множественным выбором метки объединяются через запятую. Claude не задаёт это поле; передайте его через updatedInput, чтобы ответить программно
ExitPlanMode

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

Поле Тип Пример Описание
plan string "## Refactor auth\n1. Extract..." Содержимое плана в Markdown. Внедряется из файла плана на диске
planFilePath string "/Users/.../plans/refactor-auth.md" Путь к файлу плана. Внедряется
allowedPrompts array [{"tool": "Bash", "prompt": "run tests"}] Устаревшее. Claude Code принимает поле, но игнорирует его. До v2.1.205 оно содержало разрешения на основе промптов, которые Claude запрашивал для реализации плана

В PostToolUse поле tool_response — это объект с полями plan и filePath, содержащими утверждённый план, а также внутренними флагами состояния. Читайте tool_response.plan для получения содержимого плана, а не перечитывайте файл с диска.

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

Хуки PreToolUse могут управлять тем, выполняется ли вызов инструмента. В отличие от других хуков, использующих поле decision верхнего уровня, PreToolUse возвращает своё решение внутри объекта hookSpecificOutput. Это даёт ему более широкие возможности управления: четыре исхода (разрешить, запретить, запросить подтверждение или отложить) и возможность изменить входные данные инструмента перед выполнением.

Поле Описание
permissionDecision "allow" пропускает запрос разрешения, кроме действий, которые не подтверждает автоматически ни один режим, и кроме AskUserQuestion и ExitPlanMode, которым нужен updatedInput в паре с ним. "deny" предотвращает вызов инструмента. "ask" просит пользователя подтвердить. "defer" корректно завершает работу, чтобы инструмент можно было возобновить позже. Правила запрета и запроса подтверждения всё равно применяются независимо от того, что возвращает хук
permissionDecisionReason Для "ask" показывается пользователю в запросе разрешения. Когда Claude Code отклоняет вызов в запуске с -p, где никто не может ответить на этот запрос, Claude вместо этого читает причину в результате инструмента. Для "deny" показывается Claude. Для "allow" и "defer" записывается только в отладочный лог
updatedInput Изменяет входные параметры инструмента перед выполнением. Заменяет весь объект входных данных, поэтому включайте неизменённые поля вместе с изменёнными. Claude Code проверяет правила разрешений и возможность автоматического перевода в фон команды Bash по входным данным, возвращённым вашим хуком, а не по тем, что отправил Claude. Используйте вместе с "allow" для автоматического подтверждения или с "ask", чтобы показать пользователю изменённые входные данные. Для "defer" игнорируется
additionalContext Строка, добавляемая в контекст Claude вместе с результатом инструмента. Игнорируется, когда permissionDecision равно "defer". См. Добавление контекста для Claude

Когда несколько хуков PreToolUse возвращают разные решения, приоритет таков: deny > defer > ask > allow.

Хук, блокирующий с кодом выхода 2, обрабатывается так же, как "deny": Claude видит сообщение из stderr как причину запрета.

Когда хук возвращает "ask", запрос разрешения, показываемый пользователю, содержит метку, указывающую, откуда взялся хук: [settings] для хука из любого файла настроек или из frontmatter агента, [plugin:<name>] для хука плагина или [skill] для хука из frontmatter скилла. Это помогает пользователям понять, какой источник конфигурации запрашивает подтверждение.

"ask" от хука также принудительно вызывает запрос разрешения в авторежиме: классификатор по-прежнему может запретить вызов инструмента, но не может молча его одобрить. До v2.1.211 классификатор мог одобрить команду Bash, выполняемую вне песочницы, не показывая запрошенный хуком запрос; при этом классификатор всё равно применял к этой команде собственные правила безопасности, а "deny" от хука всегда соблюдался.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}

В неинтерактивном режиме с флагом -p Claude Code предлагает AskUserQuestion и ExitPlanMode только если у запуска есть хост разрешений для получения запроса, например callback canUseTool в Agent SDK. Этим инструментам требуется взаимодействие с пользователем. Возврат permissionDecision: "allow" вместе с updatedInput удовлетворяет это требование: хук читает входные данные инструмента из stdin, получает ответ через ваш собственный интерфейс и возвращает его в updatedInput, чтобы инструмент выполнился без запроса. Одного "allow" для этих инструментов недостаточно. Для AskUserQuestion верните исходный массив questions и добавьте объект answers, сопоставляющий текст каждого вопроса с выбранным ответом.

MCP-инструмент, который его сервер помечает с помощью _meta["anthropic/requiresUserInteraction"], строже: хук не может пропустить его запрос подтверждения с помощью "allow", с updatedInput или без него, потому что Claude Code не может убедиться, что хук получил необходимое инструменту взаимодействие.

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

"defer" предназначено для интеграций, которые запускают claude -p как подпроцесс и читают его вывод JSON, например приложения на Agent SDK или пользовательского интерфейса, построенного поверх Claude Code. Оно позволяет вызывающему процессу приостановить Claude на вызове инструмента, получить ввод через собственный интерфейс и продолжить с того же места. Claude Code учитывает это значение только в неинтерактивном режиме с флагом -p. В интерактивных сессиях он записывает в лог предупреждение и игнорирует результат хука.

Типичный случай — инструмент 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 инструмента. input — это параметры, сгенерированные Claude для вызова инструмента и зафиксированные до выполнения:

{
  "type": "result",
  "subtype": "success",
  "stop_reason": "tool_deferred",
  "session_id": "abc123",
  "deferred_tool_use": {
    "id": "toolu_01abc",
    "name": "AskUserQuestion",
    "input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React"}, {"label": "Vue"}], "multiSelect": false }] }
  }
}

Ограничений по таймауту или количеству повторных попыток нет. Сессия остаётся на диске, пока вы её не возобновите, с учётом очистки по сроку хранения cleanupPeriodDays, которая по умолчанию удаляет файлы сессий через 30 дней согласно правилам очистки по сроку хранения. Если ответ не готов к моменту возобновления, хук может снова вернуть "defer", и процесс завершится так же. Вызывающий процесс сам решает, когда выйти из цикла, в итоге возвращая из хука "allow" или "deny".

"defer" работает, только когда Claude делает в ходе один вызов инструмента. Если Claude делает несколько вызовов инструментов одновременно, "defer" игнорируется с предупреждением, и инструмент проходит обычный процесс проверки разрешений. Это ограничение существует потому, что при возобновлении можно повторно выполнить только один инструмент: нельзя отложить один вызов из пакета, не оставив остальные неразрешёнными.

Если отложенный инструмент при возобновлении больше недоступен, процесс завершается с stop_reason: "tool_deferred_unavailable" и is_error: true до срабатывания хука. Это происходит, когда MCP-сервер, предоставлявший инструмент, не подключён в возобновлённой сессии. Данные deferred_tool_use всё равно включаются, чтобы вы могли определить, какой инструмент пропал.

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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    {
      "type": "addRules",
      "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
      "behavior": "allow",
      "destination": "localSettings"
    }
  ]
}

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

Хуки PermissionRequest могут разрешать или запрещать запросы разрешений. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может вернуть объект decision со следующими полями, специфичными для события:

Поле Описание
behavior "allow" предоставляет разрешение, "deny" отклоняет его. Правила запрета и запроса подтверждения всё равно применяются, поэтому хук, возвращающий "allow", не переопределяет соответствующее правило запрета
updatedInput Только для "allow": изменяет входные параметры инструмента перед выполнением. Заменяет весь объект входных данных, поэтому включайте неизменённые поля вместе с изменёнными. Изменённые входные данные повторно проверяются по правилам запрета и запроса подтверждения
updatedPermissions Только для "allow": массив записей обновления разрешений для применения, например добавление правила разрешения или смена режима разрешений сессии
message Только для "deny": сообщает Claude, почему в разрешении отказано
interrupt Только для "deny": если true, останавливает Claude

Хук, завершившийся с кодом выхода 2 без объекта decision, оставляет процесс проверки разрешений без изменений, а его stderr отбрасывается. Предоставить или отклонить запрос может только объект decision.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

Записи обновления разрешений

Поле вывода updatedPermissions и входное поле permission_suggestions используют один и тот же массив объектов-записей. У каждой записи есть type, определяющий её остальные поля, и destination, управляющий тем, куда записывается изменение.

type Поля Действие
addRules rules, behavior, destination Добавляет правила разрешений. rules — массив объектов {toolName, ruleContent?}. Не указывайте ruleContent, чтобы охватить весь инструмент. behavior — "allow", "deny" или "ask"
replaceRules rules, behavior, destination Заменяет все правила заданного behavior в destination переданными rules
removeRules rules, behavior, destination Удаляет соответствующие правила заданного behavior
setMode mode, destination Меняет режим разрешений. Допустимые режимы: default, auto, acceptEdits, dontAsk, bypassPermissions, plan и manual как псевдоним для default. Для псевдонима manual требуется Claude Code v2.1.200 или новее
addDirectories directories, destination Добавляет рабочие каталоги. directories — массив строк путей
removeDirectories directories, destination Удаляет рабочие каталоги

Поле destination в каждой записи определяет, остаётся ли изменение в памяти или сохраняется в файл настроек.

destination Куда записывается
session только в памяти, отбрасывается при завершении сессии
localSettings .claude/settings.local.json
projectSettings .claude/settings.json
userSettings ~/.claude/settings.json

Хук может вернуть одно из полученных permission_suggestions в качестве собственного вывода updatedPermissions.

PostToolUse

Запускается сразу после успешного завершения инструмента.

Сопоставляется по имени инструмента, значения те же, что и для PreToolUse.

Используйте более широкое сопоставление, когда имя инструмента не подходит в качестве фильтра:

  • Чтобы запускать хук после успешного завершения любого инструмента, опустите matcher или задайте ему значение "*". Тогда ваш хук сможет сам определить, что изменилось, например выполнив git status --porcelain, который также показывает неотслеживаемые файлы, пропускаемые git diff. Для вызовов инструментов, завершившихся сбоем, добавьте тот же хук в PostToolUseFailure.
  • Чтобы запускать хук при изменении определённого файла на диске, независимо от того, что его записало, используйте FileChanged. Claude Code не запускает хук PostToolUse, сопоставленный с Edit|Write, когда тот же файл перезаписывает команда Bash или процесс вне Claude Code.

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

Хуки PostToolUse срабатывают после того, как инструмент уже успешно выполнился. Входные данные включают как tool_input — аргументы, переданные инструменту, так и tool_response — возвращённый им результат. Точная схема обоих зависит от инструмента. Пути в tool_input файловых инструментов поступают в том же формате, что и для PreToolUse: всегда абсолютные, с нативными разделителями платформы, то есть с обратными слешами в Windows. Для MCP-инструмента входные данные также содержат объект mcp_server.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "type": "create"
  },
  "tool_use_id": "toolu_01ABC123...",
  "duration_ms": 12
}
Поле Описание
duration_ms Необязательное. Время выполнения инструмента в миллисекундах. Не включает время, проведённое в запросах разрешений и хуках PreToolUse

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

Хуки PostToolUse могут передавать Claude обратную связь после выполнения инструмента. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может возвращать следующие поля, специфичные для события:

Поле Описание
decision "block" добавляет reason рядом с результатом инструмента. Claude по-прежнему видит исходный вывод; чтобы заменить его, используйте updatedToolOutput
reason Пояснение, показываемое Claude, когда decision равно "block"
additionalContext Строка, добавляемая в контекст Claude вместе с результатом инструмента. См. Добавление контекста для Claude
classifierContext Короткая заметка о результате этого вызова для классификатора авторежима, а не для Claude. См. Аннотирование результата для классификатора авторежима. Требуется Claude Code v2.1.236 или новее
updatedToolOutput Заменяет вывод инструмента указанным значением перед отправкой Claude. Значение должно соответствовать форме вывода инструмента
updatedMCPToolOutput Заменяет вывод только для MCP-инструментов. Предпочтительнее использовать updatedToolOutput, который работает для всех инструментов

Пример ниже заменяет вывод вызова Bash. Значение замены соответствует форме вывода инструмента Bash:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude",
    "updatedToolOutput": {
      "stdout": "[redacted]",
      "stderr": "",
      "interrupted": false,
      "isImage": false
    }
  }
}

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

Верните classifierContext, чтобы отправить короткую заметку о результате вызова инструмента классификатору авторежима, а не Claude. Классификатор никогда не получает сами результаты инструментов, поэтому это поле — поддерживаемый способ сообщить ему что-либо о том, что вернул вызов, прежде чем он проверит последующие действия. Для этого поля требуется Claude Code v2.1.236 или новее.

Пример ниже сообщает классификатору, откуда взялся вывод запроса:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "classifierContext": "This query ran against the staging database, not production."
  }
}

Какой вес классификатор придаёт заметке, зависит от того, где вы настроили хук:

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

Claude Code применяет следующие ограничения при доставке заметки:

  • Длина: Claude Code ограничивает заметки для одного вызова инструмента 2 000 символами и обрезает остальное. Ограничение общее для всех хуков, отвечающих на этот вызов
  • Только синхронные ответы: Claude Code игнорирует это поле в ответе хука, который выполняется в фоне, поскольку такой ответ приходит после того, как Claude Code записывает результат инструмента
  • Вызовы, которые классификатор не записывает: транскрипт классификатора не включает операции только для чтения, такие как чтение файлов и поиск. Claude Code отбрасывает заметку, прикреплённую к одному из таких вызовов
  • Взаимодействие с перезаписью: когда заметка описывает вывод, который вы заменяете с помощью updatedToolOutput, верните оба поля в одном ответе хука. Claude Code отбрасывает заметку, если эта перезапись отклонена или её заменяет перезапись другого хука. Claude Code доставляет заметку, возвращённую без перезаписи, даже когда другой хук перезаписывает вывод

PostToolUseFailure

Запускается, когда инструмент, начавший выполнение, завершается сбоем: инструмент выбросил ошибку или MCP-инструмент вернул результат с ошибкой. Используйте его для записи сбоев в лог, отправки оповещений или передачи Claude корректирующей обратной связи.

Сопоставляется по имени инструмента, значения те же, что и для PreToolUse.

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

Хуки PostToolUseFailure получают те же поля tool_name и tool_input, что и PostToolUse, а также информацию об ошибке в виде полей верхнего уровня. Для MCP-инструмента они также получают объект mcp_server. Например, неудачная команда npm test может передать:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Exit code 1\nError: Cannot find module 'express'",
  "is_interrupt": false,
  "duration_ms": 4187
}
Поле Описание
error Строка, описывающая, что пошло не так. Формат зависит от инструмента, завершившегося сбоем
is_interrupt Необязательное логическое значение. True, когда сбой достиг Claude Code как прерывание, а не как ошибка, о которой сообщил инструмент. Отмена выполняющегося инструмента не вызывает этот хук; вместо этого результат инструмента содержит сообщение о прерывании
duration_ms Необязательное. Время выполнения инструмента в миллисекундах. Не включает время, проведённое в запросах разрешений и хуках PreToolUse

Строка 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, доступных всем хукам, ваш скрипт хука может возвращать следующие поля, специфичные для события:

Поле Описание
additionalContext Строка, добавляемая в контекст Claude вместе с ошибкой. См. Добавление контекста для Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}

PostToolBatch

Запускается один раз после того, как разрешились все вызовы инструментов в пакете, до того как Claude Code отправит следующий запрос модели. PostToolUse срабатывает один раз для каждого инструмента, то есть срабатывает параллельно, когда Claude выполняет параллельные вызовы инструментов. PostToolBatch срабатывает ровно один раз с полным пакетом, поэтому это подходящее место для внедрения контекста, который зависит от набора выполненных инструментов, а не от какого-то одного инструмента. Для этого события matcher не поддерживается.

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

Помимо общих входных полей, хуки PostToolBatch получают tool_calls — массив, описывающий каждый вызов инструмента в пакете:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolBatch",
  "tool_calls": [
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/accounts.py"},
      "tool_use_id": "toolu_01...",
      "tool_response": "1\tfrom __future__ import annotations\n2\t..."
    },
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/transactions.py"},
      "tool_use_id": "toolu_02...",
      "tool_response": "1\tfrom __future__ import annotations\n2\t..."
    }
  ]
}

tool_response содержит то же содержимое, которое модель получает в соответствующем блоке tool_result. Значение — сериализованная строка или массив блоков содержимого, в точности как их выдал инструмент. Для Read это означает текст с префиксами номеров строк, а не необработанное содержимое файла. Ответы могут быть большими, поэтому разбирайте только нужные поля.

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

Хуки PostToolBatch могут внедрять контекст для Claude. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может возвращать следующие поля, специфичные для события:

Поле Описание
additionalContext Строка контекста, внедряемая один раз перед следующим вызовом модели. Подробности доставки, что в неё помещать и как возобновлённые сессии обрабатывают прошлые значения, см. в разделе Добавление контекста для Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolBatch",
    "additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete."
  }
}

Возврат 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "auto",
  "hook_event_name": "PermissionDenied",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build",
    "description": "Clean build directory"
  },
  "tool_use_id": "toolu_01ABC123...",
  "reason": "[Irreversible Local Destruction]"
}
Поле Описание
reason Причина отказа. Для вердикта классификатора в большинстве сессий она называет сработавшее правило в квадратных скобках, например [Data Exfiltration]; другие формы см. в разделе Просмотр отказов. Для отказа без вердикта она начинается с Auto mode could not evaluate this action and is blocking it for safety. Для отказа из-за недоступности модели классификатора это фиксированный текст Classifier unavailable

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

Хуки PermissionDenied могут сообщить модели, что она может повторить попытку отклонённого вызова инструмента. Верните JSON-объект с hookSpecificOutput.retry, равным true:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionDenied",
    "retry": true
  }
}

Когда retry равно true, Claude Code добавляет в диалог сообщение, сообщающее модели, что она может повторить попытку вызова инструмента. Сам отказ Claude Code не отменяет. Если ваш хук не возвращает JSON или возвращает retry: false, отказ остаётся в силе, и модель получает исходное сообщение об отклонении.

Claude Code игнорирует retry: true, когда классификатор не вынес вердикта по действию: его ответ не удалось разобрать, или проверка безопасности, отдельная от авторежима, отклонила собственный запрос классификатора. Для таких отказов Claude Code уже сообщает модели в сообщении об отклонении, следует ли повторить попытку позже или двигаться дальше.

Notification

Запускается, когда Claude Code отправляет уведомления. Сопоставляется по типу уведомления. Опустите matcher, чтобы запускать хуки для всех типов уведомлений.

Вы получаете эти события хуков даже при отключённых уведомлениях рабочего стола: настройка preferredNotifChannel, включая notifications_disabled, меняет только способ оповещения, но не то, запускается ли ваш хук.

Matcher Когда срабатывает
permission_prompt Claude нужно, чтобы вы подтвердили использование инструмента или сетевой запрос команды в песочнице, и запрос ожидает около шести секунд
idle_prompt Claude закончил отвечать около 60 секунд назад, и с тех пор вы ничего не вводили
auth_success Аутентификация завершена
elicitation_dialog MCP-сервер открывает форму запроса данных, и вы ничего не вводили около шести секунд
elicitation_url_dialog MCP-сервер просит вас открыть URL в браузере, и вы ничего не вводили около шести секунд
elicitation_complete MCP-сервер сообщает, что запрос данных в режиме URL завершён
elicitation_response Ответ на запрос данных MCP отправляется обратно на сервер
agent_needs_input Фоновая сессия начинает ожидать вашего ввода, пока в терминале открыт вид агентов. Также срабатывает, когда терминальная сессия показывает вам вопрос участника команды агентов о настройке терминала или уведомление авторежима о плате за запросы классификатора, и вы ничего не вводили около шести секунд
agent_completed Фоновая сессия завершается или завершается сбоем. Срабатывает, только пока в терминале открыт вид агентов
quota_auto_resume_fired Claude Code продолжает вашу задачу после того, как лимит использования claude.ai приостановил её: в момент сброса или раньше, когда что-то, что вы делаете в Claude Code во время ожидания, например добавление кредитов использования, повышение тарифного плана или смена модели, снова делает использование доступным, с исключением для настройки модели
quota_auto_resume_stale Лимит использования claude.ai сбросился, пока ваш компьютер находился в спящем режиме более 30 минут. Claude Code ждёт, пока вы нажмёте Enter, вместо того чтобы продолжить. После более короткого сна он продолжает работу и вместо этого вызывает quota_auto_resume_fired
quota_auto_resume_disabled Claude Code завершает ожидание лимита использования claude.ai, не продолжая вашу задачу: autoContinueAtUsageLimit отключена или сброс сместился более чем на 24 часа во время ожидания, которое Claude Code начал самостоятельно, продолженная задача продолжала упираться в лимит, или продолжение было заблокировано до того, как достигло модели. Не срабатывает, когда вы нажимаете Esc или Ctrl+C либо выбираете Don't continue automatically

Для типов quota_auto_resume_fired, quota_auto_resume_stale и quota_auto_resume_disabled требуется Claude Code v2.1.234 или новее.

В терминальных сессиях для permission_prompt при сетевом запросе команды в песочнице требуется Claude Code v2.1.246 или новее.

Для agent_needs_input при вопросе участника команды о настройке терминала требуется Claude Code v2.1.248 или новее.

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 простаивает:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

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

Помимо общих входных полей, хуки Notification получают message с текстом уведомления, необязательное title и notification_type, указывающее, какой тип сработал.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}

Хуки 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}

Хуки SubagentStart не могут блокировать создание субагента, но могут внедрять в него контекст. Помимо полей вывода JSON, доступных всем хукам, вы можете вернуть:

Поле Описание
additionalContext Строка, добавляемая в контекст субагента в начале его диалога, перед первым промптом. См. Добавление контекста для Claude
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}

Когда хук снова запускается для того же субагента, 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, сопоставленный с SubagentHandback, получает как tool_input.message.

Хуки SubagentStop также получают массивы background_tasks и session_crons, описанные в разделе Входные данные Stop. Оба массива относятся к родительской сессии, а не к субагенту.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues...",
  "background_tasks": [],
  "session_crons": []
}

Хуки 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "TaskCreated",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "session-a1b2c3d4"
}
Поле Описание
task_id Идентификатор создаваемой задачи
task_subject Заголовок задачи
task_description Подробное описание задачи. Может отсутствовать
teammate_name Имя участника команды, создающего задачу. Может отсутствовать
team_name Устаревшее. Имя команды, производное от сессии; будет удалено в будущем выпуске

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

Хук TaskCreated может заблокировать создание двумя способами. В любом случае Claude Code удаляет задачу и возвращает ваше сообщение Claude в качестве ошибки инструмента. Claude Code игнорирует continue: false от этого события, и Claude продолжает работу.

  • Код выхода 2: Claude Code возвращает текст stderr в качестве сообщения.
  • JSON {"decision": "block", "reason": "..."}: Claude Code возвращает reason в качестве сообщения.

Этот пример блокирует задачи, заголовки которых не соответствуют требуемому формату:

#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
  echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
  exit 2
fi

exit 0

TaskCompleted

Запускается, когда задача помечается как выполненная. Это происходит в двух ситуациях: когда любой агент явно помечает задачу как выполненную с помощью инструмента TaskUpdate или когда участник команды агентов завершает свой ход с задачами в процессе выполнения. Используйте его для соблюдения критериев завершения, таких как прохождение тестов или проверок линтера, прежде чем задачу можно будет закрыть.

Хуки TaskCompleted не поддерживают matcher и срабатывают при каждом возникновении события.

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

Помимо общих входных полей, хуки TaskCompleted получают task_id, task_subject и, необязательно, task_description, teammate_name и team_name.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "session-a1b2c3d4"
}
Поле Описание
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 по-прежнему блокирует завершение.

Этот пример запускает тесты и блокирует завершение задачи, если они не проходят:

#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

# Run the test suite
if ! npm test 2>&1; then
  echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
  exit 2
fi

exit 0

Stop

Запускается, когда основной агент Claude Code закончил отвечать. Не запускается, если остановка произошла из-за прерывания пользователем. При ошибках API вместо этого срабатывает StopFailure.

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

Помимо общих входных полей, хуки Stop получают stop_hook_active, last_assistant_message, background_tasks и session_crons. Поле stop_hook_active равно true, когда Claude Code уже продолжает работу в результате хука остановки. Проверяйте это значение или обрабатывайте транскрипт, чтобы избежать блокировки по условию, которое никогда не разрешится. Claude Code применяет ограничение в 8 последовательных продолжений: после того как хуки остановки продолжили ход восемь раз подряд, Claude Code переопределяет следующую блокировку и завершает ход. Чтобы повысить ограничение, задайте CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.

Поле last_assistant_message содержит текстовое содержимое последнего ответа Claude, поэтому хуки могут получить к нему доступ без разбора файла транскрипта. Для хуков, которые действуют по только что завершённому ходу, например хуков чтения вслух или уведомлений, используйте это поле, а не чтение transcript_path: не во всех версиях гарантируется, что файл транскрипта содержит последнее сообщение в момент Stop.

Массивы background_tasks и session_crons позволяют хукам отличать «сессия завершена» от «сессия приостановлена в ожидании, пока фоновая работа снова её разбудит». Оба массива присутствуют, когда реестр задач доступен, и пусты, когда ничего не выполняется и не запланировано.

Каждая запись в background_tasks описывает одну выполняющуюся задачу и использует следующие поля:

Поле Описание
id Идентификатор задачи
type Понятная метка типа задачи, например shell, subagent, monitor, workflow, teammate, cloud session или MCP task. Каждая метка указывает, какая функция Claude Code создала задачу. Для нераспознанных типов используется необработанный дискриминант
status Текущий статус задачи
description Произвольное текстовое описание, ограниченное 1000 символами, с маркером … [+N chars] внутри строки при обрезке
command Командная строка оболочки, ограниченная 1000 символами. Присутствует только для задач shell
agent_type Имя типа субагента. Присутствует только для задач subagent
server Имя MCP-сервера. Присутствует только для задач monitor и MCP task
tool Имя MCP-инструмента. Присутствует только для задач monitor и MCP task
name Имя workflow. Присутствует только для задач workflow

Каждая запись в session_crons описывает одно запланированное пробуждение с областью действия сессии, полученное из CronCreate, ScheduleWakeup и /loop:

Поле Описание
id Идентификатор cron-задачи
schedule Cron-выражение, например 0 9 * * 1-5
recurring false для однократных пробуждений, расписание которых задаёт одно время срабатывания, true для задач, которые срабатывают повторно при каждом совпадении
prompt Промпт, отправляемый при срабатывании cron, ограниченный 1000 символами с тем же маркером … [+N chars]

Этот пример показывает входные данные Stop с одной выполняющейся задачей оболочки и одним повторяющимся cron:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true,
  "last_assistant_message": "I've completed the refactoring. Here's a summary...",
  "background_tasks": [
    {
      "id": "task-001",
      "type": "shell",
      "status": "running",
      "description": "tail logs",
      "command": "tail -f /var/log/syslog"
    }
  ],
  "session_crons": [
    {
      "id": "cron-001",
      "schedule": "0 9 * * 1-5",
      "recurring": true,
      "prompt": "check the build"
    }
  ]
}

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

Хуки Stop и SubagentStop могут управлять тем, продолжает ли Claude работу. Помимо полей вывода JSON, доступных всем хукам, ваш скрипт хука может возвращать следующие поля, специфичные для события:

Поле Описание
decision "block" не даёт Claude остановиться. Опустите, чтобы разрешить Claude остановиться
reason Обязательно, когда decision равно "block". Сообщает Claude, почему он должен продолжить
hookSpecificOutput.additionalContext Обратная связь для Claude без ошибки. Диалог продолжается, чтобы Claude мог действовать на её основе, но, в отличие от decision: "block", она отображается в транскрипте как обратная связь хука, а не как ошибка хука

Хук, который блокирует с кодом выхода 2, обрабатывается так же, как reason: Claude получает сообщение stderr в качестве объяснения, почему он должен продолжить.

{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

Используйте additionalContext, когда хук работает как задумано и даёт Claude указания, например «запусти набор тестов перед завершением». Он продолжает диалог с теми же защитами от зацикливания, что и decision: "block", а именно входным полем stop_hook_active и ограничением в 8 последовательных продолжений, но транскрипт помечает его как Stop hook feedback, и уведомление об ошибке хука не показывается:

{
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "Please run the test suite before finishing"
  }
}

StopFailure

Запускается вместо Stop, когда ход завершается из-за ошибки API. Claude Code игнорирует вывод и код выхода хука, за исключением terminalSequence. Используйте его для записи сбоев в лог, отправки оповещений или выполнения действий по восстановлению, когда Claude не может завершить ответ из-за ограничений частоты запросов, проблем с аутентификацией или других ошибок API.

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

Помимо общих входных полей, хуки StopFailure получают error, необязательное error_details и необязательное last_assistant_message. Поле error определяет тип ошибки и используется для фильтрации matcher.

Поле Описание
error Тип ошибки: rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error или unknown
error_details Дополнительные сведения об ошибке, если они доступны
last_assistant_message Отображаемый текст ошибки, показанный в диалоге. В отличие от Stop и SubagentStop, где это поле содержит разговорный вывод Claude, для StopFailure оно содержит саму строку ошибки API, например "API Error: Rate limit reached"
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "StopFailure",
  "error": "rate_limit",
  "error_details": "429 Too Many Requests",
  "last_assistant_message": "API Error: Rate limit reached"
}

Хуки StopFailure не имеют управления решениями. Они запускаются только для уведомлений и логирования.

TeammateIdle

Запускается, когда участник команды агентов собирается перейти в режим простоя после завершения своего хода. Используйте его для применения контроля качества до того, как участник команды прекратит работу, например требуя прохождения проверок линтера или проверяя наличие выходных файлов.

Хуки TeammateIdle не поддерживают matcher и срабатывают при каждом возникновении события.

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

Помимо общих входных полей, хуки TeammateIdle получают teammate_name и team_name.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "session-a1b2c3d4"
}
Поле Описание
teammate_name Имя участника команды, который собирается перейти в режим простоя
team_name Устаревшее. Имя команды, производное от сессии; будет удалено в будущем выпуске

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

Хуки TeammateIdle поддерживают два способа управления поведением участника команды:

  • Код выхода 2: участник команды получает сообщение stderr в качестве обратной связи и продолжает работу вместо перехода в режим простоя.
  • JSON {"continue": false, "stopReason": "..."}: полностью останавливает участника команды, аналогично поведению хука Stop. stopReason показывается пользователю.

Этот пример проверяет наличие артефакта сборки, прежде чем разрешить участнику команды перейти в режим простоя:

#!/bin/bash

if [ ! -f "./dist/output.js" ]; then
  echo "Build artifact missing. Run the build before stopping." >&2
  exit 2
fi

exit 0

ConfigChange

Запускается, когда файл конфигурации изменяется во время сессии. Используйте его для аудита изменений настроек, применения политик безопасности или блокировки несанкционированных изменений файлов конфигурации.

Claude Code запускает хуки ConfigChange, когда изменяется файл настроек, файл управляемой политики или файл скилла. Для управляемой политики он запускает их, только когда изменяется managed-settings.json или файл в managed-settings.d/. Настройки, управляемые сервером, и изменения управляемых настроек macOS или политики реестра Windows он применяет без запуска хуков. В WSL с wslInheritsWindowsSettings он также применяет изменённый файл управляемых настроек на стороне Windows при опросе политики, не запуская хуки.

Matcher фильтрует по источнику конфигурации:

Matcher Когда срабатывает
user_settings Изменяется ~/.claude/settings.json
project_settings Изменяется .claude/settings.json
local_settings Изменяется .claude/settings.local.json
policy_settings Изменяется managed-settings.json или файл в managed-settings.d/
skills Изменяется файл скилла в .claude/skills/

Этот пример записывает в лог все изменения конфигурации для аудита безопасности:

{
  "hooks": {
    "ConfigChange": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

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

Помимо общих входных полей, хуки ConfigChange получают source и, необязательно, file_path. Поле source указывает, какой тип конфигурации изменился, а file_path содержит путь к конкретному изменённому файлу.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ConfigChange",
  "source": "project_settings",
  "file_path": "/Users/.../my-project/.claude/settings.json"
}

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

Хуки ConfigChange могут блокировать вступление изменений конфигурации в силу. Используйте код выхода 2 или JSON-поле decision, чтобы предотвратить изменение. При блокировке новые настройки не применяются к работающей сессии.

Поле Описание
decision "block" предотвращает применение изменения конфигурации. Опустите, чтобы разрешить изменение
reason Принимается, но никогда не показывается
{
  "decision": "block",
  "reason": "Configuration changes to project settings require admin approval"
}

Изменения 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project/src",
  "hook_event_name": "CwdChanged",
  "old_cwd": "/Users/my-project",
  "new_cwd": "/Users/my-project/src"
}

Вывод CwdChanged

Помимо полей вывода JSON, доступных всем хукам, хуки CwdChanged могут возвращать watchPaths, чтобы динамически задавать, за какими путями файлов следит FileChanged:

Поле Описание
watchPaths Массив абсолютных путей. Заменяет текущий динамический список наблюдения. Пути из вашей конфигурации matcher отслеживаются всегда. Возврат пустого массива очищает динамический список, что типично при входе в новый каталог

Хуки 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 фильтрует по способу добавления каталога:

Matcher Когда срабатывает
slash_command Вы добавляете каталог с помощью /add-dir
register_repo_root Клиент SDK добавляет каталог управляющим запросом register_repo_root

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

Помимо общих входных полей, хуки DirectoryAdded получают directory и source.

Поле Описание
directory Абсолютный путь к добавленному каталогу
source Способ добавления каталога: "slash_command" для /add-dir или "register_repo_root" для управляющего запроса SDK
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "DirectoryAdded",
  "directory": "/Users/my-other-repo",
  "source": "slash_command"
}

Хуки 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 или внешним скриптом:

{
  "hooks": {
    "FileChanged": [
      {
        "matcher": "data.csv",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/normalize-line-endings.sh"
          }
        ]
      }
    ]
  }
}

Хук считывает абсолютный путь изменённого файла из поля file_path входных данных JSON в stdin. Его защитная проверка grep ищет то же, что удаляет perl, — CR в конце строки, поэтому запуск после нормализации завершается, не трогая файл. Менее строгая проверка приводит к бесконечному циклу, потому что perl -i перезаписывает файл, даже если ничего не заменяет, а Claude Code снова запускает хук после каждой перезаписи. Сохраните этот скрипт по пути /path/to/normalize-line-endings.sh и сделайте его исполняемым:

#!/bin/bash
FILE=$(jq -r .file_path)
if grep -q $'\r$' "$FILE"; then
  perl -pi -e 's/\r$//' "$FILE"
fi

Чтобы убедиться, что хук работает, попросите 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.

Поле Описание
file_path Абсолютный путь к изменённому файлу
event Что произошло: "change" для изменённого файла, "add" для созданного файла или "unlink" для удалённого файла
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "FileChanged",
  "file_path": "/Users/my-project/.envrc",
  "event": "change"
}

Вывод FileChanged

Помимо полей вывода JSON, доступных всем хукам, хуки FileChanged могут возвращать watchPaths, чтобы динамически обновлять отслеживаемые пути файлов:

Поле Описание
watchPaths Массив абсолютных путей. Заменяет текущий динамический список наблюдения. Пути из вашей конфигурации matcher отслеживаются всегда. Используйте это, когда ваш скрипт хука обнаруживает дополнительные файлы для наблюдения на основе изменённого файла

Хуки FileChanged не имеют управления решениями. Они не могут предотвратить изменение файла.

Claude Code считывает watchPaths и systemMessage из их JSON-вывода и отбрасывает continue. В интерактивных сессиях он показывает systemMessage как краткое уведомление в терминале. Сообщение не попадает в поток сообщений SDK.

WorktreeCreate

Запускается при создании worktree — будь то из claude --worktree, из субагента, использующего isolation: "worktree", или для фоновой сессии, которую Claude Code изолирует в собственном 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 репозитория на свой:

{
  "hooks": {
    "WorktreeCreate": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
          }
        ]
      }
    ]
  }
}

Хук считывает name worktree из входных данных JSON в stdin, извлекает свежую копию в новый каталог и выводит путь к каталогу. echo в последней строке — это то, что Claude Code считывает как путь к worktree. Перенаправляйте любой другой вывод в stderr, чтобы он не мешал пути.

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

Помимо общих входных полей, хуки WorktreeCreate получают поле name. Это идентификатор-слаг для нового worktree, заданный пользователем или сгенерированный автоматически, например bold-oak-a3f2.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeCreate",
  "name": "feature-auth"
}

Вывод WorktreeCreate

Хуки WorktreeCreate не используют стандартную модель решений allow/block. Вместо этого результат определяется успехом или неудачей хука. Хук должен вернуть путь к созданному каталогу 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 за его пределы. В ошибке указывается отклонённый компонент. Возвращайте нормализованный путь, который не проходит через символическую ссылку внутри репозитория. До v2.1.216 создание worktree следовало по пути хука без этой проверки.

WorktreeRemove

Выполняется при удалении worktree. Это парный хук очистки для WorktreeCreate. Событие срабатывает, когда:

  • вы выходите из сессии --worktree и выбираете её удаление
  • завершается субагент с isolation: "worktree"
  • вы удаляете фоновую сессию, worktree которой создал хук

Для worktree на основе git Claude Code выполняет очистку автоматически с помощью git worktree remove. Если вы настроили хук WorktreeCreate, добавьте к нему хук WorktreeRemove, чтобы управлять очисткой создаваемых им worktree:

  • Нет хука WorktreeRemove: когда вы выходите из сессии --worktree и выбираете удаление, Claude Code использует как резервный вариант git worktree remove --force для пути, который вернул ваш хук WorktreeCreate, поэтому worktree, который распознаёт git, удаляется. Worktree, который git не распознаёт, например созданный вашим хуком с помощью системы контроля версий, отличной от git, остаётся на диске. О том, что происходит с созданным хуком worktree при удалении фоновой сессии, см. правила удаления в agent view.
  • Хук завершается с кодом 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, который всё ещё содержит файлы, хук выполняется только тогда, когда вы подтверждаете удаление в agent view; для такого worktree claude rm вместо этого сохраняет сессию и worktree. До v2.1.216 хук выполнялся для сохранённого пути без этих проверок.

Claude Code передаёт путь, возвращённый WorktreeCreate, как worktree_path во входных данных хука. Этот пример считывает этот путь и удаляет каталог:

{
  "hooks": {
    "WorktreeRemove": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
          }
        ]
      }
    ]
  }
}

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

Помимо общих полей входных данных, хуки WorktreeRemove получают поле worktree_path — абсолютный путь к удаляемому worktree.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeRemove",
  "worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}

Результат определяется кодом выхода хука WorktreeRemove. Когда хук завершается с ненулевым кодом и каталог по пути worktree_path после этого всё ещё существует, удаление завершается ошибкой:

  • Worktree остаётся на диске, а команда хука и stderr записываются в отладочный лог.
  • Если вы удаляли фоновую сессию, сессия тоже сохраняется. Сообщение об отказе в agent view сообщает, как завершился хук, например exited 1, цитирует начало его stderr и указывает, удалит ли повторное удаление сессии каталог в любом случае.

PreCompact

Выполняется перед тем, как Claude Code собирается выполнить операцию сжатия контекста.

Значение matcher указывает, было ли сжатие запущено вручную или автоматически:

Matcher Когда срабатывает
manual /compact
auto Автосжатие, когда диалог достигает окна автосжатия

Завершитесь с кодом 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": null
}

PostCompact

Выполняется после того, как Claude Code завершает операцию сжатия контекста. Используйте это событие, чтобы реагировать на новое сжатое состояние, например записывать в лог сгенерированную сводку или обновлять внешнее состояние. Claude Code отбрасывает поля systemMessage и continue хука PostCompact.

Применяются те же значения matcher, что и для PreCompact:

Matcher Когда срабатывает
manual После /compact
auto После автосжатия, когда диалог достигает окна автосжатия

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

Помимо общих полей входных данных, хуки PostCompact получают trigger и compact_summary. Поле compact_summary содержит сводку диалога, сгенерированную операцией сжатия.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}

Хуки 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:

{
"hooks": {
"PreModelSwitch": [
{
"matcher": "claude-opus-4-6",
"hooks": [
{
"type": "command",
"command": "jq -e '.to_model | test(\"opus-4-6\")' > /dev/null && { echo 'Opus 4.6 is retired for this project. Use a newer model.' >&2; exit 2; }; exit 0"
}
]
}
]
}
}

Чтобы убедиться, что хук работает, выполните /model claude-opus-4-6 из сессии, использующей другую модель. Claude Code сохраняет текущую модель и сообщает, что хук PreModelSwitch заблокировал переключение, указывая ваше сообщение в качестве причины.

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

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

Поле Тип Описание
from_model string Идентификатор модели, с которой выполняется переключение
to_model string Идентификатор модели, на которую выполняется переключение. Matcher сравнивается с каноническим именем этой модели
requested_model string или null Модель, указанная в запросе: псевдоним, например opus, полный идентификатор модели или null, если запрос был для модели по умолчанию
source string Откуда пришёл запрос: "command" для /model <name>, настройки Model в /config или включения быстрого режима; "picker" для средства выбора модели; "sdk" для запроса set_model или смены модели в запросе apply_flag_settings от хоста Agent SDK или Remote Control
context_tokens number Токены, которые следующий запрос повторно отправляет в качестве промпта: суммарно входные токены, токены чтения кэша, создания кэша и выходные токены последнего ответа в основном диалоге. 0 до первого ответа
prompt_cache_warm boolean Вероятно ли, что кэш промптов текущей модели всё ещё прогрет, то есть переключение приведёт к его потере
cache_ttl string Время жизни кэша промптов, запрашиваемое Claude Code для этой сессии: "5m" или "1h"
estimated_cache_write_usd number Оценочная стоимость в долларах США записи context_tokens в кэш промптов на to_model по тарифу cache_ttl, без учёта следующего ответа. Серверу может не потребоваться повторно кэшировать весь контекст, поэтому рассматривайте это как оценку
pricing string Как Claude Code рассчитал estimated_cache_write_usd: "configured" — по собственным тарифам вашей организации, если она их настроила, "catalog" — по прейскурантной цене, или "default", если для to_model нет известной цены и Claude Code использовал тариф по умолчанию

Этот пример показывает входные данные для /model opus в сессии, использующей Sonnet 5:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreModelSwitch",
  "from_model": "claude-sonnet-5",
  "to_model": "claude-opus-5",
  "requested_model": "opus",
  "source": "command",
  "context_tokens": 182340,
  "prompt_cache_warm": true,
  "cache_ttl": "5m",
  "estimated_cache_write_usd": 1.1396,
  "pricing": "catalog"
}

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

Хуки PreModelSwitch могут отменить переключение, попросить пользователя подтвердить его или разрешить его выполнение. Код выхода 2 или decision: "block" верхнего уровня отменяет переключение.

Для более тонкого управления возвращайте permissionDecision и permissionDecisionReason в объекте hookSpecificOutput, как в PreToolUse. PreModelSwitch принимает "allow", "deny" и "ask". Он не принимает "defer", updatedInput или additionalContext. В таблице ниже описаны оба поля:

Поле Описание
permissionDecision "allow" выполняет переключение и пропускает подтверждение, которое Claude Code показывает, пока кэш промптов прогрет. "deny" отменяет переключение. "ask" запрашивает у пользователя подтверждение
permissionDecisionReason Для "deny" показывается пользователю как причина блокировки переключения или возвращается как ошибка для запроса set_model. Для "ask" показывается в запросе подтверждения. Игнорируется для "allow"

Только /model в интерактивной сессии может показать запрос "ask". Во всех остальных интерфейсах, включая неинтерактивный режим с флагом -p, /config и запросы set_model, Claude Code рассматривает "ask" как отказ.

Этот пример просит пользователя подтвердить переключение и приводит количество токенов из context_tokens:

{
  "hookSpecificOutput": {
    "hookEventName": "PreModelSwitch",
    "permissionDecision": "ask",
    "permissionDecisionReason": "Switching now re-sends about 180k tokens to the new model. Continue?"
  }
}

Когда несколько хуков PreModelSwitch возвращают разные решения, приоритет таков: deny > ask > allow.

Claude Code показывает пользователю любое systemMessage, которое возвращает ваш хук, независимо от решения, поэтому хук для отчёта о стоимости может вернуть {"systemMessage": "..."} и завершиться с кодом 0.

Хук PreModelSwitch, который не отвечает до истечения таймаута, блокирует переключение. В PreToolUse, напротив, командный хук с истёкшим таймаутом позволяет вызову инструмента продолжиться. Таймаут по умолчанию для этого события — 30 секунд. PreModelSwitch запускает только хуки command, http и mcp_tool, поэтому значения по умолчанию для prompt и agent не применяются.

Хук, который завершается с кодом, отличным от 0 или 2, и не выводит JSON-решения, не блокирует переключение: Claude Code показывает его stderr и применяет переключение, как описано в разделе Другие коды выхода.

PostModelSwitch

Выполняется после смены модели сессии. Используйте его, чтобы давать Claude указания, специфичные для модели, без редактирования каждого CLAUDE.md, например инструкцию для всей организации, которая применяется к определённым моделям.

PostModelSwitch требует Claude Code v2.1.251 или новее. Он не может блокировать, поскольку модель уже сменилась. Claude Code запускает хуки PostModelSwitch после любого из следующих изменений:

Claude Code не запускает хуки PostModelSwitch, когда ход обслуживает модель из цепочки резервных моделей, поскольку такая замена длится один ход и оставляет модель сессии неизменной.

Matcher подчиняется тем же правилам, что и в PreModelSwitch: Claude Code сравнивает его с каноническим именем модели, на которую переключилась сессия.

Этот пример добавляет указания всякий раз, когда модель сессии меняется на любую модель Opus:

{
  "hooks": {
    "PostModelSwitch": [
      {
        "matcher": ".*opus.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'On Opus, delegate implementation work to subagents and keep this conversation for planning and review.'"
          }
        ]
      }
    ]
  }
}

Чтобы убедиться, что хук работает, переключитесь на модель 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-вывода, доступных всем хукам, вы можете вернуть:

Поле Описание
additionalContext Строка, добавляемая в контекст Claude со следующим запросом. См. Добавление контекста для Claude

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

SessionEnd

Выполняется при завершении сессии Claude Code. Полезен для задач очистки, записи в лог статистики сессии или сохранения состояния сессии. Поддерживает matcher для фильтрации по причине выхода.

Поле reason во входных данных хука указывает, почему завершилась сессия:

Причина Описание
clear Сессия очищена командой /clear
resume Сессия переключена через интерактивную /resume
logout Пользователь вышел из системы
prompt_input_exit Пользователь вышел, когда поле ввода промпта было видимо
other Другие причины выхода
bypass_permissions_disabled Удалено в v2.1.234; Claude Code его не отправляет. Уберите его из matcher ваших SessionEnd

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

Помимо общих полей входных данных, хуки SessionEnd получают поле reason, указывающее, почему завершилась сессия. Все значения см. в таблице причин выше.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

Хуки SessionEnd не имеют управления решениями. Они не могут заблокировать завершение сессии, но могут выполнять задачи очистки. Claude Code отбрасывает их поля JSON-вывода, такие как systemMessage.

Хуки SessionEnd имеют таймаут по умолчанию 1,5 секунды. Он применяется, когда вы выходите, выполняете /clear или переключаете сессии с помощью интерактивной /resume. Дать хуку больше времени можно двумя способами:

  • timeout для отдельного хука: задайте timeout в конфигурации этого хука. Общий лимит времени автоматически повышается до наибольшего значения timeout среди хуков в ваших файлах настроек, но не более 60 секунд. Если вы повышаете лимит таким образом, хук без собственного timeout по-прежнему сохраняет значение по умолчанию. Таймауты, заданные для хуков из плагинов, не повышают лимит.
  • CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: задайте эту переменную окружения в миллисекундах, чтобы явно переопределить лимит. Заданное значение также становится таймаутом для каждого хука без собственного timeout.

Этот пример устанавливает лимит в 5 секунд:

CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

До 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 в режиме формы, наиболее распространённого случая:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": {
      "username": { "type": "string", "title": "Username" }
    }
  }
}

Для elicitation в режиме URL, используемого для аутентификации через браузер:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please authenticate",
  "mode": "url",
  "url": "https://auth.example.com/login"
}

Вывод Elicitation

Чтобы ответить программно без показа диалогового окна, верните JSON-объект с hookSpecificOutput:

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": {
      "username": "alice"
    }
  }
}
Поле Значения Описание
action accept, decline, cancel Принять, отклонить или отменить запрос
content object Значения полей формы для отправки. Используется только когда action равно accept

Код выхода 2 отклоняет elicitation. Claude Code нигде не показывает ваше сообщение stderr.

Claude Code использует hookSpecificOutput из JSON-вывода хука Elicitation и отбрасывает systemMessage и continue.

ElicitationResult

Выполняется после того, как пользователь отвечает на elicitation MCP. Хуки могут наблюдать, изменять или блокировать ответ до его отправки обратно MCP-серверу.

Поле matcher сопоставляется с именем MCP-сервера.

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

Помимо общих полей входных данных, хуки ElicitationResult получают поля mcp_server_name, action и необязательные поля mode, elicitation_id и content.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ElicitationResult",
  "mcp_server_name": "my-mcp-server",
  "action": "accept",
  "content": { "username": "alice" },
  "mode": "form",
  "elicitation_id": "elicit-123"
}

Вывод ElicitationResult

Чтобы переопределить ответ пользователя, верните JSON-объект с hookSpecificOutput:

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline",
    "content": {}
  }
}
Поле Значения Описание
action accept, decline, cancel Переопределяет действие пользователя
content object Переопределяет значения полей формы. Имеет смысл только когда action равно accept

Код выхода 2 блокирует ответ, меняя фактическое действие на decline. Claude Code нигде не показывает ваше сообщение stderr.

Claude Code использует hookSpecificOutput из JSON-вывода хука ElicitationResult и отбрасывает systemMessage и continue.

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. Отправляют входные данные hook и вашу подсказку модели Claude, по умолчанию той, которую Claude Code использует для фоновой функциональности
  2. LLM отвечает структурированным JSON, содержащим решение
  3. Claude Code автоматически обрабатывает решение

Prompt hook configuration

Установите type на "prompt" и предоставьте строку prompt вместо command. Используйте заполнитель $ARGUMENTS для внедрения данных JSON входа hook в текст вашей подсказки.

Этот hook Stop просит LLM оценить, должен ли Claude остановиться перед разрешением Claude закончить:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
Поле Обязательно Описание
type да Должно быть "prompt"
prompt да Текст подсказки для отправки LLM. Используйте $ARGUMENTS как заполнитель для JSON входа hook. Если $ARGUMENTS отсутствует, JSON входа добавляется к подсказке
model нет Модель для использования при оценке. По умолчанию модель, которую Claude Code использует для фоновой функциональности
timeout нет Таймаут в секундах. По умолчанию: 30
continueOnBlock нет На событиях, к которым это применяется, true передаёт причину ok: false обратно Claude и продолжает вместо завершения хода. По умолчанию: false. См. Response schema для поведения для каждого события

Response schema

LLM должен ответить JSON, содержащим:

{
  "ok": true | false,
  "reason": "Explanation for the decision",
  "impossible": true | false
}
Поле Описание
ok true разрешает действие. Для false, см. поведение для каждого события ниже
reason Требуется при ok равном false
impossible Опционально. Модель возвращает его с ok: false, когда она судит, что условие никогда не может быть удовлетворено. На Stop и SubagentStop, Claude Code затем позволяет ходу закончиться вместо передачи причины обратно. Agent hooks и другие события игнорируют его

Что происходит при ok: false, зависит от события:

  • Stop и SubagentStop: причина передаётся обратно Claude как его следующая инструкция и ход продолжается, если только ответ также не устанавливает impossible: true, в этом случае Claude Code позволяет остановке и ход заканчивается
  • PreToolUse: вызов инструмента отклоняется; по умолчанию ход заканчивается и причина отказа появляется в чате как строка предупреждения. Установите continueOnBlock: true для возврата причины Claude как ошибки инструмента, чтобы он мог скорректировать и продолжить, эквивалентно permissionDecision: "deny" из command hook. До v2.1.210 причина отказа возвращалась Claude как ошибка инструмента и ход продолжался
  • 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 продолжает работать с предоставленной причиной как своей следующей инструкцией:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Agent-based hooks

Agent-based hooks (type: "agent") похожи на prompt-based hooks, но с многооборотным доступом к инструментам. Вместо одного вызова LLM, agent hook порождает subagent, который может читать файлы, искать код и проверять кодовую базу для проверки условий. Agent hooks поддерживают те же события, что и prompt-based hooks, за исключением PermissionRequest.

How agent hooks work

Когда срабатывает agent hook:

  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": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Запуск 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 выполняется. Когда скрипт завершается, его выход доставляется на следующий ход разговора:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

Как только асинхронный 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 ответа hook к Claude на следующем ходу разговора. В отличие от systemMessage синхронного hook, ни одно из этих полей не показывается вам.

Claude Code проверяет, что JSON ответ соответствует той же схеме выходных данных, что и синхронные hooks, и отбрасывает любое поле, значение которого имеет неправильный тип, например systemMessage, который не является строкой, вместо его доставки. Запустите с --debug для просмотра предупреждения, называющего каждое отброшенное поле. До версии v2.1.202 неправильно сформированный JSON выход из асинхронного hook мог привести к сбою сеанса, и сбой повторялся каждый раз при возобновлении сеанса.

Уведомления о завершении асинхронного hook подавляются по умолчанию. Чтобы их увидеть, включите подробный режим с помощью Ctrl+O или запустите Claude Code с --verbose.

Запуск тестов после изменения файлов

Этот hook запускает набор тестов в фоне всякий раз, когда Claude пишет файл, затем сообщает результаты обратно Claude при завершении тестов. Сохраните этот скрипт в .claude/hooks/run-tests-async.sh в вашем проекте и сделайте его исполняемым с помощью chmod +x:

#!/bin/bash
# run-tests-async.sh

# Read hook input from stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Only run tests for source files
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi

# Run tests and report results to Claude via additionalContext
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
  MSG="Tests passed after editing $FILE_PATH"
else
  MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'

Затем добавьте эту конфигурацию в .claude/settings.json в корне вашего проекта. Флаг async: true позволяет Claude продолжать работу, пока тесты запускаются:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
            "args": [],
            "async": true
          }
        ]
      }
    ]
  }
}

Ограничения

Асинхронные hooks имеют дополнительные ограничения по сравнению с синхронными hooks:

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

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "shell": "powershell",
            "command": "Write-Host 'File written'"
          }
        ]
      }
    ]
  }
}

Чтобы ссылаться на корневой каталог проекта из команды 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::

{
  "type": "command",
  "shell": "powershell",
  "command": "& \"$env:CLAUDE_PROJECT_DIR\\.claude\\hooks\\check.ps1\""
}

Debug hooks

Детали выполнения hooks записываются в файл отладочного журнала. Запустите Claude Code с claude --debug-file <path> для записи журнала в известное расположение, или запустите claude --debug и прочитайте журнал в ~/.claude/debug/<session-id>.txt. Флаг --debug не выводит на терминал.

Например, hook PostToolUse на Write, чья команда выводит hook-ran, создаёт записи вроде:

2026-07-19T02:03:24.382Z [DEBUG] Hook output does not start with {, treating as plain text
2026-07-19T02:03:24.382Z [DEBUG] "Hook PostToolUse:Write (PostToolUse) success:\nhook-ran"

Для более детальной информации о совпадении hooks установите CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose для просмотра дополнительных строк логирования, таких как количество совпадений фильтра hook и совпадение запроса.

Для устранения неполадок распространённых проблем, таких как hooks, которые не срабатывают, Stop hooks, которые продолжают блокировать, или ошибки конфигурации, см. Limitations and troubleshooting в руководстве. Для более широкого диагностического пошагового руководства, охватывающего /context, /doctor и приоритет параметров, см. Debug your config.