agentvibes 4.6.5 → 4.6.6

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.
@@ -1 +1 @@
1
- 20260329
1
+ 20260403
@@ -1,241 +1,237 @@
1
- ---
2
- name: agentvibes
3
- description: 🎤 AgentVibes Voice Management - Manage your text-to-speech voices across multiple providers (Piper TTS, Piper, macOS Say). Switch voices, preview audio, manage providers, add custom voices, and control TTS output.
4
- ---
5
-
6
- # 🎤 AgentVibes Voice Management
7
-
8
- Manage your text-to-speech voices across multiple providers (Piper TTS, Piper, macOS Say).
9
-
10
- ---
11
-
12
- ## Available Commands
13
-
14
- ### Voice Control
15
-
16
- #### /agent-vibes:mute
17
- Mute all TTS output (persists across sessions)
18
-
19
- - Creates a mute flag that silences all voice output
20
- - Shows 🔇 indicator when TTS would have played
21
-
22
- ```bash
23
- /agent-vibes:mute
24
- ```
25
-
26
- #### /agent-vibes:unmute
27
- Unmute TTS output
28
-
29
- - Removes mute flag and restores voice output
30
-
31
- ```bash
32
- /agent-vibes:unmute
33
- ```
34
-
35
- #### /agent-vibes:list [first|last] [N]
36
- List all available voices, with optional filtering
37
-
38
- ```bash
39
- /agent-vibes:list # Show all voices
40
- /agent-vibes:list first 5 # Show first 5 voices
41
- /agent-vibes:list last 3 # Show last 3 voices
42
- ```
43
-
44
- #### /agent-vibes:preview [first|last] [N]
45
- Preview voices by playing audio samples
46
-
47
- ```bash
48
- /agent-vibes:preview # Preview first 3 voices
49
- /agent-vibes:preview 5 # Preview first 5 voices
50
- /agent-vibes:preview last 5 # Preview last 5 voices
51
- ```
52
-
53
- #### /agent-vibes:switch <voice_name>
54
- Switch to a different default voice
55
-
56
- ```bash
57
- /agent-vibes:switch en_US-amy-medium
58
- /agent-vibes:switch en_GB-alan-medium
59
- /agent-vibes:switch fr_FR-siwis-medium
60
- ```
61
-
62
- #### /agent-vibes:get
63
- Display the currently selected voice
64
-
65
- ```bash
66
- /agent-vibes:get
67
- ```
68
-
69
- #### /agent-vibes:add <name> <voice_id>
70
- Add a new custom voice from your Piper TTS account
71
-
72
- ```bash
73
- /agent-vibes:add "My Voice" abc123xyz456
74
- ```
75
-
76
- See [Getting Voice IDs](#getting-voice-ids-piper-tts) section below.
77
-
78
- #### /agent-vibes:replay [N]
79
- Replay recently played TTS audio
80
-
81
- ```bash
82
- /agent-vibes:replay # Replay last audio
83
- /agent-vibes:replay 1 # Replay most recent
84
- /agent-vibes:replay 2 # Replay second-to-last
85
- /agent-vibes:replay 3 # Replay third-to-last
86
- ```
87
-
88
- Keeps last 10 audio files in history.
89
-
90
- #### /agent-vibes:set-pretext <word>
91
- Set a prefix word/phrase for all TTS messages
92
-
93
- ```bash
94
- /agent-vibes:set-pretext AgentVibes # All TTS starts with "AgentVibes:"
95
- /agent-vibes:set-pretext "Project Alpha" # Custom phrase
96
- /agent-vibes:set-pretext "" # Clear pretext
97
- ```
98
-
99
- Saved locally in `.agentvibes/config/agentvibes.json`
100
-
101
- ---
102
-
103
- ## Provider Management
104
-
105
- #### /agent-vibes:provider list
106
- Show all available TTS providers
107
-
108
- ```bash
109
- /agent-vibes:provider list
110
- ```
111
-
112
- #### /agent-vibes:provider switch <name>
113
- Switch between providers
114
-
115
- ```bash
116
- /agent-vibes:provider switch piper # Piper TTS - Free, offline, 50+ voices
117
- /agent-vibes:provider switch macos # macOS Say - Native macOS voices (Mac only)
118
- ```
119
-
120
- #### /agent-vibes:provider info <name>
121
- Get details about a specific provider
122
-
123
- ```bash
124
- /agent-vibes:provider info piper
125
- /agent-vibes:provider info macos
126
- ```
127
-
128
- ---
129
-
130
- ## Providers
131
-
132
- | Provider | Platform | Cost | Voices | Quality |
133
- |----------|----------|------|--------|---------|
134
- | **Piper TTS** | All platforms (Linux, macOS, WSL) | Free | 50+ in 30+ languages | ⭐⭐⭐⭐ |
135
- | **macOS Say** | macOS only | Free (built-in) | 100+ system voices | ⭐⭐⭐⭐ |
136
-
137
- **On macOS**, the native `say` provider is automatically detected and recommended!
138
-
139
- ---
140
-
141
- ## Getting Voice IDs (Piper TTS)
142
-
143
- To add your own custom Piper TTS voices:
144
-
145
- 1. Go to https://piper.io/app/voice-library
146
- 2. Select or create a voice
147
- 3. Copy the voice ID (15-30 character alphanumeric string)
148
- 4. Use `/agent-vibes:add` to add it:
149
-
150
- ```bash
151
- /agent-vibes:add "My Custom Voice" xyz789abc123def456
152
- ```
153
-
154
- ---
155
-
156
- ## Default Voices
157
-
158
- ### Piper TTS (Free & Offline)
159
-
160
- **English (US):**
161
- - en_US-lessac-medium (default male voice)
162
- - en_US-amy-medium (friendly female)
163
- - en_US-ryan-high (high quality male)
164
- - en_US-libritts-high (multiple speakers)
165
-
166
- **English (UK):**
167
- - en_GB-alan-medium (British male)
168
- - en_GB-jenny_dioco-medium (British female)
169
-
170
- **Romance Languages:**
171
- - es_ES-davefx-medium (Spanish - Spain)
172
- - es_MX-claude-high (Spanish - Mexico)
173
- - fr_FR-siwis-medium (French female)
174
- - fr_FR-gilles-low (French male)
175
- - it_IT-riccardo-x_low (Italian male)
176
- - pt_BR-faber-medium (Portuguese - Brazilian)
177
-
178
- **Germanic Languages:**
179
- - de_DE-thorsten-medium (German male)
180
- - de_DE-eva_k-x_low (German female)
181
-
182
- **Asian Languages:**
183
- - ja_JP-ayanami-medium (Japanese female)
184
- - zh_CN-huayan-x_low (Chinese female)
185
- - ko_KR-kss-medium (Korean female)
186
-
187
- ### macOS Say (100+ Built-in)
188
- - Samantha
189
- - Alex
190
- - Daniel
191
- - Victoria
192
- - Karen
193
- - Moira
194
- - And 100+ more system voices
195
-
196
- ---
197
-
198
- ## Quick Examples
199
-
200
- ### Switch to a different voice
201
- ```bash
202
- /agent-vibes:switch en_US-lessac-medium # Clear male voice
203
- /agent-vibes:switch en_US-ryan-high # High quality male
204
- /agent-vibes:switch en_GB-alan-medium # British male
205
- ```
206
-
207
- ### Preview before choosing
208
- ```bash
209
- /agent-vibes:preview 5 # Preview first 5 voices
210
- /agent-vibes:preview last 3 # Preview last 3 voices
211
- ```
212
-
213
- ### Add your custom Piper voice
214
- ```bash
215
- /agent-vibes:add "My Voice" abc123xyz456
216
- /agent-vibes:switch My Voice
217
- ```
218
-
219
- ### Switch providers
220
- ```bash
221
- /agent-vibes:provider switch macos # Use native macOS voices
222
- /agent-vibes:provider switch piper # Switch back to Piper
223
- ```
224
-
225
- ### Mute/Unmute
226
- ```bash
227
- /agent-vibes:mute # Silent mode
228
- /agent-vibes:unmute # Restore voice
229
- ```
230
-
231
- ---
232
-
233
- ## Tips & Tricks
234
-
235
- - **Preview first**: Always use `/agent-vibes:preview` before switching to a new voice
236
- - **Provider switching**: Some voices are only available on specific providers
237
- - **Voice history**: Use `/agent-vibes:replay` to hear the last 10 TTS messages
238
- - **Custom pretext**: Set a pretext to brand all your responses (e.g., "AgentVibes:")
239
- - **Mute for focus**: Use `/agent-vibes:mute` during intensive work sessions
240
-
241
- Enjoy your TTS experience! 🎵
1
+ ---
2
+ name: agentvibes
3
+ description: 🎤 AgentVibes TTS for Claude Code & OpenClaw — Switch voices, set personality, control speed, background music, language learning mode, reverb/effects, and more. Free offline TTS with 914+ Piper voices across 30+ languages. Works on Windows (SAPI/Piper), macOS (Say/Piper), Linux, and Android/Termux. No account or API key required.
4
+ ---
5
+
6
+ # 🎤 AgentVibes Voice Management
7
+
8
+ Professional text-to-speech for Claude Code and OpenClaw. Free, offline, no account required.
9
+
10
+ **Providers:** Piper TTS (914+ voices, all platforms) · macOS Say (built-in) · Windows SAPI (zero setup) · Soprano (neural)
11
+
12
+ ---
13
+
14
+ ## Voice Commands
15
+
16
+ ### /agent-vibes:switch \<voice_name\>
17
+ Switch to a different voice.
18
+
19
+ ```bash
20
+ /agent-vibes:switch en_US-amy-medium
21
+ /agent-vibes:switch en_GB-alan-medium
22
+ /agent-vibes:switch fr_FR-siwis-medium
23
+ ```
24
+
25
+ ### /agent-vibes:list [first|last] [N]
26
+ List available voices.
27
+
28
+ ```bash
29
+ /agent-vibes:list # Show all voices
30
+ /agent-vibes:list first 5 # Show first 5
31
+ /agent-vibes:list last 3 # Show last 3
32
+ ```
33
+
34
+ ### /agent-vibes:preview [first|last] [N]
35
+ Preview voices with audio samples.
36
+
37
+ ```bash
38
+ /agent-vibes:preview # Preview first 3 voices
39
+ /agent-vibes:preview 5 # Preview first 5
40
+ /agent-vibes:preview last 5 # Preview last 5
41
+ ```
42
+
43
+ ### /agent-vibes:sample \<voice_name\>
44
+ Play a sample of a specific voice.
45
+
46
+ ```bash
47
+ /agent-vibes:sample en_US-ryan-high
48
+ ```
49
+
50
+ ### /agent-vibes:get
51
+ Show the currently active voice.
52
+
53
+ ### /agent-vibes:set-favorite-voice
54
+ Mark current voice as your favorite.
55
+
56
+ ---
57
+
58
+ ## Personality & Style
59
+
60
+ ### /agent-vibes:personality [name|list|add|edit|get|reset]
61
+ Set a personality style for TTS output.
62
+
63
+ ```bash
64
+ /agent-vibes:personality list # Show available personalities
65
+ /agent-vibes:personality sarcastic # Switch to sarcastic style
66
+ /agent-vibes:personality dramatic # Switch to dramatic style
67
+ /agent-vibes:personality reset # Back to default
68
+ ```
69
+
70
+ ### /agent-vibes:set-pretext \<phrase\>
71
+ Add a spoken prefix before every TTS message.
72
+
73
+ ```bash
74
+ /agent-vibes:set-pretext "AgentVibes" # Speaks "AgentVibes: ..." before each message
75
+ /agent-vibes:set-pretext "" # Clear pretext
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Speed & Effects
81
+
82
+ ### /agent-vibes:set-speed \<speed\>
83
+ Control speech rate (0.5x – 3.0x).
84
+
85
+ ```bash
86
+ /agent-vibes:set-speed 1.0 # Normal speed
87
+ /agent-vibes:set-speed 1.5 # 50% faster
88
+ /agent-vibes:set-speed 0.8 # Slower
89
+ ```
90
+
91
+ ### /agent-vibes:effects [reverb|echo|pitch|eq|reset]
92
+ Configure voice effects.
93
+
94
+ ```bash
95
+ /agent-vibes:effects reverb hall # Hall reverb
96
+ /agent-vibes:effects reverb none # No reverb
97
+ /agent-vibes:effects reset # Clear all effects
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Background Music
103
+
104
+ ### /agent-vibes:background-music [on|off|status|list|switch]
105
+ Toggle or change background music played under TTS.
106
+
107
+ ```bash
108
+ /agent-vibes:background-music on # Enable background music
109
+ /agent-vibes:background-music off # Disable
110
+ /agent-vibes:background-music list # Show available tracks
111
+ /agent-vibes:background-music switch jazz # Switch to jazz track
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Verbosity
117
+
118
+ ### /agent-vibes:verbosity [low|medium|high]
119
+ Control how much Claude speaks while working.
120
+
121
+ ```bash
122
+ /agent-vibes:verbosity low # Brief acknowledgments only
123
+ /agent-vibes:verbosity medium # Key decisions (default)
124
+ /agent-vibes:verbosity high # Full reasoning
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Mute / Replay
130
+
131
+ ### /agent-vibes:mute / /agent-vibes:unmute
132
+ Silence or restore TTS output (persists across sessions).
133
+
134
+ ### /agent-vibes:replay [N]
135
+ Replay recent audio (last 10 kept).
136
+
137
+ ```bash
138
+ /agent-vibes:replay # Replay last audio
139
+ /agent-vibes:replay 2 # Replay second-to-last
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Language & Learning
145
+
146
+ ### /agent-vibes:language \<lang\>
147
+ Set your native language.
148
+
149
+ ```bash
150
+ /agent-vibes:language english
151
+ /agent-vibes:language japanese
152
+ ```
153
+
154
+ ### /agent-vibes:learn [on|off]
155
+ Enable language learning mode — Claude speaks in both your native and target language.
156
+
157
+ ```bash
158
+ /agent-vibes:learn on
159
+ /agent-vibes:learn off
160
+ ```
161
+
162
+ ### /agent-vibes:translate \<text\>
163
+ Translate and speak text in the target language.
164
+
165
+ ---
166
+
167
+ ## Provider Management
168
+
169
+ ### /agent-vibes:provider [list|switch|info]
170
+
171
+ ```bash
172
+ /agent-vibes:provider list
173
+ /agent-vibes:provider switch piper # Piper TTS (free, offline, 914+ voices)
174
+ /agent-vibes:provider switch macos # macOS Say (Mac only)
175
+ /agent-vibes:provider switch sapi # Windows SAPI (Windows only, zero setup)
176
+ /agent-vibes:provider switch soprano # Soprano (neural)
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Providers
182
+
183
+ | Provider | Platform | Cost | Voices |
184
+ |----------|----------|------|--------|
185
+ | **Piper TTS** | All platforms | Free, offline | 914+ in 30+ languages |
186
+ | **macOS Say** | macOS only | Free (built-in) | 100+ system voices |
187
+ | **Windows SAPI** | Windows only | Free (built-in) | System voices, zero setup |
188
+ | **Soprano** | All platforms | Free | Neural voices |
189
+
190
+ ---
191
+
192
+ ## Miscellaneous
193
+
194
+ ### /agent-vibes:whoami
195
+ Show current AgentVibes configuration.
196
+
197
+ ### /agent-vibes:version
198
+ Show installed version.
199
+
200
+ ### /agent-vibes:update
201
+ Update AgentVibes to the latest version.
202
+
203
+ ### /agent-vibes:show / /agent-vibes:hide
204
+ Show or hide the AgentVibes status indicator.
205
+
206
+ ### /agent-vibes:cleanup / /agent-vibes:clean
207
+ Remove cached audio files.
208
+
209
+ ---
210
+
211
+ ## Default Voices (Piper TTS — Free & Offline)
212
+
213
+ **English (US):** en_US-lessac-medium · en_US-amy-medium · en_US-ryan-high · en_US-libritts-high (914 speakers)
214
+
215
+ **English (UK):** en_GB-alan-medium · en_GB-jenny_dioco-medium
216
+
217
+ **French:** fr_FR-siwis-medium · fr_FR-gilles-low
218
+
219
+ **German:** de_DE-thorsten-medium · de_DE-eva_k-x_low
220
+
221
+ **Spanish:** es_ES-davefx-medium · es_MX-claude-high
222
+
223
+ **Japanese:** ja_JP-ayanami-medium · **Chinese:** zh_CN-huayan-x_low · **Korean:** ko_KR-kss-medium
224
+
225
+ **+ 900 more** across 30+ languages. All voices are downloaded from [HuggingFace](https://huggingface.co/rhasspy/piper-voices) — no account required.
226
+
227
+ ---
228
+
229
+ ## Tips
230
+
231
+ - **Preview first**: Use `/agent-vibes:preview` before committing to a voice
232
+ - **Verbosity**: Set to `low` for focused work, `high` for full narration
233
+ - **BMAD party mode**: Each agent gets their own voice, music, and personality
234
+ - **Replay**: Use `/agent-vibes:replay` to re-hear the last 10 responses
235
+ - **Speed**: Combine with personality for a fully custom TTS character
236
+
237
+ Enjoy your TTS experience! 🎵
package/CLAUDE.md CHANGED
@@ -121,6 +121,35 @@ except (PermissionError, UnicodeDecodeError, OSError) as e:
121
121
  return default_value
122
122
  ```
123
123
 
124
+ ## Blessed TUI Color Rules (CRITICAL)
125
+
126
+ Paul's terminal does **not** render hex colors — they all fall back to white. Always use **named ANSI colors**.
127
+
128
+ ### Two coloring mechanisms — use the right one:
129
+
130
+ **1. Content tags (`tags: true` on the widget) — for text elements:**
131
+ ```js
132
+ blessed.text({
133
+ tags: true,
134
+ content: `{yellow-fg}some text{/yellow-fg} {magenta-fg}other text{/magenta-fg}`,
135
+ style: { bg: COLORS.headerBg },
136
+ });
137
+ ```
138
+ - ✅ Works: `{yellow-fg}`, `{cyan-fg}`, `{magenta-fg}`, `{white-fg}`, `{green-fg}`, `{blue-fg}`
139
+ - ❌ Broken: `{#ffff00-fg}`, `{#90a4ae-fg}` — hex tags render as white
140
+
141
+ **2. Style objects — for list `selected`, `item`, widget `style`:**
142
+ ```js
143
+ style: {
144
+ selected: { bg: 'blue', fg: 'yellow' }, // ✅ named only
145
+ item: { fg: 'white' },
146
+ }
147
+ ```
148
+ - ✅ Works: `'yellow'`, `'blue'`, `'cyan'`, `'magenta'`, `'green'`, `'white'`
149
+ - ❌ Broken: `'#ffff00'`, `'#4a148c'` — hex in style objects renders as white
150
+
151
+ **Direct screen children render tags correctly.** Elements parented to a `blessed.box` with `tags: false` may not render tags even if the child has `tags: true` — attach directly to `this.screen` instead (same pattern as tab bar items).
152
+
124
153
  ## Code Quality Standards
125
154
 
126
155
  - ✅ **Error handling:** No silent failures - always handle errors explicitly
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![Publish](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml/badge.svg)](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
12
12
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
13
13
 
14
- **Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v4.6.5
14
+ **Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v4.6.6
15
15
 
16
16
  ---
17
17
 
@@ -28,6 +28,7 @@
28
28
  | **Use with OpenClaw** | [OpenClaw Integration](#-openclaw-integration) |
29
29
  | **Use natural language** | [MCP Setup](docs/mcp-setup.md) |
30
30
  | **Switch voices** | [Voice Library](docs/voice-library.md) |
31
+ | **Configure BMAD Party Mode** (agents with unique voices) | [BMAD Plugin & Party Mode](#-bmad-plugin) |
31
32
  | **Fix issues** (git-lfs? MCP tokens? Read this!) | [Troubleshooting](docs/troubleshooting.md) & [FAQ](#-frequently-asked-questions-faq) |
32
33
 
33
34
  ---
@@ -40,11 +41,16 @@ Whether you're coding in Claude Code, chatting in Claude Desktop, or running Ope
40
41
 
41
42
  ---
42
43
 
44
+ ## 🧭 NEW IN v4.6.6 — Natural TUI Navigation
45
+
46
+ The Settings TUI now flows the way you'd expect. Down moves top-to-bottom through header → sub-tabs → content → footer. Left/Right switches sub-tabs and moves between footer buttons. Up from content returns to the active sub-tab — not always Voice. The Language tab has a proper scrollable list. Readme falls back to the AgentVibes package README when no local one exists. Escape from the installer no longer gets stuck.
47
+
48
+ ---
49
+
43
50
  ## 🔧 NEW IN v4.6.5 — Line Endings, TUI Non-Interactive Hint, Release Process
44
51
 
45
52
  - **`.gitattributes`** — enforces `LF` for shell scripts/JS/JSON/markdown, `CRLF` for PowerShell; stops `bin/` files showing as modified on Windows
46
53
  - **TUI non-interactive hint** — installer header now shows a two-tone hint on row 2: `Skip this UI?` (dim) + `npx agentvibes install --non-interactive` (brighter), matching the `[piper] [en_US-ryan-high]` footer aesthetic
47
- - **Release process** — mandatory human approval + privacy scan checkpoint before any `git push` or `npm publish`
48
54
 
49
55
  ---
50
56
 
@@ -452,7 +458,7 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
452
458
  - [🖥️ SSH Receiver](#️-agentvibes-receiver--remote-audio-streaming) - Stream audio from headless servers
453
459
  - [💬 Intro Text](#-intro-text-pretext---your-personal-ai-branding) - Custom TTS prefixes
454
460
  - [🎵 Custom Background Music](#-custom-background-music---complete-audio-control) - Upload your own tracks
455
- - [📰 Latest Release](#-latest-release) - v4.6.5gitattributes, party mode voice fix, macOS CI fixes
461
+ - [📰 Latest Release](#-latest-release) - v4.6.6TUI navigation & UX polish
456
462
  - [🪟 Windows Setup Guide for Claude Desktop](mcp-server/WINDOWS_SETUP.md) - Complete Windows installation with WSL & Python
457
463
 
458
464
  ### AgentVibes MCP (Natural Language Control)
@@ -498,9 +504,9 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
498
504
 
499
505
  ## 📰 Latest Release
500
506
 
501
- **[v4.6.5 - Patch Release](https://github.com/paulpreibisch/AgentVibes/releases/tag/v4.6.5)**
507
+ **[v4.6.6 - Natural TUI Navigation](https://github.com/paulpreibisch/AgentVibes/releases/tag/v4.6.6)**
502
508
 
503
- Infrastructure and tooling improvements: consistent line endings via `.gitattributes`, `bin/` execute bits preserved, and a standardized release process with mandatory human approval and privacy scan checkpoints before any push.
509
+ The Settings TUI now navigates the way you'd expect arrow keys flow naturally through the interface, the Language tab has a proper scrollable list, and the Readme tab always has something useful to show.
504
510
 
505
511
  ### 🐛 Recent Fixes (v4.6.3 / v4.6.4)
506
512
 
@@ -1174,6 +1180,14 @@ The BMAD plugin detects when you activate a BMAD agent (e.g., `/BMad:agents:pm`)
1174
1180
 
1175
1181
  **Version Support**: AgentVibes supports both BMAD v4 and v6-alpha installations. Version detection is automatic - just install BMAD and AgentVibes will detect and configure itself correctly!
1176
1182
 
1183
+ ### 🎭 Party Mode — Screenshots
1184
+
1185
+ Open the **BMad** tab in the AgentVibes TUI (`npx agentvibes`) to configure which voice each agent uses:
1186
+
1187
+ ![BMAD Party Mode Tab](docs/installation-screenshots/screenshot-bmad-party-mode.png)
1188
+
1189
+ > 📸 **Don't have a screenshot yet?** Run `npx agentvibes`, switch to the **BMad** tab, and take a screenshot — then save it as `docs/installation-screenshots/screenshot-bmad-party-mode.png`.
1190
+
1177
1191
  ### 🔊 TTS Injection: How It Works
1178
1192
 
1179
1193
  BMAD uses a **loosely-coupled injection system** for voice integration. BMAD source files contain placeholder markers that AgentVibes replaces with speaking instructions during installation:
package/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # AgentVibes Release Notes
2
2
 
3
+ ## 🧭 v4.6.6 — Natural TUI Navigation
4
+
5
+ **Release Date:** April 2026
6
+
7
+ ### Improvements
8
+
9
+ The Settings TUI now navigates the way you'd expect. Arrow keys flow naturally top-to-bottom through the interface — main header → sub-tab bar → content → footer — with no surprising jumps or dead ends.
10
+
11
+ - **Down from the main header** lands on the active sub-tab bar item, so you're always oriented before pressing further.
12
+ - **Down through content** moves row-by-row within the current tab, reaching Full Preview and the Save buttons at the bottom. It no longer auto-jumps to the next sub-tab.
13
+ - **Up from the first content row** returns you to the current sub-tab (not always Voice).
14
+ - **Left/Right in the sub-tab bar** is the natural way to switch between Voice, Effects, Personality, Output, and Language tabs.
15
+ - **Left/Right in the footer** moves between Full Preview, Save Globally, Save Locally, and Cancel Changes.
16
+ - **Language tab** now has a proper scrollable list — arrow down to enter it, Up/Down to select a language, Enter to apply, Down past the last item to reach the footer.
17
+ - **Readme tab** falls back to the AgentVibes package README when opened in a project folder that has no README of its own.
18
+ - **Escape from the installer** now navigates cleanly to Settings instead of getting stuck.
19
+
20
+ ---
21
+
3
22
  ## 🔧 v4.6.5 — Patch Release
4
23
 
5
24
  **Release Date:** April 2026
@@ -16,7 +35,6 @@
16
35
 
17
36
  - **`bin/` execute bits restored** — Re-indexed `bin/agent-vibes`, `bin/agentvibes-voice-browser.js`, `bin/mcp-server.js`, and `bin/test-bmad-pr` to ensure consistent line endings; verified all four retain `chmod +x` (`100755` mode).
18
37
 
19
- - **Standardized release process** — `/release` command now documents a privacy scan checkpoint: confirm no API keys, no personal info (email, phone, personal port numbers), before committing. CI must be green before releasing.
20
38
 
21
39
  ### User Impact
22
40
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "agentvibes",
4
- "version": "4.6.5",
4
+ "version": "4.6.6",
5
5
  "description": "Now your AI Agents can finally talk back! Professional TTS voice for Claude Code, Claude Desktop (via MCP), and Clawdbot with multi-provider support.",
6
6
  "homepage": "https://agentvibes.org",
7
7
  "keywords": [
@@ -124,7 +124,7 @@ const COLORS = {
124
124
  sectionHdr: '#7b1fa2',
125
125
  labelFg: '#e3f2fd',
126
126
  valueFg: '#ffff00',
127
- activeFg: '#ce93d8',
127
+
128
128
  btnDefault: '#6a1b9a',
129
129
  btnFocus: '#2e7d32', // Green — focused/selected
130
130
  btnFocusFg: '#ffffff',
@@ -304,7 +304,7 @@ ${_tl('bmadDesc')}
304
304
  fg: COLORS.labelFg,
305
305
  bg: COLORS.contentBg,
306
306
  border: { fg: COLORS.borderFg },
307
- selected: { bg: '#4a148c', fg: COLORS.activeFg, bold: true },
307
+ selected: { bg: 'blue', fg: 'yellow' },
308
308
  item: { fg: COLORS.labelFg },
309
309
  },
310
310
  });
@@ -681,7 +681,7 @@ ${_tl('bmadDesc')}
681
681
  fg: COLORS.labelFg,
682
682
  bg: COLORS.contentBg,
683
683
  border: { fg: '#4a148c' },
684
- selected: { bg: '#4a148c', fg: COLORS.activeFg, bold: true },
684
+ selected: { bg: 'blue', fg: 'yellow' },
685
685
  item: { fg: COLORS.labelFg },
686
686
  },
687
687
  });
@@ -1529,7 +1529,7 @@ ${_tl('bmadDesc')}
1529
1529
  fg: COLORS.labelFg,
1530
1530
  bg: COLORS.contentBg,
1531
1531
  border: { fg: COLORS.btnFocus },
1532
- selected: { bg: '#4a148c', fg: COLORS.activeFg, bold: true },
1532
+ selected: { bg: 'blue', fg: 'yellow' },
1533
1533
  item: { fg: COLORS.labelFg },
1534
1534
  },
1535
1535
  });
@@ -942,13 +942,10 @@ export function createInstallTab(screen, services) {
942
942
  _screen--;
943
943
  _showCurrentScreen();
944
944
  } else {
945
- box.hide();
946
- screen.render();
947
- // Defer so the escape keypress event finishes propagating before focus changes.
948
- // Calling focusMainTabBar() synchronously here would set focus to the tab bar
949
- // item mid-event, causing its own key(['escape']) handler to fire in the same
950
- // emission and call onFocus() → re-focus a button inside the now-hidden box.
951
- if (typeof focusMainTabBar === 'function') setTimeout(() => focusMainTabBar(), 0);
945
+ // User pressed Escape at the first screen — they want out of the installer.
946
+ // Switch to Settings so they can configure without being stuck on the install tab.
947
+ // (focusMainTabBar would re-focus the install tab item, which loops back here.)
948
+ setTimeout(() => navigationService?.switchTab('settings'), 0);
952
949
  }
953
950
  });
954
951
 
@@ -146,9 +146,12 @@ export function createReadmeTab(screen, services) {
146
146
  // Load README.md
147
147
 
148
148
  function _loadReadme() {
149
+ // Package root — works whether installed globally (node_modules/.bin) or run from source
150
+ const pkgRoot = path.resolve(new URL(import.meta.url).pathname, '..', '..', '..', '..');
149
151
  const candidates = [
150
152
  path.resolve(process.cwd(), 'README.md'),
151
153
  path.resolve(process.cwd(), 'readme.md'),
154
+ path.resolve(pkgRoot, 'README.md'), // AgentVibes package README fallback
152
155
  ];
153
156
  for (const p of candidates) {
154
157
  if (fs.existsSync(p)) {
@@ -1760,37 +1760,57 @@ export function createSettingsTab(screen, services) {
1760
1760
  style: { fg: COLORS.valueFg, bg: COLORS.contentBg },
1761
1761
  });
1762
1762
 
1763
- let _langListIdx = 0;
1764
-
1765
- const languageList = blessed.box({
1763
+ // Visible list height — cap at 10 rows (fits standard 24-row terminals with headers)
1764
+ const LANG_LIST_HEIGHT = Math.min(SUPPORTED_LANGUAGES.length, 10);
1765
+
1766
+ // blessed.list: natively focusable, handles selection highlight, scrolling.
1767
+ // keys:true needed so keypress events propagate to the element (keyable=true).
1768
+ // We immediately removeAllListeners('keypress') to strip blessed's built-in up/down nav —
1769
+ // our manual .key() handlers (registered on 'key down'/'key up') are the sole navigators.
1770
+ const languageList = blessed.list({
1766
1771
  parent: box,
1767
1772
  top: 7,
1768
1773
  left: 4,
1769
- width: 40,
1770
- height: Math.min(SUPPORTED_LANGUAGES.length + 2, 12),
1771
- tags: true,
1772
- content: '',
1773
- style: { fg: COLORS.labelFg, bg: COLORS.contentBg },
1774
+ width: 44,
1775
+ height: LANG_LIST_HEIGHT + 2, // +2 for border
1776
+ keys: true,
1777
+ mouse: true,
1778
+ tags: false,
1779
+ items: SUPPORTED_LANGUAGES.map(l => l.name),
1780
+ style: {
1781
+ selected: { bg: 'green', fg: 'white', bold: true },
1782
+ item: { fg: 'white' },
1783
+ border: { fg: 'cyan' },
1784
+ focus: { border: { fg: 'yellow' } },
1785
+ },
1786
+ border: { type: 'line' },
1787
+ scrollable: true,
1788
+ scrollbar: { style: { bg: 'blue' } },
1774
1789
  });
1790
+ // Strip blessed's built-in keypress nav (up/down/j/k/etc.) — our .key() handlers take over.
1791
+ // .key() registers on 'key <name>' events (not 'keypress'), so they survive this removal.
1792
+ languageList.removeAllListeners('keypress');
1775
1793
 
1776
- function _renderLangList() {
1777
- const lines = SUPPORTED_LANGUAGES.map((l, i) =>
1778
- i === _langListIdx
1779
- ? `{green-fg}► ${l.name}{/green-fg}`
1780
- : ` ${l.name}`
1781
- );
1782
- languageList.setContent(lines.join('\n'));
1783
- }
1794
+ // Hint shown below the list
1795
+ const langHint = blessed.text({
1796
+ parent: box,
1797
+ top: 7 + LANG_LIST_HEIGHT + 3,
1798
+ left: 4,
1799
+ content: '{gray-fg}↑↓ navigate · Enter to apply{/gray-fg}',
1800
+ tags: true,
1801
+ style: { bg: COLORS.contentBg },
1802
+ });
1784
1803
 
1804
+ // Apply button — kept for mouse users; keyboard users just press Enter on the list
1785
1805
  const langApplyBtn = _createButton(box, screen, '✓ Apply Language', COLORS, () => {
1786
- const selected = SUPPORTED_LANGUAGES[_langListIdx];
1806
+ const selected = SUPPORTED_LANGUAGES[languageList.selected ?? 0];
1787
1807
  if (selected && services.languageService) {
1788
1808
  services.languageService.setLang(selected.value);
1789
1809
  refreshLanguageDisplay();
1790
1810
  _showNotice(screen, `Language: ${selected.name}`);
1791
1811
  }
1792
1812
  }, { bg: '#2e7d32' });
1793
- langApplyBtn.top = 7 + Math.min(SUPPORTED_LANGUAGES.length + 2, 12) + 1;
1813
+ langApplyBtn.top = 7 + LANG_LIST_HEIGHT + 5;
1794
1814
  langApplyBtn.left = 4;
1795
1815
 
1796
1816
  function refreshLanguageDisplay() {
@@ -1798,19 +1818,11 @@ export function createSettingsTab(screen, services) {
1798
1818
  const found = SUPPORTED_LANGUAGES.find(l => l.value === currentLang);
1799
1819
  languageCurrentValue.setContent(found ? found.name : currentLang);
1800
1820
  const idx = SUPPORTED_LANGUAGES.findIndex(l => l.value === currentLang);
1801
- if (idx >= 0) _langListIdx = idx;
1802
- _renderLangList();
1821
+ if (idx >= 0) languageList.select(idx);
1803
1822
  screen.render();
1804
1823
  }
1805
1824
 
1806
- // Key navigation for language list
1807
- box.key(['up', 'down'], (ch, key) => {
1808
- if (_activeSubTab !== 'language') return;
1809
- if (key.name === 'up') _langListIdx = Math.max(0, _langListIdx - 1);
1810
- if (key.name === 'down') _langListIdx = Math.min(SUPPORTED_LANGUAGES.length - 1, _langListIdx + 1);
1811
- _renderLangList();
1812
- screen.render();
1813
- });
1825
+ // Key navigation wired after _navigateRow is defined (see below)
1814
1826
 
1815
1827
  // -------------------------------------------------------------------------
1816
1828
  // Display state + button-level focus navigation (story 7.6)
@@ -1847,8 +1859,7 @@ export function createSettingsTab(screen, services) {
1847
1859
  language: [
1848
1860
  languageSectionHeader,
1849
1861
  languageCurrentLabel, languageCurrentValue,
1850
- languageList,
1851
- langApplyBtn,
1862
+ languageList, langHint, langApplyBtn,
1852
1863
  ],
1853
1864
  };
1854
1865
 
@@ -1858,7 +1869,7 @@ export function createSettingsTab(screen, services) {
1858
1869
  effects: [[reverbChangeBtn, reverbTestBtn], [trackChangeBtn, musicToggleBtn, musicTestBtn], [volumeChangeBtn]],
1859
1870
  personality: [[verbosityChangeBtn], [personalityChangeBtn, personalityTestBtn], [introEditBtn, introClearBtn]],
1860
1871
  output: [[audioDstChangeBtn], [audioSshEditBtn, audioStreamModeBtn]],
1861
- language: [[langApplyBtn]],
1872
+ language: [[languageList], [langApplyBtn]],
1862
1873
  };
1863
1874
 
1864
1875
  const _subTabItemsArray = SUB_TABS.map(id => _subTabItemsMap[id]);
@@ -1906,7 +1917,7 @@ export function createSettingsTab(screen, services) {
1906
1917
 
1907
1918
  const _buttons = [
1908
1919
  _subTabItemsMap.voice, _subTabItemsMap.effects,
1909
- _subTabItemsMap.personality, _subTabItemsMap.output,
1920
+ _subTabItemsMap.personality, _subTabItemsMap.output, _subTabItemsMap.language,
1910
1921
  switchBtn, changeBtn, playBtn,
1911
1922
  reverbChangeBtn, reverbTestBtn,
1912
1923
  trackChangeBtn, musicToggleBtn, musicTestBtn,
@@ -1914,6 +1925,7 @@ export function createSettingsTab(screen, services) {
1914
1925
  verbosityChangeBtn, personalityChangeBtn, personalityTestBtn,
1915
1926
  introEditBtn, introClearBtn,
1916
1927
  audioDstChangeBtn, audioSshEditBtn, audioStreamModeBtn,
1928
+ languageList, langApplyBtn,
1917
1929
  fullPreviewBtn,
1918
1930
  saveGloballyBtn, saveLocallyBtn, cancelChangesBtn,
1919
1931
  ];
@@ -2030,74 +2042,6 @@ export function createSettingsTab(screen, services) {
2030
2042
  return;
2031
2043
  }
2032
2044
 
2033
- // _rows layout: [0]=sub-tab bar, [1..lastContent]=per-tab rows, then 3 shared bottom rows
2034
- const BOTTOM_ROWS = 2; // [fullPreviewBtn], [saveGlobally+saveLocally+cancelChanges]
2035
- const lastContentIdx = _rows.length - BOTTOM_ROWS - 1;
2036
-
2037
- // Cross-tab forward: ↓ from the effective last visible content row → jump to next sub-tab
2038
- if (delta > 0 && rowIdx >= 1 && rowIdx <= lastContentIdx) {
2039
- let isEffectiveLast = true;
2040
- for (let r = rowIdx + 1; r <= lastContentIdx; r++) {
2041
- if (_isRowVisible(_rows[r])) { isEffectiveLast = false; break; }
2042
- }
2043
- if (isEffectiveLast) {
2044
- const tabIdx = SUB_TABS.indexOf(_activeSubTab);
2045
- if (tabIdx < SUB_TABS.length - 1) {
2046
- const nextTab = SUB_TABS[tabIdx + 1];
2047
- _showSubTab(nextTab, true);
2048
- const firstRow = _rowsBySubTab[nextTab].find(row => _isRowVisible(row));
2049
- if (firstRow) {
2050
- const btn = _firstVisibleBtn(firstRow);
2051
- _currentIdx = _buttons.indexOf(btn);
2052
- _focusButton(btn);
2053
- return;
2054
- }
2055
- }
2056
- // On the last sub-tab: fall through to normal (go to fullPreviewBtn)
2057
- }
2058
- }
2059
-
2060
- // Cross-tab backward: ↑ from first content row (row 1) → jump to previous sub-tab's last row
2061
- if (delta < 0 && rowIdx === 1) {
2062
- const tabIdx = SUB_TABS.indexOf(_activeSubTab);
2063
- if (tabIdx > 0) {
2064
- const prevTab = SUB_TABS[tabIdx - 1];
2065
- _showSubTab(prevTab, true);
2066
- const prevRows = _rowsBySubTab[prevTab];
2067
- let lastRow = null;
2068
- for (let i = prevRows.length - 1; i >= 0; i--) {
2069
- if (_isRowVisible(prevRows[i])) { lastRow = prevRows[i]; break; }
2070
- }
2071
- if (lastRow) {
2072
- const btn = _firstVisibleBtn(lastRow);
2073
- _currentIdx = _buttons.indexOf(btn);
2074
- _focusButton(btn);
2075
- return;
2076
- }
2077
- }
2078
- // First sub-tab: fall through (goes to sub-tab bar at row 0)
2079
- }
2080
-
2081
- // In the bottom save row: ↓ navigates to the next visible sibling within the row
2082
- // rather than wrapping to row 0 (sub-tab bar) via modulo.
2083
- const lastRowIdx = _rows.length - 1;
2084
- if (delta > 0 && rowIdx === lastRowIdx) {
2085
- const row = _rows[lastRowIdx];
2086
- const posInRow = row.indexOf(focused);
2087
- for (let i = posInRow + 1; i < row.length; i++) {
2088
- if (!row[i].hidden) {
2089
- _currentIdx = _buttons.indexOf(row[i]);
2090
- _focusButton(row[i]);
2091
- return;
2092
- }
2093
- }
2094
- // Last sibling in the row — wrap up to sub-tab bar (row 0)
2095
- const topBtn = _firstVisibleBtn(_rows[0]);
2096
- _currentIdx = _buttons.indexOf(topBtn);
2097
- _focusButton(topBtn);
2098
- return;
2099
- }
2100
-
2101
2045
  // Skip rows where ALL buttons are hidden (e.g. SSH alias row when destination is local).
2102
2046
  // Use _firstVisibleBtn so we land on the first visible button in a mixed row.
2103
2047
  let attempts = 0;
@@ -2105,17 +2049,56 @@ export function createSettingsTab(screen, services) {
2105
2049
  rowIdx = (rowIdx + delta + _rows.length) % _rows.length;
2106
2050
  attempts++;
2107
2051
  } while (!_isRowVisible(_rows[rowIdx]) && attempts < _rows.length);
2108
- const btn = _firstVisibleBtn(_rows[rowIdx]);
2052
+ // When landing on the sub-tab bar (row 0), focus the ACTIVE sub-tab item, not the first one
2053
+ const btn = rowIdx === 0
2054
+ ? (_subTabItemsMap[_activeSubTab] ?? _firstVisibleBtn(_rows[0]))
2055
+ : _firstVisibleBtn(_rows[rowIdx]);
2109
2056
  _currentIdx = _buttons.indexOf(btn);
2110
2057
  _focusButton(btn);
2111
2058
  }
2112
2059
 
2113
2060
  for (const btn of _buttons) {
2114
- btn.key(['down'], () => _navigateRow(1));
2115
- btn.key(['up'], () => _navigateRow(-1));
2061
+ btn.key(['down'], () => {
2062
+ if (btn === languageList) return; // languageList has its own boundary-aware down handler
2063
+ _navigateRow(1);
2064
+ });
2065
+ btn.key(['up'], () => {
2066
+ if (btn === languageList) return; // languageList has its own boundary-aware up handler
2067
+ _navigateRow(-1);
2068
+ });
2116
2069
  btn.key(['escape'], () => { if (typeof focusMainTabBar === 'function') setTimeout(() => focusMainTabBar(), 0); });
2117
2070
  }
2118
2071
 
2072
+ // Language list — fully manual navigation (keys:false on the list disables blessed's built-in
2073
+ // so only our handlers run, giving us clean boundary detection without double-move issues).
2074
+ languageList.key(['down'], () => {
2075
+ const cur = languageList.selected ?? 0;
2076
+ if (cur >= SUPPORTED_LANGUAGES.length - 1) {
2077
+ _navigateRow(1); // past last item → Apply button
2078
+ } else {
2079
+ languageList.select(cur + 1);
2080
+ screen.render();
2081
+ }
2082
+ });
2083
+ languageList.key(['up'], () => {
2084
+ const cur = languageList.selected ?? 0;
2085
+ if (cur <= 0) {
2086
+ _navigateRow(-1); // past first item → sub-tab bar
2087
+ } else {
2088
+ languageList.select(cur - 1);
2089
+ screen.render();
2090
+ }
2091
+ });
2092
+ languageList.key(['enter', 'return', 'space'], () => {
2093
+ const selected = SUPPORTED_LANGUAGES[languageList.selected ?? 0];
2094
+ if (selected && services.languageService) {
2095
+ services.languageService.setLang(selected.value);
2096
+ refreshLanguageDisplay();
2097
+ _showNotice(screen, `Language: ${selected.name}`);
2098
+ }
2099
+ });
2100
+ languageList.key(['escape'], () => { if (typeof focusMainTabBar === 'function') setTimeout(() => focusMainTabBar(), 0); });
2101
+
2119
2102
  // ← / → within content rows — uses _buttonGroups (static); sub-tab bar has its own wiring
2120
2103
  const _rows = []; // populated dynamically by _showSubTab()
2121
2104
 
@@ -2129,8 +2112,9 @@ export function createSettingsTab(screen, services) {
2129
2112
  [introEditBtn, introClearBtn],
2130
2113
  [audioDstChangeBtn],
2131
2114
  [audioSshEditBtn, audioStreamModeBtn],
2132
- [fullPreviewBtn],
2133
- [saveGloballyBtn, saveLocallyBtn, cancelChangesBtn],
2115
+ [languageList],
2116
+ [langApplyBtn],
2117
+ [fullPreviewBtn, saveGloballyBtn, saveLocallyBtn, cancelChangesBtn],
2134
2118
  ];
2135
2119
 
2136
2120
  for (const row of _buttonGroups) {
@@ -2425,9 +2409,12 @@ export function createSettingsTab(screen, services) {
2425
2409
  },
2426
2410
 
2427
2411
  onFocus() {
2412
+ // Land on the active sub-tab bar item so the user can ↑↓ from there.
2428
2413
  // Use _focusButton (not raw .focus()) so olines get invalidated before render,
2429
2414
  // preventing the ghost-duplicate-row artifact on initial tab activation.
2430
- _focusButton(_buttons[_currentIdx]);
2415
+ const activeSubTabItem = _subTabItemsMap[_activeSubTab];
2416
+ _currentIdx = _buttons.indexOf(activeSubTabItem);
2417
+ _focusButton(activeSubTabItem);
2431
2418
  },
2432
2419
 
2433
2420
  onBlur() {
package/src/installer.js CHANGED
@@ -5352,6 +5352,12 @@ Troubleshooting:
5352
5352
  await fs.writeFile(langConfigPath, lang, { mode: 0o600 });
5353
5353
  }
5354
5354
 
5355
+ // Default translation to auto: syncs with BMAD communication_language if set, otherwise no translation
5356
+ const translateFile = path.join(claudeDir, 'tts-translate-to.txt');
5357
+ try { await fs.access(translateFile); } catch {
5358
+ await fs.writeFile(translateFile, 'auto', { mode: 0o600 });
5359
+ }
5360
+
5355
5361
  // Apply verbosity, personality, pretext
5356
5362
  await fs.writeFile(path.join(claudeDir, 'tts-verbosity.txt'), userConfig.verbosity);
5357
5363
  if (userConfig.personality && userConfig.personality !== 'none') {
@@ -1 +0,0 @@
1
- true