Hooks
Хуки позволяют запускать произвольные shell-команды в определённых точках жизненного цикла Kodik: перед и после вызова инструмента, при запуске сессии, при компактировании и в других ситуациях. С помощью хуков можно блокировать нежелательные действия, вести аудит-лог, автоматически модифицировать входные данные инструментов и многое другое.
Где определяются хуки
Заголовок раздела «Где определяются хуки»Хуки могут поступать из трёх источников, которые загружаются одновременно:
| Источник | Расположение файла |
|---|---|
| Проект | .kodik/hooks/hooks.json (или .kodik/hooks/hooks.toml) в корне рабочей директории |
| Пользователь | ~/.kodik/hooks/hooks.json (или hooks.toml) |
| Плагин | hooks/hooks.json внутри директории установленного плагина |
Проектные хуки применяются только к доверенным рабочим директориям. При первом обнаружении файла хуков Kodik предложит подтвердить доверие к проекту.
Kodik обнаруживает и записывает хуки только в строчной .kodik/hooks. Uppercase-каталоги Hooks не перемещаются, не объединяются и не удаляются; их содержимое неактивно и не выполняется. Нужные хуки перенесите вручную в строчный каталог.
Быстро создать хук можно с помощью команды /create-hook.
Поддерживаемые события
Заголовок раздела «Поддерживаемые события»| Событие | Когда срабатывает | Матчер |
|---|---|---|
PreToolUse |
Перед одобрением и выполнением вызова инструмента | Regex по имени инструмента |
PostToolUse |
После выполнения инструмента | Regex по имени инструмента |
PermissionRequest |
Перед показом запроса на одобрение | Regex по имени инструмента |
SubagentStart |
При запуске субагента | Regex по типу агента |
SubagentStop |
При остановке субагента | Regex по типу агента |
SessionStart |
При старте или возобновлении сессии | Regex по источнику (startup или resume) |
PreCompact |
Перед компактированием контекста | Regex по триггеру (manual или auto) |
PostCompact |
После каждой попытки компактирования | Regex по триггеру (manual или auto) |
UserPromptSubmit |
При отправке пользователем сообщения | Матчер не применяется |
Stop |
Когда ход агента завершён или отменён | Матчер не применяется |
StopFailure |
Когда ход агента завершился ошибкой | Regex по фазе сбоя (before_dispatch или provider) |
За каждым SubagentStart следует ровно один SubagentStop, в том числе для субагента, который не удалось запустить. Запущенный субагент вызывает SubagentStop, когда завершает работу, даже если запустивший его ход уже закончился.
Порядок обработки вызова инструмента
Заголовок раздела «Порядок обработки вызова инструмента»Для каждого вызова инструмента хуки и одобрение срабатывают в таком порядке:
- Один раз срабатывает
PreToolUse. Он может заблокировать вызов, изменить входные данные черезmodified_inputили одобрить вызов черезdecision: "approve". - Правила автоодобрения проверяются по входным данным с учётом изменений хука.
- Если вызов всё ещё требует одобрения, срабатывает
PermissionRequest, затем показывается запрос на одобрение с теми данными, которые действительно будут выполнены. - Инструмент выполняется, затем срабатывает
PostToolUse.
Блокировка в PreToolUse отклоняет вызов до появления запроса на одобрение, а агент получает причину блокировки. Одобрение в PreToolUse пропускает запрос так же, как одобрение в PermissionRequest. Вызовы инструментов субагентов проходят тот же порядок. Интерактивные инструменты ask_questions и check_understanding не вызывают PreToolUse.
Для автономной работы, когда некому одобрять правки, используйте режим одобрения Автопилот или Полный доступ либо хук, возвращающий decision: "approve".
Поле matcher — это строка регулярного выражения, которая проверяется против дискриминатора события:
- События с инструментами (
PreToolUse,PostToolUse,PermissionRequest): проверяется имя инструмента. - События субагентов (
SubagentStart,SubagentStop): проверяется тип агента. SessionStart: проверяется источник —startupилиresume.PreCompact/PostCompact: проверяется триггер —manualилиauto.StopFailure: проверяется фаза сбоя —before_dispatchилиprovider.UserPromptSubmit,Stop: матчер игнорируется, хук срабатывает всегда.
Матчер привязывается к началу и концу значения — например, Bash соответствует только инструменту с именем Bash. Чтобы сопоставить несколько инструментов, используйте синтаксис Edit|Write или .*.
Формат файла хуков
Заголовок раздела «Формат файла хуков»{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 audit_tool.py", "timeout": 10 } ] } ], "SessionStart": [ { "hooks": [ { "type": "command", "command": "echo session started >> ~/kodik-sessions.log" } ] } ] }}Каждая запись в массиве события содержит:
matcher— необязательная regex-строка (отсутствие означает «всегда совпадает»).hooks— массив обработчиков. Сейчас поддерживается только типcommand.command— shell-команда, которую нужно выполнить.timeout— таймаут в секундах (по умолчанию 30, диапазон 1–300).
Входные данные хука
Заголовок раздела «Входные данные хука»Хук получает на stdin JSON-объект с информацией о событии:
{ "hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": { "command": "ls -la" }, "context": { ... }}Stop и StopFailure
Заголовок раздела «Stop и StopFailure»Stop срабатывает, когда ход завершён или отменён; во входных данных есть поле outcome: "completed" или "canceled". Если ход завершился ошибкой, вместо него срабатывает StopFailure с такими полями:
| Поле | Тип | Описание |
|---|---|---|
failure_phase |
string | before_dispatch — запрос не дошёл до модели (нет сети, вход, кредиты, доступ, настройки). provider — провайдер вернул ошибку, с частичным ответом или без него. |
error_code |
string | Код ошибки, например insufficient-credits, account-auth-required, invalid-request или provider-overloaded. |
transient |
boolean | true, если сбой временный (сеть, таймаут, перегрузка, лимит запросов) и повтор позже может пройти. |
error_message |
string | Текст ошибки, показанный в чате. |
{ "hook_event_name": "StopFailure", "failure_phase": "before_dispatch", "error_code": "insufficient-credits", "transient": false, "error_message": "No credits remaining.", "context": { ... }}Вывод хуков Stop и StopFailure игнорируется.
PreCompact и PostCompact
Заголовок раздела «PreCompact и PostCompact»PreCompact срабатывает перед компактированием чата, и компактирование ждёт его завершения в пределах timeout хука, поэтому хук успевает сохранить снимок. Блокировка пропускает компактирование по /compact, при смене модели или после простоя. Компактирование посреди хода пропустить нельзя — без него ход не продолжится, — поэтому там блокировка только записывается в лог.
PostCompact срабатывает после каждой попытки компактирования, в том числе внутри субагента. Для субагента заполнено поле agent_type, а context описывает собственный чат субагента. Поле outcome принимает значения succeeded, failed или superseded (чат успел измениться раньше, и сводка отброшена). Компактирование, остановленное кнопкой «Стоп», не вызывает PostCompact. Если нажать «Стоп», пока выполняется PreCompact, завершение хука не запускает сводку и не сообщает о неудачной попытке. Это также действует при остановке чата из другого окна.
Результат выполнения хука
Заголовок раздела «Результат выполнения хука»Хук сигнализирует об исходе через код возврата и/или stdout:
| Код возврата | Значение |
|---|---|
0 |
Продолжить выполнение |
2 |
Заблокировать действие; содержимое stderr становится причиной блокировки |
Кроме того, хук может вернуть структурированный JSON в последней непустой строке stdout. Поддерживаемые поля:
| Поле | Тип | Описание |
|---|---|---|
decision |
"block" | "approve" |
Заблокировать или разрешить действие (approve работает для PreToolUse и PermissionRequest) |
reason |
string | Причина решения, показывается пользователю |
modified_input |
object | Изменённые входные данные инструмента (только для PreToolUse) |
modified_prompt |
string | Изменённый текст промпта (только для UserPromptSubmit) |
additional_context |
string | Дополнительный контекст, передаваемый агенту |
Kodik передаёт additional_context как исходный текст в блоке Markdown Hook context. Происхождение и идентификатор доставки берутся из запуска хука, а не из тегов или заголовков в его выводе. Поля modified_prompt, modified_input, разрешение и блокировка продолжают работать согласно правилам выше.
Пример блокировки с причиной:
{ "decision": "block", "reason": "Изменения в продакшн-ветке запрещены без код-ревью."}Переменные окружения
Заголовок раздела «Переменные окружения»Kodik передаёт в хук переменные окружения в зависимости от источника хука:
- Проектный хук:
KODIK_PROJECT_ROOT— путь к корню проекта. - Пользовательский хук:
KODIK_HOOKS_ROOT— путь к директории пользовательских хуков. - Хук плагина:
KODIK_PLUGIN_ROOTиKODIK_PLUGIN_ID.
Любой хук также получает KODIK_SESSION_ID — id чата, который его вызвал. Хуки для вызовов инструментов субагента сообщают id чата, который запустил субагента. Команды, которые запускает агент, получают ту же переменную, поэтому скрипт может определить, из какого чата он запущен; команды субагента получают его собственный id в KODIK_SESSION_ID и id запустившего чата в KODIK_PARENT_SESSION_ID.
Объект context во входных данных хука содержит sessionId чата, его текущий заголовок title, режим mode и рабочую директорию cwd.
Хуки в Windows
Заголовок раздела «Хуки в Windows»Хуки запускаются через cmd.exe. JSON на stdin содержит только ASCII-символы: каждый символ вне ASCII записан как \uXXXX, поэтому хук получает один и тот же текст при любой кодовой странице консоли, а любой JSON-парсер (ConvertFrom-Json, модуль json в Python) точно восстанавливает кириллицу и другие письменности. Хуки и команды агента также получают PYTHONUTF8=1 и PYTHONIOENCODING=utf-8, если вы не задали эти переменные сами, поэтому скрипты Python читают и печатают UTF-8.
Команды, которые агент запускает в Windows PowerShell 5.1, PowerShell 7 и cmd, используют UTF-8 для вывода и для текста, передаваемого другим программам через конвейер.
Windows PowerShell 5.1 читает файл .ps1 без метки порядка байтов (BOM) в системной ANSI-кодировке. Сохраняйте скрипты PowerShell с нелатинским текстом в кодировке UTF-8 с BOM.
Создание хука с помощью /create-hook
Заголовок раздела «Создание хука с помощью /create-hook»Напечатайте /create-hook в чате и опишите, что должен делать хук. Kodik сгенерирует и сохранит файл хуков в .kodik/hooks/hooks.json вашего проекта (или в глобальную директорию, если вы явно укажете). После создания хука перезагрузите окно IDE или начните новую задачу, чтобы изменения вступили в силу.
