TIPS 31 · JEV

Jev, a IA que decide e pronto

Como pôr no Claude Code um controlo antes de um comando destrutivo arrancar: uma lista de comandos proibidos, um hook guardião e o prompt para o Claude fazer isso por ti.

O que é e porque importa

O vídeo falava do Jev: uma IA que não escreve texto, mas responde com um tipo preciso, ou seja uma escolha, uma pontuação ou um verdadeiro/falso, com as probabilidades e a confiança que tem. O que fazer com a resposta decide-o o teu código. Por agora há uma lista de espera. Aqui interessa-me uma ideia só: um controlo que fica entre a IA e o comando, antes de o comando arrancar. Isso o teu Claude Code já pode ter hoje, sem esperar em lista nenhuma.

O problema é simples. O Claude Code corre comandos no terminal do teu computador. Quase sempre são inofensivos: instala um pacote, corre os testes, lê um ficheiro. Mas alguns não se desfazem. rm -rf apaga uma pasta com tudo lá dentro, sem passar pelo lixo. git push --force reescreve o histórico do projeto online. git reset --hard deita fora as alterações que ainda não guardaste num commit. Um drop table apaga uma tabela da base de dados.

Não é preciso que o Claude se engane de propósito. Basta uma instrução mal percebida, um caminho errado, uma sessão longa em que se baralhou. E se aprovas as confirmações à pressa, ou as reduziste para andar mais depressa, o momento em que podias dizer que não passa sem dares por isso.

Há mais uma coisa a saber. O rewind do Claude Code (Esc duas vezes, ou /rewind) desfaz as alterações feitas com as suas ferramentas de ficheiros, mas não as feitas por comandos no terminal. Se um rm apagar um ficheiro, o rewind não to devolve.

Por isso o travão tem de ser posto antes. Duas maneiras: uma lista de comandos proibidos, a simples, e um pequeno script que lê cada comando antes de arrancar, a mais esperta. Depois há o prompt para o Claude as fazer.

1. A lista de comandos proibidos

O Claude Code lê as definições do projeto no ficheiro .claude/settings.json, na pasta do projeto. Se queres as mesmas regras em todos os projetos, usa ~/.claude/settings.json na tua pasta de utilizador. Lá dentro escreve isto:

O ficheiro
{
  "permissions": {
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force *)",
      "Bash(git reset --hard *)"
    ]
  }
}

Um «deny» ganha sempre a qualquer permissão dada. O Claude Code vê primeiro as regras deny, depois as que pedem confirmação e depois as que permitem. Por isso, mesmo que um dia tenhas respondido «sim, não perguntes mais» a um comando, o deny continua mais forte. E vale em todos os modos de permissão, mesmo em bypassPermissions.

O limite: a regra bloqueia o comando escrito assim, não as suas variantes. Bash(rm -rf *) trava rm -rf build/, mas não o mesmo comando dentro de bash -c, ou escrito como /bin/rm -rf build/. Também rm -fr, com as letras trocadas, passa. É uma rede, não um muro.

2. O guardião: um hook que verifica cada comando

Um hook é um script que o Claude Code corre sozinho num momento preciso. O que nos interessa chama-se PreToolUse: corre antes de cada chamada a uma ferramenta, e com o filtro «Bash» só olha para os comandos do terminal. Recebe o comando em JSON. Se o script sair com código 2, o comando não arranca e o Claude lê o motivo que escreveste. Se sair com 0, o comando segue com as permissões normais.

A vantagem em relação à lista: o script procura as palavras perigosas no texto inteiro do comando. Por isso também apanha um rm -rf escondido dentro de bash -c.

Cria o ficheiro .claude/hooks/guardiao.sh no projeto e cola lá isto:

O ficheiro
#!/bin/bash
command -v jq >/dev/null || { echo "é preciso o jq" >&2; exit 2; }
CMD=$(jq -r '.tool_input.command')
if echo "$CMD" | grep -Eiq 'rm -rf|git push --force|git reset --hard|drop table'; then
  echo "Bloqueado: comando destrutivo. Pede-me autorização primeiro." >&2
  exit 2
fi
exit 0

Torna-o executável com chmod +x .claude/hooks/guardiao.sh. O script usa o jq para ler o JSON: se não o tens, no Mac instalas com brew install jq. Se faltar, a primeira linha do script bloqueia todos os comandos em vez de os deixar passar.

Depois acrescenta em .claude/settings.json, ao lado de «permissions», esta parte:

O ficheiro
"hooks": {
  "PreToolUse": [
    {
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guardiao.sh" }]
    }
  ]
}

$CLAUDE_PROJECT_DIR indica a pasta do projeto, e as aspas à volta mantêm o caminho inteiro mesmo que tenha espaços. Juntas, as duas partes dão um ficheiro assim:

O ficheiro
{
  "permissions": {
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force *)",
      "Bash(git reset --hard *)"
    ]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guardiao.sh" }]
      }
    ]
  }
}

A lista no script és tu que a aumentas: põe os comandos que no teu projeto fazem estragos, por exemplo o que esvazia a base de dados ou o script que publica a app.

3. Se não te apetece fazer à mão

Podes pedir ao Claude que faça tudo: o script, o ficheiro de definições e o teste. Escreve-lhe:

O prompt
Configura em .claude/settings.json um hook PreToolUse que bloqueie os comandos destrutivos antes de arrancarem (rm -rf, git push --force, git reset --hard, drop table) e acrescenta as mesmas regras à lista deny das permissões. Depois cria uma pasta vazia de teste e verifica que rm -rf nessa pasta fica bloqueado.

Quando acabar, lê tu o ficheiro .claude/settings.json e o script. São poucas linhas. Se alguma coisa não te bater certo, pede que te explique:

O prompt
Mostra-me o conteúdo de .claude/settings.json e do script do hook, e explica-me linha a linha o que bloqueia e o que deixa passar.

4. O teste

Uma regra que não testaste é uma regra que esperas que funcione. Escreve /hooks no Claude Code: abre-se a lista dos hooks configurados, e o teu tem de aparecer em PreToolUse. Normalmente o Claude Code lê logo as alterações ao ficheiro de definições. Se o teu hook não aparecer, fecha e volta a abrir o Claude Code.

Atenção a um pormenor: com o deny ativo, um rm -rf escrito normalmente já é travado pela lista. Para veres se o guardião funciona sozinho, pede ao Claude o mesmo comando dentro de bash -c: a lista não o vê, o guardião sim.

O prompt
Cria uma pasta vazia chamada teste-guardiao e tenta apagá-la com bash -c 'rm -rf teste-guardiao'. Depois diz-me o que te travou: a lista deny ou o hook.

Se o comando passar, o guardião não está a funcionar: normalmente o script não é executável ou o caminho no settings.json está errado, e nesses casos o Claude Code não recebe o código 2 e deixa o comando seguir. Se, pelo contrário, ficar tudo bloqueado, até um simples ls, e o motivo for «é preciso o jq», falta o jq.

5. A rede por baixo: git

O guardião reduz o risco, não o elimina. As variantes de um comando são muitas e não as vais prever todas. Por isso a verdadeira rede por baixo é o git: faz um commit antes de dares ao Claude um trabalho longo, e guarda uma cópia do projeto também online, por exemplo no GitHub. Se alguma coisa correr mal, voltas ao último commit.

E o modo bypassPermissions, o que salta os pedidos de confirmação, a documentação do Claude Code só o recomenda em ambientes isolados, como um contentor ou uma máquina virtual. Não no computador onde tens tudo.

Os erros mais comuns

  • Sobrescrever o settings.json que já existia: acrescenta as partes novas ao que lá está e confirma as vírgulas, porque basta faltar uma vírgula para estragar o ficheiro.
  • Achar que o deny cobre todas as variantes: bloqueia o comando escrito assim, por isso junta-lhe o guardião que lê o texto inteiro do comando.
  • Esquecer o chmod +x: o script não arranca e o guardião não bloqueia nada sem te avisar, por isso faz sempre o teste numa pasta vazia.
  • Contar com o rewind para os ficheiros apagados: o rewind não desfaz os comandos do terminal, por isso faz um commit antes dos trabalhos longos.

Checklist

  • Regras deny em .claude/settings.json
  • Script guardião criado e tornado executável
  • jq instalado
  • Hook visível em /hooks em PreToolUse
  • Teste numa pasta vazia: comando bloqueado
  • Commit feito antes dos trabalhos longos

Perguntas frequentes

O Claude Code não pede já autorização antes dos comandos?

No modo Manual, sim: pergunta antes de cada comando que não seja só de leitura. Mas se respondes «sim, não perguntes mais», ou trabalhas num modo que pergunta menos, essa confirmação desaparece. O deny e o guardião, pelo contrário, continuam ativos aí também.

O que é melhor, a lista deny ou o hook?

Os dois. O deny são três linhas e não precisa de mais nada. O hook lê o texto inteiro do comando e apanha as variantes mais comuns. Um tapa os buracos do outro.

O guardião também pode bloquear comandos inofensivos?

Sim: se um comando tiver essas palavras, por exemplo uma pesquisa de texto com «rm -rf» lá dentro, é travado. Não faz mal: o Claude lê o motivo e pergunta-te como continuar. Se acontecer muitas vezes, torna a lista mais precisa.

Vale para todos os meus projetos?

Em .claude/settings.json vale para esse projeto, e se o ficheiro estiver no git vale também para quem trabalha nele contigo. Em ~/.claude/settings.json vale para todos os teus projetos: nesse caso põe o script em ~/.claude/hooks e escreve esse caminho no comando do hook.

← Todos os guias