← Todos os Posts

Hooks do Claude Code explicados: a camada determinística ao redor do seu agente

Do guia: Claude Code Comprehensive Guide

O que são os hooks do Claude Code? Hooks são comandos shell definidos pelo usuário (além de endpoints HTTP, ferramentas MCP e prompts de 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 executam com ou sem a cooperação do modelo. Digite /hooks dentro de qualquer sessão para ver todos os eventos do ciclo de vida e o que está conectado a cada um. {.answer-block}

A maioria dos desenvolvedores usa o Claude Code com duas camadas de controle: permissões limitando o que o agente pode fazer, e o CLAUDE.md descrevendo o que ele deveria fazer. 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 na documentação atual, o contrato exato de entrada/saída, a configuração, cinco padrões funcionais e um framework de decisão. Cada detalhe da API foi verificado contra a referência oficial de hooks e o guia em 1º de julho de 2026 — este sistema evolui rápido, então onde este post e a referência divergirem, a referência vence. (Novo no Claude Code? Comece pela configuração em 5 minutos ou pela trilha Novo no Claude Code.)

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

O modelo mental: garantias ao redor de um núcleo não determinístico

Um agente de código é um sistema probabilístico. Peça para ele rodar o Prettier depois de cada edição e ele vai rodar — na maioria das vezes. Ele pode pular a etapa quando a mudança parece trivial, quando o contexto fica longo demais ou quando a sua formulação soa diferente. CLAUDE.md, skills e prompts são todos sugestões: de alta qualidade, geralmente seguidas, nunca garantidas.

Hooks são a casca determinística ao redor desse núcleo. A definição oficial: “comandos shell definidos pelo usuário, endpoints HTTP ou prompts de LLM que executam automaticamente em pontos específicos do ciclo de vida do Claude Code”, fornecendo “controle determinístico sobre o comportamento do Claude Code, garantindo que certas ações sempre aconteçam em vez de depender da escolha do LLM de executá-las”.3 O formatador dispara em cada edição. O guarda de comandos avalia cada chamada de Bash. O gate de conclusão verifica cada finalização.

A aplicação é real, não cosmética: hooks de PreToolUse disparam antes de qualquer verificação de modo de permissão, então um hook que retorna permissionDecision: "deny" bloqueia a ferramenta mesmo no modo bypassPermissions ou sob --dangerously-skip-permissions. O inverso não vale — um hook que retorna "allow" não pode afrouxar regras de negação das configurações. Hooks podem apertar a política além do que as permissões permitem, nunca enfraquecê-la.4

O ciclo de vida: todos os eventos de hook

Em 1º de julho de 2026, a referência documenta 30 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 em cada chamada de ferramenta dentro do loop agêntico (PreToolUse, PostToolUse). Os demais disparam em condições específicas — mudanças de configuração, compactação, subagentes, interações MCP.

Evento Dispara Um uso real
SessionStart A sessão começa ou é retomada Injetar a branch do git e issues abertas como contexto
Setup --init-only, ou --init/--maintenance no modo -p Instalar dependências no CI antes de o agente rodar
UserPromptSubmit Você envia um prompt, antes de o Claude processá-lo Anexar a data atual; rejeitar prompts contendo segredos
UserPromptExpansion Um comando digitado se expande em um prompt Auditar ou vetar expansões de skills/comandos
PreToolUse Antes de uma chamada de ferramenta executar Bloquear comandos shell destrutivos
PermissionRequest Um diálogo de permissão aparece Aprovar automaticamente comandos confiáveis para você não ser interrompido
PermissionDenied O classificador do modo automático nega uma chamada de ferramenta Retornar retry: true para o modelo poder tentar de novo
PostToolUse Depois de uma chamada de ferramenta ter sucesso Formatar automaticamente cada arquivo editado
PostToolUseFailure Depois de uma chamada de ferramenta falhar Registrar comandos que falham para triagem
PostToolBatch Depois de um lote de chamadas de ferramenta paralelas, antes da próxima chamada ao modelo Criar um checkpoint ou interromper o loop agêntico
Notification O Claude Code envia uma notificação Alerta no desktop quando o Claude precisa de input
MessageDisplay Enquanto o texto da mensagem do assistente é exibido Censurar conteúdo na tela (apenas exibição; transcript inalterado)
SubagentStart Um subagente é criado 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 escopo de tarefas
TaskCompleted Uma tarefa é marcada como concluída Verificar critérios de aceitação antes que a conclusão seja efetivada
Stop O Claude termina de responder Gate de conclusão: bloquear a finalização até os testes passarem
StopFailure O turno termina por causa de um erro de API Alertar sobre rate_limit ou billing_error (apenas log; saída ignorada)
TeammateIdle Um colega de um time de agentes está prestes a ficar ocioso Manter os colegas trabalhando em uma fila de tarefas
InstructionsLoaded Um arquivo CLAUDE.md ou .claude/rules/*.md é carregado 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 nas configurações
CwdChanged O diretório de trabalho muda Recarregar ambientes no estilo direnv
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 worktree do git
WorktreeRemove Um worktree é removido Limpeza personalizada na saída da sessão ou do subagente
PreCompact Antes da compactação de contexto Salvar estado que você não pode se dar ao luxo de perder
PostCompact Depois que a compactação termina Reinjetar contexto crítico
Elicitation Um servidor MCP solicita input do usuário Preencher formulários automaticamente em execuções headless
ElicitationResult Depois que você responde a uma elicitação MCP Validar ou substituir a resposta antes de ela retornar
SessionEnd A sessão termina Arquivar logs, desmontar recursos

Você não vai precisar da maioria deles. Quase toda configuração de produção é construída a partir de cinco: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart e Stop. Os demais existem para o dia em que você precisar deles.

O contrato: JSON na entrada, exit codes ou JSON na saída

Hooks de comando recebem JSON no stdin e respondem por meio de exit codes, stdout e stderr. (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 — além de campos específicos do evento. Um hook de 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 a parte final: UserPromptSubmit carrega prompt, SessionStart carrega source (startup/resume/clear/compact), Stop carrega stop_hook_active e last_assistant_message. Hooks disparados dentro de subagentes recebem adicionalmente agent_id e agent_type.2

Exit codes

Três desfechos:2

  • Exit 0 — sucesso. O Claude Code analisa o stdout em busca de campos de saída JSON. Para a maioria dos eventos, o stdout vai apenas para o log de debug; para UserPromptSubmit, UserPromptExpansion e SessionStart, o stdout simples é adicionado como contexto que o Claude pode ver.
  • Exit 2 — erro bloqueante. O stdout (incluindo qualquer JSON) é ignorado; o stderr é devolvido ao Claude como mensagem de erro. O que “bloquear” significa depende do evento.
  • Qualquer outro exit code — erro não bloqueante. O transcript mostra um aviso <hook name> hook error, e a execução continua.

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

O que exit 2 faz, por evento:2

Evento Efeito do exit 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 Reverte 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 loop agêntico antes da próxima chamada ao modelo
Elicitation / ElicitationResult Nega a elicitação / transforma a resposta em uma recusa
WorktreeCreate Qualquer exit diferente de zero aborta a criação do worktree

Todo o resto não pode 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; StopFailure, InstructionsLoaded, MessageDisplay e PermissionDenied ignoram o exit code — para PermissionDenied, a única alavanca é o JSON retry: true.2

Saída JSON

Para um controle mais fino do que bloquear-ou-silenciar, faça exit 0 e imprima um objeto JSON no stdout. Uma regra de antemão: exit codes ou JSON, nunca os dois — o JSON só é processado no exit 0, e o exit 2 o descarta.5

Campos universais funcionam em todos os eventos: continue: false para o Claude completamente (com stopReason mostrado ao usuário), suppressOutput esconde o stdout do transcript, systemMessage mostra um aviso ao usuário, e terminalSequence emite uma sequência de escape de terminal da lista de permitidas (notificação no desktop, título da janela, campainha). Os campos de decisão são 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 (mostrado ao Claude). Omita decision para permitir
PreToolUse hookSpecificOutput permissionDecision: "allow" | "deny" | "ask" | "defer", mais permissionDecisionReason e updatedInput para reescrever os argumentos da ferramenta antes da execução
PermissionRequest hookSpecificOutput decision.behavior: "allow" | "deny", 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: feedback sem erro que continua a conversa sem contar como erro de hook
SessionStart, Setup, SubagentStart Apenas contexto additionalContext, mais os campos exclusivos de SessionStart initialUserMessage, sessionTitle, watchPaths, reloadSkills. Sem bloqueio
MessageDisplay hookSpecificOutput displayContent substitui apenas o texto na tela
Elicitation / ElicitationResult hookSpecificOutput action: "accept" | "decline" | "cancel", mais content
WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged Nenhum Apenas efeitos colaterais

Dois detalhes que pegam as pessoas. Primeiro, PreToolUse é a exceção ao padrão de decision no nível superior: historicamente ele usava decision/reason no nível superior, mas esses campos estão deprecados para este evento ("approve"/"block" mapeiam para "allow"/"deny"); use hookSpecificOutput.permissionDecision.5 Segundo, quando vários hooks de PreToolUse discordam, a precedência é deny > defer > ask > allow.5

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

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

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

Onde você coloca isso determina o escopo: ~/.claude/settings.json se aplica a todos os seus projetos, .claude/settings.json tem escopo de projeto e pode ser commitado, .claude/settings.local.json tem escopo de projeto e fica no gitignore, e a precedência padrão de configurações se aplica — política gerenciada sobre local, sobre projeto, sobre usuário.9 Hooks também podem vir em plugins (hooks/hooks.json) e no frontmatter de skills ou agentes, e administradores corporativos podem impor hooks gerenciados que os usuários não conseguem sobrescrever.6

Matchers são avaliados pelos seus caracteres: "*", "" ou um matcher omitido corresponde a tudo; um valor contendo apenas letras, dígitos, _, -, espaços, vírgulas e | é uma string exata ou uma lista (Bash, Edit|Write); qualquer outra coisa vira uma regex JavaScript sem âncoras, então Edit.* corresponde tanto a Edit quanto a NotebookEdit — ancore com ^Edit$ quando quiser exatamente uma ferramenta. Matchers diferenciam maiúsculas de minúsculas, e cada evento faz a correspondência no seu próprio campo: nome da ferramenta para eventos de ferramenta, source para SessionStart, tipo de agente para SubagentStart, tipo de notificação para Notification.6 Para uma filtragem mais precisa em eventos de ferramenta, o campo if por handler aceita uma regra de permissão como "Bash(git *)" — mas ele é best-effort (falha aberto em comandos que não consegue analisar), então use regras de permissão, não if, para garantias rígidas.6

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

Verifique com /hooks: um navegador somente leitura mostrando cada evento, seus hooks configurados e de qual arquivo de configurações 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 ao toolchain do iOS.

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

Direto do guia oficial — todo arquivo que o Claude toca é 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 a sua stack exigir.

2. Bloquear comandos perigosos (PreToolUse, exit 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

Exit 2 bloqueia a chamada e devolve o stderr ao Claude, que ajusta 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 o humano) ou updatedInput (reescrever o comando).5

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

O stdout simples de um hook de SessionStart vira contexto que o Claude pode ver — 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

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

4. Um gate de conclusão no Stop

O Stop dispara quando o Claude termina de responder. Bloqueá-lo força o agente a continuar trabalhando até uma condição ser satisfeita:

#!/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 verificação de stop_hook_active importa: o Claude Code impõe um limite rígido de 8 bloqueios consecutivos para um hook de Stop, e um gate que nunca verifica se já disparou uma continuação vai queimar todos eles de uma vez.7 Para um direcionamento mais suave, retorne hookSpecificOutput.additionalContext em vez de decision: "block" — a mesma continuação, mas como feedback rotulado em vez de um erro de hook. E para condições pontuais, o comando embutido /goal é um hook de Stop baseado em prompt, com escopo de sessão e zero configuração.1

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

Registrar dez hooks significa dez entradas no settings.json que se desalinham 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

Adicionar um guarda agora é um chmod +x em um arquivo novo em .claude/hooks/PreToolUse/ — o settings.json nunca muda, cada script permanece pequeno o suficiente para ser testado isoladamente, e o primeiro exit 2 se propaga. Uma ressalva: o dispatcher serializa o que o Claude Code executaria em paralelo, e ele serve melhor para hooks de exit code — um hook que emite JSON deve permanecer independente, já que o stdout precisa conter exatamente um objeto JSON.5

Hook vs CLAUDE.md vs skill vs memória

Quatro mecanismos, quatro funções:

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

O modo de falha corre nas duas direções. Codificar convenções como hooks te dá scripts frágeis impondo coisas que uma frase de orientação resolve bem. Codificar política como prosa no CLAUDE.md te dá um agente que faz force-push na main justamente no dia em que importa. O teste: qual é o custo quando o modelo ignora isso uma vez? Aborrecimento → CLAUDE.md. Incidente → hook.

O que os hooks não podem fazer

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

  • Hooks não podem chamar ferramentas nem slash commands. Hooks de comando falam stdout, stderr e exit codes — nada mais. O contexto retornado via additionalContext é injetado como texto simples.
  • PostToolUse não pode desfazer. A ferramenta já rodou. A prevenção mora no PreToolUse.
  • Stop dispara no fim de toda resposta, não apenas em “tarefa concluída”, e nunca em interrupções do usuário (erros de API disparam StopFailure no lugar). A lógica do gate precisa tolerar paradas no meio da tarefa.
  • PermissionRequest não dispara no modo headless (-p). Use PreToolUse para decisões de permissão automatizadas.
  • PreToolUse não vê arquivos referenciados com @. Arquivos puxados via @ no seu prompt não envolvem chamada de ferramenta; use regras de negação de Read para proteger caminhos por essa rota.1
  • updatedInput em paralelo é não determinístico. Quando vários hooks de PreToolUse reescrevem os argumentos da mesma ferramenta, o último a terminar vence. Deixe um único hook ser dono de cada reescrita.
  • Timeouts cancelam o hook. 600 segundos por padrão para hooks de comando (30 para UserPromptSubmit, 10 para MessageDisplay); um gate lento que estoura o tempo é um gate que não rodou.
  • A saída é limitada a 10.000 caracteres — o excedente é gravado em um arquivo e substituído por uma prévia.
  • Hooks rodam com todas as permissões do seu usuário. O alerta da própria referência: eles “podem modificar, excluir ou acessar quaisquer arquivos que a sua conta de usuário pode acessar. Revise e teste todos os comandos de hook antes de adicioná-los à sua configuração.”8 Coloque aspas nas suas variáveis, use caminhos absolutos, evite arquivos sensíveis.
  • Um hook quebrado degrada todas as sessões até ser corrigido. Depure com a visualização do transcript (Ctrl+O), claude --debug-file /tmp/claude.log ou /debug no meio da sessão; uma pegadinha clássica é um perfil de shell que ecoa algo na inicialização e corrompe a saída JSON do seu hook.7

FAQ

O que são os hooks do Claude Code?

Hooks são comandos definidos pelo usuário — scripts shell, endpoints HTTP, ferramentas MCP ou prompts de 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 exit codes ou JSON: bloqueiam uma chamada de ferramenta, injetam contexto, reescrevem argumentos, mantêm o agente trabalhando. Diferentemente das instruções do CLAUDE.md, eles executam todas as vezes, independentemente do comportamento do modelo.

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

Regras de permissão são declarativas: padrões estáticos de allow/deny/ask que o próprio Claude Code avalia. Hooks de PreToolUse são programáveis: o seu código inspeciona a entrada completa da ferramenta e decide. Hooks disparam antes das verificações de modo de permissão, então o "deny" de um hook vale mesmo no modo bypassPermissions — mas o "allow" de um hook não pode sobrescrever uma regra de negação das configurações.4 Use regras de permissão para tudo que um padrão consegue expressar; recorra a um hook quando a decisão precisa de lógica, estado externo ou reescrita de entrada.

Hooks funcionam no modo headless (-p)?

Sim — com uma exceção documentada: hooks de PermissionRequest não disparam no modo não interativo, então decisões de permissão automatizadas pertencem ao PreToolUse.7 O modo headless também desbloqueia uma opção que sessões interativas ignoram: permissionDecision: "defer", que pausa uma chamada de ferramenta para que um processo que encapsula a sessão (um app do Agent SDK, uma UI personalizada) possa coletar input e retomar a sessão depois.5

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

Quase sempre é uma violação do contrato. Exit 1 não bloqueia — só exit 2 bloqueia, e apenas nos eventos que suportam bloqueio.2 Decisões em JSON só são analisadas no exit 0 — um script que imprime {"decision": "block"} e depois faz exit 2 tem seu JSON descartado. E matchers diferenciam maiúsculas de minúsculas — bash nunca corresponde a Bash. Confirme o registro com /hooks e depois teste passando um JSON de exemplo via pipe para o script e verificando echo $?.7

Fontes

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

Relacionado neste site: a seção de hooks do guia de Claude Code para a visão completa do sistema, incluindo hooks de prompt e de agente, o tutorial de hooks com cinco construções de produção e configurações completas, Hooks para desenvolvimento Apple com os padrões aplicados ao iOS, e o quickstart se você ainda não instalou 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, the /hooks menu.” 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 na China

Codex CLI vs Claude Code: sandbox no kernel vs governança com 26 hooks, benchmarks Opus 4.7 vs GPT-5.4, preços por token…

29 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