← Todos os Posts

Tutorial de Hooks do Claude Code: 5 hooks de produção do zero

From the guide: Claude Code Comprehensive Guide

Claude Code executa a ação correta aproximadamente 95% das vezes. Os 5% restantes incluem force-push na main, pular o formatter e fazer commit de código que falha no lint. Hooks eliminam esses 5% adicionando portões determinísticos em 17 pontos do ciclo de vida no fluxo de trabalho do Claude. Eles disparam sempre, sem exceção, independentemente da formulação do prompt ou do comportamento do modelo.

Resumo: Hooks são comandos shell acionados por eventos do ciclo de vida do Claude Code. Hooks PreToolUse inspecionam e bloqueiam ações (código de saída 2 = bloquear, saída 0 = permitir). Hooks PostToolUse validam e formatam após a execução. Configure-os em .claude/settings.json com um regex matcher e um array aninhado hooks. Este tutorial constrói cinco hooks de produção: auto-formatter, portão de segurança, executor de testes, alerta de notificação e verificação de qualidade antes do commit.

Principais conclusões

  • Desenvolvedores solo: Comece com o auto-formatter (Hook 1) e o portão de segurança (Hook 2). Esses dois hooks previnem os erros mais comuns do Claude Code com zero manutenção contínua.
  • Líderes de equipe: Faça commit dos hooks em .claude/settings.json no seu repositório. Cada membro da equipe recebe os mesmos portões de segurança e verificações de qualidade automaticamente.
  • Engenheiros de segurança: O código de saída 2 bloqueia a ação. O código de saída 1 apenas registra um aviso. Todo hook de segurança PreToolUse deve usar exit 2, ou não fornece nenhuma aplicação.

O que são hooks?

Hooks são comandos shell que executam em eventos específicos do ciclo de vida durante uma sessão do Claude Code. Eles rodam fora do LLM como scripts simples acionados pelas ações do Claude, não como prompts interpretados pelo modelo.

Quatro categorias principais cobrem os casos de uso mais comuns (Claude Code suporta 17 tipos de eventos no total):

  • Eventos de sessão: SessionStart e Stop disparam quando uma sessão começa ou termina. Use-os para setup, teardown e notificações.
  • Eventos de ferramenta: PreToolUse e PostToolUse disparam antes e depois do Claude usar uma ferramenta (escrever um arquivo, executar um comando bash ou pesquisar código). Esses são os hooks mais poderosos porque podem inspecionar e bloquear ações específicas.
  • Eventos de notificação: Notification dispara quando o Claude gera uma notificação. Útil para direcionar alertas ao Slack, notificações de desktop ou sistemas de log.
  • Eventos de subagente: SubagentStop dispara quando um subagente (criado via a ferramenta Agent) é concluído. Hooks disparam para ações de subagentes também, então seus portões de segurança se aplicam recursivamente.

A semântica dos códigos de saída importa. Saída 0 significa sucesso (prosseguir). Saída 2 significa bloquear a ação. Saída 1 significa um erro não-bloqueante do hook onde a ação ainda prossegue. Todo hook crítico de segurança deve usar exit 2 para realmente aplicar seu portão.


Fundamentos da configuração de hooks

Hooks ficam nos seus arquivos de configurações:

  • Nível de projeto: .claude/settings.json na raiz do seu repositório (compartilhado com sua equipe)
  • Nível de usuário: ~/.claude/settings.json (seus hooks pessoais, aplicados globalmente)

A estrutura JSON:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/your/script.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/another-script.sh"
          }
        ]
      }
    ]
  }
}

Cada entrada tem um matcher (regex correspondendo a nomes de ferramentas como Bash, Write, Edit, Read, Glob, Grep ou Agent) e um array hooks de definições de hooks. Cada hook especifica um type ("command" para comandos shell) e o command a executar. O matcher Write|Edit corresponde a ambos os tipos de ferramenta.

Você também pode gerenciar hooks interativamente com o comando /hooks dentro de uma sessão do Claude Code.

Quando um hook dispara, o Claude Code passa contexto via variáveis de ambiente ($FILE_PATH para operações de arquivo) e stdin (um objeto JSON contendo o nome da ferramenta, parâmetros e metadados da sessão). Seu script lê isso para tomar decisões.


5 Hooks práticos

Cada hook abaixo resolve um problema real que encontrei ao usar o Claude Code como minha ferramenta principal de desenvolvimento. Todos os exemplos usam o esquema aninhado correto de hooks.

1. Auto-formatação ao editar arquivo

O Claude escreve código funcionalmente correto que ocasionalmente quebra as regras de formatação do seu projeto. Em vez de pedir ao Claude para reformatar, execute seu formatter automaticamente após cada escrita de arquivo.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]]; then black --quiet \"$FILE_PATH\" 2>/dev/null; elif [[ \"$FILE_PATH\" == *.js ]] || [[ \"$FILE_PATH\" == *.ts ]]; then npx prettier --write \"$FILE_PATH\" 2>/dev/null; fi'"
          }
        ]
      }
    ]
  }
}

O Claude Code define $FILE_PATH como o arquivo modificado. O hook verifica a extensão e executa o formatter apropriado. Arquivos Python recebem black, arquivos JavaScript e TypeScript recebem prettier. O 2>/dev/null suprime saída verbosa para que você veja apenas erros reais.

Para projetos maiores, mova o comando inline para um script separado para melhor legibilidade.

2. Portão de segurança para comandos perigosos

Hooks PreToolUse na ferramenta Bash inspecionam o comando que o Claude está prestes a executar e o bloqueiam se o comando corresponder a um padrão perigoso. Escrevi este hook depois que o Claude fez force-push na main durante uma sessão de refatoração.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"rm\\s+-rf\\s+/|git\\s+push\\s+(-f|--force)\\s+(origin\\s+)?main|git\\s+reset\\s+--hard|DROP\\s+TABLE|:(){ :|:& };:\"; then echo \"BLOCKED: Dangerous command detected: $CMD\" >&2; exit 2; fi'"
          }
        ]
      }
    ]
  }
}

Quando este hook sai com código 2, o Claude Code cancela o comando pendente. A mensagem de erro é exibida tanto no seu terminal quanto no contexto do Claude, para que o modelo entenda por que a ação falhou e sugira uma alternativa mais segura.

Padrões bloqueados: - rm -rf / (exclusão recursiva a partir da raiz) - git push --force main e git push -f main (force push na branch main) - git reset --hard (destruição de trabalho não commitado) - DROP TABLE (destruição acidental de banco de dados) - Fork bombs

Personalize esta lista para o seu ambiente. Bancos de dados de produção precisam de padrões de SQL destrutivo. Deployments baseados em CLI precisam de proteções para comandos de implantação.

3. Executor de testes após alterações

Quando o Claude edita um arquivo Python, execute automaticamente os testes relevantes. Isso captura regressões imediatamente em vez de descobri-las três edições de arquivo depois.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]] && [[ \"$FILE_PATH\" != *test_* ]]; then TEST_FILE=\"tests/test_$(basename \"$FILE_PATH\")\"; if [[ -f \"$TEST_FILE\" ]]; then python -m pytest \"$TEST_FILE\" -x --tb=short 2>&1 | tail -20; fi; fi'"
          }
        ]
      }
    ]
  }
}

O hook verifica se o arquivo editado é um arquivo fonte Python (não um arquivo de teste), procura um arquivo de teste correspondente usando a convenção de nomenclatura com prefixo test_ e o executa se encontrado. A flag -x para na primeira falha, e tail -20 mantém a saída concisa.

Nota: Este hook assume um diretório tests/ plano com nomenclatura de prefixo test_. Ajuste a construção do caminho para diretórios de teste aninhados ou convenções de nomenclatura diferentes.

Este hook é particularmente valioso durante sessões de refatoração onde o Claude altera múltiplos arquivos. O ciclo de feedback imediato previne a cascata de “corrigir uma coisa, quebrar três outras” que acontece quando os testes são executados apenas no final.

4. Notificação ao final da sessão

Sessões longas do Claude Code podem levar minutos. Em vez de ficar observando o terminal, receba uma notificação quando a sessão terminar.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code session ended\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Este exemplo para macOS usa osascript para acionar uma notificação nativa. Para Linux, substitua a linha osascript por notify-send "Claude Code" "Session ended". Para notificações no Slack, use um webhook:

curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H 'Content-type: application/json' \
  -d '{"text": "Claude Code session ended"}'

Eu uso a variante do Slack para tarefas em segundo plano iniciadas com & <task> (modo background do Claude Code). A notificação de desktop é para sessões interativas.

5. Verificação de qualidade antes do commit

Antes do Claude executar git commit, valide que o código passa no linting. Isso captura problemas que a formatação sozinha não detecta: imports não utilizados, variáveis indefinidas, erros de tipo.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"^git\\s+commit\"; then if ! LINT_OUTPUT=$(ruff check . --select E,F,W 2>&1); then echo \"LINT FAILED -- fix before committing:\" >&2; echo \"$LINT_OUTPUT\" >&2; exit 2; fi; fi'"
          }
        ]
      }
    ]
  }
}

Este hook ativa apenas quando o comando Bash começa com git commit. Ele executa o ruff (um linter rápido para Python) com regras de erro, pyflakes e avisos. Se houver problemas, o commit é bloqueado (exit 2) e o Claude vê a saída do lint, o que tipicamente faz com que ele corrija os problemas e tente novamente.

Você pode empilhar múltiplas verificações de qualidade: mypy para verificação de tipos, bandit para análise de segurança, ou os scripts de validação personalizados do seu projeto. Hooks PreToolUse em comandos Bash dão a você um portão programável antes de qualquer ação no shell.


Dicas para depuração de hooks

Hooks falham silenciosamente com mais frequência do que você esperaria. Cinco técnicas que uso para depurá-los:

  1. Teste scripts independentemente primeiro. Passe JSON de exemplo para seu script manualmente: echo '{"tool_input":{"command":"git commit -m test"}}' | bash your-hook.sh. Se falhar fora do Claude Code, falha dentro dele também.
  2. Use stderr para saída de depuração. Tudo que seu hook escreve em stderr aparece no contexto do Claude. Escreva echo "DEBUG: matched $CMD" >&2 durante o desenvolvimento, depois remova quando o hook estiver sólido.
  3. Fique atento a falhas do jq. Se o caminho do seu JSON estiver errado, o jq retorna null silenciosamente e suas condicionais não vão corresponder. Teste suas expressões jq contra a entrada real da ferramenta.
  4. Verifique os códigos de saída. Exit 2 bloqueia ações. Exit 1 apenas avisa. Um hook PreToolUse que acidentalmente usa exit 1 não fornece nenhuma aplicação enquanto parece funcionar. Comece permissivo (exit 0 por padrão) e use exit 2 apenas para padrões específicos bloqueados.
  5. Mantenha hooks rápidos. Hooks executam sincronamente. Um hook que leva 5 segundos adiciona 5 segundos a cada uso de ferramenta correspondente. Eu mantenho todos os meus hooks abaixo de 2 segundos, idealmente abaixo de 500 milissegundos.

Próximos passos

Esses cinco hooks cobrem os fundamentos: formatação, segurança, testes, notificações e portões de qualidade. Uma vez que você esteja confortável com esses padrões, pode construir hooks para injeção de contexto (adicionando instruções específicas do projeto no início da sessão), proteções contra recursão (prevenindo loops infinitos de subagentes) e orquestração de fluxos de trabalho (encadeando processos de múltiplas etapas).

Para a arquitetura de hooks, todos os 17 eventos do ciclo de vida e padrões avançados, veja a seção de hooks do meu guia completo: Guia do Claude Code: Como os hooks funcionam?

Também escrevi sobre as histórias de origem dos meus 95 hooks de produção em Hooks do Claude Code: Por que cada um dos meus 95 hooks existe, que cobre os incidentes que motivaram cada um deles.


FAQ

Hooks podem impedir o Claude Code de executar um comando?

Sim. Hooks PreToolUse bloqueiam qualquer ação de ferramenta saindo com código 2. O Claude Code cancela a ação pendente e mostra a saída stderr do hook para o modelo. Exit 1 é um erro não-bloqueante do hook onde a ação ainda prossegue. Essa distinção é crítica: todo hook de segurança deve usar exit 2, não exit 1. O Claude vê o motivo da rejeição e sugere uma alternativa mais segura.

Onde coloco os arquivos de configuração de hooks?

Configurações de hooks vão em .claude/settings.json para hooks de nível de projeto (commitados no seu repositório, compartilhados com sua equipe) ou ~/.claude/settings.json para hooks de nível de usuário (pessoais, aplicados a todos os projetos). Hooks de nível de projeto têm precedência quando ambos existem. Eu recomendo usar caminhos absolutos para arquivos de script para evitar problemas de diretório de trabalho.

Hooks funcionam com subagentes?

Sim. Hooks disparam para ações de subagentes também. Se o Claude criar um subagente via a ferramenta Agent, seus hooks PreToolUse e PostToolUse executam para cada ferramenta que o subagente usa. Sem esse comportamento, um subagente poderia contornar seus portões de segurança. O evento SubagentStop permite executar limpeza ou validação quando um subagente conclui sua tarefa.

Artigos relacionados

How to Set Up Claude Code CLI: 5-Minute Quickstart

Install Claude Code, configure your first project, and run your first agentic coding session in under 5 minutes. Covers …

13 min de leitura

Codex CLI vs Claude Code in 2026: Architecture Deep Dive

Kernel-level sandboxing vs application-layer hooks, AGENTS.md vs CLAUDE.md, cloud tasks vs subagents. A technical compar…

13 min de leitura

Claude Code Hooks: Why Each of My 95 Hooks Exists

I built 95 hooks for Claude Code. Each one exists because something went wrong. Here are the origin stories and the arch…

7 min de leitura