Hooks
Hooks são comandos shell, endpoints HTTP ou prompts LLM que executam automaticamente em pontos específicos do ciclo de vida do Claude Code. Diferente do CLAUDE.md, que é instrução textual e portanto sujeita à interpretação do modelo, hooks são determinísticos: se você configurou, vai rodar.
Use hook quando precisar de garantia. "Sempre formate código depois de editar" no CLAUDE.md é uma sugestão. Um PostToolUse que roda prettier é uma certeza.
Onde Configurar
Hooks vivem em arquivos de settings JSON, na mesma hierarquia das outras configurações:
| Local | Escopo |
|---|---|
~/.claude/settings.json | Todos os projetos |
.claude/settings.json | Projeto, versionado |
.claude/settings.local.json | Projeto, gitignored |
Plugin hooks/hooks.json | Quando o plugin está habilitado |
| Skill/Agent frontmatter | Enquanto o componente está ativo |
| Managed policy settings | Toda a organização |
Estrutura de um Hook
Três níveis de aninhamento:
{
"hooks": {
"EventName": [
{
"matcher": "ToolName|OutroTool",
"hooks": [
{
"type": "command",
"command": "/caminho/pro/script.sh"
}
]
}
]
}
}
Tipos de Handler
| Tipo | O que faz |
|---|---|
command | Executa script shell, recebe JSON via stdin |
http | Manda POST com payload JSON |
prompt | Avaliação LLM single-turn (ex: "isso é seguro?") |
agent | Spawna subagente pra verificar (experimental) |
Eventos Principais
Os hooks disparam em diferentes cadências:
Por sessão
SessionStart- quando a sessão começa ou retomaSessionEnd- quando termina
Por turno
UserPromptSubmit- antes do Claude processar seu promptStop- quando o Claude termina de responderStopFailure- quando termina com falha
Por tool call
PreToolUse- antes de executar qualquer toolPostToolUse- depois de executar com sucessoPostToolUseFailure- depois de executar com falhaPermissionRequest- quando aparece dialog de permissãoPermissionDenied- quando o classificador do auto mode bloqueia
Assíncronos
FileChanged- quando um arquivo monitorado mudaCwdChanged- quando o working directory mudaInstructionsLoaded- quando CLAUDE.md ou rules carregamPreCompact/PostCompact- antes e depois de compactaçãoSubagentStart/SubagentStop- lifecycle de subagentesNotification- notificações (idle, permission prompt)
Exemplos Práticos
Bloquear comandos destrutivos
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm.sh"
}
]
}
]
}
}
E o script:
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "rm -rf bloqueado"
}
}'
else
exit 0
fi
Lint automático após editar
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npm run lint --silent || true"
}
]
}
]
}
}
Carregar contexto do projeto no início
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/load-context.sh"
}
]
}
]
}
}
#!/bin/bash
CONTEXT="Issues abertas: $(gh issue list --json title | jq -r '.[].title' | head -3)"
jq -n --arg ctx "$CONTEXT" '{
hookSpecificOutput: {
hookEventName: "SessionStart",
additionalContext: $ctx
}
}'
Filtrar output de teste para economizar tokens
Esse aqui é um dos mais valiosos. Em vez do Claude receber 10.000 linhas de output de teste, você devolve só as falhas:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/filter-test-output.sh"
}
]
}
]
}
}
#!/bin/bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command')
if [[ "$cmd" =~ ^(npm test|pytest|go test) ]]; then
filtered_cmd="$cmd 2>&1 | grep -A 5 -E '(FAIL|ERROR|error:)' | head -100"
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"updatedInput\":{\"command\":\"$filtered_cmd\"}}}"
else
echo "{}"
fi
Auto-aprovar comandos seguros
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git status|git log|git diff)",
"command": "exit 0"
}
]
}
]
}
}
Decision Control
Hooks podem influenciar o que acontece a seguir através do JSON que retornam. As principais decisões pra PreToolUse:
| Decisão | Efeito |
|---|---|
allow | Aprova a tool sem perguntar |
deny | Bloqueia |
ask | Força dialog de permissão |
defer | Deixa em aberto (pra UIs externas) |
updatedInput | Modifica o input antes de executar |
Precedência: deny > defer > ask > allow.
Exit Codes (para command hooks)
| Código | Significado |
|---|---|
0 | Sucesso, parseia stdout como JSON |
2 | Erro bloqueante, ignora JSON, usa stderr |
| Outro | Erro não-bloqueante, loga stderr e continua |
Variáveis de Ambiente
Disponíveis nos scripts:
| Variável | O que é |
|---|---|
$CLAUDE_PROJECT_DIR | Raiz do projeto |
${CLAUDE_PLUGIN_ROOT} | Diretório do plugin (se aplicável) |
${CLAUDE_PLUGIN_DATA} | Diretório de dados persistentes do plugin |
Matchers
Controlam quando o hook dispara:
| Pattern | Comportamento |
|---|---|
"*", "", omitido | Match em tudo |
Bash ou Edit|Write | Exact match ou lista |
^Notebook ou mcp__.* | Regex |
Para tools MCP o padrão é mcp__<servidor>__<tool>. Exemplo: mcp__memory__.* mata todas as tools do servidor memory.
O Comando /hooks
Digite /hooks no Claude Code pra ver um browser read-only com:
- Todos os eventos configurados
- Quantos hooks por evento
- Detalhes de matchers
- Configuração completa de cada handler
- Source (
User,Project,Local,Plugin,Session,Built-in)
Útil pra debug quando você não tem certeza do que está rodando.
O Claude Pode Escrever Hooks Pra Você
Tente prompts como:
- "Escreva um hook que roda eslint depois de cada edit de arquivo"
- "Escreva um hook que bloqueia escritas na pasta migrations"
- "Escreva um hook que filtra output do pytest pra mostrar só as falhas"
Ele edita seu settings.json e cria os scripts. Você revisa, testa, commita.
Best Practices
Hooks de produção precisam ser rápidos
Cada hook adiciona latência. Hooks pesados (que rodam test suites inteiras, por exemplo) podem ir pro SessionStart (uma vez por sessão) em vez de PostToolUse (a cada edit).
Use PreToolUse pra modificar input
Reescrever o comando do Bash pra filtrar output, redirecionar pra log, ou adicionar timeouts é uma forma poderosa de economizar tokens e proteger o sistema sem precisar bloquear nada.
Versione os hooks que importam pro time
.claude/settings.json e os scripts em .claude/hooks/ no git. Cada dev clona, e o setup já está aplicado.
.claude/settings.local.json pra preferências pessoais
Configurações que são suas (allowlists pessoais, hooks de produtividade individual) ficam aí. Não vão pro git.
Hooks gerenciados pra compliance
Em organização, settings managed forçam hooks que usuários não conseguem desabilitar. Útil pra garantir audit logs, bloqueios de segurança, validação obrigatória.
Conclusão
Hook é a forma de transformar uma instrução ("sempre faça X") em garantia ("X sempre acontece"). Use pra:
- Validar antes de comandos perigosos
- Filtrar output verboso e economizar tokens
- Carregar contexto dinâmico no começo da sessão
- Forçar formatação, lint, ou testes em momentos certos
- Audit trail de comandos sensíveis
Combinado com skills e subagents, hooks fecham o ciclo de automação determinística.
Próximo: MCP e Integrações
Referências: