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/README.md
CHANGED
|
@@ -69,6 +69,16 @@
|
|
|
69
69
|
- **新增**: `team-cli dev` 选择里程碑时,现在会直观显示已完成、进行中和未开始的状态图标及进度 (如 `[4/7]`)
|
|
70
70
|
- **改进**: 增强了 Spec 文件的解析逻辑,能够更准确地识别已标记为 `[x]` 的任务项
|
|
71
71
|
|
|
72
|
+
**v2.5.9** - Spec 状态同步与动态检测
|
|
73
|
+
- **改进**: `dev` 模式列表现在基于 Milestone 实际完成百分比动态显示 Spec 状态(即使文件头部状态未及时手动更新)
|
|
74
|
+
- **修复**: 在通过 `dev` 命令完成任务时,系统现在会自动同步并更新 Spec 文件头部的“状态”标识,确保文档与进度一致
|
|
75
|
+
|
|
76
|
+
**v2.6.0** - 统一状态解析逻辑与多维同步
|
|
77
|
+
- **重构**: 将核心 Spec 解析逻辑沉淀至 `SpecUtils` 共享工具类,实现全系统逻辑收口
|
|
78
|
+
- **改进**: `status` 命令与 `sync-memory` 命令现在统一接入动态进度推导引擎,解决状态显示落后的问题
|
|
79
|
+
- **优化**: 全面统一了各命令中状态图标(✓, ⟳, ◉, ○)的定义,提供一致的视觉交互体验
|
|
80
|
+
- **增强**: `sync-memory` 现在能更准确地根据任务勾选情况自动更新 `AI_MEMORY.md` 中的完成日期及状态列
|
|
81
|
+
|
|
72
82
|
**v2.4.10** - 修复 Claude 返回权限确认而非实际内容问题
|
|
73
83
|
- **修复**: 添加 `--dangerously-skip-permissions` 参数跳过权限确认
|
|
74
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,16 +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 statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
2949
|
-
if (statusMatch) {
|
|
2950
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
2951
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u5DF2\u5B8C\u6210";
|
|
2952
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u8FDB\u884C\u4E2D";
|
|
2953
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u5DF2\u62C6\u5206";
|
|
2954
|
-
}
|
|
2955
|
-
return "\u672A\u5F00\u59CB";
|
|
2956
|
-
}
|
|
2957
3031
|
function parseDependencies(spec) {
|
|
2958
3032
|
const deps = [];
|
|
2959
3033
|
let inDepsSection = false;
|
|
@@ -2976,63 +3050,6 @@ function parseDependencies(spec) {
|
|
|
2976
3050
|
}
|
|
2977
3051
|
return deps;
|
|
2978
3052
|
}
|
|
2979
|
-
function parseMilestones(spec) {
|
|
2980
|
-
const milestones = [];
|
|
2981
|
-
const lines = spec.split("\n");
|
|
2982
|
-
let currentMilestone = null;
|
|
2983
|
-
let inMilestone = false;
|
|
2984
|
-
for (const line of lines) {
|
|
2985
|
-
if (line.match(/^###\s+Milestone\s+\d+:/)) {
|
|
2986
|
-
if (currentMilestone) {
|
|
2987
|
-
milestones.push(currentMilestone);
|
|
2988
|
-
}
|
|
2989
|
-
const title = line.replace(/^###\s+/, "").trim();
|
|
2990
|
-
currentMilestone = { title, todos: [], completedCount: 0 };
|
|
2991
|
-
inMilestone = true;
|
|
2992
|
-
continue;
|
|
2993
|
-
}
|
|
2994
|
-
if (inMilestone && currentMilestone) {
|
|
2995
|
-
if (line.match(/^###\s+Milestone/)) {
|
|
2996
|
-
milestones.push(currentMilestone);
|
|
2997
|
-
currentMilestone = null;
|
|
2998
|
-
continue;
|
|
2999
|
-
}
|
|
3000
|
-
const todoMatch = line.match(/^-\s+\[([ xX])\]\s*(.+)/);
|
|
3001
|
-
if (todoMatch) {
|
|
3002
|
-
const isCompleted = todoMatch[1].toLowerCase() === "x";
|
|
3003
|
-
if (isCompleted) {
|
|
3004
|
-
currentMilestone.completedCount++;
|
|
3005
|
-
}
|
|
3006
|
-
currentMilestone.todos.push(todoMatch[2].trim());
|
|
3007
|
-
}
|
|
3008
|
-
}
|
|
3009
|
-
}
|
|
3010
|
-
if (currentMilestone) {
|
|
3011
|
-
milestones.push(currentMilestone);
|
|
3012
|
-
}
|
|
3013
|
-
return milestones;
|
|
3014
|
-
}
|
|
3015
|
-
function parseTodos(spec, milestoneTitle) {
|
|
3016
|
-
const lines = spec.split("\n");
|
|
3017
|
-
const todos = [];
|
|
3018
|
-
let inTargetMilestone = false;
|
|
3019
|
-
for (const line of lines) {
|
|
3020
|
-
if (line.includes(milestoneTitle)) {
|
|
3021
|
-
inTargetMilestone = true;
|
|
3022
|
-
continue;
|
|
3023
|
-
}
|
|
3024
|
-
if (inTargetMilestone) {
|
|
3025
|
-
if (line.match(/^###\s+Milestone/)) {
|
|
3026
|
-
break;
|
|
3027
|
-
}
|
|
3028
|
-
const todoMatch = line.match(/^-\s+\[[ x ]\]\s*(.+)/);
|
|
3029
|
-
if (todoMatch) {
|
|
3030
|
-
todos.push(todoMatch[1].trim());
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
}
|
|
3034
|
-
return todos;
|
|
3035
|
-
}
|
|
3036
3053
|
function topologicalSort(specs) {
|
|
3037
3054
|
const sorted = [];
|
|
3038
3055
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -3190,7 +3207,19 @@ async function askAndUpdateSpecStatus(specFile, milestone, todo) {
|
|
|
3190
3207
|
logger.warn("\u672A\u627E\u5230\u5BF9\u5E94\u7684 todo \u9879");
|
|
3191
3208
|
return;
|
|
3192
3209
|
}
|
|
3193
|
-
|
|
3210
|
+
const newContent = lines.join("\n");
|
|
3211
|
+
const computedStatus = SpecUtils.parseSpecStatus(newContent);
|
|
3212
|
+
let finalLines = lines;
|
|
3213
|
+
const statusLineIndex = finalLines.findIndex((l) => l.match(/状态.*[::]/));
|
|
3214
|
+
if (statusLineIndex !== -1) {
|
|
3215
|
+
const oldLine = finalLines[statusLineIndex];
|
|
3216
|
+
const newLine = oldLine.replace(/([::]\s*)(.+)/, `$1**${computedStatus}**`);
|
|
3217
|
+
if (oldLine !== newLine) {
|
|
3218
|
+
finalLines[statusLineIndex] = newLine;
|
|
3219
|
+
logger.info(`\u81EA\u52A8\u540C\u6B65 Spec \u5934\u90E8\u72B6\u6001\u4E3A: ${computedStatus}`);
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
await FileUtils.write(specFile, finalLines.join("\n"));
|
|
3194
3223
|
logger.success("Spec \u6587\u4EF6\u5DF2\u66F4\u65B0");
|
|
3195
3224
|
} catch (error) {
|
|
3196
3225
|
logger.warn(`\u66F4\u65B0 spec \u6587\u4EF6\u5931\u8D25: ${error}`);
|
|
@@ -4610,11 +4639,12 @@ async function displayFeatureInventory() {
|
|
|
4610
4639
|
for (const file of specs) {
|
|
4611
4640
|
const filePath = path13.join(specDir, file);
|
|
4612
4641
|
const content = await FileUtils.read(filePath);
|
|
4613
|
-
const status =
|
|
4642
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
4643
|
+
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
4614
4644
|
inventory.push({
|
|
4615
4645
|
name: file.replace(".md", ""),
|
|
4616
|
-
status,
|
|
4617
|
-
progress: getProgress(content)
|
|
4646
|
+
status: statusWithIcon,
|
|
4647
|
+
progress: SpecUtils.getProgress(content)
|
|
4618
4648
|
});
|
|
4619
4649
|
}
|
|
4620
4650
|
const tableData = inventory.map((item) => [
|
|
@@ -4681,28 +4711,6 @@ async function displayRecentActivity() {
|
|
|
4681
4711
|
logger.newLine();
|
|
4682
4712
|
}
|
|
4683
4713
|
}
|
|
4684
|
-
function parseSpecStatus2(spec) {
|
|
4685
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
4686
|
-
if (statusMatch) {
|
|
4687
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
4688
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2713 \u5DF2\u5B8C\u6210";
|
|
4689
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
4690
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
4691
|
-
}
|
|
4692
|
-
return "\u25CB \u672A\u5F00\u59CB";
|
|
4693
|
-
}
|
|
4694
|
-
function getProgress(spec) {
|
|
4695
|
-
const milestoneMatches = spec.match(/###\s+Milestone\s+\d+:/g);
|
|
4696
|
-
const milestones = milestoneMatches ? milestoneMatches.length : 0;
|
|
4697
|
-
const todoMatches = spec.match(/-\s+\[[ x ]\]/g);
|
|
4698
|
-
const totalTodos = todoMatches ? todoMatches.length : 0;
|
|
4699
|
-
const completedMatches = spec.match(/-\s+\[x\]/g);
|
|
4700
|
-
const completedTodos = completedMatches ? completedMatches.length : 0;
|
|
4701
|
-
if (totalTodos === 0) {
|
|
4702
|
-
return "-";
|
|
4703
|
-
}
|
|
4704
|
-
return `${completedTodos}/${totalTodos}`;
|
|
4705
|
-
}
|
|
4706
4714
|
var statusCommand;
|
|
4707
4715
|
var init_status = __esm({
|
|
4708
4716
|
"src/commands/status.ts"() {
|
|
@@ -5024,16 +5032,17 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5024
5032
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
5025
5033
|
const specPath = path15.join(specsDir, specFile);
|
|
5026
5034
|
const content = await FileUtils.read(specPath);
|
|
5027
|
-
const status =
|
|
5028
|
-
const
|
|
5035
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
5036
|
+
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
5037
|
+
const progress = SpecUtils.getProgress(content);
|
|
5029
5038
|
let completionDate = "-";
|
|
5030
|
-
if (status === "\
|
|
5039
|
+
if (status === "\u5DF2\u5B8C\u6210") {
|
|
5031
5040
|
const dateMatch = content.match(/完成日期.*[::]\s*(.+)/);
|
|
5032
5041
|
if (dateMatch) {
|
|
5033
5042
|
completionDate = dateMatch[1].trim();
|
|
5034
5043
|
}
|
|
5035
5044
|
}
|
|
5036
|
-
lines.push(`| ${displayName} | ${name}.md | ${
|
|
5045
|
+
lines.push(`| ${displayName} | ${name}.md | ${statusWithIcon} | ${progress} | ${completionDate} | |`);
|
|
5037
5046
|
}
|
|
5038
5047
|
const newContent = lines.join("\n") + "\n";
|
|
5039
5048
|
await replaceOrInsertSection(aiMemoryFile, "## \u529F\u80FD\u6E05\u5355", newContent);
|
|
@@ -5220,26 +5229,6 @@ async function replaceOrInsertSection(aiMemoryFile, sectionTitle, newContent) {
|
|
|
5220
5229
|
await FileUtils.write(aiMemoryFile, content.trimEnd() + "\n\n" + newContent + "\n");
|
|
5221
5230
|
}
|
|
5222
5231
|
}
|
|
5223
|
-
function parseSpecStatus3(spec) {
|
|
5224
|
-
const statusMatch = spec.match(/状态.*[::]\s*(.+)/);
|
|
5225
|
-
if (statusMatch) {
|
|
5226
|
-
const status = statusMatch[1].replace(/\*\*/g, "").trim();
|
|
5227
|
-
if (status.includes("\u5DF2\u5B8C\u6210")) return "\u2705 \u5DF2\u5B8C\u6210";
|
|
5228
|
-
if (status.includes("\u8FDB\u884C\u4E2D")) return "\u27F3 \u8FDB\u884C\u4E2D";
|
|
5229
|
-
if (status.includes("\u5DF2\u62C6\u5206")) return "\u25C9 \u5DF2\u62C6\u5206";
|
|
5230
|
-
}
|
|
5231
|
-
return "\u25CB \u672A\u5F00\u59CB";
|
|
5232
|
-
}
|
|
5233
|
-
function getSpecProgress(spec) {
|
|
5234
|
-
const todoMatches = spec.match(/-\s+\[[ x ]\]/g);
|
|
5235
|
-
const totalTodos = todoMatches ? todoMatches.length : 0;
|
|
5236
|
-
const completedMatches = spec.match(/-\s+\[x\]/g);
|
|
5237
|
-
const completedTodos = completedMatches ? completedMatches.length : 0;
|
|
5238
|
-
if (totalTodos === 0) {
|
|
5239
|
-
return "-";
|
|
5240
|
-
}
|
|
5241
|
-
return `${completedTodos}/${totalTodos}`;
|
|
5242
|
-
}
|
|
5243
5232
|
async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
5244
5233
|
logger.step("\u540C\u6B65\u6A21\u677F\u7248\u672C\u4FE1\u606F...");
|
|
5245
5234
|
const config = await readTemplateConfig(projectDir);
|