claude-notification-plugin 1.1.101 → 1.1.104
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 +20 -20
- package/README.md +1 -1
- package/commit-sha +1 -1
- package/listener/listener.js +88 -14
- package/listener/pty-runner.js +65 -6
- package/listener/session-state.js +73 -0
- package/package.json +65 -65
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-notification-plugin",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "Viacheslav Makarov",
|
|
7
|
-
"email": "npmjs@bazilio.ru"
|
|
8
|
-
},
|
|
9
|
-
"homepage": "https://github.com/Bazilio-san/claude-notification-plugin#readme",
|
|
10
|
-
"repository": "https://github.com/Bazilio-san/claude-notification-plugin",
|
|
11
|
-
"license": "MIT",
|
|
12
|
-
"keywords": [
|
|
13
|
-
"notification",
|
|
14
|
-
"telegram",
|
|
15
|
-
"windows",
|
|
16
|
-
"sound",
|
|
17
|
-
"voice",
|
|
18
|
-
"hooks"
|
|
19
|
-
]
|
|
20
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-notification-plugin",
|
|
3
|
+
"version": "1.1.104",
|
|
4
|
+
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Viacheslav Makarov",
|
|
7
|
+
"email": "npmjs@bazilio.ru"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/Bazilio-san/claude-notification-plugin#readme",
|
|
10
|
+
"repository": "https://github.com/Bazilio-san/claude-notification-plugin",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"notification",
|
|
14
|
+
"telegram",
|
|
15
|
+
"windows",
|
|
16
|
+
"sound",
|
|
17
|
+
"voice",
|
|
18
|
+
"hooks"
|
|
19
|
+
]
|
|
20
|
+
}
|
package/README.md
CHANGED
|
@@ -329,7 +329,7 @@ the same path cannot be registered twice under different aliases.
|
|
|
329
329
|
| `projects` | (required) | Map of projects: `alias → { path }` |
|
|
330
330
|
| `claudeArgs` | `[]` | Extra CLI args for Claude (e.g. `["--permission-mode", "auto"]`) |
|
|
331
331
|
| `continueSession` | `true` | Keep the PTY alive between Telegram tasks so subsequent tasks reuse the same live Claude session |
|
|
332
|
-
| `resumeLastSession` | `true` | When spawning a fresh PTY,
|
|
332
|
+
| `resumeLastSession` | `true` | When spawning a fresh PTY, resume the exact session whose ID the daemon captured from SessionStart (stored in `~/.claude/.session_state.json`). Survives listener restart. Falls back to a fresh session if no captured ID is available — does NOT use `--continue` blindly. |
|
|
333
333
|
| `sessionsListLimit` | `5` | How many most-recent sessions `/sessions` shows |
|
|
334
334
|
| `sessionWorkingThresholdSec` | `2` | A locked JSONL with mtime ≤ this many seconds is considered actively writing (status `working`, resume disabled) |
|
|
335
335
|
| `worktreeBaseDir` | `~/.claude/worktrees` | Where auto-created worktrees are stored |
|
package/commit-sha
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
6fb63d8cf4e4026a2909a47213b4303438712d8f
|
package/listener/listener.js
CHANGED
|
@@ -25,6 +25,11 @@ import {
|
|
|
25
25
|
import { JsonlReader, resolveJsonlPath, resolveJsonlByMtime, cwdToProjectDir } from './jsonl-reader.js';
|
|
26
26
|
import { listSessions } from './session-list.js';
|
|
27
27
|
import { findLocking, killPid } from './file-locks.js';
|
|
28
|
+
import {
|
|
29
|
+
getStoredSessionId,
|
|
30
|
+
setStoredSessionId,
|
|
31
|
+
clearStoredSessionId,
|
|
32
|
+
} from './session-state.js';
|
|
28
33
|
|
|
29
34
|
// ----------------------
|
|
30
35
|
// CRASH PROTECTION
|
|
@@ -138,7 +143,25 @@ const taskLogDir = config.listener?.taskLogDir || listenerLogDir;
|
|
|
138
143
|
fs.mkdirSync(taskLogDir, { recursive: true });
|
|
139
144
|
const taskLogger = createTaskLogger(taskLogDir);
|
|
140
145
|
|
|
141
|
-
|
|
146
|
+
// Pass workDirs with in-flight active tasks so PtyRunner's startup cleanup
|
|
147
|
+
// preserves their signal files instead of nuking the Stop hooks that fired
|
|
148
|
+
// while the daemon was down.
|
|
149
|
+
const activeWorkDirsOnBoot = Object.entries(queue.queues)
|
|
150
|
+
.filter(([, entry]) => entry?.active)
|
|
151
|
+
.map(([workDir]) => workDir);
|
|
152
|
+
const runner = new PtyRunner(logger, taskTimeout, taskLogger, taskLogDir, activeWorkDirsOnBoot);
|
|
153
|
+
|
|
154
|
+
// Capture Claude's real sessionId as soon as SessionStart fires, and persist it.
|
|
155
|
+
// This is what lets the next task (or the daemon after a restart) launch with
|
|
156
|
+
// `claude --resume <sid>` and continue *this exact* session — instead of
|
|
157
|
+
// `--continue` picking whichever JSONL happens to have the freshest mtime.
|
|
158
|
+
runner.on('ready', (workDir) => {
|
|
159
|
+
const sid = runner.getSessionId(workDir);
|
|
160
|
+
if (sid) {
|
|
161
|
+
setStoredSessionId(workDir, sid);
|
|
162
|
+
logger.info(`Captured sessionId for ${workDir}: ${sid}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
142
165
|
|
|
143
166
|
const worktreeManager = new WorktreeManager(config, logger);
|
|
144
167
|
|
|
@@ -211,6 +234,41 @@ async function runWatchdog () {
|
|
|
211
234
|
}
|
|
212
235
|
}
|
|
213
236
|
|
|
237
|
+
// 0. Migrate queue entries whose `project` alias no longer matches the config
|
|
238
|
+
// (e.g. user renamed `mail` → `me`). Without this, /status, /cancel, and
|
|
239
|
+
// completion notifications keep referring to the old alias even though the
|
|
240
|
+
// workDir is unchanged.
|
|
241
|
+
{
|
|
242
|
+
let migrated = 0;
|
|
243
|
+
for (const [workDir, entry] of Object.entries(queue.queues)) {
|
|
244
|
+
if (!entry) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const normalizedWd = normalizeForCompare(workDir);
|
|
248
|
+
for (const [alias, proj] of Object.entries(listenerConfig.projects)) {
|
|
249
|
+
const projPath = typeof proj === 'string' ? proj : proj?.path;
|
|
250
|
+
if (!projPath || normalizeForCompare(projPath) !== normalizedWd) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (entry.project !== alias) {
|
|
254
|
+
logger.info(`Migrating queue alias for ${workDir}: ${entry.project} → ${alias}`);
|
|
255
|
+
entry.project = alias;
|
|
256
|
+
if (entry.active) {
|
|
257
|
+
entry.active.project = alias;
|
|
258
|
+
}
|
|
259
|
+
for (const t of entry.queue || []) {
|
|
260
|
+
t.project = alias;
|
|
261
|
+
}
|
|
262
|
+
migrated++;
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (migrated > 0) {
|
|
268
|
+
queue._save();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
214
272
|
// 1. Initial watchdog sweep on startup. Must finish before orphan recovery,
|
|
215
273
|
// otherwise orphan recovery sees the stale active (still set) and re-spawns
|
|
216
274
|
// the killed task while watchdog is still awaiting its Telegram notify.
|
|
@@ -268,6 +326,11 @@ async function notifyTaskCompletion (workDir, task, kind, payload = {}) {
|
|
|
268
326
|
session.lastContextPct = Math.round((payload.totalTokens / payload.contextWindow) * 100);
|
|
269
327
|
}
|
|
270
328
|
sessions.set(workDir, session);
|
|
329
|
+
// Persist the sessionId so the next task — including across a listener
|
|
330
|
+
// restart — can resume this exact session via --resume <sid>.
|
|
331
|
+
if (payload.sessionId) {
|
|
332
|
+
setStoredSessionId(workDir, payload.sessionId);
|
|
333
|
+
}
|
|
271
334
|
|
|
272
335
|
const parts = [];
|
|
273
336
|
if (task.continueSession) {
|
|
@@ -369,19 +432,15 @@ function getClaudeArgs (projectAlias) {
|
|
|
369
432
|
return projectArgs.length > 0 ? projectArgs : globalClaudeArgs;
|
|
370
433
|
}
|
|
371
434
|
|
|
372
|
-
function hasAnyJsonl (workDir) {
|
|
373
|
-
const dirName = cwdToProjectDir(workDir);
|
|
374
|
-
const dirPath = path.join(CLAUDE_DIR, 'projects', dirName);
|
|
375
|
-
try {
|
|
376
|
-
return fs.readdirSync(dirPath).some(f => f.endsWith('.jsonl'));
|
|
377
|
-
} catch {
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
435
|
// Decide whether to add --continue or --resume <id> to claudeArgs for a fresh PTY.
|
|
383
436
|
// Priority: explicit caller flag > pending /resume <sid> > /newsession opt-out >
|
|
384
|
-
//
|
|
437
|
+
// persisted sessionId for this workDir > global resumeLastSession default.
|
|
438
|
+
//
|
|
439
|
+
// The persisted-sessionId branch is what makes "continue across listener
|
|
440
|
+
// restart" deterministic: we resume the exact session whose ID we captured
|
|
441
|
+
// from SessionStart, instead of letting --continue pick the most-recently-
|
|
442
|
+
// modified JSONL — which may belong to a different turn or branch.
|
|
443
|
+
// If reusing an idle PTY, claudeArgs are ignored anyway.
|
|
385
444
|
function applyResumeArgs (claudeArgs, workDir) {
|
|
386
445
|
if (claudeArgs.includes('--continue') || claudeArgs.includes('--resume')) {
|
|
387
446
|
return claudeArgs;
|
|
@@ -397,8 +456,21 @@ function applyResumeArgs (claudeArgs, workDir) {
|
|
|
397
456
|
freshSessionDirs.delete(workDir);
|
|
398
457
|
return claudeArgs;
|
|
399
458
|
}
|
|
400
|
-
if (resumeLastSessionEnabled
|
|
401
|
-
|
|
459
|
+
if (resumeLastSessionEnabled) {
|
|
460
|
+
const storedSid = getStoredSessionId(workDir);
|
|
461
|
+
if (storedSid) {
|
|
462
|
+
const dirName = cwdToProjectDir(workDir);
|
|
463
|
+
const jsonlPath = path.join(CLAUDE_DIR, 'projects', dirName, `${storedSid}.jsonl`);
|
|
464
|
+
if (fs.existsSync(jsonlPath)) {
|
|
465
|
+
return [...claudeArgs, '--resume', storedSid];
|
|
466
|
+
}
|
|
467
|
+
// Stale entry — JSONL was deleted/moved. Drop the stored sid so we
|
|
468
|
+
// don't keep retrying a dead session.
|
|
469
|
+
clearStoredSessionId(workDir);
|
|
470
|
+
logger.warn(`Stored sessionId ${storedSid} for ${workDir} has no JSONL — cleared`);
|
|
471
|
+
}
|
|
472
|
+
// No reliable sid → start fresh instead of falling back to the old
|
|
473
|
+
// mtime-based --continue lottery.
|
|
402
474
|
}
|
|
403
475
|
return claudeArgs;
|
|
404
476
|
}
|
|
@@ -839,6 +911,7 @@ function handleClear (args) {
|
|
|
839
911
|
// Also reset session
|
|
840
912
|
sessions.delete(workDir);
|
|
841
913
|
freshSessionDirs.add(workDir);
|
|
914
|
+
clearStoredSessionId(workDir);
|
|
842
915
|
logger.info(`Session reset for ${workDir} via /clear`);
|
|
843
916
|
|
|
844
917
|
return `🧹 [${escapeHtml(label)}] Queue cleared (${count} tasks), session reset`;
|
|
@@ -898,6 +971,7 @@ function handleNewSession (args) {
|
|
|
898
971
|
|
|
899
972
|
sessions.delete(workDir);
|
|
900
973
|
freshSessionDirs.add(workDir);
|
|
974
|
+
clearStoredSessionId(workDir);
|
|
901
975
|
logger.info(`Session reset for ${workDir} via /newsession`);
|
|
902
976
|
|
|
903
977
|
if (session) {
|
package/listener/pty-runner.js
CHANGED
|
@@ -10,6 +10,13 @@ const DEFAULT_TIMEOUT = 600_000; // 10 minutes
|
|
|
10
10
|
// Built-in slash-commands (forwarded via %cmd) rarely emit a Stop hook event,
|
|
11
11
|
// so we fall back to "done" after this much buffer inactivity.
|
|
12
12
|
const RAW_INACTIVITY_MS = 8_000;
|
|
13
|
+
// Fallback for tasks where the Stop hook silently doesn't fire (e.g. resumed
|
|
14
|
+
// sessions on Windows ConPTY): if the PTY buffer goes quiet AND its tail shows
|
|
15
|
+
// Claude's idle status bar, treat the task as complete.
|
|
16
|
+
const IDLE_PROMPT_FALLBACK_MS = 60_000;
|
|
17
|
+
// Status-bar marker shown by Claude when it's idle at the prompt. Covers
|
|
18
|
+
// "bypass permissions on", "accept edits on", "default mode" etc.
|
|
19
|
+
const IDLE_PROMPT_RE = /\b(?:bypass permissions on|accept edits on|default mode|plan mode)\b/i;
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
22
|
* PTY-based runner for Claude Code.
|
|
@@ -17,12 +24,19 @@ const RAW_INACTIVITY_MS = 8_000;
|
|
|
17
24
|
* receives completion signals via marker files written by the notifier hook.
|
|
18
25
|
*/
|
|
19
26
|
export class PtyRunner extends EventEmitter {
|
|
20
|
-
constructor (logger, timeout, taskLogger, ptyLogDir) {
|
|
27
|
+
constructor (logger, timeout, taskLogger, ptyLogDir, activeWorkDirs) {
|
|
21
28
|
super();
|
|
22
29
|
this.logger = logger;
|
|
23
30
|
this.timeout = timeout || DEFAULT_TIMEOUT;
|
|
24
31
|
this.taskLogger = taskLogger || null;
|
|
25
32
|
this.ptyLogDir = ptyLogDir || null;
|
|
33
|
+
// Set of normalized workDir paths whose orphan tasks are about to be
|
|
34
|
+
// re-started by listener startup logic. Their pending signal files (if any)
|
|
35
|
+
// must be preserved through the cleanup phase, otherwise we lose hook
|
|
36
|
+
// events the daemon never saw because it wasn't running yet.
|
|
37
|
+
this._preserveSignalsForWorkDirs = new Set(
|
|
38
|
+
(activeWorkDirs || []).map((wd) => this._normalizePath(wd))
|
|
39
|
+
);
|
|
26
40
|
// workDir -> { pty, state, currentTask, sessionId, workDir, _pendingId, _buffer, _logStream }
|
|
27
41
|
this.sessions = new Map();
|
|
28
42
|
this.pendingMarkers = new Map(); // pendingId -> resolve callback
|
|
@@ -50,17 +64,36 @@ export class PtyRunner extends EventEmitter {
|
|
|
50
64
|
// ignore
|
|
51
65
|
}
|
|
52
66
|
|
|
53
|
-
// Clean up stale marker files on startup
|
|
67
|
+
// Clean up stale marker files on startup, but preserve any whose `cwd`
|
|
68
|
+
// matches a workDir with an in-flight orphan task — those signals were
|
|
69
|
+
// written by Claude while the daemon was down and are the only way to
|
|
70
|
+
// notice that the task already finished.
|
|
54
71
|
try {
|
|
55
72
|
const files = fs.readdirSync(PTY_SIGNAL_DIR);
|
|
56
73
|
for (const f of files) {
|
|
57
|
-
if (f.endsWith('.json')) {
|
|
74
|
+
if (!f.endsWith('.json')) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const filePath = path.join(PTY_SIGNAL_DIR, f);
|
|
78
|
+
let preserve = false;
|
|
79
|
+
if (this._preserveSignalsForWorkDirs.size > 0) {
|
|
58
80
|
try {
|
|
59
|
-
|
|
81
|
+
const marker = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
82
|
+
if (marker?.cwd && this._preserveSignalsForWorkDirs.has(this._normalizePath(marker.cwd))) {
|
|
83
|
+
preserve = true;
|
|
84
|
+
}
|
|
60
85
|
} catch {
|
|
61
|
-
//
|
|
86
|
+
// unreadable — fall through and delete
|
|
62
87
|
}
|
|
63
88
|
}
|
|
89
|
+
if (preserve) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
fs.unlinkSync(filePath);
|
|
94
|
+
} catch {
|
|
95
|
+
// ignore
|
|
96
|
+
}
|
|
64
97
|
}
|
|
65
98
|
} catch {
|
|
66
99
|
// ignore
|
|
@@ -222,7 +255,33 @@ export class PtyRunner extends EventEmitter {
|
|
|
222
255
|
const CHECK_INTERVAL = 5000;
|
|
223
256
|
const checker = setInterval(() => {
|
|
224
257
|
const lastActivity = session?._lastActivityTime || 0;
|
|
225
|
-
|
|
258
|
+
const idleMs = lastActivity > 0 ? Date.now() - lastActivity : 0;
|
|
259
|
+
|
|
260
|
+
// Idle-prompt fallback: PTY went quiet AND its tail shows Claude's
|
|
261
|
+
// idle status bar — treat as completed even if the Stop hook never
|
|
262
|
+
// produced a signal (happens with resumed sessions on Windows ConPTY).
|
|
263
|
+
if (idleMs > IDLE_PROMPT_FALLBACK_MS && session) {
|
|
264
|
+
const tailRaw = (session._buffer || '').slice(-3000);
|
|
265
|
+
const tailClean = cleanPtyOutput(tailRaw);
|
|
266
|
+
if (IDLE_PROMPT_RE.test(tailClean)) {
|
|
267
|
+
clearInterval(checker);
|
|
268
|
+
this.pendingMarkers.delete(pendingId);
|
|
269
|
+
const fullClean = cleanPtyOutput(session._buffer || '').trim();
|
|
270
|
+
const text = fullClean.length > 2000 ? fullClean.slice(-2000) : fullClean;
|
|
271
|
+
this.logger.warn(`PTY idle-prompt fallback completion in ${session.workDir} (no Stop signal in ${Math.round(idleMs / 1000)}s)`);
|
|
272
|
+
resolve({
|
|
273
|
+
lastAssistantMessage: text,
|
|
274
|
+
sessionId: session.sessionId || null,
|
|
275
|
+
cost: 0,
|
|
276
|
+
numTurns: 0,
|
|
277
|
+
durationMs: idleMs,
|
|
278
|
+
isIdleFallback: true,
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (lastActivity > 0 && idleMs > inactivityMs) {
|
|
226
285
|
clearInterval(checker);
|
|
227
286
|
this.pendingMarkers.delete(pendingId);
|
|
228
287
|
reject(new Error('Marker timeout'));
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Persistent map of workDir → last known Claude sessionId.
|
|
2
|
+
// Survives listener restarts so the next task in a workDir can be launched
|
|
3
|
+
// with `claude --resume <sid>` and continue exactly the session that was
|
|
4
|
+
// running before the restart, instead of `--continue` blindly picking the
|
|
5
|
+
// most-recently-modified JSONL (which may belong to a completed turn, a
|
|
6
|
+
// different branch, or a manually-opened terminal in the same project dir).
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { CLAUDE_DIR, normalizeForCompare } from '../bin/constants.js';
|
|
11
|
+
|
|
12
|
+
const SESSION_STATE_PATH = path.join(CLAUDE_DIR, '.session_state.json');
|
|
13
|
+
|
|
14
|
+
function readState () {
|
|
15
|
+
try {
|
|
16
|
+
const raw = fs.readFileSync(SESSION_STATE_PATH, 'utf-8');
|
|
17
|
+
const data = JSON.parse(raw);
|
|
18
|
+
return data && typeof data === 'object' ? data : {};
|
|
19
|
+
} catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function writeState (state) {
|
|
25
|
+
try {
|
|
26
|
+
fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
27
|
+
const tmp = `${SESSION_STATE_PATH}.${process.pid}.tmp`;
|
|
28
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 2));
|
|
29
|
+
fs.renameSync(tmp, SESSION_STATE_PATH);
|
|
30
|
+
} catch {
|
|
31
|
+
// best-effort; never throw
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function keyFor (workDir) {
|
|
36
|
+
return normalizeForCompare(workDir);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getStoredSessionId (workDir) {
|
|
40
|
+
if (!workDir) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const state = readState();
|
|
44
|
+
const entry = state[keyFor(workDir)];
|
|
45
|
+
return entry?.sessionId || null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function setStoredSessionId (workDir, sessionId) {
|
|
49
|
+
if (!workDir || !sessionId) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const state = readState();
|
|
53
|
+
state[keyFor(workDir)] = {
|
|
54
|
+
sessionId,
|
|
55
|
+
updatedAt: new Date().toISOString(),
|
|
56
|
+
workDir,
|
|
57
|
+
};
|
|
58
|
+
writeState(state);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function clearStoredSessionId (workDir) {
|
|
62
|
+
if (!workDir) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const state = readState();
|
|
66
|
+
const k = keyFor(workDir);
|
|
67
|
+
if (k in state) {
|
|
68
|
+
delete state[k];
|
|
69
|
+
writeState(state);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { SESSION_STATE_PATH };
|
package/package.json
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-notification-plugin",
|
|
3
|
-
"productName": "claude-notification-plugin",
|
|
4
|
-
"version": "1.1.
|
|
5
|
-
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"engines": {
|
|
8
|
-
"node": ">=18.0.0"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
".claude-plugin/",
|
|
12
|
-
"bin/",
|
|
13
|
-
"claude_img/claude.png",
|
|
14
|
-
"hooks/",
|
|
15
|
-
"listener/",
|
|
16
|
-
"notifier/",
|
|
17
|
-
"commit-sha",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"bin": {
|
|
22
|
-
"claude-notify": "bin/cli.js"
|
|
23
|
-
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"prepack": "git rev-parse HEAD > commit-sha",
|
|
26
|
-
"postinstall": "node bin/install.js",
|
|
27
|
-
"lint": "eslint .",
|
|
28
|
-
"lint:fix": "eslint --fix .",
|
|
29
|
-
"listener:restart": "claude-notify listener restart",
|
|
30
|
-
"listener:stop": "claude-notify listener stop",
|
|
31
|
-
"listener:start": "claude-notify listener start",
|
|
32
|
-
"listener:status": "claude-notify listener status"
|
|
33
|
-
},
|
|
34
|
-
"keywords": [
|
|
35
|
-
"claude",
|
|
36
|
-
"claude-code",
|
|
37
|
-
"notifications",
|
|
38
|
-
"telegram",
|
|
39
|
-
"hooks",
|
|
40
|
-
"macos",
|
|
41
|
-
"linux",
|
|
42
|
-
"cross-platform"
|
|
43
|
-
],
|
|
44
|
-
"author": {
|
|
45
|
-
"name": "Viacheslav Makarov",
|
|
46
|
-
"email": "npmjs@bazilio.ru"
|
|
47
|
-
},
|
|
48
|
-
"license": "MIT",
|
|
49
|
-
"repository": {
|
|
50
|
-
"type": "git",
|
|
51
|
-
"url": "git+https://github.com/Bazilio-san/claude-notification-plugin.git"
|
|
52
|
-
},
|
|
53
|
-
"homepage": "https://github.com/Bazilio-san/claude-notification-plugin#readme",
|
|
54
|
-
"publishConfig": {
|
|
55
|
-
"access": "public"
|
|
56
|
-
},
|
|
57
|
-
"dependencies": {
|
|
58
|
-
"node-notifier": "^10.0.1",
|
|
59
|
-
"node-pty": "^1.1.0"
|
|
60
|
-
},
|
|
61
|
-
"devDependencies": {
|
|
62
|
-
"eslint-plugin-import": "^2.31.0",
|
|
63
|
-
"eslint-plugin-unused-imports": "^4.4.1"
|
|
64
|
-
}
|
|
65
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-notification-plugin",
|
|
3
|
+
"productName": "claude-notification-plugin",
|
|
4
|
+
"version": "1.1.104",
|
|
5
|
+
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18.0.0"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
".claude-plugin/",
|
|
12
|
+
"bin/",
|
|
13
|
+
"claude_img/claude.png",
|
|
14
|
+
"hooks/",
|
|
15
|
+
"listener/",
|
|
16
|
+
"notifier/",
|
|
17
|
+
"commit-sha",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"claude-notify": "bin/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"prepack": "git rev-parse HEAD > commit-sha",
|
|
26
|
+
"postinstall": "node bin/install.js",
|
|
27
|
+
"lint": "eslint .",
|
|
28
|
+
"lint:fix": "eslint --fix .",
|
|
29
|
+
"listener:restart": "claude-notify listener restart",
|
|
30
|
+
"listener:stop": "claude-notify listener stop",
|
|
31
|
+
"listener:start": "claude-notify listener start",
|
|
32
|
+
"listener:status": "claude-notify listener status"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"claude",
|
|
36
|
+
"claude-code",
|
|
37
|
+
"notifications",
|
|
38
|
+
"telegram",
|
|
39
|
+
"hooks",
|
|
40
|
+
"macos",
|
|
41
|
+
"linux",
|
|
42
|
+
"cross-platform"
|
|
43
|
+
],
|
|
44
|
+
"author": {
|
|
45
|
+
"name": "Viacheslav Makarov",
|
|
46
|
+
"email": "npmjs@bazilio.ru"
|
|
47
|
+
},
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/Bazilio-san/claude-notification-plugin.git"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/Bazilio-san/claude-notification-plugin#readme",
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"node-notifier": "^10.0.1",
|
|
59
|
+
"node-pty": "^1.1.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"eslint-plugin-import": "^2.31.0",
|
|
63
|
+
"eslint-plugin-unused-imports": "^4.4.1"
|
|
64
|
+
}
|
|
65
|
+
}
|