claude-code-sounds 1.5.4 → 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
  }
@@ -636,11 +640,17 @@ async function interactiveInstall(autoYes) {
636
640
  ? { value: "unmute", label: "Unmute sounds", hint: "Sounds are currently muted" }
637
641
  : { value: "mute", label: "Mute sounds", hint: "Silence sounds without uninstalling" };
638
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
+
639
648
  const action = await p.select({
640
649
  message: "What would you like to do?",
641
650
  options: [
642
651
  { value: "modify", label: "Modify install", hint: "Add themes, change sounds" },
643
652
  muteOption,
653
+ dndOption,
644
654
  { value: "fresh", label: "Fresh install", hint: "Start over from scratch" },
645
655
  { value: "uninstall", label: "Uninstall", hint: "Remove all sounds and hooks" },
646
656
  ],
@@ -663,6 +673,18 @@ async function interactiveInstall(autoYes) {
663
673
  return;
664
674
  }
665
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
+
666
688
  if (action === "uninstall") {
667
689
  uninstallAll();
668
690
  p.outro("All sounds removed.");
@@ -839,6 +861,13 @@ if (flags.has("--help") || flags.has("-h")) {
839
861
  } else if (flags.has("--unmute")) {
840
862
  lib.setMuted(false, paths);
841
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.");
842
871
  } else if (flags.has("--uninstall") || flags.has("--remove")) {
843
872
  p.intro(color.bold("claude-code-sounds"));
844
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.4",
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"