vibeaudio 0.4.0 โ 0.6.1
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 +140 -49
- package/package.json +7 -5
- package/scripts/postinstall.js +33 -0
- package/src/cli.js +286 -19
- package/src/hooks.js +217 -93
- package/src/interactive.js +56 -4
- package/src/mcp.js +9 -3
- package/src/player.js +102 -2
- package/src/synth/electronic.js +7 -2
- package/src/synth/generator.js +27 -0
- package/src/synth/synthwave.js +3 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
> **๐ [Listen to every genre โ](https://kiril6.github.io/vibeaudio/)** โ hear all 8 genres, the tier escalation, and the three chimes, rendered from the real synth.
|
|
12
12
|
|
|
13
|
-
> **In a hurry?** `npm i -g
|
|
13
|
+
> **In a hurry?** `npm i -g vibeaudio`, then `vibe --install-hooks`. Your next prompt has music.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -22,20 +22,25 @@ AI coding agents take 15โ45 seconds to reason, read files and write code. Star
|
|
|
22
22
|
|
|
23
23
|
* ๐งฎ **Pure synthesis, zero MP3s.** Every note, chord and pad is generated in code โ no audio assets, no npm dependencies, no `node-gyp`.
|
|
24
24
|
* ๐ผ **A different arrangement per project.** Your working directory seeds the progression, bass line and melody, so each repo has its own sound and keeps it.
|
|
25
|
-
* ๐ก๏ธ **A grace window.** Fast commands stay 100% silent โ music starts only past 1.5s (`--grace`).
|
|
26
25
|
* ๐ **Escalating layers.** Tier 1 (0โ15s) gentle intro โ Tier 2 (15โ45s) main groove โ Tier 3 (45s+) deep focus. You can hear how deep into the task the agent is.
|
|
27
26
|
* ๐ **Outcome-aware chimes.** Ascending on success, a soft descending minor chord on failure, and **silence on `Ctrl+C`** โ an abort is never reported as done.
|
|
28
27
|
* โ **A "your turn" chime.** When Claude Code stops to ask permission (or an MCP server asks for input), the music pauses and a rising two-note chime asks for you; it picks back up once you've answered.
|
|
28
|
+
* ๐ **Universal drop-in.** Hooks for **Claude Code, Codex, Cursor, Grok, Gemini CLI, Copilot CLI and Qwen Code**; MCP for **Claude Desktop and Antigravity**; the wrapper (`vibe <command>`) for anything else.
|
|
29
|
+
|
|
30
|
+
<details>
|
|
31
|
+
<summary><b>And the quieter four</b> โ silence on fast commands, several sessions at once, exit codes, the HUD</summary>
|
|
32
|
+
|
|
33
|
+
* ๐ก๏ธ **A grace window.** Fast commands stay 100% silent โ music starts only past 1.5s (`--grace`).
|
|
34
|
+
* ๐ช **Several sessions, one soundtrack.** Run as many terminals of the same agent as you like: the music plays while *any* of them is working, and each finishes with its own chime. One session ending, pausing for a permission dialog or being interrupted never cuts off another that's still going.
|
|
29
35
|
* ๐งฎ **Honest exit codes.** Your command's status passes straight through (`130` on `Ctrl+C`), so `vibe claude && next-step` behaves exactly as it would without the wrapper.
|
|
30
36
|
* ๐ **Terminal title HUD.** A live ASCII wave and elapsed timer in the window title, where it can't corrupt a full-screen TUI.
|
|
31
|
-
|
|
37
|
+
|
|
38
|
+
</details>
|
|
32
39
|
|
|
33
40
|
### ๐ผ Every project gets its own arrangement
|
|
34
41
|
|
|
35
42
|
VibeAudio seeds the composition from your **project directory**. The repo you're in picks the chord progression, the bass line, the melodic contour, and where the ornaments land โ so `~/work/api` and `~/side/game` genuinely sound different, while each one sounds the *same every time you come back to it*.
|
|
36
43
|
|
|
37
|
-
That's a deliberate choice. Focus music has one job: **be ignorable.** Music that reinvents itself every run keeps pulling your ear back, which is the opposite of what you want while reading an agent's output. Familiar-per-project gives you variety across contexts and predictability within one โ by your third session in a repo, its loop has faded into the furniture.
|
|
38
|
-
|
|
39
44
|
Pin or explore arrangements when you want to:
|
|
40
45
|
|
|
41
46
|
```bash
|
|
@@ -43,8 +48,15 @@ vibe --seed 42 --preview jazz # audition one specific arrangement
|
|
|
43
48
|
export VIBE_SEED=7 # pin the same sound everywhere
|
|
44
49
|
```
|
|
45
50
|
|
|
51
|
+
<details>
|
|
52
|
+
<summary><b>Why the same sound every time, rather than something new โ and what "procedural" actually means</b></summary>
|
|
53
|
+
|
|
54
|
+
That's a deliberate choice. Focus music has one job: **be ignorable.** Music that reinvents itself every run keeps pulling your ear back, which is the opposite of what you want while reading an agent's output. Familiar-per-project gives you variety across contexts and predictability within one โ by your third session in a repo, its loop has faded into the furniture.
|
|
55
|
+
|
|
46
56
|
**How "procedural" actually works** โ worth being precise, since it shapes what you'll hear. Each genre is **synthesized from code** (oscillators, chord tables, envelopes rendered to PCM), not shipped as audio files. The seed selects among *hand-written, human-checked* variants โ three progressions per genre, each diatonic to that genre's key, plus seeded ornament placement โ so it never invents harmony and can't wander out of key. The chosen arrangement is rendered once per project, cached under `~/.vibeaudio/cache/`, and looped. Within a session you're hearing a 6โ8 second bar repeat that gains layers as you cross the tier boundaries.
|
|
47
57
|
|
|
58
|
+
</details>
|
|
59
|
+
|
|
48
60
|
---
|
|
49
61
|
|
|
50
62
|
## ๐ Requirements
|
|
@@ -69,13 +81,11 @@ If no player is found, VibeAudio prints a one-line notice and runs your command
|
|
|
69
81
|
One line. No clone, no build step, no dependencies to resolve:
|
|
70
82
|
|
|
71
83
|
```bash
|
|
72
|
-
npm i -g
|
|
84
|
+
npm i -g vibeaudio
|
|
73
85
|
```
|
|
74
86
|
|
|
75
87
|
That puts `vibe` and `vibeaudio` on your `PATH`. Re-run the same command to update, or see [Uninstall](#-uninstall) to remove it cleanly.
|
|
76
88
|
|
|
77
|
-
> **Note:** VibeAudio isn't on the npm registry yet, so plain `npx vibeaudio` won't resolve โ use the `github:` form above.
|
|
78
|
-
|
|
79
89
|
### Then pick how it runs
|
|
80
90
|
|
|
81
91
|
**Using Claude Code, Codex, Cursor or Grok interactively?** Install the hooks โ this is the mode that actually tracks thinking:
|
|
@@ -100,8 +110,8 @@ vibe claude -p "explain this repo"
|
|
|
100
110
|
### Try it without installing
|
|
101
111
|
|
|
102
112
|
```bash
|
|
103
|
-
npx
|
|
104
|
-
npx
|
|
113
|
+
npx vibeaudio --preview jazz # hear a loop right now
|
|
114
|
+
npx vibeaudio sleep 8 # hear the wrapper: music, then the done chime
|
|
105
115
|
```
|
|
106
116
|
|
|
107
117
|
> **Heard nothing?** Music only starts once the wrapped command has run longer than the 1.5s grace window โ that's [the point](#-why), so quick commands stay silent. `sleep 8` is the reliable demo; something like `npm test` is silent if the tests finish fast or the command errors out immediately.
|
|
@@ -148,6 +158,13 @@ Choose your volume level:
|
|
|
148
158
|
4. ๐ข Loud (75%) โ Audible across the room
|
|
149
159
|
```
|
|
150
160
|
|
|
161
|
+
**Press `p` to hear the highlighted genre** โ auditioning is just a keypress, and moving on replaces it.
|
|
162
|
+
|
|
163
|
+
Installed tools sort to the top, and the list is a shortcut rather than a compatibility list: **`vibe` wraps any command at all**, and "Custom command..." takes one you type.
|
|
164
|
+
|
|
165
|
+
<details>
|
|
166
|
+
<summary><b>Picking a hook-capable agent asks two more questions</b></summary>
|
|
167
|
+
|
|
151
168
|
Pick **Claude Code** or **Codex** and it asks how the music should run โ hooks or just this session โ and the hooks branch then offers [reactive mode](#reactive-mode-opt-in) too:
|
|
152
169
|
|
|
153
170
|
```
|
|
@@ -160,9 +177,11 @@ Should the music react to what the agent is doing?
|
|
|
160
177
|
2. Reactive โ Intensity follows the tool in use - noticeable, by design
|
|
161
178
|
```
|
|
162
179
|
|
|
163
|
-
Choosing hooks installs them for the tool you picked, then launches it โ the same thing `vibe --genre <g> --volume <n> --reactive --install-hooks` does, without memorising flags. Once they're installed the first question becomes **Just launch it** / **Reconfigure the hooks**, and launching skips the genre and volume prompts, since
|
|
180
|
+
Choosing hooks installs them for the tool you picked, then launches it โ the same thing `vibe --genre <g> --volume <n> --reactive --install-hooks` does, without memorising flags. Once they're installed the first question becomes **Just launch it** / **Reconfigure the hooks**, and launching skips the genre and volume prompts, since your saved default already answers those (`vibe --genre zen` changes it any time).
|
|
164
181
|
|
|
165
|
-
|
|
182
|
+
Adding an entry to the launcher is one line in [`src/interactive.js`](src/interactive.js).
|
|
183
|
+
|
|
184
|
+
</details>
|
|
166
185
|
|
|
167
186
|
---
|
|
168
187
|
|
|
@@ -177,12 +196,17 @@ Once hooks are installed they take over: running `vibe claude` (or `vibe codex`)
|
|
|
177
196
|
```bash
|
|
178
197
|
vibe --install-hooks # every agent found on this machine
|
|
179
198
|
vibe --install-hooks --tools codex # or just one of them
|
|
180
|
-
vibe --genre jazz --volume 25 --install-hooks #
|
|
199
|
+
vibe --genre jazz --volume 25 --install-hooks # install, and save these as your default
|
|
181
200
|
vibe --install-hooks --dry-run # show what would change, write nothing
|
|
182
201
|
```
|
|
183
202
|
|
|
184
203
|
**It auto-detects.** With no `--tools`, VibeAudio wires up each supported agent it finds on your machine โ one counts as present when its config directory exists or its CLI is on your `PATH`. `--tools claude,codex,cursor,grok,gemini,copilot,qwen` overrides that. Add `--dry-run` to see, per event, what would be added or changed in each file before anything is written.
|
|
185
204
|
|
|
205
|
+
That's it โ run your agent normally, with no `vibe` prefix.
|
|
206
|
+
|
|
207
|
+
<details>
|
|
208
|
+
<summary><b>Which file and which events, per agent</b> โ seven dialects, all written for you</summary>
|
|
209
|
+
|
|
186
210
|
Each tool spells its events its own way, and VibeAudio writes whichever dialect the file expects:
|
|
187
211
|
|
|
188
212
|
| | File | Music starts | Music stops + chime | Reactive (opt-in) |
|
|
@@ -206,9 +230,11 @@ Where an agent reports more than start and stop, VibeAudio listens for that too
|
|
|
206
230
|
| **Qwen Code** | `PermissionRequest` | `PostToolUse`, `PostToolUseFailure` | `StopFailure` | `SessionEnd` |
|
|
207
231
|
| **Cursor, Grok** | โ | โ | โ | โ |
|
|
208
232
|
|
|
209
|
-
|
|
233
|
+
</details>
|
|
234
|
+
|
|
235
|
+
<details>
|
|
236
|
+
<summary><b>Six things that differ per agent</b> โ only the first one needs anything from you</summary>
|
|
210
237
|
|
|
211
|
-
Six things differ per agent, and none of them need any action from you except the first:
|
|
212
238
|
|
|
213
239
|
| | |
|
|
214
240
|
| :--- | :--- |
|
|
@@ -218,12 +244,32 @@ Six things differ per agent, and none of them need any action from you except th
|
|
|
218
244
|
| **Only Cursor can play the failure chime** | Its stop event reports whether the turn completed, aborted or errored. The others send no verdict, so a turn there always ends on the success chime โ VibeAudio won't invent a failure the agent never claimed. |
|
|
219
245
|
| **Grok and Copilot CLI get a file of their own** | Each reads every `*.json` in its `hooks/` directory, so VibeAudio writes `vibeaudio.json` rather than merging into anyone else's โ which makes uninstalling it a delete, and leaves no backup file behind. Copilot's honours `COPILOT_HOME`. |
|
|
220
246
|
|
|
221
|
-
>
|
|
247
|
+
</details>
|
|
248
|
+
|
|
249
|
+
### Several sessions at once
|
|
250
|
+
|
|
251
|
+
Every session of an agent is tracked separately, by the session id in its hook payload, so two terminals (or a terminal and the desktop app) share one soundtrack instead of fighting over it:
|
|
252
|
+
|
|
253
|
+
* **The music plays while any session is working.** A second prompt doesn't restart it, and it stops only when the last working session finishes.
|
|
254
|
+
* **Every session gets its own chime.** A quick question that finishes while another agent is still busy chimes "done" and the music carries on underneath.
|
|
255
|
+
|
|
256
|
+
<details>
|
|
257
|
+
<summary><b>The rest of how sessions share one stream</b></summary>
|
|
258
|
+
|
|
259
|
+
* **More sessions, more music.** Two sessions working at once plays at least tier 2 and three or more plays tier 3 โ the same piece with more layers, arriving at the next loop boundary and easing off as sessions finish. It only ever raises the tier, so it works alongside time escalation and [reactive mode](#reactive-mode-opt-in).
|
|
260
|
+
* **Dialogs and Esc are per session.** A permission dialog in one terminal plays the "your turn" chime but only pauses the music once *every* session is waiting; Esc ends just that session's turn.
|
|
261
|
+
* **Crashed agents can't hold it hostage.** A session that never sent `Stop` is dropped after 15 minutes, the same ceiling the background player stops at.
|
|
222
262
|
|
|
223
|
-
|
|
263
|
+
Sessions with no id in their payload share a single slot, so they behave as one. VibeAudio keeps one stream: per-session genres or several streams mixed together aren't supported.
|
|
264
|
+
|
|
265
|
+
</details>
|
|
266
|
+
|
|
267
|
+
> **No restart needed, even mid-session โ for Claude Code, Codex, Cursor and Grok.** Each re-reads its hook file every time a hook fires, so changes land on your **next prompt**. A daemon already playing keeps its old settings until that prompt replaces it โ at most the tail of one turn. Whether an open Gemini CLI, Copilot CLI or Qwen Code session does the same hasn't been checked, so start a new session there to be sure.
|
|
224
268
|
|
|
225
269
|
> **The Claude Code desktop app is covered too**, not just the terminal โ both read the same `~/.claude/settings.json`. (The separate **Claude Desktop** chat app is a different product with no hooks; that one needs [MCP](#-everything-else-claude-desktop-antigravity-via-mcp).)
|
|
226
270
|
|
|
271
|
+
> **One player is shared.** Prompt two agents at once and the last prompt owns the music. One person, one set of speakers โ deliberate, not a limitation being worked around.
|
|
272
|
+
|
|
227
273
|
### `/vibe` inside Claude Code
|
|
228
274
|
|
|
229
275
|
Installing the Claude Code hooks also adds a `/vibe` command (`~/.claude/commands/vibe.md`), so you can control the music without leaving the session:
|
|
@@ -240,23 +286,32 @@ It asks Claude to run the matching `vibe` command, so Claude Code will ask permi
|
|
|
240
286
|
|
|
241
287
|
### Changing the sound later
|
|
242
288
|
|
|
243
|
-
|
|
289
|
+
One command, and it reaches everything:
|
|
244
290
|
|
|
245
291
|
```bash
|
|
246
|
-
vibe --genre electronic --volume 25
|
|
292
|
+
vibe --genre electronic --volume 25
|
|
247
293
|
```
|
|
248
294
|
|
|
249
|
-
|
|
295
|
+
That saves your default to `~/.vibeaudio/config.json`. **Installed hooks read it on their next prompt** โ nothing to reinstall, nothing to restart. The same file is what the wrapper and the MCP server use, so there is one answer to "what genre am I on" rather than three. `vibe --status` shows it.
|
|
250
296
|
|
|
251
|
-
|
|
297
|
+
Audition before you commit: `vibe --preview electronic`.
|
|
298
|
+
|
|
299
|
+
A flag still beats an environment variable, which still beats the saved file โ so a one-off stays a one-off:
|
|
252
300
|
|
|
253
301
|
```bash
|
|
254
|
-
vibe --genre
|
|
302
|
+
vibe --genre 8bit npm test # this run only, nothing saved
|
|
255
303
|
```
|
|
256
304
|
|
|
257
|
-
|
|
305
|
+
**Coming from an older install?** Its genre and volume were frozen into the hook entries. The first `vibe --install-hooks` after upgrading carries them over into the config file, so nothing about your music changes โ unless you name a new genre or volume, or have already saved one.
|
|
306
|
+
|
|
307
|
+
[Reactive mode](#reactive-mode-opt-in) is the exception, because it isn't a setting the music reads โ it decides which hooks exist at all. Turning it on or off means an install:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
vibe --reactive --install-hooks # on
|
|
311
|
+
vibe --install-hooks # off again
|
|
312
|
+
```
|
|
258
313
|
|
|
259
|
-
|
|
314
|
+
Music already playing keeps the old genre until the next prompt swaps the daemon โ `vibe --stop` cuts it short.
|
|
260
315
|
|
|
261
316
|
Removing them is one command:
|
|
262
317
|
|
|
@@ -282,9 +337,10 @@ Each agent names its tools differently, and all the vocabularies are mapped. **M
|
|
|
282
337
|
|
|
283
338
|
Changes land at the next loop boundary, so it shifts musically rather than cutting mid-bar. Without a tool signal it falls back to time-based escalation.
|
|
284
339
|
|
|
285
|
-
**
|
|
340
|
+
**Off by default on purpose** โ music that moves every time the agent switches tools is music you *notice*, which is the opposite of what focus audio is for. Try it; if you catch yourself listening instead of reading, reinstall without `--reactive` and the `PreToolUse` hook goes away again.
|
|
286
341
|
|
|
287
|
-
|
|
342
|
+
<details>
|
|
343
|
+
<summary><b>What the installer will and won't do to your config</b> โ it edits a file you own, so here is the whole of it</summary>
|
|
288
344
|
|
|
289
345
|
* **Merges, never replaces.** Other tools' hooks are left untouched and keep their position in the file โ which is what Codex keys its trust records by.
|
|
290
346
|
* **Backs up first, once.** The first install copies your file alongside as `*.vibeaudio.bak`. Later installs leave it alone โ re-copying would overwrite your real pre-VibeAudio config with a copy of VibeAudio's own last install.
|
|
@@ -292,13 +348,15 @@ Changes land at the next loop boundary, so it shifts musically rather than cutti
|
|
|
292
348
|
* **Refuses rather than clobbers.** Malformed JSON aborts the write; a temporary `npx` checkout is rejected outright, since the hook records an absolute path that npm's cache eviction would later delete.
|
|
293
349
|
* **Can't run away.** The background player is capped at 15 minutes, so a missed stop event can't leave music looping.
|
|
294
350
|
|
|
351
|
+
</details>
|
|
352
|
+
|
|
295
353
|
---
|
|
296
354
|
|
|
297
355
|
## ๐ฅ๏ธ Everything Else (Claude Desktop, Antigravityโฆ via MCP)
|
|
298
356
|
|
|
299
357
|
**Claude Code, [Codex](https://github.com/openai/codex), [Cursor](https://cursor.com/docs/hooks), [Grok](https://docs.x.ai/build/features/hooks), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Copilot CLI](https://docs.github.com/en/copilot/reference/hooks-configuration) and [Qwen Code](https://github.com/QwenLM/qwen-code) have hook systems, and `--install-hooks` writes to all seven** โ use [hooks](#-agent-hooks-no-wrapper-needed) there, they're strictly better. This section is for everything else. MCP is the way in: it's plain stdio JSON-RPC, so the setup is identical everywhere and only the config file differs.
|
|
300
358
|
|
|
301
|
-
> **This is weaker than hooks, by nature.** Hooks fire on an event
|
|
359
|
+
> **This is weaker than hooks, by nature.** Hooks fire on an event; MCP tools are *model-invoked*, so the assistant has to decide to call `vibe_play` and remember `vibe_stop`. Expect the occasional silent turn โ say "play some focus music while you work on this" if you want it reliably. **On any of the seven agents above, use [hooks](#-agent-hooks-no-wrapper-needed) instead.**
|
|
302
360
|
|
|
303
361
|
Use an **absolute path**, not the bare `vibe` command: GUI apps launched from Finder don't inherit your shell's `PATH`, and version managers like `fnm` or `nvm` put `vibe` on a per-shell path that won't resolve. Print yours with:
|
|
304
362
|
|
|
@@ -326,6 +384,11 @@ Where the file lives:
|
|
|
326
384
|
| **Claude Desktop** (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
327
385
|
| **Antigravity, VS Code, Zed, โฆ** | that app's own MCP settings โ same JSON shape |
|
|
328
386
|
|
|
387
|
+
Restart the app afterwards.
|
|
388
|
+
|
|
389
|
+
<details>
|
|
390
|
+
<summary><b>Codex's TOML, and older Gemini CLI releases</b></summary>
|
|
391
|
+
|
|
329
392
|
Codex uses TOML rather than JSON, in `~/.codex/config.toml` โ but it has hooks, so [use those instead](#-agent-hooks-no-wrapper-needed) unless you specifically want model-invoked music:
|
|
330
393
|
|
|
331
394
|
```toml
|
|
@@ -334,15 +397,17 @@ command = "node"
|
|
|
334
397
|
args = ["/absolute/path/from/the/command/above", "--mcp"]
|
|
335
398
|
```
|
|
336
399
|
|
|
337
|
-
|
|
400
|
+
Older Gemini CLI releases predate its hook system (0.10.0 has none) โ if you're on one and can't upgrade, the same JSON goes in `~/.gemini/settings.json`.
|
|
401
|
+
|
|
402
|
+
</details>
|
|
338
403
|
|
|
339
404
|
### Changing the genre here
|
|
340
405
|
|
|
341
|
-
`vibe --genre`
|
|
406
|
+
**Set it like anywhere else.** `vibe --genre jazz --volume 25` saves the default the server reads, and it reads it per request โ so the next `vibe_play` picks it up with no restart and no config edit, even though the app launched the server and you didn't.
|
|
342
407
|
|
|
343
|
-
**
|
|
408
|
+
**Or just ask.** `vibe_play` takes a genre, so "play some jazz while you work on this" is enough. A genre the assistant passes wins over the saved default.
|
|
344
409
|
|
|
345
|
-
**Or
|
|
410
|
+
**Or pin one in the config's `env` block**, if you want this client to differ from the rest of your machine โ a GUI app won't inherit `VIBE_GENRE` from your shell. Most clients support `env` alongside `command`/`args`:
|
|
346
411
|
|
|
347
412
|
```json
|
|
348
413
|
"env": { "VIBE_GENRE": "jazz", "VIBE_VOLUME": "25" }
|
|
@@ -354,7 +419,7 @@ VIBE_GENRE = "jazz"
|
|
|
354
419
|
VIBE_VOLUME = "25"
|
|
355
420
|
```
|
|
356
421
|
|
|
357
|
-
Restart the app afterwards
|
|
422
|
+
Restart the app afterwards โ this one is read from the environment the server was launched with, so unlike the saved default it can't change under a running client.
|
|
358
423
|
|
|
359
424
|
#### Exposed MCP Tools:
|
|
360
425
|
* `vibe_play`: Start procedural focus music (`genre`: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random`; `volume`: `5-100`).
|
|
@@ -399,28 +464,43 @@ vibe --genre random claude # Surprise vibe each run
|
|
|
399
464
|
|
|
400
465
|
### โ๏ธ Set Your Favorite Genre as Default
|
|
401
466
|
|
|
402
|
-
|
|
467
|
+
```bash
|
|
468
|
+
vibe --genre jazz --volume 25
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Saved to `~/.vibeaudio/config.json` and read by all three ways of running VibeAudio โ wrapper, agent hooks, MCP. It applies on your next prompt.
|
|
403
472
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
473
|
+
```bash
|
|
474
|
+
vibe --genre jazz # lofi, synthwave, 8bit, electronic, jazz, zen, piano, drone, random
|
|
475
|
+
vibe --volume 25 # or --whisper / --quiet / --loud
|
|
476
|
+
vibe --chime-volume 70
|
|
477
|
+
vibe --status # what's saved, and what's overriding it
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
An `export VIBE_GENRE=jazz` still works and takes precedence in that shell โ useful for one terminal you want different, not needed for a default any more.
|
|
481
|
+
|
|
482
|
+
**One project that should sound different:**
|
|
409
483
|
|
|
410
484
|
```bash
|
|
411
|
-
|
|
485
|
+
cd ~/work/api
|
|
486
|
+
vibe --genre zen --here # this directory and everything under it
|
|
412
487
|
```
|
|
413
488
|
|
|
489
|
+
Your global default is untouched, and an agent launched from a subdirectory still gets it โ the lookup walks up, so `~/work/api/src` finds what you saved at `~/work/api`. `vibe --status` says which one answered (`saved for this project` / `saved` / `default`).
|
|
490
|
+
|
|
491
|
+
The full order, highest first: **a flag** โ **an environment variable** โ **`--here`** โ **your global default** โ `lofi` at 40%.
|
|
492
|
+
|
|
414
493
|
---
|
|
415
494
|
|
|
416
495
|
## โ๏ธ Options & Flags
|
|
417
496
|
|
|
418
497
|
| Flag | Description | Default |
|
|
419
498
|
| :--- | :--- | :--- |
|
|
420
|
-
| `-g, --genre <name>` | Music style: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random
|
|
421
|
-
| `-v, --volume <5-100>` | Set playback volume | `40` |
|
|
499
|
+
| `-g, --genre <name>` | Music style: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random`. With no command after it, saves your default | `lofi` |
|
|
500
|
+
| `-v, --volume <5-100>` | Set playback volume. With no command after it, saves your default | `40` |
|
|
422
501
|
| `-cv, --chime-volume <5-100>` | Set independent completion chime volume | `volume ร 1.1`, kept within 35โ65 |
|
|
423
502
|
| `--grace <ms>` | Silence window before music starts | `1500` |
|
|
503
|
+
| `--here` | With a saved setting: this directory tree only, not everywhere | off |
|
|
424
504
|
| `--seed <n>` | Force a specific arrangement | derived from the project directory |
|
|
425
505
|
| `--whisper` | Quick preset: 15% volume (headphones / late night) | โ |
|
|
426
506
|
| `--quiet` | Quick preset: 25% volume (focus / open office) | โ |
|
|
@@ -443,7 +523,8 @@ export VIBE_GENRE=jazz # or synthwave, 8bit, electronic, zen, piano, drone,
|
|
|
443
523
|
| `--version` | Show version | โ |
|
|
444
524
|
|
|
445
525
|
### โ๏ธ Environment Variables
|
|
446
|
-
|
|
526
|
+
|
|
527
|
+
**Defaults live in `~/.vibeaudio/config.json` now** โ `vibe --genre jazz` is the short way to set one. These override it for a single shell, which is what you want for one terminal that should sound different, or for a machine you don't want writing config at all:
|
|
447
528
|
|
|
448
529
|
```bash
|
|
449
530
|
export VIBE_GENRE=jazz # lofi, synthwave, 8bit, electronic, jazz, zen, piano, drone, random
|
|
@@ -456,7 +537,7 @@ export VIBE_DISABLE=1 # Mute, without uninstalling anything
|
|
|
456
537
|
|
|
457
538
|
**`VIBE_DISABLE=1` is for a shell you always want quiet** โ a CI job, a shared machine, a terminal profile you keep silent. It's read at playback time and covers hooks, wrapper and MCP alike.
|
|
458
539
|
|
|
459
|
-
**For a call that's ringing right now, use `vibe --mute` instead
|
|
540
|
+
**For a call that's ringing right now, use `vibe --mute` instead** โ an exported variable can't reach a hook that's already running.
|
|
460
541
|
|
|
461
542
|
```bash
|
|
462
543
|
vibe --mute # silent now, music returns by itself in an hour
|
|
@@ -465,11 +546,16 @@ vibe --mute 0 # stay off until I say otherwise
|
|
|
465
546
|
vibe --unmute # end it early
|
|
466
547
|
```
|
|
467
548
|
|
|
468
|
-
|
|
549
|
+
It stops whatever is playing on the spot, leaves your hooks and settings untouched, and **expires after an hour** so you can't forget it. `--preview` still plays either way โ that one is an explicit request to hear something.
|
|
550
|
+
|
|
551
|
+
<details>
|
|
552
|
+
<summary><b>Why a file, and why it expires</b></summary>
|
|
469
553
|
|
|
470
|
-
|
|
554
|
+
A hook runs as a child of your agent and inherits the environment the agent had *when it launched*, so exporting `VIBE_DISABLE` in another terminal reaches nothing already running โ and restarting your agent is exactly what you can't do mid-call. `--mute` writes a flag file, which crosses that boundary where a variable cannot.
|
|
471
555
|
|
|
472
|
-
|
|
556
|
+
The expiry is deliberate too. A call is a bounded thing; a mute you forget about is worse than no mute, because the tool just stops working and nothing ever tells you why. Expiring means the worst case is "music came back sooner than I wanted" rather than a week of silence you never diagnose. `--mute 0` opts into indefinite explicitly, and `vibe --status` always shows how much of the window is left.
|
|
557
|
+
|
|
558
|
+
</details>
|
|
473
559
|
|
|
474
560
|
---
|
|
475
561
|
|
|
@@ -511,7 +597,7 @@ This needs a PulseAudio-compatible sound server on **your local machine**: a Lin
|
|
|
511
597
|
If the second connection fails with the socket "already in use", an earlier session left it behind: `rm /tmp/vibe-pulse.sock` on the server, or set `StreamLocalBindUnlink yes` in the server's `sshd_config`. If `paplay` says access denied, your local PulseAudio requires its cookie โ copy `~/.config/pulse/cookie` to the same path on the server.
|
|
512
598
|
|
|
513
599
|
**Volume flag does nothing**
|
|
514
|
-
Shouldn't happen any more โ where the player can't attenuate (`aplay`, PowerShell), the gain is baked into the audio instead.
|
|
600
|
+
Shouldn't happen any more โ where the player can't attenuate (`aplay`, PowerShell), the gain is baked into the audio instead. A `--volume` change lands at the next loop boundary, and on hooks at your next prompt; `vibe --status` shows the volume in effect and what set it.
|
|
515
601
|
|
|
516
602
|
---
|
|
517
603
|
|
|
@@ -562,14 +648,17 @@ The last block is detected from your own `PATH`, so it answers "will this work w
|
|
|
562
648
|
```bash
|
|
563
649
|
vibe --uninstall-hooks # 1. unwire every agent, and stop any player still running
|
|
564
650
|
npm rm -g vibeaudio # 2. remove the CLI
|
|
565
|
-
rm -rf ~/.vibeaudio # 3. optional: cached audio
|
|
651
|
+
rm -rf ~/.vibeaudio # 3. optional: cached audio, saved settings, daemon state
|
|
566
652
|
```
|
|
567
653
|
|
|
654
|
+
<details>
|
|
655
|
+
<summary><b>Why that order, and what stays behind</b></summary>
|
|
656
|
+
|
|
568
657
|
Step 1 also **stops a background player that's still going**. That matters: once the hooks are gone nothing will ever send the `Stop` event, and after step 2 there's no `vibe` left to stop it with โ music would simply play on until its 15-minute cap. If you ever need to do it by hand: `pkill -f "vibeaudio.js --daemon"`.
|
|
569
658
|
|
|
570
659
|
> **Order matters.** `npm rm -g` deletes the binary but not your hook config. Removing the package first strands hook entries that point at a path that no longer exists, and your agent will run a failing hook on every prompt. If you already did it in the wrong order, reinstall, run `vibe --uninstall-hooks`, then remove again โ or delete the `vibeaudio` entries from `~/.claude/settings.json`, `~/.codex/hooks.json`, `~/.cursor/hooks.json`, `~/.gemini/settings.json` and `~/.qwen/settings.json` by hand, and delete `~/.grok/hooks/vibeaudio.json`, `~/.copilot/hooks/vibeaudio.json` and `~/.claude/commands/vibe.md`.
|
|
571
660
|
|
|
572
|
-
Step 3
|
|
661
|
+
Step 3 reclaims disk โ the audio cache, pruned to the 3 most recent projects โ and drops your saved genre and volume. Both come back on their own, so skip it if you're reinstalling.
|
|
573
662
|
|
|
574
663
|
**Two things are deliberately left behind:**
|
|
575
664
|
|
|
@@ -580,6 +669,8 @@ Step 3 only reclaims disk โ the audio cache, pruned to the 3 most recent proje
|
|
|
580
669
|
|
|
581
670
|
Apart from those two, the three commands above remove everything VibeAudio writes.
|
|
582
671
|
|
|
672
|
+
</details>
|
|
673
|
+
|
|
583
674
|
---
|
|
584
675
|
|
|
585
676
|
## ๐ค Contributing
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeaudio",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Procedural focus music while your AI coding tools (Claude Code, Codex, Cursor, Grok, Gemini, Copilot) think
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Procedural focus music while your AI coding tools (Claude Code, Codex, Cursor, Grok, Gemini, Copilot) think \u2014 a different arrangement per project.",
|
|
5
5
|
"bin": {
|
|
6
|
-
"vibeaudio": "
|
|
7
|
-
"vibe": "
|
|
6
|
+
"vibeaudio": "bin/vibeaudio.js",
|
|
7
|
+
"vibe": "bin/vibeaudio.js"
|
|
8
8
|
},
|
|
9
9
|
"main": "src/cli.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "node test/test-synth.js",
|
|
12
|
-
"start": "node bin/vibeaudio.js"
|
|
12
|
+
"start": "node bin/vibeaudio.js",
|
|
13
|
+
"postinstall": "node scripts/postinstall.js"
|
|
13
14
|
},
|
|
14
15
|
"publishConfig": {
|
|
15
16
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
"files": [
|
|
22
23
|
"bin/",
|
|
23
24
|
"src/",
|
|
25
|
+
"scripts/postinstall.js",
|
|
24
26
|
"README.md",
|
|
25
27
|
"LICENSE"
|
|
26
28
|
],
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one line an install owes the user.
|
|
3
|
+
*
|
|
4
|
+
* `npm i -g vibeaudio` used to end in silence: nothing plays until hooks are
|
|
5
|
+
* installed or a command is wrapped, so someone who installed it and then went
|
|
6
|
+
* back to work heard nothing and concluded it was broken. This says what to
|
|
7
|
+
* run next, once, at the only moment the user is definitely looking.
|
|
8
|
+
*
|
|
9
|
+
* It must never fail an install. Anything thrown here is npm's problem to
|
|
10
|
+
* report and the user's to wonder about, so everything is wrapped and the exit
|
|
11
|
+
* code is always 0 - a greeting is not worth a failed install.
|
|
12
|
+
*/
|
|
13
|
+
try {
|
|
14
|
+
// Local installs are a dependency of something else; their user is not here
|
|
15
|
+
// and not the one who would run `vibe`. CI and Docker set this too.
|
|
16
|
+
// No isTTY check: npm pipes lifecycle output often enough that gating on it
|
|
17
|
+
// would mean the hint never lands for the people who need it. A global
|
|
18
|
+
// install is already the narrow case - a dependency install sets this false.
|
|
19
|
+
if (process.env.npm_config_global === "true") {
|
|
20
|
+
const c = (code, text) => `\x1b[${code}m${text}\x1b[0m`;
|
|
21
|
+
console.log(`
|
|
22
|
+
${c("1;36", "๐ง VibeAudio installed.")} Two ways to start:
|
|
23
|
+
|
|
24
|
+
${c("1", "vibe --install-hooks")} ${c("90", "music follows your agent's thinking โ best for Claude Code, Codex, Cursorโฆ")}
|
|
25
|
+
${c("1", "vibe npm test")} ${c("90", "wrap any command that exits when it's done")}
|
|
26
|
+
|
|
27
|
+
${c("90", "vibe to pick a tool and a sound from a menu")}
|
|
28
|
+
${c("90", "vibe --preview jazz to hear one first")}
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {
|
|
32
|
+
// Deliberately silent: see above.
|
|
33
|
+
}
|