claude-notification-plugin 1.0.106 → 1.0.108

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.106",
3
+ "version": "1.0.108",
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/bin/install.js CHANGED
@@ -19,6 +19,112 @@ const installedPluginsPath = path.join(pluginsDir, 'installed_plugins.json');
19
19
  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
+ const pidFile = path.join(claudeDir, '.listener.pid');
23
+ const installLogPath = path.join(claudeDir, 'install.log');
24
+
25
+ // ──────────────────────────────────────
26
+ // Logging to file
27
+ // ──────────────────────────────────────
28
+
29
+ let logStream;
30
+
31
+ function initLog () {
32
+ fs.mkdirSync(claudeDir, { recursive: true });
33
+ logStream = fs.createWriteStream(installLogPath, { flags: 'w' });
34
+ const origLog = console.log.bind(console);
35
+ const origWarn = console.warn.bind(console);
36
+ const stamp = () => new Date().toISOString();
37
+
38
+ console.log = (...args) => {
39
+ origLog(...args);
40
+ const line = args.map(String).join(' ').replace(/\x1b\[[0-9;]*m/g, '');
41
+ logStream.write(`[${stamp()}] ${line}\n`);
42
+ };
43
+
44
+ console.warn = (...args) => {
45
+ origWarn(...args);
46
+ const line = args.map(String).join(' ').replace(/\x1b\[[0-9;]*m/g, '');
47
+ logStream.write(`[${stamp()}] WARN: ${line}\n`);
48
+ };
49
+ }
50
+
51
+ function closeLog () {
52
+ if (logStream) {
53
+ logStream.end();
54
+ }
55
+ }
56
+
57
+ // ──────────────────────────────────────
58
+ // Stop listener before reinstall
59
+ // ──────────────────────────────────────
60
+
61
+ function isProcessAlive (pid) {
62
+ if (!Number.isInteger(pid) || pid <= 0) {
63
+ return false;
64
+ }
65
+ try {
66
+ if (process.platform === 'win32') {
67
+ const result = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
68
+ encoding: 'utf-8',
69
+ windowsHide: true,
70
+ stdio: ['pipe', 'pipe', 'ignore'],
71
+ });
72
+ return result.includes(String(pid));
73
+ }
74
+ process.kill(pid, 0);
75
+ return true;
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ function killProcessTree (pid) {
82
+ if (!isProcessAlive(pid)) {
83
+ return false;
84
+ }
85
+ try {
86
+ if (process.platform === 'win32') {
87
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore', windowsHide: true });
88
+ console.log(` Listener process ${pid} killed`);
89
+ return true;
90
+ }
91
+ process.kill(pid, 'SIGTERM');
92
+ let tries = 10;
93
+ while (tries-- > 0 && isProcessAlive(pid)) {
94
+ execSync('sleep 0.2', { stdio: 'ignore' });
95
+ }
96
+ if (isProcessAlive(pid)) {
97
+ process.kill(pid, 'SIGKILL');
98
+ }
99
+ console.log(` Listener process ${pid} killed`);
100
+ return true;
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ function stopListenerIfRunning () {
107
+ let stopped = false;
108
+ const pidFromFile = (() => {
109
+ try {
110
+ if (!fs.existsSync(pidFile)) return null;
111
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
112
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
113
+ } catch { return null; }
114
+ })();
115
+
116
+ if (pidFromFile && killProcessTree(pidFromFile)) {
117
+ stopped = true;
118
+ }
119
+
120
+ try {
121
+ if (fs.existsSync(pidFile)) {
122
+ fs.unlinkSync(pidFile);
123
+ }
124
+ } catch { /* ignore */ }
125
+
126
+ return stopped;
127
+ }
22
128
 
23
129
  const HOOK_COMMAND = 'claude-notify';
24
130
  const MARKETPLACE_KEY = 'bazilio-plugins';
@@ -313,6 +419,11 @@ function addHook (settings, event) {
313
419
  // ──────────────────────────────────────
314
420
 
315
421
  async function main () {
422
+ initLog();
423
+
424
+ // 0. Stop listener if running (before overwriting files)
425
+ const listenerWasStopped = stopListenerIfRunning();
426
+
316
427
  // 1. Register plugin in Claude Code
317
428
  const version = getVersion();
318
429
 
@@ -514,9 +625,11 @@ Send any message to your bot in Telegram, then press Enter.\x1b[0m`);
514
625
  sudo apt install espeak`;
515
626
  }
516
627
 
628
+ const listenerLine = listenerWasStopped ? '\nListener was stopped (restart manually if needed).' : '';
629
+
517
630
  console.log(`
518
631
  Installed!
519
-
632
+ ${listenerLine}
520
633
  Hooks registered:
521
634
  - UserPromptSubmit (start timer)
522
635
  - Stop (task finished)
@@ -525,9 +638,13 @@ Hooks registered:
525
638
  Config: ${configPath}
526
639
  ${telegramStatus}${platformTip}
527
640
 
641
+ Log: ${installLogPath}
642
+
528
643
  To uninstall: claude-notify uninstall
529
644
 
530
645
  To disable per project, add to .claude/settings.local.json: { "env": { "CLAUDE_NOTIFY_DISABLE": "1" } }`);
646
+
647
+ closeLog();
531
648
  }
532
649
 
533
650
  main().then(() => 0);
@@ -297,8 +297,8 @@ async function setupListener () {
297
297
  taskTimeoutMinutes: L.taskTimeoutMinutes ?? 30,
298
298
  maxQueuePerWorkDir: L.maxQueuePerWorkDir ?? 10,
299
299
  maxTotalTasks: L.maxTotalTasks ?? 50,
300
- logDir: L.logDir || '',
301
- taskLogDir: L.taskLogDir || '',
300
+ logDir: L.logDir || path.join(home, '.claude'),
301
+ taskLogDir: L.taskLogDir || path.join(home, '.claude'),
302
302
  projectPath: L.projects?.default?.path || '',
303
303
  };
304
304
 
@@ -317,8 +317,8 @@ Press Enter to keep current value shown in [brackets].
317
317
  const taskTimeoutStr = await ask(rl, `Task timeout, minutes [${defaults.taskTimeoutMinutes}]: `) || String(defaults.taskTimeoutMinutes);
318
318
  const maxQueueStr = await ask(rl, `Max queue per work dir [${defaults.maxQueuePerWorkDir}]: `) || String(defaults.maxQueuePerWorkDir);
319
319
  const maxTotalStr = await ask(rl, `Max total tasks [${defaults.maxTotalTasks}]: `) || String(defaults.maxTotalTasks);
320
- const logDir = await ask(rl, `Log dir [${defaults.logDir || '(default)'}]: `) || defaults.logDir;
321
- const taskLogDir = await ask(rl, `Task log dir [${defaults.taskLogDir || '(default)'}]: `) || defaults.taskLogDir;
320
+ const logDir = await ask(rl, `Log dir [${defaults.logDir}]: `) || defaults.logDir;
321
+ const taskLogDir = await ask(rl, `Task log dir [${defaults.taskLogDir}]: `) || defaults.taskLogDir;
322
322
  const projectPath = await ask(rl, `Default project path [${defaults.projectPath || '(none)'}]: `) || defaults.projectPath;
323
323
 
324
324
  rl.close();
package/commit-sha CHANGED
@@ -1 +1 @@
1
- 3299ff296778c37204e4b4e5832343865faf035a
1
+ 4d9fe96446b31d55ec608eac82a50aaeb12e59b1
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.106",
4
+ "version": "1.0.108",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {