Wu Wei

rtk: cortar ruido sin cortar señal

· #herramientas

Hace unos días rtk iba segundo en lo más visto de GitHub. La promesa: un proxy de línea de comandos que recorta 60-90% de los tokens que un asistente de código gasta leyendo git log, ls, salidas de build, tests. Un binario en Rust, sin dependencias, que se interpone entre el comando y lo que llega al contexto del modelo.

Antes de instalar nada, algo no cuadraba: el mismo texto de descripción — palabra por palabra — aparecía copiado en media docena de repositorios distintos, de cuentas distintas. Patrón típico de spam inflando visibilidad, o peor, de binarios maliciosos disfrazados de herramienta de moda.

El repositorio original (rtk-ai/rtk) resultó limpio: código Rust real, licencia Apache 2.0, changelog y tests genuinos. El script de instalación verifica el checksum SHA-256 del binario, rechaza rutas de archivo inseguras al descomprimir, y no pide sudo ni credenciales. Los repos duplicados eran ruido alrededor, no el proyecto en sí — pero mirar antes de ejecutar un curl | sh sigue mereciendo la pena, venga de donde venga.

Lo simple funciona

Instalado, lo probé primero contra este mismo repositorio: comandos reales, no benchmarks ajenos. git log --stat se reduce 66.8%. Un ls -la verboso en node_modules, 82.8%. Un docker ps, 65%. Donde hay ruido de verdad — cabeceras, whitespace, líneas repetidas — lo corta bien, y el coste de filtrar es de milisegundos. Donde ya no había ruido (un build de Astro de cuatro páginas, un grep con dos resultados), el ahorro es 0%. Nada raro hasta aquí: hace lo que dice, cuando la entrada es la que espera.

Subir la apuesta

Un repo de cuatro páginas no prueba nada a escala. Así que cloné facebook/react (500 commits reales) y corrí dos herramientas de seguridad que rtk no soporta de forma nativa: gitleaks y codeql. Para eso existen sus comandos genéricos documentados — rtk err y rtk summary, pensados para envolver "cualquier comando" — así que no es mal uso, es la vía oficial.

gitleaks encontró 5 posibles secretos en el historial (los cinco resultaron falsos positivos reales — un token de GitHub marcado en el propio código como "público a propósito", y una clave de test repetida). rtk err redujo esa salida a un tamaño cómodo, pero mostrando solo 1 de los 5 hallazgos — los otros 4 quedan en un log en disco que nadie consulta salvo que sepa que existe.

Con codeql la escala cambia de orden de magnitud: 4471 archivos analizados, 1505 hallazgos reales, 61 de ellos etiquetados como seguridad (inyección de código, filtración de información entre ventanas). El SARIF resultante pesa 5.65 MB.

json
{
  "runs": [
    {
      "results": [
        {
          "message": { "text": "[Sensitive data] is sent to another window without origin restriction." },
          "rule": { "id": "js/cross-window-information-leak" }
        }
        // ... +1504 more
      ]
    }
  ]
}
`rtk json` sobre el SARIF real de codeql: 5,650,950 bytes de entrada, 2,689 de salida.

Un ejemplo, y "+1504 more". Nadie pegaría 5.65 MB de SARIF crudo en un LLM — hay que comparar contra lo razonable, no contra lo absurdo. Pero "usar rtk para no gastar tokens en output verboso" es exactamente la recomendación que hace su propia documentación para cualquier comando ruidoso. Un agente que la sigue al pie de la letra, sobre el resultado de un escáner de seguridad, ve 1 de 1505 problemas y da la tarea por terminada.

El mismo patrón, tres veces

Monté un test de Vitest con dos fallos a propósito. rtk vitest no llegó a correrlo: el proyecto usaba pnpm, y un aviso benigno de pnpm (scripts de build sin aprobar) hizo que rtk abortara antes de ejecutar nada. Arreglado ese aviso, rtk vitest sí corrió — pero el resultado pesó más que el crudo (4703 contra 1754 bytes): expandió rutas internas de node_modules que el reporter de Vitest ya recorta por defecto.

En React probé lo mismo a escala real: un test roto de verdad, dentro de su propio wrapper (yarn test → un script en scripts/jest/jest-cli.js). rtk jest no lo entendió — intentó invocar Jest por su cuenta, ignorando el wrapper del proyecto, y falló entero sin llegar a los tests. El wrapper genérico (rtk test yarn test ...) sí ejecutó el comando real, pero perdió el porqué: mostró qué dos tests fallaron, no el mensaje de error ni el stack trace que hacía falta para arreglarlos. Añadí violaciones de ESLint reales al mismo archivo y probé rtk lint — otra vez ignoró el wrapper propio de React (scripts/tasks/eslint.js), lanzó ESLint en crudo sobre el monorepo entero en vez del archivo afectado, y el parseo del JSON resultante falló.

Tres herramientas distintas (test runner, linter, en dos proyectos distintos), el mismo fallo: cuando el proyecto envuelve su comando con un script propio — algo muy común en repos grandes — rtk no lo detecta.

Lo que dice su propia documentación

Antes de escribir nada de esto como hallazgo, tocaba comprobar si ya estaba avisado. La web de rtk lista con precisión qué comandos optimiza — jest, vitest, eslint están todos en esa lista — y dice explícitamente que lo no listado "va por passthrough, sin cambios". Pero lo que rompimos no es el caso "no soportado": es un fallo del analizador en comandos que ellos mismos dicen soportar, cuando el proyecto los invoca a través de su propio wrapper. Su página de troubleshooting no menciona wrappers, monorepos, ni scripts personalizados de ningún tipo.

Buscando en sus Pull Requests apareció la explicación: hay soporte "inteligente" para yarn y para pnpm run <script> en desarrollo activo, todavía sin fusionar a main. El PR de yarn (#867) cita telemetría real: 126 comandos yarn sin manejar en 615 sesiones de 30 días — yarn lint 19 veces, yarn vitest 14. Y en la revisión de ese mismo PR, uno de los maintainers encontró, probando contra un proyecto real (babel/babel), un fallo de la misma familia que el nuestro: un yarn install que fracasó de verdad (código de salida 1, build de workspace roto) se colapsó a la palabra "ok". El código de salida se preservó — pero un agente viendo "ok" junto a exit 1 no tiene forma de saber qué se rompió sin volver a correr el comando en crudo.

El contador no mide lo que crees

rtk lleva su propio panel de ahorro (rtk gain). Su guía de uso publica la fórmula exacta: los tokens de entrada son len(salida_cruda_del_comando) / 4 — pero "salida cruda" significa lo que rtk mismo capturó al ejecutar, no lo que produciría el comando real del proyecto medido aparte. En la línea de rtk lint sobre React, el panel registró ~244.000 tokens de entrada — muchísimo más que el yarn lint real (1155 bytes) — porque lo que de verdad ejecutó fue ESLint sobre el monorepo entero por el fallo de detección. El 99.8% de "ahorro" que reporta es real, matemáticamente, contra ese comando equivocado que él mismo generó. No se puede comparar contra lo que un desarrollador obtendría tecleando yarn lint, porque nunca llegó a correr eso.

La línea del SARIF de codeql es distinta: ahí sí hay comparación directa, mismo archivo de entrada. El 100% de ahorro que certifica el panel es, literalmente, el mismo hecho que "esconde 1504 de 1505 hallazgos" — no es interpretación, es la misma reducción de bytes vista desde dos ángulos.

Lo que ya sabían otros

Dos issues abiertas en el repositorio, sin resolver, confirman que esto no es un caso aislado de esta prueba:

#640
Revisión de seguridad automatizada, abierta

Shell injection crítica: rtk err/test/summary pasan el comando a sh -c sin escapar, y el hook auto-aprueba cada reescritura sin pedir permiso. Además: el filtro global (~/.config/rtk) no lleva verificación de integridad — el propio informe dice que podría usarse para "esconder hallazgos de vulnerabilidades al LLM". Y en algunos casos rtk no propaga el código de salida de un test fallido: Claude Code puede leer éxito donde hubo fallo.

#590
"Deceptive and inaccurate reporting?", abierta

Un usuario, sin relación con esta prueba, señala que el "60-90%" solo cubre comandos de terminal — no las herramientas Read/Grep/Glob que dominan la mayoría de sesiones — y calcula que el ahorro real sobre una sesión completa probablemente ronda el 10%, no el titular.

El hook, de verdad

La vía "correcta" de integración no es escribir rtk a mano delante de cada comando: es un hook (rtk init -g) que reescribe los comandos de Bash de forma transparente. Lo instalé en mi configuración real de Claude Code — parcheó settings.json con una copia de seguridad automática, añadió un RTK.md de diez líneas, nada invasivo — y funcionó exactamente como está documentado, sin reiniciar nada. Dado el hallazgo de shell injection de la issue #640, que afecta justo a ese hook, lo desinstalé después de probarlo.

Conclusión

Nada de esto dice que rtk sea inútil. En los casos que soporta bien — git, ls, docker, comandos invocados de forma directa — corta ruido real sin coste apreciable. Pero el titular "60-90%" no sobrevive intacto a mirar debajo, y no es lo único que importa a la hora de decidir si algo se instala o no:

Ahorro prometido vs. medido en sesión completa 10% · anunciado 90%

90% es el techo que anuncia el README. 10% es la estimación de un usuario independiente en la issue #590 sobre una sesión real completa — cuadra con lo que vimos aquí: gana mucho en unos pocos comandos concretos, nada en el resto de la sesión.

Fiabilidad cuando algo falla o hay que revisar hallazgos 20%

Escondió información real tres veces: 4 de 5 hallazgos en gitleaks, 1504 de 1505 en codeql, y — documentado por el propio equipo — un `yarn install` fallido colapsado a la palabra "ok".

Actividad del proyecto 90%

Release cada pocos días, un candidato a la 0.44.0 publicado 3 días antes de esta prueba, 68.6k estrellas. No está abandonado ni de lejos.

Seguridad 20%

Shell injection crítica sin resolver (issue #640), abierta desde marzo — unos cuatro meses sin arreglo al momento de escribir esto.

Soporte y triage 45%

Revisión de PRs seria, con bench-tests reales de los maintainers antes de fusionar nada. Pero issues de seguridad y de honestidad en las métricas llevan meses abiertas sin respuesta sustancial.

Documentación 70%

Guía extensa en siete idiomas, changelog cuidado — pero el troubleshooting no menciona el fallo de wrappers que encontramos en tres herramientas distintas.

El ahorro depende de qué comando, de si el proyecto lo envuelve con un script propio, y el propio contador de la herramienta no está diseñado para avisarte cuando algo salió mal. La diferencia entre creer un número y medirlo tú mismo — con el comando real, a la escala real, contrastado contra la documentación, los PRs y las issues del propio proyecto — es exactamente ese trabajo el que vale la pena antes de escribir sobre cualquier herramienta.