
Claude Code hooks: den kompletta guiden (2026)
En skill kan ignoreras. Det är inte en brist i skills, det är hela designen: Claude läser beskrivningen, bestämmer om aktuell uppgift matchar, och laddar kroppen bara om den tror det. Oftast är den bedömningen rätt. Ibland är den inte det, och uppgiften där det spelar mest roll, kodgranskningen precis innan en merge, loggposten som ska finnas oavsett vad, är exakt uppgiften där "troligtvis" inte är gott nog.
Hooks är den andra halvan av Claude Code. En hook är ett skalkommando selen kör när en specifik händelse utlöses, oavsett om någon skill skulle ha tänkt på det. Ingen modellbedömning sitter mellan händelsen och kommandot. Den körs, varje gång, i ordning, och dess exitkod kan till och med stoppa Claude helt. Om du någonsin velat säga "formatera alltid den här filen efter en redigering" eller "låt aldrig Claude röra den här katalogen", är en hook verktyget byggt för den meningen.
Den här guiden täcker vad hooks är, settings.json-schemat bakom dem, sex recept du kan klistra in idag, hur hooks och skills fungerar tillsammans, och de felläge som äter upp en eftermiddag om du inte vet vad du ska leta efter.
Vad en hook faktiskt är
Claude Code utlöser namngivna händelser under en session: innan ett verktyg körs, efter ett verktyg körs, när Claude avslutar sitt svar, när en notifiering skulle visas. En hook binder ett skalkommando till en av dessa händelser, valfritt filtrerat till specifika verktyg. Selen kör ditt kommando, skickar det kontext som JSON på stdin, läser sedan exitkoden för att avgöra vad som händer härnäst.
Händelserna du använder mest:
- PreToolUse — utlöses innan ett verktygsanrop körs. En hook här kan blockera anropet helt.
- PostToolUse — utlöses efter att ett verktygsanrop slutförts. Bra för formatering, testning eller loggning av vad som just hände.
- Stop — utlöses när Claude avslutar sin vändning och är på väg att lämna tillbaka kontrollen till dig.
- Notification — utlöses när Claude Code skulle visa dig en systemnotifiering (behörighetsförfrågningar, inaktivitetsprompter).
- UserPromptSubmit — utlöses när du skickar ett meddelande, innan Claude ser det.
Det är mekanismen. Anledningen till att det spelar roll är garantin det ger dig som en skill strukturellt inte kan.
Den mentala modellen: deterministisk vs valfri
Det här är den enda idén värd att komma ihåg om du inte kommer ihåg något annat från den här guiden.
En skill är valfri. Claude läser dess beskrivning vid sessionsstart, och bestämmer sedan, baserat på din begäran, om den ska laddas och följas. Bra skills triggar tillförlitligt, men "tillförlitligt" är fortfarande en sannolikhet, inte en garanti. Claude kan misstolka en tvetydig prompt, eller två skills kan ha överlappande beskrivningar som förvirrar matchningen, ett fellägeläge vi täcker mer djupgående i vår guide om varför skills inte triggar.
En hook är deterministisk. Den frågar inte Claude om den ska köra. Den läser inte en beskrivning och bedömer relevans. Selen ser händelsen, och kommandot körs, punkt. Om händelsen är PostToolUse på Edit-verktyget körs din formatterare efter varje redigering, inklusive den Claude gjorde medan den tänkte på något helt annat.
Den skillnaden mappar direkt till när man ska ta till vilken:
| Skill | Hook | |
|---|---|---|
| Körs när | Claude bedömer den relevant | Varje gång händelsen utlöses |
| Kan hoppas över | Ja, av en dålig matchning eller upptagen kontext | Nej |
| Bäst för | Bedömning, struktur, "hur man gör X väl" | Framtvingande, "X måste alltid hända" |
| Fellöge | Tyst icke-trigger | Tyst dålig exitkod, eller blockering av allt |
Om meningen du framtvingar börjar med "Claude ska alltid..." eller "Claude får aldrig...", vill du ha en hook. Om den börjar med "när Claude gör X ska den närma sig det som..." vill du ha en skill. Att formatera kod efter varje redigering är en hook; att skriva idiomatisk Python är en skill. Att blockera commits till main är en hook; att strukturera ett bra commit-meddelande är en skill.
Anatomin av en hook i settings.json
Hooks bor under hooks-nyckeln i .claude/settings.json (projektnivå) eller ~/.claude/settings.json (användarnivå), en annan fil än CLAUDE.md och värd att inte förväxla: CLAUDE.md är prosa Claude läser, settings.json är konfiguration selen kör. Om du inte satt upp någon av dem än täcker vår CLAUDE.md-guide och vår fullständiga installationsgenomgång resten av stacken den här filen bor i. Här är ett minimalt men komplett hooks-exempel, kommenterat:
{
"hooks": {
// The event name — PreToolUse, PostToolUse, Stop, Notification, etc.
"PostToolUse": [
{
// matcher filters which tool calls trigger this hook.
// Omit it (or use "*") to match every tool.
"matcher": "Edit|Write",
"hooks": [
{
// "command" is currently the only hook type.
"type": "command",
// The shell command to run. Receives event JSON on stdin.
"command": "npx prettier --write \"$(echo $CLAUDE_TOOL_INPUT | jq -r .file_path)\"",
// Optional: kill the command if it hangs.
"timeout": 15
}
]
}
]
}
}
Några saker värda att peka ut för de brukar snubbla folk:
matcher-fältet opererar på verktygsnamnet, inte på filsökvägar eller innehåll. "Edit|Write" matchar Edit- och Write-verktygen; "Bash" matchar skalanrop. Om du behöver filtrera efter filsökväg eller kommandoinnehåll, gör det inuti ditt skript genom att läsa JSON-payloaden, inte i matchern.
Varje händelsenyckel håller en array av matcher-block, och varje matcher-block håller en array av hook-kommandon, så du kan koppla flera kommandon till en matcher, eller ett kommando till flera matchers, utan att duplicera konfiguration.
Kommandot tar emot händelsepayloaden som JSON på stdin: verktygsnamn, verktygsinput, och för PostToolUse, verktygets resultat. En hook som agerar på specifika filen som redigeras läser den JSON:en snarare än att anta att skalets arbetskatalog berättar hela historien.
Exitkoder bär betydelse. Exit 0 betyder "okej, fortsätt". En icke-noll exit på en PreToolUse-hook blockerar verktygsanropet och matar tillbaka stderr till Claude som en anledning. En icke-noll exit på PostToolUse loggas bara; verktyget körde redan, så det finns inget kvar att blockera.
Sex recept du kan använda idag
Dessa är medvetet smala. Kopiera blocket, justera kommandot, och bekräfta att det gör vad du förväntar dig på en engångsfil innan du litar på det med riktigt arbete.
1. Autoformatera efter varje redigering
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && npx prettier --write . --ignore-unknown"
}
]
}
]
}
}
Kör Prettier efter varje redigering eller skrivning. För stora repon, byt den generella . mot en sökväg härledd från hookens JSON-input så du bara formaterar den berörda filen.
2. Blockera redigeringar av skyddade sökvägar
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/guard_paths.py"
}
]
}
]
}
}
guard_paths.py läser filsökvägen från stdin-JSON, kollar den mot en spärrlista (migrations/, .env, infra/prod/), och avslutar med 1 med ett meddelande på stderr om den matchar. Det här är det närmaste en hård behörighetsgräns Claude Code har.
3. Kör tester efter källkodsändringar
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test -- --onlyChanged --silent"
}
]
}
]
}
}
Ger Claude en omedelbar signal när en redigering bryter ett test, i stället för att vänta på att du märker det vid granskningstid. Håll testkommandot smalt (--onlyChanged, en snabb delmängd) annars blir det recept sex i "när man inte ska"-avsnittet nedan.
4. Skrivbordsnotifiering när Claude är klar
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"'"
}
]
}
]
}
}
macOS-specifikt (byt ut mot notify-send på Linux). Användbart när du börjar köra längre autonoma vändningar och slutar titta på terminalen hela tiden.
5. Logga varje bash-kommando
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> \"$CLAUDE_PROJECT_DIR/.claude/bash-history.log\""
}
]
}
]
}
}
Ett granskningsspår som inte beror på att du kommer ihåg att kolla transkriptet. På en delad maskin eller ett repo med efterlevnadskrav är det här nära obligatoriskt.
6. Lint-grind före commit
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block_bad_commit.sh"
}
]
}
]
}
}
block_bad_commit.sh läser stdin, kollar om kommandot är en git commit, och om så kör din linter först, avslutar med icke-noll om den misslyckas. Det förvandlar "vänligen linta innan commit" från en begäran Claude kan glömma till en regel den inte kan komma förbi.
GRATIS STARTPAKET
Sätter du upp hooks tillsammans med dina första skills? Vi skickar våra 3 topppoängsatta skills plus installationschecklistan vi kör innan varje SkillProof-test. Gratis.
Hämta gratis startpaketHooks och skills tillsammans
De konkurrerar inte, de bästa uppsättningarna använder båda för vad var och en är bra på. Ett exempel från verkligheten: ett team vi testade ville ha varje commit skriven i deras husstil, imperativt läge, ett avgränsat prefix, en kropp som förklarar varför, och de ville också ha commits blockerade om diffen rörde en databasmigrering utan en matchande rollback-fil.
Stildelen är bedömning. Vad som räknas som ett bra "varför" varierar med ändringen, och det finns inget shellskript som tillförlitligt skriver bra prosa. Det är en skills jobb: något som Git Workflow Coach laddat närhelst Claude är på väg att committa, lär ut strukturen och ger exempel på bra kontra lata commit-meddelanden. Claude läser den, applicerar bedömning, och skriver ett meddelande som passar mönstret utan att vara en mallifyllning. Om teamet också kör strikt red-green-refactor är Test-Driven Development samma typ av bedömning-inte-lag-tillägg: den formar hur Claude angriper arbetet, en hook kan inte göra den delen.
Migreringsregeln är inte bedömning, den är lag: antingen finns rollback-filen eller inte, och teamet ville inte att "Claude bestämde att den här inte behövde det" skulle vara ett alternativ. Det är PreToolUse-hooken från recept sex, anpassad för att kolla efter den parade filen i stället för att köra en linter, blockerande git commit-anropet helt om den saknas.
Kör dem tillsammans och du får ett bra commit-meddelande som också är garanterat att klara migreringskontrollen, för skillen hanterar delen som behöver en hjärna och hooken hanterar delen som behöver en vägg. Ingen ersätter den andra. Skillen kan inte garantera efterlevnad, och en hook som skriver "fix: various changes" på varje commit skulle vara värdelös. Vår sida bästa kodningsskills rankar bedömningssidan av den här parningen efter testad poäng, om du väljer en första skill att köra tillsammans med dina hooks.
Felsökning av hooks
Hooks misslyckas tyst oftare än högljutt. Vad som vanligtvis går sönder:
Citering. Hook-kommandon är skalsträngar inuti JSON-strängar, så ett " som inte är escapat bryter JSON-parsningen innan ditt kommando ens körs. Vid tvivel, lägg den riktiga logiken i en skriptfil och låt hook-kommandot bara anropa den (bash .claude/hooks/my-hook.sh) i stället för att inline:a en komplex enrading.
Exitkoder som inte betyder vad du tror. Ett hook-skript som stöter på ett orelaterat fel (saknat beroende, åtkomst nekad) avslutar med icke-noll på samma sätt som en hook som avsiktligt vill blockera. Om en PreToolUse-hook börjar blockera varje verktygsanrop och du inte skrev den för att vara så strikt, kolla om skriptet faktiskt misslyckas i stället för att bedöma.
PATH-antaganden. Hooks körs i en skalmiljö som kanske inte matchar din interaktiva terminal. Ett kommando som fungerar fint när du skriver det själv kan misslyckas inuti en hook för att nvm, en virtualenv, eller ett verktyg installerat via ett shell-plugin inte finns på PATH i den kontexten. Använd absoluta sökvägar till binärer, eller källa rätt miljö högst upp i skriptet.
Tysta stdin-antaganden. Om ditt skript förväntar sig JSON på stdin och inte får det, för du testade det genom att köra det direkt snarare än att pipa in en exempelpayload, kommer det bete sig annorlunda under selen än det gjorde i din terminal.
Timeouts. En hook utan timeout som hänger sig hänger hela vändningen. Sätt en explicit timeout på allt som rör nätverket eller en långsam underprocess.
När man inte ska använda hooks
Hooks är billiga att skriva och lätta att överanvända. Fellöget är inte en hook som gör fel sak, det är en hook som gör rätt sak för ofta. En PostToolUse-hook som kör din fulla testsvit efter varje enskild redigering förvandlar en femsekundersändring till en tvåminuterswait, upprepad för varje redigering i en session som gör tjugo av dem.
Tumregeln: om en hooks kommando tar mer än en sekund eller två, avgränsa matchern, avgränsa vad den kollar, eller flytta den till en mindre frekvent händelse. Testa-vid-varje-redigering blir testa-vid-filskrivning blir testa-innan-commit när kontrollen blir dyrare. Matcha hookens kostnad till hur ofta dess händelse utlöses, och överväg om en skill, som bara laddar kontext och inte kör en process, är ett bättre val för allt som inte strikt är framtvingande.
Det är också värt att inte ta till en hook för att fixa en skills triggerproblem. Om en skill inte triggar när den ska är fixen en bättre beskrivning, inte en hook, eftersom hooks kör skalkommandon och inte kan ladda skill-innehåll. För det fellöget, se varför skills inte triggar.
SKILLPROOF-PAKET
Att para hooks med rätt skills är merparten av en bra Claude Code-uppsättning. Developer Toolkit buntar våra topppoängsatta kodningsskills, förkontrollerade för triggerkonflikter, så skill-halvan av den här parningen är gjord åt dig.
Hämta Developer Toolkit — $10Vanliga frågor
Saktar hooks ner varje Claude Code-session?
Bara händelserna du kopplar dem till, och bara med hur lång tid ditt kommando tar. En hook på PostToolUse för Edit körs en gång per redigering; en snabb formatterare är omärkbar, en full testsvit känns av på varje redigering, vilket är fallet som täcks ovan under när man inte ska använda hooks.
Kan en hook stoppa Claude från att göra något helt?
Ja, det är vad PreToolUse-hooks är till för. Avsluta med icke-noll och verktygsanropet blockeras innan det körs, med stderr vanligtvis synligt tillbaka till Claude som anledningen. Det är mekanismen bakom recept två (skyddade sökvägar) och recept sex (lint-grind).
Var placerar jag min hooks-konfiguration, projekt- eller användarinställningar?
Projektnivå (.claude/settings.json, committad) om den ska gälla för alla på den kodbasen: formatering, skyddade sökvägar, migreringskontroller. Användarnivå (~/.claude/settings.json) för en personlig preferens, som skrivbordsnotifieringen i recept fyra.
Vad är skillnaden mellan en hook och en skill som säger "formatera alltid kod"?
Hooken körs faktiskt alltid. En skill som säger åt Claude att alltid formatera kod är fortfarande en instruktion Claude läser och bestämmer sig för att följa; det är en stark knuff, inte en garanti, och den konkurrerar med annat i kontexten om uppmärksamhet på vilken vändning som helst. Om "alltid" är ett krav snarare än en preferens, använd en hook.
Min hook körs inte alls. Vad är det första jag ska kolla?
Bekräfta att inställningsfilen är giltig JSON (ett efterföljande kommatecken eller ett oescapat citattecken kan tyst inaktivera hela hooks-blocket) och att händelsenamnet och matchern är stavade exakt som förväntat; båda är skiftlägeskänsliga. Efter det, kolla om du redigerade projektinställningar när sessionen läser användarinställningar, eller vice versa.
★ 9.6/10 × 3
Gratis startpaket
De 3 skills som fått våra högsta testbetyg plus installationschecklistan — setupen vi själva skulle lägga på en ny maskin. Gratis, via e-post.