agentvibes 4.6.8 → 5.1.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/.agentvibes/bmad-voice-map.json +104 -0
- package/.agentvibes/config.json +13 -12
- package/.agentvibes/copilot-sessions.log +4 -0
- package/.claude/audio/tracks/Drifting Down the Hall.mp3 +0 -0
- package/.claude/audio/tracks/Late Night Hip Hop Groove.mp3 +0 -0
- package/.claude/audio/tracks/Midnight Charleston Stomp.mp3 +0 -0
- package/.claude/audio/tracks/README.md +51 -52
- package/.claude/config/audio-effects-bmad.cfg +50 -0
- package/.claude/config/audio-effects.cfg +4 -4
- package/.claude/config/background-music-enabled.txt +1 -0
- package/.claude/config/personality.txt +1 -0
- package/.claude/hooks/play-tts-piper.sh +3 -1
- package/.claude/hooks/play-tts.sh +380 -301
- package/.claude/hooks/session-start-tts.sh +81 -81
- package/.claude/hooks-windows/audio-processor.ps1 +181 -0
- package/.claude/hooks-windows/play-tts-piper.ps1 +259 -245
- package/.claude/hooks-windows/play-tts.ps1 +28 -6
- package/.claude/hooks-windows/session-start-tts.ps1 +114 -114
- package/README.md +112 -6
- package/RELEASE_NOTES.md +83 -0
- package/bin/bmad-speak.js +16 -8
- package/mcp-server/server.py +15 -8
- package/package.json +1 -1
- package/src/console/app.js +899 -897
- package/src/console/footer-config.js +50 -50
- package/src/console/navigation.js +65 -65
- package/src/console/tabs/agents-tab.js +1899 -1886
- package/src/console/tabs/music-tab.js +1076 -1039
- package/src/console/tabs/placeholder-tab.js +81 -80
- package/src/console/tabs/settings-tab.js +941 -3988
- package/src/console/tabs/setup-tab.js +2071 -0
- package/src/console/tabs/voices-tab.js +1843 -1714
- package/src/console/widgets/format-utils.js +92 -89
- package/src/console/widgets/track-picker.js +325 -322
- package/src/installer.js +6147 -6092
- package/src/services/llm-provider-service.js +486 -0
- package/src/services/navigation-service.js +123 -123
- package/src/services/tts-engine-service.js +69 -0
- package/.claude/audio/tracks/dreamy_house_loop.mp3 +0 -0
- package/src/console/tabs/install-tab.js +0 -1081
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentVibes TUI Console — Navigation Service
|
|
3
|
-
* Story 6.2: Tab Bar & Global Keyboard Navigation
|
|
4
|
-
*
|
|
5
|
-
* Manages tab state, cycling, modal overlay state, and focus stack.
|
|
6
|
-
* Used by navigation.js (key bindings) and app.js (wiring).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** Ordered list of all tab IDs — used for cycling and validation */
|
|
10
|
-
export const TAB_ORDER = ['
|
|
11
|
-
|
|
12
|
-
export class NavigationService {
|
|
13
|
-
/**
|
|
14
|
-
* @param {string} [initialTab='settings'] - Tab to activate on launch
|
|
15
|
-
*/
|
|
16
|
-
constructor(initialTab = 'settings') {
|
|
17
|
-
this._activeTab = TAB_ORDER.includes(initialTab) ? initialTab : 'settings';
|
|
18
|
-
this._switchCallbacks = [];
|
|
19
|
-
this._focusStack = [];
|
|
20
|
-
this._modalOpen = false;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Tab navigation
|
|
25
|
-
|
|
26
|
-
/** Returns the currently active tab ID */
|
|
27
|
-
getActiveTab() {
|
|
28
|
-
return this._activeTab;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Switch to the given tab.
|
|
33
|
-
* Ignores invalid tab IDs. Fires all registered onSwitch callbacks.
|
|
34
|
-
* @param {string} tabId
|
|
35
|
-
*/
|
|
36
|
-
switchTab(tabId) {
|
|
37
|
-
if (!TAB_ORDER.includes(tabId)) return;
|
|
38
|
-
if (tabId === this._activeTab) return; // no-op: already on this tab
|
|
39
|
-
this._activeTab = tabId;
|
|
40
|
-
this._switchCallbacks.forEach(cb => cb(tabId));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Activate a tab unconditionally, bypassing the same-tab no-op guard.
|
|
45
|
-
* Used for initial UI setup: the constructor pre-sets _activeTab but
|
|
46
|
-
* onSwitch callbacks must still fire to render the initial state.
|
|
47
|
-
* @param {string} tabId
|
|
48
|
-
*/
|
|
49
|
-
forceActivate(tabId) {
|
|
50
|
-
if (!TAB_ORDER.includes(tabId)) return;
|
|
51
|
-
this._activeTab = tabId;
|
|
52
|
-
this._switchCallbacks.forEach(cb => cb(tabId));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Cycle to the next tab in TAB_ORDER, wrapping from last back to first.
|
|
57
|
-
*/
|
|
58
|
-
cycleTab() {
|
|
59
|
-
const idx = TAB_ORDER.indexOf(this._activeTab);
|
|
60
|
-
const nextIdx = (idx + 1) % TAB_ORDER.length;
|
|
61
|
-
this.switchTab(TAB_ORDER[nextIdx]);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Cycle to the previous tab in TAB_ORDER, wrapping from first back to last.
|
|
66
|
-
*/
|
|
67
|
-
cycleTabPrev() {
|
|
68
|
-
const idx = TAB_ORDER.indexOf(this._activeTab);
|
|
69
|
-
const prevIdx = (idx - 1 + TAB_ORDER.length) % TAB_ORDER.length;
|
|
70
|
-
this.switchTab(TAB_ORDER[prevIdx]);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Register a callback fired whenever the active tab changes.
|
|
75
|
-
* @param {(tabId: string) => void} callback
|
|
76
|
-
*/
|
|
77
|
-
onSwitch(callback) {
|
|
78
|
-
this._switchCallbacks.push(callback);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
// Modal state (story 6.4 will expand this)
|
|
83
|
-
|
|
84
|
-
/** Returns true if a modal is currently open */
|
|
85
|
-
isModalOpen() {
|
|
86
|
-
return this._modalOpen;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Open a modal. Sets modal-open state and calls the factory fn if provided.
|
|
91
|
-
* @param {Function|null} fn - Optional factory/callback invoked immediately
|
|
92
|
-
*/
|
|
93
|
-
openModal(fn) {
|
|
94
|
-
this._modalOpen = true;
|
|
95
|
-
fn?.();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Close the current modal, restoring modal-closed state */
|
|
99
|
-
closeModal() {
|
|
100
|
-
this._modalOpen = false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// ---------------------------------------------------------------------------
|
|
105
|
-
// Focus stack (story 7.6 will use this for button-level focus)
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Push a Blessed element onto the focus stack
|
|
109
|
-
* @param {object} element - Blessed widget
|
|
110
|
-
*/
|
|
111
|
-
pushFocus(element) {
|
|
112
|
-
this._focusStack.push(element);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Pop the last element from the focus stack.
|
|
117
|
-
* Returns undefined if the stack is empty.
|
|
118
|
-
* @returns {object|undefined}
|
|
119
|
-
*/
|
|
120
|
-
popFocus() {
|
|
121
|
-
return this._focusStack.pop();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI Console — Navigation Service
|
|
3
|
+
* Story 6.2: Tab Bar & Global Keyboard Navigation
|
|
4
|
+
*
|
|
5
|
+
* Manages tab state, cycling, modal overlay state, and focus stack.
|
|
6
|
+
* Used by navigation.js (key bindings) and app.js (wiring).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Ordered list of all tab IDs — used for cycling and validation */
|
|
10
|
+
export const TAB_ORDER = ['setup', 'settings', 'voices', 'music', 'agents', 'receiver', 'readme', 'help'];
|
|
11
|
+
|
|
12
|
+
export class NavigationService {
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} [initialTab='settings'] - Tab to activate on launch
|
|
15
|
+
*/
|
|
16
|
+
constructor(initialTab = 'settings') {
|
|
17
|
+
this._activeTab = TAB_ORDER.includes(initialTab) ? initialTab : 'settings';
|
|
18
|
+
this._switchCallbacks = [];
|
|
19
|
+
this._focusStack = [];
|
|
20
|
+
this._modalOpen = false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Tab navigation
|
|
25
|
+
|
|
26
|
+
/** Returns the currently active tab ID */
|
|
27
|
+
getActiveTab() {
|
|
28
|
+
return this._activeTab;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Switch to the given tab.
|
|
33
|
+
* Ignores invalid tab IDs. Fires all registered onSwitch callbacks.
|
|
34
|
+
* @param {string} tabId
|
|
35
|
+
*/
|
|
36
|
+
switchTab(tabId) {
|
|
37
|
+
if (!TAB_ORDER.includes(tabId)) return;
|
|
38
|
+
if (tabId === this._activeTab) return; // no-op: already on this tab
|
|
39
|
+
this._activeTab = tabId;
|
|
40
|
+
this._switchCallbacks.forEach(cb => cb(tabId));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Activate a tab unconditionally, bypassing the same-tab no-op guard.
|
|
45
|
+
* Used for initial UI setup: the constructor pre-sets _activeTab but
|
|
46
|
+
* onSwitch callbacks must still fire to render the initial state.
|
|
47
|
+
* @param {string} tabId
|
|
48
|
+
*/
|
|
49
|
+
forceActivate(tabId) {
|
|
50
|
+
if (!TAB_ORDER.includes(tabId)) return;
|
|
51
|
+
this._activeTab = tabId;
|
|
52
|
+
this._switchCallbacks.forEach(cb => cb(tabId));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Cycle to the next tab in TAB_ORDER, wrapping from last back to first.
|
|
57
|
+
*/
|
|
58
|
+
cycleTab() {
|
|
59
|
+
const idx = TAB_ORDER.indexOf(this._activeTab);
|
|
60
|
+
const nextIdx = (idx + 1) % TAB_ORDER.length;
|
|
61
|
+
this.switchTab(TAB_ORDER[nextIdx]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Cycle to the previous tab in TAB_ORDER, wrapping from first back to last.
|
|
66
|
+
*/
|
|
67
|
+
cycleTabPrev() {
|
|
68
|
+
const idx = TAB_ORDER.indexOf(this._activeTab);
|
|
69
|
+
const prevIdx = (idx - 1 + TAB_ORDER.length) % TAB_ORDER.length;
|
|
70
|
+
this.switchTab(TAB_ORDER[prevIdx]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Register a callback fired whenever the active tab changes.
|
|
75
|
+
* @param {(tabId: string) => void} callback
|
|
76
|
+
*/
|
|
77
|
+
onSwitch(callback) {
|
|
78
|
+
this._switchCallbacks.push(callback);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Modal state (story 6.4 will expand this)
|
|
83
|
+
|
|
84
|
+
/** Returns true if a modal is currently open */
|
|
85
|
+
isModalOpen() {
|
|
86
|
+
return this._modalOpen;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Open a modal. Sets modal-open state and calls the factory fn if provided.
|
|
91
|
+
* @param {Function|null} fn - Optional factory/callback invoked immediately
|
|
92
|
+
*/
|
|
93
|
+
openModal(fn) {
|
|
94
|
+
this._modalOpen = true;
|
|
95
|
+
fn?.();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Close the current modal, restoring modal-closed state */
|
|
99
|
+
closeModal() {
|
|
100
|
+
this._modalOpen = false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Focus stack (story 7.6 will use this for button-level focus)
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Push a Blessed element onto the focus stack
|
|
109
|
+
* @param {object} element - Blessed widget
|
|
110
|
+
*/
|
|
111
|
+
pushFocus(element) {
|
|
112
|
+
this._focusStack.push(element);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Pop the last element from the focus stack.
|
|
117
|
+
* Returns undefined if the stack is empty.
|
|
118
|
+
* @returns {object|undefined}
|
|
119
|
+
*/
|
|
120
|
+
popFocus() {
|
|
121
|
+
return this._focusStack.pop();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes — TTS Engine Service
|
|
3
|
+
*
|
|
4
|
+
* OS-aware TTS engine detection: enumerates available engines,
|
|
5
|
+
* checks binary availability, and reports installation status.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execFileSync } from 'node:child_process';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
|
|
12
|
+
const TTS_ENGINES = [
|
|
13
|
+
{
|
|
14
|
+
id: 'piper', name: 'Piper TTS', desc: 'Open-source, fast, many voices — recommended', native: false,
|
|
15
|
+
installCmd: process.platform === 'win32'
|
|
16
|
+
? 'winget install --id Rhasspy.Piper --accept-package-agreements --accept-source-agreements'
|
|
17
|
+
: process.platform === 'darwin'
|
|
18
|
+
? 'brew install piper'
|
|
19
|
+
: 'pip install piper-tts',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'soprano', name: 'Soprano TTS', desc: 'Web-based TTS service with premium voices', native: false,
|
|
23
|
+
installCmd: 'pip install soprano-tts',
|
|
24
|
+
},
|
|
25
|
+
{ id: 'sapi', name: 'Windows SAPI', desc: 'Built-in Windows speech — no install needed', native: true, platform: 'win32' },
|
|
26
|
+
{ id: 'macos-say', name: 'macOS Say', desc: 'Built-in macOS speech synthesis — no install needed', native: true, platform: 'darwin' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export function getAvailableEngines() {
|
|
30
|
+
return TTS_ENGINES.filter(e => !e.platform || e.platform === process.platform);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function checkEngineInstalled(engineId) {
|
|
34
|
+
const engine = TTS_ENGINES.find(e => e.id === engineId);
|
|
35
|
+
if (!engine) return false;
|
|
36
|
+
if (engine.native) return true; // Native engines are always available on their platform
|
|
37
|
+
|
|
38
|
+
// Check binary availability — soprano has two possible binaries
|
|
39
|
+
const binaryMap = { piper: ['piper'], soprano: ['soprano-tts', 'soprano-webui'] };
|
|
40
|
+
const binaries = binaryMap[engineId];
|
|
41
|
+
if (!binaries) return false;
|
|
42
|
+
|
|
43
|
+
for (const binary of binaries) {
|
|
44
|
+
try {
|
|
45
|
+
if (process.platform === 'win32') {
|
|
46
|
+
// Check Windows Piper location
|
|
47
|
+
if (engineId === 'piper') {
|
|
48
|
+
const localAppData = process.env.LOCALAPPDATA ||
|
|
49
|
+
(process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'AppData', 'Local') : null);
|
|
50
|
+
if (localAppData && fs.existsSync(path.join(localAppData, 'Programs', 'Piper', 'piper.exe'))) return true;
|
|
51
|
+
}
|
|
52
|
+
execFileSync('where', [binary], { stdio: 'ignore', timeout: 2000 });
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
execFileSync('which', [binary], { stdio: 'ignore', timeout: 2000 });
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
// try next binary
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getEngineStatuses() {
|
|
65
|
+
return getAvailableEngines().map(e => ({
|
|
66
|
+
...e,
|
|
67
|
+
installed: checkEngineInstalled(e.id),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
Binary file
|