Claude Code セッションフック
4 つの Claude Code セッションライフサイクルフック: オンデマンドで init を実行し、SessionStart でプロジェクトコンテキストを注入し、トランスクリプトをバックアップし、SessionEnd 終了時にログクリーンアップを行う。
設定をやめて、構築を始めよう。
AIオーケストレーション付きSaaSビルダーテンプレート。
問題点: 新しいセッションはいつも白紙から始まる。作業中のブランチ、タスクキュー、スクリプトが必要な環境変数を毎回説明し直す。セッションが終わっても、やるべきだったクリーンアップは行われない。
すぐに使えるコード: これを settings.json に貼り付ける。毎回の起動時に Git コンテキストがチャットに届く:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Git' && git branch --show-current && git status --short | head -10"
}
]
}
]
}
}すべてのセッションがコンテキスト付きで始まる。手動セットアップは不要。
4 つのセッションライフサイクルフック
セッションの動作は 4 種類のフックタイプで制御される:
| フック | 発火タイミング | ブロック可否 | 用途 |
|---|---|---|---|
| Setup | --init または --maintenance で | NO | 初回セットアップ、マイグレーション |
| SessionStart | セッション開始/再開のたびに | NO | コンテキスト読み込み、環境変数の設定 |
| PreCompact | コンテキストコンパクション前 | NO | トランスクリプトのバックアップ |
| SessionEnd | セッション終了時 | NO | クリーンアップ、ログ |
SessionStart: 毎回コンテキストを読み込む
SessionStart はセッションが開始または再開されるたびに実行される。Claude の頭の中に常に入れておくべきものがあるときに使う。
基本的なコンテキスト注入
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Project State' && cat .claude/tasks/session-current.md 2>/dev/null || echo 'No active session'"
}
]
}
]
}
}JSON 出力を使う場合
構造化されたコンテキスト注入のために:
#!/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 のマッチャー
特定のセッションイベントをターゲットにする:
{
"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- 新しいセッションresume---resume、--continue、または/resumeからclear-/clear後compact- コンパクション後
環境変数の永続化
SessionStart は CLAUDE_ENV_FILE にアクセスしてセッション全体で有効な環境変数を設定できる:
#!/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 0CLAUDE_ENV_FILE に書き込んだものはすべて、その後 Claude が実行するすべての Bash コマンドに反映される。
Setup: 初回限りの操作
Setup フックは --init、--init-only、または --maintenance を明示的に指定したときのみ実行される。毎回の新しいセッションで発火させたくない処理に使う。
Setup と SessionStart の使い分け
| 操作 | Setup を使う | SessionStart を使う |
|---|---|---|
| 依存パッケージのインストール | Yes | No |
| データベースマイグレーションの実行 | Yes | No |
| Git ステータスの読み込み | No | Yes |
| 環境変数の設定 | Yes | Yes |
| プロジェクトコンテキストの注入 | No | Yes |
| 一時ファイルのクリーンアップ | Yes (maintenance) | No |
Setup の設定
{
"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"
}
]
}
]
}
}実行方法:
claude --init # Runs 'init' matcher
claude --init-only # Runs 'init' matcher, then exits
claude --maintenance # Runs 'maintenance' matcherSetup フックも環境変数の永続化のために CLAUDE_ENV_FILE に書き込める。
PreCompact: コンテキスト消失前に
PreCompact はコンパクションの直前に発火する。ユーザーが /compact でトリガーした場合も、ウィンドウが埋まって自動的に発火した場合も同様だ。
トランスクリプトのバックアップ
#!/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 のマッチャー
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [{ "type": "command", "command": "echo 'Auto-compacting...'" }]
},
{
"matcher": "manual",
"hooks": [{ "type": "command", "command": "echo 'Manual /compact'" }]
}
]
}
}auto- コンテキストウィンドウが埋まり、自動コンパクションmanual- ユーザーが/compactを実行
リカバリーマーカーの作成
コンパクションイベント後にコンテキストを再構築するために PreCompact と SessionStart をペアにする。有効なパターン: 共有の backup-core モジュール、しきい値ベースのトリガーを発火させる StatusLine モニター、そして PreCompact ハンドラーが 1 つの状態ファイルを通じて協調する。これによりセッション間で何も失われない。完全なウォークスルーは Context Recovery フックガイドを参照。
SessionEnd: クリーンアップ
SessionEnd はセッションが終了するときに実行される。シャットダウンをブロックできないが、クリーンアップは実行できる。
セッション統計のログ
#!/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 の終了理由
reason フィールドはセッションが終了した理由を伝える:
clear- ユーザーが/clearを実行したlogout- ユーザーがログアウトしたprompt_input_exit- プロンプト表示中にユーザーが終了したother- その他の終了理由
完全なライフサイクル例
端から端まで接続された完全なライフサイクル設定:
{
"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"
}
]
}
]
}
}入力ペイロード
SessionStart の入力
{
"session_id": "abc123",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-20250514",
"cwd": "/path/to/project"
}Setup の入力
{
"session_id": "abc123",
"hook_event_name": "Setup",
"trigger": "init",
"cwd": "/path/to/project"
}PreCompact の入力
{
"session_id": "abc123",
"hook_event_name": "PreCompact",
"transcript_path": "~/.claude/projects/.../transcript.jsonl",
"trigger": "auto",
"custom_instructions": ""
}SessionEnd の入力
{
"session_id": "abc123",
"hook_event_name": "SessionEnd",
"reason": "clear",
"cwd": "/path/to/project"
}ベストプラクティス
-
SessionStart は素早く - 毎回のセッションで実行される。重い処理は Setup に移す。
-
Setup は初回限りの処理に - 依存パッケージのインストール、マイグレーション、初回プロジェクトのブートストラップ。
-
コンパクション前にバックアップ - PreCompact はトランスクリプトを取得する最後のチャンスだ。
-
セッション終了をログに残す - SessionEnd はアナリティクスとデバッグに便利だ。
-
マッチャーを慎重に -
startupとresumeとcompactで異なる動作にすることで予期せぬ挙動を防ぐ。
次のステップ
- 全 12 フックのためにメインの Hooks ガイドをセットアップする
- コンパクションを乗り越えるために Context Recovery を設定する
- タスク完了の強制に Stop フックを使う
- 自動スキル読み込みのためにスキル起動を探索する
設定をやめて、構築を始めよう。
AIオーケストレーション付きSaaSビルダーテンプレート。