@spaceflow/review 0.68.0 → 0.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.js +48 -36
- package/package.json +1 -1
- package/src/issue-verify.service.ts +2 -0
- package/src/review-report/formatters/markdown.formatter.ts +25 -15
- package/src/review-report/formatters/terminal.formatter.ts +26 -18
- package/src/review-spec/types.ts +2 -2
- package/src/review.service.ts +9 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.68.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.67.0...@spaceflow/review@0.68.0) (2026-03-04)
|
|
4
|
+
|
|
5
|
+
### 代码重构
|
|
6
|
+
|
|
7
|
+
* **review:** 区分 ☹️ 和 👎 reaction 的语义,☹️ 标记无效,👎 标记未解决 ([f1419fe](https://github.com/Lydanne/spaceflow/commit/f1419fe47448a80f373ffac082ac3a2e9320d200))
|
|
8
|
+
|
|
9
|
+
### 其他修改
|
|
10
|
+
|
|
11
|
+
* **review-summary:** released version 0.35.0 [no ci] ([4f2607d](https://github.com/Lydanne/spaceflow/commit/4f2607def2725946f32eccc4aa4e687a3cdd9bab))
|
|
12
|
+
* **scripts:** released version 0.28.0 [no ci] ([55db5cf](https://github.com/Lydanne/spaceflow/commit/55db5cfa1dc0a1e318085caa0cfd9f91b06dcb21))
|
|
13
|
+
* **shell:** released version 0.28.0 [no ci] ([01f180f](https://github.com/Lydanne/spaceflow/commit/01f180f2508e75524a33e66fea580a738adc689f))
|
|
14
|
+
|
|
3
15
|
## [0.67.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.66.0...@spaceflow/review@0.67.0) (2026-03-03)
|
|
4
16
|
|
|
5
17
|
### 新特性
|
package/dist/index.js
CHANGED
|
@@ -1169,24 +1169,28 @@ class MarkdownFormatter {
|
|
|
1169
1169
|
if (summaries.length === 0) {
|
|
1170
1170
|
return "没有需要审查的文件";
|
|
1171
1171
|
}
|
|
1172
|
-
// 🟢 已修复 | 🔴
|
|
1172
|
+
// 🟢 已修复 | 🔴 error数量 | 🟡 warn数量 | ⚪ 已解决(非代码修复)
|
|
1173
1173
|
const issuesByFile = new Map();
|
|
1174
1174
|
for (const issue of issues){
|
|
1175
1175
|
if (issue.valid === "false") continue;
|
|
1176
1176
|
const stats = issuesByFile.get(issue.file) || {
|
|
1177
|
+
total: 0,
|
|
1177
1178
|
fixed: 0,
|
|
1178
|
-
|
|
1179
|
-
|
|
1179
|
+
errorCount: 0,
|
|
1180
|
+
warnCount: 0,
|
|
1180
1181
|
resolved: 0
|
|
1181
1182
|
};
|
|
1183
|
+
stats.total++;
|
|
1182
1184
|
if (issue.fixed) {
|
|
1183
1185
|
stats.fixed++;
|
|
1184
|
-
}
|
|
1186
|
+
}
|
|
1187
|
+
if (issue.resolved) {
|
|
1185
1188
|
stats.resolved++;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1189
|
+
}
|
|
1190
|
+
if (issue.severity === "error") {
|
|
1191
|
+
stats.errorCount++;
|
|
1188
1192
|
} else {
|
|
1189
|
-
stats.
|
|
1193
|
+
stats.warnCount++;
|
|
1190
1194
|
}
|
|
1191
1195
|
issuesByFile.set(issue.file, stats);
|
|
1192
1196
|
}
|
|
@@ -1202,18 +1206,18 @@ class MarkdownFormatter {
|
|
|
1202
1206
|
const fileSummaryLines = [];
|
|
1203
1207
|
for (const fileSummary of summaries){
|
|
1204
1208
|
const stats = issuesByFile.get(fileSummary.file) || {
|
|
1209
|
+
total: 0,
|
|
1205
1210
|
fixed: 0,
|
|
1206
|
-
|
|
1207
|
-
|
|
1211
|
+
errorCount: 0,
|
|
1212
|
+
warnCount: 0,
|
|
1208
1213
|
resolved: 0
|
|
1209
1214
|
};
|
|
1210
|
-
|
|
1211
|
-
totalAll += fileTotal;
|
|
1215
|
+
totalAll += stats.total;
|
|
1212
1216
|
totalFixed += stats.fixed;
|
|
1213
|
-
totalPendingErrors += stats.
|
|
1214
|
-
totalPendingWarns += stats.
|
|
1217
|
+
totalPendingErrors += stats.errorCount;
|
|
1218
|
+
totalPendingWarns += stats.warnCount;
|
|
1215
1219
|
totalResolved += stats.resolved;
|
|
1216
|
-
lines.push(`| \`${fileSummary.file}\` | ${
|
|
1220
|
+
lines.push(`| \`${fileSummary.file}\` | ${stats.total} | ${stats.fixed} | ${stats.errorCount} | ${stats.warnCount} | ${stats.resolved} |`);
|
|
1217
1221
|
// 收集问题总结用于折叠块展示
|
|
1218
1222
|
if (fileSummary.summary.trim()) {
|
|
1219
1223
|
fileSummaryLines.push(`### 💡 \`${fileSummary.file}\``);
|
|
@@ -1386,19 +1390,23 @@ class TerminalFormatter {
|
|
|
1386
1390
|
for (const issue of issues){
|
|
1387
1391
|
if (issue.valid === "false") continue;
|
|
1388
1392
|
const stats = issuesByFile.get(issue.file) || {
|
|
1393
|
+
total: 0,
|
|
1389
1394
|
fixed: 0,
|
|
1390
|
-
|
|
1391
|
-
|
|
1395
|
+
errorCount: 0,
|
|
1396
|
+
warnCount: 0,
|
|
1392
1397
|
resolved: 0
|
|
1393
1398
|
};
|
|
1399
|
+
stats.total++;
|
|
1394
1400
|
if (issue.fixed) {
|
|
1395
1401
|
stats.fixed++;
|
|
1396
|
-
}
|
|
1402
|
+
}
|
|
1403
|
+
if (issue.resolved) {
|
|
1397
1404
|
stats.resolved++;
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1405
|
+
}
|
|
1406
|
+
if (issue.severity === "error") {
|
|
1407
|
+
stats.errorCount++;
|
|
1400
1408
|
} else {
|
|
1401
|
-
stats.
|
|
1409
|
+
stats.warnCount++;
|
|
1402
1410
|
}
|
|
1403
1411
|
issuesByFile.set(issue.file, stats);
|
|
1404
1412
|
}
|
|
@@ -1411,21 +1419,21 @@ class TerminalFormatter {
|
|
|
1411
1419
|
const lines = [];
|
|
1412
1420
|
for (const fileSummary of summaries){
|
|
1413
1421
|
const stats = issuesByFile.get(fileSummary.file) || {
|
|
1422
|
+
total: 0,
|
|
1414
1423
|
fixed: 0,
|
|
1415
|
-
|
|
1416
|
-
|
|
1424
|
+
errorCount: 0,
|
|
1425
|
+
warnCount: 0,
|
|
1417
1426
|
resolved: 0
|
|
1418
1427
|
};
|
|
1419
|
-
|
|
1420
|
-
totalAll += fileTotal;
|
|
1428
|
+
totalAll += stats.total;
|
|
1421
1429
|
totalFixed += stats.fixed;
|
|
1422
|
-
totalPendingErrors += stats.
|
|
1423
|
-
totalPendingWarns += stats.
|
|
1430
|
+
totalPendingErrors += stats.errorCount;
|
|
1431
|
+
totalPendingWarns += stats.warnCount;
|
|
1424
1432
|
totalResolved += stats.resolved;
|
|
1425
|
-
const totalText =
|
|
1433
|
+
const totalText = stats.total > 0 ? `${BOLD}${stats.total} 问题${RESET}` : "";
|
|
1426
1434
|
const fixedText = stats.fixed > 0 ? `${GREEN}🟢 ${stats.fixed} 已修复${RESET}` : "";
|
|
1427
|
-
const errorText = stats.
|
|
1428
|
-
const warnText = stats.
|
|
1435
|
+
const errorText = stats.errorCount > 0 ? `${RED}🔴 ${stats.errorCount} error${RESET}` : "";
|
|
1436
|
+
const warnText = stats.warnCount > 0 ? `${YELLOW}🟡 ${stats.warnCount} warn${RESET}` : "";
|
|
1429
1437
|
const resolvedText = stats.resolved > 0 ? `⚪ ${stats.resolved} 已解决` : "";
|
|
1430
1438
|
const statsText = [
|
|
1431
1439
|
totalText,
|
|
@@ -2602,18 +2610,20 @@ class ReviewService {
|
|
|
2602
2610
|
specs = await this.loadSpecs(specSources, verbose);
|
|
2603
2611
|
fileContents = await this.getFileContents(owner, repo, changedFiles, commits, headSha, prNumber, verbose);
|
|
2604
2612
|
}
|
|
2605
|
-
return this.issueVerifyService.verifyIssueFixes(issues, fileContents, specs, llmMode, verbose, context.verifyConcurrency);
|
|
2613
|
+
return await this.issueVerifyService.verifyIssueFixes(issues, fileContents, specs, llmMode, verbose, context.verifyConcurrency);
|
|
2606
2614
|
}
|
|
2607
2615
|
/**
|
|
2608
2616
|
* 计算问题状态统计
|
|
2609
2617
|
*/ calculateIssueStats(issues) {
|
|
2610
2618
|
const total = issues.length;
|
|
2611
|
-
const
|
|
2612
|
-
const
|
|
2613
|
-
const
|
|
2614
|
-
const
|
|
2615
|
-
const
|
|
2616
|
-
const
|
|
2619
|
+
const validIssue = issues.filter((i)=>i.valid !== "false");
|
|
2620
|
+
const validTotal = validIssue.length;
|
|
2621
|
+
const fixed = validIssue.filter((i)=>i.fixed).length;
|
|
2622
|
+
const resolved = validIssue.filter((i)=>i.resolved).length;
|
|
2623
|
+
const invalid = total - validTotal;
|
|
2624
|
+
const pending = validTotal - fixed;
|
|
2625
|
+
const fixRate = validTotal > 0 ? Math.round(fixed / validTotal * 100 * 10) / 10 : 0;
|
|
2626
|
+
const resolveRate = validTotal > 0 ? Math.round(resolved / validTotal * 100 * 10) / 10 : 0;
|
|
2617
2627
|
return {
|
|
2618
2628
|
total,
|
|
2619
2629
|
fixed,
|
|
@@ -4314,6 +4324,7 @@ class IssueVerifyService {
|
|
|
4314
4324
|
}
|
|
4315
4325
|
verifiedIssues.push({
|
|
4316
4326
|
...issue,
|
|
4327
|
+
resolved: new Date().toISOString(),
|
|
4317
4328
|
fixed: new Date().toISOString(),
|
|
4318
4329
|
valid: FALSE,
|
|
4319
4330
|
reason: "文件已删除"
|
|
@@ -4397,6 +4408,7 @@ class IssueVerifyService {
|
|
|
4397
4408
|
console.log(` ✅ 已修复: ${result.reason}`);
|
|
4398
4409
|
}
|
|
4399
4410
|
updatedIssue.fixed = new Date().toISOString();
|
|
4411
|
+
updatedIssue.resolved = new Date().toISOString();
|
|
4400
4412
|
} else if (!result.valid) {
|
|
4401
4413
|
if (shouldLog(verbose, 1)) {
|
|
4402
4414
|
console.log(` ❌ 无效问题: ${result.reason}`);
|
package/package.json
CHANGED
|
@@ -107,6 +107,7 @@ export class IssueVerifyService {
|
|
|
107
107
|
}
|
|
108
108
|
verifiedIssues.push({
|
|
109
109
|
...issue,
|
|
110
|
+
resolved: new Date().toISOString(),
|
|
110
111
|
fixed: new Date().toISOString(),
|
|
111
112
|
valid: FALSE,
|
|
112
113
|
reason: "文件已删除",
|
|
@@ -209,6 +210,7 @@ export class IssueVerifyService {
|
|
|
209
210
|
console.log(` ✅ 已修复: ${result.reason}`);
|
|
210
211
|
}
|
|
211
212
|
updatedIssue.fixed = new Date().toISOString();
|
|
213
|
+
updatedIssue.resolved = new Date().toISOString();
|
|
212
214
|
} else if (!result.valid) {
|
|
213
215
|
if (shouldLog(verbose, 1)) {
|
|
214
216
|
console.log(` ❌ 无效问题: ${result.reason}`);
|
|
@@ -130,27 +130,37 @@ export class MarkdownFormatter implements ReviewReportFormatter, ReviewReportPar
|
|
|
130
130
|
return "没有需要审查的文件";
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
// 🟢 已修复 | 🔴
|
|
133
|
+
// 🟢 已修复 | 🔴 error数量 | 🟡 warn数量 | ⚪ 已解决(非代码修复)
|
|
134
134
|
const issuesByFile = new Map<
|
|
135
135
|
string,
|
|
136
|
-
{
|
|
136
|
+
{
|
|
137
|
+
fixed: number;
|
|
138
|
+
errorCount: number;
|
|
139
|
+
warnCount: number;
|
|
140
|
+
resolved: number;
|
|
141
|
+
total: number;
|
|
142
|
+
}
|
|
137
143
|
>();
|
|
138
144
|
for (const issue of issues) {
|
|
139
145
|
if (issue.valid === "false") continue;
|
|
140
146
|
const stats = issuesByFile.get(issue.file) || {
|
|
147
|
+
total: 0,
|
|
141
148
|
fixed: 0,
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
errorCount: 0,
|
|
150
|
+
warnCount: 0,
|
|
144
151
|
resolved: 0,
|
|
145
152
|
};
|
|
153
|
+
stats.total++;
|
|
146
154
|
if (issue.fixed) {
|
|
147
155
|
stats.fixed++;
|
|
148
|
-
}
|
|
156
|
+
}
|
|
157
|
+
if (issue.resolved) {
|
|
149
158
|
stats.resolved++;
|
|
150
|
-
}
|
|
151
|
-
|
|
159
|
+
}
|
|
160
|
+
if (issue.severity === "error") {
|
|
161
|
+
stats.errorCount++;
|
|
152
162
|
} else {
|
|
153
|
-
stats.
|
|
163
|
+
stats.warnCount++;
|
|
154
164
|
}
|
|
155
165
|
issuesByFile.set(issue.file, stats);
|
|
156
166
|
}
|
|
@@ -169,20 +179,20 @@ export class MarkdownFormatter implements ReviewReportFormatter, ReviewReportPar
|
|
|
169
179
|
const fileSummaryLines: string[] = [];
|
|
170
180
|
for (const fileSummary of summaries) {
|
|
171
181
|
const stats = issuesByFile.get(fileSummary.file) || {
|
|
182
|
+
total: 0,
|
|
172
183
|
fixed: 0,
|
|
173
|
-
|
|
174
|
-
|
|
184
|
+
errorCount: 0,
|
|
185
|
+
warnCount: 0,
|
|
175
186
|
resolved: 0,
|
|
176
187
|
};
|
|
177
|
-
|
|
178
|
-
totalAll += fileTotal;
|
|
188
|
+
totalAll += stats.total;
|
|
179
189
|
totalFixed += stats.fixed;
|
|
180
|
-
totalPendingErrors += stats.
|
|
181
|
-
totalPendingWarns += stats.
|
|
190
|
+
totalPendingErrors += stats.errorCount;
|
|
191
|
+
totalPendingWarns += stats.warnCount;
|
|
182
192
|
totalResolved += stats.resolved;
|
|
183
193
|
|
|
184
194
|
lines.push(
|
|
185
|
-
`| \`${fileSummary.file}\` | ${
|
|
195
|
+
`| \`${fileSummary.file}\` | ${stats.total} | ${stats.fixed} | ${stats.errorCount} | ${stats.warnCount} | ${stats.resolved} |`,
|
|
186
196
|
);
|
|
187
197
|
|
|
188
198
|
// 收集问题总结用于折叠块展示
|
|
@@ -31,24 +31,34 @@ export class TerminalFormatter implements ReviewReportFormatter {
|
|
|
31
31
|
// 🟢 已修复 | 🔴 待处理error | 🟡 待处理warn | ⚪ 已解决(非代码修复)
|
|
32
32
|
const issuesByFile = new Map<
|
|
33
33
|
string,
|
|
34
|
-
{
|
|
34
|
+
{
|
|
35
|
+
fixed: number;
|
|
36
|
+
errorCount: number;
|
|
37
|
+
warnCount: number;
|
|
38
|
+
resolved: number;
|
|
39
|
+
total: number;
|
|
40
|
+
}
|
|
35
41
|
>();
|
|
36
42
|
for (const issue of issues) {
|
|
37
43
|
if (issue.valid === "false") continue;
|
|
38
44
|
const stats = issuesByFile.get(issue.file) || {
|
|
45
|
+
total: 0,
|
|
39
46
|
fixed: 0,
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
errorCount: 0,
|
|
48
|
+
warnCount: 0,
|
|
42
49
|
resolved: 0,
|
|
43
50
|
};
|
|
51
|
+
stats.total++;
|
|
44
52
|
if (issue.fixed) {
|
|
45
53
|
stats.fixed++;
|
|
46
|
-
}
|
|
54
|
+
}
|
|
55
|
+
if (issue.resolved) {
|
|
47
56
|
stats.resolved++;
|
|
48
|
-
}
|
|
49
|
-
|
|
57
|
+
}
|
|
58
|
+
if (issue.severity === "error") {
|
|
59
|
+
stats.errorCount++;
|
|
50
60
|
} else {
|
|
51
|
-
stats.
|
|
61
|
+
stats.warnCount++;
|
|
52
62
|
}
|
|
53
63
|
issuesByFile.set(issue.file, stats);
|
|
54
64
|
}
|
|
@@ -63,24 +73,22 @@ export class TerminalFormatter implements ReviewReportFormatter {
|
|
|
63
73
|
const lines: string[] = [];
|
|
64
74
|
for (const fileSummary of summaries) {
|
|
65
75
|
const stats = issuesByFile.get(fileSummary.file) || {
|
|
76
|
+
total: 0,
|
|
66
77
|
fixed: 0,
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
errorCount: 0,
|
|
79
|
+
warnCount: 0,
|
|
69
80
|
resolved: 0,
|
|
70
81
|
};
|
|
71
|
-
|
|
72
|
-
totalAll += fileTotal;
|
|
82
|
+
totalAll += stats.total;
|
|
73
83
|
totalFixed += stats.fixed;
|
|
74
|
-
totalPendingErrors += stats.
|
|
75
|
-
totalPendingWarns += stats.
|
|
84
|
+
totalPendingErrors += stats.errorCount;
|
|
85
|
+
totalPendingWarns += stats.warnCount;
|
|
76
86
|
totalResolved += stats.resolved;
|
|
77
87
|
|
|
78
|
-
const totalText =
|
|
88
|
+
const totalText = stats.total > 0 ? `${BOLD}${stats.total} 问题${RESET}` : "";
|
|
79
89
|
const fixedText = stats.fixed > 0 ? `${GREEN}🟢 ${stats.fixed} 已修复${RESET}` : "";
|
|
80
|
-
const errorText =
|
|
81
|
-
|
|
82
|
-
const warnText =
|
|
83
|
-
stats.pendingWarns > 0 ? `${YELLOW}🟡 ${stats.pendingWarns} warn${RESET}` : "";
|
|
90
|
+
const errorText = stats.errorCount > 0 ? `${RED}🔴 ${stats.errorCount} error${RESET}` : "";
|
|
91
|
+
const warnText = stats.warnCount > 0 ? `${YELLOW}🟡 ${stats.warnCount} warn${RESET}` : "";
|
|
84
92
|
const resolvedText = stats.resolved > 0 ? `⚪ ${stats.resolved} 已解决` : "";
|
|
85
93
|
const statsText = [totalText, fixedText, errorText, warnText, resolvedText]
|
|
86
94
|
.filter(Boolean)
|
package/src/review-spec/types.ts
CHANGED
|
@@ -125,9 +125,9 @@ export interface ReviewStats {
|
|
|
125
125
|
invalid: number;
|
|
126
126
|
/** 待处理数 */
|
|
127
127
|
pending: number;
|
|
128
|
-
/** 修复率 (0-100),仅计算代码修复:fixed /
|
|
128
|
+
/** 修复率 (0-100),仅计算代码修复:fixed / validTotal */
|
|
129
129
|
fixRate: number;
|
|
130
|
-
/** 解决率 (0-100)
|
|
130
|
+
/** 解决率 (0-100),计算已解决:resolved / validTotal */
|
|
131
131
|
resolveRate: number;
|
|
132
132
|
}
|
|
133
133
|
|
package/src/review.service.ts
CHANGED
|
@@ -1079,7 +1079,7 @@ export class ReviewService {
|
|
|
1079
1079
|
);
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
|
-
return this.issueVerifyService.verifyIssueFixes(
|
|
1082
|
+
return await this.issueVerifyService.verifyIssueFixes(
|
|
1083
1083
|
issues,
|
|
1084
1084
|
fileContents,
|
|
1085
1085
|
specs,
|
|
@@ -1094,12 +1094,14 @@ export class ReviewService {
|
|
|
1094
1094
|
*/
|
|
1095
1095
|
protected calculateIssueStats(issues: ReviewIssue[]): ReviewStats {
|
|
1096
1096
|
const total = issues.length;
|
|
1097
|
-
const
|
|
1098
|
-
const
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1101
|
-
const
|
|
1102
|
-
const
|
|
1097
|
+
const validIssue = issues.filter((i) => i.valid !== "false");
|
|
1098
|
+
const validTotal = validIssue.length;
|
|
1099
|
+
const fixed = validIssue.filter((i) => i.fixed).length;
|
|
1100
|
+
const resolved = validIssue.filter((i) => i.resolved).length;
|
|
1101
|
+
const invalid = total - validTotal;
|
|
1102
|
+
const pending = validTotal - fixed;
|
|
1103
|
+
const fixRate = validTotal > 0 ? Math.round((fixed / validTotal) * 100 * 10) / 10 : 0;
|
|
1104
|
+
const resolveRate = validTotal > 0 ? Math.round((resolved / validTotal) * 100 * 10) / 10 : 0;
|
|
1103
1105
|
return { total, fixed, resolved, invalid, pending, fixRate, resolveRate };
|
|
1104
1106
|
}
|
|
1105
1107
|
|