@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
package/dist/lib/i18n.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
class I18n {
|
|
5
|
+
static instance;
|
|
6
|
+
currentLocale = 'en_US';
|
|
7
|
+
translations = new Map();
|
|
8
|
+
fallbackLocale = 'en_US';
|
|
9
|
+
localesDir;
|
|
10
|
+
constructor() {
|
|
11
|
+
// Determine the locales directory path
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
// Both in development (src/lib/i18n.ts) and production (dist/lib/i18n.js),
|
|
15
|
+
// locales is at the same relative path: lib -> parent -> locales
|
|
16
|
+
this.localesDir = join(__dirname, '..', 'locales');
|
|
17
|
+
this.loadTranslations();
|
|
18
|
+
this.loadSavedLocale();
|
|
19
|
+
}
|
|
20
|
+
static getInstance() {
|
|
21
|
+
if (!I18n.instance) {
|
|
22
|
+
I18n.instance = new I18n();
|
|
23
|
+
}
|
|
24
|
+
return I18n.instance;
|
|
25
|
+
}
|
|
26
|
+
loadTranslations() {
|
|
27
|
+
try {
|
|
28
|
+
// Check if locales directory exists
|
|
29
|
+
if (!existsSync(this.localesDir)) {
|
|
30
|
+
console.warn(`Locales directory not found at ${this.localesDir}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Get all JSON files in the locales directory
|
|
34
|
+
const files = readdirSync(this.localesDir);
|
|
35
|
+
const localeFiles = files.filter(file => file.endsWith('.json'));
|
|
36
|
+
if (localeFiles.length === 0) {
|
|
37
|
+
console.warn('No locale files found in locales directory');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Load each locale file
|
|
41
|
+
for (const file of localeFiles) {
|
|
42
|
+
const locale = file.replace('.json', '');
|
|
43
|
+
const filePath = join(this.localesDir, file);
|
|
44
|
+
try {
|
|
45
|
+
const fileContent = readFileSync(filePath, 'utf-8');
|
|
46
|
+
const translations = JSON.parse(fileContent);
|
|
47
|
+
this.translations.set(locale, translations);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.warn(`Failed to load locale file ${file}:`, error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Try to detect locale from environment
|
|
54
|
+
const envLocale = process.env.LANG || process.env.LC_ALL || process.env.LC_MESSAGES;
|
|
55
|
+
if (envLocale) {
|
|
56
|
+
const localeCode = envLocale.split('.')[0].replace('_', '-');
|
|
57
|
+
// First try exact match
|
|
58
|
+
if (this.translations.has(localeCode)) {
|
|
59
|
+
this.currentLocale = localeCode;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// Try language-only match (e.g., 'en' from 'en-US')
|
|
63
|
+
const langOnly = localeCode.split('-')[0];
|
|
64
|
+
const matchingLocale = Array.from(this.translations.keys()).find(key => key.startsWith(langOnly + '_'));
|
|
65
|
+
if (matchingLocale) {
|
|
66
|
+
this.currentLocale = matchingLocale;
|
|
67
|
+
}
|
|
68
|
+
else if (langOnly === 'zh') {
|
|
69
|
+
// Fallback to zh_CN for Chinese
|
|
70
|
+
this.currentLocale = 'zh_CN';
|
|
71
|
+
}
|
|
72
|
+
else if (langOnly === 'en') {
|
|
73
|
+
// Fallback to en_US for English
|
|
74
|
+
this.currentLocale = 'en_US';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.warn('Failed to load translations:', error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
setLocale(locale) {
|
|
84
|
+
if (this.translations.has(locale)) {
|
|
85
|
+
this.currentLocale = locale;
|
|
86
|
+
this.saveLocale();
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.warn(`Locale '${locale}' not supported, falling back to '${this.currentLocale}'`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
getLocale() {
|
|
93
|
+
return this.currentLocale;
|
|
94
|
+
}
|
|
95
|
+
getAvailableLocales() {
|
|
96
|
+
return Array.from(this.translations.keys());
|
|
97
|
+
}
|
|
98
|
+
translate(key, params) {
|
|
99
|
+
const keys = key.split('.');
|
|
100
|
+
let translation = this.translations.get(this.currentLocale);
|
|
101
|
+
for (const k of keys) {
|
|
102
|
+
if (translation && typeof translation === 'object' && k in translation) {
|
|
103
|
+
translation = translation[k];
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// Fallback to fallback locale
|
|
107
|
+
translation = this.translations.get(this.fallbackLocale);
|
|
108
|
+
for (const fallbackKey of keys) {
|
|
109
|
+
if (translation && typeof translation === 'object' && fallbackKey in translation) {
|
|
110
|
+
translation = translation[fallbackKey];
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
return key; // Return key if translation not found
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (typeof translation !== 'string') {
|
|
120
|
+
return key;
|
|
121
|
+
}
|
|
122
|
+
// Replace parameters in the translation string
|
|
123
|
+
if (params) {
|
|
124
|
+
return translation.replace(/\{\{(\w+)\}\}/g, (match, param) => {
|
|
125
|
+
return params[param] || match;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return translation;
|
|
129
|
+
}
|
|
130
|
+
t(key, params) {
|
|
131
|
+
return this.translate(key, params);
|
|
132
|
+
}
|
|
133
|
+
saveLocale() {
|
|
134
|
+
// Language is now saved via ConfigManager
|
|
135
|
+
// This method is kept for compatibility
|
|
136
|
+
}
|
|
137
|
+
loadSavedLocale() {
|
|
138
|
+
// Language is now loaded via ConfigManager
|
|
139
|
+
// This method is kept for compatibility
|
|
140
|
+
}
|
|
141
|
+
loadFromConfig(locale) {
|
|
142
|
+
if (this.translations.has(locale)) {
|
|
143
|
+
this.currentLocale = locale;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export const i18n = I18n.getInstance();
|
|
148
|
+
export { i18n as I18n };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ModelConfig } from './claude-code-manager.js';
|
|
2
|
+
export interface ToolInfo {
|
|
3
|
+
name: string;
|
|
4
|
+
command: string;
|
|
5
|
+
installCommand: string;
|
|
6
|
+
configPath: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
hidden?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const SUPPORTED_TOOLS: Record<string, ToolInfo>;
|
|
11
|
+
export declare class ToolManager {
|
|
12
|
+
private static instance;
|
|
13
|
+
private constructor();
|
|
14
|
+
static getInstance(): ToolManager;
|
|
15
|
+
isToolInstalled(toolName: string): boolean;
|
|
16
|
+
installTool(toolName: string): Promise<void>;
|
|
17
|
+
getToolConfig(toolName: string): any;
|
|
18
|
+
updateToolConfig(toolName: string, config: any): void;
|
|
19
|
+
loadModelConfig(toolName: string, config: ModelConfig): void;
|
|
20
|
+
getInstalledTools(): string[];
|
|
21
|
+
getSupportedTools(): ToolInfo[];
|
|
22
|
+
isGitInstalled(): boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare const toolManager: ToolManager;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { claudeCodeManager } from './claude-code-manager.js';
|
|
6
|
+
import { i18n } from './i18n.js';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import terminalLink from 'terminal-link';
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
export const SUPPORTED_TOOLS = {
|
|
12
|
+
'claude-code': {
|
|
13
|
+
name: 'claude-code',
|
|
14
|
+
command: 'claude',
|
|
15
|
+
installCommand: 'npm install -g @anthropic-ai/claude-code',
|
|
16
|
+
configPath: join(homedir(), '.claude', 'settings.json'),
|
|
17
|
+
displayName: 'Claude Code'
|
|
18
|
+
},
|
|
19
|
+
'opencode': {
|
|
20
|
+
name: 'opencode',
|
|
21
|
+
command: 'opencode',
|
|
22
|
+
installCommand: 'npm install -g opencode',
|
|
23
|
+
configPath: join(homedir(), '.config', 'opencode', 'config.json'),
|
|
24
|
+
displayName: 'OpenCode',
|
|
25
|
+
hidden: true // 暂不适配,隐藏不展示
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
export class ToolManager {
|
|
29
|
+
static instance;
|
|
30
|
+
constructor() { }
|
|
31
|
+
static getInstance() {
|
|
32
|
+
if (!ToolManager.instance) {
|
|
33
|
+
ToolManager.instance = new ToolManager();
|
|
34
|
+
}
|
|
35
|
+
return ToolManager.instance;
|
|
36
|
+
}
|
|
37
|
+
isToolInstalled(toolName) {
|
|
38
|
+
const tool = SUPPORTED_TOOLS[toolName];
|
|
39
|
+
if (!tool)
|
|
40
|
+
return false;
|
|
41
|
+
try {
|
|
42
|
+
// Windows 使用 where 命令,Unix 系统使用 which 命令
|
|
43
|
+
const checkCommand = process.platform === 'win32'
|
|
44
|
+
? `where ${tool.command}`
|
|
45
|
+
: `which ${tool.command}`;
|
|
46
|
+
execSync(checkCommand, { stdio: 'pipe' });
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async installTool(toolName) {
|
|
54
|
+
const tool = SUPPORTED_TOOLS[toolName];
|
|
55
|
+
if (!tool) {
|
|
56
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
57
|
+
}
|
|
58
|
+
const spinner = ora(i18n.t('wizard.installing_tool')).start();
|
|
59
|
+
try {
|
|
60
|
+
// 首次尝试正常安装
|
|
61
|
+
execSync(tool.installCommand, { stdio: 'inherit' });
|
|
62
|
+
spinner.succeed(i18n.t('wizard.tool_installed'));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
spinner.fail(i18n.t('wizard.install_failed'));
|
|
66
|
+
// 检查是否是权限错误 (EACCES)
|
|
67
|
+
// execSync 的错误信息可能在 stderr 中,需要检查多个来源
|
|
68
|
+
const errorMessage = (error.message || '') + (error.stderr?.toString() || '') + (error.stdout?.toString() || '');
|
|
69
|
+
const isPermissionError = errorMessage.includes('EACCES') ||
|
|
70
|
+
errorMessage.includes('permission denied') ||
|
|
71
|
+
errorMessage.includes('EPERM') ||
|
|
72
|
+
error.status === 243; // npm 权限错误的退出码
|
|
73
|
+
if (!isPermissionError) {
|
|
74
|
+
// 如果不是权限错误,直接抛出
|
|
75
|
+
throw new Error(`Failed to install ${tool.displayName}: ${error}`);
|
|
76
|
+
}
|
|
77
|
+
console.log('\n⚠️ ' + i18n.t('install.permission_detected') + '\n');
|
|
78
|
+
// Windows 平台处理
|
|
79
|
+
if (process.platform === 'win32') {
|
|
80
|
+
try {
|
|
81
|
+
// Windows: 尝试使用用户级安装(不需要管理员权限)
|
|
82
|
+
const userInstallCommand = tool.installCommand.replace('npm install -g', 'npm install -g --force');
|
|
83
|
+
console.log('🔧 ' + i18n.t('install.trying_solution', { num: '1', desc: i18n.t('install.using_force') }));
|
|
84
|
+
execSync(userInstallCommand, { stdio: 'inherit' });
|
|
85
|
+
console.log('\n✅ ' + i18n.t('install.permission_fixed'));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
catch (retryError) {
|
|
89
|
+
// 如果还是失败,显示解决方案并询问用户
|
|
90
|
+
console.log(`Retry install error ${retryError}`);
|
|
91
|
+
console.log('\n❌ ' + i18n.t('install.auto_fix_failed'));
|
|
92
|
+
console.log(chalk.yellow('\n📌 ' + i18n.t('install.windows_solutions')));
|
|
93
|
+
console.log('');
|
|
94
|
+
// 方案 1: 以管理员身份运行
|
|
95
|
+
console.log(chalk.cyan.bold(i18n.t('install.windows_solution_1_title')));
|
|
96
|
+
console.log(chalk.gray(' ' + i18n.t('install.windows_solution_1_step1')));
|
|
97
|
+
console.log(chalk.gray(' ' + i18n.t('install.windows_solution_1_step2')));
|
|
98
|
+
console.log(chalk.gray(' ' + i18n.t('install.windows_solution_1_step3')));
|
|
99
|
+
console.log(chalk.white(` ${tool.installCommand}`));
|
|
100
|
+
console.log('');
|
|
101
|
+
// 方案 2: 用户级安装
|
|
102
|
+
console.log(chalk.cyan.bold(i18n.t('install.windows_solution_2_title')));
|
|
103
|
+
console.log(chalk.gray(' ' + i18n.t('install.windows_solution_2_command')));
|
|
104
|
+
console.log(chalk.white(` ${tool.installCommand.replace('npm install -g', 'npm install -g --prefix=%APPDATA%\\npm')}`));
|
|
105
|
+
console.log('');
|
|
106
|
+
// 询问用户是否已完成安装
|
|
107
|
+
const { action } = await inquirer.prompt([
|
|
108
|
+
{
|
|
109
|
+
type: 'list',
|
|
110
|
+
name: 'action',
|
|
111
|
+
message: i18n.t('install.what_next'),
|
|
112
|
+
choices: [
|
|
113
|
+
{ name: i18n.t('install.installed_continue'), value: 'continue' },
|
|
114
|
+
{ name: i18n.t('install.cancel_install'), value: 'cancel' }
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
]);
|
|
118
|
+
if (action === 'cancel') {
|
|
119
|
+
throw new Error(i18n.t('install.user_cancelled'));
|
|
120
|
+
}
|
|
121
|
+
// 验证是否真的安装成功
|
|
122
|
+
if (!this.isToolInstalled(toolName)) {
|
|
123
|
+
console.log(chalk.red('\n❌ ' + i18n.t('install.still_not_installed', { tool: tool.displayName })));
|
|
124
|
+
throw new Error(`${tool.displayName} is not installed`);
|
|
125
|
+
}
|
|
126
|
+
console.log(chalk.green('\n✅ ' + i18n.t('install.verified_success', { tool: tool.displayName })));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// macOS 和 Linux 平台处理 - 显示建议而不是自动执行
|
|
131
|
+
console.log(chalk.yellow('\n📌 ' + i18n.t('install.unix_solutions')));
|
|
132
|
+
console.log('');
|
|
133
|
+
// 方案 1: 使用 sudo
|
|
134
|
+
console.log(chalk.cyan.bold(i18n.t('install.unix_solution_1_title')));
|
|
135
|
+
console.log(chalk.gray(' ' + i18n.t('install.unix_solution_1_desc')));
|
|
136
|
+
console.log(chalk.white(` sudo ${tool.installCommand}`));
|
|
137
|
+
console.log('');
|
|
138
|
+
// 方案 2: 使用 nvm (推荐)
|
|
139
|
+
const npmDocsUrl = 'https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally';
|
|
140
|
+
const clickableLink = terminalLink(npmDocsUrl, npmDocsUrl, { fallback: () => npmDocsUrl });
|
|
141
|
+
console.log(chalk.cyan.bold(i18n.t('install.unix_solution_2_title')));
|
|
142
|
+
console.log(chalk.gray(' ' + i18n.t('install.unix_solution_2_desc')));
|
|
143
|
+
console.log(chalk.blue(' 📖 ' + i18n.t('install.npm_docs_link') + ': ') + clickableLink);
|
|
144
|
+
console.log('');
|
|
145
|
+
// 询问用户是否已完成安装
|
|
146
|
+
const { action } = await inquirer.prompt([
|
|
147
|
+
{
|
|
148
|
+
type: 'list',
|
|
149
|
+
name: 'action',
|
|
150
|
+
message: i18n.t('install.what_next'),
|
|
151
|
+
choices: [
|
|
152
|
+
{ name: i18n.t('install.installed_continue'), value: 'continue' },
|
|
153
|
+
{ name: i18n.t('install.cancel_install'), value: 'cancel' }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
]);
|
|
157
|
+
if (action === 'cancel') {
|
|
158
|
+
throw new Error(i18n.t('install.user_cancelled'));
|
|
159
|
+
}
|
|
160
|
+
// 验证是否真的安装成功
|
|
161
|
+
if (!this.isToolInstalled(toolName)) {
|
|
162
|
+
console.log(chalk.red('\n❌ ' + i18n.t('install.still_not_installed', { tool: tool.displayName })));
|
|
163
|
+
throw new Error(`${tool.displayName} is not installed`);
|
|
164
|
+
}
|
|
165
|
+
console.log(chalk.green('\n✅ ' + i18n.t('install.verified_success', { tool: tool.displayName })));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
getToolConfig(toolName) {
|
|
170
|
+
const tool = SUPPORTED_TOOLS[toolName];
|
|
171
|
+
if (!tool) {
|
|
172
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
173
|
+
}
|
|
174
|
+
// Claude Code 使用专门的管理器
|
|
175
|
+
if (toolName === 'claude-code') {
|
|
176
|
+
return {
|
|
177
|
+
settings: claudeCodeManager.getSettings()
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
if (existsSync(tool.configPath)) {
|
|
182
|
+
const content = readFileSync(tool.configPath, 'utf-8');
|
|
183
|
+
return JSON.parse(content);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
console.warn(`Failed to read config for ${toolName}:`, error);
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
updateToolConfig(toolName, config) {
|
|
192
|
+
const tool = SUPPORTED_TOOLS[toolName];
|
|
193
|
+
if (!tool) {
|
|
194
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
195
|
+
}
|
|
196
|
+
// Claude Code 使用专门的管理器
|
|
197
|
+
if (toolName === 'claude-code') {
|
|
198
|
+
if (config.settings) {
|
|
199
|
+
claudeCodeManager.saveSettings(config.settings);
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
// 使用 dirname 获取目录路径,确保跨平台兼容(Windows/macOS/Linux)
|
|
205
|
+
const configDir = dirname(tool.configPath);
|
|
206
|
+
if (!existsSync(configDir)) {
|
|
207
|
+
mkdirSync(configDir, { recursive: true });
|
|
208
|
+
}
|
|
209
|
+
writeFileSync(tool.configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
throw new Error(`Failed to update config for ${toolName}: ${error}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
loadModelConfig(toolName, config) {
|
|
216
|
+
const tool = SUPPORTED_TOOLS[toolName];
|
|
217
|
+
if (!tool) {
|
|
218
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
219
|
+
}
|
|
220
|
+
// Claude Code 使用专门的管理器
|
|
221
|
+
if (toolName === 'claude-code') {
|
|
222
|
+
claudeCodeManager.saveModelConfig(config);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// 其他工具的配置
|
|
226
|
+
let existingConfig = this.getToolConfig(toolName) || {};
|
|
227
|
+
existingConfig = {
|
|
228
|
+
...existingConfig,
|
|
229
|
+
apiKey: config.apiKey,
|
|
230
|
+
haikuModel: config.haikuModel,
|
|
231
|
+
sonnetModel: config.sonnetModel,
|
|
232
|
+
opusModel: config.opusModel
|
|
233
|
+
};
|
|
234
|
+
this.updateToolConfig(toolName, existingConfig);
|
|
235
|
+
}
|
|
236
|
+
getInstalledTools() {
|
|
237
|
+
return Object.keys(SUPPORTED_TOOLS).filter(toolName => this.isToolInstalled(toolName));
|
|
238
|
+
}
|
|
239
|
+
getSupportedTools() {
|
|
240
|
+
return Object.values(SUPPORTED_TOOLS).filter(tool => !tool.hidden);
|
|
241
|
+
}
|
|
242
|
+
isGitInstalled() {
|
|
243
|
+
try {
|
|
244
|
+
// Windows 使用 where 命令,Unix 系统使用 which 命令
|
|
245
|
+
const checkCommand = process.platform === 'win32'
|
|
246
|
+
? `where git`
|
|
247
|
+
: `which git`;
|
|
248
|
+
execSync(checkCommand, { stdio: 'pipe' });
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export const toolManager = ToolManager.getInstance();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare class Wizard {
|
|
2
|
+
static instance: any;
|
|
3
|
+
BOX_WIDTH: number;
|
|
4
|
+
private cachedModels;
|
|
5
|
+
constructor();
|
|
6
|
+
static getInstance(): any;
|
|
7
|
+
/**
|
|
8
|
+
* Create a simple box with title using double-line border style
|
|
9
|
+
*/
|
|
10
|
+
createBox(title: any): void;
|
|
11
|
+
/**
|
|
12
|
+
* Display operation hints
|
|
13
|
+
*/
|
|
14
|
+
showOperationHints(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Prompt wrapper that shows operation hints
|
|
17
|
+
*/
|
|
18
|
+
promptWithHints(questions: any): Promise<import("inquirer").Answers>;
|
|
19
|
+
printBanner(): void;
|
|
20
|
+
resetScreen(): void;
|
|
21
|
+
runFirstTimeSetup(): Promise<void>;
|
|
22
|
+
configLanguage(): Promise<void>;
|
|
23
|
+
configApiKey(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Fetch and cache available models
|
|
26
|
+
*/
|
|
27
|
+
fetchModels(): Promise<string[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Configure models menu
|
|
30
|
+
*/
|
|
31
|
+
configModels(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Step-by-step model selection
|
|
34
|
+
*/
|
|
35
|
+
selectModels(): Promise<void>;
|
|
36
|
+
selectAndConfigureTool(): Promise<void>;
|
|
37
|
+
configureTool(toolName: any): Promise<void>;
|
|
38
|
+
showMainMenu(): Promise<void>;
|
|
39
|
+
showToolMenu(toolName: any): Promise<void>;
|
|
40
|
+
startTool(toolName: any): Promise<void>;
|
|
41
|
+
loadModelConfig(toolName: any): Promise<void>;
|
|
42
|
+
unloadModelConfig(toolName: any): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export declare const wizard: any;
|