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.
- package/index.js +50 -49
- package/package.json +1 -1
- 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
|
-
|
|
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
|
-
|
|
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}
|
|
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(
|
|
367
|
-
if (!
|
|
370
|
+
parseLineInfoFromReviewResult(lineInfoTag) {
|
|
371
|
+
if (!lineInfoTag) return null;
|
|
368
372
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
// 提取 JSON 内容并去除首尾空白字符
|
|
376
|
-
const lineInfoJson = match[1].trim();
|
|
377
|
+
debugLog(`解析 LINE_INFO 原始内容:${lineInfoTag}`);
|
|
378
|
+
debugLog(`解析 LINE_INFO JSON 内容:${jsonContent}`);
|
|
377
379
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
+
// 如果是空数组,返回 null 使用后备方案
|
|
381
|
+
if (jsonContent === '[]') {
|
|
382
|
+
debugLog('LINE_INFO 为空数组,使用 diff 块起始行号作为后备方案');
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
380
385
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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:
|
|
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,
|
|
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,
|
|
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
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
+
};
|