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/index.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
|
*/
|
|
@@ -886,22 +1001,22 @@ var init_gitlab_api = __esm({
|
|
|
886
1001
|
* 从 Git URL 中提取项目路径
|
|
887
1002
|
*/
|
|
888
1003
|
static parseProjectPath(repository) {
|
|
889
|
-
let
|
|
890
|
-
if (
|
|
891
|
-
|
|
892
|
-
const colonIndex =
|
|
1004
|
+
let path21 = repository;
|
|
1005
|
+
if (path21.startsWith("git@")) {
|
|
1006
|
+
path21 = path21.replace(/^git@/, "");
|
|
1007
|
+
const colonIndex = path21.indexOf(":");
|
|
893
1008
|
if (colonIndex !== -1) {
|
|
894
|
-
|
|
1009
|
+
path21 = path21.substring(colonIndex + 1);
|
|
895
1010
|
}
|
|
896
1011
|
} else {
|
|
897
|
-
|
|
898
|
-
const parts =
|
|
1012
|
+
path21 = path21.replace(/^https?:\/\//, "");
|
|
1013
|
+
const parts = path21.split("/");
|
|
899
1014
|
if (parts.length > 1) {
|
|
900
|
-
|
|
1015
|
+
path21 = parts.slice(1).join("/");
|
|
901
1016
|
}
|
|
902
1017
|
}
|
|
903
|
-
|
|
904
|
-
return
|
|
1018
|
+
path21 = path21.replace(/\.git$/, "");
|
|
1019
|
+
return path21;
|
|
905
1020
|
}
|
|
906
1021
|
/**
|
|
907
1022
|
* 编码项目路径用于 API 请求
|
|
@@ -955,10 +1070,10 @@ var init_gitlab_api = __esm({
|
|
|
955
1070
|
// src/index.ts
|
|
956
1071
|
init_esm_shims();
|
|
957
1072
|
init_logger();
|
|
958
|
-
import { Command as
|
|
1073
|
+
import { Command as Command17 } from "commander";
|
|
959
1074
|
import chalk4 from "chalk";
|
|
960
1075
|
import fs5 from "fs-extra";
|
|
961
|
-
import
|
|
1076
|
+
import path20 from "path";
|
|
962
1077
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
963
1078
|
|
|
964
1079
|
// src/commands/init.ts
|
|
@@ -2768,7 +2883,7 @@ ${cleanedMilestones}`;
|
|
|
2768
2883
|
function buildBreakdownPrompt(specContent) {
|
|
2769
2884
|
return `Role: Senior Technical Lead and Agile Coach
|
|
2770
2885
|
|
|
2771
|
-
Task: Break down the following feature spec into milestones and
|
|
2886
|
+
Task: Break down the following feature spec into milestones with frontend, backend, and integration tasks.
|
|
2772
2887
|
|
|
2773
2888
|
Context:
|
|
2774
2889
|
- Read TECH_STACK.md for technology constraints
|
|
@@ -2784,13 +2899,18 @@ ${specContent}
|
|
|
2784
2899
|
Output Requirements:
|
|
2785
2900
|
1. Parse the existing spec content and identify technical requirements.
|
|
2786
2901
|
2. Break it down into 2-5 milestones.
|
|
2787
|
-
3. Each milestone
|
|
2788
|
-
-
|
|
2789
|
-
-
|
|
2790
|
-
-
|
|
2902
|
+
3. Each milestone must have three sections:
|
|
2903
|
+
- **\u524D\u7AEF\u4EFB\u52A1**: Frontend tasks marked with [F] prefix
|
|
2904
|
+
- **\u540E\u7AEF\u4EFB\u52A1**: Backend tasks marked with [B] prefix, include API associations
|
|
2905
|
+
- **\u8054\u8C03\u4EFB\u52A1**: Integration tasks marked with [I] prefix, include dependencies
|
|
2791
2906
|
4. Todo items should be:
|
|
2792
|
-
- Concrete, specific, and testable
|
|
2793
|
-
- Independent as much as possible
|
|
2907
|
+
- Concrete, specific, and testable
|
|
2908
|
+
- Independent as much as possible
|
|
2909
|
+
|
|
2910
|
+
Task Prefix Format:
|
|
2911
|
+
- Frontend: \`- [ ] [F] \u5177\u4F53\u7684\u524D\u7AEF\u4EFB\u52A1\u63CF\u8FF0\`
|
|
2912
|
+
- Backend: \`- [ ] [B] \u5177\u4F53\u7684\u540E\u7AEF\u4EFB\u52A1\u63CF\u8FF0 (\u5173\u8054 API: \`METHOD /api/path\`)\`
|
|
2913
|
+
- Integration: \`- [ ] [I] \u8054\u8C03\u4EFB\u52A1\u63CF\u8FF0 (\u4F9D\u8D56: F-x, B-y)\`
|
|
2794
2914
|
|
|
2795
2915
|
IMPORTANT:
|
|
2796
2916
|
- Output ONLY the "## \u91CC\u7A0B\u7891 (Milestones)" section.
|
|
@@ -2805,9 +2925,16 @@ IMPORTANT:
|
|
|
2805
2925
|
**\u76EE\u6807**: [\u7B80\u77ED\u63CF\u8FF0\u8FD9\u4E2A\u91CC\u7A0B\u7891\u7684\u76EE\u6807]
|
|
2806
2926
|
**\u9884\u4F30**: 2 \u5929
|
|
2807
2927
|
|
|
2808
|
-
|
|
2809
|
-
- [ ]
|
|
2810
|
-
- [ ]
|
|
2928
|
+
**\u524D\u7AEF\u4EFB\u52A1**:
|
|
2929
|
+
- [ ] [F] \u524D\u7AEF\u4EFB\u52A1\u63CF\u8FF0 1
|
|
2930
|
+
- [ ] [F] \u524D\u7AEF\u4EFB\u52A1\u63CF\u8FF0 2
|
|
2931
|
+
|
|
2932
|
+
**\u540E\u7AEF\u4EFB\u52A1**:
|
|
2933
|
+
- [ ] [B] \u540E\u7AEF\u4EFB\u52A1\u63CF\u8FF0 1 (\u5173\u8054 API: \`POST /api/xxx\`)
|
|
2934
|
+
- [ ] [B] \u540E\u7AEF\u4EFB\u52A1\u63CF\u8FF0 2 (\u5173\u8054 API: \`GET /api/yyy\`)
|
|
2935
|
+
|
|
2936
|
+
**\u8054\u8C03\u4EFB\u52A1**:
|
|
2937
|
+
- [ ] [I] \u8054\u8C03\u4EFB\u52A1\u63CF\u8FF0 1 (\u4F9D\u8D56: F-1, B-1)
|
|
2811
2938
|
|
|
2812
2939
|
### Milestone 2: [\u91CC\u7A0B\u7891\u540D\u79F0]
|
|
2813
2940
|
...
|
|
@@ -2910,8 +3037,8 @@ async function selectMilestone(specFile) {
|
|
|
2910
3037
|
logger.step("\u6B65\u9AA4 2/3: \u89E3\u6790 milestones...");
|
|
2911
3038
|
logger.newLine();
|
|
2912
3039
|
const specContent = await FileUtils.read(specFile);
|
|
2913
|
-
const
|
|
2914
|
-
if (
|
|
3040
|
+
const { enhanced, isEnhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specContent);
|
|
3041
|
+
if (enhanced.length === 0) {
|
|
2915
3042
|
logger.info("\u8BE5 spec \u5C1A\u672A\u62C6\u5206 milestones");
|
|
2916
3043
|
const { breakdownNow } = await inquirer3.prompt([
|
|
2917
3044
|
{
|
|
@@ -2929,19 +3056,32 @@ async function selectMilestone(specFile) {
|
|
|
2929
3056
|
return "\u6574\u4E2A spec";
|
|
2930
3057
|
}
|
|
2931
3058
|
}
|
|
2932
|
-
const choices =
|
|
2933
|
-
const isDone = m.completedCount === m.
|
|
3059
|
+
const choices = enhanced.map((m, idx) => {
|
|
3060
|
+
const isDone = m.completedCount === m.totalCount && m.totalCount > 0;
|
|
2934
3061
|
const statusIcon = isDone ? "\u2713" : m.completedCount > 0 ? "\u27F3" : "\u25CB";
|
|
2935
|
-
const progress = `[${m.completedCount}/${m.
|
|
3062
|
+
const progress = `[${m.completedCount}/${m.totalCount}]`;
|
|
2936
3063
|
const label = isDone ? `\u2713 ${m.title}` : `${statusIcon} ${progress} ${m.title}`;
|
|
3064
|
+
let detailInfo = `(${m.totalCount} \u4E2A\u4EFB\u52A1)`;
|
|
3065
|
+
if (isEnhanced) {
|
|
3066
|
+
const fCount = m.frontendTodos.filter((t) => !t.isCompleted).length;
|
|
3067
|
+
const bCount = m.backendTodos.filter((t) => !t.isCompleted).length;
|
|
3068
|
+
const iCount = m.integrationTodos.filter((t) => !t.isCompleted).length;
|
|
3069
|
+
const details = [];
|
|
3070
|
+
if (fCount > 0) details.push(`\u524D\u7AEF:${fCount}`);
|
|
3071
|
+
if (bCount > 0) details.push(`\u540E\u7AEF:${bCount}`);
|
|
3072
|
+
if (iCount > 0) details.push(`\u8054\u8C03:${iCount}`);
|
|
3073
|
+
if (details.length > 0) {
|
|
3074
|
+
detailInfo = `(${details.join(", ")} \u672A\u5B8C\u6210)`;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
2937
3077
|
return {
|
|
2938
|
-
name: `${idx + 1}. ${label}
|
|
3078
|
+
name: `${idx + 1}. ${label} ${detailInfo}`,
|
|
2939
3079
|
value: m.title,
|
|
2940
3080
|
short: m.title
|
|
2941
3081
|
};
|
|
2942
3082
|
});
|
|
2943
3083
|
choices.push({
|
|
2944
|
-
name: `${
|
|
3084
|
+
name: `${enhanced.length + 1}. \u6574\u4E2A spec (\u5168\u90E8 milestones)`,
|
|
2945
3085
|
value: "\u6574\u4E2A spec",
|
|
2946
3086
|
short: "\u6574\u4E2A spec"
|
|
2947
3087
|
});
|
|
@@ -2962,6 +3102,79 @@ async function selectTodo(specFile, milestone) {
|
|
|
2962
3102
|
logger.newLine();
|
|
2963
3103
|
logger.step("\u6B65\u9AA4 3/3: \u9009\u62E9 todo \u4EFB\u52A1...");
|
|
2964
3104
|
logger.newLine();
|
|
3105
|
+
const specContent = await FileUtils.read(specFile);
|
|
3106
|
+
const { enhanced, isEnhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specContent);
|
|
3107
|
+
const targetMilestone = enhanced.find((m) => m.title === milestone);
|
|
3108
|
+
if (!targetMilestone || !isEnhanced) {
|
|
3109
|
+
return await selectTodoSimple(specFile, milestone);
|
|
3110
|
+
}
|
|
3111
|
+
return await selectTodoEnhanced(targetMilestone);
|
|
3112
|
+
}
|
|
3113
|
+
async function selectTodoEnhanced(milestone) {
|
|
3114
|
+
const choices = [];
|
|
3115
|
+
if (milestone.frontendTodos.length > 0) {
|
|
3116
|
+
choices.push({ name: `\u2501\u2501 \u524D\u7AEF\u4EFB\u52A1 (${milestone.frontendTodos.length}) \u2501\u2501`, disabled: true });
|
|
3117
|
+
milestone.frontendTodos.forEach((todo2, idx) => {
|
|
3118
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3119
|
+
choices.push({
|
|
3120
|
+
name: ` [F] ${icon} ${todo2.content}`,
|
|
3121
|
+
value: todo2.content,
|
|
3122
|
+
short: `[F] ${todo2.content}`
|
|
3123
|
+
});
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
if (milestone.backendTodos.length > 0) {
|
|
3127
|
+
choices.push({ name: `\u2501\u2501 \u540E\u7AEF\u4EFB\u52A1 (${milestone.backendTodos.length}) \u2501\u2501`, disabled: true });
|
|
3128
|
+
milestone.backendTodos.forEach((todo2, idx) => {
|
|
3129
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3130
|
+
const apiInfo = todo2.apiAssociation ? ` (${todo2.apiAssociation})` : "";
|
|
3131
|
+
choices.push({
|
|
3132
|
+
name: ` [B] ${icon} ${todo2.content}${apiInfo}`,
|
|
3133
|
+
value: todo2.content,
|
|
3134
|
+
short: `[B] ${todo2.content}`
|
|
3135
|
+
});
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
if (milestone.integrationTodos.length > 0) {
|
|
3139
|
+
choices.push({ name: `\u2501\u2501 \u8054\u8C03\u4EFB\u52A1 (${milestone.integrationTodos.length}) \u2501\u2501`, disabled: true });
|
|
3140
|
+
milestone.integrationTodos.forEach((todo2, idx) => {
|
|
3141
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3142
|
+
const depsInfo = todo2.dependencies ? ` (\u4F9D\u8D56: ${todo2.dependencies.join(", ")})` : "";
|
|
3143
|
+
choices.push({
|
|
3144
|
+
name: ` [I] ${icon} ${todo2.content}${depsInfo}`,
|
|
3145
|
+
value: todo2.content,
|
|
3146
|
+
short: `[I] ${todo2.content}`
|
|
3147
|
+
});
|
|
3148
|
+
});
|
|
3149
|
+
}
|
|
3150
|
+
if (milestone.generalTodos.length > 0) {
|
|
3151
|
+
choices.push({ name: `\u2501\u2501 \u901A\u7528\u4EFB\u52A1 (${milestone.generalTodos.length}) \u2501\u2501`, disabled: true });
|
|
3152
|
+
milestone.generalTodos.forEach((todo2, idx) => {
|
|
3153
|
+
const icon = todo2.isCompleted ? "[x]" : "[ ]";
|
|
3154
|
+
choices.push({
|
|
3155
|
+
name: ` ${icon} ${todo2.content}`,
|
|
3156
|
+
value: todo2.content,
|
|
3157
|
+
short: todo2.content
|
|
3158
|
+
});
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
choices.push({ name: "\u2501\u2501 \u5176\u4ED6 \u2501\u2501", disabled: true });
|
|
3162
|
+
choices.push({
|
|
3163
|
+
name: `\u5168\u90E8\u4EFB\u52A1 (\u6574\u4E2A milestone)`,
|
|
3164
|
+
value: "\u5168\u90E8\u4EFB\u52A1",
|
|
3165
|
+
short: "\u5168\u90E8\u4EFB\u52A1"
|
|
3166
|
+
});
|
|
3167
|
+
const { todo } = await inquirer3.prompt([
|
|
3168
|
+
{
|
|
3169
|
+
type: "list",
|
|
3170
|
+
name: "todo",
|
|
3171
|
+
message: "\u9009\u62E9 todo \u4EFB\u52A1:",
|
|
3172
|
+
choices
|
|
3173
|
+
}
|
|
3174
|
+
]);
|
|
3175
|
+
return todo;
|
|
3176
|
+
}
|
|
3177
|
+
async function selectTodoSimple(specFile, milestone) {
|
|
2965
3178
|
const specContent = await FileUtils.read(specFile);
|
|
2966
3179
|
const milestones = SpecUtils.parseMilestones(specContent);
|
|
2967
3180
|
const targetMilestone = milestones.find((m) => m.title === milestone);
|
|
@@ -3189,10 +3402,14 @@ async function askAndUpdateSpecStatus(specFile, milestone, todo) {
|
|
|
3189
3402
|
break;
|
|
3190
3403
|
}
|
|
3191
3404
|
if (todo !== "\u5168\u90E8\u529F\u80FD" && todo !== "\u5168\u90E8\u4EFB\u52A1") {
|
|
3192
|
-
const todoMatch = line.match(/^-\s+\[
|
|
3193
|
-
if (todoMatch
|
|
3194
|
-
|
|
3195
|
-
|
|
3405
|
+
const todoMatch = line.match(/^-\s+\[ \]\s*(\[[FBIN]\]\s*)?(.+)/);
|
|
3406
|
+
if (todoMatch) {
|
|
3407
|
+
const todoContent = todoMatch[2].trim();
|
|
3408
|
+
const cleanContent = todoContent.replace(/\s*\(关联 API:\s*`[^`]+`\)/, "").replace(/\s*\(依赖:\s*[^)]+\)/, "").trim();
|
|
3409
|
+
if (cleanContent === todo) {
|
|
3410
|
+
targetTodoIndex = i;
|
|
3411
|
+
break;
|
|
3412
|
+
}
|
|
3196
3413
|
}
|
|
3197
3414
|
}
|
|
3198
3415
|
}
|
|
@@ -4419,13 +4636,596 @@ git checkout ${data.branch}
|
|
|
4419
4636
|
`;
|
|
4420
4637
|
}
|
|
4421
4638
|
|
|
4422
|
-
// src/commands/
|
|
4639
|
+
// src/commands/accept.ts
|
|
4423
4640
|
init_esm_shims();
|
|
4424
4641
|
init_utils();
|
|
4425
4642
|
init_logger();
|
|
4426
4643
|
import { Command as Command7 } from "commander";
|
|
4644
|
+
import inquirer6 from "inquirer";
|
|
4645
|
+
import path13 from "path";
|
|
4646
|
+
var 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) => {
|
|
4647
|
+
try {
|
|
4648
|
+
logger.header("\u9A8C\u6536\u6A21\u5F0F");
|
|
4649
|
+
logger.newLine();
|
|
4650
|
+
const hasTechStack = await FileUtils.exists("TECH_STACK.md");
|
|
4651
|
+
if (!hasTechStack) {
|
|
4652
|
+
logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
|
|
4653
|
+
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
4654
|
+
process.exit(1);
|
|
4655
|
+
}
|
|
4656
|
+
logger.success("\u68C0\u6D4B\u5230\u9879\u76EE\u4E0A\u4E0B\u6587");
|
|
4657
|
+
const selectedSpec = await selectSpec2(specFile);
|
|
4658
|
+
const selectedMilestone = await selectMilestone2(selectedSpec);
|
|
4659
|
+
const result = await runAcceptanceCheck(selectedSpec, selectedMilestone);
|
|
4660
|
+
await generateAcceptanceReport(result);
|
|
4661
|
+
if (result.issues.length > 0) {
|
|
4662
|
+
await handleIssues(result);
|
|
4663
|
+
}
|
|
4664
|
+
await syncSpecStatus(selectedSpec, result);
|
|
4665
|
+
logger.header("\u9A8C\u6536\u5B8C\u6210!");
|
|
4666
|
+
logger.newLine();
|
|
4667
|
+
} catch (error) {
|
|
4668
|
+
logger.error(`\u9A8C\u6536\u5931\u8D25: ${error.message}`);
|
|
4669
|
+
if (process.env.DEBUG) {
|
|
4670
|
+
console.error(error);
|
|
4671
|
+
}
|
|
4672
|
+
process.exit(1);
|
|
4673
|
+
}
|
|
4674
|
+
});
|
|
4675
|
+
async function selectSpec2(defaultSpec) {
|
|
4676
|
+
logger.step("\u6B65\u9AA4 1/4: \u9009\u62E9 spec \u6587\u4EF6...");
|
|
4677
|
+
logger.newLine();
|
|
4678
|
+
const specDir = "docs/specs";
|
|
4679
|
+
const exists = await FileUtils.exists(specDir);
|
|
4680
|
+
if (!exists) {
|
|
4681
|
+
throw new Error("docs/specs \u76EE\u5F55\u4E0D\u5B58\u5728");
|
|
4682
|
+
}
|
|
4683
|
+
const files = await FileUtils.findFiles("*.md", specDir);
|
|
4684
|
+
const specFiles = files.filter((f) => !f.includes("template"));
|
|
4685
|
+
if (specFiles.length === 0) {
|
|
4686
|
+
throw new Error("\u672A\u627E\u5230 spec \u6587\u4EF6");
|
|
4687
|
+
}
|
|
4688
|
+
if (defaultSpec) {
|
|
4689
|
+
const fullPath = defaultSpec.startsWith("docs/specs/") ? defaultSpec : path13.join(specDir, defaultSpec);
|
|
4690
|
+
const exists2 = await FileUtils.exists(fullPath);
|
|
4691
|
+
if (!exists2) {
|
|
4692
|
+
throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${defaultSpec}`);
|
|
4693
|
+
}
|
|
4694
|
+
logger.success(`\u5DF2\u9009\u62E9: ${fullPath}`);
|
|
4695
|
+
return fullPath;
|
|
4696
|
+
}
|
|
4697
|
+
const specs = [];
|
|
4698
|
+
for (const file of specFiles) {
|
|
4699
|
+
const fullPath = path13.join(specDir, file);
|
|
4700
|
+
const content = await FileUtils.read(fullPath);
|
|
4701
|
+
const status = SpecUtils.parseSpecStatus(content);
|
|
4702
|
+
specs.push({ file: fullPath, name: file, status });
|
|
4703
|
+
}
|
|
4704
|
+
const activeSpecs = specs.filter(
|
|
4705
|
+
(s) => s.status === "\u5DF2\u62C6\u5206" || s.status === "\u8FDB\u884C\u4E2D" || s.status === "\u5DF2\u5B8C\u6210"
|
|
4706
|
+
);
|
|
4707
|
+
if (activeSpecs.length === 0) {
|
|
4708
|
+
logger.warn("\u6CA1\u6709\u53EF\u9A8C\u6536\u7684 spec \u6587\u4EF6");
|
|
4709
|
+
const { continueAnyway } = await inquirer6.prompt([
|
|
4710
|
+
{
|
|
4711
|
+
type: "confirm",
|
|
4712
|
+
name: "continueAnyway",
|
|
4713
|
+
message: "\u662F\u5426\u7EE7\u7EED\u67E5\u770B\u6240\u6709 spec?",
|
|
4714
|
+
default: true
|
|
4715
|
+
}
|
|
4716
|
+
]);
|
|
4717
|
+
if (!continueAnyway) {
|
|
4718
|
+
process.exit(0);
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
const targetSpecs = activeSpecs.length > 0 ? activeSpecs : specs;
|
|
4722
|
+
const choices = targetSpecs.map((spec) => ({
|
|
4723
|
+
name: `[${spec.status}] ${spec.name}`,
|
|
4724
|
+
value: spec.file,
|
|
4725
|
+
short: spec.name
|
|
4726
|
+
}));
|
|
4727
|
+
const { selectedFile } = await inquirer6.prompt([
|
|
4728
|
+
{
|
|
4729
|
+
type: "list",
|
|
4730
|
+
name: "selectedFile",
|
|
4731
|
+
message: "\u9009\u62E9\u8981\u9A8C\u6536\u7684 spec:",
|
|
4732
|
+
choices
|
|
4733
|
+
}
|
|
4734
|
+
]);
|
|
4735
|
+
logger.success(`\u5DF2\u9009\u62E9: ${selectedFile}`);
|
|
4736
|
+
return selectedFile;
|
|
4737
|
+
}
|
|
4738
|
+
async function selectMilestone2(specFile) {
|
|
4739
|
+
logger.newLine();
|
|
4740
|
+
logger.step("\u6B65\u9AA4 2/4: \u89E3\u6790 milestones...");
|
|
4741
|
+
logger.newLine();
|
|
4742
|
+
const specContent = await FileUtils.read(specFile);
|
|
4743
|
+
const { enhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specContent);
|
|
4744
|
+
if (enhanced.length === 0) {
|
|
4745
|
+
throw new Error("\u8BE5 spec \u5C1A\u672A\u62C6\u5206 milestones\uFF0C\u65E0\u6CD5\u8FDB\u884C\u9A8C\u6536");
|
|
4746
|
+
}
|
|
4747
|
+
const choices = enhanced.map((m, idx) => {
|
|
4748
|
+
const isDone = m.completedCount === m.totalCount;
|
|
4749
|
+
const statusIcon = isDone ? "\u2713" : m.completedCount > 0 ? "\u27F3" : "\u25CB";
|
|
4750
|
+
const progress = `[${m.completedCount}/${m.totalCount}]`;
|
|
4751
|
+
return {
|
|
4752
|
+
name: `${idx + 1}. ${statusIcon} ${progress} ${m.title}`,
|
|
4753
|
+
value: m.title,
|
|
4754
|
+
short: m.title
|
|
4755
|
+
};
|
|
4756
|
+
});
|
|
4757
|
+
const { milestone } = await inquirer6.prompt([
|
|
4758
|
+
{
|
|
4759
|
+
type: "list",
|
|
4760
|
+
name: "milestone",
|
|
4761
|
+
message: "\u9009\u62E9\u8981\u9A8C\u6536\u7684 milestone:",
|
|
4762
|
+
choices
|
|
4763
|
+
}
|
|
4764
|
+
]);
|
|
4765
|
+
return milestone;
|
|
4766
|
+
}
|
|
4767
|
+
async function runAcceptanceCheck(specFile, milestone) {
|
|
4768
|
+
logger.newLine();
|
|
4769
|
+
logger.step("\u6B65\u9AA4 3/4: \u6267\u884C\u9A8C\u6536\u68C0\u67E5...");
|
|
4770
|
+
logger.newLine();
|
|
4771
|
+
const specContent = await FileUtils.read(specFile);
|
|
4772
|
+
const { enhanced } = SpecUtils.parseMilestonesEnhancedWithFallback(specFile);
|
|
4773
|
+
const targetMilestone = enhanced.find((m) => m.title === milestone);
|
|
4774
|
+
if (!targetMilestone) {
|
|
4775
|
+
throw new Error(`\u672A\u627E\u5230 milestone: ${milestone}`);
|
|
4776
|
+
}
|
|
4777
|
+
const result = {
|
|
4778
|
+
specFile,
|
|
4779
|
+
milestone,
|
|
4780
|
+
checkTime: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN"),
|
|
4781
|
+
frontendTasks: { total: 0, completed: 0, items: [] },
|
|
4782
|
+
backendTasks: { total: 0, completed: 0, items: [] },
|
|
4783
|
+
apiVerification: { total: 0, verified: 0, items: [] },
|
|
4784
|
+
integrationTasks: { total: 0, completed: 0, items: [] },
|
|
4785
|
+
issues: []
|
|
4786
|
+
};
|
|
4787
|
+
logger.info("\u68C0\u67E5\u524D\u7AEF\u4EFB\u52A1...");
|
|
4788
|
+
for (const todo of targetMilestone.frontendTodos) {
|
|
4789
|
+
const checkResult = await checkFrontendTask(todo);
|
|
4790
|
+
result.frontendTasks.items.push(checkResult);
|
|
4791
|
+
if (checkResult.status === "pass") {
|
|
4792
|
+
result.frontendTasks.completed++;
|
|
4793
|
+
}
|
|
4794
|
+
result.frontendTasks.total++;
|
|
4795
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4796
|
+
logger.info(` ${icon} [F] ${todo.content}${checkResult.filePath ? ` - ${checkResult.filePath}` : ""}`);
|
|
4797
|
+
}
|
|
4798
|
+
logger.info("\u68C0\u67E5\u540E\u7AEF\u4EFB\u52A1...");
|
|
4799
|
+
for (const todo of targetMilestone.backendTodos) {
|
|
4800
|
+
const checkResult = await checkBackendTask(todo);
|
|
4801
|
+
result.backendTasks.items.push(checkResult);
|
|
4802
|
+
if (checkResult.status === "pass") {
|
|
4803
|
+
result.backendTasks.completed++;
|
|
4804
|
+
}
|
|
4805
|
+
result.backendTasks.total++;
|
|
4806
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4807
|
+
logger.info(` ${icon} [B] ${todo.content}${checkResult.filePath ? ` - ${checkResult.filePath}` : ""}`);
|
|
4808
|
+
}
|
|
4809
|
+
logger.info("\u9A8C\u8BC1 API \u5B9E\u73B0...");
|
|
4810
|
+
for (const todo of targetMilestone.backendTodos) {
|
|
4811
|
+
if (todo.apiAssociation) {
|
|
4812
|
+
const checkResult = await verifyApiImplementation(todo);
|
|
4813
|
+
result.apiVerification.items.push(checkResult);
|
|
4814
|
+
if (checkResult.status === "pass") {
|
|
4815
|
+
result.apiVerification.verified++;
|
|
4816
|
+
}
|
|
4817
|
+
result.apiVerification.total++;
|
|
4818
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4819
|
+
logger.info(` ${icon} ${todo.apiAssociation}${checkResult.filePath ? ` - ${checkResult.filePath}` : ""}`);
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
logger.info("\u68C0\u67E5\u8054\u8C03\u4EFB\u52A1...");
|
|
4823
|
+
for (const todo of targetMilestone.integrationTodos) {
|
|
4824
|
+
const checkResult = await checkIntegrationTask(todo);
|
|
4825
|
+
result.integrationTasks.items.push(checkResult);
|
|
4826
|
+
if (checkResult.status === "pass") {
|
|
4827
|
+
result.integrationTasks.completed++;
|
|
4828
|
+
}
|
|
4829
|
+
result.integrationTasks.total++;
|
|
4830
|
+
const icon = checkResult.status === "pass" ? "\u2713" : checkResult.status === "fail" ? "\u2717" : "\u25CB";
|
|
4831
|
+
logger.info(` ${icon} [I] ${todo.content}`);
|
|
4832
|
+
}
|
|
4833
|
+
return result;
|
|
4834
|
+
}
|
|
4835
|
+
async function checkFrontendTask(todo) {
|
|
4836
|
+
const frontendPaths = [
|
|
4837
|
+
`frontend/src/pages/${todo.content}.tsx`,
|
|
4838
|
+
`frontend/src/components/${todo.content}.tsx`,
|
|
4839
|
+
`frontend/src/components/${todo.content.replace(/\s+/g, "")}.tsx`,
|
|
4840
|
+
`frontend/src/views/${todo.content}.vue`,
|
|
4841
|
+
`frontend/src/components/${todo.content}.vue`
|
|
4842
|
+
];
|
|
4843
|
+
for (const fp of frontendPaths) {
|
|
4844
|
+
if (await FileUtils.exists(fp)) {
|
|
4845
|
+
return {
|
|
4846
|
+
description: `[F] ${todo.content}`,
|
|
4847
|
+
status: "pass",
|
|
4848
|
+
details: "\u524D\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4849
|
+
filePath: fp
|
|
4850
|
+
};
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
const pathMatch = todo.content.match(/\s*-\s*(.+?\.(tsx|vue|js|jsx))\s*/);
|
|
4854
|
+
if (pathMatch) {
|
|
4855
|
+
const filePath = pathMatch[1];
|
|
4856
|
+
if (await FileUtils.exists(filePath)) {
|
|
4857
|
+
return {
|
|
4858
|
+
description: `[F] ${todo.content}`,
|
|
4859
|
+
status: "pass",
|
|
4860
|
+
details: "\u524D\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4861
|
+
filePath
|
|
4862
|
+
};
|
|
4863
|
+
}
|
|
4864
|
+
}
|
|
4865
|
+
return {
|
|
4866
|
+
description: `[F] ${todo.content}`,
|
|
4867
|
+
status: "fail",
|
|
4868
|
+
details: "\u524D\u7AEF\u4EE3\u7801\u4E0D\u5B58\u5728\u6216\u8DEF\u5F84\u65E0\u6CD5\u9A8C\u8BC1"
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
async function checkBackendTask(todo) {
|
|
4872
|
+
const backendPaths = [
|
|
4873
|
+
`backend/src/main/java/${todo.content.replace(/\s+/g, "/")}.java`,
|
|
4874
|
+
`backend/src/${todo.content.toLowerCase().replace(/\s+/g, "/")}.java`
|
|
4875
|
+
];
|
|
4876
|
+
for (const fp of backendPaths) {
|
|
4877
|
+
if (await FileUtils.exists(fp)) {
|
|
4878
|
+
return {
|
|
4879
|
+
description: `[B] ${todo.content}`,
|
|
4880
|
+
status: "pass",
|
|
4881
|
+
details: "\u540E\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4882
|
+
filePath: fp
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
const pathMatch = todo.content.match(/\s*-\s*(.+?\.(java|kt|py|go))\s*/);
|
|
4887
|
+
if (pathMatch) {
|
|
4888
|
+
const filePath = pathMatch[1];
|
|
4889
|
+
if (await FileUtils.exists(filePath)) {
|
|
4890
|
+
return {
|
|
4891
|
+
description: `[B] ${todo.content}`,
|
|
4892
|
+
status: "pass",
|
|
4893
|
+
details: "\u540E\u7AEF\u4EE3\u7801\u5B58\u5728",
|
|
4894
|
+
filePath
|
|
4895
|
+
};
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
return {
|
|
4899
|
+
description: `[B] ${todo.content}`,
|
|
4900
|
+
status: "fail",
|
|
4901
|
+
details: "\u540E\u7AEF\u4EE3\u7801\u4E0D\u5B58\u5728\u6216\u8DEF\u5F84\u65E0\u6CD5\u9A8C\u8BC1"
|
|
4902
|
+
};
|
|
4903
|
+
}
|
|
4904
|
+
async function verifyApiImplementation(todo) {
|
|
4905
|
+
if (!todo.apiAssociation) {
|
|
4906
|
+
return {
|
|
4907
|
+
description: todo.apiAssociation || "API \u9A8C\u8BC1",
|
|
4908
|
+
status: "pending",
|
|
4909
|
+
details: "\u65E0 API \u5173\u8054\u4FE1\u606F"
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
const apiMatch = todo.apiAssociation.match(/^(GET|POST|PUT|DELETE|PATCH)\s+\/(.+)$/);
|
|
4913
|
+
if (!apiMatch) {
|
|
4914
|
+
return {
|
|
4915
|
+
description: todo.apiAssociation,
|
|
4916
|
+
status: "pending",
|
|
4917
|
+
details: "\u65E0\u6CD5\u89E3\u6790 API \u683C\u5F0F"
|
|
4918
|
+
};
|
|
4919
|
+
}
|
|
4920
|
+
const method = apiMatch[1];
|
|
4921
|
+
const apiPath = apiMatch[2];
|
|
4922
|
+
const controllerPaths = [
|
|
4923
|
+
`backend/src/main/java/**/*Controller.java`,
|
|
4924
|
+
`backend/src/**/*Controller.java`
|
|
4925
|
+
];
|
|
4926
|
+
const controllerFiles = await FileUtils.findFiles("**/*Controller.java", "backend/src");
|
|
4927
|
+
for (const cf of controllerFiles) {
|
|
4928
|
+
const content = await FileUtils.read(cf);
|
|
4929
|
+
if (content.includes(apiPath) || content.includes(`"${apiPath}"`)) {
|
|
4930
|
+
const methodAnnotations = {
|
|
4931
|
+
GET: ["@GetMapping", "@RequestMapping(method = RequestMethod.GET)"],
|
|
4932
|
+
POST: ["@PostMapping", "@RequestMapping(method = RequestMethod.POST)"],
|
|
4933
|
+
PUT: ["@PutMapping", "@RequestMapping(method = RequestMethod.PUT)"],
|
|
4934
|
+
DELETE: ["@DeleteMapping", "@RequestMapping(method = RequestMethod.DELETE)"],
|
|
4935
|
+
PATCH: ["@PatchMapping", "@RequestMapping(method = RequestMethod.PATCH)"]
|
|
4936
|
+
};
|
|
4937
|
+
for (const annotation of methodAnnotations[method] || []) {
|
|
4938
|
+
if (content.includes(annotation)) {
|
|
4939
|
+
return {
|
|
4940
|
+
description: todo.apiAssociation,
|
|
4941
|
+
status: "pass",
|
|
4942
|
+
details: "API Controller \u5B9E\u73B0\u5B58\u5728",
|
|
4943
|
+
filePath: cf
|
|
4944
|
+
};
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4949
|
+
return {
|
|
4950
|
+
description: todo.apiAssociation,
|
|
4951
|
+
status: "fail",
|
|
4952
|
+
details: "API Controller \u672A\u5B9E\u73B0"
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
async function checkIntegrationTask(todo) {
|
|
4956
|
+
const integrationEvidence = [
|
|
4957
|
+
"**/*integration*.test.*",
|
|
4958
|
+
"**/*e2e*.test.*",
|
|
4959
|
+
"**/*IT.java",
|
|
4960
|
+
"docs/integration-test-results.md",
|
|
4961
|
+
"docs/session-logs/**"
|
|
4962
|
+
];
|
|
4963
|
+
const sessionDir = "docs/sessions";
|
|
4964
|
+
if (await FileUtils.exists(sessionDir)) {
|
|
4965
|
+
const sessionFiles = await FileUtils.findFiles("*.md", sessionDir);
|
|
4966
|
+
const hasRecentSession = sessionFiles.some(
|
|
4967
|
+
(sf) => sf.includes("integration") || sf.includes("\u8054\u8C03")
|
|
4968
|
+
);
|
|
4969
|
+
if (hasRecentSession) {
|
|
4970
|
+
return {
|
|
4971
|
+
description: `[I] ${todo.content}`,
|
|
4972
|
+
status: "pass",
|
|
4973
|
+
details: "\u8054\u8C03\u8BC1\u636E\u5B58\u5728\uFF08session \u65E5\u5FD7\uFF09"
|
|
4974
|
+
};
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
return {
|
|
4978
|
+
description: `[I] ${todo.content}`,
|
|
4979
|
+
status: "pending",
|
|
4980
|
+
details: "\u5EFA\u8BAE\u4EBA\u5DE5\u9A8C\u8BC1\u8054\u8C03\u7ED3\u679C"
|
|
4981
|
+
};
|
|
4982
|
+
}
|
|
4983
|
+
async function generateAcceptanceReport(result) {
|
|
4984
|
+
logger.newLine();
|
|
4985
|
+
logger.step("\u6B65\u9AA4 4/4: \u751F\u6210\u9A8C\u6536\u62A5\u544A...");
|
|
4986
|
+
logger.newLine();
|
|
4987
|
+
const reportDir = "docs/acceptance-reports";
|
|
4988
|
+
await FileUtils.ensureDir(reportDir);
|
|
4989
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4990
|
+
const specName = path13.basename(result.specFile, ".md");
|
|
4991
|
+
const milestoneSafe = result.milestone.replace(/[^a-zA-Z0-9]/g, "-");
|
|
4992
|
+
const reportFile = path13.join(reportDir, `${timestamp}_${specName}_${milestoneSafe}.md`);
|
|
4993
|
+
const report = generateMarkdownReport(result);
|
|
4994
|
+
await FileUtils.write(reportFile, report);
|
|
4995
|
+
logger.success(`\u9A8C\u6536\u62A5\u544A\u5DF2\u751F\u6210: ${reportFile}`);
|
|
4996
|
+
logger.newLine();
|
|
4997
|
+
console.log(generateConsoleReport(result));
|
|
4998
|
+
}
|
|
4999
|
+
function generateMarkdownReport(result) {
|
|
5000
|
+
const lines = [];
|
|
5001
|
+
lines.push("# \u9A8C\u6536\u62A5\u544A");
|
|
5002
|
+
lines.push("");
|
|
5003
|
+
lines.push(`**Spec \u6587\u4EF6**: ${result.specFile}`);
|
|
5004
|
+
lines.push(`**Milestone**: ${result.milestone}`);
|
|
5005
|
+
lines.push(`**\u9A8C\u6536\u65F6\u95F4**: ${result.checkTime}`);
|
|
5006
|
+
lines.push("");
|
|
5007
|
+
lines.push("## \u68C0\u67E5\u7ED3\u679C\u6C47\u603B");
|
|
5008
|
+
lines.push("");
|
|
5009
|
+
lines.push("| \u68C0\u67E5\u9879 | \u72B6\u6001 |");
|
|
5010
|
+
lines.push("|--------|------|");
|
|
5011
|
+
lines.push(
|
|
5012
|
+
`| \u524D\u7AEF\u4EFB\u52A1 | ${result.frontendTasks.completed}/${result.frontendTasks.total} \u5B8C\u6210 |`
|
|
5013
|
+
);
|
|
5014
|
+
lines.push(
|
|
5015
|
+
`| \u540E\u7AEF\u4EFB\u52A1 | ${result.backendTasks.completed}/${result.backendTasks.total} \u5B8C\u6210 |`
|
|
5016
|
+
);
|
|
5017
|
+
lines.push(`| API \u9A8C\u8BC1 | ${result.apiVerification.verified}/${result.apiVerification.total} \u5B9E\u73B0 |`);
|
|
5018
|
+
lines.push(
|
|
5019
|
+
`| \u8054\u8C03\u4EFB\u52A1 | ${result.integrationTasks.completed}/${result.integrationTasks.total} \u5B8C\u6210 |`
|
|
5020
|
+
);
|
|
5021
|
+
lines.push("");
|
|
5022
|
+
lines.push("## \u8BE6\u7EC6\u8BB0\u5F55");
|
|
5023
|
+
lines.push("");
|
|
5024
|
+
lines.push("### \u524D\u7AEF\u4EFB\u52A1");
|
|
5025
|
+
lines.push("");
|
|
5026
|
+
for (const item of result.frontendTasks.items) {
|
|
5027
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u25CB";
|
|
5028
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""} ${item.filePath ? `(\`${item.filePath}\`)` : ""}`);
|
|
5029
|
+
}
|
|
5030
|
+
lines.push("");
|
|
5031
|
+
lines.push("### \u540E\u7AEF\u4EFB\u52A1");
|
|
5032
|
+
lines.push("");
|
|
5033
|
+
for (const item of result.backendTasks.items) {
|
|
5034
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u25CB";
|
|
5035
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""} ${item.filePath ? `(\`${item.filePath}\`)` : ""}`);
|
|
5036
|
+
}
|
|
5037
|
+
lines.push("");
|
|
5038
|
+
lines.push("### API \u9A8C\u8BC1");
|
|
5039
|
+
lines.push("");
|
|
5040
|
+
for (const item of result.apiVerification.items) {
|
|
5041
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u25CB";
|
|
5042
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""} ${item.filePath ? `(\`${item.filePath}\`)` : ""}`);
|
|
5043
|
+
}
|
|
5044
|
+
lines.push("");
|
|
5045
|
+
lines.push("### \u8054\u8C03\u4EFB\u52A1");
|
|
5046
|
+
lines.push("");
|
|
5047
|
+
for (const item of result.integrationTasks.items) {
|
|
5048
|
+
const icon = item.status === "pass" ? "\u2713" : item.status === "fail" ? "\u2717" : "\u26A0";
|
|
5049
|
+
lines.push(`- ${icon} ${item.description} - ${item.details || ""}`);
|
|
5050
|
+
}
|
|
5051
|
+
lines.push("");
|
|
5052
|
+
if (result.issues.length > 0) {
|
|
5053
|
+
lines.push("## \u53D1\u73B0\u7684\u95EE\u9898");
|
|
5054
|
+
lines.push("");
|
|
5055
|
+
lines.push("| \u95EE\u9898 | \u7C7B\u578B | \u4E25\u91CD\u7A0B\u5EA6 | \u72B6\u6001 |");
|
|
5056
|
+
lines.push("|------|------|---------|------|");
|
|
5057
|
+
for (const issue of result.issues) {
|
|
5058
|
+
lines.push(`| ${issue.title} | ${issue.type} | ${issue.severity} | ${issue.status} |`);
|
|
5059
|
+
}
|
|
5060
|
+
lines.push("");
|
|
5061
|
+
lines.push("**\u64CD\u4F5C**:");
|
|
5062
|
+
lines.push(`- \u8FD0\u884C \`team-cli bugfix ${result.specFile}\` \u67E5\u770B\u8BE6\u60C5`);
|
|
5063
|
+
lines.push("- \u6216\u8FD0\u884C `team-cli hotfix` \u7ACB\u5373\u4FEE\u590D");
|
|
5064
|
+
lines.push("");
|
|
5065
|
+
}
|
|
5066
|
+
lines.push("## \u4E0B\u4E00\u6B65\u5EFA\u8BAE");
|
|
5067
|
+
lines.push("");
|
|
5068
|
+
if (result.issues.length > 0) {
|
|
5069
|
+
lines.push("- [ ] \u4FEE\u590D\u53D1\u73B0\u7684\u95EE\u9898");
|
|
5070
|
+
}
|
|
5071
|
+
lines.push("- [ ] \u8FD0\u884C `team-cli dev` \u7EE7\u7EED\u5F00\u53D1");
|
|
5072
|
+
lines.push("- [ ] \u8FD0\u884C `team-cli accept` \u91CD\u65B0\u9A8C\u6536");
|
|
5073
|
+
lines.push("");
|
|
5074
|
+
return lines.join("\n");
|
|
5075
|
+
}
|
|
5076
|
+
function generateConsoleReport(result) {
|
|
5077
|
+
const lines = [];
|
|
5078
|
+
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");
|
|
5079
|
+
lines.push("\u2502 \u9A8C \u6536 \u7ED3 \u679C \u6458 \u8981 \u2502");
|
|
5080
|
+
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");
|
|
5081
|
+
lines.push(`\u2502 Spec: ${result.specFile.padEnd(40)}\u2502`);
|
|
5082
|
+
lines.push(`\u2502 Milestone: ${result.milestone.padEnd(37)}\u2502`);
|
|
5083
|
+
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");
|
|
5084
|
+
lines.push(
|
|
5085
|
+
`\u2502 \u524D\u7AEF\u4EFB\u52A1: ${result.frontendTasks.completed}/${result.frontendTasks.total} \u5B8C\u6210`.padEnd(47) + "\u2502"
|
|
5086
|
+
);
|
|
5087
|
+
lines.push(
|
|
5088
|
+
`\u2502 \u540E\u7AEF\u4EFB\u52A1: ${result.backendTasks.completed}/${result.backendTasks.total} \u5B8C\u6210`.padEnd(47) + "\u2502"
|
|
5089
|
+
);
|
|
5090
|
+
lines.push(
|
|
5091
|
+
`\u2502 API \u9A8C\u8BC1: ${result.apiVerification.verified}/${result.apiVerification.total} \u5B9E\u73B0`.padEnd(47) + "\u2502"
|
|
5092
|
+
);
|
|
5093
|
+
lines.push(
|
|
5094
|
+
`\u2502 \u8054\u8C03\u4EFB\u52A1: ${result.integrationTasks.completed}/${result.integrationTasks.total} \u5B8C\u6210`.padEnd(47) + "\u2502"
|
|
5095
|
+
);
|
|
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
|
+
const totalIssues = result.issues.length;
|
|
5098
|
+
if (totalIssues > 0) {
|
|
5099
|
+
lines.push(`\u2502 \u26A0 \u53D1\u73B0\u95EE\u9898: ${totalIssues} \u4E2A`.padEnd(47) + "\u2502");
|
|
5100
|
+
} else {
|
|
5101
|
+
lines.push(`\u2502 \u2713 \u9A8C\u6536\u901A\u8FC7`.padEnd(47) + "\u2502");
|
|
5102
|
+
}
|
|
5103
|
+
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");
|
|
5104
|
+
return lines.join("\n");
|
|
5105
|
+
}
|
|
5106
|
+
async function handleIssues(result) {
|
|
5107
|
+
logger.newLine();
|
|
5108
|
+
logger.warn(`\u53D1\u73B0 ${result.issues.length} \u4E2A\u95EE\u9898\u9700\u8981\u5904\u7406`);
|
|
5109
|
+
const bugfixDir = "docs/bugfixes";
|
|
5110
|
+
await FileUtils.ensureDir(bugfixDir);
|
|
5111
|
+
for (const issue of result.issues) {
|
|
5112
|
+
const bugfixFile = path13.join(
|
|
5113
|
+
bugfixDir,
|
|
5114
|
+
`${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}_${issue.id}.md`
|
|
5115
|
+
);
|
|
5116
|
+
const bugfixContent = generateBugfixContent(result, issue);
|
|
5117
|
+
await FileUtils.write(bugfixFile, bugfixContent);
|
|
5118
|
+
logger.info(` \u5DF2\u521B\u5EFA Bugfix \u8BB0\u5F55: ${bugfixFile}`);
|
|
5119
|
+
}
|
|
5120
|
+
logger.newLine();
|
|
5121
|
+
const { fixNow } = await inquirer6.prompt([
|
|
5122
|
+
{
|
|
5123
|
+
type: "confirm",
|
|
5124
|
+
name: "fixNow",
|
|
5125
|
+
message: "\u662F\u5426\u7ACB\u5373\u4FEE\u590D\u53D1\u73B0\u7684\u95EE\u9898?",
|
|
5126
|
+
default: false
|
|
5127
|
+
}
|
|
5128
|
+
]);
|
|
5129
|
+
if (fixNow) {
|
|
5130
|
+
logger.info("\u8BF7\u8FD0\u884C `team-cli hotfix` \u7ACB\u5373\u4FEE\u590D\u95EE\u9898");
|
|
5131
|
+
} else {
|
|
5132
|
+
logger.info(`\u95EE\u9898\u8BB0\u5F55\u5DF2\u4FDD\u5B58\u5230 ${bugfixDir} \u76EE\u5F55`);
|
|
5133
|
+
logger.info("\u8FD0\u884C `team-cli bugfix` \u67E5\u770B\u6240\u6709\u95EE\u9898\u8BB0\u5F55");
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
function generateBugfixContent(result, issue) {
|
|
5137
|
+
const lines = [];
|
|
5138
|
+
lines.push("# Bugfix \u8BB0\u5F55");
|
|
5139
|
+
lines.push("");
|
|
5140
|
+
lines.push(`**ID**: ${issue.id}`);
|
|
5141
|
+
lines.push(`**\u521B\u5EFA\u65F6\u95F4**: ${issue.createdAt}`);
|
|
5142
|
+
lines.push(`**\u72B6\u6001**: ${issue.status}`);
|
|
5143
|
+
lines.push("");
|
|
5144
|
+
lines.push("## \u95EE\u9898\u63CF\u8FF0");
|
|
5145
|
+
lines.push("");
|
|
5146
|
+
lines.push(issue.description);
|
|
5147
|
+
lines.push("");
|
|
5148
|
+
lines.push("## \u6240\u5C5E\u8303\u56F4");
|
|
5149
|
+
lines.push("");
|
|
5150
|
+
lines.push(`- Spec: ${result.specFile}`);
|
|
5151
|
+
lines.push(`- Milestone: ${result.milestone}`);
|
|
5152
|
+
if (issue.todo) {
|
|
5153
|
+
lines.push(`- Todo: ${issue.todo}`);
|
|
5154
|
+
}
|
|
5155
|
+
lines.push("");
|
|
5156
|
+
lines.push("## \u4E25\u91CD\u7A0B\u5EA6");
|
|
5157
|
+
lines.push("");
|
|
5158
|
+
lines.push(`- ${issue.severity.toUpperCase()}`);
|
|
5159
|
+
lines.push("");
|
|
5160
|
+
lines.push("## \u95EE\u9898\u7C7B\u578B");
|
|
5161
|
+
lines.push("");
|
|
5162
|
+
lines.push(`- ${issue.type}`);
|
|
5163
|
+
lines.push("");
|
|
5164
|
+
lines.push("## \u89E3\u51B3\u65B9\u6848");
|
|
5165
|
+
lines.push("");
|
|
5166
|
+
lines.push("<!-- TODO: \u586B\u5199\u89E3\u51B3\u65B9\u6848 -->");
|
|
5167
|
+
lines.push("");
|
|
5168
|
+
lines.push("## \u9A8C\u8BC1\u6B65\u9AA4");
|
|
5169
|
+
lines.push("");
|
|
5170
|
+
lines.push("1. ");
|
|
5171
|
+
lines.push("2. ");
|
|
5172
|
+
lines.push("3. ");
|
|
5173
|
+
lines.push("");
|
|
5174
|
+
return lines.join("\n");
|
|
5175
|
+
}
|
|
5176
|
+
async function syncSpecStatus(specFile, result) {
|
|
5177
|
+
const content = await FileUtils.read(specFile);
|
|
5178
|
+
const lines = content.split("\n");
|
|
5179
|
+
let inTargetMilestone = false;
|
|
5180
|
+
let milestoneIndex = -1;
|
|
5181
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5182
|
+
const line = lines[i];
|
|
5183
|
+
if (line.includes(result.milestone)) {
|
|
5184
|
+
inTargetMilestone = true;
|
|
5185
|
+
milestoneIndex = i;
|
|
5186
|
+
continue;
|
|
5187
|
+
}
|
|
5188
|
+
if (inTargetMilestone && line.match(/^###\s+Milestone/)) {
|
|
5189
|
+
break;
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
if (milestoneIndex !== -1) {
|
|
5193
|
+
let completedTasks = 0;
|
|
5194
|
+
let totalTasks = 0;
|
|
5195
|
+
for (let i = milestoneIndex + 1; i < lines.length; i++) {
|
|
5196
|
+
const line = lines[i];
|
|
5197
|
+
if (line.match(/^###\s+Milestone/)) {
|
|
5198
|
+
break;
|
|
5199
|
+
}
|
|
5200
|
+
if (line.match(/^-\s+\[/)) {
|
|
5201
|
+
totalTasks++;
|
|
5202
|
+
if (line.match(/^-\s+\[x\]/)) {
|
|
5203
|
+
completedTasks++;
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
}
|
|
5207
|
+
const progressLine = `${result.milestone} [${completedTasks}/${totalTasks}]`;
|
|
5208
|
+
const linesBefore = lines.slice(0, milestoneIndex);
|
|
5209
|
+
const linesAfter = lines.slice(milestoneIndex + 1);
|
|
5210
|
+
if (!lines[milestoneIndex].includes(`[${completedTasks}/${totalTasks}]`)) {
|
|
5211
|
+
const updatedMilestoneLine = lines[milestoneIndex].replace(
|
|
5212
|
+
/(\s*\[\d+\/\d+\])?$/,
|
|
5213
|
+
` [${completedTasks}/${totalTasks}]`
|
|
5214
|
+
);
|
|
5215
|
+
lines[milestoneIndex] = updatedMilestoneLine;
|
|
5216
|
+
await FileUtils.write(specFile, lines.join("\n"));
|
|
5217
|
+
logger.info(`\u5DF2\u540C\u6B65\u91CC\u7A0B\u7891\u8FDB\u5EA6: [${completedTasks}/${totalTasks}]`);
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
|
|
5222
|
+
// src/commands/lint.ts
|
|
5223
|
+
init_esm_shims();
|
|
5224
|
+
init_utils();
|
|
5225
|
+
init_logger();
|
|
5226
|
+
import { Command as Command8 } from "commander";
|
|
4427
5227
|
import { execa as execa3 } from "execa";
|
|
4428
|
-
var lintCommand = new
|
|
5228
|
+
var 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) => {
|
|
4429
5229
|
try {
|
|
4430
5230
|
logger.header("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5");
|
|
4431
5231
|
logger.newLine();
|
|
@@ -4560,9 +5360,9 @@ var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D
|
|
|
4560
5360
|
init_esm_shims();
|
|
4561
5361
|
init_utils();
|
|
4562
5362
|
init_logger();
|
|
4563
|
-
import { Command as
|
|
4564
|
-
import
|
|
4565
|
-
var statusCommand = new
|
|
5363
|
+
import { Command as Command9 } from "commander";
|
|
5364
|
+
import path14 from "path";
|
|
5365
|
+
var statusCommand = new Command9("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
|
|
4566
5366
|
try {
|
|
4567
5367
|
logger.header("\u9879\u76EE\u72B6\u6001");
|
|
4568
5368
|
logger.newLine();
|
|
@@ -4619,7 +5419,7 @@ async function displayFeatureInventory() {
|
|
|
4619
5419
|
}
|
|
4620
5420
|
const inventory = [];
|
|
4621
5421
|
for (const file of specs) {
|
|
4622
|
-
const filePath =
|
|
5422
|
+
const filePath = path14.join(specDir, file);
|
|
4623
5423
|
const content = await FileUtils.read(filePath);
|
|
4624
5424
|
const status = SpecUtils.parseSpecStatus(content);
|
|
4625
5425
|
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
@@ -4679,7 +5479,7 @@ async function displayRecentActivity() {
|
|
|
4679
5479
|
}
|
|
4680
5480
|
const sorted = files.sort().reverse().slice(0, 5);
|
|
4681
5481
|
for (const file of sorted) {
|
|
4682
|
-
const filePath =
|
|
5482
|
+
const filePath = path14.join(sessionDir, file);
|
|
4683
5483
|
const stat = await FileUtils.read(filePath);
|
|
4684
5484
|
const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
|
|
4685
5485
|
const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
|
|
@@ -4698,10 +5498,10 @@ async function displayRecentActivity() {
|
|
|
4698
5498
|
init_esm_shims();
|
|
4699
5499
|
init_utils();
|
|
4700
5500
|
init_logger();
|
|
4701
|
-
import { Command as
|
|
4702
|
-
import
|
|
4703
|
-
import
|
|
4704
|
-
var detectDepsCommand = new
|
|
5501
|
+
import { Command as Command10 } from "commander";
|
|
5502
|
+
import path15 from "path";
|
|
5503
|
+
import inquirer7 from "inquirer";
|
|
5504
|
+
var detectDepsCommand = new Command10("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
|
|
4705
5505
|
try {
|
|
4706
5506
|
logger.header("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
4707
5507
|
logger.newLine();
|
|
@@ -4727,7 +5527,7 @@ var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spe
|
|
|
4727
5527
|
process.exit(1);
|
|
4728
5528
|
}
|
|
4729
5529
|
for (const spec of specs) {
|
|
4730
|
-
const specPath =
|
|
5530
|
+
const specPath = path15.join(specsDir, spec);
|
|
4731
5531
|
logger.step(`\u5904\u7406: ${spec}`);
|
|
4732
5532
|
await detectDependencies(specPath);
|
|
4733
5533
|
logger.newLine();
|
|
@@ -4753,7 +5553,7 @@ async function detectDependencies(specFile) {
|
|
|
4753
5553
|
logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
|
|
4754
5554
|
const projectDir = ".";
|
|
4755
5555
|
const allDeps = /* @__PURE__ */ new Set();
|
|
4756
|
-
const backendDir =
|
|
5556
|
+
const backendDir = path15.join(projectDir, "backend");
|
|
4757
5557
|
const backendExists = await FileUtils.exists(backendDir);
|
|
4758
5558
|
if (backendExists) {
|
|
4759
5559
|
const apiDeps = await scanBackendApiCalls(backendDir);
|
|
@@ -4763,7 +5563,7 @@ async function detectDependencies(specFile) {
|
|
|
4763
5563
|
const serviceDeps = await scanBackendServiceRefs(backendDir);
|
|
4764
5564
|
serviceDeps.forEach((d) => allDeps.add(d));
|
|
4765
5565
|
}
|
|
4766
|
-
const frontendDir =
|
|
5566
|
+
const frontendDir = path15.join(projectDir, "frontend");
|
|
4767
5567
|
const frontendExists = await FileUtils.exists(frontendDir);
|
|
4768
5568
|
if (frontendExists) {
|
|
4769
5569
|
const frontendDeps = await scanFrontendApiCalls(frontendDir);
|
|
@@ -4789,7 +5589,7 @@ async function detectDependencies(specFile) {
|
|
|
4789
5589
|
logger.step(`- ${spec}`);
|
|
4790
5590
|
}
|
|
4791
5591
|
logger.newLine();
|
|
4792
|
-
const answers = await
|
|
5592
|
+
const answers = await inquirer7.prompt([
|
|
4793
5593
|
{
|
|
4794
5594
|
type: "confirm",
|
|
4795
5595
|
name: "autoUpdate",
|
|
@@ -4804,11 +5604,11 @@ async function detectDependencies(specFile) {
|
|
|
4804
5604
|
}
|
|
4805
5605
|
async function scanBackendApiCalls(backendDir) {
|
|
4806
5606
|
const deps = [];
|
|
4807
|
-
const srcDir =
|
|
5607
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4808
5608
|
try {
|
|
4809
5609
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4810
5610
|
for (const file of javaFiles) {
|
|
4811
|
-
const filePath =
|
|
5611
|
+
const filePath = path15.join(srcDir, file);
|
|
4812
5612
|
const content = await FileUtils.read(filePath);
|
|
4813
5613
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
4814
5614
|
let match;
|
|
@@ -4826,11 +5626,11 @@ async function scanBackendApiCalls(backendDir) {
|
|
|
4826
5626
|
}
|
|
4827
5627
|
async function scanBackendEntityRelations(backendDir) {
|
|
4828
5628
|
const deps = [];
|
|
4829
|
-
const srcDir =
|
|
5629
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4830
5630
|
try {
|
|
4831
5631
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4832
5632
|
for (const file of javaFiles) {
|
|
4833
|
-
const filePath =
|
|
5633
|
+
const filePath = path15.join(srcDir, file);
|
|
4834
5634
|
const content = await FileUtils.read(filePath);
|
|
4835
5635
|
if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
|
|
4836
5636
|
const typeRegex = /type\s*=\s*(\w+)/g;
|
|
@@ -4846,11 +5646,11 @@ async function scanBackendEntityRelations(backendDir) {
|
|
|
4846
5646
|
}
|
|
4847
5647
|
async function scanBackendServiceRefs(backendDir) {
|
|
4848
5648
|
const deps = [];
|
|
4849
|
-
const srcDir =
|
|
5649
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4850
5650
|
try {
|
|
4851
5651
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4852
5652
|
for (const file of javaFiles) {
|
|
4853
|
-
const filePath =
|
|
5653
|
+
const filePath = path15.join(srcDir, file);
|
|
4854
5654
|
const content = await FileUtils.read(filePath);
|
|
4855
5655
|
const serviceRegex = /private\s+(\w+)Service/g;
|
|
4856
5656
|
let match;
|
|
@@ -4866,11 +5666,11 @@ async function scanBackendServiceRefs(backendDir) {
|
|
|
4866
5666
|
}
|
|
4867
5667
|
async function scanFrontendApiCalls(frontendDir) {
|
|
4868
5668
|
const deps = [];
|
|
4869
|
-
const srcDir =
|
|
5669
|
+
const srcDir = path15.join(frontendDir, "src");
|
|
4870
5670
|
try {
|
|
4871
5671
|
const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
|
|
4872
5672
|
for (const file of tsFiles) {
|
|
4873
|
-
const filePath =
|
|
5673
|
+
const filePath = path15.join(srcDir, file);
|
|
4874
5674
|
const content = await FileUtils.read(filePath);
|
|
4875
5675
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
4876
5676
|
let match;
|
|
@@ -4959,9 +5759,9 @@ ${deps.map((d) => ` - [x] ${d}`).join("\n")}
|
|
|
4959
5759
|
init_esm_shims();
|
|
4960
5760
|
init_utils();
|
|
4961
5761
|
init_logger();
|
|
4962
|
-
import { Command as
|
|
4963
|
-
import
|
|
4964
|
-
var syncMemoryCommand = new
|
|
5762
|
+
import { Command as Command11 } from "commander";
|
|
5763
|
+
import path16 from "path";
|
|
5764
|
+
var syncMemoryCommand = new Command11("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
|
|
4965
5765
|
try {
|
|
4966
5766
|
logger.header("\u540C\u6B65 AI_MEMORY.md");
|
|
4967
5767
|
logger.newLine();
|
|
@@ -4993,7 +5793,7 @@ var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 A
|
|
|
4993
5793
|
});
|
|
4994
5794
|
async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
4995
5795
|
logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
|
|
4996
|
-
const specsDir =
|
|
5796
|
+
const specsDir = path16.join(projectDir, "docs/specs");
|
|
4997
5797
|
const exists = await FileUtils.exists(specsDir);
|
|
4998
5798
|
if (!exists) {
|
|
4999
5799
|
return;
|
|
@@ -5008,7 +5808,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5008
5808
|
for (const specFile of specs) {
|
|
5009
5809
|
const name = specFile.replace(".md", "");
|
|
5010
5810
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
5011
|
-
const specPath =
|
|
5811
|
+
const specPath = path16.join(specsDir, specFile);
|
|
5012
5812
|
const content = await FileUtils.read(specPath);
|
|
5013
5813
|
const status = SpecUtils.parseSpecStatus(content);
|
|
5014
5814
|
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
@@ -5027,7 +5827,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5027
5827
|
}
|
|
5028
5828
|
async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
5029
5829
|
logger.step("\u540C\u6B65 API \u5217\u8868...");
|
|
5030
|
-
const backendDir =
|
|
5830
|
+
const backendDir = path16.join(projectDir, "backend");
|
|
5031
5831
|
const exists = await FileUtils.exists(backendDir);
|
|
5032
5832
|
if (!exists) {
|
|
5033
5833
|
return;
|
|
@@ -5037,13 +5837,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
|
5037
5837
|
lines.push("");
|
|
5038
5838
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
|
|
5039
5839
|
lines.push("");
|
|
5040
|
-
const srcDir =
|
|
5840
|
+
const srcDir = path16.join(backendDir, "src");
|
|
5041
5841
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5042
5842
|
if (controllers.length === 0) {
|
|
5043
5843
|
lines.push("\u6682\u65E0 API");
|
|
5044
5844
|
} else {
|
|
5045
5845
|
for (const controllerFile of controllers) {
|
|
5046
|
-
const controllerPath =
|
|
5846
|
+
const controllerPath = path16.join(srcDir, controllerFile);
|
|
5047
5847
|
const controllerName = controllerFile.replace(".java", "");
|
|
5048
5848
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
5049
5849
|
lines.push(`### ${module} \u6A21\u5757`);
|
|
@@ -5120,7 +5920,7 @@ function extractMethodComment(content, methodName) {
|
|
|
5120
5920
|
}
|
|
5121
5921
|
async function syncDataModels(aiMemoryFile, projectDir) {
|
|
5122
5922
|
logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
|
|
5123
|
-
const backendDir =
|
|
5923
|
+
const backendDir = path16.join(projectDir, "backend");
|
|
5124
5924
|
const exists = await FileUtils.exists(backendDir);
|
|
5125
5925
|
if (!exists) {
|
|
5126
5926
|
return;
|
|
@@ -5130,7 +5930,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
5130
5930
|
lines.push("");
|
|
5131
5931
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
|
|
5132
5932
|
lines.push("");
|
|
5133
|
-
const srcDir =
|
|
5933
|
+
const srcDir = path16.join(backendDir, "src");
|
|
5134
5934
|
const entities = await FileUtils.findFiles("*Entity.java", srcDir);
|
|
5135
5935
|
if (entities.length === 0) {
|
|
5136
5936
|
lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
|
|
@@ -5138,7 +5938,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
5138
5938
|
lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
|
|
5139
5939
|
lines.push("|------|------|------|------|");
|
|
5140
5940
|
for (const entityFile of entities) {
|
|
5141
|
-
const entityPath =
|
|
5941
|
+
const entityPath = path16.join(srcDir, entityFile);
|
|
5142
5942
|
const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
|
|
5143
5943
|
const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
5144
5944
|
const content = await FileUtils.read(entityPath);
|
|
@@ -5240,26 +6040,26 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
|
5240
6040
|
await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
|
|
5241
6041
|
}
|
|
5242
6042
|
function extractRepoName(repository) {
|
|
5243
|
-
let
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
const parts =
|
|
6043
|
+
let path21 = repository;
|
|
6044
|
+
path21 = path21.replace(/^https?:\/\//, "");
|
|
6045
|
+
path21 = path21.replace(/^git@/, "");
|
|
6046
|
+
const parts = path21.split("/");
|
|
5247
6047
|
if (parts.length > 1) {
|
|
5248
|
-
|
|
6048
|
+
path21 = parts.slice(1).join("/");
|
|
5249
6049
|
}
|
|
5250
|
-
|
|
5251
|
-
return
|
|
6050
|
+
path21 = path21.replace(/\.git$/, "");
|
|
6051
|
+
return path21;
|
|
5252
6052
|
}
|
|
5253
6053
|
|
|
5254
6054
|
// src/commands/check-api.ts
|
|
5255
6055
|
init_esm_shims();
|
|
5256
6056
|
init_utils();
|
|
5257
6057
|
init_logger();
|
|
5258
|
-
import { Command as
|
|
5259
|
-
import
|
|
5260
|
-
import
|
|
6058
|
+
import { Command as Command12 } from "commander";
|
|
6059
|
+
import path17 from "path";
|
|
6060
|
+
import inquirer8 from "inquirer";
|
|
5261
6061
|
import { Listr as Listr6 } from "listr2";
|
|
5262
|
-
var checkApiCommand = new
|
|
6062
|
+
var checkApiCommand = new Command12("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
|
|
5263
6063
|
try {
|
|
5264
6064
|
logger.header("API \u68C0\u67E5");
|
|
5265
6065
|
logger.newLine();
|
|
@@ -5269,7 +6069,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
|
|
|
5269
6069
|
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
5270
6070
|
process.exit(1);
|
|
5271
6071
|
}
|
|
5272
|
-
const answers = await
|
|
6072
|
+
const answers = await inquirer8.prompt([
|
|
5273
6073
|
{
|
|
5274
6074
|
type: "list",
|
|
5275
6075
|
name: "checkType",
|
|
@@ -5320,7 +6120,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
|
|
|
5320
6120
|
}
|
|
5321
6121
|
});
|
|
5322
6122
|
async function checkApiConflicts(projectDir) {
|
|
5323
|
-
const backendDir =
|
|
6123
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
5324
6124
|
const exists = await FileUtils.exists(backendDir);
|
|
5325
6125
|
if (!exists) {
|
|
5326
6126
|
logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
|
|
@@ -5329,10 +6129,10 @@ async function checkApiConflicts(projectDir) {
|
|
|
5329
6129
|
logger.step("\u626B\u63CF\u540E\u7AEF API...");
|
|
5330
6130
|
logger.newLine();
|
|
5331
6131
|
const apiMap = /* @__PURE__ */ new Map();
|
|
5332
|
-
const srcDir =
|
|
6132
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5333
6133
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5334
6134
|
for (const controllerFile of controllers) {
|
|
5335
|
-
const controllerPath =
|
|
6135
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5336
6136
|
const apis = await extractApisFromController(controllerPath);
|
|
5337
6137
|
for (const api of apis) {
|
|
5338
6138
|
const key = `${api.method}:${api.path}`;
|
|
@@ -5363,8 +6163,8 @@ async function checkApiConflicts(projectDir) {
|
|
|
5363
6163
|
}
|
|
5364
6164
|
}
|
|
5365
6165
|
async function detectApiChanges(projectDir) {
|
|
5366
|
-
const backendDir =
|
|
5367
|
-
const registryFile =
|
|
6166
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
6167
|
+
const registryFile = path17.join(projectDir, "docs/api-registry.md");
|
|
5368
6168
|
const registryExists = await FileUtils.exists(registryFile);
|
|
5369
6169
|
if (!registryExists) {
|
|
5370
6170
|
logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
|
|
@@ -5376,10 +6176,10 @@ async function detectApiChanges(projectDir) {
|
|
|
5376
6176
|
const registryContent = await FileUtils.read(registryFile);
|
|
5377
6177
|
const existingApis = extractApisFromRegistry(registryContent);
|
|
5378
6178
|
const currentApis = /* @__PURE__ */ new Map();
|
|
5379
|
-
const srcDir =
|
|
6179
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5380
6180
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5381
6181
|
for (const controllerFile of controllers) {
|
|
5382
|
-
const controllerPath =
|
|
6182
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5383
6183
|
const apis = await extractApisFromController(controllerPath);
|
|
5384
6184
|
for (const api of apis) {
|
|
5385
6185
|
const key = `${api.method}:${api.path}`;
|
|
@@ -5441,9 +6241,9 @@ async function detectApiChanges(projectDir) {
|
|
|
5441
6241
|
}
|
|
5442
6242
|
}
|
|
5443
6243
|
async function generateApiRegistry(projectDir) {
|
|
5444
|
-
const registryFile =
|
|
6244
|
+
const registryFile = path17.join(projectDir, "docs/api-registry.md");
|
|
5445
6245
|
logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
|
|
5446
|
-
await FileUtils.ensureDir(
|
|
6246
|
+
await FileUtils.ensureDir(path17.dirname(registryFile));
|
|
5447
6247
|
const header = `# API Registry
|
|
5448
6248
|
|
|
5449
6249
|
> \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
|
|
@@ -5472,14 +6272,14 @@ async function generateApiRegistry(projectDir) {
|
|
|
5472
6272
|
*\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
|
|
5473
6273
|
`;
|
|
5474
6274
|
let content = header;
|
|
5475
|
-
const backendDir =
|
|
6275
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
5476
6276
|
const exists = await FileUtils.exists(backendDir);
|
|
5477
6277
|
if (exists) {
|
|
5478
|
-
const srcDir =
|
|
6278
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5479
6279
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5480
6280
|
const moduleMap = /* @__PURE__ */ new Map();
|
|
5481
6281
|
for (const controllerFile of controllers) {
|
|
5482
|
-
const controllerPath =
|
|
6282
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5483
6283
|
const controllerName = controllerFile.replace(".java", "");
|
|
5484
6284
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
5485
6285
|
if (!moduleMap.has(module)) {
|
|
@@ -5563,10 +6363,10 @@ function extractApisFromRegistry(registryContent) {
|
|
|
5563
6363
|
let match;
|
|
5564
6364
|
while ((match = apiRegex.exec(registryContent)) !== null) {
|
|
5565
6365
|
const method = match[1];
|
|
5566
|
-
const
|
|
6366
|
+
const path21 = match[2].trim();
|
|
5567
6367
|
const description = match[3].trim();
|
|
5568
|
-
const key = `${method}:${
|
|
5569
|
-
apis.set(key, { method, path:
|
|
6368
|
+
const key = `${method}:${path21}`;
|
|
6369
|
+
apis.set(key, { method, path: path21, description });
|
|
5570
6370
|
}
|
|
5571
6371
|
return apis;
|
|
5572
6372
|
}
|
|
@@ -5587,10 +6387,10 @@ function extractMethodComment2(content, methodName) {
|
|
|
5587
6387
|
init_esm_shims();
|
|
5588
6388
|
init_utils();
|
|
5589
6389
|
init_logger();
|
|
5590
|
-
import { Command as
|
|
5591
|
-
import
|
|
5592
|
-
import
|
|
5593
|
-
var logsCommand = new
|
|
6390
|
+
import { Command as Command13 } from "commander";
|
|
6391
|
+
import path18 from "path";
|
|
6392
|
+
import inquirer9 from "inquirer";
|
|
6393
|
+
var 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") => {
|
|
5594
6394
|
try {
|
|
5595
6395
|
logger.header("\u4F1A\u8BDD\u65E5\u5FD7");
|
|
5596
6396
|
logger.newLine();
|
|
@@ -5613,7 +6413,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
5613
6413
|
case "":
|
|
5614
6414
|
case "today": {
|
|
5615
6415
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5616
|
-
targetDir =
|
|
6416
|
+
targetDir = path18.join(sessionsDir, today);
|
|
5617
6417
|
const todayExists = await FileUtils.exists(targetDir);
|
|
5618
6418
|
if (!todayExists) {
|
|
5619
6419
|
logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
|
|
@@ -5629,7 +6429,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
5629
6429
|
break;
|
|
5630
6430
|
}
|
|
5631
6431
|
default: {
|
|
5632
|
-
targetDir =
|
|
6432
|
+
targetDir = path18.join(sessionsDir, filter);
|
|
5633
6433
|
const dateExists = await FileUtils.exists(targetDir);
|
|
5634
6434
|
if (!dateExists) {
|
|
5635
6435
|
logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
|
|
@@ -5653,11 +6453,11 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
5653
6453
|
process.exit(0);
|
|
5654
6454
|
}
|
|
5655
6455
|
for (let i = 0; i < logs.length; i++) {
|
|
5656
|
-
const relPath =
|
|
6456
|
+
const relPath = path18.relative(sessionsDir, logs[i]);
|
|
5657
6457
|
logger.step(`${i + 1}) ${relPath}`);
|
|
5658
6458
|
}
|
|
5659
6459
|
logger.newLine();
|
|
5660
|
-
const answers = await
|
|
6460
|
+
const answers = await inquirer9.prompt([
|
|
5661
6461
|
{
|
|
5662
6462
|
type: "input",
|
|
5663
6463
|
name: "selection",
|
|
@@ -5694,7 +6494,7 @@ async function collectLogFiles(targetDir) {
|
|
|
5694
6494
|
const allFiles = await FileUtils.findFiles("*.md", targetDir);
|
|
5695
6495
|
const filtered = allFiles.filter((f) => f !== "index.md");
|
|
5696
6496
|
for (const file of filtered) {
|
|
5697
|
-
const filePath =
|
|
6497
|
+
const filePath = path18.join(targetDir, file);
|
|
5698
6498
|
const stat = await FileUtils.exists(filePath);
|
|
5699
6499
|
if (stat) {
|
|
5700
6500
|
logs.push(filePath);
|
|
@@ -5707,14 +6507,14 @@ async function collectLogFiles(targetDir) {
|
|
|
5707
6507
|
|
|
5708
6508
|
// src/commands/update.ts
|
|
5709
6509
|
init_esm_shims();
|
|
5710
|
-
import { Command as
|
|
5711
|
-
import
|
|
6510
|
+
import { Command as Command14 } from "commander";
|
|
6511
|
+
import path19 from "path";
|
|
5712
6512
|
init_logger();
|
|
5713
6513
|
init_utils();
|
|
5714
6514
|
import { execa as execa4 } from "execa";
|
|
5715
|
-
import
|
|
6515
|
+
import inquirer10 from "inquirer";
|
|
5716
6516
|
import fs4 from "fs-extra";
|
|
5717
|
-
var updateCommand = new
|
|
6517
|
+
var 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) => {
|
|
5718
6518
|
try {
|
|
5719
6519
|
logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
|
|
5720
6520
|
logger.newLine();
|
|
@@ -5786,7 +6586,7 @@ var updateCommand = new Command13("update").description("\u68C0\u67E5\u5E76\u66F
|
|
|
5786
6586
|
logger.info("Dry run \u6A21\u5F0F\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u66F4\u65B0");
|
|
5787
6587
|
return;
|
|
5788
6588
|
}
|
|
5789
|
-
const answers = await
|
|
6589
|
+
const answers = await inquirer10.prompt([
|
|
5790
6590
|
{
|
|
5791
6591
|
type: "confirm",
|
|
5792
6592
|
name: "shouldUpdate",
|
|
@@ -5813,7 +6613,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5813
6613
|
for (const update of updates) {
|
|
5814
6614
|
const { type, info, updateOptions } = update;
|
|
5815
6615
|
const targetDir = type === "frontend" ? "frontend" : "backend";
|
|
5816
|
-
const targetPath =
|
|
6616
|
+
const targetPath = path19.join(projectPath, targetDir);
|
|
5817
6617
|
logger.newLine();
|
|
5818
6618
|
logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
|
|
5819
6619
|
if (updateOptions?.tag || updateOptions?.branch) {
|
|
@@ -5844,8 +6644,8 @@ async function performUpdate(projectPath, updates) {
|
|
|
5844
6644
|
}
|
|
5845
6645
|
}
|
|
5846
6646
|
const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
|
|
5847
|
-
const backupDir =
|
|
5848
|
-
await fs4.copy(targetPath,
|
|
6647
|
+
const backupDir = path19.join(projectPath, `.backup-${Date.now()}`);
|
|
6648
|
+
await fs4.copy(targetPath, path19.join(backupDir, targetDir));
|
|
5849
6649
|
logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
|
|
5850
6650
|
if (updateOptions?.dryRun) {
|
|
5851
6651
|
logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
|
|
@@ -5855,7 +6655,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5855
6655
|
continue;
|
|
5856
6656
|
}
|
|
5857
6657
|
try {
|
|
5858
|
-
const tempDir =
|
|
6658
|
+
const tempDir = path19.join(projectPath, `.template-update-${Date.now()}`);
|
|
5859
6659
|
await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
|
|
5860
6660
|
stdio: "pipe"
|
|
5861
6661
|
});
|
|
@@ -5872,7 +6672,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5872
6672
|
const currentFiles = await FileUtils.findFiles("*", targetPath);
|
|
5873
6673
|
for (const file of currentFiles) {
|
|
5874
6674
|
if (!keepFiles.includes(file)) {
|
|
5875
|
-
const filePath =
|
|
6675
|
+
const filePath = path19.join(targetPath, file);
|
|
5876
6676
|
try {
|
|
5877
6677
|
await fs4.remove(filePath);
|
|
5878
6678
|
} catch {
|
|
@@ -5894,7 +6694,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5894
6694
|
logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
5895
6695
|
logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
|
|
5896
6696
|
await fs4.remove(targetPath);
|
|
5897
|
-
await fs4.copy(
|
|
6697
|
+
await fs4.copy(path19.join(backupDir, targetDir), targetPath);
|
|
5898
6698
|
await fs4.remove(backupDir);
|
|
5899
6699
|
logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
|
|
5900
6700
|
}
|
|
@@ -5915,16 +6715,16 @@ init_esm_shims();
|
|
|
5915
6715
|
init_user_config();
|
|
5916
6716
|
init_gitlab_api();
|
|
5917
6717
|
init_logger();
|
|
5918
|
-
import { Command as
|
|
5919
|
-
import
|
|
6718
|
+
import { Command as Command15 } from "commander";
|
|
6719
|
+
import inquirer11 from "inquirer";
|
|
5920
6720
|
import chalk2 from "chalk";
|
|
5921
|
-
var setTokenCommand = new
|
|
6721
|
+
var 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) => {
|
|
5922
6722
|
try {
|
|
5923
6723
|
logger.header("GitLab Access Token \u914D\u7F6E");
|
|
5924
6724
|
logger.newLine();
|
|
5925
6725
|
let { token, url } = options;
|
|
5926
6726
|
if (!token) {
|
|
5927
|
-
const answers = await
|
|
6727
|
+
const answers = await inquirer11.prompt([
|
|
5928
6728
|
{
|
|
5929
6729
|
type: "password",
|
|
5930
6730
|
name: "token",
|
|
@@ -5986,7 +6786,7 @@ var setTokenCommand = new Command14("set-token").description("\u8BBE\u7F6E GitLa
|
|
|
5986
6786
|
process.exit(1);
|
|
5987
6787
|
}
|
|
5988
6788
|
});
|
|
5989
|
-
var showConfigCommand = new
|
|
6789
|
+
var showConfigCommand = new Command15("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
|
|
5990
6790
|
try {
|
|
5991
6791
|
logger.header("GitLab \u914D\u7F6E");
|
|
5992
6792
|
logger.newLine();
|
|
@@ -6025,14 +6825,14 @@ var showConfigCommand = new Command14("show").description("\u663E\u793A\u5F53\u5
|
|
|
6025
6825
|
process.exit(1);
|
|
6026
6826
|
}
|
|
6027
6827
|
});
|
|
6028
|
-
var removeConfigCommand = new
|
|
6828
|
+
var removeConfigCommand = new Command15("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
|
|
6029
6829
|
try {
|
|
6030
6830
|
const hasConfig = await userConfigManager.hasConfig();
|
|
6031
6831
|
if (!hasConfig) {
|
|
6032
6832
|
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
6033
6833
|
return;
|
|
6034
6834
|
}
|
|
6035
|
-
const answers = await
|
|
6835
|
+
const answers = await inquirer11.prompt([
|
|
6036
6836
|
{
|
|
6037
6837
|
type: "confirm",
|
|
6038
6838
|
name: "confirm",
|
|
@@ -6054,7 +6854,7 @@ var removeConfigCommand = new Command14("remove").alias("rm").description("\u522
|
|
|
6054
6854
|
process.exit(1);
|
|
6055
6855
|
}
|
|
6056
6856
|
});
|
|
6057
|
-
var validateTokenCommand = new
|
|
6857
|
+
var validateTokenCommand = new Command15("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
|
|
6058
6858
|
try {
|
|
6059
6859
|
logger.header("\u9A8C\u8BC1 GitLab Token");
|
|
6060
6860
|
logger.newLine();
|
|
@@ -6084,17 +6884,17 @@ var validateTokenCommand = new Command14("validate").alias("test").description("
|
|
|
6084
6884
|
process.exit(1);
|
|
6085
6885
|
}
|
|
6086
6886
|
});
|
|
6087
|
-
var configCommand = new
|
|
6887
|
+
var configCommand = new Command15("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
|
|
6088
6888
|
|
|
6089
6889
|
// src/commands/diff.ts
|
|
6090
6890
|
init_esm_shims();
|
|
6091
|
-
import { Command as
|
|
6891
|
+
import { Command as Command16 } from "commander";
|
|
6092
6892
|
import chalk3 from "chalk";
|
|
6093
6893
|
init_logger();
|
|
6094
6894
|
init_utils();
|
|
6095
6895
|
init_user_config();
|
|
6096
6896
|
init_gitlab_api();
|
|
6097
|
-
var diffCommand = new
|
|
6897
|
+
var 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) => {
|
|
6098
6898
|
try {
|
|
6099
6899
|
logger.header("\u6A21\u677F\u7248\u672C\u5BF9\u6BD4");
|
|
6100
6900
|
logger.newLine();
|
|
@@ -6380,9 +7180,9 @@ var Table = class {
|
|
|
6380
7180
|
};
|
|
6381
7181
|
|
|
6382
7182
|
// src/index.ts
|
|
6383
|
-
var __dirname2 =
|
|
6384
|
-
var pkg = fs5.readJsonSync(
|
|
6385
|
-
var program = new
|
|
7183
|
+
var __dirname2 = path20.dirname(fileURLToPath2(import.meta.url));
|
|
7184
|
+
var pkg = fs5.readJsonSync(path20.join(__dirname2, "../package.json"));
|
|
7185
|
+
var program = new Command17();
|
|
6386
7186
|
program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version(pkg.version);
|
|
6387
7187
|
program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
|
|
6388
7188
|
program.addCommand(initCommand);
|
|
@@ -6392,6 +7192,7 @@ program.addCommand(devCommand);
|
|
|
6392
7192
|
program.addCommand(addFeatureCommand);
|
|
6393
7193
|
program.addCommand(bugfixCommand);
|
|
6394
7194
|
program.addCommand(hotfixCommand);
|
|
7195
|
+
program.addCommand(acceptCommand);
|
|
6395
7196
|
program.addCommand(lintCommand);
|
|
6396
7197
|
program.addCommand(statusCommand);
|
|
6397
7198
|
program.addCommand(detectDepsCommand);
|
|
@@ -6413,7 +7214,8 @@ function showHelp() {
|
|
|
6413
7214
|
console.log(" team-cli split-prd <prd-folder> \u5C06 PRD \u62C6\u5206\u6210\u591A\u4E2A specs");
|
|
6414
7215
|
console.log(" team-cli breakdown [spec-file] \u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos");
|
|
6415
7216
|
console.log(" team-cli dev \u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1");
|
|
6416
|
-
console.log(" team-cli
|
|
7217
|
+
console.log(" team-cli accept [spec-file] \u9A8C\u6536\u529F\u80FD\uFF0C\u8D70\u67E5\u6240\u6709\u9700\u6C42");
|
|
7218
|
+
console.log(" team-cli add-feature <name> \u6DFB\u52A0\u65B0\u529F\u80FD");
|
|
6417
7219
|
console.log(" team-cli bugfix \u521B\u5EFA Bugfix \u8BB0\u5F55");
|
|
6418
7220
|
console.log(" team-cli hotfix \u521B\u5EFA Hotfix");
|
|
6419
7221
|
console.log(" team-cli detect-deps [spec] \u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
@@ -6438,6 +7240,7 @@ function showHelp() {
|
|
|
6438
7240
|
console.log(" 1. PRD \u2192 specs (split-prd)");
|
|
6439
7241
|
console.log(" 2. spec \u2192 milestones + todos (breakdown)");
|
|
6440
7242
|
console.log(" 3. \u9009\u62E9 milestone/todo \u2192 \u5B9E\u73B0 (dev)");
|
|
7243
|
+
console.log(" 4. \u9A8C\u6536 (accept) \u2192 \u9A8C\u8BC1\u8054\u8C03\u662F\u5426\u5B8C\u6210");
|
|
6441
7244
|
console.log("");
|
|
6442
7245
|
console.log(chalk4.bold("\u8FED\u4EE3\u6D41\u7A0B:"));
|
|
6443
7246
|
console.log(" team-cli add-feature <name> # \u6DFB\u52A0\u65B0\u529F\u80FD");
|