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 ...
Scanned 9/6/2026
Install to Claude Code
npx -y skills add lbachelotcapitalb/leo-bachelot-ia-skills --skill autocli-password --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Autocli Password?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/lbachelotcapitalb-autocli-password)More formats (shields.io, HTML) on the badges page.
---
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 (app-vault, Bitwarden…), pose aussi le détail dans la mémoire-pièges de ce système (`[[pitfalls-<systeme>]]`, 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 app-vault 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)
L'utilisateur 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'utilisateur est re-sollicité »). Le menu s'ouvre sur « 24 heures » ; l'utilisateur 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'utilisateur 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 l'utilisateur 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 chiffré de l'app 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`/`app-vault`.
- **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'utilisateur est re-sollicité alors que le secret est en RAM (08/08/2026)
**Symptôme, dit par l'utilisateur** : « 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é** |
| `app-vault` | 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`.
**5ᵉ cause, trouvée le 09/08 — la clé se perdait dans un script INTERMÉDIAIRE.** `bw-unlock.mjs`,
`bw-get.mjs` et `vault-connect.mjs` (dépôt `cartographie-it`) appelaient `ask-secret.sh` avec
**deux arguments**. Symptôme à reconnaître : **deux fenêtres dans une seule commande** — la mienne
(avec `bw-master`, qui mémorise), puis celle du script (sans clé, qui ne lit pas ce que je viens de
mémoriser), séparées de ~20 s. Sonder `has` avant ne protège de rien dans ce cas : la sonde répond
HIT et la fenêtre s'ouvre quand même. Corrigé : les trois passent `bw-master`. Corrigé aussi, le
filet d'extinction de `secret-agent.mjs` partait du démarrage du daemon, donc un secret confié tard
mourait avant son TTL — il part désormais de la dernière échéance connue.
⚠️ **Tout script qui appelle `ask-secret.sh` doit passer la clé en 3ᵉ argument.** Avant d'écrire un
nouvel appelant : `grep -rn "ASK, prompt, title\]" ` sur le dépôt. Et si l'avertissement
« AUCUNE clé de cache » apparaît dans une sortie où TU as pourtant passé la clé, c'est qu'un script
appelé en aval en ouvre une seconde : va corriger CE script, pas ta commande.
### Registre des clés canoniques — n'en invente jamais une nouvelle
Les noms ci-dessous sont des **exemples de nommage**. Sur une machine déjà outillée, la clé qui
fait foi est celle que les scripts existants passent déjà — `grep -rIn "ask-secret.sh" ~/.claude`
avant d'en choisir une : une clé neuve pour un secret déjà mis en cache, c'est une fenêtre de
plus pour l'utilisateur, et c'est précisément le défaut que ce skill existe pour fermer.
| clé | secret | usages typiques |
|---|---|---|
| `app-vault` | passphrase du coffre chiffré de l'app | `app-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 | 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'utilisateur.
### Le réflexe, dans cet ordre
1. **Sonder, jamais deviner** — `node "$SKILL/scripts/secret-agent.mjs" has app-vault`
→ `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'utilisateur 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'utilisateur 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 (`app-vault`, `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 du coffre app (rebuild chiffré)" "app-vault · rebuild" app-vault)" \
&& APP_VAULT_PASS="$SECRET" node app-sync.mjs rebuild ; unset SECRET APP_VAULT_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 app-vault`.
**Exemple réel — rebuild + deploy d'un coffre chiffré (app-vault) :**
```bash
SKILL=~/.claude/skills/autocli-password
cd ~/Documents/Claude/Projects/cartographie-it \
&& APP_VAULT_PASS="$("$SKILL/scripts/ask-secret.sh" "Passphrase du coffre app (rebuild chiffré)" "app-vault · rebuild")" \
node app-sync.mjs rebuild \
; unset APP_VAULT_PASS
# puis (sans secret) :
./deploy-app-vault.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'utilisateur, 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 app-vault/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'utilisateur (« 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'utilisateur 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'utilisateur 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 d'une app maison répond *« Mot de passe introuvable : fournis <APP>_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'utilisateur 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'utilisateur lisent l'agent RAM **au moment de l'appel**, pas seulement au lancement (`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'utilisateur ; 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'utilisateur **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.
## Un `set` réussi ne prouve RIEN : sonde `has` après avoir seedé (10/08/2026)
**Symptôme** : `mcp-server/bin/seed-secrets.sh` annonce « ✓ Secrets MCP chargés en RAM », et le MCP
l'app répond dans la minute « Mot de passe introuvable ».
**Cause racine, en deux étages.** (1) `bw get password "<nom>"` fait une recherche **floue** : un nom
qui matche plusieurs items (séparateur « · » commun, doublon « … — RÉVOQUÉ ») échoue, `2>/dev/null`
avale l'erreur, et le pipe pousse une **chaîne vide**. (2) `secret-agent.mjs set` ignore un payload
vide **en silence** (`if (ttl > 0 && payload)`) et sort en 0 — donc le pipe réussit, et le script
imprime son « ✓ ». Le trou est structurel : **`set` ne peut pas échouer**, il n'y a aucun retour.
**Règles actionnables** — pour tout script qui seede l'agent depuis un coffre :
- **Résous l'item par son `id`**, jamais par son nom : `bw list items --search <terme simple>`, filtre
le nom **exact** normalisé **NFC** (cf. [[pitfall-osascript-nfd-bitwarden]]), refuse 0 ou ≥2
candidats — puis `bw get password <id>`.
- **Déclare la SOURCE de la valeur par item** (`password`, ou champ custom nommé). **Jamais de repli
sur `notes`** : les items de l'utilisateur y portent un mode d'emploi en prose. Un repli sur `notes` charge
du texte **non vide**, donc accepté par l'agent — l'erreur ne se voit alors qu'au premier appel
distant (`invalid_client`), bien plus tard et bien plus loin. Vécu le 10/08 : 216 caractères de
prose chargés comme client secret Google. Un secret d'env n'est jamais multi-lignes : refuse-le.
- **Vérifie APRÈS coup avec `has`** (jamais `get`, cf. 06/08) pour CHAQUE clé attendue, et ne
n'imprime le « ✓ » final que si toutes répondent HIT. Sinon : `exit 1` et message par clé.
Généralisation : **n'annonce jamais « c'est chargé » sur la foi du code de retour d'un `set`.** La
seule preuve est une sonde de présence indépendante.
## 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 app-vault 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é app-vault), 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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!