У сесії Claude Code спрацьовує 30 подій хуків. Відповісти можуть лише 3.

Повний перелік подій хуків Claude Code: коли спрацьовує кожна з них, які 15 можуть заблокувати дію, і правило stdout, яке мовчки поглинає вивід більшості хуків. Польова довідка, зібрана з роботи хуків у продакшені на тисячах сесій агентів.

Дві поломки трапляються знову і знову, коли хуки під’єднують до Claude Code, і між собою вони не мають нічого спільного.

Перша: ви додаєте хук, і нічого не відбувається. Ні помилки, ні попередження, ні рядка в журналі. Хук просто ніколи не виконується.

Друга: хук очевидно виконується, його побічні ефекти видно на диску, але повідомлення, яке він друкує для агента, так і не доходить. Агент поводиться так, ніби хук нічого не сказав.

Обидві мають одне джерело. Система хуків більша й менш однорідна, ніж та жменя подій, яку описує більшість статей, а правила про те, кому дозволено говорити з агентом, зовсім не такі, як можна припустити. Це та довідка, якої нам самим бракувало. Ми будуємо AgentsRoom поверх цих хуків, і все, що нижче, або процитовано з офіційної довідки, або виміряно в продакшені.

Подій тридцять, а не шість

Більшість посібників описують PreToolUse, PostToolUse, UserPromptSubmit, Stop, Notification і SubagentStop. Ці шість справжні, і на них припадає більшість корисної роботи. Вони ж становлять п’яту частину того, що існує.

Повний перелік, згрупований за тим, що кожна подія спостерігає:

ГрупаПодії
СесіяSessionStart, SessionEnd, Setup
ПромптUserPromptSubmit, UserPromptExpansion
ІнструментиPreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch
ДозволиPermissionRequest, PermissionDenied
ХідStop, StopFailure
Субагенти та задачіSubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle
КонтекстPreCompact, PostCompact, InstructionsLoaded
СередовищеFileChanged, CwdChanged, ConfigChange
WorktreesWorktreeCreate, WorktreeRemove
ІнтерфейсNotification, MessageDisplay
Elicitation MCPElicitation, ElicitationResult

Часова діаграма 30 подій хуків Claude Code у порядку їх спрацювання під час сесії агента, від SessionStart через UserPromptSubmit, PreToolUse, PostToolUse і Stop до SessionEnd, із позначенням подій, які можуть заблокувати агента.

Порядок спрацювання подій в одній сесії. Блок інструмента повторюється на кожен виклик інструмента, а весь блок промпту повторюється на кожен хід.

Кілька з цих подій змінюють те, як ви думаєте про систему. PostToolUseFailure існує, тож гілка «чи спрацював інструмент» є подією, а не тим, що ви виводите з payload. PostToolBatch спрацьовує один раз після того, як завершується пакет паралельних викликів інструментів: це правильне місце, щоб запустити linter один раз, а не по разу на кожну правку. InstructionsLoaded спрацьовує, коли читається CLAUDE.md, і це дає точку, де можна перевірити, що агент справді завантажив ті правила, які ви вважаєте завантаженими.

Правило stdout, яке поглинає вивід більшості хуків

Це найкорисніше, що є на цій сторінці.

При коді виходу 0 Claude Code розбирає stdout у пошуках полів вихідного JSON. Але чи буде цей stdout колись показаний агентові, залежить від події, і список винятків короткий. З довідки:

Для більшості подій stdout пишеться в журнал налагодження, але не показується в транскрипті. Винятки: UserPromptSubmit, UserPromptExpansion і SessionStart, де stdout додається як контекст, який Claude бачить і може використати.

Три події з тридцяти. Якщо ви робите echo "warning: this migration is destructive" з хука PostToolUse і чекаєте, що агент це прочитає, він не прочитає ніколи. Ваш текст пішов у журнал налагодження.

Є рівно два способи показати текст агентові з будь-якої іншої події:

  1. Вийти з кодом 2 і записати в stderr. При виході з кодом 2 Claude Code ігнорує stdout і будь-який JSON у ньому, а stderr повертає агентові як повідомлення про помилку.
  2. Вийти з кодом 0 і надрукувати об’єкт JSON, який несе hookSpecificOutput.additionalContext.

Зверніть увагу на асиметрію в першому пункті. Вихід 0 означає, що важить stdout, а stderr не важить. Вихід 2 означає, що важить stderr, а stdout відкидається повністю. Переплутати ці два напрямки: ось чому хук може виглядати цілком правильним і все одно мовчати.

Будь-який інший код виходу є неблокуючою помилкою. У транскрипті з’являється повідомлення <hook name> hook error з першим рядком stderr, виконання триває, а повний stderr потрапляє в журнал налагодження.

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

Який канал доходить до агента, залежно від коду виходу. Пунктирний шлях: той, який усі вважають наявним і якого не існує.

Рівно половина з них може блокувати

П’ятнадцять подій зупиняють дію при виході з кодом 2. П’ятнадцять ігнорують його і йдуть далі.

Можуть блокувати: PreToolUse, PermissionRequest, UserPromptSubmit, UserPromptExpansion, Stop, SubagentStop, TeammateIdle, TaskCreated, TaskCompleted, ConfigChange, PostToolBatch, PreCompact, Elicitation, ElicitationResult, WorktreeCreate.

Не можуть блокувати: PostToolUse, PostToolUseFailure, PermissionDenied, StopFailure, Notification, SubagentStart, SessionStart, Setup, SessionEnd, CwdChanged, FileChanged, PostCompact, WorktreeRemove, InstructionsLoaded, MessageDisplay.

Практичний наслідок: запобіжник ставлять на PreToolUse, ніколи на PostToolUse. PostToolUse спрацьовує після того, як інструмент уже відпрацював успішно. Вихід з кодом 2 там не скасує запис, він лише надрукує помилку, поки шкода вже лежить на диску. Якщо ви хочете зупинити rm -rf, у вас є рівно одне місце, де це робиться.

Те, що PostToolBatch блокує, а PostToolUse ні, варте другого погляду. Це означає, що перевірка на рівні пакета все ще може зупинити хід після того, як паралельні правки записалися: це найближче до вето після запису, що система взагалі пропонує.

Matcher працює за точним збігом, доки раптом не перестає

Поле matcher змінює стратегію обчислення залежно від власних символів, і ніщо не підказує вам, який шлях було обрано.

MatcherОбчислюється як
"*", "" або відсутнійзбігається з усім
Лише літери, цифри, _, -, пробіли, ,, |точний рядок або список точних рядків, розділених | чи ,
Будь-що іншенеприв’язаний регулярний вираз JavaScript

Пастка саме в неприв’язаності. Довідка прямо каже, що регулярний вираз перевіряється через RegExp.prototype.test, який спрацьовує на збігу будь-де всередині значення. Тому Edit.* збігається і з Edit, і з NotebookEdit. Якщо ви мали на увазі один інструмент, пишіть ^Edit$.

Дві залежні від версії поведінки, які варто знати, перш ніж налагоджувати не те:

  • Коми як роздільники та терпимість до пробілів потребують Claude Code v2.1.191 або новішої версії.
  • Дефіси увійшли до набору символів точного збігу у v2.1.195. До того matcher на кшталт code-reviewer трактувався як неприв’язаний регулярний вираз, тож спрацьовував і для senior-code-reviewer.

В AgentsRoom ми обмежуємо власний хук атрибуції файлів через Write|Edit|MultiEdit|NotebookEdit: він залишається на шляху точного рядка і збігається саме з цими чотирма інструментами, і ні з чим іншим. Хуки життєвого циклу, які ми встановлюємо, взагалі не мають matcher, бо вони завжди стосуються нас.

Хуки можуть визначатися в шести місцях, і вони зливаються

Перший порив: шукати порядок пріоритетів. Його немає, і це якраз найцікавіше.

РозташуванняОбласть дії
~/.claude/settings.jsonусі ваші проєкти, локально на вашій машині
.claude/settings.jsonодин проєкт, можна комітити
.claude/settings.local.jsonодин проєкт, Claude Code додає його в gitignore
Managed policy settingsуся організація, під контролем адміністратора
hooks/hooks.json плагінапоки плагін увімкнений
Frontmatter скіла або агентапоки компонент активний

З довідки:

Записи хуків зливаються між рівнями налаштувань, а не заміняють одні одних: користувацькі, проєктні та локальні налаштування додають власні хуки, не прибираючи керовані, а параметр disableAllHooks не може вимкнути керовані хуки ззовні керованих налаштувань.

Тож проєктний хук ніколи не перекриває глобальний, він накладається згори. Шість джерел, усі додаються. Форматувальник PostToolUse, оголошений у ваших користувацьких налаштуваннях і ще раз у проєкті, виконується двічі на кожну правку, і єдиний симптом: відчуття, що все стало повільним.

Діаграма шести розташувань налаштувань Claude Code, які можуть визначати хуки: усі вони зливаються додаванням в один набір хуків, а не перекривають одне одного.

Шість джерел, один злитий набір. Ніщо тут нічого не перекриває.

Це також пояснює, чому .claude/settings.local.json є правильним місцем для інструмента, який встановлює хук у чужий проєкт. Він обмежений проєктом, Claude Code додає його в gitignore, і він завантажується без жодного прапорця CLI. Саме туди AgentsRoom пише свої записи, тож закомічений .claude/settings.json користувача ніколи не чіпається, а його колеги ніколи не успадковують шлях, прив’язаний до конкретної машини.

Чого нас навчила робота хуків у продакшені

AgentsRoom встановлює хуки в кожен проєкт, який відкриває, щоб детерміновано відстежувати статус агентів і приписувати змінені файли правильному агентові. Деякі речі виявляються лише на такому масштабі.

Невідомі назви подій ігноруються мовчки. Цього немає в документації, і ми на це покладаємося. Коли ми додаємо нову подію життєвого циклу до нашого інсталятора, користувачі зі старішим CLI отримують settings.local.json із назвою події, про яку їхній бінарник ніколи не чув. Нічого не ламається, нічого не попереджає, запис просто пропускається. Саме це робить інсталятор безпечним для випуску раніше за реліз CLI. І це ж, неминуче, причина того, що друкарська помилка дає повну тишу замість помилки.

agent_id це те, за чим ви розумієте, що перебуваєте всередині субагента. Поле присутнє лише тоді, коли хук спрацьовує всередині виклику субагента. Це важливіше, ніж звучить: Stop спрацьовує, коли субагент завершує свій хід, а не лише головний агент. Наївне правило «позначити сесію завершеною на Stop» позначає всю сесію завершеною при першому ж поверненні будь-якого субагента. Саме тому ми пропускаємо події ходу, які несуть agent_id.

Не читайте transcript_path для поточного ходу. Довідка попереджає, що транскрипт пишеться асинхронно і може відставати від розмови в пам’яті, тож найсвіжіших повідомлень може там ще не бути, коли ваш хук спрацьовує. Stop і SubagentStop отримують last_assistant_message саме для того, щоб вам ніколи не доводилося змагатися з файлом.

Хуки є єдиним надійним сигналом статусу. До хуків ми парсили PTY, щоб зрозуміти, чи агент думає, чи чекає, чи вже завершив. Це ламається в ту саму мить, коли CLI малює через альтернативний екранний буфер термінала, а саме це робить /tui fullscreen. Хуки спрацьовують однаково під будь-яким рендерером. Якщо ви будуєте будь-що, що спостерігає за агентом ззовні, будувати треба саме на цьому рівні, а парсинг термінала залишається щонайбільше запасним варіантом.

async: true не коштує нічого. Команда хука може оголосити async: true, і агент її не чекає. Наш хук робить POST на локальний ендпоїнт із обмеженням у 2 секунди й повертає керування; затримка ходу агента не змінюється, навіть коли застосунок-приймач закритий. Якщо ваш хук лише спостерігає і ніколи нічого не вирішує, зробіть його асинхронним і перестаньте за нього платити.

Ніколи не дозволяйте хуку писати сміття в термінал. Наш скрипт поглинає всі винятки, зокрема на верхньому рівні. Необроблений traceback із хука не просто тихо падає: він друкує стек викликів Python у сесію термінала користувача посеред його роботи.

Тайм-аути

Значення за замовчуванням щедрі, за трьома винятками, які такими не є:

Тип хукаТайм-аут за замовчуванням
command, http, mcp_tool600 с
prompt30 с
agent60 с
UserPromptSubmit (command, http, mcp_tool)30 с
MessageDisplay (command, http, mcp_tool)10 с
SessionEnd1,5 с спільно на всі хуки, піднімається до довшого timeout окремого хука, аж до 60 с

Бюджет SessionEnd є тим, що людей дивує. Це спільний бюджет, а не ліміт на кожен хук, тож три хуки прибирання ділять між собою 1,5 секунди, якщо ви явно його не піднімете.

Коротка версія

  • Існує 30 подій. Відомі шість.
  • stdout доходить до агента лише на UserPromptSubmit, UserPromptExpansion і SessionStart. У всіх інших випадках використовуйте вихід з кодом 2 і stderr, або additionalContext у JSON.
  • 15 подій блокують при виході з кодом 2, 15 його ігнорують. Запобіжники ставлять на PreToolUse.
  • Matcher залишається точним рядком, доки спеціальний символ не перетворить його на неприв’язаний регулярний вираз.
  • Шість джерел налаштувань зливаються додаванням. Ніщо нічого не перекриває.
  • Помилка в назві події призводить до цілковито мовчазного провалу.

Якщо ви хочете бачити, як ці події спрацьовують, а не міркувати про них, то саме це ми й побудували: AgentsRoom показує кожне спрацювання хука по агенту, по проєкту, по запуску, для десятків паралельних агентів і сесій субагентів. Хуки, які ви налаштували у власних settings, працюють рівно так, як написані, бо запускається справжній CLI.

Читати далі

Завантажити AgentsRoom

Запускайте свої AI-агенти (Claude, Codex, Antigravity CLI, OpenCode, Aider, Grok Build, Mistral Vibe, Kimi Code) на всіх ваших проєктах з одного вікна.

БезкоштовноЗавантажити AgentsRoom

Додаток-компаньйон: контролюйте своїх агентів на ходу

Використовуйте свого: Claude, Codex, Antigravity CLI або іншого AI-провайдера.

Отримати розширення
Chrome Web Store

Надсилайте баги та запити прямо у свій публічний беклог.

Погляд на AgentsRoom в дії.

Кілька проектів
Багато постачальників
Кілька агентів
Статус в реальному часі
Різниця файлів і коміт
Мобільний компаньйон
Попередній перегляд в реальному часі
Команди агентів
Автоматизація браузера
Розробка, орієнтована на беклог
Бібліотека підказок
Бібліотека навичок
Переглянути всі функції