yg-team-cli 2.5.9 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli.js +101 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.js +101 -134
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,12 @@
|
|
|
73
73
|
- **改进**: `dev` 模式列表现在基于 Milestone 实际完成百分比动态显示 Spec 状态(即使文件头部状态未及时手动更新)
|
|
74
74
|
- **修复**: 在通过 `dev` 命令完成任务时,系统现在会自动同步并更新 Spec 文件头部的“状态”标识,确保文档与进度一致
|
|
75
75
|
|
|
76
|
+
**v2.6.0** - 统一状态解析逻辑与多维同步
|
|
77
|
+
- **重构**: 将核心 Spec 解析逻辑沉淀至 `SpecUtils` 共享工具类,实现全系统逻辑收口
|
|
78
|
+
- **改进**: `status` 命令与 `sync-memory` 命令现在统一接入动态进度推导引擎,解决状态显示落后的问题
|
|
79
|
+
- **优化**: 全面统一了各命令中状态图标(✓, ⟳, ◉, ○)的定义,提供一致的视觉交互体验
|
|
80
|
+
- **增强**: `sync-memory` 现在能更准确地根据任务勾选情况自动更新 `AI_MEMORY.md` 中的完成日期及状态列
|
|
81
|
+
|
|
76
82
|
**v2.4.10** - 修复 Claude 返回权限确认而非实际内容问题
|
|
77
83
|
- **修复**: 添加 `--dangerously-skip-permissions` 参数跳过权限确认
|
|
78
84
|
- **问题**: v2.4.9 中 Claude CLI 在新目录首次运行时返回权限确认提示而非 spec 内容
|
package/dist/cli.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 {
|
|
@@ -2775,7 +2857,7 @@ async function selectSpec() {
|
|
|
2775
2857
|
for (let i = 0; i < specFiles.length; i++) {
|
|
2776
2858
|
const file = path9.join(specDir, specFiles[i]);
|
|
2777
2859
|
const spec = await FileUtils.read(file);
|
|
2778
|
-
const status = parseSpecStatus(spec);
|
|
2860
|
+
const status = SpecUtils.parseSpecStatus(spec);
|
|
2779
2861
|
const dependencies = parseDependencies(spec);
|
|
2780
2862
|
specs.push({
|
|
2781
2863
|
file,
|
|
@@ -2817,7 +2899,7 @@ async function selectMilestone(specFile) {
|
|
|
2817
2899
|
logger.step("\u6B65\u9AA4 2/3: \u89E3\u6790 milestones...");
|
|
2818
2900
|
logger.newLine();
|
|
2819
2901
|
const specContent = await FileUtils.read(specFile);
|
|
2820
|
-
const milestones = parseMilestones(specContent);
|
|
2902
|
+
const milestones = SpecUtils.parseMilestones(specContent);
|
|
2821
2903
|
if (milestones.length === 0) {
|
|
2822
2904
|
logger.info("\u8BE5 spec \u5C1A\u672A\u62C6\u5206 milestones");
|
|
2823
2905
|
const { breakdownNow } = await inquirer3.prompt([
|
|
@@ -2870,7 +2952,9 @@ async function selectTodo(specFile, milestone) {
|
|
|
2870
2952
|
logger.step("\u6B65\u9AA4 3/3: \u9009\u62E9 todo \u4EFB\u52A1...");
|
|
2871
2953
|
logger.newLine();
|
|
2872
2954
|
const specContent = await FileUtils.read(specFile);
|
|
2873
|
-
const
|
|
2955
|
+
const milestones = SpecUtils.parseMilestones(specContent);
|
|
2956
|
+
const targetMilestone = milestones.find((m) => m.title === milestone);
|
|
2957
|
+
const todos = targetMilestone ? targetMilestone.todos : [];
|
|
2874
2958
|
if (todos.length === 0) {
|
|
2875
2959
|
logger.warn("\u8BE5 milestone \u6CA1\u6709 todo \u4EFB\u52A1");
|
|
2876
2960
|
const { implementAll } = await inquirer3.prompt([
|
|
@@ -2944,26 +3028,6 @@ async function executeDevelopment(specFile, milestone, todo) {
|
|
|
2944
3028
|
logger.step("2. \u8FD0\u884C 'team-cli dev' \u7EE7\u7EED\u4E0B\u4E00\u4E2A\u4EFB\u52A1");
|
|
2945
3029
|
logger.newLine();
|
|
2946
3030
|
}
|
|
2947
|
-
function parseSpecStatus(spec) {
|
|
2948
|
-
const milestones = parseMilestones(spec);
|
|
2949
|
-
if (milestones.length > 0) {
|
|
2950
|
-
const totalTodos = milestones.reduce((sum, m) => sum + m.todos.length, 0);
|
|
2951
|
-
const completedTodos = milestones.reduce((sum, m) => sum + m.completedCount, 0);
|
|
2952
|
-
if (totalTodos > 0) {
|
|
2953
|
-
if (completedTodos === totalTodos) return "\u5DF2\u5B8C\u6210";
|
|
2954
|
-
if (completedTodos > 0) return "\u8FDB\u884C\u4E2D";
|
|
2955
|
-
}
|
|
2956
|
-
return "\u5DF2\u62C6\u5206";
|
|
2957
|
-
}
|
|
2958
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
2959
|
-
if (statusMatch) {
|
|
2960
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
2961
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u5DF2\u5B8C\u6210";
|
|
2962
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u8FDB\u884C\u4E2D";
|
|
2963
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u5DF2\u62C6\u5206";
|
|
2964
|
-
}
|
|
2965
|
-
return "\u672A\u5F00\u59CB";
|
|
2966
|
-
}
|
|
2967
3031
|
function parseDependencies(spec) {
|
|
2968
3032
|
const deps = [];
|
|
2969
3033
|
let inDepsSection = false;
|
|
@@ -2986,63 +3050,6 @@ function parseDependencies(spec) {
|
|
|
2986
3050
|
}
|
|
2987
3051
|
return deps;
|
|
2988
3052
|
}
|
|
2989
|
-
function parseMilestones(spec) {
|
|
2990
|
-
const milestones = [];
|
|
2991
|
-
const lines = spec.split("\n");
|
|
2992
|
-
let currentMilestone = null;
|
|
2993
|
-
let inMilestone = false;
|
|
2994
|
-
for (const line of lines) {
|
|
2995
|
-
if (line.match(/^###\s+Milestone\s+\d+:/)) {
|
|
2996
|
-
if (currentMilestone) {
|
|
2997
|
-
milestones.push(currentMilestone);
|
|
2998
|
-
}
|
|
2999
|
-
const title = line.replace(/^###\s+/, "").trim();
|
|
3000
|
-
currentMilestone = { title, todos: [], completedCount: 0 };
|
|
3001
|
-
inMilestone = true;
|
|
3002
|
-
continue;
|
|
3003
|
-
}
|
|
3004
|
-
if (inMilestone && currentMilestone) {
|
|
3005
|
-
if (line.match(/^###\s+Milestone/)) {
|
|
3006
|
-
milestones.push(currentMilestone);
|
|
3007
|
-
currentMilestone = null;
|
|
3008
|
-
continue;
|
|
3009
|
-
}
|
|
3010
|
-
const todoMatch = line.match(/^-\s+\[([ xX])\]\s*(.+)/);
|
|
3011
|
-
if (todoMatch) {
|
|
3012
|
-
const isCompleted = todoMatch[1].toLowerCase() === "x";
|
|
3013
|
-
if (isCompleted) {
|
|
3014
|
-
currentMilestone.completedCount++;
|
|
3015
|
-
}
|
|
3016
|
-
currentMilestone.todos.push(todoMatch[2].trim());
|
|
3017
|
-
}
|
|
3018
|
-
}
|
|
3019
|
-
}
|
|
3020
|
-
if (currentMilestone) {
|
|
3021
|
-
milestones.push(currentMilestone);
|
|
3022
|
-
}
|
|
3023
|
-
return milestones;
|
|
3024
|
-
}
|
|
3025
|
-
function parseTodos(spec, milestoneTitle) {
|
|
3026
|
-
const lines = spec.split("\n");
|
|
3027
|
-
const todos = [];
|
|
3028
|
-
let inTargetMilestone = false;
|
|
3029
|
-
for (const line of lines) {
|
|
3030
|
-
if (line.includes(milestoneTitle)) {
|
|
3031
|
-
inTargetMilestone = true;
|
|
3032
|
-
continue;
|
|
3033
|
-
}
|
|
3034
|
-
if (inTargetMilestone) {
|
|
3035
|
-
if (line.match(/^###\s+Milestone/)) {
|
|
3036
|
-
break;
|
|
3037
|
-
}
|
|
3038
|
-
const todoMatch = line.match(/^-\s+\[[ x ]\]\s*(.+)/);
|
|
3039
|
-
if (todoMatch) {
|
|
3040
|
-
todos.push(todoMatch[1].trim());
|
|
3041
|
-
}
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
return todos;
|
|
3045
|
-
}
|
|
3046
3053
|
function topologicalSort(specs) {
|
|
3047
3054
|
const sorted = [];
|
|
3048
3055
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -3201,7 +3208,7 @@ async function askAndUpdateSpecStatus(specFile, milestone, todo) {
|
|
|
3201
3208
|
return;
|
|
3202
3209
|
}
|
|
3203
3210
|
const newContent = lines.join("\n");
|
|
3204
|
-
const computedStatus = parseSpecStatus(newContent);
|
|
3211
|
+
const computedStatus = SpecUtils.parseSpecStatus(newContent);
|
|
3205
3212
|
let finalLines = lines;
|
|
3206
3213
|
const statusLineIndex = finalLines.findIndex((l) => l.match(/状态.*[::]/));
|
|
3207
3214
|
if (statusLineIndex !== -1) {
|
|
@@ -4632,11 +4639,12 @@ async function displayFeatureInventory() {
|
|
|
4632
4639
|
for (const file of specs) {
|
|
4633
4640
|
const filePath = path13.join(specDir, file);
|
|
4634
4641
|
const content = await FileUtils.read(filePath);
|
|
4635
|
-
const status =
|
|
4642
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
4643
|
+
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
4636
4644
|
inventory.push({
|
|
4637
4645
|
name: file.replace(".md", ""),
|
|
4638
|
-
status,
|
|
4639
|
-
progress: getProgress(content)
|
|
4646
|
+
status: statusWithIcon,
|
|
4647
|
+
progress: SpecUtils.getProgress(content)
|
|
4640
4648
|
});
|
|
4641
4649
|
}
|
|
4642
4650
|
const tableData = inventory.map((item) => [
|
|
@@ -4646,9 +4654,9 @@ async function displayFeatureInventory() {
|
|
|
4646
4654
|
]);
|
|
4647
4655
|
logger.table(["\u529F\u80FD", "\u72B6\u6001", "\u8FDB\u5EA6"], tableData);
|
|
4648
4656
|
logger.newLine();
|
|
4649
|
-
const completed = inventory.filter((i) => i.status
|
|
4650
|
-
const inProgress = inventory.filter((i) => i.status
|
|
4651
|
-
const pending = inventory.filter((i) => i.status
|
|
4657
|
+
const completed = inventory.filter((i) => i.status.includes("\u5DF2\u5B8C\u6210")).length;
|
|
4658
|
+
const inProgress = inventory.filter((i) => i.status.includes("\u8FDB\u884C\u4E2D")).length;
|
|
4659
|
+
const pending = inventory.filter((i) => i.status.includes("\u672A\u5F00\u59CB")).length;
|
|
4652
4660
|
logger.info(`\u603B\u8BA1: ${inventory.length} | \u5DF2\u5B8C\u6210: ${completed} | \u8FDB\u884C\u4E2D: ${inProgress} | \u672A\u5F00\u59CB: ${pending}`);
|
|
4653
4661
|
logger.newLine();
|
|
4654
4662
|
}
|
|
@@ -4703,28 +4711,6 @@ async function displayRecentActivity() {
|
|
|
4703
4711
|
logger.newLine();
|
|
4704
4712
|
}
|
|
4705
4713
|
}
|
|
4706
|
-
function parseSpecStatus2(spec) {
|
|
4707
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
4708
|
-
if (statusMatch) {
|
|
4709
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
4710
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2713 \u5DF2\u5B8C\u6210";
|
|
4711
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
4712
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
4713
|
-
}
|
|
4714
|
-
return "\u25CB \u672A\u5F00\u59CB";
|
|
4715
|
-
}
|
|
4716
|
-
function getProgress(spec) {
|
|
4717
|
-
const milestoneMatches = spec.match(/###\s+Milestone\s+\d+:/g);
|
|
4718
|
-
const milestones = milestoneMatches ? milestoneMatches.length : 0;
|
|
4719
|
-
const todoMatches = spec.match(/-\s+\[[ x ]\]/g);
|
|
4720
|
-
const totalTodos = todoMatches ? todoMatches.length : 0;
|
|
4721
|
-
const completedMatches = spec.match(/-\s+\[x\]/g);
|
|
4722
|
-
const completedTodos = completedMatches ? completedMatches.length : 0;
|
|
4723
|
-
if (totalTodos === 0) {
|
|
4724
|
-
return "-";
|
|
4725
|
-
}
|
|
4726
|
-
return `${completedTodos}/${totalTodos}`;
|
|
4727
|
-
}
|
|
4728
4714
|
var statusCommand;
|
|
4729
4715
|
var init_status = __esm({
|
|
4730
4716
|
"src/commands/status.ts"() {
|
|
@@ -5046,16 +5032,17 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5046
5032
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
5047
5033
|
const specPath = path15.join(specsDir, specFile);
|
|
5048
5034
|
const content = await FileUtils.read(specPath);
|
|
5049
|
-
const status =
|
|
5050
|
-
const
|
|
5035
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
5036
|
+
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
5037
|
+
const progress = SpecUtils.getProgress(content);
|
|
5051
5038
|
let completionDate = "-";
|
|
5052
|
-
if (status === "\
|
|
5039
|
+
if (status === "\u5DF2\u5B8C\u6210") {
|
|
5053
5040
|
const dateMatch = content.match(/完成日期.*[::]\s*(.+)/);
|
|
5054
5041
|
if (dateMatch) {
|
|
5055
5042
|
completionDate = dateMatch[1].trim();
|
|
5056
5043
|
}
|
|
5057
5044
|
}
|
|
5058
|
-
lines.push(`| ${displayName} | ${name}.md | ${
|
|
5045
|
+
lines.push(`| ${displayName} | ${name}.md | ${statusWithIcon} | ${progress} | ${completionDate} | |`);
|
|
5059
5046
|
}
|
|
5060
5047
|
const newContent = lines.join("\n") + "\n";
|
|
5061
5048
|
await replaceOrInsertSection(aiMemoryFile, "## \u529F\u80FD\u6E05\u5355", newContent);
|
|
@@ -5242,26 +5229,6 @@ async function replaceOrInsertSection(aiMemoryFile, sectionTitle, newContent) {
|
|
|
5242
5229
|
await FileUtils.write(aiMemoryFile, content.trimEnd() + "\n\n" + newContent + "\n");
|
|
5243
5230
|
}
|
|
5244
5231
|
}
|
|
5245
|
-
function parseSpecStatus3(spec) {
|
|
5246
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
5247
|
-
if (statusMatch) {
|
|
5248
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
5249
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2705 \u5DF2\u5B8C\u6210";
|
|
5250
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
5251
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
5252
|
-
}
|
|
5253
|
-
return "\u25CB \u672A\u5F00\u59CB";
|
|
5254
|
-
}
|
|
5255
|
-
function getSpecProgress(spec) {
|
|
5256
|
-
const todoMatches = spec.match(/-\s+\[[ x ]\]/g);
|
|
5257
|
-
const totalTodos = todoMatches ? todoMatches.length : 0;
|
|
5258
|
-
const completedMatches = spec.match(/-\s+\[x\]/g);
|
|
5259
|
-
const completedTodos = completedMatches ? completedMatches.length : 0;
|
|
5260
|
-
if (totalTodos === 0) {
|
|
5261
|
-
return "-";
|
|
5262
|
-
}
|
|
5263
|
-
return `${completedTodos}/${totalTodos}`;
|
|
5264
|
-
}
|
|
5265
5232
|
async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
5266
5233
|
logger.step("\u540C\u6B65\u6A21\u677F\u7248\u672C\u4FE1\u606F...");
|
|
5267
5234
|
const config = await readTemplateConfig(projectDir);
|