Pitch: Como conectei o browser a três agentes de IA diferentes (Claude Code, Codex e opencode)
Passei as últimas semanas construindo uma ferramenta chamada kolaby. Quero compartilhar as decisões técnicas e principalmente os problemas que só apareceram rodando, caso seja útil para alguém.

O problema
Trabalhando com agente de IA no front-end, todo pedido virava a mesma dança de três passos:
- tirar print do elemento
- caçar em qual arquivo aquilo mora
- escrever "aquele botão do card de preço, o segundo"
O agente é ótimo editando código e cego para a tela. Eu era o cabo de rede entre os dois.
A ideia: carimbar o JSX antes do build
A parte central é mais simples do que parece. Um plugin do Vite (e um pre-loader do webpack, no Next) intercepta cada .tsx antes do transform e carimba cada tag com a posição dela no código-fonte:
// você escreve
<button className="cta">Comprar</button>
// o browser recebe
<button className="cta" data-kolaby-loc="src/Card.tsx:14:5">Comprar</button>
Aí é só elementFromPoint no hover. Sem heurística, sem source map, sem adivinhação: o atributo carrega arquivo:linha:coluna.
Duas coisas que aprendi aqui:
Só carimbo tag intrínseca. Se você carimbar <Card />, o atributo vira uma prop do componente e o React reclama. Só tag minúscula recebe.
@babel/traverse quebrou com aquele problema clássico de duplo default em ESM (traverse is not a function). Escrevi um walker próprio sobre o @babel/parser e removi a dependência. Menos código do que a gambiarra de interop.
Para os detalhes que o atributo não tem — props, estado, árvore de componentes — leio a fiber do React direto do nó DOM (__reactFiber$..., _debugOwner). É API interna, então tudo com fallback.
A parte difícil: entregar numa conversa já aberta
Capturar foi fácil. O problema real era não fazer você digitar nada.
A primeira versão spawnava um agente novo por pedido. Funciona, mas é péssimo: processo invisível, zero contexto, e você não conversa com ele. O que eu queria era o pedido cair na conversa que já está aberta no terminal.
Cada agente resolve isso de um jeito completamente diferente:
Claude Code tem SendMessage, uma ferramenta de mensagem entre sessões. O dev server sobe um claude -p descartável que serve só de carteiro.
Codex tem codex queue --thread <id> --message <texto>, que fala com um daemon local. Eu tinha assumido que não dava, e o usuário me forçou a procurar melhor. Dava.
opencode é HTTP: POST /session/:id/prompt_async. O detalhe cruel foi achar a instância — a documentação diz que o TUI abre servidor em porta aleatória. Testei: não abre nenhuma. Só conexão de saída. Descoberta automática ainda é proposta em aberto no repositório deles, então não há arquivo de registro para ler.
A saída não é varrer portas. É perguntar ao SO quais portas os processos chamados opencode têm em escuta, e então perguntar a cada instância quais conversas ela tem. Como cada conversa carrega o próprio directory, várias instâncias abertas se resolvem sozinhas.
Mostrar o progresso sem controlar o processo
O trabalho acontece no terminal do usuário, num processo que o dev server não controla. Mas os três gravam a conversa em disco ou expõem stream, então dá para acompanhar — e cada um sinaliza o fim de um jeito:
| Agente | Fim do turno |
|---|---|
| Claude Code | não sinaliza — inferido do silêncio |
| Codex | task_complete explícito no rollout |
| opencode | status da sessão volta a nulo |
Windows cobrou caro
Quatro bugs que só existem lá, todos com sintoma enganoso:
spawn ENOENT com o programa instalado. O where codex devolve primeiro o shim do Git Bash — um arquivo sem extensão — e só depois o codex.cmd. O CreateProcess só executa extensão conhecida, então o erro sai como "não existe". O claude escapava por acaso: instala um .exe.
.cmd exige shell: true desde a correção do CVE-2024-27980. Aí você descobre que o shell do Windows não escapa argumento nenhum.
JSON.stringify parece resolver as aspas e não resolve. Ele dobra toda barra invertida, e o Windows só trata barra como escape antes de aspa. O caminho chegava no agente como D:\\projeto\\... e ele não abria o arquivo. Só peguei isso lendo o que de fato entrou na fila do Codex.
O cmd.exe corta a linha de comando em 8191 caracteres. O relatório completo (estilos computados, árvore, HTML) estoura sozinho. A solução foi gravar o relatório em arquivo e mandar uma frase apontando para ele.
O bug que mais me ensinou
Uma seleção já executada por um agente voltava a ser entregue para outro que aparecesse depois. Dois agentes editando o mesmo arquivo ao mesmo tempo.
O campo que evita isso existia — mas só era escrito por um dos caminhos de entrega. Sistema com três produtores e um consumidor: se nem todos marcam, o controle não vale nada.
Números
- 3 agentes suportados, 3 mecanismos de entrega distintos
- Vite e Next.js (webpack e Turbopack)
- 111 testes
- ~100 KB o pacote
- 2 idiomas na interface (PT-BR e EN)
- desfazer e refazer por snapshot de arquivo, porque agente erra
Status
Está no npm como kolaby. É gratuito para usar, inclusive comercialmente — o código não é aberto.
npm i -D kolaby
Aceito feedback, principalmente de quem usa agente de IA para front-end no dia a dia. E se alguém souber de um jeito melhor de descobrir a instância do opencode, me conta.