vibeaudio 0.4.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
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,601 @@
1
+ # ๐ŸŽง VibeAudio
2
+
3
+ [![test](https://github.com/kiril6/vibeaudio/actions/workflows/test.yml/badge.svg)](https://github.com/kiril6/vibeaudio/actions/workflows/test.yml)
4
+
5
+ > **Procedural focus music while your AI coding tools think.**
6
+ > Every project gets its own arrangement. Zero dependencies, zero audio files.
7
+ > Works with Claude Code, Codex, Cursor, Grok, Gemini CLI, Copilot CLI, Qwen Code, Aider โ€” and any terminal command.
8
+
9
+ **[Install](#-install)** ยท **[Agent hooks](#-agent-hooks-no-wrapper-needed)** ยท **[Genres](#-music-genres)** ยท **[Flags](#-options--flags)** ยท **[Troubleshooting](#-troubleshooting)** ยท **[Uninstall](#-uninstall)**
10
+
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
+
13
+ > **In a hurry?** `npm i -g github:kiril6/vibeaudio`, then `vibe --install-hooks`. Your next prompt has music.
14
+
15
+ ---
16
+
17
+ ## โšก Why
18
+
19
+ AI coding agents take 15โ€“45 seconds to reason, read files and write code. Staring at a blank cursor feels slow; tabbing away means checking back to see if it's done. VibeAudio fills that gap with music while the agent thinks, and a chime when your output is ready to read.
20
+
21
+ **How it behaves:**
22
+
23
+ * ๐Ÿงฎ **Pure synthesis, zero MP3s.** Every note, chord and pad is generated in code โ€” no audio assets, no npm dependencies, no `node-gyp`.
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
+ * ๐Ÿ“ˆ **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
+ * ๐Ÿ”” **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
+ * โœ‹ **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.
29
+ * ๐Ÿงฎ **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
+ * ๐ŸŒŠ **Terminal title HUD.** A live ASCII wave and elapsed timer in the window title, where it can't corrupt a full-screen TUI.
31
+ * ๐Ÿ”Œ **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.
32
+
33
+ ### ๐ŸŽผ Every project gets its own arrangement
34
+
35
+ 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
+
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
+ Pin or explore arrangements when you want to:
40
+
41
+ ```bash
42
+ vibe --seed 42 --preview jazz # audition one specific arrangement
43
+ export VIBE_SEED=7 # pin the same sound everywhere
44
+ ```
45
+
46
+ **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
+
48
+ ---
49
+
50
+ ## ๐Ÿ“‹ Requirements
51
+
52
+ * **Node.js โ‰ฅ 18**
53
+ * **An audio player.** VibeAudio shells out to whatever your OS provides:
54
+
55
+ | Platform | Player used | Volume control | Notes |
56
+ | :--- | :--- | :--- | :--- |
57
+ | **macOS** | `afplay` | โœ… | Ships with the system โ€” nothing to install. |
58
+ | **Linux** | `paplay`, `ffplay`, or `aplay` | โœ… | Install `pulseaudio-utils`, `ffmpeg`, or `alsa-utils`. |
59
+ | **Windows** | PowerShell `SoundPlayer` | โœ… | Nothing to install. |
60
+
61
+ `aplay` and PowerShell's `SoundPlayer` take no volume argument, so `--volume` is applied by rendering the loop pre-attenuated instead โ€” same result, one cached file per volume you use.
62
+
63
+ If no player is found, VibeAudio prints a one-line notice and runs your command **silently** โ€” it never blocks the tool you actually wanted to run.
64
+
65
+ ---
66
+
67
+ ## ๐Ÿš€ Install
68
+
69
+ One line. No clone, no build step, no dependencies to resolve:
70
+
71
+ ```bash
72
+ npm i -g github:kiril6/vibeaudio
73
+ ```
74
+
75
+ That puts `vibe` and `vibeaudio` on your `PATH`. Re-run the same command to update, or see [Uninstall](#-uninstall) to remove it cleanly.
76
+
77
+ > **Note:** VibeAudio isn't on the npm registry yet, so plain `npx vibeaudio` won't resolve โ€” use the `github:` form above.
78
+
79
+ ### Then pick how it runs
80
+
81
+ **Using Claude Code, Codex, Cursor or Grok interactively?** Install the hooks โ€” this is the mode that actually tracks thinking:
82
+
83
+ ```bash
84
+ vibe --install-hooks
85
+ ```
86
+
87
+ Now run your agent normally, with no prefix. Music starts when you submit a prompt and stops with a chime when the agent finishes. [Details below.](#-agent-hooks-no-wrapper-needed)
88
+
89
+ **Running one-shot commands?** Wrap them:
90
+
91
+ ```bash
92
+ vibe npm test
93
+ vibe claude -p "explain this repo"
94
+ ```
95
+
96
+ **Lost?** `vibe --help` lists every flag, and `vibe --status` reads your live setup back to you โ€” what's installed, what's playing, and which agents it found.
97
+
98
+ > **Which one you want:** the wrapper plays music for as long as the wrapped process lives. That's exactly right for a command that exits when its work is done โ€” and wrong for an interactive REPL like `claude`, where the process stays alive while you read and type, so the music never stops. Hooks know when the agent is actually thinking; the wrapper can only time the process.
99
+
100
+ ### Try it without installing
101
+
102
+ ```bash
103
+ npx github:kiril6/vibeaudio --preview jazz # hear a loop right now
104
+ npx github:kiril6/vibeaudio sleep 8 # hear the wrapper: music, then the done chime
105
+ ```
106
+
107
+ > **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.
108
+
109
+ `npx` runs from a temporary cache that npm eventually deletes, so `--install-hooks` refuses to run this way โ€” it would write a path into your agent's hook config that later vanishes. Install globally first.
110
+
111
+ ### Interactive Launcher Menu
112
+
113
+ Run with no command to get a menu for picking your AI, vibe, and volume:
114
+
115
+ ```bash
116
+ vibe
117
+ ```
118
+
119
+ ```
120
+ ๐ŸŽง VibeAudio โ€” Interactive AI Launcher
121
+
122
+ Which AI companion would you like to launch?
123
+ โฏ 1. Claude Code [โœ“ installed]
124
+ 2. Gemini CLI [โœ“ installed]
125
+ 3. Codex CLI [โœ“ installed]
126
+ 4. GitHub Copilot CLI [โœ“ installed]
127
+ 5. Grok CLI (not found in PATH)
128
+ 6. Cursor CLI (not found in PATH)
129
+ 7. Aider (not found in PATH)
130
+ 8. Ollama (Llama 3) (not found in PATH)
131
+ 9. Custom command...
132
+
133
+ Choose your sound vibe:
134
+ โฏ 1. โ˜• Lo-Fi Focus โ€” Warm Rhodes electric piano & Kalimba drops
135
+ 2. ๐ŸŒŒ Chill Synthwave โ€” '80s analog chorused pads & pulsing bass
136
+ 3. ๐Ÿ•น๏ธ Cozy 8-Bit โ€” Filtered retro chiptune arpeggios
137
+ 4. โšก Melodic Electronic โ€” Downtempo resonant plucks & tech pulse
138
+ 5. ๐ŸŽท Midnight Jazz โ€” ii-V-I piano chords & walking upright bass
139
+ 6. ๐ŸŽ‹ Zen Ambient โ€” Meditative singing bowls & floating celestial pads
140
+ 7. ๐ŸŽน Sparse Piano โ€” Single struck notes & long silences โ€” Satie-ish
141
+ 8. ๐ŸŒซ๏ธ Deep Drone โ€” Held tone & filtered noise โ€” no melody at all
142
+ 9. ๐ŸŽฒ Shuffle / Random โ€” Picks a surprise vibe each time
143
+
144
+ Choose your volume level:
145
+ โฏ 1. โ˜• Normal (40%) โ€” Balanced focus background [Default]
146
+ 2. ๐Ÿคซ Quiet (25%) โ€” Discreet focus / open office
147
+ 3. ๐ŸŒ™ Whisper (15%) โ€” Ultra-gentle / headphones / late night
148
+ 4. ๐Ÿ“ข Loud (75%) โ€” Audible across the room
149
+ ```
150
+
151
+ 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
+
153
+ ```
154
+ How should the music run?
155
+ โฏ 1. Agent hooks โ€” Music follows the agent's thinking. Set once, no wrapper needed
156
+ 2. This session only โ€” Music plays while the process lives - fine for one-shot commands
157
+
158
+ Should the music react to what the agent is doing?
159
+ โฏ 1. Steady (recommended) โ€” Intensity follows elapsed time, and stays ignorable
160
+ 2. Reactive โ€” Intensity follows the tool in use - noticeable, by design
161
+ ```
162
+
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 the hooks own those.
164
+
165
+ Installed tools sort to the top. The list is a shortcut, not a compatibility list โ€” **`vibe` wraps any command at all**, and "Custom command..." takes one you type (quoted arguments survive intact). Adding an entry is one line in [`src/interactive.js`](src/interactive.js).
166
+
167
+ ---
168
+
169
+ ## ๐Ÿช Agent Hooks (no wrapper needed)
170
+
171
+ > **`--install-hooks` supports Claude Code, Codex, Cursor, Grok, Gemini CLI, GitHub Copilot CLI and Qwen Code.** Everything else uses the [wrapper or MCP](#-everything-else-claude-desktop-antigravity-via-mcp) instead.
172
+
173
+ Wrapping (`vibe claude`) infers "the AI is thinking" from how long the process runs. Hooks know for certain โ€” so music starts the moment you submit a prompt and stops the moment the agent finishes, with no grace-window guessing and no aliases.
174
+
175
+ Once hooks are installed they take over: running `vibe claude` (or `vibe codex`) anyway plays no music of its own and says so, rather than layering a session-long loop on top of the hooks' per-prompt one.
176
+
177
+ ```bash
178
+ vibe --install-hooks # every agent found on this machine
179
+ vibe --install-hooks --tools codex # or just one of them
180
+ vibe --genre jazz --volume 25 --install-hooks # pin the genre and volume
181
+ vibe --install-hooks --dry-run # show what would change, write nothing
182
+ ```
183
+
184
+ **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
+
186
+ Each tool spells its events its own way, and VibeAudio writes whichever dialect the file expects:
187
+
188
+ | | File | Music starts | Music stops + chime | Reactive (opt-in) |
189
+ | :--- | :--- | :--- | :--- | :--- |
190
+ | **Claude Code** | `~/.claude/settings.json` | `UserPromptSubmit` | `Stop` | `PreToolUse` |
191
+ | **Codex** | `~/.codex/hooks.json` | `UserPromptSubmit` | `Stop` | `PreToolUse` |
192
+ | **Cursor** | `~/.cursor/hooks.json` | `beforeSubmitPrompt` | `stop` | `preToolUse` |
193
+ | **Grok** | `~/.grok/hooks/vibeaudio.json` | `UserPromptSubmit` | `Stop` | `PreToolUse` |
194
+ | **Gemini CLI** | `~/.gemini/settings.json` | `BeforeAgent` | `AfterAgent` | `BeforeTool` |
195
+ | **Copilot CLI** | `~/.copilot/hooks/vibeaudio.json` | `UserPromptSubmit` | `Stop` | `PreToolUse` |
196
+ | **Qwen Code** | `~/.qwen/settings.json` | `UserPromptSubmit` | `Stop` | `PreToolUse` |
197
+
198
+ Where an agent reports more than start and stop, VibeAudio listens for that too โ€” and only where the event was confirmed against the tool itself:
199
+
200
+ | | Music pauses + "your turn" chime | Music resumes | Failure chime (API error) | Session closes mid-turn (silent) |
201
+ | :--- | :--- | :--- | :--- | :--- |
202
+ | **Claude Code** | `PermissionRequest`, `Elicitation` | `PostToolUse`, `PostToolUseFailure`, `ElicitationResult` | `StopFailure` | `SessionEnd` |
203
+ | **Codex** | `PermissionRequest` | `PostToolUse` | โ€” | โ€” |
204
+ | **Gemini CLI** | `Notification` (tool permission) | `AfterTool` | โ€” | `SessionEnd` |
205
+ | **Copilot CLI** | `Notification` (permission prompt) | `PostToolUse`, `PostToolUseFailure` | โ€” | `SessionEnd` |
206
+ | **Qwen Code** | `PermissionRequest` | `PostToolUse`, `PostToolUseFailure` | `StopFailure` | `SessionEnd` |
207
+ | **Cursor, Grok** | โ€” | โ€” | โ€” | โ€” |
208
+
209
+ That's it โ€” run your agent normally, with no `vibe` prefix.
210
+
211
+ Six things differ per agent, and none of them need any action from you except the first:
212
+
213
+ | | |
214
+ | :--- | :--- |
215
+ | **Five agents tell you when they're waiting on you** | When a permission dialog opens the music stops rather than sounding busy while the agent is stuck on you, and it resumes once the thing you answered has run. Claude Code covers the terminal, desktop app and IDEs alike, plus MCP servers asking for input. Copilot CLI's own `PermissionRequest` fires before *every* permission check โ€” dialog or not โ€” so VibeAudio listens for its permission-prompt notification instead. Cursor and Grok have no such event that's been verified, so they keep playing through a prompt. |
216
+ | **Claude Code turns that never reach `Stop` still end the music** | An API error or rate limit ends the turn with `StopFailure` instead, which plays the failure chime. Interrupting (Esc, or the stop button in the desktop app) fires no hook at all, so the background player watches the session transcript for Claude Code's interrupt entry and stops silently within half a second โ€” whether the agent was writing or running a tool. Closing the session mid-turn stops it too โ€” but only if that session started the music, so closing an idle terminal never silences another one. |
217
+ | **Codex asks you to trust the hook once** | Codex keeps a per-hook trust hash in `~/.codex/config.toml` and won't run a hook it hasn't been told to trust, so the install isn't live until you approve each one the first time it fires. |
218
+ | **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
+ | **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
+
221
+ > **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.
222
+
223
+ > **One player is shared.** They all drive the same background player, so if you prompt two agents at once, the last prompt owns the music. One person, one set of speakers โ€” deliberate, not a limitation being worked around.
224
+
225
+ > **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
+
227
+ ### `/vibe` inside Claude Code
228
+
229
+ Installing the Claude Code hooks also adds a `/vibe` command (`~/.claude/commands/vibe.md`), so you can control the music without leaving the session:
230
+
231
+ ```text
232
+ /vibe what's installed and playing
233
+ /vibe mute 30 silence for 30 minutes (/vibe unmute to end it early)
234
+ /vibe stop stop the music now
235
+ /vibe genre jazz switch genre, keeping your volume
236
+ /vibe volume 20 change volume, keeping your genre
237
+ ```
238
+
239
+ It asks Claude to run the matching `vibe` command, so Claude Code will ask permission for that command the first time. If you already have your own `vibe.md` there, VibeAudio leaves it alone and says so; `--uninstall-hooks` removes only its own.
240
+
241
+ ### Changing the sound later
242
+
243
+ Re-run the install with the settings you want. It replaces the existing entry rather than adding a second one:
244
+
245
+ ```bash
246
+ vibe --genre electronic --volume 25 --install-hooks
247
+ ```
248
+
249
+ The change applies to your next prompt โ€” no restart. Audition first with `vibe --preview electronic`.
250
+
251
+ **The flags compose** โ€” set everything you want in one command, including [reactive mode](#reactive-mode-opt-in):
252
+
253
+ ```bash
254
+ vibe --genre jazz --volume 25 --reactive --install-hooks
255
+ ```
256
+
257
+ Two things to know: the reinstall **replaces** the whole entry, so flags you don't repeat are dropped (leave off `--reactive` and reactive mode goes away). And music already playing keeps the old genre until the next prompt swaps the daemon โ€” `pkill -f "vibeaudio.js --daemon"` cuts it short.
258
+
259
+ > **`VIBE_GENRE` / `VIBE_VOLUME` won't change an installed hook.** They're read once, at install time, and written into the hook command โ€” so exporting a new value later does nothing until you reinstall. The same goes for `vibe --genre <name>` on its own: with no command after it that opens the launcher menu, which asks for a genre and uses its own answer. Changing hook music always means re-running `--install-hooks`.
260
+
261
+ Removing them is one command:
262
+
263
+ ```bash
264
+ vibe --uninstall-hooks
265
+ ```
266
+
267
+ ### Reactive mode (opt-in)
268
+
269
+ ```bash
270
+ vibe --reactive --install-hooks
271
+ ```
272
+
273
+ Adds a `PreToolUse` hook so intensity follows **what the agent is doing**, not just how long it's taken:
274
+
275
+ | Agent isโ€ฆ | Tools | Tier |
276
+ | :--- | :--- | :--- |
277
+ | Looking things up, or waiting on you | `Read`, `Grep`, `Glob`, `WebFetch`, `AskUserQuestion` | 1 โ€” sparse |
278
+ | Changing code | `Edit`, `Write`, `NotebookEdit`, `apply_patch` | 2 โ€” groove enters |
279
+ | Shelling out, or handing work to a subagent | `Bash`, `Agent`, `Skill`, `shell` | 3 โ€” peak |
280
+
281
+ Each agent names its tools differently, and all the vocabularies are mapped. **MCP tools stay at tier 2** โ€” they arrive as `mcp__<server>__<tool>` and can't be enumerated, since everyone's servers differ. That's a third of real traffic, and tier 2 is the honest read: real work, rarely the heaviest thing in a turn.
282
+
283
+ 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
+
285
+ **This is 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, but if you catch yourself listening to it instead of reading, reinstall without `--reactive` (which removes the `PreToolUse` hook again).
286
+
287
+ **What the installer will and won't do to your config:**
288
+
289
+ * **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
+ * **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.
291
+ * **Reinstalling updates, never duplicates.** Uninstalling sweeps every supported agent and removes only VibeAudio's own entries.
292
+ * **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
+ * **Can't run away.** The background player is capped at 15 minutes, so a missed stop event can't leave music looping.
294
+
295
+ ---
296
+
297
+ ## ๐Ÿ–ฅ๏ธ Everything Else (Claude Desktop, Antigravityโ€ฆ via MCP)
298
+
299
+ **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
+
301
+ > **This is weaker than hooks, by nature.** Hooks fire on an event โ€” the music always starts when you submit a prompt. MCP tools are *model-invoked*: the assistant has to decide to call `vibe_play`, and to remember `vibe_stop` when it's done. The server tells it when to do that (via the MCP `instructions` field), but it's a suggestion, not a guarantee โ€” expect the occasional silent turn, and 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
+
303
+ 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
+
305
+ ```bash
306
+ echo "$(npm root -g)/vibeaudio/bin/vibeaudio.js"
307
+ ```
308
+
309
+ Most apps use this JSON shape:
310
+
311
+ ```json
312
+ {
313
+ "mcpServers": {
314
+ "vibeaudio": {
315
+ "command": "node",
316
+ "args": ["/absolute/path/from/the/command/above", "--mcp"]
317
+ }
318
+ }
319
+ }
320
+ ```
321
+
322
+ Where the file lives:
323
+
324
+ | Tool | Config file |
325
+ | :--- | :--- |
326
+ | **Claude Desktop** (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
327
+ | **Antigravity, VS Code, Zed, โ€ฆ** | that app's own MCP settings โ€” same JSON shape |
328
+
329
+ 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
+
331
+ ```toml
332
+ [mcp_servers.vibeaudio]
333
+ command = "node"
334
+ args = ["/absolute/path/from/the/command/above", "--mcp"]
335
+ ```
336
+
337
+ Restart the app afterwards. 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`.
338
+
339
+ ### Changing the genre here
340
+
341
+ `vibe --genre` doesn't apply โ€” the app launches the server, not you. Two ways instead:
342
+
343
+ **Just ask.** `vibe_play` takes a genre, so "play some jazz while you work on this" is enough, no config edit and no restart.
344
+
345
+ **Or set a default in the config's `env` block**, since a GUI app won't inherit `VIBE_GENRE` from your shell. Most clients support `env` alongside `command`/`args`:
346
+
347
+ ```json
348
+ "env": { "VIBE_GENRE": "jazz", "VIBE_VOLUME": "25" }
349
+ ```
350
+
351
+ ```toml
352
+ [mcp_servers.vibeaudio.env]
353
+ VIBE_GENRE = "jazz"
354
+ VIBE_VOLUME = "25"
355
+ ```
356
+
357
+ Restart the app afterwards. A genre the assistant passes to `vibe_play` still wins over this default.
358
+
359
+ #### Exposed MCP Tools:
360
+ * `vibe_play`: Start procedural focus music (`genre`: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random`; `volume`: `5-100`).
361
+ * `vibe_stop`: Stop music and play the completion chime (`outcome`: `success` or `failure`).
362
+ * `vibe_status`: Return current playback state and active tier.
363
+
364
+ Playback stops automatically if the desktop client disconnects, and caps out after 15 minutes โ€” which also covers the likelier case of a model that started the music and never called `vibe_stop`.
365
+
366
+ ---
367
+
368
+ ## ๐ŸŽจ Music Genres
369
+
370
+ VibeAudio includes **8 procedural music styles** synthesized entirely in code:
371
+
372
+ | Genre | Style | Vibe |
373
+ | :--- | :--- | :--- |
374
+ | `lofi` | โ˜• **Lo-Fi Focus** *(Default)* | Warm Rhodes electric piano chords & Kalimba drops |
375
+ | `synthwave` | ๐ŸŒŒ **Chill Synthwave** | '80s analog chorused pads & pulsing retro bass |
376
+ | `8bit` | ๐Ÿ•น๏ธ **Cozy 8-Bit** | Filtered retro chiptune arpeggios & NES triangle bass |
377
+ | `electronic` | โšก **Melodic Electronic** | Downtempo resonant plucks & crisp tech pulse |
378
+ | `jazz` | ๐ŸŽท **Midnight Jazz** | Classic ii-V-I jazz piano chords & walking upright bass |
379
+ | `zen` | ๐ŸŽ‹ **Zen Ambient** | Meditative Tibetan singing bowls & celestial drone (zero rhythm) |
380
+ | `piano` | ๐ŸŽน **Sparse Piano** | Single struck notes and long silences โ€” Satie-ish |
381
+ | `drone` | ๐ŸŒซ๏ธ **Deep Drone** | A held tone and filtered noise โ€” **no melody at all** |
382
+ | `random` | ๐ŸŽฒ **Shuffle Mode** | Picks a surprise genre for the run โ€” **never `drone`** |
383
+
384
+ Aliases also work: `chiptune` โ†’ `8bit`, `downtempo` โ†’ `electronic`, `bossa` โ†’ `jazz`, `ambient` โ†’ `zen`, `sparse`/`satie` โ†’ `piano`, `noise`/`focus` โ†’ `drone`.
385
+
386
+ > **If any melody distracts you, use `drone`.** Every other genre plays something โ€” notes, a progression, a bass line โ€” and some people can't read while that happens. `drone` holds one low tone under a slow-breathing noise bed and never moves: closer to a fan or rainfall than to music. Tiers add weight rather than movement.
387
+ >
388
+ > **`piano` is the gentler version of that idea.** It still plays notes โ€” two in eight seconds at tier 1 โ€” but they're single struck tones with silence between them and nothing running underneath. Higher tiers fill the gaps rather than adding a groove. Try it before `drone` if you want *something* there.
389
+ >
390
+ > For the same reason **`random` never picks `drone`**. Shuffle is for a surprise *mood*, and drone isn't one โ€” landing on a fan noise when you asked for variety reads as broken audio, not as range. Ask for it by name (or `noise` / `focus`) when you want it.
391
+
392
+ ### Usage Examples:
393
+ ```bash
394
+ vibe --genre electronic claude # Melodic Electronic
395
+ vibe --genre jazz npm test # Midnight Jazz
396
+ vibe --genre zen claude # Zen Ambient (no drums/rhythm)
397
+ vibe --genre random claude # Surprise vibe each run
398
+ ```
399
+
400
+ ### โš™๏ธ Set Your Favorite Genre as Default
401
+
402
+ How you change it depends on how you run VibeAudio โ€” **a shell `export` only reaches the wrapper**:
403
+
404
+ | You run it via | Change the genre with |
405
+ | :--- | :--- |
406
+ | **Wrapper** (`vibe <command>`) | `export VIBE_GENRE=jazz` โ€” or `--genre` per run |
407
+ | **Agent hooks** (Claude Code, Codex, Cursor, Grok) | re-run `vibe --genre jazz --install-hooks` โ€” [why](#changing-the-sound-later) |
408
+ | **MCP** (Gemini CLI, Claude Desktop, โ€ฆ) | ask the assistant, or the config's `env` block โ€” [how](#changing-the-genre-here) |
409
+
410
+ ```bash
411
+ export VIBE_GENRE=jazz # or synthwave, 8bit, electronic, zen, piano, drone, random
412
+ ```
413
+
414
+ ---
415
+
416
+ ## โš™๏ธ Options & Flags
417
+
418
+ | Flag | Description | Default |
419
+ | :--- | :--- | :--- |
420
+ | `-g, --genre <name>` | Music style: `lofi`, `synthwave`, `8bit`, `electronic`, `jazz`, `zen`, `piano`, `drone`, `random` | `lofi` |
421
+ | `-v, --volume <5-100>` | Set playback volume | `40` |
422
+ | `-cv, --chime-volume <5-100>` | Set independent completion chime volume | `volume ร— 1.1`, kept within 35โ€“65 |
423
+ | `--grace <ms>` | Silence window before music starts | `1500` |
424
+ | `--seed <n>` | Force a specific arrangement | derived from the project directory |
425
+ | `--whisper` | Quick preset: 15% volume (headphones / late night) | โ€” |
426
+ | `--quiet` | Quick preset: 25% volume (focus / open office) | โ€” |
427
+ | `--loud` | Quick preset: 75% volume (hear from across the room) | โ€” |
428
+ | `--no-chime` | Disable the resolution completion chime | `false` |
429
+ | `--no-hud` | Disable terminal window/tab title animation | `false` |
430
+ | `--preview <genre>` | Play one loop of a genre and exit | โ€” |
431
+ | `--status` | Show what's installed, running and detected, then exit | โ€” |
432
+ | `--stop` | Stop the background player, then exit | โ€” |
433
+ | `--mute [minutes]` | Silence everything for a call, then exit | `60` min (`0` = until unmuted) |
434
+ | `--unmute` | Resume normal playback, then exit | โ€” |
435
+ | `--clear-cache` | Delete all cached audio, then exit | โ€” |
436
+ | `--mcp` | Run as an MCP stdio server for desktop apps | โ€” |
437
+ | `--install-hooks` | Wire music into your agent's hooks (no wrapper needed) | โ€” |
438
+ | `--tools <list>` | With `--install-hooks`: `claude,codex,cursor,grok,gemini,copilot,qwen` | auto-detect |
439
+ | `--reactive` | With `--install-hooks`: intensity follows the tool in use | off |
440
+ | `--dry-run` | With `--install-hooks`: show what would change in each file, write nothing | off |
441
+ | `--uninstall-hooks` | Remove the hooks again, from every agent | โ€” |
442
+ | `-h, --help` | Show help and options | โ€” |
443
+ | `--version` | Show version | โ€” |
444
+
445
+ ### โš™๏ธ Environment Variables
446
+ Set persistent defaults in your `~/.zshrc` or `~/.bashrc`:
447
+
448
+ ```bash
449
+ export VIBE_GENRE=jazz # lofi, synthwave, 8bit, electronic, jazz, zen, piano, drone, random
450
+ export VIBE_VOLUME=25 # Background music at 25%
451
+ export VIBE_CHIME_VOLUME=70 # Crisp completion chime at 70%
452
+ export VIBE_GRACE_MS=3000 # Wait 3s of thinking before any music
453
+ export VIBE_SEED=7 # Same arrangement everywhere, ignoring the directory
454
+ export VIBE_DISABLE=1 # Mute, without uninstalling anything
455
+ ```
456
+
457
+ **`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
+
459
+ **For a call that's ringing right now, use `vibe --mute` instead.** An environment variable can't help there: 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.
460
+
461
+ ```bash
462
+ vibe --mute # silent now, music returns by itself in an hour
463
+ vibe --mute 15 # or pick the window
464
+ vibe --mute 0 # stay off until I say otherwise
465
+ vibe --unmute # end it early
466
+ ```
467
+
468
+ `--mute` writes a flag file, which crosses process boundaries where a variable cannot, and stops whatever is playing on the spot. Your hooks and settings are untouched, so there's nothing to put back afterwards.
469
+
470
+ **It expires after an hour by default, and that's deliberate.** 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.
471
+
472
+ Either way, `--preview` still plays: that one is an explicit request to hear something.
473
+
474
+ ---
475
+
476
+ ## ๐Ÿ”ง Troubleshooting
477
+
478
+ **No sound at all**
479
+ Check that a player exists for your platform (see [Requirements](#-requirements)). VibeAudio prints a notice to stderr when it can't find one. On Linux: `sudo apt install pulseaudio-utils` (or `ffmpeg` / `alsa-utils`).
480
+
481
+ **Music starts immediately instead of after the grace window**
482
+ It usually is waiting โ€” your AI tool's own startup (auth, session load) just takes longer than 1.5s, so music and the tool's first output appear together. Raise the window: `vibe --grace 3000 claude`.
483
+
484
+ **Music sounds stale after upgrading**
485
+ It shouldn't. The cache key includes a hash of the synth sources, so changing a generator invalidates it automatically and the old directory is pruned on the next run. To force a rebuild anyway:
486
+
487
+ ```bash
488
+ vibe --clear-cache
489
+ ```
490
+
491
+ **Silent when the agent runs on another machine over SSH**
492
+ Sound plays on the machine where the agent runs, and a remote server usually has no sound card โ€” so VibeAudio finds no player and stays quiet. You can hear it locally by forwarding a PulseAudio socket through the SSH connection: the remote `paplay` sends the audio back over SSH to your own speakers.
493
+
494
+ This needs a PulseAudio-compatible sound server on **your local machine**: a Linux desktop (PipeWire and PulseAudio both provide one) or Windows with WSLg. macOS has none built in, so this route doesn't apply there.
495
+
496
+ 1. **Connect with the socket forwarded.** On the local machine, pick the socket for your setup:
497
+
498
+ ```bash
499
+ ssh -R /tmp/vibe-pulse.sock:"$XDG_RUNTIME_DIR/pulse/native" you@server # Linux desktop
500
+ ssh -R /tmp/vibe-pulse.sock:/mnt/wslg/PulseServer you@server # WSLg
501
+ ```
502
+
503
+ 2. **On the server, point audio at it and install a player** โ€” in the shell you launch the agent from, since hooks inherit the agent's environment:
504
+
505
+ ```bash
506
+ sudo apt install pulseaudio-utils # provides paplay
507
+ export PULSE_SERVER=unix:/tmp/vibe-pulse.sock
508
+ vibe --preview jazz # you should hear it locally
509
+ ```
510
+
511
+ 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
+
513
+ **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. If you changed `--volume` and hear no difference, you're most likely on hooks, which ignore the wrapper's flags: re-run `vibe --volume 25 --install-hooks`.
515
+
516
+ ---
517
+
518
+ ## ๐Ÿฉบ What's actually running
519
+
520
+ ```bash
521
+ vibe --status
522
+ ```
523
+
524
+ Reads live state rather than guessing โ€” the fastest answer to "why do I hear nothing" or "which genre is this set to":
525
+
526
+ ```
527
+ Audio
528
+ player afplay
529
+ cache 7.6 MB in ~/.vibeaudio/cache
530
+
531
+ Hooks
532
+ Claude Code
533
+ โœ” Stop
534
+ โœ” UserPromptSubmit jazz @ 25%, reactive
535
+ โœ” PreToolUse
536
+ โœ” PermissionRequest
537
+ โœ” PostToolUse jazz @ 25%, reactive
538
+ โœ” StopFailure
539
+ โœ” SessionEnd
540
+ Codex not installed โ€” run: vibe --install-hooks
541
+ Cursor not installed โ€” run: vibe --install-hooks
542
+ Grok not installed (not found on this machine)
543
+
544
+ Background player
545
+ running pid 59078 stop it with: vibe --stop
546
+
547
+ AI tools found
548
+ โœ” Claude Code hooks โ€” installed
549
+ โœ” Codex hooks โ€” run: vibe --install-hooks
550
+ โœ” Gemini CLI hooks โ€” run: vibe --install-hooks
551
+ โœ” GitHub Copilot CLI hooks โ€” run: vibe --install-hooks
552
+ ```
553
+
554
+ The last block is detected from your own `PATH`, so it answers "will this work with my tool" without you matching yourself against a table. `vibe --stop` kills the background player on the spot; the next prompt starts a fresh one.
555
+
556
+ ---
557
+
558
+ ## ๐Ÿงน Uninstall
559
+
560
+ **Remove the hooks first, while `vibe` still exists:**
561
+
562
+ ```bash
563
+ vibe --uninstall-hooks # 1. unwire every agent, and stop any player still running
564
+ npm rm -g vibeaudio # 2. remove the CLI
565
+ rm -rf ~/.vibeaudio # 3. optional: cached audio + daemon state
566
+ ```
567
+
568
+ 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
+
570
+ > **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
+
572
+ Step 3 only reclaims disk โ€” the audio cache, pruned to the 3 most recent projects โ€” and it's regenerated on next use, so skip it if you're reinstalling.
573
+
574
+ **Two things are deliberately left behind:**
575
+
576
+ | Leftover | Why, and how to remove it |
577
+ | :--- | :--- |
578
+ | `*.vibeaudio.bak` next to each shared hook config | Your config as it was before the first install โ€” one each for Claude Code, Codex, Cursor, Gemini CLI and Qwen Code. A safety net we won't delete for you; `rm` them once you're happy the real files are correct, and `vibe --uninstall-hooks` prints the path of every one it finds. (Grok and Copilot CLI leave nothing: their files are ours alone, so uninstall deletes them outright.) |
579
+ | `vibeaudio` entries in other apps' MCP configs | VibeAudio never edits those files, so it can't clean them either. Drop the entry from [whichever config you added it to](#-everything-else-claude-desktop-antigravity-via-mcp). |
580
+
581
+ Apart from those two, the three commands above remove everything VibeAudio writes.
582
+
583
+ ---
584
+
585
+ ## ๐Ÿค Contributing
586
+
587
+ Contributions welcome โ€” see [`CONTRIBUTING.md`](CONTRIBUTING.md) for the ground rules (zero runtime dependencies, no build step, pure synth modules) and the dev loop. Found a bug? [Open an issue](https://github.com/kiril6/vibeaudio/issues/new) with your **OS**, **Node version**, and which audio player you have installed.
588
+
589
+ Working on it locally:
590
+
591
+ ```bash
592
+ git clone https://github.com/kiril6/vibeaudio.git
593
+ cd vibeaudio
594
+ npm link # puts your clone's `vibe` on PATH
595
+ npm test
596
+ ```
597
+
598
+ ---
599
+
600
+ ## ๐Ÿ“„ License
601
+ MIT ยฉ 2026
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * VibeAudio CLI Executable
5
+ */
6
+
7
+ const { run } = require("../src/cli");
8
+
9
+ // run() is async, so anything that escapes it surfaces as an unhandled
10
+ // rejection: a raw stack trace, and on Node 18+ a hard crash. A wrapper whose
11
+ // whole job is to run someone else's command should fail in one line instead.
12
+ run().catch((err) => {
13
+ console.error(`\x1b[31m[vibeaudio] ${err && err.message ? err.message : err}\x1b[0m`);
14
+ if (process.env.VIBE_DEBUG) console.error(err);
15
+ process.exit(1);
16
+ });