vibeaudio 0.5.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 +124 -44
- package/package.json +5 -3
- package/scripts/postinstall.js +33 -0
- package/src/cli.js +284 -18
- package/src/hooks.js +56 -14
- package/src/interactive.js +56 -4
- package/src/mcp.js +9 -3
- package/src/player.js +97 -0
- 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
|
@@ -22,21 +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`).
|
|
29
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.
|
|
30
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.
|
|
31
36
|
* ๐ **Terminal title HUD.** A live ASCII wave and elapsed timer in the window title, where it can't corrupt a full-screen TUI.
|
|
32
|
-
|
|
37
|
+
|
|
38
|
+
</details>
|
|
33
39
|
|
|
34
40
|
### ๐ผ Every project gets its own arrangement
|
|
35
41
|
|
|
36
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*.
|
|
37
43
|
|
|
38
|
-
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.
|
|
39
|
-
|
|
40
44
|
Pin or explore arrangements when you want to:
|
|
41
45
|
|
|
42
46
|
```bash
|
|
@@ -44,8 +48,15 @@ vibe --seed 42 --preview jazz # audition one specific arrangement
|
|
|
44
48
|
export VIBE_SEED=7 # pin the same sound everywhere
|
|
45
49
|
```
|
|
46
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
|
+
|
|
47
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.
|
|
48
57
|
|
|
58
|
+
</details>
|
|
59
|
+
|
|
49
60
|
---
|
|
50
61
|
|
|
51
62
|
## ๐ Requirements
|
|
@@ -147,6 +158,13 @@ Choose your volume level:
|
|
|
147
158
|
4. ๐ข Loud (75%) โ Audible across the room
|
|
148
159
|
```
|
|
149
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
|
+
|
|
150
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:
|
|
151
169
|
|
|
152
170
|
```
|
|
@@ -159,9 +177,11 @@ Should the music react to what the agent is doing?
|
|
|
159
177
|
2. Reactive โ Intensity follows the tool in use - noticeable, by design
|
|
160
178
|
```
|
|
161
179
|
|
|
162
|
-
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).
|
|
163
181
|
|
|
164
|
-
|
|
182
|
+
Adding an entry to the launcher is one line in [`src/interactive.js`](src/interactive.js).
|
|
183
|
+
|
|
184
|
+
</details>
|
|
165
185
|
|
|
166
186
|
---
|
|
167
187
|
|
|
@@ -176,12 +196,17 @@ Once hooks are installed they take over: running `vibe claude` (or `vibe codex`)
|
|
|
176
196
|
```bash
|
|
177
197
|
vibe --install-hooks # every agent found on this machine
|
|
178
198
|
vibe --install-hooks --tools codex # or just one of them
|
|
179
|
-
vibe --genre jazz --volume 25 --install-hooks #
|
|
199
|
+
vibe --genre jazz --volume 25 --install-hooks # install, and save these as your default
|
|
180
200
|
vibe --install-hooks --dry-run # show what would change, write nothing
|
|
181
201
|
```
|
|
182
202
|
|
|
183
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.
|
|
184
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
|
+
|
|
185
210
|
Each tool spells its events its own way, and VibeAudio writes whichever dialect the file expects:
|
|
186
211
|
|
|
187
212
|
| | File | Music starts | Music stops + chime | Reactive (opt-in) |
|
|
@@ -205,9 +230,11 @@ Where an agent reports more than start and stop, VibeAudio listens for that too
|
|
|
205
230
|
| **Qwen Code** | `PermissionRequest` | `PostToolUse`, `PostToolUseFailure` | `StopFailure` | `SessionEnd` |
|
|
206
231
|
| **Cursor, Grok** | โ | โ | โ | โ |
|
|
207
232
|
|
|
208
|
-
|
|
233
|
+
</details>
|
|
234
|
+
|
|
235
|
+
<details>
|
|
236
|
+
<summary><b>Six things that differ per agent</b> โ only the first one needs anything from you</summary>
|
|
209
237
|
|
|
210
|
-
Six things differ per agent, and none of them need any action from you except the first:
|
|
211
238
|
|
|
212
239
|
| | |
|
|
213
240
|
| :--- | :--- |
|
|
@@ -217,24 +244,32 @@ Six things differ per agent, and none of them need any action from you except th
|
|
|
217
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. |
|
|
218
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`. |
|
|
219
246
|
|
|
247
|
+
</details>
|
|
248
|
+
|
|
220
249
|
### Several sessions at once
|
|
221
250
|
|
|
222
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:
|
|
223
252
|
|
|
224
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.
|
|
225
|
-
* **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).
|
|
226
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).
|
|
227
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.
|
|
228
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.
|
|
229
262
|
|
|
230
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.
|
|
231
264
|
|
|
232
|
-
>
|
|
265
|
+
</details>
|
|
233
266
|
|
|
234
|
-
> **
|
|
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.
|
|
235
268
|
|
|
236
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).)
|
|
237
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
|
+
|
|
238
273
|
### `/vibe` inside Claude Code
|
|
239
274
|
|
|
240
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:
|
|
@@ -251,23 +286,32 @@ It asks Claude to run the matching `vibe` command, so Claude Code will ask permi
|
|
|
251
286
|
|
|
252
287
|
### Changing the sound later
|
|
253
288
|
|
|
254
|
-
|
|
289
|
+
One command, and it reaches everything:
|
|
255
290
|
|
|
256
291
|
```bash
|
|
257
|
-
vibe --genre electronic --volume 25
|
|
292
|
+
vibe --genre electronic --volume 25
|
|
258
293
|
```
|
|
259
294
|
|
|
260
|
-
|
|
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.
|
|
296
|
+
|
|
297
|
+
Audition before you commit: `vibe --preview electronic`.
|
|
261
298
|
|
|
262
|
-
|
|
299
|
+
A flag still beats an environment variable, which still beats the saved file โ so a one-off stays a one-off:
|
|
263
300
|
|
|
264
301
|
```bash
|
|
265
|
-
vibe --genre
|
|
302
|
+
vibe --genre 8bit npm test # this run only, nothing saved
|
|
266
303
|
```
|
|
267
304
|
|
|
268
|
-
|
|
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
|
+
```
|
|
269
313
|
|
|
270
|
-
|
|
314
|
+
Music already playing keeps the old genre until the next prompt swaps the daemon โ `vibe --stop` cuts it short.
|
|
271
315
|
|
|
272
316
|
Removing them is one command:
|
|
273
317
|
|
|
@@ -293,9 +337,10 @@ Each agent names its tools differently, and all the vocabularies are mapped. **M
|
|
|
293
337
|
|
|
294
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.
|
|
295
339
|
|
|
296
|
-
**
|
|
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.
|
|
297
341
|
|
|
298
|
-
|
|
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>
|
|
299
344
|
|
|
300
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.
|
|
301
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.
|
|
@@ -303,13 +348,15 @@ Changes land at the next loop boundary, so it shifts musically rather than cutti
|
|
|
303
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.
|
|
304
349
|
* **Can't run away.** The background player is capped at 15 minutes, so a missed stop event can't leave music looping.
|
|
305
350
|
|
|
351
|
+
</details>
|
|
352
|
+
|
|
306
353
|
---
|
|
307
354
|
|
|
308
355
|
## ๐ฅ๏ธ Everything Else (Claude Desktop, Antigravityโฆ via MCP)
|
|
309
356
|
|
|
310
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.
|
|
311
358
|
|
|
312
|
-
> **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.**
|
|
313
360
|
|
|
314
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:
|
|
315
362
|
|
|
@@ -337,6 +384,11 @@ Where the file lives:
|
|
|
337
384
|
| **Claude Desktop** (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
338
385
|
| **Antigravity, VS Code, Zed, โฆ** | that app's own MCP settings โ same JSON shape |
|
|
339
386
|
|
|
387
|
+
Restart the app afterwards.
|
|
388
|
+
|
|
389
|
+
<details>
|
|
390
|
+
<summary><b>Codex's TOML, and older Gemini CLI releases</b></summary>
|
|
391
|
+
|
|
340
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:
|
|
341
393
|
|
|
342
394
|
```toml
|
|
@@ -345,15 +397,17 @@ command = "node"
|
|
|
345
397
|
args = ["/absolute/path/from/the/command/above", "--mcp"]
|
|
346
398
|
```
|
|
347
399
|
|
|
348
|
-
|
|
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>
|
|
349
403
|
|
|
350
404
|
### Changing the genre here
|
|
351
405
|
|
|
352
|
-
`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.
|
|
353
407
|
|
|
354
|
-
**
|
|
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.
|
|
355
409
|
|
|
356
|
-
**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`:
|
|
357
411
|
|
|
358
412
|
```json
|
|
359
413
|
"env": { "VIBE_GENRE": "jazz", "VIBE_VOLUME": "25" }
|
|
@@ -365,7 +419,7 @@ VIBE_GENRE = "jazz"
|
|
|
365
419
|
VIBE_VOLUME = "25"
|
|
366
420
|
```
|
|
367
421
|
|
|
368
|
-
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.
|
|
369
423
|
|
|
370
424
|
#### Exposed MCP Tools:
|
|
371
425
|
* `vibe_play`: Start procedural focus music (`genre`: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random`; `volume`: `5-100`).
|
|
@@ -410,28 +464,43 @@ vibe --genre random claude # Surprise vibe each run
|
|
|
410
464
|
|
|
411
465
|
### โ๏ธ Set Your Favorite Genre as Default
|
|
412
466
|
|
|
413
|
-
|
|
467
|
+
```bash
|
|
468
|
+
vibe --genre jazz --volume 25
|
|
469
|
+
```
|
|
414
470
|
|
|
415
|
-
|
|
416
|
-
| :--- | :--- |
|
|
417
|
-
| **Wrapper** (`vibe <command>`) | `export VIBE_GENRE=jazz` โ or `--genre` per run |
|
|
418
|
-
| **Agent hooks** (Claude Code, Codex, Cursor, Grok) | re-run `vibe --genre jazz --install-hooks` โ [why](#changing-the-sound-later) |
|
|
419
|
-
| **MCP** (Gemini CLI, Claude Desktop, โฆ) | ask the assistant, or the config's `env` block โ [how](#changing-the-genre-here) |
|
|
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.
|
|
420
472
|
|
|
421
473
|
```bash
|
|
422
|
-
|
|
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
|
|
423
478
|
```
|
|
424
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:**
|
|
483
|
+
|
|
484
|
+
```bash
|
|
485
|
+
cd ~/work/api
|
|
486
|
+
vibe --genre zen --here # this directory and everything under it
|
|
487
|
+
```
|
|
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
|
+
|
|
425
493
|
---
|
|
426
494
|
|
|
427
495
|
## โ๏ธ Options & Flags
|
|
428
496
|
|
|
429
497
|
| Flag | Description | Default |
|
|
430
498
|
| :--- | :--- | :--- |
|
|
431
|
-
| `-g, --genre <name>` | Music style: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random
|
|
432
|
-
| `-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` |
|
|
433
501
|
| `-cv, --chime-volume <5-100>` | Set independent completion chime volume | `volume ร 1.1`, kept within 35โ65 |
|
|
434
502
|
| `--grace <ms>` | Silence window before music starts | `1500` |
|
|
503
|
+
| `--here` | With a saved setting: this directory tree only, not everywhere | off |
|
|
435
504
|
| `--seed <n>` | Force a specific arrangement | derived from the project directory |
|
|
436
505
|
| `--whisper` | Quick preset: 15% volume (headphones / late night) | โ |
|
|
437
506
|
| `--quiet` | Quick preset: 25% volume (focus / open office) | โ |
|
|
@@ -454,7 +523,8 @@ export VIBE_GENRE=jazz # or synthwave, 8bit, electronic, zen, piano, drone,
|
|
|
454
523
|
| `--version` | Show version | โ |
|
|
455
524
|
|
|
456
525
|
### โ๏ธ Environment Variables
|
|
457
|
-
|
|
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:
|
|
458
528
|
|
|
459
529
|
```bash
|
|
460
530
|
export VIBE_GENRE=jazz # lofi, synthwave, 8bit, electronic, jazz, zen, piano, drone, random
|
|
@@ -467,7 +537,7 @@ export VIBE_DISABLE=1 # Mute, without uninstalling anything
|
|
|
467
537
|
|
|
468
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.
|
|
469
539
|
|
|
470
|
-
**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.
|
|
471
541
|
|
|
472
542
|
```bash
|
|
473
543
|
vibe --mute # silent now, music returns by itself in an hour
|
|
@@ -476,11 +546,16 @@ vibe --mute 0 # stay off until I say otherwise
|
|
|
476
546
|
vibe --unmute # end it early
|
|
477
547
|
```
|
|
478
548
|
|
|
479
|
-
|
|
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>
|
|
480
553
|
|
|
481
|
-
|
|
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.
|
|
482
555
|
|
|
483
|
-
|
|
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>
|
|
484
559
|
|
|
485
560
|
---
|
|
486
561
|
|
|
@@ -522,7 +597,7 @@ This needs a PulseAudio-compatible sound server on **your local machine**: a Lin
|
|
|
522
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.
|
|
523
598
|
|
|
524
599
|
**Volume flag does nothing**
|
|
525
|
-
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.
|
|
526
601
|
|
|
527
602
|
---
|
|
528
603
|
|
|
@@ -573,14 +648,17 @@ The last block is detected from your own `PATH`, so it answers "will this work w
|
|
|
573
648
|
```bash
|
|
574
649
|
vibe --uninstall-hooks # 1. unwire every agent, and stop any player still running
|
|
575
650
|
npm rm -g vibeaudio # 2. remove the CLI
|
|
576
|
-
rm -rf ~/.vibeaudio # 3. optional: cached audio
|
|
651
|
+
rm -rf ~/.vibeaudio # 3. optional: cached audio, saved settings, daemon state
|
|
577
652
|
```
|
|
578
653
|
|
|
654
|
+
<details>
|
|
655
|
+
<summary><b>Why that order, and what stays behind</b></summary>
|
|
656
|
+
|
|
579
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"`.
|
|
580
658
|
|
|
581
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`.
|
|
582
660
|
|
|
583
|
-
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.
|
|
584
662
|
|
|
585
663
|
**Two things are deliberately left behind:**
|
|
586
664
|
|
|
@@ -591,6 +669,8 @@ Step 3 only reclaims disk โ the audio cache, pruned to the 3 most recent proje
|
|
|
591
669
|
|
|
592
670
|
Apart from those two, the three commands above remove everything VibeAudio writes.
|
|
593
671
|
|
|
672
|
+
</details>
|
|
673
|
+
|
|
594
674
|
---
|
|
595
675
|
|
|
596
676
|
## ๐ค Contributing
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
6
|
"vibeaudio": "bin/vibeaudio.js",
|
|
7
7
|
"vibe": "bin/vibeaudio.js"
|
|
@@ -9,7 +9,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
|
+
}
|