Domínio 3 — Claude Code Configuration & Workflows · Lição 2 de 6

settings.json, permission modes e hooks

Objetivos de aprendizagem

Os três settings.json e a precedência

ArquivoEscopoVersionado?Uso típico
~/.claude/settings.jsonUsuário (todos os projetos)NãoPreferências pessoais globais
.claude/settings.jsonProjeto (todo o time)SimPermissões e hooks padrão do time
.claude/settings.local.jsonProjeto, só nesta máquinaNã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:

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:

EventoQuando disparaCaso de uso típico
PreToolUseAntes de executar uma ferramentaValidar/bloquear comandos perigosos, exigir pré-condições
PostToolUseDepois que a ferramenta rodouRodar formatador/linter após cada edição
UserPromptSubmitAo enviar um promptInjetar contexto, validar o pedido
StopQuando o Claude vai encerrar a respostaVerificar se testes passaram antes de "dar por concluído"; notificar
SessionStart / SessionEndInício/fim da sessãoCarregar 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

Pegadinhas da prova

Resumo em 5 linhas

  1. 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.
  2. permissions.allow/deny usam Ferramenta(padrão), ex.: Bash(npm run test:*); deny vence allow.
  3. Permission modes: default (pergunta), acceptEdits (auto-aprova edições), plan (somente leitura), bypassPermissions (só ambiente isolado).
  4. Hooks executam código seu em eventos (PreToolUse, PostToolUse, Stop...): validação, bloqueio, formatação pós-edição, notificação.
  5. 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?

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?

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?

4. Qual par evento→uso está correto?