commit-report 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.
package/README.md CHANGED
@@ -8,7 +8,14 @@ Git 提交统计工具,递归扫描目录中的 Git 仓库,生成可视化 H
8
8
 
9
9
  ## 预览
10
10
 
11
- 查看 [示例报告](https://github.com/qqzhangyanhua/commitx#readme) 了解可视化效果。
11
+ ### 主界面
12
+ ![主界面](docs/image.png)
13
+
14
+ ### 数据统计
15
+ ![数据统计](docs/image%20copy.png)
16
+
17
+ ### 高级分析
18
+ ![高级分析](docs/image%20copy%202.png)
12
19
 
13
20
  ## 特性
14
21
 
package/dist/index.js CHANGED
@@ -200,10 +200,16 @@ function calculateStats(commits) {
200
200
  const directoryCommitSet = /* @__PURE__ */ new Map();
201
201
  const hourlyDistribution = new Array(24).fill(0);
202
202
  const dailyHeatmap = {};
203
+ const hourlyByAuthor = /* @__PURE__ */ new Map();
203
204
  const dailyCounts = /* @__PURE__ */ new Map();
204
205
  for (const commit of sorted) {
205
206
  const hour = commit.date.getHours();
206
207
  hourlyDistribution[hour]++;
208
+ if (!hourlyByAuthor.has(hour)) {
209
+ hourlyByAuthor.set(hour, /* @__PURE__ */ new Map());
210
+ }
211
+ const hourAuthors = hourlyByAuthor.get(hour);
212
+ hourAuthors.set(commit.author, (hourAuthors.get(commit.author) || 0) + 1);
207
213
  const dateKey = formatDateKey(commit.date);
208
214
  dailyHeatmap[dateKey] = (dailyHeatmap[dateKey] || 0) + 1;
209
215
  dailyCounts.set(dateKey, (dailyCounts.get(dateKey) || 0) + 1);
@@ -280,6 +286,19 @@ function calculateStats(commits) {
280
286
  (a, b) => b.added + b.deleted - (a.added + a.deleted)
281
287
  );
282
288
  const directories = Array.from(directoryMap.values()).sort((a, b) => b.linesChanged - a.linesChanged).slice(0, 10);
289
+ const hourlyByAuthorArray = Array.from({ length: 24 }, (_, hour) => {
290
+ const authorMap2 = hourlyByAuthor.get(hour);
291
+ const authors2 = {};
292
+ if (authorMap2) {
293
+ authorMap2.forEach((count, author) => {
294
+ authors2[author] = count;
295
+ });
296
+ }
297
+ return {
298
+ count: hourlyDistribution[hour],
299
+ authors: authors2
300
+ };
301
+ });
283
302
  return {
284
303
  totalCommits: sorted.length,
285
304
  linesAdded: totalLinesAdded,
@@ -293,6 +312,7 @@ function calculateStats(commits) {
293
312
  directories,
294
313
  hourlyDistribution,
295
314
  dailyHeatmap,
315
+ hourlyByAuthor: hourlyByAuthorArray,
296
316
  quality: calculateQualityMetrics(sorted),
297
317
  timePatterns: calculateTimePatterns(sorted),
298
318
  trends: calculateTrends(sorted),
@@ -558,10 +578,16 @@ function calculateTimePatterns(commits) {
558
578
  return emptyTimePatterns();
559
579
  }
560
580
  const weekdayDistribution = new Array(7).fill(0);
581
+ const weekdayByAuthor = /* @__PURE__ */ new Map();
561
582
  for (const commit of commits) {
562
583
  const day = commit.date.getDay();
563
584
  const idx = day === 0 ? 6 : day - 1;
564
585
  weekdayDistribution[idx]++;
586
+ if (!weekdayByAuthor.has(idx)) {
587
+ weekdayByAuthor.set(idx, /* @__PURE__ */ new Map());
588
+ }
589
+ const dayAuthors = weekdayByAuthor.get(idx);
590
+ dayAuthors.set(commit.author, (dayAuthors.get(commit.author) || 0) + 1);
565
591
  }
566
592
  const weekendCommits = (weekdayDistribution[5] + weekdayDistribution[6]) / commits.length;
567
593
  const sorted = [...commits].sort(
@@ -573,12 +599,26 @@ function calculateTimePatterns(commits) {
573
599
  }
574
600
  const avgCommitInterval = sorted.length > 1 ? totalInterval / (sorted.length - 1) / 36e5 : 0;
575
601
  const { longestStreak, currentStreak } = calculateStreaks(sorted);
602
+ const weekdayByAuthorArray = Array.from({ length: 7 }, (_, day) => {
603
+ const authorMap = weekdayByAuthor.get(day);
604
+ const authors = {};
605
+ if (authorMap) {
606
+ authorMap.forEach((count, author) => {
607
+ authors[author] = count;
608
+ });
609
+ }
610
+ return {
611
+ count: weekdayDistribution[day],
612
+ authors
613
+ };
614
+ });
576
615
  return {
577
616
  weekdayDistribution,
578
617
  weekendCommits,
579
618
  avgCommitInterval,
580
619
  longestStreak,
581
- currentStreak
620
+ currentStreak,
621
+ weekdayByAuthor: weekdayByAuthorArray
582
622
  };
583
623
  }
584
624
  function calculateStreaks(sortedCommits) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts","../src/scanner/index.ts","../src/analyzer/index.ts","../src/analyzer/git-log-parser.ts","../src/analyzer/stats-calculator.ts","../src/analyzer/advanced/team-health.ts","../src/analyzer/advanced/code-stability.ts","../src/analyzer/advanced/work-pressure.ts","../src/analyzer/advanced/contributor-churn.ts","../src/analyzer/advanced/collaboration.ts","../src/analyzer/advanced/index.ts","../src/reporter/index.ts","../src/reporter/html-builder.ts","../src/cli/time-utils.ts"],"sourcesContent":["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { checkbox } from '@inquirer/prompts';\nimport { scanRepositories } from '../scanner/index.js';\nimport { analyzeRepos } from '../analyzer/index.js';\nimport { generateReport } from '../reporter/index.js';\nimport { parsePeriod, resolveTimeRange } from './time-utils.js';\nimport type { CliOptions, RepoInfo } from '../types/index.js';\n\nconst program = new Command();\n\nprogram\n .name('commit-report')\n .description('Git 提交统计工具,生成可视化 HTML 报告')\n .version('1.0.0')\n .argument('[directory]', '要扫描的目录路径', process.cwd())\n .option('-p, --period <period>', '时间预设 (7d/1m/3m/6m/1y/all)', 'all')\n .option('-f, --from <date>', '起始日期 (YYYY-MM-DD)')\n .option('-t, --to <date>', '结束日期 (YYYY-MM-DD)')\n .option('-a, --author <name>', '过滤作者')\n .option('-o, --output <file>', '输出文件名', 'commit-report.html')\n .option('--no-open', '不自动打开浏览器')\n .option('-d, --depth <number>', '最大扫描深度', '20')\n .action(async (directory: string, opts: CliOptions) => {\n try {\n await run(directory, opts);\n } catch (error) {\n if (error instanceof Error && error.message === 'USER_CANCEL') {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n }\n console.error(chalk.red(`\\n错误: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n });\n\nasync function run(directory: string, opts: CliOptions): Promise<void> {\n // 1. 检查 Git 是否安装\n await checkGitInstalled();\n\n // 2. 解析时间范围\n const timeRange = resolveTimeRange(opts);\n\n // 3. 扫描仓库\n const repos = await scanRepositories({\n targetDir: directory,\n maxDepth: Number(opts.depth),\n });\n\n if (repos.length === 0) {\n console.log(chalk.red('未找到 Git 仓库'));\n process.exit(1);\n }\n\n // 4. 选择仓库\n let selectedRepos: RepoInfo[];\n\n if (repos.length === 1) {\n selectedRepos = repos;\n console.log(chalk.cyan(`找到 1 个 Git 仓库: ${repos[0].name}`));\n } else {\n console.log(chalk.cyan(`\\n找到 ${repos.length} 个 Git 仓库:\\n`));\n\n const selected = await checkbox<string>({\n message: '选择要分析的仓库(空格选择,回车确认)',\n choices: repos.map((repo) => ({\n name: `${repo.name} (${repo.commitCount} commits)`,\n value: repo.path,\n checked: true,\n })),\n });\n\n if (selected.length === 0) {\n console.log(chalk.yellow('未选择任何仓库'));\n process.exit(0);\n }\n\n selectedRepos = repos.filter((r) => selected.includes(r.path));\n }\n\n const timeRangeText = timeRange\n ? `${formatDate(timeRange.from)} ~ ${formatDate(timeRange.to)}`\n : '所有提交';\n console.log(\n chalk.gray(\n `\\n已选择 ${selectedRepos.length} 个仓库,时间范围:${timeRangeText}\\n`\n )\n );\n\n // 5. 分析提交记录\n const stats = await analyzeRepos({\n repos: selectedRepos,\n timeRange,\n author: opts.author,\n });\n\n if (stats.totalCommits === 0) {\n console.log(chalk.yellow('该时间段无提交记录'));\n }\n\n // 6. 生成报告\n await generateReport(stats, {\n outputPath: opts.output,\n autoOpen: opts.open,\n timeRange,\n repoNames: selectedRepos.map((r) => r.name),\n });\n}\n\nasync function checkGitInstalled(): Promise<void> {\n const { execSync } = await import('child_process');\n try {\n execSync('git --version', { stdio: 'ignore' });\n } catch {\n console.error(chalk.red('请先安装 Git'));\n process.exit(1);\n }\n}\n\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n\n// 处理 Ctrl+C\nprocess.on('SIGINT', () => {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n});\n\nprogram.parse();\n","import { readdir, stat, access } from 'node:fs/promises';\nimport { join, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { confirm } from '@inquirer/prompts';\nimport type { ScanOptions, RepoInfo } from '../types/index.js';\n\n/** 扫描时忽略的目录名 */\nconst IGNORE_DIRS = new Set([\n 'node_modules',\n '.git',\n 'vendor',\n 'dist',\n 'build',\n '.cache',\n '.next',\n '.nuxt',\n '__pycache__',\n 'target',\n]);\n\n/**\n * 递归扫描目录,查找所有 Git 仓库\n */\nexport async function scanRepositories(options: ScanOptions): Promise<RepoInfo[]> {\n const spinner = ora('扫描仓库中...').start();\n const repos: RepoInfo[] = [];\n let deepScanConfirmed = false;\n\n async function scan(dir: string, depth: number): Promise<void> {\n // 深度超过限制时,提示用户确认\n if (depth > options.maxDepth && !deepScanConfirmed) {\n spinner.stop();\n const shouldContinue = await confirm({\n message: `扫描深度已超过 ${options.maxDepth} 层,是否继续?`,\n default: false,\n });\n\n if (!shouldContinue) {\n return;\n }\n deepScanConfirmed = true;\n spinner.start('继续扫描中...');\n }\n\n // 检查当前目录是否有 .git\n try {\n const gitDir = join(dir, '.git');\n await access(gitDir);\n\n // 找到 .git,获取仓库信息\n const repoInfo = getRepoInfo(dir);\n if (repoInfo) {\n repos.push(repoInfo);\n spinner.text = `扫描仓库中... 已找到 ${repos.length} 个`;\n }\n // 找到 .git 后不再深层继续(避免子模块重复)\n return;\n } catch {\n // 没有 .git,继续扫描子目录\n }\n\n // 递归扫描子目录\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (IGNORE_DIRS.has(entry.name)) continue;\n if (entry.name.startsWith('.') && entry.name !== '.git') continue;\n\n await scan(join(dir, entry.name), depth + 1);\n }\n } catch {\n // 权限不足时跳过\n }\n }\n\n await scan(options.targetDir, 0);\n\n if (repos.length > 0) {\n spinner.succeed(`找到 ${repos.length} 个 Git 仓库`);\n } else {\n spinner.fail('未找到 Git 仓库');\n }\n\n return repos;\n}\n\n/**\n * 获取单个仓库的基本信息\n */\nfunction getRepoInfo(repoPath: string): RepoInfo | null {\n try {\n const countStr = execSync('git rev-list --count HEAD', {\n cwd: repoPath,\n stdio: ['pipe', 'pipe', 'ignore'],\n encoding: 'utf-8',\n }).trim();\n\n const commitCount = parseInt(countStr, 10) || 0;\n const name = basename(repoPath);\n\n return {\n path: repoPath,\n name,\n commitCount,\n };\n } catch {\n // 仓库可能损坏或为空\n return null;\n }\n}\n","import ora from 'ora';\nimport chalk from 'chalk';\nimport { parseGitLog } from './git-log-parser.js';\nimport { calculateStats, mergeStats } from './stats-calculator.js';\nimport { calculateAdvancedStats } from './advanced/index.js';\nimport type { AnalyzeOptions, CommitStats } from '../types/index.js';\n\n/**\n * 分析所有选定仓库的提交记录\n */\nexport async function analyzeRepos(options: AnalyzeOptions): Promise<CommitStats> {\n const { repos, timeRange, author } = options;\n const spinner = ora('分析提交记录...').start();\n const allStats: CommitStats[] = [];\n\n for (let i = 0; i < repos.length; i++) {\n const repo = repos[i];\n spinner.text = `分析提交记录 (${i + 1}/${repos.length}) - ${repo.name}`;\n\n try {\n const commits = await parseGitLog(repo.path, timeRange, author);\n\n if (commits.length > 100000) {\n spinner.info(\n chalk.yellow(`${repo.name} 包含 ${commits.length.toLocaleString()} 条提交,处理可能需要一些时间...`)\n );\n spinner.start();\n }\n\n const stats = calculateStats(commits);\n\n // 新增:计算高级统计\n const advancedStats = calculateAdvancedStats(commits);\n\n // 合并核心统计和高级统计\n const fullStats: CommitStats = {\n ...stats,\n ...advancedStats,\n };\n\n allStats.push(fullStats);\n } catch (error) {\n spinner.warn(\n chalk.yellow(\n `跳过仓库 ${repo.name}: ${error instanceof Error ? error.message : '未知错误'}`\n )\n );\n spinner.start();\n }\n }\n\n const merged = mergeStats(allStats);\n\n spinner.succeed(\n `分析完成: ${merged.totalCommits.toLocaleString()} 条提交, ` +\n `${merged.authors.length} 位作者, ` +\n `+${merged.linesAdded.toLocaleString()} / -${merged.linesDeleted.toLocaleString()} 行`\n );\n\n return merged;\n}\n","import { execSync } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport ig from 'ignore';\nimport type { CommitRecord, FileChange, TimeRange } from '../types/index.js';\n\n/** git log 的格式化分隔符 */\nconst COMMIT_SEPARATOR = '---COMMITX_SEP---';\nconst FIELD_SEPARATOR = '|';\nconst FORMAT = `${COMMIT_SEPARATOR}%H${FIELD_SEPARATOR}%an${FIELD_SEPARATOR}%ae${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s`;\n\n/**\n * 从指定仓库解析 git log,返回提交记录数组\n * timeRange 为 null 时表示获取所有提交\n */\nexport async function parseGitLog(\n repoPath: string,\n timeRange: TimeRange | null,\n author?: string\n): Promise<CommitRecord[]> {\n const ignoreFilter = await loadGitignore(repoPath);\n\n const args = [\n 'git',\n 'log',\n `--format=\"${FORMAT}\"`,\n '--numstat',\n ];\n\n if (timeRange) {\n args.push(`--since=\"${timeRange.from.toISOString()}\"`);\n args.push(`--until=\"${timeRange.to.toISOString()}\"`);\n }\n\n if (author) {\n args.push(`--author=\"${author}\"`);\n }\n\n let output: string;\n try {\n output = execSync(args.join(' '), {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 100 * 1024 * 1024, // 100MB buffer for large repos\n stdio: ['pipe', 'pipe', 'ignore'],\n });\n } catch {\n return [];\n }\n\n if (!output.trim()) {\n return [];\n }\n\n return parseOutput(output, ignoreFilter);\n}\n\n/**\n * 解析 git log 输出文本为 CommitRecord 数组\n */\nfunction parseOutput(\n output: string,\n ignoreFilter: ReturnType<typeof ig>\n): CommitRecord[] {\n const commits: CommitRecord[] = [];\n const blocks = output.split(COMMIT_SEPARATOR).filter((b) => b.trim());\n\n for (const block of blocks) {\n const lines = block.trim().split('\\n');\n if (lines.length === 0) continue;\n\n // 第一行是提交元信息\n const headerLine = lines[0].replace(/^\"|\"$/g, '');\n const parts = headerLine.split(FIELD_SEPARATOR);\n if (parts.length < 5) continue;\n\n const [hash, authorName, email, dateStr, ...messageParts] = parts;\n const message = messageParts.join(FIELD_SEPARATOR); // message 可能包含 |\n\n // 后续行是 numstat(新增\\t删除\\t文件路径)\n const files: FileChange[] = [];\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) continue;\n\n const tabParts = line.split('\\t');\n if (tabParts.length !== 3) continue;\n\n const [addedStr, deletedStr, filePath] = tabParts;\n\n // 二进制文件显示为 -\n const added = addedStr === '-' ? 0 : parseInt(addedStr, 10) || 0;\n const deleted = deletedStr === '-' ? 0 : parseInt(deletedStr, 10) || 0;\n\n // 应用 gitignore 过滤\n if (ignoreFilter.ignores(filePath)) continue;\n\n files.push({ added, deleted, path: filePath });\n }\n\n commits.push({\n hash,\n author: authorName,\n email,\n date: new Date(dateStr),\n message,\n files,\n });\n }\n\n return commits;\n}\n\n/**\n * 读取仓库的 .gitignore 规则\n */\nasync function loadGitignore(repoPath: string): ReturnType<typeof ig> {\n const ignoreInstance = ig();\n\n try {\n const content = await readFile(join(repoPath, '.gitignore'), 'utf-8');\n ignoreInstance.add(content);\n } catch {\n // 没有 .gitignore 文件,跳过\n }\n\n // 默认忽略一些常见的生成文件\n ignoreInstance.add([\n 'package-lock.json',\n 'pnpm-lock.yaml',\n 'yarn.lock',\n ]);\n\n return ignoreInstance;\n}\n","import type {\n CommitRecord,\n CommitStats,\n AuthorStats,\n FileTypeStats,\n DirectoryStats,\n BusiestDay,\n QualityMetrics,\n TimePatterns,\n TrendData,\n CollaborationMetrics,\n CommitMessageStats,\n AuthorFileTypeContribution,\n HotFile,\n WeeklyPoint,\n CumulativePoint,\n SoloFile,\n CollabFile,\n} from '../types/index.js';\nimport { extname } from 'node:path';\n\n/**\n * 根据解析后的提交记录,计算所有统计维度\n */\nexport function calculateStats(commits: CommitRecord[]): CommitStats {\n if (commits.length === 0) {\n return emptyStats();\n }\n\n // 排序:按日期升序\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n\n // 基础统计\n let totalLinesAdded = 0;\n let totalLinesDeleted = 0;\n const allFilePaths = new Set<string>();\n\n // 作者维度\n const authorMap = new Map<string, AuthorStats>();\n\n // 文件类型维度\n const fileTypeMap = new Map<string, FileTypeStats>();\n\n // 目录维度\n const directoryMap = new Map<string, DirectoryStats>();\n const directoryCommitSet = new Map<string, Set<string>>(); // dir -> Set<commitHash>\n\n // 时间分布\n const hourlyDistribution = new Array<number>(24).fill(0);\n const dailyHeatmap: Record<string, number> = {};\n\n // 每日统计(用于计算最繁忙的一天)\n const dailyCounts = new Map<string, number>();\n\n for (const commit of sorted) {\n // 时间分布\n const hour = commit.date.getHours();\n hourlyDistribution[hour]++;\n\n const dateKey = formatDateKey(commit.date);\n dailyHeatmap[dateKey] = (dailyHeatmap[dateKey] || 0) + 1;\n dailyCounts.set(dateKey, (dailyCounts.get(dateKey) || 0) + 1);\n\n // 作者统计\n const authorKey = commit.email.toLowerCase();\n let authorStat = authorMap.get(authorKey);\n if (!authorStat) {\n authorStat = {\n name: commit.author,\n email: commit.email,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n lastActiveDate: commit.date,\n };\n authorMap.set(authorKey, authorStat);\n }\n authorStat.commits++;\n authorStat.lastActiveDate = commit.date;\n\n // 文件级别统计\n for (const file of commit.files) {\n totalLinesAdded += file.added;\n totalLinesDeleted += file.deleted;\n allFilePaths.add(file.path);\n\n // 作者行数统计\n authorStat.linesAdded += file.added;\n authorStat.linesDeleted += file.deleted;\n\n // 文件类型统计\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n let ftStat = fileTypeMap.get(ext);\n if (!ftStat) {\n ftStat = { extension: ext, added: 0, deleted: 0, fileCount: 0 };\n fileTypeMap.set(ext, ftStat);\n }\n ftStat.added += file.added;\n ftStat.deleted += file.deleted;\n\n // 目录统计 —— 取第一层目录\n const topDir = getTopDirectory(file.path);\n let dirStat = directoryMap.get(topDir);\n if (!dirStat) {\n dirStat = { path: topDir, commits: 0, linesChanged: 0 };\n directoryMap.set(topDir, dirStat);\n directoryCommitSet.set(topDir, new Set());\n }\n dirStat.linesChanged += file.added + file.deleted;\n directoryCommitSet.get(topDir)!.add(commit.hash);\n }\n }\n\n // 计算目录的 commit 数量\n for (const [dir, commitSet] of directoryCommitSet) {\n const dirStat = directoryMap.get(dir);\n if (dirStat) {\n dirStat.commits = commitSet.size;\n }\n }\n\n // 计算文件类型的 fileCount\n const fileCountByExt = new Map<string, Set<string>>();\n for (const filePath of allFilePaths) {\n const ext = extname(filePath).toLowerCase() || '(无扩展名)';\n if (!fileCountByExt.has(ext)) {\n fileCountByExt.set(ext, new Set());\n }\n fileCountByExt.get(ext)!.add(filePath);\n }\n for (const [ext, files] of fileCountByExt) {\n const ftStat = fileTypeMap.get(ext);\n if (ftStat) {\n ftStat.fileCount = files.size;\n }\n }\n\n // 最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of dailyCounts) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n\n // 排序结果\n const authors = Array.from(authorMap.values()).sort(\n (a, b) => b.commits - a.commits\n );\n const fileTypes = Array.from(fileTypeMap.values()).sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n const directories = Array.from(directoryMap.values())\n .sort((a, b) => b.linesChanged - a.linesChanged)\n .slice(0, 10); // TOP 10\n\n return {\n totalCommits: sorted.length,\n linesAdded: totalLinesAdded,\n linesDeleted: totalLinesDeleted,\n filesChanged: allFilePaths.size,\n firstCommitDate: sorted[0].date,\n lastCommitDate: sorted[sorted.length - 1].date,\n busiestDay,\n authors,\n fileTypes,\n directories,\n hourlyDistribution,\n dailyHeatmap,\n quality: calculateQualityMetrics(sorted),\n timePatterns: calculateTimePatterns(sorted),\n trends: calculateTrends(sorted),\n collaboration: calculateCollaboration(sorted),\n messageStats: calculateMessageStats(sorted),\n authorFileTypeContributions: calculateAuthorFileTypeContributions(sorted),\n };\n}\n\n/**\n * 合并多个仓库的统计结果\n */\nexport function mergeStats(statsList: CommitStats[]): CommitStats {\n if (statsList.length === 0) return emptyStats();\n if (statsList.length === 1) return statsList[0];\n\n const merged = emptyStats();\n\n for (const stats of statsList) {\n merged.totalCommits += stats.totalCommits;\n merged.linesAdded += stats.linesAdded;\n merged.linesDeleted += stats.linesDeleted;\n merged.filesChanged += stats.filesChanged;\n\n // 时间维度\n if (\n !merged.firstCommitDate ||\n stats.firstCommitDate < merged.firstCommitDate\n ) {\n merged.firstCommitDate = stats.firstCommitDate;\n }\n if (\n !merged.lastCommitDate ||\n stats.lastCommitDate > merged.lastCommitDate\n ) {\n merged.lastCommitDate = stats.lastCommitDate;\n }\n\n // 小时分布\n for (let i = 0; i < 24; i++) {\n merged.hourlyDistribution[i] += stats.hourlyDistribution[i];\n }\n\n // 每日热力图\n for (const [date, count] of Object.entries(stats.dailyHeatmap)) {\n merged.dailyHeatmap[date] = (merged.dailyHeatmap[date] || 0) + count;\n }\n\n // 作者合并\n for (const author of stats.authors) {\n const existing = merged.authors.find(\n (a) => a.email.toLowerCase() === author.email.toLowerCase()\n );\n if (existing) {\n existing.commits += author.commits;\n existing.linesAdded += author.linesAdded;\n existing.linesDeleted += author.linesDeleted;\n if (author.lastActiveDate > existing.lastActiveDate) {\n existing.lastActiveDate = author.lastActiveDate;\n }\n } else {\n merged.authors.push({ ...author });\n }\n }\n\n // 文件类型合并\n for (const ft of stats.fileTypes) {\n const existing = merged.fileTypes.find(\n (f) => f.extension === ft.extension\n );\n if (existing) {\n existing.added += ft.added;\n existing.deleted += ft.deleted;\n existing.fileCount += ft.fileCount;\n } else {\n merged.fileTypes.push({ ...ft });\n }\n }\n\n // 目录合并\n for (const dir of stats.directories) {\n const existing = merged.directories.find((d) => d.path === dir.path);\n if (existing) {\n existing.commits += dir.commits;\n existing.linesChanged += dir.linesChanged;\n } else {\n merged.directories.push({ ...dir });\n }\n }\n\n // 质量指标合并\n merged.quality.avgFilesPerCommit += stats.quality.avgFilesPerCommit;\n merged.quality.avgLinesPerCommit += stats.quality.avgLinesPerCommit;\n merged.quality.churnRate += stats.quality.churnRate;\n for (const hf of stats.quality.hotFiles) {\n const existing = merged.quality.hotFiles.find((h) => h.path === hf.path);\n if (existing) {\n existing.modifyCount += hf.modifyCount;\n for (const author of hf.authors) {\n if (!existing.authors.includes(author)) {\n existing.authors.push(author);\n }\n }\n } else {\n merged.quality.hotFiles.push({ ...hf, authors: [...hf.authors] });\n }\n }\n\n // 时间模式合并\n for (let i = 0; i < 7; i++) {\n merged.timePatterns.weekdayDistribution[i] +=\n stats.timePatterns.weekdayDistribution[i];\n }\n\n // 趋势数据合并\n for (const wp of stats.trends.weeklyTrend) {\n const existing = merged.trends.weeklyTrend.find((w) => w.week === wp.week);\n if (existing) {\n existing.commits += wp.commits;\n existing.linesAdded += wp.linesAdded;\n existing.linesDeleted += wp.linesDeleted;\n } else {\n merged.trends.weeklyTrend.push({ ...wp });\n }\n }\n for (const cp of stats.trends.cumulativeLines) {\n const existing = merged.trends.cumulativeLines.find(\n (c) => c.date === cp.date\n );\n if (existing) {\n existing.netLines += cp.netLines;\n } else {\n merged.trends.cumulativeLines.push({ ...cp });\n }\n }\n\n // 协作指标合并\n for (const sf of stats.collaboration.soloFiles) {\n const existing = merged.collaboration.soloFiles.find(\n (s) => s.path === sf.path\n );\n if (existing) {\n existing.commits += sf.commits;\n } else {\n merged.collaboration.soloFiles.push({ ...sf });\n }\n }\n for (const ch of stats.collaboration.collaborationHotspots) {\n const existing = merged.collaboration.collaborationHotspots.find(\n (c) => c.path === ch.path\n );\n if (existing) {\n existing.totalCommits += ch.totalCommits;\n existing.authorCount = Math.max(existing.authorCount, ch.authorCount);\n } else {\n merged.collaboration.collaborationHotspots.push({ ...ch });\n }\n }\n\n // Commit Message 统计合并\n for (const [type, count] of Object.entries(stats.messageStats.typeDistribution)) {\n merged.messageStats.typeDistribution[type] =\n (merged.messageStats.typeDistribution[type] || 0) + count;\n }\n merged.messageStats.avgMessageLength += stats.messageStats.avgMessageLength;\n }\n\n // 重新计算最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of Object.entries(merged.dailyHeatmap)) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n merged.busiestDay = busiestDay;\n\n // 排序\n merged.authors.sort((a, b) => b.commits - a.commits);\n merged.fileTypes.sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n merged.directories.sort((a, b) => b.linesChanged - a.linesChanged);\n merged.directories = merged.directories.slice(0, 10);\n\n // 重新计算扩展字段的平均值和排序\n const repoCount = statsList.length;\n merged.quality.avgFilesPerCommit /= repoCount;\n merged.quality.avgLinesPerCommit /= repoCount;\n merged.quality.churnRate /= repoCount;\n merged.quality.hotFiles.sort((a, b) => b.modifyCount - a.modifyCount);\n merged.quality.hotFiles = merged.quality.hotFiles.slice(0, 10);\n\n // 时间模式重新计算\n const totalWeekdayCommits = merged.timePatterns.weekdayDistribution.reduce(\n (a, b) => a + b,\n 0\n );\n if (totalWeekdayCommits > 0) {\n merged.timePatterns.weekendCommits =\n (merged.timePatterns.weekdayDistribution[5] +\n merged.timePatterns.weekdayDistribution[6]) /\n totalWeekdayCommits;\n }\n\n // 趋势数据排序\n merged.trends.weeklyTrend.sort((a, b) => a.week.localeCompare(b.week));\n merged.trends.cumulativeLines.sort((a, b) => a.date.localeCompare(b.date));\n\n // 重新计算累计代码量\n let cumulative = 0;\n for (const point of merged.trends.cumulativeLines) {\n cumulative += point.netLines;\n point.netLines = cumulative;\n }\n\n // 协作指标排序\n merged.collaboration.soloFiles.sort((a, b) => b.commits - a.commits);\n merged.collaboration.soloFiles = merged.collaboration.soloFiles.slice(0, 10);\n merged.collaboration.collaborationHotspots.sort(\n (a, b) => b.totalCommits - a.totalCommits\n );\n merged.collaboration.collaborationHotspots =\n merged.collaboration.collaborationHotspots.slice(0, 10);\n\n // Commit Message 平均长度\n merged.messageStats.avgMessageLength /= repoCount;\n\n // 作者文件类型贡献合并\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n for (const stats of statsList) {\n for (const contrib of stats.authorFileTypeContributions) {\n const key = `${contrib.email.toLowerCase()}|||${contrib.extension}`;\n const existing = contributionMap.get(key);\n if (existing) {\n existing.linesAdded += contrib.linesAdded;\n existing.linesDeleted += contrib.linesDeleted;\n existing.commits += contrib.commits;\n existing.fileCount += contrib.fileCount;\n } else {\n contributionMap.set(key, { ...contrib });\n }\n }\n }\n merged.authorFileTypeContributions = Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n\n // ============================================================\n // 高级统计字段不进行多仓库合并\n // ============================================================\n // 原因:高级统计(teamHealth, stability, workPressure, contributorChurn, advancedCollaboration)\n // 需要原始 CommitRecord[] 数据才能准确计算。在 mergeStats 中仅有聚合后的统计数据,\n // 强行合并会导致结果不准确(例如 busFactor、revertRate 等指标无法简单累加)。\n //\n // 当前行为:\n // - 单仓库场景:直接返回(第184行),保留所有高级统计\n // - 多仓库场景:merged 中高级字段保持 undefined\n //\n // 如需多仓库的高级统计分析,建议:\n // 1. 使用时间范围过滤单个仓库\n // 2. 或在 analyzer/index.ts 中基于合并后的原始 commits 重新计算\n\n return merged;\n}\n\n/** 创建空的统计对象 */\nfunction emptyStats(): CommitStats {\n return {\n totalCommits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n filesChanged: 0,\n firstCommitDate: new Date(),\n lastCommitDate: new Date(),\n busiestDay: { date: '', count: 0 },\n authors: [],\n fileTypes: [],\n directories: [],\n hourlyDistribution: new Array<number>(24).fill(0),\n dailyHeatmap: {},\n quality: emptyQualityMetrics(),\n timePatterns: emptyTimePatterns(),\n trends: emptyTrendData(),\n collaboration: emptyCollaborationMetrics(),\n messageStats: emptyMessageStats(),\n authorFileTypeContributions: [],\n };\n}\n\n/** 获取文件路径的第一层目录 */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\n/** 格式化日期为 YYYY-MM-DD */\nfunction formatDateKey(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n\n// ============================================================\n// 扩展统计计算函数\n// ============================================================\n\n/** 计算代码质量指标 */\nfunction calculateQualityMetrics(commits: CommitRecord[]): QualityMetrics {\n if (commits.length === 0) {\n return emptyQualityMetrics();\n }\n\n // 平均每次提交的文件数\n const totalFiles = commits.reduce((sum, c) => sum + c.files.length, 0);\n const avgFilesPerCommit = totalFiles / commits.length;\n\n // 平均每次提交的行数\n const totalLines = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.added + f.deleted, 0),\n 0\n );\n const avgLinesPerCommit = totalLines / commits.length;\n\n // 代码流失率\n const totalAdded = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.added, 0),\n 0\n );\n const totalDeleted = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.deleted, 0),\n 0\n );\n const churnRate = totalAdded > 0 ? totalDeleted / totalAdded : 0;\n\n // 热点文件\n const fileModifyMap = new Map<string, { count: number; authors: Set<string> }>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const entry = fileModifyMap.get(file.path) || { count: 0, authors: new Set() };\n entry.count++;\n entry.authors.add(commit.author);\n fileModifyMap.set(file.path, entry);\n }\n }\n\n const hotFiles: HotFile[] = Array.from(fileModifyMap.entries())\n .map(([path, data]) => ({\n path,\n modifyCount: data.count,\n authors: Array.from(data.authors),\n }))\n .sort((a, b) => b.modifyCount - a.modifyCount)\n .slice(0, 10);\n\n return { avgFilesPerCommit, avgLinesPerCommit, churnRate, hotFiles };\n}\n\n/** 计算时间模式指标 */\nfunction calculateTimePatterns(commits: CommitRecord[]): TimePatterns {\n if (commits.length === 0) {\n return emptyTimePatterns();\n }\n\n const weekdayDistribution = new Array<number>(7).fill(0);\n\n for (const commit of commits) {\n const day = commit.date.getDay(); // 0=周日\n const idx = day === 0 ? 6 : day - 1; // 转为周一=0\n weekdayDistribution[idx]++;\n }\n\n // 周末提交占比\n const weekendCommits =\n (weekdayDistribution[5] + weekdayDistribution[6]) / commits.length;\n\n // 提交间隔\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n let totalInterval = 0;\n for (let i = 1; i < sorted.length; i++) {\n totalInterval += sorted[i].date.getTime() - sorted[i - 1].date.getTime();\n }\n const avgCommitInterval =\n sorted.length > 1 ? totalInterval / (sorted.length - 1) / 3600000 : 0;\n\n // 连续提交天数\n const { longestStreak, currentStreak } = calculateStreaks(sorted);\n\n return {\n weekdayDistribution,\n weekendCommits,\n avgCommitInterval,\n longestStreak,\n currentStreak,\n };\n}\n\n/** 计算连续提交天数 */\nfunction calculateStreaks(sortedCommits: CommitRecord[]): {\n longestStreak: number;\n currentStreak: number;\n} {\n if (sortedCommits.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n // 提取唯一日期\n const uniqueDates = new Set<string>();\n for (const commit of sortedCommits) {\n uniqueDates.add(formatDateKey(commit.date));\n }\n\n const sortedDates = Array.from(uniqueDates).sort();\n if (sortedDates.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n let longestStreak = 1;\n let currentStreakCount = 1;\n let tempStreak = 1;\n\n for (let i = 1; i < sortedDates.length; i++) {\n const prevDate = new Date(sortedDates[i - 1]);\n const currDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n tempStreak++;\n } else {\n tempStreak = 1;\n }\n\n longestStreak = Math.max(longestStreak, tempStreak);\n }\n\n // 计算当前连续天数(从最后一天往前数)\n const today = formatDateKey(new Date());\n const lastCommitDate = sortedDates[sortedDates.length - 1];\n\n // 如果最后提交日期是今天或昨天,计算当前连续\n const lastDate = new Date(lastCommitDate);\n const todayDate = new Date(today);\n const daysSinceLastCommit = Math.round(\n (todayDate.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (daysSinceLastCommit <= 1) {\n currentStreakCount = 1;\n for (let i = sortedDates.length - 2; i >= 0; i--) {\n const currDate = new Date(sortedDates[i + 1]);\n const prevDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n currentStreakCount++;\n } else {\n break;\n }\n }\n } else {\n currentStreakCount = 0;\n }\n\n return { longestStreak, currentStreak: currentStreakCount };\n}\n\n/** 获取 ISO 周标识 */\nfunction getWeekKey(date: Date): string {\n const d = new Date(date);\n d.setHours(0, 0, 0, 0);\n d.setDate(d.getDate() + 4 - (d.getDay() || 7));\n const yearStart = new Date(d.getFullYear(), 0, 1);\n const weekNo = Math.ceil(\n ((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7\n );\n return `${d.getFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\n/** 计算趋势数据 */\nfunction calculateTrends(commits: CommitRecord[]): TrendData {\n if (commits.length === 0) {\n return emptyTrendData();\n }\n\n // 周趋势\n const weekMap = new Map<string, WeeklyPoint>();\n for (const commit of commits) {\n const week = getWeekKey(commit.date);\n const entry = weekMap.get(week) || {\n week,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n };\n entry.commits++;\n for (const file of commit.files) {\n entry.linesAdded += file.added;\n entry.linesDeleted += file.deleted;\n }\n weekMap.set(week, entry);\n }\n const weeklyTrend = Array.from(weekMap.values()).sort((a, b) =>\n a.week.localeCompare(b.week)\n );\n\n // 累计代码量\n const dailyNet = new Map<string, number>();\n for (const commit of commits) {\n const dateKey = formatDateKey(commit.date);\n const net = commit.files.reduce((sum, f) => sum + f.added - f.deleted, 0);\n dailyNet.set(dateKey, (dailyNet.get(dateKey) || 0) + net);\n }\n\n let cumulative = 0;\n const cumulativeLines: CumulativePoint[] = Array.from(dailyNet.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, net]) => {\n cumulative += net;\n return { date, netLines: cumulative };\n });\n\n return { weeklyTrend, cumulativeLines };\n}\n\n/** 计算协作指标 */\nfunction calculateCollaboration(commits: CommitRecord[]): CollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaborationMetrics();\n }\n\n const fileAuthors = new Map<string, Set<string>>();\n const fileCommits = new Map<string, number>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const authors = fileAuthors.get(file.path) || new Set();\n authors.add(commit.email.toLowerCase());\n fileAuthors.set(file.path, authors);\n fileCommits.set(file.path, (fileCommits.get(file.path) || 0) + 1);\n }\n }\n\n const soloFiles: SoloFile[] = [];\n const collaborationHotspots: CollabFile[] = [];\n\n for (const [path, authors] of fileAuthors) {\n const commitCount = fileCommits.get(path) || 0;\n if (authors.size === 1 && commitCount >= 3) {\n soloFiles.push({\n path,\n author: Array.from(authors)[0],\n commits: commitCount,\n });\n } else if (authors.size >= 2 && commitCount >= 5) {\n collaborationHotspots.push({\n path,\n authorCount: authors.size,\n totalCommits: commitCount,\n });\n }\n }\n\n return {\n soloFiles: soloFiles.sort((a, b) => b.commits - a.commits).slice(0, 10),\n collaborationHotspots: collaborationHotspots\n .sort((a, b) => b.totalCommits - a.totalCommits)\n .slice(0, 10),\n };\n}\n\n/** 计算 Commit Message 统计 */\nfunction calculateMessageStats(commits: CommitRecord[]): CommitMessageStats {\n if (commits.length === 0) {\n return emptyMessageStats();\n }\n\n const typeDistribution: Record<string, number> = {};\n let totalLength = 0;\n\n const typeRegex =\n /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?:/i;\n\n for (const commit of commits) {\n totalLength += commit.message.length;\n const match = commit.message.match(typeRegex);\n if (match) {\n const type = match[1].toLowerCase();\n typeDistribution[type] = (typeDistribution[type] || 0) + 1;\n } else {\n typeDistribution['other'] = (typeDistribution['other'] || 0) + 1;\n }\n }\n\n return {\n typeDistribution,\n avgMessageLength: totalLength / commits.length,\n };\n}\n\n/** 计算作者文件类型贡献 */\nfunction calculateAuthorFileTypeContributions(\n commits: CommitRecord[]\n): AuthorFileTypeContribution[] {\n if (commits.length === 0) {\n return [];\n }\n\n // 数据结构: Map<author-email-extension, contribution>\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n\n // 跟踪每个作者-扩展名组合修改的唯一文件\n const uniqueFilesMap = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n let contribution = contributionMap.get(key);\n if (!contribution) {\n contribution = {\n author: commit.author,\n email: commit.email,\n extension: ext,\n linesAdded: 0,\n linesDeleted: 0,\n commits: 0,\n fileCount: 0,\n };\n contributionMap.set(key, contribution);\n uniqueFilesMap.set(key, new Set());\n }\n\n contribution.linesAdded += file.added;\n contribution.linesDeleted += file.deleted;\n uniqueFilesMap.get(key)!.add(file.path);\n }\n }\n\n // 统计每个组合的提交数(去重)\n const commitCountMap = new Map<string, Set<string>>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n if (!commitCountMap.has(key)) {\n commitCountMap.set(key, new Set());\n }\n commitCountMap.get(key)!.add(commit.hash);\n }\n }\n\n // 更新 commits 和 fileCount\n for (const [key, contribution] of contributionMap) {\n contribution.commits = commitCountMap.get(key)?.size || 0;\n contribution.fileCount = uniqueFilesMap.get(key)?.size || 0;\n }\n\n // 按总变更行数(增+删)降序排序,取 TOP 20\n return Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n}\n\n// ============================================================\n// 空值工厂函数\n// ============================================================\n\nfunction emptyQualityMetrics(): QualityMetrics {\n return {\n avgFilesPerCommit: 0,\n avgLinesPerCommit: 0,\n churnRate: 0,\n hotFiles: [],\n };\n}\n\nfunction emptyTimePatterns(): TimePatterns {\n return {\n weekdayDistribution: new Array<number>(7).fill(0),\n weekendCommits: 0,\n avgCommitInterval: 0,\n longestStreak: 0,\n currentStreak: 0,\n };\n}\n\nfunction emptyTrendData(): TrendData {\n return {\n weeklyTrend: [],\n cumulativeLines: [],\n };\n}\n\nfunction emptyCollaborationMetrics(): CollaborationMetrics {\n return {\n soloFiles: [],\n collaborationHotspots: [],\n };\n}\n\nfunction emptyMessageStats(): CommitMessageStats {\n return {\n typeDistribution: {},\n avgMessageLength: 0,\n };\n}\n","import type { CommitRecord, TeamHealthMetrics, CriticalAuthor } from '../../types/index.js';\n\n/**\n * 计算团队健康度指标\n */\nexport function calculateTeamHealth(commits: CommitRecord[]): TeamHealthMetrics {\n // 边界情况:空提交\n if (commits.length === 0) {\n return emptyTeamHealth();\n }\n\n // Step 1: 构建数据结构\n const authorFiles = buildAuthorFilesMap(commits);\n const fileAuthors = buildFileAuthorsMap(commits);\n\n // 边界情况:无文件变更\n if (fileAuthors.size === 0) {\n return emptyTeamHealth();\n }\n\n // Step 2: 计算每个作者的知识独占度\n const authorScores = calculateAuthorScores(authorFiles, fileAuthors);\n\n // Step 3: 筛选关键人员(评分 >10)\n const criticalAuthors = authorScores.filter((a) => a.knowledgeScore > 10);\n\n // Step 4: 计算知识分布均匀度\n const totalUniqueFiles = criticalAuthors.reduce(\n (sum, a) => sum + a.uniqueFiles.length,\n 0\n );\n const knowledgeDistribution = 1 - totalUniqueFiles / fileAuthors.size;\n\n // Step 5: 评估风险等级\n const busFactor = criticalAuthors.length;\n const riskLevel = busFactor === 1 ? 'high' : busFactor <= 3 ? 'medium' : 'low';\n\n return {\n busFactor,\n criticalAuthors,\n knowledgeDistribution,\n riskLevel,\n };\n}\n\n/**\n * 构建 作者 -> 文件集合 映射\n */\nfunction buildAuthorFilesMap(commits: CommitRecord[]): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n if (!map.has(authorKey)) {\n map.set(authorKey, new Set());\n }\n const files = map.get(authorKey)!;\n for (const file of commit.files) {\n files.add(file.path);\n }\n }\n\n return map;\n}\n\n/**\n * 构建 文件 -> (作者 -> 提交次数) 映射\n */\nfunction buildFileAuthorsMap(commits: CommitRecord[]): Map<string, Map<string, number>> {\n const map = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n for (const file of commit.files) {\n if (!map.has(file.path)) {\n map.set(file.path, new Map());\n }\n const authors = map.get(file.path)!;\n authors.set(authorKey, (authors.get(authorKey) || 0) + 1);\n }\n }\n\n return map;\n}\n\n/**\n * 计算每个作者的知识独占度评分\n */\nfunction calculateAuthorScores(\n authorFiles: Map<string, Set<string>>,\n fileAuthors: Map<string, Map<string, number>>\n): CriticalAuthor[] {\n const scores: CriticalAuthor[] = [];\n\n for (const [authorKey, files] of authorFiles) {\n const uniqueFiles: string[] = [];\n const dominantFiles: string[] = [];\n\n for (const filePath of files) {\n const authors = fileAuthors.get(filePath)!;\n const authorCount = authors.size;\n\n // 独有文件:只有该作者修改过\n if (authorCount === 1) {\n uniqueFiles.push(filePath);\n continue;\n }\n\n // 主导文件:该作者贡献 >50%\n const authorCommits = authors.get(authorKey) || 0;\n const totalCommits = Array.from(authors.values()).reduce((a, b) => a + b, 0);\n if (authorCommits / totalCommits > 0.5) {\n dominantFiles.push(filePath);\n }\n }\n\n // 知识独占度评分 = (独有文件*2 + 主导文件) / 总文件数 * 100\n const knowledgeScore =\n ((uniqueFiles.length * 2 + dominantFiles.length) / fileAuthors.size) * 100;\n\n const [name, email] = parseAuthorKey(authorKey);\n scores.push({\n name,\n email,\n uniqueFiles,\n dominantFiles,\n knowledgeScore,\n });\n }\n\n return scores;\n}\n\n/**\n * 解析作者键 \"name <email>\" -> [name, email]\n */\nfunction parseAuthorKey(authorKey: string): [string, string] {\n const match = authorKey.match(/^(.+) <(.+)>$/);\n if (match) {\n return [match[1], match[2]];\n }\n return [authorKey, ''];\n}\n\nfunction emptyTeamHealth(): TeamHealthMetrics {\n return {\n busFactor: 0,\n criticalAuthors: [],\n knowledgeDistribution: 1,\n riskLevel: 'low',\n };\n}\n","import type { CommitRecord, StabilityMetrics, FileChurn, DirectoryChurn } from '../../types/index.js';\n\n/**\n * 计算代码稳定性指标\n */\nexport function calculateStability(commits: CommitRecord[]): StabilityMetrics {\n if (commits.length === 0) {\n return emptyStability();\n }\n\n // Step 1: 统计文件级流失率\n const fileStats = new Map<string, { added: number; deleted: number; modifyCount: number }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const stat = fileStats.get(file.path) || { added: 0, deleted: 0, modifyCount: 0 };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.modifyCount++;\n fileStats.set(file.path, stat);\n }\n }\n\n // 转换为 FileChurn 数组,按流失率排序,取 TOP 20\n const fileChurnRate: FileChurn[] = Array.from(fileStats.entries())\n .map(([path, { added, deleted, modifyCount }]) => ({\n path,\n added,\n deleted,\n churnRate: added > 0 ? deleted / added : 0,\n modifyCount,\n isUnstable: deleted / Math.max(added, 1) > 0.5,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 20);\n\n // Step 2: 统计目录级流失率\n const dirStats = new Map<string, { added: number; deleted: number; files: Set<string> }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const dir = getTopDirectory(file.path);\n const stat = dirStats.get(dir) || { added: 0, deleted: 0, files: new Set() };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.files.add(file.path);\n dirStats.set(dir, stat);\n }\n }\n\n const directoryChurnRate: DirectoryChurn[] = Array.from(dirStats.entries())\n .map(([path, { added, deleted, files }]) => ({\n path,\n churnRate: added > 0 ? deleted / added : 0,\n totalChanges: added + deleted,\n fileCount: files.size,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 10);\n\n // Step 3: 计算 Revert 率\n const revertCommits = commits.filter((c) => /revert|rollback/i.test(c.message)).length;\n const revertRate = revertCommits / commits.length;\n\n // Step 4: 计算 Fix 提交率\n const fixCommits = commits.filter((c) => /^fix(\\(.+\\))?:/i.test(c.message)).length;\n const fixCommitRate = fixCommits / commits.length;\n\n // Step 5: 计算稳定性评分\n const avgChurnRate =\n fileChurnRate.length > 0\n ? fileChurnRate.reduce((sum, f) => sum + f.churnRate, 0) / fileChurnRate.length\n : 0;\n\n const stabilityScore = Math.max(\n 0,\n Math.round(100 - (avgChurnRate * 50 + revertRate * 100 + fixCommitRate * 50))\n );\n\n return {\n fileChurnRate,\n directoryChurnRate,\n revertRate: Math.round(revertRate * 100) / 100,\n fixCommitRate: Math.round(fixCommitRate * 100) / 100,\n stabilityScore,\n };\n}\n\n/**\n * 获取文件路径的第一层目录\n */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\nfunction emptyStability(): StabilityMetrics {\n return {\n fileChurnRate: [],\n directoryChurnRate: [],\n revertRate: 0,\n fixCommitRate: 0,\n stabilityScore: 100,\n };\n}\n","import type { CommitRecord, WorkPressureMetrics, HolidayCommit } from '../../types/index.js';\n\n/**\n * 计算工作压力指标\n */\nexport function calculateWorkPressure(commits: CommitRecord[]): WorkPressureMetrics {\n if (commits.length === 0) {\n return emptyWorkPressure();\n }\n\n let lateNightCommits = 0;\n let earlyMorningCommits = 0;\n let weekendCommits = 0;\n\n const holidayMap = new Map<string, { name: string; commits: number }>();\n const holidays = getHolidays();\n\n for (const commit of commits) {\n const hour = commit.date.getHours();\n const day = commit.date.getDay();\n const dateKey = formatDate(commit.date);\n\n // 深夜 (23:00-02:00)\n if (hour >= 23 || hour < 2) {\n lateNightCommits++;\n }\n\n // 凌晨 (02:00-06:00)\n if (hour >= 2 && hour < 6) {\n earlyMorningCommits++;\n }\n\n // 周末\n if (day === 0 || day === 6) {\n weekendCommits++;\n }\n\n // 假期\n const holiday = holidays.get(dateKey);\n if (holiday) {\n const entry = holidayMap.get(dateKey) || { name: holiday, commits: 0 };\n entry.commits++;\n holidayMap.set(dateKey, entry);\n }\n }\n\n const holidayCommits: HolidayCommit[] = Array.from(holidayMap.entries())\n .map(([date, { name, commits }]) => ({ date, holidayName: name, commits }))\n .sort((a, b) => b.commits - a.commits);\n\n // 非工作时间占比\n const holidayTotal = holidayCommits.reduce((sum, h) => sum + h.commits, 0);\n const offHoursCount = lateNightCommits + earlyMorningCommits + weekendCommits + holidayTotal;\n const offHoursRate = offHoursCount / commits.length;\n\n // 压力评分 (0-100)\n const lateNightWeight = (lateNightCommits / commits.length) * 40;\n const earlyMorningWeight = (earlyMorningCommits / commits.length) * 30;\n const weekendWeight = (weekendCommits / commits.length) * 20;\n const holidayWeight = (holidayCommits.length > 0 ? 1 : 0) * 10;\n\n const pressureScore = Math.round(lateNightWeight + earlyMorningWeight + weekendWeight + holidayWeight);\n\n return {\n lateNightCommits,\n earlyMorningCommits,\n weekendCommits,\n holidayCommits,\n pressureScore,\n offHoursRate: Math.round(offHoursRate * 1000) / 1000\n };\n}\n\nfunction emptyWorkPressure(): WorkPressureMetrics {\n return {\n lateNightCommits: 0,\n earlyMorningCommits: 0,\n weekendCommits: 0,\n holidayCommits: [],\n pressureScore: 0,\n offHoursRate: 0,\n };\n}\n\n/**\n * 获取中国 2024-2026 主要节假日\n */\nfunction getHolidays(): Map<string, string> {\n const holidays = new Map<string, string>();\n\n // 2024\n const dates2024: Array<[string, string]> = [\n ['2024-01-01', '元旦'],\n ['2024-02-10', '春节'], ['2024-02-11', '春节'], ['2024-02-12', '春节'],\n ['2024-04-04', '清明节'], ['2024-04-05', '清明节'], ['2024-04-06', '清明节'],\n ['2024-05-01', '劳动节'], ['2024-05-02', '劳动节'], ['2024-05-03', '劳动节'],\n ['2024-06-10', '端午节'],\n ['2024-09-15', '中秋节'], ['2024-09-16', '中秋节'], ['2024-09-17', '中秋节'],\n ['2024-10-01', '国庆节'], ['2024-10-02', '国庆节'], ['2024-10-03', '国庆节']\n ];\n\n // 2025\n const dates2025: Array<[string, string]> = [\n ['2025-01-01', '元旦'],\n ['2025-01-29', '春节'], ['2025-01-30', '春节'], ['2025-01-31', '春节'],\n ['2025-04-04', '清明节'], ['2025-04-05', '清明节'], ['2025-04-06', '清明节'],\n ['2025-05-01', '劳动节'], ['2025-05-02', '劳动节'], ['2025-05-03', '劳动节'],\n ['2025-05-31', '端午节'],\n ['2025-10-01', '国庆节'], ['2025-10-02', '国庆节'], ['2025-10-06', '中秋节']\n ];\n\n // 2026\n const dates2026: Array<[string, string]> = [\n ['2026-01-01', '元旦'],\n ['2026-02-17', '春节'], ['2026-02-18', '春节'], ['2026-02-19', '春节'],\n ['2026-04-05', '清明节'],\n ['2026-05-01', '劳动节'],\n ['2026-06-19', '端午节'],\n ['2026-09-25', '中秋节'],\n ['2026-10-01', '国庆节'], ['2026-10-02', '国庆节']\n ];\n\n [...dates2024, ...dates2025, ...dates2026].forEach(([date, name]) => {\n holidays.set(date, name);\n });\n\n return holidays;\n}\n\n/**\n * 格式化日期为 YYYY-MM-DD\n */\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n","import type { CommitRecord, ContributorChurnMetrics } from '../../types/index.js';\n\n/**\n * 计算贡献者流失率指标\n */\nexport function calculateContributorChurn(commits: CommitRecord[]): ContributorChurnMetrics {\n if (commits.length === 0) {\n return emptyContributorChurn();\n }\n\n // 统计每个作者的最后提交时间\n const authorLastCommit = new Map<string, {\n name: string;\n email: string;\n lastDate: Date;\n firstDate: Date;\n totalCommits: number;\n }>();\n\n for (const commit of commits) {\n const key = commit.email.toLowerCase();\n const existing = authorLastCommit.get(key);\n\n if (!existing) {\n authorLastCommit.set(key, {\n name: commit.author,\n email: commit.email,\n lastDate: commit.date,\n firstDate: commit.date,\n totalCommits: 1\n });\n } else {\n if (commit.date > existing.lastDate) {\n existing.lastDate = commit.date;\n }\n if (commit.date < existing.firstDate) {\n existing.firstDate = commit.date;\n }\n existing.totalCommits++;\n }\n }\n\n // 四级分类\n const now = new Date();\n const active: AuthorDetail[] = [];\n const occasional: AuthorDetail[] = [];\n const dormant: AuthorDetail[] = [];\n const lost: AuthorDetail[] = [];\n const newJoiners: AuthorDetail[] = [];\n\n for (const [, author] of authorLastCommit) {\n const daysSinceLast = Math.floor((now.getTime() - author.lastDate.getTime()) / (1000 * 60 * 60 * 24));\n const daysSinceFirst = Math.floor((now.getTime() - author.firstDate.getTime()) / (1000 * 60 * 60 * 24));\n\n const detail: AuthorDetail = {\n name: author.name,\n email: author.email,\n lastCommitDate: author.lastDate,\n daysSinceLastCommit: daysSinceLast,\n totalCommits: author.totalCommits\n };\n\n // 新加入(首次提交 <30天)\n if (daysSinceFirst < 30) {\n newJoiners.push(detail);\n }\n\n // 按最后活跃时间分类\n if (daysSinceLast < 30) {\n active.push(detail);\n } else if (daysSinceLast < 90) {\n occasional.push(detail);\n } else if (daysSinceLast < 180) {\n dormant.push(detail);\n } else {\n lost.push(detail);\n }\n }\n\n // 计算比率并排序\n const totalAuthors = authorLastCommit.size;\n const churnRate = totalAuthors > 0 ? lost.length / totalAuthors : 0;\n const retentionRate = totalAuthors > 0 ? active.length / totalAuthors : 0;\n const growthRate = totalAuthors > 0 ? newJoiners.length / totalAuthors : 0;\n\n return {\n active: active.sort((a, b) => b.totalCommits - a.totalCommits),\n occasional: occasional.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n dormant: dormant.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n lost: lost.sort((a, b) => b.totalCommits - a.totalCommits),\n newJoiners: newJoiners.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n churnRate: Math.round(churnRate * 1000) / 1000,\n retentionRate: Math.round(retentionRate * 1000) / 1000,\n growthRate: Math.round(growthRate * 1000) / 1000\n };\n}\n\nfunction emptyContributorChurn(): ContributorChurnMetrics {\n return {\n active: [],\n occasional: [],\n dormant: [],\n lost: [],\n newJoiners: [],\n churnRate: 0,\n retentionRate: 0,\n growthRate: 0,\n };\n}\n","import type { CommitRecord, AdvancedCollaborationMetrics, FilePair, AuthorPair } from '../../types/index.js';\n\nconst MAX_FILES_PER_COMMIT = 50;\n\n/**\n * 计算高级协作指标\n */\nexport function calculateAdvancedCollaboration(commits: CommitRecord[]): AdvancedCollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaboration();\n }\n\n // 统计文件修改次数\n const fileModifyCount = new Map<string, number>();\n for (const commit of commits) {\n commit.files.forEach(f => {\n fileModifyCount.set(f.path, (fileModifyCount.get(f.path) || 0) + 1);\n });\n }\n\n // Step 1: 检测紧密耦合 - 统计同提交文件对\n const tightCoupling = detectTightCoupling(commits, fileModifyCount);\n\n // Step 2: 检测结对编程\n const pairProgramming = detectPairProgramming(commits);\n\n // Step 3: 计算耦合评分\n const avgCoupling = tightCoupling.length > 0\n ? tightCoupling.reduce((sum, p) => sum + p.coupling, 0) / tightCoupling.length\n : 0;\n const couplingScore = Math.min(100, Math.round(avgCoupling * 100));\n\n return {\n tightCoupling,\n frequentPairs: [], // 简化版:跳过 24h 窗口分析\n pairProgramming,\n couplingScore,\n };\n}\n\n/**\n * 检测紧密耦合:统计同一提交中的文件对\n */\nfunction detectTightCoupling(\n commits: CommitRecord[],\n fileModifyCount: Map<string, number>\n): FilePair[] {\n const coCommitPairs = new Map<string, { count: number; file1: string; file2: string }>();\n\n for (const commit of commits) {\n // 性能优化:限制文件数,避免大型提交导致组合爆炸\n const files = commit.files\n .map(f => f.path)\n .slice(0, MAX_FILES_PER_COMMIT)\n .sort();\n\n // 组合文件对\n for (let i = 0; i < files.length; i++) {\n for (let j = i + 1; j < files.length; j++) {\n const pairKey = `${files[i]}|||${files[j]}`;\n const existing = coCommitPairs.get(pairKey) || {\n count: 0,\n file1: files[i],\n file2: files[j],\n };\n existing.count++;\n coCommitPairs.set(pairKey, existing);\n }\n }\n }\n\n // 计算耦合度并筛选\n const tightCoupling: FilePair[] = Array.from(coCommitPairs.values())\n .map(({ file1, file2, count }) => {\n const count1 = fileModifyCount.get(file1) || 1;\n const count2 = fileModifyCount.get(file2) || 1;\n const coupling = count / Math.min(count1, count2);\n\n return {\n file1,\n file2,\n coOccurrence: count,\n coupling,\n };\n })\n .filter(p => p.coOccurrence >= 3) // 至少共同出现 3 次\n .sort((a, b) => b.coupling - a.coupling)\n .slice(0, 20); // TOP 20\n\n return tightCoupling;\n}\n\n/**\n * 检测结对编程:找出在多个文件上协作的作者对\n */\nfunction detectPairProgramming(commits: CommitRecord[]): AuthorPair[] {\n // 统计每个文件的作者贡献\n const fileAuthors = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n if (!fileAuthors.has(file.path)) {\n fileAuthors.set(file.path, new Map());\n }\n const authorMap = fileAuthors.get(file.path)!;\n const email = commit.email.toLowerCase();\n authorMap.set(email, (authorMap.get(email) || 0) + 1);\n }\n }\n\n // 识别结对编程:两个作者都是文件的主要贡献者\n const authorPairMap = new Map<string, {\n author1: string;\n author2: string;\n files: Set<string>;\n count: number;\n }>();\n\n for (const [filePath, authorMap] of fileAuthors) {\n if (authorMap.size >= 2) {\n // 取贡献最多的两个作者\n const sortedAuthors = Array.from(authorMap.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 2);\n\n if (sortedAuthors.length === 2) {\n // 规范化作者对的键(按字母序)\n const [a1, a2] = [sortedAuthors[0][0], sortedAuthors[1][0]].sort();\n const pairKey = `${a1}|||${a2}`;\n\n const existing = authorPairMap.get(pairKey) || {\n author1: a1,\n author2: a2,\n files: new Set<string>(),\n count: 0,\n };\n\n existing.files.add(filePath);\n existing.count++;\n authorPairMap.set(pairKey, existing);\n }\n }\n }\n\n // 筛选并排序\n const pairProgramming: AuthorPair[] = Array.from(authorPairMap.values())\n .filter(p => p.files.size >= 3) // 至少 3 个共享文件\n .map(p => ({\n author1: p.author1,\n author2: p.author2,\n sharedFiles: Array.from(p.files),\n collaborationCount: p.count,\n }))\n .sort((a, b) => b.collaborationCount - a.collaborationCount)\n .slice(0, 10); // TOP 10\n\n return pairProgramming;\n}\n\nfunction emptyCollaboration(): AdvancedCollaborationMetrics {\n return {\n tightCoupling: [],\n frequentPairs: [],\n pairProgramming: [],\n couplingScore: 0,\n };\n}\n","import type { CommitRecord } from '../../types/index.js';\nimport type {\n TeamHealthMetrics,\n StabilityMetrics,\n WorkPressureMetrics,\n ContributorChurnMetrics,\n AdvancedCollaborationMetrics,\n} from '../../types/index.js';\n\nimport { calculateTeamHealth } from './team-health.js';\nimport { calculateStability } from './code-stability.js';\nimport { calculateWorkPressure } from './work-pressure.js';\nimport { calculateContributorChurn } from './contributor-churn.js';\nimport { calculateAdvancedCollaboration } from './collaboration.js';\n\n/**\n * 高级统计结果集合\n */\nexport interface AdvancedStats {\n teamHealth: TeamHealthMetrics;\n stability: StabilityMetrics;\n workPressure: WorkPressureMetrics;\n contributorChurn: ContributorChurnMetrics;\n advancedCollaboration: AdvancedCollaborationMetrics;\n}\n\n/**\n * 一次性计算所有高级统计\n */\nexport function calculateAdvancedStats(commits: CommitRecord[]): AdvancedStats {\n return {\n teamHealth: calculateTeamHealth(commits),\n stability: calculateStability(commits),\n workPressure: calculateWorkPressure(commits),\n contributorChurn: calculateContributorChurn(commits),\n advancedCollaboration: calculateAdvancedCollaboration(commits),\n };\n}\n\n// 导出单独的计算函数\nexport {\n calculateTeamHealth,\n calculateStability,\n calculateWorkPressure,\n calculateContributorChurn,\n calculateAdvancedCollaboration,\n};\n","import { writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport open from 'open';\nimport { buildHtml } from './html-builder.js';\nimport type { CommitStats, ReportOptions } from '../types/index.js';\n\n/**\n * 生成 HTML 报告并可选地打开浏览器\n */\nexport async function generateReport(\n stats: CommitStats,\n options: ReportOptions\n): Promise<void> {\n const spinner = ora('生成报告...').start();\n\n try {\n const html = await buildHtml(stats, options);\n const outputPath = resolve(process.cwd(), options.outputPath);\n\n await writeFile(outputPath, html, 'utf-8');\n spinner.succeed(`报告已生成: ${chalk.cyan(outputPath)}`);\n\n if (options.autoOpen) {\n await open(outputPath);\n console.log(chalk.green('✓ 已在浏览器中打开'));\n }\n } catch (error) {\n spinner.fail('生成报告失败');\n throw error;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { CommitStats, ReportOptions, ReportData } from '../types/index.js';\n\n/**\n * 组装完整的 HTML 报告\n */\nexport async function buildHtml(\n stats: CommitStats,\n options: ReportOptions\n): Promise<string> {\n const template = await loadTemplate();\n\n const reportData: ReportData = {\n stats: serializeStats(stats),\n generatedAt: new Date().toLocaleString('zh-CN'),\n timeRange: options.timeRange\n ? {\n from: options.timeRange.from.toISOString().split('T')[0],\n to: options.timeRange.to.toISOString().split('T')[0],\n }\n : null,\n repos: options.repoNames,\n };\n\n // 安全地序列化数据(防止 XSS)\n const jsonData = JSON.stringify(reportData)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026');\n\n return template.replace('__REPORT_DATA__', jsonData);\n}\n\n/**\n * 将 CommitStats 转为可 JSON 序列化的格式\n * Date 对象转为 ISO 字符串\n */\nfunction serializeStats(stats: CommitStats): Record<string, unknown> {\n const serializeAuthorDetail = (author: { lastCommitDate: Date; [key: string]: unknown }) => ({\n ...author,\n lastCommitDate: author.lastCommitDate.toISOString(),\n });\n\n return {\n ...stats,\n firstCommitDate: stats.firstCommitDate.toISOString(),\n lastCommitDate: stats.lastCommitDate.toISOString(),\n authors: stats.authors.map((a) => ({\n ...a,\n lastActiveDate: a.lastActiveDate.toISOString(),\n })),\n contributorChurn: stats.contributorChurn\n ? {\n ...stats.contributorChurn,\n active: stats.contributorChurn.active.map(serializeAuthorDetail),\n occasional: stats.contributorChurn.occasional.map(serializeAuthorDetail),\n dormant: stats.contributorChurn.dormant.map(serializeAuthorDetail),\n lost: stats.contributorChurn.lost.map(serializeAuthorDetail),\n newJoiners: stats.contributorChurn.newJoiners.map(serializeAuthorDetail),\n }\n : undefined,\n };\n}\n\n/**\n * 加载 HTML 模板\n */\nasync function loadTemplate(): Promise<string> {\n // 支持两种路径:开发模式和打包后模式\n const currentDir = dirname(fileURLToPath(import.meta.url));\n\n // 打包后: dist/index.js -> ../templates/report.html\n // 开发模式: src/reporter/html-builder.ts -> ../../templates/report.html\n const possiblePaths = [\n resolve(currentDir, '../templates/report.html'),\n resolve(currentDir, '../../templates/report.html'),\n resolve(currentDir, '../../../templates/report.html'),\n ];\n\n for (const templatePath of possiblePaths) {\n try {\n return await readFile(templatePath, 'utf-8');\n } catch {\n // 继续尝试下一个路径\n }\n }\n\n throw new Error('无法找到 HTML 模板文件');\n}\n","import type { CliOptions, TimeRange } from '../types/index.js';\n\n/** 时间预设格式: 7d / 1m / 3m / 6m / 1y / all */\nconst PERIOD_REGEX = /^(\\d+)(d|m|y)$/;\n\n/**\n * 解析时间预设字符串,返回起止时间范围\n * 'all' 表示不限时间(从 1970 年至今)\n */\nexport function parsePeriod(period: string): TimeRange | null {\n if (period === 'all') {\n return null;\n }\n\n const match = PERIOD_REGEX.exec(period);\n if (!match) {\n throw new Error(`无效的时间预设: \"${period}\",支持格式: 7d, 1m, 3m, 6m, 1y, all`);\n }\n\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const to = new Date();\n const from = new Date();\n\n switch (unit) {\n case 'd':\n from.setDate(from.getDate() - amount);\n break;\n case 'm':\n from.setMonth(from.getMonth() - amount);\n break;\n case 'y':\n from.setFullYear(from.getFullYear() - amount);\n break;\n }\n\n return { from, to };\n}\n\n/**\n * 解析日期字符串 YYYY-MM-DD\n */\nfunction parseDate(dateStr: string): Date {\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) {\n throw new Error(`无效的日期格式: \"${dateStr}\",请使用 YYYY-MM-DD 格式`);\n }\n return date;\n}\n\n/**\n * 根据 CLI 参数解析最终的时间范围\n * --from / --to 优先于 --period\n * 返回 null 表示不限时间\n */\nexport function resolveTimeRange(opts: CliOptions): TimeRange | null {\n if (opts.from || opts.to) {\n const to = opts.to ? parseDate(opts.to) : new Date();\n const from = opts.from ? parseDate(opts.from) : (() => {\n const d = new Date(to);\n d.setMonth(d.getMonth() - 3);\n return d;\n })();\n\n if (from > to) {\n throw new Error('起始日期不能晚于结束日期');\n }\n\n return { from, to };\n }\n\n return parsePeriod(opts.period);\n}\n"],"mappings":"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","names":["chalk","ora","execSync","join","stat","getTopDirectory","commits","ora","resolve","chalk","ora","readFile","ora","resolve","chalk","chalk","formatDate","execSync"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/scanner/index.ts","../src/analyzer/index.ts","../src/analyzer/git-log-parser.ts","../src/analyzer/stats-calculator.ts","../src/analyzer/advanced/team-health.ts","../src/analyzer/advanced/code-stability.ts","../src/analyzer/advanced/work-pressure.ts","../src/analyzer/advanced/contributor-churn.ts","../src/analyzer/advanced/collaboration.ts","../src/analyzer/advanced/index.ts","../src/reporter/index.ts","../src/reporter/html-builder.ts","../src/cli/time-utils.ts"],"sourcesContent":["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { checkbox } from '@inquirer/prompts';\nimport { scanRepositories } from '../scanner/index.js';\nimport { analyzeRepos } from '../analyzer/index.js';\nimport { generateReport } from '../reporter/index.js';\nimport { parsePeriod, resolveTimeRange } from './time-utils.js';\nimport type { CliOptions, RepoInfo } from '../types/index.js';\n\nconst program = new Command();\n\nprogram\n .name('commit-report')\n .description('Git 提交统计工具,生成可视化 HTML 报告')\n .version('1.0.0')\n .argument('[directory]', '要扫描的目录路径', process.cwd())\n .option('-p, --period <period>', '时间预设 (7d/1m/3m/6m/1y/all)', 'all')\n .option('-f, --from <date>', '起始日期 (YYYY-MM-DD)')\n .option('-t, --to <date>', '结束日期 (YYYY-MM-DD)')\n .option('-a, --author <name>', '过滤作者')\n .option('-o, --output <file>', '输出文件名', 'commit-report.html')\n .option('--no-open', '不自动打开浏览器')\n .option('-d, --depth <number>', '最大扫描深度', '20')\n .action(async (directory: string, opts: CliOptions) => {\n try {\n await run(directory, opts);\n } catch (error) {\n if (error instanceof Error && error.message === 'USER_CANCEL') {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n }\n console.error(chalk.red(`\\n错误: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n });\n\nasync function run(directory: string, opts: CliOptions): Promise<void> {\n // 1. 检查 Git 是否安装\n await checkGitInstalled();\n\n // 2. 解析时间范围\n const timeRange = resolveTimeRange(opts);\n\n // 3. 扫描仓库\n const repos = await scanRepositories({\n targetDir: directory,\n maxDepth: Number(opts.depth),\n });\n\n if (repos.length === 0) {\n console.log(chalk.red('未找到 Git 仓库'));\n process.exit(1);\n }\n\n // 4. 选择仓库\n let selectedRepos: RepoInfo[];\n\n if (repos.length === 1) {\n selectedRepos = repos;\n console.log(chalk.cyan(`找到 1 个 Git 仓库: ${repos[0].name}`));\n } else {\n console.log(chalk.cyan(`\\n找到 ${repos.length} 个 Git 仓库:\\n`));\n\n const selected = await checkbox<string>({\n message: '选择要分析的仓库(空格选择,回车确认)',\n choices: repos.map((repo) => ({\n name: `${repo.name} (${repo.commitCount} commits)`,\n value: repo.path,\n checked: true,\n })),\n });\n\n if (selected.length === 0) {\n console.log(chalk.yellow('未选择任何仓库'));\n process.exit(0);\n }\n\n selectedRepos = repos.filter((r) => selected.includes(r.path));\n }\n\n const timeRangeText = timeRange\n ? `${formatDate(timeRange.from)} ~ ${formatDate(timeRange.to)}`\n : '所有提交';\n console.log(\n chalk.gray(\n `\\n已选择 ${selectedRepos.length} 个仓库,时间范围:${timeRangeText}\\n`\n )\n );\n\n // 5. 分析提交记录\n const stats = await analyzeRepos({\n repos: selectedRepos,\n timeRange,\n author: opts.author,\n });\n\n if (stats.totalCommits === 0) {\n console.log(chalk.yellow('该时间段无提交记录'));\n }\n\n // 6. 生成报告\n await generateReport(stats, {\n outputPath: opts.output,\n autoOpen: opts.open,\n timeRange,\n repoNames: selectedRepos.map((r) => r.name),\n });\n}\n\nasync function checkGitInstalled(): Promise<void> {\n const { execSync } = await import('child_process');\n try {\n execSync('git --version', { stdio: 'ignore' });\n } catch {\n console.error(chalk.red('请先安装 Git'));\n process.exit(1);\n }\n}\n\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n\n// 处理 Ctrl+C\nprocess.on('SIGINT', () => {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n});\n\nprogram.parse();\n","import { readdir, stat, access } from 'node:fs/promises';\nimport { join, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { confirm } from '@inquirer/prompts';\nimport type { ScanOptions, RepoInfo } from '../types/index.js';\n\n/** 扫描时忽略的目录名 */\nconst IGNORE_DIRS = new Set([\n 'node_modules',\n '.git',\n 'vendor',\n 'dist',\n 'build',\n '.cache',\n '.next',\n '.nuxt',\n '__pycache__',\n 'target',\n]);\n\n/**\n * 递归扫描目录,查找所有 Git 仓库\n */\nexport async function scanRepositories(options: ScanOptions): Promise<RepoInfo[]> {\n const spinner = ora('扫描仓库中...').start();\n const repos: RepoInfo[] = [];\n let deepScanConfirmed = false;\n\n async function scan(dir: string, depth: number): Promise<void> {\n // 深度超过限制时,提示用户确认\n if (depth > options.maxDepth && !deepScanConfirmed) {\n spinner.stop();\n const shouldContinue = await confirm({\n message: `扫描深度已超过 ${options.maxDepth} 层,是否继续?`,\n default: false,\n });\n\n if (!shouldContinue) {\n return;\n }\n deepScanConfirmed = true;\n spinner.start('继续扫描中...');\n }\n\n // 检查当前目录是否有 .git\n try {\n const gitDir = join(dir, '.git');\n await access(gitDir);\n\n // 找到 .git,获取仓库信息\n const repoInfo = getRepoInfo(dir);\n if (repoInfo) {\n repos.push(repoInfo);\n spinner.text = `扫描仓库中... 已找到 ${repos.length} 个`;\n }\n // 找到 .git 后不再深层继续(避免子模块重复)\n return;\n } catch {\n // 没有 .git,继续扫描子目录\n }\n\n // 递归扫描子目录\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (IGNORE_DIRS.has(entry.name)) continue;\n if (entry.name.startsWith('.') && entry.name !== '.git') continue;\n\n await scan(join(dir, entry.name), depth + 1);\n }\n } catch {\n // 权限不足时跳过\n }\n }\n\n await scan(options.targetDir, 0);\n\n if (repos.length > 0) {\n spinner.succeed(`找到 ${repos.length} 个 Git 仓库`);\n } else {\n spinner.fail('未找到 Git 仓库');\n }\n\n return repos;\n}\n\n/**\n * 获取单个仓库的基本信息\n */\nfunction getRepoInfo(repoPath: string): RepoInfo | null {\n try {\n const countStr = execSync('git rev-list --count HEAD', {\n cwd: repoPath,\n stdio: ['pipe', 'pipe', 'ignore'],\n encoding: 'utf-8',\n }).trim();\n\n const commitCount = parseInt(countStr, 10) || 0;\n const name = basename(repoPath);\n\n return {\n path: repoPath,\n name,\n commitCount,\n };\n } catch {\n // 仓库可能损坏或为空\n return null;\n }\n}\n","import ora from 'ora';\nimport chalk from 'chalk';\nimport { parseGitLog } from './git-log-parser.js';\nimport { calculateStats, mergeStats } from './stats-calculator.js';\nimport { calculateAdvancedStats } from './advanced/index.js';\nimport type { AnalyzeOptions, CommitStats } from '../types/index.js';\n\n/**\n * 分析所有选定仓库的提交记录\n */\nexport async function analyzeRepos(options: AnalyzeOptions): Promise<CommitStats> {\n const { repos, timeRange, author } = options;\n const spinner = ora('分析提交记录...').start();\n const allStats: CommitStats[] = [];\n\n for (let i = 0; i < repos.length; i++) {\n const repo = repos[i];\n spinner.text = `分析提交记录 (${i + 1}/${repos.length}) - ${repo.name}`;\n\n try {\n const commits = await parseGitLog(repo.path, timeRange, author);\n\n if (commits.length > 100000) {\n spinner.info(\n chalk.yellow(`${repo.name} 包含 ${commits.length.toLocaleString()} 条提交,处理可能需要一些时间...`)\n );\n spinner.start();\n }\n\n const stats = calculateStats(commits);\n\n // 新增:计算高级统计\n const advancedStats = calculateAdvancedStats(commits);\n\n // 合并核心统计和高级统计\n const fullStats: CommitStats = {\n ...stats,\n ...advancedStats,\n };\n\n allStats.push(fullStats);\n } catch (error) {\n spinner.warn(\n chalk.yellow(\n `跳过仓库 ${repo.name}: ${error instanceof Error ? error.message : '未知错误'}`\n )\n );\n spinner.start();\n }\n }\n\n const merged = mergeStats(allStats);\n\n spinner.succeed(\n `分析完成: ${merged.totalCommits.toLocaleString()} 条提交, ` +\n `${merged.authors.length} 位作者, ` +\n `+${merged.linesAdded.toLocaleString()} / -${merged.linesDeleted.toLocaleString()} 行`\n );\n\n return merged;\n}\n","import { execSync } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport ig from 'ignore';\nimport type { CommitRecord, FileChange, TimeRange } from '../types/index.js';\n\n/** git log 的格式化分隔符 */\nconst COMMIT_SEPARATOR = '---COMMITX_SEP---';\nconst FIELD_SEPARATOR = '|';\nconst FORMAT = `${COMMIT_SEPARATOR}%H${FIELD_SEPARATOR}%an${FIELD_SEPARATOR}%ae${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s`;\n\n/**\n * 从指定仓库解析 git log,返回提交记录数组\n * timeRange 为 null 时表示获取所有提交\n */\nexport async function parseGitLog(\n repoPath: string,\n timeRange: TimeRange | null,\n author?: string\n): Promise<CommitRecord[]> {\n const ignoreFilter = await loadGitignore(repoPath);\n\n const args = [\n 'git',\n 'log',\n `--format=\"${FORMAT}\"`,\n '--numstat',\n ];\n\n if (timeRange) {\n args.push(`--since=\"${timeRange.from.toISOString()}\"`);\n args.push(`--until=\"${timeRange.to.toISOString()}\"`);\n }\n\n if (author) {\n args.push(`--author=\"${author}\"`);\n }\n\n let output: string;\n try {\n output = execSync(args.join(' '), {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 100 * 1024 * 1024, // 100MB buffer for large repos\n stdio: ['pipe', 'pipe', 'ignore'],\n });\n } catch {\n return [];\n }\n\n if (!output.trim()) {\n return [];\n }\n\n return parseOutput(output, ignoreFilter);\n}\n\n/**\n * 解析 git log 输出文本为 CommitRecord 数组\n */\nfunction parseOutput(\n output: string,\n ignoreFilter: ReturnType<typeof ig>\n): CommitRecord[] {\n const commits: CommitRecord[] = [];\n const blocks = output.split(COMMIT_SEPARATOR).filter((b) => b.trim());\n\n for (const block of blocks) {\n const lines = block.trim().split('\\n');\n if (lines.length === 0) continue;\n\n // 第一行是提交元信息\n const headerLine = lines[0].replace(/^\"|\"$/g, '');\n const parts = headerLine.split(FIELD_SEPARATOR);\n if (parts.length < 5) continue;\n\n const [hash, authorName, email, dateStr, ...messageParts] = parts;\n const message = messageParts.join(FIELD_SEPARATOR); // message 可能包含 |\n\n // 后续行是 numstat(新增\\t删除\\t文件路径)\n const files: FileChange[] = [];\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) continue;\n\n const tabParts = line.split('\\t');\n if (tabParts.length !== 3) continue;\n\n const [addedStr, deletedStr, filePath] = tabParts;\n\n // 二进制文件显示为 -\n const added = addedStr === '-' ? 0 : parseInt(addedStr, 10) || 0;\n const deleted = deletedStr === '-' ? 0 : parseInt(deletedStr, 10) || 0;\n\n // 应用 gitignore 过滤\n if (ignoreFilter.ignores(filePath)) continue;\n\n files.push({ added, deleted, path: filePath });\n }\n\n commits.push({\n hash,\n author: authorName,\n email,\n date: new Date(dateStr),\n message,\n files,\n });\n }\n\n return commits;\n}\n\n/**\n * 读取仓库的 .gitignore 规则\n */\nasync function loadGitignore(repoPath: string): ReturnType<typeof ig> {\n const ignoreInstance = ig();\n\n try {\n const content = await readFile(join(repoPath, '.gitignore'), 'utf-8');\n ignoreInstance.add(content);\n } catch {\n // 没有 .gitignore 文件,跳过\n }\n\n // 默认忽略一些常见的生成文件\n ignoreInstance.add([\n 'package-lock.json',\n 'pnpm-lock.yaml',\n 'yarn.lock',\n ]);\n\n return ignoreInstance;\n}\n","import type {\n CommitRecord,\n CommitStats,\n AuthorStats,\n FileTypeStats,\n DirectoryStats,\n BusiestDay,\n QualityMetrics,\n TimePatterns,\n TrendData,\n CollaborationMetrics,\n CommitMessageStats,\n AuthorFileTypeContribution,\n HotFile,\n WeeklyPoint,\n CumulativePoint,\n SoloFile,\n CollabFile,\n} from '../types/index.js';\nimport { extname } from 'node:path';\n\n/**\n * 根据解析后的提交记录,计算所有统计维度\n */\nexport function calculateStats(commits: CommitRecord[]): CommitStats {\n if (commits.length === 0) {\n return emptyStats();\n }\n\n // 排序:按日期升序\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n\n // 基础统计\n let totalLinesAdded = 0;\n let totalLinesDeleted = 0;\n const allFilePaths = new Set<string>();\n\n // 作者维度\n const authorMap = new Map<string, AuthorStats>();\n\n // 文件类型维度\n const fileTypeMap = new Map<string, FileTypeStats>();\n\n // 目录维度\n const directoryMap = new Map<string, DirectoryStats>();\n const directoryCommitSet = new Map<string, Set<string>>(); // dir -> Set<commitHash>\n\n // 时间分布\n const hourlyDistribution = new Array<number>(24).fill(0);\n const dailyHeatmap: Record<string, number> = {};\n const hourlyByAuthor: Map<number, Map<string, number>> = new Map(); // hour -> (author -> count)\n\n // 每日统计(用于计算最繁忙的一天)\n const dailyCounts = new Map<string, number>();\n\n for (const commit of sorted) {\n // 时间分布\n const hour = commit.date.getHours();\n hourlyDistribution[hour]++;\n\n // 按小时统计作者\n if (!hourlyByAuthor.has(hour)) {\n hourlyByAuthor.set(hour, new Map());\n }\n const hourAuthors = hourlyByAuthor.get(hour)!;\n hourAuthors.set(commit.author, (hourAuthors.get(commit.author) || 0) + 1);\n\n const dateKey = formatDateKey(commit.date);\n dailyHeatmap[dateKey] = (dailyHeatmap[dateKey] || 0) + 1;\n dailyCounts.set(dateKey, (dailyCounts.get(dateKey) || 0) + 1);\n\n // 作者统计\n const authorKey = commit.email.toLowerCase();\n let authorStat = authorMap.get(authorKey);\n if (!authorStat) {\n authorStat = {\n name: commit.author,\n email: commit.email,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n lastActiveDate: commit.date,\n };\n authorMap.set(authorKey, authorStat);\n }\n authorStat.commits++;\n authorStat.lastActiveDate = commit.date;\n\n // 文件级别统计\n for (const file of commit.files) {\n totalLinesAdded += file.added;\n totalLinesDeleted += file.deleted;\n allFilePaths.add(file.path);\n\n // 作者行数统计\n authorStat.linesAdded += file.added;\n authorStat.linesDeleted += file.deleted;\n\n // 文件类型统计\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n let ftStat = fileTypeMap.get(ext);\n if (!ftStat) {\n ftStat = { extension: ext, added: 0, deleted: 0, fileCount: 0 };\n fileTypeMap.set(ext, ftStat);\n }\n ftStat.added += file.added;\n ftStat.deleted += file.deleted;\n\n // 目录统计 —— 取第一层目录\n const topDir = getTopDirectory(file.path);\n let dirStat = directoryMap.get(topDir);\n if (!dirStat) {\n dirStat = { path: topDir, commits: 0, linesChanged: 0 };\n directoryMap.set(topDir, dirStat);\n directoryCommitSet.set(topDir, new Set());\n }\n dirStat.linesChanged += file.added + file.deleted;\n directoryCommitSet.get(topDir)!.add(commit.hash);\n }\n }\n\n // 计算目录的 commit 数量\n for (const [dir, commitSet] of directoryCommitSet) {\n const dirStat = directoryMap.get(dir);\n if (dirStat) {\n dirStat.commits = commitSet.size;\n }\n }\n\n // 计算文件类型的 fileCount\n const fileCountByExt = new Map<string, Set<string>>();\n for (const filePath of allFilePaths) {\n const ext = extname(filePath).toLowerCase() || '(无扩展名)';\n if (!fileCountByExt.has(ext)) {\n fileCountByExt.set(ext, new Set());\n }\n fileCountByExt.get(ext)!.add(filePath);\n }\n for (const [ext, files] of fileCountByExt) {\n const ftStat = fileTypeMap.get(ext);\n if (ftStat) {\n ftStat.fileCount = files.size;\n }\n }\n\n // 最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of dailyCounts) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n\n // 排序结果\n const authors = Array.from(authorMap.values()).sort(\n (a, b) => b.commits - a.commits\n );\n const fileTypes = Array.from(fileTypeMap.values()).sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n const directories = Array.from(directoryMap.values())\n .sort((a, b) => b.linesChanged - a.linesChanged)\n .slice(0, 10); // TOP 10\n\n // 转换 hourlyByAuthor 为数组格式\n const hourlyByAuthorArray = Array.from({ length: 24 }, (_, hour) => {\n const authorMap = hourlyByAuthor.get(hour);\n const authors: Record<string, number> = {};\n if (authorMap) {\n authorMap.forEach((count, author) => {\n authors[author] = count;\n });\n }\n return {\n count: hourlyDistribution[hour],\n authors,\n };\n });\n\n return {\n totalCommits: sorted.length,\n linesAdded: totalLinesAdded,\n linesDeleted: totalLinesDeleted,\n filesChanged: allFilePaths.size,\n firstCommitDate: sorted[0].date,\n lastCommitDate: sorted[sorted.length - 1].date,\n busiestDay,\n authors,\n fileTypes,\n directories,\n hourlyDistribution,\n dailyHeatmap,\n hourlyByAuthor: hourlyByAuthorArray,\n quality: calculateQualityMetrics(sorted),\n timePatterns: calculateTimePatterns(sorted),\n trends: calculateTrends(sorted),\n collaboration: calculateCollaboration(sorted),\n messageStats: calculateMessageStats(sorted),\n authorFileTypeContributions: calculateAuthorFileTypeContributions(sorted),\n };\n}\n\n/**\n * 合并多个仓库的统计结果\n */\nexport function mergeStats(statsList: CommitStats[]): CommitStats {\n if (statsList.length === 0) return emptyStats();\n if (statsList.length === 1) return statsList[0];\n\n const merged = emptyStats();\n\n for (const stats of statsList) {\n merged.totalCommits += stats.totalCommits;\n merged.linesAdded += stats.linesAdded;\n merged.linesDeleted += stats.linesDeleted;\n merged.filesChanged += stats.filesChanged;\n\n // 时间维度\n if (\n !merged.firstCommitDate ||\n stats.firstCommitDate < merged.firstCommitDate\n ) {\n merged.firstCommitDate = stats.firstCommitDate;\n }\n if (\n !merged.lastCommitDate ||\n stats.lastCommitDate > merged.lastCommitDate\n ) {\n merged.lastCommitDate = stats.lastCommitDate;\n }\n\n // 小时分布\n for (let i = 0; i < 24; i++) {\n merged.hourlyDistribution[i] += stats.hourlyDistribution[i];\n }\n\n // 每日热力图\n for (const [date, count] of Object.entries(stats.dailyHeatmap)) {\n merged.dailyHeatmap[date] = (merged.dailyHeatmap[date] || 0) + count;\n }\n\n // 作者合并\n for (const author of stats.authors) {\n const existing = merged.authors.find(\n (a) => a.email.toLowerCase() === author.email.toLowerCase()\n );\n if (existing) {\n existing.commits += author.commits;\n existing.linesAdded += author.linesAdded;\n existing.linesDeleted += author.linesDeleted;\n if (author.lastActiveDate > existing.lastActiveDate) {\n existing.lastActiveDate = author.lastActiveDate;\n }\n } else {\n merged.authors.push({ ...author });\n }\n }\n\n // 文件类型合并\n for (const ft of stats.fileTypes) {\n const existing = merged.fileTypes.find(\n (f) => f.extension === ft.extension\n );\n if (existing) {\n existing.added += ft.added;\n existing.deleted += ft.deleted;\n existing.fileCount += ft.fileCount;\n } else {\n merged.fileTypes.push({ ...ft });\n }\n }\n\n // 目录合并\n for (const dir of stats.directories) {\n const existing = merged.directories.find((d) => d.path === dir.path);\n if (existing) {\n existing.commits += dir.commits;\n existing.linesChanged += dir.linesChanged;\n } else {\n merged.directories.push({ ...dir });\n }\n }\n\n // 质量指标合并\n merged.quality.avgFilesPerCommit += stats.quality.avgFilesPerCommit;\n merged.quality.avgLinesPerCommit += stats.quality.avgLinesPerCommit;\n merged.quality.churnRate += stats.quality.churnRate;\n for (const hf of stats.quality.hotFiles) {\n const existing = merged.quality.hotFiles.find((h) => h.path === hf.path);\n if (existing) {\n existing.modifyCount += hf.modifyCount;\n for (const author of hf.authors) {\n if (!existing.authors.includes(author)) {\n existing.authors.push(author);\n }\n }\n } else {\n merged.quality.hotFiles.push({ ...hf, authors: [...hf.authors] });\n }\n }\n\n // 时间模式合并\n for (let i = 0; i < 7; i++) {\n merged.timePatterns.weekdayDistribution[i] +=\n stats.timePatterns.weekdayDistribution[i];\n }\n\n // 趋势数据合并\n for (const wp of stats.trends.weeklyTrend) {\n const existing = merged.trends.weeklyTrend.find((w) => w.week === wp.week);\n if (existing) {\n existing.commits += wp.commits;\n existing.linesAdded += wp.linesAdded;\n existing.linesDeleted += wp.linesDeleted;\n } else {\n merged.trends.weeklyTrend.push({ ...wp });\n }\n }\n for (const cp of stats.trends.cumulativeLines) {\n const existing = merged.trends.cumulativeLines.find(\n (c) => c.date === cp.date\n );\n if (existing) {\n existing.netLines += cp.netLines;\n } else {\n merged.trends.cumulativeLines.push({ ...cp });\n }\n }\n\n // 协作指标合并\n for (const sf of stats.collaboration.soloFiles) {\n const existing = merged.collaboration.soloFiles.find(\n (s) => s.path === sf.path\n );\n if (existing) {\n existing.commits += sf.commits;\n } else {\n merged.collaboration.soloFiles.push({ ...sf });\n }\n }\n for (const ch of stats.collaboration.collaborationHotspots) {\n const existing = merged.collaboration.collaborationHotspots.find(\n (c) => c.path === ch.path\n );\n if (existing) {\n existing.totalCommits += ch.totalCommits;\n existing.authorCount = Math.max(existing.authorCount, ch.authorCount);\n } else {\n merged.collaboration.collaborationHotspots.push({ ...ch });\n }\n }\n\n // Commit Message 统计合并\n for (const [type, count] of Object.entries(stats.messageStats.typeDistribution)) {\n merged.messageStats.typeDistribution[type] =\n (merged.messageStats.typeDistribution[type] || 0) + count;\n }\n merged.messageStats.avgMessageLength += stats.messageStats.avgMessageLength;\n }\n\n // 重新计算最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of Object.entries(merged.dailyHeatmap)) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n merged.busiestDay = busiestDay;\n\n // 排序\n merged.authors.sort((a, b) => b.commits - a.commits);\n merged.fileTypes.sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n merged.directories.sort((a, b) => b.linesChanged - a.linesChanged);\n merged.directories = merged.directories.slice(0, 10);\n\n // 重新计算扩展字段的平均值和排序\n const repoCount = statsList.length;\n merged.quality.avgFilesPerCommit /= repoCount;\n merged.quality.avgLinesPerCommit /= repoCount;\n merged.quality.churnRate /= repoCount;\n merged.quality.hotFiles.sort((a, b) => b.modifyCount - a.modifyCount);\n merged.quality.hotFiles = merged.quality.hotFiles.slice(0, 10);\n\n // 时间模式重新计算\n const totalWeekdayCommits = merged.timePatterns.weekdayDistribution.reduce(\n (a, b) => a + b,\n 0\n );\n if (totalWeekdayCommits > 0) {\n merged.timePatterns.weekendCommits =\n (merged.timePatterns.weekdayDistribution[5] +\n merged.timePatterns.weekdayDistribution[6]) /\n totalWeekdayCommits;\n }\n\n // 趋势数据排序\n merged.trends.weeklyTrend.sort((a, b) => a.week.localeCompare(b.week));\n merged.trends.cumulativeLines.sort((a, b) => a.date.localeCompare(b.date));\n\n // 重新计算累计代码量\n let cumulative = 0;\n for (const point of merged.trends.cumulativeLines) {\n cumulative += point.netLines;\n point.netLines = cumulative;\n }\n\n // 协作指标排序\n merged.collaboration.soloFiles.sort((a, b) => b.commits - a.commits);\n merged.collaboration.soloFiles = merged.collaboration.soloFiles.slice(0, 10);\n merged.collaboration.collaborationHotspots.sort(\n (a, b) => b.totalCommits - a.totalCommits\n );\n merged.collaboration.collaborationHotspots =\n merged.collaboration.collaborationHotspots.slice(0, 10);\n\n // Commit Message 平均长度\n merged.messageStats.avgMessageLength /= repoCount;\n\n // 作者文件类型贡献合并\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n for (const stats of statsList) {\n for (const contrib of stats.authorFileTypeContributions) {\n const key = `${contrib.email.toLowerCase()}|||${contrib.extension}`;\n const existing = contributionMap.get(key);\n if (existing) {\n existing.linesAdded += contrib.linesAdded;\n existing.linesDeleted += contrib.linesDeleted;\n existing.commits += contrib.commits;\n existing.fileCount += contrib.fileCount;\n } else {\n contributionMap.set(key, { ...contrib });\n }\n }\n }\n merged.authorFileTypeContributions = Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n\n // ============================================================\n // 高级统计字段不进行多仓库合并\n // ============================================================\n // 原因:高级统计(teamHealth, stability, workPressure, contributorChurn, advancedCollaboration)\n // 需要原始 CommitRecord[] 数据才能准确计算。在 mergeStats 中仅有聚合后的统计数据,\n // 强行合并会导致结果不准确(例如 busFactor、revertRate 等指标无法简单累加)。\n //\n // 当前行为:\n // - 单仓库场景:直接返回(第184行),保留所有高级统计\n // - 多仓库场景:merged 中高级字段保持 undefined\n //\n // 如需多仓库的高级统计分析,建议:\n // 1. 使用时间范围过滤单个仓库\n // 2. 或在 analyzer/index.ts 中基于合并后的原始 commits 重新计算\n\n return merged;\n}\n\n/** 创建空的统计对象 */\nfunction emptyStats(): CommitStats {\n return {\n totalCommits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n filesChanged: 0,\n firstCommitDate: new Date(),\n lastCommitDate: new Date(),\n busiestDay: { date: '', count: 0 },\n authors: [],\n fileTypes: [],\n directories: [],\n hourlyDistribution: new Array<number>(24).fill(0),\n dailyHeatmap: {},\n quality: emptyQualityMetrics(),\n timePatterns: emptyTimePatterns(),\n trends: emptyTrendData(),\n collaboration: emptyCollaborationMetrics(),\n messageStats: emptyMessageStats(),\n authorFileTypeContributions: [],\n };\n}\n\n/** 获取文件路径的第一层目录 */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\n/** 格式化日期为 YYYY-MM-DD */\nfunction formatDateKey(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n\n// ============================================================\n// 扩展统计计算函数\n// ============================================================\n\n/** 计算代码质量指标 */\nfunction calculateQualityMetrics(commits: CommitRecord[]): QualityMetrics {\n if (commits.length === 0) {\n return emptyQualityMetrics();\n }\n\n // 平均每次提交的文件数\n const totalFiles = commits.reduce((sum, c) => sum + c.files.length, 0);\n const avgFilesPerCommit = totalFiles / commits.length;\n\n // 平均每次提交的行数\n const totalLines = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.added + f.deleted, 0),\n 0\n );\n const avgLinesPerCommit = totalLines / commits.length;\n\n // 代码流失率\n const totalAdded = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.added, 0),\n 0\n );\n const totalDeleted = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.deleted, 0),\n 0\n );\n const churnRate = totalAdded > 0 ? totalDeleted / totalAdded : 0;\n\n // 热点文件\n const fileModifyMap = new Map<string, { count: number; authors: Set<string> }>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const entry = fileModifyMap.get(file.path) || { count: 0, authors: new Set() };\n entry.count++;\n entry.authors.add(commit.author);\n fileModifyMap.set(file.path, entry);\n }\n }\n\n const hotFiles: HotFile[] = Array.from(fileModifyMap.entries())\n .map(([path, data]) => ({\n path,\n modifyCount: data.count,\n authors: Array.from(data.authors),\n }))\n .sort((a, b) => b.modifyCount - a.modifyCount)\n .slice(0, 10);\n\n return { avgFilesPerCommit, avgLinesPerCommit, churnRate, hotFiles };\n}\n\n/** 计算时间模式指标 */\nfunction calculateTimePatterns(commits: CommitRecord[]): TimePatterns {\n if (commits.length === 0) {\n return emptyTimePatterns();\n }\n\n const weekdayDistribution = new Array<number>(7).fill(0);\n const weekdayByAuthor: Map<number, Map<string, number>> = new Map(); // weekday -> (author -> count)\n\n for (const commit of commits) {\n const day = commit.date.getDay(); // 0=周日\n const idx = day === 0 ? 6 : day - 1; // 转为周一=0\n weekdayDistribution[idx]++;\n\n // 按周几统计作者\n if (!weekdayByAuthor.has(idx)) {\n weekdayByAuthor.set(idx, new Map());\n }\n const dayAuthors = weekdayByAuthor.get(idx)!;\n dayAuthors.set(commit.author, (dayAuthors.get(commit.author) || 0) + 1);\n }\n\n // 周末提交占比\n const weekendCommits =\n (weekdayDistribution[5] + weekdayDistribution[6]) / commits.length;\n\n // 提交间隔\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n let totalInterval = 0;\n for (let i = 1; i < sorted.length; i++) {\n totalInterval += sorted[i].date.getTime() - sorted[i - 1].date.getTime();\n }\n const avgCommitInterval =\n sorted.length > 1 ? totalInterval / (sorted.length - 1) / 3600000 : 0;\n\n // 连续提交天数\n const { longestStreak, currentStreak } = calculateStreaks(sorted);\n\n // 转换 weekdayByAuthor 为数组格式\n const weekdayByAuthorArray = Array.from({ length: 7 }, (_, day) => {\n const authorMap = weekdayByAuthor.get(day);\n const authors: Record<string, number> = {};\n if (authorMap) {\n authorMap.forEach((count, author) => {\n authors[author] = count;\n });\n }\n return {\n count: weekdayDistribution[day],\n authors,\n };\n });\n\n return {\n weekdayDistribution,\n weekendCommits,\n avgCommitInterval,\n longestStreak,\n currentStreak,\n weekdayByAuthor: weekdayByAuthorArray,\n };\n}\n\n/** 计算连续提交天数 */\nfunction calculateStreaks(sortedCommits: CommitRecord[]): {\n longestStreak: number;\n currentStreak: number;\n} {\n if (sortedCommits.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n // 提取唯一日期\n const uniqueDates = new Set<string>();\n for (const commit of sortedCommits) {\n uniqueDates.add(formatDateKey(commit.date));\n }\n\n const sortedDates = Array.from(uniqueDates).sort();\n if (sortedDates.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n let longestStreak = 1;\n let currentStreakCount = 1;\n let tempStreak = 1;\n\n for (let i = 1; i < sortedDates.length; i++) {\n const prevDate = new Date(sortedDates[i - 1]);\n const currDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n tempStreak++;\n } else {\n tempStreak = 1;\n }\n\n longestStreak = Math.max(longestStreak, tempStreak);\n }\n\n // 计算当前连续天数(从最后一天往前数)\n const today = formatDateKey(new Date());\n const lastCommitDate = sortedDates[sortedDates.length - 1];\n\n // 如果最后提交日期是今天或昨天,计算当前连续\n const lastDate = new Date(lastCommitDate);\n const todayDate = new Date(today);\n const daysSinceLastCommit = Math.round(\n (todayDate.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (daysSinceLastCommit <= 1) {\n currentStreakCount = 1;\n for (let i = sortedDates.length - 2; i >= 0; i--) {\n const currDate = new Date(sortedDates[i + 1]);\n const prevDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n currentStreakCount++;\n } else {\n break;\n }\n }\n } else {\n currentStreakCount = 0;\n }\n\n return { longestStreak, currentStreak: currentStreakCount };\n}\n\n/** 获取 ISO 周标识 */\nfunction getWeekKey(date: Date): string {\n const d = new Date(date);\n d.setHours(0, 0, 0, 0);\n d.setDate(d.getDate() + 4 - (d.getDay() || 7));\n const yearStart = new Date(d.getFullYear(), 0, 1);\n const weekNo = Math.ceil(\n ((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7\n );\n return `${d.getFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\n/** 计算趋势数据 */\nfunction calculateTrends(commits: CommitRecord[]): TrendData {\n if (commits.length === 0) {\n return emptyTrendData();\n }\n\n // 周趋势\n const weekMap = new Map<string, WeeklyPoint>();\n for (const commit of commits) {\n const week = getWeekKey(commit.date);\n const entry = weekMap.get(week) || {\n week,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n };\n entry.commits++;\n for (const file of commit.files) {\n entry.linesAdded += file.added;\n entry.linesDeleted += file.deleted;\n }\n weekMap.set(week, entry);\n }\n const weeklyTrend = Array.from(weekMap.values()).sort((a, b) =>\n a.week.localeCompare(b.week)\n );\n\n // 累计代码量\n const dailyNet = new Map<string, number>();\n for (const commit of commits) {\n const dateKey = formatDateKey(commit.date);\n const net = commit.files.reduce((sum, f) => sum + f.added - f.deleted, 0);\n dailyNet.set(dateKey, (dailyNet.get(dateKey) || 0) + net);\n }\n\n let cumulative = 0;\n const cumulativeLines: CumulativePoint[] = Array.from(dailyNet.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, net]) => {\n cumulative += net;\n return { date, netLines: cumulative };\n });\n\n return { weeklyTrend, cumulativeLines };\n}\n\n/** 计算协作指标 */\nfunction calculateCollaboration(commits: CommitRecord[]): CollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaborationMetrics();\n }\n\n const fileAuthors = new Map<string, Set<string>>();\n const fileCommits = new Map<string, number>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const authors = fileAuthors.get(file.path) || new Set();\n authors.add(commit.email.toLowerCase());\n fileAuthors.set(file.path, authors);\n fileCommits.set(file.path, (fileCommits.get(file.path) || 0) + 1);\n }\n }\n\n const soloFiles: SoloFile[] = [];\n const collaborationHotspots: CollabFile[] = [];\n\n for (const [path, authors] of fileAuthors) {\n const commitCount = fileCommits.get(path) || 0;\n if (authors.size === 1 && commitCount >= 3) {\n soloFiles.push({\n path,\n author: Array.from(authors)[0],\n commits: commitCount,\n });\n } else if (authors.size >= 2 && commitCount >= 5) {\n collaborationHotspots.push({\n path,\n authorCount: authors.size,\n totalCommits: commitCount,\n });\n }\n }\n\n return {\n soloFiles: soloFiles.sort((a, b) => b.commits - a.commits).slice(0, 10),\n collaborationHotspots: collaborationHotspots\n .sort((a, b) => b.totalCommits - a.totalCommits)\n .slice(0, 10),\n };\n}\n\n/** 计算 Commit Message 统计 */\nfunction calculateMessageStats(commits: CommitRecord[]): CommitMessageStats {\n if (commits.length === 0) {\n return emptyMessageStats();\n }\n\n const typeDistribution: Record<string, number> = {};\n let totalLength = 0;\n\n const typeRegex =\n /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?:/i;\n\n for (const commit of commits) {\n totalLength += commit.message.length;\n const match = commit.message.match(typeRegex);\n if (match) {\n const type = match[1].toLowerCase();\n typeDistribution[type] = (typeDistribution[type] || 0) + 1;\n } else {\n typeDistribution['other'] = (typeDistribution['other'] || 0) + 1;\n }\n }\n\n return {\n typeDistribution,\n avgMessageLength: totalLength / commits.length,\n };\n}\n\n/** 计算作者文件类型贡献 */\nfunction calculateAuthorFileTypeContributions(\n commits: CommitRecord[]\n): AuthorFileTypeContribution[] {\n if (commits.length === 0) {\n return [];\n }\n\n // 数据结构: Map<author-email-extension, contribution>\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n\n // 跟踪每个作者-扩展名组合修改的唯一文件\n const uniqueFilesMap = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n let contribution = contributionMap.get(key);\n if (!contribution) {\n contribution = {\n author: commit.author,\n email: commit.email,\n extension: ext,\n linesAdded: 0,\n linesDeleted: 0,\n commits: 0,\n fileCount: 0,\n };\n contributionMap.set(key, contribution);\n uniqueFilesMap.set(key, new Set());\n }\n\n contribution.linesAdded += file.added;\n contribution.linesDeleted += file.deleted;\n uniqueFilesMap.get(key)!.add(file.path);\n }\n }\n\n // 统计每个组合的提交数(去重)\n const commitCountMap = new Map<string, Set<string>>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n if (!commitCountMap.has(key)) {\n commitCountMap.set(key, new Set());\n }\n commitCountMap.get(key)!.add(commit.hash);\n }\n }\n\n // 更新 commits 和 fileCount\n for (const [key, contribution] of contributionMap) {\n contribution.commits = commitCountMap.get(key)?.size || 0;\n contribution.fileCount = uniqueFilesMap.get(key)?.size || 0;\n }\n\n // 按总变更行数(增+删)降序排序,取 TOP 20\n return Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n}\n\n// ============================================================\n// 空值工厂函数\n// ============================================================\n\nfunction emptyQualityMetrics(): QualityMetrics {\n return {\n avgFilesPerCommit: 0,\n avgLinesPerCommit: 0,\n churnRate: 0,\n hotFiles: [],\n };\n}\n\nfunction emptyTimePatterns(): TimePatterns {\n return {\n weekdayDistribution: new Array<number>(7).fill(0),\n weekendCommits: 0,\n avgCommitInterval: 0,\n longestStreak: 0,\n currentStreak: 0,\n };\n}\n\nfunction emptyTrendData(): TrendData {\n return {\n weeklyTrend: [],\n cumulativeLines: [],\n };\n}\n\nfunction emptyCollaborationMetrics(): CollaborationMetrics {\n return {\n soloFiles: [],\n collaborationHotspots: [],\n };\n}\n\nfunction emptyMessageStats(): CommitMessageStats {\n return {\n typeDistribution: {},\n avgMessageLength: 0,\n };\n}\n","import type { CommitRecord, TeamHealthMetrics, CriticalAuthor } from '../../types/index.js';\n\n/**\n * 计算团队健康度指标\n */\nexport function calculateTeamHealth(commits: CommitRecord[]): TeamHealthMetrics {\n // 边界情况:空提交\n if (commits.length === 0) {\n return emptyTeamHealth();\n }\n\n // Step 1: 构建数据结构\n const authorFiles = buildAuthorFilesMap(commits);\n const fileAuthors = buildFileAuthorsMap(commits);\n\n // 边界情况:无文件变更\n if (fileAuthors.size === 0) {\n return emptyTeamHealth();\n }\n\n // Step 2: 计算每个作者的知识独占度\n const authorScores = calculateAuthorScores(authorFiles, fileAuthors);\n\n // Step 3: 筛选关键人员(评分 >10)\n const criticalAuthors = authorScores.filter((a) => a.knowledgeScore > 10);\n\n // Step 4: 计算知识分布均匀度\n const totalUniqueFiles = criticalAuthors.reduce(\n (sum, a) => sum + a.uniqueFiles.length,\n 0\n );\n const knowledgeDistribution = 1 - totalUniqueFiles / fileAuthors.size;\n\n // Step 5: 评估风险等级\n const busFactor = criticalAuthors.length;\n const riskLevel = busFactor === 1 ? 'high' : busFactor <= 3 ? 'medium' : 'low';\n\n return {\n busFactor,\n criticalAuthors,\n knowledgeDistribution,\n riskLevel,\n };\n}\n\n/**\n * 构建 作者 -> 文件集合 映射\n */\nfunction buildAuthorFilesMap(commits: CommitRecord[]): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n if (!map.has(authorKey)) {\n map.set(authorKey, new Set());\n }\n const files = map.get(authorKey)!;\n for (const file of commit.files) {\n files.add(file.path);\n }\n }\n\n return map;\n}\n\n/**\n * 构建 文件 -> (作者 -> 提交次数) 映射\n */\nfunction buildFileAuthorsMap(commits: CommitRecord[]): Map<string, Map<string, number>> {\n const map = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n for (const file of commit.files) {\n if (!map.has(file.path)) {\n map.set(file.path, new Map());\n }\n const authors = map.get(file.path)!;\n authors.set(authorKey, (authors.get(authorKey) || 0) + 1);\n }\n }\n\n return map;\n}\n\n/**\n * 计算每个作者的知识独占度评分\n */\nfunction calculateAuthorScores(\n authorFiles: Map<string, Set<string>>,\n fileAuthors: Map<string, Map<string, number>>\n): CriticalAuthor[] {\n const scores: CriticalAuthor[] = [];\n\n for (const [authorKey, files] of authorFiles) {\n const uniqueFiles: string[] = [];\n const dominantFiles: string[] = [];\n\n for (const filePath of files) {\n const authors = fileAuthors.get(filePath)!;\n const authorCount = authors.size;\n\n // 独有文件:只有该作者修改过\n if (authorCount === 1) {\n uniqueFiles.push(filePath);\n continue;\n }\n\n // 主导文件:该作者贡献 >50%\n const authorCommits = authors.get(authorKey) || 0;\n const totalCommits = Array.from(authors.values()).reduce((a, b) => a + b, 0);\n if (authorCommits / totalCommits > 0.5) {\n dominantFiles.push(filePath);\n }\n }\n\n // 知识独占度评分 = (独有文件*2 + 主导文件) / 总文件数 * 100\n const knowledgeScore =\n ((uniqueFiles.length * 2 + dominantFiles.length) / fileAuthors.size) * 100;\n\n const [name, email] = parseAuthorKey(authorKey);\n scores.push({\n name,\n email,\n uniqueFiles,\n dominantFiles,\n knowledgeScore,\n });\n }\n\n return scores;\n}\n\n/**\n * 解析作者键 \"name <email>\" -> [name, email]\n */\nfunction parseAuthorKey(authorKey: string): [string, string] {\n const match = authorKey.match(/^(.+) <(.+)>$/);\n if (match) {\n return [match[1], match[2]];\n }\n return [authorKey, ''];\n}\n\nfunction emptyTeamHealth(): TeamHealthMetrics {\n return {\n busFactor: 0,\n criticalAuthors: [],\n knowledgeDistribution: 1,\n riskLevel: 'low',\n };\n}\n","import type { CommitRecord, StabilityMetrics, FileChurn, DirectoryChurn } from '../../types/index.js';\n\n/**\n * 计算代码稳定性指标\n */\nexport function calculateStability(commits: CommitRecord[]): StabilityMetrics {\n if (commits.length === 0) {\n return emptyStability();\n }\n\n // Step 1: 统计文件级流失率\n const fileStats = new Map<string, { added: number; deleted: number; modifyCount: number }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const stat = fileStats.get(file.path) || { added: 0, deleted: 0, modifyCount: 0 };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.modifyCount++;\n fileStats.set(file.path, stat);\n }\n }\n\n // 转换为 FileChurn 数组,按流失率排序,取 TOP 20\n const fileChurnRate: FileChurn[] = Array.from(fileStats.entries())\n .map(([path, { added, deleted, modifyCount }]) => ({\n path,\n added,\n deleted,\n churnRate: added > 0 ? deleted / added : 0,\n modifyCount,\n isUnstable: deleted / Math.max(added, 1) > 0.5,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 20);\n\n // Step 2: 统计目录级流失率\n const dirStats = new Map<string, { added: number; deleted: number; files: Set<string> }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const dir = getTopDirectory(file.path);\n const stat = dirStats.get(dir) || { added: 0, deleted: 0, files: new Set() };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.files.add(file.path);\n dirStats.set(dir, stat);\n }\n }\n\n const directoryChurnRate: DirectoryChurn[] = Array.from(dirStats.entries())\n .map(([path, { added, deleted, files }]) => ({\n path,\n churnRate: added > 0 ? deleted / added : 0,\n totalChanges: added + deleted,\n fileCount: files.size,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 10);\n\n // Step 3: 计算 Revert 率\n const revertCommits = commits.filter((c) => /revert|rollback/i.test(c.message)).length;\n const revertRate = revertCommits / commits.length;\n\n // Step 4: 计算 Fix 提交率\n const fixCommits = commits.filter((c) => /^fix(\\(.+\\))?:/i.test(c.message)).length;\n const fixCommitRate = fixCommits / commits.length;\n\n // Step 5: 计算稳定性评分\n const avgChurnRate =\n fileChurnRate.length > 0\n ? fileChurnRate.reduce((sum, f) => sum + f.churnRate, 0) / fileChurnRate.length\n : 0;\n\n const stabilityScore = Math.max(\n 0,\n Math.round(100 - (avgChurnRate * 50 + revertRate * 100 + fixCommitRate * 50))\n );\n\n return {\n fileChurnRate,\n directoryChurnRate,\n revertRate: Math.round(revertRate * 100) / 100,\n fixCommitRate: Math.round(fixCommitRate * 100) / 100,\n stabilityScore,\n };\n}\n\n/**\n * 获取文件路径的第一层目录\n */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\nfunction emptyStability(): StabilityMetrics {\n return {\n fileChurnRate: [],\n directoryChurnRate: [],\n revertRate: 0,\n fixCommitRate: 0,\n stabilityScore: 100,\n };\n}\n","import type { CommitRecord, WorkPressureMetrics, HolidayCommit } from '../../types/index.js';\n\n/**\n * 计算工作压力指标\n */\nexport function calculateWorkPressure(commits: CommitRecord[]): WorkPressureMetrics {\n if (commits.length === 0) {\n return emptyWorkPressure();\n }\n\n let lateNightCommits = 0;\n let earlyMorningCommits = 0;\n let weekendCommits = 0;\n\n const holidayMap = new Map<string, { name: string; commits: number }>();\n const holidays = getHolidays();\n\n for (const commit of commits) {\n const hour = commit.date.getHours();\n const day = commit.date.getDay();\n const dateKey = formatDate(commit.date);\n\n // 深夜 (23:00-02:00)\n if (hour >= 23 || hour < 2) {\n lateNightCommits++;\n }\n\n // 凌晨 (02:00-06:00)\n if (hour >= 2 && hour < 6) {\n earlyMorningCommits++;\n }\n\n // 周末\n if (day === 0 || day === 6) {\n weekendCommits++;\n }\n\n // 假期\n const holiday = holidays.get(dateKey);\n if (holiday) {\n const entry = holidayMap.get(dateKey) || { name: holiday, commits: 0 };\n entry.commits++;\n holidayMap.set(dateKey, entry);\n }\n }\n\n const holidayCommits: HolidayCommit[] = Array.from(holidayMap.entries())\n .map(([date, { name, commits }]) => ({ date, holidayName: name, commits }))\n .sort((a, b) => b.commits - a.commits);\n\n // 非工作时间占比\n const holidayTotal = holidayCommits.reduce((sum, h) => sum + h.commits, 0);\n const offHoursCount = lateNightCommits + earlyMorningCommits + weekendCommits + holidayTotal;\n const offHoursRate = offHoursCount / commits.length;\n\n // 压力评分 (0-100)\n const lateNightWeight = (lateNightCommits / commits.length) * 40;\n const earlyMorningWeight = (earlyMorningCommits / commits.length) * 30;\n const weekendWeight = (weekendCommits / commits.length) * 20;\n const holidayWeight = (holidayCommits.length > 0 ? 1 : 0) * 10;\n\n const pressureScore = Math.round(lateNightWeight + earlyMorningWeight + weekendWeight + holidayWeight);\n\n return {\n lateNightCommits,\n earlyMorningCommits,\n weekendCommits,\n holidayCommits,\n pressureScore,\n offHoursRate: Math.round(offHoursRate * 1000) / 1000\n };\n}\n\nfunction emptyWorkPressure(): WorkPressureMetrics {\n return {\n lateNightCommits: 0,\n earlyMorningCommits: 0,\n weekendCommits: 0,\n holidayCommits: [],\n pressureScore: 0,\n offHoursRate: 0,\n };\n}\n\n/**\n * 获取中国 2024-2026 主要节假日\n */\nfunction getHolidays(): Map<string, string> {\n const holidays = new Map<string, string>();\n\n // 2024\n const dates2024: Array<[string, string]> = [\n ['2024-01-01', '元旦'],\n ['2024-02-10', '春节'], ['2024-02-11', '春节'], ['2024-02-12', '春节'],\n ['2024-04-04', '清明节'], ['2024-04-05', '清明节'], ['2024-04-06', '清明节'],\n ['2024-05-01', '劳动节'], ['2024-05-02', '劳动节'], ['2024-05-03', '劳动节'],\n ['2024-06-10', '端午节'],\n ['2024-09-15', '中秋节'], ['2024-09-16', '中秋节'], ['2024-09-17', '中秋节'],\n ['2024-10-01', '国庆节'], ['2024-10-02', '国庆节'], ['2024-10-03', '国庆节']\n ];\n\n // 2025\n const dates2025: Array<[string, string]> = [\n ['2025-01-01', '元旦'],\n ['2025-01-29', '春节'], ['2025-01-30', '春节'], ['2025-01-31', '春节'],\n ['2025-04-04', '清明节'], ['2025-04-05', '清明节'], ['2025-04-06', '清明节'],\n ['2025-05-01', '劳动节'], ['2025-05-02', '劳动节'], ['2025-05-03', '劳动节'],\n ['2025-05-31', '端午节'],\n ['2025-10-01', '国庆节'], ['2025-10-02', '国庆节'], ['2025-10-06', '中秋节']\n ];\n\n // 2026\n const dates2026: Array<[string, string]> = [\n ['2026-01-01', '元旦'],\n ['2026-02-17', '春节'], ['2026-02-18', '春节'], ['2026-02-19', '春节'],\n ['2026-04-05', '清明节'],\n ['2026-05-01', '劳动节'],\n ['2026-06-19', '端午节'],\n ['2026-09-25', '中秋节'],\n ['2026-10-01', '国庆节'], ['2026-10-02', '国庆节']\n ];\n\n [...dates2024, ...dates2025, ...dates2026].forEach(([date, name]) => {\n holidays.set(date, name);\n });\n\n return holidays;\n}\n\n/**\n * 格式化日期为 YYYY-MM-DD\n */\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n","import type { CommitRecord, ContributorChurnMetrics } from '../../types/index.js';\n\n/**\n * 计算贡献者流失率指标\n */\nexport function calculateContributorChurn(commits: CommitRecord[]): ContributorChurnMetrics {\n if (commits.length === 0) {\n return emptyContributorChurn();\n }\n\n // 统计每个作者的最后提交时间\n const authorLastCommit = new Map<string, {\n name: string;\n email: string;\n lastDate: Date;\n firstDate: Date;\n totalCommits: number;\n }>();\n\n for (const commit of commits) {\n const key = commit.email.toLowerCase();\n const existing = authorLastCommit.get(key);\n\n if (!existing) {\n authorLastCommit.set(key, {\n name: commit.author,\n email: commit.email,\n lastDate: commit.date,\n firstDate: commit.date,\n totalCommits: 1\n });\n } else {\n if (commit.date > existing.lastDate) {\n existing.lastDate = commit.date;\n }\n if (commit.date < existing.firstDate) {\n existing.firstDate = commit.date;\n }\n existing.totalCommits++;\n }\n }\n\n // 四级分类\n const now = new Date();\n const active: AuthorDetail[] = [];\n const occasional: AuthorDetail[] = [];\n const dormant: AuthorDetail[] = [];\n const lost: AuthorDetail[] = [];\n const newJoiners: AuthorDetail[] = [];\n\n for (const [, author] of authorLastCommit) {\n const daysSinceLast = Math.floor((now.getTime() - author.lastDate.getTime()) / (1000 * 60 * 60 * 24));\n const daysSinceFirst = Math.floor((now.getTime() - author.firstDate.getTime()) / (1000 * 60 * 60 * 24));\n\n const detail: AuthorDetail = {\n name: author.name,\n email: author.email,\n lastCommitDate: author.lastDate,\n daysSinceLastCommit: daysSinceLast,\n totalCommits: author.totalCommits\n };\n\n // 新加入(首次提交 <30天)\n if (daysSinceFirst < 30) {\n newJoiners.push(detail);\n }\n\n // 按最后活跃时间分类\n if (daysSinceLast < 30) {\n active.push(detail);\n } else if (daysSinceLast < 90) {\n occasional.push(detail);\n } else if (daysSinceLast < 180) {\n dormant.push(detail);\n } else {\n lost.push(detail);\n }\n }\n\n // 计算比率并排序\n const totalAuthors = authorLastCommit.size;\n const churnRate = totalAuthors > 0 ? lost.length / totalAuthors : 0;\n const retentionRate = totalAuthors > 0 ? active.length / totalAuthors : 0;\n const growthRate = totalAuthors > 0 ? newJoiners.length / totalAuthors : 0;\n\n return {\n active: active.sort((a, b) => b.totalCommits - a.totalCommits),\n occasional: occasional.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n dormant: dormant.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n lost: lost.sort((a, b) => b.totalCommits - a.totalCommits),\n newJoiners: newJoiners.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n churnRate: Math.round(churnRate * 1000) / 1000,\n retentionRate: Math.round(retentionRate * 1000) / 1000,\n growthRate: Math.round(growthRate * 1000) / 1000\n };\n}\n\nfunction emptyContributorChurn(): ContributorChurnMetrics {\n return {\n active: [],\n occasional: [],\n dormant: [],\n lost: [],\n newJoiners: [],\n churnRate: 0,\n retentionRate: 0,\n growthRate: 0,\n };\n}\n","import type { CommitRecord, AdvancedCollaborationMetrics, FilePair, AuthorPair } from '../../types/index.js';\n\nconst MAX_FILES_PER_COMMIT = 50;\n\n/**\n * 计算高级协作指标\n */\nexport function calculateAdvancedCollaboration(commits: CommitRecord[]): AdvancedCollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaboration();\n }\n\n // 统计文件修改次数\n const fileModifyCount = new Map<string, number>();\n for (const commit of commits) {\n commit.files.forEach(f => {\n fileModifyCount.set(f.path, (fileModifyCount.get(f.path) || 0) + 1);\n });\n }\n\n // Step 1: 检测紧密耦合 - 统计同提交文件对\n const tightCoupling = detectTightCoupling(commits, fileModifyCount);\n\n // Step 2: 检测结对编程\n const pairProgramming = detectPairProgramming(commits);\n\n // Step 3: 计算耦合评分\n const avgCoupling = tightCoupling.length > 0\n ? tightCoupling.reduce((sum, p) => sum + p.coupling, 0) / tightCoupling.length\n : 0;\n const couplingScore = Math.min(100, Math.round(avgCoupling * 100));\n\n return {\n tightCoupling,\n frequentPairs: [], // 简化版:跳过 24h 窗口分析\n pairProgramming,\n couplingScore,\n };\n}\n\n/**\n * 检测紧密耦合:统计同一提交中的文件对\n */\nfunction detectTightCoupling(\n commits: CommitRecord[],\n fileModifyCount: Map<string, number>\n): FilePair[] {\n const coCommitPairs = new Map<string, { count: number; file1: string; file2: string }>();\n\n for (const commit of commits) {\n // 性能优化:限制文件数,避免大型提交导致组合爆炸\n const files = commit.files\n .map(f => f.path)\n .slice(0, MAX_FILES_PER_COMMIT)\n .sort();\n\n // 组合文件对\n for (let i = 0; i < files.length; i++) {\n for (let j = i + 1; j < files.length; j++) {\n const pairKey = `${files[i]}|||${files[j]}`;\n const existing = coCommitPairs.get(pairKey) || {\n count: 0,\n file1: files[i],\n file2: files[j],\n };\n existing.count++;\n coCommitPairs.set(pairKey, existing);\n }\n }\n }\n\n // 计算耦合度并筛选\n const tightCoupling: FilePair[] = Array.from(coCommitPairs.values())\n .map(({ file1, file2, count }) => {\n const count1 = fileModifyCount.get(file1) || 1;\n const count2 = fileModifyCount.get(file2) || 1;\n const coupling = count / Math.min(count1, count2);\n\n return {\n file1,\n file2,\n coOccurrence: count,\n coupling,\n };\n })\n .filter(p => p.coOccurrence >= 3) // 至少共同出现 3 次\n .sort((a, b) => b.coupling - a.coupling)\n .slice(0, 20); // TOP 20\n\n return tightCoupling;\n}\n\n/**\n * 检测结对编程:找出在多个文件上协作的作者对\n */\nfunction detectPairProgramming(commits: CommitRecord[]): AuthorPair[] {\n // 统计每个文件的作者贡献\n const fileAuthors = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n if (!fileAuthors.has(file.path)) {\n fileAuthors.set(file.path, new Map());\n }\n const authorMap = fileAuthors.get(file.path)!;\n const email = commit.email.toLowerCase();\n authorMap.set(email, (authorMap.get(email) || 0) + 1);\n }\n }\n\n // 识别结对编程:两个作者都是文件的主要贡献者\n const authorPairMap = new Map<string, {\n author1: string;\n author2: string;\n files: Set<string>;\n count: number;\n }>();\n\n for (const [filePath, authorMap] of fileAuthors) {\n if (authorMap.size >= 2) {\n // 取贡献最多的两个作者\n const sortedAuthors = Array.from(authorMap.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 2);\n\n if (sortedAuthors.length === 2) {\n // 规范化作者对的键(按字母序)\n const [a1, a2] = [sortedAuthors[0][0], sortedAuthors[1][0]].sort();\n const pairKey = `${a1}|||${a2}`;\n\n const existing = authorPairMap.get(pairKey) || {\n author1: a1,\n author2: a2,\n files: new Set<string>(),\n count: 0,\n };\n\n existing.files.add(filePath);\n existing.count++;\n authorPairMap.set(pairKey, existing);\n }\n }\n }\n\n // 筛选并排序\n const pairProgramming: AuthorPair[] = Array.from(authorPairMap.values())\n .filter(p => p.files.size >= 3) // 至少 3 个共享文件\n .map(p => ({\n author1: p.author1,\n author2: p.author2,\n sharedFiles: Array.from(p.files),\n collaborationCount: p.count,\n }))\n .sort((a, b) => b.collaborationCount - a.collaborationCount)\n .slice(0, 10); // TOP 10\n\n return pairProgramming;\n}\n\nfunction emptyCollaboration(): AdvancedCollaborationMetrics {\n return {\n tightCoupling: [],\n frequentPairs: [],\n pairProgramming: [],\n couplingScore: 0,\n };\n}\n","import type { CommitRecord } from '../../types/index.js';\nimport type {\n TeamHealthMetrics,\n StabilityMetrics,\n WorkPressureMetrics,\n ContributorChurnMetrics,\n AdvancedCollaborationMetrics,\n} from '../../types/index.js';\n\nimport { calculateTeamHealth } from './team-health.js';\nimport { calculateStability } from './code-stability.js';\nimport { calculateWorkPressure } from './work-pressure.js';\nimport { calculateContributorChurn } from './contributor-churn.js';\nimport { calculateAdvancedCollaboration } from './collaboration.js';\n\n/**\n * 高级统计结果集合\n */\nexport interface AdvancedStats {\n teamHealth: TeamHealthMetrics;\n stability: StabilityMetrics;\n workPressure: WorkPressureMetrics;\n contributorChurn: ContributorChurnMetrics;\n advancedCollaboration: AdvancedCollaborationMetrics;\n}\n\n/**\n * 一次性计算所有高级统计\n */\nexport function calculateAdvancedStats(commits: CommitRecord[]): AdvancedStats {\n return {\n teamHealth: calculateTeamHealth(commits),\n stability: calculateStability(commits),\n workPressure: calculateWorkPressure(commits),\n contributorChurn: calculateContributorChurn(commits),\n advancedCollaboration: calculateAdvancedCollaboration(commits),\n };\n}\n\n// 导出单独的计算函数\nexport {\n calculateTeamHealth,\n calculateStability,\n calculateWorkPressure,\n calculateContributorChurn,\n calculateAdvancedCollaboration,\n};\n","import { writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport open from 'open';\nimport { buildHtml } from './html-builder.js';\nimport type { CommitStats, ReportOptions } from '../types/index.js';\n\n/**\n * 生成 HTML 报告并可选地打开浏览器\n */\nexport async function generateReport(\n stats: CommitStats,\n options: ReportOptions\n): Promise<void> {\n const spinner = ora('生成报告...').start();\n\n try {\n const html = await buildHtml(stats, options);\n const outputPath = resolve(process.cwd(), options.outputPath);\n\n await writeFile(outputPath, html, 'utf-8');\n spinner.succeed(`报告已生成: ${chalk.cyan(outputPath)}`);\n\n if (options.autoOpen) {\n await open(outputPath);\n console.log(chalk.green('✓ 已在浏览器中打开'));\n }\n } catch (error) {\n spinner.fail('生成报告失败');\n throw error;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { CommitStats, ReportOptions, ReportData } from '../types/index.js';\n\n/**\n * 组装完整的 HTML 报告\n */\nexport async function buildHtml(\n stats: CommitStats,\n options: ReportOptions\n): Promise<string> {\n const template = await loadTemplate();\n\n const reportData: ReportData = {\n stats: serializeStats(stats),\n generatedAt: new Date().toLocaleString('zh-CN'),\n timeRange: options.timeRange\n ? {\n from: options.timeRange.from.toISOString().split('T')[0],\n to: options.timeRange.to.toISOString().split('T')[0],\n }\n : null,\n repos: options.repoNames,\n };\n\n // 安全地序列化数据(防止 XSS)\n const jsonData = JSON.stringify(reportData)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026');\n\n return template.replace('__REPORT_DATA__', jsonData);\n}\n\n/**\n * 将 CommitStats 转为可 JSON 序列化的格式\n * Date 对象转为 ISO 字符串\n */\nfunction serializeStats(stats: CommitStats): Record<string, unknown> {\n const serializeAuthorDetail = (author: { lastCommitDate: Date; [key: string]: unknown }) => ({\n ...author,\n lastCommitDate: author.lastCommitDate.toISOString(),\n });\n\n return {\n ...stats,\n firstCommitDate: stats.firstCommitDate.toISOString(),\n lastCommitDate: stats.lastCommitDate.toISOString(),\n authors: stats.authors.map((a) => ({\n ...a,\n lastActiveDate: a.lastActiveDate.toISOString(),\n })),\n contributorChurn: stats.contributorChurn\n ? {\n ...stats.contributorChurn,\n active: stats.contributorChurn.active.map(serializeAuthorDetail),\n occasional: stats.contributorChurn.occasional.map(serializeAuthorDetail),\n dormant: stats.contributorChurn.dormant.map(serializeAuthorDetail),\n lost: stats.contributorChurn.lost.map(serializeAuthorDetail),\n newJoiners: stats.contributorChurn.newJoiners.map(serializeAuthorDetail),\n }\n : undefined,\n };\n}\n\n/**\n * 加载 HTML 模板\n */\nasync function loadTemplate(): Promise<string> {\n // 支持两种路径:开发模式和打包后模式\n const currentDir = dirname(fileURLToPath(import.meta.url));\n\n // 打包后: dist/index.js -> ../templates/report.html\n // 开发模式: src/reporter/html-builder.ts -> ../../templates/report.html\n const possiblePaths = [\n resolve(currentDir, '../templates/report.html'),\n resolve(currentDir, '../../templates/report.html'),\n resolve(currentDir, '../../../templates/report.html'),\n ];\n\n for (const templatePath of possiblePaths) {\n try {\n return await readFile(templatePath, 'utf-8');\n } catch {\n // 继续尝试下一个路径\n }\n }\n\n throw new Error('无法找到 HTML 模板文件');\n}\n","import type { CliOptions, TimeRange } from '../types/index.js';\n\n/** 时间预设格式: 7d / 1m / 3m / 6m / 1y / all */\nconst PERIOD_REGEX = /^(\\d+)(d|m|y)$/;\n\n/**\n * 解析时间预设字符串,返回起止时间范围\n * 'all' 表示不限时间(从 1970 年至今)\n */\nexport function parsePeriod(period: string): TimeRange | null {\n if (period === 'all') {\n return null;\n }\n\n const match = PERIOD_REGEX.exec(period);\n if (!match) {\n throw new Error(`无效的时间预设: \"${period}\",支持格式: 7d, 1m, 3m, 6m, 1y, all`);\n }\n\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const to = new Date();\n const from = new Date();\n\n switch (unit) {\n case 'd':\n from.setDate(from.getDate() - amount);\n break;\n case 'm':\n from.setMonth(from.getMonth() - amount);\n break;\n case 'y':\n from.setFullYear(from.getFullYear() - amount);\n break;\n }\n\n return { from, to };\n}\n\n/**\n * 解析日期字符串 YYYY-MM-DD\n */\nfunction parseDate(dateStr: string): Date {\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) {\n throw new Error(`无效的日期格式: \"${dateStr}\",请使用 YYYY-MM-DD 格式`);\n }\n return date;\n}\n\n/**\n * 根据 CLI 参数解析最终的时间范围\n * --from / --to 优先于 --period\n * 返回 null 表示不限时间\n */\nexport function resolveTimeRange(opts: CliOptions): TimeRange | null {\n if (opts.from || opts.to) {\n const to = opts.to ? parseDate(opts.to) : new Date();\n const from = opts.from ? parseDate(opts.from) : (() => {\n const d = new Date(to);\n d.setMonth(d.getMonth() - 3);\n return d;\n })();\n\n if (from > to) {\n throw new Error('起始日期不能晚于结束日期');\n }\n\n return { from, to };\n }\n\n return parsePeriod(opts.period);\n}\n"],"mappings":"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","names":["chalk","ora","execSync","join","authorMap","authors","stat","getTopDirectory","commits","ora","resolve","chalk","ora","readFile","ora","resolve","chalk","chalk","formatDate","execSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commit-report",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Git 提交统计工具,生成可视化 HTML 报告",
5
5
  "type": "module",
6
6
  "bin": {