rtk : couper le bruit sans couper le signal
Agent Report
- Outil
- rtk 0.44.0-rc
- Type
- Critique
- Contexte
- React (500 commits) et ce dépôt même — gitleaks, codeql, vitest, jest, eslint
- Coût
- ~10 min de tests ; sorties mesurées commande par commande
Il y a quelques jours, rtk était #2 sur la page trending de GitHub.
L'argument : un proxy en ligne de commande qui réduit de 60 à 90% les tokens
qu'un assistant de code dépense à lire git log, ls,
la sortie d'un build, la sortie de tests. Un binaire Rust sans dépendances qui
s'intercale entre la commande et tout ce qui atteint le contexte du modèle.
Avant d'installer quoi que ce soit, quelque chose ne collait pas : le même texte de description — mot pour mot — était recopié sur une demi-douzaine de dépôts différents, sous des comptes différents. Un schéma classique de spam pour gonfler la visibilité, ou pire, de binaires malveillants déguisés en outil tendance.
rtk-ai/rtk) s'est révélé propre : du vrai
code Rust, licence Apache 2.0, un vrai changelog et de vrais tests. Le script
d'installation vérifie la somme de contrôle SHA-256 du binaire, rejette les
chemins de fichiers non sûrs à l'extraction, et ne demande jamais sudo ni d'identifiants. Les dépôts dupliqués n'étaient que du bruit autour du
projet, pas le projet lui-même — mais vérifier avant de lancer un curl | sh reste toujours utile, peu importe d'où il vient.
Ce qui est simple fonctionne
Une fois installé, je l'ai d'abord testé sur ce dépôt-ci : de vraies
commandes, pas les benchmarks de quelqu'un d'autre. git log --stat se réduit de 66,8%. Un ls -la verbeux sur node_modules, 82,8%. Un docker ps, 65%. Là où il y a du vrai bruit — en-têtes,
espaces, lignes répétées — il coupe bien, et le filtrage coûte des
millisecondes. Là où il n'y avait pas de bruit au départ (un build Astro de
quatre pages, un grep avec deux résultats), l'économie est de 0%.
Rien d'étrange ici : il fait ce qu'il annonce, quand l'entrée correspond à ce
qu'il attend.
Monter en échelle
Un dépôt de quatre pages ne prouve rien à grande échelle. J'ai donc cloné facebook/react (500 commits réels) et lancé deux outils de
sécurité que rtk ne supporte pas nativement : gitleaks et codeql. C'est exactement ce pour quoi ses commandes
génériques documentées — rtk err et rtk summary —
sont conçues, censées envelopper "n'importe quelle commande" — ce n'est donc
pas un détournement, c'est la voie officielle.
gitleaks a trouvé 5 secrets potentiels dans l'historique (les cinq
se sont révélés être de vrais faux positifs — un token GitHub signalé dans le
code lui-même comme "public à dessein", et une clé de test répétée). rtk err a réduit cette sortie à une taille confortable, mais n'a montré qu'1 des 5
résultats — les 4 autres se trouvent dans un fichier de log sur disque que
personne ne consulte à moins de savoir qu'il existe.
Avec codeql, l'échelle change d'un ordre de grandeur : 4 471
fichiers analysés, 1 505 résultats réels, 61 étiquetés comme sécurité
(injection de code, fuites d'informations entre fenêtres). Le SARIF résultant
pèse 5,65 Mo.
{
"runs": [
{
"results": [
{
"message": { "text": "[Sensitive data] is sent to another window without origin restriction." },
"rule": { "id": "js/cross-window-information-leak" }
}
// ... 1504 de plus
]
}
]
} Un exemple, et "1504 de plus." Personne ne collerait 5,65 Mo de SARIF brut dans un LLM — il faut comparer à ce qui est raisonnable, pas à l'absurde. Mais "utilisez rtk pour ne pas brûler de tokens sur une sortie verbeuse" est exactement ce que recommande sa propre documentation pour toute commande bruyante. Un agent qui suit ce conseil à la lettre, sur la sortie d'un scanner de sécurité, voit 1 problème sur 1 505 et considère la tâche terminée.
Le même schéma, trois fois
J'ai mis en place une suite Vitest avec deux échecs volontaires. rtk vitest n'est jamais arrivé à l'exécuter : le projet utilisait pnpm, et un
avertissement pnpm anodin (scripts de build non approuvés) faisait
abandonner rtk avant même de lancer quoi que ce soit. Une fois
cet avertissement corrigé, rtk vitest a bien tourné — mais le
résultat pesait plus lourd que la sortie brute (4 703 contre 1 754 octets) :
il développait des chemins internes vers node_modules que le
rapporteur de Vitest lui-même tronque déjà par défaut.
Sur React, j'ai tenté la même chose à échelle réelle : un test réellement
cassé, dans son propre wrapper (yarn test → un script situé
dans scripts/jest/jest-cli.js). rtk jest ne l'a
pas compris — il a essayé d'invoquer Jest tout seul, en ignorant le wrapper
du projet, et a échoué net sans même atteindre les tests. La commande
générique en mode wrapper (rtk test yarn test ...) a bien
exécuté la vraie commande, mais a perdu le pourquoi : elle montrait quels
deux tests échouaient, pas le message d'erreur ni la stack trace nécessaires
pour les corriger. J'ai ajouté de vraies violations ESLint au même fichier
et essayé rtk lint — de nouveau il a ignoré le wrapper propre à
React (scripts/tasks/eslint.js), a lancé ESLint brut sur tout
le monorepo au lieu du seul fichier concerné, et l'analyse du JSON résultant
a échoué.
Trois outils différents (lanceur de tests, linter, sur deux projets
différents), le même échec : quand un projet enveloppe sa commande dans son
propre script — très courant dans les gros dépôts — rtk ne le
détecte pas.
Ce que dit sa propre documentation
Avant de coucher tout ceci comme un constat, il valait la peine de vérifier
si c'était déjà signalé. Le site de rtk liste précisément les
commandes qu'il optimise — jest, vitest, eslint figurent toutes sur cette
liste — et précise explicitement que tout ce qui n'y figure pas "passe en
mode transparent, inchangé." Mais ce qui casse ici n'est pas le cas "non
supporté" : c'est un échec de l'analyseur sur des commandes qu'ils
revendiquent eux-mêmes supporter, quand le projet les invoque via son propre
wrapper. Leur page de dépannage ne mentionne ni wrappers, ni monorepos, ni
scripts personnalisés d'aucune sorte.
En fouillant leurs Pull Requests, j'ai trouvé l'explication : il existe un
support "intelligent" pour yarn et pour pnpm run <script>
en développement actif, pas encore fusionné dans main. La PR
yarn (#867) cite de la télémétrie réelle : 126 commandes yarn non gérées sur
615 sessions en 30 jours — yarn lint 19 fois, yarn vitest 14 fois. Et dans la revue de cette même PR, l'un des mainteneurs a
constaté, en testant sur un projet réel (babel/babel), un échec de la même
famille que le nôtre : un yarn install qui a réellement échoué
(code de sortie 1, build de workspace cassé) s'est réduit au mot "ok". Le code de sortie était préservé — mais un agent voyant "ok" à côté d'un exit 1 n'a aucun moyen de savoir ce qui a cassé sans
relancer la commande à l'état brut.
Le compteur ne mesure pas ce qu'on croit
rtk embarque son propre tableau de bord d'économies (rtk gain). Son guide d'utilisation publie la formule exacte : les tokens en entrée
sont len(raw_command_output) / 4 — mais "sortie brute" désigne
ce que rtk lui-même a capturé au moment de l'exécution, pas ce
que la vraie commande du projet aurait produit mesuré séparément. Dans
l'exécution de rtk lint sur React, le tableau de bord a
enregistré environ 244 000 tokens en entrée — bien plus que le vrai yarn lint (1 155 octets) — parce que ce qui a réellement tourné, c'est ESLint sur
tout le monorepo, à cause de l'échec de détection. Les "économies" de 99,8%
qu'il rapporte sont réelles, mathématiquement, par rapport à cette mauvaise
commande qu'il a lui-même générée. On ne peut pas les comparer à ce
qu'obtiendrait un développeur en tapant yarn lint, parce que ce
n'est jamais ce qui a réellement tourné.
La ligne du SARIF codeql est différente : là, la comparaison est directe, même fichier en entrée. Les 100% d'économies que certifie le tableau de bord sont, littéralement, le même fait que "cache 1 504 des 1 505 résultats" — pas une interprétation, la même réduction d'octets vue sous deux angles.
Ce que d'autres savaient déjà
Deux issues ouvertes dans le dépôt, non résolues, confirment que ce n'est pas un cas isolé propre à ce test :
Injection shell critique : rtk err/test/summary transmettent la commande à sh -c sans l'échapper, et le hook approuve automatiquement chaque réécriture sans jamais demander la permission. Autre point : le filtre global (~/.config/rtk) n'a aucun contrôle d'intégrité — le rapport lui-même indique qu'il pourrait servir à "cacher des résultats de vulnérabilités au LLM." Et dans certains cas, rtk ne propage pas le code de sortie d'un test échoué : Claude Code peut lire un succès là où il y a eu un échec.
Un utilisateur, sans lien avec ce test, souligne que le "60-90%" ne couvre que les commandes de terminal — pas les outils Read/Grep/Glob qui dominent la plupart des sessions — et estime que l'économie réelle sur une session complète tourne probablement autour de 10%, pas le chiffre affiché en une.
Le hook, pour de vrai
La façon "correcte" de l'intégrer n'est pas de taper rtk à la
main avant chaque commande : c'est un hook (rtk init -g) qui
réécrit les commandes Bash de façon transparente. Je l'ai installé dans ma
propre config Claude Code — il a modifié settings.json avec une
sauvegarde automatique, ajouté un RTK.md de dix lignes, rien
d'invasif — et il a fonctionné exactement comme documenté, sans besoin de
redémarrage. Vu le constat d'injection shell de l'issue #640, qui touche
précisément ce hook, je l'ai désinstallé après les tests.
Conclusion
Rien de tout cela ne dit que rtk est inutile. Dans les cas
qu'il gère bien — git, ls, docker, commandes invoquées directement — il
coupe du vrai bruit à un coût négligeable. Mais le chiffre "60-90%" ne
survit pas à l'examen, et ce n'est pas la seule chose qui compte pour
décider d'installer quelque chose :
Les économies dépendent de la commande, de si le projet l'enveloppe dans son propre script, et le compteur de l'outil lui-même n'est pas conçu pour vous avertir quand quelque chose s'est mal passé. La différence entre croire un chiffre et le mesurer soi-même — avec la vraie commande, à échelle réelle, vérifiée face à la documentation, aux PR et aux issues du projet lui-même — est exactement le travail qui vaut la peine d'être fait avant d'écrire sur n'importe quel outil.