@delorenj/claude-notifications 1.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/README.md ADDED
@@ -0,0 +1,208 @@
1
+ # Claude Notifications ๐Ÿ””
2
+
3
+ Delightful audible notifications for Claude Code. Never alt+tab back to disappointment, thinking it was cranking away for 30-minutes on your task only to see a "Just double checking - you cool with this plan? Y/n"
4
+
5
+ ![Notification bar as seen in Ubuntu](claude-notification.png)
6
+
7
+ ## โšก Super Quick Install
8
+
9
+ ```bash
10
+ npm install -g @delorenj/claude-notifications
11
+ ```
12
+
13
+ That's it! ๐ŸŽ‰ The package will automatically:
14
+
15
+ - โœ… Install the notification system
16
+ - ๐ŸŽผ Generate a delightful notification scale
17
+ - ๐Ÿช Configure Claude Code stop hooks
18
+ - ๐Ÿงช Test the installation
19
+
20
+ ## Features
21
+
22
+ - ๐ŸŽต **Final Fantasy Dream Harp** - Classic C-D-E-G ascending/(optional)descending pattern
23
+ - ๐Ÿ”Š **Cross-Platform Audio** - Works on Linux and macOS
24
+ - ๐Ÿ–ฅ๏ธ **Desktop Notifications** - Visual notifications with Claude Code branding
25
+ - ๐Ÿช **Auto-Integration** - Automatically configures Claude Code hooks
26
+ - โšก **Zero Configuration** - Works out of the box
27
+ - ๐ŸŽจ **Customizable** - Easy to modify sounds and settings
28
+
29
+ ## Usage
30
+
31
+ After installation, Claude Code will begin notifying you when it finishes or is waiting on your response.
32
+
33
+ ### Manual Commands
34
+
35
+ ```bash
36
+ # Trigger notification manually
37
+ claude-notify
38
+
39
+ # Test the system
40
+ claude-notifications test
41
+
42
+ # Reinstall/repair
43
+ claude-notifications install
44
+
45
+ # Get help
46
+ claude-notifications help
47
+ ```
48
+
49
+ ## The Sound
50
+
51
+ - **Pattern**: C1-D1-E1-G1-C2-D2-E2-G2-C3-G2-E2-D2-C2-G1-E1-D1-C1
52
+ - **Duration**: ~2 seconds of dreamy notes
53
+
54
+ ## How It Works
55
+
56
+ The package automatically:
57
+
58
+ 1. **Detects Claude Code** - Finds your Claude Code configuration
59
+ 2. **Adds Stop Hook** - Configures the notification trigger
60
+ 3. **Creates Sound** - Generates the sound using `sox`
61
+ 4. **Sets Up Commands** - Installs `claude-notify` globally
62
+
63
+ ### Claude Code Integration
64
+
65
+ The installer automatically adds this to your Claude Code settings:
66
+
67
+ ```json
68
+ {
69
+ "hooks": {
70
+ "Stop": [
71
+ {
72
+ "matcher": "",
73
+ "hooks": [
74
+ {
75
+ "type": "command",
76
+ "command": "claude-notify"
77
+ }
78
+ ]
79
+ }
80
+ ]
81
+ }
82
+ }
83
+ ```
84
+
85
+ ## Requirements
86
+
87
+ - **Node.js** 14+ (you probably have this if you use Claude Code)
88
+ - **Linux or macOS** (Windows support coming soon)
89
+ - **Audio system** (PulseAudio, ALSA, or CoreAudio)
90
+
91
+ ### Auto-Installed Dependencies
92
+
93
+ The package will automatically install:
94
+
95
+ - `sox` for sound generation (Linux: apt/dnf, macOS: brew)
96
+ - `node-notifier` for desktop notifications
97
+
98
+ ## Customization
99
+
100
+ ### Change the Sound
101
+
102
+ Replace the generated sound file:
103
+
104
+ ```bash
105
+ # Find the sound file
106
+ ls ~/.local/share/sounds/claude-notification.wav
107
+
108
+ # Replace with your own
109
+ cp your-custom-sound.wav ~/.local/share/sounds/claude-notification.wav
110
+ ```
111
+
112
+ ### Create Custom Patterns
113
+
114
+ Use `sox` to create new victory fanfares:
115
+
116
+ > Note: Previously, a tedious chore I wouldn't recommend to a sane person. But now that we live in a fictional Blade Runner, Cyberpunk-inspired alternate timeline, just ask your robot buddy to do it!
117
+
118
+ ```bash
119
+ # Simple ascending scale
120
+ sox -n custom.wav synth 0.1 sine 261.63 : synth 0.1 sine 293.66 : synth 0.1 sine 329.63
121
+
122
+ # Your imagination is the limit!
123
+ ```
124
+
125
+ ## Troubleshooting
126
+
127
+ ### No Sound?
128
+
129
+ ```bash
130
+ # Test your audio system
131
+ paplay /usr/share/sounds/alsa/Front_Left.wav # Linux
132
+ afplay /System/Library/Sounds/Glass.aiff # macOS
133
+
134
+ # Reinstall
135
+ claude-notifications install
136
+ ```
137
+
138
+ ### No Notification?
139
+
140
+ ```bash
141
+ # Test manually
142
+ claude-notify
143
+
144
+ # Check Claude Code config
145
+ claude-notifications install # Will show config location
146
+ ```
147
+
148
+ ### Command Not Found?
149
+
150
+ ```bash
151
+ # Reinstall globally
152
+ npm install -g @delorenj/claude-notifications
153
+
154
+ # Or use npx
155
+ npx @delorenj/claude-notifications test
156
+ ```
157
+
158
+ ## Uninstall
159
+
160
+ ```bash
161
+ npm uninstall -g @delorenj/claude-notifications
162
+ ```
163
+
164
+ The uninstaller will clean up sound files and notify you about manual config cleanup.
165
+
166
+ ## Development
167
+
168
+ ### Local Development
169
+
170
+ ```bash
171
+ git clone https://github.com/delorenj/claude-notifications.git
172
+ cd claude-notifications
173
+ npm link
174
+ claude-notifications install
175
+ ```
176
+
177
+ ### Publishing
178
+
179
+ ```bash
180
+ npm version patch
181
+ npm publish
182
+ ```
183
+
184
+ ## Contributing
185
+
186
+ PRs welcome! Especially for:
187
+
188
+ - Windows support
189
+ - More sound patterns
190
+ - Better Claude Code detection
191
+ - macOS improvements
192
+
193
+ ## License
194
+
195
+ MIT License - Make it your own! ๐ŸŽต
196
+
197
+ ---
198
+
199
+ **Ready to level up your Claude Code experience?**
200
+
201
+ ```bash
202
+ npm install -g @delorenj/claude-notifications
203
+ ```
204
+
205
+ _Made with โค๏ธ for fellow developers of the world over.
206
+ but mostly for me, who alt-tabs away into the night leaving techno-breadcrumb trails of unanswered and unfinished Claude Code tasks._
207
+
208
+ ๐ŸŽฎโœจ **Enjoy!** โœจ๐ŸŽฎ
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync, spawn } = require("child_process");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+
8
+ const colors = {
9
+ red: "\x1b[31m",
10
+ green: "\x1b[32m",
11
+ yellow: "\x1b[33m",
12
+ blue: "\x1b[34m",
13
+ reset: "\x1b[0m",
14
+ };
15
+
16
+ function log(color, message) {
17
+ console.log(`${colors[color]}${message}${colors.reset}`);
18
+ }
19
+
20
+ function findClaudeCodeConfig() {
21
+ // Common Claude Code config locations
22
+ const possiblePaths = [
23
+ path.join(os.homedir(), ".claude", "config.json"),
24
+ path.join(os.homedir(), ".config", "claude", "config.json"),
25
+ path.join(os.homedir(), ".claude-code", "config.json"),
26
+ path.join(os.homedir(), ".config", "claude-code", "config.json"),
27
+ ];
28
+
29
+ for (const configPath of possiblePaths) {
30
+ if (fs.existsSync(configPath)) {
31
+ return configPath;
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+
37
+ function updateClaudeCodeConfig() {
38
+ const configPath = findClaudeCodeConfig();
39
+
40
+ if (!configPath) {
41
+ log("yellow", "โš ๏ธ Could not find Claude Code config file.");
42
+ log(
43
+ "blue",
44
+ "๐Ÿ’ก Please manually add the stop hook to your Claude Code settings:",
45
+ );
46
+ console.log(
47
+ JSON.stringify(
48
+ {
49
+ hooks: {
50
+ Stop: [
51
+ {
52
+ matcher: "",
53
+ hooks: [
54
+ {
55
+ type: "command",
56
+ command: "claude-notify",
57
+ },
58
+ ],
59
+ },
60
+ ],
61
+ },
62
+ },
63
+ null,
64
+ 2,
65
+ ),
66
+ );
67
+ return;
68
+ }
69
+
70
+ try {
71
+ let config = {};
72
+ if (fs.existsSync(configPath)) {
73
+ const configContent = fs.readFileSync(configPath, "utf8");
74
+ config = JSON.parse(configContent);
75
+ }
76
+
77
+ // Add or update hooks
78
+ if (!config.hooks) config.hooks = {};
79
+ if (!config.hooks.Stop) config.hooks.Stop = [];
80
+
81
+ // Check if our hook already exists
82
+ const existingHook = config.hooks.Stop.find(
83
+ (hook) =>
84
+ hook.hooks && hook.hooks.some((h) => h.command === "claude-notify"),
85
+ );
86
+
87
+ if (!existingHook) {
88
+ config.hooks.Stop.push({
89
+ matcher: "",
90
+ hooks: [
91
+ {
92
+ type: "command",
93
+ command: "claude-notify",
94
+ },
95
+ ],
96
+ });
97
+
98
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
99
+ log("green", "โœ… Added stop hook to Claude Code config!");
100
+ } else {
101
+ log("blue", "๐Ÿ“ Stop hook already exists in Claude Code config");
102
+ }
103
+ } catch (error) {
104
+ log("red", `โŒ Error updating Claude Code config: ${error.message}`);
105
+ }
106
+ }
107
+
108
+ 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
+ }
116
+
117
+ // Check if sox is available
118
+ try {
119
+ execSync("which sox", { stdio: "ignore" });
120
+ } catch (error) {
121
+ log("yellow", "โš ๏ธ sox not found. Installing...");
122
+ try {
123
+ if (process.platform === "linux") {
124
+ execSync("sudo apt update && sudo apt install -y sox", {
125
+ stdio: "inherit",
126
+ });
127
+ } else if (process.platform === "darwin") {
128
+ execSync("brew install sox", { stdio: "inherit" });
129
+ }
130
+ } catch (installError) {
131
+ log("red", "โŒ Could not install sox. Please install it manually.");
132
+ return false;
133
+ }
134
+ }
135
+
136
+ // Generate pleasant notification scale
137
+ log("blue", "๐ŸŽผ Generating a pleasant notification scale...");
138
+
139
+ const soxCommand =
140
+ `sox -n "${soundFile}" ` +
141
+ "synth 0.12 sine 523.25 fade 0.01 0.12 0.01 : " +
142
+ "synth 0.12 sine 587.33 fade 0.01 0.12 0.01 : " +
143
+ "synth 0.12 sine 659.25 fade 0.01 0.12 0.01 : " +
144
+ "synth 0.12 sine 783.99 fade 0.01 0.12 0.01 : " +
145
+ "synth 0.12 sine 1046.50 fade 0.01 0.12 0.01 : " +
146
+ "synth 0.12 sine 1174.66 fade 0.01 0.12 0.01 : " +
147
+ "synth 0.12 sine 1318.51 fade 0.01 0.12 0.01 : " +
148
+ "synth 0.12 sine 1567.98 fade 0.01 0.12 0.01 : " +
149
+ "synth 0.12 sine 2093.00 fade 0.01 0.12 0.01 : " +
150
+ "synth 0.12 sine 1567.98 fade 0.01 0.12 0.01 : " +
151
+ "synth 0.12 sine 1318.51 fade 0.01 0.12 0.01 : " +
152
+ "synth 0.12 sine 1174.66 fade 0.01 0.12 0.01 : " +
153
+ "synth 0.12 sine 1046.50 fade 0.01 0.12 0.01 : " +
154
+ "synth 0.12 sine 783.99 fade 0.01 0.12 0.01 : " +
155
+ "synth 0.12 sine 659.25 fade 0.01 0.12 0.01 : " +
156
+ "synth 0.12 sine 587.33 fade 0.01 0.12 0.01 : " +
157
+ "synth 0.12 sine 523.25 fade 0.01 0.12 0.01 " +
158
+ "vol 0.7";
159
+
160
+ try {
161
+ execSync(soxCommand, { stdio: "ignore" });
162
+ log("green", "โœ… Sound file created successfully!");
163
+ return true;
164
+ } catch (error) {
165
+ log("red", `โŒ Error creating sound file: ${error.message}`);
166
+ return false;
167
+ }
168
+ }
169
+
170
+ function main() {
171
+ const command = process.argv[2];
172
+
173
+ switch (command) {
174
+ case "install":
175
+ case undefined:
176
+ log("blue", "๐ŸŽต Installing Claude Notifications...");
177
+
178
+ if (createSoundFile()) {
179
+ updateClaudeCodeConfig();
180
+ log("green", "๐ŸŽ‰ Installation complete!");
181
+ log("blue", "๐Ÿงช Testing notification...");
182
+
183
+ // Test the notification
184
+ const testProcess = spawn(
185
+ "node",
186
+ [path.join(__dirname, "claude-notify.js")],
187
+ {
188
+ stdio: "inherit",
189
+ },
190
+ );
191
+
192
+ testProcess.on("close", () => {
193
+ log(
194
+ "green",
195
+ "โœ… Test complete! You should have heard a dreamy notification!",
196
+ );
197
+ console.log("");
198
+ log("blue", "Usage:");
199
+ console.log(
200
+ " claude-notify # Trigger notification manually",
201
+ );
202
+ console.log(" claude-notifications # This installer");
203
+ console.log("");
204
+ log(
205
+ "blue",
206
+ "Claude Code will now beckon you back with a pleasant notification when it finishes responses or is waiting for your input! ๐ŸŽฎ",
207
+ );
208
+ });
209
+ }
210
+ break;
211
+
212
+ case "test":
213
+ log("blue", "๐Ÿงช Testing notification...");
214
+ spawn("node", [path.join(__dirname, "claude-notify.js")], {
215
+ stdio: "inherit",
216
+ });
217
+ break;
218
+
219
+ case "uninstall":
220
+ log("blue", "๐Ÿ—‘๏ธ Uninstalling Claude Notifications...");
221
+
222
+ const soundFile = path.join(
223
+ os.homedir(),
224
+ ".local",
225
+ "share",
226
+ "sounds",
227
+ "claude-notification.wav",
228
+ );
229
+ if (fs.existsSync(soundFile)) {
230
+ fs.unlinkSync(soundFile);
231
+ log("green", "โœ… Removed sound file");
232
+ }
233
+
234
+ log(
235
+ "yellow",
236
+ "โš ๏ธ Please manually remove the stop hook from your Claude Code config",
237
+ );
238
+ log("green", "๐ŸŽ‰ Uninstallation complete!");
239
+ break;
240
+
241
+ case "help":
242
+ case "--help":
243
+ case "-h":
244
+ console.log("Notifications for Claude Code");
245
+ console.log("");
246
+ console.log("Usage:");
247
+ console.log(" claude-notifications [command]");
248
+ console.log("");
249
+ console.log("Commands:");
250
+ console.log(" install Install notifications (default)");
251
+ console.log(" test Test the notification");
252
+ console.log(" uninstall Remove notifications");
253
+ console.log(" help Show this help");
254
+ break;
255
+
256
+ default:
257
+ log("red", `โŒ Unknown command: ${command}`);
258
+ log("blue", 'Run "claude-notifications help" for usage information');
259
+ process.exit(1);
260
+ }
261
+ }
262
+
263
+ if (require.main === module) {
264
+ main();
265
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync, spawn } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+
8
+ function playSound() {
9
+ const soundFile = path.join(os.homedir(), '.local', 'share', 'sounds', 'claude-notification.wav');
10
+
11
+ if (!fs.existsSync(soundFile)) {
12
+ // Fallback to system bell
13
+ process.stdout.write('\x07');
14
+ return;
15
+ }
16
+
17
+ try {
18
+ // Try different audio players based on platform
19
+ if (process.platform === 'linux') {
20
+ // Try paplay first (PulseAudio)
21
+ try {
22
+ execSync(`paplay "${soundFile}"`, { stdio: 'ignore' });
23
+ return;
24
+ } catch (e) {
25
+ // Try aplay (ALSA)
26
+ try {
27
+ execSync(`aplay "${soundFile}"`, { stdio: 'ignore' });
28
+ return;
29
+ } catch (e2) {
30
+ // Try play (sox)
31
+ try {
32
+ execSync(`play "${soundFile}"`, { stdio: 'ignore' });
33
+ return;
34
+ } catch (e3) {
35
+ // Fallback to system bell
36
+ process.stdout.write('\x07');
37
+ }
38
+ }
39
+ }
40
+ } else if (process.platform === 'darwin') {
41
+ // macOS
42
+ try {
43
+ execSync(`afplay "${soundFile}"`, { stdio: 'ignore' });
44
+ return;
45
+ } catch (e) {
46
+ // Fallback to system bell
47
+ process.stdout.write('\x07');
48
+ }
49
+ } else {
50
+ // Other platforms - just system bell
51
+ process.stdout.write('\x07');
52
+ }
53
+ } catch (error) {
54
+ // Final fallback
55
+ process.stdout.write('\x07');
56
+ }
57
+ }
58
+
59
+ function showNotification() {
60
+ const notifier = require('node-notifier');
61
+
62
+ notifier.notify({
63
+ title: 'Claude Code',
64
+ message: 'Waiting for you...',
65
+ icon: path.join(os.homedir(), 'Pictures', 'claude.png'), // Will fallback gracefully if not found
66
+ sound: false, // We handle sound separately
67
+ urgency: 'critical'
68
+ });
69
+ }
70
+
71
+ function main() {
72
+ // Play sound in background
73
+ playSound();
74
+
75
+ // Show desktop notification
76
+ showNotification();
77
+ }
78
+
79
+ if (require.main === module) {
80
+ main();
81
+ }
Binary file
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "Stop": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "claude-notify"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
package/image.png ADDED
Binary file
package/index.js ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Main entry point for @delorenj/claude-notifications
4
+ // This allows the package to be used programmatically as well
5
+
6
+ const { spawn } = require('child_process');
7
+ const path = require('path');
8
+
9
+ function notify() {
10
+ return new Promise((resolve, reject) => {
11
+ const notifyProcess = spawn('node', [path.join(__dirname, 'bin', 'claude-notify.js')], {
12
+ stdio: 'inherit'
13
+ });
14
+
15
+ notifyProcess.on('close', (code) => {
16
+ if (code === 0) {
17
+ resolve();
18
+ } else {
19
+ reject(new Error(`Notification failed with code ${code}`));
20
+ }
21
+ });
22
+ });
23
+ }
24
+
25
+ function install() {
26
+ return new Promise((resolve, reject) => {
27
+ const installProcess = spawn('node', [path.join(__dirname, 'bin', 'claude-notifications.js'), 'install'], {
28
+ stdio: 'inherit'
29
+ });
30
+
31
+ installProcess.on('close', (code) => {
32
+ if (code === 0) {
33
+ resolve();
34
+ } else {
35
+ reject(new Error(`Installation failed with code ${code}`));
36
+ }
37
+ });
38
+ });
39
+ }
40
+
41
+ module.exports = {
42
+ notify,
43
+ install
44
+ };
45
+
46
+ // If called directly, run the CLI
47
+ if (require.main === module) {
48
+ const command = process.argv[2] || 'install';
49
+
50
+ if (command === 'notify') {
51
+ notify().catch(console.error);
52
+ } else {
53
+ install().catch(console.error);
54
+ }
55
+ }
package/install.sh ADDED
@@ -0,0 +1,161 @@
1
+ #!/bin/bash
2
+
3
+ # Claude Code Notifications Installer
4
+ # Installs a much needed notification system for Claude Code
5
+
6
+ set -e
7
+
8
+ echo "๐ŸŽต Installing Claude Code Notifications..."
9
+
10
+ # Colors for output
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ YELLOW='\033[1;33m'
14
+ BLUE='\033[0;34m'
15
+ NC='\033[0m' # No Color
16
+
17
+ # Check if we're on Linux
18
+ if [[ "$OSTYPE" != "linux-gnu"* ]]; then
19
+ echo -e "${RED}โŒ This installer is currently Linux-only. macOS support coming soon!${NC}"
20
+ exit 1
21
+ fi
22
+
23
+ # Create necessary directories
24
+ echo -e "${BLUE}๐Ÿ“ Creating directories...${NC}"
25
+ mkdir -p ~/.local/bin
26
+ mkdir -p ~/.local/share/sounds
27
+
28
+ # Check for required tools and install if needed
29
+ echo -e "${BLUE}๐Ÿ”ง Checking dependencies...${NC}"
30
+
31
+ # Check for sox
32
+ if ! command -v sox &>/dev/null; then
33
+ echo -e "${YELLOW}โš ๏ธ sox not found. Installing...${NC}"
34
+ if command -v apt &>/dev/null; then
35
+ sudo apt update && sudo apt install -y sox
36
+ elif command -v dnf &>/dev/null; then
37
+ sudo dnf install -y sox
38
+ elif command -v pacman &>/dev/null; then
39
+ sudo pacman -S sox
40
+ else
41
+ echo -e "${RED}โŒ Could not install sox. Please install it manually.${NC}"
42
+ exit 1
43
+ fi
44
+ fi
45
+
46
+ # Check for notify-send
47
+ if ! command -v notify-send &>/dev/null; then
48
+ echo -e "${YELLOW}โš ๏ธ notify-send not found. Installing...${NC}"
49
+ if command -v apt &>/dev/null; then
50
+ sudo apt install -y libnotify-bin
51
+ elif command -v dnf &>/dev/null; then
52
+ sudo dnf install -y libnotify
53
+ elif command -v pacman &>/dev/null; then
54
+ sudo pacman -S libnotify
55
+ fi
56
+ fi
57
+
58
+ # Generate that sweet Final Fantasy scale
59
+ echo -e "${BLUE}๐ŸŽผ Generating dreamy notification sound...${NC}"
60
+ sox -n ~/.local/share/sounds/claude-notification.wav \
61
+ synth 0.12 sine 523.25 fade 0.01 0.12 0.01 : \
62
+ synth 0.12 sine 587.33 fade 0.01 0.12 0.01 : \
63
+ synth 0.12 sine 659.25 fade 0.01 0.12 0.01 : \
64
+ synth 0.12 sine 783.99 fade 0.01 0.12 0.01 : \
65
+ synth 0.12 sine 1046.50 fade 0.01 0.12 0.01 : \
66
+ synth 0.12 sine 1174.66 fade 0.01 0.12 0.01 : \
67
+ synth 0.12 sine 1318.51 fade 0.01 0.12 0.01 : \
68
+ synth 0.12 sine 1567.98 fade 0.01 0.12 0.01 : \
69
+ synth 0.12 sine 2093.00 fade 0.01 0.12 0.01 :
70
+ # Uncomment for the longer, more nostalgic version
71
+ # synth 0.12 sine 1567.98 fade 0.01 0.12 0.01 : \
72
+ # synth 0.12 sine 1318.51 fade 0.01 0.12 0.01 : \
73
+ # synth 0.12 sine 1174.66 fade 0.01 0.12 0.01 : \
74
+ # synth 0.12 sine 1046.50 fade 0.01 0.12 0.01 : \
75
+ # synth 0.12 sine 783.99 fade 0.01 0.12 0.01 : \
76
+ # synth 0.12 sine 659.25 fade 0.01 0.12 0.01 : \
77
+ # synth 0.12 sine 587.33 fade 0.01 0.12 0.01 : \
78
+ # synth 0.12 sine 523.25 fade 0.01 0.12 0.01 \
79
+ vol 0.7
80
+
81
+ # Create the claude-notify script
82
+ echo -e "${BLUE}๐Ÿ“ Creating claude-notify script...${NC}"
83
+ cat >~/.local/bin/claude-notify <<'EOF'
84
+ #!/usr/bin/env bash
85
+
86
+ # Claude Code notification script
87
+ # Sends a desktop notification for Claude Code along with a dreamy notification sound
88
+
89
+ # Play notification sound (try multiple methods for compatibility)
90
+ play_sound() {
91
+ local sound_file="$HOME/.local/share/sounds/claude-notification.wav"
92
+
93
+ # Try paplay first (PulseAudio - most common on Ubuntu)
94
+ if command -v paplay >/dev/null 2>&1 && [[ -f "$sound_file" ]]; then
95
+ paplay "$sound_file" 2>/dev/null &
96
+ # Fallback to aplay (ALSA)
97
+ elif command -v aplay >/dev/null 2>&1 && [[ -f "$sound_file" ]]; then
98
+ aplay "$sound_file" 2>/dev/null &
99
+ # Fallback to system sounds
100
+ elif command -v paplay >/dev/null 2>&1; then
101
+ paplay /usr/share/sounds/alsa/Front_Left.wav 2>/dev/null &
102
+ # Final fallback to system bell
103
+ else
104
+ printf '\a'
105
+ fi
106
+ }
107
+
108
+ # Play sound in background
109
+ play_sound
110
+
111
+ # Send desktop notification
112
+ notify-send \
113
+ --app-name="Claude Code" \
114
+ --icon /home/clouedoc/Pictures/claude.png \
115
+ --urgency critical \
116
+ "Claude Code" \
117
+ "Waiting for you..."
118
+ EOF
119
+
120
+ # Make the script executable
121
+ chmod +x ~/.local/bin/claude-notify
122
+
123
+ # Add ~/.local/bin to PATH if not already there
124
+ if [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then
125
+ echo -e "${BLUE}๐Ÿ›ค๏ธ Adding ~/.local/bin to PATH...${NC}"
126
+
127
+ # Detect shell and add to appropriate config file
128
+ if [[ -n "$ZSH_VERSION" ]] || [[ "$SHELL" == *"zsh"* ]]; then
129
+ echo 'export PATH="$HOME/.local/bin:$PATH"' >>~/.zshrc
130
+ echo -e "${YELLOW}โš ๏ธ Added to ~/.zshrc - restart your terminal or run: source ~/.zshrc${NC}"
131
+ elif [[ -n "$BASH_VERSION" ]] || [[ "$SHELL" == *"bash"* ]]; then
132
+ echo 'export PATH="$HOME/.local/bin:$PATH"' >>~/.bashrc
133
+ echo -e "${YELLOW}โš ๏ธ Added to ~/.bashrc - restart your terminal or run: source ~/.bashrc${NC}"
134
+ else
135
+ echo -e "${YELLOW}โš ๏ธ Please add ~/.local/bin to your PATH manually${NC}"
136
+ fi
137
+ fi
138
+
139
+ # Test the installation
140
+ echo -e "${BLUE}๐Ÿงช Testing installation...${NC}"
141
+ if command -v claude-notify &>/dev/null; then
142
+ echo -e "${GREEN}โœ… claude-notify command is available!${NC}"
143
+ echo -e "${BLUE}๐Ÿ”Š Playing test notification...${NC}"
144
+ claude-notify
145
+ else
146
+ echo -e "${YELLOW}โš ๏ธ claude-notify not in PATH yet. Restart your terminal or source your shell config.${NC}"
147
+ fi
148
+
149
+ echo ""
150
+ echo -e "${GREEN}๐ŸŽ‰ Installation complete!${NC}"
151
+ echo ""
152
+ echo -e "${BLUE}Next steps:${NC}"
153
+ echo "1. Test the notification: ${YELLOW}claude-notify${NC}"
154
+ echo "2. Add to Claude Code settings (see README.md for JSON config)"
155
+ echo "3. Never alt-tab back to a unexpected Y/n again! ๐ŸŽต"
156
+ echo ""
157
+ echo -e "${BLUE}Configuration location:${NC}"
158
+ echo "โ€ข Script: ~/.local/bin/claude-notify"
159
+ echo "โ€ข Sound: ~/.local/share/sounds/claude-notification.wav"
160
+ echo ""
161
+ echo -e "${BLUE}Need help?${NC} Check the README.md for customization options!"
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@delorenj/claude-notifications",
3
+ "version": "1.0.0",
4
+ "description": "Delightful Notification for Claude Code",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "claude-notifications": "./bin/claude-notifications.js",
8
+ "claude-notify": "./bin/claude-notify.js"
9
+ },
10
+ "scripts": {
11
+ "postinstall": "node postinstall.js",
12
+ "preuninstall": "node preuninstall.js",
13
+ "test": "node test.js"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/delorenj/claude-notifications.git"
18
+ },
19
+ "keywords": [
20
+ "claude-code",
21
+ "notifications",
22
+ "adhd",
23
+ "audio",
24
+ "desktop-notifications",
25
+ "linux",
26
+ "productivity",
27
+ "cli"
28
+ ],
29
+ "author": "Jarad DeLorenzo",
30
+ "license": "MIT",
31
+ "bugs": {
32
+ "url": "https://github.com/delorenj/claude-notifications/issues"
33
+ },
34
+ "homepage": "https://github.com/delorenj/claude-notifications#readme",
35
+ "os": [
36
+ "linux",
37
+ "darwin"
38
+ ],
39
+ "engines": {
40
+ "node": ">=14.0.0"
41
+ },
42
+ "dependencies": {
43
+ "node-notifier": "^10.0.1",
44
+ "which": "^4.0.0"
45
+ },
46
+ "preferGlobal": true
47
+ }
package/postinstall.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("child_process");
4
+ const path = require("path");
5
+
6
+ console.log("๐ŸŽต Setting up Claude Notifications...");
7
+
8
+ // Run the main installer
9
+ const installer = spawn(
10
+ "node",
11
+ [path.join(__dirname, "bin", "claude-notifications.js"), "install"],
12
+ {
13
+ stdio: "inherit",
14
+ },
15
+ );
16
+
17
+ installer.on("close", (code) => {
18
+ if (code === 0) {
19
+ console.log("");
20
+ console.log("๐ŸŽ‰ Claude Notifications installed successfully!");
21
+ console.log("");
22
+ console.log("Usage:");
23
+ console.log(
24
+ " claude-notify # Trigger notification manually",
25
+ );
26
+ console.log(" claude-notifications test # Test the notification");
27
+ console.log("");
28
+ console.log(
29
+ "Claude Code will now beckon you back with a soothing scale! ๐ŸŽฎโœจ",
30
+ );
31
+ } else {
32
+ console.error("โŒ Installation failed");
33
+ process.exit(code);
34
+ }
35
+ });
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+
6
+ console.log('๐Ÿ—‘๏ธ Cleaning up Claude Notifications...');
7
+
8
+ // Run the uninstaller
9
+ const uninstaller = spawn('node', [path.join(__dirname, 'bin', 'claude-notifications.js'), 'uninstall'], {
10
+ stdio: 'inherit'
11
+ });
12
+
13
+ uninstaller.on('close', (code) => {
14
+ console.log('๐Ÿ‘‹ Thanks for using Claude Notifications!');
15
+ });
package/test.sh ADDED
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ # Test script for Claude Code Notifications
4
+
5
+ echo "๐Ÿงช Testing Claude Code Notifications..."
6
+
7
+ # Check if claude-notify exists
8
+ if command -v claude-notify &> /dev/null; then
9
+ echo "โœ… claude-notify command found"
10
+
11
+ # Test the notification
12
+ echo "๐Ÿ”Š Playing test notification..."
13
+ claude-notify
14
+
15
+ echo "โœ… Test complete! Did you hear the Final Fantasy fanfare?"
16
+ else
17
+ echo "โŒ claude-notify command not found"
18
+ echo "๐Ÿ’ก Try running ./install.sh first"
19
+ exit 1
20
+ fi
package/uninstall.sh ADDED
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+
3
+ # Claude Code Notifications Uninstaller
4
+
5
+ set -e
6
+
7
+ echo "๐Ÿ—‘๏ธ Uninstalling Claude Code Notifications..."
8
+
9
+ # Colors for output
10
+ RED='\033[0;31m'
11
+ GREEN='\033[0;32m'
12
+ YELLOW='\033[1;33m'
13
+ BLUE='\033[0;34m'
14
+ NC='\033[0m' # No Color
15
+
16
+ # Remove the script
17
+ if [[ -f ~/.local/bin/claude-notify ]]; then
18
+ rm ~/.local/bin/claude-notify
19
+ echo -e "${GREEN}โœ… Removed claude-notify script${NC}"
20
+ else
21
+ echo -e "${YELLOW}โš ๏ธ claude-notify script not found${NC}"
22
+ fi
23
+
24
+ # Remove the sound file
25
+ if [[ -f ~/.local/share/sounds/claude-notification.wav ]]; then
26
+ rm ~/.local/share/sounds/claude-notification.wav
27
+ echo -e "${GREEN}โœ… Removed notification sound${NC}"
28
+ else
29
+ echo -e "${YELLOW}โš ๏ธ Notification sound not found${NC}"
30
+ fi
31
+
32
+ echo ""
33
+ echo -e "${GREEN}๐ŸŽ‰ Uninstallation complete!${NC}"
34
+ echo ""
35
+ echo -e "${BLUE}Note:${NC} You may want to:"
36
+ echo "โ€ข Remove the Claude Code stop hook from your settings"
37
+ echo "โ€ข Remove PATH modification from your shell config (if added)"
38
+ echo ""
39
+ echo -e "${BLUE}Thanks for using Claude Code Notifications! ๐ŸŽต${NC}"