SessionStart hook を設定したのに、何も起きない。エラーも出ない。そういうときに疑う場所は2つあります。
最小の設定
まず、確実に動く形はこれです。
{
"hooks": {
"SessionStart": [
{
"hooks": [
{ "type": "command", "command": "bash /absolute/path/to/script.sh" }
]
}
]
}
}
置き場所は次のいずれかです。
| ファイル | 適用範囲 |
|---|---|
~/.claude/settings.json |
すべてのプロジェクト |
<プロジェクト>/.claude/settings.json |
そのフォルダだけ |
<プロジェクト>/.claude/settings.local.json |
そのフォルダだけ・共有しない |
原因1:matcher が実際の source と一致していない
matcher は省略できます。 省略するか、"" または "*" にすると、すべてのセッション開始で発火します。
値を書いた場合は、その値と一致したときだけ発火します。
| matcher | 発火する場面 |
|---|---|
startup |
新規セッション |
resume |
--resume、--continue、/resume |
clear |
/clear |
compact |
自動または手動の圧縮 |
fork |
--fork-session、/fork、/branch |
問題は、アプリの画面で「新しいスレッド」を立てたときに、それが startup として扱われるとは限らないことです。
実測では、デスクトップアプリでスレッドを開いたときに resume として発火した例がありました。"matcher": "startup" と書いていると、この場合は素通りします。
自分の環境の source を調べる
hook は起動時にJSONを標準入力で受け取ります。 その中に source が入っているので、それを記録すれば実際の値が分かります。
#!/bin/bash
cat > "$HOME/hook-source.json"
これを matcher なしで登録し、セッションを開き直してから ~/hook-source.json を見ます。
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"hook_event_name": "SessionStart",
"source": "startup"
}
この source の値が、書くべき matcher です。
判断に迷うなら、matcher を書かないのが確実です。 すべての開始で発火します。
原因2:相対パスで書いている
hook は「そのときの作業ディレクトリ」で実行されます。この作業ディレクトリは一定ではありません。
実測では、同じ設定から発火した hook の作業ディレクトリが、C:\Users\<ユーザー> と C:\Users\<ユーザー>\<プロジェクト> に分かれました。
そのため、こう書くと落ちます。
# 悪い例:どこに書かれるか分からない
echo "$(date)" >> .claude/hook.log
読み込むファイルも、書き込むファイルも、絶対パスで指定します。
# 良い例
echo "$(date)" >> "$HOME/hook.log"
相対パスのまま「ファイルが無い」を見ると、発火していないと誤読します。 実際は発火していて、別の場所に書かれているだけ、ということが起こります。
発火したかどうかの確かめ方
会話で聞いても証拠になりません。
やりがちなのが、hook に echo 合言葉 と書いて、新しいセッションで「合言葉、見えてる?」と聞く方法です。この聞き方では、質問文に合言葉が入っているため、それを読んだだけで「見えています」と答えられます。
確実なのは、会話の外にあるものに書かせることです。
#!/bin/bash
echo "$(date) | PWD=$(pwd)" >> "$HOME/hook-fired.log"
セッションを開き直したあとにファイルを見て、行が増えていれば発火しています。誰かの返答を信じる必要がありません。
終了コードの意味
| 終了コード | 動き |
|---|---|
| 0 | 成功。標準出力の内容がそのままコンテキストに入ります |
| 2 | ブロッキングエラー。標準エラー出力がエラー通知として表示されます |
| その他 | 非ブロッキングエラー。標準エラー出力の1行目が表示されます |
SessionStart は、標準出力がコンテキストに入る数少ないイベントのひとつです。日付や現在の状況を流し込みたい場合は、単に echo するだけで届きます。
タイムアウトの既定は600秒です。timeout フィールドで変更できます。
既知の不具合
公式リポジトリに報告が上がっているものです。当てはまる場合、設定が正しくても動きません。
/clearでSessionStartが発火しない(Issue #34072)- VS Code拡張で
/clearしたとき、sourceがclearではなくstartupになる(Issue #26794)。"matcher": "clear"は一致しません - Windowsでプラグイン由来の hook が実行されない(Issue #21468)
- プラグインの読み込みより先に hook が走り、起動時にエラーが出る(Issue #19491)
確認の手順
- matcher を消す。すべての開始で発火する形にする
- 書き込み先を絶対パスにする
- ファイルに書かせて確かめる。会話で聞かない
- 発火を確認できたら、
sourceを記録して、必要なら matcher を戻す

