gitlab-ai-review 4.2.3 → 6.3.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.
package/lib/config.js CHANGED
@@ -9,7 +9,161 @@ import { analyzeProject } from './project-analyzer.js';
9
9
  import { buildGenerateGuardPromptMessages } from './prompt-tools.js';
10
10
 
11
11
  /**
12
- * 使用 AI 生成审核提示词
12
+ * 🔧 AI 返回内容中提取增强后的 reviewguard.md 内容
13
+ * 查找 ===ENHANCED_CONTENT_START=== 和 ===ENHANCED_CONTENT_END=== 之间的内容
14
+ * @param {string} content - AI 返回的原始内容
15
+ * @returns {string|null} 提取的增强内容,如果没找到则返回 null
16
+ */
17
+ function extractEnhancedContent(content) {
18
+ if (!content || typeof content !== 'string') {
19
+ return null;
20
+ }
21
+
22
+ // 查找特殊标记
23
+ const startMarker = '===ENHANCED_CONTENT_START===';
24
+ const endMarker = '===ENHANCED_CONTENT_END===';
25
+
26
+ const startIdx = content.indexOf(startMarker);
27
+ const endIdx = content.indexOf(endMarker);
28
+
29
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
30
+ const enhancedContent = content.substring(startIdx + startMarker.length, endIdx).trim();
31
+ if (enhancedContent.length > 0) {
32
+ return enhancedContent;
33
+ }
34
+ }
35
+
36
+ // 备用方案:查找 ```markdown ... ``` 块(可能是完整的增强文档)
37
+ const markdownBlockMatch = content.match(/```markdown\s*([\s\S]*?)\s*```/);
38
+ if (markdownBlockMatch && markdownBlockMatch[1].includes('# ')) {
39
+ const possibleContent = markdownBlockMatch[1].trim();
40
+ // 检查是否看起来像完整的 reviewguard.md(包含标题和多个章节)
41
+ if (possibleContent.length > 500 && (possibleContent.match(/^##/gm) || []).length >= 3) {
42
+ return possibleContent;
43
+ }
44
+ }
45
+
46
+ return null;
47
+ }
48
+
49
+ /**
50
+ * 🔧 解析 AI 返回的 JSON 响应(健壮版本)
51
+ * 处理各种格式问题:markdown 代码块、转义字符、不完整的 JSON 等
52
+ * @param {string} content - AI 返回的原始内容
53
+ * @returns {Object} 解析后的 JSON 对象
54
+ */
55
+ function parseAIJsonResponse(content) {
56
+ if (!content || typeof content !== 'string') {
57
+ throw new Error('无效的输入内容');
58
+ }
59
+
60
+ let jsonStr = content.trim();
61
+
62
+ // 策略 1: 提取 ```json ... ``` 代码块
63
+ const jsonBlockMatch = jsonStr.match(/```json\s*([\s\S]*?)\s*```/);
64
+ if (jsonBlockMatch) {
65
+ jsonStr = jsonBlockMatch[1].trim();
66
+ }
67
+
68
+ // 策略 2: 如果还有其他代码块标记,移除它们
69
+ if (jsonStr.startsWith('```') && jsonStr.endsWith('```')) {
70
+ jsonStr = jsonStr.slice(3, -3).trim();
71
+ // 移除可能的语言标识
72
+ if (jsonStr.startsWith('json')) {
73
+ jsonStr = jsonStr.slice(4).trim();
74
+ }
75
+ }
76
+
77
+ // 策略 3: 尝试找到 JSON 对象的边界
78
+ const firstBrace = jsonStr.indexOf('{');
79
+ const lastBrace = jsonStr.lastIndexOf('}');
80
+ if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
81
+ jsonStr = jsonStr.substring(firstBrace, lastBrace + 1);
82
+ }
83
+
84
+ // 策略 4: 处理 enhancedContent 中的特殊字符
85
+ // 由于 enhancedContent 可能包含 markdown,需要特殊处理
86
+ try {
87
+ return JSON.parse(jsonStr);
88
+ } catch (firstError) {
89
+ // 策略 5: 尝试修复常见的 JSON 问题
90
+
91
+ // 5.1: 处理未转义的换行符
92
+ // 在 JSON 字符串值中,换行符应该是 \n 而不是实际的换行
93
+ // 但这很难处理,因为我们不知道哪些换行符在字符串值内
94
+
95
+ // 5.2: 尝试提取关键字段
96
+ const isCompleteMatch = jsonStr.match(/"isComplete"\s*:\s*(true|false)/);
97
+ const reasonMatch = jsonStr.match(/"reason"\s*:\s*"([^"]*(?:\\.[^"]*)*)"/);
98
+
99
+ if (isCompleteMatch) {
100
+ const isComplete = isCompleteMatch[1] === 'true';
101
+ const reason = reasonMatch ? reasonMatch[1].replace(/\\"/g, '"') : '无法提取原因';
102
+
103
+ // 如果不完整,尝试提取 enhancedContent
104
+ let enhancedContent = null;
105
+ if (!isComplete) {
106
+ // 尝试找到 enhancedContent 的开始位置
107
+ const enhancedMatch = jsonStr.match(/"enhancedContent"\s*:\s*"/);
108
+ if (enhancedMatch) {
109
+ const startIdx = jsonStr.indexOf(enhancedMatch[0]) + enhancedMatch[0].length;
110
+ // 从这个位置开始,找到结束引号(考虑转义)
111
+ let endIdx = startIdx;
112
+ let inEscape = false;
113
+ for (let i = startIdx; i < jsonStr.length; i++) {
114
+ if (inEscape) {
115
+ inEscape = false;
116
+ continue;
117
+ }
118
+ if (jsonStr[i] === '\\') {
119
+ inEscape = true;
120
+ continue;
121
+ }
122
+ if (jsonStr[i] === '"') {
123
+ endIdx = i;
124
+ break;
125
+ }
126
+ }
127
+ if (endIdx > startIdx) {
128
+ enhancedContent = jsonStr.substring(startIdx, endIdx)
129
+ .replace(/\\n/g, '\n')
130
+ .replace(/\\"/g, '"')
131
+ .replace(/\\\\/g, '\\');
132
+ }
133
+ }
134
+ }
135
+
136
+ // 提取 missingItems
137
+ let missingItems = [];
138
+ const missingMatch = jsonStr.match(/"missingItems"\s*:\s*\[([\s\S]*?)\]/);
139
+ if (missingMatch) {
140
+ try {
141
+ missingItems = JSON.parse(`[${missingMatch[1]}]`);
142
+ } catch (e) {
143
+ // 尝试简单提取
144
+ const items = missingMatch[1].match(/"([^"]+)"/g);
145
+ if (items) {
146
+ missingItems = items.map(s => s.replace(/"/g, ''));
147
+ }
148
+ }
149
+ }
150
+
151
+ console.log('📋 使用备用解析策略提取了关键字段');
152
+ return {
153
+ isComplete,
154
+ reason,
155
+ missingItems,
156
+ enhancedContent,
157
+ };
158
+ }
159
+
160
+ // 如果所有策略都失败了,抛出原始错误
161
+ throw new Error(`JSON 解析失败: ${firstError.message}`);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * 使用 AI 生成审核提示词(本地模式 - 读取本地文件)
13
167
  * @param {Object} aiClient - AI 客户端
14
168
  * @returns {Promise<Object>} 生成的配置
15
169
  */
@@ -24,22 +178,21 @@ export async function generateAIReviewGuardConfig(aiClient) {
24
178
  const messages = buildGenerateGuardPromptMessages(projectAnalysis);
25
179
 
26
180
  console.log('🔄 正在请求 AI 生成审核规则文档...\n');
181
+ console.log('📥 AI 实时响应:');
182
+ console.log('-'.repeat(60));
27
183
 
28
- // 调用 AI 生成
29
- const response = await aiClient.sendMessage(messages);
184
+ // 🎯 使用流式调用 AI 生成
185
+ const response = await aiClient.sendMessageStream(messages, (chunk) => {
186
+ if (chunk.content) {
187
+ process.stdout.write(chunk.content);
188
+ }
189
+ });
30
190
 
191
+ console.log('\n' + '-'.repeat(60));
31
192
  const generatedContent = response.content;
32
193
 
33
194
  console.log('✅ AI 已生成审核规则文档\n');
34
195
 
35
- // 可选:保存到项目根目录
36
- const saveToFile = process.env.AUTO_SAVE_REVIEWGUARD !== 'false'; // 默认保存
37
- if (saveToFile) {
38
- const savePath = path.join(process.cwd(), 'reviewguard.md');
39
- fs.writeFileSync(savePath, generatedContent, 'utf-8');
40
- console.log(`📝 已保存到: ${savePath}\n`);
41
- }
42
-
43
196
  return {
44
197
  type: 'markdown',
45
198
  filename: 'reviewguard.md (AI生成)',
@@ -53,6 +206,640 @@ export async function generateAIReviewGuardConfig(aiClient) {
53
206
  }
54
207
  }
55
208
 
209
+ /**
210
+ * 🎯 使用 AI 生成审核提示词(GitLab API 模式 - 获取整个项目)
211
+ * @param {Object} aiClient - AI 客户端
212
+ * @param {Object} gitlabClient - GitLab 客户端
213
+ * @param {string} projectId - 项目 ID
214
+ * @param {string} ref - 分支名(默认 main)
215
+ * @returns {Promise<Object>} 生成的配置
216
+ */
217
+ export async function generateAIReviewGuardConfigFromGitLab(aiClient, gitlabClient, projectId, ref = 'main') {
218
+ console.log('\n🤖 未找到 reviewguard.md 文件,正在通过 GitLab API 获取整个项目并生成审核提示词...\n');
219
+
220
+ try {
221
+ // 通过 GitLab API 获取整个项目代码
222
+ const projectData = await gitlabClient.getFullProjectForAnalysis(projectId, ref);
223
+
224
+ // 构建生成提示词的消息(使用全量文件)
225
+ const messages = buildGenerateGuardPromptMessagesFromFullProject(projectData);
226
+
227
+ console.log('🔄 正在请求 AI 生成审核规则文档...\n');
228
+ console.log(` 发送给 AI 的项目信息:`);
229
+ console.log(` - 文件数量: ${projectData.files.length}`);
230
+ console.log(` - 总字符数: ${(projectData.stats.totalChars / 1000).toFixed(1)}K`);
231
+ console.log(` - 技术栈: ${projectData.techStack.languages.join(', ') || '未知'}`);
232
+ console.log(` - 框架: ${projectData.techStack.frameworks.join(', ') || '无'}\n`);
233
+
234
+ console.log('📥 AI 实时响应:');
235
+ console.log('-'.repeat(60));
236
+
237
+ // 🎯 使用流式调用 AI 生成
238
+ const response = await aiClient.sendMessageStream(messages, (chunk) => {
239
+ if (chunk.content) {
240
+ process.stdout.write(chunk.content);
241
+ }
242
+ });
243
+
244
+ console.log('\n' + '-'.repeat(60));
245
+ const generatedContent = response.content;
246
+
247
+ console.log('✅ AI 已生成审核规则文档\n');
248
+
249
+ return {
250
+ type: 'markdown',
251
+ filename: 'reviewguard.md (AI从GitLab生成)',
252
+ content: generatedContent,
253
+ generated: true,
254
+ fromGitLab: true,
255
+ };
256
+ } catch (error) {
257
+ console.error('❌ 通过 GitLab API 生成审核提示词失败:', error.message);
258
+ console.log('⚠️ 将使用默认审核规则\n');
259
+ return null;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * 构建生成提示词消息(支持完整模式和轻量模式)
265
+ * @param {Object} projectData - 项目数据 { files, techStack, stats, directoryTree, fileList }
266
+ * @returns {Array} 消息数组
267
+ */
268
+ function buildGenerateGuardPromptMessagesFromFullProject(projectData) {
269
+ const { files, techStack, stats, directoryTree, fileList } = projectData;
270
+ const isFullMode = stats.mode === 'full';
271
+
272
+ let userMessage = `我需要你根据这个项目的${isFullMode ? '完整代码' : '信息'},生成一份适合该项目的 AI 代码审查规则文档(reviewguard.md)。
273
+
274
+ ## 项目技术栈
275
+
276
+ **编程语言**: ${techStack.languages.join(', ') || '未知'}
277
+ **框架/库**: ${techStack.frameworks.join(', ') || '无'}
278
+ **工具**: ${techStack.tools.join(', ') || '无'}
279
+
280
+ ## 项目统计
281
+
282
+ - 配置文件数: ${stats.configFilesCount || 0}
283
+ - 代码文件数: ${stats.sourceFilesCount || 0}
284
+ - 分析的文件: ${stats.totalFiles || 0}
285
+ - 总字符数: ${((stats.totalChars || 0) / 1000).toFixed(1)}K
286
+
287
+ ## 项目目录结构
288
+
289
+ \`\`\`
290
+ ${directoryTree || '未提供'}
291
+ \`\`\`
292
+
293
+ ## 项目文件列表
294
+
295
+ 共 ${fileList?.length || 0} 个文件:
296
+ ${fileList?.slice(0, 50).map(f => `- ${f}`).join('\n') || '未提供'}
297
+ ${fileList?.length > 50 ? `\n... 还有 ${fileList.length - 50} 个文件` : ''}
298
+
299
+ ## ${isFullMode ? '项目完整代码' : '关键配置文件和代码示例'}
300
+
301
+ `;
302
+
303
+ // 按类型分组添加文件
304
+ const configFilesContent = files.filter(f => f.type === 'config');
305
+ const sourceFilesContent = files.filter(f => f.type === 'source');
306
+ const sampleFilesContent = files.filter(f => f.type === 'sample');
307
+
308
+ if (configFilesContent.length > 0) {
309
+ userMessage += `### 📦 配置文件\n\n`;
310
+ for (const file of configFilesContent) {
311
+ const ext = file.path.split('.').pop() || '';
312
+ userMessage += `#### ${file.path}\n\n\`\`\`${ext}\n${file.content}\n\`\`\`\n\n`;
313
+ }
314
+ }
315
+
316
+ // 🎯 完整模式:添加所有代码文件的完整内容
317
+ if (sourceFilesContent.length > 0) {
318
+ userMessage += `### 📝 代码文件(完整内容)\n\n`;
319
+ for (const file of sourceFilesContent) {
320
+ const ext = file.path.split('.').pop() || '';
321
+ const truncatedNote = file.truncated ? ' [已截断]' : '';
322
+ userMessage += `#### ${file.path}${truncatedNote}\n\n\`\`\`${ext}\n${file.content}\n\`\`\`\n\n`;
323
+ }
324
+ }
325
+
326
+ // 轻量模式:添加代码摘要
327
+ if (sampleFilesContent.length > 0) {
328
+ userMessage += `### 📝 代码示例(摘要)\n\n`;
329
+ for (const file of sampleFilesContent) {
330
+ const ext = file.path.split('.').pop() || '';
331
+ userMessage += `#### ${file.path}\n\n\`\`\`${ext}\n${file.content}\n\`\`\`\n\n`;
332
+ }
333
+ }
334
+
335
+ userMessage += `
336
+ ---
337
+
338
+ ## 请生成一份详细的 reviewguard.md 文档
339
+
340
+ 文档必须包含以下所有章节:
341
+
342
+ ### 1. 项目背景与概述
343
+ - 项目名称和用途描述
344
+ - 核心功能模块说明
345
+ - 业务场景描述
346
+
347
+ ### 2. 技术栈详情
348
+ - 前端框架(React/Vue/Angular 等)及版本
349
+ - UI 组件库(如 Ant Design/Element Plus/Radix UI 等)
350
+ - 状态管理方案(Redux/Zustand/Pinia 等)
351
+ - 样式解决方案(Tailwind CSS/CSS Modules/Styled Components 等)
352
+ - 构建工具(Vite/Webpack 等)
353
+ - 代码质量工具(ESLint/Prettier 等)
354
+ - 后端技术(如有)
355
+
356
+ ### 3. 项目结构说明
357
+ - 目录结构树
358
+ - 关键文件/目录说明
359
+ - 模块划分逻辑
360
+
361
+ ### 4. 开发原则
362
+ - 组件设计原则(如 Headless UI 原则)
363
+ - 代码组织原则
364
+ - 命名规范
365
+ - 注释规范
366
+
367
+ ### 5. AI 代码审查规则(按类别分组,每个规则包含严重程度评分 1-10 分)
368
+
369
+ #### 5.1 破坏性变更检查(严重程度 8-10 分)
370
+ - 删除/重命名导出的函数、组件、类型
371
+ - API 签名变更(参数、返回值)
372
+ - 必须检查是否有其他文件引用
373
+
374
+ #### 5.2 安全漏洞(严重程度 7-10 分)
375
+ - XSS 注入风险
376
+ - 敏感信息硬编码
377
+ - 用户输入校验缺失
378
+
379
+ #### 5.3 性能问题(严重程度 5-8 分)
380
+ - 不必要的重渲染(React: 缺少 useMemo/useCallback)
381
+ - 内存泄漏(未清理 subscriptions/timers)
382
+ - 大列表未使用虚拟滚动
383
+
384
+ #### 5.4 类型安全(严重程度 4-7 分)
385
+ - 滥用 any 类型
386
+ - 类型断言不安全
387
+ - 缺少必要的类型定义
388
+
389
+ #### 5.5 代码规范(严重程度 1-4 分)
390
+ - 命名不规范
391
+ - 魔法数字未定义为常量
392
+ - 注释缺失或过期
393
+
394
+ ### 6. 回复格式规范(必须包含)
395
+
396
+ \`\`\`
397
+ 【严重程度评分:X/10】
398
+
399
+ 📋 问题描述:
400
+ 具体说明发现的问题,一句话概括
401
+
402
+ 🔍 影响分析:
403
+ • 当前文件:[具体影响]
404
+ • 受影响的其他文件:[如有,列出文件名和行号]
405
+ • 可能后果:[具体说明]
406
+
407
+ 💡 改进建议:
408
+ 1. [第一步具体操作]
409
+ 2. [第二步具体操作]
410
+ 3. [其他注意事项]
411
+ \`\`\`
412
+
413
+ ### 7. 评论示例(必须包含至少 3 个不同场景)
414
+
415
+ #### 示例 1:低风险问题(1-3 分)
416
+ 基于项目实际代码,给出一个命名规范或代码风格问题的示例
417
+
418
+ #### 示例 2:中等风险问题(4-6 分)
419
+ 基于项目实际代码,给出一个性能问题或架构问题的示例
420
+
421
+ #### 示例 3:高风险问题(7-10 分)
422
+ 基于项目实际代码,给出一个破坏性变更或安全问题的示例
423
+
424
+ ### 8. JSON 输出格式(用于 SDK 解析)
425
+
426
+ \`\`\`json
427
+ {
428
+ "reviews": [
429
+ {
430
+ "lineNumber": 15,
431
+ "isOldLine": false,
432
+ "hasIssue": true,
433
+ "comment": "【严重程度评分:8/10】\\n\\n📋 问题描述:..."
434
+ }
435
+ ]
436
+ }
437
+ \`\`\`
438
+
439
+ **重要说明:**
440
+ - 所有内容必须使用中文
441
+ - 评论示例必须具体、真实,与项目技术栈相关
442
+ - 规则要实用,能够发现真正的问题
443
+ - 格式要统一,确保所有评论都遵循相同的规范`;
444
+
445
+ return [
446
+ {
447
+ role: 'system',
448
+ content: `你是一个专业的代码审查规范设计专家。
449
+
450
+ ## 你的任务
451
+ 根据项目的完整代码来分析项目特点,并生成针对性的、详细的代码审查规则文档。
452
+
453
+ ## 要求
454
+ 1. **内容要丰富**:不要只写空洞的规则,要结合项目实际代码给出具体的示例
455
+ 2. **示例要真实**:评论示例应该基于项目实际使用的技术栈,而不是通用示例
456
+ 3. **规则要实用**:审查规则应该针对项目特点,能够发现真正的问题
457
+ 4. **格式要统一**:确保所有评论都遵循相同的格式规范
458
+
459
+ ## 输出要求
460
+ - 生成的文档必须使用中文
461
+ - 必须包含所有要求的章节
462
+ - 必须有至少 3 个具体的评论示例`
463
+ },
464
+ {
465
+ role: 'user',
466
+ content: userMessage
467
+ }
468
+ ];
469
+ }
470
+
471
+ /**
472
+ * 🎯 检查并补充现有的 reviewguard.md(智能补充模式)
473
+ * 结合项目代码判断现有规则是否完整,不完整则补充
474
+ * @param {Object} aiClient - AI 客户端
475
+ * @param {Object} gitlabClient - GitLab 客户端
476
+ * @param {string} projectId - 项目 ID
477
+ * @param {string} ref - 分支名
478
+ * @param {Object} existingGuard - 现有的 reviewguard 配置
479
+ * @returns {Promise<Object>} 检查/补充后的配置
480
+ */
481
+ export async function checkAndEnhanceReviewGuard(aiClient, gitlabClient, projectId, ref, existingGuard) {
482
+ console.log('\n🔍 检查现有 reviewguard.md 是否完整...\n');
483
+
484
+ try {
485
+ // 1. 获取项目完整代码
486
+ console.log('📡 通过 GitLab API 获取项目代码...');
487
+ const projectData = await gitlabClient.getFullProjectForAnalysis(projectId, ref);
488
+
489
+ console.log(` - 获取 ${projectData.files.length} 个文件`);
490
+ console.log(` - 总计 ${(projectData.stats.totalChars / 1000).toFixed(1)}K 字符`);
491
+ console.log(` - 技术栈: ${projectData.techStack.languages.join(', ') || '未知'}`);
492
+ console.log(` - 框架: ${projectData.techStack.frameworks.join(', ') || '无'}\n`);
493
+
494
+ // 2. 构建检查和补充的 prompt
495
+ const messages = buildCheckAndEnhancePromptMessages(existingGuard.content, projectData);
496
+
497
+ console.log('🤖 请求 AI 分析并判断 reviewguard.md 是否完整...\n');
498
+ console.log('📥 AI 实时响应:');
499
+ console.log('-'.repeat(60));
500
+
501
+ // 3. 🎯 使用流式调用 AI 分析
502
+ const response = await aiClient.sendMessageStream(messages, (chunk) => {
503
+ if (chunk.content) {
504
+ process.stdout.write(chunk.content);
505
+ }
506
+ });
507
+
508
+ console.log('\n' + '-'.repeat(60));
509
+ const result = response.content;
510
+
511
+ // 4. 解析 AI 返回结果
512
+ // 新格式:JSON 判断信息 + 可选的 ===ENHANCED_CONTENT_START=== ... ===ENHANCED_CONTENT_END=== 块
513
+ let analysisResult;
514
+ try {
515
+ // 尝试解析 JSON 部分
516
+ analysisResult = parseAIJsonResponse(result);
517
+ console.log('✅ 成功解析 AI 返回的判断信息');
518
+ console.log(` - isComplete: ${analysisResult.isComplete}`);
519
+ console.log(` - reason: ${analysisResult.reason?.substring(0, 100)}${analysisResult.reason?.length > 100 ? '...' : ''}`);
520
+ if (analysisResult.missingItems?.length > 0) {
521
+ console.log(` - 缺失项: ${analysisResult.missingItems.slice(0, 5).join(', ')}${analysisResult.missingItems.length > 5 ? '...' : ''}`);
522
+ }
523
+
524
+ // 如果不完整,尝试提取增强内容
525
+ if (!analysisResult.isComplete && !analysisResult.enhancedContent) {
526
+ const enhancedContent = extractEnhancedContent(result);
527
+ if (enhancedContent) {
528
+ analysisResult.enhancedContent = enhancedContent;
529
+ console.log(` ✅ 成功提取增强内容 (${(enhancedContent.length / 1000).toFixed(1)}K 字符)`);
530
+ }
531
+ }
532
+ } catch (e) {
533
+ console.log('⚠️ 无法解析 AI 返回的 JSON:', e.message);
534
+
535
+ // 尝试提取增强内容
536
+ const enhancedContent = extractEnhancedContent(result);
537
+ if (enhancedContent) {
538
+ console.log('📄 从特殊标记中提取到增强内容');
539
+ analysisResult = {
540
+ isComplete: false,
541
+ reason: 'JSON 解析失败,但提取到增强内容',
542
+ enhancedContent: enhancedContent,
543
+ };
544
+ } else if (result.includes('# ') && result.includes('## ')) {
545
+ // 如果看起来像 markdown 文档
546
+ console.log('📄 检测到 AI 返回了 Markdown 格式,将作为增强内容使用');
547
+ analysisResult = {
548
+ isComplete: false,
549
+ reason: 'AI 直接返回了增强后的文档',
550
+ enhancedContent: result,
551
+ };
552
+ } else {
553
+ console.log('⚠️ 将直接使用现有的 reviewguard.md');
554
+ return existingGuard;
555
+ }
556
+ }
557
+
558
+ // 5. 根据结果处理
559
+ if (analysisResult.isComplete) {
560
+ console.log('✅ reviewguard.md 已经比较完整,无需补充');
561
+ console.log(` 原因: ${analysisResult.reason}\n`);
562
+
563
+ return {
564
+ ...existingGuard,
565
+ checked: true,
566
+ isComplete: true,
567
+ checkReason: analysisResult.reason,
568
+ };
569
+ } else {
570
+ console.log('📝 reviewguard.md 需要补充,正在增强...');
571
+ console.log(` 原因: ${analysisResult.reason}\n`);
572
+
573
+ const enhancedContent = analysisResult.enhancedContent || existingGuard.content;
574
+
575
+ return {
576
+ type: 'markdown',
577
+ filename: 'reviewguard.md (AI增强)',
578
+ content: enhancedContent,
579
+ checked: true,
580
+ isComplete: false,
581
+ enhanced: true,
582
+ checkReason: analysisResult.reason,
583
+ };
584
+ }
585
+ } catch (error) {
586
+ console.error('❌ 检查 reviewguard.md 失败:', error.message);
587
+ console.log('⚠️ 将直接使用现有的 reviewguard.md\n');
588
+ return existingGuard;
589
+ }
590
+ }
591
+
592
+ /**
593
+ * 构建检查和补充 reviewguard.md 的 prompt
594
+ * @param {string} existingContent - 现有的 reviewguard.md 内容
595
+ * @param {Object} projectData - 项目数据
596
+ * @returns {Array} 消息数组
597
+ */
598
+ function buildCheckAndEnhancePromptMessages(existingContent, projectData) {
599
+ const { files, techStack, stats } = projectData;
600
+
601
+ let userMessage = `我有一个项目的代码审查规则文档(reviewguard.md),请你结合项目的实际代码,判断这个规则文档是否完整和准确。
602
+
603
+ ## 现有的 reviewguard.md 内容
604
+
605
+ \`\`\`markdown
606
+ ${existingContent}
607
+ \`\`\`
608
+
609
+ ## 项目技术栈
610
+
611
+ **编程语言**: ${techStack.languages.join(', ') || '未知'}
612
+ **框架/库**: ${techStack.frameworks.join(', ') || '无'}
613
+ **工具**: ${techStack.tools.join(', ') || '无'}
614
+
615
+ ## 项目统计
616
+
617
+ - 文件数量: ${stats.totalFiles}
618
+ - 总代码量: ${(stats.totalChars / 1000).toFixed(1)}K 字符
619
+
620
+ ## 项目完整代码
621
+
622
+ 以下是项目的所有代码文件:
623
+
624
+ `;
625
+
626
+ // 添加所有文件内容
627
+ for (const file of files) {
628
+ const ext = file.path.split('.').pop() || '';
629
+ userMessage += `
630
+ ### 📄 ${file.path}
631
+
632
+ \`\`\`${ext}
633
+ ${file.content}
634
+ \`\`\`
635
+
636
+ `;
637
+ }
638
+
639
+ userMessage += `
640
+ ---
641
+
642
+ ## 请你完成以下任务:
643
+
644
+ 1. **分析项目代码**:仔细阅读项目的所有代码文件
645
+ 2. **对比现有规则**:检查现有的 reviewguard.md 是否覆盖了项目的关键特点
646
+ 3. **判断完整性**:
647
+
648
+ 需要检查的方面:
649
+ - 是否有项目背景和概述
650
+ - 技术栈描述是否准确(框架、库、工具)
651
+ - 项目结构描述是否正确
652
+ - 代码审查规则是否涵盖项目实际使用的技术
653
+ - 是否有遗漏的重要审查规则
654
+ - 是否有具体的评论示例(不同严重程度的示例)
655
+ - 回复格式规范是否完整
656
+
657
+ 4. **输出结果**(必须严格按照以下格式):
658
+
659
+ **如果规则文档比较完整(无需补充):**
660
+
661
+ \`\`\`json
662
+ {
663
+ "isComplete": true,
664
+ "reason": "简要说明为什么认为完整"
665
+ }
666
+ \`\`\`
667
+
668
+ **如果规则文档不完整(需要补充):**
669
+
670
+ 先输出判断信息:
671
+ \`\`\`json
672
+ {
673
+ "isComplete": false,
674
+ "reason": "简要说明缺少什么",
675
+ "missingItems": ["缺少项1", "缺少项2"]
676
+ }
677
+ \`\`\`
678
+
679
+ 然后输出增强后的完整文档(用特殊标记包裹):
680
+ \`\`\`
681
+ ===ENHANCED_CONTENT_START===
682
+ (这里是补充完善后的完整 reviewguard.md 内容)
683
+ ===ENHANCED_CONTENT_END===
684
+ \`\`\`
685
+
686
+ ## 📋 增强文档必须包含的内容(非常重要!)
687
+
688
+ 生成的 reviewguard.md 必须包含以下所有章节:
689
+
690
+ ### 1. 项目背景与概述
691
+ - 项目名称和用途
692
+ - 项目的核心功能模块
693
+ - 项目的业务场景
694
+
695
+ ### 2. 技术栈详情
696
+ - 前端框架(React/Vue/Angular 等)
697
+ - UI 组件库(Ant Design/Element Plus/Radix UI 等)
698
+ - 状态管理方案
699
+ - 样式解决方案(Tailwind CSS/CSS Modules/Styled Components 等)
700
+ - 构建工具(Vite/Webpack 等)
701
+ - 代码质量工具(ESLint/Prettier 等)
702
+
703
+ ### 3. 项目结构说明
704
+ - 目录结构
705
+ - 关键文件说明
706
+ - 模块划分
707
+
708
+ ### 4. 代码审查规则(按类别分组)
709
+ 每个规则必须包含:**严重程度评分(1-10分)**
710
+
711
+ #### 4.1 破坏性变更检查(严重程度 8-10 分)
712
+ - 删除/重命名导出的函数、组件、类型
713
+ - API 签名变更
714
+ - 必须检查是否有其他文件引用
715
+
716
+ #### 4.2 安全漏洞(严重程度 7-10 分)
717
+ - XSS 注入风险
718
+ - 敏感信息硬编码
719
+ - 用户输入校验
720
+
721
+ #### 4.3 性能问题(严重程度 5-8 分)
722
+ - 不必要的重渲染
723
+ - 内存泄漏
724
+ - 大列表优化
725
+
726
+ #### 4.4 代码规范(严重程度 1-4 分)
727
+ - 命名规范
728
+ - 注释规范
729
+ - 代码组织
730
+
731
+ ### 5. 回复格式规范(必须包含)
732
+
733
+ \`\`\`
734
+ 【严重程度评分:X/10】
735
+
736
+ 📋 问题描述:
737
+ 具体说明发现的问题
738
+
739
+ 🔍 影响分析:
740
+ • 当前文件:[具体影响]
741
+ • 受影响的其他文件:[如有]
742
+ • 可能后果:[具体说明]
743
+
744
+ 💡 改进建议:
745
+ 1. [第一步具体操作]
746
+ 2. [第二步具体操作]
747
+ \`\`\`
748
+
749
+ ### 6. 评论示例(必须包含至少 3 个不同场景的示例)
750
+
751
+ #### 示例 1:低风险问题(1-3 分)
752
+ \`\`\`
753
+ 【严重程度评分:2/10】
754
+
755
+ 📋 问题描述:
756
+ 变量名 user_name 不符合 camelCase 命名规范
757
+
758
+ 🔍 影响分析:
759
+ • 当前文件:不影响功能运行
760
+ • 受影响的其他文件:无
761
+ • 可能后果:影响代码可读性
762
+
763
+ 💡 改进建议:
764
+ 1. 将 user_name 改为 userName
765
+ 2. 检查同一文件中其他变量命名
766
+ \`\`\`
767
+
768
+ #### 示例 2:中等风险问题(4-6 分)
769
+ (提供一个具体的中等风险示例)
770
+
771
+ #### 示例 3:高风险问题(7-10 分)
772
+ (提供一个具体的高风险示例,如删除正在使用的函数)
773
+
774
+ ### 7. JSON 输出格式(用于 SDK 解析)
775
+
776
+ \`\`\`json
777
+ {
778
+ "reviews": [
779
+ {
780
+ "lineNumber": 15,
781
+ "isOldLine": false,
782
+ "hasIssue": true,
783
+ "comment": "【严重程度评分:8/10】\\n\\n📋 问题描述:..."
784
+ }
785
+ ]
786
+ }
787
+ \`\`\`
788
+
789
+ **重要说明:**
790
+ - JSON 中不要包含 enhancedContent 字段
791
+ - 增强内容必须用 ===ENHANCED_CONTENT_START=== 和 ===ENHANCED_CONTENT_END=== 包裹
792
+ - 增强内容必须是完整的 markdown 文档,不是增量
793
+ - 必须保留原有内容的精华,并补充缺失部分
794
+ - 所有内容必须使用中文
795
+ - 评论示例必须具体、真实,与项目技术栈相关`;
796
+
797
+ return [
798
+ {
799
+ role: 'system',
800
+ content: `你是一个专业的代码审查规范专家。你的任务是分析项目代码,检查现有的代码审查规则文档是否完整和准确。
801
+
802
+ ## 你的职责
803
+
804
+ 1. 仔细阅读项目代码,深入理解项目的技术栈、架构和业务场景
805
+ 2. 对比现有的规则文档,找出不准确或缺失的部分
806
+ 3. 如果规则文档不完整,提供补充后的完整文档
807
+
808
+ ## 判断"完整"的标准
809
+
810
+ 文档必须包含以下所有内容才能算"完整":
811
+ - ✅ 项目背景和概述(项目用途、核心功能)
812
+ - ✅ 准确的技术栈描述(框架、库、工具)
813
+ - ✅ 项目结构说明
814
+ - ✅ 分类的代码审查规则(包含严重程度评分)
815
+ - ✅ 统一的回复格式规范
816
+ - ✅ 至少 3 个具体的评论示例(低/中/高风险)
817
+ - ✅ JSON 输出格式说明
818
+
819
+ ## 重要原则
820
+
821
+ 1. **内容要丰富**:不要只写空洞的规则,要结合项目实际代码给出具体的示例
822
+ 2. **示例要真实**:评论示例应该基于项目实际使用的技术栈,而不是通用示例
823
+ 3. **规则要实用**:审查规则应该针对项目特点,能够发现真正的问题
824
+ 4. **格式要统一**:确保所有评论都遵循相同的格式规范
825
+
826
+ ## 评分参考
827
+
828
+ - 缺少项目背景:-20%
829
+ - 缺少技术栈描述:-20%
830
+ - 缺少审查规则分类:-20%
831
+ - 缺少评论示例:-20%
832
+ - 缺少格式规范:-20%
833
+
834
+ 如果总分低于 80%,则认为"不完整",需要补充。`
835
+ },
836
+ {
837
+ role: 'user',
838
+ content: userMessage
839
+ }
840
+ ];
841
+ }
842
+
56
843
  /**
57
844
  * 读取项目根目录下的 AI Review Guard 配置文件
58
845
  * @param {Object} aiClient - AI 客户端(可选,用于生成配置)