@spaceflow/review 0.30.0 → 0.32.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 +90 -0
- package/dist/index.js +161 -52
- package/package.json +3 -3
- package/src/index.ts +7 -2
- package/src/locales/en/review.json +1 -0
- package/src/locales/zh-cn/review.json +1 -0
- package/src/review.config.ts +2 -0
- package/src/review.service.spec.ts +123 -63
- package/src/review.service.ts +159 -58
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,95 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.31.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.30.0...@spaceflow/review@0.31.0) (2026-02-25)
|
|
4
|
+
|
|
5
|
+
### 新特性
|
|
6
|
+
|
|
7
|
+
* 支持删除旧行级评论后重新创建,添加 deletePullReviewComment API ([485703c](https://github.com/Lydanne/spaceflow/commit/485703cd778eece364a297f6265a0a81fde5c6dc))
|
|
8
|
+
* 添加 /review --flush 指令,仅刷新状态不执行 LLM 审查 ([3546748](https://github.com/Lydanne/spaceflow/commit/354674857ef062a855e64529a6deab461dd8d5c6))
|
|
9
|
+
|
|
10
|
+
### 修复BUG
|
|
11
|
+
|
|
12
|
+
* Gitea 适配器 updatePullReview 使用删除+创建方式实现 ([8193668](https://github.com/Lydanne/spaceflow/commit/81936684e552d057f74b69505f93e7f86209d79a))
|
|
13
|
+
* GitHub 行级评论 reactions 使用正确的 pulls/comments API 路径 ([a028bc4](https://github.com/Lydanne/spaceflow/commit/a028bc42a1fb8a1445e0535d2da22da2ca487a8a))
|
|
14
|
+
* GitHub 行级评论使用 line+side 替代废弃的 position,批量失败时逐条发布 ([01b4873](https://github.com/Lydanne/spaceflow/commit/01b4873fe165d5c9134e0579e25a995f5eafefa4))
|
|
15
|
+
* GitHub 适配器 updatePullReview 也使用删除+创建方式,因为已提交的 review 无法更新 ([25b74ce](https://github.com/Lydanne/spaceflow/commit/25b74ce16cb34fa6446529c224288da96cb51466))
|
|
16
|
+
* GitHub 通过 GraphQL 查询 review thread resolved 状态,修复 Resolve conversation 不生效问题 ([28a4e0b](https://github.com/Lydanne/spaceflow/commit/28a4e0b0da2a1e4f233a194be34aeafb32ef1e1d))
|
|
17
|
+
* 使用 updatePullReview 替代删除+创建,避免 GitHub 重复审查报告 ([a0dc74d](https://github.com/Lydanne/spaceflow/commit/a0dc74ddc49bd4130152931bb4f95d274949afb1))
|
|
18
|
+
* 修复重复审查报告问题,支持删除已提交的 AI 评论 ([e531113](https://github.com/Lydanne/spaceflow/commit/e531113d145a51cd504ca11dd837c0d04cbaf5b6))
|
|
19
|
+
* 修复问题去重逻辑,所有历史问题(含无效和已解决)都阻止重复报告 ([0e5eadd](https://github.com/Lydanne/spaceflow/commit/0e5eadd2ab4122f433b881489889b1a154aeb676))
|
|
20
|
+
* 避免重复创建行级评论,已存在时跳过(GitHub 已提交的 review 无法删除) ([30985b7](https://github.com/Lydanne/spaceflow/commit/30985b7323b2254ca4feaa5ba39cd28ca0adaed3))
|
|
21
|
+
* 重写 syncResolvedComments,通过 GraphQL 直接查询所有 resolved threads 的 path+line 匹配 issues ([5429cbc](https://github.com/Lydanne/spaceflow/commit/5429cbc4c7ae5df94c9c6c733dc72443fddf474f))
|
|
22
|
+
|
|
23
|
+
### 代码重构
|
|
24
|
+
|
|
25
|
+
* 主评论改用 Issue Comment API,行级评论仍用 PR Review API,彻底解决 GitHub 重复审查报告问题 ([3ffb9d4](https://github.com/Lydanne/spaceflow/commit/3ffb9d47d05e74722a9a964281588e0cc26d212e))
|
|
26
|
+
* 优化 PR 审查工作流触发条件和并发控制 ([74b84d7](https://github.com/Lydanne/spaceflow/commit/74b84d7f5078b631d33c1f6759130df3d0401e51))
|
|
27
|
+
* 分离 PR 审查评论为主评论和行级评论两部分 ([dda2ef6](https://github.com/Lydanne/spaceflow/commit/dda2ef6491a8cbce890dc68eba6f5660085428b5))
|
|
28
|
+
* 提取 REVIEW_STATE 和 DIFF_SIDE 常量,消除魔法字符串 ([264132e](https://github.com/Lydanne/spaceflow/commit/264132e4ca68ef44e9e3a477eab0e9af080f5902))
|
|
29
|
+
* 移除 PR 审查工作流中的 workflows 写权限 ([63357e2](https://github.com/Lydanne/spaceflow/commit/63357e22146c803c2271f729214d54af8f34aa86))
|
|
30
|
+
* 简化 PR 审查工作流配置 ([b9b9a47](https://github.com/Lydanne/spaceflow/commit/b9b9a47a660904fc683d827c0a65020170ae323b))
|
|
31
|
+
* 重命名扩展包,统一命名规范 ([13bfefe](https://github.com/Lydanne/spaceflow/commit/13bfefe94a4a63389a17e0faefd9533bcbda8198))
|
|
32
|
+
|
|
33
|
+
### 其他修改
|
|
34
|
+
|
|
35
|
+
* **cli:** released version 0.21.0 [no ci] ([6f32080](https://github.com/Lydanne/spaceflow/commit/6f32080459bb3bcef895f3e51ee5341c2a4ddc74))
|
|
36
|
+
* **core:** released version 0.3.0 [no ci] ([7a66bea](https://github.com/Lydanne/spaceflow/commit/7a66beac3702107884f638a1f3fd54c5c10be568))
|
|
37
|
+
* **publish:** released version 0.23.0 [no ci] ([1a6510f](https://github.com/Lydanne/spaceflow/commit/1a6510f997718468efbbac377ac9d44f07e8e927))
|
|
38
|
+
|
|
39
|
+
## [0.30.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.29.3...@spaceflow/review@0.30.0) (2026-02-16)
|
|
40
|
+
|
|
41
|
+
### 新特性
|
|
42
|
+
|
|
43
|
+
* **ci-scripts:** 迁移到新架构格式 ([5df3630](https://github.com/Lydanne/spaceflow/commit/5df3630208894e6543d34f9985fcf0f612a78d7e))
|
|
44
|
+
* **ci-shell:** 迁移到新架构格式 ([b7d92fd](https://github.com/Lydanne/spaceflow/commit/b7d92fd5f83e9a5e8edeb0ce4971b19190078a7f))
|
|
45
|
+
* **cli:** 修复 commander.js 集成问题 ([8e6aeca](https://github.com/Lydanne/spaceflow/commit/8e6aecadb06e6168050371ec5da3b0a3c40d51c2))
|
|
46
|
+
* **cli:** 阶段3 - 创建新 CLI 架构基础 ([deeaf0d](https://github.com/Lydanne/spaceflow/commit/deeaf0dfeedb3e8c9a8f30fcd05c37e9db18cb55))
|
|
47
|
+
* **core:** 阶段2 - 去除核心服务的 NestJS 装饰器 ([684ec07](https://github.com/Lydanne/spaceflow/commit/684ec0723a90383a6c5fb84fc9198d8b908a89d8))
|
|
48
|
+
* **period-summary:** 迁移到新架构格式 ([b6c7a92](https://github.com/Lydanne/spaceflow/commit/b6c7a927963c84349637884e8fab37d550daa91f))
|
|
49
|
+
* **publish:** 迁移到新架构格式 ([5df49a8](https://github.com/Lydanne/spaceflow/commit/5df49a8b3df6906e7ff65fd2a3bae4ee5ef31bc5))
|
|
50
|
+
* **review:** 迁移到新架构格式 ([6bda477](https://github.com/Lydanne/spaceflow/commit/6bda4773eed9cdbc82593ee29888d1d2cce6efef))
|
|
51
|
+
|
|
52
|
+
### 修复BUG
|
|
53
|
+
|
|
54
|
+
* 修复 ReviewSpecService 构造函数调用,传入 gitProvider 参数 ([747b87e](https://github.com/Lydanne/spaceflow/commit/747b87e7e4621b8976c6444e0350e71600c20801))
|
|
55
|
+
* 修复 StorageServiceOptions 类型错误 ([0bd2820](https://github.com/Lydanne/spaceflow/commit/0bd2820af594f2f30473e57ba4843197e3e7a2ff))
|
|
56
|
+
* 修复 TypeScript 编译配置 ([8c3a4f1](https://github.com/Lydanne/spaceflow/commit/8c3a4f148ccbd5598fb553608f4832ec6a4dc40b))
|
|
57
|
+
* 修复扩展 tsconfig.json 中的路径错误 ([ff42b24](https://github.com/Lydanne/spaceflow/commit/ff42b244645128b1c31714e6e6cf6c71fa4bbc80))
|
|
58
|
+
* 清理 list.service.ts 中的 NestJS 依赖 ([f483bc1](https://github.com/Lydanne/spaceflow/commit/f483bc1edcaf472acf2bdc38902d992840d004c8))
|
|
59
|
+
* 清理命令服务中的 NestJS 依赖 ([a5ca870](https://github.com/Lydanne/spaceflow/commit/a5ca870782eaf4117ecc4f58a689e001df34ce5e))
|
|
60
|
+
* 清理命令服务中的 NestJS 依赖 ([2bebe2a](https://github.com/Lydanne/spaceflow/commit/2bebe2ab23367d661706368c37c01bc6360e5e36))
|
|
61
|
+
|
|
62
|
+
### 代码重构
|
|
63
|
+
|
|
64
|
+
* 使用 ciConfig 函数替代配置读取,简化 CI 配置获取 ([9356752](https://github.com/Lydanne/spaceflow/commit/93567520181a95398f36413a68e8334342d01e51))
|
|
65
|
+
* 删除不再使用的 cli.module.ts ([5b86cb2](https://github.com/Lydanne/spaceflow/commit/5b86cb21d0b55aa4ed8934a51c7bba88f3892ece))
|
|
66
|
+
* 删除所有不再使用的 .module.ts 文件 ([7049a59](https://github.com/Lydanne/spaceflow/commit/7049a59fca05906e163a58a2bc1ad64b51dbccf1))
|
|
67
|
+
* 增强 MCP 工具收集的日志输出 ([e889c2b](https://github.com/Lydanne/spaceflow/commit/e889c2bad0c2dba833905299af2cb4a76126ed0e))
|
|
68
|
+
* 实现 MCP 工具收集和 Inspector 模式支持 ([0702d83](https://github.com/Lydanne/spaceflow/commit/0702d83791e9836ed54576b6d3d373a0fc5085b4))
|
|
69
|
+
* 支持 verbose 计数选项,实现 -vvv 累加级别 ([42ab32d](https://github.com/Lydanne/spaceflow/commit/42ab32dbbf42cff1774d27a1ba2fedab02f33038))
|
|
70
|
+
* 清理 NestJS 依赖和模块文件 ([3efe7d7](https://github.com/Lydanne/spaceflow/commit/3efe7d7555746f93737d91508b06a0061ba2295f))
|
|
71
|
+
* 清理剩余的 NestJS 依赖 ([a3104b3](https://github.com/Lydanne/spaceflow/commit/a3104b36cecb0c93bc839472dba63b88e71bb662))
|
|
72
|
+
* 移除 dtoToJsonSchema 中的 class-validator 元数据推断逻辑 ([0dd1c8e](https://github.com/Lydanne/spaceflow/commit/0dd1c8e1424e1de57ca40b8e317f3bad00d08e7d))
|
|
73
|
+
* 移除 MonorepoService 中的 NestJS 依赖 ([c6a3243](https://github.com/Lydanne/spaceflow/commit/c6a32439da97efb287239e16723f4cb982bdf208))
|
|
74
|
+
* 移除测试文件中的 NestJS 依赖,改用直接实例化 ([325f2e3](https://github.com/Lydanne/spaceflow/commit/325f2e36b434ebb4e471c17b5e2ef2f9a4db2456))
|
|
75
|
+
* 简化 list 命令,只显示外部扩展 ([babcb24](https://github.com/Lydanne/spaceflow/commit/babcb245f527c427103726e290ff889444c1e8da))
|
|
76
|
+
* 统一配置管理,支持环境变量自动合并 ([f6b09a3](https://github.com/Lydanne/spaceflow/commit/f6b09a35c273f1f96f5a07546f2bf58ceb6942f6))
|
|
77
|
+
* 迁移 ci-scripts、ci-shell、publish、review 扩展到新架构 ([4630116](https://github.com/Lydanne/spaceflow/commit/4630116c96699a6a3c36aa439badaa3567cc4c06))
|
|
78
|
+
* 重构服务容器为懒加载依赖注入架构 ([c74a346](https://github.com/Lydanne/spaceflow/commit/c74a346ef27540836b06a8fd825283155722d4af))
|
|
79
|
+
|
|
80
|
+
### 文档更新
|
|
81
|
+
|
|
82
|
+
* **architecture:** 添加 Spaceflow 架构重设计方案 v2 ([21b252c](https://github.com/Lydanne/spaceflow/commit/21b252c97aaf4561cc5837b68229356e5ffa8d36))
|
|
83
|
+
|
|
84
|
+
### 其他修改
|
|
85
|
+
|
|
86
|
+
* **ci-scripts:** released version 0.20.0 [no ci] ([ed8d88d](https://github.com/Lydanne/spaceflow/commit/ed8d88df09c7d119df092793e4c83d451d67a6b8))
|
|
87
|
+
* **ci-shell:** released version 0.20.0 [no ci] ([5109d94](https://github.com/Lydanne/spaceflow/commit/5109d944bfbd95596e71d6e11e56d3e3599f8297))
|
|
88
|
+
* **cli:** released version 0.20.0 [no ci] ([7cb015c](https://github.com/Lydanne/spaceflow/commit/7cb015c9fba3b9b4a8a170f66597505300e35e10))
|
|
89
|
+
* **core:** released version 0.2.0 [no ci] ([6176e7e](https://github.com/Lydanne/spaceflow/commit/6176e7e5755dd594dee7d4e0016dfb89b391d824))
|
|
90
|
+
* **period-summary:** released version 0.20.0 [no ci] ([54feb4a](https://github.com/Lydanne/spaceflow/commit/54feb4adaf0d72d402287bef84fd9433db673ed6))
|
|
91
|
+
* **publish:** released version 0.22.0 [no ci] ([2e39f34](https://github.com/Lydanne/spaceflow/commit/2e39f347c514490be5da690c896849fc6dbfd513))
|
|
92
|
+
|
|
3
93
|
## [0.29.3](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.29.2...@spaceflow/review@0.29.3) (2026-02-16)
|
|
4
94
|
|
|
5
95
|
### 修复BUG
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LlmJsonPut, addLocaleResources, calculateNewLineNumber, createStreamLoggerState, defineExtension, logStreamEvent, normalizeVerbose, parallel, parseChangedLinesFromPatch, parseDiffText, parseHunksFromPatch, parseRepoUrl, parseVerbose, shouldLog, t, z } from "@spaceflow/core";
|
|
1
|
+
import { LlmJsonPut, REVIEW_STATE, addLocaleResources, calculateNewLineNumber, createStreamLoggerState, defineExtension, logStreamEvent, normalizeVerbose, parallel, parseChangedLinesFromPatch, parseDiffText, parseHunksFromPatch, parseRepoUrl, parseVerbose, shouldLog, t, z } from "@spaceflow/core";
|
|
2
2
|
import { basename, dirname, extname, isAbsolute, join, relative } from "path";
|
|
3
3
|
import { execSync, spawn } from "child_process";
|
|
4
4
|
import { access, mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
@@ -143,9 +143,9 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
143
143
|
;// CONCATENATED MODULE: external "@spaceflow/core"
|
|
144
144
|
|
|
145
145
|
;// CONCATENATED MODULE: ./src/locales/zh-cn/review.json
|
|
146
|
-
var review_namespaceObject = JSON.parse('{"description":"代码审查命令,使用 LLM 对 PR 代码进行自动审查","options.dryRun":"仅打印将要执行的操作,不实际提交评论","options.prNumber":"PR 编号,如果不指定则从环境变量获取","options.base":"基准分支/tag,用于比较差异","options.head":"目标分支/tag,用于比较差异","options.includes":"要审查的文件 glob 模式,如 *.ts *.js(可多次指定)","options.llmMode":"使用的 LLM 模式: claude-code, openai, gemini","options.files":"仅审查指定的文件(空格分隔)","options.commits":"仅审查指定的 commits(空格分隔)","options.verifyFixes":"是否验证历史问题是否已修复(默认从配置文件读取)","options.noVerifyFixes":"禁用历史问题验证","options.verifyConcurrency":"验证历史问题的并发数(默认 10)","options.analyzeDeletions":"分析删除代码可能带来的影响 (true, false, ci, pr, terminal)","options.deletionAnalysisMode":"删除代码分析模式: openai (标准模式) 或 claude-code (Agent 模式,可使用工具)","options.deletionOnly":"仅执行删除代码分析,跳过常规代码审查","options.outputFormat":"输出格式: markdown, terminal, json。不指定则智能选择(PR 用 markdown,终端用 terminal)","options.generateDescription":"使用 AI 生成 PR 功能描述","options.showAll":"显示所有发现的问题,不过滤非变更行的问题","options.eventAction":"PR 事件类型(opened, synchronize, closed 等),closed 时仅收集统计不进行 AI 审查","extensionDescription":"代码审查命令,使用 LLM 对 PR 代码进行自动审查","mcp.serverDescription":"代码审查规则查询服务","mcp.listRules":"获取当前项目的所有代码审查规则,返回规则列表包含 ID、标题、描述、适用的文件扩展名等信息","mcp.getRulesForFile":"获取某个文件应该使用的代码审查规则,根据文件扩展名和 includes 配置过滤","mcp.getRuleDetail":"获取某个规则的完整详情,包括描述、示例代码等","mcp.ruleNotFound":"规则 {{ruleId}} 不存在","mcp.dto.cwd":"项目根目录路径,默认为当前工作目录","mcp.dto.filePath":"文件路径,可以是相对路径或绝对路径","mcp.dto.includeExamples":"是否包含规则示例代码,默认 false","mcp.dto.ruleId":"规则 ID,如 JsTs.Naming.FileName"}')
|
|
146
|
+
var review_namespaceObject = JSON.parse('{"description":"代码审查命令,使用 LLM 对 PR 代码进行自动审查","options.dryRun":"仅打印将要执行的操作,不实际提交评论","options.prNumber":"PR 编号,如果不指定则从环境变量获取","options.base":"基准分支/tag,用于比较差异","options.head":"目标分支/tag,用于比较差异","options.includes":"要审查的文件 glob 模式,如 *.ts *.js(可多次指定)","options.llmMode":"使用的 LLM 模式: claude-code, openai, gemini","options.files":"仅审查指定的文件(空格分隔)","options.commits":"仅审查指定的 commits(空格分隔)","options.verifyFixes":"是否验证历史问题是否已修复(默认从配置文件读取)","options.noVerifyFixes":"禁用历史问题验证","options.verifyConcurrency":"验证历史问题的并发数(默认 10)","options.analyzeDeletions":"分析删除代码可能带来的影响 (true, false, ci, pr, terminal)","options.deletionAnalysisMode":"删除代码分析模式: openai (标准模式) 或 claude-code (Agent 模式,可使用工具)","options.deletionOnly":"仅执行删除代码分析,跳过常规代码审查","options.outputFormat":"输出格式: markdown, terminal, json。不指定则智能选择(PR 用 markdown,终端用 terminal)","options.generateDescription":"使用 AI 生成 PR 功能描述","options.showAll":"显示所有发现的问题,不过滤非变更行的问题","options.flush":"仅刷新状态(同步 reactions、resolved conversations、replies),不执行 LLM 审查","options.eventAction":"PR 事件类型(opened, synchronize, closed 等),closed 时仅收集统计不进行 AI 审查","extensionDescription":"代码审查命令,使用 LLM 对 PR 代码进行自动审查","mcp.serverDescription":"代码审查规则查询服务","mcp.listRules":"获取当前项目的所有代码审查规则,返回规则列表包含 ID、标题、描述、适用的文件扩展名等信息","mcp.getRulesForFile":"获取某个文件应该使用的代码审查规则,根据文件扩展名和 includes 配置过滤","mcp.getRuleDetail":"获取某个规则的完整详情,包括描述、示例代码等","mcp.ruleNotFound":"规则 {{ruleId}} 不存在","mcp.dto.cwd":"项目根目录路径,默认为当前工作目录","mcp.dto.filePath":"文件路径,可以是相对路径或绝对路径","mcp.dto.includeExamples":"是否包含规则示例代码,默认 false","mcp.dto.ruleId":"规则 ID,如 JsTs.Naming.FileName"}')
|
|
147
147
|
;// CONCATENATED MODULE: ./src/locales/en/review.json
|
|
148
|
-
var en_review_namespaceObject = JSON.parse('{"description":"Code review command using LLM for automated PR review","options.dryRun":"Only print actions without posting comments","options.prNumber":"PR number, auto-detected from env if not specified","options.base":"Base branch/tag for diff comparison","options.head":"Head branch/tag for diff comparison","options.includes":"File glob patterns to review, e.g. *.ts *.js (can be specified multiple times)","options.llmMode":"LLM mode: claude-code, openai, gemini","options.files":"Only review specified files (space-separated)","options.commits":"Only review specified commits (space-separated)","options.verifyFixes":"Verify if historical issues are fixed (default from config)","options.noVerifyFixes":"Disable historical issue verification","options.verifyConcurrency":"Concurrency for verifying historical issues (default 10)","options.analyzeDeletions":"Analyze impact of deleted code (true, false, ci, pr, terminal)","options.deletionAnalysisMode":"Deletion analysis mode: openai (standard) or claude-code (Agent mode with tools)","options.deletionOnly":"Only run deletion analysis, skip regular code review","options.outputFormat":"Output format: markdown, terminal, json. Auto-selected if not specified (markdown for PR, terminal for CLI)","options.generateDescription":"Generate PR description using AI","options.showAll":"Show all issues found, including those on unchanged lines","options.eventAction":"PR event type (opened, synchronize, closed, etc.), closed only collects stats without AI review","extensionDescription":"Code review command using LLM for automated PR review","mcp.serverDescription":"Code review rules query service","mcp.listRules":"List all code review rules for the current project, returning rule list with ID, title, description, applicable file extensions, etc.","mcp.getRulesForFile":"Get applicable code review rules for a specific file, filtered by file extension and includes configuration","mcp.getRuleDetail":"Get full details of a specific rule, including description, example code, etc.","mcp.ruleNotFound":"Rule {{ruleId}} not found","mcp.dto.cwd":"Project root directory path, defaults to current working directory","mcp.dto.filePath":"File path, can be relative or absolute","mcp.dto.includeExamples":"Whether to include rule example code, defaults to false","mcp.dto.ruleId":"Rule ID, e.g. JsTs.Naming.FileName"}')
|
|
148
|
+
var en_review_namespaceObject = JSON.parse('{"description":"Code review command using LLM for automated PR review","options.dryRun":"Only print actions without posting comments","options.prNumber":"PR number, auto-detected from env if not specified","options.base":"Base branch/tag for diff comparison","options.head":"Head branch/tag for diff comparison","options.includes":"File glob patterns to review, e.g. *.ts *.js (can be specified multiple times)","options.llmMode":"LLM mode: claude-code, openai, gemini","options.files":"Only review specified files (space-separated)","options.commits":"Only review specified commits (space-separated)","options.verifyFixes":"Verify if historical issues are fixed (default from config)","options.noVerifyFixes":"Disable historical issue verification","options.verifyConcurrency":"Concurrency for verifying historical issues (default 10)","options.analyzeDeletions":"Analyze impact of deleted code (true, false, ci, pr, terminal)","options.deletionAnalysisMode":"Deletion analysis mode: openai (standard) or claude-code (Agent mode with tools)","options.deletionOnly":"Only run deletion analysis, skip regular code review","options.outputFormat":"Output format: markdown, terminal, json. Auto-selected if not specified (markdown for PR, terminal for CLI)","options.generateDescription":"Generate PR description using AI","options.showAll":"Show all issues found, including those on unchanged lines","options.flush":"Only sync status (reactions, resolved conversations, replies), skip LLM review","options.eventAction":"PR event type (opened, synchronize, closed, etc.), closed only collects stats without AI review","extensionDescription":"Code review command using LLM for automated PR review","mcp.serverDescription":"Code review rules query service","mcp.listRules":"List all code review rules for the current project, returning rule list with ID, title, description, applicable file extensions, etc.","mcp.getRulesForFile":"Get applicable code review rules for a specific file, filtered by file extension and includes configuration","mcp.getRuleDetail":"Get full details of a specific rule, including description, example code, etc.","mcp.ruleNotFound":"Rule {{ruleId}} not found","mcp.dto.cwd":"Project root directory path, defaults to current working directory","mcp.dto.filePath":"File path, can be relative or absolute","mcp.dto.includeExamples":"Whether to include rule example code, defaults to false","mcp.dto.ruleId":"Rule ID, e.g. JsTs.Naming.FileName"}')
|
|
149
149
|
;// CONCATENATED MODULE: ./src/locales/index.ts
|
|
150
150
|
|
|
151
151
|
|
|
@@ -939,6 +939,7 @@ function parseAnalyzeDeletionsValue(value) {
|
|
|
939
939
|
|
|
940
940
|
|
|
941
941
|
const REVIEW_COMMENT_MARKER = "<!-- spaceflow-review -->";
|
|
942
|
+
const REVIEW_LINE_COMMENTS_MARKER = "<!-- spaceflow-review-lines -->";
|
|
942
943
|
const REVIEW_SCHEMA = {
|
|
943
944
|
type: "object",
|
|
944
945
|
properties: {
|
|
@@ -1147,6 +1148,7 @@ class ReviewService {
|
|
|
1147
1148
|
retryDelay: options.retryDelay ?? reviewConf.retryDelay ?? 1000,
|
|
1148
1149
|
generateDescription: options.generateDescription ?? reviewConf.generateDescription ?? false,
|
|
1149
1150
|
showAll: options.showAll ?? false,
|
|
1151
|
+
flush: options.flush ?? false,
|
|
1150
1152
|
eventAction: options.eventAction
|
|
1151
1153
|
};
|
|
1152
1154
|
}
|
|
@@ -1229,8 +1231,8 @@ class ReviewService {
|
|
|
1229
1231
|
if (deletionOnly) {
|
|
1230
1232
|
return this.executeDeletionOnly(context);
|
|
1231
1233
|
}
|
|
1232
|
-
// 如果是 closed
|
|
1233
|
-
if (context.eventAction === "closed") {
|
|
1234
|
+
// 如果是 closed 事件或 flush 模式,仅收集 review 状态
|
|
1235
|
+
if (context.eventAction === "closed" || context.flush) {
|
|
1234
1236
|
return this.executeCollectOnly(context);
|
|
1235
1237
|
}
|
|
1236
1238
|
if (shouldLog(verbose, 1)) {
|
|
@@ -1556,7 +1558,7 @@ class ReviewService {
|
|
|
1556
1558
|
return result;
|
|
1557
1559
|
}
|
|
1558
1560
|
/**
|
|
1559
|
-
* 仅收集 review 状态模式(用于 PR
|
|
1561
|
+
* 仅收集 review 状态模式(用于 PR 关闭或 --flush 指令)
|
|
1560
1562
|
* 从现有的 AI review 评论中读取问题状态,同步已解决/无效状态,输出统计信息
|
|
1561
1563
|
*/ async executeCollectOnly(context) {
|
|
1562
1564
|
const { owner, repo, prNumber, verbose, ci, dryRun } = context;
|
|
@@ -2423,12 +2425,12 @@ ${fileChanges || "无"}`;
|
|
|
2423
2425
|
console.warn("⚠️ 更新 PR 标题失败:", error);
|
|
2424
2426
|
}
|
|
2425
2427
|
}
|
|
2426
|
-
// 获取已解决的评论,同步 fixed
|
|
2428
|
+
// 获取已解决的评论,同步 fixed 状态(在更新 review 之前)
|
|
2427
2429
|
await this.syncResolvedComments(owner, repo, prNumber, result);
|
|
2428
2430
|
// 获取评论的 reactions,同步 valid 状态(👎 标记为无效)
|
|
2429
2431
|
await this.syncReactionsToIssues(owner, repo, prNumber, result, verbose);
|
|
2430
|
-
//
|
|
2431
|
-
await this.
|
|
2432
|
+
// 查找已有的 AI 评论(Issue Comment)
|
|
2433
|
+
const existingComment = await this.findExistingAiComment(owner, repo, prNumber);
|
|
2432
2434
|
// 调试:检查 issues 是否有 author
|
|
2433
2435
|
if (shouldLog(verbose, 3)) {
|
|
2434
2436
|
for (const issue of result.issues.slice(0, 3)){
|
|
@@ -2443,45 +2445,117 @@ ${fileChanges || "无"}`;
|
|
|
2443
2445
|
// 获取 PR 信息以获取 head commit SHA
|
|
2444
2446
|
const pr = await this.gitProvider.getPullRequest(owner, repo, prNumber);
|
|
2445
2447
|
const commitId = pr.head?.sha;
|
|
2446
|
-
//
|
|
2448
|
+
// 1. 发布或更新主评论(使用 Issue Comment API,支持删除和更新)
|
|
2449
|
+
try {
|
|
2450
|
+
if (existingComment?.id) {
|
|
2451
|
+
await this.gitProvider.updateIssueComment(owner, repo, existingComment.id, reviewBody);
|
|
2452
|
+
console.log(`✅ 已更新 AI Review 评论`);
|
|
2453
|
+
} else {
|
|
2454
|
+
await this.gitProvider.createIssueComment(owner, repo, prNumber, {
|
|
2455
|
+
body: reviewBody
|
|
2456
|
+
});
|
|
2457
|
+
console.log(`✅ 已发布 AI Review 评论`);
|
|
2458
|
+
}
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
console.warn("⚠️ 发布/更新 AI Review 评论失败:", error);
|
|
2461
|
+
}
|
|
2462
|
+
// 2. 删除旧的行级评论(逐条删除 PR Review Comment)
|
|
2463
|
+
try {
|
|
2464
|
+
const reviews = await this.gitProvider.listPullReviews(owner, repo, prNumber);
|
|
2465
|
+
const oldLineReviews = reviews.filter((r)=>r.body?.includes(REVIEW_LINE_COMMENTS_MARKER));
|
|
2466
|
+
for (const review of oldLineReviews){
|
|
2467
|
+
if (review.id) {
|
|
2468
|
+
const reviewComments = await this.gitProvider.listPullReviewComments(owner, repo, prNumber, review.id);
|
|
2469
|
+
for (const comment of reviewComments){
|
|
2470
|
+
if (comment.id) {
|
|
2471
|
+
try {
|
|
2472
|
+
await this.gitProvider.deletePullReviewComment(owner, repo, comment.id);
|
|
2473
|
+
} catch {
|
|
2474
|
+
// 删除失败忽略
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
// 评论删除后尝试删除 review 本身
|
|
2479
|
+
try {
|
|
2480
|
+
await this.gitProvider.deletePullReview(owner, repo, prNumber, review.id);
|
|
2481
|
+
} catch {
|
|
2482
|
+
// 已提交的 review 无法删除,忽略
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
if (oldLineReviews.length > 0) {
|
|
2487
|
+
console.log(`🗑️ 已清理 ${oldLineReviews.length} 个旧的行级评论 review`);
|
|
2488
|
+
}
|
|
2489
|
+
} catch (error) {
|
|
2490
|
+
console.warn("⚠️ 清理旧行级评论失败:", error);
|
|
2491
|
+
}
|
|
2492
|
+
// 3. 发布新的行级评论(使用 PR Review API)
|
|
2447
2493
|
let comments = [];
|
|
2448
2494
|
if (reviewConf.lineComments) {
|
|
2449
2495
|
comments = result.issues.filter((issue)=>!issue.fixed && issue.valid !== "false").map((issue)=>this.issueToReviewComment(issue)).filter((comment)=>comment !== null);
|
|
2450
2496
|
}
|
|
2497
|
+
if (comments.length > 0) {
|
|
2498
|
+
try {
|
|
2499
|
+
await this.gitProvider.createPullReview(owner, repo, prNumber, {
|
|
2500
|
+
event: REVIEW_STATE.COMMENT,
|
|
2501
|
+
body: REVIEW_LINE_COMMENTS_MARKER,
|
|
2502
|
+
comments,
|
|
2503
|
+
commit_id: commitId
|
|
2504
|
+
});
|
|
2505
|
+
console.log(`✅ 已发布 ${comments.length} 条行级评论`);
|
|
2506
|
+
} catch {
|
|
2507
|
+
// 批量失败时逐条发布,跳过无法定位的评论
|
|
2508
|
+
console.warn("⚠️ 批量发布行级评论失败,尝试逐条发布...");
|
|
2509
|
+
let successCount = 0;
|
|
2510
|
+
for (const comment of comments){
|
|
2511
|
+
try {
|
|
2512
|
+
await this.gitProvider.createPullReview(owner, repo, prNumber, {
|
|
2513
|
+
event: REVIEW_STATE.COMMENT,
|
|
2514
|
+
body: successCount === 0 ? REVIEW_LINE_COMMENTS_MARKER : undefined,
|
|
2515
|
+
comments: [
|
|
2516
|
+
comment
|
|
2517
|
+
],
|
|
2518
|
+
commit_id: commitId
|
|
2519
|
+
});
|
|
2520
|
+
successCount++;
|
|
2521
|
+
} catch {
|
|
2522
|
+
console.warn(`⚠️ 跳过无法定位的评论: ${comment.path}:${comment.new_position}`);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
if (successCount > 0) {
|
|
2526
|
+
console.log(`✅ 逐条发布成功 ${successCount}/${comments.length} 条行级评论`);
|
|
2527
|
+
} else {
|
|
2528
|
+
console.warn("⚠️ 所有行级评论均无法定位,已跳过");
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
/**
|
|
2534
|
+
* 查找已有的 AI 评论(Issue Comment)
|
|
2535
|
+
*/ async findExistingAiComment(owner, repo, prNumber) {
|
|
2451
2536
|
try {
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
const lineMsg = comments.length > 0 ? `,包含 ${comments.length} 条行级评论` : "";
|
|
2460
|
-
console.log(`✅ 已发布 AI Review${lineMsg}`);
|
|
2461
|
-
} catch (error) {
|
|
2462
|
-
console.warn("⚠️ 发布 AI Review 失败:", error);
|
|
2537
|
+
const comments = await this.gitProvider.listIssueComments(owner, repo, prNumber);
|
|
2538
|
+
const aiComment = comments.find((c)=>c.body?.includes(REVIEW_COMMENT_MARKER));
|
|
2539
|
+
return aiComment?.id ? {
|
|
2540
|
+
id: aiComment.id
|
|
2541
|
+
} : null;
|
|
2542
|
+
} catch {
|
|
2543
|
+
return null;
|
|
2463
2544
|
}
|
|
2464
2545
|
}
|
|
2465
2546
|
/**
|
|
2466
|
-
*
|
|
2547
|
+
* 从 PR 的所有 resolved review threads 中同步 fixed 状态到 result.issues
|
|
2548
|
+
* 直接通过 GraphQL 查询所有 resolved threads 的 path+line,匹配 issues
|
|
2467
2549
|
*/ async syncResolvedComments(owner, repo, prNumber, result) {
|
|
2468
2550
|
try {
|
|
2469
|
-
const
|
|
2470
|
-
|
|
2471
|
-
if (!aiReview?.id) {
|
|
2472
|
-
return;
|
|
2473
|
-
}
|
|
2474
|
-
// 获取该 review 的所有行级评论
|
|
2475
|
-
const reviewComments = await this.gitProvider.listPullReviewComments(owner, repo, prNumber, aiReview.id);
|
|
2476
|
-
// 找出已解决的评论(resolver 不为 null)
|
|
2477
|
-
const resolvedComments = reviewComments.filter((c)=>c.resolver !== null && c.resolver !== undefined);
|
|
2478
|
-
if (resolvedComments.length === 0) {
|
|
2551
|
+
const resolvedThreads = await this.gitProvider.listResolvedThreads(owner, repo, prNumber);
|
|
2552
|
+
if (resolvedThreads.length === 0) {
|
|
2479
2553
|
return;
|
|
2480
2554
|
}
|
|
2481
|
-
// 根据文件路径和行号匹配 issues,标记为已解决
|
|
2482
2555
|
const now = new Date().toISOString();
|
|
2483
|
-
for (const
|
|
2484
|
-
|
|
2556
|
+
for (const thread of resolvedThreads){
|
|
2557
|
+
if (!thread.path) continue;
|
|
2558
|
+
const matchedIssue = result.issues.find((issue)=>issue.file === thread.path && this.lineMatchesPosition(issue.line, thread.line));
|
|
2485
2559
|
if (matchedIssue && !matchedIssue.fixed) {
|
|
2486
2560
|
matchedIssue.fixed = now;
|
|
2487
2561
|
console.log(`🟢 问题已标记为已解决: ${matchedIssue.file}:${matchedIssue.line}`);
|
|
@@ -2509,7 +2583,7 @@ ${fileChanges || "无"}`;
|
|
|
2509
2583
|
*/ async syncReactionsToIssues(owner, repo, prNumber, result, verbose) {
|
|
2510
2584
|
try {
|
|
2511
2585
|
const reviews = await this.gitProvider.listPullReviews(owner, repo, prNumber);
|
|
2512
|
-
const aiReview = reviews.find((r)=>r.body?.includes(
|
|
2586
|
+
const aiReview = reviews.find((r)=>r.body?.includes(REVIEW_LINE_COMMENTS_MARKER));
|
|
2513
2587
|
if (!aiReview?.id) {
|
|
2514
2588
|
if (shouldLog(verbose, 2)) {
|
|
2515
2589
|
console.log(`[syncReactionsToIssues] No AI review found`);
|
|
@@ -2520,7 +2594,7 @@ ${fileChanges || "无"}`;
|
|
|
2520
2594
|
const reviewers = new Set();
|
|
2521
2595
|
// 1. 从已提交的 review 中获取评审人(排除 AI bot)
|
|
2522
2596
|
for (const review of reviews){
|
|
2523
|
-
if (review.user?.login && !review.body?.includes(
|
|
2597
|
+
if (review.user?.login && !review.body?.includes(REVIEW_LINE_COMMENTS_MARKER)) {
|
|
2524
2598
|
reviewers.add(review.user.login);
|
|
2525
2599
|
}
|
|
2526
2600
|
}
|
|
@@ -2579,7 +2653,7 @@ ${fileChanges || "无"}`;
|
|
|
2579
2653
|
commentIdToIssue.set(comment.id, matchedIssue);
|
|
2580
2654
|
}
|
|
2581
2655
|
try {
|
|
2582
|
-
const reactions = await this.gitProvider.
|
|
2656
|
+
const reactions = await this.gitProvider.getPullReviewCommentReactions(owner, repo, comment.id);
|
|
2583
2657
|
if (reactions.length === 0 || !matchedIssue) continue;
|
|
2584
2658
|
// 按 content 分组,收集每种 reaction 的用户列表
|
|
2585
2659
|
const reactionMap = new Map();
|
|
@@ -2657,20 +2731,46 @@ ${fileChanges || "无"}`;
|
|
|
2657
2731
|
}
|
|
2658
2732
|
/**
|
|
2659
2733
|
* 删除已有的 AI review(通过 marker 识别)
|
|
2734
|
+
* - 删除行级评论的 PR Review(带 REVIEW_LINE_COMMENTS_MARKER)
|
|
2735
|
+
* - 删除主评论的 Issue Comment(带 REVIEW_COMMENT_MARKER)
|
|
2660
2736
|
*/ async deleteExistingAiReviews(owner, repo, prNumber) {
|
|
2737
|
+
let deletedCount = 0;
|
|
2738
|
+
// 删除行级评论的 PR Review
|
|
2661
2739
|
try {
|
|
2662
2740
|
const reviews = await this.gitProvider.listPullReviews(owner, repo, prNumber);
|
|
2663
|
-
const aiReviews = reviews.filter((r)=>r.body?.includes(REVIEW_COMMENT_MARKER));
|
|
2741
|
+
const aiReviews = reviews.filter((r)=>r.body?.includes(REVIEW_LINE_COMMENTS_MARKER) || r.body?.includes(REVIEW_COMMENT_MARKER));
|
|
2664
2742
|
for (const review of aiReviews){
|
|
2665
2743
|
if (review.id) {
|
|
2666
|
-
|
|
2744
|
+
try {
|
|
2745
|
+
await this.gitProvider.deletePullReview(owner, repo, prNumber, review.id);
|
|
2746
|
+
deletedCount++;
|
|
2747
|
+
} catch {
|
|
2748
|
+
// 已提交的 review 无法删除,忽略
|
|
2749
|
+
}
|
|
2667
2750
|
}
|
|
2668
2751
|
}
|
|
2669
|
-
|
|
2670
|
-
|
|
2752
|
+
} catch (error) {
|
|
2753
|
+
console.warn("⚠️ 列出 PR reviews 失败:", error);
|
|
2754
|
+
}
|
|
2755
|
+
// 删除主评论的 Issue Comment
|
|
2756
|
+
try {
|
|
2757
|
+
const comments = await this.gitProvider.listIssueComments(owner, repo, prNumber);
|
|
2758
|
+
const aiComments = comments.filter((c)=>c.body?.includes(REVIEW_COMMENT_MARKER));
|
|
2759
|
+
for (const comment of aiComments){
|
|
2760
|
+
if (comment.id) {
|
|
2761
|
+
try {
|
|
2762
|
+
await this.gitProvider.deleteIssueComment(owner, repo, comment.id);
|
|
2763
|
+
deletedCount++;
|
|
2764
|
+
} catch (error) {
|
|
2765
|
+
console.warn(`⚠️ 删除评论 ${comment.id} 失败:`, error);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2671
2768
|
}
|
|
2672
2769
|
} catch (error) {
|
|
2673
|
-
console.warn("⚠️
|
|
2770
|
+
console.warn("⚠️ 列出 issue comments 失败:", error);
|
|
2771
|
+
}
|
|
2772
|
+
if (deletedCount > 0) {
|
|
2773
|
+
console.log(`🗑️ 已删除 ${deletedCount} 个旧的 AI review`);
|
|
2674
2774
|
}
|
|
2675
2775
|
}
|
|
2676
2776
|
/**
|
|
@@ -2918,8 +3018,11 @@ ${fileChanges || "无"}`;
|
|
|
2918
3018
|
return filtered;
|
|
2919
3019
|
}
|
|
2920
3020
|
filterDuplicateIssues(newIssues, existingIssues) {
|
|
2921
|
-
//
|
|
2922
|
-
|
|
3021
|
+
// 所有历史问题(无论 valid 状态)都阻止新问题重复添加
|
|
3022
|
+
// valid='false' 的问题已被评审人标记为无效,不应再次报告
|
|
3023
|
+
// valid='true' 的问题已存在,无需重复
|
|
3024
|
+
// fixed 的问题已解决,无需重复
|
|
3025
|
+
const existingKeys = new Set(existingIssues.map((issue)=>this.generateIssueKey(issue)));
|
|
2923
3026
|
const filteredIssues = newIssues.filter((issue)=>!existingKeys.has(this.generateIssueKey(issue)));
|
|
2924
3027
|
const skippedCount = newIssues.length - filteredIssues.length;
|
|
2925
3028
|
return {
|
|
@@ -2929,11 +3032,11 @@ ${fileChanges || "无"}`;
|
|
|
2929
3032
|
}
|
|
2930
3033
|
async getExistingReviewResult(owner, repo, prNumber) {
|
|
2931
3034
|
try {
|
|
2932
|
-
// 从
|
|
2933
|
-
const
|
|
2934
|
-
const
|
|
2935
|
-
if (
|
|
2936
|
-
return this.parseExistingReviewResult(
|
|
3035
|
+
// 从 Issue Comment 获取已有的审查结果
|
|
3036
|
+
const comments = await this.gitProvider.listIssueComments(owner, repo, prNumber);
|
|
3037
|
+
const existingComment = comments.find((c)=>c.body?.includes(REVIEW_COMMENT_MARKER));
|
|
3038
|
+
if (existingComment?.body) {
|
|
3039
|
+
return this.parseExistingReviewResult(existingComment.body);
|
|
2937
3040
|
}
|
|
2938
3041
|
} catch (error) {
|
|
2939
3042
|
console.warn("⚠️ 获取已有评论失败:", error);
|
|
@@ -5080,23 +5183,28 @@ const extension = defineExtension({
|
|
|
5080
5183
|
flags: "--show-all",
|
|
5081
5184
|
description: t("review:options.showAll")
|
|
5082
5185
|
},
|
|
5186
|
+
{
|
|
5187
|
+
flags: "--flush",
|
|
5188
|
+
description: t("review:options.flush")
|
|
5189
|
+
},
|
|
5083
5190
|
{
|
|
5084
5191
|
flags: "--event-action <action>",
|
|
5085
5192
|
description: t("review:options.eventAction")
|
|
5086
5193
|
}
|
|
5087
5194
|
],
|
|
5088
5195
|
run: async (_args, options, ctx)=>{
|
|
5196
|
+
const isFlush = !!options?.flush;
|
|
5089
5197
|
if (!ctx.hasService("gitProvider")) {
|
|
5090
5198
|
ctx.output.error("review 命令需要配置 Git Provider,请在 spaceflow.json 中配置 gitProvider 字段");
|
|
5091
5199
|
process.exit(1);
|
|
5092
5200
|
}
|
|
5093
|
-
if (!ctx.hasService("llmProxy")) {
|
|
5201
|
+
if (!isFlush && !ctx.hasService("llmProxy")) {
|
|
5094
5202
|
ctx.output.error("review 命令需要配置 LLM 服务,请在 spaceflow.json 中配置 llm 字段");
|
|
5095
5203
|
process.exit(1);
|
|
5096
5204
|
}
|
|
5097
5205
|
const gitProvider = ctx.getService("gitProvider");
|
|
5098
5206
|
const configReader = ctx.getService("config");
|
|
5099
|
-
const llmProxy = ctx.getService("llmProxy");
|
|
5207
|
+
const llmProxy = ctx.hasService("llmProxy") ? ctx.getService("llmProxy") : undefined;
|
|
5100
5208
|
const gitSdk = ctx.hasService("gitSdk") ? ctx.getService("gitSdk") : undefined;
|
|
5101
5209
|
const reviewSpecService = new ReviewSpecService(gitProvider);
|
|
5102
5210
|
const reviewReportService = new ReviewReportService();
|
|
@@ -5121,6 +5229,7 @@ const extension = defineExtension({
|
|
|
5121
5229
|
outputFormat: options?.outputFormat,
|
|
5122
5230
|
generateDescription: !!options?.generateDescription,
|
|
5123
5231
|
showAll: !!options?.showAll,
|
|
5232
|
+
flush: isFlush,
|
|
5124
5233
|
eventAction: options?.eventAction
|
|
5125
5234
|
};
|
|
5126
5235
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spaceflow/review",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Spaceflow 代码审查插件,使用 LLM 对 PR 代码进行自动审查",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lydanne",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"@vitest/coverage-v8": "^4.0.18",
|
|
26
26
|
"unplugin-swc": "^1.5.9",
|
|
27
27
|
"vitest": "^4.0.18",
|
|
28
|
-
"@spaceflow/cli": "0.
|
|
28
|
+
"@spaceflow/cli": "0.22.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@spaceflow/core": "0.
|
|
31
|
+
"@spaceflow/core": "0.4.0"
|
|
32
32
|
},
|
|
33
33
|
"spaceflow": {
|
|
34
34
|
"type": "flow",
|
package/src/index.ts
CHANGED
|
@@ -48,23 +48,27 @@ export const extension = defineExtension({
|
|
|
48
48
|
{ flags: "-o, --output-format <format>", description: t("review:options.outputFormat") },
|
|
49
49
|
{ flags: "--generate-description", description: t("review:options.generateDescription") },
|
|
50
50
|
{ flags: "--show-all", description: t("review:options.showAll") },
|
|
51
|
+
{ flags: "--flush", description: t("review:options.flush") },
|
|
51
52
|
{ flags: "--event-action <action>", description: t("review:options.eventAction") },
|
|
52
53
|
],
|
|
53
54
|
run: async (_args, options, ctx) => {
|
|
55
|
+
const isFlush = !!options?.flush;
|
|
54
56
|
if (!ctx.hasService("gitProvider")) {
|
|
55
57
|
ctx.output.error(
|
|
56
58
|
"review 命令需要配置 Git Provider,请在 spaceflow.json 中配置 gitProvider 字段",
|
|
57
59
|
);
|
|
58
60
|
process.exit(1);
|
|
59
61
|
}
|
|
60
|
-
if (!ctx.hasService("llmProxy")) {
|
|
62
|
+
if (!isFlush && !ctx.hasService("llmProxy")) {
|
|
61
63
|
ctx.output.error("review 命令需要配置 LLM 服务,请在 spaceflow.json 中配置 llm 字段");
|
|
62
64
|
process.exit(1);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
const gitProvider = ctx.getService<GitProviderService>("gitProvider");
|
|
66
68
|
const configReader = ctx.getService<ConfigReaderService>("config");
|
|
67
|
-
const llmProxy = ctx.
|
|
69
|
+
const llmProxy = ctx.hasService("llmProxy")
|
|
70
|
+
? ctx.getService<LlmProxyService>("llmProxy")
|
|
71
|
+
: (undefined as unknown as LlmProxyService);
|
|
68
72
|
const gitSdk = ctx.hasService("gitSdk")
|
|
69
73
|
? ctx.getService<GitSdkService>("gitSdk")
|
|
70
74
|
: undefined;
|
|
@@ -104,6 +108,7 @@ export const extension = defineExtension({
|
|
|
104
108
|
outputFormat: options?.outputFormat as ReportFormat,
|
|
105
109
|
generateDescription: !!options?.generateDescription,
|
|
106
110
|
showAll: !!options?.showAll,
|
|
111
|
+
flush: isFlush,
|
|
107
112
|
eventAction: options?.eventAction as string,
|
|
108
113
|
};
|
|
109
114
|
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"options.outputFormat": "Output format: markdown, terminal, json. Auto-selected if not specified (markdown for PR, terminal for CLI)",
|
|
18
18
|
"options.generateDescription": "Generate PR description using AI",
|
|
19
19
|
"options.showAll": "Show all issues found, including those on unchanged lines",
|
|
20
|
+
"options.flush": "Only sync status (reactions, resolved conversations, replies), skip LLM review",
|
|
20
21
|
"options.eventAction": "PR event type (opened, synchronize, closed, etc.), closed only collects stats without AI review",
|
|
21
22
|
"extensionDescription": "Code review command using LLM for automated PR review",
|
|
22
23
|
"mcp.serverDescription": "Code review rules query service",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"options.outputFormat": "输出格式: markdown, terminal, json。不指定则智能选择(PR 用 markdown,终端用 terminal)",
|
|
18
18
|
"options.generateDescription": "使用 AI 生成 PR 功能描述",
|
|
19
19
|
"options.showAll": "显示所有发现的问题,不过滤非变更行的问题",
|
|
20
|
+
"options.flush": "仅刷新状态(同步 reactions、resolved conversations、replies),不执行 LLM 审查",
|
|
20
21
|
"options.eventAction": "PR 事件类型(opened, synchronize, closed 等),closed 时仅收集统计不进行 AI 审查",
|
|
21
22
|
"extensionDescription": "代码审查命令,使用 LLM 对 PR 代码进行自动审查",
|
|
22
23
|
"mcp.serverDescription": "代码审查规则查询服务",
|
package/src/review.config.ts
CHANGED
|
@@ -63,6 +63,8 @@ export interface ReviewOptions {
|
|
|
63
63
|
generateDescription?: boolean;
|
|
64
64
|
/** 显示所有问题,不过滤非变更行的问题 */
|
|
65
65
|
showAll?: boolean;
|
|
66
|
+
/** 仅刷新状态(同步 reactions、resolved 等),不执行 LLM 审查 */
|
|
67
|
+
flush?: boolean;
|
|
66
68
|
/** PR 事件类型(opened, synchronize, closed 等) */
|
|
67
69
|
eventAction?: string;
|
|
68
70
|
concurrency?: number;
|