Claude Code'da Hooks: Olay Tabanlı Otomasyon Rehberi
Daha önce Agent Skills ve slash komutlarını, ardından Claude Code'un CLI ve print mode ile script/CI ortamlarında kullanımını ele almıştım. Skill'ler Claude'un kendi kararıyla ya da sizin çağırmanızla devreye giren, isteğe bağlı talimat setleriydi. Bu yazıda ele alacağım hooks ise tamamen farklı bir kontrol katmanı sunuyor: Claude'un "uygun görmesine" bırakılmayan, Claude Code'un yaşam döngüsündeki belirli noktalarda her seferinde otomatik çalışan kabuk komutlarıdır. Resmi code.claude.com/docs dokümantasyonunun tanımıyla, hook'lar "Claude Code'un davranışı üzerinde deterministik kontrol sağlar; belirli eylemlerin, LLM'in çalıştırmayı seçmesine güvenmek yerine her zaman gerçekleşmesini garanti eder."
Bu fark pratikte çok şey değiştiriyor. Bir skill'e "her commit'ten önce testleri çalıştır" yazabilirsiniz, ama Claude o talimatı atlayabilir ya da yanlış yorumlayabilir. Bir PreToolUse hook'u ise git commit komutunu fiilen engelleyip Claude'a neden engellendiğini bildirebilir -- kararı modele bırakmaz. Bu yazıda hook yapılandırma dosyalarını, tam olay (event) listesini, girdi/çıktı JSON şemasını, çıkış kodlarını, matcher ve if filtrelemesini, ve gerçek dünyadan örnekleri (tehlikeli komutları engelleme, otomatik formatlama, korumalı dosyalar, masaüstü bildirimleri, bağlam enjeksiyonu) inceleyeceğiz.
1. Hook Nedir, Nasıl Çalışır?
Bir hook, Claude Code'un yaşam döngüsündeki bir olay (event) tetiklendiğinde çalışan bir işleyicidir. En yaygın tür kabuk komutu çalıştıran command hook'udur, ancak dört tür daha vardır: bir URL'e POST isteği atan http, bağlı bir MCP sunucusundaki aracı çağıran mcp_tool, kararı tek seferlik bir LLM çağrısına bırakan prompt, ve araç erişimi olan bir subagent başlatan agent (deneysel). Bu yazıda ağırlıklı olarak en yaygın ve üretime en uygun tür olan command hook'larına odaklanacağız.
Hook'lar Claude Code'a üç kanaldan konuşur: stdin üzerinden olayla ilgili JSON verisini alırlar, stdout/stderr'e yazarlar ve bir exit code ile dönerler. Exit code, işlemin devam edip etmeyeceğini; stdout'a yazılan JSON ise Claude'a ek bağlam veya yapılandırılmış bir karar iletmek için kullanılır.
2. İlk Hook'unuzu Kurmak
Bir hook eklemek için bir ayarlar dosyasına hooks bloğu eklersiniz. Örneğin Claude her izin istediğinde masaüstü bildirimi almak isterseniz, ~/.claude/settings.json dosyasına şunu eklersiniz (Linux için notify-send kullanan sürüm):
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}
Ayarlar dosyanızda zaten bir hooks anahtarı varsa, mevcut olay anahtarlarının tamamını değiştirmek yerine Notification'ı bir kardeş anahtar olarak ekleyin -- her olay adı, tek bir hooks nesnesi içindeki ayrı bir anahtırdır. Kaydettikten sonra terminalde /hooks yazarak hook tarayıcısını açabilir, olayın altında hook'unuzun göründüğünü doğrulayabilirsiniz. /hooks menüsü salt okunurdur; eklemek, değiştirmek veya kaldırmak için ayarlar JSON'unu doğrudan düzenlemeniz (ya da Claude'dan düzenlemesini istemeniz) gerekir.
3. Yapılandırma Dosyalarının Konumu
Bir hook'u nereye yazdığınız, onun kapsamını belirler:
| Konum | Kapsam | Paylaşılabilir mi |
|---|---|---|
~/.claude/settings.json |
Tüm projeleriniz | Hayır, yalnızca sizin makineniz |
.claude/settings.json |
Tek proje | Evet, repoya commit edilebilir |
.claude/settings.local.json |
Tek proje | Hayır, Claude Code oluşturduğunda gitignore'lanır |
| Yönetilen politika ayarları | Organizasyon geneli | Evet, yönetici kontrollü |
Plugin hooks/hooks.json |
Plugin etkinken | Evet, plugin ile birlikte paketlenir |
| Skill veya subagent frontmatter'ı | Skill/agent aktifken | Evet, bileşen dosyasında tanımlı |
Hook'ları tamamen kapatmak için ayarlar dosyanıza "disableAllHooks": true ekleyebilirsiniz; ancak yönetilen ayarlardaki hook'lar bu bayrak orada da ayarlanmadıkça çalışmaya devam eder. Ayarlar dosyalarını Claude Code çalışırken doğrudan düzenlerseniz, dosya izleyici değişiklikleri genellikle otomatik olarak algılar.
4. Yapılandırma Yapısı: matcher ve hooks Dizisi
Her olay altında bir dizi girdi tanımlanır; her girdi bir matcher (hangi araç/olay alt türüne uygulanacağı) ve bir hooks dizisi (o eşleşmede çalışacak işleyiciler) içerir:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"timeout": 600
}
]
}
]
}
}
Matcher desenleri şu şekilde değerlendirilir:
| Desen türü | Değerlendirme | Örnekler |
|---|---|---|
"*", "", belirtilmemiş |
Her şeyle eşleşir | Her tekrar |
Harf, rakam, _, -, boşluk, ,, | |
Tam string veya liste | Bash, Edit|Write |
| Diğer karakterler | JavaScript regex, çapasız (unanchored) | ^Notebook, mcp__memory__.* |
Claude Code v2.1.191 ve sonrasında | ve , birbirinin yerine kullanılabilir, yani "Edit, Write" ile "Edit|Write" aynı sonucu verir. MCP araçları için mcp__<sunucu>__<araç> deseni (örn. mcp__github__search_repositories) ya da bir sunucunun tüm araçlarını hedeflemek için mcp__<sunucu>__.* kullanılır. Hangi olayın hangi alanı eşleştirdiği olaydan olaya değişir: PreToolUse/PostToolUse araç adını, SessionStart oturumun nasıl başladığını (startup, resume, clear, compact), Notification bildirim türünü eşleştirir. UserPromptSubmit, Stop, PostToolBatch, CwdChanged gibi bazı olaylar matcher desteklemez ve her tekrarında çalışır.
5. Tam Olay (Event) Listesi
Claude Code, oturum başına bir kez tetiklenen olaylardan (SessionStart, SessionEnd) her tur (UserPromptSubmit, Stop) ve her araç çağrısına (PreToolUse, PostToolUse) kadar geniş bir olay yelpazesi sunar. En sık kullanılanlar:
| Olay | Ne zaman tetiklenir |
|---|---|
SessionStart |
Oturum başladığında veya devam ettirildiğinde |
UserPromptSubmit |
Prompt gönderildiğinde, Claude işlemeden önce |
PreToolUse |
Bir araç çağrısından önce; engellenebilir |
PermissionRequest |
İzin diyaloğu göründüğünde |
PostToolUse |
Bir araç çağrısı başarıyla tamamlandıktan sonra |
PostToolUseFailure |
Bir araç çağrısı başarısız olduktan sonra |
Notification |
Claude Code bir bildirim gönderdiğinde |
SubagentStart / SubagentStop |
Bir subagent başlatıldığında / tamamlandığında |
Stop |
Claude yanıt vermeyi bitirdiğinde |
StopFailure |
Tur bir API hatası nedeniyle sona erdiğinde |
PreCompact / PostCompact |
Bağlam sıkıştırmasından önce / sonra |
CwdChanged |
Çalışma dizini değiştiğinde (örn. Claude cd çalıştırdığında) |
FileChanged |
İzlenen bir dosya diskte değiştiğinde |
ConfigChange |
Bir yapılandırma dosyası oturum sırasında değiştiğinde |
SessionEnd |
Oturum sona erdiğinde |
Bunların dışında PermissionDenied, PostToolBatch, MessageDisplay, TaskCreated, TaskCompleted, InstructionsLoaded, WorktreeCreate/WorktreeRemove, Elicitation/ElicitationResult, Setup ve UserPromptExpansion gibi daha uç senaryolara yönelik olaylar da bulunuyor; tam listeyi /hooks menüsünden veya resmi dokümantasyondan görebilirsiniz. Bir olayda birden fazla hook eşleşirse hepsi paralel çalışır ve aynı komut birden fazla kez tanımlanmışsa otomatik tekilleştirilir.
6. Hook'lara Gelen Girdi (stdin)
Her olay, ortak alanlarla birlikte olaya özgü veriler taşıyan bir JSON nesnesini stdin üzerinden gönderir. Örneğin Claude bir Bash komutu çalıştıracağında, bir PreToolUse hook'u şuna benzer bir girdi alır:
{
"session_id": "abc123",
"cwd": "/home/user/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
Ortak alanlar arasında session_id (oturum kimliği), cwd (olayın tetiklendiği çalışma dizini), transcript_path (konuşma kaydının yolu), permission_mode (default, plan, acceptEdits, bypassPermissions vb.) ve hook_event_name bulunur. UserPromptSubmit hook'ları ek olarak prompt alanını, SessionStart hook'ları source alanını (startup, resume, clear, compact) alır. Script'iniz bu JSON'u jq veya tercih ettiğiniz herhangi bir dille ayrıştırıp ilgili alanları okuyabilir.
7. Çıktı: Exit Kodları ve JSON
Script'iniz stdout/stderr'e yazıp bir exit code ile döner:
| Exit code | Anlamı |
|---|---|
0 |
İtiraz yok; normal akış devam eder. UserPromptSubmit ve SessionStart için stdout'a yazılan her şey Claude'un bağlamına eklenir. |
2 |
Engelleyici hata; stderr'e yazılan mesaj Claude'a geri bildirim olarak iletilir. SessionStart, Setup, Notification gibi bazı olaylarda engelleyemez, yalnızca stderr'i kullanıcıya gösterir. |
| Diğer | Engellemeyen hata; işlem devam eder, transcript'te "hook error" bildirimi görünür, tam stderr debug log'una gider. |
Exit code'lar yalnızca engelleme/sessiz kalma seçeneği sunar. Daha ince kontrol için exit 0 ile birlikte stdout'a bir JSON nesnesi yazabilirsiniz. Önemli bir kural: exit 2 ile JSON'u karıştırmayın -- Claude Code exit 2 döndüğünde JSON çıktısını yok sayar.
{
"continue": true,
"stopReason": "Build failed",
"suppressOutput": false,
"systemMessage": "Uyarı: veritabanı salt okunur",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Yıkıcı komut engellendi"
}
}
Her olayın karar bildirme şekli farklıdır. PreToolUse için hookSpecificOutput.permissionDecision kullanılır ve dört değer alır: "allow" (izin isteğini atlar, ancak reddetme kuralları hâlâ geçerlidir), "deny" (aracı iptal eder ve nedeni Claude'a iletir), "ask" (normal izin istemini gösterir), ve headless -p modunda kullanılabilen "defer". PostToolUse ve Stop gibi olaylar ise üst seviye decision: "block" alanını kullanır; UserPromptSubmit için bağlam eklemek üzere hookSpecificOutput.additionalContext alanına yazılır -- bu alan hookSpecificOutput içine yerleştirilmezse Claude Code onu sessizce yok sayar.
8. Pratik Örnek: Tehlikeli Bash Komutlarını Engelleme
Aşağıdaki script, komut içinde drop table geçiyorsa çalıştırmayı engeller ve Claude'a nedenini bildirir:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "drop table"; then
echo "Blocked: dropping tables is not allowed" >&2
exit 2
fi
exit 0
Bu script'i .claude/hooks/block-drop-table.sh olarak kaydedip chmod +x ile çalıştırılabilir yaptıktan sonra .claude/settings.json'a şu şekilde bağlarsınız:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-drop-table.sh"
}
]
}
]
}
}
Yol yer tutucularından ${CLAUDE_PROJECT_DIR} proje kökünü, ${CLAUDE_PLUGIN_ROOT} plugin dizinini temsil eder ve hem command hem args alanlarında kullanılabilir; ayrıca çalıştırılan sürece ortam değişkeni olarak da aktarılır.
9. Pratik Örnek: Düzenlemeden Sonra Otomatik Formatlama
Claude'un düzenlediği her dosyada Prettier'i otomatik çalıştırmak için PostToolUse olayını Edit|Write matcher'ı ile eşleştirin. Komut, düzenlenen dosya yolunu jq ile çıkarıp Prettier'e aktarır. Bunu .claude/settings.json'a ekleyin:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
PostToolUse hook'ları aracın çalışmasından sonra tetiklendiği için işlemi geri alamaz; bu olayı yalnızca "zaten oldu, şimdi düzelt/kaydet/bildir" türü işler için kullanın. Claude, dosyaları Bash aracıyla da değiştirebileceğinden, her değişikliği yakalamanız gerekiyorsa (uyumluluk taraması gibi) Bash'i de eşleştirip git status --porcelain ile değişen dosyaları listeleyen bir Stop hook'u eklemek daha güvenilir bir yaklaşımdır.
10. Pratik Örnek: Korumalı Dosyaları Engelleme
.env, package-lock.json veya .git/ altındaki herhangi bir dosyanın Claude tarafından değiştirilmesini önlemek isteyebilirsiniz. .claude/hooks/protect-files.sh olarak kaydedin:
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
chmod +x .claude/hooks/protect-files.sh ile çalıştırılabilir yaptıktan sonra Edit veya Write aracı çağrılmadan önce çalışacak şekilde kaydedin:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
11. Pratik Örnek: Masaüstü Bildirimleri
Notification olayı, Claude giriş veya izin beklediğinde tetiklenir. Boş matcher tüm bildirim türlerinde çalışır; belirli bir türe daraltmak için matcher'ı şu değerlerden birine ayarlayabilirsiniz:
| Matcher | Ne zaman tetiklenir |
|---|---|
permission_prompt |
Claude bir araç kullanımını onaylamanızı istediğinde |
idle_prompt |
Claude bitirdi ve bir sonraki isteğinizi beklediğinde |
auth_success |
Kimlik doğrulama tamamlandığında |
elicitation_dialog |
Bir MCP sunucusu bir form açtığında |
agent_needs_input / agent_completed |
Arka plan oturumu girdi beklerken / tamamlandığında (agent view açıkken) |
Linux için notify-send, macOS için osascript -e 'display notification ...', Windows PowerShell için System.Windows.Forms.MessageBox kullanılabilir. Bu hook'u ekledikten sonra /hooks menüsünden Notification'ı seçip kaydolduğunu doğrulayın, sonra Claude'dan izin gerektiren bir şey isteyip terminalden uzaklaşarak test edin.
12. Pratik Örnek: Sıkıştırma (Compaction) Sonrası Bağlam Enjeksiyonu
Bağlam penceresi dolduğunda Claude Code konuşmayı özetleyerek yer açar; bu süreçte önemli ayrıntılar kaybolabilir. compact matcher'lı bir SessionStart hook'u, her sıkıştırmadan sonra kritik bağlamı yeniden enjekte etmenizi sağlar. Komutunuzun stdout'a yazdığı her şey Claude'un bağlamına eklenir:
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Hatırlatma: npm yerine bun kullan. Commit öncesi bun test çalıştır.'"
}
]
}
]
}
}
echo yerine git log --oneline -5 gibi dinamik çıktı üreten herhangi bir komut kullanılabilir. Her oturum başlangıcında bağlam enjekte etmek istiyorsanız bunun yerine CLAUDE.md kullanmayı değerlendirin -- hook, özellikle sıkıştırma sonrası kaybı telafi etmek için daha uygundur.
13. Matcher ve if Alanı ile Hassas Filtreleme
matcher alanı grup seviyesinde yalnızca araç adına göre filtreler. Araç adı ve argümanları birlikte filtrelemek için if alanı kullanılır; bu alan izin kuralı sözdizimini kullanır, böylece hook süreci yalnızca gerçekten eşleşen çağrılarda başlatılır:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git *)",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
}
]
}
]
}
}
Bu filtre "en iyi çaba" ile çalışır; Bash komutu ayrıştırılamıyorsa hook yine de "açık" davranarak çalışır. Bu yüzden kesin bir izin/red kuralı için if yerine izin sistemini kullanmak gerekir. if alanı yalnızca PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest ve PermissionDenied gibi araç olaylarında çalışır; başka bir olaya eklenirse hook'un çalışmasını engeller.
if deseni |
Bash komutu | Hook çalışır mı |
|---|---|---|
Bash(git *) |
git push |
Evet |
Bash(git *) |
npm test && git push |
Evet (alt komutlar ayrı ayrı kontrol edilir) |
Bash(git *) |
echo $(date) |
Hayır |
14. Prompt ve Agent Tabanlı Hook'lar
Bazı kararlar tamamen deterministik kurallarla verilemez. type: "prompt" hook'ları, hook girdisini bir Claude modeline (varsayılan olarak Haiku, model alanıyla değiştirilebilir) göndererek yalnızca evet/hayır kararı ister:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Tüm görevler tamamlandı mı? Değilse {\"ok\": false, \"reason\": \"ne eksik\"} döndür."
}
]
}
]
}
}
"ok": false döndüğünde davranış olaya göre değişir: Stop/SubagentStop'ta reason Claude'a geri beslenir ve çalışmaya devam eder; PreToolUse'da araç çağrısı reddedilir. Dosyaların gerçek içeriğini okumak veya komut çalıştırarak doğrulama yapmak gerektiğinde type: "agent" kullanılır -- bu, araç erişimi olan bir subagent başlatır (varsayılan 60 saniye zaman aşımı, en fazla 50 araç kullanım turu). Resmi dokümantasyon agent hook'ların deneysel olduğunu ve üretim iş akışlarında komut hook'larının tercih edilmesi gerektiğini açıkça belirtiyor.
15. Birden Fazla Hook'un Birleştirilmesi
Aynı olaya birden fazla hook eşleştiğinde, hepsi paralel çalışır ve tamamlanana kadar beklenir -- bir hook'un deny döndürmesi kardeş hook'ların çalışmasını durdurmaz. Tüm hook'lar tamamlandıktan sonra sonuçlar birleştirilir: PreToolUse izin kararlarında en kısıtlayıcı yanıt kazanır (deny > defer > ask > allow sırasıyla), additionalContext metinleri ise her hook'tan toplanıp birlikte Claude'a iletilir. Birden fazla PreToolUse hook'u aynı aracın girdisini updatedInput ile yeniden yazarsa, hook'lar paralel çalıştığından son biten kazanır -- bu yüzden aynı aracın girdisini birden fazla hook'un değiştirmesinden kaçının.
16. Güvenlik Değerlendirmeleri
PreToolUsehook'ları izin modu kontrolünden önce çalışır.permissionDecision: "deny"döndüren bir hook,bypassPermissionsmodunda veya--dangerously-skip-permissionsile bile aracı engeller -- kullanıcıların izin modunu değiştirerek atlayamayacağı bir politika uygulamanızı sağlar.- Tersi geçerli değildir. Bir hook'un
"allow"döndürmesi, ayarlardaki reddetme kurallarını veya kullanıcı etkileşimi gerektiren bağlayıcı/MCP araçlarının onay istemini atlatmaz. Hook'lar kısıtlamayı sıkılaştırabilir ama izin kurallarının izin verdiğinden fazla gevşetemez. PostToolUsehook'ları geri alamaz. Araç zaten çalıştığı için sadece tepki verebilirsiniz, engelleyemezsiniz.disableAllHooksile kapatma yalnızca kullanıcı/proje hook'larını devre dışı bırakır; yönetilen politika hook'ları bu bayraktan etkilenmez.- HTTP hook'ları için yalnızca 2xx + doğru
hookSpecificOutputalanlarını içeren bir yanıt gövdesi engelleyebilir; HTTP durum kodu tek başına yeterli değildir.
17. Sorun Giderme
- Hook hiç tetiklenmiyor:
/hooksile doğru olayın altında göründüğünü doğrulayın; matcher büyük/küçük harfe duyarlıdır; script'ichmod +xile çalıştırılabilir yaptığınızdan emin olun. - "JSON validation failed" hatası: Kabuk formunda (
argsbelirtilmemiş) komutlarsh -cile çalışır ve kabuk profiliniz (.bashrc/.zshrc) koşulsuzechoiçeriyorsa bu metin hook'un JSON çıktısının önüne eklenip ayrıştırmayı bozar. Profildekiecho'ları[[ $- == *i* ]]kontrolüyle yalnızca etkileşimli kabuklarda çalışacak şekilde sarın. - "command not found": Script'e mutlak yol veya
${CLAUDE_PROJECT_DIR}ile referans verin; kabuk alıntılamasından tamamen kaçınmak için"args": []ekleyip exec formuna geçebilirsiniz. - Stop hook engelleme sınırına takılıyor: Claude Code, bir
Stophook'u ilerleme kaydetmeden art arda sekiz kez engellediğinde onu geçersiz kılar. Script'inizdestop_hook_activealanını kontrol ediptrueise erken çıkın; gerekiyorsaCLAUDE_CODE_STOP_HOOK_BLOCK_CAPile sınırı yükseltin. - Ayrıntılı hata ayıklama:
claude --debug-file /tmp/claude.logile başlatıptail -f /tmp/claude.logile hangi hook'ların eşleştiğini, exit code'larını, stdout/stderr'ini görebilirsiniz; oturum ortasında/debugile de etkinleştirilebilir.
Gerçek Dünya Örneği: Ekip İçin Kombine Bir Yapılandırma
Bir ekibin .claude/settings.json dosyasına commit edebileceği, hem güvenlik hem de kalite kontrolü sağlayan bir örnek: korumalı dosyaları engelleme, düzenlemeden sonra otomatik formatlama, ve sıkıştırma sonrası bağlam hatırlatması bir arada:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
],
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "git log --oneline -5"
}
]
}
]
}
}
Bu yapılandırma repoya commit edilirse tüm ekip aynı korumaları ve otomasyonu paylaşır. Kişisel tercihler (örneğin masaüstü bildirimleri) için bunları ~/.claude/settings.json'a ayrı tutmak, ekip standardını kişisel alışkanlıklardan ayırır.
Sıkça Sorulan Sorular
Hook ile skill arasındaki fark nedir?
Bir skill, Claude'un kendi kararıyla ya da siz /isim yazarak çağırdığınız, isteğe bağlı bir talimat setidir. Bir hook ise Claude Code'un yaşam döngüsündeki belirli bir noktada -- örneğin bir Bash komutu çalıştırılmadan hemen önce -- her seferinde otomatik olarak çalışan, deterministik bir kabuk komutudur. Hook, Claude'un o an ne düşündüğünden bağımsız çalışır; skill ise Claude'un uygun görmesine bağlıdır.
Bir hook'u hangi dosyaya yazmalıyım?
Tüm projelerinizde geçerli kişisel hook'lar için ~/.claude/settings.json, yalnızca bu projede geçerli ve ekiple paylaşılabilir hook'lar için .claude/settings.json, yalnızca sizin makinenizde kalacak proje bazlı hook'lar için .claude/settings.local.json kullanılır. Kurumsal ortamlarda yönetilen politika ayarları ve pluginlerin hooks/hooks.json dosyaları da ek katmanlardır.
Exit code 2 ile exit code 1 arasındaki fark nedir?
Exit code 0 hook'un bir itirazı olmadığını bildirir. Exit code 2 işlemi engeller; stderr'e yazdığınız mesaj Claude'a geri bildirim olarak iletilir. Bunların dışındaki bir exit code (örn. 1) işlemi engellemez; sadece transcript'te bir hata bildirimi gösterilir. SessionStart, Setup ve Notification gibi bazı olaylarda exit code 2 işlemi engelleyemez, yalnızca stderr'i kullanıcıya gösterir.
PreToolUse hook'u bypassPermissions modunu atlayabilir mi?
Evet, tersi yönde değil. PreToolUse hook'ları herhangi bir izin modu kontrolünden önce çalışır; permissionDecision: "deny" döndüren bir hook, bypassPermissions modunda bile aracı engeller. Ancak bir hook'un "allow" döndürmesi, ayarlardaki reddetme kurallarını atlatamaz -- hook'lar kısıtlamayı sıkılaştırabilir ama gevşetemez.
Hook'lar hangi sırayla çalışır, biri diğerini engelleyebilir mi?
Aynı olaya eşleşen tüm hook'lar paralel çalışır ve aynı komut birden fazla kez tanımlanmışsa tekilleştirilir. Bir hook'un deny döndürmesi diğer kardeş hook'ların çalışmasını durdurmaz; hepsi tamamlanana kadar beklenir, sonra sonuçlar birleştirilir. PreToolUse izin kararlarında en kısıtlayıcı yanıt kazanır: deny, defer, ask, allow sırasıyla önceliklidir.
Karar gerektiren ama deterministik olmayan kontroller için ne kullanılır?
type: "prompt" hook'ları, hook girdisini bir Claude modeline (varsayılan Haiku) gönderip yalnızca evet/hayır kararı almanızı sağlar. Gerçek doğrulama (dosya okuma, komut çalıştırma) gerektiğinde type: "agent" kullanılır; bu araç erişimi olan bir subagent başlatır. Resmi dokümantasyona göre agent hook'lar deneyseldir ve üretim iş akışlarında komut hook'ları tercih edilmelidir.
Özet
Hook'lar, Claude Code'a "belki yapar" yerine "her zaman yapar" garantisi ekleyen deterministik kontrol katmanıdır. Bir hooks bloğu; matcher ile hangi araç/olayı hedefleyeceğinizi, type ile nasıl çalışacağını (command, http, mcp_tool, prompt, agent), if ile araç argümanlarına göre ince filtrelemeyi belirler. Girdi stdin'den JSON olarak gelir, karar exit code ve/veya hookSpecificOutput içeren bir JSON ile iletilir. PreToolUse ile tehlikeli komutları veya korumalı dosyaları engelleyebilir, PostToolUse ile otomatik formatlama yapabilir, SessionStart ile sıkıştırma sonrası bağlamı geri kazandırabilir, Notification ile masaüstü uyarıları alabilirsiniz. Kişisel (~/.claude/settings.json) veya proje (.claude/settings.json) seviyesinde tanımlayarak kapsamı belirleyebilir, ekip standartlarını versiyon kontrolüne ekleyerek paylaşabilirsiniz. Skill'lerinizi hook'larla nasıl birlikte kullanacağınızı görmek için Agent Skills rehberimi, script/CI entegrasyonu için de CLI ve otomasyon rehberimi okuyabilirsiniz.
Bu yazı Ahmet Bilgiç tarafından ahmetbilgic.com için yazılmıştır. Diğer yazılar için: medium.com/@ahmet_bilgic07