claude-notification-plugin 1.1.16 → 1.1.17
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/bin/constants.js +11 -0
- package/bin/install.js +80 -0
- package/bin/uninstall.js +2 -1
- package/commit-sha +1 -1
- package/notifier/notifier.js +19 -13
- 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.17",
|
|
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/bin/constants.js
CHANGED
|
@@ -25,6 +25,17 @@ export const INSTALLED_PLUGINS_PATH = path.join(PLUGINS_DIR, 'installed_plugins.
|
|
|
25
25
|
export const KNOWN_MARKETPLACES_PATH = path.join(PLUGINS_DIR, 'known_marketplaces.json');
|
|
26
26
|
export const MARKETPLACES_DIR = path.join(PLUGINS_DIR, 'marketplaces');
|
|
27
27
|
|
|
28
|
+
// Windows notification shortcut
|
|
29
|
+
export const ICO_FILENAME = 'claude-notify.ico';
|
|
30
|
+
export const ICO_PATH = path.join(CLAUDE_DIR, ICO_FILENAME);
|
|
31
|
+
export const SHORTCUT_NAME = 'Claude Notify.lnk';
|
|
32
|
+
export const SHORTCUT_DIR = path.join(
|
|
33
|
+
process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming'),
|
|
34
|
+
'Microsoft', 'Windows', 'Start Menu', 'Programs'
|
|
35
|
+
);
|
|
36
|
+
export const SHORTCUT_PATH = path.join(SHORTCUT_DIR, SHORTCUT_NAME);
|
|
37
|
+
export const APP_ID = 'Claude Notify';
|
|
38
|
+
|
|
28
39
|
// Plugin identity
|
|
29
40
|
export const HOOK_COMMAND = 'claude-notify';
|
|
30
41
|
export const MARKETPLACE_KEY = 'bazilio-plugins';
|
package/bin/install.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CONFIG_PATH, PID_PATH, RESOLVER_PATH, INSTALL_LOG_PATH,
|
|
11
11
|
SETTINGS_PATH, INSTALLED_PLUGINS_PATH, KNOWN_MARKETPLACES_PATH, MARKETPLACES_DIR,
|
|
12
12
|
HOOK_COMMAND, MARKETPLACE_KEY, PLUGIN_KEY, MARKETPLACE_REPO, MARKETPLACE_GITHUB,
|
|
13
|
+
ICO_PATH, SHORTCUT_PATH, APP_ID,
|
|
13
14
|
} from './constants.js';
|
|
14
15
|
|
|
15
16
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -340,6 +341,84 @@ function registerCli () {
|
|
|
340
341
|
console.log(` CLI wrapper registered: ${wrapperPath}`);
|
|
341
342
|
}
|
|
342
343
|
|
|
344
|
+
// ──────────────────────────────────────
|
|
345
|
+
// Windows Start Menu shortcut (for notification icon)
|
|
346
|
+
// ──────────────────────────────────────
|
|
347
|
+
|
|
348
|
+
function createWindowsShortcut () {
|
|
349
|
+
if (process.platform !== 'win32') {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
const icoSrc = path.join(PACKAGE_ROOT, 'claude_img', 'claude.ico');
|
|
354
|
+
if (!fs.existsSync(icoSrc)) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
fs.copyFileSync(icoSrc, ICO_PATH);
|
|
358
|
+
const ps = `
|
|
359
|
+
Add-Type -TypeDefinition @'
|
|
360
|
+
using System;
|
|
361
|
+
using System.Runtime.InteropServices;
|
|
362
|
+
using System.Runtime.InteropServices.ComTypes;
|
|
363
|
+
public class ShortcutHelper {
|
|
364
|
+
[ComImport, Guid("000214F9-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
365
|
+
interface IShellLinkW {
|
|
366
|
+
void GetPath([MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder f,int c,IntPtr d,uint g);
|
|
367
|
+
void GetIDList(out IntPtr p);void SetIDList(IntPtr p);
|
|
368
|
+
void GetDescription([MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder n,int c);
|
|
369
|
+
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string n);
|
|
370
|
+
void GetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder d,int c);
|
|
371
|
+
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string d);
|
|
372
|
+
void GetArguments([MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder a,int c);
|
|
373
|
+
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string a);
|
|
374
|
+
void GetHotkey(out ushort h);void SetHotkey(ushort h);
|
|
375
|
+
void GetShowCmd(out int s);void SetShowCmd(int s);
|
|
376
|
+
void GetIconLocation([MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder p,int c,out int i);
|
|
377
|
+
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string p,int i);
|
|
378
|
+
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string p,uint r);
|
|
379
|
+
void Resolve(IntPtr h,uint f);
|
|
380
|
+
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string p);
|
|
381
|
+
}
|
|
382
|
+
[ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
383
|
+
interface IPropertyStore {
|
|
384
|
+
int GetCount(out uint c);int GetAt(uint i,out PROPERTYKEY k);
|
|
385
|
+
int GetValue(ref PROPERTYKEY k,out PropVariant v);
|
|
386
|
+
int SetValue(ref PROPERTYKEY k,ref PropVariant v);int Commit();
|
|
387
|
+
}
|
|
388
|
+
[StructLayout(LayoutKind.Sequential)] struct PROPERTYKEY { public Guid fmtid; public uint pid; }
|
|
389
|
+
[StructLayout(LayoutKind.Explicit)] struct PropVariant {
|
|
390
|
+
[FieldOffset(0)] public ushort vt; [FieldOffset(8)] public IntPtr pwszVal;
|
|
391
|
+
}
|
|
392
|
+
[ComImport, Guid("00021401-0000-0000-C000-000000000046")] class ShellLink {}
|
|
393
|
+
public static void Create(string lnk,string appId,string target,string ico,string desc) {
|
|
394
|
+
var link=(IShellLinkW)new ShellLink();
|
|
395
|
+
link.SetPath(target);link.SetIconLocation(ico,0);link.SetDescription(desc);
|
|
396
|
+
var store=(IPropertyStore)link;
|
|
397
|
+
var key=new PROPERTYKEY{fmtid=new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"),pid=5};
|
|
398
|
+
var pv=new PropVariant();pv.vt=31;pv.pwszVal=Marshal.StringToCoTaskMemUni(appId);
|
|
399
|
+
store.SetValue(ref key,ref pv);store.Commit();Marshal.FreeCoTaskMem(pv.pwszVal);
|
|
400
|
+
((IPersistFile)link).Save(lnk,true);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
'@
|
|
404
|
+
[ShortcutHelper]::Create('${SHORTCUT_PATH.replace(/'/g, "''")}','${APP_ID}','C:\\Windows\\explorer.exe','${ICO_PATH.replace(/'/g, "''")}','${APP_ID}')
|
|
405
|
+
`;
|
|
406
|
+
const tmpPs1 = path.join(CLAUDE_DIR, '_shortcut.ps1');
|
|
407
|
+
fs.writeFileSync(tmpPs1, ps, 'utf-8');
|
|
408
|
+
try {
|
|
409
|
+
execSync(
|
|
410
|
+
`powershell -NoProfile -ExecutionPolicy Bypass -File "${tmpPs1}"`,
|
|
411
|
+
{ stdio: 'ignore' }
|
|
412
|
+
);
|
|
413
|
+
console.log(' Windows notification shortcut created');
|
|
414
|
+
} finally {
|
|
415
|
+
fs.rmSync(tmpPs1, { force: true });
|
|
416
|
+
}
|
|
417
|
+
} catch (err) {
|
|
418
|
+
console.log(` Windows shortcut creation skipped: ${err.message}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
343
422
|
// ──────────────────────────────────────
|
|
344
423
|
// Helpers
|
|
345
424
|
// ──────────────────────────────────────
|
|
@@ -433,6 +512,7 @@ async function main () {
|
|
|
433
512
|
console.log(' Marketplace synced');
|
|
434
513
|
|
|
435
514
|
registerCli();
|
|
515
|
+
createWindowsShortcut();
|
|
436
516
|
|
|
437
517
|
console.log(' Plugin registered.');
|
|
438
518
|
|
package/bin/uninstall.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
STATE_PATH, PID_PATH, RESOLVER_PATH, LISTENER_LOG_PATH,
|
|
9
9
|
SETTINGS_PATH, INSTALLED_PLUGINS_PATH, KNOWN_MARKETPLACES_PATH,
|
|
10
10
|
HOOK_COMMAND, PLUGIN_KEY, MARKETPLACE_KEY,
|
|
11
|
+
ICO_PATH, SHORTCUT_PATH,
|
|
11
12
|
} from './constants.js';
|
|
12
13
|
|
|
13
14
|
function isPluginHookCommand (command) {
|
|
@@ -242,7 +243,7 @@ if (fs.existsSync(SETTINGS_PATH)) {
|
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
// Remove state, resolver, and listener files (config is preserved for reinstall)
|
|
245
|
-
for (const file of [STATE_PATH, RESOLVER_PATH, PID_PATH, LISTENER_LOG_PATH]) {
|
|
246
|
+
for (const file of [STATE_PATH, RESOLVER_PATH, PID_PATH, LISTENER_LOG_PATH, ICO_PATH, SHORTCUT_PATH]) {
|
|
246
247
|
if (fs.existsSync(file)) {
|
|
247
248
|
fs.unlinkSync(file);
|
|
248
249
|
console.log(`Removed ${path.basename(file)}`);
|
package/commit-sha
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
89bf4ee30af742d498e77b99e9386292480406d5
|
package/notifier/notifier.js
CHANGED
|
@@ -363,18 +363,19 @@ async function sendWebhook (config, payload) {
|
|
|
363
363
|
// DESKTOP NOTIFICATION
|
|
364
364
|
// ----------------------
|
|
365
365
|
|
|
366
|
-
function sendNativeFallback (config, message) {
|
|
366
|
+
function sendNativeFallback (config, title, message) {
|
|
367
367
|
try {
|
|
368
368
|
switch (PLATFORM) {
|
|
369
369
|
case 'darwin': {
|
|
370
|
-
const
|
|
371
|
-
|
|
370
|
+
const escapedMsg = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
371
|
+
const escapedTitle = title.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
372
|
+
spawn('osascript', ['-e', `display notification "${escapedMsg}" with title "${escapedTitle}"`], {
|
|
372
373
|
stdio: 'ignore',
|
|
373
374
|
});
|
|
374
375
|
break;
|
|
375
376
|
}
|
|
376
377
|
case 'linux':
|
|
377
|
-
spawn('notify-send', [
|
|
378
|
+
spawn('notify-send', [title, message], {
|
|
378
379
|
stdio: 'ignore',
|
|
379
380
|
});
|
|
380
381
|
break;
|
|
@@ -384,24 +385,28 @@ function sendNativeFallback (config, message) {
|
|
|
384
385
|
}
|
|
385
386
|
}
|
|
386
387
|
|
|
387
|
-
async function sendDesktopNotification (config, message) {
|
|
388
|
+
async function sendDesktopNotification (config, title, message) {
|
|
388
389
|
if (!config.desktopNotification.enabled) {
|
|
389
390
|
return;
|
|
390
391
|
}
|
|
391
392
|
try {
|
|
392
393
|
const { default: notifier } = await import('node-notifier');
|
|
393
|
-
|
|
394
|
+
let iconPath = new URL('../claude_img/claude.png', import.meta.url).pathname
|
|
394
395
|
.replace(/^\/([a-zA-Z]:)/, '$1');
|
|
396
|
+
if (PLATFORM === 'win32') {
|
|
397
|
+
iconPath = path.resolve(iconPath);
|
|
398
|
+
}
|
|
395
399
|
notifier.notify({
|
|
396
|
-
title
|
|
400
|
+
title,
|
|
397
401
|
message,
|
|
398
402
|
icon: iconPath,
|
|
399
403
|
sound: false,
|
|
400
404
|
wait: false,
|
|
405
|
+
appID: 'Claude Notify',
|
|
401
406
|
});
|
|
402
407
|
} catch (err) {
|
|
403
408
|
debugLog(config, 'node-notifier failed, trying native fallback:', err.message);
|
|
404
|
-
sendNativeFallback(config, message);
|
|
409
|
+
sendNativeFallback(config, title, message);
|
|
405
410
|
}
|
|
406
411
|
}
|
|
407
412
|
|
|
@@ -683,14 +688,15 @@ process.stdin.on('end', async () => {
|
|
|
683
688
|
const desktopStatus = eventType === 'Notification' ? 'Waiting' : 'Finished';
|
|
684
689
|
|
|
685
690
|
const branch = getBranch(cwd);
|
|
686
|
-
const label = branch ?
|
|
691
|
+
const label = branch ? `/${project}/${branch}` : `/${project}`;
|
|
687
692
|
const labelHtml = branch
|
|
688
|
-
?
|
|
689
|
-
:
|
|
693
|
+
? `/<b>${escapeHtml(project)}</b>/<b>${escapeHtml(branch)}</b>`
|
|
694
|
+
: `/<b>${escapeHtml(project)}</b>`;
|
|
690
695
|
|
|
691
696
|
const triggerLine = config.debug ? `\nTrigger: ${eventType}` : '';
|
|
692
697
|
|
|
693
|
-
const
|
|
698
|
+
const desktopTitle = label;
|
|
699
|
+
const desktopMessage = desktopStatus;
|
|
694
700
|
|
|
695
701
|
let telegramMessage =
|
|
696
702
|
`${statusEmoji} ${labelHtml} (duration: ${duration}s)${triggerLine}`;
|
|
@@ -729,7 +735,7 @@ process.stdin.on('end', async () => {
|
|
|
729
735
|
}
|
|
730
736
|
saveState(state);
|
|
731
737
|
|
|
732
|
-
await sendDesktopNotification(config, desktopMessage);
|
|
738
|
+
await sendDesktopNotification(config, desktopTitle, desktopMessage);
|
|
733
739
|
playSound(config);
|
|
734
740
|
speakResult(config, duration);
|
|
735
741
|
});
|
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.17",
|
|
5
5
|
"description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|