Хуки

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

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

Хуки Cusp используют распространённый формат хуков: те же события, конфигурация, коды выхода и JSON-вывод. Хуки, которые у вас уже есть в .claude/settings.json, работают в Cusp.

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

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

.cusp/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write|ApplyPatch",
        "hooks": [
          { "type": "command", "command": "bunx biome format --write \"$CUSP_PROJECT_DIR\"", "timeout": 30 }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.cusp/hooks/check-bash.sh" }
        ]
      }
    ]
  }
}

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

Матчеры

matcher определяет, к каким вызовам инструментов (или подтипам событий) относится группа.

  • Простое имя совпадает точно: Bash.
  • Список через | совпадает с любым из элементов: Edit|Write.
  • Всё, что содержит символы регулярных выражений, считается регулярным выражением: mcp__github__.*.
  • Пустой или отсутствующий матчер совпадает со всем.

Матчеры видят канонические имена инструментов. Вызов apply_patch от модели GPT приходит как ApplyPatch, поэтому включайте его, когда отслеживаете правки.

Сужение с помощью if

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

{ "type": "command", "command": "./scripts/guard-push.sh", "if": "Bash(git push *)" }

if проверяется только для событий инструментов.

Типы обработчиков

ТипЧто делаетОсновные поля
commandВыполняет команду оболочки. JSON-вход события поступает на stdincommand, args (форма exec без оболочки), shell, async, timeout, onFailure
httpОтправляет JSON-вход POST-запросом на URL и читает тело ответа как выводurl, headers, allowedEnvVars, timeout, onFailure
mcp_toolВызывает инструмент подключённого MCP-сервераserver, tool, input
promptЗадаёт модели вопрос о событии, на который нужно ответить «да» или «нет». $ARGUMENTS заменяется JSON-входомprompt, model
agentКак prompt, но перед ответом модель может исследовать ситуацию с помощью инструментовprompt, model

Общие поля для всех обработчиков: timeout (в секундах), statusMessage (показывается во время работы), if и once (выполнить один раз и удалить; учитывается для хуков навыков).

onFailure: "block" превращает тайм-аут или сбой в блокировку. По умолчанию упавший хук не останавливает агента.

Вход

Каждый хук получает JSON-объект. Общие поля:

ПолеЗначение
session_idСессия
transcript_pathПуть к переписке сессии, если доступен
cwdТекущий рабочий каталог
permission_modeРежим разрешений сессии
hook_event_nameСобытие, например PreToolUse

События инструментов добавляют tool_name, tool_input и tool_use_id. PostToolUse добавляет tool_response.

Переменные окружения для хуков command:

ПеременнаяЗначение
CUSP_PROJECT_DIR, CLAUDE_PROJECT_DIRКорень проекта. Рабочий каталог хука
CUSP_HOOK_EVENTИмя события
CUSP_ENV_FILE, CLAUDE_ENV_FILEДля SessionStart, Setup, CwdChanged и FileChanged: файл, в который хук может записать строки export NAME=value, чтобы задать переменные окружения для последующих команд оболочки

Вывод

Коды выхода

Код выходаЗначение
0Успех. Если stdout — JSON, он читается как решение (см. ниже). Иначе для некоторых событий stdout может добавляться в контекст
2Блокировка. Причина — в stderr. Что означает блокировка, зависит от события: блокировка PreToolUse останавливает вызов и сообщает модели причину; блокировка Stop заставляет агента продолжить работу
Любой другойНеблокирующая ошибка. Агент продолжает работу, а ошибка показывается вам

JSON-вывод

Для более тонкого управления выведите JSON-объект в stdout с кодом выхода 0:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use the deploy skill instead of calling kubectl directly."
  }
}
ПолеСобытияДействие
continue: false, stopReasonЛюбыеПолностью остановить цикл агента
decision: "block", reasonUserPromptSubmit, PostToolUse, Stop, SubagentStop, …Заблокировать и передать причину обратно
systemMessageЛюбыеУведомление, которое показывается вам
hookSpecificOutput.additionalContextSessionStart, UserPromptSubmit, PostToolUse, …Текст, добавляемый в контекст модели
hookSpecificOutput.permissionDecisionPreToolUseallow, deny, ask или defer
hookSpecificOutput.updatedInputPreToolUseЗаменить вход инструмента перед запуском
hookSpecificOutput.decisionPermissionRequestОтветить на запрос разрешения: { "behavior": "allow" } или deny

Если на одно событие отвечают несколько хуков, побеждает самый строгий ответ: для PreToolUse deny сильнее defer, defer сильнее ask, а ask сильнее allow. Запрет от хука действует в любом режиме разрешений, включая bypass. Контекст от нескольких хуков объединяется в порядке конфигурации. Каждое текстовое поле ограничено 10 000 символов.

События

СобытиеКогда срабатываетМожет блокировать или решать
SessionStartСессия запускается или возобновляетсяДобавляет контекст
SetupВыполняется разовая настройка или обслуживание репозитория—
InstructionsLoadedЗагружается файл инструкций или правило—
UserPromptSubmitПеред отправкой вашего запроса моделиЗаблокировать запрос, добавить контекст
UserPromptExpansionНавык или команда разворачивается в запросЗаблокировать
MessageDisplayПеред показом сообщения ассистентаЗаменить отображаемый текст
PreToolUseПеред запуском инструментаРазрешить, запретить, спросить, отложить; переписать вход
PermissionRequestСейчас будет показан запрос разрешенияОтветить на него
PermissionDeniedАвторежим отклонил вызовЗапросить повтор
PostToolUseПосле успешного выполнения инструментаДобавить контекст, заменить вывод, заблокировать
PostToolUseFailureПосле сбоя инструментаДобавить контекст
PostToolBatchПосле завершения пакета параллельных вызовов инструментовДобавить контекст
NotificationCusp отправляет вам уведомление, например когда ему нужен ваш ввод—
SubagentStartЗапускается субагентДобавляет контекст
SubagentStopСубагент завершает работуЗаблокировать, чтобы он продолжил
TaskCreated, TaskCompletedСоздаётся или выполняется пункт списка задач—
StopАгент завершает свой ходЗаблокировать, чтобы он продолжил
StopFailureХод завершился из-за ошибки—
TeammateIdleАгент-коллега переходит в режим ожидания—
ConfigChangeМеняется файл настроек—
CwdChangedМеняется рабочий каталогЗадать окружение
DirectoryAddedВ рабочую область добавляется каталог—
FileChangedОтслеживаемый файл меняется на дискеЗадать окружение
WorktreeCreate, WorktreeRemoveСоздаётся или удаляется git worktreeWorktreeCreate может указать путь
PreCompact, PostCompactДо и после сжатия контекста—
PreModelSwitch, PostModelSwitchДо и после смены моделиPreModelSwitch может её запретить
Elicitation, ElicitationResultMCP-сервер запрашивает у вас ввод, и после вашего ответаПринять или отклонить
SessionEndСессия завершается—

Примеры

Запретить правку сгенерированных файлов

.cusp/hooks/no-generated.sh
#!/usr/bin/env bash
path=$(jq -r '.tool_input.file_path // empty')
if [[ "$path" == *"/generated/"* ]]; then
  echo "Files under generated/ are produced by 'bun run codegen'. Edit the schema instead." >&2
  exit 2
fi
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|MultiEdit|Write|ApplyPatch|EditLines",
        "hooks": [{ "type": "command", "command": "${CUSP_PROJECT_DIR}/.cusp/hooks/no-generated.sh" }]
      }
    ]
  }
}

Продолжать, пока не пройдут тесты

Хук Stop, который блокирует ход и передаёт вывод тестов, возвращает агента к работе:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "out=$(bun test 2>&1) || { echo \"$out\" | tail -40 >&2; exit 2; }", "timeout": 300 }
        ]
      }
    ]
  }
}

Когда тесты проходят, хук завершается с кодом 0, и ход заканчивается как обычно. Если сбой не зависит от агента, он будет пытаться снова, пока вы его не прервёте; в неинтерактивном режиме сочетайте это с --max-turns.

Добавить контекст при старте сессии

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [{ "type": "command", "command": "echo \"Open issues assigned to me:\"; gh issue list --assignee @me --limit 5" }]
      }
    ]
  }
}

Попросить модель проверить работу

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "The user asked for a change and the agent says it is done. Based on this transcript excerpt, did the agent run the tests and do they pass? $ARGUMENTS"
          }
        ]
      }
    ]
  }
}

Безопасность

  • Хуки выполняются с правами вашего пользователя, вне песочницы. Прежде чем доверять репозиторию, проверьте его скрипты хуков.
  • В недоверенной рабочей области проектные и локальные настройки не могут добавлять хуки.
  • Администраторы могут разрешить хуки только из управляемых настроек с помощью allowManagedHooksOnly и ограничить HTTP-хуки одобренными URL с помощью allowedHttpHookUrls.
  • disableAllHooks: true отключает хуки.

На этой странице