---
name: autocli-password
description: >-
  Run a terminal command that needs a secret only the user knows by collecting it through a
  native hidden-input dialog piped straight into the command — never asking the user to paste it.
  Use when a command would otherwise block on a credential: deploying or rebuilding an encrypted
  artifact, decrypting a vault, logging into a CLI, an SSH key passphrase, or a `sudo` step. The
  secret stays in RAM for one command and is never written to disk or history. Déclenche AUSSI sur
  le symptôme, sans qu'on te le demande : « coffre verrouillé », « BW_SESSION absent », « mot de
  passe introuvable », un serveur MCP qui refuse faute d'identifiant, `bw status` = `locked`,
  `Permission denied (publickey)`. Un secret verrouillé n'est jamais un « non mesuré » : ouvre la
  fenêtre masquée, seed la clé, rejoue l'appel.
---

# autoCLIpassword — exécution autonome de commandes à secret

## L'idée

Quand une commande a besoin d'un secret que **seul l'utilisateur connaît**, le réflexe habituel est mauvais : soit on lui demande de coller le secret (il transite par le chat / l'historique), soit on lui tend un bloc avec un placeholder `…` à éditer à la main, soit on le code en dur. Tout ça crée de la friction et expose le secret.

À la place : **c'est toi (Claude) qui lances la commande**. Au moment où elle a besoin du secret, une **fenêtre native à champ masqué** s'ouvre sur l'écran de l'utilisateur. Il tape, valide, et la valeur part directement dans l'environnement de la commande. Tu lis la sortie et tu enchaînes. L'utilisateur **n'a qu'un geste : taper son mot de passe**. Il ne colle rien.

Pourquoi c'est mieux : le secret n'apparaît jamais dans la conversation, jamais dans `history`, jamais sur disque. Il n'existe que dans la RAM, le temps d'une commande.

## Boucle d'apprentissage — ce skill capitalise, il ne se répète pas

**Règle permanente : dès que tu rencontres une friction ou un incident avec ce mécanisme et que tu le résous, tu INSCRIS la leçon ici avant de clore.** Une saisie qui bloque, une passphrase re-demandée pour rien, un cache mal purgé, un dialogue qui timeout, un piège d'outillage en aval : chacun est un apprentissage à graver, pas à revivre. Le but est qu'une deuxième occurrence du même problème soit impossible — parce que la règle qui l'évite est déjà dans ce fichier.

Comment graver, à chaque fois :
1. **Symptôme → cause racine → règle** en une ou deux phrases, datée, ajoutée à la section pertinente (souvent « Anti-typo, mais ciblé » ou une nouvelle puce). Formule la règle de façon *actionnable* (« ne fais pas X quand Y », pas « attention à X »).
2. Si la cause touche un système précis (karto, Bitwarden…), pose aussi le détail dans la mémoire-pièges de ce système (`[[pitfalls-karto]]`, etc.) et lie-la ici.
3. Si un petit outil de contrôle a manqué (un test lecture-seule, un validateur), crée-le et référence-le, pour que la prochaine fois le diagnostic soit une commande, pas une enquête.

**Avant de reprompter ou de conclure « ça ne marche pas », relis d'abord les leçons déjà gravées ici** : la réponse à ta friction du jour y est peut-être déjà. Chaque incident bien capitalisé rend ce skill strictement plus fiable que la veille — c'est le seul état acceptable.

*(Incident fondateur, 16/07/2026 : passphrase karto correcte re-demandée 5+ fois parce que la doctrine purgeait le cache sur un échec en aval — alors que le kid concordait. Cause réelle : coffre au kid périmé vs ct. Règle gravée → section « Anti-typo, mais ciblé ». C'est précisément ce que cette boucle doit rendre non-répétable.)*

## La règle d'or (sécurité)

Un secret maître (passphrase qui déchiffre un coffre, clé qui ouvre toute une infra) **ne doit JAMAIS être persisté**. La raison : tout ce qui est stocké est volable — par un process tournant sous la session déverrouillée de l'utilisateur, ou par quelqu'un devant sa machine déverrouillée. Gardé seulement dans sa tête + transitoirement en RAM, il n'y a rien à voler.

Concrètement, à chaque usage :
- Capture le secret par **substitution de commande** (`$(...)`) dans une variable d'env, pour **une seule** commande.
- `unset` la variable juste après.
- N'écris JAMAIS le secret dans un fichier, un log, ou un argument en clair (`--passphrase 'xxx'` finit dans `history` et dans la table des process — préfère toujours une **variable d'environnement** que l'outil lit).
- Ne le ré-affiche jamais (pas d'`echo "$SECRET"`).
- **Refuse le DISQUE** pour un secret maître : jamais Keychain, jamais un champ Bitwarden, jamais `.env`. Tout ce qui touche le disque est volable et survit au redémarrage.

## Exception bornée et autorisée : mémorisation en RAM (modèle ssh-agent)

Leo a explicitement validé (25/06) une **commodité opt-in** : éviter de retaper la même passphrase
à chaque fenêtre, **sans jamais toucher le disque**. C'est le modèle `ssh-agent`/`gpg-agent`, pas
un store sur disque — donc compatible avec la règle d'or ci-dessus (l'interdit, c'est le disque).

`scripts/secret-agent.mjs` est un daemon **éphémère, RAM uniquement** : il garde des secrets
(clé → valeur) derrière une socket Unix `0600`, chaque entrée avec un **TTL plafonné à 24 h**, et
**s'éteint tout seul** dès que tout est expiré (et au plus tard à 24 h 05). Le secret ne vit que
dans le tas de ce process ; le seul fichier sur disque est la socket (0 octet de secret).

Quand une clé de cache est fournie (macOS), la saisie tient dans **UNE seule fenêtre** : une
`NSAlert` Cocoa (pont AppleScript-ObjC, zéro dépendance) avec un champ masqué **et** le menu de
durée côte à côte. Si la NSAlert échoue (vieux macOS), repli automatique sur l'ancien flux à deux
fenêtres (`display dialog` puis `choose from list`). Sans clé de cache : une seule fenêtre masquée
classique, comme avant.

Garde-fous non négociables :
- **Défaut = 24 h dès qu'une clé de cache est fournie** (changé le 08/08/2026, voir la section
  « Pourquoi Léo est re-sollicité »). Le menu s'ouvre sur « 24 heures » ; Léo peut redescendre ou
  choisir « Aucune ». Sans 3ᵉ argument, `ask-secret.sh` ne mémorise rien **et le dit sur stderr** —
  ce n'est pas un mode normal, c'est le signe que tu as oublié la clé.
- **Plafond 24 h, en dur**, dans le daemon (impossible de demander plus). Ladder : 5 min → 24 h.
  L'exposition d'un secret maître en RAM toute la journée est réelle — mais Léo a tranché (08/08) :
  il préfère ce risque, borné et sans disque, à des dizaines de fenêtres par jour. Le geste de
  réduction, c'est le **flush** quand il s'éloigne, pas une durée courte par défaut.
- **RAM seulement** : aucune écriture disque du secret, jamais.
- **Périmètre** : seulement les secrets maîtres réutilisables — voir le **registre des clés
  canoniques** ci-dessous. Pas les valeurs jetables (un mot de passe qu'on dépose une fois ne se met
  pas en cache).
- **Bouton panique** : `node ~/.claude/skills/autocli-password/scripts/secret-agent.mjs flush`
  oublie tout immédiatement (et éteint le daemon). À proposer si Leo s'éloigne de sa machine.
- **Anti-typo, mais ciblé** (leçon 16/07/2026) : ne `drop`/reprompte QUE sur une **preuve positive
  de mauvaise saisie** — une empreinte de clé qui **ne concorde pas** (`keycheck verify` → kid ≠
  canonique, unlock explicitement « clé invalide »). Un simple **échec en aval** (rebuild raté,
  « coffre indéchiffrable », deploy KO) alors que **le kid CONCORDE** signifie que la passphrase est
  BONNE et que le problème est ailleurs (coffre corrompu, kid périmé vs ct, bug d'outillage) →
  **GARDE le cache, n'ennuie pas l'utilisateur, débogue la cause.** Purger la clé sur ce genre
  d'échec = re-demander en boucle une passphrase déjà correcte (exactement ce qui s'est passé le
  16/07 : coffre karto au kid valide mais ct inouvrable — la passphrase n'a jamais été en cause).
  ⚠️ `keycheck verify` **ne déchiffre pas** (compare seulement le kid) : un kid qui concorde ne
  prouve pas que le ct s'ouvre. `vault-add.mjs` drope déjà sur mismatch pour `bw-master`/`karto`.
- **Cache par défaut pour un flux multi-étapes** : dès qu'une session va rappeler le même secret
  maître plusieurs fois (récup + rebuild + deploy…), fournis la **clé de cache** et suggère une
  durée dès la 1ʳᵉ fenêtre → UNE seule saisie pour toute la séquence, au lieu de N fenêtres.

## Pourquoi Léo est re-sollicité alors que le secret est en RAM (08/08/2026)

**Symptôme, dit par Léo** : « je mets 24 h et tu me redemandes quand même — soit ce n'est pas
vraiment en RAM, soit tu ne vérifies pas ». Les deux hypothèses étaient fausses, et la vraie cause
est pire : **le mécanisme marchait, mais on le contournait presque une fois sur deux.**

**Mesure** (extraction des invocations réelles dans les transcriptions `~/.claude/projects`) :

| 3ᵉ argument passé à `ask-secret.sh` | invocations | effet |
|---|---:|---|
| **aucune clé** (2 arguments) | **359** | fenêtre à chaque fois, **et rien n'est mémorisé** |
| `karto` | 328 | cache utilisable |
| `bw-master` | 102 | cache utilisable |
| `vps-sudo` / `vps` / `ssh-key` / `ssh-vps` | 9 / 6 / 3 / 1 | **4 noms pour 2 secrets** → aucun partage |

**Causes racines, par ordre de poids :**
1. **Clé de cache omise (44 % des appels).** Sans 3ᵉ argument, `ask-secret.sh` ne consulte même pas
   le cache et ne stocke rien : la saisie de 9 h 05 ne sert pas à celle de 9 h 07.
2. **Dérive des noms de clés.** `vps` ≠ `vps-sudo` ≠ `ssh-key` : le secret était bien en RAM, sous
   un autre nom, donc invisible.
3. **Le sélecteur de durée était sur « Aucune » par défaut**, et la touche Entrée valide OK sans
   toucher au menu → saisie correcte, mémorisation nulle, sans que rien ne le signale.
4. **Aucune sonde lisible.** `get` est la seule façon de savoir si une clé est là — or `get`
   **imprime le secret** (incident 06/08). Donc « vérifier avant de demander » n'était pas outillé.

**Corrigé le 08/08 :** défaut du menu = 24 h quand une clé est fournie · avertissement stderr quand
la clé manque · témoin stderr `cache RAM HIT` quand aucune fenêtre ne s'ouvre · nouvelles
sous-commandes `has` et `keys`.

### Registre des clés canoniques — n'en invente jamais une nouvelle

| clé | secret | usages typiques |
|---|---|---|
| `karto` | passphrase du coffre karto | `karto-sync.mjs rebuild`, deploy, `vault-add/open` |
| `bw-master` | mot de passe maître Bitwarden | `bw unlock --passwordenv`, `bw-get.mjs`, seed MCP |
| `vps-sudo` | mot de passe sudo du VPS b-capital | toute commande privilégiée sur le VPS |
| `mac-admin` | mot de passe admin macOS | `sudo` local (disques, `dd`, `fsck`) |

Avant d'introduire une 5ᵉ clé, vérifie qu'aucune de celles-ci ne désigne déjà le même secret. Une
clé nouvelle = une fenêtre de plus pour Léo.

### Le réflexe, dans cet ordre

1. **Sonder, jamais deviner** — `node "$SKILL/scripts/secret-agent.mjs" has karto`
   → `HIT <ttl>s` (exit 0) ou `MISS` (exit 3). **La valeur ne sort jamais.**
   `… keys` liste les clés en mémoire avec leur TTL restant (toujours sans les valeurs).
2. **Toujours passer la clé de cache**, même pour un appel unique — un appel unique le reste
   rarement, et sans clé la saisie est perdue pour la suite de la session.
3. **Ne jamais annoncer « le secret est en RAM »** sans avoir lancé `has` : c'est exactement
   l'affirmation non mesurée que Léo a prise en défaut.

⛔ **N'utilise JAMAIS `get` pour tester une présence** : il écrit le secret sur stdout, donc dans la
transcription. `has` existe pour ça.

## Comment lancer une commande (helper fourni)

Utilise le helper `scripts/ask-secret.sh` — il affiche la bonne fenêtre selon l'OS (macOS `osascript`, Linux `zenity`/`systemd-ask-password`, repli terminal `read -rs`) et imprime le secret saisi sur stdout. Annulation ou saisie vide → il sort en erreur, donc la commande appelante avorte proprement (rien ne se passe).

Patron général (le `&&` garantit que rien ne tourne si l'utilisateur annule). **Le 3ᵉ argument — la
clé de cache — fait partie du patron : ne le retire que pour un secret authentiquement jetable.**

```bash
SKILL=~/.claude/skills/autocli-password
node "$SKILL/scripts/secret-agent.mjs" has <clé>   # HIT/MISS, sans jamais sortir la valeur
SECRET="$("$SKILL/scripts/ask-secret.sh" "Motif clair de la demande" "Titre fenêtre" <clé>)" \
  && VAR_ATTENDUE_PAR_L_OUTIL="$SECRET" <commande qui lit cette variable> \
  ; unset SECRET
```

Sur un HIT, `ask-secret.sh` n'ouvre **aucune** fenêtre et écrit `cache RAM HIT` sur stderr : ce
témoin dans ta sortie d'outil est la preuve que Léo n'a rien eu à taper.

**3ᵉ argument optionnel = clé de cache RAM** (voir l'exception bornée ci-dessus). Fournis-le
seulement pour un secret maître réutilisable, avec une clé stable (`karto`, `bw-master`) :

```bash
# 1ʳᵉ fois : fenêtre masquée + sélecteur de durée. Cache HIT ensuite → aucune fenêtre.
SECRET="$("$SKILL/scripts/ask-secret.sh" "Passphrase karto (rebuild chiffré)" "karto · rebuild" karto)" \
  && CARTO_PASS="$SECRET" node karto-sync.mjs rebuild ; unset SECRET CARTO_PASS
```

Réutilise la **même clé** pour le même secret partout (sinon le cache ne se partage pas). Si la
commande échoue sur un secret invalide, oublie-le avant de retenter :
`node "$SKILL/scripts/secret-agent.mjs" drop karto`.

**Exemple réel — rebuild + deploy d'un coffre chiffré (karto) :**

```bash
SKILL=~/.claude/skills/autocli-password
cd ~/Documents/Claude/Projects/cartographie-it \
  && CARTO_PASS="$("$SKILL/scripts/ask-secret.sh" "Passphrase karto (rebuild chiffré)" "karto · rebuild")" \
       node karto-sync.mjs rebuild \
  ; unset CARTO_PASS
# puis (sans secret) :
./deploy-karto.sh
```

Donne à l'outil Bash un **timeout généreux** (≈ 180 s) sur la commande qui ouvre la fenêtre : il attend pendant que l'utilisateur tape.

## Avant de prompter : le secret a-t-il déjà un agent/cache natif ?

Leçon (22/07/2026) : passphrase SSH VPS re-demandée en fenêtre alors qu'elle est **déjà dans le
Keychain macOS** (posée là par Léo, cf. mémoire GTMAdvisory 16/07). Règle actionnable — pour une
**clé SSH**, avant toute fenêtre : `ssh-add --apple-load-keychain` (recharge sans rien demander),
et seulement si ça ne charge rien → fenêtre masquée puis `ssh-add --apple-use-keychain` (le
Keychain est l'exception validée pour CETTE passphrase ; la doctrine « jamais sur disque » vaut
pour les coffres karto/Bitwarden, pas pour ce cas déjà acté). Généralisation : si le secret vise
un outil qui a son propre agent/cache (ssh-agent, gpg-agent, keychain), vérifie d'abord que
l'agent n'est pas simplement vide — un agent vidé par un redémarrage n'est pas un secret perdu.

**Affûtage (25/07/2026) — le déclencheur est le SYMPTÔME, pas la lecture de ce skill.** La règle
ci-dessus n'a pas servi le 25/07 : `ssh root@VPS` a renvoyé `Permission denied (publickey)`, ç'a été
lu comme un **refus de permission**, annoncé à Léo (« je n'ai pas pu sonder ») et la réponse a été
bâtie sur la **documentation** au lieu de l'état réel — qui contenait 4 crons de plus et une heure
fausse. `ssh-add --apple-load-keychain` a suffi ensuite : la clé était dans le Keychain, l'agent
était juste vide. **Règle actionnable : `Permission denied (publickey)` sur une machine de Léo n'est
JAMAIS un refus d'accès ni une raison de se replier sur la doc.** Enchaîner immédiatement, sans
demander : `ssh-add -l` → `ssh-add --apple-load-keychain` → (si vide) fenêtre masquée +
`ssh-add --apple-use-keychain`. Ne remonter le problème à Léo qu'après les trois. Cf.
[[feedback-vps-ssh-toujours-sonder]].

## Piper un secret dans un CLI : `--passwordenv`, pas stdin (01/08/2026)

Symptôme : `printf '%s' "$PW" | bw unlock --raw` plante en
`ERR_USE_AFTER_CLOSE: readline was closed`, après avoir redessiné 15 fois son prompt
« Master password ». Cause : le CLI utilise `inquirer`, qui ouvre une `readline` sur le
TTY et **ignore un stdin déjà fermé** — piper ne « répond » pas à un prompt interactif,
ça le laisse tourner dans le vide.

Règle actionnable : **avant de piper un secret dans un CLI, cherche son option
d'environnement.** Pour Bitwarden c'est
`BWPW="$PW" bw unlock --passwordenv BWPW --raw`. Le secret reste en variable (donc hors
`history` et hors `ps`, contrairement à `--password xxx`), et le CLI ne prompte pas.
Le patron généralisable : `--*env VAR` > pipe stdin > jamais l'argument en clair.

Et ne conclus pas d'un prompt qui se redessine que la saisie est mauvaise : ici la
passphrase n'était jamais arrivée jusqu'au CLI.

## Un coffre verrouillé n'est pas un « non mesuré » (02/08/2026)

Symptôme : le MCP methylen répond *« Mot de passe introuvable : fournis METHYLEN_PASSWORD, ou
déverrouille Bitwarden (BW_SESSION) »*. Ç'a été lu comme une **limite d'environnement** : le lot a
été livré avec la mention « non mesuré, motif : coffre verrouillé », et la donnée récupérée par un
détour (l'état comptable lu en SQL côté Supabase). Le détour était bon — mais l'abandon, non :
`bin/seed-secrets.sh` ouvre UNE fenêtre masquée, charge les secrets dans l'agent RAM, et le
connecteur les relit **à chaque authentification** (`readPasswordFromAgent`), donc **sans
redémarrage**. Une fenêtre, 4 secondes, et l'appel passe.

Cause racine : le déclencheur du skill était pensé comme « j'ai une commande à lancer qui exige un
secret », pas comme « quelque chose vient de refuser faute de secret ». Le second est bien plus
fréquent, et c'est celui qui se déguise en fatalité.

**Règle actionnable — un refus faute d'identifiant est un GESTE À FAIRE, pas un constat à écrire.**
Dès que tu vois *coffre verrouillé · BW_SESSION absent · mot de passe introuvable · `bw status`
locked · un MCP qui refuse* : ouvre la fenêtre masquée immédiatement, sans demander la permission
et sans prévenir d'abord (Léo a tranché : c'est le geste attendu, la fenêtre EST la demande), puis
rejoue l'appel. Tu ne remontes le sujet que si la fenêtre est **annulée** ou si l'identifiant est
**refusé** — et alors tu écris « non mesuré » avec ce motif-là, qui est le vrai.

Corollaire à connaître avant de conclure « il faudrait redémarrer le connecteur » : les serveurs MCP
de Léo lisent l'agent RAM **au moment de l'appel**, pas seulement au lancement (methylen
`launch.sh` + `lib/cloudState.js`). Re-seeder suffit. Vérifie cette propriété dans le launcher avant
de proposer un redémarrage de session à quelqu'un qui travaille.

## `ask-secret.sh` n'a PAS de `--help` — ne le sonde jamais (06/08/2026)

**Symptôme** : `ask-secret.sh --help` a ouvert une vraie fenêtre de saisie sur l'écran de Léo ; il a
tapé un secret, le script l'a imprimé sur **stdout**, donc dans la sortie d'outil — et dans la
transcription de la session. Le secret a fui parce qu'il a été *demandé*, pas parce qu'il était stocké.

**Cause racine** : le script n'interprète aucun drapeau. `$1` est le **texte du prompt**, `$2` le titre,
`$3` la clé de cache. `--help` est donc un prompt comme un autre → dialogue → impression sur stdout.
C'est son contrat (§ « Le secret est écrit UNIQUEMENT sur stdout »), pas un bug.

**Règles** :
- **Ne lance JAMAIS `ask-secret.sh` pour découvrir son usage.** Son mode d'emploi est l'en-tête du
  fichier : `sed -n '1,20p' scripts/ask-secret.sh`. Lire, jamais exécuter.
- **Ne lance JAMAIS `ask-secret.sh` nu**, c'est-à-dire hors d'une substitution `$(...)` qui alimente
  immédiatement une variable d'environnement. Nu, sa sortie EST le secret en clair dans la transcription.
- Corollaire général : **un script dont le contrat est d'imprimer un secret sur stdout ne se sonde pas.**
  Avant d'exécuter un outil inconnu du dossier `scripts/` d'un skill à secrets, lis-le.
- Si la fuite a eu lieu : le dire à Léo **immédiatement et en clair**, ne pas ré-afficher la valeur, et
  **faire tourner le secret** — une valeur passée dans une transcription est compromise, point.

## Quand tu n'es pas sûr : vérifie en lecture seule d'abord

Avant une action **destructive ou irréversible** qui dépend du secret (déchiffrer-puis-réécrire un coffre, écraser un fichier, déployer), si tu as un doute sur la validité du secret, fais d'abord un **test en lecture seule** : déchiffre / authentifie sans rien écrire ni envoyer, et confirme que ça passe. Ça évite de partir dans une opération à mi-chemin avec un mauvais secret.

## La saisie est à l'aveugle → les fautes de frappe arrivent

Le champ est masqué : l'utilisateur peut se tromper sans le voir. Si une étape échoue (« passphrase invalide / coffre indéchiffrable »), **ne conclus rien avant de VÉRIFIER l'empreinte** : compare le kid de la saisie à la clé canonique (`keycheck verify`, ou `keyIdOf(pass)`). Deux cas nettement distincts :

- **kid ne concorde PAS** → vraie mauvaise saisie (typo, ou mauvaise passphrase). Là, re-propose la fenêtre (et `drop` le cache d'abord).
- **kid concorde mais l'étape échoue quand même** → la passphrase est BONNE ; **arrête de re-demander**. Le problème est en aval (coffre corrompu, kid périmé vs ct, transfert tronqué, bug de script). Débogue ça, garde le cache. Re-prompter en boucle ne fera que répéter le même échec avec la bonne passphrase (incident karto 16/07 : 5+ saisies inutiles d'une passphrase parfaitement correcte).

⚠️ Un kid qui concorde prouve que la passphrase est la bonne, **pas** que le ct s'ouvre (`keycheck verify` ne déchiffre pas). Pour trancher « le coffre lui-même est-il ouvrable ? », il faut un **vrai déchiffrement** (ex. `vault-open.mjs` côté karto), pas le kid.

## Lire le résultat et continuer

C'est tout l'intérêt : **l'utilisateur ne colle jamais la sortie**. Tu l'as nativement (tu as lancé la commande). Les scripts bien faits s'auto-rapportent (`✓ …` / `✗ …`) — en cas de succès, l'utilisateur n'a rien à faire. Enchaîne sur l'étape suivante. Ne lui redemande la sortie que si la commande échoue de façon opaque.

## Limite à connaître : étapes sortantes / prod

Une étape qui sort vers la production (SSH/scp vers un VPS, déploiement, appel d'API distante) peut être **bloquée par le bac à sable de Claude Code**, indépendamment du secret. Si ça arrive :
- soit l'utilisateur lance **cette ligne-là** lui-même (souvent sans secret, ex. un `./deploy.sh`),
- soit il ajoute une **règle de permission Bash** dédiée pour autoriser ce script précis.
Le mécanisme de saisie de secret de ce skill ne contourne pas ces garde-fous — il règle seulement le problème du secret interactif.

## Anti-patterns (à ne pas faire)

- ❌ Demander à l'utilisateur de **coller** son mot de passe dans le chat.
- ❌ Lui tendre une commande avec un placeholder `…`/`<password>` à remplacer.
- ❌ Mettre le secret en argument en clair (`--password xxx`, `-p xxx`) → fuite `history` + `ps`.
- ❌ Stocker un secret maître dans Keychain / Bitwarden / `.env` « pour la prochaine fois ».
- ❌ `echo`/log du secret, même pour debug.
