Claude Code es una de las herramientas de agentic coding más potentes que existen. Lee tus archivos, escribe código, ejecuta comandos y gestiona workflows de desarrollo completos desde una terminal. Pero la interfaz es una terminal. Una terminal con las mismas limitaciones que las terminales arrastran desde hace décadas: sin renderizado enriquecido de markdown, sin diffs en línea, sin llamadas a herramientas que se puedan contraer, sin más respuesta visual que la que permiten los códigos de escape ANSI.

Queríamos saberlo: ¿se le puede poner un skin? ¿Se puede reemplazar la interfaz de terminal por algo mejor y conservar debajo toda la potencia de Claude Code? Dedicamos varias sesiones de investigación a desmontar la arquitectura de Claude Code, estudiar todos los wrappers gráficos de la comunidad y diseñar desde cero un framework multiskin. Esto es todo lo que encontramos.

Cómo funciona en realidad la TUI de Claude Code

Antes de intentar reemplazar la interfaz, teníamos que entender qué íbamos a reemplazar. Claude Code no es una simple aplicación de terminal. Es un renderizador React + Ink a medida, construido desde cero.

El equipo de Anthropic reescribió por completo el motor de renderizado de Ink. Su renderizador a medida usa un búfer de pantalla basado en celdas, con Uint32Array para los caracteres, Uint16Array para los estilos y Uint8Array para el ancho de los caracteres. Del layout se encarga Yoga WASM, el motor de flexbox de Meta compilado a WebAssembly. El renderizado es diferencial: solo las celdas que cambian emiten nuevas secuencias de escape ANSI. Así lograron reducir en un 85% el parpadeo de la terminal. También admiten el modo DEC 2026 para sincronizar la salida de la terminal.

Bun empaqueta toda la aplicación en un único archivo cli.js de 10.5MB. Incluye sus propias copias de ripgrep, Tree-sitter WASM, Yoga WASM y resvg WASM, que usa en tiempo de ejecución. Cero dependencias externas. El código está escrito en TypeScript, y cerca del 90% lo escribió el propio Claude Code.

La filosofía del equipo de Anthropic: «Menos infraestructura permite que el modelo despliegue todo su potencial». Con cada nueva versión del modelo eliminan activamente código y prompts de sistema.

Con esta arquitectura no basta con inyectar CSS o cambiar un archivo de tema. El pipeline de renderizado está profundamente integrado en el árbol de componentes de React y en el renderizador de Ink a medida. Ponerle un skin a Claude Code desde dentro no es viable.

La vía de entrada: Claude Agent SDK

Si no puedes ponerle un skin desde dentro, hazlo desde fuera. El Claude Agent SDK oficial (@anthropic-ai/claude-agent-sdk) expone toda la potencia de Claude Code como un subproceso programable. Lanza la CLI con --output-format stream-json --verbose y se comunica por stdin/stdout mediante NDJSON (JSON delimitado por saltos de línea).

La función principal es query(). Devuelve un generador asíncrono que produce eventos SDKMessage tipados a medida que el modelo piensa, escribe código y usa herramientas:

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
}

El SDK te lo da todo: gestión de sesiones con opciones para reanudarlas y bifurcarlas, control granular de permisos mediante callbacks canUseTool, integración con servidores MCP, interceptación de hooks, creación de subagentes y acceso completo por streaming a cada token que produce el modelo.

El protocolo de streaming

Entender el protocolo de streaming NDJSON es imprescindible para construir un frontend a medida. Cada línea que emite el SDK es un objeto JSON completo. Un turno típico pasa por estos tipos de mensaje:

  1. SDKSystemMessage al inicio de la sesión, con el ID de sesión y las capacidades
  2. SDKPartialAssistantMessage para el streaming en tiempo real, con eventos anidados de la API de Anthropic: message_start, content_block_start, content_block_delta (donde text_delta lleva los fragmentos de texto y input_json_delta lleva la entrada de la herramienta), content_block_stop, message_delta, message_stop
  3. SDKAssistantMessage con la respuesta completa, incluidos todos los bloques de contenido, la información del modelo y el uso de tokens
  4. SDKResultMessage al final, con el costo total en USD, la duración, el ID de sesión y el subtipo de éxito o de error

Con el uso de herramientas, el flujo se bifurca: el modelo emite un content_block_start con type: "tool_use", transmite la entrada de la herramienta como deltas de JSON, luego la herramienta se ejecuta y los resultados vuelven. Si tu interfaz a medida necesita aprobar el uso de herramientas, interceptas el callback canUseTool, muestras tu propio diálogo y devuelves "allow" o "deny".

El panorama de la comunidad: 9 wrappers gráficos

No fuimos los primeros en querer una interfaz mejor para Claude Code. Analizamos a fondo nueve proyectos de la comunidad que ya construyeron wrappers gráficos. El panorama se divide según el framework:

Basados en Tauri (Rust + React): ligeros

Claudia es el más destacado. Respaldado por Y Combinator (S24), envuelve Claude Code en un shell de Tauri 2 con React 18, Vite 6, Tailwind CSS 4 y shadcn/ui. El backend en Rust se encarga de invocar la CLI mediante el plugin shell de Tauri, con SQLite para los metadatos de proyectos, los checkpoints de sesión y las estadísticas de uso. Entre sus funciones clave están el versionado de sesiones con navegación por línea de tiempo, la bifurcación desde un checkpoint, un visor visual de diffs y una interfaz para gestionar MCP. Es el proyecto más pulido de este ámbito.

Otros wrappers en Tauri son Opcode (detección de proyectos, tareas en segundo plano, seguimiento de tokens), yume (streaming con varios proveedores), cc-switch (multi-CLI, compatible con Claude, Codex y Gemini, con MCP y skills unificados) y claude-code-desktop (un wrapper mínimo que conserva las capacidades nativas).

Basados en Electron

CodePilot usa Electron 40 con Next.js 16 y React 19. Su claude-client.ts son 41KB de lógica de streaming. Tiene un esquema de SQLite con 12 tablas, 52 endpoints de API REST, hooks de notificaciones por Telegram y un modelo de procesos hijo que usa utilityProcess.fork(). Es el proyecto más pesado y el más completo en funciones, pero también el que tiene más código.

Basados en web

Claudex (React 19 + FastAPI) introdujo una innovación ingeniosa: un sidecar, el «Anthropic Bridge», que te permite enrutar las llamadas del Claude Agent SDK a través de proveedores distintos de Anthropic, como OpenRouter o GitHub Copilot. Se ejecuta en 127.0.0.1:3456 dentro de un sandbox de Docker y modifica ANTHROPIC_BASE_URL para interceptar las llamadas a la API. Otros proyectos web son claude-code-webui (Deno + Vite, adaptado a móviles) y CloudCLI (gestión remota de sesiones).

El patrón en los nueve proyectos es idéntico: lanzar la CLI claude como subproceso, capturar su salida stream-json y renderizarla en una interfaz a medida. Tauri 2 le ganó a Electron en los proyectos nuevos. React 18+ con Vite, shadcn/ui y Tailwind CSS 4 es el stack estándar de frontend. SQLite se encarga de la persistencia local en todos los casos.

Los tres enfoques que evaluamos

Con la investigación terminada, teníamos tres caminos claros:

Opción A: ampliar la Neural Interface existente. SynaBun ya ejecuta una interfaz web en el puerto 3344. Tiene un servidor Express, infraestructura de WebSocket, sesiones de terminal con ConPTY, un explorador de archivos, una interfaz de ajustes, integración con la memoria de SynaBun y un sistema para reanudar sesiones. Añadir un skin en modo chat implica sumar la gestión de subprocesos del Agent SDK en el servidor y una nueva capa de renderizado en el navegador.

Opción B: crear una aplicación nueva en Tauri o Electron. Empezar de cero con un wrapper de escritorio dedicado, siguiendo el patrón que establecieron Claudia y CodePilot. Una arquitectura limpia, pero duplica toda la infraestructura que ya tenemos.

Opción C: hacer un fork de Claude Code y modificarlo directamente. Parchear el renderizador React + Ink para que admita temas personalizados o renderizadores alternativos. Posible en teoría, pero imposible de mantener en la práctica. Cada actualización de Claude Code rompería el fork.

Elegimos la Opción A. El razonamiento fue sencillo: ya tenemos en marcha un servidor Express con WebSocket, un gestor de sesiones de terminal, un explorador de archivos, ajustes, memoria y la función de reanudar sesiones. No hace falta ningún framework nuevo. El skin de chat es solo una vista más de la aplicación existente.

El framework multiskin

No queríamos un único skin alternativo. Queríamos un sistema en el que cualquiera pueda crear e intercambiar skins a medida. La fase de investigación dio como resultado una arquitectura en cinco partes:

1. Skin = patrón de variantes

La Neural Interface de SynaBun ya tiene un sistema de variantes. El grafo de memorias en 3D y el grafo en 2D son «variantes» registradas a través de registry.js, con capacidades que se declaran mediante registerVariant(). Cada variante tiene sus propios botones en la barra de navegación, secciones de ayuda, pestañas de ajustes y atajos de teclado. El bus de eventos de state.js gestiona la comunicación entre los módulos compartidos y los renderizadores específicos de cada variante.

Un skin de Claude Code es solo una variante más. El grafo en 3D aporta graph.js; un skin de chat aporta ui-claude-skin.js. El mismo contrato, otro renderizador. No hace falta un sistema de plugins nuevo.

2. Manifiesto y detección de skins

Cada skin vive en su propia carpeta dentro de neural-interface/public/skins/<name>/, con un manifiesto skin.json. El servidor detecta los skins al arrancar: recorre el directorio y lee los manifiestos. Sueltas una carpeta y funciona. Sin marketplace, sin proceso de instalación, sin configuración. Igual que las skills de Claude Code en .claude/commands/ o las extensiones de VS Code en su carpeta de extensiones.

{
  "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: la capa de abstracción

Esta es la idea central de la arquitectura. Los eventos en bruto del SDK, como content_block_delta, son de un nivel demasiado bajo para el código de interfaz. Los renderizadores de terminal mezclan la lógica de redimensionado, de desplazamiento y de renderizado. El SkinBridge se sitúa entre el streaming del Agent SDK y el renderizador del skin. Es el dueño de la conexión WebSocket, analiza el NDJSON, acumula los deltas de texto, hace el seguimiento de las llamadas a herramientas y despacha callbacks de alto nivel.

El contrato del skin es un conjunto de callbacks de ciclo de vida que todo skin debe 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) {},
  };
}

El bridge se encarga de toda la complejidad de la conexión. Los skins nunca tocan el WebSocket. Nunca analizan NDJSON. Solo implementan callbacks de renderizado. Gracias a esta separación, un skin mínimo se puede construir en unas 100 líneas de código.

4. Componentes componibles

Crear un skin desde cero es demasiado trabajo para la mayoría de los desarrolladores. Un enfoque basado por completo en plantillas produce resultados todos iguales. El punto medio es una biblioteca de componentes de interfaz componibles que cada skin importa de forma selectiva. ¿Solo necesitas cambiar el renderizado de los mensajes? Importa el visor de diffs y el diálogo de permisos predeterminados. ¿Quieres una visualización de llamadas a herramientas totalmente a medida? Escribe la tuya, pero conserva el selector de sesiones predeterminado.

La biblioteca de componentes incluye MessageBubble para los mensajes del usuario y del asistente con markdown, ToolCallCard para las llamadas a herramientas, plegables y con paneles de entrada y salida, DiffView para diffs en paralelo o en línea, BashOutput para la salida de comandos con colores ANSI, PermissionDialog para los modales de aprobación de herramientas, CostCounter para el seguimiento de tokens y costos, SessionPicker para reanudar, bifurcar o crear sesiones, y StreamingText para efectos de máquina de escribir con cursor.

Cada componente exporta una función render(container, data, options) y usa variables CSS para los temas. Cambia los colores, el espaciado y las fuentes sin tocar la lógica del componente.

5. Tres skins integrados

El skin Terminal envuelve la integración existente de xterm.js + ConPTY. Conserva la experiencia completa de la CLI, con autocompletado con tabulador y modo vim. Es la alternativa para quienes quieren acceso directo a la terminal. No necesita el Agent SDK, porque se conecta directamente por PTY.

El skin Chat (SynaBun Chat) es la implementación de referencia y lo que diferencia al producto. Markdown en streaming con streaming-markdown + marked.js. Resaltado de código con highlight.js y detección automática. Tarjetas plegables para las llamadas a herramientas. Diffs de archivos con diff2html. Salida de bash con colores ANSI gracias a ansi_up.js. Diálogos modales para aprobar permisos. Contador de costos y tokens. Selector de sesiones para reanudar y bifurcar. Selector de modelo. El diseño apuesta por un espaciado ajustado, sin tarjetas anidadas dentro de tarjetas, con botones de utilidad de solo texto y un ritmo visual intencional.

El skin Minimal es una implementación mínima del contrato completo en 100 líneas. Salida en texto plano, diálogos alert() para los permisos y ninguna dependencia externa. Existe únicamente como tutorial, para que quienes desarrollan skins vean la implementación mínima viable.

Las decisiones sobre el stack del frontend

Todos los proyectos de la comunidad usan React. Nosotros lo estamos construyendo en JavaScript vanilla, sin depender de ningún framework. Es una decisión deliberada. La Neural Interface de SynaBun ya está hecha en JS vanilla, con un diseño pensado y creado a mano. Ninguno de los nueve wrappers de la comunidad usa JS vanilla. Ahí está la diferencia.

Para renderizar markdown gana marked.js, con 19,3 millones de descargas semanales, 36.000 estrellas en GitHub y un 97% de compatibilidad con GFM. Combinado con streaming-markdown para renderizar en tiempo real mientras llegan los tokens, produce el efecto de máquina de escribir al estilo de ChatGPT. La biblioteca streaming-markdown es optimista (renderiza el markdown parcial de inmediato) y no destructiva (solo agrega elementos al DOM y nunca modifica los existentes, así que el usuario puede copiar texto durante el streaming).

Para el resaltado de sintaxis, highlight.js admite 190+ lenguajes con detección automática y ocupa unos 20KB empaquetado. Prism.js es más rápido, pero no tiene detección automática. Shiki usa las gramáticas TextMate de VS Code y logra una precisión perfecta, pero requiere 250KB+ de WASM. highlight.js es el equilibrio adecuado.

Para renderizar diffs, diff2html ofrece vistas línea por línea y en paralelo, con resaltado de sintaxis incluido. Para la salida de la terminal, ansi_up.js convierte las secuencias de escape ANSI en HTML sin ninguna sobrecarga. Sin emulación de cursor ni búfer de pantalla: solo texto con color.

En cuanto al rendimiento, el scroll virtual con HyperList maneja 10.000+ elementos en menos de 100ms, sin dependencias y con solo 300 líneas de código. El agrupamiento con requestAnimationFrame reúne varios fragmentos del streaming en una sola pasada de layout. IntersectionObserver pospone el análisis del markdown hasta que los mensajes entran en el área visible.

Tamaño total del bundle del frontend: unos 100KB comprimidos con gzip. Eso incluye marked.js, highlight.js, streaming-markdown, DOMPurify, diff2html y ansi_up.js juntos.

El recorrido de los permisos

Una de las partes más complicadas de construir un frontend propio para Claude Code es gestionar los permisos. En la terminal, Claude Code te pregunta ahí mismo y espera a que pulses una tecla. En una interfaz propia, eso tiene que convertirse en un diálogo visual con una respuesta asíncrona.

El flujo funciona así: el Agent SDK invoca canUseTool en el servidor, que envía un evento claude:permission por WebSocket al navegador. El skin activo muestra un diálogo de permisos con el nombre de la herramienta y su entrada. El usuario hace clic en permitir o denegar. El navegador devuelve claude:permission:response por WebSocket. El servidor resuelve la promesa de canUseTool. Claude Code continúa o se detiene.

El mismo patrón sirve para interceptar AskUserQuestion, cuando Claude hace preguntas para aclarar algo. El SDK envía las preguntas, tu interfaz las muestra, el usuario contesta y las respuestas vuelven por el mismo camino.

Qué significa esto para quienes usan Claude Code

La respuesta corta a «¿se le puede poner un skin a Claude Code?» es no, no desde dentro. El renderizador de React + Ink es un sistema cerrado. Pero sí puedes construir un frontend totalmente independiente que hable con Claude Code a través del Agent SDK, y la experiencia puede ser mucho mejor que la de la terminal.

La comunidad ya lo demostró con nueve proyectos distintos. Lo que nadie ha hecho todavía es un sistema en el que los skins sean intercambiables, componibles y sin frameworks. Eso es lo que estamos construyendo.

La conclusión clave de esta investigación es que no necesitas entender las entrañas de Claude Code para construir un frontend propio. El Agent SDK lo abstrae todo en un protocolo de streaming limpio. Lo difícil no es conectarse a Claude Code. Es construir una capa de renderizado que maneje markdown en streaming, visualización de llamadas a herramientas, diffs en línea, diálogos de permisos y gestión de sesiones sin acabar convertida en un archivo espagueti de 40KB.

El patrón SkinBridge lo resuelve trazando una línea clara entre la complejidad del protocolo y el renderizado. Los skins se mantienen simples. El puente se encarga del desorden. Los componentes te permiten combinar a tu gusto.


Esta investigación está guardada en 21 memorias dentro de la memoria vectorial persistente de SynaBun, en la categoría synabun-skins. El plan de implementación completo ya existe. Todavía no se ha escrito ni una línea de código. Cuando lo construyamos, documentaremos aquí cada paso.

Si estás construyendo tu propio frontend para Claude Code o pensando en personalizar la experiencia de agentic coding, la documentación del Agent SDK y los proyectos de la comunidad mencionados arriba son los mejores puntos de partida. Y si quieres un sistema de skins que funcione sin configurar nada, síguenos de cerca. Lo estamos construyendo en abierto.