gitlab-ai-review 4.0.1 → 4.1.0
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/README.md +166 -0
- package/index.js +38 -39
- package/lib/config.js +66 -5
- package/lib/project-analyzer.js +349 -0
- package/lib/prompt-tools.js +142 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -20,6 +20,10 @@ GitLab AI Review SDK - 支持 CI/CD 自动配置和手动配置
|
|
|
20
20
|
- 自动跳过 Markdown 文件 (`.md`)
|
|
21
21
|
- 自动跳过配置文件 (`.json`, `.yml`, `.yaml`)
|
|
22
22
|
- 自动跳过依赖锁文件 (`package-lock.json`, `yarn.lock` 等)
|
|
23
|
+
- ✅ **AI 自动生成审核规则** 🚀 NEW!
|
|
24
|
+
- 自动分析项目技术栈和代码结构
|
|
25
|
+
- 智能生成适合项目的审核提示词
|
|
26
|
+
- 无需手动编写 `reviewguard.md` 文件
|
|
23
27
|
|
|
24
28
|
## 安装
|
|
25
29
|
|
|
@@ -52,6 +56,8 @@ await sdk.addComment('✅ AI 代码审查完成!');
|
|
|
52
56
|
- `CI_SERVER_URL` 或 `GITLAB_URL` - GitLab 地址
|
|
53
57
|
- `CI_PROJECT_PATH` 或 `PROJECT_ID` - 项目 ID
|
|
54
58
|
- `CI_MERGE_REQUEST_IID` 或 `MERGE_REQUEST_IID` - MR 编号
|
|
59
|
+
- `ARK_API_KEY` - AI API 密钥(用于代码审查)
|
|
60
|
+
- `AUTO_SAVE_REVIEWGUARD` - 是否自动保存生成的 reviewguard.md(默认 true)
|
|
55
61
|
|
|
56
62
|
### 方式 2:手动传递配置
|
|
57
63
|
|
|
@@ -185,6 +191,83 @@ const results = await sdk.reviewAndCommentOnLines({
|
|
|
185
191
|
});
|
|
186
192
|
```
|
|
187
193
|
|
|
194
|
+
## 配置审核规则
|
|
195
|
+
|
|
196
|
+
### 方式 1:手动创建 reviewguard.md(推荐自定义)
|
|
197
|
+
|
|
198
|
+
在项目根目录创建 `reviewguard.md` 文件,定义审核规则:
|
|
199
|
+
|
|
200
|
+
```markdown
|
|
201
|
+
# AI 代码审查规则
|
|
202
|
+
|
|
203
|
+
## 破坏性变更检查(严重程度 8-10 分)
|
|
204
|
+
- 删除/重命名导出的函数、组件、类型
|
|
205
|
+
- API 签名变更
|
|
206
|
+
- 必须检查是否有其他文件引用
|
|
207
|
+
|
|
208
|
+
## 安全漏洞(严重程度 7-10 分)
|
|
209
|
+
- XSS、SQL 注入等
|
|
210
|
+
- 敏感信息硬编码
|
|
211
|
+
- 用户输入未校验
|
|
212
|
+
|
|
213
|
+
## 性能问题(严重程度 5-8 分)
|
|
214
|
+
- 不必要的重渲染
|
|
215
|
+
- 内存泄漏
|
|
216
|
+
- 大列表未优化
|
|
217
|
+
|
|
218
|
+
...
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 方式 2:AI 自动生成(全新功能!)🚀
|
|
222
|
+
|
|
223
|
+
如果项目中**没有** `reviewguard.md` 文件,SDK 会自动:
|
|
224
|
+
1. 分析项目的技术栈(检测 `package.json`、`tsconfig.json` 等)
|
|
225
|
+
2. 收集关键文件信息
|
|
226
|
+
3. 调用 AI 生成适合该项目的审核规则
|
|
227
|
+
4. 自动保存到项目根目录
|
|
228
|
+
|
|
229
|
+
**完全零配置,开箱即用!**
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
import GitLabAIReview from 'gitlab-ai-review';
|
|
233
|
+
|
|
234
|
+
const sdk = new GitLabAIReview({
|
|
235
|
+
arkApiKey: 'your-api-key', // AI API 密钥
|
|
236
|
+
// ... 其他配置
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// 第一次调用审查时,会自动生成 reviewguard.md
|
|
240
|
+
const results = await sdk.reviewWithImpactAnalysis();
|
|
241
|
+
|
|
242
|
+
// 生成的 reviewguard.md 会保存在项目根目录,后续可以手动调整
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**自动生成的审核规则包含:**
|
|
246
|
+
- 项目概述和技术栈分析
|
|
247
|
+
- 针对性的开发原则(如 React Hooks、TypeScript 类型安全等)
|
|
248
|
+
- 完整的 AI 代码审查规则(破坏性变更、安全、性能、类型安全等)
|
|
249
|
+
- 统一的回复格式规范
|
|
250
|
+
- 严重程度评分标准(0-10 分)
|
|
251
|
+
- 实际示例演示
|
|
252
|
+
|
|
253
|
+
**环境变量控制:**
|
|
254
|
+
```bash
|
|
255
|
+
# 默认会自动保存生成的 reviewguard.md
|
|
256
|
+
# 如果不想保存,设置以下环境变量:
|
|
257
|
+
AUTO_SAVE_REVIEWGUARD=false
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 支持的配置文件
|
|
261
|
+
|
|
262
|
+
SDK 会按优先级查找以下文件:
|
|
263
|
+
1. `.aireviewguard`
|
|
264
|
+
2. `aireviewguard`
|
|
265
|
+
3. `.aireviewguard.json`
|
|
266
|
+
4. `reviewguard.md` ⭐ 推荐
|
|
267
|
+
5. `.reviewguard.md`
|
|
268
|
+
|
|
269
|
+
如果都不存在,则自动触发 AI 生成。
|
|
270
|
+
|
|
188
271
|
## 在 GitLab CI/CD 中使用
|
|
189
272
|
|
|
190
273
|
`.gitlab-ci.yml` 示例:
|
|
@@ -222,7 +305,90 @@ console.log(`共有 ${changes.length} 个文件变更`);
|
|
|
222
305
|
await sdk.addComment('🤖 AI 代码审查已完成!');
|
|
223
306
|
```
|
|
224
307
|
|
|
308
|
+
## CLI 命令行工具
|
|
309
|
+
|
|
310
|
+
你也可以直接使用 `npx` 运行审查:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# 基本用法(自动从环境变量读取配置)
|
|
314
|
+
npx gitlab-ai-review
|
|
315
|
+
|
|
316
|
+
# 禁用影响分析
|
|
317
|
+
npx gitlab-ai-review --no-impact
|
|
318
|
+
|
|
319
|
+
# 限制审查文件数量
|
|
320
|
+
npx gitlab-ai-review --max-files 5
|
|
321
|
+
|
|
322
|
+
# 限制受影响文件分析数量
|
|
323
|
+
npx gitlab-ai-review --max-affected-files 10
|
|
324
|
+
|
|
325
|
+
# 查看帮助
|
|
326
|
+
npx gitlab-ai-review --help
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**在 CI/CD 中使用 CLI:**
|
|
330
|
+
|
|
331
|
+
```yaml
|
|
332
|
+
ai-review:
|
|
333
|
+
stage: review
|
|
334
|
+
image: node:18
|
|
335
|
+
only:
|
|
336
|
+
- merge_requests
|
|
337
|
+
variables:
|
|
338
|
+
ARK_API_KEY: $ARK_API_KEY # 从 GitLab Variables 中读取
|
|
339
|
+
script:
|
|
340
|
+
- npx gitlab-ai-review
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## 项目结构分析器
|
|
344
|
+
|
|
345
|
+
新增的项目分析器可以自动检测:
|
|
346
|
+
|
|
347
|
+
### 支持的技术栈
|
|
348
|
+
|
|
349
|
+
**编程语言:**
|
|
350
|
+
- JavaScript / TypeScript
|
|
351
|
+
- Python (Django, Flask, FastAPI)
|
|
352
|
+
- Go
|
|
353
|
+
- Rust
|
|
354
|
+
- Java / Kotlin
|
|
355
|
+
- PHP (Laravel, Symfony)
|
|
356
|
+
- Ruby (Ruby on Rails)
|
|
357
|
+
|
|
358
|
+
**前端框架:**
|
|
359
|
+
- React (检测 Radix UI、Headless UI、@tanstack/react-table 等)
|
|
360
|
+
- Vue.js / Nuxt.js
|
|
361
|
+
- Next.js
|
|
362
|
+
- Svelte
|
|
363
|
+
- Angular
|
|
364
|
+
|
|
365
|
+
**构建工具:**
|
|
366
|
+
- Vite
|
|
367
|
+
- Webpack
|
|
368
|
+
- Rollup
|
|
369
|
+
- esbuild
|
|
370
|
+
|
|
371
|
+
**样式方案:**
|
|
372
|
+
- Tailwind CSS
|
|
373
|
+
- Styled Components
|
|
374
|
+
- Emotion
|
|
375
|
+
|
|
376
|
+
### 示例:查看项目分析结果
|
|
377
|
+
|
|
378
|
+
```javascript
|
|
379
|
+
import { analyzeProject } from 'gitlab-ai-review/lib/project-analyzer.js';
|
|
380
|
+
|
|
381
|
+
const analysis = analyzeProject(process.cwd());
|
|
382
|
+
|
|
383
|
+
console.log('语言:', analysis.techStack.languages);
|
|
384
|
+
console.log('框架:', analysis.techStack.frameworks);
|
|
385
|
+
console.log('工具:', analysis.techStack.tools);
|
|
386
|
+
console.log('关键文件:', analysis.keyFiles.length, '个');
|
|
387
|
+
```
|
|
388
|
+
|
|
225
389
|
## 版本
|
|
226
390
|
|
|
391
|
+
- 4.0.3 - 🚀 新增 AI 自动生成审核规则功能,支持项目结构分析
|
|
392
|
+
- 4.0.2 - 添加影响分析和智能文件过滤
|
|
227
393
|
- 1.0.1 - 添加 GitLab 客户端封装和自动配置
|
|
228
394
|
- 1.0.0 - 初始测试版本
|
package/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 支持 CI/CD 自动配置和手动配置
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { getConfig, validateConfig } from './lib/config.js';
|
|
6
|
+
import { getConfig, validateConfig, loadGuardConfig } from './lib/config.js';
|
|
7
7
|
import { GitLabClient } from './lib/gitlab-client.js';
|
|
8
8
|
import { AIClient } from './lib/ai-client.js';
|
|
9
9
|
import * as PromptTools from './lib/prompt-tools.js';
|
|
@@ -16,7 +16,7 @@ import * as ImpactAnalyzer from './lib/impact-analyzer.js';
|
|
|
16
16
|
export class GitLabAIReview {
|
|
17
17
|
constructor(options = {}) {
|
|
18
18
|
this.name = 'GitLab AI Review SDK';
|
|
19
|
-
this.version = '
|
|
19
|
+
this.version = '4.1.0';
|
|
20
20
|
|
|
21
21
|
// 如果传入了配置,使用手动配置;否则使用自动检测
|
|
22
22
|
if (options.token || options.gitlab) {
|
|
@@ -62,6 +62,27 @@ export class GitLabAIReview {
|
|
|
62
62
|
return validateConfig(this.config);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* 初始化并加载 Guard 配置(支持 AI 自动生成)
|
|
67
|
+
* 如果项目中没有 reviewguard.md,会自动分析项目并生成
|
|
68
|
+
*/
|
|
69
|
+
async initGuardConfig() {
|
|
70
|
+
if (this.config.ai?.guardConfig) {
|
|
71
|
+
return this.config.ai.guardConfig;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const guardConfig = await loadGuardConfig(this.aiClient);
|
|
75
|
+
|
|
76
|
+
if (guardConfig) {
|
|
77
|
+
this.config.ai.guardConfig = guardConfig;
|
|
78
|
+
console.log(`✅ Guard 配置已加载: ${guardConfig.filename}${guardConfig.generated ? ' (AI自动生成)' : ''}`);
|
|
79
|
+
} else {
|
|
80
|
+
console.log('⚠️ 未找到 Guard 配置,将使用默认审查规则');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return guardConfig;
|
|
84
|
+
}
|
|
85
|
+
|
|
65
86
|
/**
|
|
66
87
|
* 获取当前配置
|
|
67
88
|
*/
|
|
@@ -177,6 +198,9 @@ export class GitLabAIReview {
|
|
|
177
198
|
* @returns {Promise<Array>} 评论结果数组
|
|
178
199
|
*/
|
|
179
200
|
async reviewAndCommentOnLines(options = {}) {
|
|
201
|
+
// 确保 Guard 配置已加载
|
|
202
|
+
await this.initGuardConfig();
|
|
203
|
+
|
|
180
204
|
const { maxFiles = Infinity } = options;
|
|
181
205
|
const allChanges = await this.getMergeRequestChanges();
|
|
182
206
|
const changes = this.filterReviewableFiles(allChanges);
|
|
@@ -212,33 +236,19 @@ export class GitLabAIReview {
|
|
|
212
236
|
for (const review of mergedReviews) {
|
|
213
237
|
if (review.hasIssue) {
|
|
214
238
|
try {
|
|
215
|
-
// 找到对应的变更
|
|
216
|
-
const relatedChange = meaningfulChanges.find(c =>
|
|
217
|
-
c.lineNumber === review.lineNumber ||
|
|
218
|
-
c.lineNumber === review.oldLine ||
|
|
219
|
-
c.lineNumber === review.newLine
|
|
220
|
-
);
|
|
221
|
-
|
|
222
239
|
// 构建评论位置参数
|
|
223
240
|
const positionParams = {
|
|
224
241
|
filePath: fileName,
|
|
225
242
|
oldPath: change.old_path,
|
|
226
243
|
};
|
|
227
244
|
|
|
228
|
-
//
|
|
229
|
-
if (
|
|
230
|
-
const isDeletion = relatedChange.type === 'deletion';
|
|
231
|
-
if (isDeletion) {
|
|
232
|
-
positionParams.oldLine = relatedChange.hunkRange.oldStart;
|
|
233
|
-
} else {
|
|
234
|
-
positionParams.newLine = relatedChange.hunkRange.newStart;
|
|
235
|
-
}
|
|
236
|
-
} else if (review.oldLine && review.newLine) {
|
|
237
|
-
// 修改块:同时指定两个行号
|
|
245
|
+
// 如果是修改块(同时有 oldLine 和 newLine),同时指定两个行号
|
|
246
|
+
if (review.oldLine && review.newLine) {
|
|
238
247
|
positionParams.oldLine = review.oldLine;
|
|
239
248
|
positionParams.newLine = review.newLine;
|
|
240
249
|
} else if (review.lineNumber) {
|
|
241
|
-
//
|
|
250
|
+
// 单独的删除或新增
|
|
251
|
+
const relatedChange = meaningfulChanges.find(c => c.lineNumber === review.lineNumber);
|
|
242
252
|
const isDeletion = relatedChange && relatedChange.type === 'deletion';
|
|
243
253
|
if (isDeletion) {
|
|
244
254
|
positionParams.oldLine = review.lineNumber;
|
|
@@ -439,6 +449,9 @@ export class GitLabAIReview {
|
|
|
439
449
|
* @returns {Promise<Array>} 评论结果数组
|
|
440
450
|
*/
|
|
441
451
|
async reviewWithImpactAnalysis(options = {}) {
|
|
452
|
+
// 确保 Guard 配置已加载
|
|
453
|
+
await this.initGuardConfig();
|
|
454
|
+
|
|
442
455
|
const {
|
|
443
456
|
maxFiles = Infinity,
|
|
444
457
|
maxAffectedFiles = 10,
|
|
@@ -498,33 +511,19 @@ export class GitLabAIReview {
|
|
|
498
511
|
for (const review of mergedReviews) {
|
|
499
512
|
if (review.hasIssue) {
|
|
500
513
|
try {
|
|
501
|
-
// 找到对应的变更
|
|
502
|
-
const relatedChange = meaningfulChanges.find(c =>
|
|
503
|
-
c.lineNumber === review.lineNumber ||
|
|
504
|
-
c.lineNumber === review.oldLine ||
|
|
505
|
-
c.lineNumber === review.newLine
|
|
506
|
-
);
|
|
507
|
-
|
|
508
514
|
// 构建评论位置参数
|
|
509
515
|
const positionParams = {
|
|
510
516
|
filePath: fileName,
|
|
511
517
|
oldPath: change.old_path,
|
|
512
518
|
};
|
|
513
519
|
|
|
514
|
-
//
|
|
515
|
-
if (
|
|
516
|
-
const isDeletion = relatedChange.type === 'deletion';
|
|
517
|
-
if (isDeletion) {
|
|
518
|
-
positionParams.oldLine = relatedChange.hunkRange.oldStart;
|
|
519
|
-
} else {
|
|
520
|
-
positionParams.newLine = relatedChange.hunkRange.newStart;
|
|
521
|
-
}
|
|
522
|
-
} else if (review.oldLine && review.newLine) {
|
|
523
|
-
// 修改块:同时指定两个行号
|
|
520
|
+
// 如果是修改块(同时有 oldLine 和 newLine),同时指定两个行号
|
|
521
|
+
if (review.oldLine && review.newLine) {
|
|
524
522
|
positionParams.oldLine = review.oldLine;
|
|
525
523
|
positionParams.newLine = review.newLine;
|
|
526
524
|
} else if (review.lineNumber) {
|
|
527
|
-
//
|
|
525
|
+
// 单独的删除或新增
|
|
526
|
+
const relatedChange = meaningfulChanges.find(c => c.lineNumber === review.lineNumber);
|
|
528
527
|
const isDeletion = relatedChange && relatedChange.type === 'deletion';
|
|
529
528
|
if (isDeletion) {
|
|
530
529
|
positionParams.oldLine = review.lineNumber;
|
|
@@ -671,7 +670,7 @@ export class GitLabAIReview {
|
|
|
671
670
|
}
|
|
672
671
|
|
|
673
672
|
// 导出工具函数和类
|
|
674
|
-
export { getConfig, validateConfig } from './lib/config.js';
|
|
673
|
+
export { getConfig, validateConfig, loadGuardConfig } from './lib/config.js';
|
|
675
674
|
export { GitLabClient } from './lib/gitlab-client.js';
|
|
676
675
|
export { AIClient } from './lib/ai-client.js';
|
|
677
676
|
export { PromptTools, DiffParser, ImpactAnalyzer };
|
package/lib/config.js
CHANGED
|
@@ -5,11 +5,60 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
+
import { analyzeProject } from './project-analyzer.js';
|
|
9
|
+
import { buildGenerateGuardPromptMessages } from './prompt-tools.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 使用 AI 生成审核提示词
|
|
13
|
+
* @param {Object} aiClient - AI 客户端
|
|
14
|
+
* @returns {Promise<Object>} 生成的配置
|
|
15
|
+
*/
|
|
16
|
+
export async function generateAIReviewGuardConfig(aiClient) {
|
|
17
|
+
console.log('\n🤖 未找到 reviewguard.md 文件,正在分析项目并生成审核提示词...\n');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// 分析项目结构
|
|
21
|
+
const projectAnalysis = analyzeProject(process.cwd());
|
|
22
|
+
|
|
23
|
+
// 构建生成提示词的消息
|
|
24
|
+
const messages = buildGenerateGuardPromptMessages(projectAnalysis);
|
|
25
|
+
|
|
26
|
+
console.log('🔄 正在请求 AI 生成审核规则文档...\n');
|
|
27
|
+
|
|
28
|
+
// 调用 AI 生成
|
|
29
|
+
const response = await aiClient.sendMessage(messages);
|
|
30
|
+
|
|
31
|
+
const generatedContent = response.content;
|
|
32
|
+
|
|
33
|
+
console.log('✅ AI 已生成审核规则文档\n');
|
|
34
|
+
|
|
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
|
+
return {
|
|
44
|
+
type: 'markdown',
|
|
45
|
+
filename: 'reviewguard.md (AI生成)',
|
|
46
|
+
content: generatedContent,
|
|
47
|
+
generated: true,
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('❌ 生成审核提示词失败:', error.message);
|
|
51
|
+
console.log('⚠️ 将使用默认审核规则\n');
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
8
55
|
|
|
9
56
|
/**
|
|
10
57
|
* 读取项目根目录下的 AI Review Guard 配置文件
|
|
58
|
+
* @param {Object} aiClient - AI 客户端(可选,用于生成配置)
|
|
59
|
+
* @returns {Promise<Object>} 配置对象
|
|
11
60
|
*/
|
|
12
|
-
function readAIReviewGuardConfig() {
|
|
61
|
+
async function readAIReviewGuardConfig(aiClient = null) {
|
|
13
62
|
const configFiles = ['.aireviewguard', 'aireviewguard', '.aireviewguard.json', 'reviewguard.md', '.reviewguard.md'];
|
|
14
63
|
|
|
15
64
|
for (const filename of configFiles) {
|
|
@@ -47,6 +96,11 @@ function readAIReviewGuardConfig() {
|
|
|
47
96
|
}
|
|
48
97
|
}
|
|
49
98
|
|
|
99
|
+
// 如果没有找到配置文件,且提供了 AI 客户端,则自动生成
|
|
100
|
+
if (aiClient) {
|
|
101
|
+
return await generateAIReviewGuardConfig(aiClient);
|
|
102
|
+
}
|
|
103
|
+
|
|
50
104
|
return null;
|
|
51
105
|
}
|
|
52
106
|
|
|
@@ -75,20 +129,27 @@ export function getGitLabConfig() {
|
|
|
75
129
|
|
|
76
130
|
/**
|
|
77
131
|
* 获取 AI 配置(ARK API Key)
|
|
132
|
+
* 注意:这里不再自动读取 guardConfig,因为需要 AI 客户端才能生成
|
|
78
133
|
*/
|
|
79
134
|
export function getAIConfig() {
|
|
80
135
|
// 从环境变量读取 ARK_API_KEY
|
|
81
136
|
const arkApiKey = process.env.ARK_API_KEY || '';
|
|
82
137
|
|
|
83
|
-
// 从配置文件读取其他配置
|
|
84
|
-
const guardConfig = readAIReviewGuardConfig();
|
|
85
|
-
|
|
86
138
|
return {
|
|
87
139
|
arkApiKey,
|
|
88
|
-
guardConfig,
|
|
140
|
+
guardConfig: null, // 延迟加载,在需要时通过 loadGuardConfig 加载
|
|
89
141
|
};
|
|
90
142
|
}
|
|
91
143
|
|
|
144
|
+
/**
|
|
145
|
+
* 加载 Guard 配置(支持 AI 生成)
|
|
146
|
+
* @param {Object} aiClient - AI 客户端
|
|
147
|
+
* @returns {Promise<Object>} Guard 配置
|
|
148
|
+
*/
|
|
149
|
+
export async function loadGuardConfig(aiClient = null) {
|
|
150
|
+
return await readAIReviewGuardConfig(aiClient);
|
|
151
|
+
}
|
|
152
|
+
|
|
92
153
|
/**
|
|
93
154
|
* 获取项目配置(支持自动检测 CI 环境)
|
|
94
155
|
*/
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 项目分析器 - 用于分析项目结构和技术栈
|
|
3
|
+
* 当找不到 reviewguard.md 文件时使用
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 递归遍历目录,收集文件信息
|
|
11
|
+
* @param {string} dir - 目录路径
|
|
12
|
+
* @param {number} maxDepth - 最大深度
|
|
13
|
+
* @param {number} currentDepth - 当前深度
|
|
14
|
+
* @returns {Array} 文件列表
|
|
15
|
+
*/
|
|
16
|
+
function walkDirectory(dir, maxDepth = 3, currentDepth = 0) {
|
|
17
|
+
if (currentDepth >= maxDepth) return [];
|
|
18
|
+
|
|
19
|
+
const files = [];
|
|
20
|
+
const ignorePatterns = [
|
|
21
|
+
'node_modules',
|
|
22
|
+
'.git',
|
|
23
|
+
'dist',
|
|
24
|
+
'build',
|
|
25
|
+
'coverage',
|
|
26
|
+
'.next',
|
|
27
|
+
'.nuxt',
|
|
28
|
+
'out',
|
|
29
|
+
'public',
|
|
30
|
+
'vendor',
|
|
31
|
+
'__pycache__',
|
|
32
|
+
'.pytest_cache',
|
|
33
|
+
'.venv',
|
|
34
|
+
'venv',
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
39
|
+
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
// 跳过隐藏文件和忽略的目录
|
|
42
|
+
if (entry.name.startsWith('.') || ignorePatterns.includes(entry.name)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const fullPath = path.join(dir, entry.name);
|
|
47
|
+
|
|
48
|
+
if (entry.isDirectory()) {
|
|
49
|
+
files.push(...walkDirectory(fullPath, maxDepth, currentDepth + 1));
|
|
50
|
+
} else if (entry.isFile()) {
|
|
51
|
+
files.push(fullPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// 忽略权限错误
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return files;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 检测项目技术栈
|
|
63
|
+
* @param {string} projectRoot - 项目根目录
|
|
64
|
+
* @returns {Object} 技术栈信息
|
|
65
|
+
*/
|
|
66
|
+
export function detectTechStack(projectRoot) {
|
|
67
|
+
const techStack = {
|
|
68
|
+
languages: new Set(),
|
|
69
|
+
frameworks: new Set(),
|
|
70
|
+
tools: new Set(),
|
|
71
|
+
packageManager: null,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// 检查配置文件
|
|
75
|
+
const configFiles = {
|
|
76
|
+
'package.json': () => {
|
|
77
|
+
try {
|
|
78
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf-8'));
|
|
79
|
+
|
|
80
|
+
// 检测包管理器
|
|
81
|
+
if (fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) {
|
|
82
|
+
techStack.packageManager = 'pnpm';
|
|
83
|
+
} else if (fs.existsSync(path.join(projectRoot, 'yarn.lock'))) {
|
|
84
|
+
techStack.packageManager = 'yarn';
|
|
85
|
+
} else if (fs.existsSync(path.join(projectRoot, 'package-lock.json'))) {
|
|
86
|
+
techStack.packageManager = 'npm';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 检测框架和库
|
|
90
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
91
|
+
|
|
92
|
+
// 前端框架
|
|
93
|
+
if (deps.react) techStack.frameworks.add('React');
|
|
94
|
+
if (deps.vue) techStack.frameworks.add('Vue');
|
|
95
|
+
if (deps.next) techStack.frameworks.add('Next.js');
|
|
96
|
+
if (deps.nuxt) techStack.frameworks.add('Nuxt.js');
|
|
97
|
+
if (deps.svelte) techStack.frameworks.add('Svelte');
|
|
98
|
+
if (deps.angular || deps['@angular/core']) techStack.frameworks.add('Angular');
|
|
99
|
+
|
|
100
|
+
// UI 库
|
|
101
|
+
if (deps['@radix-ui/react-dialog'] || deps['@radix-ui/react-dropdown-menu']) {
|
|
102
|
+
techStack.frameworks.add('Radix UI');
|
|
103
|
+
}
|
|
104
|
+
if (deps['@headlessui/react']) techStack.frameworks.add('Headless UI');
|
|
105
|
+
|
|
106
|
+
// 构建工具
|
|
107
|
+
if (deps.vite) techStack.tools.add('Vite');
|
|
108
|
+
if (deps.webpack) techStack.tools.add('Webpack');
|
|
109
|
+
if (deps.rollup) techStack.tools.add('Rollup');
|
|
110
|
+
if (deps.esbuild) techStack.tools.add('esbuild');
|
|
111
|
+
|
|
112
|
+
// 样式方案
|
|
113
|
+
if (deps.tailwindcss) techStack.tools.add('Tailwind CSS');
|
|
114
|
+
if (deps['styled-components']) techStack.tools.add('Styled Components');
|
|
115
|
+
if (deps['@emotion/react']) techStack.tools.add('Emotion');
|
|
116
|
+
|
|
117
|
+
// TypeScript
|
|
118
|
+
if (deps.typescript || fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) {
|
|
119
|
+
techStack.languages.add('TypeScript');
|
|
120
|
+
} else {
|
|
121
|
+
techStack.languages.add('JavaScript');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 表格库
|
|
125
|
+
if (deps['@tanstack/react-table']) techStack.frameworks.add('@tanstack/react-table');
|
|
126
|
+
|
|
127
|
+
// Linter
|
|
128
|
+
if (deps.eslint) techStack.tools.add('ESLint');
|
|
129
|
+
if (deps.prettier) techStack.tools.add('Prettier');
|
|
130
|
+
} catch (error) {
|
|
131
|
+
// 忽略解析错误
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
'requirements.txt': () => {
|
|
135
|
+
techStack.languages.add('Python');
|
|
136
|
+
techStack.packageManager = 'pip';
|
|
137
|
+
try {
|
|
138
|
+
const content = fs.readFileSync(path.join(projectRoot, 'requirements.txt'), 'utf-8');
|
|
139
|
+
if (content.includes('django')) techStack.frameworks.add('Django');
|
|
140
|
+
if (content.includes('flask')) techStack.frameworks.add('Flask');
|
|
141
|
+
if (content.includes('fastapi')) techStack.frameworks.add('FastAPI');
|
|
142
|
+
} catch (error) {
|
|
143
|
+
// 忽略读取错误
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
'Pipfile': () => {
|
|
147
|
+
techStack.languages.add('Python');
|
|
148
|
+
techStack.packageManager = 'pipenv';
|
|
149
|
+
},
|
|
150
|
+
'pyproject.toml': () => {
|
|
151
|
+
techStack.languages.add('Python');
|
|
152
|
+
techStack.packageManager = 'poetry';
|
|
153
|
+
},
|
|
154
|
+
'Gemfile': () => {
|
|
155
|
+
techStack.languages.add('Ruby');
|
|
156
|
+
techStack.packageManager = 'bundler';
|
|
157
|
+
techStack.frameworks.add('Ruby on Rails');
|
|
158
|
+
},
|
|
159
|
+
'go.mod': () => {
|
|
160
|
+
techStack.languages.add('Go');
|
|
161
|
+
techStack.packageManager = 'go modules';
|
|
162
|
+
},
|
|
163
|
+
'Cargo.toml': () => {
|
|
164
|
+
techStack.languages.add('Rust');
|
|
165
|
+
techStack.packageManager = 'Cargo';
|
|
166
|
+
},
|
|
167
|
+
'composer.json': () => {
|
|
168
|
+
techStack.languages.add('PHP');
|
|
169
|
+
techStack.packageManager = 'Composer';
|
|
170
|
+
try {
|
|
171
|
+
const composer = JSON.parse(fs.readFileSync(path.join(projectRoot, 'composer.json'), 'utf-8'));
|
|
172
|
+
const deps = { ...composer.require, ...composer['require-dev'] };
|
|
173
|
+
if (deps.laravel) techStack.frameworks.add('Laravel');
|
|
174
|
+
if (deps.symfony) techStack.frameworks.add('Symfony');
|
|
175
|
+
} catch (error) {
|
|
176
|
+
// 忽略解析错误
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
'pom.xml': () => {
|
|
180
|
+
techStack.languages.add('Java');
|
|
181
|
+
techStack.packageManager = 'Maven';
|
|
182
|
+
techStack.frameworks.add('Spring Boot');
|
|
183
|
+
},
|
|
184
|
+
'build.gradle': () => {
|
|
185
|
+
techStack.languages.add('Java/Kotlin');
|
|
186
|
+
techStack.packageManager = 'Gradle';
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// 检查配置文件
|
|
191
|
+
for (const [filename, detector] of Object.entries(configFiles)) {
|
|
192
|
+
if (fs.existsSync(path.join(projectRoot, filename))) {
|
|
193
|
+
detector();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
languages: Array.from(techStack.languages),
|
|
199
|
+
frameworks: Array.from(techStack.frameworks),
|
|
200
|
+
tools: Array.from(techStack.tools),
|
|
201
|
+
packageManager: techStack.packageManager,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 收集项目的关键文件信息
|
|
207
|
+
* @param {string} projectRoot - 项目根目录
|
|
208
|
+
* @param {number} maxFiles - 最大文件数量
|
|
209
|
+
* @returns {Array} 文件信息列表
|
|
210
|
+
*/
|
|
211
|
+
export function collectKeyFiles(projectRoot, maxFiles = 20) {
|
|
212
|
+
const files = walkDirectory(projectRoot, 3);
|
|
213
|
+
|
|
214
|
+
// 文件扩展名优先级(越重要的排在前面)
|
|
215
|
+
const extensionPriority = {
|
|
216
|
+
'.ts': 10,
|
|
217
|
+
'.tsx': 10,
|
|
218
|
+
'.js': 9,
|
|
219
|
+
'.jsx': 9,
|
|
220
|
+
'.vue': 9,
|
|
221
|
+
'.py': 9,
|
|
222
|
+
'.go': 9,
|
|
223
|
+
'.rs': 9,
|
|
224
|
+
'.java': 9,
|
|
225
|
+
'.php': 9,
|
|
226
|
+
'.rb': 9,
|
|
227
|
+
'.cs': 9,
|
|
228
|
+
'.swift': 9,
|
|
229
|
+
'.kt': 9,
|
|
230
|
+
'.md': 5,
|
|
231
|
+
'.json': 3,
|
|
232
|
+
'.yaml': 3,
|
|
233
|
+
'.yml': 3,
|
|
234
|
+
'.toml': 3,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// 文件名优先级
|
|
238
|
+
const filenamePriority = {
|
|
239
|
+
'package.json': 100,
|
|
240
|
+
'tsconfig.json': 90,
|
|
241
|
+
'vite.config': 80,
|
|
242
|
+
'webpack.config': 80,
|
|
243
|
+
'tailwind.config': 80,
|
|
244
|
+
'README': 70,
|
|
245
|
+
'index': 60,
|
|
246
|
+
'main': 60,
|
|
247
|
+
'app': 60,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// 计算文件优先级
|
|
251
|
+
const fileInfos = files.map(filePath => {
|
|
252
|
+
const ext = path.extname(filePath);
|
|
253
|
+
const basename = path.basename(filePath, ext);
|
|
254
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
255
|
+
|
|
256
|
+
let priority = extensionPriority[ext] || 1;
|
|
257
|
+
|
|
258
|
+
// 根据文件名增加优先级
|
|
259
|
+
for (const [key, value] of Object.entries(filenamePriority)) {
|
|
260
|
+
if (basename.includes(key)) {
|
|
261
|
+
priority += value;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 项目根目录的文件优先级更高
|
|
266
|
+
const depth = relativePath.split(path.sep).length - 1;
|
|
267
|
+
priority -= depth * 5;
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
path: relativePath,
|
|
271
|
+
fullPath: filePath,
|
|
272
|
+
priority,
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// 排序并选择最重要的文件
|
|
277
|
+
fileInfos.sort((a, b) => b.priority - a.priority);
|
|
278
|
+
|
|
279
|
+
return fileInfos.slice(0, maxFiles).map(info => ({
|
|
280
|
+
path: info.path,
|
|
281
|
+
size: fs.statSync(info.fullPath).size,
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 读取关键文件的内容片段
|
|
287
|
+
* @param {string} projectRoot - 项目根目录
|
|
288
|
+
* @param {Array} keyFiles - 关键文件列表
|
|
289
|
+
* @param {number} maxLines - 每个文件最大读取行数
|
|
290
|
+
* @returns {Array} 文件内容片段
|
|
291
|
+
*/
|
|
292
|
+
export function readFileSnippets(projectRoot, keyFiles, maxLines = 50) {
|
|
293
|
+
const snippets = [];
|
|
294
|
+
|
|
295
|
+
for (const fileInfo of keyFiles.slice(0, 10)) { // 只读取前10个文件
|
|
296
|
+
try {
|
|
297
|
+
const fullPath = path.join(projectRoot, fileInfo.path);
|
|
298
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
299
|
+
const lines = content.split('\n');
|
|
300
|
+
|
|
301
|
+
// 如果文件太长,只取前面的部分
|
|
302
|
+
const snippet = lines.slice(0, maxLines).join('\n');
|
|
303
|
+
|
|
304
|
+
snippets.push({
|
|
305
|
+
path: fileInfo.path,
|
|
306
|
+
lines: Math.min(lines.length, maxLines),
|
|
307
|
+
totalLines: lines.length,
|
|
308
|
+
content: snippet,
|
|
309
|
+
});
|
|
310
|
+
} catch (error) {
|
|
311
|
+
// 忽略读取错误(可能是二进制文件)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return snippets;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 分析项目结构
|
|
320
|
+
* @param {string} projectRoot - 项目根目录
|
|
321
|
+
* @returns {Object} 项目分析结果
|
|
322
|
+
*/
|
|
323
|
+
export function analyzeProject(projectRoot = process.cwd()) {
|
|
324
|
+
console.log('📊 正在分析项目结构...');
|
|
325
|
+
|
|
326
|
+
const techStack = detectTechStack(projectRoot);
|
|
327
|
+
const keyFiles = collectKeyFiles(projectRoot);
|
|
328
|
+
const snippets = readFileSnippets(projectRoot, keyFiles);
|
|
329
|
+
|
|
330
|
+
console.log('✅ 项目分析完成');
|
|
331
|
+
console.log(` - 语言: ${techStack.languages.join(', ') || '未知'}`);
|
|
332
|
+
console.log(` - 框架: ${techStack.frameworks.join(', ') || '未知'}`);
|
|
333
|
+
console.log(` - 工具: ${techStack.tools.join(', ') || '未知'}`);
|
|
334
|
+
console.log(` - 关键文件: ${keyFiles.length} 个`);
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
techStack,
|
|
338
|
+
keyFiles,
|
|
339
|
+
snippets,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export default {
|
|
344
|
+
detectTechStack,
|
|
345
|
+
collectKeyFiles,
|
|
346
|
+
readFileSnippets,
|
|
347
|
+
analyzeProject,
|
|
348
|
+
};
|
|
349
|
+
|
package/lib/prompt-tools.js
CHANGED
|
@@ -379,6 +379,147 @@ export function buildFileReviewWithImpactMessages(fileName, meaningfulChanges, i
|
|
|
379
379
|
];
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
/**
|
|
383
|
+
* 构建生成审核提示词的消息(当找不到 reviewguard.md 时使用)
|
|
384
|
+
* @param {Object} projectAnalysis - 项目分析结果
|
|
385
|
+
* @returns {Array} 消息数组
|
|
386
|
+
*/
|
|
387
|
+
export function buildGenerateGuardPromptMessages(projectAnalysis) {
|
|
388
|
+
const { techStack, keyFiles, snippets } = projectAnalysis;
|
|
389
|
+
|
|
390
|
+
let userMessage = `我需要你根据这个项目的技术栈和代码结构,生成一份适合该项目的 AI 代码审查规则文档(reviewguard.md)。
|
|
391
|
+
|
|
392
|
+
## 项目技术栈
|
|
393
|
+
|
|
394
|
+
**编程语言**: ${techStack.languages.join(', ') || '未知'}
|
|
395
|
+
**框架/库**: ${techStack.frameworks.join(', ') || '无'}
|
|
396
|
+
**构建工具**: ${techStack.tools.join(', ') || '无'}
|
|
397
|
+
**包管理器**: ${techStack.packageManager || '未知'}
|
|
398
|
+
|
|
399
|
+
## 项目关键文件
|
|
400
|
+
|
|
401
|
+
共发现 ${keyFiles.length} 个关键文件:
|
|
402
|
+
|
|
403
|
+
${keyFiles.slice(0, 15).map((file, i) => `${i + 1}. ${file.path} (${(file.size / 1024).toFixed(1)} KB)`).join('\n')}
|
|
404
|
+
|
|
405
|
+
## 代码片段示例
|
|
406
|
+
|
|
407
|
+
${snippets.map((snippet, i) => `
|
|
408
|
+
### 文件 ${i + 1}: ${snippet.path} (共 ${snippet.totalLines} 行,显示前 ${snippet.lines} 行)
|
|
409
|
+
|
|
410
|
+
\`\`\`
|
|
411
|
+
${snippet.content}
|
|
412
|
+
\`\`\`
|
|
413
|
+
`).join('\n')}
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## 要求
|
|
418
|
+
|
|
419
|
+
请根据以上项目信息,生成一份完整的 **reviewguard.md** 文档内容。文档应包含:
|
|
420
|
+
|
|
421
|
+
### 1. 项目概述
|
|
422
|
+
- 简要描述项目的技术栈和架构
|
|
423
|
+
- 列出主要使用的框架和工具
|
|
424
|
+
|
|
425
|
+
### 2. 开发原则
|
|
426
|
+
- 根据项目技术栈,列出该项目应遵循的开发原则
|
|
427
|
+
- 例如:React 项目应包含组件设计原则、Hooks 使用规范等
|
|
428
|
+
- 例如:使用 TypeScript 的项目应强调类型安全
|
|
429
|
+
|
|
430
|
+
### 3. AI 代码审查规则
|
|
431
|
+
必须包含以下审查类别(按严重程度分类):
|
|
432
|
+
|
|
433
|
+
#### 破坏性变更检查(严重程度 8-10 分)
|
|
434
|
+
- 删除/重命名导出的函数、组件、类型
|
|
435
|
+
- API 签名变更
|
|
436
|
+
- 必须检查是否有其他文件引用
|
|
437
|
+
|
|
438
|
+
#### 安全漏洞(严重程度 7-10 分)
|
|
439
|
+
- XSS、SQL 注入等
|
|
440
|
+
- 敏感信息硬编码
|
|
441
|
+
- 用户输入未校验
|
|
442
|
+
|
|
443
|
+
#### 性能问题(严重程度 5-8 分)
|
|
444
|
+
- 不必要的重渲染(React)
|
|
445
|
+
- 内存泄漏
|
|
446
|
+
- 大列表未优化
|
|
447
|
+
|
|
448
|
+
#### 类型安全(严重程度 4-7 分,TypeScript 项目必须包含)
|
|
449
|
+
- 滥用 any 类型
|
|
450
|
+
- 缺少类型定义
|
|
451
|
+
- 不安全的类型断言
|
|
452
|
+
|
|
453
|
+
#### 代码规范(严重程度 1-4 分)
|
|
454
|
+
- 命名不规范
|
|
455
|
+
- 注释缺失
|
|
456
|
+
- 函数过长
|
|
457
|
+
|
|
458
|
+
### 4. 回复格式规范(非常重要!)
|
|
459
|
+
|
|
460
|
+
必须包含统一的回复格式,要求 AI 按照以下结构返回审查意见:
|
|
461
|
+
|
|
462
|
+
\`\`\`
|
|
463
|
+
【严重程度评分:X/10】
|
|
464
|
+
|
|
465
|
+
📋 问题描述:
|
|
466
|
+
[一句话概括问题]
|
|
467
|
+
|
|
468
|
+
🔍 影响分析:
|
|
469
|
+
• 当前文件:[具体影响]
|
|
470
|
+
• 受影响的其他文件:[文件名和行号]
|
|
471
|
+
• 可能后果:[具体说明]
|
|
472
|
+
|
|
473
|
+
💡 改进建议:
|
|
474
|
+
1. [具体操作步骤]
|
|
475
|
+
2. [注意事项]
|
|
476
|
+
\`\`\`
|
|
477
|
+
|
|
478
|
+
### 5. 严重程度评分标准
|
|
479
|
+
|
|
480
|
+
定义 0-10 分的评分标准:
|
|
481
|
+
- 1-3 分:低风险(代码风格、命名规范)
|
|
482
|
+
- 4-6 分:中等风险(违反最佳实践、性能问题)
|
|
483
|
+
- 7-8 分:高风险(不兼容的变更、安全漏洞)
|
|
484
|
+
- 9-10 分:严重风险(破坏性变更、系统崩溃)
|
|
485
|
+
|
|
486
|
+
### 6. 示例(必须包含 3-5 个实际示例)
|
|
487
|
+
|
|
488
|
+
提供具体的审查示例,展示不同严重程度的问题和回复格式
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
**输出要求:**
|
|
493
|
+
1. 直接输出 reviewguard.md 的完整 Markdown 内容
|
|
494
|
+
2. 内容必须用**中文**编写
|
|
495
|
+
3. 必须针对该项目的技术栈定制审查规则
|
|
496
|
+
4. 必须包含清晰的回复格式规范和评分标准
|
|
497
|
+
5. 必须包含 3-5 个实际示例
|
|
498
|
+
6. 内容要详细、实用、可操作
|
|
499
|
+
|
|
500
|
+
请开始生成 reviewguard.md 的内容:`;
|
|
501
|
+
|
|
502
|
+
return [
|
|
503
|
+
{
|
|
504
|
+
role: 'system',
|
|
505
|
+
content: `你是一个专业的代码审查规范设计专家。你的任务是根据项目的技术栈和代码结构,生成一份详细的 AI 代码审查规则文档(reviewguard.md)。
|
|
506
|
+
|
|
507
|
+
这份文档将用于指导 AI 进行代码审查,必须:
|
|
508
|
+
1. 针对具体技术栈定制(不要泛泛而谈)
|
|
509
|
+
2. 包含清晰的严重程度评分标准(0-10分)
|
|
510
|
+
3. 定义统一的回复格式(必须包含 emoji、评分、问题描述、影响分析、改进建议)
|
|
511
|
+
4. 提供实际的审查示例
|
|
512
|
+
5. 全部使用中文
|
|
513
|
+
|
|
514
|
+
生成的文档应该像一份专业的代码审查规范手册,让 AI 能够准确理解如何审查这个项目的代码。`
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
role: 'user',
|
|
518
|
+
content: userMessage
|
|
519
|
+
}
|
|
520
|
+
];
|
|
521
|
+
}
|
|
522
|
+
|
|
382
523
|
// 导出函数
|
|
383
524
|
export default {
|
|
384
525
|
buildSystemPrompt,
|
|
@@ -386,5 +527,6 @@ export default {
|
|
|
386
527
|
buildFileReviewMessages,
|
|
387
528
|
buildFileReviewWithImpactPrompt,
|
|
388
529
|
buildFileReviewWithImpactMessages,
|
|
530
|
+
buildGenerateGuardPromptMessages,
|
|
389
531
|
};
|
|
390
532
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitlab-ai-review",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "GitLab AI Review SDK with Impact Analysis - 支持影响分析、删除符号检测、注释代码识别、文件内部冲突检查、智能文件过滤的智能代码审查工具",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -30,16 +30,16 @@
|
|
|
30
30
|
"merge-request",
|
|
31
31
|
"automation"
|
|
32
32
|
],
|
|
33
|
-
"author": "
|
|
33
|
+
"author": "dengjiang1",
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/
|
|
37
|
+
"url": "https://github.com/dengjiang1/gitlab-ai-review.git"
|
|
38
38
|
},
|
|
39
39
|
"bugs": {
|
|
40
|
-
"url": "https://github.com/
|
|
40
|
+
"url": "https://github.com/dengjiang1/gitlab-ai-review/issues"
|
|
41
41
|
},
|
|
42
|
-
"homepage": "https://github.com/
|
|
42
|
+
"homepage": "https://github.com/dengjiang1/gitlab-ai-review#readme",
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=18.0.0"
|
|
45
45
|
},
|