@kikkimo/claude-launcher 2.5.0 → 3.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/CHANGELOG.md +42 -0
- package/README.md +17 -10
- package/claude-launcher +614 -398
- package/docs/README-zh.md +17 -10
- package/lib/api-manager.js +136 -11
- package/lib/auth/password-input.js +8 -4
- package/lib/auth/password-validator.js +83 -48
- package/lib/i18n/index.js +4 -3
- package/lib/i18n/language-manager.js +4 -3
- package/lib/i18n/locales/de.js +89 -11
- package/lib/i18n/locales/en.js +89 -11
- package/lib/i18n/locales/es.js +89 -11
- package/lib/i18n/locales/fr.js +89 -11
- package/lib/i18n/locales/it.js +89 -11
- package/lib/i18n/locales/ja.js +89 -11
- package/lib/i18n/locales/ko.js +89 -11
- package/lib/i18n/locales/pt.js +89 -11
- package/lib/i18n/locales/ru.js +89 -11
- package/lib/i18n/locales/zh-TW.js +89 -11
- package/lib/i18n/locales/zh.js +89 -11
- package/lib/launcher.js +121 -93
- package/lib/ui/api-editor.js +210 -0
- package/lib/ui/interactive-table.js +216 -99
- package/lib/ui/menu.js +73 -62
- package/lib/ui/prompts.js +168 -139
- package/lib/ui/screen.js +125 -0
- package/lib/utils/stdin-manager.js +11 -9
- package/lib/utils/version-checker.js +63 -3
- package/package.json +2 -2
- package/docs/superpowers/plans/2026-03-31-update-models-and-auto-mode.md +0 -1414
- package/docs/superpowers/specs/2026-03-31-update-models-and-auto-mode-design.md +0 -187
|
@@ -1,1414 +0,0 @@
|
|
|
1
|
-
# Update Models & Add Auto Mode — Implementation Plan
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
-
|
|
5
|
-
**Goal:** Update GLM/Kimi/MiniMax model presets to current versions, add a Claude Code "Enable Auto Mode" launcher menu item, and add dynamic context hints that appear below the main menu based on the selected item.
|
|
6
|
-
|
|
7
|
-
**Architecture:** Pure incremental changes to existing files. Model presets are updated in `providers.js`. A new `launchClaudeAutoMode()` thin wrapper is added to `launcher.js`. The `Menu` class gains an optional synchronous `hintCallback` third parameter that renders context text below menu items. The main `claude-launcher` file wires the new menu item, hint callback, and shifted case indices. All 11 i18n locale files get 4 new keys.
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** Node.js (zero dependencies), custom CLI menu system, custom i18n with `{0}`/`{1}` positional placeholders via `MessageFormatter.format()`.
|
|
10
|
-
|
|
11
|
-
**Spec:** `docs/superpowers/specs/2026-03-31-update-models-and-auto-mode-design.md`
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## File Map
|
|
16
|
-
|
|
17
|
-
| File | Action | Responsibility |
|
|
18
|
-
|---|---|---|
|
|
19
|
-
| `lib/presets/providers.js` | Modify | Update zhipu, zai, moonshot, minimax_cn, minimax_global model lists/names/aliases |
|
|
20
|
-
| `lib/launcher.js` | Modify | Add `launchClaudeAutoMode()` function and export |
|
|
21
|
-
| `lib/ui/menu.js` | Modify | Add `hintCallback` 3rd param to `displayMenu()` and `navigate()` |
|
|
22
|
-
| `claude-launcher` | Modify | New menu item at index 2, hint callback, import change, case index shifts |
|
|
23
|
-
| `lib/i18n/locales/en.js` | Modify | Add 4 new i18n keys (English — canonical) |
|
|
24
|
-
| `lib/i18n/locales/zh.js` | Modify | Add 4 new i18n keys (Chinese Simplified) |
|
|
25
|
-
| `lib/i18n/locales/zh-TW.js` | Modify | Add 4 new i18n keys (Chinese Traditional) |
|
|
26
|
-
| `lib/i18n/locales/ja.js` | Modify | Add 4 new i18n keys (Japanese) |
|
|
27
|
-
| `lib/i18n/locales/ko.js` | Modify | Add 4 new i18n keys (Korean) |
|
|
28
|
-
| `lib/i18n/locales/de.js` | Modify | Add 4 new i18n keys (German) |
|
|
29
|
-
| `lib/i18n/locales/fr.js` | Modify | Add 4 new i18n keys (French) |
|
|
30
|
-
| `lib/i18n/locales/es.js` | Modify | Add 4 new i18n keys (Spanish) |
|
|
31
|
-
| `lib/i18n/locales/it.js` | Modify | Add 4 new i18n keys (Italian) |
|
|
32
|
-
| `lib/i18n/locales/pt.js` | Modify | Add 4 new i18n keys (Portuguese) |
|
|
33
|
-
| `lib/i18n/locales/ru.js` | Modify | Add 4 new i18n keys (Russian) |
|
|
34
|
-
| `test/providers.test.js` | Create | Tests for updated provider configs |
|
|
35
|
-
| `test/menu-hints.test.js` | Create | Tests for hintCallback rendering |
|
|
36
|
-
| `package.json` | Modify | Wire `npm test` to run both test files |
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
### Task 1: Update GLM model presets (zhipu + zai)
|
|
41
|
-
|
|
42
|
-
**Files:**
|
|
43
|
-
- Modify: `lib/presets/providers.js:122-169` (zhipu and zai provider blocks)
|
|
44
|
-
|
|
45
|
-
- [ ] **Step 1: Update zhipu provider**
|
|
46
|
-
|
|
47
|
-
In `lib/presets/providers.js`, replace the `zhipu` block (lines 122–145):
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
zhipu: {
|
|
51
|
-
name: 'ZhiPu AI (GLM-5.1/5-Turbo/5/4.7) - 智谱清言',
|
|
52
|
-
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
53
|
-
models: [
|
|
54
|
-
'glm-5.1',
|
|
55
|
-
'glm-5-turbo',
|
|
56
|
-
'glm-5',
|
|
57
|
-
'glm-4.7'
|
|
58
|
-
],
|
|
59
|
-
versionAliases: {
|
|
60
|
-
'glm-4.5': 'glm-5.1',
|
|
61
|
-
'glm-4.6': 'glm-5.1'
|
|
62
|
-
},
|
|
63
|
-
authTokenFormat: 'sk-...',
|
|
64
|
-
description: 'ZhiPu AI (智谱清言) - Anthropic-compatible API for mainland China',
|
|
65
|
-
requiresToken: true,
|
|
66
|
-
compatibility: 'anthropic-compatible',
|
|
67
|
-
envVars: {
|
|
68
|
-
API_TIMEOUT_MS: '3000000',
|
|
69
|
-
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
|
|
70
|
-
},
|
|
71
|
-
note: 'Requires extended timeout for large responses'
|
|
72
|
-
},
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
- [ ] **Step 2: Update zai provider**
|
|
76
|
-
|
|
77
|
-
Replace the `zai` block (lines 146–169):
|
|
78
|
-
|
|
79
|
-
```js
|
|
80
|
-
zai: {
|
|
81
|
-
name: 'Z.ai (GLM-5.1/5-Turbo/5/4.7) - ZhiPu Global',
|
|
82
|
-
baseUrl: 'https://api.z.ai/api/anthropic',
|
|
83
|
-
models: [
|
|
84
|
-
'glm-5.1',
|
|
85
|
-
'glm-5-turbo',
|
|
86
|
-
'glm-5',
|
|
87
|
-
'glm-4.7'
|
|
88
|
-
],
|
|
89
|
-
versionAliases: {
|
|
90
|
-
'glm-4.5': 'glm-5.1',
|
|
91
|
-
'glm-4.6': 'glm-5.1'
|
|
92
|
-
},
|
|
93
|
-
authTokenFormat: 'sk-...',
|
|
94
|
-
description: 'Z.ai (ZhiPu AI Global) - Anthropic-compatible API for international users',
|
|
95
|
-
requiresToken: true,
|
|
96
|
-
compatibility: 'anthropic-compatible',
|
|
97
|
-
envVars: {
|
|
98
|
-
API_TIMEOUT_MS: '3000000',
|
|
99
|
-
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
|
|
100
|
-
},
|
|
101
|
-
note: 'Requires extended timeout for large responses'
|
|
102
|
-
},
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
- [ ] **Step 3: Verify — run node to check syntax**
|
|
106
|
-
|
|
107
|
-
Run: `node -e "const p = require('./lib/presets/providers'); const z = p.getProvider('zhipu'); const a = p.getProvider('zai'); console.log(z.name, z.models); console.log(a.name, a.models);"`
|
|
108
|
-
|
|
109
|
-
Expected output:
|
|
110
|
-
```
|
|
111
|
-
ZhiPu AI (GLM-5.1/5-Turbo/5/4.7) - 智谱清言 [ 'glm-5.1', 'glm-5-turbo', 'glm-5', 'glm-4.7' ]
|
|
112
|
-
Z.ai (GLM-5.1/5-Turbo/5/4.7) - ZhiPu Global [ 'glm-5.1', 'glm-5-turbo', 'glm-5', 'glm-4.7' ]
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
- [ ] **Step 4: Verify versionAliases only map removed models**
|
|
116
|
-
|
|
117
|
-
Run: `node -e "const p = require('./lib/presets/providers'); console.log('glm-4.5 ->', p.getLatestModel('glm-4.5', 'zhipu')); console.log('glm-4.6 ->', p.getLatestModel('glm-4.6', 'zhipu')); console.log('glm-5 ->', p.getLatestModel('glm-5', 'zhipu')); console.log('glm-5-turbo ->', p.getLatestModel('glm-5-turbo', 'zhipu')); console.log('glm-5.1 ->', p.getLatestModel('glm-5.1', 'zhipu'));"`
|
|
118
|
-
|
|
119
|
-
Expected output:
|
|
120
|
-
```
|
|
121
|
-
glm-4.5 -> glm-5.1
|
|
122
|
-
glm-4.6 -> glm-5.1
|
|
123
|
-
glm-5 -> null
|
|
124
|
-
glm-5-turbo -> null
|
|
125
|
-
glm-5.1 -> null
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
- [ ] **Step 5: Commit**
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
git add lib/presets/providers.js
|
|
132
|
-
git commit -m "feat: update GLM models to 5.1/5-Turbo for zhipu and zai providers"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
### Task 2: Update Kimi model presets (moonshot)
|
|
138
|
-
|
|
139
|
-
**Files:**
|
|
140
|
-
- Modify: `lib/presets/providers.js:37-56` (moonshot provider block)
|
|
141
|
-
|
|
142
|
-
- [ ] **Step 1: Update moonshot provider**
|
|
143
|
-
|
|
144
|
-
Replace the `moonshot` block (lines 37–56):
|
|
145
|
-
|
|
146
|
-
```js
|
|
147
|
-
moonshot: {
|
|
148
|
-
name: 'Moonshot AI (Kimi-K2.5/K2-Thinking)',
|
|
149
|
-
baseUrl: 'https://api.moonshot.cn/anthropic',
|
|
150
|
-
models: [
|
|
151
|
-
'kimi-k2.5',
|
|
152
|
-
'kimi-k2-thinking',
|
|
153
|
-
'kimi-k2-thinking-turbo'
|
|
154
|
-
],
|
|
155
|
-
versionAliases: {
|
|
156
|
-
'kimi-k2-0711-preview': 'kimi-k2.5',
|
|
157
|
-
'kimi-k2-0905-preview': 'kimi-k2.5',
|
|
158
|
-
'kimi-k2-turbo-preview': 'kimi-k2.5'
|
|
159
|
-
},
|
|
160
|
-
authTokenFormat: 'sk-...',
|
|
161
|
-
description: 'Moonshot AI - Provides Anthropic-compatible API',
|
|
162
|
-
requiresToken: true,
|
|
163
|
-
compatibility: 'anthropic-compatible',
|
|
164
|
-
envVars: {
|
|
165
|
-
API_TIMEOUT_MS: '3000000',
|
|
166
|
-
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
|
|
167
|
-
},
|
|
168
|
-
note: 'Requires extended timeout for large responses'
|
|
169
|
-
},
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
- [ ] **Step 2: Verify**
|
|
173
|
-
|
|
174
|
-
Run: `node -e "const p = require('./lib/presets/providers'); const m = p.getProvider('moonshot'); console.log(m.name, m.models); console.log('k2-0711 ->', p.getLatestModel('kimi-k2-0711-preview', 'moonshot')); console.log('k2-thinking ->', p.getLatestModel('kimi-k2-thinking', 'moonshot'));"`
|
|
175
|
-
|
|
176
|
-
Expected:
|
|
177
|
-
```
|
|
178
|
-
Moonshot AI (Kimi-K2.5/K2-Thinking) [ 'kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-thinking-turbo' ]
|
|
179
|
-
k2-0711 -> kimi-k2.5
|
|
180
|
-
k2-thinking -> null
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
- [ ] **Step 3: Commit**
|
|
184
|
-
|
|
185
|
-
```bash
|
|
186
|
-
git add lib/presets/providers.js
|
|
187
|
-
git commit -m "feat: update Kimi models to K2.5, remove preview versions"
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
### Task 3: Update MiniMax model presets (minimax_cn + minimax_global)
|
|
193
|
-
|
|
194
|
-
**Files:**
|
|
195
|
-
- Modify: `lib/presets/providers.js:73-104` (minimax_cn and minimax_global blocks)
|
|
196
|
-
|
|
197
|
-
- [ ] **Step 1: Update minimax_cn provider**
|
|
198
|
-
|
|
199
|
-
Replace the `minimax_cn` block (lines 73–88):
|
|
200
|
-
|
|
201
|
-
```js
|
|
202
|
-
minimax_cn: {
|
|
203
|
-
name: 'MiniMax CN (国内版)',
|
|
204
|
-
baseUrl: 'https://api.minimaxi.com/anthropic',
|
|
205
|
-
models: [
|
|
206
|
-
'MiniMax-M2.7',
|
|
207
|
-
'MiniMax-M2.5',
|
|
208
|
-
'MiniMax-M2.1'
|
|
209
|
-
],
|
|
210
|
-
authTokenFormat: 'sk-...',
|
|
211
|
-
description: 'MiniMax AI - Anthropic-compatible API for China users',
|
|
212
|
-
requiresToken: true,
|
|
213
|
-
compatibility: 'anthropic-compatible',
|
|
214
|
-
envVars: {
|
|
215
|
-
API_TIMEOUT_MS: '3000000',
|
|
216
|
-
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
|
|
217
|
-
},
|
|
218
|
-
note: 'Requires extended timeout for large responses'
|
|
219
|
-
},
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
- [ ] **Step 2: Update minimax_global provider**
|
|
223
|
-
|
|
224
|
-
Replace the `minimax_global` block (lines 89–104):
|
|
225
|
-
|
|
226
|
-
```js
|
|
227
|
-
minimax_global: {
|
|
228
|
-
name: 'MiniMax Global (国际版)',
|
|
229
|
-
baseUrl: 'https://api.minimax.io/anthropic',
|
|
230
|
-
models: [
|
|
231
|
-
'MiniMax-M2.7',
|
|
232
|
-
'MiniMax-M2.5',
|
|
233
|
-
'MiniMax-M2.1'
|
|
234
|
-
],
|
|
235
|
-
authTokenFormat: 'sk-...',
|
|
236
|
-
description: 'MiniMax AI - Anthropic-compatible API for international users',
|
|
237
|
-
requiresToken: true,
|
|
238
|
-
compatibility: 'anthropic-compatible',
|
|
239
|
-
envVars: {
|
|
240
|
-
API_TIMEOUT_MS: '3000000',
|
|
241
|
-
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
|
|
242
|
-
},
|
|
243
|
-
note: 'Requires extended timeout for large responses'
|
|
244
|
-
},
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
- [ ] **Step 3: Verify**
|
|
248
|
-
|
|
249
|
-
Run: `node -e "const p = require('./lib/presets/providers'); console.log(p.getProvider('minimax_cn').models); console.log(p.getProvider('minimax_global').models);"`
|
|
250
|
-
|
|
251
|
-
Expected:
|
|
252
|
-
```
|
|
253
|
-
[ 'MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1' ]
|
|
254
|
-
[ 'MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1' ]
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
- [ ] **Step 4: Commit**
|
|
258
|
-
|
|
259
|
-
```bash
|
|
260
|
-
git add lib/presets/providers.js
|
|
261
|
-
git commit -m "feat: add MiniMax M2.7 and M2.5 models"
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
### Task 4: Write tests for provider model updates
|
|
267
|
-
|
|
268
|
-
**Files:**
|
|
269
|
-
- Create: `test/providers.test.js`
|
|
270
|
-
|
|
271
|
-
- [ ] **Step 1: Create test directory and test file**
|
|
272
|
-
|
|
273
|
-
Run: `mkdir -p test`
|
|
274
|
-
|
|
275
|
-
Create `test/providers.test.js`:
|
|
276
|
-
|
|
277
|
-
```js
|
|
278
|
-
/**
|
|
279
|
-
* Tests for provider model configurations
|
|
280
|
-
* Verifies model lists, versionAliases, and upgrade detection invariants
|
|
281
|
-
*/
|
|
282
|
-
|
|
283
|
-
const assert = require('assert');
|
|
284
|
-
const {
|
|
285
|
-
getProvider,
|
|
286
|
-
getLatestModel,
|
|
287
|
-
hasModelUpgrade,
|
|
288
|
-
getSuggestedModels
|
|
289
|
-
} = require('../lib/presets/providers');
|
|
290
|
-
|
|
291
|
-
let passed = 0;
|
|
292
|
-
let failed = 0;
|
|
293
|
-
|
|
294
|
-
function test(name, fn) {
|
|
295
|
-
try {
|
|
296
|
-
fn();
|
|
297
|
-
passed++;
|
|
298
|
-
console.log(` ✓ ${name}`);
|
|
299
|
-
} catch (e) {
|
|
300
|
-
failed++;
|
|
301
|
-
console.log(` ✗ ${name}`);
|
|
302
|
-
console.log(` ${e.message}`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// ─── GLM (zhipu) ───
|
|
307
|
-
|
|
308
|
-
test('zhipu: models list is correct', () => {
|
|
309
|
-
const p = getProvider('zhipu');
|
|
310
|
-
assert.deepStrictEqual(p.models, ['glm-5.1', 'glm-5-turbo', 'glm-5', 'glm-4.7']);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test('zhipu: name includes all current model families', () => {
|
|
314
|
-
const p = getProvider('zhipu');
|
|
315
|
-
assert.ok(p.name.includes('GLM-5.1'));
|
|
316
|
-
assert.ok(p.name.includes('5-Turbo'));
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
test('zhipu: removed glm-4.5 aliases to glm-5.1', () => {
|
|
320
|
-
assert.strictEqual(getLatestModel('glm-4.5', 'zhipu'), 'glm-5.1');
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
test('zhipu: removed glm-4.6 aliases to glm-5.1', () => {
|
|
324
|
-
assert.strictEqual(getLatestModel('glm-4.6', 'zhipu'), 'glm-5.1');
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
test('zhipu: existing glm-5 has NO alias (not deprecated)', () => {
|
|
328
|
-
assert.strictEqual(getLatestModel('glm-5', 'zhipu'), null);
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test('zhipu: existing glm-5-turbo has NO alias (not deprecated)', () => {
|
|
332
|
-
assert.strictEqual(getLatestModel('glm-5-turbo', 'zhipu'), null);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
test('zhipu: existing glm-4.7 has NO alias (not deprecated)', () => {
|
|
336
|
-
assert.strictEqual(getLatestModel('glm-4.7', 'zhipu'), null);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
test('zhipu: latest glm-5.1 has NO alias', () => {
|
|
340
|
-
assert.strictEqual(getLatestModel('glm-5.1', 'zhipu'), null);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
// ─── GLM (zai) — must mirror zhipu ───
|
|
344
|
-
|
|
345
|
-
test('zai: models list matches zhipu', () => {
|
|
346
|
-
const z = getProvider('zhipu');
|
|
347
|
-
const a = getProvider('zai');
|
|
348
|
-
assert.deepStrictEqual(a.models, z.models);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test('zai: versionAliases matches zhipu', () => {
|
|
352
|
-
const z = getProvider('zhipu');
|
|
353
|
-
const a = getProvider('zai');
|
|
354
|
-
assert.deepStrictEqual(a.versionAliases, z.versionAliases);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
// ─── Kimi (moonshot) ───
|
|
358
|
-
|
|
359
|
-
test('moonshot: models list is correct', () => {
|
|
360
|
-
const p = getProvider('moonshot');
|
|
361
|
-
assert.deepStrictEqual(p.models, ['kimi-k2.5', 'kimi-k2-thinking', 'kimi-k2-thinking-turbo']);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
test('moonshot: removed kimi-k2-0711-preview aliases to kimi-k2.5', () => {
|
|
365
|
-
assert.strictEqual(getLatestModel('kimi-k2-0711-preview', 'moonshot'), 'kimi-k2.5');
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
test('moonshot: removed kimi-k2-0905-preview aliases to kimi-k2.5', () => {
|
|
369
|
-
assert.strictEqual(getLatestModel('kimi-k2-0905-preview', 'moonshot'), 'kimi-k2.5');
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
test('moonshot: removed kimi-k2-turbo-preview aliases to kimi-k2.5', () => {
|
|
373
|
-
assert.strictEqual(getLatestModel('kimi-k2-turbo-preview', 'moonshot'), 'kimi-k2.5');
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
test('moonshot: existing kimi-k2-thinking has NO alias (not deprecated)', () => {
|
|
377
|
-
assert.strictEqual(getLatestModel('kimi-k2-thinking', 'moonshot'), null);
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
test('moonshot: existing kimi-k2-thinking-turbo has NO alias (not deprecated)', () => {
|
|
381
|
-
assert.strictEqual(getLatestModel('kimi-k2-thinking-turbo', 'moonshot'), null);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
test('moonshot: latest kimi-k2.5 has NO alias', () => {
|
|
385
|
-
assert.strictEqual(getLatestModel('kimi-k2.5', 'moonshot'), null);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// ─── MiniMax ───
|
|
389
|
-
|
|
390
|
-
test('minimax_cn: models list is correct', () => {
|
|
391
|
-
const p = getProvider('minimax_cn');
|
|
392
|
-
assert.deepStrictEqual(p.models, ['MiniMax-M2.7', 'MiniMax-M2.5', 'MiniMax-M2.1']);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
test('minimax_cn: no versionAliases (all models are concurrent tiers)', () => {
|
|
396
|
-
const p = getProvider('minimax_cn');
|
|
397
|
-
assert.strictEqual(p.versionAliases, undefined);
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
test('minimax_global: models list matches minimax_cn', () => {
|
|
401
|
-
const cn = getProvider('minimax_cn');
|
|
402
|
-
const gl = getProvider('minimax_global');
|
|
403
|
-
assert.deepStrictEqual(gl.models, cn.models);
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
test('minimax_global: MiniMax-M2.1 has NO alias', () => {
|
|
407
|
-
assert.strictEqual(getLatestModel('MiniMax-M2.1', 'minimax_global'), null);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
// ─── Unchanged providers — regression guard ───
|
|
411
|
-
|
|
412
|
-
test('anthropic: models unchanged, includes claude-opus-4-6', () => {
|
|
413
|
-
const p = getProvider('anthropic');
|
|
414
|
-
assert.ok(p.models.includes('claude-opus-4-6'));
|
|
415
|
-
assert.ok(p.models.includes('claude-sonnet-4-5'));
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
test('anthropic: versionAliases still map opus series', () => {
|
|
419
|
-
assert.strictEqual(getLatestModel('claude-opus-4', 'anthropic'), 'claude-opus-4-6');
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
test('deepseek: models unchanged', () => {
|
|
423
|
-
const p = getProvider('deepseek');
|
|
424
|
-
assert.deepStrictEqual(p.models, ['deepseek-chat', 'deepseek-reasoner']);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test('kimi_for_coding: models unchanged', () => {
|
|
428
|
-
const p = getProvider('kimi_for_coding');
|
|
429
|
-
assert.deepStrictEqual(p.models, ['kimi-for-coding']);
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
// ─── Cross-cutting: no active model in versionAliases ───
|
|
433
|
-
|
|
434
|
-
test('invariant: no provider has an active model as a versionAlias key', () => {
|
|
435
|
-
const { providers } = require('../lib/presets/providers');
|
|
436
|
-
for (const [id, provider] of Object.entries(providers)) {
|
|
437
|
-
if (!provider.versionAliases) continue;
|
|
438
|
-
for (const aliasKey of Object.keys(provider.versionAliases)) {
|
|
439
|
-
assert.ok(
|
|
440
|
-
!provider.models.includes(aliasKey),
|
|
441
|
-
`Provider "${id}": model "${aliasKey}" is both in models[] and versionAliases (would trigger unwanted upgrade)`
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// ─── Summary ───
|
|
448
|
-
|
|
449
|
-
console.log(`\n ${passed} passed, ${failed} failed\n`);
|
|
450
|
-
if (failed > 0) process.exit(1);
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
- [ ] **Step 2: Run tests — verify they pass**
|
|
454
|
-
|
|
455
|
-
Run: `node test/providers.test.js`
|
|
456
|
-
|
|
457
|
-
Expected: All tests pass (0 failed).
|
|
458
|
-
|
|
459
|
-
- [ ] **Step 3: Commit**
|
|
460
|
-
|
|
461
|
-
```bash
|
|
462
|
-
git add test/providers.test.js
|
|
463
|
-
git commit -m "test: add provider model config tests"
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
---
|
|
467
|
-
|
|
468
|
-
### Task 5: Add `launchClaudeAutoMode()` to launcher
|
|
469
|
-
|
|
470
|
-
**Files:**
|
|
471
|
-
- Modify: `lib/launcher.js:182-184` (after `launchClaudeSkipPermissions`), `lib/launcher.js:352-358` (module.exports)
|
|
472
|
-
|
|
473
|
-
- [ ] **Step 1: Add function**
|
|
474
|
-
|
|
475
|
-
In `lib/launcher.js`, after the `launchClaudeSkipPermissions` function (after line 184), add:
|
|
476
|
-
|
|
477
|
-
```js
|
|
478
|
-
/**
|
|
479
|
-
* Launch Claude with auto mode enabled
|
|
480
|
-
* Note: --enable-auto-mode makes auto mode available as a permission mode.
|
|
481
|
-
* User must press Shift+Tab in the session to switch to it.
|
|
482
|
-
*/
|
|
483
|
-
function launchClaudeAutoMode() {
|
|
484
|
-
launchClaude('claude --enable-auto-mode');
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
- [ ] **Step 2: Add to module.exports**
|
|
489
|
-
|
|
490
|
-
In `lib/launcher.js`, update the `module.exports` block (line 352) to add `launchClaudeAutoMode`:
|
|
491
|
-
|
|
492
|
-
```js
|
|
493
|
-
module.exports = {
|
|
494
|
-
launchClaude,
|
|
495
|
-
launchClaudeDefault,
|
|
496
|
-
launchClaudeSkipPermissions,
|
|
497
|
-
launchClaudeAutoMode,
|
|
498
|
-
launchClaudeWithApi,
|
|
499
|
-
getProviderEnvVars,
|
|
500
|
-
testApiConnection
|
|
501
|
-
};
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
- [ ] **Step 3: Verify — require and check it exists**
|
|
505
|
-
|
|
506
|
-
Run: `node -e "const l = require('./lib/launcher'); console.log(typeof l.launchClaudeAutoMode);"`
|
|
507
|
-
|
|
508
|
-
Expected: `function`
|
|
509
|
-
|
|
510
|
-
- [ ] **Step 4: Commit**
|
|
511
|
-
|
|
512
|
-
```bash
|
|
513
|
-
git add lib/launcher.js
|
|
514
|
-
git commit -m "feat: add launchClaudeAutoMode() for --enable-auto-mode flag"
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
---
|
|
518
|
-
|
|
519
|
-
### Task 6: Add hintCallback support to Menu class
|
|
520
|
-
|
|
521
|
-
**Files:**
|
|
522
|
-
- Modify: `lib/ui/menu.js:64-96` (`displayMenu` method), `lib/ui/menu.js:111-228` (`navigate` method)
|
|
523
|
-
|
|
524
|
-
- [ ] **Step 1: Update `displayMenu` signature and rendering**
|
|
525
|
-
|
|
526
|
-
In `lib/ui/menu.js`, change the `displayMenu` method signature (line 64) from:
|
|
527
|
-
|
|
528
|
-
```js
|
|
529
|
-
displayMenu(clearScreen = true, versionInfo = null) {
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
to:
|
|
533
|
-
|
|
534
|
-
```js
|
|
535
|
-
displayMenu(clearScreen = true, versionInfo = null, hintCallback = null) {
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
Then, at the end of `displayMenu`, replace the final `console.log('');` (line 95) with hint rendering logic:
|
|
539
|
-
|
|
540
|
-
```js
|
|
541
|
-
// Render dynamic hint if callback provided
|
|
542
|
-
if (hintCallback) {
|
|
543
|
-
const hintText = hintCallback(this.selectedIndex);
|
|
544
|
-
if (hintText) {
|
|
545
|
-
console.log(colors.cyan + ' ℹ ' + colors.gray + hintText + colors.reset);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
console.log('');
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
- [ ] **Step 2: Update `navigate` signature and pass-through**
|
|
553
|
-
|
|
554
|
-
In `lib/ui/menu.js`, change the `navigate` method signature (line 111) from:
|
|
555
|
-
|
|
556
|
-
```js
|
|
557
|
-
async navigate(clearScreen = true, versionInfo = null) {
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
to:
|
|
561
|
-
|
|
562
|
-
```js
|
|
563
|
-
async navigate(clearScreen = true, versionInfo = null, hintCallback = null) {
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
Store hintCallback for redrawing — change line 118:
|
|
567
|
-
|
|
568
|
-
```js
|
|
569
|
-
this.versionInfo = versionInfo; // Store for redrawing
|
|
570
|
-
```
|
|
571
|
-
|
|
572
|
-
to:
|
|
573
|
-
|
|
574
|
-
```js
|
|
575
|
-
this.versionInfo = versionInfo; // Store for redrawing
|
|
576
|
-
this.hintCallback = hintCallback; // Store for redrawing
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
Update the initial `displayMenu` call (line 121):
|
|
580
|
-
|
|
581
|
-
```js
|
|
582
|
-
this.displayMenu(clearScreen, versionInfo, hintCallback);
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
Update both arrow key cases in `handleKeyPress` (lines 173-174 and 178-179) to pass `hintCallback`:
|
|
586
|
-
|
|
587
|
-
```js
|
|
588
|
-
case '\u001b[A': // Up arrow
|
|
589
|
-
this.selectedIndex = (this.selectedIndex - 1 + this.menuOptions.length) % this.menuOptions.length;
|
|
590
|
-
this.displayMenu(true, this.versionInfo, this.hintCallback);
|
|
591
|
-
break;
|
|
592
|
-
|
|
593
|
-
case '\u001b[B': // Down arrow
|
|
594
|
-
this.selectedIndex = (this.selectedIndex + 1) % this.menuOptions.length;
|
|
595
|
-
this.displayMenu(true, this.versionInfo, this.hintCallback);
|
|
596
|
-
break;
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
- [ ] **Step 3: Verify — require and check no syntax errors**
|
|
600
|
-
|
|
601
|
-
Run: `node -e "const Menu = require('./lib/ui/menu'); const m = new Menu(); console.log(typeof m.displayMenu, typeof m.navigate);"`
|
|
602
|
-
|
|
603
|
-
Expected: `function function`
|
|
604
|
-
|
|
605
|
-
- [ ] **Step 4: Commit**
|
|
606
|
-
|
|
607
|
-
```bash
|
|
608
|
-
git add lib/ui/menu.js
|
|
609
|
-
git commit -m "feat: add hintCallback support to Menu.displayMenu() and navigate()"
|
|
610
|
-
```
|
|
611
|
-
|
|
612
|
-
---
|
|
613
|
-
|
|
614
|
-
### Task 7: Write tests for menu hint rendering
|
|
615
|
-
|
|
616
|
-
**Files:**
|
|
617
|
-
- Create: `test/menu-hints.test.js`
|
|
618
|
-
|
|
619
|
-
- [ ] **Step 1: Create test file**
|
|
620
|
-
|
|
621
|
-
Create `test/menu-hints.test.js`:
|
|
622
|
-
|
|
623
|
-
```js
|
|
624
|
-
/**
|
|
625
|
-
* Tests for Menu hintCallback rendering
|
|
626
|
-
* Captures console.log output to verify hint behavior
|
|
627
|
-
*/
|
|
628
|
-
|
|
629
|
-
const assert = require('assert');
|
|
630
|
-
|
|
631
|
-
let passed = 0;
|
|
632
|
-
let failed = 0;
|
|
633
|
-
|
|
634
|
-
function test(name, fn) {
|
|
635
|
-
try {
|
|
636
|
-
fn();
|
|
637
|
-
passed++;
|
|
638
|
-
console.log(` ✓ ${name}`);
|
|
639
|
-
} catch (e) {
|
|
640
|
-
failed++;
|
|
641
|
-
console.log(` ✗ ${name}`);
|
|
642
|
-
console.log(` ${e.message}`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// Helper: capture console.log output during a function call
|
|
647
|
-
function captureLog(fn) {
|
|
648
|
-
const logs = [];
|
|
649
|
-
const original = console.log;
|
|
650
|
-
const originalClear = console.clear;
|
|
651
|
-
console.log = (...args) => logs.push(args.join(' '));
|
|
652
|
-
console.clear = () => {}; // suppress clear
|
|
653
|
-
try {
|
|
654
|
-
fn();
|
|
655
|
-
} finally {
|
|
656
|
-
console.log = original;
|
|
657
|
-
console.clear = originalClear;
|
|
658
|
-
}
|
|
659
|
-
return logs;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const Menu = require('../lib/ui/menu');
|
|
663
|
-
|
|
664
|
-
// ─── displayMenu with hintCallback ───
|
|
665
|
-
|
|
666
|
-
test('displayMenu: no hint when hintCallback is null', () => {
|
|
667
|
-
const m = new Menu();
|
|
668
|
-
m.setOptions(['Option A', 'Option B']);
|
|
669
|
-
const logs = captureLog(() => m.displayMenu(false, null, null));
|
|
670
|
-
const hintLines = logs.filter(l => l.includes('ℹ'));
|
|
671
|
-
assert.strictEqual(hintLines.length, 0);
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
test('displayMenu: no hint when hintCallback returns null for selected index', () => {
|
|
675
|
-
const m = new Menu();
|
|
676
|
-
m.setOptions(['Option A', 'Option B']);
|
|
677
|
-
const cb = (idx) => idx === 1 ? 'Some hint' : null;
|
|
678
|
-
const logs = captureLog(() => m.displayMenu(false, null, cb));
|
|
679
|
-
// selectedIndex defaults to 0, callback returns null for 0
|
|
680
|
-
const hintLines = logs.filter(l => l.includes('ℹ'));
|
|
681
|
-
assert.strictEqual(hintLines.length, 0);
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
test('displayMenu: shows hint when hintCallback returns string for selected index', () => {
|
|
685
|
-
const m = new Menu();
|
|
686
|
-
m.setOptions(['Option A', 'Option B']);
|
|
687
|
-
m.selectedIndex = 1;
|
|
688
|
-
const cb = (idx) => idx === 1 ? 'Test hint text' : null;
|
|
689
|
-
const logs = captureLog(() => m.displayMenu(false, null, cb));
|
|
690
|
-
const hintLines = logs.filter(l => l.includes('ℹ') && l.includes('Test hint text'));
|
|
691
|
-
assert.strictEqual(hintLines.length, 1);
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
test('displayMenu: hint changes when selectedIndex changes', () => {
|
|
695
|
-
const m = new Menu();
|
|
696
|
-
m.setOptions(['A', 'B', 'C']);
|
|
697
|
-
|
|
698
|
-
const cb = (idx) => {
|
|
699
|
-
if (idx === 0) return 'Hint for A';
|
|
700
|
-
if (idx === 1) return 'Hint for B';
|
|
701
|
-
return null;
|
|
702
|
-
};
|
|
703
|
-
|
|
704
|
-
m.selectedIndex = 0;
|
|
705
|
-
const logs0 = captureLog(() => m.displayMenu(false, null, cb));
|
|
706
|
-
assert.ok(logs0.some(l => l.includes('Hint for A')));
|
|
707
|
-
|
|
708
|
-
m.selectedIndex = 1;
|
|
709
|
-
const logs1 = captureLog(() => m.displayMenu(false, null, cb));
|
|
710
|
-
assert.ok(logs1.some(l => l.includes('Hint for B')));
|
|
711
|
-
|
|
712
|
-
m.selectedIndex = 2;
|
|
713
|
-
const logs2 = captureLog(() => m.displayMenu(false, null, cb));
|
|
714
|
-
assert.ok(!logs2.some(l => l.includes('ℹ')));
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
test('displayMenu: backward compat — works without hintCallback', () => {
|
|
718
|
-
const m = new Menu();
|
|
719
|
-
m.setOptions(['Option A']);
|
|
720
|
-
// Call with only 2 args (old signature)
|
|
721
|
-
const logs = captureLog(() => m.displayMenu(false, null));
|
|
722
|
-
assert.ok(logs.length > 0); // rendered something
|
|
723
|
-
const hintLines = logs.filter(l => l.includes('ℹ'));
|
|
724
|
-
assert.strictEqual(hintLines.length, 0);
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
// ─── navigate() pass-through and arrow-key redraw ───
|
|
728
|
-
// These tests call navigate() for real with a stubbed stdinManager,
|
|
729
|
-
// then inject arrow keys and Enter to drive the menu and capture output.
|
|
730
|
-
|
|
731
|
-
const EventEmitter = require('events');
|
|
732
|
-
const stdinManager = require('../lib/utils/stdin-manager');
|
|
733
|
-
|
|
734
|
-
// Helper: create a fake StdinScope that records 'data' handlers
|
|
735
|
-
// and allows us to inject key sequences
|
|
736
|
-
function createFakeScope() {
|
|
737
|
-
const emitter = new EventEmitter();
|
|
738
|
-
emitter.release = () => {};
|
|
739
|
-
emitter.removeListener = (ev, fn) => emitter.off(ev, fn);
|
|
740
|
-
return emitter;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Helper: run an async test (navigate returns a Promise)
|
|
744
|
-
function asyncTest(name, fn) {
|
|
745
|
-
const p = fn().then(() => {
|
|
746
|
-
passed++;
|
|
747
|
-
console.log(` ✓ ${name}`);
|
|
748
|
-
}).catch((e) => {
|
|
749
|
-
failed++;
|
|
750
|
-
console.log(` ✗ ${name}`);
|
|
751
|
-
console.log(` ${e.message}`);
|
|
752
|
-
});
|
|
753
|
-
return p;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// We need process.stdin.isTTY = true for navigate() to use raw mode path
|
|
757
|
-
const origIsTTY = process.stdin.isTTY;
|
|
758
|
-
|
|
759
|
-
const allAsync = Promise.resolve()
|
|
760
|
-
|
|
761
|
-
.then(() => asyncTest('navigate: accepts hintCallback 3rd param and renders hint on initial draw', async () => {
|
|
762
|
-
const fakeScope = createFakeScope();
|
|
763
|
-
const origAcquire = stdinManager.acquire.bind(stdinManager);
|
|
764
|
-
stdinManager.acquire = () => fakeScope;
|
|
765
|
-
process.stdin.isTTY = true;
|
|
766
|
-
|
|
767
|
-
const m = new Menu();
|
|
768
|
-
m.setOptions(['A', 'B']);
|
|
769
|
-
const cb = (idx) => idx === 0 ? 'Initial hint' : null;
|
|
770
|
-
|
|
771
|
-
let initialLogs = [];
|
|
772
|
-
const origLog = console.log;
|
|
773
|
-
const origClear = console.clear;
|
|
774
|
-
console.log = (...args) => initialLogs.push(args.join(' '));
|
|
775
|
-
console.clear = () => {};
|
|
776
|
-
|
|
777
|
-
const navPromise = m.navigate(false, null, cb);
|
|
778
|
-
|
|
779
|
-
// navigate() has now called displayMenu and registered the handler
|
|
780
|
-
// Capture is already in initialLogs. Resolve by sending Enter.
|
|
781
|
-
console.log = origLog;
|
|
782
|
-
console.clear = origClear;
|
|
783
|
-
|
|
784
|
-
fakeScope.emit('data', '\r'); // Enter → resolve
|
|
785
|
-
await navPromise;
|
|
786
|
-
|
|
787
|
-
stdinManager.acquire = origAcquire;
|
|
788
|
-
process.stdin.isTTY = origIsTTY;
|
|
789
|
-
|
|
790
|
-
assert.ok(initialLogs.some(l => l.includes('Initial hint')),
|
|
791
|
-
'Initial displayMenu call should render hint from callback');
|
|
792
|
-
}))
|
|
793
|
-
|
|
794
|
-
.then(() => asyncTest('navigate: arrow key redraw passes stored hintCallback', async () => {
|
|
795
|
-
const fakeScope = createFakeScope();
|
|
796
|
-
const origAcquire = stdinManager.acquire.bind(stdinManager);
|
|
797
|
-
stdinManager.acquire = () => fakeScope;
|
|
798
|
-
process.stdin.isTTY = true;
|
|
799
|
-
|
|
800
|
-
const m = new Menu();
|
|
801
|
-
m.setOptions(['A', 'B', 'C']);
|
|
802
|
-
const cb = (idx) => {
|
|
803
|
-
if (idx === 1) return 'Hint for B';
|
|
804
|
-
return null;
|
|
805
|
-
};
|
|
806
|
-
|
|
807
|
-
// Suppress initial draw
|
|
808
|
-
const origLog = console.log;
|
|
809
|
-
const origClear = console.clear;
|
|
810
|
-
console.log = () => {};
|
|
811
|
-
console.clear = () => {};
|
|
812
|
-
|
|
813
|
-
const navPromise = m.navigate(false, null, cb);
|
|
814
|
-
|
|
815
|
-
// Now inject Down arrow and capture the redraw
|
|
816
|
-
let downLogs = [];
|
|
817
|
-
console.log = (...args) => downLogs.push(args.join(' '));
|
|
818
|
-
fakeScope.emit('data', '\u001b[B'); // Down → index 1
|
|
819
|
-
|
|
820
|
-
console.log = origLog;
|
|
821
|
-
console.clear = origClear;
|
|
822
|
-
|
|
823
|
-
assert.ok(downLogs.some(l => l.includes('Hint for B')),
|
|
824
|
-
'After arrow down to index 1, hint should show "Hint for B"');
|
|
825
|
-
|
|
826
|
-
// Arrow down again to index 2 (no hint)
|
|
827
|
-
let downLogs2 = [];
|
|
828
|
-
console.log = (...args) => downLogs2.push(args.join(' '));
|
|
829
|
-
console.clear = () => {};
|
|
830
|
-
fakeScope.emit('data', '\u001b[B'); // Down → index 2
|
|
831
|
-
console.log = origLog;
|
|
832
|
-
console.clear = origClear;
|
|
833
|
-
|
|
834
|
-
assert.ok(!downLogs2.some(l => l.includes('ℹ')),
|
|
835
|
-
'After arrow down to index 2, no hint should appear');
|
|
836
|
-
|
|
837
|
-
// Resolve
|
|
838
|
-
console.log = () => {};
|
|
839
|
-
console.clear = () => {};
|
|
840
|
-
fakeScope.emit('data', '\r');
|
|
841
|
-
console.log = origLog;
|
|
842
|
-
console.clear = origClear;
|
|
843
|
-
await navPromise;
|
|
844
|
-
|
|
845
|
-
stdinManager.acquire = origAcquire;
|
|
846
|
-
process.stdin.isTTY = origIsTTY;
|
|
847
|
-
}))
|
|
848
|
-
|
|
849
|
-
.then(() => asyncTest('navigate: without hintCallback (2 args), no hint rendered on arrow', async () => {
|
|
850
|
-
const fakeScope = createFakeScope();
|
|
851
|
-
const origAcquire = stdinManager.acquire.bind(stdinManager);
|
|
852
|
-
stdinManager.acquire = () => fakeScope;
|
|
853
|
-
process.stdin.isTTY = true;
|
|
854
|
-
|
|
855
|
-
const m = new Menu();
|
|
856
|
-
m.setOptions(['A', 'B']);
|
|
857
|
-
|
|
858
|
-
const origLog = console.log;
|
|
859
|
-
const origClear = console.clear;
|
|
860
|
-
console.log = () => {};
|
|
861
|
-
console.clear = () => {};
|
|
862
|
-
|
|
863
|
-
// Call with only 2 args (old contract)
|
|
864
|
-
const navPromise = m.navigate(false, null);
|
|
865
|
-
|
|
866
|
-
let downLogs = [];
|
|
867
|
-
console.log = (...args) => downLogs.push(args.join(' '));
|
|
868
|
-
fakeScope.emit('data', '\u001b[B'); // Down
|
|
869
|
-
console.log = origLog;
|
|
870
|
-
console.clear = origClear;
|
|
871
|
-
|
|
872
|
-
assert.ok(!downLogs.some(l => l.includes('ℹ')),
|
|
873
|
-
'Without hintCallback, no hint should appear on arrow');
|
|
874
|
-
|
|
875
|
-
console.log = () => {};
|
|
876
|
-
console.clear = () => {};
|
|
877
|
-
fakeScope.emit('data', '\r');
|
|
878
|
-
console.log = origLog;
|
|
879
|
-
console.clear = origClear;
|
|
880
|
-
await navPromise;
|
|
881
|
-
|
|
882
|
-
stdinManager.acquire = origAcquire;
|
|
883
|
-
process.stdin.isTTY = origIsTTY;
|
|
884
|
-
}));
|
|
885
|
-
|
|
886
|
-
// ─── Summary (after async tests complete) ───
|
|
887
|
-
|
|
888
|
-
allAsync.then(() => {
|
|
889
|
-
console.log(`\n ${passed} passed, ${failed} failed\n`);
|
|
890
|
-
if (failed > 0) process.exit(1);
|
|
891
|
-
});
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
- [ ] **Step 2: Run tests — verify they pass**
|
|
895
|
-
|
|
896
|
-
Run: `node test/menu-hints.test.js`
|
|
897
|
-
|
|
898
|
-
Expected: All tests pass (0 failed). The async navigate() tests run after the sync displayMenu() tests.
|
|
899
|
-
|
|
900
|
-
- [ ] **Step 3: Commit**
|
|
901
|
-
|
|
902
|
-
```bash
|
|
903
|
-
git add test/menu-hints.test.js
|
|
904
|
-
git commit -m "test: add menu hintCallback rendering tests"
|
|
905
|
-
```
|
|
906
|
-
|
|
907
|
-
---
|
|
908
|
-
|
|
909
|
-
### Task 8: Add i18n keys to all 11 locale files
|
|
910
|
-
|
|
911
|
-
**Files:**
|
|
912
|
-
- Modify: `lib/i18n/locales/en.js` (and 10 other locale files)
|
|
913
|
-
|
|
914
|
-
The 4 new keys to add to every locale, nested under their existing parent objects:
|
|
915
|
-
|
|
916
|
-
1. `menu.main.launch_auto_mode` — inside `menu.main { ... }`
|
|
917
|
-
2. `hints.auto_mode_info` — new top-level `hints` object
|
|
918
|
-
3. `hints.active_api_info` — inside `hints { ... }`
|
|
919
|
-
4. `hints.no_active_api` — inside `hints { ... }`
|
|
920
|
-
|
|
921
|
-
- [ ] **Step 1: Add keys to en.js**
|
|
922
|
-
|
|
923
|
-
In `lib/i18n/locales/en.js`, inside `menu.main` (after the `launch_skip` line, around line 12), add:
|
|
924
|
-
|
|
925
|
-
```js
|
|
926
|
-
launch_auto_mode: "Launch Claude Code (Enable Auto Mode)",
|
|
927
|
-
```
|
|
928
|
-
|
|
929
|
-
Then, add a new `hints` top-level section. Find a suitable location (e.g. after the `version` section near the end of the file) and add:
|
|
930
|
-
|
|
931
|
-
```js
|
|
932
|
-
hints: {
|
|
933
|
-
auto_mode_info: 'Auto Mode: Currently supports Team plan. Enterprise/API rolling out. Use Shift+Tab to switch after launch.',
|
|
934
|
-
active_api_info: 'Active: {0} / {1}',
|
|
935
|
-
no_active_api: 'No active API configured. Go to "API Management" to add one.'
|
|
936
|
-
},
|
|
937
|
-
```
|
|
938
|
-
|
|
939
|
-
- [ ] **Step 2: Add keys to zh.js (Chinese Simplified)**
|
|
940
|
-
|
|
941
|
-
In `menu.main`, after `launch_skip`:
|
|
942
|
-
```js
|
|
943
|
-
launch_auto_mode: "启动 Claude Code(启用自动模式)",
|
|
944
|
-
```
|
|
945
|
-
|
|
946
|
-
New `hints` section:
|
|
947
|
-
```js
|
|
948
|
-
hints: {
|
|
949
|
-
auto_mode_info: '自动模式:目前支持 Team 计划。Enterprise/API 计划逐步推出中。启动后按 Shift+Tab 切换。',
|
|
950
|
-
active_api_info: '当前激活:{0} / {1}',
|
|
951
|
-
no_active_api: '未配置激活的API,请前往"API管理"添加。'
|
|
952
|
-
},
|
|
953
|
-
```
|
|
954
|
-
|
|
955
|
-
- [ ] **Step 3: Add keys to zh-TW.js (Chinese Traditional)**
|
|
956
|
-
|
|
957
|
-
In `menu.main`, after `launch_skip`:
|
|
958
|
-
```js
|
|
959
|
-
launch_auto_mode: "啟動 Claude Code(啟用自動模式)",
|
|
960
|
-
```
|
|
961
|
-
|
|
962
|
-
New `hints` section:
|
|
963
|
-
```js
|
|
964
|
-
hints: {
|
|
965
|
-
auto_mode_info: '自動模式:目前支援 Team 方案。Enterprise/API 方案逐步推出中。啟動後按 Shift+Tab 切換。',
|
|
966
|
-
active_api_info: '目前啟用:{0} / {1}',
|
|
967
|
-
no_active_api: '未設定啟用的API,請前往「API管理」新增。'
|
|
968
|
-
},
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
- [ ] **Step 4: Add keys to ja.js (Japanese)**
|
|
972
|
-
|
|
973
|
-
In `menu.main`, after `launch_skip`:
|
|
974
|
-
```js
|
|
975
|
-
launch_auto_mode: "Claude Code を起動(自動モード有効化)",
|
|
976
|
-
```
|
|
977
|
-
|
|
978
|
-
New `hints` section:
|
|
979
|
-
```js
|
|
980
|
-
hints: {
|
|
981
|
-
auto_mode_info: '自動モード:現在 Team プランで利用可能。Enterprise/API プランは順次展開中。起動後 Shift+Tab で切替。',
|
|
982
|
-
active_api_info: 'アクティブ:{0} / {1}',
|
|
983
|
-
no_active_api: 'アクティブなAPIがありません。「API管理」から追加してください。'
|
|
984
|
-
},
|
|
985
|
-
```
|
|
986
|
-
|
|
987
|
-
- [ ] **Step 5: Add keys to ko.js (Korean)**
|
|
988
|
-
|
|
989
|
-
In `menu.main`, after `launch_skip`:
|
|
990
|
-
```js
|
|
991
|
-
launch_auto_mode: "Claude Code 실행 (자동 모드 활성화)",
|
|
992
|
-
```
|
|
993
|
-
|
|
994
|
-
New `hints` section:
|
|
995
|
-
```js
|
|
996
|
-
hints: {
|
|
997
|
-
auto_mode_info: '자동 모드: 현재 Team 플랜에서 지원됩니다. Enterprise/API 플랜은 순차 출시 중입니다. 실행 후 Shift+Tab으로 전환하세요.',
|
|
998
|
-
active_api_info: '활성: {0} / {1}',
|
|
999
|
-
no_active_api: '활성화된 API가 없습니다. "API 관리"에서 추가하세요.'
|
|
1000
|
-
},
|
|
1001
|
-
```
|
|
1002
|
-
|
|
1003
|
-
- [ ] **Step 6: Add keys to de.js (German)**
|
|
1004
|
-
|
|
1005
|
-
In `menu.main`, after `launch_skip`:
|
|
1006
|
-
```js
|
|
1007
|
-
launch_auto_mode: "Claude Code starten (Auto-Modus aktivieren)",
|
|
1008
|
-
```
|
|
1009
|
-
|
|
1010
|
-
New `hints` section:
|
|
1011
|
-
```js
|
|
1012
|
-
hints: {
|
|
1013
|
-
auto_mode_info: 'Auto-Modus: Derzeit fuer Team-Plan verfuegbar. Enterprise/API wird schrittweise eingefuehrt. Nach dem Start mit Shift+Tab wechseln.',
|
|
1014
|
-
active_api_info: 'Aktiv: {0} / {1}',
|
|
1015
|
-
no_active_api: 'Keine aktive API konfiguriert. Gehen Sie zur "API-Verwaltung", um eine hinzuzufuegen.'
|
|
1016
|
-
},
|
|
1017
|
-
```
|
|
1018
|
-
|
|
1019
|
-
- [ ] **Step 7: Add keys to fr.js (French)**
|
|
1020
|
-
|
|
1021
|
-
In `menu.main`, after `launch_skip`:
|
|
1022
|
-
```js
|
|
1023
|
-
launch_auto_mode: "Lancer Claude Code (Activer le mode auto)",
|
|
1024
|
-
```
|
|
1025
|
-
|
|
1026
|
-
New `hints` section:
|
|
1027
|
-
```js
|
|
1028
|
-
hints: {
|
|
1029
|
-
auto_mode_info: 'Mode auto : Disponible pour le plan Team. Enterprise/API en cours de deploiement. Apres le lancement, appuyez sur Shift+Tab pour basculer.',
|
|
1030
|
-
active_api_info: 'Actif : {0} / {1}',
|
|
1031
|
-
no_active_api: 'Aucune API active configuree. Allez dans "Gestion des API" pour en ajouter une.'
|
|
1032
|
-
},
|
|
1033
|
-
```
|
|
1034
|
-
|
|
1035
|
-
- [ ] **Step 8: Add keys to es.js (Spanish)**
|
|
1036
|
-
|
|
1037
|
-
In `menu.main`, after `launch_skip`:
|
|
1038
|
-
```js
|
|
1039
|
-
launch_auto_mode: "Iniciar Claude Code (Activar modo automatico)",
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
New `hints` section:
|
|
1043
|
-
```js
|
|
1044
|
-
hints: {
|
|
1045
|
-
auto_mode_info: 'Modo automatico: Disponible para el plan Team. Enterprise/API en despliegue gradual. Despues de iniciar, presione Shift+Tab para cambiar.',
|
|
1046
|
-
active_api_info: 'Activo: {0} / {1}',
|
|
1047
|
-
no_active_api: 'No hay API activa configurada. Vaya a "Gestion de API" para agregar una.'
|
|
1048
|
-
},
|
|
1049
|
-
```
|
|
1050
|
-
|
|
1051
|
-
- [ ] **Step 9: Add keys to it.js (Italian)**
|
|
1052
|
-
|
|
1053
|
-
In `menu.main`, after `launch_skip`:
|
|
1054
|
-
```js
|
|
1055
|
-
launch_auto_mode: "Avvia Claude Code (Abilita modalita automatica)",
|
|
1056
|
-
```
|
|
1057
|
-
|
|
1058
|
-
New `hints` section:
|
|
1059
|
-
```js
|
|
1060
|
-
hints: {
|
|
1061
|
-
auto_mode_info: 'Modalita automatica: Attualmente disponibile per il piano Team. Enterprise/API in fase di rilascio graduale. Dopo l\'avvio, premi Shift+Tab per passare.',
|
|
1062
|
-
active_api_info: 'Attivo: {0} / {1}',
|
|
1063
|
-
no_active_api: 'Nessuna API attiva configurata. Vai a "Gestione API" per aggiungerne una.'
|
|
1064
|
-
},
|
|
1065
|
-
```
|
|
1066
|
-
|
|
1067
|
-
- [ ] **Step 10: Add keys to pt.js (Portuguese)**
|
|
1068
|
-
|
|
1069
|
-
In `menu.main`, after `launch_skip`:
|
|
1070
|
-
```js
|
|
1071
|
-
launch_auto_mode: "Iniciar Claude Code (Ativar modo automatico)",
|
|
1072
|
-
```
|
|
1073
|
-
|
|
1074
|
-
New `hints` section:
|
|
1075
|
-
```js
|
|
1076
|
-
hints: {
|
|
1077
|
-
auto_mode_info: 'Modo automatico: Disponivel para o plano Team. Enterprise/API em implantacao gradual. Apos iniciar, pressione Shift+Tab para alternar.',
|
|
1078
|
-
active_api_info: 'Ativo: {0} / {1}',
|
|
1079
|
-
no_active_api: 'Nenhuma API ativa configurada. Va para "Gerenciamento de API" para adicionar uma.'
|
|
1080
|
-
},
|
|
1081
|
-
```
|
|
1082
|
-
|
|
1083
|
-
- [ ] **Step 11: Add keys to ru.js (Russian)**
|
|
1084
|
-
|
|
1085
|
-
In `menu.main`, after `launch_skip`:
|
|
1086
|
-
```js
|
|
1087
|
-
launch_auto_mode: "Запустить Claude Code (Включить авторежим)",
|
|
1088
|
-
```
|
|
1089
|
-
|
|
1090
|
-
New `hints` section:
|
|
1091
|
-
```js
|
|
1092
|
-
hints: {
|
|
1093
|
-
auto_mode_info: 'Авторежим: Доступен для плана Team. Enterprise/API постепенно внедряются. После запуска нажмите Shift+Tab для переключения.',
|
|
1094
|
-
active_api_info: 'Активный: {0} / {1}',
|
|
1095
|
-
no_active_api: 'Нет настроенного активного API. Перейдите в "Управление API", чтобы добавить.'
|
|
1096
|
-
},
|
|
1097
|
-
```
|
|
1098
|
-
|
|
1099
|
-
- [ ] **Step 12: Verify — check all 4 keys exist and resolve in every locale**
|
|
1100
|
-
|
|
1101
|
-
Run:
|
|
1102
|
-
```bash
|
|
1103
|
-
node -e "
|
|
1104
|
-
const locales = ['en','zh','zh-TW','ja','ko','de','fr','es','it','pt','ru'];
|
|
1105
|
-
const keys = [
|
|
1106
|
-
['menu.main.launch_auto_mode', m => m.menu.main.launch_auto_mode],
|
|
1107
|
-
['hints.auto_mode_info', m => m.hints && m.hints.auto_mode_info],
|
|
1108
|
-
['hints.active_api_info', m => m.hints && m.hints.active_api_info],
|
|
1109
|
-
['hints.no_active_api', m => m.hints && m.hints.no_active_api]
|
|
1110
|
-
];
|
|
1111
|
-
let ok = true;
|
|
1112
|
-
locales.forEach(l => {
|
|
1113
|
-
const m = require('./lib/i18n/locales/' + l);
|
|
1114
|
-
keys.forEach(([name, getter]) => {
|
|
1115
|
-
const val = getter(m);
|
|
1116
|
-
if (!val || val === name) {
|
|
1117
|
-
console.log('FAIL ' + l + ': ' + name + ' = ' + JSON.stringify(val));
|
|
1118
|
-
ok = false;
|
|
1119
|
-
}
|
|
1120
|
-
});
|
|
1121
|
-
if (ok) console.log(l + ': all 4 keys OK');
|
|
1122
|
-
});
|
|
1123
|
-
if (!ok) { console.log('FAILED'); process.exit(1); }
|
|
1124
|
-
"
|
|
1125
|
-
```
|
|
1126
|
-
|
|
1127
|
-
Expected: all 11 lines show `all 4 keys OK`. If any key is missing or returns itself as a string, the check fails.
|
|
1128
|
-
|
|
1129
|
-
- [ ] **Step 13: Commit**
|
|
1130
|
-
|
|
1131
|
-
```bash
|
|
1132
|
-
git add lib/i18n/locales/
|
|
1133
|
-
git commit -m "feat: add i18n keys for auto mode menu and dynamic hints (11 locales)"
|
|
1134
|
-
```
|
|
1135
|
-
|
|
1136
|
-
---
|
|
1137
|
-
|
|
1138
|
-
### Task 9: Wire auto mode + hints + index shift in main file
|
|
1139
|
-
|
|
1140
|
-
**Files:**
|
|
1141
|
-
- Modify: `claude-launcher:46-50` (import), `claude-launcher:1092-1105` (menu options + navigate call), `claude-launcher:960-997` (executeSelection switch)
|
|
1142
|
-
|
|
1143
|
-
This is the largest task — it wires everything together.
|
|
1144
|
-
|
|
1145
|
-
- [ ] **Step 1: Add `launchClaudeAutoMode` to import**
|
|
1146
|
-
|
|
1147
|
-
In `claude-launcher`, update the launcher require block (lines 46-50) from:
|
|
1148
|
-
|
|
1149
|
-
```js
|
|
1150
|
-
const {
|
|
1151
|
-
launchClaudeDefault,
|
|
1152
|
-
launchClaudeSkipPermissions,
|
|
1153
|
-
launchClaudeWithApi
|
|
1154
|
-
} = require('./lib/launcher');
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
to:
|
|
1158
|
-
|
|
1159
|
-
```js
|
|
1160
|
-
const {
|
|
1161
|
-
launchClaudeDefault,
|
|
1162
|
-
launchClaudeSkipPermissions,
|
|
1163
|
-
launchClaudeAutoMode,
|
|
1164
|
-
launchClaudeWithApi
|
|
1165
|
-
} = require('./lib/launcher');
|
|
1166
|
-
```
|
|
1167
|
-
|
|
1168
|
-
- [ ] **Step 2: Add menu option at index 2 and build hintCallback**
|
|
1169
|
-
|
|
1170
|
-
In `claude-launcher`, replace the menu options block (lines 1092-1105):
|
|
1171
|
-
|
|
1172
|
-
```js
|
|
1173
|
-
// Populate menu options dynamically with i18n translations
|
|
1174
|
-
menuOptions = [
|
|
1175
|
-
await i18n.t('menu.main.launch_default'),
|
|
1176
|
-
await i18n.t('menu.main.launch_skip'),
|
|
1177
|
-
await i18n.t('menu.main.launch_api'),
|
|
1178
|
-
await i18n.t('menu.main.launch_api_skip'),
|
|
1179
|
-
await i18n.t('menu.main.api_management'),
|
|
1180
|
-
await i18n.t('menu.main.language_settings'),
|
|
1181
|
-
await i18n.t('menu.main.version_check'),
|
|
1182
|
-
await i18n.t('menu.main.exit')
|
|
1183
|
-
];
|
|
1184
|
-
|
|
1185
|
-
globalMainMenu.setOptions(menuOptions);
|
|
1186
|
-
const selection = await globalMainMenu.navigate(false, displayInfo || null); // Pass combined info to display between banner and nav
|
|
1187
|
-
```
|
|
1188
|
-
|
|
1189
|
-
with:
|
|
1190
|
-
|
|
1191
|
-
```js
|
|
1192
|
-
// Populate menu options dynamically with i18n translations
|
|
1193
|
-
menuOptions = [
|
|
1194
|
-
await i18n.t('menu.main.launch_default'),
|
|
1195
|
-
await i18n.t('menu.main.launch_skip'),
|
|
1196
|
-
await i18n.t('menu.main.launch_auto_mode'),
|
|
1197
|
-
await i18n.t('menu.main.launch_api'),
|
|
1198
|
-
await i18n.t('menu.main.launch_api_skip'),
|
|
1199
|
-
await i18n.t('menu.main.api_management'),
|
|
1200
|
-
await i18n.t('menu.main.language_settings'),
|
|
1201
|
-
await i18n.t('menu.main.version_check'),
|
|
1202
|
-
await i18n.t('menu.main.exit')
|
|
1203
|
-
];
|
|
1204
|
-
|
|
1205
|
-
// Pre-compute hint texts synchronously for menu callback
|
|
1206
|
-
const hintAutoMode = i18n.tSync('hints.auto_mode_info');
|
|
1207
|
-
const activeApi = apiManager.getActiveApi();
|
|
1208
|
-
let hintApiInfo = null;
|
|
1209
|
-
if (activeApi) {
|
|
1210
|
-
const { getProvider } = require('./lib/presets/providers');
|
|
1211
|
-
const providerConfig = getProvider(activeApi.provider);
|
|
1212
|
-
const providerName = providerConfig ? providerConfig.name : (activeApi.provider || 'Custom');
|
|
1213
|
-
hintApiInfo = i18n.tSync('hints.active_api_info', providerName, activeApi.model);
|
|
1214
|
-
} else {
|
|
1215
|
-
hintApiInfo = i18n.tSync('hints.no_active_api');
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// Synchronous hint callback — must not use await
|
|
1219
|
-
const hintCallback = (selectedIndex) => {
|
|
1220
|
-
switch (selectedIndex) {
|
|
1221
|
-
case 2: return hintAutoMode;
|
|
1222
|
-
case 3: return hintApiInfo;
|
|
1223
|
-
case 4: return hintApiInfo;
|
|
1224
|
-
default: return null;
|
|
1225
|
-
}
|
|
1226
|
-
};
|
|
1227
|
-
|
|
1228
|
-
globalMainMenu.setOptions(menuOptions);
|
|
1229
|
-
const selection = await globalMainMenu.navigate(false, displayInfo || null, hintCallback);
|
|
1230
|
-
```
|
|
1231
|
-
|
|
1232
|
-
- [ ] **Step 3: Update executeSelection switch — shift all indices**
|
|
1233
|
-
|
|
1234
|
-
In `claude-launcher`, replace the `executeSelection` function (lines 960-997):
|
|
1235
|
-
|
|
1236
|
-
```js
|
|
1237
|
-
async function executeSelection(selectedIndex) {
|
|
1238
|
-
switch (selectedIndex) {
|
|
1239
|
-
case 0: // Launch Claude Code
|
|
1240
|
-
launchClaudeDefault();
|
|
1241
|
-
break;
|
|
1242
|
-
|
|
1243
|
-
case 1: // Launch Claude Code (Skip Permissions)
|
|
1244
|
-
launchClaudeSkipPermissions();
|
|
1245
|
-
break;
|
|
1246
|
-
|
|
1247
|
-
case 2: // Launch Claude Code with 3rd-party API
|
|
1248
|
-
await handleThirdPartyApiLaunch(false);
|
|
1249
|
-
break;
|
|
1250
|
-
|
|
1251
|
-
case 3: // Launch Claude Code with 3rd-party API (Skip Permissions)
|
|
1252
|
-
await handleThirdPartyApiLaunch(true);
|
|
1253
|
-
break;
|
|
1254
|
-
|
|
1255
|
-
case 4: // 3rd-party API Management
|
|
1256
|
-
return await showApiManagementMenu();
|
|
1257
|
-
|
|
1258
|
-
case 5: // Language Settings
|
|
1259
|
-
return await showLanguageSettings();
|
|
1260
|
-
|
|
1261
|
-
case 6: // Version Update Check
|
|
1262
|
-
return await showVersionUpdateCheck();
|
|
1263
|
-
|
|
1264
|
-
case 7: // Exit
|
|
1265
|
-
console.log('');
|
|
1266
|
-
console.log(colors.green + '👋 ' + await i18n.t('menu.main.exit') + '!' + colors.reset);
|
|
1267
|
-
process.exit(0);
|
|
1268
|
-
break;
|
|
1269
|
-
|
|
1270
|
-
default:
|
|
1271
|
-
showMenu();
|
|
1272
|
-
break;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
```
|
|
1276
|
-
|
|
1277
|
-
with:
|
|
1278
|
-
|
|
1279
|
-
```js
|
|
1280
|
-
async function executeSelection(selectedIndex) {
|
|
1281
|
-
switch (selectedIndex) {
|
|
1282
|
-
case 0: // Launch Claude Code
|
|
1283
|
-
launchClaudeDefault();
|
|
1284
|
-
break;
|
|
1285
|
-
|
|
1286
|
-
case 1: // Launch Claude Code (Skip Permissions)
|
|
1287
|
-
launchClaudeSkipPermissions();
|
|
1288
|
-
break;
|
|
1289
|
-
|
|
1290
|
-
case 2: // Launch Claude Code (Enable Auto Mode)
|
|
1291
|
-
launchClaudeAutoMode();
|
|
1292
|
-
break;
|
|
1293
|
-
|
|
1294
|
-
case 3: // Launch Claude Code with 3rd-party API
|
|
1295
|
-
await handleThirdPartyApiLaunch(false);
|
|
1296
|
-
break;
|
|
1297
|
-
|
|
1298
|
-
case 4: // Launch Claude Code with 3rd-party API (Skip Permissions)
|
|
1299
|
-
await handleThirdPartyApiLaunch(true);
|
|
1300
|
-
break;
|
|
1301
|
-
|
|
1302
|
-
case 5: // 3rd-party API Management
|
|
1303
|
-
return await showApiManagementMenu();
|
|
1304
|
-
|
|
1305
|
-
case 6: // Language Settings
|
|
1306
|
-
return await showLanguageSettings();
|
|
1307
|
-
|
|
1308
|
-
case 7: // Version Update Check
|
|
1309
|
-
return await showVersionUpdateCheck();
|
|
1310
|
-
|
|
1311
|
-
case 8: // Exit
|
|
1312
|
-
console.log('');
|
|
1313
|
-
console.log(colors.green + '👋 ' + await i18n.t('menu.main.exit') + '!' + colors.reset);
|
|
1314
|
-
process.exit(0);
|
|
1315
|
-
break;
|
|
1316
|
-
|
|
1317
|
-
default:
|
|
1318
|
-
showMenu();
|
|
1319
|
-
break;
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
```
|
|
1323
|
-
|
|
1324
|
-
- [ ] **Step 4: Verify — syntax check the main file**
|
|
1325
|
-
|
|
1326
|
-
Run: `node -e "try { require('./claude-launcher'); } catch(e) { if (e.code === 'MODULE_NOT_FOUND') console.log('ERROR:', e.message); else console.log('OK: file parses'); }"`
|
|
1327
|
-
|
|
1328
|
-
Note: The main file will try to run the launcher and may fail on stdin/TTY in a non-interactive context, but any `SyntaxError` would be caught. A better check:
|
|
1329
|
-
|
|
1330
|
-
Run: `node --check claude-launcher`
|
|
1331
|
-
|
|
1332
|
-
Expected: No output (no syntax errors).
|
|
1333
|
-
|
|
1334
|
-
- [ ] **Step 5: Run all tests**
|
|
1335
|
-
|
|
1336
|
-
Run: `node test/providers.test.js && node test/menu-hints.test.js`
|
|
1337
|
-
|
|
1338
|
-
Expected: All tests pass.
|
|
1339
|
-
|
|
1340
|
-
- [ ] **Step 6: Commit**
|
|
1341
|
-
|
|
1342
|
-
```bash
|
|
1343
|
-
git add claude-launcher
|
|
1344
|
-
git commit -m "feat: add auto mode menu item, dynamic hints, and shift menu indices"
|
|
1345
|
-
```
|
|
1346
|
-
|
|
1347
|
-
---
|
|
1348
|
-
|
|
1349
|
-
### Task 10: Wire npm test to run both test files
|
|
1350
|
-
|
|
1351
|
-
**Files:**
|
|
1352
|
-
- Modify: `package.json:11` (scripts.test)
|
|
1353
|
-
|
|
1354
|
-
- [ ] **Step 1: Update package.json test script**
|
|
1355
|
-
|
|
1356
|
-
In `package.json`, change line 11 from:
|
|
1357
|
-
|
|
1358
|
-
```json
|
|
1359
|
-
"test": "echo \"No tests specified\" && exit 0",
|
|
1360
|
-
```
|
|
1361
|
-
|
|
1362
|
-
to:
|
|
1363
|
-
|
|
1364
|
-
```json
|
|
1365
|
-
"test": "node test/providers.test.js && node test/menu-hints.test.js",
|
|
1366
|
-
```
|
|
1367
|
-
|
|
1368
|
-
- [ ] **Step 2: Verify — run npm test**
|
|
1369
|
-
|
|
1370
|
-
Run: `npm test`
|
|
1371
|
-
|
|
1372
|
-
Expected: Both test files execute and all tests pass.
|
|
1373
|
-
|
|
1374
|
-
- [ ] **Step 3: Commit**
|
|
1375
|
-
|
|
1376
|
-
```bash
|
|
1377
|
-
git add package.json
|
|
1378
|
-
git commit -m "chore: wire npm test to run provider and menu hint tests"
|
|
1379
|
-
```
|
|
1380
|
-
|
|
1381
|
-
---
|
|
1382
|
-
|
|
1383
|
-
### Task 11: Manual smoke test
|
|
1384
|
-
|
|
1385
|
-
- [ ] **Step 1: Launch the app**
|
|
1386
|
-
|
|
1387
|
-
Run: `node claude-launcher`
|
|
1388
|
-
|
|
1389
|
-
- [ ] **Step 2: Verify menu shows 9 items** (0-8)
|
|
1390
|
-
|
|
1391
|
-
Check that "Launch Claude Code (Enable Auto Mode)" appears at position 2 (or the translated equivalent if not English).
|
|
1392
|
-
|
|
1393
|
-
- [ ] **Step 3: Arrow down to "Enable Auto Mode" — verify hint appears below menu**
|
|
1394
|
-
|
|
1395
|
-
Expected: A line like `ℹ Auto Mode: Currently supports Team plan...` appears below the menu options.
|
|
1396
|
-
|
|
1397
|
-
- [ ] **Step 4: Arrow to 3rd-party API option — verify hint changes**
|
|
1398
|
-
|
|
1399
|
-
Expected: Either shows `ℹ Active: {provider} / {model}` or `ℹ No active API configured...` depending on whether an API is configured.
|
|
1400
|
-
|
|
1401
|
-
- [ ] **Step 5: Arrow to other items (default launch, exit) — verify no hint**
|
|
1402
|
-
|
|
1403
|
-
Expected: Hint line disappears.
|
|
1404
|
-
|
|
1405
|
-
- [ ] **Step 6: Press Escape to exit**
|
|
1406
|
-
|
|
1407
|
-
- [ ] **Step 7: Final commit (if any fixes needed)**
|
|
1408
|
-
|
|
1409
|
-
```bash
|
|
1410
|
-
git add -A
|
|
1411
|
-
git commit -m "fix: smoke test adjustments"
|
|
1412
|
-
```
|
|
1413
|
-
|
|
1414
|
-
(Skip this commit if no fixes were needed.)
|