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

25 KiB
Executable File

GUIA COMPLETO: Claude Code Hooks

Versão: 1.0.0 | Data: 2026-02-04 | Autor: Descomplicar®


Índice

  1. Arquitectura de Hooks
  2. Configuração
  3. Variáveis de Ambiente
  4. Casos de Uso Práticos
  5. Padrões e Anti-Padrões
  6. Exemplos de Código
  7. Limitações e Workarounds
  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

{
  "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:

{
  "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

{
  "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

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:

{
  "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

{
  "source": "startup|resume|clear|compact",
  "model": "claude-opus-4-5-20251101",
  "agent_type": "CustomAgent"
}

UserPromptSubmit

{
  "prompt": "User's exact prompt text here"
}

PreToolUse (Bash Example)

{
  "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

// 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

{
  "tool_name": "Write",
  "tool_input": { },
  "tool_response": { "filePath": "/path", "success": true },
  "tool_use_id": "toolu_01ABC123..."
}

PostToolUseFailure

{
  "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

{
  "stop_hook_active": false
}

SubagentStop

{
  "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:

#!/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

#!/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:

{
  "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

#!/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:

{
  "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

#!/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:

{
  "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

#!/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

{
  "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:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Aguarda input'"
          }
        ]
      }
    ]
  }
}

macOS:

{
  "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

{
  "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

#!/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:

#!/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

# CORRECTO
FILE="$CLAUDE_PROJECT_DIR/.file"
jq -r ".tool_input.file_path" <<< "$INPUT"

# ERRADO
FILE=$CLAUDE_PROJECT_DIR/.file

2. Validate JSON Input

#!/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

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

# CORRECTO
HOOK_SCRIPT="$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh"

# ERRADO
HOOK_SCRIPT=".claude/hooks/script.sh"

5. Timeout Configurado por Tipo

{ "type": "command", "timeout": 30 }
{ "type": "prompt", "timeout": 30 }
{ "type": "agent", "timeout": 120 }

6. Async para Side Effects

{
  "type": "command",
  "async": true
}

5.2 ANTI-PADRÕES

Confiar em cwd

# ERRADO
cd .claude && ./hooks/script.sh

# CORRECTO
bash "$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh"

Infinite Stop Hooks

# 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

# ERRADO
npx prettier --write $FILE_PATH

# CORRECTO
npx prettier --write "$FILE_PATH"

Shell profile echo statements

# ~/.zshrc - ERRADO
echo "Shell ready"

# CORRECTO
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

Síncrono para long-running

// 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

#!/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

#!/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

#!/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

#!/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

#!/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

{
  "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

{
  "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

# CORRECTO
if [ "$(jq -r '.stop_hook_active' <<< "$INPUT")" = "true" ]; then
  exit 0
fi

Shell Profile Noise

# ~/.zshrc
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

Hooks não Triggering

# 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

claude --debug 2>&1 | grep -i hook

2. Verbose Mode

Ctrl+O  # Toggle verbose mode

3. Manual Testing

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

./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

# 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


Descomplicar® | 2026