Guia de campo do Claude Code em português

Hooks no Claude Code: o que são e dois exemplos completos para o Windows

Instrução no CLAUDE.md é pedido: o Claude lê e quase sempre segue. Hook é mecanismo: um comando que o Claude Code roda num momento fixo da sessão, sem depender de o modelo lembrar (p. 80). Abaixo, os eventos principais e dois hooks em PowerShell, do script ao registro no settings.json, sem jq.

O que é um hook

Formatar todo arquivo editado, impedir que alguém mexa no .env, avisar quando o Claude espera por você: tudo isso cabe num hook e acontece toda vez. Cada momento em que um hook pode rodar é um evento, e são mais de trinta (p. 80). O hook é um comando seu, com as suas permissões de usuário: ele pode ler, alterar e apagar tudo o que a sua conta alcança. Leia antes de pôr um hook de terceiros.

Os eventos que você vai usar

EventoQuando disparaUso típico
SessionStartAo abrir ou retomar a sessão, e depois de /clear e de compactarPôr contexto do dia na conversa
UserPromptSubmitQuando você envia um pedidoBarrar pedido, anexar contexto
PreToolUseAntes de uma ferramenta rodarBarrar comando ou arquivo
PostToolUseDepois que a ferramenta rodou sem erroFormatar, rodar lint, registrar
StopQuando o Claude termina de responderExigir teste antes de parar
NotificationQuando o Claude Code manda um avisoAviso na área de trabalho

Ctrl+C e Esc não disparam o Stop; erro de API dispara o StopFailure (p. 80).

Exemplo 1: formatar depois de editar (PostToolUse)

O hook recebe o evento como JSON na entrada padrão. O script pega o caminho em tool_input.file_path e chama o Prettier só para as extensões que ele conhece (p. 82). Instale o Prettier no projeto antes, com npm i -D prettier.

.claude\hooks\formatar.ps1

[Console]::InputEncoding = [Text.Encoding]::UTF8
$evento  = [Console]::In.ReadToEnd() | ConvertFrom-Json
$arquivo = $evento.tool_input.file_path
if ($arquivo -match '\.(js|jsx|ts|tsx|json|css|md)$') {
  npx --no-install prettier --write "$arquivo" *> $null
}
exit 0

.claude\settings.json

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File",
                     "${CLAUDE_PROJECT_DIR}/.claude/hooks/formatar.ps1"]
          }
        ]
      }
    ]
  }
}

Com args, o Claude Code chama o powershell.exe direto, sem shell no meio, e cada item vai como um argumento: o caminho com espaço não precisa de aspas. Por que não chamar o npx direto: nessa forma o Windows só abre executável de verdade, e o npx.cmd não é (p. 82). O --no-install impede o npx de baixar o Prettier da internet se ele faltar no projeto.

Exemplo 2: barrar edição em arquivo protegido (PreToolUse)

Num PreToolUse, o hook roda antes da ferramenta e pode impedi-la. O código de saída 2 barra, e o texto do stderr vira o motivo, que o Claude recebe e usa para mudar de rumo. O exit 1 não barra: sem JSON válido no stdout, vale como erro não bloqueante e a ação segue. Para política, só o 2 (p. 84).

.claude\hooks\proteger.ps1 (salvo em UTF-8 com BOM)

[Console]::InputEncoding  = [Text.Encoding]::UTF8
[Console]::OutputEncoding = [Text.Encoding]::UTF8
$evento  = [Console]::In.ReadToEnd() | ConvertFrom-Json
$arquivo = $evento.tool_input.file_path -replace '\\', '/'
$protegidos = '.env', 'package-lock.json', '.git/'
foreach ($p in $protegidos) {
  if ($arquivo -like "*$p*") {
    $msg = "Bloqueado: $arquivo está protegido ($p)."
    [Console]::Error.WriteLine($msg)
    exit 2
  }
}
exit 0

O registro é o do exemplo 1 com duas trocas: o evento passa a ser PreToolUse e o arquivo, proteger.ps1. O -replace troca a barra invertida por /, para o padrão .git/ casar no Windows. Peça ao Claude um comentário no .env: o Edit é barrado e ele recebe a linha Bloqueado: ….

Dois limites do exemplo (p. 84). O padrão é largo: *.env* pega também o .env.example, então afine a lista ao seu projeto. E ele não barra o Bash: um Set-Content .env passa por fora do Edit. Para isso, um segundo grupo com o matcher Bash|PowerShell, ou uma regra deny nas permissões.

O que o Claude Code faz com a saída do hook

O hook termina comEfeito num PreToolUse
exit 0, nada no stdoutSem decisão: segue o fluxo normal de permissão
exit 0 e JSON no stdoutO JSON decide: deny, ask, allow ou contexto
exit 2 e motivo no stderrBarra a ação; o stderr volta ao Claude
exit 1 ou outro, sem JSONErro não bloqueante: a ação segue

O exit 2 vence o JSON, até um "allow". Num evento que só observa, como o PostToolUse, o exit 2 não desfaz nada: só mostra o stderr ao Claude (p. 84). Com vários hooks no mesmo evento, todos rodam até o fim e vence a decisão mais restritiva, na ordem deny, defer, ask, allow.

Mac e Linux

A documentação faz o exemplo 1 numa linha, com o jq instalado: jq -r '.tool_input.file_path' | xargs npx prettier --write (p. 82). No Windows, o PowerShell lê o JSON com ConvertFrom-Json e o jq não faz falta.

Leia também