Przejdź do treści

Hooki Claude Code, które po cichu nic nie robią

Dla CTO i programistówHarness i kontrole

Filip PapranecZałożyciel Digitaliko

Każde uruchomienie przeszło. Hook nic nie zrobił. Ilustracja: plik ustawień, w którym polecenie hooka kończy się na 2>/dev/null || true, obok rzędu zielonych checków, które niczego nie dowodzą.Każde uruchomienie przeszło. Hook nic nie zrobił. Ilustracja: plik ustawień, w którym polecenie hooka kończy się na 2>/dev/null || true, obok rzędu zielonych checków, które niczego nie dowodzą.

Zespoły dodają hooki Claude Code, żeby agenci AI nie narobili szkód, a potem zakładają, że działają. Hook może przechodzić przy każdym uruchomieniu i nic nie robić. Hook, który po cichu nic nie robi, jest gorszy niż brak hooka, bo wszyscy przestają patrzeć. Rozwiązaniem są cztery tanie testy, które może skopiować każdy zespół.

Krótka odpowiedź: hook milknie, gdy jego wyjście nigdzie nie trafia, gdy jego ścieżka nie istnieje albo gdy ktoś nadpisuje jego kod wyjścia. Proszę sprawdzać wszystkie trzy w CI. A najważniejszy hook proszę podpiąć pod zdarzenie Stop, żeby agent nie mógł powiedzieć „gotowe”, dopóki testy są czerwone.

Jak hooki Claude Code w ogóle cokolwiek blokują?

Hook to polecenie powłoki, które Claude Code uruchamia przy zdarzeniu. PreToolUse działa przed wywołaniem narzędzia, Stop wtedy, gdy agent chce skończyć. Polecenie dostaje zdarzenie jako obiekt JSON na stdin.

Decyduje kod wyjścia:

  • Exit 0 przepuszcza.
  • Exit 2 blokuje. To, co hook zapisze na stderr, wraca do agenta jako powód.
  • Każdy inny kod to błąd nieblokujący. Claude Code pracuje dalej.

To cały kontrakt. Wszystko, co psuje się w hookach, psuje się w jego obrębie.

Dlaczego hook po cichu nic nie robi?

Hook może milczeć od pierwszego dnia i nikt tego nie zauważy. Zwykle z trzech powodów, z których każdy osobno jest niewinny:

  • Wyciszony. Hook zapisuje swój jedyny komunikat na stderr, ale polecenie w ustawieniach kończy się na 2>/dev/null. Agent nic nie słyszy.
  • Nieistniejący. Hook używa ścieżki względnej. W sesji uruchomionej w zagnieżdżonym repozytorium ta ścieżka nie istnieje. Exit 127, polecenie nie znalezione.
  • Zamaskowany. || true na końcu zamienia 127 na 0. Przy zdarzeniu blokującym tak samo zamienia 2 na 0. Każde uruchomienie „przechodzi”.
Po lewej hook, który milczał: polecenie bash tools/hooks/guard.sh 2>/dev/null || true i trzy przyczyny: ścieżka względna (exit 127), stderr do /dev/null oraz || true, które zamienia kod wyjścia na 0. Po prawej hook Stop w czterech krokach: stop_hook_active, typecheck, lint i testy, a przy czerwonym wyniku decision: block z logiem błędu.Po lewej hook, który milczał: polecenie bash tools/hooks/guard.sh 2>/dev/null || true i trzy przyczyny: ścieżka względna (exit 127), stderr do /dev/null oraz || true, które zamienia kod wyjścia na 0. Po prawej hook Stop w czterech krokach: stop_hook_active, typecheck, lint i testy, a przy czerwonym wyniku decision: block z logiem błędu.

Nikt tego nie zauważa, bo milczący hook wygląda dokładnie tak samo jak zdrowy. Znajdzie go dopiero analiza sesji Claude Code albo test w CI.

Zepsuty wzorzec, w uproszczeniu:

{ "type": "command", "command": "bash tools/hooks/guard.sh 2>/dev/null || true" }

Poprawiony, z naszego publicznego startera:

{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/push-gate.sh" }

Jakie cztery testy wykrywają milczący hook?

Proszę dodać do walidatora test, który działa w CI każdego repozytorium. Cztery grepy po .claude/settings.json, nic nie jest uruchamiane:

TestCo wykrywaPoprawka
Skrypt istniejeŚcieżkę, która nie istnieje"$CLAUDE_PROJECT_DIR"/... albo ścieżka bezwzględna
Skrypt jest wykonywalnyHook, który działa tylko przypadkiemchmod +x
stderr nie jest odrzucany, gdy skrypt na niego piszeWyciszony hookUsunąć 2>/dev/null
Hook PreToolUse nie kończy się na || trueBlokadę zamienioną w przepustkęUsunąć || true

Proszę przetestować ten test na prawdziwym błędzie: zepsuta konfiguracja musi zgłosić błędy krytyczne, poprawiona musi przejść.

Nie każde || true jest błędem. Hook powitalny na SessionStart może celowo używać 2>/dev/null || true, bo powitanie nigdy nie może zepsuć sesji. Test powinien zgłaszać tylko miejsca, w których oczekiwana jest blokada.

Lekcja w jednym zdaniu, tak jak stoi w naszym hooku przeciw podpisom AI: „A rule is a request. This is the lock.” Reguła jest prośbą, hook jest zamkiem. A zamek potrzebuje własnego testu.

Jeszcze niedawno agent miał swoje reguły tylko w plikach z regułami i czasem ich nie przestrzegał. Zamieniliśmy je więc w testy, których nie może obejść.

Filip Papranec
Filip Papranec
Założyciel Digitaliko

Który hook naprawdę blokuje?

Hook Stop. Agent, który mówi „gotowe”, gdy testy są czerwone, kosztuje seniora jedną rundę review. Hook Stop zmusza agenta, żeby najpierw to naprawił.

Nasza wersja z publicznego startera, lekko skrócona:

#!/bin/bash
# Stop hook: the agent can't finish while the checks are red.
in=$(cat)
[ "$(printf '%s' "$in" | jq -r '.stop_hook_active // false')" = true ] && exit 0
cd "${CLAUDE_PROJECT_DIR:-.}" 2>/dev/null || exit 0
out=$(GATE_SKIP_INSTALL=1 GATE_SCRIPTS="typecheck lint test" bash scripts/gate.sh checks 2>&1) && exit 0
jq -n --arg r "Checks are red. Fix them before you say the task is done:
$out" '{decision: "block", reason: $r}'

Krok po kroku robi to:

  1. Czyta stop_hook_active

    Jeśli jest true, agent już kontynuuje przez ten hook. Hook go przepuszcza, żeby nie kręcił się w pętli.

  2. Uruchamia testy

    Typecheck, lint i testy, te same co w CI.

  3. Blokuje z podaniem powodu

    Gdy testy są czerwone, zwraca decision: block z logiem błędu jako powodem.

  4. Agent usuwa przyczynę

    Czyta log, naprawia błąd i znów próbuje skończyć.

Blokuje JSON-em, nie kodem wyjścia. Agent pracuje dalej, a powód, który dostaje, to dokładnie to, co się nie powiodło.

To wczesne ostrzeżenie, nie gate. Rozstrzygający test powinien uruchomić się ponownie po sesji, w kodzie, którego agent nie może dotknąć. Hook oszczędza tylko jedną rundę.

Na co uważać w hooku Stop?

Hook Stop też może mieć własny cichy błąd. Stały, minimalny PATH bez katalogów z narzędziami użytkownika sprawia, że w repozytoriach z Bun brakuje narzędzi potrzebnych do testów. Gate szukany w jednym stałym miejscu działa tylko na jednej maszynie. Proszę ustawić pełny PATH i szukać gate'u względem samego hooka.

Hooki Claude Code w zespole: od czego zacząć?

Proszę sprawdzić, co z tego Państwa repozytoria już mają. Każdy punkt to kilka linii w ustawieniach albo w CI:

Trzeci punkt najłatwiej przeoczyć. Nasz hook przeciw podpisom AI czyta wejście przez jq, potem awaryjnie przez python3, a następnie przez sed. Brak jq nigdy nie może zamienić zamka w ciche przepuszczanie. Ścieżki względne z kolei psują się w zagnieżdżonych repozytoriach i worktree.

Mogą Państwo zacząć od Claude Code production starter: push gate, skan sekretów i hook Stop, na licencji MIT.

Gdzie hooki stoją obok plików z regułami, wyjaśniamy w Reguły dla kodu AI w zespole. Jak wprowadzić to w całym zespole, opisujemy w Dobre praktyki Claude Code dla zespołu: 7 zasad, które działają. Co agent może robić bez pytania i kiedy pomijanie uprawnień jest bezpieczne, opisujemy w Claude Code dangerously skip permissions.

Chcą Państwo hooków, które działają w Państwa repozytoriach?

Hooki, gate'y i CI konfigurujemy bezpośrednio w repozytorium zespołu w ramach AI-native SDLC dla zespołów deweloperskich. Państwa zespołowi brakuje raczej rąk do pracy niż reguł? Proszę zobaczyć seniora programistę dla Państwa zespołu. Większość naszego kodu pisze AI. Każdą zmianę sprawdza senior i to on odpowiada za produkcję. Od 2023 roku dostarczyliśmy ponad 15 projektów produkcyjnych.

Odezwiemy się w ciągu dwóch dni roboczych z kilkoma pytaniami o Państwa pracę. Potem ustalimy, czym zajmiemy się najpierw.

Jeśli wolą Państwo najpierw porozmawiać, proszę zarezerwować 15-minutową rozmowę wstępną.

Kiedy ostatnio sprawdzali Państwo, czy Państwa hooki nadal blokują?

W skrócie

  • Hook milknie, gdy jego stderr jest odrzucany, jego ścieżka nie istnieje albo || true nadpisuje jego kod wyjścia.
  • Cztery grepy po .claude/settings.json w CI wykrywają wszystkie trzy przypadki.
  • Hook Stop blokuje „gotowe”, dopóki testy są czerwone, i blokuje tylko raz.
  • Rozstrzygający test uruchamia się ponownie w CI, gdzie agent nie może go zmienić.

Najczęstsze pytania

Czym są hooki Claude Code?

To polecenia powłoki, które Claude Code uruchamia przy zdarzeniach, np. PreToolUse przed wywołaniem narzędzia albo Stop, gdy agent chce skończyć. Hook dostaje zdarzenie jako JSON na stdin i decyduje kodem wyjścia lub odpowiedzią JSON.

Jak hook Claude Code blokuje polecenie?

Kodem wyjścia 2. Claude Code blokuje wtedy akcję i przekazuje agentowi jako powód to, co hook zapisał na stderr. Exit 0 ją przepuszcza. Każdy inny kod to błąd nieblokujący i agent pracuje dalej.

Dlaczego mój hook Claude Code się nie uruchamia?

Najczęściej przez ścieżkę względną, która nie istnieje w katalogu sesji, przez stderr odrzucany przez 2>/dev/null albo przez || true na końcu, które nadpisuje kod wyjścia. Proszę zaczynać ścieżkę od "$CLAUDE_PROJECT_DIR" i sprawdzać konfigurację hooków w CI.

Co oznacza stop_hook_active?

Ma wartość true, gdy agent już kontynuuje pracę, bo zablokował go hook Stop. Jeśli jest true, proszę od razu zakończyć. Inaczej test, którego agent nie potrafi naprawić, będzie go blokował bez końca.

Czy hook Stop wystarczy jako quality gate?

Nie. To wczesne ostrzeżenie w trakcie sesji. Te same testy proszę uruchomić ponownie w CI i w ochronie gałęzi, gdzie agent nie może ich zmienić.

Zapytaj AI o ten artykuł

AI przy odpowiedzi wykorzysta też nasze inne artykuły i doświadczenia z prawdziwych projektów.

ChatGPTClaudePerplexity
Engineering

Kolejne artykuły

Harness i kontrole

Context engineering dla agentów kodujących: co trzymać w repo

Context engineering decyduje, co widzi agent AI piszący kod w trakcie pracy. Co trafia do AGENTS.md lub CLAUDE.md, które reguły ładują się tylko tam, gdzie obowiązują, jak zadanie staje się kontekstem i jak utrzymać całość w małym rozmiarze.

Harness i kontrole

Playwright MCP: niech agent AI udowodni, że to działa

Playwright MCP daje agentowi AI prawdziwą przeglądarkę. Jak go używamy, żeby agent sprawdził własną zmianę w działającej aplikacji i oddał PASS lub FAIL ze zrzutami ekranu, co wychwytuje, a czego nie widzą testy jednostkowe, i kiedy lepszym narzędziem są zwykłe skrypty Playwright.

Harness i kontrole

Code review z AI: Claude Code w CI i czego nie widzi

Jak uruchamiamy code review z Claude Code przy każdym pull requeście jedną wielokrotnego użytku GitHub Action: czego szuka recenzent AI, jak świecił na zielono i milczał, czego nie wyłapuje i dlaczego zatwierdza tylko człowiek.