zcf 2.7.1 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,82 +1,138 @@
1
1
  #!/usr/bin/env node
2
2
  import cac from 'cac';
3
3
  import ansis from 'ansis';
4
- import { I as I18N, Z as ZCF_CONFIG_FILE, h as SUPPORTED_LANGS, j as LANG_LABELS, H as updateZcfConfig, o as openSettingsJson, b as importRecommendedPermissions, a as importRecommendedEnv, J as readZcfConfig, K as resolveAiOutputLanguage, w as applyAiLanguageDirective, N as configureAiPersonality, u as updateDefaultModel, O as isWindows, y as readMcpConfig, F as fixWindowsMcpConfig, z as writeMcpConfig, P as selectMcpServices, B as backupMcpConfig, M as MCP_SERVICES, E as buildMcpServerConfig, D as mergeMcpServers, v as getExistingApiConfig, Q as formatApiKeyDisplay, R as modifyApiConfigPartially, T as validateApiKey, r as configureApi, U as readZcfConfigAsync, V as displayBanner, W as selectScriptLanguage, X as updatePromptOnly, Y as selectAndInstallWorkflows, _ as version, $ as handleExitPromptError, a0 as handleGeneralError, a1 as displayBannerWithInfo, i as init } from './shared/zcf.CZ3RjfyP.mjs';
4
+ import { J as getTranslation, Z as ZCF_CONFIG_FILE, h as SUPPORTED_LANGS, K as addNumbersToChoices, j as LANG_LABELS, N as updateZcfConfig, o as openSettingsJson, b as importRecommendedPermissions, a as importRecommendedEnv, O as readZcfConfig, x as applyAiLanguageDirective, P as configureAiPersonality, v as getExistingModelConfig, u as updateDefaultModel, Q as isWindows, z as readMcpConfig, G as fixWindowsMcpConfig, B as writeMcpConfig, R as selectMcpServices, D as backupMcpConfig, M as MCP_SERVICES, F as buildMcpServerConfig, E as mergeMcpServers, w as getExistingApiConfig, T as formatApiKeyDisplay, H as addCompletedOnboarding, U as modifyApiConfigPartially, V as isCcrInstalled, W as installCcr, X as setupCcrConfiguration, Y as validateApiKey, r as configureApi, _ as readZcfConfigAsync, I as I18N, $ as readCcrConfig, a0 as configureCcrFeature, a1 as handleExitPromptError, a2 as handleGeneralError, a3 as displayBanner, a4 as selectScriptLanguage, a5 as resolveAiOutputLanguage, a6 as updatePromptOnly, a7 as selectAndInstallWorkflows, a8 as version, a9 as displayBannerWithInfo, i as init, aa as checkAndUpdateTools } from './chunks/simple-config.mjs';
5
5
  import inquirer from 'inquirer';
6
6
  import { existsSync, unlinkSync } from 'node:fs';
7
7
  import { x } from 'tinyexec';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { exec } from 'child_process';
11
+ import { promisify } from 'util';
8
12
  import 'pathe';
9
13
  import 'dayjs';
10
14
  import 'node:url';
15
+ import 'ora';
16
+ import 'prompts';
17
+ import 'fs/promises';
18
+ import 'path';
19
+ import 'os';
20
+ import 'semver';
11
21
  import 'node:fs/promises';
12
- import 'node:os';
22
+ import 'node:child_process';
23
+ import 'node:util';
13
24
 
14
25
  function handleCancellation(scriptLang) {
15
- console.log(ansis.yellow(I18N[scriptLang].cancelled));
26
+ const i18n = getTranslation(scriptLang);
27
+ console.log(ansis.yellow(i18n.common.cancelled));
16
28
  }
17
29
  async function configureApiFeature(scriptLang) {
18
- const i18n = I18N[scriptLang];
30
+ const i18n = getTranslation(scriptLang);
19
31
  const existingApiConfig = getExistingApiConfig();
20
32
  if (existingApiConfig) {
21
- console.log("\n" + ansis.blue(`\u2139 ${i18n.existingApiConfig}`));
22
- console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${existingApiConfig.url || i18n.notConfigured}`));
23
- console.log(ansis.gray(` ${i18n.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.notConfigured}`));
24
- console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${existingApiConfig.authType || i18n.notConfigured}
25
- `));
33
+ console.log("\n" + ansis.blue(`\u2139 ${i18n.api.existingApiConfig}`));
34
+ console.log(ansis.gray(` ${i18n.api.apiConfigUrl}: ${existingApiConfig.url || i18n.common.notConfigured}`));
35
+ console.log(
36
+ ansis.gray(
37
+ ` ${i18n.api.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.common.notConfigured}`
38
+ )
39
+ );
40
+ console.log(
41
+ ansis.gray(` ${i18n.api.apiConfigAuthType}: ${existingApiConfig.authType || i18n.common.notConfigured}
42
+ `)
43
+ );
26
44
  const { action } = await inquirer.prompt({
27
45
  type: "list",
28
46
  name: "action",
29
- message: i18n.selectApiAction,
30
- choices: [
31
- { name: i18n.keepExistingConfig, value: "keep" },
32
- { name: i18n.modifyAllConfig, value: "modify-all" },
33
- { name: i18n.modifyPartialConfig, value: "modify-partial" }
34
- ]
47
+ message: i18n.api.selectApiAction,
48
+ choices: addNumbersToChoices([
49
+ { name: i18n.api.keepExistingConfig, value: "keep" },
50
+ { name: i18n.api.modifyAllConfig, value: "modify-all" },
51
+ { name: i18n.api.modifyPartialConfig, value: "modify-partial" },
52
+ { name: i18n.api.useCcrProxy, value: "use-ccr" }
53
+ ])
35
54
  });
36
55
  if (!action) {
37
56
  handleCancellation(scriptLang);
38
57
  return;
39
58
  }
40
59
  if (action === "keep") {
41
- console.log(ansis.green(`\u2714 ${i18n.keepExistingConfig}`));
60
+ console.log(ansis.green(`\u2714 ${i18n.api.keepExistingConfig}`));
61
+ try {
62
+ addCompletedOnboarding();
63
+ } catch (error) {
64
+ console.error(ansis.red(i18n.configuration.failedToSetOnboarding), error);
65
+ }
42
66
  return;
43
67
  } else if (action === "modify-partial") {
44
68
  await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
45
69
  return;
70
+ } else if (action === "use-ccr") {
71
+ const ccrInstalled = await isCcrInstalled();
72
+ if (!ccrInstalled) {
73
+ console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
74
+ await installCcr(scriptLang);
75
+ } else {
76
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
77
+ }
78
+ const ccrConfigured = await setupCcrConfiguration(scriptLang);
79
+ if (ccrConfigured) {
80
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
81
+ }
82
+ return;
46
83
  }
47
84
  }
48
85
  const { apiChoice } = await inquirer.prompt({
49
86
  type: "list",
50
87
  name: "apiChoice",
51
- message: i18n.configureApi,
52
- choices: [
88
+ message: i18n.api.configureApi,
89
+ choices: addNumbersToChoices([
53
90
  {
54
- name: `${i18n.useAuthToken} - ${ansis.gray(i18n.authTokenDesc)}`,
91
+ name: `${i18n.api.useAuthToken} - ${ansis.gray(i18n.api.authTokenDesc)}`,
55
92
  value: "auth_token",
56
- short: i18n.useAuthToken
93
+ short: i18n.api.useAuthToken
57
94
  },
58
95
  {
59
- name: `${i18n.useApiKey} - ${ansis.gray(i18n.apiKeyDesc)}`,
96
+ name: `${i18n.api.useApiKey} - ${ansis.gray(i18n.api.apiKeyDesc)}`,
60
97
  value: "api_key",
61
- short: i18n.useApiKey
98
+ short: i18n.api.useApiKey
99
+ },
100
+ {
101
+ name: `${i18n.api.useCcrProxy} - ${ansis.gray(i18n.api.ccrProxyDesc)}`,
102
+ value: "ccr_proxy",
103
+ short: i18n.api.useCcrProxy
62
104
  },
63
- { name: i18n.skipApi, value: "skip" }
64
- ]
105
+ { name: i18n.api.skipApi, value: "skip" }
106
+ ])
65
107
  });
66
108
  if (!apiChoice || apiChoice === "skip") {
67
109
  return;
68
110
  }
111
+ if (apiChoice === "ccr_proxy") {
112
+ const ccrInstalled = await isCcrInstalled();
113
+ if (!ccrInstalled) {
114
+ console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
115
+ await installCcr(scriptLang);
116
+ } else {
117
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
118
+ }
119
+ const ccrConfigured = await setupCcrConfiguration(scriptLang);
120
+ if (ccrConfigured) {
121
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrSetupComplete}`));
122
+ }
123
+ return;
124
+ }
69
125
  const { url } = await inquirer.prompt({
70
126
  type: "input",
71
127
  name: "url",
72
- message: i18n.enterApiUrl,
128
+ message: i18n.api.enterApiUrl,
73
129
  validate: (value) => {
74
- if (!value) return i18n.urlRequired;
130
+ if (!value) return i18n.api.urlRequired;
75
131
  try {
76
132
  new URL(value);
77
133
  return true;
78
134
  } catch {
79
- return i18n.invalidUrl;
135
+ return i18n.api.invalidUrl;
80
136
  }
81
137
  }
82
138
  });
@@ -84,18 +140,18 @@ async function configureApiFeature(scriptLang) {
84
140
  handleCancellation(scriptLang);
85
141
  return;
86
142
  }
87
- const keyMessage = apiChoice === "auth_token" ? i18n.enterAuthToken : i18n.enterApiKey;
143
+ const keyMessage = apiChoice === "auth_token" ? i18n.api.enterAuthToken : i18n.api.enterApiKey;
88
144
  const { key } = await inquirer.prompt({
89
145
  type: "input",
90
146
  name: "key",
91
147
  message: keyMessage,
92
148
  validate: (value) => {
93
149
  if (!value) {
94
- return i18n.keyRequired;
150
+ return i18n.api.keyRequired;
95
151
  }
96
152
  const validation = validateApiKey(value, scriptLang);
97
153
  if (!validation.isValid) {
98
- return validation.error || i18n.invalidKeyFormat;
154
+ return validation.error || i18n.api.invalidKeyFormat;
99
155
  }
100
156
  return true;
101
157
  }
@@ -107,25 +163,25 @@ async function configureApiFeature(scriptLang) {
107
163
  const apiConfig = { url, key, authType: apiChoice };
108
164
  const configuredApi = configureApi(apiConfig);
109
165
  if (configuredApi) {
110
- console.log(ansis.green(`\u2714 ${i18n.apiConfigSuccess}`));
166
+ console.log(ansis.green(`\u2714 ${i18n.api.apiConfigSuccess}`));
111
167
  console.log(ansis.gray(` URL: ${configuredApi.url}`));
112
168
  console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
113
169
  }
114
170
  }
115
171
  async function configureMcpFeature(scriptLang) {
116
- const i18n = I18N[scriptLang];
172
+ const i18n = getTranslation(scriptLang);
117
173
  if (isWindows()) {
118
174
  const { fixWindows } = await inquirer.prompt({
119
175
  type: "confirm",
120
176
  name: "fixWindows",
121
- message: i18n.fixWindowsMcp || "Fix Windows MCP configuration?",
177
+ message: i18n.configuration.fixWindowsMcp || "Fix Windows MCP configuration?",
122
178
  default: true
123
179
  });
124
180
  if (fixWindows) {
125
181
  const existingConfig = readMcpConfig() || { mcpServers: {} };
126
182
  const fixedConfig = fixWindowsMcpConfig(existingConfig);
127
183
  writeMcpConfig(fixedConfig);
128
- console.log(ansis.green(`\u2714 ${i18n.windowsMcpFixed || "Windows MCP configuration fixed"}`));
184
+ console.log(ansis.green(`\u2714 Windows MCP configuration fixed`));
129
185
  }
130
186
  }
131
187
  const selectedServices = await selectMcpServices(scriptLang);
@@ -135,7 +191,7 @@ async function configureMcpFeature(scriptLang) {
135
191
  if (selectedServices.length > 0) {
136
192
  const mcpBackupPath = backupMcpConfig();
137
193
  if (mcpBackupPath) {
138
- console.log(ansis.gray(`\u2714 ${i18n.mcpBackupSuccess}: ${mcpBackupPath}`));
194
+ console.log(ansis.gray(`\u2714 ${i18n.mcp.mcpBackupSuccess}: ${mcpBackupPath}`));
139
195
  }
140
196
  const newServers = {};
141
197
  for (const serviceId of selectedServices) {
@@ -147,7 +203,7 @@ async function configureMcpFeature(scriptLang) {
147
203
  type: "input",
148
204
  name: "apiKey",
149
205
  message: service.apiKeyPrompt[scriptLang],
150
- validate: (value) => !!value || i18n.keyRequired
206
+ validate: (value) => !!value || i18n.api.keyRequired
151
207
  });
152
208
  if (apiKey) {
153
209
  config = buildMcpServerConfig(service.config, apiKey, service.apiKeyPlaceholder, service.apiKeyEnvVar);
@@ -161,63 +217,104 @@ async function configureMcpFeature(scriptLang) {
161
217
  let mergedConfig = mergeMcpServers(existingConfig, newServers);
162
218
  mergedConfig = fixWindowsMcpConfig(mergedConfig);
163
219
  writeMcpConfig(mergedConfig);
164
- console.log(ansis.green(`\u2714 ${i18n.mcpConfigSuccess}`));
220
+ console.log(ansis.green(`\u2714 ${i18n.mcp.mcpConfigSuccess}`));
165
221
  }
166
222
  }
167
223
  async function configureDefaultModelFeature(scriptLang) {
168
- const i18n = I18N[scriptLang];
224
+ const i18n = getTranslation(scriptLang);
225
+ const existingModel = getExistingModelConfig();
226
+ if (existingModel) {
227
+ console.log("\n" + ansis.blue(`\u2139 ${i18n.configuration.existingModelConfig || "Existing model configuration"}`));
228
+ const modelDisplay = existingModel === "default" ? i18n.configuration.defaultModelOption || "Default (Let Claude Code choose)" : existingModel.charAt(0).toUpperCase() + existingModel.slice(1);
229
+ console.log(ansis.gray(` ${i18n.configuration.currentModel || "Current model"}: ${modelDisplay}
230
+ `));
231
+ const { modify } = await inquirer.prompt({
232
+ type: "confirm",
233
+ name: "modify",
234
+ message: i18n.configuration.modifyModel || "Modify model configuration?",
235
+ default: false
236
+ });
237
+ if (!modify) {
238
+ console.log(ansis.green(`\u2714 ${i18n.configuration.keepModel || "Keeping existing model configuration"}`));
239
+ return;
240
+ }
241
+ }
169
242
  const { model } = await inquirer.prompt({
170
243
  type: "list",
171
244
  name: "model",
172
- message: i18n.selectDefaultModel || "Select default model",
173
- choices: [
245
+ message: i18n.configuration.selectDefaultModel || "Select default model",
246
+ choices: addNumbersToChoices([
247
+ {
248
+ name: i18n.configuration.defaultModelOption || "Default (Let Claude Code choose)",
249
+ value: "default"
250
+ },
174
251
  { name: "Opus", value: "opus" },
175
252
  { name: "Sonnet", value: "sonnet" }
176
- ]
253
+ ]),
254
+ default: existingModel ? ["opus", "sonnet", "default"].indexOf(existingModel) : 2
177
255
  });
178
256
  if (!model) {
179
257
  handleCancellation(scriptLang);
180
258
  return;
181
259
  }
182
260
  updateDefaultModel(model);
183
- console.log(ansis.green(`\u2714 ${i18n.modelConfigSuccess || "Default model configured"}`));
261
+ console.log(ansis.green(`\u2714 ${i18n.configuration.modelConfigured || "Default model configured"}`));
184
262
  }
185
263
  async function configureAiMemoryFeature(scriptLang) {
186
- const i18n = I18N[scriptLang];
264
+ const i18n = getTranslation(scriptLang);
187
265
  const { option } = await inquirer.prompt({
188
266
  type: "list",
189
267
  name: "option",
190
- message: i18n.selectMemoryOption || "Select configuration option",
191
- choices: [
268
+ message: "Select configuration option",
269
+ choices: addNumbersToChoices([
192
270
  {
193
- name: i18n.configureAiLanguage || "Configure AI output language",
271
+ name: i18n.configuration.configureAiLanguage || "Configure AI output language",
194
272
  value: "language"
195
273
  },
196
274
  {
197
- name: i18n.configureAiPersonality || "Configure AI personality",
275
+ name: i18n.configuration.configureAiPersonality || "Configure AI personality",
198
276
  value: "personality"
199
277
  }
200
- ]
278
+ ])
201
279
  });
202
280
  if (!option) {
203
281
  return;
204
282
  }
205
283
  if (option === "language") {
206
284
  const zcfConfig = readZcfConfig();
207
- const aiOutputLang = await resolveAiOutputLanguage(scriptLang, void 0, zcfConfig);
285
+ const existingLang = zcfConfig?.aiOutputLang;
286
+ if (existingLang) {
287
+ console.log(
288
+ "\n" + ansis.blue(`\u2139 ${i18n.configuration.existingLanguageConfig || "Existing AI output language configuration"}`)
289
+ );
290
+ console.log(ansis.gray(` ${i18n.configuration.currentLanguage || "Current language"}: ${existingLang}
291
+ `));
292
+ const { modify } = await inquirer.prompt({
293
+ type: "confirm",
294
+ name: "modify",
295
+ message: i18n.configuration.modifyLanguage || "Modify AI output language?",
296
+ default: false
297
+ });
298
+ if (!modify) {
299
+ console.log(ansis.green(`\u2714 ${i18n.configuration.keepLanguage || "Keeping existing language configuration"}`));
300
+ return;
301
+ }
302
+ }
303
+ const { selectAiOutputLanguage } = await import('./chunks/simple-config.mjs').then(function (n) { return n.ab; });
304
+ const aiOutputLang = await selectAiOutputLanguage(scriptLang, scriptLang);
208
305
  applyAiLanguageDirective(aiOutputLang);
209
306
  updateZcfConfig({ aiOutputLang });
210
- console.log(ansis.green(`\u2714 ${i18n.aiLanguageConfigured || "AI output language configured"}`));
307
+ console.log(ansis.green(`\u2714 ${i18n.configuration.aiLanguageConfigured || "AI output language configured"}`));
211
308
  } else {
212
309
  await configureAiPersonality(scriptLang);
213
310
  }
214
311
  }
215
312
  async function clearZcfCacheFeature(scriptLang) {
216
- const i18n = I18N[scriptLang];
313
+ const i18n = getTranslation(scriptLang);
217
314
  const { confirm } = await inquirer.prompt({
218
315
  type: "confirm",
219
316
  name: "confirm",
220
- message: i18n.confirmClearCache || "Clear all ZCF preferences cache?",
317
+ message: i18n.configuration.confirmClearCache || "Clear all ZCF preferences cache?",
221
318
  default: false
222
319
  });
223
320
  if (!confirm) {
@@ -226,50 +323,59 @@ async function clearZcfCacheFeature(scriptLang) {
226
323
  }
227
324
  if (existsSync(ZCF_CONFIG_FILE)) {
228
325
  unlinkSync(ZCF_CONFIG_FILE);
229
- console.log(ansis.green(`\u2714 ${i18n.cacheCleared || "ZCF cache cleared"}`));
326
+ console.log(ansis.green(`\u2714 ${i18n.configuration.cacheCleared || "ZCF cache cleared"}`));
230
327
  } else {
231
- console.log(ansis.yellow(i18n.noCacheFound || "No cache found"));
328
+ console.log(ansis.yellow("No cache found"));
232
329
  }
233
330
  }
234
331
  async function changeScriptLanguageFeature(currentLang) {
235
- const i18n = I18N[currentLang];
332
+ const i18n = getTranslation(currentLang);
236
333
  const { lang } = await inquirer.prompt({
237
334
  type: "list",
238
335
  name: "lang",
239
- message: i18n.selectScriptLang,
240
- choices: SUPPORTED_LANGS.map((l) => ({
241
- name: LANG_LABELS[l],
242
- value: l
243
- })),
336
+ message: i18n.language.selectScriptLang,
337
+ choices: addNumbersToChoices(
338
+ SUPPORTED_LANGS.map((l) => ({
339
+ name: LANG_LABELS[l],
340
+ value: l
341
+ }))
342
+ ),
244
343
  default: SUPPORTED_LANGS.indexOf(currentLang)
245
344
  });
246
345
  if (!lang) {
247
346
  return currentLang;
248
347
  }
249
348
  updateZcfConfig({ preferredLang: lang });
250
- console.log(ansis.green(`\u2714 ${I18N[lang].languageChanged || "Language changed"}`));
349
+ const newI18n = getTranslation(lang);
350
+ console.log(ansis.green(`\u2714 ${newI18n.language.languageChanged || "Language changed"}`));
251
351
  return lang;
252
352
  }
253
353
  async function configureEnvPermissionFeature(scriptLang) {
254
- const i18n = I18N[scriptLang];
354
+ const i18n = getTranslation(scriptLang);
255
355
  const { choice } = await inquirer.prompt({
256
356
  type: "list",
257
357
  name: "choice",
258
- message: i18n.selectEnvPermissionOption,
259
- choices: [
358
+ message: i18n.configuration?.selectEnvPermissionOption || "Select option",
359
+ choices: addNumbersToChoices([
260
360
  {
261
- name: `${i18n.importRecommendedEnv} ${ansis.gray("- " + i18n.importRecommendedEnvDesc)}`,
361
+ name: `${i18n.configuration?.importRecommendedEnv || "Import environment"} ${ansis.gray(
362
+ "- " + (i18n.configuration?.importRecommendedEnvDesc || "Import env settings")
363
+ )}`,
262
364
  value: "env"
263
365
  },
264
366
  {
265
- name: `${i18n.importRecommendedPermissions} ${ansis.gray("- " + i18n.importRecommendedPermissionsDesc)}`,
367
+ name: `${i18n.configuration?.importRecommendedPermissions || "Import permissions"} ${ansis.gray(
368
+ "- " + (i18n.configuration?.importRecommendedPermissionsDesc || "Import permission settings")
369
+ )}`,
266
370
  value: "permissions"
267
371
  },
268
372
  {
269
- name: `${i18n.openSettingsJson} ${ansis.gray("- " + i18n.openSettingsJsonDesc)}`,
373
+ name: `${i18n.configuration?.openSettingsJson || "Open settings"} ${ansis.gray(
374
+ "- " + (i18n.configuration?.openSettingsJsonDesc || "View settings file")
375
+ )}`,
270
376
  value: "open"
271
377
  }
272
- ]
378
+ ])
273
379
  });
274
380
  if (!choice) {
275
381
  handleCancellation(scriptLang);
@@ -279,32 +385,37 @@ async function configureEnvPermissionFeature(scriptLang) {
279
385
  switch (choice) {
280
386
  case "env":
281
387
  await importRecommendedEnv();
282
- console.log(ansis.green(`\u2705 ${i18n.envImportSuccess}`));
388
+ console.log(ansis.green(`\u2705 ${i18n.configuration.envImportSuccess}`));
283
389
  break;
284
390
  case "permissions":
285
391
  await importRecommendedPermissions();
286
- console.log(ansis.green(`\u2705 ${i18n.permissionsImportSuccess}`));
392
+ console.log(ansis.green(`\u2705 ${i18n.configuration?.permissionsImportSuccess || "Permissions imported"}`));
287
393
  break;
288
394
  case "open":
289
- console.log(ansis.cyan(i18n.openingSettingsJson));
395
+ console.log(ansis.cyan(i18n.configuration?.openingSettingsJson || "Opening settings.json..."));
290
396
  await openSettingsJson();
291
397
  break;
292
398
  }
293
399
  } catch (error) {
294
- console.error(ansis.red(`${i18n.error}: ${error.message}`));
400
+ console.error(ansis.red(`${i18n.common.error}: ${error.message}`));
295
401
  }
296
402
  }
297
403
 
298
404
  async function executeCcusage(args = []) {
299
405
  try {
300
- const zcfConfig = await readZcfConfigAsync();
301
- const rawLang = zcfConfig?.preferredLang || "en";
302
- const lang = getValidLanguage(rawLang);
406
+ let lang = "en";
407
+ try {
408
+ const zcfConfig = await readZcfConfigAsync();
409
+ const rawLang = zcfConfig?.preferredLang || "en";
410
+ lang = getValidLanguage(rawLang);
411
+ } catch {
412
+ lang = "en";
413
+ }
303
414
  const i18n = I18N[lang];
304
415
  const command = "npx";
305
- const commandArgs = ["ccusage@latest", ...args];
306
- console.log(ansis.cyan(i18n.runningCcusage));
307
- console.log(ansis.gray(`$ npx ccusage@latest ${args.join(" ")}`));
416
+ const commandArgs = ["ccusage@latest", ...args || []];
417
+ console.log(ansis.cyan(i18n.tools.runningCcusage));
418
+ console.log(ansis.gray(`$ npx ccusage@latest ${(args || []).join(" ")}`));
308
419
  console.log("");
309
420
  await x(command, commandArgs, {
310
421
  nodeOptions: {
@@ -312,14 +423,19 @@ async function executeCcusage(args = []) {
312
423
  }
313
424
  });
314
425
  } catch (error) {
315
- const zcfConfig = await readZcfConfigAsync();
316
- const rawLang = zcfConfig?.preferredLang || "en";
317
- const lang = getValidLanguage(rawLang);
426
+ let lang = "en";
427
+ try {
428
+ const zcfConfig = await readZcfConfigAsync();
429
+ const rawLang = zcfConfig?.preferredLang || "en";
430
+ lang = getValidLanguage(rawLang);
431
+ } catch {
432
+ lang = "en";
433
+ }
318
434
  const i18n = I18N[lang];
319
- console.error(ansis.red(i18n.ccusageFailed));
320
- console.error(ansis.yellow(i18n.checkNetworkConnection));
435
+ console.error(ansis.red(i18n.tools.ccusageFailed));
436
+ console.error(ansis.yellow(i18n.tools.checkNetworkConnection));
321
437
  if (process.env.DEBUG) {
322
- console.error(ansis.gray(i18n.errorDetails), error);
438
+ console.error(ansis.gray(i18n.tools.errorDetails), error);
323
439
  }
324
440
  if (process.env.NODE_ENV !== "test") {
325
441
  process.exit(1);
@@ -328,29 +444,228 @@ async function executeCcusage(args = []) {
328
444
  }
329
445
  }
330
446
 
447
+ const execAsync = promisify(exec);
448
+ async function runCcrUi(scriptLang, apiKey) {
449
+ const i18n = I18N[scriptLang];
450
+ console.log(ansis.cyan(`
451
+ \u{1F5A5}\uFE0F ${i18n.ccr.startingCcrUi}`));
452
+ if (apiKey) {
453
+ console.log(ansis.bold.green(`
454
+ \u{1F511} ${i18n.ccr.ccrUiApiKey || "CCR UI API Key"}: ${apiKey}`));
455
+ console.log(ansis.gray(` ${i18n.ccr.ccrUiApiKeyHint || "Use this API key to login to CCR UI"}
456
+ `));
457
+ }
458
+ try {
459
+ const { stdout, stderr } = await execAsync("ccr ui");
460
+ if (stdout) console.log(stdout);
461
+ if (stderr) console.error(ansis.yellow(stderr));
462
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrUiStarted}`));
463
+ } catch (error) {
464
+ console.error(ansis.red(`\u2716 ${i18n.ccr.ccrCommandFailed}: ${error instanceof Error ? error.message : String(error)}`));
465
+ throw error;
466
+ }
467
+ }
468
+ async function runCcrStatus(scriptLang) {
469
+ const i18n = I18N[scriptLang];
470
+ console.log(ansis.cyan(`
471
+ \u{1F4CA} ${i18n.ccr.checkingCcrStatus}`));
472
+ try {
473
+ const { stdout, stderr } = await execAsync("ccr status");
474
+ if (stdout) {
475
+ console.log("\n" + ansis.bold(i18n.ccr.ccrStatusTitle));
476
+ console.log(stdout);
477
+ }
478
+ if (stderr) console.error(ansis.yellow(stderr));
479
+ } catch (error) {
480
+ console.error(ansis.red(`\u2716 ${i18n.ccr.ccrCommandFailed}: ${error instanceof Error ? error.message : String(error)}`));
481
+ throw error;
482
+ }
483
+ }
484
+ async function runCcrRestart(scriptLang) {
485
+ const i18n = I18N[scriptLang];
486
+ console.log(ansis.cyan(`
487
+ \u{1F504} ${i18n.ccr.restartingCcr}`));
488
+ try {
489
+ const { stdout, stderr } = await execAsync("ccr restart");
490
+ if (stdout) console.log(stdout);
491
+ if (stderr) console.error(ansis.yellow(stderr));
492
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrRestarted}`));
493
+ } catch (error) {
494
+ console.error(ansis.red(`\u2716 ${i18n.ccr.ccrCommandFailed}: ${error instanceof Error ? error.message : String(error)}`));
495
+ throw error;
496
+ }
497
+ }
498
+ async function runCcrStart(scriptLang) {
499
+ const i18n = I18N[scriptLang];
500
+ console.log(ansis.cyan(`
501
+ \u25B6\uFE0F ${i18n.ccr.startingCcr}`));
502
+ try {
503
+ const { stdout, stderr } = await execAsync("ccr start");
504
+ if (stdout) console.log(stdout);
505
+ if (stderr) console.error(ansis.yellow(stderr));
506
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrStarted}`));
507
+ } catch (error) {
508
+ console.error(ansis.red(`\u2716 ${i18n.ccr.ccrCommandFailed}: ${error instanceof Error ? error.message : String(error)}`));
509
+ throw error;
510
+ }
511
+ }
512
+ async function runCcrStop(scriptLang) {
513
+ const i18n = I18N[scriptLang];
514
+ console.log(ansis.cyan(`
515
+ \u23F9\uFE0F ${i18n.ccr.stoppingCcr}`));
516
+ try {
517
+ const { stdout, stderr } = await execAsync("ccr stop");
518
+ if (stdout) console.log(stdout);
519
+ if (stderr) console.error(ansis.yellow(stderr));
520
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrStopped}`));
521
+ } catch (error) {
522
+ console.error(ansis.red(`\u2716 ${i18n.ccr.ccrCommandFailed}: ${error instanceof Error ? error.message : String(error)}`));
523
+ throw error;
524
+ }
525
+ }
526
+
527
+ function isCcrConfigured() {
528
+ const CCR_CONFIG_FILE = join(homedir(), ".claude-code-router", "config.json");
529
+ if (!existsSync(CCR_CONFIG_FILE)) {
530
+ return false;
531
+ }
532
+ const config = readCcrConfig();
533
+ return config !== null && config.Providers && config.Providers.length > 0;
534
+ }
535
+ async function showCcrMenu(scriptLang) {
536
+ try {
537
+ const i18n = I18N[scriptLang];
538
+ console.log("\n" + ansis.cyan("\u2550".repeat(50)));
539
+ console.log(ansis.bold.cyan(` ${i18n.ccr.ccrMenuTitle}`));
540
+ console.log(ansis.cyan("\u2550".repeat(50)) + "\n");
541
+ console.log(` ${ansis.cyan("1.")} ${i18n.ccr.ccrMenuOptions.initCcr} ${ansis.gray("- " + i18n.ccr.ccrMenuDescriptions.initCcr)}`);
542
+ console.log(` ${ansis.cyan("2.")} ${i18n.ccr.ccrMenuOptions.startUi} ${ansis.gray("- " + i18n.ccr.ccrMenuDescriptions.startUi)}`);
543
+ console.log(` ${ansis.cyan("3.")} ${i18n.ccr.ccrMenuOptions.checkStatus} ${ansis.gray("- " + i18n.ccr.ccrMenuDescriptions.checkStatus)}`);
544
+ console.log(` ${ansis.cyan("4.")} ${i18n.ccr.ccrMenuOptions.restart} ${ansis.gray("- " + i18n.ccr.ccrMenuDescriptions.restart)}`);
545
+ console.log(` ${ansis.cyan("5.")} ${i18n.ccr.ccrMenuOptions.start} ${ansis.gray("- " + i18n.ccr.ccrMenuDescriptions.start)}`);
546
+ console.log(` ${ansis.cyan("6.")} ${i18n.ccr.ccrMenuOptions.stop} ${ansis.gray("- " + i18n.ccr.ccrMenuDescriptions.stop)}`);
547
+ console.log(` ${ansis.yellow("0.")} ${i18n.ccr.ccrMenuOptions.back}`);
548
+ console.log("");
549
+ const { choice } = await inquirer.prompt({
550
+ type: "input",
551
+ name: "choice",
552
+ message: i18n.common.enterChoice,
553
+ validate: (value) => {
554
+ const valid = ["1", "2", "3", "4", "5", "6", "0"];
555
+ return valid.includes(value) || i18n.common.invalidChoice;
556
+ }
557
+ });
558
+ switch (choice) {
559
+ case "1":
560
+ const ccrInstalled = await isCcrInstalled();
561
+ if (!ccrInstalled) {
562
+ console.log(ansis.yellow(`${i18n.ccr.installingCcr}`));
563
+ await installCcr(scriptLang);
564
+ } else {
565
+ console.log(ansis.green(`\u2714 ${i18n.ccr.ccrAlreadyInstalled}`));
566
+ }
567
+ await configureCcrFeature(scriptLang);
568
+ console.log(ansis.green(`
569
+ \u2714 ${i18n.ccr.ccrSetupComplete}`));
570
+ break;
571
+ case "2":
572
+ if (!isCcrConfigured()) {
573
+ console.log(ansis.yellow(`
574
+ \u26A0\uFE0F ${i18n.ccr.ccrNotConfigured || "CCR is not configured yet. Please initialize CCR first."}`));
575
+ console.log(ansis.cyan(` ${i18n.ccr.pleaseInitFirst || "Please select option 1 to initialize CCR."}
576
+ `));
577
+ } else {
578
+ const config = readCcrConfig();
579
+ await runCcrUi(scriptLang, config?.APIKEY);
580
+ }
581
+ break;
582
+ case "3":
583
+ if (!isCcrConfigured()) {
584
+ console.log(ansis.yellow(`
585
+ \u26A0\uFE0F ${i18n.ccr.ccrNotConfigured || "CCR is not configured yet. Please initialize CCR first."}`));
586
+ console.log(ansis.cyan(` ${i18n.ccr.pleaseInitFirst || "Please select option 1 to initialize CCR."}
587
+ `));
588
+ } else {
589
+ await runCcrStatus(scriptLang);
590
+ }
591
+ break;
592
+ case "4":
593
+ if (!isCcrConfigured()) {
594
+ console.log(ansis.yellow(`
595
+ \u26A0\uFE0F ${i18n.ccr.ccrNotConfigured || "CCR is not configured yet. Please initialize CCR first."}`));
596
+ console.log(ansis.cyan(` ${i18n.ccr.pleaseInitFirst || "Please select option 1 to initialize CCR."}
597
+ `));
598
+ } else {
599
+ await runCcrRestart(scriptLang);
600
+ }
601
+ break;
602
+ case "5":
603
+ if (!isCcrConfigured()) {
604
+ console.log(ansis.yellow(`
605
+ \u26A0\uFE0F ${i18n.ccr.ccrNotConfigured || "CCR is not configured yet. Please initialize CCR first."}`));
606
+ console.log(ansis.cyan(` ${i18n.ccr.pleaseInitFirst || "Please select option 1 to initialize CCR."}
607
+ `));
608
+ } else {
609
+ await runCcrStart(scriptLang);
610
+ }
611
+ break;
612
+ case "6":
613
+ if (!isCcrConfigured()) {
614
+ console.log(ansis.yellow(`
615
+ \u26A0\uFE0F ${i18n.ccr.ccrNotConfigured || "CCR is not configured yet. Please initialize CCR first."}`));
616
+ console.log(ansis.cyan(` ${i18n.ccr.pleaseInitFirst || "Please select option 1 to initialize CCR."}
617
+ `));
618
+ } else {
619
+ await runCcrStop(scriptLang);
620
+ }
621
+ break;
622
+ case "0":
623
+ return false;
624
+ }
625
+ if (choice !== "0") {
626
+ console.log("\n" + ansis.dim("\u2500".repeat(50)) + "\n");
627
+ const { continueInCcr } = await inquirer.prompt({
628
+ type: "confirm",
629
+ name: "continueInCcr",
630
+ message: i18n.common.returnToMenu || "Return to CCR menu?",
631
+ default: true
632
+ });
633
+ if (continueInCcr) {
634
+ return await showCcrMenu(scriptLang);
635
+ }
636
+ }
637
+ return false;
638
+ } catch (error) {
639
+ if (!handleExitPromptError(error)) {
640
+ handleGeneralError(error, scriptLang);
641
+ }
642
+ return false;
643
+ }
644
+ }
645
+
331
646
  function getValidLanguage(lang) {
332
647
  return lang && lang in I18N ? lang : "en";
333
648
  }
334
649
  async function runCcusageFeature(scriptLang) {
335
650
  const validLang = getValidLanguage(scriptLang);
336
- const i18n = I18N[validLang];
651
+ const i18n = getTranslation(validLang);
337
652
  console.log("");
338
- console.log(ansis.cyan(i18n.menuOptions.ccusage));
339
- console.log(ansis.gray(`${i18n.ccusageDescription} - https://github.com/ryoppippi/ccusage`));
653
+ console.log(ansis.cyan(i18n.menu.menuOptions.ccusage));
654
+ console.log(ansis.gray(`${i18n.tools.ccusageDescription} - https://github.com/ryoppippi/ccusage`));
340
655
  console.log("");
341
656
  const choices = [
342
- { name: i18n.ccusageModes.daily, value: "daily" },
343
- { name: i18n.ccusageModes.monthly, value: "monthly" },
344
- { name: i18n.ccusageModes.session, value: "session" },
345
- { name: i18n.ccusageModes.blocks, value: "blocks" },
346
- { name: i18n.ccusageModes.custom, value: "custom" },
347
- { name: i18n.back, value: "back" }
657
+ { name: i18n.tools.ccusageModes.daily, value: "daily" },
658
+ { name: i18n.tools.ccusageModes.monthly, value: "monthly" },
659
+ { name: i18n.tools.ccusageModes.session, value: "session" },
660
+ { name: i18n.tools.ccusageModes.blocks, value: "blocks" },
661
+ { name: i18n.tools.ccusageModes.custom, value: "custom" },
662
+ { name: i18n.common.back, value: "back" }
348
663
  ];
349
664
  const { mode } = await inquirer.prompt({
350
665
  type: "list",
351
666
  name: "mode",
352
- message: i18n.selectAnalysisMode,
353
- choices
667
+ message: i18n.tools.selectAnalysisMode,
668
+ choices: addNumbersToChoices(choices)
354
669
  });
355
670
  if (mode === "back") {
356
671
  return;
@@ -360,7 +675,7 @@ async function runCcusageFeature(scriptLang) {
360
675
  const { customArgs } = await inquirer.prompt({
361
676
  type: "input",
362
677
  name: "customArgs",
363
- message: i18n.enterCustomArgs,
678
+ message: i18n.tools.enterCustomArgs,
364
679
  default: ""
365
680
  });
366
681
  if (customArgs === null || customArgs === void 0 || customArgs === "") {
@@ -391,9 +706,13 @@ async function runCcusageFeature(scriptLang) {
391
706
  await inquirer.prompt({
392
707
  type: "input",
393
708
  name: "continue",
394
- message: ansis.gray(i18n.pressEnterToContinue)
709
+ message: ansis.gray(i18n.tools.pressEnterToContinue)
395
710
  });
396
711
  }
712
+ async function runCcrMenuFeature(scriptLang) {
713
+ const validLang = getValidLanguage(scriptLang);
714
+ await showCcrMenu(validLang);
715
+ }
397
716
 
398
717
  async function update(options = {}) {
399
718
  try {
@@ -408,21 +727,21 @@ async function update(options = {}) {
408
727
  const { lang } = await inquirer.prompt({
409
728
  type: "list",
410
729
  name: "lang",
411
- message: i18n.updateConfigLangPrompt,
412
- choices: SUPPORTED_LANGS.map((l) => ({
413
- name: `${LANG_LABELS[l]} - ${i18n.configLangHint[l]}`,
730
+ message: i18n.language.updateConfigLangPrompt,
731
+ choices: addNumbersToChoices(SUPPORTED_LANGS.map((l) => ({
732
+ name: `${LANG_LABELS[l]} - ${i18n.language.configLangHint[l]}`,
414
733
  value: l
415
- }))
734
+ })))
416
735
  });
417
736
  if (!lang) {
418
- console.log(ansis.yellow(i18n.cancelled));
737
+ console.log(ansis.yellow(i18n.common.cancelled));
419
738
  process.exit(0);
420
739
  }
421
740
  configLang = lang;
422
741
  }
423
742
  const aiOutputLang = await resolveAiOutputLanguage(scriptLang, options.aiOutputLang, zcfConfig);
424
743
  console.log(ansis.cyan(`
425
- ${i18n.updatingPrompts}
744
+ ${i18n.workflow.updatingPrompts}
426
745
  `));
427
746
  await updatePromptOnly(configLang, scriptLang, aiOutputLang);
428
747
  await selectAndInstallWorkflows(configLang, scriptLang);
@@ -446,69 +765,72 @@ async function showMainMenu() {
446
765
  let exitMenu = false;
447
766
  while (!exitMenu) {
448
767
  const i18n = I18N[scriptLang];
449
- console.log(ansis.cyan(i18n.selectFunction));
768
+ console.log(ansis.cyan(i18n.menu.selectFunction));
450
769
  console.log(" -------- Claude Code --------");
451
770
  console.log(
452
- ` ${ansis.cyan("1.")} ${i18n.menuOptions.fullInit} ${ansis.gray("- " + i18n.menuDescriptions.fullInit)}`
771
+ ` ${ansis.cyan("1.")} ${i18n.menu.menuOptions.fullInit} ${ansis.gray("- " + i18n.menu.menuDescriptions.fullInit)}`
453
772
  );
454
773
  console.log(
455
- ` ${ansis.cyan("2.")} ${i18n.menuOptions.importWorkflow} ${ansis.gray(
456
- "- " + i18n.menuDescriptions.importWorkflow
774
+ ` ${ansis.cyan("2.")} ${i18n.menu.menuOptions.importWorkflow} ${ansis.gray(
775
+ "- " + i18n.menu.menuDescriptions.importWorkflow
457
776
  )}`
458
777
  );
459
778
  console.log(
460
- ` ${ansis.cyan("3.")} ${i18n.menuOptions.configureApi} ${ansis.gray(
461
- "- " + i18n.menuDescriptions.configureApi
779
+ ` ${ansis.cyan("3.")} ${i18n.menu.menuOptions.configureApiOrCcr} ${ansis.gray(
780
+ "- " + i18n.menu.menuDescriptions.configureApiOrCcr
462
781
  )}`
463
782
  );
464
783
  console.log(
465
- ` ${ansis.cyan("4.")} ${i18n.menuOptions.configureMcp} ${ansis.gray(
466
- "- " + i18n.menuDescriptions.configureMcp
784
+ ` ${ansis.cyan("4.")} ${i18n.menu.menuOptions.configureMcp} ${ansis.gray(
785
+ "- " + i18n.menu.menuDescriptions.configureMcp
467
786
  )}`
468
787
  );
469
788
  console.log(
470
- ` ${ansis.cyan("5.")} ${i18n.menuOptions.configureModel} ${ansis.gray(
471
- "- " + i18n.menuDescriptions.configureModel
789
+ ` ${ansis.cyan("5.")} ${i18n.menu.menuOptions.configureModel} ${ansis.gray(
790
+ "- " + i18n.menu.menuDescriptions.configureModel
472
791
  )}`
473
792
  );
474
793
  console.log(
475
- ` ${ansis.cyan("6.")} ${i18n.menuOptions.configureAiMemory} ${ansis.gray(
476
- "- " + i18n.menuDescriptions.configureAiMemory
794
+ ` ${ansis.cyan("6.")} ${i18n.menu.menuOptions.configureAiMemory} ${ansis.gray(
795
+ "- " + i18n.menu.menuDescriptions.configureAiMemory
477
796
  )}`
478
797
  );
479
798
  console.log(
480
- ` ${ansis.cyan("7.")} ${i18n.menuOptions.configureEnvPermission} ${ansis.gray(
481
- "- " + i18n.menuDescriptions.configureEnvPermission
799
+ ` ${ansis.cyan("7.")} ${i18n.menu.menuOptions.configureEnvPermission} ${ansis.gray(
800
+ "- " + i18n.menu.menuDescriptions.configureEnvPermission
482
801
  )}`
483
802
  );
484
803
  console.log("");
485
- console.log(` --------- ${i18n.menuSections.otherTools} ----------`);
804
+ console.log(` --------- ${i18n.menu.menuSections.otherTools} ----------`);
486
805
  console.log(
487
- ` ${ansis.cyan("U.")} ${i18n.menuOptions.ccusage} ${ansis.gray("- " + i18n.menuDescriptions.ccusage)}`
806
+ ` ${ansis.cyan("R.")} ${i18n.menu.menuOptions.ccrManagement} ${ansis.gray("- " + i18n.menu.menuDescriptions.ccrManagement)}`
807
+ );
808
+ console.log(
809
+ ` ${ansis.cyan("U.")} ${i18n.menu.menuOptions.ccusage} ${ansis.gray("- " + i18n.menu.menuDescriptions.ccusage)}`
488
810
  );
489
811
  console.log("");
490
812
  console.log(" ------------ ZCF ------------");
491
813
  console.log(
492
- ` ${ansis.cyan("0.")} ${i18n.menuOptions.changeLanguage} ${ansis.gray(
493
- "- " + i18n.menuDescriptions.changeLanguage
814
+ ` ${ansis.cyan("0.")} ${i18n.menu.menuOptions.changeLanguage} ${ansis.gray(
815
+ "- " + i18n.menu.menuDescriptions.changeLanguage
494
816
  )}`
495
817
  );
496
818
  console.log(
497
- ` ${ansis.cyan("-.")} ${i18n.menuOptions.clearCache} ${ansis.gray("- " + i18n.menuDescriptions.clearCache)}`
819
+ ` ${ansis.cyan("-.")} ${i18n.menu.menuOptions.clearCache} ${ansis.gray("- " + i18n.menu.menuDescriptions.clearCache)}`
498
820
  );
499
- console.log(` ${ansis.red("Q.")} ${ansis.red(i18n.menuOptions.exit)}`);
821
+ console.log(` ${ansis.red("Q.")} ${ansis.red(i18n.menu.menuOptions.exit)}`);
500
822
  console.log("");
501
823
  const { choice } = await inquirer.prompt({
502
824
  type: "input",
503
825
  name: "choice",
504
- message: i18n.enterChoice,
826
+ message: i18n.common.enterChoice,
505
827
  validate: (value) => {
506
- const valid = ["1", "2", "3", "4", "5", "6", "7", "u", "U", "0", "-", "q", "Q"];
507
- return valid.includes(value) || i18n.invalidChoice;
828
+ const valid = ["1", "2", "3", "4", "5", "6", "7", "r", "R", "u", "U", "0", "-", "q", "Q"];
829
+ return valid.includes(value) || i18n.common.invalidChoice;
508
830
  }
509
831
  });
510
832
  if (!choice) {
511
- console.log(ansis.yellow(i18n.cancelled));
833
+ console.log(ansis.yellow(i18n.common.cancelled));
512
834
  exitMenu = true;
513
835
  break;
514
836
  }
@@ -534,6 +856,10 @@ async function showMainMenu() {
534
856
  case "7":
535
857
  await configureEnvPermissionFeature(scriptLang);
536
858
  break;
859
+ case "r":
860
+ case "R":
861
+ await runCcrMenuFeature(scriptLang);
862
+ break;
537
863
  case "u":
538
864
  case "U":
539
865
  await runCcusageFeature(scriptLang);
@@ -549,11 +875,11 @@ async function showMainMenu() {
549
875
  break;
550
876
  case "q":
551
877
  exitMenu = true;
552
- console.log(ansis.cyan(i18n.goodbye));
878
+ console.log(ansis.cyan(i18n.common.goodbye));
553
879
  break;
554
880
  }
555
881
  if (!exitMenu && choice.toLowerCase() !== "q") {
556
- if (choice === "0" || choice === "-" || choice.toLowerCase() === "u") {
882
+ if (choice === "0" || choice === "-" || choice.toLowerCase() === "u" || choice.toLowerCase() === "r") {
557
883
  console.log("\n" + ansis.dim("\u2500".repeat(50)) + "\n");
558
884
  continue;
559
885
  }
@@ -561,12 +887,12 @@ async function showMainMenu() {
561
887
  const { continue: shouldContinue } = await inquirer.prompt({
562
888
  type: "confirm",
563
889
  name: "continue",
564
- message: i18n.returnToMenu,
890
+ message: i18n.common.returnToMenu,
565
891
  default: true
566
892
  });
567
893
  if (!shouldContinue) {
568
894
  exitMenu = true;
569
- console.log(ansis.cyan(i18n.goodbye));
895
+ console.log(ansis.cyan(i18n.common.goodbye));
570
896
  }
571
897
  }
572
898
  }
@@ -577,6 +903,34 @@ async function showMainMenu() {
577
903
  }
578
904
  }
579
905
 
906
+ async function ccr(options = {}) {
907
+ try {
908
+ if (!options.skipBanner) {
909
+ displayBannerWithInfo();
910
+ }
911
+ const zcfConfig = await readZcfConfigAsync();
912
+ const scriptLang = options.lang || zcfConfig?.preferredLang || await selectScriptLanguage();
913
+ const continueInCcr = await showCcrMenu(scriptLang);
914
+ if (!continueInCcr && !options.skipBanner) {
915
+ await showMainMenu();
916
+ }
917
+ } catch (error) {
918
+ if (!handleExitPromptError(error)) {
919
+ handleGeneralError(error, options.lang);
920
+ }
921
+ }
922
+ }
923
+
924
+ async function checkUpdates(options = {}) {
925
+ const scriptLang = options.lang || await selectScriptLanguage();
926
+ try {
927
+ await checkAndUpdateTools(scriptLang);
928
+ } catch (error) {
929
+ console.error(ansis.red("Error checking updates:"), error);
930
+ process.exit(1);
931
+ }
932
+ }
933
+
580
934
  function setupCommands(cli) {
581
935
  cli.command("[lang]", "Show interactive menu (default)").option("--init", "Run full initialization directly").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").option("--force, -f", "Force overwrite existing configuration").action(async (lang, options) => {
582
936
  await handleDefaultCommand(lang, options);
@@ -587,9 +941,15 @@ function setupCommands(cli) {
587
941
  cli.command("update", "Update Claude Code prompts only").alias("u").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").action(async (options) => {
588
942
  await handleUpdateCommand(options);
589
943
  });
944
+ cli.command("ccr", "Configure Claude Code Router for model proxy").option("--lang, -l <lang>", "Display language (zh-CN, en)").action(async (options) => {
945
+ await ccr({ lang: options.lang });
946
+ });
590
947
  cli.command("ccu [...args]", "Run Claude Code usage analysis tool").allowUnknownOptions().action(async (args) => {
591
948
  await executeCcusage(args);
592
949
  });
950
+ cli.command("check-updates", "Check and update Claude Code and CCR to latest versions").alias("check").option("--lang, -l <lang>", "Display language (zh-CN, en)").action(async (options) => {
951
+ await checkUpdates({ lang: options.lang });
952
+ });
593
953
  cli.help((sections) => customizeHelp(sections));
594
954
  cli.version(version);
595
955
  }
@@ -628,6 +988,7 @@ function customizeHelp(sections) {
628
988
  "i"
629
989
  )} Initialize Claude Code configuration / \u521D\u59CB\u5316 Claude Code \u914D\u7F6E`,
630
990
  ` ${ansis.cyan("zcf update")} | ${ansis.cyan("u")} Update workflow-related md files / \u4EC5\u66F4\u65B0\u5DE5\u4F5C\u6D41\u76F8\u5173md`,
991
+ ` ${ansis.cyan("zcf ccr")} Configure Claude Code Router / \u914D\u7F6E\u6A21\u578B\u4EE3\u7406`,
631
992
  ` ${ansis.cyan("zcf ccu")} [args] Run Claude Code usage analysis / \u8FD0\u884C Claude Code \u7528\u91CF\u5206\u6790`,
632
993
  "",
633
994
  ansis.gray(" Shortcuts / \u5FEB\u6377\u65B9\u5F0F:"),