@settinghead/voxlert 0.3.5
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/LICENSE +21 -0
- package/README.md +353 -0
- package/assets/cortana.png +0 -0
- package/assets/deckard-cain.png +0 -0
- package/assets/demo-thumbnail.png +0 -0
- package/assets/glados.png +0 -0
- package/assets/hl-hev-suit.png +0 -0
- package/assets/logo.png +0 -0
- package/assets/red-alert-eva.png +0 -0
- package/assets/sc1-adjutant.gif +0 -0
- package/assets/sc1-kerrigan.gif +0 -0
- package/assets/sc1-protoss-advisor.jpg +0 -0
- package/assets/sc2-adjutant.jpg +0 -0
- package/assets/sc2-kerrigan.jpg +0 -0
- package/assets/ss1-shodan.png +0 -0
- package/config.default.json +35 -0
- package/openclaw-plugin/index.ts +100 -0
- package/openclaw-plugin/openclaw.plugin.json +21 -0
- package/package.json +51 -0
- package/packs/hl-hev-suit/pack.json +72 -0
- package/packs/hl-hev-suit/voice.wav +0 -0
- package/packs/red-alert-eva/pack.json +73 -0
- package/packs/red-alert-eva/voice.wav +0 -0
- package/packs/sc1-adjutant/pack.json +31 -0
- package/packs/sc1-adjutant/voice.wav +0 -0
- package/packs/sc1-kerrigan/pack.json +69 -0
- package/packs/sc1-kerrigan/voice.wav +0 -0
- package/packs/sc1-protoss-advisor/pack.json +70 -0
- package/packs/sc1-protoss-advisor/voice.wav +0 -0
- package/packs/sc2-adjutant/pack.json +14 -0
- package/packs/sc2-adjutant/voice.wav +0 -0
- package/packs/sc2-kerrigan/pack.json +69 -0
- package/packs/sc2-kerrigan/voice.wav +0 -0
- package/packs/sc2-protoss-advisor/pack.json +70 -0
- package/packs/sc2-protoss-advisor/voice.wav +0 -0
- package/packs/ss1-shodan/pack.json +69 -0
- package/packs/ss1-shodan/voice.wav +0 -0
- package/skills/voxlert-config/SKILL.md +44 -0
- package/src/activity-log.js +58 -0
- package/src/audio.js +381 -0
- package/src/cli.js +86 -0
- package/src/codex-config.js +149 -0
- package/src/commands/codex-notify.js +70 -0
- package/src/commands/config.js +141 -0
- package/src/commands/cost.js +20 -0
- package/src/commands/cursor-hook.js +52 -0
- package/src/commands/help.js +25 -0
- package/src/commands/hook-utils.js +73 -0
- package/src/commands/hook.js +27 -0
- package/src/commands/index.js +45 -0
- package/src/commands/log.js +92 -0
- package/src/commands/notification.js +50 -0
- package/src/commands/pack-helpers.js +157 -0
- package/src/commands/pack.js +25 -0
- package/src/commands/setup.js +13 -0
- package/src/commands/test.js +14 -0
- package/src/commands/uninstall.js +60 -0
- package/src/commands/version.js +12 -0
- package/src/commands/voice.js +14 -0
- package/src/commands/volume.js +38 -0
- package/src/config.js +230 -0
- package/src/cost.js +124 -0
- package/src/cursor-hooks.js +93 -0
- package/src/formats.js +55 -0
- package/src/hooks.js +129 -0
- package/src/llm.js +237 -0
- package/src/overlay.js +212 -0
- package/src/overlay.jxa +186 -0
- package/src/pack-registry.js +28 -0
- package/src/packs.js +182 -0
- package/src/paths.js +39 -0
- package/src/postinstall.js +13 -0
- package/src/providers.js +129 -0
- package/src/setup-ui.js +177 -0
- package/src/setup.js +504 -0
- package/src/tts-test.js +243 -0
- package/src/upgrade-check.js +137 -0
- package/src/voxlert.js +200 -0
- package/voxlert.sh +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Voxlert Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://youtu.be/-aiSZnGNyE4">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/demo-thumbnail.png" alt="Voxlert Demo" width="100%" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://github.com/settinghead/voxlert/actions/workflows/cli-integration.yml">
|
|
9
|
+
<img src="https://github.com/settinghead/voxlert/actions/workflows/cli-integration.yml/badge.svg" alt="CLI Integration" />
|
|
10
|
+
</a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
# Voxlert
|
|
14
|
+
|
|
15
|
+
LLM-generated voice notifications for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Cursor](https://cursor.com/docs/agent/hooks), [OpenAI Codex](https://developers.openai.com/codex/), and [OpenClaw](https://openclaw.dev), spoken by game characters like the StarCraft Adjutant, Kerrigan, C&C EVA, SHODAN, and more.
|
|
16
|
+
|
|
17
|
+
## Why Voxlert?
|
|
18
|
+
|
|
19
|
+
Existing notification chimes (like [peon-ping](https://github.com/PeonPing/peon-ping)) do a great job of telling you *when* something happened, but not *what* happened or *which* agent needs your attention. If you have several agent sessions running at once, you end up alt-tabbing through windows just to find the one waiting on you.
|
|
20
|
+
|
|
21
|
+
Voxlert makes each session speak in a distinct character voice with its own tone and vocabulary. You hear *"Query efficiency restored to nominal"* from the HEV Suit in one window and *"Pathetic test suite for code validation processed"* from SHODAN in another, and you know immediately what changed. Because phrases are generated by an LLM instead of picked from a tiny fixed set, they stay varied instead of becoming wallpaper.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Install prerequisites
|
|
26
|
+
|
|
27
|
+
| Aspect | macOS | Windows | Linux |
|
|
28
|
+
|--------|-------|---------|-------|
|
|
29
|
+
| **Node.js 18+** | [nodejs.org](https://nodejs.org) or `brew install node` | [nodejs.org](https://nodejs.org) or `winget install OpenJS.NodeJS` | [nodejs.org](https://nodejs.org) or distro package (for example `sudo apt install nodejs`) |
|
|
30
|
+
| **Audio playback** | Built-in (`afplay`) | [FFmpeg](docs/installing-ffmpeg.md) so `ffplay` is on PATH | [FFmpeg](docs/installing-ffmpeg.md) so `ffplay` is on PATH |
|
|
31
|
+
| **Audio effects** | [SoX](docs/installing-sox.md) (optional) | [SoX](docs/installing-sox.md) (optional) | [SoX](docs/installing-sox.md) (optional) |
|
|
32
|
+
|
|
33
|
+
See [Installing FFmpeg](docs/installing-ffmpeg.md) and [Installing SoX](docs/installing-sox.md) for platform-specific commands.
|
|
34
|
+
|
|
35
|
+
You will also want:
|
|
36
|
+
|
|
37
|
+
- An **LLM API key** from [OpenRouter](https://openrouter.ai) (recommended), [OpenAI](https://platform.openai.com/api-keys), [Google Gemini](https://aistudio.google.com/apikey), or [Anthropic](https://console.anthropic.com/settings/keys). You can skip this and use fallback phrases only.
|
|
38
|
+
- At least one **TTS backend** if you want spoken output instead of notifications only.
|
|
39
|
+
|
|
40
|
+
| Backend | Best for | Requirements |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| [**Qwen3-TTS**](qwen3-tts-server/README.md) (recommended) | Apple Silicon or NVIDIA GPU | Python 3.13+, 16 GB RAM, ~8 GB disk |
|
|
43
|
+
| [**Chatterbox**](docs/chatterbox-tts.md) | Any platform with GPU | Python 3.10+, CUDA or MPS |
|
|
44
|
+
|
|
45
|
+
The setup wizard auto-detects running TTS backends. If none are running yet, setup still completes, but you will only get text notifications and fallback phrases until you start one and rerun setup.
|
|
46
|
+
|
|
47
|
+
### 2. Install and run setup
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -g @settinghead/voxlert
|
|
51
|
+
voxlert setup
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The setup wizard configures:
|
|
55
|
+
|
|
56
|
+
- LLM provider and API key
|
|
57
|
+
- Voice pack downloads
|
|
58
|
+
- Active voice pack
|
|
59
|
+
- TTS backend
|
|
60
|
+
- Platform hooks for Claude Code, Cursor, and Codex
|
|
61
|
+
|
|
62
|
+
For OpenClaw, install the separate [OpenClaw plugin](docs/openclaw.md).
|
|
63
|
+
|
|
64
|
+
### 3. Start a TTS backend for spoken voice
|
|
65
|
+
|
|
66
|
+
Start [Qwen3-TTS](qwen3-tts-server/README.md) or [Chatterbox](docs/chatterbox-tts.md), then run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
voxlert setup
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This lets the wizard detect the backend and store it in config.
|
|
73
|
+
|
|
74
|
+
### 4. Verify
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
voxlert test "Hello"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
You should hear a phrase and see a notification. If you do not hear speech, check that:
|
|
81
|
+
|
|
82
|
+
- A TTS server is running
|
|
83
|
+
- `voxlert config` shows the expected `tts_backend`
|
|
84
|
+
|
|
85
|
+
> **Visual notifications**: Voxlert shows a popup with each phrase without extra install. On macOS you can use the custom overlay or system Notification Center. On Windows and Linux you get system toasts. Change it anytime with:
|
|
86
|
+
> ```bash
|
|
87
|
+
> voxlert notification
|
|
88
|
+
> ```
|
|
89
|
+
|
|
90
|
+
### From a git clone
|
|
91
|
+
|
|
92
|
+
Run `npm install` inside `cli/`, then use `node src/cli.js` or link it globally if you prefer. Config and cache live in `~/.voxlert` (Windows: `%USERPROFILE%\.voxlert`).
|
|
93
|
+
|
|
94
|
+
## Development
|
|
95
|
+
|
|
96
|
+
Run tests locally with:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm test
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
For release-impacting changes, add a changeset before opening a PR:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm run changeset
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Supported Voices
|
|
109
|
+
|
|
110
|
+
| | Pack ID | Voice | Source | Status |
|
|
111
|
+
|---|---------|-------|--------|--------|
|
|
112
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/sc1-adjutant.gif" width="48" height="48" /> | `sc1-adjutant` | **SC1 Adjutant** | StarCraft | ✅ Available |
|
|
113
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/sc2-adjutant.jpg" width="48" height="48" /> | `sc2-adjutant` | **SC2 Adjutant** | StarCraft II | ✅ Available |
|
|
114
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/red-alert-eva.png" width="48" height="48" /> | `red-alert-eva` | **EVA** | Command & Conquer: Red Alert | ✅ Available |
|
|
115
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/sc1-kerrigan.gif" width="48" height="48" /> | `sc1-kerrigan` | **SC1 Kerrigan** | StarCraft | ✅ Available |
|
|
116
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/sc2-kerrigan.jpg" width="48" height="48" /> | `sc2-kerrigan` | **SC2 Kerrigan** | StarCraft II | ✅ Available |
|
|
117
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/sc1-protoss-advisor.jpg" width="48" height="48" /> | `sc1-protoss-advisor` | **Protoss Advisor** | StarCraft | ✅ Available |
|
|
118
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/ss1-shodan.png" width="48" height="48" /> | `ss1-shodan` | **SHODAN** | System Shock | ✅ Available |
|
|
119
|
+
| <img src="https://raw.githubusercontent.com/settinghead/voxlert/main/assets/hl-hev-suit.png" width="48" height="48" /> | `hl-hev-suit` | **HEV Suit** | Half-Life | ✅ Available |
|
|
120
|
+
|
|
121
|
+
More coming soon: [Request a voice](https://github.com/settinghead/voxlert/issues/new?title=Voice+request%3A+%5BCharacter+Name%5D&body=**Character%3A**+%0A**Game%2FSource%3A**+%0A**Why%3A**+)
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
voxlert voice
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Integrations
|
|
128
|
+
|
|
129
|
+
### Claude Code
|
|
130
|
+
|
|
131
|
+
Installed through `voxlert setup`. Claude Code hook events are processed by:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
voxlert hook
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Cursor
|
|
138
|
+
|
|
139
|
+
Installed through `voxlert setup`, or add hooks manually in `~/.cursor/hooks.json`:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"version": 1,
|
|
144
|
+
"hooks": {
|
|
145
|
+
"sessionStart": [{ "command": "voxlert cursor-hook", "timeout": 10 }],
|
|
146
|
+
"sessionEnd": [{ "command": "voxlert cursor-hook", "timeout": 10 }],
|
|
147
|
+
"stop": [{ "command": "voxlert cursor-hook", "timeout": 10 }],
|
|
148
|
+
"postToolUseFailure": [{ "command": "voxlert cursor-hook", "timeout": 10 }],
|
|
149
|
+
"preCompact": [{ "command": "voxlert cursor-hook", "timeout": 10 }]
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Cursor Hook Event | Voxlert Event | Category |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `sessionStart` | SessionStart | `session.start` |
|
|
157
|
+
| `sessionEnd` | SessionEnd | `session.end` |
|
|
158
|
+
| `stop` | Stop | `task.complete` |
|
|
159
|
+
| `postToolUseFailure` | PostToolUseFailure | `task.error` |
|
|
160
|
+
| `preCompact` | PreCompact | `resource.limit` |
|
|
161
|
+
|
|
162
|
+
Restart Cursor after installing or changing hooks. See [Cursor integration](docs/cursor.md) for details.
|
|
163
|
+
|
|
164
|
+
### Codex
|
|
165
|
+
|
|
166
|
+
Voxlert uses Codex's `notify` config so that completed agent turns call:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
voxlert codex-notify
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`voxlert setup` can install or update the `notify` entry in `~/.codex/config.toml`. See [Codex integration](docs/codex.md).
|
|
173
|
+
|
|
174
|
+
### OpenClaw
|
|
175
|
+
|
|
176
|
+
OpenClaw uses a separate plugin. See [OpenClaw integration](docs/openclaw.md) for installation, config, and troubleshooting.
|
|
177
|
+
|
|
178
|
+
## Common Commands
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
voxlert setup # Interactive setup wizard
|
|
182
|
+
voxlert voice # Interactive voice pack picker
|
|
183
|
+
voxlert pack list # List available voice packs
|
|
184
|
+
voxlert pack show # Show active pack details
|
|
185
|
+
voxlert pack use <pack-id> # Switch active voice pack
|
|
186
|
+
voxlert config # Show current configuration
|
|
187
|
+
voxlert config set <key> <val> # Set a config value
|
|
188
|
+
voxlert volume # Show or change playback volume
|
|
189
|
+
voxlert notification # Choose popup / system / off
|
|
190
|
+
voxlert test "<text>" # Run the full pipeline
|
|
191
|
+
voxlert log # Stream activity log
|
|
192
|
+
voxlert uninstall # Remove installed integrations
|
|
193
|
+
voxlert help # Show full help
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## How It Works
|
|
197
|
+
|
|
198
|
+
```mermaid
|
|
199
|
+
flowchart TD
|
|
200
|
+
A1[Claude Code Hook] --> B[voxlert.sh]
|
|
201
|
+
A2[OpenClaw Plugin] --> B
|
|
202
|
+
A3[Cursor Hook] --> B
|
|
203
|
+
A4[Codex notify] --> B
|
|
204
|
+
B --> C[src/voxlert.js]
|
|
205
|
+
C --> D{Event type?}
|
|
206
|
+
D -- "Contextual (e.g. Stop)" --> E[LLM<br><i>generate in-character phrase</i>]
|
|
207
|
+
D -- "Other events" --> F[Fallback phrases<br><i>from voice pack</i>]
|
|
208
|
+
E --> G{TTS backend?}
|
|
209
|
+
F --> G
|
|
210
|
+
G -- Chatterbox --> G1[Chatterbox TTS<br><i>local speech synthesis</i>]
|
|
211
|
+
G -- Qwen3 --> G2[Qwen3-TTS<br><i>local speech synthesis</i>]
|
|
212
|
+
G1 --> H[Audio processing<br><i>echo · normalize · post-process</i>]
|
|
213
|
+
G2 --> H
|
|
214
|
+
H --> I[(Cache<br><i>LRU, keyed by phrase + params</i>)]
|
|
215
|
+
I --> J[Playback queue<br><i>serial via file lock</i>]
|
|
216
|
+
J --> K[afplay / ffplay]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
1. A hook or notify event fires from Claude Code, Cursor, Codex, or OpenClaw.
|
|
220
|
+
2. Voxlert maps it to an event category and loads the active voice pack.
|
|
221
|
+
3. Contextual events such as task completion or tool failure can use the configured LLM to generate a short in-character phrase.
|
|
222
|
+
4. Other events use predefined fallback phrases from the pack.
|
|
223
|
+
5. The chosen phrase is synthesized by the configured TTS backend.
|
|
224
|
+
6. Audio is optionally post-processed, cached, then played through a serialized queue.
|
|
225
|
+
|
|
226
|
+
## Configuration
|
|
227
|
+
|
|
228
|
+
Run `voxlert config path` to find `config.json`. You can edit it directly or use `voxlert setup` and `voxlert config set`.
|
|
229
|
+
|
|
230
|
+
| Field | Type | Default | Description |
|
|
231
|
+
|---|---|---|---|
|
|
232
|
+
| `enabled` | boolean | `true` | Master on/off switch |
|
|
233
|
+
| `llm_backend` | string | `"openrouter"` | LLM provider: `openrouter`, `openai`, `gemini`, `anthropic`, or `local` |
|
|
234
|
+
| `llm_api_key` | string \| null | `null` | API key for the chosen LLM provider |
|
|
235
|
+
| `llm_model` | string \| null | `null` | Model ID (`null` = provider default) |
|
|
236
|
+
| `openrouter_api_key` | string \| null | `null` | Legacy alias used when `llm_backend` is `openrouter` and `llm_api_key` is empty |
|
|
237
|
+
| `openrouter_model` | string \| null | `null` | Legacy alias used when `llm_model` is empty and backend is `openrouter` |
|
|
238
|
+
| `chatterbox_url` | string | `"http://localhost:8004"` | Chatterbox TTS server URL |
|
|
239
|
+
| `tts_backend` | string | `"chatterbox"` | TTS backend: `chatterbox` or `qwen` |
|
|
240
|
+
| `active_pack` | string | `"sc2-adjutant"` | Active voice pack ID |
|
|
241
|
+
| `volume` | number | `1.0` | Playback volume (0.0-1.0) |
|
|
242
|
+
| `categories` | object | — | Per-category enable/disable settings |
|
|
243
|
+
| `logging` | boolean | `true` | Activity log in `~/.voxlert/voxlert.log` |
|
|
244
|
+
| `error_log` | boolean | `false` | Fallback/error log in `~/.voxlert/fallback.log` |
|
|
245
|
+
|
|
246
|
+
### Event categories
|
|
247
|
+
|
|
248
|
+
Event categories apply across Claude Code, Cursor, Codex, and OpenClaw where the corresponding event exists.
|
|
249
|
+
|
|
250
|
+
| Category | Hook Event | Description | Default |
|
|
251
|
+
|---|---|---|---|
|
|
252
|
+
| `session.start` | SessionStart | New session begins | on |
|
|
253
|
+
| `session.end` | SessionEnd | Session ends | on |
|
|
254
|
+
| `task.complete` | Stop | Agent finishes a task | on |
|
|
255
|
+
| `task.acknowledge` | UserPromptSubmit | User sends a prompt | off |
|
|
256
|
+
| `task.error` | PostToolUseFailure | A tool call fails | on |
|
|
257
|
+
| `input.required` | PermissionRequest | Agent needs user approval | on |
|
|
258
|
+
| `resource.limit` | PreCompact | Context window nearing limit | on |
|
|
259
|
+
| `notification` | Notification | General notification | on |
|
|
260
|
+
|
|
261
|
+
Omitted categories default to enabled. Set any category to `false` to disable it:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
voxlert config set categories.task.complete true
|
|
265
|
+
voxlert config set categories.task.acknowledge false
|
|
266
|
+
voxlert config set categories.session.start true
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Logging
|
|
270
|
+
|
|
271
|
+
- Activity logging is on by default and writes one line per event to `~/.voxlert/voxlert.log`.
|
|
272
|
+
- Error logging is off by default and records fallback situations in `~/.voxlert/fallback.log`.
|
|
273
|
+
- Debug logging for hook sources is written to `~/.voxlert/hook-debug.log`.
|
|
274
|
+
|
|
275
|
+
Useful commands:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
voxlert log
|
|
279
|
+
voxlert log on
|
|
280
|
+
voxlert log off
|
|
281
|
+
voxlert log path
|
|
282
|
+
voxlert log error on
|
|
283
|
+
voxlert log error off
|
|
284
|
+
voxlert log error-path
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
You can also manage configuration interactively with the `/voxlert-config` slash command in Claude Code.
|
|
288
|
+
|
|
289
|
+
### Integration behavior
|
|
290
|
+
|
|
291
|
+
- `voxlert setup` installs hooks for Claude Code, Cursor, and Codex.
|
|
292
|
+
- Re-run setup anytime to add a platform you skipped earlier.
|
|
293
|
+
- `voxlert uninstall` removes Claude Code, Cursor, and Codex integration.
|
|
294
|
+
- OpenClaw is managed separately through its plugin.
|
|
295
|
+
- The global `enabled` flag disables processing everywhere; there is no separate per-integration toggle in `config.json`.
|
|
296
|
+
|
|
297
|
+
## Full CLI Reference
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
voxlert setup # Interactive setup wizard (LLM, voice, TTS, hooks)
|
|
301
|
+
voxlert hook # Process hook event from stdin (Claude Code)
|
|
302
|
+
voxlert cursor-hook # Process hook event from stdin (Cursor)
|
|
303
|
+
voxlert codex-notify # Process notify payload from argv (Codex)
|
|
304
|
+
voxlert config # Show current configuration
|
|
305
|
+
voxlert config show # Show current configuration
|
|
306
|
+
voxlert config set <k> <v> # Set a config value (supports categories.X dot notation)
|
|
307
|
+
voxlert config path # Print config file path
|
|
308
|
+
voxlert log # Stream activity log (tail -f style)
|
|
309
|
+
voxlert log path # Print activity log file path
|
|
310
|
+
voxlert log error-path # Print error/fallback log file path
|
|
311
|
+
voxlert log on | off # Enable or disable activity logging
|
|
312
|
+
voxlert log error on | off # Enable or disable error logging
|
|
313
|
+
voxlert voice # Interactive voice pack picker
|
|
314
|
+
voxlert pack list # List available voice packs
|
|
315
|
+
voxlert pack show # Show active pack details
|
|
316
|
+
voxlert pack use <pack-id> # Switch active voice pack
|
|
317
|
+
voxlert volume # Show current volume and prompt for new value
|
|
318
|
+
voxlert volume <0-100> # Set playback volume (0 = mute, 100 = max)
|
|
319
|
+
voxlert notification # Choose notification style (popup / system / off)
|
|
320
|
+
voxlert test "<text>" # Run full pipeline: LLM -> TTS -> audio playback
|
|
321
|
+
voxlert cost # Show accumulated token usage and estimated cost
|
|
322
|
+
voxlert cost reset # Clear the usage log
|
|
323
|
+
voxlert uninstall # Remove hooks from Claude Code, Cursor, and Codex, optionally config/cache
|
|
324
|
+
voxlert help # Show help
|
|
325
|
+
voxlert --version # Show version
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Platform Notes
|
|
329
|
+
|
|
330
|
+
- **Windows**: Install [Node.js](https://nodejs.org) and [FFmpeg](docs/installing-ffmpeg.md). Ensure the npm global bin directory is on PATH so hooks can find `voxlert` or `voxlert.cmd`.
|
|
331
|
+
- **Linux**: Install Node and [FFmpeg](docs/installing-ffmpeg.md) so `ffplay` is on PATH.
|
|
332
|
+
- **macOS**: Playback uses the built-in `afplay`; install [SoX](docs/installing-sox.md) if you want optional effects and processing.
|
|
333
|
+
|
|
334
|
+
## Uninstall
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
voxlert uninstall
|
|
338
|
+
npm uninstall -g @settinghead/voxlert
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
This removes Voxlert hooks from Claude Code, Cursor, and Codex, the `voxlert-config` skill, and optionally your local config and cache in `~/.voxlert`.
|
|
342
|
+
|
|
343
|
+
## Advanced
|
|
344
|
+
|
|
345
|
+
See [Creating Voice Packs](docs/creating-voice-packs.md) for building your own character voice packs.
|
|
346
|
+
|
|
347
|
+
## Credits
|
|
348
|
+
|
|
349
|
+
- **Protoss Advisor** voice pack inspired by [openclaw/protoss-voice](https://playbooks.com/skills/openclaw/skills/protoss-voice)
|
|
350
|
+
|
|
351
|
+
## License
|
|
352
|
+
|
|
353
|
+
MIT - see [LICENSE](LICENSE).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/assets/logo.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"enabled": true,
|
|
3
|
+
"collect_llm_data": true,
|
|
4
|
+
"llm_backend": "openrouter",
|
|
5
|
+
"llm_api_key": null,
|
|
6
|
+
"llm_model": null,
|
|
7
|
+
"openrouter_api_key": null,
|
|
8
|
+
"openrouter_model": null,
|
|
9
|
+
"local_api": {
|
|
10
|
+
"base_url": "http://localhost:8000",
|
|
11
|
+
"model": "default",
|
|
12
|
+
"max_tokens": 50,
|
|
13
|
+
"timeout": 15000
|
|
14
|
+
},
|
|
15
|
+
"chatterbox_url": "http://localhost:8004",
|
|
16
|
+
"tts_backend": "chatterbox",
|
|
17
|
+
"qwen_tts_url": "http://localhost:8100",
|
|
18
|
+
"active_pack": "sc2-adjutant",
|
|
19
|
+
"volume": 1.0,
|
|
20
|
+
"logging": true,
|
|
21
|
+
"error_log": false,
|
|
22
|
+
"overlay": true,
|
|
23
|
+
"overlay_dismiss": 4,
|
|
24
|
+
"overlay_style": "custom",
|
|
25
|
+
"categories": {
|
|
26
|
+
"session.start": true,
|
|
27
|
+
"session.end": true,
|
|
28
|
+
"task.complete": true,
|
|
29
|
+
"task.acknowledge": false,
|
|
30
|
+
"task.error": true,
|
|
31
|
+
"input.required": true,
|
|
32
|
+
"resource.limit": true,
|
|
33
|
+
"notification": true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voxlert OpenClaw plugin.
|
|
3
|
+
*
|
|
4
|
+
* Registers agent_end lifecycle hook and spawns `voxlert hook` with
|
|
5
|
+
* Stop + source: "openclaw" when an agent run completes, so users get
|
|
6
|
+
* voice notifications (especially for long-running tasks).
|
|
7
|
+
*
|
|
8
|
+
* Requires: Voxlert installed and `voxlert` on PATH when the gateway runs.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { spawn } from "child_process";
|
|
12
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
|
|
16
|
+
const HOOK_DEBUG_LOG = join(homedir(), ".voxlert", "hook-debug.log");
|
|
17
|
+
|
|
18
|
+
function debugLog(msg, data) {
|
|
19
|
+
try {
|
|
20
|
+
mkdirSync(join(homedir(), ".voxlert"), { recursive: true });
|
|
21
|
+
const line =
|
|
22
|
+
data !== undefined
|
|
23
|
+
? `[${new Date().toISOString()}] [voxlert-plugin] ${msg} ${JSON.stringify(data)}\n`
|
|
24
|
+
: `[${new Date().toISOString()}] [voxlert-plugin] ${msg}\n`;
|
|
25
|
+
appendFileSync(HOOK_DEBUG_LOG, line);
|
|
26
|
+
} catch {
|
|
27
|
+
// best-effort
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default function register(api) {
|
|
32
|
+
api.on(
|
|
33
|
+
"agent_end",
|
|
34
|
+
async (event, ctx) => {
|
|
35
|
+
const config = api.config?.plugins?.entries?.voxlert?.config ?? {};
|
|
36
|
+
if (config.enabled === false) return;
|
|
37
|
+
|
|
38
|
+
const minDurationSeconds = Number(config.minDurationSeconds) || 0;
|
|
39
|
+
const startedAt = event?.startedAt ?? event?.context?.startedAt;
|
|
40
|
+
const endedAt = event?.endedAt ?? event?.context?.endedAt;
|
|
41
|
+
if (
|
|
42
|
+
minDurationSeconds > 0 &&
|
|
43
|
+
typeof startedAt === "number" &&
|
|
44
|
+
typeof endedAt === "number"
|
|
45
|
+
) {
|
|
46
|
+
const durationMs = endedAt - startedAt;
|
|
47
|
+
if (durationMs < minDurationSeconds * 1000) return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let lastAssistantMessage = "";
|
|
51
|
+
const messages = event?.messages ?? event?.context?.messages ?? [];
|
|
52
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
53
|
+
const last = messages[messages.length - 1];
|
|
54
|
+
if (last?.content ?? last?.text) {
|
|
55
|
+
lastAssistantMessage = String(last.content ?? last.text).slice(0, 2000);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!lastAssistantMessage && event?.context?.sessionEntry) {
|
|
59
|
+
lastAssistantMessage = String(event.context.sessionEntry).slice(0, 2000);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const cwd =
|
|
63
|
+
event?.context?.workspaceDir ??
|
|
64
|
+
event?.context?.workspace ??
|
|
65
|
+
ctx?.workspaceDir ??
|
|
66
|
+
ctx?.workspace ??
|
|
67
|
+
"";
|
|
68
|
+
|
|
69
|
+
const payload = {
|
|
70
|
+
hook_event_name: "Stop",
|
|
71
|
+
source: "openclaw",
|
|
72
|
+
cwd: typeof cwd === "string" ? cwd : "",
|
|
73
|
+
last_assistant_message: lastAssistantMessage || undefined,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
debugLog("agent_end: spawning voxlert hook", payload);
|
|
78
|
+
} catch {
|
|
79
|
+
// ignore
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const child = spawn("voxlert", ["hook"], {
|
|
83
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
84
|
+
detached: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
child.on("error", (err) => {
|
|
88
|
+
debugLog("voxlert spawn error", {
|
|
89
|
+
message: err.message,
|
|
90
|
+
code: err.code,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
child.stdin.write(JSON.stringify(payload));
|
|
95
|
+
child.stdin.end();
|
|
96
|
+
child.unref();
|
|
97
|
+
},
|
|
98
|
+
{ priority: 0 }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "voxlert",
|
|
3
|
+
"name": "Voxlert Notify",
|
|
4
|
+
"description": "Speak when OpenClaw agent runs complete (e.g. long-running tasks) using Voxlert character voices.",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"minDurationSeconds": {
|
|
10
|
+
"type": "number",
|
|
11
|
+
"default": 0,
|
|
12
|
+
"description": "Only notify for runs lasting at least this many seconds (0 = always notify)"
|
|
13
|
+
},
|
|
14
|
+
"enabled": {
|
|
15
|
+
"type": "boolean",
|
|
16
|
+
"default": true,
|
|
17
|
+
"description": "Master switch for Voxlert notifications"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@settinghead/voxlert",
|
|
3
|
+
"version": "0.3.5",
|
|
4
|
+
"description": "LLM-generated voice notifications for Claude Code, Cursor, OpenAI Codex, and OpenClaw, spoken by game characters like the StarCraft Adjutant, Kerrigan, C&C EVA, SHODAN, and more.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/settinghead/voxlert.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/settinghead/voxlert",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"bin": {
|
|
12
|
+
"voxlert": "src/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src/",
|
|
16
|
+
"packs/*/pack.json",
|
|
17
|
+
"packs/*/voice.wav",
|
|
18
|
+
"skills/",
|
|
19
|
+
"assets/*.png",
|
|
20
|
+
"assets/*.jpg",
|
|
21
|
+
"assets/*.gif",
|
|
22
|
+
"config.default.json",
|
|
23
|
+
"voxlert.sh",
|
|
24
|
+
"openclaw-plugin/",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"start": "node src/voxlert.js",
|
|
29
|
+
"postinstall": "node src/postinstall.js",
|
|
30
|
+
"changeset": "changeset",
|
|
31
|
+
"version": "changeset version",
|
|
32
|
+
"release": "changeset publish",
|
|
33
|
+
"test": "node --test ./test/*.test.js"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"provenance": true
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@inquirer/checkbox": "^5.0.0",
|
|
42
|
+
"@inquirer/confirm": "^6.0.8",
|
|
43
|
+
"@inquirer/input": "^4.1.0",
|
|
44
|
+
"@inquirer/select": "^5.1.0",
|
|
45
|
+
"node-notifier": "^10.0.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
49
|
+
"@changesets/cli": "^2.30.0"
|
|
50
|
+
}
|
|
51
|
+
}
|