設定 Stop Hook,讓 Claude 完成任務後透過桌面通知、Slack、Telegram 自動推送訊息
Claude Code 執行任務時常需要幾分鐘甚至更久。你讓它重構一個模組、跑完整個測試套件、處理一批檔案——然後切到別的視窗做其他事。問題是:你不知道它什麼時候做完了。
等你回來看,可能已經完成十分鐘了,也可能卡在一個確認對話框等你回應。
用 Stop Hook 可以在 Claude 完成任務(或需要你介入)時自動發通知。不管你在哪個視窗、哪台裝置,通知直接推過來。
Claude Code 有一個 Stop 事件——每次 Claude 停下來時觸發,無論是任務完成、遇到錯誤需要確認,還是等待使用者輸入。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/notify.sh"
}
]
}
]
}
}
Hook 透過 stdin 接收一個 JSON,裡面包含 stop_reason(停止原因)和 stop_message(Claude 最後說的話)。根據這些資訊,你可以自訂通知內容。
stdin 資料結構:
{
"session_id": "xxx",
"stop_reason": "end_turn",
"stop_message": "重構完成,所有測試通過。"
}
Linux 用 notify-send,macOS 用 osascript,不需要任何外部服務:
#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 200)
case "$(uname)" in
Linux)
notify-send "Claude Code" "$message" --urgency=normal
;;
Darwin)
osascript -e "display notification \"$message\" with title \"Claude Code\""
;;
esac
優點:零設定,馬上就能用。
缺點:只有當前這台電腦能收到,切到其他裝置就看不到了。
用 Slack Incoming Webhook 把通知推到指定頻道或私訊。
事前準備:
1. 在 Slack 的 App 管理頁面 建立一個 App
2. 啟用 Incoming Webhooks,取得 Webhook URL
3. 把 URL 存到環境變數 SLACK_WEBHOOK_URL
#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)
webhook_url="${SLACK_WEBHOOK_URL}"
[[ -z "$webhook_url" ]] && exit 0
# 根據停止原因選 emoji
case "$reason" in
end_turn) emoji="✅" ;;
user_input) emoji="⏳" ;;
*) emoji="🔔" ;;
esac
payload=$(jq -n \
--arg text "$emoji *Claude Code* | \`$reason\`
$message" \
'{text: $text}')
curl -s -X POST "$webhook_url" \
-H 'Content-Type: application/json' \
-d "$payload" > /dev/null 2>&1 &
末尾的 & 很重要——讓 curl 在背景執行,不會阻塞 Claude。
Telegram Bot 適合個人使用,免費、即時,手機上就能收到。
事前準備:
1. 找 @BotFather 建立 Bot,取得 Token
2. 取得你的 Chat ID(先跟 Bot 傳一則訊息,再呼叫 getUpdates API)
3. 設定環境變數 TELEGRAM_BOT_TOKEN 和 TELEGRAM_CHAT_ID
#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)
token="${TELEGRAM_BOT_TOKEN}"
chat_id="${TELEGRAM_CHAT_ID}"
[[ -z "$token" || -z "$chat_id" ]] && exit 0
text="🤖 *Claude Code*
状態:\`$reason\`
$message"
curl -s "https://api.telegram.org/bot${token}/sendMessage" \
-d chat_id="$chat_id" \
-d text="$text" \
-d parse_mode="Markdown" > /dev/null 2>&1 &
如果你在同一台機器上但切了視窗,一聲提示音比彈窗更有效:
#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
case "$(uname)" in
Linux)
paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null &
;;
Darwin)
afplay /System/Library/Sounds/Glass.aiff &
;;
esac
可以跟其他方案搭配使用——本機播放聲音 + 遠端推送通知。
實際使用上,建議把多種方式整合到一個腳本裡:
.claude/hooks/notify.sh:
#!/bin/bash
input=$(cat)
reason=$(echo "$input" | jq -r '.stop_reason // "unknown"')
message=$(echo "$input" | jq -r '.stop_message // "Claude 已停止"' | head -c 300)
# 根據原因選 emoji
case "$reason" in
end_turn) emoji="✅"; title="任務完成" ;;
user_input) emoji="⏳"; title="等待輸入" ;;
*) emoji="🔔"; title="Claude 已停止" ;;
esac
# 1. 桌面通知(本機)
case "$(uname)" in
Linux) notify-send "Claude Code: $title" "$message" --urgency=normal ;;
Darwin) osascript -e "display notification \"$message\" with title \"Claude Code: $title\"" ;;
esac
# 2. 聲音提示
case "$(uname)" in
Linux) paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null & ;;
Darwin) afplay /System/Library/Sounds/Glass.aiff & ;;
esac
# 3. Slack(如果有設定 Webhook)
if [[ -n "$SLACK_WEBHOOK_URL" ]]; then
payload=$(jq -n --arg text "$emoji *$title*
$message" '{text: $text}')
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$payload" > /dev/null 2>&1 &
fi
# 4. Telegram(如果有設定 Bot)
if [[ -n "$TELEGRAM_BOT_TOKEN" && -n "$TELEGRAM_CHAT_ID" ]]; then
curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="$TELEGRAM_CHAT_ID" \
-d text="$emoji *$title*
$message" \
-d parse_mode="Markdown" > /dev/null 2>&1 &
fi
.claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/notify.sh"
}
]
}
]
}
}
所有網路請求都要在背景執行
通知腳本裡的 curl 一定要加 & 放背景。如果網路慢或逾時,會卡住 Claude 的整個工作階段。加 > /dev/null 2>&1 & 是標準做法。
Stop 不等於「成功完成」
stop_reason 為 end_turn 通常代表任務完成,但也可能是 Claude 不確定下一步該做什麼。如果你只想在真正完成時通知,可以在腳本裡過濾:
[[ "$reason" != "end_turn" ]] && exit 0
通知內容要截斷
Claude 的最後一則訊息可能很長(包含程式碼區塊、大量輸出)。推送到 Slack 或 Telegram 之前,用 head -c 300 截斷,避免訊息爆量。
環境變數建議放在全域設定
Webhook URL 和 Bot Token 不應該寫進專案的版本庫。建議放在 ~/.zshrc(或 ~/.bashrc)裡,或者用 direnv 管理。
設定完之後,你的工作方式會改變:
不用盯著終端機等,不用一直切視窗看進度。Claude 做它的事,你做你的事,完成了通知你。