Claude Code Session-Hooks
Vier Claude Code Session-Lifecycle-Hooks: Init bei Bedarf ausführen, Projektkontext bei SessionStart injizieren, Transkripte sichern und Cleanup beim SessionEnd-Exit protokollieren.
Sie möchten das Framework hinter diesen Projekten?
Holen Sie sich das Claude Code System, mit dem wir produktionsreife Software planen, bauen, testen und ausliefern.
Problem: Jede neue Session startet blind. Du erklärst erneut den Branch, auf dem du bist, die Task-Queue und die Env-Vars, die deine Skripte brauchen. Wenn die Session endet, passiert das Cleanup, das hätte passieren sollen, nie.
Quick Win: Füge das in settings.json ein. Git-Kontext landet beim Start in jedem Chat:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Git' && git branch --show-current && git status --short | head -10"
}
]
}
]
}
}Jede Session öffnet sich jetzt mit Kontext. Null manuelle Einrichtung.
Die vier Session-Lifecycle-Hooks
Session-Verhalten wird von vier Hook-Typen gesteuert:
| Hook | Wann er feuert | Kann blockieren? | Anwendungsfall |
|---|---|---|---|
| Setup | Mit --init oder --maintenance | NEIN | Einmalige Einrichtung, Migrationen |
| SessionStart | Jeder Session-Start/Resume | NEIN | Kontext laden, Env-Vars setzen |
| PreCompact | Vor der Kontext-Komprimierung | NEIN | Transkripte sichern |
| SessionEnd | Session wird beendet | NEIN | Cleanup, Logging |
SessionStart: Kontext jedes Mal laden
SessionStart läuft immer, wenn eine Session beginnt oder fortgesetzt wird. Greif darauf zurück, wenn etwas immer in Claudes Kopf sein soll.
Einfache Kontext-Injektion
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Project State' && cat .claude/tasks/session-current.md 2>/dev/null || echo 'No active session'"
}
]
}
]
}
}Mit JSON-Ausgabe
Für strukturierte Kontext-Injektion:
#!/usr/bin/env python3
import json
import sys
import subprocess
def get_project_context():
try:
branch = subprocess.check_output(
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
text=True, stderr=subprocess.DEVNULL
).strip()
status = subprocess.check_output(
['git', 'status', '--porcelain'],
text=True, stderr=subprocess.DEVNULL
).strip()
changes = len(status.split('\n')) if status else 0
except:
branch, changes = "unknown", 0
return f"""=== SESSION CONTEXT ===
Git Branch: {branch}
Uncommitted Changes: {changes}
=== END ===""".strip()
output = {
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": get_project_context()
}
}
print(json.dumps(output))
sys.exit(0)SessionStart-Matcher
Auf spezifische Session-Events abzielen:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [{ "type": "command", "command": "echo 'Fresh session'" }]
},
{
"matcher": "resume",
"hooks": [{ "type": "command", "command": "echo 'Resumed session'" }]
},
{
"matcher": "compact",
"hooks": [{ "type": "command", "command": "echo 'Post-compaction'" }]
}
]
}
}startup- Neue Sessionresume- Von--resume,--continueoder/resumeclear- Nach/clearcompact- Nach Komprimierung
Umgebungsvariablen persistieren
SessionStart hat Zugriff auf CLAUDE_ENV_FILE zum Setzen von session-weiten Umgebungsvariablen:
#!/bin/bash
# Persist environment changes from nvm, pyenv, etc.
ENV_BEFORE=$(export -p | sort)
# Setup commands that modify environment
source ~/.nvm/nvm.sh
nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0Alles, was in CLAUDE_ENV_FILE geschrieben wird, erscheint in jedem Bash-Befehl, den Claude danach ausführt.
Setup: Einmalige Operationen
Setup-Hooks laufen nur, wenn du explizit --init, --init-only oder --maintenance aufrufst. Gut für Arbeit, die du nicht bei jeder neuen Session ausführen willst.
Wann Setup vs. SessionStart nutzen
| Operation | Setup nutzen | SessionStart nutzen |
|---|---|---|
| Abhängigkeiten installieren | Ja | Nein |
| Datenbank-Migrationen ausführen | Ja | Nein |
| Git-Status laden | Nein | Ja |
| Umgebungsvariablen setzen | Ja | Ja |
| Projektkontext injizieren | Nein | Ja |
| Temp-Dateien aufräumen | Ja (maintenance) | Nein |
Setup-Konfiguration
{
"hooks": {
"Setup": [
{
"matcher": "init",
"hooks": [
{
"type": "command",
"command": "npm install && npm run db:migrate"
}
]
},
{
"matcher": "maintenance",
"hooks": [
{
"type": "command",
"command": "npm prune && npm dedupe && rm -rf .cache"
}
]
}
]
}
}Aufrufen mit:
claude --init # Runs 'init' matcher
claude --init-only # Runs 'init' matcher, then exits
claude --maintenance # Runs 'maintenance' matcherSetup-Hooks können auch in CLAUDE_ENV_FILE schreiben, um Umgebungsvariablen zu persistieren.
PreCompact: Vor dem Kontextverlust
PreCompact feuert kurz vor der Komprimierung, egal ob der Nutzer sie mit /compact ausgelöst hat oder sie automatisch startete, als das Fenster sich füllte.
Transkripte sichern
#!/usr/bin/env python3
import json
import sys
import shutil
from pathlib import Path
from datetime import datetime
input_data = json.load(sys.stdin)
transcript_path = input_data.get('transcript_path', '')
trigger = input_data.get('trigger', 'unknown')
if transcript_path and Path(transcript_path).exists():
backup_dir = Path('.claude/backups')
backup_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_name = f"transcript_{trigger}_{timestamp}.jsonl"
shutil.copy2(transcript_path, backup_dir / backup_name)
# Keep only last 10 backups
backups = sorted(backup_dir.glob('transcript_*.jsonl'))
for old_backup in backups[:-10]:
old_backup.unlink()
sys.exit(0)PreCompact-Matcher
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [{ "type": "command", "command": "echo 'Auto-compacting...'" }]
},
{
"matcher": "manual",
"hooks": [{ "type": "command", "command": "echo 'Manual /compact'" }]
}
]
}
}auto- Kontextfenster voll, automatische Komprimierungmanual- Nutzer hat/compactausgeführt
Recovery-Marker erstellen
Kombiniere PreCompact mit SessionStart, um Kontext nach einer Komprimierung wieder aufzubauen. Ein funktionierendes Pattern: ein gemeinsames Backup-Core-Modul, ein Statusline-Monitor, der schwellenwertbasierte Trigger feuert, und ein PreCompact-Handler, alle koordiniert durch eine State-Datei, damit nichts zwischen Sessions verloren geht. Schau dir den Context-Recovery-Hook-Leitfaden für den vollständigen Walkthrough an.
SessionEnd: Cleanup
SessionEnd läuft, wenn eine Session auf dem Weg raus ist. Es kann das Herunterfahren nicht blockieren, aber es kann Cleanup erledigen.
Session-Stats protokollieren
#!/usr/bin/env python3
import json
import sys
from pathlib import Path
from datetime import datetime
input_data = json.load(sys.stdin)
session_id = input_data.get('session_id', 'unknown')
reason = input_data.get('reason', 'unknown')
log_dir = Path('.claude/logs')
log_dir.mkdir(parents=True, exist_ok=True)
log_entry = {
"session_id": session_id,
"ended_at": datetime.now().isoformat(),
"reason": reason
}
with open(log_dir / 'session-history.jsonl', 'a') as f:
f.write(json.dumps(log_entry) + '\n')
sys.exit(0)SessionEnd-Gründe
Das reason-Feld sagt dir, warum die Session beendet wurde:
clear- Nutzer hat/clearausgeführtlogout- Nutzer hat sich ausgeloggtprompt_input_exit- Nutzer hat beendet, während der Prompt sichtbar warother- Andere Exit-Gründe
Vollständiges Lifecycle-Beispiel
Eine vollständige Lifecycle-Konfiguration von Ende zu Ende verdrahtet:
{
"hooks": {
"Setup": [
{
"matcher": "init",
"hooks": [
{
"type": "command",
"command": "npm install && echo 'Dependencies installed'"
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Context' && git status --short && echo '## Tasks' && cat .claude/tasks/session-current.md 2>/dev/null | head -20"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "cp \"$CLAUDE_TRANSCRIPT_PATH\" .claude/backups/last-transcript.jsonl 2>/dev/null || true"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "echo \"Session ended: $(date)\" >> .claude/logs/sessions.log"
}
]
}
]
}
}Input-Payloads
SessionStart-Input
{
"session_id": "abc123",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-20250514",
"cwd": "/path/to/project"
}Setup-Input
{
"session_id": "abc123",
"hook_event_name": "Setup",
"trigger": "init",
"cwd": "/path/to/project"
}PreCompact-Input
{
"session_id": "abc123",
"hook_event_name": "PreCompact",
"transcript_path": "~/.claude/projects/.../transcript.jsonl",
"trigger": "auto",
"custom_instructions": ""
}SessionEnd-Input
{
"session_id": "abc123",
"hook_event_name": "SessionEnd",
"reason": "clear",
"cwd": "/path/to/project"
}Best Practices
-
SessionStart schnell halten - Er läuft bei jeder Session. Schwere Arbeit in Setup verschieben.
-
Setup für einmalige Arbeit nutzen - Abhängigkeits-Installationen, Migrationen, erstmaliges Projekt-Bootstrap.
-
Vor der Komprimierung sichern - PreCompact ist deine letzte Chance, das Transkript zu greifen.
-
Session-Enden protokollieren - SessionEnd ist praktisch für Analytics und Debugging.
-
Sorgfältig matchen - Unterschiedliches Verhalten für
startupvs.resumevs.compactvermeidet Überraschungen.
Nächste Schritte
- Richte den Haupt-Hooks-Leitfaden für alle 12 Hooks ein
- Konfiguriere Context Recovery für das Überleben von Komprimierungen
- Nutze Stop-Hooks für Task-Durchsetzung
- Erkunde Skill-Aktivierung für automatisches Skill-Loading
Sie möchten das Framework hinter diesen Projekten?
Holen Sie sich das Claude Code System, mit dem wir produktionsreife Software planen, bauen, testen und ausliefern.
Selbstvalidierende Claude Code Agenten
Selbstvalidierende Claude Code Agenten: PostToolUse-Lint-Hooks, Stop-Hooks und schreibgeschützte Reviewer-Subagenten in Agentendefinitionen verdrahten, damit fehlerhafte Ausgaben nie ausgeliefert werden.
Kontext-Backup-Hooks für Claude Code
Ein StatusLine-gesteuerter Claude Code Kontext-Backup-Hook. Schreibt strukturierte Snapshots alle 10K Tokens, damit die Auto-Komprimierung nie Fehler, Signaturen und Entscheidungen verschluckt.

