zcf 1.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.
@@ -0,0 +1,622 @@
1
+ import prompts from '@posva/prompts';
2
+ import ansis from 'ansis';
3
+ import { existsSync, mkdirSync, readdirSync, statSync, copyFileSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { homedir, platform } from 'node:os';
5
+ import { join, dirname } from 'pathe';
6
+ import dayjs from 'dayjs';
7
+ import { exec } from 'tinyexec';
8
+
9
+ const CLAUDE_DIR = join(homedir(), ".claude");
10
+ const SETTINGS_FILE = join(CLAUDE_DIR, "settings.json");
11
+ const CLAUDE_MD_FILE = join(CLAUDE_DIR, "CLAUDE.md");
12
+ const MCP_CONFIG_FILE = join(homedir(), ".claude.json");
13
+ const SUPPORTED_LANGS = ["zh-CN", "en"];
14
+ const LANG_LABELS = {
15
+ "zh-CN": "\u7B80\u4F53\u4E2D\u6587",
16
+ "en": "English"
17
+ };
18
+ const I18N = {
19
+ "zh-CN": {
20
+ selectScriptLang: "\u9009\u62E9\u811A\u672C\u8BED\u8A00",
21
+ selectConfigLang: "\u9009\u62E9 Claude Code \u914D\u7F6E\u8BED\u8A00",
22
+ configLangHint: {
23
+ "zh-CN": "\u4E2D\u6587\u7248\uFF08\u4FBF\u4E8E\u4E2D\u6587\u7528\u6237\u81EA\u5B9A\u4E49\uFF09",
24
+ "en": "\u82F1\u6587\u7248\uFF08\u63A8\u8350\uFF0Ctoken \u6D88\u8017\u66F4\u4F4E\uFF09"
25
+ },
26
+ installPrompt: "\u68C0\u6D4B\u5230 Claude Code \u672A\u5B89\u88C5\uFF0C\u662F\u5426\u81EA\u52A8\u5B89\u88C5\uFF1F",
27
+ installing: "\u6B63\u5728\u5B89\u88C5 Claude Code...",
28
+ installSuccess: "Claude Code \u5B89\u88C5\u6210\u529F",
29
+ installFailed: "Claude Code \u5B89\u88C5\u5931\u8D25",
30
+ npmNotFound: "npm \u672A\u5B89\u88C5\u3002\u8BF7\u5148\u5B89\u88C5 Node.js \u548C npm\u3002",
31
+ configureApi: "\u662F\u5426\u914D\u7F6E API\uFF1F",
32
+ customApi: "\u914D\u7F6E API",
33
+ skipApi: "\u8DF3\u8FC7\uFF08\u7A0D\u540E\u5728 claude \u547D\u4EE4\u4E2D\u81EA\u884C\u914D\u7F6E\uFF0C\u5982 OAuth\uFF09",
34
+ enterApiUrl: "\u8BF7\u8F93\u5165 API URL",
35
+ enterApiKey: "\u8BF7\u8F93\u5165 API Key",
36
+ existingConfig: "\u68C0\u6D4B\u5230\u5DF2\u6709\u914D\u7F6E\u6587\u4EF6\uFF0C\u5982\u4F55\u5904\u7406\uFF1F",
37
+ backupAndOverwrite: "\u5907\u4EFD\u5E76\u8986\u76D6\u5168\u90E8",
38
+ updateDocsOnly: "\u4EC5\u66F4\u65B0 Prompt \u6587\u6863\u5E76\u5907\u4EFD\u65E7\u914D\u7F6E",
39
+ mergeConfig: "\u5408\u5E76\u914D\u7F6E",
40
+ skip: "\u8DF3\u8FC7",
41
+ backupSuccess: "\u5DF2\u5907\u4EFD\u6240\u6709\u914D\u7F6E\u6587\u4EF6\u5230",
42
+ copying: "\u6B63\u5728\u590D\u5236\u914D\u7F6E\u6587\u4EF6...",
43
+ configSuccess: "\u914D\u7F6E\u6587\u4EF6\u5DF2\u590D\u5236\u5230",
44
+ apiConfigSuccess: "API \u914D\u7F6E\u5B8C\u6210",
45
+ mcpConfigSuccess: "MCP \u670D\u52A1\u5DF2\u914D\u7F6E",
46
+ selectMcpServices: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 MCP \u670D\u52A1\uFF08\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
47
+ allServices: "\u5168\u90E8\u5B89\u88C5",
48
+ mcpServiceInstalled: "\u5DF2\u9009\u62E9\u7684 MCP \u670D\u52A1",
49
+ enterExaApiKey: "\u8BF7\u8F93\u5165 Exa API Key\uFF08\u53EF\u4ECE https://dashboard.exa.ai/api-keys \u83B7\u53D6\uFF09",
50
+ skipMcp: "\u8DF3\u8FC7 MCP \u914D\u7F6E",
51
+ configureMcp: "\u662F\u5426\u914D\u7F6E MCP \u670D\u52A1\uFF1F",
52
+ mcpBackupSuccess: "\u5DF2\u5907\u4EFD\u539F\u6709 MCP \u914D\u7F6E",
53
+ complete: "\u{1F389} \u914D\u7F6E\u5B8C\u6210\uFF01\u4F7F\u7528 'claude' \u547D\u4EE4\u5F00\u59CB\u4F53\u9A8C\u3002",
54
+ error: "\u9519\u8BEF",
55
+ yes: "\u662F",
56
+ no: "\u5426"
57
+ },
58
+ "en": {
59
+ selectScriptLang: "Select script language",
60
+ selectConfigLang: "Select Claude Code configuration language",
61
+ configLangHint: {
62
+ "zh-CN": "Chinese (easier for Chinese users to customize)",
63
+ "en": "English (recommended, lower token consumption)"
64
+ },
65
+ installPrompt: "Claude Code not found. Install automatically?",
66
+ installing: "Installing Claude Code...",
67
+ installSuccess: "Claude Code installed successfully",
68
+ installFailed: "Failed to install Claude Code",
69
+ npmNotFound: "npm is not installed. Please install Node.js and npm first.",
70
+ configureApi: "Configure API?",
71
+ customApi: "Configure API",
72
+ skipApi: "Skip (configure later in claude command, e.g., OAuth)",
73
+ enterApiUrl: "Enter API URL",
74
+ enterApiKey: "Enter API Key",
75
+ existingConfig: "Existing config detected. How to proceed?",
76
+ backupAndOverwrite: "Backup and overwrite all",
77
+ updateDocsOnly: "Update Prompt documents only with backup",
78
+ mergeConfig: "Merge config",
79
+ skip: "Skip",
80
+ backupSuccess: "All config files backed up to",
81
+ copying: "Copying configuration files...",
82
+ configSuccess: "Config files copied to",
83
+ apiConfigSuccess: "API configured",
84
+ mcpConfigSuccess: "MCP services configured",
85
+ selectMcpServices: "Select MCP services to install (space to select, enter to confirm)",
86
+ allServices: "Install all",
87
+ mcpServiceInstalled: "Selected MCP services",
88
+ enterExaApiKey: "Enter Exa API Key (get from https://dashboard.exa.ai/api-keys)",
89
+ skipMcp: "Skip MCP configuration",
90
+ configureMcp: "Configure MCP services?",
91
+ mcpBackupSuccess: "Original MCP config backed up",
92
+ complete: "\u{1F389} Setup complete! Use 'claude' command to start.",
93
+ error: "Error",
94
+ yes: "Yes",
95
+ no: "No"
96
+ }
97
+ };
98
+ const MCP_SERVICES = [
99
+ {
100
+ id: "context7",
101
+ name: { "zh-CN": "Context7 \u6587\u6863\u67E5\u8BE2", en: "Context7 Docs" },
102
+ description: {
103
+ "zh-CN": "\u67E5\u8BE2\u6700\u65B0\u7684\u5E93\u6587\u6863\u548C\u4EE3\u7801\u793A\u4F8B",
104
+ en: "Query latest library documentation and code examples"
105
+ },
106
+ requiresApiKey: false,
107
+ config: {
108
+ type: "stdio",
109
+ command: "npx",
110
+ args: ["-y", "@upstash/context7-mcp"],
111
+ env: {}
112
+ }
113
+ },
114
+ {
115
+ id: "mcp-deepwiki",
116
+ name: { "zh-CN": "DeepWiki", en: "DeepWiki" },
117
+ description: {
118
+ "zh-CN": "\u67E5\u8BE2 GitHub \u4ED3\u5E93\u6587\u6863\u548C\u793A\u4F8B",
119
+ en: "Query GitHub repository documentation and examples"
120
+ },
121
+ requiresApiKey: false,
122
+ config: {
123
+ type: "stdio",
124
+ command: "npx",
125
+ args: ["-y", "mcp-deepwiki@latest"],
126
+ env: {}
127
+ }
128
+ },
129
+ {
130
+ id: "Playwright",
131
+ name: { "zh-CN": "Playwright \u6D4F\u89C8\u5668\u63A7\u5236", en: "Playwright Browser Control" },
132
+ description: {
133
+ "zh-CN": "\u76F4\u63A5\u63A7\u5236\u6D4F\u89C8\u5668\u8FDB\u884C\u81EA\u52A8\u5316\u64CD\u4F5C",
134
+ en: "Direct browser control for automation"
135
+ },
136
+ requiresApiKey: false,
137
+ config: {
138
+ type: "stdio",
139
+ command: "npx",
140
+ args: ["-y", "@playwright/mcp@latest"],
141
+ env: {}
142
+ }
143
+ },
144
+ {
145
+ id: "exa",
146
+ name: { "zh-CN": "Exa AI \u641C\u7D22", en: "Exa AI Search" },
147
+ description: {
148
+ "zh-CN": "\u4F7F\u7528 Exa AI \u8FDB\u884C\u7F51\u9875\u641C\u7D22",
149
+ en: "Web search using Exa AI"
150
+ },
151
+ requiresApiKey: true,
152
+ apiKeyPrompt: {
153
+ "zh-CN": "\u8BF7\u8F93\u5165 Exa API Key",
154
+ en: "Enter Exa API Key"
155
+ },
156
+ apiKeyPlaceholder: "YOUR_EXA_API_KEY",
157
+ config: {
158
+ type: "stdio",
159
+ command: "npx",
160
+ args: ["-y", "mcp-remote", "https://mcp.exa.ai/mcp?exaApiKey=YOUR_EXA_API_KEY"],
161
+ env: {}
162
+ }
163
+ }
164
+ ];
165
+
166
+ function ensureClaudeDir() {
167
+ if (!existsSync(CLAUDE_DIR)) {
168
+ mkdirSync(CLAUDE_DIR, { recursive: true });
169
+ }
170
+ }
171
+ function backupExistingConfig() {
172
+ if (!existsSync(CLAUDE_DIR)) {
173
+ return null;
174
+ }
175
+ const timestamp = dayjs().format("YYYY-MM-DD_HH-mm-ss");
176
+ const backupBaseDir = join(CLAUDE_DIR, "backup");
177
+ const backupDir = join(backupBaseDir, `backup_${timestamp}`);
178
+ mkdirSync(backupDir, { recursive: true });
179
+ const entries = readdirSync(CLAUDE_DIR);
180
+ for (const entry of entries) {
181
+ if (entry === "backup") continue;
182
+ const srcPath = join(CLAUDE_DIR, entry);
183
+ const destPath = join(backupDir, entry);
184
+ const stat = statSync(srcPath);
185
+ if (stat.isDirectory()) {
186
+ copyDirectory(srcPath, destPath);
187
+ } else {
188
+ copyFileSync(srcPath, destPath);
189
+ }
190
+ }
191
+ return backupDir;
192
+ }
193
+ function copyConfigFiles(lang, onlyMd = false) {
194
+ const currentFileUrl = new URL(import.meta.url);
195
+ const currentFilePath = currentFileUrl.pathname;
196
+ const distDir = dirname(dirname(currentFilePath));
197
+ const rootDir = dirname(distDir);
198
+ const sourceDir = join(rootDir, "templates", lang);
199
+ if (!existsSync(sourceDir)) {
200
+ throw new Error(`Template directory not found: ${sourceDir}`);
201
+ }
202
+ if (onlyMd) {
203
+ copyMdFiles(sourceDir, CLAUDE_DIR);
204
+ } else {
205
+ copyDirectory(sourceDir, CLAUDE_DIR);
206
+ }
207
+ }
208
+ function copyMdFiles(src, dest) {
209
+ if (!existsSync(dest)) {
210
+ mkdirSync(dest, { recursive: true });
211
+ }
212
+ const entries = readdirSync(src);
213
+ for (const entry of entries) {
214
+ const srcPath = join(src, entry);
215
+ const destPath = join(dest, entry);
216
+ const stat = statSync(srcPath);
217
+ if (stat.isDirectory()) {
218
+ copyMdFiles(srcPath, destPath);
219
+ } else if (entry.endsWith(".md")) {
220
+ copyFileSync(srcPath, destPath);
221
+ }
222
+ }
223
+ }
224
+ function copyDirectory(src, dest) {
225
+ if (!existsSync(dest)) {
226
+ mkdirSync(dest, { recursive: true });
227
+ }
228
+ const entries = readdirSync(src);
229
+ for (const entry of entries) {
230
+ const srcPath = join(src, entry);
231
+ const destPath = join(dest, entry);
232
+ const stat = statSync(srcPath);
233
+ if (stat.isDirectory()) {
234
+ copyDirectory(srcPath, destPath);
235
+ } else {
236
+ copyFileSync(srcPath, destPath);
237
+ }
238
+ }
239
+ }
240
+ function configureApi(apiConfig) {
241
+ if (!apiConfig) return;
242
+ let settings = {
243
+ $schema: "https://json.schemastore.org/claude-code-settings.json",
244
+ env: {},
245
+ includeCoAuthoredBy: false,
246
+ permissions: {
247
+ allow: [
248
+ "Bash(*)",
249
+ "LS(*)",
250
+ "Read(*)",
251
+ "Write(*)",
252
+ "Edit(*)",
253
+ "MultiEdit(*)",
254
+ "Glob(*)",
255
+ "Grep(*)",
256
+ "WebFetch(*)",
257
+ "WebSearch(*)",
258
+ "TodoWrite(*)",
259
+ "NotebookRead(*)",
260
+ "NotebookEdit(*)"
261
+ ],
262
+ deny: []
263
+ },
264
+ hooks: {},
265
+ model: "opus"
266
+ };
267
+ if (existsSync(SETTINGS_FILE)) {
268
+ const content = readFileSync(SETTINGS_FILE, "utf-8");
269
+ try {
270
+ const existingSettings = JSON.parse(content);
271
+ settings = { ...settings, ...existingSettings };
272
+ if (existingSettings.env) {
273
+ settings.env = { ...settings.env, ...existingSettings.env };
274
+ }
275
+ } catch (error) {
276
+ console.error("Failed to parse existing settings.json, using defaults:", error);
277
+ }
278
+ }
279
+ settings.env.ANTHROPIC_API_KEY = apiConfig.key;
280
+ settings.env.ANTHROPIC_BASE_URL = apiConfig.url;
281
+ writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
282
+ }
283
+ function mergeConfigs(sourceFile, targetFile) {
284
+ if (!existsSync(sourceFile)) return;
285
+ let target = {};
286
+ if (existsSync(targetFile)) {
287
+ const content = readFileSync(targetFile, "utf-8");
288
+ try {
289
+ target = JSON.parse(content);
290
+ } catch {
291
+ target = {};
292
+ }
293
+ }
294
+ const source = JSON.parse(readFileSync(sourceFile, "utf-8"));
295
+ const merged = deepMerge(target, source);
296
+ writeFileSync(targetFile, JSON.stringify(merged, null, 2));
297
+ }
298
+ function deepMerge(target, source) {
299
+ const result = { ...target };
300
+ for (const key in source) {
301
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
302
+ result[key] = deepMerge(result[key] || {}, source[key]);
303
+ } else {
304
+ result[key] = source[key];
305
+ }
306
+ }
307
+ return result;
308
+ }
309
+
310
+ function getPlatform() {
311
+ const p = platform();
312
+ if (p === "win32") return "windows";
313
+ if (p === "darwin") return "macos";
314
+ return "linux";
315
+ }
316
+ async function commandExists(command) {
317
+ try {
318
+ const cmd = getPlatform() === "windows" ? "where" : "which";
319
+ await exec(cmd, [command]);
320
+ return true;
321
+ } catch {
322
+ return false;
323
+ }
324
+ }
325
+
326
+ async function isClaudeCodeInstalled() {
327
+ return await commandExists("claude");
328
+ }
329
+ async function installClaudeCode(lang) {
330
+ const i18n = I18N[lang];
331
+ console.log(i18n.installing);
332
+ try {
333
+ if (!await commandExists("npm")) {
334
+ throw new Error(i18n.npmNotFound);
335
+ }
336
+ await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
337
+ console.log(`\u2714 ${i18n.installSuccess}`);
338
+ } catch (error) {
339
+ console.error(`\u2716 ${i18n.installFailed}`);
340
+ throw error;
341
+ }
342
+ }
343
+
344
+ function getMcpConfigPath() {
345
+ return MCP_CONFIG_FILE;
346
+ }
347
+ function readMcpConfig() {
348
+ if (!existsSync(MCP_CONFIG_FILE)) {
349
+ return null;
350
+ }
351
+ try {
352
+ const content = readFileSync(MCP_CONFIG_FILE, "utf-8");
353
+ return JSON.parse(content);
354
+ } catch (error) {
355
+ console.error("Failed to parse MCP config:", error);
356
+ return null;
357
+ }
358
+ }
359
+ function writeMcpConfig(config) {
360
+ const dir = dirname(MCP_CONFIG_FILE);
361
+ if (!existsSync(dir)) {
362
+ mkdirSync(dir, { recursive: true });
363
+ }
364
+ writeFileSync(MCP_CONFIG_FILE, JSON.stringify(config, null, 2));
365
+ }
366
+ function backupMcpConfig() {
367
+ if (!existsSync(MCP_CONFIG_FILE)) {
368
+ return null;
369
+ }
370
+ const timestamp = dayjs().format("YYYY-MM-DD_HH-mm-ss");
371
+ const backupBaseDir = join(CLAUDE_DIR, "backup");
372
+ const backupPath = join(backupBaseDir, `.claude.json.backup_${timestamp}.json`);
373
+ try {
374
+ if (!existsSync(backupBaseDir)) {
375
+ mkdirSync(backupBaseDir, { recursive: true });
376
+ }
377
+ const content = readFileSync(MCP_CONFIG_FILE, "utf-8");
378
+ writeFileSync(backupPath, content);
379
+ return backupPath;
380
+ } catch (error) {
381
+ console.error("Failed to backup MCP config:", error);
382
+ return null;
383
+ }
384
+ }
385
+ function mergeMcpServers(existing, newServers) {
386
+ const config = existing || { mcpServers: {} };
387
+ if (!config.mcpServers) {
388
+ config.mcpServers = {};
389
+ }
390
+ Object.assign(config.mcpServers, newServers);
391
+ return config;
392
+ }
393
+ function buildMcpServerConfig(baseConfig, apiKey, placeholder = "YOUR_EXA_API_KEY") {
394
+ if (!apiKey) {
395
+ return { ...baseConfig };
396
+ }
397
+ const config = JSON.parse(JSON.stringify(baseConfig));
398
+ if (config.args) {
399
+ config.args = config.args.map((arg) => arg.replace(placeholder, apiKey));
400
+ }
401
+ if (config.url) {
402
+ config.url = config.url.replace(placeholder, apiKey);
403
+ }
404
+ return config;
405
+ }
406
+
407
+ async function init(options = {}) {
408
+ try {
409
+ let scriptLang = options.lang;
410
+ if (!scriptLang) {
411
+ const response = await prompts({
412
+ type: "select",
413
+ name: "lang",
414
+ message: "Select script language / \u9009\u62E9\u811A\u672C\u8BED\u8A00",
415
+ choices: SUPPORTED_LANGS.map((l) => ({
416
+ title: LANG_LABELS[l],
417
+ value: l
418
+ }))
419
+ });
420
+ scriptLang = response.lang;
421
+ }
422
+ if (!scriptLang) {
423
+ console.error(ansis.red("Language not selected"));
424
+ process.exit(1);
425
+ }
426
+ const i18n = I18N[scriptLang];
427
+ let configLang = options.configLang;
428
+ if (!configLang) {
429
+ const response = await prompts({
430
+ type: "select",
431
+ name: "lang",
432
+ message: i18n.selectConfigLang,
433
+ choices: SUPPORTED_LANGS.map((l) => ({
434
+ title: `${LANG_LABELS[l]} - ${i18n.configLangHint[l]}`,
435
+ value: l
436
+ }))
437
+ });
438
+ configLang = response.lang;
439
+ }
440
+ if (!options.skipInstall) {
441
+ const installed = await isClaudeCodeInstalled();
442
+ if (!installed) {
443
+ const response = await prompts({
444
+ type: "confirm",
445
+ name: "shouldInstall",
446
+ message: i18n.installPrompt,
447
+ initial: true
448
+ });
449
+ if (response.shouldInstall) {
450
+ await installClaudeCode(scriptLang);
451
+ } else {
452
+ console.log(ansis.yellow(i18n.skip));
453
+ }
454
+ } else {
455
+ console.log(ansis.green(`\u2714 Claude Code ${i18n.installSuccess}`));
456
+ }
457
+ }
458
+ ensureClaudeDir();
459
+ let onlyUpdateDocs = false;
460
+ let action = "new";
461
+ if (existsSync(SETTINGS_FILE) && !options.force) {
462
+ const actionResponse = await prompts({
463
+ type: "select",
464
+ name: "action",
465
+ message: i18n.existingConfig,
466
+ choices: [
467
+ { title: i18n.backupAndOverwrite, value: "backup" },
468
+ { title: i18n.updateDocsOnly, value: "docs-only" },
469
+ { title: i18n.mergeConfig, value: "merge" },
470
+ { title: i18n.skip, value: "skip" }
471
+ ]
472
+ });
473
+ action = actionResponse.action;
474
+ if (action === "skip") {
475
+ console.log(ansis.yellow(i18n.skip));
476
+ return;
477
+ }
478
+ if (action === "docs-only") {
479
+ onlyUpdateDocs = true;
480
+ }
481
+ }
482
+ let apiConfig = null;
483
+ const isNewInstall = !existsSync(SETTINGS_FILE);
484
+ if (!onlyUpdateDocs && (isNewInstall || action === "backup" || action === "merge")) {
485
+ const apiResponse = await prompts({
486
+ type: "select",
487
+ name: "apiChoice",
488
+ message: i18n.configureApi,
489
+ choices: [
490
+ { title: i18n.customApi, value: "custom" },
491
+ { title: i18n.skipApi, value: "skip" }
492
+ ]
493
+ });
494
+ const apiChoice = apiResponse.apiChoice;
495
+ if (apiChoice === "custom") {
496
+ const urlResponse = await prompts({
497
+ type: "text",
498
+ name: "url",
499
+ message: i18n.enterApiUrl,
500
+ validate: (value) => {
501
+ if (!value) return "URL is required";
502
+ try {
503
+ new URL(value);
504
+ return true;
505
+ } catch {
506
+ return "Invalid URL";
507
+ }
508
+ }
509
+ });
510
+ const url = urlResponse.url;
511
+ const keyResponse = await prompts({
512
+ type: "text",
513
+ name: "key",
514
+ message: i18n.enterApiKey,
515
+ validate: (value) => !!value || "API Key is required"
516
+ });
517
+ const key = keyResponse.key;
518
+ apiConfig = { url, key };
519
+ }
520
+ }
521
+ if (action === "backup") {
522
+ const backupDir = backupExistingConfig();
523
+ if (backupDir) {
524
+ console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
525
+ }
526
+ copyConfigFiles(configLang, false);
527
+ } else if (action === "docs-only") {
528
+ const backupDir = backupExistingConfig();
529
+ if (backupDir) {
530
+ console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
531
+ }
532
+ copyConfigFiles(configLang, true);
533
+ } else if (action === "merge") {
534
+ const backupDir = backupExistingConfig();
535
+ if (backupDir) {
536
+ console.log(ansis.gray(`\u2714 ${i18n.backupSuccess}: ${backupDir}`));
537
+ }
538
+ copyConfigFiles(configLang, false);
539
+ } else if (action === "new") {
540
+ copyConfigFiles(configLang, false);
541
+ }
542
+ if (apiConfig && !onlyUpdateDocs) {
543
+ configureApi(apiConfig);
544
+ console.log(ansis.green(`\u2714 ${i18n.apiConfigSuccess}`));
545
+ }
546
+ if (!onlyUpdateDocs) {
547
+ const mcpResponse = await prompts({
548
+ type: "confirm",
549
+ name: "shouldConfigureMcp",
550
+ message: i18n.configureMcp,
551
+ initial: true
552
+ });
553
+ if (mcpResponse.shouldConfigureMcp) {
554
+ const choices = [
555
+ {
556
+ title: ansis.bold(i18n.allServices),
557
+ value: "ALL",
558
+ selected: false
559
+ },
560
+ ...MCP_SERVICES.map((service) => ({
561
+ title: `${service.name[scriptLang]} - ${ansis.gray(service.description[scriptLang])}`,
562
+ value: service.id,
563
+ selected: false
564
+ }))
565
+ ];
566
+ const selectedResponse = await prompts({
567
+ type: "multiselect",
568
+ name: "services",
569
+ message: i18n.selectMcpServices,
570
+ choices,
571
+ instructions: false,
572
+ hint: "- Space to select. Return to submit"
573
+ });
574
+ let selectedServices = selectedResponse.services || [];
575
+ if (selectedServices.includes("ALL")) {
576
+ selectedServices = MCP_SERVICES.map((s) => s.id);
577
+ }
578
+ if (selectedServices.length > 0) {
579
+ const mcpBackupPath = backupMcpConfig();
580
+ if (mcpBackupPath) {
581
+ console.log(ansis.gray(`\u2714 ${i18n.mcpBackupSuccess}: ${mcpBackupPath}`));
582
+ }
583
+ const newServers = {};
584
+ for (const serviceId of selectedServices) {
585
+ const service = MCP_SERVICES.find((s) => s.id === serviceId);
586
+ if (!service) continue;
587
+ let config = service.config;
588
+ if (service.requiresApiKey) {
589
+ const apiKeyResponse = await prompts({
590
+ type: "text",
591
+ name: "apiKey",
592
+ message: service.apiKeyPrompt[scriptLang],
593
+ validate: (value) => !!value || "API Key is required"
594
+ });
595
+ if (apiKeyResponse.apiKey) {
596
+ config = buildMcpServerConfig(service.config, apiKeyResponse.apiKey, service.apiKeyPlaceholder);
597
+ } else {
598
+ continue;
599
+ }
600
+ }
601
+ newServers[service.id] = config;
602
+ }
603
+ const existingConfig = readMcpConfig();
604
+ const mergedConfig = mergeMcpServers(existingConfig, newServers);
605
+ try {
606
+ writeMcpConfig(mergedConfig);
607
+ console.log(ansis.green(`\u2714 ${i18n.mcpConfigSuccess}`));
608
+ } catch (error) {
609
+ console.error(ansis.red(`Failed to write MCP config: ${error}`));
610
+ }
611
+ }
612
+ }
613
+ }
614
+ console.log(ansis.green(`\u2714 ${i18n.configSuccess} ${CLAUDE_DIR}`));
615
+ console.log("\n" + ansis.cyan(i18n.complete));
616
+ } catch (error) {
617
+ console.error(ansis.red(`${I18N[options.lang || "en"].error}:`), error);
618
+ process.exit(1);
619
+ }
620
+ }
621
+
622
+ export { CLAUDE_DIR as C, I18N as I, LANG_LABELS as L, MCP_CONFIG_FILE as M, SETTINGS_FILE as S, CLAUDE_MD_FILE as a, SUPPORTED_LANGS as b, commandExists as c, MCP_SERVICES as d, isClaudeCodeInstalled as e, installClaudeCode as f, getPlatform as g, ensureClaudeDir as h, init as i, backupExistingConfig as j, copyConfigFiles as k, configureApi as l, mergeConfigs as m, getMcpConfigPath as n, backupMcpConfig as o, mergeMcpServers as p, buildMcpServerConfig as q, readMcpConfig as r, writeMcpConfig as w };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "zcf",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "packageManager": "pnpm@9.0.0",
6
+ "description": "Zero-Config Claude-Code Flow - One-click configuration tool for Claude Code",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/UfoMiao/claude-code-config#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/UfoMiao/claude-code-config.git"
12
+ },
13
+ "main": "dist/index.mjs",
14
+ "module": "dist/index.mjs",
15
+ "types": "dist/index.d.mts",
16
+ "bin": {
17
+ "zcf": "bin/zcf.mjs"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "bin",
22
+ "templates"
23
+ ],
24
+ "scripts": {
25
+ "dev": "tsx ./src/cli.ts",
26
+ "build": "unbuild",
27
+ "typecheck": "tsc",
28
+ "prepublishOnly": "npm run build",
29
+ "test": "npm run build && node bin/zcf.mjs"
30
+ },
31
+ "dependencies": {
32
+ "@posva/prompts": "^2.4.4",
33
+ "ansis": "^3.3.2",
34
+ "cac": "^6.7.14",
35
+ "dayjs": "^1.11.13",
36
+ "find-up-simple": "^1.0.1",
37
+ "pathe": "^2.0.0",
38
+ "tinyexec": "^1.0.1"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.15.12",
42
+ "tsx": "^4.19.4",
43
+ "typescript": "^5.8.3",
44
+ "unbuild": "^3.5.0"
45
+ },
46
+ "keywords": [
47
+ "claude",
48
+ "claude-code",
49
+ "config",
50
+ "cli",
51
+ "setup",
52
+ "zero-config",
53
+ "zcf",
54
+ "anthropic",
55
+ "ai",
56
+ "automation",
57
+ "mcp"
58
+ ]
59
+ }