Boopifier is a universal notification handler for Claude Code events. It reads JSON events from stdin and dispatches them to various notification handlers based on your configuration.
brew tap terraboops/boopifier https://github.com/terraboops/boopifier
brew install boopifierPrerequisites:
- Rust toolchain (install from https://rustup.rs)
- For Signal notifications: signal-cli (https://github.com/AsamK/signal-cli)
# Clone the repository
git clone https://github.com/terraboops/boopifier.git
cd boopifier
# Build and install
make installboopifier --list-handlersThis will show all available notification types:
- desktop
- sound
- signal
- webhook
Boopifier automatically finds your config file:
- Project-specific:
$CLAUDE_PROJECT_DIR/.claude/boopifier.json(when run via Claude Code hooks) - Global fallback:
~/.claude/boopifier.json
Create a .claude/boopifier.json file in your project (or globally at ~/.claude/boopifier.json):
{
"handlers": [
{
"name": "success-notification",
"type": "desktop",
"match_rules": {
"status": "success"
},
"config": {
"summary": "Build Success",
"body": "Task {{task}} completed successfully!",
"urgency": "normal",
"timeout": 5000
}
},
{
"name": "random-success-sounds",
"type": "sound",
"match_rules": {
"status": "success"
},
"config": {
"files": [
"~/sounds/success1.wav",
"~/sounds/success2.mp3",
"~/sounds/success3.wav"
],
"random": true,
"volume": 0.8
}
}
]
}# Process one event and exit
echo '{"status": "success", "task": "build"}' | boopifier
# Show debug output including ALSA warnings
echo '{"status": "success", "task": "build"}' | boopifier --debugEach handler has the following structure:
{
"name": "unique-handler-name",
"type": "handler-type",
"match_rules": { /* optional matching rules */ },
"config": { /* handler-specific configuration */ }
}Simple matching:
"match_rules": {
"event_type": "build_complete",
"status": "success"
}Complex matching:
"match_rules": {
"all": [
{"event_type": "test"},
{"status": "success"}
],
"any": [
{"priority": "high"},
{"priority": "critical"}
],
"not": {
"ignored": true
}
}Nested fields:
"match_rules": {
"tool.name": "bash",
"tool.exit_code": 0
}No rules (match all):
"match_rules": nullBoopifier supports secure credential management:
Environment variables:
{
"config": {
"webhook_url": "{{env.SLACK_WEBHOOK_URL}}"
}
}File-based secrets:
{
"config": {
"api_key": "{{file.~/.secrets/api-key}}"
}
}{
"type": "desktop",
"config": {
"summary": "Notification Title",
"body": "Notification body with {{variable}} substitution",
"urgency": "normal", // low, normal, critical
"timeout": 5000 // milliseconds
}
}Play audio files using rodio. Supports WAV, MP3, and other common formats.
Single sound file:
{
"type": "sound",
"config": {
"file": "/path/to/sound.wav",
"volume": 0.8 // 0.0 to 1.0, default 1.0
}
}Multiple files with random selection:
{
"type": "sound",
"config": {
"files": [
"/path/to/sound1.wav",
"/path/to/sound2.mp3",
"/path/to/sound3.wav"
],
"random": true, // randomly select from files array
"volume": 1.0
}
}Multiple files without random (uses first):
{
"type": "sound",
"config": {
"files": ["/path/to/primary.wav", "/path/to/fallback.wav"],
"volume": 0.8
}
}{
"type": "signal",
"config": {
"recipient": "+1234567890",
"message": "Build {{status}}: {{details}}",
"signal_cli_path": "signal-cli", // optional
"account": "+9876543210" // optional sender
}
}Slack:
{
"type": "webhook",
"config": {
"url": "{{env.SLACK_WEBHOOK_URL}}",
"type": "slack",
"text": "Build {{status}}",
"channel": "#builds",
"username": "Claude Code Bot"
}
}Discord:
{
"type": "webhook",
"config": {
"url": "{{env.DISCORD_WEBHOOK_URL}}",
"type": "discord",
"content": "Build {{status}}",
"username": "Claude Code"
}
}Generic JSON:
{
"type": "webhook",
"config": {
"url": "https://your-server.com/webhook",
"type": "json",
"payload": {
"event": "{{event_type}}",
"data": "{{details}}"
}
}
}{
"type": "email",
"config": {
"from": "bot@example.com",
"to": "user@example.com",
"subject": "Build {{status}}",
"body": "Details: {{message}}",
"smtp_server": "smtp.example.com",
"username": "{{env.SMTP_USER}}",
"password": "{{env.SMTP_PASS}}"
}
}Add boopifier to your Claude Code hooks in ~/.claude/settings.json or .claude/settings.json:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "boopifier"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "boopifier"
}
]
}
]
}
}Available hook events include:
Notification- When Claude sends a notificationStop- When Claude finishes respondingPostToolUse- After a tool is usedPermissionRequest- When permission is requested- And more (see Claude Code hooks docs)
To troubleshoot, add --debug flag to the command:
{
"type": "command",
"command": "boopifier --debug"
}This logs to /tmp/boopifier.log.
Important: Claude Code requires workspace trust to execute hooks. If your hooks aren't firing, check for this message in the debug logs:
Skipping [hook type] hook execution - workspace trust not accepted
To resolve:
- Start Claude Code from a project directory (not your home directory)
- Accept the workspace trust prompt when it appears
- Hooks will only execute in trusted workspaces
Why: Hooks execute arbitrary commands, so Claude Code requires explicit trust per workspace for security. Your home directory (~) is typically not trusted by default.
Debugging: Check ~/.claude/debug/latest for hook execution messages:
grep -i "hook" ~/.claude/debug/latestIf you see "workspace trust not accepted", move to a project directory or create a dedicated workspace like ~/projects/my-work.
Get desktop notifications when builds complete:
{
"name": "build-complete",
"type": "desktop",
"match_rules": {"event_type": "build_complete"},
"config": {
"summary": "Build Complete",
"body": "Exit code: {{exit_code}}"
}
}Send Signal messages on errors:
{
"name": "error-alert",
"type": "signal",
"match_rules": {"severity": "error"},
"config": {
"recipient": "+1234567890",
"message": "Error: {{message}}"
}
}Post to Slack on important events:
{
"name": "team-update",
"type": "webhook",
"match_rules": {"priority": "high"},
"config": {
"url": "{{env.SLACK_WEBHOOK}}",
"type": "slack",
"text": "High priority event: {{details}}",
"channel": "#dev-team"
}
}Install signal-cli:
# Arch Linux
yay -S signal-cli
# Or from source
# See https://github.com/AsamK/signal-cliEnsure your system has a notification daemon running (e.g., dunst, notify-osd).
- Check your webhook URL is correct
- Verify environment variables are set
- Use
--debugto see detailed error messages
When using the Homebrew-installed version on Linux, you may see ALSA warnings like:
ALSA lib dlmisc.c:339:(snd_dlobj_cache_get0) Cannot open shared library libasound_module_pcm_pulse.so
ALSA lib pcm_rate.c:1581:(snd_pcm_rate_open) Cannot find rate converter
These warnings are cosmetic and do not affect functionality. They occur because Homebrew's alsa-lib package is missing optional plugin libraries (PulseAudio, JACK, PipeWire, etc.). Sound playback works correctly via direct ALSA.
To suppress these warnings:
# Option 1: Redirect stderr when invoking boopifier
boopifier 2>/dev/null
# Option 2: Set environment variable
LIBASOUND_DEBUG=0 boopifier
# Option 3: In Claude Code hooks config, redirect stderr
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "sh -c 'boopifier 2>/dev/null'"
}
]
}
]
}
}Note: Binaries built locally with cargo install or make install do not show these warnings.
- See
CLAUDE.mdfor development details - Check
Cargo.tomlfor dependency information - Run
boopifier --helpfor CLI options