claude-notification-plugin 1.1.49 → 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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-notification-plugin",
3
- "version": "1.1.49",
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
- ff5e9a94bfde76173bc7c0b2658c5bb14b4503ea
1
+ 0a1e42c292641f978dba9e382f7ff36af09b1ea8
@@ -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
- for (const { pattern, msg } of errorPatterns) {
273
- if (buf.includes(pattern)) {
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);
293
+ return;
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;
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 };
283
311
  return;
284
312
  }
285
313
  }
314
+ checkedUpTo = bufLen;
286
315
  }, 3000);
287
316
 
288
317
  // Clean up error monitor when task completes normally
@@ -389,15 +418,44 @@ export class PtyRunner extends EventEmitter {
389
418
  _buffer: '',
390
419
  };
391
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
+
392
427
  ptyProcess.onData((data) => {
393
428
  session._buffer += data;
394
429
  // Keep buffer reasonable size
395
430
  if (session._buffer.length > 50000) {
396
431
  session._buffer = session._buffer.slice(-25000);
432
+ lastPermissionAnswerAt = Math.max(0, lastPermissionAnswerAt - 25000);
397
433
  }
398
434
  if (session._logStream) {
399
435
  session._logStream.write(data);
400
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
+ }
401
459
  });
402
460
 
403
461
  ptyProcess.onExit(({ exitCode }) => {
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.49",
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": {