claude-alerter
v1.1.1
Published
Cross-platform notifier for Claude Code hooks: sound and optional haptic alerts that repeat until the terminal is focused.
Maintainers
Readme
claude-alerter
Cross-platform sound notifier for Claude Code hooks. Plays a sound — on repeat until your terminal is focused — when Claude finishes a turn or needs your input. Works on macOS, Windows, and Linux.
- Configurable events — map any Claude hook event (
Stop,Notification,SubagentStop,SessionEnd, …) to a sound. - Configurable sounds — bundled WAV chimes, or point at any
.wav. - Idempotent installer — merges only its own hooks into
settings.json, backs the file up first, and never touches your other hooks.
Requirements
- Node.js (used both to install and at hook runtime).
- A system audio player:
- macOS:
afplay(built in — nothing to install). - Windows: PowerShell
Media.SoundPlayer(built in — nothing to install). - Linux: one of
paplay,aplay,ffplay, orplay(sox). For the loop to stop on focus under X11, alsoxdotool+xprop.
- macOS:
Run claude-alerter doctor (or node dist/src/cli.js doctor) to check what
your OS has and, on Linux, print the exact install command for your distro
(apt/dnf/pacman/zypper/apk). install prints the same report.
Install
From npm (no clone needed):
npx claude-alerter installOr install globally and run the claude-alerter command directly:
npm install -g claude-alerter
claude-alerter installFrom source:
git clone https://github.com/cyberash-dev/claude-alerter.git
cd claude-alerter
npm install
npm run build
node dist/src/cli.js installThe installer:
- copies the compiled notifier +
sounds/to~/.claude/notifier/, - creates
~/.claude/notifier/config.json(fromconfig.example.json) if it does not already exist, - backs up
~/.claude/settings.jsontosettings.json.bak-<timestamp>, - merges the hooks for every enabled event plus a
UserPromptSubmitstop hook.
Then open /hooks once in Claude Code (or restart it) so the new hooks load.
Preview without writing anything:
node dist/src/cli.js install --dry-runConfigure
Edit ~/.claude/notifier/config.json, then re-apply:
node ~/.claude/notifier/cli.js apply{
"terminals": ["iTerm2", "WindowsTerminal", "gnome-terminal", "..."],
"default_interval": 15,
"max_repeats": 20,
"events": {
"Stop": { "enabled": true, "sound": "done.wav", "interval": 15, "haptic": false },
"Notification": { "enabled": true, "sound": "question.wav", "interval": 15, "haptic": false },
"SubagentStop": { "enabled": false, "sound": "tick.wav" }
}
}terminals— substrings matched (case-insensitive) against the focused app/window name. When a match is focused, the loop stops.default_interval— seconds between repeats when an event omitsinterval.max_repeats— stop after this many plays even if focus is never detected (0= unlimited). Acts as a cap on Linux/Wayland where focus can't be read.events.<HookEvent>—enabled,sound(a name undersounds/or an absolute path), optionalinterval, and optionalhaptic(see below). Add any Claude hook event key here.hapticis controlled by the--logitech-hapticinstall flag, not by hand — eachinstalloverwrites it. Setsoundto""for a haptic-only event (no sound plays). An enabled event must have either a non-emptysoundorhaptic— an enabled event with neither is rejected as a no-op.
Disable an event ("enabled": false) and run apply to drop its hook;
re-enable and apply to add it back. Re-running is always safe — no duplicates.
How it stops
- If your terminal is already focused when the event fires, the sound plays once — no loop.
- Otherwise it repeats until you return to the terminal. Focus is polled
once per second, so the sound stops within ~1s of you switching back —
independent of the (longer) repeat
interval. - A
UserPromptSubmithook also callsstopas a backstop. This never stops the loop early (submitting a prompt means you are already at the terminal); it mainly covers Linux/Wayland where focus can't be read, alongsidemax_repeats.
Haptic notifications (Logitech MX Master 4)
Optional second channel: a tactile pulse on a Logitech MX Master 4 when an event
fires, alongside the sound. It is driven by a companion Logi Options+ plugin in
ClaudeHapticPlugin/ and is off unless you opt in.
How it works: when haptic is enabled for an event, the notifier drops a one-shot
trigger file into ~/.claude/notifier/haptic/. The plugin watches that
directory and raises a haptic event, which Logi Options+ maps to a waveform. The
two sides are fully decoupled — if the plugin is not installed, the trigger file
is simply ignored and the sound channel is unaffected. Alongside a sound the pulse
fires once at event time and does not loop (looping is the sound channel's
job). For a haptic-only event (sound: "") there is no sound channel, so the
pulse itself loops at interval until you return to the terminal.
Requirements:
- A Logitech MX Master 4 and Logi Options+ installed and running (the only device the Actions SDK exposes haptics for).
- To build the plugin: the .NET SDK and LogiPluginTool
(
dotnet tool install --global LogiPluginTool).
Enable it:
Build and load the plugin (dev build links it into Logi Plugin Service and triggers a reload):
cd ClaudeHapticPlugin dotnet build src/ClaudeHapticPlugin.csproj -c ReleaseFor a portable install independent of the repo path, package and install it:
LogiPluginTool pack ./bin/Release ./ClaudeHaptic.lplug4 LogiPluginTool install ./ClaudeHaptic.lplug4Turn the haptic channel on by installing with the flag:
node dist/src/cli.js install --logitech-hapticWhen
dotnetand the plugin sources are present (a from-source checkout), this builds and links the plugin for you (installingLogiPluginToolfirst if needed); otherwise it prints the manual build steps above. On Linux it is a no-op (no Logi SDK). The flag setshapticon every event in~/.claude/notifier/config.json. Haptic state always mirrors the flag: re-runninginstallwithout--logitech-hapticdisables it again. (Hand-edits tohapticare reset on the nextinstall; the sound channel is unaffected.)Verify:
node ~/.claude/notifier/cli.js test Stopshould buzz the mouse (and play the sound). Each event plays a short haptic pattern (a tuned sequence of waveforms — e.g. Stop iswave → happy_alert), defined on the plugin side inPatternsinClaudeHapticPlugin/src/ClaudeHapticPlugin.cswith the waveforms declared inClaudeHapticPlugin/src/package/events/extra/eventMapping.yaml. Edit those to change the melodies.
Commands
node dist/src/cli.js install [--dry-run] [--config <path>] [--logitech-haptic]
node ~/.claude/notifier/cli.js apply [--dry-run]
node ~/.claude/notifier/cli.js uninstall [--dry-run]
node ~/.claude/notifier/cli.js doctor # check per-OS deps
node ~/.claude/notifier/cli.js test <Event> # play a sound oncedoctor reports the audio player and focus-detection status for your OS and, if
something is missing on Linux, the install command for your distro. It exits
non-zero when no audio player is available.
uninstall removes the hooks (after a backup) and deletes
~/.claude/notifier/.
Sounds
sounds/*.wav are generated by tools/gen-sounds.ts (pure Node, no deps):
npm run gen-soundsWAV is used because it plays natively on all three platforms without extra
codecs. Replace them or point sound at your own .wav.
Platform notes
- macOS — focus via AppleScript (
System Events). The first run may prompt for Automation permission for your terminal; approve it once. - Windows — focus via
GetForegroundWindow; hooks invokenode …\cli.js. PowerShellMedia.SoundPlayerplays WAV only. - Linux — focus via
xdotool/xpropon X11. On Wayland focus can't be read, so the loop relies onmax_repeatsand the prompt-submit stop.
