Vigilância ativa

Hooks

Agent Smith está de olho

Os sentinelas invisíveis que interceptam cada ação do agente, antes ou depois que ela acontece.

Canal Sandeco
"
"

Ele não precisa ser chamado. Simplesmente está lá, observando cada movimento, esperando o gatilho que o aciona.

Hooks · A vigilância delegada

O que é um Hook?

Quatro características que separam um hook de qualquer outra peça do ecossistema.

Comando de shell

Um script que o sistema executa sem que você precise digitar nada.

Sentinela do agente

Intercepta cada ação antes ou depois da execução da ferramenta.

Bloqueia ou observa

Pode permitir, modificar ou impedir a ação que está acontecendo.

Vigilância delegada

Você configura uma vez. Ele vigia sempre, sem cansar nem dormir.

Hooks Skills

Vivem em lugares diferentes, têm propósitos diferentes e poderes diferentes. A confusão é cara.

Dimensão
Skill
Hook
Propósito
Ensina o agente a fazer algo novo
Vigia o que o agente faz
Onde mora
.claude/skills/
.claude/settings.json
Quem executa
O agente, dentro do contexto
O sistema, fora do agente
Pode ignorar?
Sim, em tese
Não. Barreira estrutural
Metáfora
Programa de pilotagem
Agent Smith vigiando

Onde os hooks moram

Hooks não ficam dentro das skills. Vivem no arquivo de configuração, no nível do sistema.

.claude/settings.json
// Hooks vivem fora das skills, no settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit",
        "command": "python scripts/check_protected.py \"$FILE_PATH\""
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "command": "python scripts/log_action.py \"$TOOL_NAME\" \"$EXIT_CODE\""
      }
    ]
  }
}

matcher

Filtra qual ferramenta dispara o hook (Edit, Bash, *).

command

Shell command que será executado pelo sistema, não pelo agente.

PreToolUse

O Smith que bloqueia

Antes da ferramenta rodar, o hook intercepta. Se ele retorna exit 1, o agente nunca executa.

Agente envia ação
Hook intercepta
Ferramenta nunca roda
PreToolUse

Proteção explícita

O script recebe o caminho, checa a lista de protegidos e retorna exit 1 para bloquear.

1

Lista de arquivos protegidos no topo

2

Compara com o argumento recebido

3

sys.exit(1) bloqueia a ação

scripts/check_protected.py
import sys

PROTECTED = [
    'config/production.yaml',
    '.env',
    'credentials.json'
]

file_path = sys.argv[1]

for protected in PROTECTED:
    if file_path.endswith(protected):
        print(f'BLOQUEADO: {file_path} é protegido.')
        sys.exit(1)   # recusa

sys.exit(0)       # libera

PostToolUse

O Smith que inspeciona

Depois que a ferramenta roda, o hook acorda. Não bloqueia nada, porque a ação já aconteceu. Ele observa, registra e devolve feedback.

Ferramenta executa
Hook inspeciona
Feedback ao agente
scripts/run_tests_after_edit.py
import subprocess
import sys

result = subprocess.run(
    ['python', '-m', 'pytest', 'tests/', '-q'],
    capture_output=True,
    text=True
)

if result.returncode != 0:
    print('ALERTA: testes falharam após a edição.')
    print(result.stdout)
    print(result.stderr)
else:
    print('Testes passaram.')

sys.exit(0)   # nunca bloqueia
PostToolUse

Reação automática

Roda os testes depois de cada edição. O resultado volta para o agente como feedback do próximo ciclo.

1

Executa pytest via subprocess

2

Imprime resultado para o agente ler

3

exit(0) sempre, nunca bloqueia

O ciclo de proteção

Cada ação do agente atravessa dois Smiths. Um pergunta antes, outro inspeciona depois. O loop nunca para.

Hands-on · 30 segundos

Seu primeiro hook: "Valeu Sandeco"

O mais simples possível. Toda mensagem sua dispara o hook, que imprime uma frase antes da resposta.

.claude/settings.json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"systemMessage\": \"Valeu Sandeco\"}'",
            "shell": "bash"
          }
        ]
      }
    ]
  }
}

UserPromptSubmit não usa matcher. Para a mensagem aparecer visível ao usuário, o hook devolve JSON com o campo systemMessage.

Claude Code · sessão ativa
> olá, tudo bem?
Hook UserPromptSubmit
Valeu Sandeco
Tudo ótimo! Como posso ajudar hoje?
aguardando próxima mensagem...

A mensagem aparece no terminal antes da resposta do Claude. Como veio em systemMessage, é exibida ao usuário pela interface.

1

Crie o arquivo

.claude/settings.json na raiz do projeto, com o JSON ao lado.

2

Reinicie o Claude Code

Hooks são lidos no boot. Aceite o registro quando ele pedir confirmação.

3

Mande qualquer mensagem

Antes da resposta, Valeu Sandeco aparece. Funciona em todo prompt.

Documentação oficial

O universo dos 28 hooks

Pre e Post são só o começo. O Claude Code expõe 28 eventos diferentes ao longo de toda a sessão.

Pode bloquear ação
Apenas observa

Categoria · 6 eventos

Sessão & configuração

Evento
Quando dispara
Bloqueia?
SessionStart
Início ou retomada de sessão Claude Code
não
Setup
Inicialização com flag --init-only
não
SessionEnd
Encerramento da sessão
não
CwdChanged
Diretório de trabalho muda durante a sessão
não
ConfigChange
Arquivo de configuração é alterado
Sim
InstructionsLoaded
CLAUDE.md ou regras de memória são carregadas
não

Categoria · 5 eventos

Turno do usuário

Evento
Quando dispara
Bloqueia?
UserPromptSubmit
Antes de processar o prompt enviado pelo usuário
Sim
UserPromptExpansion
Slash command está sendo expandido
Sim
Stop
Claude termina a resposta do turno
Sim
StopFailure
Erro ao tentar finalizar o turno
não
Notification
Claude Code envia uma notificação ao usuário
não

Categoria · 6 eventos · loop agentic

Ferramentas

Evento
Quando dispara
Bloqueia?
PreToolUse você viu
Antes do agente executar uma ferramenta
Sim
PostToolUse você viu
Depois que a ferramenta executa com sucesso
não
PostToolUseFailure
Depois que a ferramenta falha
não
PostToolBatch
Batch paralelo de ferramentas é finalizado
Sim
PermissionRequest
Diálogo de permissão aparece para o usuário
Sim
PermissionDenied
Modo automático nega uma ferramenta
Parcial

Categoria · 7 eventos

Subagentes, tarefas e arquivos

Evento
Quando dispara
Bloqueia?
SubagentStart
Um subagente é instanciado
não
SubagentStop
Subagente termina sua execução
Sim
TaskCreated
Tarefa é criada via TaskCreate
Sim
TaskCompleted
Tarefa é marcada como concluída
Sim
TeammateIdle
Outro agente colega prestes a ficar inativo
Sim
FileChanged
Arquivo observado muda no disco
não
WorktreeCreate
Git worktree é criada via batch
Sim

2 eventos

Compactação

PreCompact
Bloqueia
Antes do contexto ser compactado
PostCompact
observa
Depois da compactação acontecer

2 eventos

MCP

Elicitation
Bloqueia
Servidor MCP solicita input do usuário
ElicitationResult
Bloqueia
Usuário responde à solicitação MCP

Como um hook bloqueia uma ação

Três formas equivalentes de dizer "não" ao agente, em ordem do mais simples ao mais expressivo.

1 Exit code
sys.exit(2)

O mais simples. Qualquer linguagem.

2 JSON decision
{"decision":
  "block"}

Permite mensagem de feedback ao agente.

3 Permission deny
{"permission
  Decision":
  "deny"}

Específico de eventos de permissão.

Mãos à obra

Exemplos práticos

Cinco sentinelas que você pode configurar hoje, sem tocar em uma única ferramenta.

SessionStart UserPromptSubmit Stop PreCompact

Hook 1 · SessionStart

Boas-vindas com contexto

Ao abrir o Claude Code, o hook roda git status e os últimos commits. O agente já começa sabendo exatamente onde você parou.

.claude/settings.json
// Roda quando a sessão inicia
{
  "hooks": {
    "SessionStart": [
      {
        "command": "python scripts/git_briefing.py"
      }
    ]
  }
}
scripts/git_briefing.py
import subprocess

status = subprocess.run(
    ['git', 'status', '--short'],
    capture_output=True, text=True
).stdout

log = subprocess.run(
    ['git', 'log', '--oneline', '-n', '3'],
    capture_output=True, text=True
).stdout

print(f'Branch:\n{status}\nUltimos:\n{log}')

Ganho prático: nada de explicar de novo onde você estava. O agente abre a sessão já sabendo o estado do repositório.

Hook 2 · UserPromptSubmit

Auditoria de prompts

Cada mensagem que você envia para o agente é gravada em logs/prompts.jsonl com data e hora. Histórico completo do que você pediu, sem precisar lembrar.

.claude/settings.json
// Roda a cada prompt enviado
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "command": "python scripts/audit.py"
      }
    ]
  }
}
scripts/audit.py
import json, os, sys
from datetime import datetime

prompt = sys.stdin.read()

os.makedirs('logs', exist_ok=True)
with open('logs/prompts.jsonl', 'a') as f:
    f.write(json.dumps({
        'time': datetime.now().isoformat(),
        'prompt': prompt
    }) + '\n')

Ganho prático: revise depois o que pediu, treine prompts melhores, descubra padrões. Memória persistente sem custo.

Quer dominar cada um deles?

O livro Engenharia de Software para Agentes Inteligentes destrincha hooks, skills, MCP, subagents e tudo que faz o agente trabalhar a seu favor.

Canal Sandeco · @canalsandeco

Hook 3 · Stop

Som quando o agente termina

Você saiu para o café enquanto o agente trabalhava? O hook dispara um beep e uma notificação desktop assim que ele para de responder. Volte à janela só quando ouvir o sinal.

.claude/settings.json
// Roda quando o agente para
{
  "hooks": {
    "Stop": [
      {
        "command": "python scripts/notify.py"
      }
    ]
  }
}
scripts/notify.py
import winsound
from plyer import notification

# Beep curto: 880 Hz por 300 ms
winsound.Beep(880, 300)

# Notificação no Windows
notification.notify(
    title='Claude Code',
    message='O agente terminou. Volte à janela.',
    timeout=5
)

Ganho prático: trabalho assíncrono de verdade. Você delega, faz outra coisa, volta quando o agente avisa.

Hook 4 · PreCompact

Snapshot antes da compactação

Quando o contexto fica grande, o Claude comprime a conversa para caber. Se algo importante for cortado, o hook já salvou o original em snapshots/ antes do corte.

.claude/settings.json
// Roda antes da compactação
{
  "hooks": {
    "PreCompact": [
      {
        "command": "python scripts/snapshot.py"
      }
    ]
  }
}
scripts/snapshot.py
import os, sys
from datetime import datetime

stamp = datetime.now().strftime('%Y%m%d-%H%M%S')
dest = f'snapshots/{stamp}.md'

os.makedirs('snapshots', exist_ok=True)
with open(dest, 'w', encoding='utf-8') as f:
    f.write(sys.stdin.read())

print(f'Snapshot salvo em {dest}')

Ganho prático: compactação sem medo. O original fica no disco e você nunca perde uma decisão importante.

Hook 5 · SessionStart

Lembrete de pendências

Toda sessão começa lendo o TODO.md do projeto e mostrando o que ainda falta. Você abre o Claude Code já alinhado com as tarefas pendentes.

.claude/settings.json
// Dois hooks no mesmo evento
{
  "hooks": {
    "SessionStart": [
      { "command": "python scripts/git_briefing.py" },
      { "command": "python scripts/show_todo.py" }
    ]
  }
}
scripts/show_todo.py
import os, re, sys

if not os.path.exists('TODO.md'):
    sys.exit(0)

with open('TODO.md', encoding='utf-8') as f:
    pendentes = re.findall(r'- \[ \] (.+)', f.read())

if pendentes:
    print(f'Voce tem {len(pendentes)} pendentes:')
    for t in pendentes[:5]:
        print(f'  - {t}')

Ganho prático: bônus, dois hooks no mesmo evento. Ambos rodam em ordem antes do agente carregar.

Os cinco sentinelas lado a lado

Resumo dos hooks práticos que você acabou de ver.

Evento O que faz Ganho
SessionStart Injeta git status e últimos commits no contexto inicial Começa onde você parou
UserPromptSubmit Grava cada prompt em logs/prompts.jsonl com timestamp Histórico sem esforço
Stop Toca beep e dispara notificação quando o agente termina Trabalho assíncrono
PreCompact Salva conversa completa em snapshots/ antes do corte Compactação sem perdas
SessionStart TODO.md e exibe pendências do projeto Foco do primeiro segundo

Nenhum destes hooks usa PreToolUse ou PostToolUse. O Claude Code expõe vinte e oito eventos, você acabou de usar quatro deles.

Skill Trinity
+
Hook Agent Smith
=
Resultado Controle total

Poderoso o suficiente para ser útil, limitado o suficiente para ser confiável.

Trinity com o programa de pilotagem e Agent Smith vigiando cada manobra. É assim que se opera a Matrix sem perder o controle.

Capítulo 5 · Hooks