DEV Community

jesus manrique
jesus manrique

Posted on • Originally published at guayoyo.tech

Días 3, 4 y 5 de Macchiato: Brutalismo Rojo, un Dominio .sh, y la Noche que Casi Rompo el Monitor por las Métricas de Claude

Macchiato Días 3-5 — Header

Serie: Bitácora de Macchiato — Día 1 · Día 2 · Días 3-5


Prometí una bitácora diaria. Fallé. Tres días desaparecido. No por vago. Porque los días 3, 4 y 5 fueron una picadora de carne: rediseño, métricas que Claude esconde como un gremlin, features que mis amigos me exigieron a las 11 PM, bugs que dolieron, y un dominio que compré entre fix y fix como quien compra café a las 3 AM.

Esto no es un tutorial. Es una confesión.


Día 3 — Rojo Sobre Negro

El Día 2 terminó con un sidebar que medio funcionaba. Métricas vivas, grid de terminales, atajos de teclado. Funcionalmente sólido. Visualmente... una lástima. La app seguía pareciendo un proyecto de hackathon: fondo oscuro genérico, botones sacados de documentación de Electron, padding inconsistente, íconos que gritaban "esto lo hizo un backend".

El Día 3 me levanté con una idea fija: Macchiato tiene que verse como se siente usarlo. Y usarlo se siente como estar a las 2 AM con tres monitores, la luz apagada, monitoreando agentes de IA que queman tokens mientras vos tomás café frío. Eso no es una app bonita. Eso es una cabina de control.

Lo que no iba a ser

Antes de decir lo que es, dejame decir lo que prohibí explícitamente:

  • Nada de glassmorphism. No soy un dashboard de cripto en 2023.
  • Nada de tarjetas idénticas en grid. Si cada panel no tiene personalidad, no sirve.
  • Nada de barras laterales con borde de color de 4px. Eso es un template, no diseño.
  • Nada de emojis como íconos. No soy una app de delivery.
  • Nada de gradientes en texto. El texto se lee, no se admira.
  • Nada de hero metrics grandotes con números enormes. Eso es para vender, no para trabajar.
  • Nada de texto en mayúsculas fijas. No voy a gritar datos, se leen en sentence case como la gente.

Con eso claro, empecé a construir.

La paleta

La tomé de lo que veo a las 2 AM todos los días: negro profundo para el fondo (#0A0A0B), capas de grises industriales (#0F0F10, #18181A, #222224) para los paneles y superficies, texto en zinc (#E4E4E7) que se lee sin cansar. Bordes finos, duros, en #27272A — suficientes para separar paneles, no para decorar.

Y el acento: rojo. #E8443A.

No es un rojo Navidad. No es un rojo error. Es un rojo sangre seca, medido, industrial. El tipo de rojo que usas para decir "esto esta vivo" sin que parezca una alarma de incendio.

La razon es simple: el ambar es Guayoyo. Macchiato es otra cosa. Es mas agresivo. Es un espresso, no un latte. El rojo le da caracter sin ser amigable. Esta herramienta no quiere caerte bien. Quiere que trabajes rapido.

Sobre el rojo: solo se usa para datos vivos — tokens quemandose, costos subiendo, sesiones activas. Si ves rojo en Macchiato, algo esta pasando. El resto de la interfaz es monocromatica. Esa restriccion hace que cada pixel de color signifique algo.

Tipografia de ingeniero

JetBrains Mono para todo lo que es datos. Metricas, rutas de archivo, nombres de sesion, comandos. Si es un numero o una ruta, va en monoespaciada. Para labels, botones y navegacion: system-ui del sistema operativo, la que menos molesta. Sin fuentes custom de Google, sin Typekit. La app carga rapido o no carga.

Tamaños quirurgicos: 11px para metadata densa, 12px para valores de tabla, 13px para cuerpo de UI, 15px para headings de panel, 18px para titulos de vista. Nada mas grande que 22px en toda la app. Si necesitas un numero enorme para sentirte importante, abri Excel.

Interletraje maximo de 0.02em en valores — suficiente para que se lean, no tanto que parezca un menu de restaurante.

El resultado

Le mande un screenshot a un amigo diseñador a las 10 PM. Su respuesta textual: "parece la terminal de un banco suizo que ademas esta enojado".

Exactamente.

Macchiato ya no se ve como un proyecto de fin de semana. Se ve como una herramienta que cobra por usarse. Salvo que no cobra. Pero ese es tema para mas abajo.


Dia 4 — La Noche que las Metricas de Claude Casi Me Rompen

Hay que entender algo sobre Claude: Anthropic no quiere que sepas cuanto estas gastando.

No es una teoria conspirativa. Es diseño de producto. Claude Code reporta metricas, si. Pero las reporta en formatos inconsistentes entre versiones de API, entre modelos, entre tipos de request. Las entierra en streams de texto. Las fragmenta en JSON incompleto. Las cambia sin avisar entre minor versions.

El Dia 2 habiamos logrado leer metricas. Eran aproximadas. Servian para darte una idea. Pero tres personas distintas me dijeron lo mismo: "esta cool, pero ¿cuanto estoy gastando exactamente?" No aproximadamente. Exactamente.

El Dia 4 me propuse que Macchiato fuera preciso al centavo. Ingenuo.

Intento 1: parsear el stream

Cada request a Claude devuelve un stream de eventos. Pense que era cuestion de capturar el evento final y leer usage. Error de principiante. Claude manda los tokens de input en el primer chunk, los de output en el ultimo, y los de cache — que es donde esta el ahorro real, a veces el 90% del costo — los manda... en el medio. O no los manda. O los manda duplicados. Depende del modelo, de la version de API, y aparentemente de la fase lunar.

Primer intento: basura. Las metricas bailaban entre requests. Un mismo prompt costaba $0.14 o $0.38 segun como Claude hubiera decidido reportar ese dia.

Intento 2: headers HTTP

Los headers de Anthropic incluyen campos de costo en requests REST. Pero Claude Code usa streaming, y en streaming los headers llegan antes que el cuerpo. Sin el cuerpo completo, no sabes cuantos tokens de output se generaron. La cuenta no cierra hasta que el stream termina, y para entonces los headers ya son historia.

Segundo intento: basura tambien.

Intento 3: state machine

OK. Si el stream es inconsistente, lo trackeo yo. Construi una state machine que recibe cada fragmento del stream, lo clasifica por tipo (input, output, cache_read, cache_write), y va acumulando. Cuando el stream cierra, totaliza.

Funciono. Para Claude Opus. Claude Sonnet reportaba distinto. Haiku reportaba distinto. Claude 3.5 reportaba completamente distinto — campos con nombres diferentes, unidades diferentes, a veces tokens en vez de creditos.

A las 11 PM tenia la state machine soportando 4 modelos con 3 formatos de metricas distintos cada uno. El codigo parecia un detector de mentiras, no un parser de API.

Intento 4: tabla de normalizacion

Rendicion. No podia mantener una state machine con ifs por modelo y version. Era fragil, ilegible, e iba a romperse con el proximo modelo que Anthropic sacara.

Lo que hice fue construir una tabla de normalizacion: cada provider, cada modelo, cada version de API, cada tipo de token tiene un mapeo explicito a un schema unificado dentro de Macchiato. Claude Opus 4.6 reporta cache_creation_input_tokens -> Macchiato lo guarda como cache.write. Claude 3.5 Sonnet reporta cache_read_input_tokens en otro campo con otro nombre -> misma tabla, otro renglon, mismo schema unificado.

La tabla tiene 47 renglones. 47 formas distintas en que Anthropic te dice cuanto te costo algo.

A las 2:17 AM corri diez requests de prueba. Todos cuadraron al centavo. Me quede viendo el sidebar de Macchiato actualizarse con numeros exactos durante cinco minutos. No por debugging. Por placer.

Le mande la build a dos amigos. "Ahora si se cuanto vale cada prompt", me dijo uno. "Antes rezaba, ahora se", dijo el otro.

Eso era el punto. Pero no pude dormir tranquilo. Porque mientras yo peleaba con metricas, ellos estaban probando la app. Y encontraron cosas.


Dia 5 — Broadcast, Bugs, Imagenes y un Dominio

Dormi cuatro horas. Me desperto un mensaje de WhatsApp: "bro la app no encuentra mi node".

El bug de nvm

El tester tenia Node instalado via nvm en un path no estandar: ~/.nvm/versions/node/v22.11.0/bin/node. Macchiato buscaba node con which, que en su sistema devolvia basura porque nvm no exporta al PATH global hasta que ejecutas nvm use.

Fix: detection cascade. Primero which node, despues command -v node, despues find ~/.nvm -name node -type f, despues escanear paths comunes. Si nada funciona, la UI te dice "Node no encontrado" en vez de morir en silencio como un animal herido.

El bug de zsh

Otro tester usaba zsh con oh-my-zsh y un prompt custom de tres lineas con colores, iconos y el signo del zodiaco probablemente. El parser de salida del PTY de Macchiato se ahogaba con las secuencias de escape ANSI. La terminal mostraba glifos corruptos y parpadeos.

Tuve que reescribir el parser ANSI desde cero. No con regex. Con una tabla de secuencias de escape completa: CSI, OSC, DCS, todas. Cada secuencia mapeada a su accion: mover cursor, cambiar color, limpiar linea, hide cursor, show cursor. El parser nuevo ignora secuencias cosmeticas y solo procesa las que afectan el contenido real.

Tres horas de leer documentacion de terminales de 1987. Pero ahora Macchiato corre en bash, zsh, fish y lo que sea que usen los que personalizan su prompt con emojis del clima.

El bug del grid

Un tercer tester — el que tiene setup de tres monitores — reporto que el grid de terminales se desincronizaba al redimensionar la ventana. Movias el borde del panel izquierdo 20 pixeles y la terminal de la derecha se comprimia como un acordeon. Las sesiones PTY internas no recibian el nuevo tamaño de columna.

Fix: sistema de eventos de resize que propaga dimensiones de cada celda del grid a su sesion PTY hija en tiempo real. Cada redimensionamiento dispara SIGWINCH virtual en la sesion correcta. El grid no se mueve, las terminales no se deforman, los tres monitores del tester dejaron de sufrir.

Cada bug fue un puñal. Cada fix fue una medalla. Asi se construye software que la gente usa: con feedback de gente que no te tiene miedo.

Broadcast de comandos

Entre fix y fix, uno de los testers me tiro una idea que deberia haber sido obvia: "tengo tres proyectos abiertos en grid. Por que tengo que escribir git pull tres veces?"

Broadcast mode: un toggle en la toolbar. Lo activas, y cada tecla que escribis se manda simultaneamente a todas las terminales abiertas en el grid. Como tmux synchronize-panes, pero cross-project. git pull en tres repos distintos, un solo comando. npm install en frontend y backend simultaneo. docker compose up en dos stacks.

Lo implemente en 40 minutos. La primera prueba fue git status en tres proyectos distintos. El tester me mando un audio de WhatsApp de diez segundos que era solo risa. Ese audio vale mas que cualquier metrica de adopcion.

Pegado de imagenes

Claude Code y OpenCode aceptan imagenes si las pasas por path. Pero el workflow tradicional es ridiculo: guardas screenshot, recordas el path, escribis el path en la terminal, reza porque no tenga espacios. Tres pasos y una oracion.

En Macchiato ahora copias una imagen al portapapeles, haces Ctrl+V en el prompt de cualquier terminal, y la imagen se guarda en un directorio temporal, se referencia automaticamente en el comando, y se limpia al cerrar la sesion.

Flujo nuevo: ves un bug, haces screenshot (Shift+Cmd+4 en Mac, Win+Shift+S en Windows), volves a Macchiato, Ctrl+V, escribis "que esta pasando aca", Enter. Claude ve la imagen y el contexto. Ni un path, ni un comando extra.

Esto no es un feature. Es eliminacion de friccion.

Metricas Parte 2 — La Venganza

El parser del Dia 4 era exacto. Para requests individuales. Pero los testers — benditos testers — encontraron los edge cases que yo no vi a las 2 AM:

Streams truncados. Cuando Anthropic rate-limita un request a la mitad del stream, no hay evento final con metricas. Macchiato mostraba "0 tokens" para ese request. Mentira. El request gasto tokens, solo que Anthropic no te dijo cuantos. Fix: detectar stream truncado y mostrar metrica como "incompleta" con un indicador visual. No muestro un numero falso.

Errores sin body. Rate limit HTTP 429, server error 500, timeout. Ninguno incluye metricas. Macchiato mostraba el costo en cero porque no habia dato. Fix: mapeo de codigos HTTP a estado de metrica. Si la API dijo 429, la UI muestra "rate limited" en rojo, no un cero mentiroso.

Sesiones largas. Un tester dejo Macchiato abierto toda la tarde y ejecuto 63 requests. Al request 40, el sidebar empezo a trabarse. Cada request nuevo disparaba un recalculo completo de totales de sesion — sumar 40 requests cada vez que llega uno nuevo es O(n^2). Fix: cache de agregacion incremental. Cada request nuevo suma sus metricas a un acumulador. El sidebar lee el acumulador, no recalcula. Tiempo de render despues del fix: menos de 3ms con 100+ requests en sesion.

Probe con una sesion sintetica de 100 requests seguidos, alternando modelos y provocando errores a proposito. El sidebar no pestañeo. Las metricas exactas. Esta vez de verdad.

macchiato.sh

A las 9 PM del Dia 5, entre un fix y una prueba, abri Namecheap. Busque macchiato.sh. Nadie lo habia registrado. Un dominio .sh, limpio, corto, memorable. Catorce dolares al año.

En dos horas tenia una landing page minima montada: nombre de la app, que hace, estado del proyecto, y un campo de email para los que quieran probar la beta apenas salga. Nada de templates. Nada de animaciones. HTML pelado con la misma paleta brutalista de la app. Coherencia visual de punta a punta.

Macchiato ya tiene casa: macchiato.sh.


Lo Que Viene: Contenedores, Clusters y Escala Real

Tres dias. Brutalismo quirurgico. Metricas domadas. Broadcast. Imagenes. Bugs de calle aplastados. Dominio propio.

Pero esto no termina. Lo que sigue es mas ambicioso:

  • Docker dentro de Macchiato. Ejecutar contenedores, inspeccionarlos, ver logs, hacer troubleshoot sin salir de la app donde ya tenes tus agentes de codigo. Sin saltar entre terminal y Docker Desktop como un canguro.
  • Kubernetes. Cambio de contexto entre clusters con un atajo. Logs de pods en paneles laterales. Port-forward con un boton. Lo que todo dev que trabaja con K8s necesita a diario y ninguna herramienta de terminal te da integrado con tus agentes de IA.
  • Beta publica. En macchiato.sh, pronto. Los primeros en registrarse van a ser los primeros en romperla. Y necesito que la rompan. Porque cada bug que encuentran ahora es uno que no encuentra un usuario solo y frustrado dentro de tres meses.

Un Cafe Para Mantener Esto Vivo

Voy a ser directo: Macchiato nacio para ser gratis. No voy a ponerle paywall. No voy a esconder features detras de un plan "enterprise". No voy a cobrarte por usar tu propio dinero en APIs de IA.

Pero macchiato.sh cuesta. Mi tiempo cuesta. Y lo que viene — Docker, K8s, testing en Windows, Mac y Linux, builds firmadas, infraestructura de distribucion — cuesta mas.

Si Macchiato te sirve, si te ahorra horas, si te hace la vida mas facil en esa terminal que miras a las 2 AM igual que yo, considera tirar unos dolares. Pronto voy a habilitar donaciones en macchiato.sh. No es una suscripcion. No es un SaaS escondido. Es un cafe. Literal. Para que el proyecto siga siendo gratis para todos.

Mientras tanto, si queres estar ahi cuando la beta salga: macchiato.sh. Deja tu email. Los primeros van a tener bugs. Pero van a ser nuestros bugs.


Guayoyo Tech

Macchiato es un proyecto independiente, pero nacio dentro de Guayoyo Tech. Si tu empresa necesita integrar agentes de codigo en su stack de desarrollo — con metricas reales, con control de costos, sin depender de un solo proveedor de IA — podemos ayudar.

Hablemos — Primera consultoria gratuita de 30 minutos.

Top comments (0)