@landienzla/claude-code-notify 1.0.4 → 1.1.2

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/README.md CHANGED
@@ -1,17 +1,23 @@
1
1
  # claude-code-notify
2
2
 
3
+ [![CI](https://github.com/Landienzla/claude-code-notify/actions/workflows/ci.yml/badge.svg)](https://github.com/Landienzla/claude-code-notify/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/@landienzla/claude-code-notify)](https://www.npmjs.com/package/@landienzla/claude-code-notify)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
3
7
  Cross-platform desktop notifications for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Get notified when Claude Code needs your attention — permission approval, task completion, idle prompts, and more.
4
8
 
5
9
  Works as a **Claude Code plugin** (zero config) or as a **CLI setup tool**.
6
10
 
7
11
  ## Platforms
8
12
 
9
- | Platform | Method | Sound |
10
- |----------|--------|-------|
11
- | Windows | Toast notification (slides in from right) | Silent |
12
- | macOS | Native notification center | Default |
13
- | Linux | `notify-send` (libnotify) | Default |
14
- | WSL | Windows toast via PowerShell | Silent |
13
+ | Platform | Method |
14
+ |----------|--------|
15
+ | Windows | Toast notification (slides in from right) |
16
+ | macOS | Native notification center |
17
+ | Linux | `notify-send` (libnotify) |
18
+ | WSL | Windows toast via PowerShell |
19
+
20
+ Notifications are **silent by default**. Use `--sound` to enable sound. Use `--name` to give your Claude a name.
15
21
 
16
22
  ## Quick Start
17
23
 
@@ -53,12 +59,16 @@ Plugin mode requires no changes to your `settings.json` — the hook is loaded a
53
59
 
54
60
  | Command | Description |
55
61
  |---------|-------------|
56
- | `claude-code-notify setup` | Install notification hook into `~/.claude/` |
62
+ | `claude-code-notify setup` | Install notification hook (silent) |
63
+ | `claude-code-notify setup --sound` | Install with sound enabled |
64
+ | `claude-code-notify setup --name "Jarvis"` | Install with a custom name |
57
65
  | `claude-code-notify dry-run` | Preview what setup will change (no writes) |
58
66
  | `claude-code-notify test` | Send a test notification |
59
67
  | `claude-code-notify uninstall` | Remove notification hook and script |
60
68
  | `claude-code-notify help` | Show usage info |
61
69
 
70
+ Options can be combined: `claude-code-notify setup --sound --name "Jarvis"`
71
+
62
72
  Run `dry-run` first to see exactly what files will be created/modified before committing.
63
73
 
64
74
  ## How It Works
@@ -81,7 +91,7 @@ The hook fires on all notification types:
81
91
 
82
92
  ## Requirements
83
93
 
84
- - **Python 3** — used for JSON parsing (available by default on macOS/Linux)
94
+ - **Node.js** — already installed if you're using npm
85
95
  - **Linux:** `libnotify-bin` for `notify-send`
86
96
  ```bash
87
97
  # Debian/Ubuntu
@@ -119,9 +129,6 @@ npx @landienzla/claude-code-notify uninstall
119
129
  - Install `libnotify-bin` (see Requirements)
120
130
  - Ensure a notification daemon is running (e.g. `dunst`, `mako`, GNOME/KDE built-in)
121
131
 
122
- **Generic "Needs your attention" message instead of context:**
123
- - Ensure Python 3 is installed and on your PATH
124
-
125
132
  ## License
126
133
 
127
134
  [MIT](LICENSE)
package/bin/cli.js CHANGED
@@ -13,20 +13,30 @@ const NOTIFY_DEST = path.join(SCRIPTS_DIR, 'notify.sh');
13
13
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
14
14
  const NOTIFY_SRC = path.join(PACKAGE_ROOT, 'hooks', 'scripts', 'notify.sh');
15
15
 
16
- const HOOK_CONFIG = {
17
- Notification: [
18
- {
19
- matcher: '',
20
- hooks: [
21
- {
22
- type: 'command',
23
- command: `bash "${NOTIFY_DEST.replace(/\\/g, '/')}"`,
24
- timeout: 10
25
- }
26
- ]
27
- }
28
- ]
29
- };
16
+ // Parse flags
17
+ const args = process.argv.slice(3);
18
+ const sound = args.includes('--sound');
19
+ const nameIdx = args.indexOf('--name');
20
+ const name = nameIdx !== -1 && args[nameIdx + 1] ? args[nameIdx + 1] : null;
21
+ const soundFlag = sound ? ' --sound' : '';
22
+ const nameFlag = name ? ` --name "${name}"` : '';
23
+
24
+ function hookConfig() {
25
+ return {
26
+ Notification: [
27
+ {
28
+ matcher: '',
29
+ hooks: [
30
+ {
31
+ type: 'command',
32
+ command: `bash "${NOTIFY_DEST.replace(/\\/g, '/')}"${soundFlag}${nameFlag}`,
33
+ timeout: 10
34
+ }
35
+ ]
36
+ }
37
+ ]
38
+ };
39
+ }
30
40
 
31
41
  function dryRun() {
32
42
  console.log('Dry run — no changes will be made.\n');
@@ -36,7 +46,7 @@ function dryRun() {
36
46
  console.log(` 2. Add Notification hook to:`);
37
47
  console.log(` ${SETTINGS_PATH}\n`);
38
48
  console.log(' Hook config:');
39
- console.log(JSON.stringify(HOOK_CONFIG, null, 2).split('\n').map(l => ' ' + l).join('\n'));
49
+ console.log(JSON.stringify(hookConfig(), null, 2).split('\n').map(l => ' ' + l).join('\n'));
40
50
 
41
51
  if (fs.existsSync(SETTINGS_PATH)) {
42
52
  try {
@@ -49,16 +59,6 @@ function dryRun() {
49
59
  }
50
60
  }
51
61
 
52
- // Check python3
53
- const { spawnSync } = require('child_process');
54
- const py = spawnSync('python3', ['--version'], { stdio: 'pipe' });
55
- if (py.status !== 0) {
56
- console.log('\n Warning: Python 3 not found. Notifications will work but show');
57
- console.log(' generic messages instead of contextual ones.');
58
- } else {
59
- console.log(`\n Python 3: ${py.stdout.toString().trim()}`);
60
- }
61
-
62
62
  console.log('\nRun "claude-code-notify setup" to apply these changes.');
63
63
  }
64
64
 
@@ -68,15 +68,6 @@ function setup() {
68
68
  process.exit(1);
69
69
  }
70
70
 
71
- // Check python3 availability
72
- const { spawnSync } = require('child_process');
73
- const py = spawnSync('python3', ['--version'], { stdio: 'pipe' });
74
- if (py.status !== 0) {
75
- console.log(' Warning: Python 3 not found. Notifications will work but show');
76
- console.log(' generic messages instead of contextual ones.');
77
- console.log(' Install Python 3 for full functionality.\n');
78
- }
79
-
80
71
  // Copy notify.sh
81
72
  fs.mkdirSync(SCRIPTS_DIR, { recursive: true });
82
73
  fs.copyFileSync(NOTIFY_SRC, NOTIFY_DEST);
@@ -101,11 +92,12 @@ function setup() {
101
92
  }
102
93
 
103
94
  if (!settings.hooks) settings.hooks = {};
104
- settings.hooks.Notification = HOOK_CONFIG.Notification;
95
+ settings.hooks.Notification = hookConfig().Notification;
105
96
 
106
97
  fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
107
98
  console.log(` Updated ${SETTINGS_PATH}`);
108
99
 
100
+ if (name) console.log(` Notifications will appear as "${name}".`);
109
101
  console.log('\nDone! Restart Claude Code for notifications to take effect.');
110
102
  }
111
103
 
@@ -138,7 +130,10 @@ function test() {
138
130
  const input = JSON.stringify({ notification_message: 'Test notification from claude-code-notify' });
139
131
  try {
140
132
  const { spawnSync } = require('child_process');
141
- const result = spawnSync('bash', [NOTIFY_SRC], {
133
+ const scriptArgs = [NOTIFY_SRC];
134
+ if (sound) scriptArgs.push('--sound');
135
+ if (name) scriptArgs.push('--name', name);
136
+ const result = spawnSync('bash', scriptArgs, {
142
137
  input,
143
138
  stdio: ['pipe', 'inherit', 'inherit'],
144
139
  timeout: 15000
@@ -156,11 +151,15 @@ function help() {
156
151
  claude-code-notify - Desktop notifications for Claude Code
157
152
 
158
153
  Usage:
159
- claude-code-notify setup Install notification hook into ~/.claude/
160
- claude-code-notify dry-run Preview what setup will change (no writes)
161
- claude-code-notify uninstall Remove notification hook from ~/.claude/
162
- claude-code-notify test Send a test notification
163
- claude-code-notify help Show this help
154
+ claude-code-notify setup [options] Install notification hook into ~/.claude/
155
+ claude-code-notify dry-run [options] Preview what setup will change (no writes)
156
+ claude-code-notify uninstall Remove notification hook from ~/.claude/
157
+ claude-code-notify test [options] Send a test notification
158
+ claude-code-notify help Show this help
159
+
160
+ Options:
161
+ --sound Enable notification sound (default: silent)
162
+ --name <name> Give your Claude a name (e.g. --name "Jarvis")
164
163
 
165
164
  Plugin mode (no setup needed):
166
165
  claude --plugin-dir /path/to/node_modules/claude-code-notify
@@ -2,23 +2,41 @@
2
2
  # Cross-platform desktop notification for Claude Code
3
3
  # Reads hook JSON from stdin, extracts notification message, sends native OS notification
4
4
 
5
+ # Parse flags
6
+ SILENT=true
7
+ NAME="Claude Code"
8
+ while [ $# -gt 0 ]; do
9
+ case "$1" in
10
+ --sound) SILENT=false ;;
11
+ --silent) SILENT=true ;;
12
+ --name) shift; NAME="$1" ;;
13
+ esac
14
+ shift
15
+ done
16
+
5
17
  INPUT=$(cat)
6
18
 
7
19
  # Parse JSON and sanitize for safe shell usage
8
20
  # - Strip characters that could break shell/PowerShell/XML/AppleScript interpolation
9
21
  # - Truncate to 200 chars (OS notification display limit)
10
- MESSAGE=$(printf '%s' "$INPUT" | python3 -c "
11
- import sys, json, re
12
- data = json.load(sys.stdin)
13
- message = data.get('notification_message', data.get('message', 'Needs your attention'))
14
- message = re.sub(r'[^\w\s.,!?:;()\[\]{}/\\@#%+=\-]', '', message)
15
- message = message[:200]
16
- print(message)
22
+ MESSAGE=$(printf '%s' "$INPUT" | node -e "
23
+ let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{
24
+ try{const o=JSON.parse(d);
25
+ let m=o.notification_message||o.message||'Needs your attention';
26
+ m=m.replace(/[^\w\s.,!?:;()\[\]{}\/\\@#%+=\-]/g,'').slice(0,200);
27
+ process.stdout.write(m);
28
+ }catch(e){process.stdout.write('Needs your attention')}
29
+ });
17
30
  " 2>/dev/null)
18
31
 
19
- # Fallback if python3 parsing fails
20
32
  MESSAGE="${MESSAGE:-Needs your attention}"
21
33
 
34
+ # Build audio XML for Windows toast
35
+ AUDIO_XML=""
36
+ if [ "$SILENT" = true ]; then
37
+ AUDIO_XML='<audio silent="true"/>'
38
+ fi
39
+
22
40
  # Detect OS and send notification
23
41
  case "$(uname -s)" in
24
42
  Linux*)
@@ -28,18 +46,26 @@ case "$(uname -s)" in
28
46
  [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > \$null
29
47
  [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > \$null
30
48
  \$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
31
- \$xml.LoadXml('<toast><visual><binding template=\"ToastText01\"><text id=\"1\">$MESSAGE</text></binding></visual><audio silent=\"true\"/></toast>')
49
+ \$xml.LoadXml('<toast><visual><binding template=\"ToastText01\"><text id=\"1\">$MESSAGE</text></binding></visual>$AUDIO_XML</toast>')
32
50
  \$toast = New-Object Windows.UI.Notifications.ToastNotification \$xml
33
- [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Claude Code').Show(\$toast)
51
+ [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('$NAME').Show(\$toast)
34
52
  " 2>/dev/null
35
53
  else
36
54
  # Native Linux
37
- notify-send "Claude Code" "$MESSAGE" --app-name="Claude Code" 2>/dev/null || true
55
+ if [ "$SILENT" = true ]; then
56
+ notify-send "$NAME" "$MESSAGE" --app-name="$NAME" -h string:suppress-sound:true 2>/dev/null || true
57
+ else
58
+ notify-send "$NAME" "$MESSAGE" --app-name="$NAME" 2>/dev/null || true
59
+ fi
38
60
  fi
39
61
  ;;
40
62
  Darwin*)
41
63
  # macOS
42
- osascript -e "display notification \"$MESSAGE\" with title \"Claude Code\"" 2>/dev/null || true
64
+ if [ "$SILENT" = true ]; then
65
+ osascript -e "display notification \"$MESSAGE\" with title \"$NAME\"" 2>/dev/null || true
66
+ else
67
+ osascript -e "display notification \"$MESSAGE\" with title \"$NAME\" sound name \"default\"" 2>/dev/null || true
68
+ fi
43
69
  ;;
44
70
  CYGWIN*|MINGW*|MSYS*)
45
71
  # Native Windows (Git Bash / MSYS2)
@@ -47,9 +73,9 @@ case "$(uname -s)" in
47
73
  [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > \$null
48
74
  [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] > \$null
49
75
  \$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
50
- \$xml.LoadXml('<toast><visual><binding template=\"ToastText01\"><text id=\"1\">$MESSAGE</text></binding></visual><audio silent=\"true\"/></toast>')
76
+ \$xml.LoadXml('<toast><visual><binding template=\"ToastText01\"><text id=\"1\">$MESSAGE</text></binding></visual>$AUDIO_XML</toast>')
51
77
  \$toast = New-Object Windows.UI.Notifications.ToastNotification \$xml
52
- [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('Claude Code').Show(\$toast)
78
+ [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('$NAME').Show(\$toast)
53
79
  " 2>/dev/null
54
80
  ;;
55
81
  esac
package/package.json CHANGED
@@ -1,8 +1,14 @@
1
1
  {
2
2
  "name": "@landienzla/claude-code-notify",
3
- "version": "1.0.4",
3
+ "version": "1.1.2",
4
4
  "description": "Cross-platform desktop notifications for Claude Code — plugin and CLI setup tool",
5
5
  "author": "Talha <landienzla@gmail.com>",
6
+ "scripts": {
7
+ "test": "node --test test/cli.test.js test/notify.test.js"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
6
12
  "license": "MIT",
7
13
  "repository": {
8
14
  "type": "git",