@daylenjeez/ccm-switch 1.2.9 → 1.3.1

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
@@ -7,7 +7,7 @@
7
7
  Switch Claude Code custom model configurations from the terminal in seconds.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@daylenjeez/ccm-switch.svg?style=flat-square)](https://www.npmjs.com/package/@daylenjeez/ccm-switch)
10
- [![license](https://img.shields.io/npm/l/@daylenjeez/ccm-switch.svg?style=flat-square)](https://github.com/daylenjeez/ccm-switch/blob/main/LICENSE)
10
+ [![license](https://img.shields.io/github/license/daylenjeez/ccm-switch?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
13
  [中文文档](https://github.com/daylenjeez/ccm-switch/blob/main/README.zh-CN.md) | English
@@ -130,12 +130,12 @@ Aliases are stored in `~/.ccm/rc.json`:
130
130
  | `ccm use <name>` | | Switch by name |
131
131
  | `ccm add` | `new` | Interactive add wizard |
132
132
  | `ccm save <name>` | | Save current settings as profile |
133
- | `ccm show [name]` | | View config details |
133
+ | `ccm show [name]` | | View config details (all configs in JSON if no name) |
134
134
  | `ccm modify [name]` | `edit` | Edit existing configuration |
135
135
  | `ccm remove [name]` | `rm` | Interactive or named delete |
136
136
  | `ccm current` | | Show active configuration |
137
- | `ccm config` | | View data source mode (deprecated) |
138
137
  | `ccm sync` | | Sync cc-switch configs into standalone |
138
+ | `ccm import [file]` | | Import configs from JSON (stdin if no file) |
139
139
  | `ccm clear` | | Clean up data files |
140
140
 
141
141
  ### Aliases
package/README.zh-CN.md CHANGED
@@ -7,7 +7,7 @@
7
7
  在终端几秒内完成 Claude Code 自定义模型配置的管理和切换。
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@daylenjeez/ccm-switch.svg?style=flat-square)](https://www.npmjs.com/package/@daylenjeez/ccm-switch)
10
- [![license](https://img.shields.io/npm/l/@daylenjeez/ccm-switch.svg?style=flat-square)](https://github.com/daylenjeez/ccm-switch/blob/main/LICENSE)
10
+ [![license](https://img.shields.io/github/license/daylenjeez/ccm-switch?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
13
  [English](https://github.com/daylenjeez/ccm-switch/blob/main/README.md) | 中文文档
@@ -130,12 +130,12 @@ ANTHROPIC_DEFAULT_HAIKU_MODEL (可选):
130
130
  | `ccm use <name>` | | 按名称切换 |
131
131
  | `ccm add` | `new` | 交互式添加向导 |
132
132
  | `ccm save <name>` | | 将当前设置保存为方案 |
133
- | `ccm show [name]` | | 查看配置详情 |
133
+ | `ccm show [name]` | | 查看配置详情(无名称时输出全部 JSON) |
134
134
  | `ccm modify [name]` | `edit` | 修改已有配置 |
135
135
  | `ccm remove [name]` | `rm` | 交互式或指定名称删除 |
136
136
  | `ccm current` | | 显示当前激活配置 |
137
- | `ccm config` | | 查看数据源模式(已废弃) |
138
137
  | `ccm sync` | | 从 cc-switch 同步配置到本地 |
138
+ | `ccm import [file]` | | 从 JSON 导入配置(无文件时从 stdin 读取) |
139
139
  | `ccm clear` | | 清理数据文件 |
140
140
 
141
141
  ### 别名管理
package/dist/i18n/en.js CHANGED
@@ -18,8 +18,6 @@ const en = {
18
18
  "init.description": "Initialize ccm",
19
19
  "init.cc_switch_found": "cc-switch detected. Import configurations from it? (Y/n) ",
20
20
  "init.done": "✓ Initialized",
21
- // config
22
- "config.description": "View data source mode (deprecated)",
23
21
  // list
24
22
  "list.description": "List and select configurations",
25
23
  "list.empty": "No configurations yet. Use ccm save <name> to save current config",
@@ -44,6 +42,7 @@ const en = {
44
42
  // show
45
43
  "show.description": "View configuration details (defaults to current)",
46
44
  "show.no_current": "No active configuration. Specify a name: ccm show <name>",
45
+ "show.all_header": "All configurations:",
47
46
  // remove
48
47
  "remove.description": "Delete a configuration",
49
48
  "remove.select": "Select configuration to delete:",
@@ -122,6 +121,13 @@ const en = {
122
121
  "clear.cancelled": "Cancelled",
123
122
  "clear.removed": "✓ Deleted {path}",
124
123
  "clear.done": "✓ Cleanup complete",
124
+ // import
125
+ "import.description": "Import configurations from JSON (file or stdin)",
126
+ "import.paste_hint": "Paste JSON and press Ctrl+D (or Ctrl+Z on Windows) to finish:",
127
+ "import.file_not_found": 'File not found: {file}',
128
+ "import.json_parse_error": "Invalid JSON format",
129
+ "import.invalid_format": "Invalid format: expected object with configuration names as keys",
130
+ "import.done": "✓ Imported {count} configurations",
125
131
  // store errors
126
132
  "store.db_not_found": "cc-switch database not found: {path}",
127
133
  };
@@ -1,5 +1,6 @@
1
1
  import { type TranslationKey } from "./zh.js";
2
2
  export type Locale = "zh" | "en";
3
+ declare function getLocale(): Locale;
3
4
  export declare function setLocale(locale: Locale): void;
4
5
  export declare function t(key: TranslationKey, vars?: Record<string, string>): string;
5
- export { type TranslationKey };
6
+ export { getLocale, type TranslationKey };
@@ -33,3 +33,4 @@ export function t(key, vars) {
33
33
  }
34
34
  return text;
35
35
  }
36
+ export { getLocale };
package/dist/i18n/zh.d.ts CHANGED
@@ -13,7 +13,6 @@ declare const zh: {
13
13
  readonly "init.description": "初始化 ccm";
14
14
  readonly "init.cc_switch_found": "检测到 cc-switch 已安装,是否从中导入配置?(Y/n) ";
15
15
  readonly "init.done": "✓ 初始化完成";
16
- readonly "config.description": "查看数据源模式(已废弃)";
17
16
  readonly "list.description": "列出并选择配置方案";
18
17
  readonly "list.empty": "暂无配置方案。使用 ccm save <name> 保存当前配置";
19
18
  readonly "list.header": "可用配置:";
@@ -33,6 +32,7 @@ declare const zh: {
33
32
  readonly "save.done": "✓ 已保存当前配置为 \"{name}\"";
34
33
  readonly "show.description": "查看配置详情(不指定则显示当前)";
35
34
  readonly "show.no_current": "当前无激活配置,请指定名称: ccm show <name>";
35
+ readonly "show.all_header": "所有配置:";
36
36
  readonly "remove.description": "删除配置方案";
37
37
  readonly "remove.select": "选择要删除的配置:";
38
38
  readonly "remove.confirm": "确认删除 \"{name}\"?(y/N) ";
@@ -103,6 +103,12 @@ declare const zh: {
103
103
  readonly "clear.cancelled": "已取消清理";
104
104
  readonly "clear.removed": "✓ 已删除 {path}";
105
105
  readonly "clear.done": "✓ 清理完成";
106
+ readonly "import.description": "从 JSON 导入配置(文件或粘贴)";
107
+ readonly "import.paste_hint": "粘贴 JSON,按 Ctrl+D(或 Windows 的 Ctrl+Z)结束:";
108
+ readonly "import.file_not_found": "文件不存在: {file}";
109
+ readonly "import.json_parse_error": "JSON 格式无效";
110
+ readonly "import.invalid_format": "格式错误: 应为以配置名称为键的对象";
111
+ readonly "import.done": "✓ 已导入 {count} 个配置";
106
112
  readonly "store.db_not_found": "cc-switch 数据库不存在: {path}";
107
113
  };
108
114
  export type TranslationKey = keyof typeof zh;
package/dist/i18n/zh.js CHANGED
@@ -18,8 +18,6 @@ const zh = {
18
18
  "init.description": "初始化 ccm",
19
19
  "init.cc_switch_found": "检测到 cc-switch 已安装,是否从中导入配置?(Y/n) ",
20
20
  "init.done": "✓ 初始化完成",
21
- // config
22
- "config.description": "查看数据源模式(已废弃)",
23
21
  // list
24
22
  "list.description": "列出并选择配置方案",
25
23
  "list.empty": "暂无配置方案。使用 ccm save <name> 保存当前配置",
@@ -44,6 +42,7 @@ const zh = {
44
42
  // show
45
43
  "show.description": "查看配置详情(不指定则显示当前)",
46
44
  "show.no_current": "当前无激活配置,请指定名称: ccm show <name>",
45
+ "show.all_header": "所有配置:",
47
46
  // remove
48
47
  "remove.description": "删除配置方案",
49
48
  "remove.select": "选择要删除的配置:",
@@ -122,6 +121,13 @@ const zh = {
122
121
  "clear.cancelled": "已取消清理",
123
122
  "clear.removed": "✓ 已删除 {path}",
124
123
  "clear.done": "✓ 清理完成",
124
+ // import
125
+ "import.description": "从 JSON 导入配置(文件或粘贴)",
126
+ "import.paste_hint": "粘贴 JSON,按 Ctrl+D(或 Windows 的 Ctrl+Z)结束:",
127
+ "import.file_not_found": '文件不存在: {file}',
128
+ "import.json_parse_error": "JSON 格式无效",
129
+ "import.invalid_format": "格式错误: 应为以配置名称为键的对象",
130
+ "import.done": "✓ 已导入 {count} 个配置",
125
131
  // store errors
126
132
  "store.db_not_found": "cc-switch 数据库不存在: {path}",
127
133
  };
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
10
10
  import { tmpdir, homedir } from "os";
11
11
  import { join, dirname } from "path";
12
12
  import { fileURLToPath } from "url";
13
- import { t, setLocale } from "./i18n/index.js";
13
+ import { t, setLocale, getLocale } from "./i18n/index.js";
14
14
  import * as clack from "@clack/prompts";
15
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
16
16
  const packageJsonPath = join(__dirname, "..", "package.json");
@@ -182,13 +182,6 @@ program
182
182
  }
183
183
  }
184
184
  });
185
- // ccm config (deprecated, kept for compatibility)
186
- program
187
- .command("config")
188
- .description(t("config.description"))
189
- .action(async () => {
190
- console.log(chalk.gray("ccm now only uses standalone mode. No configuration needed."));
191
- });
192
185
  // ccm sync
193
186
  program
194
187
  .command("sync")
@@ -246,6 +239,50 @@ program
246
239
  }
247
240
  console.log(chalk.green(t("clear.done")));
248
241
  });
242
+ // ccm import
243
+ program
244
+ .command("import [file]")
245
+ .description(t("import.description"))
246
+ .action(async (file) => {
247
+ const store = ensureStore();
248
+ let jsonContent;
249
+ if (file) {
250
+ // Read from file
251
+ if (!existsSync(file)) {
252
+ console.log(chalk.red(t("import.file_not_found", { file })));
253
+ return;
254
+ }
255
+ jsonContent = readFileSync(file, "utf-8");
256
+ }
257
+ else {
258
+ // Read from stdin
259
+ console.log(chalk.gray(t("import.paste_hint")));
260
+ const chunks = [];
261
+ process.stdin.setEncoding("utf-8");
262
+ for await (const chunk of process.stdin) {
263
+ chunks.push(Buffer.from(chunk));
264
+ }
265
+ jsonContent = Buffer.concat(chunks).toString("utf-8");
266
+ }
267
+ let configs;
268
+ try {
269
+ configs = JSON.parse(jsonContent);
270
+ }
271
+ catch {
272
+ console.log(chalk.red(t("import.json_parse_error")));
273
+ return;
274
+ }
275
+ if (typeof configs !== "object" || configs === null || Object.keys(configs).length === 0) {
276
+ console.log(chalk.red(t("import.invalid_format")));
277
+ return;
278
+ }
279
+ let count = 0;
280
+ for (const [name, settingsConfig] of Object.entries(configs)) {
281
+ store.save(name, settingsConfig);
282
+ count++;
283
+ }
284
+ console.log(chalk.green(t("import.done", { count: String(count) })));
285
+ });
249
286
  // ccm list
250
287
  program
251
288
  .command("list")
@@ -517,19 +554,26 @@ program
517
554
  }
518
555
  await saveAndSwitch(store, name, settingsConfig);
519
556
  });
520
- // ccm show <name>
557
+ // ccm show [name]
521
558
  program
522
559
  .command("show [name]")
523
560
  .description(t("show.description"))
524
561
  .action(async (name) => {
525
562
  const store = ensureStore();
526
563
  if (!name) {
527
- const currentName = store.getCurrent();
528
- if (!currentName) {
529
- console.log(chalk.yellow(t("show.no_current")));
564
+ // Show all configurations
565
+ const profiles = store.list();
566
+ if (profiles.length === 0) {
567
+ console.log(chalk.yellow(t("list.empty")));
530
568
  return;
531
569
  }
532
- name = currentName;
570
+ console.log(chalk.bold(`\n${t("show.all_header")}\n`));
571
+ const allConfigs = {};
572
+ for (const profile of profiles) {
573
+ allConfigs[profile.name] = profile.settingsConfig;
574
+ }
575
+ console.log(JSON.stringify(allConfigs, null, 2));
576
+ return;
533
577
  }
534
578
  const profile = await resolveProfile(store, name);
535
579
  if (!profile)
@@ -865,7 +909,7 @@ localeCmd
865
909
  .description(t("locale.list_description"))
866
910
  .action(async () => {
867
911
  const rc = readRc();
868
- const current = rc?.locale || "zh";
912
+ const current = rc?.locale || getLocale();
869
913
  const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
870
914
  if (isInteractive) {
871
915
  const options = SUPPORTED_LOCALES.map(({ code, label }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daylenjeez/ccm-switch",
3
- "version": "1.2.9",
3
+ "version": "1.3.1",
4
4
  "description": "Claude Code Model Switcher - 快速切换 Claude Code 自定义模型配置",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@clack/prompts": "^1.2.0",
27
+ "@daylenjeez/ccm-switch": "^1.2.9",
27
28
  "better-sqlite3": "^12.8.0",
28
29
  "chalk": "^5.6.2",
29
30
  "commander": "^14.0.3"
package/src/i18n/en.ts CHANGED
@@ -25,9 +25,6 @@ const en: Record<TranslationKey, string> = {
25
25
  "init.cc_switch_found":"cc-switch detected. Import configurations from it? (Y/n) ",
26
26
  "init.done": "✓ Initialized",
27
27
 
28
- // config
29
- "config.description": "View data source mode (deprecated)",
30
-
31
28
  // list
32
29
  "list.description": "List and select configurations",
33
30
  "list.empty": "No configurations yet. Use ccm save <name> to save current config",
@@ -56,6 +53,7 @@ const en: Record<TranslationKey, string> = {
56
53
  // show
57
54
  "show.description": "View configuration details (defaults to current)",
58
55
  "show.no_current": "No active configuration. Specify a name: ccm show <name>",
56
+ "show.all_header": "All configurations:",
59
57
 
60
58
  // remove
61
59
  "remove.description": "Delete a configuration",
@@ -143,6 +141,14 @@ const en: Record<TranslationKey, string> = {
143
141
  "clear.removed": "✓ Deleted {path}",
144
142
  "clear.done": "✓ Cleanup complete",
145
143
 
144
+ // import
145
+ "import.description": "Import configurations from JSON (file or stdin)",
146
+ "import.paste_hint": "Paste JSON and press Ctrl+D (or Ctrl+Z on Windows) to finish:",
147
+ "import.file_not_found": 'File not found: {file}',
148
+ "import.json_parse_error": "Invalid JSON format",
149
+ "import.invalid_format": "Invalid format: expected object with configuration names as keys",
150
+ "import.done": "✓ Imported {count} configurations",
151
+
146
152
  // store errors
147
153
  "store.db_not_found": "cc-switch database not found: {path}",
148
154
  };
package/src/i18n/index.ts CHANGED
@@ -41,4 +41,4 @@ export function t(key: TranslationKey, vars?: Record<string, string>): string {
41
41
  return text;
42
42
  }
43
43
 
44
- export { type TranslationKey };
44
+ export { getLocale, type TranslationKey };
package/src/i18n/zh.ts CHANGED
@@ -23,9 +23,6 @@ const zh = {
23
23
  "init.cc_switch_found":"检测到 cc-switch 已安装,是否从中导入配置?(Y/n) ",
24
24
  "init.done": "✓ 初始化完成",
25
25
 
26
- // config
27
- "config.description": "查看数据源模式(已废弃)",
28
-
29
26
  // list
30
27
  "list.description": "列出并选择配置方案",
31
28
  "list.empty": "暂无配置方案。使用 ccm save <name> 保存当前配置",
@@ -54,6 +51,7 @@ const zh = {
54
51
  // show
55
52
  "show.description": "查看配置详情(不指定则显示当前)",
56
53
  "show.no_current": "当前无激活配置,请指定名称: ccm show <name>",
54
+ "show.all_header": "所有配置:",
57
55
 
58
56
  // remove
59
57
  "remove.description": "删除配置方案",
@@ -141,6 +139,14 @@ const zh = {
141
139
  "clear.removed": "✓ 已删除 {path}",
142
140
  "clear.done": "✓ 清理完成",
143
141
 
142
+ // import
143
+ "import.description": "从 JSON 导入配置(文件或粘贴)",
144
+ "import.paste_hint": "粘贴 JSON,按 Ctrl+D(或 Windows 的 Ctrl+Z)结束:",
145
+ "import.file_not_found": '文件不存在: {file}',
146
+ "import.json_parse_error": "JSON 格式无效",
147
+ "import.invalid_format": "格式错误: 应为以配置名称为键的对象",
148
+ "import.done": "✓ 已导入 {count} 个配置",
149
+
144
150
  // store errors
145
151
  "store.db_not_found": "cc-switch 数据库不存在: {path}",
146
152
  } as const;
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@ import { writeFileSync, readFileSync, unlinkSync, existsSync } from "fs";
11
11
  import { tmpdir, homedir } from "os";
12
12
  import { join, dirname } from "path";
13
13
  import { fileURLToPath } from "url";
14
- import { t, setLocale } from "./i18n/index.js";
14
+ import { t, setLocale, getLocale } from "./i18n/index.js";
15
15
  import * as clack from "@clack/prompts";
16
16
 
17
17
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -204,14 +204,6 @@ program
204
204
  }
205
205
  });
206
206
 
207
- // ccm config (deprecated, kept for compatibility)
208
- program
209
- .command("config")
210
- .description(t("config.description"))
211
- .action(async () => {
212
- console.log(chalk.gray("ccm now only uses standalone mode. No configuration needed."));
213
- });
214
-
215
207
  // ccm sync
216
208
  program
217
209
  .command("sync")
@@ -278,6 +270,54 @@ program
278
270
  console.log(chalk.green(t("clear.done")));
279
271
  });
280
272
 
273
+ // ccm import
274
+ program
275
+ .command("import [file]")
276
+ .description(t("import.description"))
277
+ .action(async (file?: string) => {
278
+ const store = ensureStore();
279
+
280
+ let jsonContent: string;
281
+ if (file) {
282
+ // Read from file
283
+ if (!existsSync(file)) {
284
+ console.log(chalk.red(t("import.file_not_found", { file })));
285
+ return;
286
+ }
287
+ jsonContent = readFileSync(file, "utf-8");
288
+ } else {
289
+ // Read from stdin
290
+ console.log(chalk.gray(t("import.paste_hint")));
291
+ const chunks: Buffer[] = [];
292
+ process.stdin.setEncoding("utf-8");
293
+ for await (const chunk of process.stdin) {
294
+ chunks.push(Buffer.from(chunk));
295
+ }
296
+ jsonContent = Buffer.concat(chunks).toString("utf-8");
297
+ }
298
+
299
+ let configs: Record<string, Record<string, unknown>>;
300
+ try {
301
+ configs = JSON.parse(jsonContent);
302
+ } catch {
303
+ console.log(chalk.red(t("import.json_parse_error")));
304
+ return;
305
+ }
306
+
307
+ if (typeof configs !== "object" || configs === null || Object.keys(configs).length === 0) {
308
+ console.log(chalk.red(t("import.invalid_format")));
309
+ return;
310
+ }
311
+
312
+ let count = 0;
313
+ for (const [name, settingsConfig] of Object.entries(configs)) {
314
+ store.save(name, settingsConfig);
315
+ count++;
316
+ }
317
+
318
+ console.log(chalk.green(t("import.done", { count: String(count) })));
319
+ });
320
+
281
321
  // ccm list
282
322
  program
283
323
  .command("list")
@@ -576,7 +616,7 @@ program
576
616
  await saveAndSwitch(store, name, settingsConfig);
577
617
  });
578
618
 
579
- // ccm show <name>
619
+ // ccm show [name]
580
620
  program
581
621
  .command("show [name]")
582
622
  .description(t("show.description"))
@@ -584,12 +624,20 @@ program
584
624
  const store = ensureStore();
585
625
 
586
626
  if (!name) {
587
- const currentName = store.getCurrent();
588
- if (!currentName) {
589
- console.log(chalk.yellow(t("show.no_current")));
627
+ // Show all configurations
628
+ const profiles = store.list();
629
+ if (profiles.length === 0) {
630
+ console.log(chalk.yellow(t("list.empty")));
590
631
  return;
591
632
  }
592
- name = currentName;
633
+
634
+ console.log(chalk.bold(`\n${t("show.all_header")}\n`));
635
+ const allConfigs: Record<string, Record<string, unknown>> = {};
636
+ for (const profile of profiles) {
637
+ allConfigs[profile.name] = profile.settingsConfig;
638
+ }
639
+ console.log(JSON.stringify(allConfigs, null, 2));
640
+ return;
593
641
  }
594
642
 
595
643
  const profile = await resolveProfile(store, name);
@@ -956,7 +1004,7 @@ localeCmd
956
1004
  .description(t("locale.list_description"))
957
1005
  .action(async () => {
958
1006
  const rc = readRc();
959
- const current = rc?.locale || "zh";
1007
+ const current = rc?.locale || getLocale();
960
1008
  const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
961
1009
 
962
1010
  if (isInteractive) {