Na terça-feira passada, quase enviei uma API key para um repositório público. Um Claude Code hook interceptou meio segundo antes do commit. Foi nesse momento que passei de "hooks são um bom extra" para "não vou rodar um agente sem eles".
Claude Code hooks são o recurso mais subestimado da ferramenta inteira, e quase ninguém na minha timeline os usa. Vou mudar isso. Vou mostrar os hooks exatos que uso trabalhando solo, a config que os alimenta, e o que genuinamente salvou meu repositório.
O que é um hook de verdade
Um hook é um comando shell que Claude Code executa automaticamente quando algo acontece. Antes de uma ferramenta rodar. Depois que um arquivo é editado. Quando o agente termina. Você não está pedindo ao Claude para se lembrar de fazer algo — o harness faz isso, deterministicamente, sempre. Essa distinção importa. Um agente que "normalmente se lembra" de rodar o formatador é um agente que vai esquecer no pior momento possível.
Hooks vivem no seu settings.json. Eis a estrutura:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "prettier --write \"$CLAUDE_FILE_PATHS\"" }
]
}
]
}
}
Esse roda o Prettier em qualquer arquivo que Claude editar, no instante em que o edita. Nunca mais penso em formatação. O diff está sempre limpo. Chega de "ops, 400 alterações de espaço em branco" poluindo meus commits.
O hook que salvou meu repositório
Este é o da API key. Um hook PreToolUse no Bash que escaneia por git commit ou git push e faz grep do diff staged procurando qualquer coisa que pareça um segredo.
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "~/.claude/scripts/secret-scan.sh" }
]
}
]
}
O script faz grep das mudanças staged por padrões como sk-, AKIA, e alguns prefixos dos meus próprios provedores. Se houver correspondência, o script sai com código não-zero, e uma saída não-zero de um hook PreToolUse bloqueia a ação. Claude literalmente não consegue executar o comando. Ele é informado do motivo e contorna o problema.
Na terça passada, acertou. Eu havia deixado um agente refatorar uma config e ele embutiu uma key de um env file que não deveria ter lido. Sem o hook, essa key estaria no GitHub, eu estaria rotacionando credenciais à meia-noite, e talvez alguém estivesse minerando crypto às minhas custas. Com o hook, recebi uma mensagem de bloqueio de uma linha e segui em frente. Doze linhas de bash. O seguro mais barato que tenho.
Meus hooks do dia a dia
Além dos dois acima, eis o que realmente merece seu lugar.
Um hook PostToolUse que roda meu verificador de tipos após edições em arquivos .ts e envia os erros diretamente de volta ao agente. Claude vê seus próprios erros de tipo sem que eu precise colá-los. Ele se autocorrige no mesmo turno. Só esse ciclo de feedback já vale a configuração. O agente corrige o erro antes de eu sequer lê-lo.
Um hook Stop que dispara uma notificação no desktop quando Claude termina uma tarefa longa. Rodo agentes em grandes refatorações e vou fazer o almoço. A notificação significa que não estou babá um terminal, olhando para ele a cada noventa segundos como um tamagotchi.
{
"Stop": [
{ "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude is done\" with title \"CC\"'" }] }
]
}
Minúsculo. Muda completamente como trabalho. Inicio uma tarefa e saio com confiança real de que serei avisado.
O que removi
Tentei um hook que rodava minha suíte de testes completa após cada edição. Parecia responsável. Era uma agonia. A suíte leva 40 segundos, então cada edição pequena travava por 40 segundos, e o agente ficava ali parado queimando minha tarde. Lição: hooks devem ser rápidos ou assíncronos. Um hook síncrono lento transforma seu agente ágil em melaço.
Agora os testes rodam só no hook Stop, uma vez, no final. Verificações rápidas (lint, tipos, scan de segredos) rodam inline. Verificações lentas rodam na fronteira. Ajuste o custo do hook à frequência com que ele é disparado. Se dispara a cada edição, é melhor retornar em menos de um segundo.
Onde colocar
Hooks específicos de projeto vão em .claude/settings.json no repositório para que sejam enviados junto com o código e meus colaboradores também os recebam. Os pessoais, como a notificação do desktop, vão em ~/.claude/settings.json para me seguir em todos os projetos. Mantenha o scanner de segredos global. Você quer ele em todo lugar, sem exceções, sem "vou adicionar depois".
Um gotcha que me custou uma hora: os comandos de hooks rodam em um shell não-interativo. Se seu comando depende de algo do seu .zshrc, não vai estar lá. Aprendi isso quando o prettier funcionava no meu terminal e silenciosamente não fazia nada no hook porque o PATH era diferente. Use caminhos absolutos ou npx.
Comece com um
Não construa tudo isso hoje. Adicione o scanner de segredos. Só esse. São doze linhas e é a diferença entre uma terça tranquila e um exercício de rotação de credenciais.
Depois, quando confiar nele, adicione o formatador. Depois a notificação. Em uma semana você terá um agente que formata limpo, bloqueia segredos, corrige seus próprios erros de tipo, e bate no seu ombro quando termina — e você terá parado de fazer essas quatro coisas manualmente.
Essa é a hora por dia. Nunca foi uma grande tarefa. Eram quarenta pequenas, e agora um script shell as faz enquanto pego meu café.
