@fixweb/utils-cli
v1.1.2
Published
Declarative CLI framework with YAML-based commands and interactive TUI
Maintainers
Readme
@fixweb/utils-cli
Framework CLI déclaratif avec support YAML et TUI interactif.
Installation
npm install @fixweb/utils-cliDémarrage rapide
// index.js
require("@fixweb/utils-cli").run("./menu.yml");# menu.yml
title: Mon CLI
version: 1.0.0
menu:
- "Commandes"
- hello: ["echo", "Hello World"]
- greet:
desc: "Dit bonjour"
exec: ["echo", "Bonjour!"]node index.jsRéférence YAML complète
Structure racine
title: string # Titre affiché en banner figlet (optionnel)
version: string # Version affichée sous le banner (optionnel)
modules: string # Chemin vers le dossier des modules (optionnel)
config: string # Chemin vers le dossier de config (optionnel)
vars: # Variables globales (optionnel)
KEY: "value"
menu: # Menu principal (requis) — tableau d'éléments
- ...Éléments du menu
Le tableau menu accepte 6 types d'éléments :
1. Label (string) — Titre de section
menu:
- "Ma Section"Affiche un titre de section dans le TUI. Toutes les commandes qui suivent sont regroupées sous ce label.
2. Commande courte — name: [cmd, ...args]
menu:
- hello: ["echo", "Hello!"]
- list-files: ["ls", "-la"]Syntaxe minimale : clé: [tableau_argv]. Le nom de la commande est la clé. exec est implicite.
3. Commande étendue — name: { desc, exec, ... }
menu:
- deploy:
desc: "Déployer l'application" # Description (optionnel)
exec: ["./deploy.sh", "{{env}}"] # Commande à exécuter (tableau argv)
async: true # Mode async avec spinner (optionnel, défaut: false)
inline: true # Reste dans le TUI (optionnel, défaut: false)
prompts: # Prompts interactifs avant exécution (optionnel)
- ...4. Sous-menu — name: { desc, items: [...] }
menu:
- Outils:
desc: "Outils divers"
items:
- tool1: ["echo", "Tool 1"]
- tool2:
desc: "Second outil"
exec: ["echo", "Tool 2"]
- SousMenu:
desc: "Imbriqué"
items:
- subtool: ["echo", "Subtool"]Les sous-menus sont récursifs — on peut imbriquer autant de niveaux que nécessaire.
5. Steps — name: { desc, steps: [...], mode }
menu:
- build-all:
desc: "Build everything"
mode: parallel # "parallel" (défaut) ou "sequential"
steps:
- name: "Frontend"
exec: ["npm", "run", "build:front"]
- name: "Backend"
exec: ["npm", "run", "build:back"]6. Include — { include: path }
menu:
- include: ./other-menu.yml
- include: menus/*.yml
- include: modules/*/commands.ymlLe fichier inclus doit contenir une clé menu: avec un tableau d'éléments.
Modes d'exécution
| Propriété | Valeur | Comportement |
|-----------|--------|-------------|
| (défaut) | — | Prend le contrôle du terminal (stdin/stdout hérités) |
| async: true | — | Spinner animé, sortie capturée, résumé affiché |
| inline: true | — | Comme async mais reste dans le TUI sans le quitter |
Prompts interactifs
Ajoutez prompts à une commande pour demander des valeurs à l'utilisateur avant exécution.
Types de prompts
| Type | Propriétés | Description |
|------|-----------|-------------|
| select | id, message, choices, defaultValue? | Liste de choix (flèches + Enter) |
| input | id, message, defaultValue? | Saisie texte libre |
| confirm | id, message, defaultValue? | Oui/Non (Y/n) |
| password | id, message | Saisie masquée (****) |
Propriétés d'un prompt
prompts:
- id: env # Identifiant (requis) — utilisable comme {{env}} dans exec
type: select # Type (requis) — select | input | confirm | password
message: "Environnement ?" # Message affiché (optionnel)
choices: [dev, staging, prod] # Choix (requis pour select)
defaultValue: "dev" # Valeur par défaut (optionnel)Exemple complet avec prompts
- deploy:
desc: "Déployer l'application"
prompts:
- id: env
type: select
message: "Environnement ?"
choices: [dev, staging, prod]
- id: version
type: input
message: "Version ?"
defaultValue: "1.0.0"
- id: confirm
type: confirm
message: "Déployer {{env}} v{{version}} ?"
exec: ["./deploy.sh", "{{env}}", "{{version}}"]Les valeurs des prompts sont injectées comme variables {{id}} dans exec et dans les message des prompts suivants.
Variables
Définition
vars:
PROJECT_DIR: /opt/myproject
LOG_FILE: /var/log/app.log
ENV: productionSyntaxes supportées
# Syntaxe moustache (recommandée)
- logs: ["tail", "-f", "{{LOG_FILE}}"]
# Syntaxe shell
- logs: ["tail", "-f", "${LOG_FILE}"]
# Avec valeur par défaut
- logs: ["tail", "-f", "${LOG_FILE:-/tmp/app.log}"]Variables système (automatiques)
| Variable | Description |
|----------|-------------|
| CLI_DIR | Dossier racine du CLI |
| MODULES_DIR | Dossier des modules |
| CONFIG_DIR | Dossier de config |
| MODULE_DIR | Dossier du module courant |
Navigation TUI
Clavier
| Touche | Action |
|--------|--------|
| ↑ ↓ | Naviguer dans le menu |
| → Enter | Entrer / Exécuter |
| ← | Retour au menu parent |
| q Ctrl+C | Quitter |
Souris
| Action | Effet | |--------|-------| | Molette haut/bas | Naviguer dans le menu | | Clic sur un item | Le sélectionner | | Clic sur l'item sélectionné | L'activer |
Indicateurs visuels
○Action/commande●Sous-menu▶Élément sélectionné
API JavaScript
Simple
require("@fixweb/utils-cli").run("./menu.yml");Avancée
const { createCLI } = require("@fixweb/utils-cli");
const cli = createCLI({
name: "mycli",
version: "1.0.0",
title: "My CLI",
menu: [
"Commands",
{ hello: ["echo", "Hello"] },
{ deploy: { desc: "Deploy", exec: ["./deploy.sh"], prompts: [
{ id: "env", type: "select", message: "Env?", choices: ["dev", "prod"] }
]}}
],
vars: { APP_DIR: "/opt/app" },
paths: { cliDir: __dirname }
});
cli.run();Avec gestion d'erreurs
require("@fixweb/utils-cli").runSafe("./menu.yml");Structure de projet recommandée
my-cli/
├── index.js # Point d'entrée : require("@fixweb/utils-cli").run("./menu.yml")
├── menu.yml # Menu principal
├── modules/ # Modules optionnels
│ ├── database/
│ │ ├── commands.yml
│ │ └── scripts/
│ │ └── backup.sh
│ └── docker/
│ ├── commands.yml
│ └── scripts/
│ └── restart.sh
└── menus/ # Menus additionnels optionnels
├── admin.yml
└── dev.yml# menu.yml
title: Mon Projet
version: 1.0.0
modules: ./modules
menu:
- "Projet"
- status: ["git", "status"]
- build:
desc: "Build le projet"
async: true
exec: ["npm", "run", "build"]
- include: modules/*/commands.yml
- include: menus/*.ymlExemples complets
CLI DevOps
title: DevOps CLI
version: 2.0.0
vars:
STACK: myapp
COMPOSE: docker-compose.yml
menu:
- "Docker"
- up:
desc: "Démarrer les services"
exec: ["docker", "compose", "-f", "{{COMPOSE}}", "up", "-d"]
- down:
desc: "Arrêter les services"
exec: ["docker", "compose", "-f", "{{COMPOSE}}", "down"]
- logs:
desc: "Voir les logs d'un service"
prompts:
- id: service
type: select
message: "Service ?"
choices: [api, web, db, redis]
exec: ["docker", "compose", "logs", "-f", "{{service}}"]
- "Déploiement"
- deploy:
desc: "Déployer en production"
mode: sequential
steps:
- name: "Tests"
exec: ["npm", "test"]
- name: "Build"
exec: ["npm", "run", "build"]
- name: "Push"
exec: ["docker", "push", "registry.example.com/{{STACK}}"]
- "Monitoring"
- Statuts:
desc: "Statut des services"
items:
- ps:
desc: "Conteneurs actifs"
inline: true
exec: ["docker", "compose", "ps"]
- disk:
desc: "Espace disque"
inline: true
exec: ["df", "-h"]CLI Monorepo
title: Monorepo
version: 1.0.0
vars:
PACKAGES: ./packages
menu:
- "Développement"
- dev:
desc: "Lancer le dev server"
prompts:
- id: pkg
type: select
message: "Package ?"
choices: [frontend, backend, shared]
exec: ["npm", "run", "dev", "--workspace", "{{pkg}}"]
- build-all:
desc: "Build tous les packages"
mode: parallel
steps:
- name: "shared"
exec: ["npm", "run", "build", "--workspace", "shared"]
- name: "frontend"
exec: ["npm", "run", "build", "--workspace", "frontend"]
- name: "backend"
exec: ["npm", "run", "build", "--workspace", "backend"]
- "Git"
- commit:
desc: "Commit conventionnel"
prompts:
- id: type
type: select
message: "Type ?"
choices: [feat, fix, chore, docs, refactor, test]
- id: scope
type: input
message: "Scope ?"
- id: msg
type: input
message: "Message ?"
exec: ["git", "commit", "-m", "{{type}}({{scope}}): {{msg}}"]Sécurité
- Les commandes sont exécutées via
spawnsans shell (pas d'injection possible) - Les arguments sont sanitisés automatiquement
- Variables d'environnement filtrées (seules les vars système sûres + vars utilisateur sont transmises)
execest un tableau[cmd, arg1, arg2], jamais une string (pas d'interprétation shell)
Compatibilité
| Plateforme | Support | |-----------|---------| | macOS / Linux | Complet | | Windows Terminal + PowerShell 7+ | Complet | | Windows PowerShell 5.1 | Partiel (pas de TUI interactif) | | CI/CD (non-TTY) | Fallback automatique (mode non-interactif) |
Variables d'environnement :
NO_COLOR=1— Désactive les couleurs ANSIFORCE_COLOR=1— Force les couleurs ANSI
Requires Node.js >= 18.
Licence
MIT
