vibeaudio 0.18.0 → 0.19.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/README.md +11 -4
- package/package.json +1 -1
- package/src/cli.js +68 -13
- package/src/interactive.js +2 -2
package/README.md
CHANGED
|
@@ -147,7 +147,7 @@ Want the music to follow what the agent is doing — calm while it reads, busies
|
|
|
147
147
|
vibe --reactive --install-hooks
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
Prefer a guided setup? Run `vibe` with nothing after it: a menu asks for your agent, genre and volume (press `p` to hear a genre first) and installs the hooks for you.
|
|
150
|
+
Prefer a guided setup? Run `vibe` with nothing after it: a menu asks for your agent, genre and volume (press `p` to hear a genre first) and installs the hooks for you. Installing hooks doesn't start the agent: open it whenever you like, and the music plays from your first prompt. ("Just launch it" and "This session only" do start it.)
|
|
151
151
|
|
|
152
152
|
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)
|
|
153
153
|
|
|
@@ -522,7 +522,7 @@ Each session is `working`, `stuck` (4 of its last 8 tool calls failed) or `waiti
|
|
|
522
522
|
{"v":1,"at":1791026130000,"event":"waiting","session":"…","project":"/work/api","tool":"Bash","status":"waiting"}
|
|
523
523
|
```
|
|
524
524
|
|
|
525
|
-
Events: `started`, `waiting`, `resumed`, `stuck`, `recovered`, `finished` (with `outcome`: `success` or `failure`), `interrupted`, `ended` (with `reason` when `vibe --stop` or `--uninstall-hooks` ended it). Every line also carries `status`, the machine's state after the event, so a consumer that only cares about the overall state can read that one field.
|
|
525
|
+
Events: `started`, `waiting`, `resumed`, `stuck`, `recovered`, `finished` (with `outcome`: `success` or `failure`), `interrupted`, `ended` (with `reason` when `vibe --stop` or `--uninstall-hooks` ended it) and `announced` (from [`vibe --announce`](#-beyond-agents-vibe---announce), with `text` and `outcome`, and no session). Every line also carries `status`, the machine's state after the event, so a consumer that only cares about the overall state can read that one field.
|
|
526
526
|
|
|
527
527
|
#### 📟 Status Bar & Desktop Integrations
|
|
528
528
|
|
|
@@ -563,11 +563,18 @@ The parentheses matter: Starship hides a group whose variables are empty, so the
|
|
|
563
563
|
vibe --events | jq --unbuffered -c 'select(.event=="waiting" or .event=="stuck") | {event, project}' \
|
|
564
564
|
| while read -r line; do curl -s -X POST -H 'Content-Type: application/json' -d "$line" https://example.com/hook; done
|
|
565
565
|
```
|
|
566
|
-
The `select` also drops the snapshot `--events` prints first (
|
|
566
|
+
The `select` also drops the snapshot `--events` prints first (its `event` is `state`), so only changes after you start it are sent: a session already waiting at that moment is not announced. For the current state, call `vibe --state` once.
|
|
567
|
+
|
|
568
|
+
**Home Assistant (or any smart light)** — send the machine's overall state on every change, and let the receiving side choose the colour. Home Assistant takes this as a [webhook trigger](https://www.home-assistant.io/docs/automation/trigger/#webhook-trigger); replace the URL and the id:
|
|
569
|
+
```bash
|
|
570
|
+
vibe --events | jq --unbuffered -c 'select(.event != "state") | {status, event, project}' \
|
|
571
|
+
| while read -r line; do curl -s -X POST -H 'Content-Type: application/json' -d "$line" http://homeassistant.local:8123/api/webhook/vibeaudio; done
|
|
572
|
+
```
|
|
573
|
+
`status` is `idle`, `working`, `stuck` or `waiting`, already the most urgent across all your sessions, so the automation is a plain switch on one field (for example amber for `waiting`, red for `stuck`, off for `idle`) and never has to count sessions itself. A web request goes out on every change, so point it at a local bridge rather than a metered service.
|
|
567
574
|
|
|
568
575
|
*Not covered yet: a macOS menu bar plugin (SwiftBar/xbar) and Linux desktop alerts. Neither has been run on a real setup, so neither is documented. `--notify` already raises a banner when a turn finishes or needs you.*
|
|
569
576
|
|
|
570
|
-
*Verified on macOS arm64 with Node 24, tmux 3.7 and Starship 1.26: the tmux and Starship segments showed `working`, `working ×2` and `waiting: web` and disappeared when idle; the `say` and webhook pipelines fired once per `waiting` event while the stream stayed open. Not run on Linux or Windows.*
|
|
577
|
+
*Verified on macOS arm64 with Node 24, tmux 3.7 and Starship 1.26 (the Home Assistant recipe: the request it sends, against a local stand-in server, was `{"status":"waiting","event":"waiting","project":"/work/api"}` on a permission dialog; not run against Home Assistant itself): the tmux and Starship segments showed `working`, `working ×2` and `waiting: web` and disappeared when idle; the `say` and webhook pipelines fired once per `waiting` event while the stream stayed open. Not run on Linux or Windows.*
|
|
571
578
|
|
|
572
579
|
The stream is a local file (`~/.vibeaudio/events.jsonl`, rotated at 256 KB), so it never leaves your machine. It keeps updating while you're muted, because a mute silences sound and a light isn't sound. The `v` field is the format version, and any breaking change will increment it.
|
|
573
580
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibeaudio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Procedural focus music while your AI coding tools (Claude Code, Codex, Cursor, Grok, Gemini, Copilot) think — a different arrangement per project.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vibeaudio": "bin/vibeaudio.js",
|
package/src/cli.js
CHANGED
|
@@ -102,7 +102,7 @@ Procedural focus music while your AI coding tools think.
|
|
|
102
102
|
--dry-run With --install-hooks: show what would change, write nothing
|
|
103
103
|
--uninstall-hooks Remove the hooks again, from every agent
|
|
104
104
|
-h, --help Show this help message
|
|
105
|
-
--version Show version
|
|
105
|
+
--version Show version (update with: npm i -g vibeaudio)
|
|
106
106
|
|
|
107
107
|
\x1b[1mSETTINGS:\x1b[0m
|
|
108
108
|
A genre or volume with no command after it is saved as your default:
|
|
@@ -115,15 +115,43 @@ Procedural focus music while your AI coding tools think.
|
|
|
115
115
|
\x1b[1m--here\x1b[0m setting beats the global one, so a one-off
|
|
116
116
|
\x1b[1mvibe --genre 8bit npm test\x1b[0m stays a one-off.
|
|
117
117
|
|
|
118
|
+
\x1b[1mINTEGRATIONS:\x1b[0m
|
|
119
|
+
Every agent's state, as one stream you can build on (lights, banners, webhooks):
|
|
120
|
+
|
|
121
|
+
vibe --events \x1b[90m# the current state, then one JSON line per change\x1b[0m
|
|
122
|
+
vibe --state \x1b[90m# one snapshot: idle, working, stuck or waiting\x1b[0m
|
|
123
|
+
vibe --statusline \x1b[90m# one plain line for tmux or a prompt\x1b[0m
|
|
124
|
+
|
|
125
|
+
Events: started, waiting, resumed, stuck, recovered, finished, interrupted, ended, announced.
|
|
126
|
+
Recipes (tmux, Starship, voice, webhook, Home Assistant): https://kiril6.github.io/vibeaudio/
|
|
127
|
+
|
|
128
|
+
\x1b[1mUNINSTALL:\x1b[0m
|
|
129
|
+
In this order, so no agent is left running a hook that points at nothing:
|
|
130
|
+
|
|
131
|
+
vibe --uninstall-hooks \x1b[90m# 1. unwire every agent, and stop any player still running\x1b[0m
|
|
132
|
+
npm rm -g vibeaudio \x1b[90m# 2. remove the CLI\x1b[0m
|
|
133
|
+
rm -rf ~/.vibeaudio \x1b[90m# 3. optional: cached audio, saved settings, state\x1b[0m
|
|
134
|
+
|
|
118
135
|
\x1b[1mENVIRONMENT:\x1b[0m
|
|
119
136
|
VIBE_GENRE=<name> Override the saved genre for this shell
|
|
120
137
|
VIBE_VOLUME=<5-100> Override the saved volume for this shell
|
|
121
138
|
VIBE_CHIME_VOLUME=<5-100> Override the saved chime volume for this shell
|
|
122
139
|
VIBE_GRACE_MS=<ms> Override the saved grace window, in ms
|
|
123
140
|
VIBE_MUSIC=<on|off> Override the saved music setting for this shell
|
|
141
|
+
VIBE_NOTIFY=<on|off> Override the saved desktop-banner setting for this shell
|
|
124
142
|
VIBE_SEED=<n> Pin the arrangement instead of deriving it from the directory
|
|
125
143
|
VIBE_DISABLE=1 Mute automatic playback without uninstalling anything
|
|
126
144
|
VIBE_NO_UPDATE_CHECK=1 Never check npm for a newer version
|
|
145
|
+
|
|
146
|
+
\x1b[1mNO SOUND?\x1b[0m
|
|
147
|
+
vibe --doctor \x1b[90m# checks the setup; each problem comes with its fix\x1b[0m
|
|
148
|
+
vibe --status \x1b[90m# shows whether you are muted, and what is installed\x1b[0m
|
|
149
|
+
vibe --unmute \x1b[90m# clears a mute\x1b[0m
|
|
150
|
+
|
|
151
|
+
\x1b[1mMORE:\x1b[0m
|
|
152
|
+
Settings and state: ~/.vibeaudio
|
|
153
|
+
Docs: https://kiril6.github.io/vibeaudio/
|
|
154
|
+
Problems and ideas: https://github.com/kiril6/vibeaudio/issues
|
|
127
155
|
`);
|
|
128
156
|
printUpdateNotice();
|
|
129
157
|
}
|
|
@@ -717,6 +745,7 @@ function installHookTargets(ids, genre, volume, reactive, dryRun = false, typed
|
|
|
717
745
|
const live = ids.filter((id) => hooks.TARGETS[id].liveReload !== false).map((id) => hooks.TARGETS[id].name);
|
|
718
746
|
if (live.length) console.log(` ${live.join(", ")}: takes effect on your next prompt - no restart needed.`);
|
|
719
747
|
console.log(` \x1b[90mChange the sound any time with: vibe --genre <name> --volume <n> — no reinstall.\x1b[0m`);
|
|
748
|
+
console.log(` \x1b[90mCheck it with vibe --status · quiet for a call: vibe --mute · everything else: vibe --help\x1b[0m`);
|
|
720
749
|
if (ids.length > 1) {
|
|
721
750
|
console.log(` \x1b[90mOne player is shared: whichever agent you prompt last owns the music.\x1b[0m`);
|
|
722
751
|
}
|
|
@@ -1659,6 +1688,42 @@ function executeCommand(cmdArgs, genre, volume, chimeVolume, grace = DEFAULT_GRA
|
|
|
1659
1688
|
process.on("SIGTERM", () => relaySignal("SIGTERM"));
|
|
1660
1689
|
}
|
|
1661
1690
|
|
|
1691
|
+
/**
|
|
1692
|
+
* Acts on what the menu returned, and says whether to launch the tool.
|
|
1693
|
+
*
|
|
1694
|
+
* Choosing hooks is setup: it installs them and stops. Nothing is started, so
|
|
1695
|
+
* someone running several agents can set one up now and open it when they
|
|
1696
|
+
* like - the music plays from their first prompt either way. Only "just launch
|
|
1697
|
+
* it" and "this session only" go on to start the tool. A failed install is an
|
|
1698
|
+
* error with a non-zero exit, not a launch without the hooks that were asked for.
|
|
1699
|
+
*/
|
|
1700
|
+
function applyMenuSelection(selection, volume) {
|
|
1701
|
+
if (!selection.installHooks) return true;
|
|
1702
|
+
const hooks = require("./hooks");
|
|
1703
|
+
try {
|
|
1704
|
+
installHookTargets([selection.hookTarget], selection.genre, volume, selection.reactive, false,
|
|
1705
|
+
{ genre: selection.genre, volume });
|
|
1706
|
+
} catch (err) {
|
|
1707
|
+
console.error(`\x1b[31m[vibeaudio] ${err.message}\x1b[0m`);
|
|
1708
|
+
process.exit(1);
|
|
1709
|
+
}
|
|
1710
|
+
const saved = loadConfig();
|
|
1711
|
+
// Everything VibeAudio is hooked into on this machine, not just this run's
|
|
1712
|
+
// pick: with several agents, "which ones are set up?" is the question, and
|
|
1713
|
+
// which one to open first is the user's call.
|
|
1714
|
+
const hooked = Object.entries(hooks.TARGETS).filter(([id]) => hooks.userHooksInstalled(null, id));
|
|
1715
|
+
const names = hooked.map(([, t]) => t.name).join(", ");
|
|
1716
|
+
const cmds = hooked.map(([, t]) => t.cmd).join(", ");
|
|
1717
|
+
console.log(
|
|
1718
|
+
`\n\x1b[32m✔ VibeAudio v${pkg.version} is hooked into: ${names} (${saved.genre} @ ${saved.volume}%).\x1b[0m ` +
|
|
1719
|
+
`Nothing was started: open ${hooked.length > 1 ? "any of them" : "it"} whenever you like (\x1b[1m${cmds}\x1b[0m), and the music plays from your first prompt.\n` +
|
|
1720
|
+
` \x1b[90mMore agents? Run vibe again for each, or vibe --install-hooks to set up every one it finds.\x1b[0m\n` +
|
|
1721
|
+
` \x1b[90mStay current: run npm i -g ${pkg.name} now and then (hooks keep working); vibe --status tells you when a newer version is out.\x1b[0m\n`
|
|
1722
|
+
);
|
|
1723
|
+
printUpdateNotice();
|
|
1724
|
+
return false;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1662
1727
|
async function run() {
|
|
1663
1728
|
const {
|
|
1664
1729
|
genre,
|
|
@@ -1875,18 +1940,7 @@ async function run() {
|
|
|
1875
1940
|
}
|
|
1876
1941
|
|
|
1877
1942
|
const chosenVol = selection.volume !== undefined ? selection.volume : volume;
|
|
1878
|
-
|
|
1879
|
-
if (selection.installHooks) {
|
|
1880
|
-
// The install can refuse (npx checkout) or abort (malformed settings).
|
|
1881
|
-
// Either way, say so and still launch the tool the user asked for -
|
|
1882
|
-
// they came here to start an agent, not to configure one.
|
|
1883
|
-
try {
|
|
1884
|
-
installHookTargets([selection.hookTarget], selection.genre, chosenVol, selection.reactive, false,
|
|
1885
|
-
{ genre: selection.genre, volume: chosenVol });
|
|
1886
|
-
} catch (err) {
|
|
1887
|
-
console.error(`\x1b[31m[vibeaudio] ${err.message}\x1b[0m`);
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1943
|
+
if (!applyMenuSelection(selection, chosenVol)) return;
|
|
1890
1944
|
|
|
1891
1945
|
return executeCommand(selection.cmd, selection.genre, chosenVol, chimeVolume, grace, noChime, noHud, music);
|
|
1892
1946
|
}
|
|
@@ -1898,6 +1952,7 @@ module.exports = {
|
|
|
1898
1952
|
run,
|
|
1899
1953
|
parseArgs,
|
|
1900
1954
|
hooksAlreadyCover,
|
|
1955
|
+
applyMenuSelection,
|
|
1901
1956
|
previewAudioFile,
|
|
1902
1957
|
windowsCommandNeedsShell
|
|
1903
1958
|
};
|
package/src/interactive.js
CHANGED
|
@@ -263,7 +263,7 @@ const DELIVERY_FRESH = [
|
|
|
263
263
|
{
|
|
264
264
|
id: "hooks",
|
|
265
265
|
name: "Agent hooks",
|
|
266
|
-
desc: "Music follows the agent's thinking.
|
|
266
|
+
desc: "Music follows the agent's thinking. Installs, then returns to your shell: nothing starts"
|
|
267
267
|
},
|
|
268
268
|
{
|
|
269
269
|
id: "wrapper",
|
|
@@ -285,7 +285,7 @@ const DELIVERY_INSTALLED = [
|
|
|
285
285
|
{
|
|
286
286
|
id: "hooks",
|
|
287
287
|
name: "Reconfigure the hooks",
|
|
288
|
-
desc: "Pick a new genre, volume or reactive setting"
|
|
288
|
+
desc: "Pick a new genre, volume or reactive setting. Nothing starts"
|
|
289
289
|
}
|
|
290
290
|
];
|
|
291
291
|
|