Özet ve sonuç
Bir Claude Code skill'i adını yazdığında kusursuz çalışıp sade sözlerle istediğinde yine de hiç yüklenmeyebilir. Claude bir skill'i yüklemeye karar vermeden önce skill başına tek satır okur: adı ve açıklamayı. Bu satırı senin sözlerinle eşleştirir. Satırda senin kelimelerin yoksa skill sessiz kalır.
Bizim çalıştırmamızda, "Generates changelog documents from repository history." açıklamalı bir release-notes skill'i, "what did we ship this week?" isteğinde 5 yeni oturumun 0'ında yüklendi. O tek satırı ne yaptığı, ne zaman kullanılacağı ve bizim yazdığımız kelimeler olarak yeniden yazdık. Aynı istek bu kez 5 / 5 yükledi.
Sonuç:
- Skill, sabit çıktılı tekrarlanan bir görev için işe yarar: haftalık notlar, bir pull request açıklaması, bir sürüm kontrol listesi.
- Tek seferlik bir soru için yaramaz ve her zaman geçerli olması gereken bir kural için doğru araç değildir. O bir hook'un işidir.
- Kimsenin kullanmadığı skill yığını maliyetlidir, çünkü liste bütçesini aşınca Claude Code açıklamaları keser, en az kullanılandan başlayarak.
- Tek kural: açıklamayı, o görevi isterken gerçekten yazdığın kelimelerle yaz.
Aşağıdaki kitte skill dosyamızın öncesi ve sonrası, bir açıklama şablonu, yeni oturum kontrol listesi ve kullandığımız yirmi istekli test var.
Belgeler ve saha ne diyor
Skill nerede durur. Skill, içinde SKILL.md olan bir klasördür: ya her proje için ~/.claude/skills/<skill-name>/ içinde ya da ekibin de alması için .claude/skills/<skill-name>/ içinde. Ortada duran bir name.md dosyası yüklenmez. s1 s2
Claude gövdeyi değil, açıklamayı okur. Ad ve açıklama listesi her turda bağlamdadır, gövde yalnızca skill çağrıldığında yüklenir. Belgelere göre Claude skill'i ne zaman uygulayacağına açıklamaya bakarak karar verir. s1
Listenin bir sınırı ve bütçesi var. Açıklama ve when_to_use metni birlikte 1.536 karakterde kesilir. Listenin tamamı bağlam penceresinin %1'iyle sınırlıdır ve taştığında Claude Code açıklamaları, en az çağırdığın skill'lerden başlayarak atar. Belgelere göre bu, Claude'un isteğinle eşleştirmek için ihtiyaç duyduğu anahtar kelimeleri kaldırır. --debug ile hata ayıklama günlüğünde bir uyarı görürsün. s1
Bozuk bir başlık, başarıya benzeyen tuzaktır. YAML bozuksa gövde boş metaveriyle yüklenir, yani /skill-name hâlâ çalışır ama Claude açıklamanı eşleştiremez. Ayrıştırma hatasını görmek için --debug ile çalıştır ya da claude plugin validate .claude/skills komutunu çalıştır. s1 s2
Rozete bak. Bir skill /skills içinde görünüyor ama Claude onu hiç çağırmıyorsa, ya disable-model-invocation: true ayarlıdır ya da açıklama isteğini ifade ediş biçimine uymuyordur. Bir user-only etiketi, Claude'un onu kendi başına tetiklemeyeceği anlamına gelir. s2
İyi bir açıklama neyi içerir. Anthropic'in rehberi, açıklamanın hem skill'in ne yaptığını hem ne zaman kullanılacağını içermesini, üçüncü tekil kişiyle yazılmasını ve kullanıcıların yazdığı anahtar terimleri taşımasını söylüyor. Claude'un bunu, potansiyel olarak 100+ skill arasından doğru olanı seçmek için kullandığını da not ediyor. s3
Claude az tetikleme eğiliminde. Anthropic'in skill-creator'ı "currently Claude has a tendency to 'undertrigger' skills" diyor ve biraz "ısrarcı" bir açıklama öneriyor. s18 Tetikleme testi 20 sorgu kullanır: tetiklemesi gerekenler ile gerekmeyenlerin karışımı, en değerlileri yakın ıskalardır. Döngü bunları %60 eğitim ve %40 ayrılmış test olarak böler, her sorguyu 3 kez çalıştırır ve test puanı en iyi olan açıklamayı tutar. s4 Değerlendirme betiği her sorguyu claude -p üzerinden çalıştırır ve skill'in kullanılıp kullanılmadığına bakar. s19
Etkinleşme, anahtar kelime eşleşmesi gibi davranır. Gerçek ikili dosyada, üçüncü taraf bir test (22 prompt, Sonnet 4.5) iki çalıştırmada %55 ve %50 temel değer ölçtü, zorunlu değerlendirme hook'u ise %100. Açık bir token içeren prompt neredeyse her zaman %100 etkinleşti, dolaylı ifade ise %0 civarında kaldı. Yazarın yorumu: anlamsal eşleşmeden çok anahtar kelime eşleşmesine yakın. Bu tek bir blog, daha eski bir model ve farklı bir depo; kural değil, destekleyici kanıt say. s5
Açıklama bir tetikleyicidir, özet değil. Dokuz gerçek skill'in denetimi aynı sonuca vardı: somut durumları sıralayan bir açıklama, skill'in ne olduğunu söyleyen bir açıklamayı yener. s8
Tersi hata da var. Bir geliştirici bir skill'in yanlış görevlerde tetiklendiğini izledi, tetikleme değerlendirmeleri yazdı, açıklamayı "Load when..." olarak yeniden yazdı ve tutmaya değer bir skill'in genellikle iki kez görmekten bıktığın bir hatadan doğduğunu not etti. s11
Başkaları da buna takıldı. "Claude Code not aware of skills in .claude/skills" başlıklı issue'nun 75 tepkisi var ve 2026-10-17 itibarıyla hâlâ açıktı. Başlıktaki geçici çözümler arasında bir CLAUDE.md işaretçisi ve bir hook var. s7
Hook'un skill'i yendiği yer. 778 oy alan bir başlıktaki en iyi cevap, skill'in "basically just a prompt you save for later" olduğunu, Claude'un kullanmaya karar verdiğini ya da senin eğik çizgi komutuyla çağırdığını, skill'ler fazla gevşek kalıp determinizm istediğinde ise hook'un işe yaradığını söylüyor. s9 Başka bir başlık, hatırlatma dürtmelerinin uzun bir oturumda sönebileceği uyarısını yapıyor. s10
Kit
Skill'in öncesi ve sonrası. Yalnızca açıklama satırı farklıdır, gövde aynıdır.
release-notes.before.SKILL.md
---
name: release-notes
description: Generates changelog documents from repository history.
---
# Release notes
1. Find the range: commits since last Monday (`git log --since="last monday" --no-merges --pretty="%h %s"`).
2. Drop `chore` and `docs` commits.
3. Group what is left by the conventional-commit scope (router, css, jsx, serve-static...), fixes and features apart.
4. Write one line per change, in plain English, present tense, no commit hashes, with the PR number at the end.
5. Output a markdown block titled "Release notes, week of <date>", with a two-line summary on top.
release-notes.before.SKILL.md on GitHub
release-notes.after.SKILL.md
---
name: release-notes
description: Writes this week's release notes from git log, grouped by scope (router, css, jsx). Use when the user asks what we shipped, what changed this week, for release notes, a changelog, or a summary of recent commits.
---
# Release notes
1. Find the range: commits since last Monday (`git log --since="last monday" --no-merges --pretty="%h %s"`).
2. Drop `chore` and `docs` commits.
3. Group what is left by the conventional-commit scope (router, css, jsx, serve-static...), fixes and features apart.
4. Write one line per change, in plain English, present tense, no commit hashes, with the PR number at the end.
5. Output a markdown block titled "Release notes, week of <date>", with a two-line summary on top.
release-notes.after.SKILL.md on GitHub
Açıklama şablonu, çalışılmış örnek olarak gerçek açıklamamızla.
description-template.md
# Skill description template
The description is the one line Claude reads before it decides to load your skill. Write it for the moment you ask, not for the file.
## Template
```
<Verb in third person> <what the skill produces>, <how it is shaped>. Use when the user asks <phrase 1>, <phrase 2>, <phrase 3>, or <phrase 4>.
```
Three parts, in this order:
1. What it does. Third person ("Writes", never "I can" or "You can use this"). Name the output.
2. When to use it. Start with "Use when the user asks".
3. Your words. The exact phrases you or your team type, copied from your own history, not synonyms you invented.
Put the key use case first. The description and `when_to_use` are cut together at 1,536 characters in the skill listing.
## Worked example (the real one from our run)
Before, a summary of the skill:
```
Generates changelog documents from repository history.
```
After, what + when + the words we type:
```
Writes this week's release notes from git log, grouped by scope (router, css, jsx). Use when the user asks what we shipped, what changed this week, for release notes, a changelog, or a summary of recent commits.
```
What changed:
- What: "Writes this week's release notes from git log, grouped by scope".
- When: "Use when the user asks ...".
- Your words: "what we shipped", "what changed this week", "release notes", "a changelog", "a summary of recent commits".
## Fill-in sheet
- Skill name:
- The prompt I keep retyping (copy it from your history):
- The output I want from it:
- Verb + output (what it does):
- Four phrases I really type (when):
- Phrases that sound close but are a different job (for your near misses):
## Check before you save
- Third person, no "I" and no "you".
- At least three phrases you really type.
- Under 1,536 characters in total.
- Not so wide that it matches a neighbour job. Add the near misses to your trigger test.
description-template.md on GitHub
Yeni oturum kontrol listesi.
trigger-checklist.md
# Fresh-session trigger checklist
A skill that runs by name can still be invisible to a plain request. Test it the way a teammate will use it.
## Test in a fresh session
1. Open a new session. A skill already used stays in the conversation, so a test in the same session proves nothing.
2. Ask for the task in your own words, without the skill's name. Example: "what did we ship this week?"
3. Watch for the Skill tool call before Claude touches anything else. If Claude runs the commands itself and writes its own summary, the skill was skipped.
4. Repeat in several fresh sessions. A handful of runs is a demo, not a statistic.
## If it was skipped, read why before you edit
1. `/skills`: is it listed, is it switched on, is there a `user-only` badge? A `user-only` badge means Claude will not trigger it on its own.
2. Ask "What skills are available?" and check your skill is in the answer.
3. `claude --debug`, then search the log for the skills line (how many loaded) and for a "Skill listing over budget" warning. Over budget means some descriptions are cut, the least invoked first.
4. `claude plugin validate .claude/skills`: a malformed header still lets `/name` work but leaves Claude with empty metadata to match against.
5. Check the file is `.claude/skills/<name>/SKILL.md`, not a loose `<name>.md`, and that the header starts on the first line.
## Fix, then prove it
1. Rewrite the description with `description-template.md`.
2. Fresh session, same plain request, several runs: does the Skill call come first now?
3. Baseline: set the skill to `"off"` in `skillOverrides` (saved to `.claude/settings.local.json` from `/skills`), ask again, then switch it back on.
## Hold it
1. Write twenty requests: ten that should load the skill, ten near misses (`evals.json` is our set).
2. Run each in its own fresh session and note whether the skill loaded (`trigger_test.py`, `tally.py`).
3. Re-run the list every time you change the description.
4. For a skill that must never miss: call it by name, or add a UserPromptSubmit hook (`hook.json`).
trigger-checklist.md on GitHub
Yirmi istekli test seti: skill'i yüklemesi gereken on istek, yüklememesi gereken on yakın ıska. Sorguları kendi depona göre düzenle.
evals.json
[
{"query": "what did we ship this week?", "should_trigger": true},
{"query": "can you put together the release notes for this week", "should_trigger": true},
{"query": "what changed in the repo since monday", "should_trigger": true},
{"query": "write the changelog for the commits we merged this week", "should_trigger": true},
{"query": "I need a summary of this week's commits for the team update on friday", "should_trigger": true},
{"query": "draft the weekly notes of what got fixed, grouped by area", "should_trigger": true},
{"query": "list what landed this week, router and css changes apart", "should_trigger": true},
{"query": "give me something to post in the team channel about this week's fixes", "should_trigger": true},
{"query": "recap the work merged since last monday", "should_trigger": true},
{"query": "what's new in hono this week? short version for the release post", "should_trigger": true},
{"query": "explain what commit 08807ce1 changes in the trie router", "should_trigger": false},
{"query": "the jsx tests are failing on cloneElement, can you fix them", "should_trigger": false},
{"query": "write a commit message for my staged changes", "should_trigger": false},
{"query": "who wrote most of the serve-static code?", "should_trigger": false},
{"query": "add a CHANGELOG entry format section to CONTRIBUTING.md", "should_trigger": false},
{"query": "show me the diff between main and my branch", "should_trigger": false},
{"query": "bump the version in package.json to the next patch", "should_trigger": false},
{"query": "why does the css helper minify url() like that?", "should_trigger": false},
{"query": "squash my last three commits into one", "should_trigger": false},
{"query": "review this PR for breaking changes before I merge it", "should_trigger": false}
]
Her isteği kendi yeni claude -p oturumunda çalıştıran ve Skill aracının çağrılıp çağrılmadığını sayan betik.
trigger_test.py
"""Twenty-prompt trigger test for one skill, the method skill-creator describes, run on the real skill folder.
For each query: a fresh `claude -p` in the repo; the run counts as loaded if the Skill tool is called with the skill's name
before the run ends (stopped early as soon as it is). Usage:
python3 trigger_test.py <repo> <skill-name> <SKILL.md to install> <evals.json> <out.json> [workers]"""
import json, os, shutil, subprocess, sys
from concurrent.futures import ThreadPoolExecutor
repo, name, skill_md, evals_path, out = sys.argv[1:6]
workers = int(sys.argv[6]) if len(sys.argv) > 6 else 4
dest = os.path.join(repo, '.claude', 'skills', name)
os.makedirs(dest, exist_ok=True)
shutil.copy(skill_md, os.path.join(dest, 'SKILL.md'))
evals = json.load(open(evals_path))
allowed = ['Bash(rtk git log *)', 'Bash(git log *)', 'Bash(git show *)', 'Bash(date *)', 'Skill', 'Read']
def run(item):
cmd = ['claude', '-p', item['query'], '--output-format', 'stream-json', '--verbose', '--allowedTools', *allowed]
p = subprocess.Popen(cmd, cwd=repo, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)
loaded = False
try:
for line in p.stdout:
try:
d = json.loads(line)
except ValueError:
continue
if d.get('type') == 'assistant':
for c in d['message'].get('content', []):
if c.get('type') == 'tool_use' and c['name'] == 'Skill' and name in json.dumps(c.get('input', {})):
loaded = True
if loaded or d.get('type') == 'result':
break
finally:
if p.poll() is None:
p.kill()
return {**item, 'loaded': loaded, 'pass': loaded == item['should_trigger']}
with ThreadPoolExecutor(workers) as ex:
results = list(ex.map(run, evals))
should = [r for r in results if r['should_trigger']]
shouldnt = [r for r in results if not r['should_trigger']]
summary = {'loaded_when_it_should': sum(r['loaded'] for r in should), 'of_should': len(should),
'loaded_when_it_should_not': sum(r['loaded'] for r in shouldnt), 'of_should_not': len(shouldnt),
'passed': sum(r['pass'] for r in results), 'total': len(results)}
json.dump({'skill': name, 'skill_md': skill_md, 'summary': summary, 'results': results}, open(out, 'w'), indent=2)
for r in results:
print(f"[{'PASS' if r['pass'] else 'FAIL'}] {'loaded ' if r['loaded'] else 'skipped'} expected={'load' if r['should_trigger'] else 'skip'}: {r['query']}")
print(json.dumps(summary))
claude -p --output-format stream-json çalıştırmalarının bir klasörü için küçük bir sayaç.
tally.py
"""Count whether a `claude -p --output-format stream-json` run loaded a given skill. Usage: python3 tally.py <skill> run1.jsonl [run2.jsonl ...]"""
import json, sys
skill, files = sys.argv[1], sys.argv[2:]
fired = 0
for f in files:
hit = False
for line in open(f):
try:
d = json.loads(line)
except ValueError:
continue
if d.get('type') == 'assistant':
for c in d['message'].get('content', []):
if c.get('type') == 'tool_use' and c['name'] == 'Skill' and skill in json.dumps(c.get('input', {})):
hit = True
fired += hit
print(f"{f}: {'loaded' if hit else 'skipped'}")
print(f"{skill}: loaded in {fired} of {len(files)} runs")
Tetiklenmesi şart olan durumlar için yedek: skill'i adıyla anan bir UserPromptSubmit hook'u.
hook.json
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "echo 'If the user asks about release notes, a changelog or what shipped this week, use the release-notes skill.'"
}
]
}
]
}
}
Pazartesi yap
- Geçen hafta en çok yeniden yazdığın prompt'u ve ondan istediğin çıktıyı tam olarak yaz.
- Bir ad, bir açıklama ve kısa numaralı bir gövdeyle
.claude/skills/<name>/SKILL.mdoluştur. - Adıyla test et, sonra yeni bir oturum aç ve görevi adı olmadan sade sözlerle iste.
- Atlanırsa, herhangi bir şeyi düzenlemeden önce
/skills,claude --debugveclaude plugin validate .claude/skillsçıktılarını oku. - Açıklamayı
description-template.mdile yeniden yaz: ne yapıyor, ne zaman kullanılır, yazdığın kelimeler. - Birkaç yeni oturumda yeniden dene, sonra bir kez
skillOverridesiçinde skill"off"iken ve bir kez tekrar açıkken dene. - Yirmi isteğini yaz,
trigger_test.pyile çalıştır ve açıklamayı her değiştirdiğinde yeniden çalıştır. - Asla ıskalamaması gereken bir skill için karar ver: adıyla çağır ya da hook ekle.
Ne ölçtük
Bunlar, açık kaynaklı Hono deposunun (s20) bir geçici kopyasında, Claude Code 2.1.294 ile ve 2026-10-12 Pazartesi'den beri 8 birleştirme dışı conventional commit üzerinde "what did we ship this week?" isteğiyle yapılmış küçük demo çalıştırmalarıdır. Bir kıyaslama değildir. Senin modelin, skill'lerin ve deponun sayıları oynatır.
| Test | Açıklama | Sonuç |
|---|---|---|
| Sade istek, 5 yeni oturum | Özet: "Generates changelog documents from repository history." | 5'in 0'ında yüklendi |
| Sade istek, 5 yeni oturum | Yeniden yazılmış: ne + ne zaman + bizim kelimelerimiz | 5'in 5'inde yüklendi |
Adıyla çağrıldı (/release-notes) |
Özet | 2'nin 2'sinde çalıştı |
skillOverrides kapalı, sonra açık |
Yeniden yazılmış | Kapalı: atlandı. Açık: yüklendi |
| Yirmi istekli test, yeniden yazımdan önce | Özet | Yüklemesi gereken 10 istekten 4'ü yükledi, 10 yakın ıskadan 0'ı, 20'den 14'ü geçti |
| Yirmi istekli test, yeniden yazımdan sonra | Yeniden yazılmış | Yüklemesi gereken 10 / 10, yakın ıska 0 / 10, 20 / 20 geçti |
| Hook artı eski açıklama | Özet | 1'in 1'inde yüklendi |
Yeniden yazımdan önce yükleyen 4 istek "release notes", "changelog", "notes" ya da "release" içerenlerdi: anahtar kelime etkisi apaçık ortada. Yeniden yazımdan sonra daha geniş açıklama tek bir yakın ıskayı bile içeri çekmedi.
Bulduğumuz uyarı ve dürüst okuma. --debug ile kurulumumuz şunu yazdı: "Skill listing over budget: 296 skills, 144833 chars > 30000 budget". Kurulumumuzda çok fazla skill var, bu yüzden açıklamalar kesiliyor. Proje skill'imiz yine de yüklendi ("Loaded 43 unique skills", biri proje skill'i) ve eski açıklama kesintiden kurtuldu: yirmi istekli testte, yalnızca açıklamada geçen "changelog" kelimesini içeren istek skill'i yükledi. Bütçenin genel olarak yeni bir skill'e nasıl davrandığını kanıtlamadık. Çok sayıda skill taşıyorsan kontrolü kendin yap. Kurulumumuzda /skills 277 skill listeledi ve release-notes proje skill'i olarak, açık ve user-only rozetsiz görünüyordu.
Aracın kendisi hakkında bir not: Anthropic'in değerlendirme betiği skill'i bizim sürümümüzde hiç görmedi, bu yüzden aynı istekleri kitteki kısa betikle çalıştırdık. Yöntem aynı: istek başına bir yeni claude -p oturumu.
Daha ileri
Ters sorunu da düzelt. Çok fazla şeyle eşleşen bir açıklama skill'in olmaması gereken yerde tetiklenmesine yol açar. Test setinin yarısı bu yüzden yakın ıskadır. Aklına gelen her kelimeyi değil, gerçekten kullandığın kelimeleri ekle. s11
Yeniden yazımı optimize edici önersin. Skill-creator'ın döngüsü yeni açıklamalar önerir ve ayarladığın sorgulara aşırı uymayı önlemek için ayrılmış test puanına göre en iyisini tutar. Değerlendirme seti olarak kendi yirmi isteğini kullan. s4
Özlü bir test prompt'u kullan. Skill-creator, Claude'un skill'lere yalnızca kendi başına kolayca halledemeyeceği görevlerde başvurduğunu belirtiyor; bu yüzden tek adımlık önemsiz bir istek, kusursuz eşleşmede bile skill tetiklemeyebilir. s4
Düz hook'un ötesine geç. Yukarıdaki üçüncü taraf testi, Claude'a her skill'i evet ya da hayır diye saydırıp sonra etkinleştirten zorunlu değerlendirme hook'unun, pasif bir hatırlatmanın yapamadığı %100'e ulaştığını ve yerel bir type: "prompt" hook'unun hook yokmuş gibi davrandığını buldu. Bizim hook'umuz basit sürüm ve bu tek bir demo çalıştırması. s5 Raporlara göre hook dürtmeleri uzun bir oturumda sönebilir. s10
Gövdeyi kısa tut. Yukarıdaki denetimde bozuk tek skill 1.002 satırdı ve her yerde MANDATORY vardı, sağlıklı olanlar kısaydı. s8
Neyi dışarıda bıraktık. Yalnızca eklenti yazarlarını ilgilendiren eklenti skill'lerini ve eklenti değerlendiricilerini, ya da bir skill'i aylar boyunca çıplak modelle karşılaştırmayı ele almadık. Bazıları ayrıca daha zayıf modeller için yazılmış skill'lerin ölü yüke dönüşebildiğini bildiriyor, o yüzden arada bir yeniden test et. Bunu ölçmedik.
SSS
/release-notes neden çalışıyor da sade sözler çalışmıyor?
Adı yazmak eşleşmeyi atlatır. Sade sözlerde Claude'un eşleştirmek için yalnızca adı ve tek açıklama satırı vardır, bu yüzden isteğinle ortak kelimesi olmayan bir özet atlanır. s1
Bir skill'i düzenledikten sonra Claude Code'u yeniden başlatmam gerekir mi?
Hayır. Claude Code, ~/.claude/skills/ ya da .claude/skills/ altında eklenen ya da düzenlenen bir skill'i mevcut oturumda alır. Oturum başladığında var olmayan bir üst düzey skills dizini oluşturursan /reload-skills çalıştır. s1
Açıklama ne kadar uzun olabilir?
Açıklama ve when_to_use listede birlikte 1.536 karakterde kesilir, o yüzden ana kullanım durumunu başa koy. Anthropic'in platform rehberi ayrıca Claude Code dışında yüklenen skill'ler için açıklamayı 1.024 karakterle sınırlar. s1 s3
Bir düzeltmeye güvenmek için beş yeni oturum yeter mi? Hayır. Yönü gösterir. Sonucumuz tek bir depoda bir demo, bu yüzden kit, açıklamayı her değiştirdiğinde yeniden çalıştırabileceğin yirmi istekli bir testle biter.
Skill yerine ne zaman hook kullanmalıyım? Bir kural yalnızca Claude bir eşleşme fark ettiğinde değil, her seferinde geçerli olmalıysa. Hook her mesaja bir satır ekler, o yüzden tetiklenmesi şart olan birkaç skill için sakla. s9
AIDive