@spaceflow/review 0.48.0 → 0.49.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.48.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.47.0...@spaceflow/review@0.48.0) (2026-02-27)
4
+
5
+ ### 新特性
6
+
7
+ * **mcp:** 添加 MCP 资源支持,包括扩展资源和内置配置/扩展列表资源 ([ab19889](https://github.com/Lydanne/spaceflow/commit/ab198890a13c998c17987734da3875834f747a70))
8
+
9
+ ### 其他修改
10
+
11
+ * **cli:** released version 0.38.0 [no ci] ([b8c1a54](https://github.com/Lydanne/spaceflow/commit/b8c1a546876b1ad74d5da755c9ecafea9a99798d))
12
+ * **core:** released version 0.16.0 [no ci] ([4486a32](https://github.com/Lydanne/spaceflow/commit/4486a320fccea858e400b79c5b4f18ed4a6f58ea))
13
+ * **publish:** released version 0.40.0 [no ci] ([8fe14c7](https://github.com/Lydanne/spaceflow/commit/8fe14c7faffa2784b91710d3f129911534bf64d2))
14
+ * **review-summary:** released version 0.17.0 [no ci] ([e00f17d](https://github.com/Lydanne/spaceflow/commit/e00f17dc2ade751ff4de7b45b4d9671b25271f7c))
15
+ * **scripts:** released version 0.17.0 [no ci] ([8946ea6](https://github.com/Lydanne/spaceflow/commit/8946ea68e1ea372ae9d1c20cef098e1ef59bdf25))
16
+ * **shell:** released version 0.17.0 [no ci] ([fb4e833](https://github.com/Lydanne/spaceflow/commit/fb4e833b1a469bf2446b25656a3b439584a4639a))
17
+
3
18
  ## [0.47.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.46.0...@spaceflow/review@0.47.0) (2026-02-27)
4
19
 
5
20
  ### 新特性
package/dist/index.js CHANGED
@@ -2427,8 +2427,14 @@ ${fileChanges || "无"}`;
2427
2427
  await this.syncResolvedComments(owner, repo, prNumber, result);
2428
2428
  // 获取评论的 reactions,同步 valid 状态(👎 标记为无效)
2429
2429
  await this.syncReactionsToIssues(owner, repo, prNumber, result, verbose);
2430
- // 查找已有的 AI 评论(Issue Comment
2431
- const existingComment = await this.findExistingAiComment(owner, repo, prNumber);
2430
+ // 查找已有的 AI 评论(Issue Comment),可能存在多个重复评论
2431
+ if (shouldLog(verbose, 2)) {
2432
+ console.log(`[postOrUpdateReviewComment] owner=${owner}, repo=${repo}, prNumber=${prNumber}`);
2433
+ }
2434
+ const existingComments = await this.findExistingAiComments(owner, repo, prNumber, verbose);
2435
+ if (shouldLog(verbose, 2)) {
2436
+ console.log(`[postOrUpdateReviewComment] found ${existingComments.length} existing AI comments`);
2437
+ }
2432
2438
  // 调试:检查 issues 是否有 author
2433
2439
  if (shouldLog(verbose, 3)) {
2434
2440
  for (const issue of result.issues.slice(0, 3)){
@@ -2445,9 +2451,19 @@ ${fileChanges || "无"}`;
2445
2451
  const commitId = pr.head?.sha;
2446
2452
  // 1. 发布或更新主评论(使用 Issue Comment API,支持删除和更新)
2447
2453
  try {
2448
- if (existingComment?.id) {
2449
- await this.gitProvider.updateIssueComment(owner, repo, existingComment.id, reviewBody);
2454
+ if (existingComments.length > 0) {
2455
+ // 更新第一个 AI 评论
2456
+ await this.gitProvider.updateIssueComment(owner, repo, existingComments[0].id, reviewBody);
2450
2457
  console.log(`✅ 已更新 AI Review 评论`);
2458
+ // 删除多余的重复 AI 评论
2459
+ for (const duplicate of existingComments.slice(1)){
2460
+ try {
2461
+ await this.gitProvider.deleteIssueComment(owner, repo, duplicate.id);
2462
+ console.log(`🗑️ 已删除重复的 AI Review 评论 (id: ${duplicate.id})`);
2463
+ } catch {
2464
+ console.warn(`⚠️ 删除重复评论失败 (id: ${duplicate.id})`);
2465
+ }
2466
+ }
2451
2467
  } else {
2452
2468
  await this.gitProvider.createIssueComment(owner, repo, prNumber, {
2453
2469
  body: reviewBody
@@ -2529,16 +2545,25 @@ ${fileChanges || "无"}`;
2529
2545
  }
2530
2546
  }
2531
2547
  /**
2532
- * 查找已有的 AI 评论(Issue Comment)
2533
- */ async findExistingAiComment(owner, repo, prNumber) {
2548
+ * 查找已有的所有 AI 评论(Issue Comment)
2549
+ * 返回所有包含 REVIEW_COMMENT_MARKER 的评论,用于更新第一个并清理重复项
2550
+ */ async findExistingAiComments(owner, repo, prNumber, verbose) {
2534
2551
  try {
2535
2552
  const comments = await this.gitProvider.listIssueComments(owner, repo, prNumber);
2536
- const aiComment = comments.find((c)=>c.body?.includes(REVIEW_COMMENT_MARKER));
2537
- return aiComment?.id ? {
2538
- id: aiComment.id
2539
- } : null;
2540
- } catch {
2541
- return null;
2553
+ if (shouldLog(verbose, 2)) {
2554
+ console.log(`[findExistingAiComments] listIssueComments returned ${Array.isArray(comments) ? comments.length : typeof comments} comments`);
2555
+ if (Array.isArray(comments)) {
2556
+ for (const c of comments.slice(0, 5)){
2557
+ console.log(`[findExistingAiComments] comment id=${c.id}, body starts with: ${c.body?.slice(0, 80) ?? "(no body)"}`);
2558
+ }
2559
+ }
2560
+ }
2561
+ return comments.filter((c)=>c.body?.includes(REVIEW_COMMENT_MARKER) && c.id).map((c)=>({
2562
+ id: c.id
2563
+ }));
2564
+ } catch (error) {
2565
+ console.warn("[findExistingAiComments] error:", error);
2566
+ return [];
2542
2567
  }
2543
2568
  }
2544
2569
  /**
@@ -5212,7 +5237,7 @@ const extension = defineExtension({
5212
5237
  deletionAnalysisMode: options?.deletionAnalysisMode,
5213
5238
  deletionOnly: !!options?.deletionOnly,
5214
5239
  outputFormat: options?.outputFormat,
5215
- generateDescription: !!options?.generateDescription,
5240
+ generateDescription: options?.generateDescription ? true : undefined,
5216
5241
  showAll: !!options?.showAll,
5217
5242
  flush: isFlush,
5218
5243
  eventAction: options?.eventAction
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaceflow/review",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "Spaceflow 代码审查插件,使用 LLM 对 PR 代码进行自动审查",
5
5
  "license": "MIT",
6
6
  "author": "Lydanne",
@@ -28,7 +28,7 @@
28
28
  "@spaceflow/cli": "0.38.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@spaceflow/core": "0.16.0"
31
+ "@spaceflow/core": "0.17.0"
32
32
  },
33
33
  "spaceflow": {
34
34
  "type": "flow",
package/src/index.ts CHANGED
@@ -98,7 +98,7 @@ export const extension = defineExtension({
98
98
  deletionAnalysisMode: options?.deletionAnalysisMode as LLMMode,
99
99
  deletionOnly: !!options?.deletionOnly,
100
100
  outputFormat: options?.outputFormat as ReportFormat,
101
- generateDescription: !!options?.generateDescription,
101
+ generateDescription: options?.generateDescription ? true : undefined,
102
102
  showAll: !!options?.showAll,
103
103
  flush: isFlush,
104
104
  eventAction: options?.eventAction as string,
@@ -1880,8 +1880,16 @@ ${fileChanges || "无"}`;
1880
1880
  // 获取评论的 reactions,同步 valid 状态(👎 标记为无效)
1881
1881
  await this.syncReactionsToIssues(owner, repo, prNumber, result, verbose);
1882
1882
 
1883
- // 查找已有的 AI 评论(Issue Comment
1884
- const existingComment = await this.findExistingAiComment(owner, repo, prNumber);
1883
+ // 查找已有的 AI 评论(Issue Comment),可能存在多个重复评论
1884
+ if (shouldLog(verbose, 2)) {
1885
+ console.log(`[postOrUpdateReviewComment] owner=${owner}, repo=${repo}, prNumber=${prNumber}`);
1886
+ }
1887
+ const existingComments = await this.findExistingAiComments(owner, repo, prNumber, verbose);
1888
+ if (shouldLog(verbose, 2)) {
1889
+ console.log(
1890
+ `[postOrUpdateReviewComment] found ${existingComments.length} existing AI comments`,
1891
+ );
1892
+ }
1885
1893
 
1886
1894
  // 调试:检查 issues 是否有 author
1887
1895
  if (shouldLog(verbose, 3)) {
@@ -1904,9 +1912,19 @@ ${fileChanges || "无"}`;
1904
1912
 
1905
1913
  // 1. 发布或更新主评论(使用 Issue Comment API,支持删除和更新)
1906
1914
  try {
1907
- if (existingComment?.id) {
1908
- await this.gitProvider.updateIssueComment(owner, repo, existingComment.id, reviewBody);
1915
+ if (existingComments.length > 0) {
1916
+ // 更新第一个 AI 评论
1917
+ await this.gitProvider.updateIssueComment(owner, repo, existingComments[0].id, reviewBody);
1909
1918
  console.log(`✅ 已更新 AI Review 评论`);
1919
+ // 删除多余的重复 AI 评论
1920
+ for (const duplicate of existingComments.slice(1)) {
1921
+ try {
1922
+ await this.gitProvider.deleteIssueComment(owner, repo, duplicate.id);
1923
+ console.log(`🗑️ 已删除重复的 AI Review 评论 (id: ${duplicate.id})`);
1924
+ } catch {
1925
+ console.warn(`⚠️ 删除重复评论失败 (id: ${duplicate.id})`);
1926
+ }
1927
+ }
1910
1928
  } else {
1911
1929
  await this.gitProvider.createIssueComment(owner, repo, prNumber, { body: reviewBody });
1912
1930
  console.log(`✅ 已发布 AI Review 评论`);
@@ -1994,19 +2012,35 @@ ${fileChanges || "无"}`;
1994
2012
  }
1995
2013
 
1996
2014
  /**
1997
- * 查找已有的 AI 评论(Issue Comment)
2015
+ * 查找已有的所有 AI 评论(Issue Comment)
2016
+ * 返回所有包含 REVIEW_COMMENT_MARKER 的评论,用于更新第一个并清理重复项
1998
2017
  */
1999
- protected async findExistingAiComment(
2018
+ protected async findExistingAiComments(
2000
2019
  owner: string,
2001
2020
  repo: string,
2002
2021
  prNumber: number,
2003
- ): Promise<{ id: number } | null> {
2022
+ verbose?: VerboseLevel,
2023
+ ): Promise<{ id: number }[]> {
2004
2024
  try {
2005
2025
  const comments = await this.gitProvider.listIssueComments(owner, repo, prNumber);
2006
- const aiComment = comments.find((c) => c.body?.includes(REVIEW_COMMENT_MARKER));
2007
- return aiComment?.id ? { id: aiComment.id } : null;
2008
- } catch {
2009
- return null;
2026
+ if (shouldLog(verbose, 2)) {
2027
+ console.log(
2028
+ `[findExistingAiComments] listIssueComments returned ${Array.isArray(comments) ? comments.length : typeof comments} comments`,
2029
+ );
2030
+ if (Array.isArray(comments)) {
2031
+ for (const c of comments.slice(0, 5)) {
2032
+ console.log(
2033
+ `[findExistingAiComments] comment id=${c.id}, body starts with: ${c.body?.slice(0, 80) ?? "(no body)"}`,
2034
+ );
2035
+ }
2036
+ }
2037
+ }
2038
+ return comments
2039
+ .filter((c) => c.body?.includes(REVIEW_COMMENT_MARKER) && c.id)
2040
+ .map((c) => ({ id: c.id! }));
2041
+ } catch (error) {
2042
+ console.warn("[findExistingAiComments] error:", error);
2043
+ return [];
2010
2044
  }
2011
2045
  }
2012
2046