🪝
Hooks: automatizar revisões, validações e ações customizadas
⏱ 14 min de leitura·+70 XP
Pré-requisitos (0/1)0%
- ⬜📋 CLAUDE.md: como dar memória, contexto e personalidade ao agente(Claude & Anthropic na Prática)
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
| Evento | Quando dispara | Pode bloquear? | Caso de uso típico |
|---|---|---|---|
| PreToolUse | Antes de Claude usar uma ferramenta | Sim (exit ≠ 0 bloqueia) | Validar antes de editar, verificar permissões |
| PostToolUse | Depois de Claude usar uma ferramenta | Não | Rodar lint, formatar código, notificar |
| Stop | Quando Claude completa a resposta | Não | Resumir sessão, notificar por Slack, fazer backup |
| Notification | Quando Claude precisa de atenção do usuário | Não | Desktop notification, som, mensagem |
| SubagentStop | Quando um subagente termina | Não | Coletar 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 0Hook 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árioHook 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 0Configuraçã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