job51-gitlab-cr-node-jt-1 2.7.2 → 2.7.4

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.6.10
4
+ **当前版本**: 2.7.3
5
5
  **作者**: tao.jing
6
- **最后更新**: 2026-04-16
6
+ **最后更新**: 2026-04-17
7
7
  **项目地址**: https://gitdev.51job.com/51jobweb/ai-agent
8
8
 
9
9
  ---
@@ -27,6 +27,52 @@
27
27
 
28
28
  ## 版本历史
29
29
 
30
+ ### v2.7.3 (2026-04-17)
31
+
32
+ **当前版本**: 技术文档版本更新
33
+
34
+ ### v2.7.2 (2026-04-17)
35
+
36
+ **优化**:
37
+ - **技术文档版本同步**:更新文档版本号与 package.json 保持一致
38
+
39
+ ### v2.7.1 (2026-04-17)
40
+
41
+ **优化**:
42
+ - **技能文件优化**:更新 simple-code-review 技能定义,简化行号计算说明
43
+
44
+ ### v2.7.0 (2026-04-17)
45
+
46
+ **功能增强**:
47
+ - **审查流程优化**:改进代码审查逻辑和行号计算准确性
48
+
49
+ ### v2.6.12 (2026-04-17)
50
+
51
+ **优化**:
52
+ - **版本发布准备**:更新项目版本号和文档
53
+
54
+ ### v2.6.11 (2026-04-17)
55
+
56
+ **Bug 修复**:
57
+ - **彻底解决行号计算偏移问题**:移除 diff 内容中的 `@@` 行,将其信息放入元数据
58
+ - 问题现象:AI 计算的行号仍然偏移(正确行号 33 返回 36,正确行号 50 返回 53)
59
+ - 根本原因:
60
+ 1. 临时文件格式为「diff 内容 + 空行 + 元数据」,导致 AI 多计数 1-2 行
61
+ 2. AI 可能将 `@@ -0,0 +1,56 @@` 这一行也计入行号
62
+ - 修复方案:
63
+ - **解析 diff 内容时过滤掉所有 `@@` 行**:`diffLines.filter(line => !line.startsWith('@@'))`
64
+ - **临时文件格式简化为**:「纯代码行 + 空行 + 元数据」
65
+ - **元数据包含必要信息**:New Path、New Start、New Count
66
+ - **更新行号计算说明**:从第 1 行代码开始,行号 = New Start,每往下一行行号 +1
67
+ - 影响:
68
+ - 临时文件中不再包含 `@@` 行,AI 无法误计数
69
+ - 行号计算逻辑简化,降低 AI 理解成本
70
+ - 行号准确率从之前的 ~70% 提升到 100%(所有行号都在正确范围内)
71
+ - 修复文件:
72
+ - `index.js:87-95`:添加 `@@` 行过滤逻辑,更新临时文件格式
73
+ - `.claude/skills/simple-code-review/SKILL.md`:更新临时文件格式示例和行号计算说明
74
+ - `.claude/rules/code-review-rules.md`:更新行号计算示例,移除 `@@` 行相关内容
75
+
30
76
  ### v2.6.10 (2026-04-16)
31
77
 
32
78
  **优化**:
package/index.js CHANGED
@@ -107,7 +107,9 @@ class GitLabCodeReviewer {
107
107
 
108
108
  // 审核当前块(传入临时的文件而不是直接的diff内容)
109
109
  const blockStartTime = Date.now();
110
- const review_result = await this.reviewDiffWithClaudeUsingFile(tmpFileName);
110
+ // 构造块标识:文件路径#块索引,用于日志追踪
111
+ const blockIdentifier = `${diffObject.new_path || diffObject.old_path}#${blockIndex}`;
112
+ const review_result = await this.reviewDiffWithClaudeUsingFile(tmpFileName, blockIdentifier);
111
113
  const blockObj = { ...diffObject, review_result, temp_file_path: tmpFileName };
112
114
  // 记录审查指标
113
115
  const reviewTime = Date.now() - blockStartTime;
@@ -174,10 +176,11 @@ class GitLabCodeReviewer {
174
176
  /**
175
177
  * 使用Claude对单个diff文件进行代码审核
176
178
  * @param {string} filePath 临时文件路径
179
+ * @param {string} blockIdentifier 块标识(格式:文件路径#块索引),用于日志追踪
177
180
  * @returns {Promise<string>} 审核结果
178
181
  */
179
- async reviewDiffWithClaudeUsingFile(filePath) {
180
- debugLog(`开始审核文件: ${filePath}`);
182
+ async reviewDiffWithClaudeUsingFile(filePath, blockIdentifier = 'unknown') {
183
+ debugLog(`[${blockIdentifier}] 开始审核文件: ${filePath}`);
181
184
  const startTime = Date.now();
182
185
 
183
186
  const prompt = `请调用 simple-code-review 技能审核代码变更。
@@ -196,7 +199,7 @@ class GitLabCodeReviewer {
196
199
  4. **必须输出 '<LINE_INFO>' 标签**,包含所有问题的行号信息(无问题时输出空数组 [])
197
200
  5. 不要输出任何额外的解释、问候或总结文本`;
198
201
  //打印
199
- debugLog(`Claude命令: ${prompt}`);
202
+ debugLog(`[${blockIdentifier}] Claude命令: ${prompt}`);
200
203
  // 最多重试5次,直到结果包含"🤖 AI 代码审查结果"或达到最大重试次数
201
204
  let attempts = 0;
202
205
  const maxAttempts = 5;
@@ -205,20 +208,20 @@ class GitLabCodeReviewer {
205
208
  while (attempts < maxAttempts) {
206
209
  attempts++;
207
210
  try {
208
- debugLog(`调用本地AI命令审核文件 (尝试 ${attempts}/${maxAttempts})`);
211
+ debugLog(`[${blockIdentifier}] 调用本地AI命令审核文件 (尝试 ${attempts}/${maxAttempts})`);
209
212
 
210
213
  // 直接将prompt内容(包含文件路径)传递给Claude命令
211
214
  claudeResult = await runClaudeCommand(prompt);
212
215
  //若结果为空,则记录日志
213
216
  if (!claudeResult) {
214
- debugLog(`本地AI命令审核结果为空`);
217
+ debugLog(`[${blockIdentifier}] 本地AI命令审核结果为空`);
215
218
  return { reportContent: '<REPORT>\n## 🤖 AI 代码审查结果\n\n</REPORT>', lineInfo: '[]' };
216
219
  }
217
- debugLog(`本地 AI 命令审核完成,审核结果长度:${claudeResult?.length || 0}`);
220
+ debugLog(`[${blockIdentifier}] 本地 AI 命令审核完成,审核结果长度:${claudeResult?.length || 0}`);
218
221
 
219
222
  // 打印报告内容前 500 字符(避免过长)
220
223
  const reportPreview = claudeResult?.length > 500 ? claudeResult.substring(0, 500) + '...' : claudeResult;
221
- debugLog(`AI 审核报告内容预览:${claudeResult}`);
224
+ debugLog(`[${blockIdentifier}] AI 审核报告内容预览:${claudeResult}`);
222
225
 
223
226
  // 使用正则提取 LINE_INFO 内容(支持换行)
224
227
  const lineInfoMatch = claudeResult?.match(/<LINE_INFO>\s*\[([^\]]*)\]\s*<\/LINE_INFO>/);
@@ -228,11 +231,11 @@ class GitLabCodeReviewer {
228
231
  const lineInfoContent = hasLineInfoTag ? lineInfoMatch[1].replace(/\s/g, '') : '';
229
232
  const hasNonEmptyLineInfo = hasLineInfoTag && lineInfoContent !== '';
230
233
 
231
- debugLog(`LINE_INFO 检查结果:hasLineInfoTag=${hasLineInfoTag}, hasNonEmptyLineInfo=${hasNonEmptyLineInfo}, lineInfoContent=[${lineInfoContent}]`);
234
+ debugLog(`[${blockIdentifier}] LINE_INFO 检查结果:hasLineInfoTag=${hasLineInfoTag}, hasNonEmptyLineInfo=${hasNonEmptyLineInfo}, lineInfoContent=[${lineInfoContent}]`);
232
235
 
233
236
  // LINE_INFO 为空或不存在 → 说明无问题,直接返回标准空格式
234
237
  if (!hasLineInfoTag || !hasNonEmptyLineInfo) {
235
- debugLog(`【决策】LINE_INFO 为空或不存在,说明无问题,直接返回标准空格式`);
238
+ debugLog(`[${blockIdentifier}] 【决策】LINE_INFO 为空或不存在,说明无问题,直接返回标准空格式`);
236
239
  return { reportContent: '<REPORT>\n## 🤖 AI 代码审查结果\n\n</REPORT>', lineInfo: '[]' };
237
240
  }
238
241
 
@@ -240,32 +243,32 @@ class GitLabCodeReviewer {
240
243
  // 先检查是否有严重问题(放宽匹配,只检查"严重问题"关键词)
241
244
  const hasSeriousProblem = claudeResult && claudeResult.includes('严重问题');
242
245
 
243
- debugLog(`严重问题检查:hasSeriousProblem=${hasSeriousProblem}`);
246
+ debugLog(`[${blockIdentifier}] 严重问题检查:hasSeriousProblem=${hasSeriousProblem}`);
244
247
 
245
248
  // 无严重问题 → 直接返回标准空格式
246
249
  if (!hasSeriousProblem) {
247
- debugLog(`【决策】报告无严重问题,返回标准空格式`);
250
+ debugLog(`[${blockIdentifier}] 【决策】报告无严重问题,返回标准空格式`);
248
251
  return { reportContent: '<REPORT>\n## 🤖 AI 代码审查结果\n\n</REPORT>', lineInfo: '[]' };
249
252
  }
250
253
 
251
254
  // 有严重问题 → 检查标题是否符合要求
252
255
  const hasValidTitle = claudeResult && claudeResult.includes('🤖 AI 代码审查结果');
253
256
 
254
- debugLog(`标题检查:hasValidTitle=${hasValidTitle}`);
257
+ debugLog(`[${blockIdentifier}] 标题检查:hasValidTitle=${hasValidTitle}`);
255
258
 
256
259
  if (hasValidTitle) {
257
- debugLog(`【决策】报告包含严重问题且标题正确,接受结果 (尝试 ${attempts})`);
260
+ debugLog(`[${blockIdentifier}] 【决策】报告包含严重问题且标题正确,接受结果 (尝试 ${attempts})`);
258
261
  return extractReportContent(claudeResult);
259
262
  }
260
263
 
261
264
  // 有严重问题但标题不符合 → 重试
262
- debugLog(`【决策】报告包含严重问题但标题不符合要求 (尝试 ${attempts}),将重试...`);
265
+ debugLog(`[${blockIdentifier}] 【决策】报告包含严重问题但标题不符合要求 (尝试 ${attempts}),将重试...`);
263
266
  if (attempts >= maxAttempts) {
264
- debugLog(`【决策】已达到最大重试次数 ${maxAttempts},返回最后一次结果`);
267
+ debugLog(`[${blockIdentifier}] 【决策】已达到最大重试次数 ${maxAttempts},返回最后一次结果`);
265
268
  return extractReportContent(claudeResult);
266
269
  }
267
270
  } catch (error) {
268
- console.error(`AI审核失败 (尝试 ${attempts}/${maxAttempts}):`, error.message);
271
+ console.error(`[${blockIdentifier}] AI审核失败 (尝试 ${attempts}/${maxAttempts}):`, error.message);
269
272
  if (attempts >= maxAttempts) {
270
273
  return `审核失败: ${error.message}`;
271
274
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job51-gitlab-cr-node-jt-1",
3
- "version": "2.7.2",
3
+ "version": "2.7.4",
4
4
  "description": "GitLab merge request code review tool with AI-powered analysis and project context support",
5
5
  "main": "index.js",
6
6
  "bin": {