SpyBara
Go Premium

hooks.md 2026-10-07 23:59 UTC to 2026-10-08 21:58 UTC

This page contains 534 additions and 365 deletions.

2026
Thu 1 23:59 Fri 2 22:59 Sat 3 23:57 Sun 4 23:58 Mon 5 23:58 Tue 6 23:59 Wed 7 23:59 Thu 8 22:58

Referência de hooks

Referência para eventos de hooks do Claude Code, esquema de configuração, formatos de entrada/saída JSON, códigos de saída, hooks assíncronos, hooks HTTP, hooks de prompt e hooks de ferramentas MCP.

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 é transmitido
<img src="https://mintcdn.com/claude-code/x7pO8l4XcvAXCoVc/images/hooks-lifecycle-dark.svg?fit=max&auto=format&n=x7pO8l4XcvAXCoVc&q=85&s=c9b3d88487335f58cce0b52e2f9e7531" className="hidden dark:block" alt="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 é transmitido" width="520" height="1336" data-path="images/hooks-lifecycle-dark.svg" />

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.

Evento Quando dispara
SessionStart Quando uma sessão começa ou é retomada
Setup Quando você inicia Claude Code com --init-only, ou com --init ou --maintenance no modo -p. Para preparação única em CI ou scripts
UserPromptSubmit Quando um prompt é enviado, antes de Claude processá-lo. Também dispara em turnos que Claude Code inicia por conta própria
UserPromptExpansion Quando um comando digitado pelo usuário se expande em um prompt, antes de chegar a Claude. Pode bloquear a expansão
PreToolUse Antes de uma chamada de ferramenta ser executada. Pode bloqueá-la
PermissionRequest Quando uma chamada de ferramenta precisa de uma decisão de permissão
PermissionDenied Quando o modo automático nega uma chamada de ferramenta, incluindo negações sem um veredicto do classificador. Use JSON hookSpecificOutput.retry: true para informar ao modelo que ele pode tentar novamente a chamada de ferramenta negada. Claude Code ignora retry quando o classificador não produziu veredicto
PostToolUse Depois que uma chamada de ferramenta é bem-sucedida
PostToolUseFailure Depois que uma chamada de ferramenta falha
PostToolBatch Depois que um lote completo de chamadas de ferramenta paralelas é resolvido, antes da próxima chamada do modelo
Notification Quando Claude Code envia uma notificação
MessageDisplay Enquanto o texto da mensagem do assistente está sendo exibido
SubagentStart Quando um subagente é criado
SubagentStop Quando um subagente termina
TaskCreated Quando uma tarefa está sendo criada via TaskCreate
TaskCompleted Quando uma tarefa está sendo marcada como concluída
Stop Quando Claude termina de responder
StopFailure Quando a rodada termina devido a um erro de API
TeammateIdle Quando um colega de equipe de agentes está prestes a ficar ocioso
InstructionsLoaded Quando um arquivo CLAUDE.md ou .claude/rules/*.md é carregado no contexto. Dispara no início da sessão e quando os arquivos são carregados lentamente durante uma sessão
ConfigChange Quando um arquivo de configuração muda durante uma sessão
CwdChanged Quando o diretório de trabalho muda, por exemplo quando Claude executa um comando cd. Útil para gerenciamento reativo do ambiente com ferramentas como direnv
DirectoryAdded Quando um diretório de trabalho é adicionado no meio da sessão via /add-dir ou a solicitação de controle SDK register_repo_root
FileChanged Quando um arquivo observado muda no disco. O campo matcher especifica quais nomes de arquivo observar
WorktreeCreate Quando um worktree está sendo criado via --worktree, isolation: "worktree", ou para uma sessão em segundo plano. Substitui o comportamento padrão do git
WorktreeRemove Quando um worktree que um hook WorktreeCreate criou está sendo removido
PreCompact Antes da compactação de contexto
PostCompact Depois que a compactação de contexto é concluída
PreModelSwitch Antes de Claude Code aplicar uma mudança de modelo que você ou um cliente solicitou. Pode bloquear a mudança
PostModelSwitch Depois que o modelo da sessão muda, incluindo mudanças que Claude Code faz por conta própria, como restaurar o modelo quando você retoma uma sessão
Elicitation Quando um servidor MCP solicita entrada do usuário durante uma chamada de ferramenta
ElicitationResult Depois que um usuário responde a uma elicitação MCP, antes da resposta ser enviada de volta ao servidor
SessionEnd Quando uma sessão é encerrada

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 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:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"args": []
}
]
}
]
}
}

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:

#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0  # no decision; normal permission flow applies
fi

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:

{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
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:

{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}

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 dispara, como "apenas 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.

Locais de hooks

Onde você define um hook determina seu escopo:

Local Escopo Compartilhável
~/.claude/settings.json Todos os seus projetos Não, local para sua máquina
.claude/settings.json Projeto único Sim, pode ser confirmado no repositório
.claude/settings.local.json Projeto único Não, gitignored quando Claude Code salva uma configuração nele
Configurações de política gerenciada Organização inteira Sim, controlado por administrador
Plugin hooks/hooks.json Quando o plugin está ativado Sim, agrupado com o plugin
Frontmatter de Skill O resto da sessão uma vez que a skill é invocada. Consulte Hooks em skills e agentes Sim, definido no arquivo da skill
Frontmatter de Subagent Enquanto esse subagente está em execução Sim, definido no arquivo do subagente

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, fileSuggestion e subagentStatusLine à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:

Valor do matcher Avaliado como Exemplo
"*", "" ou omitido Corresponder a todos dispara em cada ocorrência do evento
Apenas letras, dígitos, _, -, espaços, , e | String exata ou lista de strings exatas separadas por | ou , com espaço em branco opcional ao redor Bash corresponde apenas à ferramenta Bash; Edit|Write e Edit, Write cada um corresponde a qualquer ferramenta exatamente; code-reviewer corresponde apenas a esse tipo de agente
Contém qualquer outro caractere Expressão regular JavaScript, não ancorada ^Notebook corresponde a qualquer ferramenta cujo nome começa com Notebook; mcp__memory__.* corresponde a cada ferramenta do servidor memory

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:

Evento O que o matcher filtra Valores de matcher de exemplo
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied nome da ferramenta Bash, Edit|Write, mcp__.*
SessionStart como a sessão começou startup, resume, clear, compact, fork
Setup qual sinalizador CLI acionou a configuração init, maintenance
SessionEnd por que a sessão terminou clear, resume, logout, prompt_input_exit, other
Notification tipo de notificação permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_url_dialog, elicitation_complete, elicitation_response, agent_needs_input, agent_completed, quota_auto_resume_fired, quota_auto_resume_stale, quota_auto_resume_disabled
SubagentStart tipo de agente general-purpose, Explore, Plan, nomes de agentes personalizados ou nomes com escopo de plugin como ^my-plugin:reviewer$
PreCompact, PostCompact o que acionou a compactação manual, auto
PreModelSwitch, PostModelSwitch nome canônico do modelo para o qual a sessão muda, conforme descrito em PreModelSwitch claude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.*
SubagentStop tipo de agente mesmos valores que SubagentStart
ConfigChange fonte de configuração user_settings, project_settings, local_settings, policy_settings, skills
CwdChanged sem suporte a matcher sempre dispara em cada ocorrência
DirectoryAdded como o diretório foi adicionado slash_command, register_repo_root
FileChanged nomes de arquivo literais para monitorar (consulte FileChanged) .envrc|.env
StopFailure tipo de erro rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error, unknown
InstructionsLoaded razão de carregamento session_start, nested_traversal, path_glob_match, include, compact
UserPromptExpansion nome do comando seus nomes de skill ou comando
Elicitation nome do servidor MCP seus nomes de servidor MCP configurados
ElicitationResult nome do servidor MCP mesmos valores que Elicitation
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay sem suporte a matcher sempre dispara em cada ocorrência

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:

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

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:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

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.

Todos os hooks correspondentes executam em paralelo. 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 uma 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.

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:

Campo Obrigatório Descrição
type sim "command", "http", "mcp_tool", "prompt" ou "agent"
if não Sintaxe de regra de permissão para filtrar quando este hook executa, como "Bash(git *)" ou "Edit(*.ts)". O comando do hook apenas é executado se a chamada de ferramenta corresponde ao padrão. Consulte a tabela de correspondência Bash para saber como padrões Bash são avaliados contra subcomandos, $() e backticks. Apenas avaliado em eventos de ferramenta: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest e PermissionDenied. Em outros eventos, um hook com if definido nunca executa
timeout não Segundos antes de cancelar. Claude Code não o impõe em um hook de comando que você executa com async: true. Padrões: 600 para command, http e mcp_tool; 30 para prompt; 60 para agent. Claude Code reduz o padrão de command, http e mcp_tool para 30 em UserPromptSubmit, PreModelSwitch e PostModelSwitch, e para 10 em MessageDisplay. Hooks de SessionEnd compartilham um orçamento de 1,5 segundo; se suas configurações definirem um timeout por hook mais longo, Claude Code aumenta o orçamento para corresponder, até 60 segundos
statusMessage não Mensagem de spinner personalizada exibida enquanto o hook executa
once não Se true, Claude Code remove o hook após sua primeira execução bem-sucedida. Uma execução que falha, bloqueia com código de saída 2 ou expira deixa o hook em vigor, então ele executa novamente no próximo evento correspondente. Apenas honrado para hooks declarados em frontmatter de skill; ignorado em arquivos de configurações e frontmatter de agente

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/**)". Antes de v2.1.214, "Edit(src/**)" correspondia a um diretório nomeado src em qualquer profundidade sob o diretório de trabalho.

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.

padrão if Comando Bash Hook executa? Por quê
Bash(git *) FOO=bar git push sim atribuições iniciais são removidas; git push corresponde
Bash(git *) npm test && git push sim cada subcomando é verificado; git push corresponde
Bash(rm *) echo $(rm -rf /) sim comandos dentro de $() e backticks são verificados; rm -rf / corresponde
Bash(rm *) echo $(date) não nenhum subcomando corresponde a rm *
Bash(git push *) echo $(date) sim padrões que especificam mais do que o nome do comando executam o hook mesmo assim em $(), backticks ou $VAR

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:

Campo Obrigatório Descrição
command sim Comando shell a executar. Com args, o executável a gerar diretamente. Consulte Forma exec e forma shell
args não Lista de argumentos. Quando presente, command é resolvido como um executável e gerado diretamente com args como o vetor de argumentos, sem shell envolvido. Consulte Forma exec e forma shell
async não Se true, executa em background sem bloquear. Consulte Executar hooks em background
asyncRewake não Se true, executa em background e acorda Claude na saída do código 2. O stderr do hook, ou stdout se stderr estiver vazio, é mostrado ao Claude como um lembrete do sistema para que possa reagir a uma falha de background de longa duração
shell não Shell a usar para este hook. Aceita "bash" ou "powershell". Padrão é "bash", ou "powershell" no Windows quando Git Bash não está instalado. Definir "powershell" executa o comando via PowerShell no Windows. Não requer CLAUDE_CODE_USE_POWERSHELL_TOOL já que hooks geram PowerShell diretamente. Ignorado quando args é definido
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.

Este exemplo executa um script Node agrupado com um plugin. A forma exec passa o caminho do script resolvido como um argumento sem aspas:

{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]
}

A forma shell equivalente precisa de aspas para lidar com caminhos com espaços ou caracteres especiais:

{
  "type": "command",
  "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"
}

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.*}.

Campos de hook HTTP

Além dos campos comuns, hooks HTTP aceitam esses campos:

Campo Obrigatório Descrição
url sim URL para enviar a solicitação POST
headers não Cabeçalhos HTTP adicionais como pares chave-valor. Valores suportam interpolação de variável de ambiente usando sintaxe $VAR_NAME ou ${VAR_NAME}. Apenas variáveis listadas em allowedEnvVars são resolvidas
allowedEnvVars não Lista de nomes de variáveis de ambiente que podem ser interpoladas em valores de cabeçalho. Referências a variáveis não listadas são substituídas por strings vazias. Obrigatório para qualquer interpolação de variável de ambiente funcionar

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:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/pre-tool-use",
            "timeout": 30,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

Campos de hook de ferramenta MCP

Além dos campos comuns, hooks de ferramenta MCP aceitam esses campos:

Campo Obrigatório Descrição
server sim Nome de um servidor MCP configurado. Para um servidor fornecido por plugin, este é o nome com escopo plugin:<plugin-name>:<server-name>, como plugin:my-plugin:db, não a chave do servidor simples
tool sim Nome da ferramenta a chamar naquele servidor
input não Argumentos passados para a ferramenta. Valores de string suportam substituição ${path} da entrada JSON do hook, como "${tool_input.file_path}"

Este exemplo chama a ferramenta security_scan no servidor MCP my_server após cada Write ou Edit, passando o caminho do arquivo editado:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "my_server",
            "tool": "security_scan",
            "input": { "file_path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}
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.

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:

Campo Obrigatório Descrição
prompt sim Texto do prompt a enviar para o modelo. Use $ARGUMENTS como placeholder para a entrada JSON do hook. Escape com uma barra invertida para incluir texto literal: \$1.00 renderiza como $1.00
model não Modelo a usar para avaliação. Padrão para o modelo que Claude Code usa para funcionalidade em background

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.

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": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",
"args": []
}
]
}
]
}
}

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:

---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---

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. Antes de v2.1.218, esses hooks podiam executar de pastas que você não confiava.

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.

Campo Descrição
session_id Identificador de sessão atual
prompt_id UUID identificando o prompt do usuário sendo processado atualmente. Corresponde ao atributo prompt.id em eventos OpenTelemetry, para que você possa correlacionar saída de hook com telemetria para um único prompt. Ausente até a primeira entrada do usuário
transcript_path Caminho para JSON de conversa. O arquivo de transcrição é escrito de forma assíncrona e pode ficar atrás da conversa na memória, portanto pode não incluir ainda as mensagens mais recentes do turno atual quando um hook dispara. Hooks que precisam do texto final do assistente do turno atual devem usar last_assistant_message em Stop e SubagentStop em vez de ler a transcrição
cwd Diretório de trabalho atual quando o hook é invocado
scratchpad_dir Caminho para o diretório scratchpad da sessão, onde Claude mantém arquivos de trabalho temporários. Ausente quando a sessão não tem scratchpad ou o diretório temporário não está disponível. Requer Claude Code v2.1.257 ou posterior
permission_mode Modo de permissão atual: "default", "plan", "acceptEdits", "auto", "dontAsk" ou "bypassPermissions". O modo rotulado Manual chega como "default", nunca como "manual", portanto scripts que correspondem a "default" continuam funcionando. Nem todos os eventos recebem este campo. Verifique o exemplo JSON em cada seção evento de hook
effort Objeto com um campo level contendo o nível de esforço em vigor quando o hook é executado: "low", "medium", "high", "xhigh" ou "max". Se você definir um nível que o modelo ativo não suporta, level relata o nível que Claude Code executou em vez disso; Ajustar nível de esforço diz como ele escolhe esse nível. O objeto corresponde ao campo effort da linha de status. Presente para eventos que disparam dentro de um contexto de uso de ferramenta, como PreToolUse, PostToolUse, Stop e SubagentStop, quando o modelo atual suporta o parâmetro de esforço. O nível também está disponível para comandos de hook e a ferramenta Bash como a variável de ambiente $CLAUDE_EFFORT.
hook_event_name Nome do evento que disparou

Ao executar com --agent ou dentro de um subagente, dois campos adicionais são incluídos:

Campo Descrição
agent_id Identificador único para o subagente. Presente apenas quando o hook dispara dentro de uma chamada de subagente. Use isso para distinguir chamadas de hook de subagente de chamadas de thread principal.
agent_type Nome do agente (por exemplo, "Explore" ou "security-reviewer"). Presente quando a sessão usa --agent ou o hook dispara dentro de um subagente. Para subagentes, o tipo do subagente tem precedência sobre o valor --agent da sessão. Consulte SubagentStart para os valores que subagentes personalizados e de plugin relatam e como escrever um matcher contra um nome com escopo de plugin.

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, além das variáveis exportadoras OTEL_* que Claude Code remove de cada subprocesso que spawna e, quando CLAUDE_CODE_SUBPROCESS_ENV_SCRUB é definido como 1, as 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:

{
  "session_id": "abc123",
  "prompt_id": "550e8400-e29b-41d4-a716-446655440000",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "scratchpad_dir": "/tmp/claude-1000/-home-user-my-project/abc123/scratchpad",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite",
    "timeout": 120000,
    "run_in_background": false
  },
  "tool_use_id": "toolu_01ABC123..."
}

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

O código de saída do seu comando de hook diz ao Claude Code se a ação deve prosseguir, ser bloqueada ou ser ignorada. O código de saída não atua sozinho. Claude Code lê campos de saída JSON de stdout em cada código de saída, não apenas 0, e para eventos que usam o modelo de decisão padrão, um objeto analisado que passa na validação de esquema entra em vigor ao lado do código. O bloqueio da saída 2 é o único resultado que JSON não pode substituir.

Duas tabelas possuem as exceções por evento: Comportamento de código de saída 2 por evento diz o que códigos de saída fazem para cada evento, e Controle de decisão diz quais campos de decisão cada evento honra. Campos universais como systemMessage funcionam na maioria dos eventos e são listados na tabela Saída JSON.

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.

Para a maioria dos eventos, Claude Code escreve stdout no log de debug e não o mostra na transcrição. As exceções são UserPromptSubmit, UserPromptExpansion, SessionStart e PostModelSwitch, onde Claude Code adiciona stdout em texto simples como contexto que Claude pode ver e agir.

Se Claude Code lê seu stdout como saída JSON ou como texto simples depende de como ele começa e termina, ignorando espaço em branco ao redor:

  • Começa com { e termina com }: Claude Code o analisa como JSON. Quando a saída é duas ou mais linhas que cada uma analisa como JSON por conta própria, e nenhuma linha é um objeto saída JSON que define um campo, Claude Code trata toda a saída como texto simples. Quando uma dessas linhas define um campo, toda a saída é uma falha de análise, descrita abaixo.
  • Começa com { mas não termina com }: Claude Code o trata como texto simples.
  • Começa com qualquer outra coisa: Claude Code o trata como texto simples, um array JSON ou uma string JSON entre aspas incluída.

Para eventos que usam o modelo de decisão padrão, saída 0 com um objeto analisado que falha na validação de esquema é um erro não-bloqueador: a ação prossegue, e a transcrição mostra um aviso <hook name> hook error com a mensagem de validação. O mesmo acontece em qualquer código de saída diferente de 2, enquanto saída 2 ainda bloqueia.

Para eventos que usam o modelo de decisão padrão, quando Claude Code tenta analisar seu stdout como JSON e não consegue, ele relata um erro não-bloqueador em cada código de saída diferente de 2. A transcrição mostra um aviso <hook name> hook error com a mensagem de análise. Nos eventos que adicionam stdout em texto simples como contexto, Claude Code não adiciona o texto. Antes de v2.1.248, Claude Code tratava esse stdout como texto simples.

Stderr de um hook que sai 0 vai apenas para o log de debug, nunca para a transcrição, e Claude nunca vê. Para lê-lo você mesmo, ative debug logging. Para exibir um aviso para Claude de um hook PostToolUse ou PostToolUseFailure, saia 2 em vez disso para que Claude veja o stderr mesmo que a ferramenta já tenha executado.

Código de saída 2

Saída 2 significa um erro bloqueador. Em eventos que podem bloquear, saída 2 bloqueia se você imprime JSON ou não: até mesmo um JSON permissionDecision de "allow" não pode substituir. Claude Code ainda lê qualquer saída JSON válida em stdout. Em Elicitation e ElicitationResult, o hookSpecificOutput de um hook exit-2 é ignorado.

A mensagem de bloqueio é a razão da decisão de bloqueio do seu JSON quando faz uma, e seu texto stderr caso contrário. O que o bloqueio faz varia por evento: PreToolUse bloqueia a chamada da ferramenta, UserPromptSubmit rejeita o prompt, e assim por diante. Comportamento de código de saída 2 por evento lista o efeito para cada evento, e cada seção de evento diz onde a mensagem vai.

Um hook que sai 2 enquanto imprime JSON que falha na validação de esquema saída JSON ainda bloqueia: Claude Code usa stderr como a razão de bloqueio e registra a falha de validação no log de debug. Antes de v2.1.214, Claude Code tratava essa combinação como um erro não-bloqueador e a ação prosseguia.

Este script bloqueia comandos rm saindo 2 e deixa cada outro comando para o fluxo de permissão normal:

#!/bin/bash
# Lê entrada JSON de stdin, verifica o comando
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")

if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2  # Erro bloqueador: chamada de ferramenta é prevenida
fi

exit 0  # Sem decisão: o fluxo de permissão normal se aplica

Outros códigos de saída

Qualquer outro código de saída não bloqueia por conta própria para a maioria dos eventos de hook. O que acontece depende de seu stdout:

  • Com um objeto analisado que passa na validação de esquema, para eventos que usam o modelo de decisão padrão, Claude Code ignora o código de saída e apenas o JSON decide o resultado:
    • Cada campo que o evento suporta é honrado, incluindo permissionDecision, additionalContext, updatedInput e systemMessage, e o hook não é relatado como um erro.
    • Controle de decisão lista os campos de decisão por evento; campos universais como systemMessage seguem a tabela Saída JSON.
  • Com um objeto analisado que falha na validação de esquema, para eventos que usam o modelo de decisão padrão, é o mesmo erro não-bloqueador que na saída 0: a ação prossegue, e o aviso <hook name> hook error carrega a mensagem de validação.
  • Com stdout que Claude Code tenta analisar como JSON e não consegue, Claude Code relata o mesmo erro não-bloqueador que na saída 0 para eventos que usam o modelo de decisão padrão. A ação prossegue, e o aviso carrega a mensagem de análise.
  • Com stdout que Claude Code trata como texto simples, ou com stdout vazio, é um erro não-bloqueador para a maioria dos eventos de hook: a ação prossegue, e a transcrição mostra um aviso <hook name> hook error seguido pela primeira linha de stderr, prefixado com Failed with non-blocking status code:. Para capturar o stderr completo, ative debug logging.

Eventos fora do modelo de decisão padrão mantêm suas próprias linhas na tabela por evento: WorktreeCreate falha na criação em qualquer saída não-zero não importa o que seu JSON diz, e eventos que descartam saída de hook inteiramente, como StopFailure, ignoram seu JSON em cada código de saída, além de campos de efeito colateral como terminalSequence, que ainda disparam.

Um hook que não consegue iniciar cai no mesmo balde não-bloqueador. Quando o caminho do script não existe ou não é executável, o shell sai com um código como 127 e você vê o mesmo aviso com a mensagem do interpretador, por exemplo Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory. Para a maioria dos eventos de hook, a ação prossegue. Quando você configura um hook de política, observe este aviso em sua primeira execução: um caminho digitado incorretamente em settings.json deixa o portão silenciosamente desabilitado.

Timeouts

Além de 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, as duas famílias de hook diferem:

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.

Evento de hook Pode bloquear? O que acontece na saída 2
PreToolUse Sim Bloqueia a chamada da ferramenta
PermissionRequest Não Código de saída 2 não é honrado para este evento e o fluxo de permissão prossegue inalterado. Negue através do objeto decision em vez disso
UserPromptSubmit Sim Bloqueia o prompt, para que nunca chegue ao Claude. Consulte O que um prompt bloqueado deixa para trás
UserPromptExpansion Sim Bloqueia a expansão
Stop Sim Previne Claude de parar, continua a conversa
SubagentStop Sim Previne o subagente de parar
TeammateIdle Sim Previne o colega de ficar ocioso, para que continue trabalhando
TaskCreated Sim Reverte a criação de tarefa
TaskCompleted Sim Previne a tarefa de ser marcada como concluída
ConfigChange Sim Bloqueia a mudança de configuração de entrar em efeito (exceto policy_settings)
StopFailure Não Saída e código de saída são ignorados, exceto terminalSequence
PostToolUse Não Mostra stderr ao Claude; a ferramenta já executou
PostToolUseFailure Não Mostra stderr ao Claude; a ferramenta já falhou
PostToolBatch Sim Para o loop agêntico antes da próxima chamada de modelo
PermissionDenied Não Código de saída e stderr são ignorados porque a negação já ocorreu. Use JSON hookSpecificOutput.retry: true para dizer ao modelo que pode tentar novamente; Claude Code ignora retry: true para negações sem veredicto
Notification Não Código de saída e stderr são ignorados
SubagentStart Não Mostra stderr apenas ao usuário
SessionStart Não Mostra stderr apenas ao usuário
Setup Não Código de saída e stderr são ignorados
SessionEnd Não Mostra stderr apenas ao usuário
CwdChanged Não Mostra stderr apenas ao usuário
DirectoryAdded Não Stderr vai para o log de debug; o diretório já foi adicionado
FileChanged Não Mostra stderr apenas ao usuário
PreCompact Sim Bloqueia compactação
PostCompact Não Mostra stderr apenas ao usuário
PreModelSwitch Sim Bloqueia a mudança de modelo e mostra stderr ao usuário
PostModelSwitch Não Mostra stderr apenas ao usuário; o modelo já mudou
Elicitation Sim Recusa a solicitação, e nenhuma caixa de diálogo aparece
ElicitationResult Sim Bloqueia a resposta (ação se torna decline)
WorktreeCreate Sim Qualquer código de saída não-zero causa falha na criação de worktree
WorktreeRemove Sim Qualquer código de saída não-zero causa falha na remoção de worktree se o diretório ainda existir depois. Consulte WorktreeRemove para o que acontece com o diretório
InstructionsLoaded Não Código de saída é ignorado
MessageDisplay Não O texto original é exibido

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. Os resultados abaixo se aplicam à maioria dos eventos; um evento com seu próprio contrato de falha na tabela por evento, como WorktreeCreate, aplica esse contrato a um hook HTTP falhado também:

  • 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, execução continua
  • Falha de conexão: erro não-bloqueador, execução continua
  • Timeout: o hook é cancelado, conforme descrito em Timeouts

Diferentemente de hooks de comando, hooks HTTP não podem sinalizar um erro bloqueador apenas através de códigos de status. 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.

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.

O stdout do seu hook deve conter apenas o objeto JSON. Se seu perfil shell imprime texto na inicialização, pode interferir com análise JSON. Consulte Hook JSON não tem efeito no guia de solução de problemas.

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: Claude Code salva a saída em um arquivo no diretório de sessão e a substitui 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.
Campo Padrão Descrição
continue true Se false, Claude para de processar inteiramente após o hook executar. Tem precedência sobre qualquer campo de decisão específico do evento
stopReason nenhum Mensagem mostrada ao usuário quando continue é false. Fica na conversa, portanto Claude a vê se a conversa continuar
suppressOutput false Não tem efeito: Claude Code aceita o campo mas não age sobre ele. O stdout de um hook bem-sucedido nunca é mostrado na transcrição e é registrado no log de debug
systemMessage nenhum Mensagem de aviso mostrada ao usuário. Em Agent SDK e saída --output-format stream-json, pode chegar como um SDKInformationalMessage
terminalSequence nenhum Uma sequência de escape de terminal para Claude Code emitir em seu nome, como uma notificação de desktop, título de janela ou sino. Restrito a OSC 0/1/2/9/99/777 e BEL. Se o valor contiver algo fora da allowlist, o campo é ignorado. Use isso em vez de escrever para /dev/tty, que não está disponível para hooks

Para parar Claude inteiramente:

{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

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, funciona dentro de tmux e GNU screen, 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 dois limites:

  • Claude Code escreve a sequência apenas em uma sessão interativa, e apenas enquanto sua interface está na tela. Em modo não interativo com a flag -p e no Agent SDK, ignora o campo.
  • 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.

O exemplo abaixo dispara uma notificação de desktop de um hook Notification. A sequência de escape é construída com printf escapes octais para que os bytes de controle nunca apareçam na linha de comando do shell, e jq -n --arg constrói a saída JSON para que aspas, barras invertidas e quebras de linha na mensagem de notificação sejam escapadas corretamente:

#!/bin/bash
# Hook de notificação: ping no desktop quando Claude Code precisa de atenção.
input=$(cat)
title="Claude Code"
body=$(jq -r '.message // "Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'

A forma { "terminalSequence": "..." } é a mesma de qualquer shell ou linguagem.

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:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
  }
}

Onde o lembrete aparece depende do evento:

Quando vários hooks retornam additionalContext para o mesmo evento, Claude recebe todos os valores.

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:

Eventos Padrão de decisão Campos-chave
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact decision de nível superior decision: "block", reason. Stop e SubagentStop também aceitam hookSpecificOutput.additionalContext para feedback não-erro que continua a conversa
TeammateIdle, TaskCompleted Código de saída ou continue: false Código de saída 2 bloqueia a ação com feedback de stderr. JSON {"continue": false, "stopReason": "..."} também para o colega inteiramente, correspondendo ao comportamento do hook Stop; TaskCompleted ignora quando a ferramenta TaskUpdate disparou o evento
TaskCreated Código de saída ou decision de nível superior Código de saída 2 ou decision: "block" cancela a tarefa e retorna a mensagem para Claude. continue: false é ignorado
PreToolUse hookSpecificOutput permissionDecision (allow/deny/ask/defer), permissionDecisionReason
PreModelSwitch hookSpecificOutput ou decision de nível superior permissionDecision (allow/deny/ask), permissionDecisionReason. decision: "block" também cancela a mudança
PermissionRequest hookSpecificOutput decision.behavior (allow/deny)
PermissionDenied hookSpecificOutput retry: true diz ao modelo que pode tentar novamente a chamada de ferramenta negada; Claude Code ignora para negações sem veredicto
WorktreeCreate retorno de caminho Hook de comando imprime caminho em stdout; hook HTTP retorna hookSpecificOutput.worktreePath. Falha de hook ou caminho ausente falha na criação
WorktreeRemove Código de saída Qualquer código de saída não-zero faz a remoção falhar se o diretório ainda existir depois. Saída JSON é descartada
Elicitation, ElicitationResult hookSpecificOutput ou decision de nível superior action (accept/decline/cancel), content (valores de campo de formulário). decision: "block" também recusa
MessageDisplay hookSpecificOutput displayContent substitui o texto exibido na tela. Apenas exibição: a transcrição e o que Claude vê mantêm o original
SessionStart, SubagentStart, PostModelSwitch Apenas contexto hookSpecificOutput.additionalContext adiciona contexto para Claude. SessionStart também aceita initialUserMessage, watchPaths, sessionTitle e reloadSkills. Sem bloqueio ou controle de decisão
Setup, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged Nenhum Sem controle de decisão. Usado para efeitos colaterais como logging ou limpeza

Alguns eventos também podem reescrever conteúdo em vez de apenas permitir ou bloquear:

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:

{
"decision": "block",
"reason": "Test suite must pass before proceeding"
}

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 ele 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 exige um script, use CLAUDE.md em vez disso.

O SessionStart é executado em todas as sessões, portanto mantenha esses hooks rápidos. Apenas 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:

Matcher Quando é disparado
startup Nova sessão
resume --resume, --continue ou /resume
clear /clear
compact Compactação automática ou manual
fork Uma nova sessão bifurcada de uma existente: --fork-session com --resume ou --continue, a cópia em segundo plano de /fork, /branch ou uma conversa que você move para o segundo plano

Antes da v2.1.214, sessões bifurcadas informavam a origem "resume".

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:

Campo Descrição
source Como a sessão foi iniciada: "startup" para novas sessões, "resume" para sessões retomadas, "clear" após /clear, "compact" após compactação ou "fork" para uma nova sessão bifurcada de uma existente
model O identificador do modelo ativo. Pode ser omitido, por exemplo após /clear ou quando uma sessão é restaurada por meio da recuperação de conversa, portanto verifique se o campo existe antes de lê-lo
agent_type O nome do agente, presente quando você inicia o Claude Code com claude --agent <name>
session_title O título personalizado da sessão, presente quando um estiver definido, por exemplo com --name, /rename, a saída sessionTitle de um hook ou o renameSession() do Agent SDK. Um hook que emite sessionTitle pode verificar este campo primeiro para evitar sobrescrever um título personalizado existente

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 exigem o Claude Code v2.1.251 ou posterior.

Campo Descrição
seconds_since_last_response Segundos de tempo real desde a última resposta na transcrição retomada
context_tokens Tokens que a primeira requisição da sessão retomada reenvia como seu prompt
prompt_cache_likely_expired true quando a última resposta é mais antiga que o tempo de vida do cache de prompt da sessão ou quando uma compactação posterior substituiu a conversa em cache
estimated_cache_write_usd Custo estimado em dólares americanos de gravar context_tokens no cache de prompt no modelo da sessão, excluindo a resposta

Este exemplo mostra a entrada para uma sessão retomada 90 minutos após sua última resposta:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionStart",
  "source": "resume",
  "model": "claude-opus-5",
  "seconds_since_last_response": 5400,
  "context_tokens": 182340,
  "prompt_cache_likely_expired": true,
  "estimated_cache_write_usd": 1.1396
}

Controle de decisão do SessionStart

O Claude Code adiciona ao contexto do Claude o stdout que ele trata como texto simples. Além dos campos de saída JSON disponíveis para todos os hooks, você pode retornar estes campos específicos do evento:

Campo Descrição
additionalContext String adicionada ao contexto do Claude no início da conversa, antes do primeiro prompt. Consulte Adicionar contexto para o Claude para saber como o texto é entregue e o que colocar nele
initialUserMessage String usada como a primeira mensagem do usuário da sessão. Aplica-se no modo não interativo com a flag -p, onde se torna o primeiro turno mesmo que nenhum prompt seja fornecido. Se um prompt for fornecido, ele vem como o próximo turno. Ao contrário de additionalContext, que se anexa a um turno existente, isto cria o turno
sessionTitle Define o título da sessão, com o mesmo efeito de /rename. Use para nomear sessões automaticamente a partir da pasta de inicialização, do branch do git ou do nome do worktree. Aplica-se quando source é "startup", "resume" ou "fork"; ignorado em "clear" e "compact"
watchPaths Array de caminhos absolutos a observar para eventos FileChanged durante esta sessão
reloadSkills Booleano. Quando true, o Claude Code examina novamente os diretórios de skills e comandos após a conclusão dos hooks SessionStart, para que as skills instaladas pelo hook fiquem disponíveis na mesma sessão, a partir do primeiro prompt
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
    "sessionTitle": "auth-refactor"
  }
}

Como o stdout simples já chega ao Claude neste evento, um hook que apenas carrega contexto pode imprimir diretamente no stdout sem montar JSON. Use o formato JSON quando precisar combinar contexto com outros campos, como sessionTitle.

Use reloadSkills quando um hook SessionStart instalar ou atualizar skills. A descoberta de skills normalmente é executada antes de os hooks SessionStart terminarem, então arquivos que o hook grava em ~/.claude/skills/ ou .claude/skills/ só apareceriam na próxima sessão. Este exemplo sincroniza um repositório de skills compartilhado e solicita a nova varredura:

#!/bin/bash

git -C ~/.claude/skills/team-skills pull --quiet 2>/dev/null || \
  git clone --quiet https://git.example.com/your-org/team-skills.git ~/.claude/skills/team-skills

echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'

A URL do repositório é um espaço reservado; substitua-a pelo seu próprio repositório de skills. Com o espaço reservado, o clone falha e imprime uma mensagem fatal: no stderr. O stderr de um hook SessionStart que sai com 0 é apenas informativo, portanto a solicitação de reloadSkills ainda se aplica.

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 comandos Bash subsequentes.

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:

#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0

Para capturar todas as alterações de ambiente de comandos de configuração, compare as variáveis exportadas antes e depois:

#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0

Setup

É disparado apenas quando você inicia o Claude Code com --init-only, ou com --init ou --maintenance no modo não interativo com a flag -p. 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 SessionStart em vez disso.

O valor do matcher corresponde à flag da CLI que acionou o hook:

Matcher Quando é disparado
init claude --init-only ou claude -p --init
maintenance claude -p --maintenance

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 pipe no 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> pelo local de um 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 se estiver ausente, por exemplo um hook ou skill que testa a existência de ${CLAUDE_PLUGIN_DATA}/node_modules e executa npm install se não existir. Consulte o diretório de dados persistentes para saber onde armazenar 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":

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Setup",
  "trigger": "init"
}

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 apenas como eventos hook_response quando você inicia com --output-format stream-json --verbose.

Os hooks Setup têm acesso a CLAUDE_ENV_FILE. Variáveis gravadas nesse arquivo persistem nos comandos Bash subsequentes da sessão, como nos hooks SessionStart. Apenas 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. Este evento é disparado no início da sessão para arquivos carregados antecipadamente e novamente mais tarde quando arquivos são carregados de forma preguiçosa, 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.

Este evento não é disparado quando o Claude lê 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 CLAUDE.md é um link simbólico para ele, como um carregamento normal de CLAUDE.md.

O matcher é avaliado contra load_reason. Por exemplo, use "matcher": "session_start" para disparar apenas para arquivos carregados no início da sessão, ou "matcher": "path_glob_match|nested_traversal" para disparar apenas para carregamentos preguiçosos.

Entrada do InstructionsLoaded

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

Campo Descrição
file_path Caminho absoluto para o arquivo de instruções que foi carregado
memory_type Escopo do arquivo: "User", "Project", "Local" ou "Managed"
load_reason Por que o arquivo foi carregado: "session_start", "nested_traversal", "path_glob_match", "include" ou "compact". O valor "compact" é disparado quando arquivos de instruções são recarregados após um evento de compactação
globs Padrões glob de caminho do frontmatter paths: do arquivo, se houver. Presente apenas para carregamentos path_glob_match
trigger_file_path Caminho para o arquivo cujo acesso acionou este carregamento, para carregamentos preguiçosos
parent_file_path Caminho para o arquivo de instruções pai que incluiu este, para carregamentos include
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "InstructionsLoaded",
  "file_path": "/Users/my-project/CLAUDE.md",
  "memory_type": "Project",
  "load_reason": "session_start"
}

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 este evento para log de auditoria, rastreamento de conformidade ou observabilidade.

UserPromptSubmit

É executado quando um prompt é enviado, antes de o Claude processá-lo. 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 desses tipos na maioria dos outros eventos. Como este hook é executado antes de cada prompt e bloqueia o processamento do modelo até terminar, um hook travado paralisa a sessão. Se 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 UserPromptSubmit de comando, HTTP ou ferramenta MCP que atinge seu timeout é cancelado e sua saída, incluindo qualquer additionalContext, é descartada. O prompt ainda chega ao Claude sem esse contexto. A transcrição mostra um aviso com o nome do 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 que nomeia o hook e o timeout, porque um callback nesse ponto pode estar atuando como uma barreira de política que não deve falhar de forma permissiva. A sessão continua. Antes da v2.1.208, um timeout de callback nesse evento encerrava o turno com um erro de execução.

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 espaço reservado [Pasted text #N] chega expandido no lugar. Em sessões em que o Claude Code marca o 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 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

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":

Campo Descrição
decision "block" interrompe o prompt antes que ele chegue ao Claude. Omita para permitir que o prompt prossiga
reason Mostrado ao usuário quando decision é "block". Não é adicionado ao contexto
additionalContext String adicionada ao contexto do Claude junto com o prompt enviado. Consulte Adicionar contexto para o Claude
sessionTitle Define o título da sessão. Use para nomear sessões automaticamente com base no conteúdo do prompt
suppressOriginalPrompt Se true quando o hook bloqueia o prompt, deixa o texto do prompt fora da mensagem de bloqueio. Consulte O que um prompt bloqueado deixa para trás

Um hook que bloqueia saindo com 2 é tratado da mesma forma que reason: a mensagem de bloqueio mostra o texto do stderr ao usuário, e ele não é adicionado ao contexto.

{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here",
    "sessionTitle": "My session title",
    "suppressOriginalPrompt": true
  }
}

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 quando o hook bloqueia 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 forma 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 é expandido em um prompt antes de chegar ao Claude. Use-o para impedir que comandos específicos sejam invocados diretamente, injetar contexto para uma skill específica ou registrar em log 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.

Este evento cobre o caminho que o PreToolUse não cobre: um hook PreToolUse que corresponde à ferramenta Skill é disparado apenas quando o Claude chama a ferramenta, mas digitar /skillname diretamente contorna o PreToolUse. O UserPromptExpansion é disparado nesse caminho direto.

Faz a correspondência com 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../00893aaf.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptExpansion",
  "expansion_type": "slash_command",
  "command_name": "example-skill",
  "command_args": "arg1 arg2",
  "command_source": "plugin",
  "prompt": "/example-skill arg1 arg2"
}

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.

Campo Descrição
decision "block" impede que o comando seja expandido. Omita para permitir que ele prossiga
reason Mostrado ao usuário quando decision é "block"
additionalContext String adicionada ao contexto do Claude junto com o prompt expandido. Consulte Adicionar contexto para o Claude

Um hook que bloqueia saindo com 2 é tratado da mesma forma que reason: a mensagem de bloqueio mostra o texto do stderr ao usuário.

{
  "decision": "block",
  "reason": "This slash command is not available",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptExpansion",
    "additionalContext": "Additional context for this expansion"
  }
}

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 uma aplicação 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 este evento é de 10 segundos; se seu hook precisar de mais tempo, defina o campo timeout na entrada do hook.

O MessageDisplay é apenas de 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, então o Claude nunca vê a substituição, e o modo verboso 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ções.

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 depois que a mensagem é concluída 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 para 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 desta 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 em uma mensagem, em vez de esperar que as linhas sejam agrupadas de uma forma específica.

Campo Descrição
turn_id UUID do turno atual
message_id UUID da mensagem do assistente sendo exibida. Estável em todos os lotes da mesma mensagem. Este não é o id msg_… da API, portanto não pode ser correlacionado com os ids de mensagem da transcrição
index Índice baseado em zero deste lote dentro da mensagem
final true no último lote da mensagem. Cada mensagem tem exatamente um lote final
delta As linhas recém-concluídas desde o lote anterior, incluindo as quebras de linha finais. Sempre linhas inteiras, exceto o lote final, que pode terminar no meio de uma linha. Em execuções interativas, o delta do lote final fica vazio quando a mensagem termina com uma quebra de linha, portanto trate final, e não um delta não vazio, como o sinal de fim de mensagem. Em execuções do Agent SDK e claude -p, a chamada única carrega a mensagem inteira
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "MessageDisplay",
  "turn_id": "0c9e6a2f-7d41-4f4e-9a15-3f4f7c2b8d10",
  "message_id": "5b2a9c8e-1f63-4d8a-b7c4-9e0d2a6f1c3b",
  "index": 0,
  "final": false,
  "delta": "Here is the plan:\n"
}

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:

Campo Descrição
displayContent Texto exibido no lugar do delta. Omita-o para exibir o original

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 com base em 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 as crases 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:

{
"hooks": {
"MessageDisplay": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh",
"args": []
}
]
}
]
}
}

Salve este script em .claude/hooks/plain-display.sh no seu projeto e torne-o executável com chmod +x:

#!/bin/bash
jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'

Lotes sem markdown passam sem alterações. 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 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 FileChanged em vez de corresponder às ferramentas de edição de arquivos pelo nome. Ao contrário do PreToolUse, o Claude Code executa os hooks FileChanged depois da alteração, e eles não têm controle de decisão, portanto não podem bloquear a gravação.

Use o controle de decisão do PreToolUse para permitir, negar, pedir confirmação ou adiar a chamada de ferramenta.

Um hook de callback do Agent SDK em PreToolUse que excede seu timeout bloqueia a chamada de ferramenta, e o Claude recebe um resultado de erro que nomeia o timeout. Uma negação explícita retornada por outro hook ainda tem precedência.

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 reconhece. 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 exige 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 correspondência por caminhos não pode ser contornado via ~ ou por 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 com um segmento de caminho como /src/ em vez de ancorar com ^, já que o caminho é absoluto

Uma chamada Write no Windows entrega:

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "C:\\project\\src\\index.ts",
    "content": "..."
  },
  ...
}

Os campos de tool_input dependem da ferramenta:

Bash

Executa comandos de shell.

Campo Tipo Exemplo Descrição
command string "npm test" O comando de shell a ser executado
description string "Run test suite" Descrição opcional do que o comando faz
timeout number 120000 Timeout opcional em milissegundos. Valores acima do máximo são reduzidos ao máximo em vez de rejeitados
run_in_background boolean false Se o comando deve ser executado em segundo plano

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 apenas 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 cobre 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. Exige o Claude Code v2.1.269 ou posterior.

changedFiles e files listam o que o comando alterou; os demais campos indicam quão completa e quão confiável essa lista é.

Campo Tipo Exemplo Descrição
changedFiles array ["/path/to/src/app.ts"] Caminhos absolutos dos arquivos que o comando alterou, no máximo 200. Presente sempre que files contém um diff ou moreFiles é maior que zero
files array [{"filePath": "/path/to/src/app.ts", "hunks": [...]}] Diffs de até 5 arquivos alterados, para exibição. created ou deleted é true para um arquivo que o comando adicionou ou removeu
moreFiles number 2 Contagem de arquivos alterados sem diff em files
unavailable boolean true Definido quando o diff está incompleto ou não pôde ser obtido
skipped boolean true Definido para um comando Git que move a árvore de trabalho, como git checkout ou git stash, de modo que o Claude Code não obtém diff
shared boolean true Definido quando outra chamada da ferramenta Bash, como a de um subagente, foi executada no mesmo repositório ao mesmo tempo, de modo que algumas alterações listadas podem ser desse comando
PowerShell

Executa comandos do 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:

Campo Tipo Exemplo Descrição
command string "Get-ChildItem -Recurse" O comando do PowerShell a ser executado
description string "List files recursively" Descrição opcional do que o comando faz
timeout number 120000 Timeout opcional em milissegundos
run_in_background boolean false Se o comando deve ser executado em segundo plano

Use Bash|PowerShell como matcher em hooks que inspecionam comandos de shell, para que eles cubram ambas as ferramentas:

  • No Windows, sempre que a ferramenta PowerShell estiver habilitada, o Claude trata o PowerShell como o shell principal e encaminha os comandos de shell por ele.
  • 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.

Campo Tipo Exemplo Descrição
file_path string "/path/to/file.txt" Caminho absoluto para o arquivo a ser gravado
content string "file content" Conteúdo a ser gravado no arquivo
Edit

Substitui uma string em um arquivo existente.

Campo Tipo Exemplo Descrição
file_path string "/path/to/file.txt" Caminho absoluto para o arquivo a ser editado
old_string string "original text" Texto a ser encontrado e substituído
new_string string "replacement text" Texto de substituição
replace_all boolean false Se todas as ocorrências devem ser substituídas
Read

Lê o conteúdo de arquivos.

Campo Tipo Exemplo Descrição
file_path string "/path/to/file.txt" Caminho absoluto para o arquivo a ser lido
offset number 10 Número de linha opcional a partir do qual começar a leitura
limit number 50 Número opcional de linhas a serem lidas
Glob

Encontra arquivos que correspondem a um padrão glob.

Campo Tipo Exemplo Descrição
pattern string "**/*.ts" Padrão glob com o qual os arquivos serão comparados
path string "/path/to/dir" Diretório opcional onde pesquisar. O padrão é o diretório de trabalho atual
Grep

Pesquisa o conteúdo de arquivos com expressões regulares.

Campo Tipo Exemplo Descrição
pattern string "TODO.*fix" Padrão de expressão regular a ser pesquisado
path string "/path/to/dir" Arquivo ou diretório opcional onde pesquisar
glob string "*.ts" Padrão glob opcional para filtrar arquivos
output_mode string "content" "content", "files_with_matches" ou "count". O padrão é "files_with_matches"
-i boolean true Pesquisa sem diferenciar maiúsculas de minúsculas
multiline boolean false Habilita correspondência em várias linhas
WebFetch

Busca e processa conteúdo da web.

Campo Tipo Exemplo Descrição
url string "https://example.com/api" URL de onde buscar o conteúdo
prompt string "Extract the API endpoints" Prompt a ser executado sobre o conteúdo buscado
WebSearch

Pesquisa na web.

Campo Tipo Exemplo Descrição
query string "react hooks best practices" Consulta de pesquisa
allowed_domains array ["docs.example.com"] Opcional: incluir apenas resultados destes domínios
blocked_domains array ["spam.example.com"] Opcional: excluir resultados destes domínios
Agent

Inicia um subagente.

Campo Tipo Exemplo Descrição
prompt string "Find all API endpoints" A tarefa que o agente deve executar
description string "Find API endpoints" Descrição curta da tarefa
subagent_type string "Explore" Tipo de agente especializado a ser usado
model string "sonnet" Alias de modelo opcional para sobrescrever o padrão

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:

Campo Tipo Exemplo Descrição
status string "completed" "completed" para subagentes em primeiro plano, "async_launched" para subagentes em segundo plano. Subagentes são executados em segundo plano por padrão, portanto uma chamada Agent que omite run_in_background também produz "async_launched"
agentId string "a4d2c8f1e0b3a297" Identificador da execução do subagente
content array [{"type": "text", "text": "Found 12 endpoints..."}] Os blocos de texto finais do subagente ou, para um subagente cujo relatório passa por SubagentHandback, uma nota curta sobre essa entrega no lugar deles
resolvedModel string "claude-sonnet-4-5" Modelo com o qual o subagente começou, que pode ser diferente do modelo solicitado
modelsUsed array ["claude-sonnet-4-5", "claude-haiku-4-5"] Modelos usados em ordem, com repetições consecutivas condensadas; definido apenas quando o modelo foi trocado no meio da execução. Exige o Claude Code v2.1.212 ou posterior
totalTokens number 12450 Contagem de tokens da requisição final à API do subagente: tokens de entrada, saída e cache combinados. Isso não é um total de toda a execução
totalDurationMs number 48211 Duração em tempo real da execução do subagente
totalToolUseCount number 7 Contagem de chamadas de ferramenta que o subagente fez
usage object {"input_tokens": 8320, ...} Detalhamento de tokens por tipo da requisição final à API: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens

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 do seu resultado completed então carrega uma nota curta sobre essa entrega em vez do próprio relatório. Para ler o relatório, configure um hook PreToolUse ou PostToolUse com matcher em 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: uma inicialização 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. Ela tem status: "async_launched", agentId, description, prompt, outputFile e resolvedModel.

Em uma resposta completed, resolvedModel nomeia o modelo com o qual o subagente começou, que pode ser diferente do valor de model em tool_input, como quando availableModels ou outra substituição se aplica. Em uma resposta async_launched, resolvedModel nomeia o modelo em uso quando o agente passou para o segundo plano, de modo que uma troca ocorrida antes disso é refletida ali. modelsUsed e o comportamento de resolvedModel no momento da passagem para segundo plano exigem o Claude Code v2.1.212 ou posterior.

AskUserQuestion

Faz ao usuário de uma a quatro perguntas de múltipla escolha.

Campo Tipo Exemplo Descrição
questions array [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false}] Perguntas a apresentar, cada uma com uma string question, um header curto, um array options e uma flag multiSelect opcional
answers object {"Which framework?": "React"} Opcional. Mapeia o texto da pergunta para o rótulo da opção selecionada. Respostas de múltipla seleção unem os rótulos com vírgulas. O Claude não define este campo; forneça-o via updatedInput para responder programaticamente
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 vindo 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.

Campo Tipo Exemplo Descrição
plan string "## Refactor auth\n1. Extract..." Conteúdo do plano em Markdown. Injetado a partir do arquivo de plano no disco
planFilePath string "/Users/.../plans/refactor-auth.md" Caminho para o arquivo de plano. Injetado
allowedPrompts array [{"tool": "Bash", "prompt": "run tests"}] Descontinuado. O Claude Code aceita o campo, mas o ignora. Antes da v2.1.205, ele carregava permissões baseadas em prompt que o Claude solicitava para implementar o plano

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 o arquivo do disco novamente.

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 (permitir, negar, pedir confirmação ou adiar) e a capacidade de modificar a entrada da ferramenta antes da execução.

Campo Descrição
permissionDecision "allow" pula o prompt de permissão, exceto para as ações que nenhum modo aprova automaticamente e para AskUserQuestion e ExitPlanMode, que precisam de updatedInput combinado com ele. "deny" impede a chamada de ferramenta. "ask" solicita confirmação ao usuário. "defer" sai de forma controlada para que a ferramenta possa ser retomada depois. Regras de negação e de confirmação ainda são avaliadas, independentemente do que o hook retornar
permissionDecisionReason Para "ask", mostrado ao usuário no prompt de permissão. Quando o Claude Code nega a chamada em uma execução -p em que ninguém pode responder a esse prompt, o Claude lê o motivo no resultado da ferramenta. Para "deny", mostrado ao Claude. Para "allow" e "defer", gravado apenas no log de depuração
updatedInput Modifica os parâmetros de entrada da ferramenta antes da execução. Substitui todo o objeto de entrada, portanto inclua os campos inalterados junto com os modificados. O Claude Code avalia as regras de permissão e a elegibilidade para segundo plano automático de um comando Bash com base na entrada que seu hook retorna, não na entrada que o Claude enviou. Combine com "allow" para aprovar automaticamente, ou com "ask" para mostrar a entrada modificada ao usuário. Para "defer", é ignorado
additionalContext String adicionada ao contexto do Claude junto com o resultado da ferramenta. Ignorada quando permissionDecision é "defer". Consulte Adicionar contexto para o Claude

Quando vários hooks PreToolUse retornam decisões diferentes, a precedência é deny > defer > ask > allow.

Um hook que bloqueia saindo com 2 é tratado 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 a origem do hook: [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.

Um "ask" de um hook também força um prompt de permissão no modo auto: o classificador ainda pode negar a chamada de ferramenta, mas não pode aprová-la silenciosamente. Antes da v2.1.211, o classificador podia aprovar um comando Bash executado fora do sandbox sem mostrar o prompt que o hook solicitou; o classificador ainda aplicava suas próprias regras de segurança a esse comando, e um "deny" de hook sempre era respeitado.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}

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 apenas 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 updatedInput contendo 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:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "questions": [
        {
          "question": "Which framework?",
          "header": "Framework",
          "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}],
          "multiSelect": false
        }
      ],
      "answers": {"Which framework?": "React"}
    }
  }
}

Uma ferramenta MCP cujo servidor a marca com _meta["anthropic/requiresUserInteraction"] é mais restrita: um hook não pode pular 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 depois

"defer" é para integrações que executam claude -p como subprocesso e leem sua saída JSON, como um app do Agent SDK ou uma interface personalizada construída sobre o Claude Code. Ele permite que esse processo chamador pause o Claude em uma chamada de ferramenta, colete a entrada por meio de sua própria interface e retome de onde parou. O Claude Code respeita esse valor apenas no modo não interativo com a flag -p. Em sessões interativas, ele registra um aviso em log e ignora o resultado do hook.

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 apenas 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 completo funciona assim:

  1. O Claude chama AskUserQuestion. O hook PreToolUse é disparado.
  2. O hook retorna permissionDecision: "defer". A ferramenta não é executada. O processo sai com stop_reason: "tool_deferred" e a chamada de ferramenta pendente preservada na transcrição.
  3. O processo chamador lê deferred_tool_use do resultado do SDK, apresenta 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 carrega o id, o name e o input da ferramenta. O input são os parâmetros que o Claude gerou para a chamada de ferramenta, capturados antes da execução:

{
  "type": "result",
  "subtype": "success",
  "stop_reason": "tool_deferred",
  "session_id": "abc123",
  "deferred_tool_use": {
    "id": "toolu_01abc",
    "name": "AskUserQuestion",
    "input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false }] }
  }
}

Não há timeout nem limite de novas tentativas. A sessão permanece no disco até que você a retome, sujeita à varredura de retenção cleanupPeriodDays, que exclui arquivos de sessão após 30 dias por padrão, seguindo as regras da varredura de retenção. Se a resposta não estiver pronta quando você retomar, o hook pode retornar "defer" novamente e o processo sai da mesma forma. O processo chamador controla quando interromper o loop, retornando eventualmente "allow" ou "deny" a partir do hook.

"defer" só funciona quando o Claude faz uma única chamada de ferramenta no turno. Se o Claude fizer várias chamadas de ferramenta de uma vez, "defer" é ignorado com um aviso e a ferramenta prossegue pelo fluxo normal de permissão. A restrição existe porque a retomada só pode reexecutar uma ferramenta: não há como adiar uma chamada de um lote sem deixar as outras sem resolução.

Se a ferramenta adiada não estiver mais disponível quando você retomar, o processo sai com stop_reason: "tool_deferred_unavailable" e is_error: true antes de o hook ser disparado. Isso acontece quando um servidor MCP que fornecia a ferramenta não está conectado na sessão retomada. O payload deferred_tool_use ainda é incluído para que você possa identificar qual ferramenta ficou indisponí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 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 este 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 aguardou 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 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 esta solicitação, como adicionar uma regra de permissão 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 suas próprias opções. Alguns diálogos, como o de edição de arquivos, não leem o array e derivam suas opções da própria solicitação. Um diálogo que o lê ainda pode ocultar uma opção cuja sugestão permanece no array, por exemplo quando allowManagedPermissionRulesOnly oculta as 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 toda 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 ele negaria automaticamente uma chamada que não pode solicitar confirmação. Nenhum dos dois eventos é disparado para EndConversation.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    {
      "type": "addRules",
      "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
      "behavior": "allow",
      "destination": "localSettings"
    }
  ]
}

Controle de decisão do PermissionRequest

Os hooks PermissionRequest podem permitir ou negar solicitaçõ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:

Campo Descrição
behavior "allow" concede a permissão, "deny" a nega. Regras de negação e de confirmação ainda são avaliadas, portanto um hook que retorna "allow" não sobrescreve uma regra de negação correspondente
updatedInput Apenas para "allow": modifica os parâmetros de entrada da ferramenta antes da execução. Substitui todo o objeto de entrada, portanto inclua os campos inalterados junto com os modificados. A entrada modificada é reavaliada com base nas regras de negação e de confirmação
updatedPermissions Apenas para "allow": array de entradas de atualização de permissão a aplicar, como adicionar uma regra de permissão ou alterar o modo de permissão da sessão
message Apenas para "deny": informa ao Claude por que a permissão foi negada
interrupt Apenas para "deny": se true, interrompe o Claude

Um hook que sai com 2 sem um objeto decision deixa o fluxo de permissão inalterado, e seu stderr é descartado. Apenas o objeto decision pode conceder ou negar a solicitação.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

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.

type Campos Efeito
addRules rules, behavior, destination Adiciona regras de permissão. rules é um array de objetos {toolName, ruleContent?}. Omita ruleContent para corresponder à ferramenta inteira. behavior é "allow", "deny" ou "ask"
replaceRules rules, behavior, destination Substitui todas as regras do behavior informado no destination pelas rules fornecidas
removeRules rules, behavior, destination Remove as regras correspondentes do behavior informado
setMode mode, destination Altera o modo de permissão. Os modos válidos são default, auto, acceptEdits, dontAsk, bypassPermissions, plan e manual como alias de default
addDirectories directories, destination Adiciona diretórios de trabalho. directories é um array de strings de caminho
removeDirectories directories, destination Remove diretórios de trabalho

O campo destination em cada entrada determina se a alteração permanece na memória ou é persistida em um arquivo de configurações.

destination Grava em
session somente na memória, descartado quando a sessão termina
localSettings .claude/settings.local.json
projectSettings .claude/settings.json
userSettings ~/.claude/settings.json

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.

Faz correspondência pelo nome da ferramenta, com os mesmos valores de PreToolUse.

Faça uma correspondência mais ampla quando o nome da ferramenta não for o filtro certo:

  • Para executar um hook após qualquer ferramenta ser 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 o git diff deixa passar. Para chamadas de ferramenta que falham, adicione o mesmo hook em PostToolUseFailure.
  • Para executar um hook quando um arquivo específico mudar no disco, independentemente do que o gravou, use FileChanged. O Claude Code não executa um hook PostToolUse que corresponde a Edit|Write quando um comando Bash ou um processo fora do Claude Code reescreve o mesmo arquivo.

Entrada de 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 das ferramentas de arquivo chegam no mesmo formato que em 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "type": "create"
  },
  "tool_use_id": "toolu_01ABC123...",
  "duration_ms": 12
}
Campo Descrição
duration_ms Opcional. Tempo de execução da ferramenta em milissegundos. Exclui o tempo gasto em prompts de permissão e em hooks PreToolUse

Controle de decisão de 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:

Campo Descrição
decision "block" adiciona o reason ao lado do resultado da ferramenta. O Claude ainda vê a saída original; para substituí-la, use updatedToolOutput
reason Explicação mostrada ao Claude quando decision é "block"
additionalContext String adicionada ao contexto do Claude junto com o resultado da ferramenta. Consulte Adicionar contexto para o Claude
classifierContext Nota curta sobre o resultado desta chamada destinada ao classificador do modo auto, e não ao Claude. Consulte Anotar um resultado para o classificador do modo auto. Requer Claude Code v2.1.236 ou posterior
updatedToolOutput Substitui a saída da ferramenta pelo valor fornecido antes que ela seja enviada ao Claude. O valor deve corresponder ao formato de saída da ferramenta
updatedMCPToolOutput Substitui a saída apenas para ferramentas MCP. Prefira updatedToolOutput, que funciona para todas as ferramentas

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:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude",
    "updatedToolOutput": {
      "stdout": "[redacted]",
      "stderr": "",
      "interrupted": false,
      "isImage": false
    }
  }
}

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, e não ao Claude. O classificador nunca recebe os próprios resultados das ferramentas, então este campo é a forma suportada de informar algo sobre o que uma chamada retornou antes que ele revise ações posteriores. 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:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "classifierContext": "This query ran against the staging database, not production."
  }
}

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 verifica essa afirmação em relação às 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 SDK em TypeScript e retorna a nota durante a sessão ativa, o classificador pode considerar uma declaração do usuário repassada 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 de ambos os 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 ambos os 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

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.

Faz correspondência pelo nome da ferramenta, com os mesmos valores de PreToolUse.

Entrada de PostToolUseFailure

Os hooks PostToolUseFailure recebem os mesmos campos tool_name e tool_input que 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 com falha poderia entregar:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Exit code 1\nError: Cannot find module 'express'",
  "is_interrupt": false,
  "duration_ms": 4187
}
Campo Descrição
error String que descreve o que deu errado. O formato depende da ferramenta que falhou
is_interrupt Booleano opcional. Verdadeiro quando a falha chegou ao Claude Code como um aborto, e não como um erro relatado pela ferramenta. Cancelar uma ferramenta em execução não dispara este hook; em vez disso, o resultado da ferramenta traz a mensagem de interrupção
duration_ms Opcional. Tempo de execução da ferramenta em milissegundos. Exclui o tempo gasto em prompts de permissão e em hooks PreToolUse

A string error geralmente é o mesmo texto que o Claude recebe como resultado da ferramenta com falha. 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, 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 tenha produzido como um único bloco com stdout e stderr intercalados
  • Um payload também pode trazer apenas uma mensagem de falha sem 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 de PostToolUseFailure

Os hooks PostToolUseFailure podem fornecer contexto ao Claude após uma falha de 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:

Campo Descrição
additionalContext String adicionada ao contexto do Claude junto com o erro. Consulte Adicionar contexto para o Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}

PostToolBatch

É executado uma vez depois que todas as chamadas de ferramenta de um lote foram resolvidas, antes que o Claude Code envie 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 paralelas. 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 única ferramenta. Não há matcher para este evento.

Entrada de PostToolBatch

Além dos campos de entrada comuns, os hooks PostToolBatch recebem tool_calls, um array que descreve cada chamada de ferramenta do lote:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolBatch",
  "tool_calls": [
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/accounts.py"},
      "tool_use_id": "toolu_01...",
      "tool_response": "1\tfrom __future__ import annotations\n2\t..."
    },
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/transactions.py"},
      "tool_use_id": "toolu_02...",
      "tool_response": "1\tfrom __future__ import annotations\n2\t..."
    }
  ]
}

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.

Controle de decisão de 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:

Campo Descrição
additionalContext String de contexto injetada uma vez antes da próxima chamada ao modelo. Consulte Adicionar contexto para o Claude para detalhes de entrega, o que colocar nela e como as sessões retomadas lidam com valores anteriores
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolBatch",
    "additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete."
  }
}

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 do stopReason do JSON, ou do stderr no código de saída 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 porque sua resposta não pôde ser analisada. Este hook só é disparado no modo auto: ele não é executado quando você nega manualmente uma caixa de 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.

Faz correspondência pelo nome da ferramenta, com os mesmos valores de PreToolUse.

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "auto",
  "hook_event_name": "PermissionDenied",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build",
    "description": "Clean build directory"
  },
  "tool_use_id": "toolu_01ABC123...",
  "reason": "[Irreversible Local Destruction]"
}
Campo Descrição
reason O motivo da negação. Para um veredito do classificador, na maioria das sessões ele nomeia a regra correspondente entre colchetes, como [Data Exfiltration]; consulte Revisar negações para as outras formas. Para uma negação sem veredito, ele começa com Auto mode could not evaluate this action and is blocking it for safety. Para uma negação porque o modelo do classificador estava indisponível, é o texto fixo Classifier unavailable

Controle de decisão de 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:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionDenied",
    "retry": true
  }
}

Quando retry é true, o Claude Code adiciona uma mensagem à conversa informando ao modelo que ele pode tentar novamente a chamada de ferramenta. O Claude Code não reverte a negação em si. Se o seu hook não retornar JSON, ou retornar retry: false, a negação permanece 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 em frente.

Notification

É executado quando o Claude Code envia notificações. Faz correspondência pelo 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, não se o seu hook é executado.

Matcher Quando é disparado
permission_prompt O Claude precisa que você aprove o uso de uma ferramenta ou a requisição de rede de um comando em sandbox, e o prompt está aguardando há cerca de seis segundos
idle_prompt O Claude terminou de responder há cerca de 60 segundos e você não digitou nada desde então
auth_success A autenticação é concluída
elicitation_dialog Um servidor MCP abre um formulário de elicitação e você não digita há cerca de seis segundos
elicitation_url_dialog Um servidor MCP pede que você abra uma URL no navegador e você não digita há cerca de seis segundos
elicitation_complete Um servidor MCP informa que uma elicitação no modo URL foi concluída
elicitation_response Uma resposta de elicitação MCP é enviada de volta ao servidor
agent_needs_input Uma sessão em segundo plano começa a aguardar sua entrada enquanto a visualização de agentes está aberta em um terminal. Também é disparado quando uma sessão de terminal mostra a você uma pergunta de configuração de terminal de um colega de uma equipe de agentes ou o aviso do modo auto sobre cobranças de requisições do classificador e você não digita há cerca de seis segundos
agent_completed Uma sessão em segundo plano termina ou falha. É disparado somente enquanto a visualização de agentes está aberta em um terminal
quota_auto_resume_fired O Claude Code continua sua tarefa depois que um limite de uso do claude.ai a pausou: no momento da redefinição, ou antes disso quando algo que você faz no Claude Code durante a espera, como adicionar créditos de uso, fazer upgrade do seu plano ou trocar de modelo, torna o uso disponível novamente, com a exceção da configuração de modelo
quota_auto_resume_stale Um limite de uso do claude.ai foi redefinido enquanto seu computador ficou em suspensão por mais de cerca de 30 minutos. O Claude Code aguarda que você pressione Enter em vez de continuar. Após uma suspensão mais curta, ele continua e dispara quota_auto_resume_fired em vez disso
quota_auto_resume_disabled O Claude Code encerra a espera por um limite de uso do claude.ai sem continuar sua tarefa: autoContinueAtUsageLimit foi desativado ou a redefinição foi adiada para mais de 24 horas durante uma espera que o Claude Code iniciou por conta própria, a tarefa continuada continuou atingindo o limite, ou a continuação foi bloqueada antes de chegar ao modelo. Não é disparado quando você pressiona Esc ou Ctrl+C, ou escolhe Don't continue automatically

Os tipos quota_auto_resume_fired, quota_auto_resume_stale e quota_auto_resume_disabled requerem Claude Code v2.1.234 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 de terminal de um colega requer Claude Code v2.1.248 ou posterior.

O Claude Code temporiza permission_prompt de forma diferente em sessões nas quais envia solicitaçõ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 responder 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:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}

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 é o recurso em que o exemplo de notificação da área de trabalho se baseia. Os hooks Notification destinam-se a efeitos colaterais, como encaminhar a notificação a um serviço externo.

SubagentStart

É executado quando o Claude cria um subagente com a ferramenta Agent, quando o Claude retoma um subagente e sempre que um colega em processo de uma equipe de agentes processa uma nova mensagem. Oferece suporte a 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, não o nome do arquivo.

Para subagentes distribuídos 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 de SubagentStart

Além dos campos de entrada comuns, os hooks SubagentStart recebem agent_id com o identificador exclusivo do subagente e agent_type com o nome do agente pelo qual o matcher filtra.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}

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:

Campo Descrição
additionalContext String adicionada ao contexto do subagente no início de sua conversa, antes do seu primeiro prompt. Consulte Adicionar contexto para o Claude
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}

Quando o hook é executado novamente para o mesmo subagente, o Claude Code injeta o contexto retornado somente 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 execução seguinte.

SubagentStop

É executado quando um subagente do Claude Code termina de responder. Faz correspondência pelo tipo de agente, com os mesmos valores de SubagentStart.

Entrada de 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 criado 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 SubagentStop também é disparado quando um deles termina. Para esses eventos, agent_type é o nome do agente com o qual 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 correspondência em SubagentHandback recebe como tool_input.message.

Os hooks SubagentStop também recebem os arrays background_tasks e session_crons descritos em Entrada de Stop. Ambos os arrays têm escopo na sessão pai, não no subagente.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues...",
  "background_tasks": [],
  "session_crons": []
}

Os hooks SubagentStop usam o mesmo formato de controle de decisão que os 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 determinadas tarefas sejam criadas. Em uma sessão sem as ferramentas Task, este evento não é disparado.

Os hooks TaskCreated não oferecem suporte a matchers e são disparados em todas as ocorrências.

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "TaskCreated",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "session-a1b2c3d4"
}
Campo Descrição
task_id Identificador da tarefa que está sendo criada
task_subject Título da tarefa
task_description Descrição detalhada da tarefa. Pode estar ausente
teammate_name Nome do colega que está criando a tarefa. Pode estar ausente
team_name Descontinuado. Nome da equipe derivado da sessão; será removido em uma versão futura
agent_id Neste evento, o campo de entrada comum identifica o subagente ou o colega em processo que está criando a tarefa. Pode estar ausente. Requer Claude Code v2.1.290 ou posterior

Controle de decisão de TaskCreated

Um hook TaskCreated pode bloquear a criação de duas maneiras. 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:

#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
  echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
  exit 2
fi

exit 0

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 fechada.

Os hooks TaskCompleted não oferecem suporte a matchers e são disparados em todas as ocorrências.

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "session-a1b2c3d4"
}
Campo Descrição
task_id Identificador da tarefa que está sendo concluída
task_subject Título da tarefa
task_description Descrição detalhada da tarefa. Pode estar ausente
teammate_name Nome do colega que está concluindo a tarefa. Pode estar ausente
team_name Descontinuado. Nome da equipe derivado da sessão; será removido em uma versão futura
agent_id Neste evento, o campo de entrada comum identifica o subagente ou o colega em processo que está concluindo a tarefa. Pode estar ausente. Requer Claude Code v2.1.290 ou posterior

Controle de decisão de TaskCompleted

Os hooks TaskCompleted oferecem 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 terminando seu turno acionou o evento, interrompe o colega completamente, 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:

#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

# Run the test suite
if ! npm test 2>&1; then
  echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
  exit 2
fi

exit 0

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 vez disso.

Entrada de 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 de parada. Verifique esse valor ou processe a transcrição para evitar bloquear com base em uma condição que nunca será resolvida.

O Claude Code aplica um limite de 8 continuações consecutivas: depois que os hooks de parada continuarem o turno oito vezes seguidas, o Claude Code sobrescreve o próximo bloqueio e encerra o turno. A contagem de continuações consecutivas é redefinida sempre 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 distingam entre "a sessão terminou" e "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:

Campo Descrição
id Identificador da tarefa
type Rótulo amigável do tipo de tarefa, como shell, subagent, monitor, workflow, teammate, cloud session ou MCP task. Cada rótulo identifica qual recurso do Claude Code criou a tarefa. Usa o discriminante bruto como alternativa para tipos não reconhecidos
status Status atual da tarefa
description Descrição em texto livre, limitada a 1000 caracteres, com um marcador … [+N chars] na própria string quando cortada
command Linha de comando do shell, limitada a 1000 caracteres. Presente somente para tarefas shell
agent_type Nome do tipo de subagente. Presente somente para tarefas subagent
server Nome do servidor MCP. Presente somente para tarefas monitor e MCP task
tool Nome da ferramenta MCP. Presente somente para tarefas monitor e MCP task
name Nome do workflow. Presente somente para tarefas workflow

Cada entrada em session_crons descreve um despertar agendado com escopo de sessão, originado de CronCreate, ScheduleWakeup e /loop:

Campo Descrição
id Identificador da tarefa cron
schedule Expressão cron, por exemplo 0 9 * * 1-5
recurring false para despertares únicos cujo agendamento codifica um único horário de disparo, true para tarefas que disparam novamente a cada correspondência
prompt Prompt enviado quando o cron é disparado, limitado a 1000 caracteres com o mesmo marcador … [+N chars]

Este exemplo mostra uma entrada de Stop com uma tarefa shell em andamento e um cron recorrente:

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true,
  "last_assistant_message": "I've completed the refactoring. Here's a summary...",
  "background_tasks": [
    {
      "id": "task-001",
      "type": "shell",
      "status": "running",
      "description": "tail logs",
      "command": "tail -f /var/log/syslog"
    }
  ],
  "session_crons": [
    {
      "id": "cron-001",
      "schedule": "0 9 * * 1-5",
      "recurring": true,
      "prompt": "check the build"
    }
  ]
}

Controle de decisão de 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:

Campo Descrição
decision "block" impede que o Claude pare. Omita para permitir que o Claude pare
reason Obrigatório quando decision é "block". Informa ao Claude por que ele deve continuar
hookSpecificOutput.additionalContext Feedback sem erro para o Claude. A conversa continua para que o Claude possa agir sobre ele, mas, diferentemente de decision: "block", ele é mostrado na transcrição como feedback de hook, e não como erro de hook

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.

{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

Use additionalContext quando o hook estiver funcionando conforme projetado e dando orientações ao Claude, como "execute o conjunto de testes antes de terminar". Ele mantém a conversa em andamento com as mesmas proteções contra loop que decision: "block", ou seja, 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 é mostrada:

{
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "Please run the test suite before finishing"
  }
}

StopFailure

É executado em vez de 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, com exceção de 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 de 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.

Campo Descrição
error Tipo de erro: rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error ou unknown
error_details Detalhes adicionais sobre o erro, quando disponíveis
last_assistant_message O texto de erro renderizado mostrado na conversa. Diferentemente de Stop e SubagentStop, em que este campo contém a saída conversacional do Claude, para StopFailure ele contém a própria string do erro de API, como "API Error: Rate limit reached"
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "StopFailure",
  "error": "rate_limit",
  "error_details": "429 Too Many Requests",
  "last_assistant_message": "API Error: Rate limit reached"
}

Os hooks StopFailure não têm controle de decisão. Eles são executados apenas para fins de notificação e registro em log.

TeammateIdle

É executado quando um colega de uma equipe de agentes está prestes a ficar ocioso após terminar seu turno. Use-o para impor critérios de qualidade antes que um colega pare de trabalhar, como exigir verificações de lint aprovadas ou verificar se os arquivos de saída existem.

Os hooks TeammateIdle não oferecem suporte a matchers e são disparados em todas as ocorrências.

Entrada de TeammateIdle

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

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "session-a1b2c3d4"
}
Campo Descrição
teammate_name Nome do colega que está prestes a ficar ocioso
team_name Descontinuado. Nome da equipe derivado da sessão; será removido em uma versão futura
agent_id Neste evento, o campo de entrada comum identifica o colega em processo que está prestes a ficar ocioso. Pode estar ausente. Requer Claude Code v2.1.290 ou posterior

Controle de decisão de TeammateIdle

Os hooks TeammateIdle oferecem duas formas de controlar o comportamento do colega:

  • Código de saída 2: o colega recebe a mensagem do stderr como feedback e continua trabalhando em vez de ficar ocioso.
  • JSON {"continue": false, "stopReason": "..."}: interrompe o colega completamente, 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 fique ocioso:

#!/bin/bash

if [ ! -f "./dist/output.js" ]; then
  echo "Build artifact missing. Run the build before stopping." >&2
  exit 2
fi

exit 0

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. Ele aplica configurações gerenciadas pelo servidor e alterações nas preferências gerenciadas do macOS ou na política do registro do Windows sem executá-los. No WSL com wslInheritsWindowsSettings, ele também aplica um arquivo de configurações gerenciadas alterado do lado do Windows em sua verificação periódica de política sem executá-los.

O matcher filtra pela origem da configuração:

Matcher Quando é disparado
user_settings ~/.claude/settings.json muda
project_settings .claude/settings.json muda
local_settings .claude/settings.local.json muda
policy_settings managed-settings.json ou um arquivo em managed-settings.d/ muda
skills Um arquivo de skill em .claude/skills/ muda

Este exemplo registra em log todas as alterações de configuração para auditoria de segurança:

{
  "hooks": {
    "ConfigChange": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

Entrada de 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 para o arquivo específico que foi modificado.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ConfigChange",
  "source": "project_settings",
  "file_path": "/Users/.../my-project/.claude/settings.json"
}

Controle de decisão de 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 em JSON para impedir a alteração. Quando bloqueadas, as novas configurações não são aplicadas à sessão em execução.

Campo Descrição
decision "block" impede que a alteração de configuração seja aplicada. Omita para permitir a alteração
reason Aceito, mas nunca mostrado
{
  "decision": "block",
  "reason": "Configuration changes to project settings require admin approval"
}

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 sobre a 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, seja bloqueando com reason ou com stderr no código de saída 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íficos do projeto ou executar scripts de configuração automaticamente. Funciona em conjunto com FileChanged para ferramentas como o direnv, que gerenciam o ambiente por diretório.

Os hooks CwdChanged 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.

CwdChanged não oferece suporte a matchers e é disparado em todas as ocorrências.

Entrada de CwdChanged

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

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project/src",
  "hook_event_name": "CwdChanged",
  "old_cwd": "/Users/my-project",
  "new_cwd": "/Users/my-project/src"
}

Saída de 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:

Campo Descrição
watchPaths Array de caminhos absolutos. Substitui a lista dinâmica de observação atual. Os caminhos da sua configuração de matcher são sempre observados. Retornar um array vazio limpa a lista dinâmica, o que é comum ao entrar em um novo diretório

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 desses hooks 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 do 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; 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 que está dentro de um

O Claude Code dispara 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 do 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:

Matcher Quando é disparado
slash_command Você adiciona um diretório com /add-dir
register_repo_root Um cliente do SDK adiciona um diretório com a requisição de controle register_repo_root

Entrada de DirectoryAdded

Além dos campos de entrada comuns, os hooks DirectoryAdded recebem directory e source.

Campo Descrição
directory Caminho absoluto do diretório que foi adicionado
source Como o diretório foi adicionado: "slash_command" para /add-dir ou "register_repo_root" para a requisição de controle do SDK
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "DirectoryAdded",
  "directory": "/Users/my-other-repo",
  "source": "slash_command"
}

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 desses hooks e exibe 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 da falha vai para o log de depuração
  • register_repo_root: o Claude Code grava a saída de systemMessage e a saída de falhas somente no log de depuração

FileChanged

É executado quando um arquivo observado muda no disco. O Claude Code detecta mudanças com um observador do sistema de arquivos, e não inspecionando chamadas de ferramenta, então 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:

  • Construir a lista de observação: o valor é dividido em | e cada segmento é registrado como um nome de arquivo literal no diretório de trabalho, então ".envrc|.env" observa exatamente esses dois arquivos. Padrões regex não são úteis aqui: um valor como ^\.env observaria um arquivo literalmente chamado ^\.env.
  • Filtrar quais hooks são executados: quando um arquivo observado 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 terminações de linha em data.csv após qualquer alteração, incluindo um comando Bash ou um script externo que reescreva o arquivo:

{
  "hooks": {
    "FileChanged": [
      {
        "matcher": "data.csv",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/normalize-line-endings.sh"
          }
        ]
      }
    ]
  }
}

O hook lê o caminho absoluto do arquivo alterado do campo file_path da entrada JSON no stdin. Sua verificação com grep testa exatamente o 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 frouxa 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:

#!/bin/bash
FILE=$(jq -r .file_path)
if grep -q $'\r$' "$FILE"; then
  perl -pi -e 's/\r$//' "$FILE"
fi

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 terminações LF.

Para observar arquivos que você não consegue nomear de antemão, retorne watchPaths de um hook para atualizar a lista de observação dinamicamente. O Claude Code inicia o observador somente quando algo nomeia um arquivo a ser observado, 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 observado muda, então deixe o matcher omitido no grupo que lida com caminhos dinâmicos, o que corresponde a todo arquivo observado e não adiciona nada à lista de observação. Um matcher "*" também corresponde a todos os arquivos, mas o Claude Code o registra na lista de observação 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 de FileChanged

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

Campo Descrição
file_path Caminho absoluto para o arquivo que mudou
event O que aconteceu: "change" para um arquivo modificado, "add" para um arquivo criado ou "unlink" para um arquivo excluído
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "FileChanged",
  "file_path": "/Users/my-project/.envrc",
  "event": "change"
}

Saída de 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 observados:

Campo Descrição
watchPaths Array de caminhos absolutos. Substitui a lista dinâmica de observação atual. Os caminhos da sua configuração de matcher são sempre observados. Use isto quando seu script de hook descobrir arquivos adicionais a serem observados com base no arquivo alterado

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 desses hooks 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.

WorktreeCreate

É executado quando um worktree está sendo criado, seja a partir de claude --worktree, de um subagente usando isolation: "worktree", ou para uma sessão em segundo plano que o Claude Code isola em seu próprio 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, 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 de WorktreeCreate para ver 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:

{
  "hooks": {
    "WorktreeCreate": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
          }
        ]
      }
    ]
  }
}

O hook lê o name do worktree a partir da entrada JSON no 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 o caminho do worktree. Redirecione qualquer outra saída para o stderr para que ela não interfira no caminho.

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeCreate",
  "name": "feature-auth"
}

Saída de WorktreeCreate

Os hooks WorktreeCreate não usam o modelo padrão de decisão de 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 do 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 o 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, eliminando 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 encerra com o 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, porque um link simbólico commitado no repositório poderia redirecionar o worktree para fora dele. O erro indica o componente rejeitado. Retorne um caminho normalizado que não passe por um link simbólico dentro do repositório. Antes da v2.1.216, a criação do worktree seguia o caminho do hook sem essa verificação.

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 opta por remover o worktree quando o Claude Code pergunta
  • 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 perguntar
  • Você exclui uma sessão em segundo plano que é executada no worktree

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 não commitado. Verifique esse trabalho no seu hook WorktreeRemove antes que ele exclua qualquer coisa.

Para worktrees baseados em git, o Claude Code cuida da limpeza automaticamente com git worktree remove. Se você configurou um hook WorktreeCreate, combine-o com um hook WorktreeRemove para controlar a limpeza dos worktrees que ele cria:

  • Sem hook WorktreeRemove: quando o Claude Code remove o worktree ao sair de uma sessão de worktree, ele recorre a git worktree remove --force no caminho que seu hook WorktreeCreate retornou, de modo que um worktree que o git reconhece é removido. Um worktree que o git não reconhece, por exemplo um que seu hook criou com um sistema de controle de versão que não seja git, permanece no disco. Para saber o que a exclusão de uma sessão em segundo plano faz com um worktree criado por hook, consulte as regras de exclusão do agent view.
  • O hook sai com 0: o worktree conta como removido. O Claude Code não lê mais nada do hook, então certifique-se de que seu hook excluiu o diretório.
  • O hook sai 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 sair com código diferente de zero conta como removido. Para saber como a falha é relatada, consulte Entrada de 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 o seu hook WorktreeCreate cria um branch, exclua-o no 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 e recusa um caminho que seja um link simbólico ou que passe por um abaixo da raiz do repositório. O hook é executado para um worktree que ainda contém arquivos somente quando você confirma a exclusão no agent view; para esse tipo de worktree, claude rm mantém a sessão e o worktree. Antes da v2.1.216, o hook era executado no caminho armazenado sem essas verificações.

O Claude Code passa o caminho retornado por WorktreeCreate como worktree_path na entrada do hook. Este exemplo lê esse caminho e remove o diretório:

{
  "hooks": {
    "WorktreeRemove": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
          }
        ]
      }
    ]
  }
}

Entrada de WorktreeRemove

Além dos campos de entrada comuns, os hooks WorktreeRemove recebem o campo worktree_path, que é o caminho absoluto para o worktree que está sendo removido.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeRemove",
  "worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}

O código de saída de um hook WorktreeRemove decide o resultado. Quando um hook sai 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 do hook e o stderr 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 no agent view informa como o hook terminou, como exited 1, cita o início do stderr e diz se excluir a sessão novamente remove o diretório mesmo assim.

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:

Matcher Quando é disparado
manual /compact
auto Compactação automática quando a conversa atinge a janela de compactação automática

Saia com o código 2 para bloquear a compactação. Para um /compact manual, a mensagem do 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 de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": null
}

PostCompact

É executado após o Claude Code concluir 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 para PreCompact:

Matcher Quando é disparado
manual Após /compact
auto Após a compactação automática quando a conversa atinge a janela de compactação automática

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}

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 requisiçõ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á trocando, 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 depender apenas do 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, de modo que recusa uma troca para o Opus 4.6 saindo com o código 2 e deixa passar qualquer outro destino:

O comando verifica to_model com jq:

{
"hooks": {
"PreModelSwitch": [
{
"matcher": "claude-opus-4-6",
"hooks": [
{
"type": "command",
"command": "jq -e '.to_model | test(\"opus-4-6\")' > /dev/null && { echo 'Opus 4.6 is retired for this project. Use a newer model.' >&2; exit 2; }; exit 0"
}
]
}
]
}
}

Para confirmar que o hook funciona, execute /model claude-opus-4-6 a partir de uma sessão que esteja usando 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 de 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.

Campo Tipo Descrição
from_model string ID do modelo do qual a troca parte
to_model string ID do modelo para o qual a troca muda. O matcher é comparado com o nome canônico deste modelo
requested_model string ou null O modelo indicado na requisição: um alias como opus, um ID de modelo completo, ou null quando a requisição foi para o modelo padrão
source string De onde veio a requisição: "command" para /model <name>, a configuração Model em /config ou a ativação do modo rápido; "picker" para um seletor de modelo; "sdk" para 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
context_tokens number Tokens que a próxima requisição reenvia como seu prompt: os tokens de entrada, de leitura de cache, de criação de cache e de saída da última resposta na conversa principal, somados. 0 antes da primeira resposta
prompt_cache_warm boolean Se o cache de prompt do modelo atual provavelmente ainda está aquecido, o que significa que a troca o perde
cache_ttl string Tempo de vida do cache de prompt que o Claude Code solicita para esta sessão: "5m" ou "1h"
estimated_cache_write_usd number Custo estimado em dólares americanos de gravar context_tokens no cache de prompt em to_model na tarifa de cache_ttl, excluindo a próxima resposta. O servidor pode não precisar armazenar em cache todo o contexto novamente, então trate-o como uma estimativa
pricing string Como o Claude Code precificou estimated_cache_write_usd: "configured" nas tarifas próprias da sua organização quando ela as configurou, "catalog" no preço de tabela, ou "default" quando to_model não tem preço conhecido e o Claude Code assumiu uma tarifa padrão

Este exemplo mostra a entrada para /model opus em uma sessão que está usando o Sonnet 5:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreModelSwitch",
  "from_model": "claude-sonnet-5",
  "to_model": "claude-opus-5",
  "requested_model": "opus",
  "source": "command",
  "context_tokens": 182340,
  "prompt_cache_warm": true,
  "cache_ttl": "5m",
  "estimated_cache_write_usd": 1.1396,
  "pricing": "catalog"
}

Controle de decisão de 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:

Campo Descrição
permissionDecision "allow" prossegue e pula a confirmação que o Claude Code mostra enquanto o cache de prompt está aquecido. "deny" cancela a troca. "ask" solicita ao usuário que a confirme
permissionDecisionReason Para "deny", mostrado ao usuário como o motivo pelo qual a troca foi bloqueada, ou retornado como o erro de uma requisição set_model. Para "ask", mostrado no prompt de confirmação. Ignorado para "allow"

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:

{
  "hookSpecificOutput": {
    "hookEventName": "PreModelSwitch",
    "permissionDecision": "ask",
    "permissionDecisionReason": "Switching now re-sends about 180k tokens to the new model. Continue?"
  }
}

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 custo pode retornar {"systemMessage": "..."} e sair com 0.

Um hook PreModelSwitch que não responde antes do seu timeout bloqueia a troca. No PreToolUse, por outro lado, um hook de comando que atinge o timeout deixa a chamada de ferramenta continuar. O timeout padrão para este evento é de 30 segundos. O PreModelSwitch executa apenas hooks command, http e mcp_tool, então os padrões de prompt e agent não se aplicam.

Um hook que sai com um código diferente de 0 ou 2 e não imprime nenhuma decisão JSON não bloqueia: o Claude Code mostra seu stderr e aplica a troca, conforme descrito em Outros códigos de saída.

PostModelSwitch

É executado após a alteração do modelo da sessão. Use-o para dar ao Claude orientações específicas do modelo sem editar cada CLAUDE.md, por exemplo uma instrução válida 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á foi alterado. 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 a um turno, porque essa substituição dura um turno e deixa o modelo da sessão inalterado.

O matcher segue as mesmas regras que o PreModelSwitch: o Claude Code o compara com o nome canônico do modelo para o qual a sessão trocou.

Este exemplo adiciona orientações sempre que o modelo da sessão muda para qualquer modelo Opus:

{
  "hooks": {
    "PostModelSwitch": [
      {
        "matcher": ".*opus.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'On Opus, delegate implementation work to subagents and keep this conversation for planning and review.'"
          }
        ]
      }
    ]
  }
}

Para confirmar que o hook funciona, troque para um modelo Opus a partir de uma sessão que esteja usando um modelo diferente, por exemplo execute /model opus a partir de uma sessão do Sonnet, e depois pergunte ao Claude que orientações ele tem sobre o modelo atual.

Entrada de 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", ele é a configuração de modelo salva que o Claude Code restaurou.

Controle de decisão de PostModelSwitch

O Claude Code pega o stdout em texto simples do seu hook na saída 0, ou o 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:

Campo Descrição
additionalContext String adicionada ao contexto do Claude com a próxima requisição. Consulte Adicionar contexto para o Claude

Se o hook não tiver terminado em até 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 para o 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:

Motivo Descrição
clear Sessão limpa com o comando /clear
resume Sessão trocada via /resume interativo
logout O usuário fez logout
prompt_input_exit O usuário saiu enquanto a entrada do prompt estava visível
other Outros motivos de saída
bypass_permissions_disabled Removido na v2.1.234; o Claude Code não o envia. Remova-o dos seus matchers de SessionEnd

Entrada de 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 ver todos os valores.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

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 seus campos de saída JSON, 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ção, 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ê define também se torna o timeout de cada hook sem seu próprio timeout.

Este exemplo define o orçamento como 5 segundos:

CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

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, pulando o diálogo completamente.

Para um hook completo com sua entrada de configuração e script, consulte Responder a uma solicitação de formulário a partir de um script.

O campo matcher é comparado com o nome do servidor MCP.

Entrada de 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 elicitation no modo formulário, o caso mais comum:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": {
      "username": { "type": "string", "title": "Username" }
    }
  }
}

Para elicitation no modo URL, usado para autenticação baseada em navegador:

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please authenticate",
  "mode": "url",
  "url": "https://auth.example.com/login"
}

Saída de 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, saia com 0 e imprima um objeto hookSpecificOutput com uma action. O servidor recebe 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:

Para Retorne O servidor recebe
Responder pelo usuário "action": "accept", com os valores dos campos do formulário em content accept com o seu content
Recusar a solicitação "action": "decline" decline
Cancelar a solicitação "action": "cancel" cancel
Deixar a solicitação para o usuário Nenhuma saída, com código de saída 0 A resposta do usuário no diálogo

Esta saída responde à solicitação no modo formulário mostrada em Entrada de Elicitation. As chaves em content são os nomes das propriedades do requested_schema dessa solicitação:

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": {
      "username": "alice"
    }
  }
}

Esta saída recusa uma solicitação:

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "decline"
  }
}

No diálogo, selecionar Decline envia decline e pressionar Esc envia cancel, então retorne aquele que você quer que o servidor veja.

Para uma solicitação no modo URL, um hook que retorna accept pula 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, qualquer que seja a action que você retorne.

Outras maneiras de recusar uma elicitation

Seu hook também pode recusar destas maneiras. O servidor recebe o mesmo decline que para "action": "decline":

  • Sai com o 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:

#!/bin/bash
if [ "$(jq -r '.mode')" = "url" ]; then
  exit 2
fi

Nem o usuário nem o servidor veem por que seu hook recusou, porque o Claude Code não mostra seu stderr nem seu reason.

O Claude Code ignorou um decision de nível superior de hooks Elicitation e ElicitationResult desde a 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ção, com o nome do servidor como matcher:

{
"hooks": {
"Elicitation": [
{
"matcher": "issue-tracker",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh",
"args": []
}
]
}
]
}
}

Salve este script em .claude/hooks/answer-project-key.sh no seu projeto e torne-o executável com chmod +x:

#!/bin/bash
input=$(cat)
fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input")

if [ "$fields" = '["project_key"]' ]; then
jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}'
fi

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 elicitation 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 é comparado com o nome do servidor MCP.

Entrada de 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.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ElicitationResult",
  "mcp_server_name": "my-mcp-server",
  "action": "accept",
  "content": { "username": "alice" },
  "mode": "form"
}

Saída de ElicitationResult

Um hook ElicitationResult pode deixar a resposta do usuário passar, alterar seus valores ou bloqueá-la. Para alterar ou bloquear a resposta, saia 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:

Para Retorne O servidor recebe
Deixar a resposta passar Nenhuma saída, com código de saída 0 A resposta do usuário, inalterada
Alterar os valores enviados "action": "accept", com os novos valores em content accept com o seu content no lugar dos valores do usuário
Bloquear a resposta "action": "decline" decline, sem os valores do usuário
Cancelar a solicitação "action": "cancel" cancel, junto com os valores que o usuário enviou. Para retê-los, retorne "decline"

Esta saída altera a resposta mostrada em Entrada de ElicitationResult, de modo que o servidor recebe alice@example.com onde o usuário enviou alice:

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "accept",
    "content": {
      "username": "alice@example.com"
    }
  }
}

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 sua action substitui a dele. Verifique se a action da entrada é accept antes de retornar accept, ou 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:

#!/bin/bash
input=$(cat)

if [ "$(jq -r '.action' <<<"$input")" = "accept" ]; then
  jq '{hookSpecificOutput: {hookEventName: "ElicitationResult", action: "accept", content: (.content + {username: (.content.username + "@example.com")})}}' <<<"$input"
fi

Esta saída bloqueia a resposta:

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline"
  }
}

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 elicitation explica 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, qualquer que seja a action que você retorne.

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:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
Campo Obrigatório Descrição
type sim Deve ser "prompt"
prompt sim O texto do prompt a enviar para o LLM. Use $ARGUMENTS como placeholder para a entrada JSON do hook. Se $ARGUMENTS não estiver presente, entrada JSON é anexada ao prompt
model não Modelo a usar para avaliação. Padrão para o modelo que Claude Code usa para funcionalidade em segundo plano
timeout não Timeout em segundos. Padrão: 30
continueOnBlock não Nos eventos aos quais se aplica, true alimenta uma razão ok: false de volta para Claude e continua em vez de terminar o turno. Padrão: false. Veja Esquema de resposta para comportamento por evento

Esquema de resposta

O LLM deve responder com JSON contendo:

{
  "ok": true | false,
  "reason": "Explanation for the decision",
  "impossible": true | false
}
Campo Descrição
ok true para permitir. Para false, veja o comportamento por evento abaixo
reason Obrigatório quando ok é false
impossible Opcional. O modelo o retorna com ok: false quando julga que a condição nunca pode ser satisfeita. Em Stop e SubagentStop, Claude Code então permite que o turno termine em vez de alimentar a razão de volta. Hooks de agente e outros eventos o ignoram

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". Antes da v2.1.210, a razão de negação era retornada para Claude como o erro da ferramenta e o turno continuava
  • 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": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Hooks baseados em agente

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:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

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:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true
          }
        ]
      }
    ]
  }
}

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. Diferentemente de um systemMessage de hook síncrono, nenhum dos dois campos é mostrado para você.

Claude Code valida que a 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. Antes da v2.1.202, saída JSON malformada de um hook assíncrono poderia travar a sessão, e a falha recorria cada vez que a sessão era retomada.

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:

#!/bin/bash
# run-tests-async.sh

# Leia entrada de hook de stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Apenas execute testes para arquivos de origem
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi

# Execute testes e relate resultados ao Claude via additionalContext
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
  MSG="Tests passed after editing $FILE_PATH"
else
  MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'

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:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
            "args": [],
            "async": true
          }
        ]
      }
    ]
  }
}

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. Não há desduplicação através de múltiplos disparos do mesmo hook assíncrono.

Considerações de segurança

Aviso

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.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "shell": "powershell",
            "command": "Write-Host 'File written'"
          }
        ]
      }
    ]
  }
}

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::

{
  "type": "command",
  "shell": "powershell",
  "command": "& \"$env:CLAUDE_PROJECT_DIR\\.claude\\hooks\\check.ps1\""
}

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:

2026-07-19T02:03:24.382Z [DEBUG] Hook output does not start with {, treating as plain text
2026-07-19T02:03:24.382Z [DEBUG] "Hook PostToolUse:Write (PostToolUse) success:\nhook-ran"

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.