Enhanced Claude Code notification system that sends different Telegram messages based on notification type and context.
- π Session Start: Notifies when Claude Code session begins
- π Tool Approval: Alerts when Claude requests permission to use tools
- β Task Completed: Sends completion notification
- π€ Subagent Completed: Notifies when subagent tasks finish
- π Session End: Final notification when session closes
- π» macOS Desktop Notifications: Native notifications alongside Telegram alerts (macOS only)
Set these environment variables for Telegram notifications:
export TELEGRAM_BOT_TOKEN="your_bot_token_here"
export TELEGRAM_CHAT_ID="your_chat_id_here"Add them to your shell profile (~/.bashrc, ~/.zshrc, etc.) to persist across sessions.
- Message @BotFather on Telegram
- Send
/newbotand follow instructions - Copy the bot token provided
- Start a chat with your bot and send any message
- Get your chat ID:
https://api.telegram.org/bot<TOKEN>/getUpdates
Option A: Interactive Setup (Recommended)
- Open Claude Code and run
/hooks - For each hook event (SessionStart, Notification, Stop, SubagentStop, SessionEnd):
- Select the event type
- Add matcher (use
*to match all) - Enter command:
./scripts/claude-notification-handler.sh - Choose User settings for global config or Project settings for project-specific
Option B: Manual Configuration
Edit your Claude Code settings file:
- Global:
~/.claude/settings.json(applies to all projects) - Project:
.claude/settings.json(shared with team) - Local:
.claude/settings.local.json(personal, not committed)
Add the hooks configuration (see hooks.json.example for reference):
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "./scripts/claude-notification-handler.sh"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/claude-notification-handler.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/claude-notification-handler.sh"
}
]
}
],
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/claude-notification-handler.sh"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/claude-notification-handler.sh"
}
]
}
]
}
}Note: If using global configuration (~/.claude/settings.json), use absolute paths:
"command": "/absolute/path/to/scripts/claude-notification-handler.sh"For Project-Specific Setup:
# Make scripts executable
chmod +x scripts/claude-notification-handler.sh
chmod +x scripts/test-notifications.sh
# Optional: Copy example settings to .claude directory
mkdir -p .claude
cp .claude/settings.json.example .claude/settings.json
# Edit .claude/settings.json as neededFor Global Setup:
# Create a shared location
mkdir -p ~/claude-hooks
cp -r scripts ~/claude-hooks/
chmod +x ~/claude-hooks/scripts/*.sh
# Update hooks configuration to use absolute paths
# Edit ~/.claude/settings.json and use:
# "command": "/Users/your-username/claude-hooks/scripts/claude-notification-handler.sh"Test the notification system without waiting for actual Claude Code events:
# Run the test suite (automatically creates test environment)
./scripts/test-notifications.shYou should receive 7 different Telegram notifications, one for each event type.
| Event | Emoji | Trigger | Information Included |
|---|---|---|---|
| Session Start | π | Claude Code starts | Project name, action |
| Tool Approval | π | Permission request | Project name, approval details |
| Task Complete | β | Main task done | Project name, completion status |
| Subagent Complete | π€ | Subagent task done | Project name, completion status |
| Session End | π | Session closes | Project name, end status |
This notification system includes several reliability and security enhancements:
- Robust JSON Parsing: Uses
jqwhen available, with fallback to grep/sed - URL Encoding: Properly encodes special characters for Telegram API safety
- Error Handling: 3-attempt retry logic with 10-second timeout for network resilience
- Duration Validation: Sanity checks prevent invalid duration calculations (>24h)
- Graceful Failures: Never blocks Claude Code even if notifications fail
- Unified Handler: All hook types use the same well-tested script
- macOS Desktop Notifications: Native system notifications with platform detection and graceful fallback
Edit scripts/claude-notification-handler.sh to customize:
- Emoji icons
- Message format
- Included information
- Pattern matching for notification types
- Add new case in
claude-notification-handler.sh - Add corresponding hook event via
/hookscommand or settings file - Test with
test-notifications.sh
Edit your Claude Code settings file and remove unwanted hook events:
{
"hooks": {
"SessionStart": [...],
// "SubagentStop": [...], // Remove or comment out to disable
"Stop": [...]
}
}Or use /hooks command and delete specific hooks interactively.
-
Check environment variables:
echo $TELEGRAM_BOT_TOKEN echo $TELEGRAM_CHAT_ID
-
Verify bot token and chat ID:
curl "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/getMe" -
Test manually:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage" \ -d "chat_id=$TELEGRAM_CHAT_ID" \ -d "text=Test message"
-
Check script permissions:
ls -la scripts/claude-notification-handler.sh # Should show: -rwxr-xr-x (executable)
Run the handler directly to see error messages:
echo '{"hook_event_name":"Stop"}' | ./scripts/claude-notification-handler.sh- Verify hooks configuration location:
- Run
/hooksto check active hooks - Check
~/.claude/settings.jsonfor global hooks - Check
.claude/settings.jsonfor project hooks
- Run
- Ensure script has executable permissions:
chmod +x scripts/claude-notification-handler.sh - Verify script path is correct (use absolute paths for global config)
- Check Claude Code output for hook errors
- Restart Claude Code after configuration changes
- Create shared script location:
mkdir -p ~/claude-hooks/scripts
cp scripts/claude-notification-handler.sh ~/claude-hooks/scripts/
chmod +x ~/claude-hooks/scripts/claude-notification-handler.sh- Configure global hooks with absolute path in
~/.claude/settings.json:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "/Users/your-username/claude-hooks/scripts/claude-notification-handler.sh"
}
]
}
]
}
}- Set environment variables in your shell profile (
~/.bashrc,~/.zshrc):
export TELEGRAM_BOT_TOKEN="your_bot_token"
export TELEGRAM_CHAT_ID="your_chat_id"macOS desktop notifications are built-in and enabled by default. They work alongside Telegram notifications and use terminal-notifier for enhanced functionality.
Features:
- Click-to-activate: Click any notification to bring Terminal to the front
- Automatic platform detection (macOS only)
- Event-specific messages with appropriate details
- Sound alerts for important events (Tool Approval, Task Complete, etc.)
- Graceful fallback to basic
osascriptifterminal-notifiernot installed - Never blocks hook execution on failure
For clickable notifications that activate your terminal when clicked:
brew install terminal-notifierBenefits:
- Click notifications to jump back to your terminal
- Automatic terminal detection - supports all major terminals via
$TERM_PROGRAMwith frontmost app fallback - No additional configuration needed
- Works with all notification types
- Falls back to basic notifications if not installed
Supported Terminals:
- Terminal.app (default fallback)
- iTerm2
- Ghostty
- Alacritty
- kitty
- WezTerm
- Warp
- Hyper
- Tabby
- Rio
- VS Code integrated terminal
- Any other terminal (auto-detected via frontmost app fallback)
Note: Due to macOS terminal limitations, notifications bring the terminal app to the front but cannot navigate to a specific tab. You'll need to manually locate the correct tab after clicking.
Disabling Notifications:
Use environment variables to toggle each notification channel:
# Disable desktop notifications only
export CLAUDE_NOTIFY_DESKTOP="false"
# Disable Telegram notifications only
export CLAUDE_NOTIFY_TELEGRAM="false"
# Disable all notifications
export CLAUDE_NOTIFY_DESKTOP="false"
export CLAUDE_NOTIFY_TELEGRAM="false"Both default to true (enabled). Add to your shell profile to persist.
Add logging to the handler:
# In claude-notification-handler.sh, add:
echo "$(date): $HOOK_EVENT - $TITLE" >> ~/.claude/notifications.logMIT - Feel free to modify and distribute