claude-code-model-switch 1.0.3 → 2.1.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.
Files changed (4) hide show
  1. package/README.md +18 -14
  2. package/README_CN.md +18 -14
  3. package/index.js +225 -148
  4. package/package.json +3 -3
package/README.md CHANGED
@@ -4,12 +4,12 @@ A CLI tool for switching between different AI models in Claude Code.
4
4
 
5
5
  ## 🚀 Features
6
6
 
7
- - Quickly switch AI models used by Claude Code
7
+ - Launch Claude Code with a preconfigured model in one command (yolo mode)
8
8
  - Support for multiple pre-configured model environments
9
- - Automatic handling of model-related environment variables
9
+ - `start` injects model config as env vars; `config` persists to settings.json
10
+ - Automatic handling of model-related environment variables and defaults
10
11
  - Simple command-line interface
11
- - Secure configuration management - only modifies model-related settings
12
- - Reset to official models with unset command
12
+ - `unset` removes model-related entries from settings.json
13
13
 
14
14
  ## 📦 Installation
15
15
 
@@ -22,11 +22,14 @@ npm install -g claude-code-model-switch
22
22
  ### Basic Commands
23
23
 
24
24
  ```bash
25
- # Switch to specified model
26
- ccms deepseek-chat
25
+ # Launch Claude Code with the specified model (yolo mode enabled)
26
+ ccms start deepseek-chat
27
27
 
28
- # Or use switch subcommand
29
- ccms switch deepseek-chat
28
+ # Pass extra arguments directly to claude
29
+ ccms start deepseek-chat -p "hello"
30
+
31
+ # Write model configuration to ~/.claude/settings.json
32
+ ccms config deepseek-chat
30
33
 
31
34
  # List all available models
32
35
  ccms list
@@ -34,7 +37,7 @@ ccms list
34
37
  # Show configuration file paths
35
38
  ccms config-path
36
39
 
37
- # Remove all model-related configuration and use official models
40
+ # Remove model-related configuration from settings.json
38
41
  ccms unset
39
42
  ```
40
43
 
@@ -86,18 +89,19 @@ Edit the configuration file `~/.claude-code-model-switch/settings.json`:
86
89
 
87
90
  ### Special Rules
88
91
 
89
- 1. **Secure Configuration**: The tool only modifies model-related environment variables and won't touch other fields in the configuration file
90
- 2. **Default Values**:
92
+ 1. **start command**: `ccms start <model>` injects model config as environment variables and launches `claude` with `--dangerously-skip-permissions` (yolo mode)
93
+ 2. **config command**: `ccms config <model>` writes model config to `~/.claude/settings.json` for persistent configuration
94
+ 3. **Default Values**:
91
95
  - `CLAUDE_CODE_MAX_OUTPUT_TOKENS` defaults to `8192`
92
96
  - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` defaults to `1`
93
97
  - Unset model fields automatically use the value of `ANTHROPIC_MODEL`
94
- 3. **Configuration File**: Empty configuration file `{"models": {}}` is automatically created on first run
95
- 4. **Unset Command**: The `ccms unset` command removes only model-specific variables while preserving `CLAUDE_CODE_MAX_OUTPUT_TOKENS` and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`
98
+ 4. **Configuration File**: Empty configuration file `{"models": {}}` is automatically created on first run
99
+ 5. **Unset Command**: `ccms unset` removes only model-specific variables from `settings.json` and restores official models
96
100
 
97
101
  ## 📁 File Locations
98
102
 
99
- - Claude settings file: `~/.claude/settings.json`
100
103
  - Model configuration file: `~/.claude-code-model-switch/settings.json`
104
+ - Claude Code settings file (used by `config` / `unset`): `~/.claude/settings.json`
101
105
 
102
106
  ## 🔧 Development
103
107
 
package/README_CN.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  ## 🚀 功能特性
6
6
 
7
- - 快速切换 Claude Code 使用的 AI 模型
7
+ - 一条命令启动 Claude Code 并加载指定模型配置(yolo 模式)
8
8
  - 支持多个预配置模型环境
9
- - 自动处理模型相关环境变量
9
+ - `start` 将模型配置注入环境变量,`config` 可持久化写入 settings.json
10
+ - 自动处理模型相关环境变量及默认值
10
11
  - 命令行界面,操作简单
11
- - 安全的配置管理,只修改模型相关设置
12
- - 使用 unset 命令重置为官方模型
12
+ - `unset` 可清除 settings.json 中的模型相关配置
13
13
 
14
14
  ## 📦 安装
15
15
 
@@ -22,11 +22,14 @@ npm install -g claude-code-model-switch
22
22
  ### 基本命令
23
23
 
24
24
  ```bash
25
- # 切换到指定模型
26
- ccms deepseek-chat
25
+ # 用指定模型启动 Claude Code(自动启用 yolo 模式)
26
+ ccms start deepseek-chat
27
27
 
28
- # 或者使用 switch 子命令
29
- ccms switch deepseek-chat
28
+ # 将额外参数直接传给 claude
29
+ ccms start deepseek-chat -p "hello"
30
+
31
+ # 把模型配置写入 ~/.claude/settings.json
32
+ ccms config deepseek-chat
30
33
 
31
34
  # 列出所有可用模型
32
35
  ccms list
@@ -34,7 +37,7 @@ ccms list
34
37
  # 查看配置文件路径
35
38
  ccms config-path
36
39
 
37
- # 清除所有模型相关配置,恢复使用官方模型
40
+ # 清除 settings.json 中的模型相关配置,恢复使用官方模型
38
41
  ccms unset
39
42
  ```
40
43
 
@@ -86,18 +89,19 @@ ccms unset
86
89
 
87
90
  ### 特殊规则
88
91
 
89
- 1. **安全配置**:工具只会修改与模型相关的环境变量,不会触及配置文件中的其他字段
90
- 2. **默认值处理**:
92
+ 1. **start 命令**:`ccms start <model>` 将模型配置注入环境变量并启动 `claude`,默认附带 `--dangerously-skip-permissions`(yolo 模式)
93
+ 2. **config 命令**:`ccms config <model>` 将模型配置写入 `~/.claude/settings.json`,适合需要持久化配置的场景
94
+ 3. **默认值处理**:
91
95
  - `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 默认为 `8192`
92
96
  - `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` 默认为 `1`
93
97
  - 未设置的模型字段会自动使用 `ANTHROPIC_MODEL` 的值
94
- 3. **配置文件**:首次运行时会自动创建空的配置文件 `{"models": {}}`
95
- 4. **Unset 命令**:`ccms unset` 命令只删除模型特定的变量,同时保留 `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 和 `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`
98
+ 4. **配置文件**:首次运行时会自动创建空的配置文件 `{"models": {}}`
99
+ 5. **Unset 命令**:`ccms unset` 只删除 `settings.json` 中的模型相关变量,恢复使用官方模型
96
100
 
97
101
  ## 📁 文件位置
98
102
 
99
- - Claude 设置文件:`~/.claude/settings.json`
100
103
  - 模型配置文件:`~/.claude-code-model-switch/settings.json`
104
+ - Claude Code 设置文件(`config` / `unset` 使用):`~/.claude/settings.json`
101
105
 
102
106
  ## 🔧 开发
103
107
 
package/index.js CHANGED
@@ -2,17 +2,54 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const { spawn } = require('child_process');
5
6
  const { program } = require('commander');
6
7
 
7
- // Configuration file paths
8
8
  const CLAUDE_SETTINGS_PATH = path.join(process.env.HOME, '.claude', 'settings.json');
9
9
  const MODEL_CONFIG_PATH = path.join(process.env.HOME, '.claude-code-model-switch', 'settings.json');
10
+ const CLAUDE_BIN = process.env.CLAUDE_BIN || 'claude';
11
+ const YOLO_ARGS = ['--dangerously-skip-permissions'];
10
12
 
11
- // Default values
12
13
  const DEFAULT_MAX_OUTPUT_TOKENS = '8192';
13
14
  const DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC = '1';
14
15
 
15
- // Ensure configuration directory exists
16
+ const MODEL_ENV_VARS = [
17
+ 'ANTHROPIC_AUTH_TOKEN',
18
+ 'ANTHROPIC_BASE_URL',
19
+ 'ANTHROPIC_MODEL',
20
+ 'ANTHROPIC_SMALL_FAST_MODEL',
21
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
22
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
23
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL',
24
+ 'CLAUDE_CODE_MAX_OUTPUT_TOKENS',
25
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC',
26
+ 'CLAUDE_CODE_AUTO_COMPACT_WINDOW',
27
+ 'API_TIMEOUT_MS'
28
+ ];
29
+
30
+ const UNSET_ENV_VARS = [
31
+ 'ANTHROPIC_AUTH_TOKEN',
32
+ 'ANTHROPIC_BASE_URL',
33
+ 'ANTHROPIC_MODEL',
34
+ 'ANTHROPIC_SMALL_FAST_MODEL',
35
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
36
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
37
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL'
38
+ ];
39
+
40
+ const DERIVED_MODEL_FIELDS = [
41
+ 'ANTHROPIC_SMALL_FAST_MODEL',
42
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL',
43
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL',
44
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL'
45
+ ];
46
+
47
+ const REQUIRED_ENV_VARS = [
48
+ 'ANTHROPIC_MODEL',
49
+ 'ANTHROPIC_AUTH_TOKEN',
50
+ 'ANTHROPIC_BASE_URL'
51
+ ];
52
+
16
53
  function ensureConfigDir() {
17
54
  const configDir = path.dirname(MODEL_CONFIG_PATH);
18
55
  if (!fs.existsSync(configDir)) {
@@ -20,27 +57,23 @@ function ensureConfigDir() {
20
57
  }
21
58
  }
22
59
 
23
- // Load model configuration
24
60
  function loadModelConfig() {
25
61
  ensureConfigDir();
26
62
 
27
63
  if (!fs.existsSync(MODEL_CONFIG_PATH)) {
28
- // Create empty default configuration file
29
64
  const defaultConfig = { models: {} };
30
65
  fs.writeFileSync(MODEL_CONFIG_PATH, JSON.stringify(defaultConfig, null, 2));
31
66
  return defaultConfig;
32
67
  }
33
68
 
34
69
  try {
35
- const configContent = fs.readFileSync(MODEL_CONFIG_PATH, 'utf8');
36
- return JSON.parse(configContent);
70
+ return JSON.parse(fs.readFileSync(MODEL_CONFIG_PATH, 'utf8'));
37
71
  } catch (error) {
38
72
  console.error('Error: Failed to parse model configuration file');
39
73
  process.exit(1);
40
74
  }
41
75
  }
42
76
 
43
- // Load Claude settings
44
77
  function loadClaudeSettings() {
45
78
  if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) {
46
79
  console.error('Error: Claude settings file not found');
@@ -48,15 +81,13 @@ function loadClaudeSettings() {
48
81
  }
49
82
 
50
83
  try {
51
- const settingsContent = fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8');
52
- return JSON.parse(settingsContent);
84
+ return JSON.parse(fs.readFileSync(CLAUDE_SETTINGS_PATH, 'utf8'));
53
85
  } catch (error) {
54
86
  console.error('Error: Failed to parse Claude settings file');
55
87
  process.exit(1);
56
88
  }
57
89
  }
58
90
 
59
- // Save Claude settings
60
91
  function saveClaudeSettings(settings) {
61
92
  try {
62
93
  fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
@@ -66,20 +97,36 @@ function saveClaudeSettings(settings) {
66
97
  }
67
98
  }
68
99
 
69
- // Get model configuration
100
+ function getValidModels(config = loadModelConfig()) {
101
+ return Object.keys(config.models || {}).filter(
102
+ (name) => config.models[name] && Object.keys(config.models[name]).length > 0
103
+ );
104
+ }
105
+
106
+ function reportUnknownModel(model) {
107
+ const config = loadModelConfig();
108
+ const modelConfig = config.models && config.models[model];
109
+
110
+ if (modelConfig && Object.keys(modelConfig).length === 0) {
111
+ console.error(`Error: Model "${model}" configuration is empty`);
112
+ } else {
113
+ console.error(`Error: Model "${model}" not found`);
114
+ }
115
+
116
+ console.log('Available models:', getValidModels(config).join(', ') || 'None');
117
+ console.log('Run "ccms --help" to see available commands');
118
+ process.exit(1);
119
+ }
120
+
70
121
  function getModelConfig(modelName) {
71
122
  const config = loadModelConfig();
72
123
 
73
124
  if (!config.models || !config.models[modelName]) {
74
- console.error(`Error: Model "${modelName}" not found`);
75
- console.log('Available models:', Object.keys(config.models || {}).join(', ') || 'None');
76
- console.log('Run "ccms --help" to see available commands');
77
- process.exit(1);
125
+ reportUnknownModel(modelName);
78
126
  }
79
127
 
80
128
  const modelConfig = config.models[modelName];
81
129
 
82
- // Check if model configuration is empty
83
130
  if (Object.keys(modelConfig).length === 0) {
84
131
  console.error(`Error: Model "${modelName}" configuration is empty`);
85
132
  console.log('Run "ccms --help" to see available commands');
@@ -89,178 +136,162 @@ function getModelConfig(modelName) {
89
136
  return modelConfig;
90
137
  }
91
138
 
92
- // Apply model configuration
93
- function applyModelConfig(modelConfig) {
94
- const settings = loadClaudeSettings();
139
+ function normalizeModelConfig(modelConfig) {
140
+ const normalized = { ...modelConfig };
95
141
 
96
- // Ensure env object exists
97
- if (!settings.env) {
98
- settings.env = {};
99
- }
100
-
101
- // First, set default values for unset model fields in the config itself
102
- // If ANTHROPIC_MODEL is set but other model fields are not, use ANTHROPIC_MODEL value
103
- if (modelConfig.ANTHROPIC_MODEL) {
104
- const derivedModelFields = [
105
- 'ANTHROPIC_SMALL_FAST_MODEL',
106
- 'ANTHROPIC_DEFAULT_SONNET_MODEL',
107
- 'ANTHROPIC_DEFAULT_OPUS_MODEL'
108
- ];
109
-
110
- for (const field of derivedModelFields) {
111
- if (!modelConfig[field]) {
112
- modelConfig[field] = modelConfig.ANTHROPIC_MODEL;
142
+ if (normalized.ANTHROPIC_MODEL) {
143
+ for (const field of DERIVED_MODEL_FIELDS) {
144
+ if (!normalized[field]) {
145
+ normalized[field] = normalized.ANTHROPIC_MODEL;
113
146
  }
114
147
  }
115
148
  }
116
149
 
117
- // Only update model-related environment variables
118
- const modelEnvVars = [
119
- 'ANTHROPIC_AUTH_TOKEN',
120
- 'ANTHROPIC_BASE_URL',
121
- 'ANTHROPIC_MODEL',
122
- 'ANTHROPIC_SMALL_FAST_MODEL',
123
- 'ANTHROPIC_DEFAULT_SONNET_MODEL',
124
- 'ANTHROPIC_DEFAULT_OPUS_MODEL',
125
- 'CLAUDE_CODE_MAX_OUTPUT_TOKENS',
126
- 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC'
127
- ];
128
-
129
- // Apply configuration
130
- for (const key of modelEnvVars) {
131
- if (modelConfig[key] !== undefined) {
132
- settings.env[key] = modelConfig[key];
150
+ return normalized;
151
+ }
152
+
153
+ function buildModelEnv(modelConfig) {
154
+ const normalized = normalizeModelConfig(modelConfig);
155
+ const env = { ...process.env };
156
+
157
+ for (const key of MODEL_ENV_VARS) {
158
+ if (normalized[key] !== undefined) {
159
+ env[key] = String(normalized[key]);
133
160
  }
134
161
  }
135
162
 
136
- // Handle default value logic
137
- // Check required environment variables
138
- const requiredEnvVars = [
139
- 'ANTHROPIC_MODEL',
140
- 'ANTHROPIC_AUTH_TOKEN',
141
- 'ANTHROPIC_BASE_URL'
142
- ];
143
-
144
- for (const envVar of requiredEnvVars) {
145
- if (!settings.env[envVar]) {
163
+ for (const envVar of REQUIRED_ENV_VARS) {
164
+ if (!env[envVar]) {
146
165
  console.error(`Error: ${envVar} is required`);
147
166
  process.exit(1);
148
167
  }
149
168
  }
150
169
 
151
-
152
- // Handle special environment variable defaults
153
- if (!settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS) {
154
- settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
170
+ if (!env.CLAUDE_CODE_MAX_OUTPUT_TOKENS) {
171
+ env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
155
172
  }
156
173
 
157
- if (!settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
158
- settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
174
+ if (!env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
175
+ env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
159
176
  }
160
177
 
161
- saveClaudeSettings(settings);
162
- console.log(`✅ Switched to model: ${settings.env.ANTHROPIC_MODEL}`);
178
+ return env;
163
179
  }
164
180
 
165
- // List all models
166
- function listModels() {
167
- const config = loadModelConfig();
168
- const models = Object.keys(config.models || {});
181
+ function getStartArgsFromArgv() {
182
+ const args = process.argv.slice(2);
169
183
 
170
- if (models.length === 0) {
171
- console.log('No models configured');
172
- return;
184
+ if (args[0] !== 'start') {
185
+ return [];
173
186
  }
174
187
 
175
- console.log('Available models:');
176
- models.forEach(model => {
177
- console.log(` - ${model}`);
178
- });
188
+ let claudeArgs = args.slice(2);
189
+ if (claudeArgs[0] === '--') {
190
+ claudeArgs = claudeArgs.slice(1);
191
+ }
192
+
193
+ return claudeArgs;
179
194
  }
180
195
 
181
- // Check if model name is valid
182
- function isValidModel(modelName) {
183
- const config = loadModelConfig();
184
- return config.models &&
185
- config.models[modelName] &&
186
- Object.keys(config.models[modelName]).length > 0;
196
+ function hasPermissionOverride(args) {
197
+ return args.some((arg) =>
198
+ arg === '--dangerously-skip-permissions' ||
199
+ arg === '--allow-dangerously-skip-permissions' ||
200
+ arg.startsWith('--permission-mode')
201
+ );
187
202
  }
188
203
 
189
- // Main program
190
- program
191
- .name('ccms')
192
- .description('Claude Code Model Switch - Switch Claude Code models')
193
- .version('1.0.3')
194
- .argument('[model]', 'Model name to switch to')
195
- .action((model) => {
196
- if (model) {
197
- // If model name argument provided, attempt to switch model
198
- if (isValidModel(model)) {
199
- const modelConfig = getModelConfig(model);
200
- applyModelConfig(modelConfig);
201
- } else {
202
- const config = loadModelConfig();
203
- const modelConfig = config.models && config.models[model];
204
-
205
- if (modelConfig && Object.keys(modelConfig).length === 0) {
206
- console.error(`Error: Model "${model}" configuration is empty`);
207
- } else {
208
- console.error(`Error: Model "${model}" not found`);
209
- }
210
-
211
- const validModels = Object.keys(config.models || {}).filter(name =>
212
- config.models[name] && Object.keys(config.models[name]).length > 0
213
- );
214
- console.log('Available models:', validModels.join(', ') || 'None');
215
- console.log('Run "ccms --help" to see available commands');
216
- process.exit(1);
217
- }
204
+ function buildStartArgs(userArgs = []) {
205
+ if (hasPermissionOverride(userArgs)) {
206
+ return userArgs;
207
+ }
208
+
209
+ return [...YOLO_ARGS, ...userArgs];
210
+ }
211
+
212
+ function launchClaude(env, claudeArgs = []) {
213
+ const child = spawn(CLAUDE_BIN, claudeArgs, {
214
+ env,
215
+ stdio: 'inherit'
216
+ });
217
+
218
+ child.on('error', (error) => {
219
+ if (error.code === 'ENOENT') {
220
+ console.error(`Error: "${CLAUDE_BIN}" command not found. Is Claude Code installed?`);
218
221
  } else {
219
- // If no arguments provided, show help information
220
- program.help();
222
+ console.error(`Error: Failed to start Claude Code (${error.message})`);
221
223
  }
224
+ process.exit(1);
222
225
  });
223
226
 
224
- program
225
- .command('switch <model>')
226
- .description('Switch to specified model')
227
- .action((model) => {
228
- const modelConfig = getModelConfig(model);
229
- applyModelConfig(modelConfig);
227
+ child.on('close', (code, signal) => {
228
+ if (signal) {
229
+ process.kill(process.pid, signal);
230
+ return;
231
+ }
232
+ process.exit(code ?? 0);
230
233
  });
234
+ }
231
235
 
232
- program
233
- .command('list')
234
- .description('List all available models')
235
- .action(() => {
236
- listModels();
237
- });
236
+ function startClaudeWithModel(modelName) {
237
+ const modelConfig = getModelConfig(modelName);
238
+ const env = buildModelEnv(modelConfig);
239
+ const claudeArgs = buildStartArgs(getStartArgsFromArgv());
238
240
 
239
- // Unset all model-related configuration
240
- function unsetModelConfig() {
241
+ console.log(`[ccms] starting claude code with model: ${env.ANTHROPIC_MODEL} (yolo mode)\n`);
242
+ launchClaude(env, claudeArgs);
243
+ }
244
+
245
+ function applyModelConfig(modelConfig) {
241
246
  const settings = loadClaudeSettings();
247
+ const normalized = normalizeModelConfig(modelConfig);
242
248
 
243
- // Ensure env object exists
244
249
  if (!settings.env) {
245
250
  settings.env = {};
246
251
  }
247
252
 
248
- // Model-related environment variables to remove (excluding some settings)
249
- const modelEnvVars = [
250
- 'ANTHROPIC_AUTH_TOKEN',
251
- 'ANTHROPIC_BASE_URL',
252
- 'ANTHROPIC_MODEL',
253
- 'ANTHROPIC_SMALL_FAST_MODEL',
254
- 'ANTHROPIC_DEFAULT_SONNET_MODEL',
255
- 'ANTHROPIC_DEFAULT_OPUS_MODEL'
256
- ];
253
+ for (const key of MODEL_ENV_VARS) {
254
+ if (normalized[key] !== undefined) {
255
+ settings.env[key] = normalized[key];
256
+ }
257
+ }
258
+
259
+ for (const envVar of REQUIRED_ENV_VARS) {
260
+ if (!settings.env[envVar]) {
261
+ console.error(`Error: ${envVar} is required`);
262
+ process.exit(1);
263
+ }
264
+ }
265
+
266
+ if (!settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS) {
267
+ settings.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS = DEFAULT_MAX_OUTPUT_TOKENS;
268
+ }
269
+
270
+ if (!settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) {
271
+ settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = DEFAULT_DISABLE_NONESSENTIAL_TRAFFIC;
272
+ }
273
+
274
+ saveClaudeSettings(settings);
275
+ console.log(`Switched to model: ${settings.env.ANTHROPIC_MODEL}`);
276
+ }
277
+
278
+ function configModel(modelName) {
279
+ const modelConfig = getModelConfig(modelName);
280
+ applyModelConfig(modelConfig);
281
+ }
282
+
283
+ function unsetModelConfig() {
284
+ const settings = loadClaudeSettings();
285
+
286
+ if (!settings.env) {
287
+ settings.env = {};
288
+ }
257
289
 
258
290
  let removedCount = 0;
259
291
  const removedVars = [];
260
292
 
261
- // Remove only existing model-related environment variables
262
- for (const key of modelEnvVars) {
263
- if (settings.env.hasOwnProperty(key)) {
293
+ for (const key of UNSET_ENV_VARS) {
294
+ if (Object.prototype.hasOwnProperty.call(settings.env, key)) {
264
295
  delete settings.env[key];
265
296
  removedCount++;
266
297
  removedVars.push(key);
@@ -277,6 +308,48 @@ function unsetModelConfig() {
277
308
  }
278
309
  }
279
310
 
311
+ function listModels() {
312
+ const models = getValidModels();
313
+
314
+ if (models.length === 0) {
315
+ console.log('No models configured');
316
+ return;
317
+ }
318
+
319
+ console.log('Available models:');
320
+ models.forEach((model) => {
321
+ console.log(` - ${model}`);
322
+ });
323
+ }
324
+
325
+ program
326
+ .name('ccms')
327
+ .description('Claude Code Model Switch - Manage and launch Claude Code with configured models')
328
+ .version('2.1.0');
329
+
330
+ program
331
+ .command('start <model> [claude-args...]')
332
+ .description('Launch Claude Code with model env vars (yolo mode enabled)')
333
+ .allowUnknownOption()
334
+ .allowExcessArguments()
335
+ .action((model) => {
336
+ startClaudeWithModel(model);
337
+ });
338
+
339
+ program
340
+ .command('config <model>')
341
+ .description('Write model configuration to ~/.claude/settings.json')
342
+ .action((model) => {
343
+ configModel(model);
344
+ });
345
+
346
+ program
347
+ .command('list')
348
+ .description('List all available models')
349
+ .action(() => {
350
+ listModels();
351
+ });
352
+
280
353
  program
281
354
  .command('config-path')
282
355
  .description('Display configuration file paths')
@@ -287,9 +360,13 @@ program
287
360
 
288
361
  program
289
362
  .command('unset')
290
- .description('Remove all model-related configuration and use official models')
363
+ .description('Remove model-related configuration from ~/.claude/settings.json')
291
364
  .action(() => {
292
365
  unsetModelConfig();
293
366
  });
294
367
 
295
- program.parse();
368
+ program.parse();
369
+
370
+ if (!process.argv.slice(2).length) {
371
+ program.help();
372
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "claude-code-model-switch",
3
- "version": "1.0.3",
4
- "description": "A CLI tool to switch Claude Code models",
3
+ "version": "2.1.0",
4
+ "description": "Launch Claude Code with preconfigured model environments",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "ccms": "./index.js"
7
+ "ccms": "index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "echo \"Error: no test specified\" && exit 1"