Claude Code hooks: automatize tarefas no seu projeto .NET
Hooks do Claude Code disparam scripts quando o agente edita ou executa algo. Configure dotnet format, dotnet test e bloqueios de segurança no seu projeto .NET.

Você usa o Claude Code pra editar um endpoint, e aí precisa formatar o código, verificar se o teste ainda passa e anotar que arquivo mudou. Toda vez. Manualmente. É trabalho que o agente poderia disparar sozinho enquanto você já está lendo o próximo problema.
Hooks fazem exatamente isso.
O que são hooks
Hooks são scripts que o Claude Code executa automaticamente em resposta a eventos do próprio agente. Quando ele edita um arquivo, roda um comando Bash, ou termina uma sessão, um hook pode disparar antes, depois, ou bloquear a ação completamente.
Diferente do CLAUDE.md, que é instrução em linguagem natural, hooks são automação real: código que roda no seu ambiente, com acesso ao sistema de arquivos, ao SDK e aos seus comandos de CI.
A documentação oficial de hooks lista todos os eventos disponíveis. Para o dia a dia de um dev .NET, os dois mais úteis são PreToolUse (antes de agir, pode bloquear) e PostToolUse (depois de agir, para reagir à mudança).
Onde configurar
A configuração fica em .claude/settings.json na raiz do projeto (o mesmo arquivo onde você define permissões). Você pode commitar esse arquivo com o repositório; o time inteiro recebe os hooks automaticamente.
A estrutura básica:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/meu-script.sh"
}
]
}
]
}
}
Três coisas pra entender aqui. matcher filtra para quais ferramentas o hook roda. "Edit|Write" significa "quando o Claude Code usar Edit ou Write". O campo command aponta para o script. E ${CLAUDE_PROJECT_DIR} é uma variável que o Claude Code expande pra raiz do projeto, então o path é portável mesmo que o repo esteja em lugares diferentes em máquinas diferentes.
O hook recebe o contexto da ferramenta via stdin como JSON:
{
"hook_event_name": "PostToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "/caminho/para/Pedido.cs",
"old_string": "...",
"new_string": "..."
}
}
Com jq você lê qualquer campo desse JSON no script.
Aprenda Claude Code do jeito certo
Curso completo para devs C# .NET: hooks, CLAUDE.md, sub-agentes e workflows reais.
Ver cursosQuatro hooks práticos para projetos .NET
1. Formatar código automaticamente após edições
Todo projeto .NET deveria ter .editorconfig com as regras do time. Mas aplicar o formato manualmente depois que o Claude Code edita um arquivo é chato. Um hook PostToolUse faz isso por você:
#!/bin/bash
# .claude/hooks/format-after-edit.sh
FILE=$(jq -r '.tool_input.file_path // empty' < /dev/stdin)
if [[ "$FILE" == *.cs ]]; then
dotnet format --include "$FILE" --verbosity quiet 2>/dev/null || true
fi
No settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/format-after-edit.sh"
}
]
}
]
}
}
O || true no final garante que uma falha no dotnet format não quebre o hook e trave o agente. Importante: dê permissão de execução ao script antes de testar (chmod +x .claude/hooks/format-after-edit.sh).
2. Rodar testes quando um arquivo de teste muda
Quando o Claude Code edita um *Tests.cs, faz sentido checar se os testes ainda passam antes de continuar. Mas você não quer rodar a suite inteira a cada edição de qualquer arquivo. Só quando um teste mudou:
#!/bin/bash
# .claude/hooks/test-on-change.sh
FILE=$(jq -r '.tool_input.file_path // empty' < /dev/stdin)
if [[ "$FILE" == *Tests.cs ]] || [[ "$FILE" == *Test.cs ]]; then
echo "Arquivo de teste modificado, rodando dotnet test..."
dotnet test --no-build --verbosity minimal 2>&1
fi
Aqui o output do dotnet test aparece no terminal enquanto o Claude Code espera. Se os testes falharem, o agente vê o resultado antes de continuar. Pode usar essa informação na próxima resposta se você perguntar.
Só que tem um detalhe: se sua suite demora mais de 20-30 segundos, o hook trava o fluxo. Pra suites longas, vale rodar só o projeto de testes do arquivo que mudou ou adicionar --filter pra reduzir o escopo.
3. Bloquear comandos destrutivos
PreToolUse roda antes da ferramenta agir. Se o script retornar permissionDecision: "deny", o Claude Code cancela a ação e explica por quê. Útil pra criar uma lista de comandos que nunca podem rodar no projeto, independente do que o agente decida:
#!/bin/bash
# .claude/hooks/block-destructive.sh
COMMAND=$(jq -r '.tool_input.command // empty' < /dev/stdin)
if echo "$COMMAND" | grep -qE 'rm -rf|DROP TABLE|DELETE FROM .+WHERE 1=1'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Comando destrutivo bloqueado pelo hook de segurança do projeto. Revise manualmente antes de executar."
}
}'
else
exit 0
fi
No settings.json, esse hook vai em PreToolUse com matcher: "Bash":
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh"
}
]
}
]
}
}
Isso não substitui backup e permissões de infra. Mas elimina o caso em que o agente interpreta mal um pedido e tenta deletar algo que não devia.
4. Registrar quais arquivos foram modificados
Em projetos com code review, saber o que o Claude Code tocou numa sessão ajuda. Um hook simples que mantém um log de edições:
#!/bin/bash
# .claude/hooks/audit-log.sh
INPUT=$(cat /dev/stdin)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
if [[ -n "$FILE" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | $TOOL | $FILE" >> "${CLAUDE_PROJECT_DIR}/.claude/edit-log.txt"
fi
O .claude/edit-log.txt acumula entradas como:
2026-05-30 02:14:33 | Edit | /projeto/src/Api/Controllers/PedidoController.cs
2026-05-30 02:14:51 | Write | /projeto/src/Application/DTOs/PedidoDto.cs
Esse arquivo fica no .gitignore. É ephemeral por sessão, não artefato do repo.
Cuidados pra não travar o fluxo
Hooks síncronos bloqueiam o agente. Por padrão, o Claude Code espera o hook terminar antes de continuar. Um dotnet test de 45 segundos significa 45 segundos parado. Se o hook for de monitoramento e não precisar bloquear, use "async": true na configuração.
Exit codes importam. Exit 0 é sucesso. Exit 2 bloqueia a ação (para PreToolUse). Exit 1 ou qualquer outro valor diferente de 0 e 2 registra um erro no transcript, mas o agente continua. Use isso pra distinguir "bloqueio intencional" de "erro que aconteceu mas não é crítico".
Cuidado com loops. Um hook PostToolUse que edita um arquivo dispara o mesmo hook de novo. Se não houver condição de saída, entra em loop. Sempre condicione: verifique o tipo de arquivo, o nome da ferramenta ou o conteúdo antes de agir.
Vale combinar com CLAUDE.md
Hooks automatizam ações. O CLAUDE.md documenta intenções. Os dois trabalham juntos: você escreve no CLAUDE.md "sempre rode dotnet format antes de commitar" e coloca o hook pra garantir que isso acontece de verdade, mesmo que o agente esqueça ou decida pular.
E se o consumo de tokens tá preocupando (hooks podem adicionar output que vai pro contexto), o post como economizar tokens no Claude Code tem as técnicas que mais fazem diferença pra manter o custo controlado em sessões longas.
Para quem quer ir além do básico, o curso de Claude Code cobre workflows completos com hooks, sub-agentes e automações aplicadas em projetos ASP.NET Core reais.