470| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |470| `ElicitationResult` | After a user responds to an MCP elicitation, before the response is sent back to the server |
471| `SessionEnd` | When a session terminates |471| `SessionEnd` | When a session terminates |
472 472
473Когда несколько hooks совпадают, каждый возвращает свой собственный результат. Для решений Claude Code выбирает наиболее ограничивающий ответ. Hook `PreToolUse`, возвращающий `deny`, отменяет вызов инструмента независимо от того, что возвращают остальные. Один hook, возвращающий `ask`, вынуждает запрос разрешения, даже если остальные возвращают `allow`. Текст из `additionalContext` сохраняется от каждого hook и передаётся Claude вместе.
474
475Каждый hook имеет `type`, который определяет, как он запускается. Большинство hooks используют `"type": "command"`, который запускает команду оболочки. Доступны четыре других типа:473Каждый hook имеет `type`, который определяет, как он запускается. Большинство hooks используют `"type": "command"`, который запускает команду оболочки. Доступны четыре других типа:
476 474
477* `"type": "http"`: POST данные события на URL. См. [HTTP hooks](#http-hooks).475* `"type": "http"`: POST данные события на URL. См. [HTTP hooks](#http-hooks).
478* `"type": "mcp_tool"`: вызвать инструмент на уже подключённом MCP сервере. См. [MCP tool hooks](/ru/hooks#mcp-tool-hook-fields).476* `"type": "mcp_tool"`: вызвать инструмент на уже подключённом MCP сервере. См. [MCP tool hooks](/ru/hooks#mcp-tool-hook-fields).
479* `"type": "prompt"`: однооборотная оценка LLM. См. [Hooks на основе подсказок](#prompt-based-hooks).477* `"type": "prompt"`: однооборотная оценка LLM. См. [Prompt-based hooks](#prompt-based-hooks).
480* `"type": "agent"`: многооборотная проверка с доступом к инструментам. Hooks агентов являются экспериментальными и могут измениться. См. [Hooks на основе агентов](#agent-based-hooks).478* `"type": "agent"`: многооборотная проверка с доступом к инструментам. Agent hooks являются экспериментальными и могут измениться. См. [Agent-based hooks](#agent-based-hooks).
479
480### Объединение результатов из нескольких hooks
481
482Когда несколько hooks совпадают с одним и тем же событием, каждая команда hook выполняется до завершения, прежде чем Claude Code объединит результаты. Один hook, возвращающий `deny`, не останавливает выполнение родственных hooks. Не полагайтесь на `deny` одного hook для подавления побочных эффектов в другом hook.
483
484После завершения всех соответствующих hooks Claude Code объединяет их выходные данные. Для решений о разрешениях `PreToolUse` побеждает наиболее ограничивающий ответ: `deny` переопределяет `ask`, который переопределяет `allow`. Текст из `additionalContext` сохраняется от каждого hook и передаётся Claude вместе.
485
486Пример ниже регистрирует два hook `PreToolUse` на `Bash`. Первый добавляет каждую команду в файл журнала и выходит с 0. Второй запускает скрипт, который выходит с 2, чтобы отклонить, когда команда содержит `rm -rf`:
487
488```json theme={null}
489{
490 "hooks": {
491 "PreToolUse": [
492 {
493 "matcher": "Bash",
494 "hooks": [
495 {
496 "type": "command",
497 "command": "jq -r .tool_input.command >> ~/.claude/bash.log"
498 },
499 {
500 "type": "command",
501 "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
502 }
503 ]
504 }
505 ]
506 }
507}
508```
509
510Когда Claude пытается запустить `rm -rf /tmp/build`, оба hook выполняются параллельно. Hook логирования записывает команду в `~/.claude/bash.log` и выходит с 0, что не сообщает никакого решения. Hook защиты выходит с 2, что отклоняет вызов инструмента. Отклонение побеждает, поэтому Claude Code блокирует команду и показывает Claude stderr защиты. Запись в журнал по-прежнему записывается, потому что hook логирования уже выполнился.
481 511
482### Чтение ввода и возврат вывода512### Чтение ввода и возврат вывода
483 513
499}529}
500```530```
501 531
502Ваш скрипт может анализировать этот JSON и действовать на основе любого из этих полей. Hooks `UserPromptSubmit` получают текст `prompt` вместо этого, hooks `SessionStart` получают `source` (startup, resume, clear, compact) и так далее. См. [Общие поля ввода](/ru/hooks#common-input-fields) в справочнике для общих полей и раздел каждого события для схем, специфичных для события.532Ваш скрипт может анализировать этот JSON и действовать на основе любого из этих полей. Hooks `UserPromptSubmit` получают текст `prompt` вместо этого, hooks `SessionStart` получают `source` (startup, resume, clear, compact) и так далее. См. [Common input fields](/ru/hooks#common-input-fields) в справочнике для общих полей и раздел каждого события для схем, специфичных для события.
503 533
504#### Вывод hook534#### Вывод hook
505 535
521Код выхода определяет, что происходит дальше:551Код выхода определяет, что происходит дальше:
522 552
523* **Exit 0**: действие продолжается. Для hooks `UserPromptSubmit`, `UserPromptExpansion` и `SessionStart` всё, что вы пишете в stdout, добавляется в контекст Claude.553* **Exit 0**: действие продолжается. Для hooks `UserPromptSubmit`, `UserPromptExpansion` и `SessionStart` всё, что вы пишете в stdout, добавляется в контекст Claude.
524* **Exit 2**: действие блокируется. Напишите причину в stderr, и Claude получит её как обратную связь, чтобы он мог скорректировать. Некоторые события не могут быть заблокированы: для `SessionStart`, `Setup`, `Notification` и других, exit 2 показывает stderr пользователю и выполнение продолжается. См. [поведение кода выхода 2 для каждого события](/ru/hooks#exit-code-2-behavior-per-event) для полного списка.554* **Exit 2**: действие блокируется. Напишите причину в stderr, и Claude получит её как обратную связь, чтобы он мог скорректировать. Некоторые события не могут быть заблокированы: для `SessionStart`, `Setup`, `Notification` и других, exit 2 показывает stderr пользователю и выполнение продолжается. См. [exit code 2 behavior per event](/ru/hooks#exit-code-2-behavior-per-event) для полного списка.
525* **Любой другой код выхода**: действие продолжается. Стенограмма показывает уведомление об ошибке `<hook name> hook error`, за которым следует первая строка stderr; полный stderr переходит в [журнал отладки](/ru/hooks#debug-hooks).555* **Любой другой код выхода**: действие продолжается. Стенограмма показывает уведомление об ошибке `<hook name> hook error`, за которым следует первая строка stderr; полный stderr переходит в [debug log](/ru/hooks#debug-hooks).
526 556
527#### Структурированный вывод JSON557#### Структурированный вывод JSON
528 558
550* `"deny"`: отменить вызов инструмента и отправить причину Claude580* `"deny"`: отменить вызов инструмента и отправить причину Claude
551* `"ask"`: показать запрос разрешения пользователю как обычно581* `"ask"`: показать запрос разрешения пользователю как обычно
552 582
553Четвёртое значение, `"defer"`, доступно в [неинтерактивном режиме](/ru/headless) с флагом `-p`. Оно выходит из процесса с сохранённым вызовом инструмента, чтобы обёртка Agent SDK могла собрать ввод и возобновить. См. [Отложить вызов инструмента на потом](/ru/hooks#defer-a-tool-call-for-later) в справочнике.583Четвёртое значение, `"defer"`, доступно в [non-interactive mode](/ru/headless) с флагом `-p`. Оно выходит из процесса с сохранённым вызовом инструмента, чтобы обёртка Agent SDK могла собрать ввод и возобновить. См. [Defer a tool call for later](/ru/hooks#defer-a-tool-call-for-later) в справочнике.
554 584
555Возврат `"allow"` пропускает интерактивный запрос, но не переопределяет [правила разрешений](/ru/permissions#manage-permissions). Если правило отказа соответствует вызову инструмента, вызов блокируется даже когда ваш hook возвращает `"allow"`. Если правило запроса соответствует, пользователь по-прежнему получает запрос. Это означает, что правила отказа из любой области параметров, включая [управляемые параметры](/ru/settings#settings-files), всегда имеют приоритет над одобрениями hook.585Возврат `"allow"` пропускает интерактивный запрос, но не переопределяет [permission rules](/ru/permissions#manage-permissions). Если правило отказа соответствует вызову инструмента, вызов блокируется даже когда ваш hook возвращает `"allow"`. Если правило запроса соответствует, пользователь по-прежнему получает запрос. Это означает, что правила отказа из любой области параметров, включая [managed settings](/ru/settings#settings-files), всегда имеют приоритет над одобрениями hook.
556 586
557Другие события используют разные шаблоны решений. Например, hooks `PostToolUse` и `Stop` используют поле `decision: "block"` верхнего уровня, а `PermissionRequest` использует `hookSpecificOutput.decision.behavior`. См. [таблицу сводки](/ru/hooks#decision-control) в справочнике для полного разбора по событиям.587Другие события используют разные шаблоны решений. Например, hooks `PostToolUse` и `Stop` используют поле `decision: "block"` верхнего уровня, а `PermissionRequest` использует `hookSpecificOutput.decision.behavior`. См. [summary table](/ru/hooks#decision-control) в справочнике для полного разбора по событиям.
558 588
559Для hooks `UserPromptSubmit` используйте `additionalContext` вместо этого для внедрения текста в контекст Claude. Hooks на основе подсказок (`type: "prompt"`) обрабатывают вывод иначе: см. [Hooks на основе подсказок](#prompt-based-hooks).589Для hooks `UserPromptSubmit` используйте `additionalContext` вместо этого для внедрения текста в контекст Claude. Prompt-based hooks (`type: "prompt"`) обрабатывают вывод иначе: см. [Prompt-based hooks](#prompt-based-hooks).
560 590
561### Фильтрация hooks с помощью matchers591### Фильтрация hooks с помощью matchers
562 592
577}607}
578```608```
579 609
580Matcher `"Edit|Write"` срабатывает только, когда Claude использует инструмент `Edit` или `Write`, а не когда он использует `Bash`, `Read` или любой другой инструмент. См. [Шаблоны Matcher](/ru/hooks#matcher-patterns) для того, как простые имена и регулярные выражения оцениваются.610Matcher `"Edit|Write"` срабатывает только, когда Claude использует инструмент `Edit` или `Write`, а не когда он использует `Bash`, `Read` или любой другой инструмент. См. [Matcher patterns](/ru/hooks#matcher-patterns) для того, как простые имена и регулярные выражения оцениваются.
581 611
582<Note>612<Note>
583 Claude может также создавать или изменять файлы, запуская команды оболочки через инструмент `Bash`. Если ваш hook должен видеть каждое изменение файла, например для сканирования соответствия или логирования аудита, добавьте hook [`Stop`](/ru/hooks#stop), который сканирует рабочее дерево один раз за ход. Для покрытия за вызов вместо этого также соответствуйте `Bash` и пусть ваш скрипт перечисляет изменённые и неотслеживаемые файлы с помощью `git status --porcelain`.613 Claude может также создавать или изменять файлы, запуская команды оболочки через инструмент `Bash`. Если ваш hook должен видеть каждое изменение файла, например для сканирования соответствия или логирования аудита, добавьте hook [`Stop`](/ru/hooks#stop), который сканирует рабочее дерево один раз за ход. Для покрытия за вызов вместо этого также соответствуйте `Bash` и пусть ваш скрипт перечисляет изменённые и неотслеживаемые файлы с помощью `git status --porcelain`.
607Несколько дополнительных примеров, показывающих matchers на разных типах событий:637Несколько дополнительных примеров, показывающих matchers на разных типах событий:
608 638
609<Tabs>639<Tabs>
610 <Tab title="Регистрируйте каждую команду Bash">640 <Tab title="Логирование каждой команды Bash">
611 Соответствуйте только вызовам инструмента `Bash` и регистрируйте каждую команду в файл. Событие `PostToolUse` срабатывает после завершения команды, поэтому `tool_input.command` содержит то, что запустилось. Hook получает данные события в виде JSON на stdin, и `jq -r '.tool_input.command'` извлекает только строку команды, которую `>>` добавляет в файл журнала:641 Соответствуйте только вызовам инструмента `Bash` и логируйте каждую команду в файл. Событие `PostToolUse` срабатывает после завершения команды, поэтому `tool_input.command` содержит то, что запустилось. Hook получает данные события в виде JSON на stdin, и `jq -r '.tool_input.command'` извлекает только строку команды, которую `>>` добавляет в файл журнала:
612 642
613 ```json theme={null}643 ```json theme={null}
614 {644 {
630 </Tab>660 </Tab>
631 661
632 <Tab title="Соответствие MCP инструментам">662 <Tab title="Соответствие MCP инструментам">
633 MCP инструменты используют другое соглашение об именовании, чем встроенные инструменты: `mcp__<server>__<tool>`, где `<server>` — имя MCP сервера, а `<tool>` — инструмент, который он предоставляет. Например, `mcp__github__search_repositories` или `mcp__filesystem__read_file`. Используйте matcher regex для нацеливания на все инструменты с определённого сервера, или соответствуйте серверам с шаблоном, таким как `mcp__.*__write.*`. См. [Соответствие MCP инструментам](/ru/hooks#match-mcp-tools) в справочнике для полного списка примеров.663 MCP инструменты используют другое соглашение об именовании, чем встроенные инструменты: `mcp__<server>__<tool>`, где `<server>` — имя MCP сервера, а `<tool>` — инструмент, который он предоставляет. Например, `mcp__github__search_repositories` или `mcp__filesystem__read_file`. Используйте matcher regex для нацеливания на все инструменты с определённого сервера, или соответствуйте серверам с шаблоном, таким как `mcp__.*__write.*`. См. [Match MCP tools](/ru/hooks#match-mcp-tools) в справочнике для полного списка примеров.
634 664
635 Команда ниже извлекает имя инструмента из JSON ввода hook с помощью `jq` и записывает его в stderr. Запись в stderr сохраняет stdout чистым для вывода JSON и отправляет сообщение в [журнал отладки](/ru/hooks#debug-hooks):665 Команда ниже извлекает имя инструмента из JSON ввода hook с помощью `jq` и записывает его в stderr. Запись в stderr сохраняет stdout чистым для вывода JSON и отправляет сообщение в [debug log](/ru/hooks#debug-hooks):
636 666
637 ```json theme={null}667 ```json theme={null}
638 {668 {
676 </Tab>706 </Tab>
677</Tabs>707</Tabs>
678 708
679Для полного синтаксиса matcher см. [справочник Hooks](/ru/hooks#configuration).709Для полного синтаксиса matcher см. [Hooks reference](/ru/hooks#configuration).
680 710
681#### Фильтрация по имени инструмента и аргументам с помощью поля `if`711#### Фильтрация по имени инструмента и аргументам с помощью поля `if`
682 712
684 Поле `if` требует Claude Code v2.1.85 или позже. Более ранние версии игнорируют его и запускают hook при каждом совпадении.714 Поле `if` требует Claude Code v2.1.85 или позже. Более ранние версии игнорируют его и запускают hook при каждом совпадении.
685</Note>715</Note>
686 716
687Поле `if` использует [синтаксис правил разрешений](/ru/permissions) для фильтрации hooks по имени инструмента и аргументам вместе, поэтому процесс hook порождается только когда вызов инструмента совпадает, или когда команда Bash слишком сложна для анализа. Это выходит за рамки `matcher`, который фильтрует на уровне группы только по имени инструмента.717Поле `if` использует [permission rule syntax](/ru/permissions) для фильтрации hooks по имени инструмента и аргументам вместе, поэтому процесс hook порождается только когда вызов инструмента совпадает, или когда команда Bash слишком сложна для анализа. Это выходит за рамки `matcher`, который фильтрует на уровне группы только по имени инструмента.
688 718
689Например, чтобы запустить hook только когда Claude использует команды `git` вместо всех команд Bash:719Например, чтобы запустить hook только когда Claude использует команды `git` вместо всех команд Bash:
690 720
720| `~/.claude/settings.json` | Все ваши проекты | Нет, локально на вашей машине |750| `~/.claude/settings.json` | Все ваши проекты | Нет, локально на вашей машине |
721| `.claude/settings.json` | Один проект | Да, можно зафиксировать в репо |751| `.claude/settings.json` | Один проект | Да, можно зафиксировать в репо |
722| `.claude/settings.local.json` | Один проект | Нет, gitignored |752| `.claude/settings.local.json` | Один проект | Нет, gitignored |
723| Управляемые параметры политики | Организация | Да, контролируется администратором |753| Managed policy settings | Организация | Да, контролируется администратором |
724| [Plugin](/ru/plugins) `hooks/hooks.json` | Когда плагин включен | Да, упакован с плагином |754| [Plugin](/ru/plugins) `hooks/hooks.json` | Когда плагин включен | Да, упакован с плагином |
725| [Skill](/ru/skills) или [agent](/ru/sub-agents) frontmatter | Пока skill или agent активны | Да, определено в файле компонента |755| [Skill](/ru/skills) или [agent](/ru/sub-agents) frontmatter | Пока skill или agent активны | Да, определено в файле компонента |
726 756