13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeのhooks機能で、作業完了時にSlack通知を飛ばしてもらう

Last updated at Posted at 2025-07-01

Step.1: 作業終了時・ファイル編集時にSlack通知

お好きな .claude/settings.json.claude/settings.local.json に以下を追加します。

.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/slack-notify.sh \"Claude Code の作業が完了しました\""
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/slack-notify.sh \"ファイルの編集が完了しました\""
          }
        ]
      }
    ]
  }
}

続いて、上記で呼び出すslack-notify.shを作成します。

slack-notify.sh
#!/bin/bash

# Slack通知スクリプト
# Claude Codeの作業完了時にSlackへ通知を送信します

# 設定ファイルの読み込み
SCRIPT_DIR=$(dirname "$0")
CONFIG_FILE="${SCRIPT_DIR}/.env"

# 設定ファイルが存在する場合は読み込む
if [ -f "$CONFIG_FILE" ]; then
    source "$CONFIG_FILE"
fi

# Slack Webhook URLの確認
if [ -z "$SLACK_WEBHOOK_URL" ]; then
    echo "エラー: SLACK_WEBHOOK_URLが設定されていません" >&2
    echo "scripts/.env ファイルに SLACK_WEBHOOK_URL を設定してください" >&2
    exit 1
fi

# デフォルトのメッセージ
DEFAULT_MESSAGE="Claude Code の作業が完了しました"
MESSAGE="${1:-$DEFAULT_MESSAGE}"

# タイムスタンプ
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# JSONデータからセッション情報を取得(もし提供されていれば)
if [ ! -t 0 ]; then
    JSON_INPUT=$(cat)
    SESSION_ID=$(echo "$JSON_INPUT" | jq -r '.session_id // empty' 2>/dev/null)
    
    # セッションIDがある場合はメッセージに追加
    if [ -n "$SESSION_ID" ]; then
        MESSAGE="${MESSAGE}\nセッションID: ${SESSION_ID}"
    fi
fi

# Slack通知の送信
RESPONSE=$(curl -s -X POST "$SLACK_WEBHOOK_URL" \
    -H 'Content-Type: application/json' \
    -d @- <<EOF
{
    "text": "${MESSAGE}",
    "username": "Claude Code",
    "icon_emoji": ":robot_face:",
    "blocks": [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": ":white_check_mark: *Claude Code 通知*"
            }
        },
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "${MESSAGE}"
            }
        },
        {
            "type": "context",
            "elements": [
                {
                    "type": "mrkdwn",
                    "text": "実行時刻: ${TIMESTAMP}"
                }
            ]
        }
    ]
}
EOF
)

# レスポンスの確認
if [ "$RESPONSE" = "ok" ]; then
    echo "Slack通知を送信しました"
else
    echo "Slack通知の送信に失敗しました: $RESPONSE" >&2
    exit 1
fi

最後に.envを用意して、その中にSlack Webhook URLを格納しておきます。

.env
# Slack App の Incoming Webhooks から取得した URL を設定してください
# 例: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
SLACK_WEBHOOK_URL=<取得したURLをコピペ>

Slack Webhook URLは以下サイトを参考に取得してください。

Webhook URLは取り扱い注意です。Gitなどにプッシュしないよう注意してください。

ディレクトリはお好みですが、私は以下の感じにしてみました。
今回は実験的にローカル専用の設定にしています。

project/
├── .claude
│   └──settings.local.json  # hooksの設定を記述
└── script
    ├──slack-notify.sh      # Slackに通知を行うスクリプト
    └──.env                 # Slack Webhook URLを格納する

設定したらClaude Codeを再起動し、試してみます。
image.png

image.png

良いですね。報告してくれるようになりました。

Step.2: もっと詳細にログを出してもらう

とはいえ、上記だけだと何をしたのかわかりません。

もっといい感じに報告してもらうべく、追加で修正しましょう。

.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/slack-notify-detailed.sh Stop"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": ".*",
        "hooks": [
          {
            "type": "command",
            "command": "path/to/slack-notify-detailed.sh PostToolUse"
          }
        ]
      }
    ]
  }
}
slack-notify-detailed.sh
#!/bin/bash

# Slack詳細通知スクリプト
# Claude Codeの操作内容を詳細にSlackへ通知します

# 設定ファイルの読み込み
SCRIPT_DIR=$(dirname "$0")
CONFIG_FILE="${SCRIPT_DIR}/.env"

# 設定ファイルが存在する場合は読み込む
if [ -f "$CONFIG_FILE" ]; then
    source "$CONFIG_FILE"
fi

# Slack Webhook URLの確認
if [ -z "$SLACK_WEBHOOK_URL" ]; then
    echo "エラー: SLACK_WEBHOOK_URLが設定されていません" >&2
    echo "scripts/.env ファイルに SLACK_WEBHOOK_URL を設定してください" >&2
    exit 1
fi

# タイムスタンプ
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# JSONデータを読み込む
JSON_INPUT=$(cat)

# 基本情報の抽出
SESSION_ID=$(echo "$JSON_INPUT" | jq -r '.session_id // "N/A"')
TOOL_NAME=$(echo "$JSON_INPUT" | jq -r '.tool_name // empty')
EVENT_TYPE="${1:-PostToolUse}"  # デフォルトはPostToolUse

# ツールに応じたメッセージとアイコンの生成
case "$TOOL_NAME" in
    "Write")
        ICON=":pencil2:"
        TITLE="ファイルを作成しました"
        FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // .tool_input.path // "不明"')
        DETAILS="ファイル: \`${FILE_PATH}\`"
        ;;
    "Edit"|"MultiEdit")
        ICON=":memo:"
        TITLE="ファイルを編集しました"
        FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // .tool_input.path // "不明"')
        if [ "$TOOL_NAME" = "MultiEdit" ]; then
            EDIT_COUNT=$(echo "$JSON_INPUT" | jq -r '.tool_input.edits | length // 0')
            DETAILS="ファイル: \`${FILE_PATH}\`\n編集箇所: ${EDIT_COUNT}件"
        else
            DETAILS="ファイル: \`${FILE_PATH}\`"
        fi
        ;;
    "Bash")
        ICON=":zap:"
        TITLE="コマンドを実行しました"
        COMMAND=$(echo "$JSON_INPUT" | jq -r '.tool_input.command // "不明"')
        SUCCESS=$(echo "$JSON_INPUT" | jq -r '.tool_response.success // true')
        STATUS=$([ "$SUCCESS" = "true" ] && echo "成功" || echo "失敗")
        # コマンドが長い場合は省略
        if [ ${#COMMAND} -gt 100 ]; then
            COMMAND_DISPLAY="${COMMAND:0:97}..."
        else
            COMMAND_DISPLAY="$COMMAND"
        fi
        DETAILS="コマンド: \`${COMMAND_DISPLAY}\`\n結果: ${STATUS}"
        ;;
    "Read"|"NotebookRead")
        ICON=":book:"
        TITLE="ファイルを読み取りました"
        FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // .tool_input.notebook_path // .tool_input.path // "不明"')
        DETAILS="ファイル: \`${FILE_PATH}\`"
        ;;
    "TodoWrite")
        ICON=":white_check_mark:"
        TITLE="TODOリストを更新しました"
        TODO_COUNT=$(echo "$JSON_INPUT" | jq -r '.tool_input.todos | length // 0')
        DETAILS="タスク数: ${TODO_COUNT}件"
        ;;
    "Grep"|"Glob")
        ICON=":mag:"
        TITLE="ファイル検索を実行しました"
        PATTERN=$(echo "$JSON_INPUT" | jq -r '.tool_input.pattern // "不明"')
        DETAILS="パターン: \`${PATTERN}\`"
        ;;
    "LS")
        ICON=":file_folder:"
        TITLE="ディレクトリを一覧表示しました"
        PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.path // "不明"')
        DETAILS="パス: \`${PATH}\`"
        ;;
    "WebFetch"|"WebSearch")
        ICON=":globe_with_meridians:"
        TITLE="Web情報を取得しました"
        if [ "$TOOL_NAME" = "WebFetch" ]; then
            URL=$(echo "$JSON_INPUT" | jq -r '.tool_input.url // "不明"')
            DETAILS="URL: ${URL}"
        else
            QUERY=$(echo "$JSON_INPUT" | jq -r '.tool_input.query // "不明"')
            DETAILS="検索: ${QUERY}"
        fi
        ;;
    "")
        # Stopイベントの場合
        if [ "$EVENT_TYPE" = "Stop" ]; then
            ICON=":checkered_flag:"
            TITLE="セッションが完了しました"
            DETAILS="セッションID: \`${SESSION_ID}\`"
        else
            ICON=":question:"
            TITLE="操作を実行しました"
            DETAILS="詳細情報なし"
        fi
        ;;
    *)
        ICON=":gear:"
        TITLE="${TOOL_NAME}を実行しました"
        DETAILS="ツール: ${TOOL_NAME}"
        ;;
esac

# Slack通知の送信
RESPONSE=$(curl -s -X POST "$SLACK_WEBHOOK_URL" \
    -H 'Content-Type: application/json' \
    -d @- <<EOF
{
    "username": "Claude Code",
    "icon_emoji": ":robot_face:",
    "blocks": [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "${ICON} *${TITLE}*"
            }
        },
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "${DETAILS}"
            }
        },
        {
            "type": "context",
            "elements": [
                {
                    "type": "mrkdwn",
                    "text": "時刻: ${TIMESTAMP} | セッション: \`${SESSION_ID:0:8}...\`"
                }
            ]
        }
    ]
}
EOF
)

# レスポンスの確認
if [ "$RESPONSE" = "ok" ]; then
    echo "Slack通知を送信しました: ${TITLE}"
else
    echo "Slack通知の送信に失敗しました: $RESPONSE" >&2
    exit 1
fi

ディレクトリはこんな感じです。

project/
├── .claude
│   └──settings.local.json       # hooksの設定を記述
└── script
    ├──slack-notify-detailed.sh  # Slackに通知を行うスクリプト
    └──.env                      # Slack Webhook URLを格納する

Claude Codeを再起動し、実行してみました。
image.png

さっきよりかは何してくれてるか、わかりやすくなりましたね🙌

ただ実施した全コマンドに対して報告が来るのも流石にうるさいので、最後にまとめていい感じに投げてくれるように改良していきたいですね。

おまけ

作業完了時に通知音が出るようにしたい方は、以下のツイートを参考にすると簡単にできます。

.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "afplay /System/Library/Sounds/Funk.aiff"
          }
        ]
      }
    ]
  }
}
13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?