claude-notification-plugin 1.0.44 → 1.0.49
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-plugin/plugin.json +1 -1
- package/README.md +14 -8
- package/bin/install.js +22 -2
- package/notifier/notifier.js +181 -39
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# claude-notification-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cross-platform notifications for Claude Code task completion. Sends alerts to Telegram and desktop (Windows, macOS, Linux) when Claude finishes working.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- Desktop notifications (Windows toast, macOS Notification Center, Linux notify-send)
|
|
8
8
|
- Telegram bot messages with auto-delete
|
|
9
|
-
- Sound alert (
|
|
10
|
-
- Voice announcement with number-to-words and pluralization (EN, RU)
|
|
9
|
+
- Sound alert (Windows: PowerShell, macOS: afplay, Linux: paplay/aplay)
|
|
10
|
+
- Voice announcement with number-to-words and pluralization (EN, RU) (Windows: SAPI, macOS: say, Linux: spd-say/espeak)
|
|
11
11
|
- Separate notifications for task completion and waiting-for-input events
|
|
12
12
|
- Skips short tasks (< 15s by default)
|
|
13
13
|
- Granular per-channel enable/disable (globally and per-project)
|
|
@@ -22,8 +22,12 @@ Add the marketplace and install:
|
|
|
22
22
|
```shell
|
|
23
23
|
/plugin marketplace add Bazilio-san/claude-plugins
|
|
24
24
|
/plugin install claude-notification-plugin@bazilio-plugins
|
|
25
|
+
/reload-plugins
|
|
26
|
+
/claude-notification-plugin:setup
|
|
25
27
|
```
|
|
26
28
|
|
|
29
|
+
For a detailed visual walkthrough, see [step-by-step installation guide with screenshots](INSTALL_MARKETPLACE_AND_PLUGIN.md).
|
|
30
|
+
|
|
27
31
|
Or load directly for testing:
|
|
28
32
|
|
|
29
33
|
```bash
|
|
@@ -67,12 +71,12 @@ Config file: `~/.claude/notifier.config.json`
|
|
|
67
71
|
"deleteAfterHours": 24,
|
|
68
72
|
"includeLastCcMessageInTelegram": true
|
|
69
73
|
},
|
|
70
|
-
"
|
|
74
|
+
"desktopNotification": {
|
|
71
75
|
"enabled": true
|
|
72
76
|
},
|
|
73
77
|
"sound": {
|
|
74
78
|
"enabled": true,
|
|
75
|
-
"file": "
|
|
79
|
+
"file": ""
|
|
76
80
|
},
|
|
77
81
|
"voice": {
|
|
78
82
|
"enabled": true
|
|
@@ -85,6 +89,8 @@ Config file: `~/.claude/notifier.config.json`
|
|
|
85
89
|
|
|
86
90
|
Each channel has an `enabled` flag (`true`/`false`) for global control.
|
|
87
91
|
|
|
92
|
+
`sound.file` — path to a sound file. Leave empty for platform default (Windows: `C:/Windows/Media/notify.wav`, macOS: `/System/Library/Sounds/Glass.aiff`, Linux: `/usr/share/sounds/freedesktop/stereo/complete.oga`).
|
|
93
|
+
|
|
88
94
|
`deleteAfterHours` — auto-delete old Telegram messages after the specified number of hours (default: `24`, set `0` to disable).
|
|
89
95
|
|
|
90
96
|
`includeLastCcMessageInTelegram` — append Claude's last assistant message to the Telegram notification (default: `true`). Only affects Telegram, not Windows toast notifications. Long messages are truncated to 3500 characters.
|
|
@@ -102,7 +108,7 @@ These env vars override the global config per channel (`"1"` = on, `"0"` = off):
|
|
|
102
108
|
| Variable | Channel |
|
|
103
109
|
|--------------------------|----------------------------|
|
|
104
110
|
| `CLAUDE_NOTIFY_TELEGRAM` | Telegram messages |
|
|
105
|
-
| `
|
|
111
|
+
| `CLAUDE_NOTIFY_DESKTOP` | Desktop notifications |
|
|
106
112
|
| `CLAUDE_NOTIFY_SOUND` | Sound alert |
|
|
107
113
|
| `CLAUDE_NOTIFY_VOICE` | Voice announcement (TTS) |
|
|
108
114
|
| `CLAUDE_NOTIFY_WAITING` | Waiting-for-input events |
|
|
@@ -118,7 +124,7 @@ Add to `.claude/settings.local.json` in the project root to control channels per
|
|
|
118
124
|
"env": {
|
|
119
125
|
"DISABLE_CLAUDE_NOTIFIER": 0,
|
|
120
126
|
"CLAUDE_NOTIFY_TELEGRAM": 1,
|
|
121
|
-
"
|
|
127
|
+
"CLAUDE_NOTIFY_DESKTOP": 1,
|
|
122
128
|
"CLAUDE_NOTIFY_SOUND": 1,
|
|
123
129
|
"CLAUDE_NOTIFY_VOICE": 1,
|
|
124
130
|
"CLAUDE_NOTIFY_WAITING": 1,
|
package/bin/install.js
CHANGED
|
@@ -136,6 +136,14 @@ async function main () {
|
|
|
136
136
|
// Create / update config
|
|
137
137
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
138
138
|
|
|
139
|
+
const platform = process.platform;
|
|
140
|
+
let defaultSoundFile;
|
|
141
|
+
switch (platform) {
|
|
142
|
+
case 'darwin': defaultSoundFile = '/System/Library/Sounds/Glass.aiff'; break;
|
|
143
|
+
case 'linux': defaultSoundFile = '/usr/share/sounds/freedesktop/stereo/complete.oga'; break;
|
|
144
|
+
default: defaultSoundFile = 'C:/Windows/Media/notify.wav';
|
|
145
|
+
}
|
|
146
|
+
|
|
139
147
|
const defaults = {
|
|
140
148
|
telegram: {
|
|
141
149
|
enabled: true,
|
|
@@ -143,12 +151,12 @@ async function main () {
|
|
|
143
151
|
chatId: '',
|
|
144
152
|
deleteAfterHours: 24,
|
|
145
153
|
},
|
|
146
|
-
|
|
154
|
+
desktopNotification: {
|
|
147
155
|
enabled: true,
|
|
148
156
|
},
|
|
149
157
|
sound: {
|
|
150
158
|
enabled: true,
|
|
151
|
-
file:
|
|
159
|
+
file: defaultSoundFile,
|
|
152
160
|
},
|
|
153
161
|
voice: {
|
|
154
162
|
enabled: true,
|
|
@@ -213,6 +221,18 @@ async function main () {
|
|
|
213
221
|
console.log('Telegram: not configured (edit config later)');
|
|
214
222
|
}
|
|
215
223
|
|
|
224
|
+
if (platform === 'darwin') {
|
|
225
|
+
console.log('');
|
|
226
|
+
console.log('Tip: install terminal-notifier for better macOS notifications:');
|
|
227
|
+
console.log(' brew install terminal-notifier');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (platform === 'linux') {
|
|
231
|
+
console.log('');
|
|
232
|
+
console.log('Tip: for voice announcements, install espeak:');
|
|
233
|
+
console.log(' sudo apt install espeak');
|
|
234
|
+
}
|
|
235
|
+
|
|
216
236
|
console.log('');
|
|
217
237
|
console.log('To disable per project, add to .claude/settings.local.json:');
|
|
218
238
|
console.log(' { "env": { "DISABLE_CLAUDE_NOTIFIER": "1" } }');
|
package/notifier/notifier.js
CHANGED
|
@@ -10,6 +10,22 @@ import { spawn } from 'child_process';
|
|
|
10
10
|
// CONFIG
|
|
11
11
|
// ----------------------
|
|
12
12
|
|
|
13
|
+
const PLATFORM = process.platform; // 'win32' | 'darwin' | 'linux'
|
|
14
|
+
|
|
15
|
+
function getDefaultSoundFile () {
|
|
16
|
+
switch (PLATFORM) {
|
|
17
|
+
case 'darwin': return '/System/Library/Sounds/Glass.aiff';
|
|
18
|
+
case 'linux': return '/usr/share/sounds/freedesktop/stereo/complete.oga';
|
|
19
|
+
default: return 'C:/Windows/Media/notify.wav';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function debugLog (config, ...args) {
|
|
24
|
+
if (config.debug) {
|
|
25
|
+
console.error('[claude-notifier]', ...args);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
13
29
|
function loadConfig () {
|
|
14
30
|
const configPath = path.join(os.homedir(), '.claude', 'notifier.config.json');
|
|
15
31
|
|
|
@@ -21,12 +37,12 @@ function loadConfig () {
|
|
|
21
37
|
deleteAfterHours: 24,
|
|
22
38
|
includeLastCcMessageInTelegram: true,
|
|
23
39
|
},
|
|
24
|
-
|
|
40
|
+
desktopNotification: {
|
|
25
41
|
enabled: true,
|
|
26
42
|
},
|
|
27
43
|
sound: {
|
|
28
44
|
enabled: true,
|
|
29
|
-
file:
|
|
45
|
+
file: getDefaultSoundFile(),
|
|
30
46
|
},
|
|
31
47
|
voice: {
|
|
32
48
|
enabled: true,
|
|
@@ -43,8 +59,8 @@ function loadConfig () {
|
|
|
43
59
|
if (user.telegram) {
|
|
44
60
|
config.telegram = { ...config.telegram, ...user.telegram };
|
|
45
61
|
}
|
|
46
|
-
if (user.
|
|
47
|
-
config.
|
|
62
|
+
if (user.desktopNotification) {
|
|
63
|
+
config.desktopNotification = { ...config.desktopNotification, ...user.desktopNotification };
|
|
48
64
|
}
|
|
49
65
|
if (user.sound) {
|
|
50
66
|
config.sound = { ...config.sound, ...user.sound };
|
|
@@ -77,8 +93,8 @@ function loadConfig () {
|
|
|
77
93
|
if (process.env.CLAUDE_NOTIFY_TELEGRAM !== undefined) {
|
|
78
94
|
config.telegram.enabled = process.env.CLAUDE_NOTIFY_TELEGRAM === '1';
|
|
79
95
|
}
|
|
80
|
-
if (process.env.
|
|
81
|
-
config.
|
|
96
|
+
if (process.env.CLAUDE_NOTIFY_DESKTOP !== undefined) {
|
|
97
|
+
config.desktopNotification.enabled = process.env.CLAUDE_NOTIFY_DESKTOP === '1';
|
|
82
98
|
}
|
|
83
99
|
if (process.env.CLAUDE_NOTIFY_SOUND !== undefined) {
|
|
84
100
|
config.sound.enabled = process.env.CLAUDE_NOTIFY_SOUND === '1';
|
|
@@ -139,6 +155,57 @@ function saveState (state) {
|
|
|
139
155
|
// TELEGRAM
|
|
140
156
|
// ----------------------
|
|
141
157
|
|
|
158
|
+
function escapeHtml (text) {
|
|
159
|
+
return text
|
|
160
|
+
.replace(/&/g, '&')
|
|
161
|
+
.replace(/</g, '<')
|
|
162
|
+
.replace(/>/g, '>');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function markdownToTelegramHtml (md) {
|
|
166
|
+
const codeBlocks = [];
|
|
167
|
+
const inlineCodes = [];
|
|
168
|
+
|
|
169
|
+
// Extract fenced code blocks
|
|
170
|
+
let result = md.replace(/```[\s\S]*?```/g, (m) => {
|
|
171
|
+
const idx = codeBlocks.length;
|
|
172
|
+
const inner = m.replace(/^```\w*\n?/, '').replace(/\n?```$/, '');
|
|
173
|
+
codeBlocks.push(`<pre>${escapeHtml(inner)}</pre>`);
|
|
174
|
+
return `\x00CB${idx}\x00`;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Extract inline code
|
|
178
|
+
result = result.replace(/`([^`]+)`/g, (_, code) => {
|
|
179
|
+
const idx = inlineCodes.length;
|
|
180
|
+
inlineCodes.push(`<code>${escapeHtml(code)}</code>`);
|
|
181
|
+
return `\x00IC${idx}\x00`;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Escape HTML in remaining text
|
|
185
|
+
result = escapeHtml(result);
|
|
186
|
+
|
|
187
|
+
// Headers → bold
|
|
188
|
+
result = result.replace(/^#{1,6}\s+(.+)$/gm, '<b>$1</b>');
|
|
189
|
+
|
|
190
|
+
// Bold: **text**
|
|
191
|
+
result = result.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
|
|
192
|
+
|
|
193
|
+
// Italic: *text* (but not inside words with asterisks)
|
|
194
|
+
result = result.replace(/(?<!\w)\*([^*]+?)\*(?!\w)/g, '<i>$1</i>');
|
|
195
|
+
|
|
196
|
+
// Strikethrough: ~~text~~
|
|
197
|
+
result = result.replace(/~~(.+?)~~/g, '<s>$1</s>');
|
|
198
|
+
|
|
199
|
+
// Links: [text](url)
|
|
200
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
201
|
+
|
|
202
|
+
// Restore code blocks and inline codes
|
|
203
|
+
result = result.replace(/\x00CB(\d+)\x00/g, (_, i) => codeBlocks[i]);
|
|
204
|
+
result = result.replace(/\x00IC(\d+)\x00/g, (_, i) => inlineCodes[i]);
|
|
205
|
+
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
142
209
|
async function sendTelegram (config, state) {
|
|
143
210
|
if (!config.telegram.enabled || !config.telegram.token || !config.telegram.chatId) {
|
|
144
211
|
return;
|
|
@@ -152,7 +219,7 @@ async function sendTelegram (config, state) {
|
|
|
152
219
|
chat_id: config.telegram.chatId,
|
|
153
220
|
text: state._telegramText,
|
|
154
221
|
};
|
|
155
|
-
body.parse_mode = '
|
|
222
|
+
body.parse_mode = 'HTML';
|
|
156
223
|
const res = await fetch(`${baseUrl}/sendMessage`, {
|
|
157
224
|
method: 'POST',
|
|
158
225
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -167,6 +234,26 @@ async function sendTelegram (config, state) {
|
|
|
167
234
|
id: data.result.message_id,
|
|
168
235
|
ts: Date.now(),
|
|
169
236
|
});
|
|
237
|
+
} else if (!data.ok && body.parse_mode) {
|
|
238
|
+
// Retry without formatting if HTML parsing failed
|
|
239
|
+
const retryRes = await fetch(`${baseUrl}/sendMessage`, {
|
|
240
|
+
method: 'POST',
|
|
241
|
+
headers: { 'Content-Type': 'application/json' },
|
|
242
|
+
body: JSON.stringify({
|
|
243
|
+
chat_id: body.chat_id,
|
|
244
|
+
text: body.text,
|
|
245
|
+
}),
|
|
246
|
+
});
|
|
247
|
+
const retryData = await retryRes.json();
|
|
248
|
+
if (retryData.ok && retryData.result?.message_id) {
|
|
249
|
+
if (!state.sentMessages) {
|
|
250
|
+
state.sentMessages = [];
|
|
251
|
+
}
|
|
252
|
+
state.sentMessages.push({
|
|
253
|
+
id: retryData.result.message_id,
|
|
254
|
+
ts: Date.now(),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
170
257
|
}
|
|
171
258
|
} catch {
|
|
172
259
|
// silent fail
|
|
@@ -200,11 +287,32 @@ async function sendTelegram (config, state) {
|
|
|
200
287
|
}
|
|
201
288
|
|
|
202
289
|
// ----------------------
|
|
203
|
-
//
|
|
290
|
+
// DESKTOP NOTIFICATION
|
|
204
291
|
// ----------------------
|
|
205
292
|
|
|
206
|
-
|
|
207
|
-
|
|
293
|
+
function sendNativeFallback (config, message) {
|
|
294
|
+
try {
|
|
295
|
+
switch (PLATFORM) {
|
|
296
|
+
case 'darwin': {
|
|
297
|
+
const escaped = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
298
|
+
spawn('osascript', ['-e', `display notification "${escaped}" with title "Claude Code"`], {
|
|
299
|
+
stdio: 'ignore',
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case 'linux':
|
|
304
|
+
spawn('notify-send', ['Claude Code', message], {
|
|
305
|
+
stdio: 'ignore',
|
|
306
|
+
});
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
} catch (err) {
|
|
310
|
+
debugLog(config, 'native notification fallback failed:', err.message);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function sendDesktopNotification (config, message) {
|
|
315
|
+
if (!config.desktopNotification.enabled) {
|
|
208
316
|
return;
|
|
209
317
|
}
|
|
210
318
|
try {
|
|
@@ -212,11 +320,12 @@ async function sendWindowsNotification (config, message) {
|
|
|
212
320
|
notifier.notify({
|
|
213
321
|
title: 'Claude Code',
|
|
214
322
|
message,
|
|
215
|
-
sound:
|
|
323
|
+
sound: false,
|
|
216
324
|
wait: false,
|
|
217
325
|
});
|
|
218
|
-
} catch {
|
|
219
|
-
|
|
326
|
+
} catch (err) {
|
|
327
|
+
debugLog(config, 'node-notifier failed, trying native fallback:', err.message);
|
|
328
|
+
sendNativeFallback(config, message);
|
|
220
329
|
}
|
|
221
330
|
}
|
|
222
331
|
|
|
@@ -228,15 +337,30 @@ function playSound (config) {
|
|
|
228
337
|
if (!config.sound.enabled) {
|
|
229
338
|
return;
|
|
230
339
|
}
|
|
340
|
+
const file = config.sound.file;
|
|
231
341
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
342
|
+
switch (PLATFORM) {
|
|
343
|
+
case 'win32': {
|
|
344
|
+
const psCommand = `(New-Object Media.SoundPlayer '${file.replace(/'/g, "''")}').PlaySync()`;
|
|
345
|
+
spawn('powershell', ['-Command', psCommand], {
|
|
346
|
+
stdio: 'ignore',
|
|
347
|
+
windowsHide: true,
|
|
348
|
+
});
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case 'darwin':
|
|
352
|
+
spawn('afplay', [file], { stdio: 'ignore' });
|
|
353
|
+
break;
|
|
354
|
+
case 'linux': {
|
|
355
|
+
const child = spawn('paplay', [file], { stdio: 'ignore' });
|
|
356
|
+
child.on('error', () => {
|
|
357
|
+
spawn('aplay', [file], { stdio: 'ignore' });
|
|
358
|
+
});
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} catch (err) {
|
|
363
|
+
debugLog(config, 'playSound failed:', err.message);
|
|
240
364
|
}
|
|
241
365
|
}
|
|
242
366
|
|
|
@@ -375,19 +499,34 @@ function speakResult (config, duration) {
|
|
|
375
499
|
if (!config.voice.enabled) {
|
|
376
500
|
return;
|
|
377
501
|
}
|
|
502
|
+
const text = getVoicePhrase(duration);
|
|
378
503
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
504
|
+
switch (PLATFORM) {
|
|
505
|
+
case 'win32': {
|
|
506
|
+
const psCommand = [
|
|
507
|
+
'Add-Type -AssemblyName System.Speech;',
|
|
508
|
+
'$s = New-Object System.Speech.Synthesis.SpeechSynthesizer;',
|
|
509
|
+
`$s.Speak("${text.replace(/"/g, '`"')}");`,
|
|
510
|
+
].join('');
|
|
511
|
+
spawn('powershell', ['-Command', psCommand], {
|
|
512
|
+
stdio: 'ignore',
|
|
513
|
+
windowsHide: true,
|
|
514
|
+
});
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
case 'darwin':
|
|
518
|
+
spawn('say', [text], { stdio: 'ignore' });
|
|
519
|
+
break;
|
|
520
|
+
case 'linux': {
|
|
521
|
+
const child = spawn('spd-say', [text], { stdio: 'ignore' });
|
|
522
|
+
child.on('error', () => {
|
|
523
|
+
spawn('espeak', [text], { stdio: 'ignore' });
|
|
524
|
+
});
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} catch (err) {
|
|
529
|
+
debugLog(config, 'speakResult failed:', err.message);
|
|
391
530
|
}
|
|
392
531
|
}
|
|
393
532
|
|
|
@@ -460,7 +599,7 @@ process.stdin.on('end', async () => {
|
|
|
460
599
|
`${title}\n\nProject: ${project}\nDuration: ${duration}s\nTrigger: ${eventType}`;
|
|
461
600
|
|
|
462
601
|
let telegramMessage =
|
|
463
|
-
`${title}\n\nProject:
|
|
602
|
+
`${escapeHtml(title)}\n\nProject: <b>${escapeHtml(project)}</b>\nDuration: ${duration}s\nTrigger: ${eventType}`;
|
|
464
603
|
|
|
465
604
|
if (config.telegram.includeLastCcMessageInTelegram && event.last_assistant_message) {
|
|
466
605
|
const maxLen = 3500;
|
|
@@ -468,15 +607,18 @@ process.stdin.on('end', async () => {
|
|
|
468
607
|
if (lastMsg.length > maxLen) {
|
|
469
608
|
lastMsg = lastMsg.slice(0, maxLen) + '…';
|
|
470
609
|
}
|
|
471
|
-
telegramMessage += `\n\n${lastMsg}`;
|
|
610
|
+
telegramMessage += `\n\n${markdownToTelegramHtml(lastMsg)}`;
|
|
472
611
|
}
|
|
473
612
|
|
|
474
613
|
if (config.debug) {
|
|
475
|
-
const
|
|
614
|
+
const debugBlockMd = '\n\n*Debug:*\n'
|
|
476
615
|
+ (config.voice.enabled ? `\nVoice: ${getVoicePhrase(duration)}` : '')
|
|
477
616
|
+ `\n\nHook input:\n\`\`\`json\n${JSON.stringify(event, null, 2)}\n\`\`\``;
|
|
478
|
-
|
|
479
|
-
|
|
617
|
+
const debugBlockHtml = '\n\n<b>Debug:</b>\n'
|
|
618
|
+
+ (config.voice.enabled ? `\nVoice: ${escapeHtml(getVoicePhrase(duration))}` : '')
|
|
619
|
+
+ `\n\nHook input:\n<pre>${escapeHtml(JSON.stringify(event, null, 2))}</pre>`;
|
|
620
|
+
message += debugBlockMd;
|
|
621
|
+
telegramMessage += debugBlockHtml;
|
|
480
622
|
}
|
|
481
623
|
|
|
482
624
|
state._telegramText = `\u{1F916} ${telegramMessage}`;
|
|
@@ -484,7 +626,7 @@ process.stdin.on('end', async () => {
|
|
|
484
626
|
delete state._telegramText;
|
|
485
627
|
saveState(state);
|
|
486
628
|
|
|
487
|
-
await
|
|
629
|
+
await sendDesktopNotification(config, message);
|
|
488
630
|
playSound(config);
|
|
489
631
|
speakResult(config, duration);
|
|
490
632
|
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
3
|
"productName": "claude-notification-plugin",
|
|
4
|
-
"version": "1.0.
|
|
5
|
-
"description": "Claude Code notifications
|
|
4
|
+
"version": "1.0.49",
|
|
5
|
+
"description": "Cross-platform Claude Code notifications on task completion: Telegram, desktop (Windows/macOS/Linux), sound, and voice",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=18.0.0"
|
|
@@ -30,7 +30,10 @@
|
|
|
30
30
|
"claude-code",
|
|
31
31
|
"notifications",
|
|
32
32
|
"telegram",
|
|
33
|
-
"hooks"
|
|
33
|
+
"hooks",
|
|
34
|
+
"macos",
|
|
35
|
+
"linux",
|
|
36
|
+
"cross-platform"
|
|
34
37
|
],
|
|
35
38
|
"author": {
|
|
36
39
|
"name": "Viacheslav Makarov",
|