Você já sabe escrever um bom contrato para o seu agente: você diz a ele no AGENTS.md como quer que ele trabalhe. O problema é que um LLM lê esse contrato e decide de novo a cada turno — então a regra crítica é cumprida quase sempre, e esse \"quase\" é justamente o que te morde. Os hooks são o degrau seguinte: você tira a regra da cabeça do modelo e a coloca na infraestrutura, para que aconteça 100% das vezes sem depender do critério dele. Vou te explicar os eventos do ciclo de vida onde você engancha, a anatomia do JSON, onde vive cada hook e por que importa, e te deixo três hooks reais prontos para colar (autoformatação, bloqueio de comandos perigosos, notificação de desktop) tirados da documentação oficial da Anthropic. Um único prompt mestre para montar o seu primeiro com a segurança posta — porque um hook roda shell com as suas permissões. É o salto de \"escreva o contrato\" para \"faça o contrato se executar sozinho\".
Chega um ponto em todo projeto com IA em que você para de confiar na boa vontade do modelo. Você escreveu no seu AGENTS.md que ele formate o código, que não toque nos arquivos sensíveis, que rode os testes. E 90% das vezes ele faz. Mas os 10% restantes são justamente os que te mordem: a vez que ele editou o .env de produção, a vez que pulou a formatação e o commit ficou sujo, a vez que executou um rm -rf na pasta errada porque "parecia temporária".
O momento dos hooks é quando você pensa: "e se isso não dependesse de o modelo decidir fazer certo? E se simplesmente acontecesse, sempre, sem depender do critério dele?". É exatamente essa a promessa que a documentação oficial do Claude Code coloca no centro da página de hooks: eles dão "controle determinístico" sobre o comportamento do agente, "garantindo que certas ações sempre aconteçam em vez de depender de o LLM escolher executá-las" (tradução fiel do original em inglês). Essa frase é a tese inteira do recurso.
A dor não é um apocalipse, é uma erosão. Você pediu à IA que fizesse algo "sempre" e às vezes ela não faz, porque um LLM não é determinístico: a cada turno ele decide de novo, e num turno carregado de contexto, o seu lembrete de formatar pode ficar soterrado sob mil tokens de outra coisa. Não é que a IA seja desobediente — é que você está pedindo consistência a um sistema probabilístico. É como pedir a alguém brilhante mas distraído que nunca esqueça de fechar o registro do gás: na maioria das noites ela fecha, e é justamente por isso que você baixa a guarda.
.env, um package-lock.json, algo dentro de .git/.E aqui está o ângulo que torna este recurso avançado: se você já leu sobre escrever um bom contrato para o seu agente (o AGENTS.md), os hooks são o próximo degrau. Você passa de "escreva o contrato" (declarativo: você diz o que quer) para "faça o contrato se executar sozinho" (determinístico: a máquina o cumpre por você). É a diferença entre uma lei escrita num papel e um guarda parado na esquina.
Um hook é um comando de shell que o Claude Code executa automaticamente num ponto específico do seu ciclo de vida. Você o declara uma vez num arquivo settings.json; a partir daí, cada vez que esse ponto do ciclo acontecer, o seu comando roda. Quem o dispara não é o modelo com o critério dele: é o evento. Aí está a mágica — é determinístico, não opinável.
A documentação oficial lista muitos eventos do ciclo de vida — há um para quase qualquer momento do trabalho do agente. Não decore o número nem a lista completa: aprenda a ideia e fique com os que você de fato vai usar 95% do tempo. Estes são, com os nomes reais tal como aparecem na doc:
rm -rf ou uma edição em um arquivo protegido.SessionStart com matcher compact) — em torno da compactação do contexto, para não perder o importante quando a memória é comprimida.Pre. Se quer reagir a algo que já aconteceu (formatar, avisar, registrar), engancha no Post.Todo hook vive dentro de um bloco "hooks" no seu settings.json. A estrutura é sempre a mesma boneca russa: o nome do evento → um matcher (a que se aplica) → uma lista de hooks com type: "command" e o command a rodar. Olhe uma vez e nunca mais esquece:
{
"hooks": {
"PostToolUse": [ // 1. o EVENTO do ciclo de vida
{
"matcher": "Edit|Write", // 2. A QUAIS ferramentas se aplica
"hooks": [
{
"type": "command", // 3. e um comando de shell
"command": "...seu comando..." // 4. O QUE rodar quando acontecer
}
]
}
]
}
}PreToolUse, PostToolUse) ele filtra por nome de ferramenta: "Edit|Write" significa "só quando usar Edit ou Write", "Bash" só comandos de shell. Um matcher vazio ("") dispara sempre, para tudo. A barra | separa alternativas (em versões recentes do Claude Code a vírgula também vale: "Edit, Write" é equivalente).O lugar onde você coloca o hook define o alcance dele. Isto é chave: um hook de projeto é compartilhado com o seu time (é commitado no repo); um global é só seu. A tabela oficial resume onde cada coisa vai:
.claude/settings.json e é commitada. Assim o contrato deixa de viver na cabeça de uma pessoa e passa a viajar com o código. Esse é o poder real: a disciplina vira parte do repositório, não um post-it que alguém tem que lembrar.Chega de teoria. Aqui estão três hooks reais, tirados da documentação oficial, prontos para colar. Comece pelo que mais te dói hoje.
O clássico. Cada vez que o Claude edita ou escreve um arquivo, o Prettier passa automaticamente. Nunca mais um commit com formatação inconsistente. Vai no .claude/settings.json do seu projeto:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}jq -r '.tool_input.file_path' extrai o caminho do arquivo editado desse JSON, e xargs npx prettier --write o passa para o Prettier. jq é um leitor de JSON de linha de comando — instale-o com brew install jq no macOS ou apt-get install jq no Debian/Ubuntu.Este é o que te tira o medo. Um hook PreToolUse que revisa cada comando de Bash antes de executá-lo e o veta se contiver algo destrutivo. Aqui sim importa um detalhe técnico que a doc deixa clarísimo: bloqueia-se saindo com código 2, e o que você escrever no stderr chega ao Claude como explicação para que ele se corrija. Primeiro o script:
#!/bin/bash # .claude/hooks/block-rm-rf.sh INPUT=$(cat) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command') if echo "$COMMAND" | grep -q "rm -rf"; then echo "Bloqueado: 'rm -rf' nao e permitido. Apague caminhos concretos, nao recursivo-forcado." >&2 exit 2 # exit 2 = bloqueia a acao; stderr chega ao Claude como feedback fi exit 0 # exit 0 = sem objecao; segue o fluxo normal de permissoes
Torne-o executável e registre-o nos seus settings apontando para o script:
chmod +x .claude/hooks/block-rm-rf.sh
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
}
]
}
]
}
}grep -q "rm -rf" pega o caso óbvio, mas escapa de um rm -fr, de um rm -rf com espaços estranhos, ou de um rm --recursive --force. Serve como rede visível e como feedback educativo para a IA, mas não o confunda com uma blindagem. A própria doc diz sem rodeios: a filtragem dos hooks é best-effort e "falha aberta" (se não consegue parsear o comando, deixa passar), então para uma proibição DURA que ninguém possa pular, use o sistema de permissões (permissions.deny), não um hook. O padrão correto é: hook para reagir e avisar; permission rules para o cadeado real.PreToolUse que retorna uma decisão de bloqueio veta a ferramenta MESMO em `bypassPermissions` ou com `--dangerously-skip-permissions`. Ou seja: mesmo que você (ou a IA) tenha baixado todas as barreiras de permissões, o seu hook de bloqueio continua de pé. A regra é assimétrica e foi pensada assim de propósito: os hooks podem endurecer a política, nunca afrouxá-la além do que as regras de permissões permitem. Um hook de bloqueio é uma regra que nem você consegue pular por acidente.Para parar de vigiar o terminal. Quando o Claude termina ou precisa da sua permissão, salta uma notificação de desktop e você vai fazer outra coisa. Vai no seu ~/.claude/settings.json global (exemplo de macOS com osascript):
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code precisa da sua atencao\" with title \"Claude Code\"'"
}
]
}
]
}
}notify-send 'Claude Code' 'Claude Code precisa da sua atencao'; no Windows (PowerShell) um MessageBox de System.Windows.Forms. A estrutura do JSON é idêntica — só muda a linha do command. Truque de macOS que a própria guia documenta: se a notificação não aparecer, rode uma vez osascript -e 'display notification "test"' e depois dê permissão ao Script Editor em Ajustes do Sistema → Notificações.Os hooks não são para tudo. A regra mental é simples: se algo deve acontecer sempre, de forma mecânica e sem critério, é um hook. Se algo requer julgamento caso a caso, não o force com um hook determinístico — deixe o modelo decidir (ou use os hooks do tipo prompt/agente que a doc também suporta, mas isso é outra liga).
.env, apagar recursivo, escrever em .git/): isso é um PreToolUse de bloqueio — respaldado por uma regra de permissões para o cadeado duro.PostToolUse.PostToolUse: ele não pode desfazer o que já aconteceu — só reage. Para impedir, sempre PreToolUse. Colocar lógica complexa demais num hook o torna frágil; mantenha-os curtos e de um único propósito.Aqui o detalhe prático: a própria doc recomenda pedir ao Claude que escreva o hook descrevendo-o para ele. Mas para que saia bem —e seja seguro— convém dar um encargo estruturado, não um "me põe um hook". Este é O prompt mestre do recurso: um só, potente, que projeta o seu primeiro hook com os freios de segurança postos.
Quero montar meu primeiro hook de Claude Code para automatizar uma regra do meu fluxo. A regra que quero garantir SEMPRE é: [DESCREVA A REGRA, ex: "formatar com prettier cada arquivo que voce editar" / "bloquear qualquer comando que apague arquivos de forma recursiva" / "impedir que se edite o .env ou qualquer coisa dentro de .git/" / "me avisar por notificacao quando terminar"]. Antes de escrever nada, me ajude a projeta-lo bem respondendo isto em linguagem clara (eu nao programo): 1. O EVENTO correto do ciclo de vida para esta regra. Se a regra e IMPEDIR algo, deve ser PreToolUse (acontece antes e pode bloquear). Se e REAGIR a algo ja feito (formatar, avisar, registrar), deve ser PostToolUse ou Notification. Diga qual voce escolhe e por que. 2. O MATCHER exato (a quais ferramentas se aplica: Edit|Write, Bash, ou vazio para todas) e por que esse e nao outro. Mantenha-o o mais ESTREITO possivel: um matcher amplo demais dispara o hook onde nao deve. 3. O LUGAR onde deve viver o settings.json: - Se a regra deve ser cumprida por todo o time neste repo → .claude/settings.json (e commitado). - Se e so para minha maquina e todos os meus projetos → ~/.claude/settings.json. - Se e local e privada → .claude/settings.local.json. Diga qual e por que. 4. O BLOCO JSON completo, pronto para colar, com o hook ja montado. Se precisar de um script a parte (tipico em bloqueios com PreToolUse), me de tambem o script .sh completo e me lembre de torna-lo executavel com chmod +x. 5. SE FOR UM HOOK DE BLOQUEIO: use-o com exit 2 e uma mensagem clara no stderr que me explique por que foi bloqueado, para que voce mesmo (Claude) receba o motivo e se corrija. Nao o faca silencioso. E ME AVISE se esta regra merece tambem uma regra de permissoes (permissions.deny) como cadeado duro, porque um grep num hook e best-effort e pode ser pulado. 6. SEGURANCA, obrigatorio: me explique em uma frase QUAL comando de shell este hook vai rodar e me confirme que ele nao faz nada destrutivo nem envia dados para nenhum lugar. Me lembre que um hook roda shell com minhas permissoes e que so devo salvar comandos que eu entendo. 7. COMO EU TESTO: diga como verificar que o hook esta registrado (o comando /hooks dentro do Claude Code) e uma forma de testar que ele dispara de verdade, sem quebrar nada real. Primeiro me mostre SO o design (evento, matcher, lugar), depois o JSON e o script se for preciso, e no fim a explicacao de seguranca e o teste. Nao modifique meu settings.json ainda: quero revisar o bloco antes de cola-lo eu mesmo.
Repare no padrão: primeiro você projeta, revisa a segurança, e você cola o bloco. Você nunca deixa um hook aparecer na sua configuração sem tê-lo entendido. É a mesma disciplina de dois passos que protege qualquer automação potente: a máquina propõe, você aprova.
Para não se enrolar, isto é o que você pode delegar à IA pelo chat e o que continua sendo tarefa sua:
.sh para os hooks de bloqueio, com o seu jq, o seu exit 2 e a sua mensagem no stderr.settings.json ele deve viver.chmod +x do script, brew install jq se estiver faltando.settings.json — depois de lê-lo e entendê-lo. A rede de segurança não pode ser código que você não revisou.PreToolUse para impedir, PostToolUse/Notification para reagir.PreToolUse que veta o que não deve entrar. É a mesma filosofia de um hook, aplicada a um time inteiro em vez de a um terminal: que a qualidade não seja uma promessa que alguém lembra, mas um sensor na esteira que não dá para pular. Você põe a regra uma vez; o sistema a cumpre para sempre.Join 4,200+ builders. No credit card. Build your first app with AI in minutes.