Recherche/Créer un outil en ligne de commande qui ne perd pas de données et n’exécute pas de code non approuvé

Créer un outil en ligne de commande qui ne perd pas de données et n’exécute pas de code non approuvé

Quand une IA crée ou modifie un CLI qui a déjà des utilisateurs. Elle garde options et configuration compatibles, s’arrête plutôt que de détruire du travail, n’exécute les hooks du projet qu’après approbation, trace chaque sous-processus et affiche les résultats locaux avant le réseau.

Pour utiliser ces règles d'IA
## Garder le fichier de configuration et les options compatibles quand l’outil grandit Une fois qu'un outil a de vrais utilisateurs, il faut équilibrer une conception propre avec la compatibilité. Une refonte ne constitue pas, en soi, une raison de pénaliser les utilisateurs. ### Justifier chaque rupture externe Modifier une interface externe nécessite une amélioration concrète pour l'utilisateur, pas seulement un nettoyage. Préférez un avertissement de dépréciation et un chemin de migration à une rupture silencieuse. ### Nommer la surface protégée Déterminez quelles interfaces constituent des contrats (le format du fichier de configuration, les options et arguments de la ligne de commande) et lesquelles peuvent changer librement (API internes, emplacements des journaux, mise en forme de la sortie lisible par un humain). Protégez le premier ensemble ; gardez le second flexible pour que les refontes restent peu coûteuses. ### Délimiter la fenêtre de support Indiquez une version minimale prise en charge pour l'environnement d'exécution ou la chaîne d'outils, et faites-la progresser selon un rythme de maintenance prévisible plutôt qu'au coup par coup. ## Utiliser un seul mot par notion dans la documentation, l’aide et les commentaires Choisissez un seul terme par concept et utilisez-le à l'identique dans la documentation, les textes d'aide et les commentaires de code. Un vocabulaire qui dérive finit, discrètement, par semer la confusion chez les utilisateurs comme chez les mainteneurs. ### Consigner le glossaire par écrit Tenez une courte liste qui fixe le sens de chaque mot et les quasi-synonymes à éviter. Distinguez explicitement les concepts qui se ressemblent, comme le checkout d'origine par rapport à un checkout secondaire, ou la destination d'une opération par rapport à l'espace de travail dans lequel elle s'exécute. ### Ne jamais surcharger un mot porteur de sens Si un mot désigne déjà un concept, ne le réutilisez pas pour un autre ; choisissez un mot différent pour le second, même lorsque la réutilisation semble naturelle dans une même phrase. ## Mettre à jour l’aide à chaque changement de comportement et la vérifier par un test Chaque changement de comportement doit inclure la mise à jour de sa documentation dans la même modification. Le test à appliquer : le texte d'aide décrit-il toujours ce que le code fait réellement ? ### Garder une source primaire unique par fait Pour la documentation générée, modifiez la source de vérité (l'aide intégrée au code et les définitions de commandes) puis régénérez les copies rendues ; ne modifiez jamais à la main un fichier généré. Lorsqu'un répertoire contient à la fois des copies générées et des pages rédigées manuellement, vérifiez le rôle d'un fichier avant de le modifier. ### Faire respecter la synchronisation par un test Un test de synchronisation qui compare la sortie rendue à la source détecte mécaniquement toute dérive. Modifier le texte d'aide change aussi les instantanés rendus ; régénérez-les et relisez-les dans la même modification pour que le test reste au vert. ## S’arrêter sur une erreur plutôt que détruire les fichiers de l’utilisateur sans accord Traitez les données de l'utilisateur comme sacrées. Une commande qui échoue tout en préservant le travail vaut mieux qu'une commande qui « réussit » en le détruisant silencieusement. ### Préférer l'échec à la perte silencieuse Si une opération risque de détruire des fichiers non suivis, des modifications non validées, ou d'autres données utilisateur, arrêtez-vous avec une erreur plutôt que de continuer. ### Exiger un consentement explicite pour détruire Une action destructrice, comme la suppression forcée de données ou l'écrasement, ne s'exécute que si l'utilisateur l'a explicitement demandée. Ne supprimez ni n'écrasez jamais en tant qu'effet secondaire d'une opération sans rapport ; faites du nettoyage une étape distincte que l'utilisateur choisit. ### Privilégier la variante qui échoue, attention à l'intervalle entre vérification et usage Lorsque deux formes d'une opération existent, choisissez celle qui échoue en cas de conflit plutôt que celle qui force le passage. Restez prudent lorsqu'un délai s'écoule entre une vérification de sécurité et l'action qu'elle protège : l'état peut changer pendant cet intervalle, donc ne forcez pas le passage lors d'un nettoyage. ## Faire passer chaque sous-processus par un seul wrapper qui le journalise et le chronomètre Faites passer chaque commande externe par un unique wrapper d'exécution plutôt que d'appeler directement l'API de processus. Un chemin unique offre gratuitement une journalisation de débogage et une mesure des délais cohérentes, ainsi qu'un endroit unique où modifier le comportement. ### Un wrapper, un émetteur Conservez la grammaire de trace dans un seul composant et faites-en le seul rédacteur des appels de journalisation sous-jacents, afin qu'un sous-processus soit tracé via ce garde-fou, ou ne produise aucun enregistrement du tout. ### Les nouveaux points de lancement doivent s'inscrire explicitement Quelques formes de lancement ne peuvent pas s'adapter au wrapper et doivent construire l'enregistrement de trace à la main : le démarrer juste avant le lancement, le clôturer en cas de succès, le faire échouer en cas d'erreur. Faites du garde-fou une valeur à usage obligatoire qui déclenche une assertion si elle est abandonnée sans résolution, afin qu'une clôture oubliée fasse échouer un test plutôt que de passer silencieusement sans trace. Les processus enfants détachés en arrière-plan et les assistants interactifs sont les exceptions non tracées documentées. ### Nettoyer les variables de découverte héritées Certaines variables d'environnement sont résolues avant qu'un processus enfant ne consulte son répertoire de travail, si bien qu'une valeur héritée peut silencieusement remplacer le répertoire que vous avez choisi. Tout point de lancement qui déplace une commande utilisateur vers un répertoire sélectionné par l'outil doit effacer ces variables ; les processus enfants qui s'exécutent dans le propre contexte de l'utilisateur conservent le contexte hérité, rendu absolu. ## Décider d’après les codes de sortie et le JSON, pas d’après le texte des messages Préférez les codes de sortie, les statuts lisibles par une machine et les formats structurés à l'analyse de messages lisibles par un humain, qui se cassent au moindre changement de locale, de version ou de formulation. ### Choisir le canal structuré Lisez une sortie « porcelain » ou JSON ainsi que des indicateurs de statut numériques plutôt que la sortie par défaut destinée à l'humain. Lorsqu'une commande encode du sens dans son code de sortie, basez votre logique sur ce code, pas sur le texte du message. ### Documenter la fragilité inévitable Lorsqu'aucune alternative structurée n'existe et que vous devez analyser du texte, signalez la fragilité directement à l'endroit de l'analyse, afin que le prochain lecteur sache qu'il s'agit d'un risque connu, et non d'un contrat stable. ## Afficher d’abord les données locales et ajouter ensuite celles du réseau La réactivité est une fonctionnalité à part entière : affichez d'abord à partir des données locales, puis laissez les détails plus lents arriver ensuite. ### Ne jamais laisser le réseau bloquer la première image affichée Ne sollicitez le réseau que lorsque la requête de l'utilisateur l'exige réellement. Chaque commande devrait afficher sa première sortie à partir de données locales, puis diffuser progressivement les détails issus du réseau ; une commande incapable d'afficher sa première sortie tant qu'un appel distant n'est pas revenu constitue le mode d'échec, particulièrement pénalisant sur une installation neuve. Un chemin critique synchrone, comme une invite de commande, ne doit jamais atteindre le réseau, même de façon progressive. ### Préférer les contextes bornés par l'utilisateur pour les données distantes en direct Diffusez la sortie de la commande ligne par ligne plutôt que de la mettre en tampon. Un sélecteur interactif est l'endroit le plus indulgent pour le travail réseau : il s'affiche immédiatement, et si l'utilisateur agit avant qu'un appel lent ne revienne, la requête est simplement abandonnée. Une commande qui s'exécute jusqu'à son terme ne peut se terminer tant que tous les appels n'ont pas répondu ; récupérez donc les données distantes en direct dans le sélecteur, de façon progressive. ## Arrêter toutes les boucles quand l’utilisateur interrompt une commande en cours Lorsqu'un processus enfant meurt à cause d'un signal (interruption ou arrêt), chaque boucle du chemin d'exécution au premier plan doit s'interrompre plutôt que de passer à l'itération suivante. ### Lire le canal de signal structuré Détectez les sorties dues à un signal via le champ de signal structuré, jamais en repérant un code de sortie supérieur ou égal à 128 ni en analysant le texte d'erreur. Propagez un résultat canonique de sortie interrompue et sortez de la boucle. ### Vérifier avant toute branche qui avale les erreurs Effectuez la vérification d'interruption avant qu'une quelconque stratégie de gestion d'échec ne s'exécute, afin qu'une politique « avertir et continuer » ne puisse pas avaler une interruption. Tout nouveau code qui boucle sur des processus enfants doit effectuer cette vérification à chaque itération ; sinon, une seule interruption sur une opération à plusieurs étapes fonce à travers les étapes restantes. ## N’exécuter les hooks et alias définis par le projet qu’après approbation Les commandes définies par le projet (hooks de cycle de vie, alias, corps d'exécution tirés de la configuration du projet) constituent du code arbitraire livré dans un dépôt que l'utilisateur vient peut-être de cloner. Elles ne s'exécutent qu'après avoir été validées par un système d'approbation. ### La barrière constitue à elle seule la frontière de sécurité Ne construisez jamais un chemin de code qui exécute des commandes définies par le projet sans cette barrière. Un raccourci du type « nous avons déjà validé l'opération, donc exécutons aussi ses hooks » transforme chaque commande qui touche à la configuration du projet en exécution de code à distance ; la barrière est la seule chose qui sépare le clonage d'un dépôt de l'exécution, par un hook, d'un script tiré directement d'internet. ### Sélectionner l'ensemble approuvé une seule fois, de façon immuable Enfermez l'ensemble de commandes dans un plan approuvé immuable au point de décision, et laissez l'exécuteur ne consommer que cette valeur, sans rien conserver à partir de quoi il pourrait la reconstruire, de sorte que resélectionner une commande non approuvée devienne une erreur de compilation plutôt qu'une simple vérification de relecture. Un contexte incapable d'interroger l'utilisateur consulte l'état d'approbation en lecture seule et n'exécute que le sous-ensemble déjà approuvé. ## Ne jamais fusionner avec un contrôle de couverture en échec sans l’accord de l’utilisateur Ne fusionnez jamais une modification dont la barrière de couverture échoue sans l'approbation explicite de l'utilisateur, même lorsque la plateforme marque cette barrière comme « non requise », car elle reste bloquante en pratique. ### Savoir comment le signal est rapporté Les outils de couverture peuvent publier leurs résultats sous forme d'exécutions de vérification plutôt que de statuts de commit, lus via des API différentes ; une requête de statut combiné peut complètement manquer une exécution de vérification, et le résultat peut arriver plusieurs minutes après la fin du job. Interrogez la bonne API avant de conclure qu'une modification est au vert. ### Combler l'écart, ne pas le laisser passer En cas d'échec de couverture, enquêtez et comblez l'écart : ajoutez des tests, ou supprimez le code inutilisé, y compris les gestionnaires de cas particuliers lorsqu'il suffit de retomber sur un chemin général. Si vous pensez qu'il s'agit d'un faux positif, demandez à l'utilisateur avant de fusionner plutôt que de décider unilatéralement. Exécutez la couverture avec les mêmes indicateurs de fonctionnalité que ceux utilisés par le pipeline d'intégration continue, afin de ne pas pouvoir écarter un échec en prétendant qu'une fonctionnalité était désactivée. ## Réutiliser les bibliothèques existantes et corriger les avertissements au lieu de les masquer Vérifiez les dépendances existantes avant de développer un utilitaire maison. Une bibliothèque éprouvée pour la normalisation de chemins, l'échappement de shell, la coloration, ou la détection de variables de gabarit vaut mieux qu'une réimplémentation ad hoc qui gère mal les cas limites. ### Ne pas masquer les avertissements Ne faites jamais taire un avertissement en le supprimant globalement. Supprimez le code mort, ou laissez un marqueur daté et expliqué qui le relie à un travail à venir précis. ### Documenter les systèmes complexes sur place Un système non évident (une machine à états, un état mis en cache, une coordination entre modules, une invalidation subtile) doit être accompagné d'une spécification au niveau du module couvrant son objectif, ses décisions clés, ses contrats et ses invariants ; maintenez-la à jour. ### Garder les tests hors du code de bibliothèque N'ajoutez pas aux types de la bibliothèque des méthodes de confort réservées aux tests ; les tests doivent appeler l'API réelle ou définir leurs propres fonctions auxiliaires. Pour les littéraux multilignes, préférez de vrais retours à la ligne intégrés aux séquences d'échappement de continuation de ligne, qui suppriment silencieusement les espaces. ## Ajouter du contexte aux erreurs et ne jamais les transformer en plantages Portez du contexte avec chaque erreur. Utilisez des erreurs explicites de logique métier pour les états d'échec attendus (un espace de travail non propre, une branche manquante, un état invalide), et un enrobage de contexte pour les échecs d'E/S et de commandes externes. ### Ne jamais avaler un échec Dans une fonction qui retourne un type d'erreur, n'utilisez jamais d'appels de type panique-en-l'absence ou de style « unwrap » qui transforment un échec récupérable en plantage ; propagez avec l'opérateur d'erreur, levez une erreur explicite, ou retournez-en une. Chaque erreur enrobée doit indiquer au lecteur quelle opération a échoué et pourquoi. ## Migrer les anciennes clés de configuration au lieu de les ignorer en silence Regroupez toute la gestion de la compatibilité de configuration dans une seule couche : réécrivez les motifs dépréciés sous leur forme canonique avant que l'analyseur ne les voie. Ne supprimez jamais silencieusement une ancienne clé de configuration, car cela constitue un changement de comportement silencieux ; migrez-la plutôt. ### Un seul prédicat pour détecter et migrer Faites en sorte qu'une seule fonction idempotente réécrive un motif déprécié et rapporte ce qu'elle a changé, afin que la détection et la migration partagent un seul et même prédicat et ne puissent pas diverger. Effectuez la détection en appliquant ces mêmes fonctions à une copie de travail, de sorte qu'un avertissement se déclenche exactement lorsqu'une mise à jour effective modifierait le fichier ; verrouillez cette équivalence par un test. ### Classer le moment d'application de chaque règle Décidez, pour chaque règle, quand une réécriture s'applique : à chaque chargement, uniquement lors d'une mise à jour explicite, silencieusement sans avertissement, ou comme valeur par défaut en attente qu'une version ultérieure adoptera. Laissez intacte une valeur qui ne peut être réécrite en toute sécurité (malformée, ou une clé de destination déjà occupée), et laissez l'erreur de type propre à l'analyseur porter le message. ## Nommer les accesseurs pour que le préfixe indique le coût, et choisir où placer le cache Encodez l'intention dans les noms des accesseurs, afin qu'un appelant puisse prédire le coût et le mode d'échec à partir du seul préfixe. ### Laisser le préfixe porter le contrat Adoptez un schéma cohérent : un simple nom commun retourne une valeur sans effet de bord et peut être mis en cache ; un nom préfixé par « set » écrit un état et peut échouer ; un nom préfixé par « require » échoue lorsque la valeur est absente ; un nom préfixé par « fetch » effectue des E/S réseau ; un nom préfixé par « load » effectue des E/S fichier. Évitez un préfixe « get » redondant lorsqu'un simple nom commun se lit mieux. ### Décider où réside l'état mis en cache Partagez les valeurs mises en cache en lecture seule via un cache à comptage de références que le clonage partage. Choisissez le niveau selon le coût et la fréquence d'accès : les valeurs peu coûteuses et fréquemment utilisées bénéficient d'un « get-or-create » en mémoire ; les valeurs coûteuses persistent sur disque ; les valeurs qui cumulent les deux critères gardent une façade en mémoire au-dessus d'un support persistant. Documentez ce qui est mis en cache et ce qui ne l'est pas, ainsi que l'invariant permettant de le rafraîchir après une mutation.