claude-code-slack-stop-hook.json
Slack ping when a long Claude Code turn finishes
A Claude Code hook that posts to Slack when a turn takes longer than two minutes, so you can walk away from long agent runs and get pulled back when they're done.
Install
- Save
1-notify-long-turn.shto~/.claude/hooks/notify-long-turn.sh, thenchmod +xit. - Create a Slack incoming webhook and export the URL as
SLACK_WEBHOOK_URL. - Set
MENTIONin the script to your own Slack member ID (Slack profile -> More -> Copy member ID). Without the mention the message is silent -- the mention is what makes Slack actually notify you. - Merge
2-settings-snippet.jsoninto~/.claude/settings.json, replacing the paths with absolute ones.settings.jsondoes not expand$HOME.
Requires jq and curl.
How it works
UserPromptSubmit stamps the turn's start time to /tmp. Stop reads it back, and if the turn ran >= THRESHOLD_SECS (default 120) it posts to the webhook.
A missing stamp file means the Stop came from /clear, a resume, or a compact, so there's nothing to time and the hook exits quietly.
The Stop hook is async, so it never adds latency to your turn.
Tuning
THRESHOLD_SECS-- raise it if you get pinged too often.- Stamp files are per-session in
/tmp; a session that dies without aStopleaves one behind. Harmless, butrm /tmp/claude-turn-start-*clears them.
Note
The webhook call is fire-and-forget: output is discarded, so the hook reports success whether or not Slack accepted the post. Fine for a personal nudge, but don't debug Slack delivery by trusting that message.
| #!/usr/bin/env bash | |
| # Slack-ping yourself when a Claude Code turn runs long. | |
| # start (UserPromptSubmit) -> stamp the turn's start time | |
| # stop (Stop) -> post if the turn took >= THRESHOLD_SECS | |
| set -uo pipefail | |
| THRESHOLD_SECS=120 | |
| SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_URL:?set this to your Slack incoming webhook} | |
| MENTION="<@U0123456789>" # your Slack member ID, so the post actually notifies | |
| mode=${1:-stop} | |
| payload=$(cat) | |
| session=$(printf '%s' "$payload" | jq -r '.session_id // "unknown"') | |
| cwd=$(printf '%s' "$payload" | jq -r '.cwd // ""') | |
| stamp="/tmp/claude-turn-start-${session}" | |
| if [[ $mode == start ]]; then date +%s >"$stamp"; exit 0; fi | |
| # No stamp = Stop from /clear, resume, or compact. Nothing to time. | |
| [[ -f $stamp ]] || exit 0 | |
| started=$(<"$stamp"); rm -f "$stamp" | |
| elapsed=$(($(date +%s) - started)) | |
| ((elapsed >= THRESHOLD_SECS)) || exit 0 | |
| text="$MENTION Claude Code finished after $((elapsed / 60))m$((elapsed % 60))s in $(basename "${cwd:-$PWD}")" | |
| jq -nc --arg t "$text" '{text:$t}' \ | |
| | curl -sS -X POST -H 'Content-type: application/json' --data @- "$SLACK_WEBHOOK_URL" >/dev/null | |
| jq -nc --arg m "$text" '{systemMessage:("Slack ping sent - " + $m)}' |
| { | |
| "hooks": { | |
| "UserPromptSubmit": [ | |
| { | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "/home/YOUR_USERNAME/.claude/hooks/notify-long-turn.sh start" | |
| } | |
| ] | |
| } | |
| ], | |
| "Stop": [ | |
| { | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "/home/YOUR_USERNAME/.claude/hooks/notify-long-turn.sh stop", | |
| "timeout": 120, | |
| "async": true | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| } |
评论
?
参与讨论