お好きな .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=
Slack Webhook URLは以下サイトを参考に取得してください。
Webhook URLは取り扱い注意です。Gitなどにプッシュしないよう注意してください。
ディレクトリはお好みですが、私は以下の感じにしてみました。
今回は実験的にローカル専用の設定にしています。
project/
├── .claude
│ └──settings.local.json # hooksの設定を記述
└── script
├──slack-notify.sh # Slackに通知を行うスクリプト
└──.env # Slack Webhook URLを格納する
設定したらClaude Codeを再起動し、試してみます。
↓
良いですね。報告してくれるようになりました。
とはいえ、上記だけだと何をしたのかわかりません。
もっといい感じに報告してもらうべく、追加で修正しましょう。
.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/settings.json
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "afplay /System/Library/Sounds/Funk.aiff"
}
]
}
]
}
}
Views: 0