@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.
- package/.claude/checkpoints/1756392335.json +1 -0
- package/.claude/checkpoints/1756392341.json +1 -0
- package/.claude/checkpoints/1756392347.json +1 -0
- package/.claude/checkpoints/1756392376.json +1 -0
- package/.claude/checkpoints/1756392377.json +1 -0
- package/.claude/checkpoints/1756392386.json +1 -0
- package/.claude/checkpoints/1756392387.json +1 -0
- package/.claude/checkpoints/1756392398.json +1 -0
- package/.claude/checkpoints/1756392400.json +1 -0
- package/.claude/checkpoints/1756392427.json +1 -0
- package/.claude/checkpoints/1756392428.json +1 -0
- package/.claude/checkpoints/1756392486.json +1 -0
- package/.claude/checkpoints/1756392488.json +1 -0
- package/.claude/checkpoints/1756392558.json +1 -0
- package/.claude/checkpoints/1756392559.json +1 -0
- package/.claude/checkpoints/summary-session-20250828-105040.md +57 -0
- package/.claude/checkpoints/task-1756392207.json +1 -0
- package/.claude/checkpoints/task-1756392742.json +1 -0
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +54 -0
- package/.claude/commands/analysis/README.md +9 -0
- package/.claude/commands/analysis/bottleneck-detect.md +162 -0
- package/.claude/commands/analysis/performance-bottlenecks.md +59 -0
- package/.claude/commands/analysis/performance-report.md +25 -0
- package/.claude/commands/analysis/token-efficiency.md +45 -0
- package/.claude/commands/analysis/token-usage.md +25 -0
- package/.claude/commands/automation/README.md +9 -0
- package/.claude/commands/automation/auto-agent.md +122 -0
- package/.claude/commands/automation/self-healing.md +106 -0
- package/.claude/commands/automation/session-memory.md +90 -0
- package/.claude/commands/automation/smart-agents.md +73 -0
- package/.claude/commands/automation/smart-spawn.md +25 -0
- package/.claude/commands/automation/workflow-select.md +25 -0
- package/.claude/commands/coordination/README.md +9 -0
- package/.claude/commands/coordination/agent-spawn.md +25 -0
- package/.claude/commands/coordination/init.md +44 -0
- package/.claude/commands/coordination/orchestrate.md +43 -0
- package/.claude/commands/coordination/spawn.md +45 -0
- package/.claude/commands/coordination/swarm-init.md +85 -0
- package/.claude/commands/coordination/task-orchestrate.md +25 -0
- package/.claude/commands/github/README.md +11 -0
- package/.claude/commands/github/code-review-swarm.md +514 -0
- package/.claude/commands/github/code-review.md +25 -0
- package/.claude/commands/github/github-modes.md +147 -0
- package/.claude/commands/github/github-swarm.md +121 -0
- package/.claude/commands/github/issue-tracker.md +292 -0
- package/.claude/commands/github/issue-triage.md +25 -0
- package/.claude/commands/github/multi-repo-swarm.md +519 -0
- package/.claude/commands/github/pr-enhance.md +26 -0
- package/.claude/commands/github/pr-manager.md +170 -0
- package/.claude/commands/github/project-board-sync.md +471 -0
- package/.claude/commands/github/release-manager.md +338 -0
- package/.claude/commands/github/release-swarm.md +544 -0
- package/.claude/commands/github/repo-analyze.md +25 -0
- package/.claude/commands/github/repo-architect.md +367 -0
- package/.claude/commands/github/swarm-issue.md +482 -0
- package/.claude/commands/github/swarm-pr.md +285 -0
- package/.claude/commands/github/sync-coordinator.md +301 -0
- package/.claude/commands/github/workflow-automation.md +442 -0
- package/.claude/commands/hooks/README.md +11 -0
- package/.claude/commands/hooks/overview.md +58 -0
- package/.claude/commands/hooks/post-edit.md +117 -0
- package/.claude/commands/hooks/post-task.md +112 -0
- package/.claude/commands/hooks/pre-edit.md +113 -0
- package/.claude/commands/hooks/pre-task.md +111 -0
- package/.claude/commands/hooks/session-end.md +118 -0
- package/.claude/commands/hooks/setup.md +103 -0
- package/.claude/commands/memory/README.md +9 -0
- package/.claude/commands/memory/memory-persist.md +25 -0
- package/.claude/commands/memory/memory-search.md +25 -0
- package/.claude/commands/memory/memory-usage.md +25 -0
- package/.claude/commands/memory/neural.md +47 -0
- package/.claude/commands/memory/usage.md +46 -0
- package/.claude/commands/monitoring/README.md +9 -0
- package/.claude/commands/monitoring/agent-metrics.md +25 -0
- package/.claude/commands/monitoring/agents.md +44 -0
- package/.claude/commands/monitoring/real-time-view.md +25 -0
- package/.claude/commands/monitoring/status.md +46 -0
- package/.claude/commands/monitoring/swarm-monitor.md +25 -0
- package/.claude/commands/optimization/README.md +9 -0
- package/.claude/commands/optimization/auto-topology.md +62 -0
- package/.claude/commands/optimization/cache-manage.md +25 -0
- package/.claude/commands/optimization/parallel-execute.md +25 -0
- package/.claude/commands/optimization/parallel-execution.md +50 -0
- package/.claude/commands/optimization/topology-optimize.md +25 -0
- package/.claude/commands/pair/README.md +261 -0
- package/.claude/commands/pair/commands.md +546 -0
- package/.claude/commands/pair/config.md +510 -0
- package/.claude/commands/pair/examples.md +512 -0
- package/.claude/commands/pair/modes.md +348 -0
- package/.claude/commands/pair/session.md +407 -0
- package/.claude/commands/pair/start.md +209 -0
- package/.claude/commands/sparc/analyzer.md +52 -0
- package/.claude/commands/sparc/architect.md +53 -0
- package/.claude/commands/sparc/batch-executor.md +54 -0
- package/.claude/commands/sparc/coder.md +54 -0
- package/.claude/commands/sparc/debugger.md +54 -0
- package/.claude/commands/sparc/designer.md +53 -0
- package/.claude/commands/sparc/documenter.md +54 -0
- package/.claude/commands/sparc/innovator.md +54 -0
- package/.claude/commands/sparc/memory-manager.md +54 -0
- package/.claude/commands/sparc/optimizer.md +54 -0
- package/.claude/commands/sparc/orchestrator.md +132 -0
- package/.claude/commands/sparc/researcher.md +54 -0
- package/.claude/commands/sparc/reviewer.md +54 -0
- package/.claude/commands/sparc/sparc-modes.md +174 -0
- package/.claude/commands/sparc/swarm-coordinator.md +54 -0
- package/.claude/commands/sparc/tdd.md +54 -0
- package/.claude/commands/sparc/tester.md +54 -0
- package/.claude/commands/sparc/workflow-manager.md +54 -0
- package/.claude/commands/stream-chain/pipeline.md +121 -0
- package/.claude/commands/stream-chain/run.md +70 -0
- package/.claude/commands/swarm/analysis.md +95 -0
- package/.claude/commands/swarm/development.md +96 -0
- package/.claude/commands/swarm/examples.md +168 -0
- package/.claude/commands/swarm/maintenance.md +102 -0
- package/.claude/commands/swarm/optimization.md +117 -0
- package/.claude/commands/swarm/research.md +136 -0
- package/.claude/commands/swarm/testing.md +131 -0
- package/.claude/commands/training/README.md +9 -0
- package/.claude/commands/training/model-update.md +25 -0
- package/.claude/commands/training/neural-patterns.md +74 -0
- package/.claude/commands/training/neural-train.md +25 -0
- package/.claude/commands/training/pattern-learn.md +25 -0
- package/.claude/commands/training/specialization.md +63 -0
- package/.claude/commands/truth/start.md +143 -0
- package/.claude/commands/verify/check.md +50 -0
- package/.claude/commands/verify/start.md +128 -0
- package/.claude/commands/workflows/README.md +9 -0
- package/.claude/commands/workflows/development.md +78 -0
- package/.claude/commands/workflows/research.md +63 -0
- package/.claude/commands/workflows/workflow-create.md +25 -0
- package/.claude/commands/workflows/workflow-execute.md +25 -0
- package/.claude/commands/workflows/workflow-export.md +25 -0
- package/.claude/config.json +36 -0
- package/.claude/settings.json +162 -0
- package/.claude-flow/metrics/agent-metrics.json +1 -0
- package/.claude-flow/metrics/performance.json +9 -0
- package/.claude-flow/metrics/system-metrics.json +230 -0
- package/.claude-flow/metrics/task-metrics.json +10 -0
- package/FIXES.md +75 -0
- package/README.md +19 -2
- package/bin/claude-notifications.js +145 -46
- package/bin/claude-notify.js +84 -4
- package/lib/config.js +113 -1
- package/package.json +1 -1
- package/test-results.md +163 -0
- package/test.sh +1 -1
- 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
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
142
|
-
fs.
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
321
|
+
testProcess.on("close", (code) => {
|
|
322
|
+
if (code === 0) {
|
|
233
323
|
log(
|
|
234
324
|
"green",
|
|
235
|
-
"✅ Test complete! You should have heard a
|
|
325
|
+
"✅ Test complete! You should have heard a delightful notification!",
|
|
236
326
|
);
|
|
237
|
-
|
|
238
|
-
log("
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
"
|
|
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;
|
package/bin/claude-notify.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|