maicoqb · Engenharia de Software com uma pitada de Gestão

GitHub

Bem vindo ao mundo minimacli

Hoje vou falar um pouco sobre como criei o @minimacli/minimacli, um TUI (Terminal User Interface) que complementa o DeepSeek Harness.

Contexto

O DeepSeek Harness (DSH) é um harness que traz pra gente uma web GUI (Graphic User Interface) à la ChatGPT. É bem simples: você seleciona um Workspace (uma pasta no seu sistema) e começa a conversar com o harness a partir daquele ponto.

Gostei muito, porque parece um GPT integrado ao seu filesystem na palma da sua mão.

Mas eu queria mais: queria um TUI. Sou mais familiarizado com a utilização do terminal para agentes (OpenCode, KiroCLI, etc.), e acredito que o ambiente do terminal me deixa mais produtivo. Além disso, eu queria ter mais poder de customização, com comandos novos e etc.

O Problema

Encontrei alguns TUIs disponíveis para o DSH, mas eles possuíam 2 problemas principais: ou trabalhavam a nível de plugin, comprometendo a segurança do harness; ou, quando trabalhavam a nível de cliente, não possuíam as funcionalidades necessárias para serem um cliente completo.

Eu poderia extender essas capacidades através de plugins, já que existem diversos plugins para o DSH, que cobrem boa parte das necessidades que eu tinha antes de criar um TUI. Porém, existe uma falha de arquitetura muito grave na forma como os plugins DSH são estruturados: eles trabalham a nível de harness, estendendo as capacidades, plugados direto no código-fonte do sistema.

O problema dessa extensão é que coisas como o sandbox podem ter um bypass dos plugins, permitindo que o core do harness seja estendido a ponto de ter suas funcionalidades de segurança substituídas pelos plugins.

É claro que uma curadoria dos plugins utilizados seria suficiente para evitar comprometer o harness, mas isso exige uma análise a cada plugin instalado. Sendo que um update no plugin poderia trazer novos comprometimentos ao harness.

O que é um Harness

Para explicar melhor como funciona esse comprometimento, é importante entender o que é um harness de fato.

Quando trabalhamos com comunicação com modelos de linguagem, precisamos de várias camadas para fazer essa comunicação. Sendo elas: o Modelo, o Runtime do Modelo (Ollama/etc), o Harness (OpenCode/DSH/etc), e a Interface (OpenCode/Codex/etc).

Perceba que, no caso do OpenCode, ele atua tanto como Interface quanto como Harness. O mesmo acontece com Codex e outros GUI/TUI para utilização de Modelos de Linguagem.

O Modelo é o cérebro dessa cadeia. É ele que sabe "pensar", processar os inputs/prompts, avaliar informações, criar o código/textos e trazer o resultado.

Para o Modelo funcionar, ele precisa de um Servidor/Runtime, que é o caso do Ollama ou outros runtimes proprietários. Esse runtime é quem traz a interface, produzindo uma API (web/cmd) para a comunicação entre quem quer utilizar o modelo e o modelo em si.

Um Modelo e seu Runtime possuem apenas informações efêmeras, ou seja, eles não guardam estado do processamento. Cada iteração é uma nova iteração. Aqui entra o Harness, que guarda o contexto da conversa e também parametriza o Modelo com ferramentas para ele utilizar, aumentando a capacidade do modelo de agir e permitindo que ele possa interagir com o mundo, como ler um arquivo ou executar uma busca web, por exemplo.

Depois do Harness, precisamos de uma Interface para interagir com o sistema. Esse é o papel do OpenCode/Codex e afins. Eles são apenas uma camada para que o usuário possa se conectar com o sistema como um todo.

De forma geral, podemos forçar um pouco o nosso sistema para comparar com um sistema MVC: a Interface seria o nosso Controller; o Harness seria o Service; o Runtime seria o Repository; e o Modelo seria o Database.

Existe, porém, uma quebra dentro do Harness que nem sempre fica clara pra todos: o Core e as Capacidades. Isto é, a parte que cuida de segurança e funcionamento, e a parte que cuida de ferramentas e gestão do contexto. Ao introduzirmos plugins no DSH, estamos dando não só novas capacidades para o nosso harness, mas também expandindo/manipulando o core desse componente.

A Solução

Minha solução pra isso foi criar um cliente do harness, que traz basicamente uma interface, e que futuramente vai trazer novas capacidades para esse sistema como um todo.

Ao trazer um cliente, estou mexendo apenas na camada de interface, sem invadir ou manipular o harness como um todo. Nesse modelo, perco a capacidade de adicionar novas ferramentas ou capacidades para o meu sistema, mas garanto que não comprometo a segurança do harness.

Assim surge o minimacli, com os seguintes pilares:

  1. Segurança — segurança significa que só o harness aplica o sandbox e a política de aprovação; nenhum cliente jamais tem esse poder.

  2. Isolamento — isolamento significa que o harness e o cliente são mundos separados: o cliente acessa o harness sem entrar nos seus internos, estado ou processos.

  3. Desacoplamento — desacoplamento significa que o ciclo de vida do cliente não tem nada a ver com os plugins, perfis ou configuração do harness.

  4. Simplicidade — simplicidade significa que um único comando resolve tudo: sem startup manual da web, sem passos extras.

  5. Continuidade — continuidade significa que a mesma conversa está disponível em qualquer superfície, exatamente de onde parou.

  6. Concorrência — concorrência significa que várias sessões podem ser criadas ao mesmo tempo — mais de uma conversa rodando simultaneamente sem conflitos, até dentro do mesmo workspace.

Junto disso, a ideia do minima é ser minimalista, trazendo o mínimo de dependências dentro da aplicação.

Se quiser dar uma olhada no projeto, ele está aberto no github e já tem pacote no npm.

Próximos passos.

Agora a ideia é criar meu próprio harness, o minimacli-harness, integrado como um plugin do minimacli. O objetivo é ter um harness minimalista, que possa se comunicar com LLMs locais, com baixo custo de processamento e memória de contexto.