@xwm111/ccs 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/bin/ccs.mjs +2 -0
  4. package/dist/chunks/auto-updater.mjs +1708 -0
  5. package/dist/chunks/claude-code-incremental-manager.mjs +576 -0
  6. package/dist/chunks/installer.mjs +610 -0
  7. package/dist/cli.d.mts +1 -0
  8. package/dist/cli.d.ts +1 -0
  9. package/dist/cli.mjs +2407 -0
  10. package/dist/i18n/locales/en/api.json +51 -0
  11. package/dist/i18n/locales/en/cli.json +58 -0
  12. package/dist/i18n/locales/en/common.json +19 -0
  13. package/dist/i18n/locales/en/configuration.json +81 -0
  14. package/dist/i18n/locales/en/errors.json +26 -0
  15. package/dist/i18n/locales/en/installation.json +80 -0
  16. package/dist/i18n/locales/en/language.json +19 -0
  17. package/dist/i18n/locales/en/menu.json +31 -0
  18. package/dist/i18n/locales/en/multi-config.json +79 -0
  19. package/dist/i18n/locales/en/uninstall.json +56 -0
  20. package/dist/i18n/locales/en/updater.json +26 -0
  21. package/dist/i18n/locales/zh-CN/api.json +51 -0
  22. package/dist/i18n/locales/zh-CN/cli.json +58 -0
  23. package/dist/i18n/locales/zh-CN/common.json +19 -0
  24. package/dist/i18n/locales/zh-CN/configuration.json +81 -0
  25. package/dist/i18n/locales/zh-CN/errors.json +26 -0
  26. package/dist/i18n/locales/zh-CN/installation.json +80 -0
  27. package/dist/i18n/locales/zh-CN/language.json +19 -0
  28. package/dist/i18n/locales/zh-CN/menu.json +31 -0
  29. package/dist/i18n/locales/zh-CN/multi-config.json +79 -0
  30. package/dist/i18n/locales/zh-CN/uninstall.json +56 -0
  31. package/dist/i18n/locales/zh-CN/updater.json +26 -0
  32. package/dist/index.d.mts +222 -0
  33. package/dist/index.d.ts +222 -0
  34. package/dist/index.mjs +18 -0
  35. package/package.json +109 -0
package/dist/cli.mjs ADDED
@@ -0,0 +1,2407 @@
1
+ #!/usr/bin/env node
2
+ import cac from 'cac';
3
+ import ansis from 'ansis';
4
+ import { D as DEFAULT_CODE_TOOL_TYPE, X as ensureDir, P as exists, Y as readFile, _ as writeFile, e as ZCF_CONFIG_FILE, $ as readJsonConfig, L as LEGACY_ZCF_CONFIG_FILES, Z as ZCF_CONFIG_DIR, j as isCodeToolType, l as SUPPORTED_LANGS, H as i18n, G as ensureI18nInitialized, a0 as checkAndUpdateTools, S as SETTINGS_FILE, a1 as clearModelEnv, a2 as copyFile, r as resolveCodeToolType, a3 as version, a4 as homepage, m as LANG_LABELS, a5 as changeLanguage, E as switchToOfficialLogin, a6 as writeJsonConfig, W as promptBoolean, h as CODE_TOOL_BANNERS, a7 as initI18n } from './chunks/auto-updater.mjs';
5
+ import process from 'node:process';
6
+ import { existsSync, mkdirSync, renameSync, copyFileSync, rmSync } from 'node:fs';
7
+ import { dirname, join } from 'pathe';
8
+ import { edit, parse, stringify, initSync } from '@rainbowatcher/toml-edit-js';
9
+ import inquirer from 'inquirer';
10
+ import dayjs from 'dayjs';
11
+ import { homedir } from 'node:os';
12
+ import { pathExists } from 'fs-extra';
13
+ import { exec } from 'tinyexec';
14
+ import trash from 'trash';
15
+ import 'node:url';
16
+ import 'ora';
17
+ import 'semver';
18
+ import 'i18next';
19
+ import 'i18next-fs-backend';
20
+ import 'inquirer-toggle';
21
+ import 'node:child_process';
22
+ import 'node:util';
23
+
24
+ let initialized = false;
25
+ function ensureTomlInitSync() {
26
+ if (!initialized) {
27
+ initSync();
28
+ initialized = true;
29
+ }
30
+ }
31
+ function parseToml(content) {
32
+ ensureTomlInitSync();
33
+ return parse(content);
34
+ }
35
+ function stringifyToml(data) {
36
+ ensureTomlInitSync();
37
+ return stringify(data);
38
+ }
39
+ function batchEditToml(content, edits) {
40
+ ensureTomlInitSync();
41
+ let result = content;
42
+ for (const [path, value] of edits) {
43
+ result = edit(result, path, value);
44
+ }
45
+ return result;
46
+ }
47
+
48
+ function isSupportedLang(value) {
49
+ return SUPPORTED_LANGS.includes(value);
50
+ }
51
+ function sanitizePreferredLang(lang) {
52
+ return isSupportedLang(lang) ? lang : "en";
53
+ }
54
+ function sanitizeCodeToolType(codeTool) {
55
+ return isCodeToolType(codeTool) ? codeTool : DEFAULT_CODE_TOOL_TYPE;
56
+ }
57
+ function readTomlConfig(configPath) {
58
+ try {
59
+ if (!exists(configPath)) {
60
+ return null;
61
+ }
62
+ const content = readFile(configPath);
63
+ const parsed = parseToml(content);
64
+ return parsed;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+ function insertAtTopLevelStart(topLevel, content) {
70
+ const lines = topLevel.split("\n");
71
+ let insertLineIndex = 0;
72
+ for (let i = 0; i < lines.length; i++) {
73
+ const trimmed = lines[i].trim();
74
+ if (trimmed === "" || trimmed.startsWith("#")) {
75
+ insertLineIndex = i + 1;
76
+ } else {
77
+ insertLineIndex = i;
78
+ break;
79
+ }
80
+ }
81
+ lines.splice(insertLineIndex, 0, content.replace(/\n$/, ""));
82
+ return lines.join("\n");
83
+ }
84
+ function insertAfterVersionField(topLevel, content) {
85
+ const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m;
86
+ const match = topLevel.match(versionRegex);
87
+ if (match && match.index !== void 0) {
88
+ const versionEnd = match.index + match[0].length;
89
+ const before = topLevel.slice(0, versionEnd);
90
+ const after = topLevel.slice(versionEnd);
91
+ return `${before}
92
+ ${content.replace(/\n$/, "")}${after}`;
93
+ }
94
+ return insertAtTopLevelStart(topLevel, content);
95
+ }
96
+ function updateTopLevelTomlFields(content, version, lastUpdated) {
97
+ const firstSectionMatch = content.match(/^\[/m);
98
+ const topLevelEnd = firstSectionMatch?.index ?? content.length;
99
+ let topLevel = content.slice(0, topLevelEnd);
100
+ const rest = content.slice(topLevelEnd);
101
+ const versionRegex = /^version\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m;
102
+ const versionMatch = topLevel.match(versionRegex);
103
+ if (versionMatch) {
104
+ topLevel = topLevel.replace(versionRegex, `version = "${version}"`);
105
+ } else {
106
+ topLevel = insertAtTopLevelStart(topLevel, `version = "${version}"`);
107
+ }
108
+ const lastUpdatedRegex = /^lastUpdated\s*=\s*["'][^"']*["'][ \t]*(?:#.*)?$/m;
109
+ const lastUpdatedMatch = topLevel.match(lastUpdatedRegex);
110
+ if (lastUpdatedMatch) {
111
+ topLevel = topLevel.replace(lastUpdatedRegex, `lastUpdated = "${lastUpdated}"`);
112
+ } else {
113
+ topLevel = insertAfterVersionField(topLevel, `lastUpdated = "${lastUpdated}"`);
114
+ }
115
+ if (rest.length > 0 && !topLevel.endsWith("\n\n") && !topLevel.endsWith("\n")) {
116
+ topLevel += "\n";
117
+ }
118
+ return topLevel + rest;
119
+ }
120
+ function writeTomlConfig(configPath, config) {
121
+ try {
122
+ const configDir = dirname(configPath);
123
+ ensureDir(configDir);
124
+ if (exists(configPath)) {
125
+ const existingContent = readFile(configPath);
126
+ const edits = [
127
+ // General section
128
+ ["general.preferredLang", config.general.preferredLang],
129
+ ["general.currentTool", config.general.currentTool]
130
+ ];
131
+ if (config.general.templateLang !== void 0) {
132
+ edits.push(["general.templateLang", config.general.templateLang]);
133
+ }
134
+ if (config.general.aiOutputLang !== void 0) {
135
+ edits.push(["general.aiOutputLang", config.general.aiOutputLang]);
136
+ }
137
+ edits.push(
138
+ ["claudeCode.enabled", config.claudeCode.enabled],
139
+ ["claudeCode.outputStyles", config.claudeCode.outputStyles],
140
+ ["claudeCode.installType", config.claudeCode.installType]
141
+ );
142
+ if (config.claudeCode.defaultOutputStyle !== void 0) {
143
+ edits.push(["claudeCode.defaultOutputStyle", config.claudeCode.defaultOutputStyle]);
144
+ }
145
+ if (config.claudeCode.currentProfile !== void 0) {
146
+ edits.push(["claudeCode.currentProfile", config.claudeCode.currentProfile]);
147
+ }
148
+ if (config.claudeCode.profiles !== void 0) {
149
+ edits.push(["claudeCode.profiles", config.claudeCode.profiles]);
150
+ }
151
+ if (config.claudeCode.version !== void 0) {
152
+ edits.push(["claudeCode.version", config.claudeCode.version]);
153
+ }
154
+ try {
155
+ let updatedContent = batchEditToml(existingContent, edits);
156
+ updatedContent = updateTopLevelTomlFields(
157
+ updatedContent,
158
+ config.version,
159
+ config.lastUpdated
160
+ );
161
+ const expectedProfiles = Object.keys(config.claudeCode.profiles || {}).length;
162
+ if (expectedProfiles > 0) {
163
+ const verify = parseToml(updatedContent);
164
+ const actualProfiles = Object.keys(verify?.claudeCode?.profiles || {}).length;
165
+ if (actualProfiles !== expectedProfiles) {
166
+ throw new Error("Incremental edit produced inconsistent profiles");
167
+ }
168
+ }
169
+ writeFile(configPath, updatedContent);
170
+ } catch {
171
+ const tomlContent = stringifyToml(config);
172
+ writeFile(configPath, tomlContent);
173
+ }
174
+ } else {
175
+ const tomlContent = stringifyToml(config);
176
+ writeFile(configPath, tomlContent);
177
+ }
178
+ } catch {
179
+ }
180
+ }
181
+ function createDefaultTomlConfig(preferredLang = "en", claudeCodeInstallType = "global") {
182
+ return {
183
+ version: "1.0.0",
184
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
185
+ general: {
186
+ preferredLang,
187
+ templateLang: preferredLang,
188
+ // Default templateLang to preferredLang for new installations
189
+ aiOutputLang: preferredLang === "zh-CN" ? "zh-CN" : void 0,
190
+ currentTool: DEFAULT_CODE_TOOL_TYPE
191
+ },
192
+ claudeCode: {
193
+ enabled: true,
194
+ outputStyles: ["engineer-professional"],
195
+ defaultOutputStyle: "engineer-professional",
196
+ installType: claudeCodeInstallType,
197
+ currentProfile: "",
198
+ profiles: {}
199
+ }
200
+ };
201
+ }
202
+ function migrateFromJsonConfig(jsonConfig) {
203
+ const claudeCodeInstallType = jsonConfig.claudeCodeInstallation?.type || "global";
204
+ const defaultConfig = createDefaultTomlConfig("en", claudeCodeInstallType);
205
+ const tomlConfig = {
206
+ version: jsonConfig.version || defaultConfig.version,
207
+ lastUpdated: jsonConfig.lastUpdated || (/* @__PURE__ */ new Date()).toISOString(),
208
+ general: {
209
+ preferredLang: jsonConfig.preferredLang || defaultConfig.general.preferredLang,
210
+ templateLang: jsonConfig.templateLang || jsonConfig.preferredLang || defaultConfig.general.preferredLang,
211
+ // Backward compatibility: use preferredLang as default
212
+ aiOutputLang: jsonConfig.aiOutputLang || defaultConfig.general.aiOutputLang,
213
+ currentTool: jsonConfig.codeToolType || defaultConfig.general.currentTool
214
+ },
215
+ claudeCode: {
216
+ enabled: jsonConfig.codeToolType === "claude-code",
217
+ outputStyles: jsonConfig.outputStyles || defaultConfig.claudeCode.outputStyles,
218
+ defaultOutputStyle: jsonConfig.defaultOutputStyle ?? defaultConfig.claudeCode.defaultOutputStyle,
219
+ installType: claudeCodeInstallType,
220
+ currentProfile: jsonConfig.currentProfileId || defaultConfig.claudeCode.currentProfile,
221
+ profiles: jsonConfig.claudeCode?.profiles || {}
222
+ }
223
+ };
224
+ return tomlConfig;
225
+ }
226
+ function convertTomlToLegacyConfig(tomlConfig) {
227
+ return {
228
+ version: tomlConfig.version,
229
+ preferredLang: tomlConfig.general.preferredLang,
230
+ templateLang: tomlConfig.general.templateLang,
231
+ aiOutputLang: tomlConfig.general.aiOutputLang,
232
+ outputStyles: tomlConfig.claudeCode.outputStyles,
233
+ defaultOutputStyle: tomlConfig.claudeCode.defaultOutputStyle,
234
+ codeToolType: tomlConfig.general.currentTool,
235
+ lastUpdated: tomlConfig.lastUpdated
236
+ };
237
+ }
238
+ function convertLegacyToTomlConfig(legacyConfig) {
239
+ return migrateFromJsonConfig(legacyConfig);
240
+ }
241
+ function normalizeZcfConfig(config) {
242
+ if (!config) {
243
+ return null;
244
+ }
245
+ return {
246
+ version: typeof config.version === "string" ? config.version : "1.0.0",
247
+ preferredLang: sanitizePreferredLang(config.preferredLang),
248
+ templateLang: config.templateLang ? sanitizePreferredLang(config.templateLang) : void 0,
249
+ aiOutputLang: config.aiOutputLang,
250
+ outputStyles: Array.isArray(config.outputStyles) ? config.outputStyles : void 0,
251
+ defaultOutputStyle: typeof config.defaultOutputStyle === "string" ? config.defaultOutputStyle : void 0,
252
+ codeToolType: sanitizeCodeToolType(config.codeToolType),
253
+ lastUpdated: typeof config.lastUpdated === "string" ? config.lastUpdated : (/* @__PURE__ */ new Date()).toISOString()
254
+ };
255
+ }
256
+ function migrateZcfConfigIfNeeded() {
257
+ const target = ZCF_CONFIG_FILE;
258
+ const removed = [];
259
+ const targetExists = existsSync(target);
260
+ const legacySources = LEGACY_ZCF_CONFIG_FILES.filter((path) => existsSync(path));
261
+ if (!targetExists && legacySources.length > 0) {
262
+ const source = legacySources[0];
263
+ if (!existsSync(ZCF_CONFIG_DIR)) {
264
+ mkdirSync(ZCF_CONFIG_DIR, { recursive: true });
265
+ }
266
+ try {
267
+ renameSync(source, target);
268
+ } catch (error) {
269
+ if (error?.code !== "EXDEV") {
270
+ throw error;
271
+ }
272
+ copyFileSync(source, target);
273
+ rmSync(source, { force: true });
274
+ }
275
+ for (const leftover of legacySources.slice(1)) {
276
+ try {
277
+ rmSync(leftover, { force: true });
278
+ removed.push(leftover);
279
+ } catch {
280
+ }
281
+ }
282
+ return { migrated: true, source, target, removed };
283
+ }
284
+ if (targetExists && legacySources.length > 0) {
285
+ for (const source of legacySources) {
286
+ try {
287
+ rmSync(source, { force: true });
288
+ removed.push(source);
289
+ } catch {
290
+ }
291
+ }
292
+ return { migrated: false, target, removed };
293
+ }
294
+ return { migrated: false, target, removed };
295
+ }
296
+ function readZcfConfig() {
297
+ migrateZcfConfigIfNeeded();
298
+ const tomlConfig = readTomlConfig(ZCF_CONFIG_FILE);
299
+ if (tomlConfig) {
300
+ return convertTomlToLegacyConfig(tomlConfig);
301
+ }
302
+ const raw = readJsonConfig(ZCF_CONFIG_FILE.replace(".toml", ".json"));
303
+ const normalized = normalizeZcfConfig(raw || null);
304
+ if (normalized) {
305
+ return normalized;
306
+ }
307
+ for (const legacyPath of LEGACY_ZCF_CONFIG_FILES) {
308
+ if (existsSync(legacyPath)) {
309
+ const legacyRaw = readJsonConfig(legacyPath);
310
+ const legacyNormalized = normalizeZcfConfig(legacyRaw || null);
311
+ if (legacyNormalized) {
312
+ return legacyNormalized;
313
+ }
314
+ }
315
+ }
316
+ return null;
317
+ }
318
+ async function readZcfConfigAsync() {
319
+ return readZcfConfig();
320
+ }
321
+ function writeZcfConfig(config) {
322
+ try {
323
+ const sanitizedConfig = {
324
+ ...config,
325
+ codeToolType: sanitizeCodeToolType(config.codeToolType)
326
+ };
327
+ const existingTomlConfig = readTomlConfig(ZCF_CONFIG_FILE);
328
+ const tomlConfig = convertLegacyToTomlConfig(sanitizedConfig);
329
+ if (existingTomlConfig?.claudeCode) {
330
+ if (existingTomlConfig.claudeCode.profiles) {
331
+ tomlConfig.claudeCode.profiles = existingTomlConfig.claudeCode.profiles;
332
+ }
333
+ if (existingTomlConfig.claudeCode.currentProfile !== void 0) {
334
+ tomlConfig.claudeCode.currentProfile = existingTomlConfig.claudeCode.currentProfile;
335
+ }
336
+ if (existingTomlConfig.claudeCode.version) {
337
+ tomlConfig.claudeCode.version = existingTomlConfig.claudeCode.version;
338
+ }
339
+ }
340
+ writeTomlConfig(ZCF_CONFIG_FILE, tomlConfig);
341
+ } catch {
342
+ }
343
+ }
344
+ function updateZcfConfig(updates) {
345
+ const existingConfig = readZcfConfig();
346
+ const newConfig = {
347
+ version: updates.version || existingConfig?.version || "1.0.0",
348
+ preferredLang: updates.preferredLang || existingConfig?.preferredLang || "en",
349
+ templateLang: updates.templateLang !== void 0 ? updates.templateLang : existingConfig?.templateLang,
350
+ aiOutputLang: updates.aiOutputLang || existingConfig?.aiOutputLang,
351
+ outputStyles: updates.outputStyles !== void 0 ? updates.outputStyles : existingConfig?.outputStyles,
352
+ defaultOutputStyle: updates.defaultOutputStyle !== void 0 ? updates.defaultOutputStyle : existingConfig?.defaultOutputStyle,
353
+ codeToolType: updates.codeToolType || existingConfig?.codeToolType || DEFAULT_CODE_TOOL_TYPE,
354
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
355
+ };
356
+ writeZcfConfig(newConfig);
357
+ }
358
+ function readDefaultTomlConfig() {
359
+ return readTomlConfig(ZCF_CONFIG_FILE);
360
+ }
361
+
362
+ const CODE_TYPE_ABBREVIATIONS = {
363
+ cc: "claude-code"
364
+ };
365
+ async function resolveCodeType$1(codeTypeParam) {
366
+ if (codeTypeParam) {
367
+ const normalizedParam = codeTypeParam.toLowerCase().trim();
368
+ if (normalizedParam in CODE_TYPE_ABBREVIATIONS) {
369
+ return CODE_TYPE_ABBREVIATIONS[normalizedParam];
370
+ }
371
+ if (isValidCodeType(normalizedParam)) {
372
+ return normalizedParam;
373
+ }
374
+ const validAbbreviations = Object.keys(CODE_TYPE_ABBREVIATIONS);
375
+ const validFullTypes = Object.values(CODE_TYPE_ABBREVIATIONS);
376
+ const validOptions = [...validAbbreviations, ...validFullTypes].join(", ");
377
+ let defaultValue = DEFAULT_CODE_TOOL_TYPE;
378
+ try {
379
+ const config = await readZcfConfigAsync();
380
+ if (config?.codeToolType && isValidCodeType(config.codeToolType)) {
381
+ defaultValue = config.codeToolType;
382
+ }
383
+ } catch {
384
+ }
385
+ throw new Error(
386
+ i18n.t("errors:invalidCodeType", { value: codeTypeParam, validOptions, defaultValue })
387
+ );
388
+ }
389
+ try {
390
+ const config = await readZcfConfigAsync();
391
+ if (config?.codeToolType && isValidCodeType(config.codeToolType)) {
392
+ return config.codeToolType;
393
+ }
394
+ } catch {
395
+ }
396
+ return DEFAULT_CODE_TOOL_TYPE;
397
+ }
398
+ function isValidCodeType(value) {
399
+ return value === "claude-code";
400
+ }
401
+
402
+ class ToolUpdateScheduler {
403
+ /**
404
+ * Update tools based on code type
405
+ * @param codeType - The code tool type to update
406
+ * @param skipPrompt - Whether to skip interactive prompts
407
+ */
408
+ async updateByCodeType(codeType, skipPrompt = false) {
409
+ await ensureI18nInitialized();
410
+ switch (codeType) {
411
+ case "claude-code":
412
+ await this.updateClaudeCodeTools(skipPrompt);
413
+ break;
414
+ default:
415
+ throw new Error(`Unsupported code type: ${codeType}`);
416
+ }
417
+ }
418
+ /**
419
+ * Update Claude Code related tools
420
+ * @param skipPrompt - Whether to skip interactive prompts
421
+ */
422
+ async updateClaudeCodeTools(skipPrompt) {
423
+ await checkAndUpdateTools(skipPrompt);
424
+ }
425
+ }
426
+
427
+ async function checkUpdates(options = {}) {
428
+ try {
429
+ const skipPrompt = options.skipPrompt || false;
430
+ let codeType;
431
+ try {
432
+ codeType = await resolveCodeType$1(options.codeType);
433
+ } catch (err) {
434
+ const errorMessage = err instanceof Error ? err.message : String(err);
435
+ console.error(ansis.red(`${errorMessage}
436
+ Defaulting to "claude-code".`));
437
+ codeType = "claude-code";
438
+ }
439
+ const scheduler = new ToolUpdateScheduler();
440
+ await scheduler.updateByCodeType(codeType, skipPrompt);
441
+ } catch (error) {
442
+ const errorMessage = error instanceof Error ? error.message : String(error);
443
+ console.error(ansis.red(`${i18n.t("updater:errorCheckingUpdates")} ${errorMessage}`));
444
+ process.exit(1);
445
+ }
446
+ }
447
+
448
+ class ClaudeCodeConfigManager {
449
+ static CONFIG_FILE = ZCF_CONFIG_FILE;
450
+ static LEGACY_CONFIG_FILE = join(ZCF_CONFIG_DIR, "claude-code-configs.json");
451
+ /**
452
+ * Ensure configuration directory exists
453
+ */
454
+ static ensureConfigDir() {
455
+ ensureDir(ZCF_CONFIG_DIR);
456
+ }
457
+ /**
458
+ * Read TOML configuration
459
+ */
460
+ static readTomlConfig() {
461
+ return readDefaultTomlConfig();
462
+ }
463
+ /**
464
+ * Load TOML configuration, falling back to default when missing
465
+ */
466
+ static loadTomlConfig() {
467
+ const existingConfig = this.readTomlConfig();
468
+ if (existingConfig) {
469
+ return existingConfig;
470
+ }
471
+ return createDefaultTomlConfig();
472
+ }
473
+ /**
474
+ * Migrate legacy JSON-based configuration into TOML storage
475
+ */
476
+ static migrateFromLegacyConfig() {
477
+ if (!exists(this.LEGACY_CONFIG_FILE)) {
478
+ return null;
479
+ }
480
+ try {
481
+ const legacyConfig = readJsonConfig(this.LEGACY_CONFIG_FILE);
482
+ if (!legacyConfig) {
483
+ return null;
484
+ }
485
+ const normalizedProfiles = {};
486
+ const existingKeys = /* @__PURE__ */ new Set();
487
+ let migratedCurrentKey = "";
488
+ Object.entries(legacyConfig.profiles || {}).forEach(([legacyKey, profile]) => {
489
+ const sourceProfile = profile;
490
+ const name = sourceProfile.name?.trim() || legacyKey;
491
+ const baseKey = this.generateProfileId(name);
492
+ let uniqueKey = baseKey || legacyKey;
493
+ let suffix = 2;
494
+ while (existingKeys.has(uniqueKey)) {
495
+ uniqueKey = `${baseKey || legacyKey}-${suffix++}`;
496
+ }
497
+ existingKeys.add(uniqueKey);
498
+ const sanitizedProfile = this.sanitizeProfile({
499
+ ...sourceProfile,
500
+ name
501
+ });
502
+ normalizedProfiles[uniqueKey] = {
503
+ ...sanitizedProfile,
504
+ id: uniqueKey
505
+ };
506
+ if (legacyConfig.currentProfileId === legacyKey || legacyConfig.currentProfileId === sourceProfile.id) {
507
+ migratedCurrentKey = uniqueKey;
508
+ }
509
+ });
510
+ if (!migratedCurrentKey && legacyConfig.currentProfileId) {
511
+ const fallbackKey = this.generateProfileId(legacyConfig.currentProfileId);
512
+ if (existingKeys.has(fallbackKey)) {
513
+ migratedCurrentKey = fallbackKey;
514
+ }
515
+ }
516
+ if (!migratedCurrentKey && existingKeys.size > 0) {
517
+ migratedCurrentKey = Array.from(existingKeys)[0];
518
+ }
519
+ const migratedConfig = {
520
+ currentProfileId: migratedCurrentKey,
521
+ profiles: normalizedProfiles
522
+ };
523
+ this.writeConfig(migratedConfig);
524
+ return migratedConfig;
525
+ } catch (error) {
526
+ console.error("Failed to migrate legacy Claude Code config:", error);
527
+ return null;
528
+ }
529
+ }
530
+ /**
531
+ * Read configuration
532
+ */
533
+ static readConfig() {
534
+ try {
535
+ const tomlConfig = readDefaultTomlConfig();
536
+ if (!tomlConfig || !tomlConfig.claudeCode) {
537
+ return this.migrateFromLegacyConfig();
538
+ }
539
+ const { claudeCode } = tomlConfig;
540
+ const rawProfiles = claudeCode.profiles || {};
541
+ const sanitizedProfiles = Object.fromEntries(
542
+ Object.entries(rawProfiles).map(([key, profile]) => {
543
+ const storedProfile = this.sanitizeProfile({
544
+ ...profile,
545
+ name: profile.name || key
546
+ });
547
+ return [key, { ...storedProfile, id: key }];
548
+ })
549
+ );
550
+ const configData = {
551
+ currentProfileId: claudeCode.currentProfile || "",
552
+ profiles: sanitizedProfiles
553
+ };
554
+ if (Object.keys(configData.profiles).length === 0) {
555
+ const migrated = this.migrateFromLegacyConfig();
556
+ if (migrated) {
557
+ return migrated;
558
+ }
559
+ }
560
+ return configData;
561
+ } catch (error) {
562
+ console.error("Failed to read Claude Code config:", error);
563
+ return null;
564
+ }
565
+ }
566
+ /**
567
+ * Write configuration
568
+ */
569
+ static writeConfig(config) {
570
+ try {
571
+ this.ensureConfigDir();
572
+ const keyMap = /* @__PURE__ */ new Map();
573
+ const sanitizedProfiles = Object.fromEntries(
574
+ Object.entries(config.profiles).map(([key, profile]) => {
575
+ const normalizedName = profile.name?.trim() || key;
576
+ const profileKey = this.generateProfileId(normalizedName);
577
+ keyMap.set(key, profileKey);
578
+ const sanitizedProfile = this.sanitizeProfile({
579
+ ...profile,
580
+ name: normalizedName
581
+ });
582
+ return [profileKey, sanitizedProfile];
583
+ })
584
+ );
585
+ const tomlConfig = this.loadTomlConfig();
586
+ const nextTomlConfig = {
587
+ ...tomlConfig,
588
+ claudeCode: {
589
+ ...tomlConfig.claudeCode,
590
+ currentProfile: keyMap.get(config.currentProfileId) || config.currentProfileId,
591
+ profiles: sanitizedProfiles
592
+ }
593
+ };
594
+ writeTomlConfig(this.CONFIG_FILE, nextTomlConfig);
595
+ } catch (error) {
596
+ console.error("Failed to write Claude Code config:", error);
597
+ throw new Error(`Failed to write config: ${error instanceof Error ? error.message : String(error)}`);
598
+ }
599
+ }
600
+ /**
601
+ * Create empty configuration
602
+ */
603
+ static createEmptyConfig() {
604
+ return {
605
+ currentProfileId: "",
606
+ profiles: {}
607
+ };
608
+ }
609
+ /**
610
+ * Apply profile settings to Claude Code runtime
611
+ */
612
+ static async applyProfileSettings(profile) {
613
+ const { ensureI18nInitialized, i18n } = await import('./chunks/auto-updater.mjs').then(function (n) { return n.a8; });
614
+ ensureI18nInitialized();
615
+ try {
616
+ if (!profile) {
617
+ const { switchToOfficialLogin } = await import('./chunks/auto-updater.mjs').then(function (n) { return n.ab; });
618
+ switchToOfficialLogin();
619
+ return;
620
+ }
621
+ const { readJsonConfig: readJsonConfig2, writeJsonConfig } = await import('./chunks/auto-updater.mjs').then(function (n) { return n.a9; });
622
+ const settings = readJsonConfig2(SETTINGS_FILE) || {};
623
+ if (!settings.env)
624
+ settings.env = {};
625
+ clearModelEnv(settings.env);
626
+ if (profile.authType === "api_key") {
627
+ settings.env.ANTHROPIC_API_KEY = profile.apiKey;
628
+ delete settings.env.ANTHROPIC_AUTH_TOKEN;
629
+ } else if (profile.authType === "auth_token") {
630
+ settings.env.ANTHROPIC_AUTH_TOKEN = profile.apiKey;
631
+ delete settings.env.ANTHROPIC_API_KEY;
632
+ }
633
+ if (profile.baseUrl)
634
+ settings.env.ANTHROPIC_BASE_URL = profile.baseUrl;
635
+ else
636
+ delete settings.env.ANTHROPIC_BASE_URL;
637
+ const hasModelConfig = Boolean(
638
+ profile.primaryModel || profile.defaultHaikuModel || profile.defaultSonnetModel || profile.defaultOpusModel
639
+ );
640
+ if (hasModelConfig) {
641
+ if (profile.primaryModel)
642
+ settings.env.ANTHROPIC_MODEL = profile.primaryModel;
643
+ if (profile.defaultHaikuModel)
644
+ settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = profile.defaultHaikuModel;
645
+ if (profile.defaultSonnetModel)
646
+ settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = profile.defaultSonnetModel;
647
+ if (profile.defaultOpusModel)
648
+ settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = profile.defaultOpusModel;
649
+ } else {
650
+ clearModelEnv(settings.env);
651
+ }
652
+ writeJsonConfig(SETTINGS_FILE, settings);
653
+ const { setPrimaryApiKey, addCompletedOnboarding } = await import('./chunks/auto-updater.mjs').then(function (n) { return n.aa; });
654
+ setPrimaryApiKey();
655
+ addCompletedOnboarding();
656
+ } catch (error) {
657
+ const reason = error instanceof Error ? error.message : String(error);
658
+ throw new Error(`${i18n.t("multi-config:failedToApplySettings")}: ${reason}`);
659
+ }
660
+ }
661
+ static async applyCurrentProfile() {
662
+ const currentProfile = this.getCurrentProfile();
663
+ await this.applyProfileSettings(currentProfile);
664
+ }
665
+ /**
666
+ * Remove unsupported fields from profile payload
667
+ */
668
+ static sanitizeProfile(profile) {
669
+ const sanitized = {
670
+ name: profile.name,
671
+ authType: profile.authType
672
+ };
673
+ if (profile.apiKey)
674
+ sanitized.apiKey = profile.apiKey;
675
+ if (profile.baseUrl)
676
+ sanitized.baseUrl = profile.baseUrl;
677
+ if (profile.primaryModel)
678
+ sanitized.primaryModel = profile.primaryModel;
679
+ if (profile.defaultHaikuModel)
680
+ sanitized.defaultHaikuModel = profile.defaultHaikuModel;
681
+ if (profile.defaultSonnetModel)
682
+ sanitized.defaultSonnetModel = profile.defaultSonnetModel;
683
+ if (profile.defaultOpusModel)
684
+ sanitized.defaultOpusModel = profile.defaultOpusModel;
685
+ return sanitized;
686
+ }
687
+ /**
688
+ * Backup configuration
689
+ */
690
+ static backupConfig() {
691
+ try {
692
+ if (!exists(this.CONFIG_FILE)) {
693
+ return null;
694
+ }
695
+ const timestamp = dayjs().format("YYYY-MM-DD_HH-mm-ss");
696
+ const backupPath = join(ZCF_CONFIG_DIR, `config.backup.${timestamp}.toml`);
697
+ copyFile(this.CONFIG_FILE, backupPath);
698
+ return backupPath;
699
+ } catch (error) {
700
+ console.error("Failed to backup Claude Code config:", error);
701
+ return null;
702
+ }
703
+ }
704
+ /**
705
+ * Add configuration
706
+ */
707
+ static async addProfile(profile) {
708
+ try {
709
+ const validationErrors = this.validateProfile(profile);
710
+ if (validationErrors.length > 0) {
711
+ return {
712
+ success: false,
713
+ error: `Validation failed: ${validationErrors.join(", ")}`
714
+ };
715
+ }
716
+ const backupPath = this.backupConfig();
717
+ let config = this.readConfig();
718
+ if (!config) {
719
+ config = this.createEmptyConfig();
720
+ }
721
+ if (profile.id && config.profiles[profile.id]) {
722
+ return {
723
+ success: false,
724
+ error: `Profile with ID "${profile.id}" already exists`,
725
+ backupPath: backupPath || void 0
726
+ };
727
+ }
728
+ const normalizedName = profile.name.trim();
729
+ const profileKey = this.generateProfileId(normalizedName);
730
+ const existingNames = Object.values(config.profiles).map((p) => p.name || "");
731
+ if (config.profiles[profileKey] || existingNames.some((name) => name.toLowerCase() === normalizedName.toLowerCase())) {
732
+ return {
733
+ success: false,
734
+ error: `Profile with name "${profile.name}" already exists`,
735
+ backupPath: backupPath || void 0
736
+ };
737
+ }
738
+ const sanitizedProfile = this.sanitizeProfile({
739
+ ...profile,
740
+ name: normalizedName
741
+ });
742
+ const runtimeProfile = {
743
+ ...sanitizedProfile,
744
+ id: profileKey
745
+ };
746
+ config.profiles[profileKey] = runtimeProfile;
747
+ if (!config.currentProfileId) {
748
+ config.currentProfileId = profileKey;
749
+ }
750
+ this.writeConfig(config);
751
+ return {
752
+ success: true,
753
+ backupPath: backupPath || void 0,
754
+ addedProfile: runtimeProfile
755
+ };
756
+ } catch (error) {
757
+ return {
758
+ success: false,
759
+ error: error instanceof Error ? error.message : String(error)
760
+ };
761
+ }
762
+ }
763
+ /**
764
+ * Update configuration
765
+ */
766
+ static async updateProfile(id, data) {
767
+ try {
768
+ const validationErrors = this.validateProfile(data, true);
769
+ if (validationErrors.length > 0) {
770
+ return {
771
+ success: false,
772
+ error: `Validation failed: ${validationErrors.join(", ")}`
773
+ };
774
+ }
775
+ const backupPath = this.backupConfig();
776
+ const config = this.readConfig();
777
+ if (!config || !config.profiles[id]) {
778
+ return {
779
+ success: false,
780
+ error: `Profile with ID "${id}" not found`,
781
+ backupPath: backupPath || void 0
782
+ };
783
+ }
784
+ const existingProfile = config.profiles[id];
785
+ const nextName = data.name !== void 0 ? data.name.trim() : existingProfile.name;
786
+ const nextKey = this.generateProfileId(nextName);
787
+ const nameChanged = nextKey !== id;
788
+ if (nameChanged) {
789
+ const duplicateName = Object.entries(config.profiles).some(([key, profile]) => key !== id && (profile.name || "").toLowerCase() === nextName.toLowerCase());
790
+ if (duplicateName || config.profiles[nextKey]) {
791
+ return {
792
+ success: false,
793
+ error: `Profile with name "${data.name}" already exists`,
794
+ backupPath: backupPath || void 0
795
+ };
796
+ }
797
+ }
798
+ const mergedProfile = this.sanitizeProfile({
799
+ ...existingProfile,
800
+ ...data,
801
+ name: nextName
802
+ });
803
+ if (nameChanged) {
804
+ delete config.profiles[id];
805
+ config.profiles[nextKey] = {
806
+ ...mergedProfile,
807
+ id: nextKey
808
+ };
809
+ if (config.currentProfileId === id) {
810
+ config.currentProfileId = nextKey;
811
+ }
812
+ } else {
813
+ config.profiles[id] = {
814
+ ...mergedProfile,
815
+ id
816
+ };
817
+ }
818
+ this.writeConfig(config);
819
+ return {
820
+ success: true,
821
+ backupPath: backupPath || void 0,
822
+ updatedProfile: {
823
+ ...mergedProfile,
824
+ id: nameChanged ? nextKey : id
825
+ }
826
+ };
827
+ } catch (error) {
828
+ return {
829
+ success: false,
830
+ error: error instanceof Error ? error.message : String(error)
831
+ };
832
+ }
833
+ }
834
+ /**
835
+ * Delete configuration
836
+ */
837
+ static async deleteProfile(id) {
838
+ try {
839
+ const backupPath = this.backupConfig();
840
+ const config = this.readConfig();
841
+ if (!config || !config.profiles[id]) {
842
+ return {
843
+ success: false,
844
+ error: `Profile with ID "${id}" not found`,
845
+ backupPath: backupPath || void 0
846
+ };
847
+ }
848
+ const profileCount = Object.keys(config.profiles).length;
849
+ if (profileCount === 1) {
850
+ return {
851
+ success: false,
852
+ error: "Cannot delete the last profile. At least one profile must remain.",
853
+ backupPath: backupPath || void 0
854
+ };
855
+ }
856
+ delete config.profiles[id];
857
+ if (config.currentProfileId === id) {
858
+ const remainingIds = Object.keys(config.profiles);
859
+ config.currentProfileId = remainingIds[0];
860
+ }
861
+ this.writeConfig(config);
862
+ return {
863
+ success: true,
864
+ backupPath: backupPath || void 0,
865
+ remainingProfiles: Object.entries(config.profiles).map(([key, profile]) => ({
866
+ ...profile,
867
+ id: key
868
+ }))
869
+ };
870
+ } catch (error) {
871
+ return {
872
+ success: false,
873
+ error: error instanceof Error ? error.message : String(error)
874
+ };
875
+ }
876
+ }
877
+ /**
878
+ * Delete multiple configurations
879
+ */
880
+ static async deleteProfiles(ids) {
881
+ try {
882
+ const backupPath = this.backupConfig();
883
+ const config = this.readConfig();
884
+ if (!config) {
885
+ return {
886
+ success: false,
887
+ error: "No configuration found",
888
+ backupPath: backupPath || void 0
889
+ };
890
+ }
891
+ const missingIds = ids.filter((id) => !config.profiles[id]);
892
+ if (missingIds.length > 0) {
893
+ return {
894
+ success: false,
895
+ error: `Profiles not found: ${missingIds.join(", ")}`,
896
+ backupPath: backupPath || void 0
897
+ };
898
+ }
899
+ const remainingCount = Object.keys(config.profiles).length - ids.length;
900
+ if (remainingCount === 0) {
901
+ return {
902
+ success: false,
903
+ error: "Cannot delete all profiles. At least one profile must remain.",
904
+ backupPath: backupPath || void 0
905
+ };
906
+ }
907
+ let newCurrentProfileId;
908
+ ids.forEach((id) => {
909
+ delete config.profiles[id];
910
+ });
911
+ if (ids.includes(config.currentProfileId)) {
912
+ const remainingIds = Object.keys(config.profiles);
913
+ config.currentProfileId = remainingIds[0];
914
+ newCurrentProfileId = config.currentProfileId;
915
+ }
916
+ this.writeConfig(config);
917
+ return {
918
+ success: true,
919
+ backupPath: backupPath || void 0,
920
+ newCurrentProfileId,
921
+ deletedProfiles: ids,
922
+ remainingProfiles: Object.entries(config.profiles).map(([key, profile]) => ({
923
+ ...profile,
924
+ id: key
925
+ }))
926
+ };
927
+ } catch (error) {
928
+ return {
929
+ success: false,
930
+ error: error instanceof Error ? error.message : String(error)
931
+ };
932
+ }
933
+ }
934
+ /**
935
+ * Generate profile ID from name
936
+ */
937
+ static generateProfileId(name) {
938
+ return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "profile";
939
+ }
940
+ /**
941
+ * Switch configuration
942
+ */
943
+ static async switchProfile(id) {
944
+ try {
945
+ const config = this.readConfig();
946
+ if (!config || !config.profiles[id]) {
947
+ return {
948
+ success: false,
949
+ error: "Profile not found"
950
+ };
951
+ }
952
+ if (config.currentProfileId === id) {
953
+ return { success: true };
954
+ }
955
+ config.currentProfileId = id;
956
+ this.writeConfig(config);
957
+ return { success: true };
958
+ } catch (error) {
959
+ return {
960
+ success: false,
961
+ error: error instanceof Error ? error.message : String(error)
962
+ };
963
+ }
964
+ }
965
+ /**
966
+ * List all configurations
967
+ */
968
+ static listProfiles() {
969
+ const config = this.readConfig();
970
+ if (!config) {
971
+ return [];
972
+ }
973
+ return Object.values(config.profiles);
974
+ }
975
+ /**
976
+ * Get current configuration
977
+ */
978
+ static getCurrentProfile() {
979
+ const config = this.readConfig();
980
+ if (!config || !config.currentProfileId) {
981
+ return null;
982
+ }
983
+ return config.profiles[config.currentProfileId] || null;
984
+ }
985
+ /**
986
+ * Get configuration by ID
987
+ */
988
+ static getProfileById(id) {
989
+ const config = this.readConfig();
990
+ if (!config) {
991
+ return null;
992
+ }
993
+ return config.profiles[id] || null;
994
+ }
995
+ /**
996
+ * Get configuration by name
997
+ */
998
+ static getProfileByName(name) {
999
+ const config = this.readConfig();
1000
+ if (!config) {
1001
+ return null;
1002
+ }
1003
+ return Object.values(config.profiles).find((p) => p.name === name) || null;
1004
+ }
1005
+ /**
1006
+ * Switch to official login
1007
+ */
1008
+ static async switchToOfficial() {
1009
+ try {
1010
+ const config = this.readConfig();
1011
+ if (!config) {
1012
+ return { success: true };
1013
+ }
1014
+ config.currentProfileId = "";
1015
+ this.writeConfig(config);
1016
+ return { success: true };
1017
+ } catch (error) {
1018
+ return {
1019
+ success: false,
1020
+ error: error instanceof Error ? error.message : String(error)
1021
+ };
1022
+ }
1023
+ }
1024
+ /**
1025
+ * Validate configuration
1026
+ */
1027
+ static validateProfile(profile, isUpdate = false) {
1028
+ const errors = [];
1029
+ if (!isUpdate && (!profile.name || typeof profile.name !== "string" || profile.name.trim() === "")) {
1030
+ errors.push("Profile name is required");
1031
+ }
1032
+ if (profile.name && typeof profile.name !== "string") {
1033
+ errors.push("Profile name must be a string");
1034
+ }
1035
+ if (profile.authType && !["api_key", "auth_token", "ccr_proxy"].includes(profile.authType)) {
1036
+ errors.push("Invalid auth type. Must be one of: api_key, auth_token, ccr_proxy");
1037
+ }
1038
+ if (profile.authType === "api_key" || profile.authType === "auth_token") {
1039
+ if (!profile.apiKey || typeof profile.apiKey !== "string" || profile.apiKey.trim() === "") {
1040
+ errors.push("API key is required for api_key and auth_token types");
1041
+ }
1042
+ }
1043
+ if (profile.baseUrl) {
1044
+ try {
1045
+ new URL(profile.baseUrl);
1046
+ } catch {
1047
+ errors.push("Invalid base URL format");
1048
+ }
1049
+ }
1050
+ return errors;
1051
+ }
1052
+ /**
1053
+ * 检查是否为最后一个配置
1054
+ */
1055
+ static isLastProfile(id) {
1056
+ const config = this.readConfig();
1057
+ if (!config || !config.profiles[id]) {
1058
+ return false;
1059
+ }
1060
+ return Object.keys(config.profiles).length === 1;
1061
+ }
1062
+ }
1063
+
1064
+ function handleExitPromptError(error) {
1065
+ const isExitError = error instanceof Error && (error.name === "ExitPromptError" || error.message?.includes("ExitPromptError") || error.message?.includes("User force closed the prompt"));
1066
+ if (isExitError) {
1067
+ ensureI18nInitialized();
1068
+ console.log(ansis.cyan(`
1069
+ ${i18n.t("common:goodbye")}
1070
+ `));
1071
+ process.exit(0);
1072
+ }
1073
+ return false;
1074
+ }
1075
+ function handleGeneralError(error) {
1076
+ ensureI18nInitialized();
1077
+ console.error(ansis.red(`${i18n.t("errors:generalError")}:`), error);
1078
+ if (error instanceof Error) {
1079
+ console.error(ansis.gray(`${i18n.t("errors:stackTrace")}: ${error.stack}`));
1080
+ }
1081
+ process.exit(1);
1082
+ }
1083
+
1084
+ function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
1085
+ let currentNumber = startFrom;
1086
+ return choices.map((choice) => {
1087
+ if (choice.disabled) {
1088
+ return choice;
1089
+ }
1090
+ const numbered = {
1091
+ ...choice,
1092
+ name: `${format(currentNumber)}${choice.name}`
1093
+ };
1094
+ currentNumber++;
1095
+ return numbered;
1096
+ });
1097
+ }
1098
+
1099
+ async function configSwitchCommand(options) {
1100
+ try {
1101
+ ensureI18nInitialized();
1102
+ if (options.list) {
1103
+ await handleList(options.codeType);
1104
+ return;
1105
+ }
1106
+ if (options.target) {
1107
+ const resolvedCodeType = resolveCodeType(options.codeType);
1108
+ await handleDirectSwitch(resolvedCodeType, options.target);
1109
+ return;
1110
+ }
1111
+ await handleInteractiveSwitch(options.codeType);
1112
+ } catch (error) {
1113
+ if (process.env.NODE_ENV === "test" || process.env.VITEST) {
1114
+ throw error;
1115
+ }
1116
+ handleGeneralError(error);
1117
+ }
1118
+ }
1119
+ function resolveCodeType(codeType) {
1120
+ if (codeType !== void 0) {
1121
+ const resolved = resolveCodeToolType(codeType);
1122
+ return resolved;
1123
+ }
1124
+ const zcfConfig = readZcfConfig();
1125
+ if (zcfConfig?.codeToolType && isCodeToolType(zcfConfig.codeToolType)) {
1126
+ return zcfConfig.codeToolType;
1127
+ }
1128
+ return DEFAULT_CODE_TOOL_TYPE;
1129
+ }
1130
+ async function handleList(codeType) {
1131
+ resolveCodeType(codeType);
1132
+ await listClaudeCodeProfiles();
1133
+ }
1134
+ async function listClaudeCodeProfiles() {
1135
+ const config = ClaudeCodeConfigManager.readConfig();
1136
+ if (!config || !config.profiles || Object.keys(config.profiles).length === 0) {
1137
+ console.log(ansis.yellow(i18n.t("multi-config:noClaudeCodeProfilesAvailable")));
1138
+ return;
1139
+ }
1140
+ console.log(ansis.bold(i18n.t("multi-config:availableClaudeCodeProfiles")));
1141
+ console.log();
1142
+ const currentProfileId = config.currentProfileId;
1143
+ Object.values(config.profiles).forEach((profile) => {
1144
+ const isCurrent = profile.id === currentProfileId;
1145
+ const status = isCurrent ? ansis.green("\u25CF ") : " ";
1146
+ const current = isCurrent ? ansis.yellow(i18n.t("common:current")) : "";
1147
+ console.log(`${status}${ansis.white(profile.name)}${current}`);
1148
+ console.log(` ${ansis.cyan(`ID: ${profile.id}`)} ${ansis.gray(`(${profile.authType})`)}`);
1149
+ console.log();
1150
+ });
1151
+ }
1152
+ async function handleDirectSwitch(codeType, target) {
1153
+ resolveCodeType(codeType);
1154
+ await handleClaudeCodeDirectSwitch(target);
1155
+ }
1156
+ async function handleClaudeCodeDirectSwitch(target) {
1157
+ if (target === "official") {
1158
+ const result = await ClaudeCodeConfigManager.switchToOfficial();
1159
+ if (result.success) {
1160
+ try {
1161
+ await ClaudeCodeConfigManager.applyProfileSettings(null);
1162
+ console.log(ansis.green(i18n.t("multi-config:successfullySwitchedToOfficial")));
1163
+ } catch (error) {
1164
+ const reason = error instanceof Error ? error.message : String(error);
1165
+ console.log(ansis.red(reason));
1166
+ }
1167
+ } else {
1168
+ console.log(ansis.red(i18n.t("multi-config:failedToSwitchToOfficial", { error: result.error })));
1169
+ }
1170
+ } else {
1171
+ const config = ClaudeCodeConfigManager.readConfig();
1172
+ if (!config || !config.profiles || Object.keys(config.profiles).length === 0) {
1173
+ console.log(ansis.yellow(i18n.t("multi-config:noClaudeCodeProfilesAvailable")));
1174
+ return;
1175
+ }
1176
+ const normalizedTarget = target.trim();
1177
+ let resolvedId = normalizedTarget;
1178
+ let resolvedProfile = config.profiles[normalizedTarget];
1179
+ if (!resolvedProfile) {
1180
+ const match = Object.entries(config.profiles).find(([, profile]) => profile.name === normalizedTarget);
1181
+ if (match) {
1182
+ resolvedId = match[0];
1183
+ resolvedProfile = match[1];
1184
+ }
1185
+ }
1186
+ if (!resolvedProfile) {
1187
+ console.log(ansis.red(i18n.t("multi-config:profileNameNotFound", { name: target })));
1188
+ return;
1189
+ }
1190
+ const result = await ClaudeCodeConfigManager.switchProfile(resolvedId);
1191
+ if (result.success) {
1192
+ try {
1193
+ await ClaudeCodeConfigManager.applyProfileSettings({ ...resolvedProfile, id: resolvedId });
1194
+ console.log(ansis.green(i18n.t("multi-config:successfullySwitchedToProfile", { name: resolvedProfile.name })));
1195
+ } catch (error) {
1196
+ const reason = error instanceof Error ? error.message : String(error);
1197
+ console.log(ansis.red(reason));
1198
+ }
1199
+ } else {
1200
+ console.log(ansis.red(i18n.t("multi-config:failedToSwitchToProfile", { error: result.error })));
1201
+ }
1202
+ }
1203
+ }
1204
+ async function handleInteractiveSwitch(codeType) {
1205
+ resolveCodeType(codeType);
1206
+ await handleClaudeCodeInteractiveSwitch();
1207
+ }
1208
+ async function handleClaudeCodeInteractiveSwitch() {
1209
+ const config = ClaudeCodeConfigManager.readConfig();
1210
+ if (!config || !config.profiles || Object.keys(config.profiles).length === 0) {
1211
+ console.log(ansis.yellow(i18n.t("multi-config:noClaudeCodeProfilesAvailable")));
1212
+ return;
1213
+ }
1214
+ const currentProfileId = config.currentProfileId;
1215
+ const createClaudeCodeChoices = (profiles, currentProfileId2) => {
1216
+ const choices2 = [];
1217
+ const isOfficialMode = !currentProfileId2 || currentProfileId2 === "official";
1218
+ choices2.push({
1219
+ name: isOfficialMode ? `${ansis.green("\u25CF ")}${i18n.t("api:useOfficialLogin")} ${ansis.yellow(`(${i18n.t("common:current")})`)}` : ` ${i18n.t("api:useOfficialLogin")}`,
1220
+ value: "official"
1221
+ });
1222
+ Object.values(profiles).forEach((profile) => {
1223
+ const isCurrent = profile.id === currentProfileId2;
1224
+ choices2.push({
1225
+ name: isCurrent ? `${ansis.green("\u25CF ")}${profile.name} ${ansis.yellow("(current)")}` : ` ${profile.name}`,
1226
+ value: profile.id
1227
+ });
1228
+ });
1229
+ return choices2;
1230
+ };
1231
+ const choices = createClaudeCodeChoices(config.profiles, currentProfileId);
1232
+ try {
1233
+ const { selectedConfig } = await inquirer.prompt([{
1234
+ type: "list",
1235
+ name: "selectedConfig",
1236
+ message: i18n.t("multi-config:selectClaudeCodeConfiguration"),
1237
+ choices: addNumbersToChoices(choices)
1238
+ }]);
1239
+ if (!selectedConfig) {
1240
+ console.log(ansis.yellow(i18n.t("multi-config:cancelled")));
1241
+ return;
1242
+ }
1243
+ await handleClaudeCodeDirectSwitch(selectedConfig);
1244
+ } catch (error) {
1245
+ if (error.name === "ExitPromptError") {
1246
+ console.log(ansis.cyan(`
1247
+ ${i18n.t("common:goodbye")}`));
1248
+ return;
1249
+ }
1250
+ throw error;
1251
+ }
1252
+ }
1253
+
1254
+ const configSwitch = {
1255
+ __proto__: null,
1256
+ configSwitchCommand: configSwitchCommand
1257
+ };
1258
+
1259
+ function getDisplayWidth(str) {
1260
+ let width = 0;
1261
+ for (const char of str) {
1262
+ if (char.match(/[\u4E00-\u9FFF\uFF01-\uFF60\u3000-\u303F]/)) {
1263
+ width += 2;
1264
+ } else {
1265
+ width += 1;
1266
+ }
1267
+ }
1268
+ return width;
1269
+ }
1270
+ function padToDisplayWidth(str, targetWidth) {
1271
+ const currentWidth = getDisplayWidth(str);
1272
+ const paddingNeeded = Math.max(0, targetWidth - currentWidth);
1273
+ return str + " ".repeat(paddingNeeded);
1274
+ }
1275
+ function displayBanner(subtitle) {
1276
+ ensureI18nInitialized();
1277
+ i18n.t("cli:banner.subtitle");
1278
+ const subtitleText = subtitle;
1279
+ const paddedSubtitle = padToDisplayWidth(subtitleText, 30);
1280
+ const paddedTitle = padToDisplayWidth("Claude Code Switch", 60);
1281
+ console.log(
1282
+ ansis.cyan.bold(`
1283
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
1284
+ \u2551 \u2551
1285
+ \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
1286
+ \u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2551
1287
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
1288
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2551
1289
+ \u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2551
1290
+ \u2551 \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D ${ansis.gray(paddedSubtitle)} \u2551
1291
+ \u2551 \u2551
1292
+ \u2551 ${ansis.white.bold(paddedTitle)} \u2551
1293
+ \u2551 \u2551
1294
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
1295
+ `)
1296
+ );
1297
+ }
1298
+ function displayBannerWithInfo(subtitle) {
1299
+ displayBanner(subtitle);
1300
+ console.log(ansis.gray(` Version: ${ansis.cyan(version)} | ${ansis.cyan(homepage)}
1301
+ `));
1302
+ }
1303
+
1304
+ async function handleCancellation() {
1305
+ ensureI18nInitialized();
1306
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
1307
+ }
1308
+ async function handleOfficialLoginMode() {
1309
+ ensureI18nInitialized();
1310
+ const success = switchToOfficialLogin();
1311
+ if (success) {
1312
+ console.log(ansis.green(`\u2714 ${i18n.t("api:officialLoginConfigured")}`));
1313
+ } else {
1314
+ console.log(ansis.red(i18n.t("api:officialLoginFailed")));
1315
+ }
1316
+ }
1317
+ async function handleCustomApiMode() {
1318
+ ensureI18nInitialized();
1319
+ const { configureIncrementalManagement } = await import('./chunks/claude-code-incremental-manager.mjs');
1320
+ await configureIncrementalManagement();
1321
+ }
1322
+ async function handleSwitchConfigMode() {
1323
+ ensureI18nInitialized();
1324
+ const { configSwitchCommand } = await Promise.resolve().then(function () { return configSwitch; });
1325
+ await configSwitchCommand({});
1326
+ }
1327
+ async function configureApiFeature() {
1328
+ ensureI18nInitialized();
1329
+ const { mode } = await inquirer.prompt({
1330
+ type: "list",
1331
+ name: "mode",
1332
+ message: i18n.t("api:apiModePrompt"),
1333
+ choices: addNumbersToChoices([
1334
+ { name: i18n.t("api:apiModeOfficial"), value: "official" },
1335
+ { name: i18n.t("api:apiModeCustom"), value: "custom" },
1336
+ { name: i18n.t("api:apiModeSwitch"), value: "switch" },
1337
+ { name: i18n.t("api:apiModeSkip"), value: "skip" }
1338
+ ])
1339
+ });
1340
+ if (!mode || mode === "skip") {
1341
+ await handleCancellation();
1342
+ return;
1343
+ }
1344
+ switch (mode) {
1345
+ case "official":
1346
+ await handleOfficialLoginMode();
1347
+ break;
1348
+ case "custom":
1349
+ await handleCustomApiMode();
1350
+ break;
1351
+ case "switch":
1352
+ await handleSwitchConfigMode();
1353
+ break;
1354
+ default:
1355
+ await handleCancellation();
1356
+ break;
1357
+ }
1358
+ }
1359
+ async function promptCustomModels(defaultPrimaryModel, defaultHaikuModel, defaultSonnetModel, defaultOpusModel) {
1360
+ const { primaryModel } = await inquirer.prompt({
1361
+ type: "input",
1362
+ name: "primaryModel",
1363
+ message: `${i18n.t("configuration:enterPrimaryModel")}${i18n.t("common:emptyToSkip")}`,
1364
+ default: defaultPrimaryModel || ""
1365
+ });
1366
+ const { haikuModel } = await inquirer.prompt({
1367
+ type: "input",
1368
+ name: "haikuModel",
1369
+ message: `${i18n.t("configuration:enterHaikuModel")}${i18n.t("common:emptyToSkip")}`,
1370
+ default: defaultHaikuModel || ""
1371
+ });
1372
+ const { sonnetModel } = await inquirer.prompt({
1373
+ type: "input",
1374
+ name: "sonnetModel",
1375
+ message: `${i18n.t("configuration:enterSonnetModel")}${i18n.t("common:emptyToSkip")}`,
1376
+ default: defaultSonnetModel || ""
1377
+ });
1378
+ const { opusModel } = await inquirer.prompt({
1379
+ type: "input",
1380
+ name: "opusModel",
1381
+ message: `${i18n.t("configuration:enterOpusModel")}${i18n.t("common:emptyToSkip")}`,
1382
+ default: defaultOpusModel || ""
1383
+ });
1384
+ return { primaryModel, haikuModel, sonnetModel, opusModel };
1385
+ }
1386
+ async function changeScriptLanguageFeature(currentLang) {
1387
+ ensureI18nInitialized();
1388
+ const { lang } = await inquirer.prompt({
1389
+ type: "list",
1390
+ name: "lang",
1391
+ message: i18n.t("language:selectScriptLang"),
1392
+ choices: addNumbersToChoices(
1393
+ SUPPORTED_LANGS.map((l) => ({
1394
+ name: LANG_LABELS[l],
1395
+ value: l
1396
+ }))
1397
+ ),
1398
+ default: SUPPORTED_LANGS.indexOf(currentLang)
1399
+ });
1400
+ if (!lang) {
1401
+ return currentLang;
1402
+ }
1403
+ updateZcfConfig({ preferredLang: lang });
1404
+ await changeLanguage(lang);
1405
+ console.log(ansis.green(`\u2714 ${i18n.t("language:languageChanged") || "Language changed"}`));
1406
+ return lang;
1407
+ }
1408
+
1409
+ const features = {
1410
+ __proto__: null,
1411
+ changeScriptLanguageFeature: changeScriptLanguageFeature,
1412
+ configureApiFeature: configureApiFeature,
1413
+ promptCustomModels: promptCustomModels
1414
+ };
1415
+
1416
+ async function moveToTrash(paths) {
1417
+ const pathArray = Array.isArray(paths) ? paths : [paths];
1418
+ const results = [];
1419
+ for (const path of pathArray) {
1420
+ try {
1421
+ const exists = await pathExists(path);
1422
+ if (!exists) {
1423
+ results.push({
1424
+ success: false,
1425
+ path,
1426
+ error: "Path does not exist"
1427
+ });
1428
+ continue;
1429
+ }
1430
+ await trash(path);
1431
+ results.push({
1432
+ success: true,
1433
+ path
1434
+ });
1435
+ } catch (error) {
1436
+ results.push({
1437
+ success: false,
1438
+ path,
1439
+ error: error.message || "Unknown error occurred"
1440
+ });
1441
+ }
1442
+ }
1443
+ return results;
1444
+ }
1445
+
1446
+ class ZcfUninstaller {
1447
+ _lang;
1448
+ // Reserved for future i18n support
1449
+ conflictResolution = /* @__PURE__ */ new Map();
1450
+ constructor(lang = "en") {
1451
+ this._lang = lang;
1452
+ this.conflictResolution.set("claude-code", ["mcps"]);
1453
+ void this._lang;
1454
+ }
1455
+ /**
1456
+ * 1. Remove outputStyle field from settings.json and output-styles directory
1457
+ */
1458
+ async removeOutputStyles() {
1459
+ const result = {
1460
+ success: false,
1461
+ removed: [],
1462
+ removedConfigs: [],
1463
+ errors: [],
1464
+ warnings: []
1465
+ };
1466
+ try {
1467
+ const settingsPath = join(homedir(), ".claude", "settings.json");
1468
+ const outputStylesPath = join(homedir(), ".claude", "output-styles");
1469
+ if (await pathExists(settingsPath)) {
1470
+ const settings = readJsonConfig(settingsPath) || {};
1471
+ if (settings.outputStyle) {
1472
+ delete settings.outputStyle;
1473
+ writeJsonConfig(settingsPath, settings);
1474
+ result.removedConfigs.push("outputStyle field from settings.json");
1475
+ }
1476
+ } else {
1477
+ result.warnings.push(i18n.t("uninstall:settingsJsonNotFound"));
1478
+ }
1479
+ if (await pathExists(outputStylesPath)) {
1480
+ const trashResult = await moveToTrash(outputStylesPath);
1481
+ if (!trashResult[0]?.success) {
1482
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1483
+ }
1484
+ result.removed.push("~/.claude/output-styles/");
1485
+ } else {
1486
+ result.warnings.push(i18n.t("uninstall:outputStylesDirectoryNotFound"));
1487
+ }
1488
+ result.success = true;
1489
+ } catch (error) {
1490
+ result.errors.push(`Failed to remove output styles: ${error.message}`);
1491
+ }
1492
+ return result;
1493
+ }
1494
+ /**
1495
+ * 2. Remove custom commands directory (commands/zcf/)
1496
+ */
1497
+ async removeCustomCommands() {
1498
+ const result = {
1499
+ success: false,
1500
+ removed: [],
1501
+ removedConfigs: [],
1502
+ errors: [],
1503
+ warnings: []
1504
+ };
1505
+ try {
1506
+ const commandsPath = join(homedir(), ".claude", "commands", "zcf");
1507
+ if (await pathExists(commandsPath)) {
1508
+ const trashResult = await moveToTrash(commandsPath);
1509
+ if (!trashResult[0]?.success) {
1510
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1511
+ }
1512
+ result.removed.push("commands/zcf/");
1513
+ result.success = true;
1514
+ } else {
1515
+ result.warnings.push(i18n.t("uninstall:commandsNotFound"));
1516
+ result.success = true;
1517
+ }
1518
+ } catch (error) {
1519
+ result.errors.push(`Failed to remove custom commands: ${error.message}`);
1520
+ }
1521
+ return result;
1522
+ }
1523
+ /**
1524
+ * 3. Remove custom agents directory (agents/zcf/)
1525
+ */
1526
+ async removeCustomAgents() {
1527
+ const result = {
1528
+ success: false,
1529
+ removed: [],
1530
+ removedConfigs: [],
1531
+ errors: [],
1532
+ warnings: []
1533
+ };
1534
+ try {
1535
+ const agentsPath = join(homedir(), ".claude", "agents", "zcf");
1536
+ if (await pathExists(agentsPath)) {
1537
+ const trashResult = await moveToTrash(agentsPath);
1538
+ if (!trashResult[0]?.success) {
1539
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1540
+ }
1541
+ result.removed.push("agents/zcf/");
1542
+ result.success = true;
1543
+ } else {
1544
+ result.warnings.push(i18n.t("uninstall:agentsNotFound"));
1545
+ result.success = true;
1546
+ }
1547
+ } catch (error) {
1548
+ result.errors.push(`Failed to remove custom agents: ${error.message}`);
1549
+ }
1550
+ return result;
1551
+ }
1552
+ /**
1553
+ * 4. Remove global memory file (CLAUDE.md)
1554
+ */
1555
+ async removeClaudeMd() {
1556
+ const result = {
1557
+ success: false,
1558
+ removed: [],
1559
+ removedConfigs: [],
1560
+ errors: [],
1561
+ warnings: []
1562
+ };
1563
+ try {
1564
+ const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
1565
+ if (await pathExists(claudeMdPath)) {
1566
+ const trashResult = await moveToTrash(claudeMdPath);
1567
+ if (!trashResult[0]?.success) {
1568
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1569
+ }
1570
+ result.removed.push("CLAUDE.md");
1571
+ result.success = true;
1572
+ } else {
1573
+ result.warnings.push(i18n.t("uninstall:claudeMdNotFound"));
1574
+ result.success = true;
1575
+ }
1576
+ } catch (error) {
1577
+ result.errors.push(`Failed to remove CLAUDE.md: ${error.message}`);
1578
+ }
1579
+ return result;
1580
+ }
1581
+ /**
1582
+ * 5. Remove permissions and environment variables
1583
+ */
1584
+ async removePermissionsAndEnvs() {
1585
+ const result = {
1586
+ success: false,
1587
+ removed: [],
1588
+ removedConfigs: [],
1589
+ errors: [],
1590
+ warnings: []
1591
+ };
1592
+ try {
1593
+ const settingsPath = join(homedir(), ".claude", "settings.json");
1594
+ if (await pathExists(settingsPath)) {
1595
+ const settings = readJsonConfig(settingsPath) || {};
1596
+ let modified = false;
1597
+ if (settings.permissions) {
1598
+ delete settings.permissions;
1599
+ result.removedConfigs.push("permissions configuration");
1600
+ modified = true;
1601
+ }
1602
+ if (settings.env) {
1603
+ delete settings.env;
1604
+ result.removedConfigs.push("environment variables");
1605
+ modified = true;
1606
+ }
1607
+ if (modified) {
1608
+ writeJsonConfig(settingsPath, settings);
1609
+ }
1610
+ result.success = true;
1611
+ } else {
1612
+ result.warnings.push(i18n.t("uninstall:settingsJsonNotFound"));
1613
+ result.success = true;
1614
+ }
1615
+ } catch (error) {
1616
+ result.errors.push(`Failed to remove permissions and envs: ${error.message}`);
1617
+ }
1618
+ return result;
1619
+ }
1620
+ /**
1621
+ * 6. Remove MCP servers from .claude.json (mcpServers field only)
1622
+ */
1623
+ async removeMcps() {
1624
+ const result = {
1625
+ success: false,
1626
+ removed: [],
1627
+ removedConfigs: [],
1628
+ errors: [],
1629
+ warnings: []
1630
+ };
1631
+ try {
1632
+ const claudeJsonPath = join(homedir(), ".claude.json");
1633
+ if (await pathExists(claudeJsonPath)) {
1634
+ const config = readJsonConfig(claudeJsonPath) || {};
1635
+ if (config.mcpServers) {
1636
+ delete config.mcpServers;
1637
+ writeJsonConfig(claudeJsonPath, config);
1638
+ result.removedConfigs.push("mcpServers from .claude.json");
1639
+ }
1640
+ result.success = true;
1641
+ } else {
1642
+ result.warnings.push(i18n.t("uninstall:claudeJsonNotFound"));
1643
+ result.success = true;
1644
+ }
1645
+ } catch (error) {
1646
+ result.errors.push(`Failed to remove MCP servers: ${error.message}`);
1647
+ }
1648
+ return result;
1649
+ }
1650
+ /**
1651
+ * 7. Uninstall Claude Code Router and remove configuration
1652
+ */
1653
+ async uninstallCcr() {
1654
+ const result = {
1655
+ success: false,
1656
+ removed: [],
1657
+ removedConfigs: [],
1658
+ errors: [],
1659
+ warnings: []
1660
+ };
1661
+ try {
1662
+ const ccrPath = join(homedir(), ".claude-code-router");
1663
+ if (await pathExists(ccrPath)) {
1664
+ const trashResult = await moveToTrash(ccrPath);
1665
+ if (!trashResult[0]?.success) {
1666
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1667
+ }
1668
+ result.removed.push(".claude-code-router/");
1669
+ }
1670
+ try {
1671
+ await exec("npm", ["uninstall", "-g", "@musistudio/claude-code-router"]);
1672
+ result.removed.push("@musistudio/claude-code-router package");
1673
+ result.success = true;
1674
+ } catch (npmError) {
1675
+ if (npmError.message.includes("not found") || npmError.message.includes("not installed")) {
1676
+ result.warnings.push(i18n.t("uninstall:ccrPackageNotFound"));
1677
+ result.success = true;
1678
+ } else {
1679
+ result.errors.push(`Failed to uninstall CCR package: ${npmError.message}`);
1680
+ }
1681
+ }
1682
+ } catch (error) {
1683
+ result.errors.push(`Failed to uninstall CCR: ${error.message}`);
1684
+ }
1685
+ return result;
1686
+ }
1687
+ /**
1688
+ * 8. Uninstall CCometixLine
1689
+ */
1690
+ async uninstallCcline() {
1691
+ const result = {
1692
+ success: false,
1693
+ removed: [],
1694
+ removedConfigs: [],
1695
+ errors: [],
1696
+ warnings: []
1697
+ };
1698
+ try {
1699
+ await exec("npm", ["uninstall", "-g", "@cometix/ccline"]);
1700
+ result.removed.push("@cometix/ccline package");
1701
+ result.success = true;
1702
+ } catch (error) {
1703
+ if (error.message.includes("not found") || error.message.includes("not installed")) {
1704
+ result.warnings.push(i18n.t("uninstall:cclinePackageNotFound"));
1705
+ result.success = true;
1706
+ } else {
1707
+ result.errors.push(`Failed to uninstall CCometixLine: ${error.message}`);
1708
+ }
1709
+ }
1710
+ return result;
1711
+ }
1712
+ /**
1713
+ * 9. Uninstall Claude Code and remove entire .claude.json
1714
+ */
1715
+ async uninstallClaudeCode() {
1716
+ const result = {
1717
+ success: false,
1718
+ removed: [],
1719
+ removedConfigs: [],
1720
+ errors: [],
1721
+ warnings: []
1722
+ };
1723
+ try {
1724
+ const claudeJsonPath = join(homedir(), ".claude.json");
1725
+ if (await pathExists(claudeJsonPath)) {
1726
+ const trashResult = await moveToTrash(claudeJsonPath);
1727
+ if (!trashResult[0]?.success) {
1728
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1729
+ }
1730
+ result.removed.push(".claude.json (includes MCP configuration)");
1731
+ }
1732
+ try {
1733
+ const { uninstallCodeTool } = await import('./chunks/installer.mjs');
1734
+ const success = await uninstallCodeTool("claude-code");
1735
+ if (success) {
1736
+ result.removed.push("@anthropic-ai/claude-code");
1737
+ result.success = true;
1738
+ } else {
1739
+ result.errors.push(i18n.t("uninstall:uninstallFailed", { codeType: i18n.t("common:claudeCode"), message: "" }));
1740
+ }
1741
+ } catch (npmError) {
1742
+ if (npmError.message.includes("not found") || npmError.message.includes("not installed")) {
1743
+ result.warnings.push(i18n.t("uninstall:claudeCodePackageNotFound"));
1744
+ result.success = true;
1745
+ } else {
1746
+ result.errors.push(i18n.t("uninstall:uninstallFailed", { codeType: i18n.t("common:claudeCode"), message: `: ${npmError.message}` }));
1747
+ }
1748
+ }
1749
+ } catch (error) {
1750
+ result.errors.push(i18n.t("uninstall:uninstallFailed", { codeType: i18n.t("common:claudeCode"), message: `: ${error.message}` }));
1751
+ }
1752
+ return result;
1753
+ }
1754
+ /**
1755
+ * 10. Remove backup files
1756
+ */
1757
+ async removeBackups() {
1758
+ const result = {
1759
+ success: false,
1760
+ removed: [],
1761
+ removedConfigs: [],
1762
+ errors: [],
1763
+ warnings: []
1764
+ };
1765
+ try {
1766
+ const backupPath = join(homedir(), ".claude", "backup");
1767
+ if (await pathExists(backupPath)) {
1768
+ const trashResult = await moveToTrash(backupPath);
1769
+ if (!trashResult[0]?.success) {
1770
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1771
+ }
1772
+ result.removed.push("backup/");
1773
+ result.success = true;
1774
+ } else {
1775
+ result.warnings.push(i18n.t("uninstall:backupsNotFound"));
1776
+ result.success = true;
1777
+ }
1778
+ } catch (error) {
1779
+ result.errors.push(`Failed to remove backups: ${error.message}`);
1780
+ }
1781
+ return result;
1782
+ }
1783
+ /**
1784
+ * 11. Remove ZCF preference configuration
1785
+ */
1786
+ async removeZcfConfig() {
1787
+ const result = {
1788
+ success: false,
1789
+ removed: [],
1790
+ removedConfigs: [],
1791
+ errors: [],
1792
+ warnings: []
1793
+ };
1794
+ try {
1795
+ const zcfConfigPath = ZCF_CONFIG_FILE;
1796
+ const relativeName = zcfConfigPath.replace(homedir(), "~");
1797
+ if (await pathExists(zcfConfigPath)) {
1798
+ const trashResult = await moveToTrash(zcfConfigPath);
1799
+ if (!trashResult[0]?.success) {
1800
+ result.warnings.push(trashResult[0]?.error || "Failed to move to trash");
1801
+ }
1802
+ result.removed.push(relativeName);
1803
+ result.success = true;
1804
+ } else {
1805
+ result.warnings.push(i18n.t("uninstall:zcfConfigNotFound"));
1806
+ result.success = true;
1807
+ }
1808
+ } catch (error) {
1809
+ result.errors.push(`Failed to remove ZCF config: ${error.message}`);
1810
+ }
1811
+ return result;
1812
+ }
1813
+ /**
1814
+ * Complete uninstall - remove all directories and packages
1815
+ */
1816
+ async completeUninstall() {
1817
+ const result = {
1818
+ success: true,
1819
+ removed: [],
1820
+ removedConfigs: [],
1821
+ errors: [],
1822
+ warnings: []
1823
+ };
1824
+ try {
1825
+ const directoriesToRemove = [
1826
+ { path: join(homedir(), ".claude"), name: "~/.claude/" },
1827
+ { path: join(homedir(), ".claude.json"), name: "~/.claude.json" },
1828
+ { path: join(homedir(), ".claude-code-router"), name: "~/.claude-code-router/" }
1829
+ ];
1830
+ for (const dir of directoriesToRemove) {
1831
+ try {
1832
+ if (await pathExists(dir.path)) {
1833
+ const trashResult = await moveToTrash(dir.path);
1834
+ if (!trashResult[0]?.success) {
1835
+ result.warnings.push(`Failed to move ${dir.name} to trash: ${trashResult[0]?.error || "Unknown error"}`);
1836
+ }
1837
+ result.removed.push(dir.name);
1838
+ }
1839
+ } catch (error) {
1840
+ result.warnings.push(`Failed to remove ${dir.name}: ${error.message}`);
1841
+ }
1842
+ }
1843
+ const packagesToUninstall = [
1844
+ "@musistudio/claude-code-router",
1845
+ "@cometix/ccline",
1846
+ "@anthropic-ai/claude-code"
1847
+ ];
1848
+ for (const pkg of packagesToUninstall) {
1849
+ try {
1850
+ await exec("npm", ["uninstall", "-g", pkg]);
1851
+ result.removed.push(`${pkg} package`);
1852
+ } catch (error) {
1853
+ if (error.message.includes("not found") || error.message.includes("not installed")) {
1854
+ if (pkg.includes("claude-code-router")) {
1855
+ result.warnings.push(i18n.t("uninstall:ccrPackageNotFound"));
1856
+ } else if (pkg.includes("ccline")) {
1857
+ result.warnings.push(i18n.t("uninstall:cclinePackageNotFound"));
1858
+ } else {
1859
+ result.warnings.push(i18n.t("uninstall:claudeCodePackageNotFound"));
1860
+ }
1861
+ } else {
1862
+ result.warnings.push(`Failed to uninstall ${pkg}: ${error.message}`);
1863
+ }
1864
+ }
1865
+ }
1866
+ } catch (error) {
1867
+ result.errors.push(`Complete uninstall failed: ${error.message}`);
1868
+ result.success = false;
1869
+ }
1870
+ return result;
1871
+ }
1872
+ /**
1873
+ * Custom uninstall with conflict resolution
1874
+ */
1875
+ async customUninstall(selectedItems) {
1876
+ const resolvedItems = this.resolveConflicts(selectedItems);
1877
+ const results = [];
1878
+ for (const item of resolvedItems) {
1879
+ try {
1880
+ const result = await this.executeUninstallItem(item);
1881
+ results.push(result);
1882
+ } catch (error) {
1883
+ results.push({
1884
+ success: false,
1885
+ removed: [],
1886
+ removedConfigs: [],
1887
+ errors: [`Failed to execute ${item}: ${error.message}`],
1888
+ warnings: []
1889
+ });
1890
+ }
1891
+ }
1892
+ return results;
1893
+ }
1894
+ /**
1895
+ * Resolve conflicts between uninstall items
1896
+ */
1897
+ resolveConflicts(items) {
1898
+ const resolved = [...items];
1899
+ for (const [primary, conflicts] of this.conflictResolution) {
1900
+ if (resolved.includes(primary)) {
1901
+ conflicts.forEach((conflict) => {
1902
+ const index = resolved.indexOf(conflict);
1903
+ if (index > -1) {
1904
+ resolved.splice(index, 1);
1905
+ }
1906
+ });
1907
+ }
1908
+ }
1909
+ return resolved;
1910
+ }
1911
+ /**
1912
+ * Execute uninstall for a specific item
1913
+ */
1914
+ async executeUninstallItem(item) {
1915
+ switch (item) {
1916
+ case "output-styles":
1917
+ return await this.removeOutputStyles();
1918
+ case "commands":
1919
+ return await this.removeCustomCommands();
1920
+ case "agents":
1921
+ return await this.removeCustomAgents();
1922
+ case "claude-md":
1923
+ return await this.removeClaudeMd();
1924
+ case "permissions-envs":
1925
+ return await this.removePermissionsAndEnvs();
1926
+ case "mcps":
1927
+ return await this.removeMcps();
1928
+ case "ccr":
1929
+ return await this.uninstallCcr();
1930
+ case "ccline":
1931
+ return await this.uninstallCcline();
1932
+ case "claude-code":
1933
+ return await this.uninstallClaudeCode();
1934
+ case "backups":
1935
+ return await this.removeBackups();
1936
+ case "zcf-config":
1937
+ return await this.removeZcfConfig();
1938
+ default:
1939
+ return {
1940
+ success: false,
1941
+ removed: [],
1942
+ removedConfigs: [],
1943
+ errors: [`Unknown uninstall item: ${item}`],
1944
+ warnings: []
1945
+ };
1946
+ }
1947
+ }
1948
+ }
1949
+
1950
+ async function uninstall(options = {}) {
1951
+ try {
1952
+ ensureI18nInitialized();
1953
+ const uninstaller = new ZcfUninstaller(options.lang || "en");
1954
+ if (options.mode && options.mode !== "interactive") {
1955
+ if (options.mode === "complete") {
1956
+ await executeCompleteUninstall(uninstaller);
1957
+ return;
1958
+ } else if (options.mode === "custom" && options.items) {
1959
+ let items;
1960
+ if (typeof options.items === "string") {
1961
+ items = options.items.split(",").map((item) => item.trim());
1962
+ } else {
1963
+ items = options.items;
1964
+ }
1965
+ await executeCustomUninstall(uninstaller, items);
1966
+ return;
1967
+ }
1968
+ }
1969
+ await showInteractiveUninstall(uninstaller);
1970
+ } catch (error) {
1971
+ if (!handleExitPromptError(error)) {
1972
+ handleGeneralError(error);
1973
+ }
1974
+ }
1975
+ }
1976
+ async function showInteractiveUninstall(uninstaller) {
1977
+ console.log(ansis.cyan.bold(i18n.t("uninstall:title")));
1978
+ console.log("");
1979
+ const { mainChoice } = await inquirer.prompt({
1980
+ type: "list",
1981
+ name: "mainChoice",
1982
+ message: i18n.t("uninstall:selectMainOption"),
1983
+ choices: addNumbersToChoices([
1984
+ {
1985
+ name: `${i18n.t("uninstall:completeUninstall")} - ${ansis.gray(i18n.t("uninstall:completeUninstallDesc"))}`,
1986
+ value: "complete",
1987
+ short: i18n.t("uninstall:completeUninstall")
1988
+ },
1989
+ {
1990
+ name: `${i18n.t("uninstall:customUninstall")} - ${ansis.gray(i18n.t("uninstall:customUninstallDesc"))}`,
1991
+ value: "custom",
1992
+ short: i18n.t("uninstall:customUninstall")
1993
+ }
1994
+ ])
1995
+ });
1996
+ if (!mainChoice) {
1997
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
1998
+ return;
1999
+ }
2000
+ if (mainChoice === "complete") {
2001
+ await executeCompleteUninstall(uninstaller);
2002
+ } else {
2003
+ await showCustomUninstallMenu(uninstaller);
2004
+ }
2005
+ }
2006
+ async function showCustomUninstallMenu(uninstaller) {
2007
+ console.log("");
2008
+ console.log(ansis.cyan(i18n.t("uninstall:selectCustomItems")));
2009
+ const { customItems } = await inquirer.prompt({
2010
+ type: "checkbox",
2011
+ name: "customItems",
2012
+ message: `${i18n.t("uninstall:selectItemsToRemove")} ${i18n.t("common:multiSelectHint")}`,
2013
+ choices: [
2014
+ {
2015
+ name: i18n.t("uninstall:outputStyles"),
2016
+ value: "output-styles"
2017
+ },
2018
+ {
2019
+ name: i18n.t("uninstall:commands"),
2020
+ value: "commands"
2021
+ },
2022
+ {
2023
+ name: i18n.t("uninstall:agents"),
2024
+ value: "agents"
2025
+ },
2026
+ {
2027
+ name: i18n.t("uninstall:claudeMd"),
2028
+ value: "claude-md"
2029
+ },
2030
+ {
2031
+ name: i18n.t("uninstall:permissionsEnvs"),
2032
+ value: "permissions-envs"
2033
+ },
2034
+ {
2035
+ name: i18n.t("uninstall:mcps"),
2036
+ value: "mcps"
2037
+ },
2038
+ {
2039
+ name: i18n.t("uninstall:ccr"),
2040
+ value: "ccr"
2041
+ },
2042
+ {
2043
+ name: i18n.t("uninstall:ccline"),
2044
+ value: "ccline"
2045
+ },
2046
+ {
2047
+ name: i18n.t("uninstall:claudeCode"),
2048
+ value: "claude-code"
2049
+ },
2050
+ {
2051
+ name: i18n.t("uninstall:backups"),
2052
+ value: "backups"
2053
+ },
2054
+ {
2055
+ name: i18n.t("uninstall:zcfConfig"),
2056
+ value: "zcf-config"
2057
+ }
2058
+ ],
2059
+ validate: (answers) => {
2060
+ if (answers.length === 0) {
2061
+ return i18n.t("uninstall:selectAtLeastOne");
2062
+ }
2063
+ return true;
2064
+ }
2065
+ });
2066
+ if (!customItems || customItems.length === 0) {
2067
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
2068
+ return;
2069
+ }
2070
+ await executeCustomUninstall(uninstaller, customItems);
2071
+ }
2072
+ async function executeCompleteUninstall(uninstaller) {
2073
+ console.log("");
2074
+ console.log(ansis.red.bold(i18n.t("uninstall:executingComplete")));
2075
+ console.log(ansis.yellow(i18n.t("uninstall:completeWarning")));
2076
+ const confirm = await promptBoolean({
2077
+ message: i18n.t("uninstall:confirmComplete"),
2078
+ defaultValue: false
2079
+ });
2080
+ if (!confirm) {
2081
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
2082
+ return;
2083
+ }
2084
+ console.log("");
2085
+ console.log(ansis.cyan(i18n.t("uninstall:processingComplete")));
2086
+ const result = await uninstaller.completeUninstall();
2087
+ displayUninstallResult("complete", [result]);
2088
+ }
2089
+ async function executeCustomUninstall(uninstaller, items) {
2090
+ console.log("");
2091
+ console.log(ansis.cyan(i18n.t("uninstall:executingCustom")));
2092
+ console.log(ansis.gray(i18n.t("uninstall:selectedItems")));
2093
+ items.forEach((item) => {
2094
+ console.log(` \u2022 ${i18n.t(`uninstall:${item}`)}`);
2095
+ });
2096
+ const confirm = await promptBoolean({
2097
+ message: i18n.t("uninstall:confirmCustom"),
2098
+ defaultValue: false
2099
+ });
2100
+ if (!confirm) {
2101
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
2102
+ return;
2103
+ }
2104
+ console.log("");
2105
+ console.log(ansis.cyan(i18n.t("uninstall:processingCustom")));
2106
+ const results = await uninstaller.customUninstall(items);
2107
+ displayUninstallResult("custom", results);
2108
+ }
2109
+ function displayUninstallResult(mode, results) {
2110
+ console.log("");
2111
+ console.log(ansis.cyan("\u2500".repeat(50)));
2112
+ let totalSuccess = 0;
2113
+ let totalErrors = 0;
2114
+ let totalWarnings = 0;
2115
+ results.forEach((result) => {
2116
+ if (result.success) {
2117
+ totalSuccess++;
2118
+ }
2119
+ if (result.removed && result.removed.length > 0) {
2120
+ console.log(ansis.green(`\u{1F5D1}\uFE0F ${i18n.t("uninstall:movedToTrash")}:`));
2121
+ result.removed.forEach((item) => {
2122
+ console.log(ansis.gray(` \u2022 ${item}`));
2123
+ });
2124
+ }
2125
+ if (result.removedConfigs && result.removedConfigs.length > 0) {
2126
+ console.log(ansis.green(`\u2714 ${i18n.t("uninstall:removedConfigs")}:`));
2127
+ result.removedConfigs.forEach((item) => {
2128
+ console.log(ansis.gray(` \u2022 ${item}`));
2129
+ });
2130
+ }
2131
+ if (result.errors && result.errors.length > 0) {
2132
+ totalErrors += result.errors.length;
2133
+ console.log(ansis.red(`\u2716 ${i18n.t("uninstall:errors")}:`));
2134
+ result.errors.forEach((error) => {
2135
+ console.log(ansis.red(` \u2022 ${error}`));
2136
+ });
2137
+ }
2138
+ if (result.warnings && result.warnings.length > 0) {
2139
+ totalWarnings += result.warnings.length;
2140
+ console.log(ansis.yellow(`\u26A0 ${i18n.t("uninstall:warnings")}:`));
2141
+ result.warnings.forEach((warning) => {
2142
+ console.log(ansis.yellow(` \u2022 ${warning}`));
2143
+ });
2144
+ }
2145
+ });
2146
+ const totalRemovedFiles = results.reduce((count, result) => count + (result.removed?.length || 0), 0);
2147
+ const totalRemovedConfigs = results.reduce((count, result) => count + (result.removedConfigs?.length || 0), 0);
2148
+ console.log("");
2149
+ console.log(ansis.cyan("\u2500".repeat(50)));
2150
+ if (mode === "complete") {
2151
+ if (totalErrors === 0) {
2152
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:completeSuccess")}`));
2153
+ } else {
2154
+ console.log(ansis.yellow.bold(`\u26A0 ${i18n.t("uninstall:completePartialSuccess")}`));
2155
+ }
2156
+ } else {
2157
+ if (totalRemovedFiles > 0 && totalRemovedConfigs > 0) {
2158
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessBoth", {
2159
+ fileCount: totalRemovedFiles,
2160
+ configCount: totalRemovedConfigs
2161
+ })}`));
2162
+ } else if (totalRemovedFiles > 0) {
2163
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessFiles", {
2164
+ count: totalRemovedFiles
2165
+ })}`));
2166
+ } else if (totalRemovedConfigs > 0) {
2167
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccessConfigs", {
2168
+ count: totalRemovedConfigs
2169
+ })}`));
2170
+ } else {
2171
+ console.log(ansis.green.bold(`\u2714 ${i18n.t("uninstall:customSuccess", { count: totalSuccess })}`));
2172
+ }
2173
+ if (totalErrors > 0) {
2174
+ console.log(ansis.red(`\u2716 ${i18n.t("uninstall:errorsCount", { count: totalErrors })}`));
2175
+ }
2176
+ if (totalWarnings > 0) {
2177
+ console.log(ansis.yellow(`\u26A0 ${i18n.t("uninstall:warningsCount", { count: totalWarnings })}`));
2178
+ }
2179
+ }
2180
+ console.log("");
2181
+ }
2182
+
2183
+ function printSeparator() {
2184
+ console.log(`
2185
+ ${ansis.dim("\u2500".repeat(50))}
2186
+ `);
2187
+ }
2188
+ async function showMenu() {
2189
+ console.log(ansis.cyan(i18n.t("menu:selectFunction")));
2190
+ console.log("");
2191
+ console.log(
2192
+ ` ${ansis.cyan("1.")} ${i18n.t("menu:menuOptions.configureApiOrCcr")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.configureApiOrCcr")}`)}`
2193
+ );
2194
+ console.log("");
2195
+ console.log(
2196
+ ` ${ansis.cyan("0.")} ${i18n.t("menu:menuOptions.changeLanguage")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.changeLanguage")}`)}`
2197
+ );
2198
+ console.log(
2199
+ ` ${ansis.cyan("-.")} ${i18n.t("menu:menuOptions.uninstall")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.uninstall")}`)}`
2200
+ );
2201
+ console.log(
2202
+ ` ${ansis.cyan("+.")} ${i18n.t("menu:menuOptions.checkUpdates")} ${ansis.gray(`- ${i18n.t("menu:menuDescriptions.checkUpdates")}`)}`
2203
+ );
2204
+ console.log(` ${ansis.red("Q.")} ${ansis.red(i18n.t("menu:menuOptions.exit"))}`);
2205
+ console.log("");
2206
+ const { choice } = await inquirer.prompt({
2207
+ type: "input",
2208
+ name: "choice",
2209
+ message: i18n.t("common:enterChoice"),
2210
+ validate: (value) => {
2211
+ const valid = ["1", "0", "-", "+", "q", "Q"];
2212
+ return valid.includes(value) || i18n.t("common:invalidChoice");
2213
+ }
2214
+ });
2215
+ if (!choice) {
2216
+ console.log(ansis.yellow(i18n.t("common:cancelled")));
2217
+ return "exit";
2218
+ }
2219
+ const normalized = choice.toLowerCase();
2220
+ switch (normalized) {
2221
+ case "1":
2222
+ await configureApiFeature();
2223
+ break;
2224
+ case "0": {
2225
+ const currentLang = i18n.language;
2226
+ await changeScriptLanguageFeature(currentLang);
2227
+ printSeparator();
2228
+ return void 0;
2229
+ }
2230
+ case "-":
2231
+ await uninstall();
2232
+ printSeparator();
2233
+ return void 0;
2234
+ case "+":
2235
+ await checkUpdates();
2236
+ printSeparator();
2237
+ return void 0;
2238
+ case "q":
2239
+ console.log(ansis.cyan(i18n.t("common:goodbye")));
2240
+ return "exit";
2241
+ default:
2242
+ return void 0;
2243
+ }
2244
+ printSeparator();
2245
+ const shouldContinue = await promptBoolean({
2246
+ message: i18n.t("common:returnToMenu"),
2247
+ defaultValue: true
2248
+ });
2249
+ if (!shouldContinue) {
2250
+ console.log(ansis.cyan(i18n.t("common:goodbye")));
2251
+ return "exit";
2252
+ }
2253
+ return void 0;
2254
+ }
2255
+ async function showMainMenu() {
2256
+ try {
2257
+ let exitMenu = false;
2258
+ while (!exitMenu) {
2259
+ displayBannerWithInfo(CODE_TOOL_BANNERS["claude-code"] || "ccs");
2260
+ const result = await showMenu();
2261
+ if (result === "exit") {
2262
+ exitMenu = true;
2263
+ }
2264
+ }
2265
+ } catch (error) {
2266
+ if (!handleExitPromptError(error)) {
2267
+ handleGeneralError(error);
2268
+ }
2269
+ }
2270
+ }
2271
+
2272
+ const LANGUAGE_SELECTION_MESSAGES = {
2273
+ selectLanguage: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
2274
+ operationCancelled: "Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"
2275
+ };
2276
+ async function selectScriptLanguage(currentLang) {
2277
+ const zcfConfig = readZcfConfig();
2278
+ if (zcfConfig?.preferredLang) {
2279
+ return zcfConfig.preferredLang;
2280
+ }
2281
+ const { lang } = await inquirer.prompt({
2282
+ type: "list",
2283
+ name: "lang",
2284
+ message: LANGUAGE_SELECTION_MESSAGES.selectLanguage,
2285
+ choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
2286
+ name: LANG_LABELS[l],
2287
+ value: l
2288
+ })))
2289
+ });
2290
+ if (!lang) {
2291
+ console.log(ansis.yellow(LANGUAGE_SELECTION_MESSAGES.operationCancelled));
2292
+ process.exit(0);
2293
+ }
2294
+ const scriptLang = lang;
2295
+ updateZcfConfig({
2296
+ version,
2297
+ preferredLang: scriptLang
2298
+ });
2299
+ return scriptLang;
2300
+ }
2301
+
2302
+ async function resolveAndSwitchLanguage(lang, options, skipPrompt = false) {
2303
+ const zcfConfig = await readZcfConfigAsync();
2304
+ const targetLang = options?.allLang || lang || options?.lang || zcfConfig?.preferredLang || (skipPrompt ? "en" : await selectScriptLanguage());
2305
+ if (i18n.isInitialized && i18n.language !== targetLang) {
2306
+ await changeLanguage(targetLang);
2307
+ }
2308
+ return targetLang;
2309
+ }
2310
+ async function withLanguageResolution(action, skipPrompt = false) {
2311
+ return async (...args) => {
2312
+ const options = args[0];
2313
+ const languageOptions = extractLanguageOptions(options);
2314
+ await resolveAndSwitchLanguage(void 0, languageOptions, skipPrompt || languageOptions.skipPrompt);
2315
+ return await action(...args);
2316
+ };
2317
+ }
2318
+ function extractLanguageOptions(options) {
2319
+ if (!options || typeof options !== "object" || options === null) {
2320
+ return {};
2321
+ }
2322
+ const obj = options;
2323
+ return {
2324
+ lang: typeof obj.lang === "string" ? obj.lang : void 0,
2325
+ allLang: typeof obj.allLang === "string" ? obj.allLang : void 0,
2326
+ skipPrompt: typeof obj.skipPrompt === "boolean" ? obj.skipPrompt : void 0
2327
+ };
2328
+ }
2329
+ function customizeHelp(sections) {
2330
+ sections.unshift({
2331
+ title: "",
2332
+ body: ansis.cyan.bold(`ccs - Claude Code Switch v${version}`)
2333
+ });
2334
+ sections.push({
2335
+ title: ansis.yellow(i18n.t("cli:help.commands")),
2336
+ body: [
2337
+ ` ${ansis.cyan("ccs")} ${i18n.t("cli:help.commandDescriptions.showInteractiveMenuDefault")}`,
2338
+ ` ${ansis.cyan("ccs config-switch")} | ${ansis.cyan("cs")} ${i18n.t("cli:help.commandDescriptions.switchConfiguration")}`,
2339
+ ` ${ansis.cyan("ccs check-updates")} | ${ansis.cyan("check")} ${i18n.t("cli:help.commandDescriptions.checkUpdateVersions")}`,
2340
+ ` ${ansis.cyan("ccs uninstall")} ${i18n.t("cli:help.commandDescriptions.uninstallConfigurations")}`
2341
+ ].join("\n")
2342
+ });
2343
+ sections.push({
2344
+ title: ansis.yellow(i18n.t("cli:help.options")),
2345
+ body: [
2346
+ ` ${ansis.green("--lang, -l")} <lang> ${i18n.t("cli:help.optionDescriptions.displayLanguage")} (zh-CN, en)`,
2347
+ ` ${ansis.green("--all-lang, -g")} <lang> ${i18n.t("cli:help.optionDescriptions.setAllLanguageParams")}`,
2348
+ ` ${ansis.green("--list, -l")} ${i18n.t("cli:help.optionDescriptions.listConfigurations")}`,
2349
+ ` ${ansis.green("--help, -h")} ${i18n.t("cli:help.optionDescriptions.displayHelp")}`,
2350
+ ` ${ansis.green("--version, -v")} ${i18n.t("cli:help.optionDescriptions.displayVersion")}`
2351
+ ].join("\n")
2352
+ });
2353
+ sections.push({
2354
+ title: ansis.yellow(i18n.t("cli:help.examples")),
2355
+ body: [
2356
+ ansis.gray(` # ${i18n.t("cli:help.exampleDescriptions.showInteractiveMenu")}`),
2357
+ ` ${ansis.cyan("npx @xwm111/ccs")}`,
2358
+ "",
2359
+ ansis.gray(` # ${i18n.t("cli:help.exampleDescriptions.switchConfiguration")}`),
2360
+ ` ${ansis.cyan("ccs config-switch --list")}`,
2361
+ ` ${ansis.cyan("ccs cs my-endpoint")}`,
2362
+ "",
2363
+ ansis.gray(` # ${i18n.t("cli:help.exampleDescriptions.checkAndUpdateTools")}`),
2364
+ ` ${ansis.cyan("ccs check-updates")}`,
2365
+ ` ${ansis.cyan("ccs check")}`,
2366
+ "",
2367
+ ansis.gray(` # ${i18n.t("cli:help.exampleDescriptions.uninstallConfigurations")}`),
2368
+ ` ${ansis.cyan("ccs uninstall")}`,
2369
+ ""
2370
+ ].join("\n")
2371
+ });
2372
+ return sections;
2373
+ }
2374
+ async function setupCommands(cli) {
2375
+ try {
2376
+ const zcfConfig = await readZcfConfigAsync();
2377
+ const defaultLang = zcfConfig?.preferredLang || "en";
2378
+ await initI18n(defaultLang);
2379
+ } catch {
2380
+ }
2381
+ cli.command("", "Show interactive menu (default)").option("--lang, -l <lang>", "Display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").action(await withLanguageResolution(async () => {
2382
+ await showMainMenu();
2383
+ }));
2384
+ cli.command("config-switch [target]", "Switch Claude Code API configuration, or list available configurations").alias("cs").option("--lang <lang>", "Display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--list, -l", "List available configurations").action(await withLanguageResolution(async (target, options) => {
2385
+ await configSwitchCommand({
2386
+ target,
2387
+ list: options.list
2388
+ });
2389
+ }));
2390
+ cli.command("uninstall", "Remove ccs configurations and tools").option("--lang, -l <lang>", "Display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--mode, -m <mode>", "Uninstall mode (complete/custom/interactive), default: interactive").option("--items, -i <items>", "Comma-separated items for custom uninstall mode").action(await withLanguageResolution(async (options) => {
2391
+ await uninstall(options);
2392
+ }));
2393
+ cli.command("check-updates", "Check and update Claude Code and ccs to latest versions").alias("check").option("--lang, -l <lang>", "Display language (zh-CN, en)").option("--all-lang, -g <lang>", "Set all language parameters to this value").option("--skip-prompt, -s", "Skip all interactive prompts (non-interactive mode)").action(await withLanguageResolution(async (options) => {
2394
+ await checkUpdates(options);
2395
+ }));
2396
+ cli.help((sections) => customizeHelp(sections));
2397
+ cli.version(version);
2398
+ }
2399
+
2400
+ async function main() {
2401
+ const cli = cac("ccs");
2402
+ await setupCommands(cli);
2403
+ cli.parse();
2404
+ }
2405
+ main().catch(console.error);
2406
+
2407
+ export { ClaudeCodeConfigManager as C, addNumbersToChoices as a, features as f };