yg-team-cli 2.6.2 → 2.6.3
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 +939 -129
- package/dist/cli.js.map +1 -1
- package/dist/index.js +932 -129
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -460,6 +460,121 @@ var init_utils = __esm({
|
|
|
460
460
|
}
|
|
461
461
|
return milestones;
|
|
462
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* 解析增强版里程碑信息(支持 [F]/[B]/[I] 前缀分类)
|
|
465
|
+
*/
|
|
466
|
+
static parseMilestonesEnhanced(content) {
|
|
467
|
+
const milestones = [];
|
|
468
|
+
const lines = content.split("\n");
|
|
469
|
+
let currentMilestone = null;
|
|
470
|
+
let inMilestone = false;
|
|
471
|
+
for (const line of lines) {
|
|
472
|
+
if (line.match(/^###\s+Milestone\s+\d+:/)) {
|
|
473
|
+
if (currentMilestone) {
|
|
474
|
+
currentMilestone.totalCount = currentMilestone.todos.length;
|
|
475
|
+
milestones.push(currentMilestone);
|
|
476
|
+
}
|
|
477
|
+
const title = line.replace(/^###\s+/, "").trim();
|
|
478
|
+
currentMilestone = {
|
|
479
|
+
title,
|
|
480
|
+
todos: [],
|
|
481
|
+
frontendTodos: [],
|
|
482
|
+
backendTodos: [],
|
|
483
|
+
integrationTodos: [],
|
|
484
|
+
generalTodos: [],
|
|
485
|
+
completedCount: 0,
|
|
486
|
+
totalCount: 0
|
|
487
|
+
};
|
|
488
|
+
inMilestone = true;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (inMilestone && currentMilestone) {
|
|
492
|
+
if (line.match(/^###\s+Milestone/)) {
|
|
493
|
+
currentMilestone.totalCount = currentMilestone.todos.length;
|
|
494
|
+
milestones.push(currentMilestone);
|
|
495
|
+
currentMilestone = null;
|
|
496
|
+
const title = line.replace(/^###\s+/, "").trim();
|
|
497
|
+
currentMilestone = {
|
|
498
|
+
title,
|
|
499
|
+
todos: [],
|
|
500
|
+
frontendTodos: [],
|
|
501
|
+
backendTodos: [],
|
|
502
|
+
integrationTodos: [],
|
|
503
|
+
generalTodos: [],
|
|
504
|
+
completedCount: 0,
|
|
505
|
+
totalCount: 0
|
|
506
|
+
};
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
const todoMatch = line.match(/^-\s+\[([ xX])\]\s*(\[[FBIN]\])?\s*(.+)/);
|
|
510
|
+
if (todoMatch) {
|
|
511
|
+
const isCompleted = todoMatch[1].toLowerCase() === "x";
|
|
512
|
+
const prefix = todoMatch[2]?.trim() || "";
|
|
513
|
+
const rawContent = todoMatch[3].trim();
|
|
514
|
+
let type = "general";
|
|
515
|
+
let apiAssociation;
|
|
516
|
+
let dependencies;
|
|
517
|
+
if (prefix === "[F]") {
|
|
518
|
+
type = "frontend";
|
|
519
|
+
} else if (prefix === "[B]") {
|
|
520
|
+
type = "backend";
|
|
521
|
+
const apiMatch = rawContent.match(/\(关联 API:\s*`([^`]+)`\)/);
|
|
522
|
+
if (apiMatch) {
|
|
523
|
+
apiAssociation = apiMatch[1];
|
|
524
|
+
}
|
|
525
|
+
} else if (prefix === "[I]") {
|
|
526
|
+
type = "integration";
|
|
527
|
+
const depMatch = rawContent.match(/\(依赖:\s*([^)]+)\)/);
|
|
528
|
+
if (depMatch) {
|
|
529
|
+
dependencies = depMatch[1].split(",").map((d) => d.trim()).filter((d) => d.length > 0);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const todoItem = {
|
|
533
|
+
raw: line.trim(),
|
|
534
|
+
content: rawContent.replace(/\s*\(关联 API:\s*`[^`]+`\)/, "").replace(/\s*\(依赖:\s*[^)]+\)/, "").trim(),
|
|
535
|
+
type,
|
|
536
|
+
apiAssociation,
|
|
537
|
+
dependencies,
|
|
538
|
+
isCompleted
|
|
539
|
+
};
|
|
540
|
+
currentMilestone.todos.push(todoItem);
|
|
541
|
+
if (isCompleted) {
|
|
542
|
+
currentMilestone.completedCount++;
|
|
543
|
+
}
|
|
544
|
+
switch (type) {
|
|
545
|
+
case "frontend":
|
|
546
|
+
currentMilestone.frontendTodos.push(todoItem);
|
|
547
|
+
break;
|
|
548
|
+
case "backend":
|
|
549
|
+
currentMilestone.backendTodos.push(todoItem);
|
|
550
|
+
break;
|
|
551
|
+
case "integration":
|
|
552
|
+
currentMilestone.integrationTodos.push(todoItem);
|
|
553
|
+
break;
|
|
554
|
+
default:
|
|
555
|
+
currentMilestone.generalTodos.push(todoItem);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (currentMilestone) {
|
|
561
|
+
currentMilestone.totalCount = currentMilestone.todos.length;
|
|
562
|
+
milestones.push(currentMilestone);
|
|
563
|
+
}
|
|
564
|
+
return milestones;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* 解析增强版里程碑(向后兼容版 - 优先尝试增强解析,失败回退到简单解析)
|
|
568
|
+
*/
|
|
569
|
+
static parseMilestonesEnhancedWithFallback(content) {
|
|
570
|
+
const enhanced = this.parseMilestonesEnhanced(content);
|
|
571
|
+
const hasEnhancedFormat = enhanced.some(
|
|
572
|
+
(m) => m.todos.some(
|
|
573
|
+
(t) => t.type !== "general" || t.apiAssociation || t.dependencies
|
|
574
|
+
)
|
|
575
|
+
);
|
|
576
|
+
return { enhanced, isEnhanced: hasEnhancedFormat };
|
|
577
|
+
}
|
|
463
578
|
/**
|
|
464
579
|
* 解析 spec 状态(基于进度动态推导)
|
|
465
580
|
*/
|
|
@@ -1633,22 +1748,22 @@ var init_gitlab_api = __esm({
|
|
|
1633
1748
|
* 从 Git URL 中提取项目路径
|
|
1634
1749
|
*/
|
|
1635
1750
|
static parseProjectPath(repository) {
|
|
1636
|
-
let
|
|
1637
|
-
if (
|
|
1638
|
-
|
|
1639
|
-
const colonIndex =
|
|
1751
|
+
let path21 = repository;
|
|
1752
|
+
if (path21.startsWith("git@")) {
|
|
1753
|
+
path21 = path21.replace(/^git@/, "");
|
|
1754
|
+
const colonIndex = path21.indexOf(":");
|
|
1640
1755
|
if (colonIndex !== -1) {
|
|
1641
|
-
|
|
1756
|
+
path21 = path21.substring(colonIndex + 1);
|
|
1642
1757
|
}
|
|
1643
1758
|
} else {
|
|
1644
|
-
|
|
1645
|
-
const parts =
|
|
1759
|
+
path21 = path21.replace(/^https?:\/\//, "");
|
|
1760
|
+
const parts = path21.split("/");
|
|
1646
1761
|
if (parts.length > 1) {
|
|
1647
|
-
|
|
1762
|
+
path21 = parts.slice(1).join("/");
|
|
1648
1763
|
}
|
|
1649
1764
|
}
|
|
1650
|
-
|
|
1651
|
-
return
|
|
1765
|
+
path21 = path21.replace(/\.git$/, "");
|
|
1766
|
+
return path21;
|
|
1652
1767
|
}
|
|
1653
1768
|
/**
|
|
1654
1769
|
* 编码项目路径用于 API 请求
|
|
@@ -2667,7 +2782,7 @@ ${cleanedMilestones}`;
|
|
|
2667
2782
|
function buildBreakdownPrompt(specContent) {
|
|
2668
2783
|
return `Role: Senior Technical Lead and Agile Coach
|
|
2669
2784
|
|
|
2670
|
-
Task: Break down the following feature spec into milestones and
|
|
2785
|
+
Task: Break down the following feature spec into milestones with frontend, backend, and integration tasks.
|
|
2671
2786
|
|
|
2672
2787
|
Context:
|
|
2673
2788
|
- Read TECH_STACK.md for technology constraints
|
|
@@ -2683,13 +2798,18 @@ ${specContent}
|
|
|
2683
2798
|
Output Requirements:
|
|
2684
2799
|
1. Parse the existing spec content and identify technical requirements.
|
|
2685
2800
|
2. Break it down into 2-5 milestones.
|
|
2686
|
-
3. Each milestone
|
|
2687
|
-
-
|
|
2688
|
-
-
|
|
2689
|
-
-
|
|
2801
|
+
3. Each milestone must have three sections:
|
|
2802
|
+
- **\u524D\u7AEF\u4EFB\u52A1**: Frontend tasks marked with [F] prefix
|
|
2803
|
+
- **\u540E\u7AEF\u4EFB\u52A1**: Backend tasks marked with [B] prefix, include API associations
|
|
2804
|
+
- **\u8054\u8C03\u4EFB\u52A1**: Integration tasks marked with [I] prefix, include dependencies
|
|
2690
2805
|
4. Todo items should be:
|
|
2691
|
-
- Concrete, specific, and testable
|
|
2692
|
-
- Independent as much as possible
|
|
2806
|
+
- Concrete, specific, and testable
|
|
2807
|
+
- Independent as much as possible
|
|
2808
|
+
|
|
2809
|
+
Task Prefix Format:
|
|
2810
|
+
- Frontend: \`- [ ] [F] \u5177\u4F53\u7684\u524D\u7AEF\u4EFB\u52A1\u63CF\u8FF0\`
|
|
2811
|
+
- Backend: \`- [ ] [B] \u5177\u4F53\u7684\u540E\u7AEF\u4EFB\u52A1\u63CF\u8FF0 (\u5173\u8054 API: \`METHOD /api/path\`)\`
|
|
2812
|
+
- Integration: \`- [ ] [I] \u8054\u8C03\u4EFB\u52A1\u63CF\u8FF0 (\u4F9D\u8D56: F-x, B-y)\`
|
|
2693
2813
|
|
|
2694
2814
|
IMPORTANT:
|
|
2695
2815
|
- Output ONLY the "## \u91CC\u7A0B\u7891 (Milestones)" section.
|
|
@@ -2704,9 +2824,16 @@ IMPORTANT:
|
|
|
2704
2824
|
**\u76EE\u6807**: [\u7B80\u77ED\u63CF\u8FF0\u8FD9\u4E2A\u91CC\u7A0B\u7891\u7684\u76EE\u6807]
|
|
2705
2825
|
**\u9884\u4F30**: 2 \u5929
|
|
2706
2826
|
|
|
2707
|
-
|
|
2708
|
-
- [ ]
|
|
2709
|
-
- [ ]
|
|
2827
|
+
**\u524D\u7AEF\u4EFB\u52A1**:
|
|
2828
|
+
- [ ] [F] \u524D\u7AEF\u4EFB\u52A1\u63CF\u8FF0 1
|
|
2829
|
+
- [ ] [F] \u524D\u7AEF\u4EFB\u52A1\u63CF\u8FF0 2
|
|
2830
|
+
|
|
2831
|
+
**\u540E\u7AEF\u4EFB\u52A1**:
|
|
2832
|
+
- [ ] [B] \u540E\u7AEF\u4EFB\u52A1\u63CF\u8FF0 1 (\u5173\u8054 API: \`POST /api/xxx\`)
|
|
2833
|
+
- [ ] [B] \u540E\u7AEF\u4EFB\u52A1\u63CF\u8FF0 2 (\u5173\u8054 API: \`GET /api/yyy\`)
|
|
2834
|
+
|
|
2835
|
+
**\u8054\u8C03\u4EFB\u52A1**:
|
|
2836
|
+
- [ ] [I] \u8054\u8C03\u4EFB\u52A1\u63CF\u8FF0 1 (\u4F9D\u8D56: F-1, B-1)
|
|
2710
2837
|
|
|
2711
2838
|
### Milestone 2: [\u91CC\u7A0B\u7891\u540D\u79F0]
|
|
2712
2839
|
...
|
|
@@ -2899,8 +3026,8 @@ async function selectMilestone(specFile) {
|
|
|
2899
3026
|
logger.step("\u6B65\u9AA4 2/3: \u89E3\u6790 milestones...");
|
|
2900
3027
|
logger.newLine();
|
|
2901
3028
|
const specContent = await FileUtils.read(specFile);
|
|
2902
|
-
const
|
|
2903
|
-
if (
|
|
3029
|
+
const { enhanced, isEnhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specContent);
|
|
3030
|
+
if (enhanced.length === 0) {
|
|
2904
3031
|
logger.info("\u8BE5 spec \u5C1A\u672A\u62C6\u5206 milestones");
|
|
2905
3032
|
const { breakdownNow } = await inquirer3.prompt([
|
|
2906
3033
|
{
|
|
@@ -2918,19 +3045,32 @@ async function selectMilestone(specFile) {
|
|
|
2918
3045
|
return "\u6574\u4E2A spec";
|
|
2919
3046
|
}
|
|
2920
3047
|
}
|
|
2921
|
-
const choices =
|
|
2922
|
-
const isDone = m.completedCount === m.
|
|
3048
|
+
const choices = enhanced.map((m, idx) => {
|
|
3049
|
+
const isDone = m.completedCount === m.totalCount && m.totalCount > 0;
|
|
2923
3050
|
const statusIcon = isDone ? "\u2713" : m.completedCount > 0 ? "\u27F3" : "\u25CB";
|
|
2924
|
-
const progress = `[${m.completedCount}/${m.
|
|
3051
|
+
const progress = `[${m.completedCount}/${m.totalCount}]`;
|
|
2925
3052
|
const label = isDone ? `\u2713 ${m.title}` : `${statusIcon} ${progress} ${m.title}`;
|
|
3053
|
+
let detailInfo = `(${m.totalCount} \u4E2A\u4EFB\u52A1)`;
|
|
3054
|
+
if (isEnhanced) {
|
|
3055
|
+
const fCount = m.frontendTodos.filter((t) => !t.isCompleted).length;
|
|
3056
|
+
const bCount = m.backendTodos.filter((t) => !t.isCompleted).length;
|
|
3057
|
+
const iCount = m.integrationTodos.filter((t) => !t.isCompleted).length;
|
|
3058
|
+
const details = [];
|
|
3059
|
+
if (fCount > 0) details.push(`\u524D\u7AEF:${fCount}`);
|
|
3060
|
+
if (bCount > 0) details.push(`\u540E\u7AEF:${bCount}`);
|
|
3061
|
+
if (iCount > 0) details.push(`\u8054\u8C03:${iCount}`);
|
|
3062
|
+
if (details.length > 0) {
|
|
3063
|
+
detailInfo = `(${details.join(", ")} \u672A\u5B8C\u6210)`;
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
2926
3066
|
return {
|
|
2927
|
-
name: `${idx + 1}. ${label}
|
|
3067
|
+
name: `${idx + 1}. ${label} ${detailInfo}`,
|
|
2928
3068
|
value: m.title,
|
|
2929
3069
|
short: m.title
|
|
2930
3070
|
};
|
|
2931
3071
|
});
|
|
2932
3072
|
choices.push({
|
|
2933
|
-
name: `${
|
|
3073
|
+
name: `${enhanced.length + 1}. \u6574\u4E2A spec (\u5168\u90E8 milestones)`,
|
|
2934
3074
|
value: "\u6574\u4E2A spec",
|
|
2935
3075
|
short: "\u6574\u4E2A spec"
|
|
2936
3076
|
});
|
|
@@ -2951,6 +3091,79 @@ async function selectTodo(specFile, milestone) {
|
|
|
2951
3091
|
logger.newLine();
|
|
2952
3092
|
logger.step("\u6B65\u9AA4 3/3: \u9009\u62E9 todo \u4EFB\u52A1...");
|
|
2953
3093
|
logger.newLine();
|
|
3094
|
+
const specContent = await FileUtils.read(specFile);
|
|
3095
|
+
const { enhanced, isEnhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specContent);
|
|
3096
|
+
const targetMilestone = enhanced.find((m) => m.title === milestone);
|
|
3097
|
+
if (!targetMilestone || !isEnhanced) {
|
|
3098
|
+
return await selectTodoSimple(specFile, milestone);
|
|
3099
|
+
}
|
|
3100
|
+
return await selectTodoEnhanced(targetMilestone);
|
|
3101
|
+
}
|
|
3102
|
+
async function selectTodoEnhanced(milestone) {
|
|
3103
|
+
const choices = [];
|
|
3104
|
+
if (milestone.frontendTodos.length > 0) {
|
|
3105
|
+
choices.push({ name: `\u2501\u2501 \u524D\u7AEF\u4EFB\u52A1 (${milestone.frontendTodos.length}) \u2501\u2501`, disabled: true });
|
|
3106
|
+
milestone.frontendTodos.forEach((todo2, idx) => {
|
|
3107
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3108
|
+
choices.push({
|
|
3109
|
+
name: ` [F] ${icon} ${todo2.content}`,
|
|
3110
|
+
value: todo2.content,
|
|
3111
|
+
short: `[F] ${todo2.content}`
|
|
3112
|
+
});
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
if (milestone.backendTodos.length > 0) {
|
|
3116
|
+
choices.push({ name: `\u2501\u2501 \u540E\u7AEF\u4EFB\u52A1 (${milestone.backendTodos.length}) \u2501\u2501`, disabled: true });
|
|
3117
|
+
milestone.backendTodos.forEach((todo2, idx) => {
|
|
3118
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3119
|
+
const apiInfo = todo2.apiAssociation ? ` (${todo2.apiAssociation})` : "";
|
|
3120
|
+
choices.push({
|
|
3121
|
+
name: ` [B] ${icon} ${todo2.content}${apiInfo}`,
|
|
3122
|
+
value: todo2.content,
|
|
3123
|
+
short: `[B] ${todo2.content}`
|
|
3124
|
+
});
|
|
3125
|
+
});
|
|
3126
|
+
}
|
|
3127
|
+
if (milestone.integrationTodos.length > 0) {
|
|
3128
|
+
choices.push({ name: `\u2501\u2501 \u8054\u8C03\u4EFB\u52A1 (${milestone.integrationTodos.length}) \u2501\u2501`, disabled: true });
|
|
3129
|
+
milestone.integrationTodos.forEach((todo2, idx) => {
|
|
3130
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3131
|
+
const depsInfo = todo2.dependencies ? ` (\u4F9D\u8D56: ${todo2.dependencies.join(", ")})` : "";
|
|
3132
|
+
choices.push({
|
|
3133
|
+
name: ` [I] ${icon} ${todo2.content}${depsInfo}`,
|
|
3134
|
+
value: todo2.content,
|
|
3135
|
+
short: `[I] ${todo2.content}`
|
|
3136
|
+
});
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
if (milestone.generalTodos.length > 0) {
|
|
3140
|
+
choices.push({ name: `\u2501\u2501 \u901A\u7528\u4EFB\u52A1 (${milestone.generalTodos.length}) \u2501\u2501`, disabled: true });
|
|
3141
|
+
milestone.generalTodos.forEach((todo2, idx) => {
|
|
3142
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3143
|
+
choices.push({
|
|
3144
|
+
name: ` ${icon} ${todo2.content}`,
|
|
3145
|
+
value: todo2.content,
|
|
3146
|
+
short: todo2.content
|
|
3147
|
+
});
|
|
3148
|
+
});
|
|
3149
|
+
}
|
|
3150
|
+
choices.push({ name: "\u2501\u2501 \u5176\u4ED6 \u2501\u2501", disabled: true });
|
|
3151
|
+
choices.push({
|
|
3152
|
+
name: `\u5168\u90E8\u4EFB\u52A1 (\u6574\u4E2A milestone)`,
|
|
3153
|
+
value: "\u5168\u90E8\u4EFB\u52A1",
|
|
3154
|
+
short: "\u5168\u90E8\u4EFB\u52A1"
|
|
3155
|
+
});
|
|
3156
|
+
const { todo } = await inquirer3.prompt([
|
|
3157
|
+
{
|
|
3158
|
+
type: "list",
|
|
3159
|
+
name: "todo",
|
|
3160
|
+
message: "\u9009\u62E9 todo \u4EFB\u52A1:",
|
|
3161
|
+
choices
|
|
3162
|
+
}
|
|
3163
|
+
]);
|
|
3164
|
+
return todo;
|
|
3165
|
+
}
|
|
3166
|
+
async function selectTodoSimple(specFile, milestone) {
|
|
2954
3167
|
const specContent = await FileUtils.read(specFile);
|
|
2955
3168
|
const milestones = SpecUtils.parseMilestones(specContent);
|
|
2956
3169
|
const targetMilestone = milestones.find((m) => m.title === milestone);
|
|
@@ -3178,10 +3391,14 @@ async function askAndUpdateSpecStatus(specFile, milestone, todo) {
|
|
|
3178
3391
|
break;
|
|
3179
3392
|
}
|
|
3180
3393
|
if (todo !== "\u5168\u90E8\u529F\u80FD" && todo !== "\u5168\u90E8\u4EFB\u52A1") {
|
|
3181
|
-
const todoMatch = line.match(/^-\s+\[
|
|
3182
|
-
if (todoMatch
|
|
3183
|
-
|
|
3184
|
-
|
|
3394
|
+
const todoMatch = line.match(/^-\s+\[ \]\s*(\[[FBIN]\]\s*)?(.+)/);
|
|
3395
|
+
if (todoMatch) {
|
|
3396
|
+
const todoContent = todoMatch[2].trim();
|
|
3397
|
+
const cleanContent = todoContent.replace(/\s*\(关联 API:\s*`[^`]+`\)/, "").replace(/\s*\(依赖:\s*[^)]+\)/, "").trim();
|
|
3398
|
+
if (cleanContent === todo) {
|
|
3399
|
+
targetTodoIndex = i;
|
|
3400
|
+
break;
|
|
3401
|
+
}
|
|
3185
3402
|
}
|
|
3186
3403
|
}
|
|
3187
3404
|
}
|
|
@@ -4467,8 +4684,597 @@ Temporary solution: ${answers.solution}`
|
|
|
4467
4684
|
}
|
|
4468
4685
|
});
|
|
4469
4686
|
|
|
4470
|
-
// src/commands/
|
|
4687
|
+
// src/commands/accept.ts
|
|
4471
4688
|
import { Command as Command7 } from "commander";
|
|
4689
|
+
import inquirer6 from "inquirer";
|
|
4690
|
+
import path13 from "path";
|
|
4691
|
+
async function selectSpec2(defaultSpec) {
|
|
4692
|
+
logger.step("\u6B65\u9AA4 1/4: \u9009\u62E9 spec \u6587\u4EF6...");
|
|
4693
|
+
logger.newLine();
|
|
4694
|
+
const specDir = "docs/specs";
|
|
4695
|
+
const exists = await FileUtils.exists(specDir);
|
|
4696
|
+
if (!exists) {
|
|
4697
|
+
throw new Error("docs/specs \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
4698
|
+
}
|
|
4699
|
+
const files = await FileUtils.findFiles("*.md", specDir);
|
|
4700
|
+
const specFiles = files.filter((f) => !f.includes("template"));
|
|
4701
|
+
if (specFiles.length === 0) {
|
|
4702
|
+
throw new Error("\u672A\u627E\u5230 spec \u6587\u4EF6");
|
|
4703
|
+
}
|
|
4704
|
+
if (defaultSpec) {
|
|
4705
|
+
const fullPath = defaultSpec.startsWith("docs/specs/") ? defaultSpec : path13.join(specDir, defaultSpec);
|
|
4706
|
+
const exists2 = await FileUtils.exists(fullPath);
|
|
4707
|
+
if (!exists2) {
|
|
4708
|
+
throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${defaultSpec}`);
|
|
4709
|
+
}
|
|
4710
|
+
logger.success(`\u5DF2\u9009\u62E9: ${fullPath}`);
|
|
4711
|
+
return fullPath;
|
|
4712
|
+
}
|
|
4713
|
+
const specs = [];
|
|
4714
|
+
for (const file of specFiles) {
|
|
4715
|
+
const fullPath = path13.join(specDir, file);
|
|
4716
|
+
const content = await FileUtils.read(fullPath);
|
|
4717
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
4718
|
+
specs.push({ file: fullPath, name: file, status });
|
|
4719
|
+
}
|
|
4720
|
+
const activeSpecs = specs.filter(
|
|
4721
|
+
(s) => s.status === "\u5DF2\u62C6\u5206" || s.status === "\u8FDB\u884C\u4E2D" || s.status === "\u5DF2\u5B8C\u6210"
|
|
4722
|
+
);
|
|
4723
|
+
if (activeSpecs.length === 0) {
|
|
4724
|
+
logger.warn("\u6CA1\u6709\u53EF\u9A8C\u6536\u7684 spec \u6587\u4EF6");
|
|
4725
|
+
const { continueAnyway } = await inquirer6.prompt([
|
|
4726
|
+
{
|
|
4727
|
+
type: "confirm",
|
|
4728
|
+
name: "continueAnyway",
|
|
4729
|
+
message: "\u662F\u5426\u7EE7\u7EED\u67E5\u770B\u6240\u6709 spec?",
|
|
4730
|
+
default: true
|
|
4731
|
+
}
|
|
4732
|
+
]);
|
|
4733
|
+
if (!continueAnyway) {
|
|
4734
|
+
process.exit(0);
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
const targetSpecs = activeSpecs.length > 0 ? activeSpecs : specs;
|
|
4738
|
+
const choices = targetSpecs.map((spec) => ({
|
|
4739
|
+
name: `[${spec.status}] ${spec.name}`,
|
|
4740
|
+
value: spec.file,
|
|
4741
|
+
short: spec.name
|
|
4742
|
+
}));
|
|
4743
|
+
const { selectedFile } = await inquirer6.prompt([
|
|
4744
|
+
{
|
|
4745
|
+
type: "list",
|
|
4746
|
+
name: "selectedFile",
|
|
4747
|
+
message: "\u9009\u62E9\u8981\u9A8C\u6536\u7684 spec:",
|
|
4748
|
+
choices
|
|
4749
|
+
}
|
|
4750
|
+
]);
|
|
4751
|
+
logger.success(`\u5DF2\u9009\u62E9: ${selectedFile}`);
|
|
4752
|
+
return selectedFile;
|
|
4753
|
+
}
|
|
4754
|
+
async function selectMilestone2(specFile) {
|
|
4755
|
+
logger.newLine();
|
|
4756
|
+
logger.step("\u6B65\u9AA4 2/4: \u89E3\u6790 milestones...");
|
|
4757
|
+
logger.newLine();
|
|
4758
|
+
const specContent = await FileUtils.read(specFile);
|
|
4759
|
+
const { enhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specContent);
|
|
4760
|
+
if (enhanced.length === 0) {
|
|
4761
|
+
throw new Error("\u8BE5 spec \u5C1A\u672A\u62C6\u5206 milestones\uFF0C\u65E0\u6CD5\u8FDB\u884C\u9A8C\u6536");
|
|
4762
|
+
}
|
|
4763
|
+
const choices = enhanced.map((m, idx) => {
|
|
4764
|
+
const isDone = m.completedCount === m.totalCount;
|
|
4765
|
+
const statusIcon = isDone ? "\u2713" : m.completedCount > 0 ? "\u27F3" : "\u25CB";
|
|
4766
|
+
const progress = `[${m.completedCount}/${m.totalCount}]`;
|
|
4767
|
+
return {
|
|
4768
|
+
name: `${idx + 1}. ${statusIcon} ${progress} ${m.title}`,
|
|
4769
|
+
value: m.title,
|
|
4770
|
+
short: m.title
|
|
4771
|
+
};
|
|
4772
|
+
});
|
|
4773
|
+
const { milestone } = await inquirer6.prompt([
|
|
4774
|
+
{
|
|
4775
|
+
type: "list",
|
|
4776
|
+
name: "milestone",
|
|
4777
|
+
message: "\u9009\u62E9\u8981\u9A8C\u6536\u7684 milestone:",
|
|
4778
|
+
choices
|
|
4779
|
+
}
|
|
4780
|
+
]);
|
|
4781
|
+
return milestone;
|
|
4782
|
+
}
|
|
4783
|
+
async function runAcceptanceCheck(specFile, milestone) {
|
|
4784
|
+
logger.newLine();
|
|
4785
|
+
logger.step("\u6B65\u9AA4 3/4: \u6267\u884C\u9A8C\u6536\u68C0\u67E5...");
|
|
4786
|
+
logger.newLine();
|
|
4787
|
+
const specContent = await FileUtils.read(specFile);
|
|
4788
|
+
const { enhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specFile);
|
|
4789
|
+
const targetMilestone = enhanced.find((m) => m.title === milestone);
|
|
4790
|
+
if (!targetMilestone) {
|
|
4791
|
+
throw new Error(`\u672A\u627E\u5230 milestone: ${milestone}`);
|
|
4792
|
+
}
|
|
4793
|
+
const result = {
|
|
4794
|
+
specFile,
|
|
4795
|
+
milestone,
|
|
4796
|
+
checkTime: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN"),
|
|
4797
|
+
frontendTasks: { total: 0, completed: 0, items: [] },
|
|
4798
|
+
backendTasks: { total: 0, completed: 0, items: [] },
|
|
4799
|
+
apiVerification: { total: 0, verified: 0, items: [] },
|
|
4800
|
+
integrationTasks: { total: 0, completed: 0, items: [] },
|
|
4801
|
+
issues: []
|
|
4802
|
+
};
|
|
4803
|
+
logger.info("\u68C0\u67E5\u524D\u7AEF\u4EFB\u52A1...");
|
|
4804
|
+
for (const todo of targetMilestone.frontendTodos) {
|
|
4805
|
+
const checkResult = await checkFrontendTask(todo);
|
|
4806
|
+
result.frontendTasks.items.push(checkResult);
|
|
4807
|
+
if (checkResult.status === "pass") {
|
|
4808
|
+
result.frontendTasks.completed++;
|
|
4809
|
+
}
|
|
4810
|
+
result.frontendTasks.total++;
|
|
4811
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4812
|
+
logger.info(` ${icon} [F] ${todo.content}${checkResult.filePath ? ` - ${checkResult.filePath}` : ""}`);
|
|
4813
|
+
}
|
|
4814
|
+
logger.info("\u68C0\u67E5\u540E\u7AEF\u4EFB\u52A1...");
|
|
4815
|
+
for (const todo of targetMilestone.backendTodos) {
|
|
4816
|
+
const checkResult = await checkBackendTask(todo);
|
|
4817
|
+
result.backendTasks.items.push(checkResult);
|
|
4818
|
+
if (checkResult.status === "pass") {
|
|
4819
|
+
result.backendTasks.completed++;
|
|
4820
|
+
}
|
|
4821
|
+
result.backendTasks.total++;
|
|
4822
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4823
|
+
logger.info(` ${icon} [B] ${todo.content}${checkResult.filePath ? ` - ${checkResult.filePath}` : ""}`);
|
|
4824
|
+
}
|
|
4825
|
+
logger.info("\u9A8C\u8BC1 API \u5B9E\u73B0...");
|
|
4826
|
+
for (const todo of targetMilestone.backendTodos) {
|
|
4827
|
+
if (todo.apiAssociation) {
|
|
4828
|
+
const checkResult = await verifyApiImplementation(todo);
|
|
4829
|
+
result.apiVerification.items.push(checkResult);
|
|
4830
|
+
if (checkResult.status === "pass") {
|
|
4831
|
+
result.apiVerification.verified++;
|
|
4832
|
+
}
|
|
4833
|
+
result.apiVerification.total++;
|
|
4834
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4835
|
+
logger.info(` ${icon} ${todo.apiAssociation}${checkResult.filePath ? ` - ${checkResult.filePath}` : ""}`);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
logger.info("\u68C0\u67E5\u8054\u8C03\u4EFB\u52A1...");
|
|
4839
|
+
for (const todo of targetMilestone.integrationTodos) {
|
|
4840
|
+
const checkResult = await checkIntegrationTask(todo);
|
|
4841
|
+
result.integrationTasks.items.push(checkResult);
|
|
4842
|
+
if (checkResult.status === "pass") {
|
|
4843
|
+
result.integrationTasks.completed++;
|
|
4844
|
+
}
|
|
4845
|
+
result.integrationTasks.total++;
|
|
4846
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4847
|
+
logger.info(` ${icon} [I] ${todo.content}`);
|
|
4848
|
+
}
|
|
4849
|
+
return result;
|
|
4850
|
+
}
|
|
4851
|
+
async function checkFrontendTask(todo) {
|
|
4852
|
+
const frontendPaths = [
|
|
4853
|
+
`frontend/src/pages/${todo.content}.tsx`,
|
|
4854
|
+
`frontend/src/components/${todo.content}.tsx`,
|
|
4855
|
+
`frontend/src/components/${todo.content.replace(/\s+/g, "")}.tsx`,
|
|
4856
|
+
`frontend/src/views/${todo.content}.vue`,
|
|
4857
|
+
`frontend/src/components/${todo.content}.vue`
|
|
4858
|
+
];
|
|
4859
|
+
for (const fp of frontendPaths) {
|
|
4860
|
+
if (await FileUtils.exists(fp)) {
|
|
4861
|
+
return {
|
|
4862
|
+
description: `[F] ${todo.content}`,
|
|
4863
|
+
status: "pass",
|
|
4864
|
+
details: "\u524D\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4865
|
+
filePath: fp
|
|
4866
|
+
};
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
const pathMatch = todo.content.match(/\s*-\s*(.+?\.(tsx|vue|js|jsx))\s*/);
|
|
4870
|
+
if (pathMatch) {
|
|
4871
|
+
const filePath = pathMatch[1];
|
|
4872
|
+
if (await FileUtils.exists(filePath)) {
|
|
4873
|
+
return {
|
|
4874
|
+
description: `[F] ${todo.content}`,
|
|
4875
|
+
status: "pass",
|
|
4876
|
+
details: "\u524D\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4877
|
+
filePath
|
|
4878
|
+
};
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
return {
|
|
4882
|
+
description: `[F] ${todo.content}`,
|
|
4883
|
+
status: "fail",
|
|
4884
|
+
details: "\u524D\u7AEF\u4EE3\u7801\u4E0D\u5B58\u5728\u6216\u8DEF\u5F84\u65E0\u6CD5\u9A8C\u8BC1"
|
|
4885
|
+
};
|
|
4886
|
+
}
|
|
4887
|
+
async function checkBackendTask(todo) {
|
|
4888
|
+
const backendPaths = [
|
|
4889
|
+
`backend/src/main/java/${todo.content.replace(/\s+/g, "/")}.java`,
|
|
4890
|
+
`backend/src/${todo.content.toLowerCase().replace(/\s+/g, "/")}.java`
|
|
4891
|
+
];
|
|
4892
|
+
for (const fp of backendPaths) {
|
|
4893
|
+
if (await FileUtils.exists(fp)) {
|
|
4894
|
+
return {
|
|
4895
|
+
description: `[B] ${todo.content}`,
|
|
4896
|
+
status: "pass",
|
|
4897
|
+
details: "\u540E\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4898
|
+
filePath: fp
|
|
4899
|
+
};
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
const pathMatch = todo.content.match(/\s*-\s*(.+?\.(java|kt|py|go))\s*/);
|
|
4903
|
+
if (pathMatch) {
|
|
4904
|
+
const filePath = pathMatch[1];
|
|
4905
|
+
if (await FileUtils.exists(filePath)) {
|
|
4906
|
+
return {
|
|
4907
|
+
description: `[B] ${todo.content}`,
|
|
4908
|
+
status: "pass",
|
|
4909
|
+
details: "\u540E\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4910
|
+
filePath
|
|
4911
|
+
};
|
|
4912
|
+
}
|
|
4913
|
+
}
|
|
4914
|
+
return {
|
|
4915
|
+
description: `[B] ${todo.content}`,
|
|
4916
|
+
status: "fail",
|
|
4917
|
+
details: "\u540E\u7AEF\u4EE3\u7801\u4E0D\u5B58\u5728\u6216\u8DEF\u5F84\u65E0\u6CD5\u9A8C\u8BC1"
|
|
4918
|
+
};
|
|
4919
|
+
}
|
|
4920
|
+
async function verifyApiImplementation(todo) {
|
|
4921
|
+
if (!todo.apiAssociation) {
|
|
4922
|
+
return {
|
|
4923
|
+
description: todo.apiAssociation || "API \u9A8C\u8BC1",
|
|
4924
|
+
status: "pending",
|
|
4925
|
+
details: "\u65E0 API \u5173\u8054\u4FE1\u606F"
|
|
4926
|
+
};
|
|
4927
|
+
}
|
|
4928
|
+
const apiMatch = todo.apiAssociation.match(/^(GET|POST|PUT|DELETE|PATCH)\s+\/(.+)$/);
|
|
4929
|
+
if (!apiMatch) {
|
|
4930
|
+
return {
|
|
4931
|
+
description: todo.apiAssociation,
|
|
4932
|
+
status: "pending",
|
|
4933
|
+
details: "\u65E0\u6CD5\u89E3\u6790 API \u683C\u5F0F"
|
|
4934
|
+
};
|
|
4935
|
+
}
|
|
4936
|
+
const method = apiMatch[1];
|
|
4937
|
+
const apiPath = apiMatch[2];
|
|
4938
|
+
const controllerPaths = [
|
|
4939
|
+
`backend/src/main/java/**/*Controller.java`,
|
|
4940
|
+
`backend/src/**/*Controller.java`
|
|
4941
|
+
];
|
|
4942
|
+
const controllerFiles = await FileUtils.findFiles("**/*Controller.java", "backend/src");
|
|
4943
|
+
for (const cf of controllerFiles) {
|
|
4944
|
+
const content = await FileUtils.read(cf);
|
|
4945
|
+
if (content.includes(apiPath) || content.includes(`"${apiPath}"`)) {
|
|
4946
|
+
const methodAnnotations = {
|
|
4947
|
+
GET: ["@GetMapping", "@RequestMapping(method = RequestMethod.GET)"],
|
|
4948
|
+
POST: ["@PostMapping", "@RequestMapping(method = RequestMethod.POST)"],
|
|
4949
|
+
PUT: ["@PutMapping", "@RequestMapping(method = RequestMethod.PUT)"],
|
|
4950
|
+
DELETE: ["@DeleteMapping", "@RequestMapping(method = RequestMethod.DELETE)"],
|
|
4951
|
+
PATCH: ["@PatchMapping", "@RequestMapping(method = RequestMethod.PATCH)"]
|
|
4952
|
+
};
|
|
4953
|
+
for (const annotation of methodAnnotations[method] || []) {
|
|
4954
|
+
if (content.includes(annotation)) {
|
|
4955
|
+
return {
|
|
4956
|
+
description: todo.apiAssociation,
|
|
4957
|
+
status: "pass",
|
|
4958
|
+
details: "API Controller \u5B9E\u73B0\u5B58\u5728",
|
|
4959
|
+
filePath: cf
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
return {
|
|
4966
|
+
description: todo.apiAssociation,
|
|
4967
|
+
status: "fail",
|
|
4968
|
+
details: "API Controller \u672A\u5B9E\u73B0"
|
|
4969
|
+
};
|
|
4970
|
+
}
|
|
4971
|
+
async function checkIntegrationTask(todo) {
|
|
4972
|
+
const integrationEvidence = [
|
|
4973
|
+
"**/*integration*.test.*",
|
|
4974
|
+
"**/*e2e*.test.*",
|
|
4975
|
+
"**/*IT.java",
|
|
4976
|
+
"docs/integration-test-results.md",
|
|
4977
|
+
"docs/session-logs/**"
|
|
4978
|
+
];
|
|
4979
|
+
const sessionDir = "docs/sessions";
|
|
4980
|
+
if (await FileUtils.exists(sessionDir)) {
|
|
4981
|
+
const sessionFiles = await FileUtils.findFiles("*.md", sessionDir);
|
|
4982
|
+
const hasRecentSession = sessionFiles.some(
|
|
4983
|
+
(sf) => sf.includes("integration") || sf.includes("\u8054\u8C03")
|
|
4984
|
+
);
|
|
4985
|
+
if (hasRecentSession) {
|
|
4986
|
+
return {
|
|
4987
|
+
description: `[I] ${todo.content}`,
|
|
4988
|
+
status: "pass",
|
|
4989
|
+
details: "\u8054\u8C03\u8BC1\u636E\u5B58\u5728\uFF08session \u65E5\u5FD7\uFF09"
|
|
4990
|
+
};
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
return {
|
|
4994
|
+
description: `[I] ${todo.content}`,
|
|
4995
|
+
status: "pending",
|
|
4996
|
+
details: "\u5EFA\u8BAE\u4EBA\u5DE5\u9A8C\u8BC1\u8054\u8C03\u7ED3\u679C"
|
|
4997
|
+
};
|
|
4998
|
+
}
|
|
4999
|
+
async function generateAcceptanceReport(result) {
|
|
5000
|
+
logger.newLine();
|
|
5001
|
+
logger.step("\u6B65\u9AA4 4/4: \u751F\u6210\u9A8C\u6536\u62A5\u544A...");
|
|
5002
|
+
logger.newLine();
|
|
5003
|
+
const reportDir = "docs/acceptance-reports";
|
|
5004
|
+
await FileUtils.ensureDir(reportDir);
|
|
5005
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5006
|
+
const specName = path13.basename(result.specFile, ".md");
|
|
5007
|
+
const milestoneSafe = result.milestone.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5008
|
+
const reportFile = path13.join(reportDir, `${timestamp}_${specName}_${milestoneSafe}.md`);
|
|
5009
|
+
const report = generateMarkdownReport(result);
|
|
5010
|
+
await FileUtils.write(reportFile, report);
|
|
5011
|
+
logger.success(`\u9A8C\u6536\u62A5\u544A\u5DF2\u751F\u6210: ${reportFile}`);
|
|
5012
|
+
logger.newLine();
|
|
5013
|
+
console.log(generateConsoleReport(result));
|
|
5014
|
+
}
|
|
5015
|
+
function generateMarkdownReport(result) {
|
|
5016
|
+
const lines = [];
|
|
5017
|
+
lines.push("# \u9A8C\u6536\u62A5\u544A");
|
|
5018
|
+
lines.push("");
|
|
5019
|
+
lines.push(`**Spec \u6587\u4EF6**: ${result.specFile}`);
|
|
5020
|
+
lines.push(`**Milestone**: ${result.milestone}`);
|
|
5021
|
+
lines.push(`**\u9A8C\u6536\u65F6\u95F4**: ${result.checkTime}`);
|
|
5022
|
+
lines.push("");
|
|
5023
|
+
lines.push("## \u68C0\u67E5\u7ED3\u679C\u6C47\u603B");
|
|
5024
|
+
lines.push("");
|
|
5025
|
+
lines.push("| \u68C0\u67E5\u9879 | \u72B6\u6001 |");
|
|
5026
|
+
lines.push("|--------|------|");
|
|
5027
|
+
lines.push(
|
|
5028
|
+
`| \u524D\u7AEF\u4EFB\u52A1 | ${result.frontendTasks.completed}/${result.frontendTasks.total} \u5B8C\u6210 |`
|
|
5029
|
+
);
|
|
5030
|
+
lines.push(
|
|
5031
|
+
`| \u540E\u7AEF\u4EFB\u52A1 | ${result.backendTasks.completed}/${result.backendTasks.total} \u5B8C\u6210 |`
|
|
5032
|
+
);
|
|
5033
|
+
lines.push(`| API \u9A8C\u8BC1 | ${result.apiVerification.verified}/${result.apiVerification.total} \u5B9E\u73B0 |`);
|
|
5034
|
+
lines.push(
|
|
5035
|
+
`| \u8054\u8C03\u4EFB\u52A1 | ${result.integrationTasks.completed}/${result.integrationTasks.total} \u5B8C\u6210 |`
|
|
5036
|
+
);
|
|
5037
|
+
lines.push("");
|
|
5038
|
+
lines.push("## \u8BE6\u7EC6\u8BB0\u5F55");
|
|
5039
|
+
lines.push("");
|
|
5040
|
+
lines.push("### \u524D\u7AEF\u4EFB\u52A1");
|
|
5041
|
+
lines.push("");
|
|
5042
|
+
for (const item of result.frontendTasks.items) {
|
|
5043
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u25CB";
|
|
5044
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""} ${item.filePath ? `(\`${item.filePath}\`)` : ""}`);
|
|
5045
|
+
}
|
|
5046
|
+
lines.push("");
|
|
5047
|
+
lines.push("### \u540E\u7AEF\u4EFB\u52A1");
|
|
5048
|
+
lines.push("");
|
|
5049
|
+
for (const item of result.backendTasks.items) {
|
|
5050
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u25CB";
|
|
5051
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""} ${item.filePath ? `(\`${item.filePath}\`)` : ""}`);
|
|
5052
|
+
}
|
|
5053
|
+
lines.push("");
|
|
5054
|
+
lines.push("### API \u9A8C\u8BC1");
|
|
5055
|
+
lines.push("");
|
|
5056
|
+
for (const item of result.apiVerification.items) {
|
|
5057
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u25CB";
|
|
5058
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""} ${item.filePath ? `(\`${item.filePath}\`)` : ""}`);
|
|
5059
|
+
}
|
|
5060
|
+
lines.push("");
|
|
5061
|
+
lines.push("### \u8054\u8C03\u4EFB\u52A1");
|
|
5062
|
+
lines.push("");
|
|
5063
|
+
for (const item of result.integrationTasks.items) {
|
|
5064
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u26A0";
|
|
5065
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""}`);
|
|
5066
|
+
}
|
|
5067
|
+
lines.push("");
|
|
5068
|
+
if (result.issues.length > 0) {
|
|
5069
|
+
lines.push("## \u53D1\u73B0\u7684\u95EE\u9898");
|
|
5070
|
+
lines.push("");
|
|
5071
|
+
lines.push("| \u95EE\u9898 | \u7C7B\u578B | \u4E25\u91CD\u7A0B\u5EA6 | \u72B6\u6001 |");
|
|
5072
|
+
lines.push("|------|------|---------|------|");
|
|
5073
|
+
for (const issue of result.issues) {
|
|
5074
|
+
lines.push(`| ${issue.title} | ${issue.type} | ${issue.severity} | ${issue.status} |`);
|
|
5075
|
+
}
|
|
5076
|
+
lines.push("");
|
|
5077
|
+
lines.push("**\u64CD\u4F5C**:");
|
|
5078
|
+
lines.push(`- \u8FD0\u884C \`team-cli bugfix ${result.specFile}\` \u67E5\u770B\u8BE6\u60C5`);
|
|
5079
|
+
lines.push("- \u6216\u8FD0\u884C `team-cli hotfix` \u7ACB\u5373\u4FEE\u590D");
|
|
5080
|
+
lines.push("");
|
|
5081
|
+
}
|
|
5082
|
+
lines.push("## \u4E0B\u4E00\u6B65\u5EFA\u8BAE");
|
|
5083
|
+
lines.push("");
|
|
5084
|
+
if (result.issues.length > 0) {
|
|
5085
|
+
lines.push("- [ ] \u4FEE\u590D\u53D1\u73B0\u7684\u95EE\u9898");
|
|
5086
|
+
}
|
|
5087
|
+
lines.push("- [ ] \u8FD0\u884C `team-cli dev` \u7EE7\u7EED\u5F00\u53D1");
|
|
5088
|
+
lines.push("- [ ] \u8FD0\u884C `team-cli accept` \u91CD\u65B0\u9A8C\u6536");
|
|
5089
|
+
lines.push("");
|
|
5090
|
+
return lines.join("\n");
|
|
5091
|
+
}
|
|
5092
|
+
function generateConsoleReport(result) {
|
|
5093
|
+
const lines = [];
|
|
5094
|
+
lines.push("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
5095
|
+
lines.push("\u2502 \u9A8C \u6536 \u7ED3 \u679C \u6458 \u8981 \u2502");
|
|
5096
|
+
lines.push("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
5097
|
+
lines.push(`\u2502 Spec: ${result.specFile.padEnd(40)}\u2502`);
|
|
5098
|
+
lines.push(`\u2502 Milestone: ${result.milestone.padEnd(37)}\u2502`);
|
|
5099
|
+
lines.push("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
5100
|
+
lines.push(
|
|
5101
|
+
`\u2502 \u524D\u7AEF\u4EFB\u52A1: ${result.frontendTasks.completed}/${result.frontendTasks.total} \u5B8C\u6210`.padEnd(47) + "\u2502"
|
|
5102
|
+
);
|
|
5103
|
+
lines.push(
|
|
5104
|
+
`\u2502 \u540E\u7AEF\u4EFB\u52A1: ${result.backendTasks.completed}/${result.backendTasks.total} \u5B8C\u6210`.padEnd(47) + "\u2502"
|
|
5105
|
+
);
|
|
5106
|
+
lines.push(
|
|
5107
|
+
`\u2502 API \u9A8C\u8BC1: ${result.apiVerification.verified}/${result.apiVerification.total} \u5B9E\u73B0`.padEnd(47) + "\u2502"
|
|
5108
|
+
);
|
|
5109
|
+
lines.push(
|
|
5110
|
+
`\u2502 \u8054\u8C03\u4EFB\u52A1: ${result.integrationTasks.completed}/${result.integrationTasks.total} \u5B8C\u6210`.padEnd(47) + "\u2502"
|
|
5111
|
+
);
|
|
5112
|
+
lines.push("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
5113
|
+
const totalIssues = result.issues.length;
|
|
5114
|
+
if (totalIssues > 0) {
|
|
5115
|
+
lines.push(`\u2502 \u26A0 \u53D1\u73B0\u95EE\u9898: ${totalIssues} \u4E2A`.padEnd(47) + "\u2502");
|
|
5116
|
+
} else {
|
|
5117
|
+
lines.push(`\u2502 \u2713 \u9A8C\u6536\u901A\u8FC7`.padEnd(47) + "\u2502");
|
|
5118
|
+
}
|
|
5119
|
+
lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
5120
|
+
return lines.join("\n");
|
|
5121
|
+
}
|
|
5122
|
+
async function handleIssues(result) {
|
|
5123
|
+
logger.newLine();
|
|
5124
|
+
logger.warn(`\u53D1\u73B0 ${result.issues.length} \u4E2A\u95EE\u9898\u9700\u8981\u5904\u7406`);
|
|
5125
|
+
const bugfixDir = "docs/bugfixes";
|
|
5126
|
+
await FileUtils.ensureDir(bugfixDir);
|
|
5127
|
+
for (const issue of result.issues) {
|
|
5128
|
+
const bugfixFile = path13.join(
|
|
5129
|
+
bugfixDir,
|
|
5130
|
+
`${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}_${issue.id}.md`
|
|
5131
|
+
);
|
|
5132
|
+
const bugfixContent = generateBugfixContent(result, issue);
|
|
5133
|
+
await FileUtils.write(bugfixFile, bugfixContent);
|
|
5134
|
+
logger.info(` \u5DF2\u521B\u5EFA Bugfix \u8BB0\u5F55: ${bugfixFile}`);
|
|
5135
|
+
}
|
|
5136
|
+
logger.newLine();
|
|
5137
|
+
const { fixNow } = await inquirer6.prompt([
|
|
5138
|
+
{
|
|
5139
|
+
type: "confirm",
|
|
5140
|
+
name: "fixNow",
|
|
5141
|
+
message: "\u662F\u5426\u7ACB\u5373\u4FEE\u590D\u53D1\u73B0\u7684\u95EE\u9898?",
|
|
5142
|
+
default: false
|
|
5143
|
+
}
|
|
5144
|
+
]);
|
|
5145
|
+
if (fixNow) {
|
|
5146
|
+
logger.info("\u8BF7\u8FD0\u884C `team-cli hotfix` \u7ACB\u5373\u4FEE\u590D\u95EE\u9898");
|
|
5147
|
+
} else {
|
|
5148
|
+
logger.info(`\u95EE\u9898\u8BB0\u5F55\u5DF2\u4FDD\u5B58\u5230 ${bugfixDir} \u76EE\u5F55`);
|
|
5149
|
+
logger.info("\u8FD0\u884C `team-cli bugfix` \u67E5\u770B\u6240\u6709\u95EE\u9898\u8BB0\u5F55");
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
function generateBugfixContent(result, issue) {
|
|
5153
|
+
const lines = [];
|
|
5154
|
+
lines.push("# Bugfix \u8BB0\u5F55");
|
|
5155
|
+
lines.push("");
|
|
5156
|
+
lines.push(`**ID**: ${issue.id}`);
|
|
5157
|
+
lines.push(`**\u521B\u5EFA\u65F6\u95F4**: ${issue.createdAt}`);
|
|
5158
|
+
lines.push(`**\u72B6\u6001**: ${issue.status}`);
|
|
5159
|
+
lines.push("");
|
|
5160
|
+
lines.push("## \u95EE\u9898\u63CF\u8FF0");
|
|
5161
|
+
lines.push("");
|
|
5162
|
+
lines.push(issue.description);
|
|
5163
|
+
lines.push("");
|
|
5164
|
+
lines.push("## \u6240\u5C5E\u8303\u56F4");
|
|
5165
|
+
lines.push("");
|
|
5166
|
+
lines.push(`- Spec: ${result.specFile}`);
|
|
5167
|
+
lines.push(`- Milestone: ${result.milestone}`);
|
|
5168
|
+
if (issue.todo) {
|
|
5169
|
+
lines.push(`- Todo: ${issue.todo}`);
|
|
5170
|
+
}
|
|
5171
|
+
lines.push("");
|
|
5172
|
+
lines.push("## \u4E25\u91CD\u7A0B\u5EA6");
|
|
5173
|
+
lines.push("");
|
|
5174
|
+
lines.push(`- ${issue.severity.toUpperCase()}`);
|
|
5175
|
+
lines.push("");
|
|
5176
|
+
lines.push("## \u95EE\u9898\u7C7B\u578B");
|
|
5177
|
+
lines.push("");
|
|
5178
|
+
lines.push(`- ${issue.type}`);
|
|
5179
|
+
lines.push("");
|
|
5180
|
+
lines.push("## \u89E3\u51B3\u65B9\u6848");
|
|
5181
|
+
lines.push("");
|
|
5182
|
+
lines.push("<!-- TODO: \u586B\u5199\u89E3\u51B3\u65B9\u6848 -->");
|
|
5183
|
+
lines.push("");
|
|
5184
|
+
lines.push("## \u9A8C\u8BC1\u6B65\u9AA4");
|
|
5185
|
+
lines.push("");
|
|
5186
|
+
lines.push("1. ");
|
|
5187
|
+
lines.push("2. ");
|
|
5188
|
+
lines.push("3. ");
|
|
5189
|
+
lines.push("");
|
|
5190
|
+
return lines.join("\n");
|
|
5191
|
+
}
|
|
5192
|
+
async function syncSpecStatus(specFile, result) {
|
|
5193
|
+
const content = await FileUtils.read(specFile);
|
|
5194
|
+
const lines = content.split("\n");
|
|
5195
|
+
let inTargetMilestone = false;
|
|
5196
|
+
let milestoneIndex = -1;
|
|
5197
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5198
|
+
const line = lines[i];
|
|
5199
|
+
if (line.includes(result.milestone)) {
|
|
5200
|
+
inTargetMilestone = true;
|
|
5201
|
+
milestoneIndex = i;
|
|
5202
|
+
continue;
|
|
5203
|
+
}
|
|
5204
|
+
if (inTargetMilestone && line.match(/^###\s+Milestone/)) {
|
|
5205
|
+
break;
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
if (milestoneIndex !== -1) {
|
|
5209
|
+
let completedTasks = 0;
|
|
5210
|
+
let totalTasks = 0;
|
|
5211
|
+
for (let i = milestoneIndex + 1; i < lines.length; i++) {
|
|
5212
|
+
const line = lines[i];
|
|
5213
|
+
if (line.match(/^###\s+Milestone/)) {
|
|
5214
|
+
break;
|
|
5215
|
+
}
|
|
5216
|
+
if (line.match(/^-\s+\[/)) {
|
|
5217
|
+
totalTasks++;
|
|
5218
|
+
if (line.match(/^-\s+\[x\]/)) {
|
|
5219
|
+
completedTasks++;
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
const progressLine = `${result.milestone} [${completedTasks}/${totalTasks}]`;
|
|
5224
|
+
const linesBefore = lines.slice(0, milestoneIndex);
|
|
5225
|
+
const linesAfter = lines.slice(milestoneIndex + 1);
|
|
5226
|
+
if (!lines[milestoneIndex].includes(`[${completedTasks}/${totalTasks}]`)) {
|
|
5227
|
+
const updatedMilestoneLine = lines[milestoneIndex].replace(
|
|
5228
|
+
/(\s*\[\d+\/\d+\])?$/,
|
|
5229
|
+
` [${completedTasks}/${totalTasks}]`
|
|
5230
|
+
);
|
|
5231
|
+
lines[milestoneIndex] = updatedMilestoneLine;
|
|
5232
|
+
await FileUtils.write(specFile, lines.join("\n"));
|
|
5233
|
+
logger.info(`\u5DF2\u540C\u6B65\u91CC\u7A0B\u7891\u8FDB\u5EA6: [${completedTasks}/${totalTasks}]`);
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
5236
|
+
}
|
|
5237
|
+
var acceptCommand;
|
|
5238
|
+
var init_accept = __esm({
|
|
5239
|
+
"src/commands/accept.ts"() {
|
|
5240
|
+
"use strict";
|
|
5241
|
+
init_esm_shims();
|
|
5242
|
+
init_utils();
|
|
5243
|
+
init_logger();
|
|
5244
|
+
acceptCommand = new Command7("accept").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u9A8C\u6536\u529F\u80FD - \u8D70\u67E5\u6240\u6709\u9700\u6C42\uFF0C\u9A8C\u8BC1\u8054\u8C03\u662F\u5426\u5B8C\u6210").action(async (specFile) => {
|
|
5245
|
+
try {
|
|
5246
|
+
logger.header("\u9A8C\u6536\u6A21\u5F0F");
|
|
5247
|
+
logger.newLine();
|
|
5248
|
+
const hasTechStack = await FileUtils.exists("TECH_STACK.md");
|
|
5249
|
+
if (!hasTechStack) {
|
|
5250
|
+
logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
|
|
5251
|
+
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
5252
|
+
process.exit(1);
|
|
5253
|
+
}
|
|
5254
|
+
logger.success("\u68C0\u6D4B\u5230\u9879\u76EE\u4E0A\u4E0B\u6587");
|
|
5255
|
+
const selectedSpec = await selectSpec2(specFile);
|
|
5256
|
+
const selectedMilestone = await selectMilestone2(selectedSpec);
|
|
5257
|
+
const result = await runAcceptanceCheck(selectedSpec, selectedMilestone);
|
|
5258
|
+
await generateAcceptanceReport(result);
|
|
5259
|
+
if (result.issues.length > 0) {
|
|
5260
|
+
await handleIssues(result);
|
|
5261
|
+
}
|
|
5262
|
+
await syncSpecStatus(selectedSpec, result);
|
|
5263
|
+
logger.header("\u9A8C\u6536\u5B8C\u6210!");
|
|
5264
|
+
logger.newLine();
|
|
5265
|
+
} catch (error) {
|
|
5266
|
+
logger.error(`\u9A8C\u6536\u5931\u8D25: ${error.message}`);
|
|
5267
|
+
if (process.env.DEBUG) {
|
|
5268
|
+
console.error(error);
|
|
5269
|
+
}
|
|
5270
|
+
process.exit(1);
|
|
5271
|
+
}
|
|
5272
|
+
});
|
|
5273
|
+
}
|
|
5274
|
+
});
|
|
5275
|
+
|
|
5276
|
+
// src/commands/lint.ts
|
|
5277
|
+
import { Command as Command8 } from "commander";
|
|
4472
5278
|
import { execa as execa3 } from "execa";
|
|
4473
5279
|
var lintCommand;
|
|
4474
5280
|
var init_lint = __esm({
|
|
@@ -4477,7 +5283,7 @@ var init_lint = __esm({
|
|
|
4477
5283
|
init_esm_shims();
|
|
4478
5284
|
init_utils();
|
|
4479
5285
|
init_logger();
|
|
4480
|
-
lintCommand = new
|
|
5286
|
+
lintCommand = new Command8("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D\u95EE\u9898").option("--no-type-check", "\u8DF3\u8FC7 TypeScript \u7C7B\u578B\u68C0\u67E5").description("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5 (\u524D\u7AEF + \u540E\u7AEF)").action(async (options) => {
|
|
4481
5287
|
try {
|
|
4482
5288
|
logger.header("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5");
|
|
4483
5289
|
logger.newLine();
|
|
@@ -4611,8 +5417,8 @@ var init_lint = __esm({
|
|
|
4611
5417
|
});
|
|
4612
5418
|
|
|
4613
5419
|
// src/commands/status.ts
|
|
4614
|
-
import { Command as
|
|
4615
|
-
import
|
|
5420
|
+
import { Command as Command9 } from "commander";
|
|
5421
|
+
import path14 from "path";
|
|
4616
5422
|
async function displayProjectInfo() {
|
|
4617
5423
|
logger.info("\u9879\u76EE\u4FE1\u606F:");
|
|
4618
5424
|
logger.newLine();
|
|
@@ -4648,7 +5454,7 @@ async function displayFeatureInventory() {
|
|
|
4648
5454
|
}
|
|
4649
5455
|
const inventory = [];
|
|
4650
5456
|
for (const file of specs) {
|
|
4651
|
-
const filePath =
|
|
5457
|
+
const filePath = path14.join(specDir, file);
|
|
4652
5458
|
const content = await FileUtils.read(filePath);
|
|
4653
5459
|
const status = SpecUtils.parseSpecStatus(content);
|
|
4654
5460
|
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
@@ -4708,7 +5514,7 @@ async function displayRecentActivity() {
|
|
|
4708
5514
|
}
|
|
4709
5515
|
const sorted = files.sort().reverse().slice(0, 5);
|
|
4710
5516
|
for (const file of sorted) {
|
|
4711
|
-
const filePath =
|
|
5517
|
+
const filePath = path14.join(sessionDir, file);
|
|
4712
5518
|
const stat = await FileUtils.read(filePath);
|
|
4713
5519
|
const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
|
|
4714
5520
|
const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
|
|
@@ -4729,7 +5535,7 @@ var init_status = __esm({
|
|
|
4729
5535
|
init_esm_shims();
|
|
4730
5536
|
init_utils();
|
|
4731
5537
|
init_logger();
|
|
4732
|
-
statusCommand = new
|
|
5538
|
+
statusCommand = new Command9("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
|
|
4733
5539
|
try {
|
|
4734
5540
|
logger.header("\u9879\u76EE\u72B6\u6001");
|
|
4735
5541
|
logger.newLine();
|
|
@@ -4755,14 +5561,14 @@ var init_status = __esm({
|
|
|
4755
5561
|
});
|
|
4756
5562
|
|
|
4757
5563
|
// src/commands/detect-deps.ts
|
|
4758
|
-
import { Command as
|
|
4759
|
-
import
|
|
4760
|
-
import
|
|
5564
|
+
import { Command as Command10 } from "commander";
|
|
5565
|
+
import path15 from "path";
|
|
5566
|
+
import inquirer7 from "inquirer";
|
|
4761
5567
|
async function detectDependencies(specFile) {
|
|
4762
5568
|
logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
|
|
4763
5569
|
const projectDir = ".";
|
|
4764
5570
|
const allDeps = /* @__PURE__ */ new Set();
|
|
4765
|
-
const backendDir =
|
|
5571
|
+
const backendDir = path15.join(projectDir, "backend");
|
|
4766
5572
|
const backendExists = await FileUtils.exists(backendDir);
|
|
4767
5573
|
if (backendExists) {
|
|
4768
5574
|
const apiDeps = await scanBackendApiCalls(backendDir);
|
|
@@ -4772,7 +5578,7 @@ async function detectDependencies(specFile) {
|
|
|
4772
5578
|
const serviceDeps = await scanBackendServiceRefs(backendDir);
|
|
4773
5579
|
serviceDeps.forEach((d) => allDeps.add(d));
|
|
4774
5580
|
}
|
|
4775
|
-
const frontendDir =
|
|
5581
|
+
const frontendDir = path15.join(projectDir, "frontend");
|
|
4776
5582
|
const frontendExists = await FileUtils.exists(frontendDir);
|
|
4777
5583
|
if (frontendExists) {
|
|
4778
5584
|
const frontendDeps = await scanFrontendApiCalls(frontendDir);
|
|
@@ -4798,7 +5604,7 @@ async function detectDependencies(specFile) {
|
|
|
4798
5604
|
logger.step(`- ${spec}`);
|
|
4799
5605
|
}
|
|
4800
5606
|
logger.newLine();
|
|
4801
|
-
const answers = await
|
|
5607
|
+
const answers = await inquirer7.prompt([
|
|
4802
5608
|
{
|
|
4803
5609
|
type: "confirm",
|
|
4804
5610
|
name: "autoUpdate",
|
|
@@ -4813,11 +5619,11 @@ async function detectDependencies(specFile) {
|
|
|
4813
5619
|
}
|
|
4814
5620
|
async function scanBackendApiCalls(backendDir) {
|
|
4815
5621
|
const deps = [];
|
|
4816
|
-
const srcDir =
|
|
5622
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4817
5623
|
try {
|
|
4818
5624
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4819
5625
|
for (const file of javaFiles) {
|
|
4820
|
-
const filePath =
|
|
5626
|
+
const filePath = path15.join(srcDir, file);
|
|
4821
5627
|
const content = await FileUtils.read(filePath);
|
|
4822
5628
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
4823
5629
|
let match;
|
|
@@ -4835,11 +5641,11 @@ async function scanBackendApiCalls(backendDir) {
|
|
|
4835
5641
|
}
|
|
4836
5642
|
async function scanBackendEntityRelations(backendDir) {
|
|
4837
5643
|
const deps = [];
|
|
4838
|
-
const srcDir =
|
|
5644
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4839
5645
|
try {
|
|
4840
5646
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4841
5647
|
for (const file of javaFiles) {
|
|
4842
|
-
const filePath =
|
|
5648
|
+
const filePath = path15.join(srcDir, file);
|
|
4843
5649
|
const content = await FileUtils.read(filePath);
|
|
4844
5650
|
if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
|
|
4845
5651
|
const typeRegex = /type\s*=\s*(\w+)/g;
|
|
@@ -4855,11 +5661,11 @@ async function scanBackendEntityRelations(backendDir) {
|
|
|
4855
5661
|
}
|
|
4856
5662
|
async function scanBackendServiceRefs(backendDir) {
|
|
4857
5663
|
const deps = [];
|
|
4858
|
-
const srcDir =
|
|
5664
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4859
5665
|
try {
|
|
4860
5666
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4861
5667
|
for (const file of javaFiles) {
|
|
4862
|
-
const filePath =
|
|
5668
|
+
const filePath = path15.join(srcDir, file);
|
|
4863
5669
|
const content = await FileUtils.read(filePath);
|
|
4864
5670
|
const serviceRegex = /private\s+(\w+)Service/g;
|
|
4865
5671
|
let match;
|
|
@@ -4875,11 +5681,11 @@ async function scanBackendServiceRefs(backendDir) {
|
|
|
4875
5681
|
}
|
|
4876
5682
|
async function scanFrontendApiCalls(frontendDir) {
|
|
4877
5683
|
const deps = [];
|
|
4878
|
-
const srcDir =
|
|
5684
|
+
const srcDir = path15.join(frontendDir, "src");
|
|
4879
5685
|
try {
|
|
4880
5686
|
const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
|
|
4881
5687
|
for (const file of tsFiles) {
|
|
4882
|
-
const filePath =
|
|
5688
|
+
const filePath = path15.join(srcDir, file);
|
|
4883
5689
|
const content = await FileUtils.read(filePath);
|
|
4884
5690
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
4885
5691
|
let match;
|
|
@@ -4970,7 +5776,7 @@ var init_detect_deps = __esm({
|
|
|
4970
5776
|
init_esm_shims();
|
|
4971
5777
|
init_utils();
|
|
4972
5778
|
init_logger();
|
|
4973
|
-
detectDepsCommand = new
|
|
5779
|
+
detectDepsCommand = new Command10("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
|
|
4974
5780
|
try {
|
|
4975
5781
|
logger.header("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
4976
5782
|
logger.newLine();
|
|
@@ -4996,7 +5802,7 @@ var init_detect_deps = __esm({
|
|
|
4996
5802
|
process.exit(1);
|
|
4997
5803
|
}
|
|
4998
5804
|
for (const spec of specs) {
|
|
4999
|
-
const specPath =
|
|
5805
|
+
const specPath = path15.join(specsDir, spec);
|
|
5000
5806
|
logger.step(`\u5904\u7406: ${spec}`);
|
|
5001
5807
|
await detectDependencies(specPath);
|
|
5002
5808
|
logger.newLine();
|
|
@@ -5022,11 +5828,11 @@ var init_detect_deps = __esm({
|
|
|
5022
5828
|
});
|
|
5023
5829
|
|
|
5024
5830
|
// src/commands/sync-memory.ts
|
|
5025
|
-
import { Command as
|
|
5026
|
-
import
|
|
5831
|
+
import { Command as Command11 } from "commander";
|
|
5832
|
+
import path16 from "path";
|
|
5027
5833
|
async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
5028
5834
|
logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
|
|
5029
|
-
const specsDir =
|
|
5835
|
+
const specsDir = path16.join(projectDir, "docs/specs");
|
|
5030
5836
|
const exists = await FileUtils.exists(specsDir);
|
|
5031
5837
|
if (!exists) {
|
|
5032
5838
|
return;
|
|
@@ -5041,7 +5847,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5041
5847
|
for (const specFile of specs) {
|
|
5042
5848
|
const name = specFile.replace(".md", "");
|
|
5043
5849
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
5044
|
-
const specPath =
|
|
5850
|
+
const specPath = path16.join(specsDir, specFile);
|
|
5045
5851
|
const content = await FileUtils.read(specPath);
|
|
5046
5852
|
const status = SpecUtils.parseSpecStatus(content);
|
|
5047
5853
|
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
@@ -5060,7 +5866,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5060
5866
|
}
|
|
5061
5867
|
async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
5062
5868
|
logger.step("\u540C\u6B65 API \u5217\u8868...");
|
|
5063
|
-
const backendDir =
|
|
5869
|
+
const backendDir = path16.join(projectDir, "backend");
|
|
5064
5870
|
const exists = await FileUtils.exists(backendDir);
|
|
5065
5871
|
if (!exists) {
|
|
5066
5872
|
return;
|
|
@@ -5070,13 +5876,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
|
5070
5876
|
lines.push("");
|
|
5071
5877
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
|
|
5072
5878
|
lines.push("");
|
|
5073
|
-
const srcDir =
|
|
5879
|
+
const srcDir = path16.join(backendDir, "src");
|
|
5074
5880
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5075
5881
|
if (controllers.length === 0) {
|
|
5076
5882
|
lines.push("\u6682\u65E0 API");
|
|
5077
5883
|
} else {
|
|
5078
5884
|
for (const controllerFile of controllers) {
|
|
5079
|
-
const controllerPath =
|
|
5885
|
+
const controllerPath = path16.join(srcDir, controllerFile);
|
|
5080
5886
|
const controllerName = controllerFile.replace(".java", "");
|
|
5081
5887
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
5082
5888
|
lines.push(`### ${module} \u6A21\u5757`);
|
|
@@ -5153,7 +5959,7 @@ function extractMethodComment(content, methodName) {
|
|
|
5153
5959
|
}
|
|
5154
5960
|
async function syncDataModels(aiMemoryFile, projectDir) {
|
|
5155
5961
|
logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
|
|
5156
|
-
const backendDir =
|
|
5962
|
+
const backendDir = path16.join(projectDir, "backend");
|
|
5157
5963
|
const exists = await FileUtils.exists(backendDir);
|
|
5158
5964
|
if (!exists) {
|
|
5159
5965
|
return;
|
|
@@ -5163,7 +5969,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
5163
5969
|
lines.push("");
|
|
5164
5970
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
|
|
5165
5971
|
lines.push("");
|
|
5166
|
-
const srcDir =
|
|
5972
|
+
const srcDir = path16.join(backendDir, "src");
|
|
5167
5973
|
const entities = await FileUtils.findFiles("*Entity.java", srcDir);
|
|
5168
5974
|
if (entities.length === 0) {
|
|
5169
5975
|
lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
|
|
@@ -5171,7 +5977,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
5171
5977
|
lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
|
|
5172
5978
|
lines.push("|------|------|------|------|");
|
|
5173
5979
|
for (const entityFile of entities) {
|
|
5174
|
-
const entityPath =
|
|
5980
|
+
const entityPath = path16.join(srcDir, entityFile);
|
|
5175
5981
|
const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
|
|
5176
5982
|
const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
5177
5983
|
const content = await FileUtils.read(entityPath);
|
|
@@ -5273,15 +6079,15 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
|
5273
6079
|
await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
|
|
5274
6080
|
}
|
|
5275
6081
|
function extractRepoName(repository) {
|
|
5276
|
-
let
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
const parts =
|
|
6082
|
+
let path21 = repository;
|
|
6083
|
+
path21 = path21.replace(/^https?:\/\//, "");
|
|
6084
|
+
path21 = path21.replace(/^git@/, "");
|
|
6085
|
+
const parts = path21.split("/");
|
|
5280
6086
|
if (parts.length > 1) {
|
|
5281
|
-
|
|
6087
|
+
path21 = parts.slice(1).join("/");
|
|
5282
6088
|
}
|
|
5283
|
-
|
|
5284
|
-
return
|
|
6089
|
+
path21 = path21.replace(/\.git$/, "");
|
|
6090
|
+
return path21;
|
|
5285
6091
|
}
|
|
5286
6092
|
var syncMemoryCommand;
|
|
5287
6093
|
var init_sync_memory = __esm({
|
|
@@ -5291,7 +6097,7 @@ var init_sync_memory = __esm({
|
|
|
5291
6097
|
init_utils();
|
|
5292
6098
|
init_logger();
|
|
5293
6099
|
init_template_version();
|
|
5294
|
-
syncMemoryCommand = new
|
|
6100
|
+
syncMemoryCommand = new Command11("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
|
|
5295
6101
|
try {
|
|
5296
6102
|
logger.header("\u540C\u6B65 AI_MEMORY.md");
|
|
5297
6103
|
logger.newLine();
|
|
@@ -5325,12 +6131,12 @@ var init_sync_memory = __esm({
|
|
|
5325
6131
|
});
|
|
5326
6132
|
|
|
5327
6133
|
// src/commands/check-api.ts
|
|
5328
|
-
import { Command as
|
|
5329
|
-
import
|
|
5330
|
-
import
|
|
6134
|
+
import { Command as Command12 } from "commander";
|
|
6135
|
+
import path17 from "path";
|
|
6136
|
+
import inquirer8 from "inquirer";
|
|
5331
6137
|
import { Listr as Listr6 } from "listr2";
|
|
5332
6138
|
async function checkApiConflicts(projectDir) {
|
|
5333
|
-
const backendDir =
|
|
6139
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
5334
6140
|
const exists = await FileUtils.exists(backendDir);
|
|
5335
6141
|
if (!exists) {
|
|
5336
6142
|
logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
|
|
@@ -5339,10 +6145,10 @@ async function checkApiConflicts(projectDir) {
|
|
|
5339
6145
|
logger.step("\u626B\u63CF\u540E\u7AEF API...");
|
|
5340
6146
|
logger.newLine();
|
|
5341
6147
|
const apiMap = /* @__PURE__ */ new Map();
|
|
5342
|
-
const srcDir =
|
|
6148
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5343
6149
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5344
6150
|
for (const controllerFile of controllers) {
|
|
5345
|
-
const controllerPath =
|
|
6151
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5346
6152
|
const apis = await extractApisFromController(controllerPath);
|
|
5347
6153
|
for (const api of apis) {
|
|
5348
6154
|
const key = `${api.method}:${api.path}`;
|
|
@@ -5373,8 +6179,8 @@ async function checkApiConflicts(projectDir) {
|
|
|
5373
6179
|
}
|
|
5374
6180
|
}
|
|
5375
6181
|
async function detectApiChanges(projectDir) {
|
|
5376
|
-
const backendDir =
|
|
5377
|
-
const registryFile =
|
|
6182
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
6183
|
+
const registryFile = path17.join(projectDir, "docs/api-registry.md");
|
|
5378
6184
|
const registryExists = await FileUtils.exists(registryFile);
|
|
5379
6185
|
if (!registryExists) {
|
|
5380
6186
|
logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
|
|
@@ -5386,10 +6192,10 @@ async function detectApiChanges(projectDir) {
|
|
|
5386
6192
|
const registryContent = await FileUtils.read(registryFile);
|
|
5387
6193
|
const existingApis = extractApisFromRegistry(registryContent);
|
|
5388
6194
|
const currentApis = /* @__PURE__ */ new Map();
|
|
5389
|
-
const srcDir =
|
|
6195
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5390
6196
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5391
6197
|
for (const controllerFile of controllers) {
|
|
5392
|
-
const controllerPath =
|
|
6198
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5393
6199
|
const apis = await extractApisFromController(controllerPath);
|
|
5394
6200
|
for (const api of apis) {
|
|
5395
6201
|
const key = `${api.method}:${api.path}`;
|
|
@@ -5451,9 +6257,9 @@ async function detectApiChanges(projectDir) {
|
|
|
5451
6257
|
}
|
|
5452
6258
|
}
|
|
5453
6259
|
async function generateApiRegistry(projectDir) {
|
|
5454
|
-
const registryFile =
|
|
6260
|
+
const registryFile = path17.join(projectDir, "docs/api-registry.md");
|
|
5455
6261
|
logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
|
|
5456
|
-
await FileUtils.ensureDir(
|
|
6262
|
+
await FileUtils.ensureDir(path17.dirname(registryFile));
|
|
5457
6263
|
const header = `# API Registry
|
|
5458
6264
|
|
|
5459
6265
|
> \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
|
|
@@ -5482,14 +6288,14 @@ async function generateApiRegistry(projectDir) {
|
|
|
5482
6288
|
*\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
|
|
5483
6289
|
`;
|
|
5484
6290
|
let content = header;
|
|
5485
|
-
const backendDir =
|
|
6291
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
5486
6292
|
const exists = await FileUtils.exists(backendDir);
|
|
5487
6293
|
if (exists) {
|
|
5488
|
-
const srcDir =
|
|
6294
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5489
6295
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5490
6296
|
const moduleMap = /* @__PURE__ */ new Map();
|
|
5491
6297
|
for (const controllerFile of controllers) {
|
|
5492
|
-
const controllerPath =
|
|
6298
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5493
6299
|
const controllerName = controllerFile.replace(".java", "");
|
|
5494
6300
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
5495
6301
|
if (!moduleMap.has(module)) {
|
|
@@ -5573,10 +6379,10 @@ function extractApisFromRegistry(registryContent) {
|
|
|
5573
6379
|
let match;
|
|
5574
6380
|
while ((match = apiRegex.exec(registryContent)) !== null) {
|
|
5575
6381
|
const method = match[1];
|
|
5576
|
-
const
|
|
6382
|
+
const path21 = match[2].trim();
|
|
5577
6383
|
const description = match[3].trim();
|
|
5578
|
-
const key = `${method}:${
|
|
5579
|
-
apis.set(key, { method, path:
|
|
6384
|
+
const key = `${method}:${path21}`;
|
|
6385
|
+
apis.set(key, { method, path: path21, description });
|
|
5580
6386
|
}
|
|
5581
6387
|
return apis;
|
|
5582
6388
|
}
|
|
@@ -5599,7 +6405,7 @@ var init_check_api = __esm({
|
|
|
5599
6405
|
init_esm_shims();
|
|
5600
6406
|
init_utils();
|
|
5601
6407
|
init_logger();
|
|
5602
|
-
checkApiCommand = new
|
|
6408
|
+
checkApiCommand = new Command12("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
|
|
5603
6409
|
try {
|
|
5604
6410
|
logger.header("API \u68C0\u67E5");
|
|
5605
6411
|
logger.newLine();
|
|
@@ -5609,7 +6415,7 @@ var init_check_api = __esm({
|
|
|
5609
6415
|
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
5610
6416
|
process.exit(1);
|
|
5611
6417
|
}
|
|
5612
|
-
const answers = await
|
|
6418
|
+
const answers = await inquirer8.prompt([
|
|
5613
6419
|
{
|
|
5614
6420
|
type: "list",
|
|
5615
6421
|
name: "checkType",
|
|
@@ -5663,16 +6469,16 @@ var init_check_api = __esm({
|
|
|
5663
6469
|
});
|
|
5664
6470
|
|
|
5665
6471
|
// src/commands/logs.ts
|
|
5666
|
-
import { Command as
|
|
5667
|
-
import
|
|
5668
|
-
import
|
|
6472
|
+
import { Command as Command13 } from "commander";
|
|
6473
|
+
import path18 from "path";
|
|
6474
|
+
import inquirer9 from "inquirer";
|
|
5669
6475
|
async function collectLogFiles(targetDir) {
|
|
5670
6476
|
const logs = [];
|
|
5671
6477
|
try {
|
|
5672
6478
|
const allFiles = await FileUtils.findFiles("*.md", targetDir);
|
|
5673
6479
|
const filtered = allFiles.filter((f) => f !== "index.md");
|
|
5674
6480
|
for (const file of filtered) {
|
|
5675
|
-
const filePath =
|
|
6481
|
+
const filePath = path18.join(targetDir, file);
|
|
5676
6482
|
const stat = await FileUtils.exists(filePath);
|
|
5677
6483
|
if (stat) {
|
|
5678
6484
|
logs.push(filePath);
|
|
@@ -5689,7 +6495,7 @@ var init_logs = __esm({
|
|
|
5689
6495
|
init_esm_shims();
|
|
5690
6496
|
init_utils();
|
|
5691
6497
|
init_logger();
|
|
5692
|
-
logsCommand = new
|
|
6498
|
+
logsCommand = new Command13("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") => {
|
|
5693
6499
|
try {
|
|
5694
6500
|
logger.header("\u4F1A\u8BDD\u65E5\u5FD7");
|
|
5695
6501
|
logger.newLine();
|
|
@@ -5712,7 +6518,7 @@ var init_logs = __esm({
|
|
|
5712
6518
|
case "":
|
|
5713
6519
|
case "today": {
|
|
5714
6520
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5715
|
-
targetDir =
|
|
6521
|
+
targetDir = path18.join(sessionsDir, today);
|
|
5716
6522
|
const todayExists = await FileUtils.exists(targetDir);
|
|
5717
6523
|
if (!todayExists) {
|
|
5718
6524
|
logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
|
|
@@ -5728,7 +6534,7 @@ var init_logs = __esm({
|
|
|
5728
6534
|
break;
|
|
5729
6535
|
}
|
|
5730
6536
|
default: {
|
|
5731
|
-
targetDir =
|
|
6537
|
+
targetDir = path18.join(sessionsDir, filter);
|
|
5732
6538
|
const dateExists = await FileUtils.exists(targetDir);
|
|
5733
6539
|
if (!dateExists) {
|
|
5734
6540
|
logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
|
|
@@ -5752,11 +6558,11 @@ var init_logs = __esm({
|
|
|
5752
6558
|
process.exit(0);
|
|
5753
6559
|
}
|
|
5754
6560
|
for (let i = 0; i < logs.length; i++) {
|
|
5755
|
-
const relPath =
|
|
6561
|
+
const relPath = path18.relative(sessionsDir, logs[i]);
|
|
5756
6562
|
logger.step(`${i + 1}) ${relPath}`);
|
|
5757
6563
|
}
|
|
5758
6564
|
logger.newLine();
|
|
5759
|
-
const answers = await
|
|
6565
|
+
const answers = await inquirer9.prompt([
|
|
5760
6566
|
{
|
|
5761
6567
|
type: "input",
|
|
5762
6568
|
name: "selection",
|
|
@@ -5791,10 +6597,10 @@ var init_logs = __esm({
|
|
|
5791
6597
|
});
|
|
5792
6598
|
|
|
5793
6599
|
// src/commands/update.ts
|
|
5794
|
-
import { Command as
|
|
5795
|
-
import
|
|
6600
|
+
import { Command as Command14 } from "commander";
|
|
6601
|
+
import path19 from "path";
|
|
5796
6602
|
import { execa as execa4 } from "execa";
|
|
5797
|
-
import
|
|
6603
|
+
import inquirer10 from "inquirer";
|
|
5798
6604
|
import fs4 from "fs-extra";
|
|
5799
6605
|
async function performUpdate(projectPath, updates) {
|
|
5800
6606
|
logger.newLine();
|
|
@@ -5802,7 +6608,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5802
6608
|
for (const update of updates) {
|
|
5803
6609
|
const { type, info, updateOptions } = update;
|
|
5804
6610
|
const targetDir = type === "frontend" ? "frontend" : "backend";
|
|
5805
|
-
const targetPath =
|
|
6611
|
+
const targetPath = path19.join(projectPath, targetDir);
|
|
5806
6612
|
logger.newLine();
|
|
5807
6613
|
logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
|
|
5808
6614
|
if (updateOptions?.tag || updateOptions?.branch) {
|
|
@@ -5833,8 +6639,8 @@ async function performUpdate(projectPath, updates) {
|
|
|
5833
6639
|
}
|
|
5834
6640
|
}
|
|
5835
6641
|
const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
|
|
5836
|
-
const backupDir =
|
|
5837
|
-
await fs4.copy(targetPath,
|
|
6642
|
+
const backupDir = path19.join(projectPath, `.backup-${Date.now()}`);
|
|
6643
|
+
await fs4.copy(targetPath, path19.join(backupDir, targetDir));
|
|
5838
6644
|
logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
|
|
5839
6645
|
if (updateOptions?.dryRun) {
|
|
5840
6646
|
logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
|
|
@@ -5844,7 +6650,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5844
6650
|
continue;
|
|
5845
6651
|
}
|
|
5846
6652
|
try {
|
|
5847
|
-
const tempDir =
|
|
6653
|
+
const tempDir = path19.join(projectPath, `.template-update-${Date.now()}`);
|
|
5848
6654
|
await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
|
|
5849
6655
|
stdio: "pipe"
|
|
5850
6656
|
});
|
|
@@ -5861,7 +6667,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5861
6667
|
const currentFiles = await FileUtils.findFiles("*", targetPath);
|
|
5862
6668
|
for (const file of currentFiles) {
|
|
5863
6669
|
if (!keepFiles.includes(file)) {
|
|
5864
|
-
const filePath =
|
|
6670
|
+
const filePath = path19.join(targetPath, file);
|
|
5865
6671
|
try {
|
|
5866
6672
|
await fs4.remove(filePath);
|
|
5867
6673
|
} catch {
|
|
@@ -5883,7 +6689,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5883
6689
|
logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
5884
6690
|
logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
|
|
5885
6691
|
await fs4.remove(targetPath);
|
|
5886
|
-
await fs4.copy(
|
|
6692
|
+
await fs4.copy(path19.join(backupDir, targetDir), targetPath);
|
|
5887
6693
|
await fs4.remove(backupDir);
|
|
5888
6694
|
logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
|
|
5889
6695
|
}
|
|
@@ -5906,7 +6712,7 @@ var init_update = __esm({
|
|
|
5906
6712
|
init_template_version();
|
|
5907
6713
|
init_logger();
|
|
5908
6714
|
init_utils();
|
|
5909
|
-
updateCommand = new
|
|
6715
|
+
updateCommand = new Command14("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) => {
|
|
5910
6716
|
try {
|
|
5911
6717
|
logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
|
|
5912
6718
|
logger.newLine();
|
|
@@ -5978,7 +6784,7 @@ var init_update = __esm({
|
|
|
5978
6784
|
logger.info("Dry run \u6A21\u5F0F\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u66F4\u65B0");
|
|
5979
6785
|
return;
|
|
5980
6786
|
}
|
|
5981
|
-
const answers = await
|
|
6787
|
+
const answers = await inquirer10.prompt([
|
|
5982
6788
|
{
|
|
5983
6789
|
type: "confirm",
|
|
5984
6790
|
name: "shouldUpdate",
|
|
@@ -6003,8 +6809,8 @@ var init_update = __esm({
|
|
|
6003
6809
|
});
|
|
6004
6810
|
|
|
6005
6811
|
// src/commands/config.ts
|
|
6006
|
-
import { Command as
|
|
6007
|
-
import
|
|
6812
|
+
import { Command as Command15 } from "commander";
|
|
6813
|
+
import inquirer11 from "inquirer";
|
|
6008
6814
|
import chalk2 from "chalk";
|
|
6009
6815
|
var setTokenCommand, showConfigCommand, removeConfigCommand, validateTokenCommand, configCommand;
|
|
6010
6816
|
var init_config = __esm({
|
|
@@ -6014,13 +6820,13 @@ var init_config = __esm({
|
|
|
6014
6820
|
init_user_config();
|
|
6015
6821
|
init_gitlab_api();
|
|
6016
6822
|
init_logger();
|
|
6017
|
-
setTokenCommand = new
|
|
6823
|
+
setTokenCommand = new Command15("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) => {
|
|
6018
6824
|
try {
|
|
6019
6825
|
logger.header("GitLab Access Token \u914D\u7F6E");
|
|
6020
6826
|
logger.newLine();
|
|
6021
6827
|
let { token, url } = options;
|
|
6022
6828
|
if (!token) {
|
|
6023
|
-
const answers = await
|
|
6829
|
+
const answers = await inquirer11.prompt([
|
|
6024
6830
|
{
|
|
6025
6831
|
type: "password",
|
|
6026
6832
|
name: "token",
|
|
@@ -6082,7 +6888,7 @@ var init_config = __esm({
|
|
|
6082
6888
|
process.exit(1);
|
|
6083
6889
|
}
|
|
6084
6890
|
});
|
|
6085
|
-
showConfigCommand = new
|
|
6891
|
+
showConfigCommand = new Command15("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
|
|
6086
6892
|
try {
|
|
6087
6893
|
logger.header("GitLab \u914D\u7F6E");
|
|
6088
6894
|
logger.newLine();
|
|
@@ -6121,14 +6927,14 @@ var init_config = __esm({
|
|
|
6121
6927
|
process.exit(1);
|
|
6122
6928
|
}
|
|
6123
6929
|
});
|
|
6124
|
-
removeConfigCommand = new
|
|
6930
|
+
removeConfigCommand = new Command15("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
|
|
6125
6931
|
try {
|
|
6126
6932
|
const hasConfig = await userConfigManager.hasConfig();
|
|
6127
6933
|
if (!hasConfig) {
|
|
6128
6934
|
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
6129
6935
|
return;
|
|
6130
6936
|
}
|
|
6131
|
-
const answers = await
|
|
6937
|
+
const answers = await inquirer11.prompt([
|
|
6132
6938
|
{
|
|
6133
6939
|
type: "confirm",
|
|
6134
6940
|
name: "confirm",
|
|
@@ -6150,7 +6956,7 @@ var init_config = __esm({
|
|
|
6150
6956
|
process.exit(1);
|
|
6151
6957
|
}
|
|
6152
6958
|
});
|
|
6153
|
-
validateTokenCommand = new
|
|
6959
|
+
validateTokenCommand = new Command15("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
|
|
6154
6960
|
try {
|
|
6155
6961
|
logger.header("\u9A8C\u8BC1 GitLab Token");
|
|
6156
6962
|
logger.newLine();
|
|
@@ -6180,12 +6986,12 @@ var init_config = __esm({
|
|
|
6180
6986
|
process.exit(1);
|
|
6181
6987
|
}
|
|
6182
6988
|
});
|
|
6183
|
-
configCommand = new
|
|
6989
|
+
configCommand = new Command15("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
|
|
6184
6990
|
}
|
|
6185
6991
|
});
|
|
6186
6992
|
|
|
6187
6993
|
// src/commands/diff.ts
|
|
6188
|
-
import { Command as
|
|
6994
|
+
import { Command as Command16 } from "commander";
|
|
6189
6995
|
import chalk3 from "chalk";
|
|
6190
6996
|
async function compareTemplate(projectPath, type, localConfig, remoteTag, remoteBranch, gitlabConfig) {
|
|
6191
6997
|
try {
|
|
@@ -6364,7 +7170,7 @@ var init_diff = __esm({
|
|
|
6364
7170
|
init_utils();
|
|
6365
7171
|
init_user_config();
|
|
6366
7172
|
init_gitlab_api();
|
|
6367
|
-
diffCommand = new
|
|
7173
|
+
diffCommand = new Command16("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) => {
|
|
6368
7174
|
try {
|
|
6369
7175
|
logger.header("\u6A21\u677F\u7248\u672C\u5BF9\u6BD4");
|
|
6370
7176
|
logger.newLine();
|
|
@@ -6486,10 +7292,10 @@ var init_diff = __esm({
|
|
|
6486
7292
|
|
|
6487
7293
|
// src/index.ts
|
|
6488
7294
|
var index_exports = {};
|
|
6489
|
-
import { Command as
|
|
7295
|
+
import { Command as Command17 } from "commander";
|
|
6490
7296
|
import chalk4 from "chalk";
|
|
6491
7297
|
import fs5 from "fs-extra";
|
|
6492
|
-
import
|
|
7298
|
+
import path20 from "path";
|
|
6493
7299
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6494
7300
|
function showHelp() {
|
|
6495
7301
|
console.log("");
|
|
@@ -6500,7 +7306,8 @@ function showHelp() {
|
|
|
6500
7306
|
console.log(" team-cli split-prd <prd-folder> \u5C06 PRD \u62C6\u5206\u6210\u591A\u4E2A specs");
|
|
6501
7307
|
console.log(" team-cli breakdown [spec-file] \u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos");
|
|
6502
7308
|
console.log(" team-cli dev \u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1");
|
|
6503
|
-
console.log(" team-cli
|
|
7309
|
+
console.log(" team-cli accept [spec-file] \u9A8C\u6536\u529F\u80FD\uFF0C\u8D70\u67E5\u6240\u6709\u9700\u6C42");
|
|
7310
|
+
console.log(" team-cli add-feature <name> \u6DFB\u52A0\u65B0\u529F\u80FD");
|
|
6504
7311
|
console.log(" team-cli bugfix \u521B\u5EFA Bugfix \u8BB0\u5F55");
|
|
6505
7312
|
console.log(" team-cli hotfix \u521B\u5EFA Hotfix");
|
|
6506
7313
|
console.log(" team-cli detect-deps [spec] \u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
@@ -6525,6 +7332,7 @@ function showHelp() {
|
|
|
6525
7332
|
console.log(" 1. PRD \u2192 specs (split-prd)");
|
|
6526
7333
|
console.log(" 2. spec \u2192 milestones + todos (breakdown)");
|
|
6527
7334
|
console.log(" 3. \u9009\u62E9 milestone/todo \u2192 \u5B9E\u73B0 (dev)");
|
|
7335
|
+
console.log(" 4. \u9A8C\u6536 (accept) \u2192 \u9A8C\u8BC1\u8054\u8C03\u662F\u5426\u5B8C\u6210");
|
|
6528
7336
|
console.log("");
|
|
6529
7337
|
console.log(chalk4.bold("\u8FED\u4EE3\u6D41\u7A0B:"));
|
|
6530
7338
|
console.log(" team-cli add-feature <name> # \u6DFB\u52A0\u65B0\u529F\u80FD");
|
|
@@ -6558,6 +7366,7 @@ var init_index = __esm({
|
|
|
6558
7366
|
init_split_prd();
|
|
6559
7367
|
init_bugfix();
|
|
6560
7368
|
init_bugfix();
|
|
7369
|
+
init_accept();
|
|
6561
7370
|
init_lint();
|
|
6562
7371
|
init_status();
|
|
6563
7372
|
init_detect_deps();
|
|
@@ -6567,9 +7376,9 @@ var init_index = __esm({
|
|
|
6567
7376
|
init_update();
|
|
6568
7377
|
init_config();
|
|
6569
7378
|
init_diff();
|
|
6570
|
-
__dirname2 =
|
|
6571
|
-
pkg = fs5.readJsonSync(
|
|
6572
|
-
program = new
|
|
7379
|
+
__dirname2 = path20.dirname(fileURLToPath2(import.meta.url));
|
|
7380
|
+
pkg = fs5.readJsonSync(path20.join(__dirname2, "../package.json"));
|
|
7381
|
+
program = new Command17();
|
|
6573
7382
|
program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version(pkg.version);
|
|
6574
7383
|
program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
|
|
6575
7384
|
program.addCommand(initCommand);
|
|
@@ -6579,6 +7388,7 @@ var init_index = __esm({
|
|
|
6579
7388
|
program.addCommand(addFeatureCommand);
|
|
6580
7389
|
program.addCommand(bugfixCommand);
|
|
6581
7390
|
program.addCommand(hotfixCommand);
|
|
7391
|
+
program.addCommand(acceptCommand);
|
|
6582
7392
|
program.addCommand(lintCommand);
|
|
6583
7393
|
program.addCommand(statusCommand);
|
|
6584
7394
|
program.addCommand(detectDepsCommand);
|