Ce qu’il faut retenir pour une CLI Node.js interactive
- La bibliothèque sert à créer des questionnaires de terminal clairs, guidés et validés sans écrire toute la mécanique à la main.
- Pour un nouveau projet, je pars plutôt sur
@inquirer/prompts, et sur@inquirer/coreseulement si j’ai besoin d’un prompt sur mesure. - Les types les plus utiles sont
input,confirm,select,checkbox,search,password,numbereteditor. - Le support du français existe via
@inquirer/i18n, ce qui évite de bricoler les libellés à la main. - La contrainte principale reste l’interactivité réelle du terminal: sans TTY, le prompt ne se comporte pas correctement.
Pourquoi cette bibliothèque reste utile dans un outil CLI
Je l’utilise quand un outil doit guider l’utilisateur sans lui faire perdre de temps. Le vrai gain n’est pas décoratif; il est dans une UX plus nette et moins de logique maison à maintenir côté code. Quand je prépare un script de configuration, un assistant d’installation, un outil d’administration ou un générateur de projet, je préfère laisser la bibliothèque gérer le flux de saisie plutôt que de reconstruire tout le comportement à la main avec des événements clavier et de l’état dispersé.
Ce qui me plaît, c’est aussi la lisibilité. Une CLI mal pensée demande souvent de relire deux fois la même question, de comprendre si la réponse attendue est libre ou fermée, puis de deviner comment revenir en arrière. Avec un bon prompt, l’utilisateur comprend immédiatement ce qu’il doit faire, et le code reflète cette intention au lieu de la cacher derrière du code impératif un peu pénible à suivre.
Je réserve donc ce type d’outil aux scénarios où il faut poser plusieurs questions, valider des valeurs et enchaîner des choix. Pour une seule saisie triviale, un module plus bas niveau peut suffire. C’est justement pour ça que je regarde toujours la version du paquet et la façon de l’importer avant de brancher le moindre prompt.
Installer la bonne brique sans tomber sur l’ancienne API
Le point important aujourd’hui est d’éviter les tutoriels qui reposent encore sur l’ancienne API unique. La branche actuelle a été réécrite pour alléger le paquet et améliorer les performances; le paquet historique existe toujours pour la compatibilité, mais je ne le choisis plus pour un nouveau projet.
npm install @inquirer/prompts
import { input } from '@inquirer/prompts';
const answer = await input({ message: 'Quel est ton prénom ?' });Si je dois construire un prompt spécifique, je passe à @inquirer/core. Si l’outil doit parler français sans bricolage localisé à la main, je regarde @inquirer/i18n, qui peut lire les variables LANGUAGE, LC_ALL, LC_MESSAGES et LANG, puis retomber sur Intl si besoin. Je peux aussi forcer un import dédié comme @inquirer/i18n/fr. Quand j’ai besoin d’aller plus loin, je garde en tête les fonctions de localisation personnalisée, mais je ne les sors que pour des CLI qui doivent durer et évoluer.
Cette distinction évite de surcharger le projet dès le départ, et elle mène naturellement au vrai sujet pratique: quel prompt choisir pour quelle interaction.

Les prompts essentiels et ce que chacun résout
Je choisis les invites comme je choisirais un type de champ dans une interface web: je ne prends pas le plus riche, je prends le plus juste. Un bon prompt réduit l’effort cognitif, évite les réponses ambiguës et garde le terminal lisible, même quand le flux comporte plusieurs étapes.
| Prompt | Quand je l’utilise | Ce que ça m’apporte |
|---|---|---|
input |
Nom, e-mail, identifiant court, valeur simple | Le plus direct, avec validation légère |
confirm |
Décision binaire | Évite les réponses ambiguës |
select |
Une seule option dans une liste courte | Lisible, rapide, sans saisie libre |
checkbox |
Plusieurs choix possibles | Pratique pour des options combinables |
search |
Liste longue ou catalogue important | Réduit le temps de navigation |
password |
Secret, jeton, mot de passe | Masque la saisie |
number |
Port, délai, quantité, seuil | Validation numérique intégrée |
editor |
Message long, description, JSON, texte multi-ligne | Confortable quand la saisie dépasse une ligne |
Je réserve search aux listes qui dépassent franchement ce qu’un select peut montrer confortablement. rawlist et expand me servent plus rarement, mais ils restent utiles quand je veux des raccourcis clavier explicites ou un affichage plus compact. Le bon réflexe, c’est de faire correspondre la forme du prompt à la forme réelle de la décision.
Une fois le bon prompt choisi, le vrai travail commence: rendre le parcours fluide, réversible et simple à maintenir.
Écrire un flux de questions propre et robuste
Ce qui fait la différence entre une CLI agréable et une CLI pénible n’est pas la quantité de prompts, mais la manière de les enchaîner. Je garde les messages courts, j’ajoute des valeurs par défaut quand elles ont du sens, et je fais en sorte que chaque réponse conditionne la suivante de façon explicite.
import { input, confirm } from '@inquirer/prompts';
const sendEmail = await confirm({
message: 'Autoriser l’envoi d’e-mails ?',
});
const email = sendEmail
? await input({ message: 'Adresse e-mail' })
: null;Je traite aussi l’arrêt du prompt comme un cas normal. Un utilisateur peut faire Ctrl+C, et le programme doit sortir proprement sans afficher une pile d’erreurs inutile. Quand j’ai une question qui ne doit pas rester ouverte indéfiniment, j’utilise un AbortSignal, par exemple avec AbortSignal.timeout(5000), pour convertir l’attente en abandon contrôlé. Quand je veux nettoyer le terminal une fois la réponse capturée, je passe clearPromptOnDone dans le contexte. Si je veux distinguer un arrêt volontaire d’un vrai incident, je capture aussi ExitPromptError explicitement.
Pour les outils destinés à des utilisateurs francophones, je trouve plus propre de laisser le paquet d’i18n suivre la langue de l’environnement plutôt que de traduire chaque message au fil de l’eau. C’est ce genre de détail qui donne une impression de produit fini, pas juste d’outil qui fonctionne.
Ces choix d’ergonomie n’ont cependant de valeur que si le terminal reste réellement interactif, ce qui nous amène aux limites concrètes de la bibliothèque.
Les limites à connaître avant de le brancher en production
Je fais attention à trois scénarios, parce que ce sont eux qui cassent le plus souvent les intégrations: les hooks Git, les scripts lancés sans terminal interactif et les environnements où l’entrée standard est redirigée. Dans ces cas-là, Inquirer n’est pas faux, il est juste hors contexte.
| Situation | Ce que je choisis | Pourquoi |
|---|---|---|
| Assistant multi-étapes avec validation | Inquirer | Le flux reste lisible et maintenable |
| Une seule question simple, script ponctuel | readline |
Moins de dépendances et assez pour une interaction très courte |
| Besoin d’un prompt très spécifique | @inquirer/core |
Je fabrique mon interface sans repartir de zéro |
| Base de code ancienne avec beaucoup de prompts communautaires | Migration progressive | Je limite le risque de casse en gardant le contexte historique |
Si j’utilise husky ou lint-staged, je m’assure de démarrer un TTY, souvent via /dev/tty. Si l’entrée est pipée ou si je remplace process.stdin, je pense à process.stdin.setRawMode(true), faute de quoi les flèches ou la navigation clavier peuvent se comporter bizarrement. Avec nodemon, j’ajoute souvent --no-stdin; avec le mode watch natif de Node, ça fonctionne en général sans rustine.
Autrement dit, la bibliothèque est solide, mais elle ne remplace pas les contraintes physiques du terminal. Il faut les intégrer à la conception, pas les découvrir après la mise en production.
Les règles que j’applique avant d’en faire la base d’un assistant CLI
Je garde une règle simple: si le prompt n’aide pas à réduire une décision ou à sécuriser une entrée, il prend trop de place. Pour un outil interne, je limite le nombre de questions à l’essentiel, j’ordonne les étapes du plus simple au plus engageant, et je valide au plus près de la saisie pour éviter les surprises à la fin.- Je préfère
confirmplutôt qu’une saisie libre dès qu’il n’y a que deux issues possibles. - Je réserve
editoraux contenus longs, parce qu’un champ multiligne ne devrait pas être simulé au forceps dans un prompt standard. - Je force la locale française quand le public est clairement francophone, au lieu de compter sur un environnement de test bien configuré.
- Je teste toujours le flux dans un vrai terminal, pas seulement dans mon IDE, pour vérifier le comportement des touches, de
Ctrl+Cet des redirections. - Je garde
@inquirer/corepour les cas où la bibliothèque standard ne couvre plus mon scénario, pas comme point de départ systématique.
Au fond, Inquirer vaut surtout pour une chose: transformer un échange de terminal en parcours net, court et prévisible. C’est exactement ce qui fait gagner du temps aux utilisateurs, et ce qui rend un outil CLI agréable à maintenir côté code.