Roadmap

Dernière revue : 2026-08-08

Historique de ce qui est déjà livré dans AI-HISTORY.html. Pièges techniques et invariants du code déjà en place dans AI-CONTEXT.html. L'état et le détail des chantiers livrés sont au registre du réalisé (option registre-livrés, 2026-08-08) — cette page ne garde de chacun qu'une ligne de renvoi, à sa position d'origine. Ce qu'un chantier livré laisse malgré tout ouvert est isolé dans Reste ouvert dans les chantiers livrés.

Vue d'ensemble

ChantierStatutPriorité
Socle (favoris, statut live, drag & drop, menus contextuels, icônes) ✅ Fait
Publication npm✅ Fait
Menu contextuel enrichi (Profils & Groupes)✅ Fait
Options & polissage UI✅ Vidé — plus aucun item ouvert (2026-08-08)
Vault Auto-Unlock⛔ Hors périmètrePlugin séparé futur
Importateur MobaXterm⛔ Écarté
Éditeur par type de fichier (images)⛔ Écarté (2026-08-03)
Validation automatique (Auto-Enter)⛔ Hors périmètre (2026-08-03)Utilité à établir avant tout portage
Durcir l'import de dossier collé (sécurité)✅ Corrigé (2026-08-08)
Envoyer une commande à N sessions choisies📋 Retenu, non ouvert (2026-08-08)Après V1.0
Étapes, délais et expect dans un snippet📋 Retenu, non ouvert (2026-08-08)Après V1.0
Outils réseau au menu contextuel⚠️ Sans décision (2026-08-08)
Vulnérabilités npm de l'outillage de build✅ Lot sans majeure livré et validé (2026-08-14)Surveillance — 18 restantes bloquées par Angular 15 / pug 2
Montée de version TypeScript / webpack-cli📋 Retenu, non ouvert (2026-08-14)Basse — période calme
CHANGELOG.md du dépôt✅ Fait — introduit avec la 1.0.3 (2026-08-14)À tenir à chaque publication
Intégration X11⛔ Écarté — déjà natif
Gestionnaire de macros visuel⛔ Écarté — redondant
Workspaces distants / partage d'équipe⛔ Écarté — contrainte intenable
Conformité de la gouvernance à la charte ✅ À jour — remise à niveau 20260804-071239 (2026-08-08) Entretien récurrent

Reste ouvert dans les chantiers livrés

Le tableau ci-dessus dit d'un chantier qu'il est fait dès que ce qui le justifiait est en service et validé — pas qu'il ne reste rien de son périmètre d'origine. Ce qui a été explicitement différé, laissé à arbitrer ou jamais exercé est donc rassemblé ici, chaque ligne renvoyant au chantier qui le porte. Cette liste n'a aucune existence propre : elle ne fait que rendre visible ce qui est déjà écrit dans les fiches — actives ici, ou au registre du réalisé pour les chantiers livrés —, et se vide en même temps qu'elles. Ne jamais y consigner un point qui ne serait pas aussi dans sa fiche — ce serait le deuxième endroit où lire le même fait.

Ce qui resteChantierNature
Flèches d'activité / sur la ligne du profil concerné (profileActivity). SFTP contextuel✅ Fait (2026-08-08)
Gel de la vue : livré comme épinglage automatique de l'onglet lié (frozen), pas comme un interrupteur manuel. SFTP contextuel✅ Fait (2026-08-08)
Chargement de la liste par blocs — listing distant paginé. SFTP contextuel✅ Fait (2026-08-08)
Icône par workspace (champ icon sur SidebarWorkspace, picker ouvert depuis le menu contextuel de l'onglet). Workspaces✅ Fait (2026-08-08)
Sélecteur adaptatif : abandonné au profit d'un réglage explicite workspaceSelectorMode (tabs/dropdown), la bascule automatique par ResizeObserver ayant été refusée au test. Workspaces✅ Fait, redessiné (2026-08-08)
Couleurs contextuelles — livrées via le champ color natif des profils (icône teintée + barre sous l'onglet), pas via une bordure de terminal comme envisagé au cadrage. Workspaces✅ Fait, redessiné (2026-08-08)
Export/import JSON ponctuel des workspaces (src/workspaceShare.ts). Workspaces✅ Fait (2026-08-08)
Les trois propositions du test manuel du 2026-07-29 : masquage par défaut des créations dans les workspaces existants et comportement configurable de la barre de filtre restent écartés en statu quo ; la levée de l'ambiguïté « réordonner » / « imbriquer » au glisser-déposer est livrée partiellement (surbrillance ciblée sur « imbriquer », ligne d'insertion non livrée). Workspaces — propositions✅ Fait, partiel (2026-08-08)
États de transfert SFTP (vitesse, file d'attente) sur la ligne de session. Sessions actives✅ Fait (2026-08-08)
Chemin distant et session porteuse sur la ligne du gestionnaire de transferts — livrés en infobulle de la ligne (champs plats composés à l'inscription, piège #54 respecté), la ligne restant compacte. Contexte fourni par les appelants : le registre ne peut pas le deviner, FileTransfer n'expose que getName(). Transferts SFTP✅ Fait (2026-08-07)
Multi-sélection dans la liste distante — fichiers et dossiers, préalable au déplacement et à la suppression par lot. Transferts SFTP✅ Fait (2026-08-08)
Confirmation avant annulation d'un transfert en cours (bouton poubelle/balai du gestionnaire). Non pilotable par CDP — composant transferts monté par Angular en mode production, inatteignable — délégué à l'œil de l'utilisateur ; mécanisme déjà prouvé via ConfirmModalComponent. Transferts SFTP✅ Fait, validé à l'œil (2026-08-08)
Retour automatique sur la vue Profils quand plus aucun onglet n'a de SFTP actif — réglage sftpAutoReturnToProfiles, activé par défaut, distinct du délai de grâce de 3 s. Panneau SFTP — état de session✅ Fait (2026-08-08)
Bascule sur exit : le code la couvre (isLiveSSHTab() lit le shell). Exercée et validée par l'utilisateur le 2026-08-07 (profil gardant l'onglet ouvert en fin de session). Panneau SFTP — état de session✅ Validé (2026-08-07)
downloadFolder ne passe plus par la voie héritée : « Download directory » ouvre notre pickDirectory(), descend l'arbre par downloadRemoteTree() (4 fichiers de front, une ligne de transfert chacun), refuse les collisions à la façon Explorateur (freeLocalName(), partagé avec la livraison du marqueur) — et une coupure dit « interrompu à N % », vérifié en coupant le transport à 23 %. Panneau SFTP — état de session✅ Fait (2026-08-07)
Contrôle « le fichier est-il bien arrivé ? » — livré sur toute voie à chemin imposé (SftpTransfers.download()) : taille locale confrontée au listing, puis à une relecture fraîche, puis à la cible résolue d'un lien symbolique (le premier passage du contrôle a pris la taille du lien pour celle du fichier — résolveur borné à 8 sauts partagé dans remoteEntry.ts). Échec = état unsound « incomplet à destination », distinct d'« interrompu » : rien ne s'est cassé en vol, la copie n'est juste pas ce qu'elle devrait être. Panneau SFTP — état de session✅ Fait (2026-08-07)
Le dialogue « Enregistrer sous » d'un téléchargement de fichier par menu contextuel affiche « 127.0.0.1 » (l'hôte de l'URL de l'offre HTTP locale — le dossier n'y passe plus). Texte arbitraire impossible ; les deux alternatives (« localhost » au risque IPv6, ou la voie platform.startDownload() à dialogue) ont été présentées et écartées par l'utilisateur le 2026-08-07 — l'adresse est exacte, c'est bien un serveur local qui sert le fichier. Panneau SFTP — état de sessionÉcart assumé (2026-08-07)
Repli du marqueur — livraison dans ~/Downloads quand il n'est pas retrouvé. Exercé et validé par l'utilisateur le 2026-08-07. Marqueur de dépôt✅ Validé (2026-08-07)
État de reprise sur micro-coupure : livré, avec le dédoublonnage des tunnels multiplexés (la vraie cause des lignes en 3-4 exemplaires) — détail dans la fiche Tunnels SSH. Tunnels SSH✅ Fait (2026-08-07)
Comparer les deux identifiants de charte en début de session, repropager les copies à chaque révision, corriger la dérive dès qu'elle est constatée. Conformité de la gouvernanceRécurrent
Passe de test d'une release avant publication npm. Ne relève plus du ménage, qui est clos — c'est du test, pas du rangement. Menée le 2026-08-08 pour la 1.0.0 : 15 tests sur le paquet installé hors jonction, en build de production. Le protocole est réutilisable tel quel à chaque publication. Ménage avant release✅ Fait (2026-08-08)
Ce qui ressemble à un reste et n'en est pas un

Ces points ont été tranchés, vérifiés faux ou rendus sans objet. Les relire comme des manques ferait rouvrir un travail déjà fait :

Phase 1 — priorité absolue

Les deux fonctionnalités qui justifiaient de remplacer MobaXterm par Tabby + ce plugin — le SFTP contextuel et les workspaces. Tout ce qui a suivi est né de leur usage réel : cette phase a grossi au fil des passes de test, ce qui explique qu'elle porte aujourd'hui bien plus que deux entrées.

SFTP contextuel — au registre du réalisé.

Workspaces — au registre du réalisé.

Section "Sessions Actives" en haut de la sidebar — au registre du réalisé.

Transferts SFTP : gestionnaire horodaté & glisser-déposer — au registre du réalisé.

Panneau SFTP : suivre l'état réel de la session — au registre du réalisé.

Sessions actives visibles aussi en vue SFTP — au registre du réalisé.

Suppression SFTP par touche Suppr — au registre du réalisé.

Double-clic force un éditeur de code, jamais une exécution — au registre du réalisé.

Glisser-déposer sortant (téléchargement vers l'OS) — au registre du réalisé.

Le marqueur de dépôt (volet de ce chantier) est au registre du réalisé — ancre conservée pour les liens du journal.

Transferts par l'API officielle, fraîcheur des copies et purge des temporaires — au registre du réalisé.

Performance de la liste SFTP et rafraîchissement automatique — au registre du réalisé.

Renommer une entrée depuis le panneau SFTP — au registre du réalisé.

Ctrl+Entrée insère un saut de ligne dans le terminal — au registre du réalisé.

Intégrité des données au re-parentage et à la suppression — au registre du réalisé.

Édition d'un fichier atteint par un lien symbolique — au registre du réalisé.

Cycle de vie : démontage, fenêtres multiples, offres de glisser — au registre du réalisé.

Confirmations en modale HTML (audit de conformité) — au registre du réalisé.

Phase 2 — productivité

Sélection multiple — au registre du réalisé.

Tunnels SSH — au registre du réalisé.

Ouverture groupée d'un dossier ("Group Exec") — au registre du réalisé.

Clone Express de profil — au registre du réalisé.

Passer les popups riches en modales centrées — au registre du réalisé.

Historique des Profils Récents — au registre du réalisé.

Quick Snippets (Macros de Sidebar) — au registre du réalisé.

Internationalisation (i18n) — au registre du réalisé.

Favoris d'icônes (distincts des « Récentes ») — au registre du réalisé.

Non daté — à faire quand utile

Icônes dashboard-icons (logos de services IT) — au registre du réalisé.

Options & polissage UI ✅ Vidé — plus aucun item ouvert (2026-08-08)

Ce chantier s'est vidé par le bas

Aucun de ses items n'a jamais été construit « pour lui-même » : chacun est arrivé en dépendance d'un autre chantier, et le statut global n'a jamais suivi — d'où ce chapô, deux fois en retard sur le code. L'agencement de la page de paramètres est arrivé avec les interrupteurs par bloc ; les deux derniers points qu'il annonçait encore ouverts au 2026-08-03 le sont depuis : l'icône d'engrenage vit dans le pied de page de la sidebar (.sidebar-plus-gear, livrée avec lui en 0.3.0) et le gel de la vue est livré comme épinglage de l'onglet lié (frozen dans sftpPanel.component.ts, 2026-08-08). Constaté le 2026-08-08 en recensant ce qui restait ouvert ; rien à construire ici, la fiche reste pour la trace de ce qui a été livré par ailleurs.

Agencement de la page de paramètres (Settings Layout) ✅ Fait (2026-08-03)

Livré autrement que par le découpage prévu

Les quatre sections décrites ci-dessous supposaient un classement par sujet. La page est finalement organisée par appartenance, sur la règle posée par l'utilisateur : une option vit avec l'interrupteur de la fonctionnalité dont elle dépend et disparaît avec elle ; seul ce qui régit le plugin entier ou Tabby reste en « Général ». Deux onglets suffisent alors — « Général » et « Fonctionnalités » — là où le plan en prévoyait quatre, parce que trois des quatre rubriques n'étaient que des regroupements thématiques de réglages qui appartiennent chacun à un bloc. Détail dans Interrupteurs par bloc.

Plan d'origine, conservé pour la trace :

Structurer l'interface de configuration du plugin en 4 sections distinctes :

  1. Général & Affichage : options de masquage de l'UI native redondante, gestion des raccourcis clavier globaux.
  2. Sessions Actives & SFTP : fréquences de rafraîchissement ✅ Fait (2026-08-01) — réglage sftpAutoRefreshSeconds, 0 par défaut ; restent l'interrupteur de gel de la vue et l'historique de file d'attente.
  3. Workspaces : liste des espaces de travail, import/export JSON sélectif, assignation des couleurs de bordure par défaut.
  4. Automatisation & Macros : gestion globale des Quick Snippets. Les logs du moteur d'Auto-Enter tombent avec sa sortie du périmètre.

Retours de la passe de test du 2026-08-01 ✅ Fait (2026-08-01)

Partage rapide de groupe (Presse-papiers) — au registre du réalisé.

Notes de profil (Mémento contextuel) — au registre du réalisé.

Indicateur de santé (Ping live) — au registre du réalisé.

Robustesse — vérifier le couplage à l'hôte au démarrage — au registre du réalisé.

Combler les points de veille restants — au registre du réalisé.

Édition de profil par l'API plutôt que par le DOM — au registre du réalisé.

Interrupteurs par bloc de la sidebar — au registre du réalisé.

Conformité de la gouvernance à la charte ✅ À jour — remise à niveau 20260804-071239 (2026-08-08)

Revue faite le 2026-07-29 (A-15, Cas B) — écarts constatés et corrigés détaillés dans AI-HISTORY, commit df8ef82. Ce n'est pas un état acquis : la charte évolue, ce projet doit suivre.

Remise à niveau vers la charte 20260804-071239 ✅ Fait (2026-08-08)

Trois révisions traversées depuis 20260803-182826 : 20260803-200821 et 20260804-071239 purement additives, 20260804-064238 touchant le noyau (deuxième exception nommée d'A-4 — conversion de format du journal — et option registre-livrés). Menée le 2026-08-08 : copies verbatim vérifiées par empreinte Git, et les deux options ajoutées tranchées explicitementregistre-livrés = oui (la roadmap dépassait 3 300 lignes, portées surtout par des chantiers déjà livrés), journal-format = tableau (statu quo, l'exception de conversion d'A-4 reste inutilisée). Motifs dans PROFIL.md.

Application dans le même geste : bascule initiale de 33 chantiers livrés vers annexes/REALISE.html, fiches reprises verbatim, la roadmap ne gardant qu'une ligne de renvoi par chantier à sa position d'origine (ancres conservées — les liens existants continuent d'aboutir). La fiche Écartée « Éditeur par type de fichier » a rejoint « Hors périmètre », où vivent les idées tranchées. Les branches de sync du 2026-08-04, créées avant la fusion de la phase 0.3.0 et donc périmées (les fusionner aurait annulé la phase), ont été supprimées et la propagation refaite depuis master.

Remise à niveau vers la charte 20260731-204511

Engagée le 2026-07-31. Trois révisions traversées depuis la dernière conformité du projet : 20260731-150737 et 20260731-203812 touchent le noyau — elles ne s'appliquent jamais d'office (A-7) —, 20260731-204511 est purement additive. Vérifié à la fusion : aucune option ajoutée ou retirée (22 de part et d'autre) et les quinze invariants sont inchangés, donc aucune question de cadrage nouvelle à arbitrer. La remise à niveau est terminée : les cinq items ci-dessous ont été menés dans l'ordre où ils sont listés, le pied de page en dernier comme le veut la règle rappelée plus bas.

ItemStatut
Copies verbatim de GOUVERNANCE-IA.md et GABARITS.md20260731-204511, vérifiées par empreinte Git ✅ Fait (2026-07-31)
Créer PROFIL.md — cadrage complet, profil complet, 22 options tranchées ✅ Fait (2026-07-31)
Migrer les références « Règle N » vers les identifiants A-x ✅ Fait (2026-07-31)
Audit complet A-15, Cas B — les quatre axes menés le 2026-07-31, dates de dernière revue mises à jour en conséquence ✅ Fait (2026-07-31)
Mention de conformité du pied de page de README.html : « du 2026-07-30 », forme datée antérieure aux identifiants, devient « version 20260731-204511 » ✅ Fait (2026-07-31)
Ce que l'audit du 2026-07-31 a trouvé
AxeRésultat
Contexte — 46 pièges confrontés au code de src/ Trois corrigés en place, aucun supprimé : #19 décrivait un correctif (min-height: 8px inconditionnel) qui n'existe plus, remplacé par celui de #26 ; #33 supposait qu'on montait SFTPPanelComponent lui-même, alors que le code monte une sous-classe à template propre dont les :host hérités ne s'appliquent jamais ; #5 justifiait --ignore-scripts par une dépendance retirée depuis, le flag restant nécessaire pour une autre.
Roadmap — 16 statuts « Fait » confrontés au dépôt Aucun à faire redescendre : chacun porte une trace de code et de validation ou de mise en service. Le glisser-déposer sortant était le cas d'école tenu correctement — code commité (8a89787), statut maintenu à « Proposé » faute de test à cette date ; il est passé à « Fait » le lendemain, une fois testé.
Journal — conformité A-4 Conforme, rien à corriger : aucun (commit en cours) survivant, aucune formule ambiguë, et les treize hashes cités pointent tous vers des commits réels.
Index et cohérence croisée Un écart : ce document annonçait « 24 options de part et d'autre » là où la charte en compte 22 — un comptage qui incluait par erreur deux valeurs de l'option seuil. L'erreur était symétrique, la conclusion tenait ; le chiffre contredisait PROFIL.md. Corrigé.
Deux points laissés à l'appréciation

Socle et Publication npm n'ont pas de mention « testé et validé par l'utilisateur » : cette convention d'écriture n'existait pas encore quand ils ont été livrés. Leur statut repose donc sur la mise en service constatée — paquet publié puis réinstallé et vérifié, jonction NTFS de dev active — plutôt que sur une validation consignée. Les faire redescendre appliquerait rétroactivement une convention postérieure à des chantiers en service.

La ligne « Charte au moment du cadrage » de PROFIL.md est passée à 20260731-204511 le 2026-08-03

Elle dit sous quelle version les options ont été tranchées, pas quelle version le projet possède. Elle ne devait bouger qu'au moment où le cadrage serait effectivement relu — l'avancer sans relecture aurait écrit un fait faux dans le fichier qui sert précisément à savoir sous quelle version chaque option a été décidée. Cette relecture a eu lieu : entretien complet rouvert le 2026-08-03, vingt-deux clés confrontées une à une, une seule valeur changée (seuilroadmap-libre). Détail dans AI-HISTORY.

Le pied de page se met à jour en dernier

Il atteste que le projet suit la charte, pas que sa copie est à jour — d'où son rang dans la liste ci-dessus, après l'audit et jamais avant. Tant que celui-ci n'a pas eu lieu, l'écart entre lui et l'identifiant de GOUVERNANCE-IA.md est l'état normal et lisible que décrit A-7 : « une copie à jour posée à côté d'un projet non remis à niveau … l'écart entre les deux identifiants est le signal ». La règle vaut pour la prochaine révision : l'avancer plus tôt afficherait une conformité que personne n'a vérifiée.

Les migrations « Règle N » d'AI-HISTORY.html n'ont pas été faites, et ne le seront pas : A-4 impose un journal en ajout seul, et ces mentions nommaient la règle telle qu'elle existait à la date de l'entrée.

Entretien récurrent

Ce qui reste à faire de façon récurrente, indépendamment de toute remise à niveau :

Ménage avant release soignée — au registre du réalisé.

Après la V1.0 — retenus

Issus d'un backlog de cinq idées proposé par l'utilisateur le 2026-08-08 et instruit contre le code réellement installé (Tabby 1.0.235, sources TypeScript livrées). Trois des cinq sont parties en « Hors périmètre » ci-dessous ; les deux qui suivent sont ce qu'il en reste d'utile. Aucune n'est ouverte : elles attendent une consigne explicite (option roadmap-avant-code).

Envoyer une commande à N sessions choisies 📋 Retenu, non ouvert (2026-08-08)

Ce qui subsiste de l'idée « Multi-Exec / Broadcasting » une fois retiré ce que Tabby fait déjà. Le broadcast natif existeMultifocusService de tabby-terminal, raccourcis Ctrl-Shift-I (panes du split) et Ctrl-Alt-Shift-I (tous les onglets), plus une entrée du menu contextuel d'onglet — donc un interrupteur maison qui rejoue la frappe serait redondant, et inférieur : le service s'annule au premier clic dans un terminal sans exposer aucun moyen de l'observer, si bien qu'un interrupteur afficherait « actif » sur une diffusion morte.

Deux manques réels, que la sidebar est la seule à pouvoir combler puisqu'elle tient déjà les onglets vivants dans « Sessions actives » :

L'esprit du refus de « Group Exec » gouverne ce chantier

La saisie synchronisée a déjà été refusée une fois — « taper une commande sur douze serveurs de production ne doit pas être l'effet de bord d'un ouvrir tout » (Group Exec, clos le 2026-08-03). Le refus portait sur le couplage à l'ouverture groupée, pas sur la fonction ; un composeur à sélection explicite n'est pas ce qui a été écarté. Mais rien ne doit diffuser par effet de bord : sélection opt-in ligne par ligne, jamais « tout » par défaut.

À trancher avant de coder : miroir de frappe ou envoi de ligne (recommandation : envoi de ligne) ; le seuil de sessions au-delà duquel une confirmation s'impose ; et le retour par ligne — sendInput() fait this.session?.feedFromTerminal(...), donc sur une session morte rien ne se passe et rien ne le dit, ce qui ferait croire la commande passée partout.

Étapes, délais et expect dans un snippet 📋 Retenu, non ouvert (2026-08-08)

Ce qui subsiste de l'idée « Visual Macro Manager », dont la fiche du registre porte déjà le nom (Quick Snippets — Macros de Sidebar) : un snippet est un texte multi-lignes envoyé tel quel dans le pty, donc déjà une macro multi-commandes, avec variables, héritage profil → dossier → racine et trois réglages tri-état. Ne manquent que deux choses :

Piège à ne pas rater : l'expect natif compare chunk par chunk, sans tampon inter-chunks — une invite coupée entre deux paquets est ratée. Un expect honnête doit tenir son propre tampon glissant et retirer les séquences ANSI. Voir aussi l'idée non tranchée plus bas.

Outils réseau au menu contextuel — non tranché ⚠️ Sans décision (2026-08-08)

Quatrième idée du backlog (Ping / Traceroute / Nmap depuis le menu d'un profil, dans un onglet local). Instruite, mais volontairement laissée sans décision : ni retenue, ni écartée. Consignée pour que l'instruction ne soit pas à refaire.

Deux pièges relevés à l'instruction, à ne pas redécouvrir : lire options.port sans regarder type donne COM3 sur un profil série (l'entrée doit être masquée hors SSH/Telnet) ; et sous Windows, un binaire absent fait ouvrir un onglet vide qui ne se ferme jamais, sans message — le renderer reçoit son id avant que le processus principal ne lève l'exception. Détecter avant de lancer.

Entretien du paquet et du dépôt — retenus

Issus des trois notes du tableau de bord GitHub de l'utilisateur (users/TooMuhtsh/projects/1, écrites vers le 2026-08-10), consignées et instruites ici le 2026-08-14. Les mêmes notes visent aussi tabby-better-vault — sa part se traite dans ses propres sessions, rien ici ne la couvre. Aucune n'est ouverte : elles attendent une consigne explicite (option roadmap-avant-code).

Vulnérabilités npm de l'outillage de build ✅ Lot sans majeure livré et validé (2026-08-14) — reste en surveillance

Validé par l'utilisateur le 2026-08-14 (SVG de test collé au sélecteur d'icônes), fusionné sur master (e5b9a55). La passe de validation a fait naître un correctif connexe, livré et validé le même jour : l'avertissement de retraits passe par un toast (fusion 4d5c6a3, journal). Reste en surveillance : les 18 vulnérabilités restantes sont bloquées par Angular 15 (imposé par Tabby amont) et pug 2 — rien à faire ici tant que l'amont ne bouge pas ; re-mesurer à chaque montée de version de Tabby.

Lot « sans majeure » exécuté le 2026-08-14 sur la branche fix/audit-sans-majeure, sur consigne de l'utilisateur : npm audit fix nu, 22 → 18 vulnérabilités (1 low, 3 moderate, 14 high), les quatre attendues corrigées — dompurify 3.4.12 → 3.4.13, nanoid 3.3.16 → 3.3.18, fast-uri 3.1.4 → 3.1.5, brace-expansion 2.1.2 → 2.1.4. Seul package-lock.json a bougé, package.json est intact ; builds de production et de développement verts, aucun warning nouveau, dist/ laissé en build de dev pour la jonction.

Compatibilité de dompurify — le seul du lot qui voyage dans le bundle — instruite contre l'amont : l'advisory corrigée (GHSA-55q2-fjhq-7xh7) exige IN_PLACE et un hook de retrait, que le plugin n'utilise nulle part (un unique appel dans svgSanitizer.ts, profil SVG + allow-lists passées à l'appel, aucun hook) — nous n'étions pas vulnérables en 3.4.12. Le seul changement du diff sur notre chemin est le hoisting des enfants par déplacement au lieu de clonage : optimisation, même arbre final attendu. Validation avant fusion (le décompte « N retirés » alimente un message utilisateur) : coller dans le sélecteur d'icônes un SVG mêlant balises autorisées et non autorisées (defs, use, text autour de path/rect) et vérifier que le rendu et le décompte sont ceux d'avant.

Mesuré le 2026-08-14 : npm audit rend 22 vulnérabilités (1 low, 4 moderate, 17 high), toutes dans les devDependencies. Provenance établie le même jour :

Un npm audit fix sans --force réglerait donc 4 des 22 (dompurify compris) ; les 18 restantes sont conditionnées à Angular 19+ (bloqué par Tabby) et à pug 3. Non urgent, jugé ainsi par l'utilisateur dans la note d'origine — mais si une seule chose doit se faire vite, c'est le bump de dompurify.

Le préalable posé par la note — « vérifier si apply-loader est encore nécessaire » — est instruit : il est utilisé (webpack.config.js, { test: /\.pug$/, use: ['apply-loader', 'pug-loader'] }). pug-loader rend une fonction de template et apply-loader l'applique, ce qui donne la chaîne que template: require('./x.pug') attend. S'en passer n'est pas un retrait mais un changement de chaîne de chargement pug, à instruire comme tel. Il est par ailleurs abandonné de fait — dernière publication le 2017-04-28 (2.0.0, vérifié au registre le 2026-08-14) — et ne tient sous webpack 5 que parce que la config ne lui passe aucune option : constat convergent avec l'instruction du même sujet menée côté tabby-better-vault (rapportée par l'utilisateur le 2026-08-14). Posture commune aux deux plugins : en surveillance, pas en urgence — toute évolution de la config qui voudrait lui passer des options rouvre la question du remplacement.

Contrainte à ne pas perdre : pug@^2.0 et les versions liées sont verrouillés par Angular 15 (piège #4) — un npm audit fix --force monterait ou rétrograderait des majeures et casserait le build. Le chantier, quand il s'ouvrira : npm audit fix nu, build de production revérifié, et l'éventuel passage à pug 3 instruit séparément avec le remplacement du loader.

Montée de version TypeScript / webpack-cli 📋 Retenu, non ouvert (2026-08-14)

La note d'origine juge le bump « low-risk » parce que ce sont des devDependencies isolées du runtime de Tabby. Vrai pour webpack-cli 4→5 (webpack 5 est déjà en place) ; faux pour TypeScript : Angular 15 — imposé par la peer dependency de tabby-core — plafonne TS strictement sous 5.0, son compilateur refusant au-delà (piège #4). typescript@4.9.5 est donc déjà le plafond atteignable ; passer à TS 5.x n'est pas un bump mais une migration Angular 16+, hors de portée tant que Tabby n'y est pas — même conclusion atteinte indépendamment côté tabby-better-vault. Le chantier réel : webpack-cli et les loaders, en période calme, build de production revérifié. À savoir avant de le juger urgent : l'audit du 2026-08-14 montre que webpack-cli@4 ne tire aucune vulnérabilité — ce bump est de l'hygiène d'outillage, pas de la sécurité.

CHANGELOG.md du dépôt ✅ Fait — introduit avec la 1.0.3 (2026-08-14)

Livré avec la 1.0.3 : CHANGELOG.md à la racine, rétro-rempli de la 0.2.0 à la 1.0.3 depuis les releases GitHub et le journal, et ajouté à files — il voyage dans le paquet npm. Règle d'entretien qui reste : une entrée par publication, écrite au moment du bump de version, en anglais comme le README.

Aucun CHANGELOG.md dans le dépôt, alors que les versions s'enchaînent (1.0.0 → 1.0.1 → 1.0.2). État réel nuancé le 2026-08-14 : les releases GitHub v0.4.0 → v1.0.2 portent déjà un titre et des notes — le manque est le fichier au dépôt, seul visible depuis la page npm et le clone, et la couverture des versions antérieures (0.2.0, 0.3.0). Rétro-remplissage possible depuis les releases et le journal. À faire au plus tard avec la prochaine publication, pour que la version qui l'introduit le mentionne.

Hors périmètre

Intégration X11 ⛔ Écarté — déjà natif (2026-08-08)

Idée du backlog du 2026-08-08 : redirection X11 transparente, détection des serveurs X locaux, interrupteur par profil, éventuellement en plugin séparé tabby-better-x11. Sans objet : Tabby le fait déjà, intégralement. russh expose requestX11Forwarding() et le flux des canaux x11 entrants ; tabby-ssh implémente la chaîne complète (src/session/ssh.ts : requête x11-req avec cookie MIT-MAGIC-COOKIE-1, relais vers la socket X locale par X11Socket, src/session/x11.ts), et affiche lui-même les liens VcXsrv/Xming quand aucun serveur X ne répond. L'interrupteur par profil existe déjà (Éditer le profil → onglet Advanced → « X11 forwarding », options.x11), ainsi qu'un réglage global « Override X11 display ». Comme notre entrée « Modifier… » ouvre la modale native, c'est atteignable depuis la sidebar sans une ligne de code. Aucun plugin tabby-*x11* n'existe sur npm — l'écosystème ne l'a pas fait parce qu'il n'y a rien à faire.

Un vrai défaut amont trouvé au passage, à signaler chez Eugeny plutôt qu'à contourner ici

La regex de X11Socket.resolveDisplaySpec() (tabby-ssh/src/session/x11.ts:10) est /^(.+):(\d+)(?:.(\d+))$/ : le groupe du numéro d'écran n'est pas optionnel. Vérifié par exécution — localhost:10.0 donne bien localhost:6010, mais localhost:1, host:10 et 192.168.1.5:0 ne correspondent à rien et retombent en silence sur localhost:6000. Le champ « Override X11 display » n'obéit donc qu'à la forme longue. La correction tient en un caractère ((?:\.(\d+))?, point échappé au passage). Si quelque chose doit sortir de cette idée, c'est cette PR — pas un plugin.

Signalé en amont le 2026-08-08 : Eugeny/tabby#11576. Bug confirmé présent dans master amont (14e2d60), aucune issue ouverte ne le couvrait. L'issue porte le comportement mesuré avant/après sur les deux plates-formes, le diff proposé, et l'effet de bord à connaître : sur Linux, un localhost:0 explicite passe de la socket Unix au TCP localhost:6000 — ce que la spec veut dire, mais que -nolisten tcp refuse sur beaucoup de distributions. La forme nue :0, elle, est inchangée.

Pertinence de fond, pour mémoire : X11 est devenu marginal — serveurs administrés sans bibliothèque X, alternatives web (Cockpit, Proxmox, console du fournisseur), VNC/RDP, X2Go, Wayland qui ne le fait pas fonctionner, et WSLg côté Windows.

Gestionnaire de macros visuel ⛔ Écarté — redondant avec les snippets (2026-08-08)

Idée du backlog du 2026-08-08 : enregistrer, sauvegarder et rejouer des séquences de frappes, avec délais et affectation par profil ou workspace. Redondant aux trois quarts : la fiche livrée s'appelle déjà « Quick Snippets (Macros de Sidebar) », un snippet est un texte multi-lignes envoyé tel quel dans le pty — donc déjà une macro multi-commandes —, et l'affectation par profil/dossier avec héritage est plus fine que ce que l'idée demande. Ce qu'elle ajouterait vraiment est isolé et retenu ailleurs : étapes, délais et expect.

L'enregistrement de frappes est écarté pour une raison de fond, déjà tranchée deux fois

frontend.input$ capte chaque frappe, y compris à une invite de mot de passe : les caractères sont absents de la sortie mais intégralement présents dans l'entrée, dans l'ordre. Ils atterriraient dans config.store.sidebarPlus, en clair — vérifié sur ce poste, config.yaml ne porte aucune clé encrypted:, seule la section vault: est chiffrée. Or ce projet a déjà tranché exactement cet artefact : scripts est purgé d'office à l'export parce qu'« un couple expect/send est l'endroit où un mot de passe sudo se retrouve en dur », et Auto-Enter est sorti du périmètre pour le même motif. Un enregistreur produirait cet artefact automatiquement, sans que l'utilisateur ait choisi de l'écrire. Ne pas le construire, ici ni ailleurs.

À savoir : tabby-quick-cmds existe et recouvre presque trait pour trait nos snippets, délais entre commandes compris — c'est le « Macro Manager » moins l'enregistrement.

Workspaces distants / partage d'équipe ⛔ Écarté — contrainte intenable (2026-08-08)

Idée du backlog du 2026-08-08 : s'abonner à un workspace par URL ou chemin réseau, rafraîchi périodiquement, « lecture seule en local, secrets jamais synchronisés ». Le transport est trivial (aucune CSP dans Tabby, fetch() est le style maison, Node entier dans le renderer, UNC lisible par fs) — et c'est précisément ce qui rend l'idée dangereuse : le sujet n'est pas le transport, c'est la réconciliation.

La contrainte « lecture seule » ne peut pas être tenue, donc elle ne doit pas être promise

Il faut séparer deux objets que l'idée confond. Un workspace est un overlay dont le plugin est seul écrivain : verrouillable. Un dossier et ses profils vivent dans config.store.groups/.profiles, données natives que les paramètres de Tabby éditent sans passer par le plugin — le verrou n'existerait donc que dans la sidebar, c'est-à-dire nulle part de fiable : une convention d'affichage vendue comme une garantie, ce qui est pire que rien. S'y ajoute que le re-parentage change l'id du dossier (piège #62), donc casse toute clé de rapprochement au premier glisser-déposer, et qu'un moteur de resynchronisation est fait de la question interdite par le piège #74 (« cet id existe-t-il encore ? », qui répond non pour des entrées vivantes). L'idée se dévore elle-même : sa partie sûre (un workspace nu) est inutile — ce ne sont que des listes d'ids qui ne désignent rien chez le destinataire — et sa partie utile (les profils) n'est pas sûre.

Sécurité, pour mémoire si le sujet revenait : un abonnement automatique est plus dangereux qu'un collage sur cinq points — l'acte disparaît (personne ne lit la notice), la source devient répétée (bénigne aujourd'hui, hostile dans trois mois), la surface d'attaque devient l'URL (DNS, TLS, dépôt compromis, ACL SMB qui glissent), le rendu est automatique (une icône hostile s'exécute quand l'arbre se peint) et le contexte d'exécution est maximal. C'est en instruisant ce point qu'ont été trouvés les trous de purge du chemin déjà livré — voir Durcir l'import de dossier collé.

Le besoin réel — « mes deux ou trois machines » — est déjà couvert par le partage de groupe niveau secrets, décrit comme « celle qui se recolle à l'identique entre ses propres machines ». Ce qui manquerait au plus, sans rien du risque : importer/exporter depuis un fichier plutôt que le presse-papiers — même parsePayload(), même modale, même double décompte, seule la source change. L'acte reste manuel et le contenu reste choisi.

Éditeur par type de fichier (notamment les images)

Écarté le 2026-08-03, après construction et essai

Décision de l'utilisateur à l'usage : « Ouvrir avec » du clic droit fait le travail plus directement, et la table par extension n'apportait rien qui vaille son coût. Le chantier a été construit puis retiré le même jour ; la branche n'a pas été conservée, cette fiche est la trace.

Ce que l'essai a montré, et qui vaut pour quiconque rouvrirait la question : la règle retenue au cadrage — un type inconnu demande plutôt que d'emprunter l'éditeur d'un autre type — est cohérente sur le papier et pénible à l'usage, puisqu'elle pose une question à chaque nouvelle extension rencontrée. L'alternative, un repli silencieux sur l'éditeur par défaut, revient exactement au comportement d'aujourd'hui pour tout ce qui n'a pas été renseigné à la main. Entre les deux, il n'y avait pas de position qui justifie la table.

Aucun reste dans config.yaml — vérifié le 2026-08-03 sur la configuration de l'utilisateur, une fois son chiffrement retiré : ni clé sftpEditors orpheline, ni sftpEditorPath vidé, qui porte toujours son éditeur. La migration a bien tourné en mémoire pendant l'essai, mais rien n'en a survécu sur le disque. Le retrait n'a donc rien laissé à nettoyer. Ce paragraphe annonçait l'inverse par précaution, avant vérification : c'est la vérification qui fait foi.

Le design ci-dessous est conservé tel quel, sans être transporté ailleurs.

Demandé par l'utilisateur le 2026-07-30, en constatant à l'usage que l'éditeur retenu par l'éditeur forcé est global : une seule clé sidebarPlus.sftpEditorPath, appliquée à tous les fichiers. Un éditeur de texte convient à un .yml ou à un fichier de conf sans extension, beaucoup moins à un .png.

Détection et validation des sessions interactives (Auto-Enter)

Sorti du périmètre le 2026-08-03

Décision de l'utilisateur, sur deux motifs distincts qu'il vaut mieux ne pas confondre.

L'utilité même n'est pas établie. Le design ci-dessous prévoit d'envoyer Entrée dès qu'une invite interactive est détectée : valider quand le mot de passe est pré-rempli, passer l'invite quand il est vide. Or si l'authentification par mot de passe enregistré fonctionne de bout en bout, le premier cas ne sert à rien — Tabby envoie déjà le mot de passe. Ne resteraient que les invites qu'il ne traite pas nativement : seconde étape de keyboard-interactive, question de sudo, facteur supplémentaire. Ce sont précisément les cas où un faux positif enverrait un Entrée parasite dans un shell de production. Tant qu'aucune invite précise n'est nommée, le chantier n'a pas de cible, et lui en chercher une par le code serait prendre le risque avant d'avoir le besoin.

Le rattachement à ce plugin est discuté. L'utilisateur le verrait plutôt du côté de tabby-better-vault. À noter pour qui reprendra la question : le geste décrit ici est d'écrire dans un pty distant, pas de lire un secret — ce qui toucherait au coffre-fort est l'origine du mot de passe, pas l'action. Le rapprochement est donc moins direct qu'il n'en a l'air, et trancher demande de regarder ce que l'autre plugin fait réellement plutôt que de le supposer : la décision se prend dans une session consacrée à ce projet-là.

Le design ci-dessous est conservé tel quel et n'a pas été transporté ailleurs — contrairement au Vault Auto-Unlock, dont le périmètre d'accueil était acquis au moment de sa sortie.

Automatisation du login en simulant l'appui sur "Entrée" dès qu'une invite interactive (login/password) est détectée. Si le mot de passe est pré-rempli, cela valide ; s'il est vide, cela passe l'invite.

Alerte de fragilité technique

L'injection de ce toggle près du champ mot de passe implique d'intervenir sur le DOM de la modale native EditProfileModalComponent. Cette zone étant critique et instable (voir piège #17 et le point fragile associé dans AI-CONTEXT.html), le hook d'injection Angular/DOM devra intégrer un mécanisme de repli (fallback) strict. Si le DOM de Tabby change lors d'une mise à jour de l'application, le toggle doit se déporter gracieusement dans le menu contextuel du profil ("Options Sidebar" → "Auto-Enter") ou dans les options globales, sans provoquer de plantage de l'interface.

Vault Auto-Unlock — plugin séparé tabby-better-vault

Décision actée

Le déverrouillage automatique du coffre-fort Tabby (éviter de ressaisir le mot de passe maître à chaque démarrage) ne fait pas partie de better-sidebar — ce n'est pas une fonctionnalité de sidebar, et c'est le point le plus sensible (manipulation d'un secret utilisateur) envisagé sur cet écosystème de plugins. Devient un plugin indépendant, tabby-better-vault, publié et versionné séparément — dossier de travail et gouvernance propres créés le 2026-07-26 : .AIRules de tabby-better-vault (lien relatif valable tant que les deux dossiers restent côte à côte dans Développement/). Design détaillé ci-dessous transporté à l'identique là-bas.

Onglet de réglages unifié entre better-sidebar et tabby-better-vault ✅ Livré le 2026-08-07 — validé 25/25 en conditions réelles

Si les deux plugins sont installés ensemble, un seul onglet « Better Tabby » apparaît dans les réglages Tabby, à la place des deux onglets côte à côte. Vérifié dans le bundle compilé (node_modules/tabby-settings/dist/index.js) : Tabby ne déduplique pas deux SettingsTabProvider de même id — un seul plugin à la fois doit donc déclarer l'onglet unifié, d'où l'élection ci-dessous. Conçu, codé et validé le même jour (cinq vagues de tests, 25/25, y compris poids inversés) ; la convention vit dans src/betterPanel.ts et le CLAUDE.md des deux projets, le pendant vault dans sa branche feat/panneau-unifie.

Détection inter-plugin, par token de chaîne : pas de classe TypeScript importée d'un plugin par l'autre — ça créerait une dépendance npm dure, alors que les deux plugins doivent rester installables indépendamment. Chaque plugin « Better * » présent expose, au niveau de son module racine, un provider Angular sous une clé BetterPanelContribution:<id> (ex. BetterPanelContribution:sidebar, BetterPanelContribution:vault), de forme :

interface BetterPanelContribution {
  id: string                 // identifiant stable ('sidebar', 'vault', ...)
  title: string               // libellé affiché en sous-onglet
  hostWeight: number           // poids d'élection de l'hôte — SANS RAPPORT avec le `weight` natif
                                // de SettingsTabProvider (qui ordonne les onglets Tabby de premier
                                // niveau, déjà utilisé ici avec la valeur 2), à ne pas confondre
  componentType: Type<any>  // le composant Angular déjà existant de ce plugin, réutilisé tel quel
}

Chaque plugin interroge les tokens des autres membres connus de la famille via injector.get('BetterPanelContribution:xxx', null) — jamais d'erreur si l'autre n'est pas installé. La liste des tokens à connaître est codée en dur et documentée dans le CLAUDE.md de chaque plugin, pas un registre automatique : un futur plugin « Better X » devra être ajouté à cette liste dans les projets existants pour rejoindre l'unification.

Élection de l'hôte, poids numérique, le plus léger gagne : chaque plugin déclare un hostWeight fixe. Au démarrage, un plugin calcule l'ensemble des contributions présentes (lui-même + celles détectées) et devient hôte seulement s'il porte le hostWeight le plus faible parmi les présentes (égalité départagée par ordre alphabétique d'id, cas improbable mais couvert). Valeurs : better-sidebar = 10, better-vault = 20, espacées de 10 pour insérer un futur membre sans renuméroter.

Tel que réalisé — deux écarts à la conception d'origine, tous deux tranchés en cours de route :

Deux limites découvertes à la passe de test, non bloquantes

Le pendant symétrique côté coffre-fort est réalisé dans son propre projet (branche feat/panneau-unifie, même convention, contrat dupliqué sans import npm) — voir .AIRules de tabby-better-vault (lien relatif valable tant que les deux dossiers restent côte à côte dans Développement/).

Importateur MobaXterm

Explicitement écarté par l'utilisateur : des outils de conversion (.mxtpro/.ini) existent déjà, non prioritaire. À reconsidérer seulement si ce point de friction remonte concrètement en usage réel.