job51-gitlab-cr-node-jt-1 2.4.7 → 2.4.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.
@@ -1,9 +1,9 @@
1
1
  # GitLab Code Review AI Tool 技术文档
2
2
 
3
3
  **项目名称**: job51-gitlab-cr-node
4
- **当前版本**: 2.4.1
4
+ **当前版本**: 2.4.9
5
5
  **作者**: tao.jing
6
- **最后更新**: 2026-04-15
6
+ **最后更新**: 2026-04-16
7
7
  **项目地址**: https://gitdev.51job.com/51jobweb/ai-agent
8
8
  **当前分支**: jt-test-skill
9
9
 
@@ -338,7 +338,7 @@ class GitLabAPIClient {
338
338
 
339
339
  ## 4. 关键算法与流程
340
340
 
341
- ### 4.1 严重问题判断逻辑(核心)
341
+ ### 4.1 严重问题判断逻辑(核心)(2.4.4 更新)
342
342
 
343
343
  **位置**: `index.js:55-133`
344
344
 
@@ -368,9 +368,9 @@ class GitLabAPIClient {
368
368
  │ │ const hasSeriousProblem = │ │
369
369
  │ │ claudeResult.includes('严重问题') │ │
370
370
  │ │ │ │
371
- │ │ 如果 false → 无严重问题 │ │
372
- │ │ 返回:{ reportContent: claudeResult, │ │
373
- │ │ lineInfo: lineInfoContent } │ │
371
+ │ │ 如果 false → 无严重问题,返回标准空格式 │ │
372
+ │ │ reportContent: '<REPORT>\n## 🤖 AI 代码审查结果\n\n</REPORT>'│ │
373
+ │ │ lineInfo: '[]' │ │
374
374
  │ └───────────────────────────────────────────────────────┘ │
375
375
  │ ↓ (有严重问题) │
376
376
  │ 步骤 4: 检查标题是否符合要求 │
@@ -390,7 +390,7 @@ class GitLabAPIClient {
390
390
  | 判断步骤 | 检查内容 | 判断条件 | 通过时动作 | 失败时动作 |
391
391
  |----------|----------|----------|------------|------------|
392
392
  | **步骤 1** | LINE_INFO 标签存在性 | `hasLineInfoTag && hasNonEmptyLineInfo` | 进入步骤 2 | 返回空格式 |
393
- | **步骤 2** | 严重问题关键词 | `claudeResult.includes('严重问题')` | 进入步骤 3 | 返回原报告 |
393
+ | **步骤 2** | 严重问题关键词 | `claudeResult.includes('严重问题')` | 进入步骤 3 | 返回空格式 |
394
394
  | **步骤 3** | 标题格式正确性 | `claudeResult.includes('🤖 AI 代码审查结果')` | 接受结果 | 重试 (最多 5 次) |
395
395
 
396
396
  **重试策略详情**:
@@ -413,6 +413,10 @@ if (attempts >= maxAttempts) {
413
413
  [DEBUG] ... 【决策】报告包含严重问题且标题正确,接受结果 (尝试 1)
414
414
  ```
415
415
 
416
+ **v2.4.4 变更**:
417
+ - 无严重问题时统一返回标准空格式(之前返回原始结果)
418
+ - 达到最大重试次数时返回最后一次 AI 结果(之前返回空格式)
419
+
416
420
  ### 4.2 评论发布决策逻辑
417
421
 
418
422
  **位置**: `index.js:173-183`
@@ -491,11 +495,11 @@ getDiffBlocks(diffObj) {
491
495
  | 空格 | `current_line++` | 上下文代码,计入行号 |
492
496
  | `-` | 不变 | 删除代码,不计入行号 |
493
497
 
494
- ### 4.4 评论发布核心逻辑(详细)
498
+ ### 4.4 评论发布核心逻辑(详细)(2.4.6/2.4.7 更新)
495
499
 
496
- **位置**: `index.js:497-589`
500
+ **位置**: `index.js:577-665`
497
501
 
498
- **完整发布流程**:
502
+ **完整发布流程**(v2.4.6 更新):
499
503
  ```
500
504
  ┌─────────────────────────────────────────────────────────────────────────┐
501
505
  │ 评论发布完整流程 │
@@ -509,54 +513,75 @@ getDiffBlocks(diffObj) {
509
513
  │ │ - start_sha: 起始提交 SHA │ │
510
514
  │ └───────────────────────────────────────────────────────────────────┘ │
511
515
  │ ↓ │
512
- │ 步骤 2: 确定目标行号 (targetLine)
516
+ │ 步骤 2: 解析 LINE_INFO 中的所有问题行号
513
517
  │ ┌───────────────────────────────────────────────────────────────────┐ │
514
- │ │ 优先级 1: LINE_INFO 标签解析的行号 (最精确) │ │
515
- │ │ 优先级 2: diff 块起始行号 (后备方案) │ │
518
+ │ │ allLineInfo = parseAllLineInfoFromReviewResult(lineInfoTag) │ │
516
519
  │ │ │ │
517
- │ │ 验证:parsedLineInfo.new_line 必须在 [new_start, new_end] 范围内 │ │
520
+ │ │ v2.4.6 新增:支持多个问题行号 │ │
518
521
  │ └───────────────────────────────────────────────────────────────────┘ │
519
522
  │ ↓ │
520
- │ 步骤 3: 选择评论类型
523
+ │ 步骤 3: 循环处理每个问题
521
524
  │ ┌───────────────────────────────────────────────────────────────────┐ │
522
- │ │ targetLine Diff 评论 (行级评论,定位到具体代码行) │ │
523
- │ │ 无 targetLine → 一般讨论 (文件级评论) │ │
525
+ │ │ for (let i = 0; i < allLineInfo.length; i++) │ │
526
+ │ │ a. 提取单个问题报告内容 │ │
527
+ │ │ singleProblemContent = extractSingleProblemReport(i + 1) │ │
528
+ │ │ b. 构建目标行号 │ │
529
+ │ │ - 删除代码:使用 old_line + old_path │ │
530
+ │ │ - 新增/上下文:使用 new_line + new_path │ │
531
+ │ │ c. 发布评论到对应行 │ │
532
+ │ │ d. GitLab API 失败降级为一般讨论 │ │
524
533
  │ └───────────────────────────────────────────────────────────────────┘ │
525
534
  │ ↓ │
526
- │ 步骤 4: 调用 GitLab API 发布评论
527
- │ ┌───────────────────────────────────────────────────────────────────┐ │
528
- │ │ Diff 评论失败 → 自动降级为一般讨论 │ │
529
- │ └───────────────────────────────────────────────────────────────────┘ │
535
+ │ 步骤 4: 所有评论发布完成
530
536
  └─────────────────────────────────────────────────────────────────────────┘
531
537
  ```
532
538
 
533
- **targetLine 确定逻辑**:
539
+ **targetLine 确定逻辑**(v2.4.7 更新):
534
540
  ```javascript
535
- // 第一层:根据 diff 块第一行字符类型确定基础 targetLine
536
- let targetLine = lineInfo.firstLineFirstChar === '+'
537
- ? { new_line: lineInfo.new_start, new_path: diff_info.new_path }
538
- : lineInfo.firstLineFirstChar === '-'
539
- ? { old_line: lineInfo.old_start, old_path: diff_info.old_path }
540
- : { new_line: lineInfo.new_start, new_path: diff_info.new_path };
541
-
542
- // 第二层:尝试从 LINE_INFO 标签解析更精确的行号
543
- const parsedLineInfo = this.parseLineInfoFromReviewResult(lineInfoTag);
544
- if (parsedLineInfo) {
545
- // 验证行号是否在 diff 块范围内
546
- const newStart = lineInfo.new_start;
547
- const newCount = lineInfo.new_count || 0;
548
- const newEnd = newStart + newCount - 1;
541
+ // v2.4.7:直接使用 AI 返回的绝对行号,不再验证范围
542
+ for (let i = 0; i < allLineInfo.length; i++) {
543
+ const problemInfo = allLineInfo[i];
549
544
 
550
- if (parsedLineInfo.new_line >= newStart && parsedLineInfo.new_line <= newEnd) {
551
- // 行号有效,使用解析结果
552
- targetLine = parsedLineInfo;
553
- } else {
554
- // 行号越界,使用 diff 块起始行号
555
- debugLog(`解析的行号 ${parsedLineInfo.new_line} 不在 diff 块范围 [${newStart}, ${newEnd}] 内`);
545
+ let targetLine;
546
+ if (problemInfo.old_line && !problemInfo.new_line) {
547
+ // 删除代码,使用 old_line
548
+ targetLine = {
549
+ old_path: problemInfo.old_path,
550
+ old_line: problemInfo.old_line
551
+ };
552
+ } else if (problemInfo.new_line) {
553
+ // AI 返回的是文件中的绝对行号,直接使用
554
+ targetLine = {
555
+ new_path: problemInfo.new_path,
556
+ new_line: problemInfo.new_line
557
+ };
556
558
  }
559
+
560
+ if (!targetLine) {
561
+ // 无法解析行号,使用一般讨论
562
+ await this.createGeneralDiscussion(...);
563
+ continue;
564
+ }
565
+
566
+ // 发布评论到对应行
567
+ const payload = {
568
+ body: singleProblemContent,
569
+ position: {
570
+ position_type: 'text',
571
+ base_sha: baseSha,
572
+ head_sha: headSha,
573
+ start_sha: startSha,
574
+ ...targetLine
575
+ }
576
+ };
557
577
  }
558
578
  ```
559
579
 
580
+ **v2.4.7 重要变更**:
581
+ - **移除行号范围验证**:之前会验证 `parsedLineInfo.new_line ∈ [new_start, new_end]`
582
+ - **直接使用绝对行号**:AI 返回的行号是文件中的绝对行号,直接使用
583
+ - **原因**:AI 读取完整源文件后定位问题,返回的是文件实际行号,不是基于 `New Start` 计算的
584
+
560
585
  **GitLab API Payload 结构**:
561
586
  ```javascript
562
587
  // Diff 评论 Payload
@@ -626,38 +651,52 @@ async processWithThreadPool(tasks, processor, maxConcurrency = 3) {
626
651
  export GITLAB_CR_CONCURRENCY=5 # 默认 3
627
652
  ```
628
653
 
629
- ### 4.6 行号解析算法
654
+ ### 4.6 行号解析算法(2.4.7 更新)
655
+
656
+ **行号定义**:
657
+ - 行号是变更后文件中的**绝对行号**(从 1 开始计数)
658
+ - 例如:如果新增代码在变更后文件的第 43 行,则 `new_line = 43`
630
659
 
631
- **位置**: `index.js:435-470`
660
+ **重要变更**(v2.4.7):
661
+ - **v2.4.7 之前**:使用 diff 块的 `New Start` 作为基准计算行号,并验证行号是否在 diff 块范围内
662
+ - **v2.4.7 及之后**:AI 直接返回文件中的绝对行号,不再基于 `New Start` 计算,移除范围验证
632
663
 
633
- **LINE_INFO 解析函数**:
664
+ **原因**:
665
+ - AI 会读取完整的变更后文件来分析上下文
666
+ - AI 在完整源文件中定位问题,返回的是文件中的绝对行号
667
+ - 使用 `New Start` 验证行号范围会导致误判(如 diff 块 `New Start=1` 但 AI 返回行号 43)
668
+
669
+ **原解析函数**:
634
670
  ```javascript
635
671
  parseLineInfoFromReviewResult(lineInfoTag) {
636
- // 1. 从标签中提取 JSON 内容
672
+ // 从标签中提取 JSON 内容
637
673
  const jsonContent = lineInfoTag
638
674
  .replace(/<LINE_INFO>\s*/g, '')
639
675
  .replace(/\s*<\/LINE_INFO>/g, '')
640
676
  .trim();
641
677
 
642
- // 2. 空数组处理 → 使用后备方案
643
678
  if (jsonContent === '[]') {
644
- debugLog('LINE_INFO 为空数组,使用 diff 块起始行号作为后备方案');
645
- return null;
679
+ return null; // 空数组使用后备方案
646
680
  }
647
681
 
648
- // 3. 解析 JSON 数组
649
682
  const lineInfoArray = JSON.parse(jsonContent);
683
+ // 返回第一个问题的行号信息
684
+ return lineInfoArray[0];
685
+ }
686
+ ```
687
+
688
+ **新增解析函数**(v2.4.6):
689
+ ```javascript
690
+ parseAllLineInfoFromReviewResult(lineInfoTag) {
691
+ // 解析 LINE_INFO 中的所有问题行号
692
+ const jsonContent = lineInfoTag
693
+ .replace(/<LINE_INFO>\s*/g, '')
694
+ .replace(/\s*<\/LINE_INFO>/g, '')
695
+ .trim();
650
696
 
651
- // 4. 返回第一个问题的行号信息
652
- if (Array.isArray(lineInfoArray) && lineInfoArray.length > 0) {
653
- const lineInfo = lineInfoArray[0];
654
- return {
655
- new_path: lineInfo.new_path,
656
- new_line: lineInfo.new_line
657
- };
658
- }
697
+ if (jsonContent === '[]') return [];
659
698
 
660
- return null; // 解析失败,使用后备方案
699
+ return JSON.parse(jsonContent);
661
700
  }
662
701
  ```
663
702
 
@@ -669,27 +708,7 @@ parseLineInfoFromReviewResult(lineInfoTag) {
669
708
  | 删除代码 (`-`) | 使用 `old_line` + `old_path` |
670
709
  | 上下文代码 | 使用 `new_line` + `new_path` |
671
710
 
672
- **行号验证逻辑**:
673
- ```
674
- ┌─────────────────────────────────────────────────────────────┐
675
- │ 行号验证流程 │
676
- ├─────────────────────────────────────────────────────────────┤
677
- │ │
678
- │ 1. 从 LINE_INFO 解析行号 → parsedLineInfo.new_line │
679
- │ ↓ │
680
- │ 2. 计算 diff 块范围 │
681
- │ new_start = line_info.new_start │
682
- │ new_end = new_start + new_count - 1 │
683
- │ ↓ │
684
- │ 3. 验证:parsedLineInfo.new_line ∈ [new_start, new_end] │
685
- │ ↓ │
686
- │ 4. 判断: │
687
- │ - 在范围内 → 使用解析行号 (精确) │
688
- │ - 越界 → 使用 diff 块起始行号 (后备) │
689
- └─────────────────────────────────────────────────────────────┘
690
- ```
691
-
692
- ### 4.7 评论发布策略
711
+ ### 4.7 评论发布策略(2.4.6 更新)
693
712
 
694
713
  **增量发布模式** (`index.js:174-183`):
695
714
  ```javascript
@@ -701,14 +720,93 @@ if (review_result.includes('严重问题')) {
701
720
  }
702
721
  ```
703
722
 
704
- **行级评论定位** (`index.js:529-552`):
723
+ **多问题评论发布**(v2.4.6 新增):
705
724
  ```javascript
706
- // 优先使用 LINE_INFO 解析的行号
707
- const parsedLineInfo = this.parseLineInfoFromReviewResult(lineInfoTag);
708
- if (parsedLineInfo.new_line diff 块范围内) {
709
- targetLine = parsedLineInfo;
710
- } else {
711
- targetLine = diff_block_start_line; // 后备方案
725
+ // 一个 diff 块有多个问题时,为每个问题发布单独的评论
726
+ for (let i = 0; i < allLineInfo.length; i++) {
727
+ const problemInfo = allLineInfo[i];
728
+ // 提取单个问题的报告内容
729
+ const singleProblemContent = this.extractSingleProblemReport(fullReportContent, i + 1);
730
+ ```
731
+
732
+ **版本信息缓存**(v2.4.8 新增):
733
+ ```javascript
734
+ // 构造函数中初始化缓存
735
+ constructor() {
736
+ this._versionCache = new Map(); // 缓存 MR 版本信息
737
+ }
738
+
739
+ // 获取版本信息(带缓存)
740
+ async getVersionInfo(projectId, mergeRequestIid) {
741
+ const cacheKey = `${projectId}-${mergeRequestIid}`;
742
+
743
+ // 检查缓存
744
+ if (this._versionCache.has(cacheKey)) {
745
+ return this._versionCache.get(cacheKey);
746
+ }
747
+
748
+ // 从 API 获取并缓存
749
+ const versionInfo = await this.getMergeRequestVersions(projectId, mergeRequestIid);
750
+ this._versionCache.set(cacheKey, versionInfo);
751
+ return versionInfo;
752
+ }
753
+ ```
754
+
755
+ **缓存优化效果**:
756
+ - **优化前**: 每个问题发布评论时都调用 `getMergeRequestVersions` API
757
+ - **优化后**: 同一个 MR 的版本信息只获取一次,后续直接使用缓存
758
+ - **适用场景**: 一个 diff 块有多个问题时,避免重复 API 调用
759
+
760
+ // 构建目标行号
761
+ let targetLine;
762
+ if (problemInfo.old_line && !problemInfo.new_line) {
763
+ targetLine = { old_path: problemInfo.old_path, old_line: problemInfo.old_line };
764
+ } else if (problemInfo.new_line) {
765
+ // AI 返回的是文件中的绝对行号,直接使用
766
+ targetLine = { new_path: problemInfo.new_path, new_line: problemInfo.new_line };
767
+ }
768
+
769
+ // 发布评论到对应行
770
+ await this.createDiffDiscussion(projectId, mergeRequestIid, {
771
+ body: singleProblemContent,
772
+ position: { ...targetLine, ...shaInfo }
773
+ });
774
+ }
775
+ ```
776
+
777
+ **行级评论定位**(v2.4.7 更新):
778
+ ```javascript
779
+ // v2.4.7:直接使用 AI 返回的绝对行号,不再验证范围
780
+ if (problemInfo.new_line) {
781
+ // AI 返回的是文件中的绝对行号,直接使用
782
+ targetLine = {
783
+ new_path: problemInfo.new_path,
784
+ new_line: problemInfo.new_line
785
+ };
786
+ }
787
+ ```
788
+
789
+ **报告切分方法**(v2.4.6 新增):
790
+ ```javascript
791
+ extractSingleProblemReport(fullReport, problemIndex) {
792
+ // 使用 **问题 N**:模式来切分问题
793
+ const problemBlocks = [];
794
+ let currentBlock = [];
795
+
796
+ for (const line of lines) {
797
+ const problemMatch = line.match(/\*\*问题\s*(\d+)\*\*/);
798
+ if (problemMatch) {
799
+ if (currentBlock.length > 0) {
800
+ problemBlocks.push(currentBlock.join('\n'));
801
+ currentBlock = [];
802
+ }
803
+ currentBlock.push(line);
804
+ } else {
805
+ currentBlock.push(line);
806
+ }
807
+ }
808
+
809
+ return problemBlocks[problemIndex - 1];
712
810
  }
713
811
  ```
714
812
 
@@ -1288,6 +1386,79 @@ do {
1288
1386
  - 无问题的块跳过评论发布
1289
1387
  - 减少无效 API 调用
1290
1388
 
1389
+ ### 11.4 性能指标收集(v2.4.8 新增)
1390
+
1391
+ **指标收集器位置**: `utils.js:MetricsCollector`
1392
+
1393
+ **收集的指标类型**:
1394
+
1395
+ | 指标类型 | 具体内容 | 用途 |
1396
+ |----------|----------|------|
1397
+ | **审查统计** | 文件数、diff 块数、问题数、严重问题数、评论发布数 | 了解审查覆盖范围和问题分布 |
1398
+ | **耗时统计** | 总耗时、平均每块耗时、各块耗时明细 | 识别整体性能表现 |
1399
+ | **按 Diff 大小分析** | small (0-50 行)、medium (51-200 行)、large (200+ 行) 的块数和耗时 | 优化 diff 拆分策略,确定最佳并发粒度 |
1400
+ | **按文件类型分析** | 各文件类型(.java/.js/.vue 等)的块数和平均耗时 | 识别特定文件类型的性能特征 |
1401
+ | **慢调用分析** | 记录超过 10 秒的调用(文件、diff 大小、耗时) | 定位性能瓶颈,优化慢路径 |
1402
+ | **AI 调用统计** | 总调用数、成功数、失败数、重试数 | 评估 AI 服务稳定性,优化重试策略 |
1403
+ | **GitLab API 统计** | 总调用数、成功数、失败数 | 评估 GitLab API 可用性 |
1404
+
1405
+ **性能分析输出示例**:
1406
+
1407
+ ```
1408
+ ========== 审查统计 ==========
1409
+ 审查文件数:5
1410
+ 审查 diff 块数:12
1411
+ 发现问题总数:3
1412
+ 严重问题数:2
1413
+ 发布评论数:2
1414
+ 总耗时:45.32s
1415
+ 平均每块耗时:3.78s
1416
+ AI 调用:12/12 (成功率:100.0%)
1417
+ AI 重试次数:2
1418
+ GitLab API: 8/8
1419
+
1420
+ --- 按 Diff 块大小分析 ---
1421
+ small (0-50 行): 5 块,平均耗时 1.2s, 总耗时 6.0s
1422
+ medium (51-200 行): 5 块,平均耗时 3.5s, 总耗时 17.5s
1423
+ large (200+ 行): 2 块,平均耗时 10.9s, 总耗时 21.8s
1424
+
1425
+ --- 按文件类型分析 ---
1426
+ .java: 8 块,平均耗时 4.2s
1427
+ .js: 3 块,平均耗时 2.1s
1428
+ .vue: 1 块,平均耗时 1.5s
1429
+
1430
+ --- 慢调用分析 (>10 秒) ---
1431
+ 文件:src/service/UserService.java, Diff 大小:250 行,耗时:15.3s
1432
+ 文件:src/controller/UserController.java, Diff 大小:180 行,耗时:12.1s
1433
+ ===============================
1434
+ ```
1435
+
1436
+ **指标使用场景**:
1437
+
1438
+ 1. **优化 diff 拆分策略**:如果 large 块平均耗时远高于 small 块,可以考虑减小 diff 块大小阈值
1439
+ 2. **优化并发配置**:根据慢调用分析,调整 `GITLAB_CR_CONCURRENCY` 环境变量
1440
+ 3. **识别性能瓶颈**:通过慢调用分析定位耗时最长的文件类型和 diff 大小
1441
+ 4. **容量规划**:根据 diff 大小和耗时的关系,预估大规模代码审查的耗时
1442
+
1443
+ ### 11.5 日志分级(v2.4.8 新增)
1444
+
1445
+ **日志级别**: `DEBUG < INFO < WARN < ERROR`
1446
+
1447
+ **环境变量控制**:
1448
+ ```bash
1449
+ # 设置日志级别(默认:DEBUG)
1450
+ export LOG_LEVEL=0 # DEBUG:输出所有日志
1451
+ export LOG_LEVEL=1 # INFO:输出 INFO 及以上级别
1452
+ export LOG_LEVEL=2 # WARN:输出 WARN 及以上级别
1453
+ export LOG_LEVEL=3 # ERROR:只输出 ERROR
1454
+ ```
1455
+
1456
+ **日志函数**:
1457
+ - `debugLog(message)`: 调试信息(默认输出)
1458
+ - `infoLog(message)`: 关键里程碑(审查开始/结束、统计摘要)
1459
+ - `warnLog(message)`: 可恢复的问题
1460
+ - `errorLog(message)`: 需要关注的错误
1461
+
1291
1462
  ---
1292
1463
 
1293
1464
  ## 12. 扩展与维护
@@ -1323,11 +1494,13 @@ npm run dev
1323
1494
 
1324
1495
  | 版本 | 日期 | 变更说明 |
1325
1496
  |------|------|----------|
1326
- | 2.4.1 | 2026-04-08 | 修复 AI 审查结果为空时的返回值 |
1327
- ### 12.3 版本历史
1328
-
1329
- | 版本 | 日期 | 变更说明 |
1330
- |------|------|----------|
1497
+ | 2.4.9 | 2026-04-16 | **代码优化与性能指标收集**:(1) 删除已废弃的 `reviewDiffWithClaude` 方法,精简 79 行代码;(2) 添加 GitLab 版本信息缓存机制,避免重复 API 调用;(3) 实现日志分级系统(DEBUG/INFO/WARN/ERROR),支持通过 `LOG_LEVEL` 环境变量控制输出级别;(4) 实现完整的性能指标收集器 `MetricsCollector`,收集 diff 块大小/文件类型/耗时分布,慢调用分析 (>10 秒),AI/API 调用成功率,评论发布统计等,输出按 diff 大小(small/medium/large)和文件类型分组的性能分析,支持识别性能瓶颈和优化审查策略 |
1498
+ | 2.4.7 | 2026-04-15 | **行号计算逻辑重大修正**:修正 AI 行号计算方式,从使用 `New Start` 基准计算改为返回文件绝对行号;移除基于 diff 块范围的行号验证逻辑;更新 `code-review-rules.md` 和 `SKILL.md` 中的行号计算规范说明 |
1499
+ | 2.4.6 | 2026-04-15 | **多问题评论发布支持**:新增 `parseAllLineInfoFromReviewResult` 方法解析所有问题行号;新增 `extractSingleProblemReport` 方法切分单个问题报告;重构 `postSingleCommentToGitLab` 方法支持循环发布多条评论,每条评论定位到对应问题的行号 |
1500
+ | 2.4.5 | 2026-04-15 | **报告模板格式优化**:更新 `SKILL.md` 输出模板,使用 `**问题 N**:` 格式替代带圈数字序号;移除问题间的 `---` 分隔符,简化报告切分逻辑 |
1501
+ | 2.4.4 | 2026-04-15 | **标题验证逻辑修复**:修复 AI 审查结果标题验证逻辑,当有严重问题但标题不符合要求时重试,达到最大重试次数后返回最后一次结果 |
1502
+ | 2.4.3 | 2026-04-15 | **调试日志增强**:在 `getDiffBlocks` 方法中添加 diff 块解析日志,记录 `new_start` 和 `new_count` 值;添加文件拆分块数日志 |
1503
+ | 2.4.2 | 2026-04-15 | **元数据增强**:在临时 diff 文件中添加 `New Start` 和 `New Count` 元数据,供 AI 参考计算行号 |
1331
1504
  | 2.4.1 | 2026-04-08 | 修复 AI 审查结果为空时的返回值 |
1332
1505
  | 2.4.0 | 2026-03-25 | 更新 MR 审查模板 |
1333
1506
  | 2.3.8 | 2026-03-25 | 优化 AI 审核结果处理逻辑 |