SpyBara
Go Premium

hooks.md 2026-11-06 23:01 UTC to 2026-11-07 23:58 UTC

This page contains 1 addition and 1 deletion.

2026
Mon 2 23:57 Tue 3 23:58 Fri 6 23:01 Sat 7 23:58 Mon 9 23:02 Tue 10 21:57 Wed 11 22: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.

[!TIP]

Para um guia de início rápido com exemplos, consulte Automatizar ações com hooks.

Hooks são comandos shell definidos pelo usuário, endpoints HTTP, chamadas de ferramentas MCP, prompts LLM ou subagentos que executam automaticamente em pontos específicos do ciclo de vida do Claude Code. O Claude Code dispara os mesmos eventos de hook onde quer que seja executado: sessões no terminal, extensões de IDE, o aplicativo Desktop e sessões na nuvem. Use esta referência para consultar esquemas de eventos, opções de configuração, formatos de entrada/saída JSON e recursos avançados como hooks assíncronos, hooks HTTP e hooks de ferramentas MCP.

Um plugin também pode registrar hooks como funções JavaScript que o Claude Code chama em seu próprio processo, que podem desenhar na interface, bem como agir sobre eventos. Um plugin que faz isso é um mod, e esses hooks de função são cobertos em Reagir a eventos em vez de aqui. Os hooks nesta página continuam funcionando junto com mods.

Ciclo de vida do hook

Claude Code executa hooks em pontos específicos durante uma sessão. Quando um evento dispara e um matcher corresponde, Claude Code passa contexto JSON sobre o evento para seu manipulador de hook. Para hooks de comando, a entrada chega em stdin. Para hooks HTTP, chega como corpo da solicitação POST. Seu manipulador pode então inspecionar a entrada, tomar ação e opcionalmente retornar uma decisão.

Os eventos caem em três cadências:

  • por sessão: SessionStart e SessionEnd
  • por turno: UserPromptSubmit, Stop e StopFailure
  • em cada chamada de ferramenta dentro do loop agentic: PreToolUse e PostToolUse, exceto chamadas EndConversation, que pulam ambas

Diagrama do ciclo de vida do hook mostrando Setup opcional alimentando SessionStart, depois um loop por turno contendo UserPromptSubmit, UserPromptExpansion para slash commands, o loop agentic aninhado (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), e Stop ou StopFailure, seguido por TeammateIdle, PreCompact, PostCompact e SessionEnd, com Elicitation e ElicitationResult aninhados dentro da execução de ferramenta MCP, PermissionDenied como um ramo lateral de PermissionRequest para negações em modo automático, WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, FileChanged e DirectoryAdded como eventos assíncronos independentes, PreModelSwitch como um evento sequencial independente que é executado antes de uma mudança de modelo solicitada, PostModelSwitch como um evento assíncrono independente que é executado após as mudanças de modelo da sessão, e MessageDisplay como um evento somente de exibição que é executado enquanto o texto da mensagem do assistente é transmitido

A tabela abaixo resume quando cada evento dispara. A seção Eventos de hook documenta o esquema de entrada completo e as opções de controle de decisão para cada um.

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

Como um hook é resolvido

Para ver como o evento, o matcher e o manipulador se encaixam, considere este hook PreToolUse que bloqueia comandos shell destrutivos.

[tab] macOS/Linux

O matcher se restringe a chamadas de ferramenta Bash e a comandos Monitor, e a condição if se restringe ainda mais a subcomandos Bash correspondendo a rm *, então block-rm.sh apenas é gerado quando ambos os filtros correspondem:

{
  "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.

[tab] Windows (PowerShell)

O matcher Bash|PowerShell cobre a ferramenta PowerShell bem como Bash. Uma única regra if nomeia apenas uma ferramenta, então cada ferramenta obtém seu próprio manipulador: o primeiro se restringe a subcomandos Bash correspondendo a rm *, o segundo a comandos PowerShell correspondendo a Remove-Item *. Ambos executam o mesmo script através de powershell.exe:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
            ]
          },
          {
            "type": "command",
            "if": "PowerShell(Remove-Item *)",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"
            ]
          }
        ]
      }
    ]
  }
}

A flag -NoProfile pula o carregamento de seu perfil PowerShell para que o hook inicie rapidamente, e -ExecutionPolicy Bypass permite que PowerShell execute o arquivo de script local.

O script lê a entrada JSON de stdin, extrai o comando e retorna uma permissionDecision de "deny" se contiver rm -rf ou Remove-Item seguido por -Recurse. Salve-o em .claude/hooks/block-rm.ps1 em seu projeto:

# .claude/hooks/block-rm.ps1
$callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
$command = $callInput.tool_input.command

if ($command -match 'rm -rf|Remove-Item.*-Recurse') {
  @{
    hookSpecificOutput = @{
      hookEventName = "PreToolUse"
      permissionDecision = "deny"
      permissionDecisionReason = "Destructive command blocked by hook"
    }
  } | ConvertTo-Json
} else {
  exit 0  # no decision; normal permission flow applies
}

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.

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 ele é acionado, como "para a ferramenta Bash"
  3. Defina um ou mais manipuladores de hook para executar quando correspondido

Consulte Como um hook é resolvido acima para um passo a passo completo com um exemplo anotado.

[!NOTE]

Esta página usa termos específicos para cada nível: evento de hook para o ponto do ciclo de vida, grupo de matcher para o filtro e manipulador de hook para o comando shell, endpoint HTTP, ferramenta MCP, prompt ou agente que executa. "Hook" por si só refere-se ao recurso geral.

Locais de hooks

Onde você define um hook determina seu escopo:

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

Sessões na nuvem não leem seu ~/.claude/settings.json local. Em um ambiente auto-hospedado, Claude Code também executa os hooks que o operador propagou do ~/.claude/ do host do runner, e executa os hooks no arquivo de configurações gerenciadas da imagem do runner quando esse arquivo está entre as fontes gerenciadas que Claude Code aplica, o que por padrão significa apenas quando nem configurações gerenciadas pelo servidor nem uma política Claude Code entregue por MDM fornece o nível gerenciado. Consulte o que é transferido da sua configuração para saber quais arquivos de configurações e plugins, e portanto quais hooks, chegam a uma sessão na nuvem.

Para detalhes sobre resolução de arquivo de configurações, consulte settings.

Hooks de arquivos de configurações, configurações de política gerenciada e plugins também executam dentro de subagentes. Quando um subagente chama uma ferramenta, eventos de ferramenta como PreToolUse e PostToolUse disparam os mesmos hooks configurados que na conversa principal, e a entrada carrega os campos de entrada comuns agent_id e agent_type que identificam o subagente.

Administradores podem usar allowManagedHooksOnly em configurações gerenciadas para restringir quais hooks executam:

  • Seus hooks de usuário, projeto, local e plugin são bloqueados. Hooks de plugins forçadamente ativados em configurações gerenciadas enabledPlugins são isentos
  • Claude Code também restringe suas configurações statusLine, fileSuggestion e subagentStatusLine às configurações gerenciadas
  • Claude Code também desabilita plugins com uma fonte command, incluindo plugins forçadamente ativados em configurações gerenciadas enabledPlugins, a menos que disableCommandPluginSources seja explicitamente definido como false. Fontes command requerem Claude Code v2.1.229 ou posterior
  • Claude Code também bloqueia comandos headersHelper do marketplace a menos que disableCommandPluginSources seja explicitamente definido como false, exceto para um marketplace que as próprias configurações gerenciadas declarem

Consulte o que executa sob allowManagedHooksOnly.

Entradas de hook se mesclam entre níveis de configurações em vez de se substituírem: configurações de usuário, projeto e local adicionam seus próprios hooks sem remover os gerenciados, e a configuração disableAllHooks não pode desabilitar hooks gerenciados de fora das configurações gerenciadas.

As listas de permissões de hook HTTP se aplicam a hooks de todas as fontes, incluindo configurações de política gerenciada:

  • allowedHttpHookUrls: quando definido em qualquer nível de configurações, Claude Code executa um manipulador de hook HTTP apenas se sua URL corresponder à lista de permissões mesclada
  • httpHookAllowedEnvVars: quando definido, Claude Code interpola apenas as variáveis de ambiente nessa lista em cabeçalhos de hook

Padrões de matcher

O campo matcher filtra quando hooks disparam. Como um matcher é avaliado depende dos caracteres que contém:

Valor do matcher Avaliado como Exemplo
"*", "" ou omitido Corresponder a todos dispara em cada ocorrência do evento
Apenas letras, dígitos, _, -, espaços, , e | String exata ou lista de strings exatas separadas por | ou , com espaço em branco opcional ao redor Bash corresponde à ferramenta Bash e, em PreToolUse e PermissionRequest, também a comandos Monitor; 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, auth_storage_failure, 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, plugin_notification
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"
          }
        ]
      }
    ]
  }
}

Corresponder comandos Monitor

Um hook PreToolUse ou PermissionRequest com um matcher Bash também executa em comandos shell que Claude inicia com a ferramenta Monitor, mas seu allow e seu updatedInput não têm efeito sobre eles. Requer Claude Code v2.1.314 ou posterior. Matchers como Bash|PowerShell, ^Bash$ e Bash.* também são matchers Bash. Um hook com um matcher Bash não executa para um WebSocket watch, e um hook PostToolUse com esse matcher não executa para Monitor.

Um hook que executa para um comando Monitor pode negá-lo ou fazer com que Claude Code pergunte a você sobre ele. Para permitir ou reescrever o comando, troque o matcher Bash por um que inclua Monitor:

  • Matcher que inclui Monitor: com um matcher como Bash|Monitor ou .*, ou sem matcher, o hook recebe a própria entrada da chamada Monitor, e allow e updatedInput se aplicam. Em if, nomeie Monitor, como em Monitor(tail *), que corresponde pelas regras que Bash(tail *) segue. Se o seu if nomear Bash, o hook ainda recebe a entrada da chamada Monitor, mas allow e updatedInput não têm efeito
  • Matcher Bash: o hook recebe uma chamada Bash. Em if, nomeie Bash, como em Bash(tail *). O hook não executa se o seu if nomear Monitor

Campos do manipulador de hook

Cada objeto no array hooks interno é um manipulador de hook: o comando shell, endpoint HTTP, ferramenta MCP, prompt LLM ou agente que executa quando o matcher corresponde. Existem cinco tipos:

  • Hooks de comando (type: "command"): executam um comando shell. Seu script recebe a entrada JSON do evento em stdin e comunica resultados através de códigos de saída e stdout.
  • Hooks HTTP (type: "http"): enviam a entrada JSON do evento como uma solicitação HTTP POST para uma URL. O endpoint comunica resultados através do corpo da resposta usando o mesmo formato de saída JSON que hooks de comando.
  • Hooks de ferramenta MCP (type: "mcp_tool"): chamam uma ferramenta em um servidor MCP configurado. A saída de texto da ferramenta é tratada como stdout de hook de comando.
  • Hooks de prompt (type: "prompt"): enviam um prompt para um modelo Claude para avaliação de turno único. O modelo retorna sua decisão como JSON. Consulte Hooks baseados em prompt.
  • Hooks de agente (type: "agent"): geram um subagente que pode usar ferramentas como Read, Grep e Glob para verificar condições antes de retornar uma decisão. Hooks de agente são experimentais e podem mudar. Consulte Hooks baseados em agente.

Quando um evento dispara, seus hooks correspondentes executam em paralelo. Se mais de 12 deles forem hooks de prompt ou de agente, 12 desses executam ao mesmo tempo e o restante começa à medida que os que estão em execução terminam. O timeout de cada hook em espera começa a contar quando esse hook inicia.

Se você definir o mesmo manipulador em mais de um arquivo de configurações, ele executa uma vez. Uma cópia do mesmo manipulador de um plugin ou skill permanece separada.

Manipuladores executam no diretório atual com o ambiente do Claude Code. Se o diretório atual não existir mais, por exemplo um worktree ou diretório temporário que outro shell deletou no meio da sessão, Claude Code executa hooks de comando a partir do primeiro destes que ainda existe: o diretório em que a sessão começou, a raiz do projeto, seu diretório home ou o diretório temporário do sistema. Claude Code registra um aviso nomeando o diretório de fallback no log de debug. Para uma sessão de worktree que você inicia a partir do aplicativo desktop, consulte O que os worktrees compartilham com o checkout principal.

A variável de ambiente $CLAUDE_CODE_REMOTE é "true" em ambientes web remotos e não é definida na CLI local. Claude Code v2.1.199 e posterior define $CLAUDE_CODE_BRIDGE_SESSION_ID para o ID de sessão Remote Control enquanto a sessão local tem uma conexão Remote Control ativa.

Campos comuns

Esses campos se aplicam a todos os tipos de hook:

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

O campo if contém exatamente uma regra de permissão. Não há sintaxe &&, || ou lista para combinar regras; para aplicar múltiplas condições, defina um manipulador de hook separado para cada.

Em uma condição if para uma ferramenta de arquivo, um padrão de diretório de segmento único como "Edit(src/**)" corresponde apenas ao diretório src no diretório de trabalho e aos arquivos sob ele. Para corresponder a um diretório nomeado src em qualquer profundidade, escreva "Edit(**/src/**)".

Como padrões if correspondem a comandos Bash

Para padrões Bash no campo if, se seu comando de hook executa depende da forma do padrão e do comando Bash que Claude está invocando. Atribuições VAR=value iniciais são removidas antes da correspondência.

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

Quando Claude Code não pode determinar quais comandos a entrada Bash executa, ele executa seu hook independentemente do padrão. Como o filtro if é melhor esforço, use o sistema de permissão em vez de um hook para impor um allow ou deny duro.

Campos de hook de comando

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

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

[!NOTE]

No Windows, a forma exec requer que command seja resolvido para um executável real como .exe. Os shims .cmd e .bat que npm, npx, eslint e outras ferramentas instalam em node_modules/.bin não são executáveis e não podem ser gerados sem um shell. Para executá-los em forma exec, invoque o script subjacente com node diretamente, por exemplo "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. O padrão node mais caminho-de-script funciona em todas as plataformas porque node.exe é um binário real. Para executar um shim .cmd ou .bat por nome, use forma shell.

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

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

[!NOTE]

Em forma exec, command é apenas o nome ou caminho do executável. Se command é um nome simples sem separador de caminho e contém espaço em branco junto com args, Claude Code registra um aviso porque o spawn falhará: não há executável nomeado node script.js. Mova os tokens extras para args. Caminhos absolutos com espaços, como C:\Program Files\nodejs\node.exe, são um executável válido único e não disparam o aviso.

Campos de hook HTTP

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

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
onFailure não O que acontece com a ação quando o hook falha: "continue", o padrão, ou "block". Consulte Bloquear a ação quando um hook falha. Requer Claude Code v2.1.295 ou posterior

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.

[!NOTE]

Worktrees são diferentes. Se Claude entra em uma worktree durante a sessão, Claude Code mantém ${CLAUDE_PROJECT_DIR} onde estava e passa o caminho da worktree para seus hooks de uma forma diferente:

  • ${CLAUDE_PROJECT_DIR} fica no lugar: ainda aponta para a raiz do projeto onde a sessão começou, então um comando como ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh ainda executa o script no checkout principal.
  • cwd segue Claude: o campo cwd na entrada JSON do hook é a raiz do worktree após Claude entrar em um worktree, e o novo diretório após Claude executar cd. Leia-o quando um hook precisa saber em qual diretório Claude está trabalhando.

Para uma sessão de worktree que você inicia a partir do aplicativo desktop, consulte O que os worktrees compartilham com o checkout principal para saber para onde ${CLAUDE_PROJECT_DIR} aponta.

Prefira forma exec para qualquer hook que referencie um placeholder de caminho. Em forma shell, envolva cada placeholder em aspas duplas.

[tab] Scripts de projeto

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": []
          }
        ]
      }
    ]
  }
}

[tab] Scripts de plugin

Defina hooks de plugin em hooks/hooks.json com um campo description opcional de nível superior. Quando um plugin está ativado, seus hooks se mesclam com seus hooks de usuário e projeto.

Este exemplo executa um script de formatação agrupado com o plugin:

{
  "description": "Automatic code formatting",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
            "args": [],
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Consulte a referência de componentes de plugin para detalhes sobre como criar hooks de plugin.

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.

Limites de hooks de skill

Quando os hooks de frontmatter de uma skill não disparam depois que você ou Claude invoca a skill, execute /hooks nessa sessão. Se ele os listar, consulte Hook não dispara. Se ele não listar nenhum deles, a skill pode estar sob um bloqueio exclusivo para plugins ou ter ultrapassado um limite de hooks, e executa sem nenhum de seus hooks de frontmatter. Verifique cada caso:

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

Se nenhum dos casos for o seu, verifique se há frontmatter que não foi analisado.

O menu /hooks

Digite /hooks no Claude Code para abrir um navegador somente leitura para seus hooks configurados. A lista rotula cada hook com sua origem, como configurações de usuário, configurações de projeto, configurações locais, um plugin ou a sessão atual.

Selecione um hook para ver o texto completo do que ele executa e onde está definido, como o caminho do seu arquivo de configurações ou o nome do seu plugin.

Para navegar por todos os eventos de hook, incluindo aqueles sem hooks configurados, selecione All events no final da lista.

Desabilitar ou remover hooks

Para remover um hook definido em um arquivo de configurações, delete sua entrada desse arquivo.

Para desabilitar temporariamente todos os hooks sem removê-los, defina "disableAllHooks": true em seu arquivo de configurações. Claude Code lê o valor deixado após precedência de configurações se aplicar, então um "disableAllHooks": false no .claude/settings.json de um projeto substitui um true em suas configurações de usuário. Para desabilitar hooks para uma execução qualquer que as configurações do projeto digam, passe --settings '{"disableAllHooks": true}', que tem precedência sobre configurações de projeto e local. Não há forma de desabilitar um hook individual mantendo-o na configuração.

A configuração disableAllHooks respeita a hierarquia de configurações gerenciadas. Se um administrador configurou hooks através de configurações de política gerenciada, disableAllHooks definido em configurações de usuário, projeto ou local não pode desabilitar esses hooks gerenciados. Apenas disableAllHooks definido no nível de configurações gerenciadas pode desabilitar hooks gerenciados. Para o alcance completo de cada nível, consulte disableAllHooks.

Edições diretas a hooks em arquivos de configurações são normalmente capturadas automaticamente pelo observador de arquivo.

Entrada e saída de hook

Hooks de comando recebem dados JSON via stdin e comunicam resultados através de códigos de saída, stdout e stderr. Hooks HTTP recebem o mesmo JSON como corpo da requisição POST e comunicam resultados através do corpo da resposta HTTP. Esta seção cobre campos e comportamento comuns a todos os eventos. Cada seção de evento sob Eventos de hook inclui seu esquema de entrada específico e opções de controle de decisão.

No macOS e Linux, hooks de comando executam em sua própria sessão sem um terminal controlador. O processo de hook e qualquer processo filho não podem abrir /dev/tty ou enviar sequências de escape diretamente para a interface do Claude Code. Windows não tem /dev/tty.

Para exibir uma mensagem ao usuário em qualquer plataforma, retorne systemMessage na saída JSON. Alguns eventos descartam isso ou o entregam em outro lugar, e cada seção de evento diz assim. Para disparar uma notificação de desktop, definir um título de janela ou tocar o sino, retorne terminalSequence em vez disso.

Campos de entrada comuns

Eventos de hook recebem esses campos como JSON, além de campos específicos do evento documentados em cada seção evento de hook. Para hooks de comando, este JSON chega via stdin. Para hooks HTTP, chega como corpo da requisição POST.

Campo Descrição
session_id Identificador de sessão atual
prompt_id UUID identificando o prompt do usuário sendo processado atualmente. Corresponde ao atributo prompt.id em eventos OpenTelemetry, para que você possa correlacionar saída de hook com telemetria para um único prompt. Ausente até a primeira entrada do usuário
transcript_path Caminho para JSON de conversa. O arquivo de transcrição é escrito de forma assíncrona e pode ficar atrás da conversa na memória, portanto pode não incluir ainda as mensagens mais recentes do turno atual quando um hook dispara. Hooks que precisam do texto final do assistente do turno atual devem usar last_assistant_message em Stop e SubagentStop em vez de ler a transcrição
cwd Diretório de trabalho atual quando o hook é invocado
scratchpad_dir Caminho para o diretório scratchpad da sessão, onde Claude mantém arquivos de trabalho temporários. Ausente quando a sessão não tem scratchpad ou o diretório temporário não está disponível. Requer Claude Code v2.1.257 ou posterior
permission_mode Modo de permissão atual: "default", "plan", "acceptEdits", "auto", "dontAsk" ou "bypassPermissions". O modo rotulado Manual chega como "default", nunca como "manual", portanto scripts que correspondem a "default" continuam funcionando. Nem todos os eventos recebem este campo. Verifique o exemplo JSON em cada seção evento de hook
effort Objeto cujo level é a configuração de nível de esforço quando o hook é executado, incluindo o nível de uma skill, de um subagente ou de ultrathink: "low", "medium", "high", "xhigh" ou "max". Presente para PreToolUse, PostToolUse, Stop, SubagentStop e outros eventos com um contexto de uso de ferramenta, em modelos com esforço. Se o modelo não suportar o seu nível, level é o nível que foi executado. Um nível que você alterou pode aparecer aqui antes de começar a valer. $CLAUDE_EFFORT contém o mesmo nível.
hook_event_name Nome do evento que disparou

agent_id e agent_type dizem ao seu script em qual agente um hook disparou, como um subagente, um colega de equipe em processo ou o agente que você escolheu com --agent:

Campo Descrição
agent_id Identificador único para o subagente ou colega de equipe em processo no qual o hook dispara.
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, exceto pelas variáveis exportadoras OTEL_* que Claude Code remove de cada subprocesso que spawna e, quando CLAUDE_CODE_SUBPROCESS_ENV_SCRUB é definido como 1, pelas variáveis que ele remove. Em uma sessão que recebe a configuração HIPAA, Claude Code também remove as credenciais da Anthropic do ambiente do hook.

Por exemplo, um hook PreToolUse para um comando Bash recebe isso em stdin:

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

Quando a execução do seu hook termina, Claude Code usa o código de saída dele para decidir se continua com a ação que o disparou, como uma chamada de ferramenta ou um prompt:

  • Sucesso: seu hook sai com 0. Claude Code aplica quaisquer campos de saída JSON que seu hook imprimiu, e a ação prossegue, a menos que esses campos a bloqueiem ou neguem. Hooks que iniciam um processo em segundo plano cobre a exceção.
  • Erro bloqueador: seu hook sai com 2. Em eventos que podem bloquear, Claude Code interrompe a ação.
  • Erro não-bloqueador: seu hook sai com qualquer outro código, ou falha de alguma outra forma, como não iniciar ou imprimir JSON inválido. A ação prossegue, exceto para hooks cuja falha bloqueia a ação, por exemplo um hook com onFailure definido como "block". Em eventos como PreToolUse, você vê um aviso <hook name> hook error na transcrição.

O que seu hook imprime em stdout pode mudar o resultado. Por exemplo, quando um hook PreToolUse sai com 1 mas imprime JSON que passa na validação, esse JSON ainda pode decidir o que acontece. Para encontrar o resultado do seu hook em um evento como PreToolUse, combine o que ele imprimiu em stdout na primeira coluna com seu código de saída no topo:

Stdout Saída 0 Saída 2 Qualquer outro código de saída
Objeto JSON que passa na validação de esquema Sucesso. Os campos se aplicam Erro bloqueador. Claude Code ainda lê os campos, mas eles não podem sobrescrever o bloqueio Sucesso. Claude Code ignora o código de saída, e apenas os campos decidem. Para hooks cuja falha bloqueia a ação, a execução conta como uma falha em vez disso
JSON que não pode ser analisado ou falha na validação de esquema Erro não-bloqueador. O aviso carrega a mensagem de análise ou validação Erro bloqueador. Seu stderr é a razão Erro não-bloqueador. O aviso carrega a mensagem de análise ou validação
Texto simples, ou nada Sucesso Erro bloqueador. Seu stderr é a razão Erro não-bloqueador. O aviso carrega a primeira linha do seu stderr

Alguns eventos têm suas próprias regras:

  • WorktreeCreate: qualquer código de saída diferente de zero faz a criação de worktree falhar, não importa o que seu JSON diga.
  • WorktreeRemove: qualquer código de saída diferente de zero faz a remoção de worktree falhar se o diretório ainda existir depois.
  • Stop, SubagentStop, TaskCompleted e o hook UserPromptSubmit de um plugin: quando seu hook sai com 2 sem nada em stdout e seu stderr diz que um arquivo está ausente, como No such file or directory, Claude Code trata a execução como um erro não-bloqueador.
  • Elicitation e ElicitationResult: Claude Code aplica seu hookSpecificOutput quando seu hook sai com 0, e o ignora em qualquer outro código de saída.
  • Eventos que descartam a saída do hook, como StopFailure: Claude Code ignora seu JSON em qualquer código de saída, exceto campos de efeito colateral como terminalSequence, que ainda disparam.

Para verificar o que o código de saída 2 faz no seu evento, consulte Comportamento de código de saída 2 por evento. Para verificar quais campos de decisão ele honra, consulte Controle de decisão.

Código de saída 0

Saída 0 significa sucesso, e é o código de saída pretendido quando você imprime JSON para controle estruturado. Quando seu hook sai com 0 mas imprime JSON que não pode ser analisado ou falha na validação de esquema, a ação prossegue, a menos que o hook seja um cuja falha bloqueia a ação. Em vez de um sucesso com seus campos aplicados, a execução é um erro não-bloqueador.

Para corrigir sua saída, use a mensagem de análise ou validação que o aviso <hook name> hook error carrega. Para um hook que não é async, seu stdout conta como saída JSON apenas quando você imprime um único objeto JSON, em uma linha ou em várias, sem nada ao redor além de espaço em branco. Claude Code lê qualquer outro stdout como texto simples ou uma falha de análise:

  • Saída que não começa com {, ou que começa com { e não termina com }: texto simples. Um array JSON e uma string JSON entre aspas são texto simples por esta regra.
  • Duas ou mais linhas que cada uma analisa como JSON por conta própria, a primeira começando com { e a última terminando com }: texto simples quando nenhuma linha é um objeto de saída JSON que define um campo. Quando uma linha é, a saída é uma falha de análise, portanto imprima esse objeto sem mais nada em stdout.
  • Qualquer outra coisa que começa com { e termina com } mas não é JSON válido: uma falha de análise.

Para a maioria dos eventos, Claude Code escreve stdout no log de debug e não o mostra na transcrição. Nestes eventos, seu stdout chega ao Claude ou ao resumo de compactação:

  • UserPromptSubmit, UserPromptExpansion, SessionStart e PostModelSwitch: Claude Code adiciona stdout em texto simples como contexto que Claude pode ver e agir. Ele não adiciona stdout que não conseguiu analisar.
  • PreCompact: seu stdout se torna instruções para o resumo de compactação

Claude nunca vê stderr de um hook que sai com 0. Para lê-lo você mesmo em eventos como PreToolUse, ative debug logging. Para exibir um aviso para Claude de um hook PostToolUse ou PostToolUseFailure, saia com 2 em vez disso para que Claude veja o stderr mesmo que a ferramenta já tenha executado.

Código de saída 2

Saia com código 2 para bloquear a ação. Em eventos que podem bloquear, Claude Code interrompe a ação: um hook PreToolUse bloqueia a chamada de ferramenta, por exemplo, e um hook UserPromptSubmit rejeita o prompt.

A mensagem que acompanha o bloqueio é o stderr do seu hook. Se seu hook também imprimiu JSON que toma uma decisão de bloqueio, Claude Code usa a razão dessa decisão em vez disso.

A saída 2 bloqueia mesmo quando seu hook imprime JSON:

  • JSON que passa na validação de esquema: Claude Code ainda lê os campos de saída JSON, mas eles não podem sobrescrever o bloqueio. Nem mesmo um permissionDecision de "allow" deixa a ação passar. Em Elicitation e ElicitationResult, o hookSpecificOutput de um hook exit-2 é ignorado.
  • JSON que falha na validação de esquema: o hook ainda bloqueia. Claude Code usa seu stderr como a razão de bloqueio e registra a falha de validação no log de debug.

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

#!/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

Com este script registrado como um hook PreToolUse em Bash, um comando que começa com rm é bloqueado, e Claude recebe o stderr do hook como o erro da ferramenta, prefixado com o nome do evento, o nome da ferramenta e o comando do hook:

PreToolUse:Bash hook error: [${CLAUDE_PROJECT_DIR}/.claude/hooks/no-rm.sh]: Blocked: rm commands are not allowed

Outros códigos de saída

Um aviso <hook name> hook error com Failed with non-blocking status code: significa que o código de saída do seu hook não foi 0 nem 2 e seu stdout era texto simples ou estava vazio. A menos que o hook seja um cuja falha bloqueia a ação, isso é um erro não-bloqueador, portanto seu hook não interrompeu a ação. Para capturar o stderr completo do seu hook em vez da primeira linha que o aviso mostra, ative debug logging.

Por exemplo, quando um hook PreToolUse em Bash imprime something broke em stderr e sai com 1, o aviso PreToolUse:Bash hook error carrega esta linha:

Failed with non-blocking status code: something broke

Se o aviso carregar a mensagem No such file or directory do shell, como na linha a seguir, o hook nunca foi executado, por exemplo porque o caminho do script não existe:

Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory

Se o caminho estiver errado, corrija-o onde o hook está configurado. Na forma shell, quando o caminho do script não existe ou não é executável, o shell sai com um código como 127 e o aviso carrega a mensagem do interpretador. A menos que o hook seja um cuja falha bloqueia a ação, um hook que não consegue iniciar também é um erro não-bloqueador.

Quando você configura um hook de política, observe este aviso em sua primeira execução, porque um caminho digitado incorretamente em settings.json significa que o hook nunca executa. Para bloquear a ação quando o hook não consegue iniciar, defina onFailure: "block".

[!WARNING]

Código de saída 1 sem saída JSON válida é um erro não-bloqueador, a menos que o hook seja um cuja falha bloqueia a ação, mesmo que 1 seja o código de falha Unix convencional. Se seu hook se destina a impor uma política, use exit 2.

Timeouts

Exceto por um hook de comando que você executa com async: true, Claude Code cancela um hook command, http ou mcp_tool que atinge seu timeout, descartando a saída do hook, portanto na maioria dos eventos um hook expirado não renderiza decisão. Em PreModelSwitch, um hook cancelado em seu timeout bloqueia a mudança de modelo.

Em PreToolUse, um hook command, http ou mcp_tool expirado não bloqueia a chamada da ferramenta, exceto para hooks cuja falha bloqueia a ação. A chamada continua através do fluxo de permissão normal, portanto não conte com um hook travado para agir como um portão. Para bloquear a chamada quando um hook command ou http atinge o timeout, defina onFailure: "block". Um hook de callback do Agent SDK que excede seu timeout bloqueia a chamada da ferramenta.

Hooks que iniciam um processo em segundo plano

Se seu hook de comando toma sua decisão em um processo em segundo plano e sai, uma negação que esse processo imprime depois que Claude Code parou de ler não bloqueia a chamada de ferramenta. Para um hook que não é async, Claude Code termina de ler a saída do hook logo após o próprio processo do hook sair.

Imprima a decisão antes de iniciar um processo em segundo plano, e redirecione o stdout e o stderr desse processo. Se seu script imprime um allow e depois inicia um processo em segundo plano sem redirecionar o stdout e o stderr desse processo, Claude Code bloqueia a chamada de ferramenta. O erro do hook inclui refusing to honor the suspect capture.

Este script de hook PreToolUse imprime a decisão com policy-check.sh e depois envia métricas em segundo plano:

#!/bin/bash
"$CLAUDE_PROJECT_DIR"/.claude/hooks/policy-check.sh || exit $?
"$CLAUDE_PROJECT_DIR"/.claude/hooks/upload-metrics.sh >/dev/null 2>&1 &

O restante do script faz o seguinte:

  • || exit $?: torna o código de saída da verificação o código do próprio hook, para que uma verificação que sai com 2 para negar ainda bloqueie. Caso contrário, o hook sai com o código da última linha, que é 0.
  • >/dev/null 2>&1 &: executa upload-metrics.sh em segundo plano com sua saída redirecionada, para que ele não mantenha a saída do hook aberta.

Para testar o script, faça policy-check.sh sair com 2 e então dispare uma chamada de ferramenta correspondida pelo hook. A chamada de ferramenta é bloqueada.

Bloquear a ação quando um hook falha

Na maioria dos eventos, quando um hook falha ou atinge o timeout, Claude Code ainda executa a ação. Um hook de política com um caminho errado ou um script que trava então deixa tudo passar. Para bloquear a ação em vez disso, defina "onFailure": "block" em um hook command ou http. Requer Claude Code v2.1.295 ou posterior. Para hooks de callback do Agent SDK, consulte Bloquear a ação quando um callback falha.

[!NOTE]

No Windows sem Git Bash, um hook de comando PreToolUse ou PermissionRequest em forma shell que falha com onFailure não definido pode, em vez disso, bloquear a chamada de ferramenta.

Este hook PreToolUse em .claude/settings.json executa um script do projeto antes de cada comando Bash, e bloqueia o comando se o script falhar:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node",
            "args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js"],
            "onFailure": "block"
          }
        ]
      }
    ]
  }
}

Para testá-lo, deixe check-command.js ausente e peça a Claude para executar um comando Bash como ls. Claude Code bloqueia a chamada, e o erro inclui failed; blocking because onFailure is "block" seguido pela própria saída de erro do node, reduzida aqui a uma linha:

PreToolUse:Bash hook error: [node ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js]: failed; blocking because onFailure is "block"
Error: Cannot find module '/path/to/project/.claude/hooks/check-command.js'

Após um timeout, a mensagem diz timed out em vez de failed. Sem onFailure definido, o mesmo script ausente é um erro não-bloqueador e ls é executado.

Cada um destes conta como uma falha:

  • Não consegue iniciar: um hook de comando falha ao iniciar, por exemplo porque o script ou executável não existe
  • Código de saída diferente de 0 ou 2: conta para um hook de comando mesmo que ele tenha impresso JSON que permite a ação, como permissionDecision: "allow". Para retornar uma decisão JSON, saia com 0
  • Erro HTTP: a conexão de um hook HTTP falha, ou o status da resposta não é 2xx
  • Timeout: o hook atinge seu timeout
  • Saída inválida: a saída JSON não pode ser analisada ou falha na validação de esquema. Para um hook HTTP, um corpo 2xx que não é vazio nem um objeto JSON também conta. Stdout em texto simples de um hook de comando não é uma falha

Com "block" definido, uma falha faz o que o código de saída 2 faz nesse evento, exceto em PermissionRequest, onde ela nega a solicitação. Por exemplo, uma falha em PreToolUse bloqueia a chamada de ferramenta e uma falha em UserPromptSubmit bloqueia o prompt.

O campo não tem efeito nestes hooks:

  • Hooks Stop, SubagentStop, TaskCompleted e TeammateIdle: o código de saída 2 nesses eventos manda Claude de volta para continuar trabalhando, e Claude não consegue reparar um hook que não executa
  • Hooks de comando em segundo plano: hooks de comando que definem async ou asyncRewake

Hooks com falha no Windows sem Git Bash

Se o erro de uma chamada de ferramenta bloqueada incluir este texto, um hook de comando PreToolUse ou PermissionRequest falhou ou atingiu o timeout e bloqueou a chamada:

blocking because Git Bash wasn't found, so only exit code 0 or 2 counts as this hook's verdict.

Se o erro também disser PowerShell started no program, comece pelas correções para um command composto apenas de strings entre aspas, que o PowerShell imprime em vez de executar.

Sem Git Bash, qualquer código de saída diferente de 0 ou 2 desse hook conta como uma falha, mesmo quando ele imprime JSON que não nega a chamada. Para descobrir por que o hook falhou, verifique o log de debug. Então escolha a correção que corresponde ao seu hook:

  • A chamada deve prosseguir quando o hook falha: adicione "onFailure": "continue" ao hook
  • O hook sai com 1 para avisar, mas deve bloquear quando trava: com "onFailure": "continue", a chamada prossegue com o código de saída 1, seja um aviso ou uma falha. Adicione "onFailure": "block" em vez disso, e faça o script sair com 0 onde ele avisa, para que essas chamadas prossigam. Para ainda mostrar o aviso em PreToolUse, imprima-o em stdout no campo systemMessage de um único objeto JSON.
  • O hook é escrito para Bash: sem Git Bash, um hook em forma shell sem shell definido é executado no PowerShell, onde pode falhar. Instale o Git for Windows, ou defina CLAUDE_CODE_GIT_BASH_PATH se o Git Bash estiver instalado em um local onde o Claude Code não o encontra. Então inicie uma nova sessão.
  • O hook executa um único .exe, não um arquivo .cmd ou .bat: coloque o programa em command e seus argumentos em args para usar a forma exec, e adicione "onFailure": "block". Claude Code então inicia o programa sem shell. Sem "onFailure": "block", a chamada pode prosseguir quando o programa falha, está ausente ou atinge o timeout
  • Um hook de proteção em PowerShell que deve bloquear em caso de falha: com onFailure não definido, ele deixa de fazer isso assim que o Git for Windows é instalado e o Claude Code encontra o Git Bash. Adicione "onFailure": "block" para que uma falha ou um timeout bloqueie a chamada com ou sem Git Bash. Defina também "shell": "powershell" para que o hook continue sendo executado no PowerShell a partir de então, em vez de no Git Bash

Antes da v2.1.309, a mensagem dizia blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.

PowerShell started no program

Um hook com este erro foi executado no PowerShell, que imprime um command composto apenas de strings entre aspas, como "command": "\"C:\\Tools\\guard.exe\"", em vez de executá-lo.

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

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

Código de saída 2 é a forma de um hook sinalizar "pare, não faça isso". O efeito depende do evento, porque alguns eventos representam ações que podem ser bloqueadas (como uma chamada de ferramenta que ainda não aconteceu) e outros representam coisas que já aconteceram ou não podem ser prevenidas.

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

Para SessionStart, SubagentStart e PostModelSwitch, Claude Code renderiza o stderr de código de saída 2 na transcrição como um aviso <hook name> hook error, da mesma forma que renderiza um erro não-bloqueador. Claude não vê, e a sessão ou subagente prossegue. Para SubagentStart, o aviso aparece na própria transcrição do subagente, não na conversa pai.

Tratamento de resposta HTTP

Hooks HTTP usam códigos de status HTTP e corpos de resposta em vez de códigos de saída e stdout. Para bloquear a ação quando a requisição falha ou retorna um status não-2xx, defina onFailure: "block". Para bloquear uma chamada de ferramenta ou negar uma permissão, retorne uma resposta 2xx com um corpo JSON contendo os campos de decisão apropriados.

Após um erro não-bloqueador, a ação prossegue, a menos que o hook seja um cuja falha bloqueia a ação. Exceto em eventos com seu próprio contrato de falha na tabela por evento, como WorktreeCreate, você obtém estes resultados para cada resposta ou falha:

  • 2xx com corpo vazio: sucesso, equivalente a código de saída 0 sem saída
  • 2xx com corpo de objeto JSON: analisado usando o mesmo esquema saída JSON que hooks de comando. Um corpo que falha na validação de esquema é um erro não-bloqueador
  • 2xx com qualquer outro corpo, como texto simples: erro não-bloqueador, tratado da mesma forma que um status não-2xx. Claude Code não adiciona o texto ao contexto de Claude
  • Status não-2xx: erro não-bloqueador
  • Falha de conexão: erro não-bloqueador
  • Timeout: o hook é cancelado, conforme descrito em Timeouts

Saída JSON

Códigos de saída permitem você bloquear ou ficar em silêncio, mas saída JSON oferece controle mais granular. Em vez de sair com código 2 para bloquear, saia 0 e imprima um objeto JSON em stdout. Claude Code lê campos específicos desse JSON para controlar comportamento, incluindo controle de decisão para bloquear, permitir ou escalar para o usuário.

[!NOTE]

Escolha uma abordagem por hook: ou use códigos de saída sozinhos para sinalizar, ou saia 0 e imprima JSON para controle estruturado. Se você misturar, saída 2 mantém seu efeito de bloqueio, e Claude Code ainda lê os campos JSON, com a exceção de elicitação única anotada em Código de saída 2.

Imprima apenas o objeto JSON em stdout. Para um hook que não é async, outro texto ali, como uma linha que seu perfil shell imprime na inicialização, impede Claude Code de ler o objeto como JSON, e Hook JSON não tem efeito mostra como encontrar e silenciar esse texto.

As strings de saída de hook additionalContext, systemMessage e initialUserMessage, e seu stdout simples, são limitadas a 10.000 caracteres:

  • Escopo: Claude Code mede cada string por conta própria, mesmo quando vários hooks executam para o mesmo evento. Para saída JSON, cada campo é medido separadamente; stdout simples é medido como um todo.
  • Acima do limite: exceto para o stdout de PreCompact, Claude Code salva a saída em um arquivo no diretório de sessão. Ele substitui a saída pelo caminho do arquivo e uma visualização de até os primeiros 2.000 caracteres. Um resultado Bash grande válido é tratado da mesma forma, descrito em Limites de saída. Diferentemente desse teto Bash, este limite não tem configuração ou variável de ambiente para aumentá-lo.
  • Lendo o arquivo: Claude Code não pede a Claude para ler o arquivo, portanto mantenha qualquer coisa que Claude sempre deva ver dentro do limite.

O objeto JSON suporta três tipos de campos:

  • Campos universais como continue são listados na tabela abaixo. Cada evento os aceita, mas alguns eventos os descartam ou entregam systemMessage em outro lugar que não a transcrição. Cada seção de evento diz assim. terminalSequence funciona nesses eventos também, com as exceções listadas em Emitir notificações de terminal.
  • decision e reason de nível superior são usados por alguns eventos para bloquear ou fornecer feedback.
  • hookSpecificOutput é um objeto aninhado para eventos que precisam de controle mais rico. Requer um campo hookEventName definido para o nome do evento.
Campo Padrão Descrição
continue true Se false, Claude para de processar inteiramente após o hook executar. Tem precedência sobre qualquer campo de decisão específico do evento
stopReason nenhum Mensagem mostrada ao usuário quando continue é false. Fica na conversa, portanto Claude a vê se a conversa continuar
suppressOutput false Não tem efeito: Claude Code aceita o campo mas não age sobre ele. O stdout de um hook bem-sucedido nunca é mostrado na transcrição e é registrado no log de debug
systemMessage nenhum Mensagem de aviso mostrada ao usuário. Em Agent SDK e saída --output-format stream-json, pode chegar como um SDKInformationalMessage
terminalSequence nenhum Uma sequência de escape de terminal para Claude Code emitir em seu nome, como uma notificação de desktop, título de janela ou sino. Restrito a OSC 0/1/2/9/99/777 e BEL. Se o valor contiver algo fora da allowlist, o campo é ignorado. Use isso em vez de escrever para /dev/tty, que não está disponível para hooks

Para parar Claude inteiramente:

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

Para hooks PreToolUse e PostToolUse, a parada se aplica mesmo quando a chamada da ferramenta falha ou é concluída enquanto Claude ainda está transmitindo uma resposta.

Emitir notificações de terminal

Hooks executam sem um terminal controlador, portanto escrever sequências de escape diretamente para /dev/tty falha. Em vez disso, retorne a sequência de escape no campo terminalSequence e Claude Code a emite para você através de seu próprio caminho de escrita de terminal. Isso é livre de corrida, funciona dentro de tmux e GNU screen, e funciona no Windows onde não há /dev/tty.

O campo aceita uma string de uma ou mais sequências de escape da allowlist:

  • OSC 0, 1, 2: títulos de janela e ícone
  • OSC 9: notificações iTerm2, ConEmu, Windows Terminal e WezTerm, incluindo progresso de barra de tarefas 9;4
  • OSC 99: notificações Kitty
  • OSC 777: notificações urxvt, Ghostty e Warp
  • BEL simples

Sequências podem ser terminadas com BEL ou com ST. Qualquer coisa fora da allowlist, incluindo sequências de cursor e cor CSI, sequências de paleta OSC, hiperlinks OSC 8, escritas de área de transferência OSC 52 e OSC 1337, é rejeitada e o campo é ignorado.

Claude Code escreve a sequência em si quando processa a saída do seu hook, portanto o campo funciona em eventos que descartam systemMessage e continue, como Notification e StopFailure. Tem dois limites:

  • Claude Code escreve a sequência apenas em uma sessão interativa, e apenas enquanto sua interface está na tela. Em modo não interativo com a flag -p e no Agent SDK, ignora o campo.
  • Um hook de comando WorktreeCreate não pode retornar JSON, porque Claude Code lê seu stdout como o caminho de worktree. Um hook HTTP WorktreeCreate retorna JSON e pode incluir o campo.

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

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

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

Adicionar contexto para Claude

O campo additionalContext passa uma string do seu hook para a janela de contexto do Claude. Claude Code envolve a string em um lembrete do sistema e a insere na conversa no ponto onde o hook disparou. Claude lê o lembrete na próxima requisição ao modelo, mas não aparece como uma mensagem de chat na interface.

Retorne additionalContext dentro de hookSpecificOutput ao lado do nome do evento:

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

Onde o lembrete aparece depende do evento:

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

Se sua string contiver uma tag <system-reminder> ou </system-reminder>, Claude recebe a string com o < dessa tag substituído por &lt;.

Se um valor exceder 10.000 caracteres, Claude Code escreve o texto em um arquivo no diretório de sessão e passa Claude o caminho do arquivo com uma visualização de até os primeiros 2.000 caracteres em vez disso. Claude pode ler o arquivo, mas Claude Code não pede a Claude para.

Use additionalContext para informações que Claude deve saber sobre o estado atual do seu ambiente ou a operação que acabou de executar:

  • Estado do ambiente: o branch atual, alvo de implantação ou sinalizadores de recurso ativos
  • Regras de projeto condicional: qual comando de teste se aplica ao arquivo que acabou de ser editado, quais diretórios são somente leitura neste worktree
  • Dados externos: problemas abertos atribuídos a você, resultados recentes de CI, conteúdo obtido de um serviço interno

Para instruções que nunca mudam, prefira CLAUDE.md. Ele carrega sem executar um script e é o lugar padrão para convenções de projeto estáticas.

Escreva o texto como declarações factuais em vez de instruções de sistema imperativas. Frases como "O alvo de implantação é produção" ou "Este repositório usa bun test" lê como informação de projeto. Texto enquadrado como comandos de sistema fora de banda pode disparar as defesas de injeção de prompt do Claude, o que faz com que Claude superficialize o texto para você em vez de tratá-lo como contexto.

Claude Code salva o texto injetado na transcrição de sessão. Para eventos de mid-sessão como PostToolUse ou UserPromptSubmit, quando você retoma com --continue ou --resume, Claude Code reproduz o texto salvo em vez de re-executar o hook para turnos anteriores, portanto valores como timestamps ou SHAs de commit ficam obsoletos. Hooks SessionStart executam novamente na retomada com source definido como "resume", ou "fork" se você adicionou --fork-session, para que possam atualizar seu contexto.

Controle de decisão

Nem todo evento suporta bloqueio ou controle de comportamento através de JSON. Os eventos que fazem cada um usam um conjunto diferente de campos para expressar essa decisão. Use esta tabela como referência rápida antes de escrever um hook:

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

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

Para casos de uso de redação ou transformação, intercepte em PreToolUse para entradas de ferramenta de saída e PostToolUse para resultados de ferramenta de entrada.

Aqui estão exemplos de cada padrão em ação:

[tab] Decisão de nível superior

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

[tab] PreToolUse

Usa hookSpecificOutput para controle mais rico: permitir, negar ou escalar para o usuário. Você também pode modificar a entrada da ferramenta antes de executar ou injetar contexto adicional para Claude. Consulte Controle de decisão PreToolUse para o conjunto completo de opções.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database writes are not allowed"
  }
}

[tab] PermissionRequest

Usa hookSpecificOutput para permitir ou negar uma solicitação de permissão em nome do usuário. Ao permitir, você também pode modificar a entrada da ferramenta ou aplicar regras de permissão para que o usuário não seja solicitado novamente. Consulte Controle de decisão PermissionRequest para o conjunto completo de opções.

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

Para exemplos estendidos incluindo validação de comando Bash, filtragem de prompt e scripts de aprovação automática, consulte O que você pode automatizar no guia e a implementação de referência do validador de comando Bash.

Eventos de hook

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

SessionStart

É executado quando o Claude Code inicia uma nova sessão ou retoma uma sessão existente. Útil para carregar contexto de desenvolvimento, como issues existentes ou alterações recentes na sua base de código, ou para configurar variáveis de ambiente. Para contexto estático que não requer um script, use o CLAUDE.md.

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

O valor do matcher corresponde a como a sessão foi iniciada:

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 do /fork, /branch ou uma conversa que você move para o segundo plano

Quando você inicia uma sessão interativa, retoma uma conversa na inicialização com --continue ou --resume, ou executa /clear, os hooks SessionStart são executados em segundo plano. Você pode digitar imediatamente, e uma conversa que você retomou aparece sem esperar pelos hooks. A primeira resposta do Claude ainda espera os hooks terminarem, para que o contexto deles chegue ao Claude.

Quando você troca de conversa com /resume dentro de uma sessão, a troca espera os hooks terminarem. Se você executar /clear ou trocar para outra conversa enquanto hooks em segundo plano ainda estiverem em execução, nada do que eles retornarem se aplica à sessão.

A mesma espera se aplica na inicialização, incluindo uma sessão retomada: um prompt que você envia enquanto os hooks SessionStart ainda estão em execução não chega ao Claude até que eles terminem.

Durante qualquer uma dessas esperas, pressione Esc para trazer o prompt de volta à entrada sem enviá-lo. Os hooks continuam em execução.

Entrada do SessionStart

Além dos campos de entrada comuns, os hooks SessionStart recebem source e, opcionalmente, model, agent_type e session_title:

Campo Descrição
source Como a sessão 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 por meio da recuperação de conversa, portanto verifique se o campo existe antes de lê-lo
agent_type O nome do agente, presente quando você inicia o Claude Code com claude --agent <name>
session_title O título personalizado da sessão, presente quando um é definido, por exemplo com --name, /rename, a saída sessionTitle de um hook ou o renameSession() do Agent SDK. Um hook que emite sessionTitle pode verificar 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 requerem 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

Um hook SessionStart pode adicionar contexto para o Claude, fornecer a primeira mensagem do usuário, definir o título da sessão, observar arquivos e recarregar skills. Retorne o campo correspondente a cada um, além dos campos de saída JSON disponíveis para todos os hooks:

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, no modo não interativo com a flag -p. Ela se torna o primeiro turno mesmo que você não passe nenhum prompt. Um prompt que você passe vem em seguida, como o próximo turno
sessionTitle Define o título da sessão, com o mesmo efeito de /rename. Aplica-se quando source é "startup", "resume" ou "fork"
watchPaths Array de caminhos absolutos a serem observados para eventos FileChanged durante esta sessão
reloadSkills Booleano. Quando true, o Claude Code verifica novamente os diretórios de skills e de comandos após a conclusão dos hooks SessionStart. Consulte Recarregar skills que um hook instala

Esta saída adiciona contexto e nomeia a sessão:

{
  "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"
  }
}

Um hook que apenas adiciona contexto pode imprimi-lo sem montar JSON, porque o Claude Code adiciona o stdout em texto simples de um hook SessionStart ao contexto do Claude.

Se o hook SessionStart do seu plugin fornece initialUserMessage ou sessionTitle, instale o plugin antes do início da sessão. O Claude Code ignora ambos os campos de um plugin cuja instalação termina depois que os hooks SessionStart foram executados.

Recarregar skills que um hook instala

Para disponibilizar na mesma sessão as skills que um hook SessionStart instala, retorne reloadSkills. A descoberta de skills normalmente é executada antes de os hooks SessionStart terminarem; portanto, sem isso, arquivos que um hook grava em ~/.claude/skills/ ou .claude/skills/ podem estar ausentes quando o primeiro prompt for executado.

Este exemplo sincroniza um repositório compartilhado de skills e solicita a nova verificação:

#!/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 marcador de exemplo. Substitua-a pelo seu próprio repositório de skills.

Persistir variáveis de ambiente

Os hooks SessionStart têm acesso à variável de ambiente CLAUDE_ENV_FILE, que fornece um caminho de arquivo onde você pode persistir variáveis de ambiente para os comandos de shell que o Claude executa mais adiante na sessão.

Para definir variáveis de ambiente individuais, escreva instruções export em CLAUDE_ENV_FILE. Use anexação (>>) para preservar variáveis definidas por outros hooks:

#!/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 dos 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

Cada comando Bash executa o conteúdo do arquivo como código de shell antes do próprio comando, portanto uma linha ali pode usar qualquer coisa que o Bash avalia, como a referência $PATH em export PATH="$PATH:./node_modules/.bin".

Variáveis persistidas em comandos PowerShell

Comandos PowerShell também recebem as variáveis de CLAUDE_ENV_FILE, no Claude Code v2.1.296 ou posterior, mas o PowerShell nunca executa o arquivo. Em vez disso, o Claude Code lê as atribuições do arquivo e as copia para o ambiente do comando PowerShell. Ele faz isso somente quando cada linha é de um dos tipos a seguir, considerando tudo o que cada hook nesta sessão gravou e qualquer script para o qual você definiu CLAUDE_ENV_FILE antes da inicialização:

  • Uma linha em branco ou um comentário #
  • Uma atribuição no início da linha, escrita como export NAME=value, declare -x NAME=value ou NAME=value, com um valor que o Bash usaria exatamente como escrito, composto por qualquer combinação destas partes: texto sem aspas que use apenas letras, dígitos e os caracteres _ @ % + = : , . / -, texto entre aspas simples e texto entre aspas duplas em que qualquer $, acento grave ou " interno esteja escapado com uma barra invertida

Se qualquer linha for de outro tipo, como export PATH="$PATH:./node_modules/.bin" com seu $PATH sem escape, um comando source ou as strings $'...' que direnv export bash imprime, os comandos PowerShell não recebem nenhuma das variáveis, e claude --debug registra Session environment is not all plain assignments. Os comandos Bash ainda recebem todas elas. No Windows, os comandos PowerShell também não recebem uma variável cujo valor contenha / ou \, porque o Git Bash e o Windows escrevem caminhos de forma diferente. Um comando PowerShell em sandbox não recebe nenhuma das variáveis.

[!NOTE]

CLAUDE_ENV_FILE está disponível para hooks SessionStart, Setup, CwdChanged e FileChanged. Outros eventos de hook não têm acesso a essa variável, nem um hook executado no PowerShell, seja por meio de "shell": "powershell" ou por padrão no Windows sem Git Bash.

Setup

É disparado somente quando você inicia o Claude Code com --init-only, ou com --init ou --maintenance no modo não interativo com a flag -p. Ele não é disparado na inicialização normal. Use-o para instalação única de dependências ou limpeza agendada que você aciona explicitamente a partir de CI ou scripts, separadamente da inicialização normal da sessão. Para inicialização por sessão, use o SessionStart.

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

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 stdin. Você pode omitir o prompt quando um hook SessionStart fornece initialUserMessage ou quando você retoma uma sessão com uma chamada de ferramenta adiada.

Em caso de sucesso, --init-only não imprime nada no terminal. Para confirmar que os hooks foram executados, inicie com claude --debug-file <path> --init-only, substituindo <path> por um local de arquivo de log, e verifique no log as entradas dos hooks Setup e SessionStart.

Como o Setup não é disparado em toda inicialização, um plugin que precisa de uma dependência instalada não pode depender apenas do Setup. O padrão prático é verificar a dependência no primeiro uso e instalá-la caso esteja ausente, por exemplo, um hook ou skill que testa ${CLAUDE_PLUGIN_DATA}/node_modules e executa npm install se não existir. Consulte o diretório de dados persistentes para saber onde armazenar as dependências instaladas. Se você distribui seu plugin por meio de um marketplace, talvez não precise desse padrão: o Claude Code instala automaticamente as dependências de pacotes Node.js elegíveis quando armazena o plugin em cache.

Entrada do Setup

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

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

Controle de decisão do Setup

Os hooks Setup não podem bloquear; a execução continua com qualquer código de saída. Em todos os códigos de saída, o Claude Code descarta os campos de saída JSON de um hook Setup, como systemMessage, continue e hookSpecificOutput.additionalContext. Com -p, o stdout, o stderr e o código de saída de um hook Setup aparecem na saída da execução somente como eventos hook_response quando você inicia com --output-format stream-json --verbose.

Os hooks Setup têm acesso a CLAUDE_ENV_FILE. Se o seu hook gravar variáveis nesse arquivo, elas persistem nos comandos Bash subsequentes da sessão, como nos hooks SessionStart. Somente hooks type: "command" são executados no Setup. Um hook type: "mcp_tool" no Setup é sempre ignorado, conforme descrito em Campos de hook de ferramenta MCP.

InstructionsLoaded

É disparado quando um arquivo CLAUDE.md ou .claude/rules/*.md é carregado no contexto. Esse evento é disparado no início da sessão para arquivos carregados antecipadamente e novamente mais tarde quando arquivos são carregados sob demanda, por exemplo, quando o Claude acessa um subdiretório que contém um CLAUDE.md aninhado ou quando regras condicionais com frontmatter paths: correspondem. O hook não suporta bloqueio nem controle de decisão. Ele é executado de forma assíncrona para fins de observabilidade.

Esse evento não é disparado quando o Claude lê o AGENTS.md diretamente por meio da configuração Project instructions. Ele é disparado quando um CLAUDE.md importa seu AGENTS.md, com load_reason definido como include como para qualquer outro arquivo importado, e quando o CLAUDE.md é um link simbólico para ele, como um carregamento normal de CLAUDE.md.

O matcher é aplicado a load_reason. Por exemplo, use "matcher": "session_start" para disparar somente para arquivos carregados no início da sessão, ou "matcher": "path_glob_match|nested_traversal" para disparar somente para carregamentos sob demanda.

Entrada do InstructionsLoaded

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

Campo Descrição
file_path Caminho absoluto para o arquivo de instruções que foi carregado
memory_type Escopo do arquivo: "User", "Project", "Local" ou "Managed"
load_reason Por que o arquivo foi carregado: "session_start", "nested_traversal", "path_glob_match", "include" ou "compact". O valor "compact" é disparado quando arquivos de instruções são recarregados após um evento de compactação
globs Padrões glob de caminho do frontmatter paths: do arquivo, se houver. Presente somente para carregamentos path_glob_match
trigger_file_path Caminho para o arquivo cujo acesso acionou esse carregamento, para carregamentos sob demanda
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 esse evento para logs de auditoria, rastreamento de conformidade ou observabilidade.

UserPromptSubmit

É executado quando um prompt é enviado, antes que o Claude o processe. Isso permite adicionar contexto extra com base no prompt/conversa, validar prompts ou bloquear certos tipos de prompts.

Os hooks UserPromptSubmit não são disparados apenas em prompts que você digita. O Claude Code também os executa quando:

Os hooks UserPromptSubmit têm um timeout padrão de 30 segundos para os tipos command, http e mcp_tool, menor que o padrão de 600 segundos para esses tipos na maioria dos outros eventos. Como esse hook é executado antes de cada prompt e bloqueia o processamento do modelo até terminar, um hook travado paralisa a sessão. Se o seu hook precisar de mais tempo, defina o campo timeout na entrada do hook.

Exceto por um hook de comando que você executa com async: true, um hook de comando, HTTP ou de ferramenta MCP em UserPromptSubmit que atinge seu timeout é cancelado, e sua saída, incluindo qualquer additionalContext, é descartada. O prompt ainda chega ao Claude sem esse contexto. Para bloquear o prompt em vez disso, defina onFailure: "block" em um hook de comando ou HTTP. A transcrição mostra um aviso nomeando o hook, o timeout que foi atingido e que a saída foi descartada.

Um hook de callback do Agent SDK em UserPromptSubmit que atinge seu timeout bloqueia o prompt com uma mensagem indicando o hook e o timeout, porque um callback ali pode estar atuando como um controle de política que não deve falhar de forma permissiva. A sessão continua.

Esc durante uma verificação de prompt

Se você pressionar Esc ou interromper a sessão enquanto os hooks ainda estão verificando um prompt, a verificação para e o prompt não chega ao Claude. O texto que você digitou no terminal retorna à entrada do prompt, e os hooks o verificam novamente quando você o reenviar.

Um hook pode bloquear o prompt no mesmo momento em que você interrompe. O prompt então permanece bloqueado: você vê a mensagem de bloqueio desse hook, e o texto não retorna à entrada.

Entrada do UserPromptSubmit

Além dos campos de entrada comuns, os hooks UserPromptSubmit recebem o campo prompt contendo o texto enviado. Conteúdo colado que foi recolhido em um marcador [Pasted text #N] chega expandido no lugar. Em sessões em que o Claude Code marca texto colado para o Claude, esse conteúdo expandido fica entre uma linha <pasted_content id="…"> e uma linha </pasted_content id="…">, portanto leve essas linhas em conta se o seu hook analisar o prompt.

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

{
  "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"
}

Quando o prompt é uma skill ou um comando personalizado que você digitou, como /deploy staging, o hook recebe essa linha como você a digitou, em vez do conteúdo expandido da skill. Se o hook bloquear o prompt, o comando não é executado, portanto os comandos de shell que a skill injeta com !`<command>` também não são executados. Para fazer a correspondência pelo nome ou pelos argumentos do comando, use UserPromptExpansion.

Controle de decisão do UserPromptSubmit

Os hooks UserPromptSubmit podem controlar se um prompt enviado é processado e adicionar contexto. Todos os campos de saída JSON estão disponíveis.

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

  • Stdout em texto simples: o Claude Code adiciona ao contexto do Claude o stdout que ele trata como texto simples
  • JSON com additionalContext: use o formato JSON abaixo para ter mais controle. O campo additionalContext é adicionado como contexto

Nenhum dos canais produz uma entrada visível na transcrição. O stdout simples e o valor de additionalContext são injetados, cada um, como um lembrete do sistema que começa com o nome do hook; o Claude lê ambos. Para confirmar a entrega, verifique o log de depuração.

Para bloquear um prompt, retorne um objeto JSON com decision definido como "block":

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.

suppressOriginalPrompt altera apenas a mensagem de bloqueio. O texto enviado ainda pode aparecer em arquivos locais, como a transcrição da sessão e seu histórico de prompts, portanto um hook de bloqueio não é uma maneira de manter um segredo fora do disco. Para limitar ou remover esses arquivos, consulte Armazenamento em texto simples e Limpar dados locais.

UserPromptExpansion

É executado quando um comando digitado pelo usuário se expande em um prompt antes de chegar ao Claude. Use-o para impedir a invocação direta de comandos específicos, injetar contexto para uma skill específica ou registrar quais comandos os usuários invocam. Por exemplo, um hook que corresponde a deploy pode bloquear /deploy a menos que um arquivo de aprovação esteja presente, ou um hook que corresponde a uma skill de revisão pode anexar a checklist de revisão da equipe como additionalContext.

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

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

Entrada do UserPromptExpansion

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

{
  "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 ser renderizado, o hook é executado uma vez com essas linhas, e o Claude Code renderiza o texto de substituição do hook no lugar delas. Uma mensagem longa produz várias chamadas; uma mensagem curta pode produzir apenas uma.

Use o MessageDisplay para:

  • remover markdown para uma exibição minimalista
  • transformar o texto que um aplicativo do Agent SDK mostra aos seus usuários
  • ocultar chaves de API ou nomes de host internos das respostas do Claude

O Claude Code retém cada lote até que seu hook retorne, portanto mantenha o hook rápido. Se o hook falhar ou atingir o timeout, o Claude Code exibe o texto original. O timeout padrão para esse evento é de 10 segundos; se o seu hook precisar de mais tempo, defina o campo timeout na entrada do hook.

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

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

Em execuções não interativas, incluindo consultas do Agent SDK e claude -p, o MessageDisplay é executado uma vez por mensagem do assistente, em vez de uma vez por lote de linhas. A chamada única chega após a conclusão da mensagem e carrega o texto completo da mensagem: index é 0, final é true e delta contém a mensagem inteira. Um hook que coleta o texto de delta de cada mensagem recebe o mesmo texto total em ambos os modos.

Entrada do MessageDisplay

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

Campo Descrição
turn_id UUID do turno atual
message_id UUID da mensagem do assistente sendo exibida. Estável em todos os lotes da mesma mensagem. Este não é o id msg_… da API, portanto não pode ser correlacionado com os ids de mensagem da transcrição
index Índice, 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, portanto 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 sobre o displayContent da saída JSON deles e descarta systemMessage e continue.

Este exemplo remove a formatação markdown das respostas do Claude para uma exibição em texto simples. O script lê cada lote do stdin, remove os marcadores de negrito e os acentos graves de código inline de delta e retorna o resultado como displayContent.

[tab] macOS/Linux

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("`"; ""))}}'

[tab] Windows (PowerShell)

Registre um hook de comando que executa o script por meio do PowerShell:

{
  "hooks": {
    "MessageDisplay": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.ps1"
            ]
          }
        ]
      }
    ]
  }
}

A flag -NoProfile evita o carregamento do seu perfil do PowerShell para que o hook inicie rapidamente, e -ExecutionPolicy Bypass permite que o PowerShell execute o arquivo de script local.

Salve este script em .claude/hooks/plain-display.ps1 no seu projeto:

$batch = [Console]::In.ReadToEnd() | ConvertFrom-Json
$text = $batch.delta -replace '\*\*', '' -replace '`', ''
@{
  hookSpecificOutput = @{
    hookEventName = "MessageDisplay"
    displayContent = $text
  }
} | ConvertTo-Json

Lotes sem markdown passam sem alteração. Se o script falhar, por exemplo porque o jq não está instalado, o Claude Code exibe o texto original e registra a falha apenas na saída de depuração, não na sessão.

PreToolUse

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

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

[!WARNING]

O PreToolUse é executado somente quando o Claude chama uma ferramenta. Por padrão, arquivos que você referencia com @ no seu prompt são adicionados sem nenhuma chamada de ferramenta. O Claude Code insere o conteúdo deles enquanto monta o prompt, portanto nenhum hook PreToolUse é disparado para eles, incluindo hooks que correspondem a Read. O PreToolUse também não é disparado para EndConversation.

Você pode bloquear caminhos específicos de referências @ ou desativar a inserção:

  • Bloquear caminhos específicos: adicione uma regra de negação de Read para eles
  • Desativar a inserção: defina CLAUDE_CODE_DISABLE_FILE_MENTIONS=1 no seu shell, nas configurações de usuário ou nas configurações gerenciadas. O Claude então recebe os caminhos mencionados com uma nota para lê-los usando suas ferramentas de arquivo. Se ele fizer isso, seus hooks PreToolUse correspondentes serão executados. A variável requer o Claude Code v2.1.299 ou posterior, e o Claude Code a ignora nas configurações de projeto e locais.

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

Um hook de callback do Agent SDK em PreToolUse que excede seu timeout bloqueia a chamada de ferramenta.

Entrada do PreToolUse

Além dos campos de entrada comuns, os hooks PreToolUse recebem tool_name, tool_input e tool_use_id.

Para uma ferramenta MCP, a entrada também carrega mcp_server, um objeto com o name do servidor e um source que indica de onde veio a definição do servidor. Os valores de source incluem plugin, sdk e escopos de configuração como user e project. McpServerProvenance na referência do Agent SDK lista todos eles e explica como tratar um que você não reconheça. Baseie decisões de confiança em source, e não em name ou no prefixo de nome de ferramenta mcp__<server>__. O campo mcp_server requer o Claude Code v2.1.274 ou posterior.

Para as ferramentas de arquivo Write, Edit e Read, tool_input.file_path é sempre absoluto:

  • O Claude Code expande ~ e caminhos relativos antes de os hooks serem executados, portanto um hook que faz a correspondência de caminhos não pode ser contornado por meio de ~ ou de uma grafia relativa do mesmo caminho
  • No Windows, o caminho chega com separadores de barra invertida, mesmo quando seu hook é executado no Git Bash, onde $PWD se parece com /c/project
  • Uma comparação escrita com barras normais, como uma verificação de /src/, nunca corresponde a um caminho com barras invertidas, e a chamada de ferramenta prossegue como se o hook não tivesse nada a bloquear
  • Normalize os separadores antes de comparar: FILE_PATH="${FILE_PATH//\\//}" no Bash, ou file_path.replace("\\", "/") no Python, e então faça a correspondência de um segmento de caminho como /src/ em vez de ancorar com ^, já que o caminho é absoluto

Uma chamada Write no Windows entrega:

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

Os campos de tool_input dependem da ferramenta:

Bash

Executa comandos de shell.

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

Quando um comando Bash altera arquivos em um repositório Git, o Claude Code pode registrar o que mudou. Ele registra as alterações em todos os modos de permissão quando a configuração bashEditDiffEnabled ativa o registro; a entrada dessa configuração indica quais arquivos podem defini-la. Caso contrário, ele as registra somente no modo auto e no modo bypassPermissions, e somente quando o Claude Code orienta o Claude a editar arquivos por meio do Bash. Defina bashEditDiffEnabled como false para desativar o registro. Comandos em segundo plano e comandos somente leitura não carregam diff.

Seu hook PostToolUse então recebe os arquivos alterados em tool_response.bashEditDiff. A lista abrange o que mudou no repositório enquanto o comando era executado. Arquivos que o Git ignora e arquivos em submódulos não são listados. Requer o Claude Code v2.1.269 ou posterior.

[!NOTE]

A lista é de melhor esforço e está em beta público. O Claude Code pode deixar de capturar uma alteração, incluir um arquivo que outro processo alterou ao mesmo tempo ou parar em seus limites de tamanho. O formato do campo pode mudar. Use a lista para encontrar o que revisar, não para impor uma política.

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

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 Quantidade 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 nenhum 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 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 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 comparar os arquivos
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 no conteúdo buscado
offset number 100000 Número opcional de caracteres a pular a partir do início da página. O Claude o define para continuar lendo uma página longa. Requer o Claude Code v2.1.290 ou posterior

Pesquisa na web.

Campo Tipo Exemplo Descrição
query string "react hooks best practices" Consulta de pesquisa
allowed_domains array ["docs.example.com"] Opcional: incluir apenas resultados destes domínios
blocked_domains array ["spam.example.com"] Opcional: excluir resultados destes domínios
mode string "extended" Opcional: tipo de pesquisa. "standard" solicita uma pesquisa mais leve e "extended", a pesquisa completa. Algumas sessões não enviam mode, e uma chamada sem ele executa a pesquisa completa. Exige o Claude Code v2.1.282 ou posterior
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. Para saber quais subagentes são executados de cada forma, consulte Executar subagentes em primeiro ou segundo plano
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 pelo 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 agrupadas; definido apenas quando o modelo foi trocado durante a execução
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 Quantidade de chamadas de ferramenta que o subagente fez
usage object {"input_tokens": 8320, ...} Detalhamento por tipo dos tokens 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 executado com a ferramenta SubagentHandback, que o Claude Code fornece no modo auto, entrega seu relatório por meio dessa ferramenta em vez de retorná-lo como texto. O campo content de seu resultado completed então carrega uma nota curta sobre essa entrega em vez do próprio relatório. Para ler o relatório, faça a correspondência de um hook PreToolUse ou PostToolUse com SubagentHandback e leia tool_input.message.

Para subagentes em segundo plano, a ferramenta retorna quando a tarefa passa para o segundo plano, portanto tool_response não carrega campos de uso: um lançamento em segundo plano retorna imediatamente, e uma tarefa em primeiro plano que o Claude Code move para o segundo plano no meio da execução retorna nessa transição. Ele tem status: "async_launched", agentId, description, prompt, outputFile e resolvedModel.

Em uma resposta completed, resolvedModel indica o modelo com o qual o subagente começou, que pode diferir do valor de model em tool_input, como quando availableModels ou outra substituição se aplica. Em uma resposta async_launched, resolvedModel indica o modelo em uso quando o agente passou para o segundo plano, então uma troca que aconteceu antes da passagem para o segundo plano se reflete ali.

AskUserQuestion

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

Campo Tipo Exemplo Descrição
questions array [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false}] Perguntas a serem apresentadas, 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 esse campo; forneça-o via updatedInput para responder programaticamente
ExitPlanMode

Apresenta um plano e pede ao usuário que o aprove antes de o Claude sair do modo de planejamento. O Claude grava o plano em um arquivo no disco antes de chamar a ferramenta, portanto o tool_input literal do modelo normalmente está vazio. O Claude Code injeta o conteúdo do plano e o caminho do arquivo antes de passar a entrada para os hooks.

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

Controle de decisão do PreToolUse

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

Campo Descrição
permissionDecision "allow" pula o prompt de permissão, exceto para ações que nenhum modo aprova automaticamente e leituras de caminhos de rede. Ele também não pula a aprovação de chamadas a ferramentas que um aplicativo do Agent SDK marca como always_ask, a menos que o modo de permissão aprove a chamada. AskUserQuestion e ExitPlanMode precisam de updatedInput combinado com "allow". Para o modo de planejamento, consulte Aprovações de hooks no modo de planejamento. "deny" impede a chamada de ferramenta. "ask" pede ao usuário que confirme. "defer" encerra o turno para que a ferramenta possa ser retomada depois e exige o modo não interativo com a flag -p. As regras de deny e ask continuam sendo avaliadas, independentemente do que o hook retorne
permissionDecisionReason Para "ask", mostrado ao usuário no prompt de permissão. Quando o Claude Code nega a chamada em uma execução -p em que ninguém pode responder a esse prompt, o Claude lê o motivo no resultado da ferramenta. Para "deny", mostrado ao Claude. Para "allow" e "defer", gravado apenas no log de depuração
updatedInput Modifica os parâmetros de entrada da ferramenta antes da execução. Substitui o objeto de entrada inteiro, portanto inclua os campos inalterados junto com os modificados. O Claude Code avalia as regras de permissão e a elegibilidade para segundo plano automático de um comando Bash em relação à entrada que o seu hook retorna, não à 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. Sem permissionDecision, o modo auto pode negar a chamada alterada
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 de onde o hook veio: [settings] para um hook de qualquer arquivo de configurações ou do frontmatter de um agente, [plugin:<name>] para o hook de um plugin, ou [skill] para um hook do frontmatter de uma skill. Isso ajuda os usuários a entender qual fonte de configuração está solicitando a confirmação.

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

[!NOTE]

O PreToolUse usava anteriormente os campos de nível superior decision e reason, mas eles estão descontinuados para esse evento. Use hookSpecificOutput.permissionDecision e hookSpecificOutput.permissionDecisionReason. Os valores descontinuados "approve" e "block" correspondem a "allow" e "deny", respectivamente. Outros eventos, como PostToolUse e Stop, continuam a usar decision e reason de nível superior como seu formato atual.

updatedInput do PreToolUse no modo auto

Quando o seu hook retorna updatedInput sem permissionDecision no modo auto, o Claude Code pode negar a chamada alterada. O servidor revisou a entrada que o Claude escreveu, não a entrada que o seu hook retornou. Em sessões interativas no terminal e sessões do VS Code com conexão direta à API da Anthropic, a negação inclui este texto:

a hook changed this call's input after auto mode reviewed it

Após uma negação com esse texto, o Claude pode emitir a chamada alterada novamente para que o servidor a revise. Se o Claude repetir exatamente a entrada alterada em sua próxima resposta, o seu hook ainda é executado:

  • Ele pode negar a chamada ou enviá-la para o prompt de permissão
  • Qualquer updatedInput que ele retornar é descartado, portanto o que é executado é o que o servidor revisou

Você não precisa alterar o seu hook por causa dessa negação, a menos que o Claude informe em sua resposta que o modo auto não conseguiu verificar a chamada.

Nas sessões que mostram esse texto, um hook command nas configurações ou no arquivo de hooks de um plugin pode alterar chamadas de Bash, PowerShell, Monitor, Edit, Write e NotebookEdit sem essa negação. A negação ainda pode aparecer, como quando algum hook alterou uma chamada anterior na mesma resposta do Claude ou quando um comando Bash pede para ser executado fora do sandbox.

Decisões ask do PreToolUse no modo auto

No modo auto, o "ask" de um hook PreToolUse normalmente envia a chamada de ferramenta para o prompt de permissão, e o classificador não decide sobre ela. Algumas chamadas seguem um caminho diferente:

  • Chamadas que as próprias verificações do Claude Code também sinalizam: incluem gravações em caminhos protegidos e comandos que pedem para ser executados fora do sandbox. O classificador pode negar uma dessas chamadas antes que qualquer prompt apareça. Se ele aprovar, a chamada normalmente ainda vai para o prompt.
  • Chamadas SubagentHandback: esta ferramenta entrega o relatório de um subagente. Nenhum prompt aparece para ela, e a chamada é executada mesmo que o classificador se oponha.

Para interromper uma chamada SubagentHandback, retorne "deny". O Claude Code então informa ao subagente, até três vezes, que seu relatório não foi entregue e que ele deve chamar a ferramenta novamente. Se seu hook continuar negando, o subagente termina sem entregar um relatório por meio dessa ferramenta, e a conversa que aguarda por ele recebe uma nota informando isso.

Ferramentas que exigem interação do usuário

AskUserQuestion e ExitPlanMode exigem interação do usuário. No modo não interativo com a flag -p, o Claude Code as oferece somente quando a execução tem um host de permissão para receber o prompt, como um callback canUseTool do Agent SDK.

Um hook PreToolUse satisfaz esse requisito quando faz o seguinte:

  1. Lê a entrada da ferramenta do stdin
  2. Coleta a resposta por meio da sua própria interface
  3. Retorna permissionDecision: "allow" junto com um updatedInput que contém a resposta, para que a ferramenta seja executada sem solicitar confirmação

Retornar apenas "allow" não é suficiente para essas ferramentas.

Para AskUserQuestion, devolva o array questions original e adicione um objeto answers que mapeia o texto de cada pergunta para a resposta escolhida. Esta saída responde a uma pergunta com React:

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

Uma ferramenta MCP que seu servidor marca com _meta["anthropic/requiresUserInteraction"] é mais restrita: um hook não pode ignorar seu prompt de aprovação com "allow", com ou sem updatedInput, porque o Claude Code não pode confirmar que o hook coletou a interação de que a ferramenta precisa.

Adiar uma chamada de ferramenta para mais tarde

"defer" é para integrações que executam claude -p como um subprocesso e leem sua saída JSON, como um aplicativo do Agent SDK ou uma UI personalizada construída sobre o Claude Code. Ele permite que esse processo chamador pause o Claude em uma chamada de ferramenta, colete entrada por meio de sua própria interface e retome de onde parou.

O Claude Code respeita "defer" apenas no modo não interativo com a flag -p, e apenas quando o Claude faz uma única chamada de ferramenta no turno:

  • Em sessões interativas: ele registra um aviso no log e ignora o resultado do hook
  • Várias chamadas de ferramenta de uma vez: "defer" é ignorado com um aviso e a ferramenta prossegue pelo fluxo normal de permissão

A ferramenta AskUserQuestion é o caso típico: o Claude quer perguntar algo ao usuário, mas não há terminal para responder. Uma execução -p oferece AskUserQuestion somente quando tem um host de permissão, como uma ferramenta MCP que você passa com --permission-prompt-tool, portanto inicie a execução com um. O ciclo funciona assim:

  1. O Claude chama AskUserQuestion. O hook PreToolUse é disparado.
  2. O hook retorna permissionDecision: "defer". A ferramenta não é executada. O turno termina com um resultado que carrega stop_reason: "tool_deferred", e a chamada de ferramenta pendente permanece na transcrição.
  3. O processo chamador lê deferred_tool_use do resultado do SDK, exibe a pergunta em sua própria interface e aguarda uma resposta.
  4. O processo chamador executa claude -p --resume <session-id> com o mesmo host de permissão. A mesma chamada de ferramenta dispara o PreToolUse novamente.
  5. O hook retorna permissionDecision: "allow" com a resposta em updatedInput. A ferramenta é executada e o Claude continua.

O campo deferred_tool_use contém o id, o name e o input da ferramenta:

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

Não há timeout nem limite de novas tentativas. A sessão permanece no disco até que a limpeza por retenção de cleanupPeriodDays a exclua, após 30 dias por padrão.

Se a resposta não estiver pronta quando você retomar, o hook pode retornar "defer" novamente. Se você enviou um prompt com essa retomada, o Claude o responde sem o resultado da ferramenta e não executa a chamada. Retome sem um prompt até que o hook retorne "allow" ou "deny".

[!NOTE]

Para retomar uma sessão adiada no modo de planejamento, passe --permission-prompt-tool junto com --resume para que o Claude Code possa apresentar o plano para aprovação. Para as demais condições, consulte Retomar no modo de planejamento com -p. Requer o Claude Code v2.1.246 ou posterior.

Quando você retoma com -p, o Claude Code não restaura nenhum outro modo de permissão armazenado. Ele inicia a execução no modo de permissão em que uma nova execução claude -p iniciaria, então passe --permission-mode ou --dangerously-skip-permissions novamente se a sessão adiada usava um deles.

Retomar quando uma ferramenta adiada não está disponível

Um resultado com stop_reason: "tool_deferred_unavailable" significa que a ferramenta de uma chamada adiada não está disponível na sessão retomada, por exemplo porque seu servidor MCP não está conectado. Seu hook PreToolUse não é disparado, e o campo deferred_tool_use do resultado nomeia a ferramenta.

Enquanto a chamada estiver pendente, qualquer prompt que você enviar não chega ao Claude e a execução termina com código 1. O campo result começa com <tool> isn't available, so its deferred call didn't run. Execute a chamada ou descarte-a para que ela não seja executada:

  • Para executar a chamada: torne a ferramenta disponível e retome novamente.
  • Para descartar a chamada: retome com um prompt e com CLAUDE_CODE_DROP_UNAVAILABLE_DEFERRED_TOOL definida como 1. O Claude recebe um erro como resultado da chamada e responde ao seu prompt. Quando você retomar depois, nenhuma chamada estará pendente.

Quando você descarta a chamada, a execução grava o resultado tool_deferred_unavailable com um campo result vazio e, em seguida, a resposta do Claude em um segundo resultado. Ela termina com código 0, a menos que essa resposta seja um erro. Se você definir a variável e não enviar nenhum prompt, a execução ainda descarta a chamada e termina com código 1.

Antes da v2.1.319, o Claude Code respondia a um prompt sem o resultado da ferramenta e descartava a chamada. Nessas versões, envie um novo prompt assim que a ferramenta estiver disponível.

PermissionRequest

É executado quando o Claude Code está prestes a pedir sua permissão para usar uma ferramenta. Em sessões que não podem mostrar um prompt, como subagentes em segundo plano no modo não interativo, o Claude Code ainda executa esses hooks e, se nenhum hook retornar uma decisão, nega a chamada de ferramenta. Para uma chamada que chega a um --permission-prompt-tool ou ao callback canUseTool do Agent SDK, os hooks são executados junto com o seu host, e vale a decisão de quem decidir primeiro. Use o controle de decisão do PermissionRequest para permitir ou negar em nome do usuário.

Use esse evento quando precisar de um sinal no momento em que o Claude pede permissão para usar uma ferramenta. O Claude Code executa um hook Notification com o tipo permission_prompt somente depois que o prompt fica aguardando por cerca de seis segundos.

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

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

Entrada do PermissionRequest

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

O array permission_suggestions não é uma lista exata das opções que você vê, porque cada diálogo de permissão monta as próprias opções. Alguns diálogos, como o de edições de arquivo, não leem o array e derivam suas opções da própria requisição. Um diálogo que o lê ainda pode omitir uma opção cuja sugestão permanece no array, por exemplo quando allowManagedPermissionRulesOnly oculta opções de salvar regras. Ele também pode oferecer opções que não têm entrada de sugestão, como Yes, and switch to auto mode, que altera o modo de permissão diretamente em vez de por meio de uma atualização de permissão.

Os hooks PreToolUse são executados antes de cada chamada de ferramenta, precise ela de permissão ou não. Os hooks PermissionRequest são executados apenas quando o Claude Code está prestes a pedir sua permissão, ou quando, de outra forma, negaria automaticamente uma chamada que não pode solicitar confirmação. Nenhum dos dois eventos é disparado para EndConversation.

{
  "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 requisições de permissão. Além dos campos de saída JSON disponíveis para todos os hooks, seu script de hook pode retornar um objeto decision com estes campos específicos do evento:

Campo Descrição
behavior "allow" concede a permissão, "deny" a nega. Regras deny e ask ainda são avaliadas, portanto 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 todo o objeto de entrada, então inclua os campos inalterados junto com os modificados. A entrada modificada é reavaliada em relação às regras deny e ask. AskUserQuestion e ExitPlanMode precisam deste campo
updatedPermissions Apenas para "allow": array de entradas de atualização de permissão a aplicar, como adicionar uma regra allow ou alterar o modo de permissão da sessão
message Apenas para "deny": informa ao Claude por que a permissão foi negada
interrupt Apenas para "deny": se true, interrompe o Claude

Para permitir ou reescrever um comando Monitor, consulte Corresponder comandos Monitor.

Um hook que sai com código 2 sem um objeto decision deixa o fluxo de permissão inalterado, e seu stderr é descartado. Para conceder ou negar a requisição, retorne o objeto decision.

{
  "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 para default
addDirectories directories, destination Adiciona diretórios de trabalho. directories é um array de strings de caminho
removeDirectories directories, destination Remove diretórios de trabalho

[!NOTE]

setMode com bypassPermissions só tem efeito se você iniciou a sessão com o modo bypass já disponível: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions ou permissions.defaultMode: "bypassPermissions" nas configurações de usuário, de --settings ou gerenciadas. Caso contrário, a atualização não tem efeito. A atualização também não tem efeito quando permissions.disableBypassPermissionsMode desativa o modo, ou quando a sessão é iniciada em modo restrito.

bypassPermissions nunca é persistido como defaultMode, independentemente do destination.

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

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

Um hook pode repetir uma das permission_suggestions que recebeu como sua própria saída updatedPermissions.

PostToolUse

É executado imediatamente após uma ferramenta ser concluída com sucesso.

Corresponde ao nome da ferramenta, com os mesmos valores do PreToolUse.

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

  • Para executar um hook depois que qualquer ferramenta for concluída com sucesso, omita o matcher ou defina-o como "*". Seu hook pode então descobrir por conta própria o que mudou, por exemplo executando git status --porcelain, que também lista arquivos não rastreados que git diff não mostra. Para chamadas de ferramenta que falham, adicione o mesmo hook em PostToolUseFailure.
  • Para executar um hook quando um arquivo específico muda no disco, independentemente de quem o gravou, use FileChanged. O Claude Code não executa um hook PostToolUse com matcher Edit|Write quando um comando Bash ou um processo fora do Claude Code reescreve o mesmo arquivo.

PostToolUse após uma interrupção

Se um hook PostToolUse parar antes do tempo depois que você envia uma mensagem ou interrompe durante uma chamada de ferramenta, verifique se você pressionou Ctrl+Enter, Enter ou Esc:

  • Ctrl+Enter: se pressioná-lo encerra o turno enquanto uma ferramenta está em execução e a ferramenta termina, seus hooks PostToolUse são executados sobre o resultado dela e têm 10 segundos para terminar. A tecla age como Esc se você enfileirou um comando ! antes das suas mensagens ou se as tarefas em segundo plano estão desativadas, por exemplo com CLAUDE_CODE_DISABLE_BACKGROUND_TASKS.
  • Enter: uma mensagem que você enfileira com Enter não interrompe o turno, então os hooks mantêm seu timeout habitual.
  • Esc: você cancela os hooks imediatamente, então envie a mensagem com Enter ou Ctrl+Enter quando os hooks precisarem ser executados.

Quando os 10 segundos acabam, o Claude Code encerra um hook de comando que ainda esteja em execução, seja qual for seu timeout. Para permitir que um hook de comando mais longo termine, execute-o com async: true, o que significa que o hook não pode bloquear nem reescrever o resultado.

Antes da v2.1.319, os hooks não eram iniciados depois que você interrompia o turno com uma mensagem, e um hook em execução era interrompido.

Entrada do PostToolUse

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

{
  "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 que ela seja enviada ao Claude. O valor deve corresponder ao formato de saída da ferramenta
updatedMCPToolOutput Substitui a saída apenas para ferramentas MCP. Prefira updatedToolOutput, que funciona para todas as ferramentas

O exemplo abaixo substitui a saída de uma chamada Bash. O valor de substituição corresponde ao formato de saída da ferramenta Bash:

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

[!WARNING]

updatedToolOutput altera apenas o que o Claude vê. A ferramenta já foi executada quando o hook é disparado, então quaisquer arquivos gravados, comandos executados ou requisições de rede enviadas já surtiram efeito. A telemetria, como spans de ferramenta do OpenTelemetry e eventos de analytics, também captura a saída original antes de o hook ser executado. Para impedir ou modificar uma chamada de ferramenta antes que ela seja executada, use um hook PreToolUse.

O valor de substituição deve corresponder ao formato de saída da ferramenta. As ferramentas integradas retornam objetos estruturados em vez de strings simples. Por exemplo, Bash retorna um objeto com os campos stdout, stderr, interrupted e isImage. Para ferramentas integradas, um valor que não corresponde ao esquema de saída da ferramenta é ignorado e a saída original é usada. A saída de ferramentas MCP é repassada sem validação de esquema. Remover detalhes de erro de que o Claude precisa pode fazer com que ele prossiga com base em uma suposição falsa.

Anotar um resultado para o classificador do modo auto

Retorne classifierContext para enviar uma nota curta sobre o resultado da chamada de ferramenta ao classificador do modo auto, e não ao Claude. O classificador nunca recebe os próprios resultados das ferramentas, então este campo é a forma suportada de informar algo sobre o que uma chamada retornou antes que ele revise as ações seguintes. 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 confere essa afirmação com suas próprias mensagens na conversa
  • Callbacks do Agent SDK em processo: quando uma aplicação que incorpora o Claude Code registra o hook como um callback do TypeScript SDK e retorna a nota durante a sessão ativa, o classificador pode considerar uma declaração do usuário transmitida na nota como intenção do usuário. Essa declaração pode satisfazer um requisito de consentimento que o classificador aceitaria de uma mensagem enviada por você, mas nunca suspende um bloqueio que sua própria mensagem também não conseguiria suspender. Depois que uma sessão é retomada, o Claude Code trata as notas restauradas como contexto não verificado. Quando hooks dos dois grupos anotam a mesma chamada, o classificador trata a nota combinada como não verificada

O Claude Code aplica estes limites ao entregar a nota:

  • Tamanho: o Claude Code limita as notas de uma chamada de ferramenta a 2.000 caracteres e trunca o restante. O limite é compartilhado entre todos os hooks que respondem a essa chamada
  • Somente respostas síncronas: o Claude Code ignora o campo na resposta de um hook que é executado em segundo plano, porque essa resposta chega depois que o Claude Code registra o resultado da ferramenta
  • Chamadas que o classificador não registra: a transcrição do classificador omite consultas somente leitura, como leituras de arquivos e buscas. O Claude Code descarta uma nota anexada a uma dessas chamadas
  • Interação com reescritas: quando a nota descreve uma saída que você está substituindo com updatedToolOutput, retorne os dois campos na mesma resposta do hook. O Claude Code descarta a nota se essa reescrita for rejeitada ou se a reescrita de outro hook a substituir. O Claude Code entrega uma nota que você retorna sem reescrita mesmo quando outro hook reescreve a saída

[!WARNING]

O classificador lê o conteúdo que você coloca em classifierContext como informação vinda da aplicação que hospeda a sessão, portanto não copie saída de ferramenta não confiável nem texto de terceiros para ele. Mantenha a nota como uma afirmação curta sobre esta única chamada, como um fato sobre sua origem ou uma declaração do usuário sobre ela; não use o campo para entregar mensagens não relacionadas ou um fluxo de eventos.

PostToolUseFailure

É executado quando uma ferramenta que começou a ser executada falha: a ferramenta lançou um erro, ou uma ferramenta MCP retornou um resultado de erro. Use-o para registrar falhas em log, enviar alertas ou fornecer feedback corretivo ao Claude.

Corresponde ao nome da ferramenta, com os mesmos valores do PreToolUse.

[!NOTE]

Este evento não é disparado para chamadas de ferramenta rejeitadas antes da execução: um nome de ferramenta desconhecido, uma entrada que falha na validação de esquema ou na validação específica da ferramenta, ou uma negação de permissão. As rejeições de validação são retornadas como resultados tool_use_error e acontecem antes da execução dos hooks, portanto não disparam nem PreToolUse nem PostToolUseFailure. As negações de permissão disparam PreToolUse, mas não este evento; consulte PermissionDenied.

Entrada do PostToolUseFailure

Os hooks PostToolUseFailure recebem os mesmos campos tool_name e tool_input que o PostToolUse, junto com informações de erro como campos de nível superior. Para uma ferramenta MCP, eles também recebem o objeto mcp_server. Por exemplo, um comando npm test que falhou pode entregar:

{
  "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 (abort), e não como um erro relatado pela ferramenta. Cancelar uma ferramenta em execução não dispara este hook; em vez disso, o resultado da ferramenta traz a mensagem de interrupção
duration_ms Opcional. Tempo de execução da ferramenta em milissegundos. Exclui o tempo gasto em prompts de permissão e em hooks PreToolUse

A string error geralmente é o mesmo texto que o Claude recebe como resultado da ferramenta que falhou. Seu formato varia conforme a ferramenta e a falha. Baseie seu hook em tool_name, is_interrupt e na primeira linha Exit code N; trate o restante da string como texto de exibição, e não como um formato estável.

  • Para Bash e PowerShell, um comando que foi executado e encerrado produz uma primeira linha Exit code N, seguida de qualquer saída que o comando produziu como um único bloco com stdout e stderr intercalados
  • Um payload também pode trazer uma mensagem de falha simples, sem a linha de código de saída, quando o Claude Code não conseguiu iniciar o próprio processo do shell
  • O Claude Code trunca strings longas no meio em torno de um marcador ... [N characters truncated] ... e pode inserir linhas próprias, como Command timed out after 2m 0s

Controle de decisão do PostToolUseFailure

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

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 de forma concorrente quando o Claude faz chamadas de ferramenta em paralelo. PostToolBatch é disparado exatamente uma vez com o lote completo, então é o lugar certo para injetar contexto que depende do conjunto de ferramentas executadas, e não de uma ferramenta isolada. Não há matcher para este evento.

Entrada do PostToolBatch

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

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

tool_response contém o mesmo conteúdo que o modelo recebe no bloco tool_result correspondente. O valor é uma string serializada ou um array de blocos de conteúdo, exatamente como a ferramenta o emitiu. Para Read, isso significa texto prefixado com números de linha, e não o conteúdo bruto do arquivo. As respostas podem ser grandes, então analise apenas os campos de que você precisa.

[!NOTE]

O formato de tool_response difere do de PostToolUse. PostToolUse repassa o objeto Output estruturado da ferramenta, como {filePath: "...", type: "create"} para Write; PostToolBatch repassa o conteúdo serializado de tool_result que o modelo vê.

Controle de decisão do PostToolBatch

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

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 na saída com código 2. Você a vê como um aviso na transcrição, e ela permanece na conversa, então o Claude a vê quando a conversa continua.

PermissionDenied

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

Corresponde ao nome da ferramenta, com os mesmos valores do PreToolUse.

Entrada do PermissionDenied

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

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

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

Notification

É executado quando o Claude Code envia notificações. Corresponde ao tipo de notificação. Omita o matcher para executar hooks para todos os tipos de notificação.

Você recebe esses eventos de hook mesmo com as notificações da área de trabalho desativadas: a configuração preferredNotifChannel, incluindo notifications_disabled, altera apenas como você é alertado, e não se o seu hook é executado.

Matcher Quando é disparado
permission_prompt O Claude precisa que você aprove um uso de ferramenta ou a requisição de rede de um comando em sandbox, e o prompt está aguardando há cerca de seis segundos
idle_prompt O Claude terminou de responder há cerca de 60 segundos e você não digitou nada desde então
auth_success A autenticação é concluída
auth_storage_failure Seu login é concluído, mas o Claude Code não consegue salvar as novas credenciais em seu armazenamento de credenciais. É disparado no lugar de auth_success
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 em 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 do terminal de um colega de equipe de agentes ou o aviso do modo auto sobre cobranças de 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 apenas enquanto a visualização de agentes está aberta em um terminal
quota_auto_resume_fired O Claude Code continua sua tarefa depois que um limite de uso do claude.ai a pausou: no momento da redefinição, ou antes, quando algo que você faz no Claude Code durante a espera, como adicionar créditos de uso, fazer upgrade do seu plano ou trocar de modelo, torna o uso disponível novamente, com a exceção da configuração de modelo
quota_auto_resume_stale Um limite de uso do claude.ai foi redefinido enquanto seu computador ficou em suspensão por mais de cerca de 30 minutos. O Claude Code aguarda 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 a 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 de distância 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
plugin_notification Um mod gera uma notificação com $.ui.notify. O title do hook é o nome do mod, a menos que o mod tenha definido um

Os tipos quota_auto_resume_fired, quota_auto_resume_stale e quota_auto_resume_disabled requerem Claude Code v2.1.234 ou posterior. O tipo auth_storage_failure requer v2.1.288 ou posterior.

Em sessões de terminal, permission_prompt para a requisição de rede de um comando em sandbox requer Claude Code v2.1.246 ou posterior.

agent_needs_input para a pergunta de configuração do terminal de um colega de equipe requer Claude Code v2.1.248 ou posterior.

[!NOTE]

Os tipos permission_prompt, idle_prompt, elicitation_dialog e elicitation_url_dialog compartilham o mesmo timing das notificações da área de trabalho, então, em sessões de terminal, você só os vê quando parece estar longe do terminal:

  • Espere permission_prompt depois que você ficar cerca de seis segundos sem digitar. O cronômetro começa quando o prompt de permissão aparece, e cada tecla pressionada o adia. Para executar um hook imediatamente quando o Claude pedir permissão para usar uma ferramenta, use PermissionRequest.
  • Espere idle_prompt cerca de 60 segundos depois que o Claude terminar de responder, e apenas se você não tiver digitado nada desde então e nenhum agente em segundo plano, como um subagente em segundo plano, ainda estiver em execução. O Claude Code não envia idle_prompt enquanto aguarda a redefinição de um limite de uso do claude.ai. Quando a espera termina por conta própria, um dos tipos quota_auto_resume_* é disparado em seu lugar.
  • Espere elicitation_dialog para um formulário de elicitação, ou elicitation_url_dialog para uma requisição de URL no navegador, depois que você ficar cerca de seis segundos sem digitar. Ambos compartilham o mesmo limite de seis segundos do permission_prompt: o cronômetro começa quando o diálogo aparece, e cada tecla pressionada o adia.

Uma requisição de permissão ou elicitação que chega enquanto outro diálogo está na tela mantém o mesmo limite de seis segundos, contado a partir do momento em que a requisição chega. Sua notificação pode chegar a você enquanto a requisição ainda aguarda atrás do diálogo aberto.

O Claude Code calcula o timing de permission_prompt de forma diferente em sessões em que envia requisições de permissão ao callback canUseTool do Agent SDK, que é como o Claude Desktop e a extensão do VS Code hospedam o Claude Code:

  • Espere permission_prompt cerca de seis segundos depois que o Claude pedir permissão. O Claude Code não o adia enquanto você digita.
  • Se você ou um hook PermissionRequest responderem antes, o Claude Code não executa permission_prompt.
  • Defina CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKS como 1 para desativar permission_prompt nessas sessões.

Antes da v2.1.233, permission_prompt não era disparado nessas sessões.

Use matchers separados para executar handlers diferentes dependendo do tipo de notificação. Esta configuração aciona um script de alerta específico de permissão quando o Claude precisa de aprovação de permissão e uma notificação diferente quando o Claude está ocioso:

{
  "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, que indica qual tipo foi disparado.

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

Os hooks Notification não podem bloquear nem modificar notificações. O Claude Code descarta seus campos systemMessage e continue, mas ainda emite terminalSequence, que é do que o exemplo de notificação da área de trabalho depende. Os hooks Notification são destinados a efeitos colaterais, como encaminhar a notificação para um serviço externo.

SubagentStart

É executado quando o Claude inicia um subagente com a ferramenta Agent, quando o Claude retoma um subagente e cada vez que um colega de uma equipe de agentes em processo lida com uma nova mensagem. Suporta matchers para filtrar pelo nome do tipo de agente. Para agentes integrados, é o nome do agente, como general-purpose, Explore ou Plan. Para subagentes personalizados, é o campo name do frontmatter do agente, e não o nome do arquivo.

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

Entrada do SubagentStart

Além dos campos de entrada comuns, os hooks SubagentStart recebem agent_id com o identificador único do subagente e agent_type com o nome do agente usado pelo filtro do matcher.

{
  "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 da conversa, antes do 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 apenas quando o contexto do subagente ainda não contém a cópia de uma execução anterior. A cópia injetada na inicialização permanece no lugar, mantendo intacto o cache de prompt do subagente. Depois que a compactação automática descarta essa cópia, o Claude Code injeta novamente o contexto da próxima execução.

SubagentStop

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

Entrada do SubagentStop

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

Nem todo evento SubagentStop vem de um subagente iniciado pelo Claude. O Claude Code também executa agentes internos para alguns de seus próprios recursos, como sugestões de prompt e perguntas paralelas com /btw, e o SubagentStop também é disparado quando um deles termina. Para esses eventos, agent_type é o nome do agente com que a própria sessão é executada, como um definido com --agent ou com a configuração agent, e uma string vazia quando a sessão é executada sem um.

Um matcher que nomeia tipos de agente não corresponde a um agent_type vazio. Um hook cujo matcher é omitido, "" ou "*", ou é uma expressão regular que corresponde a uma string vazia, também é executado para eventos com agent_type vazio.

No Claude Code v2.1.271 ou posterior, um subagente que é executado com a ferramenta SubagentHandback entrega seu relatório por meio dessa ferramenta antes de parar. O campo last_assistant_message então contém o texto final do subagente, se houver, que não é o relatório entregue. O relatório é a entrada message dessa chamada, que um hook PreToolUse ou PostToolUse com matcher em SubagentHandback recebe como tool_input.message.

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

{
  "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 sem erro que mantém o subagente em execução. Retornar decision: "block" com um reason mantém o subagente em execução e entrega reason ao subagente como sua próxima instrução. Um hook que bloqueia saindo com código 2 entrega sua mensagem de stderr da mesma forma. Para injetar contexto na sessão pai depois que um subagente retorna, use um hook PostToolUse na ferramenta Agent.

TaskCreated

É executado quando uma tarefa está sendo criada por meio da ferramenta TaskCreate. Use-o para impor convenções de nomenclatura, exigir descrições de tarefas ou impedir que certas tarefas sejam criadas. Em uma sessão sem as ferramentas Task, este evento não é disparado.

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

Entrada do TaskCreated

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

{
  "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
agent_id Neste evento, o campo de entrada comum identifica o subagente ou o colega de equipe em processo que está criando a tarefa. Pode estar ausente. Requer Claude Code v2.1.290 ou posterior

Controle de decisão do TaskCreated

Um hook TaskCreated pode bloquear a criação com o código de saída 2 ou com uma decisão JSON. Em ambos os casos, o Claude Code exclui a tarefa e retorna sua mensagem ao Claude como o erro da ferramenta. O Claude Code ignora continue: false deste evento e o Claude continua trabalhando.

  • Código de saída 2: o Claude Code retorna o texto do stderr como a mensagem.
  • JSON {"decision": "block", "reason": "..."}: o Claude Code retorna reason como a mensagem.

Este exemplo bloqueia tarefas cujos assuntos não seguem o formato exigido:

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

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

exit 0

TaskCompleted

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

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

Entrada do TaskCompleted

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

{
  "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
agent_id Neste evento, o campo de entrada comum identifica o subagente ou o colega de equipe em processo que está concluindo a tarefa. Pode estar ausente. Requer Claude Code v2.1.290 ou posterior

Controle de decisão do TaskCompleted

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

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

Este exemplo executa testes e bloqueia a conclusão da tarefa se eles falharem:

#!/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 seu lugar.

[!TIP]

O comando /goal é um atalho integrado para um hook Stop baseado em prompt com escopo de sessão. Use-o quando quiser que o Claude continue trabalhando em direção a uma condição sem escrever configuração de hook.

Entrada do Stop

Além dos campos de entrada comuns, os hooks Stop recebem stop_hook_active, last_assistant_message, background_tasks e session_crons. O campo stop_hook_active é true quando o Claude Code já está continuando como resultado de um hook Stop. Verifique esse valor ou processe a transcrição para evitar bloquear em uma condição que nunca será resolvida.

O Claude Code aplica um limite de 8 continuações consecutivas: depois que os hooks Stop continuaram o turno oito vezes seguidas, o Claude Code sobrescreve o próximo bloqueio e encerra o turno. A contagem de continuações consecutivas é redefinida cada vez que o Claude chama uma ferramenta. Para aumentar o limite, defina CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.

O campo last_assistant_message contém o conteúdo de texto da resposta final do Claude, para que os hooks possam acessá-lo sem analisar o arquivo de transcrição. Para hooks que atuam sobre o turno recém-concluído, como hooks de leitura em voz alta ou de notificação, use este campo em vez de ler transcript_path: não há garantia de que o arquivo de transcrição inclua a mensagem final no momento do Stop em todas as versões.

Os arrays background_tasks e session_crons permitem que os hooks diferenciem "a sessão terminou" de "a sessão está pausada aguardando que um trabalho em segundo plano a desperte novamente". Ambos os arrays estão presentes quando o registro de tarefas está acessível e ficam vazios quando nada está em andamento ou agendado.

Cada entrada em background_tasks descreve uma tarefa em andamento e usa estes campos:

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. Recorre ao discriminante bruto para tipos não reconhecidos
status Status atual da tarefa
description Descrição em texto livre, limitada a 1000 caracteres com um marcador … [+N chars] na própria string quando cortada
command Linha de comando do shell, limitada a 1000 caracteres. Presente apenas para tarefas shell
agent_type Nome do tipo de subagente. Presente apenas para tarefas subagent
server Nome do servidor MCP. Presente apenas para tarefas monitor e MCP task
tool Nome da ferramenta MCP. Presente apenas para tarefas monitor e MCP task
name Nome do workflow. Presente apenas para tarefas workflow

Cada entrada em session_crons descreve um despertar agendado com escopo de sessão, proveniente 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 são disparadas 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 do 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 sem 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, e não como um erro de hook

Um hook que bloqueia saindo com código 2 é encaminhado da mesma forma que reason: o Claude recebe a mensagem do stderr como a explicação de por que deve continuar.

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

Use additionalContext quando o hook estiver funcionando como projetado e dando orientação ao Claude, como "execute a suíte de testes antes de terminar". Ele mantém a conversa em andamento com as mesmas proteções contra loop de decision: "block", a saber, a entrada stop_hook_active e o limite de 8 continuações consecutivas, mas a transcrição o rotula como Stop hook feedback e nenhuma notificação de erro de hook é exibida:

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

StopFailure

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

Entrada do StopFailure

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

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 depois de terminar seu turno. Use-o para impor critérios de qualidade antes que um colega de equipe pare de trabalhar, como exigir que as verificações de lint passem ou verificar se os arquivos de saída existem.

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

Entrada do TeammateIdle

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

{
  "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
agent_id Neste evento, o campo de entrada comum identifica o colega de equipe em processo que está prestes a ficar ocioso. Pode estar ausente. Requer Claude Code v2.1.290 ou posterior

Controle de decisão do TeammateIdle

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

  • Código de saída 2: o colega de equipe recebe a mensagem do stderr como feedback e continua trabalhando em vez de ficar ocioso.
  • JSON {"continue": false, "stopReason": "..."}: interrompe o colega de equipe por completo, correspondendo ao comportamento do hook Stop. O stopReason é mostrado ao usuário.

Este exemplo verifica se um artefato de build existe antes de permitir que um colega de equipe fique ocioso:

#!/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 apenas 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 do lado do Windows alterado na 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 uma decision 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"
}

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

O Claude Code age com base na decisão de bloqueio da saída JSON de um hook ConfigChange e descarta systemMessage e continue. Uma alteração bloqueada não exibe nenhuma mensagem para você nem para o Claude, quer você bloqueie com reason ou com stderr na saída com código 2. O Claude Code apenas grava uma linha no log de depuração.

CwdChanged

É executado quando um comando de shell na conversa principal altera o diretório de trabalho, por exemplo quando o Claude executa um comando cd. Use-o para reagir a mudanças de diretório: recarregar variáveis de ambiente, ativar toolchains específicas do projeto ou executar scripts de configuração automaticamente. Combina com FileChanged para ferramentas como o direnv, que gerenciam o ambiente por diretório.

Hooks CwdChanged têm acesso a CLAUDE_ENV_FILE. Se seu hook gravar variáveis nesse arquivo, elas persistem nos comandos Bash subsequentes até o próximo evento CwdChanged, quando o Claude Code as limpa.

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

Entrada do CwdChanged

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

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

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

Os hooks CwdChanged não têm controle de decisão. Eles não podem bloquear a mudança de diretório.

O Claude Code lê watchPaths e systemMessage da saída JSON deles e descarta continue. Em sessões interativas, ele mostra o systemMessage como uma breve notificação no terminal. A mensagem não chega ao fluxo de mensagens do SDK.

DirectoryAdded

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

O Claude Code não dispara este evento quando:

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

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

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

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

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 trata o restante de forma diferente conforme a origem:

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

FileChanged

É executado quando um arquivo monitorado muda no disco. O Claude Code detecta alterações com um monitor do sistema de arquivos, não inspecionando chamadas de ferramenta, então ele executa o hook independentemente do que alterou o arquivo: uma chamada de ferramenta Edit ou Write, um script que o Claude executa com Bash ou um processo totalmente fora do Claude Code. Um uso comum é recarregar variáveis de ambiente quando arquivos de configuração do projeto mudam.

O matcher deste evento tem duas funções:

  • Montar a lista de monitoramento: o valor é dividido em | e cada segmento é registrado como um nome de arquivo literal no diretório de trabalho, então ".envrc|.env" monitora exatamente esses dois arquivos. Padrões regex não são úteis aqui: um valor como ^\.env monitoraria um arquivo chamado literalmente ^\.env.
  • Filtrar quais hooks são executados: quando um arquivo monitorado muda, o mesmo valor filtra quais grupos de hooks são executados usando as regras de matcher padrão em relação ao nome base do arquivo alterado.

Este exemplo normaliza as quebras de linha em data.csv após qualquer alteração, incluindo um comando Bash ou um script externo reescrevendo o arquivo:

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

O hook lê o caminho absoluto do arquivo alterado no campo file_path da entrada JSON em stdin. Sua verificação com grep testa a mesma coisa que o perl remove, um CR no final de uma linha, então a execução após uma normalização termina sem tocar no arquivo. Uma verificação mais permissiva entra em loop infinito, porque perl -i reescreve o arquivo mesmo quando não substitui nada e o Claude Code executa o hook novamente após cada reescrita. Salve este script em /path/to/normalize-line-endings.sh e torne-o executável:

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

Para confirmar que o hook funciona, peça ao Claude para acrescentar uma linha CRLF a data.csv com um comando Bash. O Claude Code executa o hook e o arquivo termina com quebras de linha LF.

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

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

Entrada do FileChanged

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

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 o seu script de hook descobrir arquivos adicionais a serem monitorados 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 a systemMessage como uma breve notificação no terminal. A mensagem não chega ao fluxo de mensagens do SDK.

WorktreeCreate

É executado quando o Claude Code cria um worktree em casos como estes:

Um hook WorktreeCreate não é executado para um worktree que o Claude adiciona via Bash, e o hook de um plugin não é executado para aquele criado quando você inicia com --worktree e --tmux:

  • git worktree add via Bash: para inspecionar ou bloquear esse comando, adicione um hook PreToolUse com o matcher Bash.
  • --worktree com --tmux: o Claude Code cria o worktree antes de carregar os plugins, então um hook WorktreeCreate de um plugin não é executado. Para que seu hook seja executado com ambas as flags, defina-o em um arquivo de configurações. Antes da v2.1.300, o hook WorktreeCreate de um plugin também não era executado apenas com --worktree.

Por padrão, o Claude Code cria a cópia de trabalho isolada com git worktree. Configurar um hook WorktreeCreate substitui esse comportamento padrão do git, permitindo que você use um sistema de controle de versão diferente, como SVN, Perforce ou Mercurial.

Como o hook substitui totalmente o comportamento padrão, .worktreeinclude não é processado. Se você precisar copiar arquivos de configuração locais como .env para o novo worktree, faça isso dentro do seu script de hook.

O hook deve retornar o caminho para o diretório do worktree criado. O Claude Code usa esse caminho como diretório de trabalho para a sessão isolada. Consulte Saída do WorktreeCreate para saber como cada tipo de hook retorna o caminho.

O Claude Code age com base no sucesso do hook e no caminho retornado, e descarta systemMessage e continue.

Este exemplo cria uma cópia de trabalho SVN e imprime o caminho para o Claude Code usar. Substitua a URL do repositório pela sua:

{
  "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 em stdin, faz checkout de uma cópia nova em um novo diretório e imprime o caminho do diretório. O echo na última linha é o que o Claude Code lê como caminho do worktree. Redirecione qualquer outra saída para stderr para que ela não interfira no caminho.

Entrada do WorktreeCreate

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

{
  "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 permitir/bloquear. Em vez disso, o sucesso ou a falha do hook determina o resultado. O hook deve retornar o caminho para o diretório do worktree criado:

  • Hooks de comando (type: "command"): imprima o caminho como a última linha não vazia de stdout. O Claude Code remove códigos de escape ANSI antes de ler essa linha, então banners de inicialização do shell impressos antes do seu echo são ignorados. Redirecione qualquer outra saída do hook para stderr.
  • Hooks HTTP (type: "http"): retorne { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } } no corpo da resposta.

Se o hook falhar ou não produzir nenhum caminho, a criação do worktree falha com um erro. O Claude Code resolve um caminho relativo em relação ao diretório em que o hook foi executado, reduzindo quaisquer segmentos . ou .. nele. Se o caminho resultante não for um diretório em que o Claude Code possa entrar, a sessão imprime um erro indicando o caminho e sai com código 1.

O Claude Code recusa um caminho absoluto que contenha segmentos . ou .., e qualquer caminho que passe por um link simbólico abaixo da raiz do repositório. O erro indica o componente rejeitado. Um link simbólico incluído em um commit no repositório poderia redirecionar o worktree para fora dele. Retorne um caminho normalizado que não passe por um link simbólico dentro do repositório.

WorktreeRemove

É executado quando o Claude Code limpa um worktree que o seu hook WorktreeCreate criou. O evento é disparado quando:

  • Você sai de uma sessão de worktree interativa e escolhe remover o worktree quando o Claude Code solicita
  • Você sai de uma sessão de worktree interativa que não nomeou, o Claude Code não encontra arquivos alterados ou não rastreados e remove o worktree sem solicitar confirmação
  • Você exclui uma sessão em segundo plano que é executada no worktree
  • Você pede ao Claude para sair e remover o worktree durante uma sessão, e o Claude o remove com a ferramenta ExitWorktree
  • Uma sessão que claude remote-control iniciou no modo worktree termina sem falhar, e o Claude Code não encontra arquivos alterados ou não rastreados nem novos commits em seu worktree
  • Você interrompe claude remote-control enquanto sessões que ele iniciou no modo worktree ainda estão em execução, e o Claude Code não encontra arquivos alterados ou não rastreados nem novos commits em seus worktrees

Quando um subagente ou um agente de fluxo de trabalho termina, o Claude Code mantém o worktree que o seu hook WorktreeCreate criou para ele e não executa o seu hook WorktreeRemove. Remova esses worktrees você mesmo quando o trabalho estiver concluído.

O Claude Code não solicita confirmação antes de o seu hook ser executado para uma chamada ExitWorktree ou uma limpeza do claude remote-control. ExitWorktree se recusa a remover um worktree criado por hook, a menos que a chamada passe discard_changes: true, o que torna o seu hook a última verificação nesse caminho.

O Claude Code usa o git para procurar arquivos alterados ou não rastreados, então não encontra nenhum em um worktree que não seja um checkout git nem esteja dentro de um, mesmo quando o diretório contém trabalho sem commit. Verifique esse trabalho no seu hook WorktreeRemove antes que ele exclua qualquer coisa.

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

  • Sem hook WorktreeRemove: o Claude Code recorre ao git para uma remoção ao sair ou por meio de ExitWorktree, e mantém o worktree quando o claude remote-control faz a limpeza:
    • Você sai de uma sessão de worktree, ou o Claude chama ExitWorktree: o Claude Code remove o caminho que o seu hook WorktreeCreate retornou como git worktree remove --force faria, então um worktree que o git reconhece é removido. Um worktree que o git não reconhece, por exemplo um que o seu hook criou com um sistema de controle de versão que não seja git, permanece no disco.
    • claude remote-control limpa o worktree de uma sessão: o worktree permanece no disco, e o terminal que executa claude remote-control mostra worktree removal failed, kept: <path>.
    • Você exclui uma sessão em segundo plano: consulte O que a exclusão de uma sessão remove.
  • O hook termina com 0: o worktree conta como removido. O Claude Code não lê mais nada do hook, então certifique-se de que o seu hook excluiu o diretório.
  • O hook termina com código diferente de zero: a remoção falha se o diretório em worktree_path ainda existir depois, e o worktree permanece no disco sem fallback para o git. Um hook que excluiu o diretório antes de terminar com código diferente de zero conta como removido. Para saber como a falha é relatada, consulte Entrada do WorktreeRemove.

O Claude Code nunca exclui um branch pertencente a um worktree criado por hook, porque ele só conhece o caminho que seu hook WorktreeCreate retornou. Se seu hook WorktreeCreate criar um branch, exclua-o em seu hook WorktreeRemove. O Claude Code descarta os campos de saída JSON de um hook WorktreeRemove, como systemMessage e continue.

Para a exclusão de uma sessão em segundo plano, o Claude Code verifica o caminho do worktree armazenado antes de executar o hook, recusando um caminho que seja um link simbólico ou passe por um abaixo da raiz do repositório. Nesse caminho, o hook é executado para um worktree que ainda contém arquivos somente quando você confirma a exclusão na visualização de agentes. Para esse worktree, claude rm mantém a sessão e o worktree.

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

{
  "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 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 termina com código diferente de zero e o diretório em worktree_path ainda existe depois, a remoção falha:

  • O worktree permanece no disco, e o comando e o stderr do hook vão para o log de depuração.
  • Se você estava excluindo uma sessão em segundo plano, a sessão também permanece. A mensagem de recusa na visualização de agentes informa como o hook terminou, como exited 1, cita o início do seu stderr e diz se excluir a sessão novamente remove o diretório mesmo assim.
  • Se o claude remote-control estava limpando o worktree de uma sessão, o terminal que o executa mostra worktree removal failed, kept: <path>.

PreCompact

É executado antes de o Claude Code executar uma operação de compactação.

O valor do matcher indica se a compactação foi acionada manual ou automaticamente:

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

Termine com código 2 para bloquear a compactação. Para um /compact manual, a mensagem de stderr é mostrada ao usuário. Você também pode bloquear retornando JSON com "decision": "block".

Bloquear a compactação automática tem efeitos diferentes dependendo de quando ela é disparada. Se a compactação foi acionada proativamente antes do limite de contexto, o Claude Code a ignora e a conversa continua sem compactação. Se a compactação foi acionada para se recuperar de um erro de limite de contexto já retornado pela API, o erro subjacente aparece e a requisição atual falha.

O Claude Code descarta os campos systemMessage e continue de um hook PreCompact.

Entrada do PreCompact

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

{
  "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
}

Saída do PreCompact

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

PostCompact

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

Os mesmos valores de matcher se aplicam como no PreCompact:

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, e após a compactação por inatividade

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

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

  • /model <name> e o seletor do /model
  • O seletor de modelo Option+P ou Alt+P
  • A configuração Model em /config
  • Ativar o modo rápido quando isso altera o modelo da sessão
  • Uma requisição set_model, ou uma alteração de modelo em uma requisição apply_flag_settings, de um host do Agent SDK ou do Remote Control

O Claude Code não executa hooks PreModelSwitch para trocas que ele faz por conta própria, como um fallback automático de modelo ou a restauração do modelo quando você retoma uma sessão. Essas alterações chegam apenas ao PostModelSwitch.

O Claude Code compara o matcher com o nome canônico do modelo para o qual a sessão está mudando, ignorando qualquer sufixo [1m]. Um alias como opus, um ID de modelo com data e um ID específico de provedor, como um ID de modelo do Amazon Bedrock, correspondem todos ao único nome canônico para o qual são resolvidos, então claude-opus-5 cobre todas as grafias do Opus 5.

Quando o Claude Code não consegue determinar um nome canônico para o destino, por exemplo um ID de modelo personalizado que só o seu gateway de LLM conhece, ele executa todos os hooks PreModelSwitch independentemente do matcher. Um hook que bloqueia deve, portanto, verificar to_model na sua entrada em vez de confiar apenas no matcher.

Escreva o matcher como um nome exato, uma lista separada por | como claude-opus-4-6|claude-opus-5, ou uma expressão regular como .*opus.*. Este exemplo usa um matcher de nome exato e também verifica to_model na entrada do hook, então recusa uma troca para o Opus 4.6 terminando com código 2 e deixa passar qualquer outro destino:

[tab] macOS/Linux

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"
          }
        ]
      }
    ]
  }
}

[tab] Windows (PowerShell)

Registre um hook de comando que executa um script por meio do PowerShell:

{
  "hooks": {
    "PreModelSwitch": [
      {
        "matcher": "claude-opus-4-6",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-opus-46.ps1"
            ]
          }
        ]
      }
    ]
  }
}

Salve este script em .claude/hooks/block-opus-46.ps1 no seu projeto:

$hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
if ($hookInput.to_model -match 'opus-4-6') {
  [Console]::Error.WriteLine('Opus 4.6 is retired for this project. Use a newer model.')
  exit 2
}
exit 0

Para confirmar que o hook funciona, execute /model claude-opus-4-6 a partir de uma sessão que esteja executando um modelo diferente. O Claude Code mantém o modelo atual e informa que um hook PreModelSwitch bloqueou a troca, com a sua mensagem como motivo.

Entrada do PreModelSwitch

Além dos campos de entrada comuns, os hooks PreModelSwitch recebem os campos desta tabela. Os últimos cinco descrevem quanto custa reenviar a conversa para o novo modelo, para que um hook possa mostrar esse valor antes que a troca aconteça.

Campo Tipo Descrição
from_model string ID do modelo do qual a troca parte
to_model string ID do modelo para o qual a troca vai. O matcher compara com o nome canônico deste modelo
requested_model string ou null O modelo que a solicitação nomeou: um alias como opus, um ID de modelo completo ou null quando a solicitação era 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 alteração de modelo em uma requisição apply_flag_settings, de um host do Agent SDK ou do Remote Control
context_tokens number Tokens que a próxima requisição reenvia como seu prompt: os tokens de entrada, de leitura de cache, de criação de cache e de saída da última resposta na conversa principal, somados. 0 antes da primeira resposta
prompt_cache_warm boolean Se o cache de prompt do modelo atual provavelmente ainda está aquecido, o que significa que a troca o perde
cache_ttl string Tempo de vida do cache de prompt da conversa principal no momento desta troca: "5m" ou "1h"
estimated_cache_write_usd number Custo estimado em dólares americanos de gravar context_tokens no cache de prompt em to_model na taxa de cache_ttl, excluindo a próxima resposta. O servidor pode não precisar armazenar em cache novamente todo o contexto, então trate-o como uma estimativa
pricing string Como o Claude Code precificou estimated_cache_write_usd: "configured" nas taxas da sua organização quando ela as configurou, "catalog" no preço de tabela ou "default" quando to_model não tem preço conhecido e o Claude Code assumiu uma taxa padrão

Este exemplo mostra a entrada para /model opus em uma sessão que executa 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" de nível superior cancela a troca.

Para um controle mais refinado, retorne permissionDecision e permissionDecisionReason em um objeto hookSpecificOutput, como no PreToolUse. O PreModelSwitch aceita "allow", "deny" e "ask". Ele não aceita "defer", updatedInput nem additionalContext. A tabela abaixo descreve ambos os campos:

Campo Descrição
permissionDecision "allow" prossegue e ignora 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 o erro de uma requisição set_model. Para "ask", mostrado no prompt de confirmação. Ignorado para "allow"

Somente o /model em uma sessão interativa pode mostrar o prompt de "ask". Em todas as outras superfícies, incluindo o modo não interativo com a flag -p, /config e requisições set_model, o Claude Code trata "ask" como uma recusa.

Este exemplo pede ao usuário que confirme e cita a contagem de tokens de context_tokens:

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

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

O Claude Code mostra ao usuário qualquer systemMessage que o seu hook retorne, independentemente da decisão, então um hook de relatório de custos pode retornar {"systemMessage": "..."} e terminar com 0.

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

Um hook PreModelSwitch que não responde antes do seu timeout bloqueia a troca. O timeout padrão para este evento é de 30 segundos.

Um código de saída diferente de 0 ou 2 sem saída JSON válida é um erro não bloqueante, então a troca prossegue, exceto para hooks cuja falha a bloqueia.

PostModelSwitch

É executado depois que o modelo da sessão muda. Use-o para dar ao Claude orientações específicas do modelo sem editar cada CLAUDE.md, por exemplo uma instrução para toda a organização que se aplica a determinados modelos.

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

  • Uma troca que você ou um cliente solicitou
  • Um fallback automático de modelo, que altera o modelo da sessão
  • Uma configuração como opusplan entrando ou saindo do modo de planejamento
  • O Claude Code restaurando o modelo quando você retoma uma sessão

O Claude Code não executa hooks PostModelSwitch quando um modelo de uma cadeia de modelos de fallback atende um turno, porque essa substituição dura um turno e deixa o modelo da sessão inalterado.

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

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

{
  "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, mude para um modelo Opus a partir de uma sessão que esteja executando um modelo diferente, por exemplo execute /model opus em uma sessão do Sonnet, e depois pergunte ao Claude quais orientações ele tem sobre o modelo atual.

Entrada do PostModelSwitch

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

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

Controle de decisão do PostModelSwitch

O Claude Code pega o stdout em texto simples do seu hook ao terminar com 0, ou additionalContext da saída JSON, e o entrega ao Claude com a próxima requisição após a troca. Além dos campos de saída JSON disponíveis para todos os hooks, você pode retornar:

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 após você enviar o próximo prompt, o Claude Code envia essa requisição sem a saída e a anexa à requisição seguinte. Se o modelo mudar várias vezes antes da próxima requisição, o Claude Code entrega apenas a saída correspondente ao modelo de destino da última troca.

SessionEnd

É executado quando uma sessão do Claude Code termina. Útil para tarefas de limpeza, registro de estatísticas da sessão ou salvamento do estado da sessão. Suporta matchers para filtrar pelo motivo de saída.

O campo reason na entrada do hook indica por que a sessão terminou:

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 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 os campos de saída JSON deles, como systemMessage.

Os hooks SessionEnd têm um timeout padrão de 1,5 segundo. Ele se aplica quando você sai, executa /clear ou troca de sessão com o /resume interativo. Você pode dar mais tempo a um hook de duas maneiras:

  • timeout por hook: defina timeout na configuração desse hook. O orçamento geral aumenta automaticamente para corresponder ao maior timeout por hook nos seus arquivos de configurações, até 60 segundos. Se você aumentar o orçamento dessa forma, um hook sem seu próprio timeout ainda mantém o padrão. Timeouts definidos em hooks fornecidos por plugins não aumentam o orçamento.
  • CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: defina esta variável de ambiente em milissegundos para sobrescrever o orçamento explicitamente. O valor que você definir também se torna o timeout de cada hook sem seu próprio timeout.

Este exemplo define o orçamento para 5 segundos:

CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

Antes da v2.1.268, CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS aumentava apenas o orçamento geral, e um hook sem seu próprio timeout ainda era cancelado após 1,5 segundo.

Elicitation

É executado quando um servidor MCP solicita entrada do usuário no meio de uma tarefa. Por padrão, o Claude Code mostra um diálogo interativo para o usuário responder. Os hooks podem interceptar essa solicitação e responder programaticamente, ignorando totalmente o diálogo.

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

O campo matcher corresponde ao nome do servidor MCP.

Entrada do Elicitation

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

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

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

Um hook Elicitation pode responder à solicitação pelo usuário, recusá-la ou cancelá-la, ou deixá-la para o diálogo. Para responder, recusar ou cancelar, termine com 0 e imprima um objeto hookSpecificOutput com uma action. O servidor recebe a sua resposta e nenhum diálogo aparece. Cada linha desta tabela mostra o que retornar para um resultado e o que o servidor MCP recebe:

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

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

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

Esta saída recusa uma solicitação:

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

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

Para uma solicitação no modo URL, um hook que retorna accept ignora o diálogo, então a URL nunca é aberta.

O Claude Code descarta reason, systemMessage e continue da saída JSON de um hook Elicitation, seja qual for a action que você retornar.

Outras maneiras de recusar uma elicitação

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

  • Termina com código 2: o Claude Code ignora um hookSpecificOutput impresso pelo mesmo hook
  • Imprime um "decision": "block" de nível superior: o bloqueio sobrescreve uma action na mesma saída

Quando vários hooks correspondem à mesma solicitação, uma recusa de um deles sobrescreve um accept ou cancel de outro.

Este script recusa solicitações no modo URL e deixa as solicitações de formulário para o diálogo:

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

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

O Claude Code ignorou um decision de nível superior dos hooks Elicitation e ElicitationResult da v2.1.105 até a correção na v2.1.284.

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

Este exemplo responde a uma pergunta recorrente pelo usuário. Um servidor MCP chamado issue-tracker pede uma chave de projeto em um formulário, e o hook preenche DOCS. O script aceita quando project_key é o único campo do formulário. Para qualquer outra solicitação, ele não imprime nada, então o diálogo aparece.

[tab] macOS/Linux

Registre um hook de comando para o evento no seu arquivo de configurações, com o nome do servidor como matcher:

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

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

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

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

[tab] Windows (PowerShell)

Registre um hook de comando que executa o script por meio do PowerShell, com o nome do servidor como matcher:

{
  "hooks": {
    "Elicitation": [
      {
        "matcher": "issue-tracker",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy",
              "Bypass",
              "-File",
              "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1"
            ]
          }
        ]
      }
    ]
  }
}

Salve este script em .claude/hooks/answer-project-key.ps1 no seu projeto:

$request = [Console]::In.ReadToEnd() | ConvertFrom-Json
$fields = @($request.requested_schema.properties.PSObject.Properties.Name)

if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') {
  @{
    hookSpecificOutput = @{
      hookEventName = "Elicitation"
      action = "accept"
      content = @{ project_key = "DOCS" }
    }
  } | ConvertTo-Json -Depth 3
}

Para confirmar que o hook funciona, inicie o Claude Code com claude --debug e dê ao Claude uma tarefa que faça o servidor pedir a chave do projeto. Nenhum diálogo aparece, e o log de depuração tem uma linha que termina com Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}}.

ElicitationResult

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

Quando um hook Elicitation responde a uma solicitação, o Claude Code envia essa resposta ao servidor sem executar hooks ElicitationResult.

O campo matcher corresponde ao nome do servidor MCP.

Entrada do ElicitationResult

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

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

Saída do ElicitationResult

Um hook ElicitationResult pode deixar a resposta do usuário passar, alterar seus valores ou bloqueá-la. Para alterar ou bloquear a resposta, termine com 0 e imprima um objeto hookSpecificOutput com uma action. Cada linha desta tabela mostra o que retornar para um resultado e o que o servidor MCP recebe:

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

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

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

O seu content substitui todo o objeto content do usuário, então inclua os campos que você não está alterando. Retorne action junto com ele, porque o Claude Code ignora um hookSpecificOutput que não tenha action.

Os hooks ElicitationResult também são executados quando o usuário recusa ou cancela, e a sua action substitui a dele. Verifique se a action da entrada é accept antes de retornar accept, ou o seu hook transformará uma solicitação recusada em uma aceita. Este script faz a mesma alteração quando o usuário aceitou, mantém os outros campos e não imprime nada caso contrário:

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

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

Esta saída bloqueia a resposta:

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

O código de saída 2 e um "decision": "block" de nível superior também bloqueiam a resposta. Outras maneiras de recusar uma elicitação aborda qual deles tem efeito quando um hook os combina, o que o usuário vê e quais versões ignoravam decision.

O Claude Code descarta reason, systemMessage e continue da saída JSON de um hook ElicitationResult, seja qual for a action que você retornar.

Hooks baseados em prompt

Além de hooks de comando, HTTP e MCP tool, Claude Code suporta hooks baseados em prompt (type: "prompt") que usam um LLM para avaliar se deve permitir ou bloquear uma ação, e hooks de agente (type: "agent") que geram um verificador agentic com acesso a ferramentas. Nem todos os eventos suportam cada tipo de hook.

Eventos que suportam todos os cinco tipos de hook (command, http, mcp_tool, prompt e agent):

  • PermissionDenied
  • PostToolBatch
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • TeammateIdle
  • UserPromptExpansion
  • UserPromptSubmit

PermissionRequest suporta hooks command, http, mcp_tool e prompt mas não hooks agent. Se você configurar um hook de agente neste evento, Claude Code o ignora e o fluxo de permissão prossegue inalterado. Para permitir ou negar de um hook, retorne o objeto de decisão de um hook de comando ou HTTP.

Eventos que suportam hooks command, http e mcp_tool mas não prompt ou agent:

  • ConfigChange
  • CwdChanged
  • DirectoryAdded
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • MessageDisplay
  • Notification
  • PostCompact
  • PostModelSwitch
  • PreCompact
  • PreModelSwitch
  • SessionEnd
  • StopFailure
  • SubagentStart
  • WorktreeCreate
  • WorktreeRemove

SessionStart e Setup suportam hooks command e mcp_tool, e MCP tool hook fields descreve quando seus hooks mcp_tool são executados. Eles não suportam hooks http, prompt ou agent.

Como hooks baseados em prompt funcionam

Em vez de executar um comando Bash, hooks baseados em prompt:

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

Configuração de hook de prompt

Defina type para "prompt" e forneça uma string prompt em vez de um command. Use o placeholder $ARGUMENTS para injetar dados de entrada do hook em seu texto de prompt.

Em um hook de prompt ou de agente, você pode escrever o prompt como uma regra sobre o que bloquear ou permitir, como "Bloquear qualquer comando Bash que leia arquivos .env", ou como uma condição que deve ser verdadeira, como "Todos os testes unitários passam".

Este hook Stop pede ao LLM para avaliar se todas as tarefas estão completas antes de permitir que Claude termine:

{
  "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"
  • 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

[!WARNING]

Hooks de agente são experimentais. O comportamento e a configuração podem mudar em versões futuras. Para fluxos de trabalho em produção, prefira command hooks.

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

Como hooks de agente funcionam

Quando um hook de agente dispara:

  1. Claude Code gera um subagente com seu prompt e a entrada JSON do hook
  2. O subagente pode usar ferramentas como Read, Grep e Glob para investigar
  3. Após até 50 turnos, o subagente retorna uma decisão estruturada { "ok": true/false }
  4. Claude Code permite a ação se ok for true. Se ok for false, Claude Code trata o bloqueio da mesma forma que um hook de prompt com continueOnBlock: true naquele evento, conforme listado em Response schema

Hooks de agente são úteis quando a verificação requer inspecionar arquivos reais ou saída de teste, não apenas avaliar dados de entrada do hook sozinhos.

Configuração de hook de agente

Defina type para "agent" e forneça uma string prompt, usando $ARGUMENTS como placeholder para a entrada JSON do hook. Os campos de configuração são os mesmos que prompt hooks, exceto que hooks de agente têm um timeout padrão mais longo de 60 segundos e nenhum campo continueOnBlock.

O esquema de resposta é { "ok": true } para permitir ou { "ok": false, "reason": "..." } para bloquear. Em ok: false, Claude Code trata um hook de agente da forma que trata um prompt hook com continueOnBlock: true no mesmo evento; hooks de agente não têm campo continueOnBlock e não suportam o campo impossible do hook de prompt.

Este hook Stop verifica que todos os testes unitários passam antes de permitir que Claude termine:

{
  "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, exceto para saída repetida de SessionStart. Diferentemente de um systemMessage de hook síncrono, nenhum dos dois campos é mostrado para você.

Imprima a resposta JSON sozinha no stdout ou em uma linha própria:

  • Sozinha no stdout: quando a resposta é o único texto no stdout, ela pode ocupar várias linhas, como a saída formatada do jq. Ocupar várias linhas requer Claude Code v2.1.295 ou posterior.
  • Em uma linha própria: um hook assíncrono pode imprimir outro texto no stdout quando a resposta cabe sozinha em uma linha, por exemplo com jq -c.

Claude Code valida essa resposta JSON contra o mesmo esquema de saída que hooks síncronos, e descarta qualquer campo cujo valor tenha o tipo errado, como um systemMessage que não seja uma string, em vez de entregá-lo. Execute com --debug para ver um aviso nomeando cada campo descartado.

Notificações de conclusão de hook assíncrono são suprimidas por padrão. Para vê-las, ative modo verbose com Ctrl+O ou inicie Claude Code com --verbose.

Executar testes após mudanças de arquivo

Este hook inicia uma suite de testes em background sempre que Claude escreve um arquivo, então relata os resultados de volta ao Claude quando os testes terminam. Salve este script em .claude/hooks/run-tests-async.sh em seu projeto e torne-o executável com chmod +x:

#!/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.
  • Um hook SessionStart assíncrono é executado toda vez que o evento dispara. Claude Code ignora a saída do hook quando o texto de additionalContext e systemMessage corresponde ao que Claude recebeu por último desse hook, a menos que um hook SessionStart síncrono tenha adicionado outro texto desde então. Claude Code entrega o texto quando ele muda, e novamente na próxima vez que o hook for executado após uma compactação ou /clear. Antes da v2.1.295, Claude Code adicionava o mesmo texto à conversa novamente a cada retomada.

Considerações de segurança

Aviso

[!WARNING]

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

Confiança do workspace

Claude Code verifica a confiança do workspace antes de executar qualquer hook de um arquivo de configurações. O que conta como confiável depende do tipo de sessão:

  • Sessão interativa: Claude Code retém hooks de todos os arquivos de configurações, incluindo seu próprio ~/.claude/settings.json, até que você aceite o diálogo de confiança do workspace para a pasta, ou para um diretório pai cuja confiança se estende a ela
  • Sessão -p ou SDK: Claude Code nunca mostra o diálogo e trata a pasta como confiável, então hooks confirmados no .claude/settings.json de um repositório são executados em uma pasta que você nunca confiou

Antes de executar claude -p em um repositório que você não escreveu, revise seus arquivos de configurações .claude/, comece com --bare, ou desative hooks para essa execução com --settings '{"disableAllHooks": true}'. Hooks de frontmatter em um subagente de projeto seguem uma regra mais rigorosa do que hooks de arquivo de configurações. O que é executado antes de você confiar em uma pasta lista cada tipo de conteúdo de repositório por tipo de sessão.

Melhores práticas de segurança

Mantenha essas práticas em mente ao escrever hooks:

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

Ferramenta Windows PowerShell

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

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

Referenciar scripts por caminho em hooks do PowerShell

Para referenciar o diretório raiz do projeto a partir de um comando em forma de shell do PowerShell, escreva ${CLAUDE_PROJECT_DIR} ou $env:CLAUDE_PROJECT_DIR. Claude Code reescreve os espaços reservados ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} e ${CLAUDE_PLUGIN_DATA} em um comando em forma de shell do PowerShell para a forma ${env:NAME} do PowerShell, independentemente de o hook estar definido em settings.json, um plugin ou uma skill. PowerShell então resolve o valor do ambiente exportado após análise, então o espaço reservado funciona dentro de strings entre aspas duplas, mas não dentro de strings entre aspas simples, onde PowerShell nunca expande variáveis.

Não escreva a forma nua $CLAUDE_PROJECT_DIR em um hook do PowerShell. PowerShell a analisa como uma variável local indefinida e a resolve para $null, o que deixa o caminho do script sem seu prefixo de diretório raiz do projeto. Claude Code não reescreve essa forma; em vez disso, registra um aviso no log de depuração.

O exemplo abaixo mostra um hook settings.json que executa um script de projeto com a forma $env::

{
  "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 encontrar um hook lento, pesquise no log por linhas Hooks: que terminam com uma duração. No Claude Code v2.1.296 ou posterior, cada hook de comando em um evento de ferramenta, UserPromptSubmit, SessionStart, Stop e vários outros eventos deixa uma dessas linhas quando termina, independentemente do que tenha impresso. A linha fornece o nome do evento, unido por dois-pontos ao nome da ferramenta ou a outro valor com o qual o hook correspondeu, depois o comando do hook entre colchetes, o plugin de onde ele veio, se houver, como a execução terminou e quanto tempo levou, como em Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms). Uma execução também pode terminar como timed out after <N>ms, cancelled, moved to the background ou failed to start. Em alguns eventos, como Notification, SessionEnd e PreCompact, um hook de comando deixa, em vez disso, uma linha completed with status sem duração.

Para detalhes de correspondência de hook mais granulares, defina CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose para ver linhas de log adicionais como contagens de matcher de hook e correspondência de consulta.

Para troubleshooting de problemas comuns como hooks não disparando, Stop hooks que continuam bloqueando, ou erros de configuração, consulte Limitações e troubleshooting no guia. Para um passo a passo de diagnóstico mais amplo cobrindo /context, /doctor e precedência de configurações, consulte Debug your config.