()
| 638 | * Uses a disk cache to avoid reprocessing historical data. |
| 639 | */ |
| 640 | export async function aggregateClaudeCodeStats(): Promise<ClaudeCodeStats> { |
| 641 | const allSessionFiles = await getAllSessionFiles() |
| 642 | |
| 643 | if (allSessionFiles.length === 0) { |
| 644 | return getEmptyStats() |
| 645 | } |
| 646 | |
| 647 | // Use lock to prevent race conditions with background cache updates |
| 648 | const updatedCache = await withStatsCacheLock(async () => { |
| 649 | // Load the cache |
| 650 | const cache = await loadStatsCache() |
| 651 | const yesterday = getYesterdayDateString() |
| 652 | |
| 653 | // Determine what needs to be processed |
| 654 | // - If no cache: process everything up to yesterday, then today separately |
| 655 | // - If cache exists: process from day after lastComputedDate to yesterday, then today |
| 656 | let result = cache |
| 657 | |
| 658 | if (!cache.lastComputedDate) { |
| 659 | // No cache - process all historical data (everything before today) |
| 660 | logForDebugging('Stats cache empty, processing all historical data') |
| 661 | const historicalStats = await processSessionFiles(allSessionFiles, { |
| 662 | toDate: yesterday, |
| 663 | }) |
| 664 | |
| 665 | if ( |
| 666 | historicalStats.sessionStats.length > 0 || |
| 667 | historicalStats.dailyActivity.length > 0 |
| 668 | ) { |
| 669 | result = mergeCacheWithNewStats(cache, historicalStats, yesterday) |
| 670 | await saveStatsCache(result) |
| 671 | } |
| 672 | } else if (isDateBefore(cache.lastComputedDate, yesterday)) { |
| 673 | // Cache is stale - process new days |
| 674 | // Process from day after lastComputedDate to yesterday |
| 675 | const nextDay = getNextDay(cache.lastComputedDate) |
| 676 | logForDebugging( |
| 677 | `Stats cache stale (${cache.lastComputedDate}), processing ${nextDay} to ${yesterday}`, |
| 678 | ) |
| 679 | const newStats = await processSessionFiles(allSessionFiles, { |
| 680 | fromDate: nextDay, |
| 681 | toDate: yesterday, |
| 682 | }) |
| 683 | |
| 684 | if ( |
| 685 | newStats.sessionStats.length > 0 || |
| 686 | newStats.dailyActivity.length > 0 |
| 687 | ) { |
| 688 | result = mergeCacheWithNewStats(cache, newStats, yesterday) |
| 689 | await saveStatsCache(result) |
| 690 | } else { |
| 691 | // No new data, but update lastComputedDate |
| 692 | result = { ...cache, lastComputedDate: yesterday } |
| 693 | await saveStatsCache(result) |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | return result |
no test coverage detected