goodiffer 1.0.0 → 1.0.1

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.
@@ -0,0 +1,139 @@
1
+ import chalk from 'chalk';
2
+ import dayjs from 'dayjs';
3
+ import { getDatabase } from '../services/database.js';
4
+ import { GitService } from '../services/git.js';
5
+ import logger from '../utils/logger.js';
6
+
7
+ export async function statsCommand(options) {
8
+ const db = getDatabase();
9
+ const git = new GitService();
10
+
11
+ // 日期范围
12
+ const dateRange = {};
13
+ if (options.since) {
14
+ dateRange.since = dayjs(options.since).startOf('day').toISOString();
15
+ }
16
+ if (options.until) {
17
+ dateRange.until = dayjs(options.until).endOf('day').toISOString();
18
+ }
19
+
20
+ if (options.developer) {
21
+ // 开发者统计
22
+ await showDeveloperStats(db, dateRange);
23
+ } else {
24
+ // 项目统计 (默认)
25
+ await showProjectStats(db, git, dateRange);
26
+ }
27
+ }
28
+
29
+ async function showProjectStats(db, git, dateRange) {
30
+ const projectName = await git.getProjectName();
31
+ const project = db.getProject(projectName);
32
+
33
+ if (!project) {
34
+ logger.info(`项目 "${projectName}" 暂无 Review 记录`);
35
+ console.log();
36
+ console.log('提示: 运行 goodiffer 分析代码后会自动保存记录');
37
+ return;
38
+ }
39
+
40
+ const stats = db.getProjectStats(project.id, dateRange);
41
+ const developerStats = db.getDeveloperStatsByProject(project.id, dateRange);
42
+
43
+ logger.title(`项目统计: ${projectName}`);
44
+
45
+ // 显示日期范围
46
+ if (dateRange.since || dateRange.until) {
47
+ const since = dateRange.since ? dayjs(dateRange.since).format('YYYY-MM-DD') : '最早';
48
+ const until = dateRange.until ? dayjs(dateRange.until).format('YYYY-MM-DD') : '今天';
49
+ console.log(chalk.gray(`时间范围: ${since} ~ ${until}`));
50
+ console.log();
51
+ }
52
+
53
+ // 总体统计
54
+ console.log(chalk.bold('📊 总体统计'));
55
+ console.log(' ' + chalk.gray('Reviews:'), stats.total_reviews || 0);
56
+ console.log(' ' + chalk.gray('开发者:'), stats.developer_count || 0);
57
+ console.log(' ' + chalk.gray('代码变更:'),
58
+ chalk.green(`+${stats.total_insertions || 0}`),
59
+ chalk.red(`-${stats.total_deletions || 0}`)
60
+ );
61
+ console.log();
62
+
63
+ // 问题统计
64
+ console.log(chalk.bold('🔍 问题统计'));
65
+ console.log(' ' + chalk.red('Errors:'), stats.total_errors || 0);
66
+ console.log(' ' + chalk.yellow('Warnings:'), stats.total_warnings || 0);
67
+ console.log(' ' + chalk.blue('Info:'), stats.total_infos || 0);
68
+ console.log(' ' + chalk.magenta('Risks:'), stats.total_risks || 0);
69
+ console.log();
70
+
71
+ // 开发者排名
72
+ if (developerStats.length > 0) {
73
+ console.log(chalk.bold('👥 开发者贡献'));
74
+ console.log();
75
+
76
+ for (const dev of developerStats) {
77
+ const matchRate = dev.total_reviews > 0
78
+ ? Math.round((dev.commit_match_count / dev.total_reviews) * 100)
79
+ : 0;
80
+
81
+ console.log(` ${chalk.cyan(dev.display_name)} ${chalk.gray(`(${dev.git_email})`)}`);
82
+ console.log(` Reviews: ${dev.total_reviews}`,
83
+ chalk.gray('|'),
84
+ `代码: ${chalk.green('+' + (dev.total_insertions || 0))} ${chalk.red('-' + (dev.total_deletions || 0))}`,
85
+ chalk.gray('|'),
86
+ `问题: ${chalk.red(dev.total_errors || 0)}E ${chalk.yellow(dev.total_warnings || 0)}W`,
87
+ chalk.gray('|'),
88
+ `Commit匹配: ${matchRate}%`
89
+ );
90
+ console.log();
91
+ }
92
+ }
93
+ }
94
+
95
+ async function showDeveloperStats(db, dateRange) {
96
+ const developers = db.listDevelopers();
97
+
98
+ if (developers.length === 0) {
99
+ logger.info('暂无开发者数据');
100
+ return;
101
+ }
102
+
103
+ logger.title('开发者统计');
104
+
105
+ // 显示日期范围
106
+ if (dateRange.since || dateRange.until) {
107
+ const since = dateRange.since ? dayjs(dateRange.since).format('YYYY-MM-DD') : '最早';
108
+ const until = dateRange.until ? dayjs(dateRange.until).format('YYYY-MM-DD') : '今天';
109
+ console.log(chalk.gray(`时间范围: ${since} ~ ${until}`));
110
+ console.log();
111
+ }
112
+
113
+ for (const dev of developers) {
114
+ const stats = db.getDeveloperStats(dev.id, dateRange);
115
+
116
+ if (!stats || stats.total_reviews === 0) continue;
117
+
118
+ const matchRate = stats.total_reviews > 0
119
+ ? Math.round((stats.commit_match_count / stats.total_reviews) * 100)
120
+ : 0;
121
+
122
+ console.log(chalk.bold.cyan(dev.display_name), chalk.gray(`<${dev.git_email}>`));
123
+ if (dev.team) {
124
+ console.log(chalk.gray(` 团队: ${dev.team}`));
125
+ }
126
+ console.log();
127
+
128
+ console.log(` 📊 Reviews: ${stats.total_reviews} | 参与项目: ${stats.project_count}`);
129
+ console.log(` 💻 代码变更: ${chalk.green('+' + (stats.total_insertions || 0))} ${chalk.red('-' + (stats.total_deletions || 0))}`);
130
+ console.log(` 🔍 问题: ${chalk.red((stats.total_errors || 0) + 'E')} ${chalk.yellow((stats.total_warnings || 0) + 'W')} ${chalk.blue((stats.total_infos || 0) + 'I')}`);
131
+ console.log(` ⚠️ 风险: ${stats.total_risks || 0}`);
132
+ console.log(` ✓ Commit 匹配率: ${matchRate}%`);
133
+ console.log();
134
+ logger.divider();
135
+ console.log();
136
+ }
137
+ }
138
+
139
+ export default statsCommand;
package/src/index.js CHANGED
@@ -1,35 +1,9 @@
1
- import { Command } from 'commander';
2
- import { initCommand } from './commands/init.js';
3
- import { configCommand } from './commands/config.js';
4
- import { analyzeCommand } from './commands/analyze.js';
5
-
6
- export const program = new Command();
7
-
8
- program
9
- .name('goodiffer')
10
- .description('AI-powered git diff analyzer for code review')
11
- .version('1.0.0');
12
-
13
- // 初始化命令
14
- program
15
- .command('init')
16
- .description('初始化配置 (设置 AI 提供商和 API Key)')
17
- .action(initCommand);
18
-
19
- // 配置管理命令
20
- program
21
- .command('config <action> [key] [value]')
22
- .description('配置管理 (list/get/set/clear)')
23
- .action(configCommand);
24
-
25
- // 分析命令
26
- program
27
- .command('analyze', { isDefault: true })
28
- .description('分析 git diff (默认命令)')
29
- .option('-s, --staged', '分析暂存区的变更')
30
- .option('-c, --commit <sha>', '分析指定 commit')
31
- .option('--from <sha>', '分析 commit 范围 (起始)')
32
- .option('--to <sha>', '分析 commit 范围 (结束)')
33
- .action(analyzeCommand);
34
-
35
- export default program;
1
+ // Goodiffer - AI-powered git diff analyzer
2
+ // 主入口文件
3
+
4
+ export { analyzeCommand } from './commands/analyze.js';
5
+ export { initCommand } from './commands/init.js';
6
+ export { configCommand } from './commands/config.js';
7
+ export { AIClient } from './services/ai-client.js';
8
+ export { GitService } from './services/git.js';
9
+ export { generateReport } from './services/reporter.js';
@@ -0,0 +1,187 @@
1
+ // 项目报告提示词
2
+ export function buildProjectReportPrompt(data) {
3
+ return `你是一个专业的技术团队 Code Review 分析师。请根据以下数据生成一份专业的 H5 报告页面。
4
+
5
+ ## 报告类型
6
+ 项目汇总报告
7
+
8
+ ## 时间范围
9
+ ${data.dateRange.start} 至 ${data.dateRange.end}
10
+
11
+ ## 项目信息
12
+ 项目名称: ${data.project.name}
13
+ 总 Review 次数: ${data.stats.totalReviews}
14
+ 参与开发者: ${data.developers.length} 人
15
+
16
+ ## 汇总统计
17
+ - 总 Commits: ${data.stats.totalReviews}
18
+ - 总代码行变更: +${data.stats.totalInsertions} / -${data.stats.totalDeletions}
19
+ - 错误数: ${data.stats.totalErrors}
20
+ - 警告数: ${data.stats.totalWarnings}
21
+ - 信息数: ${data.stats.totalInfos}
22
+ - 风险数: ${data.stats.totalRisks}
23
+
24
+ ## 各开发者详细数据
25
+ ${data.developerStats.map(dev => `
26
+ ### ${dev.displayName} (${dev.email})
27
+ - Commits: ${dev.totalReviews}
28
+ - 代码变更: +${dev.insertions} / -${dev.deletions}
29
+ - 错误: ${dev.errors}, 警告: ${dev.warnings}
30
+ - Commit 匹配率: ${dev.commitMatchRate}%
31
+
32
+ 典型问题示例:
33
+ ${dev.topIssues.map(issue => `- [${issue.level}] ${issue.file}: ${issue.description}`).join('\n') || '无'}
34
+
35
+ 关联风险示例:
36
+ ${dev.topRisks.map(risk => `- ${risk.changedFile}: ${risk.risk}`).join('\n') || '无'}
37
+ `).join('\n')}
38
+
39
+ ## 最近 Review 记录
40
+ ${data.recentReviews.map(r => `
41
+ ---
42
+ Commit: ${r.commitSha.substring(0, 8)} - ${r.commitMessage}
43
+ 开发者: ${r.developerName}
44
+ 日期: ${r.commitDate}
45
+ 摘要: ${r.summary || '无摘要'}
46
+ 问题统计: ${r.errorCount} errors, ${r.warningCount} warnings
47
+ `).join('\n')}
48
+
49
+ ## 输出要求
50
+
51
+ 请生成一份专业的 H5 报告页面,要求如下:
52
+
53
+ ### 1. 页面结构
54
+ - **顶部头区**: 报告标题、项目名称、时间范围、生成时间
55
+ - **概览卡片区**: 关键统计数字 (Reviews数、代码变更、问题总数)
56
+ - **开发者分析区**: 每个开发者的详细分析卡片
57
+ - **问题分布图**: 使用 CSS 绘制简单的柱状图
58
+ - **时间线区**: Review 历史时间线
59
+ - **详细记录区**: 可折叠的详细 Review 记录
60
+
61
+ ### 2. 开发者分析内容 (针对每个开发者生成)
62
+ 请根据数据分析每位开发者的:
63
+ - **优点**: 从数据中提炼积极方面(如:代码质量好、commit描述清晰、错误率低等)
64
+ - **缺点**: 需要改进的地方(基于 error/warning 数据分析)
65
+ - **风险点**: 常见的关联风险模式
66
+ - **工作记录**: commit 历史摘要和代码贡献统计
67
+ - **改进建议**: 具体可执行的建议
68
+
69
+ ### 3. 视觉设计
70
+ - 现代简洁的卡片式设计
71
+ - 配色方案:
72
+ - 主色: #3B82F6 (蓝色)
73
+ - 成功: #10B981 (绿色)
74
+ - 错误: #EF4444 (红色)
75
+ - 警告: #F59E0B (橙色)
76
+ - 背景: #F9FAFB
77
+ - 圆角卡片和阴影效果
78
+ - 支持响应式布局(移动端适配)
79
+
80
+ ### 4. 技术要求
81
+ - **单文件 HTML**:所有 CSS 和 JavaScript 内联
82
+ - **无外部依赖**:可离线查看,不引用任何 CDN
83
+ - **图表用 CSS 绘制**:不使用 Chart.js 等库
84
+ - **支持打印**:添加打印媒体查询
85
+ - **中文界面**
86
+ - **折叠功能**:详细记录区域可展开/收起
87
+
88
+ ### 5. 交互功能
89
+ - 点击开发者卡片展开详情
90
+ - 详细记录区可折叠
91
+ - 统计数字悬停显示详细信息
92
+
93
+ 直接输出完整的 HTML 代码,不要包含 markdown 代码块标记,不要有任何解释文字。`;
94
+ }
95
+
96
+ // 开发者个人报告提示词
97
+ export function buildDeveloperReportPrompt(data) {
98
+ return `你是一个专业的技术团队 Code Review 分析师。请为开发者生成个人 Code Review 报告。
99
+
100
+ ## 开发者信息
101
+ 姓名: ${data.developer.displayName}
102
+ 邮箱: ${data.developer.email}
103
+ 团队: ${data.developer.team || '未设置'}
104
+
105
+ ## 时间范围
106
+ ${data.dateRange.start} 至 ${data.dateRange.end}
107
+
108
+ ## 参与项目
109
+ ${data.projects.map(p => `- ${p.name}: ${p.commits} commits`).join('\n')}
110
+
111
+ ## 汇总统计
112
+ - 总 Commits: ${data.stats.totalReviews}
113
+ - 总代码行变更: +${data.stats.totalInsertions} / -${data.stats.totalDeletions}
114
+ - 总错误: ${data.stats.totalErrors}
115
+ - 总警告: ${data.stats.totalWarnings}
116
+ - Commit 匹配率: ${data.stats.commitMatchRate}%
117
+
118
+ ## 问题类型分布
119
+ ${data.issueDistribution.map(t => `- ${t.type} (${t.level}): ${t.count} 次`).join('\n')}
120
+
121
+ ## 典型问题示例
122
+ ${data.topIssues.map(issue => `
123
+ ### ${issue.level.toUpperCase()}: ${issue.type || '其他'}
124
+ 文件: ${issue.file}${issue.line ? ':' + issue.line : ''}
125
+ 问题: ${issue.description}
126
+ 建议: ${issue.suggestion || '无'}
127
+ `).join('\n')}
128
+
129
+ ## 关联风险模式
130
+ ${data.topRisks.map(risk => `
131
+ - 文件: ${risk.changedFile}
132
+ - 影响: ${risk.relatedFiles}
133
+ - 风险: ${risk.risk}
134
+ `).join('\n') || '无关联风险记录'}
135
+
136
+ ## 详细 Review 记录
137
+ ${data.reviews.map(r => `
138
+ ---
139
+ 项目: ${r.projectName}
140
+ Commit: ${r.commitSha.substring(0, 8)} - ${r.commitMessage}
141
+ 日期: ${r.commitDate}
142
+ 摘要: ${r.summary || '无'}
143
+ 问题: ${r.errorCount}E / ${r.warningCount}W / ${r.infoCount}I
144
+ `).join('\n')}
145
+
146
+ ## 输出要求
147
+
148
+ 生成个人 Code Review 报告 H5 页面,要求:
149
+
150
+ ### 1. 页面结构
151
+ - **个人概览卡片**: 姓名、团队、关键指标
152
+ - **能力分析区**: 优点、缺点、风险点详细分析
153
+ - **趋势图区**: 问题数量趋势(CSS绘制)
154
+ - **工作记录**: 按项目分组的 commit 时间线
155
+ - **改进建议**: 3-5条具体建议
156
+
157
+ ### 2. 能力分析内容
158
+ **优点** (从数据提炼):
159
+ - 代码质量方面的积极表现
160
+ - commit message 清晰度评价
161
+ - 错误率/警告率分析
162
+
163
+ **缺点** (基于问题数据):
164
+ - 最常见的问题类型
165
+ - 需要重点关注的代码领域
166
+
167
+ **风险点**:
168
+ - 常见的关联风险模式
169
+ - 需要额外注意的代码修改习惯
170
+
171
+ ### 3. 改进建议
172
+ 基于数据给出 3-5 条具体可执行的建议,例如:
173
+ - "建议在修改 X 类文件时,同时检查 Y 文件的相关逻辑"
174
+ - "注意 Z 类型的问题,可以通过 ... 方式预防"
175
+
176
+ ### 4. 技术要求
177
+ 与项目报告相同:
178
+ - 单文件 HTML,内联 CSS/JS
179
+ - 无外部依赖
180
+ - 响应式设计
181
+ - 支持打印
182
+ - 中文界面
183
+
184
+ 直接输出完整的 HTML 代码,不要包含 markdown 代码块标记,不要有任何解释文字。`;
185
+ }
186
+
187
+ export default { buildProjectReportPrompt, buildDeveloperReportPrompt };
@@ -1,78 +1,58 @@
1
- export function buildReviewPrompt(commitMessage, diff, changedFiles) {
2
- return `你是一个专业的 Code Reviewer,专注于代码质量和潜在问题分析。
3
-
4
- ## 任务
5
-
6
- 1. **理解 Commit 意图**: 首先仔细阅读并理解以下 commit message,它描述了这次修改的目的和内容。
7
- 2. **分析代码变更**: 检查 diff 中的代码修改是否准确实现了 commit 描述的意图。
8
- 3. **识别潜在问题**: 检查代码中可能存在的问题,包括:
9
- - 编译/运行时错误风险
10
- - 逻辑错误或边界情况未处理
11
- - 类型错误或空值处理问题
12
- - 关联性 bug (修改可能影响其他模块或功能)
1
+ export function buildReviewPrompt(commitMessage, diff) {
2
+ return `你是一个专业的 Code Reviewer。请分析以下代码变更。
13
3
 
14
4
  ## Commit Message
15
- \`\`\`
16
5
  ${commitMessage}
17
- \`\`\`
18
6
 
19
- ## 变更文件列表
20
- ${changedFiles.map(f => `- ${f}`).join('\n')}
21
-
22
- ## 代码 Diff
7
+ ## 代码变更 (Git Diff)
23
8
  \`\`\`diff
24
9
  ${diff}
25
10
  \`\`\`
26
11
 
27
- ## 输出要求
12
+ ## 任务
13
+ 1. 首先理解 commit message 描述的意图
14
+ 2. 分析代码变更是否符合 commit 描述
15
+ 3. 识别潜在问题:
16
+ - 编译/运行时错误风险
17
+ - 逻辑错误
18
+ - 代码关联性问题 (修改可能影响其他模块)
28
19
 
29
- 请以 JSON 格式输出分析结果,格式如下:
20
+ ## 输出格式
21
+ 请以 JSON 格式返回分析结果:
30
22
 
31
23
  \`\`\`json
32
24
  {
33
- "summary": "简要概述这次变更的内容和目的 (1-2句话)",
34
- "commitAlignment": {
35
- "aligned": true/false,
36
- "comment": "代码修改是否符合 commit 描述的意图的说明"
37
- },
25
+ "summary": "变更概述 (1-2句话)",
26
+ "commitMatch": true/false,
27
+ "commitMatchReason": "代码是否符合commit描述的说明",
38
28
  "issues": [
39
29
  {
40
30
  "level": "error|warning|info",
41
- "type": "compile|runtime|logic|security|association",
31
+ "type": "compile|logic|association|style",
42
32
  "file": "文件路径",
43
- "line": "行号或行号范围 (如 45 或 45-52)",
33
+ "line": "行号或行号范围",
44
34
  "code": "问题代码片段",
45
- "description": "问题的详细描述",
35
+ "description": "问题描述",
46
36
  "suggestion": "修复建议",
47
- "fixPrompt": "可以直接复制到 Claude CodeCodex 中的修复提示词"
37
+ "fixPrompt": "可复制到 Claude Code/Codex 的修复提示词"
48
38
  }
49
39
  ],
50
40
  "associationRisks": [
51
41
  {
52
- "changedFile": "被修改的文件路径",
42
+ "changedFile": "修改的文件",
53
43
  "relatedFiles": ["可能受影响的文件1", "可能受影响的文件2"],
54
- "risk": "潜在的关联风险描述",
55
- "checkPrompt": "用于检查关联问题的提示词"
44
+ "risk": "风险描述",
45
+ "checkPrompt": "检查提示词"
56
46
  }
57
- ],
58
- "suggestions": [
59
- "其他改进建议 (可选)"
60
47
  ]
61
48
  }
62
49
  \`\`\`
63
50
 
64
- ## 问题级别说明
65
- - **error**: 会导致编译失败、运行时崩溃或严重 bug 的问题
66
- - **warning**: 可能导致问题或不符合最佳实践的代码
67
- - **info**: 建议性的改进,不影响功能
68
-
69
51
  ## 注意事项
70
- 1. 只报告实际存在的问题,不要过度警告
71
- 2. fixPrompt 应该简洁明确,方便用户直接复制使用
72
- 3. 关联风险只报告真正可能受影响的情况
73
- 4. 如果代码没有问题,issues 和 associationRisks 可以为空数组
74
-
75
- 请仅输出 JSON,不要包含其他解释文字。`;
52
+ - 只报告真正的问题,不要过度警告
53
+ - fixPrompt 应该简洁明了,可以直接复制使用
54
+ - 如果没有问题,issues 数组可以为空
55
+ - 关联风险只在确实存在时才报告`;
76
56
  }
77
57
 
78
58
  export default buildReviewPrompt;