Deja de Añadir un CLI a Tu App Como Algo Secundario. Hazlo el Kernel de Tu Agente.
CLI es el nuevo MCP. Eslogan aparte: CLI son superpoderes entregados a Claude, Codex y cada agente que programa por ti. Permitir que los LLMs de código verifiquen su propio trabajo programáticamente les da una ventaja injusta sobre las apps fullstack clásicas que no incluyeron esa superficie.
CLI es el nuevo full stack.
TLDR: Dos apps, mismo stack moderno, una brecha de 1.8x en commits enviados durante 30 días. La brecha no viene de la IA, el framework o el backend, sino de una capa que las guías de "stack 2026" olvidaron, y que tu carpeta scripts/ no va a reemplazar.

El viernes por la noche antes de irme a la Costa Brava, quería enviar una cosita antes de cerrar la laptop (sin que mi mujer me gritara).
Dos ventanas de Claude Code lado a lado. El mismo prompt a cada una. Dos apps diferentes, mismo stack al 95%.
Para cuando cerré la laptop, una app había enviado una funcionalidad en tres iteraciones autónomas. La otra me tenía haciendo clic en el admin como un junior tratando de debuggear producción desde el teléfono. Mismo agente, mismo yo, una carpeta de diferencia.
Mismo Stack, Una Carpeta de Diferencia
Primera app, ventana izquierda. Claude Code escribe la mutación, tipea un comando en su terminal, lee el JSON que regresa, detecta que un campo está mal casteado, lo arregla, vuelve a tipear. Tres iteraciones en total autonomía. Regreso, le digo "que tengas buen fin de semana". El commit está listo, validado al 100%.
Segunda app, ventana derecha. Claude Code escribe la mutación, luego se detiene. Me hace ping. ¿Puedes abrir el admin y verificar que esto funciona? Hago clic. Refresco, luego vuelvo a hacer clic. Re-ping. Mi mujer empieza a alzar la voz. Da igual, lo vemos el lunes. Si no, habría pasado toda la noche siendo el mouse de un agente que se suponía que programara en mi lugar.
Mismo stack, mismo yo, mismo Claude Code en ambas. La única diferencia cabe en una carpeta.
Las dos apps son mías. Una es un back-office que sincroniza un catálogo de WooCommerce a través de una API de partner cada noche, más un feed CSV semanal de un distribuidor. La otra es un back-office que pilotea una red de e-shops cliente de WooCommerce (deploys, actualizaciones de tema, sync de plugins, lo típico de fleet). Ambas construidas desde hace seis meses. Ambas corriendo Next.js, Convex, shadcn, Vercel. Mismo CLAUDE.md, mismas convenciones.
Una tiene un CLI como su capa central. La otra tiene una carpeta scripts/.
Eso es todo. Esa es toda la brecha.
Por "CLI" me refiero a un entrypoint real con sub-comandos nombrados según acciones de negocio (catalog refresh, partner sync, site init), conectado a la misma capa de negocio que usa el dashboard. Tipeas bun run cli partner sync --dry-run, y el mismo código que corre cuando un admin hace clic en "Sync" se ejecuta, excepto que devuelve JSON a stdout.
La otra app no tiene nada de eso. Solo archivos .mjs con nombres como fix-thing-2025-08.mjs (admítelo, tú también tienes una carpeta así). Cada uno escrito para "pasar una vez". La mayoría nunca corrió una segunda vez.
Esa es toda la diferencia. Y cambió cómo trabajó el agente en todos los niveles.
30 Días de Commits No Mienten: 1.8x Más Funcionalidades, Mitad de Fixes
Revisé el historial de git de ambos repos durante la misma ventana de 30 días en mayo.
La app-CLI envió 272 commits. La app-scripts envió 150. Eso es una proporción de 1.8x, con el mismo yo, mismo agente, misma rutina diaria.
Dentro del repo CLI, cada sub-comando fue tocado al menos una vez durante la ventana. 100%. Dentro de la carpeta scripts/, solo el 29% de los archivos vio alguna actividad. El resto estaba inactivo. El 41% de todos los archivos de script en la app-scripts habían sido escritos, ejecutados una vez, y nunca abiertos de nuevo. El más viejo que encontré que encaja en este perfil no había sido tocado en 57 días. Lo había olvidado completamente hasta que fui a buscarlo.
Hay un número más que es interesante, pero quiero marcarlo como hipótesis, no como prueba. Mirando los mensajes de commit etiquetados fix: versus feat:, la app-CLI tenía una proporción fix-to-feat de 0.44. La app-scripts estaba en 0.82. Aproximadamente el doble de commits de fix por funcionalidad en el lado sin CLI.
No puedo probar que el CLI cause esa brecha. Las dos apps tienen diferente madurez de dominio, diferente complejidad, diferente cobertura de casos edge. La mitad de la diferencia podría venir del hecho de que el back-office para sitios cliente es simplemente más viejo y más delicado que el de la API de partner. Pero la brecha es consistente con lo que observo diariamente, y coincide con la brecha de autonomía que describí en la intro: el agente envía trabajo más limpio cuando tiene una forma de verificarse a sí mismo, así que menos regresiones se cuelan.
La tasa de huérfanos (41% versus 0%) y la brecha de velocidad (1.8x) no son hipótesis. Esas las puedo leer directamente de git log.
El Mecanismo Real: Los Agentes Necesitan una Superficie Estructurada en Texto
La razón por la que la app-CLI produce iteraciones autónomas mientras que la app-scripts produce ping-fests no tiene nada que ver con calidad de código o tamaño de modelo.
Se trata de la superficie que tiene el agente para validar su propio trabajo.
Piensa en lo que Claude Code realmente hace en un loop de funcionalidad. Escribe código, luego necesita saber si el código hace lo que se suponía que hiciera. Si la única forma de verificar es "abrir el dashboard, hacer clic por ahí, mirar la pantalla", el agente no puede hacerlo. Los navegadores devuelven DOM. DOM sin un ojo humano para interpretar lo que se renderiza es opaco para un agente. Los colores, los estados de carga, el modal que apareció, el mensaje de validación abajo, todo eso es significativo para una persona y ruido para un modelo. El agente no tiene ground truth, así que se detiene y te pregunta.
Un CLI devuelve texto. JSON, stdout estructurado, códigos de salida. Cosas que un agente puede leer, parsear, razonar sobre ellas. El agente ejecuta el comando, lee la salida, ve que partnerStatus: "rejected" significa que la mutación no pasó, arregla el código, ejecuta de nuevo. Sin humano en el loop. La señal de feedback es nativamente legible para el modelo.
Ese es todo el principio. Superficie estructurada en texto igual a agente autónomo. Superficie solo-DOM igual a agente que te hace ping en cada iteración.
Por esto también funcionan los servidores MCP, APIs REST, endpoints tRPC, GraphQL para agentes que llaman a tu servicio. Todas son superficies estructuradas en texto. Un CLI es simplemente la encarnación más simple y local de este principio para el agente que está programando tu propia app. No llamando a un servicio remoto. Escribiendo código en tu repo y necesitando probarlo ahora.
Puedes simular esto con Playwright apuntando a tu dashboard. La gente lo hace. Funciona, más o menos. También cuesta una desaceleración de 10x, una capa de retry frágil, y un paso de comparación de screenshots que se rompe cada vez que envías un cambio de UI. Un CLI devuelve la misma respuesta en milisegundos sin fragilidad, porque el texto era siempre lo que el agente quería en primer lugar.
El Stack 2026 Olvidó una Capa (Y Toda Herramienta de AI Code Gen También la Omite)

Ve a leer cualquier guía de "mejor stack para lanzar tu herramienta programada por IA" escrita entre febrero y abril de 2026. KDnuggets, Idlen, Context Studios, MindStudio, puedes elegir una al azar. Todas convergen en las mismas seis o siete capas. Next.js para el frontend. shadcn para el kit de UI. Supabase o Convex para el backend. Clerk para auth. Stripe para pagos. Resend para email transaccional. Vercel para hosting. Algunas agregan Tailwind, OpenAI, Claude, Gemini.
Hay al menos 50 de esas guías publicadas en los últimos tres meses. Ninguna menciona un CLI para tu app.
Mismo punto ciego en el lado de IA. Cursor, v0, Bolt, Lovable, Claude Code mismo cuando scaffoldea un proyecto nuevo. Todos generan un frontend, un backend, una config de hosting. Cero de ellos genera un CLI como capa de primera clase. Si le pides a Claude Code "configura una app Next.js con Convex y Stripe", obtendrás esas tres cosas y nada más. El CLI, si acaso, aparecerá después como scaffolding (next dev, convex dev) y eso es todo.
Esto no era un problema en 2020. En 2020, escribías tu propio código, y tu IDE era tu loop de feedback. F5, F12, console.log, console.log, console.log. El DOM estaba bien porque eras tú quien lo leía.
En 2026, no eres tú quien escribe la mayoría del código. El agente lo hace. Y el agente no tiene ojos.
Un stack 2026 sin capa CLI fuerza al agente a depender de ti para cada iteración. El agente escribe una mutación, tú haces clic en el admin, le dices al agente si funcionó. El agente escribe un trabajo de sync, tú haces tail -f a los logs, le dices al agente lo que viste. Cada loop de funcionalidad te tiene como el nodo intermedio obligatorio. Crees que estás prompting a un agente para que envíe por ti, en realidad estás jugando intermediario de navegador para el agente.
La cuarta capa surge de un hecho: si quieres que el agente envíe autónomamente, necesitas darle una superficie que pueda leer.
El artículo de Idlen argumenta que elegir el backend equivocado significa reescribir tus modelos de datos a las 2am. Sí, y es peor si no tienes un CLI, porque los estás reescribiendo a mano en lugar de ejecutar bun run cli model migrate.
Por Qué los Scripts se Pudren y los CLIs Viven
La tasa de huérfanos del 41% no viene de la pereza. Viene del hecho de que una carpeta scripts/ no te pide nada arquitectónicamente.
Escribes scripts/migrate-orders-2025-04.mjs porque tienes una emergencia. Lo ejecutas una vez. Funciona. Lo commiteas (o no, dependiendo de qué tan pánico tenías). Tres semanas después, otra emergencia. Escribes scripts/migrate-orders-fix.mjs. Mismo problema, nombre ligeramente diferente. No reutilizas el primero porque no recuerdas que existe. No hay scripts/ --help. Solo hay un ls.
Toda la carpeta termina como el archivero de Karen de Contabilidad: técnicamente organizado, prácticamente inutilizable. Todo está "ahí", nadie sabe dónde, incluso Karen dejó de buscar.
Un CLI fuerza una forma diferente. No puedes agregar partner sync como sub-comando sin registrarlo en el entrypoint, lo que significa que ves todos los otros sub-comandos cada vez que agregas uno nuevo. La descubribilidad está integrada en la herramienta. Los nuevos sub-comandos heredan las mismas flags (--dry-run, --limit, --verbose), el mismo logger, el mismo manejo de errores. La idempotencia se vuelve fácil porque ya estás pasando por una capa de negocio compartida que el dashboard también usa.
Por eso la tasa de tocados está al 100% en el lado CLI. No soy más disciplinado cuando uso un CLI. El CLI es simplemente arquitectónicamente hostil al código desechable de una manera que scripts/ nunca es.
Y --help está haciendo más que ayudarte. Es el entrypoint para cualquier agente que aterriza en tu repo. Claude Code tipea bun run cli --help una vez y ahora conoce cada acción de negocio que puede disparar, con sus flags y su descripción. Sin prompt engineering, sin doc que alimentar. El CLI se documenta a sí mismo, a humanos y a agentes al mismo tiempo. Eso es lo que scripts/ nunca te dará, sin importar qué tan limpios sean tus nombres de archivo.
Caveat que debería poner aquí mismo, mientras me jacto. Mi propio CLI tiene una debilidad real. De 14 sub-comandos, 11 no tienen descripción en la salida de --help. Eso es 79% de mis comandos apareciendo como nombres desnudos sin explicación. El CLI forzó disciplina de ejecución. No forzó disciplina de documentación. Claude Code aún puede descubrir cada comando, parsear la salida JSON, y usarlo. Un dev junior abriendo el repo por primera vez tendría que leer el source. Lo estoy arreglando lentamente, pero la lección permanece: la arquitectura resuelve el problema de ejecutar, no el problema de enseñar. Aún tienes que escribir los docstrings.
Tu App Ya es Agéntica por Accidente
Lo que nadie te dice en las guías de stack-2026: un CLI que comparte la capa de negocio con tu UI hace tu app nativamente agent-ready. No como un producto separado. Como un efecto secundario gratuito.
Tres formas concretas de exponer tu CLI a un agente que no está sentado en tu IDE.
Envuélvelo como servidor MCP. Tal vez 50 líneas de TypeScript. Escribes un servidor MCP delgado que registra cada sub-comando de tu CLI como una herramienta MCP. El input de la herramienta mapea a flags CLI. El output de la herramienta es el JSON que el CLI ya devuelve. Boom, cualquier cliente MCP (Claude Desktop, Cursor, cualquier cosa que hable MCP) puede llamar tu CLI como herramienta nativa. Envolviste tu CLI existente y lo llamaste servidor MCP.
Cron más agente. Un scheduler ejecuta bun run cli catalog refresh cada seis horas. La salida JSON va a una tabla Convex. Un agente de fondo lee la última fila, decide si el refresh tuvo un error de partner, y si es así dispara un follow-up bun run cli partner reconnect. Sin navegador. Sin humano. El agente toma decisiones basadas en texto que el CLI emite, luego dispara más comandos CLI. Acabas de convertir tu back-office en un loop auto-sanador.
Gateway HTTP shell-out. Expones un endpoint diminuto de Express o Hono que toma un nombre de comando CLI más args, hace shell-out al CLI, devuelve el JSON. Autenticado por supuesto. Ahora cualquier agente externo que hable HTTP puede manejar tu app. Sin SDK que mantener. El CLI es el SDK.
Ninguno de esos tres pide un refactor de tu lógica de negocio. Son capas de exposición puras encima de código que ya escribiste. Un stack, dos modos: dashboard para humanos, CLI para agentes. El dashboard no sabía que tenía un gemelo. Ahora sí.
Los Tres Patrones de Integración (Elige Uno, Elige Bien)
Si vas a hornear un CLI en tu stack, hay tres formas de conectarlo. Solo una de ellas te da la brecha de autonomía que describí antes.
Patrón 1: CLI comparte la capa de negocio con la UI. El botón "Sync partner" del dashboard llama una mutación Convex. El comando CLI partner sync llama la misma mutación, con el mismo schema Drizzle, mismos tipos TypeScript end-to-end. Mismas garantías de idempotencia. Mismo audit log. Este es el que quieres. Todo lo que he estado describiendo asume este patrón. (Convex se empareja particularmente bien con Claude Code para exactamente esta configuración, porque la API tipada end-to-end hace el CLI un wrapper delgado alrededor de mutaciones en lugar de una implementación paralela de ellas.)
Patrón 2: CLI como cliente HTTP de tu propia API. El CLI llama tus endpoints REST o tRPC. Más fácil de aislar, agnóstico de lenguaje, puedes enviar el CLI a clientes que no ejecutan tu monorepo. Pero pierdes los beneficios de tipado, tienes que manejar auth manualmente, y la idempotencia depende de quien escribió el endpoint. Aceptable como fallback si tu backend está en un repo diferente que tu consumidor CLI. No óptimo.
Patrón 3: CLI DevOps, separado de la app. Comandos de deploy, scripts de backup, herramientas de monitoreo. Útil, pero no es un sustituto. Si tu app vive, también necesitas Patrón 1 o 2 junto a él. Patrón 3 solo es lo que la mayoría de equipos envía y lo que se confunde con "tenemos un CLI". Es solo un script de deploy.
Veredicto: Patrón 1 es el único que devuelve la brecha de velocidad. Patrón 2 es la mitad del trabajo por una fracción del beneficio. Patrón 3 es plomería de hosting disfrazada de CLI.
Si solo puedes construir un patrón, construye el primero.
Tooling: cac vs citty vs el Resto en 2026
Repaso rápido de lo que realmente vale la pena usar para construir el CLI mismo, ya que aquí es donde la mayoría se atasca por un fin de semana.
cac es mi default. Cerca de 2 KB, cero dependencias, ESM-first. Si tu CLI tiene menos de 20 sub-comandos, esta es la herramienta correcta. Lo suficientemente pequeño que no piensas en él, y Claude Code genera código cac limpio al primer intento.
citty de la gente de UnJS es la elección ascendente para 2026. Type-safe, sub-comandos lazy-loading (importa cuando empiezas a llegar a 30+), ESM-first, juega bien con Nitro y el resto del mundo UnJS. Migra a él cuando tu CLI crezca más allá de donde cac se siente apretado.
commander es la opción madura legacy. Estable, bien documentado, hará el trabajo, pero la API se siente más vieja y el bundle es más pesado de lo necesario. Elígelo solo si tu equipo ya lo conoce.
clipanion tiene sabor OOP, usado por Yarn. Bueno si te gustan las clases y quieres tipado estricto. Nicho.
oclif está sobre-arquitecturado a menos que tu CLI mismo sea el producto (piensa Heroku, Salesforce). Para un CLI que soporta una app, oclif está trayendo una grúa para mover un sofá.
Para el resto de la experiencia, quieres clack para prompts (TUI hermoso, muy reciente), picocolors para colores (más pequeño y rápido que chalk ahora), consola para logging, listr2 si tienes tareas multi-paso con barras de progreso, y bun shell o zx para scripts embebidos.
Empieza en cac. Migra a citty cuando cruces 20 sub-comandos.
No lo sobre-pienses.
Cuándo el CLI Faltante Duele (Cuatro Escenarios)
Cuatro momentos donde la ausencia de un CLI te cuesta específicamente, en caso de que el argumento abstracto no haya aterrizado.
Onboarding de un nuevo e-shop cliente. Sin CLI, cada nuevo cliente son dos a tres horas de hacer clic en el admin: provisionar dominio, configurar tema, instalar plugins, sembrar catálogo, configurar DNS. Multiplica por diez clientes en un mes. Con CLI, site init shop.example.com ejecuta toda la secuencia en cinco minutos. El agente puede ejecutarlo por su cuenta cuando un webhook de Stripe dispara "nuevo cliente".
Fix de datos recurrente. Un partner a veces devuelve precios malformados en su API. Sin CLI, cada incidente significa reescribir la mutación de fix a mano, o escarbar en scripts/ para encontrar "el que funcionó la última vez". Con CLI, tienes bun run cli prices reconcile --dry-run, idempotente, versionado, documentado en --help. El agente lo invoca él mismo cuando la alerta se dispara.
Auditoría durante incidente. Algo se rompió en prod, necesitas saber qué órdenes fueron afectadas. Sin CLI, haces grep por scripts/ buscando "esa cosa de auditoría que escribí en marzo". Con CLI, cli orders audit --since=2026-04-01 existe, está documentado, y el agente puede ejecutarlo mientras aún estás tipeando en Slack.
Refresh de datos externos. Cron tiene que refrescar un catálogo de partner cada noche. Sin CLI, el cron apunta a node scripts/old-thing.mjs y el archivo lentamente se desincroniza del schema, hasta que un martes falla silenciosamente por 48 horas antes de que alguien se dé cuenta. Con CLI, el cron apunta a bun run cli partner refresh, que comparte la misma capa de negocio que el dashboard, así que un cambio de schema rompe el cron en el próximo deploy en lugar de en medio de la noche.
Mismos cuatro problemas. El CLI hace cada uno aburrido.
La Prueba de 30 Segundos que Tu Stack Tiene que Pasar Hoy
Abre tu terminal. cd a tu repo. Tipea bun run cli --help (o yarn cli --help, o npm run cli -- --help, lo que sea tu package manager).
Hay exactamente tres resultados posibles.
Resultado A. No sale nada. O "command not found". O package.json no tiene un script cli. No tienes un CLI. Tienes una UI atornillada a un backend. El agente que programa tu app depende de ti para cada iteración, y la tasa de huérfanos de tu carpeta scripts/ está subiendo lentamente hacia 41% lo midas o no.
Resultado B. Aparece una lista, pero los sub-comandos son cosas genéricas de devops (build, dev, test, deploy) sin acciones de negocio. Tienes scaffolding de devops. Útil, pero el agente puede deployar tu código y no validar que una funcionalidad funciona. Estás en Patrón 3 de los tres patrones arriba. Mitad del viaje.
Resultado C. Aparece una lista con sub-comandos nombrados según acciones de negocio (site init, partner sync, catalog refresh), cada uno con una descripción. Tienes un stack listo para 2026. El agente que escribe tu código tiene una forma de verificarlo. Tu carpeta scripts/ está vacía o tiene menos de cinco archivos. Puedes dejar de leer.
Si obtuviste A o B, aquí es donde empiezas. Elige una o dos acciones de negocio que haces más seguido (las que aparecen en tu carpeta scripts/ bajo tres nombres diferentes), y hazlas los primeros dos sub-comandos de un CLI real. Conéctalos a través de la misma capa de negocio que usa el dashboard. Haz la salida con forma de JSON. Ese es el Patrón 1 más pequeño posible, y cambiará cómo Claude Code trabaja en tu repo para mañana en la mañana.
Ya escribí sobre CLIs como la interfaz para agentes llamando tus herramientas. Este es sobre CLIs como la interfaz para el agente escribiendo tu código desde adentro. Problema diferente, misma capa. El primero es sobre MCP versus CLI como convención de llamada remota. Este es sobre si el agente en tu IDE tiene una forma de enviar.
La próxima vez que empieces una app, decide el día uno si el CLI es un kernel o una ocurrencia tardía. Esa elección decide cuánto tiempo Claude Code pasa programando para ti, versus cuánto tiempo pasas siendo el mouse de Claude Code.
Seis meses construyendo dos apps en paralelo y no me di cuenta de que estaba ejecutando un experimento controlado en mí mismo. Como un viejo head de Linux, ya sabía que CLI le gana a la mayoría de cosas, intuitivamente. Lo que no vi venir fue la parte que más importaba: no solo velocidad y scriptabilidad, sino darle al agente un loop de feedback que pudiera leer por su cuenta.
Claude y Codex no van a sugerir esto por default. Así que díselos tú mismo: hornea una capa CLI como el kernel, día uno.
Me voy, la piña colada está esperando 😎
CLI era la capa todo el tiempo.
Fuentes
- KDnuggets, Tech Stack for Vibe Coding Modern Applications (Febrero 2026)
- Idlen, The Best Stack to Launch Your AI-Coded Tool in 2026 (Abril 2026)
- Context Studios, The Perfect Vibe Coding Tech Stack 2026: 10 Tools Every App Needs (Febrero 2026)
- Auditoría de primera mano, dos de mis propias apps, 30 días de historial git (Mayo 2026)