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.
- package/.claude/github-star-reminder.txt +1 -1
- package/.clawdbot/skill/SKILL.md +237 -241
- package/CLAUDE.md +29 -0
- package/README.md +19 -5
- package/RELEASE_NOTES.md +19 -1
- package/package.json +1 -1
- package/src/console/tabs/agents-tab.js +4 -4
- package/src/console/tabs/install-tab.js +4 -7
- package/src/console/tabs/readme-tab.js +3 -0
- package/src/console/tabs/settings-tab.js +93 -106
- package/src/installer.js +6 -0
- package/.claude/config/background-music-enabled.txt +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
20260403
|
package/.clawdbot/skill/SKILL.md
CHANGED
|
@@ -1,241 +1,237 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: agentvibes
|
|
3
|
-
description: 🎤 AgentVibes
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 🎤 AgentVibes Voice Management
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/agent-vibes:
|
|
40
|
-
/agent-vibes:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/agent-vibes:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
/agent-vibes:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/agent-vibes:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/agent-vibes:
|
|
96
|
-
/agent-vibes:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/agent-vibes:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
/agent-vibes:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/agent-vibes:
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
###
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
/agent-vibes:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
- **
|
|
236
|
-
|
|
237
|
-
|
|
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
|
[](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
|
|
12
12
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
13
13
|
|
|
14
|
-
**Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v4.6.
|
|
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.
|
|
461
|
+
- [📰 Latest Release](#-latest-release) - v4.6.6 — TUI 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.
|
|
507
|
+
**[v4.6.6 - Natural TUI Navigation](https://github.com/paulpreibisch/AgentVibes/releases/tag/v4.6.6)**
|
|
502
508
|
|
|
503
|
-
|
|
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
|
+

|
|
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.
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
-
|
|
946
|
-
|
|
947
|
-
//
|
|
948
|
-
|
|
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
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
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:
|
|
1770
|
-
height:
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
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
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
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[
|
|
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 +
|
|
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)
|
|
1802
|
-
_renderLangList();
|
|
1821
|
+
if (idx >= 0) languageList.select(idx);
|
|
1803
1822
|
screen.render();
|
|
1804
1823
|
}
|
|
1805
1824
|
|
|
1806
|
-
// Key navigation
|
|
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
|
-
|
|
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'],
|
|
2115
|
-
|
|
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
|
-
[
|
|
2133
|
-
[
|
|
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
|
-
|
|
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
|