@x-all-in-one/coding-helper 0.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/README.md +93 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +15 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +148 -0
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +133 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +128 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +4 -0
- package/dist/commands/lang.d.ts +1 -0
- package/dist/commands/lang.js +29 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/lib/api-validator.d.ts +16 -0
- package/dist/lib/api-validator.js +75 -0
- package/dist/lib/claude-code-manager.d.ts +79 -0
- package/dist/lib/claude-code-manager.js +201 -0
- package/dist/lib/command.d.ts +10 -0
- package/dist/lib/command.js +174 -0
- package/dist/lib/config.d.ts +37 -0
- package/dist/lib/config.js +92 -0
- package/dist/lib/i18n.d.ts +20 -0
- package/dist/lib/i18n.js +148 -0
- package/dist/lib/tool-manager.d.ts +24 -0
- package/dist/lib/tool-manager.js +256 -0
- package/dist/lib/wizard.d.ts +44 -0
- package/dist/lib/wizard.js +673 -0
- package/dist/locales/en_US.json +201 -0
- package/dist/locales/zh_CN.json +201 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.js +41 -0
- package/dist/utils/string-width.d.ts +38 -0
- package/dist/utils/string-width.js +130 -0
- package/package.json +66 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { configManager } from './config.js';
|
|
5
|
+
import { toolManager, SUPPORTED_TOOLS } from './tool-manager.js';
|
|
6
|
+
import { claudeCodeManager, DEFAULT_CONFIG } from './claude-code-manager.js';
|
|
7
|
+
import { i18n } from './i18n.js';
|
|
8
|
+
import { createBorderLine, createContentLine } from '../utils/string-width.js';
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { validateApiKey } from './api-validator.js';
|
|
11
|
+
export class Wizard {
|
|
12
|
+
static instance;
|
|
13
|
+
BOX_WIDTH = 63; // Default box width for UI elements
|
|
14
|
+
cachedModels = [];
|
|
15
|
+
constructor() { }
|
|
16
|
+
static getInstance() {
|
|
17
|
+
if (!Wizard.instance) {
|
|
18
|
+
Wizard.instance = new Wizard();
|
|
19
|
+
}
|
|
20
|
+
return Wizard.instance;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a simple box with title using double-line border style
|
|
24
|
+
*/
|
|
25
|
+
createBox(title) {
|
|
26
|
+
console.log(chalk.cyan.bold('\n' + createBorderLine('╔', '╗', '═', this.BOX_WIDTH)));
|
|
27
|
+
console.log(chalk.cyan.bold(createContentLine(title, '║', '║', this.BOX_WIDTH, 'center')));
|
|
28
|
+
console.log(chalk.cyan.bold(createBorderLine('╚', '╝', '═', this.BOX_WIDTH)));
|
|
29
|
+
console.log('');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Display operation hints
|
|
33
|
+
*/
|
|
34
|
+
showOperationHints() {
|
|
35
|
+
const hints = [
|
|
36
|
+
chalk.gray(i18n.t('wizard.hint_navigate')),
|
|
37
|
+
chalk.gray(i18n.t('wizard.hint_confirm'))
|
|
38
|
+
];
|
|
39
|
+
console.log(chalk.gray('💡 ') + hints.join(chalk.gray(' | ')) + '\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Prompt wrapper that shows operation hints
|
|
43
|
+
*/
|
|
44
|
+
async promptWithHints(questions) {
|
|
45
|
+
this.showOperationHints();
|
|
46
|
+
return inquirer.prompt(questions);
|
|
47
|
+
}
|
|
48
|
+
printBanner() {
|
|
49
|
+
const BANNER_WIDTH = 65;
|
|
50
|
+
const subtitle = i18n.t('wizard.banner_subtitle');
|
|
51
|
+
const subtitleLine = createContentLine(subtitle, '║', '║', BANNER_WIDTH, 'center');
|
|
52
|
+
const emptyLine = createContentLine('', '║', '║', BANNER_WIDTH, 'center');
|
|
53
|
+
const titleLine = createContentLine('Coding Helper v0.0.1', '║', '║', BANNER_WIDTH, 'center');
|
|
54
|
+
const asciiLines = [
|
|
55
|
+
' █ █ ▄▀▄ █ ▄▀▄ ',
|
|
56
|
+
' █ ▄▄ █▀█ █ █ █ ',
|
|
57
|
+
' █ █ █ █ █ ▀▄▀ '
|
|
58
|
+
].map(line => createContentLine(line, '║', '║', BANNER_WIDTH, 'center'));
|
|
59
|
+
const bannerLines = [
|
|
60
|
+
createBorderLine('╔', '╗', '═', BANNER_WIDTH),
|
|
61
|
+
emptyLine,
|
|
62
|
+
...asciiLines,
|
|
63
|
+
emptyLine,
|
|
64
|
+
titleLine,
|
|
65
|
+
subtitleLine,
|
|
66
|
+
createBorderLine('╚', '╝', '═', BANNER_WIDTH)
|
|
67
|
+
];
|
|
68
|
+
console.log(chalk.cyan.bold('\n' + bannerLines.join('\n')));
|
|
69
|
+
}
|
|
70
|
+
resetScreen() {
|
|
71
|
+
console.clear();
|
|
72
|
+
this.printBanner();
|
|
73
|
+
}
|
|
74
|
+
async runFirstTimeSetup() {
|
|
75
|
+
// 清屏并显示欢迎信息
|
|
76
|
+
this.resetScreen();
|
|
77
|
+
console.log(chalk.cyan.bold('\n' + i18n.t('wizard.welcome')));
|
|
78
|
+
console.log(chalk.gray(i18n.t('wizard.privacy_note') + '\n'));
|
|
79
|
+
// Step 1: Select language
|
|
80
|
+
await this.configLanguage();
|
|
81
|
+
// Step 2: Input API key
|
|
82
|
+
await this.configApiKey();
|
|
83
|
+
// Step 3: Configure models
|
|
84
|
+
await this.configModels();
|
|
85
|
+
// Step 4: Select and configure tool
|
|
86
|
+
await this.selectAndConfigureTool();
|
|
87
|
+
}
|
|
88
|
+
async configLanguage() {
|
|
89
|
+
while (true) {
|
|
90
|
+
this.resetScreen();
|
|
91
|
+
this.createBox(i18n.t('wizard.select_language'));
|
|
92
|
+
const currentLanguage = i18n.getLocale();
|
|
93
|
+
const { language } = await this.promptWithHints([
|
|
94
|
+
{
|
|
95
|
+
type: 'list',
|
|
96
|
+
name: 'language',
|
|
97
|
+
message: '✨ ' + i18n.t('wizard.select_language'),
|
|
98
|
+
choices: [
|
|
99
|
+
{ name: '[EN] English' + (currentLanguage === 'en_US' ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''), value: 'en_US' },
|
|
100
|
+
{ name: '[CN] 中文' + (currentLanguage === 'zh_CN' ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''), value: 'zh_CN' },
|
|
101
|
+
new inquirer.Separator(),
|
|
102
|
+
{ name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' },
|
|
103
|
+
{ name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' }
|
|
104
|
+
],
|
|
105
|
+
default: 'zh_CN'
|
|
106
|
+
}
|
|
107
|
+
]);
|
|
108
|
+
if (language === 'exit') {
|
|
109
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
else if (language === 'back') {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
configManager.setLang(language);
|
|
116
|
+
i18n.setLocale(language);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async configApiKey() {
|
|
121
|
+
while (true) {
|
|
122
|
+
this.resetScreen();
|
|
123
|
+
this.createBox(i18n.t('wizard.config_api_key'));
|
|
124
|
+
const currentConfig = configManager.getConfig();
|
|
125
|
+
if (currentConfig.api_key) {
|
|
126
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ' ') + chalk.gray(i18n.t('wizard.api_key_set') + ' (' + currentConfig.api_key.slice(0, 4) + '****)'));
|
|
127
|
+
console.log('');
|
|
128
|
+
}
|
|
129
|
+
const { action } = await this.promptWithHints([
|
|
130
|
+
{
|
|
131
|
+
type: 'list',
|
|
132
|
+
name: 'action',
|
|
133
|
+
message: i18n.t('wizard.select_action'),
|
|
134
|
+
choices: [
|
|
135
|
+
{ name: '> ' + (currentConfig.api_key ? i18n.t("wizard.update_api_key") : i18n.t('wizard.input_api_key')), value: 'input' },
|
|
136
|
+
new inquirer.Separator(),
|
|
137
|
+
{ name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' },
|
|
138
|
+
{ name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' }
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
]);
|
|
142
|
+
if (action === 'exit') {
|
|
143
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
else if (action === 'back') {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
else if (action === 'input') {
|
|
150
|
+
this.resetScreen();
|
|
151
|
+
this.createBox(i18n.t('wizard.config_api_key'));
|
|
152
|
+
const { apiKey } = await inquirer.prompt([
|
|
153
|
+
{
|
|
154
|
+
type: 'password',
|
|
155
|
+
name: 'apiKey',
|
|
156
|
+
mask: '●',
|
|
157
|
+
message: i18n.t('wizard.input_your_api_key'),
|
|
158
|
+
validate: (input) => {
|
|
159
|
+
if (!input || input.trim().length === 0) {
|
|
160
|
+
return '[!] ' + i18n.t('wizard.api_key_required');
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
]);
|
|
166
|
+
// Validate API Key
|
|
167
|
+
const spinner = ora({
|
|
168
|
+
text: i18n.t('wizard.validating_api_key'),
|
|
169
|
+
spinner: 'star2'
|
|
170
|
+
}).start();
|
|
171
|
+
const validationResult = await validateApiKey(apiKey.trim());
|
|
172
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
173
|
+
if (!validationResult.valid) {
|
|
174
|
+
if (validationResult.error === 'invalid_api_key') {
|
|
175
|
+
spinner.fail(chalk.red(i18n.t('wizard.api_key_invalid')));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
spinner.fail(chalk.red(i18n.t('wizard.api_key_network_error')));
|
|
179
|
+
}
|
|
180
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
181
|
+
continue; // Return to action menu
|
|
182
|
+
}
|
|
183
|
+
configManager.setApiKey(apiKey.trim());
|
|
184
|
+
// Clear cached models when API key changes
|
|
185
|
+
this.cachedModels = [];
|
|
186
|
+
spinner.succeed("✅ " + i18n.t('wizard.set_success'));
|
|
187
|
+
await new Promise(resolve => setTimeout(resolve, 600));
|
|
188
|
+
// After setting API key, go to model configuration
|
|
189
|
+
await this.configModels();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Fetch and cache available models
|
|
195
|
+
*/
|
|
196
|
+
async fetchModels() {
|
|
197
|
+
const apiKey = configManager.getApiKey();
|
|
198
|
+
if (!apiKey) {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
if (this.cachedModels.length > 0) {
|
|
202
|
+
return this.cachedModels;
|
|
203
|
+
}
|
|
204
|
+
const models = await claudeCodeManager.fetchAvailableModels(apiKey);
|
|
205
|
+
this.cachedModels = models;
|
|
206
|
+
return models;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Configure models menu
|
|
210
|
+
*/
|
|
211
|
+
async configModels() {
|
|
212
|
+
while (true) {
|
|
213
|
+
this.resetScreen();
|
|
214
|
+
this.createBox(i18n.t('wizard.select_models'));
|
|
215
|
+
const currentConfig = configManager.getConfig();
|
|
216
|
+
const models = currentConfig;
|
|
217
|
+
// Display current model configuration
|
|
218
|
+
console.log(chalk.cyan.bold(i18n.t('wizard.models_config_title') + ':'));
|
|
219
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_haiku_model') + ': ') + (models.haikuModel ? chalk.green(models.haikuModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
220
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_sonnet_model') + ': ') + (models.sonnetModel ? chalk.green(models.sonnetModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
221
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_opus_model') + ': ') + (models.opusModel ? chalk.green(models.opusModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
222
|
+
console.log('');
|
|
223
|
+
const { action } = await this.promptWithHints([
|
|
224
|
+
{
|
|
225
|
+
type: 'list',
|
|
226
|
+
name: 'action',
|
|
227
|
+
message: i18n.t('wizard.select_action'),
|
|
228
|
+
choices: [
|
|
229
|
+
{ name: '> ' + i18n.t('wizard.configure_models'), value: 'configure' },
|
|
230
|
+
new inquirer.Separator(),
|
|
231
|
+
{ name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' },
|
|
232
|
+
{ name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' }
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
]);
|
|
236
|
+
if (action === 'exit') {
|
|
237
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
238
|
+
process.exit(0);
|
|
239
|
+
}
|
|
240
|
+
else if (action === 'back') {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
else if (action === 'configure') {
|
|
244
|
+
await this.selectModels();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Step-by-step model selection
|
|
250
|
+
*/
|
|
251
|
+
async selectModels() {
|
|
252
|
+
const apiKey = configManager.getApiKey();
|
|
253
|
+
if (!apiKey) {
|
|
254
|
+
console.log(chalk.red('\n' + i18n.t('wizard.no_models_available')));
|
|
255
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
// Fetch available models
|
|
259
|
+
const spinner = ora({
|
|
260
|
+
text: i18n.t('wizard.fetching_models'),
|
|
261
|
+
spinner: 'star2'
|
|
262
|
+
}).start();
|
|
263
|
+
const availableModels = await this.fetchModels();
|
|
264
|
+
if (availableModels.length === 0) {
|
|
265
|
+
spinner.fail(chalk.red(i18n.t('wizard.fetch_models_failed')));
|
|
266
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
spinner.succeed(chalk.green(i18n.t('wizard.api_key_valid')));
|
|
270
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
271
|
+
const currentConfig = configManager.getConfig();
|
|
272
|
+
// Step 1: Select Haiku model
|
|
273
|
+
this.resetScreen();
|
|
274
|
+
this.createBox(i18n.t('wizard.select_haiku_model'));
|
|
275
|
+
const haikuChoices = availableModels.map(model => ({
|
|
276
|
+
name: model + (currentConfig.haikuModel === model ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''),
|
|
277
|
+
value: model
|
|
278
|
+
}));
|
|
279
|
+
haikuChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
|
|
280
|
+
const { haikuModel } = await this.promptWithHints([
|
|
281
|
+
{
|
|
282
|
+
type: 'list',
|
|
283
|
+
name: 'haikuModel',
|
|
284
|
+
message: i18n.t('wizard.select_haiku_model'),
|
|
285
|
+
choices: haikuChoices,
|
|
286
|
+
default: currentConfig.haikuModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
|
287
|
+
}
|
|
288
|
+
]);
|
|
289
|
+
if (haikuModel === 'exit') {
|
|
290
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
291
|
+
process.exit(0);
|
|
292
|
+
}
|
|
293
|
+
else if (haikuModel === 'back') {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// Step 2: Select Sonnet model
|
|
297
|
+
this.resetScreen();
|
|
298
|
+
this.createBox(i18n.t('wizard.select_sonnet_model'));
|
|
299
|
+
const sonnetChoices = availableModels.map(model => ({
|
|
300
|
+
name: model + (currentConfig.sonnetModel === model ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''),
|
|
301
|
+
value: model
|
|
302
|
+
}));
|
|
303
|
+
sonnetChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
|
|
304
|
+
const { sonnetModel } = await this.promptWithHints([
|
|
305
|
+
{
|
|
306
|
+
type: 'list',
|
|
307
|
+
name: 'sonnetModel',
|
|
308
|
+
message: i18n.t('wizard.select_sonnet_model'),
|
|
309
|
+
choices: sonnetChoices,
|
|
310
|
+
default: currentConfig.sonnetModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_SONNET_MODEL
|
|
311
|
+
}
|
|
312
|
+
]);
|
|
313
|
+
if (sonnetModel === 'exit') {
|
|
314
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
315
|
+
process.exit(0);
|
|
316
|
+
}
|
|
317
|
+
else if (sonnetModel === 'back') {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// Step 3: Select Opus model
|
|
321
|
+
this.resetScreen();
|
|
322
|
+
this.createBox(i18n.t('wizard.select_opus_model'));
|
|
323
|
+
const opusChoices = availableModels.map(model => ({
|
|
324
|
+
name: model + (currentConfig.opusModel === model ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''),
|
|
325
|
+
value: model
|
|
326
|
+
}));
|
|
327
|
+
opusChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
|
|
328
|
+
const { opusModel } = await this.promptWithHints([
|
|
329
|
+
{
|
|
330
|
+
type: 'list',
|
|
331
|
+
name: 'opusModel',
|
|
332
|
+
message: i18n.t('wizard.select_opus_model'),
|
|
333
|
+
choices: opusChoices,
|
|
334
|
+
default: currentConfig.opusModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_OPUS_MODEL
|
|
335
|
+
}
|
|
336
|
+
]);
|
|
337
|
+
if (opusModel === 'exit') {
|
|
338
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
339
|
+
process.exit(0);
|
|
340
|
+
}
|
|
341
|
+
else if (opusModel === 'back') {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
// Save the configuration
|
|
345
|
+
const saveSpinner = ora({
|
|
346
|
+
text: i18n.t('wizard.saving_model_config'),
|
|
347
|
+
spinner: 'star2'
|
|
348
|
+
}).start();
|
|
349
|
+
try {
|
|
350
|
+
configManager.setModels({
|
|
351
|
+
haikuModel,
|
|
352
|
+
sonnetModel,
|
|
353
|
+
opusModel
|
|
354
|
+
});
|
|
355
|
+
await new Promise(resolve => setTimeout(resolve, 600));
|
|
356
|
+
saveSpinner.succeed(chalk.green(i18n.t('wizard.model_config_saved')));
|
|
357
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
saveSpinner.fail(i18n.t('wizard.model_config_failed'));
|
|
361
|
+
console.error(error);
|
|
362
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async selectAndConfigureTool() {
|
|
366
|
+
while (true) {
|
|
367
|
+
this.resetScreen();
|
|
368
|
+
this.createBox(i18n.t('wizard.select_tool'));
|
|
369
|
+
const supportedTools = toolManager.getSupportedTools();
|
|
370
|
+
const toolChoices = supportedTools.map(tool => ({
|
|
371
|
+
name: `> ${tool.displayName}`,
|
|
372
|
+
value: tool.name
|
|
373
|
+
}));
|
|
374
|
+
toolChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
|
|
375
|
+
const { selectedTool } = await this.promptWithHints([
|
|
376
|
+
{
|
|
377
|
+
type: 'list',
|
|
378
|
+
name: 'selectedTool',
|
|
379
|
+
message: i18n.t('wizard.select_tool'),
|
|
380
|
+
choices: toolChoices
|
|
381
|
+
}
|
|
382
|
+
]);
|
|
383
|
+
if (selectedTool === 'exit') {
|
|
384
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
385
|
+
process.exit(0);
|
|
386
|
+
}
|
|
387
|
+
else if (selectedTool === 'back') {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
await this.configureTool(selectedTool);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async configureTool(toolName) {
|
|
394
|
+
// 检查工具是否安装
|
|
395
|
+
if (!toolManager.isToolInstalled(toolName)) {
|
|
396
|
+
console.log(chalk.yellow(`\n${i18n.t('wizard.tool_not_installed', { tool: SUPPORTED_TOOLS[toolName].displayName })}`));
|
|
397
|
+
const { shouldInstall } = await this.promptWithHints([
|
|
398
|
+
{
|
|
399
|
+
type: 'confirm',
|
|
400
|
+
name: 'shouldInstall',
|
|
401
|
+
message: i18n.t('wizard.install_tool_confirm'),
|
|
402
|
+
default: true
|
|
403
|
+
}
|
|
404
|
+
]);
|
|
405
|
+
if (shouldInstall) {
|
|
406
|
+
try {
|
|
407
|
+
await toolManager.installTool(toolName);
|
|
408
|
+
await new Promise(resolve => setTimeout(resolve, 600));
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
console.error(chalk.red(i18n.t('install.install_failed_detail')));
|
|
412
|
+
if (error.message) {
|
|
413
|
+
console.error(chalk.gray(error.message));
|
|
414
|
+
}
|
|
415
|
+
await new Promise(resolve => setTimeout(resolve, 600));
|
|
416
|
+
// 询问是否跳过安装
|
|
417
|
+
const { skipInstall } = await this.promptWithHints([
|
|
418
|
+
{
|
|
419
|
+
type: 'list',
|
|
420
|
+
name: 'skipInstall',
|
|
421
|
+
message: i18n.t('install.skip_install_confirm'),
|
|
422
|
+
choices: [
|
|
423
|
+
{ name: i18n.t('install.skip_install_yes'), value: true },
|
|
424
|
+
{ name: i18n.t('install.skip_install_no'), value: false }
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
]);
|
|
428
|
+
if (!skipInstall) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
console.log(chalk.yellow(i18n.t('wizard.install_skipped')));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// 进入工具管理菜单
|
|
439
|
+
await this.showToolMenu(toolName);
|
|
440
|
+
}
|
|
441
|
+
async showMainMenu() {
|
|
442
|
+
const cfg = configManager.getConfig();
|
|
443
|
+
i18n.loadFromConfig(cfg.lang);
|
|
444
|
+
while (true) {
|
|
445
|
+
this.resetScreen();
|
|
446
|
+
const currentCfg = configManager.getConfig();
|
|
447
|
+
this.createBox(i18n.t('wizard.main_menu_title'));
|
|
448
|
+
// 显示当前配置状态
|
|
449
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ': ') + (currentCfg.api_key ? chalk.gray(i18n.t('wizard.api_key_set') + ' (' + currentCfg.api_key.slice(0, 4) + '****)') : chalk.red(i18n.t('wizard.not_set'))));
|
|
450
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_haiku_model') + ': ') + (currentCfg.haikuModel ? chalk.green(currentCfg.haikuModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
451
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_sonnet_model') + ': ') + (currentCfg.sonnetModel ? chalk.green(currentCfg.sonnetModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
452
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_opus_model') + ': ') + (currentCfg.opusModel ? chalk.green(currentCfg.opusModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
453
|
+
console.log('');
|
|
454
|
+
const choices = [
|
|
455
|
+
{ name: '> ' + i18n.t('wizard.menu_config_language'), value: 'lang' },
|
|
456
|
+
{ name: '> ' + i18n.t('wizard.menu_config_api_key'), value: 'apikey' },
|
|
457
|
+
{ name: '> ' + i18n.t('wizard.menu_config_models'), value: 'models' },
|
|
458
|
+
{ name: '> ' + i18n.t('wizard.menu_config_tool'), value: 'tool' },
|
|
459
|
+
new inquirer.Separator(),
|
|
460
|
+
{ name: 'x ' + i18n.t('wizard.menu_exit'), value: 'exit' }
|
|
461
|
+
];
|
|
462
|
+
const { action } = await this.promptWithHints([
|
|
463
|
+
{
|
|
464
|
+
type: 'list',
|
|
465
|
+
name: 'action',
|
|
466
|
+
message: i18n.t('wizard.select_operation'),
|
|
467
|
+
choices
|
|
468
|
+
}
|
|
469
|
+
]);
|
|
470
|
+
if (action === 'exit') {
|
|
471
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
472
|
+
process.exit(0);
|
|
473
|
+
}
|
|
474
|
+
else if (action === 'lang') {
|
|
475
|
+
await this.configLanguage();
|
|
476
|
+
}
|
|
477
|
+
else if (action === 'apikey') {
|
|
478
|
+
await this.configApiKey();
|
|
479
|
+
}
|
|
480
|
+
else if (action === 'models') {
|
|
481
|
+
await this.configModels();
|
|
482
|
+
}
|
|
483
|
+
else if (action === 'tool') {
|
|
484
|
+
await this.selectAndConfigureTool();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async showToolMenu(toolName) {
|
|
489
|
+
while (true) {
|
|
490
|
+
// for claude code now
|
|
491
|
+
this.resetScreen();
|
|
492
|
+
const title = `${SUPPORTED_TOOLS[toolName].displayName} ${i18n.t('wizard.menu_title')}`;
|
|
493
|
+
this.createBox(title);
|
|
494
|
+
if (toolName === 'claude-code') {
|
|
495
|
+
console.log(chalk.yellow.bold(i18n.t('wizard.global_config_warning', { tool: 'Claude Code' })));
|
|
496
|
+
console.log('');
|
|
497
|
+
}
|
|
498
|
+
let actionText = '';
|
|
499
|
+
const chelperConfig = configManager.getConfig();
|
|
500
|
+
const detectedConfig = claudeCodeManager.getModelConfig();
|
|
501
|
+
// 显示 chelper 配置
|
|
502
|
+
console.log(chalk.cyan.bold(i18n.t('wizard.chelper_config_title') + ':'));
|
|
503
|
+
if (chelperConfig.api_key) {
|
|
504
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ': ') + chalk.gray(i18n.t('wizard.api_key_set') + ' (' + chelperConfig.api_key.slice(0, 4) + '****)'));
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ': ') + chalk.red(i18n.t('wizard.not_set')));
|
|
508
|
+
}
|
|
509
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_haiku_model') + ': ') + (chelperConfig.haikuModel ? chalk.green(chelperConfig.haikuModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
510
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_sonnet_model') + ': ') + (chelperConfig.sonnetModel ? chalk.green(chelperConfig.sonnetModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
511
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_opus_model') + ': ') + (chelperConfig.opusModel ? chalk.green(chelperConfig.opusModel) : chalk.red(i18n.t('wizard.not_set'))));
|
|
512
|
+
console.log('');
|
|
513
|
+
// 显示 Claude Code 当前配置
|
|
514
|
+
console.log(chalk.yellow.bold(i18n.t('wizard.claude_code_config_title') + ':'));
|
|
515
|
+
if (detectedConfig) {
|
|
516
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ': ') + chalk.gray(i18n.t('wizard.api_key_set') + ' (' + detectedConfig.apiKey.slice(0, 4) + '****)'));
|
|
517
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_haiku_model') + ': ') + chalk.green(detectedConfig.haikuModel));
|
|
518
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_sonnet_model') + ': ') + chalk.green(detectedConfig.sonnetModel));
|
|
519
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.current_opus_model') + ': ') + chalk.green(detectedConfig.opusModel));
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ': ') + chalk.red(i18n.t('wizard.not_set')));
|
|
523
|
+
}
|
|
524
|
+
console.log('');
|
|
525
|
+
// 判断是否需要刷新配置
|
|
526
|
+
const configMatches = detectedConfig &&
|
|
527
|
+
detectedConfig.apiKey === chelperConfig.api_key &&
|
|
528
|
+
detectedConfig.haikuModel === chelperConfig.haikuModel &&
|
|
529
|
+
detectedConfig.sonnetModel === chelperConfig.sonnetModel &&
|
|
530
|
+
detectedConfig.opusModel === chelperConfig.opusModel;
|
|
531
|
+
if (detectedConfig && configMatches) {
|
|
532
|
+
// 配置已同步
|
|
533
|
+
console.log(chalk.green('✅ ' + i18n.t('wizard.config_synced')));
|
|
534
|
+
actionText = i18n.t('wizard.action_refresh_config', { 'tool': SUPPORTED_TOOLS[toolName].displayName });
|
|
535
|
+
}
|
|
536
|
+
else if (detectedConfig) {
|
|
537
|
+
// 配置不一致,需要刷新
|
|
538
|
+
console.log(chalk.yellow('⚠️ ' + i18n.t('wizard.config_out_of_sync')));
|
|
539
|
+
actionText = i18n.t('wizard.action_refresh_config', { 'tool': SUPPORTED_TOOLS[toolName].displayName });
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
// 未配置,需要装载
|
|
543
|
+
console.log(chalk.blue('ℹ️ ' + i18n.t('wizard.config_not_loaded')));
|
|
544
|
+
actionText = i18n.t('wizard.action_load_config', { 'tool': SUPPORTED_TOOLS[toolName].displayName });
|
|
545
|
+
}
|
|
546
|
+
console.log('');
|
|
547
|
+
const choices = [];
|
|
548
|
+
choices.push({ name: '> ' + actionText, value: 'load_config' });
|
|
549
|
+
// 如果已经配置了,显示卸载选项
|
|
550
|
+
if (detectedConfig) {
|
|
551
|
+
choices.push({ name: '> ' + i18n.t('wizard.action_unload_config', { 'tool': SUPPORTED_TOOLS[toolName].displayName }), value: 'unload_config' });
|
|
552
|
+
}
|
|
553
|
+
// 如果已经配置了,显示启动选项
|
|
554
|
+
if (detectedConfig) {
|
|
555
|
+
choices.push({ name: '> ' + i18n.t('wizard.start_tool', { 'tool': SUPPORTED_TOOLS[toolName].displayName, 'shell': SUPPORTED_TOOLS[toolName].command }), value: 'start_tool' });
|
|
556
|
+
}
|
|
557
|
+
choices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
|
|
558
|
+
const { action } = await this.promptWithHints([
|
|
559
|
+
{
|
|
560
|
+
type: 'list',
|
|
561
|
+
name: 'action',
|
|
562
|
+
message: i18n.t('wizard.select_action'),
|
|
563
|
+
choices
|
|
564
|
+
}
|
|
565
|
+
]);
|
|
566
|
+
if (action === 'exit') {
|
|
567
|
+
console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
|
|
568
|
+
process.exit(0);
|
|
569
|
+
}
|
|
570
|
+
else if (action === 'back') {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
else if (action === 'load_config') {
|
|
574
|
+
await this.loadModelConfig(toolName);
|
|
575
|
+
}
|
|
576
|
+
else if (action === 'unload_config') {
|
|
577
|
+
await this.unloadModelConfig(toolName);
|
|
578
|
+
}
|
|
579
|
+
else if (action === 'start_tool') {
|
|
580
|
+
await this.startTool(toolName);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
async startTool(toolName) {
|
|
585
|
+
const tool = SUPPORTED_TOOLS[toolName];
|
|
586
|
+
if (!tool) {
|
|
587
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
588
|
+
}
|
|
589
|
+
const spinner = ora({
|
|
590
|
+
text: i18n.t('wizard.starting_tool'),
|
|
591
|
+
spinner: 'star2'
|
|
592
|
+
}).start();
|
|
593
|
+
try {
|
|
594
|
+
// 开启新窗口 执行 tool.command
|
|
595
|
+
execSync(tool.command, { stdio: 'inherit' });
|
|
596
|
+
spinner.succeed(i18n.t('wizard.tool_started'));
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
spinner.fail(i18n.t('wizard.tool_start_failed'));
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
async loadModelConfig(toolName) {
|
|
604
|
+
const spinner = ora({
|
|
605
|
+
text: i18n.t('wizard.loading_config'),
|
|
606
|
+
spinner: 'star2'
|
|
607
|
+
}).start();
|
|
608
|
+
try {
|
|
609
|
+
const config = configManager.getConfig();
|
|
610
|
+
if (!config.api_key) {
|
|
611
|
+
spinner.fail(i18n.t('wizard.missing_config'));
|
|
612
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
// Use default models if not set
|
|
616
|
+
const haikuModel = config.haikuModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
617
|
+
const sonnetModel = config.sonnetModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
618
|
+
const opusModel = config.opusModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
619
|
+
toolManager.loadModelConfig(toolName, {
|
|
620
|
+
apiKey: config.api_key,
|
|
621
|
+
haikuModel,
|
|
622
|
+
sonnetModel,
|
|
623
|
+
opusModel
|
|
624
|
+
});
|
|
625
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
626
|
+
spinner.succeed(chalk.green(i18n.t('wizard.config_loaded', { tool: SUPPORTED_TOOLS[toolName].displayName })));
|
|
627
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
spinner.fail(i18n.t('wizard.config_load_failed'));
|
|
631
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
632
|
+
console.error(error);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async unloadModelConfig(toolName) {
|
|
636
|
+
// 确认卸载操作
|
|
637
|
+
const { confirm } = await this.promptWithHints([
|
|
638
|
+
{
|
|
639
|
+
type: 'confirm',
|
|
640
|
+
name: 'confirm',
|
|
641
|
+
message: i18n.t('wizard.confirm_unload_config', { tool: SUPPORTED_TOOLS[toolName].displayName }),
|
|
642
|
+
default: false
|
|
643
|
+
}
|
|
644
|
+
]);
|
|
645
|
+
if (!confirm) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const spinner = ora({
|
|
649
|
+
text: i18n.t('wizard.unloading_config'),
|
|
650
|
+
spinner: 'star2'
|
|
651
|
+
}).start();
|
|
652
|
+
try {
|
|
653
|
+
if (toolName === 'claude-code') {
|
|
654
|
+
// 添加短暂延迟,让动画效果更流畅
|
|
655
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
656
|
+
claudeCodeManager.clearModelConfig();
|
|
657
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
658
|
+
spinner.succeed(chalk.green(i18n.t('wizard.config_unloaded')));
|
|
659
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
spinner.fail(i18n.t('wizard.tool_not_supported'));
|
|
663
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
spinner.fail(i18n.t('wizard.config_unload_failed'));
|
|
668
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
669
|
+
console.error(error);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
export const wizard = Wizard.getInstance();
|