Claude Code の Stop フックで作業完了をデスクトップ通知する設定

Claude Code 公開:

Claude が応答を終えた瞬間に通知を出す Stop フックの書き方を、macOS・Linux・Windows の 3 通りのコマンドで解説します。入力待ちを知らせる Notification フックとの使い分けと、終了コードの落とし穴も扱います。

検証日 2026年9月20日 仕様変更が早い分野です。最新の公式ドキュメントも併せてご確認ください。
目次
  1. Stop と Notification のどちらを使うか
  2. 設定例(OS 別)
    1. macOS
    2. Linux
    3. Windows(PowerShell)
  3. 終了コードの落とし穴
  4. 通知に中身を持たせる
  5. まとめ

長い作業を投げたあと、ターミナルを見張っているのは時間の無駄です。終わった瞬間に通知が来れば、その間は別の作業ができます。

Claude が応答を終えたタイミングで発火するのは Stop フックです。公式ドキュメントの入門で扱われている Notification フックは「入力待ち」を知らせるもので、目的が違います。この記事では両者を区別したうえで、3 つの OS それぞれの設定を示します。

KEY POINT

この記事で分かること

  • StopNotification の違いと、どちらを使うべきか
  • macOS・Linux・Windows それぞれの通知コマンドと設定例
  • 終了コードの落とし穴(通知スクリプトで Claude が止まらなくなる)

Stop と Notification のどちらを使うか

名前が似ていますが、発火するタイミングが違います。

イベント発火するときmatcher
StopClaude が応答を終えたとき非対応
SubagentStopサブエージェントが終了したときエージェントの種類で指定できる
NotificationClaude Code が通知を出すとき(権限確認、アイドルなど)通知の種類で指定できる

「作業が終わったら知らせてほしい」なら Stop です。「権限の確認で止まっているのに気づきたい」なら Notification を使います。両方設定しても構いません。

Notification の matcher に指定できる種類には permission_promptidle_promptauth_successagent_needs_inputagent_completed などがあります。指定できる値の一覧は hooks の matcher に書ける値一覧 にまとめています。

Stop に matcher を書いても無視される

Stop は matcher に対応していません。書いてもエラーにはならず、黙って無視されて毎回発火します。絞り込みたい場合は、フックのスクリプト側で判定してください。

設定例(OS 別)

~/.claude/settings.json に書きます。ファイルがなければ作成してください。

macOS

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"作業が完了しました\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

通知が出ない場合は、osascript が経由する Script Editor に通知の権限がない可能性があります。公式ドキュメントは「Script Editor に通知権限がないとコマンドは黙って失敗し、macOS は権限を求めるプロンプトを出さない」と説明しています。ターミナルで次を 1 回実行して Script Editor を通知設定の一覧に出現させ、システム設定 > 通知Script Editor の通知を許可してください。

osascript -e 'display notification "test"'

Linux

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' '作業が完了しました'"
          }
        ]
      }
    ]
  }
}

notify-send はデスクトップ通知デーモンを必要とします。ヘッドレスサーバー、SSH セッション、多くのコンテナにはこれがありません。まず次のコマンドを直接実行して動作を確かめてください。コマンドが見つからない場合は、Debian と Ubuntu なら libnotify-bin パッケージを入れます。

notify-send 'Claude Code' 'test'

Windows(PowerShell)

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('作業が完了しました', 'Claude Code')\""
          }
        ]
      }
    ]
  }
}

設定したら /hooks を実行すると、イベントごとのフックの一覧と、その定義元のファイルを確認できます。ただし /hooks のメニューは読み取り専用です。追加や変更は設定ファイルを直接編集するか、Claude に依頼してください。

終了コードの落とし穴

ここが Stop 固有の注意点です。Stop フックが終了コード 2 を返すと、Claude の停止がブロックされて会話が続きます。通知コマンドが失敗して 2 を返すと、意図せず Claude が動き続けることになります。

通知だけが目的なら、必ず 0 で終了させてください。

{
  "type": "command",
  "command": "notify-send 'Claude Code' '作業が完了しました' || true"
}

|| true を付けると、通知コマンドが失敗しても終了コードは 0 になります。

さらに、ブロックし続けた場合の上限もあります。公式ドキュメントによると、Claude Code は Stop フックが進展のないまま 8 回連続でブロックすると上書きします。意図的にブロックするフックを書く場合は、stdin の JSON の stop_hook_active を見て、すでに継続を引き起こしているなら早期に終了させます。

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Claude の停止を許可する
fi
# ここに本来の処理

終了コードの意味は hooks の終了コードの意味と使い分け にまとめています。

通知に中身を持たせる

Stop フックの stdin には、last_assistant_message(最後のアシスタントメッセージ)と stop_reason(停止の理由)が入ります。これを使うと、何が終わったのかを通知に出せます。

#!/usr/bin/env bash
# ~/.claude/hooks/notify-done.sh
INPUT=$(cat)
MSG=$(echo "$INPUT" | jq -r '.last_assistant_message // empty' | head -c 120)
notify-send 'Claude Code' "${MSG:-作業が完了しました}" || true
exit 0
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/notify-done.sh"
          }
        ]
      }
    ]
  }
}

stdin の JSON の構造は hooks が受け取る stdin JSON の構造 に、フック全般の組み方は hooks で lint と format を自動実行する にまとめています。

用語解説

stop_reason: Claude が停止した理由を表す文字列です。通常のターン終了では end_turn が入ります。

まとめ

  • 作業完了の通知は Stop、入力待ちの通知は Notification を使う
  • Stop は matcher に対応していない。書いても黙って無視される
  • macOS は osascript、Linux は notify-send、Windows は PowerShell の MessageBox
  • Stop フックが終了コード 2 を返すと停止がブロックされる。通知だけなら || true で 0 にする
  • 意図的にブロックするなら stop_hook_active を見て 8 回連続の上限を避ける
  • last_assistant_message を使うと、何が終わったのかを通知に含められる

よくある質問

Stop フックと Notification フックはどう違いますか?
Stop は Claude が応答を終えたときに発火します。Notification は権限確認や入力待ちなど、Claude Code が通知を出すときに発火します。「終わったら知らせてほしい」なら Stop です。
Stop フックに matcher は書けますか?
書けません。Stop は matcher に対応しておらず、書いても黙って無視されます。毎回発火します。
通知スクリプトが原因で Claude が止まらなくなることはありますか?
あります。Stop フックが終了コード 2 を返すと停止がブロックされ、会話が続きます。通知だけが目的なら必ず 0 で終了させてください。
サブエージェントの完了も通知できますか?
できます。SubagentStop フックを使います。こちらは matcher でエージェントの種類を指定できます。

参考にした一次情報

この記事は公式ドキュメントを基に AI が下書きを作成し、運営者が内容を確認して公開しています。誤りを見つけた場合は お問い合わせからお知らせください。