goodiffer 1.2.0 → 1.3.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 CHANGED
@@ -13,7 +13,9 @@ AI-powered git diff analyzer for code review - 基于 AI 的 Git Diff 智能分
13
13
  - 🔗 检测代码关联性风险
14
14
  - 📋 生成可复制的修复提示词,方便在 Claude Code / Codex 中使用
15
15
  - 🌐 支持第三方 API 代理
16
- - 🔮 **NEW** 支持代码上下文获取 (Tool Use),AI 可按需读取相关源码
16
+ - 🔮 支持代码上下文获取 (Tool Use),AI 可按需读取相关源码
17
+ - ⚡ **NEW** LSP 集成 - 提供类型信息、引用查找、符号导航等智能功能
18
+ - 🧠 **NEW** Codex 深度审查 - 基于 GPT-5.2 的 8 维度深度代码审查
17
19
 
18
20
  ## Installation
19
21
 
@@ -79,19 +81,135 @@ goodiffer -c abc123 --context # 分析指定 commit,启用上下文
79
81
 
80
82
  ### 代码上下文模式 (--context)
81
83
 
82
- 启用 `--context` 选项后,AI 在分析代码时可以:
84
+ 启用 `--context` 选项后,AI 在分析代码时可以使用以下工具:
83
85
 
86
+ #### 基础工具
84
87
  1. **read_file** - 读取项目中的源文件,了解函数/类的具体实现
85
88
  2. **find_definition** - 查找函数、类、变量的定义位置
86
89
  3. **search_code** - 在项目中搜索代码模式
87
90
  4. **list_files** - 列出目录结构
88
91
 
92
+ #### LSP 增强工具 (TypeScript/JavaScript)
93
+ 5. **get_type_info** - 获取变量、函数的类型信息和文档
94
+ 6. **find_references** - 查找符号在整个项目中的所有引用位置
95
+ 7. **get_document_symbols** - 获取文件中的所有符号列表(函数、类、变量等)
96
+ 8. **go_to_definition** - 使用 LSP 精确定位符号的定义位置
97
+
98
+ **LSP 工具优势:**
99
+ - ✅ 精确的类型分析(支持 TypeScript/JavaScript)
100
+ - ✅ 跨文件引用查找
101
+ - ✅ 快速了解文件结构
102
+ - ✅ 自动回退到正则搜索
103
+
89
104
  这使得 AI 能够:
90
105
  - 验证被调用函数的实现是否正确
91
106
  - 检查类型定义和接口
92
107
  - 发现潜在的关联影响
108
+ - 了解代码的调用关系和依赖
109
+
110
+ > **注意**: 此功能需要 Claude 模型(使用 Tool Use API)。LSP 功能自动启用,无需额外配置。
111
+
112
+ ### Codex 深度代码审查 (NEW)
113
+
114
+ 使用 OpenAI Codex (GPT-5.2) 进行深度代码审查,提供 8 维度质量评估和高推理分析。
115
+
116
+ ```bash
117
+ # 使用 Codex 深度审查最近一次 commit
118
+ goodiffer codex
119
+
120
+ # 审查暂存区
121
+ goodiffer codex -s
122
+
123
+ # 审查指定 commit
124
+ goodiffer codex -c abc123
125
+
126
+ # 指定推理强度 (low/medium/high)
127
+ goodiffer codex --reasoning high
128
+ ```
129
+
130
+ #### Codex Review 特性
131
+
132
+ **8 维度质量评估:**
133
+ 1. **Code Style & Formatting** - 代码风格与格式
134
+ 2. **Security & Compliance** - 安全性与合规性
135
+ 3. **Error Handling & Logging** - 错误处理与日志
136
+ 4. **Readability & Maintainability** - 可读性与可维护性
137
+ 5. **Performance & Scalability** - 性能与可扩展性
138
+ 6. **Testing & Quality Assurance** - 测试与质量保证
139
+ 7. **Documentation & Version Control** - 文档与版本控制
140
+ 8. **Accessibility & Internationalization** - 可访问性与国际化
141
+
142
+ **评分系统:**
143
+ - ⭐ **Extraordinary** (90-100) - 卓越质量
144
+ - ✓ **Acceptable** (70-89) - 符合标准
145
+ - ⚠️ **Poor** (0-69) - 需要改进
146
+
147
+ **高推理模式:**
148
+ - 使用 `reasoning: { effort: "high" }` 进行深度分析
149
+ - 提供置信度评分(0.0-1.0)
150
+ - 生成正确性判断(patch is correct / incorrect)
151
+
152
+ **结构化输出:**
153
+ - 使用 JSON Schema 定义输出格式(降低 35% 失败率)
154
+ - 精确的文件/行号引用
155
+ - 优先级分级(P0-P3)
156
+
157
+ **输出示例:**
158
+ ```
159
+ ╭──────────────────────────────────────────────────────────╮
160
+ │ Codex Deep Code Review Report │
161
+ ╰──────────────────────────────────────────────────────────╯
162
+
163
+ 📝 Commit: abc1234
164
+ 📋 Message: feat: add user authentication
165
+
166
+ 📊 Summary: 添加了用户认证功能...
167
+
168
+ ═══════════════════════════════════════════════════════════
169
+
170
+ 🎯 8-Dimensional Quality Assessment
171
+
172
+ 1. Security & Compliance
173
+ 🌟 Rating: EXTRAORDINARY | Score: 95/100
174
+ 密码加密使用了 bcrypt,符合安全标准
175
+
176
+ 2. Error Handling & Logging
177
+ ⚠️ Rating: POOR | Score: 60/100
178
+ 缺少异常处理和日志记录
179
+ ▸ API 调用未处理错误情况
180
+ ▸ 缺少关键操作的日志
181
+
182
+ ═══════════════════════════════════════════════════════════
183
+
184
+ ⚖️ Overall Assessment
185
+
186
+ ✅ Correctness: PATCH IS CORRECT
187
+ 📈 Confidence: 85% (High)
188
+ 💡 Explanation: 代码功能正确,但需改进错误处理
189
+
190
+ ═══════════════════════════════════════════════════════════
191
+
192
+ 🔍 Findings (2)
193
+
194
+ 🟠 P1 - URGENT (1)
195
+
196
+ [A] [P1] API 调用缺少错误处理
197
+
198
+ Location: src/auth/api.js:45-52
199
+ Confidence: 90% (Very High)
200
+ Dimension: Error Handling & Logging
201
+
202
+ 在 loginUser 函数中直接调用 API 而未处理可能的网络错误...
203
+
204
+ 💡 Suggestion: 添加 try-catch 块处理异常
205
+
206
+ 📋 修复提示词 (复制到 Claude Code/Codex):
207
+ ┌──────────────────────────────────────────────────┐
208
+ │ 在 src/auth/api.js:45 添加 try-catch 处理异常 │
209
+ └──────────────────────────────────────────────────┘
210
+ ```
93
211
 
94
- > **注意**: 此功能需要 Claude 模型(使用 Tool Use API),建议在 Claude Code 环境中使用。
212
+ > **推荐使用场景**: 关键代码审查、生产环境发布前、安全敏感功能
95
213
 
96
214
  ### 配置管理
97
215
 
package/bin/goodiffer.js CHANGED
@@ -11,10 +11,10 @@ import { reportCommand } from '../src/commands/report.js';
11
11
 
12
12
  program
13
13
  .name('goodiffer')
14
- .description('AI-powered git diff analyzer for code review')
15
- .version('1.1.1');
14
+ .description('AI-powered git diff analyzer with Codex deep code review')
15
+ .version('1.2.1');
16
16
 
17
- // 默认命令 - 分析
17
+ // 默认命令 - Codex 深度分析
18
18
  program
19
19
  .option('-s, --staged', '分析暂存区的更改')
20
20
  .option('-c, --commit <sha>', '分析指定的 commit')
@@ -22,7 +22,7 @@ program
22
22
  .option('--to <sha>', '结束 commit (与 --from 配合使用)')
23
23
  .option('-n <number>', '分析最近 n 条 commit (n <= 10), 或与 -m 配合表示起始位置')
24
24
  .option('-m <number>', '与 -n 配合使用,表示结束位置 (m-n <= 10)')
25
- .option('--context', '启用代码上下文获取 (需要 Claude Code 环境)')
25
+ .option('--reasoning <level>', '推理强度: low, medium, high, none (默认 high)', 'high')
26
26
  .option('--no-save', '不保存到数据库')
27
27
  .action(async (options) => {
28
28
  await analyzeCommand(options);
@@ -88,7 +88,7 @@ program
88
88
  .option('--since <date>', '开始日期 (YYYY-MM-DD)')
89
89
  .option('--until <date>', '结束日期 (YYYY-MM-DD)')
90
90
  .option('-o, --output <path>', '输出文件路径')
91
- .option('--open', '生成后自动打开')
91
+ .option('--no-open', '不自动打开浏览器')
92
92
  .option('-f, --force', '强制重新生成 (忽略缓存)')
93
93
  .action(async (options) => {
94
94
  await reportCommand(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goodiffer",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "AI-powered git diff analyzer for code review - 智能代码审查工具",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,25 +2,35 @@ import ora from 'ora';
2
2
  import { getConfig, isConfigured } from '../utils/config-store.js';
3
3
  import { GitService } from '../services/git.js';
4
4
  import { AIClient } from '../services/ai-client.js';
5
- import { buildReviewPrompt, buildReviewPromptWithTools } from '../prompts/review-prompt.js';
6
- import { generateReport } from '../services/reporter.js';
5
+ import { buildCodexReviewPrompt } from '../prompts/codex-review-prompt.js';
6
+ import { generateCodexReport } from '../services/codex-reporter.js';
7
7
  import { getDatabase } from '../services/database.js';
8
- import { CodeContextService } from '../services/code-context.js';
9
- import { MCPClientService } from '../services/mcp-client.js';
10
8
  import logger from '../utils/logger.js';
11
9
 
12
- // 解析 AI 响应
13
- function parseAIResponse(response) {
14
- try {
15
- let jsonStr = response;
16
- const jsonMatch = response.match(/```(?:json)?\s*([\s\S]*?)```/);
17
- if (jsonMatch) {
18
- jsonStr = jsonMatch[1].trim();
19
- }
20
- return JSON.parse(jsonStr);
21
- } catch {
22
- return null;
10
+ /**
11
+ * 提取统计数据
12
+ */
13
+ function extractStats(result) {
14
+ const stats = {
15
+ p0: 0,
16
+ p1: 0,
17
+ p2: 0,
18
+ p3: 0,
19
+ risks: 0
20
+ };
21
+
22
+ if (result.findings) {
23
+ result.findings.forEach(f => {
24
+ const priority = f.priority || 3;
25
+ stats[`p${priority}`]++;
26
+ });
27
+ }
28
+
29
+ if (result.associationRisks) {
30
+ stats.risks = result.associationRisks.length;
23
31
  }
32
+
33
+ return stats;
24
34
  }
25
35
 
26
36
  export async function analyzeCommand(options) {
@@ -89,71 +99,49 @@ export async function analyzeCommand(options) {
89
99
  process.exit(1);
90
100
  }
91
101
 
92
- // 获取项目信息和分支
102
+ // 获取项目信息
93
103
  const projectName = await git.getProjectName();
94
104
  const branch = await git.getCurrentBranch();
105
+ const changedFiles = await git.getChangedFiles(
106
+ options.commit || (options.from ? `${options.from}..${options.to}` : 'HEAD~1')
107
+ );
95
108
 
96
109
  spinner.succeed('获取 Git 信息完成');
97
110
 
98
- // 检查是否启用代码上下文模式
99
- const useContext = options.context;
100
-
101
- // 如果启用上下文模式,提示信息
102
- if (useContext) {
103
- if (MCPClientService.isInClaudeCode()) {
104
- logger.info('已启用代码上下文模式 (MCP)');
105
- } else {
106
- logger.info('已启用代码上下文模式 (本地文件读取)');
107
- }
108
- }
109
-
110
- // 构建提示词
111
- const prompt = useContext
112
- ? buildReviewPromptWithTools(commitInfo.message, diff)
113
- : buildReviewPrompt(commitInfo.message, diff);
111
+ // 构建 Codex review 提示词
112
+ const prompt = buildCodexReviewPrompt(commitInfo.message, diff, {
113
+ repository: projectName,
114
+ baseSha: options.from || 'HEAD~1',
115
+ headSha: options.to || commitInfo.sha,
116
+ changedFiles
117
+ });
114
118
 
115
119
  // 调用 AI 分析
116
- spinner = ora('AI 正在分析代码...').start();
120
+ spinner = ora('Codex 深度分析中...').start();
117
121
 
118
122
  const aiClient = new AIClient(config);
119
- let response = '';
123
+ let result = null;
120
124
 
121
125
  try {
122
- if (useContext) {
123
- // 启用代码上下文的分析模式
124
- const codeContext = new CodeContextService(process.cwd());
125
- await codeContext.initialize(true); // 尝试启用 MCP
126
-
127
- const tools = CodeContextService.getToolDefinitions();
128
-
129
- response = await aiClient.analyzeWithTools(
130
- prompt,
131
- tools,
132
- async (toolName, toolInput) => {
133
- return await codeContext.executeTool(toolName, toolInput);
134
- },
135
- (progress) => {
136
- if (progress.type === 'iteration') {
137
- spinner.text = `AI 正在分析... (迭代 ${progress.iteration})`;
138
- } else if (progress.type === 'tool_calls') {
139
- const toolNames = progress.tools.map(t => t.name).join(', ');
140
- spinner.text = `AI 正在获取上下文: ${toolNames}`;
141
- } else if (progress.type === 'tool_result') {
142
- spinner.text = `AI 正在分析... (已获取 ${progress.tool} 结果)`;
143
- }
144
- },
145
- 10 // 增加最大迭代次数到 10
146
- );
147
-
148
- await codeContext.close();
149
- } else {
150
- // 普通分析模式
151
- response = await aiClient.analyzeStream(prompt, (chunk) => {
152
- spinner.text = `AI 正在分析... (${response.length} 字符)`;
153
- });
154
- }
126
+ // 使用 Codex 深度分析
127
+ const reasoningEffort = options.reasoning || 'high';
128
+
129
+ result = await aiClient.analyzeWithCodex(prompt, {
130
+ reasoningEffort: reasoningEffort,
131
+ onProgress: (progress) => {
132
+ if (progress.type === 'info') {
133
+ spinner.text = progress.message;
134
+ } else if (progress.type === 'analyzing') {
135
+ spinner.text = '🧠 Codex 深度分析中...';
136
+ } else if (progress.type === 'complete') {
137
+ spinner.succeed('✅ 分析完成');
138
+ } else if (progress.type === 'error') {
139
+ spinner.fail(`❌ ${progress.message}`);
140
+ }
141
+ }
142
+ });
155
143
  } catch (error) {
156
- spinner.fail('AI 分析失败');
144
+ spinner.fail('分析失败');
157
145
  if (error.message.includes('401')) {
158
146
  logger.error('API Key 无效或已过期');
159
147
  } else if (error.message.includes('403')) {
@@ -174,10 +162,8 @@ export async function analyzeCommand(options) {
174
162
  process.exit(1);
175
163
  }
176
164
 
177
- spinner.succeed('AI 分析完成');
178
-
179
- // 生成报告
180
- generateReport(response, commitInfo);
165
+ // 生成 Codex 格式报告
166
+ generateCodexReport(result, commitInfo);
181
167
 
182
168
  // 保存到数据库 (除非指定 --no-save)
183
169
  if (options.save !== false) {
@@ -190,44 +176,8 @@ export async function analyzeCommand(options) {
190
176
  // 获取或创建开发者
191
177
  const developer = db.getOrCreateDeveloper(author.email, author.name);
192
178
 
193
- // 解析 AI 响应
194
- const parsed = parseAIResponse(response);
195
-
196
179
  // 提取统计数据
197
- let summary = '';
198
- let commitMatch = false;
199
- let commitMatchReason = '';
200
- let errorCount = 0;
201
- let warningCount = 0;
202
- let infoCount = 0;
203
- let riskCount = 0;
204
- let issues = [];
205
- let associationRisks = [];
206
-
207
- if (parsed) {
208
- summary = parsed.summary || '';
209
- commitMatch = parsed.commitMatch || false;
210
- commitMatchReason = parsed.commitMatchReason || '';
211
-
212
- // 支持新格式 findings (按优先级统计)
213
- if (parsed.findings && Array.isArray(parsed.findings)) {
214
- issues = parsed.findings;
215
- errorCount = issues.filter(i => i.priority === 0 || i.priority === 1).length; // P0, P1 算 error
216
- warningCount = issues.filter(i => i.priority === 2).length; // P2 算 warning
217
- infoCount = issues.filter(i => i.priority === 3 || i.priority === undefined).length; // P3 算 info
218
- } else if (parsed.issues && Array.isArray(parsed.issues)) {
219
- // 兼容旧格式
220
- issues = parsed.issues;
221
- errorCount = issues.filter(i => i.level === 'error').length;
222
- warningCount = issues.filter(i => i.level === 'warning').length;
223
- infoCount = issues.filter(i => i.level === 'info').length;
224
- }
225
-
226
- if (parsed.associationRisks && Array.isArray(parsed.associationRisks)) {
227
- associationRisks = parsed.associationRisks;
228
- riskCount = associationRisks.length;
229
- }
230
- }
180
+ const stats = extractStats(result);
231
181
 
232
182
  // 保存 review 记录
233
183
  const reviewId = db.saveReview({
@@ -243,18 +193,20 @@ export async function analyzeCommand(options) {
243
193
  filesChanged: diffStats.filesChanged,
244
194
  insertions: diffStats.insertions,
245
195
  deletions: diffStats.deletions,
246
- diffContent: null, // 不存储 diff 内容以节省空间
247
- aiResponse: response,
248
- summary,
249
- commitMatch,
250
- commitMatchReason,
251
- errorCount,
252
- warningCount,
253
- infoCount,
254
- riskCount,
196
+ diffContent: null,
197
+ aiResponse: JSON.stringify(result, null, 2),
198
+ summary: result.summary || '',
199
+ commitMatch: result.commitMatch || false,
200
+ commitMatchReason: result.commitMatchReason || '',
201
+ errorCount: stats.p0 + stats.p1,
202
+ warningCount: stats.p2,
203
+ infoCount: stats.p3,
204
+ riskCount: stats.risks,
255
205
  modelUsed: config.model,
256
- issues,
257
- associationRisks
206
+ issues: result.findings || [],
207
+ associationRisks: result.associationRisks || [],
208
+ dimensions: result.dimensions || [],
209
+ overallAssessment: result.overall_assessment || {}
258
210
  });
259
211
 
260
212
  logger.success(`Review #${reviewId} 已保存到数据库`);
@@ -284,7 +236,6 @@ async function analyzeMultipleCommits(options, config, git) {
284
236
  }
285
237
 
286
238
  if (n !== null && m === null) {
287
- // 只有 -n,表示分析最近 n 条
288
239
  if (n <= 0 || n > 10) {
289
240
  logger.error('n 必须在 1-10 之间');
290
241
  process.exit(1);
@@ -292,7 +243,6 @@ async function analyzeMultipleCommits(options, config, git) {
292
243
  }
293
244
 
294
245
  if (n !== null && m !== null) {
295
- // 同时有 -n 和 -m,表示第 n 条到第 m 条
296
246
  if (n <= 0 || m <= 0) {
297
247
  logger.error('n 和 m 必须大于 0');
298
248
  process.exit(1);
@@ -313,10 +263,8 @@ async function analyzeMultipleCommits(options, config, git) {
313
263
  // 获取 commits
314
264
  let commits;
315
265
  if (m !== null) {
316
- // 第 n 条到第 m 条
317
266
  commits = await git.getCommitRange(n, m);
318
267
  } else {
319
- // 最近 n 条
320
268
  commits = await git.getRecentCommits(n);
321
269
  }
322
270
 
@@ -347,10 +295,9 @@ async function analyzeMultipleCommits(options, config, git) {
347
295
  const commit = commits[i];
348
296
  const shortSha = commit.sha.substring(0, 7);
349
297
 
350
- spinner = ora(`[${i + 1}/${commits.length}] 分析 commit ${shortSha}...`).start();
298
+ spinner = ora(`[${i + 1}/${commits.length}] Codex 分析 commit ${shortSha}...`).start();
351
299
 
352
300
  try {
353
- // 获取 diff
354
301
  const diff = await git.getCommitDiff(commit.sha);
355
302
  if (!diff || diff.trim() === '') {
356
303
  spinner.warn(`[${i + 1}/${commits.length}] commit ${shortSha} 没有代码变更,跳过`);
@@ -358,14 +305,26 @@ async function analyzeMultipleCommits(options, config, git) {
358
305
  }
359
306
 
360
307
  const diffStats = await git.getDiffStats(`${commit.sha}~1`, commit.sha);
308
+ const changedFiles = await git.getChangedFiles(commit.sha);
309
+
310
+ // 构建 Codex prompt
311
+ const prompt = buildCodexReviewPrompt(commit.message, diff, {
312
+ repository: projectName,
313
+ baseSha: `${commit.sha}~1`,
314
+ headSha: commit.sha,
315
+ changedFiles
316
+ });
361
317
 
362
- // 构建提示词并分析
363
- const prompt = buildReviewPrompt(commit.message, diff);
364
- let response = '';
318
+ let result = null;
365
319
 
366
320
  try {
367
- response = await aiClient.analyzeStream(prompt, (chunk) => {
368
- spinner.text = `[${i + 1}/${commits.length}] 分析 commit ${shortSha}... (${response.length} 字符)`;
321
+ result = await aiClient.analyzeWithCodex(prompt, {
322
+ reasoningEffort: options.reasoning || 'high',
323
+ onProgress: (progress) => {
324
+ if (progress.type === 'info') {
325
+ spinner.text = `[${i + 1}/${commits.length}] ${progress.message}`;
326
+ }
327
+ }
369
328
  });
370
329
  } catch (error) {
371
330
  spinner.fail(`[${i + 1}/${commits.length}] commit ${shortSha} 分析失败: ${error.message}`);
@@ -375,7 +334,7 @@ async function analyzeMultipleCommits(options, config, git) {
375
334
  spinner.succeed(`[${i + 1}/${commits.length}] commit ${shortSha} 分析完成`);
376
335
 
377
336
  // 生成报告
378
- generateReport(response, { sha: commit.sha, message: commit.message });
337
+ generateCodexReport(result, { sha: commit.sha, message: commit.message });
379
338
 
380
339
  // 保存到数据库
381
340
  if (options.save !== false) {
@@ -383,41 +342,7 @@ async function analyzeMultipleCommits(options, config, git) {
383
342
  const db = getDatabase();
384
343
  const project = db.getOrCreateProject(projectName, process.cwd());
385
344
  const developer = db.getOrCreateDeveloper(commit.author.email, commit.author.name);
386
- const parsed = parseAIResponse(response);
387
-
388
- let summary = '';
389
- let commitMatch = false;
390
- let commitMatchReason = '';
391
- let errorCount = 0;
392
- let warningCount = 0;
393
- let infoCount = 0;
394
- let riskCount = 0;
395
- let issues = [];
396
- let associationRisks = [];
397
-
398
- if (parsed) {
399
- summary = parsed.summary || '';
400
- commitMatch = parsed.commitMatch || false;
401
- commitMatchReason = parsed.commitMatchReason || '';
402
-
403
- // 支持新格式 findings (按优先级统计)
404
- if (parsed.findings && Array.isArray(parsed.findings)) {
405
- issues = parsed.findings;
406
- errorCount = issues.filter(i => i.priority === 0 || i.priority === 1).length;
407
- warningCount = issues.filter(i => i.priority === 2).length;
408
- infoCount = issues.filter(i => i.priority === 3 || i.priority === undefined).length;
409
- } else if (parsed.issues && Array.isArray(parsed.issues)) {
410
- issues = parsed.issues;
411
- errorCount = issues.filter(i => i.level === 'error').length;
412
- warningCount = issues.filter(i => i.level === 'warning').length;
413
- infoCount = issues.filter(i => i.level === 'info').length;
414
- }
415
-
416
- if (parsed.associationRisks && Array.isArray(parsed.associationRisks)) {
417
- associationRisks = parsed.associationRisks;
418
- riskCount = associationRisks.length;
419
- }
420
- }
345
+ const stats = extractStats(result);
421
346
 
422
347
  const reviewId = db.saveReview({
423
348
  projectId: project.id,
@@ -433,17 +358,19 @@ async function analyzeMultipleCommits(options, config, git) {
433
358
  insertions: diffStats.insertions,
434
359
  deletions: diffStats.deletions,
435
360
  diffContent: null,
436
- aiResponse: response,
437
- summary,
438
- commitMatch,
439
- commitMatchReason,
440
- errorCount,
441
- warningCount,
442
- infoCount,
443
- riskCount,
361
+ aiResponse: JSON.stringify(result, null, 2),
362
+ summary: result.summary || '',
363
+ commitMatch: result.commitMatch || false,
364
+ commitMatchReason: result.commitMatchReason || '',
365
+ errorCount: stats.p0 + stats.p1,
366
+ warningCount: stats.p2,
367
+ infoCount: stats.p3,
368
+ riskCount: stats.risks,
444
369
  modelUsed: config.model,
445
- issues,
446
- associationRisks
370
+ issues: result.findings || [],
371
+ associationRisks: result.associationRisks || [],
372
+ dimensions: result.dimensions || [],
373
+ overallAssessment: result.overall_assessment || {}
447
374
  });
448
375
 
449
376
  results.push({ commit, reviewId, success: true });
@@ -65,7 +65,8 @@ export async function reportCommand(options) {
65
65
  console.log();
66
66
  logger.success(`报告已保存到: ${outputPath}`);
67
67
 
68
- if (options.open) {
68
+ // 自动在浏览器中打开报告
69
+ if (options.open !== false) {
69
70
  await open(outputPath);
70
71
  }
71
72
 
@@ -137,9 +138,9 @@ export async function reportCommand(options) {
137
138
  console.log(` 状态: 使用缓存 (无新数据)`);
138
139
  }
139
140
 
140
- if (options.open) {
141
+ // 自动在浏览器中打开报告
142
+ if (options.open !== false) {
141
143
  console.log();
142
- logger.info('正在打开报告...');
143
144
  await open(outputPath);
144
145
  }
145
146
  }