J'ai encore fait un terminal, Unterm — son utilisateur par défaut n'est pas humain
Un chiffre d’abord : ces six derniers mois, plus de 80 % des commandes exécutées dans mon terminal ne venaient pas de moi. C’était Claude Code, Codex, toute une flopée d’agents, au boulot toute la journée, chaque tâche roulant facilement plusieurs heures.
Le problème vient de là. Les terminaux que j’utilisais — iTerm, Windows Terminal, Warp — ont tous été conçus pour « une personne assise qui tape » : une ligne, un coup d’œil, encore une ligne. Quand l’utilisateur principal est passé de moi à un agent, cette hypothèse par défaut a lâché sur plusieurs points. J’ai encaissé les coups un par un, et j’ai fini par construire mon propre terminal. Il s’appelle Unterm.
Ce qui m’a vraiment poussé à me lancer, c’est une semaine où j’ai regardé comment je passais mes journées : une bonne moitié à servir d’administrateur réseau, d’auditeur de sécurité et de gestionnaire de fenêtres pour les agents — aucune de ces tâches ne relevait du « produit » lui-même, mais personne ne s’en chargeait à ma place, alors j’ai construit un truc pour les absorber.
Voici les brèches que j’ai rencontrées, et comment je les ai colmatées.
Unterm : qu’est-ce que c’est, et d’où vient le nom
En une phrase : Unterm est un terminal conçu pour être piloté directement par des agents IA externes (unterm.app, open source, GitHub zhitongblog/unterm).
Ce n’est pas un « terminal avec une IA intégrée ». Il n’a pas de fenêtre de chat — à la place, il expose l’intégralité du terminal via le protocole MCP, pour que les agents puissent ouvrir des fenêtres, taper des commandes et lire les sorties à l’écran. Le slogan que j’ai écrit pour lui se résume à une phrase :
The terminal that runs every AI coding agent. (Le terminal capable de faire tourner chaque agent de code IA.)
La thèse derrière, aussi en une phrase :
Un terminal n’a pas besoin d’une IA greffée à l’intérieur — il doit pouvoir être piloté de l’extérieur par une IA.
Côté capacités : installation en un clic de Claude Code, Codex CLI, Gemini CLI, OpenCode, Aider ; local d’abord, open source, scriptable (avec un unterm-cli) ; interface disponible en neuf langues.
En pratique, ça ressemble à ça : je dis à Claude Code « lance le build de ce projet, et si ça plante, lis les logs et corrige », il se connecte à Unterm via MCP, ouvre lui-même une fenêtre, tape ses commandes, lit les sorties à l’écran, et si une erreur surgit, il la corrige lui-même — sans que j’aie à copier-coller quoi que ce soit entre la fenêtre de chat et le terminal. Le terminal est devenu pour la première fois les mains de l’agent, et non plus un relais où je transporte manuellement les sorties. Quand je veux suivre, je jette un œil ; quand je ne veux pas, il avance tout seul.
Le nom est simple : Un + term. term, c’est terminal ; Un, c’est « libérer » — libérer la frappe au clavier des mains humaines pour la confier à l’IA. Quand je l’ai construit, j’avais une seule image en tête : ce terminal n’est pas fait pour mes mains, il est fait pour les agents ; je recule et je surveille. C’est exactement ce que dit le nom.
1. Proxy : rendre le mur invisible pour l’agent
Je suis en Chine, et 80 % du travail d’un agent nécessite de passer le pare-feu : installer des paquets, git clone, télécharger des poids de modèles, appeler des API étrangères.
Dans un terminal ordinaire, c’est à moi de faire export HTTPS_PROXY à la main, une fois par shell. Mais dès que l’agent ouvre une nouvelle fenêtre ou forke un sous-processus, le proxy s’évapore — et là, blocage, timeout, network error. Le problème, c’est que l’agent ne sait pas que c’est un problème réseau : il croit avoir mal écrit sa commande, alors il modifie la commande, change de source, réessaie, et enfonce tout encore plus. Une nuit, je lui ai demandé de télécharger des poids de modèle. Le lendemain matin, j’ai vu qu’il avait réessayé pendant plus de quarante minutes sur un proxy mort depuis longtemps, les logs avaient envahi tout l’écran, et pas un seul octet n’avait été téléchargé.
J’ai donc intégré le proxy dans le terminal lui-même. Unterm embarque une couche proxy connectée à Clash, et toutes les sessions passent par défaut par les mêmes routes. J’ai configuré un pool de nœuds (en ce moment, huit lignes Singapore optimisées pour Gemini et GPT), le terminal se teste tout seul toutes les trente secondes, prend la route la plus rapide ; si le nœud actif tombe, il bascule automatiquement sur le nœud vivant le plus rapide du pool. Et le proxy est configuré au niveau du système d’exploitation (registre Windows, scutil sur macOS, variable d’environnement sous Linux), pas shell par shell — chaque fenêtre ouverte ensuite hérite du proxy d’emblée.
Certains détails ont été arrachés par l’expérience. Par exemple, localhost et les adresses intranet ne doivent pas passer par le proxy, sinon l’agent ne peut même plus se connecter aux services qu’il a lui-même lancés en local — ces règles no-proxy doivent être configurées par défaut, pas rattrapées après que l’agent soit tombé dedans. Autre exemple : le pool de nœuds ne devrait pas se gérer à la main — Unterm lit directement les groupes Clash et la latence en temps réel de chaque nœud, je construis un pool tournant en quelques clics dans l’interface, quels nœuds entrent dans le pool, à quelle fréquence il tourne, sans jamais toucher un fichier de configuration.
Ce n’est pas une solution miracle : si toutes les lignes du pool sont bridées, je suis coincé quand même. Mais le cas « ce nœud-là est encore en train de faire des siennes » qui arrive huit fois par jour, il s’en occupe tout seul.
2. Sécurité : donner la machine à l’IA, c’est lui donner rm -rf aussi
Donner un terminal nu à un agent, c’est lui donner toute la machine. rm -rf, git push --force, DROP TABLE — une commande mal tapée, et c’est perdu pour toujours, d’autant qu’il tape plus vite que vous ne réagissez. Un terminal ordinaire ne sait pas que derrière se trouve une IA : il traite l’agent et moi comme la même paire de mains.
Je ne voulais pas choisir entre les deux extrêmes : le laisser courir nu à mes risques et périls, ou confirmer chaque commande à la main, ce qui revient à taper moi-même. Unterm a donc ajouté quelques barrières intermédiaires :
- Par défaut « suggestion », pas exécution directe : l’agent dépose la commande dans ma ligne d’entrée, mais ne presse pas Entrée à ma place — un Tab de ma part, et ça passe.
- Autorisation par paliers : toute une classe de commandes sûres passe automatiquement ; seuls
rm -rf, force-push etDROP TABLE— les trucs qui peuvent vraiment tout casser — sont bloqués pour une confirmation séparée. - Identités isolées : chaque fenêtre dispose d’un profil d’identité indépendant (tokens GitHub / AWS / npm, identité git, clé SSH) ; l’agent reçoit ce périmètre délimité, pas l’ensemble du trousseau de clés de ma machine.
- Confiance explicite : c’est moi qui décide quel agent est de confiance ; les écritures de ceux qui le sont passent sans confirmation.
- Lecture des erreurs réelles : il peut scanner les lignes qui ressemblent à des erreurs sur l’écran — il lit les vrais résultats, pas ce qu’il imagine.
Le réglage par défaut est « conservateur » : un agent fraîchement connecté, pas encore de confiance, passe par moi pour tout ; une fois que je l’ai regardé tourner quelques fois et confirmé qu’il est fiable, j’ouvre en bloc la catégorie de commandes sûres qu’il utilise le plus. C’est un peu plus lent pendant la période de rodage, mais ça ne me donne pas de sueurs froides dès le premier démarrage.
À force de travailler avec des agents, on finit forcément par tomber sur l’un d’eux qui tape un rm -rf avec une belle assurance pour « nettoyer le cache », mais avec un chemin légèrement faux. Un humain marque une demi-seconde d’arrêt avant d’appuyer sur Entrée. Un agent, non — cette pause, c’est le terminal qui doit la fournir à sa place.
3. Enregistrement : une fois l’agent terminé, je veux pouvoir rembobiner
C’est la fonctionnalité dont je me sers le plus, et dont je ne pourrais plus me passer.
L’agent tourne trois heures sans surveillance, et quand vous revenez, vous voulez savoir exactement ce qu’il a fait. Lire le résumé qu’il a écrit lui-même ne suffit pas — il peut omettre, embellir, voire se couvrir. Ce qu’il vous faut, c’est la trace brute : chaque commande, chaque sortie, conservées telles quelles.
Unterm enregistre donc toute la session du début à la fin : ce que l’agent a tapé, ce que le terminal a renvoyé, tout est stocké dans un enregistrement rejouable — on peut le rembobiner comme une vidéo ou en feuilleter l’historique commande par commande. Les tokens et les clés sont automatiquement masqués à l’enregistrement, pour que ce fichier ne devienne pas lui-même une fuite de données.
C’est la boîte noire de l’agent : quand quelque chose déraille, je rembobine pour voir à quelle étape ça a bifurqué ; quand tout se passe bien, je le garde comme démo ou matériau de rétrospective.
Un exemple de la semaine dernière : un agent a planté une série de tests, puis a tranquillement déclaré « c’est réparé ». J’ai rembobiné la session jusqu’au moment où il a touché le code, et en quelques minutes c’était clair — pour faire passer les tests au vert, il avait discrètement commenté une assertion. Avec seulement le résumé qu’il avait écrit, je n’aurais jamais attrapé ça. L’enregistrement peut aussi être exporté par blocs entiers — je l’ai utilisé plusieurs fois comme matériau pédagogique pour montrer « comment l’IA travaille vraiment ».
Si j’ose laisser tourner un agent sans surveillance, c’est pour moitié grâce à cet enregistrement qu’on peut rembobiner.
4. Parallélisme : je fais tourner plusieurs agents depuis longtemps
La normale aujourd’hui, c’est trois ou quatre agents qui tournent en même temps : l’un s’occupe du front-end, l’autre attaque la migration de base de données, un autre complète les tests. Dans un terminal ordinaire, ce sont trois ou quatre fenêtres noires entre lesquelles je fais alt-tab, et à force de basculer j’oublie qui attend une décision de ma part et qui est planté là à rien faire.
Unterm a rendu la gestion d’une flotte naturelle :
- Lancement en masse : une commande ouvre une nouvelle fenêtre et lui assigne le travail directement ;
- Diffusion : si je veux que les quatre agents « tirent le dernier code et relancent le build », une commande part vers toutes les fenêtres — pas besoin de coller quatre fois ;
- Attente suspendue : une fois le travail distribué, je peux attendre qu’une fenêtre signale « terminé / erreur » avant de revenir la traiter.
En pratique, un matin ressemble à ça : j’ouvre trois fenêtres, j’assigne à chacune « refaire la page de connexion selon le nouveau design », « migrer la table des commandes vers le nouveau schéma », « compléter les tests pour la logique de règlement » ; je diffuse ensuite « poster l’avancement toutes les dix minutes », puis je vais répondre à mes messages. Je reviens, je balaie l’état des trois fenêtres d’un coup d’œil — si l’une est bloquée, j’entre dedans et lui dis deux mots, les autres continuent. Je suis passé de « surveiller l’un après l’autre » à « balayer la rangée d’un regard ».
Mon travail aujourd’hui, ce n’est pas d’écrire du code, c’est d’orchestrer une flotte d’agents (j’en ai parlé dans l’article précédent). Et pour orchestrer, il faut avoir entre les mains un poste de commandement qui tient la route — pas quatre boîtes noires qui s’ignorent l’une l’autre et dont je dois garder le mapping dans ma tête.
5. Fenêtres : plus il y a d’agents, plus le bureau part en chaos
Quand la flotte tourne, ce qui devient ingérable, ce ne sont pas les commandes, c’est l’espace : quelle fenêtre est en train de déployer, laquelle est un brouillon ouvert à la va-vite pour tester un truc, laquelle a été envoyée en mission il y a deux heures et n’a toujours pas donné signe de vie ? Essayer de retenir ça dans sa tête, quelques fenêtres suffisent pour que ça s’emmêle.
Ce que j’ai fait, c’est l’ordre le plus basique qui soit :
- Les fenêtres ont un nom : alpha, bravo, charlie, pas des numéros impossibles à retenir. C’est du vécu : en ce moment même, pendant que j’écris cet article, j’ai deux instances Unterm actives sur ma machine — alpha est ouverte dans le répertoire du site doaipm, bravo dans ce dépôt de contenu — un coup d’œil suffit pour savoir qui est qui.
- Les layouts se sauvegardent et se restaurent : les panneaux bien découpés, les fenêtres bien rangées, tout s’enregistre comme un workspace et retrouve le même état au prochain démarrage — plus besoin de reconstruire les Lego chaque matin.
- Appel par nom : l’agent peut dire « mets la fenêtre de déploiement au premier plan » — je n’ai pas à fouiller dans une dizaine de cadres.
La fonctionnalité workspace a l’air anodine, mais en pratique c’est un vrai gain : j’ai un layout fixe « publication d’article » — une fenêtre pour le répertoire du site, une pour le dépôt de contenu, une pour lancer le build, une pour surveiller les logs — une fois sauvegardé, une touche au démarrage le restitue, les quatre fenêtres avec leur cd déjà fait, sans rien à replacer.
Entre douze fenêtres sans nom et un poste de travail capable de nommer chacune, c’est toute la différence.
Et tmux ? Et Warp ?
Vous allez demander : tmux a le multi-panneau depuis longtemps, Warp a greffé une IA à l’intérieur — qu’est-ce que vous apportez en plus ?
tmux, je l’ai utilisé pendant des années, le multi-panneau est très bien fait, mais il est conçu pour les dix doigts d’un vrai humain : il ne sait pas si derrière se trouve un agent, il ne se soucie pas que votre commande risque de vider une base de données, et il n’a aucune idée que vous êtes en Chine avec chaque commande vers l’extérieur qui se débat dans des timeouts. Warp est une autre voie : une IA greffée dans le terminal — mais c’est « une IA qui vit dans le terminal », pas « un terminal piloté par une IA » : le protagoniste reste la personne devant l’écran, l’agent au mieux copilote, et les problèmes de proxy et de réseau chinois, ça ne le concerne pas. iTerm et Windows Terminal, je les utilise aussi tous les jours, ce sont de bons terminaux — mais « bon » veut dire « agréable pour l’humain » : couleurs, polices, raccourcis, division des panneaux, et pas un seul de ces choix de conception ne traite « celui qui tape, c’est un agent » comme un citoyen de première classe.
Les deux approches sont bien — elles ne répondent simplement pas à ma question : quand l’utilisateur principal passe de l’humain à l’agent, à quoi doit ressembler le terminal ? Le proxy, les barrières de sécurité, l’enregistrement qu’on peut rembobiner, l’orchestration de flotte, la gestion des fenêtres par nom — pour moi, ce ne sont pas des fonctionnalités isolées, ce sont les différentes faces d’un même problème.
Enfin
Les terminaux ne manquent pas. En faire un de plus, j’ai moi-même senti que ça méritait une explication.
La raison tient en une phrase : je ne voulais pas faire un terminal plus ergonomique — je voulais faire un terminal dont l’utilisateur par défaut n’est pas humain. Rien de ce qui est décrit au-dessus ne vient d’une roadmap — c’est l’ensemble des choses qui m’ont vraiment exaspéré à cette époque.
Un point assez concret : Unterm lui-même a été construit en décrivant à l’IA ce que je voulais, en langage ordinaire, dans le terminal — je n’ai pas écrit son code de production. J’ai expliqué clairement « ça doit pouvoir être piloté par un agent, se basculer sur un proxy tout seul, bloquer rm -rf, rejouer toute une session » — et l’IA l’a construit. Si on sait le décrire, elle sait le construire — c’est comme ça que j’ai toujours travaillé.
Si le monde avait besoin d’un terminal de plus, je n’en sais rien. Moi, j’en avais besoin.
Pour aller plus loin
- L’article précédent de cette série : D’exécutant à orchestrateur : votre nouveau métier, c’est diriger une flotte d’agents
- Site officiel / dépôt open source : unterm.app · GitHub
zhitongblog/unterm
Discussion