Module 6 — Créer ses propres commandes slash : les skills de A à Z
Les modules précédents ont fait le tour des commandes livrées. Ce module apprend à en ajouter : les skills. Depuis la v2.1.199, commandes personnalisées et skills sont la même chose. Un fichier .claude/skills/deploy/SKILL.md et un fichier .claude/commands/deploy.md créent tous les deux /deploy ; l'ancien format reste supporté, le nouveau apporte un dossier de fichiers de support, un frontmatter plus riche et la possibilité que Claude décide seul d'invoquer la skill. À la différence du CLAUDE.md, le corps d'une skill ne se charge que lorsqu'elle est invoquée : une longue matière de référence ne coûte presque rien tant qu'on ne s'en sert pas.
Anatomie d'une skill
Une skill est un dossier contenant au minimum un fichier SKILL.md. Le nom du dossier devient la commande à taper : .claude/skills/deploy/SKILL.md crée /deploy. Le dossier peut porter d'autres fichiers (scripts, spécifications, exemples) que Claude lira à la demande. Le SKILL.md a deux parties : un frontmatter YAML entre --- qui décrit la skill (Claude s'en sert pour décider s'il la charge seul), et un contenu Markdown qui est le prompt livré à Claude quand la skill est invoquée. Le frontmatter n'est lu que si --- est la toute première ligne ; sinon, Claude Code prend l'ensemble du fichier comme corps.
---
description: Résume les changements non commités et signale ce qui est risqué. Utiliser quand l'utilisateur demande « qu'est-ce qui a changé ? » ou veut un message de commit.
---
## Changements en cours
!`git diff HEAD`
## Instructions
Résume en deux ou trois puces, puis liste les risques : gestion d'erreur absente, valeurs codées en dur, tests à mettre à jour. Si le diff est vide, dis-le.
La ligne !`git diff HEAD` est une injection dynamique : Claude Code exécute la commande shell avant l'envoi, et sa sortie remplace le placeholder. Le modèle reçoit le diff vivant, pas la commande.
Portées : où enregistrer une skill
Sept emplacements coexistent :
| Emplacement | Chemin | Chargée dans |
|---|---|---|
| Entreprise | .claude/skills/<nom>/SKILL.md dans les settings managés | Machines où l'organisation la déploie |
| Personnelle | ~/.claude/skills/<nom>/SKILL.md | Tous les projets de la machine |
| Projet | .claude/skills/<nom>/SKILL.md | Toutes les sessions du dépôt |
| Nichée | <sous-dossier>/.claude/skills/<nom>/SKILL.md | Sessions démarrées dans/sous <sous-dossier>, sinon quand Claude touche un fichier de ce chemin |
| Dossier ajouté | .claude/skills/<nom>/SKILL.md dans un dossier --add-dir | Uniquement cette session |
| Plugin | <plugin>/skills/<nom>/SKILL.md | Où le plugin est actif, sous /<plugin>:<nom> |
| Compte claude.ai | Skills activées côté claude.ai | Cowork et sessions nuage (module 15) |
Précédences en homonymie : entreprise > personnelle > projet. Un fichier .claude/commands/ cède devant une skill du même nom. Une skill de plugin est préfixée. Le nom synced est réservé. Une skill de dépôt s'applique en -p même dans un dossier non approuvé : relire les allowed-tools avant tout claude dans le dépôt.
Référence complète du frontmatter
Tous les champs sont optionnels ; seul description est recommandé. Booléens : yes/no/on/off/1/0 en plus de true/false (v2.1.218+).
| Champ | Effet |
|---|---|
name | Nom d'affichage. Par défaut, le nom du dossier. Ne change pas la commande à taper, sauf dans un plugin où name remplace le dernier segment. |
description | Ce que fait la skill et quand l'utiliser. Cap combiné avec when_to_use : 1 536 caractères. |
when_to_use | Contexte supplémentaire : phrases-déclencheurs, exemples. |
argument-hint | Aide d'autocomplétion : [fichier] [format]. |
arguments | Liste nommée pour la substitution $nom. Chaîne ou liste YAML. |
disable-model-invocation | true empêche Claude de charger la skill seul (et de la déclencher via tâche planifiée). |
user-invocable | false cache du menu / : seule l'invocation par Claude marche. |
allowed-tools | Outils autorisés sans confirmation pendant le tour. Grant effacée au prochain message. |
disallowed-tools | Outils retirés du pool pendant le tour. Ne peut pas retirer EndConversation s'il reste d'autres outils. |
model | Modèle pour la durée du tour ; mêmes valeurs que /model, ou inherit. |
effort | Effort pour le tour : low, medium, high, xhigh, max. |
context | fork fait tourner la skill dans un sous-agent isolé. |
agent | Type de sous-agent quand context: fork : Explore, Plan, general-purpose ou un sous-agent de .claude/agents/. |
background | Avec context: fork, false bloque le tour. Défaut true. v2.1.218+. |
hooks | Hooks enregistrés à l'invocation, actifs pour la session. |
paths | Motifs glob limitant l'auto-invocation aux fichiers correspondants. |
shell | bash (défaut) ou powershell pour les commandes injectées. |
metadata | Carte YAML libre pour son propre outillage. |
license, compatibility | Champs du standard Agent Skills, acceptés sans effet. |
Hors Claude Code (upload claude.ai, API Skills, package_skill.py), seuls name, description, license, compatibility, metadata et allowed-tools sont acceptés ; les autres sont des extensions Claude Code.
Arguments : $ARGUMENTS, $0/$1, $nom
Une skill reçoit ce qui suit son nom sur la ligne. Quatre formes de placeholder : $ARGUMENTS (totalité comme chaîne unique — sans placeholder, Claude Code les colle en fin de skill sous la forme ARGUMENTS: <valeur>), $ARGUMENTS[N] (accès indexé base 0), $N (raccourci équivalent, $0, $1), et $nom (argument nommé déclaré dans arguments: [issue, branche], $issue prend le premier).
Les valeurs multi-mots doivent être entre guillemets (/migrer "SearchBar" JavaScript TypeScript). Un placeholder indexé sans argument reste inchangé ; un nommé sans valeur devient une chaîne vide. Un argument contenant $1 ou $ARGUMENTS est inséré littéralement sans nouvelle expansion. Pour un $ littéral, on l'échappe : \$1.00.
Injection dynamique et références de fichier
Deux formes injectent du contenu externe avant l'envoi à Claude. Ligne inline !`commande` : reconnue seulement si ! est en début de ligne ou juste après une espace ; la sortie remplace le placeholder et n'est pas re-scannée. Bloc multiligne ```! pour plusieurs commandes. Les références @fichier attachent un fichier au contexte à l'invocation, utile pour partager un fragment de convention entre plusieurs skills.
Deux variables sont substituées dans le corps et dans les règles Bash de allowed-tools : ${CLAUDE_SKILL_DIR} (dossier de la skill) et ${CLAUDE_PROJECT_DIR} (racine du projet, v2.1.196+). L'astuce classique consiste à écrire un script dans le dossier de la skill et à pré-approuver son exécution : allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/gen.sh *) puis, dans le corps, appeler ${CLAUDE_SKILL_DIR}/scripts/gen.sh. Si une commande injectée échoue, l'invocation entière est avortée — Claude ne voit rien. Un exit code 1 des commandes de recherche listées dans « Output limits » est traité comme normal ; ajouter || true sur les autres commandes attendues en exit non nul.
Sous-agent, permissions et visibilité
context: fork fait tourner la skill dans un sous-agent isolé. Le corps devient le prompt ; l'historique de la conversation n'est pas transmis. Trois raisons : tâche longue en arrière-plan, palette d'outils réduite (agent: Explore en lecture seule), grosse sortie à contenir. Le fork tourne en arrière-plan par défaut ; background: false (v2.1.218+) bloque le tour. Un fork en arrière-plan écrit hors des checkpoints : /rewind ne défait pas ses éditions, il faut passer par git.
allowed-tools autorise des outils sans confirmation pendant le tour d'invocation (grant effacée au prochain message) ; disallowed-tools bloque ; disable-model-invocation: true empêche Claude de charger la skill seul (utile pour tout effet de bord) ; user-invocable: false fait l'inverse (cachée du menu, seule l'invocation par Claude marche). Le fichier skillOverrides applique une visibilité de l'extérieur : "on", "name-only", "user-invocable-only", "off". Le menu /skills l'écrit tout seul.
Rechargement et itération
Claude Code surveille les répertoires de skills et prend en compte ajouts, modifications, suppressions sans redémarrer, sauf en mode bare. /reload-skills force le rescan. La meilleure discipline pour tester reste la comparaison A/B ; le plugin officiel skill-creator automatise la boucle et écrit un evals.json dans le dossier de la skill.
Claude Code charge la liste des skills à chaque tour avec un budget de 1 % de la fenêtre du modèle. Au-delà, les descriptions sont raccourcies en commençant par les moins invoquées. Placer la phrase-clé en premier, plafonner à 1 536 caractères la somme description + when_to_use, et passer une skill secondaire à "name-only" dans skillOverrides gardent la liste utile. /skill-doctor (v2.1.252+) montre le coût de chaque skill.
Fil rouge : six skills pour Kiosque
Six skills à écrire dans .claude/skills/ de Kiosque. Aucune ne nécessite d'outillage externe au-delà de git, gh, pytest, ruff et make.
1. /commit — message conventionnel à partir du diff staged
---
description: Rédige un message de commit conventionnel à partir des changements indexés et propose de commiter. Utiliser après `git add` ou quand l'utilisateur demande « commit ».
argument-hint: "[portée]"
disable-model-invocation: true
allowed-tools: Bash(git diff --staged*) Bash(git status*) Bash(git commit *)
---
## Diff indexé
!`git diff --staged`
## Statut
!`git status --short`
## Instructions
Rédige un message **conventionnel** (`feat:`, `fix:`, `refactor:`, `test:`, `chore:`, `docs:`) en français, portée = $ARGUMENTS s'il est fourni, sinon déduite du chemin des fichiers.
- Titre : 72 caractères max, présent, impératif.
- Corps facultatif : seulement si le « pourquoi » n'est pas évident.
- Refuse et signale si le diff contient `.env` ou `secrets/`.
Exécute ensuite `git commit -m "<message>"`.
2. /revue [fichier] — relecture en lecture seule
---
description: Relit un fichier (ou le diff courant) à la recherche de bugs, d'oublis de tests et de violations de conventions. Lecture seule.
argument-hint: "[chemin]"
allowed-tools: Read Grep Glob Bash(git diff*)
disallowed-tools: Edit Write Bash(git commit*) Bash(git push*)
---
Cible : $ARGUMENTS (si vide, prends `git diff HEAD`).
## Diff de référence
!`git diff HEAD -- $ARGUMENTS`
## Instructions
Relis le code **en lecture seule** — n'édite rien. Cherche :
1. **Bugs** : condition inversée, mauvais type, off-by-one, gestion d'erreur absente.
2. **Sécurité** : injection SQL, secret en clair, absence de validation d'entrée.
3. **Conventions Kiosque** (voir `.claude/rules/api.md`) : noms de fonction en anglais, commentaires en français, `raise HTTPException` plutôt que `return` d'erreur.
4. **Tests manquants** : nouvelle branche non couverte, cas d'erreur non testé.
Rends un rapport en quatre sections (Bugs / Sécurité / Conventions / Tests), chaque item avec fichier, ligne, phrase.
3. /nouveau-endpoint <ressource> — route + schéma + test
---
description: Génère un endpoint FastAPI CRUD (route + schéma Pydantic + test pytest) pour une nouvelle ressource.
argument-hint: "<ressource-au-singulier>"
allowed-tools: Read Grep Glob Edit Write
---
Ressource cible : **$ARGUMENTS**.
@.claude/rules/api.md
Routes existantes :
!`ls app/routes/`
## Instructions
Crée : `app/schemas/$0.py` (Pydantic `Create`, `Read`, `Update`), `app/routes/$0.py` (`GET /$0s`, `GET /$0s/{id}`, `POST`, `PATCH`, `DELETE` ; SQLAlchemy via `Depends(get_db)`), `tests/test_$0.py` (un test heureux par endpoint, 404 sur `GET /$0s/{id}`, 422 sur `POST`). Enregistre le routeur dans `app/main.py` et lance `pytest tests/test_$0.py`.
4. /tests-cibles — pytest sur les tests touchés par le diff
---
description: Lance uniquement les tests pytest qui touchent aux fichiers du diff courant. Utiliser avant chaque commit pour un feedback rapide.
allowed-tools: Bash(git diff*) Bash(pytest*)
---
## Fichiers modifiés
!`git diff --name-only origin/main...HEAD`
## Instructions
Prends les fichiers de `tests/` modifiés, ajoute `tests/test_<module>.py` pour chaque `app/<module>.py` modifié, puis lance `pytest -x -q <fichiers>`. Si rien n'est ciblé, `pytest -x -q -k <mot-clé>` pour couvrir les tests indirects. Rapporte les échecs (test, ligne, assertion). Ne relance pas toute la suite — `make test` reste au clavier de l'utilisateur.
5. /doc-api — met à jour docs/api.md depuis les routes
---
description: Régénère `docs/api.md` à partir des routes FastAPI de `app/routes/`. Utiliser après tout ajout ou modification d'endpoint.
allowed-tools: Read Grep Glob Edit Write Bash(rg *)
paths: app/routes/**
---
Routes :
!`rg -n "@(?:router|app)\.(get|post|patch|put|delete)" app/routes/`
## Instructions
Pour chaque endpoint : méthode, chemin, tag, résumé (docstring), schémas Pydantic d'entrée et sortie, codes de réponse. Écris `docs/api.md` : un tableau par tag `| Méthode | Chemin | Entrée | Sortie | Statuts |`, puis une section H3 par endpoint avec description française (reprends la docstring quand elle existe). Termine par « Généré par `/doc-api` — ne pas éditer à la main ».
6. /notes-de-version <tag> — sous-agent forké
---
description: Rédige les notes de version markdown pour un tag Git à partir des commits depuis le tag précédent. Tourne dans un sous-agent séparé.
argument-hint: "<tag>"
context: fork
agent: Explore
background: true
allowed-tools: Bash(git log*) Bash(git tag*) Bash(git show*) Read Grep
---
Tag cible : **$ARGUMENTS**.
Tag précédent :
!`git describe --tags --abbrev=0 $ARGUMENTS^ 2>/dev/null || echo "(aucun tag antérieur)"`
Journal :
!`git log --pretty=format:"%h %s" $(git describe --tags --abbrev=0 $ARGUMENTS^ 2>/dev/null)..$ARGUMENTS`
## Instructions
Regroupe en quatre sections : **Nouveautés**, **Corrections**, **Améliorations internes**, **Documentation**. Ignore les commits de merge et de type `chore(deps)`. Une phrase par item, claire pour un utilisateur non technique (« Le module de paiement accepte un second prestataire »), pas le message brut. Retourne le markdown dans la conversation ; ne crée pas de fichier.
Depuis la v2.1.199, une seule ligne enchaîne deux skills : /revue /commit paiements charge les deux skills et leur passe paiements comme argument, utile quand la revue ne doit rien bloquer.