@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.
- package/README.md +60 -0
- package/dist/{index.js → cli/index.js} +800 -385
- package/dist/cli/index.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +591 -0
- package/dist/mcp/index.js.map +1 -0
- package/package.json +8 -4
- package/dist/index.js.map +0 -1
- /package/dist/{index.d.ts → cli/index.d.ts} +0 -0
|
@@ -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
|
|
11
|
-
import
|
|
9
|
+
// src/cli/cli.ts
|
|
10
|
+
import path8 from "path";
|
|
11
|
+
import fs6 from "fs";
|
|
12
12
|
import { Command } from "commander";
|
|
13
|
-
import
|
|
13
|
+
import pc7 from "picocolors";
|
|
14
14
|
|
|
15
15
|
// package.json
|
|
16
|
-
var version = "0.
|
|
16
|
+
var version = "0.3.1";
|
|
17
17
|
|
|
18
|
-
// src/prompts.ts
|
|
19
|
-
import
|
|
20
|
-
import
|
|
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/
|
|
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
|
|
27
|
-
|
|
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/
|
|
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 =
|
|
138
|
+
var __dirname = path2.dirname(__filename);
|
|
63
139
|
function getTemplatesDir() {
|
|
64
|
-
return
|
|
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
|
|
74
|
-
if (!
|
|
149
|
+
const fs7 = __require("fs");
|
|
150
|
+
if (!fs7.existsSync(dirPath)) {
|
|
75
151
|
return true;
|
|
76
152
|
}
|
|
77
|
-
const files =
|
|
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/
|
|
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/
|
|
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
|
|
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(
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
-
|
|
388
|
+
logger.warn(`\u26A0 GitHub Token \u65E0\u6548: ${validateResult.error}`);
|
|
504
389
|
if (validateResult.rateLimitReset) {
|
|
505
|
-
|
|
390
|
+
logger.dim(` Rate limit \u5C06\u5728 ${validateResult.rateLimitReset.toLocaleString()} \u91CD\u7F6E`);
|
|
506
391
|
}
|
|
507
|
-
|
|
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
|
-
|
|
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
|
-
|
|
521
|
-
|
|
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
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
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
|
-
|
|
457
|
+
logger.warn(`\u26A0 ${cmd.description} \u5931\u8D25`);
|
|
574
458
|
if (result.stderr) {
|
|
575
|
-
|
|
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 =
|
|
588
|
-
|
|
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/
|
|
596
|
-
|
|
597
|
-
|
|
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
|
|
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
|
-
|
|
784
|
+
pc3.yellow(` \u26A0 \u53D1\u73B0\u65B0\u7248\u672C ${pc3.bold(latestVersion)}\uFF0C\u5F53\u524D ${currentVersion}`)
|
|
659
785
|
);
|
|
660
|
-
console.log(
|
|
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(
|
|
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(
|
|
814
|
+
console.log(pc3.green(`\u2714 \u5DF2\u662F\u6700\u65B0\u7248\u672C (${currentVersion})`));
|
|
689
815
|
return;
|
|
690
816
|
}
|
|
691
|
-
console.log(
|
|
817
|
+
console.log(pc3.cyan(`\u53D1\u73B0\u65B0\u7248\u672C: ${currentVersion} \u2192 ${latestVersion}`));
|
|
692
818
|
if (checkOnly) {
|
|
693
|
-
console.log(
|
|
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(
|
|
824
|
+
console.log(pc3.dim(`\u6B63\u5728\u6267\u884C: ${updateCmd}`));
|
|
699
825
|
try {
|
|
700
826
|
execSync(updateCmd, { stdio: "inherit" });
|
|
701
|
-
console.log(
|
|
827
|
+
console.log(pc3.green(`\u2714 \u66F4\u65B0\u5B8C\u6210`));
|
|
702
828
|
} catch {
|
|
703
|
-
console.log(
|
|
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/
|
|
709
|
-
import
|
|
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
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
|
|
891
|
+
output += "\n";
|
|
741
892
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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
|
-
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
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
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
|
792
|
-
const
|
|
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(
|
|
1158
|
+
console.log(topic.content(data));
|
|
795
1159
|
console.log();
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
|
|
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
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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 (
|
|
819
|
-
|
|
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(` ${
|
|
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(
|
|
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 =
|
|
1267
|
+
const registryPath = path8.join(templatesDir, "template.config.json");
|
|
857
1268
|
const registry = JSON.parse(
|
|
858
|
-
|
|
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(
|
|
1286
|
+
console.log(pc7.cyan(" \u5355\u4F53\u9879\u76EE:"));
|
|
876
1287
|
for (const t of singleTemplates) {
|
|
877
|
-
console.log(` ${
|
|
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(
|
|
1292
|
+
console.log(pc7.cyan(" \u524D\u540E\u7AEF\u5206\u79BB:"));
|
|
882
1293
|
for (const t of fullstackTemplates) {
|
|
883
|
-
console.log(` ${
|
|
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(
|
|
1309
|
+
console.log(pc7.cyan("\u914D\u7F6E\u7BA1\u7406\u547D\u4EE4:"));
|
|
899
1310
|
console.log();
|
|
900
|
-
console.log(` ${
|
|
901
|
-
console.log(` ${
|
|
902
|
-
console.log(` ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1357
|
+
console.log(pc7.dim(` ${cdCmd}pnpm dev`));
|
|
943
1358
|
} else {
|
|
944
|
-
console.log(
|
|
1359
|
+
console.log(pc7.dim(` ${cdCmd}make dev`));
|
|
945
1360
|
}
|
|
946
1361
|
} else if (pm === "maven") {
|
|
947
|
-
console.log(
|
|
1362
|
+
console.log(pc7.dim(` ${cdCmd}./mvnw spring-boot:run`));
|
|
948
1363
|
} else if (pm === "gradle") {
|
|
949
|
-
console.log(
|
|
1364
|
+
console.log(pc7.dim(` ${cdCmd}./gradlew bootRun`));
|
|
950
1365
|
} else {
|
|
951
|
-
console.log(
|
|
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();
|