opencode-plugin-boops 1.0.0 → 2.0.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
@@ -4,18 +4,28 @@ Sound notifications for OpenCode - plays pleasant "boop" sounds when tasks compl
4
4
 
5
5
  ## Features
6
6
 
7
- - 🎵 Pleasant glass chime when AI completes a response
8
- - 📡 Sonar ping when AI needs your permission or input
9
- - 🔄 Automatic fallback to alternative sounds if preferred sounds aren't available
7
+ - 🎵 Soft, friendly notification sounds when AI completes tasks
8
+ - 📡 Gentle alert when AI needs your permission or input
9
+ - 🌐 Works out of the box with online sounds (auto-downloaded and cached)
10
+ - 🔄 Fully configurable - use URLs or local files
10
11
  - 🐧 Works on Linux (with `paplay` or `aplay`)
11
- - 🍎 Works on macOS (requires custom sound configuration)
12
+ - 🍎 Works on macOS (with `afplay`)
12
13
 
13
14
  ## Installation
14
15
 
15
- ### Using OpenCode config
16
+ Add to your OpenCode config file:
16
17
 
17
- Add to your `opencode.json` or `~/.config/opencode/opencode.json`:
18
+ **Global installation** (recommended):
19
+ ```bash
20
+ # Edit ~/.config/opencode/opencode.json
21
+ ```
22
+
23
+ **Per-project installation**:
24
+ ```bash
25
+ # Edit opencode.json in your project root
26
+ ```
18
27
 
28
+ Add the plugin to the config:
19
29
  ```json
20
30
  {
21
31
  "$schema": "https://opencode.ai/config.json",
@@ -23,51 +33,238 @@ Add to your `opencode.json` or `~/.config/opencode/opencode.json`:
23
33
  }
24
34
  ```
25
35
 
26
- ### Manual installation
36
+ Then restart OpenCode. The plugin will be automatically downloaded and installed from npm.
27
37
 
28
- Place the plugin file in your OpenCode plugins directory:
38
+ ### Quick setup
39
+
40
+ If you don't have an OpenCode config yet:
29
41
 
30
42
  ```bash
31
- # Global
32
- mkdir -p ~/.config/opencode/plugins
33
- cp index.ts ~/.config/opencode/plugins/boops.ts
43
+ # Create global config
44
+ mkdir -p ~/.config/opencode
45
+ echo '{"$schema":"https://opencode.ai/config.json","plugin":["opencode-plugin-boops"]}' > ~/.config/opencode/opencode.json
46
+ ```
47
+
48
+ Or for a specific project:
34
49
 
35
- # Per-project
36
- mkdir -p .opencode/plugins
37
- cp index.ts .opencode/plugins/boops.ts
50
+ ```bash
51
+ # In your project directory
52
+ echo '{"$schema":"https://opencode.ai/config.json","plugin":["opencode-plugin-boops"]}' > opencode.json
38
53
  ```
39
54
 
40
55
  ## How it works
41
56
 
42
- The plugin listens to OpenCode events:
57
+ The plugin listens to OpenCode events and plays sounds based on your configuration. By default, it uses online sounds that are automatically downloaded and cached:
58
+
59
+ - **`session.idle`** - AI finishes responding → soft "pristine" notification
60
+ - **`permission.asked`** - AI needs permission → gentle "relax" chime
61
+ - **`session.error`** - An error occurs → friendly "magic" alert
62
+
63
+ Sounds are downloaded once on first use and cached in `~/.cache/opencode/boops/` for instant playback.
64
+
65
+ ## Configuration
66
+
67
+ The plugin automatically creates a configuration file at `~/.config/opencode/plugins/boops/boops.toml` on first install.
68
+
69
+ ### Customize your config
70
+
71
+ The config is automatically created from `boops.default.toml` when you install the plugin. Edit it to customize your sounds:
72
+
73
+ ```bash
74
+ # Edit your config
75
+ $EDITOR ~/.config/opencode/plugins/boops/boops.toml
76
+ ```
77
+
78
+ Example configuration:
79
+
80
+ ```toml
81
+ # ~/.config/opencode/plugins/boops/boops.toml
43
82
 
44
- - **`session.idle`** - Fires when the AI finishes responding → plays completion sound (glass.ogg)
45
- - **`permission.asked`** - Fires when the AI needs permission → plays attention sound (sonar.ogg)
83
+ [sounds]
84
+ # Use sound names from sounds.json (recommended)
85
+ "session.idle" = "pristine"
86
+ "permission.asked" = "relax"
87
+ "session.error" = "magic"
46
88
 
47
- ## Customization
89
+ # Or use full URLs:
90
+ # "session.idle" = "https://example.com/sound.ogg"
48
91
 
49
- ### Custom sounds
92
+ # Or use local file paths:
93
+ # "session.idle" = "/usr/share/sounds/gnome/default/alerts/drip.ogg"
94
+ ```
95
+
96
+ ### Sound sources
50
97
 
51
- To use your own sounds, modify the sound file paths in `index.ts`:
98
+ The plugin supports multiple ways to specify sounds:
52
99
 
53
- ```typescript
54
- const inputSound = '/path/to/your/input-sound.ogg'
55
- const completeSound = '/path/to/your/complete-sound.ogg'
100
+ **1. Sound names from sounds.json (easiest):**
101
+ ```toml
102
+ "session.idle" = "pristine"
103
+ "permission.asked" = "relax"
104
+ "session.error" = "magic"
56
105
  ```
57
106
 
58
- ### macOS
107
+ Use the TUI browser (`~/.config/opencode/plugins/boops/browse`) to explore all 448 sounds with semantic tags!
59
108
 
60
- On macOS, you can use system sounds:
109
+ **2. Full URLs:**
110
+ ```toml
111
+ "session.idle" = "https://example.com/sound.ogg"
112
+ ```
61
113
 
62
- ```typescript
63
- const inputSound = '/System/Library/Sounds/Ping.aiff'
64
- const completeSound = '/System/Library/Sounds/Glass.aiff'
114
+ **3. Local file paths:**
115
+ ```toml
116
+ "session.idle" = "/usr/share/sounds/gnome/default/alerts/drip.ogg"
65
117
  ```
66
118
 
67
- And update the `playSound` function to use `afplay`:
119
+ **Caching:** Remote sounds (IDs and URLs) are cached in `~/.cache/opencode/boops/` by event name (e.g. `session-idle`, `permission-asked`). Once downloaded, they're reused instantly. If you change a sound, the cache is automatically updated on next play.
120
+
121
+ ### Available events
122
+
123
+ You can configure sounds for any of the 28 OpenCode events:
124
+
125
+ **Session events (8):**
126
+ - `session.idle` - AI completes response
127
+ - `session.error` - Error occurs
128
+ - `session.created` - New session starts
129
+ - `session.deleted` - Session deleted
130
+ - `session.compacted` - Session compacted
131
+ - `session.diff` - Session diff generated
132
+ - `session.status` - Session status changed
133
+ - `session.updated` - Session updated
134
+
135
+ **Permission events (2):**
136
+ - `permission.asked` - AI needs permission
137
+ - `permission.replied` - Permission response given
138
+
139
+ **File events (2):**
140
+ - `file.edited` - File is edited
141
+ - `file.watcher.updated` - File watcher detects change
142
+
143
+ **Tool events (2):**
144
+ - `tool.execute.before` - Before tool execution
145
+ - `tool.execute.after` - After tool execution
146
+
147
+ **Message events (4):**
148
+ - `message.part.removed` - Message part removed
149
+ - `message.part.updated` - Message part updated
150
+ - `message.removed` - Message removed
151
+ - `message.updated` - Message updated
68
152
 
69
- ```typescript
70
- await Bun.$`afplay ${primaryFile}`.quiet()
153
+ **LSP events (2):**
154
+ - `lsp.client.diagnostics` - LSP diagnostics received
155
+ - `lsp.updated` - LSP server updated
156
+
157
+ **TUI events (3):**
158
+ - `tui.prompt.append` - Text appended to prompt
159
+ - `tui.command.execute` - TUI command executed
160
+ - `tui.toast.show` - Toast notification shown
161
+
162
+ **Other events (5):**
163
+ - `command.executed` - Command executed
164
+ - `todo.updated` - Todo list updated
165
+ - `installation.updated` - Installation/package updated
166
+ - `server.connected` - Connected to server
167
+
168
+ See the [default config](boops.default.toml) for the complete list with descriptions.
169
+
170
+ ### Event filters
171
+
172
+ For advanced use cases, you can add filters to play sounds only when certain conditions match. This is useful for events that fire frequently or in different contexts.
173
+
174
+ **Example: Only play sound for main session, not subagents:**
175
+
176
+ ```toml
177
+ [sounds.session.idle]
178
+ sound = "pristine"
179
+ not_if = { agent = "explore" } # Skip subagent completions
180
+ ```
181
+
182
+ **How to find available properties:**
183
+
184
+ 1. Check OpenCode logs when the event fires:
185
+ ```bash
186
+ tail -f ~/.local/share/opencode/log/*.log | grep "session.idle event"
187
+ ```
188
+
189
+ 2. The logs will show all event properties you can filter on
190
+
191
+ **Filter syntax:**
192
+
193
+ ```toml
194
+ [sounds.event-name]
195
+ sound = "/path/to/sound"
196
+ only_if = { property = "value" } # Only play if property equals value
197
+ not_if = { property = "value" } # Don't play if property equals value
198
+ ```
199
+
200
+ ### Testing sounds
201
+
202
+ You can test sounds without restarting OpenCode using the custom tool:
203
+
204
+ ```bash
205
+ # Test a configured event (reloads config automatically)
206
+ test-sound session.idle
207
+
208
+ # Test a sound ID directly
209
+ # Test by sound name
210
+ test-sound pristine
211
+ test-sound "access granted computer voice"
212
+
213
+ # Test a URL directly
214
+ test-sound https://example.com/sound.ogg
215
+
216
+ # Test a local file directly
217
+ test-sound /usr/share/sounds/alert.ogg
218
+ ```
219
+
220
+ This is helpful when:
221
+ - Configuring new sounds
222
+ - Trying different sound files before committing to config
223
+ - Testing event filters
224
+ - Verifying URLs/IDs work
225
+
226
+ The test command automatically reloads your config file, so you can edit `boops.toml` and test immediately!
227
+
228
+ ### Browse sounds interactively
229
+
230
+ The plugin includes a beautiful TUI for browsing and assigning sounds:
231
+
232
+ ```bash
233
+ # If plugin is installed
234
+ ~/.config/opencode/plugins/boops/browse
235
+
236
+ # Or try it with npx (browse-only, saving disabled)
237
+ npx opencode-plugin-boops browse
238
+ ```
239
+
240
+ Features:
241
+ - 🎨 Browse 448 sounds with tags and descriptions
242
+ - 🏷️ Filter by tags (music, bell, quiet, etc.)
243
+ - 🔍 Search by name
244
+ - 🎵 Preview sounds before assigning
245
+ - ⚡ Assign sounds to OpenCode events (if plugin installed)
246
+ - 🖱️ Full mouse support with hover effects
247
+ - ⌨️ Keyboard navigation
248
+
249
+ **Note**: When running via `npx`, you can browse and preview sounds, but saving assignments requires the plugin to be installed.
250
+
251
+ ### Custom sound player
252
+
253
+ By default, the plugin auto-detects `paplay` (PulseAudio), `aplay` (ALSA), or `afplay` (macOS). You can override this:
254
+
255
+ ```toml
256
+ player = "afplay" # Force specific player
257
+ ```
258
+
259
+ ### macOS example
260
+
261
+ ```toml
262
+ player = "afplay"
263
+
264
+ [sounds]
265
+ "session.idle" = "/System/Library/Sounds/Glass.aiff"
266
+ "permission.asked" = "/System/Library/Sounds/Ping.aiff"
267
+ "session.error" = "/System/Library/Sounds/Basso.aiff"
71
268
  ```
72
269
 
73
270
  ## Requirements
@@ -75,28 +272,40 @@ await Bun.$`afplay ${primaryFile}`.quiet()
75
272
  - OpenCode 1.0+
76
273
  - Linux: `paplay` (PulseAudio) or `aplay` (ALSA)
77
274
  - macOS: `afplay` (built-in)
78
- - Sound files at specified paths (or system sounds)
275
+ - Internet connection (only for initial sound download)
79
276
 
80
277
  ## Troubleshooting
81
278
 
82
279
  ### No sound playing
83
280
 
84
- 1. Check if sound files exist:
281
+ 1. **Use the test command first:**
85
282
  ```bash
86
- ls /usr/share/sounds/gnome/default/alerts/glass.ogg
87
- ls /usr/share/sounds/gnome/default/alerts/sonar.ogg
283
+ test-sound event="session.idle"
88
284
  ```
89
285
 
90
- 2. Test sound manually:
286
+ 2. **Check if sound player is installed:**
91
287
  ```bash
92
- paplay /usr/share/sounds/gnome/default/alerts/glass.ogg
288
+ which paplay # Linux (PulseAudio)
289
+ which aplay # Linux (ALSA)
290
+ which afplay # macOS
93
291
  ```
94
292
 
95
- 3. Check OpenCode logs:
293
+ 3. **Check OpenCode logs:**
96
294
  ```bash
97
295
  tail -f ~/.local/share/opencode/log/*.log | grep boops
98
296
  ```
99
297
 
298
+ 4. **Check cached sounds:**
299
+ ```bash
300
+ ls -la ~/.cache/opencode/boops/
301
+ ```
302
+
303
+ 5. **Clear cache and re-download:**
304
+ ```bash
305
+ rm -rf ~/.cache/opencode/boops/
306
+ # Then use test-sound to re-download
307
+ ```
308
+
100
309
  ### Sounds too loud/quiet
101
310
 
102
311
  Adjust your system volume or use different sound files.
@@ -109,6 +318,10 @@ Contributions welcome! Feel free to:
109
318
  - Improve cross-platform compatibility
110
319
  - Add more event triggers
111
320
 
321
+ ## Credits
322
+
323
+ Sounds from [Notification Sounds](https://notificationsounds.com) - a wonderful collection of free notification sounds provided under Creative Commons Attribution license.
324
+
112
325
  ## License
113
326
 
114
327
  MIT
@@ -0,0 +1,70 @@
1
+ # OpenCode Boops Plugin Configuration
2
+ # Sounds can be local file paths OR URLs (automatically downloaded and cached)
3
+
4
+ # player = "paplay" # Auto-detected if not set (paplay/aplay/afplay)
5
+
6
+ [sounds]
7
+ "session.idle" = "pristine" # AI completes response
8
+ "permission.asked" = "relax" # AI needs permission
9
+ "session.error" = "magic" # Error occurs
10
+
11
+ # You can use:
12
+ # - Sound names: "pristine" (searches sounds.json by name)
13
+ # - Full URLs: "https://example.com/sound.ogg"
14
+ # - Local paths: "/usr/share/sounds/..."
15
+
16
+ # Advanced: Event filters (play sound only when conditions match)
17
+ # Uncomment and adjust after checking logs to see available properties
18
+ # [sounds.session.idle]
19
+ # sound = "pristine"
20
+ # not_if = { agent = "explore" } # Don't play for subagent completions
21
+
22
+ # Optional events (uncomment to enable):
23
+ # Command events
24
+ # "command.executed" = "/path/to/sound" # Command executed
25
+
26
+ # File events
27
+ # "file.edited" = "/path/to/sound" # File edited
28
+ # "file.watcher.updated" = "/path/to/sound" # File watcher detected change
29
+
30
+ # Installation events
31
+ # "installation.updated" = "/path/to/sound" # Installation/package updated
32
+
33
+ # LSP events
34
+ # "lsp.client.diagnostics" = "/path/to/sound" # LSP diagnostics received
35
+ # "lsp.updated" = "/path/to/sound" # LSP server updated
36
+
37
+ # Message events
38
+ # "message.part.removed" = "/path/to/sound" # Message part removed
39
+ # "message.part.updated" = "/path/to/sound" # Message part updated
40
+ # "message.removed" = "/path/to/sound" # Message removed
41
+ # "message.updated" = "/path/to/sound" # Message updated
42
+
43
+ # Permission events
44
+ # "permission.replied" = "/path/to/sound" # Permission response given
45
+
46
+ # Server events
47
+ # "server.connected" = "/path/to/sound" # Connected to server
48
+
49
+ # Session events
50
+ # "session.created" = "/path/to/sound" # New session created
51
+ # "session.compacted" = "/path/to/sound" # Session compacted
52
+ # "session.deleted" = "/path/to/sound" # Session deleted
53
+ # "session.diff" = "/path/to/sound" # Session diff generated
54
+ # "session.status" = "/path/to/sound" # Session status changed
55
+ # "session.updated" = "/path/to/sound" # Session updated
56
+
57
+ # Todo events
58
+ # "todo.updated" = "/path/to/sound" # Todo list updated
59
+
60
+ # Tool events
61
+ # "tool.execute.before" = "/path/to/sound" # Before tool execution
62
+ # "tool.execute.after" = "/path/to/sound" # After tool execution
63
+
64
+ # TUI events
65
+ # "tui.prompt.append" = "/path/to/sound" # Text appended to prompt
66
+ # "tui.command.execute" = "/path/to/sound" # TUI command executed
67
+ # "tui.toast.show" = "/path/to/sound" # Toast notification shown
68
+
69
+ [fallbacks]
70
+ # default = "/usr/share/sounds/alsa/Front_Center.wav" # Optional: local fallback if download fails