EN JA
無料ダウンロード

Claude Code hooks でセッション状態を追跡する

更新日: 2026年8月26日

Claude Code の hooks は「このツールをブロックする」「編集に lint をかける」といった ポリシー制御の文脈で語られがちですが、実はセッションが今何をしているか—— 処理中か、完了したか、質問でブロックしているか——を教えてくれる唯一の公開 API でもあります。 このガイドでは hook イベントから信頼できるセッション状態を導出する方法と、素朴な実装が 壊れるポイントを解説します。私たちのアプリ AgentManager も この土台の上に作られています。

hooks を 1 分で

hook は、 Claude Code がライフサイクルイベントのたびに実行するシェルコマンドです。stdin から JSON ペイロード(session_idhook_event_namecwd と イベント固有のフィールド)を受け取ります。登録先は ~/.claude/settings.json (全プロジェクト共通)またはプロジェクトの .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "$HOME/.claude/scripts/on-stop.sh" }
        ]
      }
    ]
  }
}

状態追跡に関係するイベントは次のとおりです:

イベント発火タイミング
SessionStartセッションの開始・再開
UserPromptSubmitプロンプトの送信
PreToolUse / PostToolUse各ツール呼び出しの前後
NotificationClaude が入力を必要とするとき(許可ダイアログ、放置リマインダー等)
Stopメインエージェントの応答完了
SubagentStopサブエージェント(Task)の完了
SessionEndセッションの終了

イベントから状態へ

知りたいことは 4 状態に集約できます: 確認待ち(あなたでブロック中)、 処理中(作業中)、 完了(ターン終了)、 待機(開いているが何も起きていない)。まずは素朴な対応表から:

イベント新しい状態理由
SessionStart待機まだ何も頼まれていない
UserPromptSubmit処理中入力できた=ダイアログは残っていない。ターン開始
PreToolUsePostToolUse処理中ツール活動=作業進行中
AskUserQuestion / ExitPlanModePreToolUse確認待ちこれらのツールは即座にあなたの回答(選択/プラン承認)でブロックする
Notificationpermission_prompt確認待ち許可ダイアログが未応答のまま表示されている
Notificationidle_prompt完了ターン終了後の放置リマインダーであって新しい質問ではない
Stop完了ターン完了
SessionEnd(削除)セッション消滅

動く最小実装

1 つのスクリプトをいくつかのイベントに登録し、session_id ごとに小さな JSON ファイルを 1 つ書きます。あとはステータスバーウィジェットでも tmux のセグメントでも ダッシュボードでも、そのディレクトリを描画すればいい。~/.claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      { "hooks": [ { "type": "command", "command": "$HOME/.claude/track-status.sh running" } ] }
    ],
    "Notification": [
      { "hooks": [ { "type": "command", "command": "$HOME/.claude/track-status.sh waiting" } ] }
    ],
    "Stop": [
      { "hooks": [ { "type": "command", "command": "$HOME/.claude/track-status.sh done" } ] }
    ],
    "SessionEnd": [
      { "hooks": [ { "type": "command", "command": "$HOME/.claude/track-status.sh ended" } ] }
    ]
  }
}

~/.claude/track-status.sh はこう書きます(jq が必要。chmod +x を忘れずに):

#!/bin/bash
# stdin の hook ペイロードを読み、セッションごとの状態ファイルを保つ。
STATE="$1"
DIR="$HOME/.claude/session-status"
mkdir -p "$DIR"

PAYLOAD=$(cat)
SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id')
CWD=$(echo "$PAYLOAD" | jq -r '.cwd')

if [ "$STATE" = "ended" ]; then
  rm -f "$DIR/$SESSION_ID.json"
else
  # アトミックに書く(一時ファイル + mv)。読み手に書きかけを見せない。
  jq -n --arg state "$STATE" --arg cwd "$CWD" \
    '{state: $state, cwd: $cwd, updated: now | todate}' \
    > "$DIR/$SESSION_ID.json.tmp" && mv "$DIR/$SESSION_ID.json.tmp" "$DIR/$SESSION_ID.json"
fi

これで cat ~/.claude/session-status/*.json が全セッションのライブな状態を返します。 設計上の注意は 2 つ、上のコードに織り込み済みです: ファイルのキーは作業ディレクトリではなく session_id(同じディレクトリで複数セッションは普通に起きる)、書き込みはアトミックに。 この 4 イベント版は対応表の細かい確認待ち(AskUserQuestionExitPlanModeidle_prompt)を省いているので、動いたら足してください。

素朴な実装が壊れるエッジケース

ここからが本題です。実際にデバッグ時間を溶かすのはこの 4 つで、先に知っておくことにこのガイドの価値の大半があります。

1. 許可ダイアログへの「応答」はイベントを発火しない

hook が教えてくれるのは許可ダイアログが出たこと(Notification)までで、 あなたが答えたことは分かりません。次のイベントが来るのは、承認したツールが完了したとき (PostToolUse)かターンが終わったとき(Stop)。長いビルドを承認すると、 ビルド中ずっと「確認待ち」表示が残ります。これは設計で受け止めるしかありません: 確認待ちは「次のライフサイクルイベントで解除される」ものとして扱い、解除の遅れは許容する。 誤りは少なくとも安全側です(「確認待ちの残留」であって「確認待ちの見逃し」ではない)。

2. Stop は「全部止まった」を意味しない

バックグラウンドのサブエージェントは、メインエージェントの Stop 後も動き続けます。 サブエージェントの作業中に Stop で「完了」に倒すと、状態が嘘をつきます。 サブエージェントのライフサイクルを追跡し、「メインが停止済みかつサブエージェントが 残っていない」ときだけ完了に倒してください。さらにその記録は SessionStart で リセットすること——resume でも発火するので、リセットしないと消えたサブエージェントの ID の せいで永遠に「処理中」に固定されます。

3. 順序の罠: Stop に「確認待ち」を踏み潰させない

バックグラウンドのサブエージェントが許可待ちでブロック → Notification が 確認待ちを告げる → メインエージェントが終了 → Stop が完了を告げる。 純粋な「最後のイベント勝ち」では、本当にブロックしているセッションが隠れてしまいます。 対策は、確認待ちの理由(メイン由来かサブエージェント由来か)を追跡し、 対応する解除信号だけがフラグを降ろせるようにすること。放置中に定期的に再発火する idle_prompt にも同じ配慮が必要です。

4. 異常終了はファイルを残す

SessionEnd は保証されません。クラッシュや強制 kill では発火せず、 サブエージェントを ESC で中断すると SubagentStop も発火しません。 長期運用するならガベージコレクションが要ります(例: 所有プロセスが消えた状態ファイルは破棄)。

なぜここまで気を使うのか? 失敗の向きが対称ではないからです。 「確認待ちの残留」は一瞥を無駄にするだけですが、「確認待ちの見逃し」は停止時間を まるごと無駄にします——それはこのトラッカーを作った理由そのものです。迷ったら 確認待ち表示に倒してください。

自作するか、インストールするか

ここまでの内容は、1 つの用途・1 つの表示形式に絞れば半日で作れます。エッジケースの 対応は実際に踏むたびに固めていけばいい。それが楽しいなら良い半日です (私たちは明らかに楽しみました)。もう一つの選択肢:

AgentManager はこのパイプラインを macOS 向けに製品化したものです。 hook がセッションごとの状態ファイルを書き、常時最前面の小さなウィンドウが全セッションを 状態ランプ付きで描画します — 確認待ち (返答の形つき: 許可/プラン/選択)、 処理中、 完了、 待機。 上のエッジケースはすべて対応済みで、許可を承認した瞬間に「確認待ち」は即時に消え、残留しません。 さらに hook スクリプトには収まらない部分——確認待ち発生時のウィンドウ自動表示、 クリックで該当ターミナルペインへのジャンプ——も含みます。hooks の登録はワンクリックで、 きれいに解除でき、自作の hooks とも共存します。

セッション状態、作らずに使う

AgentManager は Claude Code の hook イベントを全セッションのライブな ステータスボードに変えます。無料で使えます — アカウント登録不要。

macOS 13+ — フリープランは同時表示 2 セッションまで