O Claude Code é uma das ferramentas de agentic coding mais poderosas que existem. Ele lê seus arquivos, escreve código, roda comandos e gerencia workflows inteiros de desenvolvimento a partir de um terminal. Mas a interface é um terminal. Um terminal preso às mesmas limitações que os terminais têm há décadas: sem renderização rica de markdown, sem diffs inline, sem chamadas de ferramenta recolhíveis, sem nenhum feedback visual além do que os códigos de escape ANSI conseguem fazer.
Queríamos saber: dá para criar uma skin para ele? Dá para trocar a interface de terminal por algo melhor e manter todo o poder do Claude Code por baixo? Passamos várias sessões de pesquisa desmontando a arquitetura do Claude Code, estudando cada wrapper de interface gráfica da comunidade e projetando do zero um framework com várias skins. Isto é tudo o que descobrimos.
Como a TUI do Claude Code funciona de verdade
Antes de tentar substituir a interface, precisávamos entender o que estávamos substituindo. O Claude Code não é uma aplicação de terminal simples. É um renderizador React + Ink próprio, construído do zero.
A equipe da Anthropic reescreveu por completo o motor de renderização do Ink. O renderizador próprio usa um buffer de tela baseado em células, com Uint32Array para os caracteres, Uint16Array para os estilos e Uint8Array para a largura dos caracteres. O layout fica por conta do Yoga WASM, o motor de flexbox da Meta compilado para WebAssembly. A renderização é diferencial, ou seja, só as células que mudaram emitem novas sequências de escape ANSI. Foi assim que eles reduziram em 85% o flickering no terminal. Há também suporte ao modo DEC 2026 para saída sincronizada no terminal.
A aplicação inteira é empacotada pelo Bun em um único arquivo cli.js de 10.5MB. Ela traz embutidas as próprias cópias do ripgrep, do Tree-sitter WASM, do Yoga WASM e do resvg WASM, carregadas em runtime. Zero dependências externas. O código é TypeScript, e cerca de 90% dele foi escrito pelo próprio Claude Code.
A filosofia da equipe da Anthropic: "Menos infraestrutura permite que o modelo entregue todo o seu potencial." A cada lançamento de modelo, eles apagam código e prompts de sistema sem dó.
Com essa arquitetura, você não pode simplesmente injetar CSS ou trocar um arquivo de tema. O pipeline de renderização está profundamente integrado à árvore de componentes React e ao renderizador Ink próprio. Criar uma skin para o Claude Code por dentro não é viável.
A porta de entrada: Claude Agent SDK
Se não dá para criar a skin por dentro, o jeito é contornar. O Claude Agent SDK oficial (@anthropic-ai/claude-agent-sdk) expõe todo o poder do Claude Code como um subprocesso programável. Ele inicia a CLI com --output-format stream-json --verbose e se comunica por stdin/stdout usando NDJSON (JSON delimitado por quebras de linha).
A função central é query(). Ela retorna um gerador assíncrono que emite eventos SDKMessage tipados conforme o modelo pensa, escreve código e usa ferramentas:
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const msg of query({
prompt: "Fix the bug in auth.ts",
options: {
model: "opus",
allowedTools: ["Read", "Edit", "Bash"],
mcpServers: { synabun: { command: "node", args: ["./mcp/preload.js"] } },
canUseTool: async (toolName, input) => {
// Show approval dialog in your custom UI
return await showPermissionDialog(toolName, input);
}
}
})) {
renderMessage(msg); // Your custom rendering
}
O SDK entrega tudo: gerenciamento de sessões com retomada e fork, controle granular de permissões com callbacks canUseTool, integração com servidores MCP, interceptação de hooks, criação de subagentes e acesso completo, por streaming, a cada token que o modelo produz.
O protocolo de streaming
Entender o protocolo de streaming em NDJSON é essencial para construir um frontend próprio. Cada linha que o SDK emite é um objeto JSON completo. Um turno típico passa por estes tipos de mensagem:
- SDKSystemMessage no início da sessão, com o ID da sessão e as capacidades
- SDKPartialAssistantMessage para o streaming em tempo real, com eventos da API da Anthropic aninhados:
message_start,content_block_start,content_block_delta(em quetext_deltacarrega os trechos de texto em si einput_json_deltacarrega a entrada da ferramenta),content_block_stop,message_delta,message_stop - SDKAssistantMessage com a resposta completa, incluindo todos os blocos de conteúdo, informações do modelo e uso de tokens
- SDKResultMessage no final, com o custo total em USD, a duração, o ID da sessão e o subtipo de sucesso ou erro
No uso de ferramentas, o fluxo se bifurca: o modelo emite um content_block_start com type: "tool_use", transmite a entrada da ferramenta como deltas de JSON, a ferramenta executa e os resultados voltam. Se a sua interface precisa aprovar o uso de ferramentas, você intercepta o callback canUseTool, mostra o seu próprio diálogo e retorna "allow" ou "deny".
O cenário da comunidade: 9 wrappers de interface gráfica
Não fomos os primeiros a querer uma interface melhor para o Claude Code. Mergulhamos em nove projetos da comunidade que já construíram wrappers de interface gráfica. O cenário se divide por framework:
Baseados em Tauri (Rust + React) — leves
O Claudia é o mais notável. Apoiado pela Y Combinator (S24), ele envolve o Claude Code em um shell Tauri 2 com React 18, Vite 6, Tailwind CSS 4 e shadcn/ui. O backend em Rust cuida da chamada à CLI pelo plugin de shell do Tauri, com SQLite para metadados de projetos, checkpoints de sessão e estatísticas de uso. Entre os principais recursos estão o versionamento de sessões com navegação por linha do tempo, o fork a partir de um checkpoint, um visualizador de diffs e uma interface de gerenciamento de MCP. É o projeto mais bem-acabado da área.
Outros wrappers em Tauri incluem o Opcode (descoberta de projetos, tarefas em segundo plano, acompanhamento de tokens), o yume (streaming com vários provedores), o cc-switch (várias CLIs, com suporte a Claude, Codex e Gemini e com MCP e skills unificados) e o claude-code-desktop (wrapper mínimo que preserva as capacidades nativas).
Baseados em Electron
O CodePilot usa Electron 40 com Next.js 16 e React 19. Seu claude-client.ts tem 41KB de lógica de streaming. Ele tem um schema SQLite de 12 tabelas, 52 endpoints de API REST, hooks de notificação pelo Telegram e um modelo de processos filhos que usa utilityProcess.fork(). É o projeto mais pesado e mais completo em recursos, mas também o que tem a maior base de código.
Baseados na web
O Claudex (React 19 + FastAPI) trouxe uma inovação esperta: um sidecar chamado "Anthropic Bridge", que permite rotear as chamadas do Claude Agent SDK por provedores que não são a Anthropic, como OpenRouter ou GitHub Copilot. Ele roda em 127.0.0.1:3456 dentro de um sandbox Docker e altera ANTHROPIC_BASE_URL para interceptar as chamadas de API. Outros projetos web incluem o claude-code-webui (Deno + Vite, funciona bem no celular) e o CloudCLI (gerenciamento remoto de sessões).
O padrão nos nove projetos é idêntico: iniciar a CLI claude como subprocesso, capturar a saída em stream-json e renderizá-la em uma interface própria. O Tauri 2 venceu o Electron nos projetos novos. React 18+ com Vite, shadcn/ui e Tailwind CSS 4 é a stack padrão de frontend. O SQLite cuida da persistência local em todos eles.
As três abordagens que avaliamos
Com a pesquisa concluída, tínhamos três caminhos claros pela frente:
Opção A: estender a Neural Interface que já existe. O SynaBun já roda uma interface web na porta 3344. Ela tem um servidor Express, infraestrutura de WebSocket, sessões de terminal com ConPTY, um explorador de arquivos, interface de ajustes, integração com a memória do SynaBun e um sistema de retomada de sessões. Adicionar uma skin em modo chat significa adicionar o gerenciamento de subprocessos do Agent SDK no servidor e uma nova camada de renderização no navegador.
Opção B: construir um novo app em Tauri ou Electron. Começar do zero com um wrapper de desktop dedicado, seguindo o padrão estabelecido pelo Claudia e pelo CodePilot. Arquitetura limpa, mas duplica toda a infraestrutura que já temos.
Opção C: fazer um fork do Claude Code e modificá-lo diretamente. Aplicar patches no renderizador React + Ink para dar suporte a temas próprios ou a renderizadores alternativos. Possível na teoria, impossível de manter na prática. Cada atualização do Claude Code quebraria o fork.
Escolhemos a Opção A. O raciocínio foi simples: já temos um servidor Express rodando com WebSocket, um gerenciador de sessões de terminal, um explorador de arquivos, ajustes, memória e retomada de sessões. Nenhum framework novo. A skin de chat é só mais uma visualização na aplicação que já existe.
O framework com várias skins
Não queríamos só uma skin alternativa. Queríamos um sistema em que qualquer pessoa pudesse criar e trocar skins próprias. A fase de pesquisa resultou em uma arquitetura de cinco partes:
1. Skin = padrão de variantes
A Neural Interface do SynaBun já tem um sistema de variantes. O grafo de memórias em 3D e o grafo em 2D são ambos "variantes" registradas pelo registry.js, com as capacidades declaradas via registerVariant(). Cada variante tem seus próprios botões na barra de navegação, seções de ajuda, abas de ajustes e atalhos de teclado. O barramento de eventos em state.js cuida da comunicação entre os módulos compartilhados e os renderizadores específicos de cada variante.
Uma skin do Claude Code é só mais uma variante. O grafo 3D fornece o graph.js, uma skin de chat fornece o ui-claude-skin.js. Mesmo contrato, outro renderizador. Sem precisar de um novo sistema de plugins.
2. Manifesto e descoberta de skins
Cada skin fica em sua própria pasta em neural-interface/public/skins/<name>/, com um manifesto skin.json. O servidor descobre as skins na inicialização, varrendo o diretório e lendo os manifestos. Solte uma pasta ali e funciona. Sem marketplace, sem fluxo de instalação, sem configuração. Como as skills do Claude Code em .claude/commands/ ou as extensões do VS Code na pasta de extensões.
{
"name": "SynaBun Chat",
"id": "chat",
"version": "1.0.0",
"author": "SynaBun",
"entry": "index.js",
"css": "styles.css",
"capabilities": ["streaming", "diffs", "permissions", "cost-tracking"],
"requires": ["skin-bridge"]
}
3. SkinBridge: a camada de abstração
Esta é a sacada central da arquitetura. Eventos crus do SDK, como content_block_delta, são de nível baixo demais para código de interface. Renderizadores de terminal embolam a lógica de redimensionamento, rolagem e renderização. O SkinBridge fica entre o streaming do Agent SDK e o renderizador da skin. Ele é dono da conexão WebSocket, interpreta o NDJSON, acumula os deltas de texto, acompanha as chamadas de ferramenta e dispara callbacks de alto nível.
O contrato da skin é um conjunto de callbacks de ciclo de vida que toda skin precisa implementar:
export function createSkin(container, bridge) {
return {
mount(container) {},
unmount() {},
onTextChunk(text, messageId) {},
onTextComplete(fullText, messageId) {},
onToolStart(toolName, toolInput, toolId) {},
onToolComplete(toolName, result, toolId) {},
onPermissionRequest(toolName, input, respond) {},
onResult({ cost, tokens, duration, sessionId }) {},
onError(message, subtype) {},
};
}
O bridge cuida de toda a complexidade da conexão. As skins nunca tocam no WebSocket. Nunca interpretam NDJSON. Só implementam callbacks de renderização. Com essa separação, você pode construir uma skin mínima em cerca de 100 linhas de código.
4. Componentes combináveis
Construir uma skin do zero dá trabalho demais para a maioria dos desenvolvedores. Uma abordagem toda baseada em templates gera resultados todos iguais. O meio-termo é uma biblioteca de componentes de interface combináveis, que cada skin importa conforme precisa. Só quer mudar a renderização das mensagens? Importe o visualizador de diffs e o diálogo de permissões padrão. Quer uma exibição de chamadas de ferramenta totalmente sua? Escreva a sua, mas mantenha o seletor de sessões padrão.
A biblioteca de componentes inclui MessageBubble para mensagens do usuário e do assistente com markdown, ToolCallCard para chamadas de ferramenta recolhíveis com painéis de entrada e saída, DiffView para diffs lado a lado ou inline, BashOutput para saída de comandos com cores ANSI, PermissionDialog para modais de aprovação de ferramentas, CostCounter para acompanhar tokens e custo, SessionPicker para os fluxos de retomar, bifurcar e criar sessão, e StreamingText para efeitos de máquina de escrever com cursor.
Cada componente exporta uma função render(container, data, options) e usa variáveis CSS para os temas. Mude cores, espaçamento e fontes sem tocar na lógica do componente.
5. Três skins integradas
A skin Terminal encapsula a integração xterm.js + ConPTY que já existe. Ela preserva a experiência completa da CLI, com autocompletar por tab e modo vim. É a alternativa para desenvolvedores que querem acesso direto ao terminal. Não precisa do Agent SDK, porque se conecta diretamente via PTY.
A skin Chat (SynaBun Chat) é a implementação de referência e o diferencial do produto. Markdown em streaming com streaming-markdown + marked.js. Destaque de código com highlight.js e detecção automática. Cards recolhíveis para chamadas de ferramenta. Diffs de arquivos com diff2html. Saída do bash com cores ANSI via ansi_up.js. Diálogos modais de aprovação de permissões. Contador de custo e tokens. Seletor de sessão com retomar e bifurcar. Seletor de modelo. O design segue espaçamento enxuto, nada de card dentro de card, botões utilitários só com texto e um ritmo visual intencional.
A skin Minimal é uma implementação enxuta, de 100 linhas, do contrato completo. Saída em texto puro, diálogos alert() para permissões, nenhuma dependência externa. Ela existe apenas como tutorial, para quem desenvolve skins ver a implementação mínima viável.
As decisões de stack do frontend
Todo projeto da comunidade usa React. Nós estamos construindo em JavaScript vanilla, com zero dependências de framework. É uma escolha deliberada. A Neural Interface do SynaBun já é JS vanilla, com um design intencional e feito à mão. Nenhum dos nove wrappers da comunidade é JS vanilla. Esse é o diferencial.
Para a renderização de markdown, o marked.js vence com 19,3 milhões de downloads semanais, 36.000 estrelas no GitHub e 97% de conformidade com GFM. Combinado com o streaming-markdown para renderizar em tempo real durante o streaming de tokens, ele produz o efeito de máquina de escrever no estilo do ChatGPT. A biblioteca streaming-markdown é otimista (renderiza markdown parcial na hora) e não destrutiva (só adiciona elementos ao DOM, nunca modifica os existentes, então o usuário pode copiar texto durante o streaming).
Para o destaque de sintaxe, o highlight.js tem suporte a 190+ linguagens com detecção automática em cerca de 20KB no bundle. O Prism.js é mais rápido, mas não tem detecção automática. O Shiki usa as gramáticas TextMate do VS Code para uma precisão perfeita, mas exige 250KB+ de WASM. O highlight.js é o equilíbrio certo.
Para a renderização de diffs, o diff2html oferece visualização linha a linha e lado a lado, com destaque de sintaxe embutido. Para a saída do terminal, o ansi_up.js converte sequências de escape ANSI em HTML com zero overhead. Sem emulação de cursor, sem buffer de tela, só texto colorido.
Para a performance, a rolagem virtual com HyperList dá conta de 10.000+ itens em menos de 100ms, com zero dependências e apenas 300 linhas de código. O agrupamento com requestAnimationFrame junta vários pedaços do streaming em uma única passada de layout. O IntersectionObserver adia o parsing do markdown até as mensagens entrarem na área visível.
Bundle total do frontend: aproximadamente 100KB com gzip. Isso é marked.js, highlight.js, streaming-markdown, DOMPurify, diff2html e ansi_up.js somados.
A ida e volta das permissões
Uma das partes mais complicadas de construir um frontend próprio para o Claude Code é lidar com as permissões. No terminal, o Claude Code pergunta inline e espera uma tecla. Em uma interface própria, isso precisa virar um diálogo visual com resposta assíncrona.
O fluxo funciona assim: o Agent SDK chega ao canUseTool no servidor, que envia um evento claude:permission por WebSocket para o navegador. A skin ativa renderiza um diálogo de permissão mostrando o nome da ferramenta e a entrada. O usuário clica em permitir ou negar. O navegador envia claude:permission:response de volta por WebSocket. O servidor resolve a promise de canUseTool. O Claude Code continua ou para.
O mesmo padrão cuida da interceptação de AskUserQuestion, quando o Claude faz perguntas para esclarecer algo. O SDK envia as perguntas, sua interface as exibe, o usuário responde e as respostas voltam pelo mesmo caminho.
O que isso significa para quem usa o Claude Code
A resposta curta para "dá para criar uma skin para o Claude Code" é não, não por dentro. O renderizador React + Ink é um sistema fechado. Mas você pode construir um frontend totalmente separado que conversa com o Claude Code pelo Agent SDK, e a experiência pode ser bem melhor que a do terminal.
A comunidade já provou isso com nove projetos diferentes. O que ninguém fez ainda foi construir um sistema em que as skins sejam intercambiáveis, componíveis e livres de framework. É isso que estamos construindo.
A principal conclusão desta pesquisa é que você não precisa entender o funcionamento interno do Claude Code para construir um frontend próprio. O Agent SDK abstrai tudo em um protocolo de streaming limpo. A parte difícil não é se conectar ao Claude Code. É construir uma camada de renderização que dê conta de markdown em streaming, visualização de chamadas de ferramenta, diffs inline, diálogos de permissão e gerenciamento de sessões sem virar um arquivo de 40KB de código espaguete.
O padrão SkinBridge resolve isso traçando uma linha clara entre a complexidade do protocolo e a renderização. As skins continuam simples. A ponte cuida da bagunça. Os componentes deixam você combinar como quiser.
Esta pesquisa está guardada em 21 memórias na memória vetorial persistente do SynaBun, na categoria synabun-skins. O plano completo de implementação existe. Nenhum código foi escrito ainda. Quando construirmos, vamos documentar cada passo aqui.
Se você está construindo seu próprio frontend para o Claude Code ou pensando em personalizar a experiência de agentic coding, a documentação do Agent SDK e os projetos da comunidade listados acima são os melhores pontos de partida. E, se você quer um sistema de skins que simplesmente funciona, sem configuração, acompanhe a gente. Estamos construindo tudo em aberto.