Automatizando fluxos de trabalho inteiros com o ralph-starter
O ralph-starter roda loops no estilo Ralph Wiggum — busca uma spec, executa o agente de IA, checa testes/lint/build, devolve os erros, repete. Como funciona e por que eu construí.
O que é o ralph-starter
O ralph-starter é uma CLI que roda agentes de codificação com IA em loops autônomos. Você dá uma tarefa a ele (ou uma issue do GitHub, um ticket do Linear, uma página do Notion), ele executa o agente e em seguida checa testes, lint e build. Se algo falhar, ele devolve o erro e faz o loop de novo. Quando tudo passa, ele faz commit, push e abre um PR.
Ele funciona com os agentes que você já tem instalados: Claude Code, Cursor, Codex CLI, OpenCode, OpenClaw e Amp, além de dois agentes baseados em SDK (Anthropic SDK e OpenCode SDK). Ele detecta automaticamente o que está na sua máquina com probes de --version e usa o primeiro que responder.
É open source, licenciado sob MIT. Eu construí porque estava cansado de ser o intermediário entre o meu terminal e a minha janela de chat com a IA.
Por que eu construí
Eu usava assistentes de codificação com IA todos os dias e o fluxo de trabalho era sempre o mesmo: ler um ticket, codar, travar, colar contexto no chat, adaptar a sugestão, colar de volta, rodar os testes, colar o erro, receber um fix, colar de volta. O lint reclama. Mais uma viagem de ida e volta. Aí commit, push, abrir um PR.
São umas 12 etapas, de 5 a 8 vezes por dia. A IA estava fazendo a parte difícil (escrever o código) e eu era o relay movendo texto entre janelas. Uma área de transferência humana.
Então eu escrevi um script que faz esse relay. Ele pega uma spec, envia para o agente, roda minha suíte de testes e manda a saída de erro de volta quando algo falha. Esse script virou o ralph-starter.
Onde é mais útil
O ralph-starter funciona melhor quando você tem:
- Uma spec clara. “Adicionar um endpoint
/healthque retorna 200 com o body JSON{ status: 'ok' }” termina em 1 loop. “Melhorar o app” ainda vai rodar, o agente vai analisar o seu codebase e escolher algo para melhorar, mas pode levar 4 loops e o resultado pode não ser o que você queria. - Testes. O loop precisa de algo para validar. Se você não tem testes, o agente não sabe quando terminou.
- Trabalho de implementação rotineiro. Endpoints, correção de bugs, atualização de componentes, mudanças de configuração. Aquele tipo de coisa que enche um backlog de sprint.
Specs vagas não quebram o ralph-starter, elas só custam mais. “Refatorar o sistema de auth” faz o agente tentar uma abordagem diferente a cada loop até o circuito breaker disparar. “Adicionar middleware JWT em src/middleware/auth.ts usando bcrypt, cookies httpOnly, com testes para login com sucesso e com falha” termina em 2 loops porque o agente sabe exatamente como é o “pronto”. Eu faço o raciocínio e a escrita da spec. O ralph-starter cuida da tradução de spec para código.
Como começar
Você pode começar a partir de uma ideia e o ralph-starter gera a spec para você, ou apontá-lo para uma issue existente do GitHub ou um ticket do Linear e ele busca a spec automaticamente:
# Install and initialize
npx ralph-starter init
O ralph-starter init configura os arquivos do Ralph Playbook: AGENTS.md (instruções do agente e comandos de validação), PROMPT_plan.md, PROMPT_build.md, IMPLEMENTATION_PLAN.md e uma pasta specs/. Se esses arquivos já existirem, o wizard os detecta e oferece continuar o loop de build em vez disso:
Rode sua primeira tarefa com uma spec inline:
ralph-starter run "add a /ping endpoint that returns pong" --commit
Ou aponte para uma issue do GitHub ou um conjunto filtrado de tickets do Linear. Note que --issue é exclusivo do GitHub; para o Linear você filtra por projeto e label:
# From GitHub
ralph-starter run --from github --project rubenmarcus/ralph-starter --issue 2
# From Linear
ralph-starter run --from linear --project "Mobile App" --label "sprint-1" --commit --pr
Para conectar GitHub, Linear, Notion ou Figma como fontes de spec, use os comandos de configuração. As credenciais ficam em ~/.ralph-starter/sources.json; as variáveis de ambiente (GITHUB_TOKEN, LINEAR_API_KEY, NOTION_API_KEY, FIGMA_TOKEN) têm precedência:
ralph-starter config set github.token ghp_xxx
ralph-starter config set linear.apiKey lin_api_xxx
ralph-starter config set notion.token secret_xxx
ralph-starter setup configura as preferências de agente, e ralph-starter integrations test github verifica a conectividade antes de você torrar tokens em uma execução.
Como o loop funciona
O executor do loop (runLoop em src/loop/executor.ts) segue esta sequência:
1. Fetch spec (GitHub issue, Linear ticket, Notion page, inline text, file, URL)
2. Create branch (auto/github-145)
3. Run agent with the spec as prompt
4. Run validations: test → lint → build
5. If any validation fails → feed error output back to agent → go to step 3
6. If all pass → commit, push, open PR
Não há arquivo de configuração para os comandos de validação. O ralph-starter os detecta. Ele faz parse do seu AGENTS.md procurando comandos entre crases após os itens de test/lint/build, e faz fallback para os scripts do package.json:
<!-- AGENTS.md -->
- **Test**: `pnpm test`
- **Lint**: `pnpm lint`
- **Build**: `pnpm build`
Quando uma validação falha, o stderr/stdout bruto vira lastValidationFeedback e é injetado no prompt da próxima iteração pelo construtor de contexto, junto com o resumo da spec, a tarefa atual do plano e os últimos registros do log de iteração. O agente vê TypeError: Cannot read property 'id' of undefined at src/routes/user.ts:42 e sabe exatamente o que corrigir. Ele não recebe um resumo da falha. Ele recebe a falha.
Um detalhe que a maioria das pessoas perde: no modo auto em lote, o loop pula comandos de teste e roda só build e lint. Isso é deliberado, para que um teste preexistente que esteja falhando não prenda toda tarefa num loop de correção por um bug que ela não introduziu.
O loop tem sete motivos de saída: completed, file_signal (um marcador RALPH_COMPLETE ou .ralph-done, ou todas as caixas marcadas em IMPLEMENTATION_PLAN.md), circuit_breaker, rate_limit, cost_ceiling, blocked e max_iterations. Você sempre sabe por que uma execução terminou.
Exemplo real: construindo uma landing page a partir de uma issue do GitHub
Aqui está uma execução real. Apontei o ralph-starter para uma issue do GitHub pedindo uma landing page para uma pet shop em Londres. A spec tinha 8 tarefas (header, hero, serviços, galeria, depoimentos, formulário de contato, footer, polishing).
O ralph-starter detectou 28 skills instaladas (frontend-design, tailwind, responsive-web-design, etc.) e injetou as relevantes no prompt, limitadas a 5 ativas por iteração para que o prompt não se afogasse em instruções:
O loop rodou por 2 iterações. A primeira completou 5 de 8 tarefas, a segunda pegou o restante (Depoimentos, Formulário de Contato, Footer, Polishing). A detecção de stall fica de olho em iterações sem mudanças de arquivos, sem progresso de tarefas e sem atividade de validação, e para depois de 3 dessas seguidas (4 para planos com mais de 5 tarefas).
Resultado final:
Cost Summary:
Tokens: 47.0K (764 in / 46.2K out)
Cost: $0.606 ($0.348/iteration avg)
Loop completed!
Exit reason: completed
Iterations: 2
Total duration: 8m 19s
Total cost: $0.696 (47.0K tokens)
8 minutos. 69 centavos. Uma landing page completa com componentes React, estilização em Tailwind e layout responsivo. Eu não cheguei a abrir o editor.
Custos de token e como mantê-los baixos
Aqui estão meus números reais. Rastreei meu janeiro inteiro. 187 tarefas concluídas. $22,41 no total. Média de $0,12 por tarefa.
O motivo de ser barato é o prompt caching. Com o Claude Code, o primeiro loop envia o contexto completo a $3,00 por milhão de tokens de entrada. Os loops 2, 3, 4 reutilizam os tokens em cache a $0,30 por milhão. Isso é 90% a menos.
Antes de cada execução, o ralph-starter mostra uma estimativa para você saber o que esperar:
Depois de cada execução ele mostra o breakdown real: tokens de entrada, tokens de saída, custo por iteração. Para um limite duro, --max-cost 2 mata o loop em $2 e --rate-limit 50 limita as chamadas de API por hora. A maioria das tarefas termina em 2 a 3 loops, e depois do primeiro a maior parte da entrada está em cache. Breakdown detalhado com números exatos aqui.
O que mantém os custos baixos: specs boas (menos loops), prompt caching (90% de desconto nos tokens de entrada depois do loop 1), o circuit breaker (nenhum dinheiro torrado em tarefas insolúveis) e skills (o agente acerta em menos iterações).
Modo batch: 10 issues, 8 PRs
Durante o sprint grooming eu etiqueto os tickets bem definidos como “auto-ready”. Aí eu rodo um único comando e vou almoçar:
O ralph-starter pega todas as issues correspondentes e inicia o loop em cada uma:
# From GitHub
ralph-starter auto --source github --project multivmlabs/ralph-starter --label "auto-ready" --limit 10
# From Linear
ralph-starter auto --source linear --project ENG --label "auto-ready" --limit 10
# Preview without executing, or run tasks in parallel worktrees
ralph-starter auto --source github --project multivmlabs/ralph-starter --dry-run
ralph-starter auto --source github --project multivmlabs/ralph-starter --parallel --concurrency 3
Cada issue recebe sua própria branch, seu próprio loop, seu próprio PR. As branches seguem uma convenção única, auto/<source>-<id>:
[1/10] Issue #145: Add health check endpoint
> Branch: auto/github-145
> 2 loops > Validation: passed
> PR #151 created
[2/10] Issue #147: Add rate limit headers
> Branch: auto/github-147
> 1 loop > Validation: passed
> PR #152 created
[3/10] Issue #150: Improve performance
> 3 loops > Circuit breaker tripped. Skipping.
...
Completed: 8/10 | Failed: 2/10
Total cost: $1.84
8 de 10. As 2 falhas foram tickets vagos. “Improve performance” não tinha métricas nem metas, então o agente tentava otimizações diferentes a cada loop sem nada para validar contra, e o circuit breaker disparou após 3 loops. A outra era um ticket de refatoração que referenciava uma discussão de reunião de time que o agente nunca viu.
O circuit breaker dispara após 3 falhas consecutivas ou 5 repetições do mesmo erro. “Mesmo erro” não é uma comparação de string: a mensagem é normalizada (números de linha, timestamps, endereços hex, stack frames removidos) e hasheada, então foo.ts:12:4 e foo.ts:87:21 contam como uma única falha. Após um disparo há um cooldown de 30 segundos com uma retry permitida, e então o loop para. Ajuste ambos com --circuit-breaker-failures e --circuit-breaker-errors.
Modo swarm: mesma tarefa, três agentes
Um loop é uma aposta. O modo swarm faz várias:
ralph-starter run "rewrite the date parser" --swarm --strategy race
ralph-starter run "rewrite the date parser" --swarm --strategy consensus
ralph-starter run "migrate to ESM" --swarm --strategy pipeline
Cada agente roda o loop completo em seu próprio git worktree sob .ralph/worktrees/, então ninguém sobrescreve ninguém. Em race, o primeiro loop bem-sucedido vence. Em consensus, todos os agentes terminam e o vencedor é a execução bem-sucedida com menos iterações. Em pipeline, os agentes rodam sequencialmente em um worktree compartilhado, o trabalho vai sendo commitado entre estágios, e o último agente revisa e faz o polishing. A branch vencedora vira um PR com uma tabela de custo e iterações por agente no corpo. Escrevi um post completo sobre os internals de como o swarm funciona.
Escolhendo um agente
Você pode ser explícito sobre qual agente usar:
ralph-starter run "your task" --agent claude-code
ralph-starter run "your task" --agent codex
ralph-starter run "your task" --agent cursor
Eu uso o Claude Code diariamente porque o prompt caching deixa os loops mais baratos e a saída stream-json permite ao ralph-starter acompanhar o progresso em tempo real. Mas o executor do loop e o pipeline de validação são idênticos para todos os agentes. Rodei a mesma tarefa de auth JWT em 4 agentes diferentes e todos chegaram lá, só que com contagens de loop e custos diferentes.
Por que eu continuo construindo
Fiz uma comparação lado a lado de 12 tarefas da mesma sprint. 6 manuais, 6 com o ralph-starter. As tarefas com ralph-starter tiveram em média 12 minutos da minha atenção contra 45 minutos codando manualmente. A qualidade do código foi comparável.
Agora eu gasto meu tempo em três coisas: escrever specs claras (a entrada), revisar PRs (a saída) e decisões de arquitetura (a parte que a IA não consegue fazer). O ralph-starter cuida de tudo que está entre elas. Todo PR que ele produz passa em testes, lint e build. Quando eu codo manualmente, às vezes eu pulo testes para mudanças pequenas. O loop não deixa o agente pular nada, e sinceramente essa disciplina é melhor que a minha.
Sobre o nome
O nome vem da técnica Ralph Wiggum. Você dá uma tarefa à IA e a deixa seguir até terminar. Sem microgerenciamento. Explicação completa aqui.
Links
O ralph-starter é open source, licenciado sob MIT.
Posts relacionados:
- The Ralph Wiggum technique
- Specs are the new code
- I tried 5 AI coding agents on the same task
- Prompt caching saved me $47
- ralph-starter vs doing it manually
- Figma to code in one command
- ralph-starter with Linear
Se você testar, abra uma issue ou deixe uma star. Todo feedback é bem-vindo.