SpyBara
Go Premium

hooks.md 2026-10-02 22:59 UTC to 2026-10-03 04:00 UTC

This page contains 599 additions and 594 deletions.

2026
Thu 1 23:59 Fri 2 22:59 Sat 3 04:59

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.

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 você envia um prompt, antes de Claude processá-lo
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 está sendo removido na saída da sessão, quando um subagente termina, ou quando você exclui uma sessão em segundo plano
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

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 você envia um prompt, antes de Claude processá-lo
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 está sendo removido na saída da sessão, quando um subagente termina, ou quando você exclui uma sessão em segundo plano
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 em nuvem em Claude Code na web 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 em 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 abaixo 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. Usa a mesma sintaxe que regras de permissão
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.

Para padrões Bash, 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 solicitaçã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 solicitaçã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. Requer Claude Code v2.1.196 ou posterior
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 da rodada atual quando um hook dispara. Hooks que precisam do texto final do assistente da rodada 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.

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 em muitos 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 agentic 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 Nega a elicitação
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 troubleshooting.

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 lista de permissões, 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 na lista de permissões:

  • 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 lista de permissões, 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 solicitação de 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 nesta 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 hookSpecificOutput action (accept/decline/cancel), content (valores de campo de formulário para accept)
ElicitationResult hookSpecificOutput action (accept/decline/cancel), content (valores de campo de formulário override)
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 os 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 o CLAUDE.md.

O SessionStart é executado em todas as sessões, então mantenha esses hooks rápidos. Somente hooks type: "command" e type: "mcp_tool" são suportados. Consulte Campos de hook de ferramenta MCP para saber quando os hooks mcp_tool são executados.

O valor do matcher corresponde à forma 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 retomada 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ê alterna entre conversas com /resume dentro de uma sessão, a troca espera os hooks terminarem. Se você executar /clear ou alternar 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 começou: "startup" para novas sessões, "resume" para sessões retomadas, "clear" após /clear, "compact" após a compactação ou "fork" para uma nova sessão bifurcada de uma existente
model O identificador do modelo ativo. Ele pode ser omitido, por exemplo após /clear ou quando uma sessão é restaurada pela recuperação de conversa, então verifique a existência do campo 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 está definido, por exemplo com --name, /rename, a saída sessionTitle de um hook ou renameSession() do Agent SDK. Um hook que emite sessionTitle pode verificar esse 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 em seguida como o próximo turno. Diferentemente de additionalContext, que se anexa a um turno existente, isso 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 estejam 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 para este 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 instala ou atualiza skills. A descoberta de skills normalmente é executada antes de os hooks SessionStart terminarem, então os arquivos que o hook grava em ~/.claude/skills/ ou .claude/skills/ só apareceriam, de outra forma, 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 placeholder; substitua-a pelo seu próprio repositório de skills. Com o placeholder, o clone falha e imprime uma mensagem fatal: no stderr. O stderr de um hook SessionStart que sai com 0 é apenas informativo, então a solicitação 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, grave 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 feitas por 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 somente quando você inicia o Claude Code com --init-only, ou com --init ou --maintenance no modo não interativo com a flag -p. Ele não é disparado na inicialização normal. Use-o para instalação única de dependências ou limpeza agendada que você aciona explicitamente a partir de CI ou scripts, separadamente da inicialização normal da sessão. Para inicialização por sessão, use o SessionStart.

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

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> por um local de arquivo de log, e verifique no log as entradas dos hooks Setup e SessionStart.

Como o Setup não é disparado a cada 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 ${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 ao armazenar 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 todo código de saída, o Claude Code descarta os campos de saída JSON de um hook Setup, como systemMessage, continue e hookSpecificOutput.additionalContext. Com -p, o stdout, o stderr e o código de saída de um hook Setup aparecem na saída da execução somente como eventos hook_response quando você inicia com --output-format stream-json --verbose.

Os hooks Setup têm acesso a CLAUDE_ENV_FILE. As variáveis gravadas nesse arquivo persistem nos comandos Bash subsequentes da sessão, assim como nos hooks SessionStart. Somente hooks type: "command" são executados em Setup. Um hook type: "mcp_tool" em 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 de forma antecipada e novamente mais tarde quando arquivos são carregados de forma tardia, 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 somente para arquivos carregados no início da sessão, ou "matcher": "path_glob_match|nested_traversal" para disparar somente para carregamentos tardios.

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 os 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 somente para carregamentos path_glob_match
trigger_file_path Caminho para o arquivo cujo acesso acionou este carregamento, para carregamentos tardios
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 o usuário envia um prompt, antes que o Claude o processe. Isso permite que você adicione contexto adicional com base no prompt/conversa, valide prompts ou bloqueie certos tipos de prompts.

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é ser concluído, um hook travado paralisa a sessão. Se o seu hook precisar de mais tempo, defina o campo timeout na entrada do hook.

Exceto por um hook de comando que você executa com async: true, um hook 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 nomeando o hook, o timeout que foi atingido e que a saída foi descartada.

Um hook de callback do Agent SDK em UserPromptSubmit que atinge seu timeout bloqueia o prompt com uma mensagem nomeando 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 aberta. 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 que o usuário enviou. O conteúdo colado que foi recolhido em um placeholder [Pasted text #N] chega expandido no lugar. Em sessões nas quais 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="…">, então leve essas linhas em conta se o seu hook analisar o prompt.

Os hooks UserPromptSubmit também recebem session_title quando a sessão tem um título personalizado, com o mesmo significado do campo session_title do SessionStart.

{
  "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 do usuário é processado e adicionar contexto. Todos os campos de saída JSON estão disponíveis.

Há duas formas de adicionar contexto à conversa com código de saída 0:

  • Stdout de 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, cada um, injetados 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 é encaminhado 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 se o hook bloquear com decision: "block" quanto saindo com 2. Um hook com saída 2 que não imprime JSON sempre recebe o texto do prompt em sua mensagem de bloqueio.

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, então 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 se expande em um prompt antes de chegar ao Claude. Use isso 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 somente quando o Claude chama a ferramenta, mas digitar /skillname diretamente contorna o PreToolUse. O UserPromptExpansion é disparado nesse caminho direto.

Faz a correspondência em command_name. Deixe o matcher vazio para disparar em todo comando do tipo prompt.

Entrada do UserPromptExpansion

Além dos campos de entrada comuns, os hooks UserPromptExpansion recebem expansion_type, command_name, command_args, command_source e a string prompt original. O campo expansion_type é slash_command para skills e comandos personalizados, ou mcp_prompt para prompts de servidor 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 se expanda. 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 é encaminhado 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 renderização, 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 mínima
  • transformar o texto que uma aplicação do Agent SDK mostra aos seus usuários
  • ocultar chaves de API ou hostnames internos das respostas do Claude

O Claude Code retém cada lote até que seu hook retorne, então 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 o seu hook precisar de mais tempo, defina o campo timeout na entrada do hook.

O MessageDisplay serve apenas para exibição: o texto de substituição altera somente o que é renderizado na tela. A transcrição e o que o Claude vê mantêm o texto original, então o Claude nunca vê a substituição, e o modo verbose mostra o original. O hook recebe apenas o texto das mensagens do assistente, então os resultados de ferramentas e o texto que você digita são renderizados sem alteração.

O MessageDisplay não suporta matchers e é disparado para toda mensagem do assistente que transmite texto; mensagens sem texto, como respostas que contêm apenas chamadas de ferramenta, não o acionam.

Em execuções não interativas, incluindo consultas do Agent SDK e claude -p, o MessageDisplay é executado uma vez por mensagem do assistente em vez de uma vez por lote de linhas. A chamada única chega 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 de cada mensagem recebe o mesmo texto total em ambos os modos.

Entrada do MessageDisplay

Além dos campos de entrada comuns, os hooks MessageDisplay recebem identificadores do turno e da mensagem, a posição desta chamada dentro da mensagem e o novo texto em delta. Os limites dos lotes dependem de como o texto é transmitido, então use index e final para acompanhar o progresso de 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, então não pode ser correlacionado com os ids de mensagens da transcrição
index Índice, começando 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, então trate final, e não um delta não vazio, como o sinal de fim da mensagem. Em execuções do Agent SDK e de 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 os acentos graves de código inline de delta e retorna o resultado como displayContent.

Registre um hook de comando para o evento no seu arquivo de configurações:

{
"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ção. Se o script falhar, por exemplo porque o jq não está instalado, o Claude Code exibe o texto original e registra a falha somente na saída de depuração, não na sessão.

PreToolUse

É executado depois que o Claude cria os parâmetros da ferramenta e antes de processar a chamada de ferramenta. Faz a correspondência com qualquer nome de ferramenta exceto EndConversation: ferramentas integradas como Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion e ExitPlanMode, e quaisquer nomes de ferramentas MCP.

Para executar um hook quando um arquivo específico muda no disco, independentemente de quem o gravou, use o FileChanged em vez de fazer a correspondência de ferramentas de edição de arquivos pelo nome. Diferentemente do PreToolUse, o Claude Code executa os hooks FileChanged após a alteração, e eles não têm controle de decisão, então não podem bloquear a gravação.

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

Um hook de callback do Agent SDK em PreToolUse que excede seu timeout bloqueia a chamada de ferramenta, e o Claude recebe um resultado de erro nomeando 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 informa 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 as 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, então um hook que faz correspondência em caminhos não pode ser contornado via ~ ou uma forma relativa de escrever o mesmo caminho
  • No Windows, o caminho chega com separadores de barra invertida, mesmo quando seu hook é executado no Git Bash, onde $PWD se parece com /c/project
  • Uma comparação escrita com barras normais, como uma verificação de /src/, nunca corresponde a um caminho com barras invertidas, e a chamada de ferramenta prossegue como se o hook não tivesse nada a bloquear
  • Normalize os separadores antes de comparar: FILE_PATH="${FILE_PATH//\\//}" no Bash, ou file_path.replace("\\", "/") no Python, e então faça a correspondência de um segmento de caminho como /src/ em vez de ancorar com ^, já que o caminho é absoluto

Uma chamada Write no Windows entrega:

{
  "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 informa quais arquivos podem defini-la. Caso contrário, ele as registra somente no modo auto e no modo bypassPermissions, e somente quando o Claude Code orienta o Claude a editar arquivos por meio do Bash. Defina bashEditDiffEnabled como false para desativar o registro. Comandos em segundo plano e comandos somente leitura não carregam diff.

Seu hook PostToolUse então recebe os arquivos alterados em tool_response.bashEditDiff. A lista 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 campos restantes informam 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 cubram ambas as ferramentas:

  • No Windows, onde quer que a ferramenta PowerShell esteja habilitada, o Claude trata o PowerShell como o shell principal e encaminha os comandos de shell por meio dele.
  • No Windows sem Git Bash, a ferramenta é habilitada automaticamente e o Claude Code não registra a ferramenta Bash.
  • Um hook que corresponde apenas a Bash nunca é disparado nesse caso.
Write

Cria ou sobrescreve um arquivo.

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 no qual 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 no qual 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 a 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 da qual 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 somente 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. Os subagentes são executados em segundo plano por padrão, então 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 recolhidas; definido somente 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 de API do subagente: tokens de entrada, de saída e de cache combinados. 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 feitas pelo subagente
usage object {"input_tokens": 8320, ...} Detalhamento de tokens por tipo da requisição final de API: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens

No Claude Code v2.1.271 ou posterior, um subagente que é 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, faça a correspondência de um hook PreToolUse ou PostToolUse em SubagentHandback e leia tool_input.message.

Para subagentes em segundo plano, a ferramenta retorna quando a tarefa passa para o segundo plano, então 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 que ocorreu antes disso é refletida ali. modelsUsed e o comportamento de resolvedModel no momento da passagem para o 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"}], "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 seleção múltipla 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 que o Claude saia do modo de planejamento. O Claude grava o plano em um arquivo no disco antes de chamar a ferramenta, então 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 aos hooks.

Campo Tipo Exemplo Descrição
plan string "## Refactor auth\n1. Extract..." Conteúdo do plano em Markdown. Injetado a partir do arquivo do plano no disco
planFilePath string "/Users/.../plans/refactor-auth.md" Caminho para o arquivo do 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 novamente do disco.

Controle de decisão do PreToolUse

Os hooks PreToolUse podem controlar se uma chamada de ferramenta prossegue. Diferentemente de outros hooks que usam um campo decision de nível superior, o PreToolUse retorna sua decisão dentro de um objeto hookSpecificOutput. Isso lhe dá um controle mais rico: quatro resultados (allow, deny, ask ou defer), além da capacidade de modificar a entrada da ferramenta antes da execução.

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 a ele. "deny" impede a chamada de ferramenta. "ask" solicita a confirmação do usuário. "defer" sai de forma controlada para que a ferramenta possa ser retomada depois. As regras deny e ask 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 na qual 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 somente no log de depuração
updatedInput Modifica os parâmetros de entrada da ferramenta antes da execução. Substitui o objeto de entrada inteiro, então 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, e 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. Ignorado 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 é encaminhado da mesma forma que "deny": o Claude vê a mensagem do stderr como o motivo da negação.

Quando um hook retorna "ask", o prompt de permissão exibido ao usuário inclui um rótulo que identifica 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.

O "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 solicitado pelo hook; o classificador ainda aplicava suas próprias regras de segurança a esse comando, e um "deny" de hook era sempre respeitado.

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

No modo não interativo com a flag -p, o Claude Code oferece AskUserQuestion e ExitPlanMode somente quando a execução tem um host de permissão para receber o prompt, como um callback canUseTool do Agent SDK. Essas ferramentas exigem interação do usuário. Retornar permissionDecision: "allow" junto com updatedInput satisfaz esse requisito: o hook lê a entrada da ferramenta do stdin, coleta a resposta por meio da sua própria UI e a retorna em updatedInput 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.

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 consegue confirmar que o hook coletou a interação de que a ferramenta precisa.

Adiar uma chamada de ferramenta para mais tarde

"defer" é para integrações que executam claude -p como subprocesso e leem sua saída JSON, como um app do Agent SDK ou uma UI personalizada construída sobre o Claude Code. Ele permite que esse processo chamador pause o Claude em uma chamada de ferramenta, colete a entrada por meio da sua própria interface e retome de onde parou. O Claude Code respeita este valor somente 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 somente quando tem um host de permissão, como uma ferramenta MCP que você passa com --permission-prompt-tool, então inicie a execução com um. O ciclo funciona assim:

  1. O Claude chama AskUserQuestion. O hook PreToolUse é disparado.
  2. O hook retorna permissionDecision: "defer". A ferramenta não é executada. O 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 na sua própria UI 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"}, {"label": "Vue"}], "multiSelect": false }] }
  }
}

Não há timeout nem limite de novas tentativas. A sessão permanece no disco até que você a retome, sujeita à limpeza de retenção cleanupPeriodDays, que exclui os arquivos de sessão após 30 dias por padrão, seguindo as regras de limpeza 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" 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ões. A restrição existe porque a retomada só pode executar novamente 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 que o hook seja 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 ausente.

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, ele nega a chamada de ferramenta. Para uma chamada que chega a um --permission-prompt-tool ou ao callback canUseTool do Agent SDK, os hooks são executados junto com o seu host, e o que decidir primeiro se aplica. 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 esperou cerca de seis segundos.

O Claude Code não executa hooks PermissionRequest para a requisição de rede de um comando em sandbox. Para obter um sinal para esse prompt, use o tipo de notificação permission_prompt.

Faz a correspondência com o nome da ferramenta, com os mesmos valores do PreToolUse.

Entrada do PermissionRequest

Os hooks PermissionRequest recebem os campos tool_name e tool_input como os hooks PreToolUse, mas sem tool_use_id. Para uma ferramenta MCP, eles também recebem o objeto mcp_server. Um array opcional permission_suggestions contém as atualizações de permissão que o Claude Code sugere para esta solicitação, como adicionar uma regra allow ou alterar o modo de permissão.

O array permission_suggestions não é uma lista exata das opções que você vê, porque cada diálogo de permissão monta suas próprias opções. Alguns diálogos, como o de edições de arquivos, não leem o array de forma alguma e derivam suas opções da própria solicitação. Um diálogo que o lê ainda pode omitir 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 cada chamada de ferramenta, precise ela de permissão ou não. Os hooks PermissionRequest são executados somente quando o Claude Code está prestes a pedir sua permissão, ou quando, de outra forma, ele negaria automaticamente uma chamada que não pode exibir um prompt. 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. As regras deny e ask ainda são avaliadas, então um hook que retorna "allow" não sobrescreve uma regra deny correspondente
updatedInput Somente para "allow": modifica os parâmetros de entrada da ferramenta antes da execução. Substitui o objeto de entrada inteiro, então inclua os campos inalterados junto com os modificados. A entrada modificada é reavaliada com base nas regras deny e ask
updatedPermissions Somente para "allow": array de entradas de atualização de permissão a serem aplicadas, como adicionar uma regra allow ou alterar o modo de permissão da sessão
message Somente para "deny": informa ao Claude por que a permissão foi negada
interrupt Somente para "deny": se true, interrompe o Claude

Um hook que sai com 2 sem um objeto decision deixa o fluxo de permissões inalterado, e seu stderr é descartado. Somente 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. O alias manual exige o Claude Code v2.1.200 ou posterior
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 ecoar 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 adequado:

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

Entrada do PostToolUse

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

Os hooks PostToolUse podem fornecer feedback ao Claude após a execução da ferramenta. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento:

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 de ela ser enviada ao Claude. O valor deve corresponder ao formato de saída da ferramenta
updatedMCPToolOutput Substitui a saída somente 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 informá-lo 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 com base nas suas próprias mensagens na conversa
  • Callbacks in-process do Agent SDK: quando uma aplicação que incorpora o Claude Code registra o hook como um callback do TypeScript SDK e retorna a nota durante a sessão ativa, o classificador pode considerar uma declaração do usuário repassada na nota como intenção do usuário. Tal declaração pode satisfazer um requisito de consentimento que o classificador aceitaria de uma mensagem enviada por você, mas nunca remove um bloqueio que sua própria mensagem também não conseguiria remover. 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 do PostToolUseFailure

Os hooks PostToolUseFailure recebem os mesmos campos tool_name e tool_input que o PostToolUse, junto com informações de erro como campos de nível superior. Para uma ferramenta MCP, eles também recebem o objeto mcp_server. Por exemplo, um comando npm test com falha pode 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 uma interrupção, 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 contém 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 que falhou. Seu formato varia conforme a ferramenta e a falha. Baseie seu hook em tool_name, is_interrupt e na primeira linha Exit code N; trate o restante da string como texto de exibição, não como um formato estável.

  • Para Bash e PowerShell, um comando que foi executado e encerrado produz uma primeira linha Exit code N, seguida de qualquer saída que o comando produziu como um único bloco com stdout e stderr intercalados
  • Um payload também pode conter uma mensagem de falha simples 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 do 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 de o Claude Code enviar a próxima requisição ao modelo. PostToolUse é disparado uma vez por ferramenta, o que significa que é disparado simultaneamente quando o Claude faz chamadas de ferramenta em paralelo. PostToolBatch é disparado exatamente uma vez com o lote completo, portanto é 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 do PostToolBatch

Além dos campos de entrada comuns, os hooks PostToolBatch recebem tool_calls, um array que descreve cada chamada de ferramenta no 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 em vez do conteúdo bruto do arquivo. As respostas podem ser grandes, portanto analise apenas os campos de que você precisa.

Controle de decisão do PostToolBatch

Os hooks PostToolBatch podem injetar contexto para o Claude. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento:

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 incluir e como 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 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 a resposta dele 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 do PermissionDenied

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

{
  "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 classificador estava indisponível, é o texto fixo Classifier unavailable

Controle de decisão do PermissionDenied

Os hooks PermissionDenied podem informar ao modelo que ele pode tentar novamente a chamada de ferramenta negada. Retorne um objeto JSON com hookSpecificOutput.retry definido como true:

{
  "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 próprio Claude Code não reverte a negação. Se o seu hook não retornar JSON, ou retornar retry: false, a negação se mantém e o modelo recebe a mensagem de rejeição original.

O Claude Code ignora retry: true quando o classificador não produziu nenhum veredito sobre a ação: sua resposta não pôde ser analisada, ou uma verificação de segurança separada do modo auto recusou a própria requisição do classificador. Para essas negações, o Claude Code já informa ao modelo, na mensagem de rejeição, se deve tentar novamente mais tarde ou seguir 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 uma 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 digitou nada por cerca de seis segundos
elicitation_url_dialog Um servidor MCP pede que você abra uma URL no navegador e você não digitou nada por 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 equipe de agentes ou o aviso do modo auto sobre cobranças por requisições do classificador e você não digitou nada por 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: na redefinição, ou antes 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 estava em suspensão por mais de cerca de 30 minutos. O Claude Code aguarda você pressionar Enter em vez de continuar. Após uma suspensão mais curta, ele continua e dispara quota_auto_resume_fired
quota_auto_resume_disabled O Claude Code encerra sua espera por um limite de uso do claude.ai sem continuar sua tarefa: autoContinueAtUsageLimit foi desativado ou a redefinição passou para mais de 24 horas adiante 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 de equipe requer Claude Code v2.1.248 ou posterior.

O Claude Code cronometra 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 pede 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 do Notification

Além dos campos de entrada comuns, os hooks Notification recebem message com o texto da notificação, um title opcional e notification_type indicando 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 que o exemplo de notificação da área de trabalho utiliza. Os hooks Notification destinam-se a efeitos colaterais, como encaminhar a notificação para 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 de uma equipe de agentes in-process processa uma nova mensagem. Suporta matchers para filtrar pelo nome do tipo de agente. Para agentes integrados, é o nome do agente, como general-purpose, Explore ou Plan. Para subagentes personalizados, é o campo name do frontmatter do agente, não o nome do arquivo.

Para subagentes fornecidos por um plugin, o tipo de agente é o identificador com escopo de plugin, como my-plugin:reviewer, não o nome simples do frontmatter. Os dois-pontos colocam um nome com escopo de plugin no caminho de expressão regular, portanto ancore o matcher com ^ e $ para uma correspondência exata: ^my-plugin:reviewer$.

Entrada do SubagentStart

Além dos campos de entrada comuns, os hooks SubagentStart recebem agent_id com o identificador único do subagente e agent_type com o nome do agente 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 de 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 próxima execução.

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 do SubagentStop

Além dos campos de entrada comuns, os hooks SubagentStop recebem stop_hook_active, agent_id, agent_type, agent_transcript_path e last_assistant_message. O campo agent_type é o valor usado para a filtragem do matcher. O transcript_path é a transcrição da sessão principal, enquanto agent_transcript_path é a transcrição do próprio subagente, armazenada em uma pasta aninhada subagents/. O campo last_assistant_message contém o conteúdo de texto da resposta final do subagente, para que os hooks possam acessá-lo sem analisar o arquivo de transcrição.

Nem todo evento SubagentStop vem de um subagente 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 o 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 executado com a ferramenta SubagentHandback entrega seu relatório por meio dessa ferramenta antes de parar. O campo last_assistant_message passa então a conter 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 do 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 dos hooks Stop, incluindo hookSpecificOutput.additionalContext com hookEventName definido como "SubagentStop", para feedback que não é de erro e 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 suportam matchers e são disparados em todas as ocorrências.

Entrada do TaskCreated

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

{
  "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 de equipe 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

Controle de decisão do TaskCreated

Um hook TaskCreated pode bloquear a criação de duas formas. 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 ou verificações de lint aprovados, antes que uma tarefa possa ser fechada.

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

Entrada do TaskCompleted

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

{
  "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 de equipe 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

Controle de decisão do TaskCompleted

Os hooks TaskCompleted suportam duas formas de controlar a conclusão de tarefas:

  • Código de saída 2: a tarefa não é marcada como concluída e a mensagem de stderr é devolvida ao modelo como feedback.
  • JSON {"continue": false, "stopReason": "..."}: quando um colega de equipe que termina 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 do Stop

Além dos campos de entrada comuns, os hooks Stop recebem stop_hook_active, last_assistant_message, background_tasks e session_crons. O campo stop_hook_active é true quando o Claude Code já está continuando como resultado de um hook 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 continuaram o turno oito vezes seguidas, o Claude Code sobrescreve o próximo bloqueio e encerra o turno. 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 "a sessão terminou" de "a sessão está pausada aguardando que um trabalho em segundo plano a desperte novamente". Ambos os arrays estão presentes quando o registro de tarefas está acessível e ficam vazios quando não há nada 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 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 de 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 do Stop

Os hooks Stop e SubagentStop podem controlar se o Claude continua. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar estes campos específicos do evento:

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 que não é de erro para o Claude. A conversa continua para que o Claude possa agir com base nele, mas, ao contrário de decision: "block", ele é mostrado na transcrição como feedback do hook em vez de um erro do hook

Um hook que bloqueia saindo com código 2 é encaminhado da mesma forma que reason: o Claude recebe a mensagem de 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 está funcionando conforme projetado e fornecendo orientação ao Claude, como "execute a suíte de testes antes de terminar". Ele mantém a conversa em andamento com as mesmas proteções contra loop de decision: "block", 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, exceto terminalSequence. Use-o para registrar falhas em log, enviar alertas ou tomar ações de recuperação quando o Claude não consegue concluir uma resposta devido a rate limits, problemas de autenticação ou outros erros de API.

Entrada do StopFailure

Além dos campos de entrada comuns, os hooks StopFailure recebem error, error_details opcional e last_assistant_message opcional. O campo error identifica o tipo de erro e é usado para a filtragem do matcher.

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. Ao contrário 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 de erro da 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 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 suportam matchers e são disparados em todas as ocorrências.

Entrada do TeammateIdle

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

{
  "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 de equipe que está prestes a ficar ocioso
team_name Descontinuado. Nome da equipe derivado da sessão; será removido em uma versão futura

Controle de decisão do TeammateIdle

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

  • Código de saída 2: o colega recebe a mensagem de 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 do ConfigChange

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

{
  "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 do ConfigChange

Os hooks ConfigChange podem impedir que alterações de configuração entrem em vigor. Use o código de saída 2 ou um 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 exibido
{
  "decision": "block",
  "reason": "Configuration changes to project settings require admin approval"
}

As alterações de policy_settings não podem ser bloqueadas. Os hooks ainda são disparados para origens policy_settings quando um arquivo de configurações gerenciadas na máquina muda, então você pode usá-los para registrar essas edições em log, mas qualquer decisão de bloqueio é ignorada. Isso garante que as configurações gerenciadas pela empresa sempre entrem em vigor. O Claude Code não executa hooks ConfigChange quando configurações gerenciadas pelo servidor chegam ou são atualizadas.

O Claude Code age com base na decisão de bloqueio da saída JSON de um hook ConfigChange e descarta systemMessage e continue. Uma alteração bloqueada não exibe nenhuma mensagem para você nem para o Claude, seja o bloqueio feito 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íficas do projeto ou executar scripts de configuração automaticamente. Funciona em conjunto com FileChanged para ferramentas como 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.

O CwdChanged não suporta matchers e é disparado em todas as ocorrências.

Entrada do CwdChanged

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

{
  "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 do CwdChanged

Além dos campos de saída JSON disponíveis para todos os hooks, os hooks CwdChanged podem retornar watchPaths para definir dinamicamente quais caminhos de arquivo o FileChanged monitora:

Campo Descrição
watchPaths Array de caminhos absolutos. Substitui a lista de monitoramento dinâmica atual. Os caminhos da sua configuração de matcher são sempre monitorados. 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 deles e descarta continue. Em sessões interativas, ele mostra o systemMessage como uma breve notificação no terminal. A mensagem não chega ao fluxo de mensagens do SDK.

DirectoryAdded

É executado depois que você adiciona um diretório de trabalho no meio da sessão com o comando /add-dir, ou depois que um cliente SDK adiciona um com a requisição de controle register_repo_root. Use-o para preparar um repositório recém-adicionado, por exemplo instalando suas dependências.

O Claude Code não dispara este evento quando:

  • Você passa um diretório com a flag de inicialização --add-dir; SessionStart cobre esses diretórios
  • Você adiciona um diretório na aba Workspace de /permissions
  • Você adiciona um diretório que já é um diretório de trabalho ou está dentro de um

O Claude Code dispara o DirectoryAdded depois de atualizar o estado do sandbox e das permissões, então as ferramentas em sandbox já veem o novo diretório quando seu hook é executado. Os próprios comandos de hook são executados fora do sandbox.

O Claude Code não espera pelo hook: a adição é concluída imediatamente, e o hook é executado em segundo plano com o timeout padrão de 600 segundos.

O matcher filtra pela forma como o diretório foi adicionado:

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

Entrada do 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 deles e apresenta o restante de forma diferente conforme a origem:

  • slash_command: o Claude Code entrega o systemMessage do hook ao Claude como contexto no próximo turno da conversa, em vez de mostrá-lo a você. Uma contagem de hooks com falha aparece na transcrição. A saída completa das falhas vai para o log de depuração
  • register_repo_root: o Claude Code grava a saída de systemMessage e a saída de falhas somente no log de depuração

FileChanged

É executado quando um arquivo monitorado muda no disco. O Claude Code detecta alterações com um monitor do sistema de arquivos, não inspecionando chamadas de ferramenta, portanto 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 monitoramento: o valor é dividido em | e cada segmento é registrado como um nome de arquivo literal no diretório de trabalho, então ".envrc|.env" monitora exatamente esses dois arquivos. Padrões regex não são úteis aqui: um valor como ^\.env monitoraria um arquivo literalmente chamado ^\.env.
  • Filtrar quais hooks são executados: quando um arquivo monitorado muda, o mesmo valor filtra quais grupos de hooks são executados usando as regras de matcher padrão em relação ao nome base do arquivo alterado.

Este exemplo normaliza as terminações de linha em data.csv após qualquer alteração, incluindo um comando Bash ou um script externo que reescreve 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 proteção com grep testa a mesma coisa que o perl remove, um CR no final de uma linha, portanto a execução após uma normalização termina sem tocar no arquivo. Uma proteção menos rigorosa entra em loop para sempre, 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 fica com terminações LF.

Para monitorar arquivos que você não pode nomear de antemão, retorne watchPaths de um hook para atualizar a lista de monitoramento dinamicamente. O Claude Code inicia o monitor somente quando algo nomeia um arquivo a ser monitorado, então inicialize a lista com um grupo FileChanged cujo matcher nomeie pelo menos um arquivo, ou com um hook SessionStart ou CwdChanged que retorne watchPaths. O matcher ainda filtra quais grupos de hooks são executados quando um arquivo monitorado muda, então dê ao grupo que lida com caminhos dinâmicos um matcher omitido, que corresponde a todos os arquivos monitorados e não adiciona nada à lista de monitoramento. Um matcher "*" também corresponde a todos os arquivos, mas o Claude Code o registra na lista de monitoramento como qualquer outro valor, como um arquivo literal chamado *.

Os hooks FileChanged têm acesso a CLAUDE_ENV_FILE. As variáveis gravadas nesse arquivo persistem nos comandos Bash subsequentes até o próximo evento CwdChanged, quando o Claude Code as limpa.

Entrada do FileChanged

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

Campo Descrição
file_path Caminho absoluto do 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 do FileChanged

Além dos campos de saída JSON disponíveis para todos os hooks, os hooks FileChanged podem retornar watchPaths para atualizar dinamicamente quais caminhos de arquivo são monitorados:

Campo Descrição
watchPaths Array de caminhos absolutos. Substitui a lista de monitoramento dinâmica atual. Os caminhos da sua configuração de matcher são sempre monitorados. Use isto quando seu script de hook descobrir arquivos adicionais a monitorar 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 deles e descarta continue. Em sessões interativas, ele mostra o systemMessage como uma breve notificação no terminal. A mensagem não chega ao fluxo de mensagens do SDK.

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 o comportamento padrão por completo, .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 do diretório do worktree criado. O Claude Code usa esse caminho como o diretório de trabalho da sessão isolada. Consulte Saída do 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 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 do WorktreeCreate

Além dos campos de entrada comuns, os hooks WorktreeCreate recebem o campo name. Ele é 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 do 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 os códigos de escape ANSI antes de ler essa linha, então os 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 com o nome do 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 um worktree está sendo removido. Este é o equivalente de limpeza do WorktreeCreate. O evento é disparado quando:

  • você sai de uma sessão --worktree e escolhe removê-lo
  • um subagente com isolation: "worktree" termina
  • você exclui uma sessão em segundo plano cujo worktree foi criado pelo hook

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

  • Sem hook WorktreeRemove: quando você sai de uma sessão --worktree e escolhe a remoção, o Claude Code 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 da visualização de agentes.
  • O hook encerra com 0: o worktree é considerado 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 encerra 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 do git. Um hook que excluiu o diretório antes de encerrar com código diferente de zero é considerado como tendo removido o worktree. Para saber como a falha é relatada, consulte Entrada do WorktreeRemove.

O Claude Code nunca exclui um branch pertencente a um worktree criado por hook, porque ele conhece apenas o caminho que seu hook WorktreeCreate retornou. Se seu hook WorktreeCreate criar 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 na visualização de agentes; para um worktree assim, 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 pelo 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 do 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 encerra com código diferente de zero e o diretório em worktree_path ainda existe depois, a remoção falha:

  • O worktree permanece no disco, e o comando e o stderr do hook vão para o log de depuração.
  • Se você estava excluindo uma sessão em segundo plano, a sessão também permanece. A mensagem de recusa na visualização de agentes informa como o hook terminou, como exited 1, cita o início do seu stderr e diz se excluir a sessão novamente remove o diretório mesmo assim.

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

Encerre 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 do PreCompact

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

{
  "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 depois que o Claude Code conclui uma operação de compactação. Use este evento para reagir ao novo estado compactado, por exemplo, para registrar em log o resumo gerado ou atualizar um estado externo. O Claude Code descarta os campos systemMessage e continue de um hook PostCompact.

Os mesmos valores de matcher do PreCompact se aplicam:

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 do PostCompact

Além dos campos de entrada comuns, os hooks PostCompact recebem trigger e compact_summary. O campo compact_summary contém o resumo da conversa gerado pela operação de compactação.

{
  "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 vai custar antes que ela aconteça.

O PreModelSwitch requer o Claude Code v2.1.251 ou posterior. O Claude Code o executa para estas solicitações:

  • /model <name> e o seletor do /model
  • O seletor de modelo Option+P ou Alt+P
  • A configuração Model em /config
  • Ativar o modo rápido quando isso altera o modelo da sessão
  • Uma requisição set_model, ou uma mudança 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 mudanças 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 abrange 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 somente o seu gateway de LLM conhece, ele executa todos os hooks PreModelSwitch independentemente do matcher. Portanto, um hook que bloqueia deve verificar to_model em 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 encerrando com o código 2 e permite 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 em 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 do PreModelSwitch

Além dos campos de entrada comuns, os hooks PreModelSwitch recebem os campos desta tabela. Os cinco últimos 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 de origem da troca
to_model string ID do modelo de destino da troca. O matcher é comparado com o nome canônico desse modelo
requested_model string ou null O modelo indicado na solicitação: um alias como opus, um ID de modelo completo, ou null quando a solicitação foi para o modelo padrão
source string De onde veio a solicitaçã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 mudança 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 à taxa de cache_ttl, excluindo a próxima resposta. O servidor pode não precisar refazer o cache de todo o contexto, então trate-o como uma estimativa
pricing string Como o Claude Code precificou estimated_cache_write_usd: "configured" pelas taxas próprias da sua organização quando ela as configurou, "catalog" pelo preço de tabela, ou "default" quando to_model não tem preço conhecido e o Claude Code assumiu uma taxa padrão

Este exemplo mostra a entrada para /model opus em uma sessão 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 do PreModelSwitch

Os hooks PreModelSwitch podem cancelar a troca, pedir ao usuário que a confirme ou deixá-la prosseguir. O código de saída 2 ou um decision: "block" no 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" pede 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 erro para 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 seu hook retornar, independentemente da decisão, então um hook de relatório de custo pode retornar {"systemMessage": "..."} e encerrar 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 atingiu 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 encerra 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 depois que o modelo da sessão muda. Use-o para dar ao Claude orientações específicas do modelo sem editar cada CLAUDE.md, por exemplo, uma instrução para toda a organização que se aplica a determinados modelos.

O PostModelSwitch requer o Claude Code v2.1.251 ou posterior. Ele não pode bloquear, porque o modelo já mudou. O Claude Code executa hooks PostModelSwitch após qualquer uma destas mudanças:

  • 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 mantém o modelo da sessão inalterado.

O matcher segue as mesmas regras do PreModelSwitch: o Claude Code o compara com o nome canônico do modelo para o qual a sessão 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 em uma sessão do Sonnet, e depois pergunte ao Claude quais orientações ele tem sobre o modelo atual.

Entrada do PostModelSwitch

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

requested_model é null quando source é "auto". Quando source é "resume", é a configuração de modelo salva que o Claude Code restaurou.

Controle de decisão do PostModelSwitch

O Claude Code pega o stdout em texto simples do seu hook ao encerrar com 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 dentro de cinco segundos depois que 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 referente ao modelo de destino da última troca.

SessionEnd

É executado quando uma sessão do Claude Code termina. Útil para tarefas de limpeza, registro em log 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 do SessionEnd

Além dos campos de entrada comuns, os hooks SessionEnd recebem um campo reason indicando por que a sessão terminou. Consulte a tabela de motivos acima para 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 uma caixa de diálogo interativa para o usuário responder. Os hooks podem interceptar essa solicitação e responder programaticamente, pulando totalmente a caixa de diálogo.

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

Entrada do Elicitation

Além dos campos de entrada comuns, os hooks Elicitation recebem mcp_server_name, message e os campos opcionais mode, url, elicitation_id e requested_schema.

Para elicitação no modo de formulário, o caso mais comum:

{
  "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 elicitação no modo URL, usada 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 do Elicitation

Para responder programaticamente sem mostrar a caixa de diálogo, retorne um objeto JSON com hookSpecificOutput:

{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": {
      "username": "alice"
    }
  }
}
Campo Valores Descrição
action accept, decline, cancel Se deve aceitar, recusar ou cancelar a solicitação
content object Valores dos campos do formulário a serem enviados. Usado somente quando action é accept

O código de saída 2 nega a elicitação. O Claude Code não mostra sua mensagem de stderr em lugar nenhum.

O Claude Code age com base no hookSpecificOutput da saída JSON de um hook Elicitation e descarta systemMessage e continue.

ElicitationResult

É executado depois que um usuário responde a uma elicitação MCP. Os hooks podem observar, modificar ou bloquear a resposta antes que ela seja enviada de volta ao servidor MCP.

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

Entrada do ElicitationResult

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

{
  "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",
  "elicitation_id": "elicit-123"
}

Saída do ElicitationResult

Para sobrescrever a resposta do usuário, retorne um objeto JSON com hookSpecificOutput:

{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline",
    "content": {}
  }
}
Campo Valores Descrição
action accept, decline, cancel Sobrescreve a ação do usuário
content object Sobrescreve os valores dos campos do formulário. Significativo somente quando action é accept

O código de saída 2 bloqueia a resposta, alterando a ação efetiva para decline. O Claude Code não mostra sua mensagem de stderr em lugar nenhum.

O Claude Code age com base no hookSpecificOutput da saída JSON de um hook ElicitationResult e descarta systemMessage e continue.

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.

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.