Kirjoitat CLAUDE.md-tiedostoon, että koodi ajetaan Prettierin läpi jokaisen muutoksen jälkeen. Claude muistaa sen useimmiten. Sitten tulee pitkä istunto, konteksti täyttyy ja yksi tiedosto jää muotoilematta. Kukaan ei huomaa ennen kuin CI valittaa.
Hook korjaa juuri tämän. Se on komento, jonka Claude Code ajaa itse tietyssä kohdassa, eikä Claude voi unohtaa sitä. Anthropicin dokumentaatio kutsuu tätä deterministiseksi hallinnaksi: tietyt asiat tapahtuvat aina, eivätkä ne jää kielimallin valinnan varaan.
Tässä oppaassa käydään läpi, mistä hook koostuu, mihin tiedostoon se kirjoitetaan ja mitkä tapahtumat kannattaa tuntea. Lopussa on neljä valmista esimerkkiä, jotka voit kopioida omaan projektiisi.
Mikä on Claude Code hook?
Hook on käyttäjän määrittelemä komento, joka käynnistyy, kun Claude Codessa tapahtuu jotain tiettyä. Tapahtuma voi olla vaikkapa se, että Claude on juuri muokkaamassa tiedostoa, se on saanut komennon ajettua tai se jää odottamaan lupaasi.
Yleensä hook on shell-komento tai skripti. Claude Code antaa sille tapahtuman tiedot JSON-muodossa, ja skripti päättää exit-koodilla tai omalla JSON-vastauksellaan, mitä seuraavaksi tapahtuu.
Hookilla voit siis:
- muotoilla tiedoston heti, kun Claude on muokannut sitä
- estää vaarallisen komennon ennen kuin se ajetaan
- saada työpöytäilmoituksen, kun Claude tarvitsee sinua
- lisätä Clauden kontekstiin tietoa istunnon alussa tai tiivistyksen jälkeen
Hookit eivät korvaa ohjeita. Harkintaa vaativat työtavat sopivat paremmin skilleihin, jotka ovat Clauden luettavia ohjeita. Hook on sääntö, joka pitää joka kerta.
Missä hookit määritellään
Hookit kirjoitetaan asetustiedoston hooks-osioon. Tiedoston paikka ratkaisee, mihin hook vaikuttaa:
| Tiedosto | Koskee | Jaettavissa |
|---|---|---|
~/.claude/settings.json | kaikkia omia projektejasi | ei, pysyy koneellasi |
.claude/settings.json | yhtä projektia | kyllä, voi viedä versionhallintaan |
.claude/settings.local.json | yhtä projektia | ei, Claude Code lisää sen gitignoreen tallentaessaan siihen asetuksen |
Lisäksi hookeja voi tulla organisaation hallinnoiduista asetuksista, pluginin hooks/hooks.json-tiedostosta sekä skillin tai subagentin frontmatterista. Eri tasojen hookit eivät korvaa toisiaan, vaan ne lasketaan yhteen.
Kun olet lisännyt hookin, kirjoita Claude Codessa /hooks. Komento avaa näkymän, josta näet kaikki käytössä olevat hookit tapahtumittain. Jos muokkaat asetustiedostoa käsin kesken istunnon, Claude Code yleensä huomaa muutoksen itse. Jos ei huomaa, käynnistä istunto uudelleen.
Asetustiedostojen tasoista ja oikeuksista on oma oppaansa: Claude Code oikeudet ja asetukset.
Hookin rakenne: tapahtuma, matcher ja käsittelijä
Hook-asetuksessa on kolme sisäkkäistä tasoa. Ensin valitaan tapahtuma, jonka jälkeen tulee matcher-ryhmä, joka rajaa milloin hook laukeaa. Sisimpänä on yksi tai useampi käsittelijä, eli varsinainen ajettava komento.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
Tässä PostToolUse on tapahtuma, Edit|Write on matcher ja sisin olio on käsittelijä. Kaikki tapahtumat ovat saman hooks-olion avaimia. Jos tiedostossa on jo hooks-osio, lisää uusi tapahtuma sen rinnalle äläkä korvaa koko oliota.
Käsittelijän type on useimmiten command, joka ajaa shell-komennon. Muita tyyppejä ovat:
http, joka lähettää tapahtuman tiedot POST-pyyntönä osoitteeseenmcp_tool, joka kutsuu MCP-palvelimen työkaluaprompt, joka pyytää Claude-mallia tekemään päätöksen yhdellä kutsullaagent, joka käynnistää subagentin tarkistamaan asian työkaluilla (dokumentaation mukaan vielä kokeellinen)
Aloita command-tyypillä. Muut tyypit ovat hyödyllisiä, kun tarvitset harkintaa tai keskitettyä palvelinta.
Tärkeimmät tapahtumat
Tapahtumia on kymmeniä. Alkuun riittää kourallinen:
| Tapahtuma | Milloin se laukeaa |
|---|---|
SessionStart | istunto alkaa tai jatkuu |
UserPromptSubmit | lähetät viestin, ennen kuin Claude käsittelee sen |
PreToolUse | ennen työkalun käyttöä, voi estää sen |
PermissionRequest | työkalun käyttö tarvitsee lupapäätöksen |
PostToolUse | työkalun käyttö onnistui |
Notification | Claude Code lähettää ilmoituksen |
Stop | Claude on vastannut |
PreCompact | ennen kontekstin tiivistystä |
SessionEnd | istunto päättyy |
Koko lista on hook-referenssissä. Huomaa, että Stop laukeaa aina kun Claude lopettaa vastauksen, ei vain silloin kun koko tehtävä on valmis.
Matcher: rajaa milloin hook laukeaa
Ilman matcheria hook laukeaa tapahtuman jokaisella kerralla. Matcherilla rajaat sen. Eri tapahtumat vertaavat matcheria eri asiaan:
PreToolUsejaPostToolUsevertaavat työkalun nimeen, esimerkiksiBash,Edit|Writetaimcp__.*SessionStartvertaa siihen, miten istunto alkoi:startup,resume,clear,compacttaiforkNotificationvertaa ilmoituksen tyyppiin, esimerkiksipermission_prompttaiidle_prompt
Pystyviiva erottaa vaihtoehdot, joten Edit|Write osuu sekä muokkaukseen että uuden tiedoston kirjoittamiseen. Tyhjä merkkijono "" osuu kaikkeen. Matcherit erottavat isot ja pienet kirjaimet, joten bash ei osu Bash-työkaluun.
Jotkin tapahtumat, kuten UserPromptSubmit ja Stop, eivät tue matcheria lainkaan. Ne laukeavat aina.
Työkalutapahtumissa voi rajata vielä tarkemmin if-kentällä. Se käyttää samaa kirjoitustapaa kuin lupasäännöt, joten "if": "Bash(git *)" ajaa hookin vain git-komennoille eikä jokaiselle Bash-komennolle. Dokumentaation mukaan if on kuitenkin paras arvaus. Jos jokin on pakko estää aina, käytä lupasääntöjä.
Mitä hook saa ja miten se vastaa
Kun tapahtuma laukeaa, Claude Code syöttää hookille JSONin stdinin kautta. Bash-komennon kohdalla PreToolUse-hook saa esimerkiksi tällaisen:
{
"session_id": "abc123",
"cwd": "/Users/maija/verkkokauppa",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
Tiedostojen muokkauksessa polku on kentässä tool_input.file_path. Siksi esimerkeissä käytetään jq-työkalua, joka poimii kentän JSONista. Asenna se macOS:ssä komennolla brew install jq tai Debianissa ja Ubuntussa komennolla apt-get install jq.
Hook vastaa exit-koodilla:
- Exit 0: ei huomautettavaa.
PreToolUse-hookissa tämä ei hyväksy komentoa, vaan normaali lupakysely jatkuu tavalliseen tapaan. - Exit 2: estä. Kirjoita syy stderriin, niin Claude saa sen palautteena ja voi muuttaa toimintaansa.
- Muu koodi: useimmissa tapahtumissa toiminto jatkuu, ja keskusteluun tulee ilmoitus hook-virheestä.
Jos tarvitset enemmän kuin kyllä tai ei, päätä skripti koodiin 0 ja tulosta JSON. PreToolUse-hook voi esimerkiksi kieltäytyä ja kertoa syyn:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Käytä pnpm:ää, ei npm:ää"
}
}
Arvoja ovat deny, ask ja allow. Kun hook palauttaa allow, se ohittaa lupakyselyn, mutta asetusten kieltosäännöt pätevät silti. Valitse yhdelle hookille joko exit-koodi 2 tai JSON, älä molempia.
Useampi hook samassa tapahtumassa
Samaan tapahtumaan voi kytkeä monta hookia. Voit esimerkiksi kirjata jokaisen Bash-komennon lokiin ja samalla estää vaaralliset komennot:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r .tool_input.command >> ~/.claude/bash.log"
},
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/esta-vaaralliset.sh"
}
]
}
]
}
}
Claude Code ajaa osuvat hookit rinnakkain ja odottaa, että jokainen on valmis, ennen kuin se yhdistää tulokset. PreToolUse-päätöksissä tiukin vastaus voittaa järjestyksessä deny, ask, allow.
Tästä seuraa yksi ansa. Kun yksi hook estää komennon, muut ajetaan silti loppuun. Yllä olevassa esimerkissä lokiin kirjautuu myös estetty komento, koska lokihook ehti jo tehdä työnsä. Älä siis rakenna mitään sen varaan, että yhden hookin kielto pysäyttäisi toisen hookin sivuvaikutukset.
Asetustiedostojen hookit toimivat myös subagenteissa. Kun subagentti käyttää työkalua, samat PreToolUse- ja PostToolUse-hookit laukeavat kuin pääkeskustelussa. Suojaus pätee siis, vaikka Claude jakaisi työn osiin.
Esimerkki 1: ilmoitus, kun Claude odottaa sinua
Pitkän tehtävän aikana terminaalin tuijottaminen on ajanhukkaa. Tämä hook näyttää macOS:ssä työpöytäilmoituksen aina, kun Claude tarvitsee sinua. Lisää se tiedostoon ~/.claude/settings.json, niin se toimii kaikissa projekteissa:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code odottaa sinua\" with title \"Claude Code\"'"
}
]
}
]
}
}
Linuxissa komento on notify-send 'Claude Code' 'Claude Code odottaa sinua'. Jos notify-send puuttuu, Debianissa ja Ubuntussa se tulee paketissa libnotify-bin. Palvelimilla ja SSH-yhteyksien yli ilmoituksia ei yleensä näy, koska niistä puuttuu työpöydän ilmoituspalvelu.
macOS:ssä on yksi kompastuskivi. osascript lähettää ilmoitukset Script Editorin kautta, ja jos Script Editorilla ei ole ilmoituslupaa, mitään ei näy eikä macOS kysy lupaa. Aja terminaalissa kerran osascript -e 'display notification "testi"', avaa sitten Järjestelmäasetusten Ilmoitukset, etsi Script Editor ja salli ilmoitukset.
Jos haluat ilmoituksen vain lupakyselyistä, vaihda matcheriksi permission_prompt. Terminaalissa ilmoitus tulee silloin, kun lupakysely on odottanut noin kuusi sekuntia.
Esimerkki 2: formatteri jokaisen muokkauksen jälkeen
Tämä on se hook, jonka useimmat ottavat käyttöön ensimmäisenä. Se ajaa Prettierin jokaiselle tiedostolle, jota Claude muokkaa tai kirjoittaa. Lisää se projektin tiedostoon .claude/settings.json, jolloin koko tiimi saa saman muotoilun:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
jq poimii JSONista muokatun tiedoston polun, ja xargs antaa sen Prettierille. Onnistunut hook ei näy keskustelussa mitenkään, joten testaa se pyytämällä Claudea lisäämään JavaScript-tiedostoon rivi, jossa on yksinkertaiset lainausmerkit. Prettierin oletusasetuksilla ne muuttuvat kaksinkertaisiksi.
Yksi rajoitus kannattaa tietää. Edit|Write ei osu tiedostoihin, joita Claude muuttaa Bash-komennoilla, esimerkiksi sedillä. Jos sinun on pakko nähdä jokainen muutos, dokumentaatio neuvoo lisäämään Stop-hookin, joka käy työpuun läpi kerran vuoron lopussa.
Pythonissa sama toimii esimerkiksi Ruffilla. Vaihda vain komento siihen, millä projektisi muotoilee koodin.
Esimerkki 3: estä vaaralliset komennot
Joitain komentoja et halua Clauden ajavan koskaan, vaikka hyväksyisit ne kiireessä. Tämä skripti estää rm -rf-komennot ja suorat pushit main-haaraan. Tallenna se tiedostoon .claude/hooks/esta-vaaralliset.sh:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE 'rm -rf'; then
echo "Estetty: rm -rf ei ole sallittu tässä projektissa" >&2
exit 2
fi
if echo "$COMMAND" | grep -qE 'git push .*main'; then
echo "Estetty: älä pushaa suoraan mainiin, tee haara ja PR" >&2
exit 2
fi
exit 0
Anna skriptille suoritusoikeus:
chmod +x .claude/hooks/esta-vaaralliset.sh
Kytke se sitten PreToolUse-tapahtumaan Bash-työkalulle:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/esta-vaaralliset.sh"
}
]
}
]
}
}
$CLAUDE_PROJECT_DIR osoittaa projektin juureen, josta istunto alkoi, joten skripti löytyy, vaikka Claude olisi siirtynyt alikansioon. Lainausmerkit sen ympärillä pitävät polun ehjänä, jos siinä on välilyöntejä.
Kun Claude yrittää ajaa estetyn komennon, se saa stderrin viestin palautteena ja etsii toisen tavan. Dokumentaation mukaan deny-päätöksen tehnyt PreToolUse-hook pitää kaikissa lupatiloissa, myös bypassPermissions-tilassa.
Muista silti, että tällainen tekstihaku on suodatin eikä muuri. Komennon voi kirjoittaa monella tavalla, joten varsinainen raja kannattaa vetää lupasäännöillä. Hook on hyvä lisäkerros.
Samalla mallilla voit suojata tiedostoja. Vaihda matcheriksi Edit|Write, lue polku kentästä .tool_input.file_path ja vertaa sitä listaan, jossa on vaikkapa .env, package-lock.json ja .git/.
Esimerkki 4: muistuta säännöistä tiivistyksen jälkeen
Kun konteksti täyttyy, Claude Code tiivistää keskustelun. Silloin yksityiskohtia katoaa. SessionStart-hook compact-matcherilla ajetaan jokaisen tiivistyksen jälkeen, ja komennon tulostama teksti lisätään Clauden kontekstiin:
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Muistutus: käytä pnpm:ää. Aja pnpm test ennen committia.' && git log --oneline -5"
}
]
}
]
}
}
Tässä hook kertoo pari sääntöä ja näyttää viisi viimeisintä committia. Jos haluat saman tiedon jokaisen istunnon alkuun, CLAUDE.md on siihen parempi paikka. Kontekstin hallinnasta kerrotaan lisää oppaassa Claude Code konteksti ja muisti.
Kun tarvitset harkintaa: prompt-hookit
Kaikkea ei voi tarkistaa grepillä. prompt-tyyppinen hook lähettää ohjeesi ja tapahtuman tiedot Claude-mallille, joka vastaa JSONilla "ok": true tai "ok": false ja syyllä.
Tämä Stop-hook kysyy mallilta, onko kaikki pyydetty tehty. Jos ei ole, Claude jatkaa töitä ja käyttää syytä seuraavana ohjeenaan:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Tarkista, onko kaikki pyydetyt tehtävät tehty. Jos ei, vastaa {\"ok\": false, \"reason\": \"mitä on vielä tekemättä\"}."
}
]
}
]
}
}
Jokainen tarkistus on mallikutsu, joten käytä tätä harkiten. Dokumentaation mukaan Claude Code ohittaa Stop-hookin, jos se on estänyt lopettamisen kahdeksan kertaa peräkkäin ilman Clauden työkalukutsua välissä.
Kun hook ei toimi
Tavallisimmat syyt löytyvät nopeasti:
- Hook ei näy
/hooks-näkymässä. Tarkista tiedoston paikka ja JSON. Pilkku viimeisen alkion perässä tai kommentti rikkoo tiedoston. - Hook näkyy mutta ei laukea. Tarkista matcherin kirjoitusasu.
Bashjabashovat eri asioita. - Keskusteluun tulee hook-virhe. Aja skripti käsin ja syötä sille esimerkki-JSON:
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./.claude/hooks/esta-vaaralliset.sh
echo $?
- "command not found". Käytä absoluuttista polkua tai
$CLAUDE_PROJECT_DIR-muuttujaa ja tarkista, että skriptillä on suoritusoikeus. - JSON-vastaus ei tee mitään. Tarkista, ettei shellin profiili tulosta mitään ennen JSONia, ja että kentät ovat oikealla tasolla. Esimerkiksi
additionalContextkuuluuhookSpecificOutput-olion sisään.
Tarkemmat tiedot saat lokista. Käynnistä Claude Code komennolla claude --debug-file /tmp/claude.log ja seuraa lokia toisessa terminaalissa komennolla tail -f /tmp/claude.log. Kesken istunnon saat lokin päälle komennolla /debug.
Jos haluat kytkeä kaikki hookit pois, aseta asetustiedostoon "disableAllHooks": true.
Turvallisuus: lue ennen kuin lisäät
Komentohook ajetaan täysillä käyttäjäoikeuksillasi. Se voi lukea, muuttaa ja poistaa mitä tahansa tiedostoa, johon sinulla on pääsy. Lue siis jokainen komento ennen kuin lisäät sen, varsinkin jos kopioit sen netistä.
Vieraan repon kanssa kannattaa olla tarkkana. Interaktiivisessa istunnossa Claude Code ei aja asetustiedostojen hookeja ennen kuin olet hyväksynyt kansion luotettavaksi. claude -p -ajossa tätä kyselyä ei tule, joten repoon tallennetut hookit ajetaan suoraan. Lue silloin ensin repon .claude-kansio tai aja ilman hookeja asetuksella --settings '{"disableAllHooks": true}'. Skriptattua käyttöä käsitellään tarkemmin oppaassa Claude Code headless-automaatio.
Dokumentaation omat neuvot hook-skripteille ovat lyhyet: älä luota syötteeseen sokeasti, laita shell-muuttujat lainausmerkkeihin ("$VAR" eikä $VAR), tarkista polut ..-merkkien varalta, käytä absoluuttisia polkuja ja jätä arkaluonteiset tiedostot kuten .env rauhaan.
Hook, skill vai CLAUDE.md?
Kolme työkalua, kolme eri tarkoitusta:
| CLAUDE.md | Skill | Hook | |
|---|---|---|---|
| Mikä se on | pysyvä ohje | ladattava ohje tai työtapa | ajettava komento |
| Milloin vaikuttaa | joka istunnossa | kun Claude tai sinä otatte sen käyttöön | aina kun tapahtuma osuu |
| Kuka päättää | Claude tulkitsee | Claude tulkitsee | Claude Code ajaa |
| Esimerkki | "käytä pnpm:ää" | julkaisun tarkistuslista | Prettier muokkauksen jälkeen |
Hyvä nyrkkisääntö: kun huomaat, että Claude jättää jonkin säännön välillä noudattamatta ja sen on pakko pitää, siirrä sääntö hookiksi. Kun kyse on työtavasta, jota Claude soveltaa harkiten, tee siitä skill. Projektin perustiedot kuuluvat edelleen CLAUDE.md-tiedostoon.
Jos haluat jakaa hookit, skillit ja muut laajennukset useaan projektiin kerralla, ne voi paketoida pluginiksi.
Mistä aloittaa
Aloita yhdellä hookilla. Ilmoitushook on helpoin, koska se ei muuta mitään projektissasi ja huomaat heti, toimiiko se. Sen jälkeen lisää formatteri projektin .claude/settings.json-tiedostoon ja vie se versionhallintaan, niin koko tiimi saa saman muotoilun.
Kun nämä toimivat, mieti, mitä olet joutunut korjaamaan Clauden jäljiltä useammin kuin kerran. Usein siitä saa seuraavan hookin. Lisää Claude Code -vinkkejä löytyy oppaasta Claude Code käytännön vinkit ja Claude Code -oppaista.