Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
423 changes: 289 additions & 134 deletions .claude/skills/create-skill/SKILL.md

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions .claude/skills/create-skill/assets/skelet-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: <naam>
description: <welke kennis of stijl de skill draagt> Gebruik wanneer <triggerzinnen>. LET OP — <exclusion-clause>.
allowed-tools: Read Grep Glob
metadata:
ceda-id: ceda.<naam>
ceda-version: "0.1.0"
ceda-type: reference
ceda-subtype: <knowledge | presentation>
ceda-origin: <own | extended | external>
ceda-upstream: ""
ceda-source: <self | pad | url | intern:vindplaats>
ceda-activation: ambient
ceda-binding: <default | suggestie>
ceda-execution: inline
ceda-scope: <org | project>
ceda-verifies: observable
---

# <Naam in gewone taal>

<Eén alinea: welke kennis dit is, en wat de lezer ermee moet doen vóór hij iets voorstelt.>

## Wanneer dit geldt

<Situatie waarin deze kennis relevant is. Geen procedure om te draaien.>

## <Inhoudelijke kop die het feit noemt>

<De kennis zelf. Kies de vorm bij het onderwerp — zie references/vorm-patronen.md.>

## Important

- <Wat er misgaat als je dit negeert>
- <Waar deze kennis níet geldt>
43 changes: 43 additions & 0 deletions .claude/skills/create-skill/assets/skelet-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: <naam>
description: <wat de skill doet> Gebruik wanneer <triggerzinnen>. LET OP — <exclusion-clause>.
allowed-tools: <Read Grep Glob Write Edit Bash>
metadata:
ceda-id: ceda.<naam>
ceda-version: "0.1.0"
ceda-type: workflow
ceda-subtype: ""
ceda-origin: <own | extended | external>
ceda-upstream: ""
ceda-source: <self | pad | url | intern:vindplaats>
ceda-activation: command
ceda-binding: default
ceda-execution: inline
ceda-scope: <org | project>
ceda-verifies: <measurable | observable>
---

# <Naam in gewone taal>

<Eén alinea: wat dit doet en wat de lezer moet doen vóór hij handelt.>

## Workflow

When the user invokes `/<naam> [optional: argument]`:

### 1. <Stap>

### 2. <Stap>

### N. Bevestig en voer uit

<Toon wat er gaat gebeuren, wacht op akkoord.>

## Verificatie

<Het commando met de drempel, of de checklist.>

## Important

- <Wat er misgaat als je dit negeert>
- <Wat deze skill niet doet, en wat dan wel>
122 changes: 122 additions & 0 deletions .claude/skills/create-skill/references/description-schrijven.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# De description schrijven — en de overlap oplossen

De description is wat de agent bij het starten van elke sessie in z'n systeemprompt krijgt,
van álle skills tegelijk. De rest van de skill bestaat op dat moment nog niet. De keuze "is
deze skill relevant" wordt volledig op deze ene regel gemaakt. Een uitstekende skill met een
vage description vuurt nooit.

Budget: 1024 tekens, hard begrensd door de spec — maar dat is een plafond, geen streefwaarde.

## Hoeveel triggeroppervlak heb je nodig? Kijk naar `ceda-activation`

| Activation | Wat de description moet doen | Richtlijn |
|---|---|---|
| `ambient` | vuren op iets wat niemand aankondigt: een geplakte foutmelding, een bestand dat iemand opent | het volle budget als het nodig is |
| `command`, `chained` | herkend worden als iemand `/naam` typt of de projectinstructies ernaar verwijzen | ≤400 tekens |

Bij `ambient` is onder-triggeren het grootste risico; formuleer daar bij twijfel iets te
opdringerig. Bij een aangeroepen skill werkt dat averechts: de activatie is al geregeld, en
elk extra woord concurreert alleen nog met de descriptions van álle andere skills.

De validator waarschuwt boven de 400 tekens bij `command` of `chained`.

## Wat er niet in hoort

Mechaniek. Concreet: bestemmingen en paden (`data/<datum>/<repo>/`), repo- en bestandsnamen,
tool- en stapnamen, vlaggen, bestandsformaten, het interne schema.

De toets: **verandert dit zonder dat de trigger verandert, dan hoort het in de body.** Verhuist
de output morgen naar een andere repo, dan is dat geen reden om de description aan te raken —
staat het pad erin, dan is het dat wel, en betaal je die regel intussen in élke sessie.

De validator waarschuwt op paden, bestandsnamen, `--vlaggen` en `<placeholders>` in de
description.

## Vier eisen

1. **Derde persoon, met echte triggers.** Niet wat de skill *is* maar wanneer hij *aan moet*.
Neem de woorden op die de gebruiker letterlijk typt — inclusief productnamen, foutmeldingen
en systeemnamen. `surf-sdp-helm-flux` doet dit goed: hij noemt SDP, Harbor, cr.surf.nl,
FluxCD, HelmRepository, HelmRelease én "even if they only paste a pipeline log or kubectl
output without an explicit question". Dat laatste is de belangrijkste zin: mensen plakken
een fout, ze stellen geen vraag.
2. **Een exclusion-clause.** Zie hieronder — er zijn twee vormen en de tweede wordt vaak
vergeten.
3. **Taal: volg de gebruiker.** Schrijf in de taal waarin de triggers gesteld zijn. Twee
varianten alleen als het team het onderwerp echt in twee talen benoemt
(`issue`/`melding`, `deployen`/`uitrollen`). Een verzonnen vertaling naast elke term kost
budget en vuurt nergens op.
4. **Niet tijdsgebonden.** Geen "de nieuwe manier om…" — dat veroudert stil.

Bij twijfel — en alleen bij `ambient` — iets te opdringerig formuleren. Onder-triggeren is
daar in de praktijk vaker het probleem, en een skill die te vaak afgaat merk je meteen.

## De exclusion-clause heeft twee vormen

**(a) Verwijzend — een andere skill hoort hier te vuren.**

> LET OP — als het doel is te sorteren en de top-N te selecteren, gebruik dan
> `voorspellen-ranking`.

**(b) Begrenzend — geen enkele skill hoort hier te vuren.**

Dit is de vorm die het vaakst ontbreekt, en hij is belangrijker. Een skill met scherpe
triggers vuurt ook op het buurdomein, en dan geeft hij advies dat dáár aantoonbaar fout is.

`surf-sdp-helm-flux` is het voorbeeld: hij triggert op Helm, Flux, HelmRelease en geplakte
`kubectl`-output. Wie een gewone Kubernetes-opstelling debugt krijgt dan SDP-specifieke
antwoorden — de `+`↔`_` OCI-tagtruc, `cr.surf.nl`-authenticatie, de Protected-vlag op GitLab
runners. Alle drie kloppen ze niet buiten SDP. Wat er hoort te staan:

> LET OP — niet voor Helm of Flux buiten het SURF SDP-platform. Zonder Harbor/cr.surf.nl en
> een GitLab-SDP-pipeline gelden deze conventies niet; val dan terug op generieke
> Helm/Flux-kennis.

Vuistregel: kan iemand met een *vergelijkbaar maar ander* systeem deze skill per ongeluk
binnenhalen? Dan hoort vorm (b) erin, met de aannames die dan wegvallen.

**De validator toetst deze twee vormen, en alleen deze twee.** Vorm (a) telt pas als de clause
de andere skill daadwerkelijk *noemt* — in backticks of als `skill <naam>`. "Gebruik dan deze
skill" wijst naar zichzelf en verbreedt de trigger in plaats van hem te begrenzen; dat is geen
clause. Vorm (b) telt pas met een scope erachter: `niet voor X`, niet een kale "niet
gebruiken". Reden: met losse trefwoorden matchte de check ook op "buiten**wereld**" en op
"fill in the template **instead** of leaving it blank", en verzweeg hij daardoor het enige
paar in de collectie met een byte-identieke description.

## Vorm

```
<Wat de skill doet, één zin, derde persoon.> Gebruik wanneer <expliciete triggerzinnen,
systeemnamen, foutmeldingen — ook als iemand alleen output plakt zonder vraag>. LET OP —
<verwijzend of begrenzend, of allebei>.
```

## De overlap-check

Draai deze stap altijd, ook als de nieuwe skill duidelijk uniek voelt.

```bash
grep -h "^description:" .claude/skills/*/SKILL.md
```

Bepaal per bestaande skill of hij triggerwoorden deelt met de nieuwe. Vier of meer gedeelde
inhoudswoorden is genoeg om onbetrouwbaar te worden. `scripts/validate-skill.py
.claude/skills` doet deze vergelijking machinaal en waarschuwt per paar; woorden die in meer
dan ~12% van alle descriptions voorkomen ("maak", "levert", "gebruik") gooit hij eerst weg,
want die dragen geen triggersignaal.

**Bij overlap verander je twee descriptions, niet één.** De nieuwe skill krijgt een clause die
naar de bestaande wijst, en de bestaande krijgt er een die terugwijst — in dezelfde PR. Doe je
dat niet, dan groeit de collectie en verslechtert de activatie tegelijk.

Bekende overlappen in de huidige collectie, bruikbaar als testgeval:
`vormgever-npuls-huisstijl` / `-2`, `generate_slides_retro` / `generate-slides-retro-simple`,
`write-issue` / `write-issue-cowork`.

## Toetsen voor je verder gaat

- Zou deze description vuren op elk van de triggerzinnen uit het interview? Zo nee, welk woord
ontbreekt?
- Zou hij vuren op een zin die bij een andere skill hoort, of op een buurdomein waar de
aannames niet gelden? Dan de exclusion-clause verscherpen.
- Staat er een woord in dat alleen jij gebruikt en de gebruiker nooit typt? Vervangen.
106 changes: 106 additions & 0 deletions .claude/skills/create-skill/references/frontmatter-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Frontmatter — spec-conform, met CEDA-metadata

De Agent Skills-specificatie kent **zes** frontmatter-velden. Meer bestaat er niet; eigen
velden horen onder `metadata:`, een map van string naar string. Alles wat wij extra willen
weten staat daar, met een `ceda-`-prefix zodat het niet botst met dat van iemand anders.

```yaml
---
name: check-style
description: <het enige veld dat activeert — zie references/description-schrijven.md>
allowed-tools: Read Grep Glob
compatibility: Requires uv and ruff # alleen als de skill echt iets nodig heeft
metadata:
ceda-id: ceda.check-style
ceda-version: "1.2.0"
ceda-type: reference # workflow | reference | connector
ceda-subtype: knowledge # alleen bij reference: knowledge | presentation
ceda-origin: own # external | extended | own
ceda-upstream: "" # verplicht bij origin: extended — een SKILL
ceda-source: docs/ceda-python.md # self | pad | url | intern:<vindplaats>
ceda-activation: ambient # ambient | command | hook | scheduled | chained
ceda-binding: default # hard | default | suggestie
ceda-execution: inline # inline | isolated | deterministic
ceda-scope: org # org | project (user is een laag, geen scope)
ceda-verifies: measurable # measurable | observable | none
---
```

## De zes spec-velden

| Veld | Verplicht | Regels |
|---|---|---|
| `name` | ja | 1-64 tekens, alleen `a-z`, `0-9` en `-`. Niet beginnen of eindigen met een streepje, geen dubbele streepjes, en **gelijk aan de directorynaam**. Underscores zijn ongeldig. |
| `description` | ja | 1-1024 tekens. Wat de skill doet én wanneer je 'm gebruikt, met de woorden waarop hij moet vuren. |
| `allowed-tools` | nee | Een **spatie-gescheiden string**, geen YAML-lijst: `Read Grep Glob` of `Bash(git:*) Read`. Experimenteel in de spec, maar Claude Code leest 'm. |
| `compatibility` | nee | Max 500 tekens. Wat de omgeving moet hebben: `Requires glab, kubectl and SDP tenant access`. Alleen invullen als het echt een eis is. |
| `license` | nee | Kort: een licentienaam of een verwijzing naar een meegeleverd bestand. |
| `metadata` | nee | Map van string naar string. Hier staat alles van ons. Geen geneste lijsten — waarden zijn strings, dus quote versienummers en gebruik `""` voor "niet van toepassing". |

## De CEDA-metadata

Expliciet vragen tijdens het interview:

| Sleutel | Beslisregel |
|---|---|
| `ceda-type` | Bevat het een stappenreeks of beslislogica? → `workflow`. Injecteert het kennis, regels of stijl? → `reference`. Levert het data of tools via een protocol? → `connector`. |
| `ceda-subtype` | Alleen bij reference. Verandert het *wat* Claude weet (`knowledge`) of *hoe* Claude formuleert (`presentation`)? |
| `ceda-origin` | Ongewijzigd overgenomen (`external`), externe basis die wij aanscherpen (`extended`, vereist `ceda-upstream`), of geen generiek equivalent (`own`). |
| `ceda-source` | Waar staat de bron van waarheid buiten de skill? Nooit leeg: leeg betekent tegelijk "geen bron" en "nog niet ingevuld". |
| `ceda-scope` | Alle CEDA-repo's (`org`) of alleen dit project (`project`). Lokaler wint bij conflict. |
| `ceda-verifies` | Een commando met een drempel (`measurable`), een checklist die iemand nakijkt (`observable`), of niets meetbaars (`none`, vereist motivatie in de body). |

Zelf invullen, alleen melden in de draft:

| Sleutel | Default | Wanneer afwijken |
|---|---|---|
| `ceda-id` | `ceda.<name>` | Nooit wijzigen na aanmaak; hij overleeft hernoemen. |
| `ceda-version` | `"0.1.0"` | Bump bij inhoudelijke wijziging. Quoten, anders is het geen string. |
| `ceda-activation` | `command` bij workflow, `ambient` bij reference | `hook` bij runtime-afdwinging, `scheduled` bij cron, `chained` als alleen een andere workflow hem aanroept. |
| `ceda-binding` | `default` | `hard` alleen als er echt een hook is die het tegenhoudt. |
| `ceda-execution` | `inline` | `isolated` als de output comprimeert, `deterministic` bij een script of hook zonder model in de lus. |

## Gebundelde bestanden staan in de body, niet in de frontmatter

Er is geen `bundles:`-veld in de spec, en een laadconditie in de frontmatter zou toch niets
doen: de agent leest de body, niet onze metadata. Zet ze dus in een sectie onderaan
`SKILL.md`, met per bestand de conditie waaronder het gelezen moet worden:

```markdown
## Gebundelde bestanden

- `references/gotchas.md` — lees altijd, voor je iets voorstelt
- `references/api-errors.md` — lees als de API iets anders dan 200 teruggeeft
- `scripts/hr-status.sh <release> <namespace>` — draaien, niet lezen: geeft een
gezondheidssamenvatting in één keer
```

"Zie references/ voor details" werkt niet — dan wordt het of altijd of nooit gelezen. Eén hop
diep: een bundle die naar een bundle verwijst is een skill die zichzelf niet meer overziet.

## Wat de validator controleert

`scripts/validate-skill.py` faalt op:

- `name` buiten de spec-regels of ongelijk aan de directorynaam
- `description` leeg of boven 1024 tekens
- `allowed-tools` als YAML-lijst in plaats van een spatie-gescheiden string
- CEDA-metadata op topniveau in plaats van onder `metadata:`
- een waarde buiten de toegestane set van `ceda-type`, `-subtype`, `-origin`, `-activation`,
`-binding`, `-execution`, `-scope`, `-verifies`
- `ceda-binding: hard` zonder `ceda-activation: hook`
- `ceda-origin: extended` zonder `ceda-upstream`
- ontbrekende `ceda-source`
- `ceda-scope: user`, of `ceda-source: self` met `ceda-scope: project`
- `ceda-subtype` op een niet-reference
- een bestand in `references/`, `assets/` of `scripts/` dat nergens in de body genoemd wordt
- `SKILL.md` boven de 500 regels zonder gebundelde bestanden
- `ceda-verifies: none` zonder motivatie in de body

Waarschuwingen: overlappende triggerwoorden zonder exclusion-clause, ontbrekende
`allowed-tools` (fout bij `ceda-origin: external`), ontbrekende `ceda-id`/`ceda-version`,
tijdsgebonden description, `ceda-source: intern:` (reviewer moet controleren of de skill
zelfstandig leesbaar is).

Draai daarnaast de referentie-validator van de spec zelf als je die hebt:
`skills-ref validate ./<skill>`.
Loading
Loading