Pular para o conteúdo principal
Para um guia de início rápido com exemplos, consulte Automatizar ações com hooks.
Hooks são comandos shell definidos pelo usuário, endpoints HTTP, chamadas de ferramentas MCP, prompts LLM ou subagentos que executam automaticamente em pontos específicos do ciclo de vida do Claude Code. O Claude Code dispara os mesmos eventos de hook onde quer que seja executado: sessões no terminal, extensões de IDE, o aplicativo Desktop e sessões na nuvem. Use esta referência para consultar esquemas de eventos, opções de configuração, formatos de entrada/saída JSON e recursos avançados como hooks assíncronos, hooks HTTP e hooks de ferramentas MCP. Um plugin também pode registrar hooks como funções JavaScript que o Claude Code chama em seu próprio processo, que podem desenhar na interface, bem como agir sobre eventos. Um plugin que faz isso é um mod, e esses hooks de função são cobertos em Reagir a eventos em vez de aqui. Os hooks nesta página continuam funcionando junto com mods.

Ciclo de vida do hook

Claude Code executa hooks em pontos específicos durante uma sessão. Quando um evento dispara e um matcher corresponde, Claude Code passa contexto JSON sobre o evento para seu manipulador de hook. Para hooks de comando, a entrada chega em stdin. Para hooks HTTP, chega como corpo da solicitação POST. Seu manipulador pode então inspecionar a entrada, tomar ação e opcionalmente retornar uma decisão. Os eventos caem em três cadências:
  • por sessão: SessionStart e SessionEnd
  • por turno: UserPromptSubmit, Stop e StopFailure
  • em cada chamada de ferramenta dentro do loop agentic: PreToolUse e PostToolUse, exceto chamadas EndConversation, que pulam ambas
Diagrama do ciclo de vida do hook mostrando Setup opcional alimentando SessionStart, depois um loop por turno contendo UserPromptSubmit, UserPromptExpansion para slash commands, o loop agentic aninhado (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), e Stop ou StopFailure, seguido por TeammateIdle, PreCompact, PostCompact e SessionEnd, com Elicitation e ElicitationResult aninhados dentro da execução de ferramenta MCP, PermissionDenied como um ramo lateral de PermissionRequest para negações em modo automático, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged e DirectoryAdded como eventos assíncronos independentes, PreModelSwitch como um evento sequencial independente que é executado antes de uma mudança de modelo solicitada, PostModelSwitch como um evento assíncrono independente que é executado após as mudanças de modelo da sessão, e MessageDisplay como um evento somente de exibição que é executado enquanto o texto da mensagem do assistente é transmitidoDiagrama do ciclo de vida do hook mostrando Setup opcional alimentando SessionStart, depois um loop por turno contendo UserPromptSubmit, UserPromptExpansion para slash commands, o loop agentic aninhado (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), e Stop ou StopFailure, seguido por TeammateIdle, PreCompact, PostCompact e SessionEnd, com Elicitation e ElicitationResult aninhados dentro da execução de ferramenta MCP, PermissionDenied como um ramo lateral de PermissionRequest para negações em modo automático, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged e DirectoryAdded como eventos assíncronos independentes, PreModelSwitch como um evento sequencial independente que é executado antes de uma mudança de modelo solicitada, PostModelSwitch como um evento assíncrono independente que é executado após as mudanças de modelo da sessão, e MessageDisplay como um evento somente de exibição que é executado enquanto o texto da mensagem do assistente é transmitido
A tabela abaixo resume quando cada evento dispara. A seção Eventos de hook documenta o esquema de entrada completo e as opções de controle de decisão para cada um.

Como um hook é resolvido

Para ver como o evento, o matcher e o manipulador se encaixam, considere este hook PreToolUse que bloqueia comandos shell destrutivos.
O matcher se restringe a chamadas de ferramenta Bash e a comandos Monitor, e a condição if se restringe ainda mais a subcomandos Bash correspondendo a rm *, então block-rm.sh apenas é gerado quando ambos os filtros correspondem:
O script lê a entrada JSON de stdin, extrai o comando e retorna uma permissionDecision de "deny" se contiver rm -rf. Salve-o em .claude/hooks/block-rm.sh em seu projeto e torne-o executável com chmod +x .claude/hooks/block-rm.sh para que Claude Code possa executá-lo:
Este script, como os outros exemplos Bash nesta página que analisam entrada JSON, usa jq, então instale jq e certifique-se de que está em seu PATH antes de tentar executá-los.
Agora suponha que Claude Code decida executar Bash "rm -rf /tmp/build" contra a configuração macOS/Linux. Aqui está o que acontece:
Diagrama de resolução de hook: PreToolUse dispara, o matcher verifica correspondência de Bash, então a condição if verifica correspondência de Bash(rm *). Se ambos corresponderem, o comando do hook é executado e retorna permissionDecision deny, então a chamada da ferramenta é bloqueada e Claude Code continua. Se qualquer verificação falhar em corresponder, o hook é ignorado e a chamada da ferramenta é permitida prosseguir.Diagrama de resolução de hook: PreToolUse dispara, o matcher verifica correspondência de Bash, então a condição if verifica correspondência de Bash(rm *). Se ambos corresponderem, o comando do hook é executado e retorna permissionDecision deny, então a chamada da ferramenta é bloqueada e Claude Code continua. Se qualquer verificação falhar em corresponder, o hook é ignorado e a chamada da ferramenta é permitida prosseguir.
1

Evento dispara

O evento PreToolUse dispara. Claude Code envia a entrada da ferramenta como JSON em stdin para o hook:
2

Matcher verifica

O matcher "Bash" corresponde ao nome da ferramenta, então este grupo de hook é ativado. Se você omitir o matcher ou usar "*", o grupo é ativado em cada ocorrência do evento.
3

Condição if verifica

A condição if "Bash(rm *)" corresponde porque rm -rf /tmp/build é um subcomando correspondendo a rm *, então este manipulador é gerado. Se o comando tivesse sido npm test, a verificação if falharia e block-rm.sh nunca seria executado, evitando a sobrecarga de geração de processo. O campo if é opcional; sem ele, cada manipulador no grupo correspondido é executado.
4

Manipulador de hook executa

O script inspeciona o comando completo e encontra rm -rf, então imprime uma decisão em stdout:
Se o comando tivesse sido uma variante mais segura de rm como rm file.txt, o script teria atingido exit 0 em vez disso. Código de saída 0 sem saída significa que o hook não tem decisão a relatar, então a chamada da ferramenta continua através do fluxo de permissão normal. O hook pode negar a chamada, mas ficar em silêncio não a aprova.
5

Claude Code age sobre o resultado

Claude Code lê a decisão JSON, bloqueia a chamada da ferramenta e mostra a razão ao Claude.
A seção Configuração abaixo documenta o esquema completo, e cada seção evento de hook documenta qual entrada seu comando recebe e qual saída pode retornar.

Configuração

Hooks são definidos em arquivos de configurações JSON. A configuração tem três níveis de aninhamento:
  1. Escolha um evento de hook para responder, como PreToolUse ou Stop
  2. Adicione um grupo de matcher para filtrar quando ele é acionado, como “para a ferramenta Bash”
  3. Defina um ou mais manipuladores de hook para executar quando correspondido
Consulte Como um hook é resolvido acima para um passo a passo completo com um exemplo anotado.
Esta página usa termos específicos para cada nível: evento de hook para o ponto do ciclo de vida, grupo de matcher para o filtro e manipulador de hook para o comando shell, endpoint HTTP, ferramenta MCP, prompt ou agente que executa. “Hook” por si só refere-se ao recurso geral.

Locais de hooks

Onde você define um hook determina seu escopo: Sessões na nuvem não leem seu ~/.claude/settings.json local. Em um ambiente auto-hospedado, Claude Code também executa os hooks que o operador propagou do ~/.claude/ do host do runner, e executa os hooks no arquivo de configurações gerenciadas da imagem do runner quando esse arquivo está entre as fontes gerenciadas que Claude Code aplica, o que por padrão significa apenas quando nem configurações gerenciadas pelo servidor nem uma política Claude Code entregue por MDM fornece o nível gerenciado. Consulte o que é transferido da sua configuração para saber quais arquivos de configurações e plugins, e portanto quais hooks, chegam a uma sessão na nuvem. Para detalhes sobre resolução de arquivo de configurações, consulte settings. Hooks de arquivos de configurações, configurações de política gerenciada e plugins também executam dentro de subagentes. Quando um subagente chama uma ferramenta, eventos de ferramenta como PreToolUse e PostToolUse disparam os mesmos hooks configurados que na conversa principal, e a entrada carrega os campos de entrada comuns agent_id e agent_type que identificam o subagente. Administradores podem usar allowManagedHooksOnly em configurações gerenciadas para restringir quais hooks executam:
  • Seus hooks de usuário, projeto, local e plugin são bloqueados. Hooks de plugins forçadamente ativados em configurações gerenciadas enabledPlugins são isentos
  • Claude Code também restringe suas configurações statusLine e fileSuggestion às configurações gerenciadas
  • Claude Code também desabilita plugins com uma fonte command, incluindo plugins forçadamente ativados em configurações gerenciadas enabledPlugins, a menos que disableCommandPluginSources seja explicitamente definido como false. Fontes command requerem Claude Code v2.1.229 ou posterior
  • Claude Code também bloqueia comandos headersHelper do marketplace a menos que disableCommandPluginSources seja explicitamente definido como false, exceto para um marketplace que as próprias configurações gerenciadas declarem
Consulte o que executa sob allowManagedHooksOnly. Entradas de hook se mesclam entre níveis de configurações em vez de se substituírem: configurações de usuário, projeto e local adicionam seus próprios hooks sem remover os gerenciados, e a configuração disableAllHooks não pode desabilitar hooks gerenciados de fora das configurações gerenciadas. As listas de permissões de hook HTTP se aplicam a hooks de todas as fontes, incluindo configurações de política gerenciada:
  • allowedHttpHookUrls: quando definido em qualquer nível de configurações, Claude Code executa um manipulador de hook HTTP apenas se sua URL corresponder à lista de permissões mesclada
  • httpHookAllowedEnvVars: quando definido, Claude Code interpola apenas as variáveis de ambiente nessa lista em cabeçalhos de hook

Padrões de matcher

O campo matcher filtra quando hooks disparam. Como um matcher é avaliado depende dos caracteres que contém: Um matcher no caminho de expressão regular é testado com RegExp.prototype.test do JavaScript, que sucede em uma correspondência em qualquer lugar no valor. Edit.* corresponde tanto a Edit quanto a NotebookEdit; envolva o padrão em ^ e $, como em ^Edit$, quando você precisa de uma correspondência de string inteira. FileChanged e StopFailure usam um conjunto de correspondência exata mais estreito de apenas letras, dígitos, _ e |. Um hífen, espaço ou vírgula em um matcher para esses dois eventos o mantém no caminho de expressão regular, e apenas | separa alternativas. Todos os outros eventos com suporte a matcher na tabela a seguir aceitam | ou ,. O evento FileChanged não segue essas regras ao construir sua lista de monitoramento. Consulte FileChanged. Cada tipo de evento corresponde em um campo diferente: Corresponder StopFailure em cloud_credential_error requer Claude Code v2.1.267 ou posterior, a primeira versão que relata falhas de carregamento de credenciais sob esse valor em vez de server_error ou unknown. Para a maioria dos eventos, Claude Code avalia o matcher contra um campo da entrada JSON que envia para seu hook em stdin. Para eventos de ferramenta, esse campo é tool_name. Para PreModelSwitch e PostModelSwitch, Claude Code avalia o matcher contra o nome canônico que deriva de to_model, conforme descrito em PreModelSwitch. Cada seção evento de hook lista o conjunto completo de valores de matcher e o esquema de entrada para esse evento. Este exemplo executa um script de linting apenas quando Claude escreve ou edita um arquivo:
Se você adicionar um campo matcher a um evento sem suporte a matcher, ele é silenciosamente ignorado. Para eventos de ferramenta, você pode filtrar mais estreitamente definindo o campo if em manipuladores de hook individuais. if usa sintaxe de regra de permissão para corresponder contra o nome da ferramenta e argumentos juntos, então "Bash(git *)" executa quando qualquer subcomando da entrada Bash corresponde a git * e "Edit(*.ts)" executa apenas para arquivos TypeScript.

Corresponder ferramentas MCP

Ferramentas de servidor MCP aparecem como ferramentas regulares em eventos de ferramenta (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), então você pode corresponder a elas da mesma forma que corresponde a qualquer outro nome de ferramenta. Ferramentas MCP seguem o padrão de nomenclatura mcp__<server>__<tool>, por exemplo:
  • mcp__memory__create_entities: ferramenta create entities do servidor Memory
  • mcp__filesystem__read_file: ferramenta read file do servidor Filesystem
  • mcp__github__search_repositories: ferramenta search do servidor GitHub
Para corresponder a cada ferramenta de um servidor, anexe .* ao prefixo do servidor. O .* é obrigatório: um matcher como mcp__memory ou mcp__brave-search contém apenas caracteres de correspondência exata, então é comparado como uma string exata e não corresponde a nenhuma ferramenta.
  • mcp__memory__.* corresponde a todas as ferramentas do servidor memory
  • mcp__brave-search__.* corresponde a todas as ferramentas de um servidor cujo nome contém um hífen
  • mcp__.*__write.* corresponde a qualquer ferramenta cujo nome começa com write de qualquer servidor
Ferramentas de um servidor MCP fornecido por plugin usam um segmento de servidor com escopo que inclui o nome do plugin: mcp__plugin_<plugin-name>_<server-name>__<tool>. Um matcher escrito contra a chave do servidor simples nunca dispara para essas ferramentas. Para um plugin nomeado my-plugin que agrupa um servidor sob a chave db, uma ferramenta query aparece como mcp__plugin_my-plugin_db__query, então o matcher para cada ferramenta daquele servidor é mcp__plugin_my-plugin_db__.*. Use o mesmo nome de ferramenta com escopo no campo if de um manipulador. Consulte Servidores MCP fornecidos por plugin para saber como o nome com escopo é construído. Este exemplo registra todas as operações do servidor memory e valida operações de escrita de qualquer servidor MCP:

Corresponder comandos Monitor

Um hook PreToolUse ou PermissionRequest com um matcher Bash também executa em comandos shell que Claude inicia com a ferramenta Monitor, mas seu allow e seu updatedInput não têm efeito sobre eles. Requer Claude Code v2.1.314 ou posterior. Matchers como Bash|PowerShell, ^Bash$ e Bash.* também são matchers Bash. Um hook com um matcher Bash não executa para um WebSocket watch, e um hook PostToolUse com esse matcher não executa para Monitor. Um hook que executa para um comando Monitor pode negá-lo ou fazer com que Claude Code pergunte a você sobre ele. Para permitir ou reescrever o comando, troque o matcher Bash por um que inclua Monitor:
  • Matcher que inclui Monitor: com um matcher como Bash|Monitor ou .*, ou sem matcher, o hook recebe a própria entrada da chamada Monitor, e allow e updatedInput se aplicam. Em if, nomeie Monitor, como em Monitor(tail *), que corresponde pelas regras que Bash(tail *) segue. Se o seu if nomear Bash, o hook ainda recebe a entrada da chamada Monitor, mas allow e updatedInput não têm efeito
  • Matcher Bash: o hook recebe uma chamada Bash. Em if, nomeie Bash, como em Bash(tail *). O hook não executa se o seu if nomear Monitor

Campos do manipulador de hook

Cada objeto no array hooks interno é um manipulador de hook: o comando shell, endpoint HTTP, ferramenta MCP, prompt LLM ou agente que executa quando o matcher corresponde. Existem cinco tipos:
  • Hooks de comando (type: "command"): executam um comando shell. Seu script recebe a entrada JSON do evento em stdin e comunica resultados através de códigos de saída e stdout.
  • Hooks HTTP (type: "http"): enviam a entrada JSON do evento como uma solicitação HTTP POST para uma URL. O endpoint comunica resultados através do corpo da resposta usando o mesmo formato de saída JSON que hooks de comando.
  • Hooks de ferramenta MCP (type: "mcp_tool"): chamam uma ferramenta em um servidor MCP configurado. A saída de texto da ferramenta é tratada como stdout de hook de comando.
  • Hooks de prompt (type: "prompt"): enviam um prompt para um modelo Claude para avaliação de turno único. O modelo retorna sua decisão como JSON. Consulte Hooks baseados em prompt.
  • Hooks de agente (type: "agent"): geram um subagente que pode usar ferramentas como Read, Grep e Glob para verificar condições antes de retornar uma decisão. Hooks de agente são experimentais e podem mudar. Consulte Hooks baseados em agente.
Quando um evento dispara, seus hooks correspondentes executam em paralelo. Se mais de 12 deles forem hooks de prompt ou de agente, 12 desses executam ao mesmo tempo e o restante começa à medida que os que estão em execução terminam. O timeout de cada hook em espera começa a contar quando esse hook inicia. Se você definir o mesmo manipulador em mais de um arquivo de configurações, ele executa uma vez. Uma cópia do mesmo manipulador de um plugin ou skill permanece separada. Manipuladores executam no diretório atual com o ambiente do Claude Code. Se o diretório atual não existir mais, por exemplo um worktree ou diretório temporário que outro shell deletou no meio da sessão, Claude Code executa hooks de comando a partir do primeiro destes que ainda existe: o diretório em que a sessão começou, a raiz do projeto, seu diretório home ou o diretório temporário do sistema. Claude Code registra um aviso nomeando o diretório de fallback no log de debug. Para uma sessão de worktree que você inicia a partir do aplicativo desktop, consulte O que os worktrees compartilham com o checkout principal. A variável de ambiente $CLAUDE_CODE_REMOTE é "true" em ambientes web remotos e não é definida na CLI local. Claude Code v2.1.199 e posterior define $CLAUDE_CODE_BRIDGE_SESSION_ID para o ID de sessão Remote Control enquanto a sessão local tem uma conexão Remote Control ativa.

Campos comuns

Esses campos se aplicam a todos os tipos de hook: O campo if contém exatamente uma regra de permissão. Não há sintaxe &&, || ou lista para combinar regras; para aplicar múltiplas condições, defina um manipulador de hook separado para cada. Em uma condição if para uma ferramenta de arquivo, um padrão de diretório de segmento único como "Edit(src/**)" corresponde apenas ao diretório src no diretório de trabalho e aos arquivos sob ele. Para corresponder a um diretório nomeado src em qualquer profundidade, escreva "Edit(**/src/**)".

Como padrões if correspondem a comandos Bash

Para padrões Bash no campo if, se seu comando de hook executa depende da forma do padrão e do comando Bash que Claude está invocando. Atribuições VAR=value iniciais são removidas antes da correspondência. Quando Claude Code não pode determinar quais comandos a entrada Bash executa, ele executa seu hook independentemente do padrão. Como o filtro if é melhor esforço, use o sistema de permissão em vez de um hook para impor um allow ou deny duro.

Campos de hook de comando

Além dos campos comuns, hooks de comando aceitam esses campos:
Forma exec e forma shell
Um hook de comando executa como forma exec quando args é definido, e forma shell quando args é omitido. Defina args sempre que o hook referenciar um placeholder de caminho, já que cada elemento é passado como um argumento sem aspas. Omita args quando você precisar de recursos de shell como pipes ou &&, ou quando nenhuma preocupação se aplica. Forma exec executa quando args está presente. Claude Code resolve command como um executável em PATH e o gera diretamente com args como o vetor de argumentos. Não há shell, então cada elemento args é um argumento exatamente como escrito, e placeholders de caminho como ${CLAUDE_PLUGIN_ROOT} são substituídos em command e em cada elemento args como strings simples. Caracteres especiais como apóstrofos, $ e backticks passam verbatim porque não há shell para interpretá-los. Nenhuma tokenização de shell acontece em nenhuma plataforma. Forma shell executa quando args está ausente. A string command é passada para um shell: sh -c em macOS e Linux, Git Bash no Windows, ou PowerShell quando Git Bash não está instalado. Defina o campo shell para escolher explicitamente. O shell tokeniza a string, expande variáveis e interpreta pipes, &&, redirecionamentos e globs.
No Windows, a forma exec requer que command seja resolvido para um executável real como .exe. Os shims .cmd e .bat que npm, npx, eslint e outras ferramentas instalam em node_modules/.bin não são executáveis e não podem ser gerados sem um shell. Para executá-los em forma exec, invoque o script subjacente com node diretamente, por exemplo "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. O padrão node mais caminho-de-script funciona em todas as plataformas porque node.exe é um binário real. Para executar um shim .cmd ou .bat por nome, use forma shell.
Este exemplo executa um script Node agrupado com um plugin. A forma exec passa o caminho do script resolvido como um argumento sem aspas:
A forma shell equivalente precisa de aspas para lidar com caminhos com espaços ou caracteres especiais:
Ambas as formas suportam os mesmos placeholders de caminho, e ambas os exportam como as variáveis de ambiente CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT e CLAUDE_PLUGIN_DATA no processo gerado, então um script pode ler process.env.CLAUDE_PLUGIN_ROOT independentemente de como foi lançado. Hooks de plugin adicionalmente substituem valores ${user_config.*}, apenas em forma exec: o valor é substituído em command e em cada elemento args como uma string simples, então nenhum shell o re-analisa. Um hook de plugin em forma shell cujo command referencia ${user_config.*} falha com um erro em vez de executar. Para usar um valor de opção de um hook em forma shell, leia a variável de ambiente $CLAUDE_PLUGIN_OPTION_<KEY>, como $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL para uma opção webhook_url, ou defina args para mudar o hook para forma exec. Antes de v2.1.207, comandos de hook de plugin em forma shell também substituíam ${user_config.*}.
Em forma exec, command é apenas o nome ou caminho do executável. Se command é um nome simples sem separador de caminho e contém espaço em branco junto com args, Claude Code registra um aviso porque o spawn falhará: não há executável nomeado node script.js. Mova os tokens extras para args. Caminhos absolutos com espaços, como C:\Program Files\nodejs\node.exe, são um executável válido único e não disparam o aviso.

Campos de hook HTTP

Além dos campos comuns, hooks HTTP aceitam esses campos: Claude Code envia a entrada JSON do hook como corpo da solicitação POST com Content-Type: application/json. O corpo da resposta usa o mesmo formato de saída JSON que hooks de comando. O tratamento de erros difere dos hooks de comando; consulte Tratamento de resposta HTTP. Este exemplo envia eventos PreToolUse para um serviço de validação local, autenticando com um token da variável de ambiente MY_TOKEN:

Campos de hook de ferramenta MCP

Além dos campos comuns, hooks de ferramenta MCP aceitam esses campos: Este exemplo chama a ferramenta security_scan no servidor MCP my_server após cada Write ou Edit, passando o caminho do arquivo editado:
Como o resultado da ferramenta é lido
Claude Code lê o conteúdo de texto da ferramenta da mesma forma que lê stdout de hook de comando, seguindo a regra de análise sob código de saída 0. Se a ferramenta retornar isError: true, o hook produz um erro não-bloqueador e a execução continua. Se um erro de hook contiver uma destas mensagens, Claude Code não conseguiu ler a resposta da ferramenta, então o hook não deu nenhum veredito:
Em PreToolUse, Claude Code recusa a chamada de ferramenta que o hook estava verificando. Em outros eventos, como UserPromptSubmit, PostToolUse e Stop, o erro é não-bloqueador. Se o erro continuar aparecendo, retorne menos dados da ferramenta ou altere o tool ou input do hook para que a resposta seja menor.
Quando o servidor ainda está se conectando
Em eventos onde um hook pode bloquear ou mudar o resultado, como PreToolUse ou Stop, Claude Code aguarda um servidor em conexão antes de chamar a ferramenta, por no máximo MCP_TIMEOUT e dentro do timeout do próprio hook. Em eventos observacionais, como Notification ou SessionEnd, ele não aguarda. Um servidor mostrando o status cached se conecta quando o hook chama sua ferramenta. Se o servidor não estiver conectado naquele ponto, o hook produz um erro não-bloqueador e a execução continua. O hook nunca inicia um fluxo OAuth, então autentique o servidor de /mcp primeiro.
Eventos que disparam antes dos servidores MCP estarem disponíveis
SessionStart no lançamento, incluindo com --continue ou --resume, e cada evento Setup disparam antes dos servidores MCP da sessão estarem disponíveis para hooks. Claude Code pula seus hooks mcp_tool sem chamar a ferramenta, e o log de debug registra mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), ou a mesma mensagem nomeando Setup. Quando SessionStart dispara novamente mais tarde na sessão, após /clear ou uma compactação, seus hooks mcp_tool executam. Para qualquer coisa que a sessão precise no lançamento, use um hook type: "command" em SessionStart em vez disso.

Campos de hook de prompt e agente

Além dos campos comuns, hooks de prompt e agente aceitam esses campos:

Referenciar scripts por caminho

Use esses placeholders para referenciar scripts de hook relativos à raiz do projeto ou plugin, independentemente do diretório de trabalho quando o hook executa:
  • ${CLAUDE_PROJECT_DIR}: a raiz do projeto onde a sessão começou. Claude Code também define essa variável no ambiente de servidores MCP stdio e servidores LSP de plugin.
  • ${CLAUDE_PLUGIN_ROOT}: o diretório de instalação do plugin, para scripts agrupados com um plugin. Consulte variáveis de ambiente de plugin para saber como o caminho se comporta entre atualizações.
  • ${CLAUDE_PLUGIN_DATA}: o diretório de dados persistentes do plugin, para dependências e estado que devem sobreviver a atualizações de plugin.
Worktrees são diferentes. Se Claude entra em uma worktree durante a sessão, Claude Code mantém ${CLAUDE_PROJECT_DIR} onde estava e passa o caminho da worktree para seus hooks de uma forma diferente:
  • ${CLAUDE_PROJECT_DIR} fica no lugar: ainda aponta para a raiz do projeto onde a sessão começou, então um comando como ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh ainda executa o script no checkout principal.
  • cwd segue Claude: o campo cwd na entrada JSON do hook é a raiz do worktree após Claude entrar em um worktree, e o novo diretório após Claude executar cd. Leia-o quando um hook precisa saber em qual diretório Claude está trabalhando.
Para uma sessão de worktree que você inicia a partir do aplicativo desktop, consulte O que os worktrees compartilham com o checkout principal para saber para onde ${CLAUDE_PROJECT_DIR} aponta.
Prefira forma exec para qualquer hook que referencie um placeholder de caminho. Em forma shell, envolva cada placeholder em aspas duplas.
Este exemplo usa ${CLAUDE_PROJECT_DIR} para executar um verificador de estilo do diretório .claude/hooks/ do projeto após qualquer chamada de ferramenta Write ou Edit:

Hooks em skills e agentes

Além de arquivos de configurações e plugins, hooks podem ser definidos diretamente em skills e subagentes usando frontmatter, no mesmo formato de configuração que hooks baseados em configurações. Por quanto tempo Claude Code os mantém registrados depende do componente:
  • Hooks de subagente: Claude Code os executa apenas enquanto esse subagente está em execução e os remove quando termina. Claude Code converte um hook Stop aqui para SubagentStop, o evento que dispara quando um subagente completa.
  • Hooks de skill: Claude Code os registra quando você ou Claude invoca a skill e continua executando-os pelo resto da sessão, em turnos após o próprio turno da skill também. Para fazer Claude Code remover um hook após sua primeira execução bem-sucedida em vez disso, defina once: true nele.
Esta skill define um hook PreToolUse que executa um script de validação de segurança antes de cada comando Bash:
Subagentes usam o mesmo formato em seu frontmatter YAML. Hooks de frontmatter em uma skill de projeto seguem a mesma regra de confiança de workspace que hooks em arquivos de configurações. Claude Code os registra quando você ou Claude invoca a skill, incluindo em uma execução -p em uma pasta que você não confiou. Hooks de frontmatter em um subagente de projeto executam apenas após você aceitar o diálogo de confiança de workspace para a pasta de onde o arquivo do agente veio. Uma sessão -p não conta como aceitá-lo. O que executa antes de você confiar em uma pasta compara isso com a regra de arquivo de configurações, e a página de subagentes lista quais escopos estão isentos.

Limites de hooks de skill

Quando os hooks de frontmatter de uma skill não disparam depois que você ou Claude invoca a skill, execute /hooks nessa sessão. Se ele os listar, consulte Hook não dispara. Se ele não listar nenhum deles, a skill pode estar sob um bloqueio exclusivo para plugins ou ter ultrapassado um limite de hooks, e executa sem nenhum de seus hooks de frontmatter. Verifique cada caso:
  • Bloqueio exclusivo para plugins: o administrador da sua organização pode restringir hooks a plugins e configurações gerenciadas com a configuração gerenciada strictPluginOnlyCustomization. Uma skill mantém seus hooks de frontmatter sob esse bloqueio apenas se for uma skill de plugin, uma skill incluída ou uma skill no diretório de configurações gerenciadas. Os hooks nos seus próprios arquivos de configurações também não executam.
    • Verificar: digite /status em uma sessão do Claude Code. Se a linha Setting sources incluir Enterprise managed settings, pergunte ao seu administrador se strictPluginOnlyCustomization bloqueia hooks.
    • Corrigir: mova a skill para um plugin, porque skills de plugin mantêm seus hooks de frontmatter. Liste o plugin em um marketplace e depois instale-o a partir dele. Se Claude Code recusar o marketplace com is blocked by enterprise policy, peça ao seu administrador para permitir esse marketplace.
  • Limite de hooks: no Claude Code v2.1.304 ou posterior, cada fonte de skills tem seu próprio limite por sessão. Seu projeto, sua pasta de skills pessoais e cada plugin contam como fontes separadas. As skills de uma fonte podem registrar no máximo 1.024 hooks. Os matchers desses hooks podem totalizar no máximo 16.384 caracteres. Um matcher com vários hooks sob ele conta uma vez para cada hook.
    • Verificar: repita a sessão em que o hook não disparou, com o log de debug ativado:
      1. Inicie uma nova sessão a partir do seu shell com claude --debug. Se você não inicia sessões a partir de um shell, digite /debug primeiro na nova sessão.
      2. Invoque as mesmas skills na mesma ordem, incluindo as que Claude invocou, porque ambos os totais se acumulam skill por skill. Peça ao Claude para invocar qualquer skill que você não possa digitar como comando.
      3. Procure registers none of its hooks no log de debug. Uma linha contendo esse texto nomeia a skill que ultrapassou o limite.
    • Corrigir: declare menos hooks ou matchers mais curtos nessa skill ou nas outras skills da mesma fonte e, em seguida, inicie uma nova sessão. Se essas skills não forem suas para editar, peça essa mudança ao autor delas. Até lá, inicie uma nova sessão e invoque a skill de que você precisa antes de qualquer outra skill da mesma fonte.
Se nenhum dos casos for o seu, verifique se há frontmatter que não foi analisado.

O menu /hooks

Digite /hooks no Claude Code para abrir um navegador somente leitura para seus hooks configurados. A lista rotula cada hook com sua origem, como configurações de usuário, configurações de projeto, configurações locais, um plugin ou a sessão atual. Selecione um hook para ver o texto completo do que ele executa e onde está definido, como o caminho do seu arquivo de configurações ou o nome do seu plugin. Para navegar por todos os eventos de hook, incluindo aqueles sem hooks configurados, selecione All events no final da lista.

Desabilitar ou remover hooks

Para remover um hook definido em um arquivo de configurações, delete sua entrada desse arquivo. Para desabilitar temporariamente todos os hooks sem removê-los, defina "disableAllHooks": true em seu arquivo de configurações. Claude Code lê o valor deixado após precedência de configurações se aplicar, então um "disableAllHooks": false no .claude/settings.json de um projeto substitui um true em suas configurações de usuário. Para desabilitar hooks para uma execução qualquer que as configurações do projeto digam, passe --settings '{"disableAllHooks": true}', que tem precedência sobre configurações de projeto e local. Não há forma de desabilitar um hook individual mantendo-o na configuração. A configuração disableAllHooks respeita a hierarquia de configurações gerenciadas. Se um administrador configurou hooks através de configurações de política gerenciada, disableAllHooks definido em configurações de usuário, projeto ou local não pode desabilitar esses hooks gerenciados. Apenas disableAllHooks definido no nível de configurações gerenciadas pode desabilitar hooks gerenciados. Para o alcance completo de cada nível, consulte disableAllHooks. Edições diretas a hooks em arquivos de configurações são normalmente capturadas automaticamente pelo observador de arquivo.

Entrada e saída de hook

Hooks de comando recebem dados JSON via stdin e comunicam resultados através de códigos de saída, stdout e stderr. Hooks HTTP recebem o mesmo JSON como corpo da requisição POST e comunicam resultados através do corpo da resposta HTTP. Esta seção cobre campos e comportamento comuns a todos os eventos. Cada seção de evento sob Eventos de hook inclui seu esquema de entrada específico e opções de controle de decisão. No macOS e Linux, hooks de comando executam em sua própria sessão sem um terminal controlador. O processo de hook e qualquer processo filho não podem abrir /dev/tty ou enviar sequências de escape diretamente para a interface do Claude Code. Windows não tem /dev/tty. Para exibir uma mensagem ao usuário em qualquer plataforma, retorne systemMessage na saída JSON. Alguns eventos descartam isso ou o entregam em outro lugar, e cada seção de evento diz assim. Para disparar uma notificação de desktop, definir um título de janela ou tocar o sino, retorne terminalSequence em vez disso.

Campos de entrada comuns

Eventos de hook recebem esses campos como JSON, além de campos específicos do evento documentados em cada seção evento de hook. Para hooks de comando, este JSON chega via stdin. Para hooks HTTP, chega como corpo da requisição POST. agent_id e agent_type dizem ao seu script em qual agente um hook disparou, como um subagente, um colega de equipe em processo ou o agente que você escolheu com --agent: Apenas hooks SessionStart podem receber um campo model, e Claude Code nem sempre o inclui. Hooks PreModelSwitch e PostModelSwitch recebem from_model e to_model em vez disso, portanto use um hook PostModelSwitch para acompanhar o modelo conforme ele muda durante uma sessão. Não há variável de ambiente $CLAUDE_MODEL. O hook pode ler $ANTHROPIC_MODEL se você defini-lo em seu shell, mas esse valor não muda quando você alterna modelos com /model durante uma sessão. Um processo de hook herda o ambiente pai, exceto pelas variáveis exportadoras OTEL_* que Claude Code remove de cada subprocesso que spawna e, quando CLAUDE_CODE_SUBPROCESS_ENV_SCRUB é definido como 1, pelas variáveis que ele remove. Em uma sessão que recebe a configuração HIPAA, Claude Code também remove as credenciais da Anthropic do ambiente do hook. Por exemplo, um hook PreToolUse para um comando Bash recebe isso em stdin:
Os campos tool_name, tool_input e tool_use_id são específicos do evento. Cada seção evento de hook documenta os campos adicionais para esse evento.

Saída de código de saída

Quando a execução do seu hook termina, Claude Code usa o código de saída dele para decidir se continua com a ação que o disparou, como uma chamada de ferramenta ou um prompt:
  • Sucesso: seu hook sai com 0. Claude Code aplica quaisquer campos de saída JSON que seu hook imprimiu, e a ação prossegue, a menos que esses campos a bloqueiem ou neguem. Hooks que iniciam um processo em segundo plano cobre a exceção.
  • Erro bloqueador: seu hook sai com 2. Em eventos que podem bloquear, Claude Code interrompe a ação.
  • Erro não-bloqueador: seu hook sai com qualquer outro código, ou falha de alguma outra forma, como não iniciar ou imprimir JSON inválido. A ação prossegue, exceto para hooks cuja falha bloqueia a ação, por exemplo um hook com onFailure definido como "block". Em eventos como PreToolUse, você vê um aviso <hook name> hook error na transcrição.
O que seu hook imprime em stdout pode mudar o resultado. Por exemplo, quando um hook PreToolUse sai com 1 mas imprime JSON que passa na validação, esse JSON ainda pode decidir o que acontece. Para encontrar o resultado do seu hook em um evento como PreToolUse, combine o que ele imprimiu em stdout na primeira coluna com seu código de saída no topo: Alguns eventos têm suas próprias regras:
  • WorktreeCreate: qualquer código de saída diferente de zero faz a criação de worktree falhar, não importa o que seu JSON diga.
  • WorktreeRemove: qualquer código de saída diferente de zero faz a remoção de worktree falhar se o diretório ainda existir depois.
  • Stop, SubagentStop, TaskCompleted e o hook UserPromptSubmit de um plugin: quando seu hook sai com 2 sem nada em stdout e seu stderr diz que um arquivo está ausente, como No such file or directory, Claude Code trata a execução como um erro não-bloqueador.
  • Elicitation e ElicitationResult: Claude Code aplica seu hookSpecificOutput quando seu hook sai com 0, e o ignora em qualquer outro código de saída.
  • Eventos que descartam a saída do hook, como StopFailure: Claude Code ignora seu JSON em qualquer código de saída, exceto campos de efeito colateral como terminalSequence, que ainda disparam.
Para verificar o que o código de saída 2 faz no seu evento, consulte Comportamento de código de saída 2 por evento. Para verificar quais campos de decisão ele honra, consulte Controle de decisão.

Código de saída 0

Saída 0 significa sucesso, e é o código de saída pretendido quando você imprime JSON para controle estruturado. Quando seu hook sai com 0 mas imprime JSON que não pode ser analisado ou falha na validação de esquema, a ação prossegue, a menos que o hook seja um cuja falha bloqueia a ação. Em vez de um sucesso com seus campos aplicados, a execução é um erro não-bloqueador. Para corrigir sua saída, use a mensagem de análise ou validação que o aviso <hook name> hook error carrega. Para um hook que não é async, seu stdout conta como saída JSON apenas quando você imprime um único objeto JSON, em uma linha ou em várias, sem nada ao redor além de espaço em branco. Claude Code lê qualquer outro stdout como texto simples ou uma falha de análise:
  • Saída que não começa com {, ou que começa com { e não termina com }: texto simples. Um array JSON e uma string JSON entre aspas são texto simples por esta regra.
  • Duas ou mais linhas que cada uma analisa como JSON por conta própria, a primeira começando com { e a última terminando com }: texto simples quando nenhuma linha é um objeto de saída JSON que define um campo. Quando uma linha é, a saída é uma falha de análise, portanto imprima esse objeto sem mais nada em stdout.
  • Qualquer outra coisa que começa com { e termina com } mas não é JSON válido: uma falha de análise.
Para a maioria dos eventos, Claude Code escreve stdout no log de debug e não o mostra na transcrição. Nestes eventos, seu stdout chega ao Claude ou ao resumo de compactação:
  • UserPromptSubmit, UserPromptExpansion, SessionStart e PostModelSwitch: Claude Code adiciona stdout em texto simples como contexto que Claude pode ver e agir. Ele não adiciona stdout que não conseguiu analisar.
  • PreCompact: seu stdout se torna instruções para o resumo de compactação
Claude nunca vê stderr de um hook que sai com 0. Para lê-lo você mesmo em eventos como PreToolUse, ative debug logging. Para exibir um aviso para Claude de um hook PostToolUse ou PostToolUseFailure, saia com 2 em vez disso para que Claude veja o stderr mesmo que a ferramenta já tenha executado.

Código de saída 2

Saia com código 2 para bloquear a ação. Em eventos que podem bloquear, Claude Code interrompe a ação: um hook PreToolUse bloqueia a chamada de ferramenta, por exemplo, e um hook UserPromptSubmit rejeita o prompt. A mensagem que acompanha o bloqueio é o stderr do seu hook. Se seu hook também imprimiu JSON que toma uma decisão de bloqueio, Claude Code usa a razão dessa decisão em vez disso. A saída 2 bloqueia mesmo quando seu hook imprime JSON:
  • JSON que passa na validação de esquema: Claude Code ainda lê os campos de saída JSON, mas eles não podem sobrescrever o bloqueio. Nem mesmo um permissionDecision de "allow" deixa a ação passar. Em Elicitation e ElicitationResult, o hookSpecificOutput de um hook exit-2 é ignorado.
  • JSON que falha na validação de esquema: o hook ainda bloqueia. Claude Code usa seu stderr como a razão de bloqueio e registra a falha de validação no log de debug.
Este script bloqueia comandos rm saindo 2 e deixa cada outro comando para o fluxo de permissão normal:
Com este script registrado como um hook PreToolUse em Bash, um comando que começa com rm é bloqueado, e Claude recebe o stderr do hook como o erro da ferramenta, prefixado com o nome do evento, o nome da ferramenta e o comando do hook:

Outros códigos de saída

Um aviso <hook name> hook error com Failed with non-blocking status code: significa que o código de saída do seu hook não foi 0 nem 2 e seu stdout era texto simples ou estava vazio. A menos que o hook seja um cuja falha bloqueia a ação, isso é um erro não-bloqueador, portanto seu hook não interrompeu a ação. Para capturar o stderr completo do seu hook em vez da primeira linha que o aviso mostra, ative debug logging. Por exemplo, quando um hook PreToolUse em Bash imprime something broke em stderr e sai com 1, o aviso PreToolUse:Bash hook error carrega esta linha:
Se o aviso carregar a mensagem No such file or directory do shell, como na linha a seguir, o hook nunca foi executado, por exemplo porque o caminho do script não existe:
Se o caminho estiver errado, corrija-o onde o hook está configurado. Na forma shell, quando o caminho do script não existe ou não é executável, o shell sai com um código como 127 e o aviso carrega a mensagem do interpretador. A menos que o hook seja um cuja falha bloqueia a ação, um hook que não consegue iniciar também é um erro não-bloqueador. Quando você configura um hook de política, observe este aviso em sua primeira execução, porque um caminho digitado incorretamente em settings.json significa que o hook nunca executa. Para bloquear a ação quando o hook não consegue iniciar, defina onFailure: "block".
Código de saída 1 sem saída JSON válida é um erro não-bloqueador, a menos que o hook seja um cuja falha bloqueia a ação, mesmo que 1 seja o código de falha Unix convencional. Se seu hook se destina a impor uma política, use exit 2.

Timeouts

Exceto por um hook de comando que você executa com async: true, Claude Code cancela um hook command, http ou mcp_tool que atinge seu timeout, descartando a saída do hook, portanto na maioria dos eventos um hook expirado não renderiza decisão. Em PreModelSwitch, um hook cancelado em seu timeout bloqueia a mudança de modelo. Em PreToolUse, um hook command, http ou mcp_tool expirado não bloqueia a chamada da ferramenta, exceto para hooks cuja falha bloqueia a ação. A chamada continua através do fluxo de permissão normal, portanto não conte com um hook travado para agir como um portão. Para bloquear a chamada quando um hook command ou http atinge o timeout, defina onFailure: "block". Um hook de callback do Agent SDK que excede seu timeout bloqueia a chamada da ferramenta.
Se você usa o Code Mode, os timeouts em PreToolUse funcionam de forma diferente. Consulte Chamada de ferramenta recusada por um hook.

Hooks que iniciam um processo em segundo plano

Um hook pode iniciar um programa de longa duração, como um servidor de desenvolvimento, e deixá-lo em execução enquanto a sessão continua. No script do hook ou no comando em forma shell:
  • Execute o programa em segundo plano
  • Redirecione o stdout e o stderr dele. Por exemplo, o comando Bash npm run dev > dev-server.log 2>&1 & envia ambos para um arquivo. Se você não os redirecionar, um hook que não é async leva até 10 segundos para terminar depois que o próprio processo do hook sai.
Se seu hook de comando toma sua decisão em um processo em segundo plano e sai, uma negação que esse processo imprime depois que Claude Code parou de ler não bloqueia a chamada de ferramenta. Para um hook que não é async, Claude Code termina de ler a saída do hook logo após o próprio processo do hook sair. Imprima a decisão antes de iniciar um processo em segundo plano, e redirecione o stdout e o stderr desse processo. Se seu script imprime um allow e depois inicia um processo em segundo plano sem redirecionar o stdout e o stderr desse processo, Claude Code bloqueia a chamada de ferramenta. O erro do hook inclui stdio went quiet e refusing to honor the suspect capture. Este script de hook PreToolUse imprime a decisão com policy-check.sh e depois envia métricas em segundo plano:
O restante do script faz o seguinte:
  • || exit $?: torna o código de saída da verificação o código do próprio hook, para que uma verificação que sai com 2 para negar ainda bloqueie. Caso contrário, o hook sai com o código da última linha, que é 0.
  • >/dev/null 2>&1 &: executa upload-metrics.sh em segundo plano com sua saída redirecionada, para que ele não mantenha a saída do hook aberta.
Para testar o script, faça policy-check.sh sair com 2 e então dispare uma chamada de ferramenta correspondida pelo hook. A chamada de ferramenta é bloqueada.

Bloquear a ação quando um hook falha

Na maioria dos eventos, quando um hook falha ou atinge o timeout, Claude Code ainda executa a ação. Um hook de política com um caminho errado ou um script que trava então deixa tudo passar. Para bloquear a ação em vez disso, defina "onFailure": "block" em um hook command ou http. Requer Claude Code v2.1.295 ou posterior. Para hooks de callback do Agent SDK, consulte Bloquear a ação quando um callback falha.
No Windows sem Git Bash, um hook de comando PreToolUse ou PermissionRequest em forma shell que falha com onFailure não definido pode, em vez disso, bloquear a chamada de ferramenta.
Este hook PreToolUse em .claude/settings.json executa um script do projeto antes de cada comando Bash, e bloqueia o comando se o script falhar:
Para testá-lo, deixe check-command.js ausente e peça a Claude para executar um comando Bash como ls. Claude Code bloqueia a chamada, e o erro inclui failed; blocking because onFailure is "block" seguido pela própria saída de erro do node, reduzida aqui a uma linha:
Após um timeout, a mensagem diz timed out em vez de failed. Sem onFailure definido, o mesmo script ausente é um erro não-bloqueador e ls é executado. Cada um destes conta como uma falha:
  • Não consegue iniciar: um hook de comando falha ao iniciar, por exemplo porque o script ou executável não existe
  • Código de saída diferente de 0 ou 2: conta para um hook de comando mesmo que ele tenha impresso JSON que permite a ação, como permissionDecision: "allow". Para retornar uma decisão JSON, saia com 0
  • Erro HTTP: a conexão de um hook HTTP falha, ou o status da resposta não é 2xx
  • Timeout: o hook atinge seu timeout
  • Saída inválida: a saída JSON não pode ser analisada ou falha na validação de esquema. Para um hook HTTP, um corpo 2xx que não é vazio nem um objeto JSON também conta. Stdout em texto simples de um hook de comando não é uma falha
Com "block" definido, uma falha faz o que o código de saída 2 faz nesse evento, exceto em PermissionRequest, onde ela nega a solicitação. Por exemplo, uma falha em PreToolUse bloqueia a chamada de ferramenta e uma falha em UserPromptSubmit bloqueia o prompt. O campo não tem efeito nestes hooks:
  • Hooks Stop, SubagentStop, TaskCompleted e TeammateIdle: o código de saída 2 nesses eventos manda Claude de volta para continuar trabalhando, e Claude não consegue reparar um hook que não executa
  • Hooks de comando em segundo plano: hooks de comando que definem async ou asyncRewake
Se você usa o Code Mode, as falhas em PreToolUse funcionam de forma diferente. Consulte Chamada de ferramenta recusada por um hook.

Hooks com falha no Windows sem Git Bash

Se o erro de uma chamada de ferramenta bloqueada incluir este texto, um hook de comando PreToolUse ou PermissionRequest falhou ou atingiu o timeout e bloqueou a chamada:
Se o erro também disser PowerShell started no program, comece pelas correções para um command composto apenas de strings entre aspas, que o PowerShell imprime em vez de executar. Sem Git Bash, qualquer código de saída diferente de 0 ou 2 desse hook conta como uma falha, mesmo quando ele imprime JSON que não nega a chamada. Para descobrir por que o hook falhou, verifique o log de debug. Então escolha a correção que corresponde ao seu hook:
  • A chamada deve prosseguir quando o hook falha: adicione "onFailure": "continue" ao hook
  • O hook sai com 1 para avisar, mas deve bloquear quando trava: com "onFailure": "continue", a chamada prossegue com o código de saída 1, seja um aviso ou uma falha. Adicione "onFailure": "block" em vez disso, e faça o script sair com 0 onde ele avisa, para que essas chamadas prossigam. Para ainda mostrar o aviso em PreToolUse, imprima-o em stdout no campo systemMessage de um único objeto JSON.
  • O hook é escrito para Bash: sem Git Bash, um hook em forma shell sem shell definido é executado no PowerShell, onde pode falhar. Instale o Git for Windows, ou defina CLAUDE_CODE_GIT_BASH_PATH se o Git Bash estiver instalado em um local onde o Claude Code não o encontra. Então inicie uma nova sessão.
  • O hook executa um único .exe, não um arquivo .cmd ou .bat: coloque o programa em command e seus argumentos em args para usar a forma exec, e adicione "onFailure": "block". Claude Code então inicia o programa sem shell. Sem "onFailure": "block", a chamada pode prosseguir quando o programa falha, está ausente ou atinge o timeout
  • Um hook de proteção em PowerShell que deve bloquear em caso de falha: com onFailure não definido, ele deixa de fazer isso assim que o Git for Windows é instalado e o Claude Code encontra o Git Bash. Adicione "onFailure": "block" para que uma falha ou um timeout bloqueie a chamada com ou sem Git Bash. Defina também "shell": "powershell" para que o hook continue sendo executado no PowerShell a partir de então, em vez de no Git Bash
Antes da v2.1.309, a mensagem dizia blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.

PowerShell started no program

Um hook com este erro foi executado no PowerShell, que imprime um command composto apenas de strings entre aspas, como "command": "\"C:\\Tools\\guard.exe\"", em vez de executá-lo.
  • Para imprimir texto, como JSON: defina "shell": "powershell", e o hook sai com 0
  • Para executar um arquivo .ps1, .cmd ou .bat, ou um .exe cujo caminho contém uma variável: defina "shell": "powershell" e coloque & antes do caminho entre aspas. Deixe $HOME como está escrito, e escreva variáveis de ambiente da mesma forma que o exemplo check.ps1 escreve $env:CLAUDE_PROJECT_DIR
  • Para executar um único .exe em um caminho sem $: remova as aspas internas de command e então aplique a correção para um hook que executa um único .exe, escrevendo "args": [] se o programa não receber argumentos
  • Para executar um script escrito para Bash: aplique a correção para um hook escrito para Bash

Comportamento de código de saída 2 por evento

Código de saída 2 é a forma de um hook sinalizar “pare, não faça isso”. O efeito depende do evento, porque alguns eventos representam ações que podem ser bloqueadas (como uma chamada de ferramenta que ainda não aconteceu) e outros representam coisas que já aconteceram ou não podem ser prevenidas. Para SessionStart, SubagentStart e PostModelSwitch, Claude Code renderiza o stderr de código de saída 2 na transcrição como um aviso <hook name> hook error, da mesma forma que renderiza um erro não-bloqueador. Claude não vê, e a sessão ou subagente prossegue. Para SubagentStart, o aviso aparece na própria transcrição do subagente, não na conversa pai.

Tratamento de resposta HTTP

Hooks HTTP usam códigos de status HTTP e corpos de resposta em vez de códigos de saída e stdout. Para bloquear a ação quando a requisição falha ou retorna um status não-2xx, defina onFailure: "block". Para bloquear uma chamada de ferramenta ou negar uma permissão, retorne uma resposta 2xx com um corpo JSON contendo os campos de decisão apropriados. Após um erro não-bloqueador, a ação prossegue, a menos que o hook seja um cuja falha bloqueia a ação. Exceto em eventos com seu próprio contrato de falha na tabela por evento, como WorktreeCreate, você obtém estes resultados para cada resposta ou falha:
  • 2xx com corpo vazio: sucesso, equivalente a código de saída 0 sem saída
  • 2xx com corpo de objeto JSON: analisado usando o mesmo esquema saída JSON que hooks de comando. Um corpo que falha na validação de esquema é um erro não-bloqueador
  • 2xx com qualquer outro corpo, como texto simples: erro não-bloqueador, tratado da mesma forma que um status não-2xx. Claude Code não adiciona o texto ao contexto de Claude
  • Status não-2xx: erro não-bloqueador
  • Falha de conexão: erro não-bloqueador
  • Timeout: o hook é cancelado, conforme descrito em Timeouts

Saída JSON

Códigos de saída permitem você bloquear ou ficar em silêncio, mas saída JSON oferece controle mais granular. Em vez de sair com código 2 para bloquear, saia 0 e imprima um objeto JSON em stdout. Claude Code lê campos específicos desse JSON para controlar comportamento, incluindo controle de decisão para bloquear, permitir ou escalar para o usuário.
Escolha uma abordagem por hook: ou use códigos de saída sozinhos para sinalizar, ou saia 0 e imprima JSON para controle estruturado. Se você misturar, saída 2 mantém seu efeito de bloqueio, e Claude Code ainda lê os campos JSON, com a exceção de elicitação única anotada em Código de saída 2.
Imprima apenas o objeto JSON em stdout. Para um hook que não é async, outro texto ali, como uma linha que seu perfil shell imprime na inicialização, impede Claude Code de ler o objeto como JSON, e Hook JSON não tem efeito mostra como encontrar e silenciar esse texto. As strings de saída de hook additionalContext, systemMessage e initialUserMessage, e seu stdout simples, são limitadas a 10.000 caracteres:
  • Escopo: Claude Code mede cada string por conta própria, mesmo quando vários hooks executam para o mesmo evento. Para saída JSON, cada campo é medido separadamente; stdout simples é medido como um todo.
  • Acima do limite: exceto para o stdout de PreCompact, Claude Code salva a saída em um arquivo no diretório de sessão. Ele substitui a saída pelo caminho do arquivo e uma visualização de até os primeiros 2.000 caracteres. Um resultado Bash grande válido é tratado da mesma forma, descrito em Limites de saída. Diferentemente desse teto Bash, este limite não tem configuração ou variável de ambiente para aumentá-lo.
  • Lendo o arquivo: Claude Code não pede a Claude para ler o arquivo, portanto mantenha qualquer coisa que Claude sempre deva ver dentro do limite.
O objeto JSON suporta três tipos de campos:
  • Campos universais como continue são listados na tabela abaixo. Cada evento os aceita, mas alguns eventos os descartam ou entregam systemMessage em outro lugar que não a transcrição. Cada seção de evento diz assim. terminalSequence funciona nesses eventos também, com as exceções listadas em Emitir notificações de terminal.
  • decision e reason de nível superior são usados por alguns eventos para bloquear ou fornecer feedback.
  • hookSpecificOutput é um objeto aninhado para eventos que precisam de controle mais rico. Requer um campo hookEventName definido para o nome do evento.
Para parar Claude inteiramente:
Para hooks PreToolUse e PostToolUse, a parada se aplica mesmo quando a chamada da ferramenta falha ou é concluída enquanto Claude ainda está transmitindo uma resposta.

Emitir notificações de terminal

Hooks executam sem um terminal controlador, portanto escrever sequências de escape diretamente para /dev/tty falha. Em vez disso, retorne a sequência de escape no campo terminalSequence e Claude Code a emite para você através de seu próprio caminho de escrita de terminal. Isso é livre de corrida e funciona no Windows onde não há /dev/tty. O campo aceita uma string de uma ou mais sequências de escape da allowlist:
  • OSC 0, 1, 2: títulos de janela e ícone
  • OSC 9: notificações iTerm2, ConEmu, Windows Terminal e WezTerm, incluindo progresso de barra de tarefas 9;4
  • OSC 99: notificações Kitty
  • OSC 777: notificações urxvt, Ghostty e Warp
  • BEL simples
Sequências podem ser terminadas com BEL ou com ST. Qualquer coisa fora da allowlist, incluindo sequências de cursor e cor CSI, sequências de paleta OSC, hiperlinks OSC 8, escritas de área de transferência OSC 52 e OSC 1337, é rejeitada e o campo é ignorado. Claude Code escreve a sequência em si quando processa a saída do seu hook, portanto o campo funciona em eventos que descartam systemMessage e continue, como Notification e StopFailure. Tem alguns limites:
  • Claude Code escreve a sequência apenas em uma sessão interativa, e apenas enquanto sua interface está aberta. Em modo não interativo com a flag -p e no Agent SDK, ignora o campo.
  • O campo funciona apenas a partir de um hook que não é executado em segundo plano, portanto deixe async e asyncRewake desativados.
  • Um hook de comando WorktreeCreate não pode retornar JSON, porque Claude Code lê seu stdout como o caminho de worktree. Um hook HTTP WorktreeCreate retorna JSON e pode incluir o campo.
Se você usa tmux ou GNU screen, conclua primeiro a configuração para eles. O exemplo abaixo dispara uma notificação de desktop de um hook Notification em um terminal que lê OSC 777, como Ghostty ou Warp. A chamada jq -nc --arg do script constrói a saída JSON e escapa quaisquer aspas, barras invertidas e quebras de linha na mensagem de notificação:
A forma { "terminalSequence": "..." } é a mesma de qualquer shell ou linguagem.

Receber notificações de terminal dentro de tmux ou GNU screen

Quando Claude Code é executado dentro de tmux ou GNU screen, as sequências OSC precisam do seguinte para chegar ao seu aplicativo de terminal:
  • tmux 3.3 ou posterior: adicione set -g allow-passthrough on a ~/.tmux.conf e então execute tmux source-file ~/.tmux.conf a partir de um shell. A opção se aplica a todos os programas nesses painéis, não apenas ao Claude Code. Com on, apenas os painéis que o tmux está mostrando podem enviá-las. Para permitir que outros painéis em uma sessão à qual você está conectado também as enviem, use all no lugar de on, no tmux 3.4 ou posterior.
  • GNU screen: mantenha a janela do Claude Code visível no screen, porque sequências de uma janela em segundo plano não chegam ao seu aplicativo de terminal

Adicionar contexto para Claude

O campo additionalContext passa uma string do seu hook para a janela de contexto do Claude. Claude Code envolve a string em um lembrete do sistema e a insere na conversa no ponto onde o hook disparou. Claude lê o lembrete na próxima requisição ao modelo, mas não aparece como uma mensagem de chat na interface. Retorne additionalContext dentro de hookSpecificOutput ao lado do nome do evento:
Onde o lembrete aparece depende do evento: Quando vários hooks retornam additionalContext para o mesmo evento, Claude recebe todos os valores. Se sua string contiver uma tag <system-reminder> ou </system-reminder>, Claude recebe a string com o < dessa tag substituído por &lt;. Se um valor exceder 10.000 caracteres, Claude Code escreve o texto em um arquivo no diretório de sessão e passa Claude o caminho do arquivo com uma visualização de até os primeiros 2.000 caracteres em vez disso. Claude pode ler o arquivo, mas Claude Code não pede a Claude para. Use additionalContext para informações que Claude deve saber sobre o estado atual do seu ambiente ou a operação que acabou de executar:
  • Estado do ambiente: o branch atual, alvo de implantação ou sinalizadores de recurso ativos
  • Regras de projeto condicional: qual comando de teste se aplica ao arquivo que acabou de ser editado, quais diretórios são somente leitura neste worktree
  • Dados externos: problemas abertos atribuídos a você, resultados recentes de CI, conteúdo obtido de um serviço interno
Para instruções que nunca mudam, prefira CLAUDE.md. Ele carrega sem executar um script e é o lugar padrão para convenções de projeto estáticas. Escreva o texto como declarações factuais em vez de instruções de sistema imperativas. Frases como “O alvo de implantação é produção” ou “Este repositório usa bun test” lê como informação de projeto. Texto enquadrado como comandos de sistema fora de banda pode disparar as defesas de injeção de prompt do Claude, o que faz com que Claude superficialize o texto para você em vez de tratá-lo como contexto. Claude Code salva o texto injetado na transcrição de sessão. Para eventos de mid-sessão como PostToolUse ou UserPromptSubmit, quando você retoma com --continue ou --resume, Claude Code reproduz o texto salvo em vez de re-executar o hook para turnos anteriores, portanto valores como timestamps ou SHAs de commit ficam obsoletos. Hooks SessionStart executam novamente na retomada com source definido como "resume", ou "fork" se você adicionou --fork-session, para que possam atualizar seu contexto.

Controle de decisão

Nem todo evento suporta bloqueio ou controle de comportamento através de JSON. Os eventos que fazem cada um usam um conjunto diferente de campos para expressar essa decisão. Use esta tabela como referência rápida antes de escrever um hook: Alguns eventos também podem reescrever conteúdo em vez de apenas permitir ou bloquear:
  • PreToolUse: updatedInput diretamente sob hookSpecificOutput substitui os argumentos de uma ferramenta antes de executar. Consulte Controle de decisão PreToolUse
  • PermissionRequest: updatedInput dentro do objeto decision. Consulte Controle de decisão PermissionRequest
  • PostToolUse: updatedToolOutput substitui o resultado da ferramenta. Em uma sessão na nuvem do Cowork, Claude Code ignora updatedToolOutput quando a ferramenta é AskUserQuestion ou ExitPlanMode, e Claude lê a resposta ou o plano original. Consulte Controle de decisão PostToolUse
  • UserPromptSubmit: não pode substituir o prompt; apenas injeta additionalContext ao lado dele
Para casos de uso de redação ou transformação, intercepte em PreToolUse para entradas de ferramenta de saída e PostToolUse para resultados de ferramenta de entrada. Aqui estão exemplos de cada padrão em ação:
O único valor para decision é "block". Para permitir que a ação prossiga, omita decision do seu JSON, ou saia 0 sem qualquer JSON:
Para exemplos estendidos incluindo validação de comando Bash, filtragem de prompt e scripts de aprovação automática, consulte O que você pode automatizar no guia e a implementação de referência do validador de comando Bash.

Eventos de hook

Cada evento corresponde a um ponto no ciclo de vida do Claude Code em que hooks podem ser executados. As seções abaixo estão ordenadas de acordo com o ciclo de vida: da configuração da sessão, passando pelo loop agêntico, até o fim da sessão. Cada seção descreve quando o evento é disparado, quais matchers ele suporta, a entrada JSON que recebe e como controlar o comportamento por meio da saída.

SessionStart

É executado quando o Claude Code inicia uma nova sessão ou retoma uma sessão existente. Útil para carregar contexto de desenvolvimento, como issues existentes ou alterações recentes na sua base de código, ou para configurar variáveis de ambiente. Para contexto estático que não requer um script, use o CLAUDE.md. O SessionStart é executado em todas as sessões, portanto mantenha esses hooks rápidos. Somente hooks type: "command" e type: "mcp_tool" são suportados. Consulte Campos de hook de ferramenta MCP para saber quando hooks mcp_tool são executados. O valor do matcher corresponde a como a sessão foi iniciada: Quando você inicia uma sessão interativa, retoma uma conversa na inicialização com --continue ou --resume, ou executa /clear, os hooks SessionStart são executados em segundo plano. Você pode digitar imediatamente, e uma conversa que você retomou aparece sem esperar pelos hooks. A primeira resposta do Claude ainda espera os hooks terminarem, para que o contexto deles chegue ao Claude. Quando você troca de conversa com /resume dentro de uma sessão, a troca espera os hooks terminarem. Se você executar /clear ou trocar para outra conversa enquanto hooks em segundo plano ainda estiverem em execução, nada do que eles retornarem se aplica à sessão. A mesma espera se aplica na inicialização, incluindo uma sessão retomada: um prompt que você envia enquanto os hooks SessionStart ainda estão em execução não chega ao Claude até que eles terminem. Durante qualquer uma dessas esperas, pressione Esc para trazer o prompt de volta à entrada sem enviá-lo. Os hooks continuam em execução.

Entrada do SessionStart

Além dos campos de entrada comuns, os hooks SessionStart recebem source e, opcionalmente, model, agent_type e session_title: Uma sessão que você não nomeou ainda pode ter um título gerado. Esse título não é um título personalizado e não aparece em session_title. Quando source é "resume" ou "fork" e a transcrição contém pelo menos uma resposta do Claude, os hooks SessionStart também recebem os quatro campos abaixo. Seu hook pode usá-los para informar quanto custa retomar uma conversa antiga antes da primeira requisição, por exemplo em um systemMessage. Esses campos requerem o Claude Code v2.1.251 ou posterior. Este exemplo mostra a entrada para uma sessão retomada 90 minutos após sua última resposta:

Controle de decisão do SessionStart

Um hook SessionStart pode adicionar contexto para o Claude, fornecer a primeira mensagem do usuário, definir o título da sessão, observar arquivos e recarregar skills. Retorne o campo correspondente a cada um, além dos campos de saída JSON disponíveis para todos os hooks: Esta saída adiciona contexto e nomeia a sessão:
Um hook que apenas adiciona contexto pode imprimi-lo sem montar JSON, porque o Claude Code adiciona o stdout em texto simples de um hook SessionStart ao contexto do Claude. Se o hook SessionStart do seu plugin fornece initialUserMessage ou sessionTitle, instale o plugin antes do início da sessão. O Claude Code ignora ambos os campos de um plugin cuja instalação termina depois que os hooks SessionStart foram executados.

Recarregar skills que um hook instala

Para disponibilizar na mesma sessão as skills que um hook SessionStart instala, retorne reloadSkills. A descoberta de skills normalmente é executada antes de os hooks SessionStart terminarem; portanto, sem isso, arquivos que um hook grava em ~/.claude/skills/ ou .claude/skills/ podem estar ausentes quando o primeiro prompt for executado. Este exemplo sincroniza um repositório compartilhado de skills e solicita a nova verificação:
A URL do repositório é um marcador de exemplo. Substitua-a pelo seu próprio repositório de skills.

Persistir variáveis de ambiente

Os hooks SessionStart têm acesso à variável de ambiente CLAUDE_ENV_FILE, que fornece um caminho de arquivo onde você pode persistir variáveis de ambiente para os comandos de shell que o Claude executa mais adiante na sessão. Para definir variáveis de ambiente individuais, escreva instruções export em CLAUDE_ENV_FILE. Use anexação (>>) para preservar variáveis definidas por outros hooks:
Para capturar todas as alterações de ambiente dos comandos de configuração, compare as variáveis exportadas antes e depois:
Cada comando Bash executa o conteúdo do arquivo como código de shell antes do próprio comando, portanto uma linha ali pode usar qualquer coisa que o Bash avalia, como a referência $PATH em export PATH="$PATH:./node_modules/.bin".
Variáveis persistidas em comandos PowerShell
Comandos PowerShell também recebem as variáveis de CLAUDE_ENV_FILE, no Claude Code v2.1.296 ou posterior, mas o PowerShell nunca executa o arquivo. Em vez disso, o Claude Code lê as atribuições do arquivo e as copia para o ambiente do comando PowerShell. Ele faz isso somente quando cada linha é de um dos tipos a seguir, considerando tudo o que cada hook nesta sessão gravou e qualquer script para o qual você definiu CLAUDE_ENV_FILE antes da inicialização:
  • Uma linha em branco ou um comentário #
  • Uma atribuição no início da linha, escrita como export NAME=value, declare -x NAME=value ou NAME=value, com um valor que o Bash usaria exatamente como escrito, composto por qualquer combinação destas partes: texto sem aspas que use apenas letras, dígitos e os caracteres _ @ % + = : , . / -, texto entre aspas simples e texto entre aspas duplas em que qualquer $, acento grave ou " interno esteja escapado com uma barra invertida
Se qualquer linha for de outro tipo, como export PATH="$PATH:./node_modules/.bin" com seu $PATH sem escape, um comando source ou as strings $'...' que direnv export bash imprime, os comandos PowerShell não recebem nenhuma das variáveis, e claude --debug registra Session environment is not all plain assignments. Os comandos Bash ainda recebem todas elas. No Windows, os comandos PowerShell também não recebem uma variável cujo valor contenha / ou \, porque o Git Bash e o Windows escrevem caminhos de forma diferente. Um comando PowerShell em sandbox não recebe nenhuma das variáveis.
CLAUDE_ENV_FILE está disponível para hooks SessionStart, Setup, CwdChanged e FileChanged. Outros eventos de hook não têm acesso a essa variável, nem um hook executado no PowerShell, seja por meio de "shell": "powershell" ou por padrão no Windows sem Git Bash.

Setup

É disparado somente quando você inicia o Claude Code com --init-only, ou com --init ou --maintenance no modo não interativo com a flag -p. Ele não é disparado na inicialização normal. Use-o para instalação única de dependências ou limpeza agendada que você aciona explicitamente a partir de CI ou scripts, separadamente da inicialização normal da sessão. Para inicialização por sessão, use o SessionStart. O valor do matcher corresponde à flag da CLI que acionou o hook: Quando você executa claude --init-only, o Claude Code executa os hooks Setup e os hooks SessionStart com o matcher startup e, em seguida, sai sem iniciar uma conversa. Quando você inicia ou continua uma conversa com -p, também precisa fornecer um prompt, como argumento ou via stdin. Você pode omitir o prompt quando um hook SessionStart fornece initialUserMessage ou quando você retoma uma sessão com uma chamada de ferramenta adiada. Em caso de sucesso, --init-only não imprime nada no terminal. Para confirmar que os hooks foram executados, inicie com claude --debug-file <path> --init-only, substituindo <path> por um local de arquivo de log, e verifique no log as entradas dos hooks Setup e SessionStart. Como o Setup não é disparado em toda inicialização, um plugin que precisa de uma dependência instalada não pode depender apenas do Setup. O padrão prático é verificar a dependência no primeiro uso e instalá-la caso esteja ausente, por exemplo, um hook ou skill que testa ${CLAUDE_PLUGIN_DATA}/node_modules e executa npm install se não existir. Consulte o diretório de dados persistentes para saber onde armazenar as dependências instaladas. Se você distribui seu plugin por meio de um marketplace, talvez não precise desse padrão: o Claude Code instala automaticamente as dependências de pacotes Node.js elegíveis quando armazena o plugin em cache.

Entrada do Setup

Além dos campos de entrada comuns, os hooks Setup recebem um campo trigger definido como "init" ou "maintenance":

Controle de decisão do Setup

Os hooks Setup não podem bloquear; a execução continua com qualquer código de saída. Em todos os códigos de saída, o Claude Code descarta os campos de saída JSON de um hook Setup, como systemMessage, continue e hookSpecificOutput.additionalContext. Com -p, o stdout, o stderr e o código de saída de um hook Setup aparecem na saída da execução somente como eventos hook_response quando você inicia com --output-format stream-json --verbose. Os hooks Setup têm acesso a CLAUDE_ENV_FILE. Se o seu hook gravar variáveis nesse arquivo, elas persistem nos comandos Bash subsequentes da sessão, como nos hooks SessionStart. Somente hooks type: "command" são executados no Setup. Um hook type: "mcp_tool" no Setup é sempre ignorado, conforme descrito em Campos de hook de ferramenta MCP.

InstructionsLoaded

É disparado quando um arquivo CLAUDE.md ou .claude/rules/*.md é carregado no contexto. Esse evento é disparado no início da sessão para arquivos carregados antecipadamente e novamente mais tarde quando arquivos são carregados sob demanda, por exemplo, quando o Claude acessa um subdiretório que contém um CLAUDE.md aninhado ou quando regras condicionais com frontmatter paths: correspondem. O hook não suporta bloqueio nem controle de decisão. Ele é executado de forma assíncrona para fins de observabilidade. Esse evento não é disparado quando o Claude lê o AGENTS.md diretamente por meio da configuração Project instructions. Ele é disparado quando um CLAUDE.md importa seu AGENTS.md, com load_reason definido como include como para qualquer outro arquivo importado, e quando o CLAUDE.md é um link simbólico para ele, como um carregamento normal de CLAUDE.md. O matcher é aplicado a load_reason. Por exemplo, use "matcher": "session_start" para disparar somente para arquivos carregados no início da sessão, ou "matcher": "path_glob_match|nested_traversal" para disparar somente para carregamentos sob demanda.

Entrada do InstructionsLoaded

Além dos campos de entrada comuns, os hooks InstructionsLoaded recebem estes campos:

Controle de decisão do InstructionsLoaded

Os hooks InstructionsLoaded não têm controle de decisão. Eles não podem bloquear nem modificar o carregamento de instruções. O Claude Code descarta seus campos de saída JSON, como systemMessage e continue. Use esse evento para logs de auditoria, rastreamento de conformidade ou observabilidade.

UserPromptSubmit

É executado quando um prompt é enviado, antes que o Claude o processe. Isso permite adicionar contexto extra com base no prompt/conversa, validar prompts ou bloquear certos tipos de prompts. Os hooks UserPromptSubmit não são disparados apenas em prompts que você digita. O Claude Code também os executa quando: Os hooks UserPromptSubmit têm um timeout padrão de 30 segundos para os tipos command, http e mcp_tool, menor que o padrão de 600 segundos para esses tipos na maioria dos outros eventos. Como esse hook é executado antes de cada prompt e bloqueia o processamento do modelo até terminar, um hook travado paralisa a sessão. Se o seu hook precisar de mais tempo, defina o campo timeout na entrada do hook. Exceto por um hook de comando que você executa com async: true, um hook de comando, HTTP ou de ferramenta MCP em UserPromptSubmit que atinge seu timeout é cancelado, e sua saída, incluindo qualquer additionalContext, é descartada. O prompt ainda chega ao Claude sem esse contexto. Para bloquear o prompt em vez disso, defina onFailure: "block" em um hook de comando ou HTTP. A transcrição mostra um aviso nomeando o hook, o timeout que foi atingido e que a saída foi descartada. Um hook de callback do Agent SDK em UserPromptSubmit que atinge seu timeout bloqueia o prompt com uma mensagem indicando o hook e o timeout, porque um callback ali pode estar atuando como um controle de política que não deve falhar de forma permissiva. A sessão continua.

Esc durante uma verificação de prompt

Se você pressionar Esc ou interromper a sessão enquanto os hooks ainda estão verificando um prompt, a verificação para e o prompt não chega ao Claude. O texto que você digitou no terminal retorna à entrada do prompt, e os hooks o verificam novamente quando você o reenviar. Um hook pode bloquear o prompt no mesmo momento em que você interrompe. O prompt então permanece bloqueado: você vê a mensagem de bloqueio desse hook, e o texto não retorna à entrada.

Entrada do UserPromptSubmit

Além dos campos de entrada comuns, os hooks UserPromptSubmit recebem o campo prompt contendo o texto enviado. Conteúdo colado que foi recolhido em um marcador [Pasted text #N] chega expandido no lugar. Em sessões em que o Claude Code marca texto colado para o Claude, esse conteúdo expandido fica entre uma linha <pasted_content id="…"> e uma linha </pasted_content id="…">, portanto leve essas linhas em conta se o seu hook analisar o prompt. Os hooks UserPromptSubmit também recebem session_title quando a sessão tem um título personalizado, com o mesmo significado do campo session_title do SessionStart.
Quando o prompt é uma skill ou um comando personalizado que você digitou, como /deploy staging, o hook recebe essa linha como você a digitou, em vez do conteúdo expandido da skill. Se o hook bloquear o prompt, o comando não é executado, portanto os comandos de shell que a skill injeta com !`<command>` também não são executados. Para fazer a correspondência pelo nome ou pelos argumentos do comando, use UserPromptExpansion.

Controle de decisão do UserPromptSubmit

Os hooks UserPromptSubmit podem controlar se um prompt enviado é processado e adicionar contexto. Todos os campos de saída JSON estão disponíveis. Há duas maneiras de adicionar contexto à conversa com o código de saída 0:
  • Stdout em texto simples: o Claude Code adiciona ao contexto do Claude o stdout que ele trata como texto simples
  • JSON com additionalContext: use o formato JSON abaixo para ter mais controle. O campo additionalContext é adicionado como contexto
Nenhum dos canais produz uma entrada visível na transcrição. O stdout simples e o valor de additionalContext são injetados, cada um, como um lembrete do sistema que começa com o nome do hook; o Claude lê ambos. Para confirmar a entrega, verifique o log de depuração. Para bloquear um prompt, retorne um objeto JSON com decision definido como "block": Se o seu hook bloquear saindo com código 2, o texto do stderr é tratado da mesma forma que reason.

O que um prompt bloqueado deixa para trás

Um prompt bloqueado nunca chega ao Claude, mas seu texto não é removido de todos os lugares. Por padrão, a mensagem de bloqueio mostrada ao usuário termina com Original prompt: seguido do texto enviado, e o Claude Code grava essa mensagem no arquivo de transcrição da sessão no disco. Para deixar o texto fora da mensagem, imprima JSON com "suppressOriginalPrompt": true dentro de hookSpecificOutput. Isso funciona tanto se o hook bloquear com decision: "block" quanto saindo com 2. suppressOriginalPrompt altera apenas a mensagem de bloqueio. O texto enviado ainda pode aparecer em arquivos locais, como a transcrição da sessão e seu histórico de prompts, portanto um hook de bloqueio não é uma maneira de manter um segredo fora do disco. Para limitar ou remover esses arquivos, consulte Armazenamento em texto simples e Limpar dados locais.

UserPromptExpansion

É executado quando um comando digitado pelo usuário se expande em um prompt antes de chegar ao Claude. Use-o para impedir a invocação direta de comandos específicos, injetar contexto para uma skill específica ou registrar quais comandos os usuários invocam. Por exemplo, um hook que corresponde a deploy pode bloquear /deploy a menos que um arquivo de aprovação esteja presente, ou um hook que corresponde a uma skill de revisão pode anexar a checklist de revisão da equipe como additionalContext. Esse evento cobre o caminho que o PreToolUse não cobre: um hook PreToolUse que corresponde à ferramenta Skill é disparado somente quando o Claude chama a ferramenta, mas digitar /skillname diretamente ignora o PreToolUse. O UserPromptExpansion é disparado nesse caminho direto. Faz a correspondência em command_name. Deixe o matcher vazio para disparar em todo comando do tipo prompt.

Entrada do UserPromptExpansion

Além dos campos de entrada comuns, os hooks UserPromptExpansion recebem expansion_type, command_name, command_args, command_source e a string prompt original. O campo expansion_type é slash_command para skills e comandos personalizados, ou mcp_prompt para prompts de servidores MCP.

Controle de decisão do UserPromptExpansion

Os hooks UserPromptExpansion podem bloquear a expansão ou adicionar contexto. Todos os campos de saída JSON estão disponíveis. Um hook que bloqueia saindo com 2 é encaminhado da mesma forma que reason: a mensagem de bloqueio mostra o texto do stderr ao usuário.

MessageDisplay

É executado enquanto uma mensagem do assistente é transmitida para a tela. O Claude Code exibe a mensagem em incrementos: cada vez que um lote de linhas recém-concluídas está pronto para ser renderizado, o hook é executado uma vez com essas linhas, e o Claude Code renderiza o texto de substituição do hook no lugar delas. Uma mensagem longa produz várias chamadas; uma mensagem curta pode produzir apenas uma. Use o MessageDisplay para:
  • remover markdown para uma exibição minimalista
  • transformar o texto que um aplicativo do Agent SDK mostra aos seus usuários
  • ocultar chaves de API ou nomes de host internos das respostas do Claude
O Claude Code retém cada lote até que seu hook retorne, portanto mantenha o hook rápido. Se o hook falhar ou atingir o timeout, o Claude Code exibe o texto original. O timeout padrão para esse evento é de 10 segundos; se o seu hook precisar de mais tempo, defina o campo timeout na entrada do hook. O MessageDisplay serve apenas para exibição: o texto de substituição altera somente o que é renderizado na tela. A transcrição e o que o Claude vê mantêm o texto original, portanto o Claude nunca vê a substituição, e o modo verbose mostra o original. O hook recebe apenas o texto das mensagens do assistente, portanto resultados de ferramentas e o texto que você digita são renderizados sem alteração. O MessageDisplay não suporta matchers e é disparado para toda mensagem do assistente que transmite texto; mensagens sem texto, como respostas que contêm apenas chamadas de ferramenta, não o acionam. Em execuções não interativas, incluindo consultas do Agent SDK e claude -p, o MessageDisplay é executado uma vez por mensagem do assistente, em vez de uma vez por lote de linhas. A chamada única chega após a conclusão da mensagem e carrega o texto completo da mensagem: index é 0, final é true e delta contém a mensagem inteira. Um hook que coleta o texto de delta de cada mensagem recebe o mesmo texto total em ambos os modos.

Entrada do MessageDisplay

Além dos campos de entrada comuns, os hooks MessageDisplay recebem identificadores do turno e da mensagem, a posição dessa chamada dentro da mensagem e o novo texto em delta. Os limites dos lotes dependem de como o texto é transmitido, portanto use index e final para acompanhar o progresso pela mensagem, em vez de esperar que as linhas sejam agrupadas de uma forma específica.

Saída do MessageDisplay

Além dos campos de saída JSON disponíveis para todos os hooks, os hooks MessageDisplay podem retornar displayContent para substituir o delta na tela: Os hooks MessageDisplay não têm controle de decisão. Eles não podem bloquear a mensagem nem alterar o que é armazenado na transcrição ou enviado ao Claude. O Claude Code age sobre o displayContent da saída JSON deles e descarta systemMessage e continue. Este exemplo remove a formatação markdown das respostas do Claude para uma exibição em texto simples. O script lê cada lote do stdin, remove os marcadores de negrito e os acentos graves de código inline de delta e retorna o resultado como displayContent.
Registre um hook de comando para o evento no seu arquivo de configurações:
Salve este script em .claude/hooks/plain-display.sh no seu projeto e torne-o executável com chmod +x:
Lotes sem markdown passam sem alteração. Se o script falhar, por exemplo porque o jq não está instalado, o Claude Code exibe o texto original e registra a falha apenas na saída de depuração, não na sessão.

PreToolUse

É executado depois que o Claude cria os parâmetros da ferramenta e antes de processar a chamada de ferramenta. Faz a correspondência com qualquer nome de ferramenta, exceto EndConversation: ferramentas integradas como Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion e ExitPlanMode, e quaisquer nomes de ferramentas MCP. Para executar um hook quando um arquivo específico muda no disco, independentemente de quem o gravou, use o FileChanged em vez de fazer a correspondência de ferramentas de edição de arquivos pelo nome. Diferentemente do PreToolUse, o Claude Code executa os hooks FileChanged após a alteração, e eles não têm controle de decisão, portanto não podem bloquear a gravação.
O PreToolUse é executado somente quando o Claude chama uma ferramenta. Por padrão, arquivos que você referencia com @ no seu prompt são adicionados sem nenhuma chamada de ferramenta. O Claude Code insere o conteúdo deles enquanto monta o prompt, portanto nenhum hook PreToolUse é disparado para eles, incluindo hooks que correspondem a Read. O PreToolUse também não é disparado para EndConversation.Você pode bloquear caminhos específicos de referências @ ou desativar a inserção:
  • Bloquear caminhos específicos: adicione uma regra de negação de Read para eles
  • Desativar a inserção: defina CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 no seu shell, nas configurações de usuário ou nas configurações gerenciadas. O Claude então recebe os caminhos mencionados com uma nota para lê-los usando suas ferramentas de arquivo. Se ele fizer isso, seus hooks PreToolUse correspondentes serão executados. A variável requer o Claude Code v2.1.299 ou posterior, e o Claude Code a ignora nas configurações de projeto e locais.
Use o controle de decisão do PreToolUse para permitir, negar, perguntar ou adiar a chamada de ferramenta. Um hook de callback do Agent SDK em PreToolUse que excede seu timeout bloqueia a chamada de ferramenta.

Entrada do PreToolUse

Além dos campos de entrada comuns, os hooks PreToolUse recebem tool_name, tool_input e tool_use_id. Para uma ferramenta MCP, a entrada também carrega mcp_server, um objeto com o name do servidor e um source que indica de onde veio a definição do servidor. Os valores de source incluem plugin, sdk e escopos de configuração como user e project. McpServerProvenance na referência do Agent SDK lista todos eles e explica como tratar um que você não reconheça. Baseie decisões de confiança em source, e não em name ou no prefixo de nome de ferramenta mcp__<server>__. O campo mcp_server requer o Claude Code v2.1.274 ou posterior. Para as ferramentas de arquivo Write, Edit e Read, tool_input.file_path é sempre absoluto:
  • O Claude Code expande ~ e caminhos relativos antes de os hooks serem executados, portanto um hook que faz a correspondência de caminhos não pode ser contornado por meio de ~ ou de uma grafia relativa do mesmo caminho
  • No Windows, o caminho chega com separadores de barra invertida, mesmo quando seu hook é executado no Git Bash, onde $PWD se parece com /c/project
  • Uma comparação escrita com barras normais, como uma verificação de /src/, nunca corresponde a um caminho com barras invertidas, e a chamada de ferramenta prossegue como se o hook não tivesse nada a bloquear
  • Normalize os separadores antes de comparar: FILE_PATH="${FILE_PATH//\\//}" no Bash, ou file_path.replace("\\", "/") no Python, e então faça a correspondência de um segmento de caminho como /src/ em vez de ancorar com ^, já que o caminho é absoluto
Uma chamada Write no Windows entrega:
Os campos de tool_input dependem da ferramenta:
Bash
Executa comandos de shell. Quando um comando Bash altera arquivos em um repositório Git, o Claude Code pode registrar o que mudou. Ele registra as alterações em todos os modos de permissão quando a configuração bashEditDiffEnabled ativa o registro; a entrada dessa configuração indica quais arquivos podem defini-la. Caso contrário, ele as registra somente no modo auto e no modo bypassPermissions, e somente quando o Claude Code orienta o Claude a editar arquivos por meio do Bash. Defina bashEditDiffEnabled como false para desativar o registro. Comandos em segundo plano e comandos somente leitura não carregam diff. Seu hook PostToolUse então recebe os arquivos alterados em tool_response.bashEditDiff. A lista abrange o que mudou no repositório enquanto o comando era executado. Arquivos que o Git ignora e arquivos em submódulos não são listados. Requer o Claude Code v2.1.269 ou posterior.
A lista é de melhor esforço e está em beta público. O Claude Code pode deixar de capturar uma alteração, incluir um arquivo que outro processo alterou ao mesmo tempo ou parar em seus limites de tamanho. O formato do campo pode mudar. Use a lista para encontrar o que revisar, não para impor uma política.
changedFiles e files listam o que o comando alterou; os campos restantes indicam quão completa e quão confiável é essa lista.
PowerShell
Executa comandos PowerShell. Consulte a ferramenta PowerShell para ver a disponibilidade por plataforma. Os campos correspondem aos da ferramenta Bash, com a string do comando em command: Use Bash|PowerShell como matcher em hooks que inspecionam comandos de shell, para que cubram ambas as ferramentas:
  • No Windows, onde quer que a ferramenta PowerShell esteja habilitada, o Claude trata o PowerShell como o shell principal e encaminha os comandos de shell por meio dele.
  • No Windows sem Git Bash, a ferramenta é habilitada automaticamente e o Claude Code não registra a ferramenta Bash.
  • Um hook que corresponde apenas a Bash nunca é disparado nesse caso.
Write
Cria ou sobrescreve um arquivo.
Edit
Substitui uma string em um arquivo existente.
Read
Lê o conteúdo de arquivos.
Glob
Encontra arquivos que correspondem a um padrão glob.
Grep
Pesquisa o conteúdo de arquivos com expressões regulares.
WebFetch
Busca e processa conteúdo da web.
WebSearch
Pesquisa na web.
Agent
Inicia um subagente. Quando uma chamada Agent em primeiro plano é concluída, seu hook PostToolUse recebe o resultado do subagente e a telemetria da execução em tool_response. Leia esses campos para inspecionar a execução; para totais de tokens e custos entre subagentes, use os contadores de tokens e custos filtrados por query_source "subagent", já que totalTokens e usage cobrem apenas a requisição final: No Claude Code v2.1.271 ou posterior, um subagente executado com a ferramenta SubagentHandback, que o Claude Code fornece no modo auto, entrega seu relatório por meio dessa ferramenta em vez de retorná-lo como texto. O campo content de seu resultado completed então carrega uma nota curta sobre essa entrega em vez do próprio relatório. Para ler o relatório, faça a correspondência de um hook PreToolUse ou PostToolUse com SubagentHandback e leia tool_input.message. Para subagentes em segundo plano, a ferramenta retorna quando a tarefa passa para o segundo plano, portanto tool_response não carrega campos de uso: um lançamento em segundo plano retorna imediatamente, e uma tarefa em primeiro plano que o Claude Code move para o segundo plano no meio da execução retorna nessa transição. Ele tem status: "async_launched", agentId, description, prompt, outputFile e resolvedModel. Em uma resposta completed, resolvedModel indica o modelo com o qual o subagente começou, que pode diferir do valor de model em tool_input, como quando availableModels ou outra substituição se aplica. Em uma resposta async_launched, resolvedModel indica o modelo em uso quando o agente passou para o segundo plano, então uma troca que aconteceu antes da passagem para o segundo plano se reflete ali.
AskUserQuestion
Faz ao usuário de uma a quatro perguntas de múltipla escolha.
ExitPlanMode
Apresenta um plano e pede ao usuário que o aprove antes de o Claude sair do modo de planejamento. O Claude grava o plano em um arquivo no disco antes de chamar a ferramenta, portanto o tool_input literal do modelo normalmente está vazio. O Claude Code injeta o conteúdo do plano e o caminho do arquivo antes de passar a entrada para os hooks. No PostToolUse, tool_response é um objeto com os campos plan e filePath contendo o plano aprovado, além de flags de status internas. Leia tool_response.plan para obter o conteúdo do plano em vez de ler novamente o arquivo do disco.

Controle de decisão do PreToolUse

Os hooks PreToolUse podem controlar se uma chamada de ferramenta prossegue. Ao contrário de outros hooks que usam um campo decision de nível superior, o PreToolUse retorna sua decisão dentro de um objeto hookSpecificOutput. Isso lhe dá um controle mais rico: quatro resultados (allow, deny, ask ou defer), além da capacidade de modificar a entrada da ferramenta antes da execução. Para permitir ou reescrever um comando Monitor, consulte Corresponder a comandos Monitor. Quando vários hooks PreToolUse retornam decisões diferentes, a precedência é deny > defer > ask > allow. Um hook que bloqueia saindo com 2 é encaminhado da mesma forma que "deny": o Claude vê a mensagem do stderr como o motivo da negação. Quando um hook retorna "ask", o prompt de permissão exibido ao usuário inclui um rótulo que identifica de onde o hook veio: [settings] para um hook de qualquer arquivo de configurações ou do frontmatter de um agente, [plugin:<name>] para o hook de um plugin, ou [skill] para um hook do frontmatter de uma skill. Isso ajuda os usuários a entender qual fonte de configuração está solicitando a confirmação.
O PreToolUse usava anteriormente os campos de nível superior decision e reason, mas eles estão descontinuados para esse evento. Use hookSpecificOutput.permissionDecision e hookSpecificOutput.permissionDecisionReason. Os valores descontinuados "approve" e "block" correspondem a "allow" e "deny", respectivamente. Outros eventos, como PostToolUse e Stop, continuam a usar decision e reason de nível superior como seu formato atual.

updatedInput do PreToolUse no modo auto

Quando o seu hook retorna updatedInput sem permissionDecision no modo auto, o Claude Code pode negar a chamada alterada. O servidor revisou a entrada que o Claude escreveu, não a entrada que o seu hook retornou. Em sessões interativas no terminal e sessões do VS Code com conexão direta à API da Anthropic, a negação inclui este texto:
Após uma negação com esse texto, o Claude pode emitir a chamada alterada novamente para que o servidor a revise. Se o Claude repetir exatamente a entrada alterada em sua próxima resposta, o seu hook ainda é executado:
  • Ele pode negar a chamada ou enviá-la para o prompt de permissão
  • Qualquer updatedInput que ele retornar é descartado, portanto o que é executado é o que o servidor revisou
Você não precisa alterar o seu hook por causa dessa negação, a menos que o Claude informe em sua resposta que o modo auto não conseguiu verificar a chamada. Nas sessões que mostram esse texto, um hook command nas configurações ou no arquivo de hooks de um plugin pode alterar chamadas de Bash, PowerShell, Monitor, Edit, Write e NotebookEdit sem essa negação. A negação ainda pode aparecer, como quando algum hook alterou uma chamada anterior na mesma resposta do Claude ou quando um comando Bash pede para ser executado fora do sandbox.

Decisões ask do PreToolUse no modo auto

No modo auto, o "ask" de um hook PreToolUse normalmente envia a chamada de ferramenta para o prompt de permissão, e o classificador não decide sobre ela. Algumas chamadas seguem um caminho diferente:
  • Chamadas que as próprias verificações do Claude Code também sinalizam: incluem gravações em caminhos protegidos e comandos que pedem para ser executados fora do sandbox. O classificador pode negar uma dessas chamadas antes que qualquer prompt apareça. Se ele aprovar, a chamada normalmente ainda vai para o prompt.
  • Chamadas SubagentHandback: esta ferramenta entrega o relatório de um subagente. Nenhum prompt aparece para ela, e a chamada é executada mesmo que o classificador se oponha.
Para interromper uma chamada SubagentHandback, retorne "deny". O Claude Code então informa ao subagente, até três vezes, que seu relatório não foi entregue e que ele deve chamar a ferramenta novamente. Se seu hook continuar negando, o subagente termina sem entregar um relatório por meio dessa ferramenta, e a conversa que aguarda por ele recebe uma nota informando isso.

Ferramentas que exigem interação do usuário

AskUserQuestion e ExitPlanMode exigem interação do usuário. No modo não interativo com a flag -p, o Claude Code as oferece somente quando a execução tem um host de permissão para receber o prompt, como um callback canUseTool do Agent SDK. Um hook PreToolUse satisfaz esse requisito quando faz o seguinte:
  1. Lê a entrada da ferramenta do stdin
  2. Coleta a resposta por meio da sua própria interface
  3. Retorna permissionDecision: "allow" junto com um updatedInput que contém a resposta, para que a ferramenta seja executada sem solicitar confirmação
Retornar apenas "allow" não é suficiente para essas ferramentas. Para AskUserQuestion, devolva o array questions original e adicione um objeto answers que mapeia o texto de cada pergunta para a resposta escolhida. Esta saída responde a uma pergunta com React:
Uma ferramenta MCP que seu servidor marca com _meta["anthropic/requiresUserInteraction"] é mais restrita: um hook não pode ignorar seu prompt de aprovação com "allow", com ou sem updatedInput, porque o Claude Code não pode confirmar que o hook coletou a interação de que a ferramenta precisa.

Adiar uma chamada de ferramenta para mais tarde

"defer" é para integrações que executam claude -p como um subprocesso e leem sua saída JSON, como um aplicativo do Agent SDK ou uma UI personalizada construída sobre o Claude Code. Ele permite que esse processo chamador pause o Claude em uma chamada de ferramenta, colete entrada por meio de sua própria interface e retome de onde parou. O Claude Code respeita "defer" apenas no modo não interativo com a flag -p, e apenas quando o Claude faz uma única chamada de ferramenta no turno:
  • Em sessões interativas: ele registra um aviso no log e ignora o resultado do hook
  • Várias chamadas de ferramenta de uma vez: "defer" é ignorado com um aviso e a ferramenta prossegue pelo fluxo normal de permissão
A ferramenta AskUserQuestion é o caso típico: o Claude quer perguntar algo ao usuário, mas não há terminal para responder. Uma execução -p oferece AskUserQuestion somente quando tem um host de permissão, como uma ferramenta MCP que você passa com --permission-prompt-tool, portanto inicie a execução com um. O ciclo funciona assim:
  1. O Claude chama AskUserQuestion. O hook PreToolUse é disparado.
  2. O hook retorna permissionDecision: "defer". A ferramenta não é executada. O turno termina com um resultado que carrega stop_reason: "tool_deferred", e a chamada de ferramenta pendente permanece na transcrição.
  3. O processo chamador lê deferred_tool_use do resultado do SDK, exibe a pergunta em sua própria interface e aguarda uma resposta.
  4. O processo chamador executa claude -p --resume <session-id> com o mesmo host de permissão. A mesma chamada de ferramenta dispara o PreToolUse novamente.
  5. O hook retorna permissionDecision: "allow" com a resposta em updatedInput. A ferramenta é executada e o Claude continua.
O campo deferred_tool_use contém o id, o name e o input da ferramenta:
Não há timeout nem limite de novas tentativas. A sessão permanece no disco até que a limpeza por retenção de cleanupPeriodDays a exclua, após 30 dias por padrão. Se a resposta não estiver pronta quando você retomar, o hook pode retornar "defer" novamente. Se você enviou um prompt com essa retomada, o Claude o responde sem o resultado da ferramenta e não executa a chamada. Retome sem um prompt até que o hook retorne "allow" ou "deny".
Para retomar uma sessão adiada no modo de planejamento, passe --permission-prompt-tool junto com --resume para que o Claude Code possa apresentar o plano para aprovação. Para as demais condições, consulte Retomar no modo de planejamento com -p. Requer o Claude Code v2.1.246 ou posterior.Quando você retoma com -p, o Claude Code não restaura nenhum outro modo de permissão armazenado. Ele inicia a execução no modo de permissão em que uma nova execução claude -p iniciaria, então passe --permission-mode ou --dangerously-skip-permissions novamente se a sessão adiada usava um deles.
Retomar quando uma ferramenta adiada não está disponível
Um resultado com stop_reason: "tool_deferred_unavailable" significa que a ferramenta de uma chamada adiada não está disponível na sessão retomada, por exemplo porque seu servidor MCP não está conectado. Seu hook PreToolUse não é disparado, e o campo deferred_tool_use do resultado nomeia a ferramenta. Enquanto a chamada estiver pendente, qualquer prompt que você enviar não chega ao Claude e a execução termina com código 1. O campo result começa com <tool> isn't available, so its deferred call didn't run. Execute a chamada ou descarte-a para que ela não seja executada:
  • Para executar a chamada: torne a ferramenta disponível e retome novamente.
  • Para descartar a chamada: retome com um prompt e com CLAUDE_CODE_DROP_UNAVAILABLE_DEFERRED_TOOL definida como 1. O Claude recebe um erro como resultado da chamada e responde ao seu prompt. Quando você retomar depois, nenhuma chamada estará pendente.
Quando você descarta a chamada, a execução grava o resultado tool_deferred_unavailable com um campo result vazio e, em seguida, a resposta do Claude em um segundo resultado. Ela termina com código 0, a menos que essa resposta seja um erro. Se você definir a variável e não enviar nenhum prompt, a execução ainda descarta a chamada e termina com código 1. Antes da v2.1.319, o Claude Code respondia a um prompt sem o resultado da ferramenta e descartava a chamada. Nessas versões, envie um novo prompt assim que a ferramenta estiver disponível.

PermissionRequest

É executado quando o Claude Code está prestes a pedir sua permissão para usar uma ferramenta. Em sessões que não podem mostrar um prompt, como subagentes em segundo plano no modo não interativo, o Claude Code ainda executa esses hooks e, se nenhum hook retornar uma decisão, nega a chamada de ferramenta. Para uma chamada que chega a um --permission-prompt-tool ou ao callback canUseTool do Agent SDK, os hooks são executados junto com o seu host, e vale a decisão de quem decidir primeiro. Use o controle de decisão do PermissionRequest para permitir ou negar em nome do usuário. Use esse evento quando precisar de um sinal no momento em que o Claude pede permissão para usar uma ferramenta. O Claude Code executa um hook Notification com o tipo permission_prompt somente depois que o prompt fica aguardando por cerca de seis segundos. O Claude Code não executa hooks PermissionRequest para a requisição de rede de um comando em sandbox. Para obter um sinal para esse prompt, use o tipo de notificação permission_prompt. Faz a correspondência com o nome da ferramenta, com os mesmos valores do PreToolUse.

Entrada do PermissionRequest

Os hooks PermissionRequest recebem os campos tool_name e tool_input como os hooks PreToolUse, mas sem tool_use_id. Para uma ferramenta MCP, eles também recebem o objeto mcp_server. Um array opcional permission_suggestions contém as atualizações de permissão que o Claude Code sugere para essa requisição, como adicionar uma regra de permissão allow ou alterar o modo de permissão. O array permission_suggestions não é uma lista exata das opções que você vê, porque cada diálogo de permissão monta as próprias opções. Alguns diálogos, como o de edições de arquivo, não leem o array e derivam suas opções da própria requisição. Um diálogo que o lê ainda pode omitir uma opção cuja sugestão permanece no array, por exemplo quando allowManagedPermissionRulesOnly oculta opções de salvar regras. Ele também pode oferecer opções que não têm entrada de sugestão, como Yes, and switch to auto mode, que altera o modo de permissão diretamente em vez de por meio de uma atualização de permissão. Os hooks PreToolUse são executados antes de cada chamada de ferramenta, precise ela de permissão ou não. Os hooks PermissionRequest são executados apenas quando o Claude Code está prestes a pedir sua permissão, ou quando, de outra forma, negaria automaticamente uma chamada que não pode solicitar confirmação. Nenhum dos dois eventos é disparado para EndConversation.

Controle de decisão do PermissionRequest

Os hooks PermissionRequest podem permitir ou negar requisições de permissão. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar um objeto decision com estes campos específicos do evento: Para permitir ou reescrever um comando Monitor, consulte Corresponder comandos Monitor. Um hook que sai com código 2 sem um objeto decision deixa o fluxo de permissão inalterado, e seu stderr é descartado. Para conceder ou negar a requisição, retorne o objeto decision.

Entradas de atualização de permissão

O campo de saída updatedPermissions e o campo de entrada permission_suggestions usam o mesmo array de objetos de entrada. Cada entrada tem um type que determina seus outros campos, e um destination que controla onde a alteração é gravada. Em rules, para uma regra de permissão escrita como Tool(specifier), toolName é o nome da ferramenta e ruleContent é o especificador:
  • Bash(npm test): { "toolName": "Bash", "ruleContent": "npm test" }
  • Bash ou Bash(*): { "toolName": "Bash" }, porque ambos correspondem à ferramenta inteira
  • mcp__github__get_issue: { "toolName": "mcp__github__get_issue" }
Deixe ruleContent de fora de uma regra allow para uma ferramenta MCP. Se você o adicionar, a regra não corresponde a nenhuma chamada.
setMode com bypassPermissions só tem efeito se você iniciou a sessão com o modo bypass já disponível: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions ou permissions.defaultMode: "bypassPermissions" nas configurações de usuário, de --settings ou gerenciadas. Caso contrário, a atualização não tem efeito. A atualização também não tem efeito quando permissions.disableBypassPermissionsMode desativa o modo, ou quando a sessão é iniciada em modo restrito.bypassPermissions nunca é persistido como defaultMode, independentemente do destination.
O campo destination em cada entrada determina se a alteração permanece na memória ou é persistida em um arquivo de configurações. Um hook pode repetir uma das permission_suggestions que recebeu como sua própria saída updatedPermissions.

PostToolUse

É executado imediatamente após uma ferramenta ser concluída com sucesso. Corresponde ao nome da ferramenta, com os mesmos valores do PreToolUse. Use uma correspondência mais ampla quando o nome da ferramenta não for o filtro certo:
  • Para executar um hook depois que qualquer ferramenta for concluída com sucesso, omita o matcher ou defina-o como "*". Seu hook pode então descobrir por conta própria o que mudou, por exemplo executando git status --porcelain, que também lista arquivos não rastreados que git diff não mostra. Para chamadas de ferramenta que falham, adicione o mesmo hook em PostToolUseFailure.
  • Para executar um hook quando um arquivo específico muda no disco, independentemente de quem o gravou, use FileChanged. O Claude Code não executa um hook PostToolUse com matcher Edit|Write quando um comando Bash ou um processo fora do Claude Code reescreve o mesmo arquivo.

PostToolUse após uma interrupção

Se um hook PostToolUse parar antes do tempo depois que você envia uma mensagem ou interrompe durante uma chamada de ferramenta, verifique se você pressionou Ctrl+Enter, Enter ou Esc:
  • Ctrl+Enter: se pressioná-lo encerra o turno enquanto uma ferramenta está em execução e a ferramenta termina, seus hooks PostToolUse são executados sobre o resultado dela e têm 10 segundos para terminar. A tecla age como Esc se você enfileirou um comando ! antes das suas mensagens ou se as tarefas em segundo plano estão desativadas, por exemplo com CLAUDE_CODE_DISABLE_BACKGROUND_TASKS.
  • Enter: uma mensagem que você enfileira com Enter não interrompe o turno, então os hooks mantêm seu timeout habitual.
  • Esc: você cancela os hooks imediatamente, então envie a mensagem com Enter ou Ctrl+Enter quando os hooks precisarem ser executados.
Quando os 10 segundos acabam, o Claude Code encerra um hook de comando que ainda esteja em execução, seja qual for seu timeout. Para permitir que um hook de comando mais longo termine, execute-o com async: true, o que significa que o hook não pode bloquear nem reescrever o resultado. Antes da v2.1.319, os hooks não eram iniciados depois que você interrompia o turno com uma mensagem, e um hook em execução era interrompido.

Entrada do PostToolUse

Os hooks PostToolUse são disparados depois que uma ferramenta já foi executada com sucesso. A entrada inclui tanto tool_input, os argumentos enviados à ferramenta, quanto tool_response, o resultado que ela retornou. O esquema exato de ambos depende da ferramenta. Os caminhos em tool_input de ferramentas de arquivo chegam no mesmo formato que no PreToolUse: sempre absolutos, com os separadores nativos da plataforma, ou seja, barras invertidas no Windows. Para uma ferramenta MCP, a entrada também traz o objeto mcp_server.

Controle de decisão do PostToolUse

Os hooks PostToolUse podem fornecer feedback ao Claude após a execução da ferramenta. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento: O exemplo abaixo substitui a saída de uma chamada Bash. O valor de substituição corresponde ao formato de saída da ferramenta Bash:
updatedToolOutput altera apenas o que o Claude vê. A ferramenta já foi executada quando o hook é disparado, então quaisquer arquivos gravados, comandos executados ou requisições de rede enviadas já surtiram efeito. A telemetria, como spans de ferramenta do OpenTelemetry e eventos de analytics, também captura a saída original antes de o hook ser executado. Para impedir ou modificar uma chamada de ferramenta antes que ela seja executada, use um hook PreToolUse.O valor de substituição deve corresponder ao formato de saída da ferramenta. As ferramentas integradas retornam objetos estruturados em vez de strings simples. Por exemplo, Bash retorna um objeto com os campos stdout, stderr, interrupted e isImage. Para ferramentas integradas, um valor que não corresponde ao esquema de saída da ferramenta é ignorado e a saída original é usada. A saída de ferramentas MCP é repassada sem validação de esquema. Remover detalhes de erro de que o Claude precisa pode fazer com que ele prossiga com base em uma suposição falsa.

Anotar um resultado para o classificador do modo auto

Retorne classifierContext para enviar uma nota curta sobre o resultado da chamada de ferramenta ao classificador do modo auto em vez de ao Claude. A nota serve para a revisão de ações posteriores pelo classificador. O campo requer Claude Code v2.1.236 ou posterior. O exemplo abaixo informa ao classificador de onde veio a saída de uma consulta:
O peso que o classificador dá à nota depende de onde você configurou o hook:
  • Hooks configurados no Claude Code: para hooks de arquivos de configurações, plugins, skills e frontmatter de agentes, o classificador trata a nota como contexto não verificado fornecido pela aplicação. A nota nunca estabelece a intenção do usuário e, se afirmar que você aprovou ou solicitou algo, o classificador confere essa afirmação com suas próprias mensagens na conversa
  • Callbacks do Agent SDK em processo: quando uma aplicação que incorpora o Claude Code registra o hook como um callback do TypeScript SDK e retorna a nota durante a sessão ativa, o classificador pode considerar uma declaração do usuário transmitida na nota como intenção do usuário. Essa declaração pode satisfazer um requisito de consentimento que o classificador aceitaria de uma mensagem enviada por você, mas nunca suspende um bloqueio que sua própria mensagem também não conseguiria suspender. Depois que uma sessão é retomada, o Claude Code trata as notas restauradas como contexto não verificado. Quando hooks dos dois grupos anotam a mesma chamada, o classificador trata a nota combinada como não verificada
O Claude Code aplica estes limites ao entregar a nota:
  • Tamanho: o Claude Code limita as notas de uma chamada de ferramenta a 2.000 caracteres e trunca o restante. O limite é compartilhado entre todos os hooks que respondem a essa chamada
  • Somente respostas síncronas: o Claude Code ignora o campo na resposta de um hook que é executado em segundo plano, porque essa resposta chega depois que o Claude Code registra o resultado da ferramenta
  • Chamadas que o classificador não registra: a transcrição do classificador omite consultas somente leitura, como leituras de arquivos e buscas. O Claude Code descarta uma nota anexada a uma dessas chamadas
  • Interação com reescritas: quando a nota descreve uma saída que você está substituindo com updatedToolOutput, retorne os dois campos na mesma resposta do hook. O Claude Code descarta a nota se essa reescrita for rejeitada ou se a reescrita de outro hook a substituir. O Claude Code entrega uma nota que você retorna sem reescrita mesmo quando outro hook reescreve a saída
O classificador lê o conteúdo que você coloca em classifierContext como informação vinda da aplicação que hospeda a sessão, portanto não copie saída de ferramenta não confiável nem texto de terceiros para ele. Mantenha a nota como uma afirmação curta sobre esta única chamada, como um fato sobre sua origem ou uma declaração do usuário sobre ela; não use o campo para entregar mensagens não relacionadas ou um fluxo de eventos.

PostToolUseFailure

É executado quando uma ferramenta que começou a ser executada falha: a ferramenta lançou um erro, ou uma ferramenta MCP retornou um resultado de erro. Use-o para registrar falhas em log, enviar alertas ou fornecer feedback corretivo ao Claude. Corresponde ao nome da ferramenta, com os mesmos valores do PreToolUse.
Este evento não é disparado para chamadas de ferramenta rejeitadas antes da execução: um nome de ferramenta desconhecido, uma entrada que falha na validação de esquema ou na validação específica da ferramenta, ou uma negação de permissão. As rejeições de validação são retornadas como resultados tool_use_error e acontecem antes da execução dos hooks, portanto não disparam nem PreToolUse nem PostToolUseFailure. As negações de permissão disparam PreToolUse, mas não este evento; consulte PermissionDenied.

Entrada do PostToolUseFailure

Os hooks PostToolUseFailure recebem os mesmos campos tool_name e tool_input que o PostToolUse, junto com informações de erro como campos de nível superior. Para uma ferramenta MCP, eles também recebem o objeto mcp_server. Por exemplo, um comando npm test que falhou pode entregar:
A string error geralmente é o mesmo texto que o Claude recebe como resultado da ferramenta que falhou. Seu formato varia conforme a ferramenta e a falha. Baseie seu hook em tool_name, is_interrupt e na primeira linha Exit code N; trate o restante da string como texto de exibição, e não como um formato estável.
  • Para Bash e PowerShell, um comando que foi executado e encerrado produz uma primeira linha Exit code N, seguida de qualquer saída que o comando produziu como um único bloco com stdout e stderr intercalados
  • Um payload também pode trazer uma mensagem de falha simples, sem a linha de código de saída, quando o Claude Code não conseguiu iniciar o próprio processo do shell
  • O Claude Code trunca strings longas no meio em torno de um marcador ... [N characters truncated] ... e pode inserir linhas próprias, como Command timed out after 2m 0s

Controle de decisão do PostToolUseFailure

Os hooks PostToolUseFailure podem fornecer contexto ao Claude após a falha de uma ferramenta. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento:

PostToolBatch

É executado uma vez depois que todas as chamadas de ferramenta de um lote foram resolvidas, antes de o Claude Code enviar a próxima requisição ao modelo. PostToolUse é disparado uma vez por ferramenta, o que significa que é disparado de forma concorrente quando o Claude faz chamadas de ferramenta em paralelo. PostToolBatch é disparado exatamente uma vez com o lote completo, então é o lugar certo para injetar contexto que depende do conjunto de ferramentas executadas, e não de uma ferramenta isolada. Não há matcher para este evento.

Entrada do PostToolBatch

Além dos campos de entrada comuns, os hooks PostToolBatch recebem tool_calls, um array que descreve cada chamada de ferramenta do lote:
tool_response contém o mesmo conteúdo que o modelo recebe no bloco tool_result correspondente. O valor é uma string serializada ou um array de blocos de conteúdo, exatamente como a ferramenta o emitiu. Para Read, isso significa texto prefixado com números de linha, e não o conteúdo bruto do arquivo. As respostas podem ser grandes, então analise apenas os campos de que você precisa.
O formato de tool_response difere do de PostToolUse. PostToolUse repassa o objeto Output estruturado da ferramenta, como {filePath: "...", type: "create"} para Write; PostToolBatch repassa o conteúdo serializado de tool_result que o modelo vê.

Controle de decisão do PostToolBatch

Os hooks PostToolBatch podem injetar contexto para o Claude. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento:
Retornar decision: "block" ou continue: false interrompe o loop agêntico antes da próxima chamada ao modelo. A mensagem de bloqueio vem do reason ou stopReason do JSON, ou do stderr na saída com código 2. Você a vê como um aviso na transcrição, e ela permanece na conversa, então o Claude a vê quando a conversa continua.

PermissionDenied

É executado quando o modo auto nega uma chamada de ferramenta, inclusive quando nega sem um veredito do classificador porque uma verificação de segurança separada do modo auto recusou a própria requisição do classificador ou sua resposta não pôde ser analisada. Este hook só é disparado no modo auto: ele não é executado quando você nega manualmente um diálogo de permissão, quando um hook PreToolUse bloqueia uma chamada ou quando uma regra deny corresponde. Use-o para registrar negações em log, ajustar a configuração ou informar ao modelo que ele pode tentar novamente a chamada de ferramenta. Corresponde ao nome da ferramenta, com os mesmos valores do PreToolUse.

Entrada do PermissionDenied

Além dos campos de entrada comuns, os hooks PermissionDenied recebem tool_name, tool_input, tool_use_id e reason. Para uma ferramenta MCP, eles também recebem o objeto mcp_server.

Controle de decisão do PermissionDenied

Os hooks PermissionDenied podem informar ao modelo que ele pode tentar novamente a chamada de ferramenta negada. Retorne um objeto JSON com hookSpecificOutput.retry definido como true:
Quando retry é true, o Claude Code adiciona uma mensagem à conversa informando ao modelo que ele pode tentar novamente a chamada de ferramenta. O próprio Claude Code não reverte a negação. Se o seu hook não retornar JSON, ou retornar retry: false, a negação se mantém e o modelo recebe a mensagem de rejeição original. O Claude Code ignora retry: true quando o classificador não produziu nenhum veredito sobre a ação: sua resposta não pôde ser analisada, ou uma verificação de segurança separada do modo auto recusou a própria requisição do classificador. Para essas negações, o Claude Code já informa ao modelo, na mensagem de rejeição, se deve tentar novamente mais tarde ou seguir adiante.

Notification

É executado quando o Claude Code envia notificações. Corresponde ao tipo de notificação. Omita o matcher para executar hooks para todos os tipos de notificação. Você recebe esses eventos de hook mesmo com as notificações da área de trabalho desativadas: a configuração preferredNotifChannel, incluindo notifications_disabled, altera apenas como você é alertado, e não se o seu hook é executado. Os tipos quota_auto_resume_fired, quota_auto_resume_stale e quota_auto_resume_disabled requerem Claude Code v2.1.234 ou posterior. O tipo auth_storage_failure requer v2.1.288 ou posterior. Em sessões de terminal, permission_prompt para a requisição de rede de um comando em sandbox requer Claude Code v2.1.246 ou posterior. agent_needs_input para a pergunta de configuração do terminal de um colega de equipe requer Claude Code v2.1.248 ou posterior.
Os tipos permission_prompt, idle_prompt, elicitation_dialog e elicitation_url_dialog compartilham o mesmo timing das notificações da área de trabalho, então, em sessões de terminal, você só os vê quando parece estar longe do terminal:
  • Espere permission_prompt depois que você ficar cerca de seis segundos sem digitar. O cronômetro começa quando o prompt de permissão aparece, e cada tecla pressionada o adia. Para executar um hook imediatamente quando o Claude pedir permissão para usar uma ferramenta, use PermissionRequest.
  • Espere idle_prompt cerca de 60 segundos depois que o Claude terminar de responder, e apenas se você não tiver digitado nada desde então e nenhum agente em segundo plano, como um subagente em segundo plano, ainda estiver em execução. O Claude Code não envia idle_prompt enquanto aguarda a redefinição de um limite de uso do claude.ai. Quando a espera termina por conta própria, um dos tipos quota_auto_resume_* é disparado em seu lugar.
  • Espere elicitation_dialog para um formulário de elicitação, ou elicitation_url_dialog para uma requisição de URL no navegador, depois que você ficar cerca de seis segundos sem digitar. Ambos compartilham o mesmo limite de seis segundos do permission_prompt: o cronômetro começa quando o diálogo aparece, e cada tecla pressionada o adia.
Uma requisição de permissão ou elicitação que chega enquanto outro diálogo está na tela mantém o mesmo limite de seis segundos, contado a partir do momento em que a requisição chega. Sua notificação pode chegar a você enquanto a requisição ainda aguarda atrás do diálogo aberto.
O Claude Code calcula o timing de permission_prompt de forma diferente em sessões em que envia requisições de permissão ao callback canUseTool do Agent SDK, que é como o Claude Desktop e a extensão do VS Code hospedam o Claude Code:
  • Espere permission_prompt cerca de seis segundos depois que o Claude pedir permissão. O Claude Code não o adia enquanto você digita.
  • Se você ou um hook PermissionRequest responderem antes, o Claude Code não executa permission_prompt.
  • Defina CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS como 1 para desativar permission_prompt nessas sessões.
Antes da v2.1.233, permission_prompt não era disparado nessas sessões. Use matchers separados para executar handlers diferentes dependendo do tipo de notificação. Esta configuração aciona um script de alerta específico de permissão quando o Claude precisa de aprovação de permissão e uma notificação diferente quando o Claude está ocioso:

Entrada do Notification

Além dos campos de entrada comuns, os hooks Notification recebem message com o texto da notificação, um title opcional e notification_type, que indica qual tipo foi disparado.
Os hooks Notification não podem bloquear nem modificar notificações. O Claude Code descarta seus campos systemMessage e continue, mas ainda emite terminalSequence, que é do que o exemplo de notificação da área de trabalho depende. Os hooks Notification são destinados a efeitos colaterais, como encaminhar a notificação para um serviço externo.

SubagentStart

É executado quando o Claude inicia um subagente com a ferramenta Agent, quando o Claude retoma um subagente e cada vez que um colega de uma equipe de agentes em processo lida com uma nova mensagem. Suporta matchers para filtrar pelo nome do tipo de agente. Para agentes integrados, é o nome do agente, como general-purpose, Explore ou Plan. Para subagentes personalizados, é o campo name do frontmatter do agente, e não o nome do arquivo. Para subagentes fornecidos por um plugin, o tipo de agente é o identificador com escopo de plugin, como my-plugin:reviewer, e não o nome simples do frontmatter. Os dois-pontos colocam um nome com escopo de plugin no caminho de expressão regular, então ancore o matcher com ^ e $ para uma correspondência exata: ^my-plugin:reviewer$.

Entrada do SubagentStart

Além dos campos de entrada comuns, os hooks SubagentStart recebem agent_id com o identificador único do subagente e agent_type com o nome do agente usado pelo filtro do matcher.
Os hooks SubagentStart não podem bloquear a criação de subagentes, mas podem injetar contexto no subagente. Além dos campos de saída JSON disponíveis para todos os hooks, você pode retornar:
Quando o hook é executado novamente para o mesmo subagente, o Claude Code injeta o contexto retornado apenas quando o contexto do subagente ainda não contém a cópia de uma execução anterior. A cópia injetada na inicialização permanece no lugar, mantendo intacto o cache de prompt do subagente. Depois que a compactação automática descarta essa cópia, o Claude Code injeta novamente o contexto da próxima execução.

SubagentStop

É executado quando um subagente do Claude Code termina de responder. Corresponde ao tipo de agente, com os mesmos valores do SubagentStart.

Entrada do SubagentStop

Além dos campos de entrada comuns, os hooks SubagentStop recebem stop_hook_active, agent_id, agent_type, agent_transcript_path e last_assistant_message. O campo agent_type é o valor usado para a filtragem do matcher. O transcript_path é a transcrição da sessão principal, enquanto agent_transcript_path é a transcrição do próprio subagente, armazenada em uma pasta aninhada subagents/. O campo last_assistant_message contém o conteúdo de texto da resposta final do subagente, para que os hooks possam acessá-lo sem analisar o arquivo de transcrição. Nem todo evento SubagentStop vem de um subagente iniciado pelo Claude. O Claude Code também executa agentes internos para alguns de seus próprios recursos, como sugestões de prompt e perguntas paralelas com /btw, e o SubagentStop também é disparado quando um deles termina. Para esses eventos, agent_type é o nome do agente com que a própria sessão é executada, como um definido com --agent ou com a configuração agent, e uma string vazia quando a sessão é executada sem um. Um matcher que nomeia tipos de agente não corresponde a um agent_type vazio. Um hook cujo matcher é omitido, "" ou "*", ou é uma expressão regular que corresponde a uma string vazia, também é executado para eventos com agent_type vazio. No Claude Code v2.1.271 ou posterior, um subagente que é executado com a ferramenta SubagentHandback entrega seu relatório por meio dessa ferramenta antes de parar. O campo last_assistant_message então contém o texto final do subagente, se houver, que não é o relatório entregue. O relatório é a entrada message dessa chamada, que um hook PreToolUse ou PostToolUse com matcher em SubagentHandback recebe como tool_input.message. Os hooks SubagentStop também recebem os arrays background_tasks e session_crons descritos em Entrada do Stop. Ambos os arrays têm como escopo a sessão pai, e não o subagente.
Os hooks SubagentStop usam o mesmo formato de controle de decisão dos hooks Stop, incluindo hookSpecificOutput.additionalContext com hookEventName definido como "SubagentStop", para feedback sem erro que mantém o subagente em execução. Retornar decision: "block" com um reason mantém o subagente em execução e entrega reason ao subagente como sua próxima instrução. Um hook que bloqueia saindo com código 2 entrega sua mensagem de stderr da mesma forma. Para injetar contexto na sessão pai depois que um subagente retorna, use um hook PostToolUse na ferramenta Agent.

TaskCreated

É executado quando uma tarefa está sendo criada por meio da ferramenta TaskCreate. Use-o para impor convenções de nomenclatura, exigir descrições de tarefas ou impedir que certas tarefas sejam criadas. Em uma sessão sem as ferramentas Task, este evento não é disparado. Os hooks TaskCreated não suportam matchers e são disparados em todas as ocorrências.

Entrada do TaskCreated

Além dos campos de entrada comuns, os hooks TaskCreated recebem task_id, task_subject e, opcionalmente, task_description, teammate_name e team_name.

Controle de decisão do TaskCreated

Um hook TaskCreated pode bloquear a criação com o código de saída 2 ou com uma decisão JSON. Em ambos os casos, o Claude Code exclui a tarefa e retorna sua mensagem ao Claude como o erro da ferramenta. O Claude Code ignora continue: false deste evento e o Claude continua trabalhando.
  • Código de saída 2: o Claude Code retorna o texto do stderr como a mensagem.
  • JSON {"decision": "block", "reason": "..."}: o Claude Code retorna reason como a mensagem.
Este exemplo bloqueia tarefas cujos assuntos não seguem o formato exigido:

TaskCompleted

É executado quando uma tarefa está sendo marcada como concluída. Isso é disparado em duas situações: quando qualquer agente marca explicitamente uma tarefa como concluída por meio da ferramenta TaskUpdate, ou quando um colega de uma equipe de agentes termina seu turno com tarefas em andamento. Use-o para impor critérios de conclusão, como testes aprovados ou verificações de lint, antes que uma tarefa possa ser encerrada. Os hooks TaskCompleted não suportam matchers e são disparados em todas as ocorrências.

Entrada do TaskCompleted

Além dos campos de entrada comuns, os hooks TaskCompleted recebem task_id, task_subject e, opcionalmente, task_description, teammate_name e team_name.

Controle de decisão do TaskCompleted

Os hooks TaskCompleted suportam duas formas de controlar a conclusão de tarefas:
  • Código de saída 2: a tarefa não é marcada como concluída e a mensagem do stderr é enviada de volta ao modelo como feedback.
  • JSON {"continue": false, "stopReason": "..."}: quando um colega de equipe terminando seu turno acionou o evento, interrompe o colega de equipe por completo, correspondendo ao comportamento do hook Stop. O stopReason é mostrado ao usuário. Quando a ferramenta TaskUpdate acionou o evento, o Claude Code ignora continue: false; o código de saída 2 ainda bloqueia a conclusão.
Este exemplo executa testes e bloqueia a conclusão da tarefa se eles falharem:

Stop

É executado quando o agente principal do Claude Code termina de responder. Não é executado se a parada ocorreu devido a uma interrupção do usuário. Erros de API disparam StopFailure em seu lugar.
O comando /goal é um atalho integrado para um hook Stop baseado em prompt com escopo de sessão. Use-o quando quiser que o Claude continue trabalhando em direção a uma condição sem escrever configuração de hook.

Entrada do Stop

Além dos campos de entrada comuns, os hooks Stop recebem stop_hook_active, last_assistant_message, background_tasks e session_crons. O campo stop_hook_active é true quando o Claude Code já está continuando como resultado de um hook Stop. Verifique esse valor ou processe a transcrição para evitar bloquear em uma condição que nunca será resolvida. O Claude Code aplica um limite de 8 continuações consecutivas: depois que os hooks Stop continuaram o turno oito vezes seguidas, o Claude Code sobrescreve o próximo bloqueio e encerra o turno. A contagem de continuações consecutivas é redefinida cada vez que o Claude chama uma ferramenta. Para aumentar o limite, defina CLAUDE_CODE_STOP_HOOK_BLOCK_CAP. O campo last_assistant_message contém o conteúdo de texto da resposta final do Claude, para que os hooks possam acessá-lo sem analisar o arquivo de transcrição. Para hooks que atuam sobre o turno recém-concluído, como hooks de leitura em voz alta ou de notificação, use este campo em vez de ler transcript_path: não há garantia de que o arquivo de transcrição inclua a mensagem final no momento do Stop em todas as versões. Os arrays background_tasks e session_crons permitem que os hooks diferenciem “a sessão terminou” de “a sessão está pausada aguardando que um trabalho em segundo plano a desperte novamente”. Ambos os arrays estão presentes quando o registro de tarefas está acessível e ficam vazios quando nada está em andamento ou agendado. Cada entrada em background_tasks descreve uma tarefa em andamento e usa estes campos: Cada entrada em session_crons descreve um despertar agendado com escopo de sessão, proveniente de CronCreate, ScheduleWakeup e /loop: Este exemplo mostra uma entrada do Stop com uma tarefa de shell em andamento e um cron recorrente:

Controle de decisão do Stop

Os hooks Stop e SubagentStop podem controlar se o Claude continua. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento: Um hook que bloqueia saindo com código 2 é encaminhado da mesma forma que reason: o Claude recebe a mensagem do stderr como a explicação de por que deve continuar.
Use additionalContext quando o hook estiver funcionando como projetado e dando orientação ao Claude, como “execute a suíte de testes antes de terminar”. Ele mantém a conversa em andamento com as mesmas proteções contra loop de decision: "block", a saber, a entrada stop_hook_active e o limite de 8 continuações consecutivas, mas a transcrição o rotula como Stop hook feedback e nenhuma notificação de erro de hook é exibida:

StopFailure

É executado em vez do Stop quando o turno termina devido a um erro de API. O Claude Code ignora a saída e o código de saída do hook, exceto terminalSequence. Use-o para registrar falhas em log, enviar alertas ou tomar ações de recuperação quando o Claude não consegue concluir uma resposta devido a rate limits, problemas de autenticação ou outros erros de API.

Entrada do StopFailure

Além dos campos de entrada comuns, os hooks StopFailure recebem error, error_details opcional e last_assistant_message opcional. O campo error identifica o tipo de erro e é usado para a filtragem do matcher.
Os hooks StopFailure não têm controle de decisão. Eles são executados apenas para fins de notificação e log.

TeammateIdle

É executado quando um colega de uma equipe de agentes está prestes a ficar ocioso depois de terminar seu turno. Use-o para impor critérios de qualidade antes que um colega de equipe pare de trabalhar, como exigir que as verificações de lint passem ou verificar se os arquivos de saída existem. Os hooks TeammateIdle não suportam matchers e são disparados em todas as ocorrências.

Entrada do TeammateIdle

Além dos campos de entrada comuns, os hooks TeammateIdle recebem teammate_name e team_name.

Controle de decisão do TeammateIdle

Os hooks TeammateIdle suportam duas formas de controlar o comportamento do colega de equipe:
  • Código de saída 2: o colega de equipe recebe a mensagem do stderr como feedback e continua trabalhando em vez de ficar ocioso.
  • JSON {"continue": false, "stopReason": "..."}: interrompe o colega de equipe por completo, correspondendo ao comportamento do hook Stop. O stopReason é mostrado ao usuário.
Este exemplo verifica se um artefato de build existe antes de permitir que um colega de equipe fique ocioso:

ConfigChange

É executado quando um arquivo de configuração muda durante uma sessão. Use-o para auditar alterações de configurações, impor políticas de segurança ou bloquear modificações não autorizadas em arquivos de configuração. O Claude Code executa hooks ConfigChange quando um arquivo de configurações, um arquivo de política gerenciada ou um arquivo de skill muda. Para política gerenciada, ele os executa somente quando managed-settings.json ou um arquivo em managed-settings.d/ muda. O matcher filtra pela origem da configuração: Alterações como estas entram em vigor sem executar os hooks:
  • Regras e configurações que você salva com Yes, and don’t ask again, /permissions ou /model, na sessão a partir da qual você as salvou. Se outra sessão aberta carregar o mesmo arquivo, os hooks são executados lá.
  • Atualizações nas configurações gerenciadas pelo servidor, nas preferências gerenciadas do macOS ou na política do registro do Windows
  • No WSL com wslInheritsWindowsSettings, uma alteração no arquivo de configurações gerenciadas do lado do Windows, que o Claude Code detecta em sua verificação periódica de políticas
Este exemplo registra em log as alterações de configuração para auditoria de segurança:

Entrada do ConfigChange

Além dos campos de entrada comuns, os hooks ConfigChange recebem source e, opcionalmente, file_path. O campo source indica qual tipo de configuração mudou, e file_path fornece o caminho do arquivo específico que foi modificado.

Controle de decisão do ConfigChange

Os hooks ConfigChange podem impedir que alterações de configuração entrem em vigor. Use o código de saída 2 ou uma decision JSON para impedir a alteração. Quando bloqueadas, as novas configurações não são aplicadas à sessão em execução.
Alterações de policy_settings não podem ser bloqueadas. Os hooks ainda são disparados para origens policy_settings quando um arquivo de configurações gerenciadas na máquina muda, então você pode usá-los para registrar essas edições em log, mas qualquer decisão de bloqueio é ignorada. Isso garante que as configurações gerenciadas pela empresa sempre entrem em vigor. O Claude Code não executa hooks ConfigChange quando configurações gerenciadas pelo servidor chegam ou são atualizadas. O Claude Code age com base na decisão de bloqueio da saída JSON de um hook ConfigChange e descarta systemMessage e continue. Uma alteração bloqueada não exibe nenhuma mensagem para você nem para o Claude, quer você bloqueie com reason ou com stderr na saída com código 2. O Claude Code apenas grava uma linha no log de depuração.

CwdChanged

É executado quando um comando de shell na conversa principal altera o diretório de trabalho, por exemplo quando o Claude executa um comando cd. Use-o para reagir a mudanças de diretório: recarregar variáveis de ambiente, ativar toolchains específicas do projeto ou executar scripts de configuração automaticamente. Combina com FileChanged para ferramentas como o direnv, que gerenciam o ambiente por diretório. Hooks CwdChanged têm acesso a CLAUDE_ENV_FILE. Se seu hook gravar variáveis nesse arquivo, elas persistem nos comandos Bash subsequentes até o próximo evento CwdChanged, quando o Claude Code as limpa. O CwdChanged não suporta matchers e é disparado em todas as ocorrências.

Entrada do CwdChanged

Além dos campos de entrada comuns, os hooks CwdChanged recebem old_cwd e new_cwd.

Saída do CwdChanged

Além dos campos de saída JSON disponíveis para todos os hooks, os hooks CwdChanged podem retornar watchPaths para definir dinamicamente quais caminhos de arquivo o FileChanged observa: Os hooks CwdChanged não têm controle de decisão. Eles não podem bloquear a mudança de diretório. O Claude Code lê watchPaths e systemMessage da saída JSON deles e descarta continue. Em sessões interativas, ele mostra o systemMessage como uma breve notificação no terminal. A mensagem não chega ao fluxo de mensagens do SDK.

DirectoryAdded

É executado depois que você adiciona um diretório de trabalho no meio da sessão com o comando /add-dir, ou depois que um cliente SDK adiciona um com a requisição de controle register_repo_root. Use-o para preparar um repositório recém-adicionado, por exemplo instalando suas dependências. O Claude Code não dispara este evento quando:
  • Você passa um diretório com a flag de inicialização --add-dir; o SessionStart cobre esses diretórios
  • Você adiciona um diretório na aba Workspace de /permissions
  • Você adiciona um diretório que já é um diretório de trabalho ou está dentro de um
O Claude Code dispara o DirectoryAdded depois de atualizar o estado do sandbox e das permissões, então as ferramentas em sandbox já veem o novo diretório quando seu hook é executado. Os próprios comandos de hook são executados fora do sandbox. O Claude Code não espera pelo hook: a adição é concluída imediatamente, e o hook é executado em segundo plano com o timeout padrão de 600 segundos. O matcher filtra pela forma como o diretório foi adicionado:

Entrada do DirectoryAdded

Além dos campos de entrada comuns, os hooks DirectoryAdded recebem directory e source.
Os hooks DirectoryAdded não têm controle de decisão. Eles não podem bloquear a adição, que já foi concluída quando o hook é executado. O Claude Code descarta o campo continue da saída JSON deles e trata o restante de forma diferente conforme a origem:
  • slash_command: o Claude Code entrega o systemMessage do hook ao Claude como contexto no próximo turno da conversa, em vez de mostrá-lo a você. Uma contagem de hooks com falha aparece na transcrição. A saída completa das falhas vai para o log de depuração
  • register_repo_root: o Claude Code grava a saída de systemMessage e a saída das falhas apenas no log de depuração

FileChanged

É executado quando um arquivo monitorado muda no disco. O Claude Code detecta alterações com um monitor do sistema de arquivos, não inspecionando chamadas de ferramenta, então ele executa o hook independentemente do que alterou o arquivo: uma chamada de ferramenta Edit ou Write, um script que o Claude executa com Bash ou um processo totalmente fora do Claude Code. Um uso comum é recarregar variáveis de ambiente quando arquivos de configuração do projeto mudam. O matcher deste evento tem duas funções:
  • Montar a lista de monitoramento: o valor é dividido em | e cada segmento é registrado como um nome de arquivo literal no diretório de trabalho, então ".envrc|.env" monitora exatamente esses dois arquivos. Padrões regex não são úteis aqui: um valor como ^\.env monitoraria um arquivo chamado literalmente ^\.env.
  • Filtrar quais hooks são executados: quando um arquivo monitorado muda, o mesmo valor filtra quais grupos de hooks são executados usando as regras de matcher padrão em relação ao nome base do arquivo alterado.
Este exemplo normaliza as quebras de linha em data.csv após qualquer alteração, incluindo um comando Bash ou um script externo reescrevendo o arquivo:
O hook lê o caminho absoluto do arquivo alterado no campo file_path da entrada JSON em stdin. Sua verificação com grep testa a mesma coisa que o perl remove, um CR no final de uma linha, então a execução após uma normalização termina sem tocar no arquivo. Uma verificação mais permissiva entra em loop infinito, porque perl -i reescreve o arquivo mesmo quando não substitui nada e o Claude Code executa o hook novamente após cada reescrita. Salve este script em /path/to/normalize-line-endings.sh e torne-o executável:
Para confirmar que o hook funciona, peça ao Claude para acrescentar uma linha CRLF a data.csv com um comando Bash. O Claude Code executa o hook e o arquivo termina com quebras de linha LF. Para monitorar arquivos que você não pode nomear de antemão, retorne watchPaths de um hook para atualizar a lista de monitoramento dinamicamente. O Claude Code inicia o monitor somente quando algo nomeia um arquivo a ser monitorado, então inicialize a lista com um grupo FileChanged cujo matcher nomeie pelo menos um arquivo, ou com um hook SessionStart ou CwdChanged que retorne watchPaths. O matcher ainda filtra quais grupos de hooks são executados quando um arquivo monitorado muda, então deixe sem matcher o grupo que lida com caminhos dinâmicos, o que corresponde a todos os arquivos monitorados e não adiciona nada à lista de monitoramento. Um matcher "*" também corresponde a todos os arquivos, mas o Claude Code o registra na lista de monitoramento como qualquer outro valor, como um arquivo literal chamado *. Os hooks FileChanged têm acesso a CLAUDE_ENV_FILE. As variáveis gravadas nesse arquivo persistem nos comandos Bash subsequentes até o próximo evento CwdChanged, quando o Claude Code as limpa.

Entrada do FileChanged

Além dos campos de entrada comuns, os hooks FileChanged recebem file_path e event.

Saída do FileChanged

Além dos campos de saída JSON disponíveis para todos os hooks, os hooks FileChanged podem retornar watchPaths para atualizar dinamicamente quais caminhos de arquivo são monitorados: Os hooks FileChanged não têm controle de decisão. Eles não podem impedir que a alteração do arquivo ocorra. O Claude Code lê watchPaths e systemMessage da saída JSON deles e descarta continue. Em sessões interativas, ele mostra a systemMessage como uma breve notificação no terminal. A mensagem não chega ao fluxo de mensagens do SDK.

WorktreeCreate

É executado quando o Claude Code cria um worktree em casos como estes: Um hook WorktreeCreate não é executado para um worktree que o Claude adiciona via Bash, e o hook de um plugin não é executado para aquele criado quando você inicia com --worktree e --tmux:
  • git worktree add via Bash: para inspecionar ou bloquear esse comando, adicione um hook PreToolUse com o matcher Bash.
  • --worktree com --tmux: o Claude Code cria o worktree antes de carregar os plugins, então um hook WorktreeCreate de um plugin não é executado. Para que seu hook seja executado com ambas as flags, defina-o em um arquivo de configurações. Antes da v2.1.300, o hook WorktreeCreate de um plugin também não era executado apenas com --worktree.
Por padrão, o Claude Code cria a cópia de trabalho isolada com git worktree. Configurar um hook WorktreeCreate substitui esse comportamento padrão do git, permitindo que você use um sistema de controle de versão diferente, como SVN, Perforce ou Mercurial. Como o hook substitui totalmente o comportamento padrão, .worktreeinclude não é processado. Se você precisar copiar arquivos de configuração locais como .env para o novo worktree, faça isso dentro do seu script de hook. O hook deve retornar o caminho para o diretório do worktree criado. O Claude Code usa esse caminho como diretório de trabalho para a sessão isolada. Consulte Saída do WorktreeCreate para saber como cada tipo de hook retorna o caminho. O Claude Code age com base no sucesso do hook e no caminho retornado, e descarta systemMessage e continue. Este exemplo cria uma cópia de trabalho SVN e imprime o caminho para o Claude Code usar. Substitua a URL do repositório pela sua:
O hook lê o name do worktree da entrada JSON em stdin, faz checkout de uma cópia nova em um novo diretório e imprime o caminho do diretório. O echo na última linha é o que o Claude Code lê como caminho do worktree. Redirecione qualquer outra saída para stderr para que ela não interfira no caminho.

Entrada do WorktreeCreate

Além dos campos de entrada comuns, os hooks WorktreeCreate recebem o campo name. Este é um identificador slug para o novo worktree, especificado pelo usuário ou gerado automaticamente, por exemplo bold-oak-a3f2.

Saída do WorktreeCreate

Os hooks WorktreeCreate não usam o modelo padrão de decisão permitir/bloquear. Em vez disso, o sucesso ou a falha do hook determina o resultado. O hook deve retornar o caminho para o diretório do worktree criado:
  • Hooks de comando (type: "command"): imprima o caminho como a última linha não vazia de stdout. O Claude Code remove códigos de escape ANSI antes de ler essa linha, então banners de inicialização do shell impressos antes do seu echo são ignorados. Redirecione qualquer outra saída do hook para stderr.
  • Hooks HTTP (type: "http"): retorne { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } } no corpo da resposta.
Se o hook falhar ou não produzir nenhum caminho, a criação do worktree falha com um erro. O Claude Code resolve um caminho relativo em relação ao diretório em que o hook foi executado, reduzindo quaisquer segmentos . ou .. nele. Se o caminho resultante não for um diretório em que o Claude Code possa entrar, a sessão imprime um erro indicando o caminho e sai com código 1. O Claude Code recusa um caminho absoluto que contenha segmentos . ou .., e qualquer caminho que passe por um link simbólico abaixo da raiz do repositório. O erro indica o componente rejeitado. Um link simbólico incluído em um commit no repositório poderia redirecionar o worktree para fora dele. Retorne um caminho normalizado que não passe por um link simbólico dentro do repositório.

WorktreeRemove

É executado quando o Claude Code limpa um worktree que o seu hook WorktreeCreate criou. O evento é disparado quando:
  • Você sai de uma sessão de worktree interativa e escolhe remover o worktree quando o Claude Code solicita
  • Você sai de uma sessão de worktree interativa que não nomeou, o Claude Code não encontra arquivos alterados ou não rastreados e remove o worktree sem solicitar confirmação
  • Você exclui uma sessão em segundo plano que é executada no worktree
  • Você pede ao Claude para sair e remover o worktree durante uma sessão, e o Claude o remove com a ferramenta ExitWorktree
  • Uma sessão que claude remote-control iniciou no modo worktree termina sem falhar, e o Claude Code não encontra arquivos alterados ou não rastreados nem novos commits em seu worktree
  • Você interrompe claude remote-control enquanto sessões que ele iniciou no modo worktree ainda estão em execução, e o Claude Code não encontra arquivos alterados ou não rastreados nem novos commits em seus worktrees
Quando um subagente ou um agente de fluxo de trabalho termina, o Claude Code mantém o worktree que o seu hook WorktreeCreate criou para ele e não executa o seu hook WorktreeRemove. Remova esses worktrees você mesmo quando o trabalho estiver concluído. O Claude Code não solicita confirmação antes de o seu hook ser executado para uma chamada ExitWorktree ou uma limpeza do claude remote-control. ExitWorktree se recusa a remover um worktree criado por hook, a menos que a chamada passe discard_changes: true, o que torna o seu hook a última verificação nesse caminho. O Claude Code usa o git para procurar arquivos alterados ou não rastreados, então não encontra nenhum em um worktree que não seja um checkout git nem esteja dentro de um, mesmo quando o diretório contém trabalho sem commit. Verifique esse trabalho no seu hook WorktreeRemove antes que ele exclua qualquer coisa. Para worktrees baseados em git, o Claude Code lida com a limpeza automaticamente com git worktree remove. Se você configurou um hook WorktreeCreate, combine-o com um hook WorktreeRemove para controlar a limpeza:
  • Sem hook WorktreeRemove: o Claude Code recorre ao git para uma remoção ao sair ou por meio de ExitWorktree, e mantém o worktree quando o claude remote-control faz a limpeza:
    • Você sai de uma sessão de worktree, ou o Claude chama ExitWorktree: o Claude Code remove o caminho que o seu hook WorktreeCreate retornou como git worktree remove --force faria, então um worktree que o git reconhece é removido. Um worktree que o git não reconhece, por exemplo um que o seu hook criou com um sistema de controle de versão que não seja git, permanece no disco.
    • claude remote-control limpa o worktree de uma sessão: o worktree permanece no disco, e o terminal que executa claude remote-control mostra worktree removal failed, kept: <path>.
    • Você exclui uma sessão em segundo plano: consulte O que a exclusão de uma sessão remove.
  • O hook termina com 0: o worktree conta como removido. O Claude Code não lê mais nada do hook, então certifique-se de que o seu hook excluiu o diretório.
  • O hook termina com código diferente de zero: a remoção falha se o diretório em worktree_path ainda existir depois, e o worktree permanece no disco sem fallback para o git. Um hook que excluiu o diretório antes de terminar com código diferente de zero conta como removido. Para saber como a falha é relatada, consulte Entrada do WorktreeRemove.
O Claude Code nunca exclui um branch pertencente a um worktree criado por hook, porque ele só conhece o caminho que seu hook WorktreeCreate retornou. Se seu hook WorktreeCreate criar um branch, exclua-o em seu hook WorktreeRemove. O Claude Code descarta os campos de saída JSON de um hook WorktreeRemove, como systemMessage e continue. Para a exclusão de uma sessão em segundo plano, o Claude Code verifica o caminho do worktree armazenado antes de executar o hook, recusando um caminho que seja um link simbólico ou passe por um abaixo da raiz do repositório. Nesse caminho, o hook é executado para um worktree que ainda contém arquivos somente quando você confirma a exclusão na visualização de agentes. Para esse worktree, claude rm mantém a sessão e o worktree. O Claude Code passa o caminho retornado pelo WorktreeCreate como worktree_path na entrada do hook. Este exemplo lê esse caminho e remove o diretório:

Entrada do WorktreeRemove

Além dos campos de entrada comuns, os hooks WorktreeRemove recebem o campo worktree_path, que é o caminho absoluto para o worktree sendo removido.
O código de saída de um hook WorktreeRemove decide o resultado. Quando um hook termina com código diferente de zero e o diretório em worktree_path ainda existe depois, a remoção falha:
  • O worktree permanece no disco, e o comando e o stderr do hook vão para o log de depuração.
  • Se você estava excluindo uma sessão em segundo plano, a sessão também permanece. A mensagem de recusa na visualização de agentes informa como o hook terminou, como exited 1, cita o início do seu stderr e diz se excluir a sessão novamente remove o diretório mesmo assim.
  • Se o claude remote-control estava limpando o worktree de uma sessão, o terminal que o executa mostra worktree removal failed, kept: <path>.

PreCompact

É executado antes de o Claude Code executar uma operação de compactação. O valor do matcher indica se a compactação foi acionada manual ou automaticamente: Termine com código 2 para bloquear a compactação. Para um /compact manual, a mensagem de stderr é mostrada ao usuário. Você também pode bloquear retornando JSON com "decision": "block". Bloquear a compactação automática tem efeitos diferentes dependendo de quando ela é disparada. Se a compactação foi acionada proativamente antes do limite de contexto, o Claude Code a ignora e a conversa continua sem compactação. Se a compactação foi acionada para se recuperar de um erro de limite de contexto já retornado pela API, o erro subjacente aparece e a requisição atual falha. O Claude Code descarta os campos systemMessage e continue de um hook PreCompact.

Entrada do PreCompact

Além dos campos de entrada comuns, os hooks PreCompact recebem trigger e custom_instructions. Para manual, custom_instructions contém o que o usuário passa para /compact e é null quando ele não passa nada. Para auto, custom_instructions é null.

Saída do PreCompact

Quando um hook PreCompact termina com 0 e não bloqueia, o Claude Code usa seu stdout como instruções para o resumo da compactação. Mantenha esse stdout abaixo de 10.000 caracteres por hook. O Claude Code corta saídas mais longas no limite e acrescenta um marcador.

PostCompact

É executado depois que o Claude Code conclui uma operação de compactação. Use este evento para reagir ao novo estado compactado, por exemplo para registrar em log o resumo gerado ou atualizar um estado externo. O Claude Code descarta os campos systemMessage e continue de um hook PostCompact. Os mesmos valores de matcher se aplicam como no PreCompact:

Entrada do PostCompact

Além dos campos de entrada comuns, os hooks PostCompact recebem trigger e compact_summary. O campo compact_summary contém o resumo da conversa gerado pela operação de compactação.
Os hooks PostCompact não têm controle de decisão. Eles não podem afetar o resultado da compactação, mas podem executar tarefas de acompanhamento.

PreModelSwitch

É executado antes de o Claude Code aplicar uma troca de modelo que você ou um cliente solicitou. Use-o para bloquear uma troca, exigir confirmação ou mostrar quanto a troca custará antes que ela aconteça. O PreModelSwitch requer o Claude Code v2.1.251 ou posterior. O Claude Code o executa para estas solicitações:
  • /model <name> e o seletor do /model
  • O seletor de modelo Option+P ou Alt+P
  • A configuração Model em /config
  • Ativar o modo rápido quando isso altera o modelo da sessão
  • Uma requisição set_model, ou uma alteração de modelo em uma requisição apply_flag_settings, de um host do Agent SDK ou do Remote Control
O Claude Code não executa hooks PreModelSwitch para trocas que ele faz por conta própria, como um fallback automático de modelo ou a restauração do modelo quando você retoma uma sessão. Essas alterações chegam apenas ao PostModelSwitch. O Claude Code compara o matcher com o nome canônico do modelo para o qual a sessão está mudando, ignorando qualquer sufixo [1m]. Um alias como opus, um ID de modelo com data e um ID específico de provedor, como um ID de modelo do Amazon Bedrock, correspondem todos ao único nome canônico para o qual são resolvidos, então claude-opus-5 cobre todas as grafias do Opus 5. Quando o Claude Code não consegue determinar um nome canônico para o destino, por exemplo um ID de modelo personalizado que só o seu gateway de LLM conhece, ele executa todos os hooks PreModelSwitch independentemente do matcher. Um hook que bloqueia deve, portanto, verificar to_model na sua entrada em vez de confiar apenas no matcher. Escreva o matcher como um nome exato, uma lista separada por | como claude-opus-4-6|claude-opus-5, ou uma expressão regular como .*opus.*. Este exemplo usa um matcher de nome exato e também verifica to_model na entrada do hook, então recusa uma troca para o Opus 4.6 terminando com código 2 e deixa passar qualquer outro destino:
O comando verifica to_model com jq:
Para confirmar que o hook funciona, execute /model claude-opus-4-6 a partir de uma sessão que esteja executando um modelo diferente. O Claude Code mantém o modelo atual e informa que um hook PreModelSwitch bloqueou a troca, com a sua mensagem como motivo.

Entrada do PreModelSwitch

Além dos campos de entrada comuns, os hooks PreModelSwitch recebem os campos desta tabela. Os últimos cinco descrevem quanto custa reenviar a conversa para o novo modelo, para que um hook possa mostrar esse valor antes que a troca aconteça. Este exemplo mostra a entrada para /model opus em uma sessão que executa o Sonnet 5:

Controle de decisão do PreModelSwitch

Os hooks PreModelSwitch podem cancelar a troca, pedir ao usuário que a confirme ou deixá-la prosseguir. O código de saída 2 ou um decision: "block" de nível superior cancela a troca. Para um controle mais refinado, retorne permissionDecision e permissionDecisionReason em um objeto hookSpecificOutput, como no PreToolUse. O PreModelSwitch aceita "allow", "deny" e "ask". Ele não aceita "defer", updatedInput nem additionalContext. A tabela abaixo descreve ambos os campos: Somente o /model em uma sessão interativa pode mostrar o prompt de "ask". Em todas as outras superfícies, incluindo o modo não interativo com a flag -p, /config e requisições set_model, o Claude Code trata "ask" como uma recusa. Este exemplo pede ao usuário que confirme e cita a contagem de tokens de context_tokens:
Quando vários hooks PreModelSwitch retornam decisões diferentes, a precedência é deny > ask > allow. O Claude Code mostra ao usuário qualquer systemMessage que o seu hook retorne, independentemente da decisão, então um hook de relatório de custos pode retornar {"systemMessage": "..."} e terminar com 0.

Timeouts e outros códigos de saída do PreModelSwitch

Um hook PreModelSwitch que não responde antes do seu timeout bloqueia a troca. O timeout padrão para este evento é de 30 segundos. Um código de saída diferente de 0 ou 2 sem saída JSON válida é um erro não bloqueante, então a troca prossegue, exceto para hooks cuja falha a bloqueia.

PostModelSwitch

É executado depois que o modelo da sessão muda. Use-o para dar ao Claude orientações específicas do modelo sem editar cada CLAUDE.md, por exemplo uma instrução para toda a organização que se aplica a determinados modelos. O PostModelSwitch requer o Claude Code v2.1.251 ou posterior. Ele não pode bloquear, porque o modelo já mudou. O Claude Code executa hooks PostModelSwitch após qualquer uma destas alterações:
  • Uma troca que você ou um cliente solicitou
  • Um fallback automático de modelo, que altera o modelo da sessão
  • Uma configuração como opusplan entrando ou saindo do modo de planejamento
  • O Claude Code restaurando o modelo quando você retoma uma sessão
O Claude Code não executa hooks PostModelSwitch quando um modelo de uma cadeia de modelos de fallback atende um turno, porque essa substituição dura um turno e deixa o modelo da sessão inalterado. O matcher segue as mesmas regras do PreModelSwitch: o Claude Code o compara com o nome canônico do modelo para o qual a sessão mudou. Este exemplo adiciona orientações sempre que o modelo da sessão muda para qualquer modelo Opus:
Para confirmar que o hook funciona, mude para um modelo Opus a partir de uma sessão que esteja executando um modelo diferente, por exemplo execute /model opus em uma sessão do Sonnet, e depois pergunte ao Claude quais orientações ele tem sobre o modelo atual.

Entrada do PostModelSwitch

Os hooks PostModelSwitch recebem os mesmos campos que o PreModelSwitch, com hook_event_name definido como "PostModelSwitch" e mais dois valores de source: "auto" para um fallback automático ou outra alteração que o Claude Code fez por conta própria, e "resume" para o modelo restaurado quando você retoma uma sessão. requested_model é null quando source é "auto". Quando source é "resume", é a configuração de modelo salva que o Claude Code restaurou.

Controle de decisão do PostModelSwitch

O Claude Code pega o stdout em texto simples do seu hook ao terminar com 0, ou additionalContext da saída JSON, e o entrega ao Claude com a próxima requisição após a troca. Além dos campos de saída JSON disponíveis para todos os hooks, você pode retornar: Se o hook não tiver terminado dentro de cinco segundos após você enviar o próximo prompt, o Claude Code envia essa requisição sem a saída e a anexa à requisição seguinte. Se o modelo mudar várias vezes antes da próxima requisição, o Claude Code entrega apenas a saída correspondente ao modelo de destino da última troca.

SessionEnd

É executado quando uma sessão do Claude Code termina. Útil para tarefas de limpeza, registro de estatísticas da sessão ou salvamento do estado da sessão. Suporta matchers para filtrar pelo motivo de saída. O campo reason na entrada do hook indica por que a sessão terminou:

Entrada do SessionEnd

Além dos campos de entrada comuns, os hooks SessionEnd recebem um campo reason indicando por que a sessão terminou. Consulte a tabela de motivos acima para todos os valores.
Os hooks SessionEnd não têm controle de decisão. Eles não podem bloquear o encerramento da sessão, mas podem executar tarefas de limpeza. O Claude Code descarta os campos de saída JSON deles, como systemMessage. Os hooks SessionEnd têm um timeout padrão de 1,5 segundo. Ele se aplica quando você sai, executa /clear ou troca de sessão com o /resume interativo. Você pode dar mais tempo a um hook de duas maneiras:
  • timeout por hook: defina timeout na configuração desse hook. O orçamento geral aumenta automaticamente para corresponder ao maior timeout por hook nos seus arquivos de configurações, até 60 segundos. Se você aumentar o orçamento dessa forma, um hook sem seu próprio timeout ainda mantém o padrão. Timeouts definidos em hooks fornecidos por plugins não aumentam o orçamento.
  • CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: defina esta variável de ambiente em milissegundos para sobrescrever o orçamento explicitamente. O valor que você definir também se torna o timeout de cada hook sem seu próprio timeout.
Este exemplo define o orçamento para 5 segundos:
Antes da v2.1.268, CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS aumentava apenas o orçamento geral, e um hook sem seu próprio timeout ainda era cancelado após 1,5 segundo.

Elicitation

É executado quando um servidor MCP solicita entrada do usuário no meio de uma tarefa. Por padrão, o Claude Code mostra um diálogo interativo para o usuário responder. Os hooks podem interceptar essa solicitação e responder programaticamente, ignorando totalmente o diálogo. Para um hook completo com sua entrada de configurações e script, consulte Responder a uma solicitação de formulário a partir de um script. O campo matcher corresponde ao nome do servidor MCP.

Entrada do Elicitation

Além dos campos de entrada comuns, os hooks Elicitation recebem mcp_server_name, message e os campos opcionais mode, url, elicitation_id e requested_schema. Para elicitação no modo de formulário, o caso mais comum:
Para elicitação no modo URL, usada para autenticação baseada em navegador:

Saída do Elicitation

Um hook Elicitation pode responder à solicitação pelo usuário, recusá-la ou cancelá-la, ou deixá-la para o diálogo. Para responder, recusar ou cancelar, termine com 0 e imprima um objeto hookSpecificOutput com uma action. O servidor recebe a sua resposta e nenhum diálogo aparece. Cada linha desta tabela mostra o que retornar para um resultado e o que o servidor MCP recebe: Esta saída responde à solicitação no modo de formulário mostrada em Entrada do Elicitation. As chaves em content são os nomes de propriedades do requested_schema dessa solicitação:
Esta saída recusa uma solicitação:
Para uma solicitação no modo URL, um hook que retorna accept ignora o diálogo, então a URL nunca é aberta. O Claude Code descarta reason, systemMessage e continue da saída JSON de um hook Elicitation, seja qual for a action que você retornar.

Outras maneiras de recusar uma elicitação

O seu hook também pode recusar destas maneiras. O servidor recebe o mesmo decline que para "action": "decline":
  • Termina com código 2: o Claude Code ignora um hookSpecificOutput impresso pelo mesmo hook
  • Imprime um "decision": "block" de nível superior: o bloqueio sobrescreve uma action na mesma saída
Quando vários hooks correspondem à mesma solicitação, uma recusa de um deles sobrescreve um accept ou cancel de outro. Este script recusa solicitações no modo URL e deixa as solicitações de formulário para o diálogo:
Nem o usuário nem o servidor veem por que o seu hook recusou, porque o Claude Code não mostra o seu stderr nem o seu reason. O Claude Code ignorou um decision de nível superior dos hooks Elicitation e ElicitationResult da v2.1.105 até a correção na v2.1.284.

Responder a uma solicitação de formulário a partir de um script

Este exemplo responde a uma pergunta recorrente pelo usuário. Um servidor MCP chamado issue-tracker pede uma chave de projeto em um formulário, e o hook preenche DOCS. O script aceita quando project_key é o único campo do formulário. Para qualquer outra solicitação, ele não imprime nada, então o diálogo aparece.
Registre um hook de comando para o evento no seu arquivo de configurações, com o nome do servidor como matcher:
Salve este script em .claude/hooks/answer-project-key.sh no seu projeto e torne-o executável com chmod +x:
Para confirmar que o hook funciona, inicie o Claude Code com claude --debug e dê ao Claude uma tarefa que faça o servidor pedir a chave do projeto. Nenhum diálogo aparece, e o log de depuração tem uma linha que termina com Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}.

ElicitationResult

É executado depois que um usuário responde a uma elicitação MCP. Os hooks podem observar, modificar ou bloquear a resposta antes que ela seja enviada de volta ao servidor MCP. Quando um hook Elicitation responde a uma solicitação, o Claude Code envia essa resposta ao servidor sem executar hooks ElicitationResult. O campo matcher corresponde ao nome do servidor MCP.

Entrada do ElicitationResult

Além dos campos de entrada comuns, os hooks ElicitationResult recebem mcp_server_name, action e os campos opcionais mode, elicitation_id e content.

Saída do ElicitationResult

Um hook ElicitationResult pode deixar a resposta do usuário passar, alterar seus valores ou bloqueá-la. Para alterar ou bloquear a resposta, termine com 0 e imprima um objeto hookSpecificOutput com uma action. Cada linha desta tabela mostra o que retornar para um resultado e o que o servidor MCP recebe: Esta saída altera a resposta mostrada em Entrada do ElicitationResult, então o servidor recebe alice@example.com onde o usuário enviou alice:
O seu content substitui todo o objeto content do usuário, então inclua os campos que você não está alterando. Retorne action junto com ele, porque o Claude Code ignora um hookSpecificOutput que não tenha action. Os hooks ElicitationResult também são executados quando o usuário recusa ou cancela, e a sua action substitui a dele. Verifique se a action da entrada é accept antes de retornar accept, ou o seu hook transformará uma solicitação recusada em uma aceita. Este script faz a mesma alteração quando o usuário aceitou, mantém os outros campos e não imprime nada caso contrário:
Esta saída bloqueia a resposta:
O código de saída 2 e um "decision": "block" de nível superior também bloqueiam a resposta. Outras maneiras de recusar uma elicitação aborda qual deles tem efeito quando um hook os combina, o que o usuário vê e quais versões ignoravam decision. O Claude Code descarta reason, systemMessage e continue da saída JSON de um hook ElicitationResult, seja qual for a action que você retornar.

Hooks baseados em prompt

Além de hooks de comando, HTTP e MCP tool, Claude Code suporta hooks baseados em prompt (type: "prompt") que usam um LLM para avaliar se deve permitir ou bloquear uma ação, e hooks de agente (type: "agent") que geram um verificador agentic com acesso a ferramentas. Nem todos os eventos suportam cada tipo de hook. Eventos que suportam todos os cinco tipos de hook (command, http, mcp_tool, prompt e agent):
  • PermissionDenied
  • PostToolBatch
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • TeammateIdle
  • UserPromptExpansion
  • UserPromptSubmit
PermissionRequest suporta hooks command, http, mcp_tool e prompt mas não hooks agent. Se você configurar um hook de agente neste evento, Claude Code o ignora e o fluxo de permissão prossegue inalterado. Para permitir ou negar de um hook, retorne o objeto de decisão de um hook de comando ou HTTP. Eventos que suportam hooks command, http e mcp_tool mas não prompt ou agent:
  • ConfigChange
  • CwdChanged
  • DirectoryAdded
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • MessageDisplay
  • Notification
  • PostCompact
  • PostModelSwitch
  • PreCompact
  • PreModelSwitch
  • SessionEnd
  • StopFailure
  • SubagentStart
  • WorktreeCreate
  • WorktreeRemove
SessionStart e Setup suportam hooks command e mcp_tool, e MCP tool hook fields descreve quando seus hooks mcp_tool são executados. Eles não suportam hooks http, prompt ou agent.

Como hooks baseados em prompt funcionam

Em vez de executar um comando Bash, hooks baseados em prompt:
  1. Enviam a entrada do hook e seu prompt para um modelo Claude, por padrão aquele que Claude Code usa para funcionalidade em segundo plano
  2. O LLM responde com JSON estruturado contendo uma decisão
  3. Claude Code processa a decisão automaticamente

Configuração de hook de prompt

Defina type para "prompt" e forneça uma string prompt em vez de um command. Use o placeholder $ARGUMENTS para injetar dados de entrada do hook em seu texto de prompt. Em um hook de prompt ou de agente, você pode escrever o prompt como uma regra sobre o que bloquear ou permitir, como “Bloquear qualquer comando Bash que leia arquivos .env”, ou como uma condição que deve ser verdadeira, como “Todos os testes unitários passam”. Este hook Stop pede ao LLM para avaliar se todas as tarefas estão completas antes de permitir que Claude termine:

Esquema de resposta

O LLM deve responder com JSON contendo:
O que acontece em ok: false depende do evento:
  • Stop e SubagentStop: a razão é alimentada de volta para Claude como sua próxima instrução e o turno continua, a menos que a resposta também defina impossible: true, caso em que Claude Code permite a parada e o turno termina
  • PreToolUse: a chamada de ferramenta é negada; por padrão o turno termina e a razão de negação aparece no chat como uma linha de aviso. Defina continueOnBlock: true para em vez disso retornar a razão para Claude como o erro da ferramenta para que possa se ajustar e continuar, equivalente a um hook de comando com permissionDecision: "deny"
  • PostToolUse: por padrão o turno termina e a razão aparece no chat como uma linha de aviso. Defina continueOnBlock: true para alimentar a razão de volta para Claude e continuar o turno em vez disso
  • PostToolBatch, UserPromptSubmit e UserPromptExpansion: o turno termina e a razão aparece como uma linha de aviso. Esses eventos terminam o turno em decision: "block" independentemente de continue
  • PostToolUseFailure e TaskCreated: a razão é retornada para Claude como um erro de ferramenta e o turno continua, independentemente de continueOnBlock
  • TaskCompleted: quando dispara porque uma tarefa é marcada como concluída durante um turno, a razão é retornada para Claude como um erro de ferramenta e o turno continua, independentemente de continueOnBlock. Quando dispara porque um colega para, se comporta como TeammateIdle e interrompe o colega por padrão
  • TeammateIdle: por padrão o colega para e a razão aparece como uma linha de aviso. Defina continueOnBlock: true para alimentar a razão de volta para o colega e mantê-lo trabalhando em vez disso
  • PermissionRequest: ok: false não tem efeito. Para negar uma aprovação de um hook, use um hook de comando retornando hookSpecificOutput.decision.behavior: "deny"
  • PermissionDenied: ok: false não tem efeito porque a negação já aconteceu. A única saída que este evento lê é hookSpecificOutput.retry, que hooks de prompt e agente não podem definir. Eles são executados neste evento, mas sua saída é descartada. Use um hook de comando para retornar retry
Se você precisar de controle mais fino em qualquer evento, use um hook de comando com os campos por evento descritos em Controle de decisão.

Verificar múltiplas condições antes de parar

Este hook Stop usa um prompt detalhado para verificar três condições antes de permitir que Claude pare. Hooks SubagentStop usam o mesmo formato para avaliar se um subagente deve parar. Se o modelo retornar "ok": false porque a condição ainda não foi atendida, Claude continua trabalhando com a razão fornecida como sua próxima instrução:

Hooks baseados em agente

Hooks de agente são experimentais. O comportamento e a configuração podem mudar em versões futuras. Para fluxos de trabalho em produção, prefira command hooks.
Hooks baseados em agente (type: "agent") são como hooks baseados em prompt mas com acesso a ferramentas de múltiplos turnos. Em vez de uma única chamada LLM, um hook de agente gera um subagente que pode ler arquivos, pesquisar código e inspecionar o codebase para verificar condições. Hooks de agente suportam os mesmos eventos que hooks baseados em prompt, exceto PermissionRequest.

Como hooks de agente funcionam

Quando um hook de agente dispara:
  1. Claude Code gera um subagente com seu prompt e a entrada JSON do hook
  2. O subagente pode usar ferramentas como Read, Grep e Glob para investigar
  3. Após até 50 turnos, o subagente retorna uma decisão estruturada { "ok": true/false }
  4. Claude Code permite a ação se ok for true. Se ok for false, Claude Code trata o bloqueio da mesma forma que um hook de prompt com continueOnBlock: true naquele evento, conforme listado em Response schema
Hooks de agente são úteis quando a verificação requer inspecionar arquivos reais ou saída de teste, não apenas avaliar dados de entrada do hook sozinhos.

Configuração de hook de agente

Defina type para "agent" e forneça uma string prompt, usando $ARGUMENTS como placeholder para a entrada JSON do hook. Os campos de configuração são os mesmos que prompt hooks, exceto que hooks de agente têm um timeout padrão mais longo de 60 segundos e nenhum campo continueOnBlock. O esquema de resposta é { "ok": true } para permitir ou { "ok": false, "reason": "..." } para bloquear. Em ok: false, Claude Code trata um hook de agente da forma que trata um prompt hook com continueOnBlock: true no mesmo evento; hooks de agente não têm campo continueOnBlock e não suportam o campo impossible do hook de prompt. Este hook Stop verifica que todos os testes unitários passam antes de permitir que Claude termine:

Executar hooks em background

Por padrão, hooks bloqueiam a execução de Claude até que completem. Para tarefas de longa duração como deployments, suites de teste ou chamadas de API externas, defina "async": true para executar o hook em background enquanto Claude continua trabalhando. Hooks assíncronos não podem bloquear ou controlar comportamento de Claude: campos de resposta como decision, permissionDecision e continue não têm efeito, porque a ação que controlariam já completou.

Configurar um hook assíncrono

Adicione "async": true à configuração de um hook de comando para executá-lo em background sem bloquear Claude. Este campo está apenas disponível em hooks type: "command". Este hook executa um script de teste após cada chamada de ferramenta Write. Claude continua trabalhando imediatamente enquanto run-tests.sh executa. Quando o script termina, sua saída é entregue no próximo turno de conversa:
Uma vez que um hook assíncrono está executando em background, Claude Code não impõe timeout nele. Claude Code ainda impõe timeout em um hook que você executa com asyncRewake. Claude Code entrega resultados de um hook assíncrono apenas enquanto a sessão está em execução:
  • Em modo não-interativo com a flag -p, Claude Code mata qualquer hook assíncrono ainda em execução no teardown e o finaliza com resultado cancelled
  • Se o trabalho do seu hook deve sobreviver a uma sessão claude -p, inicie um processo totalmente desacoplado a partir dele

Como hooks assíncronos executam

Quando um hook assíncrono dispara, Claude Code inicia o processo do hook e imediatamente continua sem esperar que termine. O hook recebe a mesma entrada JSON via stdin que um hook síncrono. Após o processo em background sair, Claude Code entrega os campos additionalContext e systemMessage da resposta JSON do hook ao Claude no próximo turno de conversa, exceto para saída repetida de SessionStart. Diferentemente de um systemMessage de hook síncrono, nenhum dos dois campos é mostrado para você. Imprima a resposta JSON sozinha no stdout ou em uma linha própria:
  • Sozinha no stdout: quando a resposta é o único texto no stdout, ela pode ocupar várias linhas, como a saída formatada do jq. Ocupar várias linhas requer Claude Code v2.1.295 ou posterior.
  • Em uma linha própria: um hook assíncrono pode imprimir outro texto no stdout quando a resposta cabe sozinha em uma linha, por exemplo com jq -c.
Claude Code valida essa resposta JSON contra o mesmo esquema de saída que hooks síncronos, e descarta qualquer campo cujo valor tenha o tipo errado, como um systemMessage que não seja uma string, em vez de entregá-lo. Execute com --debug para ver um aviso nomeando cada campo descartado. Notificações de conclusão de hook assíncrono são suprimidas por padrão. Para vê-las, ative modo verbose com Ctrl+O ou inicie Claude Code com --verbose.

Executar testes após mudanças de arquivo

Este hook inicia uma suite de testes em background sempre que Claude escreve um arquivo, então relata os resultados de volta ao Claude quando os testes terminam. Salve este script em .claude/hooks/run-tests-async.sh em seu projeto e torne-o executável com chmod +x:
Então adicione esta configuração a .claude/settings.json na raiz do seu projeto. A flag async: true permite que Claude continue trabalhando enquanto testes executam:

Limitações

Hooks assíncronos têm restrições adicionais comparados a hooks síncronos:
  • Saída de hook é entregue no próximo turno de conversa. Se a sessão está ociosa, a resposta espera até a próxima interação do usuário. Exceção: um hook asyncRewake que sai com código 2 acorda Claude imediatamente mesmo quando a sessão está ociosa.
  • Cada execução cria um processo em background separado.
  • Um hook SessionStart assíncrono é executado toda vez que o evento dispara. Claude Code ignora a saída do hook quando o texto de additionalContext e systemMessage corresponde ao que Claude recebeu por último desse hook, a menos que um hook SessionStart síncrono tenha adicionado outro texto desde então. Claude Code entrega o texto quando ele muda, e novamente na próxima vez que o hook for executado após uma compactação ou /clear. Antes da v2.1.295, Claude Code adicionava o mesmo texto à conversa novamente a cada retomada.

Considerações de segurança

Aviso

Hooks de comando executam comandos shell com suas permissões completas de usuário. Eles podem modificar, deletar ou acessar qualquer arquivo que sua conta de usuário pode acessar. Revise e teste todos os comandos de hook antes de adicioná-los à sua configuração.

Confiança do workspace

Claude Code verifica a confiança do workspace antes de executar qualquer hook de um arquivo de configurações. O que conta como confiável depende do tipo de sessão:
  • Sessão interativa: Claude Code retém hooks de todos os arquivos de configurações, incluindo seu próprio ~/.claude/settings.json, até que você aceite o diálogo de confiança do workspace para a pasta, ou para um diretório pai cuja confiança se estende a ela
  • Sessão -p ou SDK: Claude Code nunca mostra o diálogo e trata a pasta como confiável, então hooks confirmados no .claude/settings.json de um repositório são executados em uma pasta que você nunca confiou
Antes de executar claude -p em um repositório que você não escreveu, revise seus arquivos de configurações .claude/, comece com --bare, ou desative hooks para essa execução com --settings '{"disableAllHooks": true}'. Hooks de frontmatter em um subagente de projeto seguem uma regra mais rigorosa do que hooks de arquivo de configurações. O que é executado antes de você confiar em uma pasta lista cada tipo de conteúdo de repositório por tipo de sessão.

Melhores práticas de segurança

Mantenha essas práticas em mente ao escrever hooks:
  • Valide e sanitize entradas: nunca confie em dados de entrada cegamente
  • Sempre cite variáveis shell: use "$VAR" não $VAR
  • Bloqueie traversal de caminho: verifique .. em caminhos de arquivo
  • Use caminhos absolutos: especifique caminhos completos para scripts. Na forma exec, use ${CLAUDE_PROJECT_DIR} e o caminho não precisa de aspas. Na forma shell, envolva-o em aspas duplas
  • Pule arquivos sensíveis: evite .env, .git/, chaves, etc.

Ferramenta Windows PowerShell

No Windows, você pode executar hooks individuais em PowerShell definindo "shell": "powershell" em um hook de comando. Claude Code auto-detecta pwsh.exe, o executável do PowerShell 7 e posterior, e volta para powershell.exe para Windows PowerShell 5.1.

Referenciar scripts por caminho em hooks do PowerShell

Para referenciar o diretório raiz do projeto a partir de um comando em forma de shell do PowerShell, escreva ${CLAUDE_PROJECT_DIR} ou $env:CLAUDE_PROJECT_DIR. Claude Code reescreve os espaços reservados ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} e ${CLAUDE_PLUGIN_DATA} em um comando em forma de shell do PowerShell para a forma ${env:NAME} do PowerShell, independentemente de o hook estar definido em settings.json, um plugin ou uma skill. PowerShell então resolve o valor do ambiente exportado após análise, então o espaço reservado funciona dentro de strings entre aspas duplas, mas não dentro de strings entre aspas simples, onde PowerShell nunca expande variáveis. Não escreva a forma nua $CLAUDE_PROJECT_DIR em um hook do PowerShell. PowerShell a analisa como uma variável local indefinida e a resolve para $null, o que deixa o caminho do script sem seu prefixo de diretório raiz do projeto. Claude Code não reescreve essa forma; em vez disso, registra um aviso no log de depuração. O exemplo abaixo mostra um hook settings.json que executa um script de projeto com a forma $env::

Debug de hooks

Detalhes de execução de hook são escritos no arquivo de log de debug. Inicie Claude Code com claude --debug-file <path> para escrever o log em um local conhecido, ou execute claude --debug e leia o log em ~/.claude/debug/<session-id>.txt. A flag --debug não imprime no terminal. Por exemplo, um hook PostToolUse em Write cujo comando imprime hook-ran produz entradas como:
Para encontrar um hook lento, pesquise no log por linhas Hooks: que terminam com uma duração. No Claude Code v2.1.296 ou posterior, cada hook de comando em um evento de ferramenta, UserPromptSubmit, SessionStart, Stop e vários outros eventos deixa uma dessas linhas quando termina, independentemente do que tenha impresso. A linha fornece o nome do evento, unido por dois-pontos ao nome da ferramenta ou a outro valor com o qual o hook correspondeu, depois o comando do hook entre colchetes, o plugin de onde ele veio, se houver, como a execução terminou e quanto tempo levou, como em Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms). Uma execução também pode terminar como timed out after <N>ms, cancelled, moved to the background ou failed to start. Em alguns eventos, como Notification, SessionEnd e PreCompact, um hook de comando deixa, em vez disso, uma linha completed with status sem duração. Para detalhes de correspondência de hook mais granulares, defina CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose para ver linhas de log adicionais como contagens de matcher de hook e correspondência de consulta. Para troubleshooting de problemas comuns como hooks não disparando, Stop hooks que continuam bloqueando, ou erros de configuração, consulte Limitações e troubleshooting no guia. Para um passo a passo de diagnóstico mais amplo cobrindo /context, /doctor e precedência de configurações, consulte Debug your config.