job51-gitlab-cr-node-skill-prompt-optimize 1.5.0 → 1.5.2

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.
Files changed (3) hide show
  1. package/index.js +50 -49
  2. package/package.json +1 -1
  3. package/utils.js +74 -67
package/index.js CHANGED
@@ -78,6 +78,7 @@ class GitLabCodeReviewer {
78
78
 
79
79
 
80
80
  // 提取REPORT标签内容并返回
81
+ // 提取 REPORT 标签和 LINE_INFO 标签内容并返回对象
81
82
  return extractReportContent(claudeResult);
82
83
  } else {
83
84
  debugLog(`AI审核结果不包含"🤖 AI 代码审查结果" (尝试 ${attempts}),将重试...`);
@@ -85,7 +86,8 @@ class GitLabCodeReviewer {
85
86
  debugLog(`已达到最大重试次数 ${maxAttempts},返回最后一次结果,${claudeResult}`);
86
87
 
87
88
  // 提取REPORT标签内容并返回
88
- return extractReportContent(claudeResult);
89
+ // 提取 REPORT 标签和 LINE_INFO 标签内容并返回对象
90
+ return extractReportContent(claudeResult);
89
91
  }
90
92
  }
91
93
  } catch (error) {
@@ -138,7 +140,7 @@ ${diffObject.diff}`;
138
140
  const blockObj = { ...diffObject, review_result, temp_file_path: tmpFileName };
139
141
 
140
142
  // 检查审查结果中是否包含严重问题,只有包含严重问题才发布评论
141
- if (blockObj.review_result && blockObj.review_result.includes('🔴 严重问题')) {
143
+ if (blockObj.review_result && blockObj.review_result.reportContent && blockObj.review_result.reportContent.includes('🔴 严重问题')) {
142
144
  // 立即发布评论
143
145
  await this.postSingleCommentToGitLab(projectId, mergeRequestIid, {
144
146
  diff_info: blockObj,
@@ -235,6 +237,7 @@ ${diffObject.diff}`;
235
237
  debugLog(`AI审核成功,包含"🤖 AI 代码审查结果" (尝试 ${attempts})`);
236
238
 
237
239
  // 提取REPORT标签内容并返回
240
+ // 提取 REPORT 标签和 LINE_INFO 标签内容并返回对象
238
241
  return extractReportContent(claudeResult);
239
242
  } else {
240
243
  debugLog(`AI审核结果不包含"🤖 AI 代码审查结果" (尝试 ${attempts}),将重试...`);
@@ -242,7 +245,8 @@ ${diffObject.diff}`;
242
245
  debugLog(`已达到最大重试次数 ${maxAttempts},返回最后一次结果,${claudeResult}`);
243
246
 
244
247
  // 提取REPORT标签内容并返回
245
- return extractReportContent(claudeResult);
248
+ // 提取 REPORT 标签和 LINE_INFO 标签内容并返回对象
249
+ return extractReportContent(claudeResult);
246
250
  }
247
251
  }
248
252
  } catch (error) {
@@ -359,55 +363,49 @@ ${diffObject.diff}`;
359
363
  }
360
364
 
361
365
  /**
362
- * 从 LINE_INFO 标签中解析行号信息
363
- * @param {string} reviewResult 审查结果文本
366
+ * 从 LINE_INFO 标签字符串中解析行号信息
367
+ * @param {string} lineInfoTag LINE_INFO 标签字符串,如 "<LINE_INFO>[...]</LINE_INFO>"
364
368
  * @returns {Object|null} 行号信息对象 {new_path, new_line, old_path, old_line} 或 null
365
369
  */
366
- parseLineInfoFromReviewResult(reviewResult) {
367
- if (!reviewResult) return null;
370
+ parseLineInfoFromReviewResult(lineInfoTag) {
371
+ if (!lineInfoTag) return null;
368
372
 
369
- // 解析<LINE_INFO>[{"new_path":"xxx","new_line":87,"old_path":"yyy","old_line":85}]</LINE_INFO>格式
370
- const lineInfoRegex = /<LINE_INFO>\s*([\s\S]*?)\s*<\/LINE_INFO>/;
371
- const match = reviewResult.match(lineInfoRegex);
373
+ try {
374
+ // 从标签中提取 JSON 内容:<LINE_INFO>[{...}]</LINE_INFO>
375
+ const jsonContent = lineInfoTag.replace(/<LINE_INFO>\s*/g, '').replace(/\s*<\/LINE_INFO>/g, '').trim();
372
376
 
373
- if (match) {
374
- try {
375
- // 提取 JSON 内容并去除首尾空白字符
376
- const lineInfoJson = match[1].trim();
377
+ debugLog(`解析 LINE_INFO 原始内容:${lineInfoTag}`);
378
+ debugLog(`解析 LINE_INFO JSON 内容:${jsonContent}`);
377
379
 
378
- debugLog(`解析 LINE_INFO 原始内容:${match[1]}`);
379
- debugLog(`解析 LINE_INFO 处理后内容:${lineInfoJson}`);
380
+ // 如果是空数组,返回 null 使用后备方案
381
+ if (jsonContent === '[]') {
382
+ debugLog('LINE_INFO 为空数组,使用 diff 块起始行号作为后备方案');
383
+ return null;
384
+ }
380
385
 
381
- // 如果是空数组,返回 null 使用后备方案
382
- if (lineInfoJson === '[]') {
383
- debugLog('LINE_INFO 为空数组,使用 diff 块起始行号作为后备方案');
384
- return null;
386
+ const lineInfoArray = JSON.parse(jsonContent);
387
+ debugLog(`解析 LINE_INFO JSON 成功:${JSON.stringify(lineInfoArray)}`);
388
+
389
+ if (Array.isArray(lineInfoArray) && lineInfoArray.length > 0) {
390
+ debugLog(`从 LINE_INFO 中解析出行号信息:${JSON.stringify(lineInfoArray)}`);
391
+ // 返回第一个问题的行号信息(后续可扩展为支持多个问题分别评论)
392
+ const lineInfo = lineInfoArray[0];
393
+ // 构建符合 GitLab API 的 position 对象
394
+ const position = {
395
+ new_path: lineInfo.new_path,
396
+ new_line: lineInfo.new_line
397
+ };
398
+ // 可选字段:如果存在 old_path 和 old_line 也加入
399
+ if (lineInfo.old_path) {
400
+ position.old_path = lineInfo.old_path;
385
401
  }
386
-
387
- const lineInfoArray = JSON.parse(lineInfoJson);
388
- debugLog(`解析 LINE_INFO JSON 成功:${JSON.stringify(lineInfoArray)}`);
389
-
390
- if (Array.isArray(lineInfoArray) && lineInfoArray.length > 0) {
391
- debugLog(`从 LINE_INFO 中解析出行号信息:${JSON.stringify(lineInfoArray)}`);
392
- // 返回第一个问题的行号信息(后续可扩展为支持多个问题分别评论)
393
- const lineInfo = lineInfoArray[0];
394
- // 构建符合 GitLab API 的 position 对象
395
- const position = {
396
- new_path: lineInfo.new_path,
397
- new_line: lineInfo.new_line
398
- };
399
- // 可选字段:如果存在 old_path 和 old_line 也加入
400
- if (lineInfo.old_path) {
401
- position.old_path = lineInfo.old_path;
402
- }
403
- if (lineInfo.old_line) {
404
- position.old_line = lineInfo.old_line;
405
- }
406
- return position;
402
+ if (lineInfo.old_line) {
403
+ position.old_line = lineInfo.old_line;
407
404
  }
408
- } catch (error) {
409
- debugLog(`解析 LINE_INFO JSON 失败:${error.message}`);
405
+ return position;
410
406
  }
407
+ } catch (error) {
408
+ debugLog(`解析 LINE_INFO JSON 失败:${error.message}`);
411
409
  }
412
410
 
413
411
  debugLog('无法从 LINE_INFO 中解析行号,将使用 diff 块起始行号作为后备方案');
@@ -442,6 +440,9 @@ ${diffObject.diff}`;
442
440
  async postSingleCommentToGitLab(projectId, mergeRequestIid, result) {
443
441
  try {
444
442
  const { diff_info, review_result, block_index } = result;
443
+ // review_result 现在是对象:{ reportContent, lineInfo }
444
+ const reviewContent = review_result?.reportContent || '';
445
+ const lineInfoTag = review_result?.lineInfo || null;
445
446
  const file_path = diff_info.new_path || diff_info.old_path;
446
447
  const file_path_with_line = `${file_path}#L${block_index}`;
447
448
 
@@ -461,9 +462,9 @@ ${diffObject.diff}`;
461
462
  // 直接使用diff_info中的line_info
462
463
  const lineInfo = diff_info.line_info;
463
464
 
464
- if (!lineInfo) {
465
+ if (!lineInfo && !lineInfoTag) {
465
466
  // 如果没有行号信息,创建一般讨论
466
- await this.createGeneralDiscussion(projectId, mergeRequestIid, file_path_with_line, review_result);
467
+ await this.createGeneralDiscussion(projectId, mergeRequestIid, file_path_with_line, reviewContent);
467
468
  debugLog(`评论已发布到文件 ${file_path_with_line} (无法解析行号)`);
468
469
  return;
469
470
  }
@@ -476,7 +477,7 @@ ${diffObject.diff}`;
476
477
  : { new_line: lineInfo.new_start, new_path: diff_info.new_path, old_line: lineInfo.old_start, old_path: diff_info.old_path };
477
478
 
478
479
  // 尝试从审查结果中解析更精确的行号信息(从 LINE_INFO 标签)
479
- const parsedLineInfo = this.parseLineInfoFromReviewResult(review_result);
480
+ const parsedLineInfo = lineInfoTag ? this.parseLineInfoFromReviewResult(lineInfoTag) : null;
480
481
  if (parsedLineInfo) {
481
482
  debugLog(`从 LINE_INFO 中解析出行号成功,使用解析后的行号覆盖 diff 块起始行号`);
482
483
  targetLine = parsedLineInfo;
@@ -488,7 +489,7 @@ ${diffObject.diff}`;
488
489
  if (targetLine) {
489
490
  // 创建diff评论
490
491
  const payload = {
491
- body: review_result,
492
+ body: reviewContent,
492
493
  position: {
493
494
  position_type: 'text',
494
495
  base_sha: baseSha,
@@ -503,12 +504,12 @@ ${diffObject.diff}`;
503
504
  debugLog(`评论已发布到文件 ${file_path_with_line} 的相关变更区域`);
504
505
  } catch (error) {
505
506
  console.error(`发布评论到文件 ${file_path_with_line} 的变更区域失败,改用一般讨论:`, error.message);
506
- await this.createGeneralDiscussion(projectId, mergeRequestIid, file_path_with_line, review_result);
507
+ await this.createGeneralDiscussion(projectId, mergeRequestIid, file_path_with_line, reviewContent);
507
508
  debugLog(`评论已发布到文件 ${file_path_with_line} (作为一般讨论)`);
508
509
  }
509
510
  } else {
510
511
  // 如果没有找到任何行号,创建一般讨论
511
- await this.createGeneralDiscussion(projectId, mergeRequestIid, file_path_with_line, review_result);
512
+ await this.createGeneralDiscussion(projectId, mergeRequestIid, file_path_with_line, reviewContent);
512
513
  debugLog(`评论已发布到文件 ${file_path_with_line} (无特定行号)`);
513
514
  }
514
515
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job51-gitlab-cr-node-skill-prompt-optimize",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
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": {
package/utils.js CHANGED
@@ -1,67 +1,74 @@
1
- /**
2
- * 工具函数集合
3
- */
4
-
5
- const axios = require('axios');
6
-
7
- // 调试日志函数
8
- function debugLog(message) {
9
- console.log('[DEBUG]', new Date().toISOString(), message);
10
- }
11
-
12
- /**
13
- * 提取REPORT标签之间的内容
14
- * @param {string} text 包含REPORT标签的文本
15
- * @returns {string} 提取后的内容
16
- */
17
- function extractReportContent(text) {
18
- const reportRegex = /<REPORT>([\s\S]*?)<\/REPORT>/;
19
- const match = text.match(reportRegex);
20
- return match ? match[1].trim() : text;
21
- }
22
-
23
- // 创建一个可配置的GitLab API客户端
24
- class GitLabAPIClient {
25
- constructor(gitlabToken, gitlabUrl = null) {
26
- this.gitlabToken = gitlabToken;
27
- this.gitlabUrl = gitlabUrl || 'https://gitdev.51job.com';
28
- this.axiosInstance = axios.create({
29
- headers: {
30
- 'PRIVATE-TOKEN': this.gitlabToken,
31
- },
32
- });
33
- }
34
-
35
- /**
36
- * 通用的GitLab API调用函数
37
- * @param {string} endpoint API端点
38
- * @param {Object} options 额外选项(method, data等)
39
- * @returns {Promise} API调用结果
40
- */
41
- async callGitLabAPI(endpoint, options = {}) {
42
- const { method = 'GET', data = null, headers = {} } = options;
43
-
44
- try {
45
- const response = await this.axiosInstance({
46
- method,
47
- url: `${this.gitlabUrl}${endpoint}`,
48
- headers: {
49
- 'Content-Type': 'application/json',
50
- ...headers
51
- },
52
- data
53
- });
54
-
55
- return response.data;
56
- } catch (error) {
57
- console.error(`GitLab API调用失败: ${method} ${endpoint}`, error.message);
58
- throw error;
59
- }
60
- }
61
- }
62
-
63
- module.exports = {
64
- GitLabAPIClient,
65
- debugLog,
66
- extractReportContent
67
- };
1
+ /**
2
+ * 工具函数集合
3
+ */
4
+
5
+ const axios = require('axios');
6
+
7
+ // 调试日志函数
8
+ function debugLog(message) {
9
+ console.log('[DEBUG]', new Date().toISOString(), message);
10
+ }
11
+
12
+ /**
13
+ * 提取 REPORT 标签和 LINE_INFO 标签的内容
14
+ * @param {string} text 包含 REPORT 和 LINE_INFO 标签的文本
15
+ * @returns {{reportContent: string, lineInfo: string|null}} 包含 REPORT 内容和 LINE_INFO 标签的对象
16
+ */
17
+ function extractReportContent(text) {
18
+ const reportRegex = /<REPORT>([\s\S]*?)<\/REPORT>/;
19
+ const lineInfoRegex = /<LINE_INFO>[\s\S]*?<\/LINE_INFO>/;
20
+
21
+ const reportMatch = text.match(reportRegex);
22
+ const lineInfoMatch = text.match(lineInfoRegex);
23
+
24
+ const reportContent = reportMatch ? reportMatch[1].trim() : text;
25
+ const lineInfo = lineInfoMatch ? lineInfoMatch[0] : null;
26
+
27
+ return { reportContent, lineInfo };
28
+ }
29
+
30
+ // 创建一个可配置的 GitLab API 客户端
31
+ class GitLabAPIClient {
32
+ constructor(gitlabToken, gitlabUrl = null) {
33
+ this.gitlabToken = gitlabToken;
34
+ this.gitlabUrl = gitlabUrl || 'https://gitdev.51job.com';
35
+ this.axiosInstance = axios.create({
36
+ headers: {
37
+ 'PRIVATE-TOKEN': this.gitlabToken,
38
+ },
39
+ });
40
+ }
41
+
42
+ /**
43
+ * 通用的 GitLab API 调用函数
44
+ * @param {string} endpoint API 端点
45
+ * @param {Object} options 额外选项(method, data 等)
46
+ * @returns {Promise} API 调用结果
47
+ */
48
+ async callGitLabAPI(endpoint, options = {}) {
49
+ const { method = 'GET', data = null, headers = {} } = options;
50
+
51
+ try {
52
+ const response = await this.axiosInstance({
53
+ method,
54
+ url: `${this.gitlabUrl}${endpoint}`,
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ ...headers
58
+ },
59
+ data
60
+ });
61
+
62
+ return response.data;
63
+ } catch (error) {
64
+ console.error(`GitLab API 调用失败:${method} ${endpoint}`, error.message);
65
+ throw error;
66
+ }
67
+ }
68
+ }
69
+
70
+ module.exports = {
71
+ GitLabAPIClient,
72
+ debugLog,
73
+ extractReportContent
74
+ };