Análise completa do mecanismo Claude Code Hooks: cinco tipos de eventos, controle de códigos de saída e quatro configurações reais para auditar, interceptar e automatizar ações de IA.
Cada vez que o Claude Code lê um arquivo, escreve código ou executa um comando, há um sistema de eventos rodando em segundo plano. Os Hooks são a interface para se conectar a esse sistema — você pode injetar sua própria lógica em qualquer momento: executar linters automaticamente, registrar operações, interceptar ações perigosas ou disparar qualquer comando de shell.
Este artigo cobre completamente o mecanismo de Hooks, a configuração e o uso prático.
Hooks são comandos de shell configurados no settings.json que o Claude Code executa automaticamente quando eventos específicos ocorrem.
A analogia mais direta: git hooks. O git pode disparar scripts antes e depois de operações como commit e push. Os Claude Code Hooks funcionam exatamente da mesma forma — a diferença é que os pontos de disparo são as chamadas de ferramentas da IA.
Por que isso importa?
Quanto mais capaz o Claude Code se torna, mais você precisa de uma camada de controle determinista. Os Hooks fornecem:
- Garantias de execução que não dependem do prompt (o Claude pode ignorar instruções, mas os hooks sempre rodam)
- Registros de operações auditáveis
- Verificações de qualidade automatizadas
| Tipo | Gatilho | Uso típico |
|---|---|---|
PreToolUse |
Antes de uma chamada de ferramenta | Bloquear operações perigosas, registrar intenção |
PostToolUse |
Depois de uma chamada de ferramenta | Auto-lint, rodar testes |
PreCompact |
Antes da compactação do contexto | Salvar snapshot do estado atual |
Notification |
Quando o Claude envia uma notificação | Alertas de desktop, mensagens no Slack |
Stop |
Quando o Claude termina de responder | Resumir logs, disparar fluxos posteriores |
Os mais usados são PreToolUse e PostToolUse, para interceptar e pós-processar chamadas de ferramentas.
Os Hooks vão em ~/.claude/settings.json (global) ou em .claude/settings.json na raiz do projeto (nível projeto):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npm run lint --silent"
}
]
}
]
}
}
Três campos principais:
matcher: Regex que corresponde a nomes de ferramentas, determina quais chamadas disparam este hook. "Write|Edit" dispara ao escrever ou editar arquivos. Deixe vazio ou use ".*" para corresponder a todas as ferramentas.type: Por enquanto apenas "command".command: Qualquer comando de shell.Nomes de ferramentas necessários para escrever matchers:
| Nome | Ação |
|---|---|
Write |
Escrever um novo arquivo |
Edit |
Editar um arquivo |
Bash |
Executar um comando de shell |
Read |
Ler um arquivo |
Glob |
Busca de arquivos |
Grep |
Busca de conteúdo |
TodoWrite |
Atualizar lista de tarefas |
Durante a execução, os dados são passados como JSON pelo stdin:
{
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.rb",
"content": "..."
},
"tool_response": "..."
}
PreToolUse recebe tool_input (os argumentos antes da chamada). PostToolUse também recebe tool_response (o valor de retorno da ferramenta).
Exemplo de hook com lógica condicional:
#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')
# Executar rubocop apenas em arquivos .rb
if [[ "$file" == *.rb ]]; then
rubocop "$file" --autocorrect-all --no-color
fi
O código de saída do hook controla o comportamento posterior do Claude Code:
| Código de saída | Significado |
|---|---|
0 |
Sucesso, continuar execução |
2 |
Bloquear: cancelar a chamada de ferramenta atual, enviar stderr de volta ao Claude |
| Outros não-zero | Registrar o erro, mas continuar |
O código de saída 2 é o mais poderoso — permite escrever lógica de interceptação no PreToolUse para impedir o Claude de realizar uma operação e explicar o motivo.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
}
]
}
]
}
}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); if echo \"$cmd\" | grep -qE \"rm.*/(migrations|seeds)\"; then echo \"Não é permitido excluir os diretórios migrations ou seeds\" >&2; exit 2; fi'"
}
]
}
]
}
}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|Bash",
"hooks": [
{
"type": "command",
"command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') $CLAUDE_TOOL_NAME: $(echo $CLAUDE_TOOL_INPUT | jq -c .)\" >> ~/.claude/audit.log"
}
]
}
]
}
}
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude concluiu\" with title \"Claude Code\"'"
}
]
}
]
}
}
Os Hooks podem ficar em dois lugares:
Global (~/.claude/settings.json): Regras comuns a todos os projetos — logs, notificações.
Nível projeto (.claude/settings.json): Verificações específicas do projeto. Os hooks de projeto e os globais se combinam e ambos são executados.
Em projetos de equipe, faça commit do settings.json do projeto no git para que todos executem os mesmos hooks.
Passos para resolver problemas quando um hook não está funcionando:
write não é Writeecho "hook triggered" >> /tmp/hook.log para confirmar o disparoO valor central dos Hooks é conectar o comportamento imprevisível da IA com os padrões de engenharia deterministas. O Claude pode esquecer de rodar os testes, mas um PostToolUse hook não. O Claude pode excluir arquivos por acidente, mas um interceptor PreToolUse não vai deixar passar.
Comece com um hook: auto-lint ao escrever arquivos. Quando estiver funcionando, adicione mais.