claude-notification-plugin 1.0.96 → 1.0.99

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.96",
3
+ "version": "1.0.99",
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
@@ -24,6 +24,23 @@ Sends alerts to Telegram and desktop (Windows, macOS, Linux) when Claude finishe
24
24
  npm install -g claude-notification-plugin
25
25
  ```
26
26
 
27
+
28
+ ## Telegram Setup
29
+
30
+ If you plan to work with Telegram, you need to pre-register the bot and send a message to it
31
+
32
+ 1. Open Telegram, find **@BotFather**
33
+ 2. Send `/newbot`, follow prompts, pick a name
34
+ 3. Copy the bot token (format: `123456789:ABCdef...`)
35
+ 4. **Send any message to your new bot**
36
+
37
+
38
+ ## Setup
39
+
40
+ ```bash
41
+ claude-notify install
42
+ ```
43
+
27
44
  The installer prompts for Telegram bot credentials and sets everything up.
28
45
  Re-run `claude-notify install` anytime to reconfigure.
29
46
 
@@ -151,24 +168,21 @@ Add to `.claude/settings.local.json` in the project root:
151
168
  }
152
169
  ```
153
170
 
154
- ## Telegram Setup
155
-
156
- 1. Open Telegram, find **@BotFather**
157
- 2. Send `/newbot`, follow prompts, pick a name
158
- 3. Copy the bot token (format: `123456789:ABCdef...`)
159
- 4. **Send any message to your new bot**
160
- 5. Open `https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates`
161
- 6. Find `"chat":{"id":123456789}` in the response — that's your Chat ID
162
-
163
- Alternative: add **@userinfobot** to a chat and it will reply with the ID.
164
-
165
171
  ## Telegram Listener
166
172
 
167
173
  Background daemon that receives tasks from Telegram and executes them via `claude -p`. The result is sent back to Telegram.
168
174
 
169
175
  The Listener uses the same bot and `chatId` as notifications.
170
176
 
171
- ### 1. Add a `listener` section to config
177
+ ### 1. Configure the listener
178
+
179
+ Run the interactive setup wizard:
180
+
181
+ ```bash
182
+ claude-notify listener setup
183
+ ```
184
+
185
+ Alternatively, add a `listener` section to config manually:
172
186
 
173
187
  ```json
174
188
  {
@@ -212,9 +226,10 @@ The bot replies with status and results:
212
226
 
213
227
  ```bash
214
228
  claude-notify listener status # Check if running
215
- claude-notify listener stop # Stop
216
- claude-notify listener restart # Restart
229
+ claude-notify listener stop
230
+ claude-notify listener restart
217
231
  claude-notify listener logs # View last 50 log lines
232
+ claude-notify listener setup # Interactive listener configuration
218
233
  ```
219
234
 
220
235
  ### 5. Bot commands
@@ -264,14 +279,25 @@ Worktrees are auto-created when you use `@project/branch` syntax (controlled by
264
279
  /rmworktree @api feature/payments ← remove
265
280
  ```
266
281
 
282
+
267
283
  [Detailed Guide](listener/LISTENER-DETAILED.md) — internals, architecture, troubleshooting, full session example.
268
284
 
285
+
286
+ ## Manual Telegram bot chatId retrieval:
287
+
288
+ - Open `https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates`
289
+ - Find `"chat":{"id":123456789}` in the response — that's your Chat ID
290
+
291
+ Alternative: add **@userinfobot** to a chat and it will reply with the ID.
292
+
293
+
269
294
  ## CLI Commands
270
295
 
271
296
  ```
272
297
  claude-notify install Reinstall plugin registration, Telegram config, hooks
298
+ claude-notify uninstall Remove plugin, hooks, config, CLI wrappers
273
299
  claude-notify listener <action> Manage the Telegram Listener daemon
274
- Actions: start, stop, status, logs, restart
300
+ Actions: start, stop, status, setup, logs, restart
275
301
  ```
276
302
 
277
303
  ## License
package/bin/cli.js CHANGED
@@ -29,13 +29,13 @@ switch (command) {
29
29
  if (!process.stdin.isTTY) {
30
30
  await import(toImportUrl(path.join('..', 'notifier', 'notifier.js')));
31
31
  } else {
32
- console.log('Usage: claude-notify <command> [options]');
33
- console.log('');
34
- console.log('Commands:');
35
- console.log(' install Setup plugin registration, Telegram config, hooks');
36
- console.log(' uninstall Remove plugin, hooks, config, CLI wrappers');
37
- console.log(' listener <action> Manage the Telegram Listener daemon');
38
- console.log(' Actions: start, stop, status, logs, restart');
32
+ console.log(`Usage: claude-notify <command> [options]
33
+
34
+ Commands:
35
+ install Setup plugin registration, Telegram config, hooks
36
+ uninstall Remove plugin, hooks, config, CLI wrappers
37
+ listener <action> Manage the Telegram Listener daemon
38
+ Actions: start, stop, status, logs, restart`);
39
39
  process.exit(command ? 1 : 0);
40
40
  }
41
41
  }
package/bin/install.js CHANGED
@@ -484,6 +484,19 @@ Claude Notification Plugin - Setup
484
484
  addHook(settings, 'Stop');
485
485
  addHook(settings, 'Notification');
486
486
 
487
+ // Register plugin as enabled
488
+ settings.enabledPlugins = settings.enabledPlugins || {};
489
+ settings.enabledPlugins[PLUGIN_KEY] = true;
490
+
491
+ // Register marketplace
492
+ settings.extraKnownMarketplaces = settings.extraKnownMarketplaces || {};
493
+ settings.extraKnownMarketplaces[MARKETPLACE_KEY] = {
494
+ source: {
495
+ source: 'github',
496
+ repo: MARKETPLACE_GITHUB,
497
+ },
498
+ };
499
+
487
500
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
488
501
 
489
502
  // 5. Summary
@@ -513,9 +526,7 @@ ${telegramStatus}${platformTip}
513
526
 
514
527
  To uninstall: claude-notify uninstall
515
528
 
516
- To disable per project, add to .claude/settings.local.json:
517
- { "env": { "CLAUDE_NOTIFY_DISABLE": "1" } }
518
- `);
529
+ To disable per project, add to .claude/settings.local.json: { "env": { "CLAUDE_NOTIFY_DISABLE": "1" } }`);
519
530
  }
520
531
 
521
532
  main().then(() => 0);
@@ -3,6 +3,7 @@
3
3
  import fs from 'fs';
4
4
  import os from 'os';
5
5
  import path from 'path';
6
+ import readline from 'readline';
6
7
  import { spawn, execSync } from 'child_process';
7
8
  import { fileURLToPath } from 'url';
8
9
 
@@ -43,15 +44,19 @@ switch (command) {
43
44
  stopDaemon();
44
45
  setTimeout(() => startDaemon(), 1000);
45
46
  break;
47
+ case 'setup':
48
+ setupListener();
49
+ break;
46
50
  default:
47
- console.log('Usage: claude-notify listener <start|stop|status|logs|restart>');
48
- console.log('');
49
- console.log('Commands:');
50
- console.log(' start Start the listener daemon');
51
- console.log(' stop Stop the listener daemon');
52
- console.log(' status Show daemon status');
53
- console.log(' logs Show recent log entries');
54
- console.log(' restart Restart the daemon');
51
+ console.log(`Usage: claude-notify listener <start|stop|status|setup|logs|restart>
52
+
53
+ Commands:
54
+ start Start the listener daemon
55
+ stop Stop the listener daemon
56
+ status Show daemon status
57
+ setup Interactive listener configuration
58
+ logs Show recent log entries
59
+ restart Restart the daemon`);
55
60
  process.exit(command ? 1 : 0);
56
61
  }
57
62
 
@@ -132,9 +137,9 @@ function startDaemon () {
132
137
  fs.mkdirSync(path.dirname(PID_FILE), { recursive: true });
133
138
  fs.writeFileSync(PID_FILE, String(child.pid));
134
139
 
135
- console.log(`Listener started (PID: ${child.pid})`);
136
- console.log(`Log: ${logFile}`);
137
- console.log(`Projects: ${Object.keys(config.listener.projects).join(', ')}`);
140
+ console.log(`Listener started (PID: ${child.pid})
141
+ Log: ${logFile}
142
+ Projects: ${Object.keys(config.listener.projects).join(', ')}`);
138
143
  }
139
144
 
140
145
  function stopDaemon () {
@@ -191,8 +196,8 @@ function showStatus () {
191
196
  }
192
197
 
193
198
  const logFile = getLogFile();
194
- console.log(`Status: running (PID: ${pid})`);
195
- console.log(`Log: ${logFile}`);
199
+ console.log(`Status: running (PID: ${pid})
200
+ Log: ${logFile}`);
196
201
 
197
202
  // Show last few log lines
198
203
  try {
@@ -266,3 +271,75 @@ function isProcessAlive (pid) {
266
271
  return false;
267
272
  }
268
273
  }
274
+
275
+ function ask (rl, question) {
276
+ return new Promise((resolve) => {
277
+ rl.question(question, (answer) => resolve(answer.trim()));
278
+ });
279
+ }
280
+
281
+ async function setupListener () {
282
+ let config = {};
283
+ if (fs.existsSync(CONFIG_FILE)) {
284
+ try {
285
+ config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
286
+ } catch {
287
+ // ignore
288
+ }
289
+ }
290
+
291
+ config.listener = config.listener || {};
292
+ const L = config.listener;
293
+ const home = os.homedir();
294
+
295
+ const defaults = {
296
+ worktreeBaseDir: L.worktreeBaseDir || path.join(home, '.claude', 'worktrees'),
297
+ taskTimeoutMinutes: L.taskTimeoutMinutes ?? 30,
298
+ maxQueuePerWorkDir: L.maxQueuePerWorkDir ?? 10,
299
+ maxTotalTasks: L.maxTotalTasks ?? 50,
300
+ logDir: L.logDir || '',
301
+ taskLogDir: L.taskLogDir || '',
302
+ projectPath: L.projects?.default?.path || '',
303
+ };
304
+
305
+ const rl = readline.createInterface({
306
+ input: process.stdin,
307
+ output: process.stdout,
308
+ });
309
+
310
+ console.log(`
311
+ Listener Setup
312
+ ==============
313
+ Press Enter to keep current value shown in [brackets].
314
+ `);
315
+
316
+ const worktreeBaseDir = await ask(rl, `Worktree base dir [${defaults.worktreeBaseDir}]: `) || defaults.worktreeBaseDir;
317
+ const taskTimeoutStr = await ask(rl, `Task timeout, minutes [${defaults.taskTimeoutMinutes}]: `) || String(defaults.taskTimeoutMinutes);
318
+ const maxQueueStr = await ask(rl, `Max queue per work dir [${defaults.maxQueuePerWorkDir}]: `) || String(defaults.maxQueuePerWorkDir);
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;
322
+ const projectPath = await ask(rl, `Default project path [${defaults.projectPath || '(none)'}]: `) || defaults.projectPath;
323
+
324
+ rl.close();
325
+
326
+ L.worktreeBaseDir = worktreeBaseDir;
327
+ L.taskTimeoutMinutes = parseInt(taskTimeoutStr, 10) || defaults.taskTimeoutMinutes;
328
+ L.maxQueuePerWorkDir = parseInt(maxQueueStr, 10) || defaults.maxQueuePerWorkDir;
329
+ L.maxTotalTasks = parseInt(maxTotalStr, 10) || defaults.maxTotalTasks;
330
+ L.logDir = logDir;
331
+ L.taskLogDir = taskLogDir;
332
+
333
+ if (projectPath) {
334
+ L.projects = L.projects || {};
335
+ L.projects.default = L.projects.default || {};
336
+ L.projects.default.path = projectPath;
337
+ }
338
+
339
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
340
+
341
+ console.log(`
342
+ Listener config saved to ${CONFIG_FILE}
343
+ Run "claude-notify listener start" to apply.
344
+ `);
345
+ }
package/bin/uninstall.js CHANGED
@@ -12,6 +12,8 @@ const settingsPath = path.join(claudeDir, 'settings.json');
12
12
  const statePath = path.join(claudeDir, '.notifier_state.json');
13
13
 
14
14
  const HOOK_COMMAND = 'claude-notify';
15
+ const PLUGIN_KEY = 'claude-notification-plugin@bazilio-plugins';
16
+ const MARKETPLACE_KEY = 'bazilio-plugins';
15
17
 
16
18
  // Remove hooks from settings.json
17
19
  if (fs.existsSync(settingsPath)) {
@@ -34,6 +36,22 @@ if (fs.existsSync(settingsPath)) {
34
36
  }
35
37
  }
36
38
 
39
+ // Remove plugin from enabledPlugins
40
+ if (settings.enabledPlugins?.[PLUGIN_KEY]) {
41
+ delete settings.enabledPlugins[PLUGIN_KEY];
42
+ if (Object.keys(settings.enabledPlugins).length === 0) {
43
+ delete settings.enabledPlugins;
44
+ }
45
+ }
46
+
47
+ // Remove marketplace from extraKnownMarketplaces
48
+ if (settings.extraKnownMarketplaces?.[MARKETPLACE_KEY]) {
49
+ delete settings.extraKnownMarketplaces[MARKETPLACE_KEY];
50
+ if (Object.keys(settings.extraKnownMarketplaces).length === 0) {
51
+ delete settings.extraKnownMarketplaces;
52
+ }
53
+ }
54
+
37
55
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
38
56
  } catch {
39
57
  // ignore
@@ -80,7 +98,6 @@ for (const dir of wrapperDirs) {
80
98
  }
81
99
 
82
100
  // Remove from installed_plugins.json
83
- const PLUGIN_KEY = 'claude-notification-plugin@bazilio-plugins';
84
101
  const installedPluginsPath = path.join(claudeDir, 'plugins', 'installed_plugins.json');
85
102
  let pluginEntryRemoved = false;
86
103
  if (fs.existsSync(installedPluginsPath)) {
package/commit-sha CHANGED
@@ -1 +1 @@
1
- dbbfbd026315df5250a8765f89adbd2e125e745e
1
+ cea9eaeeae4658684c2f1977bdc09fa20916bf7c
@@ -276,6 +276,26 @@ TaskRunner emit 'complete'/'error'/'timeout'
276
276
 
277
277
  ## Configuration
278
278
 
279
+ ### Interactive setup
280
+
281
+ ```bash
282
+ claude-notify listener setup
283
+ ```
284
+
285
+ Prompts for each listener setting interactively. Current values are shown in `[brackets]` — press Enter to keep them:
286
+
287
+ - **Worktree base dir** — where auto-created worktrees are stored
288
+ - **Task timeout, minutes** — max execution time per task
289
+ - **Max queue per work dir** — queue limit per working directory
290
+ - **Max total tasks** — global task limit across all queues
291
+ - **Log dir** — listener operational log directory
292
+ - **Task log dir** — task Q&A log directory
293
+ - **Default project path** — path for the `default` project alias
294
+
295
+ Re-run `claude-notify listener setup` anytime to reconfigure.
296
+
297
+ ### Manual configuration
298
+
279
299
  Full example of `~/.claude/notifier.config.json` with the listener section:
280
300
 
281
301
  ```json
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.96",
4
+ "version": "1.0.99",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {