Skip to main content

mnemo/
cli.rs

1use clap::{Parser, Subcommand, ValueEnum};
2use clap_complete::Shell;
3use std::path::PathBuf;
4
5use crate::export::ExportFormat;
6
7#[derive(Parser, Debug)]
8#[command(
9    name = "mnemo",
10    version,
11    about = "Navigation et recherche dans l'historique Bash",
12    long_about = None,
13)]
14pub struct Cli {
15    #[command(subcommand)]
16    pub command: Command,
17}
18
19#[derive(Subcommand, Debug)]
20pub enum Command {
21    /// Initialise la configuration et la base de données.
22    Init {
23        /// Lance l'assistant d'onboarding interactif (intégration Bash, import,
24        /// diagnostic). Toutes les actions proposées sont non destructives.
25        #[arg(long)]
26        wizard: bool,
27        /// En mode `--wizard` non interactif, accepte les choix sûrs par défaut
28        /// sans rien supprimer ni purger.
29        #[arg(long)]
30        yes: bool,
31    },
32
33    /// Génère un script de complétion shell sur stdout (bash, zsh, fish).
34    ///
35    /// mnemo n'écrit jamais dans vos fichiers shell : redirigez la sortie vers
36    /// l'emplacement adéquat (voir `docs/UX_ONBOARDING.md`).
37    Completions {
38        /// Shell cible.
39        #[arg(value_enum)]
40        shell: CompletionShell,
41    },
42
43    /// Importe l'historique Bash (~/.bash_history par défaut) dans la base.
44    Import {
45        /// Fichier d'historique à importer.
46        #[arg(long)]
47        file: Option<PathBuf>,
48    },
49
50    /// Ajoute une commande dans la base.
51    Add {
52        /// Commande à enregistrer.
53        #[arg(long)]
54        cmd: String,
55        /// Répertoire de travail (défaut : répertoire courant).
56        #[arg(long)]
57        cwd: Option<String>,
58        /// Code de sortie de la commande.
59        #[arg(long = "exit-code", default_value_t = 0)]
60        exit_code: i64,
61    },
62
63    /// Ouvre l'interface TUI interactive de recherche.
64    Search {
65        /// Requête initiale (positionnelle, optionnelle).
66        query: Option<String>,
67        /// Requête explicite (équivalent à l'argument positionnel).
68        #[arg(long = "query", value_name = "TEXTE", conflicts_with = "query")]
69        query_opt: Option<String>,
70        /// Mode non interactif : imprime les résultats sur stdout sans TUI.
71        #[arg(long)]
72        print: bool,
73        /// Nombre maximal de résultats affichés en mode --print.
74        #[arg(long, default_value_t = 20)]
75        limit: usize,
76        /// Filtre sur un projet Git (nom du dossier racine ou chemin git_root).
77        #[arg(long, value_name = "NOM")]
78        project: Option<String>,
79        /// Filtre sur une branche Git.
80        #[arg(long, value_name = "BRANCHE")]
81        branch: Option<String>,
82        /// Filtre sur un code de sortie exact (ex : 0, 1, 127).
83        #[arg(long = "exit-code", value_name = "CODE")]
84        exit_code: Option<i64>,
85        /// N'affiche que les commandes en échec (exit_code ≠ 0).
86        #[arg(long, conflicts_with = "exit_code")]
87        failed: bool,
88        /// Limite l'âge des résultats (durée `24h`/`7d`/`2w`/`3m`/`1y` ou date `AAAA-MM-JJ`).
89        #[arg(long, value_name = "DURÉE|DATE")]
90        since: Option<String>,
91        /// N'affiche que les commandes antérieures à une date (`AAAA-MM-JJ`).
92        /// Alias : `--until`.
93        #[arg(long, visible_alias = "until", value_name = "DATE")]
94        before: Option<String>,
95        /// Filtre sur un répertoire de travail exact.
96        #[arg(long, value_name = "CHEMIN")]
97        cwd: Option<String>,
98        /// Filtre sur un shell exact (ex : bash, zsh).
99        #[arg(long, value_name = "SHELL")]
100        shell: Option<String>,
101        /// Produit une sortie JSON stable (implique le mode non interactif).
102        #[arg(long)]
103        json: bool,
104        /// N'affiche que les identifiants, un par ligne (implique le mode non
105        /// interactif). Pratique pour chaîner avec `mnemo show`/`mnemo print`.
106        #[arg(long = "id-only", conflicts_with = "json")]
107        id_only: bool,
108    },
109
110    /// Ouvre la TUI avancée (interface interactive principale).
111    Tui {
112        /// Requête initiale (positionnelle, optionnelle).
113        query: Option<String>,
114        /// Filtre initial sur un projet Git (nom du dossier racine).
115        #[arg(long, value_name = "NOM")]
116        project: Option<String>,
117        /// Filtre initial sur une branche Git.
118        #[arg(long, value_name = "BRANCHE")]
119        branch: Option<String>,
120        /// Filtre initial sur un répertoire de travail.
121        #[arg(long, value_name = "CHEMIN")]
122        cwd: Option<String>,
123        /// N'affiche que les commandes en échec (exit_code ≠ 0).
124        #[arg(long)]
125        failed: bool,
126    },
127
128    /// Affiche le snippet d'intégration Bash à ajouter dans ~/.bashrc.
129    Bashrc,
130
131    /// Gère l'intégration shell installée dans ~/.bashrc.
132    Shell {
133        #[command(subcommand)]
134        action: ShellCommand,
135    },
136
137    /// Applique les migrations de schéma SQLite en attente.
138    Migrate,
139
140    /// Affiche des statistiques d'usage (texte simple).
141    Stats {
142        /// Filtre sur un projet Git (nom du dossier racine, chemin git_root, ou `current`).
143        #[arg(long, value_name = "NOM")]
144        project: Option<String>,
145        /// Filtre sur une branche Git.
146        #[arg(long, value_name = "BRANCHE")]
147        branch: Option<String>,
148        /// Limite la fenêtre d'analyse (durée `24h`/`7d`/`2w`/`3m`/`1y` ou date `AAAA-MM-JJ`).
149        #[arg(long, value_name = "DURÉE|DATE")]
150        since: Option<String>,
151        /// Produit une sortie JSON exploitable.
152        #[arg(long)]
153        json: bool,
154    },
155
156    /// Diagnostique l'installation locale de mnemo.
157    Doctor {
158        /// Répare les éléments manquants (config, base, bloc .bashrc).
159        #[arg(long)]
160        fix: bool,
161        /// Produit une sortie JSON exploitable.
162        #[arg(long)]
163        json: bool,
164    },
165
166    /// Gère la configuration locale de mnemo.
167    Config {
168        #[command(subcommand)]
169        action: ConfigCommand,
170    },
171
172    /// Crée une sauvegarde locale complète (archive .tar.gz).
173    Backup {
174        /// Dossier de destination (défaut : ~/.local/share/mnemo/backups/).
175        #[arg(long, value_name = "DOSSIER")]
176        output: Option<PathBuf>,
177        /// Produit une sortie JSON exploitable.
178        #[arg(long)]
179        json: bool,
180    },
181
182    /// Restaure une sauvegarde (.tar.gz) après vérification.
183    Restore {
184        /// Chemin de l'archive de sauvegarde.
185        archive: PathBuf,
186        /// Montre ce qui serait fait sans rien modifier.
187        #[arg(long = "dry-run")]
188        dry_run: bool,
189        /// Confirme la restauration sans question interactive.
190        #[arg(long)]
191        yes: bool,
192    },
193
194    /// Exporte les commandes en JSON ou CSV.
195    Export {
196        /// Format de sortie.
197        #[arg(long, value_enum)]
198        format: ExportFormat,
199        /// Filtre sur un projet Git (nom du dossier racine ou chemin git_root).
200        #[arg(long, value_name = "NOM")]
201        project: Option<String>,
202        /// Filtre sur une branche Git.
203        #[arg(long, value_name = "BRANCHE")]
204        branch: Option<String>,
205        /// Fichier de sortie (défaut : stdout).
206        #[arg(long, value_name = "FICHIER")]
207        output: Option<PathBuf>,
208        /// Compresse la sortie en gzip (`.json.gz` / `.csv.gz`).
209        #[arg(long)]
210        gzip: bool,
211    },
212
213    /// Affiche les dernières commandes avec leurs IDs.
214    List {
215        /// Nombre de commandes affichées (défaut : 20).
216        #[arg(long)]
217        limit: Option<usize>,
218        /// Filtre sur un projet Git (nom du dossier racine ou chemin git_root).
219        #[arg(long, value_name = "NOM")]
220        project: Option<String>,
221        /// Filtre sur une branche Git.
222        #[arg(long, value_name = "BRANCHE")]
223        branch: Option<String>,
224        /// Produit une sortie JSON exploitable.
225        #[arg(long)]
226        json: bool,
227    },
228
229    /// Affiche le détail complet d'une commande par son ID, sans l'exécuter.
230    ///
231    /// mnemo n'exécute jamais une commande de l'historique : `show` se contente
232    /// de lire la base. Si la commande a déjà été redactée, sa forme redactée
233    /// stockée est affichée telle quelle.
234    Show {
235        /// Identifiant de la commande (voir `mnemo list` ou `mnemo search`).
236        id: i64,
237    },
238
239    /// Imprime uniquement la commande brute sur stdout, sans décor ni exécution.
240    ///
241    /// Aucun label, aucune couleur : la sortie peut être copiée ou redirigée.
242    /// mnemo n'exécute jamais la commande ; l'utilisateur reste responsable de
243    /// ce qu'il fait de la sortie.
244    Print {
245        /// Identifiant de la commande (voir `mnemo list` ou `mnemo search`).
246        id: i64,
247    },
248
249    /// Supprime une commande par son ID (après confirmation).
250    Delete {
251        /// Identifiant de la commande à supprimer.
252        id: i64,
253        /// Montre la commande ciblée sans la supprimer.
254        #[arg(long = "dry-run")]
255        dry_run: bool,
256        /// Confirme la suppression sans question interactive.
257        #[arg(long)]
258        yes: bool,
259    },
260
261    /// Nettoie les commandes plus anciennes qu'une durée donnée.
262    Prune {
263        /// Durée d'ancienneté (ex : 30d, 12w, 6m, 1y).
264        #[arg(long = "older-than", value_name = "DURÉE")]
265        older_than: String,
266        /// Filtre sur un projet Git (nom du dossier racine ou chemin git_root).
267        #[arg(long, value_name = "NOM")]
268        project: Option<String>,
269        /// Filtre sur une branche Git.
270        #[arg(long, value_name = "BRANCHE")]
271        branch: Option<String>,
272        /// Montre ce qui serait supprimé sans rien modifier.
273        #[arg(long = "dry-run")]
274        dry_run: bool,
275        /// Confirme le nettoyage sans question interactive.
276        #[arg(long)]
277        yes: bool,
278    },
279
280    /// Affiche des informations détaillées de version et de build.
281    Version,
282
283    /// Vérifie si une nouvelle version est disponible (sans rien installer).
284    ///
285    /// En terminal interactif, si une mise à jour existe, propose de lancer
286    /// `mnemo upgrade` immédiatement (réponse par défaut : non). En mode non
287    /// interactif (CI, script, cron, pipe), reste une simple vérification.
288    /// `--upgrade` enchaîne directement l'installation quand une mise à jour est
289    /// disponible ; combiné à `--yes`, il permet un upgrade automatisé.
290    /// `--require-signature` rend la vérification Sigstore (cosign) obligatoire
291    /// lors de l'upgrade enchaîné.
292    Update {
293        /// Sortie au format JSON (vérification seule, sans proposition).
294        #[arg(long)]
295        json: bool,
296        /// Si une mise à jour est disponible, lance directement `mnemo upgrade`.
297        #[arg(long)]
298        upgrade: bool,
299        /// Avec `--upgrade`, installe sans confirmation interactive.
300        #[arg(long)]
301        yes: bool,
302        /// Avec `--upgrade`, exige une signature Sigstore valide (cosign).
303        #[arg(long = "require-signature")]
304        require_signature: bool,
305    },
306
307    /// Télécharge et installe la dernière version stable (remplace le binaire).
308    Upgrade {
309        /// Montre ce qui serait fait sans rien télécharger ni remplacer.
310        #[arg(long = "dry-run")]
311        dry_run: bool,
312        /// Confirme l'installation sans question interactive.
313        #[arg(long)]
314        yes: bool,
315        /// Force une version précise (ex : v0.5.0) au lieu de la dernière.
316        #[arg(long, value_name = "VERSION")]
317        version: Option<String>,
318        /// Force un triplet cible (ex : aarch64-unknown-linux-musl).
319        #[arg(long, value_name = "CIBLE")]
320        target: Option<String>,
321        /// Exige une signature Sigstore valide (cosign requis) avant d'installer.
322        #[arg(long = "require-signature")]
323        require_signature: bool,
324    },
325
326    /// Désinstalle mnemo : binaire + intégration shell. Conserve les données.
327    Uninstall {
328        /// Montre ce qui serait supprimé sans rien modifier.
329        #[arg(long = "dry-run")]
330        dry_run: bool,
331        /// Confirme la désinstallation sans question interactive.
332        #[arg(long)]
333        yes: bool,
334        /// Supprime AUSSI la configuration, la base et les sauvegardes.
335        #[arg(long)]
336        purge: bool,
337    },
338
339    /// Inspecte le projet courant et les projets connus de l'historique.
340    Project {
341        #[command(subcommand)]
342        action: ProjectCommand,
343    },
344
345    /// Maintenance de l'historique (nettoyage automatique configurable).
346    Maintenance {
347        #[command(subcommand)]
348        action: MaintenanceCommand,
349    },
350
351    /// Navigue, consulte et exporte des sessions de travail.
352    ///
353    /// Une session regroupe les commandes partageant un même `session_id`,
354    /// capturé par l'intégration shell (`MNEMO_SESSION_ID`). Les commandes
355    /// importées ou enregistrées sans cet identifiant ne sont pas rattachées à
356    /// une session.
357    Session {
358        #[command(subcommand)]
359        action: SessionCommand,
360    },
361
362    /// Analyse et redacte les secrets présents dans l'historique déjà stocké.
363    ///
364    /// `scan` repère les commandes potentiellement sensibles et les affiche
365    /// toujours sous forme redactée. `redact` les nettoie en place (dry-run par
366    /// défaut, sauvegarde obligatoire avant toute écriture). Aucun secret n'est
367    /// jamais affiché en clair.
368    Secrets {
369        #[command(subcommand)]
370        action: SecretsCommand,
371    },
372}
373
374/// Regroupe les options de `mnemo search` pour éviter une fonction à trop
375/// d'arguments (filtres combinables passés en un bloc).
376#[derive(Debug, Default)]
377pub struct SearchArgs {
378    pub query: Option<String>,
379    pub print: bool,
380    pub limit: usize,
381    pub project: Option<String>,
382    pub branch: Option<String>,
383    pub exit_code: Option<i64>,
384    pub failed: bool,
385    pub since: Option<String>,
386    pub before: Option<String>,
387    pub cwd: Option<String>,
388    pub shell: Option<String>,
389    pub json: bool,
390    pub id_only: bool,
391}
392
393/// Shells supportés par `mnemo completions`. Limité volontairement à bash, zsh
394/// et fish (un shell inconnu produit une erreur claire de clap). L'enregistrement
395/// automatique du hook reste, lui, Bash-first.
396#[derive(Clone, Copy, Debug, ValueEnum)]
397pub enum CompletionShell {
398    Bash,
399    Zsh,
400    Fish,
401}
402
403impl CompletionShell {
404    /// Convertit vers le générateur `clap_complete` correspondant.
405    pub fn generator(self) -> Shell {
406        match self {
407            CompletionShell::Bash => Shell::Bash,
408            CompletionShell::Zsh => Shell::Zsh,
409            CompletionShell::Fish => Shell::Fish,
410        }
411    }
412}
413
414#[derive(Subcommand, Debug)]
415pub enum ConfigCommand {
416    /// Affiche la configuration effective (valeurs par défaut incluses).
417    Show,
418    /// Affiche le chemin du fichier de configuration.
419    Path,
420    /// Ouvre la configuration dans l'éditeur ($EDITOR, sinon nano/vi).
421    Edit,
422    /// Vérifie la validité du fichier de configuration.
423    Validate,
424    /// Gère la liste des commandes ignorées dans `mnemo stats`.
425    StatsIgnore {
426        #[command(subcommand)]
427        action: StatsIgnoreCommand,
428    },
429}
430
431#[derive(Subcommand, Debug)]
432pub enum ProjectCommand {
433    /// Affiche le projet détecté pour le répertoire courant.
434    Current,
435    /// Liste les projets connus de l'historique.
436    List {
437        /// Nombre maximal de projets affichés.
438        #[arg(long, value_name = "N")]
439        limit: Option<usize>,
440        /// Sortie JSON.
441        #[arg(long)]
442        json: bool,
443    },
444    /// Affiche le détail d'un projet : activité, branches, derniers échecs.
445    Show {
446        /// Racine ou nom court du projet (incompatible avec `--current`).
447        #[arg(value_name = "PROJET", conflicts_with = "current")]
448        project: Option<String>,
449        /// Cible le projet du répertoire courant.
450        #[arg(long)]
451        current: bool,
452        /// Nombre maximal de commandes récentes affichées.
453        #[arg(long, value_name = "N")]
454        limit: Option<usize>,
455        /// Sortie JSON.
456        #[arg(long)]
457        json: bool,
458    },
459    /// Génère un rapport d'activité réutilisable (Markdown par défaut, ou JSON).
460    Report {
461        /// Racine ou nom court du projet (incompatible avec `--current`).
462        #[arg(value_name = "PROJET", conflicts_with = "current")]
463        project: Option<String>,
464        /// Cible le projet du répertoire courant.
465        #[arg(long)]
466        current: bool,
467        /// Borne inférieure (`24h`, `7d`, `2w`, `3m`, `1y` ou `AAAA-MM-JJ`).
468        #[arg(long, value_name = "DURÉE|DATE")]
469        since: Option<String>,
470        /// Borne supérieure (`AAAA-MM-JJ` exclue, ou durée).
471        #[arg(long, value_name = "DURÉE|DATE")]
472        until: Option<String>,
473        /// Format de sortie (`markdown` par défaut).
474        #[arg(long, value_enum, default_value = "markdown")]
475        format: SessionFormat,
476        /// Fichier de sortie (défaut : stdout).
477        #[arg(long, value_name = "FICHIER")]
478        output: Option<PathBuf>,
479        /// Autorise l'écrasement d'un fichier de sortie existant.
480        #[arg(long)]
481        force: bool,
482        /// Nombre maximal de commandes détaillées dans le rapport.
483        #[arg(long, value_name = "N")]
484        limit: Option<usize>,
485    },
486}
487
488#[derive(Subcommand, Debug)]
489pub enum MaintenanceCommand {
490    /// Affiche l'état de la maintenance et ce qui serait nettoyé.
491    Status,
492    /// Exécute le nettoyage configuré.
493    Run {
494        /// Montre ce qui serait supprimé sans rien modifier.
495        #[arg(long = "dry-run")]
496        dry_run: bool,
497        /// Confirme le nettoyage sans question interactive.
498        #[arg(long)]
499        yes: bool,
500    },
501}
502
503#[derive(Subcommand, Debug)]
504pub enum StatsIgnoreCommand {
505    /// Ajoute une commande à la liste ignorée du Top commandes.
506    Add {
507        /// Nom de commande (ex: `create_dir`).
508        name: String,
509    },
510    /// Retire une commande de la liste ignorée.
511    Remove {
512        /// Nom de commande (ex: `create_dir`).
513        name: String,
514    },
515    /// Affiche les commandes actuellement ignorées.
516    List,
517}
518
519#[derive(Subcommand, Debug)]
520pub enum ShellCommand {
521    /// Met à niveau l'intégration Bash installée dans ~/.bashrc.
522    ///
523    /// Remplace un bloc obsolète par la version courante (capture de
524    /// `MNEMO_SESSION_ID` pour `mnemo session`), après sauvegarde et sans
525    /// toucher au reste du fichier. Sans bloc installé, propose `mnemo init`.
526    Upgrade,
527}
528
529/// Format d'export d'une session (`mnemo session export`).
530#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
531pub enum SessionFormat {
532    Markdown,
533    Json,
534}
535
536#[derive(Subcommand, Debug)]
537pub enum SessionCommand {
538    /// Liste les sessions connues, de la plus récente à la plus ancienne.
539    List {
540        /// Nombre maximal de sessions affichées.
541        #[arg(long, value_name = "N")]
542        limit: Option<usize>,
543    },
544    /// Affiche les commandes d'une session, dans l'ordre chronologique.
545    Show {
546        /// Identifiant de session (voir `mnemo session list`).
547        session_id: String,
548        /// Nombre maximal de commandes affichées.
549        #[arg(long, value_name = "N")]
550        limit: Option<usize>,
551    },
552    /// Exporte une session en Markdown (défaut) ou JSON.
553    Export {
554        /// Identifiant de session à exporter (incompatible avec `--last`).
555        #[arg(value_name = "SESSION_ID", conflicts_with = "last")]
556        session_id: Option<String>,
557        /// Cible la session la plus récente au lieu d'un identifiant explicite.
558        #[arg(long)]
559        last: bool,
560        /// Format de sortie (`markdown` par défaut).
561        #[arg(long, value_enum, default_value = "markdown")]
562        format: SessionFormat,
563        /// Fichier de sortie (défaut : stdout).
564        #[arg(long, value_name = "FICHIER")]
565        output: Option<PathBuf>,
566        /// Autorise l'écrasement d'un fichier de sortie existant.
567        #[arg(long)]
568        force: bool,
569    },
570}
571
572#[derive(Subcommand, Debug)]
573pub enum SecretsCommand {
574    /// Repère les commandes potentiellement sensibles (lecture seule).
575    ///
576    /// Les commandes sont toujours affichées sous forme redactée ; aucun secret
577    /// n'apparaît en clair. N'effectue aucune modification.
578    Scan {
579        /// Nombre maximal de résultats affichés.
580        #[arg(long, value_name = "N")]
581        limit: Option<usize>,
582        /// Sortie JSON (sans valeurs sensibles).
583        #[arg(long)]
584        json: bool,
585    },
586    /// Redacte en place les commandes sensibles déjà stockées.
587    ///
588    /// Dry-run par défaut : sans `--apply`, rien n'est modifié. Avec `--apply`,
589    /// une sauvegarde est créée avant toute écriture et seule la colonne
590    /// `command` est mise à jour.
591    Redact {
592        /// Montre ce qui serait redacté sans rien modifier (comportement par
593        /// défaut, accepté explicitement).
594        #[arg(long = "dry-run")]
595        dry_run: bool,
596        /// Applique réellement la redaction (sinon dry-run).
597        #[arg(long)]
598        apply: bool,
599        /// Confirme la redaction sans question interactive.
600        #[arg(long)]
601        yes: bool,
602        /// Force une sauvegarde avant redaction (toujours effectuée avec
603        /// `--apply`, ce drapeau le rend explicite).
604        #[arg(long)]
605        backup: bool,
606    },
607}