@delorenj/claude-notifications 1.1.0 → 2.0.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.
Files changed (148) hide show
  1. package/.claude/checkpoints/1756392335.json +1 -0
  2. package/.claude/checkpoints/1756392341.json +1 -0
  3. package/.claude/checkpoints/1756392347.json +1 -0
  4. package/.claude/checkpoints/1756392376.json +1 -0
  5. package/.claude/checkpoints/1756392377.json +1 -0
  6. package/.claude/checkpoints/1756392386.json +1 -0
  7. package/.claude/checkpoints/1756392387.json +1 -0
  8. package/.claude/checkpoints/1756392398.json +1 -0
  9. package/.claude/checkpoints/1756392400.json +1 -0
  10. package/.claude/checkpoints/1756392427.json +1 -0
  11. package/.claude/checkpoints/1756392428.json +1 -0
  12. package/.claude/checkpoints/1756392486.json +1 -0
  13. package/.claude/checkpoints/1756392488.json +1 -0
  14. package/.claude/checkpoints/1756392558.json +1 -0
  15. package/.claude/checkpoints/1756392559.json +1 -0
  16. package/.claude/checkpoints/summary-session-20250828-105040.md +57 -0
  17. package/.claude/checkpoints/task-1756392207.json +1 -0
  18. package/.claude/checkpoints/task-1756392742.json +1 -0
  19. package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +54 -0
  20. package/.claude/commands/analysis/README.md +9 -0
  21. package/.claude/commands/analysis/bottleneck-detect.md +162 -0
  22. package/.claude/commands/analysis/performance-bottlenecks.md +59 -0
  23. package/.claude/commands/analysis/performance-report.md +25 -0
  24. package/.claude/commands/analysis/token-efficiency.md +45 -0
  25. package/.claude/commands/analysis/token-usage.md +25 -0
  26. package/.claude/commands/automation/README.md +9 -0
  27. package/.claude/commands/automation/auto-agent.md +122 -0
  28. package/.claude/commands/automation/self-healing.md +106 -0
  29. package/.claude/commands/automation/session-memory.md +90 -0
  30. package/.claude/commands/automation/smart-agents.md +73 -0
  31. package/.claude/commands/automation/smart-spawn.md +25 -0
  32. package/.claude/commands/automation/workflow-select.md +25 -0
  33. package/.claude/commands/coordination/README.md +9 -0
  34. package/.claude/commands/coordination/agent-spawn.md +25 -0
  35. package/.claude/commands/coordination/init.md +44 -0
  36. package/.claude/commands/coordination/orchestrate.md +43 -0
  37. package/.claude/commands/coordination/spawn.md +45 -0
  38. package/.claude/commands/coordination/swarm-init.md +85 -0
  39. package/.claude/commands/coordination/task-orchestrate.md +25 -0
  40. package/.claude/commands/github/README.md +11 -0
  41. package/.claude/commands/github/code-review-swarm.md +514 -0
  42. package/.claude/commands/github/code-review.md +25 -0
  43. package/.claude/commands/github/github-modes.md +147 -0
  44. package/.claude/commands/github/github-swarm.md +121 -0
  45. package/.claude/commands/github/issue-tracker.md +292 -0
  46. package/.claude/commands/github/issue-triage.md +25 -0
  47. package/.claude/commands/github/multi-repo-swarm.md +519 -0
  48. package/.claude/commands/github/pr-enhance.md +26 -0
  49. package/.claude/commands/github/pr-manager.md +170 -0
  50. package/.claude/commands/github/project-board-sync.md +471 -0
  51. package/.claude/commands/github/release-manager.md +338 -0
  52. package/.claude/commands/github/release-swarm.md +544 -0
  53. package/.claude/commands/github/repo-analyze.md +25 -0
  54. package/.claude/commands/github/repo-architect.md +367 -0
  55. package/.claude/commands/github/swarm-issue.md +482 -0
  56. package/.claude/commands/github/swarm-pr.md +285 -0
  57. package/.claude/commands/github/sync-coordinator.md +301 -0
  58. package/.claude/commands/github/workflow-automation.md +442 -0
  59. package/.claude/commands/hooks/README.md +11 -0
  60. package/.claude/commands/hooks/overview.md +58 -0
  61. package/.claude/commands/hooks/post-edit.md +117 -0
  62. package/.claude/commands/hooks/post-task.md +112 -0
  63. package/.claude/commands/hooks/pre-edit.md +113 -0
  64. package/.claude/commands/hooks/pre-task.md +111 -0
  65. package/.claude/commands/hooks/session-end.md +118 -0
  66. package/.claude/commands/hooks/setup.md +103 -0
  67. package/.claude/commands/memory/README.md +9 -0
  68. package/.claude/commands/memory/memory-persist.md +25 -0
  69. package/.claude/commands/memory/memory-search.md +25 -0
  70. package/.claude/commands/memory/memory-usage.md +25 -0
  71. package/.claude/commands/memory/neural.md +47 -0
  72. package/.claude/commands/memory/usage.md +46 -0
  73. package/.claude/commands/monitoring/README.md +9 -0
  74. package/.claude/commands/monitoring/agent-metrics.md +25 -0
  75. package/.claude/commands/monitoring/agents.md +44 -0
  76. package/.claude/commands/monitoring/real-time-view.md +25 -0
  77. package/.claude/commands/monitoring/status.md +46 -0
  78. package/.claude/commands/monitoring/swarm-monitor.md +25 -0
  79. package/.claude/commands/optimization/README.md +9 -0
  80. package/.claude/commands/optimization/auto-topology.md +62 -0
  81. package/.claude/commands/optimization/cache-manage.md +25 -0
  82. package/.claude/commands/optimization/parallel-execute.md +25 -0
  83. package/.claude/commands/optimization/parallel-execution.md +50 -0
  84. package/.claude/commands/optimization/topology-optimize.md +25 -0
  85. package/.claude/commands/pair/README.md +261 -0
  86. package/.claude/commands/pair/commands.md +546 -0
  87. package/.claude/commands/pair/config.md +510 -0
  88. package/.claude/commands/pair/examples.md +512 -0
  89. package/.claude/commands/pair/modes.md +348 -0
  90. package/.claude/commands/pair/session.md +407 -0
  91. package/.claude/commands/pair/start.md +209 -0
  92. package/.claude/commands/sparc/analyzer.md +52 -0
  93. package/.claude/commands/sparc/architect.md +53 -0
  94. package/.claude/commands/sparc/batch-executor.md +54 -0
  95. package/.claude/commands/sparc/coder.md +54 -0
  96. package/.claude/commands/sparc/debugger.md +54 -0
  97. package/.claude/commands/sparc/designer.md +53 -0
  98. package/.claude/commands/sparc/documenter.md +54 -0
  99. package/.claude/commands/sparc/innovator.md +54 -0
  100. package/.claude/commands/sparc/memory-manager.md +54 -0
  101. package/.claude/commands/sparc/optimizer.md +54 -0
  102. package/.claude/commands/sparc/orchestrator.md +132 -0
  103. package/.claude/commands/sparc/researcher.md +54 -0
  104. package/.claude/commands/sparc/reviewer.md +54 -0
  105. package/.claude/commands/sparc/sparc-modes.md +174 -0
  106. package/.claude/commands/sparc/swarm-coordinator.md +54 -0
  107. package/.claude/commands/sparc/tdd.md +54 -0
  108. package/.claude/commands/sparc/tester.md +54 -0
  109. package/.claude/commands/sparc/workflow-manager.md +54 -0
  110. package/.claude/commands/stream-chain/pipeline.md +121 -0
  111. package/.claude/commands/stream-chain/run.md +70 -0
  112. package/.claude/commands/swarm/analysis.md +95 -0
  113. package/.claude/commands/swarm/development.md +96 -0
  114. package/.claude/commands/swarm/examples.md +168 -0
  115. package/.claude/commands/swarm/maintenance.md +102 -0
  116. package/.claude/commands/swarm/optimization.md +117 -0
  117. package/.claude/commands/swarm/research.md +136 -0
  118. package/.claude/commands/swarm/testing.md +131 -0
  119. package/.claude/commands/training/README.md +9 -0
  120. package/.claude/commands/training/model-update.md +25 -0
  121. package/.claude/commands/training/neural-patterns.md +74 -0
  122. package/.claude/commands/training/neural-train.md +25 -0
  123. package/.claude/commands/training/pattern-learn.md +25 -0
  124. package/.claude/commands/training/specialization.md +63 -0
  125. package/.claude/commands/truth/start.md +143 -0
  126. package/.claude/commands/verify/check.md +50 -0
  127. package/.claude/commands/verify/start.md +128 -0
  128. package/.claude/commands/workflows/README.md +9 -0
  129. package/.claude/commands/workflows/development.md +78 -0
  130. package/.claude/commands/workflows/research.md +63 -0
  131. package/.claude/commands/workflows/workflow-create.md +25 -0
  132. package/.claude/commands/workflows/workflow-execute.md +25 -0
  133. package/.claude/commands/workflows/workflow-export.md +25 -0
  134. package/.claude/config.json +36 -0
  135. package/.claude/settings.json +162 -0
  136. package/.claude-flow/metrics/agent-metrics.json +1 -0
  137. package/.claude-flow/metrics/performance.json +9 -0
  138. package/.claude-flow/metrics/system-metrics.json +230 -0
  139. package/.claude-flow/metrics/task-metrics.json +10 -0
  140. package/FIXES.md +75 -0
  141. package/README.md +19 -2
  142. package/bin/claude-notifications.js +145 -46
  143. package/bin/claude-notify.js +84 -4
  144. package/lib/config.js +113 -1
  145. package/package.json +1 -1
  146. package/test-results.md +163 -0
  147. package/test.sh +1 -1
  148. package/ruv-swarm-mcp.db +0 -0
package/FIXES.md ADDED
@@ -0,0 +1,75 @@
1
+ # Claude Notifications - Install Process Fixes
2
+
3
+ ## Issues Identified and Fixed
4
+
5
+ ### 1. **Path Migration Inconsistency** ✅ FIXED
6
+ **Problem:** The code migrated from `~/.local/share/sounds/` to `~/.config/claude-notifications/sounds/` but cleanup logic was inconsistent and error-prone.
7
+ **Solution:**
8
+ - Created centralized `cleanupLegacySoundFiles()` function in `lib/config.js`
9
+ - Properly handles multiple legacy paths with extensible architecture
10
+ - Safe error handling for file operations
11
+
12
+ ### 2. **Missing Configuration Directory Creation** ✅ FIXED
13
+ **Problem:** Installation could fail if the `~/.config/claude-notifications/` directory didn't exist.
14
+ **Solution:**
15
+ - Added `ensureConfigDirectory()` function to create parent directories
16
+ - Both sound file creation functions now call this before attempting to write
17
+ - Prevents installation failures due to missing directories
18
+
19
+ ### 3. **Incomplete Configuration Migration** ✅ FIXED
20
+ **Problem:** Config migration only handled `secondSound` → `soundType` but didn't validate or save migrated configs.
21
+ **Solution:**
22
+ - Enhanced `getConfig()` with comprehensive migration logic
23
+ - Validates `soundType` values and resets invalid ones
24
+ - Automatically saves migrated configurations back to disk
25
+ - Clear console logging for migration events
26
+
27
+ ### 4. **Poor Error Handling During Installation** ✅ FIXED
28
+ **Problem:** Installation would fail completely if any single step failed, with no graceful degradation.
29
+ **Solution:**
30
+ - Individual sound file creation returns success/failure status
31
+ - Installation continues even if one sound file fails
32
+ - More informative error messages and status reporting
33
+ - Better test notification feedback
34
+
35
+ ### 5. **Incorrect Reference in test.sh** ✅ FIXED
36
+ **Problem:** `test.sh` referenced non-existent `./install.sh` script.
37
+ **Solution:** Updated error message to reference correct installation commands.
38
+
39
+ ### 6. **Missing Temp Directory Error Handling** ✅ FIXED
40
+ **Problem:** Sound generation could fail if temporary directory creation failed.
41
+ **Solution:** Added try/catch around temp directory creation with early return on failure.
42
+
43
+ ## Technical Improvements
44
+
45
+ ### Enhanced Error Resilience
46
+ - Installation now continues even if Claude Code config can't be found/updated
47
+ - Sound file creation has individual success tracking
48
+ - Temp directory operations are safely handled
49
+
50
+ ### Better User Experience
51
+ - More descriptive status messages during installation
52
+ - Clear indication of what worked vs. what failed
53
+ - Improved usage instructions after installation
54
+ - Better test feedback
55
+
56
+ ### Code Organization
57
+ - Centralized path management and cleanup logic
58
+ - Consistent error handling patterns
59
+ - Proper separation of concerns between modules
60
+
61
+ ### Backward Compatibility
62
+ - Automatic cleanup of old sound file locations
63
+ - Migration of legacy configuration formats
64
+ - Extensible architecture for future path changes
65
+
66
+ ## Files Modified
67
+ - `bin/claude-notifications.js` - Main installer improvements
68
+ - `lib/config.js` - Enhanced configuration management and migration
69
+ - `test.sh` - Fixed installation reference
70
+
71
+ ## Next Steps for Production
72
+ 1. Test installation on clean system
73
+ 2. Test upgrade from legacy installation
74
+ 3. Verify sound file generation on different platforms
75
+ 4. Test Claude Code integration
package/README.md CHANGED
@@ -20,8 +20,9 @@ That's it! 🎉 The package will automatically:
20
20
  ## Features
21
21
 
22
22
  - 🎵 **Final Fantasy Dream Harp** - Classic C-D-E-G ascending/(optional)descending pattern
23
+ - 🔔 **Service Desk Bell** - Optional short, crisp bell sound for a quick "done!" signal
23
24
  - 🔊 **Cross-Platform Audio** - Works on Linux and macOS
24
- - 🖥️ **Desktop Notifications** - Visual notifications with Claude Code branding
25
+ - 🖥️ **Desktop Notifications** - Visual notifications with Claude Code branding (optional)
25
26
  - 🪝 **Auto-Integration** - Automatically configures Claude Code hooks
26
27
  - ⚡ **Zero Configuration** - Works out of the box
27
28
  - webhook **Webhook Support** - Trigger a webhook in addition to or instead of the sound
@@ -37,9 +38,15 @@ After installation, Claude Code will begin notifying you when it finishes or is
37
38
  # Trigger notification manually
38
39
  claude-notify
39
40
 
41
+ # Trigger bell notification manually
42
+ claude-notify --bell
43
+
40
44
  # Test the system
41
45
  claude-notifications test
42
46
 
47
+ # Test the bell sound
48
+ claude-notifications test-bell
49
+
43
50
  # Reinstall/repair
44
51
  claude-notifications install
45
52
 
@@ -121,6 +128,8 @@ Create a configuration file at `~/.config/claude-notifications/settings.json`.
121
128
  ```json
122
129
  {
123
130
  "sound": true,
131
+ "soundType": "claude-notification",
132
+ "desktopNotification": false,
124
133
  "webhook": {
125
134
  "enabled": true,
126
135
  "url": "https://maker.ifttt.com/trigger/claude_notification/with/key/YOUR_KEY",
@@ -131,7 +140,11 @@ Create a configuration file at `~/.config/claude-notifications/settings.json`.
131
140
 
132
141
  **Configuration Options:**
133
142
 
134
- - `sound`: (boolean) Whether to play the notification sound. Defaults to `true`.
143
+ - `sound`: (boolean) Whether to play notification sounds. Defaults to `true`.
144
+ - `soundType`: (string) Which sound to play. Available options:
145
+ - `"claude-notification"` - Final Fantasy dream harp (default)
146
+ - `"claude-notification-bell"` - Service desk bell
147
+ - `desktopNotification`: (boolean) Whether to show desktop notification banners. Defaults to `false`.
135
148
  - `webhook.enabled`: (boolean) Whether to trigger the webhook. Defaults to `false`.
136
149
  - `webhook.url`: (string) The URL to send the POST request to.
137
150
  - `webhook.replaceSound`: (boolean) If `true`, the sound will not play when a webhook is triggered. Defaults to `false`.
@@ -144,6 +157,10 @@ The webhook will be sent as a `POST` request with a JSON payload:
144
157
  }
145
158
  ```
146
159
 
160
+ ### Sound Files
161
+
162
+ Sound files are stored in `~/.config/claude-notifications/sounds/` and can be customized by replacing the `.wav` files in that directory.
163
+
147
164
  ### Create Custom Patterns
148
165
 
149
166
  Use `sox` to create new victory fanfares:
@@ -4,6 +4,7 @@ const { execSync, spawn } = require("child_process");
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const os = require("os");
7
+ const { ensureConfigDirectory, ensureSoundsDirectory, getSoundPath, SOUND_TYPES, soundsDir } = require("../lib/config");
7
8
 
8
9
  const colors = {
9
10
  red: "\x1b[31m",
@@ -106,13 +107,9 @@ function updateClaudeCodeConfig() {
106
107
  }
107
108
 
108
109
  function createSoundFile() {
109
- const soundDir = path.join(os.homedir(), ".local", "share", "sounds");
110
- const soundFile = path.join(soundDir, "claude-notification.wav");
111
-
112
- // Create directory if it doesn't exist
113
- if (!fs.existsSync(soundDir)) {
114
- fs.mkdirSync(soundDir, { recursive: true });
115
- }
110
+ ensureConfigDirectory();
111
+ ensureSoundsDirectory();
112
+ const soundFile = getSoundPath(SOUND_TYPES.HARP);
116
113
 
117
114
  // Check if sox is available
118
115
  try {
@@ -138,8 +135,13 @@ function createSoundFile() {
138
135
 
139
136
  // Create individual note files first (safer approach)
140
137
  const tempDir = path.join(os.tmpdir(), "claude-notifications");
141
- if (!fs.existsSync(tempDir)) {
142
- fs.mkdirSync(tempDir, { recursive: true });
138
+ try {
139
+ if (!fs.existsSync(tempDir)) {
140
+ fs.mkdirSync(tempDir, { recursive: true });
141
+ }
142
+ } catch (error) {
143
+ log("red", `❌ Cannot create temp directory: ${error.message}`);
144
+ return false;
143
145
  }
144
146
 
145
147
  const notes = [
@@ -207,6 +209,80 @@ function createSoundFile() {
207
209
  }
208
210
  }
209
211
 
212
+ function generateBellSoxCommand(outputFile) {
213
+ // Bell sound parameters - adjust these to customize the bell
214
+ const bellParams = {
215
+ // Base tone generation
216
+ duration: 0.1, // Length of the initial bell strike (seconds)
217
+ frequency: 1600, // Pitch of the bell (Hz) - higher = more "ting", lower = more "dong"
218
+
219
+ // Fade envelope
220
+ fadeIn: 0, // Fade in time (seconds) - 0 for immediate attack
221
+ fadeDuration: 0.1, // Total fade duration (seconds)
222
+ fadeOut: 0.05, // Fade out time (seconds) - creates the bell decay
223
+
224
+ // Volume
225
+ volume: 0.9, // Master volume (0.0 to 1.0)
226
+
227
+ // Echo effect parameters (creates the "ringing" quality)
228
+ echoGain: 0.5, // Overall echo volume (0.0 to 1.0)
229
+ echoDecay: 0.5, // How quickly echoes fade (0.0 to 1.0)
230
+
231
+ // Individual echo delays and volumes (milliseconds, volume)
232
+ echo1: { delay: 250, volume: 0.2 }, // First echo - quarter second delay
233
+ echo2: { delay: 500, volume: 0.05 }, // Second echo - half second delay
234
+ echo3: { delay: 750, volume: 0.01 }, // Third echo - three quarter second delay
235
+
236
+ // Reverb parameters (adds spatial depth)
237
+ reverb: {
238
+ roomSize: 40, // Room size percentage (0-100) - larger = more spacious
239
+ preDelay: 65, // Pre-delay in ms - time before reverb starts
240
+ reverbTime: 100, // Reverb decay time percentage (0-100)
241
+ wetGain: 100, // Wet signal gain percentage (0-100) - reverb volume
242
+ dryGain: 12, // Dry signal gain percentage (0-100) - original signal volume
243
+ stereoDepth: 0, // Stereo depth (0-100) - 0 = mono, higher = wider stereo
244
+ },
245
+ };
246
+
247
+ // Build the sox command with clear parameter mapping
248
+ const command = [
249
+ "sox -n", // Generate from nothing (null input)
250
+ `"${outputFile}"`, // Output file
251
+ `synth ${bellParams.duration} sine ${bellParams.frequency}`, // Generate sine wave
252
+ `fade ${bellParams.fadeIn} ${bellParams.fadeDuration} ${bellParams.fadeOut}`, // Apply fade envelope
253
+ `vol ${bellParams.volume}`, // Set volume
254
+ `echos ${bellParams.echoGain} ${bellParams.echoDecay}`, // Echo effect base settings
255
+ `${bellParams.echo1.delay} ${bellParams.echo1.volume}`, // Echo 1: 250ms delay, 0.2 volume
256
+ `${bellParams.echo2.delay} ${bellParams.echo2.volume}`, // Echo 2: 500ms delay, 0.1 volume
257
+ `${bellParams.echo3.delay} ${bellParams.echo3.volume}`, // Echo 3: 750ms delay, 0.05 volume
258
+ `reverb ${bellParams.reverb.roomSize} ${bellParams.reverb.preDelay}`, // Reverb room & pre-delay
259
+ `${bellParams.reverb.reverbTime} ${bellParams.reverb.wetGain}`, // Reverb time & wet gain
260
+ `${bellParams.reverb.dryGain} ${bellParams.reverb.stereoDepth}`, // Dry gain & stereo depth
261
+ ].join(" ");
262
+
263
+ return command;
264
+ }
265
+
266
+ function createBellSoundFile() {
267
+ ensureConfigDirectory();
268
+ ensureSoundsDirectory();
269
+ const soundFile = getSoundPath(SOUND_TYPES.BELL);
270
+
271
+ log("blue", "🔔 Generating service desk bell sound...");
272
+
273
+ try {
274
+ // Generate the bell sound using our documented sox command builder
275
+ const bellCommand = generateBellSoxCommand(soundFile);
276
+ execSync(bellCommand, { stdio: "ignore", timeout: 5000 });
277
+
278
+ log("green", "✅ Bell sound file created successfully!");
279
+ return true;
280
+ } catch (error) {
281
+ log("red", `❌ Error creating bell sound file: ${error.message}`);
282
+ return false;
283
+ }
284
+ }
285
+
210
286
  function main() {
211
287
  const command = process.argv[2];
212
288
 
@@ -215,38 +291,55 @@ function main() {
215
291
  case undefined:
216
292
  log("blue", "🎵 Installing Claude Notifications...");
217
293
 
218
- if (createSoundFile()) {
219
- updateClaudeCodeConfig();
220
- log("green", "🎉 Installation complete!");
221
- log("blue", "🧪 Testing notification...");
294
+ const soundCreated = createSoundFile();
295
+ const bellCreated = createBellSoundFile();
296
+
297
+ if (soundCreated && bellCreated) {
298
+ log("green", "✅ Sound files created successfully!");
299
+ } else if (soundCreated || bellCreated) {
300
+ log("yellow", "⚠️ Some sound files failed to create, but installation can continue");
301
+ } else {
302
+ log("red", "❌ Failed to create sound files. Please check if sox is installed and try again.");
303
+ process.exit(1);
304
+ }
305
+
306
+ // Always try to update Claude Code config, but don't fail if it's not found
307
+ updateClaudeCodeConfig();
308
+
309
+ log("green", "🎉 Installation complete!");
310
+ log("blue", "🧪 Testing notification...");
222
311
 
223
- // Test the notification
224
- const testProcess = spawn(
225
- "node",
226
- [path.join(__dirname, "claude-notify.js")],
227
- {
228
- stdio: "inherit",
229
- },
230
- );
312
+ // Test the notification
313
+ const testProcess = spawn(
314
+ "node",
315
+ [path.join(__dirname, "claude-notify.js")],
316
+ {
317
+ stdio: "inherit",
318
+ },
319
+ );
231
320
 
232
- testProcess.on("close", () => {
321
+ testProcess.on("close", (code) => {
322
+ if (code === 0) {
233
323
  log(
234
324
  "green",
235
- "✅ Test complete! You should have heard a dreamy notification!",
325
+ "✅ Test complete! You should have heard a delightful notification!",
236
326
  );
237
- console.log("");
238
- log("blue", "Usage:");
239
- console.log(
240
- " claude-notify # Trigger notification manually",
241
- );
242
- console.log(" claude-notifications # This installer");
243
- console.log("");
244
- log(
245
- "blue",
246
- "Claude Code will now beckon you back with a pleasant notification when it finishes responses or is waiting for your input! 🎮",
247
- );
248
- });
249
- }
327
+ } else {
328
+ log("yellow", "⚠️ Test notification had issues, but installation completed");
329
+ }
330
+ console.log("");
331
+ log("blue", "Usage:");
332
+ console.log(
333
+ " claude-notify # Trigger notification manually",
334
+ );
335
+ console.log(" claude-notify --bell # Trigger bell notification");
336
+ console.log(" claude-notifications test # Test the notifications");
337
+ console.log("");
338
+ log(
339
+ "blue",
340
+ "Claude Code will now beckon you back with pleasant notifications! 🎮",
341
+ );
342
+ });
250
343
  break;
251
344
 
252
345
  case "test":
@@ -256,21 +349,26 @@ function main() {
256
349
  });
257
350
  break;
258
351
 
352
+ case "test-bell":
353
+ log("blue", "🔔 Testing bell notification...");
354
+ spawn("node", [path.join(__dirname, "claude-notify.js"), "--bell"], {
355
+ stdio: "inherit",
356
+ });
357
+ break;
358
+
259
359
  case "uninstall":
260
360
  log("blue", "🗑️ Uninstalling Claude Notifications...");
261
361
 
262
- const soundFile = path.join(
263
- os.homedir(),
264
- ".local",
265
- "share",
266
- "sounds",
267
- "claude-notification.wav",
268
- );
269
- if (fs.existsSync(soundFile)) {
270
- fs.unlinkSync(soundFile);
271
- log("green", "✅ Removed sound file");
362
+ // Remove sounds directory
363
+ if (fs.existsSync(soundsDir)) {
364
+ fs.rmSync(soundsDir, { recursive: true, force: true });
365
+ log("green", "✅ Removed sounds directory");
272
366
  }
273
367
 
368
+ // Clean up old sound files from legacy location
369
+ const { cleanupLegacySoundFiles } = require("../lib/config");
370
+ cleanupLegacySoundFiles();
371
+
274
372
  log(
275
373
  "yellow",
276
374
  "⚠️ Please manually remove the stop hook from your Claude Code config",
@@ -289,6 +387,7 @@ function main() {
289
387
  console.log("Commands:");
290
388
  console.log(" install Install notifications (default)");
291
389
  console.log(" test Test the notification");
390
+ console.log(" test-bell Test the bell notification");
292
391
  console.log(" uninstall Remove notifications");
293
392
  console.log(" help Show this help");
294
393
  break;
@@ -6,19 +6,31 @@ const path = require('path');
6
6
  const os = require('os');
7
7
  const http = require('http');
8
8
  const https = require('https');
9
- const { getConfig } = require('../lib/config');
9
+ const { getConfig, getSoundPath, SOUND_TYPES } = require('../lib/config');
10
10
 
11
11
  const config = getConfig();
12
12
 
13
+ // Check for command line arguments
14
+ const args = process.argv.slice(2);
15
+ const useBell = args.includes('--bell') || args.includes('-b');
16
+ const showConfig = args.includes('-c') || args.includes('--config');
17
+
13
18
  function playSound() {
14
19
  if (!config.sound) {
15
20
  return;
16
21
  }
17
22
 
18
- const soundFile = path.join(os.homedir(), '.local', 'share', 'sounds', 'claude-notification.wav');
23
+ // Determine which sound to play
24
+ let soundType = config.soundType;
25
+ if (useBell) {
26
+ soundType = SOUND_TYPES.BELL;
27
+ }
28
+
29
+ const soundFile = getSoundPath(soundType);
19
30
 
20
31
  if (!fs.existsSync(soundFile)) {
21
- process.stdout.write('\x07');
32
+ console.warn(`Sound file not found: ${soundFile}`);
33
+ process.stdout.write('\x07'); // Fallback to system beep
22
34
  return;
23
35
  }
24
36
 
@@ -97,7 +109,73 @@ function showNotification() {
97
109
  });
98
110
  }
99
111
 
112
+ function showConfigInfo() {
113
+ const configPath = path.join(os.homedir(), '.config', 'claude-notifications', 'settings.json');
114
+ const soundsDir = path.join(os.homedir(), '.config', 'claude-notifications', 'sounds');
115
+
116
+ console.log('🔍 Claude Notifications Config Debug Info:');
117
+ console.log('');
118
+ console.log('📁 Config file location:');
119
+ console.log(` ${configPath}`);
120
+ console.log(` Exists: ${fs.existsSync(configPath) ? '✅' : '❌'}`);
121
+
122
+ if (fs.existsSync(configPath)) {
123
+ try {
124
+ const configContent = fs.readFileSync(configPath, 'utf-8');
125
+ console.log(' Content:');
126
+ console.log(` ${configContent.split('\n').map(line => ` ${line}`).join('\n')}`);
127
+ } catch (error) {
128
+ console.log(` Error reading: ${error.message}`);
129
+ }
130
+ }
131
+
132
+ console.log('');
133
+ console.log('🔊 Sounds directory:');
134
+ console.log(` ${soundsDir}`);
135
+ console.log(` Exists: ${fs.existsSync(soundsDir) ? '✅' : '❌'}`);
136
+
137
+ if (fs.existsSync(soundsDir)) {
138
+ try {
139
+ const soundFiles = fs.readdirSync(soundsDir);
140
+ console.log(' Files:');
141
+ soundFiles.forEach(file => {
142
+ const filePath = path.join(soundsDir, file);
143
+ const stats = fs.statSync(filePath);
144
+ console.log(` - ${file} (${Math.round(stats.size / 1024)}KB)`);
145
+ });
146
+ } catch (error) {
147
+ console.log(` Error reading directory: ${error.message}`);
148
+ }
149
+ }
150
+
151
+ console.log('');
152
+ console.log('⚙️ Current config values:');
153
+ console.log(` sound: ${config.sound}`);
154
+ console.log(` soundType: ${config.soundType}`);
155
+ console.log(` desktopNotification: ${config.desktopNotification}`);
156
+ console.log(` webhook.enabled: ${config.webhook.enabled}`);
157
+
158
+ console.log('');
159
+ console.log('🎵 Sound file paths:');
160
+ const { SOUND_TYPES, getSoundPath } = require('../lib/config');
161
+ Object.values(SOUND_TYPES).forEach(soundType => {
162
+ const soundPath = getSoundPath(soundType);
163
+ console.log(` ${soundType}: ${soundPath}`);
164
+ console.log(` Exists: ${fs.existsSync(soundPath) ? '✅' : '❌'}`);
165
+ });
166
+
167
+ console.log('');
168
+ console.log('🔧 Command line args:');
169
+ console.log(` useBell: ${useBell}`);
170
+ console.log(` showConfig: ${showConfig}`);
171
+ }
172
+
100
173
  function main() {
174
+ if (showConfig) {
175
+ showConfigInfo();
176
+ return;
177
+ }
178
+
101
179
  if (config.webhook.enabled) {
102
180
  triggerWebhook();
103
181
  if (!config.webhook.replaceSound) {
@@ -105,7 +183,9 @@ function main() {
105
183
  }
106
184
  } else {
107
185
  playSound();
108
- showNotification();
186
+ if (config.desktopNotification) {
187
+ showNotification();
188
+ }
109
189
  }
110
190
  }
111
191
 
package/lib/config.js CHANGED
@@ -3,10 +3,19 @@ const path = require('path');
3
3
  const os = require('os');
4
4
 
5
5
  const configPath = path.join(os.homedir(), '.config', 'claude-notifications', 'settings.json');
6
+ const soundsDir = path.join(os.homedir(), '.config', 'claude-notifications', 'sounds');
7
+
8
+ // Available sound types (filenames without extension)
9
+ const SOUND_TYPES = {
10
+ HARP: 'claude-notification',
11
+ BELL: 'claude-notification-bell'
12
+ };
6
13
 
7
14
  function getConfig() {
8
15
  const defaultConfig = {
9
16
  sound: true,
17
+ soundType: SOUND_TYPES.HARP, // Default to harp sound
18
+ desktopNotification: false,
10
19
  webhook: {
11
20
  enabled: false,
12
21
  url: null,
@@ -21,6 +30,35 @@ function getConfig() {
21
30
  try {
22
31
  const configContent = fs.readFileSync(configPath, 'utf-8');
23
32
  const userConfig = JSON.parse(configContent);
33
+
34
+ // Handle migration from old configurations
35
+ let migrated = false;
36
+
37
+ // Migration 1: old 'secondSound' config to 'soundType'
38
+ if (userConfig.secondSound === true && !userConfig.soundType) {
39
+ userConfig.soundType = SOUND_TYPES.BELL;
40
+ delete userConfig.secondSound;
41
+ migrated = true;
42
+ }
43
+
44
+ // Migration 2: ensure soundType is valid
45
+ if (userConfig.soundType && !Object.values(SOUND_TYPES).includes(userConfig.soundType)) {
46
+ console.warn(`Invalid soundType '${userConfig.soundType}', resetting to default`);
47
+ userConfig.soundType = SOUND_TYPES.HARP;
48
+ migrated = true;
49
+ }
50
+
51
+ // Save migrated config back to file
52
+ if (migrated) {
53
+ try {
54
+ ensureConfigDirectory();
55
+ fs.writeFileSync(configPath, JSON.stringify({ ...defaultConfig, ...userConfig }, null, 2));
56
+ console.log('Configuration migrated successfully');
57
+ } catch (writeError) {
58
+ console.error('Failed to save migrated config:', writeError);
59
+ }
60
+ }
61
+
24
62
  return { ...defaultConfig, ...userConfig };
25
63
  } catch (error) {
26
64
  console.error('Error reading or parsing config file:', error);
@@ -28,4 +66,78 @@ function getConfig() {
28
66
  }
29
67
  }
30
68
 
31
- module.exports = { getConfig };
69
+ function getSoundPath(soundType) {
70
+ return path.join(soundsDir, `${soundType}.wav`);
71
+ }
72
+
73
+ function ensureConfigDirectory() {
74
+ const configDir = path.dirname(configPath);
75
+ if (!fs.existsSync(configDir)) {
76
+ fs.mkdirSync(configDir, { recursive: true });
77
+ }
78
+ }
79
+
80
+ function ensureSoundsDirectory() {
81
+ if (!fs.existsSync(soundsDir)) {
82
+ fs.mkdirSync(soundsDir, { recursive: true });
83
+ }
84
+ }
85
+
86
+ function cleanupLegacySoundFiles() {
87
+ // Clean up old sound files from legacy locations
88
+ const legacyPaths = [
89
+ // Legacy location 1: ~/.local/share/sounds/
90
+ {
91
+ dir: path.join(os.homedir(), '.local', 'share', 'sounds'),
92
+ files: ['claude-notification.wav', 'claude-notification-bell.wav']
93
+ },
94
+ // Add more legacy paths here if needed in future migrations
95
+ ];
96
+
97
+ let cleanedCount = 0;
98
+
99
+ legacyPaths.forEach(({ dir, files }) => {
100
+ if (fs.existsSync(dir)) {
101
+ files.forEach(file => {
102
+ const filePath = path.join(dir, file);
103
+ if (fs.existsSync(filePath)) {
104
+ try {
105
+ fs.unlinkSync(filePath);
106
+ console.log(`✅ Removed legacy sound file: ${filePath}`);
107
+ cleanedCount++;
108
+ } catch (error) {
109
+ console.error(`❌ Failed to remove legacy sound file ${filePath}:`, error);
110
+ }
111
+ }
112
+ });
113
+
114
+ // Try to remove empty directory if it only contained our files
115
+ try {
116
+ const remainingFiles = fs.readdirSync(dir);
117
+ if (remainingFiles.length === 0) {
118
+ fs.rmdirSync(dir);
119
+ console.log(`✅ Removed empty legacy directory: ${dir}`);
120
+ }
121
+ } catch (error) {
122
+ // Directory not empty or can't be removed, that's fine
123
+ }
124
+ }
125
+ });
126
+
127
+ if (cleanedCount > 0) {
128
+ console.log(`✅ Cleaned up ${cleanedCount} legacy sound file(s)`);
129
+ }
130
+
131
+ return cleanedCount;
132
+ }
133
+
134
+ module.exports = {
135
+ getConfig,
136
+ getSoundPath,
137
+ ensureConfigDirectory,
138
+ ensureSoundsDirectory,
139
+ cleanupLegacySoundFiles,
140
+ SOUND_TYPES,
141
+ soundsDir,
142
+ configPath
143
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delorenj/claude-notifications",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Delightful Notification for Claude Code",
5
5
  "main": "index.js",
6
6
  "bin": {