ccconfig 1.2.0 → 1.3.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 CHANGED
@@ -1,5 +1,7 @@
1
1
  # Claude Code Configuration Manager
2
2
 
3
+ [English](README.md) | [中文](README_zh.md)
4
+
3
5
  Quickly switch between different claude-code providers
4
6
 
5
7
 
@@ -35,7 +37,8 @@ ccconfig add
35
37
  # - ANTHROPIC_BASE_URL
36
38
  # - ANTHROPIC_AUTH_TOKEN
37
39
  # - ANTHROPIC_API_KEY
38
- # - Description
40
+ # - ANTHROPIC_MODEL (optional)
41
+ # - ANTHROPIC_SMALL_FAST_MODEL (optional)
39
42
 
40
43
  # 3. Switch configuration
41
44
  ccconfig use work
@@ -72,7 +75,8 @@ ccconfig add
72
75
  # - ANTHROPIC_BASE_URL
73
76
  # - ANTHROPIC_AUTH_TOKEN
74
77
  # - ANTHROPIC_API_KEY
75
- # - Description
78
+ # - ANTHROPIC_MODEL (optional)
79
+ # - ANTHROPIC_SMALL_FAST_MODEL (optional)
76
80
 
77
81
  # 3. Switch configuration
78
82
  ccconfig use work
@@ -179,6 +183,91 @@ if (Test-Path $cconfigEnv) {
179
183
 
180
184
  ## Advanced Usage
181
185
 
186
+ ### Update Existing Configuration
187
+
188
+ If you need to modify an existing configuration, use the `update` command:
189
+
190
+ ```bash
191
+ # Update a configuration interactively
192
+ ccconfig update work
193
+
194
+ # The tool will:
195
+ # 1. Show current values as defaults
196
+ # 2. Prompt for each field
197
+ # 3. Press Enter to keep current value, or type new value to update
198
+ ```
199
+
200
+ **Example:**
201
+ ```bash
202
+ $ ccconfig update work
203
+ Updating configuration 'work'
204
+ Press Enter to keep the current value, or enter a new value to update
205
+
206
+ ANTHROPIC_BASE_URL [https://api.company.com]: https://new-api.company.com
207
+ ANTHROPIC_AUTH_TOKEN [sk-ant-api...]: <press Enter to keep>
208
+ ANTHROPIC_API_KEY []: sk-new-key-123
209
+ ANTHROPIC_MODEL [claude-sonnet-4-5-20250929]: <press Enter to keep>
210
+ Do you want to set ANTHROPIC_SMALL_FAST_MODEL? (y/N) [n]:
211
+
212
+ ✓ Configuration 'work' updated
213
+ ```
214
+
215
+ **Note:** After updating a configuration, remember to activate it with `ccconfig use work` if you want the changes to take effect immediately.
216
+
217
+ ### Shell Completion
218
+
219
+ ccconfig supports shell completion for commands, profile names, and options. This makes it easier to discover and use commands.
220
+
221
+ **Features:**
222
+ - ✅ Command completion (list, add, update, use, remove, etc.)
223
+ - ✅ Profile name completion (dynamically reads from your configurations)
224
+ - ✅ Option completion (--permanent, --show-secret, etc.)
225
+ - ✅ Mode completion (settings, env)
226
+ - ✅ Format completion (bash, zsh, fish, etc.)
227
+
228
+ **Installation:**
229
+
230
+ ```bash
231
+ # Bash
232
+ ccconfig completion bash >> ~/.bashrc
233
+ source ~/.bashrc
234
+
235
+ # Zsh
236
+ ccconfig completion zsh >> ~/.zshrc
237
+ source ~/.zshrc
238
+
239
+ # Fish
240
+ ccconfig completion fish > ~/.config/fish/completions/ccconfig.fish
241
+ # Fish will automatically load it on next startup
242
+
243
+ # PowerShell
244
+ ccconfig completion pwsh >> $PROFILE
245
+ # Reload profile: . $PROFILE
246
+ ```
247
+
248
+ **Note for PowerShell:** If you get an error about `$PROFILE` not existing, create it first:
249
+ ```powershell
250
+ New-Item -Path $PROFILE -ItemType File -Force
251
+ ccconfig completion pwsh >> $PROFILE
252
+ . $PROFILE
253
+ ```
254
+
255
+ **Usage examples after installing completion:**
256
+
257
+ ```bash
258
+ # Type 'ccconfig' and press TAB to see all commands
259
+ ccconfig <TAB>
260
+ # Shows: list, add, update, use, remove, current, mode, env, edit, completion
261
+
262
+ # Type 'ccconfig use' and press TAB to see all profiles
263
+ ccconfig use <TAB>
264
+ # Shows: work, personal, project1, etc.
265
+
266
+ # Type 'ccconfig mode' and press TAB
267
+ ccconfig mode <TAB>
268
+ # Shows: settings, env
269
+ ```
270
+
182
271
  ### Quick Aliases
183
272
 
184
273
  ```bash
package/README_zh.md ADDED
@@ -0,0 +1,406 @@
1
+ # Claude Code 配置管理器
2
+
3
+ [English](README.md) | [中文](README_zh.md)
4
+
5
+ 快速切换不同的 claude-code 提供商配置
6
+
7
+ ```bash
8
+ # 工作时间切换到公司配置
9
+ ccconfig use company
10
+
11
+ # 下班后切换回个人配置
12
+ ccconfig use personal
13
+
14
+ # 永久写入 shell 配置文件(无需每次 eval 或 source)
15
+ ccconfig use personal --permanent # 或使用 -p 简写
16
+ ```
17
+
18
+ ## 快速开始
19
+
20
+ ### 安装
21
+
22
+ ```bash
23
+ # 从 npm 安装(推荐)
24
+ npm install -g ccconfig
25
+ ```
26
+
27
+ ### ENV 模式(推荐,默认)
28
+
29
+ ```bash
30
+ # 1. 配置 Shell 自动加载(见下文)
31
+
32
+ # 2. 添加配置(交互模式)
33
+ ccconfig add
34
+ # 按提示输入:
35
+ # - 名称
36
+ # - ANTHROPIC_BASE_URL
37
+ # - ANTHROPIC_AUTH_TOKEN
38
+ # - ANTHROPIC_API_KEY
39
+ # - ANTHROPIC_MODEL(可选)
40
+ # - ANTHROPIC_SMALL_FAST_MODEL(可选)
41
+
42
+ # 3. 切换配置
43
+ ccconfig use work
44
+
45
+ # 4. 立即生效(选择一种方法):
46
+ # 方法 A: 临时生效(仅当前 shell)
47
+ eval $(ccconfig env bash) # 或使用输出中检测到的命令
48
+
49
+ # 方法 B: 永久生效(写入 shell 配置文件)
50
+ ccconfig use work --permanent # 或使用 -p 简写
51
+ # 自动检测并修改 ~/.bashrc、~/.zshrc 或 config.fish
52
+ ```
53
+
54
+ ### Settings 模式
55
+
56
+ Settings 模式直接修改 `~/.claude/settings.json` 文件,这是 Claude Code 的原生配置文件。此模式适合不想配置 shell 脚本的情况。
57
+
58
+ **工作原理:**
59
+ - 直接将环境变量写入 `~/.claude/settings.json` 的 `env` 字段
60
+ - Claude Code 启动时读取这些设置
61
+ - 无需 shell 配置
62
+ - 每次切换后需要重启 Claude Code
63
+
64
+ **设置步骤:**
65
+
66
+ ```bash
67
+ # 1. 切换到 settings 模式
68
+ ccconfig mode settings
69
+
70
+ # 2. 添加配置(交互模式)
71
+ ccconfig add
72
+ # 按提示输入:
73
+ # - 名称
74
+ # - ANTHROPIC_BASE_URL
75
+ # - ANTHROPIC_AUTH_TOKEN
76
+ # - ANTHROPIC_API_KEY
77
+ # - ANTHROPIC_MODEL(可选)
78
+ # - ANTHROPIC_SMALL_FAST_MODEL(可选)
79
+
80
+ # 3. 切换配置
81
+ ccconfig use work
82
+
83
+ # 4. 重启 Claude Code
84
+ # 配置现已生效!
85
+ ```
86
+
87
+ **验证:**
88
+ ```bash
89
+ # 查看当前配置
90
+ ccconfig current
91
+
92
+ # 直接查看 settings 文件
93
+ cat ~/.claude/settings.json
94
+ ```
95
+
96
+ #### ENV 模式 Shell 配置
97
+
98
+ 您有两种方式配置 shell 环境:
99
+
100
+ **方式 1: 自动配置(推荐)**
101
+
102
+ 使用 `-p/--permanent` 标志自动写入您的 shell 配置:
103
+
104
+ ```bash
105
+ # 自动检测您的 shell 并写入相应的配置文件
106
+ ccconfig use <profile> --permanent
107
+
108
+ # 您将看到:
109
+ # - 修改 shell 配置的警告
110
+ # - 目标文件路径
111
+ # - 内容预览
112
+ # - 确认提示(yes/no)
113
+
114
+ # 这将修改:
115
+ # - Fish: ~/.config/fish/config.fish
116
+ # - Bash: ~/.bashrc
117
+ # - Zsh: ~/.zshrc
118
+ # - PowerShell: ~/.config/powershell/profile.ps1
119
+ ```
120
+
121
+ 工具会在 `# >>> ccconfig >>>` 和 `# <<< ccconfig <<<` 标记之间添加一个标记块,便于后续识别和更新。
122
+
123
+ **安全特性:**
124
+ - **需要用户确认**: 修改文件前会提示确认
125
+ - **内容预览**: 显示将要写入的确切内容
126
+ - **清晰说明**: 解释将要进行的更改
127
+ - **非破坏性**: 保留现有内容,仅更新 ccconfig 块
128
+ - **仅交互模式**: 需要交互式终端以防止意外修改
129
+
130
+ **方式 2: 手动配置**
131
+
132
+ 如果您喜欢手动配置,请将以下内容添加到您的 shell 启动文件:
133
+
134
+ **Fish** (`~/.config/fish/config.fish`):
135
+ ```fish
136
+ # 加载 Claude Code 环境变量
137
+ set -l ccconfig_env ~/.config/ccconfig/current.env
138
+ if test -f $ccconfig_env
139
+ for line in (cat $ccconfig_env)
140
+ set -l parts (string split -m1 '=' $line)
141
+ if test (count $parts) -eq 2
142
+ set -gx $parts[1] $parts[2]
143
+ end
144
+ end
145
+ end
146
+ ```
147
+
148
+ **Bash** (`~/.bashrc`):
149
+ ```bash
150
+ # 加载 Claude Code 环境变量
151
+ if [ -f ~/.config/ccconfig/current.env ]; then
152
+ set -a
153
+ . ~/.config/ccconfig/current.env
154
+ set +a
155
+ fi
156
+ ```
157
+
158
+ **Zsh** (`~/.zshrc`):
159
+ ```zsh
160
+ # 加载 Claude Code 环境变量
161
+ if [ -f ~/.config/ccconfig/current.env ]; then
162
+ set -a
163
+ . ~/.config/ccconfig/current.env
164
+ set +a
165
+ fi
166
+ ```
167
+
168
+ **PowerShell** (`$PROFILE`):
169
+ ```powershell
170
+ # 加载 Claude Code 环境变量
171
+ $cconfigEnv = "$env:USERPROFILE\.config\ccconfig\current.env"
172
+ if (Test-Path $cconfigEnv) {
173
+ Get-Content $cconfigEnv | ForEach-Object {
174
+ if ($_ -match '^([^=]+)=(.*)$') {
175
+ [Environment]::SetEnvironmentVariable($matches[1], $matches[2], 'Process')
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ **注意**: 手动配置允许您通过更改 `current.env` 动态切换配置文件,而 `-p/--permanent` 直接将值写入 shell 配置文件。
182
+
183
+ ## 高级用法
184
+
185
+ ### 更新现有配置
186
+
187
+ 如果您需要修改已有配置,使用 `update` 命令:
188
+
189
+ ```bash
190
+ # 交互式更新配置
191
+ ccconfig update work
192
+
193
+ # 工具会:
194
+ # 1. 显示当前值作为默认值
195
+ # 2. 提示输入每个字段
196
+ # 3. 按 Enter 保持当前值,或输入新值来更新
197
+ ```
198
+
199
+ **示例:**
200
+ ```bash
201
+ $ ccconfig update work
202
+ Updating configuration 'work'
203
+ Press Enter to keep the current value, or enter a new value to update
204
+
205
+ ANTHROPIC_BASE_URL [https://api.company.com]: https://new-api.company.com
206
+ ANTHROPIC_AUTH_TOKEN [sk-ant-api...]: <按 Enter 保持不变>
207
+ ANTHROPIC_API_KEY []: sk-new-key-123
208
+ ANTHROPIC_MODEL [claude-sonnet-4-5-20250929]: <按 Enter 保持不变>
209
+ Do you want to set ANTHROPIC_SMALL_FAST_MODEL? (y/N) [n]:
210
+
211
+ ✓ Configuration 'work' updated
212
+ ```
213
+
214
+ **注意:** 更新配置后,如果要立即生效,记得使用 `ccconfig use work` 激活配置。
215
+
216
+ ### Shell 自动补全
217
+
218
+ ccconfig 支持命令、配置名称和选项的 shell 自动补全,让您更容易发现和使用命令。
219
+
220
+ **功能:**
221
+ - ✅ 命令补全 (list, add, update, use, remove 等)
222
+ - ✅ 配置名称补全(动态读取您的配置)
223
+ - ✅ 选项补全 (--permanent, --show-secret 等)
224
+ - ✅ 模式补全 (settings, env)
225
+ - ✅ 格式补全 (bash, zsh, fish 等)
226
+
227
+ **安装:**
228
+
229
+ ```bash
230
+ # Bash
231
+ ccconfig completion bash >> ~/.bashrc
232
+ source ~/.bashrc
233
+
234
+ # Zsh
235
+ ccconfig completion zsh >> ~/.zshrc
236
+ source ~/.zshrc
237
+
238
+ # Fish
239
+ ccconfig completion fish > ~/.config/fish/completions/ccconfig.fish
240
+ # Fish 会在下次启动时自动加载
241
+
242
+ # PowerShell
243
+ ccconfig completion pwsh >> $PROFILE
244
+ # 重新加载配置: . $PROFILE
245
+ ```
246
+
247
+ **PowerShell 注意事项:** 如果遇到 `$PROFILE` 不存在的错误,请先创建它:
248
+ ```powershell
249
+ New-Item -Path $PROFILE -ItemType File -Force
250
+ ccconfig completion pwsh >> $PROFILE
251
+ . $PROFILE
252
+ ```
253
+
254
+ **安装补全后的使用示例:**
255
+
256
+ ```bash
257
+ # 输入 'ccconfig' 然后按 TAB 查看所有命令
258
+ ccconfig <TAB>
259
+ # 显示: list, add, update, use, remove, current, mode, env, edit, completion
260
+
261
+ # 输入 'ccconfig use' 然后按 TAB 查看所有配置
262
+ ccconfig use <TAB>
263
+ # 显示: work, personal, project1 等
264
+
265
+ # 输入 'ccconfig mode' 然后按 TAB
266
+ ccconfig mode <TAB>
267
+ # 显示: settings, env
268
+ ```
269
+
270
+ ### 快捷别名
271
+
272
+ ```bash
273
+ # 添加到 ~/.bashrc 或 ~/.zshrc
274
+ alias ccs='ccconfig'
275
+ alias ccs-use='ccconfig use'
276
+ alias ccs-list='ccconfig list'
277
+ alias ccs-current='ccconfig current'
278
+
279
+ # Fish (~/.config/fish/config.fish)
280
+ abbr ccs 'ccconfig'
281
+ abbr ccs-use 'ccconfig use'
282
+ abbr ccs-list 'ccconfig list'
283
+ ```
284
+
285
+ ### 项目级配置
286
+
287
+ 对于特定项目,您可以导出 .env 文件:
288
+
289
+ ```bash
290
+ # 导出到项目目录
291
+ cd my-project
292
+ ccconfig use project-config
293
+ ccconfig env dotenv > .env
294
+
295
+ # 使用项目配置
296
+ source .env
297
+ ```
298
+
299
+ ### 备份和同步
300
+
301
+ ```bash
302
+ # 备份配置
303
+ cp ~/.config/ccconfig/profiles.json ~/backup/ccconfig-profiles.json
304
+
305
+ # 同步到新机器
306
+ scp ~/backup/ccconfig-profiles.json new-machine:~/.config/ccconfig/
307
+
308
+ # 或使用版本控制(注意安全!)
309
+ cd ~/.config/ccconfig
310
+ git init
311
+ echo "*.env" >> .gitignore
312
+ git add profiles.json
313
+ git commit -m "ccconfig profiles"
314
+ ```
315
+
316
+ ## 故障排除
317
+
318
+ ### 配置未生效
319
+
320
+ **Settings 模式**:
321
+ 1. **检查配置是否正确写入**:
322
+ ```bash
323
+ ccconfig current
324
+ # 查看【1】~/.claude/settings.json 部分
325
+ ```
326
+ 2. **直接验证 settings.json**:
327
+ ```bash
328
+ cat ~/.claude/settings.json | grep -A 5 '"env"'
329
+ ```
330
+ 3. **确认已重启 Claude Code**:
331
+ - 完全退出 Claude Code(不只是关闭窗口)
332
+ - 重新启动应用程序
333
+ 4. **检查 `~/.claude/settings.json` 中的 `env` 字段**:
334
+ ```json
335
+ {
336
+ "env": {
337
+ "ANTHROPIC_BASE_URL": "https://api.anthropic.com",
338
+ "ANTHROPIC_AUTH_TOKEN": "sk-...",
339
+ "ANTHROPIC_API_KEY": "sk-..."
340
+ }
341
+ }
342
+ ```
343
+
344
+ **ENV 模式**:
345
+ 1. **检查环境变量文件**:
346
+ ```bash
347
+ cat ~/.config/ccconfig/current.env
348
+ ```
349
+ 2. **如果使用 --permanent 标志**:
350
+ - 工具会显示警告并在修改文件前要求确认
351
+ - 检查您的 shell 配置文件是否有 ccconfig 块:
352
+ ```bash
353
+ # For bash/zsh
354
+ cat ~/.bashrc | grep -A 5 "ccconfig"
355
+ # For fish
356
+ cat ~/.config/fish/config.fish | grep -A 5 "ccconfig"
357
+ ```
358
+ - 重启 shell 或运行: `source ~/.bashrc`(或您的 shell 对应的命令)
359
+ - 注意: 您也可以使用 `-p` 作为 `--permanent` 的简写
360
+ - 要取消操作,在提示时输入 "no"
361
+
362
+ 3. **如果使用手动配置或 eval 命令**:
363
+ - 确认 Shell 配置正确: `cat ~/.bashrc | grep ccconfig`
364
+ - 重启 Shell 或使用 `eval $(ccconfig env bash)`
365
+
366
+ 4. **检查进程环境变量**:
367
+ ```bash
368
+ ccconfig current
369
+ # 查看【3】当前进程环境变量部分
370
+ ```
371
+
372
+ ### 切换模式后配置丢失
373
+
374
+ 切换模式不会影响已保存的配置,只会改变配置的应用方式。切换后,您需要再次 `use`:
375
+
376
+ ```bash
377
+ ccconfig mode env # 切换到 env 模式
378
+ ccconfig use work # 重新应用配置
379
+ ```
380
+
381
+ ### 文件权限问题
382
+
383
+ ```bash
384
+ # 修复配置文件权限
385
+ chmod 600 ~/.config/ccconfig/profiles.json
386
+ chmod 600 ~/.claude/settings.json
387
+ chmod 600 ~/.config/ccconfig/current.env
388
+ ```
389
+
390
+ ## 安全考虑
391
+
392
+ 1. **文件权限**: 工具会自动将配置文件设置为 600 权限(仅所有者可读写)
393
+
394
+ 2. **敏感信息**:
395
+ - API 密钥默认隐藏,使用 `--show-secret` 查看完整值
396
+ - 不要将配置文件提交到公共仓库
397
+ - 使用 `.gitignore` 排除敏感文件
398
+
399
+ 3. **环境变量**: ENV 模式的环境变量会被子进程继承,请注意安全
400
+
401
+ 4. **版本控制**: 如果对配置进行版本控制,请使用加密或私有仓库
402
+
403
+ ## 许可证
404
+
405
+ MIT
406
+
package/ccconfig.js CHANGED
@@ -147,6 +147,8 @@ function updateClaudeSettings(envVars) {
147
147
  delete settings.env.ANTHROPIC_BASE_URL;
148
148
  delete settings.env.ANTHROPIC_AUTH_TOKEN;
149
149
  delete settings.env.ANTHROPIC_API_KEY;
150
+ delete settings.env.ANTHROPIC_MODEL;
151
+ delete settings.env.ANTHROPIC_SMALL_FAST_MODEL;
150
152
 
151
153
  // Set new environment variables
152
154
  Object.assign(settings.env, envVars);
@@ -323,8 +325,11 @@ function list() {
323
325
  if (profile.env && profile.env.ANTHROPIC_BASE_URL) {
324
326
  console.log(` URL: ${profile.env.ANTHROPIC_BASE_URL}`);
325
327
  }
326
- if (profile.description) {
327
- console.log(` Description: ${profile.description}`);
328
+ if (profile.env && profile.env.ANTHROPIC_MODEL) {
329
+ console.log(` Model: ${profile.env.ANTHROPIC_MODEL}`);
330
+ }
331
+ if (profile.env && profile.env.ANTHROPIC_SMALL_FAST_MODEL) {
332
+ console.log(` Small Fast Model: ${profile.env.ANTHROPIC_SMALL_FAST_MODEL}`);
328
333
  }
329
334
  console.log('');
330
335
  }
@@ -374,7 +379,8 @@ async function add(name) {
374
379
  });
375
380
  };
376
381
 
377
- let baseUrl, authToken, apiKey, description;
382
+ let baseUrl, authToken, apiKey, model, smallFastModel;
383
+ let profiles;
378
384
 
379
385
  try {
380
386
  if (!name) {
@@ -386,38 +392,48 @@ async function add(name) {
386
392
  process.exit(1);
387
393
  }
388
394
 
395
+ // Check if configuration already exists before asking for details
396
+ profiles = loadProfiles() || {profiles: {}};
397
+
398
+ if (profiles.profiles[name]) {
399
+ console.error(`Error: Configuration '${name}' already exists`);
400
+ console.error('To update, please edit the configuration file directly');
401
+ process.exit(1);
402
+ }
403
+
389
404
  baseUrl = await askQuestion(
390
- 'Please enter ANTHROPIC_BASE_URL (can be empty, default https://api.anthropic.com)',
405
+ 'Please enter ANTHROPIC_BASE_URL (press Enter for default)',
391
406
  'https://api.anthropic.com');
392
407
 
393
408
  authToken =
394
- await askQuestion('Please enter ANTHROPIC_AUTH_TOKEN (can be empty)');
409
+ await askQuestion('Please enter ANTHROPIC_AUTH_TOKEN (press Enter to set as empty)');
410
+
411
+ apiKey = await askQuestion('Please enter ANTHROPIC_API_KEY (press Enter to set as empty)');
395
412
 
396
- apiKey = await askQuestion('Please enter ANTHROPIC_API_KEY (can be empty)');
413
+ model = await askQuestion('Please enter ANTHROPIC_MODEL (press Enter to skip)');
397
414
 
398
- description = await askQuestion(
399
- 'Please enter configuration description (can be empty)');
415
+ smallFastModel = await askQuestion('Please enter ANTHROPIC_SMALL_FAST_MODEL (press Enter to skip)');
400
416
  } finally {
401
417
  if (rl) {
402
418
  rl.close();
403
419
  }
404
420
  }
405
421
 
406
- const profiles = loadProfiles() || {profiles: {}};
407
-
408
- if (profiles.profiles[name]) {
409
- console.error(`Error: Configuration '${name}' already exists`);
410
- console.error('To update, please edit the configuration file directly');
411
- process.exit(1);
412
- }
413
-
414
422
  const envVars = {
415
423
  ANTHROPIC_BASE_URL: baseUrl || '',
416
424
  ANTHROPIC_AUTH_TOKEN: authToken || '',
417
425
  ANTHROPIC_API_KEY: apiKey || ''
418
426
  };
419
427
 
420
- profiles.profiles[name] = {env: envVars, description};
428
+ // Add optional model variables if provided
429
+ if (model) {
430
+ envVars.ANTHROPIC_MODEL = model;
431
+ }
432
+ if (smallFastModel) {
433
+ envVars.ANTHROPIC_SMALL_FAST_MODEL = smallFastModel;
434
+ }
435
+
436
+ profiles.profiles[name] = {env: envVars};
421
437
 
422
438
  saveProfiles(profiles);
423
439
  console.log(`✓ Configuration '${name}' added`);
@@ -441,6 +457,12 @@ async function add(name) {
441
457
  safePrint('ANTHROPIC_BASE_URL', envVars.ANTHROPIC_BASE_URL, false);
442
458
  safePrint('ANTHROPIC_AUTH_TOKEN', envVars.ANTHROPIC_AUTH_TOKEN);
443
459
  safePrint('ANTHROPIC_API_KEY', envVars.ANTHROPIC_API_KEY);
460
+ if (envVars.ANTHROPIC_MODEL) {
461
+ safePrint('ANTHROPIC_MODEL', envVars.ANTHROPIC_MODEL, false);
462
+ }
463
+ if (envVars.ANTHROPIC_SMALL_FAST_MODEL) {
464
+ safePrint('ANTHROPIC_SMALL_FAST_MODEL', envVars.ANTHROPIC_SMALL_FAST_MODEL, false);
465
+ }
444
466
  console.log('');
445
467
  console.log('This information has been saved to:');
446
468
  console.log(` ${PROFILES_FILE}`);
@@ -450,6 +472,131 @@ async function add(name) {
450
472
  console.log('Or run ccconfig edit to open it with your preferred editor');
451
473
  }
452
474
 
475
+ /**
476
+ * Update existing configuration
477
+ */
478
+ async function update(name) {
479
+ // Auto-initialize if needed
480
+ initIfNeeded();
481
+
482
+ const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
483
+
484
+ if (!isInteractive) {
485
+ console.error('Error: Interactive mode required for updating configurations');
486
+ console.error('This command must be run in an interactive terminal');
487
+ process.exit(1);
488
+ }
489
+
490
+ let rl = null;
491
+
492
+ const askQuestion = (question, defaultValue = '') => {
493
+ if (!rl) {
494
+ rl = readline.createInterface(
495
+ {input: process.stdin, output: process.stdout});
496
+ }
497
+ return new Promise(resolve => {
498
+ const suffix = defaultValue ? ` [${defaultValue}]` : '';
499
+ rl.question(`${question}${suffix}: `, answer => {
500
+ const trimmed = answer.trim();
501
+ resolve(trimmed ? trimmed : defaultValue);
502
+ });
503
+ });
504
+ };
505
+
506
+ let baseUrl, authToken, apiKey, model, smallFastModel;
507
+ let profiles;
508
+
509
+ try {
510
+ if (!name) {
511
+ name = await askQuestion('Please enter configuration name to update');
512
+ }
513
+
514
+ if (!name) {
515
+ console.error('Error: Configuration name cannot be empty');
516
+ process.exit(1);
517
+ }
518
+
519
+ // Check if configuration exists
520
+ profiles = loadProfiles() || {profiles: {}};
521
+
522
+ if (!profiles.profiles[name]) {
523
+ console.error(`Error: Configuration '${name}' does not exist`);
524
+ console.error('Run ccconfig list to see available configurations');
525
+ console.error(`Or use 'ccconfig add ${name}' to create a new configuration`);
526
+ process.exit(1);
527
+ }
528
+
529
+ const existingProfile = profiles.profiles[name];
530
+ const existingEnv = existingProfile.env || {};
531
+
532
+ console.log(`Updating configuration '${name}'`);
533
+ console.log('Press Enter to keep current value/default, or enter new value to update');
534
+ console.log('');
535
+
536
+ baseUrl = await askQuestion(
537
+ 'ANTHROPIC_BASE_URL (press Enter to keep current/default)',
538
+ existingEnv.ANTHROPIC_BASE_URL || 'https://api.anthropic.com');
539
+
540
+ authToken =
541
+ await askQuestion('ANTHROPIC_AUTH_TOKEN (press Enter to keep current/set empty)', existingEnv.ANTHROPIC_AUTH_TOKEN || '');
542
+
543
+ apiKey = await askQuestion('ANTHROPIC_API_KEY (press Enter to keep current/set empty)', existingEnv.ANTHROPIC_API_KEY || '');
544
+
545
+ model = await askQuestion('ANTHROPIC_MODEL (press Enter to skip/keep current)', existingEnv.ANTHROPIC_MODEL || '');
546
+
547
+ smallFastModel = await askQuestion('ANTHROPIC_SMALL_FAST_MODEL (press Enter to skip/keep current)', existingEnv.ANTHROPIC_SMALL_FAST_MODEL || '');
548
+ } finally {
549
+ if (rl) {
550
+ rl.close();
551
+ }
552
+ }
553
+
554
+ const envVars = {
555
+ ANTHROPIC_BASE_URL: baseUrl || '',
556
+ ANTHROPIC_AUTH_TOKEN: authToken || '',
557
+ ANTHROPIC_API_KEY: apiKey || ''
558
+ };
559
+
560
+ // Add optional model variables if provided
561
+ if (model) {
562
+ envVars.ANTHROPIC_MODEL = model;
563
+ }
564
+ if (smallFastModel) {
565
+ envVars.ANTHROPIC_SMALL_FAST_MODEL = smallFastModel;
566
+ }
567
+
568
+ profiles.profiles[name] = {env: envVars};
569
+
570
+ saveProfiles(profiles);
571
+ console.log(`✓ Configuration '${name}' updated`);
572
+ console.log('');
573
+ console.log('Updated environment variables:');
574
+ const safePrint = (key, value, mask = true) => {
575
+ if (!value) {
576
+ console.log(` ${key}: (not set)`);
577
+ return;
578
+ }
579
+ if (!mask) {
580
+ console.log(` ${key}: ${value}`);
581
+ return;
582
+ }
583
+ const masked = value.length > 20 ? value.substring(0, 20) + '...' : value;
584
+ console.log(` ${key}: ${masked}`);
585
+ };
586
+ safePrint('ANTHROPIC_BASE_URL', envVars.ANTHROPIC_BASE_URL, false);
587
+ safePrint('ANTHROPIC_AUTH_TOKEN', envVars.ANTHROPIC_AUTH_TOKEN);
588
+ safePrint('ANTHROPIC_API_KEY', envVars.ANTHROPIC_API_KEY);
589
+ if (envVars.ANTHROPIC_MODEL) {
590
+ safePrint('ANTHROPIC_MODEL', envVars.ANTHROPIC_MODEL, false);
591
+ }
592
+ if (envVars.ANTHROPIC_SMALL_FAST_MODEL) {
593
+ safePrint('ANTHROPIC_SMALL_FAST_MODEL', envVars.ANTHROPIC_SMALL_FAST_MODEL, false);
594
+ }
595
+ console.log('');
596
+ console.log('Run the following command to activate:');
597
+ console.log(` ccconfig use ${name}`);
598
+ }
599
+
453
600
  /**
454
601
  * Remove configuration
455
602
  */
@@ -860,7 +1007,9 @@ function current(showSecret = false) {
860
1007
  const processEnv = {
861
1008
  ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL,
862
1009
  ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN,
863
- ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY
1010
+ ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
1011
+ ANTHROPIC_MODEL: process.env.ANTHROPIC_MODEL,
1012
+ ANTHROPIC_SMALL_FAST_MODEL: process.env.ANTHROPIC_SMALL_FAST_MODEL
864
1013
  };
865
1014
  const currentProfile = getCurrentProfile();
866
1015
 
@@ -890,6 +1039,12 @@ function current(showSecret = false) {
890
1039
 
891
1040
  console.log(` ANTHROPIC_BASE_URL: ${baseUrl}`);
892
1041
  console.log(` ANTHROPIC_AUTH_TOKEN: ${maskedToken}`);
1042
+ if (settings.env.ANTHROPIC_MODEL) {
1043
+ console.log(` ANTHROPIC_MODEL: ${settings.env.ANTHROPIC_MODEL}`);
1044
+ }
1045
+ if (settings.env.ANTHROPIC_SMALL_FAST_MODEL) {
1046
+ console.log(` ANTHROPIC_SMALL_FAST_MODEL: ${settings.env.ANTHROPIC_SMALL_FAST_MODEL}`);
1047
+ }
893
1048
  } else {
894
1049
  console.log(' (not configured)');
895
1050
  }
@@ -909,6 +1064,12 @@ function current(showSecret = false) {
909
1064
 
910
1065
  console.log(` ANTHROPIC_BASE_URL: ${baseUrl}`);
911
1066
  console.log(` ANTHROPIC_AUTH_TOKEN: ${maskedToken}`);
1067
+ if (envFile.ANTHROPIC_MODEL) {
1068
+ console.log(` ANTHROPIC_MODEL: ${envFile.ANTHROPIC_MODEL}`);
1069
+ }
1070
+ if (envFile.ANTHROPIC_SMALL_FAST_MODEL) {
1071
+ console.log(` ANTHROPIC_SMALL_FAST_MODEL: ${envFile.ANTHROPIC_SMALL_FAST_MODEL}`);
1072
+ }
912
1073
  } else {
913
1074
  console.log(' (not configured)');
914
1075
  }
@@ -927,6 +1088,12 @@ function current(showSecret = false) {
927
1088
 
928
1089
  console.log(` ANTHROPIC_BASE_URL: ${baseUrl}`);
929
1090
  console.log(` ANTHROPIC_AUTH_TOKEN: ${maskedToken}`);
1091
+ if (processEnv.ANTHROPIC_MODEL) {
1092
+ console.log(` ANTHROPIC_MODEL: ${processEnv.ANTHROPIC_MODEL}`);
1093
+ }
1094
+ if (processEnv.ANTHROPIC_SMALL_FAST_MODEL) {
1095
+ console.log(` ANTHROPIC_SMALL_FAST_MODEL: ${processEnv.ANTHROPIC_SMALL_FAST_MODEL}`);
1096
+ }
930
1097
  } else {
931
1098
  console.log(' (not set)');
932
1099
  }
@@ -1074,6 +1241,268 @@ function env(format = 'bash') {
1074
1241
  }
1075
1242
  }
1076
1243
 
1244
+ /**
1245
+ * Generate shell completion script
1246
+ */
1247
+ function completion(shell) {
1248
+ if (!shell) {
1249
+ console.error('Error: Missing shell type');
1250
+ console.error('Usage: ccconfig completion <bash|zsh|fish|powershell|pwsh>');
1251
+ console.error('');
1252
+ console.error('To install:');
1253
+ console.error(' Bash: ccconfig completion bash >> ~/.bashrc');
1254
+ console.error(' Zsh: ccconfig completion zsh >> ~/.zshrc');
1255
+ console.error(' Fish: ccconfig completion fish > ~/.config/fish/completions/ccconfig.fish');
1256
+ console.error(' PowerShell: ccconfig completion pwsh >> $PROFILE');
1257
+ process.exit(1);
1258
+ }
1259
+
1260
+ const commands = 'list ls add update use remove rm current mode env edit';
1261
+
1262
+ switch (shell) {
1263
+ case 'bash':
1264
+ console.log(`# ccconfig bash completion
1265
+ _ccconfig_completions() {
1266
+ local cur prev commands profiles
1267
+ COMPREPLY=()
1268
+ cur="\${COMP_WORDS[COMP_CWORD]}"
1269
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
1270
+ commands="${commands}"
1271
+
1272
+ # Get available profiles
1273
+ if [ -f ~/.config/ccconfig/profiles.json ]; then
1274
+ profiles=$(node -e "try { const data = require(process.env.HOME + '/.config/ccconfig/profiles.json'); console.log(Object.keys(data.profiles || {}).join(' ')); } catch(e) { }" 2>/dev/null)
1275
+ fi
1276
+
1277
+ case "\${COMP_CWORD}" in
1278
+ 1)
1279
+ COMPREPLY=( $(compgen -W "\${commands}" -- \${cur}) )
1280
+ ;;
1281
+ 2)
1282
+ case "\${prev}" in
1283
+ use|update|remove|rm)
1284
+ COMPREPLY=( $(compgen -W "\${profiles}" -- \${cur}) )
1285
+ ;;
1286
+ mode)
1287
+ COMPREPLY=( $(compgen -W "settings env" -- \${cur}) )
1288
+ ;;
1289
+ env)
1290
+ COMPREPLY=( $(compgen -W "bash zsh fish sh powershell pwsh dotenv" -- \${cur}) )
1291
+ ;;
1292
+ esac
1293
+ ;;
1294
+ 3)
1295
+ case "\${COMP_WORDS[1]}" in
1296
+ use)
1297
+ COMPREPLY=( $(compgen -W "--permanent -p" -- \${cur}) )
1298
+ ;;
1299
+ current)
1300
+ COMPREPLY=( $(compgen -W "--show-secret -s" -- \${cur}) )
1301
+ ;;
1302
+ esac
1303
+ ;;
1304
+ esac
1305
+ }
1306
+
1307
+ complete -F _ccconfig_completions ccconfig
1308
+ `);
1309
+ break;
1310
+
1311
+ case 'zsh':
1312
+ console.log(`# ccconfig zsh completion
1313
+ _ccconfig() {
1314
+ local -a commands profiles modes formats
1315
+ commands=(
1316
+ 'list:List all configurations'
1317
+ 'ls:List all configurations'
1318
+ 'add:Add new configuration'
1319
+ 'update:Update existing configuration'
1320
+ 'use:Switch to specified configuration'
1321
+ 'remove:Remove configuration'
1322
+ 'rm:Remove configuration'
1323
+ 'current:Display current configuration'
1324
+ 'mode:View or switch mode'
1325
+ 'env:Output environment variables'
1326
+ 'edit:Show configuration file location'
1327
+ )
1328
+
1329
+ modes=('settings' 'env')
1330
+ formats=('bash' 'zsh' 'fish' 'sh' 'powershell' 'pwsh' 'dotenv')
1331
+
1332
+ # Get available profiles
1333
+ if [ -f ~/.config/ccconfig/profiles.json ]; then
1334
+ profiles=($(node -e "try { const data = require(process.env.HOME + '/.config/ccconfig/profiles.json'); console.log(Object.keys(data.profiles || {}).join(' ')); } catch(e) { }" 2>/dev/null))
1335
+ fi
1336
+
1337
+ case $CURRENT in
1338
+ 2)
1339
+ _describe 'command' commands
1340
+ ;;
1341
+ 3)
1342
+ case $words[2] in
1343
+ use|update|remove|rm)
1344
+ _describe 'profile' profiles
1345
+ ;;
1346
+ mode)
1347
+ _describe 'mode' modes
1348
+ ;;
1349
+ env)
1350
+ _describe 'format' formats
1351
+ ;;
1352
+ esac
1353
+ ;;
1354
+ 4)
1355
+ case $words[2] in
1356
+ use)
1357
+ _arguments '-p[Write permanently to shell config]' '--permanent[Write permanently to shell config]'
1358
+ ;;
1359
+ current)
1360
+ _arguments '-s[Show full token]' '--show-secret[Show full token]'
1361
+ ;;
1362
+ esac
1363
+ ;;
1364
+ esac
1365
+ }
1366
+
1367
+ compdef _ccconfig ccconfig
1368
+ `);
1369
+ break;
1370
+
1371
+ case 'fish':
1372
+ console.log(`# ccconfig fish completion
1373
+
1374
+ # Commands
1375
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "list" -d "List all configurations"
1376
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "ls" -d "List all configurations"
1377
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "add" -d "Add new configuration"
1378
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "update" -d "Update existing configuration"
1379
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "use" -d "Switch to specified configuration"
1380
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "remove" -d "Remove configuration"
1381
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "rm" -d "Remove configuration"
1382
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "current" -d "Display current configuration"
1383
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "mode" -d "View or switch mode"
1384
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "env" -d "Output environment variables"
1385
+ complete -c ccconfig -f -n "__fish_use_subcommand" -a "edit" -d "Show configuration file location"
1386
+
1387
+ # Get profile names dynamically
1388
+ function __ccconfig_profiles
1389
+ if test -f ~/.config/ccconfig/profiles.json
1390
+ node -e "try { const data = require(process.env.HOME + '/.config/ccconfig/profiles.json'); Object.keys(data.profiles || {}).forEach(k => console.log(k)); } catch(e) { }" 2>/dev/null
1391
+ end
1392
+ end
1393
+
1394
+ # Profile name completion for use, update, remove
1395
+ complete -c ccconfig -f -n "__fish_seen_subcommand_from use update remove rm" -a "(__ccconfig_profiles)"
1396
+
1397
+ # Mode options
1398
+ complete -c ccconfig -f -n "__fish_seen_subcommand_from mode" -a "settings env"
1399
+
1400
+ # Env format options
1401
+ complete -c ccconfig -f -n "__fish_seen_subcommand_from env" -a "bash zsh fish sh powershell pwsh dotenv"
1402
+
1403
+ # Flags for use command
1404
+ complete -c ccconfig -f -n "__fish_seen_subcommand_from use" -s p -l permanent -d "Write permanently to shell config"
1405
+
1406
+ # Flags for current command
1407
+ complete -c ccconfig -f -n "__fish_seen_subcommand_from current" -s s -l show-secret -d "Show full token"
1408
+
1409
+ # Global flags
1410
+ complete -c ccconfig -f -s h -l help -d "Display help information"
1411
+ complete -c ccconfig -f -s V -l version -d "Display version information"
1412
+ `);
1413
+ break;
1414
+
1415
+ case 'powershell':
1416
+ case 'pwsh':
1417
+ console.log(`# ccconfig PowerShell completion
1418
+
1419
+ # Get available profiles
1420
+ function Get-CconfigProfiles {
1421
+ $profilesPath = Join-Path $env:USERPROFILE ".config\\ccconfig\\profiles.json"
1422
+ if (Test-Path $profilesPath) {
1423
+ try {
1424
+ $profiles = Get-Content $profilesPath | ConvertFrom-Json
1425
+ return $profiles.profiles.PSObject.Properties.Name
1426
+ } catch {
1427
+ return @()
1428
+ }
1429
+ }
1430
+ return @()
1431
+ }
1432
+
1433
+ # Register argument completer for ccconfig
1434
+ Register-ArgumentCompleter -Native -CommandName ccconfig -ScriptBlock {
1435
+ param($wordToComplete, $commandAst, $cursorPosition)
1436
+
1437
+ $commands = @('list', 'ls', 'add', 'update', 'use', 'remove', 'rm', 'current', 'mode', 'env', 'edit', 'completion')
1438
+ $modes = @('settings', 'env')
1439
+ $formats = @('bash', 'zsh', 'fish', 'sh', 'powershell', 'pwsh', 'dotenv')
1440
+
1441
+ # Parse the command line
1442
+ $tokens = $commandAst.ToString() -split '\\s+'
1443
+ $position = $tokens.Count - 1
1444
+
1445
+ # If we're completing the first argument (command)
1446
+ if ($position -eq 1 -or ($position -eq 2 -and $wordToComplete)) {
1447
+ $commands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1448
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
1449
+ }
1450
+ return
1451
+ }
1452
+
1453
+ # Get the command (first argument)
1454
+ $command = if ($tokens.Count -gt 1) { $tokens[1] } else { '' }
1455
+
1456
+ # Second argument completions based on command
1457
+ if ($position -eq 2 -or ($position -eq 3 -and $wordToComplete)) {
1458
+ switch ($command) {
1459
+ { $_ -in 'use', 'update', 'remove', 'rm' } {
1460
+ Get-CconfigProfiles | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1461
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
1462
+ }
1463
+ }
1464
+ 'mode' {
1465
+ $modes | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1466
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
1467
+ }
1468
+ }
1469
+ 'env' {
1470
+ $formats | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1471
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
1472
+ }
1473
+ }
1474
+ 'completion' {
1475
+ @('bash', 'zsh', 'fish', 'powershell', 'pwsh') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1476
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
1477
+ }
1478
+ }
1479
+ }
1480
+ return
1481
+ }
1482
+
1483
+ # Flag completions
1484
+ if ($position -ge 3 -and $command -eq 'use') {
1485
+ @('-p', '--permanent') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1486
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', 'Write permanently to shell config')
1487
+ }
1488
+ }
1489
+
1490
+ if ($position -ge 2 -and $command -eq 'current') {
1491
+ @('-s', '--show-secret') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
1492
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', 'Show full token')
1493
+ }
1494
+ }
1495
+ }
1496
+ `);
1497
+ break;
1498
+
1499
+ default:
1500
+ console.error(`Error: Unsupported shell: ${shell}`);
1501
+ console.error('Supported shells: bash, zsh, fish, powershell, pwsh');
1502
+ process.exit(1);
1503
+ }
1504
+ }
1505
+
1077
1506
  /**
1078
1507
  * Display help information
1079
1508
  */
@@ -1102,6 +1531,8 @@ function help() {
1102
1531
  ' list|ls List all configurations (default)');
1103
1532
  console.log(
1104
1533
  ' add [name] Add new configuration (interactive)');
1534
+ console.log(
1535
+ ' update [name] Update existing configuration (interactive)');
1105
1536
  console.log(
1106
1537
  ' use <name> [-p|--permanent] Switch to specified configuration');
1107
1538
  console.log(
@@ -1114,6 +1545,8 @@ function help() {
1114
1545
  ' env [format] Output environment variables (env mode)');
1115
1546
  console.log(
1116
1547
  ' edit Show configuration file location');
1548
+ console.log(
1549
+ ' completion <bash|zsh|fish|pwsh> Generate shell completion script');
1117
1550
  console.log('');
1118
1551
  console.log('Flags:');
1119
1552
  console.log(
@@ -1170,6 +1603,9 @@ async function main() {
1170
1603
  case 'add':
1171
1604
  await add(filteredArgs[1]);
1172
1605
  break;
1606
+ case 'update':
1607
+ await update(filteredArgs[1]);
1608
+ break;
1173
1609
  case 'remove':
1174
1610
  case 'rm':
1175
1611
  remove(filteredArgs[1]);
@@ -1186,6 +1622,9 @@ async function main() {
1186
1622
  case 'edit':
1187
1623
  edit();
1188
1624
  break;
1625
+ case 'completion':
1626
+ completion(filteredArgs[1]);
1627
+ break;
1189
1628
  default:
1190
1629
  if (!command) {
1191
1630
  list();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccconfig",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Cross-platform Claude Code configuration switching tool",
5
5
  "main": "ccconfig.js",
6
6
  "bin": {