agentvibes 3.5.9 → 4.0.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/bmad-voices-enabled.flag +0 -0
- package/.agentvibes/bmad/bmad-voices.md +69 -0
- package/.claude/config/audio-effects.cfg +1 -1
- package/.claude/config/background-music-position.txt +1 -27
- package/.claude/github-star-reminder.txt +1 -1
- package/.claude/hooks/audio-processor.sh +32 -17
- package/.claude/hooks/bmad-speak-enhanced.sh +5 -5
- package/.claude/hooks/bmad-speak.sh +4 -4
- package/.claude/hooks/bmad-voice-manager.sh +8 -8
- package/.claude/hooks/clawdbot-receiver-SECURE.sh +23 -25
- package/.claude/hooks/clawdbot-receiver.sh +28 -4
- package/.claude/hooks/language-manager.sh +1 -1
- package/.claude/hooks/path-resolver.sh +60 -0
- package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +90 -0
- package/.claude/hooks/play-tts-piper.sh +82 -24
- package/.claude/hooks/play-tts-ssh-remote.sh +13 -15
- package/.claude/hooks/play-tts.sh +16 -5
- package/.claude/hooks/session-start-tts.sh +26 -56
- package/.claude/hooks/soprano-gradio-synth.py +1 -1
- package/.claude/hooks/verbosity-manager.sh +10 -4
- package/.claude/settings.json +1 -1
- package/CLAUDE.md +129 -104
- package/README.md +418 -10
- package/RELEASE_NOTES.md +60 -1036
- package/bin/agentvibes-voice-browser.js +1827 -0
- package/bin/agentvibes.js +100 -0
- package/mcp-server/server.py +67 -3
- package/package.json +11 -2
- package/src/console/app.js +806 -0
- package/src/console/audio-env.js +123 -0
- package/src/console/brand-colors.js +13 -0
- package/src/console/footer-config.js +42 -0
- package/src/console/modals/.gitkeep +0 -0
- package/src/console/modals/modal-overlay.js +247 -0
- package/src/console/navigation.js +60 -0
- package/src/console/tabs/.gitkeep +0 -0
- package/src/console/tabs/agents-tab.js +369 -0
- package/src/console/tabs/help-tab.js +261 -0
- package/src/console/tabs/install-tab.js +990 -0
- package/src/console/tabs/music-tab.js +997 -0
- package/src/console/tabs/placeholder-tab.js +45 -0
- package/src/console/tabs/readme-tab.js +267 -0
- package/src/console/tabs/settings-tab.js +3949 -0
- package/src/console/tabs/voices-tab.js +1574 -0
- package/src/installer/music-file-input.js +304 -0
- package/src/installer.js +1353 -676
- package/src/services/.gitkeep +0 -0
- package/src/services/agent-voice-store.js +163 -0
- package/src/services/config-service.js +240 -0
- package/src/services/navigation-service.js +123 -0
- package/src/services/provider-service.js +132 -0
- package/src/services/verbosity-service.js +157 -0
- package/src/utils/audio-duration-validator.js +298 -0
- package/src/utils/audio-format-validator.js +277 -0
- package/src/utils/dependency-checker.js +3 -3
- package/src/utils/file-ownership-verifier.js +358 -0
- package/src/utils/music-file-validator.js +275 -0
- package/src/utils/preview-list-prompt.js +136 -0
- package/src/utils/provider-validator.js +144 -132
- package/src/utils/secure-music-storage.js +412 -0
- package/templates/agentvibes-receiver.sh +11 -7
- package/voice-assignments.json +8245 -0
- package/.claude/config/background-music-volume.txt +0 -1
- package/.claude/config/background-music.cfg +0 -1
- package/.claude/config/background-music.txt +0 -1
- package/.claude/config/tts-speech-rate.txt +0 -1
- package/.claude/config/tts-verbosity.txt +0 -1
- package/.claude/hooks/bmad-party-manager.sh +0 -225
- package/.claude/hooks/stop.sh +0 -38
- package/.claude/piper-voices-dir.txt +0 -1
- package/.mcp.json +0 -34
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI Console — Placeholder Tab Component
|
|
3
|
+
* Story 6.2: Tab Bar & Global Keyboard Navigation
|
|
4
|
+
*
|
|
5
|
+
* Creates a stub content box for each tab ID.
|
|
6
|
+
* These are replaced by real tab implementations in Epics 7-11.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import blessed from 'blessed';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a hidden placeholder box for a tab, appended into the content area.
|
|
13
|
+
*
|
|
14
|
+
* @param {object} contentArea - Blessed box to append into (this.contentArea from app.js)
|
|
15
|
+
* @param {string} label - Human-readable tab name for display (e.g. 'Settings')
|
|
16
|
+
* @returns {object} The created Blessed box widget
|
|
17
|
+
*/
|
|
18
|
+
export function createPlaceholderTab(contentArea, label) {
|
|
19
|
+
const box = blessed.box({
|
|
20
|
+
parent: contentArea,
|
|
21
|
+
top: 0,
|
|
22
|
+
left: 0,
|
|
23
|
+
width: '100%',
|
|
24
|
+
height: '100%',
|
|
25
|
+
content: `{center}{bold}${label}{/bold}{/center}\n\n{center}Coming in a future story...{/center}`,
|
|
26
|
+
tags: true,
|
|
27
|
+
hidden: true,
|
|
28
|
+
style: {
|
|
29
|
+
fg: '#90a4ae',
|
|
30
|
+
bg: '#0a0e1a',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return box;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Map of tabId → display label for all 7 tabs */
|
|
38
|
+
export const TAB_DISPLAY_LABELS = {
|
|
39
|
+
settings: 'Settings',
|
|
40
|
+
voices: 'Voices',
|
|
41
|
+
music: 'Music',
|
|
42
|
+
readme: 'Readme',
|
|
43
|
+
help: 'Help',
|
|
44
|
+
install: 'Install',
|
|
45
|
+
};
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentVibes TUI Console — Readme Tab
|
|
3
|
+
* Epic 13: Story 13.2
|
|
4
|
+
*
|
|
5
|
+
* Implements the Tab Component Contract:
|
|
6
|
+
* createReadmeTab(screen, services) → { box, show, hide, onFocus, onBlur, getFooterText, getFooterColor }
|
|
7
|
+
*
|
|
8
|
+
* Features: renders README.md with styled markdown, scrolling, search.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
|
|
14
|
+
const IS_TEST = process.env.AGENTVIBES_TEST_MODE === 'true';
|
|
15
|
+
|
|
16
|
+
let blessed;
|
|
17
|
+
if (!IS_TEST) {
|
|
18
|
+
const { default: b } = await import('blessed');
|
|
19
|
+
blessed = b;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
const COLORS = {
|
|
25
|
+
contentBg: '#0a0e1a',
|
|
26
|
+
h1Fg: '#69f0ae', // Green — H1
|
|
27
|
+
h2Fg: '#82b1ff', // Blue — H2
|
|
28
|
+
h3Fg: '#80d8ff', // Light blue — H3
|
|
29
|
+
codeFg: '#ffff00', // Yellow — code spans
|
|
30
|
+
boldFg: '#e3f2fd', // Bright — bold text
|
|
31
|
+
quoteFg: '#90a4ae', // Gray — blockquotes
|
|
32
|
+
labelFg: '#e3f2fd',
|
|
33
|
+
borderFg: '#455a64',
|
|
34
|
+
footerBg: '#455a64', // Dark gray — Readme tab footer
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const FOOTER_TEXT = '[↑↓/jk] Scroll [PgUp/PgDn] Page [/] Search [S/V/M/A/R] Tab [Q] Quit';
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Markdown renderer (story 13.2)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Render a single markdown line to blessed tagged string.
|
|
44
|
+
* Handles: H1/H2/H3, bold (**), code spans (`), blockquotes (>), plain text.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} line
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
export function renderMarkdownLine(line) {
|
|
50
|
+
// H1
|
|
51
|
+
if (/^# /.test(line)) {
|
|
52
|
+
const text = line.replace(/^# /, '');
|
|
53
|
+
return `{bold}{${COLORS.h1Fg}-fg}${text}{/${COLORS.h1Fg}-fg}{/bold}`;
|
|
54
|
+
}
|
|
55
|
+
// H2
|
|
56
|
+
if (/^## /.test(line)) {
|
|
57
|
+
const text = line.replace(/^## /, '');
|
|
58
|
+
return `{bold}{${COLORS.h2Fg}-fg}${text}{/${COLORS.h2Fg}-fg}{/bold}`;
|
|
59
|
+
}
|
|
60
|
+
// H3
|
|
61
|
+
if (/^### /.test(line)) {
|
|
62
|
+
const text = line.replace(/^### /, '');
|
|
63
|
+
return `{${COLORS.h3Fg}-fg}${text}{/${COLORS.h3Fg}-fg}`;
|
|
64
|
+
}
|
|
65
|
+
// Blockquote
|
|
66
|
+
if (/^> /.test(line)) {
|
|
67
|
+
const text = line.replace(/^> /, '');
|
|
68
|
+
return `{${COLORS.quoteFg}-fg}│ ${text}{/${COLORS.quoteFg}-fg}`;
|
|
69
|
+
}
|
|
70
|
+
// Horizontal rule
|
|
71
|
+
if (/^---+$/.test(line.trim())) {
|
|
72
|
+
return `{${COLORS.quoteFg}-fg}${'─'.repeat(66)}{/${COLORS.quoteFg}-fg}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Inline: code spans, bold
|
|
76
|
+
let result = line;
|
|
77
|
+
// Escape existing blessed tags (literal braces in content)
|
|
78
|
+
// Code spans: `text`
|
|
79
|
+
result = result.replace(/`([^`]+)`/g, `{${COLORS.codeFg}-fg}$1{/${COLORS.codeFg}-fg}`);
|
|
80
|
+
// Bold: **text**
|
|
81
|
+
result = result.replace(/\*\*([^*]+)\*\*/g, `{bold}$1{/bold}`);
|
|
82
|
+
// Italic: *text*
|
|
83
|
+
result = result.replace(/\*([^*]+)\*/g, `{${COLORS.h3Fg}-fg}$1{/${COLORS.h3Fg}-fg}`);
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Render full markdown content to blessed tagged multi-line string.
|
|
90
|
+
* @param {string} markdown
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
93
|
+
export function renderMarkdown(markdown) {
|
|
94
|
+
return markdown
|
|
95
|
+
.split('\n')
|
|
96
|
+
.map(line => renderMarkdownLine(line))
|
|
97
|
+
.join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
function createTestStub() {
|
|
103
|
+
return {
|
|
104
|
+
box: {},
|
|
105
|
+
show: () => {},
|
|
106
|
+
hide: () => {},
|
|
107
|
+
onFocus: () => {},
|
|
108
|
+
onBlur: () => {},
|
|
109
|
+
getFooterText: () => FOOTER_TEXT,
|
|
110
|
+
getFooterColor: () => COLORS.footerBg,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create the Readme tab component.
|
|
118
|
+
*
|
|
119
|
+
* @param {object} screen - Blessed screen instance
|
|
120
|
+
* @param {object} services
|
|
121
|
+
* @returns {{ box, show, hide, onFocus, onBlur, getFooterText, getFooterColor }}
|
|
122
|
+
*/
|
|
123
|
+
export function createReadmeTab(screen, services) {
|
|
124
|
+
if (IS_TEST) return createTestStub();
|
|
125
|
+
|
|
126
|
+
const { focusMainTabBar } = services;
|
|
127
|
+
|
|
128
|
+
// -------------------------------------------------------------------------
|
|
129
|
+
// Container
|
|
130
|
+
|
|
131
|
+
const box = blessed.box({
|
|
132
|
+
parent: screen,
|
|
133
|
+
top: 4,
|
|
134
|
+
left: 0,
|
|
135
|
+
width: '100%',
|
|
136
|
+
bottom: 2,
|
|
137
|
+
hidden: true,
|
|
138
|
+
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
139
|
+
border: { type: 'line' },
|
|
140
|
+
borderStyle: { fg: COLORS.borderFg },
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// -------------------------------------------------------------------------
|
|
144
|
+
// Load README.md
|
|
145
|
+
|
|
146
|
+
function _loadReadme() {
|
|
147
|
+
const candidates = [
|
|
148
|
+
path.resolve(process.cwd(), 'README.md'),
|
|
149
|
+
path.resolve(process.cwd(), 'readme.md'),
|
|
150
|
+
];
|
|
151
|
+
for (const p of candidates) {
|
|
152
|
+
if (fs.existsSync(p)) {
|
|
153
|
+
try {
|
|
154
|
+
return fs.readFileSync(p, 'utf8');
|
|
155
|
+
} catch {
|
|
156
|
+
// Unreadable
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return '# README\n\n*(No README.md found in current directory)*';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// -------------------------------------------------------------------------
|
|
164
|
+
// Scrollable content
|
|
165
|
+
|
|
166
|
+
const scrollBox = blessed.box({
|
|
167
|
+
parent: box,
|
|
168
|
+
top: 1,
|
|
169
|
+
left: 2,
|
|
170
|
+
width: '96%',
|
|
171
|
+
bottom: 4,
|
|
172
|
+
scrollable: true,
|
|
173
|
+
alwaysScroll: true,
|
|
174
|
+
tags: true,
|
|
175
|
+
keys: true,
|
|
176
|
+
vi: true,
|
|
177
|
+
mouse: true,
|
|
178
|
+
scrollbar: { ch: '│', style: { fg: COLORS.borderFg } },
|
|
179
|
+
content: '',
|
|
180
|
+
style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Scroll indicator
|
|
184
|
+
const scrollIndicator = blessed.text({
|
|
185
|
+
parent: box,
|
|
186
|
+
bottom: 2,
|
|
187
|
+
right: 2,
|
|
188
|
+
content: '',
|
|
189
|
+
tags: true,
|
|
190
|
+
style: { fg: COLORS.quoteFg, bg: COLORS.contentBg },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// -------------------------------------------------------------------------
|
|
194
|
+
// Render
|
|
195
|
+
|
|
196
|
+
function refreshContent() {
|
|
197
|
+
const markdown = _loadReadme();
|
|
198
|
+
const rendered = renderMarkdown(markdown);
|
|
199
|
+
scrollBox.setContent(rendered);
|
|
200
|
+
scrollIndicator.setContent('{#607d8b-fg}↓ Scroll for more content ↓{/#607d8b-fg}');
|
|
201
|
+
screen.render();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Scroll events update indicator
|
|
205
|
+
scrollBox.on('scroll', () => {
|
|
206
|
+
const atBottom = scrollBox.getScrollPerc() >= 99;
|
|
207
|
+
scrollIndicator.setContent(
|
|
208
|
+
atBottom ? '' : '{#607d8b-fg}↓ Scroll for more content ↓{/#607d8b-fg}'
|
|
209
|
+
);
|
|
210
|
+
screen.render();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// PgUp/PgDn
|
|
214
|
+
scrollBox.key(['pageup'], () => {
|
|
215
|
+
scrollBox.scroll(-10);
|
|
216
|
+
screen.render();
|
|
217
|
+
});
|
|
218
|
+
scrollBox.key(['pagedown'], () => {
|
|
219
|
+
scrollBox.scroll(10);
|
|
220
|
+
screen.render();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// [↑] at top of content → jump to main header tab bar
|
|
224
|
+
scrollBox.key(['up'], () => {
|
|
225
|
+
if (scrollBox.getScroll() === 0 && typeof focusMainTabBar === 'function') {
|
|
226
|
+
focusMainTabBar();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Escape → return to header tab bar
|
|
231
|
+
scrollBox.key(['escape'], () => {
|
|
232
|
+
if (typeof focusMainTabBar === 'function') { focusMainTabBar(); screen.render(); }
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// -------------------------------------------------------------------------
|
|
236
|
+
// Tab Component Contract
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
box,
|
|
240
|
+
|
|
241
|
+
show() {
|
|
242
|
+
box.show();
|
|
243
|
+
refreshContent();
|
|
244
|
+
screen.render();
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
hide() {
|
|
248
|
+
box.hide();
|
|
249
|
+
screen.render();
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
onFocus() {
|
|
253
|
+
scrollBox.focus();
|
|
254
|
+
screen.render();
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
onBlur() {},
|
|
258
|
+
|
|
259
|
+
getFooterText() {
|
|
260
|
+
return FOOTER_TEXT;
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
getFooterColor() {
|
|
264
|
+
return COLORS.footerBg;
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|