Claude How To # Хуки Хуки — це автоматизовані скрипти, які виконуються у відповідь на конкретні події під час сесій 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 навичок/агентів — хуки часу життя компонентів ### Базова структура конфігурації ```json { "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"` (вебхук) або `"agent"` (субагент) | `"command"` | | `command` | Shell-команда для виконання | `"$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh"` | | `timeout` | Необовʼязковий таймаут у секундах (за замовчуванням 60) | `30` | | `once` | Якщо `true`, хук запускається лише один раз за сесію | `true` | ### Шаблони 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 і коди виходу. ```json { "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` з міркувань безпеки. ```json { "hooks": { "PostToolUse": [{ "type": "http", "url": "https://my-webhook.example.com/hook", "matcher": "Write" }] } } ``` **Ключові властивості:** - `"type": "http"` — ідентифікує як HTTP-хук - `"url"` — URL ендпоінту вебхука - Маршрутизується через sandbox, коли sandbox увімкнено - Вимагає явного списку `allowedEnvVars` для будь-якої інтерполяції змінних оточення в URL ### Prompt-хуки Промпти, оцінювані LLM, де вміст хука є промптом, який оцінює Claude. Переважно використовуються з подіями `Stop` та `SubagentStop` для інтелектуальної перевірки завершення завдань. ```json { "type": "prompt", "prompt": "Evaluate if Claude completed all requested tasks.", "timeout": 30 } ``` LLM оцінює промпт і повертає структуроване рішення (деталі див. у [Хуки на основі промптів](#хуки-на-основі-промптів)). ### Agent-хуки Хуки верифікації на основі субагентів, які створюють виділеного агента для оцінки умов або виконання складних перевірок. На відміну від prompt-хуків (однокрокова оцінка LLM), agent-хуки можуть використовувати інструменти та виконувати багатокрокове міркування. ```json { "type": "agent", "prompt": "Verify the code changes follow our architecture guidelines. Check the relevant design docs and compare.", "timeout": 120 } ``` **Ключові властивості:** - `"type": "agent"` — ідентифікує як agent-хук - `"prompt"` — опис завдання для субагента - Агент може використовувати інструменти (Read, Grep, Bash тощо) для оцінки - Повертає структуроване рішення, аналогічне prompt-хукам ## Події хуків Claude Code підтримує **26 подій хуків**: | Подія | Коли спрацьовує | Вхід matcher | Може блокувати | Типове використання | |-------|----------------|-------------|----------------|---------------------| | **SessionStart** | Початок/відновлення/очищення/компакція сесії | startup/resume/clear/compact | Ні | Налаштування середовища | | **InstructionsLoaded** | Після завантаження CLAUDE.md або файлу правил | (немає) | Ні | Модифікація/фільтрація інструкцій | | **UserPromptSubmit** | Користувач подає промпт | (немає) | Так | Валідація промптів | | **PreToolUse** | Перед виконанням інструмента | Назва інструмента | Так (allow/deny/ask) | Валідація, модифікація вхідних даних | | **PermissionRequest** | Показ діалогу дозволів | Назва інструмента | Так | Автозатвердження/відхилення | | **PermissionDenied** | Користувач відхиляє запит дозволу | Назва інструмента | Ні | Логування, аналітика, політики | | **PostToolUse** | Після успішного виконання інструмента | Назва інструмента | Ні | Контекст, зворотний звʼязок | | **PostToolUseFailure** | Невдале виконання інструмента | Назва інструмента | Ні | Обробка помилок, логування | | **Notification** | Надсилання сповіщення | Тип сповіщення | Ні | Кастомні сповіщення | | **SubagentStart** | Створення субагента | Назва типу агента | Ні | Налаштування субагента | | **SubagentStop** | Завершення субагента | Назва типу агента | Так | Валідація субагента | | **Stop** | Claude завершує відповідь | (немає) | Так | Перевірка завершення завдання | | **StopFailure** | Помилка API завершує хід | (немає) | Ні | Відновлення після помилок, логування | | **TeammateIdle** | Неактивність учасника Agent Teams | (немає) | Так | Координація учасників | | **TaskCompleted** | Завдання позначено як виконане | (немає) | Так | Дії після завершення завдання | | **TaskCreated** | Завдання створене через TaskCreate | (немає) | Ні | Відстеження завдань, логування | | **ConfigChange** | Зміна файлу конфігурації | (немає) | Так (крім policy) | Реакція на оновлення конфігурації | | **CwdChanged** | Зміна робочого каталогу | (немає) | Ні | Налаштування для каталогу | | **FileChanged** | Зміна відстежуваного файлу | (немає) | Ні | Моніторинг файлів, перебудова | | **PreCompact** | Перед компакцією контексту | manual/auto | Ні | Дії перед компакцією | | **PostCompact** | Після завершення компакції | (немає) | Ні | Дії після компакції | | **WorktreeCreate** | Створення робочого дерева (worktree) | (немає) | Так (повернення шляху) | Ініціалізація worktree | | **WorktreeRemove** | Видалення робочого дерева | (немає) | Ні | Очищення worktree | | **Elicitation** | MCP-сервер запитує введення користувача | (немає) | Так | Валідація введення | | **ElicitationResult** | Відповідь користувача на elicitation | (немає) | Так | Обробка відповіді | | **SessionEnd** | Завершення сесії | (немає) | Ні | Очищення, фінальне логування | ### PreToolUse Запускається після створення параметрів інструмента Claude і перед обробкою. Використовується для валідації або модифікації вхідних даних інструмента. **Конфігурація:** ```json { "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"` - `permissionDecisionReason`: Пояснення рішення - `updatedInput`: Модифіковані вхідні параметри інструмента ### PostToolUse Запускається одразу після завершення інструмента. Використовується для верифікації, логування або надання контексту назад Claude. **Конфігурація:** ```json { "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py" } ] } ] } } ``` **Управління виводом:** - Рішення `"block"` подає Claude зворотний звʼязок - `additionalContext`: Контекст, доданий для Claude ### UserPromptSubmit Запускається, коли користувач подає промпт, перед тим як Claude його обробить. **Конфігурація:** ```json { "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 або субагента перед зупинкою. Це корисно для оцінки завершення завдання. **Конфігурація:** ```json { "hooks": { "Stop": [ { "hooks": [ { "type": "prompt", "prompt": "Evaluate if Claude completed all requested tasks.", "timeout": 30 } ] } ] } } ``` ### SubagentStart Запускається при початку виконання субагента. Вхід matcher — назва типу агента, що дозволяє хукам націлюватися на конкретні типи субагентів. **Конфігурація:** ```json { "hooks": { "SubagentStart": [ { "matcher": "code-review", "hooks": [ { "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/subagent-init.sh" } ] } ] } } ``` ### SessionStart Запускається при старті або відновленні сесії. Може зберігати змінні оточення. **Matcher:** `startup`, `resume`, `clear`, `compact` **Спеціальна можливість:** Використовуйте `CLAUDE_ENV_FILE` для збереження змінних оточення (також доступно в хуках `CwdChanged` та `FileChanged`): ```bash #!/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` — інша причина **Конфігурація:** ```json { "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:** ```yaml --- 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` з областю дії цього субагента. Це гарантує, що хук зупинки спрацьовує лише коли завершує роботу саме цей субагент, а не при зупинці основної сесії. ```yaml --- 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 Обробка запитів дозволів з кастомним форматом виводу: ```json { "hookSpecificOutput": { "hookEventName": "PermissionRequest", "decision": { "behavior": "allow|deny", "updatedInput": {}, "message": "Custom message", "interrupt": false } } } ``` ## Вхідні та вихідні дані хуків ### JSON-вхід (через stdin) Усі хуки отримують JSON-вхід через stdin: ```json { "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) ```json { "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" } } } ``` ## Змінні оточення | Змінна | Доступність | Опис | |--------|------------|------| | `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: ```json { "hooks": { "Stop": [ { "hooks": [ { "type": "prompt", "prompt": "Review if all tasks are complete. Return your decision.", "timeout": 30 } ] } ] } } ``` **Схема відповіді LLM:** ```json { "decision": "approve", "reason": "All tasks completed successfully", "continue": false, "stopReason": "Task complete" } ``` ## Приклади ### Приклад 1: Валідатор Bash-команд (PreToolUse) **Файл:** `.claude/hooks/validate-bash.py` ```python #!/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() ``` **Конфігурація:** ```json { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-bash.py\"" } ] } ] } } ``` ### Приклад 2: Сканер безпеки (PostToolUse) **Файл:** `.claude/hooks/security-scan.py` ```python #!/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` ```bash #!/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` ```python #!/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-хук (на основі промпта) ```json { "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` ```python #!/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 = 128000 # Контекстне вікно Claude (налаштуйте для вашої моделі) 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() ``` **Конфігурація:** ```json { "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\"" } ] } ] } } ``` **Як це працює:** 1. `UserPromptSubmit` спрацьовує перед обробкою промпта — зберігає поточний лічильник токенів 2. `Stop` спрацьовує після відповіді Claude — обчислює дельту та повідомляє про використання 3. Кожна сесія ізольована через `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` ```bash # Попередній перегляд того, що буде додано 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 --hard` - `DROP TABLE`, `kubectl delete`, `terraform destroy` - `npm publish`, `curl | bash`, деплої на продакшн ## Хуки плагінів Плагіни можуть включати хуки у файлі `hooks/hooks.json`: **Файл:** `plugins/hooks/hooks.json` ```json { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh" } ] } ] } } ``` **Змінні оточення в хуках плагінів:** - `${CLAUDE_PLUGIN_ROOT}` — шлях до каталогу плагіна - `${CLAUDE_PLUGIN_DATA}` — шлях до каталогу даних плагіна Це дозволяє плагінам включати кастомні хуки валідації та автоматизації. ## Хуки MCP-інструментів MCP-інструменти використовують шаблон `mcp____`: ```json { "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 для детальних журналів хуків: ```bash claude --debug ``` ### Verbose-режим Використовуйте `Ctrl+O` в Claude Code для увімкнення verbose-режиму та перегляду прогресу виконання хуків. ### Тестування хуків окремо ```bash # Тест із зразковим JSON-вводом echo '{"tool_name": "Bash", "tool_input": {"command": "ls -la"}}' | python3 .claude/hooks/validate-bash.py # Перевірка коду виходу echo $? ``` ## Повний приклад конфігурації ```json { "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: Створення каталогу хуків ```bash mkdir -p ~/.claude/hooks ``` ### Крок 2: Копіювання прикладів хуків ```bash cp 06-hooks/*.sh ~/.claude/hooks/ chmod +x ~/.claude/hooks/*.sh ``` ### Крок 3: Налаштування у settings Відредагуйте `~/.claude/settings.json` або `.claude/settings.json` з конфігурацією хуків, показаною вище. ## Повʼязані концепції - **[Контрольні точки та відкат](../08-checkpoints/)** — збереження та відновлення стану розмови - **[Слеш-команди](../01-slash-commands/)** — створення кастомних слеш-команд - **[Навички](../03-skills/)** — повторно використовувані автономні можливості - **[Субагенти](../04-subagents/)** — делеговане виконання завдань - **[Плагіни](../07-plugins/)** — обʼєднані пакети розширень - **[Розширені функції](../09-advanced-features/)** — дослідження розширених можливостей Claude Code ## Додаткові ресурси - **[Офіційна документація хуків](https://code.claude.com/docs/en/hooks)** — повний довідник хуків - **[Довідник CLI](https://code.claude.com/docs/en/cli-reference)** — документація інтерфейсу командного рядка - **[Посібник з памʼяті](../02-memory/)** — конфігурація постійного контексту --- **Останнє оновлення**: 9 квітня 2026 **Версія Claude Code**: 2.1.97 **Сумісні моделі**: Claude Sonnet 4.6, Claude Opus 4.6, Claude Haiku 4.5