Créer un CLI en Rust pour piloter son portfolio
Retour d'expérience sur la création de portfolio-cli : un outil en Rust pour gérer l'intégralité de mon portfolio depuis le terminal, avec upload d'images, intégration IA et authentification par token.
Par Alane Jaunet • 17 mars 2026
Pourquoi un CLI pour gérer un portfolio ?
Mon portfolio tourne sur une stack Next.js avec une API REST et un panel admin classique. Ça fonctionne, mais à chaque fois que je voulais ajouter un projet ou modifier une compétence, il fallait ouvrir le navigateur, naviguer dans l'interface, remplir des formulaires...
C'est en échangeant avec Oalacea que l'idée m'est venue : pourquoi ne pas tout faire depuis le terminal ? Il m'a montré l'approche CLI pour gérer du contenu, et j'ai tout de suite vu le potentiel.
Pourquoi Rust ?
J'ai rapidement convergé vers Rust — c'était la solution la plus directe pour ce que je voulais :
- Un seul binaire — pas de runtime Node, pas de dépendances système.
cargo installet c'est prêt. - La performance — le CLI démarre instantanément, les commandes s'exécutent sans latence perceptible.
- L'écosystème —
clappour le parsing de commandes,reqwestpour les requêtes HTTP,serdepour la sérialisation. Chaque crate fait bien son travail.
C'était aussi l'occasion d'apprendre Rust sur un projet concret avec de vrais appels API, plutôt que sur des exercices théoriques.
Architecture
La structure est volontairement simple :
src/
├── main.rs # Routing des commandes (clap)
├── config.rs # Config TOML persistante
├── api_client.rs # Client HTTP générique avec Bearer token
├── ai_client.rs # Client OpenAI-compatible
├── output.rs # Formatage tableaux + couleurs
├── models/mod.rs # Structs serde pour chaque entité
└── commands/ # Un module par ressource
Chaque entité du portfolio (projets, compétences, expériences, certifications...) a son module dans commands/. Le pattern est toujours le même : list, get, create, update, delete.
Le client API générique
Le cœur du CLI, c'est un client HTTP qui gère l'authentification et la sérialisation de manière générique :
pub fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T> { let mut req = self.client.get(self.url(path)); if !self.token.is_empty() { req = req.bearer_auth(&self.token); } let resp = req.send().context("Erreur réseau")?; // ... Ok(resp.json()?) }
Un seul get() sert pour tous les types : Vec<Project>, PersonalInfo, SiteConfig... Rust infère le type de retour à partir du contexte d'appel. C'est propre et il n'y a aucune duplication.
La config persistante
La configuration (URL de l'API, token d'auth) est stockée dans ~/.config/portfolio-cli/config.toml. Le crate dirs gère les chemins spécifiques à chaque OS, et toml fait la sérialisation.
portfolio config set api https://pro.newalfox.fr portfolio login # Token stocké localement, réutilisé à chaque commande
Ce que ça donne au quotidien
Ajouter un projet :
portfolio projects create \ --title "Palnia" \ --year "2026" \ --description "SaaS de productivité" \ --state IN_PROGRESS
Lister mes compétences frontend :
portfolio skills list --category FRONTEND
Modifier une stat du site :
portfolio site-config update-stat --key projects --value "100+"
Chaque commande prend quelques secondes. Pas de navigateur, pas de formulaire, pas de clic.
Upload d'images
Un ajout récent : la possibilité d'uploader des images directement depuis le CLI vers Vercel Blob Storage.
portfolio projects update <id> \ --image-path ./screenshot.png \ --image-alt "Capture d'écran du projet"
Le CLI envoie le fichier en multipart à l'API, qui le stocke sur Vercel Blob et met à jour la référence en base.
Intégration IA
Le CLI intègre un module IA via claude-max-api-proxy, un proxy local qui expose une API OpenAI-compatible connectée à Claude.
# Générer une description de projet portfolio ai describe --title "Mon Projet" --techs "React, TypeScript" # Résumé exécutif du portfolio complet portfolio ai summary
Le proxy tourne sur localhost:3456 et utilise l'abonnement Claude existant — pas de clé API supplémentaire nécessaire.
Intégration Claude Code
La commande portfolio init génère un fichier d'instructions pour Claude Code. Résultat : Claude Code peut utiliser le CLI de manière autonome pour gérer le portfolio. On lui dit "ajoute ce projet", et il exécute les bonnes commandes.
C'est un cas d'usage concret de l'agent coding : au lieu de modifier du JSON ou de naviguer dans une UI, l'IA pilote directement le CLI.
Les crates qui font le travail
| Crate | Rôle |
|-------|------|
| clap | Parsing des commandes avec le derive macro |
| reqwest | Client HTTP (mode blocking, suffisant pour un CLI) |
| serde + serde_json | Sérialisation/désérialisation JSON |
| toml | Config persistante |
| tabled | Tableaux formatés dans le terminal |
| colored | Couleurs pour les messages de succès/erreur |
| anyhow | Gestion d'erreurs ergonomique |
| dialoguer | Prompts interactifs (saisie du token) |
Ce que j'ai appris
- Rust est idéal pour les CLI — le temps de compilation est le seul inconvénient, mais le résultat est un binaire rapide et sans dépendance.
- Les generics de Rust brillent dans un client API — un seul
get<T>()remplace des dizaines de fonctions spécifiques. - Un CLI bien fait change la façon de travailler — ce qui prenait 2 minutes dans un formulaire prend 5 secondes dans le terminal.
- L'intégration avec un agent IA ouvre des possibilités intéressantes : le CLI devient une interface naturelle pour l'automatisation.