claude-notification-plugin 1.1.43 → 1.1.52
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/commit-sha +1 -1
- package/listener/message-parser.js +8 -0
- package/listener/pty-runner.js +86 -5
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-notification-plugin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.52",
|
|
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/commit-sha
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0a1e42c292641f978dba9e382f7ff36af09b1ea8
|
|
@@ -6,6 +6,9 @@ const COMMANDS = [
|
|
|
6
6
|
'/history', '/stop', '/help',
|
|
7
7
|
];
|
|
8
8
|
|
|
9
|
+
// Telegram bot commands to silently ignore (not tasks, not our commands)
|
|
10
|
+
const IGNORED_COMMANDS = ['/start'];
|
|
11
|
+
|
|
9
12
|
/**
|
|
10
13
|
* Parse a Telegram message into a command or task.
|
|
11
14
|
*
|
|
@@ -32,6 +35,11 @@ export function parseMessage (text) {
|
|
|
32
35
|
const parts = trimmed.split(/\s+/);
|
|
33
36
|
const cmd = parts[0].toLowerCase().replace(/@\w+$/, ''); // strip @botname
|
|
34
37
|
|
|
38
|
+
// Telegram built-in commands — silently ignore
|
|
39
|
+
if (IGNORED_COMMANDS.includes(cmd)) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
// Known command
|
|
36
44
|
if (COMMANDS.includes(cmd)) {
|
|
37
45
|
return {
|
package/listener/pty-runner.js
CHANGED
|
@@ -261,28 +261,57 @@ export class PtyRunner extends EventEmitter {
|
|
|
261
261
|
writeLines();
|
|
262
262
|
this.logger.info(`PTY task sent to ${workDir}: ${task.text.slice(0, 100)}`);
|
|
263
263
|
|
|
264
|
-
// Monitor PTY output for fatal errors that prevent task completion
|
|
264
|
+
// Monitor PTY output for fatal errors that prevent task completion.
|
|
265
|
+
// Some patterns (e.g. "auto mode temporarily unavailable") can appear in the
|
|
266
|
+
// Claude CLI status bar while Claude is still actively working. To avoid false
|
|
267
|
+
// positives we require the buffer to stop growing for two consecutive checks
|
|
268
|
+
// before treating the match as a real error.
|
|
265
269
|
const errorPatterns = [
|
|
266
270
|
{ pattern: 'auto mode temporarily unavailable', msg: 'Claude auto mode temporarily unavailable — retry later' },
|
|
267
271
|
{ pattern: 'Session expired', msg: 'Claude session expired' },
|
|
268
272
|
{ pattern: 'Authentication required', msg: 'Claude authentication required' },
|
|
269
273
|
];
|
|
274
|
+
let errorCandidate = null; // { msg, bufLen } — suspected error awaiting confirmation
|
|
275
|
+
let checkedUpTo = 0; // buffer offset already scanned — avoids re-matching the same text
|
|
270
276
|
const errorCheckInterval = setInterval(() => {
|
|
271
277
|
const buf = session._buffer || '';
|
|
272
|
-
|
|
273
|
-
|
|
278
|
+
const bufLen = buf.length;
|
|
279
|
+
|
|
280
|
+
// If we have a candidate from the previous cycle, confirm it
|
|
281
|
+
if (errorCandidate) {
|
|
282
|
+
if (bufLen === errorCandidate.bufLen) {
|
|
283
|
+
// Buffer did not grow — Claude stopped, treat as real error
|
|
274
284
|
clearInterval(errorCheckInterval);
|
|
275
|
-
this.logger.error(`PTY fatal: ${msg} in ${workDir}`);
|
|
285
|
+
this.logger.error(`PTY fatal: ${errorCandidate.msg} in ${workDir}`);
|
|
276
286
|
if (session._pendingId && this.pendingMarkers.has(session._pendingId)) {
|
|
277
287
|
this.pendingMarkers.delete(session._pendingId);
|
|
278
288
|
}
|
|
279
289
|
session.state = 'idle';
|
|
280
290
|
session.currentTask = null;
|
|
281
291
|
this._destroyPty(workDir);
|
|
282
|
-
this.emit('error', workDir, task, msg);
|
|
292
|
+
this.emit('error', workDir, task, errorCandidate.msg);
|
|
283
293
|
return;
|
|
284
294
|
}
|
|
295
|
+
// Buffer grew — Claude is still working, false positive
|
|
296
|
+
this.logger.info(`PTY error candidate dismissed (buffer grew) in ${workDir}`);
|
|
297
|
+
errorCandidate = null;
|
|
298
|
+
checkedUpTo = bufLen;
|
|
299
|
+
return;
|
|
285
300
|
}
|
|
301
|
+
|
|
302
|
+
// Scan only new buffer content for pattern matches
|
|
303
|
+
if (bufLen <= checkedUpTo) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const fresh = buf.slice(checkedUpTo);
|
|
307
|
+
for (const { pattern, msg } of errorPatterns) {
|
|
308
|
+
if (fresh.includes(pattern)) {
|
|
309
|
+
this.logger.warn(`PTY error candidate detected: "${pattern}" in ${workDir}, waiting to confirm…`);
|
|
310
|
+
errorCandidate = { msg, bufLen };
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
checkedUpTo = bufLen;
|
|
286
315
|
}, 3000);
|
|
287
316
|
|
|
288
317
|
// Clean up error monitor when task completes normally
|
|
@@ -349,6 +378,17 @@ export class PtyRunner extends EventEmitter {
|
|
|
349
378
|
args.push('--permission-mode', 'auto');
|
|
350
379
|
}
|
|
351
380
|
|
|
381
|
+
// Reduce PTY output noise: disable animations, progress bar, tips
|
|
382
|
+
if (!args.includes('--settings')) {
|
|
383
|
+
args.push('--settings', JSON.stringify({
|
|
384
|
+
prefersReducedMotion: true,
|
|
385
|
+
outputStyle: 'plain',
|
|
386
|
+
terminalProgressBarEnabled: false,
|
|
387
|
+
spinnerTipsEnabled: false,
|
|
388
|
+
showTurnDuration: false,
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
|
|
352
392
|
this.logger.info(`Creating PTY session in ${workDir} with args: ${JSON.stringify(args)}`);
|
|
353
393
|
|
|
354
394
|
const shell = process.platform === 'win32' ? 'cmd.exe' : '/bin/bash';
|
|
@@ -378,15 +418,44 @@ export class PtyRunner extends EventEmitter {
|
|
|
378
418
|
_buffer: '',
|
|
379
419
|
};
|
|
380
420
|
|
|
421
|
+
// Auto-answer permission prompts: track buffer position and cooldown to
|
|
422
|
+
// prevent double-fire when Claude re-renders the same prompt on screen.
|
|
423
|
+
let lastPermissionAnswerAt = 0;
|
|
424
|
+
let lastPermissionAnswerTime = 0;
|
|
425
|
+
const PERMISSION_COOLDOWN_MS = 5000;
|
|
426
|
+
|
|
381
427
|
ptyProcess.onData((data) => {
|
|
382
428
|
session._buffer += data;
|
|
383
429
|
// Keep buffer reasonable size
|
|
384
430
|
if (session._buffer.length > 50000) {
|
|
385
431
|
session._buffer = session._buffer.slice(-25000);
|
|
432
|
+
lastPermissionAnswerAt = Math.max(0, lastPermissionAnswerAt - 25000);
|
|
386
433
|
}
|
|
387
434
|
if (session._logStream) {
|
|
388
435
|
session._logStream.write(data);
|
|
389
436
|
}
|
|
437
|
+
|
|
438
|
+
// Auto-answer interactive permission prompts that appear when
|
|
439
|
+
// "auto mode temporarily unavailable". The prompt looks like:
|
|
440
|
+
// Do you want to proceed?
|
|
441
|
+
// ❯ 1. Yes
|
|
442
|
+
// 2. Yes, and always allow …
|
|
443
|
+
// 3. No
|
|
444
|
+
// We send Enter to approve (cursor ❯ is already on "1. Yes").
|
|
445
|
+
// Cooldown prevents double-fire from screen redraws.
|
|
446
|
+
const now = Date.now();
|
|
447
|
+
if (session.state === 'busy'
|
|
448
|
+
&& session._buffer.length - lastPermissionAnswerAt > 50
|
|
449
|
+
&& now - lastPermissionAnswerTime > PERMISSION_COOLDOWN_MS) {
|
|
450
|
+
const tail = session._buffer.slice(lastPermissionAnswerAt);
|
|
451
|
+
const clean = tail.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '').replace(/\s/g, '');
|
|
452
|
+
if (clean.includes('Doyouwanttoproceed') && clean.includes('1.Yes')) {
|
|
453
|
+
lastPermissionAnswerAt = session._buffer.length;
|
|
454
|
+
lastPermissionAnswerTime = now;
|
|
455
|
+
this.logger.info(`Auto-answering permission prompt in ${session.workDir}`);
|
|
456
|
+
session.pty.write('\r');
|
|
457
|
+
}
|
|
458
|
+
}
|
|
390
459
|
});
|
|
391
460
|
|
|
392
461
|
ptyProcess.onExit(({ exitCode }) => {
|
|
@@ -423,14 +492,26 @@ export class PtyRunner extends EventEmitter {
|
|
|
423
492
|
|
|
424
493
|
/**
|
|
425
494
|
* Wait for PTY output to stabilize (Claude has loaded).
|
|
495
|
+
* Automatically answers the workspace trust prompt if it appears.
|
|
426
496
|
*/
|
|
427
497
|
_waitForReady (session, timeoutMs) {
|
|
428
498
|
return new Promise((resolve) => {
|
|
429
499
|
let lastLength = 0;
|
|
430
500
|
let stableCount = 0;
|
|
501
|
+
let trustAnswered = false;
|
|
431
502
|
const checkInterval = 500;
|
|
432
503
|
|
|
433
504
|
const timer = setInterval(() => {
|
|
505
|
+
// Detect and auto-answer workspace trust prompt
|
|
506
|
+
if (!trustAnswered) {
|
|
507
|
+
const buf = (session._buffer || '').replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '').replace(/\s/g, '');
|
|
508
|
+
if (buf.includes('trustthisfolder') || buf.includes('Yesiproceed')) {
|
|
509
|
+
trustAnswered = true;
|
|
510
|
+
this.logger.info('Auto-answering workspace trust prompt');
|
|
511
|
+
session.pty.write('\r');
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
434
515
|
const currentLength = session._buffer.length;
|
|
435
516
|
if (currentLength > 0 && currentLength === lastLength) {
|
|
436
517
|
stableCount++;
|
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.1.
|
|
4
|
+
"version": "1.1.52",
|
|
5
5
|
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|