ccman 3.0.33 → 3.1.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.
Files changed (3) hide show
  1. package/README.md +6 -6
  2. package/dist/index.js +906 -253
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "../core/package.json"() {
16
16
  package_default = {
17
17
  name: "@ccman/core",
18
- version: "3.0.33",
18
+ version: "3.1.1",
19
19
  type: "module",
20
20
  description: "Core business logic for ccman",
21
21
  main: "./dist/index.js",
@@ -43,12 +43,12 @@ var init_package = __esm({
43
43
  license: "MIT",
44
44
  repository: {
45
45
  type: "git",
46
- url: "https://github.com/2ue/ccm.git",
46
+ url: "https://github.com/2ue/ccman.git",
47
47
  directory: "packages/core"
48
48
  },
49
- homepage: "https://github.com/2ue/ccm#readme",
49
+ homepage: "https://github.com/2ue/ccman#readme",
50
50
  bugs: {
51
- url: "https://github.com/2ue/ccm/issues"
51
+ url: "https://github.com/2ue/ccman/issues"
52
52
  },
53
53
  dependencies: {
54
54
  "@iarna/toml": "^2.2.5",
@@ -65,6 +65,59 @@ var init_package = __esm({
65
65
  }
66
66
  });
67
67
 
68
+ // ../core/dist/constants.js
69
+ var TOOL_TYPES, MAIN_TOOL_TYPES, TOOL_CONFIG;
70
+ var init_constants = __esm({
71
+ "../core/dist/constants.js"() {
72
+ "use strict";
73
+ TOOL_TYPES = {
74
+ CODEX: "codex",
75
+ CLAUDE: "claude",
76
+ MCP: "mcp",
77
+ GEMINI: "gemini"
78
+ };
79
+ MAIN_TOOL_TYPES = {
80
+ CODEX: TOOL_TYPES.CODEX,
81
+ CLAUDE: TOOL_TYPES.CLAUDE,
82
+ GEMINI: TOOL_TYPES.GEMINI
83
+ };
84
+ TOOL_CONFIG = {
85
+ [TOOL_TYPES.CODEX]: {
86
+ displayName: "Codex",
87
+ color: "blue",
88
+ textColorClass: "text-blue-600",
89
+ bgColorClass: "bg-blue-50",
90
+ hoverBgColorClass: "hover:bg-blue-100",
91
+ description: "Codex AI \u52A9\u624B"
92
+ },
93
+ [TOOL_TYPES.CLAUDE]: {
94
+ displayName: "Claude Code",
95
+ color: "purple",
96
+ textColorClass: "text-purple-600",
97
+ bgColorClass: "bg-purple-50",
98
+ hoverBgColorClass: "hover:bg-purple-100",
99
+ description: "Claude Code AI \u52A9\u624B"
100
+ },
101
+ [TOOL_TYPES.MCP]: {
102
+ displayName: "MCP",
103
+ color: "gray",
104
+ textColorClass: "text-gray-600",
105
+ bgColorClass: "bg-gray-50",
106
+ hoverBgColorClass: "hover:bg-gray-100",
107
+ description: "MCP \u670D\u52A1"
108
+ },
109
+ [TOOL_TYPES.GEMINI]: {
110
+ displayName: "Gemini CLI",
111
+ color: "green",
112
+ textColorClass: "text-green-600",
113
+ bgColorClass: "bg-green-50",
114
+ hoverBgColorClass: "hover:bg-green-100",
115
+ description: "Gemini CLI AI \u52A9\u624B"
116
+ }
117
+ };
118
+ }
119
+ });
120
+
68
121
  // ../core/dist/paths.js
69
122
  import * as os from "os";
70
123
  import * as path from "path";
@@ -77,6 +130,9 @@ function getCodexDir() {
77
130
  function getClaudeDir() {
78
131
  return claudeDir;
79
132
  }
133
+ function getGeminiDir() {
134
+ return geminiDir;
135
+ }
80
136
  function getConfigPath() {
81
137
  return path.join(ccmanDir, "config.json");
82
138
  }
@@ -92,7 +148,13 @@ function getClaudeConfigPath() {
92
148
  function getClaudeJsonPath() {
93
149
  return path.join(rootDir, ".claude.json");
94
150
  }
95
- var isDev, isTest, rootDir, ccmanDir, codexDir, claudeDir;
151
+ function getGeminiSettingsPath() {
152
+ return path.join(geminiDir, "settings.json");
153
+ }
154
+ function getGeminiEnvPath() {
155
+ return path.join(geminiDir, ".env");
156
+ }
157
+ var isDev, isTest, rootDir, ccmanDir, codexDir, claudeDir, geminiDir;
96
158
  var init_paths = __esm({
97
159
  "../core/dist/paths.js"() {
98
160
  "use strict";
@@ -108,6 +170,7 @@ var init_paths = __esm({
108
170
  ccmanDir = path.join(rootDir, ".ccman");
109
171
  codexDir = path.join(rootDir, ".codex");
110
172
  claudeDir = path.join(rootDir, ".claude");
173
+ geminiDir = path.join(rootDir, ".gemini");
111
174
  }
112
175
  });
113
176
 
@@ -344,15 +407,13 @@ function migrateMCPConfig(config) {
344
407
  config.managedServerNames = {
345
408
  claude: config.managedServerNames,
346
409
  codex: [],
347
- cursor: [],
348
- windsurf: []
410
+ gemini: []
349
411
  };
350
412
  } else if (!config.managedServerNames) {
351
413
  config.managedServerNames = {
352
414
  claude: [],
353
415
  codex: [],
354
- cursor: [],
355
- windsurf: []
416
+ gemini: []
356
417
  };
357
418
  }
358
419
  if (config.servers) {
@@ -374,8 +435,7 @@ function loadMCPConfig() {
374
435
  managedServerNames: {
375
436
  claude: [],
376
437
  codex: [],
377
- cursor: [],
378
- windsurf: []
438
+ gemini: []
379
439
  }
380
440
  };
381
441
  }
@@ -441,10 +501,10 @@ function writeMCPConfigForApp(app, _provider) {
441
501
  break;
442
502
  case "codex":
443
503
  return;
444
- case "cursor":
445
- return;
446
- case "windsurf":
447
- return;
504
+ case "gemini":
505
+ configPath = getGeminiSettingsPath();
506
+ configDir = getGeminiDir();
507
+ break;
448
508
  }
449
509
  ensureDir(configDir);
450
510
  let appConfig = {};
@@ -625,6 +685,127 @@ var init_mcp2 = __esm({
625
685
  }
626
686
  });
627
687
 
688
+ // ../core/dist/presets/gemini.js
689
+ var GEMINI_PRESETS;
690
+ var init_gemini = __esm({
691
+ "../core/dist/presets/gemini.js"() {
692
+ "use strict";
693
+ GEMINI_PRESETS = [
694
+ {
695
+ name: "Google Gemini (API Key)",
696
+ baseUrl: "",
697
+ description: "\u4F7F\u7528\u5B98\u65B9 Gemini API\uFF08\u901A\u8FC7 GEMINI_API_KEY \u6216 GOOGLE_API_KEY \u8BA4\u8BC1\uFF09"
698
+ },
699
+ {
700
+ name: "PackyAPI",
701
+ baseUrl: "https://www.packyapi.com",
702
+ description: "PackyAPI Gemini \u517C\u5BB9\u670D\u52A1"
703
+ },
704
+ {
705
+ name: "LiteLLM Proxy",
706
+ baseUrl: "http://localhost:4000",
707
+ description: "\u4F7F\u7528\u672C\u5730 LiteLLM Proxy \u4F5C\u4E3A Gemini \u517C\u5BB9\u4EE3\u7406"
708
+ }
709
+ ];
710
+ }
711
+ });
712
+
713
+ // ../core/dist/writers/gemini.js
714
+ import * as fs5 from "fs";
715
+ function loadEnvFile(envPath) {
716
+ if (!fileExists(envPath))
717
+ return {};
718
+ const content = fs5.readFileSync(envPath, "utf-8");
719
+ const result = {};
720
+ for (const line of content.split("\n")) {
721
+ const trimmed = line.trim();
722
+ if (!trimmed || trimmed.startsWith("#"))
723
+ continue;
724
+ const eqIndex = trimmed.indexOf("=");
725
+ if (eqIndex === -1)
726
+ continue;
727
+ const key = trimmed.slice(0, eqIndex).trim();
728
+ const value = trimmed.slice(eqIndex + 1).trim();
729
+ if (!key)
730
+ continue;
731
+ result[key] = value;
732
+ }
733
+ return result;
734
+ }
735
+ function saveEnvFile(envPath, env) {
736
+ const lines = [];
737
+ const keys = Object.keys(env).sort();
738
+ for (const key of keys) {
739
+ lines.push(`${key}=${String(env[key])}`);
740
+ }
741
+ const content = lines.join("\n") + (lines.length ? "\n" : "");
742
+ fs5.writeFileSync(envPath, content, { mode: 384 });
743
+ }
744
+ function writeGeminiConfig(provider) {
745
+ const settingsPath = getGeminiSettingsPath();
746
+ const envPath = getGeminiEnvPath();
747
+ const dir = getGeminiDir();
748
+ ensureDir(dir);
749
+ let settings = {};
750
+ if (fileExists(settingsPath)) {
751
+ try {
752
+ const content = fs5.readFileSync(settingsPath, "utf-8");
753
+ const parsed = JSON.parse(content);
754
+ if (parsed && typeof parsed === "object") {
755
+ settings = parsed;
756
+ }
757
+ } catch (error) {
758
+ throw new Error(`\u65E0\u6CD5\u8BFB\u53D6 Gemini settings.json: ${error.message}`);
759
+ }
760
+ }
761
+ if (!settings.ide || typeof settings.ide !== "object") {
762
+ settings.ide = {};
763
+ }
764
+ if (settings.ide.enabled === void 0) {
765
+ settings.ide.enabled = true;
766
+ }
767
+ if (!settings.security || typeof settings.security !== "object") {
768
+ settings.security = {};
769
+ }
770
+ if (!settings.security.auth || typeof settings.security.auth !== "object") {
771
+ settings.security.auth = {};
772
+ }
773
+ if (settings.security.auth.selectedType === void 0) {
774
+ settings.security.auth.selectedType = "gemini-api-key";
775
+ }
776
+ try {
777
+ const tempPath = `${settingsPath}.tmp`;
778
+ fs5.writeFileSync(tempPath, JSON.stringify(settings, null, 2), {
779
+ mode: 384
780
+ });
781
+ fs5.renameSync(tempPath, settingsPath);
782
+ } catch (error) {
783
+ throw new Error(`\u5199\u5165 Gemini settings.json \u5931\u8D25: ${error.message}`);
784
+ }
785
+ const env = loadEnvFile(envPath);
786
+ if (provider.baseUrl && provider.baseUrl.trim().length > 0) {
787
+ env.GOOGLE_GEMINI_BASE_URL = provider.baseUrl;
788
+ } else {
789
+ delete env.GOOGLE_GEMINI_BASE_URL;
790
+ }
791
+ if (provider.apiKey && provider.apiKey.trim().length > 0) {
792
+ env.GEMINI_API_KEY = provider.apiKey;
793
+ } else {
794
+ delete env.GEMINI_API_KEY;
795
+ }
796
+ if (provider.model && provider.model.trim().length > 0) {
797
+ env.GEMINI_MODEL = provider.model;
798
+ }
799
+ saveEnvFile(envPath, env);
800
+ }
801
+ var init_gemini2 = __esm({
802
+ "../core/dist/writers/gemini.js"() {
803
+ "use strict";
804
+ init_paths();
805
+ init_file();
806
+ }
807
+ });
808
+
628
809
  // ../core/dist/tool-manager.types.js
629
810
  var ProviderNotFoundError, ProviderNameConflictError, PresetNameConflictError;
630
811
  var init_tool_manager_types = __esm({
@@ -901,6 +1082,9 @@ function createClaudeManager() {
901
1082
  function createMCPManager() {
902
1083
  return createToolManager("mcp");
903
1084
  }
1085
+ function createGeminiManager() {
1086
+ return createToolManager("gemini");
1087
+ }
904
1088
  var TOOL_CONFIGS;
905
1089
  var init_tool_manager = __esm({
906
1090
  "../core/dist/tool-manager.js"() {
@@ -913,6 +1097,8 @@ var init_tool_manager = __esm({
913
1097
  init_codex2();
914
1098
  init_claude2();
915
1099
  init_mcp2();
1100
+ init_gemini();
1101
+ init_gemini2();
916
1102
  init_tool_manager_types();
917
1103
  TOOL_CONFIGS = {
918
1104
  codex: {
@@ -949,11 +1135,16 @@ var init_tool_manager = __esm({
949
1135
  }
950
1136
  return mcpServer;
951
1137
  });
952
- for (const app of ["claude", "codex", "cursor", "windsurf"]) {
1138
+ for (const app of ["claude", "codex", "gemini"]) {
953
1139
  mcpConfig.managedServerNames[app] = mcpConfig.servers.filter((s) => s.enabledApps.includes(app)).map((s) => s.name);
954
1140
  }
955
1141
  saveMCPConfig(mcpConfig);
956
1142
  }
1143
+ },
1144
+ gemini: {
1145
+ configPath: path6.join(getCcmanDir(), "gemini.json"),
1146
+ builtinPresets: GEMINI_PRESETS,
1147
+ writer: writeGeminiConfig
957
1148
  }
958
1149
  };
959
1150
  }
@@ -968,20 +1159,20 @@ var init_migrate = __esm({
968
1159
  });
969
1160
 
970
1161
  // ../core/dist/config.js
971
- import * as fs5 from "fs";
1162
+ import * as fs6 from "fs";
972
1163
  function ensureConfigDir() {
973
1164
  const dir = getCcmanDir();
974
- if (!fs5.existsSync(dir)) {
975
- fs5.mkdirSync(dir, { recursive: true, mode: 448 });
1165
+ if (!fs6.existsSync(dir)) {
1166
+ fs6.mkdirSync(dir, { recursive: true, mode: 448 });
976
1167
  }
977
1168
  }
978
1169
  function loadConfig() {
979
1170
  const configPath = getConfigPath();
980
- if (!fs5.existsSync(configPath)) {
1171
+ if (!fs6.existsSync(configPath)) {
981
1172
  return {};
982
1173
  }
983
1174
  try {
984
- const content = fs5.readFileSync(configPath, "utf-8");
1175
+ const content = fs6.readFileSync(configPath, "utf-8");
985
1176
  return JSON.parse(content);
986
1177
  } catch (error) {
987
1178
  throw new Error(`Failed to load config: ${error.message}`);
@@ -992,10 +1183,10 @@ function saveConfig(config) {
992
1183
  const configPath = getConfigPath();
993
1184
  try {
994
1185
  const tempPath = `${configPath}.tmp`;
995
- fs5.writeFileSync(tempPath, JSON.stringify(config, null, 2), {
1186
+ fs6.writeFileSync(tempPath, JSON.stringify(config, null, 2), {
996
1187
  mode: 384
997
1188
  });
998
- fs5.renameSync(tempPath, configPath);
1189
+ fs6.renameSync(tempPath, configPath);
999
1190
  } catch (error) {
1000
1191
  throw new Error(`Failed to save config: ${error.message}`);
1001
1192
  }
@@ -1319,15 +1510,15 @@ var init_merge_advanced = __esm({
1319
1510
  });
1320
1511
 
1321
1512
  // ../core/dist/sync/merge.js
1322
- import fs6 from "fs";
1513
+ import fs7 from "fs";
1323
1514
  import path7 from "path";
1324
1515
  function backupConfig(configPath, keepCount = 3) {
1325
- if (!fs6.existsSync(configPath)) {
1516
+ if (!fs7.existsSync(configPath)) {
1326
1517
  throw new Error(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${configPath}`);
1327
1518
  }
1328
1519
  const timestamp = Date.now();
1329
1520
  const backupPath = `${configPath}.backup.${timestamp}`;
1330
- fs6.copyFileSync(configPath, backupPath);
1521
+ fs7.copyFileSync(configPath, backupPath);
1331
1522
  cleanupOldBackups(configPath, keepCount);
1332
1523
  return backupPath;
1333
1524
  }
@@ -1336,7 +1527,7 @@ function cleanupOldBackups(configPath, keepCount) {
1336
1527
  const basename = path7.basename(configPath);
1337
1528
  const backupPrefix = `${basename}.backup.`;
1338
1529
  try {
1339
- const files = fs6.readdirSync(dir);
1530
+ const files = fs7.readdirSync(dir);
1340
1531
  const backups = files.filter((f) => f.startsWith(backupPrefix)).map((f) => {
1341
1532
  const timestampStr = f.substring(backupPrefix.length);
1342
1533
  const timestamp = parseInt(timestampStr, 10);
@@ -1352,7 +1543,7 @@ function cleanupOldBackups(configPath, keepCount) {
1352
1543
  const toDelete = backups.slice(keepCount);
1353
1544
  for (const backup of toDelete) {
1354
1545
  try {
1355
- fs6.unlinkSync(backup.path);
1546
+ fs7.unlinkSync(backup.path);
1356
1547
  } catch (error) {
1357
1548
  console.warn(`\u65E0\u6CD5\u5220\u9664\u65E7\u5907\u4EFD\u6587\u4EF6 ${backup.name}: ${error.message}`);
1358
1549
  }
@@ -1368,91 +1559,81 @@ var init_merge = __esm({
1368
1559
  });
1369
1560
 
1370
1561
  // ../core/dist/sync/sync-v2.js
1371
- import fs7 from "fs";
1562
+ import fs8 from "fs";
1372
1563
  import path8 from "path";
1373
1564
  async function uploadToCloud(config, password) {
1374
1565
  const ccmanDir2 = getCcmanDir();
1375
- const codexConfigPath = path8.join(ccmanDir2, "codex.json");
1376
- const claudeConfigPath = path8.join(ccmanDir2, "claude.json");
1377
- const codexConfig = readJSON(codexConfigPath);
1378
- const claudeConfig = readJSON(claudeConfigPath);
1379
- const encryptedCodexProviders = encryptProviders(codexConfig.providers, password);
1380
- const encryptedClaudeProviders = encryptProviders(claudeConfig.providers, password);
1381
- const encryptedCodexConfig = {
1382
- ...codexConfig,
1383
- // 保留所有字段
1384
- providers: encryptedCodexProviders
1385
- // 只替换 providers(加密后的)
1386
- };
1387
- const encryptedClaudeConfig = {
1388
- ...claudeConfig,
1389
- // 保留所有字段
1390
- providers: encryptedClaudeProviders
1391
- // 只替换 providers(加密后的)
1392
- };
1393
- const codexJson = JSON.stringify(encryptedCodexConfig, null, 2);
1394
- const claudeJson = JSON.stringify(encryptedClaudeConfig, null, 2);
1395
- await uploadToWebDAV(config, CODEX_REMOTE_PATH, codexJson);
1396
- await uploadToWebDAV(config, CLAUDE_REMOTE_PATH, claudeJson);
1566
+ const toolKeys = Object.keys(TOOL_SYNC_CONFIG);
1567
+ for (const tool of toolKeys) {
1568
+ const { remotePath, configFilename } = TOOL_SYNC_CONFIG[tool];
1569
+ const configPath = path8.join(ccmanDir2, configFilename);
1570
+ const localConfig = readJSON(configPath);
1571
+ const encryptedProviders = encryptProviders(localConfig.providers, password);
1572
+ const encryptedConfig = {
1573
+ ...localConfig,
1574
+ // 保留所有字段
1575
+ providers: encryptedProviders
1576
+ // 只替换 providers(加密后的)
1577
+ };
1578
+ const jsonContent = JSON.stringify(encryptedConfig, null, 2);
1579
+ await uploadToWebDAV(config, remotePath, jsonContent);
1580
+ }
1397
1581
  updateLastSyncTime();
1398
1582
  console.log("\u2705 \u914D\u7F6E\u5DF2\u4E0A\u4F20\u5230\u4E91\u7AEF");
1399
1583
  }
1400
1584
  async function downloadFromCloud(config, password) {
1401
- const codexExists = await existsOnWebDAV(config, CODEX_REMOTE_PATH);
1402
- const claudeExists = await existsOnWebDAV(config, CLAUDE_REMOTE_PATH);
1403
- if (!codexExists && !claudeExists) {
1585
+ const ccmanDir2 = getCcmanDir();
1586
+ const toolKeys = Object.keys(TOOL_SYNC_CONFIG);
1587
+ const existsChecks = await Promise.all(toolKeys.map(async (tool) => {
1588
+ const { remotePath } = TOOL_SYNC_CONFIG[tool];
1589
+ return existsOnWebDAV(config, remotePath);
1590
+ }));
1591
+ if (!existsChecks.some((exists) => exists)) {
1404
1592
  throw new Error("\u8FDC\u7A0B\u914D\u7F6E\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u4E0A\u4F20\u914D\u7F6E");
1405
1593
  }
1406
- const codexJson = codexExists ? await downloadFromWebDAV(config, CODEX_REMOTE_PATH) : null;
1407
- const claudeJson = claudeExists ? await downloadFromWebDAV(config, CLAUDE_REMOTE_PATH) : null;
1408
- const remoteCodexConfig = codexJson ? JSON.parse(codexJson) : null;
1409
- const remoteClaudeConfig = claudeJson ? JSON.parse(claudeJson) : null;
1410
- let decryptedCodexProviders = null;
1411
- let decryptedClaudeProviders = null;
1412
- try {
1413
- if (remoteCodexConfig) {
1414
- decryptedCodexProviders = decryptProviders(remoteCodexConfig.providers, password);
1415
- }
1416
- if (remoteClaudeConfig) {
1417
- decryptedClaudeProviders = decryptProviders(remoteClaudeConfig.providers, password);
1594
+ const remoteConfigs = [];
1595
+ for (let i = 0; i < toolKeys.length; i++) {
1596
+ const tool = toolKeys[i];
1597
+ const { remotePath } = TOOL_SYNC_CONFIG[tool];
1598
+ if (existsChecks[i]) {
1599
+ const jsonContent = await downloadFromWebDAV(config, remotePath);
1600
+ const remoteConfig = JSON.parse(jsonContent);
1601
+ try {
1602
+ const decryptedProviders = decryptProviders(remoteConfig.providers, password);
1603
+ remoteConfigs.push({ tool, config: remoteConfig, decryptedProviders });
1604
+ } catch (error) {
1605
+ throw new Error("\u89E3\u5BC6\u5931\u8D25\uFF1A\u5BC6\u7801\u9519\u8BEF\u6216\u6570\u636E\u635F\u574F");
1606
+ }
1607
+ } else {
1608
+ remoteConfigs.push({ tool, config: null, decryptedProviders: null });
1418
1609
  }
1419
- } catch (error) {
1420
- throw new Error("\u89E3\u5BC6\u5931\u8D25\uFF1A\u5BC6\u7801\u9519\u8BEF\u6216\u6570\u636E\u635F\u574F");
1421
1610
  }
1422
1611
  const backupPaths = [];
1423
- const ccmanDir2 = getCcmanDir();
1424
- const codexConfigPath = path8.join(ccmanDir2, "codex.json");
1425
- const claudeConfigPath = path8.join(ccmanDir2, "claude.json");
1426
1612
  try {
1427
- if (fs7.existsSync(codexConfigPath)) {
1428
- backupPaths.push(backupConfig(codexConfigPath));
1429
- }
1430
- if (fs7.existsSync(claudeConfigPath)) {
1431
- backupPaths.push(backupConfig(claudeConfigPath));
1613
+ for (const tool of toolKeys) {
1614
+ const { configFilename } = TOOL_SYNC_CONFIG[tool];
1615
+ const configPath = path8.join(ccmanDir2, configFilename);
1616
+ if (fs8.existsSync(configPath)) {
1617
+ backupPaths.push(backupConfig(configPath));
1618
+ }
1432
1619
  }
1433
1620
  } catch (error) {
1434
1621
  throw new Error(`\u5907\u4EFD\u5931\u8D25: ${error.message}`);
1435
1622
  }
1436
1623
  try {
1437
- if (remoteCodexConfig && decryptedCodexProviders) {
1438
- const newCodexConfig = {
1439
- ...remoteCodexConfig,
1440
- // 使用云端配置的所有字段
1441
- providers: decryptedCodexProviders
1442
- // 只替换 providers(解密后的)
1443
- };
1444
- writeJSON(codexConfigPath, newCodexConfig);
1445
- applyCurrentProvider("codex", newCodexConfig);
1446
- }
1447
- if (remoteClaudeConfig && decryptedClaudeProviders) {
1448
- const newClaudeConfig = {
1449
- ...remoteClaudeConfig,
1624
+ for (const { tool, config: remoteConfig, decryptedProviders } of remoteConfigs) {
1625
+ if (!remoteConfig || !decryptedProviders)
1626
+ continue;
1627
+ const { configFilename } = TOOL_SYNC_CONFIG[tool];
1628
+ const configPath = path8.join(ccmanDir2, configFilename);
1629
+ const newConfig = {
1630
+ ...remoteConfig,
1450
1631
  // 使用云端配置的所有字段
1451
- providers: decryptedClaudeProviders
1632
+ providers: decryptedProviders
1452
1633
  // 只替换 providers(解密后的)
1453
1634
  };
1454
- writeJSON(claudeConfigPath, newClaudeConfig);
1455
- applyCurrentProvider("claude", newClaudeConfig);
1635
+ writeJSON(configPath, newConfig);
1636
+ applyCurrentProvider(tool, newConfig);
1456
1637
  }
1457
1638
  updateLastSyncTime();
1458
1639
  console.log("\u2705 \u914D\u7F6E\u5DF2\u4ECE\u4E91\u7AEF\u4E0B\u8F7D\u5E76\u5E94\u7528");
@@ -1460,17 +1641,21 @@ async function downloadFromCloud(config, password) {
1460
1641
  } catch (error) {
1461
1642
  for (const backupPath of backupPaths) {
1462
1643
  const originalPath = backupPath.replace(/\.backup\.\d+$/, "");
1463
- if (fs7.existsSync(backupPath)) {
1464
- fs7.copyFileSync(backupPath, originalPath);
1644
+ if (fs8.existsSync(backupPath)) {
1645
+ fs8.copyFileSync(backupPath, originalPath);
1465
1646
  }
1466
1647
  }
1467
1648
  throw new Error(`\u8986\u76D6\u914D\u7F6E\u5931\u8D25\uFF0C\u5DF2\u6062\u590D\u5907\u4EFD: ${error.message}`);
1468
1649
  }
1469
1650
  }
1470
1651
  async function mergeSync(config, password) {
1471
- const codexExists = await existsOnWebDAV(config, CODEX_REMOTE_PATH);
1472
- const claudeExists = await existsOnWebDAV(config, CLAUDE_REMOTE_PATH);
1473
- if (!codexExists && !claudeExists) {
1652
+ const ccmanDir2 = getCcmanDir();
1653
+ const toolKeys = Object.keys(TOOL_SYNC_CONFIG);
1654
+ const existsChecks = await Promise.all(toolKeys.map(async (tool) => {
1655
+ const { remotePath } = TOOL_SYNC_CONFIG[tool];
1656
+ return existsOnWebDAV(config, remotePath);
1657
+ }));
1658
+ if (!existsChecks.some((exists) => exists)) {
1474
1659
  console.log("\u8FDC\u7A0B\u914D\u7F6E\u4E0D\u5B58\u5728\uFF0C\u6267\u884C\u4E0A\u4F20\u64CD\u4F5C");
1475
1660
  await uploadToCloud(config, password);
1476
1661
  return {
@@ -1478,30 +1663,31 @@ async function mergeSync(config, password) {
1478
1663
  backupPaths: []
1479
1664
  };
1480
1665
  }
1481
- const codexJson = codexExists ? await downloadFromWebDAV(config, CODEX_REMOTE_PATH) : null;
1482
- const claudeJson = claudeExists ? await downloadFromWebDAV(config, CLAUDE_REMOTE_PATH) : null;
1483
- const remoteCodexConfig = codexJson ? JSON.parse(codexJson) : null;
1484
- const remoteClaudeConfig = claudeJson ? JSON.parse(claudeJson) : null;
1485
- let remoteCodexProviders = [];
1486
- let remoteClaudeProviders = [];
1487
- try {
1488
- if (remoteCodexConfig) {
1489
- remoteCodexProviders = decryptProviders(remoteCodexConfig.providers, password);
1490
- }
1491
- if (remoteClaudeConfig) {
1492
- remoteClaudeProviders = decryptProviders(remoteClaudeConfig.providers, password);
1666
+ const mergeDataList = [];
1667
+ for (let i = 0; i < toolKeys.length; i++) {
1668
+ const tool = toolKeys[i];
1669
+ const { remotePath, configFilename } = TOOL_SYNC_CONFIG[tool];
1670
+ const configPath = path8.join(ccmanDir2, configFilename);
1671
+ const localConfig = readJSON(configPath);
1672
+ let remoteProviders = [];
1673
+ if (existsChecks[i]) {
1674
+ try {
1675
+ const jsonContent = await downloadFromWebDAV(config, remotePath);
1676
+ const remoteConfig = JSON.parse(jsonContent);
1677
+ remoteProviders = decryptProviders(remoteConfig.providers, password);
1678
+ } catch (error) {
1679
+ throw new Error("\u89E3\u5BC6\u5931\u8D25\uFF1A\u5BC6\u7801\u9519\u8BEF\u6216\u6570\u636E\u635F\u574F");
1680
+ }
1493
1681
  }
1494
- } catch (error) {
1495
- throw new Error("\u89E3\u5BC6\u5931\u8D25\uFF1A\u5BC6\u7801\u9519\u8BEF\u6216\u6570\u636E\u635F\u574F");
1682
+ const mergeResult = mergeProviders(localConfig.providers, remoteProviders);
1683
+ mergeDataList.push({
1684
+ tool,
1685
+ localConfig,
1686
+ remoteProviders,
1687
+ mergeResult
1688
+ });
1496
1689
  }
1497
- const ccmanDir2 = getCcmanDir();
1498
- const codexConfigPath = path8.join(ccmanDir2, "codex.json");
1499
- const claudeConfigPath = path8.join(ccmanDir2, "claude.json");
1500
- const localCodexConfig = readJSON(codexConfigPath);
1501
- const localClaudeConfig = readJSON(claudeConfigPath);
1502
- const codexMergeResult = mergeProviders(localCodexConfig.providers, remoteCodexProviders);
1503
- const claudeMergeResult = mergeProviders(localClaudeConfig.providers, remoteClaudeProviders);
1504
- const hasChanges = codexMergeResult.hasChanges || claudeMergeResult.hasChanges;
1690
+ const hasChanges = mergeDataList.some((data) => data.mergeResult.hasChanges);
1505
1691
  if (!hasChanges) {
1506
1692
  console.log("\u2139\uFE0F \u914D\u7F6E\u5DF2\u540C\u6B65\uFF0C\u65E0\u9700\u64CD\u4F5C");
1507
1693
  return {
@@ -1511,56 +1697,47 @@ async function mergeSync(config, password) {
1511
1697
  }
1512
1698
  const backupPaths = [];
1513
1699
  try {
1514
- if (fs7.existsSync(codexConfigPath)) {
1515
- backupPaths.push(backupConfig(codexConfigPath));
1516
- }
1517
- if (fs7.existsSync(claudeConfigPath)) {
1518
- backupPaths.push(backupConfig(claudeConfigPath));
1700
+ for (const tool of toolKeys) {
1701
+ const { configFilename } = TOOL_SYNC_CONFIG[tool];
1702
+ const configPath = path8.join(ccmanDir2, configFilename);
1703
+ if (fs8.existsSync(configPath)) {
1704
+ backupPaths.push(backupConfig(configPath));
1705
+ }
1519
1706
  }
1520
1707
  } catch (error) {
1521
1708
  throw new Error(`\u5907\u4EFD\u5931\u8D25: ${error.message}`);
1522
1709
  }
1523
- const mergedCodexPresets = mergePresets(localCodexConfig.presets, remoteCodexConfig?.presets);
1524
- const mergedClaudePresets = mergePresets(localClaudeConfig.presets, remoteClaudeConfig?.presets);
1525
1710
  try {
1526
- const mergedCodexConfig = {
1527
- ...localCodexConfig,
1528
- // 保留本地配置的所有字段
1529
- providers: codexMergeResult.merged,
1530
- // 替换为合并后的 providers
1531
- presets: mergedCodexPresets
1532
- // 替换为合并后的 presets
1533
- };
1534
- const mergedClaudeConfig = {
1535
- ...localClaudeConfig,
1536
- // 保留本地配置的所有字段
1537
- providers: claudeMergeResult.merged,
1538
- // 替换为合并后的 providers
1539
- presets: mergedClaudePresets
1540
- // 替换为合并后的 presets
1541
- };
1542
- writeJSON(codexConfigPath, mergedCodexConfig);
1543
- writeJSON(claudeConfigPath, mergedClaudeConfig);
1544
- applyCurrentProvider("codex", mergedCodexConfig);
1545
- applyCurrentProvider("claude", mergedClaudeConfig);
1546
- const encryptedCodexProviders = encryptProviders(codexMergeResult.merged, password);
1547
- const encryptedClaudeProviders = encryptProviders(claudeMergeResult.merged, password);
1548
- const encryptedCodexConfig = {
1549
- ...mergedCodexConfig,
1550
- // 保留合并后配置的所有字段
1551
- providers: encryptedCodexProviders
1552
- // 只替换 providers(加密后的)
1553
- };
1554
- const encryptedClaudeConfig = {
1555
- ...mergedClaudeConfig,
1556
- // 保留合并后配置的所有字段
1557
- providers: encryptedClaudeProviders
1558
- // 只替换 providers(加密后的)
1559
- };
1560
- const codexJson2 = JSON.stringify(encryptedCodexConfig, null, 2);
1561
- const claudeJson2 = JSON.stringify(encryptedClaudeConfig, null, 2);
1562
- await uploadToWebDAV(config, CODEX_REMOTE_PATH, codexJson2);
1563
- await uploadToWebDAV(config, CLAUDE_REMOTE_PATH, claudeJson2);
1711
+ for (let i = 0; i < mergeDataList.length; i++) {
1712
+ const { tool, localConfig, mergeResult } = mergeDataList[i];
1713
+ const { remotePath, configFilename } = TOOL_SYNC_CONFIG[tool];
1714
+ const configPath = path8.join(ccmanDir2, configFilename);
1715
+ let remoteConfig = null;
1716
+ if (existsChecks[i]) {
1717
+ const jsonContent2 = await downloadFromWebDAV(config, remotePath);
1718
+ remoteConfig = JSON.parse(jsonContent2);
1719
+ }
1720
+ const mergedPresets = mergePresets(localConfig.presets, remoteConfig?.presets);
1721
+ const mergedConfig = {
1722
+ ...localConfig,
1723
+ // 保留本地配置的所有字段
1724
+ providers: mergeResult.merged,
1725
+ // 替换为合并后的 providers
1726
+ presets: mergedPresets
1727
+ // 替换为合并后的 presets
1728
+ };
1729
+ writeJSON(configPath, mergedConfig);
1730
+ applyCurrentProvider(tool, mergedConfig);
1731
+ const encryptedProviders = encryptProviders(mergeResult.merged, password);
1732
+ const encryptedConfig = {
1733
+ ...mergedConfig,
1734
+ // 保留合并后配置的所有字段
1735
+ providers: encryptedProviders
1736
+ // 只替换 providers(加密后的)
1737
+ };
1738
+ const jsonContent = JSON.stringify(encryptedConfig, null, 2);
1739
+ await uploadToWebDAV(config, remotePath, jsonContent);
1740
+ }
1564
1741
  updateLastSyncTime();
1565
1742
  console.log("\u2705 \u914D\u7F6E\u5DF2\u5408\u5E76\u5E76\u540C\u6B65\u5230\u4E91\u7AEF");
1566
1743
  return {
@@ -1570,8 +1747,8 @@ async function mergeSync(config, password) {
1570
1747
  } catch (error) {
1571
1748
  for (const backupPath of backupPaths) {
1572
1749
  const originalPath = backupPath.replace(/\.backup\.\d+$/, "");
1573
- if (fs7.existsSync(backupPath)) {
1574
- fs7.copyFileSync(backupPath, originalPath);
1750
+ if (fs8.existsSync(backupPath)) {
1751
+ fs8.copyFileSync(backupPath, originalPath);
1575
1752
  }
1576
1753
  }
1577
1754
  throw new Error(`\u5408\u5E76\u914D\u7F6E\u5931\u8D25\uFF0C\u5DF2\u6062\u590D\u5907\u4EFD: ${error.message}`);
@@ -1585,13 +1762,10 @@ function applyCurrentProvider(tool, config) {
1585
1762
  if (!provider) {
1586
1763
  return;
1587
1764
  }
1588
- if (tool === "codex") {
1589
- writeCodexConfig(provider);
1590
- } else {
1591
- writeClaudeConfig(provider);
1592
- }
1765
+ const { writerFunc } = TOOL_SYNC_CONFIG[tool];
1766
+ writerFunc(provider);
1593
1767
  }
1594
- var CODEX_REMOTE_PATH, CLAUDE_REMOTE_PATH;
1768
+ var TOOL_SYNC_CONFIG;
1595
1769
  var init_sync_v2 = __esm({
1596
1770
  "../core/dist/sync/sync-v2.js"() {
1597
1771
  "use strict";
@@ -1604,13 +1778,30 @@ var init_sync_v2 = __esm({
1604
1778
  init_file();
1605
1779
  init_codex();
1606
1780
  init_claude();
1607
- CODEX_REMOTE_PATH = ".ccman/codex.json";
1608
- CLAUDE_REMOTE_PATH = ".ccman/claude.json";
1781
+ init_gemini2();
1782
+ init_constants();
1783
+ TOOL_SYNC_CONFIG = {
1784
+ [MAIN_TOOL_TYPES.CODEX]: {
1785
+ remotePath: ".ccman/codex.json",
1786
+ configFilename: "codex.json",
1787
+ writerFunc: writeCodexConfig
1788
+ },
1789
+ [MAIN_TOOL_TYPES.CLAUDE]: {
1790
+ remotePath: ".ccman/claude.json",
1791
+ configFilename: "claude.json",
1792
+ writerFunc: writeClaudeConfig
1793
+ },
1794
+ [MAIN_TOOL_TYPES.GEMINI]: {
1795
+ remotePath: ".ccman/gemini.json",
1796
+ configFilename: "gemini.json",
1797
+ writerFunc: writeGeminiConfig
1798
+ }
1799
+ };
1609
1800
  }
1610
1801
  });
1611
1802
 
1612
1803
  // ../core/dist/export.js
1613
- import * as fs8 from "fs";
1804
+ import * as fs9 from "fs";
1614
1805
  import * as path9 from "path";
1615
1806
  function validateExport() {
1616
1807
  const ccmanDir2 = getCcmanDir();
@@ -1640,7 +1831,7 @@ function validateImportDir(sourceDir) {
1640
1831
  foundFiles: []
1641
1832
  };
1642
1833
  }
1643
- const stats = fs8.statSync(sourceDir);
1834
+ const stats = fs9.statSync(sourceDir);
1644
1835
  if (!stats.isDirectory()) {
1645
1836
  return {
1646
1837
  valid: false,
@@ -1680,13 +1871,13 @@ function exportConfig(targetDir) {
1680
1871
  const codexSrc = path9.join(ccmanDir2, CODEX_CONFIG_FILE);
1681
1872
  const codexDst = path9.join(targetDir, CODEX_CONFIG_FILE);
1682
1873
  if (fileExists(codexSrc)) {
1683
- fs8.copyFileSync(codexSrc, codexDst);
1874
+ fs9.copyFileSync(codexSrc, codexDst);
1684
1875
  exportedFiles.push(CODEX_CONFIG_FILE);
1685
1876
  }
1686
1877
  const claudeSrc = path9.join(ccmanDir2, CLAUDE_CONFIG_FILE);
1687
1878
  const claudeDst = path9.join(targetDir, CLAUDE_CONFIG_FILE);
1688
1879
  if (fileExists(claudeSrc)) {
1689
- fs8.copyFileSync(claudeSrc, claudeDst);
1880
+ fs9.copyFileSync(claudeSrc, claudeDst);
1690
1881
  exportedFiles.push(CLAUDE_CONFIG_FILE);
1691
1882
  }
1692
1883
  return {
@@ -1712,7 +1903,7 @@ function importConfig(sourceDir) {
1712
1903
  backupPaths.push(backupPath);
1713
1904
  }
1714
1905
  const codexSrc = path9.join(sourceDir, CODEX_CONFIG_FILE);
1715
- fs8.copyFileSync(codexSrc, codexDst);
1906
+ fs9.copyFileSync(codexSrc, codexDst);
1716
1907
  importedFiles.push(CODEX_CONFIG_FILE);
1717
1908
  }
1718
1909
  if (validation.foundFiles.includes(CLAUDE_CONFIG_FILE)) {
@@ -1722,7 +1913,7 @@ function importConfig(sourceDir) {
1722
1913
  backupPaths.push(backupPath);
1723
1914
  }
1724
1915
  const claudeSrc = path9.join(sourceDir, CLAUDE_CONFIG_FILE);
1725
- fs8.copyFileSync(claudeSrc, claudeDst);
1916
+ fs9.copyFileSync(claudeSrc, claudeDst);
1726
1917
  importedFiles.push(CLAUDE_CONFIG_FILE);
1727
1918
  }
1728
1919
  return {
@@ -1734,7 +1925,7 @@ function importConfig(sourceDir) {
1734
1925
  for (const backupPath of backupPaths) {
1735
1926
  const originalPath = backupPath.replace(/\.backup\.\d+$/, "");
1736
1927
  if (fileExists(backupPath)) {
1737
- fs8.copyFileSync(backupPath, originalPath);
1928
+ fs9.copyFileSync(backupPath, originalPath);
1738
1929
  }
1739
1930
  }
1740
1931
  throw new Error(`\u5BFC\u5165\u5931\u8D25\uFF0C\u5DF2\u6062\u590D\u5907\u4EFD: ${error.message}`);
@@ -1753,7 +1944,7 @@ var init_export = __esm({
1753
1944
  });
1754
1945
 
1755
1946
  // ../core/dist/claude-clean.js
1756
- import * as fs9 from "fs";
1947
+ import * as fs10 from "fs";
1757
1948
  function formatBytes(bytes) {
1758
1949
  if (bytes < 1024)
1759
1950
  return `${bytes} B`;
@@ -1763,7 +1954,7 @@ function formatBytes(bytes) {
1763
1954
  }
1764
1955
  function getFileSize(filePath) {
1765
1956
  try {
1766
- const stats = fs9.statSync(filePath);
1957
+ const stats = fs10.statSync(filePath);
1767
1958
  return stats.size;
1768
1959
  } catch {
1769
1960
  return 0;
@@ -1772,18 +1963,18 @@ function getFileSize(filePath) {
1772
1963
  function backupFile(filePath) {
1773
1964
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-").split(".")[0];
1774
1965
  const backupPath = `${filePath}.backup-${timestamp}`;
1775
- fs9.copyFileSync(filePath, backupPath);
1966
+ fs10.copyFileSync(filePath, backupPath);
1776
1967
  return backupPath;
1777
1968
  }
1778
1969
  function saveJsonAtomic(filePath, data) {
1779
1970
  const tempPath = `${filePath}.tmp`;
1780
1971
  const content = JSON.stringify(data, null, 2);
1781
- fs9.writeFileSync(tempPath, content, { mode: 384 });
1782
- fs9.renameSync(tempPath, filePath);
1972
+ fs10.writeFileSync(tempPath, content, { mode: 384 });
1973
+ fs10.renameSync(tempPath, filePath);
1783
1974
  }
1784
1975
  function analyzeClaudeJson() {
1785
1976
  const filePath = getClaudeJsonPath();
1786
- if (!fs9.existsSync(filePath)) {
1977
+ if (!fs10.existsSync(filePath)) {
1787
1978
  return {
1788
1979
  fileSize: 0,
1789
1980
  fileSizeFormatted: "0 B",
@@ -1799,7 +1990,7 @@ function analyzeClaudeJson() {
1799
1990
  };
1800
1991
  }
1801
1992
  const fileSize = getFileSize(filePath);
1802
- const content = fs9.readFileSync(filePath, "utf-8");
1993
+ const content = fs10.readFileSync(filePath, "utf-8");
1803
1994
  const config = JSON.parse(content);
1804
1995
  const projects = config.projects || {};
1805
1996
  const projectHistory = [];
@@ -1833,12 +2024,12 @@ function analyzeClaudeJson() {
1833
2024
  }
1834
2025
  function cleanClaudeJson(options = {}) {
1835
2026
  const filePath = getClaudeJsonPath();
1836
- if (!fs9.existsSync(filePath)) {
2027
+ if (!fs10.existsSync(filePath)) {
1837
2028
  throw new Error(`${filePath} \u6587\u4EF6\u4E0D\u5B58\u5728`);
1838
2029
  }
1839
2030
  const backupPath = backupFile(filePath);
1840
2031
  const sizeBefore = getFileSize(filePath);
1841
- const content = fs9.readFileSync(filePath, "utf-8");
2032
+ const content = fs10.readFileSync(filePath, "utf-8");
1842
2033
  const config = JSON.parse(content);
1843
2034
  const cleanedItems = applyCleanOptions(config, options);
1844
2035
  saveJsonAtomic(filePath, config);
@@ -1919,10 +2110,12 @@ var init_dist = __esm({
1919
2110
  "../core/dist/index.js"() {
1920
2111
  "use strict";
1921
2112
  init_package();
2113
+ init_constants();
1922
2114
  init_tool_manager();
1923
2115
  init_codex2();
1924
2116
  init_claude2();
1925
2117
  init_mcp2();
2118
+ init_gemini();
1926
2119
  init_mcp();
1927
2120
  init_migrate();
1928
2121
  init_paths();
@@ -2646,7 +2839,7 @@ var init_sync = __esm({
2646
2839
 
2647
2840
  // src/index.ts
2648
2841
  import { Command as Command3 } from "commander";
2649
- import chalk33 from "chalk";
2842
+ import chalk40 from "chalk";
2650
2843
 
2651
2844
  // src/utils/logo.ts
2652
2845
  init_dist();
@@ -2705,6 +2898,21 @@ function formatProviderTable(providers, currentId) {
2705
2898
  }
2706
2899
 
2707
2900
  // src/interactive.ts
2901
+ var CLI_TOOL_CONFIG = {
2902
+ [TOOL_TYPES.CODEX]: { name: "Codex", emoji: "\u{1F536}", cmd: "cx" },
2903
+ [TOOL_TYPES.CLAUDE]: { name: "Claude", emoji: "\u{1F537}", cmd: "cc" },
2904
+ [TOOL_TYPES.GEMINI]: { name: "Gemini", emoji: "\u{1F48E}", cmd: "gm" }
2905
+ };
2906
+ function getManager(tool) {
2907
+ switch (tool) {
2908
+ case TOOL_TYPES.CODEX:
2909
+ return createCodexManager();
2910
+ case TOOL_TYPES.CLAUDE:
2911
+ return createClaudeManager();
2912
+ case TOOL_TYPES.GEMINI:
2913
+ return createGeminiManager();
2914
+ }
2915
+ }
2708
2916
  async function promptProviderForm(defaults) {
2709
2917
  const answers = await inquirer7.prompt([
2710
2918
  {
@@ -2766,6 +2974,7 @@ async function startMainMenu() {
2766
2974
  choices: [
2767
2975
  { name: "\u{1F537} Claude \u7BA1\u7406", value: "claude" },
2768
2976
  { name: "\u{1F536} Codex \u7BA1\u7406", value: "codex" },
2977
+ { name: "\u{1F48E} Gemini \u7BA1\u7406", value: "gemini" },
2769
2978
  { name: "\u{1F504} WebDAV \u540C\u6B65", value: "sync" },
2770
2979
  { name: "\u{1F4E6} \u9884\u7F6E\u670D\u52A1\u5546\u7BA1\u7406", value: "presets" },
2771
2980
  { name: "\u274C \u9000\u51FA", value: "exit" }
@@ -2780,6 +2989,8 @@ async function startMainMenu() {
2780
2989
  await startClaudeMenu();
2781
2990
  } else if (choice === "codex") {
2782
2991
  await startCodexMenu();
2992
+ } else if (choice === "gemini") {
2993
+ await startGeminiMenu();
2783
2994
  } else if (choice === "sync") {
2784
2995
  const { startSyncMenu: startSyncMenu2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
2785
2996
  await startSyncMenu2();
@@ -2789,14 +3000,16 @@ async function startMainMenu() {
2789
3000
  }
2790
3001
  }
2791
3002
  async function startClaudeMenu() {
2792
- await showToolMenu("claude");
3003
+ await showToolMenu(TOOL_TYPES.CLAUDE);
2793
3004
  }
2794
3005
  async function startCodexMenu() {
2795
- await showToolMenu("codex");
3006
+ await showToolMenu(TOOL_TYPES.CODEX);
3007
+ }
3008
+ async function startGeminiMenu() {
3009
+ await showToolMenu(TOOL_TYPES.GEMINI);
2796
3010
  }
2797
3011
  async function showToolMenu(tool) {
2798
- const toolName = tool === "claude" ? "Claude" : "Codex";
2799
- const toolEmoji = tool === "claude" ? "\u{1F537}" : "\u{1F536}";
3012
+ const { name: toolName, emoji: toolEmoji } = CLI_TOOL_CONFIG[tool];
2800
3013
  while (true) {
2801
3014
  console.log();
2802
3015
  const { action } = await inquirer7.prompt([
@@ -2810,7 +3023,7 @@ async function showToolMenu(tool) {
2810
3023
  { name: "\u{1F4CB} \u5217\u51FA\u6240\u6709\u670D\u52A1\u5546", value: "list" },
2811
3024
  { name: "\u{1F441}\uFE0F \u67E5\u770B\u5F53\u524D\u670D\u52A1\u5546", value: "current" },
2812
3025
  { name: "\u270F\uFE0F \u7F16\u8F91\u670D\u52A1\u5546", value: "edit" },
2813
- { name: "\u{1F4CB} \u514B\u9686\u670D\u52A1\u5546", value: "clone" },
3026
+ { name: "\u{1F501} \u514B\u9686\u670D\u52A1\u5546", value: "clone" },
2814
3027
  { name: "\u{1F5D1}\uFE0F \u5220\u9664\u670D\u52A1\u5546", value: "remove" },
2815
3028
  { name: "\u2B05\uFE0F \u8FD4\u56DE\u4E0A\u7EA7", value: "back" }
2816
3029
  ]
@@ -2861,8 +3074,8 @@ async function showPresetsMenu() {
2861
3074
  console.log(chalk11.yellow("\n\u26A0\uFE0F \u9884\u7F6E\u670D\u52A1\u5546\u7BA1\u7406\u529F\u80FD\u5373\u5C06\u63A8\u51FA\n"));
2862
3075
  }
2863
3076
  async function handleAdd(tool) {
2864
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
2865
- const toolName = tool === "claude" ? "Claude" : "Codex";
3077
+ const manager = getManager(tool);
3078
+ const { name: toolName, cmd } = CLI_TOOL_CONFIG[tool];
2866
3079
  const presets = manager.listPresets();
2867
3080
  console.log(chalk11.bold(`
2868
3081
  \u{1F4DD} \u6DFB\u52A0 ${toolName} \u670D\u52A1\u5546
@@ -2960,14 +3173,12 @@ async function handleAdd(tool) {
2960
3173
  manager.switch(provider.id);
2961
3174
  console.log(chalk11.green("\u2705 \u5DF2\u5207\u6362\u5230\u65B0\u670D\u52A1\u5546\n"));
2962
3175
  } else {
2963
- console.log(
2964
- chalk11.blue("\u{1F4A1} \u7A0D\u540E\u5207\u6362:") + chalk11.white(` ccman ${tool === "codex" ? "cx" : "cc"} use "${provider.name}"
2965
- `)
2966
- );
3176
+ console.log(chalk11.blue("\u{1F4A1} \u7A0D\u540E\u5207\u6362:") + chalk11.white(` ccman ${cmd} use "${provider.name}"
3177
+ `));
2967
3178
  }
2968
3179
  }
2969
3180
  async function handleSwitch(tool) {
2970
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
3181
+ const manager = getManager(tool);
2971
3182
  const providers = manager.list();
2972
3183
  const current = manager.getCurrent();
2973
3184
  if (providers.length === 0) {
@@ -2992,10 +3203,10 @@ async function handleSwitch(tool) {
2992
3203
  `));
2993
3204
  }
2994
3205
  async function handleList(tool) {
2995
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
3206
+ const manager = getManager(tool);
2996
3207
  const providers = manager.list();
2997
3208
  const current = manager.getCurrent();
2998
- const toolName = tool === "claude" ? "Claude" : "Codex";
3209
+ const { name: toolName } = CLI_TOOL_CONFIG[tool];
2999
3210
  if (providers.length === 0) {
3000
3211
  console.log(chalk11.yellow(`
3001
3212
  \u26A0\uFE0F \u6682\u65E0 ${toolName} \u670D\u52A1\u5546
@@ -3007,9 +3218,9 @@ async function handleList(tool) {
3007
3218
  console.log(formatProviderTable(providers, current?.id));
3008
3219
  }
3009
3220
  async function handleCurrent(tool) {
3010
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
3221
+ const manager = getManager(tool);
3011
3222
  const current = manager.getCurrent();
3012
- const toolName = tool === "claude" ? "Claude" : "Codex";
3223
+ const { name: toolName } = CLI_TOOL_CONFIG[tool];
3013
3224
  if (!current) {
3014
3225
  console.log(chalk11.yellow(`
3015
3226
  \u26A0\uFE0F \u672A\u9009\u62E9\u4EFB\u4F55 ${toolName} \u670D\u52A1\u5546
@@ -3028,7 +3239,7 @@ async function handleCurrent(tool) {
3028
3239
  console.log();
3029
3240
  }
3030
3241
  async function handleEdit(tool) {
3031
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
3242
+ const manager = getManager(tool);
3032
3243
  const providers = manager.list();
3033
3244
  if (providers.length === 0) {
3034
3245
  console.log(chalk11.yellow("\n\u26A0\uFE0F \u6682\u65E0\u670D\u52A1\u5546\n"));
@@ -3089,7 +3300,7 @@ async function handleEdit(tool) {
3089
3300
  console.log(chalk11.green("\n\u2705 \u7F16\u8F91\u6210\u529F\n"));
3090
3301
  }
3091
3302
  async function handleClone(tool) {
3092
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
3303
+ const manager = getManager(tool);
3093
3304
  const providers = manager.list();
3094
3305
  if (providers.length === 0) {
3095
3306
  console.log(chalk11.yellow("\n\u26A0\uFE0F \u6682\u65E0\u670D\u52A1\u5546\n"));
@@ -3136,7 +3347,7 @@ async function handleClone(tool) {
3136
3347
  console.log();
3137
3348
  }
3138
3349
  async function handleRemove(tool) {
3139
- const manager = tool === "codex" ? createCodexManager() : createClaudeManager();
3350
+ const manager = getManager(tool);
3140
3351
  const providers = manager.list();
3141
3352
  if (providers.length === 0) {
3142
3353
  console.log(chalk11.yellow("\n\u26A0\uFE0F \u6682\u65E0\u670D\u52A1\u5546\n"));
@@ -4648,7 +4859,7 @@ function editCommand3(program2) {
4648
4859
  targetId = selectedId;
4649
4860
  }
4650
4861
  const provider = manager.get(targetId);
4651
- const currentCommand3 = provider.baseUrl;
4862
+ const currentCommand4 = provider.baseUrl;
4652
4863
  const currentArgs = provider.apiKey;
4653
4864
  const currentEnv = provider.model;
4654
4865
  console.log(chalk30.bold("\n\u270F\uFE0F \u7F16\u8F91 MCP \u670D\u52A1\u5668\n"));
@@ -4664,7 +4875,7 @@ function editCommand3(program2) {
4664
4875
  type: "input",
4665
4876
  name: "command",
4666
4877
  message: "\u542F\u52A8\u547D\u4EE4:",
4667
- default: currentCommand3
4878
+ default: currentCommand4
4668
4879
  },
4669
4880
  {
4670
4881
  type: "input",
@@ -4683,7 +4894,7 @@ function editCommand3(program2) {
4683
4894
  if (answers.name && answers.name !== provider.name) {
4684
4895
  updates.name = answers.name;
4685
4896
  }
4686
- if (answers.command && answers.command !== currentCommand3) {
4897
+ if (answers.command && answers.command !== currentCommand4) {
4687
4898
  updates.baseUrl = answers.command;
4688
4899
  }
4689
4900
  if (answers.args && answers.args !== currentArgs) {
@@ -4732,44 +4943,480 @@ function createMCPCommands(program2) {
4732
4943
  editCommand3(program2);
4733
4944
  }
4734
4945
 
4946
+ // src/commands/gemini/add.ts
4947
+ init_dist();
4948
+ import chalk31 from "chalk";
4949
+ import inquirer22 from "inquirer";
4950
+ function addCommand4(program2) {
4951
+ program2.command("add").description("\u6DFB\u52A0\u65B0\u7684 Gemini CLI \u670D\u52A1\u5546(\u4EA4\u4E92\u5F0F)").action(async () => {
4952
+ try {
4953
+ const manager = createGeminiManager();
4954
+ console.log(chalk31.bold("\n\u{1F4DD} \u6DFB\u52A0 Gemini CLI \u670D\u52A1\u5546\n"));
4955
+ const { usePreset } = await inquirer22.prompt([
4956
+ {
4957
+ type: "list",
4958
+ name: "usePreset",
4959
+ message: "\u9009\u62E9\u914D\u7F6E\u6765\u6E90:",
4960
+ choices: [
4961
+ { name: "\u{1F4E6} \u4F7F\u7528\u9884\u7F6E\u670D\u52A1\u5546", value: true },
4962
+ { name: "\u270F\uFE0F \u81EA\u5B9A\u4E49\u914D\u7F6E", value: false }
4963
+ ]
4964
+ }
4965
+ ]);
4966
+ let name;
4967
+ let desc;
4968
+ let baseUrl;
4969
+ let apiKey;
4970
+ if (usePreset) {
4971
+ const { presetName } = await inquirer22.prompt([
4972
+ {
4973
+ type: "list",
4974
+ name: "presetName",
4975
+ message: "\u9009\u62E9\u9884\u7F6E\u670D\u52A1\u5546:",
4976
+ choices: GEMINI_PRESETS.map((p) => ({
4977
+ name: `${p.name} - ${p.description}`,
4978
+ value: p.name
4979
+ }))
4980
+ }
4981
+ ]);
4982
+ const preset = GEMINI_PRESETS.find((p) => p.name === presetName);
4983
+ console.log(chalk31.blue(`
4984
+ \u4F7F\u7528\u9884\u8BBE: ${preset.name} - ${preset.description}
4985
+ `));
4986
+ const input = await promptProviderForm({
4987
+ name: preset.name,
4988
+ desc: "",
4989
+ baseUrl: preset.baseUrl,
4990
+ apiKey: ""
4991
+ });
4992
+ name = input.name;
4993
+ desc = input.desc;
4994
+ baseUrl = input.baseUrl;
4995
+ apiKey = input.apiKey;
4996
+ } else {
4997
+ const answers = await inquirer22.prompt([
4998
+ {
4999
+ type: "input",
5000
+ name: "name",
5001
+ message: "\u670D\u52A1\u5546\u540D\u79F0:",
5002
+ validate: (value) => {
5003
+ if (!value) return "\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
5004
+ return true;
5005
+ }
5006
+ },
5007
+ {
5008
+ type: "input",
5009
+ name: "baseUrl",
5010
+ message: "API \u5730\u5740 (\u53EF\u4E3A\u7A7A\uFF0C\u4F7F\u7528\u5B98\u65B9\u9ED8\u8BA4):",
5011
+ validate: (value) => {
5012
+ if (!value) return true;
5013
+ if (!value.startsWith("http://") && !value.startsWith("https://")) {
5014
+ return "API \u5730\u5740\u5FC5\u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934";
5015
+ }
5016
+ return true;
5017
+ }
5018
+ },
5019
+ {
5020
+ type: "password",
5021
+ name: "apiKey",
5022
+ message: "API \u5BC6\u94A5 (\u53EF\u4E3A\u7A7A\uFF0C\u4F7F\u7528\u73AF\u5883\u5DF2\u914D\u7F6E\u7684 Key):",
5023
+ mask: "*"
5024
+ }
5025
+ ]);
5026
+ name = answers.name;
5027
+ desc = void 0;
5028
+ baseUrl = answers.baseUrl || "";
5029
+ apiKey = answers.apiKey || "";
5030
+ }
5031
+ const provider = manager.add({ name, desc, baseUrl, apiKey });
5032
+ console.log();
5033
+ console.log(chalk31.green("\u2705 \u6DFB\u52A0\u6210\u529F"));
5034
+ console.log();
5035
+ console.log(` ${chalk31.bold(provider.name)} ${chalk31.blue("[Gemini CLI]")}`);
5036
+ console.log(` ${chalk31.gray(provider.baseUrl || "(\u4F7F\u7528\u9ED8\u8BA4\u7AEF\u70B9)")}`);
5037
+ console.log();
5038
+ const { switchNow } = await inquirer22.prompt([
5039
+ {
5040
+ type: "confirm",
5041
+ name: "switchNow",
5042
+ message: "\u662F\u5426\u7ACB\u5373\u5207\u6362\u5230\u6B64\u670D\u52A1\u5546?",
5043
+ default: true
5044
+ }
5045
+ ]);
5046
+ if (switchNow) {
5047
+ manager.switch(provider.id);
5048
+ console.log(chalk31.green("\u2705 \u5DF2\u5207\u6362\u5230\u65B0\u670D\u52A1\u5546"));
5049
+ console.log();
5050
+ console.log(chalk31.gray("\u914D\u7F6E\u5DF2\u66F4\u65B0:"));
5051
+ console.log(chalk31.gray(` - ${getGeminiSettingsPath()}`));
5052
+ console.log(chalk31.gray(` - ${getGeminiEnvPath()}`));
5053
+ } else {
5054
+ console.log(chalk31.blue("\u{1F4A1} \u7A0D\u540E\u5207\u6362:") + chalk31.white(` ccman gm use "${provider.name}"`));
5055
+ }
5056
+ } catch (error) {
5057
+ console.error(chalk31.red(`
5058
+ \u274C ${error.message}
5059
+ `));
5060
+ process.exit(1);
5061
+ }
5062
+ });
5063
+ }
5064
+
5065
+ // src/commands/gemini/list.ts
5066
+ init_dist();
5067
+ import chalk32 from "chalk";
5068
+ function listCommand4(program2) {
5069
+ program2.command("list").alias("ls").description("\u5217\u51FA\u6240\u6709 Gemini CLI \u670D\u52A1\u5546").action(async () => {
5070
+ try {
5071
+ const manager = createGeminiManager();
5072
+ const providers = manager.list();
5073
+ const current = manager.getCurrent();
5074
+ if (providers.length === 0) {
5075
+ console.log(chalk32.yellow("\n\u26A0\uFE0F \u6682\u65E0 Gemini CLI \u670D\u52A1\u5546\n"));
5076
+ console.log(chalk32.blue("\u{1F4A1} \u6DFB\u52A0\u670D\u52A1\u5546:") + chalk32.white(" ccman gm add\n"));
5077
+ return;
5078
+ }
5079
+ console.log(chalk32.bold(`
5080
+ \u{1F4CB} Gemini CLI \u670D\u52A1\u5546 (${providers.length} \u4E2A)`));
5081
+ console.log(formatProviderTable(providers, current?.id));
5082
+ } catch (error) {
5083
+ console.error(chalk32.red(`
5084
+ \u274C ${error.message}
5085
+ `));
5086
+ process.exit(1);
5087
+ }
5088
+ });
5089
+ }
5090
+
5091
+ // src/commands/gemini/use.ts
5092
+ init_dist();
5093
+ import chalk33 from "chalk";
5094
+ import inquirer23 from "inquirer";
5095
+ function useCommand3(program2) {
5096
+ program2.command("use [name]").description("\u5207\u6362 Gemini CLI \u670D\u52A1\u5546").action(async (name) => {
5097
+ try {
5098
+ const manager = createGeminiManager();
5099
+ const providers = manager.list();
5100
+ if (providers.length === 0) {
5101
+ console.log(chalk33.yellow("\n\u26A0\uFE0F \u6682\u65E0 Gemini CLI \u670D\u52A1\u5546\n"));
5102
+ console.log(chalk33.blue("\u{1F4A1} \u6DFB\u52A0\u670D\u52A1\u5546:") + chalk33.white(" ccman gm add\n"));
5103
+ return;
5104
+ }
5105
+ let targetId;
5106
+ if (name) {
5107
+ const provider2 = manager.findByName(name);
5108
+ if (!provider2) {
5109
+ throw new ProviderNotFoundError(name);
5110
+ }
5111
+ targetId = provider2.id;
5112
+ } else {
5113
+ const { selectedId } = await inquirer23.prompt([
5114
+ {
5115
+ type: "list",
5116
+ name: "selectedId",
5117
+ message: "\u9009\u62E9\u8981\u5207\u6362\u7684\u670D\u52A1\u5546:",
5118
+ choices: providers.map((p) => ({
5119
+ name: `${p.name} - ${p.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)"}`,
5120
+ value: p.id
5121
+ }))
5122
+ }
5123
+ ]);
5124
+ targetId = selectedId;
5125
+ }
5126
+ manager.switch(targetId);
5127
+ const provider = manager.get(targetId);
5128
+ console.log();
5129
+ console.log(chalk33.green("\u2705 \u5207\u6362\u6210\u529F"));
5130
+ console.log();
5131
+ console.log(` ${chalk33.bold(provider.name)} ${chalk33.blue("[Gemini CLI]")}`);
5132
+ console.log(` ${chalk33.gray(`URL: ${provider.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)"}`)}`);
5133
+ console.log();
5134
+ console.log(chalk33.gray("\u914D\u7F6E\u5DF2\u66F4\u65B0:"));
5135
+ console.log(chalk33.gray(` - ${getGeminiSettingsPath()}`));
5136
+ console.log(chalk33.gray(` - ${getGeminiEnvPath()}`));
5137
+ console.log();
5138
+ } catch (error) {
5139
+ if (error instanceof ProviderNotFoundError) {
5140
+ console.error(chalk33.red(`
5141
+ \u274C \u670D\u52A1\u5546\u4E0D\u5B58\u5728: ${error.message}
5142
+ `));
5143
+ console.log(chalk33.blue("\u{1F4A1} \u67E5\u770B\u6240\u6709\u670D\u52A1\u5546:") + chalk33.white(" ccman gm list\n"));
5144
+ } else {
5145
+ console.error(chalk33.red(`
5146
+ \u274C ${error.message}
5147
+ `));
5148
+ }
5149
+ process.exit(1);
5150
+ }
5151
+ });
5152
+ }
5153
+
5154
+ // src/commands/gemini/current.ts
5155
+ init_dist();
5156
+ import chalk34 from "chalk";
5157
+ function currentCommand3(program2) {
5158
+ program2.command("current").description("\u663E\u793A\u5F53\u524D Gemini CLI \u670D\u52A1\u5546").action(async () => {
5159
+ try {
5160
+ const manager = createGeminiManager();
5161
+ const current = manager.getCurrent();
5162
+ if (!current) {
5163
+ console.log(chalk34.yellow("\n\u26A0\uFE0F \u5F53\u524D\u6CA1\u6709\u6FC0\u6D3B\u7684 Gemini CLI \u670D\u52A1\u5546\n"));
5164
+ console.log(chalk34.blue("\u{1F4A1} \u5217\u51FA\u670D\u52A1\u5546:") + chalk34.white(" ccman gm list\n"));
5165
+ return;
5166
+ }
5167
+ console.log(chalk34.bold("\n\u{1F3AF} \u5F53\u524D Gemini CLI \u670D\u52A1\u5546\n"));
5168
+ console.log(` \u540D\u79F0: ${chalk34.bold(current.name)}`);
5169
+ console.log(` \u5730\u5740: ${chalk34.gray(current.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)")}`);
5170
+ console.log();
5171
+ } catch (error) {
5172
+ console.error(chalk34.red(`
5173
+ \u274C ${error.message}
5174
+ `));
5175
+ process.exit(1);
5176
+ }
5177
+ });
5178
+ }
5179
+
5180
+ // src/commands/gemini/remove.ts
5181
+ init_dist();
5182
+ import chalk35 from "chalk";
5183
+ import inquirer24 from "inquirer";
5184
+ function removeCommand4(program2) {
5185
+ program2.command("remove [name]").alias("rm").description("\u5220\u9664 Gemini CLI \u670D\u52A1\u5546").action(async (name) => {
5186
+ try {
5187
+ const manager = createGeminiManager();
5188
+ const providers = manager.list();
5189
+ if (providers.length === 0) {
5190
+ console.log(chalk35.yellow("\n\u26A0\uFE0F \u6682\u65E0 Gemini CLI \u670D\u52A1\u5546\n"));
5191
+ console.log(chalk35.blue("\u{1F4A1} \u6DFB\u52A0\u670D\u52A1\u5546:") + chalk35.white(" ccman gm add\n"));
5192
+ return;
5193
+ }
5194
+ let targetId;
5195
+ let targetName;
5196
+ if (name) {
5197
+ const provider = manager.findByName(name);
5198
+ if (!provider) {
5199
+ console.log(chalk35.red(`
5200
+ \u274C \u670D\u52A1\u5546\u4E0D\u5B58\u5728: ${name}
5201
+ `));
5202
+ process.exit(1);
5203
+ }
5204
+ targetId = provider.id;
5205
+ targetName = provider.name;
5206
+ } else {
5207
+ const { selectedId } = await inquirer24.prompt([
5208
+ {
5209
+ type: "list",
5210
+ name: "selectedId",
5211
+ message: "\u9009\u62E9\u8981\u5220\u9664\u7684\u670D\u52A1\u5546:",
5212
+ choices: providers.map((p) => ({
5213
+ name: `${p.name} - ${p.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)"}`,
5214
+ value: p.id
5215
+ }))
5216
+ }
5217
+ ]);
5218
+ const provider = manager.get(selectedId);
5219
+ targetId = provider.id;
5220
+ targetName = provider.name;
5221
+ }
5222
+ const { confirm } = await inquirer24.prompt([
5223
+ {
5224
+ type: "confirm",
5225
+ name: "confirm",
5226
+ message: `\u786E\u5B9A\u8981\u5220\u9664\u670D\u52A1\u5546 "${targetName}" \u5417\uFF1F`,
5227
+ default: false
5228
+ }
5229
+ ]);
5230
+ if (!confirm) {
5231
+ console.log(chalk35.gray("\n\u5DF2\u53D6\u6D88\u5220\u9664\n"));
5232
+ return;
5233
+ }
5234
+ manager.remove(targetId);
5235
+ console.log(chalk35.green("\n\u2705 \u5DF2\u5220\u9664\u670D\u52A1\u5546\n"));
5236
+ } catch (error) {
5237
+ console.error(chalk35.red(`
5238
+ \u274C ${error.message}
5239
+ `));
5240
+ process.exit(1);
5241
+ }
5242
+ });
5243
+ }
5244
+
5245
+ // src/commands/gemini/edit.ts
5246
+ init_dist();
5247
+ import chalk36 from "chalk";
5248
+ import inquirer25 from "inquirer";
5249
+ function editCommand4(program2) {
5250
+ program2.command("edit [name]").description("\u7F16\u8F91 Gemini CLI \u670D\u52A1\u5546").action(async (name) => {
5251
+ try {
5252
+ const manager = createGeminiManager();
5253
+ const providers = manager.list();
5254
+ if (providers.length === 0) {
5255
+ console.log(chalk36.yellow("\n\u26A0\uFE0F \u6682\u65E0 Gemini CLI \u670D\u52A1\u5546\n"));
5256
+ console.log(chalk36.blue("\u{1F4A1} \u6DFB\u52A0\u670D\u52A1\u5546:") + chalk36.white(" ccman gm add\n"));
5257
+ return;
5258
+ }
5259
+ let targetId;
5260
+ if (name) {
5261
+ const provider2 = manager.findByName(name);
5262
+ if (!provider2) {
5263
+ console.log(chalk36.red(`
5264
+ \u274C \u670D\u52A1\u5546\u4E0D\u5B58\u5728: ${name}
5265
+ `));
5266
+ process.exit(1);
5267
+ }
5268
+ targetId = provider2.id;
5269
+ } else {
5270
+ const { selectedId } = await inquirer25.prompt([
5271
+ {
5272
+ type: "list",
5273
+ name: "selectedId",
5274
+ message: "\u9009\u62E9\u8981\u7F16\u8F91\u7684\u670D\u52A1\u5546:",
5275
+ choices: providers.map((p) => ({
5276
+ name: `${p.name} - ${p.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)"}`,
5277
+ value: p.id
5278
+ }))
5279
+ }
5280
+ ]);
5281
+ targetId = selectedId;
5282
+ }
5283
+ const provider = manager.get(targetId);
5284
+ const input = await promptProviderForm({
5285
+ name: provider.name,
5286
+ desc: provider.desc ?? "",
5287
+ baseUrl: provider.baseUrl,
5288
+ apiKey: provider.apiKey
5289
+ });
5290
+ manager.edit(targetId, {
5291
+ name: input.name,
5292
+ desc: input.desc,
5293
+ baseUrl: input.baseUrl,
5294
+ apiKey: input.apiKey
5295
+ });
5296
+ console.log(chalk36.green("\n\u2705 \u7F16\u8F91\u6210\u529F\n"));
5297
+ } catch (error) {
5298
+ console.error(chalk36.red(`
5299
+ \u274C ${error.message}
5300
+ `));
5301
+ process.exit(1);
5302
+ }
5303
+ });
5304
+ }
5305
+
5306
+ // src/commands/gemini/clone.ts
5307
+ init_dist();
5308
+ import chalk37 from "chalk";
5309
+ import inquirer26 from "inquirer";
5310
+ function cloneCommand3(program2) {
5311
+ program2.command("clone [name]").description("\u514B\u9686 Gemini CLI \u670D\u52A1\u5546").action(async (name) => {
5312
+ try {
5313
+ const manager = createGeminiManager();
5314
+ const providers = manager.list();
5315
+ if (providers.length === 0) {
5316
+ console.log(chalk37.yellow("\n\u26A0\uFE0F \u6682\u65E0 Gemini CLI \u670D\u52A1\u5546\n"));
5317
+ console.log(chalk37.blue("\u{1F4A1} \u6DFB\u52A0\u670D\u52A1\u5546:") + chalk37.white(" ccman gm add\n"));
5318
+ return;
5319
+ }
5320
+ let sourceId;
5321
+ if (name) {
5322
+ const provider = manager.findByName(name);
5323
+ if (!provider) {
5324
+ console.log(chalk37.red(`
5325
+ \u274C \u670D\u52A1\u5546\u4E0D\u5B58\u5728: ${name}
5326
+ `));
5327
+ process.exit(1);
5328
+ }
5329
+ sourceId = provider.id;
5330
+ } else {
5331
+ const { selectedId } = await inquirer26.prompt([
5332
+ {
5333
+ type: "list",
5334
+ name: "selectedId",
5335
+ message: "\u9009\u62E9\u8981\u514B\u9686\u7684\u670D\u52A1\u5546:",
5336
+ choices: providers.map((p) => ({
5337
+ name: `${p.name} - ${p.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)"}`,
5338
+ value: p.id
5339
+ }))
5340
+ }
5341
+ ]);
5342
+ sourceId = selectedId;
5343
+ }
5344
+ const { newName } = await inquirer26.prompt([
5345
+ {
5346
+ type: "input",
5347
+ name: "newName",
5348
+ message: "\u8F93\u5165\u65B0\u670D\u52A1\u5546\u540D\u79F0:",
5349
+ validate: (value) => {
5350
+ if (!value) return "\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
5351
+ return true;
5352
+ }
5353
+ }
5354
+ ]);
5355
+ const newProvider = manager.clone(sourceId, newName);
5356
+ console.log();
5357
+ console.log(chalk37.green("\u2705 \u514B\u9686\u6210\u529F"));
5358
+ console.log();
5359
+ console.log(` ${chalk37.bold(newProvider.name)} ${chalk37.blue("[Gemini CLI]")}`);
5360
+ console.log(` ${chalk37.gray(newProvider.baseUrl || "(\u9ED8\u8BA4\u7AEF\u70B9)")}`);
5361
+ console.log();
5362
+ } catch (error) {
5363
+ console.error(chalk37.red(`
5364
+ \u274C ${error.message}
5365
+ `));
5366
+ process.exit(1);
5367
+ }
5368
+ });
5369
+ }
5370
+
5371
+ // src/commands/gemini/index.ts
5372
+ function createGeminiCommands(program2) {
5373
+ addCommand4(program2);
5374
+ listCommand4(program2);
5375
+ useCommand3(program2);
5376
+ currentCommand3(program2);
5377
+ removeCommand4(program2);
5378
+ editCommand4(program2);
5379
+ cloneCommand3(program2);
5380
+ }
5381
+
4735
5382
  // src/index.ts
4736
5383
  init_sync();
4737
5384
 
4738
5385
  // src/commands/export.ts
4739
5386
  init_dist();
4740
- import chalk31 from "chalk";
5387
+ import chalk38 from "chalk";
4741
5388
  import path10 from "path";
4742
5389
  function exportCommand(program2) {
4743
5390
  program2.command("export <\u76EE\u6807\u76EE\u5F55>").description("\u5BFC\u51FA\u914D\u7F6E\u5230\u672C\u5730\u76EE\u5F55\uFF08\u5305\u542B API Key\uFF09").action(async (targetDir) => {
4744
5391
  try {
4745
- console.log(chalk31.bold("\n\u{1F4E6} \u5BFC\u51FA\u914D\u7F6E\n"));
5392
+ console.log(chalk38.bold("\n\u{1F4E6} \u5BFC\u51FA\u914D\u7F6E\n"));
4746
5393
  const validation = validateExport();
4747
5394
  if (!validation.valid) {
4748
- console.log(chalk31.red(`\u274C ${validation.message}
5395
+ console.log(chalk38.red(`\u274C ${validation.message}
4749
5396
  `));
4750
5397
  process.exit(1);
4751
5398
  }
4752
5399
  const resolvedPath = targetDir.startsWith("~") ? path10.join(process.env.HOME || "", targetDir.slice(1)) : path10.resolve(targetDir);
4753
5400
  console.log("\u5BFC\u51FA\u6587\u4EF6:");
4754
- console.log(` ${chalk31.cyan("codex.json")} - Codex \u914D\u7F6E`);
4755
- console.log(` ${chalk31.cyan("claude.json")} - Claude \u914D\u7F6E`);
5401
+ console.log(` ${chalk38.cyan("codex.json")} - Codex \u914D\u7F6E`);
5402
+ console.log(` ${chalk38.cyan("claude.json")} - Claude \u914D\u7F6E`);
4756
5403
  console.log();
4757
- console.log(`\u76EE\u6807\u76EE\u5F55: ${chalk31.cyan(resolvedPath)}`);
5404
+ console.log(`\u76EE\u6807\u76EE\u5F55: ${chalk38.cyan(resolvedPath)}`);
4758
5405
  console.log();
4759
- console.log(chalk31.yellow("\u26A0\uFE0F \u5BFC\u51FA\u6587\u4EF6\u5305\u542B API Key\uFF0C\u8BF7\u59A5\u5584\u4FDD\u7BA1"));
5406
+ console.log(chalk38.yellow("\u26A0\uFE0F \u5BFC\u51FA\u6587\u4EF6\u5305\u542B API Key\uFF0C\u8BF7\u59A5\u5584\u4FDD\u7BA1"));
4760
5407
  console.log();
4761
5408
  const result = exportConfig(resolvedPath);
4762
- console.log(chalk31.green("\u2705 \u5BFC\u51FA\u6210\u529F"));
5409
+ console.log(chalk38.green("\u2705 \u5BFC\u51FA\u6210\u529F"));
4763
5410
  console.log();
4764
5411
  console.log("\u5DF2\u5BFC\u51FA\u6587\u4EF6:");
4765
5412
  for (const file of result.exportedFiles) {
4766
- console.log(` ${chalk31.cyan("\u2713")} ${file}`);
5413
+ console.log(` ${chalk38.cyan("\u2713")} ${file}`);
4767
5414
  }
4768
5415
  console.log();
4769
- console.log(chalk31.blue(`\u{1F4A1} \u5BFC\u5165\u547D\u4EE4: ccman import ${resolvedPath}
5416
+ console.log(chalk38.blue(`\u{1F4A1} \u5BFC\u5165\u547D\u4EE4: ccman import ${resolvedPath}
4770
5417
  `));
4771
5418
  } catch (error) {
4772
- console.error(chalk31.red(`
5419
+ console.error(chalk38.red(`
4773
5420
  \u274C ${error.message}
4774
5421
  `));
4775
5422
  process.exit(1);
@@ -4779,31 +5426,31 @@ function exportCommand(program2) {
4779
5426
 
4780
5427
  // src/commands/import.ts
4781
5428
  init_dist();
4782
- import chalk32 from "chalk";
4783
- import inquirer22 from "inquirer";
5429
+ import chalk39 from "chalk";
5430
+ import inquirer27 from "inquirer";
4784
5431
  import path11 from "path";
4785
5432
  function importCommand(program2) {
4786
5433
  program2.command("import <\u6E90\u76EE\u5F55>").description("\u4ECE\u672C\u5730\u76EE\u5F55\u5BFC\u5165\u914D\u7F6E\uFF08\u4F1A\u8986\u76D6\u5F53\u524D\u914D\u7F6E\uFF09").action(async (sourceDir) => {
4787
5434
  try {
4788
5435
  const resolvedPath = sourceDir.startsWith("~") ? path11.join(process.env.HOME || "", sourceDir.slice(1)) : path11.resolve(sourceDir);
4789
- console.log(chalk32.bold("\n\u{1F4E5} \u5BFC\u5165\u914D\u7F6E\n"));
5436
+ console.log(chalk39.bold("\n\u{1F4E5} \u5BFC\u5165\u914D\u7F6E\n"));
4790
5437
  const validation = validateImportDir(resolvedPath);
4791
5438
  if (!validation.valid) {
4792
- console.log(chalk32.red(`\u274C ${validation.message}
5439
+ console.log(chalk39.red(`\u274C ${validation.message}
4793
5440
  `));
4794
5441
  process.exit(1);
4795
5442
  }
4796
- console.log(chalk32.yellow("\u26A0\uFE0F \u8B66\u544A\uFF1A\u5BFC\u5165\u5C06\u8986\u76D6\u5F53\u524D\u914D\u7F6E\n"));
4797
- console.log(`\u6E90\u76EE\u5F55: ${chalk32.cyan(resolvedPath)}`);
5443
+ console.log(chalk39.yellow("\u26A0\uFE0F \u8B66\u544A\uFF1A\u5BFC\u5165\u5C06\u8986\u76D6\u5F53\u524D\u914D\u7F6E\n"));
5444
+ console.log(`\u6E90\u76EE\u5F55: ${chalk39.cyan(resolvedPath)}`);
4798
5445
  console.log();
4799
5446
  console.log("\u627E\u5230\u914D\u7F6E\u6587\u4EF6:");
4800
5447
  for (const file of validation.foundFiles) {
4801
- console.log(` ${chalk32.cyan("\u2713")} ${file}`);
5448
+ console.log(` ${chalk39.cyan("\u2713")} ${file}`);
4802
5449
  }
4803
5450
  console.log();
4804
- console.log(chalk32.gray("\u5F53\u524D\u914D\u7F6E\u5C06\u88AB\u8986\u76D6\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09"));
5451
+ console.log(chalk39.gray("\u5F53\u524D\u914D\u7F6E\u5C06\u88AB\u8986\u76D6\uFF08\u81EA\u52A8\u5907\u4EFD\uFF09"));
4805
5452
  console.log();
4806
- const { confirmFirst } = await inquirer22.prompt([
5453
+ const { confirmFirst } = await inquirer27.prompt([
4807
5454
  {
4808
5455
  type: "confirm",
4809
5456
  name: "confirmFirst",
@@ -4812,13 +5459,13 @@ function importCommand(program2) {
4812
5459
  }
4813
5460
  ]);
4814
5461
  if (!confirmFirst) {
4815
- console.log(chalk32.gray("\n\u274C \u5DF2\u53D6\u6D88\n"));
5462
+ console.log(chalk39.gray("\n\u274C \u5DF2\u53D6\u6D88\n"));
4816
5463
  return;
4817
5464
  }
4818
5465
  console.log();
4819
- console.log(chalk32.red.bold("\u26A0\uFE0F \u6700\u540E\u786E\u8BA4\uFF1A\u6B64\u64CD\u4F5C\u5C06\u8986\u76D6\u6240\u6709\u5F53\u524D\u914D\u7F6E\uFF01"));
5466
+ console.log(chalk39.red.bold("\u26A0\uFE0F \u6700\u540E\u786E\u8BA4\uFF1A\u6B64\u64CD\u4F5C\u5C06\u8986\u76D6\u6240\u6709\u5F53\u524D\u914D\u7F6E\uFF01"));
4820
5467
  console.log();
4821
- const { confirmSecond } = await inquirer22.prompt([
5468
+ const { confirmSecond } = await inquirer27.prompt([
4822
5469
  {
4823
5470
  type: "confirm",
4824
5471
  name: "confirmSecond",
@@ -4827,31 +5474,31 @@ function importCommand(program2) {
4827
5474
  }
4828
5475
  ]);
4829
5476
  if (!confirmSecond) {
4830
- console.log(chalk32.gray("\n\u274C \u5DF2\u53D6\u6D88\n"));
5477
+ console.log(chalk39.gray("\n\u274C \u5DF2\u53D6\u6D88\n"));
4831
5478
  return;
4832
5479
  }
4833
5480
  console.log();
4834
- console.log(chalk32.gray("\u{1F4BE} \u5907\u4EFD\u5F53\u524D\u914D\u7F6E..."));
4835
- console.log(chalk32.gray("\u{1F4E5} \u5BFC\u5165\u65B0\u914D\u7F6E..."));
5481
+ console.log(chalk39.gray("\u{1F4BE} \u5907\u4EFD\u5F53\u524D\u914D\u7F6E..."));
5482
+ console.log(chalk39.gray("\u{1F4E5} \u5BFC\u5165\u65B0\u914D\u7F6E..."));
4836
5483
  const result = importConfig(resolvedPath);
4837
5484
  console.log();
4838
- console.log(chalk32.green("\u2705 \u5BFC\u5165\u6210\u529F"));
5485
+ console.log(chalk39.green("\u2705 \u5BFC\u5165\u6210\u529F"));
4839
5486
  console.log();
4840
5487
  if (result.backupPaths.length > 0) {
4841
5488
  console.log("\u5907\u4EFD\u6587\u4EF6:");
4842
5489
  for (const backupPath of result.backupPaths) {
4843
- console.log(` ${chalk32.gray(backupPath)}`);
5490
+ console.log(` ${chalk39.gray(backupPath)}`);
4844
5491
  }
4845
5492
  console.log();
4846
5493
  }
4847
5494
  console.log("\u5DF2\u5BFC\u5165\u6587\u4EF6:");
4848
5495
  for (const file of result.importedFiles) {
4849
- console.log(` ${chalk32.cyan("\u2713")} ${file}`);
5496
+ console.log(` ${chalk39.cyan("\u2713")} ${file}`);
4850
5497
  }
4851
5498
  console.log();
4852
- console.log(chalk32.blue("\u{1F4A1} \u8BF7\u4F7F\u7528 'ccman cx use' \u6216 'ccman cc use' \u5207\u6362\u670D\u52A1\u5546\n"));
5499
+ console.log(chalk39.blue("\u{1F4A1} \u8BF7\u4F7F\u7528 'ccman cx use' \u6216 'ccman cc use' \u5207\u6362\u670D\u52A1\u5546\n"));
4853
5500
  } catch (error) {
4854
- console.error(chalk32.red(`
5501
+ console.error(chalk39.red(`
4855
5502
  \u274C ${error.message}
4856
5503
  `));
4857
5504
  process.exit(1);
@@ -4862,10 +5509,10 @@ function importCommand(program2) {
4862
5509
  // src/index.ts
4863
5510
  init_dist();
4864
5511
  if (process.env.NODE_ENV === "development") {
4865
- console.log(chalk33.gray("\n[\u5F00\u53D1\u6A21\u5F0F] \u914D\u7F6E\u76EE\u5F55:"));
4866
- console.log(chalk33.gray(` ccman: ${getCcmanDir()}`));
4867
- console.log(chalk33.gray(` codex: ${getCodexDir()}`));
4868
- console.log(chalk33.gray(` claude: ${getClaudeDir()}`));
5512
+ console.log(chalk40.gray("\n[\u5F00\u53D1\u6A21\u5F0F] \u914D\u7F6E\u76EE\u5F55:"));
5513
+ console.log(chalk40.gray(` ccman: ${getCcmanDir()}`));
5514
+ console.log(chalk40.gray(` codex: ${getCodexDir()}`));
5515
+ console.log(chalk40.gray(` claude: ${getClaudeDir()}`));
4869
5516
  console.log();
4870
5517
  }
4871
5518
  var program = new Command3();
@@ -4877,21 +5524,21 @@ program.name("ccman").description("Codex/Claude Code API \u670D\u52A1\u5546\u914
4877
5524
  });
4878
5525
  program.on("command:*", (operands) => {
4879
5526
  const unknownCommand = operands[0];
4880
- console.error(chalk33.red(`
5527
+ console.error(chalk40.red(`
4881
5528
  \u274C \u672A\u77E5\u547D\u4EE4: ${unknownCommand}
4882
5529
  `));
4883
- const availableCommands = ["cx", "cc", "mcp", "sync", "export", "import"];
5530
+ const availableCommands = ["cx", "cc", "gm", "mcp", "sync", "export", "import"];
4884
5531
  const suggestions = availableCommands.filter(
4885
5532
  (cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd)
4886
5533
  );
4887
5534
  if (suggestions.length > 0) {
4888
- console.log(chalk33.yellow("\u{1F4A1} \u4F60\u662F\u4E0D\u662F\u60F3\u8F93\u5165:"));
5535
+ console.log(chalk40.yellow("\u{1F4A1} \u4F60\u662F\u4E0D\u662F\u60F3\u8F93\u5165:"));
4889
5536
  suggestions.forEach((cmd) => {
4890
- console.log(chalk33.cyan(` ccman ${cmd}`));
5537
+ console.log(chalk40.cyan(` ccman ${cmd}`));
4891
5538
  });
4892
5539
  console.log();
4893
5540
  }
4894
- console.log(chalk33.gray("\u67E5\u770B\u6240\u6709\u53EF\u7528\u547D\u4EE4: ") + chalk33.cyan("ccman --help"));
5541
+ console.log(chalk40.gray("\u67E5\u770B\u6240\u6709\u53EF\u7528\u547D\u4EE4: ") + chalk40.cyan("ccman --help"));
4895
5542
  console.log();
4896
5543
  process.exit(1);
4897
5544
  });
@@ -4907,6 +5554,12 @@ cc.action(async () => {
4907
5554
  printLogo();
4908
5555
  await startClaudeMenu();
4909
5556
  });
5557
+ var gm = program.command("gm").description("\u7BA1\u7406 Gemini CLI \u670D\u52A1\u5546");
5558
+ createGeminiCommands(gm);
5559
+ gm.action(async () => {
5560
+ printLogo();
5561
+ await startGeminiMenu();
5562
+ });
4910
5563
  var mcp = program.command("mcp").description("\u7BA1\u7406 MCP \u670D\u52A1\u5668");
4911
5564
  createMCPCommands(mcp);
4912
5565
  mcp.action(() => {