Files
descomplicar-meta-plugin/docs/03-GUIA-HOOKS.md
Emanuel Almeida 692475a315 feat(v1.5.2): Execute database migrations and complete setup
- Execute all 6 migrations on Desk CRM production database
- Create missing tables: cr_lsps, cr_agent_lsps, cr_lsp_usage
- Create archive tables: cr_*_usage_archive (4 tables)
- Create system tables: cr_migrations, cr_maintenance_log
- Make all scripts executable (chmod +x)
- Total cr_* tables: 38

Migration files:
- 001_initial_schema.sql
- 002_add_lsps.sql
- 003_add_relationships.sql
- 004_add_telemetry.sql
- 005_add_archive_tables.sql
- 006_add_maintenance_log.sql

Scripts:
- session-init.sh, session-end.sh
- inject-context.sh, inject-agent-context.sh
- record-usage.sh, db-backup.sh, sync-to-mysql.sh

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 16:18:02 +00:00

1184 lines
25 KiB
Markdown
Executable File

# GUIA COMPLETO: Claude Code Hooks
> **Versão:** 1.0.0 | **Data:** 2026-02-04 | **Autor:** Descomplicar®
---
## Índice
1. [Arquitectura de Hooks](#1-arquitectura-de-hooks)
2. [Configuração](#2-configuração)
3. [Variáveis de Ambiente](#3-variáveis-de-ambiente)
4. [Casos de Uso Práticos](#4-casos-de-uso-práticos)
5. [Padrões e Anti-Padrões](#5-padrões-e-anti-padrões)
6. [Exemplos de Código](#6-exemplos-de-código)
7. [Limitações e Workarounds](#7-limitações-e-workarounds)
8. [Debugging e Troubleshooting](#8-debugging-e-troubleshooting)
---
## 1. ARQUITECTURA DE HOOKS
### 1.1 O que são Hooks?
Hooks são **comandos shell ou prompts LLM** que executam automaticamente em pontos específicos do ciclo de vida do Claude Code. Funcionam como **checkpoints programáveis** que permitem:
- Injectar lógica custom
- Enforçar padrões de código
- Integrar ferramentas externas
- Validar acções antes/depois da execução
**Diferença entre Hooks e Skills:**
| Aspecto | Hooks | Skills |
|---------|-------|--------|
| Execução | Determinística (sempre que evento dispara) | Por decisão do agente |
| Trigger | Automático por evento | Quando Claude escolhe usar |
| Controlo | Total sobre flow | Sugestivo |
### 1.2 Ciclo de Vida Completo
```
SessionStart
UserPromptSubmit → [Hook: validar/enriquecer prompt]
PreToolUse → [Hook: bloquear/modificar tool]
PermissionRequest → [Hook: auto-aprovar/negar]
[Tool Execution]
PostToolUse → [Hook: formatar/validar resultado] ou
PostToolUseFailure → [Hook: log erro/recuperação]
SubagentStart → [Hook: enriquecer contexto subagent]
SubagentStop → [Hook: validar resultado subagent]
Stop → [Hook: decidir se continua]
PreCompact → [Hook: preparar compactação]
SessionEnd → [Hook: cleanup/logging]
```
### 1.3 Ordem de Execução
1. **Matching:** Claude Code verifica se matcher combina com evento
2. **Parallel Execution:** Todos os hooks que combinam correm em paralelo
3. **Deduplication:** Comandos idênticos executam uma vez só
4. **Serial Blocking:** Se hook bloqueia (exit 2), acção é cancelada
5. **Output Processing:** Claude Code processa JSON output
**Importante:** Hooks rodam **sequencialmente em relação a tool calls** mas **em paralelo entre si**.
### 1.4 Eventos Disponíveis (12 total)
| Evento | Quando | Bloqueia? | Matcher Support |
|--------|--------|----------|-----------------|
| **SessionStart** | Início/resumo sessão | Não | `startup`, `resume`, `clear`, `compact` |
| **UserPromptSubmit** | Antes Claude processar prompt | Sim | Nenhum (sempre fire) |
| **PreToolUse** | Antes tool executar | Sim | Tool name (Bash, Edit, Write, etc) |
| **PermissionRequest** | Quando permission dialog mostra | Sim | Tool name |
| **PostToolUse** | Após tool sucesso | Não | Tool name |
| **PostToolUseFailure** | Após tool falha | Não | Tool name |
| **Notification** | Quando notificação dispara | Não | `permission_prompt`, `idle_prompt`, etc |
| **SubagentStart** | Quando subagent spawns | Não | Agent name (Explore, Plan, etc) |
| **SubagentStop** | Quando subagent termina | Sim | Agent name |
| **Stop** | Quando Claude termina resposta | Sim | Nenhum (sempre fire) |
| **PreCompact** | Antes compactação contexto | Não | `manual`, `auto` |
| **SessionEnd** | Quando sessão termina | Não | `clear`, `logout`, `prompt_input_exit`, `other` |
### 1.5 Tipos de Hooks
```
Hook
├── Command Hook (type: "command")
│ ├── Sync (default)
│ └── Async (async: true) ← Não bloqueia Claude
├── Prompt Hook (type: "prompt")
│ └── Single-turn LLM evaluation
└── Agent Hook (type: "agent")
└── Multi-turn LLM com tool access
```
---
## 2. CONFIGURAÇÃO
### 2.1 Ficheiros de Configuração
Hooks são definidos em **JSON settings** com hierarquia de scopes:
```
~/.claude/settings.json (User scope - Global)
↑ (precedência)
.claude/settings.json (Project scope - Shared via git)
.claude/settings.local.json (Project scope - .gitignored)
Managed policy settings (Organization - Highest precedence)
Plugin hooks/hooks.json (When plugin enabled)
Skill/Agent frontmatter (While active)
```
**Regra Precedência:** Higher scope sobrescreve lower scope. Não é cumulativo.
### 2.2 Estrutura JSON Completa
```json
{
"hooks": {
"EventName": [
{
"matcher": "regex_pattern_or_*",
"hooks": [
{
"type": "command|prompt|agent",
"command": "shell_command_or_script_path",
"prompt": "LLM prompt text",
"model": "sonnet|haiku|opus",
"timeout": 600,
"async": false,
"statusMessage": "Custom spinner text"
}
]
}
]
}
}
```
**Campos por tipo:**
| Campo | Command | Prompt | Agent |
|-------|---------|--------|-------|
| `type` | ✓ | ✓ | ✓ |
| `command` | ✓ | - | - |
| `prompt` | - | ✓ | ✓ |
| `model` | - | ✓ | ✓ |
| `timeout` | ✓ | ✓ | ✓ |
| `async` | ✓ | - | - |
| `statusMessage` | ✓ | ✓ | ✓ |
### 2.3 Matcher Patterns (Regex)
Matchers são **regex strings** que filtram eventos:
```json
{
"matcher": "Edit|Write",
"matcher": "Bash",
"matcher": "mcp__memory__.*",
"matcher": "mcp__.*__write.*",
"matcher": "*",
"matcher": ""
}
```
**Matcher por evento:**
| Evento | Matcher contra |
|--------|---------------|
| PreToolUse/PostToolUse/PermissionRequest | tool_name (Bash, Write, Edit, Read, Glob, Grep, Task, WebFetch, WebSearch) |
| SessionStart | source (startup, resume, clear, compact) |
| SessionEnd | reason (clear, logout, prompt_input_exit, other) |
| Notification | notification_type (permission_prompt, idle_prompt) |
| SubagentStart/SubagentStop | agent_type (Bash, Explore, Plan, custom) |
| PreCompact | trigger (manual, auto) |
| UserPromptSubmit/Stop | SEM matcher (sempre fire) |
### 2.4 Path References com Variáveis
```json
{
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/my-script.sh"
}
```
**Variáveis disponíveis:**
| Variável | Descrição |
|----------|-----------|
| `$CLAUDE_PROJECT_DIR` | Raiz do projecto |
| `${CLAUDE_PLUGIN_ROOT}` | Raiz do plugin |
| Qualquer env var | Variáveis do sistema |
### 2.5 Gestão via Menu Interactivo
```bash
claude
/hooks
```
O menu permite:
- Ver todos hooks por evento
- Adicionar novo hook
- Deletar hook existente
- Desabilitar temporariamente todos hooks
- Validar JSON syntax
**Nota:** Edições directas a ficheiros JSON só tomam efeito após reload via `/hooks` ou restart da sessão.
---
## 3. VARIÁVEIS DE AMBIENTE
### 3.1 Input JSON (via stdin)
Todos os hooks recebem **JSON estruturado via stdin**:
```json
{
"session_id": "abc123-def456",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/current/working/dir",
"permission_mode": "default|plan|acceptEdits|dontAsk|bypassPermissions",
"hook_event_name": "PreToolUse"
}
```
### 3.2 Input Schema por Evento
#### SessionStart
```json
{
"source": "startup|resume|clear|compact",
"model": "claude-opus-4-5-20251101",
"agent_type": "CustomAgent"
}
```
#### UserPromptSubmit
```json
{
"prompt": "User's exact prompt text here"
}
```
#### PreToolUse (Bash Example)
```json
{
"tool_name": "Bash",
"tool_use_id": "toolu_01ABC123...",
"tool_input": {
"command": "npm test",
"description": "Run test suite",
"timeout": 120000,
"run_in_background": false
}
}
```
#### Tool-specific Input Schemas
```javascript
// Write tool
tool_input: { "file_path": "/path/to/file", "content": "file content" }
// Edit tool
tool_input: { "file_path": "/path/file", "old_string": "...", "new_string": "...", "replace_all": false }
// Read tool
tool_input: { "file_path": "/path/file", "offset": 10, "limit": 50 }
// Glob tool
tool_input: { "pattern": "**/*.ts", "path": "/optional/dir" }
// Grep tool
tool_input: { "pattern": "TODO.*fix", "path": "/dir", "glob": "*.ts", "output_mode": "content", "-i": true }
// WebFetch tool
tool_input: { "url": "https://api.example.com", "prompt": "Extract endpoints" }
// WebSearch tool
tool_input: { "query": "react hooks", "allowed_domains": [...], "blocked_domains": [...] }
// Task tool (Subagent)
tool_input: { "prompt": "...", "description": "...", "subagent_type": "Explore", "model": "sonnet" }
```
#### PostToolUse
```json
{
"tool_name": "Write",
"tool_input": { },
"tool_response": { "filePath": "/path", "success": true },
"tool_use_id": "toolu_01ABC123..."
}
```
#### PostToolUseFailure
```json
{
"tool_name": "Bash",
"tool_input": { "command": "npm test" },
"tool_use_id": "toolu_01ABC123...",
"error": "Command exited with non-zero status code 1",
"is_interrupt": false
}
```
#### Stop
```json
{
"stop_hook_active": false
}
```
#### SubagentStop
```json
{
"stop_hook_active": false,
"agent_id": "agent-abc123",
"agent_type": "Explore",
"agent_transcript_path": "~/.claude/.../subagents/agent-abc123.jsonl"
}
```
### 3.3 Environment Variables
Hook scripts herdam **todas env vars do sistema** + especiais:
| Variável | Descrição | Disponível em |
|----------|-----------|---------------|
| `CLAUDE_PROJECT_DIR` | Raiz do projecto | Todos |
| `CLAUDE_CODE_REMOTE` | "true" se cloud | Todos |
| `CLAUDE_ENV_FILE` | Path para persistir vars | SessionStart apenas |
### 3.4 Persistir Environment Variables (SessionStart)
Apenas **SessionStart hooks** podem settar env vars permanentemente:
```bash
#!/bin/bash
# ~/.claude/hooks/setup-env.sh
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG=1' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0
```
---
## 4. CASOS DE USO PRÁTICOS
### 4.1 Validação e Bloqueio Pré-Execução
**Caso:** Bloquear comandos perigosos
```bash
#!/bin/bash
# .claude/hooks/block-dangerous.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE 'rm -rf|DROP TABLE|DELETE FROM'; then
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Comando destrutivo bloqueado."
}
}'
exit 0
fi
exit 0
```
**Config:**
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
}
]
}
]
}
}
```
### 4.2 Auto-Formatação Pós-Edição
**Caso:** Rodar Prettier/Ruff após file write
```bash
#!/bin/bash
# .claude/hooks/format-after-write.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
case "$FILE_PATH" in
*.ts|*.tsx|*.js|*.jsx)
npx prettier --write "$FILE_PATH" 2>/dev/null
;;
*.py)
ruff format "$FILE_PATH" 2>/dev/null
;;
*.go)
gofmt -w "$FILE_PATH" 2>/dev/null
;;
esac
exit 0
```
**Config:**
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-after-write.sh",
"timeout": 30
}
]
}
]
}
}
```
### 4.3 Logging e Auditoria
**Caso:** Log de todos bash commands
```bash
#!/bin/bash
# .claude/hooks/audit-bash.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "$TIMESTAMP | $COMMAND" >> ~/.claude/bash-audit.log
exit 0
```
**Config:**
```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-bash.sh",
"async": true
}
]
}
]
}
}
```
### 4.4 Protecção de Ficheiros Sensíveis
**Caso:** Prevenir edições a .env, package-lock.json, .git
```bash
#!/bin/bash
# .claude/hooks/protect-files.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/" "secrets/" "credentials")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Ficheiro protegido: '"$pattern"'"
}
}'
exit 0
fi
done
exit 0
```
### 4.5 Re-inject Context após Compactação
**Caso:** Re-injectar regras do projecto após compact
```json
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'CONTEXT RE-INJECTED:\n\n1. Use Bun, not npm\n2. Run bun test before commits\n3. Follow functional style'"
}
]
}
]
}
}
```
### 4.6 Notificações Desktop
**Linux:**
```json
{
"hooks": {
"Notification": [
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Aguarda input'"
}
]
}
]
}
}
```
**macOS:**
```json
{
"hooks": {
"Notification": [
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude waiting\" with title \"Claude Code\"'"
}
]
}
]
}
}
```
### 4.7 Validação de Testes antes de Stop
**Caso:** Não deixar Claude parar se testes falharem
```json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Run the test suite. Respond with {\"ok\": true} if all tests pass, or {\"ok\": false, \"reason\": \"Test failed\"} if any fail. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}
```
### 4.8 Enriquecer Contexto no UserPromptSubmit
**Caso:** Auto-adicionar info de git/time
```bash
#!/bin/bash
# .claude/hooks/enrich-context.sh
echo ""
echo "=== CONTEXT ==="
echo "Last 3 commits:"
git log --oneline -3 2>/dev/null || echo "No git repo"
echo ""
echo "Modified files:"
git status --short 2>/dev/null | head -5
exit 0
```
### 4.9 Modificação de Input (v2.0.10+)
**Novidade:** PreToolUse pode modificar tool inputs antes da execução:
```bash
#!/bin/bash
# .claude/hooks/force-dry-run.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Forçar --dry-run em comandos npm
if [[ "$COMMAND" == npm* ]] && [[ "$COMMAND" != *"--dry-run"* ]]; then
MODIFIED="$COMMAND --dry-run"
jq -n --arg cmd "$MODIFIED" '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"modifiedInput": {
"command": $cmd
}
}
}'
exit 0
fi
exit 0
```
---
## 5. PADRÕES E ANTI-PADRÕES
### 5.1 BOAS PRÁTICAS
#### 1. Always Quote Variables
```bash
# CORRECTO
FILE="$CLAUDE_PROJECT_DIR/.file"
jq -r ".tool_input.file_path" <<< "$INPUT"
# ERRADO
FILE=$CLAUDE_PROJECT_DIR/.file
```
#### 2. Validate JSON Input
```bash
#!/bin/bash
INPUT=$(cat)
if ! echo "$INPUT" | jq empty 2>/dev/null; then
echo "Invalid JSON input" >&2
exit 1
fi
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
```
#### 3. Block Path Traversal
```bash
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE_PATH" == *".."* ]]; then
echo "Path traversal detected" >&2
exit 2
fi
```
#### 4. Use Absolute Paths
```bash
# CORRECTO
HOOK_SCRIPT="$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh"
# ERRADO
HOOK_SCRIPT=".claude/hooks/script.sh"
```
#### 5. Timeout Configurado por Tipo
```json
{ "type": "command", "timeout": 30 }
{ "type": "prompt", "timeout": 30 }
{ "type": "agent", "timeout": 120 }
```
#### 6. Async para Side Effects
```json
{
"type": "command",
"async": true
}
```
### 5.2 ANTI-PADRÕES
#### ❌ Confiar em cwd
```bash
# ERRADO
cd .claude && ./hooks/script.sh
# CORRECTO
bash "$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh"
```
#### ❌ Infinite Stop Hooks
```bash
# ERRADO - loop infinito
if [ some condition ]; then
echo '{"decision": "block"}'
fi
# CORRECTO
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fi
```
#### ❌ Não citar paths com spaces
```bash
# ERRADO
npx prettier --write $FILE_PATH
# CORRECTO
npx prettier --write "$FILE_PATH"
```
#### ❌ Shell profile echo statements
```bash
# ~/.zshrc - ERRADO
echo "Shell ready"
# CORRECTO
if [[ $- == *i* ]]; then
echo "Shell ready"
fi
```
#### ❌ Síncrono para long-running
```json
// ERRADO
{ "command": "npm run test-suite", "async": false }
// CORRECTO
{ "command": "npm run test-suite", "async": true }
```
### 5.3 Performance Considerations
| Padrão | Impacto | Recomendação |
|--------|---------|--------------|
| Multiple hooks mesmo evento | O(n) paralelo | OK até 5 hooks |
| Timeout muito alto | Bloqueia Claude | Use defaults |
| Shell scripts lentos | Latência | Considerar async |
| JQ parsing complex | CPU intensive | Python/Node |
| File I/O em hook | Disk latency | Minimizar |
**Benchmarks típicos:**
| Operação | Tempo |
|----------|-------|
| Simple bash | 10-50ms |
| JQ parsing | 5-20ms |
| Prettier format | 100-500ms |
| Test suite | 5-30s |
---
## 6. EXEMPLOS DE CÓDIGO
### 6.1 Validador Inteligente de Bash
```bash
#!/bin/bash
# .claude/hooks/validate-bash.sh
set -euo pipefail
INPUT=$(cat)
if ! COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null); then
echo "Failed to parse hook input" >&2
exit 1
fi
declare -a DENY_PATTERNS=(
'rm -rf'
'DROP TABLE'
'DELETE FROM'
'> /etc/'
'sudo'
'chmod 777'
)
for pattern in "${DENY_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qiF "$pattern"; then
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Padrão perigoso: '"$pattern"'"
}
}'
exit 0
fi
done
exit 0
```
### 6.2 Code Quality Enforcement
```bash
#!/bin/bash
# .claude/hooks/enforce-quality.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE_PATH" ] && exit 0
case "$FILE_PATH" in
*.ts|*.tsx|*.js|*.jsx)
npx eslint --fix "$FILE_PATH" 2>/dev/null || true
npx prettier --write "$FILE_PATH" 2>/dev/null || true
;;
*.py)
ruff format "$FILE_PATH" 2>/dev/null || true
mypy "$FILE_PATH" 2>/dev/null || true
;;
*.go)
gofmt -w "$FILE_PATH" 2>/dev/null || true
;;
*.rs)
rustfmt "$FILE_PATH" 2>/dev/null || true
;;
esac
exit 0
```
### 6.3 Auto-Approval com Lógica Custom
```bash
#!/bin/bash
# .claude/hooks/smart-permissions.sh
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
# Read-only tools: auto-approve
case "$TOOL_NAME" in
Read|Glob|Grep|WebFetch|WebSearch)
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": { "behavior": "allow" }
}
}'
exit 0
;;
esac
exit 0
```
### 6.4 Context Injection na Sessão
```bash
#!/bin/bash
# .claude/hooks/inject-session-context.sh
CONTEXT=""
if [ -d .git ]; then
CONTEXT+="=== Git Status ===$(printf '\n')"
CONTEXT+="$(git status --short | head -5)$(printf '\n\n')"
CONTEXT+="=== Recent Commits ===$(printf '\n')"
CONTEXT+="$(git log --oneline -3)$(printf '\n\n')"
fi
if [ -f package.json ]; then
CONTEXT+="=== Test Suite ===$(printf '\n')"
CONTEXT+="Ready to run: npm test$(printf '\n\n')"
fi
jq -n --arg ctx "$CONTEXT" '{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": $ctx
}
}'
exit 0
```
### 6.5 Async Test Runner
```bash
#!/bin/bash
# .claude/hooks/run-tests-async.sh
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[[ ! "$FILE_PATH" =~ \.(ts|js|py|rs)$ ]] && exit 0
TEST_OUTPUT=$(npm test 2>&1 || true)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
jq -n '{
"systemMessage": "✓ Tests passed"
}'
else
jq -n --arg output "$TEST_OUTPUT" '{
"systemMessage": "✗ Tests failed",
"additionalContext": "Test output:\n" + $output
}'
fi
exit 0
```
### 6.6 Prompt-Based Hook: Stop Decision
```json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop. Check:\n1. All tasks complete?\n2. Any errors need fixing?\n3. Follow-up needed?\n\nRespond: {\"ok\": true} or {\"ok\": false, \"reason\": \"...\"}",
"timeout": 30,
"model": "sonnet"
}
]
}
]
}
}
```
### 6.7 Agent-Based Hook: Test Validation
```json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify all tests pass. Use Bash to run test suite. Respond {\"ok\": true} if pass or {\"ok\": false, \"reason\": \"...\"} if fail.",
"timeout": 300,
"model": "sonnet"
}
]
}
]
}
}
```
---
## 7. LIMITAÇÕES E WORKAROUNDS
### 7.1 Limitações Técnicas
| Limitação | Detalhe | Workaround |
|-----------|---------|-----------|
| Sem tool calls directos | Hooks não chamam tools | Agent hooks |
| PostToolUse não bloqueia | Tool já executou | Use PreToolUse |
| Timeout 10min default | Long-running bloqueia | `async: true` |
| Sem slash commands | Não pode `/compact` | Script manipulation |
| Single-turn prompts | One-shot apenas | Agent hooks |
| Matcher case-sensitive | "Bash" ≠ "bash" | Exact case |
| PermissionRequest em -p | Não dispara | Use PreToolUse |
### 7.2 Workarounds Comuns
#### Infinite Stop Loop
```bash
# CORRECTO
if [ "$(jq -r '.stop_hook_active' <<< "$INPUT")" = "true" ]; then
exit 0
fi
```
#### Shell Profile Noise
```bash
# ~/.zshrc
if [[ $- == *i* ]]; then
echo "Shell ready"
fi
```
#### Hooks não Triggering
```bash
# Debug checklist:
claude --debug
# 1. Matcher case-sensitive?
# 2. JSON syntax valid?
# 3. Ficheiro executable?
# 4. Event name correcto?
# 5. File path existe?
```
### 7.3 Feature Gaps
| Feature | Status | Alternative |
|---------|--------|-------------|
| Hook para Custom Subagents | ❌ | Skill frontmatter |
| Hook Output Transforms | Parcial | JSON output fields |
| Conditional Execution | ❌ | Prompt/Agent hooks |
| Hook Chaining | ❌ | Multiple hooks |
| Rollback Actions | ❌ | Logging + manual |
---
## 8. DEBUGGING E TROUBLESHOOTING
### 8.1 Técnicas de Debug
#### 1. Debug Mode
```bash
claude --debug 2>&1 | grep -i hook
```
#### 2. Verbose Mode
```
Ctrl+O # Toggle verbose mode
```
#### 3. Manual Testing
```bash
echo '{
"session_id": "test",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf /tmp" }
}' | /path/to/hook.sh
echo "Exit code: $?"
```
#### 4. JSON Validation
```bash
./hook.sh < input.json | jq empty
echo "Valid: $?"
```
### 8.2 Common Errors
| Erro | Causa | Solução |
|------|-------|---------|
| `command not found` | Path incorrecto | `$CLAUDE_PROJECT_DIR` |
| `JSON validation failed` | Invalid output | Test com `jq empty` |
| `jq: command not found` | jq não instalado | `apt install jq` |
| `Permission denied` | Não executable | `chmod +x` |
| `Hook timed out` | Script lento | Aumentar timeout |
| `Exit code 127` | Script not found | Verificar path |
| `Matcher mismatch` | Case errado | Use correct case |
### 8.3 Troubleshooting Checklist
```
1. [ ] Hook aparece em /hooks menu?
2. [ ] Hook dispara quando esperado?
3. [ ] Script executa correctamente?
4. [ ] JSON output processado?
5. [ ] Performance aceitável?
6. [ ] Infinite loops ou side effects?
```
### 8.4 Debug Session Completa
```bash
# 1. Run with debug
$ claude --debug 2>&1 | tee debug.log
# 2. Look for errors
$ grep -i "hook.*error" debug.log
# 3. Test manually
$ cat > test_input.json << 'EOF'
{
"session_id": "test",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {"command": "npm test"}
}
EOF
$ cat test_input.json | .claude/hooks/my-hook.sh
$ echo "Exit code: $?"
# 4. Validate JSON
$ cat test_input.json | .claude/hooks/my-hook.sh | jq empty
# 5. Check matcher
$ grep -A2 "matcher" .claude/settings.json
# 6. Reload hooks
/hooks # no Claude Code
```
---
## REFERÊNCIAS
- [Hooks reference - Claude Code Docs](https://code.claude.com/docs/en/hooks)
- [Automate workflows with hooks](https://code.claude.com/docs/en/hooks-guide)
- [Claude Code power user customization](https://claude.com/blog/how-to-configure-hooks)
- [GitHub - claude-code-hooks-mastery](https://github.com/disler/claude-code-hooks-mastery)
- [Complete guide to hooks - Eesel AI](https://www.eesel.ai/blog/hooks-in-claude-code)
---
**Descomplicar®** | 2026