Quatre prompts que tu retapes deviennent quatre fichiers
Les extensions de Claude Code sont les fichiers qu'un repo peut contenir pour que tu arrêtes de retaper le même prompt : un skill, un hook, un subagent et un serveur MCP. Nous avons fait tourner quatre tâches récurrentes sur un vrai repo, une petite API de facturation avec un fichier JSON pour base de données. Chaque tâche a d'abord reçu la mauvaise extension, et le mauvais choix a échoué à l'écran à chaque fois. Ensuite, chacune est devenue un fichier commité. Cette page couvre les quatre gestes et la carte de choix qui décide quel type utiliser.
Deux résultats encadrent tout ce qui suit. On décrit souvent un hook comme une garantie et une règle de fichier mémoire comme une demande, pourtant notre premier hook s'est exécuté, a affiché son avertissement, et l'écriture est passée quand même. Ce qui transforme un hook qui s'exécute en hook qui bloque est expliqué dans la section sur le hook. Le premier serveur de docs que nous avons branché n'a rien changé, et celui qui a répondu était le serveur de docs de l'outil lui-même. La recette de hook que la plupart des gens copient exige aussi un fichier script séparé, alors que la nôtre tient entièrement dans le fichier de réglages.
Avant d'écrire l'un de ces quatre fichiers, il faut savoir ce que ta session charge déjà.
Les quatre en bref, et ce que ta session charge déjà
Une extension de Claude Code est un fichier que tu ajoutes à ton repo pour ne plus retaper les mêmes instructions dans la zone de prompt. Il en existe quatre types, et les lecteurs qui les connaissent déjà peuvent passer à la section suivante.
- Un skill est un runbook rangé dans ton repo. Claude ne charge ses étapes que lorsque cette tâche se présente, et tu peux le lancer en tapant son nom.
- Un hook ressemble à un hook de commit dans git, mais pour les actions de Claude lui-même. C'est un script qui s'exécute sur un événement, comme une édition, quelle que soit l'intention de Claude.
- Un subagent est un prestataire avec un bureau vide. Il reçoit ton brief, fait la lecture, et te renvoie un rapport au lieu de la pile de fichiers.
- Un serveur MCP est un client d'API que Claude peut appeler, pour des données qui ne sont pas sur ton disque, comme une base de données ou la documentation d'un outil.
Pour mesurer quoi que ce soit, il faut d'abord une référence. Ouvre une session toute neuve et lance la commande de contexte. Elle affiche un tableau de tout ce qui est chargé avant ton premier mot. Sur notre banc d'essai, le total était de 26,2k tokens sur une fenêtre de 1m, sans ligne de fichiers mémoire ni ligne MCP. Chaque tâche que tu donneras ensuite à Claude ajoute une ligne à ce tableau ou le laisse intact.
Le premier job : la checklist de release devient un skill
Un skill est une checklist rangée dans son propre dossier, que Claude ne lit que lorsque tu l'appelles. La première tâche est une checklist de release que tu colles sans cesse dans la zone de prompt : typecheck, tests, montée de version, une ligne de changelog, commit et tag.
L'endroit évident pour la mettre est le fichier mémoire, et c'est le mauvais choix, car tout ce qui s'y trouve est chargé à chaque tour de chaque session. Nous y avons mis les étapes et relu le tableau de contexte. Il avait gagné une ligne Memory files de 210 tokens, chargée que tu sois en train de publier une release ou non. La doc le dit elle-même : quand une section de ce fichier est devenue une procédure, sa place est dans un skill.
Le bon choix est un dossier sous le répertoire des skills, avec les étapes dans .claude/skills/release/SKILL.md, une courte description en tête, et une ligne de frontmatter en plus, disable-model-invocation: true. Cette ligne retire le skill de la liste de Claude, donc toi seul peux le lancer, ce qui est voulu pour quelque chose qui commite et qui tague. Tu tapes ensuite un slash et son nom, et la checklist s'exécute.
| Lecture du contexte | Total | Ligne Memory files |
|---|---|---|
| Référence, session neuve | 26.2k / 1m | aucune |
| Checklist dans le fichier mémoire | 26.4k / 1m | 210 tokens |
| Checklist en skill | 26.2k / 1m | aucune, skill non listé |
À l'exécution, Claude a terminé les six étapes en moins d'une minute : typecheck, 34 tests, montée de version de 0.4.0 à 0.4.1, ligne de changelog, commit et tag. Il a aussi repéré un défaut de notre propre checklist. La commande de commit telle qu'écrite ignore les fichiers non suivis, alors Claude a d'abord ajouté le nouveau changelog à l'index et l'a signalé. La release est maintenant une commande que tu tapes, elle vit dans le repo pour toute l'équipe, et elle ne coûte rien en contexte tant que tu ne la tapes pas.
Le deuxième job : la règle à tenir devient un hook
Un hook est un script que Claude Code exécute avant le déclenchement d'un outil, ici avant toute édition ou écriture, et la réponse de ce script décide de la suite. La deuxième tâche est une règle qui ne doit jamais être enfreinte : dans ce repo, le fichier de données stockées n'est jamais modifié à la main, et chaque changement passe par une migration.
Écrire cette règle en majuscules dans le fichier mémoire ne la fait pas respecter. La doc est directe : ce fichier est du contexte, pas une configuration appliquée, et une instruction comme « ne modifie jamais ce fichier » est une demande, pas une garantie, alors qu'un hook qui bloque l'édition est une application stricte.
Le premier hook évident est celui que nous avons écrit. Dans le fichier de réglages, il surveille les écritures sur le fichier de données, affiche un avertissement et se termine avec le code zéro, comme un script de log. Nous avons ensuite demandé à Claude de changer directement un montant de facture dans le fichier, sans migration. Le hook s'est exécuté et l'avertissement s'est affiché, mais l'écriture est passée : le prix unitaire de la troisième facture est passé de 8000 à 12000. La réponse de Claude n'a jamais mentionné le hook, car un hook qui se termine proprement sans rien afficher n'a pris aucune décision, et ses avertissements n'atteignent jamais Claude.
La garantie n'est pas que le hook s'exécute, c'est la réponse qu'il renvoie. Le correctif garde le même hook et lui fait renvoyer une décision deny : une courte ligne JSON portant la raison que tu veux que Claude lise. Se terminer avec le code deux bloque de la même façon. Le hook entier est une commande en ligne dans .claude/settings.json, sans fichier script :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -c 'if (.tool_input.file_path // \"\") | test(\"data/.*\\\\.json$\") then {hookSpecificOutput: {hookEventName: \"PreToolUse\", permissionDecision: \"deny\", permissionDecisionReason: \"Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions).\"}} else empty end'"
}
]
}
]
}
}
Avec la même demande, l'édition a été refusée et le fichier de données est resté intact. Claude a vu cette erreur : PreToolUse:Edit hook error: Stored data is never edited by hand: write a migration in src/store/migrations.ts (README § Conventions). Il a dit n'avoir rien changé et a demandé s'il fallait contourner le garde-fou ou écrire la migration.
La limite, que Claude a nommée lui-même : ce hook ne surveille que les outils d'édition et d'écriture, et lors d'un essai ultérieur Claude a proposé de faire le changement par le shell à la place. Un hook doit couvrir aussi le shell, ou le shell doit rester hors de ta liste d'autorisations, car une protection qui ne couvre qu'une porte reste une demande. Le hook ne coûte rien en contexte, car il s'exécute en dehors de ta conversation.
Le troisième job : l'audit qui inonde devient un subagent
Un subagent est une session Claude séparée avec son propre contexte vierge. Il reçoit ton brief, lit ce dont il a besoin, et seul un résumé te revient. La troisième tâche est un audit qui inonde ton fil : vérifier chaque fichier du dossier source par rapport aux conventions du README, et lister chaque violation avec le fichier et la ligne.
Demandé en ligne, Claude lit chaque fichier dans ta conversation, et tout ce contenu y reste pour le reste de la session, que tu le consultes encore ou non. Dans notre essai, Claude a lu les 19 fichiers source, la ligne Messages du tableau de contexte a gonflé, et les résultats se sont retrouvés enfouis sous la pile.
Le bon choix est un fichier sous .claude/agents/, ici .claude/agents/conventions-auditor.md, avec un nom, une description de quand l'utiliser, les outils qu'il peut employer et un modèle fixé. Fixer le modèle compte, car un subagent hérite sinon de celui de ta session, et la plupart des tâches de lecture n'ont pas besoin du gros.
Nous avons posé exactement la même question et Claude a de nouveau lu les fichiers lui-même. Dans les deux essais, il n'a jamais appelé l'agent, bien qu'il soit listé : 0 délégation sur 2. Ce qui a marché, c'est de le nommer au début du prompt : « use the conventions auditor subagent ». Un seul appel est parti et un court résumé est revenu.
| Essai | Contexte total | Ligne Messages |
|---|---|---|
| Audit en ligne | 46.1k | 19.3k |
| Subagent nommé | 34.1k | 7.2k |
Les résultats étaient aussi bons : le subagent a trouvé un bug de chemin que l'essai en ligne avait manqué, et en a manqué un que l'essai en ligne avait trouvé. Donc le fichier fait exister le subagent, et ton prompt fait que Claude l'utilise ; tant que Claude ne le choisit pas seul, tu le nommes. Une limite : la facture n'a pas bougé. Les deux essais ont coûté la même chose, car la lecture a bien lieu, simplement pas dans ta fenêtre.
Le quatrième job : la donnée hors d'atteinte devient un serveur MCP
Un serveur MCP est un client d'API que Claude peut appeler, et la tâche pour laquelle il existe, ce sont des données plus récentes que le modèle ou situées hors de ton disque. Le réflexe est ici le mauvais choix, alors nous l'avons fait en premier.
Nous avons branché un serveur de docs public pour notre bibliothèque de test et demandé à Claude un test avec des fake timers. Rien n'a changé : Claude connaissait déjà cette bibliothèque, et n'a appelé le serveur que lorsque nous l'avons nommé. Des docs que le modèle connaît déjà ne sont pas des données hors de sa portée.
Le vrai test est une question sur Claude Code lui-même, à propos d'un réglage de hook plus récent que le modèle : quel réglage fait bloquer un hook en échec, quelle version l'a ajouté, et quel est le JSON exact. Sans serveur, Claude a lu le schéma de réglages de sa propre installation, n'a rien trouvé, et a répondu qu'il ne trouvait pas ce réglage et qu'il n'en inventerait pas un (« I won't guess one »). Cette réponse est honnête et inutile, car le réglage existe ; il est arrivé après les connaissances du modèle.
La solution est d'ajouter au repo le serveur de docs de Claude Code lui-même, avec la portée projet pour que la config atterrisse dans un fichier à la racine du repo que toute ton équipe reçoit :
claude mcp add --transport http claude-docs --scope project https://code.claude.com/docs/mcp
Cela écrit .mcp.json. Claude Code liste le serveur comme en attente jusqu'à ce que tu l'approuves à ta prochaine session, et ses outils sont ensuite disponibles pour Claude. Nous avons posé la même question en mots simples, sans nommer le serveur. Claude a cherché dans les docs, trouvé la page, et répondu avec le réglage, la version et le JSON à ajouter, avec le lien.
Le réglage est "onFailure": "block", qui fait bloquer l'action à un hook en échec au lieu de la laisser passer. Il est arrivé en v2.1.295, alors que notre installation était encore en 2.1.294. Voilà la limite : Claude n'a jamais dit que notre installation ne pouvait pas l'utiliser, car il n'a pas vérifié la version, donc c'est à toi de vérifier ta version. Dans le tableau de contexte, le serveur n'apparaît que par les noms de ses outils, chargés à la demande tant que Claude n'en utilise pas un.
Quatre fichiers, une carte de choix
Après les quatre tâches, git status montre exactement quatre fichiers, car le hook n'a pas besoin de script à lui :
.claude/skills/release/SKILL.md.claude/settings.json.claude/agents/conventions-auditor.md.mcp.json
Commite-les, et la prochaine session sur ce repo les aura tous, comme quiconque le clone.
Chaque choix était le bon grâce à un tableau que fournit la doc, et que tu peux garder comme carte de décision :
| Situation | Extension |
|---|---|
| Une procédure que tu colles encore une fois | Skill |
| Quelque chose qui doit arriver à chaque fois, sans demander | Hook |
| Une tâche annexe qui inonde ta conversation de sorties que tu ne reliras pas | Subagent |
| Des données que tu recopies sans cesse depuis un endroit que Claude ne voit pas | Serveur MCP |
Les types se combinent aussi. La doc associe un skill à un serveur MCP : le serveur fournit la connexion, et le skill apprend à Claude à bien s'en servir. Un skill peut aussi s'exécuter dans un subagent forké, pour que sa lecture reste hors de ta fenêtre. Quand un autre repo a besoin de la même installation, un plugin est la boîte qui les livre tous ensemble.
La carte décide du type, pas de la qualité. Un hook qui ne fait que logger, un subagent que tu ne nommes jamais et un serveur pour des docs que le modèle connaît étaient chacun dans le bon type et ont tous échoué. Choisis le type avec la carte, puis fais en sorte que le fichier réponde, pas seulement qu'il existe.
Verdict : quand l'un, quand aucun, la règle unique
Les quatre tâches que tu retapais ont d'abord été essayées de la mauvaise façon, ont échoué à l'écran, et sont maintenant des fichiers du repo qui fonctionnent. La release est une commande que tu tapes. La règle sur les données est un hook qui refuse l'édition et fait demander Claude. L'audit revient sous forme de résumé. La question de docs obtient le réglage et la version que Claude ne trouvait pas seul.
Le hook par lequel nous avons ouvert, celui qui s'exécutait et laissait passer l'écriture, a enseigné la règle qui sous-tend tout le reste : un hook ne décide que par ce que renvoie sa commande, donc un hook qui ne fait que logger est une demande déguisée.
Certains cas n'appellent aucun de ces outils :
- Une convention que Claude rate sans cesse reste dans le fichier mémoire, sous forme d'une ligne, pas d'une procédure.
- Une tâche que tu dois piloter dans les deux sens, avec ton historique sous les yeux, reste dans le fil principal et non dans un subagent.
- Un serveur de docs pour une bibliothèque que le modèle connaît déjà coûte du contexte et ne change rien. Connecte ce que le modèle ne peut pas avoir, pas ce qu'il a déjà.
La règle à retenir : donne à chaque tâche que tu retapes son propre fichier, choisis le type avec la carte, et sache que parmi eux, seul le hook ne demande jamais.
AIDive