Jak działają hooki w Claude Code: konfiguracja i zasady
Dowiedz się, jak automatyzować zadania w Claude Code za pomocą hooków. Wyjaśniamy obsługę zdarzeń, interpretację kodu wyjścia 2 oraz wpływ skryptów na bezpieczeństwo narzędzia.
Czym jest hook w Claude Code
Hooki w Claude Code to polecenia powłoki, które Claude Code wykonuje samodzielnie w określonych momentach swojego cyklu życia. Na tym polega zasadnicza różnica między hookiem a plikiem reguł. Instrukcja w CLAUDE.md jest wskazówką, którą model rozważa w kontekście wszystkich innych wytycznych. Hook jest kodem, który wykonuje się niezależnie od decyzji modelu. Jeśli agent notorycznie pomija formater, o którym wspomniano dwukrotnie, nie potrzeba bardziej stanowczych instrukcji. Potrzebny jest hook.
Mechanizm ten jest prosty. Polecenie rejestruje się w pliku ustawień pod nazwą zdarzenia. Gdy zdarzenie wystąpi, Claude Code uruchamia polecenie i przesyła dane zdarzenia do jego standardowego wejścia (stdin) w formacie JSON (JavaScript object notation). Polecenie odczytuje te dane, wykonuje zadanie i zwraca status wyjścia. Kod wyjścia 2 z hooka PreToolUse anuluje wywołanie narzędzia przed jego uruchomieniem, a treść zapisana przez skrypt do standardowego wyjścia błędów (stderr) jest przekazywana modelowi jako uzasadnienie.
Nazwy zdarzeń oraz pól pochodzą z dokumentacji hooków Claude Code, zweryfikowanej w sierpniu 2026 roku dla wersji 2.1.232. Ten interfejs zmienia się dynamicznie, dlatego przed skopiowaniem kodu JSON z jakiegokolwiek wpisu, w tym z niniejszego, należy sprawdzić dokumentację dla używanej wersji. Własną konfigurację można wyświetlić za pomocą claude --version.
Lokalizacja konfiguracji hooków
Hook jest blokiem JSON w pliku ustawień. Może on znajdować się w jednej z sześciu lokalizacji, a zakres pliku wyznacza zakres działania hooka.
~/.claude/settings.json: każdy projekt na danej maszynie, niedostępny dla innych użytkowników..claude/settings.json: pojedynczy projekt, zatwierdzony w repozytorium, dzięki czemu każdy użytkownik po sklonowaniu otrzymuje ten hook..claude/settings.local.json: pojedynczy projekt, tylko na danej maszynie.- Ustawienia polityki zarządzanej: w skali całej organizacji, konfigurowane przez administratora.
hooks/hooks.jsonwewnątrz wtyczki, aktywne podczas działania tej wtyczki.- Frontmatter umiejętności lub subagenta, aktywne podczas działania tego komponentu.
Wpisy hooków z tych plików łączą się, zamiast nadpisywać się nawzajem. Plik ustawień projektu dodaje swoje hooki do tych z ustawień użytkownika, zamiast je zastępować, dzięki czemu jedno zdarzenie może obsługiwać kilka hooków z różnych plików. Ustawienie "disableAllHooks": true wyłącza je, z jednym wyjątkiem: hooki z ustawień polityki zarządzanej działają nadal, chyba że to ustawienie zostanie zastosowane również w ramach polityki zarządzanej.
Uruchom /hooks wewnątrz sesji, aby wyświetlić listę wszystkich aktualnie zarejestrowanych hooków, pogrupowanych według zdarzeń, wraz ze źródłowym plikiem i dopasowaniem dla każdego z nich. Menu jest tylko do odczytu, więc modyfikacja hooka wymaga edycji pliku ustawień. Mechanizm monitorowania plików zazwyczaj wykrywa zmiany bez konieczności restartu.
Jakie zdarzenia hooków istnieją w Claude Code
Wersja 2.1.232 wymienia trzydzieści jeden zdarzeń, od SessionStart do SessionEnd, obejmujących kompaktowanie, subagenty, drzewa robocze oraz pliki konfiguracyjne. Praca serwerowa wykorzystuje tylko część z nich.
PreToolUse: przed wykonaniem wywołania narzędzia. Jest to zdarzenie, które może blokować działanie.PostToolUse: po pomyślnym zakończeniu wywołania narzędzia.PostToolUseFailureuruchamia się w przypadku niepowodzenia, więc hook wymagający monitorowania każdego wyniku musi obsługiwać oba.PermissionRequest: gdy wywołanie narzędzia wymaga decyzji o uprawnieniach, czyli w momencie, w którym pojawiłby się monit o zatwierdzenie.UserPromptSubmit: po wysłaniu promptu, zanim Claude go przetworzy. Wszystko, co ten hook wypisze na stdout, zostaje dodane do kontekstu modelu.SessionStartorazSessionEnd: na każdym z końców sesji.SessionStarturuchamia się również po kompaktowaniu, przy wartości dopasowaniacompact.Stop: gdy Claude zakończy odpowiadanie. Dzieje się to raz na turę, a nie raz na ukończone zadanie.
Każda grupa zawiera matcher, który decyduje, które wystąpienia uruchamiają dany hook. W przypadku zdarzeń narzędziowych filtrowanie odbywa się według nazwy narzędzia, więc "Edit|Write" uruchamia się tylko przy edycji plików i w żadnym innym przypadku. Dopasowania (matchers) uwzględniają wielkość liter. Puste dopasowanie uruchamia hook przy każdym wystąpieniu. Narzędzia z serwera MCP (model context protocol) są nazywane mcp__<server>__<tool>, więc dopasowanie "mcp__github__.*" przechwytuje narzędzia jednego serwera, pozostawiając pozostałe bez zmian.
Hooki Stop zawierają pułapkę, o której warto wiedzieć przed rozpoczęciem pisania. Hook Stop, który blokuje działanie, odsyła model z powrotem do pracy, a Claude Code nadpisuje hook po ośmiu kolejnych blokadach. Należy odczytać pole stop_hook_active z danych wejściowych hooka i zakończyć działanie z kodem 0, gdy jest ono prawdziwe; w przeciwnym razie hook będzie zapętlał się do momentu osiągnięcia tego limitu.
Dane otrzymywane przez hook na standardowym wejściu
Gdy Claude przygotowuje się do uruchomienia npm test, hook PreToolUse na Bash odczytuje następujące dane ze standardowego wejścia (stdin):
{
"session_id": "abc123",
"cwd": "/home/deploy/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}Każde zdarzenie zawiera session_id, cwd, permission_mode, transcript_path oraz hook_event_name. Zdarzenia narzędziowe dodają tool_name, tool_input oraz tool_use_id. Pozostałe zdarzenia posiadają własne pola: UserPromptSubmit otrzymuje tekst prompt, a SessionStart otrzymuje source typu startup, resume, clear, compact lub fork.
jq jest standardowym narzędziem do odczytu tych danych wewnątrz skryptu powłoki, a minimalny obraz serwera go nie zawiera. Należy je zainstalować za pomocą sudo apt install -y jq w systemach Ubuntu i Debian.
Wpływ kodu wyjścia na wywołanie narzędzia w trakcie działania
Istnieją trzy możliwe rezultaty.
- Kod wyjścia 0 oznacza, że hook nie zgłasza zastrzeżeń. W przypadku
PreToolUsenie jest to równoznaczne z zatwierdzeniem, a standardowy proces weryfikacji uprawnień nadal przebiega. W przypadkuUserPromptSubmitorazSessionStart, zawartość stdout jest dodawana do kontekstu modelu. - Kod wyjścia 2 blokuje działanie w zdarzeniach, które można zablokować, w tym
PreToolUse, a zawartość stderr staje się powodem wyświetlanym modelowi. W zdarzeniach, których nie można zablokować, takich jakPostToolUse, blokada jest ignorowana, chociaż stderr nadal trafia do modelu jako informacja zwrotna. - Każdy inny kod wyjścia jest błędem nieblokującym. Działanie jest kontynuowane. Zapis sesji zawiera powiadomienie o błędzie hooka wraz z pierwszą linią stderr po tekście
Failed with non-blocking status code:.
W przypadku operacji wykraczających poza blokowanie lub brak reakcji, należy użyć kodu wyjścia 0 i wypisać obiekt JSON do stdout. Hook PreToolUse podejmuje decyzję za pomocą permissionDecision:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database drops go through a migration, not through the agent."
}
}"allow" pomija interaktywny monit, "deny" anuluje wywołanie i przesyła powód do modelu, a "ask" wyświetla monit w standardowy sposób. Należy wybrać jeden styl dla każdego hooka. Łączenie kodu wyjścia 2 z decyzją JSON w stdout prowadzi do niejednoznacznych wyników.
Gdy kilka hooków pasuje do jednego zdarzenia, są one uruchamiane równolegle i każdy z nich kończy swoje działanie. Kod wyjścia deny z jednego hooka nie zatrzymuje pozostałych, więc hook logujący zapisze swoją linię nawet wtedy, gdy hook zabezpieczający odrzuci to samo wywołanie. Claude Code scala odpowiedzi i stosuje najbardziej restrykcyjną z nich, zgodnie z kolejnością: odrzuć, odrocz, zapytaj, zezwól.
Przykład 1: blokowanie destrukcyjnego polecenia przed jego wykonaniem
Zapisz poniższą treść jako .claude/hooks/block-destructive.sh w swoim projekcie:
#!/bin/bash
# Deny a Bash tool call whose command matches a banned pattern.
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
for pattern in 'rm -rf /' 'mkfs' 'dd if=' 'DROP TABLE'; do
if printf '%s' "$COMMAND" | grep -qiF -- "$pattern"; then
echo "Blocked by policy: the command matches '$pattern'. A human runs this one." >&2
exit 2
fi
done
exit 0Nadaj uprawnienia do wykonywania, a następnie zarejestruj skrypt w PreToolUse w .claude/settings.json:
chmod +x .claude/hooks/block-destructive.sh{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
"timeout": 10,
"statusMessage": "Checking the command against policy"
}
]
}
]
}
}Przetestuj skrypt ręcznie, zanim zaczniesz mu ufać, ponieważ hook, który zawiesza się na własnych danych wejściowych, domyślnie zezwala na działanie:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /var/lib/postgresql"}}' \
| .claude/hooks/block-destructive.sh
echo $?Powinieneś zobaczyć linię Blocked by policy: na stderr oraz kod wyjścia 2. Podaj nieszkodliwe polecenie, takie jak ls -la; nie powinieneś otrzymać żadnego wyjścia, a kod wyjścia powinien wynosić 0. W sesji odrzucone wywołanie pojawi się w transkrypcie wraz z Twoim komunikatem jako przyczyną, a model odczyta tę wiadomość i dostosuje swoje działanie.
Jedna właściwość sprawia, że warto to wdrożyć: hooki PreToolUse uruchamiają się przed sprawdzeniem trybu uprawnień, w każdym trybie uprawnień, więc odmowa obowiązuje nawet w bypassPermissions. To właśnie czyni hook użytecznym w połączeniu z trybem automatycznym Claude Code i jego ustawieniami uprawnień, gdzie monity są ograniczone, ale hook nadal działa.
Bądź świadomy ograniczeń tego rozwiązania. Dopasowywanie wzorców w ciągu polecenia to zabezpieczenie przed nieostrożnością agenta, a nie bariera przed celowym działaniem, ponieważ to samo polecenie można zapisać w formie, której Twój grep nigdy nie wykryje. Twarde reguły powinny znajdować się w systemie uprawnień oraz na koncie, na którym uruchomiony jest proces.
Przykład 2: formatowanie i linting po każdej edycji
PostToolUse z dopasowaniem Edit|Write uruchamia się po każdym narzędziu do edycji plików. Zapisz to jako .claude/hooks/after-edit.sh:
#!/bin/bash
# Format the edited file, then report lint failures back to the model.
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
case "$FILE" in
*.py)
ruff format "$FILE" >/dev/null 2>&1
if ! ruff check "$FILE" >&2; then
exit 2
fi
;;
*.sh)
if ! shellcheck "$FILE" >&2; then
exit 2
fi
;;
esac
exit 0{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/after-edit.sh",
"timeout": 60
}
]
}
]
}
}Poproś Claude o dodanie niepoprawnie wciętej funkcji do pliku Python, a następnie otwórz ten plik. Zostanie on sformatowany. Jest to potwierdzenie, że hook zadziałał, ponieważ pomyślne wykonanie hooka nie generuje żadnych komunikatów w konwersacji.
Kod wyjścia 2 w tym miejscu nie cofa żadnych zmian. PostToolUse uruchamia się po zakończeniu działania narzędzia, więc edycja znajduje się na dysku w obu przypadkach. Kod wyjścia 2 sprawia, że wynik ruff check trafia do modelu jako informacja zwrotna, dzięki czemu model naprawia błąd, który właśnie wprowadził, zamiast przechodzić dalej. Na tym polega różnica między błędem lintingu wykrytym w momencie commitu a błędem, który agent naprawia w tej samej turze.
W tym przypadku istotne są dwa limity dopasowania. Edit|Write nie widzi plików zmienionych przez polecenie powłoki, a Claude wystarczająco często zapisuje pliki przez Bash, aby ta luka była odczuwalna. Aby uzyskać pokrycie dla każdego wywołania, dopasuj również Bash i spraw, aby skrypt wyświetlał zmienione pliki za pomocą git status --porcelain. Aby uzyskać pokrycie raz na turę, umieść skanowanie w hooku Stop.
Przykład 3: rejestrowanie każdego wywołania narzędzia do celów audytu
Pusty dopasowywacz w PostToolUse uruchamia się przy każdym narzędziu. Przesyłanie rekordu do dziennika systemowego zamiast do pliku w katalogu domowym chroni go przed dostępem z poziomu powłoki agenta:
{
"hooks": {
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{time: now|todate, session: .session_id, cwd: .cwd, tool: .tool_name, input: .tool_input}' | logger -t claude-code -p local0.info"
}
]
}
]
}
}Odczytaj dane za pomocą journalctl -t claude-code -o cat | tail -n 5. Powinna pojawić się jedna linia JSON na każde wywołanie narzędzia, z najnowszymi wpisami na końcu. Brak danych oznacza, że skrypt typu hook nie został uruchomiony; poniższa sekcja rozwiązywania problemów opisuje przyczyny.
Dodaj ten sam blok pod PostToolUseFailure, aby przechwytywać wywołania, które zakończyły się niepowodzeniem, ponieważ PostToolUse uruchamia się tylko w przypadku sukcesu, a nieudana komenda jest zazwyczaj tą istotną. Powodem użycia logger zamiast dopisywania do pliku w katalogu domowym jest kwestia uprawnień: hook działa jako ten sam użytkownik co powłoka agenta, więc wszystko, do czego użytkownik może dopisywać, może również wyczyścić. Dziennik jest zapisywany przez systemd-journald na własnym koncie.
Czas trwania skryptu typu hook
The data behind this chart
[
{
"label": "command, http or mcp_tool hook",
"default_timeout_seconds": 600
},
{
"label": "agent hook",
"default_timeout_seconds": 60
},
{
"label": "prompt hook",
"default_timeout_seconds": 30
},
{
"label": "command hook on UserPromptSubmit",
"default_timeout_seconds": 30
},
{
"label": "command hook on MessageDisplay",
"default_timeout_seconds": 10
},
{
"label": "any hook on SessionEnd",
"default_timeout_seconds": 1.5
}
]Skrypt typu hook otrzymuje domyślnie 600 sekund, co odpowiada dziesięciu minutom. Niektóre zdarzenia drastycznie skracają ten czas. Skrypty SessionEnd współdzielą budżet wynoszący łącznie 1.5 sekund, dlatego czyszczenie sesji musi przebiegać szybko. Ustawienie wyższej wartości timeout dla danego hooka zwiększa ten współdzielony budżet do maksymalnie 60 sekund.
Hook, który przekroczy limit czasu, zostaje anulowany i nie generuje żadnej decyzji. W przypadku mechanizmu zabezpieczającego PreToolUse oznacza to brak blokady: wywołanie narzędzia jest kontynuowane zgodnie ze standardowym procesem uprawnień. Z tego powodu skrypty zabezpieczające powinny być lekkie. W przypadku zadań długotrwałych, na które nikt nie oczekuje, takich jak przesyłanie logów, należy ustawić "async": true. Wówczas hook uruchomi się w tle, nie wstrzymując wywołania narzędzia.
Hooki, pliki reguł, umiejętności i serwery MCP
Cztery elementy są często ze sobą mylone, ponieważ każdy z nich zmienia sposób działania agenta. Tylko jeden z nich przestaje być sugestią.
Plik reguł (CLAUDE.md lub plik w .claude/rules/) to tekst ładowany do kontekstu modelu. Kształtuje on zachowanie, ale niczego nie wymusza. W obliczu długiej konwersacji, dużego diffa i nowego żądania użytkownika, jedna linijka pliku może zostać zignorowana. Jest to zwykły mechanizm stojący za agentami ignorującymi zapisane instrukcje.
Umiejętność (skill) to folder z instrukcjami i skryptami, które model ładuje, gdy uzna je za istotne. Ta ocena jest istotą umiejętności, ale stanowi również jej ograniczenie: ostateczną decyzję nadal podejmuje model. Obie strony tego zagadnienia można zaobserwować w umiejętności takiej jak Ponytail, która nakłania agenta do wprowadzania najmniejszych działających zmian, ponieważ kształtuje ona sposób podejścia do całego zadania w sposób, w jaki żaden hook nie mógłby tego zrobić, i działa tylko wtedy, gdy model zdecyduje się ją załadować.
Serwer MCP (model context protocol) dostarcza modelowi nowe narzędzia do wywołania. Rozszerza on zakres dostępności agenta. Nie zmusza jednak agenta do korzystania z czegokolwiek i jest osobnym procesem, który należy obsługiwać, co stanowi odrębne zadanie: zobacz uruchamianie serwerów MCP na VPS.
Hook jest jedynym z tych czterech elementów, który działa bez wyboru ze strony modelu. Plików reguł należy używać do określania preferencji, a umiejętności do procedur, których model powinien przestrzegać w odpowiednich sytuacjach. Hooka należy użyć w kroku, który musi wystąpić za każdym razem, lub w przypadku rzeczy, która nigdy nie może się wydarzyć. Głębokie porównanie, w tym sytuacje, w których umiejętność przewyższa plik reguł, znajduje się w porównaniu umiejętności, MCP i plików reguł.
Wtyczka (plugin) to raczej sposób pakowania niż piąty mechanizm. Łączy ona hooki wraz z umiejętnościami w jedną instalowalną jednostkę, dzięki czemu zespół może dostarczyć te same zabezpieczenia na każdą maszynę: zobacz jak działają wtyczki Claude Code.
Decyzja dotycząca bezpieczeństwa na współdzielonym VPS
Hook to kod wyzwalany przez agenta, który uruchamia się z uprawnieniami użytkownika inicjującego Claude Code. Dziedziczy on środowisko oraz uprawnienia do plików tego użytkownika. Na laptopie jest to kwestia przepływu pracy. Na serwerze VPS, gdzie agent działa bez nadzoru, jest to kwestia bezpieczeństwa składająca się z czterech praktycznych elementów.
Hook w repozytorium to kod, którego nie napisałeś. .claude/settings.json jest zatwierdzony w repozytorium, więc sklonowanie go i rozpoczęcie sesji wewnątrz może zarejestrować hooki, które były częścią repozytorium. Claude Code blokuje hooki projektowe za oknem dialogowym zaufania do obszaru roboczego dla danego folderu, co oznacza, że zaakceptowanie zaufania jest momentem, w którym decydujesz o ich uruchomieniu. Najpierw przeczytaj blok hooks.
Hook widzi pełne dane wejściowe narzędzia. Hook audytowy, który loguje tool_input, zapisuje każdy argument każdego polecenia do pliku, w tym każdy token, który znalazł się w wierszu poleceń. Taki dziennik wymaga następnie tej samej ochrony co sam sekret, co stanowi część szerszego problemu utrzymywania sekretów poza zasięgiem agenta AI.
Hook może zapisywać dane w kontekście modelu. Wszystko, co hook SessionStart lub UserPromptSubmit wypisze na standardowe wyjście (stdout), jest dodawane do konwersacji. Hook, który przesyła tekst z zewnątrz, systemu śledzenia zgłoszeń lub pliku dziennika, przekazuje niezaufany tekst do modelu tak, jakby został on wpisany przez Ciebie. Traktuj to wyjście (stdout) jako dane wejściowe, a nie jako wyjściowe.
Uprawnienia to realna kontrola. Uruchamiaj agenta jako dedykowanego użytkownika bez przywilejów, z tylko takimi regułami sudo, jakich potrzebuje. Warto posiadać regułę PreToolUse typu deny, jednak z założenia jest to mechanizm typu best effort: dokumentacja referencyjna mówi to samo o filtrze if i zaleca korzystanie z systemowych uprawnień, gdy wymagana jest twarda blokada. Reguły uprawnień oraz konto, na którym działa proces, to elementy, które zapewniają bezpieczeństwo pod obciążeniem.
Jedna właściwość obowiązuje w każdej konfiguracji. Hooki PreToolUse uruchamiają się przed sprawdzeniem trybu uprawnień w każdym trybie, więc hook zwracający deny blokuje narzędzie nawet w trybie bypassPermissions. Hooki mogą zaostrzać to, na co pozwalają reguły uprawnień. Nie mogą one jednak tych reguł rozluźniać.
Dlaczego mój hook nie jest wyzwalany?
Należy postępować zgodnie z poniższą kolejnością. Każdy krok wskazuje objaw, który faktycznie występuje.
- Uruchom
/hooksi sprawdź, czy hook pojawia się pod oczekiwanym zdarzeniem. Brak hooka w menu zazwyczaj oznacza błąd składni JSON w pliku ustawień, ponieważ przecinki na końcu listy oraz komentarze są niedozwolone, lub plik nie znajduje się w jednej z sześciu powyższych lokalizacji. - Porównaj matcher dokładnie z nazwą narzędzia. Matchery rozróżniają wielkość liter, więc
"bash"nigdy nie dopasuje narzędziaBash. - Uruchom skrypt ręcznie z przykładowymi danymi wejściowymi, zgodnie z przykładem 1 powyżej. Nieoczekiwany kod wyjścia oznacza błąd w skrypcie, a Claude Code zgłasza go jako błąd hooka, a nie jako decyzję.
- Komunikat o treści
jq: command not foundoznacza, żejqnie jest zainstalowane na danej maszynie. Błądcommand not founddla własnego skryptu oznacza, że ścieżka nie została rozpoznana, więc należy użyć${CLAUDE_PROJECT_DIR}lub ścieżki bezwzględnej. Jeśli skrypt w ogóle się nie uruchamia, prawdopodobnie nie posiada uprawnień do wykonywania. - Hook wypisuje poprawny JSON, ale nic się nie dzieje. Hook w formie powłoki jest uruchamiany przez
sh -c, a jeśli profil powłoki wypisuje baner, jest on dodawany przed kodem JSON. Standardowe wyjście nie zaczyna się już od{, więc Claude Code odczytuje całość jako zwykły tekst i ignoruje decyzję. Przy kodzie wyjścia 0 nic nie jest raportowane poza dziennikiem debugowania. Owiń wszelkieechow profilu tak, aby uruchamiały się tylko w powłokach interaktywnych. - Nadal występuje problem: rozpocznij sesję za pomocą
claude --debug-file /tmp/claude.logi uruchomtail -f /tmp/claude.logw drugim terminalu. Dziennik debugowania rejestruje, które hooki zostały dopasowane, jaki kod wyjścia zwrócił każdy z nich oraz wszystko, co zostało wypisane na stdout i stderr.
FAQ
Jaka jest różnica między hookiem Claude Code a instrukcją w CLAUDE.md?
Instrukcja CLAUDE.md jest tekstem w kontekście modelu, więc konkuruje o uwagę z konwersacją oraz bieżącym żądaniem, a model może ją ważyć względem nich. Hook to polecenie powłoki, które Claude Code uruchamia w ustalonym punkcie swojego cyklu życia, więc wykonuje się ono przy każdym wystąpieniu zdarzenia, niezależnie od decyzji modelu. Instrukcji używaj do określania preferencji. Hooka używaj do kroków, które muszą zawsze wystąpić, lub działań, które nigdy nie mogą mieć miejsca.
Jak zablokować uruchamianie konkretnego polecenia powłoki przez Claude Code?
Zarejestruj hook PreToolUse z dopasowaniem Bash, który odczytuje polecenie z .tool_input.command, zapisuje powód do stderr i kończy działanie kodem 2. Claude Code anuluje wywołanie i wyświetla modelowi podany powód. Dzieje się to przed sprawdzeniem trybu uprawnień, więc blokada działa nawet w trybie bypassPermissions. Dopasowywanie wzorców w ciągu polecenia jest mechanizmem ochronnym, a nie barierą bezpieczeństwa, ponieważ to samo polecenie można zapisać w formie, której wzorzec nie wykryje. Dlatego należy wspierać to regułami uprawnień oraz korzystaniem z konta o ograniczonych przywilejach.
Mój hook wypisuje poprawny JSON, ale nic się nie dzieje. Dlaczego?
Najczęstszą przyczyną jest konfiguracja profilu powłoki. Hook bez pola args uruchamia się przez sh -c, a niektóre profile wypisują baner przy każdym uruchomieniu powłoki, co trafia do stdout przed kodem JSON. Ponieważ wyjście nie zaczyna się już od {, Claude Code traktuje całość jako zwykły tekst i ignoruje decyzję, a przy kodzie wyjścia 0 w transkrypcji nie pojawia się żadna informacja. Zabezpiecz wszelkie echo w swoim profilu testem powłoki interaktywnej, a następnie potwierdź poprawkę, odczytując dziennik debugowania z claude --debug-file /tmp/claude.log.
Czy uruchamianie hooków Claude Code na współdzielonym serwerze jest bezpieczne?
Hooki działają jako użytkownik, który uruchomił Claude Code, z uprawnieniami do plików tego użytkownika, więc hook może wykonać wszystko, co może to konto. Dwa nawyki eliminują większość ryzyka: uruchamiaj agenta jako dedykowane konto o ograniczonych uprawnieniach z wąską polityką sudo oraz czytaj blok hooks każdego repozytorium przed zaakceptowaniem okna dialogowego zaufania do obszaru roboczego, ponieważ hooki projektowe są dostarczane wewnątrz .claude/settings.json. Ustaw "disableAllHooks": true w pliku ustawień, jeśli chcesz, aby żaden z nich nie był uruchamiany.