Claude Code headless: dá para rodar o agente dentro de um script, sem aprovar cada passo?

agente Claude Code headless rodando comandos em script sem aprovação manual
Resposta rápida

Claude Code headless é o agente rodando fora do terminal interativo: você passa claude -p "..." com o prompt, ele executa o loop até o fim, imprime o resultado e sai. Sem humano na frente, quem decide o que pode rodar é a configuração: --allowedTools pré-aprova ferramentas, --permission-mode fixa a linha de base da sessão e o modo dontAsk nega de saída tudo que abriria um prompt, posicionado pela doc justamente para CI e ambientes restritos. Some a isso --output-format, --max-turns, --bare e a leitura do código de saída (0 = sucesso) e o script anda sozinho sem virar aposta.

Fala aí, beleza? Agente de IA rodando sozinho dentro de um script é uma das coisas mais massa (e mais assustadoras) que dá pra montar hoje

No terminal a lógica é confortável: o Claude pede, tu aprova ou nega, a vida segue

Só que num pipeline de CI, num cron, num script em lote, não tem ninguém pra clicar em nada

E aí a pergunta muda de figura: não é mais "o agente consegue rodar sem interface?", é "o que exatamente ele tem permissão de fazer quando a resposta padrão não existe mais?"

Este post cobre as três partes disso: como rodar o Claude Code de forma não interativa, qual modo de permissão faz sentido quando ninguém está no terminal, e o que precisa estar decidido ANTES de tirar o humano do meio

Como rodar o Claude Code dentro de um script, passo a passo

A base é uma flag só, o resto é configuração em volta dela

  1. Rode com <code>-p</code> (ou <code>–print</code>) passando o prompt

Com essa flag o Claude Code executa o loop do agente até o fim, imprime o resultado e sai, sem interface de terminal

claude -p "Resuma as mudanças do último commit"

É o modo não interativo puro: entra prompt, sai texto, processo encerrado

  1. Escolha o formato de saída com <code>–output-format</code>

São três opções: <code>text</code>, <code>json</code> e <code>stream-json</code>

claude -p "Resuma as mudanças do último commit" --output-format json

O <code>stream-json</code> é JSON delimitado por linha, feito pra você processar em tempo real, conforme a execução acontece, em vez de esperar o fim

O erro comum deste passo: o script assumir que a saída é texto e tentar parsear como JSON (ou o contrário)

Decida o formato antes de escrever o parser, não depois

  1. Pré-aprove as ferramentas com <code>–allowedTools</code>

Essa flag usa a sintaxe de regras de permissão e serve exatamente pra execução não travar pedindo confirmação

claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"

O erro comum deste passo: esquecer uma ferramenta na lista

Num terminal isso viraria um prompt e você resolveria em dois segundos, num script vira uma execução que não sai do lugar

Domine o Claude Code do básico ao avançado
Pré-inscrição Formação Claude Code

Domine o Claude Code do básico ao avançado

Você vai aprender a criar sistemas completos com Claude Code, sem precisar ser programador. Inscreva-se para ter acesso a um desconto de lançamento e bônus especiais!

  1. Fixe a linha de base com <code>–permission-mode</code>

O <code>–allowedTools</code> diz o que pode passar

A flag <code>–permission-mode</code> define o comportamento padrão da sessão inteira, ou seja, o que acontece com tudo que NÃO está na lista

claude -p "..." --allowedTools "Read,Edit" --permission-mode dontAsk

Os modos estão comparados na próxima seção

  1. Limite o gasto com <code>–max-turns</code>

Essa flag limita o número máximo de turnos que o agente pode dar numa execução (no Agent SDK, a opção equivalente é <code>max_turns</code>)

É o seu freio contra a execução que entra em loop de tentativa e fica ali, girando

  1. Ramifique o script pelo código de saída

O Claude Code sai com código 0 em caso de sucesso e código diferente de zero quando a execução falha

if claude -p "Rode os testes e corrija o que quebrou" --allowedTools "Read,Edit,Bash" --permission-mode dontAsk; then
  echo "ok"
else
  status=$?
  if [ "$status" -eq 143 ]; then
    echo "interrompido por SIGTERM"
  else
    echo "falhou com status $status"
  fi
fi

O 143 tem nome e sobrenome: se a execução <code>claude -p</code> for interrompida com SIGTERM, o Claude Code aborta o turno em andamento, encerra a árvore de processos de qualquer comando Bash rodando, executa os hooks SessionEnd e sai com 143

O erro comum deste passo: tratar todo código diferente de zero como bug do agente

Interrupção do orquestrador não é bug 🙂

  1. Capture o id da sessão e use <code>–resume</code> quando o script toca várias conversas
claude -p "..." --resume <session-id>

Dá pra guardar o id da sessão e retomar aquela conversa específica depois, o que é o que segura um script em lote que precisa continuar de onde parou em vez de começar do zero a cada rodada

Modos de permissão do Claude Code: qual deixa o script andar sozinho

Aqui é o coração da coisa

O modo de permissão é o que define o comportamento quando aparece uma ação que ninguém pré-aprovou

Modo O que passa sem humano O que continua barrado Uso em pipeline
auto mode Quem revisa a ação antes de executar não é você, é um segundo modelo classificador. É o modo padrão de novas sessões nos planos Pro, Max e Team desde 14 de agosto de 2026 A revisão é do classificador, e a própria Anthropic diz que ele pega cerca de 83% dos comportamentos exagerados antes de rodarem Serve como padrão do dia a dia, mas a decisão fica com o classificador, não com a sua allowlist
acceptEdits Escreve arquivos sem perguntar e auto aprova comandos comuns de sistema de arquivos como <code>mkdir</code>, <code>touch</code>, <code>mv</code> e <code>cp</code> Outros comandos de shell e requisições de rede continuam precisando de entrada em <code>–allowedTools</code> ou de uma regra <code>permissions.allow</code> Bom quando o trabalho é editar arquivo no repositório e nada além disso
dontAsk Só roda o que bate com <code>permissions.allow</code>, comandos Bash somente leitura e chamadas aprovadas por um hook PreToolUse Nega automaticamente TODA chamada de ferramenta que normalmente abriria um prompt (disponível apenas na CLI, com <code>⏵⏵ don’t ask on</code> na barra de status) É o modo que a doc posiciona pra CI e ambientes restritos, onde a sessão nunca espera input
bypassPermissions Pula todos os prompts de aprovação Nenhum prompt sobra, já que ele não pergunta nada Trate como o modo sem ponto de parada declarado

Repare na diferença de filosofia entre os dois extremos da tabela

O <code>dontAsk</code> é fail-closed por natureza: dúvida vira "não"

O <code>bypassPermissions</code> é o oposto: ele simplesmente não pergunta

E se a dúvida ainda for sobre o dia a dia interativo, quando aprovar e quando negar cada pedido do agente é uma conversa separada dessa aqui

Qual modo escolher quando ninguém está no terminal

Veredito direto: pra um agente travado, a combinação que a doc recomenda é lista de ferramentas permitidas MAIS <code>permissionMode: "dontAsk"</code>

O raciocínio é bonito de simples: o que está na lista é aprovado, o resto é negado de saída em vez de virar pergunta

Num script, pergunta sem destinatário é o mesmo que processo parado, então negar é literalmente mais útil do que perguntar

claude -p "Atualize o CHANGELOG com os commits desta branch" \
  --allowedTools "Read,Edit" \
  --permission-mode dontAsk

E o auto mode nessa história?

Ele tem um contexto numérico que vale conhecer: segundo a Anthropic, os usuários aceitam 93% dos prompts de permissão do Claude Code, e foi esse dado que motivou a criação do modo

Faz sentido, né? Se 93 de cada 100 pedidos terminam em "sim", o prompt vira ruído e o humano vira carimbo

Só que a mesma Anthropic diz que o auto mode pega cerca de 83% dos comportamentos exagerados do agente antes que eles rodem

Ou seja: é bom, é MUITO melhor que nada, e não é proteção total

Pra pipeline, isso importa: você troca uma allowlist que você escreveu (e consegue auditar) por um julgamento de modelo

Já o <code>bypassPermissions</code> deixa de ser atalho e vira aposta no momento em que o script roda em máquina com configuração herdada, mexe fora do diretório de trabalho ou toca rede

Nesse cenário, "pular todos os prompts" quer dizer que não sobra nenhum ponto de parada declarado

Se você quer fixar o padrão em vez de repetir flag em todo comando, dá pra usar o campo <code>defaultMode</code> dentro de <code>permissions</code> no arquivo de settings:

{
  "permissions": {
    "defaultMode": "acceptEdits"
  }
}

Esse é o exemplo que a doc traz, com <code>acceptEdits</code> no valor, então não leia ele como recomendação pro seu caso

Pro cenário deste post o valor ali é outro: troque <code>acceptEdits</code> por <code>dontAsk</code> e o padrão da sessão passa a ser o modo que a gente escolheu na seção anterior, sem repetir flag em toda chamada

E tome cuidado com a hierarquia, porque ela decide quem ganha: enterprise settings vem primeiro, depois <code>.claude/settings.local.json</code> (local do projeto), depois <code>.claude/settings.json</code> (projeto) e por último <code>~/.claude/settings.json</code> (usuário)

Já me ferrei mais de uma vez achando que o arquivo de usuário mandava em tudo…

O que muda na segurança quando o humano sai do meio

Quando você aprova cada passo, você É a última linha de defesa

Tira o humano e essa linha passa a ser a configuração, só isso

A doc de segurança lista defesas do Claude Code contra injeção de prompt que continuam valendo:

  • aprovação obrigatória por padrão para ferramentas que fazem requisições de rede
  • janela de contexto isolada no web fetch
  • detecção de injeção de comando: comando bash suspeito exige aprovação manual mesmo se já estava na allowlist
  • matching fail-closed: comando não reconhecido cai em aprovação manual

Se liga no detalhe: várias dessas defesas terminam em "aprovação manual"

Num script, aprovação manual é exatamente o que não existe, então esses caminhos viram parada ou negação, dependendo do modo que você escolheu

No auto mode existe uma camada a mais: uma sonda do lado do servidor varre os resultados das ferramentas atrás de conteúdo suspeito, e o classificador de aprovação nunca vê os resultados das ferramentas, justamente pra que texto injetado não influencie a decisão dele

A confiança do diretório é o que liga o resto

O Claude Code mostra um diálogo de confiança do diretório antes de carregar a configuração

É essa confiança que libera a auto instalação de plugins de marketplace e a execução de hooks definidos pelo projeto, além de fazer valer o <code>.claude/settings.json</code> e o <code>.mcp.json</code>

A aceitação fica salva por diretório de projeto, com uma exceção: no diretório home ela vale só para a sessão atual

Traduzindo pro seu pipeline: confiar num diretório não é um clique bobo, é a chave que liga a configuração daquele projeto

<code>–bare</code> pra não herdar a máquina hospedeira

Em CI ou ambientes com script, a doc indica a flag <code>–bare</code> pra iniciar o Claude Code sem carregar hooks, plugins, auto memory e <code>CLAUDE.md</code> da máquina hospedeira

claude -p "..." --bare --output-format json

É o que separa "execução reproduzível" de "funciona na minha máquina"

Mas tem um efeito colateral que precisa entrar na conta, e é aqui que as duas partes do post conversam

Lembra que no <code>dontAsk</code> uma das coisas que passam são as chamadas aprovadas por um hook PreToolUse? Se você roda com <code>–bare</code>, os hooks não são carregados, então essa via de aprovação simplesmente não existe naquela execução

Sobram as outras duas: o que bate com <code>permissions.allow</code> e os comandos Bash somente leitura

Ou seja: ou você aceita a allowlist como único caminho de aprovação naquele pipeline, ou não usa <code>–bare</code> ali

Escolha consciente, não surpresa no meio da madrugada 😀

Sandbox de Bash: onde o comando pode escrever

O sandbox de Bash é embutido no Claude Code e roda em macOS, Linux e WSL2

No macOS não tem nada pra instalar, o isolamento usa o framework Seatbelt do sistema

Em Linux e WSL2 ele depende dos pacotes <code>bubblewrap</code> (isolamento de sistema de arquivos) e <code>socat</code> (relay de rede)

Windows nativo não é suportado, e a orientação é rodar dentro de uma distribuição WSL2

Com o sandbox ativo, por padrão os comandos não conseguem modificar arquivos fora do diretório de trabalho atual e do diretório temporário da sessão sem permissão explícita, o que inclui arquivos de configuração do shell como <code>~/.bashrc</code> e binários de sistema em <code>/bin</code>

E a doc é bem clara num ponto: sandbox eficaz exige isolamento de sistema de arquivos E de rede juntos, porque sem isolamento de rede um agente comprometido poderia exfiltrar arquivos sensíveis como chaves SSH

Isolamento de rede TLS-aware mais forte é área em desenvolvimento

Um último ponto pra quem administra time: nos planos Team e Enterprise o auto mode vem disponível por padrão, e o administrador pode desligar pra organização inteira nas managed settings, com <code>permissions.disableAutoMode</code> definido como <code>"disable"</code>

Onde rodar sem aprovação faz sentido (e onde não faz)

Bora aos casos concretos

CI e ambientes restritos É o cenário que a própria doc usa pra posicionar o <code>dontAsk</code>: sessão que nunca espera input, allowlist enxuta, tudo fora dela negado de saída

GitHub Actions A integração oficial é a action anthropics/claude-code-action, na versão <code>anthropics/claude-code-action@v1</code>, e ela roda no runner do próprio usuário

A autenticação aceita <code>ANTHROPIC_API_KEY</code> (chave da Claude Console) ou <code>CLAUDE_CODE_OAUTH_TOKEN</code> (token OAuth disponível nos planos Pro, Max, Team e Enterprise)

O caminho mais simples de setup é rodar <code>/install-github-app</code> dentro do Claude Code no terminal

E o app do GitHub precisa de permissões de leitura e escrita em Contents (pra modificar arquivos), Issues (pra responder issues) e Pull requests (pra criar PRs e enviar commits)

Scripts em lote Encadear sessões com <code>–resume</code> resolve o caso do processo que roda em várias etapas e precisa manter o fio da conversa entre elas

Aplicações usando o Agent SDK Ali existe o callback <code>canUseTool</code>, que dispara quando o Claude quer usar uma ferramenta que não foi aprovada automaticamente e devolve uma decisão programática:

// devolve allow ou deny de forma programática
{ behavior: "allow" }
{ behavior: "deny", message: "fora do escopo deste job" }

Detalhe importante da ordem: os hooks são avaliados ANTES do <code>canUseTool</code> e podem negar a chamada de saída

E onde não faz sentido?

Onde o script mexe fora do diretório de trabalho, toca rede ou roda numa máquina com configuração herdada de outro contexto

Nesses três, o custo de errar não compensa a comodidade de não ser perguntado

Se você ainda está decidindo qual agente usar em cada caso, essa conta de risco muda bastante de ferramenta pra ferramenta

Quando o script trava ou falha: sintomas e o que fazer

Sintoma: a execução fica parada e não sai nunca

Causa: alguma ferramenta ficou de fora da allowlist, e como uma execução <code>-p</code> não interativa não tem interface, não existe prompt pra onde escalar a decisão

Solução: <code>–allowedTools</code> mais <code>–permission-mode dontAsk</code>, ou então delegar a aprovação com <code>–permission-prompt-tool</code>

Prevenção: escreva a allowlist a partir do que o job REALMENTE faz, não do que ele talvez faça um dia

Sintoma: o agente se comporta diferente na máquina de CI

Causa: hooks, plugins, auto memory e <code>CLAUDE.md</code> da máquina hospedeira sendo carregados junto

Solução: <code>–bare</code>, lembrando que aí os hooks somem da equação e a allowlist fica sozinha no comando

Prevenção: trate o ambiente de CI como ambiente limpo por contrato, não por sorte

Sintoma: execução longa demais ou girando em loop

Causa: nada limita quantos turnos o agente pode dar

Solução: <code>–max-turns</code> mais ramificação por código de saída no script

Prevenção: teto de turnos definido junto com o prompt, não depois do primeiro susto

Sintoma: processo morto pelo orquestrador e o time achando que é bug

Causa: SIGTERM

Nesse caso o Claude Code aborta o turno em andamento, encerra a árvore de processos de qualquer comando Bash em execução, roda os hooks SessionEnd e sai com código 143

Solução: o script trata 143 como interrupção, não como falha do agente

Prevenção: documente o 143 no seu wrapper, senão alguém vai abrir issue por isso 😀

Sintoma: você precisa bloquear uma chamada específica que a allowlist não cobre bem

Causa: allowlist é lista, não é lógica

Solução: hook PreToolUse

Um hook que sai com código 2 bloqueia a chamada da ferramenta, e o texto escrito em <code>stderr</code> volta como feedback pro Claude

Pra controle mais fino, o hook sai com código 0 e imprime um JSON no <code>stdout</code> com a decisão (bloquear, permitir ou escalar)

Tome cuidado com dois detalhes: o Claude Code só processa o JSON quando o código de saída é 0 (JSON impresso junto com exit 2 não vira decisão estruturada), e hook nenhum vale se aquela execução subiu com <code>–bare</code>

Conclusão

Rodar o Claude Code headless não é decisão de coragem, é decisão de configuração

A sequência é essa: define a allowlist, escolhe o modo de permissão, isola o ambiente e trata o código de saída

O caminho conservador que a doc aponta pra agente travado continua sendo <code>–allowedTools</code> mais <code>dontAsk</code>, com <code>–bare</code> quando o ambiente não é seu (e ciente de que ali não tem hook pra te salvar) e sandbox quando o comando pode escrever onde não devia

Próximo passo prático: monte uma execução mínima num repositório de teste antes de plugar no pipeline de verdade

claude -p "Liste os arquivos alterados e explique o que mudou" \
  --output-format json \
  --allowedTools "Read" \
  --permission-mode dontAsk \
  --max-turns 5

Rodou, saiu JSON, exit 0? Então você já tem a peça que o script vai chamar, e pode ir soltando a coleira ferramenta por ferramenta, com consciência do que cada uma libera

Essa área muda rápido (modo padrão, sandbox, defesas), então aqui no Hora de Codar a gente vai voltando no assunto conforme a doc oficial mexer

até o próximo post!

Perguntas frequentes

Dá para integrar o Claude Code headless com o GitHub Actions?

Dá, a integração oficial é a action anthropics/claude-code-action@v1, que roda no runner do próprio usuário. A autenticação aceita ANTHROPIC_API_KEY (chave da Claude Console) ou CLAUDE_CODE_OAUTH_TOKEN (disponível nos planos Pro, Max, Team e Enterprise), e o caminho mais simples de setup é rodar /install-github-app dentro do Claude Code no terminal. O app do GitHub precisa de permissões de leitura e escrita em Contents, Issues e Pull requests.

O sandbox de Bash do Claude Code funciona em execução headless no Windows?

Não de forma nativa. O sandbox é embutido no Claude Code e roda em macOS, Linux e WSL2, e a orientação para quem está no Windows é rodar dentro de uma distribuição WSL2. No macOS o isolamento usa o Seatbelt do próprio sistema, sem nada pra instalar; em Linux e WSL2 ele depende dos pacotes bubblewrap e socat.

Como rodar o Claude Code em CI sem carregar hooks, plugins e CLAUDE.md da máquina?

Usando a flag –bare, indicada pela doc justamente para CI ou ambientes com script. Com ela o Claude Code inicia sem carregar os hooks, plugins, auto memory e CLAUDE.md da máquina hospedeira, ou seja, roda isolado do que está configurado localmente. Só lembre do efeito colateral: sem hooks carregados, um hook PreToolUse não aprova nada naquela execução, então no modo dontAsk sobram como caminho de liberação o que bate com permissions.allow e os comandos Bash somente leitura.

O que acontece se eu rodar claude -p sem configurar –permission-prompt-tool?

Existe a flag –permission-prompt-tool justamente pra delegar a decisão de aprovação a outro lugar. Numa execução -p não interativa sem essa flag configurada, não tem prompt para onde escalar a decisão, então quem monta o script precisa resolver isso via –allowedTools e –permission-mode antes de rodar, não durante.

Um hook PreToolUse consegue bloquear uma chamada mesmo com –allowedTools liberando ela?

Consegue. Um hook PreToolUse que sai com código 2 bloqueia a chamada da ferramenta, e o texto escrito em stderr volta como feedback pro Claude. Pra controle mais fino, o hook sai com código 0 e imprime um JSON no stdout com a decisão, mas o Claude Code só processa esse JSON quando o código de saída é 0. Vale o aviso: se aquela execução subiu com –bare, os hooks não são carregados, então esse controle não vale ali.

Dá para retomar uma execução do Claude Code headless que parou no meio do script?

Dá, capturando o id da sessão e usando –resume <session-id> na chamada seguinte. É o que segura um script em lote que precisa continuar de onde parou em vez de começar do zero a cada rodada, principalmente quando o script toca várias conversas diferentes.



Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares