Visão geral dos pacotes
Struct principal: ChatCLI
A structChatCLI em cli/cli.go é o coração do sistema. Seus campos mais importantes estão organizados por responsabilidade:
LLM e Provider
Histórico e Compactação
Controle de Execução
Subsistemas
Estado Auxiliar
Fluxo de inicialização
O diagrama abaixo mostra a sequência completa de boot, demain.go até o loop interativo:
detectProjectDir()
Caminha do diretório atual até a raiz procurando marcadores de projeto:.agent/(marcador explícito do ChatCLI) — prioridade.git/(convencao comum)
"" se nenhum marcador for encontrado.
Fluxo de mensagens: modo chat
No modo interativo normal, cada mensagem do usuário segue este pipeline:Type-ahead (messageQueue)
Mensagens digitadas enquanto o LLM processa são armazenadas em uma fila FIFO (messageQueue). Após cada resposta, o sistema drena a fila e processa cada mensagem sequencialmente, sem necessidade de re-digitação. O modo coder tem sua própria fila com indicador visual em tempo real (▼ N msg fila) quando há mensagens enfileiradas.
Input guard (anti-typeahead em prompts de segurança)
Distinto do typeahead “bom” acima, quando uma security box aparece no meio de um turn — três camadas descartam input em queue para evitar consumir bytes comoy/n:
- TTY flush —
TCIFLUSH(Linux),TIOCFLUSH(BSD/Darwin),FlushConsoleInputBuffer(Windows). - Drain channel — esvazia o buffer non-blocking do reader.
- 250ms debounce — descarta input nos primeiros 250ms após a box renderizar.
stty sane no /dev/tty controlador para recuperar de um teardown anterior do go-prompt que pudesse ter deixado o terminal em raw mode.
Fluxo de mensagens: modo Agent/Coder
A entrada no modo agente usa um mecanismo depanic/recover para sair do loop do go-prompt:
Anchor Reminder
A cada turno, o agente injeta um lembrete curto no histórico para manter o LLM focado na tarefa original. Isso evita drift em conversas longas.Parsing de tool calls
O parser emcli/agent/toolcall_parser.go usa um scanner stateful (não regex) para robustez:
Formatos suportados
Algoritmo do scanner
- Busca
<tool_callcase-insensitive - Verifica que o próximo char é whitespace ou
>(não parte de outra tag) scanTagEnd(): avança respeitando aspas (single e double quotes)- Dentro de aspas,
>é tratado como texto literal - Suporta entidades HTML (
>,", etc.)
- Dentro de aspas,
- Extrai atributos
nameeargsindependente da ordem - Se self-closing falha, tenta paired tags com
</tool_call> - Fallback para JSON: tenta
parseJSONToolCalls()em paralelo
Por que não regex?
Argumentos de tools frequentemente contêm>, ", e JSON aninhado. Regex não consegue distinguir > dentro de um atributo quoted de > que fecha a tag. O scanner stateful resolve isso rastreando o estado de quote.
Pipeline de sanitização de argumentos
Após o parsing, cada tool call passa por um pipeline de 7 etapas emagent_tool_sanitizer.go:
Compactação de histórico (3 níveis)
OHistoryCompactor em cli/history_compactor.go gerencia o tamanho do histórico através de um pipeline progressivo:
Trigger no modo agente
No modo agente, a compactação é verificada a cada turno do loop ReAct. O trigger ocorre quando o uso de tokens ultrapassa 60% do budget do modelo.Sistema de checkpoint/rewind
Orewind.go implementa um sistema de snapshots do histórico:
Estrutura
Comportamento
- Quando salva: Antes de cada chamada ao LLM (
saveCheckpoint()) - Limite: Máximo de 20 checkpoints (FIFO — os mais antigos são descartados)
- Deep copy: Cada checkpoint contém uma cópia completa e independente do histórico
- Trigger: Pressionar Esc+Esc (dois Esc em menos de 500ms) abre o menu de rewind
- Restauração: O usuário escolhe um checkpoint, e o histórico é substituído pela cópia salva
Registry de provedores
Padrão de auto-registro
Cada provedor LLM se registra automaticamente viainit() no pacote llm/registry:
ProviderInfo
Fluxo de descoberta
switch/case. Para adicionar um novo provedor, basta criar o pacote com register.go e implementar a interface LLMClient.
Memory Worker (processo background)
OmemoryWorker em cli/memory_worker.go extrai memórias da conversa em background:
Parâmetros
Triggers
- nudge(): Chamado após cada resposta do LLM. Se houver >= 4 novas mensagens e cooldown expirado, executa extração
- Ticker de 3 minutos: O loop background verifica periodicamente (para sessões longas onde o usuário digita pouco)
- Compaction ticker (6h): Consolida fatos antigos com scores baixos
- Cleanup ticker (24h): Remove notas diárias expiradas
Pipeline de extracao
Componentes principais
CLI e Modos
A structChatCLI em cli/cli.go é o ponto central (~923 linhas após decomposição). O método Start() inicia o modo interativo usando Bubble Tea (Charmbracelet). Métodos auxiliares, gerenciamento de histórico, troca de modos, formatação de saída, construção de prompts, gerenciamento de sessões e tratamento de tools foram extraídos para arquivos dedicados (cli_helpers.go, cli_history.go, cli_mode.go, cli_output.go, cli_prompt.go, cli_session.go, cli_tools.go).
A troca entre modos usa um mecanismo de
panic/recover para sair do loop do go-prompt.
O ChatCLI usa um histórico unificado (cli.history) compartilhado entre todos os modos. Ao trocar de modo, o contexto completo é preservado. Os comandos /compact e /rewind operam diretamente sobre esse histórico único.
Message Bus
O pacotecli/bus implementa um barramento de mensagens tipado com:
- Pub/sub com filtros por canal e tipo
- Request-reply com correlation IDs
- Métricas atômicas de throughput
Multi-Agent
O sistema de orquestração emcli/agent/workers gerencia 12 agents especialistas que executam em goroutines paralelas com semáforo configurável. Cada worker possui:
- Mini ReAct loop isolado (observe -> reason -> act)
- Skills próprias (scripts aceleradores e descritivas)
- File locks (mutex per-filepath)
- Timeout e max turns configuráveis
Tecnologias
Padrões de design
- Auto-registro via
init()— Provedores se registram automaticamente - Interface-driven —
LLMClienteToolAwareClientpara polimorfismo - Fallback chain — Classificacao inteligente de erros + cooldown exponencial
- Stateful parser — Parsing de XML com atributos escapados (mais robusto que regex)
- embed.FS — Arquivos i18n embarcados no binário (sempre usa
/, nuncafilepath.Join) - Panic/recover — Troca de modos no go-prompt sem reiniciar o processo
- Histórico unificado — Um único array de mensagens compartilhado entre chat, agent e coder
- Checkpoint/rewind — Deep copy do histórico antes de cada chamada LLM, com restauração seletiva
- Compactação em 3 níveis — Trimming near-lossless, sumarização estruturada, truncamento de emergência
- Type-ahead queue — Mensagens digitadas durante processamento são enfileiradas e drenadas automaticamente
- Workspace context injection — Bootstrap + memória injetados automaticamente em todo system prompt
- Context injection via system prompt — Contextos attached (
/context attach) são injetados comoSystemPartscomCacheControlno modelo de mensagem, permitindo cache automático por provider (Anthropiccache_control: ephemeral, OpenAI prompt caching, Google context caching) - Background memory extraction — Worker em goroutine extrai e consolida memórias da conversa sem bloquear o fluxo principal