# Hooks Claude Codessa 2026: vaiheittainen käyttöönotto ja 7 valmista esimerkkiä

> 7 valmista hookia tiedostoon .claude/settings.json, kopioitavaksi ja liitettäväksi: .env-suojaus, prettier, force-push main-haaraan, ajatusviivat ja kehityspalvelin. 4 käsittelijätyyppiä ja 31 tapahtumaa.

Source: https://ailoot.ai/blog/fi/claude-code-hooks/
Canonical: https://ailoot.ai/blog/fi/claude-code-hooks/
Language: fi
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-121
Revision: sha256-d231583b07877db6a1e8525b624edec8c0e0f937eef2b6efd4f46397e8fba2fc


Claude Coden hookit ovat shell-komentoja, HTTP-kutsuja tai lyhyitä LLM-tarkistuksia, jotka suoritetaan automaattisesti ennen Claude-toimintoa tai sen jälkeen. Ne lisäävät tarkistuksia tiettyihin työkalutapahtumiin, muotoilevat koodia muokkauksen jälkeen ja lisäävät tuoretta kontekstia istunnon alussa. Osuva PreToolUse-hook voi hylätä kyseisen kutsun. Näiden regex-esimerkkien tarkistukset ovat käytännön apuja, eivät kaikkien tiedosto- ja Git-käyttöpolkujen tietoturvaraja. Seuraavaksi käydään läpi 4 käsittelijätyyppiä, 31 elinkaaritapahtumaa, testi sille, ”milloin sääntö kuuluu hookiin”, 7 valmista hookia kopioitavaksi tiedostoon `.claude/settings.json` sekä 3 sudenkuoppaa, joihin olen itse törmännyt.



[Ailoot-dokumentaatio: API, avaimet ja käyttöönotto](https://ailoot.ai/docs/).

## Mitä hookit ovat Claude Codessa?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Hook on shell-komento, HTTP-kutsu, lyhyt LLM-kehote tai aliagentti, jonka Claude Code suorittaa tietyn elinkaaritapahtuman yhteydessä. Toisin kuin tiedostossa `CLAUDE.md` olevat ohjeet, jotka malli saattaa sivuuttaa tai unohtaa, hook toimii samalla tavalla joka kerta: se on koodia, ei ehdotus.

</div>

Pidin puolen vuoden ajan sääntöä ”älä koskaan muokkaa `.env`-tiedostoa suoraan” tiedostossa `CLAUDE.md`. Claude rikkoi sitä kerran kuukaudessa. Ei tahallaan, vaan se yksinkertaisesti unohti asian pitkän istunnon loppupuolella. Kerran `.env`-tiedostoon päätyi ylimääräinen token; toisella kerralla samanniminen muuttuja korvasi käytössä olleen muuttujan.

Siirsin säännön hookiin, jolloin sitä ei enää voinut unohtaa. Kun osuva suoran muokkauksen työkalu antaa `.env`-polun, määritetty hook hylkää kyseisen kutsun. Bash-uudelleenohjaukset, skriptit, MCP-työkalut ja muut käyttöpolut vaativat erilliset rajoitukset; esimerkki ei tee tiedostosta muuttumatonta.

<figure>
<blockquote>
<p>Hookit ovat shell-komentoja, HTTP-päätepisteitä tai LLM-kehotteita, jotka määrittelet ja jotka suoritetaan automaattisesti Claude Coden elinkaaren tietyissä kohdissa.</p>
</blockquote>
<figcaption>— Anthropic, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Toisin sanoen hook on oma komentosi, jonka Claude Code suorittaa tietyn tapahtuman yhteydessä. Jokainen tiedostomuokkaus, Bash-käynnistys, istunnon aloitus tai aliagentin valmistuminen on tapahtuma, johon voit liittää hookin.

Miksi tällä on merkitystä vibe-koodaajalle:

- Suojaa salaisuuksia sisältävät tiedostot tahattomilta muokkauksilta.
- Suorita prettier/eslint automaattisesti jokaisen koodin generoinnin jälkeen.
- Estä `git push --force` main-haaraan ilman nimenomaista lupaa.
- Lisää tuore konteksti jokaisen istunnon alussa.
- Kirjaa Clauden toiminnot tarkastusta varten.

CLAUDE.md käsittelee kontekstia. Hookit käsittelevät **toimintoja**. Nämä ovat eri asioita, ja niiden sekoittaminen on aloittelijoiden yleisin virhe.

## Miten hookit eroavat CLAUDE.md-tiedostosta ja skilleistä?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

`CLAUDE.md` on tekstiä, jonka malli lukee istunnon alussa ja jota se yrittää noudattaa. Hook on shell-komento, jonka Claude Code -prosessi suorittaa tapahtuman yhteydessä mallista riippumatta. Skill on nimetty ohjepaketti, jonka Claude voi käynnistää Skill-työkalulla funktion tapaan. Kaikki kolme palvelevat erilaisia tehtäviä.

</div>

Anthropicin dokumentaatiossa selitetään nimenomaisesti `CLAUDE.md`-tiedoston ja hookien ero:

<figure>
<blockquote>
<p>CLAUDE.md-tiedoston sisältö toimitetaan käyttäjäviestinä järjestelmäkehotteen jälkeen, ei osana järjestelmäkehotetta. Claude lukee sen ja yrittää noudattaa sitä, mutta tiukasta noudattamisesta ei ole takeita.</p>
</blockquote>
<figcaption>— Anthropic, muistidokumentaatio, https://code.claude.com/docs/en/memory</figcaption>
</figure>

Toisin sanoen `CLAUDE.md`-tiedoston sisältö välitetään Claudelle käyttäjän ensimmäisenä viestinä, ei järjestelmäohjeena. Malli lukee sen ja yrittää noudattaa sitä. Takeita ei kuitenkaan ole, etenkään pitkän istunnon loppupuolella, kun konteksti kasvaa ja vanhemmat ohjeet putoavat pois.

Hook toimii eri tavalla. Se sijaitsee `settings.json`-tiedostossa, ei riipu mallista, ja Claude Code -prosessi suorittaa sen tietyn tapahtuman yhteydessä: `PreToolUse`, `PostToolUse`, `SessionStart` ja muut. Osuva PreToolUse-tarkistus voi estää kyseisen kutsun ennen suoritusta. PostToolUse toimii jo tehdyn toiminnon jälkeen eikä peru sitä; exit-koodin vaikutus riippuu tapahtumasta.

Vertailu:

| Ominaisuus | CLAUDE.md | Hook | Skill |
|----|----|----|----|
| Sijainti | `CLAUDE.md` projektin juuressa | `.claude/settings.json` | `.claude/skills/`-kansio |
| Suorittaja | Malli lukee sen osana käyttäjäviestiä | Claude Code -prosessi tapahtuman yhteydessä | Malli Skill-työkalun kautta |
| Suoritustakuu | Ei (parhaan yrityksen mukaan) | Määritetty osuva komentohook suorittaa koodia; virheet ja poistumiskoodin vaikutus riippuvat tapahtumasta | Mallikutsun kautta (myönnetyllä luvalla, mutta ei shell-tasolla) |
| Käyttötarkoitus | Projektin konteksti, sävy, tyyli, reititys | Rajoitukset, muotoilu, tiettyjen tiedostojen tarkistukset | Monivaiheiset menettelyt parametreineen |
| Suoritusajankohta | Istunnon jokaisen malliviestin yhteydessä | Elinkaaritapahtuman yhteydessä | Kun malli käynnistää sen |
| Voiko estää toiminnon? | Ei | Osuvan PreToolUse-kutsun voi estää; PostToolUse ei peru toimintoa | Ei |

`CLAUDE.md`-malli kokonaisuudessaan sekä 6 sääntöä elävälle tiedostolle ovat oppaassa [CLAUDE.md:n määrittäminen vuonna 2026: käyttövalmis malli ja 6 sääntöä](https://ailoot.ai/blog/fi/claude-md-template/). Lue skilleistä nimettyinä toimintoina oppaasta [Claude Code Skills: oman kirjaston rakentaminen](https://ailoot.ai/blog/fi/claude-code-skills-library/).

Hook on alin taso: ei mallia, ei tulkintaa, vain shell.

Hookit ovat yksi [kontekstisuunnittelun](https://ailoot.ai/blog/fi/context-engineering/) työkaluista Plan Moden ja /compactin ohella.



## 4 hook-tyyppiä: Command, HTTP, Prompt, Agent — minkä valitset?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Claude Codessa on 4 käsittelijätyyppiä. Command on shell-skripti ja yleisin tyyppi. HTTP lähettää POST-pyynnön URL-osoitteeseesi, ja se sopii tiimeille ja integraatioille. Prompt tekee lyhyen Haiku-kutsun semanttista tarkistusta varten. Agent luo työkaluja käyttävän aliagentin perusteelliseen tarkistukseen. Aloittelijat tarvitsevat Command- ja Prompt-tyypit; ne kattavat 90% tehtävistä.

</div>

| Tyyppi | Toiminta | Milloin valita | Vaativuus |
|----|----|----|----|
| **Command** | Suorittaa shell-komennon, vastaanottaa JSONin stdin-syötteenä ja vastaa poistumiskoodilla ja stdoutilla | 90% tapauksista: polkujen tarkistaminen, muotoilu, sisällön grep, lsof, git-toiminnot | matala |
| **HTTP** | Lähettää URL-osoitteeseesi POST-pyynnön JSON-rungolla ja vastaanottaa JSON-vastauksen | Kun sääntö kuuluu tiimin/CI:n piiriin: keskitetty validointi, yhteinen tilan koontinäyttö, etätarkastus | keskitaso (vaatii palvelimen) |
| **Prompt** | Lähettää lyhyen kehotteen Haikulle (oletusarvoisesti) ja saa semanttiseen arvioon vastauksen kyllä/ei | Kun grep ei riitä: ”arvioi, kuulostaako tämä teksti yritysmäiseltä” | matala |
| **Agent** | Luo aliagentin, jolla on Read/Grep/Glob-käyttöoikeudet monivaiheisia tarkistuksia varten | Raskas validointi: ”etsi kaikki muuttuneet tiedostot ja tarkista, että niitä vastaavat testit ovat olemassa” | korkea |

Yleisin virhe on yrittää tehdä Commandilla jotain, johon tarvitaan Prompt. Esimerkiksi ”älä käytä ajatusviivoja `.md`-tiedostoissa” on grep-tehtävä, joten Command sopii siihen erinomaisesti. Mutta ”älä kirjoita yritysmäiseen sävyyn” on semanttinen sääntö ja vaatii Promptin.

Jos olet vasta aloittamassa, käytä Commandia. Kaikki alla olevat 7 käyttövalmista hookia ovat Command-tyyppiä. HTTP/Prompt/Agent tulevat mukaan myöhemmin, kun olet valmis.

## Missä settings.json sijaitsee, ja mitkä ovat 4 määritystasoa?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Claude Code etsii määrityksiä neljästä paikasta: yritystaso (MDM:n kautta käyttöönotettu), käyttäjä (`~/.claude/settings.json`), projekti (`.claude/settings.json` arkiston juuressa) ja paikallinen (`.claude/settings.local.json`, ei gitissä). Kaikki 4 tiedostoa yhdistetään istunnon alussa, ja alempi taso ohittaa ylemmän.

</div>

| Taso | Polku | Soveltamisala | Gitissä? |
|----|----|----|----|
| Yritys | `/Library/Application Support/ClaudeCode/settings.json` (Mac), `/etc/claude-code/settings.json` (Linux) | Koko tiimille MDM:n kautta | ei |
| Käyttäjä | `~/.claude/settings.json` | Kaikki yhden käyttäjän projektit | ei |
| Projekti | `.claude/settings.json` arkiston juuressa | Tämä projekti, koko tiimille | **kyllä** |
| Paikallinen | `.claude/settings.local.json` | Vain tämä projekti, vain tämä käyttäjä | **ei** (`.gitignore`-tiedostossa) |

Pidän alla kuvatut 7 hookia projektin `.claude/settings.json`-tiedostossa, joten ne tulevat automaattisesti mukaan, kun arkisto kloonataan, ja Claude noudattaa samoja sääntöjä uudessa ikkunassa. Henkilökohtaiset kokeelliset hookit laitan tiedostoon `.claude/settings.local.json`, jotta ne eivät häiritse tiimiä.

Kaikki 4 tiedostoa yhdistetään istunnon alussa. Jos säännöt ovat ristiriidassa, alempi taso ohittaa ylemmän (paikallinen ohittaa projektin, projekti käyttäjän).

**Nopein tapa luoda ensimmäinen hook** on Claude Coden interaktiivinen `/hooks`-valikko. Valitse luettelosta tapahtuma, valitse matcher (esimerkiksi `Bash` tai `Edit`) ja liitä komento. Claude Code kirjoittaa kelvollisen JSONin `settings.json`-tiedostoon puolestasi.

## 31 elinkaaritapahtumaa: mitä aloittelijan kannattaa käyttää?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Claude Codessa on tällä hetkellä 31 elinkaaritapahtumaa, jotka jakautuvat kolmeen rytmiin: kerran istunnossa (`SessionStart`/`SessionEnd`), kerran vuorossa (`UserPromptSubmit`/`Stop`) ja jokaisella työkalukutsulla (`PreToolUse`/`PostToolUse`). Aloittelija tarvitsee 4 näistä 31 tapahtumasta: `PreToolUse` rajoituksiin, `PostToolUse` automaattisiin muokkauksiin, `SessionStart` kontekstiin ja `Stop` lopputarkistuksiin.

</div>

<figure>
<blockquote>
<p>Tapahtumat esiintyvät kolmella taajuudella: kerran istunnossa (SessionStart, SessionEnd), kerran vuorossa (UserPromptSubmit, Stop, StopFailure) ja jokaisella työkalukutsulla agenttisilmukan sisällä (PreToolUse, PostToolUse).</p>
</blockquote>
<figcaption>— Anthropic, hook-viite, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

On tärkeää ymmärtää nämä kolme rytmiä, jotta vältyt sekaannukselta:

1.  **Kerran istunnossa** - `SessionStart`, `SessionEnd`, `Setup`. Nämä suoritetaan kerran, kun Claude Code käynnistyy ja sulkeutuu. Käytä niitä kontekstin alustamiseen ja väliaikaisten tiedostojen siivoamiseen.
2.  **Kerran vuorossa** - `UserPromptSubmit`, `UserPromptExpansion`, `Stop`, `StopFailure`. Nämä käynnistyvät jokaisen viestisi yhteydessä ja aina, kun malli pysähtyy. Käytä niitä keskustelun kirjaamiseen ja lopputuloksen tarkistamiseen.
3.  **Jokaisella työkalukutsulla** - `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied`, `PostToolBatch`, `SubagentStart`, `SubagentStop`. Nämä käynnistyvät jokaisen tiedostomuokkauksen, Bash-käynnistyksen, lukemisen tai haun yhteydessä. Tämä on tihein kerros, jossa rajoitukset, muotoilu ja validointi tapahtuvat.

31 tapahtuman täydellinen luettelo sisältää asynkronisia/reaktiivisia tapahtumia: `TeammateIdle`, `PreCompact`, `PostCompact`, `Elicitation`, `ElicitationResult`, `InstructionsLoaded`, `ConfigChange`, `CwdChanged`, `FileChanged`, `WorktreeCreate`, `WorktreeRemove`, `Notification`. Ne ovat edistyneisiin käyttötapauksiin; jätä ne toistaiseksi väliin.

## Viiden kysymyksen testi: milloin sääntö kuuluu hookiin ja milloin se kannattaa pitää CLAUDE.md-tiedostossa?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Jos sääntö koskee toimintaa (ei kontekstia), liittyy turvallisuuteen/rahaan/dataan, sen voi tarkistaa grepillä/regexillä, sen rikkominen on vaikea korjata ja Claude on jo rikkonut sitä ainakin kerran, se kuuluu hookiin. Jos alle kolmeen näistä viidestä kysymyksestä vastataan ”kyllä”, sääntö jää tiedostoon `CLAUDE.md`. Sovelsin testiä 80 riviini, ja siirsin 5 sääntöä hookeihin.

</div>

Minulle tämä toimii lakina, ei ehdotuksena. Pelkistin valinnan `CLAUDE.md`-tiedoston ja hookin välillä yksinkertaiseksi viiden kysymyksen testiksi. Jos vähintään kolmeen vastataan ”kyllä”, sääntö menee hookiin. Jos kyllä-vastauksia on alle kolme, se jää tekstiin.

**Testi ”milloin sääntö kuuluu hookiin?”:**

1.  Koskeeko sääntö toimintaa eikä kontekstia? (”älä suorita `rm -rf`” on toiminta; ”minulla on TypeScript-projekti” on konteksti.)
2.  Liittyykö sääntö turvallisuuteen, rahaan tai dataan? (”älä koske `.env`-tiedostoon”, ”älä julkaise avaimia”.)
3.  Voiko sen tarkistaa grepillä, regexillä tai shell-komennon poistumiskoodilla? (”tarkista `.md`-tiedoston muokkauksen jälkeen, ettei siinä ole ajatusviivaa”.)
4.  Olisiko rikkomisen seurauksia vaikea korjata? (”force-push main-haaraan” on vaikea korjata; ”piste unohtui lopusta” on helppo.)
5.  Oletko jo nähnyt Clauden unohtavan tämän säännön ainakin kerran?

**Esimerkki: ”Älä käytä ajatusviivoja, käytä vain yhdysmerkkejä.”** Toiminta? Kyllä. Turvallisuus? Ei. Tarkistettavissa grepillä? Kyllä. Vaikea korjata? Ei. Oletko nähnyt rikkomuksen? Kyllä. **3/5: laita se hookiin.**

**Vastaesimerkki: ”Minulla on vibe-koodaajia, ei kehittäjiä; kirjoita ilman slangia.”** Toiminta? Enemmän kontekstia. Turvallisuus? Ei. Tarkistettavissa regexillä? Liian vaikeaa. Vaikea korjata? Ei. Oletko nähnyt rikkomuksen? Kyllä. **1/5: pidä sääntö tiedostossa `CLAUDE.md`.**

Viisi sääntöäni läpäisi testin. Siirsin ne hookeihin. Loput 75% jäivät tekstitiedostoon.

## 7 valmista hookia kopioitavaksi

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Alla on 7 valmista hookia tiedostoon `.claude/settings.json`: estä `.env`-muokkaukset, suorita prettier automaattisesti, estä force-push main-haaraan, etsi ajatusviivoja, vapauta portti ennen komentoa `npm run dev`, tee automaattinen commit onnistuneiden testien jälkeen ja lisää tuore konteksti istunnon alussa. Kopioi kukin tiedostoon, käynnistä Claude Code uudelleen, niin se toimii.

</div>

Kaikki 7 hookia tallennetaan yhteen `.claude/settings.json`-tiedostoon projektin juuressa. Jokaisessa kohteessa on tapahtumatyyppi, työkalusuodatin (matcher) ja shell-komento. Poistumiskoodi 2 hylkää osuvan PreToolUse-kutsun ennen suorittamista. PostToolUse-tapahtumassa toiminto on jo tapahtunut: hook antaa palautetta eikä voi perua sitä.

<figure>
<blockquote>
<p>Useimpien hook-tapahtumien kohdalla vain poistumiskoodi 2 estää toiminnon. Claude Code käsittelee poistumiskoodin 1 ei-estävänä virheenä ja jatkaa toimintoa, vaikka 1 tarkoittaa Unixissa tavallisesti epäonnistumista.</p>
</blockquote>
<figcaption>— Anthropic, hook-viite, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

`exit 0` tarkoittaa onnistumista. `exit 1` tarkoittaa virhettä, mutta Claude jatkaa (tämä on ensimmäinen alla kuvattu antipattern). `exit 2` toimii tapahtumakohtaisesti: se voi estää PreToolUse-kutsun, kun taas PostToolUse antaa vain palautetta jo tehdystä toiminnosta.

### Hook 1. Tarkista suorat `.env`-muokkaukset (PreToolUse, exit 2)

Estää yritykset muokata mitä tahansa tiedostoa, joka vastaa nimiä `.env`, `.env.local`, `.env.production` ja niin edelleen.

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '(^|/)\\.env(\\.|$)' && { echo 'Älä muokkaa `.env`-tiedostoa suoraan. Kerro, mikä muuttuja pitää lisätä, niin avaan tiedoston itse.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** Ennen jokaista `Edit`/`Write`/`MultiEdit`-toimintoa se hakee tiedostopolun ja tarkistaa grepillä, vastaako se `.env`-mallia. Jos vastaa, se palauttaa poistumiskoodin 2 ja viestin. Claude näkee stderr-viestin ja sanoo minulle: ”Yritin muokata `.env`-tiedostoa, mutta hook esti sen; kerro, mikä muuttuja pitää lisätä.”

### Hook 2. Suorita prettier automaattisesti jokaisen koodimuokkauksen jälkeen (PostToolUse, exit 0)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(ts|tsx|js|jsx|json)$' && npx prettier --write \"$FILE\" 2>/dev/null & exit 0"
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** Jokaisen `Edit`/`Write`-toiminnon jälkeen se hakee polun ja suorittaa prettierin taustalla merkillä `&`, jos tiedostopääte on `.ts`/`.tsx`/`.js`/`.jsx`/`.json`. Se päättää aina poistumiskoodilla 0, koska kyse on muotoilusta, ei rajoituksesta. Jos prettier epäonnistuu, sillä ei ole väliä; tiedosto pysyy ennallaan.

Ero hookiin 1 on perustavanlaatuinen: PreToolUse + exit 2 on kova esto **ennen** toimintoa. PostToolUse + exit 0 on pehmeä muokkaus **toiminnon jälkeen**. Ne ovat kaksi eri tilaa eri tehtäviin.

### Hook 3. Estä `git push --force` main-haaraan (PreToolUse, exit 2)

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE 'git[[:space:]]+push' && echo \"$CMD\" | grep -qE '(\\-\\-force|\\-\\-force-with-lease|[[:space:]]-f([[:space:]]|$))' && echo \"$CMD\" | grep -qE '([[:space:]/:])main([[:space:]/:]|$)' && { echo 'Force-push main-haaraan on kielletty. Luo uusi haara tai pyydä minua tekemään se käsin.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** Jokaisen Bash-käynnistyksen yhteydessä se tarkistaa, että komento sisältää sanan `push`, parametrin `--force`/`--force-with-lease`/`-f` ja maininnan `main`. Jos kaikki kolme ehtoa täyttyvät, toiminto estetään selkeällä viestillä.

Aiemmin pidin tätä sääntöä `CLAUDE.md`-tiedostossa kokonaan isoilla kirjaimilla. Claude rikkoi sitä silti noin joka toinen viikko, kun pyysin sitä ”force-pushaamaan huonon commitini kumoamiseksi”. Hook tunnistaa näytetyt kirjaimelliset komentomuodot, mutta aliakset, skriptit, implisiittinen kohdehaara tai toinen Git-asiakas voivat ohittaa merkkijonotarkistuksen. Suojaa main-haara etävarastossa ja rajaa agentin käytettävissä olevia tunnistetietoja.

### Hook 4. Estä ajatusviivat tekstitiedostoissa (PostToolUse, exit 2)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(md|mdx|tsx?)$' && grep -q $'\\xe2\\x80\\x94' \"$FILE\" && { echo \"Tiedosto $FILE sisältää ajatusviivan (U+2014). Korvaa se tavallisella yhdysmerkillä (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** PostToolUse ja exit 2 ilmoittavat Claudelle epäonnistuneesta jälkitarkistuksesta muokkauksen jälkeen. Ne eivät peru tai estä jo tehtyä kirjoitusta. Jos Claude muokkaa tiedostoa ja jättää siihen ajatusviivan, hook epäonnistuu, Claude saa palautetta ja yrittää uudelleen. 2–3 istunnon jälkeen Claude lakkaa kokonaan lisäämästä tätä merkkiä.

Huomaa, että grep etsii nimenomaan ajatusviivamerkkiä (U+2014) merkkijonolla `$'\xe2\x80\x94'`, joka on Bashin merkintätapa sen kirjoittamiseksi UTF-8-tavuina. Jos kirjoitat vain `grep -q '-'`, se osuu tavallisiin yhdysmerkkeihin ja suoritetaan jokaiselle tiedostolle. Näkymätön merkki rikkoo koko logiikan.

### Hook 5. Vapauta portti 3000 ennen komentoa `npm run dev` (PreToolUse, exit 0)

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE '(npm|pnpm|yarn)[[:space:]]+run[[:space:]]+dev' && { lsof -ti:3000 | xargs -r kill -9 2>/dev/null; echo 'Portti 3000 vapautettu ennen käynnistystä' >&2; }; exit 0"
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** Ennen jokaista `npm run dev` -komentoa (tai `pnpm`/`yarn`-komentoa) se tappaa portissa 3000 olevan prosessin. Se päättää aina poistumiskoodilla 0, koska kyse on valmistelusta, ei rajoituksesta. Näin vältetään tilanne, jossa Claude näkee viestin ”portti on käytössä”, luulee kyseessä olevan koodiongelman ja alkaa korjata asioita, vaikka vanha palvelin on vain jäänyt käyntiin.

### Hook 6. Tee automaattinen commit onnistuneiden testien jälkeen (PostToolUse, exit 0)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat); CMD=$(printf '%s' \"$INPUT\" | jq -r '.tool_input.command // empty') || exit 1; EXIT=$(printf '%s' \"$INPUT\" | jq -r '.tool_response.exit_code // empty') || exit 1; case \"$CMD\" in 'npm test'|'pnpm test'|'yarn test') ;; *) exit 0 ;; esac; [ \"$EXIT\" = \"0\" ] || exit 0; git diff --cached --quiet || { echo 'Existing staged changes: review manually' >&2; exit 0; }; git add -- src/orders.ts tests/orders.test.ts || exit 1; git diff --cached --quiet && exit 0; git commit -m 'tests: checkpoint after successful run'"
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** Se lukee JSON-syötteen stdinistä kerran ja hyväksyy vain näytetyt täsmälliset testikomennot. Se jatkaa vain, jos annettu `tool_response.exit_code` on nimenomaisesti nolla. Jos asennettu versio ei tarjoa kenttää, commit ohitetaan onnistumisen arvaamisen sijaan. Korvaa `src/orders.ts` ja `tests/orders.test.ts` tehtävän omistamilla täsmällisillä tiedostoilla. Valmiiksi stagella olevat muutokset pysäyttävät reseptin, jotta muiden työ ei päädy mukaan. Epäonnistunut add pysäyttää sen, tyhjä diffi ei luo committia ja tavalliset commit-hookit säilyvät käytössä. Testaa onnistunut, epäonnistunut ja puuttuvan tuloksen syöte offline-tilassa ennen käyttöönottoa.

Antipattern: `git commit --no-verify` -komennon lisääminen jokaiseen Edit-toimintoon. Pidä commit-tarkistukset käytössä. Jos tarkistus käynnistää automaation uudelleen rekursiivisesti, rajaa laukaisin tai suunnittele tarkistus uudelleen; älä ohita tarkistuksia oletuksena. Tarkista stagella oleva diffi ja tehtävän omistus ennen automaattisten commitien käyttöönottoa.

### Hook 7. Lisää tuore konteksti istunnon alkaessa (SessionStart ja additionalContext)

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo 'no-branch'); STATUS=$(git status --short 2>/dev/null | head -10); echo \"{\\\"additionalContext\\\":\\\"Nykyinen haara: $BRANCH. Committoimattomat muutokset:\\n$STATUS\\\"}\""
          }
        ]
      }
    ]
  }
}
```

**Mitä se tekee:** Istunnon alussa se kerää nykyisen Git-haaran ja luettelon committoimattomista muutoksista ja palauttaa sitten JSONin, jossa on `additionalContext`-kenttä. Claude Code lisää tekstin ensimmäiseen järjestelmäviestiin, joten Claude tietää alusta alkaen, missä se on ja mitä edelliseltä istunnolta on vielä kesken.

<figure>
<blockquote>
<p>Käytä additionalContext-kenttää tietoihin, jotka Clauden pitäisi tietää ympäristön nykytilasta tai juuri tehdystä toiminnosta: ympäristön tila (nykyinen haara, käyttöönottokohde, aktiiviset ominaisuusliput), ehdolliset projektisäännöt (mikä testikomento koskee juuri muokattua tiedostoa), ulkoiset tiedot (sinulle osoitetut avoimet issue-asiat, viimeisimmät CI-tulokset).</p>
</blockquote>
<figcaption>— Anthropic, hook-viite, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Tätä hookia voi laajentaa hakemaan uusimman commitin ja sen kuvauksen, GitHub Actions -tilan, Linear/Jiran aktiivisen sprintin ja nykyisen arkiston avoimet issuet.

## 3 hook-sudenkuoppaa, joihin joka toinen törmää

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Ensimmäinen sudenkuoppa: exit 1 -koodilla päättyvä hook päästää toiminnon läpi hiljaisesti, ja Claude sivuuttaa virheen. Toinen: raskas työ hookissa keskeytyy 60 sekunnin aikakatkaisun jälkeen. Kolmas: hook näkee vain `~/.zshrc`-tiedoston ympäristömuuttujat, ei projektin `.env`-tiedostoa. Kaikki nämä voi korjata yhdellä rivillä, mutta ilman tietoa sudenkuopista voi vaikuttaa siltä, että ”hookit eivät toimi”.

</div>

### Sudenkuoppa 1. Exit 1 -koodilla päättyvä hook päästää toiminnon läpi hiljaisesti

Vaarallisin niistä. Luulin viikon ajan, että `.env`-suojaus toimi, mutta hookissa olikin `jq -r '.tool_input.file_path'` ilman null-tarkistusta. Kun `tool_input` oli tyhjä, jq päättyi koodiin 1. Claude tulkitsi exit 1 -koodin näin: ”hook-virhe, jatka”, joten toiminto suoritettiin. En ymmärtänyt, miksi `.env`-tiedostoon koskettiin edelleen. Huomasin syyn vasta, kun suoritin hookin manuaalisesti tyhjällä syötteellä päätteessä.

**Korjaukseen tarvitaan yksi sääntö:** tarkista jokaisen hookin alussa null-arvo ja päätä se merkintään `|| exit 0`. Tällöin odottamaton poistumiskoodi johtaa ”turvalliseen” poistumiseen sen sijaan, että virhe ohitettaisiin hiljaisesti.

```bash
# Turvallisen alun ja lopun malli:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... logiikkasi ...
exit 0   # tai || exit 0 samalla rivillä
```

### Sudenkuoppa 2. Aikakatkaisu pysäyttää hitaat hookit

Minulla oli hook, joka suoritti `eslint --fix` -komennon koko tiedostolle Edit-toiminnon jälkeen. Suurten tiedostojen (1000+ riviä) käsittely kesti 8–10 sekuntia. Claude Coden hookien oletusaikakatkaisu on 60 sekuntia, joten luulin pysyväni rajan sisällä. Mutta jos minuutin aikana tehdään useita Edit-muokkauksia, hookit kasaantuvat jonoon, ja osa niistä jää yksinkertaisesti suorittamatta. Huomasin asian vasta, kun lokiin ilmestyi ”hook timed out”.

**Korjaus:** jos hook vaatii raskasta työtä, suorita se taustalla. Käytä komentoa `eslint --fix "$FILE" &` odottamatta tulosta. Hook palauttaa silloin exit 0 -koodin millisekunneissa, kun varsinainen työ suoritetaan taustalla.

```bash
# Raskas toiminto taustalla:
npx eslint --fix "$FILE" &
exit 0
```

### Sudenkuoppa 3. Hook ei näe projektin ympäristömuuttujia

Luulin, että `process.env` olisi käytettävissä hookissa kuten Node.js-sovelluksessa. Näin ei ole. Hook suoritetaan Claude Code -isäntäprosessin käynnistämässä shellissä, ja se näkee vain `~/.zshrc`- tai `~/.bashrc`-tiedostossa olevat tiedot sekä ne, jotka Claude Code välittää erikseen. Jos hook riippuu `.env`-tiedoston `OPENAI_API_KEY`-avaimesta, sinun on luettava `.env` manuaalisesti.

**Korjaus:** lue `.env` hook-komennossa erikseen.

```bash
# Välitä projektin ympäristö hookille:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# Tai dotenvillä:
dotenv -e .env -- node my-validator.js
```

Tämä on erityisen hankalaa monorepossa, jossa eri paketeilla on omat `.env`-tiedostonsa. Ratkaise tilanne välittämällä `PWD` ja suorittamalla komennon alussa erikseen `cd "$PWD"`.

## Miten testaat hookit ennen commitin tekemistä?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Hook on shell-komento. Voit suorittaa sen manuaalisesti päätteessä valheellisella stdin-syötteellä, tarkistaa poistumiskoodin ja stderr-viestin ja tehdä tämän kaiken ennen kuin Claude Code suorittaa sen. Näin säästät tunteja virheenjäljityksessä ja vältät hookit, jotka estävät oman toimintansa.

</div>

**Perustesti.** Ota hook-komento JSONista, avaa pääte ja syötä sille JSON, jonka Claude Code lähettäisi:

```bash
# Testaa hook 1 (suojaa .env):
echo '{"tool_input":{"file_path":".env"}}' | bash -c '
FILE=$(jq -r ".tool_input.file_path // empty")
[ -z "$FILE" ] && exit 0
echo "$FILE" | grep -qE "(^|/)\.env(\.|$)" && {
  echo "BLOCKED" >&2
  exit 2
} || exit 0
'
echo "Exit code: $?"
```

Sinun pitäisi nähdä stderr-viestissä `BLOCKED` ja tulosteessa `Exit code: 2`. Jos saat exit 0 -koodin, regex ei toimi; korjaa grep.

**Tarkistettavat skenaariot:**

1.  **Onnistunut tapaus** - tiedosto pitäisi estää. Hookissa 1 `.env`, hookissa 3 `git push --force main`.
2.  **Negatiivinen tapaus** - tiedostoa ei pitäisi estää. Hookissa 1 `README.md`, hookissa 3 `git push origin feature-branch`.
3.  **Poikkeustapaukset** - tyhjä syöte, null-arvot, odottamattomat merkit. Hookin pitäisi joko palauttaa exit 0 tai epäonnistua selkeällä virheellä, ei päästää toimintoa läpi hiljaisesti.

**Kokeellisten hookien sijoituspaikka.** Älä laita niitä tiedostoon `.claude/settings.json`, vaan tiedostoon `.claude/settings.local.json` (ei gitissä). Lisää uudet hookit sinne `PostToolUse`-kohtaan käyttäen `echo`-komentoa varsinaisen toiminnon sijasta, tarkista käynnistyvätkö ne ja siirrä ne sitten tuotannon `settings.json`-tiedostoon.

```json
// Kokeilu tiedostossa settings.local.json - kirjoittaa tiedostoon /tmp/hook-debug.log:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[hook fired] $(date) $(jq -r '.tool_input.file_path // \"?\"')\" >> /tmp/hook-debug.log"
          }
        ]
      }
    ]
  }
}
```

Kun olet tehnyt Claude Codessa 5–10 muokkausta, avaa `/tmp/hook-debug.log` ja tarkista, mitkä tiedostot käynnistivät hookin. Jos tiedosto on tyhjä, matcher ei täsmää; korjaa regex.

## Mitä seuraavaksi?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

Tärkein asia

Aloita PostToolUse-hookeilla, jotka päättyvät exit 0 -koodiin; ne ovat turvallisimpia eivätkä estä mitään. Lisää viikon kuluttua ensimmäinen PreToolUse-hook exit 2 -koodilla suojaamaan `.env`-tiedostoa tai estämään force-push. Kahden viikon kuluttua `CLAUDE.md` on lyhentynyt 200-rivisestä sopimuksesta lyhyeksi, 50–80-riviseksi kontekstikuvaukseksi, ja valitut tapahtumatarkistukset on siirretty hookeihin. Ehdottomat käyttörajat toteutetaan erikseen oikeuksilla, hiekkalaatikolla ja etävaraston suojauksilla.

</div>

5 askelta tulevalle viikolle:

1.  **Etsi `CLAUDE.md`-tiedostosta** sanoja kuten ”älä koskaan”, ”kielletty”, ”aina” ja ”täytyy”. Jokainen tällainen sääntö voi sopia hookiin. Jos sääntö on ehdoton, tekstitiedosto ei pakota sitä noudattamaan.
2.  **Aloita PostToolUse + exit 0 -yhdistelmällä.** Ne ovat turvallisimpia: ne tekevät muutoksen mutta eivät estä mitään. Prettier (hook 2), kehityspalvelimen lopettaminen (hook 5), automaattinen commit testien jälkeen (hook 6).
3.  **Lisää ensimmäinen PreToolUse + exit 2 -hook vasta, kun olet varma.** Voit esimerkiksi suojata `.env`-tiedoston (hook 1) tai estää force-pushin (hook 3). Tämä on kova esto, ja grep-komennon virhe voi estää myös sinut.
4.  **Testaa hookit manuaalisesti ennen commitia** (katso yllä oleva osio). Se säästää tuntikausia virheenjäljityksessä.
5.  **Pidä `settings.local.json` kokeiluja varten.** Lisää uudet hookit sinne käyttäen echo-komentoa varsinaisen toiminnon sijasta.

Kun hookit on määritetty, kokonaisuuden seuraava vaihe on [muutosten kumoaminen Claude Codessa](https://ailoot.ai/blog/fi/claude-code-undo-changes/), jos hook päästää jotain vaarallista läpi. Jos haluat Clauden muistavan projektisi rakenteen istuntojen välillä, rakenna [Second Brain Claude Codessa](https://ailoot.ai/blog/fi/claude-code-second-brain/) (`business/`-kansio + linkitetty tietämyskanta). Jos haluat nimetä projektisi skillit, jotta niitä voi käyttää uudelleen, katso [Claude Code Skills](https://ailoot.ai/blog/fi/claude-code-skills-library/).

<div class="section" role="doc-bibliography" aria-label="Sources">

### Lähteet

- [Claude Code Hooks reference - Anthropic official documentation](https://code.claude.com/docs/en/hooks)
- [Claude Code Memory documentation - about CLAUDE.md and the system prompt](https://code.claude.com/docs/en/memory)
- [Automate workflows with hooks - Anthropic’s official guide with examples](https://code.claude.com/docs/en/hooks-guide)

</div>





Jos tarvitset pääsyn neuroverkkoihin Ailootin kautta, tutustu [Ailootiin](https://ailoot.ai/).

## Kysymyksiä tästä oppaasta: Hooks Claude Codessa 2026: vaiheittainen käyttöönotto ja 7 valmista esimerkkiä

### Miksi hookin pitää lukea JSON-syöte vain kerran?

Stdin on virta: ensimmäinen jq-prosessi kuluttaa sen, ja myöhemmät lukuyritykset kohtaavat EOF:n. Tallenna syöte kerran ja poimi komento- ja tuloskentät siitä. Käsittele puuttuva tai tuntematon tulos varmistamattomana ja ohita automaattinen commit.

### Muodostavatko regex-esimerkkihookit tietoturvarajan?

Eivät. Ne tarkistavat tietyt tapahtumat ja tunnistettavat komentomerkkijonot. Komentotulkin uudelleenohjaukset, aliakset, skriptit, MCP-työkalut tai muu kirjoituspolku voivat ohittaa ne. Ehdottomiin rajoihin tarvitaan rajatut tunnistetiedot, tiedostojen oikeudet, hiekkalaatikko ja suojatut haarat.


## Ailoot · Yritys ja sen projektisalkku

Ailoot perustuu kertyneeseen toimintakokemukseen, laajaan asiakaskuntaan, vakaaseen palveluun ja jatkuvaan KPI-seurantaan.

Yritys ja sen projektisalkku: miljoonia asiakkaita; kuukausittainen myynti miljardiluokassa.

Ailoot hyödyntää tätä toimintakokemusta tarjotessaan API-yhteyden tekoälytoimintoihin asiakkaiden tuotteissa, agenteissa, sovelluksissa ja automaatioissa.
