PostToolUse — automação pós-ação
TL;DR
PostToolUse executa depois de qualquer tool call completar. Diferente do PreToolUse (que decide se executa), o PostToolUse reage ao resultado. Usos principais: auto-lint depois de edições de arquivo, notificações de ações, logging de resultados, e disparar ações secundárias dependentes. O hook não pode desfazer a tool call — só reagir a ela.
Como funciona
Após cada tool call completar (com sucesso ou falha), o runtime:
- Serializa o input e output da tool como JSON
- Executa o hook command com esse JSON via stdin
- O output do hook é logado (não volta para o agente por padrão)
- A sessão continua independente do exit code do hook
PostToolUse não bloqueia
Diferente do PreToolUse, um exit code não-zero no PostToolUse não bloqueia a continuação da sessão. É um hook de reação, não de controle.
Estrutura do input
O hook recebe via stdin o JSON da tool call completa:
{
"tool_name": "Edit",
"tool_input": {
"file_path": "/projeto/src/services/orders.ts",
"old_string": "...",
"new_string": "..."
},
"tool_output": {
"success": true,
"message": "File edited successfully"
}
}Auto-lint depois de edições
O caso de uso mais comum: rodar o linter automaticamente depois que o agente edita um arquivo TypeScript/JavaScript.
#!/bin/bash
# hooks/auto-lint.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
SUCCESS=$(echo "$INPUT" | jq -r '.tool_output.success // false')
# Só para edições bem-sucedidas de arquivos TS/JS
if [[ "$TOOL" != "Edit" && "$TOOL" != "Write" ]]; then exit 0; fi
if [[ "$SUCCESS" != "true" ]]; then exit 0; fi
if [[ ! "$FILE" =~ \.(ts|tsx|js|jsx)$ ]]; then exit 0; fi
# Rodar linter no arquivo modificado
npx eslint "$FILE" --fix --quiet 2>/dev/null
exit 0Configuração:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/auto-lint.sh" }]
},
{
"matcher": "Write",
"hooks": [{ "type": "command", "command": "~/.claude/hooks/auto-lint.sh" }]
}
]
}
}Auto-format depois de edições
Similar ao lint, rodar Prettier automaticamente:
#!/bin/bash
# hooks/auto-format.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
FORMATTABLE_EXTENSIONS=("ts" "tsx" "js" "jsx" "json" "css" "md")
EXTENSION="${FILE##*.}"
for ext in "${FORMATTABLE_EXTENSIONS[@]}"; do
if [[ "$EXTENSION" == "$ext" ]]; then
npx prettier --write "$FILE" --log-level silent 2>/dev/null
break
fi
done
exit 0Logging de ações do agente
Registrar todas as edições de arquivo para auditoria:
#!/bin/bash
# hooks/log-file-changes.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
SESSION="${CLAUDE_SESSION_ID:-unknown}"
if [[ "$TOOL" =~ ^(Edit|Write|Read)$ && -n "$FILE" ]]; then
echo "$TIMESTAMP | $SESSION | $TOOL | $FILE" >> ~/.claude/file-changes.log
fi
exit 0Notificação de conclusão de tarefa longa
Para tarefas longas (como npm test ou build), notificar quando concluírem:
#!/bin/bash
# hooks/notify-on-bash-complete.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
SUCCESS=$(echo "$INPUT" | jq -r '.tool_output.success // false')
if [[ "$TOOL" != "Bash" ]]; then exit 0; fi
# Notificar para comandos longos
LONG_COMMANDS=("npm test" "npm run build" "npm run lint" "pytest" "cargo build")
for cmd in "${LONG_COMMANDS[@]}"; do
if echo "$COMMAND" | grep -q "^$cmd"; then
STATUS="✅"
[[ "$SUCCESS" != "true" ]] && STATUS="❌"
# notify-send no Linux / osascript no Mac
notify-send "Claude Code" "$STATUS $COMMAND concluído" 2>/dev/null \
|| osascript -e "display notification \"$STATUS $COMMAND concluído\" with title \"Claude Code\"" 2>/dev/null
break
fi
done
exit 0Disparar testes automaticamente
Após editar um arquivo de implementação, rodar os testes relacionados:
#!/bin/bash
# hooks/auto-run-tests.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
if [[ ! "$FILE" =~ src/.+\.(ts|js)$ ]]; then exit 0; fi
# Derivar o arquivo de teste do arquivo de implementação
TEST_FILE=$(echo "$FILE" | sed 's|src/|tests/|' | sed 's|\.\(ts\|js\)$|.test.\1|')
if [[ -f "$TEST_FILE" ]]; then
npx jest "$TEST_FILE" --no-coverage --silent 2>&1 | tail -5
fi
exit 0PostToolUse para Bash — reagir a saída de comandos
#!/bin/bash
# hooks/log-failing-commands.sh
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
SUCCESS=$(echo "$INPUT" | jq -r '.tool_output.success // true')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
OUTPUT=$(echo "$INPUT" | jq -r '.tool_output.output // ""')
if [[ "$TOOL" != "Bash" || "$SUCCESS" == "true" ]]; then exit 0; fi
# Logar comandos que falharam com seu output
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "=== $TIMESTAMP ===" >> ~/.claude/failures.log
echo "COMMAND: $COMMAND" >> ~/.claude/failures.log
echo "OUTPUT: $OUTPUT" >> ~/.claude/failures.log
echo "" >> ~/.claude/failures.log
exit 0Armadilhas
Hook pesado em PostToolUse: o hook executa a cada tool call. Um hook que demora 2 segundos em cada Edit vai tornar a sessão muito mais lenta. Mantenha PostToolUse leve.
Tentar desfazer a ação: PostToolUse não pode desfazer — se o agente editou um arquivo, o arquivo já foi editado. Use PreToolUse para prevenir, PostToolUse para reagir.
Depender do exit code para controle: PostToolUse não bloqueia baseado em exit code. Se você precisa de controle, use PreToolUse.
Veja também
- 01 - Sistema de hooks — lifecycle completo
- 02 - PreToolUse — controle antes de executar
- 04 - Stop hook — ações no encerramento da sessão
- 08 - Testando hooks — debugging de hooks
- Hooks e Guardrails — índice do galho