К основному содержанию
скилл CX Codex

hook-developer — скилл для Claude Code: справочник по хукам

Справочник по хукам Claude: схемы входных и выходных данных, регистрация в settings.json, тестирование. Пригодится, когда пишешь свой хук и не помнишь формат.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add parcadei/Continuous-Claude-v3 --skill hook-developer -a claude-code
Открыть на GitHub

Что делает hook-developer

Скилл даёт таблицу всех событий хуков: PreToolUse, PostToolUse, UserPromptSubmit, PermissionRequest, SessionStart, SessionEnd, Stop, SubagentStart, SubagentStop, PreCompact, Notification. Для каждого указано, когда он срабатывает, может ли блокировать вызов и зачем нужен. Тип хука задаётся как type: "command" для bash или type: "prompt" для оценки через LLM.

Дальше идут JSON-схемы входа и выхода по каждому событию. Например, у PreToolUse на выходе permissionDecision со значениями allow, deny или ask, а код выхода 2 блокирует инструмент и показывает stderr модели. У PostToolUse поле ответа называется tool_response, не tool_result — на этом легко споткнуться.

Есть нюансы, которые ломают хуки чаще всего. В Stop и SubagentStop нужно проверять stop_hook_active: true, иначе получишь бесконечный цикл. В SessionStart переменная CLAUDE_ENV_FILE принимает строки вида export VAR=value, чтобы окружение сохранилось. SessionEnd уже не влияет на сессию — только уборка и логи.

Какие задачи решает

  • написать хук с правильной схемой входа и выхода
  • заблокировать опасный вызов инструмента до его выполнения
  • зарегистрировать хук в settings.json без ошибок
  • разобраться, почему хук не получает нужные поля
  • не уйти в бесконечный цикл на Stop и SubagentStop

Примеры запросов агенту

  • Помоги написать хук PreToolUse, который блокирует rm -rf
  • Какой формат выхода у PostToolUse, чтобы вернуть контекст модели?
  • Проверь мой хук на Stop: не зациклится ли он?

Как установить hook-developer

1Через Skills CLI (Vercel)

npx skills add parcadei/Continuous-Claude-v3 --skill hook-developer -a claude-code

2Вручную: положить файлы

~/.claude/skills/hook-developer/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/hook-developer/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора67

repoprompt Исследует кодовую базу через CLI без чтения полных файлов, экономя токены. Пригодится для поиска сигнатур и срезов строк под задачу. CX Codex +1 515установок continuity-ledger Скилл пишет YAML-хендофф с целями, решениями и следующими шагами, чтобы после /clear сессия продолжилась с того же места. CX Codex +1 509установок create-handoff Скилл пишет YAML-документ о ходе работы, чтобы следующая сессия агента подхватила задачу без потери контекста. CX Codex +1 508установок compound-learnings Скилл анализирует сохранённые заметки из недавних сессий и превращает повторяющиеся выводы в постоянные правила и новые скиллы проекта. CX Codex +1 505установок tldr-code Скилл для анализа кода через CLI tldr: дерево файлов, граф вызовов, потоки данных и мёртвый код. Экономит токены по сравнению с чтением файлов целиком. CX Codex +1 503установок research-external Скилл для поиска по внешней документации, библиотекам и практикам 2024–2025 годов без изучения локальной кодовой базы. Запускает поиск через Perplexity, Nia Docs и Firecrawl. CX Codex +1 501установок debug-hooks Пошаговый сценарий отладки хуков Claude Code, когда они молчат, падают без логов или отдают не те данные. CX Codex +1 499установок describe-pr Готовит описание PR по локальному шаблону, изучая изменения и проверки. Нужен перед обновлением текста pull request через GitHub CLI. CX Codex +1 499установок

Похожие

cli-developer VoltAgent Community Проектирует команды и терминальные интерфейсы, помогает добавить разбор аргументов, интерактивные запросы и обработку ошибок. Подходит для разработки CLI-инструментов. 312установок claude-settings-audit getsentry Анализирует репозиторий и предлагает разрешения settings.json для команд только на чтение. Пригодится при настройке проекта или проверке текущих прав. CX Codex +1 4 500установок openclaw-config adisinghstudent Скилл помогает управлять конфигурацией бота OpenClaw, диагностировать каналы связи и находить сбои в шлюзе или сессиях. CX Codex +1 2 800установок mcpc apify Управляет MCP-серверами через консоль: держит фоновые сессии, находит инструменты на лету и вызывает их терминальными командами без раздувания контекста агента. CX Codex +1 815установок agmsg fujibee Скилл для переписки между агентами через SQLite: Claude, Codex, Gemini CLI и другие обмениваются сообщениями без сети и демона. CX Codex +1 ★ 1 567звёзд репо cmux-release manaflow-ai Автоматизирует подготовку релизов cmux: собирает журнал изменений из пулл-реквестов и обновляет версии проекта. CX Codex +1 4 700установок

Рядом в теме «Настройка агента: память, контекст, субагенты»

Всё по теме «Настройка агента: память, контекст, субагенты» →

Вопросы о hook-developer

Как установить hook-developer в Claude?

Выполни npx skills add parcadei/Continuous-Claude-v3 --skill hook-developer -a claude-code. После этого файл скилла лежит в ~/.claude/skills/hook-developer/SKILL.md.

Что делает hook-developer?

Это справочник по хукам Claude: список событий, схемы входных и выходных данных, регистрация в settings.json и подводные камни вроде stop_hook_active.

Чем отличаются типы хуков command и prompt?

type: "command" запускает bash-скрипт, а type: "prompt" отдаёт решение на оценку LLM. Выбор зависит от того, нужна ли логика на shell или суждение модели.

Исходный текст

SKILL.md · MIT
---
name: hook-developer
description: Complete Claude Code hooks reference - input/output schemas, registration, testing patterns
---

# Hook Developer

Complete reference for developing Claude Code hooks. Use this to write hooks with correct input/output schemas.

## When to Use

- Creating a new hook
- Debugging hook input/output format
- Understanding what fields are available
- Setting up hook registration in settings.json
- Learning what hooks can block vs inject context

## Quick Reference

| Hook | Fires When | Can Block? | Primary Use |
|------|-----------|------------|-------------|
| **PreToolUse** | Before tool executes | YES | Block/modify tool calls |
| **PostToolUse** | After tool completes | Partial | React to tool results |
| **UserPromptSubmit** | User sends prompt | YES | Validate/inject context |
| **PermissionRequest** | Permission dialog shows | YES | Auto-approve/deny |
| **SessionStart** | Session begins | NO | Load context, set env vars |
| **SessionEnd** | Session ends | NO | Cleanup/save state |
| **Stop** | Agent finishes | YES | Force continuation |
| **SubagentStart** | Subagent spawns | NO | Pattern coordination |
| **SubagentStop** | Subagent finishes | YES | Force continuation |
| **PreCompact** | Before compaction | NO | Save state |
| **Notification** | Notification sent | NO | Custom alerts |

**Hook type options:** `type: "command"` (bash) or `type: "prompt"` (LLM evaluation)

---

## Hook Input/Output Schemas

### PreToolUse

**Purpose:** Block or modify tool execution before it happens.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "default|plan|acceptEdits|bypassPermissions",
  "hook_event_name": "PreToolUse",
  "tool_name": "string",
  "tool_input": {
    "file_path": "string",
    "command": "string"
  },
  "tool_use_id": "string"
}
```

**Output (JSON):**
```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "string",
    "updatedInput": {}
  },
  "continue": true,
  "stopReason": "string",
  "systemMessage": "string",
  "suppressOutput": true
}
```

**Exit code 2:** Blocks tool, stderr shown to Claude.

**Common matchers:** `Bash`, `Edit|Write`, `Read`, `Task`, `mcp__.*`

---

### PostToolUse

**Purpose:** React to tool execution results, provide feedback to Claude.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "PostToolUse",
  "tool_name": "string",
  "tool_input": {},
  "tool_response": {
    "filePath": "string",
    "success": true,
    "output": "string",
    "exitCode": 0
  },
  "tool_use_id": "string"
}
```

**CRITICAL:** The response field is `tool_response`, NOT `tool_result`.

**Output (JSON):**
```json
{
  "decision": "block",
  "reason": "string",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "string"
  },
  "continue": true,
  "stopReason": "string",
  "suppressOutput": true
}
```

**Blocking:** `"decision": "block"` with `"reason"` prompts Claude to address the issue.

**Common matchers:** `Edit|Write`, `Bash`

---

### UserPromptSubmit

**Purpose:** Validate user prompts, inject context before Claude processes.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "string"
}
```

**Output (Plain text):**
```
Any stdout text is added to context for Claude.
```

**Output (JSON):**
```json
{
  "decision": "block",
  "reason": "string",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "string"
  }
}
```

**Blocking:** `"decision": "block"` erases prompt, shows `"reason"` to user only (not Claude).

**Exit code 2:** Blocks prompt, shows stderr to user only.

---

### PermissionRequest

**Purpose:** Automate permission dialog decisions.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "PermissionRequest",
  "tool_name": "string",
  "tool_input": {}
}
```

**Output:**
```json
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow|deny",
      "updatedInput": {},
      "message": "string",
      "interrupt": false
    }
  }
}
```

---

### SessionStart

**Purpose:** Initialize session, load context, set environment variables.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SessionStart",
  "source": "startup|resume|clear|compact"
}
```

**Environment variable:** `CLAUDE_ENV_FILE` - write `export VAR=value` to persist env vars.

**Output (Plain text or JSON):**
```json
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "string"
  },
  "suppressOutput": true
}
```

Plain text stdout is added as context.

---

### SessionEnd

**Purpose:** Cleanup, save state, log session.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SessionEnd",
  "reason": "clear|logout|prompt_input_exit|other"
}
```

**Output:** Cannot affect session (already ending). Use for cleanup only.

---

### Stop

**Purpose:** Control when Claude stops, force continuation.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}
```

**CRITICAL:** Check `stop_hook_active: true` to prevent infinite loops!

**Output:**
```json
{
  "decision": "block",
  "reason": "string"
}
```

**Blocking:** `"decision": "block"` forces Claude to continue with `"reason"` as prompt.

---

### SubagentStart

**Purpose:** Run when a subagent (Task tool) is spawned.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SubagentStart",
  "agent_id": "string"
}
```

**Output:** Context injection only (cannot block).

---

### SubagentStop

**Purpose:** Control when subagents (Task tool) stop.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false
}
```

**Output:** Same as Stop.

---

### PreCompact

**Purpose:** Save state before context compaction.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "PreCompact",
  "trigger": "manual|auto",
  "custom_instructions": "string"
}
```

**Matchers:** `manual`, `auto`

**Output:**
```json
{
  "continue": true,
  "systemMessage": "string"
}
```

---

### Notification

**Purpose:** Custom notification handling.

**Input:**
```json
{
  "session_id": "string",
  "transcript_path": "string",
  "cwd": "string",
  "permission_mode": "string",
  "hook_event_name": "Notification",
  "message": "string",
  "notification_type": "permission_prompt|idle_prompt|auth_success|elicitation_dialog"
}
```

**Matchers:** `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`, `*`

**Output:**
```json
{
  "continue": true,
  "suppressOutput": true,
  "systemMessage": "string"
}
```

---

## Registration in settings.json

### Standard Structure

```json
{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
            "timeout": 60
          }
        ]
      }
    ]
  }
}
```

### Matcher Patterns

| Pattern | Matches |
|---------|---------|
| `Bash` | Exactly Bash tool |
| `Edit\|Write` | Edit OR Write |
| `Read.*` | Regex: Read* |
| `mcp__.*__write.*` | MCP write tools |
| `*` | All tools |

**Case-sensitive:** `Bash` ≠ `bash`

### Events Requiring Matchers

- PreToolUse - YES (required)
- PostToolUse - YES (required)
- PermissionRequest - YES (required)
- Notification - YES (optional)
- SessionStart - YES (`startup|resume|clear|compact`)
- PreCompact - YES (`manual|auto`)

### Events Without Matchers

```json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }]
      }
    ]
  }
}
```

---

## Hook Types

### Command Hooks (type: "command")

Default type. Executes bash commands or scripts.

```json
{
  "type": "command",
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
  "timeout": 60
}
```

### Prompt-Based Hooks (type: "prompt")

Uses LLM (Haiku) for context-aware decisions. Best for Stop/SubagentStop.

```json
{
  "type": "prompt",
  "prompt": "Evaluate if Claude should stop. Context: $ARGUMENTS. Check if all tasks are complete.",
  "timeout": 30
}
```

**Response schema:**
```json
{
  "decision": "approve" | "block",
  "reason": "Explanation",
  "continue": false,
  "stopReason": "Message to user",
  "systemMessage": "Warning"
}
```

## MCP Tool Naming

MCP tools use pattern `mcp__<server>__<tool>`:

| Pattern | Matches |
|---------|---------|
| `mcp__memory__.*` | All memory server tools |
| `mcp__.*__write.*` | All MCP write tools |
| `mcp__github__.*` | All GitHub tools |

---

## Environment Variables

### Available to All Hooks

| Variable | Description |
|----------|-------------|
| `CLAUDE_PROJECT_DIR` | Absolute path to project root |
| `CLAUDE_CODE_REMOTE` | "true" if remote/web, empty if local CLI |

### SessionStart Only

| Variable | Description |
|----------|-------------|
| `CLAUDE_ENV_FILE` | Path to write `export VAR=value` lines |

### Plugin Hooks Only

| Variable | Description |
|----------|-------------|
| `CLAUDE_PLUGIN_ROOT` | Absolute path to plugin directory |

---

## Exit Codes

| Exit Code | Behavior | stdout | stderr |
|-----------|----------|--------|--------|
| **0** | Success | JSON processed | Ignored |
| **2** | Blocking error | IGNORED | Error message |
| **Other** | Non-blocking error | Ignored | Verbose mode |

### Exit Code 2 by Hook

| Hook | Effect |
|------|--------|
| PreToolUse | Blocks tool, stderr to Claude |
| PostToolUse | stderr to Claude (tool already ran) |
| UserPromptSubmit | Blocks prompt, stderr to user only |
| Stop | Blocks stop, stderr to Claude |

---

## Shell Wrapper Pattern

```bash
#!/bin/bash
set -e
cd "$CLAUDE_PROJECT_DIR/.claude/hooks"
cat | npx tsx src/my-hook.ts
```

Or for bundled:

```bash
#!/bin/bash
set -e
cd "$HOME/.claude/hooks"
cat | node dist/my-hook.mjs
```

---

## TypeScript Handler Pattern

```typescript
import { readFileSync } from 'fs';

interface HookInput {
  session_id: string;
  hook_event_name: string;
  tool_name?: string;
  tool_input?: Record<string, unknown>;
  tool_response?: Record<string, unknown>;
  // ... other fields per hook type
}

function readStdin(): string {
  return readFileSync(0, 'utf-8');
}

async function main() {
  const input: HookInput = JSON.parse(readStdin());

  // Process input

  const output = {
    decision: 'block',  // or undefined to allow
    reason: 'Why blocking'
  };

  console.log(JSON.stringify(output));
}

main().catch(console.error);
```

---

## Testing Hooks

### Manual Test Commands

```bash
# PostToolUse (Write)
echo '{"tool_name":"Write","tool_input":{"file_path":"test.md"},"tool_response":{"success":true},"session_id":"test"}' | \
  .claude/hooks/my-hook.sh

# PreToolUse (Bash)
echo '{"tool_name":"Bash","tool_input":{"command":"ls"},"session_id":"test"}' | \
  .claude/hooks/my-hook.sh

# SessionStart
echo '{"hook_event_name":"SessionStart","source":"startup","session_id":"test"}' | \
  .claude/hooks/session-start.sh

# SessionEnd
echo '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"test"}' | \
  .claude/hooks/session-end.sh

# UserPromptSubmit
echo '{"prompt":"test prompt","session_id":"test"}' | \
  .claude/hooks/prompt-submit.sh
```

### Rebuild After TypeScript Edits

```bash
cd .claude/hooks
npx esbuild src/my-hook.ts \
  --bundle --platform=node --format=esm \
  --outfile=dist/my-hook.mjs
```

---

## Common Patterns

### Block Dangerous Files (PreToolUse)

```python
#!/usr/bin/env python3
import json, sys

data = json.load(sys.stdin)
path = data.get('tool_input', {}).get('file_path', '')

BLOCKED = ['.env', 'secrets.json', '.git/']
if any(b in path for b in BLOCKED):
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": f"Blocked: {path} is protected"
        }
    }))
else:
    print('{}')
```

### Auto-Format Files (PostToolUse)

```bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
  npx prettier --write "$FILE" 2>/dev/null
fi

echo '{}'
```

### Inject Git Context (UserPromptSubmit)

```bash
#!/bin/bash
echo "Git status:"
git status --short 2>/dev/null || echo "(not a git repo)"
echo ""
echo "Recent commits:"
git log --oneline -5 2>/dev/null || echo "(no commits)"
```

### Force Test Verification (Stop)

```python
#!/usr/bin/env python3
import json, sys, subprocess

data = json.load(sys.stdin)

# Prevent infinite loops
if data.get('stop_hook_active'):
    print('{}')
    sys.exit(0)

# Check if tests pass
result = subprocess.run(['npm', 'test'], capture_output=True)
if result.returncode != 0:
    print(json.dumps({
        "decision": "block",
        "reason": "Tests are failing. Please fix before stopping."
    }))
else:
    print('{}')
```

---

## Debugging Checklist

- [ ] Hook registered in settings.json?
- [ ] Shell script has `+x` permission?
- [ ] Bundle rebuilt after TS changes?
- [ ] Using `tool_response` not `tool_result`?
- [ ] Output is valid JSON (or plain text)?
- [ ] Checking `stop_hook_active` in Stop hooks?
- [ ] Using `$CLAUDE_PROJECT_DIR` for paths?

---

## Key Learnings from Past Sessions

1. **Field names matter** - `tool_response` not `tool_result`
2. **Output format** - `decision: "block"` + `reason` for blocking
3. **Exit code 2** - stderr goes to Claude/user, stdout IGNORED
4. **Rebuild bundles** - TypeScript source edits don't auto-apply
5. **Test manually** - `echo '{}' | ./hook.sh` before relying on it
6. **Check outputs first** - `ls .claude/cache/` before editing code
7. **Detached spawn hides errors** - add logging to debug

## See Also

- `/debug-hooks` - Systematic debugging workflow
- `.claude/rules/hooks.md` - Hook development rules

© parcadei · MIT · parcadei/Continuous-Claude-v3

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой