Claude Code Hooks — автоматизация, о которой ты не знал
Hooks в Claude Code позволяют запускать свои команды до и после любого действия Claude. Линтер, форматирование, блокировка файлов — автоматически.
TL;DR: Hooks в Claude Code — это пользовательские команды, которые автоматически запускаются до или после определённых событий: редактирование файла, запуск bash, начало сессии. Можно настроить авто-линтинг, защиту конфигов, персистентное окружение.
Claude Code пишет код, запускает команды, редактирует файлы. Но иногда хочется, чтобы после каждого редактирования Python-файла автоматически запускался ruff format, или чтобы Claude не мог случайно перезаписать docker-compose.prod.yml. Hooks решают именно это.
Как работают hooks
Hook — это команда, которая привязана к событию жизненного цикла Claude Code. Событие наступает — команда запускается. Всё просто.
Есть несколько типов событий:
- SessionStart — при старте сессии Claude Code
- PreToolUse — перед выполнением инструмента (Bash, Edit, Write)
- PostToolUse — после выполнения инструмента
- Notification — когда Claude отправляет уведомление
Настройка через settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "ruff format \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
}
]
}
]
}
}
Этот пример запускает ruff format после каждого редактирования файла. matcher фильтрует, на какие инструменты реагировать — здесь на Edit и Write.
Персистентное окружение через SessionStart
Одна из главных проблем Claude Code: переменные окружения не сохраняются между bash-командами. Каждая команда запускается в чистом шелле. Если ты работаешь с conda или virtualenv — это боль.
Решение через SessionStart hook:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "echo 'source /path/to/venv/bin/activate' >> \"$CLAUDE_ENV_FILE\""
}
]
}
]
}
}
$CLAUDE_ENV_FILE — специальный файл, который Claude Code сорсит перед каждой bash-командой. Всё, что ты туда запишешь при старте, будет доступно в каждой последующей команде.
Это решает проблему, которая мучала многих: нужно активировать conda environment, но Claude Code забывает об этом после первой команды.
Защита файлов от записи
Допустим, у тебя есть продакшн-конфиги, которые Claude точно не должен трогать. Можно заблокировать через deny-правила в permissions, а можно через PreToolUse hook:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo \"$CLAUDE_FILE_PATH\" | grep -q 'deploy/prod' && echo 'BLOCK: production configs are read-only' && exit 1 || exit 0"
}
]
}
]
}
}
Если hook возвращает ненулевой код выхода, действие Claude блокируется. Здесь — любая попытка отредактировать файл в deploy/prod/ будет остановлена.
Мне кажется, это надёжнее, чем deny-правила в permissions, потому что hook может проверять более сложные условия — имя ветки, время суток, что угодно.
Авто-форматирование после редактирования
Самый популярный use case. Claude пишет код, который работает, но форматирование бывает слегка кривое. Hook решает:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "ext=\"${CLAUDE_FILE_PATH##*.}\"; case \"$ext\" in py) ruff format \"$CLAUDE_FILE_PATH\" 2>/dev/null;; js|ts|tsx) npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null;; esac; true"
}
]
}
]
}
}
Определяет расширение файла и запускает нужный форматер: ruff для Python, prettier для JavaScript/TypeScript. Финальный true гарантирует, что hook не заблокирует работу, даже если форматер упал.
Переменные окружения в hooks
Hooks получают набор переменных окружения с контекстом:
CLAUDE_PROJECT_DIR— корневая директория проектаCLAUDE_FILE_PATH— путь к файлу (для Edit/Write/Read)CLAUDE_ENV_FILE— путь к файлу окружения (для SessionStart)CLAUDE_TOOL_NAME— имя инструмента (Bash, Edit, Write)
Эти переменные позволяют писать умные hooks, которые адаптируются к контексту.
Где хранить hooks
Hooks поддерживают ту же систему скоупов, что и settings:
~/.claude/settings.json— глобальные hooks для всех проектов.claude/settings.json— проектные hooks, общие для команды.claude/settings.local.json— локальные, только для тебя
Для команды удобно хранить hooks в проектном settings.json — тогда у всех разработчиков будет одинаковое поведение. Личные hooks (вроде активации conda) лучше в пользовательский файл.
Есть ещё allowManagedHooksOnly в managed settings — для организаций, которые хотят контролировать, какие hooks разрешены. При включении работают только hooks из managed settings и SDK.
Часто задаваемые вопросы
Замедляют ли hooks работу Claude Code? Зависит от самого hook. Быстрые команды вроде ruff format добавляют миллисекунды. Но если hook делает что-то тяжёлое (полный билд, запуск тестов), задержка будет заметна. Оптимизируй команды.
Можно ли отключить hooks временно? Да. Параметр disableAllHooks: true в settings.json отключает все hooks разом.
Что будет, если hook упадёт с ошибкой? Зависит от типа. PreToolUse hook с ненулевым exit code блокирует действие. PostToolUse hook с ошибкой — нет, действие уже выполнено. Рекомендую добавлять || true к PostToolUse hooks.
Что ещё почитать
- Разрешения в Claude Code — allow, deny и sandbox — другой способ контролировать поведение Claude
- CLAUDE.md — как писать инструкции для Claude Code — настройка контекста проекта
- Настройка Claude Code — полный гайд по settings.json — общий обзор всех настроек