Created
July 20, 2025 23:48
-
-
Save qsun/7e6ed61feecf77d700a8281a00cb0149 to your computer and use it in GitHub Desktop.
When claude code finishes the conversation, send a slack message to remind myself
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
| #!/usr/bin/env python3 | |
| import json | |
| import sys | |
| import os | |
| import requests | |
| from urllib.parse import quote | |
| from datetime import datetime | |
| # ~/.claude/settings.json | |
| ''' | |
| { | |
| "permissions": { | |
| "allow": [ | |
| "Bash(git push:*)", | |
| "Bash(git checkout:*)", | |
| "Bash(git add:*)", | |
| "Bash(grep:*)", | |
| "Bash(find:*)", | |
| "Bash(nslookup:*)", | |
| "Bash(aws elbv2 describe-load-balancers:*)", | |
| "Bash(kubectl get:*)", | |
| "Bash(kubectl describe:*)", | |
| "Bash(kubectl logs:*)", | |
| "Bash(curl:*)", | |
| "Bash(jira create:*)", | |
| "WebFetch(domain:docs.anthropic.com)", | |
| "Bash(aws logs :*)" | |
| ], | |
| "deny": [] | |
| }, | |
| "hooks": { | |
| "Notification": [ | |
| { | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "/home/qsun/bins/finish.py" | |
| } | |
| ] | |
| } | |
| ], | |
| "Stop": [ | |
| { | |
| "matcher": "", | |
| "hooks": [ | |
| { | |
| "type": "command", | |
| "command": "python3 /home/qsun/bins/finish.py" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| } | |
| ''' | |
| SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/XX/YYY/ZZZ" | |
| LOG_FILE = "/tmp/finish.log" | |
| def log_session(project_name, last_prompt, session_id, transcript_path): | |
| """Log session information to persistent log file.""" | |
| try: | |
| timestamp = datetime.now().isoformat() | |
| log_entry = { | |
| "timestamp": timestamp, | |
| "session_id": session_id, | |
| "project_name": project_name, | |
| "last_prompt": last_prompt, | |
| "transcript_path": transcript_path | |
| } | |
| with open(LOG_FILE, 'a') as f: | |
| f.write(json.dumps(log_entry) + '\n') | |
| except Exception as e: | |
| print(f"Error writing to log file: {e}") | |
| def send_slack_notification(project_name, last_prompt, session_id, transcript_path): | |
| """Send notification to Slack when Claude Code conversation finishes.""" | |
| # Read last prompt from transcript if available | |
| last_user_message = "No prompt available" | |
| if transcript_path and os.path.exists(transcript_path): | |
| try: | |
| with open(transcript_path, 'r') as f: | |
| lines = f.readlines() | |
| # Parse JSONL format and find last user message | |
| for line in reversed(lines): | |
| line = line.strip() | |
| if not line: | |
| continue | |
| try: | |
| entry = json.loads(line) | |
| # Look for user type messages | |
| if (entry.get('type') == 'user' and | |
| 'message' in entry and | |
| entry['message'].get('role') == 'user'): | |
| content = entry['message'].get('content', '') | |
| if isinstance(content, str) and content.strip(): | |
| last_user_message = content.strip() | |
| break | |
| except json.JSONDecodeError: | |
| continue | |
| except Exception: | |
| pass | |
| # If last_prompt is provided, use it instead | |
| if last_prompt: | |
| last_user_message = last_prompt | |
| # Truncate long messages | |
| if len(last_user_message) > 200: | |
| last_user_message = last_user_message[:197] + "..." | |
| message = { | |
| "text": f"🤖 Claude Code Session Finished", | |
| "blocks": [ | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": f"*Claude Code Session Finished*\n\n*Project:* `{project_name}`\n*Session ID:* `{session_id}`\n*Last Prompt:* {last_user_message}" | |
| } | |
| } | |
| ] | |
| } | |
| try: | |
| response = requests.post(SLACK_WEBHOOK_URL, json=message, timeout=10) | |
| if response.status_code == 200: | |
| print("Slack notification sent successfully") | |
| else: | |
| print(f"Failed to send Slack notification: {response.status_code}") | |
| except Exception as e: | |
| print(f"Error sending Slack notification: {e}") | |
| def main(): | |
| try: | |
| # Read JSON data from stdin | |
| input_data = json.loads(sys.stdin.read()) | |
| # Extract required information | |
| session_id = input_data.get("session_id", "unknown") | |
| cwd = input_data.get("cwd", os.getcwd()) | |
| transcript_path = input_data.get("transcript_path") | |
| # Get project name from cwd | |
| project_name = os.path.basename(cwd) if cwd else "unknown" | |
| # Extract last prompt if available | |
| last_prompt = input_data.get("last_prompt", "") | |
| # Log session to persistent file | |
| log_session(project_name, last_prompt, session_id, transcript_path) | |
| # Send Slack notification | |
| send_slack_notification(project_name, last_prompt, session_id, transcript_path) | |
| # Exit successfully | |
| sys.exit(0) | |
| except Exception as e: | |
| print(f"Hook error: {e}") | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment