Skip to content

Commit 98f6934

Browse files
ChanMeng666claude
andcommitted
feat: snooze / temporary mute for audio hooks (v4.4.0)
Add marker-file based snooze system that temporarily silences all audio hooks for a specified duration with automatic resumption. Closes #7. - New scripts/snooze.sh standalone CLI (snooze, status, resume) - is_snoozed() check in hook_runner.py and hook_config.sh - --snooze/--resume/--snooze-status flags in configure.sh - Inline snooze commands in quick-configure.sh (curl | bash) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5b7b3c0 commit 98f6934

File tree

8 files changed

+478
-3
lines changed

8 files changed

+478
-3
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ All notable changes to Claude Code Audio Hooks will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [4.4.0] - 2026-03-13
9+
10+
### Added
11+
- **Snooze / Temporary Mute** (closes #7): Temporarily silence all audio hooks for a specified duration with automatic resumption
12+
- New `scripts/snooze.sh` standalone CLI: `bash scripts/snooze.sh 1h` to snooze, `status` to check, `off` to resume
13+
- Marker-file based design — no daemon or cleanup needed; hooks self-expire
14+
- Accepts flexible duration formats: `30m`, `1h`, `2h`, `90m`, bare numbers (minutes), `30s`
15+
- `--snooze`, `--resume`, `--snooze-status` flags added to `scripts/configure.sh`
16+
- `--snooze`, `--resume`, `--snooze-status` flags added to `scripts/quick-configure.sh` (inline, works via `curl | bash`)
17+
- Snooze check integrated into both `hooks/hook_runner.py` (Python) and `hooks/shared/hook_config.sh` (Bash)
18+
- Debug logging: snoozed hooks log "SNOOZED" with remaining time
19+
20+
### Changed
21+
- `hook_runner.py` version bumped to 4.4.0
22+
23+
---
24+
825
## [4.3.1] - 2026-02-17
926

1027
### Added

CLAUDE.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Claude Code Audio Hooks - AI Assistant Guide
22

3-
> **Version:** 4.3.1 | **Last Updated:** 2026-02-17
3+
> **Version:** 4.4.0 | **Last Updated:** 2026-03-13
44
55
This document is designed for AI assistants (Claude Code, Cursor, Copilot, etc.) to understand and help users install this project correctly.
66

@@ -40,6 +40,16 @@ Gives: desktop notifications + system sounds for Stop, Notification, SubagentSto
4040

4141
Gives: custom MP3 audio + desktop notifications + context-aware alerts + TTS (optional).
4242

43+
### Snooze / Temporary Mute
44+
```bash
45+
bash scripts/snooze.sh # Snooze 30m (default)
46+
bash scripts/snooze.sh 1h # Snooze 1 hour
47+
bash scripts/snooze.sh status # Show remaining time
48+
bash scripts/snooze.sh off # Resume immediately
49+
bash scripts/configure.sh --snooze 1h # Via configure.sh
50+
bash scripts/configure.sh --resume # Cancel snooze
51+
```
52+
4353
## Platform Detection Decision Tree
4454

4555
Use this decision tree to determine the correct installation method:
@@ -106,12 +116,13 @@ claude-code-audio-hooks/
106116
107117
├── scripts/
108118
│ ├── quick-setup.sh # Lite tier installer (zero deps, curl | bash)
109-
│ ├── quick-configure.sh # Lite tier hook manager (enable/disable/list)
119+
│ ├── quick-configure.sh # Lite tier hook manager (enable/disable/list/snooze)
110120
│ ├── quick-unsetup.sh # Lite tier uninstaller
111121
│ ├── install-complete.sh # Full installer (all platforms)
112122
│ ├── install-windows.ps1 # PowerShell installer (Windows)
113123
│ ├── uninstall.sh # Uninstaller
114124
│ ├── configure.sh # Configuration utility
125+
│ ├── snooze.sh # Snooze / temporary mute tool
115126
│ ├── test-audio.sh # Audio testing tool
116127
│ └── diagnose.py # Diagnostic tool
117128
@@ -400,6 +411,7 @@ Instruct user to:
400411

401412
| Version | Date | Key Changes |
402413
|---------|------|-------------|
414+
| 4.4.0 | 2026-03-13 | Snooze / temporary mute: `snooze.sh` CLI, marker-file based, auto-resume; `--snooze`/`--resume` in configure.sh and quick-configure.sh (closes #7) |
403415
| 4.3.1 | 2026-02-17 | Quick Setup customization: new `quick-configure.sh` for enabling/disabling individual hooks without cloning; fix `quick-unsetup.sh` missing PermissionRequest |
404416
| 4.3.0 | 2026-02-17 | Per-hook notification mode overrides: independently control audio/desktop notifications per hook type via `notification_settings.per_hook` |
405417
| 4.2.2 | 2026-02-14 | Robust theme switching: remove conflicting `audio_files` config, add hook_runner.py auto-sync from project dir, configure.sh syncs on theme switch |

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,9 @@ Already using Claude Code? Just tell it what you want in plain language:
633633
| **Audio only, no popups** | *"Turn off all desktop notification popups, keep audio only"* |
634634
| **Popups only, no audio** | *"Switch to desktop notifications only, disable all audio"* |
635635
| **Audio + popups for critical hooks** | *"Set global mode to audio_only, but enable audio + desktop popup for stop, notification, and permission_request hooks"* |
636+
| **Snooze for a meeting** | *"Snooze all audio hooks for 1 hour"* |
637+
| **Check snooze status** | *"Are my audio hooks snoozed right now?"* |
638+
| **Resume after snooze** | *"Cancel the snooze and resume audio hooks"* |
636639
| **Silence noisy hooks** | *"Set pretooluse and posttooluse to disabled mode so they don't play audio or show popups"* |
637640
| **Mixed per-hook setup** | *"I want audio for all hooks, but also desktop popups only for task completion and authorization requests"* |
638641

@@ -751,6 +754,38 @@ bash scripts/configure.sh --help
751754

752755
---
753756

757+
#### **Snooze / Temporary Mute**
758+
759+
Need silence during a meeting or focus session? Snooze all hooks temporarily — they auto-resume when the timer expires.
760+
761+
**Standalone script:**
762+
```bash
763+
bash scripts/snooze.sh # Snooze 30 minutes (default)
764+
bash scripts/snooze.sh 1h # Snooze 1 hour
765+
bash scripts/snooze.sh 2h # Snooze 2 hours
766+
bash scripts/snooze.sh 90m # Snooze 90 minutes
767+
bash scripts/snooze.sh status # Check remaining time
768+
bash scripts/snooze.sh off # Resume immediately
769+
```
770+
771+
**Via configure.sh:**
772+
```bash
773+
bash scripts/configure.sh --snooze 1h
774+
bash scripts/configure.sh --snooze-status
775+
bash scripts/configure.sh --resume
776+
```
777+
778+
**Via quick-configure.sh (Lite tier, no clone needed):**
779+
```bash
780+
curl -sL ...quick-configure.sh | bash -s -- --snooze 1h
781+
curl -sL ...quick-configure.sh | bash -s -- --snooze-status
782+
curl -sL ...quick-configure.sh | bash -s -- --resume
783+
```
784+
785+
**How it works:** A timestamp marker file is written to the temp directory. All hook runners check this file before playing audio. When the timestamp is in the past, hooks automatically resume — no cleanup daemon needed.
786+
787+
---
788+
754789
### **Manual Configuration**
755790

756791
Edit `config/user_preferences.json`:

hooks/hook_runner.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
# Version used for auto-sync: when the installed copy in ~/.claude/hooks/
3131
# detects a newer version in the project directory, it self-updates.
32-
HOOK_RUNNER_VERSION = "4.3.0"
32+
HOOK_RUNNER_VERSION = "4.4.0"
3333

3434
# =============================================================================
3535
# DEBUG LOGGING SYSTEM
@@ -383,6 +383,25 @@ def is_hook_enabled(hook_type: str) -> bool:
383383
return result
384384

385385

386+
def is_snoozed() -> bool:
387+
"""Check if hooks are temporarily snoozed via marker file."""
388+
snooze_file = QUEUE_DIR / "snooze_until"
389+
if not snooze_file.exists():
390+
return False
391+
try:
392+
snooze_until = float(snooze_file.read_text(encoding="utf-8").strip())
393+
if time.time() < snooze_until:
394+
remaining = snooze_until - time.time()
395+
log_debug(f"Snoozed: {remaining:.0f}s remaining")
396+
return True
397+
else:
398+
log_debug("Snooze expired")
399+
return False
400+
except (ValueError, OSError) as e:
401+
log_debug(f"Error reading snooze file: {e}")
402+
return False
403+
404+
386405
CUSTOM_AUDIO_FILES = {
387406
"notification": "chime-notification-urgent.mp3",
388407
"stop": "chime-task-complete.mp3",
@@ -1020,6 +1039,11 @@ def run_hook(hook_type: str, stdin_data: dict = None) -> int:
10201039
log_trigger(hook_type, "DISABLED")
10211040
return 0
10221041

1042+
# Check if snoozed
1043+
if is_snoozed():
1044+
log_trigger(hook_type, "SNOOZED")
1045+
return 0
1046+
10231047
# Check debounce
10241048
if should_debounce(hook_type):
10251049
log_trigger(hook_type, "DEBOUNCED")

hooks/shared/hook_config.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,16 @@ EOF
216216
[ "$enabled" = "true" ]
217217
}
218218

219+
# Check if hooks are temporarily snoozed via marker file
220+
is_snoozed() {
221+
local snooze_file="$QUEUE_DIR/snooze_until"
222+
[ -f "$snooze_file" ] || return 1
223+
local snooze_until current_time
224+
snooze_until=$(cat "$snooze_file" 2>/dev/null) || return 1
225+
current_time=$(date +%s)
226+
[ "$current_time" -lt "${snooze_until%%.*}" ] 2>/dev/null
227+
}
228+
219229
# Get audio file path for a hook type
220230
get_audio_file() {
221231
local hook_type="$1"
@@ -549,6 +559,12 @@ get_and_play_audio() {
549559
exit 0 # Hook disabled, exit silently
550560
fi
551561

562+
# Check if snoozed
563+
if is_snoozed; then
564+
log_debug "Hook $hook_type snoozed, skipping"
565+
exit 0
566+
fi
567+
552568
# Check debounce (prevent rapid-fire notifications)
553569
if should_debounce "$hook_type"; then
554570
exit 0 # Debounced, exit silently
@@ -625,6 +641,7 @@ cleanup_hooks() {
625641
# =============================================================================
626642

627643
# Export functions for use in hook scripts
644+
export -f is_snoozed
628645
export -f is_hook_enabled
629646
export -f get_audio_file
630647
export -f play_audio_internal

scripts/configure.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,16 @@ ${CYAN}PROGRAMMATIC MODE${NC} (with arguments):
617617
Reset to recommended defaults
618618
(Enables: notification, stop, subagent_stop, permission_request; Disables: all others)
619619
620+
${BOLD}--snooze [DURATION]${NC}
621+
Temporarily mute all audio hooks (default: 30m)
622+
Examples: --snooze 30m, --snooze 1h, --snooze 2h
623+
624+
${BOLD}--resume${NC}
625+
Cancel snooze early, resume all hooks
626+
627+
${BOLD}--snooze-status${NC}
628+
Show current snooze status
629+
620630
${BOLD}--apply${NC}
621631
Save configuration without prompting
622632
(Auto-applied after --enable, --disable, --set, --reset)
@@ -927,6 +937,19 @@ process_arguments() {
927937
cmd_hook_mode "${hook_modes[@]}"
928938
exit 0
929939
;;
940+
--snooze)
941+
shift
942+
bash "$PROJECT_DIR/scripts/snooze.sh" "${1:-30m}"
943+
exit 0
944+
;;
945+
--resume|--unsnooze)
946+
bash "$PROJECT_DIR/scripts/snooze.sh" resume
947+
exit 0
948+
;;
949+
--snooze-status)
950+
bash "$PROJECT_DIR/scripts/snooze.sh" status
951+
exit 0
952+
;;
930953
--reset)
931954
cmd_reset
932955
exit 0

scripts/quick-configure.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ error() { printf "${RED}[ERROR]${NC} %s\n" "$1"; }
4040
# All Quick Setup hooks
4141
ALL_HOOKS=("Stop" "Notification" "SubagentStop" "PermissionRequest")
4242

43+
# Cross-platform temp/queue directory (must match hook_runner.py / hook_config.sh)
44+
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "mingw"* ]] || [[ "$OSTYPE" == "cygwin" ]]; then
45+
QUEUE_DIR="${TEMP:-${TMP:-/tmp}}/claude_audio_hooks_queue"
46+
else
47+
QUEUE_DIR="/tmp/claude_audio_hooks_queue"
48+
fi
49+
SNOOZE_FILE="$QUEUE_DIR/snooze_until"
50+
4351
# =============================================================================
4452
# PLATFORM DETECTION (same as quick-setup.sh)
4553
# =============================================================================
@@ -474,6 +482,9 @@ show_usage() {
474482
printf " quick-configure.sh --enable <Hook> [Hook...] Enable hooks\n"
475483
printf " quick-configure.sh --disable <Hook> [Hook...] Disable hooks\n"
476484
printf " quick-configure.sh --only <Hook> [Hook...] Keep only these hooks\n"
485+
printf " quick-configure.sh --snooze [DURATION] Temporarily mute all hooks (default: 30m)\n"
486+
printf " quick-configure.sh --resume Cancel snooze, resume hooks\n"
487+
printf " quick-configure.sh --snooze-status Show snooze status\n"
477488
printf " quick-configure.sh --help Show this help\n"
478489
printf "\n${BOLD}Available hooks:${NC}\n"
479490
printf " Stop - Sound + notification when tasks complete\n"
@@ -487,6 +498,10 @@ show_usage() {
487498
printf " quick-configure.sh --only Stop Notification\n\n"
488499
printf " # Re-enable everything:\n"
489500
printf " quick-configure.sh --enable Stop Notification SubagentStop PermissionRequest\n\n"
501+
printf " # Snooze all hooks for 1 hour:\n"
502+
printf " quick-configure.sh --snooze 1h\n\n"
503+
printf " # Check snooze status:\n"
504+
printf " quick-configure.sh --snooze-status\n\n"
490505
printf "${BOLD}Remote usage (no clone needed):${NC}\n"
491506
printf " curl -sL https://raw.githubusercontent.com/ChanMeng666/claude-code-audio-hooks/master/scripts/quick-configure.sh | bash -s -- --list\n"
492507
printf "\n"
@@ -548,6 +563,22 @@ main() {
548563
shift
549564
done
550565
;;
566+
--snooze|-s)
567+
action="snooze"
568+
shift
569+
if [ $# -gt 0 ] && [[ ! "$1" =~ ^-- ]]; then
570+
hook_args+=("$1")
571+
shift
572+
fi
573+
;;
574+
--resume|--unsnooze)
575+
action="resume"
576+
shift
577+
;;
578+
--snooze-status)
579+
action="snooze-status"
580+
shift
581+
;;
551582
--help|-h)
552583
show_usage
553584
exit 0
@@ -656,6 +687,81 @@ main() {
656687
printf "\n${BOLD}Restart Claude Code for changes to take effect.${NC}\n"
657688
;;
658689

690+
snooze)
691+
# Inline snooze (self-contained, no external script needed)
692+
local duration_str="${hook_args[0]:-30m}"
693+
local duration_seconds=0
694+
695+
if [[ "$duration_str" =~ ^([0-9]+)h$ ]]; then
696+
duration_seconds=$(( ${BASH_REMATCH[1]} * 3600 ))
697+
elif [[ "$duration_str" =~ ^([0-9]+)m$ ]]; then
698+
duration_seconds=$(( ${BASH_REMATCH[1]} * 60 ))
699+
elif [[ "$duration_str" =~ ^([0-9]+)s$ ]]; then
700+
duration_seconds=$(( ${BASH_REMATCH[1]} ))
701+
elif [[ "$duration_str" =~ ^([0-9]+)$ ]]; then
702+
duration_seconds=$(( ${BASH_REMATCH[1]} * 60 ))
703+
else
704+
error "Invalid duration format '$duration_str'. Examples: 30m, 1h, 2h, 90m"
705+
exit 1
706+
fi
707+
708+
if [ "$duration_seconds" -le 0 ]; then
709+
error "Duration must be greater than 0"
710+
exit 1
711+
fi
712+
713+
if [ "$duration_seconds" -gt 86400 ]; then
714+
warn "Snoozing for more than 24 hours. Consider disabling hooks instead."
715+
fi
716+
717+
mkdir -p "$QUEUE_DIR" 2>/dev/null
718+
local snooze_until=$(( $(date +%s) + duration_seconds ))
719+
echo "$snooze_until" > "$SNOOZE_FILE"
720+
721+
success "Snoozed! All audio hooks muted for $duration_str"
722+
printf "Hooks will auto-resume at %s\n" "$(date -d "@$snooze_until" 2>/dev/null || date -r "$snooze_until" 2>/dev/null || echo "epoch $snooze_until")"
723+
printf "\nTo cancel: quick-configure.sh --resume\n"
724+
;;
725+
726+
resume)
727+
if [ -f "$SNOOZE_FILE" ]; then
728+
rm -f "$SNOOZE_FILE"
729+
success "Resumed! Audio hooks are active again."
730+
else
731+
info "Not snoozed. Audio hooks are already active."
732+
fi
733+
;;
734+
735+
snooze-status)
736+
if [ ! -f "$SNOOZE_FILE" ]; then
737+
info "Not snoozed — audio hooks are active."
738+
else
739+
local snooze_until
740+
snooze_until="$(cat "$SNOOZE_FILE" 2>/dev/null)" || {
741+
info "Not snoozed — audio hooks are active."
742+
exit 0
743+
}
744+
snooze_until="${snooze_until%%.*}"
745+
local current_time
746+
current_time="$(date +%s)"
747+
if [ "$current_time" -lt "$snooze_until" ] 2>/dev/null; then
748+
local remaining=$(( snooze_until - current_time ))
749+
local hours=$(( remaining / 3600 ))
750+
local minutes=$(( (remaining % 3600) / 60 ))
751+
local friendly=""
752+
if [ "$hours" -gt 0 ]; then friendly="${hours}h "; fi
753+
if [ "$minutes" -gt 0 ]; then friendly="${friendly}${minutes}m"; fi
754+
if [ -z "$friendly" ]; then friendly="${remaining}s"; fi
755+
warn "Snoozed — ~${friendly} remaining"
756+
printf "Resumes at %s\n" "$(date -d "@$snooze_until" 2>/dev/null || date -r "$snooze_until" 2>/dev/null || echo "epoch $snooze_until")"
757+
printf "\nTo cancel: quick-configure.sh --resume\n"
758+
else
759+
info "Snooze expired — audio hooks are active."
760+
rm -f "$SNOOZE_FILE" 2>/dev/null
761+
fi
762+
fi
763+
;;
764+
659765
*)
660766
show_usage
661767
exit 0

0 commit comments

Comments
 (0)