Claude Code v2.1.198 から、サブエージェントは既定でバックグラウンドに回ります。メインの会話を止めずに別の実装が進む反面、完了や入力待ちに気づけないと放置になりますよね。Notification hook を設定すれば、agent_completed と agent_needs_input を OS の通知へそのまま流せます。
バックグラウンド実行が既定になった
変わったのは実行方式です。v2.1.198(2026年7月1日)で、サブエージェントはフォアグラウンド固定からバックグラウンド既定に切り替わりました。現行の最新は v2.1.199。
| 項目 | v2.1.197まで | v2.1.198以降 |
|---|---|---|
| サブエージェントの既定 | フォアグラウンド(会話をブロック) | バックグラウンド(会話を継続) |
| 完了の扱い | 戻り値がその場で返る | 完了時に通知イベントが発火 |
| コード作業の後始末 | 手動で commit / PR 作成 | 自動で commit・push・ドラフトPR |
Ctrl+B で手動でバックグラウンドへ送っていた頃の運用は Claude CodeのバックグラウンドBash にまとめています。今回はその既定化に伴って要る、完了の受け取り側の話です。
会話を続けている間に何が起きるか
バックグラウンド化で困るのは、終わったタイミングが視界から外れることです。3体を並列に流して別々の実装を進めると、どれがいつ終わったのか、どれが入力待ちで止まっているのかがコンソール上で埋もれます。実際、サブエージェントを3体流して別実装を書いていたとき、1体が確認待ちのまま40分ほど止まっていました。気づいたのは他の作業がひと段落したあと。
止まった瞬間に気づくには、Notification hook を挟みます。
Notification hook で完了と入力待ちを拾う
Claude Code のフックは、特定のイベントで外部コマンドを叩く仕組みです。公式ドキュメントの “Hook events” に一覧があり、その中の Notification が通知系を担当します。フックの型そのものの違いは Claude Code Hooksのprompt型/agent型とは で整理しました。
agent_completed と agent_needs_input の違い
バックグラウンド実行で使うのは、次の通知タイプです。
- agent_completed: サブエージェントが作業を終えたとき
- agent_needs_input: 権限確認などでエージェントが人の入力を待っているとき
完了だけ拾うなら前者、止まりに気づきたいなら後者。放置を防ぎたいので、両方まとめて拾います。
settings.json に書く
~/.claude/settings.json かプロジェクトの .claude/settings.json に、通知タイプを matcher として登録します。
{
"hooks": {
"Notification": [
{
"matcher": "agent_completed|agent_needs_input",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/notify.sh"
}
]
}
]
}
}
matcher はパイプで複数の通知タイプを OR 指定できます。command は絶対パスか $CLAUDE_PROJECT_DIR 起点で書く。相対パスだと発火時の作業ディレクトリ次第でずれます。
フックが受け取る JSON
フックのコマンドには、標準入力から JSON が渡されます。Notification の場合はこの形。
{
"session_id": "abc123",
"hook_event_name": "Notification",
"cwd": "/path/to/project",
"transcript_path": "/path/to/transcript.jsonl",
"message": "通知の本文"
}
使うのは主に message。ここに通知の文面が入るので、そのまま OS の通知本文へ渡します。どのセッションかを区別したいときは session_id や cwd も添えられます。
matcher に指定できる通知タイプ
公式の “Matcher patterns” によると、Notification の matcher には次の通知タイプを指定できます。エージェント系の2つ以外にも、権限プロンプトやアイドル、認証成功などが並びます。
permission_prompt
idle_prompt
auth_success
elicitation_dialog
elicitation_complete
elicitation_response
agent_needs_input
agent_completed
権限確認だけ音を鳴らしたいなら permission_prompt 単体、放置検知なら agent_needs_input を足す、という具合に絞れます。
OS ネイティブに通知を出す
#!/usr/bin/env bash
# .claude/notify.sh
# 標準入力の JSON から message を取り出して macOS の通知へ流す
input=$(cat)
msg=$(printf '%s' "$input" | jq -r '.message')
osascript -e "display notification \"$msg\" with title \"Claude Code\" sound name \"Glass\""
このスクリプトを叩くと、サブエージェント完了時に通知センターへ届きます。
Claude Code
作業が完了しました
(Glass の通知音が鳴る)
JSON のパースに jq を使っています。入っていない環境では brew install jq で先に入れておく。
macOS で通知と音を出す
osascript の sound name にサウンド名を渡すと通知音が鳴ります。Glass や Ping など、/System/Library/Sounds にある名前が使える。音だけ別に鳴らしたいなら afplay /System/Library/Sounds/Glass.aiff でも同じです。
WSL から Windows トーストを出す
WSL で作業しているなら、powershell.exe を呼んで Windows 側のトーストを出します。BurntToast モジュールを入れておくと1行で書ける。
input=$(cat)
msg=$(printf '%s' "$input" | jq -r '.message')
powershell.exe -NoProfile -Command \
"New-BurntToastNotification -Text 'Claude Code', '$msg'"
PowerShell 側で Install-Module BurntToast を一度実行しておきます。VS Code のターミナルから WSL を使っている場合も、powershell.exe に PATH が通っていればそのまま鳴ります。
Notification・SubagentStop・Stop の使い分け
完了に絡むフックは、発火の粒度が違います。やりたいことで選びます。
| フック | 発火タイミング | matcher | 主な用途 |
|---|---|---|---|
Notification | Claude Code が通知を出すとき | 通知タイプ(agent_completed 等) | 完了・入力待ち・権限要求を外部へ |
SubagentStop | サブエージェントが1体終わるたび | agent_type | 個別エージェントの後処理 |
Stop | メインの応答が終わるたび | なし(常時発火) | セッション単位の後始末 |
通知を人へ飛ばすなら Notification。終わったエージェントの種類ごとに後処理を分けたいなら SubagentStop の agent_type でマッチ。応答1往復ごとに必ず走らせたいなら matcher を持たない Stop。SessionStart hook が開始側なら、Stop は終了側という対応です。
ドラフトPRハンドオフの受け取り方
v2.1.198 では通知だけでなく、成果物の渡し方も変わりました。バックグラウンドのサブエージェントがコード作業を終えると、自動で commit・push し、worktree 上にドラフトPRを開きます。手動の PR 作成が要らなくなった一方、承認前のブランチに push が走る点は把握しておく必要があります。
自動で push される範囲
ハンドオフされるのは、エージェントが作った worktree 上のブランチです。ここまでを自動でやってくれます。
- 変更のコミット
- 作業ブランチへの push
- ドラフト状態での PR 作成
ドラフトなので、そのままマージには進みません。人がレビューして Ready にするまでは、下ごしらえの状態です。
ブランチ保護で暴走を止める
自動 push を野放しにすると、エージェントの中間コミットが保護されていないブランチへ直接届きます。GitHub 側の branch protection を先に固めておく。
# NG: main を保護していない
# → エージェントの自動 push が main に載りうる
# OK: main への直 push を禁止し、レビュー必須にする
# → 変更はドラフトPR経由でしか入らない
agent_needs_input を通知に流すようにしてからは、確認待ちで止まった瞬間に気づけます。自動ハンドオフを安全に回すなら、止まったエージェントを放置しない。通知はそのための最低ラインです。
通知が飛ばないとき
設定したのに鳴らない、という詰まり方をしやすいポイントがいくつかあります。
- matcher の一致: v2.1.195 で、ハイフンを含む matcher が部分一致から完全一致へ変わりました。MCP 由来の名前(
mcp__brave-searchなど)を書いている場合はmcp__brave-search__.*のようにパターンで書きます。 - jq が無い:
messageを取り出せず空通知になります。which jqで確認します。 - command のパス: 相対パスは発火時の
cwd次第でずれます。絶対パスか$CLAUDE_PROJECT_DIR起点にします。 - WSL で powershell.exe が見つからない: Windows の PATH が WSL に引き継がれているかを確認します。
フックの構文自体を疑うなら、まず Stop に echo fired >> /tmp/hook.log だけ仕込んで、発火が起きているか切り分けます。ログに追記されなければ、通知コマンドではなくフック登録側の問題です。
まとめ
- v2.1.198 でサブエージェントは既定バックグラウンド。完了と入力待ちは
Notificationhook で拾う - matcher に
agent_completed/agent_needs_inputを指定し、標準入力の JSON からmessageを取り出す - macOS は
osascript、WSL はpowershell.exe経由でトーストや通知音へ - 個別の後処理は
SubagentStop、セッション終わりはStopと役割が分かれる - ドラフトPR自動ハンドオフは branch protection とセット。保護なしの自動 push は避ける

