@mohak34/opencode-notifier 0.1.32-beta.0 → 0.1.33

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.
Files changed (3) hide show
  1. package/README.md +15 -13
  2. package/dist/index.js +22 -13
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -66,12 +66,12 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
66
66
  "minDuration": 0
67
67
  },
68
68
  "events": {
69
- "permission": { "sound": true, "notification": true },
70
- "complete": { "sound": true, "notification": true },
71
- "subagent_complete": { "sound": false, "notification": false },
72
- "error": { "sound": true, "notification": true },
73
- "question": { "sound": true, "notification": true },
74
- "user_cancelled": { "sound": false, "notification": false }
69
+ "permission": { "sound": true, "notification": true, "command": true },
70
+ "complete": { "sound": true, "notification": true, "command": true },
71
+ "subagent_complete": { "sound": false, "notification": false, "command": true },
72
+ "error": { "sound": true, "notification": true, "command": true },
73
+ "question": { "sound": true, "notification": true, "command": true },
74
+ "user_cancelled": { "sound": false, "notification": false, "command": true }
75
75
  },
76
76
  "messages": {
77
77
  "permission": "Session needs permission: {sessionTitle}",
@@ -123,7 +123,7 @@ Create `~/.config/opencode/opencode-notifier.json` with the defaults:
123
123
  - `showSessionTitle` - Include the session title in notification messages via `{sessionTitle}` placeholder (default: true)
124
124
  - `showIcon` - Show OpenCode icon, Windows/Linux only (default: true)
125
125
  - `suppressWhenFocused` - Skip notifications and sounds when the terminal is the active window (default: true). See [Focus detection](#focus-detection) for platform details
126
- - `notificationSystem` - macOS only: `"osascript"`, `"node-notifier"`, or `"ghostty"` (default: "osascript"). Use `"ghostty"` if you're running Ghostty terminal for native OSC 777 notifications
126
+ - `notificationSystem` - macOS only: `"osascript"`, `"node-notifier"`, or `"ghostty"` (default: "osascript"). Use `"ghostty"` if you're running Ghostty terminal for native OSC 9 notifications
127
127
  - `linux.grouping` - Linux only: replace notifications in-place instead of stacking (default: false). Requires `notify-send` 0.8+
128
128
 
129
129
  ### Events
@@ -133,18 +133,20 @@ Control each event separately:
133
133
  ```json
134
134
  {
135
135
  "events": {
136
- "permission": { "sound": true, "notification": true },
137
- "complete": { "sound": true, "notification": true },
138
- "subagent_complete": { "sound": false, "notification": false },
139
- "error": { "sound": true, "notification": true },
140
- "question": { "sound": true, "notification": true },
141
- "user_cancelled": { "sound": false, "notification": false }
136
+ "permission": { "sound": true, "notification": true, "command": true },
137
+ "complete": { "sound": true, "notification": true, "command": true },
138
+ "subagent_complete": { "sound": false, "notification": false, "command": true },
139
+ "error": { "sound": true, "notification": true, "command": true },
140
+ "question": { "sound": true, "notification": true, "command": true },
141
+ "user_cancelled": { "sound": false, "notification": false, "command": true }
142
142
  }
143
143
  }
144
144
  ```
145
145
 
146
146
  `user_cancelled` fires when you press ESC to abort a session. It's silent by default so intentional cancellations don't trigger error alerts. Set `sound` or `notification` to `true` if you want confirmation when cancelling.
147
147
 
148
+ The `command` property controls whether the custom command (see [Custom commands](#custom-commands)) runs for that event. Defaults to `true` for all events. Set it to `false` to suppress the command for specific events without disabling it globally.
149
+
148
150
  Or use true/false for both:
149
151
 
150
152
  ```json
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import { basename } from "path";
3
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
3
+ import { readFileSync as readFileSync2, writeFileSync } from "fs";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync, existsSync } from "fs";
@@ -9,7 +9,8 @@ import { homedir } from "os";
9
9
  import { fileURLToPath } from "url";
10
10
  var DEFAULT_EVENT_CONFIG = {
11
11
  sound: true,
12
- notification: true
12
+ notification: true,
13
+ command: true
13
14
  };
14
15
  var DEFAULT_CONFIG = {
15
16
  sound: true,
@@ -31,11 +32,11 @@ var DEFAULT_CONFIG = {
31
32
  events: {
32
33
  permission: { ...DEFAULT_EVENT_CONFIG },
33
34
  complete: { ...DEFAULT_EVENT_CONFIG },
34
- subagent_complete: { sound: false, notification: false },
35
+ subagent_complete: { ...DEFAULT_EVENT_CONFIG, sound: false, notification: false },
35
36
  error: { ...DEFAULT_EVENT_CONFIG },
36
37
  question: { ...DEFAULT_EVENT_CONFIG },
37
38
  interrupted: { ...DEFAULT_EVENT_CONFIG },
38
- user_cancelled: { sound: false, notification: false }
39
+ user_cancelled: { ...DEFAULT_EVENT_CONFIG, sound: false, notification: false }
39
40
  },
40
41
  messages: {
41
42
  permission: "Session needs permission: {sessionTitle}",
@@ -82,12 +83,14 @@ function parseEventConfig(userEvent, defaultConfig) {
82
83
  if (typeof userEvent === "boolean") {
83
84
  return {
84
85
  sound: userEvent,
85
- notification: userEvent
86
+ notification: userEvent,
87
+ command: userEvent
86
88
  };
87
89
  }
88
90
  return {
89
91
  sound: userEvent.sound ?? defaultConfig.sound,
90
- notification: userEvent.notification ?? defaultConfig.notification
92
+ notification: userEvent.notification ?? defaultConfig.notification,
93
+ command: userEvent.command ?? defaultConfig.command
91
94
  };
92
95
  }
93
96
  function parseVolume(value, defaultVolume) {
@@ -114,7 +117,8 @@ function loadConfig() {
114
117
  const globalNotification = userConfig.notification ?? DEFAULT_CONFIG.notification;
115
118
  const defaultWithGlobal = {
116
119
  sound: globalSound,
117
- notification: globalNotification
120
+ notification: globalNotification,
121
+ command: true
118
122
  };
119
123
  const userCommand = userConfig.command ?? {};
120
124
  const commandArgs = Array.isArray(userCommand.args) ? userCommand.args.filter((arg) => typeof arg === "string") : undefined;
@@ -140,11 +144,11 @@ function loadConfig() {
140
144
  events: {
141
145
  permission: parseEventConfig(userConfig.events?.permission ?? userConfig.permission, defaultWithGlobal),
142
146
  complete: parseEventConfig(userConfig.events?.complete ?? userConfig.complete, defaultWithGlobal),
143
- subagent_complete: parseEventConfig(userConfig.events?.subagent_complete ?? userConfig.subagent_complete, { sound: false, notification: false }),
147
+ subagent_complete: parseEventConfig(userConfig.events?.subagent_complete ?? userConfig.subagent_complete, { sound: false, notification: false, command: true }),
144
148
  error: parseEventConfig(userConfig.events?.error ?? userConfig.error, defaultWithGlobal),
145
149
  question: parseEventConfig(userConfig.events?.question ?? userConfig.question, defaultWithGlobal),
146
150
  interrupted: parseEventConfig(userConfig.events?.interrupted ?? userConfig.interrupted, defaultWithGlobal),
147
- user_cancelled: parseEventConfig(userConfig.events?.user_cancelled ?? userConfig.user_cancelled, { sound: false, notification: false })
151
+ user_cancelled: parseEventConfig(userConfig.events?.user_cancelled ?? userConfig.user_cancelled, { sound: false, notification: false, command: true })
148
152
  },
149
153
  messages: {
150
154
  permission: userConfig.messages?.permission ?? DEFAULT_CONFIG.messages.permission,
@@ -184,6 +188,9 @@ function isEventSoundEnabled(config, event) {
184
188
  function isEventNotificationEnabled(config, event) {
185
189
  return config.events[event].notification;
186
190
  }
191
+ function isEventCommandEnabled(config, event) {
192
+ return config.events[event].command;
193
+ }
187
194
  function getMessage(config, event) {
188
195
  return config.messages[event];
189
196
  }
@@ -262,6 +269,7 @@ function detectNotifySendCapabilities() {
262
269
  function sendLinuxNotificationDirect(title, message, timeout, iconPath, grouping = true) {
263
270
  return new Promise((resolve) => {
264
271
  const args = [];
272
+ args.push("--app-name", "opencode");
265
273
  if (iconPath) {
266
274
  args.push("--icon", iconPath);
267
275
  }
@@ -336,7 +344,8 @@ async function sendNotification(title, message, timeout, iconPath, notificationS
336
344
  title,
337
345
  message,
338
346
  timeout,
339
- icon: iconPath
347
+ icon: iconPath,
348
+ "app-name": "opencode"
340
349
  };
341
350
  platformNotifier.notify(notificationOptions, () => {
342
351
  resolve();
@@ -420,7 +429,7 @@ async function playOnLinux(soundPath, volume) {
420
429
  const players = [
421
430
  { command: "paplay", args: [`--volume=${pulseVolume}`, soundPath] },
422
431
  { command: "aplay", args: [soundPath] },
423
- { command: "mpv", args: ["--no-video", "--no-terminal", `--volume=${percentVolume}`, soundPath] },
432
+ { command: "mpv", args: ["--no-video", "--no-terminal", "--script-opts=autoload-disabled=yes", `--volume=${percentVolume}`, soundPath] },
424
433
  { command: "ffplay", args: ["-nodisp", "-autoexit", "-loglevel", "quiet", "-volume", `${percentVolume}`, soundPath] }
425
434
  ];
426
435
  for (const player of players) {
@@ -630,7 +639,7 @@ function loadTurnCount() {
630
639
  }
631
640
  function saveTurnCount(count) {
632
641
  try {
633
- writeFileSync2(getStatePath(), JSON.stringify({ turn: count }));
642
+ writeFileSync(getStatePath(), JSON.stringify({ turn: count }));
634
643
  } catch {}
635
644
  }
636
645
  function incrementTurnCount() {
@@ -697,7 +706,7 @@ async function handleEvent(config, eventType, projectName, elapsedSeconds, sessi
697
706
  promises.push(playSound(eventType, customSoundPath, soundVolume));
698
707
  }
699
708
  const minDuration = config.command?.minDuration;
700
- const shouldSkipCommand = typeof minDuration === "number" && Number.isFinite(minDuration) && minDuration > 0 && typeof elapsedSeconds === "number" && Number.isFinite(elapsedSeconds) && elapsedSeconds < minDuration;
709
+ const shouldSkipCommand = !isEventCommandEnabled(config, eventType) || typeof minDuration === "number" && Number.isFinite(minDuration) && minDuration > 0 && typeof elapsedSeconds === "number" && Number.isFinite(elapsedSeconds) && elapsedSeconds < minDuration;
701
710
  if (!shouldSkipCommand) {
702
711
  runCommand2(config, eventType, message, sessionTitle, projectName, timestamp, turn);
703
712
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohak34/opencode-notifier",
3
- "version": "0.1.32-beta.0",
3
+ "version": "0.1.33",
4
4
  "description": "OpenCode plugin that sends system notifications and plays sounds when permission is needed, generation completes, or errors occur",
5
5
  "author": "mohak34",
6
6
  "license": "MIT",