zcf 2.7.0 → 2.8.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.
@@ -6,9 +6,12 @@ import dayjs from 'dayjs';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { exec } from 'tinyexec';
8
8
  import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
9
+ import { exec as exec$1 } from 'node:child_process';
10
+ import { promisify } from 'node:util';
9
11
  import { homedir, platform } from 'node:os';
12
+ import { join as join$1 } from 'node:path';
10
13
 
11
- const version = "2.7.0";
14
+ const version = "2.8.0";
12
15
  const homepage = "https://github.com/UfoMiao/zcf";
13
16
 
14
17
  const common$1 = {
@@ -23,7 +26,7 @@ const common$1 = {
23
26
  none: "\u65E0",
24
27
  notConfigured: "\u672A\u914D\u7F6E",
25
28
  spaceToSelectReturn: "- \u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u63D0\u4EA4",
26
- enterChoice: "\u8BF7\u8F93\u5165\u9009\u9879\uFF0C\u56DE\u8F66\u786E\u8BA4",
29
+ enterChoice: "\u8BF7\u8F93\u5165\u9009\u9879\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF08\u4E0D\u533A\u5206\u5927\u5C0F\u5199\uFF09",
27
30
  invalidChoice: "\u65E0\u6548\u9009\u62E9\u3002\u8BF7\u8F93\u5165\u6709\u6548\u9009\u9879\u3002",
28
31
  goodbye: "\u{1F44B} \u611F\u8C22\u4F7F\u7528 ZCF\uFF01\u518D\u89C1\uFF01",
29
32
  returnToMenu: "\u8FD4\u56DE\u4E3B\u83DC\u5355\uFF1F",
@@ -52,6 +55,7 @@ const installation$1 = {
52
55
  installPrompt: "\u68C0\u6D4B\u5230 Claude Code \u672A\u5B89\u88C5\uFF0C\u662F\u5426\u81EA\u52A8\u5B89\u88C5\uFF1F",
53
56
  installing: "\u6B63\u5728\u5B89\u88C5 Claude Code...",
54
57
  installSuccess: "Claude Code \u5B89\u88C5\u6210\u529F",
58
+ alreadyInstalled: "Claude Code \u5DF2\u5B89\u88C5",
55
59
  installFailed: "Claude Code \u5B89\u88C5\u5931\u8D25",
56
60
  npmNotFound: "npm \u672A\u5B89\u88C5\u3002\u8BF7\u5148\u5B89\u88C5 Node.js \u548C npm\u3002",
57
61
  // Termux specific
@@ -70,6 +74,8 @@ const api$1 = {
70
74
  authTokenDesc: "\u9002\u7528\u4E8E\u901A\u8FC7 OAuth \u6216\u6D4F\u89C8\u5668\u767B\u5F55\u83B7\u53D6\u7684\u4EE4\u724C",
71
75
  useApiKey: "\u4F7F\u7528 API Key (\u5BC6\u94A5\u8BA4\u8BC1)",
72
76
  apiKeyDesc: "\u9002\u7528\u4E8E\u4ECE Anthropic Console \u83B7\u53D6\u7684 API \u5BC6\u94A5",
77
+ useCcrProxy: "\u4F7F\u7528 CCR \u4EE3\u7406",
78
+ ccrProxyDesc: "\u901A\u8FC7 Claude Code Router \u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
73
79
  skipApi: "\u8DF3\u8FC7\uFF08\u7A0D\u540E\u624B\u52A8\u914D\u7F6E\uFF09",
74
80
  enterApiUrl: "\u8BF7\u8F93\u5165 API URL",
75
81
  enterAuthToken: "\u8BF7\u8F93\u5165 Auth Token",
@@ -147,7 +153,17 @@ const configuration$1 = {
147
153
  openSettingsJsonDesc: "\u9AD8\u7EA7\u7528\u6237\u81EA\u5B9A\u4E49",
148
154
  envImportSuccess: "\u73AF\u5883\u53D8\u91CF\u5DF2\u5BFC\u5165",
149
155
  permissionsImportSuccess: "\u6743\u9650\u914D\u7F6E\u5DF2\u5BFC\u5165",
150
- openingSettingsJson: "\u6B63\u5728\u6253\u5F00 settings.json..."
156
+ openingSettingsJson: "\u6B63\u5728\u6253\u5F00 settings.json...",
157
+ // JSON config related
158
+ invalidConfiguration: "\u914D\u7F6E\u65E0\u6548",
159
+ failedToParseJson: "\u89E3\u6790 JSON \u6587\u4EF6\u5931\u8D25:",
160
+ failedToBackupConfig: "\u5907\u4EFD\u914D\u7F6E\u5931\u8D25",
161
+ failedToReadTemplateSettings: "\u8BFB\u53D6\u6A21\u677F\u8BBE\u7F6E\u5931\u8D25",
162
+ failedToMergeSettings: "\u5408\u5E76\u8BBE\u7F6E\u5931\u8D25",
163
+ preservingExistingSettings: "\u4FDD\u7559\u73B0\u6709\u8BBE\u7F6E",
164
+ memoryDirNotFound: "\u672A\u627E\u5230\u8BB0\u5FC6\u76EE\u5F55",
165
+ failedToSetOnboarding: "\u8BBE\u7F6E\u5F15\u5BFC\u6807\u5FD7\u5931\u8D25",
166
+ fixWindowsMcp: "\u4FEE\u590D Windows MCP \u914D\u7F6E\uFF1F"
151
167
  };
152
168
 
153
169
  const mcp$1 = {
@@ -174,11 +190,14 @@ const menu$1 = {
174
190
  menuOptions: {
175
191
  fullInit: "\u5B8C\u6574\u521D\u59CB\u5316",
176
192
  importWorkflow: "\u5BFC\u5165\u5DE5\u4F5C\u6D41",
193
+ configureApiOrCcr: "\u914D\u7F6E API \u6216 CCR \u4EE3\u7406",
177
194
  configureApi: "\u914D\u7F6E API",
178
195
  configureMcp: "\u914D\u7F6E MCP",
179
196
  configureModel: "\u914D\u7F6E\u9ED8\u8BA4\u6A21\u578B",
180
197
  configureAiMemory: "\u914D\u7F6E Claude \u5168\u5C40\u8BB0\u5FC6",
181
198
  configureEnvPermission: "\u5BFC\u5165\u63A8\u8350\u73AF\u5883\u53D8\u91CF\u548C\u6743\u9650\u914D\u7F6E",
199
+ configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
200
+ ccrManagement: "CCR",
182
201
  ccusage: "ccusage",
183
202
  installBmad: "\u5B89\u88C5 BMad Method",
184
203
  clearCache: "\u6E05\u9664\u504F\u597D\u7F13\u5B58",
@@ -186,13 +205,16 @@ const menu$1 = {
186
205
  exit: "\u9000\u51FA"
187
206
  },
188
207
  menuDescriptions: {
189
- fullInit: "\u5B89\u88C5 Claude Code + \u5BFC\u5165\u5DE5\u4F5C\u6D41 + \u914D\u7F6E API + \u914D\u7F6E MCP \u670D\u52A1",
208
+ fullInit: "\u5B89\u88C5 Claude Code + \u5BFC\u5165\u5DE5\u4F5C\u6D41 + \u914D\u7F6E API \u6216 CCR \u4EE3\u7406 + \u914D\u7F6E MCP \u670D\u52A1",
190
209
  importWorkflow: "\u4EC5\u5BFC\u5165/\u66F4\u65B0\u5DE5\u4F5C\u6D41\u76F8\u5173\u6587\u4EF6",
210
+ configureApiOrCcr: "\u914D\u7F6E API URL\u3001\u8BA4\u8BC1\u4FE1\u606F\u6216 CCR \u4EE3\u7406",
191
211
  configureApi: "\u914D\u7F6E API URL \u548C\u8BA4\u8BC1\u4FE1\u606F",
192
212
  configureMcp: "\u914D\u7F6E MCP \u670D\u52A1\uFF08\u542B Windows \u4FEE\u590D\uFF09",
193
213
  configureModel: "\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u578B\uFF08opus/sonnet\uFF09",
194
214
  configureAiMemory: "\u914D\u7F6E AI \u8F93\u51FA\u8BED\u8A00\u548C\u89D2\u8272\u98CE\u683C",
195
215
  configureEnvPermission: "\u5BFC\u5165\u9690\u79C1\u4FDD\u62A4\u73AF\u5883\u53D8\u91CF\u548C\u7CFB\u7EDF\u6743\u9650\u914D\u7F6E",
216
+ configureCcr: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
217
+ ccrManagement: "\u914D\u7F6E Claude Code Router \u4EE5\u4F7F\u7528\u591A\u4E2A AI \u6A21\u578B",
196
218
  ccusage: "Claude Code \u7528\u91CF\u5206\u6790",
197
219
  installBmad: "AI \u9A71\u52A8\u7684\u5F00\u53D1\u65B9\u6CD5\u8BBA\u6846\u67B6",
198
220
  clearCache: "\u6E05\u9664\u504F\u597D\u8BED\u8A00\u7B49\u7F13\u5B58",
@@ -326,19 +348,102 @@ const tools$1 = {
326
348
  errorDetails: "\u9519\u8BEF\u8BE6\u60C5:"
327
349
  };
328
350
 
351
+ const ccrMessages$1 = {
352
+ // Installation
353
+ installingCcr: "\u6B63\u5728\u5B89\u88C5 Claude Code Router...",
354
+ ccrInstallSuccess: "Claude Code Router \u5B89\u88C5\u6210\u529F",
355
+ ccrInstallFailed: "\u5B89\u88C5 Claude Code Router \u5931\u8D25",
356
+ ccrAlreadyInstalled: "Claude Code Router \u5DF2\u5B89\u88C5",
357
+ // Configuration
358
+ configureCcr: "\u914D\u7F6E\u6A21\u578B\u4EE3\u7406 (CCR)",
359
+ useCcrProxy: "\u4F7F\u7528 CCR \u4EE3\u7406",
360
+ ccrProxyDesc: "\u901A\u8FC7 Claude Code Router \u8FDE\u63A5\u591A\u4E2A AI \u6A21\u578B",
361
+ fetchingPresets: "\u6B63\u5728\u83B7\u53D6\u63D0\u4F9B\u5546\u9884\u8BBE...",
362
+ noPresetsAvailable: "\u6CA1\u6709\u53EF\u7528\u7684\u9884\u8BBE",
363
+ selectCcrPreset: "\u9009\u62E9\u4E00\u4E2A\u63D0\u4F9B\u5546\u9884\u8BBE\uFF1A",
364
+ keyRequired: "API \u5BC6\u94A5\u4E0D\u80FD\u4E3A\u7A7A",
365
+ // Existing config
366
+ existingCcrConfig: "\u53D1\u73B0\u73B0\u6709\u7684 CCR \u914D\u7F6E",
367
+ overwriteCcrConfig: "\u662F\u5426\u5907\u4EFD\u73B0\u6709\u7684 CCR \u914D\u7F6E\u5E76\u91CD\u65B0\u914D\u7F6E\uFF1F",
368
+ keepingExistingConfig: "\u4FDD\u7559\u73B0\u6709\u914D\u7F6E",
369
+ backupCcrConfig: "\u6B63\u5728\u5907\u4EFD\u73B0\u6709\u7684 CCR \u914D\u7F6E...",
370
+ ccrBackupSuccess: "CCR \u914D\u7F6E\u5DF2\u5907\u4EFD\u5230\uFF1A{path}",
371
+ ccrBackupFailed: "\u5907\u4EFD CCR \u914D\u7F6E\u5931\u8D25",
372
+ // Model selection
373
+ selectDefaultModelForProvider: "\u9009\u62E9 {provider} \u7684\u9ED8\u8BA4\u6A21\u578B\uFF1A",
374
+ enterApiKeyForProvider: "\u8BF7\u8F93\u5165 {provider} \u7684 API \u5BC6\u94A5\uFF1A",
375
+ // Skip option
376
+ skipOption: "\u8DF3\u8FC7\uFF0C\u5728 CCR \u4E2D\u81EA\u884C\u914D\u7F6E",
377
+ skipConfiguring: "\u8DF3\u8FC7\u9884\u8BBE\u914D\u7F6E\uFF0C\u5C06\u521B\u5EFA\u7A7A\u914D\u7F6E\u6846\u67B6",
378
+ // Success/Error messages
379
+ ccrConfigSuccess: "CCR \u914D\u7F6E\u5DF2\u4FDD\u5B58",
380
+ proxyConfigSuccess: "\u4EE3\u7406\u8BBE\u7F6E\u5DF2\u914D\u7F6E",
381
+ ccrConfigFailed: "\u914D\u7F6E CCR \u5931\u8D25",
382
+ ccrSetupComplete: "CCR \u8BBE\u7F6E\u5B8C\u6210",
383
+ fetchPresetsError: "\u83B7\u53D6\u63D0\u4F9B\u5546\u9884\u8BBE\u5931\u8D25",
384
+ failedToStartCcrService: "\u542F\u52A8 CCR \u670D\u52A1\u5931\u8D25",
385
+ errorStartingCcrService: "\u542F\u52A8 CCR \u670D\u52A1\u65F6\u51FA\u9519",
386
+ // CCR service status
387
+ ccrRestartSuccess: "CCR \u670D\u52A1\u5DF2\u91CD\u542F",
388
+ ccrRestartFailed: "CCR \u670D\u52A1\u91CD\u542F\u5931\u8D25",
389
+ // Configuration tips
390
+ configTips: "\u914D\u7F6E\u63D0\u793A",
391
+ useClaudeCommand: "\u8BF7\u4F7F\u7528 claude \u547D\u4EE4\u542F\u52A8 Claude Code\uFF08\u800C\u975E ccr code\uFF09",
392
+ advancedConfigTip: "\u60A8\u53EF\u4EE5\u4F7F\u7528 ccr ui \u547D\u4EE4\u8FDB\u884C\u66F4\u9AD8\u7EA7\u7684\u914D\u7F6E",
393
+ manualConfigTip: "\u624B\u52A8\u4FEE\u6539\u914D\u7F6E\u6587\u4EF6\u540E\uFF0C\u8BF7\u6267\u884C ccr restart \u4F7F\u914D\u7F6E\u751F\u6548",
394
+ // CCR Menu
395
+ ccrMenuTitle: "CCR - Claude Code Router \u7BA1\u7406",
396
+ ccrMenuOptions: {
397
+ initCcr: "\u521D\u59CB\u5316 CCR",
398
+ startUi: "\u542F\u52A8 CCR UI",
399
+ checkStatus: "\u67E5\u8BE2 CCR \u72B6\u6001",
400
+ restart: "\u91CD\u542F CCR",
401
+ start: "\u542F\u52A8 CCR",
402
+ stop: "\u505C\u6B62 CCR",
403
+ back: "\u8FD4\u56DE\u4E3B\u83DC\u5355"
404
+ },
405
+ ccrMenuDescriptions: {
406
+ initCcr: "\u5B89\u88C5\u5E76\u914D\u7F6E CCR",
407
+ startUi: "\u6253\u5F00 Web \u754C\u9762\u7BA1\u7406 CCR",
408
+ checkStatus: "\u67E5\u770B CCR \u670D\u52A1\u8FD0\u884C\u72B6\u6001",
409
+ restart: "\u91CD\u542F CCR \u670D\u52A1",
410
+ start: "\u542F\u52A8 CCR \u670D\u52A1",
411
+ stop: "\u505C\u6B62 CCR \u670D\u52A1"
412
+ },
413
+ // Command execution messages
414
+ startingCcrUi: "\u6B63\u5728\u542F\u52A8 CCR UI...",
415
+ ccrUiStarted: "CCR UI \u5DF2\u542F\u52A8",
416
+ checkingCcrStatus: "\u6B63\u5728\u67E5\u8BE2 CCR \u72B6\u6001...",
417
+ ccrStatusTitle: "CCR \u72B6\u6001\u4FE1\u606F\uFF1A",
418
+ restartingCcr: "\u6B63\u5728\u91CD\u542F CCR...",
419
+ ccrRestarted: "CCR \u5DF2\u91CD\u542F",
420
+ startingCcr: "\u6B63\u5728\u542F\u52A8 CCR...",
421
+ ccrStarted: "CCR \u5DF2\u542F\u52A8",
422
+ stoppingCcr: "\u6B63\u5728\u505C\u6B62 CCR...",
423
+ ccrStopped: "CCR \u5DF2\u505C\u6B62",
424
+ ccrCommandFailed: "\u6267\u884C CCR \u547D\u4EE4\u5931\u8D25",
425
+ // Configuration check messages
426
+ ccrNotConfigured: "CCR \u5C1A\u672A\u914D\u7F6E\u3002\u8BF7\u5148\u521D\u59CB\u5316 CCR\u3002",
427
+ pleaseInitFirst: "\u8BF7\u9009\u62E9\u9009\u9879 1 \u6765\u521D\u59CB\u5316 CCR\u3002",
428
+ // UI API Key messages
429
+ ccrUiApiKey: "CCR UI \u767B\u5F55\u5BC6\u94A5",
430
+ ccrUiApiKeyHint: "\u4F7F\u7528\u6B64\u5BC6\u94A5\u767B\u5F55 CCR UI \u754C\u9762"
431
+ };
432
+
329
433
  const zhCN = {
330
- ...common$1,
331
- ...language$1,
332
- ...installation$1,
333
- ...api$1,
334
- ...configuration$1,
335
- ...mcp$1,
336
- ...menu$1,
337
- ...workflow$1,
338
- ...cli$1,
339
- ...bmad$1,
340
- ...errors$1,
341
- ...tools$1
434
+ common: common$1,
435
+ language: language$1,
436
+ installation: installation$1,
437
+ api: api$1,
438
+ configuration: configuration$1,
439
+ mcp: mcp$1,
440
+ menu: menu$1,
441
+ workflow: workflow$1,
442
+ cli: cli$1,
443
+ bmad: bmad$1,
444
+ errors: errors$1,
445
+ tools: tools$1,
446
+ ccr: ccrMessages$1
342
447
  };
343
448
 
344
449
  const common = {
@@ -353,7 +458,7 @@ const common = {
353
458
  none: "None",
354
459
  notConfigured: "Not configured",
355
460
  spaceToSelectReturn: "- Space to select. Return to submit",
356
- enterChoice: "Enter your choice, and press enter",
461
+ enterChoice: "Enter your choice and press enter (case-insensitive)",
357
462
  invalidChoice: "Invalid choice. Please enter a valid option.",
358
463
  goodbye: "\u{1F44B} Thanks for using ZCF! Goodbye!",
359
464
  returnToMenu: "Return to main menu?",
@@ -382,6 +487,7 @@ const installation = {
382
487
  installPrompt: "Claude Code not found. Install automatically?",
383
488
  installing: "Installing Claude Code...",
384
489
  installSuccess: "Claude Code installed successfully",
490
+ alreadyInstalled: "Claude Code is already installed",
385
491
  installFailed: "Failed to install Claude Code",
386
492
  npmNotFound: "npm is not installed. Please install Node.js and npm first.",
387
493
  // Termux specific
@@ -400,6 +506,8 @@ const api = {
400
506
  authTokenDesc: "For tokens obtained via OAuth or browser login",
401
507
  useApiKey: "Use API Key (Key authentication)",
402
508
  apiKeyDesc: "For API keys from Anthropic Console",
509
+ useCcrProxy: "Use CCR Proxy",
510
+ ccrProxyDesc: "Use multiple AI models via Claude Code Router",
403
511
  skipApi: "Skip (configure manually later)",
404
512
  enterApiUrl: "Enter API URL",
405
513
  enterAuthToken: "Enter Auth Token",
@@ -477,7 +585,17 @@ const configuration = {
477
585
  openSettingsJsonDesc: "Advanced user customization",
478
586
  envImportSuccess: "Environment variables imported",
479
587
  permissionsImportSuccess: "Permissions imported",
480
- openingSettingsJson: "Opening settings.json..."
588
+ openingSettingsJson: "Opening settings.json...",
589
+ // JSON config related
590
+ invalidConfiguration: "Invalid configuration",
591
+ failedToParseJson: "Failed to parse JSON file:",
592
+ failedToBackupConfig: "Failed to backup config",
593
+ failedToReadTemplateSettings: "Failed to read template settings",
594
+ failedToMergeSettings: "Failed to merge settings",
595
+ preservingExistingSettings: "Preserving existing settings",
596
+ memoryDirNotFound: "Memory directory not found",
597
+ failedToSetOnboarding: "Failed to set onboarding flag",
598
+ fixWindowsMcp: "Fix Windows MCP configuration?"
481
599
  };
482
600
 
483
601
  const mcp = {
@@ -504,11 +622,14 @@ const menu = {
504
622
  menuOptions: {
505
623
  fullInit: "Full initialization",
506
624
  importWorkflow: "Import workflow",
625
+ configureApiOrCcr: "Configure API / CCR proxy",
507
626
  configureApi: "Configure API",
508
627
  configureMcp: "Configure MCP",
509
628
  configureModel: "Configure default model",
510
629
  configureAiMemory: "Configure Claude global memory",
511
630
  configureEnvPermission: "Import recommended environment variables and permissions",
631
+ configureCcr: "Configure Model Proxy (CCR)",
632
+ ccrManagement: "CCR",
512
633
  ccusage: "ccusage",
513
634
  installBmad: "Install BMad Method",
514
635
  clearCache: "Clear preference cache",
@@ -516,13 +637,16 @@ const menu = {
516
637
  exit: "Exit"
517
638
  },
518
639
  menuDescriptions: {
519
- fullInit: "Install Claude Code + Import workflow + Configure API + Configure MCP services",
640
+ fullInit: "Install Claude Code + Import workflow + Configure API or CCR proxy + Configure MCP services",
520
641
  importWorkflow: "Import/update workflow-related files only",
642
+ configureApiOrCcr: "Configure API URL, authentication or CCR proxy",
521
643
  configureApi: "Configure API URL and authentication",
522
644
  configureMcp: "Configure MCP services (includes Windows fix)",
523
645
  configureModel: "Set default model (opus/sonnet)",
524
646
  configureAiMemory: "Configure AI output language and personality",
525
647
  configureEnvPermission: "Import privacy protection environment variables and system permissions",
648
+ configureCcr: "Configure Claude Code Router to use multiple AI models",
649
+ ccrManagement: "Configure Claude Code Router to use multiple AI models",
526
650
  ccusage: "Claude Code usage analysis",
527
651
  installBmad: "AI-driven development methodology framework",
528
652
  clearCache: "Clear preference language and other caches",
@@ -656,25 +780,111 @@ const tools = {
656
780
  errorDetails: "Error details:"
657
781
  };
658
782
 
783
+ const ccrMessages = {
784
+ // Installation
785
+ installingCcr: "Installing Claude Code Router...",
786
+ ccrInstallSuccess: "Claude Code Router installed successfully",
787
+ ccrInstallFailed: "Failed to install Claude Code Router",
788
+ ccrAlreadyInstalled: "Claude Code Router is already installed",
789
+ // Configuration
790
+ configureCcr: "Configure Model Proxy (CCR)",
791
+ useCcrProxy: "Use CCR Proxy",
792
+ ccrProxyDesc: "Connect to multiple AI models via Claude Code Router",
793
+ fetchingPresets: "Fetching provider presets...",
794
+ noPresetsAvailable: "No presets available",
795
+ selectCcrPreset: "Select a provider preset:",
796
+ keyRequired: "API key is required",
797
+ // Existing config
798
+ existingCcrConfig: "Existing CCR configuration found",
799
+ overwriteCcrConfig: "Backup existing CCR configuration and reconfigure?",
800
+ keepingExistingConfig: "Keeping existing configuration",
801
+ backupCcrConfig: "Backing up existing CCR configuration...",
802
+ ccrBackupSuccess: "CCR configuration backed up to: {path}",
803
+ ccrBackupFailed: "Failed to backup CCR configuration",
804
+ // Model selection
805
+ selectDefaultModelForProvider: "Select default model for {provider}:",
806
+ enterApiKeyForProvider: "Enter API key for {provider}:",
807
+ // Skip option
808
+ skipOption: "Skip, configure in CCR manually",
809
+ skipConfiguring: "Skipping preset configuration, will create empty configuration framework",
810
+ // Success/Error messages
811
+ ccrConfigSuccess: "CCR configuration saved",
812
+ proxyConfigSuccess: "Proxy settings configured",
813
+ ccrConfigFailed: "Failed to configure CCR",
814
+ ccrSetupComplete: "CCR setup complete",
815
+ fetchPresetsError: "Failed to fetch provider presets",
816
+ failedToStartCcrService: "Failed to start CCR service",
817
+ errorStartingCcrService: "Error starting CCR service",
818
+ // CCR service status
819
+ ccrRestartSuccess: "CCR service restarted",
820
+ ccrRestartFailed: "Failed to restart CCR service",
821
+ // Configuration tips
822
+ configTips: "Configuration Tips",
823
+ useClaudeCommand: "Use the claude command to start Claude Code (not ccr code)",
824
+ advancedConfigTip: "You can use the ccr ui command for advanced configuration",
825
+ manualConfigTip: "After manually modifying the configuration file, run ccr restart to apply changes",
826
+ // CCR Menu
827
+ ccrMenuTitle: "CCR - Claude Code Router Management",
828
+ ccrMenuOptions: {
829
+ initCcr: "Initialize CCR",
830
+ startUi: "Start CCR UI",
831
+ checkStatus: "Check CCR Status",
832
+ restart: "Restart CCR",
833
+ start: "Start CCR",
834
+ stop: "Stop CCR",
835
+ back: "Back to Main Menu"
836
+ },
837
+ ccrMenuDescriptions: {
838
+ initCcr: "Install and configure CCR",
839
+ startUi: "Open web interface to manage CCR",
840
+ checkStatus: "View CCR service status",
841
+ restart: "Restart CCR service",
842
+ start: "Start CCR service",
843
+ stop: "Stop CCR service"
844
+ },
845
+ // Command execution messages
846
+ startingCcrUi: "Starting CCR UI...",
847
+ ccrUiStarted: "CCR UI started",
848
+ checkingCcrStatus: "Checking CCR status...",
849
+ ccrStatusTitle: "CCR Status:",
850
+ restartingCcr: "Restarting CCR...",
851
+ ccrRestarted: "CCR restarted",
852
+ startingCcr: "Starting CCR...",
853
+ ccrStarted: "CCR started",
854
+ stoppingCcr: "Stopping CCR...",
855
+ ccrStopped: "CCR stopped",
856
+ ccrCommandFailed: "Failed to execute CCR command",
857
+ // Configuration check messages
858
+ ccrNotConfigured: "CCR is not configured yet. Please initialize CCR first.",
859
+ pleaseInitFirst: "Please select option 1 to initialize CCR.",
860
+ // UI API Key messages
861
+ ccrUiApiKey: "CCR UI API Key",
862
+ ccrUiApiKeyHint: "Use this API key to login to CCR UI"
863
+ };
864
+
659
865
  const en = {
660
- ...common,
661
- ...language,
662
- ...installation,
663
- ...api,
664
- ...configuration,
665
- ...mcp,
666
- ...menu,
667
- ...workflow,
668
- ...cli,
669
- ...bmad,
670
- ...errors,
671
- ...tools
866
+ common,
867
+ language,
868
+ installation,
869
+ api,
870
+ configuration,
871
+ mcp,
872
+ menu,
873
+ workflow,
874
+ cli,
875
+ bmad,
876
+ errors,
877
+ tools,
878
+ ccr: ccrMessages
672
879
  };
673
880
 
674
- const translations = {
881
+ const I18N$1 = {
675
882
  "zh-CN": zhCN,
676
883
  en
677
884
  };
885
+ function getTranslation(lang) {
886
+ return I18N$1[lang];
887
+ }
678
888
 
679
889
  const CLAUDE_DIR = join(homedir(), ".claude");
680
890
  const SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
@@ -692,7 +902,7 @@ const AI_OUTPUT_LANGUAGES = {
692
902
  en: { label: "English", directive: "Always respond in English" },
693
903
  custom: { label: "Custom", directive: "" }
694
904
  };
695
- const I18N = translations;
905
+ const I18N = I18N$1;
696
906
  const MCP_SERVICES = [
697
907
  {
698
908
  id: "context7",
@@ -748,8 +958,8 @@ const MCP_SERVICES = [
748
958
  },
749
959
  requiresApiKey: true,
750
960
  apiKeyPrompt: {
751
- "zh-CN": "\u8BF7\u8F93\u5165 Exa API Key",
752
- en: "Enter Exa API Key"
961
+ "zh-CN": "\u8BF7\u8F93\u5165 Exa API Key\uFF08\u53EF\u4ECE https://dashboard.exa.ai/api-keys \u83B7\u53D6\uFF09",
962
+ en: "Enter Exa API Key (get from https://dashboard.exa.ai/api-keys)"
753
963
  },
754
964
  apiKeyEnvVar: "EXA_API_KEY",
755
965
  config: {
@@ -763,6 +973,21 @@ const MCP_SERVICES = [
763
973
  }
764
974
  ];
765
975
 
976
+ function addNumbersToChoices(choices, startFrom = 1, format = (n) => `${n}. `) {
977
+ let currentNumber = startFrom;
978
+ return choices.map((choice) => {
979
+ if (choice.disabled) {
980
+ return choice;
981
+ }
982
+ const numbered = {
983
+ ...choice,
984
+ name: `${format(currentNumber)}${choice.name}`
985
+ };
986
+ currentNumber++;
987
+ return numbered;
988
+ });
989
+ }
990
+
766
991
  class FileSystemError extends Error {
767
992
  constructor(message, path, cause) {
768
993
  super(message);
@@ -874,8 +1099,8 @@ function readJsonConfig(path, options = {}) {
874
1099
  const content = readFile(path);
875
1100
  const data = JSON.parse(content);
876
1101
  if (validate && !validate(data)) {
877
- const i18n = I18N[readZcfConfig()?.preferredLang || "en"];
878
- console.warn(`${i18n.invalidConfiguration} (${path})`);
1102
+ const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
1103
+ console.warn(`${i18n.configuration.invalidConfiguration} (${path})`);
879
1104
  return defaultValue;
880
1105
  }
881
1106
  if (sanitize) {
@@ -883,8 +1108,8 @@ function readJsonConfig(path, options = {}) {
883
1108
  }
884
1109
  return data;
885
1110
  } catch (error) {
886
- const i18n = I18N[readZcfConfig()?.preferredLang || "en"];
887
- console.error(`${i18n.failedToParseJson} ${path}`, error);
1111
+ const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
1112
+ console.error(`${i18n.configuration.failedToParseJson} ${path}`, error);
888
1113
  return defaultValue;
889
1114
  }
890
1115
  }
@@ -909,8 +1134,8 @@ function backupJsonConfig(path, backupDir) {
909
1134
  copyFile(path, backupPath);
910
1135
  return backupPath;
911
1136
  } catch (error) {
912
- const i18n = I18N[readZcfConfig()?.preferredLang || "en"];
913
- console.error(i18n.failedToBackupConfig, error);
1137
+ const i18n = getTranslation(readZcfConfig()?.preferredLang || "en");
1138
+ console.error(i18n.configuration.failedToBackupConfig, error);
914
1139
  return null;
915
1140
  }
916
1141
  }
@@ -990,23 +1215,23 @@ function getPersonalityInfo(personalityId) {
990
1215
  return AI_PERSONALITIES.find((p) => p.id === personalityId);
991
1216
  }
992
1217
  async function configureAiPersonality(scriptLang, showExisting = true) {
993
- const i18n = I18N[scriptLang];
1218
+ const i18n = getTranslation(scriptLang);
994
1219
  const existingPersonality = getExistingPersonality();
995
1220
  if (showExisting && existingPersonality) {
996
1221
  const personalityInfo = getPersonalityInfo(existingPersonality);
997
1222
  if (personalityInfo) {
998
- console.log("\n" + ansis.blue(`\u2139 ${i18n.existingPersonality || "Existing AI personality configuration"}`));
1223
+ console.log("\n" + ansis.blue(`\u2139 ${i18n.configuration.existingPersonality || "Existing AI personality configuration"}`));
999
1224
  console.log(
1000
- ansis.gray(` ${i18n.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
1225
+ ansis.gray(` ${i18n.configuration.currentPersonality || "Current personality"}: ${personalityInfo.name[scriptLang]}`)
1001
1226
  );
1002
1227
  const { modify } = await inquirer.prompt({
1003
1228
  type: "confirm",
1004
1229
  name: "modify",
1005
- message: i18n.modifyPersonality || "Modify AI personality?",
1230
+ message: i18n.configuration.modifyPersonality || "Modify AI personality?",
1006
1231
  default: false
1007
1232
  });
1008
1233
  if (!modify) {
1009
- console.log(ansis.green(`\u2714 ${i18n.keepPersonality || "Keeping existing personality"}`));
1234
+ console.log(ansis.green(`\u2714 ${i18n.configuration.keepPersonality || "Keeping existing personality"}`));
1010
1235
  return;
1011
1236
  }
1012
1237
  }
@@ -1014,16 +1239,16 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
1014
1239
  const { personality } = await inquirer.prompt({
1015
1240
  type: "list",
1016
1241
  name: "personality",
1017
- message: i18n.selectAiPersonality || "Select AI personality",
1018
- choices: AI_PERSONALITIES.map((p) => ({
1019
- name: p.id !== "custom" ? `${p.name[scriptLang]} - ${ansis.gray(p.directive[scriptLang].substring(0, 50) + "...")}` : `${p.name[scriptLang]} - ${ansis.gray(i18n.customPersonalityHint || "Define your own personality")}`,
1242
+ message: i18n.configuration.selectAiPersonality || "Select AI personality",
1243
+ choices: addNumbersToChoices(AI_PERSONALITIES.map((p) => ({
1244
+ name: p.id !== "custom" ? `${p.name[scriptLang]} - ${ansis.gray(p.directive[scriptLang].substring(0, 50) + "...")}` : `${p.name[scriptLang]} - ${ansis.gray(i18n.configuration.customPersonalityHint || "Define your own personality")}`,
1020
1245
  value: p.id,
1021
1246
  short: p.name[scriptLang]
1022
- })),
1247
+ }))),
1023
1248
  default: existingPersonality ? AI_PERSONALITIES.findIndex((p) => p.id === existingPersonality) : 0
1024
1249
  });
1025
1250
  if (!personality) {
1026
- console.log(ansis.yellow(i18n.cancelled));
1251
+ console.log(ansis.yellow(i18n.common.cancelled));
1027
1252
  return;
1028
1253
  }
1029
1254
  let directive = "";
@@ -1031,11 +1256,11 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
1031
1256
  const { customDirective } = await inquirer.prompt({
1032
1257
  type: "input",
1033
1258
  name: "customDirective",
1034
- message: i18n.enterCustomPersonality || "Enter custom personality directive",
1035
- validate: (value) => !!value || i18n.directiveCannotBeEmpty
1259
+ message: i18n.configuration.enterCustomPersonality || "Enter custom personality directive",
1260
+ validate: (value) => !!value || i18n.configuration.directiveCannotBeEmpty
1036
1261
  });
1037
1262
  if (!customDirective) {
1038
- console.log(ansis.yellow(i18n.cancelled));
1263
+ console.log(ansis.yellow(i18n.common.cancelled));
1039
1264
  return;
1040
1265
  }
1041
1266
  directive = customDirective;
@@ -1047,14 +1272,16 @@ async function configureAiPersonality(scriptLang, showExisting = true) {
1047
1272
  }
1048
1273
  await applyPersonalityDirective(directive);
1049
1274
  updateZcfConfig({ aiPersonality: personality });
1050
- console.log(ansis.green(`\u2714 ${i18n.personalityConfigured || "AI personality configured"}`));
1275
+ console.log(ansis.green(`\u2714 ${i18n.configuration.personalityConfigured || "AI personality configured"}`));
1051
1276
  }
1052
1277
  async function applyPersonalityDirective(directive) {
1053
1278
  try {
1054
1279
  const personalityFile = join(CLAUDE_DIR, "personality.md");
1055
1280
  writeFile(personalityFile, directive);
1056
1281
  } catch (error) {
1057
- console.error(ansis.red(I18N[readZcfConfig()?.preferredLang || "en"].failedToApplyPersonality), error);
1282
+ const lang = readZcfConfig()?.preferredLang || "en";
1283
+ const errorI18n = getTranslation(lang);
1284
+ console.error(ansis.red(errorI18n.configuration.failedToApplyPersonality || "Failed to apply personality"), error);
1058
1285
  }
1059
1286
  }
1060
1287
 
@@ -1090,9 +1317,9 @@ function handleExitPromptError(error) {
1090
1317
  if (error instanceof Error && error.name === "ExitPromptError") {
1091
1318
  const zcfConfig = readZcfConfig();
1092
1319
  const defaultLang = zcfConfig?.preferredLang || "zh-CN";
1093
- const i18n = I18N[defaultLang];
1320
+ const i18n = getTranslation(defaultLang);
1094
1321
  console.log(ansis.cyan(`
1095
- ${i18n.goodbye}
1322
+ ${i18n.common.goodbye}
1096
1323
  `));
1097
1324
  process.exit(0);
1098
1325
  }
@@ -1101,7 +1328,8 @@ ${i18n.goodbye}
1101
1328
  function handleGeneralError(error, lang) {
1102
1329
  const zcfConfig = readZcfConfig();
1103
1330
  const defaultLang = lang || zcfConfig?.preferredLang || "en";
1104
- const errorMsg = I18N[defaultLang].error;
1331
+ const i18n = getTranslation(defaultLang);
1332
+ const errorMsg = i18n.common.error || "Error";
1105
1333
  console.error(ansis.red(`${errorMsg}:`), error);
1106
1334
  if (error instanceof Error) {
1107
1335
  console.error(ansis.gray(`Stack: ${error.stack}`));
@@ -1200,6 +1428,142 @@ function mergeAndCleanPermissions(templatePermissions, userPermissions) {
1200
1428
  return cleanupPermissions(template, user);
1201
1429
  }
1202
1430
 
1431
+ function getPlatform() {
1432
+ const p = platform();
1433
+ if (p === "win32") return "windows";
1434
+ if (p === "darwin") return "macos";
1435
+ return "linux";
1436
+ }
1437
+ function isTermux() {
1438
+ return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
1439
+ }
1440
+ function getTermuxPrefix() {
1441
+ return process.env.PREFIX || "/data/data/com.termux/files/usr";
1442
+ }
1443
+ function isWindows() {
1444
+ return getPlatform() === "windows";
1445
+ }
1446
+ function getMcpCommand() {
1447
+ if (isWindows()) {
1448
+ return ["cmd", "/c", "npx"];
1449
+ }
1450
+ return ["npx"];
1451
+ }
1452
+ async function commandExists(command) {
1453
+ try {
1454
+ const cmd = getPlatform() === "windows" ? "where" : "which";
1455
+ const res = await exec(cmd, [command]);
1456
+ if (res.exitCode === 0) {
1457
+ return true;
1458
+ }
1459
+ } catch {
1460
+ }
1461
+ if (isTermux()) {
1462
+ const termuxPrefix = getTermuxPrefix();
1463
+ const possiblePaths = [
1464
+ `${termuxPrefix}/bin/${command}`,
1465
+ `${termuxPrefix}/usr/bin/${command}`,
1466
+ `/data/data/com.termux/files/usr/bin/${command}`
1467
+ ];
1468
+ for (const path of possiblePaths) {
1469
+ if (existsSync(path)) {
1470
+ return true;
1471
+ }
1472
+ }
1473
+ }
1474
+ if (getPlatform() !== "windows") {
1475
+ const commonPaths = [
1476
+ `/usr/local/bin/${command}`,
1477
+ `/usr/bin/${command}`,
1478
+ `/bin/${command}`,
1479
+ `${process.env.HOME}/.local/bin/${command}`
1480
+ ];
1481
+ for (const path of commonPaths) {
1482
+ if (existsSync(path)) {
1483
+ return true;
1484
+ }
1485
+ }
1486
+ }
1487
+ return false;
1488
+ }
1489
+
1490
+ function getMcpConfigPath() {
1491
+ return ClAUDE_CONFIG_FILE;
1492
+ }
1493
+ function readMcpConfig() {
1494
+ return readJsonConfig(ClAUDE_CONFIG_FILE);
1495
+ }
1496
+ function writeMcpConfig(config) {
1497
+ writeJsonConfig(ClAUDE_CONFIG_FILE, config);
1498
+ }
1499
+ function backupMcpConfig() {
1500
+ const backupBaseDir = join(CLAUDE_DIR, "backup");
1501
+ return backupJsonConfig(ClAUDE_CONFIG_FILE, backupBaseDir);
1502
+ }
1503
+ function mergeMcpServers(existing, newServers) {
1504
+ const config = existing || { mcpServers: {} };
1505
+ if (!config.mcpServers) {
1506
+ config.mcpServers = {};
1507
+ }
1508
+ Object.assign(config.mcpServers, newServers);
1509
+ return config;
1510
+ }
1511
+ function applyPlatformCommand(config) {
1512
+ if (config.command === "npx" && isWindows()) {
1513
+ const mcpCmd = getMcpCommand();
1514
+ config.command = mcpCmd[0];
1515
+ config.args = [...mcpCmd.slice(1), ...config.args || []];
1516
+ }
1517
+ }
1518
+ function buildMcpServerConfig(baseConfig, apiKey, placeholder = "YOUR_EXA_API_KEY", envVarName) {
1519
+ const config = deepClone(baseConfig);
1520
+ applyPlatformCommand(config);
1521
+ if (!apiKey) {
1522
+ return config;
1523
+ }
1524
+ if (envVarName && config.env) {
1525
+ config.env[envVarName] = apiKey;
1526
+ return config;
1527
+ }
1528
+ if (config.args) {
1529
+ config.args = config.args.map((arg) => arg.replace(placeholder, apiKey));
1530
+ }
1531
+ if (config.url) {
1532
+ config.url = config.url.replace(placeholder, apiKey);
1533
+ }
1534
+ return config;
1535
+ }
1536
+ function fixWindowsMcpConfig(config) {
1537
+ if (!isWindows() || !config.mcpServers) {
1538
+ return config;
1539
+ }
1540
+ const fixed = { ...config };
1541
+ for (const [, serverConfig] of Object.entries(fixed.mcpServers)) {
1542
+ if (serverConfig && typeof serverConfig === "object" && "command" in serverConfig) {
1543
+ applyPlatformCommand(serverConfig);
1544
+ }
1545
+ }
1546
+ return fixed;
1547
+ }
1548
+ function addCompletedOnboarding() {
1549
+ try {
1550
+ let config = readMcpConfig();
1551
+ if (!config) {
1552
+ config = { mcpServers: {} };
1553
+ }
1554
+ if (config.hasCompletedOnboarding === true) {
1555
+ return;
1556
+ }
1557
+ config.hasCompletedOnboarding = true;
1558
+ writeMcpConfig(config);
1559
+ } catch (error) {
1560
+ const lang = readZcfConfig()?.preferredLang || "en";
1561
+ const i18n = getTranslation(lang);
1562
+ console.error(i18n.configuration?.failedToAddOnboardingFlag || "Failed to add onboarding flag", error);
1563
+ throw error;
1564
+ }
1565
+ }
1566
+
1203
1567
  function ensureClaudeDir() {
1204
1568
  ensureDir(CLAUDE_DIR);
1205
1569
  }
@@ -1239,8 +1603,8 @@ function copyConfigFiles(lang, onlyMd = false) {
1239
1603
  function copyClaudeMemoryFiles(lang, rootDir) {
1240
1604
  const memorySourceDir = join(rootDir, "templates", lang, "memory");
1241
1605
  if (!exists(memorySourceDir)) {
1242
- const i18n = I18N[lang];
1243
- throw new Error(`${i18n.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
1606
+ const i18n = getTranslation(lang);
1607
+ throw new Error(`${i18n.configuration.memoryDirNotFound || "Memory directory not found:"} ${memorySourceDir}`);
1244
1608
  }
1245
1609
  const files = readDir(memorySourceDir);
1246
1610
  files?.forEach((file) => {
@@ -1259,7 +1623,9 @@ function getDefaultSettings() {
1259
1623
  const templateSettingsPath = join(rootDir, "templates", "settings.json");
1260
1624
  return readJsonConfig(templateSettingsPath) || {};
1261
1625
  } catch (error) {
1262
- console.error(I18N[readZcfConfig()?.preferredLang || "en"].failedToReadTemplateSettings, error);
1626
+ const lang = readZcfConfig()?.preferredLang || "en";
1627
+ const i18n = getTranslation(lang);
1628
+ console.error(i18n.configuration.failedToReadTemplateSettings || "Failed to read template settings", error);
1263
1629
  return {};
1264
1630
  }
1265
1631
  }
@@ -1284,6 +1650,13 @@ function configureApi(apiConfig) {
1284
1650
  settings.env.ANTHROPIC_BASE_URL = apiConfig.url;
1285
1651
  }
1286
1652
  writeJsonConfig(SETTINGS_FILE, settings);
1653
+ try {
1654
+ addCompletedOnboarding();
1655
+ } catch (error) {
1656
+ const lang = readZcfConfig()?.preferredLang || "en";
1657
+ const i18n = getTranslation(lang);
1658
+ console.error(i18n.configuration.failedToSetOnboarding || "Failed to set onboarding flag", error);
1659
+ }
1287
1660
  return apiConfig;
1288
1661
  }
1289
1662
  function mergeConfigs(sourceFile, targetFile) {
@@ -1306,7 +1679,9 @@ function mergeSettingsFile(templatePath, targetPath) {
1306
1679
  try {
1307
1680
  const templateSettings = readJsonConfig(templatePath);
1308
1681
  if (!templateSettings) {
1309
- console.error(I18N[readZcfConfig()?.preferredLang || "en"].failedToReadTemplateSettings);
1682
+ const lang = readZcfConfig()?.preferredLang || "en";
1683
+ const i18n = getTranslation(lang);
1684
+ console.error(i18n.configuration?.failedToReadTemplateSettings || "Failed to read template settings");
1310
1685
  return;
1311
1686
  }
1312
1687
  if (!exists(targetPath)) {
@@ -1333,9 +1708,13 @@ function mergeSettingsFile(templatePath, targetPath) {
1333
1708
  }
1334
1709
  writeJsonConfig(targetPath, mergedSettings);
1335
1710
  } catch (error) {
1336
- console.error(I18N[readZcfConfig()?.preferredLang || "en"].failedToMergeSettings, error);
1711
+ const lang = readZcfConfig()?.preferredLang || "en";
1712
+ const i18n = getTranslation(lang);
1713
+ console.error(i18n.configuration.failedToMergeSettings || "Failed to merge settings", error);
1337
1714
  if (exists(targetPath)) {
1338
- console.error(I18N[readZcfConfig()?.preferredLang || "en"].preservingExistingSettings);
1715
+ const lang2 = readZcfConfig()?.preferredLang || "en";
1716
+ const i18n2 = getTranslation(lang2);
1717
+ console.warn(i18n2.configuration.preservingExistingSettings || "Preserving existing settings");
1339
1718
  } else {
1340
1719
  copyFile(templatePath, targetPath);
1341
1720
  }
@@ -1379,11 +1758,11 @@ function applyAiLanguageDirective(aiOutputLang) {
1379
1758
  }
1380
1759
 
1381
1760
  function validateApiKey(apiKey, lang = "zh-CN") {
1382
- const i18n = I18N[lang];
1761
+ const i18n = getTranslation(lang);
1383
1762
  if (!apiKey || apiKey.trim() === "") {
1384
1763
  return {
1385
1764
  isValid: false,
1386
- error: i18n.apiKeyValidation.empty
1765
+ error: i18n.api.apiKeyValidation.empty
1387
1766
  };
1388
1767
  }
1389
1768
  return { isValid: true };
@@ -1401,22 +1780,22 @@ async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
1401
1780
  const { authType: selectedAuthType } = await inquirer.prompt({
1402
1781
  type: "list",
1403
1782
  name: "authType",
1404
- message: i18n.configureApi,
1405
- choices: [
1783
+ message: i18n.api.configureApi,
1784
+ choices: addNumbersToChoices([
1406
1785
  {
1407
- name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
1786
+ name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
1408
1787
  value: "auth_token",
1409
- short: i18n.useAuthToken
1788
+ short: i18n.api.useAuthToken
1410
1789
  },
1411
1790
  {
1412
- name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
1791
+ name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
1413
1792
  value: "api_key",
1414
- short: i18n.useApiKey
1793
+ short: i18n.api.useApiKey
1415
1794
  }
1416
- ]
1795
+ ])
1417
1796
  });
1418
1797
  if (!selectedAuthType) {
1419
- console.log(ansis.yellow(i18n.cancelled));
1798
+ console.log(ansis.yellow(i18n.common.cancelled));
1420
1799
  return null;
1421
1800
  }
1422
1801
  authType = selectedAuthType;
@@ -1424,39 +1803,39 @@ async function configureApiCompletely(i18n, scriptLang, preselectedAuthType) {
1424
1803
  const { url } = await inquirer.prompt({
1425
1804
  type: "input",
1426
1805
  name: "url",
1427
- message: i18n.enterApiUrl,
1806
+ message: i18n.api.enterApiUrl,
1428
1807
  validate: (value) => {
1429
- if (!value) return i18n.urlRequired;
1808
+ if (!value) return i18n.api.urlRequired;
1430
1809
  try {
1431
1810
  new URL(value);
1432
1811
  return true;
1433
1812
  } catch {
1434
- return i18n.invalidUrl;
1813
+ return i18n.api.invalidUrl;
1435
1814
  }
1436
1815
  }
1437
1816
  });
1438
1817
  if (url === void 0) {
1439
- console.log(ansis.yellow(i18n.cancelled));
1818
+ console.log(ansis.yellow(i18n.common.cancelled));
1440
1819
  return null;
1441
1820
  }
1442
- const keyMessage = authType === "auth_token" ? i18n.enterAuthToken : i18n.enterApiKey;
1821
+ const keyMessage = authType === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
1443
1822
  const { key } = await inquirer.prompt({
1444
1823
  type: "input",
1445
1824
  name: "key",
1446
1825
  message: keyMessage,
1447
1826
  validate: (value) => {
1448
1827
  if (!value) {
1449
- return i18n.keyRequired;
1828
+ return i18n.api.keyRequired;
1450
1829
  }
1451
1830
  const validation = validateApiKey(value, scriptLang);
1452
1831
  if (!validation.isValid) {
1453
- return validation.error || i18n.invalidKeyFormat;
1832
+ return validation.error || i18n.api.invalidKeyFormat;
1454
1833
  }
1455
1834
  return true;
1456
1835
  }
1457
1836
  });
1458
1837
  if (key === void 0) {
1459
- console.log(ansis.yellow(i18n.cancelled));
1838
+ console.log(ansis.yellow(i18n.common.cancelled));
1460
1839
  return null;
1461
1840
  }
1462
1841
  console.log(ansis.gray(` API Key: ${formatApiKeyDisplay(key)}`));
@@ -1471,276 +1850,144 @@ async function modifyApiConfigPartially(existingConfig, i18n, scriptLang) {
1471
1850
  const { item } = await inquirer.prompt({
1472
1851
  type: "list",
1473
1852
  name: "item",
1474
- message: i18n.selectModifyItems,
1475
- choices: [
1476
- { name: i18n.modifyApiUrl, value: "url" },
1477
- { name: i18n.modifyApiKey, value: "key" },
1478
- { name: i18n.modifyAuthType, value: "authType" }
1479
- ]
1853
+ message: i18n.api.selectModifyItems,
1854
+ choices: addNumbersToChoices([
1855
+ { name: i18n.api.modifyApiUrl, value: "url" },
1856
+ { name: i18n.api.modifyApiKey, value: "key" },
1857
+ { name: i18n.api.modifyAuthType, value: "authType" }
1858
+ ])
1480
1859
  });
1481
1860
  if (!item) {
1482
- console.log(ansis.yellow(i18n.cancelled));
1861
+ console.log(ansis.yellow(i18n.common.cancelled));
1483
1862
  return;
1484
1863
  }
1485
1864
  if (item === "url") {
1486
1865
  const { url } = await inquirer.prompt({
1487
1866
  type: "input",
1488
1867
  name: "url",
1489
- message: i18n.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.none),
1868
+ message: i18n.api.enterNewApiUrl.replace("{url}", currentConfig.url || i18n.common.none),
1490
1869
  default: currentConfig.url,
1491
1870
  validate: (value) => {
1492
- if (!value) return i18n.urlRequired;
1871
+ if (!value) return i18n.api.urlRequired;
1493
1872
  try {
1494
1873
  new URL(value);
1495
1874
  return true;
1496
1875
  } catch {
1497
- return i18n.invalidUrl;
1876
+ return i18n.api.invalidUrl;
1498
1877
  }
1499
1878
  }
1500
1879
  });
1501
1880
  if (url === void 0) {
1502
- console.log(ansis.yellow(i18n.cancelled));
1881
+ console.log(ansis.yellow(i18n.common.cancelled));
1503
1882
  return;
1504
1883
  }
1505
1884
  currentConfig.url = url;
1506
1885
  const savedConfig = configureApi(currentConfig);
1507
1886
  if (savedConfig) {
1508
- console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
1509
- console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${savedConfig.url}`));
1887
+ console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
1888
+ console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${savedConfig.url}`));
1510
1889
  }
1511
1890
  } else if (item === "key") {
1512
1891
  const authType = currentConfig.authType || "auth_token";
1513
- const keyMessage = authType === "auth_token" ? i18n.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.none) : i18n.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.none);
1892
+ const keyMessage = authType === "auth_token" ? i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none) : i18n.api.enterNewApiKey.replace("{key}", currentConfig.key ? formatApiKeyDisplay(currentConfig.key) : i18n.common.none);
1514
1893
  const { key } = await inquirer.prompt({
1515
1894
  type: "input",
1516
1895
  name: "key",
1517
1896
  message: keyMessage,
1518
1897
  validate: (value) => {
1519
1898
  if (!value) {
1520
- return i18n.keyRequired;
1899
+ return i18n.api.keyRequired;
1521
1900
  }
1522
1901
  const validation = validateApiKey(value, scriptLang);
1523
1902
  if (!validation.isValid) {
1524
- return validation.error || i18n.invalidKeyFormat;
1903
+ return validation.error || i18n.api.invalidKeyFormat;
1525
1904
  }
1526
1905
  return true;
1527
1906
  }
1528
1907
  });
1529
1908
  if (key === void 0) {
1530
- console.log(ansis.yellow(i18n.cancelled));
1909
+ console.log(ansis.yellow(i18n.common.cancelled));
1531
1910
  return;
1532
1911
  }
1533
1912
  currentConfig.key = key;
1534
1913
  const savedConfig = configureApi(currentConfig);
1535
1914
  if (savedConfig) {
1536
- console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
1537
- console.log(ansis.gray(` ${i18n.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
1915
+ console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
1916
+ console.log(ansis.gray(` ${i18n.api.apiConfigKey}: ${formatApiKeyDisplay(savedConfig.key)}`));
1538
1917
  }
1539
1918
  } else if (item === "authType") {
1540
1919
  const { authType } = await inquirer.prompt({
1541
1920
  type: "list",
1542
1921
  name: "authType",
1543
- message: i18n.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.none),
1544
- choices: [
1922
+ message: i18n.api.selectNewAuthType.replace("{type}", currentConfig.authType || i18n.common.none),
1923
+ choices: addNumbersToChoices([
1545
1924
  { name: "Auth Token (OAuth)", value: "auth_token" },
1546
1925
  { name: "API Key", value: "api_key" }
1547
- ],
1926
+ ]),
1548
1927
  default: currentConfig.authType === "api_key" ? 1 : 0
1549
1928
  });
1550
1929
  if (authType === void 0) {
1551
- console.log(ansis.yellow(i18n.cancelled));
1930
+ console.log(ansis.yellow(i18n.common.cancelled));
1552
1931
  return;
1553
1932
  }
1554
1933
  currentConfig.authType = authType;
1555
1934
  const savedConfig = configureApi(currentConfig);
1556
1935
  if (savedConfig) {
1557
- console.log(ansis.green(`\u2714 ${i18n.modificationSaved}`));
1558
- console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${savedConfig.authType}`));
1936
+ console.log(ansis.green(`\u2714 ${i18n.api.modificationSaved}`));
1937
+ console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${savedConfig.authType}`));
1559
1938
  }
1560
1939
  }
1561
1940
  }
1562
1941
  async function updatePromptOnly(configLang, scriptLang, aiOutputLang) {
1563
- const i18n = I18N[scriptLang];
1942
+ const i18n = getTranslation(scriptLang);
1564
1943
  const backupDir = backupExistingConfig();
1565
1944
  if (backupDir) {
1566
- console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
1945
+ console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
1567
1946
  }
1568
1947
  copyConfigFiles(configLang, true);
1569
1948
  if (aiOutputLang) {
1570
1949
  applyAiLanguageDirective(aiOutputLang);
1571
1950
  }
1572
1951
  await configureAiPersonality(scriptLang);
1573
- console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
1574
- console.log("\n" + ansis.cyan(i18n.complete));
1575
- }
1576
-
1577
- function getPlatform() {
1578
- const p = platform();
1579
- if (p === "win32") return "windows";
1580
- if (p === "darwin") return "macos";
1581
- return "linux";
1582
- }
1583
- function isTermux() {
1584
- return !!(process.env.PREFIX && process.env.PREFIX.includes("com.termux")) || !!process.env.TERMUX_VERSION || existsSync("/data/data/com.termux/files/usr");
1585
- }
1586
- function getTermuxPrefix() {
1587
- return process.env.PREFIX || "/data/data/com.termux/files/usr";
1588
- }
1589
- function isWindows() {
1590
- return getPlatform() === "windows";
1591
- }
1592
- function getMcpCommand() {
1593
- if (isWindows()) {
1594
- return ["cmd", "/c", "npx"];
1595
- }
1596
- return ["npx"];
1597
- }
1598
- async function commandExists(command) {
1599
- try {
1600
- const cmd = getPlatform() === "windows" ? "where" : "which";
1601
- const res = await exec(cmd, [command]);
1602
- if (res.exitCode === 0) {
1603
- return true;
1604
- }
1605
- } catch {
1606
- }
1607
- if (isTermux()) {
1608
- const termuxPrefix = getTermuxPrefix();
1609
- const possiblePaths = [
1610
- `${termuxPrefix}/bin/${command}`,
1611
- `${termuxPrefix}/usr/bin/${command}`,
1612
- `/data/data/com.termux/files/usr/bin/${command}`
1613
- ];
1614
- for (const path of possiblePaths) {
1615
- if (existsSync(path)) {
1616
- return true;
1617
- }
1618
- }
1619
- }
1620
- if (getPlatform() !== "windows") {
1621
- const commonPaths = [
1622
- `/usr/local/bin/${command}`,
1623
- `/usr/bin/${command}`,
1624
- `/bin/${command}`,
1625
- `${process.env.HOME}/.local/bin/${command}`
1626
- ];
1627
- for (const path of commonPaths) {
1628
- if (existsSync(path)) {
1629
- return true;
1630
- }
1631
- }
1632
- }
1633
- return false;
1952
+ console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
1953
+ console.log("\n" + ansis.cyan(i18n.common.complete));
1634
1954
  }
1635
1955
 
1636
1956
  async function isClaudeCodeInstalled() {
1637
1957
  return await commandExists("claude");
1638
1958
  }
1639
1959
  async function installClaudeCode(lang) {
1640
- const i18n = I18N[lang];
1960
+ const i18n = getTranslation(lang);
1641
1961
  if (isTermux()) {
1642
- console.log(ansis.yellow(`\u2139 ${i18n.termuxDetected}`));
1962
+ console.log(ansis.yellow(`\u2139 ${i18n.installation.termuxDetected}`));
1643
1963
  const termuxPrefix = getTermuxPrefix();
1644
- console.log(ansis.gray(i18n.termuxPathInfo.replace("{path}", termuxPrefix)));
1964
+ console.log(ansis.gray(i18n.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
1645
1965
  console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
1646
1966
  console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
1647
1967
  }
1648
- console.log(i18n.installing);
1968
+ console.log(i18n.installation.installing);
1649
1969
  try {
1650
1970
  await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
1651
- console.log(`\u2714 ${i18n.installSuccess}`);
1971
+ console.log(`\u2714 ${i18n.installation.installSuccess}`);
1652
1972
  if (isTermux()) {
1653
1973
  console.log(ansis.gray(`
1654
1974
  Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
1655
1975
  }
1656
1976
  } catch (error) {
1657
- console.error(`\u2716 ${i18n.installFailed}`);
1977
+ console.error(`\u2716 ${i18n.installation.installFailed}`);
1658
1978
  if (isTermux()) {
1659
1979
  console.error(ansis.yellow(`
1660
- ${i18n.termuxInstallHint}
1980
+ ${i18n.installation.termuxInstallHint}
1661
1981
  `));
1662
1982
  }
1663
1983
  throw error;
1664
1984
  }
1665
1985
  }
1666
1986
 
1667
- function getMcpConfigPath() {
1668
- return ClAUDE_CONFIG_FILE;
1669
- }
1670
- function readMcpConfig() {
1671
- return readJsonConfig(ClAUDE_CONFIG_FILE);
1672
- }
1673
- function writeMcpConfig(config) {
1674
- writeJsonConfig(ClAUDE_CONFIG_FILE, config);
1675
- }
1676
- function backupMcpConfig() {
1677
- const backupBaseDir = join(CLAUDE_DIR, "backup");
1678
- return backupJsonConfig(ClAUDE_CONFIG_FILE, backupBaseDir);
1679
- }
1680
- function mergeMcpServers(existing, newServers) {
1681
- const config = existing || { mcpServers: {} };
1682
- if (!config.mcpServers) {
1683
- config.mcpServers = {};
1684
- }
1685
- Object.assign(config.mcpServers, newServers);
1686
- return config;
1687
- }
1688
- function applyPlatformCommand(config) {
1689
- if (config.command === "npx" && isWindows()) {
1690
- const mcpCmd = getMcpCommand();
1691
- config.command = mcpCmd[0];
1692
- config.args = [...mcpCmd.slice(1), ...config.args || []];
1693
- }
1694
- }
1695
- function buildMcpServerConfig(baseConfig, apiKey, placeholder = "YOUR_EXA_API_KEY", envVarName) {
1696
- const config = deepClone(baseConfig);
1697
- applyPlatformCommand(config);
1698
- if (!apiKey) {
1699
- return config;
1700
- }
1701
- if (envVarName && config.env) {
1702
- config.env[envVarName] = apiKey;
1703
- return config;
1704
- }
1705
- if (config.args) {
1706
- config.args = config.args.map((arg) => arg.replace(placeholder, apiKey));
1707
- }
1708
- if (config.url) {
1709
- config.url = config.url.replace(placeholder, apiKey);
1710
- }
1711
- return config;
1712
- }
1713
- function fixWindowsMcpConfig(config) {
1714
- if (!isWindows() || !config.mcpServers) {
1715
- return config;
1716
- }
1717
- const fixed = { ...config };
1718
- for (const [, serverConfig] of Object.entries(fixed.mcpServers)) {
1719
- if (serverConfig && typeof serverConfig === "object" && "command" in serverConfig) {
1720
- applyPlatformCommand(serverConfig);
1721
- }
1722
- }
1723
- return fixed;
1724
- }
1725
- function addCompletedOnboarding() {
1726
- try {
1727
- let config = readMcpConfig();
1728
- if (!config) {
1729
- config = { mcpServers: {} };
1730
- }
1731
- config.hasCompletedOnboarding = true;
1732
- config = fixWindowsMcpConfig(config);
1733
- writeMcpConfig(config);
1734
- } catch (error) {
1735
- console.error(I18N[readZcfConfig()?.preferredLang || "en"].failedToAddOnboardingFlag, error);
1736
- throw error;
1737
- }
1738
- }
1739
-
1740
1987
  async function selectAiOutputLanguage(scriptLang, defaultLang) {
1741
- const i18n = I18N[scriptLang];
1988
+ const i18n = getTranslation(scriptLang);
1742
1989
  console.log(ansis.dim(`
1743
- ${i18n.aiOutputLangHint}
1990
+ ${i18n.language.aiOutputLangHint}
1744
1991
  `));
1745
1992
  const aiLangChoices = Object.entries(AI_OUTPUT_LANGUAGES).map(([key, value]) => ({
1746
1993
  title: value.label,
@@ -1750,15 +1997,15 @@ async function selectAiOutputLanguage(scriptLang, defaultLang) {
1750
1997
  const { lang } = await inquirer.prompt({
1751
1998
  type: "list",
1752
1999
  name: "lang",
1753
- message: i18n.selectAiOutputLang,
1754
- choices: aiLangChoices.map((choice) => ({
2000
+ message: i18n.language.selectAiOutputLang,
2001
+ choices: addNumbersToChoices(aiLangChoices.map((choice) => ({
1755
2002
  name: choice.title,
1756
2003
  value: choice.value
1757
- })),
2004
+ }))),
1758
2005
  default: defaultChoice
1759
2006
  });
1760
2007
  if (!lang) {
1761
- console.log(ansis.yellow(i18n.cancelled));
2008
+ console.log(ansis.yellow(i18n.common.cancelled));
1762
2009
  process.exit(0);
1763
2010
  }
1764
2011
  let aiOutputLang = lang;
@@ -1766,11 +2013,11 @@ async function selectAiOutputLanguage(scriptLang, defaultLang) {
1766
2013
  const { customLang } = await inquirer.prompt({
1767
2014
  type: "input",
1768
2015
  name: "customLang",
1769
- message: i18n.enterCustomLanguage,
1770
- validate: (value) => !!value || i18n.languageRequired
2016
+ message: i18n.language.enterCustomLanguage,
2017
+ validate: (value) => !!value || i18n.language?.languageRequired || "Language is required"
1771
2018
  });
1772
2019
  if (!customLang) {
1773
- console.log(ansis.yellow(i18n.cancelled));
2020
+ console.log(ansis.yellow(i18n.common.cancelled));
1774
2021
  process.exit(0);
1775
2022
  }
1776
2023
  return customLang;
@@ -1789,10 +2036,10 @@ async function selectScriptLanguage(currentLang) {
1789
2036
  type: "list",
1790
2037
  name: "lang",
1791
2038
  message: "Select ZCF display language / \u9009\u62E9ZCF\u663E\u793A\u8BED\u8A00",
1792
- choices: SUPPORTED_LANGS.map((l) => ({
2039
+ choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
1793
2040
  name: LANG_LABELS[l],
1794
2041
  value: l
1795
- }))
2042
+ })))
1796
2043
  });
1797
2044
  if (!lang) {
1798
2045
  console.log(ansis.yellow("Operation cancelled / \u64CD\u4F5C\u5DF2\u53D6\u6D88"));
@@ -1806,19 +2053,19 @@ async function selectScriptLanguage(currentLang) {
1806
2053
  return scriptLang;
1807
2054
  }
1808
2055
  async function resolveAiOutputLanguage(scriptLang, commandLineOption, savedConfig) {
1809
- const i18n = I18N[scriptLang];
2056
+ const i18n = getTranslation(scriptLang);
1810
2057
  if (commandLineOption) {
1811
2058
  return commandLineOption;
1812
2059
  }
1813
2060
  if (savedConfig?.aiOutputLang) {
1814
- console.log(ansis.gray(`\u2714 ${i18n.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
2061
+ console.log(ansis.gray(`\u2714 ${i18n.language.aiOutputLangHint}: ${savedConfig.aiOutputLang}`));
1815
2062
  return savedConfig.aiOutputLang;
1816
2063
  }
1817
2064
  return await selectAiOutputLanguage(scriptLang, scriptLang);
1818
2065
  }
1819
2066
 
1820
2067
  async function selectMcpServices(scriptLang) {
1821
- const i18n = I18N[scriptLang];
2068
+ const i18n = getTranslation(scriptLang);
1822
2069
  const choices = MCP_SERVICES.map((service) => ({
1823
2070
  name: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
1824
2071
  value: service.id,
@@ -1827,11 +2074,11 @@ async function selectMcpServices(scriptLang) {
1827
2074
  const { services } = await inquirer.prompt({
1828
2075
  type: "checkbox",
1829
2076
  name: "services",
1830
- message: `${i18n.selectMcpServices}${i18n.multiSelectHint}`,
2077
+ message: `${i18n.mcp.selectMcpServices}${i18n.common.multiSelectHint}`,
1831
2078
  choices
1832
2079
  });
1833
2080
  if (services === void 0) {
1834
- console.log(ansis.yellow(i18n.cancelled));
2081
+ console.log(ansis.yellow(i18n.common.cancelled));
1835
2082
  return void 0;
1836
2083
  }
1837
2084
  return services;
@@ -1891,11 +2138,11 @@ function getRootDir() {
1891
2138
  return dirname(distDir);
1892
2139
  }
1893
2140
  async function selectAndInstallWorkflows(configLang, scriptLang) {
1894
- const i18n = I18N[scriptLang];
2141
+ const i18n = getTranslation(scriptLang);
1895
2142
  const workflows = getOrderedWorkflows();
1896
2143
  const choices = workflows.map((workflow) => {
1897
2144
  const nameKey = workflow.id;
1898
- const name = i18n.workflowOption[nameKey] || workflow.id;
2145
+ const name = i18n.workflow.workflowOption[nameKey] || workflow.id;
1899
2146
  return {
1900
2147
  name,
1901
2148
  value: workflow.id,
@@ -1905,11 +2152,11 @@ async function selectAndInstallWorkflows(configLang, scriptLang) {
1905
2152
  const { selectedWorkflows } = await inquirer.prompt({
1906
2153
  type: "checkbox",
1907
2154
  name: "selectedWorkflows",
1908
- message: `${i18n.selectWorkflowType}${i18n.multiSelectHint}`,
2155
+ message: `${i18n.workflow.selectWorkflowType}${i18n.common.multiSelectHint}`,
1909
2156
  choices
1910
2157
  });
1911
2158
  if (!selectedWorkflows || selectedWorkflows.length === 0) {
1912
- console.log(ansis.yellow(i18n.cancelled));
2159
+ console.log(ansis.yellow(i18n.common.cancelled));
1913
2160
  return;
1914
2161
  }
1915
2162
  await cleanupOldVersionFiles(scriptLang);
@@ -1922,7 +2169,7 @@ async function selectAndInstallWorkflows(configLang, scriptLang) {
1922
2169
  }
1923
2170
  async function installWorkflowWithDependencies(config, configLang, scriptLang) {
1924
2171
  const rootDir = getRootDir();
1925
- const i18n = I18N[scriptLang];
2172
+ const i18n = getTranslation(scriptLang);
1926
2173
  const result = {
1927
2174
  workflow: config.id,
1928
2175
  success: true,
@@ -1930,9 +2177,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
1930
2177
  installedAgents: [],
1931
2178
  errors: []
1932
2179
  };
1933
- const workflowName = i18n.workflowOption[config.id] || config.id;
2180
+ const workflowName = i18n.workflow.workflowOption[config.id] || config.id;
1934
2181
  console.log(ansis.cyan(`
1935
- \u{1F4E6} ${i18n.installingWorkflow}: ${workflowName}...`));
2182
+ \u{1F4E6} ${i18n.workflow.installingWorkflow}: ${workflowName}...`));
1936
2183
  const commandsDir = join(CLAUDE_DIR, "commands", "zcf");
1937
2184
  if (!existsSync(commandsDir)) {
1938
2185
  await mkdir(commandsDir, { recursive: true });
@@ -1945,9 +2192,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
1945
2192
  try {
1946
2193
  await copyFile$1(commandSource, commandDest);
1947
2194
  result.installedCommands.push(destFileName);
1948
- console.log(ansis.gray(` \u2714 ${i18n.installedCommand}: zcf/${destFileName}`));
2195
+ console.log(ansis.gray(` \u2714 ${i18n.workflow.installedCommand}: zcf/${destFileName}`));
1949
2196
  } catch (error) {
1950
- const errorMsg = `${i18n.failedToInstallCommand} ${commandFile}: ${error}`;
2197
+ const errorMsg = `${i18n.workflow.failedToInstallCommand} ${commandFile}: ${error}`;
1951
2198
  result.errors?.push(errorMsg);
1952
2199
  console.error(ansis.red(` \u2717 ${errorMsg}`));
1953
2200
  result.success = false;
@@ -1966,9 +2213,9 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
1966
2213
  try {
1967
2214
  await copyFile$1(agentSource, agentDest);
1968
2215
  result.installedAgents.push(agent.filename);
1969
- console.log(ansis.gray(` \u2714 ${i18n.installedAgent}: zcf/${config.category}/${agent.filename}`));
2216
+ console.log(ansis.gray(` \u2714 ${i18n.workflow.installedAgent}: zcf/${config.category}/${agent.filename}`));
1970
2217
  } catch (error) {
1971
- const errorMsg = `${i18n.failedToInstallAgent} ${agent.filename}: ${error}`;
2218
+ const errorMsg = `${i18n.workflow.failedToInstallAgent} ${agent.filename}: ${error}`;
1972
2219
  result.errors?.push(errorMsg);
1973
2220
  console.error(ansis.red(` \u2717 ${errorMsg}`));
1974
2221
  if (agent.required) {
@@ -1979,20 +2226,20 @@ async function installWorkflowWithDependencies(config, configLang, scriptLang) {
1979
2226
  }
1980
2227
  }
1981
2228
  if (result.success) {
1982
- console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflowInstallSuccess}`));
2229
+ console.log(ansis.green(`\u2714 ${workflowName} ${i18n.workflow.workflowInstallSuccess}`));
1983
2230
  if (config.id === "bmadWorkflow") {
1984
2231
  console.log(ansis.cyan(`
1985
- ${i18n.bmadInitPrompt}`));
2232
+ ${i18n.bmad.bmadInitPrompt}`));
1986
2233
  }
1987
2234
  } else {
1988
- console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflowInstallError}`));
2235
+ console.log(ansis.red(`\u2717 ${workflowName} ${i18n.workflow.workflowInstallError}`));
1989
2236
  }
1990
2237
  return result;
1991
2238
  }
1992
2239
  async function cleanupOldVersionFiles(scriptLang) {
1993
- const i18n = I18N[scriptLang];
2240
+ const i18n = getTranslation(scriptLang);
1994
2241
  console.log(ansis.cyan(`
1995
- \u{1F9F9} ${i18n.cleaningOldFiles || "Cleaning up old version files"}...`));
2242
+ \u{1F9F9} ${i18n.workflow.cleaningOldFiles || "Cleaning up old version files"}...`));
1996
2243
  const oldCommandFiles = [
1997
2244
  join(CLAUDE_DIR, "commands", "workflow.md"),
1998
2245
  join(CLAUDE_DIR, "commands", "feat.md")
@@ -2005,9 +2252,9 @@ async function cleanupOldVersionFiles(scriptLang) {
2005
2252
  if (existsSync(file)) {
2006
2253
  try {
2007
2254
  await rm(file, { force: true });
2008
- console.log(ansis.gray(` \u2714 ${i18n.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2255
+ console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2009
2256
  } catch (error) {
2010
- console.error(ansis.yellow(` \u26A0 ${i18n.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2257
+ console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2011
2258
  }
2012
2259
  }
2013
2260
  }
@@ -2015,14 +2262,466 @@ async function cleanupOldVersionFiles(scriptLang) {
2015
2262
  if (existsSync(file)) {
2016
2263
  try {
2017
2264
  await rm(file, { force: true });
2018
- console.log(ansis.gray(` \u2714 ${i18n.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2265
+ console.log(ansis.gray(` \u2714 ${i18n.workflow.removedOldFile || "Removed old file"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2019
2266
  } catch (error) {
2020
- console.error(ansis.yellow(` \u26A0 ${i18n.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2267
+ console.error(ansis.yellow(` \u26A0 ${i18n.workflow.failedToRemoveFile || "Failed to remove"}: ${file.replace(CLAUDE_DIR, "~/.claude")}`));
2021
2268
  }
2022
2269
  }
2023
2270
  }
2024
2271
  }
2025
2272
 
2273
+ const execAsync$1 = promisify(exec$1);
2274
+ async function isCcrInstalled() {
2275
+ try {
2276
+ await execAsync$1("ccr version");
2277
+ return true;
2278
+ } catch {
2279
+ try {
2280
+ await execAsync$1("which ccr");
2281
+ return true;
2282
+ } catch {
2283
+ return false;
2284
+ }
2285
+ }
2286
+ }
2287
+ async function installCcr(scriptLang) {
2288
+ const i18n = getTranslation(scriptLang);
2289
+ const installed = await isCcrInstalled();
2290
+ if (installed) {
2291
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
2292
+ return;
2293
+ }
2294
+ console.log(ansis.cyan(`\u{1F4E6} ${i18n.ccr.installingCcr}`));
2295
+ try {
2296
+ await execAsync$1("npm install -g claude-code-router --force");
2297
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrInstallSuccess}`));
2298
+ } catch (error) {
2299
+ if (error.message?.includes("EEXIST")) {
2300
+ console.log(ansis.yellow(`\u26A0 ${i18n.ccr.ccrAlreadyInstalled}`));
2301
+ return;
2302
+ }
2303
+ console.error(ansis.red(`\u2716 ${i18n.ccr.ccrInstallFailed}`));
2304
+ throw error;
2305
+ }
2306
+ }
2307
+
2308
+ const PROVIDER_PRESETS_URL = "https://pub-0dc3e1677e894f07bbea11b17a29e032.r2.dev/providers.json";
2309
+ async function fetchProviderPresets() {
2310
+ try {
2311
+ const controller = new AbortController();
2312
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
2313
+ const response = await fetch(PROVIDER_PRESETS_URL, {
2314
+ signal: controller.signal
2315
+ });
2316
+ clearTimeout(timeoutId);
2317
+ if (!response.ok) {
2318
+ throw new Error(`HTTP error! status: ${response.status}`);
2319
+ }
2320
+ const data = await response.json();
2321
+ const presets = [];
2322
+ if (Array.isArray(data)) {
2323
+ for (const provider of data) {
2324
+ if (provider && typeof provider === "object") {
2325
+ presets.push({
2326
+ name: provider.name || "",
2327
+ provider: provider.name || "",
2328
+ baseURL: provider.api_base_url || provider.baseURL || provider.url,
2329
+ requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
2330
+ models: provider.models || [],
2331
+ description: provider.description || provider.name || "",
2332
+ transformer: provider.transformer
2333
+ });
2334
+ }
2335
+ }
2336
+ } else if (data && typeof data === "object") {
2337
+ for (const [key, value] of Object.entries(data)) {
2338
+ if (typeof value === "object" && value !== null) {
2339
+ const provider = value;
2340
+ presets.push({
2341
+ name: provider.name || key,
2342
+ provider: key,
2343
+ baseURL: provider.api_base_url || provider.baseURL || provider.url,
2344
+ requiresApiKey: provider.api_key === "" || provider.requiresApiKey !== false,
2345
+ models: provider.models || [],
2346
+ description: provider.description || "",
2347
+ transformer: provider.transformer
2348
+ });
2349
+ }
2350
+ }
2351
+ }
2352
+ return presets;
2353
+ } catch (error) {
2354
+ return getFallbackPresets();
2355
+ }
2356
+ }
2357
+ function getFallbackPresets() {
2358
+ return [
2359
+ {
2360
+ name: "dashscope",
2361
+ provider: "dashscope",
2362
+ baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
2363
+ requiresApiKey: true,
2364
+ models: ["qwen3-coder-plus"],
2365
+ description: "Alibaba DashScope",
2366
+ transformer: {
2367
+ use: [["maxtoken", { max_tokens: 65536 }]],
2368
+ "qwen3-coder-plus": {
2369
+ use: ["enhancetool"]
2370
+ }
2371
+ }
2372
+ },
2373
+ {
2374
+ name: "deepseek",
2375
+ provider: "deepseek",
2376
+ baseURL: "https://api.deepseek.com/chat/completions",
2377
+ requiresApiKey: true,
2378
+ models: ["deepseek-chat", "deepseek-reasoner"],
2379
+ description: "DeepSeek AI models",
2380
+ transformer: {
2381
+ use: ["deepseek"],
2382
+ "deepseek-chat": {
2383
+ use: ["tooluse"]
2384
+ }
2385
+ }
2386
+ },
2387
+ {
2388
+ name: "gemini",
2389
+ provider: "gemini",
2390
+ baseURL: "https://generativelanguage.googleapis.com/v1beta/models/",
2391
+ requiresApiKey: true,
2392
+ models: ["gemini-2.5-flash", "gemini-2.5-pro"],
2393
+ description: "Google Gemini models",
2394
+ transformer: {
2395
+ use: ["gemini"]
2396
+ }
2397
+ },
2398
+ {
2399
+ name: "modelscope",
2400
+ provider: "modelscope",
2401
+ baseURL: "https://api-inference.modelscope.cn/v1/chat/completions",
2402
+ requiresApiKey: true,
2403
+ models: ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507", "ZhipuAI/GLM-4.5"],
2404
+ description: "ModelScope AI models",
2405
+ transformer: {
2406
+ use: [["maxtoken", { max_tokens: 65536 }]],
2407
+ "Qwen/Qwen3-Coder-480B-A35B-Instruct": {
2408
+ use: ["enhancetool"]
2409
+ },
2410
+ "Qwen/Qwen3-235B-A22B-Thinking-2507": {
2411
+ use: ["reasoning"]
2412
+ }
2413
+ }
2414
+ },
2415
+ {
2416
+ name: "openrouter",
2417
+ provider: "openrouter",
2418
+ baseURL: "https://openrouter.ai/api/v1/chat/completions",
2419
+ requiresApiKey: true,
2420
+ models: [
2421
+ "google/gemini-2.5-pro-preview",
2422
+ "anthropic/claude-sonnet-4",
2423
+ "anthropic/claude-3.5-sonnet",
2424
+ "anthropic/claude-3.7-sonnet:thinking"
2425
+ ],
2426
+ description: "OpenRouter API",
2427
+ transformer: {
2428
+ use: ["openrouter"]
2429
+ }
2430
+ },
2431
+ {
2432
+ name: "siliconflow",
2433
+ provider: "siliconflow",
2434
+ baseURL: "https://api.siliconflow.cn/v1/chat/completions",
2435
+ requiresApiKey: true,
2436
+ models: ["moonshotai/Kimi-K2-Instruct"],
2437
+ description: "SiliconFlow AI",
2438
+ transformer: {
2439
+ use: [["maxtoken", { max_tokens: 16384 }]]
2440
+ }
2441
+ },
2442
+ {
2443
+ name: "volcengine",
2444
+ provider: "volcengine",
2445
+ baseURL: "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
2446
+ requiresApiKey: true,
2447
+ models: ["deepseek-v3-250324", "deepseek-r1-250528"],
2448
+ description: "Volcengine AI",
2449
+ transformer: {
2450
+ use: ["deepseek"]
2451
+ }
2452
+ }
2453
+ ];
2454
+ }
2455
+
2456
+ const execAsync = promisify(exec$1);
2457
+ const CCR_CONFIG_DIR = join$1(homedir(), ".claude-code-router");
2458
+ const CCR_CONFIG_FILE = join$1(CCR_CONFIG_DIR, "config.json");
2459
+ const CCR_BACKUP_DIR = CCR_CONFIG_DIR;
2460
+ function ensureCcrConfigDir() {
2461
+ if (!existsSync(CCR_CONFIG_DIR)) {
2462
+ mkdirSync(CCR_CONFIG_DIR, { recursive: true });
2463
+ }
2464
+ }
2465
+ function backupCcrConfig(scriptLang) {
2466
+ const i18n = getTranslation(scriptLang);
2467
+ try {
2468
+ if (!existsSync(CCR_CONFIG_FILE)) {
2469
+ return null;
2470
+ }
2471
+ const timestamp = dayjs().format("YYYY-MM-DDTHH-mm-ss-SSS") + "Z";
2472
+ const backupFileName = `config.json.${timestamp}.bak`;
2473
+ const backupPath = join$1(CCR_BACKUP_DIR, backupFileName);
2474
+ console.log(ansis.cyan(`${i18n.ccr.backupCcrConfig}`));
2475
+ copyFileSync(CCR_CONFIG_FILE, backupPath);
2476
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrBackupSuccess.replace("{path}", backupPath)}`));
2477
+ return backupPath;
2478
+ } catch (error) {
2479
+ console.error(ansis.red(`${i18n.ccr.ccrBackupFailed}:`), error.message);
2480
+ return null;
2481
+ }
2482
+ }
2483
+ function readCcrConfig() {
2484
+ if (!existsSync(CCR_CONFIG_FILE)) {
2485
+ return null;
2486
+ }
2487
+ return readJsonConfig(CCR_CONFIG_FILE);
2488
+ }
2489
+ function writeCcrConfig(config) {
2490
+ ensureCcrConfigDir();
2491
+ writeJsonConfig(CCR_CONFIG_FILE, config);
2492
+ }
2493
+ async function configureCcrProxy(ccrConfig) {
2494
+ const settings = readJsonConfig(SETTINGS_FILE) || {};
2495
+ const host = ccrConfig.HOST || "127.0.0.1";
2496
+ const port = ccrConfig.PORT || 3456;
2497
+ const apiKey = ccrConfig.APIKEY || "sk-zcf-x-ccr";
2498
+ if (!settings.env) {
2499
+ settings.env = {};
2500
+ }
2501
+ settings.env.ANTHROPIC_BASE_URL = `http://${host}:${port}`;
2502
+ settings.env.ANTHROPIC_API_KEY = apiKey;
2503
+ writeJsonConfig(SETTINGS_FILE, settings);
2504
+ }
2505
+ async function selectCcrPreset(scriptLang) {
2506
+ const i18n = getTranslation(scriptLang);
2507
+ console.log(ansis.cyan(`${i18n.ccr.fetchingPresets}`));
2508
+ const presets = await fetchProviderPresets();
2509
+ if (!presets || presets.length === 0) {
2510
+ console.log(ansis.yellow(`${i18n.ccr.noPresetsAvailable}`));
2511
+ return null;
2512
+ }
2513
+ try {
2514
+ const choices = [
2515
+ ...presets.map((p, index) => ({
2516
+ name: `${index + 1}. ${p.name}`,
2517
+ value: p
2518
+ })),
2519
+ {
2520
+ name: `${presets.length + 1}. ${i18n.ccr.skipOption}`,
2521
+ value: "skip"
2522
+ }
2523
+ ];
2524
+ const { preset } = await inquirer.prompt({
2525
+ type: "list",
2526
+ name: "preset",
2527
+ message: i18n.ccr.selectCcrPreset,
2528
+ choices
2529
+ });
2530
+ return preset;
2531
+ } catch (error) {
2532
+ if (error.name === "ExitPromptError") {
2533
+ console.log(ansis.yellow(i18n.common.cancelled));
2534
+ return null;
2535
+ }
2536
+ throw error;
2537
+ }
2538
+ }
2539
+ async function configureCcrWithPreset(preset, scriptLang) {
2540
+ const i18n = getTranslation(scriptLang);
2541
+ const provider = {
2542
+ name: preset.name,
2543
+ // Use the original name from JSON
2544
+ api_base_url: preset.baseURL || "",
2545
+ api_key: "",
2546
+ models: preset.models
2547
+ };
2548
+ if (preset.transformer) {
2549
+ provider.transformer = preset.transformer;
2550
+ }
2551
+ if (preset.requiresApiKey) {
2552
+ try {
2553
+ const { apiKey } = await inquirer.prompt({
2554
+ type: "input",
2555
+ name: "apiKey",
2556
+ message: i18n.ccr.enterApiKeyForProvider.replace("{provider}", preset.name),
2557
+ validate: (value) => !!value || i18n.api.keyRequired
2558
+ });
2559
+ provider.api_key = apiKey;
2560
+ } catch (error) {
2561
+ if (error.name === "ExitPromptError") {
2562
+ throw error;
2563
+ }
2564
+ throw error;
2565
+ }
2566
+ } else {
2567
+ provider.api_key = "sk-free";
2568
+ }
2569
+ let defaultModel = preset.models[0];
2570
+ if (preset.models.length > 1) {
2571
+ try {
2572
+ const { model } = await inquirer.prompt({
2573
+ type: "list",
2574
+ name: "model",
2575
+ message: i18n.ccr.selectDefaultModelForProvider.replace("{provider}", preset.name),
2576
+ choices: preset.models.map((m, index) => ({
2577
+ name: `${index + 1}. ${m}`,
2578
+ value: m
2579
+ }))
2580
+ });
2581
+ defaultModel = model;
2582
+ } catch (error) {
2583
+ if (error.name === "ExitPromptError") {
2584
+ throw error;
2585
+ }
2586
+ throw error;
2587
+ }
2588
+ }
2589
+ const router = {
2590
+ default: `${preset.name},${defaultModel}`,
2591
+ // Use the original name
2592
+ background: `${preset.name},${defaultModel}`,
2593
+ think: `${preset.name},${defaultModel}`,
2594
+ longContext: `${preset.name},${defaultModel}`,
2595
+ longContextThreshold: 6e4,
2596
+ webSearch: `${preset.name},${defaultModel}`
2597
+ };
2598
+ const config = {
2599
+ LOG: true,
2600
+ CLAUDE_PATH: "",
2601
+ HOST: "127.0.0.1",
2602
+ PORT: 3456,
2603
+ APIKEY: "sk-zcf-x-ccr",
2604
+ API_TIMEOUT_MS: "600000",
2605
+ PROXY_URL: "",
2606
+ transformers: [],
2607
+ Providers: [provider],
2608
+ Router: router
2609
+ };
2610
+ return config;
2611
+ }
2612
+ async function restartAndCheckCcrStatus(scriptLang) {
2613
+ const i18n = getTranslation(scriptLang);
2614
+ try {
2615
+ console.log(ansis.cyan(`${i18n.ccr.restartingCcr}`));
2616
+ await execAsync("ccr restart");
2617
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestartSuccess}`));
2618
+ console.log(ansis.cyan(`${i18n.ccr.checkingCcrStatus}`));
2619
+ const { stdout } = await execAsync("ccr status");
2620
+ console.log(ansis.gray(stdout));
2621
+ } catch (error) {
2622
+ console.error(ansis.red(`${i18n.ccr.ccrRestartFailed}:`), error.message || error);
2623
+ if (process.env.DEBUG) {
2624
+ console.error("Full error:", error);
2625
+ }
2626
+ }
2627
+ }
2628
+ function showConfigurationTips(scriptLang, apiKey) {
2629
+ const i18n = getTranslation(scriptLang);
2630
+ console.log(ansis.bold.cyan(`
2631
+ \u{1F4CC} ${i18n.ccr.configTips}:`));
2632
+ console.log(ansis.blue(` \u2022 ${i18n.ccr.advancedConfigTip}`));
2633
+ console.log(ansis.blue(` \u2022 ${i18n.ccr.manualConfigTip}`));
2634
+ console.log(ansis.bold.yellow(` \u2022 ${i18n.ccr.useClaudeCommand}`));
2635
+ if (apiKey) {
2636
+ console.log(ansis.bold.green(` \u2022 ${i18n.ccr.ccrUiApiKey || "CCR UI API Key"}: ${apiKey}`));
2637
+ console.log(ansis.gray(` ${i18n.ccr.ccrUiApiKeyHint || "Use this API key to login to CCR UI"}`));
2638
+ }
2639
+ console.log("");
2640
+ }
2641
+ async function setupCcrConfiguration(scriptLang) {
2642
+ const i18n = getTranslation(scriptLang);
2643
+ try {
2644
+ const existingConfig = readCcrConfig();
2645
+ if (existingConfig) {
2646
+ console.log(ansis.blue(`\u2139 ${i18n.ccr.existingCcrConfig}`));
2647
+ let shouldBackupAndReconfigure = false;
2648
+ try {
2649
+ const result = await inquirer.prompt({
2650
+ type: "confirm",
2651
+ name: "overwrite",
2652
+ message: i18n.ccr.overwriteCcrConfig,
2653
+ default: false
2654
+ });
2655
+ shouldBackupAndReconfigure = result.overwrite;
2656
+ } catch (error) {
2657
+ if (error.name === "ExitPromptError") {
2658
+ console.log(ansis.yellow(i18n.common.cancelled));
2659
+ return false;
2660
+ }
2661
+ throw error;
2662
+ }
2663
+ if (!shouldBackupAndReconfigure) {
2664
+ console.log(ansis.yellow(`${i18n.ccr.keepingExistingConfig}`));
2665
+ await configureCcrProxy(existingConfig);
2666
+ return true;
2667
+ }
2668
+ backupCcrConfig(scriptLang);
2669
+ }
2670
+ const preset = await selectCcrPreset(scriptLang);
2671
+ if (!preset) {
2672
+ return false;
2673
+ }
2674
+ let config;
2675
+ if (preset === "skip") {
2676
+ console.log(ansis.yellow(`${i18n.ccr.skipConfiguring}`));
2677
+ config = {
2678
+ LOG: false,
2679
+ CLAUDE_PATH: "",
2680
+ HOST: "127.0.0.1",
2681
+ PORT: 3456,
2682
+ APIKEY: "sk-zcf-x-ccr",
2683
+ API_TIMEOUT_MS: "600000",
2684
+ PROXY_URL: "",
2685
+ transformers: [],
2686
+ Providers: [],
2687
+ // Empty providers array
2688
+ Router: {
2689
+ // Empty router configuration - user will configure in CCR UI
2690
+ }
2691
+ };
2692
+ } else {
2693
+ config = await configureCcrWithPreset(preset, scriptLang);
2694
+ }
2695
+ writeCcrConfig(config);
2696
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrConfigSuccess}`));
2697
+ await configureCcrProxy(config);
2698
+ console.log(ansis.green(`\u2714 ${i18n.ccr.proxyConfigSuccess}`));
2699
+ await restartAndCheckCcrStatus(scriptLang);
2700
+ showConfigurationTips(scriptLang, config.APIKEY);
2701
+ try {
2702
+ addCompletedOnboarding();
2703
+ } catch (error) {
2704
+ console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
2705
+ }
2706
+ return true;
2707
+ } catch (error) {
2708
+ if (error.name === "ExitPromptError") {
2709
+ console.log(ansis.yellow(i18n.common.cancelled));
2710
+ return false;
2711
+ }
2712
+ console.error(ansis.red(`${i18n.ccr.ccrConfigFailed}:`), error);
2713
+ return false;
2714
+ }
2715
+ }
2716
+ async function configureCcrFeature(scriptLang) {
2717
+ const i18n = getTranslation(scriptLang);
2718
+ const backupDir = backupExistingConfig();
2719
+ if (backupDir) {
2720
+ console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
2721
+ }
2722
+ await setupCcrConfiguration(scriptLang);
2723
+ }
2724
+
2026
2725
  async function init(options = {}) {
2027
2726
  try {
2028
2727
  if (!options.skipBanner) {
@@ -2032,22 +2731,22 @@ async function init(options = {}) {
2032
2731
  const i18n = I18N[scriptLang];
2033
2732
  if (isTermux()) {
2034
2733
  console.log(ansis.yellow(`
2035
- \u2139 ${i18n.termuxDetected}`));
2036
- console.log(ansis.gray(i18n.termuxEnvironmentInfo));
2734
+ \u2139 ${i18n.installation.termuxDetected}`));
2735
+ console.log(ansis.gray(i18n.installation.termuxEnvironmentInfo));
2037
2736
  }
2038
2737
  let configLang = options.configLang;
2039
2738
  if (!configLang) {
2040
2739
  const { lang } = await inquirer.prompt({
2041
2740
  type: "list",
2042
2741
  name: "lang",
2043
- message: i18n.selectConfigLang,
2044
- choices: SUPPORTED_LANGS.map((l) => ({
2045
- name: `${LANG_LABELS[l]} - ${i18n.configLangHint[l]}`,
2742
+ message: i18n.language.selectConfigLang,
2743
+ choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
2744
+ name: `${LANG_LABELS[l]} - ${i18n.language.configLangHint[l]}`,
2046
2745
  value: l
2047
- }))
2746
+ })))
2048
2747
  });
2049
2748
  if (!lang) {
2050
- console.log(ansis.yellow(i18n.cancelled));
2749
+ console.log(ansis.yellow(i18n.common.cancelled));
2051
2750
  process.exit(0);
2052
2751
  }
2053
2752
  configLang = lang;
@@ -2059,20 +2758,20 @@ async function init(options = {}) {
2059
2758
  const { shouldInstall } = await inquirer.prompt({
2060
2759
  type: "confirm",
2061
2760
  name: "shouldInstall",
2062
- message: i18n.installPrompt,
2761
+ message: i18n.installation.installPrompt,
2063
2762
  default: true
2064
2763
  });
2065
2764
  if (shouldInstall === void 0) {
2066
- console.log(ansis.yellow(i18n.cancelled));
2765
+ console.log(ansis.yellow(i18n.common.cancelled));
2067
2766
  process.exit(0);
2068
2767
  }
2069
2768
  if (shouldInstall) {
2070
2769
  await installClaudeCode(scriptLang);
2071
2770
  } else {
2072
- console.log(ansis.yellow(i18n.skip));
2771
+ console.log(ansis.yellow(i18n.common.skip));
2073
2772
  }
2074
2773
  } else {
2075
- console.log(ansis.green(`\u2714 ${i18n.installSuccess}`));
2774
+ console.log(ansis.green(`\u2714 ${i18n.installation.alreadyInstalled}`));
2076
2775
  }
2077
2776
  ensureClaudeDir();
2078
2777
  let action = "new";
@@ -2080,21 +2779,21 @@ async function init(options = {}) {
2080
2779
  const { action: userAction } = await inquirer.prompt({
2081
2780
  type: "list",
2082
2781
  name: "action",
2083
- message: i18n.existingConfig,
2084
- choices: [
2085
- { name: i18n.backupAndOverwrite, value: "backup" },
2086
- { name: i18n.updateDocsOnly, value: "docs-only" },
2087
- { name: i18n.mergeConfig, value: "merge" },
2088
- { name: i18n.skip, value: "skip" }
2089
- ]
2782
+ message: i18n.configuration.existingConfig,
2783
+ choices: addNumbersToChoices([
2784
+ { name: i18n.configuration.backupAndOverwrite, value: "backup" },
2785
+ { name: i18n.configuration.updateDocsOnly, value: "docs-only" },
2786
+ { name: i18n.configuration.mergeConfig, value: "merge" },
2787
+ { name: i18n.common.skip, value: "skip" }
2788
+ ])
2090
2789
  });
2091
2790
  if (!userAction) {
2092
- console.log(ansis.yellow(i18n.cancelled));
2791
+ console.log(ansis.yellow(i18n.common.cancelled));
2093
2792
  process.exit(0);
2094
2793
  }
2095
2794
  action = userAction;
2096
2795
  if (action === "skip") {
2097
- console.log(ansis.yellow(i18n.skip));
2796
+ console.log(ansis.yellow(i18n.common.skip));
2098
2797
  return;
2099
2798
  }
2100
2799
  }
@@ -2103,65 +2802,103 @@ async function init(options = {}) {
2103
2802
  if (action !== "docs-only" && (isNewInstall || ["backup", "merge"].includes(action))) {
2104
2803
  const existingApiConfig = getExistingApiConfig();
2105
2804
  if (existingApiConfig) {
2106
- console.log("\n" + ansis.blue(`\u2139 ${i18n.existingApiConfig}`));
2107
- console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${existingApiConfig.url || i18n.notConfigured}`));
2805
+ console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
2806
+ console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
2108
2807
  console.log(
2109
2808
  ansis.gray(
2110
- ` ${i18n.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.notConfigured}`
2809
+ ` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
2111
2810
  )
2112
2811
  );
2113
- console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${existingApiConfig.authType || i18n.notConfigured}
2812
+ console.log(ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
2114
2813
  `));
2115
2814
  const { action: apiAction } = await inquirer.prompt({
2116
2815
  type: "list",
2117
2816
  name: "action",
2118
- message: i18n.selectApiAction,
2119
- choices: [
2120
- { name: i18n.keepExistingConfig, value: "keep" },
2121
- { name: i18n.modifyAllConfig, value: "modify-all" },
2122
- { name: i18n.modifyPartialConfig, value: "modify-partial" },
2123
- { name: i18n.skipApi, value: "skip" }
2124
- ]
2817
+ message: i18n.api.selectApiAction,
2818
+ choices: addNumbersToChoices([
2819
+ { name: i18n.api.keepExistingConfig, value: "keep" },
2820
+ { name: i18n.api.modifyAllConfig, value: "modify-all" },
2821
+ { name: i18n.api.modifyPartialConfig, value: "modify-partial" },
2822
+ { name: i18n.api.useCcrProxy, value: "use-ccr" },
2823
+ { name: i18n.api.skipApi, value: "skip" }
2824
+ ])
2125
2825
  });
2126
2826
  if (!apiAction) {
2127
- console.log(ansis.yellow(i18n.cancelled));
2827
+ console.log(ansis.yellow(i18n.common.cancelled));
2128
2828
  process.exit(0);
2129
2829
  }
2130
2830
  if (apiAction === "keep" || apiAction === "skip") {
2131
2831
  apiConfig = null;
2832
+ if (apiAction === "keep") {
2833
+ try {
2834
+ addCompletedOnboarding();
2835
+ } catch (error) {
2836
+ console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
2837
+ }
2838
+ }
2132
2839
  } else if (apiAction === "modify-partial") {
2133
2840
  await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
2134
2841
  apiConfig = null;
2135
2842
  } else if (apiAction === "modify-all") {
2136
2843
  apiConfig = await configureApiCompletely(i18n, scriptLang);
2844
+ } else if (apiAction === "use-ccr") {
2845
+ const ccrInstalled = await isCcrInstalled();
2846
+ if (!ccrInstalled) {
2847
+ console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
2848
+ await installCcr(scriptLang);
2849
+ } else {
2850
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
2851
+ }
2852
+ const ccrConfigured = await setupCcrConfiguration(scriptLang);
2853
+ if (ccrConfigured) {
2854
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
2855
+ apiConfig = null;
2856
+ }
2137
2857
  }
2138
2858
  } else {
2139
2859
  const { apiChoice } = await inquirer.prompt({
2140
2860
  type: "list",
2141
2861
  name: "apiChoice",
2142
- message: i18n.configureApi,
2862
+ message: i18n.api.configureApi,
2143
2863
  choices: [
2144
2864
  {
2145
- name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
2865
+ name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
2146
2866
  value: "auth_token",
2147
- short: i18n.useAuthToken
2867
+ short: i18n.api.useAuthToken
2148
2868
  },
2149
2869
  {
2150
- name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
2870
+ name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
2151
2871
  value: "api_key",
2152
- short: i18n.useApiKey
2872
+ short: i18n.api.useApiKey
2153
2873
  },
2154
2874
  {
2155
- name: i18n.skipApi,
2875
+ name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
2876
+ value: "ccr_proxy",
2877
+ short: i18n.api.useCcrProxy
2878
+ },
2879
+ {
2880
+ name: i18n.api.skipApi,
2156
2881
  value: "skip"
2157
2882
  }
2158
2883
  ]
2159
2884
  });
2160
2885
  if (!apiChoice) {
2161
- console.log(ansis.yellow(i18n.cancelled));
2886
+ console.log(ansis.yellow(i18n.common.cancelled));
2162
2887
  process.exit(0);
2163
2888
  }
2164
- if (apiChoice !== "skip") {
2889
+ if (apiChoice === "ccr_proxy") {
2890
+ const ccrInstalled = await isCcrInstalled();
2891
+ if (!ccrInstalled) {
2892
+ await installCcr(scriptLang);
2893
+ } else {
2894
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
2895
+ }
2896
+ const ccrConfigured = await setupCcrConfiguration(scriptLang);
2897
+ if (ccrConfigured) {
2898
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
2899
+ apiConfig = null;
2900
+ }
2901
+ } else if (apiChoice !== "skip") {
2165
2902
  apiConfig = await configureApiCompletely(i18n, scriptLang, apiChoice);
2166
2903
  }
2167
2904
  }
@@ -2169,7 +2906,7 @@ async function init(options = {}) {
2169
2906
  if (["backup", "docs-only", "merge"].includes(action)) {
2170
2907
  const backupDir = backupExistingConfig();
2171
2908
  if (backupDir) {
2172
- console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
2909
+ console.log(ansis.gray(`\u2714 ${i18n.configuration.backupSuccess}: ${backupDir}`));
2173
2910
  }
2174
2911
  }
2175
2912
  if (action === "docs-only") {
@@ -2184,30 +2921,25 @@ async function init(options = {}) {
2184
2921
  if (apiConfig && action !== "docs-only") {
2185
2922
  const configuredApi = configureApi(apiConfig);
2186
2923
  if (configuredApi) {
2187
- console.log(ansis.green(`\u2714 ${i18n.apiConfigSuccess}`));
2924
+ console.log(ansis.green(`\u2714 ${i18n.api.apiConfigSuccess}`));
2188
2925
  console.log(ansis.gray(` URL: ${configuredApi.url}`));
2189
2926
  console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
2190
- try {
2191
- addCompletedOnboarding();
2192
- } catch (error) {
2193
- console.error(ansis.red(i18n.failedToSetOnboarding), error);
2194
- }
2195
2927
  }
2196
2928
  }
2197
2929
  if (action !== "docs-only") {
2198
2930
  const { shouldConfigureMcp } = await inquirer.prompt({
2199
2931
  type: "confirm",
2200
2932
  name: "shouldConfigureMcp",
2201
- message: i18n.configureMcp,
2933
+ message: i18n.mcp.configureMcp,
2202
2934
  default: true
2203
2935
  });
2204
2936
  if (shouldConfigureMcp === void 0) {
2205
- console.log(ansis.yellow(i18n.cancelled));
2937
+ console.log(ansis.yellow(i18n.common.cancelled));
2206
2938
  process.exit(0);
2207
2939
  }
2208
2940
  if (shouldConfigureMcp) {
2209
2941
  if (isWindows()) {
2210
- console.log(ansis.blue(`\u2139 ${I18N[scriptLang].windowsDetected}`));
2942
+ console.log(ansis.blue(`\u2139 ${i18n.installation.windowsDetected}`));
2211
2943
  }
2212
2944
  const selectedServices = await selectMcpServices(scriptLang);
2213
2945
  if (selectedServices === void 0) {
@@ -2216,7 +2948,7 @@ async function init(options = {}) {
2216
2948
  if (selectedServices.length > 0) {
2217
2949
  const mcpBackupPath = backupMcpConfig();
2218
2950
  if (mcpBackupPath) {
2219
- console.log(ansis.gray(`\u2714 ${i18n.mcpBackupSuccess}: ${mcpBackupPath}`));
2951
+ console.log(ansis.gray(`\u2714 ${i18n.mcp.mcpBackupSuccess}: ${mcpBackupPath}`));
2220
2952
  }
2221
2953
  const newServers = {};
2222
2954
  for (const serviceId of selectedServices) {
@@ -2228,10 +2960,10 @@ async function init(options = {}) {
2228
2960
  type: "input",
2229
2961
  name: "apiKey",
2230
2962
  message: service.apiKeyPrompt[scriptLang],
2231
- validate: (value) => !!value || i18n.keyRequired
2963
+ validate: (value) => !!value || i18n.api.keyRequired
2232
2964
  });
2233
2965
  if (apiKey === void 0) {
2234
- console.log(ansis.yellow(`${i18n.skip}: ${service.name[scriptLang]}`));
2966
+ console.log(ansis.yellow(`${i18n.common.skip}: ${service.name[scriptLang]}`));
2235
2967
  continue;
2236
2968
  }
2237
2969
  if (apiKey) {
@@ -2247,9 +2979,9 @@ async function init(options = {}) {
2247
2979
  mergedConfig = fixWindowsMcpConfig(mergedConfig);
2248
2980
  try {
2249
2981
  writeMcpConfig(mergedConfig);
2250
- console.log(ansis.green(`\u2714 ${i18n.mcpConfigSuccess}`));
2982
+ console.log(ansis.green(`\u2714 ${i18n.mcp.mcpConfigSuccess}`));
2251
2983
  } catch (error) {
2252
- console.error(ansis.red(`${i18n.failedToWriteMcpConfig} ${error}`));
2984
+ console.error(ansis.red(`${i18n.configuration.failedToWriteMcpConfig} ${error}`));
2253
2985
  }
2254
2986
  }
2255
2987
  }
@@ -2259,8 +2991,8 @@ async function init(options = {}) {
2259
2991
  preferredLang: scriptLang,
2260
2992
  aiOutputLang
2261
2993
  });
2262
- console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
2263
- console.log("\n" + ansis.cyan(i18n.complete));
2994
+ console.log(ansis.green(`\u2714 ${i18n.configuration.configSuccess} ${CLAUDE_DIR}`));
2995
+ console.log("\n" + ansis.cyan(i18n.common.complete));
2264
2996
  } catch (error) {
2265
2997
  if (!handleExitPromptError(error)) {
2266
2998
  handleGeneralError(error, options.lang);
@@ -2346,4 +3078,4 @@ async function openSettingsJson() {
2346
3078
  }
2347
3079
  }
2348
3080
 
2349
- export { handleExitPromptError as $, AI_OUTPUT_LANGUAGES as A, backupMcpConfig as B, CLAUDE_DIR as C, mergeMcpServers as D, buildMcpServerConfig as E, fixWindowsMcpConfig as F, addCompletedOnboarding as G, updateZcfConfig as H, I18N as I, readZcfConfig as J, resolveAiOutputLanguage as K, LEGACY_ZCF_CONFIG_FILE as L, MCP_SERVICES as M, configureAiPersonality as N, isWindows as O, selectMcpServices as P, formatApiKeyDisplay as Q, modifyApiConfigPartially as R, SETTINGS_FILE as S, validateApiKey as T, readZcfConfigAsync as U, displayBanner as V, selectScriptLanguage as W, updatePromptOnly as X, selectAndInstallWorkflows as Y, ZCF_CONFIG_FILE as Z, version as _, importRecommendedEnv as a, handleGeneralError as a0, displayBannerWithInfo as a1, importRecommendedPermissions as b, commandExists as c, cleanupPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, SUPPORTED_LANGS as h, init as i, LANG_LABELS as j, isClaudeCodeInstalled as k, installClaudeCode as l, mergeAndCleanPermissions as m, ensureClaudeDir as n, openSettingsJson as o, backupExistingConfig as p, copyConfigFiles as q, configureApi as r, mergeConfigs as s, mergeSettingsFile as t, updateDefaultModel as u, getExistingApiConfig as v, applyAiLanguageDirective as w, getMcpConfigPath as x, readMcpConfig as y, writeMcpConfig as z };
3081
+ export { readCcrConfig as $, AI_OUTPUT_LANGUAGES as A, backupMcpConfig as B, CLAUDE_DIR as C, mergeMcpServers as D, buildMcpServerConfig as E, fixWindowsMcpConfig as F, addCompletedOnboarding as G, getTranslation as H, I18N as I, addNumbersToChoices as J, updateZcfConfig as K, LEGACY_ZCF_CONFIG_FILE as L, MCP_SERVICES as M, readZcfConfig as N, resolveAiOutputLanguage as O, configureAiPersonality as P, isWindows as Q, selectMcpServices as R, SETTINGS_FILE as S, formatApiKeyDisplay as T, modifyApiConfigPartially as U, isCcrInstalled as V, installCcr as W, setupCcrConfiguration as X, validateApiKey as Y, ZCF_CONFIG_FILE as Z, readZcfConfigAsync as _, importRecommendedEnv as a, configureCcrFeature as a0, handleExitPromptError as a1, handleGeneralError as a2, displayBanner as a3, selectScriptLanguage as a4, updatePromptOnly as a5, selectAndInstallWorkflows as a6, version as a7, displayBannerWithInfo as a8, importRecommendedPermissions as b, commandExists as c, cleanupPermissions as d, CLAUDE_MD_FILE as e, ClAUDE_CONFIG_FILE as f, getPlatform as g, SUPPORTED_LANGS as h, init as i, LANG_LABELS as j, isClaudeCodeInstalled as k, installClaudeCode as l, mergeAndCleanPermissions as m, ensureClaudeDir as n, openSettingsJson as o, backupExistingConfig as p, copyConfigFiles as q, configureApi as r, mergeConfigs as s, mergeSettingsFile as t, updateDefaultModel as u, getExistingApiConfig as v, applyAiLanguageDirective as w, getMcpConfigPath as x, readMcpConfig as y, writeMcpConfig as z };