zcf 1.2.0 → 2.0.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.
package/dist/cli.mjs CHANGED
@@ -1,17 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import cac from 'cac';
3
3
  import ansis from 'ansis';
4
- import { v as displayBanner, x as selectScriptLanguage, y as readZcfConfig, I as I18N, S as SETTINGS_FILE, d as SUPPORTED_LANGS, L as LANG_LABELS, z as resolveAiOutputLanguage, B as updatePromptOnly, D as updateZcfConfig, E as version, i as init } from './shared/zcf.D3MMT8L8.mjs';
4
+ import { z as displayBanner, B as selectScriptLanguage, D as readZcfConfig, I as I18N, S as SETTINGS_FILE, d as SUPPORTED_LANGS, L as LANG_LABELS, E as resolveAiOutputLanguage, F as updatePromptOnly, G as updateZcfConfig, H as version, Z as ZCF_CONFIG_FILE, p as applyAiLanguageDirective, J as configureAiPersonality, u as updateDefaultModel, K as isWindows, r as readMcpConfig, x as fixWindowsMcpConfig, w as writeMcpConfig, M as MCP_SERVICES, s as backupMcpConfig, v as buildMcpServerConfig, t as mergeMcpServers, o as getExistingApiConfig, N as formatApiKeyDisplay, O as modifyApiConfigPartially, P as validateApiKey, l as configureApi, Q as displayBannerWithInfo, i as init } from './shared/zcf.DGNSM22u.mjs';
5
5
  import prompts from '@posva/prompts';
6
- import { existsSync } from 'node:fs';
6
+ import { existsSync, unlinkSync } from 'node:fs';
7
7
  import 'node:os';
8
8
  import 'pathe';
9
9
  import 'dayjs';
10
+ import 'node:url';
10
11
  import 'tinyexec';
11
12
 
12
13
  async function update(options = {}) {
13
14
  try {
14
- displayBanner("Update configuration for Claude Code");
15
+ if (!options.skipBanner) {
16
+ displayBanner("Update configuration for Claude Code");
17
+ }
15
18
  const scriptLang = await selectScriptLanguage();
16
19
  const zcfConfig = readZcfConfig();
17
20
  const i18n = I18N[scriptLang];
@@ -58,11 +61,426 @@ ${i18n.updatingPrompts}
58
61
  }
59
62
  }
60
63
 
64
+ function handleCancellation(scriptLang) {
65
+ console.log(ansis.yellow(I18N[scriptLang].cancelled));
66
+ }
67
+ async function configureApiFeature(scriptLang) {
68
+ const i18n = I18N[scriptLang];
69
+ const existingApiConfig = getExistingApiConfig();
70
+ if (existingApiConfig) {
71
+ console.log("\n" + ansis.blue(`\u2139 ${i18n.existingApiConfig}`));
72
+ console.log(ansis.gray(` ${i18n.apiConfigUrl}: ${existingApiConfig.url || i18n.notConfigured}`));
73
+ console.log(ansis.gray(` ${i18n.apiConfigKey}: ${existingApiConfig.key ? formatApiKeyDisplay(existingApiConfig.key) : i18n.notConfigured}`));
74
+ console.log(ansis.gray(` ${i18n.apiConfigAuthType}: ${existingApiConfig.authType || i18n.notConfigured}
75
+ `));
76
+ const actionResponse = await prompts({
77
+ type: "select",
78
+ name: "action",
79
+ message: i18n.selectApiAction,
80
+ choices: [
81
+ { title: i18n.keepExistingConfig, value: "keep" },
82
+ { title: i18n.modifyAllConfig, value: "modify-all" },
83
+ { title: i18n.modifyPartialConfig, value: "modify-partial" }
84
+ ]
85
+ });
86
+ if (!actionResponse.action) {
87
+ handleCancellation(scriptLang);
88
+ return;
89
+ }
90
+ if (actionResponse.action === "keep") {
91
+ console.log(ansis.green(`\u2714 ${i18n.keepExistingConfig}`));
92
+ return;
93
+ } else if (actionResponse.action === "modify-partial") {
94
+ await modifyApiConfigPartially(existingApiConfig, i18n, scriptLang);
95
+ return;
96
+ }
97
+ }
98
+ const apiResponse = await prompts({
99
+ type: "select",
100
+ name: "apiChoice",
101
+ message: i18n.configureApi,
102
+ choices: [
103
+ {
104
+ title: i18n.useAuthToken,
105
+ value: "auth_token",
106
+ description: ansis.gray(i18n.authTokenDesc)
107
+ },
108
+ {
109
+ title: i18n.useApiKey,
110
+ value: "api_key",
111
+ description: ansis.gray(i18n.apiKeyDesc)
112
+ },
113
+ {
114
+ title: i18n.skipApi,
115
+ value: "skip"
116
+ }
117
+ ]
118
+ });
119
+ if (!apiResponse.apiChoice || apiResponse.apiChoice === "skip") {
120
+ return;
121
+ }
122
+ const apiChoice = apiResponse.apiChoice;
123
+ const urlResponse = await prompts({
124
+ type: "text",
125
+ name: "url",
126
+ message: i18n.enterApiUrl,
127
+ validate: (value) => {
128
+ if (!value) return i18n.urlRequired;
129
+ try {
130
+ new URL(value);
131
+ return true;
132
+ } catch {
133
+ return i18n.invalidUrl;
134
+ }
135
+ }
136
+ });
137
+ if (!urlResponse.url) {
138
+ handleCancellation(scriptLang);
139
+ return;
140
+ }
141
+ const keyMessage = apiChoice === "auth_token" ? i18n.enterAuthToken : i18n.enterApiKey;
142
+ const keyResponse = await prompts({
143
+ type: "text",
144
+ name: "key",
145
+ message: keyMessage,
146
+ validate: (value) => {
147
+ if (!value) {
148
+ return i18n.keyRequired;
149
+ }
150
+ const validation = validateApiKey(value, scriptLang);
151
+ if (!validation.isValid) {
152
+ return validation.error || i18n.invalidKeyFormat;
153
+ }
154
+ return true;
155
+ }
156
+ });
157
+ if (!keyResponse.key) {
158
+ handleCancellation(scriptLang);
159
+ return;
160
+ }
161
+ const apiConfig = { url: urlResponse.url, key: keyResponse.key, authType: apiChoice };
162
+ const configuredApi = configureApi(apiConfig);
163
+ if (configuredApi) {
164
+ console.log(ansis.green(`\u2714 ${i18n.apiConfigSuccess}`));
165
+ console.log(ansis.gray(` URL: ${configuredApi.url}`));
166
+ console.log(ansis.gray(` Key: ${formatApiKeyDisplay(configuredApi.key)}`));
167
+ }
168
+ }
169
+ async function configureMcpFeature(scriptLang) {
170
+ const i18n = I18N[scriptLang];
171
+ if (isWindows()) {
172
+ const fixResponse = await prompts({
173
+ type: "confirm",
174
+ name: "fixWindows",
175
+ message: i18n.fixWindowsMcp || "Fix Windows MCP configuration?",
176
+ initial: true
177
+ });
178
+ if (fixResponse.fixWindows) {
179
+ const existingConfig = readMcpConfig() || { mcpServers: {} };
180
+ const fixedConfig = fixWindowsMcpConfig(existingConfig);
181
+ writeMcpConfig(fixedConfig);
182
+ console.log(ansis.green(`\u2714 ${i18n.windowsMcpFixed || "Windows MCP configuration fixed"}`));
183
+ }
184
+ }
185
+ const choices = [
186
+ {
187
+ title: ansis.bold(i18n.allServices),
188
+ value: "ALL",
189
+ selected: false
190
+ },
191
+ ...MCP_SERVICES.map((service) => ({
192
+ title: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
193
+ value: service.id,
194
+ selected: false
195
+ }))
196
+ ];
197
+ const selectedResponse = await prompts({
198
+ type: "multiselect",
199
+ name: "services",
200
+ message: i18n.selectMcpServices,
201
+ choices,
202
+ instructions: false,
203
+ hint: i18n.spaceToSelectReturn
204
+ });
205
+ if (!selectedResponse.services) {
206
+ return;
207
+ }
208
+ let selectedServices = selectedResponse.services || [];
209
+ if (selectedServices.includes("ALL")) {
210
+ selectedServices = MCP_SERVICES.map((s) => s.id);
211
+ }
212
+ if (selectedServices.length > 0) {
213
+ const mcpBackupPath = backupMcpConfig();
214
+ if (mcpBackupPath) {
215
+ console.log(ansis.gray(`\u2714 ${i18n.mcpBackupSuccess}: ${mcpBackupPath}`));
216
+ }
217
+ const newServers = {};
218
+ for (const serviceId of selectedServices) {
219
+ const service = MCP_SERVICES.find((s) => s.id === serviceId);
220
+ if (!service) continue;
221
+ let config = service.config;
222
+ if (service.requiresApiKey) {
223
+ const apiKeyResponse = await prompts({
224
+ type: "text",
225
+ name: "apiKey",
226
+ message: service.apiKeyPrompt[scriptLang],
227
+ validate: (value) => !!value || i18n.keyRequired
228
+ });
229
+ if (apiKeyResponse.apiKey) {
230
+ config = buildMcpServerConfig(service.config, apiKeyResponse.apiKey, service.apiKeyPlaceholder);
231
+ } else {
232
+ continue;
233
+ }
234
+ }
235
+ newServers[service.id] = config;
236
+ }
237
+ const existingConfig = readMcpConfig();
238
+ let mergedConfig = mergeMcpServers(existingConfig, newServers);
239
+ mergedConfig = fixWindowsMcpConfig(mergedConfig);
240
+ writeMcpConfig(mergedConfig);
241
+ console.log(ansis.green(`\u2714 ${i18n.mcpConfigSuccess}`));
242
+ }
243
+ }
244
+ async function configureDefaultModelFeature(scriptLang) {
245
+ const i18n = I18N[scriptLang];
246
+ const modelResponse = await prompts({
247
+ type: "select",
248
+ name: "model",
249
+ message: i18n.selectDefaultModel || "Select default model",
250
+ choices: [
251
+ { title: "Opus", value: "opus" },
252
+ { title: "Sonnet", value: "sonnet" }
253
+ ]
254
+ });
255
+ if (!modelResponse.model) {
256
+ handleCancellation(scriptLang);
257
+ return;
258
+ }
259
+ updateDefaultModel(modelResponse.model);
260
+ console.log(ansis.green(`\u2714 ${i18n.modelConfigSuccess || "Default model configured"}`));
261
+ }
262
+ async function configureAiMemoryFeature(scriptLang) {
263
+ const i18n = I18N[scriptLang];
264
+ const memoryResponse = await prompts({
265
+ type: "select",
266
+ name: "option",
267
+ message: i18n.selectMemoryOption || "Select configuration option",
268
+ choices: [
269
+ {
270
+ title: i18n.configureAiLanguage || "Configure AI output language",
271
+ value: "language"
272
+ },
273
+ {
274
+ title: i18n.configureAiPersonality || "Configure AI personality",
275
+ value: "personality"
276
+ }
277
+ ]
278
+ });
279
+ if (!memoryResponse.option) {
280
+ return;
281
+ }
282
+ if (memoryResponse.option === "language") {
283
+ const zcfConfig = readZcfConfig();
284
+ const aiOutputLang = await resolveAiOutputLanguage(scriptLang, void 0, zcfConfig);
285
+ applyAiLanguageDirective(aiOutputLang);
286
+ updateZcfConfig({ aiOutputLang });
287
+ console.log(ansis.green(`\u2714 ${i18n.aiLanguageConfigured || "AI output language configured"}`));
288
+ } else {
289
+ await configureAiPersonality(scriptLang);
290
+ }
291
+ }
292
+ async function clearZcfCacheFeature(scriptLang) {
293
+ const i18n = I18N[scriptLang];
294
+ const confirmResponse = await prompts({
295
+ type: "confirm",
296
+ name: "confirm",
297
+ message: i18n.confirmClearCache || "Clear all ZCF preferences cache?",
298
+ initial: false
299
+ });
300
+ if (!confirmResponse.confirm) {
301
+ handleCancellation(scriptLang);
302
+ return;
303
+ }
304
+ if (existsSync(ZCF_CONFIG_FILE)) {
305
+ unlinkSync(ZCF_CONFIG_FILE);
306
+ console.log(ansis.green(`\u2714 ${i18n.cacheCleared || "ZCF cache cleared"}`));
307
+ } else {
308
+ console.log(ansis.yellow(i18n.noCacheFound || "No cache found"));
309
+ }
310
+ }
311
+ async function changeScriptLanguageFeature(currentLang) {
312
+ const i18n = I18N[currentLang];
313
+ const langResponse = await prompts({
314
+ type: "select",
315
+ name: "lang",
316
+ message: i18n.selectScriptLang,
317
+ choices: SUPPORTED_LANGS.map((l) => ({
318
+ title: LANG_LABELS[l],
319
+ value: l
320
+ })),
321
+ initial: SUPPORTED_LANGS.indexOf(currentLang)
322
+ });
323
+ if (!langResponse.lang) {
324
+ return currentLang;
325
+ }
326
+ updateZcfConfig({ preferredLang: langResponse.lang });
327
+ console.log(ansis.green(`\u2714 ${I18N[langResponse.lang].languageChanged || "Language changed"}`));
328
+ return langResponse.lang;
329
+ }
330
+
331
+ async function showMainMenu() {
332
+ try {
333
+ displayBannerWithInfo();
334
+ const zcfConfig = readZcfConfig();
335
+ let scriptLang = zcfConfig?.preferredLang || await selectScriptLanguage();
336
+ let exitMenu = false;
337
+ while (!exitMenu) {
338
+ const i18n = I18N[scriptLang];
339
+ console.log(ansis.cyan(i18n.selectFunction));
340
+ console.log(" -------- Claude Code --------");
341
+ console.log(
342
+ ` ${ansis.cyan("1.")} ${i18n.menuOptions.fullInit} ${ansis.gray("- " + i18n.menuDescriptions.fullInit)}`
343
+ );
344
+ console.log(
345
+ ` ${ansis.cyan("2.")} ${i18n.menuOptions.importWorkflow} ${ansis.gray(
346
+ "- " + i18n.menuDescriptions.importWorkflow
347
+ )}`
348
+ );
349
+ console.log(
350
+ ` ${ansis.cyan("3.")} ${i18n.menuOptions.configureApi} ${ansis.gray(
351
+ "- " + i18n.menuDescriptions.configureApi
352
+ )}`
353
+ );
354
+ console.log(
355
+ ` ${ansis.cyan("4.")} ${i18n.menuOptions.configureMcp} ${ansis.gray(
356
+ "- " + i18n.menuDescriptions.configureMcp
357
+ )}`
358
+ );
359
+ console.log(
360
+ ` ${ansis.cyan("5.")} ${i18n.menuOptions.configureModel} ${ansis.gray(
361
+ "- " + i18n.menuDescriptions.configureModel
362
+ )}`
363
+ );
364
+ console.log(
365
+ ` ${ansis.cyan("6.")} ${i18n.menuOptions.configureAiMemory} ${ansis.gray(
366
+ "- " + i18n.menuDescriptions.configureAiMemory
367
+ )}`
368
+ );
369
+ console.log("");
370
+ console.log(" ------------ ZCF ------------");
371
+ console.log(
372
+ ` ${ansis.cyan("0.")} ${i18n.menuOptions.changeLanguage} ${ansis.gray(
373
+ "- " + i18n.menuDescriptions.changeLanguage
374
+ )}`
375
+ );
376
+ console.log(
377
+ ` ${ansis.cyan("-.")} ${i18n.menuOptions.clearCache} ${ansis.gray("- " + i18n.menuDescriptions.clearCache)}`
378
+ );
379
+ console.log(` ${ansis.red("q.")} ${ansis.red(i18n.menuOptions.exit)}`);
380
+ console.log("");
381
+ const response = await prompts({
382
+ type: "text",
383
+ name: "choice",
384
+ message: i18n.enterChoice || "Enter your choice",
385
+ validate: (value) => {
386
+ const valid = ["1", "2", "3", "4", "5", "6", "0", "-", "q", "Q"];
387
+ return valid.includes(value) || i18n.invalidChoice;
388
+ }
389
+ });
390
+ if (!response.choice) {
391
+ console.log(ansis.yellow(i18n.cancelled));
392
+ exitMenu = true;
393
+ break;
394
+ }
395
+ switch (response.choice.toLowerCase()) {
396
+ case "1":
397
+ await init({ lang: scriptLang, skipBanner: true });
398
+ break;
399
+ case "2":
400
+ await update({ skipBanner: true });
401
+ break;
402
+ case "3":
403
+ await configureApiFeature(scriptLang);
404
+ break;
405
+ case "4":
406
+ await configureMcpFeature(scriptLang);
407
+ break;
408
+ case "5":
409
+ await configureDefaultModelFeature(scriptLang);
410
+ break;
411
+ case "6":
412
+ await configureAiMemoryFeature(scriptLang);
413
+ break;
414
+ case "-":
415
+ await clearZcfCacheFeature(scriptLang);
416
+ break;
417
+ case "0":
418
+ const newLang = await changeScriptLanguageFeature(scriptLang);
419
+ if (newLang !== scriptLang) {
420
+ scriptLang = newLang;
421
+ console.log("\n" + ansis.dim("\u2500".repeat(50)) + "\n");
422
+ const newI18n = I18N[scriptLang];
423
+ const continueResponse = await prompts({
424
+ type: "confirm",
425
+ name: "continue",
426
+ message: newI18n.returnToMenu,
427
+ initial: true
428
+ });
429
+ if (!continueResponse.continue) {
430
+ exitMenu = true;
431
+ console.log(ansis.cyan(newI18n.goodbye));
432
+ }
433
+ continue;
434
+ }
435
+ break;
436
+ case "q":
437
+ exitMenu = true;
438
+ console.log(ansis.cyan(i18n.goodbye));
439
+ break;
440
+ }
441
+ if (!exitMenu && response.choice.toLowerCase() !== "q") {
442
+ console.log("\n" + ansis.dim("\u2500".repeat(50)) + "\n");
443
+ const continueResponse = await prompts({
444
+ type: "confirm",
445
+ name: "continue",
446
+ message: i18n.returnToMenu,
447
+ initial: true
448
+ });
449
+ if (!continueResponse.continue) {
450
+ exitMenu = true;
451
+ console.log(ansis.cyan(i18n.goodbye));
452
+ }
453
+ }
454
+ }
455
+ } catch (error) {
456
+ const zcfConfig = readZcfConfig();
457
+ const defaultLang = zcfConfig?.preferredLang || "en";
458
+ const errorMsg = I18N[defaultLang].error;
459
+ console.error(ansis.red(`${errorMsg}:`), error);
460
+ if (error instanceof Error) {
461
+ console.error(ansis.gray(`Stack: ${error.stack}`));
462
+ }
463
+ process.exit(1);
464
+ }
465
+ }
466
+
61
467
  const cli = cac("zcf");
62
- cli.command("[lang]", "Initialize Claude Code configuration (default)").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").option("--force, -f", "Force overwrite existing configuration").action(async (lang, options) => {
468
+ 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) => {
469
+ if (options.init) {
470
+ await init({
471
+ lang: lang || options.lang,
472
+ configLang: options.configLang,
473
+ force: options.force
474
+ });
475
+ } else {
476
+ await showMainMenu();
477
+ }
478
+ });
479
+ cli.command("init", "Initialize Claude Code configuration").alias("i").option("--lang, -l <lang>", "ZCF display language (zh-CN, en)").option("--config-lang, -c <lang>", "Configuration language (zh-CN, en)").option("--ai-output-lang, -a <lang>", "AI output language").option("--force, -f", "Force overwrite existing configuration").action(async (options) => {
63
480
  await init({
64
- lang: lang || options.lang,
481
+ lang: options.lang,
65
482
  configLang: options.configLang,
483
+ aiOutputLang: options.aiOutputLang,
66
484
  force: options.force
67
485
  });
68
486
  });
@@ -77,16 +495,19 @@ cli.help((sections) => {
77
495
  sections.push({
78
496
  title: ansis.yellow("Commands / \u547D\u4EE4:"),
79
497
  body: [
80
- ` ${ansis.cyan("zcf")} Initialize configuration (default) / \u521D\u59CB\u5316\u914D\u7F6E\uFF08\u9ED8\u8BA4\uFF09`,
498
+ ` ${ansis.cyan("zcf")} Show interactive menu (default) / \u663E\u793A\u4EA4\u4E92\u5F0F\u83DC\u5355\uFF08\u9ED8\u8BA4\uFF09`,
499
+ ` ${ansis.cyan("zcf init")} | ${ansis.cyan("i")} Initialize Claude Code configuration / \u521D\u59CB\u5316 Claude Code \u914D\u7F6E`,
81
500
  ` ${ansis.cyan("zcf update")} | ${ansis.cyan("u")} Update workflow-related md files / \u4EC5\u66F4\u65B0\u5DE5\u4F5C\u6D41\u76F8\u5173md`,
82
501
  "",
83
- ansis.gray(" Shortcut / \u5FEB\u6377\u65B9\u5F0F:"),
502
+ ansis.gray(" Shortcuts / \u5FEB\u6377\u65B9\u5F0F:"),
503
+ ` ${ansis.cyan("zcf i")} Quick init / \u5FEB\u901F\u521D\u59CB\u5316`,
84
504
  ` ${ansis.cyan("zcf u")} Quick update / \u5FEB\u901F\u66F4\u65B0`
85
505
  ].join("\n")
86
506
  });
87
507
  sections.push({
88
508
  title: ansis.yellow("Options / \u9009\u9879:"),
89
509
  body: [
510
+ ` ${ansis.green("--init")} Run full initialization directly / \u76F4\u63A5\u8FD0\u884C\u5B8C\u6574\u521D\u59CB\u5316`,
90
511
  ` ${ansis.green("--config-lang, -c")} <lang> Configuration language / \u914D\u7F6E\u8BED\u8A00 (zh-CN, en)`,
91
512
  ` ${ansis.green("--force, -f")} Force overwrite / \u5F3A\u5236\u8986\u76D6\u73B0\u6709\u914D\u7F6E`,
92
513
  ` ${ansis.green("--help, -h")} Display help / \u663E\u793A\u5E2E\u52A9`,
@@ -96,15 +517,20 @@ cli.help((sections) => {
96
517
  sections.push({
97
518
  title: ansis.yellow("Examples / \u793A\u4F8B:"),
98
519
  body: [
99
- ansis.gray(" # Initialize with interactive prompts / \u4EA4\u4E92\u5F0F\u521D\u59CB\u5316"),
520
+ ansis.gray(" # Show interactive menu / \u663E\u793A\u4EA4\u4E92\u5F0F\u83DC\u5355"),
100
521
  ` ${ansis.cyan("npx zcf")}`,
101
522
  "",
523
+ ansis.gray(" # Run full initialization / \u8FD0\u884C\u5B8C\u6574\u521D\u59CB\u5316"),
524
+ ` ${ansis.cyan("npx zcf init")}`,
525
+ ` ${ansis.cyan("npx zcf i")}`,
526
+ ` ${ansis.cyan("npx zcf --init")}`,
527
+ "",
102
528
  ansis.gray(" # Update workflow-related md files only / \u4EC5\u66F4\u65B0\u5DE5\u4F5C\u6D41\u76F8\u5173md\u6587\u4EF6"),
103
529
  ` ${ansis.cyan("npx zcf u")}`,
104
530
  "",
105
531
  ansis.gray(" # Force overwrite with Chinese config / \u5F3A\u5236\u4F7F\u7528\u4E2D\u6587\u914D\u7F6E\u8986\u76D6"),
106
- ` ${ansis.cyan("npx zcf -c zh-CN -f")}`,
107
- ` ${ansis.cyan("npx zcf --config-lang zh-CN --force")}`
532
+ ` ${ansis.cyan("npx zcf --init -c zh-CN -f")}`,
533
+ ` ${ansis.cyan("npx zcf --init --config-lang zh-CN --force")}`
108
534
  ].join("\n")
109
535
  });
110
536
  return sections;