settings.json, permission modes e hooks
Objetivos de aprendizagem
- Distinguir os três arquivos de configuração:
~/.claude/settings.json(usuário),.claude/settings.json(projeto, versionado) e.claude/settings.local.json(local, não versionado) — e a precedência entre eles. - Configurar
permissionscom listasallow/denye umdefaultMode. - Explicar os permission modes (
default,acceptEdits,plan,bypassPermissions, entre outros). - Configurar hooks (
PreToolUse,PostToolUse,Stop...) para validação, bloqueio, formatação e notificação. - Justificar quando usar hooks (garantia determinística) em vez de instruções no CLAUDE.md (aderência probabilística).
Os três settings.json e a precedência
| Arquivo | Escopo | Versionado? | Uso típico |
|---|---|---|---|
~/.claude/settings.json | Usuário (todos os projetos) | Não | Preferências pessoais globais |
.claude/settings.json | Projeto (todo o time) | Sim | Permissões e hooks padrão do time |
.claude/settings.local.json | Projeto, só nesta máquina | Não (gitignored) | Exceções pessoais/experimentos locais |
Na resolução, configurações mais específicas prevalecem: políticas gerenciadas da organização (quando existem) vêm acima de tudo; depois o local sobrepõe o de projeto, que sobrepõe o de usuário. O paralelo com a lição 1 é direto: o que o time inteiro deve receber vai no arquivo de projeto versionado; o que é seu fica no de usuário ou no local.
Permissions: allow, deny e defaultMode
O bloco permissions controla o que o Claude Code pode fazer sem perguntar (allow) e o que é proibido mesmo que aprovado (deny). As regras usam o formato Ferramenta(padrão):
{
"permissions": {
"defaultMode": "default",
"allow": [
"Bash(npm run test:*)",
"Bash(npm run lint)",
"Read"
],
"deny": [
"Bash(rm -rf *)",
"Read(.env*)",
"Bash(git push --force*)"
]
}
}
Os permission modes definem a postura padrão da sessão:
default— pede confirmação para ações sensíveis (edições, comandos não permitidos).acceptEdits— auto-aprova edições de arquivos no projeto (bom para fluxo de implementação intenso).plan— somente leitura: explora e planeja, sem editar até o plano ser aprovado (lição 4).bypassPermissions— não pergunta nada; reservado a ambientes isolados (containers/CI sandbox), nunca à máquina de trabalho com dados sensíveis.
Atenção: deny vence allow. E bypassPermissions em máquina de desenvolvedor com credenciais reais é resposta errada por definição em cenários de prova — o modo existe para ambientes descartáveis.
Hooks: garantia determinística
Hooks são comandos do seu sistema executados automaticamente em eventos do ciclo de vida do Claude Code. A distinção central — cobrada repetidamente no exame, inclusive fora deste domínio — é:
Instrução em CLAUDE.md/prompt = probabilística (o modelo tende a obedecer, mas pode falhar). Hook = determinístico (o código roda sempre, independentemente do que o modelo "decidir"). Quando a exigência é "nunca X" ou "sempre Y" com consequência real, a resposta é enforcement programático — hook — e não prompt.
Principais eventos:
| Evento | Quando dispara | Caso de uso típico |
|---|---|---|
PreToolUse | Antes de executar uma ferramenta | Validar/bloquear comandos perigosos, exigir pré-condições |
PostToolUse | Depois que a ferramenta rodou | Rodar formatador/linter após cada edição |
UserPromptSubmit | Ao enviar um prompt | Injetar contexto, validar o pedido |
Stop | Quando o Claude vai encerrar a resposta | Verificar se testes passaram antes de "dar por concluído"; notificar |
SessionStart / SessionEnd | Início/fim da sessão | Carregar contexto dinâmico, logging/auditoria |
Exemplo real: settings.json de projeto com hooks
Formatação automática após toda edição (PostToolUse com matcher de ferramenta) e um guarda de segurança (PreToolUse em Bash):
{
"permissions": {
"defaultMode": "acceptEdits",
"deny": ["Read(.env*)"]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "python .claude/hooks/block_force_push.py" }
]
}
]
}
}
O hook recebe um JSON no stdin com os dados do evento (incluindo tool_input). Um hook PreToolUse pode negar a ação devolvendo uma decisão de permissão no stdout — o bloqueio acontece no harness, não depende de o modelo "concordar":
#!/usr/bin/env python3
"""Hook PreToolUse: bloqueia force push, decisao devolvida ao harness."""
import json
import sys
payload = json.load(sys.stdin)
command = payload.get("tool_input", {}).get("command", "")
if "push --force" in command or "push -f" in command:
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Force push bloqueado pela politica do time."
}
}))
sys.exit(0)
flowchart LR
M[Claude decide chamar Bash] --> H1{Hook PreToolUse}
H1 -- deny --> B[Chamada bloqueada +
motivo devolvido ao modelo]
H1 -- allow/sem decisão --> X[Ferramenta executa]
X --> H2[Hook PostToolUse
ex.: prettier no arquivo editado]
Hook ou instrução? O critério de decisão
- "Prefira nomes descritivos de variáveis" → CLAUDE.md: orientação de estilo, tolerante a desvio.
- "Nunca commitar em
main", "nunca ler.env", "todo arquivo editado sai formatado" → hook/permissions: exigência com consequência, precisa de garantia. - Time observa que "o Claude às vezes esquece de rodar o linter" mesmo com a instrução no CLAUDE.md → mover de instrução para hook
PostToolUseé a correção estrutural, não reescrever a instrução "com mais ênfase".
Pegadinhas da prova
- Distrator típico: reforçar o prompt ("deixe MUITO claro que é obrigatório") quando o cenário exige garantia — enforcement por prompt continua probabilístico; a resposta é hook ou
deny. - Distrator típico: colocar configuração do time em
settings.local.json— o local é gitignored; padrão de time vai em.claude/settings.jsonversionado. - Distrator típico: confundir a direção da precedência — o local (mais específico) sobrepõe o de projeto, que sobrepõe o de usuário.
- Distrator típico: usar
PostToolUsepara impedir uma ação — depois que a ferramenta rodou, já rodou; bloqueio éPreToolUse(oudenyem permissions). - Distrator típico:
bypassPermissionscomo "conveniência" na máquina do desenvolvedor — só é aceitável em ambiente isolado/descartável.
Resumo em 5 linhas
- Três arquivos: usuário (
~/.claude/settings.json), projeto (.claude/settings.json, versionado — padrão do time), local (.claude/settings.local.json, gitignored); o mais específico prevalece. permissions.allow/denyusamFerramenta(padrão), ex.:Bash(npm run test:*);denyvenceallow.- Permission modes:
default(pergunta),acceptEdits(auto-aprova edições),plan(somente leitura),bypassPermissions(só ambiente isolado). - Hooks executam código seu em eventos (
PreToolUse,PostToolUse,Stop...): validação, bloqueio, formatação pós-edição, notificação. - Hook = garantia determinística; CLAUDE.md = orientação probabilística — "nunca/sempre com consequência real" pede hook, não prompt.
Documentação oficial
Questões de fixação
1. O CLAUDE.md instrui "sempre rode o formatador após editar arquivos", mas em ~15% das sessões arquivos ficam sem formatar. O time quer que isso nunca mais aconteça. Qual é a solução correta?
Gabarito: B. "Nunca mais acontecer" exige garantia determinística: o hook roda o formatador após toda edição, sem depender da aderência do modelo. A continua probabilística — é o mesmo mecanismo que já falha 15% das vezes. C bloqueia o fluxo sem garantir a formatação. D só evita um prompt de permissão; não faz o formatador rodar.
2. Você configurou hooks úteis para o time inteiro, mas os colegas relatam que nada acontece nas máquinas deles. Você os definiu em .claude/settings.local.json. Qual é o problema?
Gabarito: C. O arquivo local é pessoal e gitignored — nunca chega aos colegas; o análogo exato do erro "instrução do time em ~/.claude/CLAUDE.md" da lição 1. A está errada: hooks podem viver em qualquer settings.json. B mistura conceitos — /memory lida com memória, não hooks. D é falso e perigoso: hooks funcionam em qualquer modo.
3. Por política de segurança, o Claude Code jamais deve ler arquivos .env, mesmo que um desenvolvedor aprove manualmente. Onde isso deve ser garantido?
Gabarito: A. deny é enforcement no harness: bloqueia inclusive o que o usuário tentaria aprovar, e versionado vale para o time todo. B é probabilístico. C age tarde demais — o segredo já entrou no contexto (e "apagar da conversa" não é o que PostToolUse faz). D não impede leitura: plan mode é justamente o modo de ler sem editar.
4. Qual par evento→uso está correto?
Gabarito: B. PreToolUse roda antes da ferramenta e pode negar a ação — é o ponto de bloqueio. A é impossível: PostToolUse dispara depois que o comando já rodou. C usa o evento errado — Stop dispara ao encerrar a resposta, não a cada edição (isso é PostToolUse). D confunde papéis: bloqueio por ferramenta/caminho é PreToolUse ou permissions.deny, não um evento de início de sessão.