All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- macOS Tahoe: click-to-focus for all terminals β replaced AppleScript-based window activation with the
focus-windowbinary subcommand (AXTitle + CGS APIs) for regular terminals (iTerm2, Terminal.app, Alacritty, Warp, Hyper, etc.). macOS Tahoe (26.x) broke Automation permission prompts for notification click handlers, causing osascript to fail silently. The new approach uses Accessibility + Screen Recording instead of Automation, with graceful fallback to app-level activation when permissions are not granted (#47) - macOS: app always activates on click β when window title doesn't match the project folder name, the terminal app now still gets activated (app-level focus) instead of doing nothing
- Symlink resolution β
focus-windownow resolves symlinks viafilepath.EvalSymlinksfor more robust binary path handling
- CI: real-network E2E tests non-blocking β marked real-network install E2E tests as
continue-on-erroracross all platforms to prevent flaky network timeouts from failing CI jobs
- Local plugin development tooling β new scripts (
dev-local-plugin.sh,dev-real-plugin.sh,e2e-real-claude.sh) and Makefile targets for isolated and real-Claude plugin workflows, so contributors can reproduce plugin updates and click-to-focus issues with less setup - Linux focus diagnostics β
linux-focus-debug.shscript for troubleshooting click-to-focus issues on Linux - Developer documentation β
docs/LOCAL_DEVELOPMENT.mdguide, expandedCONTRIBUTING.md,docs/CLICK_TO_FOCUS.md, anddocs/troubleshooting.md
- Linux: exact window targeting for click-to-focus β notification clicks now capture exact window hints (X11 window ID, WM class, PID) from the hook process and use them before falling back to generic terminal title matching, significantly improving reliability on multi-window setups
- Bootstrap plugin updates β hardened marketplace checkout and installed plugin version verification during bootstrap, so one-line installs recover cleanly when Claude leaves an older cached plugin version in place
- Linux: X11 window ID for click-to-focus β notifications now pass the terminal's X11 window ID (
$WINDOWID) to the daemon, enabling directxdotool windowactivatetargeting instead of unreliable title-based matching. IncludesTryFocusWithWindowIDpriority path and automatic fallback to existing methods - tmux pane target fallback β
GetTmuxPaneTargetnow uses the resolved tmux binary and explicit socket path for better compatibility across environments (e.g., whenTMUX_PANEis not set)
- Notification suppression default β changed default
SuppressQuestionAfterAnyNotificationSecondsfrom 0 to 7 seconds, preventing repeated "question" notifications immediately after other notifications
- Improved installation instructions and error handling in
install.shβ better diagnostic messages for curl/transport failures, especially for Windows users behind corporate proxies - Clarified README and
setup.shto specify commands should be run inside Claude Code
- macOS: iTerm2 + tmux -CC (control mode) click-to-focus β when using iTerm2's tmux integration (
tmux -CC), clicking a notification now switches to the correct iTerm2 tab via the iTerm2 Python API. Standardtmux select-windowdoesn't work in control mode; the plugin auto-detects -CC mode and falls back gracefully if the Python API is unavailable (#41) - Automatic iTerm2 Python venv setup β
bootstrap.shandinstall.shnow create a Python venv with theiterm2module for tmux -CC support (macOS only, when iTerm2 and tmux are detected)
- Linux: click-to-focus reliability β focus methods (
activate-window-by-title, GNOMEFocusApp,xdotool) now properly validate activation results instead of silently succeeding when no window was actually focused.xdotool windowactivateuses--syncto ensure the activation request completes (#44) - Linux: terminal detection in hook process β focus target is now detected in the hook process instead of the daemon, since the daemon may have been started from a different environment where terminal-specific env vars (e.g.
TERMINATOR_UUID) are not available (#44) - "Updated to" message shown repeatedly β the
[claude-notifications] Updated to vX.Y.Zsystem message is now shown only once per version, using a stamp file to prevent duplicates across hook invocations
- bootstrap.sh: shim directories replace symlinks β old cached version paths now use a lightweight shim
hook-wrapper.shthat forwards to the current install, instead of symlinks (which are unreliable on Windows)
- Linux: click-to-focus for Terminator β Terminator does not set
TERM_PROGRAM, causing the daemon to fall back to generic"Terminal"and fail to find any Terminator window. Now detects Terminator via itsTERMINATOR_UUIDenvironment variable, which it always sets (#44)
- macOS: click-to-focus support for Cursor IDE β Cursor (VS Code fork) is now recognized as an Electron-based editor, using the binary
focus-windowsubcommand instead of AppleScript (which fails on Electron apps with error -1708). Bundle IDcom.todesktop.230313mzl4w4u92is auto-detected via__CFBundleIdentifier(#39)
- Renamed internal
isVSCodeBundleIDβisElectronEditorBundleIDandbuildVSCodeFocusScriptβbuildElectronEditorFocusScriptto better reflect that the logic applies to all Electron-based editors, not just VS Code
- Linux: desktop-entry notification hint β GNOME now correctly identifies the source app when clicking notifications, focusing the terminal/VS Code instead of opening Nautilus. Also adds
suppress-soundhint to prevent duplicate notification sounds (#42) β contributed by @ductrantrong
- tmux: click-to-focus targets wrong pane β
GetTmuxPaneTarget()now reads$TMUX_PANEenv var (stable, per-process) instead oftmux display-message(returns whichever pane is active). Fixes click-to-focus switching to the wrong tmux tab when the user has navigated away (#41) - bootstrap.sh: respect
CLAUDE_CONFIG_DIRβ bootstrap now checksCLAUDE_CONFIG_DIR(official Claude Code env var) beforeCLAUDE_HOMEwhen locating the plugins directory (#43)
- Configurable suppress filters β new
suppressFiltersconfig array lets users suppress notifications matching specific conditions (status, git branch, folder name). All specified fields in a rule must match (AND logic); omitted fields act as wildcards. Contributed by @ekain-fr (#40)
- Suppress filter check before dedup lock β moved
ShouldFilter()beforeAcquireLock()in hook handler to prevent filtered events from consuming dedup lock slots and blocking subsequent legitimate notifications
- Suppress notifications for subagents β new
suppressForSubagentsconfig option (default:true) detects subagent sessions by/subagents/intranscript_pathand suppresses notifications from both Stop and SubagentStop hooks. Covers in-process subagents, teammates, and Task tool completions
- Cooldown
0was impossible to set βApplyDefaults()could not distinguish "not configured" from explicit0, silently overwriting it with12. ChangedsuppressQuestionAfterTaskCompleteSecondsandsuppressQuestionAfterAnyNotificationSecondsfromintto*intsonull= default,0= disabled (#37) permission_promptnotifications never fired βsuppressQuestionAfterAnyNotificationSecondsdefault of12was too aggressive, suppressing mid-task question notifications. Changed default to0(disabled).suppressQuestionAfterTaskCompleteSeconds(12s) remains sufficient for duplicate protection- Validate both cooldown fields β
Validate()now checkssuppressQuestionAfterAnyNotificationSecondsfor negative values (previously only checkedsuppressQuestionAfterTaskCompleteSeconds)
- Windows: remove redundant
shprefix from hook commands β Claude Code already spawns a shell for hooks; the extrashwas misinterpreted as a script filename on some Windows environments, causing "cannot execute binary file" (#35)
- Windows: "cannot execute binary file" in non-MSYS shells β added fallback Windows detection via
$OSenvironment variable (set toWindows_NTon all Windows), fixing hook failures whenuname -sdoesn't returnMINGW/MSYS/CYGWIN - Git text-symlink detection β
hook-wrapper.shnow detects whenbin/claude-notificationsis a Git text-symlink stub (plain text file containing a target path) instead of a real symlink, and resolves or invalidates it to prevent "cannot execute binary file" errors - Windows: re-detect binary after auto-install β after
run_installon Windows,hook-wrapper.shre-runsdetect_windows_binaryto prefer the freshly downloaded.exeover the.batwrapper
- Windows: "cannot execute binary file" on Stop hook β Git Bash (MINGW) cannot
exec.batfiles directly; they requirecmd.exefor interpretation. Addeddetect_windows_binaryto prefer the native.exeover the.batwrapper, andrun_windows_batto route.batexecution throughcmd.exe /c call - CI race condition β real-network E2E tests now skip gracefully when the latest release binary isn't yet available (happens when CI and Release workflow run in parallel)
- Session ID prefix in notification titles β notifications now show
[bold 06ddb8f7]instead of just[bold], making it easy to distinguish sessions even when Claude Code assigns different session IDs within the same conversation (e.g. plan mode β implementation) - Duration and actions for all notification types β question, plan, review, and session limit notifications now consistently show tool counts and elapsed time (e.g.
π 2 new β± 45s), not just task_complete
- Refactored summary generation: extracted
getActionsString()andappendActions()helpers to eliminate duplicated duration/tool-count logic across all status types
- Ghostty click-to-focus β clicking a notification focuses the correct Ghostty window via AXDocument (OSC 7 CWD file:// URL) (#34)
raiseWindowByAXDocumentC function matches windows by AXDocument attributecwdToFileURLconverts CWD to RFC-3986-compliant file:// URL vianet/url- One-time Accessibility permission prompt on first use
- Retry with backoff for AX window focus β both Ghostty and non-Ghostty paths now retry up to 3 times (150/250/400ms) instead of a single fixed sleep
- Best case: 150ms (was 800ms for Ghostty, 600ms for others)
- 3 chances to find the window instead of 1
raiseWindowByAXTitlenow checksAXIsProcessTrusted()and returns -1 on missing Accessibility permission instead of silently failing with "window not found"cwdToFileURLstrips trailing slash to avoid double-slash in file:// URLs
- Split
raiseWindowByTitleintofindSwitchAndActivate(one-shot) +raiseWindowByAXTitle(retriable) - Updated documentation: README, Click-to-Focus guide, added Plugin Compatibility doc
- Zellij click-to-focus support β clicking a notification switches to the correct zellij tab (#33)
- Detects
$ZELLIJ/$ZELLIJ_SESSION_NAMEenvironment variables - Parses active tab name from
zellij action dump-layout(KDL format) - Executes
zellij -s <session> action go-to-tab-name <tab>on notification click
- Detects
- Multiplexer registry architecture β extensible system for terminal multiplexer integrations
- Adding a new multiplexer = one file + one line in the registry
notifier.gono longer needs changes when adding multiplexer support- Priority order: tmux (first), zellij (second) β first detected wins
- E2E tests for zellij β full end-to-end tests with real zellij sessions via
creack/pty
- Refactored tmux integration β tmux click-to-focus now uses the shared multiplexer registry instead of hardcoded
if IsTmux()checks
- Window-specific click-to-focus β clicking a notification now focuses the exact project window instead of just activating the app (#31)
- macOS VS Code: CGo AXUIElement API via
focus-windowsubcommand with cross-Space support (private CGS APIs) - macOS other terminals: AppleScript title search by folder name with
exit repeatfix - Linux: folder name threaded through daemon IPC to xdotool/wlrctl/gdbus focus methods
- macOS VS Code: CGo AXUIElement API via
- Screen Recording permission prompt β one-time notification when Screen Recording access is needed, with click-to-open System Settings
SendQuickNotification()β standalone function for one-off notifications without a Notifier instance- Emoji-based compact notification format β shorter, more readable notification messages
- Window title matching β replaced substring matching with component-based matching (split by " β " / " - ") to prevent false positives (e.g., folder
appno longer matchesmy-app) - AppleScript escaping β
sanitizeForAppleScriptnow properly escapes",\, and'instead of stripping them - Double punctuation β notification messages no longer end with duplicate periods
- Timestamp-filtered summaries β task and review summaries now use only recent messages
SendDesktopsignature β now acceptscwdfor window-specific focus:SendDesktop(status, message, sessionID, cwd)
- ClaudeNotifier.app Swift rewrite β full rewrite of the macOS notifier as a Swift package with UNUserNotificationCenter, hybrid fallback to osascript, and click-to-focus via NSWorkspace
- Notification subtitle β git branch and folder name shown as native subtitle (
main Β· my-project) instead of being crammed into the title - Notification actions β "Open" and "Dismiss" buttons on long-press/swipe via UNNotificationCategory
- Thread grouping β notifications grouped by session ID via
threadIdentifier, no longer replacing each other - Time-sensitive notifications β API errors and session limits break through Focus Mode via
interruptionLevel = .timeSensitive(macOS 12+) -nosoundflag β suppresses Swift notification sound so Go audio player is the single sound source (fixes double sound)- tmux click-to-focus β clicking a notification in tmux switches to the correct window and pane via
-execute - tmux E2E tests β comprehensive integration tests for tmux pane detection, args building, and socket path extraction
- install.sh ClaudeNotifier.app support β downloads signed+notarized ClaudeNotifier.app.zip from GitHub Releases
- tmux shell quoting β tmux path and socket path properly quoted with single quotes in
-executecommands - ARC delegate lifetime β
withExtendedLifetime(appDelegate)prevents premature deallocation in callback mode - AppleScript newline escaping β newlines and carriage returns stripped from title/message/subtitle before passing to osascript
- build-app.sh codesign β
CODESIGN_FLAGSconverted from string to bash array to handle spaces in entitlements path - Flaky CI connectivity check β
SKIP_CONNECTIVITY_CHECKenv var for E2E tests to avoid flakycurl https://github.comtimeouts on macOS runners
SendDesktopsignature β now acceptssessionIDfor thread grouping:SendDesktop(status, message, sessionID)- Notification title β cleaned up to show only status emoji + session name, metadata moved to subtitle
- Config survives plugin updates β config.json moved to
~/.claude/claude-notifications-go/config.jsonoutside the plugin cache, so settings are no longer lost when bootstrap.sh clears the cache during updates (#30) - Automatic migration β existing config is auto-migrated from the old location on first run (atomic write with temp file + rename)
- Resilient fallback chain β corrupted config never crashes the plugin; falls back to legacy path or defaults with stderr warnings
- Cross-platform test isolation β
setTestHomehelper correctly sets both HOME and USERPROFILE for Windows compatibility - Lint errcheck β fixed unchecked
os.Chmodreturn value in test cleanup
- Settings wizard β now writes config to both stable and legacy paths for backward compatibility with older binary versions
- Documentation β all config path references updated across README, webhook guides, and architecture docs
- ntfy.sh webhook documentation β custom webhook example for push notifications via ntfy.sh (#29)
- Bootstrap: version symlinks β after update, creates symlinks from old version dirs to the new one so running Claude Code sessions don't break before restart
- Bootstrap: Bash 3.2 compatibility β fixed empty array handling for macOS default bash
- Updating section in README β simplified to use the same bootstrap command as installation
- Bootstrap success message β clarifies that update command is same as install
- One-command bootstrap installer β
curl -fsSL .../bootstrap.sh | bashhandles marketplace setup, plugin install, and binary download in a single command error.mp3sound β dedicated error alert sound for API errors and session limit notifications (replaces reusedquestion.mp3)
- Session names β simplified from two-word
bold-catto single-wordcatformat for cleaner notification titles - Notification title format β session ID moved after branch name:
β Completed main [cat]instead ofβ Completed [bold-cat] main - README installation section β bootstrap method is now primary, manual install moved to collapsible fallback
- Terminal bell for tab indicators π (#28, thanks @retr0h!)
- Sends BEL character (
\a) to trigger terminal tab indicators (Ghostty tab highlight, tmux window bell flag) - Works independently of desktop notifications β bell fires even when desktop notifications are disabled
- New
terminalBellconfig option (default:true) - Cross-platform:
/dev/ttyon Unix,os.Stdouton Windows - Graceful degradation: silently skipped if TTY unavailable (Docker, CI, piped environments)
- Sends BEL character (
list-soundsCLI utility β lists all available notification sound files (#23)/soundsskill command β interactive sound browser with preview from Claude Code
- Updated notification sound files for plan readiness and review completion statuses
- GNOME Terminal detection β detect GNOME Terminal via
GNOME_TERMINAL_SCREEN/GNOME_TERMINAL_SERVICEenv vars for click-to-focus support on Linux (#22, thanks @ductrantrong)
- Windows checksum verification β strip leading backslash from
sha256sumoutput on Windows (MSYS2/Git Bash/Cygwin) which caused checksum mismatch (#26)
- Unified API Error notifications β merged
api_error(401) andapi_error_overloadedinto a single API Error type in documentation - Updating section β documented auto-update mechanism via
hook-wrapper.shwith manual fallback - Release checklist β added docs/RELEASE.md with version bump locations, auto-update explanation, and full release steps
- install.sh β
install_gnome_activate_window_extension()now returns failure code when GNOME shell/extensions not found or extension couldn't be enabled (#19)
- Per-status notification control ποΈ (#16)
- Disable individual notification types (e.g., disable only
task_completewhile keeping others) - New
enabledfield in status config:"enabled": falseto disable a specific type - Backward compatible: missing
enabledfield defaults totrue - Updated
/notifications-settingswizard with Step 4.5 for selecting notification types - Example config to disable task_complete:
{ "statuses": { "task_complete": { "enabled": false, "title": "...", "sound": "..." } } }
- Disable individual notification types (e.g., disable only
- Added
Enabled *boolfield toStatusInfostruct (pointer for nil = true default) - New methods:
IsStatusEnabled(),IsStatusDesktopEnabled(),IsStatusWebhookEnabled() - Updated
sendNotifications()to check per-status enabled - 14 new tests for per-status enabled functionality
- Console notification on plugin update - shows warning message in Claude Code console when binary is installed or updated
- First install:
[claude-notifications] Installed v1.13.0 - Update:
[claude-notifications] Updated to v1.13.0 - Uses
systemMessageJSON format for Claude Code hook response
- First install:
- Added
systemMessageoutput tohook-wrapper.shfor user-visible notifications - New E2E test for systemMessage output
- Automatic version-aware updates - wrapper now checks if binary version matches plugin version
- After Claude auto-updates plugin files, binary is automatically updated on next hook call
- Compares
claude-notifications versionoutput withplugin.jsonversion - Uses
--forceflag to replace outdated binaries - Fallback: if version check fails, still works based on file existence
- Added version comparison logic to
hook-wrapper.sh - 4 new E2E tests for version checking (mismatch triggers update, match skips update, etc.)
- Auto-download after plugin auto-update - binaries now download automatically when hook is first called
- Previously: after Claude auto-updated the plugin, binary was missing and hooks failed
- Now:
hook-wrapper.shdetects missing binary and triggers download on first use - Zero downtime: if download fails, hooks exit gracefully without blocking Claude
- POSIX-compatible wrapper works on macOS, Linux, and Windows (Git Bash)
- New
bin/hook-wrapper.sh- lazy binary download wrapper - Updated
hooks/hooks.json- all hooks now use wrapper - 17 new E2E tests for hook-wrapper covering offline, mock, and real network scenarios
notifyOnTextResponseconfig option - notifications now arrive for text-only responses (e.g., extended thinking "Baked for 32s")- Default:
true(enabled) - Set to
falsein config to disable notifications when Claude responds without using tools
- Default:
- Fixed test flakiness on Go 1.25+ by cleaning up stale state files between test runs
- Windows CI tests - install.sh now tested on all 3 platforms (macOS, Linux, Windows)
- Binary execution verification - installer now verifies downloaded binary actually runs (
--versioncheck) - Network error diagnostics - detailed hints for DNS, SSL, timeout, and firewall issues
- Graceful offline mode - if GitHub is unreachable but binary exists, uses existing installation
- Cross-platform compatibility for install.sh:
- Windows-compatible
pingsyntax (-n/-winstead of-c/-W) - Portable temp directory (
${TMPDIR:-${TEMP:-/tmp}}) - Proper
.batwrapper creation on Windows - Extended regex with
grep -Efor portability
- Windows-compatible
- E2E test coverage - 35+ tests covering offline, mock server, and real network scenarios
- Utility downloads are now non-blocking - if sound-preview or list-devices fail, main install continues
- Fixed installer hanging when optional utility downloads fail
- Fixed checksum verification for cross-platform builds
- double-shot-latte compatibility π€
- Notifications are now automatically suppressed when running in background judge mode
- Detects
CLAUDE_HOOK_JUDGE_MODE=trueenvironment variable set by double-shot-latte plugin - Zero configuration required - just update the plugin and it works automatically
- Other plugin developers can use the same mechanism to suppress notifications in background Claude instances
- Added π€ Plugin Compatibility section to README
- Documented how other plugins can suppress notifications using
CLAUDE_HOOK_JUDGE_MODE=true
- Auto-update now always works -
/claude-notifications-go:notifications-initreliably updates binaries even from old cached plugins- Downloads latest
install.shdirectly from GitHub before running - Uses
--forceflag to replace existing binaries - Cross-platform temp directory (
$TMPDIR,$TEMP,/tmpfallback) - Fixes: old cached plugins used outdated installer without utility download support
- Downloads latest
- Installer now downloads utility binaries (#14)
sound-previewandlist-deviceswere missing after/claude-notifications-go:notifications-init- Installer script now downloads all three binaries
- Creates proper symlinks for all utilities
- Audio device selection support π (thanks @tkaufmann!)
- Route notification sounds to a specific audio output device
- New
audioDeviceconfig option innotifications.desktopsection - New
list-devicesCLI tool to enumerate available audio devices - New
--deviceflag forsound-previewutility
- Audio backend replacement - Replaced
oto/v3(beep/speaker) withmalgo(miniaudio bindings)- Better cross-platform audio support
- Native device enumeration and selection
- More reliable playback on all platforms
- Windows CI test failures - Fixed
.exeextension handling in cross-platform tests - Memory safety - DeviceID now properly copied instead of storing pointer to freed memory
- Player state check - Play() now returns error if player is already closed
- WaitGroup race condition - Added
closingflag to prevent race between Close() and playSoundAsync() - CI test resilience - Audio tests now skip gracefully in CI environments without audio backend
- Ghost notifications after 60 seconds π» (#11)
idle_prompthook was firing 60 seconds afterPreToolUse(AskUserQuestion)- This caused duplicate "Question" notifications with delay
- Now Notification hook only responds to
permission_prompt, ignoringidle_prompt AskUserQuestionis already covered by PreToolUse hook (instant notification)
- Proper Unicode handling for multibyte characters π (thanks @patrick-fu!)
- Text truncation now uses rune count instead of byte count
- Emoji, CJK, Cyrillic and other multibyte characters no longer get cut mid-character
truncateTextandextractFirstSentencework correctly with international text
- CI tests now pass in GitHub Actions π§ͺ
TestGetGitBranch_RealRepofailed in CI due to detached HEAD from PR checkout- Now uses isolated temporary git repository with known branch name
- Added
TestGetGitBranch_DetachedHeadto verify empty string for detached HEAD
- Fixed fallback logic in content lock π
- v1.6.2 had a bug: when lock was busy, code still proceeded without lock
- Now correctly exits when another process holds the lock
- Only uses fallback on actual errors (e.g., /tmp unavailable)
- Race condition in content-based deduplication π
- Stop and Notification hooks were running simultaneously, both passing duplicate check
- Added shared content lock to serialize duplicate check and state update
- Now only one hook can check and save notification state at a time
- Prevents duplicate notifications when different hooks fire near-simultaneously
- Version numbers no longer break sentence extraction π§
- Text like "ΠΠΈΠ½Π°ΡΠ½ΠΈΠΊ v1.6.0 ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½!" was incorrectly cut at "v1."
- Now correctly handles version numbers, decimals, IP addresses
- Dots after digits are no longer treated as sentence endings
-
Folder name in notification titles π
- Notification titles now show the project folder name
- Format:
β Completed [session-name] main my-project - Helps identify which project the notification is from
-
Content-based duplicate detection π
- Prevents duplicate notifications with similar text within 3 minutes
- Normalizes messages (ignores trailing dots, case differences)
- Example: "Completed" and "Question" with same text won't both show
- Fixes issue where different hooks sent near-identical notifications
- Git branch name in notifications πΏ
- Notification titles now show the current git branch
- Format:
β Completed [session-name] main - Only shown when working in a git repository
- Click-to-focus now works on macOS Sequoia (15.x) π―
- Removed
-senderoption that was conflicting with-activate - Trade-off: notifications no longer show custom Claude icon
- Click-to-focus now reliably activates terminal window
- Removed
- Skip terminal-notifier integration test in CI (no NotificationCenter available)
-
Click-to-focus notifications on macOS π―
- Clicking a notification activates your terminal window
- Auto-detects terminal app (Warp, iTerm, Terminal, kitty, Alacritty, etc.)
- Uses
terminal-notifierunder the hood - Enable with
"clickToFocus": truein desktop config (enabled by default) - Manual override:
"terminalBundleID": "com.your.terminal"
-
Claude icon in notifications π€ (removed in 1.4.2 due to macOS Sequoia conflict)
- Custom Claude icon displayed on the left side of macOS notifications
- Auto-creates
ClaudeNotifications.appon first notification β οΈ Removed in v1.4.2:-senderoption conflicted with click-to-focus on macOS 15.x
- Shorter notification titles
β Task Completedββ Completedπ Review Completedβπ Reviewβ Claude Has Questionsββ Questionπ Plan Ready for Reviewβπ Plan
- Terminal bundle ID detection via
__CFBundleIdentifierandTERM_PROGRAM Uses(removed in 1.4.2)-sender com.claude.notificationsfor reliable icon display
- Lark/Feishu webhook support - New webhook preset for Lark (ι£δΉ¦) notifications
- Interactive card format with colored headers based on notification status
- Supports all notification types (Task Complete, Review Complete, Question, Plan Ready)
- Wide screen mode for better readability
- Session ID included in notifications
- Add
"preset": "lark"to webhook configuration to enable
- Webhook notifications never sent (#6)
Shutdown()now waits for in-flight HTTP requests to complete before exit- Added
defer webhookSvc.Shutdown(5s)toHandleHook()for graceful shutdown - Previously:
cancel()was called immediately, interrupting HTTP requests - Now:
cancel()is only called after completion or on timeout
- E2E test
TestE2E_WebhookGracefulShutdown- deterministic graceful shutdown verification - Unit tests for
Shutdown()+SendAsync()combination - Updated
webhookInterfaceto includeShutdown(timeout)method
- Subagent notification control - New config option
notifyOnSubagentStop- Prevents premature "Completed" notifications when Task agents (subagents) finish
- Main Claude session continues working without distracting notifications
- Default:
false(notifications disabled for subagents) - Users can enable via
"notifyOnSubagentStop": truein config if desired - Fixes issue where Plan/Explore agents triggered completion notifications while Claude was still thinking
- SubagentStop hook now checks config before sending notifications
- Split SubagentStop and Stop hook handling for better control
- Added
NotifyOnSubagentStopboolean field toNotificationsConfigstruct - Updated hook handler in
internal/hooks/hooks.goto respect config setting - Added comprehensive tests for both enabled and disabled states
- All existing tests pass with new functionality
- Volume control on macOS π
- Replaced
effects.Volumewitheffects.Gainfor reliable volume control - Volume settings (e.g., 30%) now work correctly on macOS
- Simplified volume conversion logic (linear instead of logarithmic)
- Affects both notification sounds and
sound-previewutility - All tests passing with new implementation
- Replaced
- GitHub Actions build step - Windows builds now work correctly
- Added
shell: bashto build step for all platforms - Resolved PowerShell syntax error preventing Windows builds from completing
- Added
- Simplified
volumeToGain()function - removed complex logarithmic calculations - Updated documentation in code to reflect linear gain formula:
output = input * (1 + Gain)
- Missing sound-preview binary - fixes
/notifications-settingssound preview- Added
sound-previewutility to build system - Now built for all platforms (darwin, linux, windows)
- Included in GitHub Releases
- Supports interactive sound preview during settings configuration
- Handles MP3, WAV, FLAC, OGG, AIFF formats
- Added
- New notification type: API Error 401 π΄
- Detects authentication errors when OAuth token expires
- Shows "π΄ API Error: 401" with message "Please run /login"
- Triggered when both "API Error: 401" and "Please run /login" appear in assistant messages
- Priority detection (checks before tool-based detection)
- Added comprehensive tests for API error detection
- Binary size optimization - 30% smaller release binaries
- Production builds now use
-ldflags="-s -w" -trimpathflags - Binary size reduced from ~10 MB to ~7 MB per platform
- Faster download times for users (5 seconds instead of 8 seconds)
- Better privacy (no developer paths in binaries)
- Deterministic builds across different machines
- Development builds unchanged (still include debug symbols)
- Production builds now use
- Updated notification count from 5 to 6 types in README
- All tests passing with new features
- Critical bug in duration calculation ("Took" time in notifications)
- User text messages were not being detected in transcript parsing
GetLastUserTimestampnow correctly parses string content format- Duration now shows accurate time (e.g., "Took 5m" instead of "Took 2h 30m")
- Tool counting now accurate (prevents showing inflated counts like "Edited 32 files")
- Added custom JSON marshaling/unmarshaling for
MessageContentto handle both string and array content formats
- Fixed
pkg/jsonl/jsonl.go: AddedContentStringfield and customUnmarshalJSON/MarshalJSONmethods - User messages with
"content": "text"format now properly parsed (previously only array format worked) - All existing tests pass + added new tests for string content parsing
1.0.2 - 2025-10-23
- Linux ARM64 support for Raspberry Pi and other ARM64 Linux systems (#2)
- Native ARM64 runner (
ubuntu-24.04-arm) for reliable builds - Full audio and notification support via CGO
- Automatic binary download via
/claude-notifications-go:notifications-initcommand
- Native ARM64 runner (
- Webhook configuration validation now only runs when webhooks are enabled (#1)
- Previously caused "invalid webhook preset: none" error even with webhooks disabled
- Preset and format validation now conditional on
webhook.enabledflag
- Documentation updates for clarity and platform-specific instructions
1.0.1 - 2025-10-22
- Windows ARM64 binary support
- Windows CMD and PowerShell compatibility improvements
- Plugin installation and hook integration issues
- Plugin manifest command paths
- POSIX-compliant OS detection for better cross-platform support
1.0.0 - 2025-10-20
- Initial release of Claude Notifications plugin
- Cross-platform desktop notifications (macOS, Linux, Windows)
- Smart notification system with 5 types:
- Task Complete
- Review Complete
- Question
- Plan Ready
- Session Limit Reached
- State machine analysis for accurate notification detection
- Webhook integrations (Slack, Discord, Telegram, Custom)
- Enterprise-grade webhook features:
- Retry logic with exponential backoff
- Circuit breaker for fault tolerance
- Rate limiting with token bucket algorithm
- Audio notification support (MP3, WAV, FLAC, OGG, AIFF)
- Volume control (0-100%)
- Interactive setup wizards
- Two-phase lock deduplication system
- Friendly session names
- Pre-built binaries for all platforms
- GitHub Releases distribution
- Error handling improvements across webhook and notifier packages
- Data race in error handler
- Question notification cooldown system
- Cross-platform path normalization