Skip to content
Apothem
Справочник

Хуки

Обработчики событий инструментов, срабатывающие на SessionStart, PreToolUse, PostToolUse и Stop и загружающие контексты сообщений, которые обеспечивают изоляцию git и дисциплину планов, с протестированным контрактом времени выполнения и состоянием для каждого пользователя.

Хуки срабатывают на событиях инструментов и загружают контексты сообщений, которые ассистент воспринимает как дополнительные системные промпты. Подключение находится в блоке хуков settings.json Claude Code (установка через движок) или в hooks.json плагина (установка плагина), в файле хуков hooks.json Codex, в карте хуков settings.json Qwen Code, в ключе hooks файла .codebuddy/settings.local.json CodeBuddy (только SessionStart и PostToolUse), в файле хуков Kiro .kiro/hooks/apothem.json (только SessionStart), в файле хуков GitHub Copilot .github/hooks/apothem.json (только sessionStart и postToolUse), в ключе hooks файла .devin/config.local.json Windsurf (Devin Local) (только SessionStart и PostToolUse) и в проектном файле хуков Cursor .cursor/hooks.json (только sessionStart).

СобытиеТайм-аутНазначение
SessionStart30 секундПрочитать память, проверить состояние плана, сообщить о готовности; после сжатия контекста — доставить контекст восстановления; отметить базу знаний, изменившуюся с момента её базовой точки
PreCompact30 секундПроверить, что состояние вынесено вовне перед сжатием контекста (Codex, Qwen Code; не регистрируется в Claude Code)
PostCompact30 секундВосстановить рабочее состояние после сжатия контекста (Codex, Qwen Code; не регистрируется в Claude Code)
Stop60 секундПротокол завершения сессии, включается по запросу
PostToolUse10 секундРекомендация по размеру контекста: на первой отметке сохранить состояние, а на второй оператору сообщается, что сессия длинная; не более двух раз за цикл сжатия контекста; в Claude Code выполняется в фоне
PreToolUse (оболочка: Bash, PowerShell, run_shell_command)10 секундИзоляция артефактов git, локальность планов, защита от динамического eval
PreToolUse (Write/Edit/NotebookEdit/apply_patch)10 секундИзоляция ссылок на планы, заголовок авторства, локальность планов, защиты зависимостей и динамического eval
PreToolUse (AskUserQuestion)10 секундКорректность формы маркера (Recommended)

Диспетчер

Записи хуков, установленные движком, вызывают диспетчер по абсолютному пути скрипта с интерпретатором, определённым при установке. Шаблоны содержат токены ${PYTHON_BIN} и ${HARNESS_ROOT}, которые установщик подставляет во время установки. Claude Code и Qwen Code материализуют диспетчер в <harness-root>/.apothem/support/hooks/; Codex — в <harness-root>/hooks/. Записи Claude Code используют exec-форму (command плюс args), поэтому им не нужна оболочка. Плагин Claude Code запускает каждый хук как bash "${CLAUDE_PLUGIN_ROOT}/lib/apothem/hooks/lib/bootstrap.sh" <event> [<message-file> ...] в оболочке хуков Claude Code по умолчанию (sh в macOS и Linux, Git Bash в Windows); загрузчик находит интерпретатор Python 3.10+ и запускает поставляемый в комплекте диспетчер. Найденный интерпретатор он кэширует в interpreter-path (interpreter-path-ps1 для bootstrap.ps1) в каталоге состояния хуков (см. ниже) и использует его повторно, пока интерпретатор существует, так что проверка PATH выполняется один раз, а не перед каждым вызовом инструмента; удалите файл кэша, чтобы принудительно запустить новую проверку. Учитываются только абсолютный каталог состояния и абсолютный кэшированный путь.

Каждая поверхность регистрирует одну запись диспетчера на событие и сопоставитель, перечисляя все сообщения, которые этот сопоставитель оценивает: один процесс читает полезную нагрузку один раз, выполняет каждую защиту и отвечает одним конвертом, поэтому Write запускает один диспетчер, а не по одному на каждую защиту. Установка Claude Code через движок также запускает шлюз соответствия (gate.py --hook) на Write и Edit отдельной записью. В Windows без Git for Windows используйте установку через движок (apothem install --harness claude-code), хукам которой в exec-форме оболочка не нужна. Диспетчер — это автономный скрипт на стандартной библиотеке: он сам настраивает свой sys.path, поэтому импортируемый пакет apothem на хосте не нужен. В чекауте исходного кода модульная форма python -m apothem.hooks.dispatch <event> (с PYTHONPATH=src) выполняет тот же код для разработки. Диспетчер загружает hooks/lib/resolve_root.py для разрешения корня экосистемы и делегирует работу hooks/emit_hook_context.py и hooks/session_start_bootstrap.py.

Контракты вендоров

Окружение, отличное от Claude Code, запускает тот же диспетчер с --contract <id> после скрипта, например "<python>" "<project>/.apothem/support/hooks/dispatch.py" --contract codebuddy SessionStart. Контракт (hooks/lib/vendor_contract.py) переводит написание событий и имён инструментов вендора и ключи полезной нагрузки в форму Claude, которую читают обработчики, переписывает конверт в форму, которую читает вендор, и отбрасывает событие, которое вендор не передаёт модели. Без флага ничего не переводится, и вывод — это конверт Claude, байт в байт. Флаг входит в документированную командную строку диспетчера: dispatch.py [--contract <id>] <Event> [<message> ...].

КонтрактЧитаетВыводДоставляемые события
codebuddystdin Claudeконверт ClaudeSessionStart, UserPromptSubmit, PostToolUse
devinstdin Claude с именами инструментов в нижнем регистре (exec, write, edit); корень проекта из DEVIN_PROJECT_DIRконверт ClaudeSessionStart, UserPromptSubmit, PostToolUse
cursorсобытия в camelCase, conversation_id, workspace_roots{"additional_context": ...}SessionStart
copilotsessionId, toolName, toolArgs в camelCase или форма PascalCase со snake_case{"additionalContext": ...}SessionStart, PostToolUse, Notification
kirostdin, похожий на Claude, идентификаторы инструментов Kiro (execute_bash, fs_write, str_replace)голый текстSessionStart, UserPromptSubmit
claudestdin Claudeконверт Claudeвсе события

Контракты codebuddy, copilot, cursor, devin и kiro уже сегодня устанавливаются адаптером Apothem (devin — адаптером Windsurf); контракт claude доказывает, что командная строка по умолчанию не изменилась. Диспетчер остаётся рекомендательным при любом контракте: ни одна форма не несёт разрешения, решения, блокировки или переписанного ввода инструмента.

Cursor и Devin Local загружают хуки, которые хранят файлы настроек Claude Code, а также свои собственные, поэтому при установке Apothem для Claude Code на той же машине могут выполняться два набора. Начальная загрузка SessionStart доставляется один раз: собственная команда вендора (--contract cursor или --contract devin) и копия Claude Code, которая является командной строкой по умолчанию, разделяют один маркер на сессию и источник в каталоге состояния хуков, и та, что запускается второй, молчит. Командная строка по умолчанию участвует только тогда, когда окружение процесса называет хост (CURSOR_PROJECT_DIR, CURSOR_VERSION или DEVIN_PROJECT_DIR); в Claude Code, Codex и Qwen Code она никогда не читает маркер. Маркер действует десять секунд, поэтому более поздний запуск той же сессии, например второе сжатие контекста, всё равно доставляет свой контекст восстановления. Ни один счётчик PostToolUse не дедуплицируется, поэтому счётчик размера контекста работает в обоих местах и достигает своих порогов раньше. Установка выводит примечание, в котором названо переключение вендора для отключения импорта (в Cursor — «Include Third-Party Plugins, Skills, and Other Configs»; в Devin — read_config_from.claude).

Команда регистрируется только там, где вендор передаёт её вывод модели. Kiro добавляет stdout хука в контекст только для SessionStart и UserPromptSubmit, поэтому файл Kiro регистрирует только SessionStart. Copilot читает additionalContext из sessionStart и postToolUse и только поля разрешений из preToolUse, поэтому файл Copilot регистрирует эти два события под их именами в camelCase и ни одной защиты. Каждая запись Copilot содержит команду bash и команду powershell. Оба файла принадлежат Apothem и указывают интерпретатор и путь проекта машины, на которой выполнялась установка; страницы окружений объясняют, что это значит для файла, который фиксируется в репозитории.

Devin Local читает additionalContext из SessionStart, UserPromptSubmit и PostToolUse и не документирует его для PreToolUse, поэтому регистрация Windsurf содержит SessionStart и PostToolUse без сопоставителя (сопоставитель Devin — это регулярное выражение, и опущенный сопоставитель соответствует любому инструменту). Она помещается в локальное переопределение .devin/config.local.json, которое Devin документирует как игнорируемое git и сам исключает из git через .git/info/exclude. Хук sessionStart в Cursor добавляет additional_context в начальный контекст беседы; первая версия регистрирует только это событие. .cursor/hooks.json в Cursor — единственный проектный файл хуков, поэтому он принадлежит оператору: установка вливает в него одну запись, сохраняет записи и ключи оператора, отказывается от файла, который не может переписать без потерь, и отличает свою запись по пути диспетчера в команде. Настольное приложение Cursor запускает команды хуков через PowerShell в Windows, поэтому на хосте Windows команда начинается с & .

Во всех пяти файлах запись принадлежит Apothem, если её команда запускает .apothem/support/[<harness>/]hooks/dispatch.py с --contract <id>, под каким бы путём проекта она ни была записана. Поэтому переименованный, перемещённый или клонированный проект сохраняет один набор записей, а ваш хук, например hooks/dispatch.py в вашем собственном дереве или python -m apothem.conformity.gate, никогда не заменяется и не удаляется. apothem verify сообщает об отсутствии регистрации, как только в файле не остаётся ни одной записи Apothem, а удаление, которое не может прочитать файл, сообщает о нём как о пропущенном. Путь проекта или интерпретатора, содержащий $, обратную кавычку, двойную кавычку или обратную косую черту, отклоняется при установке с указанием пути, потому что команда в кавычках запустила бы другой файл.

Контексты сообщений

Контексты Markdown для каждого события находятся в src/apothem/hooks/messages/:

  • Жизненный цикл сессии: sessionstart.md, skill-index.md, precompact.md, postcompact.md, stop.md, posttooluse-proactive-compaction.md
  • Вызовы оболочки: pretooluse-bash.md, pretooluse-bash-plan-guard.md
  • Запись файлов: pretooluse-write.md, pretooluse-edit.md, pretooluse-notebookedit.md, pretooluse-write-plan-guard.md, pretooluse-write-header-guard.md, pretooluse-edit-header-guard.md, pretooluse-conformity.md
  • Защиты содержимого: pretooluse-dependency-guard.md, pretooluse-eval-guard.md
  • Вопросы: pretooluse-askuserquestion-recommended.md

В Claude Code postcompact.md доставляется хуком SessionStart, когда сессия возобновляется после сжатия контекста, поскольку Claude Code не передаёт вывод хуков PreCompact и PostCompact ассистенту.

skill-index.md генерируется, а не пишется вручную: scripts/dev/generate_skill_index.py строит его из каталога навыков и команд, по одной строке на группу задач, причём ручные навыки показаны как /apothem:<name>. В Claude Code хук SessionStart добавляет его в контекст сессии, если файл существует. Он существует потому, что Claude Code убирает описания навыков из своего списка, как только список превышает свой бюджет символов, а индекс сохраняет возможность найти каждый навык по задаче. Установка плагина показывает ручные навыки как /apothem:<name>; установка через движок убирает префикс. Текст остаётся короче 1 600 символов, а generate_skill_index.py --check завершается ошибкой, когда зафиксированный файл больше не соответствует каталогу.

Когда Apothem работает только как плагин Claude Code, при запуске не загружается ни один файл правил. Тогда хук SessionStart добавляет короткий указатель на поставляемый в комплекте каталог rules/. Если в этом каталоге есть apothem-rule-index.md, указатель также велит сессии сначала прочитать четыре основных правила, потому что в этом режиме их ничто не загружает (agnostic-posture, authority-inquiry, session-closure, context-management), и называет индекс, который говорит, какое правило читать для какой задачи. Текст индекса не встраивается.

Когда в проекте есть база знаний, созданная навыком knowledge-wiki, хук SessionStart сравнивает её отпечаток с отпечатком, записанным при последней базовой точке, и, если они различаются, добавляет одну строку, предлагающую режим дрейфа этого навыка (--mode drift --kb <path>). Отпечаток — это число страниц плюс самая новая запись журнала; записанная копия — самая новая строка baseline в log.md базы, которую пишет только режим дрейфа. Хук проверяет четыре пути под корнем проекта, по порядку: knowledge-base/, wiki/, docs/knowledge-base/ и docs/wiki/, и только каталог, который содержит и index.md, и log.md. Проект без базы тратит на это меньше миллисекунды. С базой хук читает не более последних 256 КиБ log.md и перечисляет три плоских каталога; он никогда не обходит дерево и никогда не пишет. Строка содержит фиксированный путь, дату и два числа страниц, но никогда не текст из базы. Она молчит, когда нет базы, нет базовой точки, нет изменений или есть файл, который нельзя прочитать, и повторяется в каждой сессии, пока режим дрейфа не запишет новую базовую точку. База по любому другому пути работает во всех режимах навыка, но не получает строки при запуске сессии.

Каждый файл контекста загружается диспетчером, когда срабатывает его событие и его аргумент контекста зарегистрирован в файле настроек. Строка комментария с лицензией и завершающий раздел ## Bindings удаляются до того, как текст попадает к ассистенту; оба существуют для сопровождающих и шлюза соответствия.

Защита, которая ничего не находит, ничего не внедряет. Сработавшая защита внедряет только раздел ## Advisory своего сообщения, заполненный тем, что она нашла (путь, идентификатор, примитив), а за ним — Full procedure: <message-file>. Остальная часть файла — полная процедура, которую ассистент читает только тогда, когда ему нужны подробности. Защиты судят о том, что находится «внутри проекта», по корню git над рабочим каталогом вызова инструмента, а не по дереву, в которое установлен хук.

Контракт времени выполнения

Каждая зарегистрированная команда хука соблюдает один и тот же контракт, который проверяет tests/hooks/test_hook_contract.py для плагина, установки Claude Code через движок, Codex, Qwen Code и установки CodeBuddy (файлы Kiro, Copilot, Cursor и Windsurf проверяются на соответствие своим контрактам tests/hooks/test_native_hook_registrations.py и запускаются так, как их запускают вендоры, tests/integration/test_native_hook_files_roundtrip.py, test_cursor_hooks_roundtrip.py и test_windsurf_hooks_roundtrip.py):

  • Запускается из чистого чекаута. Код выхода 0, а стандартный вывод пуст или содержит один объект JSON. Ни один хук не зависит от бита исполнения файла.
  • Регистрируется один раз. У каждого события и сопоставителя одна запись диспетчера на поверхность, перечисляющая все сообщения, которые он оценивает, поэтому защита никогда не запускается дважды на одном вызове инструмента.
  • Ограничен. Ни один выводимый текст не превышает 10 000 символов. Защита зависимостей срабатывает только тогда, когда запись нацелена на манифест зависимостей или lock-файл, а защита от динамического eval — только когда содержимое или команда содержит примитив вычисления или небезопасной десериализации. Рекомендация по сжатию контекста срабатывает не более двух раз за цикл сжатия контекста.
  • Использует каналы, которые доставляет окружение. Указания PreToolUse передаются в hookSpecificOutput; защита AskUserQuestion возвращает additionalContext, а при строгом режиме, включаемом по запросу, — permissionDecision: deny. Claude Code отбрасывает вывод PreCompact и PostCompact, поэтому в Claude Code контекст восстановления после сжатия приходит с событием SessionStart, источником которого является compact.
  • Никогда не запускает код проекта. Загрузчик определяет собственное установленное дерево и никогда не берёт скрипт из открытого проекта. Указания при запуске сессии читаются из установленного дерева; каталог проекта читается только для сводок планов и памяти и проверки дрейфа базы знаний.
  • При сбое не блокирует работу (fail-open). Отсутствующий файл сообщения, отсутствующий интерпретатор или повреждённое состояние дают код выхода 0 и валидный конверт.
ПеременнаяПо умолчаниюДействие
APOTHEM_HOOKS_DISABLEне задана1, true, yes или on отключает каждый хук, маршрутизируемый диспетчером, и хук шлюза соответствия установки через движок
APOTHEM_SESSION_END_ENABLEDвыключено1, true, yes или on включает протокол завершения сессии на Stop. Хук Stop, который возвращает контекст, заставляет ассистента продолжить работу, поэтому протокол стоит один дополнительный ход за сессию; он никогда не срабатывает на остановке, которая уже следует за продолжением, вызванным хуком
APOTHEM_SESSION_END_MIN_STOPS3Число завершений хода, после которого может сработать протокол завершения сессии
APOTHEM_PROACTIVE_COMPACTION_ENABLEDвключено0, false, no или off отключает рекомендацию по сжатию контекста
APOTHEM_CONTEXT_WINDOW_TOKENSне заданаРазмер окна контекста; с ней (или с CLAUDE_CODE_AUTO_COMPACT_WINDOW Claude Code) рекомендации срабатывают при 50% и 75% окна, иначе при 120 000 и 250 000 токенов
APOTHEM_PROACTIVE_COMPACTION_TOOL_THRESHOLD18Вызовы инструментов на окно рекомендации; используется, только когда размер контекста невозможно измерить
APOTHEM_PROACTIVE_COMPACTION_OUTPUT_THRESHOLD20000Байты вывода инструментов на окно рекомендации; используется, только когда размер контекста невозможно измерить
APOTHEM_HOOK_STATE_DIRсм. нижеКаталог для состояния хуков каждой сессии

Рекомендация по сжатию контекста измеряет контекст по самой новой записи об использовании в транскрипте сессии (входные плюс кэшированные токены), поэтому она отражает то, что модель фактически удерживает. Резкое падение этого размера отмечает сжатие контекста и перезапускает бюджет из двух рекомендаций. Там, где транскрипт недоступен, она вместо этого считает вызовы инструментов и байты вывода.

Состояние хуков каждой сессии хранится в каталоге конкретного пользователя, создаваемом с режимом 0700: APOTHEM_HOOK_STATE_DIR, если переменная задана, иначе ${CLAUDE_PLUGIN_DATA}/state для плагина Claude Code, иначе $XDG_STATE_HOME/apothem, иначе ~/.local/state/apothem (%LOCALAPPDATA%\apothem\state в Windows).

Сгенерированный инвентарь сообщений хуков

NameDescriptionSource
postcompactPost-compaction Blind Bootstrap. Restore working state from durable files; conversation context is now compressed.src/apothem/hooks/messages/postcompact.md
posttooluse-proactive-compactionProactive-compaction tracker for PostToolUse tool calls.src/apothem/hooks/messages/posttooluse-proactive-compaction.md
precompactPre-compaction protocol. Externalize all in-conversation state BEFORE the harness compresses context.src/apothem/hooks/messages/precompact.md
pretooluse-askuserquestion-recommendedCall-time (Recommended)-marker guard for AskUserQuestion tool calls.src/apothem/hooks/messages/pretooluse-askuserquestion-recommended.md
pretooluse-bash-plan-guardPlans-Discipline write guard for Bash tool calls (companion to pretooluse-write-plan-guard.md).src/apothem/hooks/messages/pretooluse-bash-plan-guard.md
pretooluse-bashGit write guard: plan-internal isolation, numeric-prefix discipline, human-only authorship.src/apothem/hooks/messages/pretooluse-bash.md
pretooluse-conformity> Advisory: this hook reports; it does not block. Mechanical enforcement runs in CI.src/apothem/hooks/messages/pretooluse-conformity.md
pretooluse-dependency-guardDependency-manifest supply-chain guard (advisory).src/apothem/hooks/messages/pretooluse-dependency-guard.md
pretooluse-edit-header-guardSPDX-header inject guard for Edit tool calls.src/apothem/hooks/messages/pretooluse-edit-header-guard.md
pretooluse-editCodebase isolation (Edit scope).src/apothem/hooks/messages/pretooluse-edit.md
pretooluse-eval-guardDynamic-eval / unsafe-deserialization guard — advisory prompt-injection defense.src/apothem/hooks/messages/pretooluse-eval-guard.md
pretooluse-notebookeditCodebase isolation (notebook scope).src/apothem/hooks/messages/pretooluse-notebookedit.md
pretooluse-write-header-guardSPDX-header inject guard for Write tool calls.src/apothem/hooks/messages/pretooluse-write-header-guard.md
pretooluse-write-plan-guardPlans-Discipline write guard for Write / Edit / NotebookEdit tool calls.src/apothem/hooks/messages/pretooluse-write-plan-guard.md
pretooluse-writeCodebase isolation + frontmatter compliance.src/apothem/hooks/messages/pretooluse-write.md
sessionstartSession-start conformity posture.src/apothem/hooks/messages/sessionstart.md
skill-indexSkills by task. /apothem:name = manual, run only on request.src/apothem/hooks/messages/skill-index.md
stopSession-end protocol: externalize state; evaluate memory and artifacts.src/apothem/hooks/messages/stop.md

On this page