Para um guia de início rápido com exemplos, veja Começar com hooks do Claude Code.

Configuração

Os hooks do Claude Code são configurados em seus arquivos de configurações:

  • ~/.claude/settings.json - Configurações do usuário
  • .claude/settings.json - Configurações do projeto
  • .claude/settings.local.json - Configurações locais do projeto (não commitadas)
  • Configurações de política gerenciada empresarial

Estrutura

Os hooks são organizados por matchers, onde cada matcher pode ter múltiplos hooks:

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}
  • matcher: Padrão para corresponder nomes de ferramentas, sensível a maiúsculas e minúsculas (aplicável apenas para PreToolUse e PostToolUse)
    • Strings simples correspondem exatamente: Write corresponde apenas à ferramenta Write
    • Suporta regex: Edit|Write ou Notebook.*
    • Use * para corresponder a todas as ferramentas. Você também pode usar string vazia ("") ou deixar matcher em branco.
  • hooks: Array de comandos para executar quando o padrão corresponder
    • type: Atualmente apenas "command" é suportado
    • command: O comando bash para executar (pode usar a variável de ambiente $CLAUDE_PROJECT_DIR)
    • timeout: (Opcional) Por quanto tempo um comando deve executar, em segundos, antes de cancelar esse comando específico.

Para eventos como UserPromptSubmit, Notification, Stop, e SubagentStop que não usam matchers, você pode omitir o campo matcher:

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/prompt-validator.py"
          }
        ]
      }
    ]
  }
}

Scripts de Hook Específicos do Projeto

Você pode usar a variável de ambiente CLAUDE_PROJECT_DIR (disponível apenas quando o Claude Code gera o comando hook) para referenciar scripts armazenados em seu projeto, garantindo que funcionem independentemente do diretório atual do Claude:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}

Eventos de Hook

PreToolUse

Executa depois que o Claude cria parâmetros de ferramenta e antes de processar a chamada da ferramenta.

Matchers comuns:

  • Task - Tarefas de subagente (veja documentação de subagentes)
  • Bash - Comandos shell
  • Glob - Correspondência de padrão de arquivo
  • Grep - Busca de conteúdo
  • Read - Leitura de arquivo
  • Edit, MultiEdit - Edição de arquivo
  • Write - Escrita de arquivo
  • WebFetch, WebSearch - Operações web

PostToolUse

Executa imediatamente após uma ferramenta completar com sucesso.

Reconhece os mesmos valores de matcher que PreToolUse.

Notification

Executa quando o Claude Code envia notificações. Notificações são enviadas quando:

  1. Claude precisa de sua permissão para usar uma ferramenta. Exemplo: “Claude precisa de sua permissão para usar Bash”
  2. A entrada do prompt ficou inativa por pelo menos 60 segundos. “Claude está esperando por sua entrada”

UserPromptSubmit

Executa quando o usuário submete um prompt, antes do Claude processá-lo. Isso permite que você adicione contexto adicional baseado no prompt/conversa, valide prompts, ou bloqueie certos tipos de prompts.

Stop

Executa quando o agente principal do Claude Code terminou de responder. Não executa se a parada ocorreu devido a uma interrupção do usuário.

SubagentStop

Executa quando um subagente do Claude Code (chamada da ferramenta Task) terminou de responder.

SessionEnd

Executa quando uma sessão do Claude Code termina. Útil para tarefas de limpeza, registrar estatísticas de sessão, ou salvar estado da sessão.

O campo reason na entrada do hook será um de:

  • clear - Sessão limpa com comando /clear
  • logout - Usuário fez logout
  • prompt_input_exit - Usuário saiu enquanto entrada de prompt estava visível
  • other - Outras razões de saída

PreCompact

Executa antes do Claude Code estar prestes a executar uma operação de compactação.

Matchers:

  • manual - Invocado de /compact
  • auto - Invocado de auto-compact (devido à janela de contexto cheia)

SessionStart

Executa quando o Claude Code inicia uma nova sessão ou retoma uma sessão existente (que atualmente inicia uma nova sessão por baixo dos panos). Útil para carregar contexto de desenvolvimento como problemas existentes ou mudanças recentes em sua base de código.

Matchers:

  • startup - Invocado da inicialização
  • resume - Invocado de --resume, --continue, ou /resume
  • clear - Invocado de /clear

Entrada de Hook

Hooks recebem dados JSON via stdin contendo informações da sessão e dados específicos do evento:

{
  // Campos comuns
  session_id: string
  transcript_path: string  // Caminho para JSON da conversa
  cwd: string              // O diretório de trabalho atual quando o hook é invocado

  // Campos específicos do evento
  hook_event_name: string
  ...
}

Entrada PreToolUse

O esquema exato para tool_input depende da ferramenta.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  }
}

Entrada PostToolUse

O esquema exato para tool_input e tool_response depende da ferramenta.

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "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",
    "success": true
  }
}

Entrada Notification

{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Task completed successfully"
}

Entrada UserPromptSubmit

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

Entrada Stop e SubagentStop

stop_hook_active é true quando o Claude Code já está continuando como resultado de um hook de parada. Verifique este valor ou processe a transcrição para prevenir que o Claude Code execute indefinidamente.

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

Entrada PreCompact

Para manual, custom_instructions vem do que o usuário passa para /compact. Para auto, custom_instructions está vazio.

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

Entrada SessionStart

{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "hook_event_name": "SessionStart",
  "source": "startup"
}

Entrada SessionEnd

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

Saída de Hook

Há duas maneiras para hooks retornarem saída de volta para o Claude Code. A saída comunica se deve bloquear e qualquer feedback que deve ser mostrado ao Claude e ao usuário.

Simples: Código de Saída

Hooks comunicam status através de códigos de saída, stdout, e stderr:

  • Código de saída 0: Sucesso. stdout é mostrado ao usuário no modo transcrição (CTRL-R), exceto para UserPromptSubmit e SessionStart, onde stdout é adicionado ao contexto.
  • Código de saída 2: Erro de bloqueio. stderr é alimentado de volta ao Claude para processar automaticamente. Veja comportamento por evento de hook abaixo.
  • Outros códigos de saída: Erro não bloqueante. stderr é mostrado ao usuário e execução continua.

Lembrete: Claude Code não vê stdout se o código de saída for 0, exceto para o hook UserPromptSubmit onde stdout é injetado como contexto.

Comportamento do Código de Saída 2

Evento de HookComportamento
PreToolUseBloqueia a chamada da ferramenta, mostra stderr ao Claude
PostToolUseMostra stderr ao Claude (ferramenta já executou)
NotificationN/A, mostra stderr apenas ao usuário
UserPromptSubmitBloqueia processamento do prompt, apaga prompt, mostra stderr apenas ao usuário
StopBloqueia parada, mostra stderr ao Claude
SubagentStopBloqueia parada, mostra stderr ao subagente Claude
PreCompactN/A, mostra stderr apenas ao usuário
SessionStartN/A, mostra stderr apenas ao usuário
SessionEndN/A, mostra stderr apenas ao usuário

Avançado: Saída JSON

Hooks podem retornar JSON estruturado em stdout para controle mais sofisticado:

Campos JSON Comuns

Todos os tipos de hook podem incluir estes campos opcionais:

{
  "continue": true, // Se Claude deve continuar após execução do hook (padrão: true)
  "stopReason": "string", // Mensagem mostrada quando continue é false

  "suppressOutput": true, // Ocultar stdout do modo transcrição (padrão: false)
  "systemMessage": "string" // Mensagem de aviso opcional mostrada ao usuário
}

Se continue for false, Claude para de processar após os hooks executarem.

  • Para PreToolUse, isso é diferente de "permissionDecision": "deny", que apenas bloqueia uma chamada de ferramenta específica e fornece feedback automático ao Claude.
  • Para PostToolUse, isso é diferente de "decision": "block", que fornece feedback automatizado ao Claude.
  • Para UserPromptSubmit, isso previne que o prompt seja processado.
  • Para Stop e SubagentStop, isso tem precedência sobre qualquer saída "decision": "block".
  • Em todos os casos, "continue" = false tem precedência sobre qualquer saída "decision": "block".

stopReason acompanha continue com uma razão mostrada ao usuário, não mostrada ao Claude.

Controle de Decisão PreToolUse

Hooks PreToolUse podem controlar se uma chamada de ferramenta prossegue.

  • "allow" contorna o sistema de permissão. permissionDecisionReason é mostrado ao usuário mas não ao Claude.
  • "deny" previne que a chamada da ferramenta execute. permissionDecisionReason é mostrado ao Claude.
  • "ask" pede ao usuário para confirmar a chamada da ferramenta na UI. permissionDecisionReason é mostrado ao usuário mas não ao Claude.
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow" | "deny" | "ask",
    "permissionDecisionReason": "My reason here"
  }
}

Os campos decision e reason estão depreciados para hooks PreToolUse. Use hookSpecificOutput.permissionDecision e hookSpecificOutput.permissionDecisionReason em vez disso. Os campos depreciados "approve" e "block" mapeiam para "allow" e "deny" respectivamente.

Controle de Decisão PostToolUse

Hooks PostToolUse podem fornecer feedback ao Claude após execução da ferramenta.

  • "block" automaticamente solicita ao Claude com reason.
  • undefined não faz nada. reason é ignorado.
  • "hookSpecificOutput.additionalContext" adiciona contexto para Claude considerar.
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}

Controle de Decisão UserPromptSubmit

Hooks UserPromptSubmit podem controlar se um prompt de usuário é processado.

  • "block" previne que o prompt seja processado. O prompt submetido é apagado do contexto. "reason" é mostrado ao usuário mas não adicionado ao contexto.
  • undefined permite que o prompt prossiga normalmente. "reason" é ignorado.
  • "hookSpecificOutput.additionalContext" adiciona a string ao contexto se não bloqueado.
{
  "decision": "block" | undefined,
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}

Controle de Decisão Stop/SubagentStop

Hooks Stop e SubagentStop podem controlar se Claude deve continuar.

  • "block" previne que Claude pare. Você deve popular reason para Claude saber como prosseguir.
  • undefined permite que Claude pare. reason é ignorado.
{
  "decision": "block" | undefined,
  "reason": "Must be provided when Claude is blocked from stopping"
}

Controle de Decisão SessionStart

Hooks SessionStart permitem que você carregue contexto no início de uma sessão.

  • "hookSpecificOutput.additionalContext" adiciona a string ao contexto.
  • Valores additionalContext de múltiplos hooks são concatenados.
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}

Controle de Decisão SessionEnd

Hooks SessionEnd executam quando uma sessão termina. Eles não podem bloquear terminação de sessão mas podem realizar tarefas de limpeza.

Exemplo de Código de Saída: Validação de Comando Bash

#!/usr/bin/env python3
import json
import re
import sys

# Define validation rules as a list of (regex pattern, message) tuples
VALIDATION_RULES = [
    (
        r"\bgrep\b(?!.*\|)",
        "Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
    ),
    (
        r"\bfind\s+\S+\s+-name\b",
        "Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
    ),
]


def validate_command(command: str) -> list[str]:
    issues = []
    for pattern, message in VALIDATION_RULES:
        if re.search(pattern, command):
            issues.append(message)
    return issues


try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")

if tool_name != "Bash" or not command:
    sys.exit(1)

# Validate the command
issues = validate_command(command)

if issues:
    for message in issues:
        print(f"• {message}", file=sys.stderr)
    # Exit code 2 blocks tool call and shows stderr to Claude
    sys.exit(2)

Exemplo de Saída JSON: UserPromptSubmit para Adicionar Contexto e Validação

Para hooks UserPromptSubmit, você pode injetar contexto usando qualquer método:

  • Código de saída 0 com stdout: Claude vê o contexto (caso especial para UserPromptSubmit)
  • Saída JSON: Fornece mais controle sobre o comportamento
#!/usr/bin/env python3
import json
import sys
import re
import datetime

# Load input from stdin
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

prompt = input_data.get("prompt", "")

# Check for sensitive patterns
sensitive_patterns = [
    (r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),
]

for pattern, message in sensitive_patterns:
    if re.search(pattern, prompt):
        # Use JSON output to block with a specific reason
        output = {
            "decision": "block",
            "reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
        }
        print(json.dumps(output))
        sys.exit(0)

# Add current time to context
context = f"Current time: {datetime.datetime.now()}"
print(context)

"""
The following is also equivalent:
print(json.dumps({
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": context,
  },
}))
"""

# Allow the prompt to proceed with the additional context
sys.exit(0)

Exemplo de Saída JSON: PreToolUse com Aprovação

#!/usr/bin/env python3
import json
import sys

# Load input from stdin
try:
    input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
    print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
    sys.exit(1)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})

# Example: Auto-approve file reads for documentation files
if tool_name == "Read":
    file_path = tool_input.get("file_path", "")
    if file_path.endswith((".md", ".mdx", ".txt", ".json")):
        # Use JSON output to auto-approve the tool call
        output = {
            "decision": "approve",
            "reason": "Documentation file auto-approved",
            "suppressOutput": True  # Don't show in transcript mode
        }
        print(json.dumps(output))
        sys.exit(0)

# For other cases, let the normal permission flow proceed
sys.exit(0)

Trabalhando com Ferramentas MCP

Os hooks do Claude Code funcionam perfeitamente com ferramentas do Model Context Protocol (MCP). Quando servidores MCP fornecem ferramentas, elas aparecem com um padrão de nomenclatura especial que você pode corresponder em seus hooks.

Nomenclatura de Ferramenta MCP

Ferramentas MCP seguem o padrão mcp__<server>__<tool>, por exemplo:

  • mcp__memory__create_entities - Ferramenta de criar entidades do servidor Memory
  • mcp__filesystem__read_file - Ferramenta de ler arquivo do servidor Filesystem
  • mcp__github__search_repositories - Ferramenta de busca do servidor GitHub

Configurando Hooks para Ferramentas MCP

Você pode direcionar ferramentas MCP específicas ou servidores MCP inteiros:

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

Exemplos

Para exemplos práticos incluindo formatação de código, notificações, e proteção de arquivo, veja Mais Exemplos no guia de início.

Considerações de Segurança

USE POR SUA PRÓPRIA CONTA E RISCO: Hooks do Claude Code executam comandos shell arbitrários em seu sistema automaticamente. Ao usar hooks, você reconhece que:

  • Você é o único responsável pelos comandos que configura
  • Hooks podem modificar, deletar, ou acessar qualquer arquivo que sua conta de usuário pode acessar
  • Hooks maliciosos ou mal escritos podem causar perda de dados ou dano ao sistema
  • Anthropic não fornece garantia e não assume responsabilidade por quaisquer danos resultantes do uso de hooks
  • Você deve testar completamente hooks em um ambiente seguro antes do uso em produção

Sempre revise e entenda quaisquer comandos de hook antes de adicioná-los à sua configuração.

Melhores Práticas de Segurança

Aqui estão algumas práticas chave para escrever hooks mais seguros:

  1. Valide e sanitize entradas - Nunca confie em dados de entrada cegamente
  2. Sempre quote variáveis shell - Use "$VAR" não $VAR
  3. Bloqueie travessia de caminho - Verifique por .. em caminhos de arquivo
  4. Use caminhos absolutos - Especifique caminhos completos para scripts (use $CLAUDE_PROJECT_DIR para o caminho do projeto)
  5. Pule arquivos sensíveis - Evite .env, .git/, chaves, etc.

Segurança de Configuração

Edições diretas em hooks em arquivos de configurações não têm efeito imediatamente. Claude Code:

  1. Captura um snapshot de hooks na inicialização
  2. Usa este snapshot durante toda a sessão
  3. Avisa se hooks são modificados externamente
  4. Requer revisão no menu /hooks para mudanças se aplicarem

Isso previne modificações maliciosas de hook de afetar sua sessão atual.

Detalhes de Execução de Hook

  • Timeout: Limite de execução de 60 segundos por padrão, configurável por comando.
    • Um timeout para um comando individual não afeta os outros comandos.
  • Paralelização: Todos os hooks correspondentes executam em paralelo
  • Deduplicação: Múltiplos comandos de hook idênticos são deduplicados automaticamente
  • Ambiente: Executa no diretório atual com ambiente do Claude Code
    • A variável de ambiente CLAUDE_PROJECT_DIR está disponível e contém o caminho absoluto para o diretório raiz do projeto (onde Claude Code foi iniciado)
  • Entrada: JSON via stdin
  • Saída:
    • PreToolUse/PostToolUse/Stop/SubagentStop: Progresso mostrado na transcrição (Ctrl-R)
    • Notification/SessionEnd: Registrado apenas para debug (--debug)
    • UserPromptSubmit/SessionStart: stdout adicionado como contexto para Claude

Depuração

Solução de Problemas Básica

Se seus hooks não estão funcionando:

  1. Verifique configuração - Execute /hooks para ver se seu hook está registrado
  2. Verifique sintaxe - Garanta que suas configurações JSON são válidas
  3. Teste comandos - Execute comandos de hook manualmente primeiro
  4. Verifique permissões - Certifique-se de que scripts são executáveis
  5. Revise logs - Use claude --debug para ver detalhes de execução de hook

Problemas comuns:

  • Aspas não escapadas - Use \" dentro de strings JSON
  • Matcher errado - Verifique se nomes de ferramenta correspondem exatamente (sensível a maiúsculas e minúsculas)
  • Comando não encontrado - Use caminhos completos para scripts

Depuração Avançada

Para problemas complexos de hook:

  1. Inspecione execução de hook - Use claude --debug para ver execução detalhada de hook
  2. Valide esquemas JSON - Teste entrada/saída de hook com ferramentas externas
  3. Verifique variáveis de ambiente - Verifique se ambiente do Claude Code está correto
  4. Teste casos extremos - Tente hooks com caminhos de arquivo ou entradas incomuns
  5. Monitore recursos do sistema - Verifique por exaustão de recursos durante execução de hook
  6. Use logging estruturado - Implemente logging em seus scripts de hook

Exemplo de Saída de Debug

Use claude --debug para ver detalhes de execução de hook:

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>

Mensagens de progresso aparecem no modo transcrição (Ctrl-R) mostrando:

  • Qual hook está executando
  • Comando sendo executado
  • Status de sucesso/falha
  • Mensagens de saída ou erro