Claude Code hooks: ajan kurallarını garantiye nasıl dönüştürürsünüz
Claude Code hooks, Claude Code'un belirli olaylarda kendisinin çalıştırdığı shell komutlarıdır: araç çağrısından önce ve sonra, prompt gönderildiğinde, oturum başlarken ve ajan durmadan hemen önce. Settings.json içinde tanımlanırlar; çıkış kodu 2 eylemi engeller ve ajana hata mesajı döndürür. Bu sayede CLAUDE.md'deki bir kural, model tarafından unutulamayacak bir garantiye dönüşür.
Claude Code hooks nedir
Hook, komutu model değil, Claude Code programının kendisi belirli bir anda çalıştırdığı bir komuttur. Ajan rm -rf çalıştırmak istediğinde hook devreye girer ve komutun geçip geçmeyeceğine karar verir. Ajan bir dosyayı düzenlediğinde hook biçimlendiriciyi çalıştırır. Ajan işini bitirmek istediğinde hook testleri çalıştırır ve testler kırmızıyken durmasına izin vermez.
CLAUDE.md'deki talimatlardan temel farkı şudur: talimat, modelin belli bir olasılıkla dikkate aldığı bir metindir; hook ise deterministik koddur. Bağlam ne kadar uzun olursa olsun, ajan neyi "kararlaştırdıysa" ve oturumun yüzüncü dakikasında kuralı unutup unutmadığından bağımsız olarak her zaman çalışır. Bu yüzden claude code hooks, bir dileği garantiye çevirmenin temel yoludur.
Neden CLAUDE.md kuralları yetmez
Tipik bir durum: ajan bir özelliği ekledi, birim testleri yeşil, sohbette kendinden emin bir "Tamam" yazıyor. Ama uçtan uca senaryo çalıştırılmamış, linter koşulmamış, migrasyon ortada patlamış. CLAUDE.md'de ise "bitirmeden önce testleri çalıştır" yazıyor. Model bilerek yalan söylemiyor; sadece kendi işini kendisi değerlendiriyor ve bu öz değerlendirme güvenilir değil.
Buradan harness ilkesi çıkar: mekanik olarak kontrol edilebilen her şeyi model değil, mekanizma kontrol eder. "Linter'ı çalıştır" kuralı, linter'ı çalıştıran bir hook olur. "Prod yapılandırmasına dokunma" kuralı, o dosyaya yazmayı engelleyen bir hook olur. Metin talimat olarak kalır, ama artık ajana neden durdurulduğunu açıklamaktan başka bir işe yaramaz.
Olaylar: hook'lar ne zaman çalışır
Her hook, oturumun yaşam döngüsündeki bir olaya bağlıdır. Başlıca olaylar:
| Olay | Ne zaman çalışır | Tipik kullanım |
|---|---|---|
PreToolUse | araç çağrısından önce (Bash, Edit, Write vb.) | tehlikeli komutları yasaklama, dosyaları koruma |
PostToolUse | araç çağrısı başarıyla tamamlandıktan sonra | biçimlendirici, linter, düzenlemeden sonra hızlı testler |
UserPromptSubmit | kullanıcı prompt'u gönderdi, işlenmeden önce | bağlam ekleme, içinde sır olan prompt'u durdurma |
Stop | ajan yanıtı bitirmek üzereyken | kontroller geçmeden durmasına izin vermeme |
SubagentStop | bir alt ajan işini bitirirken | alt ajan sonucunu doğrulama |
SessionStart | oturum başladığında veya devam ettirildiğinde | durumu yükleme: git status, plan, ilerleme günlüğü |
SessionEnd | oturum sona erdiğinde | temizlik, log kaydı |
Notification | Claude Code bildirim gönderdiğinde (örneğin izin beklediğinde) | ses veya telefona mesaj |
PreCompact | bağlam sıkıştırılmadan önce | sıkıştırmadan önce önemli bilgileri kaydetme |
Güvenilirlik açısından en faydalı olanlar PreToolUse, PostToolUse ve Stop'tur: ajanın üç zayıf noktasını kapatırlar — tehlikeli eylemler, kontrolsüz düzenlemeler ve erken "bitti" beyanı.
Hook'lar nerede tanımlanır: settings.json
Hook'lar Claude Code'un ayar dosyalarında tanımlanır:
~/.claude/settings.json— kişisel ayarlar, tüm projeler için;- depodaki
.claude/settings.json— ekip için ortak, git'e commit edilir; .claude/settings.local.json— projeye özel yerel ayarlar, git'e girmez.
Yapı şöyledir: olay → matcher içeren grupların listesi → komutların listesi.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint.sh",
"timeout": 60
}
]
}
]
}
}
matcher, araç adı veya ona uygulanan düzenli ifadedir: Bash, Edit|Write, .*. Araçlarla ilgili olaylar için gerekir; Stop veya UserPromptSubmit için atlanabilir. CLAUDE_PROJECT_DIR değişkeni proje kökünü gösterir, bu yüzden betikleri .claude/hooks/ içinde tutup bu değişken üzerinden çağırmak uygundur. Mevcut hook'ları Claude Code içinde /hooks komutuyla görebilirsiniz.
Hook veriyi nasıl alır ve nasıl yanıtlar
Girdi: stdin'deki JSON
Claude Code hook'a olay hakkında bir JSON gönderir: oturum kimliği, çalışma dizini, olay adı ve araçlar için tool_name ile tool_input. Bash için komut tool_input.command içinde, Edit ve Write için dosya yolu tool_input.file_path içindedir. Bunu jq ile okumak kolaydır.
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf build/" },
"cwd": "/home/dev/project"
}
Çıktı: dönüş kodu
| Kod | Ne olur |
|---|---|
0 | başarı, iş devam eder; UserPromptSubmit ve SessionStart için stdout ajanın bağlamına eklenir |
2 | engelleyici hata: stderr ajana iletilir ve eylem durdurulur (PreToolUse'da araç çağrısı iptal edilir, Stop'ta ajan çalışmaya devam eder) |
| diğeri | engellemeyen hata: stderr kullanıcıya gösterilir, iş devam eder |
Çıkış kodu 2 temel mekanizmadır. Ajan stderr'deki metni geri bildirim olarak alır ve düzeltebilir: komutu yeniden yazar, testi onarır, dosyaya dokunmaz. Bu yüzden stderr'e "hata" değil, net bir neden ve yerine ne yapılacağını yazın.
Dönüş kodunun dışında hook, stdout'a "decision": "block" ve "reason" içeren bir JSON da yazabilir. Bu ince ayar içindir; çoğu iş için kod 2 ve stderr'deki mesaj yeterlidir.
Örnek 1: her düzenlemeden sonra linter ve testler
Edit|Write matcher'ına sahip bir PostToolUse hook'u dosya değiştirildikten sonra çalışır. Araç zaten uygulanmıştır ve düzenleme geri alınamaz, ama kod 2 linter çıktısını ajana döndürür ve ajan hatayı yarım saat sonra değil, hemen düzeltir.
#!/usr/bin/env bash
# .claude/hooks/lint.sh
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
*.py)
if ! out=$(ruff check "$file" 2>&1); then
echo "Linter $file dosyasında hatalar buldu:" >&2
echo "$out" >&2
exit 2
fi ;;
*.php)
if ! out=$(php -l "$file" 2>&1); then
echo "$out" >&2
exit 2
fi ;;
esac
exit 0
Buraya on dakikalık tam test takımını koymayın: hook her düzenlemede çalışır. Düzenlemeden sonra hızlı kontroller (sözdizimi, linter, tek modülün testleri) yapın, ağır olanları Stop hook'una bırakın.
Örnek 2: tehlikeli komutları yasaklama
Bash matcher'ına sahip bir PreToolUse hook'u komutu çalıştırılmadan önce görür. Kod 2 çağrıyı iptal eder ve ajan açıklamayı alır.
#!/usr/bin/env bash
# .claude/hooks/guard.sh
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'rm -rf /|git push (-f|--force)|DROP (TABLE|DATABASE)'; then
echo "Komut proje politikasıyla engellendi: $cmd. Tehlikeli işlemleri insan yapar." >&2
exit 2
fi
exit 0
Dosyaları da aynı şekilde korumak mümkündür: Edit|Write üzerindeki bir hook file_path'i kontrol eder ve .env, prod yapılandırmaları, üretilmiş kod veya zaten uygulanmış migrasyon dosyalarına yazmayı engeller. Düzenli ifadeli arama aşılabilir — ajan komutu başka bir biçimde yazabilir. Hook güçlü bir sigortadır, ama erişim hakları ve sandbox'ın yerini tutmaz.
Örnek 3: testler kırmızıyken ajan "bitti" diyemez
Stop hook'u ajan işini bitirmek üzereyken çalışır. Testler geçmiyorsa kod 2 ajanın durmasını engeller ve test çıktısı bir sonraki görev olarak ajana gider. Bu, Tanımlanmış Tamamlanma (Definition of Done) kuralının mekanik bir versiyonudur.
#!/usr/bin/env bash
# .claude/hooks/done.sh
input=$(cat)
# sonsuz döngüye karşı koruma: hook işi bir kez zaten uzattı
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fi
if ! out=$(npm test --silent 2>&1); then
echo "Testler geçmedi, iş bitmedi:" >&2
echo "$out" | tail -n 40 >&2
exit 2
fi
exit 0
stop_hook_active alanı, ajan bir stop hook'u yüzünden işi zaten sürdürüyorsa true olur. Bu kontrol olmadan hook ajanı sonsuz bir döngüde çalıştırabilir — tam da "bütün gece yanlış yere çalışan ajan" durumu. Otonom döngülerde ayrıca deneme sınırı ve insanı çağıran bir koşul ekleyin.
Örnek 4: oturum başında durumu yükleme
SessionStart'ta hook'un stdout'u bağlama düşer. Mevcut dalı, commit edilmemiş değişiklikleri ve plandaki sonraki görevi yazdırmak faydalıdır; ajan sıfırdan başlamaz.
#!/usr/bin/env bash
echo "Dal: $(git branch --show-current)"
git status --short | head -n 20
[ -f PROGRESS.md ] && tail -n 15 PROGRESS.md
exit 0
Hook'lar kuralları garantiye nasıl çevirir
Harness kurallarını üç katmana bölmek kullanışlıdır. Birincisi CLAUDE.md'deki metin: nasıl çalışılacağını açıklar, ama olasılıksal olarak uygulanır. İkincisi çalıştırılabilir kontroller: testler, E2E, ihlalde düşen bir betik biçimindeki mimari kurallar. Üçüncüsü hook'lar: bu kontrolleri doğru anda, modelin katılımı olmadan otomatik çalıştırırlar.
Pratik kural: ajan aynı kuralı iki kez ihlal ettiyse, CLAUDE.md'ye büyük harflerle bir uyarı daha eklemeyin. Bu ihlalin bir betikle kontrol edilip edilemeyeceğini sorun. Edilebiliyorsa betiği yazın ve hook'a bağlayın. Böylece metin talimat kısalır, davranış ise istikrarlı olur.
| Metindeki kural | Hook ile garanti |
|---|---|
| "Kodu standarda göre biçimlendir" | PostToolUse, değişen dosyada biçimlendiriciyi çalıştırır |
| "force ile push atma" | Bash üzerindeki PreToolUse komutu engeller, exit 2 |
| "Testler kırmızıyken bitti deme" | Stop testleri çalıştırır ve işi bitirmeye izin vermez |
| "Ortam dosyasını (.env) düzenleme" | Edit|Write üzerindeki PreToolUse yolu kontrol eder |
| "Nerede kaldığını hatırla" | SessionStart planı ve git status'u yazdırır |
Sık yapılan hatalar
- Kod 2 yerine 1. Betik 1 koduyla çıkar, ajan bir şey öğrenmez ve eylem geçer. Yalnızca kod 2 engeller.
- Hatanın stdout'a yazılması. Kod 2'de ajana stderr gider.
>&2ile yazın. - Sık tetiklenen olayda yavaş hook. PostToolUse içinde tam test takımı her düzenlemeyi bir dakikalık yapar. Ağır işleri Stop'a koyun.
- Sonsuz Stop.
stop_hook_activekontrolü ya da bir sınır yoksa ajan sonsuz döner. - Göreli yollar. Hook oturumun çalışma dizininde çalışır;
$CLAUDE_PROJECT_DIRkullanın. - Güvenilmeyen hazır hook. Hook, onayınız olmadan sizin yetkilerinizle çalışır. Başka depolardan gelen betikleri güvenmeden önce okuyun.
Sonuç
Claude Code hooks en önemli kuralları metinden koda taşır: PreToolUse tehlikeli eylemleri durdurur, PostToolUse her düzenlemeyi kontrol eder, Stop erken "hazır" beyanını engeller, SessionStart ajana proje hafızasını geri verir. İlk olarak ajanın zaten ihlal ettiği iki üç kuralla başlayın ve stderr mesajlarını anlaşılır tutun; böylece ajan hataları kendisi düzeltir, siz de süreci değil sonucu kontrol edersiniz. Doğrulama, hazır olma kontrolü ve otonom döngüler kursta uygulamalı olarak ele alınır.
Sık sorulan sorular
Hook'ları değiştirdikten sonra Claude Code'u yeniden başlatmak gerekir mi?
settings.json değişiklikleri en güvenilir biçimde yeni bir oturumda uygulanır; mevcut hook yapılandırmasını /hooks komutuyla görebilirsiniz.
Hook'ları bash dışında bir dilde yazabilir miyim?
Evet, hook herhangi bir komuttur: Python, Node.js betiği ya da çalıştırılabilir dosya. Önemli olan stdin'den JSON okuyup doğru çıkış kodunu döndürmektir.
Ajan başarılı bir hook'un çıktısını görür mü?
Genellikle hayır: kod 0 ile çıktı bağlama girmez. İstisnalar UserPromptSubmit ve SessionStart'tır; orada stdout ajanın bağlamına eklenir.
Hook'lar alt ajanlar için de çalışır mı?
Evet, alt ajanların araç çağrıları aynı PreToolUse ve PostToolUse hook'larından geçer; işlerinin bitişi için ayrı bir SubagentStop olayı vardır.
Hook'ları depoya commit etmek güvenli mi?
Evet, .claude/settings.json ve .claude/hooks/ içindeki betikler ekip için yaygın bir uygulamadır; kişisel hook'ları settings.local.json veya ~/.claude/settings.json içinde tutun.
Bu konu “Harness Engineering: AI Ajanları İçin Güvenilirlik Mühendisliği” kursunda uygulamalı olarak ayrıntılı işleniyor.
- Ajan 'Hazır' Dedi, Özellik Çalışmıyor: Doğrulama Boşluğu ve Definition of Done
- Tüm Testler Yeşil, Özellik Bozuk: AI Ajanları İçin E2E Testler ve Mimari Kurallar
- AI Ajanını Saatlerce Denetimsiz Çalıştırmak: Loop Engineering ve Durdurma Koşulları