repo-wrapped 0.0.3 → 0.0.5

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.
Files changed (38) hide show
  1. package/dist/commands/generate.js +104 -95
  2. package/dist/constants/chronotypes.js +23 -23
  3. package/dist/constants/colors.js +18 -18
  4. package/dist/constants/index.js +18 -18
  5. package/dist/formatters/index.js +17 -17
  6. package/dist/formatters/timeFormatter.js +28 -29
  7. package/dist/generators/html/templates/achievementsSection.js +42 -43
  8. package/dist/generators/html/templates/commitQualitySection.js +25 -26
  9. package/dist/generators/html/templates/contributionGraph.js +64 -48
  10. package/dist/generators/html/templates/impactSection.js +19 -20
  11. package/dist/generators/html/templates/knowledgeSection.js +86 -87
  12. package/dist/generators/html/templates/streakSection.js +8 -9
  13. package/dist/generators/html/templates/timePatternsSection.js +45 -46
  14. package/dist/generators/html/utils/colorUtils.js +61 -21
  15. package/dist/generators/html/utils/commitMapBuilder.js +23 -24
  16. package/dist/generators/html/utils/dateRangeCalculator.js +56 -57
  17. package/dist/generators/html/utils/developerStatsCalculator.js +28 -29
  18. package/dist/generators/html/utils/scriptLoader.js +15 -16
  19. package/dist/generators/html/utils/styleLoader.js +17 -18
  20. package/dist/generators/html/utils/weekGrouper.js +27 -28
  21. package/dist/index.js +78 -78
  22. package/dist/types/index.js +2 -2
  23. package/dist/utils/achievementDefinitions.js +433 -433
  24. package/dist/utils/achievementEngine.js +169 -170
  25. package/dist/utils/commitQualityAnalyzer.js +367 -368
  26. package/dist/utils/fileHotspotAnalyzer.js +269 -270
  27. package/dist/utils/gitParser.js +136 -126
  28. package/dist/utils/htmlGenerator.js +245 -233
  29. package/dist/utils/impactAnalyzer.js +247 -248
  30. package/dist/utils/knowledgeDistributionAnalyzer.js +380 -374
  31. package/dist/utils/matrixGenerator.js +369 -350
  32. package/dist/utils/slideGenerator.js +170 -171
  33. package/dist/utils/streakCalculator.js +134 -135
  34. package/dist/utils/timePatternAnalyzer.js +304 -305
  35. package/dist/utils/wrappedDisplay.js +124 -115
  36. package/dist/utils/wrappedGenerator.js +376 -377
  37. package/dist/utils/wrappedHtmlGenerator.js +105 -106
  38. package/package.json +10 -10
@@ -1,305 +1,304 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getChronotypeDescription = exports.getChronotypeLabel = exports.analyzeTimePatterns = void 0;
4
- const date_fns_1 = require("date-fns");
5
- function analyzeTimePatterns(commits, startDate, endDate) {
6
- if (commits.length === 0) {
7
- return getEmptyPattern();
8
- }
9
- // Filter commits within the date range
10
- const filteredCommits = commits.filter(c => {
11
- const commitDate = new Date(c.date);
12
- return commitDate >= startDate && commitDate <= endDate;
13
- });
14
- if (filteredCommits.length === 0) {
15
- return getEmptyPattern();
16
- }
17
- // 1. Hourly distribution
18
- const hourlyMap = new Map();
19
- for (let i = 0; i < 24; i++) {
20
- hourlyMap.set(i, 0);
21
- }
22
- filteredCommits.forEach(commit => {
23
- const hour = (0, date_fns_1.getHours)(new Date(commit.date));
24
- hourlyMap.set(hour, (hourlyMap.get(hour) || 0) + 1);
25
- });
26
- const hourly = Array.from(hourlyMap.entries()).map(([hour, count]) => ({
27
- hour,
28
- commitCount: count,
29
- percentage: (count / filteredCommits.length) * 100
30
- }));
31
- // 2. Daily distribution (day of week)
32
- const dayMap = new Map();
33
- for (let i = 0; i < 7; i++) {
34
- dayMap.set(i, []);
35
- }
36
- filteredCommits.forEach(commit => {
37
- const dayIndex = (0, date_fns_1.getDay)(new Date(commit.date));
38
- dayMap.get(dayIndex).push(1);
39
- });
40
- const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
41
- const dailyAverage = Array.from(dayMap.entries()).map(([dayIndex, commits]) => ({
42
- dayOfWeek: dayNames[dayIndex],
43
- commitCount: commits.length,
44
- avgCommitsPerDay: commits.length / Math.ceil((endDate.getTime() - startDate.getTime()) / (7 * 24 * 60 * 60 * 1000))
45
- }));
46
- // 3. Peak hour
47
- const peakHourEntry = hourly.reduce((max, curr) => curr.commitCount > max.commitCount ? curr : max);
48
- const peakHour = {
49
- hour: peakHourEntry.hour,
50
- commitCount: peakHourEntry.commitCount
51
- };
52
- // 4. Peak day
53
- const peakDayEntry = dailyAverage.reduce((max, curr) => curr.commitCount > max.commitCount ? curr : max);
54
- const peakDay = {
55
- dayOfWeek: peakDayEntry.dayOfWeek,
56
- avgCommits: peakDayEntry.avgCommitsPerDay
57
- };
58
- // 5. Chronotype detection
59
- const chronotypeData = getChronotype(hourly);
60
- // 6. Work-life balance
61
- const workLifeBalance = calculateWorkLifeBalance(dailyAverage);
62
- // 7. Timing stats
63
- const timingStats = calculateTimingStats(filteredCommits);
64
- // 8. Burnout risk
65
- const burnoutRisk = detectBurnoutRisk(hourly, dailyAverage, filteredCommits);
66
- // 9. Consistency score
67
- const consistency = calculateConsistency(filteredCommits);
68
- return {
69
- hourly,
70
- dailyAverage,
71
- chronotype: chronotypeData.type,
72
- chronotypeConfidence: chronotypeData.confidence,
73
- peakHour,
74
- peakDay,
75
- workLifeBalance,
76
- timingStats,
77
- burnoutRisk,
78
- consistency
79
- };
80
- }
81
- exports.analyzeTimePatterns = analyzeTimePatterns;
82
- function getChronotype(hourly) {
83
- const totalCommits = hourly.reduce((sum, h) => sum + h.commitCount, 0);
84
- if (totalCommits === 0) {
85
- return { type: 'balanced', confidence: 0 };
86
- }
87
- // Calculate time period percentages
88
- const morningCommits = hourly.filter(h => h.hour >= 6 && h.hour < 12).reduce((sum, h) => sum + h.commitCount, 0);
89
- const eveningCommits = hourly.filter(h => h.hour >= 18 && h.hour < 24).reduce((sum, h) => sum + h.commitCount, 0);
90
- const nightCommits = hourly.filter(h => h.hour >= 0 && h.hour < 6).reduce((sum, h) => sum + h.commitCount, 0);
91
- const morningPct = (morningCommits / totalCommits) * 100;
92
- const eveningPct = (eveningCommits / totalCommits) * 100;
93
- const nightPct = (nightCommits / totalCommits) * 100;
94
- // Vampire: 30%+ commits between midnight-6am
95
- if (nightPct >= 30) {
96
- return { type: 'vampire', confidence: nightPct };
97
- }
98
- // Early Bird: 60%+ commits before 12pm
99
- if (morningPct >= 60) {
100
- return { type: 'early-bird', confidence: morningPct };
101
- }
102
- // Night Owl: 60%+ commits after 6pm
103
- if (eveningPct >= 60) {
104
- return { type: 'night-owl', confidence: eveningPct };
105
- }
106
- // Balanced: Even distribution
107
- return { type: 'balanced', confidence: 100 - Math.max(morningPct, eveningPct, nightPct) };
108
- }
109
- function calculateWorkLifeBalance(dailyAverage) {
110
- const weekdayCommits = dailyAverage
111
- .filter(d => !['Saturday', 'Sunday'].includes(d.dayOfWeek))
112
- .reduce((sum, d) => sum + d.commitCount, 0);
113
- const weekendCommits = dailyAverage
114
- .filter(d => ['Saturday', 'Sunday'].includes(d.dayOfWeek))
115
- .reduce((sum, d) => sum + d.commitCount, 0);
116
- const totalCommits = weekdayCommits + weekendCommits;
117
- const ratio = totalCommits > 0 ? weekdayCommits / weekendCommits : 0;
118
- // Score: 5 stars = ratio > 10 (very little weekend work)
119
- // 1 star = ratio < 2 (too much weekend work)
120
- let score = 5;
121
- if (ratio < 2)
122
- score = 1;
123
- else if (ratio < 4)
124
- score = 2;
125
- else if (ratio < 6)
126
- score = 3;
127
- else if (ratio < 10)
128
- score = 4;
129
- return {
130
- weekdayCommits,
131
- weekendCommits,
132
- ratio,
133
- score
134
- };
135
- }
136
- function calculateTimingStats(commits) {
137
- if (commits.length === 0) {
138
- return {
139
- avgFirstCommit: '00:00',
140
- avgLastCommit: '00:00',
141
- avgSessionLength: 0,
142
- longestSession: { date: new Date(), hours: 0 }
143
- };
144
- }
145
- // Group commits by date
146
- const commitsByDate = new Map();
147
- commits.forEach(commit => {
148
- const dateKey = (0, date_fns_1.format)(new Date(commit.date), 'yyyy-MM-dd');
149
- if (!commitsByDate.has(dateKey)) {
150
- commitsByDate.set(dateKey, []);
151
- }
152
- commitsByDate.get(dateKey).push(new Date(commit.date));
153
- });
154
- // Calculate first/last commit times and session lengths
155
- let totalFirstHour = 0;
156
- let totalLastHour = 0;
157
- let totalSessionHours = 0;
158
- let longestSession = { date: new Date(), hours: 0 };
159
- commitsByDate.forEach((times, dateKey) => {
160
- times.sort((a, b) => a.getTime() - b.getTime());
161
- const firstCommit = times[0];
162
- const lastCommit = times[times.length - 1];
163
- totalFirstHour += firstCommit.getHours() + (firstCommit.getMinutes() / 60);
164
- totalLastHour += lastCommit.getHours() + (lastCommit.getMinutes() / 60);
165
- const sessionLength = (0, date_fns_1.differenceInHours)(lastCommit, firstCommit);
166
- totalSessionHours += sessionLength;
167
- if (sessionLength > longestSession.hours) {
168
- longestSession = { date: (0, date_fns_1.parse)(dateKey, 'yyyy-MM-dd', new Date()), hours: sessionLength };
169
- }
170
- });
171
- const daysWithCommits = commitsByDate.size;
172
- const avgFirstHour = totalFirstHour / daysWithCommits;
173
- const avgLastHour = totalLastHour / daysWithCommits;
174
- const formatTime = (decimalHour) => {
175
- const hour = Math.floor(decimalHour);
176
- const minute = Math.floor((decimalHour - hour) * 60);
177
- return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
178
- };
179
- return {
180
- avgFirstCommit: formatTime(avgFirstHour),
181
- avgLastCommit: formatTime(avgLastHour),
182
- avgSessionLength: totalSessionHours / daysWithCommits,
183
- longestSession
184
- };
185
- }
186
- function detectBurnoutRisk(hourly, dailyAverage, commits) {
187
- const indicators = [];
188
- let riskScore = 0;
189
- // Check for late-night commits (after 11 PM)
190
- const lateNightCommits = hourly.filter(h => h.hour >= 23).reduce((sum, h) => sum + h.commitCount, 0);
191
- const veryLateCommits = hourly.filter(h => h.hour >= 0 && h.hour < 6).reduce((sum, h) => sum + h.commitCount, 0);
192
- if (lateNightCommits > 15) {
193
- indicators.push(`${lateNightCommits} late-night commits (after 11 PM)`);
194
- riskScore += 20;
195
- }
196
- if (veryLateCommits > 10) {
197
- indicators.push(`${veryLateCommits} commits between midnight-6am`);
198
- riskScore += 30;
199
- }
200
- // Weekend work is not counted as a burnout indicator since it's ambiguous
201
- // (could be hobby projects vs. overwork) - focusing on late night patterns instead
202
- // Check for uneven distribution (working all hours)
203
- const activeHours = hourly.filter(h => h.commitCount > 0).length;
204
- if (activeHours > 16) {
205
- indicators.push('Commits spread across too many hours');
206
- riskScore += 15;
207
- }
208
- // Determine risk level
209
- let level = 'low';
210
- if (riskScore >= 50)
211
- level = 'high';
212
- else if (riskScore >= 25)
213
- level = 'medium';
214
- // Add positive indicators if low risk
215
- if (riskScore < 25) {
216
- indicators.push('✅ Regular commit times (±2 hour variance)');
217
- indicators.push('✅ Healthy work-life balance');
218
- }
219
- return {
220
- level,
221
- indicators,
222
- score: Math.min(riskScore, 100)
223
- };
224
- }
225
- function calculateConsistency(commits) {
226
- if (commits.length === 0) {
227
- return { score: 0, variance: 0, regularity: 'chaotic' };
228
- }
229
- // Calculate variance in commit times (hour of day)
230
- const hours = commits.map(c => (0, date_fns_1.getHours)(new Date(c.date)));
231
- const avgHour = hours.reduce((sum, h) => sum + h, 0) / hours.length;
232
- const variance = hours.reduce((sum, h) => sum + Math.pow(h - avgHour, 2), 0) / hours.length;
233
- const stdDev = Math.sqrt(variance);
234
- // Score based on standard deviation (lower = more consistent)
235
- // stdDev < 2: very consistent (same 2-hour window)
236
- // stdDev < 4: consistent
237
- // stdDev < 6: irregular
238
- // stdDev >= 6: chaotic
239
- let score = 10;
240
- let regularity = 'very-consistent';
241
- if (stdDev >= 6) {
242
- score = Math.max(0, 10 - stdDev);
243
- regularity = 'chaotic';
244
- }
245
- else if (stdDev >= 4) {
246
- score = 10 - stdDev;
247
- regularity = 'irregular';
248
- }
249
- else if (stdDev >= 2) {
250
- score = 10 - (stdDev / 2);
251
- regularity = 'consistent';
252
- }
253
- else {
254
- score = 10 - (stdDev / 4);
255
- regularity = 'very-consistent';
256
- }
257
- return {
258
- score: Math.max(0, Math.min(10, score)),
259
- variance: stdDev,
260
- regularity
261
- };
262
- }
263
- function getEmptyPattern() {
264
- return {
265
- hourly: Array.from({ length: 24 }, (_, i) => ({ hour: i, commitCount: 0, percentage: 0 })),
266
- dailyAverage: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].map(day => ({
267
- dayOfWeek: day,
268
- commitCount: 0,
269
- avgCommitsPerDay: 0
270
- })),
271
- chronotype: 'balanced',
272
- chronotypeConfidence: 0,
273
- peakHour: { hour: 0, commitCount: 0 },
274
- peakDay: { dayOfWeek: 'Monday', avgCommits: 0 },
275
- workLifeBalance: { weekdayCommits: 0, weekendCommits: 0, ratio: 0, score: 5 },
276
- timingStats: {
277
- avgFirstCommit: '00:00',
278
- avgLastCommit: '00:00',
279
- avgSessionLength: 0,
280
- longestSession: { date: new Date(), hours: 0 }
281
- },
282
- burnoutRisk: { level: 'low', indicators: [], score: 0 },
283
- consistency: { score: 0, variance: 0, regularity: 'chaotic' }
284
- };
285
- }
286
- function getChronotypeLabel(chronotype) {
287
- const labels = {
288
- 'early-bird': '🐦 Early Bird',
289
- 'night-owl': '🦉 Night Owl',
290
- 'balanced': '⚖️ Balanced',
291
- 'vampire': '🧛 Vampire'
292
- };
293
- return labels[chronotype] || chronotype;
294
- }
295
- exports.getChronotypeLabel = getChronotypeLabel;
296
- function getChronotypeDescription(chronotype) {
297
- const descriptions = {
298
- 'early-bird': "You're most productive in the morning. Consider scheduling important work before lunch.",
299
- 'night-owl': "You're most productive in the evening. Night time is when you shine!",
300
- 'balanced': "You maintain a well-balanced coding schedule throughout the day.",
301
- 'vampire': "⚠️ Many late-night commits detected. Consider adjusting your schedule for better work-life balance."
302
- };
303
- return descriptions[chronotype] || '';
304
- }
305
- exports.getChronotypeDescription = getChronotypeDescription;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeTimePatterns = analyzeTimePatterns;
4
+ exports.getChronotypeLabel = getChronotypeLabel;
5
+ exports.getChronotypeDescription = getChronotypeDescription;
6
+ const date_fns_1 = require("date-fns");
7
+ function analyzeTimePatterns(commits, startDate, endDate) {
8
+ if (commits.length === 0) {
9
+ return getEmptyPattern();
10
+ }
11
+ // Filter commits within the date range
12
+ const filteredCommits = commits.filter(c => {
13
+ const commitDate = new Date(c.date);
14
+ return commitDate >= startDate && commitDate <= endDate;
15
+ });
16
+ if (filteredCommits.length === 0) {
17
+ return getEmptyPattern();
18
+ }
19
+ // 1. Hourly distribution
20
+ const hourlyMap = new Map();
21
+ for (let i = 0; i < 24; i++) {
22
+ hourlyMap.set(i, 0);
23
+ }
24
+ filteredCommits.forEach(commit => {
25
+ const hour = (0, date_fns_1.getHours)(new Date(commit.date));
26
+ hourlyMap.set(hour, (hourlyMap.get(hour) || 0) + 1);
27
+ });
28
+ const hourly = Array.from(hourlyMap.entries()).map(([hour, count]) => ({
29
+ hour,
30
+ commitCount: count,
31
+ percentage: (count / filteredCommits.length) * 100
32
+ }));
33
+ // 2. Daily distribution (day of week)
34
+ const dayMap = new Map();
35
+ for (let i = 0; i < 7; i++) {
36
+ dayMap.set(i, []);
37
+ }
38
+ filteredCommits.forEach(commit => {
39
+ const dayIndex = (0, date_fns_1.getDay)(new Date(commit.date));
40
+ dayMap.get(dayIndex).push(1);
41
+ });
42
+ const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
43
+ const dailyAverage = Array.from(dayMap.entries()).map(([dayIndex, commits]) => ({
44
+ dayOfWeek: dayNames[dayIndex],
45
+ commitCount: commits.length,
46
+ avgCommitsPerDay: commits.length / Math.ceil((endDate.getTime() - startDate.getTime()) / (7 * 24 * 60 * 60 * 1000))
47
+ }));
48
+ // 3. Peak hour
49
+ const peakHourEntry = hourly.reduce((max, curr) => curr.commitCount > max.commitCount ? curr : max);
50
+ const peakHour = {
51
+ hour: peakHourEntry.hour,
52
+ commitCount: peakHourEntry.commitCount
53
+ };
54
+ // 4. Peak day
55
+ const peakDayEntry = dailyAverage.reduce((max, curr) => curr.commitCount > max.commitCount ? curr : max);
56
+ const peakDay = {
57
+ dayOfWeek: peakDayEntry.dayOfWeek,
58
+ avgCommits: peakDayEntry.avgCommitsPerDay
59
+ };
60
+ // 5. Chronotype detection
61
+ const chronotypeData = getChronotype(hourly);
62
+ // 6. Work-life balance
63
+ const workLifeBalance = calculateWorkLifeBalance(dailyAverage);
64
+ // 7. Timing stats
65
+ const timingStats = calculateTimingStats(filteredCommits);
66
+ // 8. Burnout risk
67
+ const burnoutRisk = detectBurnoutRisk(hourly, dailyAverage, filteredCommits);
68
+ // 9. Consistency score
69
+ const consistency = calculateConsistency(filteredCommits);
70
+ return {
71
+ hourly,
72
+ dailyAverage,
73
+ chronotype: chronotypeData.type,
74
+ chronotypeConfidence: chronotypeData.confidence,
75
+ peakHour,
76
+ peakDay,
77
+ workLifeBalance,
78
+ timingStats,
79
+ burnoutRisk,
80
+ consistency
81
+ };
82
+ }
83
+ function getChronotype(hourly) {
84
+ const totalCommits = hourly.reduce((sum, h) => sum + h.commitCount, 0);
85
+ if (totalCommits === 0) {
86
+ return { type: 'balanced', confidence: 0 };
87
+ }
88
+ // Calculate time period percentages
89
+ const morningCommits = hourly.filter(h => h.hour >= 6 && h.hour < 12).reduce((sum, h) => sum + h.commitCount, 0);
90
+ const eveningCommits = hourly.filter(h => h.hour >= 18 && h.hour < 24).reduce((sum, h) => sum + h.commitCount, 0);
91
+ const nightCommits = hourly.filter(h => h.hour >= 0 && h.hour < 6).reduce((sum, h) => sum + h.commitCount, 0);
92
+ const morningPct = (morningCommits / totalCommits) * 100;
93
+ const eveningPct = (eveningCommits / totalCommits) * 100;
94
+ const nightPct = (nightCommits / totalCommits) * 100;
95
+ // Vampire: 30%+ commits between midnight-6am
96
+ if (nightPct >= 30) {
97
+ return { type: 'vampire', confidence: nightPct };
98
+ }
99
+ // Early Bird: 60%+ commits before 12pm
100
+ if (morningPct >= 60) {
101
+ return { type: 'early-bird', confidence: morningPct };
102
+ }
103
+ // Night Owl: 60%+ commits after 6pm
104
+ if (eveningPct >= 60) {
105
+ return { type: 'night-owl', confidence: eveningPct };
106
+ }
107
+ // Balanced: Even distribution
108
+ return { type: 'balanced', confidence: 100 - Math.max(morningPct, eveningPct, nightPct) };
109
+ }
110
+ function calculateWorkLifeBalance(dailyAverage) {
111
+ const weekdayCommits = dailyAverage
112
+ .filter(d => !['Saturday', 'Sunday'].includes(d.dayOfWeek))
113
+ .reduce((sum, d) => sum + d.commitCount, 0);
114
+ const weekendCommits = dailyAverage
115
+ .filter(d => ['Saturday', 'Sunday'].includes(d.dayOfWeek))
116
+ .reduce((sum, d) => sum + d.commitCount, 0);
117
+ const totalCommits = weekdayCommits + weekendCommits;
118
+ const ratio = totalCommits > 0 ? weekdayCommits / weekendCommits : 0;
119
+ // Score: 5 stars = ratio > 10 (very little weekend work)
120
+ // 1 star = ratio < 2 (too much weekend work)
121
+ let score = 5;
122
+ if (ratio < 2)
123
+ score = 1;
124
+ else if (ratio < 4)
125
+ score = 2;
126
+ else if (ratio < 6)
127
+ score = 3;
128
+ else if (ratio < 10)
129
+ score = 4;
130
+ return {
131
+ weekdayCommits,
132
+ weekendCommits,
133
+ ratio,
134
+ score
135
+ };
136
+ }
137
+ function calculateTimingStats(commits) {
138
+ if (commits.length === 0) {
139
+ return {
140
+ avgFirstCommit: '00:00',
141
+ avgLastCommit: '00:00',
142
+ avgSessionLength: 0,
143
+ longestSession: { date: new Date(), hours: 0 }
144
+ };
145
+ }
146
+ // Group commits by date
147
+ const commitsByDate = new Map();
148
+ commits.forEach(commit => {
149
+ const dateKey = (0, date_fns_1.format)(new Date(commit.date), 'yyyy-MM-dd');
150
+ if (!commitsByDate.has(dateKey)) {
151
+ commitsByDate.set(dateKey, []);
152
+ }
153
+ commitsByDate.get(dateKey).push(new Date(commit.date));
154
+ });
155
+ // Calculate first/last commit times and session lengths
156
+ let totalFirstHour = 0;
157
+ let totalLastHour = 0;
158
+ let totalSessionHours = 0;
159
+ let longestSession = { date: new Date(), hours: 0 };
160
+ commitsByDate.forEach((times, dateKey) => {
161
+ times.sort((a, b) => a.getTime() - b.getTime());
162
+ const firstCommit = times[0];
163
+ const lastCommit = times[times.length - 1];
164
+ totalFirstHour += firstCommit.getHours() + (firstCommit.getMinutes() / 60);
165
+ totalLastHour += lastCommit.getHours() + (lastCommit.getMinutes() / 60);
166
+ const sessionLength = (0, date_fns_1.differenceInHours)(lastCommit, firstCommit);
167
+ totalSessionHours += sessionLength;
168
+ if (sessionLength > longestSession.hours) {
169
+ longestSession = { date: (0, date_fns_1.parse)(dateKey, 'yyyy-MM-dd', new Date()), hours: sessionLength };
170
+ }
171
+ });
172
+ const daysWithCommits = commitsByDate.size;
173
+ const avgFirstHour = totalFirstHour / daysWithCommits;
174
+ const avgLastHour = totalLastHour / daysWithCommits;
175
+ const formatTime = (decimalHour) => {
176
+ const hour = Math.floor(decimalHour);
177
+ const minute = Math.floor((decimalHour - hour) * 60);
178
+ return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
179
+ };
180
+ return {
181
+ avgFirstCommit: formatTime(avgFirstHour),
182
+ avgLastCommit: formatTime(avgLastHour),
183
+ avgSessionLength: totalSessionHours / daysWithCommits,
184
+ longestSession
185
+ };
186
+ }
187
+ function detectBurnoutRisk(hourly, dailyAverage, commits) {
188
+ const indicators = [];
189
+ let riskScore = 0;
190
+ // Check for late-night commits (after 11 PM)
191
+ const lateNightCommits = hourly.filter(h => h.hour >= 23).reduce((sum, h) => sum + h.commitCount, 0);
192
+ const veryLateCommits = hourly.filter(h => h.hour >= 0 && h.hour < 6).reduce((sum, h) => sum + h.commitCount, 0);
193
+ if (lateNightCommits > 15) {
194
+ indicators.push(`${lateNightCommits} late-night commits (after 11 PM)`);
195
+ riskScore += 20;
196
+ }
197
+ if (veryLateCommits > 10) {
198
+ indicators.push(`${veryLateCommits} commits between midnight-6am`);
199
+ riskScore += 30;
200
+ }
201
+ // Weekend work is not counted as a burnout indicator since it's ambiguous
202
+ // (could be hobby projects vs. overwork) - focusing on late night patterns instead
203
+ // Check for uneven distribution (working all hours)
204
+ const activeHours = hourly.filter(h => h.commitCount > 0).length;
205
+ if (activeHours > 16) {
206
+ indicators.push('Commits spread across too many hours');
207
+ riskScore += 15;
208
+ }
209
+ // Determine risk level
210
+ let level = 'low';
211
+ if (riskScore >= 50)
212
+ level = 'high';
213
+ else if (riskScore >= 25)
214
+ level = 'medium';
215
+ // Add positive indicators if low risk
216
+ if (riskScore < 25) {
217
+ indicators.push('✅ Regular commit times (±2 hour variance)');
218
+ indicators.push('✅ Healthy work-life balance');
219
+ }
220
+ return {
221
+ level,
222
+ indicators,
223
+ score: Math.min(riskScore, 100)
224
+ };
225
+ }
226
+ function calculateConsistency(commits) {
227
+ if (commits.length === 0) {
228
+ return { score: 0, variance: 0, regularity: 'chaotic' };
229
+ }
230
+ // Calculate variance in commit times (hour of day)
231
+ const hours = commits.map(c => (0, date_fns_1.getHours)(new Date(c.date)));
232
+ const avgHour = hours.reduce((sum, h) => sum + h, 0) / hours.length;
233
+ const variance = hours.reduce((sum, h) => sum + Math.pow(h - avgHour, 2), 0) / hours.length;
234
+ const stdDev = Math.sqrt(variance);
235
+ // Score based on standard deviation (lower = more consistent)
236
+ // stdDev < 2: very consistent (same 2-hour window)
237
+ // stdDev < 4: consistent
238
+ // stdDev < 6: irregular
239
+ // stdDev >= 6: chaotic
240
+ let score = 10;
241
+ let regularity = 'very-consistent';
242
+ if (stdDev >= 6) {
243
+ score = Math.max(0, 10 - stdDev);
244
+ regularity = 'chaotic';
245
+ }
246
+ else if (stdDev >= 4) {
247
+ score = 10 - stdDev;
248
+ regularity = 'irregular';
249
+ }
250
+ else if (stdDev >= 2) {
251
+ score = 10 - (stdDev / 2);
252
+ regularity = 'consistent';
253
+ }
254
+ else {
255
+ score = 10 - (stdDev / 4);
256
+ regularity = 'very-consistent';
257
+ }
258
+ return {
259
+ score: Math.max(0, Math.min(10, score)),
260
+ variance: stdDev,
261
+ regularity
262
+ };
263
+ }
264
+ function getEmptyPattern() {
265
+ return {
266
+ hourly: Array.from({ length: 24 }, (_, i) => ({ hour: i, commitCount: 0, percentage: 0 })),
267
+ dailyAverage: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].map(day => ({
268
+ dayOfWeek: day,
269
+ commitCount: 0,
270
+ avgCommitsPerDay: 0
271
+ })),
272
+ chronotype: 'balanced',
273
+ chronotypeConfidence: 0,
274
+ peakHour: { hour: 0, commitCount: 0 },
275
+ peakDay: { dayOfWeek: 'Monday', avgCommits: 0 },
276
+ workLifeBalance: { weekdayCommits: 0, weekendCommits: 0, ratio: 0, score: 5 },
277
+ timingStats: {
278
+ avgFirstCommit: '00:00',
279
+ avgLastCommit: '00:00',
280
+ avgSessionLength: 0,
281
+ longestSession: { date: new Date(), hours: 0 }
282
+ },
283
+ burnoutRisk: { level: 'low', indicators: [], score: 0 },
284
+ consistency: { score: 0, variance: 0, regularity: 'chaotic' }
285
+ };
286
+ }
287
+ function getChronotypeLabel(chronotype) {
288
+ const labels = {
289
+ 'early-bird': '🐦 Early Bird',
290
+ 'night-owl': '🦉 Night Owl',
291
+ 'balanced': '⚖️ Balanced',
292
+ 'vampire': '🧛 Vampire'
293
+ };
294
+ return labels[chronotype] || chronotype;
295
+ }
296
+ function getChronotypeDescription(chronotype) {
297
+ const descriptions = {
298
+ 'early-bird': "You're most productive in the morning. Consider scheduling important work before lunch.",
299
+ 'night-owl': "You're most productive in the evening. Night time is when you shine!",
300
+ 'balanced': "You maintain a well-balanced coding schedule throughout the day.",
301
+ 'vampire': "⚠️ Many late-night commits detected. Consider adjusting your schedule for better work-life balance."
302
+ };
303
+ return descriptions[chronotype] || '';
304
+ }