claude-notification-plugin 1.1.16 → 1.1.18

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.16",
3
+ "version": "1.1.18",
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
- ab57729646f07446deb7b0f196e087ac48952b7b
1
+ dc3b7baa9288c9a4f880b681d2183fe9a80cd283
@@ -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 escaped = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
371
- spawn('osascript', ['-e', `display notification "${escaped}" with title "Claude Code"`], {
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', ['Claude Code', message], {
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
- const iconPath = new URL('../claude_img/claude.png', import.meta.url).pathname
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: 'Claude Code',
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,17 @@ 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 ? `@${project}/${branch}` : `@${project}`;
687
- const labelHtml = branch
688
- ? `@<b>${escapeHtml(project)}</b>/<b>${escapeHtml(branch)}</b>`
689
- : `@<b>${escapeHtml(project)}</b>`;
691
+ let label = `/${project}`;
692
+ let labelHtml = `<b>${escapeHtml(project)}</b>`;
693
+ if (branch) {
694
+ label += `/${branch}`;
695
+ labelHtml += `/<b>${escapeHtml(branch)}</b>`;
696
+ }
690
697
 
691
698
  const triggerLine = config.debug ? `\nTrigger: ${eventType}` : '';
692
699
 
693
- const desktopMessage = `${desktopStatus}: ${label}`;
700
+ const desktopTitle = label;
701
+ const desktopMessage = desktopStatus;
694
702
 
695
703
  let telegramMessage =
696
704
  `${statusEmoji} ${labelHtml} (duration: ${duration}s)${triggerLine}`;
@@ -729,7 +737,7 @@ process.stdin.on('end', async () => {
729
737
  }
730
738
  saveState(state);
731
739
 
732
- await sendDesktopNotification(config, desktopMessage);
740
+ await sendDesktopNotification(config, desktopTitle, desktopMessage);
733
741
  playSound(config);
734
742
  speakResult(config, duration);
735
743
  });
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.16",
4
+ "version": "1.1.18",
5
5
  "description": "Claude Code task-completion notifications: Telegram, desktop notifications (Windows/macOS/Linux), sound, and voice",
6
6
  "type": "module",
7
7
  "engines": {