@agile-team/robot-cli 2.3.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.js +337 -521
  3. package/package.json +2 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [3.0.0] - 2026-03-28
9
+
10
+ ### ⚠️ BREAKING CHANGES
11
+
12
+ - 交互界面从 `inquirer` 全面迁移至 `@clack/prompts`,视觉体验完全不同
13
+ - 移除 `inquirer` 和 `@types/inquirer` 依赖
14
+
15
+ ### Added
16
+
17
+ - **@clack/prompts 现代化 UI**:使用 `p.select`、`p.text`、`p.confirm`、`p.note`、`p.intro`/`p.outro` 等,交互体验对齐 create-vue
18
+ - **Banner 框框展示**:双线框 `╔══╗` 样式包裹 ROBOT CLI 标题,信息分区更清晰
19
+ - **下载进度条**:基于 `content-length` 的流式进度条 `[████████░░░░] 40% 1.0MB/2.6MB`
20
+ - **Gitee 备用源**:模板配置新增 `giteeUrl` 字段,GitHub 不可达时自动切换 Gitee 下载
21
+ - robot-admin / monorepo / micro-app / module-federation → `gitee.com/ycyplus163/Robot_Admin`
22
+ - robot-uniapp → `gitee.com/ycyplus163/Robot_uniApp`
23
+ - **取消操作支持**:所有交互步骤支持 Ctrl+C 优雅退出 (`p.isCancel`)
24
+
25
+ ### Changed
26
+
27
+ - 主菜单、模板选择、项目配置、确认创建等全部替换为 @clack/prompts 组件
28
+ - 项目创建完成信息使用 `p.note` 卡片式展示
29
+ - 分类浏览流程简化:自动跳过只有单一选项的层级
30
+ - 项目配置不再需要最后的"确认配置"步骤(逐项确认更自然)
31
+ - 构建产物从 ~63KB 减小至 ~59KB
32
+
33
+ ### Removed
34
+
35
+ - `inquirer` 依赖及其 `@types/inquirer` 类型定义
36
+ - `getVersionLabel()` 辅助函数(改为内联 `VERSION_LABELS` 查找)
37
+
8
38
  ## [2.3.0] - 2026-03-28
9
39
 
10
40
  ### Fixed (Root-Cause)
package/dist/index.js CHANGED
@@ -3,14 +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 inquirer2 from "inquirer";
6
+ import * as p2 from "@clack/prompts";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // src/create.ts
10
10
  import fs3 from "fs-extra";
11
11
  import path3 from "path";
12
12
  import chalk3 from "chalk";
13
- import inquirer from "inquirer";
13
+ import * as p from "@clack/prompts";
14
14
  import ora from "ora";
15
15
  import { execSync as execSync2 } from "child_process";
16
16
 
@@ -36,6 +36,7 @@ var TEMPLATE_CATEGORIES = {
36
36
  name: "Robot Admin \u5B8C\u6574\u7248",
37
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",
38
38
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
39
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
39
40
  features: [
40
41
  "Naive UI",
41
42
  "Vue Router",
@@ -63,6 +64,7 @@ var TEMPLATE_CATEGORIES = {
63
64
  name: "Robot Monorepo",
64
65
  description: "bun workspace + Monorepo \u591A\u5305\u7BA1\u7406\u67B6\u6784",
65
66
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
67
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
66
68
  branch: "monorepo",
67
69
  features: ["bun workspace", "Monorepo", "\u591A\u5305\u7BA1\u7406", "\u5171\u4EAB\u7EC4\u4EF6"],
68
70
  version: "full"
@@ -76,6 +78,7 @@ var TEMPLATE_CATEGORIES = {
76
78
  name: "Robot MicroApp \u5FAE\u524D\u7AEF",
77
79
  description: "\u57FA\u4E8E MicroApp \u7684\u5FAE\u524D\u7AEF\u67B6\u6784\u65B9\u6848",
78
80
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
81
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
79
82
  branch: "micro-app",
80
83
  features: ["MicroApp", "\u5FAE\u524D\u7AEF", "\u4E3B\u5B50\u5E94\u7528", "\u8DEF\u7531\u5171\u4EAB"],
81
84
  version: "full"
@@ -84,6 +87,7 @@ var TEMPLATE_CATEGORIES = {
84
87
  name: "Robot Module Federation \u6A21\u5757\u8054\u90A6",
85
88
  description: "\u57FA\u4E8E Vite Module Federation \u7684\u6A21\u5757\u8054\u90A6\u65B9\u6848",
86
89
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Admin",
90
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_Admin",
87
91
  branch: "module-federation",
88
92
  features: ["Module Federation", "\u6A21\u5757\u8054\u90A6", "Vite", "\u8FDC\u7A0B\u6A21\u5757"],
89
93
  version: "full"
@@ -131,6 +135,7 @@ var TEMPLATE_CATEGORIES = {
131
135
  name: "Robot uni-app \u5B8C\u6574\u7248",
132
136
  description: "\u591A\u7AEF\u9002\u914D + \u63D2\u4EF6\u5E02\u573A + \u5B8C\u6574\u793A\u4F8B",
133
137
  repoUrl: "https://github.com/ChenyCHENYU/Robot_Uniapp",
138
+ giteeUrl: "https://gitee.com/ycyplus163/Robot_uniApp",
134
139
  features: ["\u591A\u7AEF\u53D1\u5E03", "uView UI", "\u63D2\u4EF6\u96C6\u6210"],
135
140
  version: "full"
136
141
  },
@@ -341,8 +346,8 @@ async function clearCache() {
341
346
  var TIMEOUT_PRIMARY = 12e4;
342
347
  var TIMEOUT_MIRROR = 6e4;
343
348
  var MAX_RETRIES = 3;
344
- function getGitHubMirrors(repoUrl) {
345
- return [
349
+ function getGitHubMirrors(repoUrl, giteeUrl) {
350
+ const mirrors = [
346
351
  repoUrl,
347
352
  // 1. 原始 GitHub
348
353
  repoUrl.replace("github.com", "hub.gitmirror.com"),
@@ -350,6 +355,8 @@ function getGitHubMirrors(repoUrl) {
350
355
  `https://ghproxy.net/${repoUrl}`
351
356
  // 3. ghproxy.net
352
357
  ];
358
+ if (giteeUrl) mirrors.push(giteeUrl);
359
+ return mirrors;
353
360
  }
354
361
  async function fetchWithRetry(downloadUrl, timeout, retries) {
355
362
  let lastError;
@@ -374,10 +381,10 @@ async function fetchWithRetry(downloadUrl, timeout, retries) {
374
381
  }
375
382
  throw lastError;
376
383
  }
377
- async function tryDownload(repoUrl, branch = "main", spinner) {
384
+ async function tryDownload(repoUrl, branch = "main", spinner, giteeUrl) {
378
385
  const url = new URL(repoUrl);
379
386
  const host = url.hostname;
380
- const mirrors = host === "github.com" ? getGitHubMirrors(repoUrl) : [repoUrl];
387
+ const mirrors = host === "github.com" ? getGitHubMirrors(repoUrl, giteeUrl) : [repoUrl];
381
388
  for (let i = 0; i < mirrors.length; i++) {
382
389
  const current = mirrors[i];
383
390
  const isOriginal = i === 0;
@@ -408,8 +415,9 @@ async function tryDownload(repoUrl, branch = "main", spinner) {
408
415
  throw new Error("\u6240\u6709\u4E0B\u8F7D\u6E90\u5747\u4E0D\u53EF\u7528");
409
416
  }
410
417
  async function downloadTemplate(template, options = {}) {
411
- const { spinner, noCache } = options;
418
+ const { spinner, noCache, giteeUrl: optGiteeUrl } = options;
412
419
  const branch = template.branch || "main";
420
+ const giteeUrl = optGiteeUrl || template.giteeUrl;
413
421
  if (!template?.repoUrl) {
414
422
  throw new Error(`\u6A21\u677F\u914D\u7F6E\u65E0\u6548: ${JSON.stringify(template)}`);
415
423
  }
@@ -418,14 +426,37 @@ async function downloadTemplate(template, options = {}) {
418
426
  const { response, sourceName } = await tryDownload(
419
427
  template.repoUrl,
420
428
  branch,
421
- spinner
429
+ spinner,
430
+ giteeUrl
422
431
  );
423
432
  if (spinner) spinner.text = "\u4FDD\u5B58\u4E0B\u8F7D\u6587\u4EF6...";
424
433
  const timestamp = Date.now();
425
434
  const tempZip = path.join(os.tmpdir(), `robot-template-${timestamp}.zip`);
426
435
  const tempExtract = path.join(os.tmpdir(), `robot-extract-${timestamp}`);
427
- const buffer = Buffer.from(await response.arrayBuffer());
428
- await fs.writeFile(tempZip, buffer);
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
+ }
429
460
  if (spinner) spinner.text = "\u89E3\u538B\u6A21\u677F\u6587\u4EF6...";
430
461
  await extract(tempZip, { dir: tempExtract });
431
462
  if (spinner) spinner.text = "\u67E5\u627E\u9879\u76EE\u7ED3\u6784...";
@@ -778,17 +809,9 @@ function formatBytes(bytes) {
778
809
  }
779
810
 
780
811
  // src/create.ts
781
- function getVersionLabel(version) {
782
- const label = VERSION_LABELS[version] || version;
783
- if (version === "full") return chalk3.green(`[${label}]`);
784
- if (version === "micro") return chalk3.blue(`[${label}]`);
785
- return chalk3.yellow(`[${label}]`);
786
- }
787
812
  var STRIP_VERSION_RE = /\s*(完整版|精简版|微服务版)\s*$/;
788
813
  async function createProject(projectName, options = {}) {
789
- console.log();
790
- console.log(chalk3.cyan(" Robot CLI - \u5F00\u59CB\u521B\u5EFA\u9879\u76EE"));
791
- console.log();
814
+ p.intro(chalk3.bgCyan.black(" Robot CLI - \u5F00\u59CB\u521B\u5EFA\u9879\u76EE "));
792
815
  let template;
793
816
  if (options.from) {
794
817
  template = {
@@ -842,38 +865,33 @@ async function handleProjectName(projectName, template) {
842
865
  if (projectName) {
843
866
  const v = validateProjectName(projectName);
844
867
  if (!v.valid) {
845
- console.log(chalk3.red("\u9879\u76EE\u540D\u79F0\u4E0D\u5408\u6CD5:"));
846
- v.errors.forEach((e) => console.log(chalk3.red(` ${e}`)));
847
- console.log();
848
- const { newName } = await inquirer.prompt([
849
- {
850
- type: "input",
851
- name: "newName",
852
- message: "\u8BF7\u8F93\u5165\u65B0\u7684\u9879\u76EE\u540D\u79F0:",
853
- validate: (input) => {
854
- const r = validateProjectName(input);
855
- return r.valid || r.errors[0];
856
- }
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];
857
876
  }
858
- ]);
877
+ });
878
+ if (p.isCancel(newName)) process.exit(0);
859
879
  return newName;
860
880
  }
861
881
  return projectName;
862
882
  }
863
883
  const defaultName = generateDefaultProjectName(template);
864
- const { name } = await inquirer.prompt([
865
- {
866
- type: "input",
867
- name: "name",
868
- message: "\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0:",
869
- default: defaultName,
870
- validate: (input) => {
871
- if (!input.trim()) return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
872
- const r = validateProjectName(input);
873
- return r.valid || r.errors[0];
874
- }
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];
875
892
  }
876
- ]);
893
+ });
894
+ if (p.isCancel(name)) process.exit(0);
877
895
  return name;
878
896
  }
879
897
  function generateDefaultProjectName(template) {
@@ -894,36 +912,16 @@ async function selectTemplate(templateOption) {
894
912
  return await selectTemplateMethod();
895
913
  }
896
914
  async function selectTemplateMethod() {
897
- console.log();
898
- console.log(chalk3.bold(" \u9009\u62E9\u6A21\u677F\u521B\u5EFA\u65B9\u5F0F"));
899
- console.log(chalk3.dim(" \u8BF7\u9009\u62E9\u6700\u9002\u5408\u4F60\u7684\u6A21\u677F\u6D4F\u89C8\u65B9\u5F0F"));
900
- console.log();
901
- const { selectionMode } = await inquirer.prompt([
902
- {
903
- type: "list",
904
- name: "selectionMode",
905
- message: "\u6A21\u677F\u9009\u62E9\u65B9\u5F0F:",
906
- choices: [
907
- {
908
- 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")}`,
909
- value: "recommended"
910
- },
911
- {
912
- 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")}`,
913
- value: "category"
914
- },
915
- {
916
- name: `${chalk3.cyan(">")} ${chalk3.bold("\u5173\u952E\u8BCD\u641C\u7D22")} ${chalk3.dim("\u2014 \u6309\u6280\u672F\u6808\u3001\u529F\u80FD\u7279\u6027\u67E5\u627E")}`,
917
- value: "search"
918
- },
919
- {
920
- name: `${chalk3.cyan(">")} ${chalk3.bold("\u5168\u90E8\u6A21\u677F")} ${chalk3.dim("\u2014 \u67E5\u770B\u6240\u6709\u53EF\u7528\u6A21\u677F")}`,
921
- value: "all"
922
- }
923
- ],
924
- pageSize: 10
925
- }
926
- ]);
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);
927
925
  switch (selectionMode) {
928
926
  case "recommended":
929
927
  return await selectFromRecommended();
@@ -940,400 +938,258 @@ async function selectTemplateMethod() {
940
938
  async function selectFromRecommended() {
941
939
  const recommended = getRecommendedTemplates();
942
940
  if (Object.keys(recommended).length === 0) {
943
- console.log(chalk3.yellow("\u6682\u65E0\u63A8\u8350\u6A21\u677F"));
941
+ p.log.warn("\u6682\u65E0\u63A8\u8350\u6A21\u677F");
944
942
  return await selectTemplateMethod();
945
943
  }
946
- console.log();
947
- console.log(chalk3.bold(" \u63A8\u8350\u6A21\u677F"));
948
- console.log(chalk3.dim(" \u57FA\u4E8E\u56E2\u961F\u4F7F\u7528\u9891\u7387\u548C\u9879\u76EE\u6210\u719F\u5EA6\u63A8\u8350"));
949
- console.log();
950
- const choices = [];
951
- const entries = Object.entries(recommended);
952
- entries.forEach(([key, template], index) => {
953
- const tags = template.features.slice(0, 3).map((f) => chalk3.dim(`[${f}]`)).join(" ");
954
- const ver = getVersionLabel(template.version);
955
- choices.push({
956
- name: `${chalk3.bold.white(template.name.replace(STRIP_VERSION_RE, ""))} ${ver} - ${chalk3.dim(template.description)}
957
- ${tags}${template.features.length > 3 ? chalk3.dim(` +${template.features.length - 3}more`) : ""}`,
958
- value: { key, ...template },
959
- 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}`
960
953
  });
961
- if (index < entries.length - 1) {
962
- choices.push({
963
- name: chalk3.dim("\u2500".repeat(70)),
964
- value: `sep_${index}`,
965
- disabled: true
966
- });
967
- }
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
968
959
  });
969
- choices.push({ name: chalk3.dim(" <- \u8FD4\u56DE\u9009\u62E9\u5176\u4ED6\u65B9\u5F0F"), value: "back" });
970
- const { selectedTemplate } = await inquirer.prompt([
971
- {
972
- type: "list",
973
- name: "selectedTemplate",
974
- message: "\u9009\u62E9\u63A8\u8350\u6A21\u677F:",
975
- choices,
976
- pageSize: 15,
977
- loop: false
978
- }
979
- ]);
980
- if (selectedTemplate === "back") return await selectTemplateMethod();
981
- 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 };
982
964
  }
983
965
  async function selectByCategory() {
984
966
  while (true) {
985
- const cat = await selectCategory();
986
- if (cat === "back_to_method") return await selectTemplateMethod();
987
- const stack = await selectStack(cat);
988
- if (stack === "back_to_category") continue;
989
- if (stack === "back_to_method") return await selectTemplateMethod();
990
- const pattern = await selectPattern(cat, stack);
991
- if (pattern === "back_to_stack" || pattern === "back_to_category") continue;
992
- if (pattern === "back_to_method") return await selectTemplateMethod();
993
- const tpl = await selectSpecificTemplate(cat, stack, pattern);
994
- if (typeof tpl === "string") continue;
995
- return tpl;
996
- }
997
- }
998
- async function selectCategory() {
999
- const choices = Object.entries(TEMPLATE_CATEGORIES).map(([key, c]) => ({
1000
- name: c.name,
1001
- value: key
1002
- }));
1003
- choices.push({
1004
- name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"),
1005
- value: "back_to_method"
1006
- });
1007
- const { categoryKey } = await inquirer.prompt([
1008
- { type: "list", name: "categoryKey", message: "\u8BF7\u9009\u62E9\u9879\u76EE\u7C7B\u578B:", choices }
1009
- ]);
1010
- return categoryKey;
1011
- }
1012
- async function selectStack(categoryKey) {
1013
- const category = TEMPLATE_CATEGORIES[categoryKey];
1014
- const choices = Object.entries(category.stacks).map(([key, s]) => ({
1015
- name: s.name,
1016
- value: key
1017
- }));
1018
- choices.push(
1019
- {
1020
- name: chalk3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1021
- value: "separator",
1022
- disabled: true
1023
- },
1024
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u9879\u76EE\u7C7B\u578B\u9009\u62E9"), value: "back_to_category" },
1025
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_method" }
1026
- );
1027
- if (choices.length === 4) return choices[0].value;
1028
- const { stackKey } = await inquirer.prompt([
1029
- { type: "list", name: "stackKey", message: "\u8BF7\u9009\u62E9\u6280\u672F\u6808:", choices }
1030
- ]);
1031
- return stackKey;
1032
- }
1033
- async function selectPattern(catKey, stackKey) {
1034
- if (["back_to_category", "back_to_method"].includes(stackKey))
1035
- return stackKey;
1036
- const stack = TEMPLATE_CATEGORIES[catKey].stacks[stackKey];
1037
- const choices = Object.entries(stack.patterns).map(([key, p]) => ({
1038
- name: p.name,
1039
- value: key
1040
- }));
1041
- choices.push(
1042
- {
1043
- name: chalk3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1044
- value: "separator",
1045
- disabled: true
1046
- },
1047
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6280\u672F\u6808\u9009\u62E9"), value: "back_to_stack" },
1048
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u9879\u76EE\u7C7B\u578B\u9009\u62E9"), value: "back_to_category" },
1049
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_method" }
1050
- );
1051
- if (choices.length === 5) return choices[0].value;
1052
- const { patternKey } = await inquirer.prompt([
1053
- { type: "list", name: "patternKey", message: "\u8BF7\u9009\u62E9\u67B6\u6784\u6A21\u5F0F:", choices }
1054
- ]);
1055
- return patternKey;
1056
- }
1057
- async function selectSpecificTemplate(catKey, stackKey, patternKey) {
1058
- if (["back_to_stack", "back_to_category", "back_to_method"].includes(patternKey)) {
1059
- return patternKey;
1060
- }
1061
- const templates = getTemplatesByCategory(catKey, stackKey, patternKey);
1062
- const choices = Object.entries(templates).map(([key, t]) => ({
1063
- name: `${t.name} - ${chalk3.dim(t.description)}`,
1064
- value: { key, ...t },
1065
- short: t.name
1066
- }));
1067
- choices.push(
1068
- {
1069
- name: chalk3.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
1070
- value: "separator",
1071
- disabled: true
1072
- },
1073
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u67B6\u6784\u6A21\u5F0F\u9009\u62E9"), value: "back_to_pattern" },
1074
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6280\u672F\u6808\u9009\u62E9"), value: "back_to_stack" },
1075
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u9879\u76EE\u7C7B\u578B\u9009\u62E9"), value: "back_to_category" },
1076
- { name: chalk3.dim("\u2190 \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_method" }
1077
- );
1078
- const { selectedTemplate } = await inquirer.prompt([
1079
- {
1080
- type: "list",
1081
- name: "selectedTemplate",
1082
- message: "\u8BF7\u9009\u62E9\u6A21\u677F\u7248\u672C:",
1083
- 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;
1084
996
  }
1085
- ]);
1086
- 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
+ }
1087
1035
  }
1088
1036
  async function selectBySearch() {
1089
1037
  while (true) {
1090
- const { keyword } = await inquirer.prompt([
1091
- {
1092
- type: "input",
1093
- name: "keyword",
1094
- message: "\u8BF7\u8F93\u5165\u641C\u7D22\u5173\u952E\u8BCD (\u540D\u79F0\u3001\u63CF\u8FF0\u3001\u6280\u672F\u6808):",
1095
- validate: (input) => input.trim() ? true : "\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A"
1096
- }
1097
- ]);
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();
1098
1043
  const results = searchTemplates(keyword);
1099
1044
  if (Object.keys(results).length === 0) {
1100
- console.log();
1101
- console.log(chalk3.yellow("\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6A21\u677F"));
1102
- console.log(chalk3.dim(`\u641C\u7D22\u5173\u952E\u8BCD: "${keyword}"`));
1103
- console.log();
1104
- const { action } = await inquirer.prompt([
1105
- {
1106
- type: "list",
1107
- name: "action",
1108
- message: "\u8BF7\u9009\u62E9\u4E0B\u4E00\u6B65\u64CD\u4F5C:",
1109
- choices: [
1110
- { name: "\u91CD\u65B0\u641C\u7D22", value: "retry" },
1111
- { name: chalk3.dim("<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back" }
1112
- ]
1113
- }
1114
- ]);
1115
- if (action === "retry") continue;
1116
- 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;
1117
1055
  }
1118
- console.log();
1119
- console.log(chalk3.bold(" \u641C\u7D22\u7ED3\u679C"));
1120
- console.log(
1121
- chalk3.dim(
1122
- `\u5173\u952E\u8BCD: "${keyword}" \u2022 \u627E\u5230 ${Object.keys(results).length} \u4E2A\u5339\u914D\u6A21\u677F`
1123
- )
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" }
1124
1070
  );
1125
- console.log();
1126
- const choices = Object.entries(results).map(([key, t]) => {
1127
- const hl = (text) => text.replace(
1128
- new RegExp(`(${keyword})`, "gi"),
1129
- chalk3.bgYellow.black("$1")
1130
- );
1131
- const ver = getVersionLabel(t.version);
1132
- const info = t.features.slice(0, 2).join(" \u2022 ");
1133
- return {
1134
- name: `${chalk3.bold(hl(t.name.replace(STRIP_VERSION_RE, "")))} ${ver}
1135
- ${chalk3.dim(hl(t.description))}
1136
- ${chalk3.dim(`${info} \u2022 key: ${key}`)}
1137
- ${chalk3.dim("\u2500".repeat(60))}`,
1138
- value: { key, ...t },
1139
- short: t.name
1140
- };
1071
+ const selected = await p.select({
1072
+ message: "\u9009\u62E9\u6A21\u677F:",
1073
+ options
1141
1074
  });
1142
- choices.push(
1143
- { name: chalk3.dim("\u2501".repeat(70)), value: "separator", disabled: true },
1144
- { name: "\u91CD\u65B0\u641C\u7D22", value: "search_again" },
1145
- { name: chalk3.dim("<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_mode" }
1146
- );
1147
- const { selectedTemplate } = await inquirer.prompt([
1148
- {
1149
- type: "list",
1150
- name: "selectedTemplate",
1151
- message: "\u9009\u62E9\u6A21\u677F:",
1152
- choices,
1153
- pageSize: 15,
1154
- loop: false
1155
- }
1156
- ]);
1157
- if (selectedTemplate === "search_again") continue;
1158
- if (selectedTemplate === "back_to_mode")
1159
- return await selectTemplateMethod();
1160
- 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 };
1161
1080
  }
1162
1081
  }
1163
1082
  async function selectFromAll() {
1164
1083
  const allTemplates = getAllTemplates();
1165
- console.log();
1166
- console.log(chalk3.bold(" \u6240\u6709\u53EF\u7528\u6A21\u677F"));
1167
- console.log(chalk3.dim(` \u5171 ${Object.keys(allTemplates).length} \u4E2A\u6A21\u677F\u53EF\u9009`));
1168
- console.log();
1169
- 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 = [];
1170
1086
  for (const [_catKey, category] of Object.entries(TEMPLATE_CATEGORIES)) {
1171
- categorizedChoices.push({
1172
- name: chalk3.yellow.bold(category.name),
1173
- value: `${_catKey}_header`,
1174
- disabled: true
1175
- });
1176
1087
  for (const [_sKey, stack] of Object.entries(category.stacks)) {
1177
1088
  for (const _pattern of Object.values(stack.patterns)) {
1178
- for (const [key, t] of Object.entries(_pattern.templates)) {
1179
- const ver = getVersionLabel(t.version);
1180
- categorizedChoices.push({
1181
- name: ` \u25CF ${chalk3.bold(t.name.replace(STRIP_VERSION_RE, ""))} ${ver} - ${chalk3.dim(t.description)}
1182
- ${chalk3.dim(`\u6280\u672F\u6808: ${stack.name} \u2022 \u547D\u4EE4: robot create my-app -t ${key}`)}`,
1183
- value: { key, ...t },
1184
- short: t.name
1185
- });
1186
- categorizedChoices.push({
1187
- name: chalk3.dim(" " + "\u2500".repeat(66)),
1188
- value: `sep_${key}`,
1189
- 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}`
1190
1095
  });
1191
1096
  }
1192
1097
  }
1193
1098
  }
1194
1099
  }
1195
- categorizedChoices.push(
1196
- { name: chalk3.dim("\u2501".repeat(70)), value: "separator", disabled: true },
1197
- { name: chalk3.dim("<- \u8FD4\u56DE\u6A21\u677F\u9009\u62E9\u65B9\u5F0F"), value: "back_to_mode" }
1198
- );
1199
- const { selectedTemplate } = await inquirer.prompt([
1200
- {
1201
- type: "list",
1202
- name: "selectedTemplate",
1203
- message: "\u9009\u62E9\u6A21\u677F:",
1204
- choices: categorizedChoices,
1205
- pageSize: 25,
1206
- loop: false
1207
- }
1208
- ]);
1209
- if (selectedTemplate === "back_to_mode") return await selectTemplateMethod();
1210
- 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 };
1211
1109
  }
1212
1110
  async function configureProject(options) {
1213
- console.log();
1214
- console.log(chalk3.bold(" \u9879\u76EE\u914D\u7F6E"));
1215
- console.log();
1111
+ p.log.step(chalk3.bold("\u9879\u76EE\u914D\u7F6E"));
1216
1112
  const available = detectPackageManager();
1217
1113
  const hasBun = available.includes("bun");
1218
1114
  const hasPnpm = available.includes("pnpm");
1219
- const managerChoices = [];
1220
- if (available.includes("bun"))
1221
- managerChoices.push({
1222
- name: "bun (\u63A8\u8350 - \u6781\u901F\u5B89\u88C5\uFF0C\u73B0\u4EE3\u5316\uFF0C\u6027\u80FD\u6700\u4F73)",
1223
- value: "bun"
1224
- });
1225
- if (available.includes("pnpm"))
1226
- managerChoices.push({
1227
- name: "pnpm (\u63A8\u8350 - \u5FEB\u901F\u5B89\u88C5\uFF0C\u8282\u7701\u78C1\u76D8\u7A7A\u95F4)",
1228
- value: "pnpm"
1229
- });
1230
- if (available.includes("yarn"))
1231
- managerChoices.push({
1232
- name: "yarn (\u517C\u5BB9\u6027\u597D - \u9002\u7528\u4E8E\u73B0\u6709yarn\u9879\u76EE)",
1233
- value: "yarn"
1234
- });
1235
- if (available.includes("npm"))
1236
- managerChoices.push({
1237
- name: "npm (\u9ED8\u8BA4 - Node.js\u5185\u7F6E\uFF0C\u517C\u5BB9\u6027\u6700\u597D)",
1238
- value: "npm"
1239
- });
1240
- if (managerChoices.length === 0) {
1241
- managerChoices.push(
1242
- { name: "npm (\u9ED8\u8BA4)", value: "npm" },
1243
- { name: "bun (\u5982\u5DF2\u5B89\u88C5)", value: "bun" },
1244
- { name: "pnpm (\u5982\u5DF2\u5B89\u88C5)", value: "pnpm" },
1245
- { name: "yarn (\u5982\u5DF2\u5B89\u88C5)", value: "yarn" }
1246
- );
1247
- }
1248
- const config = await inquirer.prompt([
1249
- {
1250
- type: "confirm",
1251
- name: "initGit",
1252
- message: "\u662F\u5426\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
1253
- default: true
1254
- },
1255
- {
1256
- type: "confirm",
1257
- name: "installDeps",
1258
- message: "\u662F\u5426\u7ACB\u5373\u5B89\u88C5\u4F9D\u8D56?",
1259
- default: !options.skipInstall
1260
- },
1261
- {
1262
- type: "list",
1263
- name: "packageManager",
1264
- message: "\u9009\u62E9\u5305\u7BA1\u7406\u5668:",
1265
- choices: managerChoices,
1266
- default: hasBun ? "bun" : hasPnpm ? "pnpm" : "npm",
1267
- when: (a) => a.installDeps
1268
- },
1269
- {
1270
- type: "input",
1271
- name: "description",
1272
- message: "\u9879\u76EE\u63CF\u8FF0 (\u53EF\u9009):",
1273
- default: ""
1274
- },
1275
- {
1276
- type: "input",
1277
- name: "author",
1278
- message: "\u4F5C\u8005 (\u53EF\u9009):",
1279
- default: getGitUser()
1280
- },
1281
- {
1282
- type: "confirm",
1283
- name: "confirmConfig",
1284
- message: "\u786E\u8BA4\u4EE5\u4E0A\u914D\u7F6E?",
1285
- 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
+ );
1286
1143
  }
1287
- ]);
1288
- if (!config.confirmConfig) {
1289
- const { action } = await inquirer.prompt([
1290
- {
1291
- type: "list",
1292
- name: "action",
1293
- message: "\u8BF7\u9009\u62E9\u64CD\u4F5C:",
1294
- choices: [
1295
- { name: "\u91CD\u65B0\u914D\u7F6E", value: "reconfigure" },
1296
- { name: "\u53D6\u6D88\u521B\u5EFA", value: "cancel" }
1297
- ]
1298
- }
1299
- ]);
1300
- if (action === "reconfigure") return await configureProject(options);
1301
- console.log(chalk3.yellow("\u53D6\u6D88\u521B\u5EFA\u9879\u76EE"));
1302
- 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;
1303
1151
  }
1304
- 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
+ };
1305
1171
  }
1306
1172
  async function confirmCreation(projectName, template, config) {
1307
- console.log();
1308
- console.log(chalk3.bold(" \u9879\u76EE\u521B\u5EFA\u4FE1\u606F\u786E\u8BA4:"));
1309
- console.log();
1310
- console.log(` \u9879\u76EE\u540D\u79F0: ${chalk3.cyan(projectName)}`);
1311
- console.log(` \u9009\u62E9\u6A21\u677F: ${chalk3.cyan(template.name)}`);
1312
- console.log(` \u6A21\u677F\u63CF\u8FF0: ${chalk3.dim(template.description)}`);
1313
- console.log(
1314
- ` \u5305\u542B\u529F\u80FD: ${chalk3.dim(template.features.join(", ") || "\u81EA\u5B9A\u4E49\u6A21\u677F")}`
1315
- );
1316
- if (config.description)
1317
- console.log(` \u9879\u76EE\u63CF\u8FF0: ${chalk3.dim(config.description)}`);
1318
- if (config.author) console.log(` \u4F5C\u3000\u3000\u8005: ${chalk3.dim(config.author)}`);
1319
- console.log(
1320
- ` \u521D\u59CB\u5316Git: ${config.initGit ? chalk3.green("\u662F") : chalk3.dim("\u5426")}`
1321
- );
1322
- console.log(
1323
- ` \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"
1324
1186
  );
1325
- console.log(` \u6E90\u7801\u4ED3\u5E93: ${chalk3.dim(template.repoUrl)}`);
1326
- console.log();
1327
- const { confirmed } = await inquirer.prompt([
1328
- {
1329
- type: "confirm",
1330
- name: "confirmed",
1331
- message: "\u786E\u8BA4\u521B\u5EFA\u9879\u76EE?",
1332
- default: true
1333
- }
1334
- ]);
1335
- if (!confirmed) {
1336
- 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"));
1337
1193
  process.exit(0);
1338
1194
  }
1339
1195
  }
@@ -1352,17 +1208,13 @@ async function executeCreation(projectName, template, config, options) {
1352
1208
  const projectPath = path3.resolve(projectName);
1353
1209
  if (fs3.existsSync(projectPath)) {
1354
1210
  spinner.stop();
1355
- console.log(chalk3.yellow("\u9879\u76EE\u76EE\u5F55\u5DF2\u5B58\u5728"));
1356
- const { overwrite } = await inquirer.prompt([
1357
- {
1358
- type: "confirm",
1359
- name: "overwrite",
1360
- message: "\u76EE\u5F55\u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6?",
1361
- default: false
1362
- }
1363
- ]);
1364
- if (!overwrite) {
1365
- console.log(chalk3.yellow("\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"));
1366
1218
  process.exit(0);
1367
1219
  }
1368
1220
  spinner.start("\u6E05\u7406\u73B0\u6709\u76EE\u5F55...");
@@ -1401,41 +1253,31 @@ async function executeCreation(projectName, template, config, options) {
1401
1253
  });
1402
1254
  }
1403
1255
  spinner.succeed(chalk3.green("\u9879\u76EE\u521B\u5EFA\u6210\u529F!"));
1404
- console.log();
1405
- console.log(chalk3.green("\u9879\u76EE\u521B\u5EFA\u5B8C\u6210!"));
1406
- console.log();
1407
- console.log(chalk3.blue("\u9879\u76EE\u4FE1\u606F:"));
1408
- console.log(` \u4F4D\u7F6E: ${chalk3.cyan(projectPath)}`);
1409
- console.log(` \u6A21\u677F: ${chalk3.cyan(template.name)}`);
1410
- console.log(
1411
- ` Git\u4ED3\u5E93: ${config.initGit ? chalk3.green("\u5DF2\u521D\u59CB\u5316") : chalk3.dim("\u672A\u521D\u59CB\u5316")}`
1412
- );
1413
- console.log(
1414
- ` \u4F9D\u8D56\u5B89\u88C5: ${config.installDeps ? chalk3.green("\u5DF2\u5B8C\u6210") : chalk3.dim("\u9700\u624B\u52A8\u5B89\u88C5")}`
1415
- );
1416
- console.log();
1417
- console.log(chalk3.blue("\u5FEB\u901F\u5F00\u59CB:"));
1418
- console.log(chalk3.cyan(` cd ${projectName}`));
1419
1256
  const pm = config.packageManager || "bun";
1420
- if (!config.installDeps) {
1421
- console.log(chalk3.cyan(` ${pm} install`));
1422
- }
1423
1257
  const cmd = getStartCommand(template, pm);
1424
- if (cmd) console.log(chalk3.cyan(` ${cmd}`));
1425
- if (pm === "bun")
1426
- console.log(chalk3.dim(" # \u6216\u4F7F\u7528 npm: npm install && npm run dev"));
1427
- else if (pm === "npm")
1428
- console.log(
1429
- chalk3.dim(" # \u6216\u4F7F\u7528 bun: bun install && bun run dev (\u5982\u5DF2\u5B89\u88C5)")
1430
- );
1431
- console.log();
1432
- spinner.start("\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();
1433
1274
  const stats = await generateProjectStats(projectPath);
1434
- spinner.stop();
1275
+ statsSpinner.stop();
1435
1276
  if (stats) {
1436
1277
  printProjectStats(stats);
1437
1278
  console.log();
1438
1279
  }
1280
+ p.outro(chalk3.green("Happy coding!"));
1439
1281
  } catch (error) {
1440
1282
  if (tempPath) await fs3.remove(tempPath).catch(() => {
1441
1283
  });
@@ -1597,55 +1439,32 @@ function getPackageVersion() {
1597
1439
  }
1598
1440
  var VERSION = getPackageVersion();
1599
1441
  function showWelcome() {
1600
- const supportsColor = process.stdout.isTTY && process.stdout.getColorDepth() > 1;
1601
1442
  console.log();
1602
- if (supportsColor) {
1603
- console.log(chalk5.bold.cyan(" R O B O T C L I"));
1604
- } else {
1605
- console.log(" R O B O T C L I");
1606
- }
1607
- console.log(
1608
- chalk5.dim(` v${VERSION} | ${chalk5.reset.dim("\u5DE5\u7A0B\u5316\u9879\u76EE\u811A\u624B\u67B6")}`)
1609
- );
1610
- console.log(chalk5.dim(" \u524D\u7AEF / \u540E\u7AEF / \u79FB\u52A8\u7AEF / \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"));
1611
1448
  console.log();
1612
1449
  }
1613
1450
  async function showMainMenu() {
1614
1451
  const allTemplates = getAllTemplates();
1615
1452
  const count = Object.keys(allTemplates).length;
1616
- console.log(
1617
- chalk5.dim(
1618
- ` ${count} \u4E2A\u6A21\u677F\u53EF\u7528 \xB7 Node ${process.version} \xB7 v${VERSION}`
1619
- )
1620
- );
1621
- console.log();
1622
- const { action } = await inquirer2.prompt([
1623
- {
1624
- type: "list",
1625
- name: "action",
1626
- message: "\u8BF7\u9009\u62E9\u64CD\u4F5C:",
1627
- choices: [
1628
- {
1629
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u521B\u5EFA\u65B0\u9879\u76EE")} ${chalk5.dim("\u2014 \u9009\u62E9\u6A21\u677F\u521B\u5EFA\u9879\u76EE")}`,
1630
- value: "create"
1631
- },
1632
- {
1633
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u67E5\u770B\u6A21\u677F\u5217\u8868")} ${chalk5.dim("\u2014 \u6D4F\u89C8\u6240\u6709\u53EF\u7528\u6A21\u677F")}`,
1634
- value: "list"
1635
- },
1636
- {
1637
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u641C\u7D22\u6A21\u677F")} ${chalk5.dim("\u2014 \u6309\u5173\u952E\u8BCD\u641C\u7D22\u6A21\u677F")}`,
1638
- value: "search"
1639
- },
1640
- {
1641
- name: `${chalk5.cyan(">")} ${chalk5.bold("\u73AF\u5883\u8BCA\u65AD")} ${chalk5.dim("\u2014 \u68C0\u67E5\u5F00\u53D1\u73AF\u5883")}`,
1642
- value: "doctor"
1643
- },
1644
- { name: chalk5.dim("\u2500".repeat(50)), value: "sep", disabled: true },
1645
- { name: `${chalk5.dim(" \u9000\u51FA")}`, value: "exit" }
1646
- ]
1647
- }
1648
- ]);
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
+ }
1649
1468
  switch (action) {
1650
1469
  case "create":
1651
1470
  await createProject(void 0, {});
@@ -1660,7 +1479,7 @@ async function showMainMenu() {
1660
1479
  await runDoctor();
1661
1480
  break;
1662
1481
  case "exit":
1663
- console.log(chalk5.dim(" \u518D\u89C1!"));
1482
+ p2.outro(chalk5.dim("\u518D\u89C1!"));
1664
1483
  process.exit(0);
1665
1484
  }
1666
1485
  }
@@ -1684,14 +1503,11 @@ function showTemplateList(recommended) {
1684
1503
  }
1685
1504
  }
1686
1505
  async function searchInteractive() {
1687
- const { keyword } = await inquirer2.prompt([
1688
- {
1689
- type: "input",
1690
- name: "keyword",
1691
- message: "\u641C\u7D22\u5173\u952E\u8BCD:",
1692
- validate: (i) => i.trim() ? true : "\u8BF7\u8F93\u5165\u5173\u952E\u8BCD"
1693
- }
1694
- ]);
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;
1695
1511
  showSearchResults(keyword);
1696
1512
  }
1697
1513
  function showSearchResults(keyword) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agile-team/robot-cli",
3
- "version": "2.3.0",
3
+ "version": "3.0.0",
4
4
  "description": "现代化项目脚手架工具,支持多技术栈快速创建项目 - 优先 bun,兼容 npm/pnpm/yarn",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,16 +20,15 @@
20
20
  "release:major": "bun version major && bunx npm publish --access public"
21
21
  },
22
22
  "dependencies": {
23
+ "@clack/prompts": "^1.1.0",
23
24
  "chalk": "^5.3.0",
24
25
  "commander": "^11.0.0",
25
26
  "extract-zip": "^2.0.1",
26
27
  "fs-extra": "^11.1.0",
27
- "inquirer": "^9.2.0",
28
28
  "ora": "^7.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/fs-extra": "^11.0.4",
32
- "@types/inquirer": "^9.0.9",
33
32
  "@types/node": "^22.0.0",
34
33
  "oxlint": "^0.16.0",
35
34
  "tsup": "^8.4.0",