← Todos os Posts

Hooks do Claude Code explicados: a camada determinística em volta do seu agente

Do guia: Claude Code Comprehensive Guide

O que são os hooks do Claude Code? Hooks são comandos de shell definidos pelo usuário (além de endpoints HTTP, ferramentas MCP e prompts para o modelo) que o Claude Code executa automaticamente em pontos fixos do seu ciclo de vida: antes de uma chamada de ferramenta, depois de uma edição, no início da sessão, quando o Claude termina de responder.1 Enquanto o CLAUDE.md dá ao modelo instruções que ele provavelmente vai seguir, os hooks são executados quer o modelo coopere, quer não. Digite /hooks em qualquer sessão para ver todos os eventos do ciclo de vida e o que está ligado a cada um. {.answer-block}

A maioria dos desenvolvedores usa o Claude Code com duas camadas de controle: as permissões, que filtram o que o agente pode fazer, e o CLAUDE.md, que descreve o que ele deveria fazer. Os hooks são a terceira camada, e a única que garante alguma coisa. A seguir: o modelo mental, todos os eventos do ciclo de vida presentes na documentação atual, o contrato exato de entrada e saída, a configuração, cinco padrões funcionais e um quadro de decisão. Cada detalhe de API foi verificado em 8 de agosto de 2026 contra a referência oficial de hooks e o guia — este sistema muda rápido, então, onde este post e a referência divergirem, quem manda é a referência. (É seu primeiro contato com o Claude Code? Comece pela instalação em 5 minutos ou pela trilha de início.)

Resumo: os hooks recebem JSON no stdin e respondem com códigos de saída ou com JSON no stdout. O código 0 permite, o código 2 bloqueia (nos eventos capazes de bloquear) e o código 1 — o código de falha convencional no Unix — não bloqueia nada, o que é de longe a maior armadilha dos hooks.2 Configure-os no settings.json sob nomes de eventos como PreToolUse e Stop, filtrados por matchers. Use hooks para tudo o que precisa acontecer sempre; use o CLAUDE.md para tudo o que o modelo apenas deveria saber.

O modelo mental: garantias em volta de um núcleo não determinístico

Um agente de programação é um sistema probabilístico. Peça que ele rode o Prettier depois de cada edição e ele vai rodar — na maior parte das vezes. Ele pode pular a etapa quando a mudança parece trivial, quando o contexto se alonga ou quando o seu jeito de pedir soa diferente. CLAUDE.md, skills e prompts são todos sugestões: de boa qualidade, quase sempre seguidas, nunca garantidas.

Os hooks são a casca determinística em volta desse núcleo. O guia abre com uma definição de uma linha só – “Hooks são comandos de shell definidos pelo usuário.” – e diz o essencial sem rodeios: os hooks dão a você “controle determinístico: certas ações sempre acontecem, em vez de depender de o LLM escolher executá-las”.3 (Essa linha única do guia subestima a superfície atual; a definição mais completa da referência já acrescenta endpoints HTTP e prompts para o LLM, e os handlers também existem como ferramentas MCP – assunto da seção de configuração, mais adiante.) O formatador dispara a cada edição. A barreira de comandos avalia toda chamada Bash. O portão de conclusão confere cada encerramento.

A imposição é real, não cosmética: os hooks PreToolUse disparam antes de qualquer checagem do modo de permissão, então um hook que devolve permissionDecision: "deny" bloqueia a ferramenta mesmo no modo bypassPermissions ou sob --dangerously-skip-permissions. O inverso não vale — um hook que devolve "allow" não afrouxa regras de negação vindas das configurações. Os hooks conseguem apertar a política além do que as permissões liberam, mas nunca enfraquecê-la.4

O ciclo de vida: todos os eventos de hook

Em 8 de agosto de 2026, a referência documenta 31 eventos de hook.1 Eles se dividem em três cadências: uma vez por sessão (SessionStart, SessionEnd), uma vez por turno (UserPromptSubmit, Stop, StopFailure) e a cada chamada de ferramenta dentro do laço agêntico (PreToolUse, PostToolUse). O restante dispara em condições específicas — mudanças de configuração, compactação, subagentes, interações MCP.

Evento Quando dispara Um uso real
SessionStart A sessão começa ou é retomada Injetar a branch do git e as issues abertas como contexto
Setup --init-only, ou --init/--maintenance no modo -p Instalar dependências na CI antes de o agente rodar
UserPromptSubmit Você envia um prompt, antes de o Claude processá-lo Anexar a data atual; recusar prompts com segredos
UserPromptExpansion Um comando digitado se expande em um prompt Auditar ou vetar expansões de skills e comandos
PreToolUse Antes de uma chamada de ferramenta ser executada Bloquear comandos de shell destrutivos
PermissionRequest Uma caixa de diálogo de permissão aparece Aprovar comandos confiáveis automaticamente, sem perguntar
PermissionDenied O classificador do modo automático nega uma chamada Devolver retry: true para o modelo poder tentar de novo
PostToolUse Depois de uma chamada de ferramenta bem-sucedida Formatar automaticamente cada arquivo editado
PostToolUseFailure Depois de uma chamada de ferramenta falhar Registrar comandos com falha para triagem
PostToolBatch Depois de um lote de chamadas paralelas, antes da próxima chamada ao modelo Salvar um checkpoint ou interromper o laço agêntico
Notification O Claude Code envia uma notificação Alerta na área de trabalho quando o Claude precisa de resposta
MessageDisplay Enquanto o texto de uma mensagem do assistente é exibido Censurar na tela (só a exibição; a transcrição fica intacta)
SubagentStart Um subagente é iniciado Injetar contexto específico do tipo de agente
SubagentStop Um subagente termina Validar a saída do subagente antes de ela retornar
TaskCreated Uma tarefa é criada via TaskCreate Impor regras de nomenclatura ou de escopo das tarefas
TaskCompleted Uma tarefa é marcada como concluída Conferir os critérios de aceite antes de a conclusão valer
Stop O Claude termina de responder Portão de conclusão: impedir o fim até os testes passarem
StopFailure O turno acaba por causa de um erro de API Alertar em rate_limit ou billing_error (só log; saída ignorada)
TeammateIdle Um colega de um time de agentes está prestes a ficar ocioso Manter os colegas trabalhando a partir de uma fila
InstructionsLoaded Um arquivo CLAUDE.md ou .claude/rules/*.md entra no contexto Registrar quais instruções entraram na sessão
ConfigChange Um arquivo de configuração muda no meio da sessão Bloquear edições não autorizadas das configurações
CwdChanged O diretório de trabalho muda Recarregar ambientes no estilo do direnv
DirectoryAdded Um diretório de trabalho é registrado no meio da sessão via /add-dir ou pelo register_repo_root do SDK (v2.1.219+) Carregar o contexto desse repositório assim que ele entra na sessão
FileChanged Um arquivo monitorado muda no disco Atualizar variáveis de ambiente quando o .env muda
WorktreeCreate Um worktree é criado via --worktree ou isolation: "worktree" Substituir o provisionamento padrão de worktrees do git
WorktreeRemove Um worktree é removido Limpeza sob medida no fim da sessão ou do subagente
PreCompact Antes da compactação do contexto Salvar o estado que você não pode perder
PostCompact Quando a compactação termina Reinjetar o contexto crítico
Elicitation Um servidor MCP pede uma entrada do usuário Preencher formulários automaticamente em execuções headless
ElicitationResult Depois de você responder a uma solicitação MCP Validar ou substituir a resposta antes de ela retornar
SessionEnd A sessão é encerrada Arquivar logs, liberar recursos

Você não vai precisar da maioria deles. Quase toda configuração de produção nasce de cinco: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart e Stop. O resto existe para o dia em que você precisar.

O contrato: JSON na entrada, códigos de saída ou JSON na saída

Os hooks do tipo comando recebem JSON no stdin e respondem por códigos de saída, stdout e stderr. (Os hooks HTTP recebem o mesmo JSON como corpo de um POST e respondem pelo corpo da resposta.)2

Todo evento entrega um envelope comum — session_id, transcript_path, cwd e hook_event_name, com permission_mode na maioria dos eventos — mais campos próprios do evento. Um hook PreToolUse para um comando Bash recebe:

{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "npm test" }
}

Outros eventos trocam o final: UserPromptSubmit carrega prompt, SessionStart carrega source (startup/resume/clear/compact/fork – o quinto valor chegou com as sessões bifurcadas na v2.1.214, e um hook que filtra por source copiado de uma lista antiga de quatro valores vai ignorar os forks em silêncio), Stop carrega stop_hook_active e last_assistant_message. Hooks disparados dentro de subagentes recebem ainda agent_id e agent_type.2

Códigos de saída

Três desfechos:2

  • Código 0 — sucesso. O Claude Code analisa o stdout em busca de campos de saída em JSON. Na maioria dos eventos o stdout vai só para o log de depuração; em UserPromptSubmit, UserPromptExpansion e SessionStart, um stdout simples é acrescentado como contexto que o Claude enxerga.
  • Código 2 — erro bloqueante. O stdout (inclusive qualquer JSON) é ignorado; o stderr volta para o Claude como mensagem de erro. O que “bloquear” significa depende do evento.
  • Qualquer outro código de saída — erro não bloqueante. A transcrição mostra um aviso <hook name> hook error, e a execução segue.

Essa última linha merece negrito: o código 1 não bloqueia nada. A documentação avisa isso diretamente — o Claude Code trata o código 1 como erro não bloqueante e prossegue, mesmo sendo 1 o código de falha convencional no Unix. Hooks de política precisam usar exit 2.2

O que o código 2 faz, evento a evento:2

Evento Efeito do código 2
PreToolUse Bloqueia a chamada de ferramenta
PermissionRequest Nega a permissão
UserPromptSubmit Bloqueia o processamento e apaga o prompt
UserPromptExpansion Bloqueia a expansão
Stop / SubagentStop Impede a parada; a conversa continua
TeammateIdle Impede o colega de ficar ocioso
TaskCreated / TaskCompleted Desfaz a criação / impede a conclusão
ConfigChange Bloqueia a mudança de configuração (exceto policy_settings)
PreCompact Bloqueia a compactação
PostToolBatch Interrompe o laço agêntico antes da próxima chamada ao modelo
Elicitation / ElicitationResult Nega a solicitação / transforma a resposta em recusa
WorktreeCreate Qualquer código diferente de zero aborta a criação do worktree

Todo o resto não consegue bloquear. PostToolUse e PostToolUseFailure mostram o stderr ao Claude (a ferramenta já rodou); SessionStart, Notification, SessionEnd, CwdChanged, FileChanged, PostCompact, SubagentStart e Setup mostram o stderr apenas ao usuário, enquanto DirectoryAdded manda o stderr só para o log de depuração; StopFailure, InstructionsLoaded, MessageDisplay e PermissionDenied ignoram o código de saída — e, no caso de PermissionDenied, a única alavanca é o campo JSON retry: true.2

Saída em JSON

Para um controle mais fino do que bloquear ou silenciar, saia com o código 0 e imprima um objeto JSON no stdout. Uma regra logo de cara: códigos de saída ou JSON, nunca os dois — o JSON só é processado com o código 0, e o código 2 o descarta.5

Campos universais funcionam em qualquer evento: continue: false para o Claude por completo (com stopReason exibido ao usuário), suppressOutput esconde o stdout da transcrição, systemMessage mostra um aviso ao usuário e terminalSequence emite uma sequência de escape de terminal permitida (notificação na área de trabalho, título de janela, campainha). Já os campos de decisão variam por evento:5

Eventos Padrão de decisão Campos principais
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact decision no nível superior decision: "block" + reason (exibido ao Claude). Omita decision para permitir
PreToolUse hookSpecificOutput permissionDecision: "allow" | "deny" | "ask" | "defer", além de permissionDecisionReason e updatedInput para reescrever os argumentos da ferramenta antes da execução
PermissionRequest hookSpecificOutput decision.behavior: "allow" | "deny", com decision.updatedInput opcional
PermissionDenied hookSpecificOutput retry: true diz ao modelo que ele pode tentar de novo
PostToolUse hookSpecificOutput updatedToolOutput substitui o resultado da ferramenta
Stop / SubagentStop hookSpecificOutput additionalContext: retorno sem caráter de erro que continua a conversa sem contar como erro de hook
SessionStart, Setup, SubagentStart Só contexto additionalContext, mais initialUserMessage, sessionTitle, watchPaths e reloadSkills, exclusivos do SessionStart. Sem bloqueio
MessageDisplay hookSpecificOutput displayContent substitui apenas o texto exibido na tela
Elicitation / ElicitationResult hookSpecificOutput action: "accept" | "decline" | "cancel", além de content
TeammateIdle, TaskCreated, TaskCompleted continue universal continue: false + stopReason interrompe por completo o fluxo do colega ou da tarefa (o código 2 é o bloqueio próprio do evento)
WorktreeCreate Retorno de um caminho Hooks do tipo comando imprimem o caminho do worktree no stdout; hooks HTTP devolvem hookSpecificOutput.worktreePath; uma falha ou um caminho ausente derruba a criação
WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged Nenhum Só efeitos colaterais

Dois detalhes que pegam as pessoas. Primeiro, PreToolUse é a exceção ao padrão do decision no nível superior: historicamente ele usava decision/reason ali, mas esses campos estão obsoletos para este evento ("approve"/"block" correspondem a "allow"/"deny"); use hookSpecificOutput.permissionDecision.5 Segundo, quando vários hooks PreToolUse discordam, a precedência é deny > defer > ask > allow – vence a resposta mais restritiva. Ainda assim, dê a cada decisão um único hook responsável, em vez de se apoiar nesse desempate.5

Configuração: settings.json, matchers, escopo

A configuração de hooks se aninha em três níveis: escolha um evento, acrescente um grupo de matchers para filtrar quando ele dispara e defina um ou mais handlers de hook para rodar.6

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "/path/to/lint-check.sh" }
        ]
      }
    ]
  }
}

Onde você coloca isso define o escopo: ~/.claude/settings.json vale para todos os seus projetos, .claude/settings.json é do projeto e pode ir para o repositório, .claude/settings.local.json é do projeto e fica fora do git, e vale a precedência habitual das configurações — política gerenciada acima da local, que fica acima da do projeto, que fica acima da do usuário.9 Os hooks também podem vir em plugins (hooks/hooks.json) e no frontmatter de uma skill ou de um agente, e administradores corporativos podem impor hooks gerenciados que os usuários não conseguem sobrescrever.6

Os matchers são avaliados pelos caracteres que contêm: "*", "" ou um matcher omitido casam com tudo; um valor que só tenha letras, dígitos, _, -, espaços, vírgulas e | é uma string exata ou uma lista (Bash, Edit|Write); qualquer outra coisa vira uma expressão regular de JavaScript sem âncoras, de modo que Edit.* casa tanto com Edit quanto com NotebookEdit — ancore com ^Edit$ quando quiser exatamente uma ferramenta. Os matchers diferenciam maiúsculas de minúsculas, e cada evento casa contra o próprio campo: nome da ferramenta nos eventos de ferramenta, source no SessionStart, tipo de agente no SubagentStart, tipo de notificação no Notification.6 Para um filtro mais afiado nos eventos de ferramenta, o campo if de cada handler aceita uma regra de permissão como "Bash(git *)" — mas ele age na medida do possível (libera quando não consegue analisar o comando), então use regras de permissão, e não if, para garantias firmes.6 Vale conhecer uma mudança de semântica: desde a v2.1.214, um padrão de caminho de um único segmento no if (como Edit(src/**)) casa apenas com um src de primeiro nível abaixo do diretório de trabalho; um if escrito antes dessa versão para de casar, sem avisar, com caminhos aninhados como packages/app/src/ – escreva Edit(**/src/**) para recuperar o comportamento em qualquer profundidade.6

Os handlers vêm em cinco tipos: command (shell), http (endpoint POST), mcp_tool, prompt (avaliação do modelo em um turno) e agent (um subagente com acesso a Read/Grep/Glob; experimental). Tempos limite padrão: 600 segundos para command/http/mcp_tool (reduzidos para 30 no UserPromptSubmit e para 10 no MessageDisplay), 30 para prompt e 60 para agent — ajustáveis hook a hook com timeout.6 Todos os hooks correspondentes rodam em paralelo, com handlers idênticos deduplicados, e $CLAUDE_PROJECT_DIR aponta seus scripts para a raiz do projeto.

Confirme com /hooks: um navegador somente leitura que mostra cada evento, os hooks configurados nele e de qual arquivo de configuração cada um veio. Para mudar qualquer coisa, edite o JSON (ou peça ao Claude). Para desligar tudo temporariamente, defina "disableAllHooks": true.6

Cinco padrões

Genéricos e mínimos. O tutorial de hooks constrói versões de produção mais completas de vários deles, e Hooks para desenvolvimento Apple os aplica à cadeia de ferramentas do iOS.

1. Formatação automática depois das edições (PostToolUse)

Direto do guia oficial — todo arquivo que o Claude toca sai formatado, sem exceções:3

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
        ]
      }
    ]
  }
}

Troque o comando por ruff format, gofmt ou swiftformat, conforme o seu stack exigir.

2. Bloquear comandos perigosos (PreToolUse, código 2)

#!/bin/bash
# .claude/hooks/guard-bash.sh — register on PreToolUse, matcher "Bash"
command=$(jq -r '.tool_input.command // empty')
case "$command" in
  *"rm -rf"* | *"git push --force"* | *"DROP TABLE"*)
    echo "Blocked: matches a destructive pattern. Propose a safer alternative." >&2
    exit 2 ;;
esac
exit 0

O código 2 bloqueia a chamada e devolve o stderr ao Claude, que corrige o rumo em vez de tentar de novo às cegas. O equivalente em JSON — permissionDecision: "deny" com um motivo — faz o mesmo, com espaço para evoluir para "ask" (escalar para a pessoa) ou updatedInput (reescrever o comando).5

3. Injetar contexto no início da sessão (SessionStart)

Um stdout simples vindo de um hook SessionStart vira contexto que o Claude enxerga — sem precisar de JSON:1

#!/bin/bash
# .claude/hooks/session-context.sh — register on SessionStart
echo "Current branch: $(git branch --show-current)"
echo "Recent commits:"
git log --oneline -5
echo "Uncommitted files: $(git status --porcelain | wc -l | tr -d ' ')"
exit 0

Reserve isso para estado dinâmico. Convenções estáticas pertencem ao CLAUDE.md, o que a própria documentação recomenda para contexto que não exige um script.1

4. Um portão de conclusão no Stop

Stop dispara quando o Claude termina de responder. Bloqueá-lo obriga o agente a continuar trabalhando até que uma condição se cumpra:

#!/bin/bash
# .claude/hooks/stop-gate.sh — register on Stop
input=$(cat)
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # already continuing because of this hook; don't loop forever
fi
if ! npm test --silent > /tmp/stop-gate.log 2>&1; then
  jq -n '{decision: "block", reason: "Tests are failing. Fix them before finishing. Log: /tmp/stop-gate.log"}'
fi
exit 0

A checagem de stop_hook_active importa: o Claude Code limita um hook de Stop a 8 bloqueios consecutivos por padrão (aumentável com CLAUDE_CODE_STOP_HOOK_BLOCK_CAP), e um portão que nunca verifica se ele mesmo provocou a continuação queima todos eles de uma vez.7 Para uma condução mais suave, devolva hookSpecificOutput.additionalContext em vez de decision: "block" — a mesma continuação, mas como retorno identificado, e não como erro de hook. E, para condições pontuais, o comando embutido /goal é um hook de Stop baseado em prompt, restrito à sessão e sem configuração nenhuma.1

5. O dispatcher: um ponto de entrada, muitos hooks pequenos

Registrar dez hooks significa dez entradas no settings.json que se desencontram entre máquinas e projetos. A alternativa: registrar um dispatcher por evento e rotear por convenção.

#!/bin/bash
# .claude/hooks/dispatch.sh — register once per event you care about
input=$(cat)
event=$(echo "$input" | jq -r '.hook_event_name')
dir="$CLAUDE_PROJECT_DIR/.claude/hooks/$event"
[ -d "$dir" ] || exit 0
for hook in "$dir"/*.sh; do
  [ -x "$hook" ] || continue
  echo "$input" | "$hook" || exit $?
done
exit 0

Acrescentar uma barreira agora é um chmod +x em um arquivo novo dentro de .claude/hooks/PreToolUse/ — o settings.json nunca muda, cada script fica pequeno o bastante para ser testado isoladamente e o primeiro código 2 se propaga. Uma ressalva: o dispatcher serializa o que o Claude Code rodaria em paralelo, e combina melhor com hooks baseados em códigos de saída — um hook que emite JSON deveria continuar sozinho, já que o stdout precisa conter exatamente um objeto JSON.5

Hook, CLAUDE.md, skill ou memória

Quatro mecanismos, quatro funções:

Mecanismo Função Regra de escolha
Hook Imposição Se pular a etapa precisa ser impossível — formatação, segurança, portões — é um hook
CLAUDE.md Orientação Se é uma convenção que o modelo deveria conhecer em toda sessão — stack, estilo, comandos — é o CLAUDE.md
Skill Capacidade Se é um procedimento com instruções e scripts próprios, invocado quando faz sentido, é uma skill
Memória Lembrança Se é um fato aprendido numa sessão de que as sessões futuras precisam, é memória

O modo de falha vale nas duas direções. Codificar convenções como hooks rende scripts frágeis impondo aquilo que uma frase de orientação resolve tranquilamente. Codificar política como prosa no CLAUDE.md rende um agente que dá force push na main justo no dia em que isso importa. O teste: qual é o custo de o modelo ignorar isso uma vez? Irritação → CLAUDE.md. Incidente → hook.

O que os hooks não fazem

Limites honestos, todos vindos da documentação oficial:7

  • Hooks não conseguem chamar ferramentas nem comandos de barra. Hooks do tipo comando falam por stdout, stderr e códigos de saída — nada além disso. O contexto devolvido por additionalContext é injetado como texto puro.
  • PostToolUse não desfaz nada. A ferramenta já rodou. A prevenção mora no PreToolUse.
  • Stop dispara no fim de toda resposta, não só quando a “tarefa está completa”, e nunca em uma interrupção do usuário (erros de API disparam StopFailure no lugar). A lógica do portão precisa tolerar paradas no meio da tarefa.
  • PermissionRequest não dispara em execuções headless (-p) simples. Ele dispara, sim, sob -p quando um callback canUseTool do Agent SDK fornece a solicitação, e também nas chamadas de ferramenta de subagentes em segundo plano; para todo o resto automatizado, use PreToolUse.
  • PreToolUse não vê arquivos referenciados por @. Arquivos puxados por @ no seu prompt não envolvem chamada de ferramenta nenhuma; proteja esses caminhos com regras de negação no Read.1
  • updatedInput em paralelo não é confiável, por design. Quando vários hooks PreToolUse reescrevem os argumentos da mesma ferramenta, só uma reescrita sobrevive e você não escolhe qual. Deixe um único hook dono de cada reescrita.
  • Tempos limite cancelam o hook. 600 segundos por padrão nos hooks do tipo comando (30 no UserPromptSubmit, 10 no MessageDisplay); um portão lento que estoura o tempo é um portão que não rodou.
  • A saída é limitada a 10.000 caracteres — o excedente vai para um arquivo e é substituído por uma prévia.
  • Os hooks rodam com todas as suas permissões de usuário. O aviso da própria referência: eles “podem modificar, apagar ou acessar quaisquer arquivos que a sua conta de usuário consiga acessar. Revise e teste todos os comandos de hook antes de adicioná-los à sua configuração”.8 Coloque as variáveis entre aspas, use caminhos absolutos, deixe os arquivos sensíveis de fora.
  • Um hook quebrado degrada todas as sessões até ser consertado. Depure com a visão de transcrição (Ctrl+O), com claude --debug-file /tmp/claude.log ou com /debug no meio da sessão; uma cilada clássica é um perfil de shell que imprime algo na inicialização e corrompe a saída JSON do seu hook.7

Perguntas frequentes

O que são os hooks do Claude Code?

Hooks são comandos definidos pelo usuário — scripts de shell, endpoints HTTP, ferramentas MCP ou prompts para o modelo — que o Claude Code executa automaticamente em pontos específicos do ciclo de vida.3 Eles recebem o JSON do evento no stdin e respondem com códigos de saída ou com JSON: bloquear uma chamada de ferramenta, injetar contexto, reescrever argumentos, manter o agente trabalhando. Diferente das instruções do CLAUDE.md, eles são executados sempre, seja qual for o comportamento do modelo.

Qual é a diferença entre os hooks PreToolUse e as permissões?

Regras de permissão são declarativas: padrões estáticos de permitir, negar ou perguntar que o próprio Claude Code avalia. Os hooks PreToolUse são programáveis: o seu código inspeciona a entrada completa da ferramenta e decide. Os hooks disparam antes das checagens do modo de permissão, então um "deny" de um hook se sustenta mesmo no modo bypassPermissions — mas um "allow" de um hook não passa por cima de uma regra de negação das configurações.4 Use regras de permissão para tudo o que um padrão consegue expressar; recorra a um hook quando a decisão exigir lógica, estado externo ou reescrita da entrada.

Os hooks funcionam no modo headless (-p)?

Sim — com uma nuance: os hooks PermissionRequest pulam as execuções -p simples (nada fornece um pedido de permissão ali), embora disparem quando um callback canUseTool do Agent SDK fornece um, e também nas chamadas de ferramenta de subagentes em segundo plano. As decisões automáticas de permissão para execuções headless simples pertencem ao PreToolUse.7 O modo headless ainda libera uma opção que as sessões interativas ignoram: permissionDecision: "defer", que pausa uma chamada de ferramenta para que um processo externo (um aplicativo feito com o Agent SDK, uma interface própria) colete a entrada e retome a sessão mais tarde.5

Por que meu hook roda mas não bloqueia nada?

Quase sempre é uma violação do contrato. O código 1 não bloqueia — só o código 2 bloqueia, e apenas nos eventos que suportam bloqueio.2 Decisões em JSON só são interpretadas com o código 0 — um script que imprime {"decision": "block"} e depois sai com 2 tem seu JSON descartado. E os matchers diferenciam maiúsculas de minúsculas — bash nunca casa com Bash. Confirme o registro com /hooks e depois teste jogando um JSON de exemplo no script por um pipe e conferindo o echo $?.7

Fontes

Verificado contra a documentação oficial em 8 de agosto de 2026. A API de hooks mudou bastante ao longo das versões v2.1.x do Claude Code (novos eventos, novos campos, nova semântica de matchers), então trate os detalhes sensíveis à versão como válidos nessa data.

Relacionado neste site: a seção de hooks do guia do Claude Code para a visão do sistema inteiro, incluindo hooks do tipo prompt e agent; o tutorial de hooks para cinco construções de produção com as configurações completas; Hooks para desenvolvimento Apple para os padrões aplicados ao iOS; e a introdução rápida, caso você ainda não tenha instalado o Claude Code.


  1. Anthropic, “Hooks reference — Hook lifecycle and hook events”. code.claude.com/docs/en/hooks#hook-events 

  2. Anthropic, “Hooks reference — Hook input and output; exit code output; exit code 2 behavior per event”. code.claude.com/docs/en/hooks#exit-code-output 

  3. Anthropic, “Automate actions with hooks”. code.claude.com/docs/en/hooks-guide 

  4. Anthropic, “Hooks guide — Hooks and permission modes”. code.claude.com/docs/en/hooks-guide#hooks-and-permission-modes 

  5. Anthropic, “Hooks reference — JSON output and decision control”. code.claude.com/docs/en/hooks#json-output 

  6. Anthropic, “Hooks reference — Configuration: hook locations, matcher patterns, hook handler fields, o menu /hooks”. code.claude.com/docs/en/hooks#configuration 

  7. Anthropic, “Hooks guide — Limitations and troubleshooting”. code.claude.com/docs/en/hooks-guide#limitations-and-troubleshooting 

  8. Anthropic, “Hooks reference — Security considerations”. code.claude.com/docs/en/hooks#security-considerations 

  9. Anthropic, “Claude Code settings”. code.claude.com/docs/en/settings 

Artigos relacionados

Codex CLI vs Claude Code 2026: arquitetura, preços e acesso da China

Codex CLI vs Claude Code em 2026: sandbox de kernel, governança por hooks, contexto dos modelos, preços, acesso à nuvem …

31 min de leitura

Claude Code Hooks: Por que cada um dos meus 95 hooks existe

Construí 95 hooks para Claude Code. Cada um existe porque algo deu errado. Aqui estão as histórias de origem e a arquite…

9 min de leitura