Guia de Claude Code Hooks: checks seguros antes e depois do trabalho
Guia prático de Claude Code Hooks para iniciantes: bloquear comandos perigosos, salvar logs, formatar e rodar testes.
Claude Code Hooks são verificações automáticas executadas antes ou depois de uma ação do Claude Code. Em vez de repetir em todo prompt “não execute comandos perigosos”, “formate o arquivo” ou “rode os testes”, você pode colocar essas regras dentro do workflow do projeto.
O modelo mental mais simples é este: PreToolUse é o freio, PostToolUse é a manutenção, UserPromptSubmit é o registro de entrada e Stop é a checagem antes de sair. Hooks não substituem revisão humana, permissões ou CI. Eles tornam as verificações repetidas mais difíceis de esquecer.
Este artigo segue a referência oficial de Claude Code Hooks e a documentação oficial de settings. Para organizar o contexto do projeto, leia também boas práticas de CLAUDE.md. Para o desenho de segurança, combine com o guia de permissões do Claude Code.
Quatro eventos para aprender primeiro
Claude Code tem vários eventos de Hooks, mas a maioria dos times consegue valor começando por estes quatro.
| Evento | Melhor uso | Exemplo |
|---|---|---|
UserPromptSubmit | Antes de o prompt chegar ao Claude | Salvar a solicitação, detectar segredos, adicionar contexto leve |
PreToolUse | Antes de executar uma ferramenta | Bloquear comandos Bash destrutivos ou ações em produção |
PostToolUse | Depois de uma ferramenta executar com sucesso | Rodar formatter, lint ou testes relacionados |
Stop | Quando Claude vai terminar a resposta | Checar conflitos, salvar resumo, lembrar validação faltante |
Use PreToolUse quando a ação não pode acontecer. Use PostToolUse quando a ação já aconteceu e precisa de limpeza ou validação. Use UserPromptSubmit para observar a qualidade das solicitações. Use Stop para evitar encerrar uma sessão com problema evidente.
Regras compartilhadas normalmente ficam em .claude/settings.json. Experimentos pessoais são mais seguros em .claude/settings.local.json, porque não devem ir para o repositório. Em equipe, documente quais Hooks bloqueiam, quais apenas registram e quem mantém cada um.
Configuração mínima para copiar
A configuração abaixo salva prompts, bloqueia comandos Bash perigosos, executa checks depois de edições e registra um resumo no final.
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/log-prompt.mjs"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/block-dangerous-command.mjs"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/run-quality-checks.mjs",
"timeout": 120
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/stop-summary.mjs"
}
]
}
]
}
}
O detalhe importante é o array hooks aninhado. A estrutura atual define o evento, um grupo com matcher quando necessário, e depois handlers como type: "command". Exemplos antigos com command diretamente ao lado de matcher ainda aparecem, mas não são o padrão recomendado para copiar hoje.
Use case 1: bloquear comandos perigosos com PreToolUse
O primeiro use case real é segurança de comandos. Quando o agente trabalha rápido, o momento crítico não é depois da execução, e sim imediatamente antes. PreToolUse pode ler a entrada do Bash e negar uma ação claramente arriscada.
Salve como .claude/hooks/block-dangerous-command.mjs.
import fs from "node:fs";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const command = String(input.tool_input?.command || "");
const denyPatterns = [
/rm\s+-rf\s+(\/|\*|\.|\$HOME)/i,
/cat\s+\.env(\.|$|\s)/i,
/printenv/i,
/aws\s+.*\s+delete-/i,
/gcloud\s+.*\s+delete/i,
/kubectl\s+delete\s+(namespace|deployment|secret)/i,
/DROP\s+DATABASE/i
];
const matched = denyPatterns.find((pattern) => pattern.test(command));
if (matched) {
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: `Blocked by project hook: ${matched}`
}
}));
process.exit(0);
}
Isso não é um produto completo de segurança. Expressões regulares podem falhar. Mesmo assim, o script bloqueia erros óbvios: mostrar .env, apagar diretórios amplos, deletar recursos cloud, remover objetos Kubernetes ou executar SQL destrutivo. Para produção, combine com permissões do Claude Code, branches protegidos, CI e aprovação humana.
Use case 2: registrar prompts com UserPromptSubmit
O segundo use case é observabilidade do prompt. Muitas sessões ruins não começam por causa de um modelo fraco, mas por uma solicitação vaga: “conserta”, “limpa”, “deixa melhor”. Com log, você consegue revisar quais pedidos geram bons resultados.
Salve como .claude/hooks/log-prompt.mjs.
import fs from "node:fs";
import path from "node:path";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const dir = path.join(process.cwd(), ".claude", "hook-logs");
fs.mkdirSync(dir, { recursive: true });
const record = {
time: new Date().toISOString(),
event: input.hook_event_name,
cwd: input.cwd,
prompt: input.prompt || input.user_prompt || ""
};
fs.appendFileSync(
path.join(dir, "prompts.jsonl"),
JSON.stringify(record) + "\n",
"utf8"
);
Mantenha esse log local no começo. Prompts podem conter nomes de clientes, URLs internas, tokens colados por engano ou contexto de negócio sensível. Adicione .claude/hook-logs/ ao .gitignore e defina retenção, acesso e responsabilidade antes de virar padrão do time.
Use case 3: rodar format e test depois de editar
O terceiro use case é o ciclo de qualidade diário. Depois que Claude escreve ou edita um arquivo, execute formatter e uma verificação pequena. Assim você não precisa escrever “formate e teste” no final de todo prompt.
Salve como .claude/hooks/run-quality-checks.mjs.
import fs from "node:fs";
import { execFileSync } from "node:child_process";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
const filePath = String(input.tool_input?.file_path || "");
const isSourceFile = /\.(js|jsx|ts|tsx|css|md|mdx|json)$/.test(filePath);
if (!isSourceFile) process.exit(0);
const run = (cmd, args) => {
try {
return execFileSync(cmd, args, {
cwd: process.cwd(),
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"]
});
} catch (error) {
return String(error.stdout || "") + String(error.stderr || "");
}
};
const messages = [];
messages.push(run("npx", ["prettier", "--write", filePath]));
if (/\.(js|jsx|ts|tsx)$/.test(filePath)) {
messages.push(run("npm", ["test", "--", "--runInBand"]));
}
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: "PostToolUse",
additionalContext: messages.join("\n").slice(-4000)
}
}));
Em monorepos grandes, não rode a suíte inteira a cada edição. Comece por format, depois adicione testes relacionados e deixe checks pesados para Hooks async ou CI. Também limite a saída enviada de volta ao Claude para não consumir contexto com logs enormes.
Use case 4: revisar a sessão com Stop
Stop dispara quando Claude está prestes a terminar. É um bom lugar para registrar resumo ou bloquear apenas estados claramente incompletos, como conflitos de Git não resolvidos.
import fs from "node:fs";
import { execFileSync } from "node:child_process";
const input = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
if (input.stop_hook_active) {
process.exit(0);
}
let status = "";
try {
status = execFileSync("git", ["status", "--short"], {
cwd: process.cwd(),
encoding: "utf8"
});
} catch {
process.exit(0);
}
if (status.includes("UU ")) {
console.error("Git conflict remains. Resolve conflicts before finishing.");
process.exit(2);
}
fs.mkdirSync(".claude/hook-logs", { recursive: true });
fs.appendFileSync(
".claude/hook-logs/stop.jsonl",
JSON.stringify({
time: new Date().toISOString(),
lastAssistantMessage: input.last_assistant_message || "",
gitStatus: status.slice(0, 2000)
}) + "\n"
);
Tome cuidado com Stop. Se bloquear demais, o agente pode parecer preso tentando finalizar. Verifique stop_hook_active, mantenha condições estreitas e comece registrando mais do que bloqueando.
Pitfall: erros comuns e como evitar
O primeiro pitfall é tratar Hooks como substituto de permissões. Um command hook roda com as permissões do seu usuário do sistema. Se estiver mal escrito, pode ler segredos ou apagar arquivos. Valide entradas, proteja caminhos, evite .env e .git, e trate path traversal.
O segundo pitfall é executar trabalho demais de forma síncrona. Formatar um arquivo é aceitável. Rodar build, testes completos, browser tests e deploy depois de cada edição geralmente pesa demais. Checks rápidos ficam no PostToolUse; checks lentos vão para async ou CI.
O terceiro pitfall é salvar logs sem política. Logs ajudam no debug, mas podem conter informação sensível. Defina local, retenção, acesso e exclusão do Git antes de padronizar.
O quarto pitfall é confundir matcher com lógica de negócio. matcher: "Bash" só envia eventos Bash para o handler. A decisão real precisa analisar o comando completo dentro do script.
Nota de verificação no estilo Masa
Na prática, o maior ganho veio de devolver format e test para Claude com additionalContext. Claude consegue corrigir o próximo passo sem alguém copiar logs no chat. O bloqueio de comandos perigosos também ajuda, não porque é perfeito, mas porque para erros óbvios no momento certo.
Para um time iniciante, eu implantaria em três etapas: uma semana de logs de prompts, depois format após edição, e então uma lista pequena de padrões Bash proibidos. Esse caminho cria confiança antes de adicionar atrito.
Se quiser levar isso para o time, a página de training de Claude Code cobre permissões, CLAUDE.md, Hooks e review workflow. Para uso individual, o mesmo conteúdo pode virar uma checklist Gumroad para iniciar projetos com uma base repetível.
Resumo
Claude Code Hooks são guardrails práticos. Use UserPromptSubmit para entender solicitações, PreToolUse para bloquear ações arriscadas, PostToolUse para verificar mudanças e Stop para não encerrar com problemas evidentes.
Comece pequeno. Um Hook simples que continua ligado vale mais do que uma automação ambiciosa que o time desativa depois de dois dias.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Permission receipt no Claude Code: escopo, prova e rollback
Padrão de permission receipt para Claude Code: ações permitidas, limites de aprovação, comandos de prova, rollback e CTA de receita.
Agent Harness seguro para Claude Code e Codex: permissoes, verificacao e rollback
Monte uma base segura para agentes com Claude Code e Codex usando politicas, plano, verificacao e recuperacao.
Subagentes no Claude Code: guia prático para delegar trabalho com segurança
Guia prático de subagentes no Claude Code para dividir artigos e código: regras, prompts, riscos e checklist.