@agile-team/robot-cli 2.2.0 → 3.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/index.js CHANGED
@@ -3,15 +3,14 @@ import { readFileSync } from "fs";
3
3
  import { fileURLToPath } from "url";
4
4
  import { dirname, join } from "path";
5
5
  import chalk5 from "chalk";
6
- import gradient from "gradient-string";
7
- import inquirer2 from "inquirer";
6
+ import * as p2 from "@clack/prompts";
8
7
  import { Command } from "commander";
9
8
 
10
9
  // src/create.ts
11
10
  import fs3 from "fs-extra";
12
11
  import path3 from "path";
13
12
  import chalk3 from "chalk";
14
- import inquirer from "inquirer";
13
+ import * as p from "@clack/prompts";
15
14
  import ora from "ora";
16
15
  import { execSync as execSync2 } from "child_process";
17
16
 
@@ -25,7 +24,7 @@ import extract from "extract-zip";
25
24
  // src/config/templates.config.ts
26
25
  var TEMPLATE_CATEGORIES = {
27
26
  frontend: {
28
- name: "\u{1F3A8} \u524D\u7AEF\u9879\u76EE",
27
+ name: "\u524D\u7AEF\u9879\u76EE",
29
28
  stacks: {
30
29
  vue: {
31
30
  name: "Vue.js",
@@ -37,6 +36,7 @@ var TEMPLATE_CATEGORIES = {
37
36
  name: "Robot Admin \u5B8C\u6574\u7248",
38
37
  description: "\u5305\u542B50+\u5B8C\u6574\u793A\u4F8B\u3001\u6743\u9650\u7BA1\u7406\u3001\u56FE\u8868\u7EC4\u4EF6\u3001\u6700\u4F73\u5B9E\u8DF5\u7B49\u7B49",
39
38
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
39
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
40
40
  features: [
41
41
  "Naive UI",
42
42
  "Vue Router",
@@ -64,13 +64,9 @@ var TEMPLATE_CATEGORIES = {
64
64
  name: "Robot Monorepo",
65
65
  description: "bun workspace + Monorepo \u591A\u5305\u7BA1\u7406\u67B6\u6784",
66
66
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
67
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
67
68
  branch: "monorepo",
68
- features: [
69
- "bun workspace",
70
- "Monorepo",
71
- "\u591A\u5305\u7BA1\u7406",
72
- "\u5171\u4EAB\u7EC4\u4EF6"
73
- ],
69
+ features: ["bun workspace", "Monorepo", "\u591A\u5305\u7BA1\u7406", "\u5171\u4EAB\u7EC4\u4EF6"],
74
70
  version: "full"
75
71
  }
76
72
  }
@@ -82,26 +78,18 @@ var TEMPLATE_CATEGORIES = {
82
78
  name: "Robot MicroApp \u5FAE\u524D\u7AEF",
83
79
  description: "\u57FA\u4E8E MicroApp \u7684\u5FAE\u524D\u7AEF\u67B6\u6784\u65B9\u6848",
84
80
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
81
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
85
82
  branch: "micro-app",
86
- features: [
87
- "MicroApp",
88
- "\u5FAE\u524D\u7AEF",
89
- "\u4E3B\u5B50\u5E94\u7528",
90
- "\u8DEF\u7531\u5171\u4EAB"
91
- ],
83
+ features: ["MicroApp", "\u5FAE\u524D\u7AEF", "\u4E3B\u5B50\u5E94\u7528", "\u8DEF\u7531\u5171\u4EAB"],
92
84
  version: "full"
93
85
  },
94
86
  "robot-module-federation": {
95
87
  name: "Robot Module Federation \u6A21\u5757\u8054\u90A6",
96
88
  description: "\u57FA\u4E8E Vite Module Federation \u7684\u6A21\u5757\u8054\u90A6\u65B9\u6848",
97
89
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
90
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
98
91
  branch: "module-federation",
99
- features: [
100
- "Module Federation",
101
- "\u6A21\u5757\u8054\u90A6",
102
- "Vite",
103
- "\u8FDC\u7A0B\u6A21\u5757"
104
- ],
92
+ features: ["Module Federation", "\u6A21\u5757\u8054\u90A6", "Vite", "\u8FDC\u7A0B\u6A21\u5757"],
105
93
  version: "full"
106
94
  }
107
95
  }
@@ -135,7 +123,7 @@ var TEMPLATE_CATEGORIES = {
135
123
  }
136
124
  },
137
125
  mobile: {
138
- name: "\u{1F4F1} \u79FB\u52A8\u7AEF\u9879\u76EE",
126
+ name: "\u79FB\u52A8\u7AEF\u9879\u76EE",
139
127
  stacks: {
140
128
  uniapp: {
141
129
  name: "uni-app",
@@ -147,6 +135,7 @@ var TEMPLATE_CATEGORIES = {
147
135
  name: "Robot uni-app \u5B8C\u6574\u7248",
148
136
  description: "\u591A\u7AEF\u9002\u914D + \u63D2\u4EF6\u5E02\u573A + \u5B8C\u6574\u793A\u4F8B",
149
137
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Uniapp",
138
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_uniApp",
150
139
  features: ["\u591A\u7AEF\u53D1\u5E03", "uView UI", "\u63D2\u4EF6\u96C6\u6210"],
151
140
  version: "full"
152
141
  },
@@ -164,7 +153,7 @@ var TEMPLATE_CATEGORIES = {
164
153
  }
165
154
  },
166
155
  backend: {
167
- name: "\u{1F680} \u540E\u7AEF\u9879\u76EE",
156
+ name: "\u540E\u7AEF\u9879\u76EE",
168
157
  stacks: {
169
158
  nestjs: {
170
159
  name: "NestJS",
@@ -212,7 +201,7 @@ var TEMPLATE_CATEGORIES = {
212
201
  }
213
202
  },
214
203
  desktop: {
215
- name: "\u{1F4BB} \u684C\u9762\u7AEF\u9879\u76EE",
204
+ name: "\u684C\u9762\u7AEF\u9879\u76EE",
216
205
  stacks: {
217
206
  electron: {
218
207
  name: "Electron",
@@ -354,62 +343,123 @@ async function getCacheStats() {
354
343
  async function clearCache() {
355
344
  await fs.remove(CACHE_DIR);
356
345
  }
357
- async function tryDownload(repoUrl, branch = "main", spinner) {
358
- const url = new URL(repoUrl);
359
- const host = url.hostname;
360
- const mirrors = host === "github.com" ? [repoUrl, `https://ghproxy.com/${repoUrl}`] : [repoUrl];
361
- for (let i = 0; i < mirrors.length; i++) {
362
- const current = mirrors[i];
363
- const isOriginal = current === repoUrl;
364
- const sourceName = isOriginal ? `${host} \u5B98\u65B9` : `${host} \u955C\u50CF`;
346
+ var TIMEOUT_PRIMARY = 12e4;
347
+ var TIMEOUT_MIRROR = 6e4;
348
+ var MAX_RETRIES = 3;
349
+ function getGitHubMirrors(repoUrl, giteeUrl) {
350
+ const mirrors = [
351
+ repoUrl,
352
+ // 1. 原始 GitHub
353
+ repoUrl.replace("github.com", "hub.gitmirror.com"),
354
+ // 2. gitmirror
355
+ `https://ghproxy.net/${repoUrl}`
356
+ // 3. ghproxy.net
357
+ ];
358
+ if (giteeUrl) mirrors.push(giteeUrl);
359
+ return mirrors;
360
+ }
361
+ async function fetchWithRetry(downloadUrl, timeout, retries) {
362
+ let lastError;
363
+ for (let attempt = 1; attempt <= retries; attempt++) {
365
364
  try {
366
- if (spinner) spinner.text = `\u{1F50D} \u8FDE\u63A5\u5230 ${sourceName}...`;
367
- const downloadUrl = current.endsWith(".zip") ? current : buildDownloadUrl(current, branch);
368
- if (spinner) spinner.text = `\u{1F4E6} \u4ECE ${sourceName} \u4E0B\u8F7D\u6A21\u677F...`;
369
365
  const response = await fetch(downloadUrl, {
370
- signal: AbortSignal.timeout(isOriginal ? 15e3 : 1e4),
366
+ signal: AbortSignal.timeout(timeout),
371
367
  headers: { "User-Agent": "Robot-CLI" }
372
368
  });
373
369
  if (!response.ok) {
374
- if (response.status === 404) throw new Error(`\u4ED3\u5E93\u4E0D\u5B58\u5728: ${repoUrl}`);
370
+ if (response.status === 404) throw new Error(`\u4ED3\u5E93\u4E0D\u5B58\u5728 (404)`);
375
371
  throw new Error(`HTTP ${response.status}`);
376
372
  }
373
+ return response;
374
+ } catch (error) {
375
+ lastError = error;
376
+ if (lastError.message.includes("404")) throw lastError;
377
+ if (attempt < retries) {
378
+ await new Promise((r) => setTimeout(r, 2e3 * attempt));
379
+ }
380
+ }
381
+ }
382
+ throw lastError;
383
+ }
384
+ async function tryDownload(repoUrl, branch = "main", spinner, giteeUrl) {
385
+ const url = new URL(repoUrl);
386
+ const host = url.hostname;
387
+ const mirrors = host === "github.com" ? getGitHubMirrors(repoUrl, giteeUrl) : [repoUrl];
388
+ for (let i = 0; i < mirrors.length; i++) {
389
+ const current = mirrors[i];
390
+ const isOriginal = i === 0;
391
+ let sourceName;
392
+ try {
393
+ sourceName = isOriginal ? host : new URL(current.replace(/^(https?:\/\/[^/]+)\/.*/, "$1")).hostname;
394
+ } catch {
395
+ sourceName = `\u955C\u50CF ${i}`;
396
+ }
397
+ try {
398
+ if (spinner) spinner.text = `\u8FDE\u63A5 ${sourceName} ...`;
399
+ const downloadUrl = current.endsWith(".zip") ? current : buildDownloadUrl(current, branch);
400
+ const timeout = isOriginal ? TIMEOUT_PRIMARY : TIMEOUT_MIRROR;
401
+ if (spinner) spinner.text = `\u4ECE ${sourceName} \u4E0B\u8F7D\u6A21\u677F (\u6700\u591A\u91CD\u8BD5${MAX_RETRIES}\u6B21)...`;
402
+ const response = await fetchWithRetry(downloadUrl, timeout, MAX_RETRIES);
377
403
  if (spinner) {
378
404
  const len = response.headers.get("content-length");
379
405
  const sizeInfo = len ? `${(parseInt(len) / 1024 / 1024).toFixed(1)}MB ` : "";
380
- spinner.text = `\u{1F4E6} \u4E0B\u8F7D\u4E2D... (${sizeInfo}from ${sourceName})`;
406
+ spinner.text = `\u4E0B\u8F7D\u4E2D ${sizeInfo}(${sourceName})`;
381
407
  }
382
408
  return { response, sourceName };
383
409
  } catch (error) {
384
410
  if (i === mirrors.length - 1) throw error;
385
- if (spinner) spinner.text = `\u26A0\uFE0F ${sourceName} \u8BBF\u95EE\u5931\u8D25\uFF0C\u5C1D\u8BD5\u5176\u4ED6\u6E90...`;
411
+ if (spinner) spinner.text = `${sourceName} \u4E0D\u53EF\u7528\uFF0C\u5207\u6362\u4E0B\u4E00\u4E2A\u6E90...`;
386
412
  await new Promise((r) => setTimeout(r, 1e3));
387
413
  }
388
414
  }
389
415
  throw new Error("\u6240\u6709\u4E0B\u8F7D\u6E90\u5747\u4E0D\u53EF\u7528");
390
416
  }
391
417
  async function downloadTemplate(template, options = {}) {
392
- const { spinner, noCache } = options;
418
+ const { spinner, noCache, giteeUrl: optGiteeUrl } = options;
393
419
  const branch = template.branch || "main";
420
+ const giteeUrl = optGiteeUrl || template.giteeUrl;
394
421
  if (!template?.repoUrl) {
395
422
  throw new Error(`\u6A21\u677F\u914D\u7F6E\u65E0\u6548: ${JSON.stringify(template)}`);
396
423
  }
397
424
  try {
398
- if (spinner) spinner.text = "\u{1F310} \u5F00\u59CB\u4E0B\u8F7D\u6700\u65B0\u6A21\u677F...";
425
+ if (spinner) spinner.text = "\u5F00\u59CB\u4E0B\u8F7D\u6700\u65B0\u6A21\u677F...";
399
426
  const { response, sourceName } = await tryDownload(
400
427
  template.repoUrl,
401
428
  branch,
402
- spinner
429
+ spinner,
430
+ giteeUrl
403
431
  );
404
- if (spinner) spinner.text = "\u{1F4BE} \u4FDD\u5B58\u4E0B\u8F7D\u6587\u4EF6...";
432
+ if (spinner) spinner.text = "\u4FDD\u5B58\u4E0B\u8F7D\u6587\u4EF6...";
405
433
  const timestamp = Date.now();
406
434
  const tempZip = path.join(os.tmpdir(), `robot-template-${timestamp}.zip`);
407
435
  const tempExtract = path.join(os.tmpdir(), `robot-extract-${timestamp}`);
408
- const buffer = Buffer.from(await response.arrayBuffer());
409
- await fs.writeFile(tempZip, buffer);
410
- if (spinner) spinner.text = "\u{1F4C2} \u89E3\u538B\u6A21\u677F\u6587\u4EF6...";
436
+ const contentLength = response.headers.get("content-length");
437
+ const totalSize = contentLength ? parseInt(contentLength, 10) : 0;
438
+ if (totalSize > 0 && response.body && spinner) {
439
+ const reader = response.body.getReader();
440
+ const chunks = [];
441
+ let received = 0;
442
+ while (true) {
443
+ const { done, value } = await reader.read();
444
+ if (done) break;
445
+ chunks.push(value);
446
+ received += value.length;
447
+ const pct = Math.round(received / totalSize * 100);
448
+ const filled = Math.round(pct / 5);
449
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
450
+ const sizeMB = (received / 1024 / 1024).toFixed(1);
451
+ const totalMB = (totalSize / 1024 / 1024).toFixed(1);
452
+ spinner.text = `\u4E0B\u8F7D\u4E2D [${bar}] ${pct}% ${sizeMB}MB/${totalMB}MB (${sourceName})`;
453
+ }
454
+ const buffer = Buffer.concat(chunks);
455
+ await fs.writeFile(tempZip, buffer);
456
+ } else {
457
+ const buffer = Buffer.from(await response.arrayBuffer());
458
+ await fs.writeFile(tempZip, buffer);
459
+ }
460
+ if (spinner) spinner.text = "\u89E3\u538B\u6A21\u677F\u6587\u4EF6...";
411
461
  await extract(tempZip, { dir: tempExtract });
412
- if (spinner) spinner.text = "\u{1F50D} \u67E5\u627E\u9879\u76EE\u7ED3\u6784...";
462
+ if (spinner) spinner.text = "\u67E5\u627E\u9879\u76EE\u7ED3\u6784...";
413
463
  const extractedItems = await fs.readdir(tempExtract);
414
464
  const repoName = template.repoUrl.split("/").pop() || "";
415
465
  const projectDir = extractedItems.find(
@@ -421,7 +471,7 @@ async function downloadTemplate(template, options = {}) {
421
471
  );
422
472
  }
423
473
  const sourcePath = path.join(tempExtract, projectDir);
424
- if (spinner) spinner.text = "\u2705 \u9A8C\u8BC1\u6A21\u677F\u5B8C\u6574\u6027...";
474
+ if (spinner) spinner.text = "\u9A8C\u8BC1\u6A21\u677F\u5B8C\u6574\u6027...";
425
475
  if (!fs.existsSync(path.join(sourcePath, "package.json"))) {
426
476
  throw new Error("\u6A21\u677F\u7F3A\u5C11 package.json \u6587\u4EF6");
427
477
  }
@@ -429,7 +479,7 @@ async function downloadTemplate(template, options = {}) {
429
479
  saveToCache(template.repoUrl, sourcePath, branch).catch(() => {
430
480
  });
431
481
  }
432
- if (spinner) spinner.text = `\u{1F389} \u6A21\u677F\u4E0B\u8F7D\u5B8C\u6210 (via ${sourceName})`;
482
+ if (spinner) spinner.text = `\u6A21\u677F\u4E0B\u8F7D\u5B8C\u6210 (via ${sourceName})`;
433
483
  await fs.remove(tempZip).catch(() => {
434
484
  });
435
485
  return sourcePath;
@@ -437,9 +487,9 @@ async function downloadTemplate(template, options = {}) {
437
487
  if (!noCache) {
438
488
  const cached = await getCachedTemplate(template.repoUrl);
439
489
  if (cached) {
440
- if (spinner) spinner.text = "\u{1F4E6} \u7F51\u7EDC\u4E0D\u53EF\u7528\uFF0C\u4F7F\u7528\u7F13\u5B58\u6A21\u677F...";
490
+ if (spinner) spinner.text = "\u7F51\u7EDC\u4E0D\u53EF\u7528\uFF0C\u4F7F\u7528\u7F13\u5B58\u6A21\u677F...";
441
491
  console.log();
442
- console.log(` ${chalk.yellow("\u26A0\uFE0F \u4F7F\u7528\u7F13\u5B58\u7248\u672C\uFF08\u975E\u6700\u65B0\uFF09")}`);
492
+ console.log(` ${chalk.yellow(" \u6CE8\u610F: \u4F7F\u7528\u7F13\u5B58\u7248\u672C\uFF08\u975E\u6700\u65B0\uFF09")}`);
443
493
  return cached;
444
494
  }
445
495
  }
@@ -457,9 +507,9 @@ async function downloadTemplate(template, options = {}) {
457
507
  const isTimeout = errMsg.includes("aborted") || errMsg.includes("timeout") || downloadError.name === "TimeoutError";
458
508
  let msg = `\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25: ${errMsg}`;
459
509
  if (isTimeout) {
460
- msg += "\n\n\u{1F4A1} \u5EFA\u8BAE:\n1. \u5F53\u524D\u7F51\u7EDC\u8FDE\u63A5\u8F83\u6162\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\n2. \u5982\u679C\u5728\u56FD\u5185\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u79D1\u5B66\u4E0A\u7F51\u6216\u914D\u7F6E\u4EE3\u7406\n3. \u4F7F\u7528 --no-cache \u5F3A\u5236\u91CD\u65B0\u4E0B\u8F7D";
510
+ msg += "\n\n \u5EFA\u8BAE:\n 1. \u5F53\u524D\u7F51\u7EDC\u8FDE\u63A5\u8F83\u6162\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\n 2. \u5982\u679C\u5728\u56FD\u5185\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u4EE3\u7406\u6216\u79D1\u5B66\u4E0A\u7F51\n 3. \u4F7F\u7528 robot doctor \u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5";
461
511
  } else if (downloadError.code === "ENOTFOUND") {
462
- msg += "\n\n\u{1F4A1} \u5EFA\u8BAE:\n1. \u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\n2. \u5982\u679C\u5728\u56FD\u5185\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u79D1\u5B66\u4E0A\u7F51\n3. \u7A0D\u540E\u91CD\u8BD5";
512
+ msg += "\n\n \u5EFA\u8BAE:\n 1. \u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u662F\u5426\u6B63\u5E38\n 2. \u5982\u679C\u5728\u56FD\u5185\uFF0C\u5C1D\u8BD5\u4F7F\u7528\u4EE3\u7406\n 3. \u7A0D\u540E\u91CD\u8BD5";
463
513
  }
464
514
  throw new Error(msg);
465
515
  }
@@ -638,9 +688,9 @@ async function copyTemplate(sourcePath, targetPath, spinner) {
638
688
  throw new Error(`\u6E90\u8DEF\u5F84\u4E0D\u5B58\u5728: ${sourcePath}`);
639
689
  }
640
690
  await fs2.ensureDir(targetPath);
641
- if (spinner) spinner.text = "\u{1F4CA} \u7EDF\u8BA1\u6587\u4EF6\u6570\u91CF...";
691
+ if (spinner) spinner.text = "\u7EDF\u8BA1\u6587\u4EF6\u6570\u91CF...";
642
692
  const totalFiles = await countFiles(sourcePath);
643
- if (spinner) spinner.text = `\u{1F4CB} \u5F00\u59CB\u590D\u5236 ${totalFiles} \u4E2A\u6587\u4EF6...`;
693
+ if (spinner) spinner.text = `\u5F00\u59CB\u590D\u5236 ${totalFiles} \u4E2A\u6587\u4EF6...`;
644
694
  let copied = 0;
645
695
  const skipDirs = /* @__PURE__ */ new Set([
646
696
  "node_modules",
@@ -664,19 +714,19 @@ async function copyTemplate(sourcePath, targetPath, spinner) {
664
714
  copied++;
665
715
  if (spinner && (copied % 10 === 0 || copied === totalFiles)) {
666
716
  const pct = Math.round(copied / totalFiles * 100);
667
- spinner.text = `\u{1F4CB} \u590D\u5236\u4E2D... ${copied}/${totalFiles} (${pct}%)`;
717
+ spinner.text = `\u590D\u5236\u4E2D ${copied}/${totalFiles} (${pct}%)`;
668
718
  }
669
719
  }
670
720
  }
671
721
  }
672
722
  await copyDir(sourcePath, targetPath);
673
- if (spinner) spinner.text = `\u2705 \u6587\u4EF6\u590D\u5236\u5B8C\u6210 (${copied} \u4E2A\u6587\u4EF6)`;
723
+ if (spinner) spinner.text = `\u6587\u4EF6\u590D\u5236\u5B8C\u6210 (${copied} \u4E2A\u6587\u4EF6)`;
674
724
  }
675
725
  async function installDependencies(projectPath, spinner, packageManager = "npm") {
676
726
  try {
677
727
  const pkgJson = path2.join(projectPath, "package.json");
678
728
  if (!fs2.existsSync(pkgJson)) {
679
- if (spinner) spinner.text = "\u26A0\uFE0F \u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5 (\u65E0 package.json)";
729
+ if (spinner) spinner.text = "\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5 (\u65E0 package.json)";
680
730
  return;
681
731
  }
682
732
  const cmds = {
@@ -685,20 +735,20 @@ async function installDependencies(projectPath, spinner, packageManager = "npm")
685
735
  yarn: "yarn install",
686
736
  npm: "npm install"
687
737
  };
688
- if (spinner) spinner.text = `\u{1F4E6} \u4F7F\u7528 ${packageManager} \u5B89\u88C5\u4F9D\u8D56...`;
738
+ if (spinner) spinner.text = `\u4F7F\u7528 ${packageManager} \u5B89\u88C5\u4F9D\u8D56...`;
689
739
  execSync(cmds[packageManager], {
690
740
  cwd: projectPath,
691
741
  stdio: "ignore",
692
742
  timeout: 3e5
693
743
  });
694
- if (spinner) spinner.text = `\u2705 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210 (${packageManager})`;
744
+ if (spinner) spinner.text = `\u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210 (${packageManager})`;
695
745
  } catch (error) {
696
- if (spinner) spinner.text = "\u26A0\uFE0F \u4F9D\u8D56\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5B89\u88C5";
746
+ if (spinner) spinner.text = "\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5B89\u88C5";
697
747
  console.log();
698
- console.log(chalk2.yellow("\u26A0\uFE0F \u81EA\u52A8\u5B89\u88C5\u4F9D\u8D56\u5931\u8D25"));
748
+ console.log(chalk2.yellow("\u81EA\u52A8\u5B89\u88C5\u4F9D\u8D56\u5931\u8D25"));
699
749
  console.log(chalk2.dim(` \u9519\u8BEF: ${error.message}`));
700
750
  console.log();
701
- console.log(chalk2.blue("\u{1F4A1} \u8BF7\u624B\u52A8\u5B89\u88C5:"));
751
+ console.log(chalk2.blue("\u8BF7\u624B\u52A8\u5B89\u88C5:"));
702
752
  console.log(chalk2.cyan(` cd ${path2.basename(projectPath)}`));
703
753
  console.log(chalk2.cyan(` ${packageManager} install`));
704
754
  console.log();
@@ -738,7 +788,7 @@ async function generateProjectStats(projectPath) {
738
788
  }
739
789
  }
740
790
  function printProjectStats(stats) {
741
- console.log(chalk2.blue("\u{1F4CA} \u9879\u76EE\u7EDF\u8BA1:"));
791
+ console.log(chalk2.blue("\u9879\u76EE\u7EDF\u8BA1:"));
742
792
  console.log(` \u6587\u4EF6\u6570\u91CF: ${chalk2.cyan(String(stats.files))} \u4E2A`);
743
793
  console.log(` \u76EE\u5F55\u6570\u91CF: ${chalk2.cyan(String(stats.directories))} \u4E2A`);
744
794
  console.log(` \u9879\u76EE\u5927\u5C0F: ${chalk2.cyan(formatBytes(stats.size))}`);
@@ -759,17 +809,9 @@ function formatBytes(bytes) {
759
809
  }
760
810
 
761
811
  // src/create.ts
762
- function getVersionLabel(version) {
763
- const label = VERSION_LABELS[version] || version;
764
- if (version === "full") return chalk3.green(`[${label}]`);
765
- if (version === "micro") return chalk3.blue(`[${label}]`);
766
- return chalk3.yellow(`[${label}]`);
767
- }
768
812
  var STRIP_VERSION_RE = /\s*(完整版|精简版|微服务版)\s*$/;
769
813
  async function createProject(projectName, options = {}) {
770
- console.log();
771
- console.log(chalk3.cyan(" Robot CLI - \u5F00\u59CB\u521B\u5EFA\u9879\u76EE"));
772
- console.log();
814
+ p.intro(chalk3.bgCyan.black(" Robot CLI - \u5F00\u59CB\u521B\u5EFA\u9879\u76EE "));
773
815
  let template;
774
816
  if (options.from) {
775
817
  template = {
@@ -791,7 +833,7 @@ async function createProject(projectName, options = {}) {
791
833
  if (options.dryRun) {
792
834
  console.log();
793
835
  console.log(
794
- chalk3.yellow("\u{1F50D} Dry Run \u6A21\u5F0F - \u4EE5\u4E0B\u4E3A\u9884\u89C8\u4FE1\u606F\uFF0C\u672A\u5B9E\u9645\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C:")
836
+ chalk3.yellow("Dry Run \u6A21\u5F0F - \u4EE5\u4E0B\u4E3A\u9884\u89C8\u4FE1\u606F\uFF0C\u672A\u5B9E\u9645\u6267\u884C\u4EFB\u4F55\u64CD\u4F5C:")
795
837
  );
796
838
  console.log();
797
839
  console.log(` \u9879\u76EE\u8DEF\u5F84: ${chalk3.cyan(path3.resolve(finalProjectName))}`);
@@ -823,38 +865,33 @@ async function handleProjectName(projectName, template) {
823
865
  if (projectName) {
824
866
  const v = validateProjectName(projectName);
825
867
  if (!v.valid) {
826
- console.log(chalk3.red("\u274C \u9879\u76EE\u540D\u79F0\u4E0D\u5408\u6CD5:"));
827
- v.errors.forEach((e) => console.log(chalk3.red(` ${e}`)));
828
- console.log();
829
- const { newName } = await inquirer.prompt([
830
- {
831
- type: "input",
832
- name: "newName",
833
- message: "\u8BF7\u8F93\u5165\u65B0\u7684\u9879\u76EE\u540D\u79F0:",
834
- validate: (input) => {
835
- const r = validateProjectName(input);
836
- return r.valid || r.errors[0];
837
- }
868
+ p.log.error("\u9879\u76EE\u540D\u79F0\u4E0D\u5408\u6CD5:");
869
+ v.errors.forEach((e) => p.log.warn(` ${e}`));
870
+ const newName = await p.text({
871
+ message: "\u8BF7\u8F93\u5165\u65B0\u7684\u9879\u76EE\u540D\u79F0:",
872
+ validate: (input) => {
873
+ if (!input) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
874
+ const r = validateProjectName(input);
875
+ return r.valid ? void 0 : r.errors[0];
838
876
  }
839
- ]);
877
+ });
878
+ if (p.isCancel(newName)) process.exit(0);
840
879
  return newName;
841
880
  }
842
881
  return projectName;
843
882
  }
844
883
  const defaultName = generateDefaultProjectName(template);
845
- const { name } = await inquirer.prompt([
846
- {
847
- type: "input",
848
- name: "name",
849
- message: "\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0:",
850
- default: defaultName,
851
- validate: (input) => {
852
- if (!input.trim()) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
853
- const r = validateProjectName(input);
854
- return r.valid || r.errors[0];
855
- }
884
+ const name = await p.text({
885
+ message: "\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0:",
886
+ defaultValue: defaultName,
887
+ placeholder: defaultName,
888
+ validate: (input) => {
889
+ if (!input?.trim()) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
890
+ const r = validateProjectName(input);
891
+ return r.valid ? void 0 : r.errors[0];
856
892
  }
857
- ]);
893
+ });
894
+ if (p.isCancel(name)) process.exit(0);
858
895
  return name;
859
896
  }
860
897
  function generateDefaultProjectName(template) {
@@ -869,42 +906,22 @@ async function selectTemplate(templateOption) {
869
906
  if (all[templateOption]) {
870
907
  return { key: templateOption, ...all[templateOption] };
871
908
  }
872
- console.log(chalk3.yellow(`\u26A0\uFE0F \u6A21\u677F "${templateOption}" \u4E0D\u5B58\u5728`));
909
+ console.log(chalk3.yellow(`\u6A21\u677F "${templateOption}" \u4E0D\u5B58\u5728`));
873
910
  console.log();
874
911
  }
875
912
  return await selectTemplateMethod();
876
913
  }
877
914
  async function selectTemplateMethod() {
878
- console.log();
879
- console.log(chalk3.bold(" \u9009\u62E9\u6A21\u677F\u521B\u5EFA\u65B9\u5F0F"));
880
- console.log(chalk3.dim(" \u8BF7\u9009\u62E9\u6700\u9002\u5408\u4F60\u7684\u6A21\u677F\u6D4F\u89C8\u65B9\u5F0F"));
881
- console.log();
882
- const { selectionMode } = await inquirer.prompt([
883
- {
884
- type: "list",
885
- name: "selectionMode",
886
- message: "\u6A21\u677F\u9009\u62E9\u65B9\u5F0F:",
887
- choices: [
888
- {
889
- name: `${chalk3.cyan(">")} ${chalk3.bold("\u63A8\u8350\u6A21\u677F")} ${chalk3.dim("\u2014 \u57FA\u4E8E\u56E2\u961F\u4F7F\u7528\u9891\u7387\u63A8\u8350\u7684\u70ED\u95E8\u6A21\u677F")}`,
890
- value: "recommended"
891
- },
892
- {
893
- name: `${chalk3.cyan(">")} ${chalk3.bold("\u5206\u7C7B\u6D4F\u89C8")} ${chalk3.dim("\u2014 \u524D\u7AEF\u3001\u540E\u7AEF\u3001\u79FB\u52A8\u7AEF\u3001\u684C\u9762\u7AEF\u5206\u7C7B")}`,
894
- value: "category"
895
- },
896
- {
897
- name: `${chalk3.cyan(">")} ${chalk3.bold("\u5173\u952E\u8BCD\u641C\u7D22")} ${chalk3.dim("\u2014 \u6309\u6280\u672F\u6808\u3001\u529F\u80FD\u7279\u6027\u67E5\u627E")}`,
898
- value: "search"
899
- },
900
- {
901
- name: `${chalk3.cyan(">")} ${chalk3.bold("\u5168\u90E8\u6A21\u677F")} ${chalk3.dim("\u2014 \u67E5\u770B\u6240\u6709\u53EF\u7528\u6A21\u677F")}`,
902
- value: "all"
903
- }
904
- ],
905
- pageSize: 10
906
- }
907
- ]);
915
+ const selectionMode = await p.select({
916
+ message: "\u6A21\u677F\u9009\u62E9\u65B9\u5F0F:",
917
+ options: [
918
+ { value: "recommended", label: "\u63A8\u8350\u6A21\u677F", hint: "\u57FA\u4E8E\u56E2\u961F\u4F7F\u7528\u9891\u7387\u63A8\u8350\u7684\u70ED\u95E8\u6A21\u677F" },
919
+ { value: "category", label: "\u5206\u7C7B\u6D4F\u89C8", hint: "\u524D\u7AEF\u3001\u540E\u7AEF\u3001\u79FB\u52A8\u7AEF\u3001\u684C\u9762\u7AEF\u5206\u7C7B" },
920
+ { value: "search", label: "\u5173\u952E\u8BCD\u641C\u7D22", hint: "\u6309\u6280\u672F\u6808\u3001\u529F\u80FD\u7279\u6027\u67E5\u627E" },
921
+ { value: "all", label: "\u5168\u90E8\u6A21\u677F", hint: "\u67E5\u770B\u6240\u6709\u53EF\u7528\u6A21\u677F" }
922
+ ]
923
+ });
924
+ if (p.isCancel(selectionMode)) process.exit(0);
908
925
  switch (selectionMode) {
909
926
  case "recommended":
910
927
  return await selectFromRecommended();
@@ -921,400 +938,258 @@ async function selectTemplateMethod() {
921
938
  async function selectFromRecommended() {
922
939
  const recommended = getRecommendedTemplates();
923
940
  if (Object.keys(recommended).length === 0) {
924
- console.log(chalk3.yellow("\u26A0\uFE0F \u6682\u65E0\u63A8\u8350\u6A21\u677F"));
941
+ p.log.warn("\u6682\u65E0\u63A8\u8350\u6A21\u677F");
925
942
  return await selectTemplateMethod();
926
943
  }
927
- console.log();
928
- console.log(chalk3.bold(" \u63A8\u8350\u6A21\u677F"));
929
- console.log(chalk3.dim(" \u57FA\u4E8E\u56E2\u961F\u4F7F\u7528\u9891\u7387\u548C\u9879\u76EE\u6210\u719F\u5EA6\u63A8\u8350"));
930
- console.log();
931
- const choices = [];
932
- const entries = Object.entries(recommended);
933
- entries.forEach(([key, template], index) => {
934
- const tags = template.features.slice(0, 3).map((f) => chalk3.dim(`[${f}]`)).join(" ");
935
- const ver = getVersionLabel(template.version);
936
- choices.push({
937
- name: `${chalk3.bold.white(template.name.replace(STRIP_VERSION_RE, ""))} ${ver} - ${chalk3.dim(template.description)}
938
- ${tags}${template.features.length > 3 ? chalk3.dim(` +${template.features.length - 3}more`) : ""}`,
939
- value: { key, ...template },
940
- short: template.name
944
+ p.log.info(chalk3.bold("\u63A8\u8350\u6A21\u677F") + chalk3.dim(" \u2014 \u57FA\u4E8E\u56E2\u961F\u4F7F\u7528\u9891\u7387\u548C\u9879\u76EE\u6210\u719F\u5EA6\u63A8\u8350"));
945
+ const options = [];
946
+ for (const [key, template] of Object.entries(recommended)) {
947
+ const ver = VERSION_LABELS[template.version] || template.version;
948
+ const tags = template.features.slice(0, 3).join(", ");
949
+ options.push({
950
+ value: key,
951
+ label: `${template.name.replace(STRIP_VERSION_RE, "")} [${ver}]`,
952
+ hint: `${template.description} | ${tags}`
941
953
  });
942
- if (index < entries.length - 1) {
943
- choices.push({
944
- name: chalk3.dim("\u2500".repeat(70)),
945
- value: `sep_${index}`,
946
- disabled: true
947
- });
948
- }
954
+ }
955
+ options.push({ value: "back", label: "<- \u8FD4\u56DE\u9009\u62E9\u5176\u4ED6\u65B9\u5F0F" });
956
+ const selected = await p.select({
957
+ message: "\u9009\u62E9\u63A8\u8350\u6A21\u677F:",
958
+ options
949
959
  });
950
- choices.push({ name: chalk3.dim(" <- \u8FD4\u56DE\u9009\u62E9\u5176\u4ED6\u65B9\u5F0F"), value: "back" });
951
- const { selectedTemplate } = await inquirer.prompt([
952
- {
953
- type: "list",
954
- name: "selectedTemplate",
955
- message: "\u9009\u62E9\u63A8\u8350\u6A21\u677F:",
956
- choices,
957
- pageSize: 15,
958
- loop: false
959
- }
960
- ]);
961
- if (selectedTemplate === "back") return await selectTemplateMethod();
962
- return selectedTemplate;
960
+ if (p.isCancel(selected)) process.exit(0);
961
+ if (selected === "back") return await selectTemplateMethod();
962
+ const t = recommended[selected];
963
+ return { key: selected, ...t };
963
964
  }
964
965
  async function selectByCategory() {
965
966
  while (true) {
966
- const cat = await selectCategory();
967
- if (cat === "back_to_method") return await selectTemplateMethod();
968
- const stack = await selectStack(cat);
969
- if (stack === "back_to_category") continue;
970
- if (stack === "back_to_method") return await selectTemplateMethod();
971
- const pattern = await selectPattern(cat, stack);
972
- if (pattern === "back_to_stack" || pattern === "back_to_category") continue;
973
- if (pattern === "back_to_method") return await selectTemplateMethod();
974
- const tpl = await selectSpecificTemplate(cat, stack, pattern);
975
- if (typeof tpl === "string") continue;
976
- return tpl;
977
- }
978
- }
979
- async function selectCategory() {
980
- const choices = Object.entries(TEMPLATE_CATEGORIES).map(([key, c]) => ({
981
- name: c.name,
982
- value: key
983
- }));
984
- choices.push({
985
- name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"),
986
- value: "back_to_method"
987
- });
988
- const { categoryKey } = await inquirer.prompt([
989
- { type: "list", name: "categoryKey", message: "\u8BF7\u9009\u62E9\u9879\u76EE\u7C7B\u578B:", choices }
990
- ]);
991
- return categoryKey;
992
- }
993
- async function selectStack(categoryKey) {
994
- const category = TEMPLATE_CATEGORIES[categoryKey];
995
- const choices = Object.entries(category.stacks).map(([key, s]) => ({
996
- name: s.name,
997
- value: key
998
- }));
999
- choices.push(
1000
- {
1001
- name: chalk3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1002
- value: "separator",
1003
- disabled: true
1004
- },
1005
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u9879\u76EE\u7C7B\u578B\u9009\u62E9"), value: "back_to_category" },
1006
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_method" }
1007
- );
1008
- if (choices.length === 4) return choices[0].value;
1009
- const { stackKey } = await inquirer.prompt([
1010
- { type: "list", name: "stackKey", message: "\u8BF7\u9009\u62E9\u6280\u672F\u6808:", choices }
1011
- ]);
1012
- return stackKey;
1013
- }
1014
- async function selectPattern(catKey, stackKey) {
1015
- if (["back_to_category", "back_to_method"].includes(stackKey))
1016
- return stackKey;
1017
- const stack = TEMPLATE_CATEGORIES[catKey].stacks[stackKey];
1018
- const choices = Object.entries(stack.patterns).map(([key, p]) => ({
1019
- name: p.name,
1020
- value: key
1021
- }));
1022
- choices.push(
1023
- {
1024
- name: chalk3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1025
- value: "separator",
1026
- disabled: true
1027
- },
1028
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6280\u672F\u6808\u9009\u62E9"), value: "back_to_stack" },
1029
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u9879\u76EE\u7C7B\u578B\u9009\u62E9"), value: "back_to_category" },
1030
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_method" }
1031
- );
1032
- if (choices.length === 5) return choices[0].value;
1033
- const { patternKey } = await inquirer.prompt([
1034
- { type: "list", name: "patternKey", message: "\u8BF7\u9009\u62E9\u67B6\u6784\u6A21\u5F0F:", choices }
1035
- ]);
1036
- return patternKey;
1037
- }
1038
- async function selectSpecificTemplate(catKey, stackKey, patternKey) {
1039
- if (["back_to_stack", "back_to_category", "back_to_method"].includes(patternKey)) {
1040
- return patternKey;
1041
- }
1042
- const templates = getTemplatesByCategory(catKey, stackKey, patternKey);
1043
- const choices = Object.entries(templates).map(([key, t]) => ({
1044
- name: `${t.name} - ${chalk3.dim(t.description)}`,
1045
- value: { key, ...t },
1046
- short: t.name
1047
- }));
1048
- choices.push(
1049
- {
1050
- name: chalk3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1051
- value: "separator",
1052
- disabled: true
1053
- },
1054
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u67B6\u6784\u6A21\u5F0F\u9009\u62E9"), value: "back_to_pattern" },
1055
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6280\u672F\u6808\u9009\u62E9"), value: "back_to_stack" },
1056
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u9879\u76EE\u7C7B\u578B\u9009\u62E9"), value: "back_to_category" },
1057
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_method" }
1058
- );
1059
- const { selectedTemplate } = await inquirer.prompt([
1060
- {
1061
- type: "list",
1062
- name: "selectedTemplate",
1063
- message: "\u8BF7\u9009\u62E9\u6A21\u677F\u7248\u672C:",
1064
- choices
967
+ const catOptions = Object.entries(TEMPLATE_CATEGORIES).map(([key, c]) => ({
968
+ value: key,
969
+ label: c.name
970
+ }));
971
+ catOptions.push({ value: "back_to_method", label: "<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F" });
972
+ const catKey = await p.select({
973
+ message: "\u8BF7\u9009\u62E9\u9879\u76EE\u7C7B\u578B:",
974
+ options: catOptions
975
+ });
976
+ if (p.isCancel(catKey)) process.exit(0);
977
+ if (catKey === "back_to_method") return await selectTemplateMethod();
978
+ const category = TEMPLATE_CATEGORIES[catKey];
979
+ const stackEntries = Object.entries(category.stacks);
980
+ let stackKey;
981
+ if (stackEntries.length === 1) {
982
+ stackKey = stackEntries[0][0];
983
+ } else {
984
+ const stackOptions = stackEntries.map(([key, s]) => ({
985
+ value: key,
986
+ label: s.name
987
+ }));
988
+ stackOptions.push({ value: "back", label: "<- \u8FD4\u56DE" });
989
+ const sk = await p.select({
990
+ message: "\u8BF7\u9009\u62E9\u6280\u672F\u6808:",
991
+ options: stackOptions
992
+ });
993
+ if (p.isCancel(sk)) process.exit(0);
994
+ if (sk === "back") continue;
995
+ stackKey = sk;
1065
996
  }
1066
- ]);
1067
- return selectedTemplate;
997
+ const stack = category.stacks[stackKey];
998
+ const patternEntries = Object.entries(stack.patterns);
999
+ let patternKey;
1000
+ if (patternEntries.length === 1) {
1001
+ patternKey = patternEntries[0][0];
1002
+ } else {
1003
+ const patternOptions = patternEntries.map(([key, pt]) => ({
1004
+ value: key,
1005
+ label: pt.name
1006
+ }));
1007
+ patternOptions.push({ value: "back", label: "<- \u8FD4\u56DE" });
1008
+ const pk = await p.select({
1009
+ message: "\u8BF7\u9009\u62E9\u67B6\u6784\u6A21\u5F0F:",
1010
+ options: patternOptions
1011
+ });
1012
+ if (p.isCancel(pk)) process.exit(0);
1013
+ if (pk === "back") continue;
1014
+ patternKey = pk;
1015
+ }
1016
+ const templates = getTemplatesByCategory(catKey, stackKey, patternKey);
1017
+ const tplOptions = Object.entries(templates).map(([key, t2]) => {
1018
+ const ver = VERSION_LABELS[t2.version] || t2.version;
1019
+ return {
1020
+ value: key,
1021
+ label: `${t2.name} [${ver}]`,
1022
+ hint: t2.description
1023
+ };
1024
+ });
1025
+ tplOptions.push({ value: "back", label: "<- \u8FD4\u56DE", hint: "" });
1026
+ const tplKey = await p.select({
1027
+ message: "\u8BF7\u9009\u62E9\u6A21\u677F\u7248\u672C:",
1028
+ options: tplOptions
1029
+ });
1030
+ if (p.isCancel(tplKey)) process.exit(0);
1031
+ if (tplKey === "back") continue;
1032
+ const t = templates[tplKey];
1033
+ return { key: tplKey, ...t };
1034
+ }
1068
1035
  }
1069
1036
  async function selectBySearch() {
1070
1037
  while (true) {
1071
- const { keyword } = await inquirer.prompt([
1072
- {
1073
- type: "input",
1074
- name: "keyword",
1075
- message: "\u8BF7\u8F93\u5165\u641C\u7D22\u5173\u952E\u8BCD (\u540D\u79F0\u3001\u63CF\u8FF0\u3001\u6280\u672F\u6808):",
1076
- validate: (input) => input.trim() ? true : "\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A"
1077
- }
1078
- ]);
1038
+ const keyword = await p.text({
1039
+ message: "\u8BF7\u8F93\u5165\u641C\u7D22\u5173\u952E\u8BCD (\u540D\u79F0\u3001\u63CF\u8FF0\u3001\u6280\u672F\u6808):",
1040
+ validate: (input) => input?.trim() ? void 0 : "\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A"
1041
+ });
1042
+ if (p.isCancel(keyword)) return await selectTemplateMethod();
1079
1043
  const results = searchTemplates(keyword);
1080
1044
  if (Object.keys(results).length === 0) {
1081
- console.log();
1082
- console.log(chalk3.yellow("\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6A21\u677F"));
1083
- console.log(chalk3.dim(`\u641C\u7D22\u5173\u952E\u8BCD: "${keyword}"`));
1084
- console.log();
1085
- const { action } = await inquirer.prompt([
1086
- {
1087
- type: "list",
1088
- name: "action",
1089
- message: "\u8BF7\u9009\u62E9\u4E0B\u4E00\u6B65\u64CD\u4F5C:",
1090
- choices: [
1091
- { name: "\u91CD\u65B0\u641C\u7D22", value: "retry" },
1092
- { name: chalk3.dim("<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back" }
1093
- ]
1094
- }
1095
- ]);
1096
- if (action === "retry") continue;
1097
- return await selectTemplateMethod();
1045
+ p.log.warn(`\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6A21\u677F (\u5173\u952E\u8BCD: "${keyword}")`);
1046
+ const action = await p.select({
1047
+ message: "\u8BF7\u9009\u62E9\u4E0B\u4E00\u6B65\u64CD\u4F5C:",
1048
+ options: [
1049
+ { value: "retry", label: "\u91CD\u65B0\u641C\u7D22" },
1050
+ { value: "back", label: "<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F" }
1051
+ ]
1052
+ });
1053
+ if (p.isCancel(action) || action === "back") return await selectTemplateMethod();
1054
+ continue;
1098
1055
  }
1099
- console.log();
1100
- console.log(chalk3.bold(" \u641C\u7D22\u7ED3\u679C"));
1101
- console.log(
1102
- chalk3.dim(
1103
- `\u5173\u952E\u8BCD: "${keyword}" \u2022 \u627E\u5230 ${Object.keys(results).length} \u4E2A\u5339\u914D\u6A21\u677F`
1104
- )
1056
+ p.log.info(`\u5173\u952E\u8BCD: "${keyword}" -- \u627E\u5230 ${Object.keys(results).length} \u4E2A\u5339\u914D\u6A21\u677F`);
1057
+ const options = [];
1058
+ for (const [key, t2] of Object.entries(results)) {
1059
+ const ver = VERSION_LABELS[t2.version] || t2.version;
1060
+ const info = t2.features.slice(0, 3).join(", ");
1061
+ options.push({
1062
+ value: key,
1063
+ label: `${t2.name.replace(STRIP_VERSION_RE, "")} [${ver}]`,
1064
+ hint: `${t2.description} | ${info}`
1065
+ });
1066
+ }
1067
+ options.push(
1068
+ { value: "search_again", label: "\u91CD\u65B0\u641C\u7D22" },
1069
+ { value: "back_to_mode", label: "<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F" }
1105
1070
  );
1106
- console.log();
1107
- const choices = Object.entries(results).map(([key, t]) => {
1108
- const hl = (text) => text.replace(
1109
- new RegExp(`(${keyword})`, "gi"),
1110
- chalk3.bgYellow.black("$1")
1111
- );
1112
- const ver = getVersionLabel(t.version);
1113
- const info = t.features.slice(0, 2).join(" \u2022 ");
1114
- return {
1115
- name: `${chalk3.bold(hl(t.name.replace(STRIP_VERSION_RE, "")))} ${ver}
1116
- ${chalk3.dim(hl(t.description))}
1117
- ${chalk3.dim(`${info} \u2022 key: ${key}`)}
1118
- ${chalk3.dim("\u2500".repeat(60))}`,
1119
- value: { key, ...t },
1120
- short: t.name
1121
- };
1071
+ const selected = await p.select({
1072
+ message: "\u9009\u62E9\u6A21\u677F:",
1073
+ options
1122
1074
  });
1123
- choices.push(
1124
- { name: chalk3.dim("\u2501".repeat(70)), value: "separator", disabled: true },
1125
- { name: "\u91CD\u65B0\u641C\u7D22", value: "search_again" },
1126
- { name: chalk3.dim("<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_mode" }
1127
- );
1128
- const { selectedTemplate } = await inquirer.prompt([
1129
- {
1130
- type: "list",
1131
- name: "selectedTemplate",
1132
- message: "\u9009\u62E9\u6A21\u677F:",
1133
- choices,
1134
- pageSize: 15,
1135
- loop: false
1136
- }
1137
- ]);
1138
- if (selectedTemplate === "search_again") continue;
1139
- if (selectedTemplate === "back_to_mode")
1140
- return await selectTemplateMethod();
1141
- return selectedTemplate;
1075
+ if (p.isCancel(selected)) process.exit(0);
1076
+ if (selected === "search_again") continue;
1077
+ if (selected === "back_to_mode") return await selectTemplateMethod();
1078
+ const t = results[selected];
1079
+ return { key: selected, ...t };
1142
1080
  }
1143
1081
  }
1144
1082
  async function selectFromAll() {
1145
1083
  const allTemplates = getAllTemplates();
1146
- console.log();
1147
- console.log(chalk3.bold(" \u6240\u6709\u53EF\u7528\u6A21\u677F"));
1148
- console.log(chalk3.dim(` \u5171 ${Object.keys(allTemplates).length} \u4E2A\u6A21\u677F\u53EF\u9009`));
1149
- console.log();
1150
- const categorizedChoices = [];
1084
+ p.log.info(chalk3.bold("\u6240\u6709\u53EF\u7528\u6A21\u677F") + chalk3.dim(` -- \u5171 ${Object.keys(allTemplates).length} \u4E2A\u6A21\u677F\u53EF\u9009`));
1085
+ const options = [];
1151
1086
  for (const [_catKey, category] of Object.entries(TEMPLATE_CATEGORIES)) {
1152
- categorizedChoices.push({
1153
- name: chalk3.yellow.bold(category.name),
1154
- value: `${_catKey}_header`,
1155
- disabled: true
1156
- });
1157
1087
  for (const [_sKey, stack] of Object.entries(category.stacks)) {
1158
1088
  for (const _pattern of Object.values(stack.patterns)) {
1159
- for (const [key, t] of Object.entries(_pattern.templates)) {
1160
- const ver = getVersionLabel(t.version);
1161
- categorizedChoices.push({
1162
- name: ` \u25CF ${chalk3.bold(t.name.replace(STRIP_VERSION_RE, ""))} ${ver} - ${chalk3.dim(t.description)}
1163
- ${chalk3.dim(`\u6280\u672F\u6808: ${stack.name} \u2022 \u547D\u4EE4: robot create my-app -t ${key}`)}`,
1164
- value: { key, ...t },
1165
- short: t.name
1166
- });
1167
- categorizedChoices.push({
1168
- name: chalk3.dim(" " + "\u2500".repeat(66)),
1169
- value: `sep_${key}`,
1170
- disabled: true
1089
+ for (const [key, t2] of Object.entries(_pattern.templates)) {
1090
+ const ver = VERSION_LABELS[t2.version] || t2.version;
1091
+ options.push({
1092
+ value: key,
1093
+ label: `${t2.name.replace(STRIP_VERSION_RE, "")} [${ver}]`,
1094
+ hint: `${category.name} > ${stack.name} | ${t2.description}`
1171
1095
  });
1172
1096
  }
1173
1097
  }
1174
1098
  }
1175
1099
  }
1176
- categorizedChoices.push(
1177
- { name: chalk3.dim("\u2501".repeat(70)), value: "separator", disabled: true },
1178
- { name: chalk3.dim("<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_mode" }
1179
- );
1180
- const { selectedTemplate } = await inquirer.prompt([
1181
- {
1182
- type: "list",
1183
- name: "selectedTemplate",
1184
- message: "\u9009\u62E9\u6A21\u677F:",
1185
- choices: categorizedChoices,
1186
- pageSize: 25,
1187
- loop: false
1188
- }
1189
- ]);
1190
- if (selectedTemplate === "back_to_mode") return await selectTemplateMethod();
1191
- return selectedTemplate;
1100
+ options.push({ value: "back_to_mode", label: "<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F" });
1101
+ const selected = await p.select({
1102
+ message: "\u9009\u62E9\u6A21\u677F:",
1103
+ options
1104
+ });
1105
+ if (p.isCancel(selected)) process.exit(0);
1106
+ if (selected === "back_to_mode") return await selectTemplateMethod();
1107
+ const t = allTemplates[selected];
1108
+ return { key: selected, ...t };
1192
1109
  }
1193
1110
  async function configureProject(options) {
1194
- console.log();
1195
- console.log(chalk3.bold(" \u9879\u76EE\u914D\u7F6E"));
1196
- console.log();
1111
+ p.log.step(chalk3.bold("\u9879\u76EE\u914D\u7F6E"));
1197
1112
  const available = detectPackageManager();
1198
1113
  const hasBun = available.includes("bun");
1199
1114
  const hasPnpm = available.includes("pnpm");
1200
- const managerChoices = [];
1201
- if (available.includes("bun"))
1202
- managerChoices.push({
1203
- name: "bun (\u63A8\u8350 - \u6781\u901F\u5B89\u88C5\uFF0C\u73B0\u4EE3\u5316\uFF0C\u6027\u80FD\u6700\u4F73)",
1204
- value: "bun"
1205
- });
1206
- if (available.includes("pnpm"))
1207
- managerChoices.push({
1208
- name: "pnpm (\u63A8\u8350 - \u5FEB\u901F\u5B89\u88C5\uFF0C\u8282\u7701\u78C1\u76D8\u7A7A\u95F4)",
1209
- value: "pnpm"
1210
- });
1211
- if (available.includes("yarn"))
1212
- managerChoices.push({
1213
- name: "yarn (\u517C\u5BB9\u6027\u597D - \u9002\u7528\u4E8E\u73B0\u6709yarn\u9879\u76EE)",
1214
- value: "yarn"
1215
- });
1216
- if (available.includes("npm"))
1217
- managerChoices.push({
1218
- name: "npm (\u9ED8\u8BA4 - Node.js\u5185\u7F6E\uFF0C\u517C\u5BB9\u6027\u6700\u597D)",
1219
- value: "npm"
1220
- });
1221
- if (managerChoices.length === 0) {
1222
- managerChoices.push(
1223
- { name: "npm (\u9ED8\u8BA4)", value: "npm" },
1224
- { name: "bun (\u5982\u5DF2\u5B89\u88C5)", value: "bun" },
1225
- { name: "pnpm (\u5982\u5DF2\u5B89\u88C5)", value: "pnpm" },
1226
- { name: "yarn (\u5982\u5DF2\u5B89\u88C5)", value: "yarn" }
1227
- );
1228
- }
1229
- const config = await inquirer.prompt([
1230
- {
1231
- type: "confirm",
1232
- name: "initGit",
1233
- message: "\u662F\u5426\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
1234
- default: true
1235
- },
1236
- {
1237
- type: "confirm",
1238
- name: "installDeps",
1239
- message: "\u662F\u5426\u7ACB\u5373\u5B89\u88C5\u4F9D\u8D56?",
1240
- default: !options.skipInstall
1241
- },
1242
- {
1243
- type: "list",
1244
- name: "packageManager",
1245
- message: "\u9009\u62E9\u5305\u7BA1\u7406\u5668:",
1246
- choices: managerChoices,
1247
- default: hasBun ? "bun" : hasPnpm ? "pnpm" : "npm",
1248
- when: (a) => a.installDeps
1249
- },
1250
- {
1251
- type: "input",
1252
- name: "description",
1253
- message: "\u9879\u76EE\u63CF\u8FF0 (\u53EF\u9009):",
1254
- default: ""
1255
- },
1256
- {
1257
- type: "input",
1258
- name: "author",
1259
- message: "\u4F5C\u8005 (\u53EF\u9009):",
1260
- default: getGitUser()
1261
- },
1262
- {
1263
- type: "confirm",
1264
- name: "confirmConfig",
1265
- message: "\u786E\u8BA4\u4EE5\u4E0A\u914D\u7F6E?",
1266
- default: true
1115
+ const initGit = await p.confirm({
1116
+ message: "\u662F\u5426\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
1117
+ initialValue: true
1118
+ });
1119
+ if (p.isCancel(initGit)) process.exit(0);
1120
+ const installDeps = await p.confirm({
1121
+ message: "\u662F\u5426\u7ACB\u5373\u5B89\u88C5\u4F9D\u8D56?",
1122
+ initialValue: !options.skipInstall
1123
+ });
1124
+ if (p.isCancel(installDeps)) process.exit(0);
1125
+ let packageManager = hasBun ? "bun" : hasPnpm ? "pnpm" : "npm";
1126
+ if (installDeps) {
1127
+ const managerOptions = [];
1128
+ if (available.includes("bun"))
1129
+ managerOptions.push({ value: "bun", label: "bun", hint: "\u63A8\u8350 - \u6781\u901F\u5B89\u88C5" });
1130
+ if (available.includes("pnpm"))
1131
+ managerOptions.push({ value: "pnpm", label: "pnpm", hint: "\u63A8\u8350 - \u8282\u7701\u78C1\u76D8\u7A7A\u95F4" });
1132
+ if (available.includes("yarn"))
1133
+ managerOptions.push({ value: "yarn", label: "yarn", hint: "\u517C\u5BB9\u6027\u597D" });
1134
+ if (available.includes("npm"))
1135
+ managerOptions.push({ value: "npm", label: "npm", hint: "Node.js \u5185\u7F6E" });
1136
+ if (managerOptions.length === 0) {
1137
+ managerOptions.push(
1138
+ { value: "npm", label: "npm" },
1139
+ { value: "bun", label: "bun" },
1140
+ { value: "pnpm", label: "pnpm" },
1141
+ { value: "yarn", label: "yarn" }
1142
+ );
1267
1143
  }
1268
- ]);
1269
- if (!config.confirmConfig) {
1270
- const { action } = await inquirer.prompt([
1271
- {
1272
- type: "list",
1273
- name: "action",
1274
- message: "\u8BF7\u9009\u62E9\u64CD\u4F5C:",
1275
- choices: [
1276
- { name: "\u91CD\u65B0\u914D\u7F6E", value: "reconfigure" },
1277
- { name: "\u53D6\u6D88\u521B\u5EFA", value: "cancel" }
1278
- ]
1279
- }
1280
- ]);
1281
- if (action === "reconfigure") return await configureProject(options);
1282
- console.log(chalk3.yellow("\u53D6\u6D88\u521B\u5EFA\u9879\u76EE"));
1283
- process.exit(0);
1144
+ const pm = await p.select({
1145
+ message: "\u9009\u62E9\u5305\u7BA1\u7406\u5668:",
1146
+ options: managerOptions,
1147
+ initialValue: hasBun ? "bun" : hasPnpm ? "pnpm" : "npm"
1148
+ });
1149
+ if (p.isCancel(pm)) process.exit(0);
1150
+ packageManager = pm;
1284
1151
  }
1285
- return config;
1152
+ const description = await p.text({
1153
+ message: "\u9879\u76EE\u63CF\u8FF0 (\u53EF\u9009):",
1154
+ defaultValue: "",
1155
+ placeholder: "\u8F93\u5165\u9879\u76EE\u63CF\u8FF0\u6216\u76F4\u63A5\u56DE\u8F66\u8DF3\u8FC7"
1156
+ });
1157
+ if (p.isCancel(description)) process.exit(0);
1158
+ const author = await p.text({
1159
+ message: "\u4F5C\u8005 (\u53EF\u9009):",
1160
+ defaultValue: getGitUser(),
1161
+ placeholder: getGitUser() || "\u8F93\u5165\u4F5C\u8005\u540D"
1162
+ });
1163
+ if (p.isCancel(author)) process.exit(0);
1164
+ return {
1165
+ initGit,
1166
+ installDeps,
1167
+ packageManager,
1168
+ description,
1169
+ author
1170
+ };
1286
1171
  }
1287
1172
  async function confirmCreation(projectName, template, config) {
1288
- console.log();
1289
- console.log(chalk3.bold(" \u9879\u76EE\u521B\u5EFA\u4FE1\u606F\u786E\u8BA4:"));
1290
- console.log();
1291
- console.log(` \u9879\u76EE\u540D\u79F0: ${chalk3.cyan(projectName)}`);
1292
- console.log(` \u9009\u62E9\u6A21\u677F: ${chalk3.cyan(template.name)}`);
1293
- console.log(` \u6A21\u677F\u63CF\u8FF0: ${chalk3.dim(template.description)}`);
1294
- console.log(
1295
- ` \u5305\u542B\u529F\u80FD: ${chalk3.dim(template.features.join(", ") || "\u81EA\u5B9A\u4E49\u6A21\u677F")}`
1296
- );
1297
- if (config.description)
1298
- console.log(` \u9879\u76EE\u63CF\u8FF0: ${chalk3.dim(config.description)}`);
1299
- if (config.author) console.log(` \u4F5C\u3000\u3000\u8005: ${chalk3.dim(config.author)}`);
1300
- console.log(
1301
- ` \u521D\u59CB\u5316Git: ${config.initGit ? chalk3.green("\u662F") : chalk3.dim("\u5426")}`
1302
- );
1303
- console.log(
1304
- ` \u5B89\u88C5\u4F9D\u8D56: ${config.installDeps ? chalk3.green("\u662F") + chalk3.dim(` (${config.packageManager})`) : chalk3.dim("\u5426")}`
1173
+ p.note(
1174
+ [
1175
+ `${chalk3.dim("\u9879\u76EE\u540D\u79F0:")} ${chalk3.cyan(projectName)}`,
1176
+ `${chalk3.dim("\u9009\u62E9\u6A21\u677F:")} ${chalk3.cyan(template.name)}`,
1177
+ `${chalk3.dim("\u6A21\u677F\u63CF\u8FF0:")} ${template.description}`,
1178
+ `${chalk3.dim("\u5305\u542B\u529F\u80FD:")} ${template.features.join(", ") || "\u81EA\u5B9A\u4E49\u6A21\u677F"}`,
1179
+ config.description ? `${chalk3.dim("\u9879\u76EE\u63CF\u8FF0:")} ${config.description}` : "",
1180
+ config.author ? `${chalk3.dim("\u4F5C \u8005:")} ${config.author}` : "",
1181
+ `${chalk3.dim("\u521D\u59CB\u5316Git:")} ${config.initGit ? chalk3.green("\u662F") : "\u5426"}`,
1182
+ `${chalk3.dim("\u5B89\u88C5\u4F9D\u8D56:")} ${config.installDeps ? chalk3.green("\u662F") + ` (${config.packageManager})` : "\u5426"}`,
1183
+ `${chalk3.dim("\u6E90\u7801\u4ED3\u5E93:")} ${template.repoUrl}`
1184
+ ].filter(Boolean).join("\n"),
1185
+ "\u9879\u76EE\u521B\u5EFA\u4FE1\u606F\u786E\u8BA4"
1305
1186
  );
1306
- console.log(` \u6E90\u7801\u4ED3\u5E93: ${chalk3.dim(template.repoUrl)}`);
1307
- console.log();
1308
- const { confirmed } = await inquirer.prompt([
1309
- {
1310
- type: "confirm",
1311
- name: "confirmed",
1312
- message: "\u786E\u8BA4\u521B\u5EFA\u9879\u76EE?",
1313
- default: true
1314
- }
1315
- ]);
1316
- if (!confirmed) {
1317
- console.log(chalk3.yellow("\u53D6\u6D88\u521B\u5EFA"));
1187
+ const confirmed = await p.confirm({
1188
+ message: "\u786E\u8BA4\u521B\u5EFA\u9879\u76EE?",
1189
+ initialValue: true
1190
+ });
1191
+ if (p.isCancel(confirmed) || !confirmed) {
1192
+ p.outro(chalk3.yellow("\u53D6\u6D88\u521B\u5EFA"));
1318
1193
  process.exit(0);
1319
1194
  }
1320
1195
  }
@@ -1323,34 +1198,30 @@ async function executeCreation(projectName, template, config, options) {
1323
1198
  if (!template?.name)
1324
1199
  throw new Error(`\u6A21\u677F\u6570\u636E\u65E0\u6548: ${JSON.stringify(template)}`);
1325
1200
  const spinner = ora({
1326
- text: "\u{1F680} \u51C6\u5907\u521B\u5EFA\u9879\u76EE...",
1201
+ text: "\u51C6\u5907\u521B\u5EFA\u9879\u76EE...",
1327
1202
  spinner: "dots",
1328
1203
  color: "cyan"
1329
1204
  }).start();
1330
1205
  let tempPath;
1331
1206
  try {
1332
- spinner.text = "\u{1F4C1} \u68C0\u67E5\u9879\u76EE\u76EE\u5F55...";
1207
+ spinner.text = "\u68C0\u67E5\u9879\u76EE\u76EE\u5F55...";
1333
1208
  const projectPath = path3.resolve(projectName);
1334
1209
  if (fs3.existsSync(projectPath)) {
1335
1210
  spinner.stop();
1336
- console.log(chalk3.yellow("\u26A0\uFE0F \u9879\u76EE\u76EE\u5F55\u5DF2\u5B58\u5728"));
1337
- const { overwrite } = await inquirer.prompt([
1338
- {
1339
- type: "confirm",
1340
- name: "overwrite",
1341
- message: "\u76EE\u5F55\u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6?",
1342
- default: false
1343
- }
1344
- ]);
1345
- if (!overwrite) {
1346
- console.log(chalk3.yellow("\u274C \u53D6\u6D88\u521B\u5EFA"));
1211
+ p.log.warn("\u9879\u76EE\u76EE\u5F55\u5DF2\u5B58\u5728");
1212
+ const overwrite = await p.confirm({
1213
+ message: "\u76EE\u5F55\u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6?",
1214
+ initialValue: false
1215
+ });
1216
+ if (p.isCancel(overwrite) || !overwrite) {
1217
+ p.outro(chalk3.yellow("\u53D6\u6D88\u521B\u5EFA"));
1347
1218
  process.exit(0);
1348
1219
  }
1349
- spinner.start("\u{1F5D1}\uFE0F \u6E05\u7406\u73B0\u6709\u76EE\u5F55...");
1220
+ spinner.start("\u6E05\u7406\u73B0\u6709\u76EE\u5F55...");
1350
1221
  await fs3.remove(projectPath);
1351
- spinner.text = "\u{1F4C1} \u51C6\u5907\u521B\u5EFA\u65B0\u76EE\u5F55...";
1222
+ spinner.text = "\u51C6\u5907\u521B\u5EFA\u65B0\u76EE\u5F55...";
1352
1223
  }
1353
- spinner.text = "\u{1F310} \u4E0B\u8F7D\u6700\u65B0\u6A21\u677F...";
1224
+ spinner.text = "\u4E0B\u8F7D\u6700\u65B0\u6A21\u677F...";
1354
1225
  try {
1355
1226
  tempPath = await downloadTemplate(template, {
1356
1227
  spinner,
@@ -1361,63 +1232,52 @@ async function executeCreation(projectName, template, config, options) {
1361
1232
  } catch (error) {
1362
1233
  spinner.fail("\u6A21\u677F\u4E0B\u8F7D\u5931\u8D25");
1363
1234
  console.log();
1364
- console.log(chalk3.red("\u274C \u6A21\u677F\u4E0B\u8F7D\u9519\u8BEF:"));
1365
- console.log(chalk3.dim(` ${error.message}`));
1235
+ console.log(chalk3.dim(` ${error.message}`));
1366
1236
  console.log();
1367
- throw error;
1237
+ return;
1368
1238
  }
1369
1239
  await copyTemplate(tempPath, projectPath, spinner);
1370
- spinner.text = "\u2699\uFE0F \u5904\u7406\u9879\u76EE\u914D\u7F6E...";
1240
+ spinner.text = "\u5904\u7406\u9879\u76EE\u914D\u7F6E...";
1371
1241
  await processProjectConfig(projectPath, projectName, template, config);
1372
1242
  if (config.initGit) {
1373
- spinner.text = "\u{1F4DD} \u521D\u59CB\u5316 Git \u4ED3\u5E93...";
1243
+ spinner.text = "\u521D\u59CB\u5316 Git \u4ED3\u5E93...";
1374
1244
  initializeGitRepository(projectPath);
1375
1245
  }
1376
1246
  if (config.installDeps) {
1377
- spinner.text = `\u{1F4E6} \u4F7F\u7528 ${config.packageManager} \u5B89\u88C5\u4F9D\u8D56...`;
1247
+ spinner.text = `\u4F7F\u7528 ${config.packageManager} \u5B89\u88C5\u4F9D\u8D56...`;
1378
1248
  await installDependencies(projectPath, spinner, config.packageManager);
1379
1249
  }
1380
1250
  if (tempPath) {
1381
- spinner.text = "\u{1F9F9} \u6E05\u7406\u4E34\u65F6\u6587\u4EF6...";
1251
+ spinner.text = "\u6E05\u7406\u4E34\u65F6\u6587\u4EF6...";
1382
1252
  await fs3.remove(tempPath).catch(() => {
1383
1253
  });
1384
1254
  }
1385
- spinner.succeed(chalk3.green("\u{1F389} \u9879\u76EE\u521B\u5EFA\u6210\u529F!"));
1386
- console.log();
1387
- console.log(chalk3.green("\u9879\u76EE\u521B\u5EFA\u5B8C\u6210!"));
1388
- console.log();
1389
- console.log(chalk3.blue("\u9879\u76EE\u4FE1\u606F:"));
1390
- console.log(` \u4F4D\u7F6E: ${chalk3.cyan(projectPath)}`);
1391
- console.log(` \u6A21\u677F: ${chalk3.cyan(template.name)}`);
1392
- console.log(
1393
- ` Git\u4ED3\u5E93: ${config.initGit ? chalk3.green("\u5DF2\u521D\u59CB\u5316") : chalk3.dim("\u672A\u521D\u59CB\u5316")}`
1394
- );
1395
- console.log(
1396
- ` \u4F9D\u8D56\u5B89\u88C5: ${config.installDeps ? chalk3.green("\u5DF2\u5B8C\u6210") : chalk3.dim("\u9700\u624B\u52A8\u5B89\u88C5")}`
1397
- );
1398
- console.log();
1399
- console.log(chalk3.blue("\u5FEB\u901F\u5F00\u59CB:"));
1400
- console.log(chalk3.cyan(` cd ${projectName}`));
1255
+ spinner.succeed(chalk3.green("\u9879\u76EE\u521B\u5EFA\u6210\u529F!"));
1401
1256
  const pm = config.packageManager || "bun";
1402
- if (!config.installDeps) {
1403
- console.log(chalk3.cyan(` ${pm} install`));
1404
- }
1405
1257
  const cmd = getStartCommand(template, pm);
1406
- if (cmd) console.log(chalk3.cyan(` ${cmd}`));
1407
- if (pm === "bun")
1408
- console.log(chalk3.dim(" # \u6216\u4F7F\u7528 npm: npm install && npm run dev"));
1409
- else if (pm === "npm")
1410
- console.log(
1411
- chalk3.dim(" # \u6216\u4F7F\u7528 bun: bun install && bun run dev (\u5982\u5DF2\u5B89\u88C5)")
1412
- );
1413
- console.log();
1414
- spinner.start("\u{1F4CA} \u7EDF\u8BA1\u9879\u76EE\u4FE1\u606F...");
1258
+ const steps = [`cd ${projectName}`];
1259
+ if (!config.installDeps) steps.push(`${pm} install`);
1260
+ if (cmd) steps.push(cmd);
1261
+ p.note(
1262
+ [
1263
+ `${chalk3.dim("\u4F4D\u7F6E:")} ${chalk3.cyan(projectPath)}`,
1264
+ `${chalk3.dim("\u6A21\u677F:")} ${chalk3.cyan(template.name)}`,
1265
+ `${chalk3.dim("Git:")} ${config.initGit ? chalk3.green("\u5DF2\u521D\u59CB\u5316") : "\u672A\u521D\u59CB\u5316"}`,
1266
+ `${chalk3.dim("\u4F9D\u8D56:")} ${config.installDeps ? chalk3.green("\u5DF2\u5B8C\u6210") : "\u9700\u624B\u52A8\u5B89\u88C5"}`,
1267
+ "",
1268
+ chalk3.bold("\u5FEB\u901F\u5F00\u59CB:"),
1269
+ ...steps.map((s) => ` ${chalk3.cyan(s)}`)
1270
+ ].join("\n"),
1271
+ "\u9879\u76EE\u521B\u5EFA\u5B8C\u6210"
1272
+ );
1273
+ const statsSpinner = ora("\u7EDF\u8BA1\u9879\u76EE\u4FE1\u606F...").start();
1415
1274
  const stats = await generateProjectStats(projectPath);
1416
- spinner.stop();
1275
+ statsSpinner.stop();
1417
1276
  if (stats) {
1418
1277
  printProjectStats(stats);
1419
1278
  console.log();
1420
1279
  }
1280
+ p.outro(chalk3.green("Happy coding!"));
1421
1281
  } catch (error) {
1422
1282
  if (tempPath) await fs3.remove(tempPath).catch(() => {
1423
1283
  });
@@ -1467,7 +1327,7 @@ function initializeGitRepository(projectPath) {
1467
1327
  stdio: "ignore"
1468
1328
  });
1469
1329
  } catch {
1470
- console.log(chalk3.yellow("\u26A0\uFE0F Git \u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u521D\u59CB\u5316"));
1330
+ console.log(chalk3.yellow("Git \u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u521D\u59CB\u5316"));
1471
1331
  }
1472
1332
  }
1473
1333
  function getStartCommand(template, pm) {
@@ -1538,9 +1398,9 @@ async function runDoctor(options = {}) {
1538
1398
  detail: cacheStats.count > 0 ? `${cacheStats.count} \u4E2A\u6A21\u677F (${formatBytes(cacheStats.totalSize)})` : "\u7A7A"
1539
1399
  });
1540
1400
  const icons = {
1541
- ok: chalk4.green("\u2705"),
1542
- warn: chalk4.yellow("\u26A0\uFE0F "),
1543
- error: chalk4.red("\u274C")
1401
+ ok: chalk4.green("[OK]"),
1402
+ warn: chalk4.yellow("[!!]"),
1403
+ error: chalk4.red("[NO]")
1544
1404
  };
1545
1405
  for (const r of results) {
1546
1406
  const icon = icons[r.status];
@@ -1550,11 +1410,11 @@ async function runDoctor(options = {}) {
1550
1410
  const hasError = results.some((r) => r.status === "error");
1551
1411
  const hasWarn = results.some((r) => r.status === "warn");
1552
1412
  if (hasError) {
1553
- console.log(chalk4.red(" \u8BCA\u65AD\u7ED3\u679C: \u73AF\u5883\u5B58\u5728\u95EE\u9898\uFF0C\u8BF7\u4FEE\u590D\u4E0A\u8FF0\u9519\u8BEF \u274C"));
1413
+ console.log(chalk4.red(" \u8BCA\u65AD\u7ED3\u679C: \u73AF\u5883\u5B58\u5728\u95EE\u9898\uFF0C\u8BF7\u4FEE\u590D\u4E0A\u8FF0\u9519\u8BEF"));
1554
1414
  } else if (hasWarn) {
1555
- console.log(chalk4.yellow(" \u8BCA\u65AD\u7ED3\u679C: \u73AF\u5883\u57FA\u672C\u6B63\u5E38\uFF0C\u90E8\u5206\u7EC4\u4EF6\u7F3A\u5931 \u26A0\uFE0F"));
1415
+ console.log(chalk4.yellow(" \u8BCA\u65AD\u7ED3\u679C: \u73AF\u5883\u57FA\u672C\u6B63\u5E38\uFF0C\u90E8\u5206\u7EC4\u4EF6\u7F3A\u5931"));
1556
1416
  } else {
1557
- console.log(chalk4.green(" \u8BCA\u65AD\u7ED3\u679C: \u73AF\u5883\u5065\u5EB7 \u2705"));
1417
+ console.log(chalk4.green(" \u8BCA\u65AD\u7ED3\u679C: \u73AF\u5883\u5065\u5EB7"));
1558
1418
  }
1559
1419
  console.log();
1560
1420
  if (options.clearCache) {
@@ -1578,58 +1438,33 @@ function getPackageVersion() {
1578
1438
  }
1579
1439
  }
1580
1440
  var VERSION = getPackageVersion();
1581
- var robotGradient = gradient(["#36d1dc", "#5b86e5"]);
1582
1441
  function showWelcome() {
1583
- const bannerLines = [
1584
- " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557",
1585
- " \u2551 \u2551",
1586
- " \u2551 R O B O T C L I \u2551",
1587
- " \u2551 \u2551",
1588
- " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
1589
- ].join("\n");
1590
- console.log();
1591
- console.log(robotGradient.multiline(bannerLines));
1592
1442
  console.log();
1593
- console.log(chalk5.gray(` v${VERSION} \xB7 \u5DE5\u7A0B\u5316\u9879\u76EE\u811A\u624B\u67B6`));
1594
- console.log(chalk5.gray(" \u524D\u7AEF \xB7 \u540E\u7AEF \xB7 \u79FB\u52A8\u7AEF \xB7 \u684C\u9762\u7AEF"));
1443
+ console.log(chalk5.cyan(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
1444
+ console.log(chalk5.cyan(" \u2551") + chalk5.bold.cyan(" R O B O T C L I ") + chalk5.cyan("\u2551"));
1445
+ console.log(chalk5.cyan(" \u2551") + chalk5.dim(` v${VERSION} | \u5DE5\u7A0B\u5316\u9879\u76EE\u811A\u624B\u67B6`.padEnd(38)) + chalk5.cyan("\u2551"));
1446
+ console.log(chalk5.cyan(" \u2551") + chalk5.dim(" \u524D\u7AEF / \u540E\u7AEF / \u79FB\u52A8\u7AEF / \u684C\u9762\u7AEF".padEnd(38)) + chalk5.cyan("\u2551"));
1447
+ console.log(chalk5.cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
1595
1448
  console.log();
1596
1449
  }
1597
1450
  async function showMainMenu() {
1598
1451
  const allTemplates = getAllTemplates();
1599
1452
  const count = Object.keys(allTemplates).length;
1600
- console.log(
1601
- chalk5.dim(
1602
- ` ${count} \u4E2A\u6A21\u677F\u53EF\u7528 \xB7 Node ${process.version} \xB7 v${VERSION}`
1603
- )
1604
- );
1605
- console.log();
1606
- const { action } = await inquirer2.prompt([
1607
- {
1608
- type: "list",
1609
- name: "action",
1610
- message: "\u8BF7\u9009\u62E9\u64CD\u4F5C:",
1611
- choices: [
1612
- {
1613
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u521B\u5EFA\u65B0\u9879\u76EE")} ${chalk5.dim("\u2014 \u9009\u62E9\u6A21\u677F\u521B\u5EFA\u9879\u76EE")}`,
1614
- value: "create"
1615
- },
1616
- {
1617
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u67E5\u770B\u6A21\u677F\u5217\u8868")} ${chalk5.dim("\u2014 \u6D4F\u89C8\u6240\u6709\u53EF\u7528\u6A21\u677F")}`,
1618
- value: "list"
1619
- },
1620
- {
1621
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u641C\u7D22\u6A21\u677F")} ${chalk5.dim("\u2014 \u6309\u5173\u952E\u8BCD\u641C\u7D22\u6A21\u677F")}`,
1622
- value: "search"
1623
- },
1624
- {
1625
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u73AF\u5883\u8BCA\u65AD")} ${chalk5.dim("\u2014 \u68C0\u67E5\u5F00\u53D1\u73AF\u5883")}`,
1626
- value: "doctor"
1627
- },
1628
- { name: chalk5.dim("\u2500".repeat(50)), value: "sep", disabled: true },
1629
- { name: `${chalk5.dim(" \u9000\u51FA")}`, value: "exit" }
1630
- ]
1631
- }
1632
- ]);
1453
+ p2.intro(chalk5.bgCyan.black(` ${count} \u4E2A\u6A21\u677F\u53EF\u7528 \xB7 Node ${process.version} \xB7 v${VERSION} `));
1454
+ const action = await p2.select({
1455
+ message: "\u8BF7\u9009\u62E9\u64CD\u4F5C:",
1456
+ options: [
1457
+ { value: "create", label: "\u521B\u5EFA\u65B0\u9879\u76EE", hint: "\u9009\u62E9\u6A21\u677F\u521B\u5EFA\u9879\u76EE" },
1458
+ { value: "list", label: "\u67E5\u770B\u6A21\u677F\u5217\u8868", hint: "\u6D4F\u89C8\u6240\u6709\u53EF\u7528\u6A21\u677F" },
1459
+ { value: "search", label: "\u641C\u7D22\u6A21\u677F", hint: "\u6309\u5173\u952E\u8BCD\u641C\u7D22\u6A21\u677F" },
1460
+ { value: "doctor", label: "\u73AF\u5883\u8BCA\u65AD", hint: "\u68C0\u67E5\u5F00\u53D1\u73AF\u5883" },
1461
+ { value: "exit", label: "\u9000\u51FA" }
1462
+ ]
1463
+ });
1464
+ if (p2.isCancel(action)) {
1465
+ p2.outro(chalk5.dim("\u518D\u89C1!"));
1466
+ process.exit(0);
1467
+ }
1633
1468
  switch (action) {
1634
1469
  case "create":
1635
1470
  await createProject(void 0, {});
@@ -1644,7 +1479,7 @@ async function showMainMenu() {
1644
1479
  await runDoctor();
1645
1480
  break;
1646
1481
  case "exit":
1647
- console.log(chalk5.dim(" \u518D\u89C1!"));
1482
+ p2.outro(chalk5.dim("\u518D\u89C1!"));
1648
1483
  process.exit(0);
1649
1484
  }
1650
1485
  }
@@ -1668,14 +1503,11 @@ function showTemplateList(recommended) {
1668
1503
  }
1669
1504
  }
1670
1505
  async function searchInteractive() {
1671
- const { keyword } = await inquirer2.prompt([
1672
- {
1673
- type: "input",
1674
- name: "keyword",
1675
- message: "\u641C\u7D22\u5173\u952E\u8BCD:",
1676
- validate: (i) => i.trim() ? true : "\u8BF7\u8F93\u5165\u5173\u952E\u8BCD"
1677
- }
1678
- ]);
1506
+ const keyword = await p2.text({
1507
+ message: "\u641C\u7D22\u5173\u952E\u8BCD:",
1508
+ validate: (i) => i?.trim() ? void 0 : "\u8BF7\u8F93\u5165\u5173\u952E\u8BCD"
1509
+ });
1510
+ if (p2.isCancel(keyword)) return;
1679
1511
  showSearchResults(keyword);
1680
1512
  }
1681
1513
  function showSearchResults(keyword) {