@spaceflow/review 0.55.0 → 0.56.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 +11 -0
- package/dist/index.js +49 -2
- package/package.json +1 -1
- package/src/locales/en/review.json +3 -1
- package/src/locales/zh-cn/review.json +3 -1
- package/src/mcp/index.ts +54 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.55.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.54.0...@spaceflow/review@0.55.0) (2026-03-02)
|
|
4
|
+
|
|
5
|
+
### 新特性
|
|
6
|
+
|
|
7
|
+
* **review:** 保留历史行级评论,为每轮 Review 生成独立评论并添加上轮回顾 ([de431a0](https://github.com/Lydanne/spaceflow/commit/de431a09b4e3b5e1ada9ee5f1ee65786d22b6ff9))
|
|
8
|
+
* **review:** 支持用户手动 resolve 评论并在报告中区分 AI 修复与手动解决 ([c968b65](https://github.com/Lydanne/spaceflow/commit/c968b65c850bc68de3f4409aa3b5294e5a0311ff))
|
|
9
|
+
|
|
10
|
+
### 修复BUG
|
|
11
|
+
|
|
12
|
+
* **review:** 修复率计算仅统计 AI 修复的问题,排除手动解决的问题 ([12b3415](https://github.com/Lydanne/spaceflow/commit/12b3415749c9d8523e8b23365fbb39fc7657ff1d))
|
|
13
|
+
|
|
3
14
|
## [0.54.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.53.0...@spaceflow/review@0.54.0) (2026-03-02)
|
|
4
15
|
|
|
5
16
|
### 新特性
|
package/dist/index.js
CHANGED
|
@@ -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.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"}')
|
|
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","mcp.getRulesFromDir":"从指定目录加载代码审查规则。读取目录下所有 .md 文件,解析为规则并按规则 ID 去重(后加载的覆盖先加载的)","mcp.dto.dirPath":"包含规则 .md 文件的目录路径,可以是相对路径或绝对路径"}')
|
|
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.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"}')
|
|
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","mcp.getRulesFromDir":"Load code review rules from a specific directory. Reads all .md files in the directory, parses them into rules, and deduplicates by rule ID (later rules override earlier ones)","mcp.dto.dirPath":"Directory path containing rule .md files, can be relative or absolute"}')
|
|
149
149
|
;// CONCATENATED MODULE: ./src/locales/index.ts
|
|
150
150
|
|
|
151
151
|
|
|
@@ -5038,6 +5038,10 @@ const getRulesForFileInputSchema = z.object({
|
|
|
5038
5038
|
const getRuleDetailInputSchema = z.object({
|
|
5039
5039
|
ruleId: z.string().describe(t("review:mcp.dto.ruleId"))
|
|
5040
5040
|
});
|
|
5041
|
+
const getRulesFromDirInputSchema = z.object({
|
|
5042
|
+
dirPath: z.string().describe(t("review:mcp.dto.dirPath")),
|
|
5043
|
+
includeExamples: z.boolean().optional().describe(t("review:mcp.dto.includeExamples"))
|
|
5044
|
+
});
|
|
5041
5045
|
/**
|
|
5042
5046
|
* 获取 GitProviderService(可选)
|
|
5043
5047
|
*/ function getGitProvider(ctx) {
|
|
@@ -5190,6 +5194,49 @@ const tools = [
|
|
|
5190
5194
|
}))
|
|
5191
5195
|
};
|
|
5192
5196
|
}
|
|
5197
|
+
},
|
|
5198
|
+
{
|
|
5199
|
+
name: "get_rules_from_dir",
|
|
5200
|
+
description: t("review:mcp.getRulesFromDir"),
|
|
5201
|
+
inputSchema: getRulesFromDirInputSchema,
|
|
5202
|
+
handler: async (input, ctx)=>{
|
|
5203
|
+
const { dirPath, includeExamples } = input;
|
|
5204
|
+
const workDir = ctx.cwd;
|
|
5205
|
+
const resolvedDir = dirPath.startsWith("/") ? dirPath : join(workDir, dirPath);
|
|
5206
|
+
if (!existsSync(resolvedDir)) {
|
|
5207
|
+
return {
|
|
5208
|
+
error: `Directory not found: ${resolvedDir}`
|
|
5209
|
+
};
|
|
5210
|
+
}
|
|
5211
|
+
const gitProvider = getGitProvider(ctx);
|
|
5212
|
+
const specService = new ReviewSpecService(gitProvider);
|
|
5213
|
+
const specs = await specService.loadReviewSpecs(resolvedDir);
|
|
5214
|
+
const dedupedSpecs = specService.deduplicateSpecs(specs);
|
|
5215
|
+
const rules = dedupedSpecs.flatMap((spec)=>spec.rules.map((rule)=>({
|
|
5216
|
+
id: rule.id,
|
|
5217
|
+
title: rule.title,
|
|
5218
|
+
description: includeExamples ? rule.description : rule.description.slice(0, 200) + (rule.description.length > 200 ? "..." : ""),
|
|
5219
|
+
severity: rule.severity || spec.severity,
|
|
5220
|
+
extensions: spec.extensions,
|
|
5221
|
+
specFile: spec.filename,
|
|
5222
|
+
includes: spec.includes,
|
|
5223
|
+
...includeExamples && rule.examples.length > 0 ? {
|
|
5224
|
+
examples: rule.examples.map((ex)=>({
|
|
5225
|
+
type: ex.type,
|
|
5226
|
+
lang: ex.lang,
|
|
5227
|
+
code: ex.code
|
|
5228
|
+
}))
|
|
5229
|
+
} : {
|
|
5230
|
+
hasExamples: rule.examples.length > 0
|
|
5231
|
+
}
|
|
5232
|
+
})));
|
|
5233
|
+
return {
|
|
5234
|
+
dir: resolvedDir,
|
|
5235
|
+
specFiles: dedupedSpecs.length,
|
|
5236
|
+
total: rules.length,
|
|
5237
|
+
rules
|
|
5238
|
+
};
|
|
5239
|
+
}
|
|
5193
5240
|
}
|
|
5194
5241
|
];
|
|
5195
5242
|
|
package/package.json
CHANGED
|
@@ -28,5 +28,7 @@
|
|
|
28
28
|
"mcp.dto.cwd": "Project root directory path, defaults to current working directory",
|
|
29
29
|
"mcp.dto.filePath": "File path, can be relative or absolute",
|
|
30
30
|
"mcp.dto.includeExamples": "Whether to include rule example code, defaults to false",
|
|
31
|
-
"mcp.dto.ruleId": "Rule ID, e.g. JsTs.Naming.FileName"
|
|
31
|
+
"mcp.dto.ruleId": "Rule ID, e.g. JsTs.Naming.FileName",
|
|
32
|
+
"mcp.getRulesFromDir": "Load code review rules from a specific directory. Reads all .md files in the directory, parses them into rules, and deduplicates by rule ID (later rules override earlier ones)",
|
|
33
|
+
"mcp.dto.dirPath": "Directory path containing rule .md files, can be relative or absolute"
|
|
32
34
|
}
|
|
@@ -28,5 +28,7 @@
|
|
|
28
28
|
"mcp.dto.cwd": "项目根目录路径,默认为当前工作目录",
|
|
29
29
|
"mcp.dto.filePath": "文件路径,可以是相对路径或绝对路径",
|
|
30
30
|
"mcp.dto.includeExamples": "是否包含规则示例代码,默认 false",
|
|
31
|
-
"mcp.dto.ruleId": "规则 ID,如 JsTs.Naming.FileName"
|
|
31
|
+
"mcp.dto.ruleId": "规则 ID,如 JsTs.Naming.FileName",
|
|
32
|
+
"mcp.getRulesFromDir": "从指定目录加载代码审查规则。读取目录下所有 .md 文件,解析为规则并按规则 ID 去重(后加载的覆盖先加载的)",
|
|
33
|
+
"mcp.dto.dirPath": "包含规则 .md 文件的目录路径,可以是相对路径或绝对路径"
|
|
32
34
|
}
|
package/src/mcp/index.ts
CHANGED
|
@@ -16,6 +16,11 @@ export const getRuleDetailInputSchema = z.object({
|
|
|
16
16
|
ruleId: z.string().describe(t("review:mcp.dto.ruleId")),
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
export const getRulesFromDirInputSchema = z.object({
|
|
20
|
+
dirPath: z.string().describe(t("review:mcp.dto.dirPath")),
|
|
21
|
+
includeExamples: z.boolean().optional().describe(t("review:mcp.dto.includeExamples")),
|
|
22
|
+
});
|
|
23
|
+
|
|
19
24
|
/**
|
|
20
25
|
* 获取 GitProviderService(可选)
|
|
21
26
|
*/
|
|
@@ -165,4 +170,53 @@ export const tools = [
|
|
|
165
170
|
};
|
|
166
171
|
},
|
|
167
172
|
},
|
|
173
|
+
{
|
|
174
|
+
name: "get_rules_from_dir",
|
|
175
|
+
description: t("review:mcp.getRulesFromDir"),
|
|
176
|
+
inputSchema: getRulesFromDirInputSchema,
|
|
177
|
+
handler: async (input, ctx) => {
|
|
178
|
+
const { dirPath, includeExamples } = input as z.infer<typeof getRulesFromDirInputSchema>;
|
|
179
|
+
const workDir = ctx.cwd;
|
|
180
|
+
const resolvedDir = dirPath.startsWith("/") ? dirPath : join(workDir, dirPath);
|
|
181
|
+
|
|
182
|
+
if (!existsSync(resolvedDir)) {
|
|
183
|
+
return { error: `Directory not found: ${resolvedDir}` };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const gitProvider = getGitProvider(ctx);
|
|
187
|
+
const specService = new ReviewSpecService(gitProvider);
|
|
188
|
+
const specs = await specService.loadReviewSpecs(resolvedDir);
|
|
189
|
+
const dedupedSpecs = specService.deduplicateSpecs(specs);
|
|
190
|
+
|
|
191
|
+
const rules = dedupedSpecs.flatMap((spec) =>
|
|
192
|
+
spec.rules.map((rule) => ({
|
|
193
|
+
id: rule.id,
|
|
194
|
+
title: rule.title,
|
|
195
|
+
description: includeExamples
|
|
196
|
+
? rule.description
|
|
197
|
+
: rule.description.slice(0, 200) + (rule.description.length > 200 ? "..." : ""),
|
|
198
|
+
severity: rule.severity || spec.severity,
|
|
199
|
+
extensions: spec.extensions,
|
|
200
|
+
specFile: spec.filename,
|
|
201
|
+
includes: spec.includes,
|
|
202
|
+
...(includeExamples && rule.examples.length > 0
|
|
203
|
+
? {
|
|
204
|
+
examples: rule.examples.map((ex) => ({
|
|
205
|
+
type: ex.type,
|
|
206
|
+
lang: ex.lang,
|
|
207
|
+
code: ex.code,
|
|
208
|
+
})),
|
|
209
|
+
}
|
|
210
|
+
: { hasExamples: rule.examples.length > 0 }),
|
|
211
|
+
})),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
dir: resolvedDir,
|
|
216
|
+
specFiles: dedupedSpecs.length,
|
|
217
|
+
total: rules.length,
|
|
218
|
+
rules,
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
},
|
|
168
222
|
];
|