Przejdź do głównej zawartości

Mistrzostwo w systemie hooków

Twój młodszy programista właśnie poprosił Claude Code o “uporządkowanie plików migracji bazy danych”. Claude zinterpretował to jako usunięcie historii migracji. Skrypt wdrożeniowy produkcyjnego uruchomił się następnego ranka i odtworzył każdą tabelę od podstaw. Twój piątkowy wieczór to teraz operacja odzyskiwania.

Hooki to zapobiegają. To deterministyczne polecenia powłoki, prompty LLM lub wywołania agentów, które uruchamiają się w określonych punktach cyklu życia Claude Code. Nie zależą od tego, czy model postępuje zgodnie z instrukcjami — wykonują się, czy model współpracuje, czy nie.

  • Działający model mentalny 14 zdarzeń hooków i kiedy każde się uruchamia
  • Gotowe do produkcji skrypty hooków dla ochrony plików, auto-formatowania i egzekwowania bezpieczeństwa
  • Wzorce hooków opartych na promptach i agentach, które wykorzystują osąd AI deterministycznie
  • Techniki debugowania, gdy hooki zachowują się nieoczekiwanie

Każda sesja Claude Code przechodzi przez przewidywalny cykl życia. Hooki przechwytują ten cykl w 14 odrębnych punktach:

ZdarzenieKiedy się uruchamiaMoże zablokować?
SessionStartSesja rozpoczyna się lub wznawiaTak
UserPromptSubmitPo naciśnięciu Enter, przed przetworzeniem przez ClaudeTak
PreToolUsePrzed wykonaniem jakiegokolwiek wywołania narzędziaTak
PermissionRequestGdy pojawi się okno dialogowe uprawnieńTak
PostToolUsePo udanym wywołaniu narzędziaNie
PostToolUseFailurePo nieudanym wywołaniu narzędziaNie
NotificationGdy Claude wysyła powiadomienieNie
SubagentStartGdy pojawia się subagentNie
SubagentStopGdy subagent kończyNie
StopGdy Claude kończy odpowiadaćTak
TeammateIdleGdy członek zespołu agentów staje się bezczynnyTak
TaskCompletedGdy zadanie jest oznaczone jako ukończoneTak
PreCompactPrzed kompaktowaniem kontekstuNie
SessionEndGdy sesja kończy sięNie

Kolumna “Może zablokować?” jest kluczowa. Hooki PreToolUse mogą zapobiec wykonaniu niebezpiecznego polecenia. Hooki Stop mogą zmusić Claude do kontynuowania pracy, gdy próbuje przedwcześnie zakończyć.

Hooki znajdują się w plikach JSON ustawień. Konfiguracja ma trzy poziomy:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-dangerous-commands.sh"
}
]
}
]
}
}

Gdzie umieszczać hooki:

LokalizacjaZakresDo współdzielenia
~/.claude/settings.jsonWszystkie Twoje projektyNie
.claude/settings.jsonBieżący projekt, wszyscy członkowie zespołuTak (commituj)
.claude/settings.local.jsonBieżący projekt, tylko TyNie (gitignored)

Pole matcher to regex, który filtruje, kiedy hook się uruchamia:

  • "Bash" — tylko wywołania narzędzia Bash
  • "Edit|Write" — narzędzia modyfikacji plików
  • "mcp__.*" — każde narzędzie MCP
  • Pomiń matcher lub użyj "*", aby dopasować wszystko

Ten hook PostToolUse uruchamia Prettier na każdym pliku, który Claude edytuje lub zapisuje:

.claude/hooks/auto-format.sh
#!/bin/bash
# Runs after Edit or Write tool calls
FILE_PATH=$(jq -r '.tool_input.file_path // .tool_input.path // empty')
if [ -n "$FILE_PATH" ] && [ -f "$FILE_PATH" ]; then
case "$FILE_PATH" in
*.ts|*.tsx|*.js|*.jsx|*.json|*.css|*.md)
npx prettier --write "$FILE_PATH" 2>/dev/null
;;
esac
fi
exit 0

Konfiguracja hooka:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/auto-format.sh"
}
]
}
]
}
}
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Glass.aiff"
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Ping.aiff"
}
]
}
]
}
}

Blokuj Claude przed modyfikowaniem plików, które powinny być zmieniane tylko przez określone procesy:

.claude/hooks/protect-files.sh
#!/bin/bash
FILE_PATH=$(jq -r '.tool_input.file_path // .tool_input.path // empty')
PROTECTED_PATTERNS=(
"*/migrations/*"
".env*"
"*/secrets/*"
"package-lock.json"
"yarn.lock"
)
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == $pattern ]]; then
jq -n "{hookSpecificOutput:{hookEventName:\"PreToolUse\",permissionDecision:\"deny\",permissionDecisionReason:\"Protected file: $FILE_PATH cannot be modified by Claude\"}}"
exit 0
fi
done
exit 0

Gdy potrzebujesz osądu AI zamiast dopasowywania wzorców, użyj hooków opartych na promptach. Wysyłają one kontekst zdarzenia do LLM i działają na podstawie odpowiedzi:

{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "prompt",
"prompt": "Review the assistant's final response. Did it actually complete the requested task, or did it stop early with a vague summary? If the task is incomplete, respond with {\"decision\": \"block\", \"reason\": \"Task not complete: [specific missing items]\"}. If complete, respond with {\"decision\": \"allow\"}."
}
]
}
]
}
}

Ten wzorzec wyłapuje tendencję Claude do ogłaszania zwycięstwa przed faktycznym zakończeniem pracy.

Niektóre hooki nie powinny blokować głównego przepływu. Wykonywanie testów w tle to klasyczny przykład:

{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/run-tests-async.sh",
"async": true
}
]
}
]
}
}

Hooki asynchroniczne uruchamiają się natychmiast, ale nie blokują Claude przed kontynuowaniem. Wynik jest dostarczany, gdy jest dostępny.

Gdy hooki źle się zachowują, użyj flagi debug:

Okno terminala
claude --debug "hooks"

To pokazuje każde zdarzenie hooka, ocenę matchera i wykonanie handlera. Częste problemy:

  • Hook się nie uruchamia: Sprawdź, czy Twój regex matchera faktycznie pasuje do nazwy narzędzia. "Bash" pasuje do narzędzia Bash, nie "bash" (wielkość liter ma znaczenie).
  • Hook się uruchamia, ale nie ma efektu: Sprawdź kod wyjścia i output JSON. Kod wyjścia 0 oznacza “zezwól”. Kod wyjścia 2 oznacza “zablokuj”. Wszystko inne to błąd.
  • Uprawnienia skryptu hooka: Upewnij się, że pliki .claude/hooks/*.sh są wykonywalne (chmod +x).

Hooki spowalniają każdą operację: Każdy synchroniczny hook dodaje opóźnienie do wywołania narzędzia, które przechwytuje. Jeśli Twój hook PostToolUse uruchamia pełny zestaw testów przy każdej edycji, Claude będzie powolny. Przenieś ciężkie operacje do hooków asynchronicznych.

Hooki konfliktują ze sobą: Gdy wiele hooków uruchamia się na tym samym zdarzeniu, wszystkie działają. Jeśli jeden zezwala, a drugi odmawia, odmowa wygrywa. Użyj /hooks, aby zobaczyć wszystkie skonfigurowane hooki i ich źródła.

Hooki oparte na promptach zużywają dodatkowe tokeny: Hooki oparte na promptach wykonują dodatkowe wywołania API. Każde dodaje koszt. Używaj ich oszczędnie i preferuj hooki poleceń dla zadań dopasowywania wzorców.

Hook nie może odczytać stdin: Hooki poleceń otrzymują JSON na stdin. Jeśli Twój skrypt odczytuje z pliku lub nie przetwarza stdin, nie otrzyma kontekstu zdarzenia. Zawsze używaj jq do parsowania wejścia.