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 +77 -49
- package/bin/cli.js +49 -1
- package/bin/lib.js +43 -0
- package/hooks/play-sound.sh +9 -0
- package/package.json +1 -1
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
|
-
##
|
|
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
|
+

|
|
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
|
-
|
|
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
|
-
|
|
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
|
-

|
|
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,
|
package/hooks/play-sound.sh
CHANGED
|
@@ -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
|