# Hook'lar dileği garantiye çevirir

CLAUDE.md'deki bir satır, modelin çoğu zaman uyduğu bir rica. Hook ise harness'ın çalıştırdığı kod, yani her seferinde olur. Hook'u yalnızca istisnası olmayan kurallar için seçin.

## Rica etmek ile yapmak arasındaki fark

CLAUDE.md'ye "formatter'ı her zaman çalıştır" yazarsanız Claude çoğu zaman çalıştırır. Çoğu zaman garanti değil, çünkü model o satırı okuyup bağlamındaki her şeyle tartıyor.

**Hook** tartmayı ortadan kaldırıyor. Claude Code'un yaşam döngüsündeki bir noktaya bağlanmış kendi kodunuz bu; model hatırlasa da hatırlamasa da harness çalıştırıyor. Modele sorulmuyor. Modelin etrafındaki program yapıyor.

Fikrin tamamı bu ve döngünün işleyişinden çıkıyor: bir şeyi çalıştıran model değil, harness.

## Event'ler

Hook bir event'e bağlanıyor. İki türleri var.

Exit code 2 ile **engelleyebilen** event'ler arasında `PreToolUse`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `PreCompact` ve `ConfigChange` var. Bunlarda amaç eylemi durdurmak.

Yalnızca **gözlemleyen** event'ler arasında `SessionStart`, `SessionEnd`, `PostToolUse`, `PostToolUseFailure`, `Notification`, `SubagentStart`, `CwdChanged`, `FileChanged` ve `PostCompact` var. Bunlar bir şey olduktan sonra çalışıyor, tepki verebiliyor ama geri alamıyor.

İşleyici (handler) beş türde: `command`, `http`, `mcp_tool`, `prompt` ve `agent`. Yalnızca `command` ve `http` deterministik. `prompt` ya da `agent` hook'u başka bir modele soruyor, yani yine yargı, sadece başka bir yerde.

## İki somut örnek

Her düzenlemede formatter. `Edit|Write` matcher'ına sahip bir `PostToolUse` hook'u her dosya değişikliğinden sonra formatter'ınızı çalıştırıyor. "Formatlamayı unutma" kuralı ortadan kalkıyor, çünkü hatırlanacak bir şey kalmıyor.

Claude durmadan önce tip kontrolü. Şu komutu çalıştıran bir `Stop` hook'u:

```bash
pnpm tsc --noEmit 1>&2 || exit 2
```

Kontrol başarısız olursa komut 2 koduyla çıkıyor, tur engelleniyor ve stderr'deki hata metni Claude'a geri gidiyor. Kontrol geçene kadar düzeltmeye devam ediyor. `1>&2` önemli: Claude'a ulaşan kanal stderr.

```terminal
$ add a rating field to the review form
* src/ReviewForm.tsx dosyasını düzenler
* Hook (Edit): formatter çalışır
* Bitirmeye çalışır
* Hook (Stop): tsc başarısız, "Property 'rating' is missing in ReviewProps"
* src/types.ts dosyasını düzenler
* Tekrar bitirmeye çalışır, hook geçer
# Claude tip kontrolü yapmaya karar vermedi. Hook, bitirmeyi buna bağladı.
```

**Question:** Bir Stop hook'u 2 koduyla çıkıp stderr'e hata yazarsa ne olur?

- Oturum kapanır
- Hook atlanır ve kaydedilir
- Claude'un durması engellenir ve stderr metni ona gider
- Hata yalnızca size gösterilir

**Answer:** Claude'un durması engellenir ve stderr metni ona gider. Engelleyebilen event'lerde exit code 2 engeller ve stderr Claude'a geri verilir. Diğer sıfır olmayan kodlar engellemeyen hata sayılır.


## İstisnası olan kurallar buraya ait değil

Hook'lar tasarım gereği katı. Değeri de bedeli de bu. Resmi tavsiye, hook'ları yalnızca istisnası hiç olmayan kurallar için kullanmak ve tercihleri CLAUDE.md'de tutmak. Kırk hook'unuz varsa çoğu muhtemelen tercihtir.

Sizi kendi hook'larınızdan koruyan sınırlar da var. Arada araç çağrısı olmadan arka arkaya sekiz kez engelleyen bir Stop hook'u geçersiz kılınıyor ve tur bitiyor. `CLAUDE_CODE_STOP_HOOK_BLOCK_CAP` bu sayıyı yükseltiyor.

> **Exit code ya da JSON, ikisi birden değil**
Hook, exit code ile sinyal verebilir ya da `decision` ve `hookSpecificOutput` gibi alanlarla JSON basabilir. Hook başına tek bir yöntem seçin. Ayrıca çöken ya da zaman aşımına uğrayan hook normalde eylemi geçirir. `onFailure: "block"` (2.1.295 ve sonrası) bunu engellemeye çeviriyor.


## Nerede tanımlanır

Hook'lar `~/.claude/settings.json`, `.claude/settings.json`, `.claude/settings.local.json`, managed settings, bir plugin'in `hooks/hooks.json` dosyası ve skill ya da alt ajan frontmatter'ında tutuluyor; frontmatter'dakiler yalnızca o skill ya da alt ajan çalışırken etkin. Etkin olanlara bakmak için `/hooks` yazın.

`*`, boş ya da hiç verilmeyen matcher her şeyle eşleşir. `|` ile ayrılmış düz isimler birebir eşleşir; başka herhangi bir karakter matcher'ı, başa ve sona sabitlenmemiş bir JavaScript düzenli ifadesine çevirir. İşleyiciyi daha da daraltmak için `if` alanı `Bash(git *)` gibi izin kuralı sözdizimini kabul ediyor.

> **Bir kuralı garantiye çevirin**
CLAUDE.md'nizden çiğnendiğini gördüğünüz bir kural seçin. Gerçekten istisnasız mı diye sorun. Öyleyse hook olarak yazın, durumu tetikleyin ve kayıtlı olduğunu `/hooks` içinde doğrulayın.


**Question:** Hangi kural hook için iyi bir adaydır?

- Küçük fonksiyonları tercih et
- Her dosya düzenlemesinden sonra formatter'ı çalıştır
- Gerekçeni kısaca açıkla
- Dostça değişken adları kullan

**Answer:** Her dosya düzenlemesinden sonra formatter'ı çalıştır. Düzenlemeden sonra formatlamak mekanik ve istisnasız. Diğer üçü tercih, yani CLAUDE.md'ye ait.
