yg-team-cli 2.5.8 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/cli.js +110 -121
- package/dist/cli.js.map +1 -1
- package/dist/index.js +110 -121
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -422,7 +422,89 @@ var init_utils = __esm({
|
|
|
422
422
|
return spec;
|
|
423
423
|
}
|
|
424
424
|
/**
|
|
425
|
-
*
|
|
425
|
+
* 解析里程碑信息
|
|
426
|
+
*/
|
|
427
|
+
static parseMilestones(content) {
|
|
428
|
+
const milestones = [];
|
|
429
|
+
const lines = content.split("\n");
|
|
430
|
+
let currentMilestone = null;
|
|
431
|
+
let inMilestone = false;
|
|
432
|
+
for (const line of lines) {
|
|
433
|
+
if (line.match(/^###\s+Milestone\s+\d+:/)) {
|
|
434
|
+
if (currentMilestone) {
|
|
435
|
+
milestones.push(currentMilestone);
|
|
436
|
+
}
|
|
437
|
+
const title = line.replace(/^###\s+/, "").trim();
|
|
438
|
+
currentMilestone = { title, todos: [], completedCount: 0 };
|
|
439
|
+
inMilestone = true;
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (inMilestone && currentMilestone) {
|
|
443
|
+
if (line.match(/^###\s+Milestone/)) {
|
|
444
|
+
milestones.push(currentMilestone);
|
|
445
|
+
currentMilestone = null;
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const todoMatch = line.match(/^-\s+\[([ xX])\]\s*(.+)/);
|
|
449
|
+
if (todoMatch) {
|
|
450
|
+
const isCompleted = todoMatch[1].toLowerCase() === "x";
|
|
451
|
+
if (isCompleted) {
|
|
452
|
+
currentMilestone.completedCount++;
|
|
453
|
+
}
|
|
454
|
+
currentMilestone.todos.push(todoMatch[2].trim());
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (currentMilestone) {
|
|
459
|
+
milestones.push(currentMilestone);
|
|
460
|
+
}
|
|
461
|
+
return milestones;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* 解析 spec 状态(基于进度动态推导)
|
|
465
|
+
*/
|
|
466
|
+
static parseSpecStatus(content) {
|
|
467
|
+
const milestones = this.parseMilestones(content);
|
|
468
|
+
if (milestones.length > 0) {
|
|
469
|
+
const totalTodos = milestones.reduce((sum, m) => sum + m.todos.length, 0);
|
|
470
|
+
const completedTodos = milestones.reduce((sum, m) => sum + m.completedCount, 0);
|
|
471
|
+
if (totalTodos > 0) {
|
|
472
|
+
if (completedTodos === totalTodos) return "\u5DF2\u5B8C\u6210";
|
|
473
|
+
if (completedTodos > 0) return "\u8FDB\u884C\u4E2D";
|
|
474
|
+
}
|
|
475
|
+
return "\u5DF2\u62C6\u5206";
|
|
476
|
+
}
|
|
477
|
+
const statusMatch = content.match(/状态.*[::]\s*(.+)/);
|
|
478
|
+
if (statusMatch) {
|
|
479
|
+
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
480
|
+
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u5DF2\u5B8C\u6210";
|
|
481
|
+
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u8FDB\u884C\u4E2D";
|
|
482
|
+
if (status.includes("\u5DF2\u62C6\u5206")) return "\u5DF2\u62C6\u5206";
|
|
483
|
+
}
|
|
484
|
+
return "\u672A\u5F00\u59CB";
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* 获取进度字符串
|
|
488
|
+
*/
|
|
489
|
+
static getProgress(content) {
|
|
490
|
+
const milestones = this.parseMilestones(content);
|
|
491
|
+
if (milestones.length === 0) return "-";
|
|
492
|
+
const totalTodos = milestones.reduce((sum, m) => sum + m.todos.length, 0);
|
|
493
|
+
const completedTodos = milestones.reduce((sum, m) => sum + m.completedCount, 0);
|
|
494
|
+
if (totalTodos === 0) return "-";
|
|
495
|
+
return `${completedTodos}/${totalTodos}`;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* 获取带图标的状态
|
|
499
|
+
*/
|
|
500
|
+
static getStatusWithIcon(status) {
|
|
501
|
+
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2713 \u5DF2\u5B8C\u6210";
|
|
502
|
+
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
503
|
+
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
504
|
+
return "\u25CB \u672A\u5F00\u59CB";
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* 获取 spec 状态 (原始版本兼容)
|
|
426
508
|
*/
|
|
427
509
|
static async getSpecStatus(file) {
|
|
428
510
|
try {
|
|
@@ -2786,7 +2868,7 @@ async function selectSpec() {
|
|
|
2786
2868
|
for (let i = 0; i < specFiles.length; i++) {
|
|
2787
2869
|
const file = path9.join(specDir, specFiles[i]);
|
|
2788
2870
|
const spec = await FileUtils.read(file);
|
|
2789
|
-
const status = parseSpecStatus(spec);
|
|
2871
|
+
const status = SpecUtils.parseSpecStatus(spec);
|
|
2790
2872
|
const dependencies = parseDependencies(spec);
|
|
2791
2873
|
specs.push({
|
|
2792
2874
|
file,
|
|
@@ -2828,7 +2910,7 @@ async function selectMilestone(specFile) {
|
|
|
2828
2910
|
logger.step("\u6B65\u9AA4 2/3: \u89E3\u6790 milestones...");
|
|
2829
2911
|
logger.newLine();
|
|
2830
2912
|
const specContent = await FileUtils.read(specFile);
|
|
2831
|
-
const milestones = parseMilestones(specContent);
|
|
2913
|
+
const milestones = SpecUtils.parseMilestones(specContent);
|
|
2832
2914
|
if (milestones.length === 0) {
|
|
2833
2915
|
logger.info("\u8BE5 spec \u5C1A\u672A\u62C6\u5206 milestones");
|
|
2834
2916
|
const { breakdownNow } = await inquirer3.prompt([
|
|
@@ -2881,7 +2963,9 @@ async function selectTodo(specFile, milestone) {
|
|
|
2881
2963
|
logger.step("\u6B65\u9AA4 3/3: \u9009\u62E9 todo \u4EFB\u52A1...");
|
|
2882
2964
|
logger.newLine();
|
|
2883
2965
|
const specContent = await FileUtils.read(specFile);
|
|
2884
|
-
const
|
|
2966
|
+
const milestones = SpecUtils.parseMilestones(specContent);
|
|
2967
|
+
const targetMilestone = milestones.find((m) => m.title === milestone);
|
|
2968
|
+
const todos = targetMilestone ? targetMilestone.todos : [];
|
|
2885
2969
|
if (todos.length === 0) {
|
|
2886
2970
|
logger.warn("\u8BE5 milestone \u6CA1\u6709 todo \u4EFB\u52A1");
|
|
2887
2971
|
const { implementAll } = await inquirer3.prompt([
|
|
@@ -2955,16 +3039,6 @@ async function executeDevelopment(specFile, milestone, todo) {
|
|
|
2955
3039
|
logger.step("2. \u8FD0\u884C 'team-cli dev' \u7EE7\u7EED\u4E0B\u4E00\u4E2A\u4EFB\u52A1");
|
|
2956
3040
|
logger.newLine();
|
|
2957
3041
|
}
|
|
2958
|
-
function parseSpecStatus(spec) {
|
|
2959
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
2960
|
-
if (statusMatch) {
|
|
2961
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
2962
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u5DF2\u5B8C\u6210";
|
|
2963
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u8FDB\u884C\u4E2D";
|
|
2964
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u5DF2\u62C6\u5206";
|
|
2965
|
-
}
|
|
2966
|
-
return "\u672A\u5F00\u59CB";
|
|
2967
|
-
}
|
|
2968
3042
|
function parseDependencies(spec) {
|
|
2969
3043
|
const deps = [];
|
|
2970
3044
|
let inDepsSection = false;
|
|
@@ -2987,63 +3061,6 @@ function parseDependencies(spec) {
|
|
|
2987
3061
|
}
|
|
2988
3062
|
return deps;
|
|
2989
3063
|
}
|
|
2990
|
-
function parseMilestones(spec) {
|
|
2991
|
-
const milestones = [];
|
|
2992
|
-
const lines = spec.split("\n");
|
|
2993
|
-
let currentMilestone = null;
|
|
2994
|
-
let inMilestone = false;
|
|
2995
|
-
for (const line of lines) {
|
|
2996
|
-
if (line.match(/^###\s+Milestone\s+\d+:/)) {
|
|
2997
|
-
if (currentMilestone) {
|
|
2998
|
-
milestones.push(currentMilestone);
|
|
2999
|
-
}
|
|
3000
|
-
const title = line.replace(/^###\s+/, "").trim();
|
|
3001
|
-
currentMilestone = { title, todos: [], completedCount: 0 };
|
|
3002
|
-
inMilestone = true;
|
|
3003
|
-
continue;
|
|
3004
|
-
}
|
|
3005
|
-
if (inMilestone && currentMilestone) {
|
|
3006
|
-
if (line.match(/^###\s+Milestone/)) {
|
|
3007
|
-
milestones.push(currentMilestone);
|
|
3008
|
-
currentMilestone = null;
|
|
3009
|
-
continue;
|
|
3010
|
-
}
|
|
3011
|
-
const todoMatch = line.match(/^-\s+\[([ xX])\]\s*(.+)/);
|
|
3012
|
-
if (todoMatch) {
|
|
3013
|
-
const isCompleted = todoMatch[1].toLowerCase() === "x";
|
|
3014
|
-
if (isCompleted) {
|
|
3015
|
-
currentMilestone.completedCount++;
|
|
3016
|
-
}
|
|
3017
|
-
currentMilestone.todos.push(todoMatch[2].trim());
|
|
3018
|
-
}
|
|
3019
|
-
}
|
|
3020
|
-
}
|
|
3021
|
-
if (currentMilestone) {
|
|
3022
|
-
milestones.push(currentMilestone);
|
|
3023
|
-
}
|
|
3024
|
-
return milestones;
|
|
3025
|
-
}
|
|
3026
|
-
function parseTodos(spec, milestoneTitle) {
|
|
3027
|
-
const lines = spec.split("\n");
|
|
3028
|
-
const todos = [];
|
|
3029
|
-
let inTargetMilestone = false;
|
|
3030
|
-
for (const line of lines) {
|
|
3031
|
-
if (line.includes(milestoneTitle)) {
|
|
3032
|
-
inTargetMilestone = true;
|
|
3033
|
-
continue;
|
|
3034
|
-
}
|
|
3035
|
-
if (inTargetMilestone) {
|
|
3036
|
-
if (line.match(/^###\s+Milestone/)) {
|
|
3037
|
-
break;
|
|
3038
|
-
}
|
|
3039
|
-
const todoMatch = line.match(/^-\s+\[[ x ]\]\s*(.+)/);
|
|
3040
|
-
if (todoMatch) {
|
|
3041
|
-
todos.push(todoMatch[1].trim());
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
return todos;
|
|
3046
|
-
}
|
|
3047
3064
|
function topologicalSort(specs) {
|
|
3048
3065
|
const sorted = [];
|
|
3049
3066
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -3201,7 +3218,19 @@ async function askAndUpdateSpecStatus(specFile, milestone, todo) {
|
|
|
3201
3218
|
logger.warn("\u672A\u627E\u5230\u5BF9\u5E94\u7684 todo \u9879");
|
|
3202
3219
|
return;
|
|
3203
3220
|
}
|
|
3204
|
-
|
|
3221
|
+
const newContent = lines.join("\n");
|
|
3222
|
+
const computedStatus = SpecUtils.parseSpecStatus(newContent);
|
|
3223
|
+
let finalLines = lines;
|
|
3224
|
+
const statusLineIndex = finalLines.findIndex((l) => l.match(/状态.*[::]/));
|
|
3225
|
+
if (statusLineIndex !== -1) {
|
|
3226
|
+
const oldLine = finalLines[statusLineIndex];
|
|
3227
|
+
const newLine = oldLine.replace(/([::]\s*)(.+)/, `$1**${computedStatus}**`);
|
|
3228
|
+
if (oldLine !== newLine) {
|
|
3229
|
+
finalLines[statusLineIndex] = newLine;
|
|
3230
|
+
logger.info(`\u81EA\u52A8\u540C\u6B65 Spec \u5934\u90E8\u72B6\u6001\u4E3A: ${computedStatus}`);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
await FileUtils.write(specFile, finalLines.join("\n"));
|
|
3205
3234
|
logger.success("Spec \u6587\u4EF6\u5DF2\u66F4\u65B0");
|
|
3206
3235
|
} catch (error) {
|
|
3207
3236
|
logger.warn(`\u66F4\u65B0 spec \u6587\u4EF6\u5931\u8D25: ${error}`);
|
|
@@ -4581,11 +4610,12 @@ async function displayFeatureInventory() {
|
|
|
4581
4610
|
for (const file of specs) {
|
|
4582
4611
|
const filePath = path13.join(specDir, file);
|
|
4583
4612
|
const content = await FileUtils.read(filePath);
|
|
4584
|
-
const status =
|
|
4613
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
4614
|
+
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
4585
4615
|
inventory.push({
|
|
4586
4616
|
name: file.replace(".md", ""),
|
|
4587
|
-
status,
|
|
4588
|
-
progress: getProgress(content)
|
|
4617
|
+
status: statusWithIcon,
|
|
4618
|
+
progress: SpecUtils.getProgress(content)
|
|
4589
4619
|
});
|
|
4590
4620
|
}
|
|
4591
4621
|
const tableData = inventory.map((item) => [
|
|
@@ -4652,28 +4682,6 @@ async function displayRecentActivity() {
|
|
|
4652
4682
|
logger.newLine();
|
|
4653
4683
|
}
|
|
4654
4684
|
}
|
|
4655
|
-
function parseSpecStatus2(spec) {
|
|
4656
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
4657
|
-
if (statusMatch) {
|
|
4658
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
4659
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2713 \u5DF2\u5B8C\u6210";
|
|
4660
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
4661
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
4662
|
-
}
|
|
4663
|
-
return "\u25CB \u672A\u5F00\u59CB";
|
|
4664
|
-
}
|
|
4665
|
-
function getProgress(spec) {
|
|
4666
|
-
const milestoneMatches = spec.match(/###\s+Milestone\s+\d+:/g);
|
|
4667
|
-
const milestones = milestoneMatches ? milestoneMatches.length : 0;
|
|
4668
|
-
const todoMatches = spec.match(/-\s+\[[ x ]\]/g);
|
|
4669
|
-
const totalTodos = todoMatches ? todoMatches.length : 0;
|
|
4670
|
-
const completedMatches = spec.match(/-\s+\[x\]/g);
|
|
4671
|
-
const completedTodos = completedMatches ? completedMatches.length : 0;
|
|
4672
|
-
if (totalTodos === 0) {
|
|
4673
|
-
return "-";
|
|
4674
|
-
}
|
|
4675
|
-
return `${completedTodos}/${totalTodos}`;
|
|
4676
|
-
}
|
|
4677
4685
|
|
|
4678
4686
|
// src/commands/detect-deps.ts
|
|
4679
4687
|
init_esm_shims();
|
|
@@ -4991,16 +4999,17 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
4991
4999
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
4992
5000
|
const specPath = path15.join(specsDir, specFile);
|
|
4993
5001
|
const content = await FileUtils.read(specPath);
|
|
4994
|
-
const status =
|
|
4995
|
-
const
|
|
5002
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
5003
|
+
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
5004
|
+
const progress = SpecUtils.getProgress(content);
|
|
4996
5005
|
let completionDate = "-";
|
|
4997
|
-
if (status === "\
|
|
5006
|
+
if (status === "\u5DF2\u5B8C\u6210") {
|
|
4998
5007
|
const dateMatch = content.match(/完成日期.*[::]\s*(.+)/);
|
|
4999
5008
|
if (dateMatch) {
|
|
5000
5009
|
completionDate = dateMatch[1].trim();
|
|
5001
5010
|
}
|
|
5002
5011
|
}
|
|
5003
|
-
lines.push(`| ${displayName} | ${name}.md | ${
|
|
5012
|
+
lines.push(`| ${displayName} | ${name}.md | ${statusWithIcon} | ${progress} | ${completionDate} | |`);
|
|
5004
5013
|
}
|
|
5005
5014
|
const newContent = lines.join("\n") + "\n";
|
|
5006
5015
|
await replaceOrInsertSection(aiMemoryFile, "## \u529F\u80FD\u6E05\u5355", newContent);
|
|
@@ -5187,26 +5196,6 @@ async function replaceOrInsertSection(aiMemoryFile, sectionTitle, newContent) {
|
|
|
5187
5196
|
await FileUtils.write(aiMemoryFile, content.trimEnd() + "\n\n" + newContent + "\n");
|
|
5188
5197
|
}
|
|
5189
5198
|
}
|
|
5190
|
-
function parseSpecStatus3(spec) {
|
|
5191
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
5192
|
-
if (statusMatch) {
|
|
5193
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
5194
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2705 \u5DF2\u5B8C\u6210";
|
|
5195
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
5196
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
5197
|
-
}
|
|
5198
|
-
return "\u25CB \u672A\u5F00\u59CB";
|
|
5199
|
-
}
|
|
5200
|
-
function getSpecProgress(spec) {
|
|
5201
|
-
const todoMatches = spec.match(/-\s+\[[ x ]\]/g);
|
|
5202
|
-
const totalTodos = todoMatches ? todoMatches.length : 0;
|
|
5203
|
-
const completedMatches = spec.match(/-\s+\[x\]/g);
|
|
5204
|
-
const completedTodos = completedMatches ? completedMatches.length : 0;
|
|
5205
|
-
if (totalTodos === 0) {
|
|
5206
|
-
return "-";
|
|
5207
|
-
}
|
|
5208
|
-
return `${completedTodos}/${totalTodos}`;
|
|
5209
|
-
}
|
|
5210
5199
|
async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
5211
5200
|
logger.step("\u540C\u6B65\u6A21\u677F\u7248\u672C\u4FE1\u606F...");
|
|
5212
5201
|
const config = await readTemplateConfig(projectDir);
|