claude-notification-plugin 1.0.92 → 1.0.94

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
- "version": "1.0.92",
3
+ "version": "1.0.94",
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
@@ -30,9 +30,11 @@ Re-run `claude-notify install` anytime to reconfigure.
30
30
  ## Uninstall
31
31
 
32
32
  ```bash
33
- npm uninstall -g claude-notification-plugin
33
+ claude-notify uninstall
34
34
  ```
35
35
 
36
+ This removes hooks, config, CLI wrappers, plugin registration, and the npm global package.
37
+
36
38
  ## Configuration
37
39
 
38
40
  Config file: `~/.claude/notifier.config.json`
package/bin/cli.js CHANGED
@@ -1,29 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { fileURLToPath } from 'url';
3
+ import { fileURLToPath, pathToFileURL } from 'url';
4
4
  import path from 'path';
5
5
 
6
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
7
 
8
+ function toImportUrl (relativePath) {
9
+ return pathToFileURL(path.join(__dirname, relativePath)).href;
10
+ }
11
+
8
12
  const command = process.argv[2];
9
13
 
10
14
  switch (command) {
11
15
  case 'install':
12
- await import(path.join(__dirname, 'install.js'));
16
+ await import(toImportUrl('install.js'));
13
17
  break;
14
18
  case 'uninstall':
15
- await import(path.join(__dirname, 'uninstall.js'));
19
+ await import(toImportUrl('uninstall.js'));
16
20
  break;
17
21
  case 'listener': {
18
22
  // Shift argv so listener-cli.js sees subcommand as argv[2]
19
23
  process.argv = [process.argv[0], process.argv[1], ...process.argv.slice(3)];
20
- await import(path.join(__dirname, 'listener-cli.js'));
24
+ await import(toImportUrl('listener-cli.js'));
21
25
  break;
22
26
  }
23
27
  default:
24
28
  // Hook mode: Claude Code pipes JSON to stdin with no args
25
29
  if (!process.stdin.isTTY) {
26
- await import(path.join(__dirname, '..', 'notifier', 'notifier.js'));
30
+ await import(toImportUrl(path.join('..', 'notifier', 'notifier.js')));
27
31
  } else {
28
32
  console.log('Usage: claude-notify <command> [options]');
29
33
  console.log('');
package/bin/install.js CHANGED
@@ -233,6 +233,23 @@ function registerCli () {
233
233
  // Helpers
234
234
  // ──────────────────────────────────────
235
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
+ // On Unix, try opening /dev/tty directly (works even when npm pipes stdin)
242
+ if (process.platform !== 'win32') {
243
+ try {
244
+ const fd = fs.openSync('/dev/tty', 'r');
245
+ return fs.createReadStream(null, { fd, encoding: 'utf-8' });
246
+ } catch {
247
+ // no TTY available (CI/CD, Docker, etc.)
248
+ }
249
+ }
250
+ return null;
251
+ }
252
+
236
253
  function ask (rl, question) {
237
254
  return new Promise((resolve) => {
238
255
  rl.question(question, (answer) => resolve(answer.trim()));
@@ -310,16 +327,6 @@ async function main () {
310
327
  console.log(' Plugin registered.');
311
328
 
312
329
  // 2. Interactive Telegram setup
313
- const rl = readline.createInterface({
314
- input: process.stdin,
315
- output: process.stdout,
316
- });
317
-
318
- console.log('');
319
- console.log('Claude Notification Plugin - Setup');
320
- console.log('==================================');
321
- console.log('');
322
-
323
330
  let existing = {};
324
331
  if (fs.existsSync(configPath)) {
325
332
  try {
@@ -335,41 +342,61 @@ async function main () {
335
342
  let token = existingToken;
336
343
  let chatId = existingChatId;
337
344
 
338
- if (existingToken) {
339
- const masked = existingToken.slice(0, 6) + '...' + existingToken.slice(-4);
340
- console.log(`Telegram token found: ${masked}`);
341
- const reuse = await ask(rl, 'Keep existing token? (Y/n): ');
342
- if (reuse.toLowerCase() === 'n') {
343
- token = await ask(rl, 'New Bot Token: ');
344
- chatId = '';
345
- }
346
- } else {
347
- const useTelegram = await ask(rl, 'Configure Telegram? (y/N): ');
348
- if (useTelegram.toLowerCase() === 'y') {
349
- token = await ask(rl, 'Bot Token: ');
350
- }
351
- }
345
+ const ttyInput = openTtyInput();
346
+ if (ttyInput) {
347
+ const rl = readline.createInterface({
348
+ input: ttyInput,
349
+ output: process.stdout,
350
+ });
352
351
 
353
- if (token && !chatId) {
354
- console.log('');
355
- console.log('Send any message to your bot in Telegram, then press Enter.');
356
- await ask(rl, '');
352
+ console.log(`
353
+ Claude Notification Plugin - Setup
354
+ ==================================
355
+ `);
356
+
357
+ if (existingToken) {
358
+ const masked = existingToken.slice(0, 6) + '...' + existingToken.slice(-4);
359
+ console.log(`Telegram token found: ${masked}`);
360
+ const reuse = await ask(rl, 'Keep existing token? (Y/n): ');
361
+ if (reuse.toLowerCase() === 'n') {
362
+ token = await ask(rl, 'New Bot Token: ');
363
+ chatId = '';
364
+ }
365
+ } else {
366
+ const useTelegram = await ask(rl, 'Configure Telegram? (y/N): ');
367
+ if (useTelegram.toLowerCase() === 'y') {
368
+ token = await ask(rl, 'Bot Token: ');
369
+ }
370
+ }
357
371
 
358
- console.log('Fetching Chat ID...');
359
- chatId = await fetchChatId(token);
372
+ if (token && !chatId) {
373
+ console.log('\nSend any message to your bot in Telegram, then press Enter.');
374
+ await ask(rl, '');
375
+
376
+ console.log('Fetching Chat ID...');
377
+ chatId = await fetchChatId(token);
378
+
379
+ if (chatId) {
380
+ console.log('Chat ID detected: ' + chatId);
381
+ } else {
382
+ console.log('Could not detect Chat ID automatically.');
383
+ chatId = await ask(rl, 'Enter Chat ID manually: ');
384
+ }
385
+ } else if (token && chatId) {
386
+ console.log(`Chat ID: ${chatId}`);
387
+ }
360
388
 
361
- if (chatId) {
362
- console.log('Chat ID detected: ' + chatId);
363
- } else {
364
- console.log('Could not detect Chat ID automatically.');
365
- chatId = await ask(rl, 'Enter Chat ID manually: ');
389
+ rl.close();
390
+ if (ttyInput !== process.stdin) {
391
+ ttyInput.destroy();
366
392
  }
367
- } else if (token && chatId) {
368
- console.log(`Chat ID: ${chatId}`);
393
+ } else {
394
+ const telegramMsg = token && chatId
395
+ ? 'Telegram: using existing config.'
396
+ : 'Telegram: skipped. Run "claude-notify install" to configure.';
397
+ console.log(`\nNon-interactive install (stdin is not a terminal).\n${telegramMsg}`);
369
398
  }
370
399
 
371
- rl.close();
372
-
373
400
  // 3. Write config
374
401
  fs.mkdirSync(claudeDir, { recursive: true });
375
402
 
@@ -451,38 +478,35 @@ async function main () {
451
478
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
452
479
 
453
480
  // 5. Summary
454
- console.log('');
455
- console.log('Installed!');
456
- console.log('');
457
- console.log('Hooks registered:');
458
- console.log(' - UserPromptSubmit (start timer)');
459
- console.log(' - Stop (task finished)');
460
- console.log(' - Notification (waiting for input)');
461
- console.log('');
462
- console.log('Config: ' + configPath);
463
-
464
- if (config.telegram.token && config.telegram.chatId) {
465
- console.log('Telegram: configured');
466
- } else {
467
- console.log('Telegram: not configured (edit config later)');
468
- }
481
+ const telegramStatus = config.telegram.token && config.telegram.chatId
482
+ ? 'Telegram: configured'
483
+ : 'Telegram: not configured (edit config later)';
469
484
 
485
+ let platformTip = '';
470
486
  if (platform === 'darwin') {
471
- console.log('');
472
- console.log('Tip: install terminal-notifier for better macOS notifications:');
473
- console.log(' brew install terminal-notifier');
487
+ platformTip = `\nTip: install terminal-notifier for better macOS notifications:
488
+ brew install terminal-notifier`;
489
+ } else if (platform === 'linux') {
490
+ platformTip = `\nTip: for voice announcements, install espeak:
491
+ sudo apt install espeak`;
474
492
  }
475
493
 
476
- if (platform === 'linux') {
477
- console.log('');
478
- console.log('Tip: for voice announcements, install espeak:');
479
- console.log(' sudo apt install espeak');
480
- }
494
+ console.log(`
495
+ Installed!
481
496
 
482
- console.log('');
483
- console.log('To disable per project, add to .claude/settings.local.json:');
484
- console.log(' { "env": { "CLAUDE_NOTIFY_DISABLE": "1" } }');
485
- console.log('');
497
+ Hooks registered:
498
+ - UserPromptSubmit (start timer)
499
+ - Stop (task finished)
500
+ - Notification (waiting for input)
501
+
502
+ Config: ${configPath}
503
+ ${telegramStatus}${platformTip}
504
+
505
+ To uninstall: claude-notify uninstall
506
+
507
+ To disable per project, add to .claude/settings.local.json:
508
+ { "env": { "CLAUDE_NOTIFY_DISABLE": "1" } }
509
+ `);
486
510
  }
487
511
 
488
512
  main().then(() => 0);
package/bin/uninstall.js CHANGED
@@ -104,14 +104,27 @@ if (fs.existsSync(pluginCacheDir)) {
104
104
  cacheRemoved = true;
105
105
  }
106
106
 
107
- console.log('');
108
- console.log('Claude Notification Plugin uninstalled.');
109
- console.log('Hooks removed from settings.json');
110
- console.log('Config files deleted.');
111
- if (pluginEntryRemoved || cacheRemoved) {
112
- console.log('Plugin registration cleaned.');
113
- }
114
- if (cliBinsRemoved) {
115
- console.log('CLI wrapper scripts removed.');
107
+ const extras = [
108
+ pluginEntryRemoved || cacheRemoved ? 'Plugin registration cleaned.' : '',
109
+ cliBinsRemoved ? 'CLI wrapper scripts removed.' : '',
110
+ ].filter(Boolean).join('\n');
111
+
112
+ console.log(`
113
+ Claude Notification Plugin uninstalled.
114
+ Hooks removed from settings.json
115
+ Config files deleted.${extras ? `\n${extras}` : ''}
116
+ `);
117
+
118
+ // If run manually (not via npm lifecycle), remove the global npm package too
119
+ if (!process.env.npm_lifecycle_event) {
120
+ try {
121
+ console.log('Removing npm global package...');
122
+ execSync('npm uninstall -g claude-notification-plugin', {
123
+ stdio: 'inherit',
124
+ windowsHide: true,
125
+ });
126
+ console.log('');
127
+ } catch {
128
+ console.log('Could not remove npm package automatically.\nRun manually: npm uninstall -g claude-notification-plugin\n');
129
+ }
116
130
  }
117
- console.log('');
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.92",
4
+ "version": "1.0.94",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {
@@ -20,7 +20,6 @@
20
20
  "claude-notify": "bin/cli.js"
21
21
  },
22
22
  "scripts": {
23
- "preuninstall": "node bin/uninstall.js",
24
23
  "postinstall": "node bin/install.js",
25
24
  "lint": "eslint .",
26
25
  "lint:fix": "eslint --fix ."