Poradnik Claude Code Hooks: 5 produkcyjnych hooków od podstaw
Claude Code wykonuje właściwą akcję w około 95% przypadków. Pozostałe 5% obejmuje force-push na main, pominięcie formatera i commitowanie kodu, który nie przechodzi lintingu. Hooki eliminują te 5%, dodając deterministyczne bramki w 17 punktach cyklu życia workflow Claude. Uruchamiają się za każdym razem, bez wyjątku, niezależnie od sformułowania promptu czy zachowania modelu.
W skrócie: Hooki to polecenia powłoki wyzwalane przez zdarzenia cyklu życia Claude Code. Hooki PreToolUse sprawdzają i blokują akcje (kod wyjścia 2 = blokada, kod 0 = pozwolenie). Hooki PostToolUse walidują i formatują po fakcie. Konfiguruje się je w .claude/settings.json z wyrażeniem regularnym matcher i zagnieżdżoną tablicą hooks. Ten poradnik buduje pięć produkcyjnych hooków: automatyczny formater, bramkę bezpieczeństwa, runner testów, powiadomienia i kontrolę jakości przed commitem.
Kluczowe wnioski
- Samodzielni programiści: Zacznij od automatycznego formatera (Hook 1) i bramki bezpieczeństwa (Hook 2). Te dwa hooki zapobiegają najczęstszym błędom Claude Code bez żadnej bieżącej konserwacji.
- Liderzy zespołów: Commituj hooki do
.claude/settings.jsonw repozytorium. Każdy członek zespołu automatycznie otrzymuje te same bramki bezpieczeństwa i kontrole jakości. - Inżynierowie bezpieczeństwa: Kod wyjścia 2 blokuje akcję. Kod wyjścia 1 tylko loguje ostrzeżenie. Każdy hook bezpieczeństwa PreToolUse musi używać
exit 2, w przeciwnym razie nie zapewnia żadnego egzekwowania.
Czym są hooki?
Hooki to polecenia powłoki wykonywane w określonych momentach cyklu życia sesji Claude Code. Działają poza LLM jako zwykłe skrypty wyzwalane przez akcje Claude, a nie jako prompty interpretowane przez model.
Cztery kluczowe kategorie obejmują najczęstsze przypadki użycia (Claude Code obsługuje łącznie 17 typów zdarzeń):
- Zdarzenia sesji:
SessionStartiStopuruchamiają się na początku i końcu sesji. Służą do konfiguracji, czyszczenia zasobów i powiadomień. - Zdarzenia narzędzi:
PreToolUseiPostToolUseuruchamiają się przed i po użyciu narzędzia przez Claude (zapis pliku, uruchomienie polecenia bash lub przeszukiwanie kodu). To najpotężniejsze hooki, ponieważ mogą sprawdzać i blokować określone akcje. - Zdarzenia powiadomień:
Notificationuruchamia się, gdy Claude generuje powiadomienie. Przydatne do kierowania alertów do Slacka, powiadomień systemowych lub systemów logowania. - Zdarzenia subagentów:
SubagentStopuruchamia się po zakończeniu działania subagenta (uruchomionego przez narzędzie Agent). Hooki uruchamiają się również dla akcji subagentów, więc bramki bezpieczeństwa działają rekurencyjnie.
Semantyka kodów wyjścia ma znaczenie. Kod 0 oznacza sukces (kontynuuj). Kod 2 oznacza blokadę akcji. Kod 1 oznacza nieblokujący błąd hooka — akcja nadal jest wykonywana. Każdy hook krytyczny dla bezpieczeństwa musi używać exit 2, aby faktycznie egzekwować swoją bramkę.
Podstawy konfiguracji hooków
Hooki znajdują się w plikach ustawień:
- Poziom projektu:
.claude/settings.jsonw katalogu głównym repozytorium (współdzielony z zespołem) - Poziom użytkownika:
~/.claude/settings.json(osobiste hooki, stosowane globalnie)
Struktura JSON:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/your/script.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/path/to/another-script.sh"
}
]
}
]
}
}
Każdy wpis zawiera matcher (wyrażenie regularne dopasowujące nazwy narzędzi, takie jak Bash, Write, Edit, Read, Glob, Grep lub Agent) oraz tablicę hooks z definicjami hooków. Każdy hook określa type ("command" dla poleceń powłoki) i command do wykonania. Matcher Write|Edit dopasowuje oba typy narzędzi.
Hookami można również zarządzać interaktywnie za pomocą polecenia /hooks wewnątrz sesji Claude Code.
Gdy hook się uruchamia, Claude Code przekazuje kontekst przez zmienne środowiskowe ($FILE_PATH dla operacji na plikach) i stdin (obiekt JSON zawierający nazwę narzędzia, parametry i metadane sesji). Skrypt odczytuje te dane, aby podejmować decyzje.
5 praktycznych hooków
Każdy poniższy hook rozwiązuje realny problem, który napotkałem podczas używania Claude Code jako głównego narzędzia programistycznego. Wszystkie przykłady używają poprawnego zagnieżdżonego schematu hooków.
1. Automatyczne formatowanie przy edycji pliku
Claude pisze funkcjonalnie poprawny kod, który czasami łamie reguły formatowania projektu. Zamiast prosić Claude o przeformatowanie, uruchom formater automatycznie po każdym zapisie pliku.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]]; then black --quiet \"$FILE_PATH\" 2>/dev/null; elif [[ \"$FILE_PATH\" == *.js ]] || [[ \"$FILE_PATH\" == *.ts ]]; then npx prettier --write \"$FILE_PATH\" 2>/dev/null; fi'"
}
]
}
]
}
}
Claude Code ustawia $FILE_PATH na zmodyfikowany plik. Hook sprawdza rozszerzenie i uruchamia odpowiedni formater. Pliki Python przechodzą przez black, pliki JavaScript i TypeScript przez prettier. Przekierowanie 2>/dev/null tłumi nadmierny output, więc widoczne są tylko rzeczywiste błędy.
W większych projektach warto przenieść polecenie inline do osobnego skryptu dla czytelności.
2. Bramka bezpieczeństwa dla niebezpiecznych poleceń
Hooki PreToolUse na narzędziu Bash sprawdzają polecenie, które Claude zamierza wykonać, i blokują je, jeśli pasuje do niebezpiecznego wzorca. Napisałem ten hook po tym, jak Claude zrobił force-push na main podczas sesji refaktoryzacji.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"rm\\s+-rf\\s+/|git\\s+push\\s+(-f|--force)\\s+(origin\\s+)?main|git\\s+reset\\s+--hard|DROP\\s+TABLE|:(){ :|:& };:\"; then echo \"BLOCKED: Dangerous command detected: $CMD\" >&2; exit 2; fi'"
}
]
}
]
}
}
Gdy ten hook kończy działanie z kodem 2, Claude Code anuluje oczekujące polecenie. Komunikat błędu wyświetla się zarówno w terminalu, jak i w kontekście Claude, więc model rozumie, dlaczego akcja nie powiodła się, i sugeruje bezpieczniejszą alternatywę.
Blokowane wzorce:
- rm -rf / (rekurencyjne usuwanie od katalogu głównego)
- git push --force main i git push -f main (force-push na gałąź main)
- git reset --hard (niszczenie niezacommitowanych zmian)
- DROP TABLE (przypadkowe zniszczenie bazy danych)
- Fork bomby
Dostosuj tę listę do swojego środowiska. Produkcyjne bazy danych wymagają wzorców destrukcyjnych zapytań SQL. Wdrożenia oparte na CLI wymagają zabezpieczeń poleceń deploymentu.
3. Uruchamianie testów po zmianach
Gdy Claude edytuje plik Python, automatycznie uruchom odpowiednie testy. Pozwala to wychwycić regresje natychmiast, zamiast odkrywać je po trzech kolejnych edycjach plików.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]] && [[ \"$FILE_PATH\" != *test_* ]]; then TEST_FILE=\"tests/test_$(basename \"$FILE_PATH\")\"; if [[ -f \"$TEST_FILE\" ]]; then python -m pytest \"$TEST_FILE\" -x --tb=short 2>&1 | tail -20; fi; fi'"
}
]
}
]
}
}
Hook sprawdza, czy edytowany plik jest plikiem źródłowym Python (nie plikiem testowym), szuka odpowiedniego pliku testowego z konwencją nazewnictwa test_ i uruchamia go, jeśli istnieje. Flaga -x zatrzymuje się przy pierwszym niepowodzeniu, a tail -20 utrzymuje zwięzły output.
Uwaga: Ten hook zakłada płaski katalog tests/ z konwencją nazewnictwa test_. Dostosuj konstrukcję ścieżki dla zagnieżdżonych katalogów testowych lub innych konwencji nazewnictwa.
Ten hook jest szczególnie wartościowy podczas sesji refaktoryzacji, gdy Claude modyfikuje wiele plików. Natychmiastowa pętla zwrotna zapobiega kaskadzie „napraw jedno, zepsuj trzy inne”, która pojawia się, gdy testy uruchamiane są dopiero na końcu.
4. Powiadomienie po zakończeniu sesji
Długotrwałe sesje Claude Code mogą trwać wiele minut. Zamiast obserwować terminal, otrzymaj powiadomienie po zakończeniu sesji.
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code session ended\" with title \"Claude Code\"'"
}
]
}
]
}
}
Ten przykład dla macOS używa osascript do wyświetlenia natywnego powiadomienia. Na Linuksie zamień linię osascript na notify-send "Claude Code" "Session ended". Dla powiadomień na Slacku użyj webhooka:
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
-d '{"text": "Claude Code session ended"}'
Używam wariantu ze Slackiem dla zadań w tle uruchamianych za pomocą & <task> (tryb pracy w tle Claude Code). Powiadomienie systemowe obsługuje sesje interaktywne.
5. Kontrola jakości przed commitem
Zanim Claude uruchomi git commit, sprawdź, czy kod przechodzi linting. Pozwala to wychwycić problemy, których samo formatowanie nie wykryje: nieużywane importy, niezdefiniowane zmienne, błędy typów.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"^git\\s+commit\"; then if ! LINT_OUTPUT=$(ruff check . --select E,F,W 2>&1); then echo \"LINT FAILED -- fix before committing:\" >&2; echo \"$LINT_OUTPUT\" >&2; exit 2; fi; fi'"
}
]
}
]
}
}
Ten hook aktywuje się tylko wtedy, gdy polecenie Bash zaczyna się od git commit. Uruchamia ruff (szybki linter Python) z regułami błędów, pyflakes i ostrzeżeń. Jeśli wykryje jakiekolwiek problemy, commit jest blokowany (exit 2), a Claude widzi wynik lintingu, co zazwyczaj powoduje, że naprawia problemy i ponawia próbę.
Można nakładać wiele warstw kontroli jakości: mypy do sprawdzania typów, bandit do skanowania bezpieczeństwa lub własne skrypty walidacyjne projektu. Hooki PreToolUse na poleceniach Bash dają programowalną bramkę przed każdą akcją w powłoce.
Wskazówki dotyczące debugowania hooków
Hooki częściej, niż można by się spodziewać, zawodzą po cichu. Pięć technik, których używam do ich debugowania:
- Najpierw przetestuj skrypty niezależnie. Przekaż przykładowy JSON do skryptu ręcznie:
echo '{"tool_input":{"command":"git commit -m test"}}' | bash your-hook.sh. Jeśli nie działa poza Claude Code, nie zadziała wewnątrz. - Używaj stderr do debugowania. Wszystko, co hook wypisze na stderr, pojawia się w kontekście Claude. Pisz
echo "DEBUG: matched $CMD" >&2podczas tworzenia, a potem usuń, gdy hook jest stabilny. - Uważaj na błędy jq. Jeśli ścieżka JSON jest niepoprawna,
jqcicho zwracanulli warunki nie zadziałają. Testuj wyrażeniajqna prawdziwym wejściu narzędzia. - Weryfikuj kody wyjścia. Exit 2 blokuje akcje. Exit 1 tylko ostrzega. Hook PreToolUse, który przypadkowo używa
exit 1, nie zapewnia żadnego egzekwowania, choć wygląda, jakby działał. Zacznij od trybu permisywnego (domyślnie exit 0) i używajexit 2tylko dla konkretnych blokowanych wzorców. - Dbaj o szybkość hooków. Hooki działają synchronicznie. Hook, który zajmuje 5 sekund, dodaje 5 sekund do każdego dopasowanego użycia narzędzia. Utrzymuję wszystkie moje hooki poniżej 2 sekund, najlepiej poniżej 500 milisekund.
Kolejne kroki
Te pięć hooków obejmuje podstawy: formatowanie, bezpieczeństwo, testowanie, powiadomienia i bramki jakości. Gdy opanujesz te wzorce, możesz budować hooki do wstrzykiwania kontekstu (dodawanie instrukcji specyficznych dla projektu na początku sesji), zabezpieczeń przed rekurencją (zapobieganie nieskończonym pętlom subagentów) i orkiestracji workflow (łączenie procesów wieloetapowych).
Architekturę hooków, wszystkie 17 zdarzeń cyklu życia i zaawansowane wzorce znajdziesz w sekcji hooków mojego pełnego przewodnika: Claude Code Guide: How Do Hooks Work?
Napisałem również o historiach powstania moich 95 produkcyjnych hooków w artykule Claude Code Hooks: Why Each of My 95 Hooks Exists, który opisuje incydenty, które zmotywowały do stworzenia każdego z nich.
FAQ
Czy hooki mogą zablokować Claude Code przed uruchomieniem polecenia?
Tak. Hooki PreToolUse blokują dowolną akcję narzędzia, kończąc działanie z kodem 2. Claude Code anuluje oczekującą akcję i pokazuje modelowi output stderr hooka. Kod wyjścia 1 to nieblokujący błąd hooka — akcja nadal jest wykonywana. To rozróżnienie jest kluczowe: każdy hook bezpieczeństwa musi używać exit 2, nie exit 1. Claude widzi powód odrzucenia i sugeruje bezpieczniejszą alternatywę.
Gdzie umieścić pliki konfiguracyjne hooków?
Konfiguracje hooków umieszcza się w .claude/settings.json dla hooków na poziomie projektu (commitowane do repozytorium, współdzielone z zespołem) lub ~/.claude/settings.json dla hooków na poziomie użytkownika (osobiste, stosowane do każdego projektu). Hooki na poziomie projektu mają pierwszeństwo, gdy istnieją oba pliki. Zalecam używanie ścieżek bezwzględnych do plików skryptów, aby uniknąć problemów z katalogiem roboczym.
Czy hooki działają z subagentami?
Tak. Hooki uruchamiają się również dla akcji subagentów. Jeśli Claude uruchomi subagenta za pomocą narzędzia Agent, hooki PreToolUse i PostToolUse wykonują się dla każdego narzędzia używanego przez subagenta. Bez tego zachowania subagent mógłby ominąć bramki bezpieczeństwa. Zdarzenie SubagentStop pozwala uruchomić czyszczenie lub walidację po zakończeniu zadania przez subagenta.