claude-notification-plugin 1.0.59 → 1.0.63
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 +42 -54
- package/bin/listener-cli.js +255 -0
- package/commands/listener.md +100 -0
- package/listener/listener.js +613 -0
- package/listener/logger.js +46 -0
- package/listener/message-parser.js +100 -0
- package/listener/task-runner.js +148 -0
- package/listener/telegram-poller.js +142 -0
- package/listener/work-queue.js +306 -0
- package/listener/worktree-manager.js +279 -0
- package/notifier/notifier.js +4 -2
- package/package.json +4 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.63",
|
|
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
|
@@ -6,18 +6,19 @@ Cross-platform notifications for Claude Code task completion. Sends alerts to Te
|
|
|
6
6
|
|
|
7
7
|
- Desktop notifications (Windows toast, macOS Notification Center, Linux notify-send)
|
|
8
8
|
- Telegram bot messages with auto-delete
|
|
9
|
-
- Sound alert
|
|
10
|
-
- Voice announcement
|
|
9
|
+
- Sound alert
|
|
10
|
+
- Voice announcement
|
|
11
11
|
- Separate notifications for task completion and waiting-for-input events
|
|
12
12
|
- Skips short tasks (< 15s by default)
|
|
13
13
|
- Granular per-channel enable/disable (globally and per-project)
|
|
14
14
|
- Debug mode with full hook event dump
|
|
15
|
+
- [Telegram Listener](LISTENER.md) — your remote control for Claude: send a message in Telegram, and the task starts running on your PC
|
|
15
16
|
|
|
16
17
|
## Install
|
|
17
18
|
|
|
18
19
|
### Option A: Claude Code Plugin (recommended)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Auto-updates, seamless integration with the plugin ecosystem.
|
|
21
22
|
|
|
22
23
|
```shell
|
|
23
24
|
/plugin marketplace add Bazilio-san/claude-plugins
|
|
@@ -26,26 +27,15 @@ Add the marketplace and install:
|
|
|
26
27
|
/claude-notification-plugin:setup
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Or load directly for testing:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
claude --plugin-dir /path/to/claude-notification-plugin
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Hooks are registered automatically.
|
|
38
|
-
|
|
39
|
-
To enable auto-updates, go to `/plugin` → **Marketplaces** tab → select `bazilio-plugins` → **Enable auto-update**.
|
|
30
|
+
Go to `/plugin` → **Marketplaces** tab → select `bazilio-plugins` → **Enable auto-update**.
|
|
40
31
|
|
|
41
|
-
|
|
32
|
+
For a detailed visual walkthrough, see [step-by-step installation guide with screenshots](INSTALL_MARKETPLACE_AND_PLUGIN.md).
|
|
42
33
|
|
|
43
|
-
```shell
|
|
44
|
-
/claude-notification-plugin:setup
|
|
45
|
-
```
|
|
46
34
|
|
|
47
35
|
### Option B: npm global package
|
|
48
36
|
|
|
37
|
+
Simple install, works without the plugin system.
|
|
38
|
+
|
|
49
39
|
```bash
|
|
50
40
|
npm install -g claude-notification-plugin
|
|
51
41
|
claude-notify-install
|
|
@@ -89,39 +79,27 @@ Config file: `~/.claude/notifier.config.json`
|
|
|
89
79
|
}
|
|
90
80
|
```
|
|
91
81
|
|
|
92
|
-
Each channel has an `enabled` flag (`true`/`false`) for global control.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
102
|
-
`
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
`
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
| Variable | Channel |
|
|
115
|
-
|-------------------------------------------------------|-----------------------------------------|
|
|
116
|
-
| `CLAUDE_NOTIFY_TELEGRAM` | Telegram messages |
|
|
117
|
-
| `CLAUDE_NOTIFY_DESKTOP` | Desktop notifications |
|
|
118
|
-
| `CLAUDE_NOTIFY_SOUND` | Sound alert |
|
|
119
|
-
| `CLAUDE_NOTIFY_VOICE` | Voice announcement (TTS) |
|
|
120
|
-
| `CLAUDE_NOTIFY_WAITING` | Waiting-for-input events |
|
|
121
|
-
| `CLAUDE_NOTIFY_DEBUG` | Debug mode |
|
|
122
|
-
| `CLAUDE_NOTIFY_INCLUDE_LAST_CC_MESSAGE_IN_TELEGRAM` | Include last Claude message in Telegram |
|
|
123
|
-
| `CLAUDE_NOTIFY_WEBHOOK_URL` | Webhook URL for POST requests |
|
|
124
|
-
| `CLAUDE_NOTIFY_SEND_USER_PROMPT_TO_WEBHOOK` | Send user prompts to webhook |
|
|
82
|
+
Each channel has an `enabled` flag (`true`/`false`) for global control. Environment variables override config values (`"1"` = on, `"0"` = off, or a string for URLs).
|
|
83
|
+
|
|
84
|
+
| Config option | Env var override | Default | Description |
|
|
85
|
+
|---|---|---|---|
|
|
86
|
+
| `telegram.enabled` | `CLAUDE_NOTIFY_TELEGRAM` | `true` | Telegram messages |
|
|
87
|
+
| `telegram.token` | `CLAUDE_NOTIFY_TELEGRAM_TOKEN` | — | Bot token |
|
|
88
|
+
| `telegram.chatId` | `CLAUDE_NOTIFY_TELEGRAM_CHAT_ID` | — | Chat ID |
|
|
89
|
+
| `telegram.deleteAfterHours` | — | `24` | Auto-delete old messages (hours, `0` to disable) |
|
|
90
|
+
| `telegram.includeLastCcMessageInTelegram` | `CLAUDE_NOTIFY_INCLUDE_LAST_CC_MESSAGE_IN_TELEGRAM` | `true` | Append Claude's last message to Telegram notification (truncated to 3500 chars) |
|
|
91
|
+
| `desktopNotification.enabled` | `CLAUDE_NOTIFY_DESKTOP` | `true` | Desktop notifications (toast / Notification Center / notify-send) |
|
|
92
|
+
| `sound.enabled` | `CLAUDE_NOTIFY_SOUND` | `true` | Sound alert |
|
|
93
|
+
| `sound.file` | — | platform default | Path to a custom sound file |
|
|
94
|
+
| `voice.enabled` | `CLAUDE_NOTIFY_VOICE` | `true` | Voice announcement (TTS) |
|
|
95
|
+
| `notifyOnWaiting` | `CLAUDE_NOTIFY_WAITING` | `false` | Notify when Claude is waiting for input (e.g. permission prompts) |
|
|
96
|
+
| `webhookUrl` | `CLAUDE_NOTIFY_WEBHOOK_URL` | `""` | POST notification data (JSON) to this URL. Payload: `title`, `project`, `branch`, `duration`, `trigger`, `voicePhrase`, `hookEvent` |
|
|
97
|
+
| `sendUserPromptToWebhook` | `CLAUDE_NOTIFY_SEND_USER_PROMPT_TO_WEBHOOK` | `false` | Also send user prompts to the webhook. Payload: `title`, `project`, `trigger`, `prompt`, `hookEvent` |
|
|
98
|
+
| `minSeconds` | — | `15` | Skip notifications for tasks shorter than this (seconds) |
|
|
99
|
+
| `debug` | `CLAUDE_NOTIFY_DEBUG` | `false` | Include voice phrase text and full hook event JSON in notifications |
|
|
100
|
+
| — | `CLAUDE_NOTIFY_DISABLE` | `0` | Disable all notifications (`1` to disable) |
|
|
101
|
+
|
|
102
|
+
Default sound files: Windows `C:/Windows/Media/notify.wav`, macOS `/System/Library/Sounds/Glass.aiff`, Linux `/usr/share/sounds/freedesktop/stereo/complete.oga`.
|
|
125
103
|
|
|
126
104
|
### Per-project configuration
|
|
127
105
|
|
|
@@ -164,7 +142,6 @@ Notifications include project name, git branch (when available), duration, and t
|
|
|
164
142
|
Project: my-project
|
|
165
143
|
Branch: feature-auth
|
|
166
144
|
Duration: 45s
|
|
167
|
-
Trigger: Stop
|
|
168
145
|
```
|
|
169
146
|
|
|
170
147
|
When Claude is waiting for input (and `notifyOnWaiting` is enabled):
|
|
@@ -175,7 +152,6 @@ When Claude is waiting for input (and `notifyOnWaiting` is enabled):
|
|
|
175
152
|
Project: my-project
|
|
176
153
|
Branch: feature-auth
|
|
177
154
|
Duration: 30s
|
|
178
|
-
Trigger: Notification
|
|
179
155
|
```
|
|
180
156
|
|
|
181
157
|
## Telegram Setup
|
|
@@ -190,19 +166,31 @@ Trigger: Notification
|
|
|
190
166
|
|
|
191
167
|
Alternative for Chat ID: add **@userinfobot** to a chat and it will reply with the ID.
|
|
192
168
|
|
|
169
|
+
## Telegram Listener (Telegram → Claude Code)
|
|
170
|
+
|
|
171
|
+
Your remote control for Claude: send a message in Telegram, and the task starts running on your PC. See **[LISTENER.md](LISTENER.md)** for the full guide.
|
|
172
|
+
|
|
193
173
|
## Uninstall
|
|
194
174
|
|
|
195
175
|
### Plugin install
|
|
196
176
|
|
|
197
177
|
Uninstall via the plugin manager or remove `--plugin-dir` flag.
|
|
198
178
|
|
|
199
|
-
### npm
|
|
179
|
+
### npm
|
|
200
180
|
|
|
201
181
|
```bash
|
|
202
182
|
claude-notify-uninstall
|
|
203
183
|
npm uninstall -g claude-notification-plugin
|
|
204
184
|
```
|
|
205
185
|
|
|
186
|
+
## Testing (load without install)
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
claude --plugin-dir /path/to/claude-notification-plugin
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Hooks are registered automatically.
|
|
193
|
+
|
|
206
194
|
## License
|
|
207
195
|
|
|
208
196
|
MIT
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { spawn, execSync } from 'child_process';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
const PID_FILE = path.join(os.homedir(), '.claude', '.listener.pid');
|
|
13
|
+
const LOG_FILE = path.join(os.homedir(), '.claude', '.listener.log');
|
|
14
|
+
const CONFIG_FILE = path.join(os.homedir(), '.claude', 'notifier.config.json');
|
|
15
|
+
const LISTENER_SCRIPT = path.join(__dirname, '..', 'listener', 'listener.js');
|
|
16
|
+
|
|
17
|
+
const command = process.argv[2];
|
|
18
|
+
|
|
19
|
+
switch (command) {
|
|
20
|
+
case 'start':
|
|
21
|
+
startDaemon();
|
|
22
|
+
break;
|
|
23
|
+
case 'stop':
|
|
24
|
+
stopDaemon();
|
|
25
|
+
break;
|
|
26
|
+
case 'status':
|
|
27
|
+
showStatus();
|
|
28
|
+
break;
|
|
29
|
+
case 'logs':
|
|
30
|
+
showLogs();
|
|
31
|
+
break;
|
|
32
|
+
case 'restart':
|
|
33
|
+
stopDaemon();
|
|
34
|
+
setTimeout(() => startDaemon(), 1000);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
console.log('Usage: claude-notify-listener <start|stop|status|logs|restart>');
|
|
38
|
+
console.log('');
|
|
39
|
+
console.log('Commands:');
|
|
40
|
+
console.log(' start Start the listener daemon');
|
|
41
|
+
console.log(' stop Stop the listener daemon');
|
|
42
|
+
console.log(' status Show daemon status');
|
|
43
|
+
console.log(' logs Show recent log entries');
|
|
44
|
+
console.log(' restart Restart the daemon');
|
|
45
|
+
process.exit(command ? 1 : 0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function startDaemon () {
|
|
49
|
+
// Check if already running
|
|
50
|
+
const existingPid = readPid();
|
|
51
|
+
if (existingPid && isProcessAlive(existingPid)) {
|
|
52
|
+
console.log(`Listener is already running (PID: ${existingPid})`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Clean stale PID file
|
|
57
|
+
if (existingPid) {
|
|
58
|
+
try {
|
|
59
|
+
fs.unlinkSync(PID_FILE);
|
|
60
|
+
} catch {
|
|
61
|
+
// ignore
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Validate config
|
|
66
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
67
|
+
console.error(`Config not found: ${CONFIG_FILE}`);
|
|
68
|
+
console.error('Run claude-notify-install first, or create the config manually.');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let config;
|
|
73
|
+
try {
|
|
74
|
+
config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error(`Invalid config: ${err.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const token = process.env.CLAUDE_NOTIFY_TELEGRAM_TOKEN || config.telegramToken || config.telegram?.token;
|
|
81
|
+
const chatId = process.env.CLAUDE_NOTIFY_TELEGRAM_CHAT_ID || config.telegramChatId || config.telegram?.chatId;
|
|
82
|
+
|
|
83
|
+
if (!token || !chatId) {
|
|
84
|
+
console.error('Missing telegramToken or telegramChatId in config.');
|
|
85
|
+
console.error('These are required for the listener to receive messages.');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!config.listener?.projects || Object.keys(config.listener.projects).length === 0) {
|
|
90
|
+
console.error('No projects defined in config.listener.projects');
|
|
91
|
+
console.error('');
|
|
92
|
+
console.error('Add projects to your config:');
|
|
93
|
+
console.error(JSON.stringify({
|
|
94
|
+
listener: {
|
|
95
|
+
projects: {
|
|
96
|
+
default: { path: '/path/to/your/project' },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}, null, 2));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Ensure log directory exists
|
|
104
|
+
fs.mkdirSync(path.dirname(LOG_FILE), { recursive: true });
|
|
105
|
+
|
|
106
|
+
// Open log file for child stdio
|
|
107
|
+
const logFd = fs.openSync(LOG_FILE, 'a');
|
|
108
|
+
|
|
109
|
+
// Spawn detached child
|
|
110
|
+
const child = spawn(process.execPath, [LISTENER_SCRIPT], {
|
|
111
|
+
detached: true,
|
|
112
|
+
stdio: ['ignore', logFd, logFd],
|
|
113
|
+
env: { ...process.env },
|
|
114
|
+
windowsHide: true,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
child.unref();
|
|
118
|
+
fs.closeSync(logFd);
|
|
119
|
+
|
|
120
|
+
// Write PID
|
|
121
|
+
fs.mkdirSync(path.dirname(PID_FILE), { recursive: true });
|
|
122
|
+
fs.writeFileSync(PID_FILE, String(child.pid));
|
|
123
|
+
|
|
124
|
+
console.log(`Listener started (PID: ${child.pid})`);
|
|
125
|
+
console.log(`Log: ${LOG_FILE}`);
|
|
126
|
+
console.log(`Projects: ${Object.keys(config.listener.projects).join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function stopDaemon () {
|
|
130
|
+
const pid = readPid();
|
|
131
|
+
if (!pid) {
|
|
132
|
+
console.log('Listener is not running (no PID file)');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!isProcessAlive(pid)) {
|
|
137
|
+
console.log(`Listener is not running (stale PID: ${pid})`);
|
|
138
|
+
cleanPid();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(`Stopping listener (PID: ${pid})...`);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
if (process.platform === 'win32') {
|
|
146
|
+
execSync(`taskkill /PID ${pid} /T /F`, {
|
|
147
|
+
stdio: 'ignore',
|
|
148
|
+
windowsHide: true,
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
process.kill(pid, 'SIGTERM');
|
|
152
|
+
// Wait for graceful shutdown
|
|
153
|
+
let tries = 10;
|
|
154
|
+
while (tries-- > 0 && isProcessAlive(pid)) {
|
|
155
|
+
execSync('sleep 0.5', { stdio: 'ignore' });
|
|
156
|
+
}
|
|
157
|
+
if (isProcessAlive(pid)) {
|
|
158
|
+
process.kill(pid, 'SIGKILL');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// Process may already be dead
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
cleanPid();
|
|
166
|
+
console.log('Listener stopped');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function showStatus () {
|
|
170
|
+
const pid = readPid();
|
|
171
|
+
if (!pid) {
|
|
172
|
+
console.log('Status: not running');
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!isProcessAlive(pid)) {
|
|
177
|
+
console.log(`Status: not running (stale PID: ${pid})`);
|
|
178
|
+
cleanPid();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log(`Status: running (PID: ${pid})`);
|
|
183
|
+
console.log(`Log: ${LOG_FILE}`);
|
|
184
|
+
|
|
185
|
+
// Show last few log lines
|
|
186
|
+
try {
|
|
187
|
+
if (fs.existsSync(LOG_FILE)) {
|
|
188
|
+
const content = fs.readFileSync(LOG_FILE, 'utf-8');
|
|
189
|
+
const lines = content.trim().split('\n');
|
|
190
|
+
const last = lines.slice(-5);
|
|
191
|
+
console.log('\nRecent log:');
|
|
192
|
+
for (const line of last) {
|
|
193
|
+
console.log(` ${line}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// ignore
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function showLogs () {
|
|
202
|
+
try {
|
|
203
|
+
if (!fs.existsSync(LOG_FILE)) {
|
|
204
|
+
console.log('No log file found');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const content = fs.readFileSync(LOG_FILE, 'utf-8');
|
|
208
|
+
const lines = content.trim().split('\n');
|
|
209
|
+
const last = lines.slice(-50);
|
|
210
|
+
for (const line of last) {
|
|
211
|
+
console.log(line);
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
console.error(`Failed to read log: ${err.message}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function readPid () {
|
|
219
|
+
try {
|
|
220
|
+
if (fs.existsSync(PID_FILE)) {
|
|
221
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);
|
|
222
|
+
return isNaN(pid) ? null : pid;
|
|
223
|
+
}
|
|
224
|
+
} catch {
|
|
225
|
+
// ignore
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function cleanPid () {
|
|
231
|
+
try {
|
|
232
|
+
if (fs.existsSync(PID_FILE)) {
|
|
233
|
+
fs.unlinkSync(PID_FILE);
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
// ignore
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isProcessAlive (pid) {
|
|
241
|
+
try {
|
|
242
|
+
if (process.platform === 'win32') {
|
|
243
|
+
const result = execSync(`tasklist /FI "PID eq ${pid}" /NH`, {
|
|
244
|
+
encoding: 'utf-8',
|
|
245
|
+
windowsHide: true,
|
|
246
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
247
|
+
});
|
|
248
|
+
return result.includes(String(pid));
|
|
249
|
+
}
|
|
250
|
+
process.kill(pid, 0);
|
|
251
|
+
return true;
|
|
252
|
+
} catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manage the Telegram Listener daemon (start, stop, status, logs, restart)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Listener Management
|
|
6
|
+
|
|
7
|
+
Manage the Telegram Listener daemon that receives tasks from Telegram and executes them via `claude -p`.
|
|
8
|
+
|
|
9
|
+
The user invokes this command as `/claude-notification-plugin:listener <action>`.
|
|
10
|
+
|
|
11
|
+
## Actions
|
|
12
|
+
|
|
13
|
+
Determine the action from the user's input. If no action is provided, show the help text below and ask which action they want.
|
|
14
|
+
|
|
15
|
+
### start
|
|
16
|
+
|
|
17
|
+
Start the listener daemon.
|
|
18
|
+
|
|
19
|
+
1. Determine the plugin root path — use the directory where this command file lives: `${CLAUDE_PLUGIN_ROOT}`. The listener CLI script is at `${CLAUDE_PLUGIN_ROOT}/bin/listener-cli.js`.
|
|
20
|
+
2. Run in a shell:
|
|
21
|
+
```bash
|
|
22
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/listener-cli.js" start
|
|
23
|
+
```
|
|
24
|
+
3. Show the output to the user. If it says "Listener started", confirm success. If there is an error (missing config, missing projects, already running), explain what to do.
|
|
25
|
+
|
|
26
|
+
### stop
|
|
27
|
+
|
|
28
|
+
Stop the listener daemon.
|
|
29
|
+
|
|
30
|
+
1. Run:
|
|
31
|
+
```bash
|
|
32
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/listener-cli.js" stop
|
|
33
|
+
```
|
|
34
|
+
2. Show the output.
|
|
35
|
+
|
|
36
|
+
### status
|
|
37
|
+
|
|
38
|
+
Show the listener status.
|
|
39
|
+
|
|
40
|
+
1. Run:
|
|
41
|
+
```bash
|
|
42
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/listener-cli.js" status
|
|
43
|
+
```
|
|
44
|
+
2. Show the output.
|
|
45
|
+
|
|
46
|
+
### logs
|
|
47
|
+
|
|
48
|
+
Show recent log entries.
|
|
49
|
+
|
|
50
|
+
1. Run:
|
|
51
|
+
```bash
|
|
52
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/listener-cli.js" logs
|
|
53
|
+
```
|
|
54
|
+
2. Show the output.
|
|
55
|
+
|
|
56
|
+
### restart
|
|
57
|
+
|
|
58
|
+
Restart the listener daemon.
|
|
59
|
+
|
|
60
|
+
1. Run:
|
|
61
|
+
```bash
|
|
62
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/listener-cli.js" restart
|
|
63
|
+
```
|
|
64
|
+
2. Show the output.
|
|
65
|
+
|
|
66
|
+
## Help text
|
|
67
|
+
|
|
68
|
+
If the user doesn't specify an action, show:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Telegram Listener — receives tasks from Telegram and executes via claude -p.
|
|
72
|
+
|
|
73
|
+
Usage:
|
|
74
|
+
/claude-notification-plugin:listener start — Start the daemon
|
|
75
|
+
/claude-notification-plugin:listener stop — Stop the daemon
|
|
76
|
+
/claude-notification-plugin:listener status — Show status
|
|
77
|
+
/claude-notification-plugin:listener logs — Show recent logs
|
|
78
|
+
/claude-notification-plugin:listener restart — Restart the daemon
|
|
79
|
+
|
|
80
|
+
Prerequisites:
|
|
81
|
+
1. Telegram bot configured (token + chatId in ~/.claude/notifier.config.json)
|
|
82
|
+
2. "listener.projects" section in config with at least one project
|
|
83
|
+
|
|
84
|
+
See LISTENER.md for detailed documentation.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Important
|
|
88
|
+
|
|
89
|
+
- The `${CLAUDE_PLUGIN_ROOT}` variable resolves to the plugin installation directory. Always use it to build the path to `bin/listener-cli.js`.
|
|
90
|
+
- If the config doesn't have a `listener.projects` section, tell the user to add one and show a minimal example:
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"listener": {
|
|
94
|
+
"projects": {
|
|
95
|
+
"default": { "path": "/path/to/your/project" }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
- If Telegram credentials are missing, suggest running `/claude-notification-plugin:setup` first.
|