# Hooks i Claude Code 2026: trinnvis oppsett og 7 ferdige eksempler

> 7 ferdige hooks for .claude/settings.json, klare til å kopiere og lime inn: beskyttelse av .env, prettier, force-push til main, em-dash og en utviklingsserver. 4 handlertyper og 31 hendelser.

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


Hooks i Claude Code er shell-kommandoer, HTTP-kall eller korte LLM-sjekker som kjøres automatisk før eller etter en Claude-handling. De legger til kontroller ved bestemte verktøyhendelser, formaterer kode etter redigering og tilfører kontekst ved øktstart. En matchende PreToolUse-hook kan avslå det kallet. Regex-eksemplene er praktiske vern, ikke sikkerhetsgrense for alle fil- eller Git-ruter. Nedenfor finner du en gjennomgang av 4 handlertyper, 31 livssyklushendelser, en test for «når en regel hører hjemme i en hook», 7 ferdige hooks for `.claude/settings.json` og 3 fallgruver jeg selv har støtt på.



[Ailoot-dokumentasjon: API, nøkler og oppsett](https://ailoot.ai/docs/).

## Hva er hooks i Claude Code?

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

Viktigste punkt

En hook er en shell-kommando, et HTTP-kall, en kort LLM-forespørsel eller en subagent som Claude Code kjører som svar på en bestemt livssyklushendelse. I motsetning til instruksjoner i `CLAUDE.md`, som modellen kan ignorere eller glemme, kjører en hook på samme måte hver gang: det er kode, ikke et forslag.

</div>

I et halvt år hadde jeg regelen «aldri rediger `.env` direkte» i `CLAUDE.md`. Claude brøt den omtrent én gang i måneden. Ikke med vilje; den glemte det rett og slett mot slutten av en lang økt. En gang havnet et ekstra token i `.env`; en annen gang overskrev en variabel med duplisert navn den som var i bruk.

Jeg flyttet regelen til en hook, slik at den ikke kunne glemmes. Når et matchende direkte redigeringsverktøy oppgir en `.env`-bane, avviser den konfigurerte hooken kallet. Bash-omdirigering, skript, MCP og andre tilgangsruter trenger separate kontroller. Eksemplet gjør ikke filen uforanderlig.

<figure>
<blockquote>
<p>Hooks er shell-kommandoer, HTTP-endepunkter eller LLM-forespørsler som du definerer, og som kjøres automatisk på bestemte punkter i Claude Codes livssyklus.</p>
</blockquote>
<figcaption>— Anthropic, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Med andre ord er en hook kommandoen din som Claude Code kjører ved en bestemt hendelse. Hver filredigering, Bash-oppstart, øktstart eller fullføring av en subagent er en hendelse du kan knytte en hook til.

Hvorfor dette er viktig for en vibe coder:

- Beskytt filer som inneholder hemmeligheter, mot utilsiktede redigeringer.
- Kjør prettier/eslint automatisk etter hver kodegenerering.
- Blokker `git push --force` til main uten uttrykkelig tillatelse.
- Legg til fersk kontekst ved starten av hver økt.
- Loggfør Claudes handlinger for revisjon.

CLAUDE.md håndterer kontekst. Hooks håndterer **handlinger**. Dette er to forskjellige ting, og å forveksle dem er den vanligste nybegynnerfeilen.

## Hvordan skiller hooks seg fra CLAUDE.md og skills?

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

Viktigste punkt

`CLAUDE.md` er tekst som modellen leser ved starten av en økt og prøver å følge. En hook er en shell-kommando som Claude Code-prosessen kjører som svar på en hendelse, uavhengig av modellen. En skill er en navngitt pakke med instruksjoner som Claude kan påkalle med Skill-verktøyet, omtrent som en funksjon. Alle tre dekker forskjellige typer oppgaver.

</div>

Anthropics dokumentasjon forklarer uttrykkelig forskjellen mellom `CLAUDE.md` og hooks:

<figure>
<blockquote>
<p>Innholdet i CLAUDE.md leveres som en brukermelding etter systemprompten, ikke som en del av systemprompten. Claude leser det og prøver å følge det, men streng etterlevelse er ikke garantert.</p>
</blockquote>
<figcaption>— Anthropic, dokumentasjon om minne, https://code.claude.com/docs/en/memory</figcaption>
</figure>

Sagt på en annen måte: innholdet i `CLAUDE.md` når Claude som den første meldingen fra brukeren, ikke som en systeminstruksjon. Modellen leser det og prøver å følge det. Men det finnes ingen garantier, særlig mot slutten av en lang økt, når konteksten vokser og eldre instruksjoner skyves ut.

En hook fungerer annerledes. Den ligger i `settings.json`, er ikke avhengig av modellen og kjøres av Claude Code-prosessen ved en bestemt hendelse: `PreToolUse`, `PostToolUse`, `SessionStart` og andre. Kommandoen kjøres før Claude mottar resultatet, og kan fysisk blokkere en handling (`exit 2`) eller endre en fil etter redigering (`exit 0`).

Sammenligning:

| Funksjon | CLAUDE.md | Hook | Skill |
|----|----|----|----|
| Hvor ligger den? | `CLAUDE.md` i prosjektets rotmappe | `.claude/settings.json` | Mappen `.claude/skills/` |
| Hvem kjører den? | Modellen leser den som del av en brukermelding | Claude Code-prosessen ved en hendelse | Modellen via Skill-verktøyet |
| Garanti for kjøring | Nei (best effort) | Konfigurert matchende kommandohook kjører kode; feil og avslutningsatferd avhenger av hendelsen | Via et modellkall (med godkjenning, men ikke på shell-nivå) |
| Hva brukes den til? | Prosjektkontekst, tone, stil, ruting | Begrensninger, formatering, kontroller av bestemte filer | Komplekse prosedyrer med flere trinn og parametere |
| Når kjører den? | Ved hver modellmelding i en økt | Ved en livssyklushendelse | Når modellen påkaller den |
| Kan den blokkere en handling? | Nei | Ja (`exit 2`) | Nei |

Den fullstendige `CLAUDE.md`-malen og 6 regler for en levende fil finnes i veiledningen [Slik konfigurerer du CLAUDE.md i 2026: en ferdig mal og 6 regler](https://ailoot.ai/blog/nb/claude-md-template/). Les om skills som navngitte funksjoner i [Claude Code Skills: slik bygger du ditt eget bibliotek](https://ailoot.ai/blog/nb/claude-code-skills-library/).

En hook er det laveste nivået: ingen modell, ingen tolkning, bare shell.

Hooks er ett av verktøyene for [kontekstteknikk](https://ailoot.ai/blog/nb/context-engineering/), sammen med Plan Mode og /compact.



## 4 hooktyper: Command, HTTP, Prompt, Agent – hvilken bør du velge?

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

Viktigste punkt

Claude Code har 4 handlertyper. Command er et shell-skript og den vanligste typen. HTTP sender en POST-forespørsel til URL-en din, for team og integrasjoner. Prompt foretar et kort Haiku-kall for en semantisk sjekk. Agent oppretter en subagent med tilgang til verktøy for grundig validering. Nybegynnere trenger Command og Prompt; de dekker 90% av oppgavene.

</div>

| Type | Hva den gjør | Når du bør velge den | Kompleksitet |
|----|----|----|----|
| **Command** | Kjører en shell-kommando, mottar JSON på stdin og svarer med en exitkode og stdout | I 90% av tilfellene: kontroll av stier, formatering, grep i innhold, lsof, git-operasjoner | lav |
| **HTTP** | Sender en POST-forespørsel med en JSON-body til URL-en din og mottar JSON som svar | Når en regel hører hjemme i et team/CI: sentralisert validering, et felles statusdashboard, ekstern revisjon | middels (krever en server) |
| **Prompt** | Sender en kort prompt til Haiku (som standard) og får ja/nei for en semantisk vurdering | Når grep ikke er nok: «vurder om denne teksten høres bedriftsaktig ut» | lav |
| **Agent** | Oppretter en subagent med tilgang til Read/Grep/Glob for kontroller med flere trinn | Grundig validering: «finn alle endrede filer og sjekk at det finnes tilsvarende tester» | høy |

Den vanligste feilen er å prøve å bruke Command til noe som krever Prompt. «Ikke bruk em-dash i `.md`-filer» er for eksempel en grep-oppgave, så Command passer perfekt. Men «ikke skriv i en bedriftsaktig tone» er semantisk og krever derfor Prompt.

Hvis du nettopp har begynt, bruk Command. Alle de 7 ferdige hookene nedenfor er Command. HTTP/Prompt/Agent kommer senere, når du er klar.

## Hvor ligger settings.json, og hva er de 4 konfigurasjonsnivåene?

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

Viktigste punkt

Claude Code leter etter konfigurasjon på 4 steder: enterprise (distribuert via MDM), bruker (`~/.claude/settings.json`), prosjekt (`.claude/settings.json` i repoets rotmappe) og lokal (`.claude/settings.local.json`, ikke i git). Alle 4 filene slås sammen ved starten av en økt, og det lavere nivået har forrang.

</div>

| Nivå | Sti | Når gjelder det? | I git? |
|----|----|----|----|
| Enterprise | `/Library/Application Support/ClaudeCode/settings.json` (Mac), `/etc/claude-code/settings.json` (Linux) | For hele teamet via MDM | nei |
| Bruker | `~/.claude/settings.json` | Alle prosjekter for én bruker | nei |
| Prosjekt | `.claude/settings.json` i repoets rotmappe | Dette prosjektet, for hele teamet | **ja** |
| Lokal | `.claude/settings.local.json` | Bare dette prosjektet, bare denne brukeren | **nei** (i `.gitignore`) |

Jeg oppbevarer de 7 hookene som beskrives nedenfor i prosjektets **`.claude/settings.json`**, slik at de inkluderes automatisk når repoet klones, og Claude følger de samme reglene i et nytt vindu. Personlige eksperimentelle hooks ligger i `.claude/settings.local.json`, så de ikke kommer i veien for teamet.

Alle 4 filene slås sammen ved starten av en økt. Hvis regler er i konflikt, har det lavere nivået forrang (lokalt over prosjekt, prosjekt over bruker).

**Den raskeste måten å lage din første hook på** er den interaktive `/hooks`-menyen i Claude Code. Velg en hendelse fra listen, velg en matcher (for eksempel `Bash` eller `Edit`), og lim inn en kommando. Claude Code skriver gyldig JSON til `settings.json` for deg.

## 31 livssyklushendelser: hvilke trenger nybegynnere?

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

Viktigste punkt

Claude Code har for tiden 31 livssyklushendelser, delt inn i 3 frekvenser: én gang per økt (`SessionStart`/`SessionEnd`), én gang per tur (`UserPromptSubmit`/`Stop`) og ved hvert verktøykall (`PreToolUse`/`PostToolUse`). Nybegynnere trenger 4 av de 31 hendelsene: `PreToolUse` for begrensninger, `PostToolUse` for automatiske redigeringer, `SessionStart` for kontekst og `Stop` for sluttkontroller.

</div>

<figure>
<blockquote>
<p>Hendelser inntreffer med tre frekvenser: én gang per økt (SessionStart, SessionEnd), én gang per tur (UserPromptSubmit, Stop, StopFailure) og ved hvert verktøykall i den agentiske løkken (PreToolUse, PostToolUse).</p>
</blockquote>
<figcaption>— Anthropic, referanse for hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Det er viktig å forstå de tre frekvensene, slik at du ikke blir forvirret:

1.  **Én gang per økt** - `SessionStart`, `SessionEnd`, `Setup`. Disse kjøres én gang når Claude Code åpnes og lukkes. Bruk dem til å initialisere kontekst og rydde opp i midlertidige filer.
2.  **Én gang per tur** - `UserPromptSubmit`, `UserPromptExpansion`, `Stop`, `StopFailure`. Disse utløses ved hver melding fra deg og hver gang modellen stopper. Bruk dem til å loggføre samtalen og kontrollere sluttresultatet.
3.  **Ved hvert verktøykall** - `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied`, `PostToolBatch`, `SubagentStart`, `SubagentStop`. Disse utløses ved hver filredigering, Bash-oppstart, lesehandling eller søk. Dette er det mest aktive laget, der begrensninger, formatering og valideringer hører hjemme.

Den fullstendige listen over 31 hendelser inkluderer også asynkrone/reaktive hendelser: `TeammateIdle`, `PreCompact`, `PostCompact`, `Elicitation`, `ElicitationResult`, `InstructionsLoaded`, `ConfigChange`, `CwdChanged`, `FileChanged`, `WorktreeCreate`, `WorktreeRemove`, `Notification`. Disse er for avanserte scenarioer; hopp over dem foreløpig.

## Test med fem spørsmål: når hører en regel hjemme i en hook, og når blir den i CLAUDE.md?

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

Viktigste punkt

Hvis en regel gjelder en handling (ikke kontekst), handler om sikkerhet/penger/data, kan kontrolleres med grep/regex, er vanskelig å gjøre om hvis den brytes, og Claude allerede har brutt den minst én gang, hører den hjemme i en hook. Hvis færre enn tre av de fem svarene er «ja», blir den i `CLAUDE.md`. Jeg brukte testen på de 80 linjene mine, og 5 regler ble flyttet til hooks.

</div>

For meg fungerer dette som en lov, ikke et forslag. Jeg kokte valget mellom `CLAUDE.md` og en hook ned til en enkel test med fem spørsmål. Hvis minst tre svar er «ja», legges regelen i en hook. Færre enn tre, og den blir stående i teksten.

**Testen «når hører en regel hjemme i en hook?»:**

1.  Gjelder denne regelen en handling snarere enn kontekst? («ikke kjør `rm -rf`» er en handling; «jeg har et TypeScript-prosjekt» er kontekst.)
2.  Handler denne regelen om sikkerhet, penger eller data? («ikke rør `.env`», «ikke publiser nøkler».)
3.  Kan du kontrollere den med grep, regex eller exitkoden til en shell-kommando? («etter redigering av `.md`, sjekk at det ikke finnes em-dash.»)
4.  Ville konsekvensene være smertefulle å gjøre om hvis regelen brytes? («force-push til main» er vanskelig å gjøre om; «glemte punktum på slutten» er enkelt.)
5.  Har du allerede sett Claude glemme denne regelen minst én gang?

**Eksempel: «Ikke bruk em-dash; bruk bare bindestreker.»** Handling? Ja. Sikkerhet? Nei. Kan kontrolleres med grep? Ja. Vanskelig å gjøre om? Nei. Sett den brutt? Ja. **3 av 5: legg den i en hook.**

**Moteksempel: «Jeg har vibe coders, ikke utviklere; skriv uten slang.»** Handling? Mer som kontekst. Sikkerhet? Nei. Kan kontrolleres med regex? For vanskelig. Vanskelig å gjøre om? Nei. Sett den brutt? Ja. **1 av 5: behold den i `CLAUDE.md`.**

Fem av reglene mine besto testen. De ble flyttet til hooks. De resterende 75% ble stående i tekstfilen.

## 7 hooks du kan kopiere

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

Viktigste punkt

Nedenfor finner du 7 ferdige hooks for `.claude/settings.json`: blokker `.env`-redigeringer, kjør prettier automatisk, forhindre force-push til main, sjekk etter em-dash, frigjør en port før `npm run dev`, auto-commit etter vellykkede tester og legg til fersk kontekst ved starten av en økt. Kopier hver hook inn i filen, start Claude Code på nytt, så fungerer den.

</div>

Alle 7 hookene ligger i én `.claude/settings.json`-fil i prosjektets rotmappe. Hver oppføring har en hendelsestype, et verktøyfilter (matcher) og en shell-kommando. Exitkode 2 avviser et matchende PreToolUse-kall før utføring. I PostToolUse har handlingen allerede skjedd. Hooken gir tilbakemelding og kan ikke gjøre den ugjort.

<figure>
<blockquote>
<p>For de fleste hook-hendelser er det bare exitkode 2 som blokkerer en handling. Claude Code behandler exitkode 1 som en ikke-blokkerende feil og fortsetter handlingen, selv om 1 vanligvis betyr feil i Unix.</p>
</blockquote>
<figcaption>— Anthropic, referanse for hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

`exit 0` betyr suksess. `exit 1` betyr feil, men Claude fortsetter (dette er det første antipatternet nedenfor). `exit 2` har hendelsesspesifikk atferd. Det kan blokkere PreToolUse, mens PostToolUse bare gir tilbakemelding om en allerede utført handling.

### Hook 1. Kontroller direkte `.env`-redigeringer (PreToolUse, exit 2)

Blokkerer forsøk på å redigere filer direkte som samsvarer med `.env`, `.env.local`, `.env.production` og så videre.

```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 'Do not edit `.env` directly. Tell me which variable to add, and I’ll open the file myself.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** Før hver `Edit`/`Write`/`MultiEdit` henter den filstien, sjekker med grep om den samsvarer med `.env`-mønsteret, og returnerer exitkode 2 med en melding hvis den gjør det. Claude ser stderr og sier til meg: «Jeg prøvde å redigere `.env`, men hooken blokkerte meg; si hvilken variabel jeg skal legge til.»

### Hook 2. Kjør prettier automatisk etter hver kodeendring (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"
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** Etter hver `Edit`/`Write` henter den filstien og kjører prettier i bakgrunnen med `&` hvis filtypen er `.ts`/`.tsx`/`.js`/`.jsx`/`.json`. Den avslutter alltid med exitkode 0 fordi dette handler om formatering, ikke en begrensning. Hvis prettier feiler, er det ikke så farlig; filen forblir som den var.

Forskjellen fra hook 1 er grunnleggende: PreToolUse + `exit 2` er en hard blokkering **før** handlingen. PostToolUse + `exit 0` er en myk redigering **etter** handlingen. Dette er to forskjellige moduser for forskjellige oppgaver.

### Hook 3. Blokker `git push --force` til main-branchen (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 to main is forbidden. Create a new branch or ask me to do it manually.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** Ved hver Bash-oppstart sjekker den om kommandoen inneholder `push`, inkluderer `--force`/`--force-with-lease`/`-f` og nevner `main`. Hvis alle tre betingelsene stemmer, blokkerer den med en tydelig melding.

Før hadde jeg denne regelen i `CLAUDE.md` med bare store bokstaver. Claude brøt den fortsatt omtrent annenhver uke når jeg ba den om å «force-pushe for å angre den dårlige commiten min». Hooken fanger bokstavelige kommandoformer her, men aliaser, skript, implisitt målgren eller annen Git-klient kan omgå tekstkontrollen. Håndhev main-grenvern hos remote og begrens agentens tilganger.

### Hook 4. Blokker em-dash i tekstfiler (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 \"File $FILE contains an em dash (U+2014). Replace it with a regular hyphen (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** PostToolUse med exitkode 2 rapporterer etterkontrollfeil til Claude etter redigeringen. Den ruller ikke tilbake og hindrer ikke skrivingen. Hvis Claude redigerte en fil og lot et em-dash stå igjen, mislykkes hooken, Claude får tilbakemelding og prøver igjen. Etter 2–3 økter slutter Claude helt å sette inn dette tegnet.

Merk at grep søker spesifikt etter em-dash-tegnet (U+2014) med `$'\xe2\x80\x94'`, Bash-notasjonen for å skrive det som UTF-8-byte. Hvis du bare skriver `grep -q '-'`, vil den også finne vanlige bindestreker og kjøre for hver fil. Ett usynlig tegn ødelegger hele logikken.

### Hook 5. Frigjør port 3000 før `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 'Port 3000 freed before launch' >&2; }; exit 0"
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** Før hver `npm run dev` (eller `pnpm`/`yarn`) avslutter den prosessen på port 3000. Den avslutter alltid med exitkode 0 fordi dette er forberedelse, ikke en begrensning. Dette beskytter mot en type felle der Claude ser «port i bruk», tror det er et kodeproblem og begynner å fikse ting, mens den gamle serveren bare fortsatt kjører.

### Hook 6. Auto-commit etter vellykkede tester (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'"
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** JSON-stdin leses én gang, og bare eksakte enkle testkommandoer godtas. Den fortsetter bare når `tool_response.exit_code` uttrykkelig er null. Mangler feltet i installert versjon, hoppes commit over fremfor å gjette suksess. Erstatt `src/orders.ts` og `tests/orders.test.ts` med eksakte filer eid av oppgaven. Eksisterende staged endringer stopper oppskriften, så uvedkommende arbeid ikke inkluderes. Feilet add stopper, tom diff lager ingen commit og vanlige commit-hooks beholdes. Test vellykket, feilet og manglende resultat frakoblet før aktivering.

Antipattern: `git commit --no-verify` ved hver Edit. Behold commit-kontroller. Utløser en kontroll automatiseringen rekursivt, avgrens utløseren eller endre kontrollen fremfor å omgå verifikasjon som standard. Gjennomgå staged diff og oppgaveeierskap før automatiske commits aktiveres.

### Hook 7. Legg til fersk kontekst ved øktstart (SessionStart med 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\\\":\\\"Current branch: $BRANCH. Uncommitted changes:\\n$STATUS\\\"}\""
          }
        ]
      }
    ]
  }
}
```

**Hva den gjør:** Ved øktstart henter den gjeldende Git-branch og listen over endringer som ikke er committet, og returnerer deretter JSON med et `additionalContext`-felt. Claude Code legger denne teksten til den første systemmeldingen, slik at Claude fra starten av vet hvor den er og hva som gjenstår fra forrige økt.

<figure>
<blockquote>
<p>Bruk additionalContext for informasjon Claude bør kjenne til om miljøets nåværende tilstand eller en nettopp fullført operasjon: miljøtilstand (gjeldende branch, distribusjonsmål, aktive feature flags), betingede prosjektregler (hvilken testkommando som gjelder for filen som nettopp ble redigert), eksterne data (åpne saker som er tildelt deg, nylige CI-resultater).</p>
</blockquote>
<figcaption>— Anthropic, referanse for hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Utvidelser av denne hooken: hent siste commit og beskrivelsen av den, GitHub Actions-status, aktiv sprint fra Linear/Jira og åpne saker for det gjeldende repoet.

## 3 fallgruver med hooks som annenhver person støter på

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

Viktigste punkt

Den første fallgruven: En hook med exit 1 passerer lydløst, og Claude ignorerer feilen. Den andre: Tungt arbeid i en hook avbrytes etter en tidsavbruddsgrense på 60 sekunder. Den tredje: En hook ser bare miljøvariablene fra `~/.zshrc`, ikke prosjektets `.env`. Hver av dem kan fikses med én linje, men uten kjennskap til disse fallgruvene kan det virke som om «hooks ikke fungerer».

</div>

### Fallgruve 1. En hook med exit 1 passerer lydløst

Dette er den farligste. I en uke trodde jeg `.env`-beskytteren fungerte, men det viste seg at jeg hadde `jq -r '.tool_input.file_path'` i hooken uten å kontrollere for null. Når `tool_input` var tom, avsluttet jq med kode 1. Claude behandlet exit 1 som «hookfeil, fortsett», så handlingen ble gjennomført. Jeg skjønte ikke hvorfor `.env` fortsatt ble berørt. Jeg så årsaken først da jeg kjørte hooken manuelt i en terminal med tom input.

**Løsningen er én regel:** Start hver hook med å kontrollere for null, og avslutt den med `|| exit 0`. Da fører en uventet exit til en «trygg» avslutning i stedet for å passere lydløst.

```bash
# Mal for trygg start og avslutning:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... logikken din ...
exit 0   # eller || exit 0 på én linje
```

### Fallgruve 2. Langsomme hooks avbrytes av et tidsavbrudd

Jeg hadde en hook som kjørte `eslint --fix` på hele filen etter Edit. Store filer (1000+ linjer) tok 8–10 sekunder å behandle. Standard tidsavbrudd for hooks i Claude Code er 60 sekunder, så jeg trodde jeg holdt meg innenfor grensen. Men når flere Edits skjer i løpet av ett minutt, hoper hookene seg opp og havner i kø, og noen blir rett og slett hoppet over. Jeg skjønte det først da loggen viste «hook timed out».

**Løsning:** Hvis en hook krever tungt arbeid, kjør det i bakgrunnen. Bruk `eslint --fix "$FILE" &` uten å vente på resultatet. Da returnerer hooken exit 0 på millisekunder mens selve arbeidet kjører i bakgrunnen.

```bash
# Tung operasjon i bakgrunnen:
npx eslint --fix "$FILE" &
exit 0
```

### Fallgruve 3. En hook kan ikke se prosjektets miljøvariabler

Jeg trodde `process.env` ville være tilgjengelig i en hook slik det er i en Node.js-app. Det er det ikke. Hooken kjører i et shell som startes av Claude Code-foreldreprosessen og ser bare det som finnes i `~/.zshrc` eller `~/.bashrc`, pluss det Claude Code uttrykkelig sender videre. Hvis hooken din er avhengig av `OPENAI_API_KEY` fra `.env`, må du lese `.env` manuelt.

**Løsning:** Les `.env` uttrykkelig i hook-kommandoen.

```bash
# Send prosjektmiljøet videre til hooken:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# Eller med dotenv:
dotenv -e .env -- node my-validator.js
```

Dette er spesielt plagsomt i en monorepo, der ulike pakker har hver sine `.env`-filer. Løs det ved å sende videre `PWD` og uttrykkelig kjøre `cd "$PWD"` i starten av kommandoen.

## Hvordan tester du hooks før du committer?

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

Viktigste punkt

En hook er en shell-kommando. Du kan kjøre den manuelt i en terminal med falsk stdin, kontrollere exitkoden og stderr, og gjøre alt dette før Claude Code kjører den. Det sparer timer med feilsøking og beskytter deg mot hooks som blokkerer sin egen funksjon.

</div>

**Grunnleggende testscenario.** Hent hook-kommandoen fra JSON, åpne en terminal og send inn JSON-en Claude Code ville ha sendt:

```bash
# Test hook 1 (beskytt .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: $?"
```

Du skal se `BLOCKED` i stderr og `Exit code: 2`. Hvis du får exit 0, fungerer ikke regex-uttrykket ditt; rett grep.

**Scenarioer du bør kontrollere:**

1.  **Positivt tilfelle** - filen skal blokkeres. `.env` for hook 1, `git push --force main` for hook 3.
2.  **Negativt tilfelle** - filen skal ikke blokkeres. `README.md` for hook 1, `git push origin feature-branch` for hook 3.
3.  **Grensetilfeller** - tom input, null-verdier, uventede tegn. Hooken skal enten returnere exit 0 eller feile med en tydelig melding, ikke passere lydløst.

**Hvor eksperimentelle hooks skal legges.** Ikke i `.claude/settings.json`, men i `.claude/settings.local.json` (ikke i git). Legg nye hooks der under `PostToolUse`, med `echo` i stedet for den virkelige handlingen. Kontroller om de utløses, og flytt dem deretter til produksjonsfilen `settings.json`.

```json
// Eksperiment i settings.local.json - skriver til /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"
          }
        ]
      }
    ]
  }
}
```

Etter 5–10 redigeringer i Claude Code åpner du `/tmp/hook-debug.log` for å se hvilke filer som utløste hooken. Hvis filen er tom, passer ikke matcheren; rett regex-uttrykket.

## Hva nå?

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

Viktigste punkt

Begynn med PostToolUse-hooks som avslutter med exit 0; de er tryggest og blokkerer ingenting. Etter en uke legger du til din første PreToolUse-hook med exit 2 for å beskytte `.env` eller forhindre force-push. Etter to uker krymper `CLAUDE.md` fra en kontrakt på 200 linjer til en kort kontekstbeskrivelse på 50–80 linjer, mens utvalgte hendelseskontroller flyttes til hooks og harde tilgangsgrenser håndheves separat av tillatelser, sandbox og eksternt vern.

</div>

5 steg for den kommende uken:

1.  **Søk i `CLAUDE.md` med grep** etter ord som «aldri», «forbudt», «alltid» og «må». Hver slik regel er en kandidat for en hook. Hvis en regel er en instruks, håndhever ikke tekstfilen den.
2.  **Begynn med PostToolUse + `exit 0`.** Dette er tryggest: de gjør en endring, men blokkerer ingenting. Prettier (hook 2), avslutte utviklingsserveren (hook 5), automatisk commit etter tester (hook 6).
3.  **Legg først til din første PreToolUse + `exit 2` når du føler deg trygg.** Beskytt for eksempel `.env` (hook 1) eller forhindre force-push (hook 3). Dette er en hard blokkering, og en feil i grep-kommandoen kan også blokkere deg selv.
4.  **Test hooks manuelt før du committer** (se delen ovenfor). Det sparer timer med feilsøking.
5.  **Bruk `settings.local.json` til eksperimenter.** Legg nye hooks der med echo i stedet for den virkelige handlingen.

Når hookene er konfigurert, er neste steg i denne artikkelserien [slik angrer du endringer i Claude Code](https://ailoot.ai/blog/nb/claude-code-undo-changes/), i tilfelle en hook slipper gjennom noe farlig. Hvis du vil at Claude skal huske prosjektstrukturen mellom økter, bygger du en [Second Brain i Claude Code](https://ailoot.ai/blog/nb/claude-code-second-brain/) (`business/`-mappe + koblet kunnskapsbase). Og hvis du vil gi prosjektets skills navn slik at de kan gjenbrukes, kan du lese [Claude Code Skills](https://ailoot.ai/blog/nb/claude-code-skills-library/).

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

### Kilder

- [Claude Code Hooks-referanse - offisiell dokumentasjon fra Anthropic](https://code.claude.com/docs/en/hooks)
- [Dokumentasjon om Claude Code-minne - om CLAUDE.md og systemprompten](https://code.claude.com/docs/en/memory)
- [Automatiser arbeidsflyter med hooks - Anthropics offisielle veiledning med eksempler](https://code.claude.com/docs/en/hooks-guide)

</div>





Hvis du trenger tilgang til nevrale nettverk gjennom Ailoot, kan du ta en titt på [Ailoot](https://ailoot.ai/).

## Spørsmål om denne veiledningen: Hooks i Claude Code 2026: trinnvis oppsett og 7 ferdige eksempler

### Hvorfor må en hook lese JSON-inndata bare én gang?

Stdin er en strøm. Første jq-prosess bruker den opp, slik at neste lesing møter EOF. Lagre inndata én gang og hent kommando- og resultatfelt fra verdien. Manglende eller ukjent resultat må behandles som uverifisert og hoppe over automatisk commit.

### Etablerer regex-hookene en sikkerhetsgrense?

Nei. De kontrollerer bestemte hendelser og gjenkjennelige kommandostrenger. Shell-omdirigering, aliaser, skript, MCP eller annen skriverute kan omgå. Bruk begrensede tilganger, filsystemtillatelser, sandbox og beskyttede grener for harde grenser.


## Ailoot · Selskapet og dets prosjektportefølje

Ailoot bygger på etablert driftserfaring, en stor kundebase, stabil tjeneste og løpende KPI-kontroll.

Selskapet og dets prosjektportefølje: millioner av kunder; månedlig salg for milliarder.

Ailoot bruker denne driftserfaringen til API-tilgang til AI-funksjoner i kundenes produkter, agenter, applikasjoner og automatiseringer.
