job51-gitlab-cr-node-jt-1 2.7.6 → 2.7.9
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.
|
@@ -45,6 +45,17 @@
|
|
|
45
45
|
> - 如果项目现有接口多数没有显式参数判空逻辑,**不得建议添加**
|
|
46
46
|
> - 参数校验形式应参考同文件/同模块其他接口的写法,保持一致性
|
|
47
47
|
> - **仅在校验风格与同一文件中其他接口明显不一致时**,才需要提示
|
|
48
|
+
> - **⚠️ 老代码修改审查原则**(关键规则):
|
|
49
|
+
> - **审查范围限制**:对于已有代码的修改(不是新增),**只针对修改的具体内容进行评审**
|
|
50
|
+
> - **禁止建议周边逻辑改动**:**不得报告整体逻辑或相关逻辑的修改优化建议**(如"建议重构整个方法"、"建议优化周边调用链"等)
|
|
51
|
+
> - **原因**:
|
|
52
|
+
> - 贸然修改原有逻辑会引入风险,可能破坏已验证的稳定功能
|
|
53
|
+
> - 周边逻辑的改动不在当前需求点范围内,可能不会经过有效测试
|
|
54
|
+
> - 原有代码已经过开发自测和 QA 测试验证,应保持最小改动原则
|
|
55
|
+
> - **正确做法**:
|
|
56
|
+
> - 只审查变更行本身是否有运行时异常风险、安全问题等严重缺陷
|
|
57
|
+
> - 不建议对方法的整体逻辑、调用链、数据流等进行优化
|
|
58
|
+
> - 即使周边逻辑看起来可以优化,也**保持沉默**,不在本次 CR 中报告
|
|
48
59
|
|
|
49
60
|
0. **Diff 数据结构与上下文读取规则**:
|
|
50
61
|
> - **临时文件格式说明**:
|
|
@@ -288,6 +299,33 @@
|
|
|
288
299
|
|
|
289
300
|
18. **输出格式**:严格参照 SKILL.md 模板,无某类问题时省略对应部分;
|
|
290
301
|
> - **必须输出 `<LINE_INFO>` 标签**:包含所有问题的行号信息
|
|
302
|
+
> - **⚠️ 标签位置要求(最高优先级)**:
|
|
303
|
+
> - **`<LINE_INFO>` 标签必须放置在 `</REPORT>` 标签之后**
|
|
304
|
+
> - **禁止**将 `<LINE_INFO>` 包含在 `<REPORT>...</REPORT>` 标签内部
|
|
305
|
+
> - **禁止**将 `<LINE_INFO>` 穿插在报告内容中
|
|
306
|
+
> - **正确格式示例**:
|
|
307
|
+
```markdown
|
|
308
|
+
<REPORT>
|
|
309
|
+
## 🤖 AI 代码审查结果
|
|
310
|
+
|
|
311
|
+
### 🔴 严重问题
|
|
312
|
+
|
|
313
|
+
**问题 1**:...
|
|
314
|
+
</REPORT>
|
|
315
|
+
|
|
316
|
+
<LINE_INFO>[{"new_path":"...","new_line":10}]
|
|
317
|
+
</LINE_INFO>
|
|
318
|
+
```
|
|
319
|
+
> - **错误格式示例**(禁止):
|
|
320
|
+
```markdown
|
|
321
|
+
<REPORT>
|
|
322
|
+
## 🤖 AI 代码审查结果
|
|
323
|
+
|
|
324
|
+
**问题 1**:...
|
|
325
|
+
<LINE_INFO>[{"new_path":"...","new_line":10}]
|
|
326
|
+
</LINE_INFO>
|
|
327
|
+
</REPORT>
|
|
328
|
+
```
|
|
291
329
|
> - **LINE_INFO 输出约束**(关键规则):
|
|
292
330
|
> - **仅有存在严重问题时,才允许在 LINE_INFO 中输出行号数据**
|
|
293
331
|
> - **无严重问题时,LINE_INFO 必须输出空数组 `[]`**
|
|
@@ -322,6 +360,7 @@
|
|
|
322
360
|
> - **禁止**在报告中输出错误代码示例(已经有行号定位了,不需要重复)
|
|
323
361
|
|
|
324
362
|
19. **误报防控检查清单**(报告前必须确认):
|
|
363
|
+
> - [ ] **⚠️ 再次查阅代码确认(最高优先级)**:报告任何问题前,必须再次使用 Read 工具查阅相关代码文件。查阅范围包括:**代码所在方法、方法调用的上下游链路、相关依赖类/接口**。仔细确认代码逻辑,验证问题是否真实存在。禁止凭记忆或第一次分析就报告问题。
|
|
325
364
|
> - [ ] **已安全处理验证**:代码是否已有判空/异常处理/边界检查?如是,省略该问题
|
|
326
365
|
> - [ ] **逻辑一致性验证**:问题描述是否自相矛盾?(如"有判空但说未判空"、"逻辑正确但报告为问题")
|
|
327
366
|
> - [ ] 已追踪读取相关方法实现,确认可能返回危险值
|
|
@@ -54,6 +54,7 @@ description: 代码审查技能,审查变更代码并输出 REPORT 和 LINE_IN
|
|
|
54
54
|
- 行号信息放入 `<LINE_INFO>` 标签(必须输出)
|
|
55
55
|
- LINE_INFO 格式参考 @.claude/rules/code-review-rules.md 第 18 条
|
|
56
56
|
- **问题和行号对应**:`<LINE_INFO>` 中的第 N 个元素对应第 N 个问题的行号
|
|
57
|
+
- **⚠️ 关键格式要求**:`<LINE_INFO>` 标签必须放置在 `</REPORT>` 标签之后,**禁止**出现在 `</REPORT>` 之前或穿插在报告内容中
|
|
57
58
|
|
|
58
59
|
## 输出模板
|
|
59
60
|
|
package/index.js
CHANGED
|
@@ -170,9 +170,277 @@ class GitLabCodeReviewer {
|
|
|
170
170
|
// 结束审查统计
|
|
171
171
|
this.metrics.endReview();
|
|
172
172
|
|
|
173
|
+
// ========== 新增:生成并发布汇总报告 ==========
|
|
174
|
+
try {
|
|
175
|
+
debugLog('开始生成汇总报告...');
|
|
176
|
+
|
|
177
|
+
// 收集所有审查报告内容
|
|
178
|
+
const allReportsText = this.collectAllReviewReports(results);
|
|
179
|
+
|
|
180
|
+
// 调用 AI 生成汇总报告
|
|
181
|
+
const summaryReport = await this.generateSummaryReportWithAI(allReportsText);
|
|
182
|
+
|
|
183
|
+
debugLog(`汇总报告生成完成,长度:${summaryReport.length} 字符`);
|
|
184
|
+
|
|
185
|
+
// 发布汇总报告到 GitLab
|
|
186
|
+
await this.postSummaryCommentToGitLab(projectId, mergeRequestIid, summaryReport);
|
|
187
|
+
debugLog('汇总报告已成功发布到 GitLab MR');
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('汇总报告生成或发布失败:', error.message);
|
|
190
|
+
// 不抛出错误,确保主流程评论已发布的成功
|
|
191
|
+
}
|
|
192
|
+
|
|
173
193
|
return results;
|
|
174
194
|
}
|
|
175
195
|
|
|
196
|
+
/**
|
|
197
|
+
* 收集并拼接所有审查报告内容
|
|
198
|
+
* @param {Array} results 所有审查结果数组
|
|
199
|
+
* @returns {string} 拼接后的完整报告文本,包含文件和 diff 块标识
|
|
200
|
+
*/
|
|
201
|
+
collectAllReviewReports(results) {
|
|
202
|
+
let allReportsText = '';
|
|
203
|
+
|
|
204
|
+
// 遍历所有审查结果
|
|
205
|
+
for (let i = 0; i < results.length; i++) {
|
|
206
|
+
const result = results[i];
|
|
207
|
+
const filePath = result.diff_info.new_path || result.diff_info.old_path;
|
|
208
|
+
const blockIndex = result.block_index;
|
|
209
|
+
const reportContent = result.review_result?.reportContent || '';
|
|
210
|
+
|
|
211
|
+
// 添加文件和 diff 块标识
|
|
212
|
+
allReportsText += `\n\n========== 审查报告 #${i + 1} ==========`;
|
|
213
|
+
allReportsText += `\n文件:${filePath}`;
|
|
214
|
+
allReportsText += `\nDiff 块索引:#${blockIndex}`;
|
|
215
|
+
allReportsText += `\n\n${reportContent}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return allReportsText;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 调用 Claude AI 生成汇总报告
|
|
223
|
+
* @param {string} allReportsText 所有审查报告拼接后的文本
|
|
224
|
+
* @returns {Promise<string>} AI 生成的汇总报告(Markdown 格式)
|
|
225
|
+
*/
|
|
226
|
+
async generateSummaryReportWithAI(allReportsText) {
|
|
227
|
+
debugLog('开始调用 Claude 生成汇总报告');
|
|
228
|
+
|
|
229
|
+
// 构造 Prompt
|
|
230
|
+
const prompt = this.buildSummaryPrompt(allReportsText);
|
|
231
|
+
|
|
232
|
+
// 创建临时文件存储 prompt 内容
|
|
233
|
+
const tmpFileName = `summary_prompt_${Date.now()}.txt`;
|
|
234
|
+
fs.writeFileSync(tmpFileName, prompt, 'utf-8');
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
// 调用 Claude CLI 命令
|
|
238
|
+
const summaryReport = await this.callClaudeForSummary(tmpFileName);
|
|
239
|
+
|
|
240
|
+
// 提取 <SUMMARY_REPORT> 标签内容(如果有)
|
|
241
|
+
const reportMatch = summaryReport.match(/<SUMMARY_REPORT>(.*?)<\/SUMMARY_REPORT>/s);
|
|
242
|
+
if (reportMatch) {
|
|
243
|
+
return reportMatch[1].trim();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 如果没有标签,返回完整内容
|
|
247
|
+
return summaryReport.trim();
|
|
248
|
+
|
|
249
|
+
} finally {
|
|
250
|
+
// 删除临时文件
|
|
251
|
+
try {
|
|
252
|
+
if (fs.existsSync(tmpFileName)) {
|
|
253
|
+
fs.unlinkSync(tmpFileName);
|
|
254
|
+
}
|
|
255
|
+
} catch (cleanupError) {
|
|
256
|
+
console.error('清理临时文件失败:', cleanupError.message);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 构造汇总报告生成的 Prompt
|
|
263
|
+
* @param {string} allReportsText 所有审查报告文本
|
|
264
|
+
* @returns {string} 完整的 Prompt 内容
|
|
265
|
+
*/
|
|
266
|
+
buildSummaryPrompt(allReportsText) {
|
|
267
|
+
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
|
268
|
+
|
|
269
|
+
const prompt = `你是一个专业的代码审查汇总助手。请根据以下所有审查报告内容,生成一份汇总报告。
|
|
270
|
+
|
|
271
|
+
**要求**:
|
|
272
|
+
1. **仅输出一份汇总报告**,必须以 \`<SUMMARY_REPORT>\` 开始,以 \`</SUMMARY_REPORT>\` 结束
|
|
273
|
+
2. **不得输出任何解释、问候或额外文本**
|
|
274
|
+
3. **汇总报告必须包含**:
|
|
275
|
+
- 审查概览:文件数量、diff 块数量、问题总数、严重问题数、警告数
|
|
276
|
+
- 无问题文件列表:列出所有没有问题的文件路径(完整路径)
|
|
277
|
+
- 严重问题汇总:按文件分组,完整展示每个问题的描述、位置、修改建议
|
|
278
|
+
- 警告汇总:按文件分组,完整展示每个问题的描述、位置、修改建议
|
|
279
|
+
- 审查详情:使用 \`<details>\` 折叠标签隐藏所有完整报告内容
|
|
280
|
+
- 建议:针对问题的处理建议
|
|
281
|
+
4. **统计数据准确**:根据提供的报告内容准确统计文件数、问题数
|
|
282
|
+
5. **完整展示问题**:每个问题都要完整展示描述、位置、修改建议,不要简化
|
|
283
|
+
6. **即使无问题也要生成报告**:如果所有文件都没有问题,生成一个简短的汇总报告说明审查完成且无问题
|
|
284
|
+
|
|
285
|
+
**输出格式模板**:
|
|
286
|
+
|
|
287
|
+
<SUMMARY_REPORT>
|
|
288
|
+
## 🤖 AI 代码审查汇总报告
|
|
289
|
+
|
|
290
|
+
**生成时间**: ${timestamp}
|
|
291
|
+
**审查范围**: [文件数量]个文件,[diff块数量]个变更块
|
|
292
|
+
**发现问题**: [总数]个,其中严重问题[数量]个
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
### 📊 审查概览
|
|
297
|
+
|
|
298
|
+
- ✅ 无问题文件:[数量]个(详见下方列表)
|
|
299
|
+
- ⚠️ 有问题文件:[数量]个
|
|
300
|
+
- 🔴 严重问题:[数量]个
|
|
301
|
+
- 🟡 一般警告:[数量]个
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
### ✅ 无问题文件列表
|
|
306
|
+
|
|
307
|
+
以下文件审查完成,未发现问题:
|
|
308
|
+
|
|
309
|
+
1. \`[文件路径1]\`
|
|
310
|
+
2. \`[文件路径2]\`
|
|
311
|
+
...
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
### 🔴 严重问题汇总
|
|
316
|
+
|
|
317
|
+
以下文件包含严重问题,需要优先处理:
|
|
318
|
+
|
|
319
|
+
#### 1. [文件路径] ([问题数量]个问题)
|
|
320
|
+
|
|
321
|
+
**问题 1**:[完整问题描述]
|
|
322
|
+
- **位置**:Diff 块 #[块索引],[行号]
|
|
323
|
+
- **修改建议**:[完整修改建议,包含示例代码]
|
|
324
|
+
|
|
325
|
+
**问题 2**:...
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
### 🟡 一般警告汇总
|
|
330
|
+
|
|
331
|
+
以下文件包含警告级别问题:
|
|
332
|
+
|
|
333
|
+
#### 1. [文件路径] ([警告数量]个警告)
|
|
334
|
+
|
|
335
|
+
**问题 1**:[完整问题描述]
|
|
336
|
+
- **位置**:Diff 块 #[块索引],[行号]
|
|
337
|
+
- **修改建议**:[完整修改建议]
|
|
338
|
+
|
|
339
|
+
...
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
### 📝 审查详情
|
|
344
|
+
|
|
345
|
+
<details>
|
|
346
|
+
<summary>点击展开完整审查报告</summary>
|
|
347
|
+
|
|
348
|
+
#### 文件:[文件路径1]
|
|
349
|
+
|
|
350
|
+
**Diff 块 #0**
|
|
351
|
+
|
|
352
|
+
[完整报告内容]
|
|
353
|
+
|
|
354
|
+
...
|
|
355
|
+
|
|
356
|
+
</details>
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### 💡 建议
|
|
361
|
+
|
|
362
|
+
1. **优先处理严重问题**:这些问题可能导致运行时异常或数据错误
|
|
363
|
+
2. **测试验证**:建议对修改后的代码进行充分的单元测试和集成测试
|
|
364
|
+
3. **代码规范**:部分警告涉及代码风格,建议参考项目规范进行统一
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
*本报告由 AI 自动生成,建议结合人工审查确认问题准确性*
|
|
369
|
+
</SUMMARY_REPORT>
|
|
370
|
+
|
|
371
|
+
**提供的审查报告内容**:
|
|
372
|
+
|
|
373
|
+
${allReportsText}
|
|
374
|
+
|
|
375
|
+
**最后强调**:必须严格按照上述模板格式输出,以 \`<SUMMARY_REPORT>\` 开始,以 \`</SUMMARY_REPORT>\` 结束,不得输出其他任何文本!`;
|
|
376
|
+
|
|
377
|
+
return prompt;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* 调用 Claude CLI 命令生成汇总报告
|
|
382
|
+
* @param {string} promptFile Prompt 临时文件路径
|
|
383
|
+
* @returns {Promise<string>} Claude 返回的汇总报告内容
|
|
384
|
+
*/
|
|
385
|
+
async callClaudeForSummary(promptFile) {
|
|
386
|
+
const maxRetries = 5;
|
|
387
|
+
let attempt = 0;
|
|
388
|
+
|
|
389
|
+
while (attempt < maxRetries) {
|
|
390
|
+
attempt++;
|
|
391
|
+
debugLog(`调用 Claude 生成汇总报告,尝试 ${attempt}/${maxRetries}`);
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
// 使用 spawn 执行 Claude CLI 命令
|
|
395
|
+
const claudeProcess = spawn('claude', ['--tools', 'default', '-p', '--', promptFile], {
|
|
396
|
+
cwd: process.cwd(),
|
|
397
|
+
env: process.env,
|
|
398
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
let stdout = '';
|
|
402
|
+
let stderr = '';
|
|
403
|
+
|
|
404
|
+
claudeProcess.stdout.on('data', (data) => {
|
|
405
|
+
stdout += data.toString();
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
claudeProcess.stderr.on('data', (data) => {
|
|
409
|
+
stderr += data.toString();
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// 等待进程完成
|
|
413
|
+
await new Promise((resolve, reject) => {
|
|
414
|
+
claudeProcess.on('close', (code) => {
|
|
415
|
+
if (code === 0) {
|
|
416
|
+
resolve();
|
|
417
|
+
} else {
|
|
418
|
+
reject(new Error(`Claude process exited with code ${code}: ${stderr}`));
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
claudeProcess.on('error', (error) => {
|
|
423
|
+
reject(error);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// 验证返回内容是否包含 SUMMARY_REPORT 标签
|
|
428
|
+
if (stdout.includes('<SUMMARY_REPORT>')) {
|
|
429
|
+
debugLog('汇总报告生成成功');
|
|
430
|
+
return stdout;
|
|
431
|
+
} else {
|
|
432
|
+
debugLog(`返回内容不符合预期,缺少 <SUMMARY_REPORT> 标签,重试...`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
} catch (error) {
|
|
436
|
+
debugLog(`调用 Claude 失败:${error.message},重试...`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// 如果所有重试都失败,抛出错误
|
|
441
|
+
throw new Error('生成汇总报告失败,已达到最大重试次数');
|
|
442
|
+
}
|
|
443
|
+
|
|
176
444
|
/**
|
|
177
445
|
* 使用Claude对单个diff文件进行代码审核
|
|
178
446
|
* @param {string} filePath 临时文件路径
|
|
@@ -722,6 +990,38 @@ class GitLabCodeReviewer {
|
|
|
722
990
|
{ method: 'POST', data: payload }
|
|
723
991
|
);
|
|
724
992
|
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* 发布汇总报告到 GitLab MR
|
|
996
|
+
* @param {number} projectId GitLab 项目 ID
|
|
997
|
+
* @param {number} mergeRequestIid 合并请求 IID
|
|
998
|
+
* @param {string} summaryReport Markdown 格式的汇总报告
|
|
999
|
+
* @returns {Promise<void>}
|
|
1000
|
+
*/
|
|
1001
|
+
async postSummaryCommentToGitLab(projectId, mergeRequestIid, summaryReport) {
|
|
1002
|
+
try {
|
|
1003
|
+
debugLog(`开始发布汇总报告到 MR ${mergeRequestIid}`);
|
|
1004
|
+
|
|
1005
|
+
// 构造评论 Payload
|
|
1006
|
+
const discussionData = {
|
|
1007
|
+
body: summaryReport
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// 调用 GitLab API 发布一般讨论评论
|
|
1011
|
+
// 使用已有的 gitlabClient 实例
|
|
1012
|
+
await this.gitlabClient.callGitLabAPI(
|
|
1013
|
+
`/projects/${projectId}/merge_requests/${mergeRequestIid}/discussions`,
|
|
1014
|
+
{ method: 'POST', data: discussionData }
|
|
1015
|
+
);
|
|
1016
|
+
|
|
1017
|
+
debugLog('汇总报告已成功发布');
|
|
1018
|
+
this.metrics.recordCommentPublished();
|
|
1019
|
+
|
|
1020
|
+
} catch (error) {
|
|
1021
|
+
console.error('发布汇总报告失败:', error.message);
|
|
1022
|
+
// 不抛出错误,允许主流程继续(确保已发布的单个评论不受影响)
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
725
1025
|
}
|
|
726
1026
|
|
|
727
1027
|
// 主函数
|