
Een skill is een map met een SKILL.md: een YAML-frontmatter (name, description) en Markdown-instructies, plus scripts en referenties die pas op aanvraag worden geladen. Alleen de description blijft permanent in de context staan, en die wordt afgekapt, wat er de enige trigger van maakt die telt. Dezelfde map werkt in Claude Code, Codex, Copilot en OpenCode, mits je hem in de juiste map plaatst.
Een skill is een map met daarin een bestand SKILL.md. Het schrijven is het makkelijke deel. De rest vraagt te weten waar je het neerzet zodat de agent het vindt, te controleren dat het echt wordt geactiveerd, en te meten wat het de rest van de tijd kost. Deze gids doet alle drie, aan de hand van een skill die speciaal voor dit doel is gebouwd en uitgevoerd.
Wat is een skill precies?
Het Agent Skills-formaat is gemaakt door Anthropic en vervolgens als open standaard gepubliceerd, de specificatie staat tegenwoordig op agentskills.io. Ze komt neer op weinig: een map, een verplicht SKILL.md, en verder wat je zelf wilt.
regex-verifiee/
├── SKILL.md # verplicht: YAML-frontmatter + Markdown-instructies
├── scripts/ # optioneel: code die de agent uitvoert
├── references/ # optioneel: documentatie die op aanvraag wordt geladen
└── examples/ # optioneel: sjablonen, datasetsHet frontmatter is kort en strikt. name: hoogstens 64 tekens, alleen kleine letters, cijfers en koppeltekens, geen koppelteken aan het begin of einde, geen dubbel koppelteken, en het moet identiek zijn aan de naam van de bovenliggende map. description: verplicht, hoogstens 1.024 tekens, ze zegt wat de skill doet en wanneer je hem gebruikt. Drie optionele velden maken het geheel compleet: license, compatibility (500 tekens) en metadata. Een zesde, allowed-tools, is in de specificatie als experimenteel gemarkeerd.
Het mechanisme dat de zaak interessant maakt, heet progressive disclosure, in drie niveaus. Bij het opstarten laadt de agent alleen de name en de description van elke skill, de specificatie mikt op zo’n honderd tokens. Zodra een taak aansluit, leest hij de hoofdtekst van SKILL.md (aanbevolen onder de 5.000 tokens, maximaal 500 regels). Pas daarna, als hij het nodig heeft, opent hij de bestanden in scripts/, references/ of assets/. De technische blogpost van Anthropic van 16 oktober 2025 zegt het andersom: besteed zorg aan de name en de description, want daarop, en alleen daarop, beslist de agent of hij de skill activeert.
Een skill die nooit wordt geactiveerd, kost dus toch zijn beschrijving, bij elke sessie, in elk contextvenster. Dat is precies wat het commando /skill-doctor van Claude Code meet, en het is de eerste kostenpost die wordt onderzocht in het verslag van Spotify over de tokenfactuur.
Waar zoekt elke agent zijn skills?
Dit is het onderdeel dat niemand correct beschrijft, omdat het van tool tot tool verschilt. Hier zijn de paden zoals ze in de documentatie van elke uitgever staan, geraadpleegd op 7 september 2026.
| Tool | Project | Gebruiker |
|---|---|---|
| Claude Code | .claude/skills/<nom>/SKILL.md | ~/.claude/skills/<nom>/SKILL.md |
| Codex | .agents/skills/ (en .codex/skills/, zie verderop) | $CODEX_HOME/skills, ~/.agents/skills |
| GitHub Copilot | .github/skills, .claude/skills, .agents/skills | ~/.copilot/skills, ~/.agents/skills |
| OpenCode | .opencode/skills, .claude/skills, .agents/skills | ~/.config/opencode/skills, ~/.claude/skills, ~/.agents/skills |
Daar volgen twee dingen uit. .agents/skills is bezig het gedeelde neutrale pad te worden, Codex, Copilot en OpenCode lezen het alle drie. En zowel Copilot als OpenCode lezen ook .claude/skills, de map van Claude Code fungeert de facto als uitwisselingsformaat. Eén en dezelfde skillmap kan dus meerdere agents bedienen zonder te worden gedupliceerd. Wat de omgevingen betreft: GitHub kondigt skills aan voor de cloud-agent, codereview, Copilot CLI, de GitHub Copilot-app en de agentmodus van VS Code en JetBrains-IDE’s.
Op 7 september 2026 telt de showcase van agentskills.io 46 compatibele producten, van Cursor tot Goose, met onder meer Junie, Kiro, Roo Code, Laravel Boost, OpenCode en OpenClaw.
Een skill bouwen die iets oplevert: regex-verifiee
Een goede skill legt een procedure vast die je herhaaldelijk uitvoert en die de agent overslaat als je ze niet oplegt. Het gekozen geval hier: reguliere expressies. Een agent levert je in drie seconden een regex, zonder hem ooit op een tegenvoorbeeld te testen. De skill regex-verifiee verbiedt dat soort antwoord.
Het SKILL.md, volledig voor het frontmatter en het begin van de hoofdtekst:
---
name: regex-verifiee
description: Écrire une expression régulière et la prouver avant de la livrer. À utiliser dès qu'une demande porte sur une regex, une expression régulière, une validation de format (code postal, SIRET, IBAN, e-mail, téléphone, slug, plaque), un preg_match, un preg_replace ou un RegExp. Impose un fichier de cas valides et invalides, son exécution dans le moteur cible (Node et PHP), puis la livraison regex + tableau de cas + limites.
---
# Regex vérifiée
Une regex qui n'a jamais tourné sur ses contre-exemples n'est pas une regex, c'est une intuition.
Suivez ces cinq étapes dans l'ordre. Ne sautez pas l'étape 4.
## Procédure
1. **Cadrer.** Demandez, ou décidez explicitement : le moteur (JavaScript, PCRE/PHP, POSIX), la
chaîne testée (déjà nettoyée ou brute), et si la valeur peut être vide.
2. **Proposer.** Écrivez la regex ancrée et, en une phrase par groupe, ce que chaque partie accepte.
3. **Écrire les cas.** Créez un fichier JSON : au moins six valides et six invalides. Les invalides
doivent inclure les pièges de `references/cas-types.md` pour le type de donnée concerné.
4. **Exécuter.** Lancez le script sur les deux moteurs et collez la sortie brute dans la réponse.
5. **Livrer.** Réponse finale = la regex + le tableau de cas produit par le script + les limites.De beschrijving is bewust rijk aan triggerwoorden: “regex”, “reguliere expressie”, “postcode”, “SIRET”, “preg_match”, “RegExp”. Dat zijn de woorden die de lezer typt. Schrijf ze in de taal waarin men jou aanspreekt, niet in die van de documentatie.
De map bevat vervolgens een casusbestand in JSON-formaat, twee runners (één in Node, één in PHP) die hetzelfde bestand lezen, en een referentie references/cas-types.md die de tegenvoorbeelden per gegevenstype opsomt. Het casusbestand ziet er zo uit:
{
"name": "code postal français",
"engine": "both",
"pattern": "^(?:0[1-9]|[1-8]\\d|9[0-8])\\d{3}$",
"flags": "",
"valid": ["01000", "20000", "62500", "75001", "97400", "98000"],
"invalid": ["", "00000", "99999", "7500", "750011", "75 001", "2A000", " 75001", "75001\n"]
}Beide runners tonen dezelfde tabel en geven een exitcode ongelijk aan nul zodra een casus faalt. Die exitcode doet het werk: de agent kan niet concluderen op basis van een commando dat op een fout uitkomt.
Eerste verrassing, verkregen door het testharnas zelf te draaien nog voordat ik er een agent op zette: dezelfde regex geeft niet hetzelfde verdict in de twee engines.
$ node scripts/verifier.mjs examples/code-postal-fr.json
moteur : node v22.23.2
regex : /^(?:0[1-9]|[1-8]\d|9[0-8])\d{3}$/
...
"75001\n" false false ok
15 cas, 0 échec(s)
$ php scripts/verifier.php examples/code-postal-fr.json
moteur : PHP 8.4.19, PCRE 10.47 2025-10-21
regex : /^(?:0[1-9]|[1-8]\d|9[0-8])\d{3}$/
...
"75001\n" false true ECHEC
15 cas, 1 échec(s)In PCRE accepteert $ een afsluitende regeleinde, in JavaScript niet. Een formulierveld dat binnenkomt met een \n aan het eind, komt dus door de PHP-validatie en faalt bij de JavaScript-validatie, met dezelfde expressie in beide bestanden. Beide oplossingen, allebei hier getest: de modifier D aan de PHP-kant (preg_match('/^\d{5}$/D', "75001\n") geeft 0 terug), of een overdraagbaar anker (?![\s\S]) in plaats van $, dat de vijftien gevallen in beide engines doorstaat.
Testen met Codex zonder aan ~/.codex te raken
De documentatie van Codex beschrijft een stapel van roots: $CWD/.agents/skills, de bovenliggende mappen, $REPO_ROOT/.agents/skills, dan $HOME/.agents/skills, /etc/codex/skills en de skills die met de CLI worden meegeleverd. Met andere woorden, een projectskill bestaat echt: je hoeft niet in ~/.codex/skills of ~/.codex/config.toml te schrijven om hem te proberen.
Rest de vraag wat de binary werkelijk doet. Codex stelt daarvoor een debugcommando beschikbaar dat de prompt toont zoals het model hem ontvangt, zonder het model aan te roepen:
$ mkdir -p demo-projet/.codex/skills
$ cp -R regex-verifiee demo-projet/.codex/skills/
$ cd demo-projet && codex debug prompt-input "test"
### Skill roots
- `r0` = `/…/demo-projet/.codex/skills`
- `r1` = `/Users/gekkode/.codex/skills`
- `r2` = `/Users/gekkode/.agents/skills`
- `r3` = `/Users/gekkode/.codex/skills/.system`
- `r4` … `r10` = caches de plugins
### Available skills
- regex-verifiee: Écrire une expression régulière et la prouver avant de la livrer. À utiliser dès qu'une deman (file: r0/regex-verifiee/SKILL.md)De skill wordt gezien, vanuit <projet>/.codex/skills, een pad dat de documentatie niet vermeldt, maar dat de binary wel degelijk scant, en zelfs als eerste. Door een tweede testskill in <projet>/.agents/skills te plaatsen, verscheen een root r11 met dat pad: beide werken, en de map .codex/ komt als eerste.
Dan de echte test. Zelfde prompt, zelfde model, zelfde machine, een paar minuten na elkaar: eenmaal in het project met de skill, eenmaal in een lege map.
codex exec -m gpt-6-astra -s workspace-write --skip-git-repo-check \
"Dans un formulaire PHP, je dois valider le code postal saisi par le visiteur. Donne-moi l'expression reguliere a utiliser."Zonder de skill: 19 seconden, geen enkel commando uitgevoerd, en dit antwoord, preg_match('/\A[0-9]{5}\z/', $codePostal), met een zin die zegt dat de expressie het formaat controleert en niet of de postcode echt bestaat. Door het harnas gehaald, faalt deze regex op twee van de vijftien gevallen: hij accepteert 00000 en 99999.
Met de skill: 106 seconden, tien commando’s uitgevoerd. Codex las het SKILL.md, dan de twee scripts, dan references/cas-types.md, de volledige progressive disclosure, niveau na niveau. Vervolgens schreef hij zijn eigen casusbestand (achttien gevallen, waaronder "75001\n00000" en "2a000", overgenomen uit de referentie), draaide de twee runners, en leverde dit:
$codePostal = $_POST['code_postal'] ?? '';
$valide = is_string($codePostal)
&& preg_match('/\A(?:0[1-9]|[1-8][0-9]|9[0-8])[0-9]{3}\z/', $codePostal) === 1;Met daaronder de twee tabellen van achttien gevallen en een sectie “beperkingen”. Merk op dat hij niet dezelfde expressie in de twee engines gebruikte. \A … \z voor PCRE, ^ … (?![\s\S]) voor JavaScript. De skill heeft hem dat nooit opgedragen, hij heeft het afgeleid door de gevallen uit te voeren.
Een skill van een zestigtal regels heeft dus een antwoord van drie seconden, fout op twee gevallen, omgezet in een procedure van twee minuten waarvan het resultaat verifieerbaar is. De activering gebeurde vanzelf, op basis van de beschrijving: de prompt bevat noch het woord “skill”, noch de naam regex-verifiee. In Codex kun je het ook afdwingen met $regex-verifiee in de prompt, in Claude Code met /regex-verifiee.
Wat kost een beschrijving, en waarom ze wordt afgekapt
Tijdens de uitvoering gaf Codex een waarschuwing die ik niet had verwacht:
Skill descriptions were shortened to fit the skills context budget. Codex can still see every skill, but some descriptions are shorter. Disable unused skills or plugins to leave more room for the rest.
Geverifieerd met een testskill waarvan de beschrijving bestaat uit driehonderd bekende tekens: op deze machine, met 146 geïnstalleerde skills, wordt elke beschrijving afgekapt tot 94 tekens. Mijn beschrijving van 422 tekens komt dus voor twee derde afgeknot bij het model aan, midden in het woord “demande”. Alles wat daarna komt, “SIRET”, “IBAN”, “preg_match”, “RegExp”, levert niets meer op voor de activering.
Het budget is instelbaar. Codex accepteert een sleutel skills.max_context_tokens, die je als override kunt meegeven zonder in het configuratiebestand te schrijven:
codex debug prompt-input -c skills.max_context_tokens=16000 "test"| Budget | Behouden beschrijvingslengte | Grootte van het skillblok |
|---|---|---|
| 2.000 | 40 tekens | 8.202 tekens |
| 4.000 | 54 tekens | 16.492 tekens |
| 5.000 | 82 tekens | 20.482 tekens |
| standaard | 94 tekens | 22.230 tekens |
| 8.000 | 198 tekens | 32.295 tekens |
| 16.000 | 300 tekens (geen afkapping) | 40.201 tekens |
Daaruit volgen twee regels. Zet de triggerwoorden in de eerste negentig tekens van de beschrijving, de rest is een bonus. En houd er rekening mee dat hoe meer skills je installeert, hoe meer je de beschrijving van alle andere skills afknot. De catalogus nam hier 22.230 tekens context in, bij elke sessie, voor 146 skills waarvan ik er maar een handvol gebruik. Verwijderen is beter dan het budget verhogen.
Claude Code past hetzelfde principe toe, met gepubliceerde cijfers. Het lijstbudget is 1% van het contextvenster van het model, instelbaar via skillListingBudgetFraction of de omgevingsvariabele SLASH_COMMAND_TOOL_CHAR_BUDGET. Elke vermelding is hoe dan ook geplafonneerd: description en when_to_use samengevoegd worden afgekapt tot 1.536 tekens, een plafond instelbaar via skillListingMaxDescChars. En als de lijst overloopt, is de documentatie expliciet over de offervolgorde: Claude Code verwijdert eerst de beschrijvingen van de skills die je het minst aanroept. De naam zelf blijft altijd in de lijst staan.
Beide tools bieden hetzelfde ontsnappingsluik: uitschakelen in plaats van het budget op te blazen. Bij Claude Code accepteert de instelling skillOverrides vier statussen per skill, on, name-only (de naam zonder de beschrijving), user-invocable-only en off, en het commando /skills schrijft ze voor je weg in .claude/settings.local.json. Bij Codex is dat een blok in ~/.codex/config.toml:
[[skills.config]]
path = "/chemin/vers/le/skill/SKILL.md"
enabled = falseDezelfde skill laden en verspreiden in Claude Code
De map verandert niet. Twee locaties, afhankelijk van of de skill je overal volgt of bij de repository hoort:
cp -R regex-verifiee ~/.claude/skills/ # voor al je projecten
cp -R regex-verifiee mon-projet/.claude/skills/ # geversioneerd met de repositoryDe documentatie van Claude Code voegt frontmatter-velden toe die niet in de specificatie staan. disable-model-invocation: true voorkomt automatische activering en beperkt de skill tot een handmatige aanroep via /nom, de juiste instelling voor alles wat pusht, deployt of verwijdert. allowed-tools kent toolpermissies toe voor alleen de gespreksbeurt die de skill aanroept, en de permissie vervalt weer bij het volgende bericht. user-invocable: false reserveert de skill voor het model.
Om de skill naar een team te verspreiden, is de verpakking een plugin: een manifest .claude-plugin/plugin.json in de root, de skill in de map skills/, en een bestand .claude-plugin/marketplace.json dat de marketplace declareert. De installatie verloopt vervolgens via /plugin marketplace add compte/depot en daarna /plugin install mon-plugin@ma-marketplace, en /reload-plugins herlaadt zonder de sessie te verlaten.
Rest de vraag die pijn doet. Activeert je skill wel, en levert hij iets op? Claude Code beantwoordt beide helften. /skill-doctor, dat versie 2.1.252 of nieuwer vereist, somt de geladen skills op, hun aantal aanroepen en hun laatste gebruik, en signaleert die welke nooit hebben gediend, het rapport opent in het tabblad Stats van de pluginmanager, en wordt in modus -p als platte tekst afgedrukt. En claude plugin eval, in early access, automatiseert precies de vergelijking die ik hierboven met de hand heb gemaakt. De hulp van het commando is op deze machine ondubbelzinnig:
$ claude plugin eval --help
Run eval cases (evals/**/case.yaml or evals/**/prompt.md + graders/*.md) against
a plugin and report scored results.
--ablation <mode> Run a no-plugin baseline arm and report the score delta
(none | with-without; default: with-without …)
--runs <n> Override per-case runs (default: case.runs ?? 3)
--threshold <0..1> Exit 1 if any case score is below this threshold
--json Emit aggregate-result.json to stdout (for CI)Een evaluatiegeval is een map evals/<cas>/ met daarin een prompt.md, een realistische prompt die de skill vooral niet bij naam noemt, en correctors in graders/. De optie --ablation with-without speelt elk geval opnieuw af met en zonder de plugin en toont het scoreverschil: dat is de enige eerlijke manier om te bewijzen dat een skill iets oplevert.
Een skill delen, en die van anderen controleren
Drie distributiekanalen bestaan naast elkaar. Een kale Git-repository, die je in de juiste map kloont, het eenvoudigst en het best controleerbaar. Een plugin, voor zowel Claude Code als Codex: bij Codex leeft elke plugin onder plugins/<nom>/ met een verplicht manifest .codex-plugin/plugin.json en optionele mappen skills/, .app.json, .mcp.json. En de publieke marketplaces: ClawHub, Skills.sh, SkillsMP.
Merk terzijde op dat de catalogus github.com/openai/skills, nog overal aangehaald, als verouderd gemarkeerd staat en naar github.com/openai/plugins verwijst. De systeemskill $skill-installer installeert nog altijd in $CODEX_HOME/skills/<nom>.
Het derde kanaal vraagt om wantrouwen. De ToxicSkills-audit die Snyk op 5 februari 2026 publiceerde doorzocht 3.984 skills van ClawHub en skills.sh: 1.467 daarvan (36,82%) vertonen minstens één beveiligingsgebrek, en 534, 13,4% van het totaal, minstens één kritiek gebrek. Zesenzeventig kwaadaardige payloads zijn door menselijke controle bevestigd: diefstal van inloggegevens, installatie van een achterdeur, exfiltratie van data, acht van die skills stonden op de dag van publicatie nog altijd online. Alle bevestigde payloads bevatten kwaadaardige code, en 91% daarvan voegt ook nog prompt-injectie toe.
Een skill is tekst die je agent zal volgen, plus scripts die hij met jouw rechten zal uitvoeren. Lees voor je er een installeert het hele SKILL.md, lees elk bestand in scripts/, zoek naar netwerkaanroepen en in base64 gecodeerde strings, en weiger elke skill die om een API-sleutel of token vraagt. Dezelfde reflexen als voor de sandbox en de permissies van een code-agent.
Skills of MCP?
Skills en MCP beantwoorden niet dezelfde behoefte. Een skill levert een procedure en knowhow, in Markdown, zonder proces, zonder netwerk, zonder authenticatie. Een MCP-server levert tools en data: hij praat met een database, een API, een extern bestandssysteem, met de bijbehorende inloggegevens.
De snelste test past in één zin. Als je behoefte zich laat schrijven als “doe het zo”, is het een skill. Schrijft hij zich als “haal dit op” of “schrijf dat ergens weg”, dan is het een MCP-server. Het kostenverschil loopt langs dezelfde lijn: een inactieve skill kost zijn beschrijving, een paar tientallen tokens, terwijl een aangesloten MCP-server permanent de definitie van al zijn tools kost, bij elke sessie.
Beide combineren heel goed: een skill die zegt in welke volgorde je de tools van een MCP-server aanroept, is vaak het beste van twee werelden. Dat is trouwens de richting die het protocol zelf inslaat, de revisie 2026-07-28 van de MCP-specificatie vermeldt een werkgroep “Skills over MCP”, die tot doel heeft gestructureerde instructies via MCP te ontdekken en te gebruiken. Voor het serverdeel neemt de gids over de MCP-server in PHP het over.
De vier fouten die steeds terugkomen
Een beschrijving die de skill beschrijft in plaats van te zeggen wanneer je hem gebruikt. “Hulp bij regex” wordt nooit geactiveerd. Schrijf de woorden die de gebruiker typt, in zijn taal, en zet ze vooraan, vanwege de afkapping die hierboven is gemeten.
Een SKILL.md van tweeduizend regels. De hele hoofdtekst komt bij activering in de context terecht. De specificatie raadt aan onder de 500 regels te blijven en de details naar references/ te verplaatsen, alleen geladen indien nodig. In de test hierboven opende Codex cas-types.md pas op het moment dat hij zijn gevallen schreef.
Geheimen in de skill. Een skill wordt gedeeld, geversioneerd, gepubliceerd. Een API-sleutel die erin blijft hangen, komt in een publieke repository terecht. De skill leest een omgevingsvariabele, hij bevat hem niet.
Skills opstapelen “voor het geval dat”. Elke geïnstalleerde skill verkort de beschrijving van alle andere en knabbelt aan het contextvenster. Inventariseer wat nooit is geactiveerd en verwijder het.
Wat je moet onthouden
- Een skill = een map + een
SKILL.md(namevan hoogstens 64 tekens,descriptionvan hoogstens 1.024) + wat je er verder bij wilt. - Alleen de beschrijving wordt permanent geladen, en ze wordt afgekapt: de triggerwoorden horen in de eerste 90 tekens.
.agents/skillsaan projectkant, plus.claude/skillsgelezen door Copilot en OpenCode: eenzelfde map bedient meerdere agents.- Een projectskill test je zonder iets bij jezelf te installeren:
<projet>/.codex/skills/voor Codex,<projet>/.claude/skills/voor Claude Code. - Een goede skill legt een verifieerbare procedure en een exitcode op, geen stijlinstructie.
- Lees elke skill die je van een marketplace installeert helemaal door: 13,4% van de skills die Snyk auditeerde, draagt een kritiek gebrek.
Veelgemaakte fouten
references/, dat de agent alleen opent als hij het nodig heeft.scripts/ bevat code die hij met jouw rechten zal uitvoeren. Lees alles vooraf, ook de scripts.

