claude-notification-plugin 1.0.90 → 1.0.93
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 +11 -10
- package/bin/cli.js +37 -0
- package/bin/install.js +79 -63
- package/bin/listener-cli.js +2 -2
- package/bin/uninstall.js +4 -3
- package/listener/LISTENER-DETAILED.md +7 -7
- package/package.json +2 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.93",
|
|
4
4
|
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Viacheslav Makarov",
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ npm install -g claude-notification-plugin
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
The installer prompts for Telegram bot credentials and sets everything up.
|
|
28
|
-
Re-run `claude-notify
|
|
28
|
+
Re-run `claude-notify install` anytime to reconfigure.
|
|
29
29
|
|
|
30
30
|
## Uninstall
|
|
31
31
|
|
|
@@ -186,7 +186,7 @@ The `"default"` alias receives messages without a `@project` prefix.
|
|
|
186
186
|
### 2. Start the listener
|
|
187
187
|
|
|
188
188
|
```bash
|
|
189
|
-
claude-notify
|
|
189
|
+
claude-notify listener start
|
|
190
190
|
```
|
|
191
191
|
|
|
192
192
|
### 3. Send tasks from Telegram
|
|
@@ -209,10 +209,10 @@ The bot replies with status and results:
|
|
|
209
209
|
### 4. Manage the daemon
|
|
210
210
|
|
|
211
211
|
```bash
|
|
212
|
-
claude-notify
|
|
213
|
-
claude-notify
|
|
214
|
-
claude-notify
|
|
215
|
-
claude-notify
|
|
212
|
+
claude-notify listener status # Check if running
|
|
213
|
+
claude-notify listener stop # Stop
|
|
214
|
+
claude-notify listener restart # Restart
|
|
215
|
+
claude-notify listener logs # View last 50 log lines
|
|
216
216
|
```
|
|
217
217
|
|
|
218
218
|
### 5. Bot commands
|
|
@@ -266,10 +266,11 @@ Worktrees are auto-created when you use `@project/branch` syntax (controlled by
|
|
|
266
266
|
|
|
267
267
|
## CLI Commands
|
|
268
268
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
```
|
|
270
|
+
claude-notify install Reinstall plugin registration, Telegram config, hooks
|
|
271
|
+
claude-notify listener <action> Manage the Telegram Listener daemon
|
|
272
|
+
Actions: start, stop, status, logs, restart
|
|
273
|
+
```
|
|
273
274
|
|
|
274
275
|
## License
|
|
275
276
|
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
const command = process.argv[2];
|
|
9
|
+
|
|
10
|
+
switch (command) {
|
|
11
|
+
case 'install':
|
|
12
|
+
await import(path.join(__dirname, 'install.js'));
|
|
13
|
+
break;
|
|
14
|
+
case 'uninstall':
|
|
15
|
+
await import(path.join(__dirname, 'uninstall.js'));
|
|
16
|
+
break;
|
|
17
|
+
case 'listener': {
|
|
18
|
+
// Shift argv so listener-cli.js sees subcommand as argv[2]
|
|
19
|
+
process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
|
|
20
|
+
await import(path.join(__dirname, 'listener-cli.js'));
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
default:
|
|
24
|
+
// Hook mode: Claude Code pipes JSON to stdin with no args
|
|
25
|
+
if (!process.stdin.isTTY) {
|
|
26
|
+
await import(path.join(__dirname, '..', 'notifier', 'notifier.js'));
|
|
27
|
+
} else {
|
|
28
|
+
console.log('Usage: claude-notify <command> [options]');
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('Commands:');
|
|
31
|
+
console.log(' install Setup plugin registration, Telegram config, hooks');
|
|
32
|
+
console.log(' uninstall Remove plugin, hooks, config, CLI wrappers');
|
|
33
|
+
console.log(' listener <action> Manage the Telegram Listener daemon');
|
|
34
|
+
console.log(' Actions: start, stop, status, logs, restart');
|
|
35
|
+
process.exit(command ? 1 : 0);
|
|
36
|
+
}
|
|
37
|
+
}
|
package/bin/install.js
CHANGED
|
@@ -20,17 +20,14 @@ const knownMarketplacesPath = path.join(pluginsDir, 'known_marketplaces.json');
|
|
|
20
20
|
const marketplacesDir = path.join(pluginsDir, 'marketplaces');
|
|
21
21
|
const RESOLVER_PATH = path.join(claudeDir, 'claude-notify-resolve.js');
|
|
22
22
|
|
|
23
|
-
const HOOK_COMMAND = 'claude-
|
|
23
|
+
const HOOK_COMMAND = 'claude-notify';
|
|
24
24
|
const MARKETPLACE_KEY = 'bazilio-plugins';
|
|
25
25
|
const PLUGIN_KEY = 'claude-notification-plugin@bazilio-plugins';
|
|
26
26
|
const MARKETPLACE_REPO = 'https://github.com/Bazilio-san/claude-plugins.git';
|
|
27
27
|
const MARKETPLACE_GITHUB = 'Bazilio-san/claude-plugins';
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
'claude-notify-install': 'bin/install.js',
|
|
32
|
-
'claude-notify-uninstall': 'bin/uninstall.js',
|
|
33
|
-
};
|
|
29
|
+
const CLI_BIN_NAME = 'claude-notify';
|
|
30
|
+
const CLI_BIN_TARGET = 'bin/cli.js';
|
|
34
31
|
|
|
35
32
|
// ──────────────────────────────────────
|
|
36
33
|
// Plugin registration
|
|
@@ -217,34 +214,39 @@ function registerCli () {
|
|
|
217
214
|
|
|
218
215
|
const isWindows = process.platform === 'win32';
|
|
219
216
|
const resolverNative = RESOLVER_PATH.replace(/\//g, path.sep);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
for (const [name, relPath] of Object.entries(CLI_BINS)) {
|
|
223
|
-
if (isWindows) {
|
|
224
|
-
const cmdFile = path.join(binDir, `${name}.cmd`);
|
|
225
|
-
const content = `@echo off\r\nnode "${resolverNative}" ${relPath} %*\r\n`;
|
|
226
|
-
fs.writeFileSync(cmdFile, content);
|
|
227
|
-
created.push(cmdFile);
|
|
228
|
-
} else {
|
|
229
|
-
const shFile = path.join(binDir, name);
|
|
230
|
-
const content = `#!/bin/sh\nexec node "${RESOLVER_PATH}" "${relPath}" "$@"\n`;
|
|
231
|
-
fs.writeFileSync(shFile, content, { mode: 0o755 });
|
|
232
|
-
created.push(shFile);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
217
|
+
let wrapperPath;
|
|
235
218
|
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
219
|
+
if (isWindows) {
|
|
220
|
+
wrapperPath = path.join(binDir, `${CLI_BIN_NAME}.cmd`);
|
|
221
|
+
const content = `@echo off\r\nnode "${resolverNative}" ${CLI_BIN_TARGET} %*\r\n`;
|
|
222
|
+
fs.writeFileSync(wrapperPath, content);
|
|
223
|
+
} else {
|
|
224
|
+
wrapperPath = path.join(binDir, CLI_BIN_NAME);
|
|
225
|
+
const content = `#!/bin/sh\nexec node "${RESOLVER_PATH}" "${CLI_BIN_TARGET}" "$@"\n`;
|
|
226
|
+
fs.writeFileSync(wrapperPath, content, { mode: 0o755 });
|
|
241
227
|
}
|
|
228
|
+
|
|
229
|
+
console.log(` CLI wrapper registered: ${wrapperPath}`);
|
|
242
230
|
}
|
|
243
231
|
|
|
244
232
|
// ──────────────────────────────────────
|
|
245
233
|
// Helpers
|
|
246
234
|
// ──────────────────────────────────────
|
|
247
235
|
|
|
236
|
+
function openTtyInput () {
|
|
237
|
+
// If stdin is already a TTY (e.g. local `npm install`), use it directly
|
|
238
|
+
if (process.stdin.isTTY) {
|
|
239
|
+
return process.stdin;
|
|
240
|
+
}
|
|
241
|
+
// Otherwise try to open the TTY device directly (works even when npm pipes stdin)
|
|
242
|
+
try {
|
|
243
|
+
const ttyPath = process.platform === 'win32' ? '\\\\.\\CON' : '/dev/tty';
|
|
244
|
+
return fs.createReadStream(ttyPath, { encoding: 'utf-8' });
|
|
245
|
+
} catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
248
250
|
function ask (rl, question) {
|
|
249
251
|
return new Promise((resolve) => {
|
|
250
252
|
rl.question(question, (answer) => resolve(answer.trim()));
|
|
@@ -322,16 +324,6 @@ async function main () {
|
|
|
322
324
|
console.log(' Plugin registered.');
|
|
323
325
|
|
|
324
326
|
// 2. Interactive Telegram setup
|
|
325
|
-
const rl = readline.createInterface({
|
|
326
|
-
input: process.stdin,
|
|
327
|
-
output: process.stdout,
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
console.log('');
|
|
331
|
-
console.log('Claude Notification Plugin - Setup');
|
|
332
|
-
console.log('==================================');
|
|
333
|
-
console.log('');
|
|
334
|
-
|
|
335
327
|
let existing = {};
|
|
336
328
|
if (fs.existsSync(configPath)) {
|
|
337
329
|
try {
|
|
@@ -347,41 +339,65 @@ async function main () {
|
|
|
347
339
|
let token = existingToken;
|
|
348
340
|
let chatId = existingChatId;
|
|
349
341
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
chatId = '';
|
|
357
|
-
}
|
|
358
|
-
} else {
|
|
359
|
-
const useTelegram = await ask(rl, 'Configure Telegram? (y/N): ');
|
|
360
|
-
if (useTelegram.toLowerCase() === 'y') {
|
|
361
|
-
token = await ask(rl, 'Bot Token: ');
|
|
362
|
-
}
|
|
363
|
-
}
|
|
342
|
+
const ttyInput = openTtyInput();
|
|
343
|
+
if (ttyInput) {
|
|
344
|
+
const rl = readline.createInterface({
|
|
345
|
+
input: ttyInput,
|
|
346
|
+
output: process.stdout,
|
|
347
|
+
});
|
|
364
348
|
|
|
365
|
-
if (token && !chatId) {
|
|
366
349
|
console.log('');
|
|
367
|
-
console.log('
|
|
368
|
-
|
|
350
|
+
console.log('Claude Notification Plugin - Setup');
|
|
351
|
+
console.log('==================================');
|
|
352
|
+
console.log('');
|
|
369
353
|
|
|
370
|
-
|
|
371
|
-
|
|
354
|
+
if (existingToken) {
|
|
355
|
+
const masked = existingToken.slice(0, 6) + '...' + existingToken.slice(-4);
|
|
356
|
+
console.log(`Telegram token found: ${masked}`);
|
|
357
|
+
const reuse = await ask(rl, 'Keep existing token? (Y/n): ');
|
|
358
|
+
if (reuse.toLowerCase() === 'n') {
|
|
359
|
+
token = await ask(rl, 'New Bot Token: ');
|
|
360
|
+
chatId = '';
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
const useTelegram = await ask(rl, 'Configure Telegram? (y/N): ');
|
|
364
|
+
if (useTelegram.toLowerCase() === 'y') {
|
|
365
|
+
token = await ask(rl, 'Bot Token: ');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (token && !chatId) {
|
|
370
|
+
console.log('');
|
|
371
|
+
console.log('Send any message to your bot in Telegram, then press Enter.');
|
|
372
|
+
await ask(rl, '');
|
|
373
|
+
|
|
374
|
+
console.log('Fetching Chat ID...');
|
|
375
|
+
chatId = await fetchChatId(token);
|
|
376
|
+
|
|
377
|
+
if (chatId) {
|
|
378
|
+
console.log('Chat ID detected: ' + chatId);
|
|
379
|
+
} else {
|
|
380
|
+
console.log('Could not detect Chat ID automatically.');
|
|
381
|
+
chatId = await ask(rl, 'Enter Chat ID manually: ');
|
|
382
|
+
}
|
|
383
|
+
} else if (token && chatId) {
|
|
384
|
+
console.log(`Chat ID: ${chatId}`);
|
|
385
|
+
}
|
|
372
386
|
|
|
373
|
-
|
|
374
|
-
|
|
387
|
+
rl.close();
|
|
388
|
+
if (ttyInput !== process.stdin) {
|
|
389
|
+
ttyInput.destroy();
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
console.log('');
|
|
393
|
+
console.log('Non-interactive install (stdin is not a terminal).');
|
|
394
|
+
if (token && chatId) {
|
|
395
|
+
console.log('Telegram: using existing config.');
|
|
375
396
|
} else {
|
|
376
|
-
console.log('
|
|
377
|
-
chatId = await ask(rl, 'Enter Chat ID manually: ');
|
|
397
|
+
console.log('Telegram: skipped. Run "claude-notify install" to configure.');
|
|
378
398
|
}
|
|
379
|
-
} else if (token && chatId) {
|
|
380
|
-
console.log(`Chat ID: ${chatId}`);
|
|
381
399
|
}
|
|
382
400
|
|
|
383
|
-
rl.close();
|
|
384
|
-
|
|
385
401
|
// 3. Write config
|
|
386
402
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
387
403
|
|
package/bin/listener-cli.js
CHANGED
|
@@ -44,7 +44,7 @@ switch (command) {
|
|
|
44
44
|
setTimeout(() => startDaemon(), 1000);
|
|
45
45
|
break;
|
|
46
46
|
default:
|
|
47
|
-
console.log('Usage: claude-notify
|
|
47
|
+
console.log('Usage: claude-notify listener <start|stop|status|logs|restart>');
|
|
48
48
|
console.log('');
|
|
49
49
|
console.log('Commands:');
|
|
50
50
|
console.log(' start Start the listener daemon');
|
|
@@ -75,7 +75,7 @@ function startDaemon () {
|
|
|
75
75
|
// Validate config
|
|
76
76
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
77
77
|
console.error(`Config not found: ${CONFIG_FILE}`);
|
|
78
|
-
console.error('Run claude-notify
|
|
78
|
+
console.error('Run claude-notify install first, or create the config manually.');
|
|
79
79
|
process.exit(1);
|
|
80
80
|
}
|
|
81
81
|
|
package/bin/uninstall.js
CHANGED
|
@@ -11,7 +11,7 @@ const configPath = path.join(claudeDir, 'notifier.config.json');
|
|
|
11
11
|
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
12
12
|
const statePath = path.join(claudeDir, '.notifier_state.json');
|
|
13
13
|
|
|
14
|
-
const HOOK_COMMAND = 'claude-
|
|
14
|
+
const HOOK_COMMAND = 'claude-notify';
|
|
15
15
|
|
|
16
16
|
// Remove hooks from settings.json
|
|
17
17
|
if (fs.existsSync(settingsPath)) {
|
|
@@ -49,7 +49,8 @@ for (const file of [configPath, statePath, resolverPath]) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// Remove CLI wrapper scripts
|
|
52
|
-
|
|
52
|
+
// Current name + legacy names from previous versions
|
|
53
|
+
const WRAPPER_NAMES = ['claude-notify', 'claude-notify-listener', 'claude-notify-install', 'claude-notify-uninstall'];
|
|
53
54
|
let cliBinsRemoved = false;
|
|
54
55
|
const ext = process.platform === 'win32' ? '.cmd' : '';
|
|
55
56
|
|
|
@@ -69,7 +70,7 @@ try {
|
|
|
69
70
|
wrapperDirs.add(path.join(home, '.local', 'bin'));
|
|
70
71
|
|
|
71
72
|
for (const dir of wrapperDirs) {
|
|
72
|
-
for (const name of
|
|
73
|
+
for (const name of WRAPPER_NAMES) {
|
|
73
74
|
const filePath = path.join(dir, `${name}${ext}`);
|
|
74
75
|
if (fs.existsSync(filePath)) {
|
|
75
76
|
fs.unlinkSync(filePath);
|
|
@@ -87,7 +87,7 @@ The `offset` parameter in the request ensures each message is processed exactly
|
|
|
87
87
|
|
|
88
88
|
## Detached process: why the listener lives without a terminal
|
|
89
89
|
|
|
90
|
-
When you run `claude-notify
|
|
90
|
+
When you run `claude-notify listener start`, the following happens:
|
|
91
91
|
|
|
92
92
|
```
|
|
93
93
|
Your terminal
|
|
@@ -117,7 +117,7 @@ Your terminal
|
|
|
117
117
|
`child.unref()` — allows `listener-cli.js` to exit without waiting for the child.
|
|
118
118
|
|
|
119
119
|
Result: the listener runs as a background OS process. It is not tied to a terminal or to Claude Code — only to the operating system. It will only stop when:
|
|
120
|
-
- The `claude-notify
|
|
120
|
+
- The `claude-notify listener stop` command is issued (or `/stop` in Telegram)
|
|
121
121
|
- The computer is shut down or restarted
|
|
122
122
|
- It crashes due to an error
|
|
123
123
|
|
|
@@ -856,7 +856,7 @@ exec(`claude -p "${userText}"`)
|
|
|
856
856
|
### Listener won't start
|
|
857
857
|
|
|
858
858
|
```bash
|
|
859
|
-
claude-notify
|
|
859
|
+
claude-notify listener status
|
|
860
860
|
# → Status: not running
|
|
861
861
|
```
|
|
862
862
|
|
|
@@ -865,11 +865,11 @@ Check:
|
|
|
865
865
|
1. Does the config exist? `cat ~/.claude/notifier.config.json`
|
|
866
866
|
2. Are `telegram.token` and `telegram.chatId` present?
|
|
867
867
|
3. Is there a `listener.projects` section?
|
|
868
|
-
4. Logs: `claude-notify
|
|
868
|
+
4. Logs: `claude-notify listener logs`
|
|
869
869
|
|
|
870
870
|
### Bot doesn't respond
|
|
871
871
|
|
|
872
|
-
1. Is the listener running? `claude-notify
|
|
872
|
+
1. Is the listener running? `claude-notify listener status`
|
|
873
873
|
2. Is the chatId correct? Messages from other chats are ignored (check the log: `WARN Ignored message from chat ...`)
|
|
874
874
|
3. Is the bot added to the chat? Write `/help` to the bot — if there's no response, check the token
|
|
875
875
|
|
|
@@ -882,7 +882,7 @@ Check:
|
|
|
882
882
|
Or restart the listener:
|
|
883
883
|
|
|
884
884
|
```bash
|
|
885
|
-
claude-notify
|
|
885
|
+
claude-notify listener restart
|
|
886
886
|
```
|
|
887
887
|
|
|
888
888
|
The watchdog will automatically clear stale tasks on the next startup.
|
|
@@ -925,7 +925,7 @@ Suppose you have two projects: an API server and a web application.
|
|
|
925
925
|
```
|
|
926
926
|
=== 10:00 — Startup ===
|
|
927
927
|
|
|
928
|
-
You (terminal): claude-notify
|
|
928
|
+
You (terminal): claude-notify listener start
|
|
929
929
|
→ Listener started (PID: 12345)
|
|
930
930
|
|
|
931
931
|
=== 10:01 — First task ===
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
3
|
"productName": "claude-notification-plugin",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.93",
|
|
5
5
|
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -17,10 +17,7 @@
|
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
|
19
19
|
"bin": {
|
|
20
|
-
"claude-notify
|
|
21
|
-
"claude-notify-uninstall": "bin/uninstall.js",
|
|
22
|
-
"claude-notifier": "notifier/notifier.js",
|
|
23
|
-
"claude-notify-listener": "bin/listener-cli.js"
|
|
20
|
+
"claude-notify": "bin/cli.js"
|
|
24
21
|
},
|
|
25
22
|
"scripts": {
|
|
26
23
|
"preuninstall": "node bin/uninstall.js",
|