ekky
Um daemon que gera código para minhas tasks no background, cada uma na sua worktree, e me deixa branches pra revisar.
- status
- ativo
- stack
- Go / Svelte 5 / Claude Code
- tópicos
- software-engineering / ai
O ekky é o harness que eu uso pra passar tasks de código para um agente sem ficar olhando ele trabalhar. Ele recebe uma lista de tasks, roda uma sessão headless do Claude Code em cada uma, cada sessão na sua própria worktree do git, e no fim deixa uma branch local e um resumo curto do resultado. Nada me notifica, e nada sai da minha máquina até eu mandar. A ideia é sentar e encontrar cinco branches esperando revisão, em vez de cinco terminais esperando por mim.
O porquê, e do que eu desisti no caminho, está no post. Esta página é a outra metade: o que ele é e como funciona por dentro.
Da direção até a branch
flowchart LR accTitle: O pipeline do ekky accDescr: Uma direção decidida, por mim; decisões e tasks, por mim com o Claude; o drain, pelo ekky, uma worktree por task; revisão e promote, por mim. dir["Uma direção decidida<span class='who'>eu</span>"]:::eu cut["Decisões + tasks<span class='who'>eu + Claude</span>"]:::par run["O drain<span class='who'>ekky, uma worktree por task</span>"] rev["Revisão, depois promote<span class='who'>eu</span>"]:::eu dir --> cut --> run --> rev
Tudo começa com uma direção que eu já decidi: uma RFC, um design doc, uma página de anotações. O ekky:stage me sabatina sobre ela, procurando cada ponto em que um agente headless teria que chutar, e depois escreve tudo com duas seções no final: ## Decisions e ## Tasks, uma task por PR futuro. O ekky:decompose transforma essas seções em um task.md por task: o que significa estar pronto, as decisões que já tomei e a única coisa que o agente não pode fazer. Uma task que depende de outra declara isso com after:.
O ekky tackle enfileira as tasks e inicia o drain, que roda as que estão prontas algumas de cada vez. Depois, o ekky:review me mostra o resumo e o diff da próxima task terminada, e eu aceito ou corrijo na mão. O ekky:promote renomeia as branches, faz o push e abre os PRs. É o único passo que encosta em qualquer coisa fora da minha máquina.
Como funciona por dentro
O dispatcher é um binário Go só com a biblioteca padrão. O drain é um serviço oneshot do systemd de usuário, que fica em loop até não sobrar nada pronto. Cada tentativa é um processo claude no seu próprio process group, com a task no stdin, um system prompt que obriga o agente a escrever a própria spec antes de mexer no código, um limite de 90 minutos e um orçamento em dólar1.
Não tem banco de dados. O estado é uma pasta, com uma subpasta por task: o task.md, que só eu edito, um arquivo status com uma palavra só, o result.json do agente e o log dele. Todo comando reconstrói a visão a partir desses arquivos a cada chamada, então quando algo quebra eu consigo descobrir o que aconteceu.
O agente não é dono de nada além dos próprios commits. Ele não consegue fazer push: não tem credencial do GitHub, a URL de push dele aponta pra /nonexistent, tem um hook de pre-push atrás disso, e ele roda sem servidores MCP. Ele também não faz perguntas. Quando a task deixa algo em aberto, ele escolhe a opção em que apostaria, implementa, e anota o que rejeitou e por quê. Ele só pode parar e perguntar quando a resposta jogaria fora mais da metade do trabalho. Uma tentativa que falhou nunca é retomada: a próxima começa do zero, com o final do log anterior anexado à task.
Eu fico em três lugares: nas decisões antes do corte, na revisão de cada diff e no promote. Uma correção que eu faço na mão move só a branch dela, e o review faz o rebase da task seguinte em cima dela quando eu abro essa, um salto de cada vez. Uma interface web, só leitura e só em localhost, mostra o backlog agrupado por quem cada task está esperando: eu, a IA ou ninguém.
Um exemplo
A divisão inglês/português deste blog passou pelo ekky. Uma RFC virou cinco tasks em cadeia, de ekky/i18n-01 a ekky/i18n-05, cada uma cortada da branch da anterior. A parte do agente levou uns 26 minutos, somando as cinco. A minha parte foi ler os diffs.

O que ainda precisa melhorar
- Ele assume que o usuário roda Linux (+ systemd), possui a CLI do Claude Code e o trabalha da mesma forma que eu. O código está publicado pra ser lido, não instalado, e não tem licença.
- Dependências são cadeias, não grafos. Uma task pode esperar por exatamente uma outra, por simplicidade.
- Se um comentário na revisão muda a própria RFC e invalida branches que já existem, não tem caminho de retrabalho. Existia um caminho, mas na prática, o retrabalho quase nunca era viável - era mais fácil descartar o que foi feito.
- O agente só tem as ferramentas nativas: nada de LSP, nada de navegador. Em trabalho de UI, conferir se ficou certo continua sendo comigo. Existe espaço para otimizar consumo de token e engenharia de contexto.
- As correções que faço na mão ficam registradas na task, mas transformar isso em diretrizes melhores pra próxima execução (novos guardrails e regras dentro dos repos nos quais o agente trabalhou) eu ainda faço na mão, quando lembro.
- Esse orçamento não é lá tão útil, pois atualmente tenho acesso a assinaturas 5x Max da Anthropic e raramente me faltam tokens. De qualquer forma, desenvolvi o orçamento para caso algum dia eu precise.↩