yg-team-cli 2.6.1 → 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/README.md +5 -0
- package/dist/cli.js +955 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.js +948 -134
- 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();
|
|
@@ -4455,15 +5255,26 @@ var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D
|
|
|
4455
5255
|
stdio: "inherit"
|
|
4456
5256
|
});
|
|
4457
5257
|
}
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
5258
|
+
if (options.typeCheck !== false) {
|
|
5259
|
+
try {
|
|
5260
|
+
logger.step("\u6B63\u5728\u8FDB\u884C TypeScript \u7C7B\u578B\u68C0\u67E5...");
|
|
5261
|
+
await execa3("npx", ["tsc", "--noEmit"], {
|
|
5262
|
+
cwd: "frontend",
|
|
5263
|
+
stdio: "pipe"
|
|
5264
|
+
});
|
|
5265
|
+
} catch (tscError) {
|
|
5266
|
+
logger.warn("TypeScript \u7C7B\u578B\u68C0\u67E5\u53D1\u73B0\u95EE\u9898\uFF0C\u8BF7\u68C0\u67E5\u4EE3\u7801\u7C7B\u578B\u5B89\u5168\u3002");
|
|
5267
|
+
if (process.env.DEBUG) {
|
|
5268
|
+
console.log(tscError.stdout);
|
|
5269
|
+
}
|
|
5270
|
+
results.frontend.errors.push("TypeScript \u7C7B\u578B\u68C0\u67E5\u5931\u8D25 (\u5DF2\u8DF3\u8FC7\u963B\u585E)");
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
4462
5273
|
results.frontend.passed = true;
|
|
4463
5274
|
logger.success("\u524D\u7AEF\u4EE3\u7801\u68C0\u67E5\u901A\u8FC7");
|
|
4464
5275
|
} catch (error) {
|
|
4465
5276
|
results.frontend.errors.push(error.message);
|
|
4466
|
-
logger.error(
|
|
5277
|
+
logger.error(`\u524D\u7AEF\u4EE3\u7801\u68C0\u67E5\u5931\u8D25: ${error.message}`);
|
|
4467
5278
|
}
|
|
4468
5279
|
} else {
|
|
4469
5280
|
logger.info("\u672A\u627E\u5230\u524D\u7AEF\u9879\u76EE (frontend/package.json)");
|
|
@@ -4549,9 +5360,9 @@ var lintCommand = new Command7("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D
|
|
|
4549
5360
|
init_esm_shims();
|
|
4550
5361
|
init_utils();
|
|
4551
5362
|
init_logger();
|
|
4552
|
-
import { Command as
|
|
4553
|
-
import
|
|
4554
|
-
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 () => {
|
|
4555
5366
|
try {
|
|
4556
5367
|
logger.header("\u9879\u76EE\u72B6\u6001");
|
|
4557
5368
|
logger.newLine();
|
|
@@ -4608,7 +5419,7 @@ async function displayFeatureInventory() {
|
|
|
4608
5419
|
}
|
|
4609
5420
|
const inventory = [];
|
|
4610
5421
|
for (const file of specs) {
|
|
4611
|
-
const filePath =
|
|
5422
|
+
const filePath = path14.join(specDir, file);
|
|
4612
5423
|
const content = await FileUtils.read(filePath);
|
|
4613
5424
|
const status = SpecUtils.parseSpecStatus(content);
|
|
4614
5425
|
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
@@ -4668,7 +5479,7 @@ async function displayRecentActivity() {
|
|
|
4668
5479
|
}
|
|
4669
5480
|
const sorted = files.sort().reverse().slice(0, 5);
|
|
4670
5481
|
for (const file of sorted) {
|
|
4671
|
-
const filePath =
|
|
5482
|
+
const filePath = path14.join(sessionDir, file);
|
|
4672
5483
|
const stat = await FileUtils.read(filePath);
|
|
4673
5484
|
const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
|
|
4674
5485
|
const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
|
|
@@ -4687,10 +5498,10 @@ async function displayRecentActivity() {
|
|
|
4687
5498
|
init_esm_shims();
|
|
4688
5499
|
init_utils();
|
|
4689
5500
|
init_logger();
|
|
4690
|
-
import { Command as
|
|
4691
|
-
import
|
|
4692
|
-
import
|
|
4693
|
-
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) => {
|
|
4694
5505
|
try {
|
|
4695
5506
|
logger.header("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
4696
5507
|
logger.newLine();
|
|
@@ -4716,7 +5527,7 @@ var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spe
|
|
|
4716
5527
|
process.exit(1);
|
|
4717
5528
|
}
|
|
4718
5529
|
for (const spec of specs) {
|
|
4719
|
-
const specPath =
|
|
5530
|
+
const specPath = path15.join(specsDir, spec);
|
|
4720
5531
|
logger.step(`\u5904\u7406: ${spec}`);
|
|
4721
5532
|
await detectDependencies(specPath);
|
|
4722
5533
|
logger.newLine();
|
|
@@ -4742,7 +5553,7 @@ async function detectDependencies(specFile) {
|
|
|
4742
5553
|
logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
|
|
4743
5554
|
const projectDir = ".";
|
|
4744
5555
|
const allDeps = /* @__PURE__ */ new Set();
|
|
4745
|
-
const backendDir =
|
|
5556
|
+
const backendDir = path15.join(projectDir, "backend");
|
|
4746
5557
|
const backendExists = await FileUtils.exists(backendDir);
|
|
4747
5558
|
if (backendExists) {
|
|
4748
5559
|
const apiDeps = await scanBackendApiCalls(backendDir);
|
|
@@ -4752,7 +5563,7 @@ async function detectDependencies(specFile) {
|
|
|
4752
5563
|
const serviceDeps = await scanBackendServiceRefs(backendDir);
|
|
4753
5564
|
serviceDeps.forEach((d) => allDeps.add(d));
|
|
4754
5565
|
}
|
|
4755
|
-
const frontendDir =
|
|
5566
|
+
const frontendDir = path15.join(projectDir, "frontend");
|
|
4756
5567
|
const frontendExists = await FileUtils.exists(frontendDir);
|
|
4757
5568
|
if (frontendExists) {
|
|
4758
5569
|
const frontendDeps = await scanFrontendApiCalls(frontendDir);
|
|
@@ -4778,7 +5589,7 @@ async function detectDependencies(specFile) {
|
|
|
4778
5589
|
logger.step(`- ${spec}`);
|
|
4779
5590
|
}
|
|
4780
5591
|
logger.newLine();
|
|
4781
|
-
const answers = await
|
|
5592
|
+
const answers = await inquirer7.prompt([
|
|
4782
5593
|
{
|
|
4783
5594
|
type: "confirm",
|
|
4784
5595
|
name: "autoUpdate",
|
|
@@ -4793,11 +5604,11 @@ async function detectDependencies(specFile) {
|
|
|
4793
5604
|
}
|
|
4794
5605
|
async function scanBackendApiCalls(backendDir) {
|
|
4795
5606
|
const deps = [];
|
|
4796
|
-
const srcDir =
|
|
5607
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4797
5608
|
try {
|
|
4798
5609
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4799
5610
|
for (const file of javaFiles) {
|
|
4800
|
-
const filePath =
|
|
5611
|
+
const filePath = path15.join(srcDir, file);
|
|
4801
5612
|
const content = await FileUtils.read(filePath);
|
|
4802
5613
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
4803
5614
|
let match;
|
|
@@ -4815,11 +5626,11 @@ async function scanBackendApiCalls(backendDir) {
|
|
|
4815
5626
|
}
|
|
4816
5627
|
async function scanBackendEntityRelations(backendDir) {
|
|
4817
5628
|
const deps = [];
|
|
4818
|
-
const srcDir =
|
|
5629
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4819
5630
|
try {
|
|
4820
5631
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4821
5632
|
for (const file of javaFiles) {
|
|
4822
|
-
const filePath =
|
|
5633
|
+
const filePath = path15.join(srcDir, file);
|
|
4823
5634
|
const content = await FileUtils.read(filePath);
|
|
4824
5635
|
if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
|
|
4825
5636
|
const typeRegex = /type\s*=\s*(\w+)/g;
|
|
@@ -4835,11 +5646,11 @@ async function scanBackendEntityRelations(backendDir) {
|
|
|
4835
5646
|
}
|
|
4836
5647
|
async function scanBackendServiceRefs(backendDir) {
|
|
4837
5648
|
const deps = [];
|
|
4838
|
-
const srcDir =
|
|
5649
|
+
const srcDir = path15.join(backendDir, "src");
|
|
4839
5650
|
try {
|
|
4840
5651
|
const javaFiles = await FileUtils.findFiles("*.java", srcDir);
|
|
4841
5652
|
for (const file of javaFiles) {
|
|
4842
|
-
const filePath =
|
|
5653
|
+
const filePath = path15.join(srcDir, file);
|
|
4843
5654
|
const content = await FileUtils.read(filePath);
|
|
4844
5655
|
const serviceRegex = /private\s+(\w+)Service/g;
|
|
4845
5656
|
let match;
|
|
@@ -4855,11 +5666,11 @@ async function scanBackendServiceRefs(backendDir) {
|
|
|
4855
5666
|
}
|
|
4856
5667
|
async function scanFrontendApiCalls(frontendDir) {
|
|
4857
5668
|
const deps = [];
|
|
4858
|
-
const srcDir =
|
|
5669
|
+
const srcDir = path15.join(frontendDir, "src");
|
|
4859
5670
|
try {
|
|
4860
5671
|
const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
|
|
4861
5672
|
for (const file of tsFiles) {
|
|
4862
|
-
const filePath =
|
|
5673
|
+
const filePath = path15.join(srcDir, file);
|
|
4863
5674
|
const content = await FileUtils.read(filePath);
|
|
4864
5675
|
const pathRegex = /"(\/api\/[^"]+)"/g;
|
|
4865
5676
|
let match;
|
|
@@ -4948,9 +5759,9 @@ ${deps.map((d) => ` - [x] ${d}`).join("\n")}
|
|
|
4948
5759
|
init_esm_shims();
|
|
4949
5760
|
init_utils();
|
|
4950
5761
|
init_logger();
|
|
4951
|
-
import { Command as
|
|
4952
|
-
import
|
|
4953
|
-
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 () => {
|
|
4954
5765
|
try {
|
|
4955
5766
|
logger.header("\u540C\u6B65 AI_MEMORY.md");
|
|
4956
5767
|
logger.newLine();
|
|
@@ -4982,7 +5793,7 @@ var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 A
|
|
|
4982
5793
|
});
|
|
4983
5794
|
async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
4984
5795
|
logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
|
|
4985
|
-
const specsDir =
|
|
5796
|
+
const specsDir = path16.join(projectDir, "docs/specs");
|
|
4986
5797
|
const exists = await FileUtils.exists(specsDir);
|
|
4987
5798
|
if (!exists) {
|
|
4988
5799
|
return;
|
|
@@ -4997,7 +5808,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
4997
5808
|
for (const specFile of specs) {
|
|
4998
5809
|
const name = specFile.replace(".md", "");
|
|
4999
5810
|
const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
5000
|
-
const specPath =
|
|
5811
|
+
const specPath = path16.join(specsDir, specFile);
|
|
5001
5812
|
const content = await FileUtils.read(specPath);
|
|
5002
5813
|
const status = SpecUtils.parseSpecStatus(content);
|
|
5003
5814
|
const statusWithIcon = SpecUtils.getStatusWithIcon(status);
|
|
@@ -5016,7 +5827,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
|
5016
5827
|
}
|
|
5017
5828
|
async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
5018
5829
|
logger.step("\u540C\u6B65 API \u5217\u8868...");
|
|
5019
|
-
const backendDir =
|
|
5830
|
+
const backendDir = path16.join(projectDir, "backend");
|
|
5020
5831
|
const exists = await FileUtils.exists(backendDir);
|
|
5021
5832
|
if (!exists) {
|
|
5022
5833
|
return;
|
|
@@ -5026,13 +5837,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
|
|
|
5026
5837
|
lines.push("");
|
|
5027
5838
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
|
|
5028
5839
|
lines.push("");
|
|
5029
|
-
const srcDir =
|
|
5840
|
+
const srcDir = path16.join(backendDir, "src");
|
|
5030
5841
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5031
5842
|
if (controllers.length === 0) {
|
|
5032
5843
|
lines.push("\u6682\u65E0 API");
|
|
5033
5844
|
} else {
|
|
5034
5845
|
for (const controllerFile of controllers) {
|
|
5035
|
-
const controllerPath =
|
|
5846
|
+
const controllerPath = path16.join(srcDir, controllerFile);
|
|
5036
5847
|
const controllerName = controllerFile.replace(".java", "");
|
|
5037
5848
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
5038
5849
|
lines.push(`### ${module} \u6A21\u5757`);
|
|
@@ -5109,7 +5920,7 @@ function extractMethodComment(content, methodName) {
|
|
|
5109
5920
|
}
|
|
5110
5921
|
async function syncDataModels(aiMemoryFile, projectDir) {
|
|
5111
5922
|
logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
|
|
5112
|
-
const backendDir =
|
|
5923
|
+
const backendDir = path16.join(projectDir, "backend");
|
|
5113
5924
|
const exists = await FileUtils.exists(backendDir);
|
|
5114
5925
|
if (!exists) {
|
|
5115
5926
|
return;
|
|
@@ -5119,7 +5930,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
5119
5930
|
lines.push("");
|
|
5120
5931
|
lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
|
|
5121
5932
|
lines.push("");
|
|
5122
|
-
const srcDir =
|
|
5933
|
+
const srcDir = path16.join(backendDir, "src");
|
|
5123
5934
|
const entities = await FileUtils.findFiles("*Entity.java", srcDir);
|
|
5124
5935
|
if (entities.length === 0) {
|
|
5125
5936
|
lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
|
|
@@ -5127,7 +5938,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
|
|
|
5127
5938
|
lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
|
|
5128
5939
|
lines.push("|------|------|------|------|");
|
|
5129
5940
|
for (const entityFile of entities) {
|
|
5130
|
-
const entityPath =
|
|
5941
|
+
const entityPath = path16.join(srcDir, entityFile);
|
|
5131
5942
|
const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
|
|
5132
5943
|
const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
5133
5944
|
const content = await FileUtils.read(entityPath);
|
|
@@ -5229,26 +6040,26 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
|
5229
6040
|
await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
|
|
5230
6041
|
}
|
|
5231
6042
|
function extractRepoName(repository) {
|
|
5232
|
-
let
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
const parts =
|
|
6043
|
+
let path21 = repository;
|
|
6044
|
+
path21 = path21.replace(/^https?:\/\//, "");
|
|
6045
|
+
path21 = path21.replace(/^git@/, "");
|
|
6046
|
+
const parts = path21.split("/");
|
|
5236
6047
|
if (parts.length > 1) {
|
|
5237
|
-
|
|
6048
|
+
path21 = parts.slice(1).join("/");
|
|
5238
6049
|
}
|
|
5239
|
-
|
|
5240
|
-
return
|
|
6050
|
+
path21 = path21.replace(/\.git$/, "");
|
|
6051
|
+
return path21;
|
|
5241
6052
|
}
|
|
5242
6053
|
|
|
5243
6054
|
// src/commands/check-api.ts
|
|
5244
6055
|
init_esm_shims();
|
|
5245
6056
|
init_utils();
|
|
5246
6057
|
init_logger();
|
|
5247
|
-
import { Command as
|
|
5248
|
-
import
|
|
5249
|
-
import
|
|
6058
|
+
import { Command as Command12 } from "commander";
|
|
6059
|
+
import path17 from "path";
|
|
6060
|
+
import inquirer8 from "inquirer";
|
|
5250
6061
|
import { Listr as Listr6 } from "listr2";
|
|
5251
|
-
var checkApiCommand = new
|
|
6062
|
+
var checkApiCommand = new Command12("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
|
|
5252
6063
|
try {
|
|
5253
6064
|
logger.header("API \u68C0\u67E5");
|
|
5254
6065
|
logger.newLine();
|
|
@@ -5258,7 +6069,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
|
|
|
5258
6069
|
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
5259
6070
|
process.exit(1);
|
|
5260
6071
|
}
|
|
5261
|
-
const answers = await
|
|
6072
|
+
const answers = await inquirer8.prompt([
|
|
5262
6073
|
{
|
|
5263
6074
|
type: "list",
|
|
5264
6075
|
name: "checkType",
|
|
@@ -5309,7 +6120,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
|
|
|
5309
6120
|
}
|
|
5310
6121
|
});
|
|
5311
6122
|
async function checkApiConflicts(projectDir) {
|
|
5312
|
-
const backendDir =
|
|
6123
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
5313
6124
|
const exists = await FileUtils.exists(backendDir);
|
|
5314
6125
|
if (!exists) {
|
|
5315
6126
|
logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
|
|
@@ -5318,10 +6129,10 @@ async function checkApiConflicts(projectDir) {
|
|
|
5318
6129
|
logger.step("\u626B\u63CF\u540E\u7AEF API...");
|
|
5319
6130
|
logger.newLine();
|
|
5320
6131
|
const apiMap = /* @__PURE__ */ new Map();
|
|
5321
|
-
const srcDir =
|
|
6132
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5322
6133
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5323
6134
|
for (const controllerFile of controllers) {
|
|
5324
|
-
const controllerPath =
|
|
6135
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5325
6136
|
const apis = await extractApisFromController(controllerPath);
|
|
5326
6137
|
for (const api of apis) {
|
|
5327
6138
|
const key = `${api.method}:${api.path}`;
|
|
@@ -5352,8 +6163,8 @@ async function checkApiConflicts(projectDir) {
|
|
|
5352
6163
|
}
|
|
5353
6164
|
}
|
|
5354
6165
|
async function detectApiChanges(projectDir) {
|
|
5355
|
-
const backendDir =
|
|
5356
|
-
const registryFile =
|
|
6166
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
6167
|
+
const registryFile = path17.join(projectDir, "docs/api-registry.md");
|
|
5357
6168
|
const registryExists = await FileUtils.exists(registryFile);
|
|
5358
6169
|
if (!registryExists) {
|
|
5359
6170
|
logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
|
|
@@ -5365,10 +6176,10 @@ async function detectApiChanges(projectDir) {
|
|
|
5365
6176
|
const registryContent = await FileUtils.read(registryFile);
|
|
5366
6177
|
const existingApis = extractApisFromRegistry(registryContent);
|
|
5367
6178
|
const currentApis = /* @__PURE__ */ new Map();
|
|
5368
|
-
const srcDir =
|
|
6179
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5369
6180
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5370
6181
|
for (const controllerFile of controllers) {
|
|
5371
|
-
const controllerPath =
|
|
6182
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5372
6183
|
const apis = await extractApisFromController(controllerPath);
|
|
5373
6184
|
for (const api of apis) {
|
|
5374
6185
|
const key = `${api.method}:${api.path}`;
|
|
@@ -5430,9 +6241,9 @@ async function detectApiChanges(projectDir) {
|
|
|
5430
6241
|
}
|
|
5431
6242
|
}
|
|
5432
6243
|
async function generateApiRegistry(projectDir) {
|
|
5433
|
-
const registryFile =
|
|
6244
|
+
const registryFile = path17.join(projectDir, "docs/api-registry.md");
|
|
5434
6245
|
logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
|
|
5435
|
-
await FileUtils.ensureDir(
|
|
6246
|
+
await FileUtils.ensureDir(path17.dirname(registryFile));
|
|
5436
6247
|
const header = `# API Registry
|
|
5437
6248
|
|
|
5438
6249
|
> \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
|
|
@@ -5461,14 +6272,14 @@ async function generateApiRegistry(projectDir) {
|
|
|
5461
6272
|
*\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
|
|
5462
6273
|
`;
|
|
5463
6274
|
let content = header;
|
|
5464
|
-
const backendDir =
|
|
6275
|
+
const backendDir = path17.join(projectDir, "backend");
|
|
5465
6276
|
const exists = await FileUtils.exists(backendDir);
|
|
5466
6277
|
if (exists) {
|
|
5467
|
-
const srcDir =
|
|
6278
|
+
const srcDir = path17.join(backendDir, "src");
|
|
5468
6279
|
const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
|
|
5469
6280
|
const moduleMap = /* @__PURE__ */ new Map();
|
|
5470
6281
|
for (const controllerFile of controllers) {
|
|
5471
|
-
const controllerPath =
|
|
6282
|
+
const controllerPath = path17.join(srcDir, controllerFile);
|
|
5472
6283
|
const controllerName = controllerFile.replace(".java", "");
|
|
5473
6284
|
const module = controllerName.replace(/Controller$/, "").toLowerCase();
|
|
5474
6285
|
if (!moduleMap.has(module)) {
|
|
@@ -5552,10 +6363,10 @@ function extractApisFromRegistry(registryContent) {
|
|
|
5552
6363
|
let match;
|
|
5553
6364
|
while ((match = apiRegex.exec(registryContent)) !== null) {
|
|
5554
6365
|
const method = match[1];
|
|
5555
|
-
const
|
|
6366
|
+
const path21 = match[2].trim();
|
|
5556
6367
|
const description = match[3].trim();
|
|
5557
|
-
const key = `${method}:${
|
|
5558
|
-
apis.set(key, { method, path:
|
|
6368
|
+
const key = `${method}:${path21}`;
|
|
6369
|
+
apis.set(key, { method, path: path21, description });
|
|
5559
6370
|
}
|
|
5560
6371
|
return apis;
|
|
5561
6372
|
}
|
|
@@ -5576,10 +6387,10 @@ function extractMethodComment2(content, methodName) {
|
|
|
5576
6387
|
init_esm_shims();
|
|
5577
6388
|
init_utils();
|
|
5578
6389
|
init_logger();
|
|
5579
|
-
import { Command as
|
|
5580
|
-
import
|
|
5581
|
-
import
|
|
5582
|
-
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") => {
|
|
5583
6394
|
try {
|
|
5584
6395
|
logger.header("\u4F1A\u8BDD\u65E5\u5FD7");
|
|
5585
6396
|
logger.newLine();
|
|
@@ -5602,7 +6413,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
5602
6413
|
case "":
|
|
5603
6414
|
case "today": {
|
|
5604
6415
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5605
|
-
targetDir =
|
|
6416
|
+
targetDir = path18.join(sessionsDir, today);
|
|
5606
6417
|
const todayExists = await FileUtils.exists(targetDir);
|
|
5607
6418
|
if (!todayExists) {
|
|
5608
6419
|
logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
|
|
@@ -5618,7 +6429,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
5618
6429
|
break;
|
|
5619
6430
|
}
|
|
5620
6431
|
default: {
|
|
5621
|
-
targetDir =
|
|
6432
|
+
targetDir = path18.join(sessionsDir, filter);
|
|
5622
6433
|
const dateExists = await FileUtils.exists(targetDir);
|
|
5623
6434
|
if (!dateExists) {
|
|
5624
6435
|
logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
|
|
@@ -5642,11 +6453,11 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
|
|
|
5642
6453
|
process.exit(0);
|
|
5643
6454
|
}
|
|
5644
6455
|
for (let i = 0; i < logs.length; i++) {
|
|
5645
|
-
const relPath =
|
|
6456
|
+
const relPath = path18.relative(sessionsDir, logs[i]);
|
|
5646
6457
|
logger.step(`${i + 1}) ${relPath}`);
|
|
5647
6458
|
}
|
|
5648
6459
|
logger.newLine();
|
|
5649
|
-
const answers = await
|
|
6460
|
+
const answers = await inquirer9.prompt([
|
|
5650
6461
|
{
|
|
5651
6462
|
type: "input",
|
|
5652
6463
|
name: "selection",
|
|
@@ -5683,7 +6494,7 @@ async function collectLogFiles(targetDir) {
|
|
|
5683
6494
|
const allFiles = await FileUtils.findFiles("*.md", targetDir);
|
|
5684
6495
|
const filtered = allFiles.filter((f) => f !== "index.md");
|
|
5685
6496
|
for (const file of filtered) {
|
|
5686
|
-
const filePath =
|
|
6497
|
+
const filePath = path18.join(targetDir, file);
|
|
5687
6498
|
const stat = await FileUtils.exists(filePath);
|
|
5688
6499
|
if (stat) {
|
|
5689
6500
|
logs.push(filePath);
|
|
@@ -5696,14 +6507,14 @@ async function collectLogFiles(targetDir) {
|
|
|
5696
6507
|
|
|
5697
6508
|
// src/commands/update.ts
|
|
5698
6509
|
init_esm_shims();
|
|
5699
|
-
import { Command as
|
|
5700
|
-
import
|
|
6510
|
+
import { Command as Command14 } from "commander";
|
|
6511
|
+
import path19 from "path";
|
|
5701
6512
|
init_logger();
|
|
5702
6513
|
init_utils();
|
|
5703
6514
|
import { execa as execa4 } from "execa";
|
|
5704
|
-
import
|
|
6515
|
+
import inquirer10 from "inquirer";
|
|
5705
6516
|
import fs4 from "fs-extra";
|
|
5706
|
-
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) => {
|
|
5707
6518
|
try {
|
|
5708
6519
|
logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
|
|
5709
6520
|
logger.newLine();
|
|
@@ -5775,7 +6586,7 @@ var updateCommand = new Command13("update").description("\u68C0\u67E5\u5E76\u66F
|
|
|
5775
6586
|
logger.info("Dry run \u6A21\u5F0F\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u66F4\u65B0");
|
|
5776
6587
|
return;
|
|
5777
6588
|
}
|
|
5778
|
-
const answers = await
|
|
6589
|
+
const answers = await inquirer10.prompt([
|
|
5779
6590
|
{
|
|
5780
6591
|
type: "confirm",
|
|
5781
6592
|
name: "shouldUpdate",
|
|
@@ -5802,7 +6613,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5802
6613
|
for (const update of updates) {
|
|
5803
6614
|
const { type, info, updateOptions } = update;
|
|
5804
6615
|
const targetDir = type === "frontend" ? "frontend" : "backend";
|
|
5805
|
-
const targetPath =
|
|
6616
|
+
const targetPath = path19.join(projectPath, targetDir);
|
|
5806
6617
|
logger.newLine();
|
|
5807
6618
|
logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
|
|
5808
6619
|
if (updateOptions?.tag || updateOptions?.branch) {
|
|
@@ -5833,8 +6644,8 @@ async function performUpdate(projectPath, updates) {
|
|
|
5833
6644
|
}
|
|
5834
6645
|
}
|
|
5835
6646
|
const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
|
|
5836
|
-
const backupDir =
|
|
5837
|
-
await fs4.copy(targetPath,
|
|
6647
|
+
const backupDir = path19.join(projectPath, `.backup-${Date.now()}`);
|
|
6648
|
+
await fs4.copy(targetPath, path19.join(backupDir, targetDir));
|
|
5838
6649
|
logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
|
|
5839
6650
|
if (updateOptions?.dryRun) {
|
|
5840
6651
|
logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
|
|
@@ -5844,7 +6655,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5844
6655
|
continue;
|
|
5845
6656
|
}
|
|
5846
6657
|
try {
|
|
5847
|
-
const tempDir =
|
|
6658
|
+
const tempDir = path19.join(projectPath, `.template-update-${Date.now()}`);
|
|
5848
6659
|
await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
|
|
5849
6660
|
stdio: "pipe"
|
|
5850
6661
|
});
|
|
@@ -5861,7 +6672,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5861
6672
|
const currentFiles = await FileUtils.findFiles("*", targetPath);
|
|
5862
6673
|
for (const file of currentFiles) {
|
|
5863
6674
|
if (!keepFiles.includes(file)) {
|
|
5864
|
-
const filePath =
|
|
6675
|
+
const filePath = path19.join(targetPath, file);
|
|
5865
6676
|
try {
|
|
5866
6677
|
await fs4.remove(filePath);
|
|
5867
6678
|
} catch {
|
|
@@ -5883,7 +6694,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
5883
6694
|
logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
5884
6695
|
logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
|
|
5885
6696
|
await fs4.remove(targetPath);
|
|
5886
|
-
await fs4.copy(
|
|
6697
|
+
await fs4.copy(path19.join(backupDir, targetDir), targetPath);
|
|
5887
6698
|
await fs4.remove(backupDir);
|
|
5888
6699
|
logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
|
|
5889
6700
|
}
|
|
@@ -5904,16 +6715,16 @@ init_esm_shims();
|
|
|
5904
6715
|
init_user_config();
|
|
5905
6716
|
init_gitlab_api();
|
|
5906
6717
|
init_logger();
|
|
5907
|
-
import { Command as
|
|
5908
|
-
import
|
|
6718
|
+
import { Command as Command15 } from "commander";
|
|
6719
|
+
import inquirer11 from "inquirer";
|
|
5909
6720
|
import chalk2 from "chalk";
|
|
5910
|
-
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) => {
|
|
5911
6722
|
try {
|
|
5912
6723
|
logger.header("GitLab Access Token \u914D\u7F6E");
|
|
5913
6724
|
logger.newLine();
|
|
5914
6725
|
let { token, url } = options;
|
|
5915
6726
|
if (!token) {
|
|
5916
|
-
const answers = await
|
|
6727
|
+
const answers = await inquirer11.prompt([
|
|
5917
6728
|
{
|
|
5918
6729
|
type: "password",
|
|
5919
6730
|
name: "token",
|
|
@@ -5975,7 +6786,7 @@ var setTokenCommand = new Command14("set-token").description("\u8BBE\u7F6E GitLa
|
|
|
5975
6786
|
process.exit(1);
|
|
5976
6787
|
}
|
|
5977
6788
|
});
|
|
5978
|
-
var showConfigCommand = new
|
|
6789
|
+
var showConfigCommand = new Command15("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
|
|
5979
6790
|
try {
|
|
5980
6791
|
logger.header("GitLab \u914D\u7F6E");
|
|
5981
6792
|
logger.newLine();
|
|
@@ -6014,14 +6825,14 @@ var showConfigCommand = new Command14("show").description("\u663E\u793A\u5F53\u5
|
|
|
6014
6825
|
process.exit(1);
|
|
6015
6826
|
}
|
|
6016
6827
|
});
|
|
6017
|
-
var removeConfigCommand = new
|
|
6828
|
+
var removeConfigCommand = new Command15("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
|
|
6018
6829
|
try {
|
|
6019
6830
|
const hasConfig = await userConfigManager.hasConfig();
|
|
6020
6831
|
if (!hasConfig) {
|
|
6021
6832
|
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
6022
6833
|
return;
|
|
6023
6834
|
}
|
|
6024
|
-
const answers = await
|
|
6835
|
+
const answers = await inquirer11.prompt([
|
|
6025
6836
|
{
|
|
6026
6837
|
type: "confirm",
|
|
6027
6838
|
name: "confirm",
|
|
@@ -6043,7 +6854,7 @@ var removeConfigCommand = new Command14("remove").alias("rm").description("\u522
|
|
|
6043
6854
|
process.exit(1);
|
|
6044
6855
|
}
|
|
6045
6856
|
});
|
|
6046
|
-
var validateTokenCommand = new
|
|
6857
|
+
var validateTokenCommand = new Command15("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
|
|
6047
6858
|
try {
|
|
6048
6859
|
logger.header("\u9A8C\u8BC1 GitLab Token");
|
|
6049
6860
|
logger.newLine();
|
|
@@ -6073,17 +6884,17 @@ var validateTokenCommand = new Command14("validate").alias("test").description("
|
|
|
6073
6884
|
process.exit(1);
|
|
6074
6885
|
}
|
|
6075
6886
|
});
|
|
6076
|
-
var configCommand = new
|
|
6887
|
+
var configCommand = new Command15("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
|
|
6077
6888
|
|
|
6078
6889
|
// src/commands/diff.ts
|
|
6079
6890
|
init_esm_shims();
|
|
6080
|
-
import { Command as
|
|
6891
|
+
import { Command as Command16 } from "commander";
|
|
6081
6892
|
import chalk3 from "chalk";
|
|
6082
6893
|
init_logger();
|
|
6083
6894
|
init_utils();
|
|
6084
6895
|
init_user_config();
|
|
6085
6896
|
init_gitlab_api();
|
|
6086
|
-
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) => {
|
|
6087
6898
|
try {
|
|
6088
6899
|
logger.header("\u6A21\u677F\u7248\u672C\u5BF9\u6BD4");
|
|
6089
6900
|
logger.newLine();
|
|
@@ -6369,9 +7180,9 @@ var Table = class {
|
|
|
6369
7180
|
};
|
|
6370
7181
|
|
|
6371
7182
|
// src/index.ts
|
|
6372
|
-
var __dirname2 =
|
|
6373
|
-
var pkg = fs5.readJsonSync(
|
|
6374
|
-
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();
|
|
6375
7186
|
program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version(pkg.version);
|
|
6376
7187
|
program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
|
|
6377
7188
|
program.addCommand(initCommand);
|
|
@@ -6381,6 +7192,7 @@ program.addCommand(devCommand);
|
|
|
6381
7192
|
program.addCommand(addFeatureCommand);
|
|
6382
7193
|
program.addCommand(bugfixCommand);
|
|
6383
7194
|
program.addCommand(hotfixCommand);
|
|
7195
|
+
program.addCommand(acceptCommand);
|
|
6384
7196
|
program.addCommand(lintCommand);
|
|
6385
7197
|
program.addCommand(statusCommand);
|
|
6386
7198
|
program.addCommand(detectDepsCommand);
|
|
@@ -6402,7 +7214,8 @@ function showHelp() {
|
|
|
6402
7214
|
console.log(" team-cli split-prd <prd-folder> \u5C06 PRD \u62C6\u5206\u6210\u591A\u4E2A specs");
|
|
6403
7215
|
console.log(" team-cli breakdown [spec-file] \u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos");
|
|
6404
7216
|
console.log(" team-cli dev \u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1");
|
|
6405
|
-
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");
|
|
6406
7219
|
console.log(" team-cli bugfix \u521B\u5EFA Bugfix \u8BB0\u5F55");
|
|
6407
7220
|
console.log(" team-cli hotfix \u521B\u5EFA Hotfix");
|
|
6408
7221
|
console.log(" team-cli detect-deps [spec] \u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
@@ -6427,6 +7240,7 @@ function showHelp() {
|
|
|
6427
7240
|
console.log(" 1. PRD \u2192 specs (split-prd)");
|
|
6428
7241
|
console.log(" 2. spec \u2192 milestones + todos (breakdown)");
|
|
6429
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");
|
|
6430
7244
|
console.log("");
|
|
6431
7245
|
console.log(chalk4.bold("\u8FED\u4EE3\u6D41\u7A0B:"));
|
|
6432
7246
|
console.log(" team-cli add-feature <name> # \u6DFB\u52A0\u65B0\u529F\u80FD");
|