@lambertkeith/spec-go 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,27 +6,75 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
- // src/cli.ts
10
- import path7 from "path";
11
- import fs5 from "fs";
9
+ // src/cli/cli.ts
10
+ import path8 from "path";
11
+ import fs6 from "fs";
12
12
  import { Command } from "commander";
13
- import pc6 from "picocolors";
13
+ import pc7 from "picocolors";
14
14
 
15
15
  // package.json
16
- var version = "0.2.5";
16
+ var version = "0.3.1";
17
17
 
18
- // src/prompts.ts
19
- import path2 from "path";
20
- import fs from "fs";
18
+ // src/cli/prompts.ts
19
+ import path5 from "path";
20
+ import fs3 from "fs";
21
21
  import { input, select, confirm, Separator } from "@inquirer/prompts";
22
22
  import pc from "picocolors";
23
23
 
24
- // src/utils.ts
24
+ // src/core/types.ts
25
+ var silentLogger = {
26
+ info: () => {
27
+ },
28
+ success: () => {
29
+ },
30
+ warn: () => {
31
+ },
32
+ error: () => {
33
+ },
34
+ dim: () => {
35
+ }
36
+ };
37
+
38
+ // src/core/config.ts
39
+ import fs from "fs";
25
40
  import path from "path";
26
- import { fileURLToPath } from "url";
27
- import { spawn } from "child_process";
41
+ import os from "os";
42
+ var CONFIG_PATH = path.join(os.homedir(), ".spec-go.json");
43
+ var DEFAULT_CONFIG = {
44
+ defaults: {
45
+ github: false,
46
+ public: false,
47
+ template: ""
48
+ },
49
+ github: {
50
+ token: "",
51
+ defaultOrg: ""
52
+ }
53
+ };
54
+ function ensureConfigExists() {
55
+ if (!fs.existsSync(CONFIG_PATH)) {
56
+ saveConfig(DEFAULT_CONFIG);
57
+ }
58
+ }
59
+ function loadConfig() {
60
+ try {
61
+ if (fs.existsSync(CONFIG_PATH)) {
62
+ const content = fs.readFileSync(CONFIG_PATH, "utf-8");
63
+ return JSON.parse(content);
64
+ }
65
+ } catch {
66
+ }
67
+ return {};
68
+ }
69
+ function saveConfig(config) {
70
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
71
+ fs.chmodSync(CONFIG_PATH, 384);
72
+ }
73
+ function getConfigPath() {
74
+ return CONFIG_PATH;
75
+ }
28
76
 
29
- // src/debug.ts
77
+ // src/core/debug.ts
30
78
  var debugEnabled = false;
31
79
  function setDebugEnabled(enabled) {
32
80
  debugEnabled = enabled;
@@ -57,11 +105,39 @@ function formatData(data) {
57
105
  }
58
106
  }
59
107
 
60
- // src/utils.ts
108
+ // src/core/exit-codes.ts
109
+ var ExitCodes = {
110
+ /** 成功完成 */
111
+ SUCCESS: 0,
112
+ /** 用户错误:参数错误、输入无效 */
113
+ USER_ERROR: 1,
114
+ /** 外部错误:网络、GitHub API、包管理器 */
115
+ EXTERNAL_ERROR: 2,
116
+ /** 未知内部错误 */
117
+ INTERNAL_ERROR: 10,
118
+ /** 模板相关错误 */
119
+ TEMPLATE_ERROR: 11,
120
+ /** 文件系统错误 */
121
+ FILE_SYSTEM_ERROR: 12,
122
+ /** Git 操作错误 */
123
+ GIT_ERROR: 13
124
+ };
125
+ var CliError = class extends Error {
126
+ constructor(message, exitCode = ExitCodes.INTERNAL_ERROR) {
127
+ super(message);
128
+ this.exitCode = exitCode;
129
+ this.name = "CliError";
130
+ }
131
+ };
132
+
133
+ // src/core/utils.ts
134
+ import path2 from "path";
135
+ import { fileURLToPath } from "url";
136
+ import { spawn } from "child_process";
61
137
  var __filename = fileURLToPath(import.meta.url);
62
- var __dirname = path.dirname(__filename);
138
+ var __dirname = path2.dirname(__filename);
63
139
  function getTemplatesDir() {
64
- return path.resolve(__dirname, "..", "templates");
140
+ return path2.resolve(__dirname, "..", "..", "templates");
65
141
  }
66
142
  function isValidPackageName(name) {
67
143
  return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name);
@@ -70,11 +146,11 @@ function toValidPackageName(name) {
70
146
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9-~]+/g, "-");
71
147
  }
72
148
  function isEmpty(dirPath) {
73
- const fs6 = __require("fs");
74
- if (!fs6.existsSync(dirPath)) {
149
+ const fs7 = __require("fs");
150
+ if (!fs7.existsSync(dirPath)) {
75
151
  return true;
76
152
  }
77
- const files = fs6.readdirSync(dirPath);
153
+ const files = fs7.readdirSync(dirPath);
78
154
  return files.length === 0 || files.length === 1 && files[0] === ".git";
79
155
  }
80
156
  function execAsync(command, args, options = {}) {
@@ -108,157 +184,7 @@ function execAsync(command, args, options = {}) {
108
184
  });
109
185
  }
110
186
 
111
- // src/exit-codes.ts
112
- var ExitCodes = {
113
- /** 成功完成 */
114
- SUCCESS: 0,
115
- /** 用户错误:参数错误、输入无效 */
116
- USER_ERROR: 1,
117
- /** 外部错误:网络、GitHub API、包管理器 */
118
- EXTERNAL_ERROR: 2,
119
- /** 未知内部错误 */
120
- INTERNAL_ERROR: 10,
121
- /** 模板相关错误 */
122
- TEMPLATE_ERROR: 11,
123
- /** 文件系统错误 */
124
- FILE_SYSTEM_ERROR: 12,
125
- /** Git 操作错误 */
126
- GIT_ERROR: 13
127
- };
128
- var CliError = class extends Error {
129
- constructor(message, exitCode = ExitCodes.INTERNAL_ERROR) {
130
- super(message);
131
- this.exitCode = exitCode;
132
- this.name = "CliError";
133
- }
134
- };
135
-
136
- // src/prompts.ts
137
- var DEFAULT_TEMPLATE = "node-ts";
138
- async function runPrompts(argProjectName, options, userConfig = {}, interactive = true) {
139
- const defaults = userConfig.defaults ?? {};
140
- const templatesDir = getTemplatesDir();
141
- const registryPath = path2.join(templatesDir, "template.config.json");
142
- const registry = JSON.parse(
143
- fs.readFileSync(registryPath, "utf-8")
144
- );
145
- debug("prompts", `\u4EA4\u4E92\u6A21\u5F0F: ${interactive}`);
146
- debug("prompts", `CLI \u53C2\u6570`, { argProjectName, options });
147
- let projectName = argProjectName;
148
- if (!projectName) {
149
- if (!interactive) {
150
- console.log(pc.red("\u9519\u8BEF: \u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0"));
151
- throw new CliError("\u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0", ExitCodes.USER_ERROR);
152
- }
153
- projectName = await input({
154
- message: "\u9879\u76EE\u540D\u79F0:",
155
- default: "my-project",
156
- validate: (value) => {
157
- if (!value.trim()) {
158
- return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
159
- }
160
- if (!isValidPackageName(toValidPackageName(value))) {
161
- return "\u65E0\u6548\u7684\u9879\u76EE\u540D\u79F0";
162
- }
163
- return true;
164
- }
165
- });
166
- }
167
- const validName = toValidPackageName(projectName);
168
- const targetDir = path2.resolve(process.cwd(), validName);
169
- if (fs.existsSync(targetDir) && !isEmpty(targetDir)) {
170
- if (!interactive) {
171
- console.log(pc.yellow(`\u8B66\u544A: \u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6`));
172
- } else {
173
- const overwrite = await confirm({
174
- message: `\u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u662F\u5426\u7EE7\u7EED? (\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6)`,
175
- default: false
176
- });
177
- if (!overwrite) {
178
- const error = new Error("PROMPT_CANCELLED");
179
- throw error;
180
- }
181
- }
182
- }
183
- let template = options.template;
184
- if (!template) {
185
- if (!interactive) {
186
- template = defaults.template || DEFAULT_TEMPLATE;
187
- console.log(pc.dim(`\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F: ${template}`));
188
- } else {
189
- const defaultTemplate = defaults.template;
190
- const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
191
- const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
192
- const templateChoices = [];
193
- templateChoices.push(new Separator("\u2500\u2500 \u5355\u4F53\u9879\u76EE \u2500\u2500"));
194
- for (const t of singleTemplates) {
195
- templateChoices.push({
196
- name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
197
- value: t.name
198
- });
199
- }
200
- if (fullstackTemplates.length > 0) {
201
- templateChoices.push(new Separator("\u2500\u2500 \u524D\u540E\u7AEF\u5206\u79BB \u2500\u2500"));
202
- for (const t of fullstackTemplates) {
203
- templateChoices.push({
204
- name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
205
- value: t.name
206
- });
207
- }
208
- }
209
- template = await select({
210
- message: "\u9009\u62E9\u6A21\u677F:",
211
- choices: templateChoices,
212
- default: defaultTemplate && registry.templates.some((t) => t.name === defaultTemplate) ? defaultTemplate : void 0
213
- });
214
- }
215
- }
216
- const found = registry.templates.find((t) => t.name === template);
217
- if (!found) {
218
- console.log(pc.red(`\u9519\u8BEF: \u672A\u627E\u5230\u6A21\u677F "${template}"`));
219
- console.log(pc.dim(`\u53EF\u7528\u6A21\u677F: ${registry.templates.map((t) => t.name).join(", ")}`));
220
- throw new CliError(`\u672A\u627E\u5230\u6A21\u677F "${template}"`, ExitCodes.USER_ERROR);
221
- }
222
- let initGit2 = options.git !== false;
223
- if (options.git === void 0 && interactive) {
224
- initGit2 = await confirm({
225
- message: "\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
226
- default: true
227
- });
228
- }
229
- let createGithub = options.github ?? false;
230
- if (initGit2 && options.github === void 0 && interactive) {
231
- createGithub = await confirm({
232
- message: "\u5728 GitHub \u521B\u5EFA\u8FDC\u7A0B\u4ED3\u5E93?",
233
- default: defaults.github ?? false
234
- });
235
- }
236
- let isPublic = options.public ?? false;
237
- if (createGithub && options.public === void 0 && interactive) {
238
- const publicDefault = defaults.public ?? false;
239
- isPublic = await select({
240
- message: "\u4ED3\u5E93\u53EF\u89C1\u6027:",
241
- choices: [
242
- { name: "private (\u79C1\u6709)", value: false },
243
- { name: "public (\u516C\u5F00)", value: true }
244
- ],
245
- default: publicDefault
246
- });
247
- }
248
- const result = {
249
- projectName: validName,
250
- template,
251
- targetDir,
252
- initGit: initGit2,
253
- createGithub,
254
- isPublic,
255
- noInstall: options.install === false
256
- };
257
- debug("prompts", "\u6700\u7EC8\u9879\u76EE\u9009\u9879", result);
258
- return result;
259
- }
260
-
261
- // src/scaffold.ts
187
+ // src/core/scaffold.ts
262
188
  import path3 from "path";
263
189
  import fs2 from "fs-extra";
264
190
  async function scaffoldProject(options) {
@@ -361,49 +287,7 @@ async function copyFile(srcPath, destPath, projectName, config) {
361
287
  await fs2.writeFile(destPath, content, "utf-8");
362
288
  }
363
289
 
364
- // src/git.ts
365
- import pc2 from "picocolors";
366
-
367
- // src/config.ts
368
- import fs3 from "fs";
369
- import path4 from "path";
370
- import os from "os";
371
- var CONFIG_PATH = path4.join(os.homedir(), ".spec-go.json");
372
- var DEFAULT_CONFIG = {
373
- defaults: {
374
- github: false,
375
- public: false,
376
- template: ""
377
- },
378
- github: {
379
- token: "",
380
- defaultOrg: ""
381
- }
382
- };
383
- function ensureConfigExists() {
384
- if (!fs3.existsSync(CONFIG_PATH)) {
385
- saveConfig(DEFAULT_CONFIG);
386
- }
387
- }
388
- function loadConfig() {
389
- try {
390
- if (fs3.existsSync(CONFIG_PATH)) {
391
- const content = fs3.readFileSync(CONFIG_PATH, "utf-8");
392
- return JSON.parse(content);
393
- }
394
- } catch {
395
- }
396
- return {};
397
- }
398
- function saveConfig(config) {
399
- fs3.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
400
- fs3.chmodSync(CONFIG_PATH, 384);
401
- }
402
- function getConfigPath() {
403
- return CONFIG_PATH;
404
- }
405
-
406
- // src/github-api.ts
290
+ // src/core/github-api.ts
407
291
  var GITHUB_API_BASE = "https://api.github.com";
408
292
  async function githubFetch(endpoint, token, options = {}) {
409
293
  const url = `${GITHUB_API_BASE}${endpoint}`;
@@ -413,7 +297,7 @@ async function githubFetch(endpoint, token, options = {}) {
413
297
  ...options,
414
298
  headers: {
415
299
  "Accept": "application/vnd.github+json",
416
- "Authorization": `Bearer ${token.slice(0, 8)}...`,
300
+ "Authorization": `Bearer ${token}`,
417
301
  "X-GitHub-Api-Version": "2022-11-28",
418
302
  "User-Agent": "spec-go",
419
303
  ...options.headers
@@ -479,7 +363,7 @@ async function createRepository(token, options) {
479
363
  });
480
364
  }
481
365
 
482
- // src/git.ts
366
+ // src/core/git.ts
483
367
  async function initGit(targetDir) {
484
368
  debug("git", `\u521D\u59CB\u5316 Git \u4ED3\u5E93: ${targetDir}`);
485
369
  await execAsync("git", ["init"], { cwd: targetDir });
@@ -487,27 +371,27 @@ async function initGit(targetDir) {
487
371
  await execAsync("git", ["commit", "-m", "Initial commit"], { cwd: targetDir });
488
372
  debug("git", "Git \u521D\u59CB\u5316\u5B8C\u6210");
489
373
  }
490
- async function createGithubRepo(repoName, targetDir, isPublic) {
374
+ async function createGithubRepo(options) {
375
+ const { repoName, targetDir, isPublic, organization, logger = silentLogger } = options;
491
376
  debug("git", `\u521B\u5EFA GitHub \u4ED3\u5E93: ${repoName}, \u516C\u5F00: ${isPublic}`);
492
377
  const config = loadConfig();
493
378
  if (!config.github?.token) {
494
- console.log(pc2.yellow("\u26A0 \u672A\u914D\u7F6E GitHub Token\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u521B\u5EFA"));
495
- console.log(pc2.dim(` \u8FD0\u884C ${pc2.cyan("spec-go config --setup-github")} \u914D\u7F6E Token`));
496
- console.log(pc2.dim(` \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${getConfigPath()}`));
379
+ logger.warn("\u26A0 \u672A\u914D\u7F6E GitHub Token\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u521B\u5EFA");
380
+ logger.dim(` \u8FD0\u884C spec-go config --setup-github \u914D\u7F6E Token`);
381
+ logger.dim(` \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${getConfigPath()}`);
497
382
  return null;
498
383
  }
499
384
  const token = config.github.token;
500
- const defaultOrg = config.github.defaultOrg;
385
+ const defaultOrg = organization || config.github.defaultOrg;
501
386
  const validateResult = await validateToken(token);
502
387
  if (!validateResult.success) {
503
- console.log(pc2.yellow(`\u26A0 GitHub Token \u65E0\u6548: ${validateResult.error}`));
388
+ logger.warn(`\u26A0 GitHub Token \u65E0\u6548: ${validateResult.error}`);
504
389
  if (validateResult.rateLimitReset) {
505
- console.log(pc2.dim(` Rate limit \u5C06\u5728 ${validateResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
390
+ logger.dim(` Rate limit \u5C06\u5728 ${validateResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`);
506
391
  }
507
- console.log(pc2.dim(` \u8FD0\u884C ${pc2.cyan("spec-go config --setup-github")} \u91CD\u65B0\u914D\u7F6E`));
392
+ logger.dim(` \u8FD0\u884C spec-go config --setup-github \u91CD\u65B0\u914D\u7F6E`);
508
393
  return null;
509
394
  }
510
- const username = validateResult.data.login;
511
395
  const createResult = await createRepository(token, {
512
396
  name: repoName,
513
397
  isPrivate: !isPublic,
@@ -515,12 +399,12 @@ async function createGithubRepo(repoName, targetDir, isPublic) {
515
399
  });
516
400
  if (!createResult.success) {
517
401
  if (createResult.statusCode === 422) {
518
- console.log(pc2.yellow(`\u26A0 \u4ED3\u5E93 "${repoName}" \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0`));
402
+ logger.warn(`\u26A0 \u4ED3\u5E93 "${repoName}" \u5DF2\u5B58\u5728\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u540D\u79F0`);
519
403
  } else if (createResult.rateLimitReset) {
520
- console.log(pc2.yellow(`\u26A0 API \u8BF7\u6C42\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650`));
521
- console.log(pc2.dim(` \u5C06\u5728 ${createResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
404
+ logger.warn(`\u26A0 API \u8BF7\u6C42\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650`);
405
+ logger.dim(` \u5C06\u5728 ${createResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`);
522
406
  } else {
523
- console.log(pc2.yellow(`\u26A0 \u521B\u5EFA\u4ED3\u5E93\u5931\u8D25: ${createResult.error}`));
407
+ logger.warn(`\u26A0 \u521B\u5EFA\u4ED3\u5E93\u5931\u8D25: ${createResult.error}`);
524
408
  }
525
409
  return null;
526
410
  }
@@ -549,55 +433,297 @@ async function createGithubRepo(repoName, targetDir, isPublic) {
549
433
  { cwd: targetDir }
550
434
  );
551
435
  if (pushMaster.code !== 0) {
552
- console.log(pc2.yellow(`\u26A0 \u63A8\u9001\u5931\u8D25: ${pushResult.stderr || pushMaster.stderr}`));
553
- console.log(pc2.dim(` \u4ED3\u5E93\u5DF2\u521B\u5EFA: ${repo.html_url}`));
554
- console.log(pc2.dim(" \u8BF7\u624B\u52A8\u63A8\u9001\u4EE3\u7801"));
436
+ logger.warn(`\u26A0 \u63A8\u9001\u5931\u8D25: ${pushResult.stderr || pushMaster.stderr}`);
437
+ logger.dim(` \u4ED3\u5E93\u5DF2\u521B\u5EFA: ${repo.html_url}`);
438
+ logger.dim(" \u8BF7\u624B\u52A8\u63A8\u9001\u4EE3\u7801");
555
439
  return repo.html_url;
556
440
  }
557
441
  }
558
442
  return repo.html_url;
559
443
  }
560
444
 
561
- // src/post-init.ts
562
- import path5 from "path";
563
- import pc3 from "picocolors";
564
- async function runPostInit(targetDir, commands) {
445
+ // src/core/post-init.ts
446
+ import path4 from "path";
447
+ async function runPostInit(targetDir, commands, options = {}) {
448
+ const { logger = silentLogger } = options;
565
449
  for (const cmd of commands) {
566
- console.log(pc3.dim(` ${cmd.description}...`));
450
+ logger.dim(` ${cmd.description}...`);
567
451
  const [command, ...args] = cmd.command.split(" ");
568
452
  const result = await execAsync(command, args, {
569
453
  cwd: targetDir,
570
454
  stdio: "pipe"
571
455
  });
572
456
  if (result.code !== 0) {
573
- console.log(pc3.yellow(`\u26A0 ${cmd.description} \u5931\u8D25`));
457
+ logger.warn(`\u26A0 ${cmd.description} \u5931\u8D25`);
574
458
  if (result.stderr) {
575
- console.log(pc3.dim(result.stderr.slice(0, 200)));
459
+ logger.dim(result.stderr.slice(0, 200));
576
460
  }
577
461
  }
578
462
  }
579
463
  }
580
- async function runWorkspacePostInit(targetDir, config) {
464
+ async function runWorkspacePostInit(targetDir, config, options = {}) {
465
+ const { logger = silentLogger } = options;
581
466
  if (config.postInit && config.postInit.length > 0) {
582
- await runPostInit(targetDir, config.postInit);
467
+ await runPostInit(targetDir, config.postInit, options);
583
468
  }
584
469
  if (config.workspaces) {
585
470
  for (const ws of config.workspaces) {
586
471
  if (ws.postInit && ws.postInit.length > 0) {
587
- const wsDir = path5.join(targetDir, ws.name);
588
- console.log(pc3.dim(` [${ws.name}]`));
589
- await runPostInit(wsDir, ws.postInit);
472
+ const wsDir = path4.join(targetDir, ws.name);
473
+ logger.dim(` [${ws.name}]`);
474
+ await runPostInit(wsDir, ws.postInit, options);
590
475
  }
591
476
  }
592
477
  }
593
478
  }
594
479
 
595
- // src/update-check.ts
596
- import fs4 from "fs";
597
- import path6 from "path";
480
+ // src/cli/prompts.ts
481
+ var DEFAULT_TEMPLATE = "node-ts";
482
+ async function runPrompts(argProjectName, options, userConfig = {}, interactive = true) {
483
+ const defaults = userConfig.defaults ?? {};
484
+ const templatesDir = getTemplatesDir();
485
+ const registryPath = path5.join(templatesDir, "template.config.json");
486
+ const registry = JSON.parse(
487
+ fs3.readFileSync(registryPath, "utf-8")
488
+ );
489
+ debug("prompts", `\u4EA4\u4E92\u6A21\u5F0F: ${interactive}`);
490
+ debug("prompts", `CLI \u53C2\u6570`, { argProjectName, options });
491
+ let projectName = argProjectName;
492
+ if (!projectName) {
493
+ if (!interactive) {
494
+ console.log(pc.red("\u9519\u8BEF: \u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0"));
495
+ throw new CliError("\u975E\u4EA4\u4E92\u6A21\u5F0F\u4E0B\u5FC5\u987B\u63D0\u4F9B\u9879\u76EE\u540D\u79F0", ExitCodes.USER_ERROR);
496
+ }
497
+ projectName = await input({
498
+ message: "\u9879\u76EE\u540D\u79F0:",
499
+ default: "my-project",
500
+ validate: (value) => {
501
+ if (!value.trim()) {
502
+ return "\u9879\u76EE\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
503
+ }
504
+ if (!isValidPackageName(toValidPackageName(value))) {
505
+ return "\u65E0\u6548\u7684\u9879\u76EE\u540D\u79F0";
506
+ }
507
+ return true;
508
+ }
509
+ });
510
+ }
511
+ const validName = toValidPackageName(projectName);
512
+ const targetDir = path5.resolve(process.cwd(), validName);
513
+ if (fs3.existsSync(targetDir) && !isEmpty(targetDir)) {
514
+ if (!interactive) {
515
+ console.log(pc.yellow(`\u8B66\u544A: \u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6`));
516
+ } else {
517
+ const overwrite = await confirm({
518
+ message: `\u76EE\u6807\u76EE\u5F55 "${validName}" \u975E\u7A7A\uFF0C\u662F\u5426\u7EE7\u7EED? (\u5C06\u8986\u76D6\u5DF2\u6709\u6587\u4EF6)`,
519
+ default: false
520
+ });
521
+ if (!overwrite) {
522
+ const error = new Error("PROMPT_CANCELLED");
523
+ throw error;
524
+ }
525
+ }
526
+ }
527
+ let template = options.template;
528
+ if (!template) {
529
+ if (!interactive) {
530
+ template = defaults.template || DEFAULT_TEMPLATE;
531
+ console.log(pc.dim(`\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F: ${template}`));
532
+ } else {
533
+ const defaultTemplate = defaults.template;
534
+ const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
535
+ const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
536
+ const templateChoices = [];
537
+ templateChoices.push(new Separator("\u2500\u2500 \u5355\u4F53\u9879\u76EE \u2500\u2500"));
538
+ for (const t of singleTemplates) {
539
+ templateChoices.push({
540
+ name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
541
+ value: t.name
542
+ });
543
+ }
544
+ if (fullstackTemplates.length > 0) {
545
+ templateChoices.push(new Separator("\u2500\u2500 \u524D\u540E\u7AEF\u5206\u79BB \u2500\u2500"));
546
+ for (const t of fullstackTemplates) {
547
+ templateChoices.push({
548
+ name: `${t.displayName} ${pc.dim(`- ${t.description}`)}`,
549
+ value: t.name
550
+ });
551
+ }
552
+ }
553
+ template = await select({
554
+ message: "\u9009\u62E9\u6A21\u677F:",
555
+ choices: templateChoices,
556
+ default: defaultTemplate && registry.templates.some((t) => t.name === defaultTemplate) ? defaultTemplate : void 0
557
+ });
558
+ }
559
+ }
560
+ const found = registry.templates.find((t) => t.name === template);
561
+ if (!found) {
562
+ console.log(pc.red(`\u9519\u8BEF: \u672A\u627E\u5230\u6A21\u677F "${template}"`));
563
+ console.log(pc.dim(`\u53EF\u7528\u6A21\u677F: ${registry.templates.map((t) => t.name).join(", ")}`));
564
+ throw new CliError(`\u672A\u627E\u5230\u6A21\u677F "${template}"`, ExitCodes.USER_ERROR);
565
+ }
566
+ let initGit2 = options.git !== false;
567
+ if (options.git === void 0 && interactive) {
568
+ initGit2 = await confirm({
569
+ message: "\u521D\u59CB\u5316 Git \u4ED3\u5E93?",
570
+ default: true
571
+ });
572
+ }
573
+ let createGithub = options.github ?? false;
574
+ if (initGit2 && options.github === void 0 && interactive) {
575
+ createGithub = await confirm({
576
+ message: "\u5728 GitHub \u521B\u5EFA\u8FDC\u7A0B\u4ED3\u5E93?",
577
+ default: defaults.github ?? false
578
+ });
579
+ }
580
+ let isPublic = options.public ?? false;
581
+ if (createGithub && options.public === void 0 && interactive) {
582
+ const publicDefault = defaults.public ?? false;
583
+ isPublic = await select({
584
+ message: "\u4ED3\u5E93\u53EF\u89C1\u6027:",
585
+ choices: [
586
+ { name: "private (\u79C1\u6709)", value: false },
587
+ { name: "public (\u516C\u5F00)", value: true }
588
+ ],
589
+ default: publicDefault
590
+ });
591
+ }
592
+ const result = {
593
+ projectName: validName,
594
+ template,
595
+ targetDir,
596
+ initGit: initGit2,
597
+ createGithub,
598
+ isPublic,
599
+ noInstall: options.install === false
600
+ };
601
+ debug("prompts", "\u6700\u7EC8\u9879\u76EE\u9009\u9879", result);
602
+ return result;
603
+ }
604
+
605
+ // src/cli/github-setup.ts
606
+ import { input as input2, confirm as confirm2 } from "@inquirer/prompts";
607
+ import pc2 from "picocolors";
608
+ var TOKEN_HELP_URL = "https://github.com/settings/tokens/new?scopes=repo&description=spec-go";
609
+ function isUserCancelled(err) {
610
+ const error = err;
611
+ return error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed");
612
+ }
613
+ async function runGitHubSetup() {
614
+ console.log();
615
+ console.log(pc2.cyan("\u914D\u7F6E GitHub Token"));
616
+ console.log();
617
+ console.log("\u9700\u8981\u4E00\u4E2A\u5177\u6709 repo \u6743\u9650\u7684 Personal Access Token (Classic) \u6765\u521B\u5EFA\u4ED3\u5E93\u3002");
618
+ console.log();
619
+ console.log(pc2.dim("\u83B7\u53D6\u6B65\u9AA4:"));
620
+ console.log(pc2.dim("1. \u8BBF\u95EE GitHub Settings > Developer settings > Personal access tokens"));
621
+ console.log(pc2.dim('2. \u70B9\u51FB "Generate new token (classic)"'));
622
+ console.log(pc2.dim('3. \u52FE\u9009 "repo" \u6743\u9650'));
623
+ console.log(pc2.dim("4. \u751F\u6210\u5E76\u590D\u5236 Token"));
624
+ console.log();
625
+ console.log(pc2.dim(`\u5FEB\u6377\u94FE\u63A5: ${TOKEN_HELP_URL}`));
626
+ console.log();
627
+ try {
628
+ const token = await input2({
629
+ message: "\u8BF7\u8F93\u5165 GitHub Token:",
630
+ validate: (value) => {
631
+ if (!value.trim()) {
632
+ return "Token \u4E0D\u80FD\u4E3A\u7A7A";
633
+ }
634
+ if (!value.startsWith("ghp_") && !value.startsWith("github_pat_")) {
635
+ return "Token \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5E94\u4EE5 ghp_ \u6216 github_pat_ \u5F00\u5934";
636
+ }
637
+ return true;
638
+ }
639
+ });
640
+ console.log();
641
+ console.log(pc2.dim("\u6B63\u5728\u9A8C\u8BC1 Token..."));
642
+ const result = await validateToken(token.trim());
643
+ if (!result.success) {
644
+ console.log(pc2.red(`\u2717 Token \u9A8C\u8BC1\u5931\u8D25: ${result.error}`));
645
+ if (result.rateLimitReset) {
646
+ console.log(pc2.dim(` Rate limit \u5C06\u5728 ${result.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
647
+ }
648
+ return false;
649
+ }
650
+ const user = result.data;
651
+ console.log(pc2.green(`\u2714 Token \u6709\u6548\uFF0C\u5DF2\u767B\u5F55\u4E3A: ${user.login}${user.name ? ` (${user.name})` : ""}`));
652
+ console.log();
653
+ const setOrg = await confirm2({
654
+ message: "\u662F\u5426\u8BBE\u7F6E\u9ED8\u8BA4\u7EC4\u7EC7? (\u5426\u5219\u4ED3\u5E93\u5C06\u521B\u5EFA\u5728\u4E2A\u4EBA\u8D26\u6237\u4E0B)",
655
+ default: false
656
+ });
657
+ let defaultOrg;
658
+ if (setOrg) {
659
+ defaultOrg = await input2({
660
+ message: "\u9ED8\u8BA4\u7EC4\u7EC7\u540D\u79F0:",
661
+ validate: (value) => {
662
+ if (!value.trim()) {
663
+ return "\u7EC4\u7EC7\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
664
+ }
665
+ return true;
666
+ }
667
+ });
668
+ defaultOrg = defaultOrg.trim();
669
+ }
670
+ const config = loadConfig();
671
+ config.github = {
672
+ token: token.trim(),
673
+ ...defaultOrg && { defaultOrg }
674
+ };
675
+ saveConfig(config);
676
+ console.log();
677
+ console.log(pc2.green("\u2714 GitHub \u914D\u7F6E\u5DF2\u4FDD\u5B58"));
678
+ console.log(pc2.dim(" \u914D\u7F6E\u6587\u4EF6\u5DF2\u8BBE\u7F6E\u4E3A\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BFB"));
679
+ return true;
680
+ } catch (err) {
681
+ if (isUserCancelled(err)) {
682
+ console.log(pc2.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
683
+ return false;
684
+ }
685
+ throw err;
686
+ }
687
+ }
688
+ function showConfig() {
689
+ const config = loadConfig();
690
+ console.log();
691
+ console.log(pc2.cyan("\u5F53\u524D\u914D\u7F6E:"));
692
+ console.log();
693
+ if (config.defaults) {
694
+ console.log(pc2.dim("defaults:"));
695
+ if (config.defaults.github !== void 0) {
696
+ console.log(` github: ${config.defaults.github}`);
697
+ }
698
+ if (config.defaults.public !== void 0) {
699
+ console.log(` public: ${config.defaults.public}`);
700
+ }
701
+ if (config.defaults.template !== void 0) {
702
+ console.log(` template: ${config.defaults.template}`);
703
+ }
704
+ }
705
+ if (config.github) {
706
+ console.log(pc2.dim("github:"));
707
+ if (config.github.token) {
708
+ const masked = config.github.token.slice(0, 8) + "..." + config.github.token.slice(-4);
709
+ console.log(` token: ${masked}`);
710
+ }
711
+ if (config.github.defaultOrg) {
712
+ console.log(` defaultOrg: ${config.github.defaultOrg}`);
713
+ }
714
+ }
715
+ if (!config.defaults && !config.github) {
716
+ console.log(pc2.dim("(\u65E0\u914D\u7F6E)"));
717
+ }
718
+ console.log();
719
+ }
720
+
721
+ // src/cli/update-check.ts
722
+ import fs4 from "fs";
723
+ import path6 from "path";
598
724
  import os2 from "os";
599
725
  import { execSync } from "child_process";
600
- import pc4 from "picocolors";
726
+ import pc3 from "picocolors";
601
727
  var CACHE_PATH = path6.join(os2.homedir(), ".spec-go-update-check");
602
728
  var CACHE_TTL = 24 * 60 * 60 * 1e3;
603
729
  var PACKAGE_NAME = "@lambertkeith/spec-go";
@@ -655,9 +781,9 @@ async function checkForUpdates(currentVersion) {
655
781
  if (latestVersion && compareVersions(currentVersion, latestVersion) < 0) {
656
782
  console.log();
657
783
  console.log(
658
- pc4.yellow(` \u26A0 \u53D1\u73B0\u65B0\u7248\u672C ${pc4.bold(latestVersion)}\uFF0C\u5F53\u524D ${currentVersion}`)
784
+ pc3.yellow(` \u26A0 \u53D1\u73B0\u65B0\u7248\u672C ${pc3.bold(latestVersion)}\uFF0C\u5F53\u524D ${currentVersion}`)
659
785
  );
660
- console.log(pc4.cyan(` \u8FD0\u884C ${pc4.bold(`npm update -g ${PACKAGE_NAME}`)} \u66F4\u65B0`));
786
+ console.log(pc3.cyan(` \u8FD0\u884C ${pc3.bold(`npm update -g ${PACKAGE_NAME}`)} \u66F4\u65B0`));
661
787
  console.log();
662
788
  }
663
789
  }
@@ -680,148 +806,433 @@ function getUpdateCommand(pm) {
680
806
  async function runUpdate(currentVersion, checkOnly) {
681
807
  const latestVersion = await fetchLatestVersion();
682
808
  if (!latestVersion) {
683
- console.log(pc4.red("\u2717 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
809
+ console.log(pc3.red("\u2717 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
684
810
  process.exit(ExitCodes.EXTERNAL_ERROR);
685
811
  }
686
812
  const comparison = compareVersions(currentVersion, latestVersion);
687
813
  if (comparison >= 0) {
688
- console.log(pc4.green(`\u2714 \u5DF2\u662F\u6700\u65B0\u7248\u672C (${currentVersion})`));
814
+ console.log(pc3.green(`\u2714 \u5DF2\u662F\u6700\u65B0\u7248\u672C (${currentVersion})`));
689
815
  return;
690
816
  }
691
- console.log(pc4.cyan(`\u53D1\u73B0\u65B0\u7248\u672C: ${currentVersion} \u2192 ${latestVersion}`));
817
+ console.log(pc3.cyan(`\u53D1\u73B0\u65B0\u7248\u672C: ${currentVersion} \u2192 ${latestVersion}`));
692
818
  if (checkOnly) {
693
- console.log(pc4.dim(`\u8FD0\u884C spec-go update \u66F4\u65B0`));
819
+ console.log(pc3.dim(`\u8FD0\u884C spec-go update \u66F4\u65B0`));
694
820
  return;
695
821
  }
696
822
  const pm = detectPackageManager();
697
823
  const updateCmd = getUpdateCommand(pm);
698
- console.log(pc4.dim(`\u6B63\u5728\u6267\u884C: ${updateCmd}`));
824
+ console.log(pc3.dim(`\u6B63\u5728\u6267\u884C: ${updateCmd}`));
699
825
  try {
700
826
  execSync(updateCmd, { stdio: "inherit" });
701
- console.log(pc4.green(`\u2714 \u66F4\u65B0\u5B8C\u6210`));
827
+ console.log(pc3.green(`\u2714 \u66F4\u65B0\u5B8C\u6210`));
702
828
  } catch {
703
- console.log(pc4.red("\u2717 \u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\u66F4\u65B0\u547D\u4EE4"));
829
+ console.log(pc3.red("\u2717 \u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\u66F4\u65B0\u547D\u4EE4"));
704
830
  process.exit(ExitCodes.EXTERNAL_ERROR);
705
831
  }
706
832
  }
707
833
 
708
- // src/github-setup.ts
709
- import { input as input2, confirm as confirm2 } from "@inquirer/prompts";
834
+ // src/cli/guide.ts
835
+ import path7 from "path";
836
+ import fs5 from "fs";
837
+ import { select as select2 } from "@inquirer/prompts";
710
838
  import pc5 from "picocolors";
711
- var TOKEN_HELP_URL = "https://github.com/settings/tokens/new?scopes=repo&description=spec-go";
712
- function isUserCancelled(err) {
713
- const error = err;
714
- return error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed");
715
- }
716
- async function runGitHubSetup() {
717
- console.log();
718
- console.log(pc5.cyan("\u914D\u7F6E GitHub Token"));
719
- console.log();
720
- console.log("\u9700\u8981\u4E00\u4E2A\u5177\u6709 repo \u6743\u9650\u7684 Personal Access Token (Classic) \u6765\u521B\u5EFA\u4ED3\u5E93\u3002");
721
- console.log();
722
- console.log(pc5.dim("\u83B7\u53D6\u6B65\u9AA4:"));
723
- console.log(pc5.dim("1. \u8BBF\u95EE GitHub Settings > Developer settings > Personal access tokens"));
724
- console.log(pc5.dim('2. \u70B9\u51FB "Generate new token (classic)"'));
725
- console.log(pc5.dim('3. \u52FE\u9009 "repo" \u6743\u9650'));
726
- console.log(pc5.dim("4. \u751F\u6210\u5E76\u590D\u5236 Token"));
727
- console.log();
728
- console.log(pc5.dim(`\u5FEB\u6377\u94FE\u63A5: ${TOKEN_HELP_URL}`));
729
- console.log();
730
- try {
731
- const token = await input2({
732
- message: "\u8BF7\u8F93\u5165 GitHub Token:",
733
- validate: (value) => {
734
- if (!value.trim()) {
735
- return "Token \u4E0D\u80FD\u4E3A\u7A7A";
736
- }
737
- if (!value.startsWith("ghp_") && !value.startsWith("github_pat_")) {
738
- return "Token \u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u5E94\u4EE5 ghp_ \u6216 github_pat_ \u5F00\u5934";
839
+
840
+ // src/cli/guide-content.ts
841
+ import pc4 from "picocolors";
842
+ var guideTopics = [
843
+ // 入门指南类
844
+ {
845
+ id: "quickstart",
846
+ title: "\u5FEB\u901F\u5F00\u59CB",
847
+ description: "5\u5206\u949F\u5FEB\u901F\u4E0A\u624B spec-go",
848
+ category: "\u5165\u95E8\u6307\u5357",
849
+ content: () => `
850
+ ${pc4.cyan("\u5FEB\u901F\u5F00\u59CB")}
851
+
852
+ ${pc4.bold("1. \u521B\u5EFA\u9879\u76EE")}
853
+ ${pc4.green("spec-go my-app")}
854
+
855
+ \u6309\u7167\u4EA4\u4E92\u5F0F\u63D0\u793A\u9009\u62E9\u6A21\u677F\u548C\u914D\u7F6E\u9009\u9879\u3002
856
+
857
+ ${pc4.bold("2. \u8FDB\u5165\u9879\u76EE\u76EE\u5F55")}
858
+ ${pc4.green("cd my-app")}
859
+
860
+ ${pc4.bold("3. \u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668")}
861
+ ${pc4.green("pnpm dev")}
862
+
863
+ \u6839\u636E\u4E0D\u540C\u6A21\u677F\uFF0C\u542F\u52A8\u547D\u4EE4\u53EF\u80FD\u6709\u6240\u4E0D\u540C\u3002
864
+
865
+ ${pc4.dim("\u63D0\u793A\uFF1A\u4F7F\u7528")} ${pc4.green("spec-go guide templates")} ${pc4.dim("\u67E5\u770B\u6240\u6709\u53EF\u7528\u6A21\u677F")}
866
+ ${pc4.dim("\u63D0\u793A\uFF1A\u4F7F\u7528")} ${pc4.green("spec-go guide examples")} ${pc4.dim("\u67E5\u770B\u66F4\u591A\u4F7F\u7528\u793A\u4F8B")}
867
+ `
868
+ },
869
+ {
870
+ id: "templates",
871
+ title: "\u6A21\u677F\u5217\u8868",
872
+ description: "\u67E5\u770B\u6240\u6709\u53EF\u7528\u7684\u9879\u76EE\u6A21\u677F",
873
+ category: "\u5165\u95E8\u6307\u5357",
874
+ content: (data) => {
875
+ const templates = data?.templates || [];
876
+ const singleTemplates = templates.filter((t) => t.category !== "fullstack");
877
+ const fullstackTemplates = templates.filter((t) => t.category === "fullstack");
878
+ let output = `
879
+ ${pc4.cyan("\u53EF\u7528\u6A21\u677F")}
880
+
881
+ `;
882
+ if (singleTemplates.length > 0) {
883
+ output += `${pc4.bold("\u5355\u4F53\u9879\u76EE\uFF1A")}
884
+ `;
885
+ for (const t of singleTemplates) {
886
+ output += ` ${pc4.green(t.name.padEnd(24))} ${pc4.dim(t.displayName)}
887
+ `;
888
+ output += ` ${" ".repeat(24)} ${pc4.dim(t.description)}
889
+ `;
739
890
  }
740
- return true;
891
+ output += "\n";
741
892
  }
742
- });
743
- console.log();
744
- console.log(pc5.dim("\u6B63\u5728\u9A8C\u8BC1 Token..."));
745
- const result = await validateToken(token.trim());
746
- if (!result.success) {
747
- console.log(pc5.red(`\u2717 Token \u9A8C\u8BC1\u5931\u8D25: ${result.error}`));
748
- if (result.rateLimitReset) {
749
- console.log(pc5.dim(` Rate limit \u5C06\u5728 ${result.rateLimitReset.toLocaleString()} \u91CD\u7F6E`));
893
+ if (fullstackTemplates.length > 0) {
894
+ output += `${pc4.bold("\u524D\u540E\u7AEF\u5206\u79BB\uFF1A")}
895
+ `;
896
+ for (const t of fullstackTemplates) {
897
+ output += ` ${pc4.green(t.name.padEnd(24))} ${pc4.dim(t.displayName)}
898
+ `;
899
+ output += ` ${" ".repeat(24)} ${pc4.dim(t.description)}
900
+ `;
901
+ }
902
+ output += "\n";
750
903
  }
751
- return false;
904
+ output += `${pc4.dim("\u4F7F\u7528\u65B9\u5F0F\uFF1A")}
905
+ `;
906
+ output += ` ${pc4.green("spec-go my-app --template <\u6A21\u677F\u540D>")}
907
+ `;
908
+ output += ` ${pc4.green("spec-go list")} ${pc4.dim("# \u67E5\u770B\u7B80\u6D01\u5217\u8868")}
909
+ `;
910
+ return output;
752
911
  }
753
- const user = result.data;
754
- console.log(pc5.green(`\u2714 Token \u6709\u6548\uFF0C\u5DF2\u767B\u5F55\u4E3A: ${user.login}${user.name ? ` (${user.name})` : ""}`));
755
- console.log();
756
- const setOrg = await confirm2({
757
- message: "\u662F\u5426\u8BBE\u7F6E\u9ED8\u8BA4\u7EC4\u7EC7? (\u5426\u5219\u4ED3\u5E93\u5C06\u521B\u5EFA\u5728\u4E2A\u4EBA\u8D26\u6237\u4E0B)",
758
- default: false
759
- });
760
- let defaultOrg;
761
- if (setOrg) {
762
- defaultOrg = await input2({
763
- message: "\u9ED8\u8BA4\u7EC4\u7EC7\u540D\u79F0:",
764
- validate: (value) => {
765
- if (!value.trim()) {
766
- return "\u7EC4\u7EC7\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
767
- }
768
- return true;
769
- }
770
- });
771
- defaultOrg = defaultOrg.trim();
912
+ },
913
+ {
914
+ id: "examples",
915
+ title: "\u4F7F\u7528\u793A\u4F8B",
916
+ description: "\u5E38\u89C1\u4F7F\u7528\u573A\u666F\u548C\u6700\u4F73\u5B9E\u8DF5",
917
+ category: "\u5165\u95E8\u6307\u5357",
918
+ content: () => `
919
+ ${pc4.cyan("\u4F7F\u7528\u793A\u4F8B")}
920
+
921
+ ${pc4.bold("1. \u521B\u5EFA React \u9879\u76EE")}
922
+ ${pc4.green("spec-go my-react-app --template react-ts")}
923
+
924
+ ${pc4.bold("2. \u521B\u5EFA\u5168\u6808\u9879\u76EE\uFF08\u524D\u540E\u7AEF\u5206\u79BB\uFF09")}
925
+ ${pc4.green("spec-go my-fullstack --template fullstack-react-node")}
926
+
927
+ ${pc4.bold("3. \u521B\u5EFA API \u9879\u76EE")}
928
+ ${pc4.green("spec-go my-api --template express-ts")}
929
+
930
+ ${pc4.bold("4. \u975E\u4EA4\u4E92\u6A21\u5F0F\uFF08CI/CD\uFF09")}
931
+ ${pc4.green("spec-go my-app --template node-ts --yes --no-install")}
932
+
933
+ ${pc4.bold("5. \u521B\u5EFA\u5E76\u63A8\u9001\u5230 GitHub")}
934
+ ${pc4.green("spec-go my-app --template react-ts --github --public")}
935
+
936
+ ${pc4.bold("6. \u8DF3\u8FC7 Git \u521D\u59CB\u5316")}
937
+ ${pc4.green("spec-go my-app --template vue-ts --no-git")}
938
+
939
+ ${pc4.dim("\u63D0\u793A\uFF1A\u4F7F\u7528")} ${pc4.green("spec-go --help")} ${pc4.dim("\u67E5\u770B\u6240\u6709\u53EF\u7528\u9009\u9879")}
940
+ `
941
+ },
942
+ // 功能特性类
943
+ {
944
+ id: "github",
945
+ title: "GitHub \u96C6\u6210",
946
+ description: "GitHub \u4ED3\u5E93\u521B\u5EFA\u548C\u914D\u7F6E",
947
+ category: "\u529F\u80FD\u7279\u6027",
948
+ content: (data) => `
949
+ ${pc4.cyan("GitHub \u96C6\u6210")}
950
+
951
+ ${pc4.bold("\u914D\u7F6E GitHub Token")}
952
+
953
+ ${pc4.dim("1. \u751F\u6210 Personal Access Token")}
954
+ \u8BBF\u95EE\uFF1Ahttps://github.com/settings/tokens
955
+ \u6743\u9650\uFF1Arepo (\u5B8C\u6574\u4ED3\u5E93\u8BBF\u95EE\u6743\u9650)
956
+
957
+ ${pc4.dim("2. \u914D\u7F6E Token")}
958
+ ${pc4.green("spec-go config --setup-github")}
959
+
960
+ ${pc4.dim("3. \u9A8C\u8BC1\u914D\u7F6E")}
961
+ ${pc4.green("spec-go config --show")}
962
+
963
+ ${pc4.bold("\u4F7F\u7528 GitHub \u96C6\u6210")}
964
+
965
+ ${pc4.dim("\u521B\u5EFA\u9879\u76EE\u5E76\u63A8\u9001\u5230 GitHub\uFF1A")}
966
+ ${pc4.green("spec-go my-app --github")} ${pc4.dim("# \u521B\u5EFA\u79C1\u6709\u4ED3\u5E93")}
967
+ ${pc4.green("spec-go my-app --github --public")} ${pc4.dim("# \u521B\u5EFA\u516C\u5F00\u4ED3\u5E93")}
968
+
969
+ ${pc4.bold("\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E")}
970
+ ${data?.configPath || "~/.spec-go.json"}
971
+
972
+ ${pc4.dim("\u63D0\u793A\uFF1AToken \u4F1A\u5B89\u5168\u5B58\u50A8\u5728\u672C\u5730\u914D\u7F6E\u6587\u4EF6\u4E2D")}
973
+ `
974
+ },
975
+ {
976
+ id: "mcp",
977
+ title: "MCP Server",
978
+ description: "MCP Server \u914D\u7F6E\u548C\u4F7F\u7528",
979
+ category: "\u529F\u80FD\u7279\u6027",
980
+ content: () => `
981
+ ${pc4.cyan("MCP Server \u914D\u7F6E")}
982
+
983
+ ${pc4.bold("\u4EC0\u4E48\u662F MCP Server\uFF1F")}
984
+
985
+ MCP (Model Context Protocol) Server \u5141\u8BB8 Claude Code \u76F4\u63A5\u8C03\u7528 spec-go \u7684\u529F\u80FD\u3002
986
+
987
+ ${pc4.bold("\u6784\u5EFA MCP Server")}
988
+
989
+ ${pc4.dim("1. \u6784\u5EFA\u9879\u76EE")}
990
+ ${pc4.green("pnpm build")}
991
+
992
+ ${pc4.dim("2. \u914D\u7F6E Claude Code")}
993
+ \u7F16\u8F91 ${pc4.dim("~/.claude/settings.json")}\uFF1A
994
+
995
+ {
996
+ "mcpServers": {
997
+ "spec-go": {
998
+ "command": "node",
999
+ "args": ["/path/to/create-idp/dist/mcp/index.js"]
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ ${pc4.dim("3. \u91CD\u542F Claude Code")}
1005
+
1006
+ ${pc4.bold("\u53EF\u7528\u5DE5\u5177")}
1007
+
1008
+ \u2022 create_project - \u521B\u5EFA\u65B0\u9879\u76EE
1009
+ \u2022 list_templates - \u5217\u51FA\u6240\u6709\u6A21\u677F
1010
+ \u2022 validate_project_name - \u9A8C\u8BC1\u9879\u76EE\u540D\u79F0
1011
+ \u2022 init_git_repo - \u521D\u59CB\u5316 Git \u4ED3\u5E93
1012
+ \u2022 create_github_repo - \u521B\u5EFA GitHub \u4ED3\u5E93
1013
+
1014
+ ${pc4.bold("\u4F7F\u7528\u793A\u4F8B")}
1015
+
1016
+ \u5728 Claude Code \u4E2D\u76F4\u63A5\u8BF4\uFF1A
1017
+ ${pc4.dim('"\u4F7F\u7528 spec-go \u521B\u5EFA\u4E00\u4E2A React \u9879\u76EE"')}
1018
+
1019
+ ${pc4.dim("\u63D0\u793A\uFF1A\u67E5\u770B\u9879\u76EE README \u4E86\u89E3\u66F4\u591A MCP \u914D\u7F6E\u7EC6\u8282")}
1020
+ `
1021
+ },
1022
+ // 参考文档类
1023
+ {
1024
+ id: "commands",
1025
+ title: "\u547D\u4EE4\u53C2\u8003",
1026
+ description: "\u6240\u6709\u547D\u4EE4\u7684\u5B8C\u6574\u8BF4\u660E",
1027
+ category: "\u53C2\u8003\u6587\u6863",
1028
+ content: () => `
1029
+ ${pc4.cyan("\u547D\u4EE4\u53C2\u8003")}
1030
+
1031
+ ${pc4.bold("\u4E3B\u547D\u4EE4")}
1032
+
1033
+ ${pc4.green("spec-go [project-name] [options]")}
1034
+ \u521B\u5EFA\u65B0\u9879\u76EE
1035
+
1036
+ ${pc4.dim("\u9009\u9879\uFF1A")}
1037
+ -t, --template <name> \u6307\u5B9A\u6A21\u677F
1038
+ --github \u521B\u5EFA GitHub \u4ED3\u5E93
1039
+ --public \u521B\u5EFA\u516C\u5F00\u4ED3\u5E93\uFF08\u9ED8\u8BA4\u79C1\u6709\uFF09
1040
+ --no-git \u8DF3\u8FC7 Git \u521D\u59CB\u5316
1041
+ --no-install \u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5
1042
+ -y, --yes \u975E\u4EA4\u4E92\u6A21\u5F0F\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C
1043
+ --debug \u542F\u7528\u8C03\u8BD5\u8F93\u51FA
1044
+
1045
+ ${pc4.bold("\u5B50\u547D\u4EE4")}
1046
+
1047
+ ${pc4.green("spec-go list [--json]")}
1048
+ \u5217\u51FA\u6240\u6709\u53EF\u7528\u6A21\u677F
1049
+
1050
+ ${pc4.green("spec-go config")}
1051
+ \u7BA1\u7406\u914D\u7F6E
1052
+ --setup-github \u914D\u7F6E GitHub Token
1053
+ --show \u663E\u793A\u5F53\u524D\u914D\u7F6E
1054
+ --path \u663E\u793A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84
1055
+
1056
+ ${pc4.green("spec-go update [--check]")}
1057
+ \u68C0\u67E5\u5E76\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C
1058
+ --check \u4EC5\u68C0\u67E5\u7248\u672C\uFF0C\u4E0D\u6267\u884C\u66F4\u65B0
1059
+
1060
+ ${pc4.green("spec-go guide [topic] [--list] [--json]")}
1061
+ \u663E\u793A\u4F7F\u7528\u6307\u5357
1062
+ --list \u5217\u51FA\u6240\u6709\u53EF\u7528\u4E3B\u9898
1063
+ --json JSON \u683C\u5F0F\u8F93\u51FA
1064
+
1065
+ ${pc4.dim("\u63D0\u793A\uFF1A\u4F7F\u7528")} ${pc4.green("spec-go <command> --help")} ${pc4.dim("\u67E5\u770B\u547D\u4EE4\u8BE6\u7EC6\u5E2E\u52A9")}
1066
+ `
1067
+ },
1068
+ {
1069
+ id: "config",
1070
+ title: "\u914D\u7F6E\u8BE6\u89E3",
1071
+ description: "\u914D\u7F6E\u6587\u4EF6\u7ED3\u6784\u548C\u8BF4\u660E",
1072
+ category: "\u53C2\u8003\u6587\u6863",
1073
+ content: (data) => `
1074
+ ${pc4.cyan("\u914D\u7F6E\u8BE6\u89E3")}
1075
+
1076
+ ${pc4.bold("\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E")}
1077
+
1078
+ ${data?.configPath || "~/.spec-go.json"}
1079
+
1080
+ ${pc4.bold("\u914D\u7F6E\u7ED3\u6784")}
1081
+
1082
+ {
1083
+ "github": {
1084
+ "token": "ghp_xxxxxxxxxxxx" ${pc4.dim("// GitHub Personal Access Token")}
1085
+ },
1086
+ "defaults": {
1087
+ "template": "node-ts", ${pc4.dim("// \u9ED8\u8BA4\u6A21\u677F")}
1088
+ "github": false, ${pc4.dim("// \u9ED8\u8BA4\u662F\u5426\u521B\u5EFA GitHub \u4ED3\u5E93")}
1089
+ "public": false ${pc4.dim("// \u9ED8\u8BA4\u4ED3\u5E93\u53EF\u89C1\u6027")}
772
1090
  }
773
- const config = loadConfig();
774
- config.github = {
775
- token: token.trim(),
776
- ...defaultOrg && { defaultOrg }
777
- };
778
- saveConfig(config);
779
- console.log();
780
- console.log(pc5.green("\u2714 GitHub \u914D\u7F6E\u5DF2\u4FDD\u5B58"));
781
- console.log(pc5.dim(" \u914D\u7F6E\u6587\u4EF6\u5DF2\u8BBE\u7F6E\u4E3A\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BFB"));
782
- return true;
1091
+ }
1092
+
1093
+ ${pc4.bold("\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4")}
1094
+
1095
+ ${pc4.green("spec-go config --setup-github")} ${pc4.dim("# \u914D\u7F6E GitHub Token")}
1096
+ ${pc4.green("spec-go config --show")} ${pc4.dim("# \u663E\u793A\u5F53\u524D\u914D\u7F6E")}
1097
+ ${pc4.green("spec-go config --path")} ${pc4.dim("# \u663E\u793A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84")}
1098
+
1099
+ ${pc4.bold("\u5B89\u5168\u6027\u8BF4\u660E")}
1100
+
1101
+ \u2022 Token \u5B58\u50A8\u5728\u672C\u5730\u914D\u7F6E\u6587\u4EF6\u4E2D
1102
+ \u2022 \u914D\u7F6E\u6587\u4EF6\u6743\u9650\u5E94\u8BBE\u7F6E\u4E3A\u4EC5\u5F53\u524D\u7528\u6237\u53EF\u8BFB
1103
+ \u2022 \u4E0D\u8981\u5C06\u914D\u7F6E\u6587\u4EF6\u63D0\u4EA4\u5230\u7248\u672C\u63A7\u5236\u7CFB\u7EDF
1104
+
1105
+ ${pc4.dim("\u63D0\u793A\uFF1A\u4F7F\u7528")} ${pc4.green("spec-go guide github")} ${pc4.dim("\u4E86\u89E3\u5982\u4F55\u914D\u7F6E GitHub \u96C6\u6210")}
1106
+ `
1107
+ }
1108
+ ];
1109
+
1110
+ // src/cli/guide.ts
1111
+ function prepareDynamicData() {
1112
+ const data = {};
1113
+ try {
1114
+ const templatesDir = getTemplatesDir();
1115
+ const registryPath = path7.join(templatesDir, "template.config.json");
1116
+ const registry = JSON.parse(
1117
+ fs5.readFileSync(registryPath, "utf-8")
1118
+ );
1119
+ data.templates = registry.templates;
783
1120
  } catch (err) {
784
- if (isUserCancelled(err)) {
785
- console.log(pc5.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
786
- return false;
787
- }
788
- throw err;
1121
+ data.templates = [];
1122
+ }
1123
+ try {
1124
+ data.configPath = getConfigPath();
1125
+ } catch (err) {
1126
+ data.configPath = "~/.spec-go.json";
789
1127
  }
1128
+ return data;
790
1129
  }
791
- function showConfig() {
792
- const config = loadConfig();
1130
+ async function showGuideMenu(data) {
1131
+ const categories = Array.from(new Set(guideTopics.map((t) => t.category)));
1132
+ const choices = categories.flatMap((category) => {
1133
+ const topicsInCategory = guideTopics.filter((t) => t.category === category);
1134
+ return [
1135
+ { name: pc5.cyan(`\u2500\u2500 ${category} \u2500\u2500`), value: "", disabled: true },
1136
+ ...topicsInCategory.map((t) => ({
1137
+ name: `${t.title} ${pc5.dim(`- ${t.description}`)}`,
1138
+ value: t.id
1139
+ }))
1140
+ ];
1141
+ });
1142
+ const topicId = await select2({
1143
+ message: "\u9009\u62E9\u4E3B\u9898:",
1144
+ choices
1145
+ });
1146
+ if (topicId) {
1147
+ showTopic(topicId, data);
1148
+ }
1149
+ }
1150
+ function showTopic(topicId, data) {
1151
+ const topic = guideTopics.find((t) => t.id === topicId);
1152
+ if (!topic) {
1153
+ console.log(pc5.red(`\u9519\u8BEF: \u672A\u627E\u5230\u4E3B\u9898 "${topicId}"`));
1154
+ console.log(pc5.dim(`\u4F7F\u7528 ${pc5.green("spec-go guide --list")} \u67E5\u770B\u6240\u6709\u53EF\u7528\u4E3B\u9898`));
1155
+ process.exit(1);
1156
+ }
793
1157
  console.log();
794
- console.log(pc5.cyan("\u5F53\u524D\u914D\u7F6E:"));
1158
+ console.log(topic.content(data));
795
1159
  console.log();
796
- if (config.defaults) {
797
- console.log(pc5.dim("defaults:"));
798
- if (config.defaults.github !== void 0) {
799
- console.log(` github: ${config.defaults.github}`);
800
- }
801
- if (config.defaults.public !== void 0) {
802
- console.log(` public: ${config.defaults.public}`);
803
- }
804
- if (config.defaults.template !== void 0) {
805
- console.log(` template: ${config.defaults.template}`);
1160
+ }
1161
+ function listTopics() {
1162
+ const categories = Array.from(new Set(guideTopics.map((t) => t.category)));
1163
+ console.log();
1164
+ console.log(pc5.cyan("\u53EF\u7528\u4E3B\u9898"));
1165
+ console.log();
1166
+ for (const category of categories) {
1167
+ console.log(pc5.bold(`${category}:`));
1168
+ const topicsInCategory = guideTopics.filter((t) => t.category === category);
1169
+ for (const topic of topicsInCategory) {
1170
+ console.log(` ${pc5.green(topic.id.padEnd(16))} ${pc5.dim(topic.description)}`);
806
1171
  }
1172
+ console.log();
807
1173
  }
808
- if (config.github) {
809
- console.log(pc5.dim("github:"));
810
- if (config.github.token) {
811
- const masked = config.github.token.slice(0, 8) + "..." + config.github.token.slice(-4);
812
- console.log(` token: ${masked}`);
813
- }
814
- if (config.github.defaultOrg) {
815
- console.log(` defaultOrg: ${config.github.defaultOrg}`);
816
- }
1174
+ console.log(pc5.dim("\u4F7F\u7528\u65B9\u5F0F:"));
1175
+ console.log(` ${pc5.green("spec-go guide <\u4E3B\u9898ID>")}`);
1176
+ console.log();
1177
+ }
1178
+ function outputJson() {
1179
+ const output = {
1180
+ topics: guideTopics.map((t) => ({
1181
+ id: t.id,
1182
+ title: t.title,
1183
+ description: t.description,
1184
+ category: t.category
1185
+ }))
1186
+ };
1187
+ console.log(JSON.stringify(output, null, 2));
1188
+ }
1189
+ async function runGuide(topic, options) {
1190
+ const data = prepareDynamicData();
1191
+ if (options.json) {
1192
+ outputJson();
1193
+ return;
817
1194
  }
818
- if (!config.defaults && !config.github) {
819
- console.log(pc5.dim("(\u65E0\u914D\u7F6E)"));
1195
+ if (options.list) {
1196
+ listTopics();
1197
+ return;
1198
+ }
1199
+ if (topic) {
1200
+ showTopic(topic, data);
1201
+ return;
1202
+ }
1203
+ try {
1204
+ await showGuideMenu(data);
1205
+ } catch (err) {
1206
+ const error = err;
1207
+ if (error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed")) {
1208
+ console.log(pc5.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
1209
+ process.exit(0);
1210
+ }
1211
+ throw err;
820
1212
  }
821
- console.log();
822
1213
  }
823
1214
 
824
- // src/cli.ts
1215
+ // src/cli/console-logger.ts
1216
+ import pc6 from "picocolors";
1217
+ var consoleLogger = {
1218
+ info(message) {
1219
+ console.log(message);
1220
+ },
1221
+ success(message) {
1222
+ console.log(pc6.green(message));
1223
+ },
1224
+ warn(message) {
1225
+ console.log(pc6.yellow(message));
1226
+ },
1227
+ error(message) {
1228
+ console.log(pc6.red(message));
1229
+ },
1230
+ dim(message) {
1231
+ console.log(pc6.dim(message));
1232
+ }
1233
+ };
1234
+
1235
+ // src/cli/cli.ts
825
1236
  function isInteractive(options) {
826
1237
  if (options.yes) return false;
827
1238
  return process.stdin.isTTY === true;
@@ -833,7 +1244,7 @@ async function createCli() {
833
1244
  setDebugEnabled(true);
834
1245
  }
835
1246
  console.log();
836
- console.log(` ${pc6.cyan("spec-go")} ${pc6.dim(`v${version}`)}`);
1247
+ console.log(` ${pc7.cyan("spec-go")} ${pc7.dim(`v${version}`)}`);
837
1248
  console.log();
838
1249
  checkForUpdates(version).catch(() => {
839
1250
  });
@@ -845,7 +1256,7 @@ async function createCli() {
845
1256
  } catch (err) {
846
1257
  const error = err;
847
1258
  if (error.message === "PROMPT_CANCELLED" || error.name === "ExitPromptError" || error.message.includes("User force closed")) {
848
- console.log(pc6.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
1259
+ console.log(pc7.yellow("\n\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
849
1260
  process.exit(ExitCodes.SUCCESS);
850
1261
  }
851
1262
  throw err;
@@ -853,9 +1264,9 @@ async function createCli() {
853
1264
  });
854
1265
  program.command("list").description("\u5217\u51FA\u6240\u6709\u53EF\u7528\u6A21\u677F").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action((options) => {
855
1266
  const templatesDir = getTemplatesDir();
856
- const registryPath = path7.join(templatesDir, "template.config.json");
1267
+ const registryPath = path8.join(templatesDir, "template.config.json");
857
1268
  const registry = JSON.parse(
858
- fs5.readFileSync(registryPath, "utf-8")
1269
+ fs6.readFileSync(registryPath, "utf-8")
859
1270
  );
860
1271
  if (options.json) {
861
1272
  const output = {
@@ -872,15 +1283,15 @@ async function createCli() {
872
1283
  const singleTemplates = registry.templates.filter((t) => t.category !== "fullstack");
873
1284
  const fullstackTemplates = registry.templates.filter((t) => t.category === "fullstack");
874
1285
  console.log();
875
- console.log(pc6.cyan(" \u5355\u4F53\u9879\u76EE:"));
1286
+ console.log(pc7.cyan(" \u5355\u4F53\u9879\u76EE:"));
876
1287
  for (const t of singleTemplates) {
877
- console.log(` ${pc6.green(t.name.padEnd(24))} ${pc6.dim(t.displayName)}`);
1288
+ console.log(` ${pc7.green(t.name.padEnd(24))} ${pc7.dim(t.displayName)}`);
878
1289
  }
879
1290
  if (fullstackTemplates.length > 0) {
880
1291
  console.log();
881
- console.log(pc6.cyan(" \u524D\u540E\u7AEF\u5206\u79BB:"));
1292
+ console.log(pc7.cyan(" \u524D\u540E\u7AEF\u5206\u79BB:"));
882
1293
  for (const t of fullstackTemplates) {
883
- console.log(` ${pc6.green(t.name.padEnd(24))} ${pc6.dim(t.displayName)}`);
1294
+ console.log(` ${pc7.green(t.name.padEnd(24))} ${pc7.dim(t.displayName)}`);
884
1295
  }
885
1296
  }
886
1297
  console.log();
@@ -895,43 +1306,47 @@ async function createCli() {
895
1306
  console.log(getConfigPath());
896
1307
  } else {
897
1308
  console.log();
898
- console.log(pc6.cyan("\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4:"));
1309
+ console.log(pc7.cyan("\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4:"));
899
1310
  console.log();
900
- console.log(` ${pc6.dim("spec-go config --setup-github")} \u914D\u7F6E GitHub Token`);
901
- console.log(` ${pc6.dim("spec-go config --show")} \u663E\u793A\u5F53\u524D\u914D\u7F6E`);
902
- console.log(` ${pc6.dim("spec-go config --path")} \u663E\u793A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84`);
1311
+ console.log(` ${pc7.dim("spec-go config --setup-github")} \u914D\u7F6E GitHub Token`);
1312
+ console.log(` ${pc7.dim("spec-go config --show")} \u663E\u793A\u5F53\u524D\u914D\u7F6E`);
1313
+ console.log(` ${pc7.dim("spec-go config --path")} \u663E\u793A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84`);
903
1314
  console.log();
904
1315
  }
905
1316
  });
906
1317
  program.command("update").description("\u68C0\u67E5\u5E76\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C").option("--check", "\u4EC5\u68C0\u67E5\u7248\u672C\uFF0C\u4E0D\u6267\u884C\u66F4\u65B0").action(async (options) => {
907
1318
  await runUpdate(version, options.check ?? false);
908
1319
  });
1320
+ program.command("guide [topic]").description("\u663E\u793A\u4F7F\u7528\u6307\u5357\u548C\u5E2E\u52A9\u6587\u6863").option("--list", "\u5217\u51FA\u6240\u6709\u53EF\u7528\u4E3B\u9898").option("--json", "\u4EE5 JSON \u683C\u5F0F\u8F93\u51FA").action(async (topic, options) => {
1321
+ await runGuide(topic, options);
1322
+ });
909
1323
  return program;
910
1324
  }
911
1325
  async function executeProject(options) {
912
1326
  const templateConfig = await scaffoldProject(options);
913
- console.log(pc6.green("\u2714 \u9879\u76EE\u6587\u4EF6\u5DF2\u751F\u6210"));
1327
+ console.log(pc7.green("\u2714 \u9879\u76EE\u6587\u4EF6\u5DF2\u751F\u6210"));
914
1328
  if (!options.noInstall && templateConfig) {
915
1329
  if (templateConfig.type === "workspace") {
916
- await runWorkspacePostInit(options.targetDir, templateConfig);
917
- console.log(pc6.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
1330
+ await runWorkspacePostInit(options.targetDir, templateConfig, { logger: consoleLogger });
1331
+ console.log(pc7.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
918
1332
  } else if (templateConfig.postInit) {
919
- await runPostInit(options.targetDir, templateConfig.postInit);
920
- console.log(pc6.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
1333
+ await runPostInit(options.targetDir, templateConfig.postInit, { logger: consoleLogger });
1334
+ console.log(pc7.green("\u2714 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210"));
921
1335
  }
922
1336
  }
923
1337
  if (options.initGit) {
924
1338
  await initGit(options.targetDir);
925
- console.log(pc6.green("\u2714 Git \u4ED3\u5E93\u5DF2\u521D\u59CB\u5316"));
1339
+ console.log(pc7.green("\u2714 Git \u4ED3\u5E93\u5DF2\u521D\u59CB\u5316"));
926
1340
  }
927
1341
  if (options.createGithub) {
928
- const repoUrl = await createGithubRepo(
929
- options.projectName,
930
- options.targetDir,
931
- options.isPublic
932
- );
1342
+ const repoUrl = await createGithubRepo({
1343
+ repoName: options.projectName,
1344
+ targetDir: options.targetDir,
1345
+ isPublic: options.isPublic,
1346
+ logger: consoleLogger
1347
+ });
933
1348
  if (repoUrl) {
934
- console.log(pc6.green(`\u2714 GitHub \u4ED3\u5E93\u5DF2\u521B\u5EFA: ${pc6.cyan(repoUrl)}`));
1349
+ console.log(pc7.green(`\u2714 GitHub \u4ED3\u5E93\u5DF2\u521B\u5EFA: ${pc7.cyan(repoUrl)}`));
935
1350
  }
936
1351
  }
937
1352
  console.log();
@@ -939,21 +1354,21 @@ async function executeProject(options) {
939
1354
  const cdCmd = options.targetDir !== process.cwd() ? `cd ${options.projectName} && ` : "";
940
1355
  if (templateConfig?.type === "workspace") {
941
1356
  if (pm === "pnpm") {
942
- console.log(pc6.dim(` ${cdCmd}pnpm dev`));
1357
+ console.log(pc7.dim(` ${cdCmd}pnpm dev`));
943
1358
  } else {
944
- console.log(pc6.dim(` ${cdCmd}make dev`));
1359
+ console.log(pc7.dim(` ${cdCmd}make dev`));
945
1360
  }
946
1361
  } else if (pm === "maven") {
947
- console.log(pc6.dim(` ${cdCmd}./mvnw spring-boot:run`));
1362
+ console.log(pc7.dim(` ${cdCmd}./mvnw spring-boot:run`));
948
1363
  } else if (pm === "gradle") {
949
- console.log(pc6.dim(` ${cdCmd}./gradlew bootRun`));
1364
+ console.log(pc7.dim(` ${cdCmd}./gradlew bootRun`));
950
1365
  } else {
951
- console.log(pc6.dim(` ${cdCmd}${pm} dev`));
1366
+ console.log(pc7.dim(` ${cdCmd}${pm} dev`));
952
1367
  }
953
1368
  console.log();
954
1369
  }
955
1370
 
956
- // src/index.ts
1371
+ // src/cli/index.ts
957
1372
  async function main() {
958
1373
  ensureConfigExists();
959
1374
  const program = await createCli();