@daylenjeez/ccm-switch 1.2.1 → 1.2.3

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
@@ -10,7 +10,7 @@ Switch Claude Code custom model configurations from the terminal in seconds.
10
10
  [![license](https://img.shields.io/npm/l/@daylenjeez/ccm-switch.svg?style=flat-square)](https://github.com/daylenjeez/ccm-switch/blob/main/LICENSE)
11
11
  [![node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
12
12
 
13
- [中文文档](./README.zh-CN.md) | English
13
+ [中文文档](https://github.com/daylenjeez/ccm-switch/blob/main/README.zh-CN.md) | English
14
14
 
15
15
  [Install](#-install) · [Quick Start](#-quick-start) · [Commands](#-commands) · [How It Works](#%EF%B8%8F-how-it-works)
16
16
 
@@ -52,17 +52,17 @@ ccm add # Interactive wizard to add a provider
52
52
 
53
53
  ## 🔌 cc-switch Integration
54
54
 
55
- Already using [cc-switch](https://github.com/farion1231/cc-switch)? ccm reads its SQLite database directly:
55
+ Already using [cc-switch](https://github.com/farion1231/cc-switch)? ccm can sync its configurations into standalone storage:
56
56
 
57
57
  ```bash
58
58
  $ ccm init
59
59
  cc-switch detected. Import configurations from it? (Y/n)
60
- ✓ Initialized in cc-switch mode
61
- Imported 4 configurations
60
+ ✓ Initialized
61
+ Synced 4 configurations
62
62
  Active: OpenRouter
63
63
  ```
64
64
 
65
- All configs sync both ways add in ccm, see it in cc-switch UI, and vice versa.
65
+ You can also run `ccm sync` at any time to pull the latest cc-switch configurations into `~/.ccm/config.json`.
66
66
 
67
67
  ## ➕ Adding Configurations
68
68
 
@@ -132,7 +132,9 @@ Aliases are stored in `~/.ccm/rc.json`:
132
132
  | `ccm modify [name]` | `edit` | Edit existing configuration |
133
133
  | `ccm remove [name]` | `rm` | Interactive or named delete |
134
134
  | `ccm current` | | Show active configuration |
135
- | `ccm config` | | Switch storage mode |
135
+ | `ccm config` | | View data source mode (deprecated) |
136
+ | `ccm sync` | | Sync cc-switch configs into standalone |
137
+ | `ccm clear` | | Clean up data files |
136
138
 
137
139
  ### Aliases
138
140
 
@@ -190,6 +192,18 @@ Claude Code reads `~/.claude/settings.json` on startup. The `env` field controls
190
192
 
191
193
  `ccm use` writes the selected profile into `settings.json` while preserving personal settings (`language`, `permissions`, etc.). Restart Claude Code to apply.
192
194
 
195
+ ## 🗑️ Uninstall
196
+
197
+ Removing `ccm` only removes the CLI itself. Your data files are left behind.
198
+
199
+ Use `ccm clear` to delete them automatically, or clean them up manually:
200
+
201
+ - `~/.ccm/rc.json` — aliases and locale
202
+ - `~/.ccm/config.json` — profiles
203
+ - `~/.claude/settings.json` — may still contain an active `env` profile written by ccm
204
+
205
+ > Do **not** delete `~/.cc-switch/cc-switch.db` unless you are also uninstalling cc-switch; that database is owned by cc-switch.
206
+
193
207
  ## 📄 License
194
208
 
195
209
  [MIT](./LICENSE)
package/README.zh-CN.md CHANGED
@@ -10,7 +10,7 @@
10
10
  [![license](https://img.shields.io/npm/l/@daylenjeez/ccm-switch.svg?style=flat-square)](https://github.com/daylenjeez/ccm-switch/blob/main/LICENSE)
11
11
  [![node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
12
12
 
13
- English | [中文文档](./README.zh-CN.md)
13
+ English | [中文文档](https://github.com/daylenjeez/ccm-switch/blob/main/README.zh-CN.md)
14
14
 
15
15
  [安装](#-安装) · [快速开始](#-快速开始) · [命令一览](#-命令一览) · [工作原理](#%EF%B8%8F-工作原理)
16
16
 
@@ -52,17 +52,17 @@ ccm add # 交互式向导添加供应商
52
52
 
53
53
  ## 🔌 cc-switch 集成
54
54
 
55
- 已经在用 [cc-switch](https://github.com/farion1231/cc-switch)?ccm 直接读取它的 SQLite 数据库:
55
+ 已经在用 [cc-switch](https://github.com/farion1231/cc-switch)?ccm 可以将它的配置同步到本地:
56
56
 
57
57
  ```bash
58
58
  $ ccm init
59
59
  检测到 cc-switch 已安装,是否从中导入配置?(Y/n)
60
- 已初始化为 cc-switch 模式
61
- 已导入 4 个配置
60
+ 初始化完成
61
+ 已同步 4 个配置
62
62
  当前激活: OpenRouter
63
63
  ```
64
64
 
65
- 双向同步 ccm 中添加的配置在 cc-switch UI 中也能看到,反之亦然。
65
+ 你也可以随时运行 `ccm sync` 将最新的 cc-switch 配置同步到 `~/.ccm/config.json`。
66
66
 
67
67
  ## ➕ 添加配置
68
68
 
@@ -132,7 +132,9 @@ ANTHROPIC_DEFAULT_HAIKU_MODEL (可选):
132
132
  | `ccm modify [name]` | `edit` | 修改已有配置 |
133
133
  | `ccm remove [name]` | `rm` | 交互式或指定名称删除 |
134
134
  | `ccm current` | | 显示当前激活配置 |
135
- | `ccm config` | | 切换存储模式 |
135
+ | `ccm config` | | 查看数据源模式(已废弃) |
136
+ | `ccm sync` | | 从 cc-switch 同步配置到本地 |
137
+ | `ccm clear` | | 清理数据文件 |
136
138
 
137
139
  ### 别名管理
138
140
 
@@ -190,6 +192,18 @@ Claude Code 启动时读取 `~/.claude/settings.json`,`env` 字段控制 API
190
192
 
191
193
  `ccm use` 将选中配置写入 `settings.json`,同时保留 `language`、`permissions` 等个人设置。重启 Claude Code 后生效。
192
194
 
195
+ ## 🗑️ 卸载
196
+
197
+ 卸载 `ccm` 只会删除 CLI 程序本身,相关的数据文件仍然保留。
198
+
199
+ 你可以先运行 `ccm clear` 自动清理数据文件,也可以手动删除:
200
+
201
+ - `~/.ccm/rc.json` — 别名和语言设置
202
+ - `~/.ccm/config.json` — 配置方案
203
+ - `~/.claude/settings.json` — 可能仍包含 ccm 写入的 `env` 配置
204
+
205
+ > 除非你也卸载了 cc-switch,否则**不要**删除 `~/.cc-switch/cc-switch.db`;该数据库由 cc-switch 管理。
206
+
193
207
  ## 📄 License
194
208
 
195
209
  [MIT](./LICENSE)
package/dist/claude.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export declare function readClaudeSettings(): Record<string, unknown>;
2
2
  export declare function applyProfile(settingsConfig: Record<string, unknown>): void;
3
+ export declare function clearEnvFromSettings(): void;
3
4
  export declare function getSettingsPath(): string;
package/dist/claude.js CHANGED
@@ -20,6 +20,15 @@ export function applyProfile(settingsConfig) {
20
20
  const merged = { ...preserved, ...settingsConfig };
21
21
  writeFileSync(SETTINGS_PATH, JSON.stringify(merged, null, 2));
22
22
  }
23
+ export function clearEnvFromSettings() {
24
+ if (!existsSync(SETTINGS_PATH))
25
+ return;
26
+ const current = readClaudeSettings();
27
+ if (!("env" in current))
28
+ return;
29
+ delete current.env;
30
+ writeFileSync(SETTINGS_PATH, JSON.stringify(current, null, 2));
31
+ }
23
32
  export function getSettingsPath() {
24
33
  return SETTINGS_PATH;
25
34
  }
package/dist/i18n/en.js CHANGED
@@ -17,24 +17,15 @@ const en = {
17
17
  // init
18
18
  "init.description": "Initialize ccm",
19
19
  "init.cc_switch_found": "cc-switch detected. Import configurations from it? (Y/n) ",
20
- "init.imported": "✓ Imported {count} configurations",
21
- "init.current": "Active: {name}",
22
- "init.no_current": "No active configuration",
23
- "init.done_cc_switch": "✓ Initialized in cc-switch mode",
24
- "init.done_standalone": "✓ Initialized in standalone mode",
20
+ "init.done": "✓ Initialized",
25
21
  // config
26
- "config.description": "View or switch data source mode",
27
- "config.current_mode": "Current mode: {mode}",
28
- "config.switch_confirm": "Switch mode? (y/N) ",
29
- "config.cc_switch_not_installed": "cc-switch is not installed",
30
- "config.switched": "✓ Switched to {mode} mode",
22
+ "config.description": "View data source mode (deprecated)",
31
23
  // list
32
24
  "list.description": "List and select configurations",
33
25
  "list.empty": "No configurations yet. Use ccm save <name> to save current config",
34
26
  "list.header": "Available configurations:",
35
27
  "list.select": "Select configuration:",
36
28
  "list.current_marker": "(current)",
37
- "list.cancelled": "Cancelled",
38
29
  "list.choose_number": "Enter number to switch (Enter to skip): ",
39
30
  // current
40
31
  "current.description": "Show the currently active configuration",
@@ -118,6 +109,21 @@ const en = {
118
109
  "alias.rm_alias": "Alias {name}",
119
110
  "alias.rm_config": "Config {target}",
120
111
  "alias.rm_choose": "Choose (1/2): ",
112
+ // sync
113
+ "sync.description": "Sync configurations from cc-switch",
114
+ "sync.no_cc_switch": "cc-switch database not detected",
115
+ "sync.empty": "No Claude configurations found in cc-switch",
116
+ "sync.done": "✓ Synced {count} configurations",
117
+ "sync.current": "Active: {name}",
118
+ "sync.no_current": "No active configuration",
119
+ // clear
120
+ "clear.description": "Clean up ccm data files",
121
+ "clear.confirm": "Delete all ccm data files? (y/N) ",
122
+ "clear.cancelled": "Cancelled",
123
+ "clear.removed": "✓ Deleted {path}",
124
+ "clear.clear_env": "Also clear env config in ~/.claude/settings.json? (y/N) ",
125
+ "clear.env_cleared": "✓ Cleared env config",
126
+ "clear.done": "✓ Cleanup complete",
121
127
  // store errors
122
128
  "store.db_not_found": "cc-switch database not found: {path}",
123
129
  };
package/dist/i18n/zh.d.ts CHANGED
@@ -12,22 +12,13 @@ declare const zh: {
12
12
  readonly "suggest.use_list": "使用 ccm list 查看所有可用配置";
13
13
  readonly "init.description": "初始化 ccm";
14
14
  readonly "init.cc_switch_found": "检测到 cc-switch 已安装,是否从中导入配置?(Y/n) ";
15
- readonly "init.imported": "✓ 已导入 {count} 个配置";
16
- readonly "init.current": "当前激活: {name}";
17
- readonly "init.no_current": "当前无激活配置";
18
- readonly "init.done_cc_switch": "✓ 已初始化为 cc-switch 模式";
19
- readonly "init.done_standalone": "✓ 已初始化为独立模式";
20
- readonly "config.description": "查看或切换数据源模式";
21
- readonly "config.current_mode": "当前模式: {mode}";
22
- readonly "config.switch_confirm": "是否切换模式?(y/N) ";
23
- readonly "config.cc_switch_not_installed": "cc-switch 未安装";
24
- readonly "config.switched": "✓ 已切换为 {mode} 模式";
15
+ readonly "init.done": "✓ 初始化完成";
16
+ readonly "config.description": "查看数据源模式(已废弃)";
25
17
  readonly "list.description": "列出并选择配置方案";
26
18
  readonly "list.empty": "暂无配置方案。使用 ccm save <name> 保存当前配置";
27
19
  readonly "list.header": "可用配置:";
28
20
  readonly "list.select": "选择配置:";
29
21
  readonly "list.current_marker": "(当前)";
30
- readonly "list.cancelled": "已取消";
31
22
  readonly "list.choose_number": "输入序号切换 (回车跳过): ";
32
23
  readonly "current.description": "显示当前生效的配置";
33
24
  readonly "current.none": "当前无激活配置";
@@ -101,6 +92,19 @@ declare const zh: {
101
92
  readonly "alias.rm_alias": "别名 {name}";
102
93
  readonly "alias.rm_config": "配置 {target}";
103
94
  readonly "alias.rm_choose": "请选择 (1/2): ";
95
+ readonly "sync.description": "从 cc-switch 同步配置";
96
+ readonly "sync.no_cc_switch": "未检测到 cc-switch 数据库";
97
+ readonly "sync.empty": "cc-switch 中没有找到 Claude 配置";
98
+ readonly "sync.done": "✓ 已同步 {count} 个配置";
99
+ readonly "sync.current": "当前激活: {name}";
100
+ readonly "sync.no_current": "当前无激活配置";
101
+ readonly "clear.description": "清理 ccm 数据文件";
102
+ readonly "clear.confirm": "确认删除所有 ccm 数据文件?(y/N) ";
103
+ readonly "clear.cancelled": "已取消清理";
104
+ readonly "clear.removed": "✓ 已删除 {path}";
105
+ readonly "clear.clear_env": "是否同时清除 ~/.claude/settings.json 中的 env 配置?(y/N) ";
106
+ readonly "clear.env_cleared": "✓ 已清除 env 配置";
107
+ readonly "clear.done": "✓ 清理完成";
104
108
  readonly "store.db_not_found": "cc-switch 数据库不存在: {path}";
105
109
  };
106
110
  export type TranslationKey = keyof typeof zh;
package/dist/i18n/zh.js CHANGED
@@ -17,24 +17,15 @@ const zh = {
17
17
  // init
18
18
  "init.description": "初始化 ccm",
19
19
  "init.cc_switch_found": "检测到 cc-switch 已安装,是否从中导入配置?(Y/n) ",
20
- "init.imported": "✓ 已导入 {count} 个配置",
21
- "init.current": "当前激活: {name}",
22
- "init.no_current": "当前无激活配置",
23
- "init.done_cc_switch": "✓ 已初始化为 cc-switch 模式",
24
- "init.done_standalone": "✓ 已初始化为独立模式",
20
+ "init.done": "✓ 初始化完成",
25
21
  // config
26
- "config.description": "查看或切换数据源模式",
27
- "config.current_mode": "当前模式: {mode}",
28
- "config.switch_confirm": "是否切换模式?(y/N) ",
29
- "config.cc_switch_not_installed": "cc-switch 未安装",
30
- "config.switched": "✓ 已切换为 {mode} 模式",
22
+ "config.description": "查看数据源模式(已废弃)",
31
23
  // list
32
24
  "list.description": "列出并选择配置方案",
33
25
  "list.empty": "暂无配置方案。使用 ccm save <name> 保存当前配置",
34
26
  "list.header": "可用配置:",
35
27
  "list.select": "选择配置:",
36
28
  "list.current_marker": "(当前)",
37
- "list.cancelled": "已取消",
38
29
  "list.choose_number": "输入序号切换 (回车跳过): ",
39
30
  // current
40
31
  "current.description": "显示当前生效的配置",
@@ -118,6 +109,21 @@ const zh = {
118
109
  "alias.rm_alias": "别名 {name}",
119
110
  "alias.rm_config": "配置 {target}",
120
111
  "alias.rm_choose": "请选择 (1/2): ",
112
+ // sync
113
+ "sync.description": "从 cc-switch 同步配置",
114
+ "sync.no_cc_switch": "未检测到 cc-switch 数据库",
115
+ "sync.empty": "cc-switch 中没有找到 Claude 配置",
116
+ "sync.done": "✓ 已同步 {count} 个配置",
117
+ "sync.current": "当前激活: {name}",
118
+ "sync.no_current": "当前无激活配置",
119
+ // clear
120
+ "clear.description": "清理 ccm 数据文件",
121
+ "clear.confirm": "确认删除所有 ccm 数据文件?(y/N) ",
122
+ "clear.cancelled": "已取消清理",
123
+ "clear.removed": "✓ 已删除 {path}",
124
+ "clear.clear_env": "是否同时清除 ~/.claude/settings.json 中的 env 配置?(y/N) ",
125
+ "clear.env_cleared": "✓ 已清除 env 配置",
126
+ "clear.done": "✓ 清理完成",
121
127
  // store errors
122
128
  "store.db_not_found": "cc-switch 数据库不存在: {path}",
123
129
  };
package/dist/index.js CHANGED
@@ -3,11 +3,11 @@ import { Command } from "commander";
3
3
  import chalk from "chalk";
4
4
  import { readRc, writeRc, getStore } from "./utils.js";
5
5
  import { ccSwitchExists } from "./store/cc-switch.js";
6
- import { readClaudeSettings, applyProfile } from "./claude.js";
6
+ import { readClaudeSettings, applyProfile, clearEnvFromSettings, getSettingsPath } from "./claude.js";
7
7
  import { createInterface } from "readline";
8
8
  import { spawnSync } from "child_process";
9
- import { writeFileSync, readFileSync, unlinkSync } from "fs";
10
- import { tmpdir } from "os";
9
+ import { writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
10
+ import { tmpdir, homedir } from "os";
11
11
  import { join } from "path";
12
12
  import { t, setLocale } from "./i18n/index.js";
13
13
  import * as clack from "@clack/prompts";
@@ -26,14 +26,9 @@ function ask(question) {
26
26
  });
27
27
  });
28
28
  }
29
- // Helper: ensure initialized
29
+ // Helper: ensure store ready
30
30
  function ensureStore() {
31
- const store = getStore();
32
- if (!store) {
33
- console.log(chalk.yellow(t("common.not_init")));
34
- process.exit(1);
35
- }
36
- return store;
31
+ return getStore();
37
32
  }
38
33
  // Helper: format env for display
39
34
  function formatEnv(env) {
@@ -154,49 +149,109 @@ program
154
149
  .command("init")
155
150
  .description(t("init.description"))
156
151
  .action(async () => {
152
+ const rc = readRc();
153
+ writeRc({ aliases: rc?.aliases, locale: rc?.locale });
154
+ console.log(chalk.green(t("init.done")));
157
155
  if (ccSwitchExists()) {
158
156
  const use = await ask(t("init.cc_switch_found"));
159
157
  if (use.toLowerCase() !== "n") {
160
- writeRc({ mode: "cc-switch" });
161
158
  const { CcSwitchStore } = await import("./store/cc-switch.js");
162
- const store = new CcSwitchStore();
163
- const profiles = store.list();
164
- const current = store.getCurrent();
165
- console.log(chalk.green(t("init.done_cc_switch")));
166
- console.log(chalk.green(t("init.imported", { count: String(profiles.length) })));
159
+ const { StandaloneStore } = await import("./store/standalone.js");
160
+ const ccStore = new CcSwitchStore();
161
+ const standaloneStore = new StandaloneStore();
162
+ const profiles = ccStore.list();
163
+ const current = ccStore.getCurrent();
164
+ for (const profile of profiles) {
165
+ standaloneStore.save(profile.name, profile.settingsConfig);
166
+ }
167
+ if (current) {
168
+ standaloneStore.setCurrent(current);
169
+ }
170
+ console.log(chalk.green(t("sync.done", { count: String(profiles.length) })));
167
171
  if (current) {
168
- console.log(chalk.gray(t("init.current", { name: current })));
172
+ console.log(chalk.gray(t("sync.current", { name: current })));
169
173
  }
170
174
  else {
171
- console.log(chalk.gray(t("init.no_current")));
175
+ console.log(chalk.gray(t("sync.no_current")));
172
176
  }
173
- return;
177
+ ccStore.close();
174
178
  }
175
179
  }
176
- writeRc({ mode: "standalone" });
177
- console.log(chalk.green(t("init.done_standalone")));
178
180
  });
179
- // ccm config
181
+ // ccm config (deprecated, kept for compatibility)
180
182
  program
181
183
  .command("config")
182
184
  .description(t("config.description"))
183
185
  .action(async () => {
184
- const rc = readRc();
185
- if (!rc) {
186
- console.log(chalk.yellow(t("common.not_init")));
186
+ console.log(chalk.gray("ccm now only uses standalone mode. No configuration needed."));
187
+ });
188
+ // ccm sync
189
+ program
190
+ .command("sync")
191
+ .description(t("sync.description"))
192
+ .action(async () => {
193
+ if (!ccSwitchExists()) {
194
+ console.log(chalk.red(t("sync.no_cc_switch")));
187
195
  return;
188
196
  }
189
- console.log(t("config.current_mode", { mode: chalk.cyan(rc.mode) }));
190
- const confirm = await ask(t("config.switch_confirm"));
191
- if (confirm.toLowerCase() === "y") {
192
- const newMode = rc.mode === "cc-switch" ? "standalone" : "cc-switch";
193
- if (newMode === "cc-switch" && !ccSwitchExists()) {
194
- console.log(chalk.red(t("config.cc_switch_not_installed")));
195
- return;
197
+ const { CcSwitchStore } = await import("./store/cc-switch.js");
198
+ const { StandaloneStore } = await import("./store/standalone.js");
199
+ const ccStore = new CcSwitchStore();
200
+ const standaloneStore = new StandaloneStore();
201
+ const profiles = ccStore.list();
202
+ if (profiles.length === 0) {
203
+ console.log(chalk.yellow(t("sync.empty")));
204
+ ccStore.close();
205
+ return;
206
+ }
207
+ for (const profile of profiles) {
208
+ standaloneStore.save(profile.name, profile.settingsConfig);
209
+ }
210
+ const current = ccStore.getCurrent();
211
+ if (current) {
212
+ standaloneStore.setCurrent(current);
213
+ }
214
+ console.log(chalk.green(t("sync.done", { count: String(profiles.length) })));
215
+ if (current) {
216
+ console.log(chalk.gray(t("sync.current", { name: current })));
217
+ }
218
+ else {
219
+ console.log(chalk.gray(t("sync.no_current")));
220
+ }
221
+ ccStore.close();
222
+ });
223
+ // ccm clear
224
+ program
225
+ .command("clear")
226
+ .description(t("clear.description"))
227
+ .action(async () => {
228
+ const confirm = await ask(t("clear.confirm"));
229
+ if (confirm.toLowerCase() !== "y") {
230
+ console.log(chalk.gray(t("clear.cancelled")));
231
+ return;
232
+ }
233
+ const rcPath = join(homedir(), ".ccm", "rc.json");
234
+ const configPath = join(homedir(), ".ccm", "config.json");
235
+ if (existsSync(configPath)) {
236
+ unlinkSync(configPath);
237
+ console.log(chalk.green(t("clear.removed", { path: configPath })));
238
+ }
239
+ if (existsSync(rcPath)) {
240
+ unlinkSync(rcPath);
241
+ console.log(chalk.green(t("clear.removed", { path: rcPath })));
242
+ }
243
+ const settingsPath = getSettingsPath();
244
+ if (existsSync(settingsPath)) {
245
+ const settings = readClaudeSettings();
246
+ if ("env" in settings) {
247
+ const clearEnv = await ask(t("clear.clear_env"));
248
+ if (clearEnv.toLowerCase() === "y") {
249
+ clearEnvFromSettings();
250
+ console.log(chalk.green(t("clear.env_cleared")));
251
+ }
196
252
  }
197
- writeRc({ mode: newMode });
198
- console.log(chalk.green(t("config.switched", { mode: newMode })));
199
253
  }
254
+ console.log(chalk.green(t("clear.done")));
200
255
  });
201
256
  // ccm list
202
257
  program
@@ -245,8 +300,7 @@ program
245
300
  initialValue: initial >= 0 ? profiles[initial].name : profiles[0].name,
246
301
  });
247
302
  if (clack.isCancel(selected)) {
248
- clack.cancel(t("list.cancelled"));
249
- return;
303
+ process.exit(0);
250
304
  }
251
305
  switchTo(selected);
252
306
  }
@@ -528,8 +582,7 @@ program
528
582
  options,
529
583
  });
530
584
  if (clack.isCancel(selected)) {
531
- clack.cancel(t("list.cancelled"));
532
- return;
585
+ process.exit(0);
533
586
  }
534
587
  name = selected;
535
588
  }
@@ -675,8 +728,7 @@ program
675
728
  options,
676
729
  });
677
730
  if (clack.isCancel(selected)) {
678
- clack.cancel(t("list.cancelled"));
679
- return;
731
+ process.exit(0);
680
732
  }
681
733
  name = selected;
682
734
  }
@@ -809,10 +861,6 @@ const SUPPORTED_LOCALES = [
809
861
  ];
810
862
  const switchLocale = (code) => {
811
863
  const rc = readRc();
812
- if (!rc) {
813
- console.log(chalk.yellow(t("common.not_init")));
814
- return;
815
- }
816
864
  rc.locale = code;
817
865
  writeRc(rc);
818
866
  setLocale(code);
@@ -837,7 +885,9 @@ localeCmd
837
885
  options,
838
886
  initialValue: current,
839
887
  });
840
- if (clack.isCancel(selected) || selected === current)
888
+ if (clack.isCancel(selected))
889
+ process.exit(0);
890
+ if (selected === current)
841
891
  return;
842
892
  switchLocale(selected);
843
893
  }
@@ -46,10 +46,17 @@ export class CcSwitchStore {
46
46
  .run(JSON.stringify(settingsConfig), name);
47
47
  }
48
48
  else {
49
+ const maxSort = this.db
50
+ .prepare(`SELECT COALESCE(MAX(sort_index), -1) as max_sort FROM providers WHERE app_type = 'claude'`)
51
+ .get();
52
+ const sortIndex = (maxSort?.max_sort ?? -1) + 1;
49
53
  const id = crypto.randomUUID();
50
54
  this.db
51
- .prepare(`INSERT INTO providers (id, app_type, name, settings_config, meta, created_at) VALUES (?, 'claude', ?, ?, '{}', ?)`)
52
- .run(id, name, JSON.stringify(settingsConfig), Date.now());
55
+ .prepare(`INSERT INTO providers (
56
+ id, app_type, name, settings_config, website_url, category,
57
+ created_at, sort_index, notes, icon, icon_color, meta, is_current, in_failover_queue
58
+ ) VALUES (?, 'claude', ?, ?, NULL, NULL, ?, ?, NULL, NULL, NULL, '{}', 0, 0)`)
59
+ .run(id, name, JSON.stringify(settingsConfig), Date.now(), sortIndex);
53
60
  }
54
61
  }
55
62
  remove(name) {
@@ -59,6 +66,13 @@ export class CcSwitchStore {
59
66
  return result.changes > 0;
60
67
  }
61
68
  getCurrent() {
69
+ // Prefer DB is_current so we stay in sync with cc-switch UI
70
+ const dbRow = this.db
71
+ .prepare(`SELECT name FROM providers WHERE app_type = 'claude' AND is_current = 1 LIMIT 1`)
72
+ .get();
73
+ if (dbRow)
74
+ return dbRow.name;
75
+ // Fallback to settings.json
62
76
  if (!existsSync(SETTINGS_PATH))
63
77
  return undefined;
64
78
  try {
@@ -79,6 +93,15 @@ export class CcSwitchStore {
79
93
  const profile = this.get(name);
80
94
  if (!profile)
81
95
  throw new Error(t("error.not_found", { name }));
96
+ const tx = this.db.transaction(() => {
97
+ this.db
98
+ .prepare(`UPDATE providers SET is_current = 0 WHERE app_type = 'claude'`)
99
+ .run();
100
+ this.db
101
+ .prepare(`UPDATE providers SET is_current = 1 WHERE app_type = 'claude' AND id = ?`)
102
+ .run(profile.id);
103
+ });
104
+ tx();
82
105
  if (existsSync(SETTINGS_PATH)) {
83
106
  const settings = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
84
107
  settings.currentProviderClaude = profile.id;
package/dist/types.d.ts CHANGED
@@ -12,7 +12,6 @@ export interface DataStore {
12
12
  setCurrent(name: string): void;
13
13
  }
14
14
  export interface RcConfig {
15
- mode: "cc-switch" | "standalone";
16
15
  aliases?: Record<string, string>;
17
16
  locale?: "zh" | "en";
18
17
  }
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { RcConfig } from "./types.js";
2
2
  import type { DataStore } from "./store/interface.js";
3
- export declare function readRc(): RcConfig | undefined;
3
+ export declare function readRc(): RcConfig;
4
4
  export declare function writeRc(rc: RcConfig): void;
5
- export declare function getStore(): DataStore | null;
5
+ export declare function getStore(): DataStore;
package/dist/utils.js CHANGED
@@ -1,18 +1,20 @@
1
1
  import { homedir } from "os";
2
2
  import { join } from "path";
3
3
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
- import { CcSwitchStore } from "./store/cc-switch.js";
5
4
  import { StandaloneStore } from "./store/standalone.js";
6
5
  const CCM_DIR = join(homedir(), ".ccm");
7
6
  const RC_PATH = join(CCM_DIR, "rc.json");
8
7
  export function readRc() {
9
- if (!existsSync(RC_PATH))
10
- return undefined;
8
+ if (!existsSync(RC_PATH)) {
9
+ writeRc({});
10
+ return {};
11
+ }
11
12
  try {
12
13
  return JSON.parse(readFileSync(RC_PATH, "utf-8"));
13
14
  }
14
15
  catch {
15
- return undefined;
16
+ writeRc({});
17
+ return {};
16
18
  }
17
19
  }
18
20
  export function writeRc(rc) {
@@ -22,13 +24,5 @@ export function writeRc(rc) {
22
24
  writeFileSync(RC_PATH, JSON.stringify(rc, null, 2));
23
25
  }
24
26
  export function getStore() {
25
- const rc = readRc();
26
- if (!rc)
27
- return null;
28
- if (rc.mode === "cc-switch") {
29
- return new CcSwitchStore();
30
- }
31
- else {
32
- return new StandaloneStore();
33
- }
27
+ return new StandaloneStore();
34
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daylenjeez/ccm-switch",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Claude Code Model Switcher - 快速切换 Claude Code 自定义模型配置",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/claude.ts CHANGED
@@ -25,6 +25,14 @@ export function applyProfile(settingsConfig: Record<string, unknown>): void {
25
25
  writeFileSync(SETTINGS_PATH, JSON.stringify(merged, null, 2));
26
26
  }
27
27
 
28
+ export function clearEnvFromSettings(): void {
29
+ if (!existsSync(SETTINGS_PATH)) return;
30
+ const current = readClaudeSettings();
31
+ if (!("env" in current)) return;
32
+ delete current.env;
33
+ writeFileSync(SETTINGS_PATH, JSON.stringify(current, null, 2));
34
+ }
35
+
28
36
  export function getSettingsPath(): string {
29
37
  return SETTINGS_PATH;
30
38
  }
package/src/i18n/en.ts CHANGED
@@ -23,18 +23,10 @@ const en: Record<TranslationKey, string> = {
23
23
  // init
24
24
  "init.description": "Initialize ccm",
25
25
  "init.cc_switch_found":"cc-switch detected. Import configurations from it? (Y/n) ",
26
- "init.imported": "✓ Imported {count} configurations",
27
- "init.current": "Active: {name}",
28
- "init.no_current": "No active configuration",
29
- "init.done_cc_switch": "✓ Initialized in cc-switch mode",
30
- "init.done_standalone": "✓ Initialized in standalone mode",
26
+ "init.done": "✓ Initialized",
31
27
 
32
28
  // config
33
- "config.description": "View or switch data source mode",
34
- "config.current_mode": "Current mode: {mode}",
35
- "config.switch_confirm": "Switch mode? (y/N) ",
36
- "config.cc_switch_not_installed": "cc-switch is not installed",
37
- "config.switched": "✓ Switched to {mode} mode",
29
+ "config.description": "View data source mode (deprecated)",
38
30
 
39
31
  // list
40
32
  "list.description": "List and select configurations",
@@ -42,7 +34,6 @@ const en: Record<TranslationKey, string> = {
42
34
  "list.header": "Available configurations:",
43
35
  "list.select": "Select configuration:",
44
36
  "list.current_marker": "(current)",
45
- "list.cancelled": "Cancelled",
46
37
  "list.choose_number": "Enter number to switch (Enter to skip): ",
47
38
 
48
39
  // current
@@ -137,6 +128,23 @@ const en: Record<TranslationKey, string> = {
137
128
  "alias.rm_config": "Config {target}",
138
129
  "alias.rm_choose": "Choose (1/2): ",
139
130
 
131
+ // sync
132
+ "sync.description": "Sync configurations from cc-switch",
133
+ "sync.no_cc_switch": "cc-switch database not detected",
134
+ "sync.empty": "No Claude configurations found in cc-switch",
135
+ "sync.done": "✓ Synced {count} configurations",
136
+ "sync.current": "Active: {name}",
137
+ "sync.no_current": "No active configuration",
138
+
139
+ // clear
140
+ "clear.description": "Clean up ccm data files",
141
+ "clear.confirm": "Delete all ccm data files? (y/N) ",
142
+ "clear.cancelled": "Cancelled",
143
+ "clear.removed": "✓ Deleted {path}",
144
+ "clear.clear_env": "Also clear env config in ~/.claude/settings.json? (y/N) ",
145
+ "clear.env_cleared": "✓ Cleared env config",
146
+ "clear.done": "✓ Cleanup complete",
147
+
140
148
  // store errors
141
149
  "store.db_not_found": "cc-switch database not found: {path}",
142
150
  };
package/src/i18n/zh.ts CHANGED
@@ -21,18 +21,10 @@ const zh = {
21
21
  // init
22
22
  "init.description": "初始化 ccm",
23
23
  "init.cc_switch_found":"检测到 cc-switch 已安装,是否从中导入配置?(Y/n) ",
24
- "init.imported": "✓ 已导入 {count} 个配置",
25
- "init.current": "当前激活: {name}",
26
- "init.no_current": "当前无激活配置",
27
- "init.done_cc_switch": "✓ 已初始化为 cc-switch 模式",
28
- "init.done_standalone": "✓ 已初始化为独立模式",
24
+ "init.done": "✓ 初始化完成",
29
25
 
30
26
  // config
31
- "config.description": "查看或切换数据源模式",
32
- "config.current_mode": "当前模式: {mode}",
33
- "config.switch_confirm": "是否切换模式?(y/N) ",
34
- "config.cc_switch_not_installed": "cc-switch 未安装",
35
- "config.switched": "✓ 已切换为 {mode} 模式",
27
+ "config.description": "查看数据源模式(已废弃)",
36
28
 
37
29
  // list
38
30
  "list.description": "列出并选择配置方案",
@@ -40,7 +32,6 @@ const zh = {
40
32
  "list.header": "可用配置:",
41
33
  "list.select": "选择配置:",
42
34
  "list.current_marker": "(当前)",
43
- "list.cancelled": "已取消",
44
35
  "list.choose_number": "输入序号切换 (回车跳过): ",
45
36
 
46
37
  // current
@@ -135,6 +126,23 @@ const zh = {
135
126
  "alias.rm_config": "配置 {target}",
136
127
  "alias.rm_choose": "请选择 (1/2): ",
137
128
 
129
+ // sync
130
+ "sync.description": "从 cc-switch 同步配置",
131
+ "sync.no_cc_switch": "未检测到 cc-switch 数据库",
132
+ "sync.empty": "cc-switch 中没有找到 Claude 配置",
133
+ "sync.done": "✓ 已同步 {count} 个配置",
134
+ "sync.current": "当前激活: {name}",
135
+ "sync.no_current": "当前无激活配置",
136
+
137
+ // clear
138
+ "clear.description": "清理 ccm 数据文件",
139
+ "clear.confirm": "确认删除所有 ccm 数据文件?(y/N) ",
140
+ "clear.cancelled": "已取消清理",
141
+ "clear.removed": "✓ 已删除 {path}",
142
+ "clear.clear_env": "是否同时清除 ~/.claude/settings.json 中的 env 配置?(y/N) ",
143
+ "clear.env_cleared": "✓ 已清除 env 配置",
144
+ "clear.done": "✓ 清理完成",
145
+
138
146
  // store errors
139
147
  "store.db_not_found": "cc-switch 数据库不存在: {path}",
140
148
  } as const;
package/src/index.ts CHANGED
@@ -4,11 +4,11 @@ import { Command } from "commander";
4
4
  import chalk from "chalk";
5
5
  import { readRc, writeRc, getStore } from "./utils.js";
6
6
  import { ccSwitchExists } from "./store/cc-switch.js";
7
- import { readClaudeSettings, applyProfile, getSettingsPath } from "./claude.js";
7
+ import { readClaudeSettings, applyProfile, clearEnvFromSettings, getSettingsPath } from "./claude.js";
8
8
  import { createInterface } from "readline";
9
9
  import { spawnSync } from "child_process";
10
- import { writeFileSync, readFileSync, unlinkSync } from "fs";
11
- import { tmpdir } from "os";
10
+ import { writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
11
+ import { tmpdir, homedir } from "os";
12
12
  import { join } from "path";
13
13
  import { t, setLocale } from "./i18n/index.js";
14
14
  import * as clack from "@clack/prompts";
@@ -31,14 +31,9 @@ function ask(question: string): Promise<string> {
31
31
  });
32
32
  }
33
33
 
34
- // Helper: ensure initialized
34
+ // Helper: ensure store ready
35
35
  function ensureStore() {
36
- const store = getStore();
37
- if (!store) {
38
- console.log(chalk.yellow(t("common.not_init")));
39
- process.exit(1);
40
- }
41
- return store;
36
+ return getStore();
42
37
  }
43
38
 
44
39
  // Helper: format env for display
@@ -172,50 +167,122 @@ program
172
167
  .command("init")
173
168
  .description(t("init.description"))
174
169
  .action(async () => {
170
+ const rc = readRc();
171
+ writeRc({ aliases: rc?.aliases, locale: rc?.locale });
172
+ console.log(chalk.green(t("init.done")));
173
+
175
174
  if (ccSwitchExists()) {
176
175
  const use = await ask(t("init.cc_switch_found"));
177
176
  if (use.toLowerCase() !== "n") {
178
- writeRc({ mode: "cc-switch" });
179
177
  const { CcSwitchStore } = await import("./store/cc-switch.js");
180
- const store = new CcSwitchStore();
181
- const profiles = store.list();
182
- const current = store.getCurrent();
183
- console.log(chalk.green(t("init.done_cc_switch")));
184
- console.log(chalk.green(t("init.imported", { count: String(profiles.length) })));
178
+ const { StandaloneStore } = await import("./store/standalone.js");
179
+ const ccStore = new CcSwitchStore();
180
+ const standaloneStore = new StandaloneStore();
181
+ const profiles = ccStore.list();
182
+ const current = ccStore.getCurrent();
183
+
184
+ for (const profile of profiles) {
185
+ standaloneStore.save(profile.name, profile.settingsConfig);
186
+ }
187
+ if (current) {
188
+ standaloneStore.setCurrent(current);
189
+ }
190
+
191
+ console.log(chalk.green(t("sync.done", { count: String(profiles.length) })));
185
192
  if (current) {
186
- console.log(chalk.gray(t("init.current", { name: current })));
193
+ console.log(chalk.gray(t("sync.current", { name: current })));
187
194
  } else {
188
- console.log(chalk.gray(t("init.no_current")));
195
+ console.log(chalk.gray(t("sync.no_current")));
189
196
  }
190
- return;
197
+ ccStore.close();
191
198
  }
192
199
  }
193
-
194
- writeRc({ mode: "standalone" });
195
- console.log(chalk.green(t("init.done_standalone")));
196
200
  });
197
201
 
198
- // ccm config
202
+ // ccm config (deprecated, kept for compatibility)
199
203
  program
200
204
  .command("config")
201
205
  .description(t("config.description"))
202
206
  .action(async () => {
203
- const rc = readRc();
204
- if (!rc) {
205
- console.log(chalk.yellow(t("common.not_init")));
207
+ console.log(chalk.gray("ccm now only uses standalone mode. No configuration needed."));
208
+ });
209
+
210
+ // ccm sync
211
+ program
212
+ .command("sync")
213
+ .description(t("sync.description"))
214
+ .action(async () => {
215
+ if (!ccSwitchExists()) {
216
+ console.log(chalk.red(t("sync.no_cc_switch")));
206
217
  return;
207
218
  }
208
- console.log(t("config.current_mode", { mode: chalk.cyan(rc.mode) }));
209
- const confirm = await ask(t("config.switch_confirm"));
210
- if (confirm.toLowerCase() === "y") {
211
- const newMode = rc.mode === "cc-switch" ? "standalone" : "cc-switch";
212
- if (newMode === "cc-switch" && !ccSwitchExists()) {
213
- console.log(chalk.red(t("config.cc_switch_not_installed")));
214
- return;
219
+ const { CcSwitchStore } = await import("./store/cc-switch.js");
220
+ const { StandaloneStore } = await import("./store/standalone.js");
221
+ const ccStore = new CcSwitchStore();
222
+ const standaloneStore = new StandaloneStore();
223
+
224
+ const profiles = ccStore.list();
225
+ if (profiles.length === 0) {
226
+ console.log(chalk.yellow(t("sync.empty")));
227
+ ccStore.close();
228
+ return;
229
+ }
230
+
231
+ for (const profile of profiles) {
232
+ standaloneStore.save(profile.name, profile.settingsConfig);
233
+ }
234
+
235
+ const current = ccStore.getCurrent();
236
+ if (current) {
237
+ standaloneStore.setCurrent(current);
238
+ }
239
+
240
+ console.log(chalk.green(t("sync.done", { count: String(profiles.length) })));
241
+ if (current) {
242
+ console.log(chalk.gray(t("sync.current", { name: current })));
243
+ } else {
244
+ console.log(chalk.gray(t("sync.no_current")));
245
+ }
246
+
247
+ ccStore.close();
248
+ });
249
+
250
+ // ccm clear
251
+ program
252
+ .command("clear")
253
+ .description(t("clear.description"))
254
+ .action(async () => {
255
+ const confirm = await ask(t("clear.confirm"));
256
+ if (confirm.toLowerCase() !== "y") {
257
+ console.log(chalk.gray(t("clear.cancelled")));
258
+ return;
259
+ }
260
+
261
+ const rcPath = join(homedir(), ".ccm", "rc.json");
262
+ const configPath = join(homedir(), ".ccm", "config.json");
263
+
264
+ if (existsSync(configPath)) {
265
+ unlinkSync(configPath);
266
+ console.log(chalk.green(t("clear.removed", { path: configPath })));
267
+ }
268
+ if (existsSync(rcPath)) {
269
+ unlinkSync(rcPath);
270
+ console.log(chalk.green(t("clear.removed", { path: rcPath })));
271
+ }
272
+
273
+ const settingsPath = getSettingsPath();
274
+ if (existsSync(settingsPath)) {
275
+ const settings = readClaudeSettings();
276
+ if ("env" in settings) {
277
+ const clearEnv = await ask(t("clear.clear_env"));
278
+ if (clearEnv.toLowerCase() === "y") {
279
+ clearEnvFromSettings();
280
+ console.log(chalk.green(t("clear.env_cleared")));
281
+ }
215
282
  }
216
- writeRc({ mode: newMode });
217
- console.log(chalk.green(t("config.switched", { mode: newMode })));
218
283
  }
284
+
285
+ console.log(chalk.green(t("clear.done")));
219
286
  });
220
287
 
221
288
  // ccm list
@@ -270,8 +337,7 @@ program
270
337
  });
271
338
 
272
339
  if (clack.isCancel(selected)) {
273
- clack.cancel(t("list.cancelled"));
274
- return;
340
+ process.exit(0);
275
341
  }
276
342
  switchTo(selected as string);
277
343
  } else {
@@ -583,8 +649,7 @@ program
583
649
  });
584
650
 
585
651
  if (clack.isCancel(selected)) {
586
- clack.cancel(t("list.cancelled"));
587
- return;
652
+ process.exit(0);
588
653
  }
589
654
  name = selected as string;
590
655
  } else {
@@ -741,8 +806,7 @@ program
741
806
  });
742
807
 
743
808
  if (clack.isCancel(selected)) {
744
- clack.cancel(t("list.cancelled"));
745
- return;
809
+ process.exit(0);
746
810
  }
747
811
  name = selected as string;
748
812
  } else {
@@ -887,7 +951,6 @@ const SUPPORTED_LOCALES: Array<{ code: string; label: string }> = [
887
951
 
888
952
  const switchLocale = (code: string) => {
889
953
  const rc = readRc();
890
- if (!rc) { console.log(chalk.yellow(t("common.not_init"))); return; }
891
954
  rc.locale = code as "zh" | "en";
892
955
  writeRc(rc);
893
956
  setLocale(code as "zh" | "en");
@@ -916,7 +979,8 @@ localeCmd
916
979
  initialValue: current,
917
980
  });
918
981
 
919
- if (clack.isCancel(selected) || selected === current) return;
982
+ if (clack.isCancel(selected)) process.exit(0);
983
+ if (selected === current) return;
920
984
  switchLocale(selected as string);
921
985
  } else {
922
986
  console.log(chalk.bold(`\n${t("locale.list_header")}\n`));
@@ -62,12 +62,22 @@ export class CcSwitchStore implements DataStore {
62
62
  )
63
63
  .run(JSON.stringify(settingsConfig), name);
64
64
  } else {
65
+ const maxSort = this.db
66
+ .prepare(
67
+ `SELECT COALESCE(MAX(sort_index), -1) as max_sort FROM providers WHERE app_type = 'claude'`
68
+ )
69
+ .get() as { max_sort: number } | undefined;
70
+ const sortIndex = (maxSort?.max_sort ?? -1) + 1;
71
+
65
72
  const id = crypto.randomUUID();
66
73
  this.db
67
74
  .prepare(
68
- `INSERT INTO providers (id, app_type, name, settings_config, meta, created_at) VALUES (?, 'claude', ?, ?, '{}', ?)`
75
+ `INSERT INTO providers (
76
+ id, app_type, name, settings_config, website_url, category,
77
+ created_at, sort_index, notes, icon, icon_color, meta, is_current, in_failover_queue
78
+ ) VALUES (?, 'claude', ?, ?, NULL, NULL, ?, ?, NULL, NULL, NULL, '{}', 0, 0)`
69
79
  )
70
- .run(id, name, JSON.stringify(settingsConfig), Date.now());
80
+ .run(id, name, JSON.stringify(settingsConfig), Date.now(), sortIndex);
71
81
  }
72
82
  }
73
83
 
@@ -81,6 +91,15 @@ export class CcSwitchStore implements DataStore {
81
91
  }
82
92
 
83
93
  getCurrent(): string | undefined {
94
+ // Prefer DB is_current so we stay in sync with cc-switch UI
95
+ const dbRow = this.db
96
+ .prepare(
97
+ `SELECT name FROM providers WHERE app_type = 'claude' AND is_current = 1 LIMIT 1`
98
+ )
99
+ .get() as { name: string } | undefined;
100
+ if (dbRow) return dbRow.name;
101
+
102
+ // Fallback to settings.json
84
103
  if (!existsSync(SETTINGS_PATH)) return undefined;
85
104
  try {
86
105
  const settings = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
@@ -101,6 +120,18 @@ export class CcSwitchStore implements DataStore {
101
120
  const profile = this.get(name);
102
121
  if (!profile) throw new Error(t("error.not_found", { name }));
103
122
 
123
+ const tx = this.db.transaction(() => {
124
+ this.db
125
+ .prepare(`UPDATE providers SET is_current = 0 WHERE app_type = 'claude'`)
126
+ .run();
127
+ this.db
128
+ .prepare(
129
+ `UPDATE providers SET is_current = 1 WHERE app_type = 'claude' AND id = ?`
130
+ )
131
+ .run(profile.id);
132
+ });
133
+ tx();
134
+
104
135
  if (existsSync(SETTINGS_PATH)) {
105
136
  const settings = JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
106
137
  settings.currentProviderClaude = profile.id;
package/src/types.ts CHANGED
@@ -14,7 +14,6 @@ export interface DataStore {
14
14
  }
15
15
 
16
16
  export interface RcConfig {
17
- mode: "cc-switch" | "standalone";
18
17
  aliases?: Record<string, string>;
19
18
  locale?: "zh" | "en";
20
19
  }
package/src/utils.ts CHANGED
@@ -3,18 +3,21 @@ import { join } from "path";
3
3
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
4
  import type { RcConfig } from "./types.js";
5
5
  import type { DataStore } from "./store/interface.js";
6
- import { CcSwitchStore } from "./store/cc-switch.js";
7
6
  import { StandaloneStore } from "./store/standalone.js";
8
7
 
9
8
  const CCM_DIR = join(homedir(), ".ccm");
10
9
  const RC_PATH = join(CCM_DIR, "rc.json");
11
10
 
12
- export function readRc(): RcConfig | undefined {
13
- if (!existsSync(RC_PATH)) return undefined;
11
+ export function readRc(): RcConfig {
12
+ if (!existsSync(RC_PATH)) {
13
+ writeRc({});
14
+ return {};
15
+ }
14
16
  try {
15
17
  return JSON.parse(readFileSync(RC_PATH, "utf-8"));
16
18
  } catch {
17
- return undefined;
19
+ writeRc({});
20
+ return {};
18
21
  }
19
22
  }
20
23
 
@@ -25,13 +28,6 @@ export function writeRc(rc: RcConfig): void {
25
28
  writeFileSync(RC_PATH, JSON.stringify(rc, null, 2));
26
29
  }
27
30
 
28
- export function getStore(): DataStore | null {
29
- const rc = readRc();
30
- if (!rc) return null;
31
-
32
- if (rc.mode === "cc-switch") {
33
- return new CcSwitchStore();
34
- } else {
35
- return new StandaloneStore();
36
- }
31
+ export function getStore(): DataStore {
32
+ return new StandaloneStore();
37
33
  }