@delorenj/claude-notifications 1.0.2 โ 1.1.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/.github/workflows/npm-publish-github-packages.yml +36 -0
- package/README.md +35 -0
- package/bin/claude-notifications.js +61 -21
- package/bin/claude-notify.js +56 -23
- package/lib/config.js +31 -0
- package/mise.toml +3 -0
- package/package.json +1 -1
- package/ruv-swarm-mcp.db +0 -0
- package/install.sh +0 -161
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 20
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm test
|
|
20
|
+
|
|
21
|
+
publish-gpr:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: read
|
|
26
|
+
packages: write
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: 20
|
|
32
|
+
registry-url: https://npm.pkg.github.com/
|
|
33
|
+
- run: npm ci
|
|
34
|
+
- run: npm publish
|
|
35
|
+
env:
|
|
36
|
+
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ That's it! ๐ The package will automatically:
|
|
|
24
24
|
- ๐ฅ๏ธ **Desktop Notifications** - Visual notifications with Claude Code branding
|
|
25
25
|
- ๐ช **Auto-Integration** - Automatically configures Claude Code hooks
|
|
26
26
|
- โก **Zero Configuration** - Works out of the box
|
|
27
|
+
- webhook **Webhook Support** - Trigger a webhook in addition to or instead of the sound
|
|
27
28
|
- ๐จ **Customizable** - Easy to modify sounds and settings
|
|
28
29
|
|
|
29
30
|
## Usage
|
|
@@ -109,6 +110,40 @@ ls ~/.local/share/sounds/claude-notification.wav
|
|
|
109
110
|
cp your-custom-sound.wav ~/.local/share/sounds/claude-notification.wav
|
|
110
111
|
```
|
|
111
112
|
|
|
113
|
+
### Configure Webhooks
|
|
114
|
+
|
|
115
|
+
You can configure a webhook to be triggered when a notification occurs. This is useful for integrating with other services, such as IFTTT, Zapier, or a custom server.
|
|
116
|
+
|
|
117
|
+
Create a configuration file at `~/.config/claude-notifications/settings.json`.
|
|
118
|
+
|
|
119
|
+
**Example `settings.json`:**
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"sound": true,
|
|
124
|
+
"webhook": {
|
|
125
|
+
"enabled": true,
|
|
126
|
+
"url": "https://maker.ifttt.com/trigger/claude_notification/with/key/YOUR_KEY",
|
|
127
|
+
"replaceSound": false
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Configuration Options:**
|
|
133
|
+
|
|
134
|
+
- `sound`: (boolean) Whether to play the notification sound. Defaults to `true`.
|
|
135
|
+
- `webhook.enabled`: (boolean) Whether to trigger the webhook. Defaults to `false`.
|
|
136
|
+
- `webhook.url`: (string) The URL to send the POST request to.
|
|
137
|
+
- `webhook.replaceSound`: (boolean) If `true`, the sound will not play when a webhook is triggered. Defaults to `false`.
|
|
138
|
+
|
|
139
|
+
The webhook will be sent as a `POST` request with a JSON payload:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"message": "Claude is waiting for you..."
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
112
147
|
### Create Custom Patterns
|
|
113
148
|
|
|
114
149
|
Use `sox` to create new victory fanfares:
|
|
@@ -136,33 +136,73 @@ function createSoundFile() {
|
|
|
136
136
|
// Generate pleasant notification scale
|
|
137
137
|
log("blue", "๐ผ Generating a pleasant notification scale...");
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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";
|
|
139
|
+
// Create individual note files first (safer approach)
|
|
140
|
+
const tempDir = path.join(os.tmpdir(), "claude-notifications");
|
|
141
|
+
if (!fs.existsSync(tempDir)) {
|
|
142
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const notes = [
|
|
146
|
+
{ freq: 523.25, name: "C5" }, // C5
|
|
147
|
+
{ freq: 587.33, name: "D5" }, // D5
|
|
148
|
+
{ freq: 659.25, name: "E5" }, // E5
|
|
149
|
+
{ freq: 783.99, name: "G5" }, // G5
|
|
150
|
+
{ freq: 1046.5, name: "C6" }, // C6
|
|
151
|
+
{ freq: 1174.66, name: "D6" }, // D6
|
|
152
|
+
{ freq: 1318.51, name: "E6" }, // E6
|
|
153
|
+
{ freq: 1567.98, name: "G6" }, // G6
|
|
154
|
+
{ freq: 2093.0, name: "C7" }, // C7
|
|
155
|
+
];
|
|
159
156
|
|
|
160
157
|
try {
|
|
161
|
-
|
|
158
|
+
const noteFiles = [];
|
|
159
|
+
|
|
160
|
+
// Generate each note individually (much safer)
|
|
161
|
+
for (let i = 0; i < notes.length; i++) {
|
|
162
|
+
const noteFile = path.join(tempDir, `note_${i}.wav`);
|
|
163
|
+
const noteCommand = `sox -n "${noteFile}" synth 0.08 sine ${notes[i].freq} fade 0.01 0.08 0.01 vol 0.7`;
|
|
164
|
+
execSync(noteCommand, { stdio: "ignore", timeout: 5000 }); // 5 second timeout per note
|
|
165
|
+
noteFiles.push(noteFile);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Concatenate all notes into final file
|
|
169
|
+
const concatCommand = `sox ${noteFiles.map((f) => `"${f}"`).join(" ")} "${soundFile}"`;
|
|
170
|
+
execSync(concatCommand, { stdio: "ignore", timeout: 10000 }); // 10 second timeout for concat
|
|
171
|
+
|
|
172
|
+
// Clean up temp files
|
|
173
|
+
noteFiles.forEach((file) => {
|
|
174
|
+
if (fs.existsSync(file)) {
|
|
175
|
+
fs.unlinkSync(file);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Remove temp directory if empty
|
|
180
|
+
try {
|
|
181
|
+
fs.rmdirSync(tempDir);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
// Directory might not be empty, that's ok
|
|
184
|
+
}
|
|
185
|
+
|
|
162
186
|
log("green", "โ
Sound file created successfully!");
|
|
163
187
|
return true;
|
|
164
188
|
} catch (error) {
|
|
165
189
|
log("red", `โ Error creating sound file: ${error.message}`);
|
|
190
|
+
|
|
191
|
+
// Clean up any temp files on error
|
|
192
|
+
try {
|
|
193
|
+
const tempFiles = fs
|
|
194
|
+
.readdirSync(tempDir)
|
|
195
|
+
.filter((f) => f.startsWith("note_"));
|
|
196
|
+
tempFiles.forEach((file) => {
|
|
197
|
+
const filePath = path.join(tempDir, file);
|
|
198
|
+
if (fs.existsSync(filePath)) {
|
|
199
|
+
fs.unlinkSync(filePath);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
} catch (cleanupError) {
|
|
203
|
+
// Ignore cleanup errors
|
|
204
|
+
}
|
|
205
|
+
|
|
166
206
|
return false;
|
|
167
207
|
}
|
|
168
208
|
}
|
package/bin/claude-notify.js
CHANGED
|
@@ -1,79 +1,112 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { execSync
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
|
+
const http = require('http');
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const { getConfig } = require('../lib/config');
|
|
10
|
+
|
|
11
|
+
const config = getConfig();
|
|
7
12
|
|
|
8
13
|
function playSound() {
|
|
14
|
+
if (!config.sound) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
const soundFile = path.join(os.homedir(), '.local', 'share', 'sounds', 'claude-notification.wav');
|
|
10
19
|
|
|
11
20
|
if (!fs.existsSync(soundFile)) {
|
|
12
|
-
// Fallback to system bell
|
|
13
21
|
process.stdout.write('\x07');
|
|
14
22
|
return;
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
try {
|
|
18
|
-
// Try different audio players based on platform
|
|
19
26
|
if (process.platform === 'linux') {
|
|
20
|
-
// Try paplay first (PulseAudio)
|
|
21
27
|
try {
|
|
22
28
|
execSync(`paplay "${soundFile}"`, { stdio: 'ignore' });
|
|
23
|
-
return;
|
|
24
29
|
} catch (e) {
|
|
25
|
-
// Try aplay (ALSA)
|
|
26
30
|
try {
|
|
27
31
|
execSync(`aplay "${soundFile}"`, { stdio: 'ignore' });
|
|
28
|
-
return;
|
|
29
32
|
} catch (e2) {
|
|
30
|
-
// Try play (sox)
|
|
31
33
|
try {
|
|
32
34
|
execSync(`play "${soundFile}"`, { stdio: 'ignore' });
|
|
33
|
-
return;
|
|
34
35
|
} catch (e3) {
|
|
35
|
-
// Fallback to system bell
|
|
36
36
|
process.stdout.write('\x07');
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
} else if (process.platform === 'darwin') {
|
|
41
|
-
// macOS
|
|
42
41
|
try {
|
|
43
42
|
execSync(`afplay "${soundFile}"`, { stdio: 'ignore' });
|
|
44
|
-
return;
|
|
45
43
|
} catch (e) {
|
|
46
|
-
// Fallback to system bell
|
|
47
44
|
process.stdout.write('\x07');
|
|
48
45
|
}
|
|
49
46
|
} else {
|
|
50
|
-
// Other platforms - just system bell
|
|
51
47
|
process.stdout.write('\x07');
|
|
52
48
|
}
|
|
53
49
|
} catch (error) {
|
|
54
|
-
// Final fallback
|
|
55
50
|
process.stdout.write('\x07');
|
|
56
51
|
}
|
|
57
52
|
}
|
|
58
53
|
|
|
54
|
+
function triggerWebhook() {
|
|
55
|
+
if (!config.webhook || !config.webhook.enabled || !config.webhook.url) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { url } = config.webhook;
|
|
60
|
+
const data = JSON.stringify({ message: 'Claude is waiting for you...' });
|
|
61
|
+
|
|
62
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
63
|
+
|
|
64
|
+
const options = {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'Content-Length': data.length
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const req = protocol.request(url, options, (res) => {
|
|
73
|
+
// We don't really care about the response, but it's good practice to handle it
|
|
74
|
+
res.on('data', () => {});
|
|
75
|
+
res.on('end', () => {});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
req.on('error', (error) => {
|
|
79
|
+
console.error('Error triggering webhook:', error);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
req.write(data);
|
|
83
|
+
req.end();
|
|
84
|
+
}
|
|
85
|
+
|
|
59
86
|
function showNotification() {
|
|
60
87
|
const notifier = require('node-notifier');
|
|
61
88
|
|
|
62
89
|
notifier.notify({
|
|
63
90
|
title: 'Claude Code',
|
|
64
91
|
message: 'Waiting for you...',
|
|
65
|
-
icon: path.join(os.homedir(), 'Pictures', 'claude.png'),
|
|
66
|
-
sound: false,
|
|
67
|
-
urgency: 'critical'
|
|
92
|
+
icon: path.join(os.homedir(), 'Pictures', 'claude.png'),
|
|
93
|
+
sound: false,
|
|
94
|
+
urgency: 'critical',
|
|
95
|
+
id: 'claude-code-notification',
|
|
96
|
+
replaceId: 'claude-code-notification'
|
|
68
97
|
});
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
function main() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
if (config.webhook.enabled) {
|
|
102
|
+
triggerWebhook();
|
|
103
|
+
if (!config.webhook.replaceSound) {
|
|
104
|
+
playSound();
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
playSound();
|
|
108
|
+
showNotification();
|
|
109
|
+
}
|
|
77
110
|
}
|
|
78
111
|
|
|
79
112
|
if (require.main === module) {
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const configPath = path.join(os.homedir(), '.config', 'claude-notifications', 'settings.json');
|
|
6
|
+
|
|
7
|
+
function getConfig() {
|
|
8
|
+
const defaultConfig = {
|
|
9
|
+
sound: true,
|
|
10
|
+
webhook: {
|
|
11
|
+
enabled: false,
|
|
12
|
+
url: null,
|
|
13
|
+
replaceSound: false
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(configPath)) {
|
|
18
|
+
return defaultConfig;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
23
|
+
const userConfig = JSON.parse(configContent);
|
|
24
|
+
return { ...defaultConfig, ...userConfig };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Error reading or parsing config file:', error);
|
|
27
|
+
return defaultConfig;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { getConfig };
|
package/mise.toml
ADDED
package/package.json
CHANGED
package/ruv-swarm-mcp.db
ADDED
|
Binary file
|
package/install.sh
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
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!"
|