Disparando um Comando Shell no PostToolUse com Hooks
Um hook PostToolUse é um comando shell que roda automaticamente logo após uma chamada de ferramenta correspondente ser concluída.
O uso mais comum é a formatação automática: no momento em que o Claude edita um arquivo, um formatador é executado nele sem que ninguém peça.
Resumo
Hooks são configurados em settings.json, sob um nome de evento como PostToolUse.
Cada entrada de hook tem um matcher, que o limita a ferramentas específicas como Edit ou Write, e um ou mais comandos hooks para executar quando esse matcher é acionado.
Diferente de um comando de barra, nada sobre um hook passa pelo modelo. O harness executa o comando shell diretamente e não pergunta ao Claude se deveria.
Um hook recebe detalhes sobre a chamada de ferramenta concluída como JSON, tipicamente via stdin, incluindo qual arquivo foi tocado.
Como o hook é determinístico, ele é o lugar certo para qualquer coisa que deva acontecer sempre, sem exceções e sem chamadas de julgamento.
Receita
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATH\""
}
]
}
]
}
}Quando usar isso:
- Formatar automaticamente um arquivo no instante em que ele é editado, para que a formatação nunca precise ser solicitada.
- Executar um linter e exibir avisos logo após uma alteração, em vez de esperar por uma etapa de CI separada.
- Registrar cada arquivo tocado durante uma sessão para uma trilha de auditoria.
- Iniciar um conjunto de testes rápido e restrito para o arquivo que acabou de ser alterado.
- Regenerar um artefato derivado (como uma configuração compilada ou um índice de tipos) após a alteração de sua origem.
Exemplo de Trabalho
#!/usr/bin/env bash
# .claude/hooks/format-on-edit.sh
# Lê o payload do evento PostToolUse via stdin e formata o arquivo tocado.
set -euo pipefail
payload="$(cat)"
file_path="$(echo "$payload" | jq -r '.tool_input.file_path // empty')"
if [ -z "$file_path" ]; then
exit 0
fi
case "$file_path" in
*.ts|*.tsx|*.js|*.jsx|*.json|*.css|*.md)
npx prettier --write "$file_path"
echo "Formatted: $file_path"
;;
*)
exit 0
;;
esac{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/format-on-edit.sh"
}
]
}
]
}
}O que isso demonstra:
- O campo
matcherlimita o hook às ferramentasEditeWrite, para que ele nunca seja acionado em, por exemplo, um comandoBash. - O script lê o payload do evento via stdin e extrai o caminho do arquivo alterado com
jq, em vez de adivinhar uma variável de ambiente. - Uma instrução
caserestringe a formatação a tipos de arquivo que o Prettier realmente entende, para que o hook opere silenciosamente em arquivos que não deveria tocar. - O script é um arquivo separado e versionado, não uma linha única inline, o que o torna testável e legível por si só.
set -euo pipefailfaz com que o script falhe ruidosamente em erros inesperados em vez de fazer nada silenciosamente.
Mergulho Profundo
Como Funciona
- O Claude Code emite um evento
PostToolUseimediatamente após uma chamada de ferramenta ser concluída com sucesso. - O harness verifica o
matcherde cada hookPostToolUseregistrado contra a ferramenta que acabou de rodar; apenas hooks correspondentes são executados. - O
commandde cada hook correspondente roda como um comando shell simples, com o payload do evento (nome da ferramenta, entrada da ferramenta e resultado) disponível para ele, tipicamente via stdin como JSON. - O código de saída e a saída do hook são reportados de volta ao Claude Code; uma saída não zero é tipicamente apresentada como uma falha, útil para hooks que também validam.
- Hooks rodam de forma síncrona com a conclusão da chamada da ferramenta, mas não bloqueiam ou alteram a chamada da ferramenta em si, já que a ferramenta já foi concluída quando o PostToolUse é acionado. Esse comportamento de bloqueio pertence ao PreToolUse.
Padrões de Matcher
| Matcher | Corresponde |
|---|---|
Edit | Apenas a ferramenta Edit. |
Write | Apenas a ferramenta Write. |
Edit|Write | Edit ou Write (alternância de regex). |
* ou omitido | Toda chamada de ferramenta, independentemente de qual ferramenta rodou. |
Notas do Script Shell
# Prefira ler entrada estruturada em vez de depender de variáveis de ambiente
# que podem ou não ser definidas consistentemente entre implementações de hook.
payload="$(cat)"
file_path="$(echo "$payload" | jq -r '.tool_input.file_path // empty')"
# Proteja contra um caminho vazio antes de fazer qualquer trabalho.
[ -z "$file_path" ] && exit 0- Sempre proteja contra um caminho de arquivo ausente ou vazio; nem todo payload de chamada de ferramenta terá a estrutura que um hook de formatação espera.
- Mantenha o hook rápido. Ele roda em cada chamada de ferramenta correspondente, então um hook lento (um conjunto completo de testes, uma compilação grande) tornará cada edição visivelmente lenta.
- Prefira um arquivo de script dedicado em vez de um comando inline longo em
settings.json, pois é mais fácil de testar, comparar e raciocinar isoladamente.
Armadilhas
- Assumir que o hook pode bloquear ou desfazer a edição - PostToolUse é acionado após a chamada da ferramenta já ter sido concluída, então o hook não pode impedir que a edição aconteça. Correção: use um hook PreToolUse em vez disso se o objetivo for rejeitar ou bloquear uma ação antes que ela ocorra.
- Um hook lento faz cada edição parecer lenta - um hook que executa um conjunto completo de testes ou uma compilação lenta em cada edição de arquivo adiciona essa latência a cada edição, não apenas às significativas. Correção: escopo o hook para operações rápidas, ou restrinja o matcher e a verificação de tipo de arquivo para que ele só rode onde importa.
- Codificar um caminho de arquivo em vez de lê-lo do payload do evento - um hook escrito para um arquivo específico quebra no momento em que precisa lidar com um segundo arquivo. Correção: sempre leia o caminho real do arquivo a partir do payload da chamada da ferramenta, nunca assuma um caminho fixo.
- Esquecer o matcher e disparar em toda chamada de ferramenta - um hook sem escopo (sem matcher, ou
*) dispara mesmo em ferramentas não relacionadas comoBashouRead, desperdiçando ciclos e poluindo a saída. Correção: defina um matcher que nomeie exatamente as ferramentas que o hook deseja, comoEdit|Write. - Um hook que falha em arquivos que não entende - um formatador invocado contra um arquivo binário ou uma extensão não suportada pode gerar um erro e poluir a sessão com ruído. Correção: adicione uma guarda explícita de extensão de arquivo (como a instrução
caseacima) para que o hook saia silenciosamente em arquivos que não deveria tocar. - Tratar a saída do hook como invisível - a saída padrão e o código de saída de um hook são apresentados de volta à sessão, então um hook falante ou falho adiciona ruído (ou erros aparentes) a cada edição. Correção: mantenha a saída do hook mínima e use apenas um código de saída não zero em falhas genuínas que o usuário deva ver.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Pedir ao Claude para formatar o arquivo no prompt | Uma solicitação única, não algo que deva acontecer em cada edição daqui para frente. | O comportamento deve ser incondicional e automático para toda a equipe, não dependente de lembrar de pedir. |
| Um hook PreToolUse | O objetivo é validar ou bloquear a edição antes que ela aconteça, não reagir após sua conclusão. | A ação é um efeito colateral que deve rodar após uma edição bem-sucedida, como formatação ou logging. |
| Uma etapa de pipeline de CI | A verificação é cara (conjunto completo de testes, compilação completa) e não precisa rodar em cada edição local. | Feedback rápido em cada edição é mais importante do que centralizar a verificação em CI. |
FAQs
Um hook PostToolUse pode parar a edição que ele está reagindo?
Não. PostToolUse é acionado após a chamada da ferramenta já ter sido concluída, então a edição já aconteceu quando o hook roda. O bloqueio pertence a um hook PreToolUse em vez disso.
Como um hook PostToolUse sabe qual arquivo foi editado?
O payload do evento inclui a entrada da chamada da ferramenta, tipicamente entregue como JSON via stdin, que contém o caminho do arquivo para chamadas de ferramenta Edit e Write.
Contra o que o campo matcher realmente corresponde?
- Ele corresponde ao nome da ferramenta que acabou de rodar, como
EditouWrite. - A alternância no estilo Regex (
Edit|Write) permite que uma entrada de hook cubra múltiplas ferramentas. - Omitir o matcher, ou usar
*, faz o hook disparar em toda chamada de ferramenta.
Múltiplos hooks PostToolUse podem rodar para o mesmo evento?
Sim. Múltiplas entradas de hook podem ser registradas sob PostToolUse, e cada entrada cujo matcher corresponde à ferramenta concluída é executada.
O que acontece se o script do hook sair com um status não zero?
A saída não zero é tipicamente apresentada de volta à sessão como um sinal de falha, o que é útil para hooks que realizam validação leve além de um efeito colateral como formatação.
Um hook de formatação deve rodar contra todo tipo de arquivo?
Não. Ele deve verificar a extensão (ou caminho) do arquivo e apenas rodar o formatador contra tipos que ele realmente entende, saindo silenciosamente em qualquer outra coisa.
Um hook PostToolUse é o lugar certo para operações lentas como um conjunto completo de testes?
Geralmente não, já que o hook roda em cada edição correspondente e um hook lento adiciona essa latência a cada edição. Verificações lentas são geralmente melhores deixadas para CI ou um comando acionado manualmente.
Onde os hooks PostToolUse são configurados?
Em settings.json, sob um array hooks.PostToolUse, onde cada entrada tem um matcher e um ou mais comandos hooks para executar.
O hook roda dentro do mesmo processo que o Claude Code?
Não. O command do hook roda como um comando shell independente, separado do processo de raciocínio do modelo, e apenas sua saída e código de saída são reportados de volta.
Um hook PostToolUse pode ver o resultado da chamada da ferramenta, não apenas sua entrada?
Sim. O payload do evento geralmente inclui tanto a entrada quanto o resultado da ferramenta, o que é útil para hooks que querem reagir de forma diferente dependendo do que a edição realmente produziu.
É seguro commitar um script de hook PostToolUse em um repositório compartilhado?
Sim, e é recomendado, já que um script de hook com escopo de projeto em controle de versão significa que cada colega de equipe obtém o mesmo comportamento automático de formatação ou logging sem configuração individual.
Qual é a diferença entre um hook PostToolUse e um hook Stop?
Um hook PostToolUse é acionado após cada chamada de ferramenta correspondente individual ser concluída, enquanto um hook Stop é acionado uma vez, quando a sessão ou turno termina, tornando-o mais adequado para resumos de fim de turno ou limpeza em vez de reações por edição.
Relacionado
- Comandos Personalizados, Hooks e Subagentes: Para que Serve Cada Um - como PostToolUse se encaixa entre os três pontos de extensão.
- Bloqueando Edições de Arquivo de Risco com um Hook PreToolUse - o hook que roda antes da edição em vez de depois.
- Eventos de Hook Comuns e Quando Usar Cada Um - o catálogo completo de eventos de ciclo de vida aos quais os hooks podem se anexar.
- Melhores Práticas para Comandos Personalizados, Hooks e Subagentes - convenções para escopar hooks com segurança.
Versões da Stack: Escrito contra a linha de modelos Claude atual em ~junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão), e Claude Haiku 4.5. Nomes de modelos, preços e recursos de produtos mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs antes de confiar neles.