Skip to content

Add Event Notifications Plugin for Claude Code Hooks#22

Open
nodeGarden wants to merge 5 commits intodisler:mainfrom
nodeGarden:feature/event-notifications
Open

Add Event Notifications Plugin for Claude Code Hooks#22
nodeGarden wants to merge 5 commits intodisler:mainfrom
nodeGarden:feature/event-notifications

Conversation

@nodeGarden
Copy link
Copy Markdown

@nodeGarden nodeGarden commented Nov 30, 2025

Add Event Notifications Plugin for Claude Code Hooks

Overview

Adds an event notifications plugin that provides audio announcements and OS notifications for Claude Code hook events. Built on top of the plugin manager system, this demonstrates the plugin architecture in action.

Important

This was vibe coded with Claude


What's Included

This PR builds on the plugin manager system (#21) and adds:

Event Notifications Plugin

  • Audio announcements via TTS (Text-to-Speech)
  • OS notifications for important hook events
  • Multiple TTS providers: ElevenLabs, OpenAI TTS, pyttsx3 (offline)
  • Configurable per hook type via config/audio_config.json
  • Zero dependencies required - all TTS providers are optional

Plugin Features

  • ✅ Handles all 9 hook types (SessionStart, SessionEnd, PreToolUse, PostToolUse, Stop, SubagentStop, Notification, PreCompact, UserPromptSubmit)
  • ✅ Configurable enable/disable per hook
  • ✅ Custom messages per hook type
  • ✅ Automatic source_app detection (env → config → folder name)
  • ✅ Background execution (non-blocking)
  • ✅ Graceful fallback if TTS unavailable

File Structure

.claude/hooks/plugins/event_notifications/
├── plugin.json                      # Plugin metadata
├── config/
│   └── audio_config.json           # Per-hook configuration
├── src/
│   ├── plugin.py                   # Main entry point
│   ├── audio_announcer.py          # TTS orchestration
│   ├── tts_providers.py            # Provider implementations
│   └── _announce_background.py    # Background execution wrapper
└── test.py                         # Plugin test suite

Configuration

Plugin Metadata (plugin.json)

{
  "name": "event_notifications",
  "version": "1.0.0",
  "description": "Event notifications via audio announcements and OS notifications",
  "enabled": true,
  "priority": 50,
  "hooks": ["SessionStart", "SessionEnd", "PreToolUse", "PostToolUse", "Stop", "SubagentStop", "Notification", "PreCompact", "UserPromptSubmit"],
  "entry_point": "src.plugin:handle_hook"
}

Hook Configuration (config/audio_config.json)

{
  "PostToolUse": {
    "enabled": false,
    "message": "{source_app} used {tool_name}"
  },
  "Stop": {
    "enabled": true,
    "message": "{source_app} completed all tasks"
  }
}

TTS Provider Support

1. ElevenLabs

  • High quality voices
  • Requires API key: ELEVENLABS_API_KEY
  • Optional package: elevenlabs

2. OpenAI TTS

  • Natural sounding voices
  • Requires API key: OPENAI_API_KEY
  • Optional package: openai

3. pyttsx3 (Offline) (Recommended)

  • Works without internet
  • No API key required
  • Optional package: pyttsx3

Fallback: If no TTS provider is available, notifications are silently skipped.

Usage Examples

Enable Notifications for Specific Hooks

Edit .claude/hooks/plugins/event_notifications/config/audio_config.json:

{
  "Stop": {
    "enabled": true,
    "message": "Task completed"
  },
  "SubagentStop": {
    "enabled": true,
    "message": "Subagent finished"
  }
}

Customize Messages with Variables

Available variables: {source_app}, {session_id}, {tool_name}, etc.

{
  "PostToolUse": {
    "enabled": true,
    "message": "{source_app} used {tool_name}"
  }
}

Set Source App Name

Three ways (priority order):

  1. Environment variable: export CLAUDE_SOURCE_APP="my-app"
  2. Config file: Create .claude/hooks/config.json with {"source_app": "my-app"}
  3. Auto-detection: Uses project folder name automatically

Testing

Test the Plugin

# Test all hooks
cd .claude/hooks/plugins/event_notifications
python3 test.py

# Test specific hook via plugin manager
cd .claude/hooks
python3 plugin_manager.py --test Stop

List All Plugins

cd .claude/hooks
python3 plugin_manager.py --list

Expected output:

Discovered Plugins:
------------------------------------------------------------
event_notifications v1.0.0 (✓ enabled)
  Hooks: SessionStart, SessionEnd, PreToolUse, PostToolUse, Stop, SubagentStop, Notification, PreCompact, UserPromptSubmit

Dependencies

All optional - plugin works without any of these:

# Install TTS providers (optional)
pip install elevenlabs  # For ElevenLabs TTS
pip install openai      # For OpenAI TTS
pip install pyttsx3     # For offline TTS

Integration with Plugin Manager

This plugin demonstrates:

  • ✅ Auto-discovery from plugins/ directory
  • ✅ Metadata validation via plugin.json
  • ✅ Priority-based execution (priority: 50)
  • ✅ Fail-safe error handling (never crashes hooks)
  • ✅ Graceful degradation (works without TTS)
  • ✅ Source app detection (env → config → folder name)

Benefits

For Users

  • Ambient awareness - Know when tasks complete without watching the screen
  • Multi-tasking - Work on other things while Claude processes
  • Configurable - Enable only the notifications you want
  • Zero setup - Works with folder name auto-detection

For Developers

  • Reference implementation - Shows how to build a complete plugin
  • Best practices - Background execution, config loading, error handling
  • Extensible - Easy to add new TTS providers or notification types

Changes Summary

New Files:

  • plugins/event_notifications/ - Complete plugin implementation (12 files)
    • plugin.json - Plugin metadata
    • config/audio_config.json - Hook configuration
    • src/plugin.py - Main entry point
    • src/audio_announcer.py - TTS orchestration
    • src/tts_providers.py - Provider implementations
    • src/_announce_background.py - Background execution
    • test.py - Plugin test suite

Dependencies on:

Breaking Changes

None. This is a purely additive feature.

Migration Notes

If you were using the old audio announcements system:

  1. The new plugin-based system replaces standalone audio hooks
  2. Configuration is now in plugins/event_notifications/config/audio_config.json
  3. All features are preserved with better organization

Related PRs


Branch: feature/event-notifications
Base: main
Commits: 4

  • 033dcbc Add plugin manager system for hooks
  • 8c3571f Improve plugin system with logging, validation, and dev tools
  • 5f7384f Add comprehensive test suite for plugin manager
  • 9518d3e Add source_app auto-detection to hook plugins
  • 935d97e Add event notifications plugin

Testing:

  • ✅ Plugin manager tests: 9/9 passing
  • ✅ Event notifications plugin discovered and enabled
  • ✅ Source app detection working (env → config → folder name)

Documentation:

  • ✅ Plugin configuration examples
  • ✅ TTS provider setup instructions
  • ✅ Testing guide included

nodeGarden and others added 5 commits November 30, 2025 17:22
Implements a clean plugin architecture that eliminates dirty git state
and follows Python plugin framework best practices.

**Plugin Manager:**
- Auto-discovers plugins from plugins/ directory
- Loads plugin metadata from plugin.json
- Executes plugins with fail-safe error handling
- Supports multiple plugins per hook with priority ordering

**Hook Integration:**
- Minimal 7-line addition to each of 9 hook files
- Graceful fallback if plugin_manager doesn't exist
- No patching required - clean single import

**Developer Experience:**
- Comprehensive PLUGIN_DEVELOPMENT.md guide
- Complete TEMPLATE plugin skeleton
- Drop-in installation (no scripts needed)
- Clean git state (no modified core files)

**Benefits:**
- Eliminates dirty git state problem
- Upstream-friendly minimal changes
- Supports unlimited plugins simultaneously
- Easy plugin distribution and updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implemented all recommended improvements from code review:

**1. Professional Logging System**
- Replaced all print() statements with proper logging module
- Configured logger with formatter: [PluginManager] LEVEL: message
- Default level: WARNING (configurable with --verbose flag)
- Logs to stderr to avoid interfering with stdout
- Better error context in log messages

**2. Plugin Version Compatibility**
- Added __version__ = "1.0.0" to plugin manager
- New min_manager_version field in plugin.json
- Automatic version checking during plugin load
- Semantic versioning support (major.minor.patch)
- _is_version_compatible() helper function
- Skips incompatible plugins with clear warning

**3. Plugin Reload Support**
- Added reload_plugins() method to PluginManager
- Clears and rediscovers all plugins from disk
- Useful for development when modifying plugins
- CLI: python3 plugin_manager.py --reload
- Logs reload progress with DEBUG level

**4. Config Schema Validation**
- Comprehensive PLUGIN_METADATA_SCHEMA definition
- Validates all metadata fields and formats
- _validate_metadata() with detailed error messages
- Checks: name format, version format, entry point syntax
- Optional jsonschema support for strict validation
- Falls back to basic validation if jsonschema unavailable

**Enhanced CLI:**
- --list: List all plugins (existing)
- --test HOOK: Test plugins for hook type (existing)
- --reload: Reload plugins from disk (NEW)
- --verbose: Enable DEBUG logging (NEW)

**Benefits:**
- More professional error messages
- Easier debugging with verbose mode
- Prevents incompatible plugin versions
- Hot-reload during development
- Better metadata validation
- Production-ready logging

All improvements backward compatible. Existing plugins work unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Created complete test coverage for plugin_manager.py following
the project's simple Python test pattern (no pytest required).

**Test Coverage (9 tests, 100% pass):**

1. **test_version_compatibility()**
   - Exact version matches
   - Newer versions (major, minor, patch)
   - Older versions (should fail)
   - Edge cases and boundary conditions

2. **test_metadata_validation()**
   - Valid metadata acceptance
   - Missing required fields detection
   - Invalid name format (uppercase, special chars)
   - Invalid version format (non-semver)
   - Invalid entry point format

3. **test_plugin_class()**
   - Plugin properties (name, version, enabled)
   - Hook support checking
   - Execution without errors
   - Disabled plugin behavior

4. **test_plugin_error_handling()**
   - Plugins that raise exceptions
   - Error catching and logging
   - Hook execution continues despite errors

5. **test_plugin_manager_discovery()**
   - Auto-discovery from plugins directory
   - Plugin loading and initialization
   - Plugin registration

6. **test_plugin_manager_version_check()**
   - Incompatible plugins skipped
   - Version requirement enforcement
   - Warning messages logged

7. **test_plugin_manager_reload()**
   - Plugin rediscovery from disk
   - State clearing and rebuilding
   - Hot-reload during development

8. **test_plugin_priority_execution()**
   - Plugins execute in priority order
   - Lower number = higher priority
   - Execution sequence verification

9. **test_execute_plugins_function()**
   - Convenience function works correctly
   - Singleton manager instance
   - Error handling for unknown hooks

**Test Infrastructure:**
- TestResults class tracks pass/fail
- Temporary directories for isolated testing
- No external dependencies (uses stdlib only)
- Clear pass/fail output with summary
- Proper cleanup after tests

**Usage:**
```bash
# Run all tests
python3 .claude/hooks/tests/test_plugin_manager.py

# Verbose output
python3 .claude/hooks/tests/test_plugin_manager.py --verbose
```

**Benefits:**
- Verifies all improvements work correctly
- Catches regressions during future changes
- Documents expected behavior
- No pytest/dependencies required
- Fast execution (<1 second)

All tests passing ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Multi-modal notification system for hook events using audio (TTS)
and macOS OS notifications.

**Features:**
- 🔊 Multiple TTS providers (macOS say, ElevenLabs, OpenAI, pyttsx3)
- 🔔 macOS system notifications (banner/alert types)
- 🎵 System sound effects for quick feedback
- 📝 Template-based customizable announcements
- 🎭 Profile system for quick notification mode switching
- 🔊 Relative volume (doesn't interfere with other audio)
- 🎚️ Verbosity levels (minimal, normal, detailed)

**Profile System:**
- default: Minimal (Stop, Notification, PreCompact only)
- verbose: All events with audio
- silent: OS notifications only, no audio
- quiet: Sounds only, no TTS

**Volume System:**
- Audio plays at percentage of current system volume
- Uses afplay for relative volume control
- No system volume changes (safe for Zoom/music)

**Configuration:**
- Profile-based enable/disable for audio/notifications/sounds
- Per-hook settings for volume, sounds, notification text
- Template system with variable interpolation
- Future-ready for per-app profile switching in UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implements a flexible source_app detection system with fallback chain:
1. CLAUDE_SOURCE_APP environment variable (highest priority)
2. source_app in .claude/hooks/config.json
3. Project directory name auto-detection (fallback)

**New Files:**
- utils/source_app.py - Detection utility with get_source_app()
- config.json - Configured with "cc-hook-multi-agent-obvs"
- config.json.example - Template for other projects

**Modified Hooks:**
All 9 hook files now add source_app to input_data before calling plugins:
- notification.py
- post_tool_use.py
- pre_compact.py
- pre_tool_use.py
- session_end.py
- session_start.py
- stop.py
- subagent_stop.py
- user_prompt_submit.py

**Benefits:**
- Plugins can now identify which app triggered the hook
- No manual configuration required (auto-detects from folder name)
- Flexible override via env var or config file
- Consistent source_app across all hooks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant