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