claude-notification-plugin 1.0.99 → 1.0.102

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.99",
3
+ "version": "1.0.102",
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/uninstall.js CHANGED
@@ -10,20 +10,81 @@ const claudeDir = path.join(home, '.claude');
10
10
  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
+ const pidFile = path.join(claudeDir, '.listener.pid');
13
14
 
14
15
  const HOOK_COMMAND = 'claude-notify';
15
16
  const PLUGIN_KEY = 'claude-notification-plugin@bazilio-plugins';
16
17
  const MARKETPLACE_KEY = 'bazilio-plugins';
17
18
 
19
+ function isPluginHookCommand (command) {
20
+ if (typeof command !== 'string') {
21
+ return false;
22
+ }
23
+
24
+ const normalized = command.trim().toLowerCase();
25
+ if (!normalized) {
26
+ return false;
27
+ }
28
+
29
+ return normalized === HOOK_COMMAND || normalized.startsWith(`${HOOK_COMMAND} `);
30
+ }
31
+
32
+ // Stop listener daemon if running
33
+ let listenerStopped = false;
34
+ try {
35
+ if (fs.existsSync(pidFile)) {
36
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
37
+ if (!isNaN(pid)) {
38
+ try {
39
+ if (process.platform === 'win32') {
40
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore', windowsHide: true });
41
+ } else {
42
+ process.kill(pid, 'SIGTERM');
43
+ let tries = 10;
44
+ const isAlive = (p) => {
45
+ try {
46
+ process.kill(p, 0);
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ };
52
+ while (tries-- > 0 && isAlive(pid)) {
53
+ execSync('sleep 0.5', { stdio: 'ignore' });
54
+ }
55
+ if (isAlive(pid)) {
56
+ process.kill(pid, 'SIGKILL');
57
+ }
58
+ }
59
+ listenerStopped = true;
60
+ } catch {
61
+ // process may already be dead
62
+ }
63
+ fs.unlinkSync(pidFile);
64
+ }
65
+ }
66
+ } catch {
67
+ // ignore
68
+ }
69
+
18
70
  // Remove hooks from settings.json
71
+ let hooksRemoved = false;
72
+ let hooksRemoveError = '';
19
73
  if (fs.existsSync(settingsPath)) {
20
74
  try {
21
75
  const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
76
+ let hadPluginHooks = false;
22
77
 
23
78
  if (settings.hooks) {
24
79
  for (const event of Object.keys(settings.hooks)) {
80
+ const eventHooks = Array.isArray(settings.hooks[event]) ? settings.hooks[event] : [];
81
+ const hadInEvent = eventHooks.some((matcher) =>
82
+ matcher.hooks?.some((h) => isPluginHookCommand(h.command)),
83
+ );
84
+ hadPluginHooks = hadPluginHooks || hadInEvent;
85
+
25
86
  settings.hooks[event] = settings.hooks[event].filter((matcher) =>
26
- !matcher.hooks?.some((h) => h.command?.includes(HOOK_COMMAND)),
87
+ !matcher.hooks?.some((h) => isPluginHookCommand(h.command)),
27
88
  );
28
89
 
29
90
  if (settings.hooks[event].length === 0) {
@@ -53,22 +114,35 @@ if (fs.existsSync(settingsPath)) {
53
114
  }
54
115
 
55
116
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
56
- } catch {
57
- // ignore
117
+
118
+ // Verify hooks were actually removed
119
+ const verify = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
120
+ const remainingPluginHooks = verify.hooks
121
+ ? Object.values(verify.hooks).some((matchers) =>
122
+ Array.isArray(matchers) &&
123
+ matchers.some((m) => m.hooks?.some((h) => isPluginHookCommand(h.command))),
124
+ )
125
+ : false;
126
+ hooksRemoved = hadPluginHooks && !remainingPluginHooks;
127
+ if (hadPluginHooks && remainingPluginHooks) {
128
+ hooksRemoveError = 'Hooks still present in settings.json after removal attempt';
129
+ }
130
+ } catch (err) {
131
+ hooksRemoveError = `Failed to update settings.json: ${err.message}`;
58
132
  }
59
133
  }
60
134
 
61
- // Remove config, state, and resolver files
135
+ // Remove config, state, resolver, and listener files
62
136
  const resolverPath = path.join(claudeDir, 'claude-notify-resolve.js');
63
- for (const file of [configPath, statePath, resolverPath]) {
137
+ const listenerLogFile = path.join(claudeDir, '.cc-n-listener.log');
138
+ for (const file of [configPath, statePath, resolverPath, pidFile, listenerLogFile]) {
64
139
  if (fs.existsSync(file)) {
65
140
  fs.unlinkSync(file);
66
141
  }
67
142
  }
68
143
 
69
- // Remove CLI wrapper scripts
70
- // Current name + legacy names from previous versions
71
- const WRAPPER_NAMES = ['claude-notify', 'claude-notify-listener', 'claude-notify-install', 'claude-notify-uninstall'];
144
+ // Remove CLI wrapper script
145
+ const WRAPPER_NAMES = ['claude-notify'];
72
146
  let cliBinsRemoved = false;
73
147
  const ext = process.platform === 'win32' ? '.cmd' : '';
74
148
 
@@ -121,7 +195,14 @@ if (fs.existsSync(pluginCacheDir)) {
121
195
  cacheRemoved = !fs.existsSync(pluginCacheDir);
122
196
  }
123
197
 
198
+ const hooksStatus = hooksRemoved
199
+ ? 'Hooks removed from settings.json'
200
+ : hooksRemoveError
201
+ ? `Warning: ${hooksRemoveError}`
202
+ : 'No hooks found in settings.json';
203
+
124
204
  const extras = [
205
+ listenerStopped ? 'Listener daemon stopped.' : '',
125
206
  pluginEntryRemoved || cacheRemoved ? 'Plugin registration cleaned.' : '',
126
207
  cliBinsRemoved ? 'CLI wrapper scripts removed.' : '',
127
208
  ].filter(Boolean).join('\n');
@@ -132,13 +213,13 @@ if (cacheStillExists) {
132
213
  Warning: Could not fully remove plugin cache directory:
133
214
  ${pluginCacheDir}
134
215
  Please remove it manually.
135
- Hooks removed from settings.json
216
+ ${hooksStatus}
136
217
  Config files deleted.${extras ? `\n${extras}` : ''}
137
218
  `);
138
219
  } else {
139
220
  console.log(`
140
221
  Claude Notification Plugin uninstalled.
141
- Hooks removed from settings.json
222
+ ${hooksStatus}
142
223
  Config files deleted.${extras ? `\n${extras}` : ''}
143
224
  `);
144
225
  }
package/commit-sha CHANGED
@@ -1 +1 @@
1
- cea9eaeeae4658684c2f1977bdc09fa20916bf7c
1
+ 8cd1016fa338ed33fd68b1c9350ca62df5b00bf4
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.99",
4
+ "version": "1.0.102",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {