cc-model-switcher
Version:
A simple CLI tool to switch Claude Code AI models easily
218 lines (186 loc) • 6.1 kB
JavaScript
const { Command } = require('commander');
const chalk = require('chalk');
const inquirer = require('inquirer');
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
const program = new Command();
// 获取用户主目录
const homeDir = require('os').homedir();
const configPath = path.join(homeDir, '.models.json');
// 默认配置 - 使用占位符
const defaultConfig = {
models: {
kimi: {
description: "Kimi AI (Moonshot)",
env: {
"ANTHROPIC_BASE_URL": "https://api.moonshot.cn/anthropic/",
"ANTHROPIC_AUTH_TOKEN": "YOUR_KIMI_API_KEY_HERE",
"API_TIMEOUT_MS": "600000",
"ANTHROPIC_MODEL": "kimi-k2-0711-preview",
"ANTHROPIC_SMALL_FAST_MODEL": "kimi-k2-0711-preview"
}
},
deepseek: {
description: "DeepSeek V3.1",
env: {
"ANTHROPIC_BASE_URL": "https://api.deepseek.com/anthropic",
"ANTHROPIC_AUTH_TOKEN": "YOUR_DEEPSEEK_API_KEY_HERE",
"API_TIMEOUT_MS": "600000",
"ANTHROPIC_MODEL": "deepseek-chat",
"ANTHROPIC_SMALL_FAST_MODEL": "deepseek-chat"
}
}
}
};
// 初始化配置文件
function initConfig() {
if (!fs.existsSync(configPath)) {
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
console.log(chalk.green(`✅ 已创建配置文件: ${configPath}`));
}
}
// 读取配置
function readConfig() {
try {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
return config;
} catch (error) {
console.error(chalk.red(`❌ 读取配置文件失败: ${error.message}`));
process.exit(1);
}
}
// 保存配置
function saveConfig(config) {
try {
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
} catch (error) {
console.error(chalk.red(`❌ 保存配置文件失败: ${error.message}`));
process.exit(1);
}
}
// 列出所有模型
function listModels() {
const config = readConfig();
console.log(chalk.cyan('\n📋 可用模型:'));
Object.entries(config.models).forEach(([name, model]) => {
console.log(` ${chalk.green(name)} - ${model.description}`);
});
console.log();
}
// 检查并提示API密钥
function checkApiKeys(config) {
let hasPlaceholder = false;
Object.entries(config.models).forEach(([name, model]) => {
if (model.env.ANTHROPIC_AUTH_TOKEN.includes('YOUR_') || model.env.ANTHROPIC_AUTH_TOKEN === 'placeholder') {
console.log(chalk.yellow(`⚠️ 模型 ${name} 需要配置API密钥`));
hasPlaceholder = true;
}
});
if (hasPlaceholder) {
console.log(chalk.cyan(`\n💡 请编辑配置文件添加API密钥: ${configPath}`));
console.log(chalk.gray('将 ANTHROPIC_AUTH_TOKEN 的值替换为你的实际API密钥'));
}
return hasPlaceholder;
}
// 切换模型
function switchModel(modelName) {
const config = readConfig();
if (!config.models[modelName]) {
console.log(chalk.red(`❌ 模型 "${modelName}" 不存在`));
listModels();
process.exit(1);
}
const model = config.models[modelName];
// 检查API密钥
if (checkApiKeys(config)) {
process.exit(1);
}
console.log(chalk.cyan(`🔄 正在切换到模型: ${chalk.green(modelName)}`));
// 设置环境变量并启动Claude Code
const env = { ...process.env };
Object.entries(model.env).forEach(([key, value]) => {
env[key] = value;
});
console.log(chalk.green(`✅ 已切换到 ${modelName},正在启动 Claude Code...`));
// 启动Claude Code - 正确处理Windows环境
const workDir = process.cwd();
console.log(chalk.cyan(`📁 工作目录: ${workDir}`));
const platform = process.platform;
let claude;
if (platform === 'win32') {
// Windows: 直接使用cwd参数设置工作目录
const normalizedPath = path.resolve(workDir);
console.log(chalk.gray(`规范化路径: ${normalizedPath}`));
// 修复日期: 2025-01-14
// 修复内容: 使用spawn的cwd参数直接设置工作目录,避免PowerShell路径处理问题
console.log(chalk.blue(`🚀 正在启动 Claude Code (工作目录: ${normalizedPath})`));
claude = spawn('npx', ['claude'], {
stdio: 'inherit',
env: env,
shell: true,
cwd: normalizedPath
});
} else {
// macOS/Linux: 正常启动
claude = spawn('claude', [], {
stdio: 'inherit',
env: env,
shell: true,
cwd: workDir
});
}
claude.on('error', (error) => {
if (error.code === 'ENOENT') {
console.error(chalk.red('❌ Claude Code 未安装或未添加到PATH'));
console.log(chalk.yellow('Windows用户请确保:'));
console.log(chalk.yellow('1. 已安装Claude Code'));
console.log(chalk.yellow('2. 已安装Git Bash'));
console.log(chalk.yellow('3. 已将claude命令添加到系统PATH'));
console.log(chalk.yellow('4. 或设置环境变量 CLAUDE_CODE_GIT_BASH_PATH'));
} else {
console.error(chalk.red(`❌ 启动Claude Code失败: ${error.message}`));
}
process.exit(1);
});
}
// 交互式选择模型
async function interactiveSelect() {
const config = readConfig();
const models = Object.entries(config.models).map(([name, model]) => ({
name: `${name} - ${model.description}`,
value: name
}));
const answer = await inquirer.prompt([
{
type: 'list',
name: 'model',
message: '选择要使用的模型:',
choices: models
}
]);
switchModel(answer.model);
}
// 主程序
program
.name('cc_switch')
.description('Claude Code 模型切换器')
.version('1.0.0');
program
.argument('[model]', '模型名称')
.option('-l, --list', '列出所有可用模型')
.option('-i, --interactive', '交互式选择模型')
.action((model, options) => {
initConfig();
if (options.list) {
listModels();
} else if (options.interactive) {
interactiveSelect();
} else if (model) {
switchModel(model);
} else {
switchModel('kimi'); // 默认使用kimi
}
});
program.parse();