Last active
March 23, 2026 02:25
-
-
Save koteitan/24eaecd79c9c121bf2d1f0a85e292878 to your computer and use it in GitHub Desktop.
claude-pushover: send a push notification from claude code when the turn passes back to the user
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # claude-pushover - Send Claude Code conversation summary to Pushover | |
| # push over https://pushover.net/ is a web API to make a push notification into iPhone App | |
| # Configuration | |
| SECURITY_MODE=0 # 0=off (detailed messages), 1=on (simple message only) | |
| MAX_CHARS=800 # Maximum message length for Pushover | |
| DEBUG_LOG="$HOME/claude-pushover-debug.log" # Debug log file | |
| # Function to extract message from hook JSON input | |
| get_hook_message() { | |
| # Check security mode first | |
| if [[ $SECURITY_MODE -eq 1 ]]; then | |
| echo "claude code finish the job." | |
| return | |
| fi | |
| local json_input="" | |
| # Read JSON from stdin if available (Claude Code hooks pass data via stdin) | |
| if [[ -t 0 ]]; then | |
| echo "$(date): No stdin available" >> "$DEBUG_LOG" | |
| echo "Claude Code task completed" | |
| return | |
| fi | |
| # Read all stdin | |
| json_input=$(cat) | |
| # Debug: log the raw input | |
| { | |
| echo "=== $(date) ===" | |
| echo "Raw input:" | |
| echo "$json_input" | |
| echo "---" | |
| } >> "$DEBUG_LOG" | |
| # Try to parse with jq if available | |
| if command -v jq &>/dev/null; then | |
| # Extract transcript path from hook JSON | |
| local transcript_path=$(echo "$json_input" | jq -r '.transcript_path // empty' 2>/dev/null) | |
| # Also try to find the most recent transcript file in case the hook provides an old one | |
| local transcript_dir=$(dirname "$transcript_path" 2>/dev/null) | |
| if [[ -d "$transcript_dir" ]]; then | |
| local latest_transcript=$(ls -t "$transcript_dir"/*.jsonl 2>/dev/null | head -1) | |
| if [[ -f "$latest_transcript" ]]; then | |
| echo "Hook provided: $transcript_path" >> "$DEBUG_LOG" | |
| echo "Latest found: $latest_transcript" >> "$DEBUG_LOG" | |
| transcript_path="$latest_transcript" | |
| fi | |
| fi | |
| echo "Using transcript path: $transcript_path" >> "$DEBUG_LOG" | |
| if [[ -f "$transcript_path" ]]; then | |
| # Extract last 5 lines of assistant text messages using tail and jq | |
| local last_assistant=$(tail -50 "$transcript_path" | jq -r 'select(.type == "assistant") | .message.content[]? | select(.type == "text") | .text' 2>/dev/null | tail -5) | |
| # Extract last user text message only (excluding tool results) | |
| # First get the last user JSON entry, then extract content to preserve multi-line messages | |
| # Limit to last 5 lines for notification preview | |
| local last_user=$(tail -50 "$transcript_path" | jq -c 'select(.type == "user") | select(.message.content | type == "string")' 2>/dev/null | tail -1 | jq -r '.message.content' 2>/dev/null | tail -5) | |
| echo "Extracted assistant: ${last_assistant:0:100}..." >> "$DEBUG_LOG" | |
| echo "Extracted user: ${last_user:0:100}..." >> "$DEBUG_LOG" | |
| # Build the message | |
| local message="" | |
| if [[ -n "$last_user" && "$last_user" != "null" ]]; then | |
| # Truncate user message if too long | |
| if [[ ${#last_user} -gt 300 ]]; then | |
| message="User: ${last_user:0:300}..." | |
| else | |
| message="User: $last_user" | |
| fi | |
| fi | |
| if [[ -n "$last_assistant" && "$last_assistant" != "null" ]]; then | |
| if [[ -n "$message" ]]; then | |
| message="$message | |
| Claude: $last_assistant" | |
| else | |
| message="$last_assistant" | |
| fi | |
| fi | |
| if [[ -n "$message" ]]; then | |
| echo "Extracted message:" >> "$DEBUG_LOG" | |
| echo "$message" >> "$DEBUG_LOG" | |
| echo "$message" | |
| return | |
| else | |
| echo "No message extracted from transcript" >> "$DEBUG_LOG" | |
| fi | |
| else | |
| echo "Transcript file not found: $transcript_path" >> "$DEBUG_LOG" | |
| fi | |
| fi | |
| # Final fallback | |
| echo "Claude Code task completed" | |
| } | |
| # Get the message | |
| MESSAGE=$(get_hook_message) | |
| # Truncate if too long | |
| if [[ ${#MESSAGE} -gt $MAX_CHARS ]]; then | |
| MESSAGE="${MESSAGE:0:$MAX_CHARS}..." | |
| fi | |
| # Send to pushover with emoji prefix | |
| pushover "🤖 Claude: | |
| $MESSAGE" >/dev/null 2>&1 | |
| exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # please replace <your application key> and <your user key> into your settings. | |
| curl -s\ | |
| --form-string "token=<your application key>"\ | |
| --form-string "user=<your user key>"\ | |
| --form-string "message=$1" https://api.pushover.net/1/messages.json > /dev/null |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "hooks": { | |
| "Notification": [ | |
| { | |
| "matcher": "", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "claude-pushover.bash" | |
| } | |
| ] | |
| } | |
| ], | |
| "Stop": [ | |
| { | |
| "matcher": "", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "claude-pushover.bash" | |
| } | |
| ] | |
| } | |
| ] | |
| }, | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The tail of the last message from claude code is displayed in the app.

