* docs: sync to Claude Code v2.1.257 Audit against Claude Code v2.1.257 (repo baseline was v2.1.245, 12 releases behind). Facts verified against the official docs and cross-checked against the locally installed 2.1.257 binary. Critical (P0): - lesson-quiz L06 Q9 asked how many hook events exist, offering 16/25/31/40. With the real answer now 33, no option was correct. Option C 31 -> 33; answer letter unchanged. - 03-skills taught SKILL.md `name`/`description` as required. Official docs now state all frontmatter fields are optional and `name` defaults to the directory name. Corrected in all five languages. Hook events 31 -> 33 (PreModelSwitch/PostModelSwitch, v2.1.251) across 30 occurrences in 22 files, including three that read 25. Delta proved by set-diff against the official event table: no phantom events. Both events added to the 06-hooks tables with matcher, blocking behavior and 30s timeout, plus detailed per-event sections. CLI corrections verified against the binary: - `claude plugin tag` takes a path, not a version - `claude plugin init` / `validate` require their arguments - `plugin init` scaffolds at ~/.claude/skills/<name>/, not project-local - `--channels` entries must be tagged plugin:<name>@<marketplace> - `--remote` is a deprecated alias for `--cloud` - no `claude remote-control` subcommand exists, only the --remote-control flag Also: CATALOG summary-table totals recomputed in all five languages (column 4 is a derived total, so the count change invalidated it); stale Opus 4.6/4.7 rosters aligned to canonical; self-assessment Advanced Features rescored 0-3 to stop a real gap being graded as mastery; 9 dangling quiz Review pointers repaired; new features documented (--restricted, Fable 5.1, experimental.cacheTtl, CLAUDE_CODE_SUBAGENT_MODEL_FORCE). Deliberately out of scope: pre-existing translation staleness whose fix is translation authorship rather than correction (ja/uk April-2026 root-doc bodies, zh stubs, vi/README stub, translated CATALOG "New Features"). Claude-Session: https://claude.ai/code/session_01128xfEd8W7c2cMRw6N7uiF * fix(hooks): complete the 31 → 33 hook-event propagation across all languages The v2.1.257 sync bumped the hook-event count in the lesson and catalog summaries but left 14 files still asserting 31, four of which the same commit restamped 2.1.257. It also bumped the prose count in claude_concepts_guide.md without adding the two rows, making that file contradict itself where it had been internally consistent before. Adds PreModelSwitch and PostModelSwitch (v2.1.251) everywhere an exhaustive enumeration exists, and corrects the count wherever a list is deliberately partial: - README.md / INDEX.md in en, ja, zh, uk and vi/INDEX.md — count 31 → 33 and both events added to the lifecycle group - resources.md in all five languages — count only; these lists end in "and more" and are partial by design - claude_concepts_guide.md — the two missing table rows Also brings every hook-event table up to the 33 its own summary row claims. The English CATALOG.md listed 29; the translated catalogs listed 25 and the ja/uk concept guides 27 and 28. Completing them required translating six events that had never reached the mirrors: Setup, UserPromptExpansion, PermissionDenied, PostToolBatch, MessageDisplay and DirectoryAdded. Summary-table arithmetic is untouched — those columns count example scripts, not events. Verified: no stale count survives outside CHANGELOG history, every exhaustive enumeration lists exactly 33, and pre-commit passes 23/23. Claude-Session: https://claude.ai/code/session_01UXmqJZNk1BR4uerJA85NiW
Хуки
Хуки — це автоматизовані скрипти, які виконуються у відповідь на конкретні події під час сесій Claude Code. Вони забезпечують автоматизацію, валідацію, управління дозволами та кастомні робочі процеси.
Огляд
Хуки — це автоматичні дії (shell-команди, HTTP-вебхуки, LLM-промпти або оцінки субагентів), що виконуються автоматично при виникненні конкретних подій у Claude Code. Вони отримують JSON-вхід і повідомляють результати через коди виходу та JSON-вивід.
Ключові можливості:
- Автоматизація на основі подій
- Введення/виведення на основі JSON
- Підтримка типів хуків: command, prompt, HTTP та agent
- Відповідність шаблонам (pattern matching) для хуків, специфічних для інструментів
Конфігурація
Хуки налаштовуються у файлах налаштувань з конкретною структурою:
~/.claude/settings.json— налаштування користувача (усі проєкти).claude/settings.json— налаштування проєкту (спільні, комітяться).claude/settings.local.json— локальні налаштування проєкту (не комітяться)- Керована політика (Managed policy) — загальноорганізаційні налаштування
hooks/hooks.jsonплагіна — хуки з областю дії плагіна- Frontmatter навичок/агентів — хуки часу життя компонентів
Базова структура конфігурації
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 60
}
]
}
]
}
}
Ключові поля:
| Поле | Опис | Приклад |
|---|---|---|
matcher |
Шаблон для відповідності назвам інструментів (чутливий до регістру) | "Write", "Edit|Write", "*" |
hooks |
Масив визначень хуків | [{ "type": "command", ... }] |
type |
Тип хука: "command" (bash), "prompt" (LLM), "http" (вебхук), "mcp_tool" (виклик MCP-інструмента, v2.1.118+) або "agent" (субагент) |
"command" |
command |
Shell-команда для виконання | "$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh" |
timeout |
Необовʼязковий таймаут у секундах. За замовчуванням: 600 для command/http/mcp_tool, 30 для prompt, 60 для agent. | 30 |
once |
Якщо true, хук запускається лише один раз за сесію |
true |
async |
Якщо true, виконується у фоні без блокування |
true |
asyncRewake |
Якщо true, виконується у фоні та будить Claude при коді виходу 2. Неявно вмикає async. |
true |
shell |
Приймає "bash" або "powershell". За замовчуванням "bash", або "powershell" у Windows, якщо Git Bash не встановлено. |
"bash" |
statusMessage |
Власне повідомлення індикатора, що показується під час роботи хука | "Форматування…" |
Примітка: Деякі події знижують типовий таймаут.
UserPromptSubmitзнижує типове значення для command, http і mcp_tool до 30 секунд, аMessageDisplay— до 10 секунд. ХукиSessionEndмають спільний бюджет 1,5 секунди; якщо у ваших налаштуваннях указано довшийtimeoutдля окремого хука, Claude Code піднімає бюджет відповідно, до 60 секунд.
Шаблони matcher
| Шаблон | Опис | Приклад |
|---|---|---|
| Точний рядок | Відповідає конкретному інструменту | "Write" |
| Regex-шаблон | Відповідає кільком інструментам | "Edit|Write" |
| Підстановочний знак | Відповідає всім інструментам | "*" або "" |
| MCP-інструменти | Шаблон сервера та інструмента | "mcp__memory__.*" |
Значення matcher для InstructionsLoaded:
| Значення matcher | Опис |
|---|---|
session_start |
Інструкції завантажені при запуску сесії |
nested_traversal |
Інструкції завантажені при обході вкладених каталогів |
path_glob_match |
Інструкції завантажені через відповідність glob-шаблону шляху |
Типи хуків
Claude Code підтримує чотири типи хуків:
Command-хуки
Тип за замовчуванням. Виконує shell-команду та комунікує через JSON stdin/stdout і коди виходу.
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate.py\"",
"timeout": 60
}
HTTP-хуки
Додано у v2.1.63.
Віддалені вебхук-ендпоінти, які отримують той самий JSON-вхід, що й command-хуки. HTTP-хуки надсилають POST JSON на URL і отримують JSON-відповідь. HTTP-хуки маршрутизуються через пісочницю (sandbox), коли вона увімкнена. Інтерполяція змінних оточення в URL вимагає явного списку allowedEnvVars з міркувань безпеки.
{
"hooks": {
"PostToolUse": [{
"type": "http",
"url": "https://my-webhook.example.com/hook",
"matcher": "Write"
}]
}
}
Ключові властивості:
"type": "http"— ідентифікує як HTTP-хук"url"— URL ендпоінту вебхука- Маршрутизується через sandbox, коли sandbox увімкнено
- Вимагає явного списку
allowedEnvVarsдля будь-якої інтерполяції змінних оточення в URL
MCP tool-хуки
Додано у v2.1.118.
Тип mcp_tool викликає налаштований MCP-інструмент напряму; конфігурація посилається на ім'я MCP-сервера та інструмента, а не на shell-команду чи URL. Корисно, коли логіка валідації або реакції вже живе в налаштованому вами MCP-сервері.
{
"matcher": "Edit",
"hooks": [{
"type": "mcp_tool",
"server": "my-mcp-server",
"tool": "validate_edit"
}]
}
Ключові властивості:
"type": "mcp_tool"— ідентифікує як MCP tool-хук"server"— ім'я налаштованого MCP-сервера"tool"— ім'я інструмента, який слід викликати на цьому сервері
Prompt-хуки
Промпти, оцінювані LLM, де вміст хука є промптом, який оцінює Claude. Переважно використовуються з подіями Stop та SubagentStop для інтелектуальної перевірки завершення завдань.
{
"type": "prompt",
"prompt": "Evaluate if Claude completed all requested tasks.",
"timeout": 30
}
LLM оцінює промпт і повертає структуроване рішення (деталі див. у Хуки на основі промптів).
Agent-хуки
Хуки верифікації на основі субагентів, які створюють виділеного агента для оцінки умов або виконання складних перевірок. На відміну від prompt-хуків (однокрокова оцінка LLM), agent-хуки можуть використовувати інструменти та виконувати багатокрокове міркування.
{
"type": "agent",
"prompt": "Verify the code changes follow our architecture guidelines. Check the relevant design docs and compare.",
"timeout": 120
}
Примітка: Agent-хуки є експериментальними і можуть змінитися.
Ключові властивості:
"type": "agent"— ідентифікує як agent-хук"prompt"— опис завдання для субагента- Агент може використовувати інструменти (Read, Grep, Bash тощо) для оцінки
- Повертає структуроване рішення, аналогічне prompt-хукам
Події хуків
Claude Code підтримує 33 події хуків:
| Подія | Коли спрацьовує | Вхід matcher | Може блокувати | Типове використання |
|---|---|---|---|---|
| SessionStart | Початок/відновлення/очищення/компакція/форк сесії | startup/resume/clear/compact/fork | Ні | Налаштування середовища |
| Setup | Початкове налаштування середовища (один раз за сесію) | (немає) | Ні | Провізіонування інструментів, встановлення залежностей |
| InstructionsLoaded | Після завантаження CLAUDE.md або файлу правил | (немає) | Ні | Модифікація/фільтрація інструкцій |
| UserPromptSubmit | Користувач подає промпт | (немає) | Так | Валідація промптів |
| UserPromptExpansion | Промпт розгорнуто (наприклад, @-згадки, слеш-команди розв'язано) |
(немає) | Так | Перетворення або перевірка розгорнутого промпту |
| MessageDisplay | Під час відображення тексту відповіді асистента | (немає) | Ні | Перетворення або приховування показаного тексту (v2.1.152) |
| PreToolUse | Перед виконанням інструмента | Назва інструмента | Так (allow/deny/ask) | Валідація, модифікація вхідних даних |
| PermissionRequest | Показ діалогу дозволів | Назва інструмента | Так | Автозатвердження/відхилення |
| PermissionDenied | Користувач відхиляє запит дозволу | Назва інструмента | Ні | Логування, аналітика, політики |
| PostToolUse | Після успішного виконання інструмента | Назва інструмента | Ні | Контекст, зворотний звʼязок |
| PostToolUseFailure | Невдале виконання інструмента | Назва інструмента | Ні | Обробка помилок, логування |
| PostToolBatch | Після завершення пакета викликів інструментів | (немає) | Ні | Зведена звітність, пакетна валідація |
| Notification | Надсилання сповіщення | Тип сповіщення | Ні | Кастомні сповіщення |
| SubagentStart | Створення субагента | Назва типу агента | Ні | Налаштування субагента |
| SubagentStop | Завершення субагента | Назва типу агента | Так | Валідація субагента |
| Stop | Claude завершує відповідь | (немає) | Так | Перевірка завершення завдання |
| StopFailure | Помилка API завершує хід | (немає) | Ні | Відновлення після помилок, логування |
| TeammateIdle | Неактивність учасника Agent Teams | (немає) | Так | Координація учасників |
| TaskCompleted | Завдання позначено як виконане | (немає) | Так | Дії після завершення завдання |
| TaskCreated | Завдання створене через TaskCreate | (немає) | Ні | Відстеження завдань, логування |
| ConfigChange | Зміна файлу конфігурації | (немає) | Так (крім policy) | Реакція на оновлення конфігурації |
| CwdChanged | Зміна робочого каталогу | (немає) | Ні | Налаштування для каталогу |
| DirectoryAdded | Новий робочий каталог зареєстровано під час сесії через /add-dir або control-запит SDK register_repo_root (v2.1.219) |
(немає) | Ні | Налаштування інструментів для доданого каталогу |
| FileChanged | Зміна відстежуваного файлу | (немає) | Ні | Моніторинг файлів, перебудова |
| PreCompact | Перед компакцією контексту | manual/auto | Ні | Дії перед компакцією |
| PostCompact | Після завершення компакції | (немає) | Ні | Дії після компакції |
| PreModelSwitch | Перед тим, як Claude Code застосує запитану зміну моделі | Канонічна назва моделі, на яку відбувається перехід (з to_model) |
Так | Контроль або блокування зміни моделі |
| PostModelSwitch | Після зміни моделі сесії, зокрема змін, які Claude Code робить сам (наприклад, відновлення моделі при відновленні сесії) | Канонічна назва моделі, на яку відбувся перехід (з to_model) |
Ні | Логування або реакція на зміну моделі |
| WorktreeCreate | Створення робочого дерева (worktree) | (немає) | Так (повернення шляху) | Ініціалізація worktree |
| WorktreeRemove | Видалення робочого дерева | (немає) | Ні | Очищення worktree |
| Elicitation | MCP-сервер запитує введення користувача | (немає) | Так | Валідація введення |
| ElicitationResult | Відповідь користувача на elicitation | (немає) | Так | Обробка відповіді |
| SessionEnd | Завершення сесії | (немає) | Ні | Очищення, фінальне логування |
TaskCreatedіTaskCompletedпотребують увімкнених todo-інструментів (v2.1.233). Ці події виникають від todo / task-tracking інструментів (TaskCreate/Get/Update/List,TodoWrite), які більше не доступні на Opus 4.8, Sonnet 5, Fable 5, Mythos 5 та новіших моделях. На цих моделях хук лишається валідною конфігурацією, але просто ніколи не спрацьовує — без виводу і без помилки. ВстановітьCLAUDE_CODE_ENABLE_TODO_TOOLS=1, щоб повернути їх.
PreToolUse
Запускається після створення параметрів інструмента Claude і перед обробкою. Використовується для валідації або модифікації вхідних даних інструмента.
Конфігурація:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py"
}
]
}
]
}
}
Типові matcher: Task, Bash, Glob, Grep, Read, Edit, Write, WebFetch, WebSearch
Управління виводом:
permissionDecision:"allow","deny","ask"або"defer""allow"пропускає запит дозволу (крім інструментів, які потребують взаємодії з користувачем, та інструментів конекторів, для яких організація встановилаask)"deny"блокує виклик інструмента"ask"просить користувача підтвердити"defer"коректно завершує роботу, щоб інструмент можна було відновити пізніше;permissionDecisionReason,updatedInputтаadditionalContextдля цього значення ігноруються- Правила deny та ask обчислюються незалежно від того, що повертає хук. Якщо кілька хуків
PreToolUseдають різні рішення, пріоритет такий:deny>defer>ask>allow
permissionDecisionReason: Пояснення рішення. Показується користувачеві (не Claude) для"allow"та"ask"; показується Claude для"deny"; ігнорується для"defer"updatedInput: Модифіковані вхідні параметри інструмента
PostToolUse
Запускається одразу після завершення інструмента. Використовується для верифікації, логування або надання контексту назад Claude.
Конфігурація:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py"
}
]
}
]
}
}
Управління виводом:
- Рішення
"block"подає Claude зворотний звʼязок additionalContext: Контекст, доданий для Claude
UserPromptSubmit
Запускається, коли користувач подає промпт, перед тим як Claude його обробить.
Конфігурація:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py"
}
]
}
]
}
}
Управління виводом:
decision:"block"для запобігання обробціreason: Пояснення у разі блокуванняadditionalContext: Контекст, доданий до промпта
Stop та SubagentStop
Запускаються, коли Claude завершує відповідь (Stop) або субагент завершує роботу (SubagentStop). Підтримують оцінку на основі промптів для інтелектуальної перевірки завершення завдань.
Додаткове поле введення: Обидва хуки Stop та SubagentStop отримують поле last_assistant_message у JSON-вході, що містить останнє повідомлення від Claude або субагента перед зупинкою. Це корисно для оцінки завершення завдання.
Конфігурація:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if Claude completed all requested tasks.",
"timeout": 30
}
]
}
]
}
}
SubagentStart
Запускається при початку виконання субагента. Вхід matcher — назва типу агента, що дозволяє хукам націлюватися на конкретні типи субагентів.
Конфігурація:
{
"hooks": {
"SubagentStart": [
{
"matcher": "code-review",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/subagent-init.sh"
}
]
}
]
}
}
SessionStart
Запускається при старті або відновленні сесії. Може зберігати змінні оточення.
Matcher: startup, resume, clear, compact, fork
Оновлення v2.1.214: сесії, створені через форк, тепер повідомляють джерело
"fork"замість попереднього"resume".
Спеціальна можливість: Використовуйте CLAUDE_ENV_FILE для збереження змінних оточення (також доступно в хуках CwdChanged та FileChanged):
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
fi
exit 0
SessionEnd
Запускається при завершенні сесії для очищення або фінального логування. Не може блокувати завершення.
Значення поля reason:
clear— користувач очистив сесіюlogout— користувач вийшов із системиprompt_input_exit— користувач вийшов через введення промптаother— інша причина
Конфігурація:
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-cleanup.sh\""
}
]
}
]
}
}
Подія Notification
Оновлені matcher для подій сповіщень:
permission_prompt— сповіщення про запит дозволуidle_prompt— сповіщення про стан простоюauth_success— успішна автентифікаціяelicitation_dialog— діалог, показаний користувачу
Хуки з областю дії компонентів
Хуки можна прикріплювати до конкретних компонентів (навички, агенти, команди) у їхньому frontmatter:
У SKILL.md, agent.md або command.md:
---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/check.sh"
once: true # Запустити лише один раз за сесію
---
Підтримувані події для хуків компонентів: PreToolUse, PostToolUse, Stop
Це дозволяє визначати хуки безпосередньо в компоненті, що їх використовує, зберігаючи повʼязаний код разом.
Хуки у frontmatter субагента
Коли хук Stop визначений у frontmatter субагента, він автоматично перетворюється на хук SubagentStop з областю дії цього субагента. Це гарантує, що хук зупинки спрацьовує лише коли завершує роботу саме цей субагент, а не при зупинці основної сесії.
---
name: code-review-agent
description: Automated code review subagent
hooks:
Stop:
- hooks:
- type: prompt
prompt: "Verify the code review is thorough and complete."
# Наведений Stop-хук автоматично перетворюється на SubagentStop для цього субагента
---
Подія PermissionRequest
Обробка запитів дозволів з кастомним форматом виводу:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow|deny",
"updatedInput": {},
"message": "Custom message",
"interrupt": false
}
}
}
Вхідні та вихідні дані хуків
JSON-вхід (через stdin)
Усі хуки отримують JSON-вхід через stdin:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/current/working/directory",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.js",
"content": "..."
},
"tool_use_id": "toolu_01ABC123...",
"agent_id": "agent-abc123",
"agent_type": "main",
"worktree": "/path/to/worktree"
}
Загальні поля:
| Поле | Опис |
|---|---|
session_id |
Унікальний ідентифікатор сесії |
transcript_path |
Шлях до файлу транскрипту розмови |
cwd |
Поточний робочий каталог |
hook_event_name |
Назва події, що запустила хук |
agent_id |
Ідентифікатор агента, що запускає хук |
agent_type |
Тип агента ("main", назва типу субагента тощо) |
worktree |
Шлях до git worktree, якщо агент працює в ньому |
Коди виходу
| Код виходу | Значення | Поведінка |
|---|---|---|
| 0 | Успіх | Продовжити, розібрати JSON stdout |
| 2 | Блокуюча помилка | Заблокувати операцію, stderr показується як помилка |
| Інші | Неблокуюча помилка | Продовжити, stderr показується у verbose-режимі |
JSON-вивід (stdout, код виходу 0)
{
"continue": true,
"stopReason": "Optional message if stopping",
"suppressOutput": false,
"systemMessage": "Optional warning message",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "File is in allowed directory",
"updatedInput": {
"file_path": "/modified/path.js"
}
}
}
retry(PermissionDenied): Використайте JSONhookSpecificOutput.retry: true, щоб повідомити модель, що вона може повторити відхилений виклик інструмента.
Змінні оточення
| Змінна | Доступність | Опис |
|---|---|---|
CLAUDE_PROJECT_DIR |
Усі хуки | Абсолютний шлях до кореня проєкту |
CLAUDE_ENV_FILE |
SessionStart, CwdChanged, FileChanged | Шлях до файлу для збереження змінних оточення |
CLAUDE_CODE_REMOTE |
Усі хуки | "true" при роботі у віддаленому середовищі |
${CLAUDE_PLUGIN_ROOT} |
Хуки плагінів | Шлях до каталогу плагіна |
${CLAUDE_PLUGIN_DATA} |
Хуки плагінів | Шлях до каталогу даних плагіна |
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS |
Хуки SessionEnd | Налаштовуваний таймаут у мілісекундах для хуків SessionEnd (перевизначає стандартний) |
Хуки на основі промптів
Для подій Stop та SubagentStop можна використовувати оцінку на основі LLM:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review if all tasks are complete. Return your decision.",
"timeout": 30
}
]
}
]
}
}
Схема відповіді LLM:
{
"decision": "approve",
"reason": "All tasks completed successfully",
"continue": false,
"stopReason": "Task complete"
}
Приклади
Приклад 1: Валідатор Bash-команд (PreToolUse)
Файл: .claude/hooks/validate-bash.py
#!/usr/bin/env python3
import json
import sys
import re
BLOCKED_PATTERNS = [
(r"\brm\s+-rf\s+/", "Blocking dangerous rm -rf / command"),
(r"\bsudo\s+rm", "Blocking sudo rm command"),
]
def main():
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name", "")
if tool_name != "Bash":
sys.exit(0)
command = input_data.get("tool_input", {}).get("command", "")
for pattern, message in BLOCKED_PATTERNS:
if re.search(pattern, command):
print(message, file=sys.stderr)
sys.exit(2) # Код виходу 2 = блокуюча помилка
sys.exit(0)
if __name__ == "__main__":
main()
Конфігурація:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\""
}
]
}
]
}
}
Приклад 2: Сканер безпеки (PostToolUse)
Файл: .claude/hooks/security-scan.py
#!/usr/bin/env python3
import json
import sys
import re
SECRET_PATTERNS = [
(r"password\s*=\s*['\"][^'\"]+['\"]", "Potential hardcoded password"),
(r"api[_-]?key\s*=\s*['\"][^'\"]+['\"]", "Potential hardcoded API key"),
]
def main():
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name", "")
if tool_name not in ["Write", "Edit"]:
sys.exit(0)
tool_input = input_data.get("tool_input", {})
content = tool_input.get("content", "") or tool_input.get("new_string", "")
file_path = tool_input.get("file_path", "")
warnings = []
for pattern, message in SECRET_PATTERNS:
if re.search(pattern, content, re.IGNORECASE):
warnings.append(message)
if warnings:
output = {
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": f"Security warnings for {file_path}: " + "; ".join(warnings)
}
}
print(json.dumps(output))
sys.exit(0)
if __name__ == "__main__":
main()
Приклад 3: Автоформатування коду (PostToolUse)
Файл: .claude/hooks/format-code.sh
#!/bin/bash
# Читання JSON з stdin
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('tool_name', ''))")
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin).get('tool_input', {}).get('file_path', ''))")
if [ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ]; then
exit 0
fi
# Форматування залежно від розширення файлу
case "$FILE_PATH" in
*.js|*.jsx|*.ts|*.tsx|*.json)
command -v prettier &>/dev/null && prettier --write "$FILE_PATH" 2>/dev/null
;;
*.py)
command -v black &>/dev/null && black "$FILE_PATH" 2>/dev/null
;;
*.go)
command -v gofmt &>/dev/null && gofmt -w "$FILE_PATH" 2>/dev/null
;;
esac
exit 0
Приклад 4: Валідатор промптів (UserPromptSubmit)
Файл: .claude/hooks/validate-prompt.py
#!/usr/bin/env python3
import json
import sys
import re
BLOCKED_PATTERNS = [
(r"delete\s+(all\s+)?database", "Dangerous: database deletion"),
(r"rm\s+-rf\s+/", "Dangerous: root deletion"),
]
def main():
input_data = json.load(sys.stdin)
prompt = input_data.get("user_prompt", "") or input_data.get("prompt", "")
for pattern, message in BLOCKED_PATTERNS:
if re.search(pattern, prompt, re.IGNORECASE):
output = {
"decision": "block",
"reason": f"Blocked: {message}"
}
print(json.dumps(output))
sys.exit(0)
sys.exit(0)
if __name__ == "__main__":
main()
Приклад 5: Інтелектуальний Stop-хук (на основі промпта)
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review if Claude completed all requested tasks. Check: 1) Were all files created/modified? 2) Were there unresolved errors? If incomplete, explain what's missing.",
"timeout": 30
}
]
}
]
}
}
Приклад 6: Трекер використання контексту (пара хуків)
Відстеження споживання токенів на запит за допомогою хуків UserPromptSubmit (перед повідомленням) та Stop (після відповіді).
Файл: .claude/hooks/context-tracker.py
#!/usr/bin/env python3
"""
Context Usage Tracker — Відстежує споживання токенів на запит.
Використовує UserPromptSubmit як хук "перед повідомленням" і Stop як хук "після відповіді"
для обчислення дельти використання токенів для кожного запиту.
Методи підрахунку токенів:
1. Оцінка за символами (за замовчуванням): ~4 символи на токен, без залежностей
2. tiktoken (необовʼязково): Точніший (~90-95%), потребує: pip install tiktoken
"""
import json
import os
import sys
import tempfile
# Конфігурація
CONTEXT_LIMIT = 1000000 # 1M у Opus 5, Sonnet 5, Opus 4.8, Sonnet 4.6; 200000 у Haiku 4.5 (налаштуйте для вашої моделі)
USE_TIKTOKEN = False # Встановіть True, якщо tiktoken встановлено для кращої точності
def get_state_file(session_id: str) -> str:
"""Отримати шлях до тимчасового файлу для збереження лічильника токенів, ізольовано за сесією."""
return os.path.join(tempfile.gettempdir(), f"claude-context-{session_id}.json")
def count_tokens(text: str) -> int:
"""
Підрахунок токенів у тексті.
Використовує tiktoken з кодуванням p50k_base, якщо доступно (~90-95% точності),
інакше повертається до оцінки за символами (~80-90% точності).
"""
if USE_TIKTOKEN:
try:
import tiktoken
enc = tiktoken.get_encoding("p50k_base")
return len(enc.encode(text))
except ImportError:
pass # Повернутися до оцінки
# Оцінка на основі символів: ~4 символи на токен для англійської
return len(text) // 4
def read_transcript(transcript_path: str) -> str:
"""Читання та конкатенація всього вмісту з файлу транскрипту."""
if not transcript_path or not os.path.exists(transcript_path):
return ""
content = []
with open(transcript_path, "r") as f:
for line in f:
try:
entry = json.loads(line.strip())
# Витяг текстового вмісту з різних форматів повідомлень
if "message" in entry:
msg = entry["message"]
if isinstance(msg.get("content"), str):
content.append(msg["content"])
elif isinstance(msg.get("content"), list):
for block in msg["content"]:
if isinstance(block, dict) and block.get("type") == "text":
content.append(block.get("text", ""))
except json.JSONDecodeError:
continue
return "\n".join(content)
def handle_user_prompt_submit(data: dict) -> None:
"""Хук перед повідомленням: зберегти поточний лічильник токенів перед запитом."""
session_id = data.get("session_id", "unknown")
transcript_path = data.get("transcript_path", "")
transcript_content = read_transcript(transcript_path)
current_tokens = count_tokens(transcript_content)
# Зберегти в тимчасовий файл для подальшого порівняння
state_file = get_state_file(session_id)
with open(state_file, "w") as f:
json.dump({"pre_tokens": current_tokens}, f)
def handle_stop(data: dict) -> None:
"""Хук після відповіді: обчислити дельту та повідомити про використання."""
session_id = data.get("session_id", "unknown")
transcript_path = data.get("transcript_path", "")
transcript_content = read_transcript(transcript_path)
current_tokens = count_tokens(transcript_content)
# Завантажити лічильник перед повідомленням
state_file = get_state_file(session_id)
pre_tokens = 0
if os.path.exists(state_file):
try:
with open(state_file, "r") as f:
state = json.load(f)
pre_tokens = state.get("pre_tokens", 0)
except (json.JSONDecodeError, IOError):
pass
# Обчислити дельту
delta_tokens = current_tokens - pre_tokens
remaining = CONTEXT_LIMIT - current_tokens
percentage = (current_tokens / CONTEXT_LIMIT) * 100
# Повідомити про використання
method = "tiktoken" if USE_TIKTOKEN else "estimated"
print(f"Context ({method}): ~{current_tokens:,} tokens ({percentage:.1f}% used, ~{remaining:,} remaining)", file=sys.stderr)
if delta_tokens > 0:
print(f"This request: ~{delta_tokens:,} tokens", file=sys.stderr)
def main():
data = json.load(sys.stdin)
event = data.get("hook_event_name", "")
if event == "UserPromptSubmit":
handle_user_prompt_submit(data)
elif event == "Stop":
handle_stop(data)
sys.exit(0)
if __name__ == "__main__":
main()
Конфігурація:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-tracker.py\""
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context-tracker.py\""
}
]
}
]
}
}
Як це працює:
UserPromptSubmitспрацьовує перед обробкою промпта — зберігає поточний лічильник токенівStopспрацьовує після відповіді Claude — обчислює дельту та повідомляє про використання- Кожна сесія ізольована через
session_idв імені тимчасового файлу
Методи підрахунку токенів:
| Метод | Точність | Залежності | Швидкість |
|---|---|---|---|
| Оцінка за символами | ~80-90% | Немає | <1мс |
| tiktoken (p50k_base) | ~90-95% | pip install tiktoken |
<10мс |
Примітка: Anthropic не випустили офіційний офлайн-токенізатор. Обидва методи є наближеннями. Транскрипт включає промпти користувача, відповіді Claude та вивід інструментів, але НЕ системні промпти або внутрішній контекст.
Приклад 7: Початкове налаштування дозволів Auto-Mode (одноразовий скрипт)
Одноразовий скрипт налаштування, що додає до ~/.claude/settings.json ~67 безпечних правил дозволів, еквівалентних базовому набору auto-mode Claude Code — без жодного хука, без запамʼятовування майбутніх виборів. Запустіть один раз; безпечно для повторного запуску (пропускає правила, що вже присутні).
Файл: 09-advanced-features/setup-auto-mode-permissions.py
# Попередній перегляд того, що буде додано
python3 09-advanced-features/setup-auto-mode-permissions.py --dry-run
# Застосувати
python3 09-advanced-features/setup-auto-mode-permissions.py
Що додається:
| Категорія | Приклади |
|---|---|
| Вбудовані інструменти | Read(*), Edit(*), Write(*), Glob(*), Grep(*), Agent(*), WebSearch(*) |
| Git читання | Bash(git status:*), Bash(git log:*), Bash(git diff:*) |
| Git запис (локально) | Bash(git add:*), Bash(git commit:*), Bash(git checkout:*) |
| Пакетні менеджери | Bash(npm install:*), Bash(pip install:*), Bash(cargo build:*) |
| Збірка та тестування | Bash(make:*), Bash(pytest:*), Bash(go test:*) |
| Загальні shell-команди | Bash(ls:*), Bash(cat:*), Bash(find:*), Bash(cp:*), Bash(mv:*) |
| GitHub CLI | Bash(gh pr view:*), Bash(gh pr create:*), Bash(gh issue list:*) |
Що навмисно виключено (цей скрипт ніколи не додає):
rm -rf,sudo, force push,git reset --hardDROP TABLE,kubectl delete,terraform destroynpm publish,curl | bash, деплої на продакшн
Хуки плагінів
Плагіни можуть включати хуки у файлі hooks/hooks.json:
Файл: plugins/hooks/hooks.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
}
]
}
]
}
}
Змінні оточення в хуках плагінів:
${CLAUDE_PLUGIN_ROOT}— шлях до каталогу плагіна${CLAUDE_PLUGIN_DATA}— шлях до каталогу даних плагіна
Це дозволяє плагінам включати кастомні хуки валідації та автоматизації.
Хуки MCP-інструментів
MCP-інструменти використовують шаблон mcp__<server>__<tool>:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo '{\"systemMessage\": \"Memory operation logged\"}'"
}
]
}
]
}
}
Міркування безпеки
Застереження
ВИКОРИСТОВУЙТЕ НА ВЛАСНИЙ РИЗИК: Хуки виконують довільні shell-команди. Ви несете повну відповідальність за:
- Команди, які ви налаштовуєте
- Дозволи на доступ/модифікацію файлів
- Потенційну втрату даних або пошкодження системи
- Тестування хуків у безпечних середовищах перед використанням на продакшні
Примітки щодо безпеки
- Потрібна довіра до робочого простору: Команди виводу хуків
statusLineтаfileSuggestionтепер вимагають прийняття довіри до робочого простору перед набранням чинності. - HTTP-хуки та змінні оточення: HTTP-хуки вимагають явного списку
allowedEnvVarsдля використання інтерполяції змінних оточення в URL. Це запобігає випадковому витоку чутливих змінних оточення на віддалені ендпоінти. - Ієрархія керованих налаштувань: Налаштування
disableAllHooksтепер поважає ієрархію керованих налаштувань, тобто налаштування рівня організації можуть примусово вимкнути хуки, що не може бути перевизначено окремими користувачами.
Найкращі практики
| Рекомендовано | Не рекомендовано |
|---|---|
| Валідувати та санітизувати всі вхідні дані | Довіряти вхідним даним сліпо |
Екранувати змінні shell: "$VAR" |
Використовувати без лапок: $VAR |
Блокувати обхід шляху (..) |
Дозволяти довільні шляхи |
Використовувати абсолютні шляхи з $CLAUDE_PROJECT_DIR |
Жорстко кодувати шляхи |
Пропускати чутливі файли (.env, .git/, ключі) |
Обробляти всі файли |
| Тестувати хуки окремо спочатку | Деплоїти неперевірені хуки |
Використовувати явний allowedEnvVars для HTTP-хуків |
Відкривати всі змінні оточення для вебхуків |
Налагодження
Увімкнення режиму налагодження
Запустіть Claude з прапорцем debug для детальних журналів хуків:
claude --debug
Verbose-режим
Використовуйте Ctrl+O в Claude Code для увімкнення verbose-режиму та перегляду прогресу виконання хуків.
Тестування хуків окремо
# Тест із зразковим JSON-вводом
echo '{"tool_name": "Bash", "tool_input": {"command": "ls -la"}}' | python3 .claude/hooks/validate-bash.py
# Перевірка коду виходу
echo $?
Повний приклад конфігурації
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\"",
"timeout": 10
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh\"",
"timeout": 30
},
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py\"",
"timeout": 10
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py\""
}
]
}
],
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/session-init.sh\""
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Verify all tasks are complete before stopping.",
"timeout": 30
}
]
}
]
}
}
Деталі виконання хуків
| Аспект | Поведінка |
|---|---|
| Таймаут | 60 секунд за замовчуванням, налаштовується для кожної команди |
| Паралелізація | Усі відповідні хуки запускаються паралельно |
| Дедуплікація | Ідентичні команди хуків дедуплікуються |
| Середовище | Запускається в поточному каталозі з середовищем Claude Code |
Усунення несправностей
Хук не виконується
- Перевірте правильність синтаксису JSON-конфігурації
- Переконайтеся, що шаблон matcher відповідає назві інструмента
- Перевірте існування та виконуваність скрипта:
chmod +x script.sh - Запустіть
claude --debugдля перегляду журналів виконання хуків - Переконайтеся, що хук читає JSON з stdin (не з аргументів команди)
Хук блокує несподівано
- Тестуйте хук зі зразковим JSON:
echo '{"tool_name": "Write", ...}' | ./hook.py - Перевірте код виходу: має бути 0 для дозволу, 2 для блокування
- Перевірте вивід stderr (показується при коді виходу 2)
Помилки парсингу JSON
- Завжди читайте з stdin, не з аргументів команди
- Використовуйте належний парсинг JSON (не маніпуляцію рядками)
- Обробляйте відсутні поля коректно
Встановлення
Крок 1: Створення каталогу хуків
mkdir -p ~/.claude/hooks
Крок 2: Копіювання прикладів хуків
cp 06-hooks/*.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/*.sh
Крок 3: Налаштування у settings
Відредагуйте ~/.claude/settings.json або .claude/settings.json з конфігурацією хуків, показаною вище.
Повʼязані концепції
- Контрольні точки та відкат — збереження та відновлення стану розмови
- Слеш-команди — створення кастомних слеш-команд
- Навички — повторно використовувані автономні можливості
- Субагенти — делеговане виконання завдань
- Плагіни — обʼєднані пакети розширень
- Розширені функції — дослідження розширених можливостей Claude Code
Додаткові ресурси
- Офіційна документація хуків — повний довідник хуків
- Довідник CLI — документація інтерфейсу командного рядка
- Посібник з памʼяті — конфігурація постійного контексту
Останнє оновлення: 2 вересня 2026 Версія Claude Code: 2.1.257 Джерела:
- https://code.claude.com/docs/en/hooks Сумісні моделі: Claude Fable 5, Claude Opus 5, Claude Sonnet 5, Claude Sonnet 4.6, Claude Opus 4.8, Claude Haiku 4.5