Observed behavior
After Claude Code finishes a turn, peon-ping plays the task.complete sound once (correct), but then keeps re-playing it roughly every 60 seconds until I focus the terminal. I want exactly one sound per completion.
Root cause (from reading peon.sh)
The default install binds peon-ping to Claude Code's Notification hook. Claude Code re-fires this hook with notification_type = "idle_prompt" every ~60s while the terminal is unfocused and Claude is awaiting user input. In peon.sh around lines 4260-4270, idle_prompt is routed to category task.complete and a sound is played on every fire:
elif ntype == 'idle_prompt':
category = 'task.complete'
...
notify = '1'
Because the Stop hook also routes to task.complete (once), users end up with: one sound from Stop + an unbounded repeat loop from idle_prompt firings.
Workaround
Remove the Notification hook binding for peon.sh from ~/.claude/settings.json. Keeps Stop (one-shot completion sound) and PermissionRequest (separate hook) working, loses sound on rare elicitation_dialog events.
Questions
- Is the current behavior intentional, or should
idle_prompt be deduplicated (e.g., play at most once per idle state, or skipped if a Stop-triggered task.complete already fired in the last N seconds)?
- Could the default installer skip binding
Notification for Claude Code, or add a config flag like suppress_idle_prompt_repeats: true?
- Are
categories.task.complete users expected to differentiate Stop-fired completion from idle_prompt-fired completion? Those feel like semantically different events.
Environment
- peon-ping 2.20.0
- macOS 26.3.1 (Tahoe)
- Claude Code
Observed behavior
After Claude Code finishes a turn, peon-ping plays the
task.completesound once (correct), but then keeps re-playing it roughly every 60 seconds until I focus the terminal. I want exactly one sound per completion.Root cause (from reading peon.sh)
The default install binds peon-ping to Claude Code's
Notificationhook. Claude Code re-fires this hook withnotification_type = "idle_prompt"every ~60s while the terminal is unfocused and Claude is awaiting user input. Inpeon.sharound lines 4260-4270,idle_promptis routed to categorytask.completeand a sound is played on every fire:Because the
Stophook also routes totask.complete(once), users end up with: one sound from Stop + an unbounded repeat loop from idle_prompt firings.Workaround
Remove the
Notificationhook binding forpeon.shfrom~/.claude/settings.json. Keeps Stop (one-shot completion sound) and PermissionRequest (separate hook) working, loses sound on rareelicitation_dialogevents.Questions
idle_promptbe deduplicated (e.g., play at most once per idle state, or skipped if a Stop-triggeredtask.completealready fired in the last N seconds)?Notificationfor Claude Code, or add a config flag likesuppress_idle_prompt_repeats: true?categories.task.completeusers expected to differentiate Stop-fired completion from idle_prompt-fired completion? Those feel like semantically different events.Environment