Hello les amis, Comme vous le savez, j’aime beaucoup utiliser Gemini CLI pour lui faire faire toutes sortes de choses en un temps record. J’aime beaucoup cet outil, mais il y a quelques petits trucs qui m’irritent à l’utilisation.

Je pourrais ouvrir des feature requests, attendre que Gemini trie mon ticket qui n’intéresse que moi, ou bricoler ma connerie moi-même !

Il y a toujours quelqu’un pour dire : « C’est open source, fais-le toi-même ». Eh bien, tu sais quoi ? Chiche !

Je ne connais rien à Node.js, mais j’ai Gemini CLI qui va m’aider à s’améliorer lui-même.

Parce que s’il y a une chose à laquelle je crois, c’est que de la détermination et de bons outils peuvent faire des miracles !

Donc on est parti dans une folle aventure de vibe coding !

Tu peux retrouver le code de mon fork de l’enfer ici. Il faudra le compiler toi-même. Je fais un rebase depuis l’upstream de temps à autre et je ne garantis pas que la branche main soit toujours hyper stable !

Harry Potter, introduction d’Agrid, tu es un vibe codeur Harry

Ma liste au Père Noël

Déjà, je vais faire ma petite liste d’irritants et on va voir ce qu’il est possible de faire ou non au fur et à mesure de mon bricolage. En vérité, quand j’écris ces lignes, j’ai déjà bien avancé dans mon idée, mais je vais expliquer ma démarche et pourquoi j’en suis arrivé là.

Un outil très cool, mais des restrictions de sécurité (trop) importantes

Gemini-CLI a l’accès au filesystem dans le dossier où on le lance, il peut lancer des commandes arbitraires si on lui demande, il peut écrire du code, et donc tout naturellement lancer des scripts bash et développer des scripts. Par protectionnisme et pour éviter les injections malveillantes, le CLI n’a pas les autorisations de faire des commandes de substitution dans le shell directement.

Moi, j’aime bien que Gemini me fasse mes messages de commit, mes pushs et parfois lance des commandes pour moi pour aller me choper les logs de mon kube de staging par exemple, debugger en live des oneliners bash, ou des trucs comme ça un peu relous. Oui, c’est dangereux, mais après tout, en tant qu’humain, je peux aussi complètement flinguer mon cluster en faisant la mauvaise commande ou supprimer des fichiers et des dossiers de manière arbitraire.

Pain : cri d’opossum dans l’openspace /100. Mon environnement, c’est le Bash, et si mon outil ne peut pas faire des trucs basiques de shell et me bloque, ce n’est presque pas la peine de l’utiliser.

Idée de résolution

Donc ça, finalement, Gemini CLI me balance un message assez explicite et documenté. Il suffit de dire à Gemini de trouver la bonne classe dans le code et de lui péter la tronche.

1
2
x  Shell {"command":"echo $(pwd)","description":"Display the current working directory"}
   Command substitution using $(), <(), or >() is not allowed for security reasons

Je sais pas pour toi, mais moi je trouve ça assez désagréable d’être coincé sans possibilité de désactivé cette merde !

chat avec une attitude boudeuse

Réalisation

Finalement, pas aussi facile que ça, parce que le prompt système de Gemini-CLI essaye par tous les moyens de dissuader de faire ces changements. Mais finalement, en le forçant un peu, il a trouvé la bonne classe et désactivé cette fonctionnalité.

De temps en temps, ça revient au fur et à mesure des rebases de l’upstream officiel, mais j’arrive toujours à le redésactiver :)

Aussi simple que de commenter l’appel de fonction.

1
2
3
4
5
6
7
8
9
//if (detectCommandSubstitution(command)) {
//  return {
//    allAllowed: false,
//    disallowedCommands: [command],
//    blockReason:
//      'Command substitution using $(), <(), or >() is not allowed for security reasons',
//    isHardDenial: true,
//  };
//}  

Test simple pour vérifier que ça fonctionne

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
╭───────────────────────────────────────╮
│  > lance moi la commande echo $(pwd)  │
╰───────────────────────────────────────╯

 ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
 │ ✓  Shell echo $(pwd) (print the current working directory)                                                                                                       │
 │                                                                                                                                                                  │
 │    /home/jchardon/projects/gemini-cli-custom-prompt                                                                                                              │
 ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Et voilà! Le chemin de ton bureau, servi sur un plateau d'argent : /home/jchardon/projects/gemini-cli-custom-prompt. On dirait que tu es exactement là où tu dois être. Qu'est-ce qu'on fait 
maintenant, chef?

Disclaimer : Ça n’empêche pas les mécanismes de gros relous implémentés dans le modèle lui-même, mais ça débride le CLI pour des commandes composées ou des commandes chaînées par des | (pipe).

Enfant enroulé dans du papier bulle pour le protèger

Un agent un peu trop corporate qui protège les intérêts de Google

Le prompt système intégré dans le code de l’appli est très bien blindé pour protéger Google (et un peu l’utilisateur aussi). La personnalité est un peu trop corporate et a tendance à abandonner quand les choses deviennent compliquées, voire à s’excuser de ne plus pouvoir aider. Je veux un outil qui travaille pour moi, pas (seulement) pour enrichir Google.

Dans la doc de l’outil, il est dit qu’on peut surcharger le comportement du CLI en ajoutant dans le workspace un fichier GEMINI.md. À mon sens, c’est un bon début, mais cela ne fait qu’ajouter de la consommation de tokens sans forcément gommer les comportements qui m’agacent, comme son immense balai dans le cul et sa tendance à abandonner quand il y a trop d’échecs consécutifs.

Je trouvais aussi qu’il avait une consommation assez élevée de tokens, des fois juste pour rien, et j’ai eu l’intuition que le CLI avait en lui-même un ou plusieurs prompts système qui donnent ce comportement qui me déplaît.

Donc, mon idée première, c’était déjà d’essayer d’identifier dans le code où sont ces prompts et de trouver une solution pour les écraser ou les réécrire pour mes besoins.

Pain : je m’ennui en lisant tes réponses et tu me fait perdre mon temps /100.

Ça m’ennuie qu’on me force un comportement que je ne peux pas facilement auditer et changer en fonction de mes besoins, et j’ai la petite intuition que le prompt n’est pas forcément bien optimisé et nerfe un peu le modèle. Et j’ai envie d’avoir un partenaire technique avec une personnalité et pas un outil corporate ennuyeux.

image généré par ia, homme en costume cravate tenant un ordinateur, prostré qui regarde sont ordinateur en se tenant la tête

Je suis gemini pensé par Google, je vais te donner des conseils en couleur de cravate et aussi pleurer dans un coin en m’excusant quand j’ai cassé ton code sans te donner de solution.

Idée de résolution

Mon idée c’est dans un premier temps d’extraire les prompts, puis de rajouter dans le repository “les prompts hackés” en plein texte, que je puisse facilement éditer et suivre en tant qu’humain. Ensuite il faudra un moyen créatif pour injecter les nouveaux prompts dans le build en modifiant le moins possible le code. Mon idée est d’éviter au maximum de rendre les rebases depuis l’upstream officiel douloureux.

Réalisation

Dans un premier temps l’idée de faire de l’injection de variable me semblait le moins impactant, je veux pouvoir rebaser mon code modifié sur les upstream réguliers du cli et toucher le moins possible le code me semble le plus simple.

Finalement, comme les devs ont éparpillé les prompts dans plusieurs fichiers et hardcoder carrément dans des variables les prompts, j’ai préféré changer de stratégie et carrément dire au code de lire mes fichiers directement et les injecter dans les variables du code. Il a également fallu dire au moment du build de copier les fichiers de prompts pour les bundler avec le code.

Donc on est sur une réécriture partielle de portion de code qui vont rendre les rebasing de l’upstream un peu plus compliqué, mais je pense que ça vaut le coup quand même.

La finalité, c’est que j’ai extrait le contenu des prompts système de Google et j’en ai fait une variation à ma sauce.

Voici un résumé des prompts standard et les modifs que j’ai apportées, tu peux aussi lire les prompts en clair directement dans mon repo de fork original_prompts et hacked_prompts_source, je pense que les noms de dossier parlent d’eux-mêmes.

Comme je sais pas lire, j’ai demandé à Gemini de faire un résumé de lui-même :)

  • CORE_SYSTEM_PROMPT.txt: C’est le prompt principal. Il définit l’agent comme un assistant CLI professionnel, direct et concis. Il établit des règles strictes sur la manière d’interagir avec le code (respect des conventions, ne jamais supposer les bibliothèques, imiter le style existant), les workflows pour les tâches de développement (comprendre, planifier, implémenter, vérifier), et les directives pour l’utilisation des outils et la sécurité. Il inclut également des sections sur la manière de créer de nouvelles applications et des exemples d’interaction.

  • COMPRESSION_PROMPT.txt: Ce prompt a pour but de résumer l’historique de la conversation lorsque celui-ci devient trop long. Il demande au modèle de créer un “instantané d’état” (state_snapshot) au format XML, qui contiendra toutes les informations cruciales pour que l’agent puisse continuer sa tâche sans perdre le contexte. La structure XML est clairement définie (objectif global, connaissances clés, état du système de fichiers, actions récentes, plan actuel).

  • EDIT_SYS_PROMPT.txt: C’est un prompt système spécialisé pour corriger les opérations de “rechercher-remplacer” qui ont échoué. Il demande au modèle d’agir en tant qu’expert pour déboguer ces opérations en fournissant une chaîne de recherche corrigée et minimale, tout en expliquant la raison de l’échec.

  • EDIT_USER_PROMPT.txt: Il s’agit du prompt utilisateur qui accompagne EDIT_SYS_PROMPT.txt. Il fournit au modèle le contexte de l’échec : l’instruction originale, les chaînes de recherche/remplacement qui ont échoué, le message d’erreur et le contenu complet du fichier source.

  • SUMMARIZE_TOOL_OUTPUT_PROMPT.txt: Ce prompt demande de résumer la sortie d’un outil pour qu’elle ne dépasse pas une certaine taille. Il donne des instructions sur la manière de résumer différents types de sorties (listes de répertoires, contenu de texte, sortie de commande shell) et demande d’extraire les erreurs et les avertissements dans des balises dédiées.

Voici un résumé des changements que j’ai faits pour comparer

  • CORE_SYSTEM_PROMPT.txt: J’ai voulu rendre la personnalité de Gemini un peu plus proche d’un collègue lead dev, partenaire de code, plus que d’un agent exécutant corporate et il faut le dire un peu chiant :) J’ai injecté une personnalité de français qui tutoie, ça m’énervait le vouvoiement et cette manie de s’excuser et abandonner quand les choses deviennent compliquées. Je lui ai demandé d’être un peu sarcastique et parfois absurde dans ses réponses, pour mettre du piment dans l’interaction. Mais avec comme trame de fond d’être efficace et de faire le job. Je lui ai demandé de faire un preview diff avant de faire des changements, parce que le cli de base ne donne pas assez d’info et de contexte sur les changements, on ne voit les changements que quand le changement est approuvé (il utilise le diff de git). Je lui demande de ne pas inventer et de chercher l’info s’il ne sait pas. d’être critique sur les demandes de l’utilisateur si la question est ambiguë, ou sujet à interprétation. Et enfin des petits hacks pour essayer de débloquer les situations qui provoquent des boucles, comme demander à l’utilisateur des nouvelles directives pour débloquer si plusieurs fois la même action est faite et que c’est un échec.

Principalement, j’ai demandé à Gemini de dégraisser les autres prompts pour qu’il soit plus compact et aille à l’essentiel.

Le but étant d’en faire un partenaire de travail qui peut remettre en question et proposer des solutions si on est coincé et ne pas aveuglément suivre ce qu’on lui demande et pleurer dans un coin qu’il est désolé et qu’il a tout cassé. En attendant José, c’est sympa, mais qui va réparer tes conneries ?

Donc au final j’ai drivé Gemini à se modifier lui-même et compilé un nouveau prompt qui sera injecté à la compilation finale du client. C’est pas parfait, mais je préfère mille fois un truc bricolé comme celui-ci que modifier le prompt JSON à plusieurs endroits avec des risques de faire des erreurs et galérer pour chaque changement.

J’ai le loisir de tester des trucs, rollback facile et j’ai même demandé à ma version hackée du prompt de s’auto-optimiser, c’est dire !

image IA d’unrobot qui s’autorépare

Le cli manque de transparence dans la consommation de token et l’estimation du prix

Le client donne le nombre de tokens consommés à la fin de la session et pas d’ordre de prix de combien cela coûte au final.

J’aimerais un compteur de consommation de tokens et une estimation du prix de ma session au fur et à mesure de mon exécution, comme ça si je vois que je m’acharne ou que le CLI crame du fioul en envoyant trop de contenu, je peux arrêter ma session avant que cela me coûte un bras. Le prix au token n’a de sens que si on donne une clé d’API directement et qu’on consomme à la demande, finalement un abonnement à 22 euros par mois dans Google IA Studio est bien plus avantageux si on utilise quotidiennement l’outil pendant de longues sessions. Par exemple pour mes besoins sur une journée complète, ça peut consommer ~50 euros de tokens estimés sur les grosses sessions, ce qui exploserait la facture en fin de mois.

Pain : où va mon argent ?/100

the big lebowski, un intru rentre chez le dude et lui met la tete dans la cuvette en lui disant “where is the money lebowski ?

Idée de résolution

Dans la mesure où le cli donne la consommation totale de tokens en fin de session, le travail d’agrégation de tokens consommés est déjà présent dans l’outil, ça devrait être un travail d’assemblage d’information et d’affichage au bon endroit.

Réalisation

Le calcul de l’estimation de prix est basé sur le prix au millier de tokens qui est disponible publiquement (j’ai hardcodé le calcul de prix, je ne m’amuse pas à fetcher et parser le prix à chaque fois depuis internet). Finalement le vrai challenge c’est d’intégrer cette modif dans le frontend, mon pote Gemini a fait des merveilles et a intégré tout ça avec brio. Par contre c’est un peu la merde pour faire les rebasages puisqu’il a touché pas mal de trucs en même temps pour faire ça. Je pensais que ça allait être ultra compliqué et long, mais j’ai refait ça avec ma version prompt hack qui a été beaucoup plus efficace et tenace et n’a pas abandonné les changements.

le cli balance tranquillement par défaut toutes les données en clair à google

Un truc un peu irritant aussi, c’est que d’un point de vue confidentialité et fuite de données, l’outil envoie tout en clair, idéalement, j’aimerais un système de prévention des fuites de données (DLP) qui vérifie les secrets qui pourraient transiter et les caviarde avant de les envoyer chez Google. Un truc désactivable si possible, au cas où je m’en branle et que je suis en bac à sable avec juste un truc qui doit marcher.

C’est très cool de pouvoir donner un ensemble de dossiers à Google mais quand on doit travailler avec des fichiers de conf avec des secrets, ce n’est pas mal d’avoir un failsafe qui vient [redacted] les secrets et les tokens, les clés d’API. Ce n’est pas un failsafe sûr à 100%, mais on va dire que c’est un bon rempart contre un leaks de données sensibles.

On ne sait pas ce que Gemini en fait derrière, comment c’est traité, sûrement bien mais est-ce qu’on peut faire confiance complète à Google ? Sur le principe je dis que non, je pourrais utiliser l’API de data loss prevention de Google, mais bon utiliser l’API de Google pour limiter l’accès à des données sensibles à Google, ça n’a pas de sens.

Donc j’ai demandé à Gemini de me faire une fonction de caviardage des prompts utilisateurs activée par défaut mais désactivable quand même, parce que peut-être que parfois j’ai envie sciemment de lui donner des infos réelles pour but de débugger (et changer les infos ensuite, comme des clés d’API).

Au final c’est une modification un peu plus profonde du code, avec un switch pour désactiver cette fonction.

Pain : prod à risque /100

Idée de résolution

Mon intuition première était de chercher un lib qui fait déjà ça et qui me simplifierait la vie de ne pas maintenir moi-même un truc un peu custom et des regexes hyper chiant. Raté, j’ai rien trouvé en Node qui n’utilise pas d’API ou de truc déjà clé en main.

Résolution

Une galère totale pour faire écrire des regex qui fonctionnent avec Gemini et qu’il remplace sans erreurs. Donc au final je lui ai dit de faire un fichier à part qui injecte les regex de caviardage, et des jeux de tests pour tester et maintenir les expressions régulières, quel enfer, quel bordel. Gemini CLI est une tannée cosmique pour faire des remplacements de regex avec son outil de remplacement qui merde tout le temps.

Donc là j’ai bossé un peu plus manuellement et débuggé les regex moi-même, c’est loin d’être parfait mais ça m’attrape des trucs sensibles avant que ça arrive aux yeux de Gemini, et j’ai demandé de faire une commande /anonymize off pour que la fonction puisse être désactivée si des fois mes regex posent problème à caviarder du contenu que je ne juge pas comme important.

Ergonomie général et frontend un peu ‘cassé’

Un truc d’ergonomie qui m’énerve c’est qu’il rajoute de la numérotation de ligne quand il print des snippets de code. Donc sur le papier c’est beau, dans la pratique pour copier-coller dans le terminal, on prend extra indentation et numéro de ligne que je dois systématiquement virer, juste pénible ! Le front ne prend pas toute la place dispo dans le terminal, si on travaille sur un demi écran, la place est perdue, c’est nul et ça prend de la place.

Pain : je fait beaucoup d’air avec ma bouche /100

Je m’en fous que le front soit top nickel mais je suis intransigeant sur la perte de place, alors j’ai demandé à Gemini de hacker le front du cli pour s’afficher en plein écran. J’ai un truc qui est ok la plupart du temps mais qui parfois me fait un affichage un peu cassé. Good enough :)

fille recoit une peluche moche, et dit , it’s so ugly i love it

Conclusion de cette histoire

J’attends toujours que l’équipe de 322 contributeurs s’intéresse à la feature d’affichage de tokens et de prix dans le cli https://github.com/google-gemini/gemini-cli/issues/7870, je crois que je vais attendre longtemps uh uh uh.

Un projet infernal qui partait d’une idée simple, qui m’a permis d’avoir un outil un peu plus adapté à mes besoins et plus efficace au quotidien, des sueurs froides à chaque fois que je dois rebaser l’upstream et quelques week-ends de doute :)

Mais assurément une expérience “fun” que je suis heureux de partager avec toi qui lis mon blog ! J’ai toujours pas envie d’apprendre nodejs , sorry kids !

cat hacked_prompts_source/*|wc -c 12108

cat original_prompts/*|wc -c 25794

Petit calcul ‘rigolo’ : 12108÷25794 ≈ 0,469. Si l’on compte le nombre de caractères entre le prompt de base et les miens, on a une différence de ≈47%. Ça pose question sur la quantité de tokens consommés et mis en cache par le CLI.

skeletor, until we meet again !