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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +41 -15
- package/bin/cli.js +7 -7
- package/bin/install.js +14 -3
- package/bin/listener-cli.js +90 -13
- package/bin/uninstall.js +18 -1
- package/commit-sha +1 -1
- package/listener/LISTENER-DETAILED.md +20 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
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
|
|
216
|
-
claude-notify listener 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(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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);
|
package/bin/listener-cli.js
CHANGED
|
@@ -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(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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": {
|