Хуки
Запускайте свои команды, HTTP-запросы или проверки моделью в заданных точках цикла агента — с теми же событиями и JSON-протоколом, что у ваших существующих хуков.
Хуки позволяют встроить собственную логику в цикл агента: форматировать файл после каждой правки, блокировать команду, которую ваша команда никогда не хочет запускать, добавлять контекст при старте сессии или заставлять агента работать, пока не пройдут тесты. В отличие от инструкций, хуки выполняются всегда. Они не зависят от того, вспомнит ли модель что-то сделать.
Хуки Cusp используют распространённый формат хуков: те же события, конфигурация, коды выхода и JSON-вывод. Хуки, которые у вас уже есть в .claude/settings.json, работают в Cusp.
Конфигурация
Хуки задаются в разделе hooks любого файла настроек. Каждому событию соответствует список групп с матчерами, а в каждой группе перечислены обработчики.
{
"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-вход события поступает на stdin | command, 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", reason | UserPromptSubmit, PostToolUse, Stop, SubagentStop, … | Заблокировать и передать причину обратно |
systemMessage | Любые | Уведомление, которое показывается вам |
hookSpecificOutput.additionalContext | SessionStart, UserPromptSubmit, PostToolUse, … | Текст, добавляемый в контекст модели |
hookSpecificOutput.permissionDecision | PreToolUse | allow, deny, ask или defer |
hookSpecificOutput.updatedInput | PreToolUse | Заменить вход инструмента перед запуском |
hookSpecificOutput.decision | PermissionRequest | Ответить на запрос разрешения: { "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 | После завершения пакета параллельных вызовов инструментов | Добавить контекст |
Notification | Cusp отправляет вам уведомление, например когда ему нужен ваш ввод | — |
SubagentStart | Запускается субагент | Добавляет контекст |
SubagentStop | Субагент завершает работу | Заблокировать, чтобы он продолжил |
TaskCreated, TaskCompleted | Создаётся или выполняется пункт списка задач | — |
Stop | Агент завершает свой ход | Заблокировать, чтобы он продолжил |
StopFailure | Ход завершился из-за ошибки | — |
TeammateIdle | Агент-коллега переходит в режим ожидания | — |
ConfigChange | Меняется файл настроек | — |
CwdChanged | Меняется рабочий каталог | Задать окружение |
DirectoryAdded | В рабочую область добавляется каталог | — |
FileChanged | Отслеживаемый файл меняется на диске | Задать окружение |
WorktreeCreate, WorktreeRemove | Создаётся или удаляется git worktree | WorktreeCreate может указать путь |
PreCompact, PostCompact | До и после сжатия контекста | — |
PreModelSwitch, PostModelSwitch | До и после смены модели | PreModelSwitch может её запретить |
Elicitation, ElicitationResult | MCP-сервер запрашивает у вас ввод, и после вашего ответа | Принять или отклонить |
SessionEnd | Сессия завершается | — |
Примеры
Запретить правку сгенерированных файлов
#!/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отключает хуки.