yg-team-cli 2.0.0 → 2.1.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/dist/cli.js +1674 -274
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1927 -489
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/index.js
CHANGED
|
@@ -1,173 +1,864 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
2
11
|
|
|
3
|
-
//
|
|
4
|
-
import
|
|
5
|
-
import
|
|
12
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var init_esm_shims = __esm({
|
|
16
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
}
|
|
19
|
+
});
|
|
6
20
|
|
|
7
21
|
// src/lib/logger.ts
|
|
8
22
|
import chalk from "chalk";
|
|
9
23
|
import ora from "ora";
|
|
10
|
-
var Logger
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
24
|
+
var Logger, logger;
|
|
25
|
+
var init_logger = __esm({
|
|
26
|
+
"src/lib/logger.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
init_esm_shims();
|
|
29
|
+
Logger = class {
|
|
30
|
+
spinner = null;
|
|
31
|
+
/**
|
|
32
|
+
* 打印标题(加粗)
|
|
33
|
+
*/
|
|
34
|
+
header(text) {
|
|
35
|
+
console.log(chalk.bold(text));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 打印成功信息(绿色)
|
|
39
|
+
*/
|
|
40
|
+
success(text) {
|
|
41
|
+
console.log(chalk.green("\u2713"), text);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 打印错误信息(红色)
|
|
45
|
+
*/
|
|
46
|
+
error(text) {
|
|
47
|
+
console.error(chalk.red("\u2717"), text);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 打印警告信息(黄色)
|
|
51
|
+
*/
|
|
52
|
+
warn(text) {
|
|
53
|
+
console.warn(chalk.yellow("\u26A0"), text);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 打印信息(蓝色)
|
|
57
|
+
*/
|
|
58
|
+
info(text) {
|
|
59
|
+
console.info(chalk.blue("\u2139"), text);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 打印步骤信息
|
|
63
|
+
*/
|
|
64
|
+
step(text) {
|
|
65
|
+
console.log(chalk.cyan("\u2192"), text);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 打印空行
|
|
69
|
+
*/
|
|
70
|
+
newLine() {
|
|
71
|
+
console.log("");
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 打印分隔线
|
|
75
|
+
*/
|
|
76
|
+
separator(char = "\u2500", length = 50) {
|
|
77
|
+
console.log(chalk.gray(char.repeat(length)));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 开始加载动画
|
|
81
|
+
*/
|
|
82
|
+
startLoading(text) {
|
|
83
|
+
this.spinner = ora({
|
|
84
|
+
text,
|
|
85
|
+
color: "cyan"
|
|
86
|
+
}).start();
|
|
87
|
+
return this.spinner;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 更新加载文本
|
|
91
|
+
*/
|
|
92
|
+
updateLoading(text) {
|
|
93
|
+
if (this.spinner) {
|
|
94
|
+
this.spinner.text = text;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 停止加载并显示成功
|
|
99
|
+
*/
|
|
100
|
+
succeedLoading(text) {
|
|
101
|
+
if (this.spinner) {
|
|
102
|
+
this.spinner.succeed(text);
|
|
103
|
+
this.spinner = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 停止加载并显示失败
|
|
108
|
+
*/
|
|
109
|
+
failLoading(text) {
|
|
110
|
+
if (this.spinner) {
|
|
111
|
+
this.spinner.fail(text);
|
|
112
|
+
this.spinner = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 停止加载并显示警告
|
|
117
|
+
*/
|
|
118
|
+
warnLoading(text) {
|
|
119
|
+
if (this.spinner) {
|
|
120
|
+
this.spinner.warn(text);
|
|
121
|
+
this.spinner = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 停止加载并显示信息
|
|
126
|
+
*/
|
|
127
|
+
infoLoading(text) {
|
|
128
|
+
if (this.spinner) {
|
|
129
|
+
this.spinner.info(text);
|
|
130
|
+
this.spinner = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 打印表格
|
|
135
|
+
*/
|
|
136
|
+
table(headers, rows) {
|
|
137
|
+
const columnWidths = headers.map((h, i) => {
|
|
138
|
+
const maxWidth = Math.max(
|
|
139
|
+
h.length,
|
|
140
|
+
...rows.map((row) => (row[i] || "").length)
|
|
141
|
+
);
|
|
142
|
+
return maxWidth + 2;
|
|
143
|
+
});
|
|
144
|
+
const headerRow = headers.map((h, i) => chalk.bold(h.padEnd(columnWidths[i]))).join("");
|
|
145
|
+
console.log(headerRow);
|
|
146
|
+
const separator = columnWidths.map((w) => "\u2500".repeat(w)).join("\u253C");
|
|
147
|
+
console.log(chalk.gray(separator));
|
|
148
|
+
rows.forEach((row) => {
|
|
149
|
+
const dataRow = row.map((cell, i) => (cell || "").padEnd(columnWidths[i])).join("");
|
|
150
|
+
console.log(dataRow);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 打印代码块
|
|
155
|
+
*/
|
|
156
|
+
code(code, language = "") {
|
|
157
|
+
if (language) {
|
|
158
|
+
console.log(chalk.gray(`\`\`\`${language}`));
|
|
159
|
+
}
|
|
160
|
+
console.log(chalk.gray(code));
|
|
161
|
+
if (language) {
|
|
162
|
+
console.log(chalk.gray("```"));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 打印列表
|
|
167
|
+
*/
|
|
168
|
+
list(items, indent = 2) {
|
|
169
|
+
items.forEach((item) => {
|
|
170
|
+
console.log(" ".repeat(indent) + chalk.gray("\u2022") + " " + item);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 打印带标签的文本
|
|
175
|
+
*/
|
|
176
|
+
labeled(label, text, labelColor = "blue") {
|
|
177
|
+
const coloredLabel = chalk[labelColor](`[${label}]`);
|
|
178
|
+
console.log(`${coloredLabel} ${text}`);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
logger = new Logger();
|
|
133
182
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// src/lib/utils.ts
|
|
186
|
+
import fs from "fs-extra";
|
|
187
|
+
import path2 from "path";
|
|
188
|
+
import { glob } from "glob";
|
|
189
|
+
var FileUtils, StringUtils2, DateUtils, GitUtils, SpecUtils;
|
|
190
|
+
var init_utils = __esm({
|
|
191
|
+
"src/lib/utils.ts"() {
|
|
192
|
+
"use strict";
|
|
193
|
+
init_esm_shims();
|
|
194
|
+
FileUtils = class {
|
|
195
|
+
/**
|
|
196
|
+
* 确保目录存在
|
|
197
|
+
*/
|
|
198
|
+
static async ensureDir(dir) {
|
|
199
|
+
await fs.ensureDir(dir);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 读取文件内容
|
|
203
|
+
*/
|
|
204
|
+
static async read(file) {
|
|
205
|
+
return await fs.readFile(file, "utf-8");
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* 写入文件内容
|
|
209
|
+
*/
|
|
210
|
+
static async write(file, content) {
|
|
211
|
+
await fs.writeFile(file, content, "utf-8");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 检查文件是否存在
|
|
215
|
+
*/
|
|
216
|
+
static async exists(file) {
|
|
217
|
+
return await fs.pathExists(file);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 复制文件
|
|
221
|
+
*/
|
|
222
|
+
static async copy(src, dest) {
|
|
223
|
+
await fs.copy(src, dest);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 删除文件或目录
|
|
227
|
+
*/
|
|
228
|
+
static async remove(file) {
|
|
229
|
+
await fs.remove(file);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 移动文件
|
|
233
|
+
*/
|
|
234
|
+
static async move(src, dest) {
|
|
235
|
+
await fs.move(src, dest);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 使用 glob 查找文件
|
|
239
|
+
*/
|
|
240
|
+
static async findFiles(pattern, cwd) {
|
|
241
|
+
return await glob(pattern, {
|
|
242
|
+
cwd,
|
|
243
|
+
absolute: false,
|
|
244
|
+
nodir: true
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* 读取 JSON 文件
|
|
249
|
+
*/
|
|
250
|
+
static async readJson(file) {
|
|
251
|
+
return await fs.readJson(file);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 写入 JSON 文件
|
|
255
|
+
*/
|
|
256
|
+
static async writeJson(file, data) {
|
|
257
|
+
await fs.writeJson(file, data, { spaces: 2 });
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
StringUtils2 = class {
|
|
261
|
+
/**
|
|
262
|
+
* 转换为 kebab-case
|
|
263
|
+
*/
|
|
264
|
+
static toKebabCase(str) {
|
|
265
|
+
return str.toLowerCase().replace(/[\s_]+/g, "-").replace(/[^\w\-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 转换为 PascalCase
|
|
269
|
+
*/
|
|
270
|
+
static toPascalCase(str) {
|
|
271
|
+
return str.replace(/[-_\s](\w)/g, (_, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toUpperCase());
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* 转换为 camelCase
|
|
275
|
+
*/
|
|
276
|
+
static toCamelCase(str) {
|
|
277
|
+
const pascal = this.toPascalCase(str);
|
|
278
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* 转换为 snake_case
|
|
282
|
+
*/
|
|
283
|
+
static toSnakeCase(str) {
|
|
284
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`).replace(/^_/, "").replace(/-+/g, "_").replace(/[\s]+/g, "_");
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* 首字母大写
|
|
288
|
+
*/
|
|
289
|
+
static capitalize(str) {
|
|
290
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* 截断字符串
|
|
294
|
+
*/
|
|
295
|
+
static truncate(str, length, suffix = "...") {
|
|
296
|
+
if (str.length <= length) return str;
|
|
297
|
+
return str.slice(0, length - suffix.length) + suffix;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 生成 slug
|
|
301
|
+
*/
|
|
302
|
+
static slugify(str) {
|
|
303
|
+
return str.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
DateUtils = class _DateUtils {
|
|
307
|
+
/**
|
|
308
|
+
* 格式化日期
|
|
309
|
+
*/
|
|
310
|
+
static format(date = /* @__PURE__ */ new Date(), format = "YYYY-MM-DD HH:mm:ss") {
|
|
311
|
+
const year = date.getFullYear();
|
|
312
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
313
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
314
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
315
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
316
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
317
|
+
return format.replace("YYYY", String(year)).replace("MM", month).replace("DD", day).replace("HH", hours).replace("mm", minutes).replace("ss", seconds);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 获取相对时间
|
|
321
|
+
*/
|
|
322
|
+
static relative(date) {
|
|
323
|
+
const now = /* @__PURE__ */ new Date();
|
|
324
|
+
const diff = now.getTime() - date.getTime();
|
|
325
|
+
const seconds = Math.floor(diff / 1e3);
|
|
326
|
+
const minutes = Math.floor(seconds / 60);
|
|
327
|
+
const hours = Math.floor(minutes / 60);
|
|
328
|
+
const days = Math.floor(hours / 24);
|
|
329
|
+
if (days > 7) {
|
|
330
|
+
return _DateUtils.format(date, "YYYY-MM-DD");
|
|
331
|
+
} else if (days > 0) {
|
|
332
|
+
return `${days} \u5929\u524D`;
|
|
333
|
+
} else if (hours > 0) {
|
|
334
|
+
return `${hours} \u5C0F\u65F6\u524D`;
|
|
335
|
+
} else if (minutes > 0) {
|
|
336
|
+
return `${minutes} \u5206\u949F\u524D`;
|
|
337
|
+
} else {
|
|
338
|
+
return "\u521A\u521A";
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
GitUtils = class {
|
|
343
|
+
/**
|
|
344
|
+
* 检查是否在 Git 仓库中
|
|
345
|
+
*/
|
|
346
|
+
static async isGitRepo(cwd = process.cwd()) {
|
|
347
|
+
const gitDir = path2.join(cwd, ".git");
|
|
348
|
+
return await FileUtils.exists(gitDir);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* 获取当前分支名
|
|
352
|
+
*/
|
|
353
|
+
static async getCurrentBranch(cwd = process.cwd()) {
|
|
354
|
+
const { execa: execa5 } = await import("execa");
|
|
355
|
+
const { stdout } = await execa5("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
356
|
+
cwd
|
|
357
|
+
});
|
|
358
|
+
return stdout.trim();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 获取当前 commit hash
|
|
362
|
+
*/
|
|
363
|
+
static async getCurrentCommit(cwd = process.cwd()) {
|
|
364
|
+
const { execa: execa5 } = await import("execa");
|
|
365
|
+
const { stdout } = await execa5("git", ["rev-parse", "HEAD"], { cwd });
|
|
366
|
+
return stdout.trim().slice(0, 7);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
SpecUtils = class {
|
|
370
|
+
/**
|
|
371
|
+
* 解析 spec 文件
|
|
372
|
+
*/
|
|
373
|
+
static async parseSpec(file) {
|
|
374
|
+
const content = await FileUtils.read(file);
|
|
375
|
+
const spec = {};
|
|
376
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
377
|
+
if (titleMatch) {
|
|
378
|
+
spec.title = titleMatch[1];
|
|
379
|
+
}
|
|
380
|
+
const nameMatch = content.match(/\*\*功能名称\*\*:\s*(.+)$/m);
|
|
381
|
+
if (nameMatch) {
|
|
382
|
+
spec.name = nameMatch[1];
|
|
383
|
+
}
|
|
384
|
+
const priorityMatch = content.match(/\*\*优先级\*\*:\s*(P[0-2])/);
|
|
385
|
+
if (priorityMatch) {
|
|
386
|
+
spec.priority = priorityMatch[1];
|
|
387
|
+
}
|
|
388
|
+
const statusMatch = content.match(/\*\*状态\*\*:\s*(.+)$/m);
|
|
389
|
+
if (statusMatch) {
|
|
390
|
+
spec.status = statusMatch[1];
|
|
391
|
+
}
|
|
392
|
+
const depSection = content.match(/## 依赖关系\s+([\s\S]+?)##/m);
|
|
393
|
+
if (depSection) {
|
|
394
|
+
const depMatches = depSection[1].matchAll(/- \[([ x])\]\s*(.+)$/gm);
|
|
395
|
+
spec.dependencies = Array.from(depMatches).map((m) => ({
|
|
396
|
+
completed: m[1] === "x",
|
|
397
|
+
name: m[2]
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
return spec;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* 获取 spec 状态
|
|
404
|
+
*/
|
|
405
|
+
static async getSpecStatus(file) {
|
|
406
|
+
try {
|
|
407
|
+
const spec = await this.parseSpec(file);
|
|
408
|
+
return spec.status || "\u672A\u77E5";
|
|
409
|
+
} catch {
|
|
410
|
+
return "\u9519\u8BEF";
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
145
414
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// src/lib/user-config.ts
|
|
418
|
+
var user_config_exports = {};
|
|
419
|
+
__export(user_config_exports, {
|
|
420
|
+
UserConfigManager: () => UserConfigManager,
|
|
421
|
+
userConfigManager: () => userConfigManager
|
|
422
|
+
});
|
|
423
|
+
import path4 from "path";
|
|
424
|
+
import os from "os";
|
|
425
|
+
import crypto from "crypto";
|
|
426
|
+
var UserConfigManager, userConfigManager;
|
|
427
|
+
var init_user_config = __esm({
|
|
428
|
+
"src/lib/user-config.ts"() {
|
|
429
|
+
"use strict";
|
|
430
|
+
init_esm_shims();
|
|
431
|
+
init_utils();
|
|
432
|
+
init_logger();
|
|
433
|
+
UserConfigManager = class {
|
|
434
|
+
configPath;
|
|
435
|
+
constructor() {
|
|
436
|
+
const configDir = path4.join(os.homedir(), ".team-cli");
|
|
437
|
+
this.configPath = path4.join(configDir, "config.json");
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* 加载用户配置
|
|
441
|
+
*/
|
|
442
|
+
async load() {
|
|
443
|
+
try {
|
|
444
|
+
const exists = await FileUtils.exists(this.configPath);
|
|
445
|
+
if (!exists) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
const content = await FileUtils.read(this.configPath);
|
|
449
|
+
const config = JSON.parse(content);
|
|
450
|
+
if (config.gitlab?.accessToken) {
|
|
451
|
+
config.gitlab.accessToken = this.decrypt(config.gitlab.accessToken);
|
|
452
|
+
}
|
|
453
|
+
return config;
|
|
454
|
+
} catch (error) {
|
|
455
|
+
logger.debug(`\u52A0\u8F7D\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* 保存用户配置
|
|
461
|
+
*/
|
|
462
|
+
async save(config) {
|
|
463
|
+
try {
|
|
464
|
+
const configDir = path4.dirname(this.configPath);
|
|
465
|
+
await FileUtils.ensureDir(configDir);
|
|
466
|
+
const configToSave = { ...config };
|
|
467
|
+
if (configToSave.gitlab?.accessToken) {
|
|
468
|
+
configToSave.gitlab.accessToken = this.encrypt(configToSave.gitlab.accessToken);
|
|
469
|
+
}
|
|
470
|
+
const content = JSON.stringify(configToSave, null, 2);
|
|
471
|
+
await FileUtils.write(this.configPath, content);
|
|
472
|
+
} catch (error) {
|
|
473
|
+
throw new Error(`\u4FDD\u5B58\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* 更新 GitLab Token
|
|
478
|
+
*/
|
|
479
|
+
async updateGitLabToken(token, baseUrl) {
|
|
480
|
+
const config = await this.load() || {
|
|
481
|
+
gitlab: {
|
|
482
|
+
accessToken: "",
|
|
483
|
+
baseUrl: "https://gitlab.com",
|
|
484
|
+
timeout: 3e4
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
config.gitlab.accessToken = token;
|
|
488
|
+
if (baseUrl) {
|
|
489
|
+
config.gitlab.baseUrl = baseUrl;
|
|
490
|
+
}
|
|
491
|
+
await this.save(config);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* 获取 GitLab Token
|
|
495
|
+
*/
|
|
496
|
+
async getGitLabToken() {
|
|
497
|
+
const config = await this.load();
|
|
498
|
+
return config?.gitlab?.accessToken || null;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* 获取 GitLab 配置
|
|
502
|
+
*/
|
|
503
|
+
async getGitLabConfig() {
|
|
504
|
+
const config = await this.load();
|
|
505
|
+
return config?.gitlab || null;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 检查是否已有配置
|
|
509
|
+
*/
|
|
510
|
+
async hasConfig() {
|
|
511
|
+
const config = await this.load();
|
|
512
|
+
return config !== null && config.gitlab?.accessToken !== void 0;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* 删除配置
|
|
516
|
+
*/
|
|
517
|
+
async removeConfig() {
|
|
518
|
+
try {
|
|
519
|
+
const exists = await FileUtils.exists(this.configPath);
|
|
520
|
+
if (exists) {
|
|
521
|
+
await FileUtils.remove(this.configPath);
|
|
522
|
+
}
|
|
523
|
+
} catch (error) {
|
|
524
|
+
throw new Error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* 简单加密 (使用机器特定密钥)
|
|
529
|
+
*/
|
|
530
|
+
encrypt(text) {
|
|
531
|
+
const key = this.getMachineKey();
|
|
532
|
+
const iv = crypto.randomBytes(16);
|
|
533
|
+
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
|
|
534
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
535
|
+
encrypted += cipher.final("hex");
|
|
536
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* 简单解密
|
|
540
|
+
*/
|
|
541
|
+
decrypt(encryptedText) {
|
|
542
|
+
try {
|
|
543
|
+
const key = this.getMachineKey();
|
|
544
|
+
const parts = encryptedText.split(":");
|
|
545
|
+
const iv = Buffer.from(parts[0], "hex");
|
|
546
|
+
const encrypted = parts[1];
|
|
547
|
+
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
|
|
548
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
549
|
+
decrypted += decipher.final("utf8");
|
|
550
|
+
return decrypted;
|
|
551
|
+
} catch {
|
|
552
|
+
return encryptedText;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* 获取机器特定密钥
|
|
557
|
+
*/
|
|
558
|
+
getMachineKey() {
|
|
559
|
+
const hostname = os.hostname();
|
|
560
|
+
const platform = os.platform();
|
|
561
|
+
const arch = os.arch();
|
|
562
|
+
const cpus = os.cpus();
|
|
563
|
+
const machineInfo = `${hostname}-${platform}-${arch}-${cpus[0]?.model || "unknown"}`;
|
|
564
|
+
return crypto.createHash("sha256").update(machineInfo).digest();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* 获取配置目录
|
|
568
|
+
*/
|
|
569
|
+
getConfigDir() {
|
|
570
|
+
return path4.dirname(this.configPath);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* 获取配置文件路径
|
|
574
|
+
*/
|
|
575
|
+
getConfigPath() {
|
|
576
|
+
return this.configPath;
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
userConfigManager = new UserConfigManager();
|
|
153
580
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// src/lib/gitlab-api.ts
|
|
584
|
+
var gitlab_api_exports = {};
|
|
585
|
+
__export(gitlab_api_exports, {
|
|
586
|
+
GitLabAPI: () => GitLabAPI
|
|
587
|
+
});
|
|
588
|
+
var GitLabAPI;
|
|
589
|
+
var init_gitlab_api = __esm({
|
|
590
|
+
"src/lib/gitlab-api.ts"() {
|
|
591
|
+
"use strict";
|
|
592
|
+
init_esm_shims();
|
|
593
|
+
GitLabAPI = class {
|
|
594
|
+
config;
|
|
595
|
+
defaultTimeout = 3e4;
|
|
596
|
+
constructor(config) {
|
|
597
|
+
this.config = {
|
|
598
|
+
...config,
|
|
599
|
+
timeout: config.timeout || this.defaultTimeout
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* 验证 Token 是否有效
|
|
604
|
+
*/
|
|
605
|
+
async authenticate() {
|
|
606
|
+
try {
|
|
607
|
+
const response = await this.request("/user");
|
|
608
|
+
return response.ok;
|
|
609
|
+
} catch (error) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* 列出项目的所有 Tags
|
|
615
|
+
*/
|
|
616
|
+
async listTags(projectPath) {
|
|
617
|
+
try {
|
|
618
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
619
|
+
const response = await this.request(`/projects/${encodedPath}/repository/tags?per_page=100`);
|
|
620
|
+
if (!response.ok) {
|
|
621
|
+
throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
|
|
622
|
+
}
|
|
623
|
+
const tags = await response.json();
|
|
624
|
+
return tags.sort((a, b) => {
|
|
625
|
+
return this.compareVersionStrings(b.name, a.name);
|
|
626
|
+
});
|
|
627
|
+
} catch (error) {
|
|
628
|
+
throw new Error(`\u83B7\u53D6 Tags \u5931\u8D25: ${error}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* 列出项目的所有 Branches
|
|
633
|
+
*/
|
|
634
|
+
async listBranches(projectPath) {
|
|
635
|
+
try {
|
|
636
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
637
|
+
const response = await this.request(`/projects/${encodedPath}/repository/branches?per_page=100`);
|
|
638
|
+
if (!response.ok) {
|
|
639
|
+
throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
|
|
640
|
+
}
|
|
641
|
+
const branches = await response.json();
|
|
642
|
+
return branches.sort((a, b) => {
|
|
643
|
+
if (a.default) return -1;
|
|
644
|
+
if (b.default) return 1;
|
|
645
|
+
return a.name.localeCompare(b.name);
|
|
646
|
+
});
|
|
647
|
+
} catch (error) {
|
|
648
|
+
throw new Error(`\u83B7\u53D6 Branches \u5931\u8D25: ${error}`);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* 验证 Tag 是否存在
|
|
653
|
+
*/
|
|
654
|
+
async validateTag(projectPath, tag) {
|
|
655
|
+
try {
|
|
656
|
+
const tags = await this.listTags(projectPath);
|
|
657
|
+
return tags.some((t) => t.name === tag);
|
|
658
|
+
} catch {
|
|
659
|
+
return false;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* 验证 Branch 是否存在
|
|
664
|
+
*/
|
|
665
|
+
async validateBranch(projectPath, branch) {
|
|
666
|
+
try {
|
|
667
|
+
const branches = await this.listBranches(projectPath);
|
|
668
|
+
return branches.some((b) => b.name === branch);
|
|
669
|
+
} catch {
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* 获取项目信息
|
|
675
|
+
*/
|
|
676
|
+
async getProject(projectPath) {
|
|
677
|
+
try {
|
|
678
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
679
|
+
const response = await this.request(`/projects/${encodedPath}`);
|
|
680
|
+
if (!response.ok) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
return await response.json();
|
|
684
|
+
} catch {
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* 获取指定 Tag 的 commit 信息
|
|
690
|
+
*/
|
|
691
|
+
async getTagCommit(projectPath, tag) {
|
|
692
|
+
try {
|
|
693
|
+
const tags = await this.listTags(projectPath);
|
|
694
|
+
const targetTag = tags.find((t) => t.name === tag);
|
|
695
|
+
return targetTag?.commit.id || null;
|
|
696
|
+
} catch {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* 获取指定 Branch 的最新 commit 信息
|
|
702
|
+
*/
|
|
703
|
+
async getBranchCommit(projectPath, branch) {
|
|
704
|
+
try {
|
|
705
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
706
|
+
const response = await this.request(
|
|
707
|
+
`/projects/${encodedPath}/repository/branches/${encodeURIComponent(branch)}`
|
|
708
|
+
);
|
|
709
|
+
if (!response.ok) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const branchInfo = await response.json();
|
|
713
|
+
return branchInfo.commit.id;
|
|
714
|
+
} catch {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* 对比两个版本之间的差异
|
|
720
|
+
*/
|
|
721
|
+
async compareVersions(projectPath, from, to) {
|
|
722
|
+
try {
|
|
723
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
724
|
+
const response = await this.request(
|
|
725
|
+
`/projects/${encodedPath}/repository/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`
|
|
726
|
+
);
|
|
727
|
+
if (!response.ok) {
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
return await response.json();
|
|
731
|
+
} catch {
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* 比较两个版本号(用于排序)
|
|
737
|
+
* 返回值: -1 (v1 < v2), 0 (v1 == v2), 1 (v1 > v2)
|
|
738
|
+
*/
|
|
739
|
+
compareVersionStrings(v1, v2) {
|
|
740
|
+
const version1 = v1.replace(/^v/, "");
|
|
741
|
+
const version2 = v2.replace(/^v/, "");
|
|
742
|
+
const parts1 = version1.split(".").map((p) => parseInt(p, 10) || 0);
|
|
743
|
+
const parts2 = version2.split(".").map((p) => parseInt(p, 10) || 0);
|
|
744
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
745
|
+
const p1 = parts1[i] || 0;
|
|
746
|
+
const p2 = parts2[i] || 0;
|
|
747
|
+
if (p1 > p2) return 1;
|
|
748
|
+
if (p1 < p2) return -1;
|
|
749
|
+
}
|
|
750
|
+
return 0;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* 获取最新的版本(优先 Tag,否则 latest commit)
|
|
754
|
+
*/
|
|
755
|
+
async getLatestVersion(projectPath) {
|
|
756
|
+
try {
|
|
757
|
+
const tags = await this.listTags(projectPath);
|
|
758
|
+
const branches = await this.listBranches(projectPath);
|
|
759
|
+
if (tags.length > 0) {
|
|
760
|
+
const latestTag = tags[0];
|
|
761
|
+
return {
|
|
762
|
+
type: "tag",
|
|
763
|
+
name: latestTag.name,
|
|
764
|
+
commit: latestTag.commit.id
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
const defaultBranch = branches.find((b) => b.default);
|
|
768
|
+
if (defaultBranch) {
|
|
769
|
+
return {
|
|
770
|
+
type: "commit",
|
|
771
|
+
name: defaultBranch.name,
|
|
772
|
+
commit: defaultBranch.commit.id
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
return null;
|
|
776
|
+
} catch {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* 解析项目路径
|
|
782
|
+
* 从 Git URL 中提取项目路径
|
|
783
|
+
*/
|
|
784
|
+
static parseProjectPath(repository) {
|
|
785
|
+
let path17 = repository;
|
|
786
|
+
path17 = path17.replace(/^https?:\/\//, "");
|
|
787
|
+
path17 = path17.replace(/^git@/, "");
|
|
788
|
+
const parts = path17.split("/");
|
|
789
|
+
if (parts.length > 1) {
|
|
790
|
+
path17 = parts.slice(1).join("/");
|
|
791
|
+
}
|
|
792
|
+
path17 = path17.replace(/\.git$/, "");
|
|
793
|
+
return path17;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* 编码项目路径用于 API 请求
|
|
797
|
+
*/
|
|
798
|
+
encodeProjectPath(projectPath) {
|
|
799
|
+
return encodeURIComponent(projectPath).replace(/%2F/g, "%2F");
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* 发送 HTTP 请求
|
|
803
|
+
*/
|
|
804
|
+
async request(endpoint, options = {}) {
|
|
805
|
+
const url = `${this.config.baseUrl}/api/v4${endpoint}`;
|
|
806
|
+
const headers = {
|
|
807
|
+
"PRIVATE-TOKEN": this.config.accessToken,
|
|
808
|
+
"Content-Type": "application/json",
|
|
809
|
+
...options.headers
|
|
810
|
+
};
|
|
811
|
+
const controller = new AbortController();
|
|
812
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
813
|
+
try {
|
|
814
|
+
const response = await fetch(url, {
|
|
815
|
+
...options,
|
|
816
|
+
headers,
|
|
817
|
+
signal: controller.signal
|
|
818
|
+
});
|
|
819
|
+
return response;
|
|
820
|
+
} catch (error) {
|
|
821
|
+
if (error.name === "AbortError") {
|
|
822
|
+
throw new Error("\u8BF7\u6C42\u8D85\u65F6");
|
|
823
|
+
}
|
|
824
|
+
throw error;
|
|
825
|
+
} finally {
|
|
826
|
+
clearTimeout(timeoutId);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* 获取默认分支
|
|
831
|
+
*/
|
|
832
|
+
async getDefaultBranch(projectPath) {
|
|
833
|
+
try {
|
|
834
|
+
const project = await this.getProject(projectPath);
|
|
835
|
+
return project?.default_branch || null;
|
|
836
|
+
} catch {
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
};
|
|
160
841
|
}
|
|
161
|
-
};
|
|
162
|
-
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
// src/index.ts
|
|
845
|
+
init_esm_shims();
|
|
846
|
+
init_logger();
|
|
847
|
+
import { Command as Command16 } from "commander";
|
|
848
|
+
import chalk4 from "chalk";
|
|
163
849
|
|
|
164
850
|
// src/commands/init.ts
|
|
851
|
+
init_esm_shims();
|
|
852
|
+
init_logger();
|
|
165
853
|
import { Command } from "commander";
|
|
166
854
|
import inquirer from "inquirer";
|
|
167
|
-
import
|
|
855
|
+
import path5 from "path";
|
|
168
856
|
import fs2 from "fs-extra";
|
|
169
857
|
|
|
170
858
|
// src/lib/claude.ts
|
|
859
|
+
init_esm_shims();
|
|
860
|
+
init_logger();
|
|
861
|
+
init_utils();
|
|
171
862
|
import { execa } from "execa";
|
|
172
863
|
var ClaudeAI = class {
|
|
173
864
|
verbose;
|
|
@@ -202,25 +893,45 @@ var ClaudeAI = class {
|
|
|
202
893
|
async prompt(promptText, options) {
|
|
203
894
|
const spinner = logger.startLoading("\u6B63\u5728\u8C03\u7528 Claude...");
|
|
204
895
|
try {
|
|
205
|
-
const args = [
|
|
896
|
+
const args = [];
|
|
897
|
+
const validContextFiles = [];
|
|
206
898
|
if (options?.contextFiles && options.contextFiles.length > 0) {
|
|
207
899
|
for (const file of options.contextFiles) {
|
|
208
|
-
|
|
900
|
+
const exists = await FileUtils.exists(file);
|
|
901
|
+
if (exists) {
|
|
902
|
+
validContextFiles.push(file);
|
|
903
|
+
} else {
|
|
904
|
+
logger.warn(`\u4E0A\u4E0B\u6587\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u5DF2\u8DF3\u8FC7: ${file}`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
for (const file of validContextFiles) {
|
|
908
|
+
args.push("--context", file);
|
|
209
909
|
}
|
|
210
910
|
}
|
|
211
|
-
|
|
911
|
+
args.push("--no-confirm", "-p", promptText);
|
|
912
|
+
const result = await execa("claude", args, {
|
|
212
913
|
stdio: this.verbose ? "inherit" : "pipe",
|
|
213
|
-
timeout: options?.timeout || 3e5
|
|
914
|
+
timeout: options?.timeout || 3e5,
|
|
214
915
|
// 默认 5 分钟
|
|
916
|
+
reject: false
|
|
917
|
+
// 不自动拒绝非零退出码
|
|
215
918
|
});
|
|
216
919
|
spinner.succeed("Claude \u54CD\u5E94\u5B8C\u6210");
|
|
217
|
-
|
|
920
|
+
if (result.exitCode !== 0 && !result.stdout) {
|
|
921
|
+
const stderr = result.stderr || "";
|
|
922
|
+
throw new Error(`Claude \u547D\u4EE4\u6267\u884C\u5931\u8D25 (\u9000\u51FA\u7801: ${result.exitCode})${stderr ? `
|
|
923
|
+
${stderr}` : ""}`);
|
|
924
|
+
}
|
|
925
|
+
return result.stdout || "";
|
|
218
926
|
} catch (error) {
|
|
219
927
|
spinner.fail("Claude \u8C03\u7528\u5931\u8D25");
|
|
220
928
|
if (error.killed && error.signal === "SIGTERM") {
|
|
221
929
|
throw new Error("Claude \u6267\u884C\u8D85\u65F6");
|
|
222
930
|
}
|
|
223
|
-
|
|
931
|
+
const stderr = error.stderr || "";
|
|
932
|
+
const exitCode = error.exitCode !== void 0 ? ` (\u9000\u51FA\u7801: ${error.exitCode})` : "";
|
|
933
|
+
throw new Error(`Claude \u8C03\u7528\u5931\u8D25: ${error.message}${exitCode}${stderr ? `
|
|
934
|
+
${stderr}` : ""}`);
|
|
224
935
|
}
|
|
225
936
|
}
|
|
226
937
|
/**
|
|
@@ -244,12 +955,17 @@ var ClaudeAI = class {
|
|
|
244
955
|
const role = msg.role === "system" ? "\u7CFB\u7EDF\u6307\u4EE4" : `${msg.role === "user" ? "\u7528\u6237" : "\u52A9\u624B"}`;
|
|
245
956
|
return `[${role}]: ${msg.content}`;
|
|
246
957
|
}).join("\n\n");
|
|
247
|
-
const
|
|
958
|
+
const args = ["--no-confirm", "-p", fullPrompt];
|
|
959
|
+
const result = await execa("claude", args, {
|
|
248
960
|
stdio: this.verbose ? "inherit" : "pipe",
|
|
249
|
-
timeout: options?.timeout || 3e5
|
|
961
|
+
timeout: options?.timeout || 3e5,
|
|
962
|
+
reject: false
|
|
250
963
|
});
|
|
251
964
|
spinner.succeed("Claude \u54CD\u5E94\u5B8C\u6210");
|
|
252
|
-
|
|
965
|
+
if (result.exitCode !== 0 && !result.stdout) {
|
|
966
|
+
throw new Error(`Claude \u547D\u4EE4\u6267\u884C\u5931\u8D25 (\u9000\u51FA\u7801: ${result.exitCode})`);
|
|
967
|
+
}
|
|
968
|
+
return result.stdout || "";
|
|
253
969
|
} catch (error) {
|
|
254
970
|
spinner.fail("Claude \u8C03\u7528\u5931\u8D25");
|
|
255
971
|
throw error;
|
|
@@ -460,234 +1176,135 @@ ${projectContext}
|
|
|
460
1176
|
};
|
|
461
1177
|
var claudeAI = new ClaudeAI();
|
|
462
1178
|
|
|
463
|
-
// src/
|
|
464
|
-
|
|
465
|
-
import
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* 写入文件内容
|
|
482
|
-
*/
|
|
483
|
-
static async write(file, content) {
|
|
484
|
-
await fs.writeFile(file, content, "utf-8");
|
|
485
|
-
}
|
|
486
|
-
/**
|
|
487
|
-
* 检查文件是否存在
|
|
488
|
-
*/
|
|
489
|
-
static async exists(file) {
|
|
490
|
-
return await fs.pathExists(file);
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* 复制文件
|
|
494
|
-
*/
|
|
495
|
-
static async copy(src, dest) {
|
|
496
|
-
await fs.copy(src, dest);
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* 删除文件或目录
|
|
500
|
-
*/
|
|
501
|
-
static async remove(file) {
|
|
502
|
-
await fs.remove(file);
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* 移动文件
|
|
506
|
-
*/
|
|
507
|
-
static async move(src, dest) {
|
|
508
|
-
await fs.move(src, dest);
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* 使用 glob 查找文件
|
|
512
|
-
*/
|
|
513
|
-
static async findFiles(pattern, cwd) {
|
|
514
|
-
return await glob(pattern, {
|
|
515
|
-
cwd,
|
|
516
|
-
absolute: false,
|
|
517
|
-
nodir: true
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* 读取 JSON 文件
|
|
522
|
-
*/
|
|
523
|
-
static async readJson(file) {
|
|
524
|
-
return await fs.readJson(file);
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* 写入 JSON 文件
|
|
528
|
-
*/
|
|
529
|
-
static async writeJson(file, data) {
|
|
530
|
-
await fs.writeJson(file, data, { spaces: 2 });
|
|
1179
|
+
// src/commands/init.ts
|
|
1180
|
+
init_utils();
|
|
1181
|
+
import { Listr } from "listr2";
|
|
1182
|
+
|
|
1183
|
+
// src/lib/template-version.ts
|
|
1184
|
+
init_esm_shims();
|
|
1185
|
+
init_utils();
|
|
1186
|
+
init_logger();
|
|
1187
|
+
import { execa as execa2 } from "execa";
|
|
1188
|
+
import path3 from "path";
|
|
1189
|
+
var DEFAULT_TEMPLATES = {
|
|
1190
|
+
frontend: {
|
|
1191
|
+
repository: "https://gitlab.yungu-inc.org/static/fe-tpl-ai"
|
|
1192
|
+
},
|
|
1193
|
+
backend: {
|
|
1194
|
+
repository: "git@gitlab.yungu-inc.org:yungu-app/java-scaffold-template.git"
|
|
531
1195
|
}
|
|
532
1196
|
};
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
* 转换为 PascalCase
|
|
542
|
-
*/
|
|
543
|
-
static toPascalCase(str) {
|
|
544
|
-
return str.replace(/[-_\s](\w)/g, (_, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toUpperCase());
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* 转换为 camelCase
|
|
548
|
-
*/
|
|
549
|
-
static toCamelCase(str) {
|
|
550
|
-
const pascal = this.toPascalCase(str);
|
|
551
|
-
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* 转换为 snake_case
|
|
555
|
-
*/
|
|
556
|
-
static toSnakeCase(str) {
|
|
557
|
-
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`).replace(/^_/, "").replace(/-+/g, "_").replace(/[\s]+/g, "_");
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* 首字母大写
|
|
561
|
-
*/
|
|
562
|
-
static capitalize(str) {
|
|
563
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1197
|
+
function getConfigPath(projectPath) {
|
|
1198
|
+
return path3.join(projectPath, ".team-cli", "template.json");
|
|
1199
|
+
}
|
|
1200
|
+
async function readTemplateConfig(projectPath) {
|
|
1201
|
+
const configPath = getConfigPath(projectPath);
|
|
1202
|
+
const exists = await FileUtils.exists(configPath);
|
|
1203
|
+
if (!exists) {
|
|
1204
|
+
return null;
|
|
564
1205
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
return str.slice(0, length - suffix.length) + suffix;
|
|
1206
|
+
try {
|
|
1207
|
+
const content = await FileUtils.read(configPath);
|
|
1208
|
+
return JSON.parse(content);
|
|
1209
|
+
} catch {
|
|
1210
|
+
return null;
|
|
571
1211
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
1212
|
+
}
|
|
1213
|
+
async function saveTemplateConfig(projectPath, config) {
|
|
1214
|
+
const configDir = path3.join(projectPath, ".team-cli");
|
|
1215
|
+
await FileUtils.ensureDir(configDir);
|
|
1216
|
+
const configPath = getConfigPath(projectPath);
|
|
1217
|
+
const content = JSON.stringify(config, null, 2);
|
|
1218
|
+
await FileUtils.write(configPath, content);
|
|
1219
|
+
}
|
|
1220
|
+
async function initTemplateConfig(projectPath) {
|
|
1221
|
+
const config = await readTemplateConfig(projectPath);
|
|
1222
|
+
if (config) {
|
|
1223
|
+
return;
|
|
577
1224
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
return format.replace("YYYY", String(year)).replace("MM", month).replace("DD", day).replace("HH", hours).replace("mm", minutes).replace("ss", seconds);
|
|
1225
|
+
await saveTemplateConfig(projectPath, DEFAULT_TEMPLATES);
|
|
1226
|
+
}
|
|
1227
|
+
async function getLatestCommit(repository) {
|
|
1228
|
+
try {
|
|
1229
|
+
const { stdout } = await execa2("git", ["ls-remote", repository, "HEAD"], {
|
|
1230
|
+
stdio: "pipe"
|
|
1231
|
+
});
|
|
1232
|
+
const commit = stdout.split(" ")[0];
|
|
1233
|
+
return commit;
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
logger.debug(`\u83B7\u53D6 ${repository} \u6700\u65B0 commit \u5931\u8D25: ${error}`);
|
|
1236
|
+
return "";
|
|
591
1237
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
1238
|
+
}
|
|
1239
|
+
async function getLatestTag(repository) {
|
|
1240
|
+
try {
|
|
1241
|
+
const { stdout } = await execa2(
|
|
1242
|
+
"git",
|
|
1243
|
+
["ls-remote", "--tags", repository],
|
|
1244
|
+
{
|
|
1245
|
+
stdio: "pipe"
|
|
1246
|
+
}
|
|
1247
|
+
);
|
|
1248
|
+
const tags = stdout.split("\n").filter((line) => line.includes("refs/tags/") && !line.includes("^{}")).map((line) => {
|
|
1249
|
+
const [, ref] = line.split(" ");
|
|
1250
|
+
return ref.replace("refs/tags/", "");
|
|
1251
|
+
}).filter((tag) => /^v?\d+\.\d+\.\d+/.test(tag)).sort((a, b) => {
|
|
1252
|
+
const va = a.replace(/^v/, "").split(".").map(Number);
|
|
1253
|
+
const vb = b.replace(/^v/, "").split(".").map(Number);
|
|
1254
|
+
for (let i = 0; i < 3; i++) {
|
|
1255
|
+
if ((va[i] || 0) > (vb[i] || 0)) return 1;
|
|
1256
|
+
if ((va[i] || 0) < (vb[i] || 0)) return -1;
|
|
1257
|
+
}
|
|
1258
|
+
return 0;
|
|
1259
|
+
});
|
|
1260
|
+
return tags[tags.length - 1] || "";
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
logger.debug(`\u83B7\u53D6 ${repository} \u6700\u65B0 tag \u5931\u8D25: ${error}`);
|
|
1263
|
+
return "";
|
|
613
1264
|
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
static async isGitRepo(cwd = process.cwd()) {
|
|
620
|
-
const gitDir = path.join(cwd, ".git");
|
|
621
|
-
return await FileUtils.exists(gitDir);
|
|
1265
|
+
}
|
|
1266
|
+
async function checkTemplateUpdate(projectPath, type) {
|
|
1267
|
+
const config = await readTemplateConfig(projectPath);
|
|
1268
|
+
if (!config || !config[type]) {
|
|
1269
|
+
return null;
|
|
622
1270
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
});
|
|
631
|
-
return stdout.trim();
|
|
1271
|
+
const repository = config[type].repository;
|
|
1272
|
+
const currentCommit = config[type].commit;
|
|
1273
|
+
const currentTag = config[type].tag;
|
|
1274
|
+
const latestCommit = await getLatestCommit(repository);
|
|
1275
|
+
const latestTag = await getLatestTag(repository);
|
|
1276
|
+
if (!latestCommit) {
|
|
1277
|
+
return null;
|
|
632
1278
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
1279
|
+
const needsUpdate = currentCommit && currentCommit !== latestCommit;
|
|
1280
|
+
return {
|
|
1281
|
+
type,
|
|
1282
|
+
repository,
|
|
1283
|
+
currentCommit,
|
|
1284
|
+
currentTag,
|
|
1285
|
+
latestCommit,
|
|
1286
|
+
latestTag,
|
|
1287
|
+
needsUpdate: needsUpdate || false
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
async function updateTemplateVersion(projectPath, type, commit, options) {
|
|
1291
|
+
const config = await readTemplateConfig(projectPath);
|
|
1292
|
+
if (!config) {
|
|
1293
|
+
return;
|
|
640
1294
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
* 解析 spec 文件
|
|
645
|
-
*/
|
|
646
|
-
static async parseSpec(file) {
|
|
647
|
-
const content = await FileUtils.read(file);
|
|
648
|
-
const spec = {};
|
|
649
|
-
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
650
|
-
if (titleMatch) {
|
|
651
|
-
spec.title = titleMatch[1];
|
|
652
|
-
}
|
|
653
|
-
const nameMatch = content.match(/\*\*功能名称\*\*:\s*(.+)$/m);
|
|
654
|
-
if (nameMatch) {
|
|
655
|
-
spec.name = nameMatch[1];
|
|
656
|
-
}
|
|
657
|
-
const priorityMatch = content.match(/\*\*优先级\*\*:\s*(P[0-2])/);
|
|
658
|
-
if (priorityMatch) {
|
|
659
|
-
spec.priority = priorityMatch[1];
|
|
660
|
-
}
|
|
661
|
-
const statusMatch = content.match(/\*\*状态\*\*:\s*(.+)$/m);
|
|
662
|
-
if (statusMatch) {
|
|
663
|
-
spec.status = statusMatch[1];
|
|
664
|
-
}
|
|
665
|
-
const depSection = content.match(/## 依赖关系\s+([\s\S]+?)##/m);
|
|
666
|
-
if (depSection) {
|
|
667
|
-
const depMatches = depSection[1].matchAll(/- \[([ x])\]\s*(.+)$/gm);
|
|
668
|
-
spec.dependencies = Array.from(depMatches).map((m) => ({
|
|
669
|
-
completed: m[1] === "x",
|
|
670
|
-
name: m[2]
|
|
671
|
-
}));
|
|
672
|
-
}
|
|
673
|
-
return spec;
|
|
1295
|
+
config[type].commit = commit;
|
|
1296
|
+
if (options?.tag) {
|
|
1297
|
+
config[type].tag = options.tag;
|
|
674
1298
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
*/
|
|
678
|
-
static async getSpecStatus(file) {
|
|
679
|
-
try {
|
|
680
|
-
const spec = await this.parseSpec(file);
|
|
681
|
-
return spec.status || "\u672A\u77E5";
|
|
682
|
-
} catch {
|
|
683
|
-
return "\u9519\u8BEF";
|
|
684
|
-
}
|
|
1299
|
+
if (options?.branch) {
|
|
1300
|
+
config[type].branch = options.branch;
|
|
685
1301
|
}
|
|
686
|
-
|
|
1302
|
+
config[type].lastUpdate = (/* @__PURE__ */ new Date()).toISOString();
|
|
1303
|
+
await saveTemplateConfig(projectPath, config);
|
|
1304
|
+
}
|
|
687
1305
|
|
|
688
1306
|
// src/commands/init.ts
|
|
689
|
-
|
|
690
|
-
var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u540D\u79F0").description("\u521D\u59CB\u5316\u65B0\u9879\u76EE").option("-d, --dir <directory>", "\u9879\u76EE\u76EE\u5F55", ".").option("--no-docker", "\u4E0D\u751F\u6210 Docker \u914D\u7F6E").option("--no-git", "\u4E0D\u521D\u59CB\u5316 Git").action(async (projectName, options) => {
|
|
1307
|
+
var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u540D\u79F0").description("\u521D\u59CB\u5316\u65B0\u9879\u76EE").option("-d, --dir <directory>", "\u9879\u76EE\u76EE\u5F55", ".").option("--no-docker", "\u4E0D\u751F\u6210 Docker \u914D\u7F6E").option("--no-git", "\u4E0D\u521D\u59CB\u5316 Git").option("--tag <tag>", "\u6307\u5B9A\u6A21\u677F\u6807\u7B7E (\u524D\u540E\u7AEF\u901A\u7528)").option("--backend-tag <tag>", "\u6307\u5B9A\u540E\u7AEF\u6A21\u677F\u6807\u7B7E").option("--frontend-tag <tag>", "\u6307\u5B9A\u524D\u7AEF\u6A21\u677F\u6807\u7B7E").option("--backend-branch <branch>", "\u6307\u5B9A\u540E\u7AEF\u6A21\u677F\u5206\u652F").option("--frontend-branch <branch>", "\u6307\u5B9A\u524D\u7AEF\u6A21\u677F\u5206\u652F").action(async (projectName, options) => {
|
|
691
1308
|
try {
|
|
692
1309
|
if (!projectName) {
|
|
693
1310
|
const answers = await inquirer.prompt([
|
|
@@ -718,7 +1335,7 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
|
|
|
718
1335
|
logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
|
|
719
1336
|
process.exit(1);
|
|
720
1337
|
}
|
|
721
|
-
const projectPath =
|
|
1338
|
+
const projectPath = path5.resolve(options.dir, projectName);
|
|
722
1339
|
if (await FileUtils.exists(projectPath)) {
|
|
723
1340
|
logger.error(`\u76EE\u5F55\u5DF2\u5B58\u5728: ${projectPath}`);
|
|
724
1341
|
logger.info("\u8BF7\u9009\u62E9\u5176\u4ED6\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55");
|
|
@@ -737,11 +1354,11 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
|
|
|
737
1354
|
title: "\u521B\u5EFA\u9879\u76EE\u76EE\u5F55",
|
|
738
1355
|
task: async () => {
|
|
739
1356
|
await FileUtils.ensureDir(projectPath);
|
|
740
|
-
await FileUtils.ensureDir(
|
|
741
|
-
await FileUtils.ensureDir(
|
|
742
|
-
await FileUtils.ensureDir(
|
|
743
|
-
await FileUtils.ensureDir(
|
|
744
|
-
await FileUtils.ensureDir(
|
|
1357
|
+
await FileUtils.ensureDir(path5.join(projectPath, "frontend"));
|
|
1358
|
+
await FileUtils.ensureDir(path5.join(projectPath, "backend"));
|
|
1359
|
+
await FileUtils.ensureDir(path5.join(projectPath, "docs/specs"));
|
|
1360
|
+
await FileUtils.ensureDir(path5.join(projectPath, "docs/api"));
|
|
1361
|
+
await FileUtils.ensureDir(path5.join(projectPath, "docs/sessions"));
|
|
745
1362
|
}
|
|
746
1363
|
},
|
|
747
1364
|
{
|
|
@@ -771,7 +1388,12 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
|
|
|
771
1388
|
{
|
|
772
1389
|
title: "\u514B\u9686\u540E\u7AEF\u6A21\u677F",
|
|
773
1390
|
task: async () => {
|
|
774
|
-
|
|
1391
|
+
const backendTag = options.backendTag || options.tag;
|
|
1392
|
+
const backendBranch = options.backendBranch;
|
|
1393
|
+
await cloneBackendTemplate(projectPath, {
|
|
1394
|
+
tag: backendTag,
|
|
1395
|
+
branch: backendBranch
|
|
1396
|
+
});
|
|
775
1397
|
}
|
|
776
1398
|
},
|
|
777
1399
|
{
|
|
@@ -916,7 +1538,7 @@ docs/
|
|
|
916
1538
|
\u2514\u2500\u2500 sessions/ # \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
|
|
917
1539
|
\`\`\`
|
|
918
1540
|
`;
|
|
919
|
-
await FileUtils.write(
|
|
1541
|
+
await FileUtils.write(path5.join(projectPath, "TECH_STACK.md"), content);
|
|
920
1542
|
}
|
|
921
1543
|
async function generateConventions(projectPath) {
|
|
922
1544
|
const content = `# \u5F00\u53D1\u89C4\u8303
|
|
@@ -1219,7 +1841,7 @@ test('renders user name', () => {
|
|
|
1219
1841
|
});
|
|
1220
1842
|
\`\`\`
|
|
1221
1843
|
`;
|
|
1222
|
-
await FileUtils.write(
|
|
1844
|
+
await FileUtils.write(path5.join(projectPath, "CONVENTIONS.md"), content);
|
|
1223
1845
|
}
|
|
1224
1846
|
async function generateAIMemory(projectPath, projectName) {
|
|
1225
1847
|
const content = `# AI Memory - \u9879\u76EE\u72B6\u6001\u8BB0\u5F55
|
|
@@ -1230,6 +1852,15 @@ async function generateAIMemory(projectPath, projectName) {
|
|
|
1230
1852
|
- **\u5F53\u524D\u9636\u6BB5**: \u521D\u59CB\u5316
|
|
1231
1853
|
- **\u6700\u540E\u66F4\u65B0**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
1232
1854
|
|
|
1855
|
+
## \u6A21\u677F\u7248\u672C\u4FE1\u606F
|
|
1856
|
+
|
|
1857
|
+
> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u7BA1\u7406\uFF0C\u8BB0\u5F55\u4F7F\u7528\u7684\u524D\u540E\u7AEF\u6A21\u677F\u7248\u672C
|
|
1858
|
+
|
|
1859
|
+
| \u7C7B\u578B | \u4ED3\u5E93 | Tag | Branch | Commit | \u66F4\u65B0\u65F6\u95F4 |
|
|
1860
|
+
|------|------|-----|--------|--------|----------|
|
|
1861
|
+
| \u524D\u7AEF | - | - | - | - | - |
|
|
1862
|
+
| \u540E\u7AEF | - | - | - | - | - |
|
|
1863
|
+
|
|
1233
1864
|
## \u529F\u80FD\u6E05\u5355 (Feature Inventory)
|
|
1234
1865
|
|
|
1235
1866
|
| \u529F\u80FD | Spec \u6587\u4EF6 | \u72B6\u6001 | \u8FDB\u5EA6 | \u5B8C\u6210\u65E5\u671F | \u5907\u6CE8 |
|
|
@@ -1269,7 +1900,7 @@ async function generateAIMemory(projectPath, projectName) {
|
|
|
1269
1900
|
| Bug ID | \u65E5\u671F | \u95EE\u9898\u63CF\u8FF0 | \u72B6\u6001 |
|
|
1270
1901
|
|--------|------|---------|------|
|
|
1271
1902
|
`;
|
|
1272
|
-
await FileUtils.write(
|
|
1903
|
+
await FileUtils.write(path5.join(projectPath, "AI_MEMORY.md"), content);
|
|
1273
1904
|
}
|
|
1274
1905
|
async function generateSpecTemplate(projectPath) {
|
|
1275
1906
|
const content = `# [\u529F\u80FD\u6807\u9898]
|
|
@@ -1333,35 +1964,78 @@ async function generateSpecTemplate(projectPath) {
|
|
|
1333
1964
|
----
|
|
1334
1965
|
*\u751F\u6210\u4E8E: {{TIMESTAMP}} by team-cli*
|
|
1335
1966
|
`;
|
|
1336
|
-
await FileUtils.write(
|
|
1967
|
+
await FileUtils.write(path5.join(projectPath, "docs/specs/template.md"), content);
|
|
1337
1968
|
}
|
|
1338
|
-
async function cloneBackendTemplate(projectPath) {
|
|
1969
|
+
async function cloneBackendTemplate(projectPath, versionOptions) {
|
|
1339
1970
|
const templateRepo = process.env.TEMPLATE_REPO || "git@gitlab.yungu-inc.org:yungu-app/java-scaffold-template.git";
|
|
1340
|
-
const backendPath =
|
|
1971
|
+
const backendPath = path5.join(projectPath, "backend");
|
|
1341
1972
|
try {
|
|
1342
1973
|
const { execaCommand } = await import("execa");
|
|
1343
|
-
const tempDir =
|
|
1344
|
-
|
|
1974
|
+
const tempDir = path5.join(projectPath, ".template-temp");
|
|
1975
|
+
if (versionOptions?.tag || versionOptions?.branch) {
|
|
1976
|
+
const { userConfigManager: userConfigManager2 } = await Promise.resolve().then(() => (init_user_config(), user_config_exports));
|
|
1977
|
+
const { GitLabAPI: GitLabAPI2 } = await Promise.resolve().then(() => (init_gitlab_api(), gitlab_api_exports));
|
|
1978
|
+
const config = await userConfigManager2.getGitLabConfig();
|
|
1979
|
+
if (config) {
|
|
1980
|
+
const gitlabAPI = new GitLabAPI2(config);
|
|
1981
|
+
const projectPathEncoded = GitLabAPI2.parseProjectPath(templateRepo);
|
|
1982
|
+
if (versionOptions.tag) {
|
|
1983
|
+
const isValid = await gitlabAPI.validateTag(projectPathEncoded, versionOptions.tag);
|
|
1984
|
+
if (!isValid) {
|
|
1985
|
+
logger.error(`\u540E\u7AEF\u6A21\u677F tag "${versionOptions.tag}" \u4E0D\u5B58\u5728`);
|
|
1986
|
+
process.exit(1);
|
|
1987
|
+
}
|
|
1988
|
+
logger.info(`\u4F7F\u7528\u540E\u7AEF\u6A21\u677F tag: ${versionOptions.tag}`);
|
|
1989
|
+
}
|
|
1990
|
+
if (versionOptions.branch) {
|
|
1991
|
+
const isValid = await gitlabAPI.validateBranch(projectPathEncoded, versionOptions.branch);
|
|
1992
|
+
if (!isValid) {
|
|
1993
|
+
logger.error(`\u540E\u7AEF\u6A21\u677F\u5206\u652F "${versionOptions.branch}" \u4E0D\u5B58\u5728`);
|
|
1994
|
+
process.exit(1);
|
|
1995
|
+
}
|
|
1996
|
+
logger.info(`\u4F7F\u7528\u540E\u7AEF\u6A21\u677F\u5206\u652F: ${versionOptions.branch}`);
|
|
1997
|
+
}
|
|
1998
|
+
} else {
|
|
1999
|
+
logger.warn("\u672A\u914D\u7F6E GitLab Token\uFF0C\u8DF3\u8FC7\u7248\u672C\u9A8C\u8BC1");
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
const ref = versionOptions?.tag || versionOptions?.branch || "HEAD";
|
|
2003
|
+
await execaCommand(`git clone --depth=1 --branch ${ref} ${templateRepo} ${tempDir}`, {
|
|
1345
2004
|
stdio: "inherit",
|
|
1346
2005
|
timeout: 6e4
|
|
1347
2006
|
});
|
|
2007
|
+
const { execa: e } = await import("execa");
|
|
2008
|
+
const { stdout: commit } = await e("git", ["rev-parse", "HEAD"], {
|
|
2009
|
+
cwd: tempDir,
|
|
2010
|
+
stdio: "pipe"
|
|
2011
|
+
});
|
|
2012
|
+
const { stdout: tags } = await e("git", ["tag", "-l", "--sort=-v:refname"], {
|
|
2013
|
+
cwd: tempDir,
|
|
2014
|
+
stdio: "pipe"
|
|
2015
|
+
});
|
|
2016
|
+
const latestTag = tags.split("\n")[0] || void 0;
|
|
1348
2017
|
await fs2.copy(tempDir, backendPath, {
|
|
1349
2018
|
filter: (src) => !src.includes(".git")
|
|
1350
2019
|
});
|
|
1351
2020
|
await fs2.remove(tempDir);
|
|
1352
|
-
const gitDir =
|
|
2021
|
+
const gitDir = path5.join(backendPath, ".git");
|
|
1353
2022
|
if (await FileUtils.exists(gitDir)) {
|
|
1354
2023
|
await FileUtils.remove(gitDir);
|
|
1355
2024
|
}
|
|
2025
|
+
await initTemplateConfig(projectPath);
|
|
2026
|
+
await updateTemplateVersion(projectPath, "backend", commit.trim(), {
|
|
2027
|
+
tag: versionOptions?.tag || latestTag,
|
|
2028
|
+
branch: versionOptions?.branch
|
|
2029
|
+
});
|
|
1356
2030
|
} catch (error) {
|
|
1357
2031
|
logger.warn("\u514B\u9686\u540E\u7AEF\u6A21\u677F\u5931\u8D25\uFF0C\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
|
|
1358
|
-
await FileUtils.ensureDir(
|
|
1359
|
-
await FileUtils.ensureDir(
|
|
1360
|
-
await FileUtils.ensureDir(
|
|
2032
|
+
await FileUtils.ensureDir(path5.join(backendPath, "src/main/java/com/example"));
|
|
2033
|
+
await FileUtils.ensureDir(path5.join(backendPath, "src/main/resources"));
|
|
2034
|
+
await FileUtils.ensureDir(path5.join(backendPath, "src/test/java"));
|
|
1361
2035
|
}
|
|
1362
2036
|
}
|
|
1363
2037
|
async function generateFrontendScaffold(projectPath) {
|
|
1364
|
-
const frontendPath =
|
|
2038
|
+
const frontendPath = path5.join(projectPath, "frontend");
|
|
1365
2039
|
try {
|
|
1366
2040
|
const prompt = `Read TECH_STACK.md and CONVENTIONS.md.
|
|
1367
2041
|
Initialize a Next.js 14 frontend in ./frontend with:
|
|
@@ -1374,16 +2048,16 @@ Initialize a Next.js 14 frontend in ./frontend with:
|
|
|
1374
2048
|
Do not run any servers, just generate the folder structure and configuration files.`;
|
|
1375
2049
|
await claudeAI.prompt(prompt, {
|
|
1376
2050
|
contextFiles: [
|
|
1377
|
-
|
|
1378
|
-
|
|
2051
|
+
path5.join(projectPath, "TECH_STACK.md"),
|
|
2052
|
+
path5.join(projectPath, "CONVENTIONS.md")
|
|
1379
2053
|
]
|
|
1380
2054
|
});
|
|
1381
2055
|
} catch (error) {
|
|
1382
2056
|
logger.warn("Claude \u751F\u6210\u524D\u7AEF\u5931\u8D25\uFF0C\u5C06\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
|
|
1383
|
-
await FileUtils.ensureDir(
|
|
1384
|
-
await FileUtils.ensureDir(
|
|
1385
|
-
await FileUtils.ensureDir(
|
|
1386
|
-
await FileUtils.ensureDir(
|
|
2057
|
+
await FileUtils.ensureDir(path5.join(frontendPath, "src/app"));
|
|
2058
|
+
await FileUtils.ensureDir(path5.join(frontendPath, "src/components"));
|
|
2059
|
+
await FileUtils.ensureDir(path5.join(frontendPath, "src/lib"));
|
|
2060
|
+
await FileUtils.ensureDir(path5.join(frontendPath, "public"));
|
|
1387
2061
|
}
|
|
1388
2062
|
}
|
|
1389
2063
|
async function generateDockerFiles(projectPath) {
|
|
@@ -1405,9 +2079,12 @@ async function initGit(projectPath, projectName) {
|
|
|
1405
2079
|
}
|
|
1406
2080
|
|
|
1407
2081
|
// src/commands/breakdown.ts
|
|
2082
|
+
init_esm_shims();
|
|
2083
|
+
init_utils();
|
|
2084
|
+
init_logger();
|
|
1408
2085
|
import { Command as Command2 } from "commander";
|
|
1409
2086
|
import inquirer2 from "inquirer";
|
|
1410
|
-
import
|
|
2087
|
+
import path6 from "path";
|
|
1411
2088
|
import { Listr as Listr2 } from "listr2";
|
|
1412
2089
|
var breakdownCommand = new Command2("breakdown").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos").action(async (specFile) => {
|
|
1413
2090
|
try {
|
|
@@ -1453,9 +2130,9 @@ var breakdownCommand = new Command2("breakdown").argument("[spec-file]", "Spec \
|
|
|
1453
2130
|
choices: ctx.specs
|
|
1454
2131
|
}
|
|
1455
2132
|
]);
|
|
1456
|
-
return
|
|
2133
|
+
return path6.join("docs/specs", selectedFile);
|
|
1457
2134
|
}
|
|
1458
|
-
const fullPath = specFile.startsWith("docs/specs/") ? specFile :
|
|
2135
|
+
const fullPath = specFile.startsWith("docs/specs/") ? specFile : path6.join("docs/specs", specFile);
|
|
1459
2136
|
const exists = await FileUtils.exists(fullPath);
|
|
1460
2137
|
if (!exists) {
|
|
1461
2138
|
throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${specFile}`);
|
|
@@ -1582,9 +2259,12 @@ Important Instructions:
|
|
|
1582
2259
|
}
|
|
1583
2260
|
|
|
1584
2261
|
// src/commands/dev.ts
|
|
2262
|
+
init_esm_shims();
|
|
2263
|
+
init_utils();
|
|
2264
|
+
init_logger();
|
|
1585
2265
|
import { Command as Command3 } from "commander";
|
|
1586
2266
|
import inquirer3 from "inquirer";
|
|
1587
|
-
import
|
|
2267
|
+
import path7 from "path";
|
|
1588
2268
|
var devCommand = new Command3("dev").description("\u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1").action(async () => {
|
|
1589
2269
|
try {
|
|
1590
2270
|
logger.header("\u5F00\u53D1\u6A21\u5F0F");
|
|
@@ -1629,7 +2309,7 @@ async function selectSpec() {
|
|
|
1629
2309
|
}
|
|
1630
2310
|
const specs = [];
|
|
1631
2311
|
for (let i = 0; i < specFiles.length; i++) {
|
|
1632
|
-
const file =
|
|
2312
|
+
const file = path7.join(specDir, specFiles[i]);
|
|
1633
2313
|
const spec = await FileUtils.read(file);
|
|
1634
2314
|
const status = parseSpecStatus(spec);
|
|
1635
2315
|
const dependencies = parseDependencies(spec);
|
|
@@ -1943,8 +2623,8 @@ async function generateSessionLog(specFile, milestone, todo, taskDescription, re
|
|
|
1943
2623
|
const sessionDir = "docs/sessions";
|
|
1944
2624
|
await FileUtils.ensureDir(sessionDir);
|
|
1945
2625
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1946
|
-
const specName =
|
|
1947
|
-
const logFile =
|
|
2626
|
+
const specName = path7.basename(specFile, ".md");
|
|
2627
|
+
const logFile = path7.join(sessionDir, `${timestamp}_${specName}.md`);
|
|
1948
2628
|
const content = `# \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
|
|
1949
2629
|
|
|
1950
2630
|
**\u65F6\u95F4**: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}
|
|
@@ -1976,9 +2656,12 @@ ${result}
|
|
|
1976
2656
|
}
|
|
1977
2657
|
|
|
1978
2658
|
// src/commands/add-feature.ts
|
|
2659
|
+
init_esm_shims();
|
|
2660
|
+
init_utils();
|
|
2661
|
+
init_logger();
|
|
1979
2662
|
import { Command as Command4 } from "commander";
|
|
1980
2663
|
import inquirer4 from "inquirer";
|
|
1981
|
-
import
|
|
2664
|
+
import path8 from "path";
|
|
1982
2665
|
import { Listr as Listr3 } from "listr2";
|
|
1983
2666
|
var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "\u529F\u80FD\u540D\u79F0").description("\u6DFB\u52A0\u65B0\u529F\u80FD\uFF08\u652F\u6301 PRD \u6216\u7B80\u5355\u63CF\u8FF0\u6A21\u5F0F\uFF09").action(async (featureName) => {
|
|
1984
2667
|
try {
|
|
@@ -1997,7 +2680,7 @@ var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "
|
|
|
1997
2680
|
process.exit(1);
|
|
1998
2681
|
}
|
|
1999
2682
|
const featureSlug = StringUtils2.toKebabCase(featureName);
|
|
2000
|
-
const specFile =
|
|
2683
|
+
const specFile = path8.join("docs/specs", `${featureSlug}.md`);
|
|
2001
2684
|
const specExists = await FileUtils.exists(specFile);
|
|
2002
2685
|
if (specExists) {
|
|
2003
2686
|
logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
|
|
@@ -2056,7 +2739,7 @@ async function addFeatureFromPrd(featureName, featureSlug, specFile) {
|
|
|
2056
2739
|
const specs = files.filter((f) => !f.includes("template"));
|
|
2057
2740
|
ctx2.completedSpecs = [];
|
|
2058
2741
|
for (const file of specs) {
|
|
2059
|
-
const status = await SpecUtils.getSpecStatus(
|
|
2742
|
+
const status = await SpecUtils.getSpecStatus(path8.join(specDir, file));
|
|
2060
2743
|
if (status === "\u5DF2\u5B8C\u6210") {
|
|
2061
2744
|
ctx2.completedSpecs.push(file.replace(".md", ""));
|
|
2062
2745
|
}
|
|
@@ -2128,7 +2811,7 @@ async function addFeatureSimple(featureName, featureSlug, specFile) {
|
|
|
2128
2811
|
const specs = files.filter((f) => !f.includes("template"));
|
|
2129
2812
|
ctx2.completedSpecs = [];
|
|
2130
2813
|
for (const file of specs) {
|
|
2131
|
-
const status = await SpecUtils.getSpecStatus(
|
|
2814
|
+
const status = await SpecUtils.getSpecStatus(path8.join(specDir, file));
|
|
2132
2815
|
if (status === "\u5DF2\u5B8C\u6210") {
|
|
2133
2816
|
ctx2.completedSpecs.push(file.replace(".md", ""));
|
|
2134
2817
|
}
|
|
@@ -2225,7 +2908,7 @@ async function buildProjectContext() {
|
|
|
2225
2908
|
const files = await FileUtils.findFiles("*.md", "docs/specs");
|
|
2226
2909
|
const specs = files.filter((f) => !f.includes("template"));
|
|
2227
2910
|
for (const file of specs) {
|
|
2228
|
-
const status = await SpecUtils.getSpecStatus(
|
|
2911
|
+
const status = await SpecUtils.getSpecStatus(path8.join("docs/specs", file));
|
|
2229
2912
|
context.push(` - ${file.replace(".md", "")} [${status}]`);
|
|
2230
2913
|
}
|
|
2231
2914
|
}
|
|
@@ -2465,8 +3148,11 @@ async function askToAdjust(specFile) {
|
|
|
2465
3148
|
}
|
|
2466
3149
|
|
|
2467
3150
|
// src/commands/split-prd.ts
|
|
3151
|
+
init_esm_shims();
|
|
3152
|
+
init_utils();
|
|
3153
|
+
init_logger();
|
|
2468
3154
|
import { Command as Command5 } from "commander";
|
|
2469
|
-
import
|
|
3155
|
+
import path9 from "path";
|
|
2470
3156
|
import { Listr as Listr4 } from "listr2";
|
|
2471
3157
|
var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u6587\u6863\u76EE\u5F55").description("\u5C06 PRD \u62C6\u5206\u6210\u591A\u4E2A specs").action(async (prdFolder) => {
|
|
2472
3158
|
try {
|
|
@@ -2496,7 +3182,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
|
|
|
2496
3182
|
ctx2.prdFiles = [];
|
|
2497
3183
|
for (const ext of supportedExtensions) {
|
|
2498
3184
|
const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
|
|
2499
|
-
ctx2.prdFiles.push(...files.map((f) =>
|
|
3185
|
+
ctx2.prdFiles.push(...files.map((f) => path9.join(prdFolder, f)));
|
|
2500
3186
|
}
|
|
2501
3187
|
if (ctx2.prdFiles.length === 0) {
|
|
2502
3188
|
throw new Error(
|
|
@@ -2514,7 +3200,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
|
|
|
2514
3200
|
{
|
|
2515
3201
|
title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
|
|
2516
3202
|
task: async (ctx2) => {
|
|
2517
|
-
const screenshotDir =
|
|
3203
|
+
const screenshotDir = path9.join(prdFolder, "screenshots");
|
|
2518
3204
|
const dirExists = await FileUtils.exists(screenshotDir);
|
|
2519
3205
|
if (!dirExists) {
|
|
2520
3206
|
logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
|
|
@@ -2525,7 +3211,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
|
|
|
2525
3211
|
ctx2.screenshots = [];
|
|
2526
3212
|
for (const ext of imageExtensions) {
|
|
2527
3213
|
const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
|
|
2528
|
-
ctx2.screenshots.push(...files.map((f) =>
|
|
3214
|
+
ctx2.screenshots.push(...files.map((f) => path9.join(screenshotDir, f)));
|
|
2529
3215
|
}
|
|
2530
3216
|
logger.success(`\u627E\u5230 ${ctx2.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
|
|
2531
3217
|
}
|
|
@@ -2536,8 +3222,8 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
|
|
|
2536
3222
|
const entries = await FileUtils.findFiles("*/", prdFolder);
|
|
2537
3223
|
ctx2.demoRepos = [];
|
|
2538
3224
|
for (const entry of entries) {
|
|
2539
|
-
const dirPath =
|
|
2540
|
-
const gitDir =
|
|
3225
|
+
const dirPath = path9.join(prdFolder, entry);
|
|
3226
|
+
const gitDir = path9.join(dirPath, ".git");
|
|
2541
3227
|
const hasGit = await FileUtils.exists(gitDir);
|
|
2542
3228
|
if (hasGit) {
|
|
2543
3229
|
ctx2.demoRepos.push(dirPath);
|
|
@@ -2705,9 +3391,12 @@ IMPORTANT:
|
|
|
2705
3391
|
}
|
|
2706
3392
|
|
|
2707
3393
|
// src/commands/bugfix.ts
|
|
3394
|
+
init_esm_shims();
|
|
3395
|
+
init_utils();
|
|
3396
|
+
init_logger();
|
|
2708
3397
|
import { Command as Command6 } from "commander";
|
|
2709
3398
|
import inquirer5 from "inquirer";
|
|
2710
|
-
import
|
|
3399
|
+
import path10 from "path";
|
|
2711
3400
|
import { Listr as Listr5 } from "listr2";
|
|
2712
3401
|
var bugfixCommand = new Command6("bugfix").description("\u521B\u5EFA Bugfix \u8BB0\u5F55").action(async () => {
|
|
2713
3402
|
try {
|
|
@@ -2756,7 +3445,7 @@ var bugfixCommand = new Command6("bugfix").description("\u521B\u5EFA Bugfix \u8B
|
|
|
2756
3445
|
const relatedSpec = await findRelatedSpec(answers.description);
|
|
2757
3446
|
const bugfixDir = "docs/bugfixes";
|
|
2758
3447
|
await FileUtils.ensureDir(bugfixDir);
|
|
2759
|
-
const bugfixFile =
|
|
3448
|
+
const bugfixFile = path10.join(bugfixDir, `${timestamp}_${bugId}.md`);
|
|
2760
3449
|
const content = formatBugfixDocument({
|
|
2761
3450
|
id: bugId,
|
|
2762
3451
|
severity: answers.severity,
|
|
@@ -2822,8 +3511,8 @@ var hotfixCommand = new Command6("hotfix").description("\u7D27\u6025\u4FEE\u590D
|
|
|
2822
3511
|
{
|
|
2823
3512
|
title: "\u521B\u5EFA hotfix \u5206\u652F",
|
|
2824
3513
|
task: async () => {
|
|
2825
|
-
const { execa:
|
|
2826
|
-
await
|
|
3514
|
+
const { execa: execa5 } = await import("execa");
|
|
3515
|
+
await execa5("git", ["checkout", "-b", branchName], { stdio: "inherit" });
|
|
2827
3516
|
}
|
|
2828
3517
|
},
|
|
2829
3518
|
{
|
|
@@ -2832,7 +3521,7 @@ var hotfixCommand = new Command6("hotfix").description("\u7D27\u6025\u4FEE\u590D
|
|
|
2832
3521
|
const timestamp = DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss");
|
|
2833
3522
|
const hotfixDir = "docs/hotfixes";
|
|
2834
3523
|
await FileUtils.ensureDir(hotfixDir);
|
|
2835
|
-
const hotfixFile =
|
|
3524
|
+
const hotfixFile = path10.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
|
|
2836
3525
|
const content = formatHotfixDocument({
|
|
2837
3526
|
id: hotfixId,
|
|
2838
3527
|
description: answers.description,
|
|
@@ -2847,9 +3536,9 @@ var hotfixCommand = new Command6("hotfix").description("\u7D27\u6025\u4FEE\u590D
|
|
|
2847
3536
|
{
|
|
2848
3537
|
title: "\u521B\u5EFA\u521D\u59CB commit",
|
|
2849
3538
|
task: async () => {
|
|
2850
|
-
const { execa:
|
|
2851
|
-
await
|
|
2852
|
-
await
|
|
3539
|
+
const { execa: execa5 } = await import("execa");
|
|
3540
|
+
await execa5("git", ["add", "docs/hotfixes"], { stdio: "pipe" });
|
|
3541
|
+
await execa5(
|
|
2853
3542
|
"git",
|
|
2854
3543
|
[
|
|
2855
3544
|
"commit",
|
|
@@ -2907,7 +3596,7 @@ async function findRelatedSpec(description) {
|
|
|
2907
3596
|
}
|
|
2908
3597
|
const keywords = extractKeywords(description);
|
|
2909
3598
|
for (const file of specs) {
|
|
2910
|
-
const filePath =
|
|
3599
|
+
const filePath = path10.join(specDir, file);
|
|
2911
3600
|
const content = await FileUtils.read(filePath);
|
|
2912
3601
|
for (const keyword of keywords) {
|
|
2913
3602
|
if (content.toLowerCase().includes(keyword.toLowerCase())) {
|
|
@@ -2995,8 +3684,11 @@ git checkout ${data.branch}
|
|
|
2995
3684
|
}
|
|
2996
3685
|
|
|
2997
3686
|
// src/commands/lint.ts
|
|
3687
|
+
init_esm_shims();
|
|
3688
|
+
init_utils();
|
|
3689
|
+
init_logger();
|
|
2998
3690
|
import { Command as Command7 } from "commander";
|
|
2999
|
-
import { execa as
|
|
3691
|
+
import { execa as execa3 } from "execa";
|
|
3000
3692
|
var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D\u95EE\u9898").description("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5 (\u524D\u7AEF + \u540E\u7AEF)").action(async (options) => {
|
|
3001
3693
|
try {
|
|
3002
3694
|
logger.header("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5");
|
|
@@ -3017,17 +3709,17 @@ var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D
|
|
|
3017
3709
|
try {
|
|
3018
3710
|
logger.step("\u68C0\u67E5\u524D\u7AEF\u4EE3\u7801...");
|
|
3019
3711
|
if (options.fix) {
|
|
3020
|
-
await
|
|
3712
|
+
await execa3("npm", ["run", "lint", "--", "--fix"], {
|
|
3021
3713
|
cwd: "frontend",
|
|
3022
3714
|
stdio: "inherit"
|
|
3023
3715
|
});
|
|
3024
3716
|
} else {
|
|
3025
|
-
await
|
|
3717
|
+
await execa3("npm", ["run", "lint"], {
|
|
3026
3718
|
cwd: "frontend",
|
|
3027
3719
|
stdio: "inherit"
|
|
3028
3720
|
});
|
|
3029
3721
|
}
|
|
3030
|
-
await
|
|
3722
|
+
await execa3("npx", ["tsc", "--noEmit"], {
|
|
3031
3723
|
cwd: "frontend",
|
|
3032
3724
|
stdio: "pipe"
|
|
3033
3725
|
});
|
|
@@ -3050,23 +3742,23 @@ var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D
|
|
|
3050
3742
|
logger.step("\u68C0\u67E5\u540E\u7AEF\u4EE3\u7801...");
|
|
3051
3743
|
if (hasGradle) {
|
|
3052
3744
|
if (options.fix) {
|
|
3053
|
-
await
|
|
3745
|
+
await execa3("./gradlew", ["spotlessApply"], {
|
|
3054
3746
|
cwd: "backend",
|
|
3055
3747
|
stdio: "inherit"
|
|
3056
3748
|
});
|
|
3057
3749
|
}
|
|
3058
|
-
await
|
|
3750
|
+
await execa3("./gradlew", ["checkstyleMain", "compileJava"], {
|
|
3059
3751
|
cwd: "backend",
|
|
3060
3752
|
stdio: "inherit"
|
|
3061
3753
|
});
|
|
3062
3754
|
} else if (hasMaven) {
|
|
3063
3755
|
if (options.fix) {
|
|
3064
|
-
await
|
|
3756
|
+
await execa3("./mvnw", ["spotless:apply"], {
|
|
3065
3757
|
cwd: "backend",
|
|
3066
3758
|
stdio: "inherit"
|
|
3067
3759
|
});
|
|
3068
3760
|
}
|
|
3069
|
-
await
|
|
3761
|
+
await execa3("./mvnw", ["checkstyle:check", "compile"], {
|
|
3070
3762
|
cwd: "backend",
|
|
3071
3763
|
stdio: "inherit"
|
|
3072
3764
|
});
|
|
@@ -3118,8 +3810,11 @@ var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D
|
|
|
3118
3810
|
});
|
|
3119
3811
|
|
|
3120
3812
|
// src/commands/status.ts
|
|
3813
|
+
init_esm_shims();
|
|
3814
|
+
init_utils();
|
|
3815
|
+
init_logger();
|
|
3121
3816
|
import { Command as Command8 } from "commander";
|
|
3122
|
-
import
|
|
3817
|
+
import path11 from "path";
|
|
3123
3818
|
var statusCommand = new Command8("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
|
|
3124
3819
|
try {
|
|
3125
3820
|
logger.header("\u9879\u76EE\u72B6\u6001");
|
|
@@ -3177,7 +3872,7 @@ async function displayFeatureInventory() {
|
|
|
3177
3872
|
}
|
|
3178
3873
|
const inventory = [];
|
|
3179
3874
|
for (const file of specs) {
|
|
3180
|
-
const filePath =
|
|
3875
|
+
const filePath = path11.join(specDir, file);
|
|
3181
3876
|
const content = await FileUtils.read(filePath);
|
|
3182
3877
|
const status = parseSpecStatus2(content);
|
|
3183
3878
|
inventory.push({
|
|
@@ -3236,7 +3931,7 @@ async function displayRecentActivity() {
|
|
|
3236
3931
|
}
|
|
3237
3932
|
const sorted = files.sort().reverse().slice(0, 5);
|
|
3238
3933
|
for (const file of sorted) {
|
|
3239
|
-
const filePath =
|
|
3934
|
+
const filePath = path11.join(sessionDir, file);
|
|
3240
3935
|
const stat = await FileUtils.read(filePath);
|
|
3241
3936
|
const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
|
|
3242
3937
|
const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
|
|
@@ -3274,8 +3969,11 @@ function getProgress(spec) {
|
|
|
3274
3969
|
}
|
|
3275
3970
|
|
|
3276
3971
|
// src/commands/detect-deps.ts
|
|
3972
|
+
init_esm_shims();
|
|
3973
|
+
init_utils();
|
|
3974
|
+
init_logger();
|
|
3277
3975
|
import { Command as Command9 } from "commander";
|
|
3278
|
-
import
|
|
3976
|
+
import path12 from "path";
|
|
3279
3977
|
import inquirer6 from "inquirer";
|
|
3280
3978
|
var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
|
|
3281
3979
|
try {
|
|
@@ -3303,7 +4001,7 @@ var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spe
|
|
|
3303
4001
|
process.exit(1);
|
|
3304
4002
|
}
|
|
3305
4003
|
for (const spec of specs) {
|
|
3306
|
-
const specPath =
|
|
4004
|
+
const specPath = path12.join(specsDir, spec);
|
|
3307
4005
|
logger.step(`\u5904\u7406: ${spec}`);
|
|
3308
4006
|
await detectDependencies(specPath);
|
|
3309
4007
|
logger.newLine();
|
|
@@ -3329,7 +4027,7 @@ async function detectDependencies(specFile) {
|
|
|
3329
4027
|
logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
|
|
3330
4028
|
const projectDir = ".";
|
|
3331
4029
|
const allDeps = /* @__PURE__ */ new Set();
|
|
3332
|
-
const backendDir =
|
|
4030
|
+
const backendDir = path12.join(projectDir, "backend");
|
|
3333
4031
|
const backendExists = await FileUtils.exists(backendDir);
|
|
3334
4032
|
if (backendExists) {
|
|
3335
4033
|
const apiDeps = await scanBackendApiCalls(backendDir);
|
|
@@ -3339,7 +4037,7 @@ async function detectDependencies(specFile) {
|
|
|
3339
4037
|
const serviceDeps = await scanBackendServiceRefs(backendDir);
|
|
3340
4038
|
serviceDeps.forEach((d) => allDeps.add(d));
|
|
3341
4039
|
}
|
|
3342
|
-
const frontendDir =
|
|
4040
|
+
const frontendDir = path12.join(projectDir, "frontend");
|
|
3343
4041
|
const frontendExists = await FileUtils.exists(frontendDir);
|
|
3344
4042
|
if (frontendExists) {
|
|
3345
4043
|
const frontendDeps = await scanFrontendApiCalls(frontendDir);
|
|
@@ -3380,11 +4078,11 @@ async function detectDependencies(specFile) {
|
|
|
3380
4078
|
}
|
|
3381
4079
|
async function scanBackendApiCalls(backendDir) {
|
|
3382
4080
|
const deps = [];
|
|
3383
|
-
const srcDir =
|
|
4081
|
+
const srcDir = path12.join(backendDir, "src");
|
|
3384
4082
|
try {
|
|
3385
4083
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
3386
4084
|
for (const file of javaFiles) {
|
|
3387
|
-
const filePath =
|
|
4085
|
+
const filePath = path12.join(srcDir, file);
|
|
3388
4086
|
const content = await FileUtils.read(filePath);
|
|
3389
4087
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
3390
4088
|
let match;
|
|
@@ -3402,11 +4100,11 @@ async function scanBackendApiCalls(backendDir) {
|
|
|
3402
4100
|
}
|
|
3403
4101
|
async function scanBackendEntityRelations(backendDir) {
|
|
3404
4102
|
const deps = [];
|
|
3405
|
-
const srcDir =
|
|
4103
|
+
const srcDir = path12.join(backendDir, "src");
|
|
3406
4104
|
try {
|
|
3407
4105
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
3408
4106
|
for (const file of javaFiles) {
|
|
3409
|
-
const filePath =
|
|
4107
|
+
const filePath = path12.join(srcDir, file);
|
|
3410
4108
|
const content = await FileUtils.read(filePath);
|
|
3411
4109
|
if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
|
|
3412
4110
|
const typeRegex = /type\s*=\s*(\w+)/g;
|
|
@@ -3422,11 +4120,11 @@ async function scanBackendEntityRelations(backendDir) {
|
|
|
3422
4120
|
}
|
|
3423
4121
|
async function scanBackendServiceRefs(backendDir) {
|
|
3424
4122
|
const deps = [];
|
|
3425
|
-
const srcDir =
|
|
4123
|
+
const srcDir = path12.join(backendDir, "src");
|
|
3426
4124
|
try {
|
|
3427
4125
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
3428
4126
|
for (const file of javaFiles) {
|
|
3429
|
-
const filePath =
|
|
4127
|
+
const filePath = path12.join(srcDir, file);
|
|
3430
4128
|
const content = await FileUtils.read(filePath);
|
|
3431
4129
|
const serviceRegex = /private\s+(\w+)Service/g;
|
|
3432
4130
|
let match;
|
|
@@ -3442,11 +4140,11 @@ async function scanBackendServiceRefs(backendDir) {
|
|
|
3442
4140
|
}
|
|
3443
4141
|
async function scanFrontendApiCalls(frontendDir) {
|
|
3444
4142
|
const deps = [];
|
|
3445
|
-
const srcDir =
|
|
4143
|
+
const srcDir = path12.join(frontendDir, "src");
|
|
3446
4144
|
try {
|
|
3447
4145
|
const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
|
|
3448
4146
|
for (const file of tsFiles) {
|
|
3449
|
-
const filePath =
|
|
4147
|
+
const filePath = path12.join(srcDir, file);
|
|
3450
4148
|
const content = await FileUtils.read(filePath);
|
|
3451
4149
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
3452
4150
|
let match;
|
|
@@ -3532,8 +4230,11 @@ ${deps.map((d) => ` - [x] ${d}`).join("\n")}
|
|
|
3532
4230
|
}
|
|
3533
4231
|
|
|
3534
4232
|
// src/commands/sync-memory.ts
|
|
4233
|
+
init_esm_shims();
|
|
4234
|
+
init_utils();
|
|
4235
|
+
init_logger();
|
|
3535
4236
|
import { Command as Command10 } from "commander";
|
|
3536
|
-
import
|
|
4237
|
+
import path13 from "path";
|
|
3537
4238
|
var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
|
|
3538
4239
|
try {
|
|
3539
4240
|
logger.header("\u540C\u6B65 AI_MEMORY.md");
|
|
@@ -3553,6 +4254,7 @@ var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 A
|
|
|
3553
4254
|
await syncFeatureInventory(aiMemoryFile, ".");
|
|
3554
4255
|
await syncApiInventory(aiMemoryFile, ".");
|
|
3555
4256
|
await syncDataModels(aiMemoryFile, ".");
|
|
4257
|
+
await syncTemplateVersions(aiMemoryFile, ".");
|
|
3556
4258
|
await updateSyncTime(aiMemoryFile);
|
|
3557
4259
|
logger.success("AI_MEMORY.md \u5DF2\u540C\u6B65");
|
|
3558
4260
|
} catch (error) {
|
|
@@ -3565,7 +4267,7 @@ var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 A
|
|
|
3565
4267
|
});
|
|
3566
4268
|
async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
3567
4269
|
logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
|
|
3568
|
-
const specsDir =
|
|
4270
|
+
const specsDir = path13.join(projectDir, "docs/specs");
|
|
3569
4271
|
const exists = await FileUtils.exists(specsDir);
|
|
3570
4272
|
if (!exists) {
|
|
3571
4273
|
return;
|
|
@@ -3580,7 +4282,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
3580
4282
|
for (const specFile of specs) {
|
|
3581
4283
|
const name = specFile.replace(".md", "");
|
|
3582
4284
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3583
|
-
const specPath =
|
|
4285
|
+
const specPath = path13.join(specsDir, specFile);
|
|
3584
4286
|
const content = await FileUtils.read(specPath);
|
|
3585
4287
|
const status = parseSpecStatus3(content);
|
|
3586
4288
|
const progress = getSpecProgress(content);
|
|
@@ -3598,7 +4300,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
3598
4300
|
}
|
|
3599
4301
|
async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
3600
4302
|
logger.step("\u540C\u6B65 API \u5217\u8868...");
|
|
3601
|
-
const backendDir =
|
|
4303
|
+
const backendDir = path13.join(projectDir, "backend");
|
|
3602
4304
|
const exists = await FileUtils.exists(backendDir);
|
|
3603
4305
|
if (!exists) {
|
|
3604
4306
|
return;
|
|
@@ -3608,13 +4310,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
|
3608
4310
|
lines.push("");
|
|
3609
4311
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
|
|
3610
4312
|
lines.push("");
|
|
3611
|
-
const srcDir =
|
|
4313
|
+
const srcDir = path13.join(backendDir, "src");
|
|
3612
4314
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
3613
4315
|
if (controllers.length === 0) {
|
|
3614
4316
|
lines.push("\u6682\u65E0 API");
|
|
3615
4317
|
} else {
|
|
3616
4318
|
for (const controllerFile of controllers) {
|
|
3617
|
-
const controllerPath =
|
|
4319
|
+
const controllerPath = path13.join(srcDir, controllerFile);
|
|
3618
4320
|
const controllerName = controllerFile.replace(".java", "");
|
|
3619
4321
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
3620
4322
|
lines.push(`### ${module} \u6A21\u5757`);
|
|
@@ -3691,7 +4393,7 @@ function extractMethodComment(content, methodName) {
|
|
|
3691
4393
|
}
|
|
3692
4394
|
async function syncDataModels(aiMemoryFile, projectDir) {
|
|
3693
4395
|
logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
|
|
3694
|
-
const backendDir =
|
|
4396
|
+
const backendDir = path13.join(projectDir, "backend");
|
|
3695
4397
|
const exists = await FileUtils.exists(backendDir);
|
|
3696
4398
|
if (!exists) {
|
|
3697
4399
|
return;
|
|
@@ -3701,7 +4403,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
3701
4403
|
lines.push("");
|
|
3702
4404
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
|
|
3703
4405
|
lines.push("");
|
|
3704
|
-
const srcDir =
|
|
4406
|
+
const srcDir = path13.join(backendDir, "src");
|
|
3705
4407
|
const entities = await FileUtils.findFiles("*Entity.java", srcDir);
|
|
3706
4408
|
if (entities.length === 0) {
|
|
3707
4409
|
lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
|
|
@@ -3709,7 +4411,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
3709
4411
|
lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
|
|
3710
4412
|
lines.push("|------|------|------|------|");
|
|
3711
4413
|
for (const entityFile of entities) {
|
|
3712
|
-
const entityPath =
|
|
4414
|
+
const entityPath = path13.join(srcDir, entityFile);
|
|
3713
4415
|
const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
|
|
3714
4416
|
const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
3715
4417
|
const content = await FileUtils.read(entityPath);
|
|
@@ -3798,10 +4500,56 @@ function getSpecProgress(spec) {
|
|
|
3798
4500
|
}
|
|
3799
4501
|
return `${completedTodos}/${totalTodos}`;
|
|
3800
4502
|
}
|
|
4503
|
+
async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
4504
|
+
logger.step("\u540C\u6B65\u6A21\u677F\u7248\u672C\u4FE1\u606F...");
|
|
4505
|
+
const config = await readTemplateConfig(projectDir);
|
|
4506
|
+
if (!config) {
|
|
4507
|
+
return;
|
|
4508
|
+
}
|
|
4509
|
+
const lines = [];
|
|
4510
|
+
lines.push("## \u6A21\u677F\u7248\u672C\u4FE1\u606F");
|
|
4511
|
+
lines.push("");
|
|
4512
|
+
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u7BA1\u7406\uFF0C\u8BB0\u5F55\u4F7F\u7528\u7684\u524D\u540E\u7AEF\u6A21\u677F\u7248\u672C");
|
|
4513
|
+
lines.push("");
|
|
4514
|
+
lines.push("| \u7C7B\u578B | \u4ED3\u5E93 | Tag | Branch | Commit | \u66F4\u65B0\u65F6\u95F4 |");
|
|
4515
|
+
lines.push("|------|------|-----|--------|--------|----------|");
|
|
4516
|
+
if (config.frontend) {
|
|
4517
|
+
const repoName = extractRepoName(config.frontend.repository);
|
|
4518
|
+
const commit = config.frontend.commit?.substring(0, 8) || "-";
|
|
4519
|
+
const tag = config.frontend.tag || "-";
|
|
4520
|
+
const branch = config.frontend.branch || "-";
|
|
4521
|
+
const updateDate = config.frontend.lastUpdate ? new Date(config.frontend.lastUpdate).toLocaleDateString("zh-CN") : "-";
|
|
4522
|
+
lines.push(`| \u524D\u7AEF | ${repoName} | ${tag} | ${branch} | ${commit} | ${updateDate} |`);
|
|
4523
|
+
}
|
|
4524
|
+
if (config.backend) {
|
|
4525
|
+
const repoName = extractRepoName(config.backend.repository);
|
|
4526
|
+
const commit = config.backend.commit?.substring(0, 8) || "-";
|
|
4527
|
+
const tag = config.backend.tag || "-";
|
|
4528
|
+
const branch = config.backend.branch || "-";
|
|
4529
|
+
const updateDate = config.backend.lastUpdate ? new Date(config.backend.lastUpdate).toLocaleDateString("zh-CN") : "-";
|
|
4530
|
+
lines.push(`| \u540E\u7AEF | ${repoName} | ${tag} | ${branch} | ${commit} | ${updateDate} |`);
|
|
4531
|
+
}
|
|
4532
|
+
const newContent = lines.join("\n") + "\n";
|
|
4533
|
+
await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
|
|
4534
|
+
}
|
|
4535
|
+
function extractRepoName(repository) {
|
|
4536
|
+
let path17 = repository;
|
|
4537
|
+
path17 = path17.replace(/^https?:\/\//, "");
|
|
4538
|
+
path17 = path17.replace(/^git@/, "");
|
|
4539
|
+
const parts = path17.split("/");
|
|
4540
|
+
if (parts.length > 1) {
|
|
4541
|
+
path17 = parts.slice(1).join("/");
|
|
4542
|
+
}
|
|
4543
|
+
path17 = path17.replace(/\.git$/, "");
|
|
4544
|
+
return path17;
|
|
4545
|
+
}
|
|
3801
4546
|
|
|
3802
4547
|
// src/commands/check-api.ts
|
|
4548
|
+
init_esm_shims();
|
|
4549
|
+
init_utils();
|
|
4550
|
+
init_logger();
|
|
3803
4551
|
import { Command as Command11 } from "commander";
|
|
3804
|
-
import
|
|
4552
|
+
import path14 from "path";
|
|
3805
4553
|
import inquirer7 from "inquirer";
|
|
3806
4554
|
import { Listr as Listr6 } from "listr2";
|
|
3807
4555
|
var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
|
|
@@ -3865,7 +4613,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
|
|
|
3865
4613
|
}
|
|
3866
4614
|
});
|
|
3867
4615
|
async function checkApiConflicts(projectDir) {
|
|
3868
|
-
const backendDir =
|
|
4616
|
+
const backendDir = path14.join(projectDir, "backend");
|
|
3869
4617
|
const exists = await FileUtils.exists(backendDir);
|
|
3870
4618
|
if (!exists) {
|
|
3871
4619
|
logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
|
|
@@ -3874,10 +4622,10 @@ async function checkApiConflicts(projectDir) {
|
|
|
3874
4622
|
logger.step("\u626B\u63CF\u540E\u7AEF API...");
|
|
3875
4623
|
logger.newLine();
|
|
3876
4624
|
const apiMap = /* @__PURE__ */ new Map();
|
|
3877
|
-
const srcDir =
|
|
4625
|
+
const srcDir = path14.join(backendDir, "src");
|
|
3878
4626
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
3879
4627
|
for (const controllerFile of controllers) {
|
|
3880
|
-
const controllerPath =
|
|
4628
|
+
const controllerPath = path14.join(srcDir, controllerFile);
|
|
3881
4629
|
const apis = await extractApisFromController(controllerPath);
|
|
3882
4630
|
for (const api of apis) {
|
|
3883
4631
|
const key = `${api.method}:${api.path}`;
|
|
@@ -3908,8 +4656,8 @@ async function checkApiConflicts(projectDir) {
|
|
|
3908
4656
|
}
|
|
3909
4657
|
}
|
|
3910
4658
|
async function detectApiChanges(projectDir) {
|
|
3911
|
-
const backendDir =
|
|
3912
|
-
const registryFile =
|
|
4659
|
+
const backendDir = path14.join(projectDir, "backend");
|
|
4660
|
+
const registryFile = path14.join(projectDir, "docs/api-registry.md");
|
|
3913
4661
|
const registryExists = await FileUtils.exists(registryFile);
|
|
3914
4662
|
if (!registryExists) {
|
|
3915
4663
|
logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
|
|
@@ -3921,10 +4669,10 @@ async function detectApiChanges(projectDir) {
|
|
|
3921
4669
|
const registryContent = await FileUtils.read(registryFile);
|
|
3922
4670
|
const existingApis = extractApisFromRegistry(registryContent);
|
|
3923
4671
|
const currentApis = /* @__PURE__ */ new Map();
|
|
3924
|
-
const srcDir =
|
|
4672
|
+
const srcDir = path14.join(backendDir, "src");
|
|
3925
4673
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
3926
4674
|
for (const controllerFile of controllers) {
|
|
3927
|
-
const controllerPath =
|
|
4675
|
+
const controllerPath = path14.join(srcDir, controllerFile);
|
|
3928
4676
|
const apis = await extractApisFromController(controllerPath);
|
|
3929
4677
|
for (const api of apis) {
|
|
3930
4678
|
const key = `${api.method}:${api.path}`;
|
|
@@ -3986,9 +4734,9 @@ async function detectApiChanges(projectDir) {
|
|
|
3986
4734
|
}
|
|
3987
4735
|
}
|
|
3988
4736
|
async function generateApiRegistry(projectDir) {
|
|
3989
|
-
const registryFile =
|
|
4737
|
+
const registryFile = path14.join(projectDir, "docs/api-registry.md");
|
|
3990
4738
|
logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
|
|
3991
|
-
await FileUtils.ensureDir(
|
|
4739
|
+
await FileUtils.ensureDir(path14.dirname(registryFile));
|
|
3992
4740
|
const header = `# API Registry
|
|
3993
4741
|
|
|
3994
4742
|
> \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
|
|
@@ -4017,14 +4765,14 @@ async function generateApiRegistry(projectDir) {
|
|
|
4017
4765
|
*\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
|
|
4018
4766
|
`;
|
|
4019
4767
|
let content = header;
|
|
4020
|
-
const backendDir =
|
|
4768
|
+
const backendDir = path14.join(projectDir, "backend");
|
|
4021
4769
|
const exists = await FileUtils.exists(backendDir);
|
|
4022
4770
|
if (exists) {
|
|
4023
|
-
const srcDir =
|
|
4771
|
+
const srcDir = path14.join(backendDir, "src");
|
|
4024
4772
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
4025
4773
|
const moduleMap = /* @__PURE__ */ new Map();
|
|
4026
4774
|
for (const controllerFile of controllers) {
|
|
4027
|
-
const controllerPath =
|
|
4775
|
+
const controllerPath = path14.join(srcDir, controllerFile);
|
|
4028
4776
|
const controllerName = controllerFile.replace(".java", "");
|
|
4029
4777
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
4030
4778
|
if (!moduleMap.has(module)) {
|
|
@@ -4108,10 +4856,10 @@ function extractApisFromRegistry(registryContent) {
|
|
|
4108
4856
|
let match;
|
|
4109
4857
|
while ((match = apiRegex.exec(registryContent)) !== null) {
|
|
4110
4858
|
const method = match[1];
|
|
4111
|
-
const
|
|
4859
|
+
const path17 = match[2].trim();
|
|
4112
4860
|
const description = match[3].trim();
|
|
4113
|
-
const key = `${method}:${
|
|
4114
|
-
apis.set(key, { method, path:
|
|
4861
|
+
const key = `${method}:${path17}`;
|
|
4862
|
+
apis.set(key, { method, path: path17, description });
|
|
4115
4863
|
}
|
|
4116
4864
|
return apis;
|
|
4117
4865
|
}
|
|
@@ -4129,8 +4877,11 @@ function extractMethodComment2(content, methodName) {
|
|
|
4129
4877
|
}
|
|
4130
4878
|
|
|
4131
4879
|
// src/commands/logs.ts
|
|
4880
|
+
init_esm_shims();
|
|
4881
|
+
init_utils();
|
|
4882
|
+
init_logger();
|
|
4132
4883
|
import { Command as Command12 } from "commander";
|
|
4133
|
-
import
|
|
4884
|
+
import path15 from "path";
|
|
4134
4885
|
import inquirer8 from "inquirer";
|
|
4135
4886
|
var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668 (today, --all, \u6216\u65E5\u671F YYYY-MM-DD)").description("\u67E5\u770B\u4F1A\u8BDD\u65E5\u5FD7").action(async (filter = "today") => {
|
|
4136
4887
|
try {
|
|
@@ -4155,7 +4906,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
4155
4906
|
case "":
|
|
4156
4907
|
case "today": {
|
|
4157
4908
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4158
|
-
targetDir =
|
|
4909
|
+
targetDir = path15.join(sessionsDir, today);
|
|
4159
4910
|
const todayExists = await FileUtils.exists(targetDir);
|
|
4160
4911
|
if (!todayExists) {
|
|
4161
4912
|
logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
|
|
@@ -4171,7 +4922,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
4171
4922
|
break;
|
|
4172
4923
|
}
|
|
4173
4924
|
default: {
|
|
4174
|
-
targetDir =
|
|
4925
|
+
targetDir = path15.join(sessionsDir, filter);
|
|
4175
4926
|
const dateExists = await FileUtils.exists(targetDir);
|
|
4176
4927
|
if (!dateExists) {
|
|
4177
4928
|
logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
|
|
@@ -4195,7 +4946,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
4195
4946
|
process.exit(0);
|
|
4196
4947
|
}
|
|
4197
4948
|
for (let i = 0; i < logs.length; i++) {
|
|
4198
|
-
const relPath =
|
|
4949
|
+
const relPath = path15.relative(sessionsDir, logs[i]);
|
|
4199
4950
|
logger.step(`${i + 1}) ${relPath}`);
|
|
4200
4951
|
}
|
|
4201
4952
|
logger.newLine();
|
|
@@ -4236,7 +4987,7 @@ async function collectLogFiles(targetDir) {
|
|
|
4236
4987
|
const allFiles = await FileUtils.findFiles("*.md", targetDir);
|
|
4237
4988
|
const filtered = allFiles.filter((f) => f !== "index.md");
|
|
4238
4989
|
for (const file of filtered) {
|
|
4239
|
-
const filePath =
|
|
4990
|
+
const filePath = path15.join(targetDir, file);
|
|
4240
4991
|
const stat = await FileUtils.exists(filePath);
|
|
4241
4992
|
if (stat) {
|
|
4242
4993
|
logs.push(filePath);
|
|
@@ -4247,9 +4998,683 @@ async function collectLogFiles(targetDir) {
|
|
|
4247
4998
|
return logs;
|
|
4248
4999
|
}
|
|
4249
5000
|
|
|
5001
|
+
// src/commands/update.ts
|
|
5002
|
+
init_esm_shims();
|
|
5003
|
+
import { Command as Command13 } from "commander";
|
|
5004
|
+
import path16 from "path";
|
|
5005
|
+
init_logger();
|
|
5006
|
+
init_utils();
|
|
5007
|
+
import { execa as execa4 } from "execa";
|
|
5008
|
+
import inquirer9 from "inquirer";
|
|
5009
|
+
import fs3 from "fs-extra";
|
|
5010
|
+
var updateCommand = new Command13("update").description("\u68C0\u67E5\u5E76\u66F4\u65B0\u6A21\u677F\u7248\u672C").option("-f, --frontend", "\u68C0\u67E5\u524D\u7AEF\u6A21\u677F\u66F4\u65B0").option("-b, --backend", "\u68C0\u67E5\u540E\u7AEF\u6A21\u677F\u66F4\u65B0").option("-a, --all", "\u68C0\u67E5\u6240\u6709\u6A21\u677F (\u9ED8\u8BA4)").option("-t, --tag <tag>", "\u66F4\u65B0\u5230\u6307\u5B9A\u6807\u7B7E").option("-B, --branch <branch>", "\u66F4\u65B0\u5230\u6307\u5B9A\u5206\u652F").option("--dry-run", "\u9884\u89C8\u66F4\u65B0\uFF0C\u4E0D\u5B9E\u9645\u6267\u884C").action(async (options) => {
|
|
5011
|
+
try {
|
|
5012
|
+
logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
|
|
5013
|
+
logger.newLine();
|
|
5014
|
+
const hasConfig = await FileUtils.exists("TECH_STACK.md");
|
|
5015
|
+
if (!hasConfig) {
|
|
5016
|
+
logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
|
|
5017
|
+
logger.info("\u8BF7\u5148\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
5018
|
+
process.exit(1);
|
|
5019
|
+
}
|
|
5020
|
+
const projectPath = ".";
|
|
5021
|
+
const checkAll = !options.frontend && !options.backend;
|
|
5022
|
+
const checkFrontend = options.frontend || checkAll;
|
|
5023
|
+
const checkBackend = options.backend || checkAll;
|
|
5024
|
+
const forceUpdate = options.tag || options.branch;
|
|
5025
|
+
const updates = [];
|
|
5026
|
+
const updateOptions = {
|
|
5027
|
+
tag: options.tag,
|
|
5028
|
+
branch: options.branch,
|
|
5029
|
+
dryRun: options.dryRun
|
|
5030
|
+
};
|
|
5031
|
+
if (checkFrontend) {
|
|
5032
|
+
logger.step("\u68C0\u67E5\u524D\u7AEF\u6A21\u677F...");
|
|
5033
|
+
const frontendInfo = await checkTemplateUpdate(projectPath, "frontend");
|
|
5034
|
+
if (frontendInfo) {
|
|
5035
|
+
if (frontendInfo.needsUpdate || forceUpdate) {
|
|
5036
|
+
const version = updateOptions.tag || updateOptions.branch || frontendInfo.latestTag || frontendInfo.latestCommit?.substring(0, 8);
|
|
5037
|
+
logger.success(`\u524D\u7AEF\u6A21\u677F${forceUpdate ? "\u5C06\u66F4\u65B0" : "\u6709\u66F4\u65B0"} (${version})`);
|
|
5038
|
+
updates.push({ type: "frontend", info: frontendInfo, updateOptions });
|
|
5039
|
+
} else {
|
|
5040
|
+
logger.info("\u524D\u7AEF\u6A21\u677F\u5DF2\u662F\u6700\u65B0\u7248\u672C");
|
|
5041
|
+
}
|
|
5042
|
+
} else {
|
|
5043
|
+
logger.info("\u524D\u7AEF\u6A21\u677F\u672A\u914D\u7F6E\u6216\u65E0\u6CD5\u68C0\u67E5");
|
|
5044
|
+
}
|
|
5045
|
+
logger.newLine();
|
|
5046
|
+
}
|
|
5047
|
+
if (checkBackend) {
|
|
5048
|
+
logger.step("\u68C0\u67E5\u540E\u7AEF\u6A21\u677F...");
|
|
5049
|
+
const backendInfo = await checkTemplateUpdate(projectPath, "backend");
|
|
5050
|
+
if (backendInfo) {
|
|
5051
|
+
if (backendInfo.needsUpdate || forceUpdate) {
|
|
5052
|
+
const version = updateOptions.tag || updateOptions.branch || backendInfo.latestTag || backendInfo.latestCommit?.substring(0, 8);
|
|
5053
|
+
logger.success(`\u540E\u7AEF\u6A21\u677F${forceUpdate ? "\u5C06\u66F4\u65B0" : "\u6709\u66F4\u65B0"} (${version})`);
|
|
5054
|
+
updates.push({ type: "backend", info: backendInfo, updateOptions });
|
|
5055
|
+
} else {
|
|
5056
|
+
logger.info("\u540E\u7AEF\u6A21\u677F\u5DF2\u662F\u6700\u65B0\u7248\u672C");
|
|
5057
|
+
}
|
|
5058
|
+
} else {
|
|
5059
|
+
logger.info("\u540E\u7AEF\u6A21\u677F\u672A\u914D\u7F6E\u6216\u65E0\u6CD5\u68C0\u67E5");
|
|
5060
|
+
}
|
|
5061
|
+
logger.newLine();
|
|
5062
|
+
}
|
|
5063
|
+
if (updates.length === 0) {
|
|
5064
|
+
logger.success("\u6240\u6709\u6A21\u677F\u5DF2\u662F\u6700\u65B0\u7248\u672C!");
|
|
5065
|
+
return;
|
|
5066
|
+
}
|
|
5067
|
+
if (options.dryRun) {
|
|
5068
|
+
logger.header(`[Dry Run] \u53D1\u73B0 ${updates.length} \u4E2A\u6A21\u677F`);
|
|
5069
|
+
} else {
|
|
5070
|
+
logger.header(`\u53D1\u73B0 ${updates.length} \u4E2A\u6A21\u677F\u66F4\u65B0`);
|
|
5071
|
+
}
|
|
5072
|
+
logger.newLine();
|
|
5073
|
+
for (const update of updates) {
|
|
5074
|
+
const version = update.updateOptions?.tag || update.updateOptions?.branch || update.info.latestTag || update.info.latestCommit?.substring(0, 8);
|
|
5075
|
+
logger.step(`${update.type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F: ${version}`);
|
|
5076
|
+
}
|
|
5077
|
+
logger.newLine();
|
|
5078
|
+
if (options.dryRun) {
|
|
5079
|
+
logger.info("Dry run \u6A21\u5F0F\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u66F4\u65B0");
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
const answers = await inquirer9.prompt([
|
|
5083
|
+
{
|
|
5084
|
+
type: "confirm",
|
|
5085
|
+
name: "shouldUpdate",
|
|
5086
|
+
message: "\u662F\u5426\u66F4\u65B0\u6A21\u677F?",
|
|
5087
|
+
default: false
|
|
5088
|
+
}
|
|
5089
|
+
]);
|
|
5090
|
+
if (answers.shouldUpdate) {
|
|
5091
|
+
await performUpdate(projectPath, updates);
|
|
5092
|
+
} else {
|
|
5093
|
+
logger.info("\u5DF2\u53D6\u6D88\u66F4\u65B0");
|
|
5094
|
+
}
|
|
5095
|
+
} catch (error) {
|
|
5096
|
+
logger.error(`\u66F4\u65B0\u68C0\u67E5\u5931\u8D25: ${error.message}`);
|
|
5097
|
+
if (process.env.DEBUG) {
|
|
5098
|
+
console.error(error);
|
|
5099
|
+
}
|
|
5100
|
+
process.exit(1);
|
|
5101
|
+
}
|
|
5102
|
+
});
|
|
5103
|
+
async function performUpdate(projectPath, updates) {
|
|
5104
|
+
logger.newLine();
|
|
5105
|
+
logger.info("\u5F00\u59CB\u66F4\u65B0\u6A21\u677F...");
|
|
5106
|
+
for (const update of updates) {
|
|
5107
|
+
const { type, info, updateOptions } = update;
|
|
5108
|
+
const targetDir = type === "frontend" ? "frontend" : "backend";
|
|
5109
|
+
const targetPath = path16.join(projectPath, targetDir);
|
|
5110
|
+
logger.newLine();
|
|
5111
|
+
logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
|
|
5112
|
+
if (updateOptions?.tag || updateOptions?.branch) {
|
|
5113
|
+
const { userConfigManager: userConfigManager2 } = await Promise.resolve().then(() => (init_user_config(), user_config_exports));
|
|
5114
|
+
const { GitLabAPI: GitLabAPI2 } = await Promise.resolve().then(() => (init_gitlab_api(), gitlab_api_exports));
|
|
5115
|
+
const config = await userConfigManager2.getGitLabConfig();
|
|
5116
|
+
if (config) {
|
|
5117
|
+
const gitlabAPI = new GitLabAPI2(config);
|
|
5118
|
+
const projectPathEncoded = GitLabAPI2.parseProjectPath(info.repository);
|
|
5119
|
+
if (updateOptions.tag) {
|
|
5120
|
+
const isValid = await gitlabAPI.validateTag(projectPathEncoded, updateOptions.tag);
|
|
5121
|
+
if (!isValid) {
|
|
5122
|
+
logger.error(`${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F tag "${updateOptions.tag}" \u4E0D\u5B58\u5728`);
|
|
5123
|
+
continue;
|
|
5124
|
+
}
|
|
5125
|
+
logger.info(`\u4F7F\u7528 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F tag: ${updateOptions.tag}`);
|
|
5126
|
+
}
|
|
5127
|
+
if (updateOptions.branch) {
|
|
5128
|
+
const isValid = await gitlabAPI.validateBranch(projectPathEncoded, updateOptions.branch);
|
|
5129
|
+
if (!isValid) {
|
|
5130
|
+
logger.error(`${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F\u5206\u652F "${updateOptions.branch}" \u4E0D\u5B58\u5728`);
|
|
5131
|
+
continue;
|
|
5132
|
+
}
|
|
5133
|
+
logger.info(`\u4F7F\u7528 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F\u5206\u652F: ${updateOptions.branch}`);
|
|
5134
|
+
}
|
|
5135
|
+
} else {
|
|
5136
|
+
logger.warn("\u672A\u914D\u7F6E GitLab Token\uFF0C\u8DF3\u8FC7\u7248\u672C\u9A8C\u8BC1");
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
|
|
5140
|
+
const backupDir = path16.join(projectPath, `.backup-${Date.now()}`);
|
|
5141
|
+
await fs3.copy(targetPath, path16.join(backupDir, targetDir));
|
|
5142
|
+
logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
|
|
5143
|
+
if (updateOptions?.dryRun) {
|
|
5144
|
+
logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
|
|
5145
|
+
logger.info(` Ref: ${ref}`);
|
|
5146
|
+
logger.info(` \u4ED3\u5E93: ${info.repository}`);
|
|
5147
|
+
logger.info("\u5907\u4EFD\u5DF2\u521B\u5EFA\uFF0C\u8DF3\u8FC7\u5B9E\u9645\u66F4\u65B0");
|
|
5148
|
+
continue;
|
|
5149
|
+
}
|
|
5150
|
+
try {
|
|
5151
|
+
const tempDir = path16.join(projectPath, `.template-update-${Date.now()}`);
|
|
5152
|
+
await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
|
|
5153
|
+
stdio: "pipe"
|
|
5154
|
+
});
|
|
5155
|
+
const { stdout: commit } = await execa4("git", ["rev-parse", "HEAD"], {
|
|
5156
|
+
cwd: tempDir,
|
|
5157
|
+
stdio: "pipe"
|
|
5158
|
+
});
|
|
5159
|
+
const { stdout: tags } = await execa4("git", ["tag", "-l", "--sort=-v:refname"], {
|
|
5160
|
+
cwd: tempDir,
|
|
5161
|
+
stdio: "pipe"
|
|
5162
|
+
});
|
|
5163
|
+
const latestTag = tags.split("\n")[0] || void 0;
|
|
5164
|
+
const keepFiles = [".env.local", "package-lock.json", "node_modules"];
|
|
5165
|
+
const currentFiles = await FileUtils.findFiles("*", targetPath);
|
|
5166
|
+
for (const file of currentFiles) {
|
|
5167
|
+
if (!keepFiles.includes(file)) {
|
|
5168
|
+
const filePath = path16.join(targetPath, file);
|
|
5169
|
+
try {
|
|
5170
|
+
await fs3.remove(filePath);
|
|
5171
|
+
} catch {
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
}
|
|
5175
|
+
await fs3.copy(tempDir, targetPath, {
|
|
5176
|
+
filter: (src) => !src.includes(".git")
|
|
5177
|
+
});
|
|
5178
|
+
await fs3.remove(tempDir);
|
|
5179
|
+
await updateTemplateVersion(projectPath, type, commit.trim(), {
|
|
5180
|
+
tag: updateOptions?.tag || latestTag,
|
|
5181
|
+
branch: updateOptions?.branch
|
|
5182
|
+
});
|
|
5183
|
+
logger.success(`${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F\u66F4\u65B0\u5B8C\u6210!`);
|
|
5184
|
+
logger.info(`\u65B0\u7248\u672C: ${updateOptions?.tag || updateOptions?.branch || latestTag || commit.substring(0, 8)}`);
|
|
5185
|
+
logger.info(`\u5907\u4EFD\u4F4D\u7F6E: ${backupDir}`);
|
|
5186
|
+
} catch (error) {
|
|
5187
|
+
logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
5188
|
+
logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
|
|
5189
|
+
await fs3.remove(targetPath);
|
|
5190
|
+
await fs3.copy(path16.join(backupDir, targetDir), targetPath);
|
|
5191
|
+
await fs3.remove(backupDir);
|
|
5192
|
+
logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
logger.newLine();
|
|
5196
|
+
logger.header("\u6A21\u677F\u66F4\u65B0\u5B8C\u6210!");
|
|
5197
|
+
logger.newLine();
|
|
5198
|
+
logger.info("\u4E0B\u4E00\u6B65:");
|
|
5199
|
+
logger.step("1. \u68C0\u67E5\u66F4\u65B0\u540E\u7684\u4EE3\u7801");
|
|
5200
|
+
logger.step("2. \u8FD0\u884C npm install \u5B89\u88C5\u65B0\u4F9D\u8D56");
|
|
5201
|
+
logger.step("3. \u6D4B\u8BD5\u5E94\u7528\u662F\u5426\u6B63\u5E38\u8FD0\u884C");
|
|
5202
|
+
logger.step("4. \u63D0\u4EA4\u4EE3\u7801\u5230 Git");
|
|
5203
|
+
logger.newLine();
|
|
5204
|
+
}
|
|
5205
|
+
|
|
5206
|
+
// src/commands/config.ts
|
|
5207
|
+
init_esm_shims();
|
|
5208
|
+
init_user_config();
|
|
5209
|
+
init_gitlab_api();
|
|
5210
|
+
init_logger();
|
|
5211
|
+
import { Command as Command14 } from "commander";
|
|
5212
|
+
import inquirer10 from "inquirer";
|
|
5213
|
+
import chalk2 from "chalk";
|
|
5214
|
+
var setTokenCommand = new Command14("set-token").description("\u8BBE\u7F6E GitLab Access Token").option("-t, --token <token>", "Access Token").option("-u, --url <url>", "GitLab Base URL", "https://gitlab.com").action(async (options) => {
|
|
5215
|
+
try {
|
|
5216
|
+
logger.header("GitLab Access Token \u914D\u7F6E");
|
|
5217
|
+
logger.newLine();
|
|
5218
|
+
let { token, url } = options;
|
|
5219
|
+
if (!token) {
|
|
5220
|
+
const answers = await inquirer10.prompt([
|
|
5221
|
+
{
|
|
5222
|
+
type: "password",
|
|
5223
|
+
name: "token",
|
|
5224
|
+
message: "\u8BF7\u8F93\u5165 GitLab Access Token:",
|
|
5225
|
+
mask: "*",
|
|
5226
|
+
validate: (input) => {
|
|
5227
|
+
if (!input || input.trim().length === 0) {
|
|
5228
|
+
return "Token \u4E0D\u80FD\u4E3A\u7A7A";
|
|
5229
|
+
}
|
|
5230
|
+
return true;
|
|
5231
|
+
}
|
|
5232
|
+
},
|
|
5233
|
+
{
|
|
5234
|
+
type: "input",
|
|
5235
|
+
name: "url",
|
|
5236
|
+
message: "\u8BF7\u8F93\u5165 GitLab Base URL:",
|
|
5237
|
+
default: "https://gitlab.com",
|
|
5238
|
+
validate: (input) => {
|
|
5239
|
+
try {
|
|
5240
|
+
new URL(input);
|
|
5241
|
+
return true;
|
|
5242
|
+
} catch {
|
|
5243
|
+
return "\u8BF7\u8F93\u5165\u6709\u6548\u7684 URL";
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
]);
|
|
5248
|
+
token = answers.token;
|
|
5249
|
+
url = answers.url;
|
|
5250
|
+
}
|
|
5251
|
+
try {
|
|
5252
|
+
new URL(url);
|
|
5253
|
+
} catch {
|
|
5254
|
+
logger.error("\u65E0\u6548\u7684 GitLab URL");
|
|
5255
|
+
process.exit(1);
|
|
5256
|
+
}
|
|
5257
|
+
url = url.replace(/\/+$/, "");
|
|
5258
|
+
logger.info("\u9A8C\u8BC1 Token...");
|
|
5259
|
+
const gitlabAPI = new GitLabAPI({ accessToken: token, baseUrl: url });
|
|
5260
|
+
const isValid = await gitlabAPI.authenticate();
|
|
5261
|
+
if (!isValid) {
|
|
5262
|
+
logger.error("Token \u9A8C\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5:");
|
|
5263
|
+
logger.info("1. Token \u662F\u5426\u6B63\u786E");
|
|
5264
|
+
logger.info("2. Token \u662F\u5426\u6709 api \u6743\u9650");
|
|
5265
|
+
logger.info("3. GitLab URL \u662F\u5426\u6B63\u786E");
|
|
5266
|
+
process.exit(1);
|
|
5267
|
+
}
|
|
5268
|
+
logger.success("Token \u9A8C\u8BC1\u6210\u529F!");
|
|
5269
|
+
await userConfigManager.updateGitLabToken(token, url);
|
|
5270
|
+
logger.success("\u914D\u7F6E\u5DF2\u4FDD\u5B58!");
|
|
5271
|
+
logger.newLine();
|
|
5272
|
+
logger.info(`GitLab URL: ${chalk2.cyan(url)}`);
|
|
5273
|
+
logger.info(`\u914D\u7F6E\u6587\u4EF6: ${chalk2.gray(userConfigManager.getConfigPath())}`);
|
|
5274
|
+
} catch (error) {
|
|
5275
|
+
logger.error(`\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
5276
|
+
if (process.env.DEBUG) {
|
|
5277
|
+
console.error(error);
|
|
5278
|
+
}
|
|
5279
|
+
process.exit(1);
|
|
5280
|
+
}
|
|
5281
|
+
});
|
|
5282
|
+
var showConfigCommand = new Command14("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
|
|
5283
|
+
try {
|
|
5284
|
+
logger.header("GitLab \u914D\u7F6E");
|
|
5285
|
+
logger.newLine();
|
|
5286
|
+
const hasConfig = await userConfigManager.hasConfig();
|
|
5287
|
+
if (!hasConfig) {
|
|
5288
|
+
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
5289
|
+
logger.info("\u8BF7\u4F7F\u7528 'team-cli config set-token' \u8FDB\u884C\u914D\u7F6E");
|
|
5290
|
+
} else {
|
|
5291
|
+
const config = await userConfigManager.load();
|
|
5292
|
+
if (config?.gitlab) {
|
|
5293
|
+
const token = config.gitlab.accessToken;
|
|
5294
|
+
const maskedToken = token ? `${token.substring(0, 8)}${"*".repeat(Math.min(16, token.length - 8))}` : "";
|
|
5295
|
+
logger.info(`GitLab URL: ${chalk2.cyan(config.gitlab.baseUrl)}`);
|
|
5296
|
+
logger.info(`Access Token: ${chalk2.yellow(maskedToken)}`);
|
|
5297
|
+
logger.info(`Timeout: ${chalk2.gray(config.gitlab.timeout || 3e4)}ms`);
|
|
5298
|
+
if (config.preferences) {
|
|
5299
|
+
logger.newLine();
|
|
5300
|
+
logger.info("\u504F\u597D\u8BBE\u7F6E:");
|
|
5301
|
+
if (config.preferences.defaultBackendBranch) {
|
|
5302
|
+
logger.info(` \u9ED8\u8BA4\u540E\u7AEF\u5206\u652F: ${chalk2.cyan(config.preferences.defaultBackendBranch)}`);
|
|
5303
|
+
}
|
|
5304
|
+
if (config.preferences.defaultFrontendBranch) {
|
|
5305
|
+
logger.info(` \u9ED8\u8BA4\u524D\u7AEF\u5206\u652F: ${chalk2.cyan(config.preferences.defaultFrontendBranch)}`);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
logger.newLine();
|
|
5309
|
+
logger.info("\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E:");
|
|
5310
|
+
logger.info(` ${chalk2.gray(userConfigManager.getConfigPath())}`);
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
} catch (error) {
|
|
5314
|
+
logger.error(`\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
5315
|
+
if (process.env.DEBUG) {
|
|
5316
|
+
console.error(error);
|
|
5317
|
+
}
|
|
5318
|
+
process.exit(1);
|
|
5319
|
+
}
|
|
5320
|
+
});
|
|
5321
|
+
var removeConfigCommand = new Command14("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
|
|
5322
|
+
try {
|
|
5323
|
+
const hasConfig = await userConfigManager.hasConfig();
|
|
5324
|
+
if (!hasConfig) {
|
|
5325
|
+
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
5326
|
+
return;
|
|
5327
|
+
}
|
|
5328
|
+
const answers = await inquirer10.prompt([
|
|
5329
|
+
{
|
|
5330
|
+
type: "confirm",
|
|
5331
|
+
name: "confirm",
|
|
5332
|
+
message: "\u786E\u5B9A\u8981\u5220\u9664 GitLab \u914D\u7F6E\u5417?",
|
|
5333
|
+
default: false
|
|
5334
|
+
}
|
|
5335
|
+
]);
|
|
5336
|
+
if (!answers.confirm) {
|
|
5337
|
+
logger.info("\u5DF2\u53D6\u6D88");
|
|
5338
|
+
return;
|
|
5339
|
+
}
|
|
5340
|
+
await userConfigManager.removeConfig();
|
|
5341
|
+
logger.success("\u914D\u7F6E\u5DF2\u5220\u9664");
|
|
5342
|
+
} catch (error) {
|
|
5343
|
+
logger.error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
5344
|
+
if (process.env.DEBUG) {
|
|
5345
|
+
console.error(error);
|
|
5346
|
+
}
|
|
5347
|
+
process.exit(1);
|
|
5348
|
+
}
|
|
5349
|
+
});
|
|
5350
|
+
var validateTokenCommand = new Command14("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
|
|
5351
|
+
try {
|
|
5352
|
+
logger.header("\u9A8C\u8BC1 GitLab Token");
|
|
5353
|
+
logger.newLine();
|
|
5354
|
+
const config = await userConfigManager.getGitLabConfig();
|
|
5355
|
+
if (!config) {
|
|
5356
|
+
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
5357
|
+
logger.info("\u8BF7\u4F7F\u7528 'team-cli config set-token' \u8FDB\u884C\u914D\u7F6E");
|
|
5358
|
+
process.exit(1);
|
|
5359
|
+
}
|
|
5360
|
+
logger.info("\u6B63\u5728\u9A8C\u8BC1...");
|
|
5361
|
+
const gitlabAPI = new GitLabAPI(config);
|
|
5362
|
+
const isValid = await gitlabAPI.authenticate();
|
|
5363
|
+
if (isValid) {
|
|
5364
|
+
logger.success("Token \u6709\u6548!");
|
|
5365
|
+
logger.newLine();
|
|
5366
|
+
logger.info(`GitLab URL: ${chalk2.cyan(config.baseUrl)}`);
|
|
5367
|
+
} else {
|
|
5368
|
+
logger.error("Token \u9A8C\u8BC1\u5931\u8D25");
|
|
5369
|
+
logger.info("\u8BF7\u68C0\u67E5 Token \u662F\u5426\u6B63\u786E\u6216\u5DF2\u8FC7\u671F");
|
|
5370
|
+
process.exit(1);
|
|
5371
|
+
}
|
|
5372
|
+
} catch (error) {
|
|
5373
|
+
logger.error(`\u9A8C\u8BC1\u5931\u8D25: ${error.message}`);
|
|
5374
|
+
if (process.env.DEBUG) {
|
|
5375
|
+
console.error(error);
|
|
5376
|
+
}
|
|
5377
|
+
process.exit(1);
|
|
5378
|
+
}
|
|
5379
|
+
});
|
|
5380
|
+
var configCommand = new Command14("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
|
|
5381
|
+
|
|
5382
|
+
// src/commands/diff.ts
|
|
5383
|
+
init_esm_shims();
|
|
5384
|
+
import { Command as Command15 } from "commander";
|
|
5385
|
+
import chalk3 from "chalk";
|
|
5386
|
+
init_logger();
|
|
5387
|
+
init_utils();
|
|
5388
|
+
init_user_config();
|
|
5389
|
+
init_gitlab_api();
|
|
5390
|
+
var diffCommand = new Command15("diff").description("\u5BF9\u6BD4\u672C\u5730\u4E0E\u8FDC\u7A0B\u6A21\u677F\u5DEE\u5F02").option("-f, --frontend", "\u5BF9\u6BD4\u524D\u7AEF\u6A21\u677F").option("-b, --backend", "\u5BF9\u6BD4\u540E\u7AEF\u6A21\u677F").option("-t, --tag <tag>", "\u6307\u5B9A\u8FDC\u7A0B\u6807\u7B7E").option("-B, --branch <branch>", "\u6307\u5B9A\u8FDC\u7A0B\u5206\u652F").option("-o, --output <format>", "\u8F93\u51FA\u683C\u5F0F (table|json|diff)", "table").action(async (options) => {
|
|
5391
|
+
try {
|
|
5392
|
+
logger.header("\u6A21\u677F\u7248\u672C\u5BF9\u6BD4");
|
|
5393
|
+
logger.newLine();
|
|
5394
|
+
const hasConfig = await FileUtils.exists("TECH_STACK.md");
|
|
5395
|
+
if (!hasConfig) {
|
|
5396
|
+
logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
|
|
5397
|
+
logger.info("\u8BF7\u5148\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
5398
|
+
process.exit(1);
|
|
5399
|
+
}
|
|
5400
|
+
const projectPath = ".";
|
|
5401
|
+
const checkAll = !options.frontend && !options.backend;
|
|
5402
|
+
const checkFrontend = options.frontend || checkAll;
|
|
5403
|
+
const checkBackend = options.backend || checkAll;
|
|
5404
|
+
const config = await readTemplateConfig(projectPath);
|
|
5405
|
+
if (!config) {
|
|
5406
|
+
logger.error("\u672A\u627E\u5230\u6A21\u677F\u914D\u7F6E");
|
|
5407
|
+
process.exit(1);
|
|
5408
|
+
}
|
|
5409
|
+
const gitlabConfig = await userConfigManager.getGitLabConfig();
|
|
5410
|
+
if (!gitlabConfig) {
|
|
5411
|
+
logger.warn("\u672A\u914D\u7F6E GitLab Token");
|
|
5412
|
+
logger.info("\u8BF7\u4F7F\u7528 'team-cli config set-token' \u8FDB\u884C\u914D\u7F6E");
|
|
5413
|
+
logger.info("\u5C06\u4F7F\u7528\u57FA\u672C git \u547D\u4EE4\u8FDB\u884C\u5BF9\u6BD4");
|
|
5414
|
+
}
|
|
5415
|
+
const results = [];
|
|
5416
|
+
if (checkFrontend && config.frontend) {
|
|
5417
|
+
const result = await compareTemplate(
|
|
5418
|
+
projectPath,
|
|
5419
|
+
"frontend",
|
|
5420
|
+
config.frontend,
|
|
5421
|
+
options.tag,
|
|
5422
|
+
options.branch,
|
|
5423
|
+
gitlabConfig || void 0
|
|
5424
|
+
);
|
|
5425
|
+
if (result) {
|
|
5426
|
+
results.push(result);
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
if (checkBackend && config.backend) {
|
|
5430
|
+
const result = await compareTemplate(
|
|
5431
|
+
projectPath,
|
|
5432
|
+
"backend",
|
|
5433
|
+
config.backend,
|
|
5434
|
+
options.tag,
|
|
5435
|
+
options.branch,
|
|
5436
|
+
gitlabConfig || void 0
|
|
5437
|
+
);
|
|
5438
|
+
if (result) {
|
|
5439
|
+
results.push(result);
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
if (results.length === 0) {
|
|
5443
|
+
logger.info("\u6CA1\u6709\u53EF\u5BF9\u6BD4\u7684\u6A21\u677F");
|
|
5444
|
+
return;
|
|
5445
|
+
}
|
|
5446
|
+
switch (options.output) {
|
|
5447
|
+
case "json":
|
|
5448
|
+
outputJson(results);
|
|
5449
|
+
break;
|
|
5450
|
+
case "diff":
|
|
5451
|
+
outputDiff(results);
|
|
5452
|
+
break;
|
|
5453
|
+
case "table":
|
|
5454
|
+
default:
|
|
5455
|
+
outputTable(results);
|
|
5456
|
+
break;
|
|
5457
|
+
}
|
|
5458
|
+
} catch (error) {
|
|
5459
|
+
logger.error(`\u5BF9\u6BD4\u5931\u8D25: ${error.message}`);
|
|
5460
|
+
if (process.env.DEBUG) {
|
|
5461
|
+
console.error(error);
|
|
5462
|
+
}
|
|
5463
|
+
process.exit(1);
|
|
5464
|
+
}
|
|
5465
|
+
});
|
|
5466
|
+
async function compareTemplate(projectPath, type, localConfig, remoteTag, remoteBranch, gitlabConfig) {
|
|
5467
|
+
try {
|
|
5468
|
+
logger.step(`\u5BF9\u6BD4${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
|
|
5469
|
+
const repository = localConfig.repository;
|
|
5470
|
+
const localCommit = localConfig.commit;
|
|
5471
|
+
const localTag = localConfig.tag;
|
|
5472
|
+
const localBranch = localConfig.branch;
|
|
5473
|
+
let remoteCommit = null;
|
|
5474
|
+
let remoteTagName;
|
|
5475
|
+
let remoteBranchName;
|
|
5476
|
+
if (gitlabConfig) {
|
|
5477
|
+
const gitlabAPI = new GitLabAPI(gitlabConfig);
|
|
5478
|
+
const projectPathEncoded = GitLabAPI.parseProjectPath(repository);
|
|
5479
|
+
if (remoteTag) {
|
|
5480
|
+
remoteCommit = await gitlabAPI.getTagCommit(projectPathEncoded, remoteTag);
|
|
5481
|
+
remoteTagName = remoteTag;
|
|
5482
|
+
} else if (remoteBranch) {
|
|
5483
|
+
remoteCommit = await gitlabAPI.getBranchCommit(projectPathEncoded, remoteBranch);
|
|
5484
|
+
remoteBranchName = remoteBranch;
|
|
5485
|
+
} else {
|
|
5486
|
+
const latest = await gitlabAPI.getLatestVersion(projectPathEncoded);
|
|
5487
|
+
if (latest) {
|
|
5488
|
+
remoteCommit = latest.commit;
|
|
5489
|
+
if (latest.type === "tag") {
|
|
5490
|
+
remoteTagName = latest.name;
|
|
5491
|
+
} else {
|
|
5492
|
+
remoteBranchName = latest.name;
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
if (remoteCommit && localCommit && remoteCommit !== localCommit) {
|
|
5497
|
+
const from = localTag || localBranch || localCommit;
|
|
5498
|
+
const to = remoteTagName || remoteBranchName || remoteCommit;
|
|
5499
|
+
const diff = await gitlabAPI.compareVersions(projectPathEncoded, from, to);
|
|
5500
|
+
return {
|
|
5501
|
+
type,
|
|
5502
|
+
local: {
|
|
5503
|
+
commit: localCommit,
|
|
5504
|
+
tag: localTag,
|
|
5505
|
+
branch: localBranch
|
|
5506
|
+
},
|
|
5507
|
+
remote: {
|
|
5508
|
+
commit: remoteCommit,
|
|
5509
|
+
tag: remoteTagName,
|
|
5510
|
+
branch: remoteBranchName
|
|
5511
|
+
},
|
|
5512
|
+
diff: diff || void 0
|
|
5513
|
+
};
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
if (!remoteCommit) {
|
|
5517
|
+
const { execa: execa5 } = await import("execa");
|
|
5518
|
+
const ref = remoteTag || remoteBranch || "HEAD";
|
|
5519
|
+
const { stdout } = await execa5("git", ["ls-remote", repository, ref], {
|
|
5520
|
+
stdio: "pipe"
|
|
5521
|
+
});
|
|
5522
|
+
remoteCommit = stdout.split(" ")[0];
|
|
5523
|
+
remoteTagName = remoteTag;
|
|
5524
|
+
remoteBranchName = remoteBranch;
|
|
5525
|
+
}
|
|
5526
|
+
const needsUpdate = localCommit !== remoteCommit;
|
|
5527
|
+
logger.info(
|
|
5528
|
+
`${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F: ${needsUpdate ? chalk3.yellow("\u6709\u66F4\u65B0") : chalk3.green("\u5DF2\u662F\u6700\u65B0")}`
|
|
5529
|
+
);
|
|
5530
|
+
return {
|
|
5531
|
+
type,
|
|
5532
|
+
local: {
|
|
5533
|
+
commit: localCommit,
|
|
5534
|
+
tag: localTag,
|
|
5535
|
+
branch: localBranch
|
|
5536
|
+
},
|
|
5537
|
+
remote: {
|
|
5538
|
+
commit: remoteCommit || void 0,
|
|
5539
|
+
tag: remoteTagName,
|
|
5540
|
+
branch: remoteBranchName
|
|
5541
|
+
}
|
|
5542
|
+
};
|
|
5543
|
+
} catch (error) {
|
|
5544
|
+
logger.error(`\u5BF9\u6BD4${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F\u5931\u8D25: ${error}`);
|
|
5545
|
+
return null;
|
|
5546
|
+
}
|
|
5547
|
+
}
|
|
5548
|
+
function outputTable(results) {
|
|
5549
|
+
logger.newLine();
|
|
5550
|
+
for (const result of results) {
|
|
5551
|
+
const typeName = result.type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF";
|
|
5552
|
+
console.log(chalk3.bold(`${typeName}\u6A21\u677F:`));
|
|
5553
|
+
console.log("");
|
|
5554
|
+
const t = new Table({
|
|
5555
|
+
head: [chalk3.cyan("\u9879\u76EE"), chalk3.cyan("\u7248\u672C")],
|
|
5556
|
+
colWidths: [20, 50]
|
|
5557
|
+
});
|
|
5558
|
+
const localVersion = result.local.tag || result.local.branch || result.local.commit?.substring(0, 8) || "-";
|
|
5559
|
+
const localInfo = `Commit: ${result.local.commit?.substring(0, 8) || "-"}${result.local.tag ? `
|
|
5560
|
+
Tag: ${result.local.tag}` : ""}${result.local.branch ? `
|
|
5561
|
+
Branch: ${result.local.branch}` : ""}`;
|
|
5562
|
+
t.push(["\u672C\u5730", localInfo]);
|
|
5563
|
+
const remoteVersion = result.remote.tag || result.remote.branch || result.remote.commit?.substring(0, 8) || "-";
|
|
5564
|
+
const remoteInfo = `Commit: ${result.remote.commit?.substring(0, 8) || "-"}${result.remote.tag ? `
|
|
5565
|
+
Tag: ${result.remote.tag}` : ""}${result.remote.branch ? `
|
|
5566
|
+
Branch: ${result.remote.branch}` : ""}`;
|
|
5567
|
+
t.push(["\u8FDC\u7A0B", remoteInfo]);
|
|
5568
|
+
console.log(t.toString());
|
|
5569
|
+
if (result.diff && result.diff.commits.length > 0) {
|
|
5570
|
+
console.log("");
|
|
5571
|
+
console.log(chalk3.bold("\u65B0\u589E\u63D0\u4EA4:"));
|
|
5572
|
+
const commitsTable = new Table({
|
|
5573
|
+
head: [chalk3.cyan("Commit"), chalk3.cyan("\u4F5C\u8005"), chalk3.cyan("\u65F6\u95F4"), chalk3.cyan("\u63CF\u8FF0")],
|
|
5574
|
+
colWidths: [10, 15, 20, 50],
|
|
5575
|
+
wordWrap: true
|
|
5576
|
+
});
|
|
5577
|
+
for (const commit of result.diff.commits.slice(0, 10)) {
|
|
5578
|
+
commitsTable.push([
|
|
5579
|
+
commit.short_id,
|
|
5580
|
+
commit.author_name,
|
|
5581
|
+
new Date(commit.created_at).toLocaleDateString("zh-CN"),
|
|
5582
|
+
commit.title
|
|
5583
|
+
]);
|
|
5584
|
+
}
|
|
5585
|
+
console.log(commitsTable.toString());
|
|
5586
|
+
if (result.diff.commits.length > 10) {
|
|
5587
|
+
console.log(chalk3.gray(`... \u8FD8\u6709 ${result.diff.commits.length - 10} \u4E2A\u63D0\u4EA4`));
|
|
5588
|
+
}
|
|
5589
|
+
const files = result.diff.diffs;
|
|
5590
|
+
const added = files.filter((f) => f.new_file).length;
|
|
5591
|
+
const deleted = files.filter((f) => f.deleted_file).length;
|
|
5592
|
+
const modified = files.length - added - deleted;
|
|
5593
|
+
console.log("");
|
|
5594
|
+
console.log(chalk3.bold("\u6587\u4EF6\u53D8\u66F4:"));
|
|
5595
|
+
console.log(` ${chalk3.green("+")} \u65B0\u589E: ${added}`);
|
|
5596
|
+
console.log(` ${chalk3.red("-")} \u5220\u9664: ${deleted}`);
|
|
5597
|
+
console.log(` ${chalk3.yellow("~")} \u4FEE\u6539: ${modified}`);
|
|
5598
|
+
}
|
|
5599
|
+
console.log("");
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
function outputJson(results) {
|
|
5603
|
+
console.log(JSON.stringify(results, null, 2));
|
|
5604
|
+
}
|
|
5605
|
+
function outputDiff(results) {
|
|
5606
|
+
logger.newLine();
|
|
5607
|
+
for (const result of results) {
|
|
5608
|
+
const typeName = result.type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF";
|
|
5609
|
+
console.log(chalk3.bold(`${typeName}\u6A21\u677F:`));
|
|
5610
|
+
console.log("");
|
|
5611
|
+
const from = result.local.tag || result.local.branch || result.local.commit?.substring(0, 8) || "unknown";
|
|
5612
|
+
const to = result.remote.tag || result.remote.branch || result.remote.commit?.substring(0, 8) || "unknown";
|
|
5613
|
+
console.log(`\u672C\u5730\u7248\u672C: ${chalk3.cyan(from)}`);
|
|
5614
|
+
console.log(`\u8FDC\u7A0B\u7248\u672C: ${chalk3.cyan(to)}`);
|
|
5615
|
+
if (result.diff && result.diff.commits.length > 0) {
|
|
5616
|
+
console.log("");
|
|
5617
|
+
console.log(chalk3.bold("\u63D0\u4EA4\u5386\u53F2:"));
|
|
5618
|
+
for (const commit of result.diff.commits) {
|
|
5619
|
+
console.log("");
|
|
5620
|
+
console.log(chalk3.yellow(`commit ${commit.id}`));
|
|
5621
|
+
console.log(`Author: ${commit.author_name} <${commit.author_email}>`);
|
|
5622
|
+
console.log(`Date: ${new Date(commit.created_at).toLocaleString("zh-CN")}`);
|
|
5623
|
+
console.log("");
|
|
5624
|
+
console.log(` ${commit.title}`);
|
|
5625
|
+
if (commit.message) {
|
|
5626
|
+
console.log(` ${commit.message.split("\n").join("\n ")}`);
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
console.log("");
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
var Table = class {
|
|
5634
|
+
options;
|
|
5635
|
+
rows = [];
|
|
5636
|
+
constructor(options) {
|
|
5637
|
+
this.options = options;
|
|
5638
|
+
}
|
|
5639
|
+
push(row) {
|
|
5640
|
+
this.rows.push(row);
|
|
5641
|
+
}
|
|
5642
|
+
toString() {
|
|
5643
|
+
if (this.rows.length === 0) return "";
|
|
5644
|
+
let result = "";
|
|
5645
|
+
const colWidths = this.options.colWidths || [];
|
|
5646
|
+
if (this.options.head) {
|
|
5647
|
+
result += "| ";
|
|
5648
|
+
for (let i = 0; i < this.options.head.length; i++) {
|
|
5649
|
+
const width = colWidths[i] || 20;
|
|
5650
|
+
const cell = this.options.head[i] || "";
|
|
5651
|
+
result += cell.padEnd(width) + " | ";
|
|
5652
|
+
}
|
|
5653
|
+
result += "\n";
|
|
5654
|
+
result += "|";
|
|
5655
|
+
for (let i = 0; i < this.options.head.length; i++) {
|
|
5656
|
+
const width = colWidths[i] || 20;
|
|
5657
|
+
result += "-".repeat(width + 2) + "|";
|
|
5658
|
+
}
|
|
5659
|
+
result += "\n";
|
|
5660
|
+
}
|
|
5661
|
+
for (const row of this.rows) {
|
|
5662
|
+
result += "| ";
|
|
5663
|
+
for (let i = 0; i < row.length; i++) {
|
|
5664
|
+
const width = colWidths[i] || 20;
|
|
5665
|
+
const cell = String(row[i] || "");
|
|
5666
|
+
const displayCell = this.options.wordWrap && cell.length > width ? cell.substring(0, width - 3) + "..." : cell;
|
|
5667
|
+
result += displayCell.padEnd(width) + " | ";
|
|
5668
|
+
}
|
|
5669
|
+
result += "\n";
|
|
5670
|
+
}
|
|
5671
|
+
return result;
|
|
5672
|
+
}
|
|
5673
|
+
};
|
|
5674
|
+
|
|
4250
5675
|
// src/index.ts
|
|
4251
|
-
var program = new
|
|
4252
|
-
program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version("2.
|
|
5676
|
+
var program = new Command16();
|
|
5677
|
+
program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version("2.1.1");
|
|
4253
5678
|
program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
|
|
4254
5679
|
program.addCommand(initCommand);
|
|
4255
5680
|
program.addCommand(splitPrdCommand);
|
|
@@ -4264,6 +5689,9 @@ program.addCommand(detectDepsCommand);
|
|
|
4264
5689
|
program.addCommand(syncMemoryCommand);
|
|
4265
5690
|
program.addCommand(checkApiCommand);
|
|
4266
5691
|
program.addCommand(logsCommand);
|
|
5692
|
+
program.addCommand(updateCommand);
|
|
5693
|
+
program.addCommand(configCommand);
|
|
5694
|
+
program.addCommand(diffCommand);
|
|
4267
5695
|
program.action(() => {
|
|
4268
5696
|
showHelp();
|
|
4269
5697
|
});
|
|
@@ -4271,7 +5699,7 @@ function showHelp() {
|
|
|
4271
5699
|
console.log("");
|
|
4272
5700
|
logger.header("team-cli - AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6");
|
|
4273
5701
|
console.log("");
|
|
4274
|
-
console.log(
|
|
5702
|
+
console.log(chalk4.bold("\u4F7F\u7528\u65B9\u6CD5:"));
|
|
4275
5703
|
console.log(" team-cli init [project-name] \u521D\u59CB\u5316\u65B0\u9879\u76EE");
|
|
4276
5704
|
console.log(" team-cli split-prd <prd-folder> \u5C06 PRD \u62C6\u5206\u6210\u591A\u4E2A specs");
|
|
4277
5705
|
console.log(" team-cli breakdown [spec-file] \u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos");
|
|
@@ -4285,21 +5713,24 @@ function showHelp() {
|
|
|
4285
5713
|
console.log(" team-cli status \u67E5\u770B\u9879\u76EE\u72B6\u6001");
|
|
4286
5714
|
console.log(" team-cli lint \u4EE3\u7801\u8D28\u91CF\u68C0\u67E5 (\u524D\u7AEF+\u540E\u7AEF)");
|
|
4287
5715
|
console.log(" team-cli logs [date] \u67E5\u770B\u4F1A\u8BDD\u65E5\u5FD7");
|
|
5716
|
+
console.log(" team-cli update \u68C0\u67E5\u5E76\u66F4\u65B0\u6A21\u677F\u7248\u672C");
|
|
5717
|
+
console.log(" team-cli config \u7BA1\u7406 GitLab \u914D\u7F6E");
|
|
5718
|
+
console.log(" team-cli diff \u5BF9\u6BD4\u672C\u5730\u4E0E\u8FDC\u7A0B\u6A21\u677F\u5DEE\u5F02");
|
|
4288
5719
|
console.log(" team-cli --help \u663E\u793A\u5E2E\u52A9\u4FE1\u606F");
|
|
4289
5720
|
console.log("");
|
|
4290
|
-
console.log(
|
|
5721
|
+
console.log(chalk4.bold("\u793A\u4F8B:"));
|
|
4291
5722
|
console.log(" team-cli init my-project");
|
|
4292
5723
|
console.log(" cd my-project");
|
|
4293
5724
|
console.log(" team-cli add-feature payment-system");
|
|
4294
5725
|
console.log(" team-cli breakdown docs/specs/xxx.md");
|
|
4295
5726
|
console.log(" team-cli dev");
|
|
4296
5727
|
console.log("");
|
|
4297
|
-
console.log(
|
|
5728
|
+
console.log(chalk4.bold("\u5F00\u53D1\u6D41\u7A0B:"));
|
|
4298
5729
|
console.log(" 1. PRD \u2192 specs (split-prd)");
|
|
4299
5730
|
console.log(" 2. spec \u2192 milestones + todos (breakdown)");
|
|
4300
5731
|
console.log(" 3. \u9009\u62E9 milestone/todo \u2192 \u5B9E\u73B0 (dev)");
|
|
4301
5732
|
console.log("");
|
|
4302
|
-
console.log(
|
|
5733
|
+
console.log(chalk4.bold("\u8FED\u4EE3\u6D41\u7A0B:"));
|
|
4303
5734
|
console.log(" team-cli add-feature <name> # \u6DFB\u52A0\u65B0\u529F\u80FD");
|
|
4304
5735
|
console.log(" team-cli detect-deps [spec] # \u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
4305
5736
|
console.log(" team-cli sync-memory # \u540C\u6B65 AI_MEMORY");
|
|
@@ -4308,7 +5739,14 @@ function showHelp() {
|
|
|
4308
5739
|
console.log(" team-cli hotfix # \u7D27\u6025\u4FEE\u590D");
|
|
4309
5740
|
console.log(" team-cli status # \u67E5\u770B\u9879\u76EE\u72B6\u6001");
|
|
4310
5741
|
console.log("");
|
|
4311
|
-
console.log(
|
|
5742
|
+
console.log(chalk4.bold("\u6A21\u677F\u7BA1\u7406:"));
|
|
5743
|
+
console.log(" team-cli config set-token # \u8BBE\u7F6E GitLab Access Token");
|
|
5744
|
+
console.log(" team-cli config show # \u663E\u793A\u5F53\u524D\u914D\u7F6E");
|
|
5745
|
+
console.log(" team-cli init --tag v1.0.0 # \u4F7F\u7528\u6307\u5B9A tag \u521D\u59CB\u5316");
|
|
5746
|
+
console.log(" team-cli update --tag v1.1.0 # \u66F4\u65B0\u5230\u6307\u5B9A\u7248\u672C");
|
|
5747
|
+
console.log(" team-cli diff # \u5BF9\u6BD4\u6A21\u677F\u5DEE\u5F02");
|
|
5748
|
+
console.log("");
|
|
5749
|
+
console.log(chalk4.gray("\u66F4\u591A\u4FE1\u606F: https://github.com/yungu/team-cli"));
|
|
4312
5750
|
console.log("");
|
|
4313
5751
|
}
|
|
4314
5752
|
process.on("uncaughtException", (error) => {
|