Pular para o conteúdo

Certificação Claude Developer Foundations · parte 16 de 20

Operação do Claude Code: permissões e execução sem interface

Como o Claude Code decide se uma ação pode acontecer, como as permissões se combinam e como rodá-lo sem interface num script ou pipeline de CI.

Publicada em · 36 min de leitura

Tamanho da fonte:
Largura da coluna:

Quando você abre o Claude Code num projeto, ele pode ler arquivos, editar código e rodar comandos no seu terminal. Isso é o que torna a ferramenta útil e também o que a torna arriscada, porque quem escolhe a ação é o modelo e quem executa é o programa. Esta aula responde duas perguntas que aparecem toda semana no trabalho: como o Claude Code decide se uma ação pode acontecer, e como rodá-lo sem interface, dentro de um script ou de um pipeline de CI.

Antes de começar, um aviso sobre a fonte. Tudo o que está aqui sobre comportamento do Claude Code vem da documentação oficial em code.claude.com, consultada em 18 de outubro de 2026, e foi conferido em parte com testes reais na versão 2.1.296 instalada na máquina de estudo. Essa ferramenta muda rápido. Quando um comportamento depender da versão, a aula diz qual. A lista do que não foi verificado fica no fim.

Mapa da aula

ParteSeçõesPergunta que responde
1. Como o Claude Code decide1 a 4Onde ficam as configurações e qual vence quando duas dizem coisas diferentes?
2. Regras de permissão5 a 9Como escrever allow, ask e deny, e por que deny sempre prevalece?
3. Modos de permissão10 a 12O que cada modo libera sem perguntar, e como a sessão escolhe o seu?
4. Claude Code sem interface13 a 16Como usar claude -p com formatos de saída confiáveis, em scripts e em CI?
5. Verificar qual regra venceu17 e 18Como descobrir, na prática, por que uma ação foi permitida ou negada?
6. PráticaExercícios, erros comuns, glossário e limitesComo praticar, o que evitar e o que ainda não foi confirmado

Pré-requisito: saber o que é um agent e o que é uma ferramenta (ler arquivo, rodar comando). Se faltar, leia Agents primeiro, especialmente as seções sobre o loop do agent e sobre hooks, que aqui só aparecem na parte que interessa à precedência.

Parte 1: Como o Claude Code decide

1. Por que existe um sistema de permissões

Imagine um estagiário muito rápido, que lê documentação em segundos e digita comandos sem cansar. Você pode pedir a ele, por escrito, que não mexa na pasta de produção. Ele provavelmente vai obedecer. Mas se a instrução estiver só num bilhete, nada impede que um dia ele esqueça o bilhete, ou que uma mensagem que ele leu mande fazer o contrário. Um controle de verdade precisa estar numa fechadura, não num bilhete.

No Claude Code, o bilhete é o prompt, o CLAUDE.md e as instruções da conversa. Eles orientam o que o modelo tenta fazer. A fechadura são as permissões, e a documentação oficial deixa a divisão explícita: as regras de permissão são aplicadas pelo Claude Code, não pelo modelo. Instruções no prompt ou no CLAUDE.md moldam o que o Claude tenta fazer, mas não mudam o que o Claude Code permite.

Essa distinção importa porque o risco é concreto. Se o modelo lê um arquivo .env durante uma investigação, o conteúdo entra no contexto e pode sair junto com a próxima chamada à API. Se ele roda git push --force numa branch compartilhada, o estrago não tem volta fácil. Um sistema de permissões responde a essas situações de forma previsível: cada chamada de ferramenta passa por uma decisão, e a decisão depende de regras escritas em arquivos que você pode ler, versionar e auditar.

2. Onde ficam as configurações

O Claude Code lê suas configurações de arquivos JSON em quatro lugares, e uma quinta fonte pode vir da organização. Cada arquivo tem um escopo, que é o conjunto de pessoas e projetos afetados por ele. A documentação oficial organiza assim:

EscopoArquivoQuem é afetadoPara que serve
Usuário~/.claude/settings.jsonVocê, em todos os projetos da máquinaPreferências pessoais e regras que valem em todo lugar
Projeto compartilhado.claude/settings.jsonQuem trabalha na pasta, inclusive o time pelo gitPermissões, hooks e variáveis que o projeto exige
Projeto local.claude/settings.local.jsonSó você, só neste projetoExceções pessoais e testes antes de compartilhar
Gerenciadomanaged-settings.json e outras fontes gerenciadasTodos que a organização implantaPolítica de segurança e exigências de compliance

Duas observações evitam confusão. A primeira é que ~/.claude.json não é o mesmo arquivo que ~/.claude/settings.json. O primeiro guarda estado do aplicativo e opções de interface, e o segundo guarda permissões, hooks e variáveis de ambiente. Quem coloca permissions no arquivo errado vê a regra ser ignorada e não recebe aviso claro. A segunda é que o arquivo local do projeto, o settings.local.json, é feito para ficar fora do git. Quando você cria esse arquivo na mão, precisa acrescentá-lo ao .gitignore por conta própria.

O caminho de arquivos gerenciados depende da plataforma e de como a organização entrega a política (arquivo, MDM ou console). Nesta aula não confirmei esse caminho para cada sistema. Quem precisar dele deve consultar a página de configurações gerenciadas da documentação.

3. A ordem de precedência

Quando a mesma chave aparece em mais de um lugar, o Claude Code usa o valor do nível mais alto que a define. A documentação oficial ordena assim, do mais forte para o mais fraco:

  1. Configurações gerenciadas, vindas da organização. Nada que você escreve em arquivos próprios ou em --settings sobrepõe uma chave gerenciada.
  2. Linha de comando, com --settings <arquivo-ou-json>, válida só para aquela sessão.
  3. Configurações locais do projeto, em .claude/settings.local.json.
  4. Configurações compartilhadas do projeto, em .claude/settings.json.
  5. Configurações do usuário, em ~/.claude/settings.json.

A ordem faz sentido pela pergunta que cada nível responde. A organização precisa vencer porque é ela quem responde pela segurança do parque inteiro. A linha de comando vem em seguida porque é a escolha mais recente e mais específica, feita agora, para esta sessão. O arquivo local vem antes do compartilhado porque é a preferência pessoal sobre o que o time definiu. E o usuário fica por último porque é o padrão mais amplo, que vale para todos os projetos e cede a qualquer coisa mais específica.

precedencia
figura 1 · precedencia

O diagrama mostra a pilha da esquerda para a direita, do nível mais forte ao mais fraco. Uma chave definida num nível alto cobre a mesma chave em todos os níveis à direita dele. A seta rotulada com “sobrepõe” lê-se assim: o nível da esquerda sobrepõe o da direita. O destaque pulsante marca a camada da organização, que nenhum arquivo do usuário nem a linha de comando consegue sobrepor, salvo as exceções de segurança que a documentação lista à parte. Duas nuances da documentação merecem atenção. Uma variável de ambiente não é um nível da pilha: quando existe uma variável de shell e uma chave de configuração para o mesmo comportamento, a documentação decide par a par qual dos dois vale. Flags como --model também não entram na pilha, e cada chave diz na sua própria página quais flags a sobrepõem.

4. Listas se somam e negações valem para todos

A precedência descrita acima vale para valores simples, como um nome de modelo. Para listas, a regra é diferente. Nos escopos que não são gerenciados, o Claude Code soma as listas de permissions.allow de todos os arquivos, em vez de deixar a de cima apagar a de baixo. Quem escreveu allow no arquivo do usuário e allow no arquivo do projeto obtém a união das duas.

A soma faz sentido para permissões. Se o time autoriza npm test e você autoriza git diff, o resultado deveria ser que você pode fazer as duas coisas. Trocar uma lista pela outra faria a autorização do projeto apagar a sua sem aviso, ou vice-versa.

A soma, porém, não vale para negações, e aqui está a regra que mais confunde. A documentação diz que, se uma ferramenta é negada em qualquer nível, nenhum outro nível pode permiti-la. Uma negação gerenciada não pode ser derrubada por --allowedTools, e --disallowedTools pode acrescentar restrições além das que o gerenciado define. O mesmo vale entre escopos de arquivo: se as configurações do usuário permitem uma ação e as do projeto a negam, a negação bloqueia. A recíproca também é verdadeira: uma negação no usuário bloqueia uma permissão do projeto.

Essa assimetria é intencional. Permitir é uma concessão, e concessões se acumulam. Negar é uma garantia, e uma garantia que pudesse ser desfeita por qualquer arquivo lido depois não serviria para nada. A seção 6 mostra como isso aparece na lógica de avaliação.

Parte 2: Regras de permissão

5. A sintaxe de uma regra

Uma regra de permissão tem a forma Ferramenta ou Ferramenta(especificador). Sem parênteses, a regra vale para a ferramenta inteira. Com parênteses, ela vale só para as chamadas cujo conteúdo casa com o padrão. Alguns exemplos da documentação, com o que cada um faz:

Bash casa todos os comandos de shell. Bash(*) é equivalente a ele. Bash(npm test) casa só o comando exato. Bash(npm run *) casa qualquer comando que começa com npm run . O espaço antes do asterisco importa: a documentação avisa que Bash(git diff*) também casa git diff-index, enquanto Bash(git diff *) não casa.

Read(./.env) casa a leitura desse arquivo, e Read(./secrets/**) casa tudo dentro da pasta. Edit(src/**) cobre edições em src. Um detalhe que costuma pegar: a documentação diz que as regras de caminho para arquivos são consultadas só para Edit e Read. Se você escrever Write(docs/**), o Claude Code aceita a regra, mas nunca a consulta, e avisa na inicialização. O certo é usar Edit(docs/**), que cobre a escrita também. WebFetch(domain:github.com) restringe o domínio de busca, e servidores MCP aparecem como mcp__nome-do-servidor.

Essa sintaxe aparece em dois lugares: nos arquivos de configuração, dentro de permissions.allow, permissions.ask e permissions.deny, e na linha de comando, em --allowedTools e --disallowedTools. A forma é a mesma nos dois.

6. allow, ask e deny, e por que deny prevalece

Existem três listas de regras, e cada uma responde a uma pergunta diferente. allow diz que a ferramenta roda sem pedir confirmação. ask diz que a chamada sempre pede confirmação, mesmo que alguma outra regra permita. deny diz que a chamada não acontece, ponto. A documentação define a ordem de avaliação de forma explícita: deny, depois ask, depois allow. A primeira regra que casa determina o resultado, e a especificidade da regra não muda essa ordem.

A ordem fixa resolve um problema real. Suponha que você escreva deny: ["Bash(aws *)"] para proibir a CLI da AWS, e depois, para facilitar a vida, allow: ["Bash(aws s3 ls)"]. Numa avaliação que desse prioridade à regra mais específica, o allow venceria e a proibição ficaria furada. Com a ordem da documentação, o deny casa primeiro e bloqueia. A documentação diz textualmente que uma regra de allow não pode abrir exceção numa regra de deny. A mesma lógica vale para ask contra allow: uma regra de ask que casa pede confirmação, mesmo quando uma regra de allow mais específica também casa.

Por que essa escolha é a certa? Porque o custo de errar é assimétrico. Se uma permissão ampla bloqueia por engano algo que deveria passar, o usuário vê a negação, lê a mensagem e ajusta a regra em um minuto. Se uma negação deixa passar algo que deveria ser proibido, o dano pode ser irreversível e ninguém percebe. Com deny sempre vencendo, o erro barato é o que acontece.

fluxo da decisao
figura 2 · fluxo da decisao

O diagrama lê-se de cima para baixo. Uma chamada de ferramenta entra e passa primeiro pelo hook PreToolUse, que a seção 8 explica. Depois, as três listas são consultadas nesta ordem, e a primeira que casa decide. Os losangos são as perguntas, as caixas são os desfechos, e as linhas que terminam em ✕ são os caminhos bloqueados. A linha verde é a chamada que roda. Se nenhuma regra casa, o modo de permissão da sessão decide, e é a ele que a parte 3 se dedica. Um detalhe de leitura: a linha laranja que sai da regra ask passa por trás da caixa do modo de permissão até chegar a “Pergunta”, e o rótulo “casa” dela fica perto do losango allow. Siga a cor da linha, não a posição do rótulo.

A figura mostra a ordem de decisão para uma chamada de ferramenta: deny primeiro, depois ask, depois allow. Se nenhuma regra casar, vale o modo de permissão.

regras
figura 3 · regras

7. Permissões do projeto só valem depois da confiança

Existe uma regra de segurança que surpreende quem usa claude -p em CI. A documentação diz que as entradas permissions.allow e permissions.additionalDirectories de um .claude/settings.json de projeto concedem capacidade. Por isso o Claude Code só as aplica depois que você aceita o diálogo de confiança da pasta. Já as entradas deny e ask não dependem disso, porque só restringem.

A razão é simples. Um repositório clonado de alguém pode trazer um .claude/settings.json que libera comandos perigosos. Se essa liberação valesse no momento do clone, o simples ato de abrir o projeto daria permissão a quem escreveu o arquivo. O diálogo de confiança existe para que você veja o que a pasta quer liberar antes de aceitar.

O problema é que o diálogo só aparece em sessões interativas. Um claude -p nunca o mostra. Eu testei isso com um .claude/settings.json numa pasta temporária que nunca foi confiada, com uma regra allow para Bash(echo *) e uma regra deny para Bash(echo bloqueado *). O aviso que o Claude Code imprimiu no stderr começa assim, e o restante da mensagem indica como aceitar a confiança numa sessão interativa:

Ignoring 1 permissions.allow entry from .claude/settings.json: this workspace has not been trusted. [...]

Ou seja, a permissão de allow foi ignorada, e a negação continuou valendo. O resultado da mesma execução confirmou o deny: o campo permission_denials do JSON listou a chamada echo bloqueado teste. A consequência prática é que, em CI, uma permissão definida só no arquivo do projeto pode não existir, e a execução falha ou pede confirmação de um jeito que o pipeline não espera. Para CI, o mais seguro é passar as permissões na linha de comando, como a parte 4 mostra.

8. Hooks rodam antes das regras

Os hooks PreToolUse são scripts que rodam antes de cada chamada de ferramenta, e a documentação diz que eles acontecem antes do diálogo de permissão. Um hook pode negar a chamada, forçar uma pergunta ou deixá-la passar. Quem já leu Agents viu um hook que lê o JSON da chamada pela entrada padrão e sai com código 2 para bloquear. Aqui interessa a relação com as regras.

A relação tem duas partes, e as duas são fáceis de esquecer. Primeiro, um hook que sai com código 2 bloqueia a chamada antes de as regras serem avaliadas. Isso significa que ele bloqueia mesmo quando uma regra allow liberaria a chamada. Segundo, um hook que devolve allow não passa por cima de deny nem de ask. A documentação afirma que as regras de deny e ask são avaliadas independentemente do que o hook devolve. Na prática: o hook pode endurecer uma decisão, mas não pode afrouxar uma negação.

Esse arranjo permite um padrão comum. Você escreve allow: ["Bash"] para não ser interrompido em todo comando, e registra um hook que rejeita os comandos específicos que deveriam ser barrados. O allow amplo resolve o atrito, e o hook resolve a garantia.

9. Bordas que pegam quem confia demais

As regras de Bash casam com o texto do comando que o Claude escreveu, depois que o Claude Code separa comandos compostos e remove alguns invólucros. Elas não casam com o mesmo programa chamado de outra forma. A documentação cita o caso de Bash(rm *), que não bloqueia /bin/rm nem find -delete. Por isso uma regra de Bash é uma barreira contra a forma mais comum de chamar o comando, não uma fronteira de segurança em volta do programa. Para uma garantia real, a documentação indica o sandbox ou um hook.

Há também um conjunto de comandos somente leitura, como ls, cat, grep, find, git diff e git log nas formas de leitura, que rodam sem pedir confirmação em qualquer modo. Essa lista não é configurável. Se você quer que um deles peça confirmação, precisa de uma regra ask explícita para ele.

Uma regra de deny que nomeia só a ferramenta, sem parênteses, faz algo mais forte. A documentação diz que ela remove a ferramenta do contexto do modelo, de modo que o Claude nem a enxerga. Já deny: ["Bash(rm *)"] deixa a ferramenta disponível e bloqueia só as chamadas que casam. Escolher entre as duas formas é escolher entre impedir a intenção e impedir o ato.

Parte 3: Modos de permissão

10. O que cada modo libera

As regras dizem caso a caso o que pode. Os modos de permissão dizem o padrão da sessão para tudo o que nenhuma regra decidiu. A documentação lista seis valores, e a tabela abaixo resume o que cada um deixa rodar sem perguntar, conforme a página oficial de modos:

ModoO que roda sem perguntarPara que serve
default (Manual na interface)Só leiturasQuem quer revisar cada ação
acceptEditsLeituras, edições de arquivo e comandos comuns de arquivo (mkdir, touch, mv, cp) nos caminhos de trabalhoIterar em código que você revisa depois
planLeituras e comandos somente leitura para explorar. Não edita código-fontePlanejar antes de mexer
autoTudo que um segundo modelo, o classificador, aprova ao revisar cada açãoTarefas longas com menos interrupções
dontAskTudo que não pediria confirmação, mais as ferramentas já liberadas por allow. O que pediria confirmação é negadoCI travado com lista exata
bypassPermissionsTudo, sem checar, exceto as ações que nenhum modo aprovaSó em contêiner ou máquina virtual isolada

Vale entender por que cada modo existe, não só o que ele faz. O default é o padrão conservador, adequado quando o risco é alto e você quer ler cada passo. O acceptEdits reconhece que editar arquivos é a ação mais frequente e, em geral, a mais reversível, porque o versionamento guarda o estado anterior. O plan separa a fase de entender da fase de agir: o modelo lê, pesquisa e propõe, sem tocar no código. O auto troca a sua atenção por um segundo modelo, o que reduz a fadiga de confirmar cada comando, mas transfere a decisão para um classificador que também pode errar. O dontAsk é o modo para automação: se algo precisar de confirmação, a resposta é não, e a execução segue. E o bypassPermissions existe para ambientes descartáveis, porque remove a checagem inteira.

Na prática, o modo dontAsk é o que mais combina com CI. Ele transforma a pergunta “posso?” em “estava na lista?”, que é exatamente o tipo de resposta que um pipeline consegue tratar. A documentação lembra que um allow explícito continua valendo nesse modo, e que AskUserQuestion, por exemplo, é negada mesmo que você tente liberá-la.

Uma ressalva sobre bypassPermissions: a documentação diz que, nesse modo, o Claude Code pula as confirmações inclusive para escritas em caminhos protegidos como .git e .claude. Não testei o comportamento das regras de deny dentro desse modo, então não afirmo nada específico sobre ele aqui. A regra geral da ordem deny, ask, allow vem antes do modo na avaliação, e a documentação de permissões trata o modo como a última etapa.

11. Como a sessão escolhe o modo inicial

Quando você não passa nada, a sessão começa num modo que depende de algumas fontes. A primeira é a flag --permission-mode, que vence qualquer configuração de arquivo. A segunda é a chave defaultMode dentro de permissions, nas configurações. A documentação diz que o modo inicial pode ser definido por essa chave em qualquer arquivo de configuração, com uma exceção importante: em .claude/settings.json e .claude/settings.local.json, os valores auto e bypassPermissions não têm efeito. Se você os escrever ali, a sessão começa no modo padrão, e um defaultMode do arquivo do usuário também não é usado nesse caso. A razão é a mesma da seção 7: um repositório não deve poder escolher, sozinho, um modo que dispensa confirmação.

O modo inicial de uma execução claude -p tem uma regra própria, e ela muda com a versão. A documentação diz que, em sessões que buscam feature flags, o modo padrão do -p é default. Em sessões que não buscam, como em provedores de terceiros ou com telemetria desligada, o modo é auto a partir da versão 2.1.285. Como a versão instalada na máquina de estudo é a 2.1.296, uma execução sem flag explícita pode começar em auto dependendo do ambiente. A lição para script é não depender do padrão. Passe --permission-mode sempre que a execução for automatizada.

12. Como desligar modos inteiros

Às vezes a questão não é escolher um modo, mas impedir que alguém use um. A documentação oferece duas chaves para isso: permissions.disableBypassPermissionsMode e permissions.disableAutoMode, ambas com o valor "disable". Elas funcionam em qualquer arquivo de configuração, mas a própria documentação recomenda colocá-las no arquivo gerenciado, onde não podem ser desfeitas. Uma chave no arquivo do usuário, por exemplo, serve para você mesmo se bloquear, o que pode ser útil para quem quer se proteger de um impulso de rodar tudo sem pensar.

Parte 4: Claude Code sem interface

13. O que é claude -p

Até aqui a aula tratou de uma sessão interativa, com um humano do outro lado respondendo às perguntas de permissão. claude -p (ou --print) é o modo sem interface: você passa um prompt, o Claude Code executa a tarefa, imprime o resultado e sai. A documentação chama esse uso de execução programática, e ele é a base de scripts, de hooks de build e de pipelines de CI.

A mudança de contexto explica por que as permissões ganham outro peso. Numa sessão interativa, uma pergunta de permissão espera você responder. Num -p, não existe ninguém para responder. Por isso a documentação trata o tema em separado: sem uma regra ou um modo que decida de antemão, a chamada que pediria confirmação é negada. O pipeline então precisa estar preparado para uma execução que terminou com ações bloqueadas, e não para uma que travou esperando.

O prompt pode ser um argumento ou vir da entrada padrão. O exemplo da documentação faz um pipe de um log de build:

cat build-error.txt | claude -p 'explique a causa raiz deste erro de build de forma concisa' > output.txt

A entrada pela pipe tem limite de 10 MB. Acima disso, a documentação diz que o Claude Code sai com erro e código de saída diferente de zero, e recomenda gravar o conteúdo num arquivo e passar o caminho no prompt.

Os códigos de saída são o contrato que um script usa para decidir. A documentação diz que o sucesso sai com código 0 e a falha com código diferente de zero. Testei um caso de erro de uso: uma flag com valor inválido. O comando foi claude -p "oi" --permission-mode modo-que-nao-existe. O resultado foi código de saída 1, nada na saída padrão e esta mensagem no stderr:

error: option '--permission-mode <mode>' argument 'modo-que-nao-existe' is invalid. Allowed choices are acceptEdits, auto, bypassPermissions, manual, dontAsk, plan.

Esse teste mostra uma distinção útil. Erro de uso, como uma flag inválida, aparece no stderr e interrompe antes da execução. Erro dentro da execução, como falta de autenticação, a documentação diz que aparece como resultado na saída padrão. Um script que só verifica o código de saída pode, portanto, perder uma falha de execução que saiu com código 0. A documentação não detalha, nesta página, qual código de saída acompanha cada tipo de falha dentro da execução, então não afirmo um valor específico. O que ela garante é que essa falha aparece como resultado na saída padrão. Por isso um script robusto lê a saída (no formato json, o campo is_error e o result) além de olhar o código de saída.

Há ainda um sinal de término que vale conhecer. Se alguém encerra a execução com SIGTERM, por exemplo com kill, o processo sai com código 143 e deixa a volta em andamento sem resultado. Para terminar a volta com elegância, a documentação indica enviar SIGINT antes.

14. Formatos de saída

Por padrão, -p imprime texto puro, que serve para um humano e é ruim para um programa. Para automação, a flag --output-format aceita três valores. text é o padrão. json devolve um único objeto ao final. stream-json devolve uma sequência de objetos JSON, um por linha, enquanto a execução acontece, e exige --verbose.

O formato json é o mais usado em script, e o que a documentação chama de estruturado. Para ver os campos reais, rodei uma tarefa pequena com ele: pedi ao modelo que executasse um comando de shell que a regra de negação bloqueava (detalhes na seção 18). Estes são os campos que o objeto devolveu, com os valores que importam:

type: "result"
subtype: "success"
is_error: false
num_turns: 2
total_cost_usd: 0.00249621
result: "FIM"
session_id: "cf4914b5-cdb9-4095-9358-8694479d5ad6"
permission_denials: [{"tool_name": "Bash", "tool_use_id": "...", "tool_input": {"command": "echo bloqueado teste", ...}}]

A lista completa tem mais campos, como tempos de execução, uso de tokens e modelUsage, que não detalhei aqui. Os que um script mais precisa são estes. result traz o texto final, e é o campo que o jq -r '.result' da documentação extrai. session_id identifica a conversa, para depois retomá-la com --resume. total_cost_usd é uma estimativa do lado do cliente, que a documentação diz poder diferir da fatura. E permission_denials lista as chamadas que foram negadas, com a ferramenta e a entrada. Esse último campo é o que permite a um pipeline saber que a execução terminou “com sucesso” mas deixou ações de fora. Quando a tarefa depende de uma escrita que foi negada, um script que só lê result vai acreditar que algo foi feito.

Quando você passa --json-schema, a documentação diz que a resposta traz o resultado validado no campo structured_output. Ela não diz, nessa página, se result continua preenchido nesse caso, então o script deve ler structured_output para obter os dados estruturados, e não presumir o conteúdo de result. A documentação também avisa que o Claude Code valida a sintaxe do schema, mas trata a palavra-chave format como anotação, sem validá-la no cliente.

O formato stream-json serve quando você quer acompanhar a execução em tempo real. Com --include-partial-messages, o fluxo traz os pedaços do texto à medida que são gerados. A última linha é sempre um objeto result, com o texto final e o custo. A documentação dá um exemplo de filtro com jq que mostra só o texto conforme chega:

claude -p "Explique recursão" --output-format stream-json --verbose --include-partial-messages | \
  jq -rj 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | .event.delta.text'

15. Uso em scripts: as flags que dão controle

Uma execução sem interface precisa de limites, porque ninguém está olhando para interromper um loop que gasta dinheiro. A documentação oferece três limites principais, e cada um tem uma razão diferente.

--max-turns limita o número de voltas do loop agêntico. Ao atingir o limite, a execução sai com erro. Sem essa flag, não há teto, e um modelo que se perde pode girar por muito tempo. Esse é o mesmo problema que o campo maxTurns resolve em subagents, visto em Agents, mas aqui ele é o teto da execução inteira.

--max-budget-usd interrompe a execução quando o gasto estimado chega ao valor. A documentação alerta para dois pontos. O gasto dos subagents conta para o mesmo teto. E o gasto pode ultrapassar o limite antes de a checagem acontecer, por isso convém deixar folga. Quando você retoma uma conversa com --continue ou --resume, os totais de execuções anteriores não contam para o novo limite.

--permission-mode e as listas de permissão definem o que a execução pode fazer. Para CI, a documentação recomenda a combinação de dontAsk com uma lista exata, em que cada ação liberada aparece nominalmente. Um exemplo oficial, que não rodei, é:

claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"

Esse comando só pode rodar npm test e ler arquivos. Qualquer outra ação que pediria confirmação é negada, e a negação aparece em permission_denials. A diferença para --allowedTools "Bash" é grande: a lista exata limita a execução ao que você escreveu, enquanto a forma ampla libera qualquer comando de shell.

Para execuções sem ninguém para responder, a flag --permission-prompts none diz à execução que não há quem responda às solicitações de permissão. A documentação explica que, quando existe um host de permissão (um SDK com callback de permissão, ou uma ferramenta passada em --permission-prompt-tool), a execução fica esperando a resposta dele a cada pedido. Com a flag, as solicitações que nenhuma regra ou hook resolveu são negadas e a execução segue. Essa flag exige a versão 2.1.259 ou superior.

Para retomar uma conversa, --resume aceita o session_id capturado de uma execução anterior. A documentação mostra o padrão em shell:

session_id=$(claude -p "Inicie uma revisão" --output-format json | jq -r '.session_id')
claude -p "Continue essa revisão" --resume "$session_id"

16. Em CI, prefira --bare

Quando você roda claude -p numa máquina de CI, o Claude Code descobre e carrega tudo o que uma sessão interativa carregaria: hooks, skills, servidores MCP, memória automática e o CLAUDE.md. A documentação oferece a flag --bare para pular essa descoberta, e a recomenda para scripts e chamadas por SDK. O motivo é segurança e reprodutibilidade. Sem --bare, a documentação diz que um -p roda os hooks do .claude/settings.json do projeto e conecta os servidores do .mcp.json, mesmo numa pasta que você nunca confiou, porque o -p não mostra o diálogo de confiança. Um clone de repositório desconhecido, num runner de CI, poderia assim executar código que não foi revisado.

Com --bare, só entra na execução o que você passar pela linha de comando, como --settings, --mcp-config e --agents. A documentação traz uma ressalva que importa para quem depende de login: em modo bare, o Claude Code não lê credenciais OAuth nem o chaveiro do sistema, então a chave da API precisa estar em ANTHROPIC_API_KEY. Um exemplo oficial, que não executei porque exige chave:

claude --bare -p "Resuma o README.md" --allowedTools "Read"

A documentação também diz que o modo bare, na versão atual, ainda é opcional, e que a intenção é torná-lo o padrão do -p em uma versão futura. Vale checar as notas de versão antes de depender de um comportamento específico.

Parte 5: Verificar qual regra venceu

17. Ferramentas para inspecionar a configuração

Quando uma ação foi negada e você não sabe por quê, há três perguntas a fazer: quais arquivos o Claude Code leu, quais regras estão em vigor e quais chaves são inválidas. A documentação oferece uma ferramenta para cada uma.

O comando /permissions, dentro da sessão, lista as regras de permissão em vigor e indica de qual arquivo cada uma veio. É a forma mais direta de ver a pilha de regras que a seção 6 descreveu. Você pode abri-lo enquanto o Claude trabalha, e uma regra adicionada ou removida passa a valer na próxima chamada de ferramenta.

O comando /status, também dentro da sessão, mostra quais fontes de configuração estão ativas, e se há política gerenciada em efeito. Ele diz quais arquivos foram carregados, mas não diz qual arquivo forneceu cada chave. A documentação é explícita sobre esse limite, e é justamente por ele que /permissions existe.

O comando claude doctor, no terminal e sem iniciar sessão, faz um diagnóstico somente leitura da instalação e dos arquivos de configuração. Ele aponta arquivos com erro de validação, como um matcher que não é texto. O /doctor, dentro da sessão, faz uma verificação mais completa e propõe correções, pedindo confirmação antes de aplicá-las. Eu rodei o claude doctor numa pasta de teste e ele respondeu “No installation issues found.”, o que confirma que o arquivo de exemplo não tinha erro de sintaxe ou de esquema.

Por fim, a documentação de depuração sugere dois testes de isolamento. O claude --safe-mode inicia a sessão com as personalizações desligadas, incluindo CLAUDE.md, hooks, skills e servidores MCP, mas mantendo permissões e a política gerenciada. Se o problema some nesse modo, a causa está em alguma personalização. O --setting-sources limita quais escopos são carregados, aceitando user, project e local. Foi com ele que isolei os testes da seção 18, para que as configurações do meu usuário não entrassem na conta.

18. Como verificar na prática, com um experimento

A documentação não traz um comando que explique, para uma chamada específica, qual regra decidiu o resultado. Pesquisei a página de permissões e a de depuração de configuração e não encontrei essa função. A verificação, portanto, é um experimento: você escreve uma regra conhecida, faz uma chamada previsível e lê o resultado no formato json, no campo permission_denials. Esse é o método que usei para os testes desta aula, e ele pode ser repetido.

O arquivo de teste foi criado numa pasta temporária, com o conteúdo abaixo. Validei o JSON com python3 -m json.tool, e ele passou sem erro.

{
  "permissions": {
    "allow": ["Bash(echo *)"],
    "ask": ["Bash(echo perguntar *)"],
    "deny": ["Bash(echo bloqueado *)"]
  }
}

Rodei três execuções, sempre com --setting-sources project para isolar as configurações do usuário, com o modelo Haiku, --max-turns 3 e --max-budget-usd 0.10. A tabela resume o que cada uma mostrou:

ExecuçãoConfiguração extraResultadopermission_denialsCusto estimado
A: comando negado pelo arquivoNenhumaCampo result apenas com FIMUma entrada, BashUS$ 0,0025
B: allow da linha de comando contra deny do arquivo--settings com allow: Bash(echo bloqueado *)Campo result com FIM e a explicação de que o comando foi negadoUma entrada, BashUS$ 0,0020
C: allow da linha de comando sem conflito--settings com allow: Bash(echo permitido *)Campo result com FIMLista vaziaUS$ 0,0020

A execução B é a mais instrutiva. Ela reproduz exatamente o caso que a seção 4 descreve: uma permissão passada na linha de comando, que tem precedência sobre os arquivos, não consegue derrubar uma negação do arquivo do projeto. O campo permission_denials registrou a chamada negada, e o texto final do modelo disse que o comando tinha sido negado pela permissão e não executado. A execução C mostra o contraste: sem conflito com nenhum deny, a permissão da linha de comando funciona e a lista de negações fica vazia. O aviso sobre a confiança da pasta apareceu no stderr das execuções A e C, que eu inspecionei. Na B, não li o stderr, então não afirmo o que ele continha.

Esse experimento tem uma limitação que vale registrar. Na execução B, o texto final explicou a negação, mas um pipeline que lê apenas result depende de o modelo mencionar o bloqueio, e isso não é garantido. Na execução A, o result trouxe só FIM, sem nenhuma menção à negação. Por isso o campo permission_denials é a fonte correta para essa pergunta, e não o texto da resposta.

Erros comuns e boas práticas

Confiar que o prompt impede uma ação

O erro mais frequente é escrever no prompt “não apague arquivos” e considerar o assunto resolvido. Como a seção 1 explicou, o prompt orienta o modelo e não bloqueia ação alguma. Se o risco é real, a proteção precisa estar numa regra deny, num modo que exige confirmação ou num hook. O prompt continua útil, mas como complemento.

Colocar a permissão no arquivo errado

Outro erro comum é escrever permissions dentro de ~/.claude.json, achando que é o arquivo de configurações. A regra é ignorada sem erro claro. O arquivo certo é ~/.claude/settings.json para o usuário, e .claude/settings.json para o projeto. Se uma regra do projeto parecer ignorada em -p, a causa mais provável é a confiança da pasta, como na seção 7.

Escrever a regra de arquivo com o nome da ferramenta errada

Uma regra Write(docs/**) é aceita e nunca consultada, porque as regras de caminho só são verificadas para Edit e Read. A correção é Edit(docs/**), que cobre as escritas. O erro é silencioso, então vale rodar uma execução de teste ao escrever uma regra nova.

Liberar Bash de forma ampla em CI

allow: ["Bash"] libera qualquer comando de shell, inclusive os que apagam ou enviam dados. É o atalho que mais aparece em tutoriais e o que mais dá problema depois. Em CI, uma lista exata com dontAsk, como na seção 15, deixa o escopo claro e transforma tudo fora da lista em negação visível em permission_denials.

Depender do modo padrão do -p

Como a seção 11 mostrou, o modo inicial do -p depende do ambiente e da versão. Um pipeline que funciona numa máquina pode pedir outro comportamento em outra. A boa prática é passar --permission-mode explicitamente em todo script, para que a execução não mude sem aviso quando a ferramenta mudar.

Usar bypassPermissions fora de um ambiente descartável

O modo que remove todas as confirmações é apropriado em contêiner ou máquina virtual que você pode jogar fora. Em máquina de desenvolvedor, ele transforma qualquer erro de raciocínio do modelo em ação real. A documentação pede que ele seja usado só em ambientes isolados, e essa recomendação deve ser lida como uma regra.

Não testar a regra antes de confiar nela

Uma negação que nunca foi exercitada pode estar escrita com um erro de padrão e nunca casar. O experimento da seção 18 custa centavos e confirma a regra. Quem depende de uma proteção deve testá-la como testa código, com uma chamada que deveria ser negada e uma que deveria passar.

Exercícios

  1. Crie uma pasta temporária com um .claude/settings.json que negue Bash(git push *) e permita Bash(git status). Rode claude -p pedindo que o modelo execute git push e leia o campo permission_denials. Depois troque a regra de deny por uma de allow e confirme a mudança. Explique por que a regra de deny não pode ser derrubada por um allow mais específico.

  2. Escreva um arquivo gerenciado imaginário e um de projeto que liberem o mesmo comando. Explique, com as palavras da seção 3, qual decisão prevalece e por que a organização não pode ser sobreposta por --settings. Não é preciso ter acesso a uma política gerenciada real para responder.

  3. Monte um script de CI que roda claude -p para revisar um diff. O script deve usar --output-format json, --max-turns, --max-budget-usd, --permission-mode dontAsk com uma lista exata, e falhar se permission_denials não estiver vazio. Justifique cada flag com o risco que ela controla.

Glossário

TermoSignificado
PrecedênciaOrdem que decide qual valor vence quando a mesma chave está em mais de um lugar
EscopoConjunto de pessoas e projetos afetados por um arquivo de configuração
Regra de permissãoEntrada em allow, ask ou deny que casa uma ferramenta, opcionalmente com um padrão
allowRegra que libera a chamada sem confirmação, desde que nenhuma regra de deny ou ask case antes
askRegra que sempre pede confirmação para a chamada
denyRegra que impede a chamada, e que nenhuma outra regra consegue desfazer
Diálogo de confiançaAviso interativo que pede para confirmar uma pasta antes de aplicar permissões que ela define
Modo de permissãoPadrão da sessão para as chamadas que nenhuma regra decidiu, como default, plan ou dontAsk
ClassificadorSegundo modelo que revisa cada ação no modo auto
Hook PreToolUseScript executado antes de cada chamada de ferramenta, que pode bloqueá-la
claude -pModo sem interface, que executa uma tarefa, imprime o resultado e sai
permission_denialsCampo do JSON de saída com a lista de chamadas negadas durante a execução
Modo bareExecução com --bare, que pula a descoberta automática de configurações e exige a chave de API em variável de ambiente

O que não foi verificado

Confirmei na documentação e, quando possível, em teste, os pontos desta aula. Ficaram de fora ou com verificação parcial os seguintes itens, que devem ser conferidos antes de virar resposta de prova.

Não rodei nenhuma execução com bypassPermissions, então não afirmo como as regras de deny se comportam nesse modo, apesar de a documentação tratar o modo como a última etapa da avaliação. Não confirmei o caminho exato do arquivo gerenciado em macOS, Linux e Windows. Não testei o formato da saída de --debug para regras de permissão, porque a documentação não o descreve em detalhe nesta página. Não listei todos os campos do JSON de -p, só os que apareceram na execução de teste, e não testei stream-json com as flags de eventos. Os exemplos com --permission-mode dontAsk, --bare, --json-schema e --resume vêm da documentação e não foram executados aqui, porque exigem um pipeline ou uma chave de API dedicada. Por fim, a versão 2.1.296 é a da máquina de estudo, e comportamentos ligados a versões, como o modo inicial do -p, podem mudar.

Fontes

  • Settings files and precedence: ordem de precedência, escopos, regra de soma de listas e exceções de política gerenciada. Consultada em 18 de outubro de 2026.
  • Configure permissions: sintaxe das regras, ordem deny, ask, allow, confiança do projeto, bordas de Bash e Read, hooks e precedência. Consultada em 18 de outubro de 2026.
  • Choose a permission mode: modos, modo inicial por contexto, comportamento de cada modo com -p. Consultada em 18 de outubro de 2026.
  • CLI reference: flags de -p, --permission-mode, --allowedTools, --disallowedTools, --settings, --setting-sources e --safe-mode. Consultada em 18 de outubro de 2026.
  • Run Claude Code programmatically: formatos de saída, códigos de saída, --bare, --max-turns, --max-budget-usd, --json-schema e --permission-prompts. Consultada em 18 de outubro de 2026.
  • Debug your configuration: /status, /permissions, claude doctor, /doctor e o teste de configuração limpa. Consultada em 18 de outubro de 2026.
  • Testes locais com Claude Code 2.1.296, em 18 de outubro de 2026: execuções de claude -p com configurações de teste, validação de JSON com python3 -m json.tool, claude doctor e erro de flag inválida. Os resultados citados nas seções 7, 13, 14 e 18 vêm desses testes.

Relacionados

↑ Voltar ao topo

Atalhos de teclado

Abrir busca
/
Alternar tema claro/escuro
t
Aula anterior na série
←
Próxima aula na série
→
Lista de atalhos
?