oc-sounds 0.1.1 → 0.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 +14 -5
- package/dist/config.d.ts +2 -0
- package/dist/config.js +9 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +30 -14
- package/examples/opencode.json +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,7 +95,8 @@ hooks. The built-in tool palette is:
|
|
|
95
95
|
| --- | --- |
|
|
96
96
|
| Read files | `page` |
|
|
97
97
|
| Glob and grep | `release` |
|
|
98
|
-
| Web search
|
|
98
|
+
| Web search | `arrival` |
|
|
99
|
+
| Web fetch | `loading` |
|
|
99
100
|
| Write and edit | `toggle` |
|
|
100
101
|
| Patch a file | `arrival` |
|
|
101
102
|
| Shell / code execution | `tick` |
|
|
@@ -104,6 +105,7 @@ hooks. The built-in tool palette is:
|
|
|
104
105
|
| Ask the user a question | `chime` |
|
|
105
106
|
| Other tools | `tick` |
|
|
106
107
|
| Tool succeeds / fails | `release` / `error` |
|
|
108
|
+
| Web fetch succeeds | `ready` |
|
|
107
109
|
|
|
108
110
|
Session actions also have cues. The option keys are listed below:
|
|
109
111
|
|
|
@@ -147,9 +149,9 @@ lifecycle cues.
|
|
|
147
149
|
## Customize
|
|
148
150
|
|
|
149
151
|
**Every action's sound is configurable.** Use `cues` for session actions such as
|
|
150
|
-
thinking, replying, and completion,
|
|
151
|
-
|
|
152
|
-
to mute it. Omitted settings keep their defaults.
|
|
152
|
+
thinking, replying, and completion, `tools` for individual tool-start sounds,
|
|
153
|
+
and `returns` for successful per-tool return sounds. Set a value to any Cuelume
|
|
154
|
+
sound name, or `false` to mute it. Omitted settings keep their defaults.
|
|
153
155
|
|
|
154
156
|
Edit the existing plugin entry in `~/.config/opencode/opencode.jsonc` (global)
|
|
155
157
|
or your project's `opencode.jsonc`. Merge it with your other settings. If you use
|
|
@@ -180,9 +182,12 @@ a local checkout, keep its directory path in `package`:
|
|
|
180
182
|
"read": "tick",
|
|
181
183
|
"edit": "toggle",
|
|
182
184
|
"shell": "pulse",
|
|
183
|
-
"webfetch": "
|
|
185
|
+
"webfetch": "loading",
|
|
184
186
|
"websearch": "arrival",
|
|
185
187
|
"my_mcp_tool": "sparkle"
|
|
188
|
+
},
|
|
189
|
+
"returns": {
|
|
190
|
+
"webfetch": "ready"
|
|
186
191
|
}
|
|
187
192
|
}
|
|
188
193
|
}
|
|
@@ -215,6 +220,10 @@ does not require rebuilding or reinstalling the plugin.
|
|
|
215
220
|
name (`read` also matches `functions.read` / `functions_read`). Exact names
|
|
216
221
|
take precedence. `false` mutes that tool's starting cue; completion and error
|
|
217
222
|
cues are controlled separately. Tool overrides take precedence over `cues.tool`.
|
|
223
|
+
- `returns`: override successful return cues by exact effective tool name or
|
|
224
|
+
built-in short name. The default `webfetch` return is `ready`; other successful
|
|
225
|
+
tools fall back to `cues.toolComplete`. Set a value to `false` to mute that
|
|
226
|
+
tool's successful return without muting its starting cue.
|
|
218
227
|
|
|
219
228
|
All 17 Cuelume sounds are available:
|
|
220
229
|
`chime`, `sparkle`, `droplet`, `bloom`, `whisper`, `tick`, `press`, `release`,
|
package/dist/config.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare const defaultCues: {
|
|
|
23
23
|
};
|
|
24
24
|
export type Action = keyof typeof defaultCues;
|
|
25
25
|
export declare const defaultToolCues: Record<string, SoundName>;
|
|
26
|
+
export declare const defaultToolReturnCues: Record<string, SoundName>;
|
|
26
27
|
export type PlayerName = "auto" | "paplay" | "pw-play" | "afplay" | "ffplay";
|
|
27
28
|
export declare const defaultPlayback: {
|
|
28
29
|
minIntervalMs: number;
|
|
@@ -39,5 +40,6 @@ export interface Options {
|
|
|
39
40
|
playback: PlaybackOptions;
|
|
40
41
|
cues: Record<Action, SoundName | false>;
|
|
41
42
|
tools: Record<string, SoundName | false>;
|
|
43
|
+
returns: Record<string, SoundName | false>;
|
|
42
44
|
}
|
|
43
45
|
export declare function parseOptions(input: unknown): Options;
|
package/dist/config.js
CHANGED
|
@@ -29,7 +29,7 @@ export const defaultToolCues = {
|
|
|
29
29
|
glob: "release",
|
|
30
30
|
grep: "release",
|
|
31
31
|
websearch: "arrival",
|
|
32
|
-
webfetch: "
|
|
32
|
+
webfetch: "loading",
|
|
33
33
|
write: "toggle",
|
|
34
34
|
edit: "toggle",
|
|
35
35
|
patch: "arrival",
|
|
@@ -41,6 +41,9 @@ export const defaultToolCues = {
|
|
|
41
41
|
task: "arrival",
|
|
42
42
|
skill: "sparkle",
|
|
43
43
|
};
|
|
44
|
+
export const defaultToolReturnCues = {
|
|
45
|
+
webfetch: "ready",
|
|
46
|
+
};
|
|
44
47
|
export const defaultPlayback = { minIntervalMs: 180, dedupeWindowMs: 300 };
|
|
45
48
|
function object(value) {
|
|
46
49
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
@@ -56,7 +59,7 @@ function cue(value) {
|
|
|
56
59
|
export function parseOptions(input) {
|
|
57
60
|
const raw = object(input);
|
|
58
61
|
for (const key of Object.keys(raw)) {
|
|
59
|
-
if (!["enabled", "volume", "player", "playback", "cues", "tools"].includes(key)) {
|
|
62
|
+
if (!["enabled", "volume", "player", "playback", "cues", "tools", "returns"].includes(key)) {
|
|
60
63
|
throw new Error(`oc-sounds: unknown option ${key}`);
|
|
61
64
|
}
|
|
62
65
|
}
|
|
@@ -80,6 +83,9 @@ export function parseOptions(input) {
|
|
|
80
83
|
const tools = Object.create(null);
|
|
81
84
|
for (const [key, value] of Object.entries(object(raw.tools ?? {})))
|
|
82
85
|
tools[key] = cue(value);
|
|
86
|
+
const returns = Object.create(null);
|
|
87
|
+
for (const [key, value] of Object.entries(object(raw.returns ?? {})))
|
|
88
|
+
returns[key] = cue(value);
|
|
83
89
|
const playback = { ...defaultPlayback };
|
|
84
90
|
for (const [key, value] of Object.entries(object(raw.playback ?? {}))) {
|
|
85
91
|
if (!Object.hasOwn(playback, key))
|
|
@@ -89,5 +95,5 @@ export function parseOptions(input) {
|
|
|
89
95
|
}
|
|
90
96
|
playback[key] = value;
|
|
91
97
|
}
|
|
92
|
-
return { enabled, volume, player: player, playback, cues, tools };
|
|
98
|
+
return { enabled, volume, player: player, playback, cues, tools, returns };
|
|
93
99
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Plugin } from "@opencode/plugin";
|
|
2
2
|
import { type Action, type Options } from "./config.js";
|
|
3
3
|
import { type Player } from "./player.js";
|
|
4
|
-
export { defaultCues, defaultToolCues, defaultPlayback, sounds } from "./config.js";
|
|
4
|
+
export { defaultCues, defaultToolCues, defaultToolReturnCues, defaultPlayback, sounds } from "./config.js";
|
|
5
5
|
export type { Action, Options, PlaybackOptions, SoundName } from "./config.js";
|
|
6
6
|
type Event = ReturnType<Plugin.Context["event"]["subscribe"]> extends AsyncIterable<infer E> ? E : never;
|
|
7
7
|
export declare function actionForEvent(event: Event): Action | undefined;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Plugin } from "@opencode/plugin";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4
|
-
import { defaultToolCues, parseOptions } from "./config.js";
|
|
4
|
+
import { defaultToolCues, defaultToolReturnCues, parseOptions } from "./config.js";
|
|
5
5
|
import { createPlayer } from "./player.js";
|
|
6
6
|
import { schedulePlayer } from "./scheduler.js";
|
|
7
|
-
export { defaultCues, defaultToolCues, defaultPlayback, sounds } from "./config.js";
|
|
7
|
+
export { defaultCues, defaultToolCues, defaultToolReturnCues, defaultPlayback, sounds } from "./config.js";
|
|
8
8
|
const eventActions = {
|
|
9
9
|
"session.inbox.enqueued": "prompt",
|
|
10
10
|
"session.execution.started": "start",
|
|
@@ -26,6 +26,9 @@ const eventActions = {
|
|
|
26
26
|
export function actionForEvent(event) {
|
|
27
27
|
return eventActions[event.type];
|
|
28
28
|
}
|
|
29
|
+
function shortToolName(tool) {
|
|
30
|
+
return tool.split(".").at(-1).replace(/^(functions|tools)_/, "");
|
|
31
|
+
}
|
|
29
32
|
const importantActions = new Set([
|
|
30
33
|
"complete", "error", "toolError", "permission", "question", "interrupt",
|
|
31
34
|
]);
|
|
@@ -37,21 +40,31 @@ export async function setup(ctx, makePlayer = createPlayer) {
|
|
|
37
40
|
const player = schedulePlayer(makePlayer(options.player, options.volume), options.playback);
|
|
38
41
|
const controller = new AbortController();
|
|
39
42
|
const registrations = [];
|
|
43
|
+
const calls = new Map();
|
|
40
44
|
const emit = (action, tool) => {
|
|
41
45
|
if (controller.signal.aborted)
|
|
42
46
|
return;
|
|
43
47
|
let sound = options.cues[action];
|
|
44
48
|
if (tool !== undefined) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
const short = shortToolName(tool);
|
|
50
|
+
if (action === "tool") {
|
|
51
|
+
if (Object.hasOwn(options.tools, tool))
|
|
52
|
+
sound = options.tools[tool];
|
|
53
|
+
else if (Object.hasOwn(options.tools, short))
|
|
54
|
+
sound = options.tools[short];
|
|
55
|
+
// An explicit general cue replaces the built-in palette for all tools.
|
|
56
|
+
else if (!Object.hasOwn(ctx.options.cues ?? {}, "tool")) {
|
|
57
|
+
if (Object.hasOwn(defaultToolCues, short))
|
|
58
|
+
sound = defaultToolCues[short];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (action === "toolComplete") {
|
|
62
|
+
if (Object.hasOwn(options.returns, tool))
|
|
63
|
+
sound = options.returns[tool];
|
|
64
|
+
else if (Object.hasOwn(options.returns, short))
|
|
65
|
+
sound = options.returns[short];
|
|
66
|
+
else if (Object.hasOwn(defaultToolReturnCues, short))
|
|
67
|
+
sound = defaultToolReturnCues[short];
|
|
55
68
|
}
|
|
56
69
|
}
|
|
57
70
|
if (sound)
|
|
@@ -64,11 +77,14 @@ export async function setup(ctx, makePlayer = createPlayer) {
|
|
|
64
77
|
};
|
|
65
78
|
try {
|
|
66
79
|
registrations.push(await ctx.tool.hook("execute.before", (event) => {
|
|
67
|
-
const name = event.tool
|
|
80
|
+
const name = shortToolName(event.tool);
|
|
81
|
+
calls.set(event.id, event.tool);
|
|
68
82
|
emit(name === "question" ? "question" : "tool", event.tool);
|
|
69
83
|
}));
|
|
70
84
|
registrations.push(await ctx.tool.hook("execute.after", (event) => {
|
|
71
|
-
|
|
85
|
+
const tool = calls.get(event.id) ?? event.tool;
|
|
86
|
+
calls.delete(event.id);
|
|
87
|
+
emit(event.status === "error" ? "toolError" : "toolComplete", typeof tool === "string" ? tool : undefined);
|
|
72
88
|
}));
|
|
73
89
|
}
|
|
74
90
|
catch (error) {
|
package/examples/opencode.json
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"glob": "release",
|
|
38
38
|
"grep": "release",
|
|
39
39
|
"websearch": "arrival",
|
|
40
|
-
"webfetch": "
|
|
40
|
+
"webfetch": "loading",
|
|
41
41
|
"write": "toggle",
|
|
42
42
|
"edit": "toggle",
|
|
43
43
|
"patch": "arrival",
|
|
@@ -48,6 +48,9 @@
|
|
|
48
48
|
"subagent": "arrival",
|
|
49
49
|
"task": "arrival",
|
|
50
50
|
"skill": "sparkle"
|
|
51
|
+
},
|
|
52
|
+
"returns": {
|
|
53
|
+
"webfetch": "ready"
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
}
|