🧠FFVAcademy
🪝

Hooks: automatizar revisões, validações e ações customizadas

14 min de leitura·+70 XP
Pré-requisitos (0/1)0%

Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.

Hooks são scripts shell que o Claude Code executa automaticamente em resposta a eventos — antes ou depois de usar ferramentas, ao completar uma resposta, ao iniciar uma sessão. Eles são o mecanismo de automação determinística: enquanto instruções no CLAUDE.md dependem de Claude seguir, hooks são executados pelo runtime do Claude Code independentemente.

Eventos disponíveis: quando cada hook dispara

EventoQuando disparaPode bloquear?Caso de uso típico
PreToolUseAntes de Claude usar uma ferramentaSim (exit ≠ 0 bloqueia)Validar antes de editar, verificar permissões
PostToolUseDepois de Claude usar uma ferramentaNãoRodar lint, formatar código, notificar
StopQuando Claude completa a respostaNãoResumir sessão, notificar por Slack, fazer backup
NotificationQuando Claude precisa de atenção do usuárioNãoDesktop notification, som, mensagem
SubagentStopQuando um subagente terminaNãoColetar resultado de tarefas paralelas
# Estrutura: hooks ficam em .claude/hooks/ (por projeto) ou ~/.claude/hooks/ (global)

# Nomeação: <evento>/<nome-do-hook>.sh
# Exemplos:
.claude/hooks/
├── PostToolUse/
│   ├── auto-lint.sh          # roda lint depois de qualquer Edit
│   ├── auto-format.sh        # formata o arquivo editado
│   └── notify-edit.sh        # loga edições em audit log
├── PreToolUse/
│   ├── protect-prod.sh       # bloqueia edição em arquivos de config de produção
│   └── require-tests.sh      # bloqueia Bash(npm deploy) sem testes passando
├── Stop/
│   └── slack-notify.sh       # posta no Slack quando Claude termina uma tarefa
└── Notification/
    └── desktop-alert.sh      # notificação macOS quando Claude precisa de input

# Cada hook recebe via stdin um JSON com contexto:
# Para PostToolUse/PreToolUse:
# {
#   "tool_name": "Edit",
#   "tool_input": {
#     "file_path": "/path/to/arquivo.ts",
#     "old_string": "...",
#     "new_string": "..."
#   }
# }

Hook PostToolUse: lint automático após edições

#!/bin/bash
# .claude/hooks/PostToolUse/auto-lint.sh
# Roda lint automaticamente quando Claude edita arquivos de código

# Ler o JSON do stdin
INPUT=$(cat)

# Extrair nome da ferramenta e path do arquivo
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('tool_name', ''))")
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('tool_input', {}).get('file_path', ''))")

# Só agir em edições de arquivo (Edit ou Write)
if [[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]]; then
  exit 0
fi

# Sem arquivo? Sai silenciosamente
if [[ -z "$FILE_PATH" ]]; then
  exit 0
fi

# Rodar o linter adequado por extensão
case "$FILE_PATH" in
  *.ts|*.tsx)
    cd "$(dirname "$FILE_PATH")/.."
    npx eslint "$FILE_PATH" --fix --quiet 2>&1
    ;;
  *.py)
    ruff check "$FILE_PATH" --fix --quiet 2>&1
    ;;
  *.go)
    gofmt -w "$FILE_PATH" 2>&1
    ;;
esac

# Hooks PostToolUse: exit code não afeta Claude — é apenas notificação
exit 0

Hook PreToolUse: bloquear ações não autorizadas

#!/bin/bash
# .claude/hooks/PreToolUse/protect-prod-config.sh
# Bloqueia edições em arquivos de configuração de produção

INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('tool_name', ''))")
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('tool_input', {}).get('file_path', ''), 2>'/dev/null')" 2>/dev/null)

# Só verificar edições de arquivo
if [[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]]; then
  exit 0
fi

# Verificar se o arquivo é de configuração de produção
PROTECTED_PATTERNS=(
  "config/production.json"
  ".env.production"
  "terraform/prod/"
  "k8s/production/"
)

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "BLOQUEADO: $FILE_PATH é um arquivo de configuração de produção." >&2
    echo "Para editar arquivos de produção, faça manualmente com revisão do time." >&2
    exit 1  # Exit não-zero BLOQUEIA a ação de ferramenta
  fi
done

exit 0  # Permitido

# O que Claude vê quando um PreToolUse bloqueia:
# "Hook de segurança bloqueou a edição. Mensagem: BLOQUEADO: config/production.json
#  é um arquivo de configuração de produção. Para editar arquivos de produção..."
# Claude então tenta outra abordagem ou pede confirmação explícita do usuário

Hook Stop: notificação e resumo ao final da tarefa

#!/bin/bash
# .claude/hooks/Stop/notify-completion.sh
# Notifica quando Claude completa uma tarefa (útil para tarefas longas em background)

INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('session_id', 'unknown'))")
STOP_REASON=$(echo "$INPUT" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('stop_reason', 'end_turn'))")

# Notificação macOS
if [[ "$OSTYPE" == "darwin"* ]]; then
  osascript -e "display notification "Claude Code finalizou a tarefa"     with title "Claude Code"     subtitle "Sessão: $SESSION_ID"     sound name "Glass""
fi

# Notificação Linux (notify-send)
if command -v notify-send &>/dev/null; then
  notify-send "Claude Code" "Tarefa finalizada (sessão $SESSION_ID)"
fi

# Opcional: postar no Slack (se SLACK_WEBHOOK configurado)
if [[ -n "$SLACK_WEBHOOK_URL" && "$STOP_REASON" == "end_turn" ]]; then
  PROJECT=$(basename "$PWD")
  curl -s -X POST "$SLACK_WEBHOOK_URL"     -H "Content-Type: application/json"     -d "{"text": "✅ Claude Code finalizou tarefa em *$PROJECT*"}"
fi

exit 0

Configuração e debugging de hooks

# Habilitar hooks em .claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",          # filtro de evento (regex)
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/PostToolUse/auto-lint.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Edit|Write|Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/PreToolUse/protect-prod-config.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": ".*",                  # roda sempre que Claude para
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/Stop/notify-completion.sh"
          }
        ]
      }
    ]
  }
}

# Debugging: testar um hook manualmente
echo '{"tool_name": "Edit", "tool_input": {"file_path": "/path/arquivo.ts"}}'   | bash .claude/hooks/PostToolUse/auto-lint.sh

# Ver logs de hooks durante sessão:
# Claude Code exibe output do hook no terminal se ele escreve em stderr
# Saída em stdout é silenciosa (não mostrada ao usuário)
# Use stderr para mensagens de debug/status

# Timeout padrão: 60 segundos por hook
# Para hooks lentos (testes), configure timeout maior no settings:
{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "...", "timeout": 120 }]
    }]
  }
}
Hooks vs CLAUDE.md vs instruções inline: use CLAUDE.md para contexto e preferências comportamentais (Claude pode ou não seguir). Use instruções inline na sessão para ajustes pontuais. Use hooks para automação que precisa ser garantida — lint que roda sempre, validações de segurança, notificações ao final. Hooks são determinísticos: não dependem de Claude "lembrar" de fazer algo.
💡
Próximo: Skills e slash commands — como criar workflows customizados que você invoca com /nome-do-comando para automatizar tarefas repetitivas.
🧩

Quiz rápido

3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo