claude-code-sounds 1.5.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,18 +39,6 @@ The bash installer requires `jq` (`brew install jq`).
39
39
 
40
40
  </details>
41
41
 
42
- ## Usage
43
-
44
- ```bash
45
- npx claude-code-sounds # Interactive install
46
- npx claude-code-sounds --theme portal # Install a specific theme directly
47
- npx claude-code-sounds --mix # Jump to sound assignment grid
48
- npx claude-code-sounds --yes # Install defaults, skip all prompts
49
- npx claude-code-sounds --list # List available themes
50
- npx claude-code-sounds --uninstall # Remove all sounds and hooks
51
- npx claude-code-sounds --help # Show help
52
- ```
53
-
54
42
  ## Themes
55
43
 
56
44
  | Theme | Sounds | Vibe |
@@ -73,7 +61,78 @@ npx claude-code-sounds --help # Show help
73
61
 
74
62
  Each theme maps sounds across all 11 Claude Code lifecycle events.
75
63
 
76
- ## Hook Events
64
+ ## Usage
65
+
66
+ ```bash
67
+ npx claude-code-sounds # Interactive install
68
+ npx claude-code-sounds --theme portal # Install a specific theme directly
69
+ npx claude-code-sounds --mix # Jump to sound assignment grid
70
+ npx claude-code-sounds --yes # Install defaults, skip all prompts
71
+ npx claude-code-sounds --list # List available themes
72
+ npx claude-code-sounds --mute # Mute all sounds
73
+ npx claude-code-sounds --unmute # Unmute all sounds
74
+ npx claude-code-sounds --dnd # Auto-mute when in video calls
75
+ npx claude-code-sounds --no-dnd # Disable auto-mute
76
+ npx claude-code-sounds --uninstall # Remove all sounds and hooks
77
+ npx claude-code-sounds --help # Show help
78
+ ```
79
+
80
+ ## Muting
81
+
82
+ Mute sounds without uninstalling — three ways:
83
+
84
+ - **Slash command** (inside Claude Code): type `/mute` or `/unmute`
85
+ - **CLI flag**: `npx claude-code-sounds --mute` or `--unmute`
86
+ - **Interactive menu**: run `npx claude-code-sounds` and select "Mute sounds" / "Unmute sounds"
87
+
88
+ Muting creates a sentinel file at `~/.claude/sounds/.muted`. The hook script checks for it and exits immediately, so there's zero overhead when muted.
89
+
90
+ ### Do Not Disturb
91
+
92
+ Sounds are automatically muted when active video calls are detected (Zoom, FaceTime, Webex). This is enabled by default.
93
+
94
+ Edit `~/.claude/sounds/.dnd` to add or remove app names — one process name per line, `#` for comments.
95
+
96
+ To disable: `npx claude-code-sounds --no-dnd` or use the interactive menu. Re-enable with `--dnd`.
97
+
98
+ ## Customizing
99
+
100
+ Re-run with `--mix` to open the sound assignment grid, where you can reassign sounds to hooks, add themes, or preview clips:
101
+
102
+ ```bash
103
+ npx claude-code-sounds --mix
104
+ ```
105
+
106
+ ![Sound assignment grid](images/sound-grid.png)
107
+
108
+ You can also drop any `.wav` or `.mp3` into the sound directories manually:
109
+
110
+ ```
111
+ ~/.claude/sounds/
112
+ ├── start/ # add files here for session start
113
+ ├── stop/ # add files here for response complete
114
+ ├── error/ # add files here for failures
115
+ └── ...
116
+ ```
117
+
118
+ The script picks randomly from whatever files are in each directory.
119
+
120
+ ## Uninstalling
121
+
122
+ ```bash
123
+ npx claude-code-sounds --uninstall
124
+ ```
125
+
126
+ This removes all sound files, the hook script, and the hooks config from `settings.json`.
127
+
128
+ <details>
129
+ <summary><h2 style="display:inline">How It Works</h2></summary>
130
+
131
+ A single script (`~/.claude/hooks/play-sound.sh`) handles all events. It takes a category name as an argument, picks a random `.wav` or `.mp3` from `~/.claude/sounds/<category>/`, and plays it with `afplay`.
132
+
133
+ Hooks are configured in `~/.claude/settings.json` — each Claude Code lifecycle event calls the script with the appropriate category.
134
+
135
+ ### Hook Events
77
136
 
78
137
  | Event | Hook | When |
79
138
  |---|---|---|
@@ -89,7 +148,10 @@ Each theme maps sounds across all 11 Claude Code lifecycle events.
89
148
  | `compact` | `PreCompact` | Context compaction |
90
149
  | `teammate-idle` | `TeammateIdle` | Teammate went idle |
91
150
 
92
- ## Creating a Theme
151
+ </details>
152
+
153
+ <details>
154
+ <summary><h2 style="display:inline">Creating a Theme</h2></summary>
93
155
 
94
156
  Themes live in `themes/<name>/` with two items:
95
157
 
@@ -116,41 +178,7 @@ Defines metadata and maps sound files to hook categories:
116
178
 
117
179
  Place audio files (`.wav` or `.mp3`) in `themes/<name>/sounds/` with filenames matching the `name` field in `theme.json`.
118
180
 
119
- ## How It Works
120
-
121
- A single script (`~/.claude/hooks/play-sound.sh`) handles all events. It takes a category name as an argument, picks a random `.wav` or `.mp3` from `~/.claude/sounds/<category>/`, and plays it with `afplay`.
122
-
123
- Hooks are configured in `~/.claude/settings.json` — each Claude Code lifecycle event calls the script with the appropriate category.
124
-
125
- ## Customizing
126
-
127
- Re-run with `--mix` to open the sound assignment grid, where you can reassign sounds to hooks, add themes, or preview clips:
128
-
129
- ```bash
130
- npx claude-code-sounds --mix
131
- ```
132
-
133
- ![Sound assignment grid](images/sound-grid.png)
134
-
135
- You can also drop any `.wav` or `.mp3` into the sound directories manually:
136
-
137
- ```
138
- ~/.claude/sounds/
139
- ├── start/ # add files here for session start
140
- ├── stop/ # add files here for response complete
141
- ├── error/ # add files here for failures
142
- └── ...
143
- ```
144
-
145
- The script picks randomly from whatever files are in each directory.
146
-
147
- ## Uninstalling
148
-
149
- ```bash
150
- npx claude-code-sounds --uninstall
151
- ```
152
-
153
- This removes all sound files, the hook script, and the hooks config from `settings.json`.
181
+ </details>
154
182
 
155
183
  ## Disclaimer
156
184
 
package/bin/cli.js CHANGED
@@ -381,6 +381,8 @@ function showHelp() {
381
381
  npx claude-code-sounds --list List available themes
382
382
  npx claude-code-sounds --mute Mute all sounds
383
383
  npx claude-code-sounds --unmute Unmute all sounds
384
+ npx claude-code-sounds --dnd Auto-mute when in video calls
385
+ npx claude-code-sounds --no-dnd Disable auto-mute
384
386
  npx claude-code-sounds --uninstall Remove all sounds and hooks
385
387
  npx claude-code-sounds --help Show this help
386
388
 
@@ -391,6 +393,8 @@ function showHelp() {
391
393
  -l, --list List available themes
392
394
  --mute Mute all sounds
393
395
  --unmute Unmute all sounds
396
+ --dnd Auto-mute when in video calls
397
+ --no-dnd Disable auto-mute
394
398
  -h, --help Show this help
395
399
  `);
396
400
  }
@@ -626,14 +630,27 @@ async function interactiveInstall(autoYes) {
626
630
  const existing = detectExistingInstall();
627
631
 
628
632
  if (existing && !autoYes) {
633
+ const muted = lib.isMuted(paths);
634
+ const muteStatus = muted ? color.yellow(" (muted)") : "";
629
635
  p.log.info(
630
- `Already installed — ${color.bold(existing.themeDisplays.join(", "))} (${existing.totalEnabled} sounds enabled)`
636
+ `Already installed — ${color.bold(existing.themeDisplays.join(", "))} (${existing.totalEnabled} sounds enabled)${muteStatus}`
631
637
  );
632
638
 
639
+ const muteOption = muted
640
+ ? { value: "unmute", label: "Unmute sounds", hint: "Sounds are currently muted" }
641
+ : { value: "mute", label: "Mute sounds", hint: "Silence sounds without uninstalling" };
642
+
643
+ const dnd = lib.isDnd(paths);
644
+ const dndOption = dnd
645
+ ? { value: "dnd-off", label: "Disable Do Not Disturb", hint: "Stop auto-muting during calls" }
646
+ : { value: "dnd-on", label: "Enable Do Not Disturb", hint: "Auto-mute during video calls" };
647
+
633
648
  const action = await p.select({
634
649
  message: "What would you like to do?",
635
650
  options: [
636
651
  { value: "modify", label: "Modify install", hint: "Add themes, change sounds" },
652
+ muteOption,
653
+ dndOption,
637
654
  { value: "fresh", label: "Fresh install", hint: "Start over from scratch" },
638
655
  { value: "uninstall", label: "Uninstall", hint: "Remove all sounds and hooks" },
639
656
  ],
@@ -644,6 +661,30 @@ async function interactiveInstall(autoYes) {
644
661
  process.exit(0);
645
662
  }
646
663
 
664
+ if (action === "mute") {
665
+ lib.setMuted(true, paths);
666
+ p.outro("Sounds muted.");
667
+ return;
668
+ }
669
+
670
+ if (action === "unmute") {
671
+ lib.setMuted(false, paths);
672
+ p.outro("Sounds unmuted.");
673
+ return;
674
+ }
675
+
676
+ if (action === "dnd-on") {
677
+ lib.setDnd(true, paths);
678
+ p.outro("Do Not Disturb enabled. Edit ~/.claude/sounds/.dnd to customize.");
679
+ return;
680
+ }
681
+
682
+ if (action === "dnd-off") {
683
+ lib.setDnd(false, paths);
684
+ p.outro("Do Not Disturb disabled.");
685
+ return;
686
+ }
687
+
647
688
  if (action === "uninstall") {
648
689
  uninstallAll();
649
690
  p.outro("All sounds removed.");
@@ -820,6 +861,13 @@ if (flags.has("--help") || flags.has("-h")) {
820
861
  } else if (flags.has("--unmute")) {
821
862
  lib.setMuted(false, paths);
822
863
  console.log(" Sounds unmuted.");
864
+ } else if (flags.has("--dnd")) {
865
+ lib.setDnd(true, paths);
866
+ console.log(" Do Not Disturb enabled. Sounds auto-mute when video call apps are detected.");
867
+ console.log(" Edit ~/.claude/sounds/.dnd to customize the app list.");
868
+ } else if (flags.has("--no-dnd")) {
869
+ lib.setDnd(false, paths);
870
+ console.log(" Do Not Disturb disabled.");
823
871
  } else if (flags.has("--uninstall") || flags.has("--remove")) {
824
872
  p.intro(color.bold("claude-code-sounds"));
825
873
  uninstallAll();
package/bin/lib.js CHANGED
@@ -133,6 +133,41 @@ function setMuted(muted, paths) {
133
133
  }
134
134
  }
135
135
 
136
+ const DND_DEFAULTS = [
137
+ "# Auto-mute during active calls",
138
+ "# One process name per line, # for comments",
139
+ "# Tip: use meeting-specific processes, not main apps (they stay open after calls)",
140
+ "# Find process names with: pgrep -la <app>",
141
+ "CptHost",
142
+ "FaceTime",
143
+ "Webex",
144
+ ];
145
+
146
+ function isDnd(paths) {
147
+ return fs.existsSync(path.join(paths.SOUNDS_DIR, ".dnd"));
148
+ }
149
+
150
+ function setDnd(enabled, paths) {
151
+ const dndPath = path.join(paths.SOUNDS_DIR, ".dnd");
152
+ if (enabled) {
153
+ mkdirp(paths.SOUNDS_DIR);
154
+ fs.writeFileSync(dndPath, DND_DEFAULTS.join("\n") + "\n");
155
+ // Ensure the installed hook script supports DND
156
+ _updateHookScript(paths);
157
+ } else if (fs.existsSync(dndPath)) {
158
+ fs.unlinkSync(dndPath);
159
+ }
160
+ }
161
+
162
+ function _updateHookScript(paths) {
163
+ const hookSrc = path.join(paths.PKG_DIR, "hooks", "play-sound.sh");
164
+ const hookDest = path.join(paths.HOOKS_DIR, "play-sound.sh");
165
+ if (fs.existsSync(hookDest) && fs.existsSync(hookSrc)) {
166
+ fs.copyFileSync(hookSrc, hookDest);
167
+ fs.chmodSync(hookDest, 0o755);
168
+ }
169
+ }
170
+
136
171
  // ─── Detect Existing Install ─────────────────────────────────────────────────
137
172
 
138
173
  function detectExistingInstall(paths) {
@@ -230,6 +265,11 @@ function installHooksConfig(paths) {
230
265
  }
231
266
  }
232
267
  }
268
+
269
+ // Enable Do Not Disturb by default (auto-mute during video calls)
270
+ if (!isDnd(paths)) {
271
+ setDnd(true, paths);
272
+ }
233
273
  }
234
274
 
235
275
  function uninstallAll(paths) {
@@ -300,6 +340,9 @@ module.exports = {
300
340
  writeInstalled,
301
341
  isMuted,
302
342
  setMuted,
343
+ DND_DEFAULTS,
344
+ isDnd,
345
+ setDnd,
303
346
  detectExistingInstall,
304
347
  installSounds,
305
348
  installHooksConfig,
@@ -6,6 +6,15 @@ CATEGORY="${1:-}"
6
6
  cat > /dev/null 2>&1
7
7
 
8
8
  [[ -f "$SOUNDS_DIR/.muted" ]] && exit 0
9
+
10
+ # Auto-mute: skip when video call apps are running
11
+ if [[ -f "$SOUNDS_DIR/.dnd" ]]; then
12
+ while IFS= read -r proc || [[ -n "$proc" ]]; do
13
+ [[ -z "$proc" || "$proc" == \#* ]] && continue
14
+ pgrep -xi "$proc" > /dev/null 2>&1 && exit 0
15
+ done < "$SOUNDS_DIR/.dnd"
16
+ fi
17
+
9
18
  [[ -z "$CATEGORY" ]] && exit 0
10
19
  DIR="$SOUNDS_DIR/$CATEGORY"
11
20
  [[ ! -d "$DIR" ]] && exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-sounds",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "Sound themes for Claude Code lifecycle hooks",
5
5
  "bin": {
6
6
  "claude-code-sounds": "bin/cli.js"