repo-wrapped 0.0.2 โ 0.0.4
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/dist/commands/generate.js +104 -95
- package/dist/constants/chronotypes.js +23 -23
- package/dist/constants/colors.js +18 -18
- package/dist/constants/index.js +18 -18
- package/dist/formatters/index.js +17 -17
- package/dist/formatters/timeFormatter.js +28 -29
- package/dist/generators/html/templates/achievementsSection.js +42 -43
- package/dist/generators/html/templates/commitQualitySection.js +25 -26
- package/dist/generators/html/templates/contributionGraph.js +47 -48
- package/dist/generators/html/templates/impactSection.js +19 -20
- package/dist/generators/html/templates/knowledgeSection.js +86 -87
- package/dist/generators/html/templates/streakSection.js +8 -9
- package/dist/generators/html/templates/timePatternsSection.js +45 -46
- package/dist/generators/html/utils/colorUtils.js +21 -21
- package/dist/generators/html/utils/commitMapBuilder.js +23 -24
- package/dist/generators/html/utils/dateRangeCalculator.js +56 -57
- package/dist/generators/html/utils/developerStatsCalculator.js +28 -29
- package/dist/generators/html/utils/scriptLoader.js +15 -16
- package/dist/generators/html/utils/styleLoader.js +17 -18
- package/dist/generators/html/utils/weekGrouper.js +27 -28
- package/dist/index.js +99 -77
- package/dist/types/index.js +2 -2
- package/dist/utils/achievementDefinitions.js +433 -433
- package/dist/utils/achievementEngine.js +169 -170
- package/dist/utils/commitQualityAnalyzer.js +367 -368
- package/dist/utils/fileHotspotAnalyzer.js +269 -270
- package/dist/utils/gitParser.js +136 -125
- package/dist/utils/htmlGenerator.js +232 -233
- package/dist/utils/impactAnalyzer.js +247 -248
- package/dist/utils/knowledgeDistributionAnalyzer.js +373 -374
- package/dist/utils/matrixGenerator.js +349 -350
- package/dist/utils/slideGenerator.js +170 -171
- package/dist/utils/streakCalculator.js +134 -135
- package/dist/utils/timePatternAnalyzer.js +304 -305
- package/dist/utils/wrappedDisplay.js +124 -115
- package/dist/utils/wrappedGenerator.js +376 -377
- package/dist/utils/wrappedHtmlGenerator.js +105 -106
- package/package.json +10 -10
|
@@ -1,350 +1,349 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateCommitMatrix =
|
|
7
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const date_fns_1 = require("date-fns");
|
|
9
|
-
const constants_1 = require("../constants");
|
|
10
|
-
const achievementEngine_1 = require("./achievementEngine");
|
|
11
|
-
const commitQualityAnalyzer_1 = require("./commitQualityAnalyzer");
|
|
12
|
-
const fileHotspotAnalyzer_1 = require("./fileHotspotAnalyzer");
|
|
13
|
-
const streakCalculator_1 = require("./streakCalculator");
|
|
14
|
-
const timePatternAnalyzer_1 = require("./timePatternAnalyzer");
|
|
15
|
-
function generateCommitMatrix(commits, year, monthsToShow, repoPath, repoName, repoUrl) {
|
|
16
|
-
// Get today or end of year if it's a past year
|
|
17
|
-
const now = new Date();
|
|
18
|
-
const isCurrentYear = year === now.getFullYear();
|
|
19
|
-
// Calculate the end date (today if current year, or end of December for past years)
|
|
20
|
-
const endDate = isCurrentYear
|
|
21
|
-
? now
|
|
22
|
-
: new Date(year, 11, 31);
|
|
23
|
-
// Calculate start date (N months back from end date)
|
|
24
|
-
const startDate = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(endDate, monthsToShow - 1));
|
|
25
|
-
// Get the full week range (start on Sunday, end on Saturday)
|
|
26
|
-
const weekStartDate = (0, date_fns_1.startOfWeek)(startDate, { weekStartsOn: 0 });
|
|
27
|
-
const weekEndDate = (0, date_fns_1.endOfWeek)(endDate, { weekStartsOn: 0 });
|
|
28
|
-
// Create a map of date string -> commit count
|
|
29
|
-
const commitMap = new Map();
|
|
30
|
-
commits.forEach(commit => {
|
|
31
|
-
const commitDate = new Date(commit.date);
|
|
32
|
-
if (commitDate >= startDate && commitDate <= endDate) {
|
|
33
|
-
const dateKey = (0, date_fns_1.format)(commitDate, 'yyyy-MM-dd');
|
|
34
|
-
commitMap.set(dateKey, (commitMap.get(dateKey) || 0) + 1);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
const totalCommits = Array.from(commitMap.values()).reduce((sum, count) => sum + count, 0);
|
|
38
|
-
// Calculate streaks
|
|
39
|
-
const streakData = (0, streakCalculator_1.calculateStreaks)(commits, startDate, endDate);
|
|
40
|
-
// Analyze time patterns
|
|
41
|
-
const timePattern = (0, timePatternAnalyzer_1.analyzeTimePatterns)(commits, startDate, endDate);
|
|
42
|
-
// Analyze commit quality
|
|
43
|
-
const commitQuality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits);
|
|
44
|
-
// Analyze file hotspots
|
|
45
|
-
const fileHotspots = (0, fileHotspotAnalyzer_1.analyzeFileHotspots)(repoPath, startDate, endDate);
|
|
46
|
-
// Prepare analysis data for achievements
|
|
47
|
-
const analysisData = {
|
|
48
|
-
commits,
|
|
49
|
-
totalCommits,
|
|
50
|
-
streakData,
|
|
51
|
-
timePattern,
|
|
52
|
-
commitQuality,
|
|
53
|
-
fileHotspots,
|
|
54
|
-
dateRange: { start: startDate, end: endDate }
|
|
55
|
-
};
|
|
56
|
-
// Check achievements
|
|
57
|
-
const achievementProgress = (0, achievementEngine_1.checkAchievements)(analysisData);
|
|
58
|
-
return formatGitHubStyle(commitMap, weekStartDate, weekEndDate, startDate, endDate, totalCommits, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, repoName, repoUrl);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
currentWeek.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
output += '\n';
|
|
129
|
-
output += chalk_1.default.dim('
|
|
130
|
-
|
|
131
|
-
formatCell(
|
|
132
|
-
formatCell(
|
|
133
|
-
formatCell(
|
|
134
|
-
formatCell(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
output += '\n';
|
|
139
|
-
output += chalk_1.default.
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
output += '\n';
|
|
158
|
-
output += chalk_1.default.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
output +=
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
output += '\n';
|
|
188
|
-
output += chalk_1.default.
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
output +=
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
output +=
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
output +=
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
commitQuality.codeHygiene.
|
|
227
|
-
commitQuality.codeHygiene.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
output += chalk_1.default.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
output +=
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
output += '\n';
|
|
293
|
-
output += chalk_1.default.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
output +=
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
output +=
|
|
314
|
-
output +=
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
.
|
|
325
|
-
|
|
326
|
-
a.earnedDate)
|
|
327
|
-
.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCommitMatrix = generateCommitMatrix;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const date_fns_1 = require("date-fns");
|
|
9
|
+
const constants_1 = require("../constants");
|
|
10
|
+
const achievementEngine_1 = require("./achievementEngine");
|
|
11
|
+
const commitQualityAnalyzer_1 = require("./commitQualityAnalyzer");
|
|
12
|
+
const fileHotspotAnalyzer_1 = require("./fileHotspotAnalyzer");
|
|
13
|
+
const streakCalculator_1 = require("./streakCalculator");
|
|
14
|
+
const timePatternAnalyzer_1 = require("./timePatternAnalyzer");
|
|
15
|
+
function generateCommitMatrix(commits, year, monthsToShow, repoPath, repoName, repoUrl) {
|
|
16
|
+
// Get today or end of year if it's a past year
|
|
17
|
+
const now = new Date();
|
|
18
|
+
const isCurrentYear = year === now.getFullYear();
|
|
19
|
+
// Calculate the end date (today if current year, or end of December for past years)
|
|
20
|
+
const endDate = isCurrentYear
|
|
21
|
+
? now
|
|
22
|
+
: new Date(year, 11, 31);
|
|
23
|
+
// Calculate start date (N months back from end date)
|
|
24
|
+
const startDate = (0, date_fns_1.startOfMonth)((0, date_fns_1.subMonths)(endDate, monthsToShow - 1));
|
|
25
|
+
// Get the full week range (start on Sunday, end on Saturday)
|
|
26
|
+
const weekStartDate = (0, date_fns_1.startOfWeek)(startDate, { weekStartsOn: 0 });
|
|
27
|
+
const weekEndDate = (0, date_fns_1.endOfWeek)(endDate, { weekStartsOn: 0 });
|
|
28
|
+
// Create a map of date string -> commit count
|
|
29
|
+
const commitMap = new Map();
|
|
30
|
+
commits.forEach(commit => {
|
|
31
|
+
const commitDate = new Date(commit.date);
|
|
32
|
+
if (commitDate >= startDate && commitDate <= endDate) {
|
|
33
|
+
const dateKey = (0, date_fns_1.format)(commitDate, 'yyyy-MM-dd');
|
|
34
|
+
commitMap.set(dateKey, (commitMap.get(dateKey) || 0) + 1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const totalCommits = Array.from(commitMap.values()).reduce((sum, count) => sum + count, 0);
|
|
38
|
+
// Calculate streaks
|
|
39
|
+
const streakData = (0, streakCalculator_1.calculateStreaks)(commits, startDate, endDate);
|
|
40
|
+
// Analyze time patterns
|
|
41
|
+
const timePattern = (0, timePatternAnalyzer_1.analyzeTimePatterns)(commits, startDate, endDate);
|
|
42
|
+
// Analyze commit quality
|
|
43
|
+
const commitQuality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits);
|
|
44
|
+
// Analyze file hotspots
|
|
45
|
+
const fileHotspots = (0, fileHotspotAnalyzer_1.analyzeFileHotspots)(repoPath, startDate, endDate);
|
|
46
|
+
// Prepare analysis data for achievements
|
|
47
|
+
const analysisData = {
|
|
48
|
+
commits,
|
|
49
|
+
totalCommits,
|
|
50
|
+
streakData,
|
|
51
|
+
timePattern,
|
|
52
|
+
commitQuality,
|
|
53
|
+
fileHotspots,
|
|
54
|
+
dateRange: { start: startDate, end: endDate }
|
|
55
|
+
};
|
|
56
|
+
// Check achievements
|
|
57
|
+
const achievementProgress = (0, achievementEngine_1.checkAchievements)(analysisData);
|
|
58
|
+
return formatGitHubStyle(commitMap, weekStartDate, weekEndDate, startDate, endDate, totalCommits, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, repoName, repoUrl);
|
|
59
|
+
}
|
|
60
|
+
function formatGitHubStyle(commitMap, weekStartDate, weekEndDate, dataStartDate, dataEndDate, totalCommits, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, repoName, repoUrl) {
|
|
61
|
+
const days = (0, date_fns_1.eachDayOfInterval)({ start: weekStartDate, end: weekEndDate });
|
|
62
|
+
const dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
63
|
+
// Group days into weeks
|
|
64
|
+
const weeks = [];
|
|
65
|
+
let currentWeek = [];
|
|
66
|
+
days.forEach(day => {
|
|
67
|
+
currentWeek.push(day);
|
|
68
|
+
if (currentWeek.length === 7) {
|
|
69
|
+
weeks.push(currentWeek);
|
|
70
|
+
currentWeek = [];
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// Build the output with header
|
|
74
|
+
let output = '\n';
|
|
75
|
+
// Add repository info if available
|
|
76
|
+
if (repoName) {
|
|
77
|
+
output += chalk_1.default.bold.cyan(` ${repoName}\n`);
|
|
78
|
+
if (repoUrl) {
|
|
79
|
+
output += chalk_1.default.dim(` ${repoUrl}\n`);
|
|
80
|
+
}
|
|
81
|
+
output += '\n';
|
|
82
|
+
}
|
|
83
|
+
// Add stats
|
|
84
|
+
output += chalk_1.default.dim(' ') + chalk_1.default.white(`${totalCommits} contributions in the last ${(0, date_fns_1.format)(dataStartDate, 'MMM yyyy')} - ${(0, date_fns_1.format)(dataEndDate, 'MMM yyyy')}\n\n`);
|
|
85
|
+
// Build month headers
|
|
86
|
+
let monthHeader = ' ';
|
|
87
|
+
let currentMonth = '';
|
|
88
|
+
weeks.forEach((week, weekIndex) => {
|
|
89
|
+
const firstDayOfWeek = week[0];
|
|
90
|
+
const monthName = (0, date_fns_1.format)(firstDayOfWeek, 'MMM');
|
|
91
|
+
if (monthName !== currentMonth) {
|
|
92
|
+
monthHeader += chalk_1.default.bold(monthName.padEnd(4));
|
|
93
|
+
currentMonth = monthName;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
monthHeader += ' ';
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
output += monthHeader + '\n';
|
|
100
|
+
// Day rows with better formatting
|
|
101
|
+
for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
|
|
102
|
+
// Only show Mon, Wed, Fri labels
|
|
103
|
+
const label = [1, 3, 5].includes(dayOfWeek)
|
|
104
|
+
? chalk_1.default.dim(dayLabels[dayOfWeek].padEnd(5))
|
|
105
|
+
: ' ';
|
|
106
|
+
let row = label + ' ';
|
|
107
|
+
weeks.forEach(week => {
|
|
108
|
+
const day = week[dayOfWeek];
|
|
109
|
+
if (day) {
|
|
110
|
+
const dateKey = (0, date_fns_1.format)(day, 'yyyy-MM-dd');
|
|
111
|
+
const count = commitMap.get(dateKey) || 0;
|
|
112
|
+
const isInRange = day >= dataStartDate && day <= dataEndDate;
|
|
113
|
+
if (isInRange) {
|
|
114
|
+
row += formatCell(count) + ' ';
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
row += chalk_1.default.dim('โ ') + ' ';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
row += ' ';
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
output += row + '\n';
|
|
125
|
+
}
|
|
126
|
+
// Add legend with better spacing
|
|
127
|
+
output += '\n';
|
|
128
|
+
output += chalk_1.default.dim(' Learn how we count contributions\n');
|
|
129
|
+
output += chalk_1.default.dim(' Less ') +
|
|
130
|
+
formatCell(0) + ' ' +
|
|
131
|
+
formatCell(1) + ' ' +
|
|
132
|
+
formatCell(4) + ' ' +
|
|
133
|
+
formatCell(7) + ' ' +
|
|
134
|
+
formatCell(10) + ' ' +
|
|
135
|
+
chalk_1.default.dim('More\n');
|
|
136
|
+
// Streak & Momentum Section
|
|
137
|
+
output += '\n';
|
|
138
|
+
output += chalk_1.default.bold.cyan('๐ Streak & Momentum\n');
|
|
139
|
+
output += chalk_1.default.dim('โ'.repeat(50)) + '\n';
|
|
140
|
+
const currentStreakEmoji = streakData.currentStreak.isActive ? '๐ฅ' : '๐ค';
|
|
141
|
+
const currentStreakText = streakData.currentStreak.days > 0
|
|
142
|
+
? `${streakData.currentStreak.days} day${streakData.currentStreak.days > 1 ? 's' : ''}`
|
|
143
|
+
: 'No active streak';
|
|
144
|
+
output += `${currentStreakEmoji} Current Streak: ${chalk_1.default.bold.green(currentStreakText)}`;
|
|
145
|
+
if (streakData.currentStreak.isActive && streakData.currentStreak.days > 0) {
|
|
146
|
+
output += chalk_1.default.dim(` - ${(0, streakCalculator_1.getStreakMotivation)(streakData.currentStreak.days, true)}`);
|
|
147
|
+
}
|
|
148
|
+
output += '\n';
|
|
149
|
+
const longestStreakText = streakData.longestStreak.days > 0
|
|
150
|
+
? `${streakData.longestStreak.days} day${streakData.longestStreak.days > 1 ? 's' : ''} (${(0, date_fns_1.format)(streakData.longestStreak.startDate, 'MMM d')} - ${(0, date_fns_1.format)(streakData.longestStreak.endDate, 'MMM d, yyyy')})`
|
|
151
|
+
: 'No streaks yet';
|
|
152
|
+
output += `๐ Longest Streak: ${chalk_1.default.bold.yellow(longestStreakText)}\n`;
|
|
153
|
+
const activePercentage = streakData.activeDayPercentage.toFixed(1);
|
|
154
|
+
output += `๐ Active Days: ${chalk_1.default.bold.cyan(streakData.totalActiveDays)}/${streakData.totalDays} (${activePercentage}%)\n`;
|
|
155
|
+
// Time & Productivity Patterns Section
|
|
156
|
+
output += '\n';
|
|
157
|
+
output += chalk_1.default.bold.cyan('โฐ Productivity Patterns\n');
|
|
158
|
+
output += chalk_1.default.dim('โ'.repeat(50)) + '\n';
|
|
159
|
+
// Peak hour
|
|
160
|
+
const peakHourLabel = timePattern.peakHour.hour === 0 ? '12:00 AM' :
|
|
161
|
+
timePattern.peakHour.hour < 12 ? `${timePattern.peakHour.hour}:00 AM` :
|
|
162
|
+
timePattern.peakHour.hour === 12 ? '12:00 PM' :
|
|
163
|
+
`${timePattern.peakHour.hour - 12}:00 PM`;
|
|
164
|
+
output += `โฑ๏ธ Peak Hour: ${chalk_1.default.bold.green(peakHourLabel)} (${timePattern.peakHour.commitCount} commits)\n`;
|
|
165
|
+
// Chronotype
|
|
166
|
+
const chronotypeLabel = (0, timePatternAnalyzer_1.getChronotypeLabel)(timePattern.chronotype);
|
|
167
|
+
const chronotypeDesc = (0, timePatternAnalyzer_1.getChronotypeDescription)(timePattern.chronotype);
|
|
168
|
+
output += `${chronotypeLabel} ${chalk_1.default.dim(`(${timePattern.chronotypeConfidence.toFixed(0)}% confidence)`)}\n`;
|
|
169
|
+
if (chronotypeDesc) {
|
|
170
|
+
output += chalk_1.default.dim(` ${chronotypeDesc}\n`);
|
|
171
|
+
}
|
|
172
|
+
// Work-life balance
|
|
173
|
+
const stars = 'โญ'.repeat(timePattern.workLifeBalance.score);
|
|
174
|
+
output += `โ๏ธ Work-Life Balance: ${stars} ${chalk_1.default.dim(`(${timePattern.workLifeBalance.score}/5)`)}\n`;
|
|
175
|
+
output += chalk_1.default.dim(` Weekday: ${timePattern.workLifeBalance.weekdayCommits} | Weekend: ${timePattern.workLifeBalance.weekendCommits}\n`);
|
|
176
|
+
// Consistency
|
|
177
|
+
output += `๐ Consistency: ${chalk_1.default.bold.yellow(timePattern.consistency.score.toFixed(1))}/10 ${chalk_1.default.dim(`(${timePattern.consistency.regularity})`)}\n`;
|
|
178
|
+
// Burnout warnings
|
|
179
|
+
if (timePattern.burnoutRisk.level !== 'low') {
|
|
180
|
+
output += `\n${chalk_1.default.yellow('โ ๏ธ Burnout Risk: ' + timePattern.burnoutRisk.level.toUpperCase())}\n`;
|
|
181
|
+
timePattern.burnoutRisk.indicators.forEach((indicator) => {
|
|
182
|
+
output += chalk_1.default.dim(` โข ${indicator}\n`);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// Commit Quality Section
|
|
186
|
+
output += '\n';
|
|
187
|
+
output += chalk_1.default.bold.cyan('๐ Commit Quality\n');
|
|
188
|
+
output += chalk_1.default.dim('โ'.repeat(50)) + '\n';
|
|
189
|
+
const rating = (0, commitQualityAnalyzer_1.getQualityRating)(commitQuality.overallScore);
|
|
190
|
+
const level = (0, commitQualityAnalyzer_1.getQualityLevel)(commitQuality.overallScore);
|
|
191
|
+
output += `๐ Overall Score: ${chalk_1.default.bold.green(commitQuality.overallScore.toFixed(1))}/10 ${rating} ${chalk_1.default.dim(`(${level})`)}\n`;
|
|
192
|
+
output += `โ
Convention: ${chalk_1.default.bold.cyan(commitQuality.conventionalCommits.adherence.toFixed(1))}%`;
|
|
193
|
+
if (commitQuality.conventionalCommits.adherence < 70) {
|
|
194
|
+
output += chalk_1.default.yellow(' โ ๏ธ');
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
output += chalk_1.default.green(' โจ');
|
|
198
|
+
}
|
|
199
|
+
output += '\n';
|
|
200
|
+
// Subject quality
|
|
201
|
+
output += `๐ Subject Quality: ${chalk_1.default.bold.yellow(commitQuality.subjectQuality.score.toFixed(1))}/10 `;
|
|
202
|
+
output += chalk_1.default.dim(`(avg ${commitQuality.subjectQuality.avgLength} chars)\n`);
|
|
203
|
+
// Body quality
|
|
204
|
+
output += `๐ Body Quality: ${chalk_1.default.bold.yellow(commitQuality.bodyQuality.score.toFixed(1))}/10 `;
|
|
205
|
+
output += chalk_1.default.dim(`(${commitQuality.bodyQuality.withBody}/${commitQuality.totalCommits} with body)\n`);
|
|
206
|
+
// Top commit types
|
|
207
|
+
if (Object.keys(commitQuality.conventionalCommits.types).length > 0) {
|
|
208
|
+
output += `\n${chalk_1.default.dim('Top Commit Types:')}\n`;
|
|
209
|
+
const sortedTypes = Object.entries(commitQuality.conventionalCommits.types)
|
|
210
|
+
.sort(([, a], [, b]) => b - a)
|
|
211
|
+
.slice(0, 3);
|
|
212
|
+
sortedTypes.forEach(([type, count]) => {
|
|
213
|
+
output += chalk_1.default.dim(` ${type}: ${count}\n`);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// Improvements
|
|
217
|
+
if (commitQuality.improvements && commitQuality.improvements.length > 0) {
|
|
218
|
+
output += `\n${chalk_1.default.yellow('๐ก Tips for Improvement:')}\n`;
|
|
219
|
+
commitQuality.improvements.slice(0, 2).forEach((tip) => {
|
|
220
|
+
output += chalk_1.default.dim(` โข ${tip}\n`);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
// Code hygiene metrics (if any)
|
|
224
|
+
const totalHygiene = commitQuality.codeHygiene.quickFixes +
|
|
225
|
+
commitQuality.codeHygiene.workInProgress +
|
|
226
|
+
commitQuality.codeHygiene.oops +
|
|
227
|
+
commitQuality.codeHygiene.vague;
|
|
228
|
+
if (totalHygiene > 0) {
|
|
229
|
+
output += `\n${chalk_1.default.dim('๐งน Code Hygiene Indicators:\n')}`;
|
|
230
|
+
if (commitQuality.codeHygiene.quickFixes > 0) {
|
|
231
|
+
output += chalk_1.default.dim(` Quick fixes (typos/spelling): ${commitQuality.codeHygiene.quickFixes}\n`);
|
|
232
|
+
}
|
|
233
|
+
if (commitQuality.codeHygiene.workInProgress > 0) {
|
|
234
|
+
output += chalk_1.default.dim(` Work in progress: ${commitQuality.codeHygiene.workInProgress}\n`);
|
|
235
|
+
}
|
|
236
|
+
if (commitQuality.codeHygiene.oops > 0) {
|
|
237
|
+
output += chalk_1.default.dim(` Correction commits: ${commitQuality.codeHygiene.oops}\n`);
|
|
238
|
+
}
|
|
239
|
+
if (commitQuality.codeHygiene.vague > 0) {
|
|
240
|
+
output += chalk_1.default.dim(` Vague messages: ${commitQuality.codeHygiene.vague}\n`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// File & Code Hotspots Section
|
|
244
|
+
output += '\n' + chalk_1.default.bold.hex('#58a6ff')('๐ฅ TOP FILE HOTSPOTS') + '\n';
|
|
245
|
+
if (!fileHotspots || fileHotspots.topFiles.length === 0) {
|
|
246
|
+
output += chalk_1.default.dim(' No file changes detected in this period.\n');
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const topFilesToShow = fileHotspots.topFiles.slice(0, 7);
|
|
250
|
+
const maxChanges = Math.max(...topFilesToShow.map((f) => f.changeCount));
|
|
251
|
+
topFilesToShow.forEach((file) => {
|
|
252
|
+
const barLength = Math.max(1, Math.round((file.changeCount / maxChanges) * 30));
|
|
253
|
+
const bar = chalk_1.default.hex('#f85149')('โ'.repeat(barLength));
|
|
254
|
+
const fileName = file.path.length > 40 ? '...' + file.path.slice(-37) : file.path;
|
|
255
|
+
output += chalk_1.default.dim(` ${fileName}\n`);
|
|
256
|
+
output += ` ${bar} ${chalk_1.default.bold(file.changeCount)} changes, ${file.authors.length} author${file.authors.length !== 1 ? 's' : ''}\n`;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
// Technical Debt Warnings
|
|
260
|
+
if (fileHotspots && fileHotspots.technicalDebt.length > 0) {
|
|
261
|
+
output += '\n' + chalk_1.default.bold.hex('#f85149')('โ ๏ธ TECHNICAL DEBT WARNINGS') + '\n';
|
|
262
|
+
const topDebt = fileHotspots.technicalDebt.slice(0, 5);
|
|
263
|
+
topDebt.forEach((debt) => {
|
|
264
|
+
const color = (0, fileHotspotAnalyzer_1.getRiskLevelColor)(debt.riskLevel);
|
|
265
|
+
const colorFn = color === 'red' ? chalk_1.default.red : color === 'yellow' ? chalk_1.default.yellow : chalk_1.default.gray;
|
|
266
|
+
const fileName = debt.file.length > 45 ? '...' + debt.file.slice(-42) : debt.file;
|
|
267
|
+
output += colorFn(` ${fileName}\n`);
|
|
268
|
+
output += chalk_1.default.dim(` Risk: ${debt.riskLevel.toUpperCase()} (complexity: ${debt.score}/10)\n`);
|
|
269
|
+
if (debt.recommendations.length > 0) {
|
|
270
|
+
output += chalk_1.default.dim(` โ ${debt.recommendations[0]}\n`);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
// Ownership Risks
|
|
275
|
+
if (fileHotspots && fileHotspots.ownershipRisks) {
|
|
276
|
+
const highRiskOwnership = fileHotspots.ownershipRisks.filter((risk) => risk.busFactorRisk >= 7);
|
|
277
|
+
if (highRiskOwnership.length > 0) {
|
|
278
|
+
output += '\n' + chalk_1.default.bold.hex('#d29922')('๐ BUS FACTOR WARNINGS') + '\n';
|
|
279
|
+
const topRisks = highRiskOwnership.slice(0, 5);
|
|
280
|
+
topRisks.forEach((risk) => {
|
|
281
|
+
const icon = (0, fileHotspotAnalyzer_1.getOwnershipIcon)(risk.ownershipType);
|
|
282
|
+
const fileName = risk.file.length > 45 ? '...' + risk.file.slice(-42) : risk.file;
|
|
283
|
+
output += chalk_1.default.yellow(` ${icon} ${fileName}\n`);
|
|
284
|
+
const topOwner = risk.owners[0];
|
|
285
|
+
output += chalk_1.default.dim(` ${topOwner.author} owns ${topOwner.percentage.toFixed(0)}% (${topOwner.commits} commits)\n`);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Achievements Section
|
|
290
|
+
if (achievementProgress) {
|
|
291
|
+
output += '\n';
|
|
292
|
+
output += chalk_1.default.bold.hex('#ffd700')('๐ ACHIEVEMENTS') + '\n';
|
|
293
|
+
output += chalk_1.default.dim('โ'.repeat(50)) + '\n';
|
|
294
|
+
// Show newly earned badges
|
|
295
|
+
if (achievementProgress.recentlyEarned && achievementProgress.recentlyEarned.length > 0) {
|
|
296
|
+
output += '\n' + chalk_1.default.bold.green('โจ NEW BADGES EARNED! ๐') + '\n';
|
|
297
|
+
achievementProgress.recentlyEarned.forEach((achievement) => {
|
|
298
|
+
output += chalk_1.default.green(` ${achievement.emoji} ${achievement.name} - ${achievement.description}\n`);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
// Show progress
|
|
302
|
+
output += `\n${chalk_1.default.cyan('Progress:')} ${chalk_1.default.bold.white(achievementProgress.unlockedCount)}/${achievementProgress.totalCount} badges unlocked `;
|
|
303
|
+
output += chalk_1.default.dim(`(${achievementProgress.completionPercentage.toFixed(1)}%)\n`);
|
|
304
|
+
// Progress bar
|
|
305
|
+
const barLength = 30;
|
|
306
|
+
const filledLength = Math.round((achievementProgress.completionPercentage / 100) * barLength);
|
|
307
|
+
const bar = 'โ'.repeat(filledLength) + 'โ'.repeat(barLength - filledLength);
|
|
308
|
+
output += ` ${chalk_1.default.hex('#ffd700')(bar)}\n`;
|
|
309
|
+
// Show next milestone
|
|
310
|
+
if (achievementProgress.nextMilestones && achievementProgress.nextMilestones.length > 0) {
|
|
311
|
+
const next = achievementProgress.nextMilestones[0];
|
|
312
|
+
output += `\n${chalk_1.default.yellow('๐ฏ Next Milestone:')}\n`;
|
|
313
|
+
output += ` ${next.emoji} ${chalk_1.default.bold(next.name)} ${chalk_1.default.dim(`(${next.progress.toFixed(0)}%)`)}\n`;
|
|
314
|
+
output += chalk_1.default.dim(` ${next.description}\n`);
|
|
315
|
+
// Progress bar for next milestone
|
|
316
|
+
const nextBarLength = 25;
|
|
317
|
+
const nextFilledLength = Math.round((next.progress / 100) * nextBarLength);
|
|
318
|
+
const nextBar = 'โ'.repeat(nextFilledLength) + 'โ'.repeat(nextBarLength - nextFilledLength);
|
|
319
|
+
output += ` ${chalk_1.default.hex('#58a6ff')(nextBar)} ${chalk_1.default.dim((100 - next.progress).toFixed(0) + '% to go!')}\n`;
|
|
320
|
+
}
|
|
321
|
+
// Show recent unlocks (excluding today's new badges)
|
|
322
|
+
const recentUnlocks = achievementProgress.achievements
|
|
323
|
+
.filter((a) => a.isUnlocked &&
|
|
324
|
+
!achievementProgress.recentlyEarned.some((r) => r.id === a.id) &&
|
|
325
|
+
a.earnedDate)
|
|
326
|
+
.sort((a, b) => b.earnedDate.getTime() - a.earnedDate.getTime())
|
|
327
|
+
.slice(0, 3);
|
|
328
|
+
if (recentUnlocks.length > 0) {
|
|
329
|
+
output += `\n${chalk_1.default.dim('Recent Unlocks:')}\n`;
|
|
330
|
+
recentUnlocks.forEach((achievement) => {
|
|
331
|
+
const daysAgo = Math.floor((Date.now() - achievement.earnedDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
332
|
+
const timeStr = daysAgo === 0 ? 'today' : daysAgo === 1 ? '1 day ago' : `${daysAgo} days ago`;
|
|
333
|
+
output += chalk_1.default.dim(` ${achievement.emoji} ${achievement.name} (${timeStr})\n`);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return output;
|
|
338
|
+
}
|
|
339
|
+
function formatCell(count) {
|
|
340
|
+
if (count === 0)
|
|
341
|
+
return chalk_1.default.hex('#ebedf0')('โ ');
|
|
342
|
+
if (count <= constants_1.CONTRIBUTION_LEVELS.LEVEL_1)
|
|
343
|
+
return chalk_1.default.hex('#9be9a8')('โ ');
|
|
344
|
+
if (count <= constants_1.CONTRIBUTION_LEVELS.LEVEL_2)
|
|
345
|
+
return chalk_1.default.hex('#40c463')('โ ');
|
|
346
|
+
if (count <= constants_1.CONTRIBUTION_LEVELS.LEVEL_3)
|
|
347
|
+
return chalk_1.default.hex('#30a14e')('โ ');
|
|
348
|
+
return chalk_1.default.hex('#216e39')('โ ');
|
|
349
|
+
}
|