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,248 +1,247 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.analyzeImpact = void 0;
4
- // File weight patterns based on criticality
5
- const FILE_WEIGHTS = [
6
- { pattern: /^src\/(core|lib)\//, weight: 1.5, category: 'core' },
7
- { pattern: /^(src|lib)\/.*\.(ts|js|tsx|jsx)$/, weight: 1.0, category: 'feature' },
8
- { pattern: /(test|spec|__tests__)\/|\.test\.|\.spec\./, weight: 0.8, category: 'test' },
9
- { pattern: /\.(config|rc)\.|\.env|tsconfig|webpack|vite|eslint|prettier/, weight: 0.6, category: 'config' },
10
- { pattern: /\.md$|^docs\/|^README/, weight: 0.4, category: 'docs' },
11
- { pattern: /package\.json|\.lock$|yarn\.lock|pnpm-lock/, weight: 0.3, category: 'config' },
12
- ];
13
- // Commit type weights based on conventional commits
14
- const TYPE_WEIGHTS = {
15
- feat: 1.5,
16
- fix: 1.2,
17
- refactor: 1.0,
18
- perf: 1.3,
19
- test: 0.9,
20
- docs: 0.5,
21
- chore: 0.4,
22
- style: 0.3,
23
- ci: 0.4,
24
- build: 0.5,
25
- };
26
- /**
27
- * Get file weight and category based on path
28
- */
29
- function getFileInfo(filePath) {
30
- const normalizedPath = filePath.toLowerCase().replace(/\\/g, '/');
31
- for (const { pattern, weight, category } of FILE_WEIGHTS) {
32
- if (pattern.test(normalizedPath)) {
33
- return { weight, category };
34
- }
35
- }
36
- // Default for unknown files
37
- return { weight: 0.7, category: 'feature' };
38
- }
39
- /**
40
- * Extract commit type from conventional commit message
41
- */
42
- function getCommitType(message) {
43
- const match = message.match(/^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?:/i);
44
- return match ? match[1].toLowerCase() : null;
45
- }
46
- /**
47
- * Calculate recency factor (recent changes weighted higher)
48
- */
49
- function getRecencyFactor(commitDate, now = new Date()) {
50
- const daysSinceCommit = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
51
- // Exponential decay over 90 days
52
- if (daysSinceCommit <= 7)
53
- return 1.0;
54
- if (daysSinceCommit <= 30)
55
- return 0.9;
56
- if (daysSinceCommit <= 60)
57
- return 0.7;
58
- if (daysSinceCommit <= 90)
59
- return 0.5;
60
- return 0.3;
61
- }
62
- /**
63
- * Extract files changed from commit message (simplified - assumes file paths in message)
64
- * In a real implementation, this would come from git diff
65
- */
66
- function extractFilesFromCommit(commit) {
67
- // For now, return empty - in a real implementation we'd parse git log --name-only
68
- // This is a placeholder that allows the analyzer to work with existing data
69
- return [];
70
- }
71
- /**
72
- * Analyze impact of commits
73
- */
74
- function analyzeImpact(commits, changedFiles) {
75
- if (commits.length === 0) {
76
- return getEmptyImpactAnalysis();
77
- }
78
- const now = new Date();
79
- const fileImpacts = new Map();
80
- let coreContributions = 0;
81
- let featureWork = 0;
82
- let maintenanceWork = 0;
83
- let documentationWork = 0;
84
- let totalRawScore = 0;
85
- let recentScore = 0; // Last 30 days
86
- let olderScore = 0; // 30-90 days ago
87
- // If we have file change data from fileHotspotAnalyzer, use it
88
- if (changedFiles && changedFiles.size > 0) {
89
- changedFiles.forEach((data, filePath) => {
90
- const { weight, category } = getFileInfo(filePath);
91
- const impactScore = data.count * weight;
92
- fileImpacts.set(filePath, {
93
- score: impactScore,
94
- count: data.count,
95
- lastChanged: data.lastChanged,
96
- category
97
- });
98
- totalRawScore += impactScore;
99
- // Categorize contributions
100
- switch (category) {
101
- case 'core':
102
- coreContributions += impactScore;
103
- break;
104
- case 'test':
105
- case 'config':
106
- maintenanceWork += impactScore;
107
- break;
108
- case 'docs':
109
- documentationWork += impactScore;
110
- break;
111
- default:
112
- featureWork += impactScore;
113
- }
114
- });
115
- }
116
- // Process commits for type-based weighting and recency
117
- commits.forEach(commit => {
118
- const commitDate = new Date(commit.date);
119
- const commitType = getCommitType(commit.message);
120
- const typeWeight = commitType ? (TYPE_WEIGHTS[commitType] || 1.0) : 1.0;
121
- const recencyFactor = getRecencyFactor(commitDate, now);
122
- const daysSinceCommit = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
123
- const baseScore = typeWeight * recencyFactor;
124
- if (daysSinceCommit <= 30) {
125
- recentScore += baseScore;
126
- }
127
- else if (daysSinceCommit <= 90) {
128
- olderScore += baseScore;
129
- }
130
- // Additional category scoring based on commit type
131
- if (commitType === 'feat') {
132
- featureWork += typeWeight;
133
- }
134
- else if (commitType === 'fix' || commitType === 'refactor') {
135
- maintenanceWork += typeWeight;
136
- }
137
- else if (commitType === 'docs') {
138
- documentationWork += typeWeight;
139
- }
140
- });
141
- // Normalize overall score to 0-100
142
- const maxPossibleScore = commits.length * 1.5 * 1.0; // Max type weight * max recency
143
- const normalizedScore = Math.min(100, Math.round((totalRawScore + recentScore) / Math.max(maxPossibleScore, 1) * 100));
144
- // Get top 10 impact files
145
- const topImpactFiles = Array.from(fileImpacts.entries())
146
- .sort((a, b) => b[1].score - a[1].score)
147
- .slice(0, 10)
148
- .map(([path, data]) => ({
149
- path,
150
- impactScore: Math.round(data.score * 10) / 10,
151
- changeCount: data.count,
152
- lastChanged: data.lastChanged,
153
- category: data.category
154
- }));
155
- // Determine trend
156
- let impactTrend = 'stable';
157
- if (recentScore > olderScore * 1.2) {
158
- impactTrend = 'increasing';
159
- }
160
- else if (recentScore < olderScore * 0.8) {
161
- impactTrend = 'decreasing';
162
- }
163
- // Generate insights
164
- const insights = generateInsights({
165
- coreContributions,
166
- featureWork,
167
- maintenanceWork,
168
- documentationWork,
169
- topImpactFiles,
170
- impactTrend,
171
- totalCommits: commits.length
172
- });
173
- // Normalize breakdown scores
174
- const breakdownTotal = coreContributions + featureWork + maintenanceWork + documentationWork || 1;
175
- return {
176
- overallScore: normalizedScore,
177
- scoreBreakdown: {
178
- coreContributions: Math.round((coreContributions / breakdownTotal) * 100),
179
- featureWork: Math.round((featureWork / breakdownTotal) * 100),
180
- maintenanceWork: Math.round((maintenanceWork / breakdownTotal) * 100),
181
- documentationWork: Math.round((documentationWork / breakdownTotal) * 100),
182
- },
183
- topImpactFiles,
184
- impactTrend,
185
- insights
186
- };
187
- }
188
- exports.analyzeImpact = analyzeImpact;
189
- /**
190
- * Generate meaningful insights from impact data
191
- */
192
- function generateInsights(data) {
193
- const insights = [];
194
- const total = data.coreContributions + data.featureWork + data.maintenanceWork + data.documentationWork || 1;
195
- // Core contributions insight
196
- const corePercentage = (data.coreContributions / total) * 100;
197
- if (corePercentage > 30) {
198
- insights.push(`Strong focus on core modules — ${corePercentage.toFixed(0)}% of impact from critical paths`);
199
- }
200
- // Feature work insight
201
- const featurePercentage = (data.featureWork / total) * 100;
202
- if (featurePercentage > 50) {
203
- insights.push(`Feature-focused development — ${featurePercentage.toFixed(0)}% of contributions are new functionality`);
204
- }
205
- // Maintenance insight
206
- const maintenancePercentage = (data.maintenanceWork / total) * 100;
207
- if (maintenancePercentage > 40) {
208
- insights.push(`Strong maintenance focus — keeping the codebase healthy`);
209
- }
210
- // Documentation insight
211
- const docPercentage = (data.documentationWork / total) * 100;
212
- if (docPercentage < 5 && data.totalCommits > 20) {
213
- insights.push(`Documentation contributions below average — consider adding READMEs or inline docs`);
214
- }
215
- else if (docPercentage > 15) {
216
- insights.push(`Good documentation practices — ${docPercentage.toFixed(0)}% of work includes docs`);
217
- }
218
- // Trend insight
219
- if (data.impactTrend === 'increasing') {
220
- insights.push(`Impact trending upward — recent contributions are higher value`);
221
- }
222
- else if (data.impactTrend === 'decreasing') {
223
- insights.push(`Recent activity lower than historical average`);
224
- }
225
- // Top files insight
226
- if (data.topImpactFiles.length > 0) {
227
- const topFile = data.topImpactFiles[0];
228
- insights.push(`Highest impact: ${topFile.path.split('/').pop()} (${topFile.changeCount} changes)`);
229
- }
230
- return insights.slice(0, 5); // Limit to 5 insights
231
- }
232
- /**
233
- * Return empty analysis for no commits
234
- */
235
- function getEmptyImpactAnalysis() {
236
- return {
237
- overallScore: 0,
238
- scoreBreakdown: {
239
- coreContributions: 0,
240
- featureWork: 0,
241
- maintenanceWork: 0,
242
- documentationWork: 0,
243
- },
244
- topImpactFiles: [],
245
- impactTrend: 'stable',
246
- insights: ['No commit data available for analysis']
247
- };
248
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeImpact = analyzeImpact;
4
+ // File weight patterns based on criticality
5
+ const FILE_WEIGHTS = [
6
+ { pattern: /^src\/(core|lib)\//, weight: 1.5, category: 'core' },
7
+ { pattern: /^(src|lib)\/.*\.(ts|js|tsx|jsx)$/, weight: 1.0, category: 'feature' },
8
+ { pattern: /(test|spec|__tests__)\/|\.test\.|\.spec\./, weight: 0.8, category: 'test' },
9
+ { pattern: /\.(config|rc)\.|\.env|tsconfig|webpack|vite|eslint|prettier/, weight: 0.6, category: 'config' },
10
+ { pattern: /\.md$|^docs\/|^README/, weight: 0.4, category: 'docs' },
11
+ { pattern: /package\.json|\.lock$|yarn\.lock|pnpm-lock/, weight: 0.3, category: 'config' },
12
+ ];
13
+ // Commit type weights based on conventional commits
14
+ const TYPE_WEIGHTS = {
15
+ feat: 1.5,
16
+ fix: 1.2,
17
+ refactor: 1.0,
18
+ perf: 1.3,
19
+ test: 0.9,
20
+ docs: 0.5,
21
+ chore: 0.4,
22
+ style: 0.3,
23
+ ci: 0.4,
24
+ build: 0.5,
25
+ };
26
+ /**
27
+ * Get file weight and category based on path
28
+ */
29
+ function getFileInfo(filePath) {
30
+ const normalizedPath = filePath.toLowerCase().replace(/\\/g, '/');
31
+ for (const { pattern, weight, category } of FILE_WEIGHTS) {
32
+ if (pattern.test(normalizedPath)) {
33
+ return { weight, category };
34
+ }
35
+ }
36
+ // Default for unknown files
37
+ return { weight: 0.7, category: 'feature' };
38
+ }
39
+ /**
40
+ * Extract commit type from conventional commit message
41
+ */
42
+ function getCommitType(message) {
43
+ const match = message.match(/^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?:/i);
44
+ return match ? match[1].toLowerCase() : null;
45
+ }
46
+ /**
47
+ * Calculate recency factor (recent changes weighted higher)
48
+ */
49
+ function getRecencyFactor(commitDate, now = new Date()) {
50
+ const daysSinceCommit = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
51
+ // Exponential decay over 90 days
52
+ if (daysSinceCommit <= 7)
53
+ return 1.0;
54
+ if (daysSinceCommit <= 30)
55
+ return 0.9;
56
+ if (daysSinceCommit <= 60)
57
+ return 0.7;
58
+ if (daysSinceCommit <= 90)
59
+ return 0.5;
60
+ return 0.3;
61
+ }
62
+ /**
63
+ * Extract files changed from commit message (simplified - assumes file paths in message)
64
+ * In a real implementation, this would come from git diff
65
+ */
66
+ function extractFilesFromCommit(commit) {
67
+ // For now, return empty - in a real implementation we'd parse git log --name-only
68
+ // This is a placeholder that allows the analyzer to work with existing data
69
+ return [];
70
+ }
71
+ /**
72
+ * Analyze impact of commits
73
+ */
74
+ function analyzeImpact(commits, changedFiles) {
75
+ if (commits.length === 0) {
76
+ return getEmptyImpactAnalysis();
77
+ }
78
+ const now = new Date();
79
+ const fileImpacts = new Map();
80
+ let coreContributions = 0;
81
+ let featureWork = 0;
82
+ let maintenanceWork = 0;
83
+ let documentationWork = 0;
84
+ let totalRawScore = 0;
85
+ let recentScore = 0; // Last 30 days
86
+ let olderScore = 0; // 30-90 days ago
87
+ // If we have file change data from fileHotspotAnalyzer, use it
88
+ if (changedFiles && changedFiles.size > 0) {
89
+ changedFiles.forEach((data, filePath) => {
90
+ const { weight, category } = getFileInfo(filePath);
91
+ const impactScore = data.count * weight;
92
+ fileImpacts.set(filePath, {
93
+ score: impactScore,
94
+ count: data.count,
95
+ lastChanged: data.lastChanged,
96
+ category
97
+ });
98
+ totalRawScore += impactScore;
99
+ // Categorize contributions
100
+ switch (category) {
101
+ case 'core':
102
+ coreContributions += impactScore;
103
+ break;
104
+ case 'test':
105
+ case 'config':
106
+ maintenanceWork += impactScore;
107
+ break;
108
+ case 'docs':
109
+ documentationWork += impactScore;
110
+ break;
111
+ default:
112
+ featureWork += impactScore;
113
+ }
114
+ });
115
+ }
116
+ // Process commits for type-based weighting and recency
117
+ commits.forEach(commit => {
118
+ const commitDate = new Date(commit.date);
119
+ const commitType = getCommitType(commit.message);
120
+ const typeWeight = commitType ? (TYPE_WEIGHTS[commitType] || 1.0) : 1.0;
121
+ const recencyFactor = getRecencyFactor(commitDate, now);
122
+ const daysSinceCommit = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
123
+ const baseScore = typeWeight * recencyFactor;
124
+ if (daysSinceCommit <= 30) {
125
+ recentScore += baseScore;
126
+ }
127
+ else if (daysSinceCommit <= 90) {
128
+ olderScore += baseScore;
129
+ }
130
+ // Additional category scoring based on commit type
131
+ if (commitType === 'feat') {
132
+ featureWork += typeWeight;
133
+ }
134
+ else if (commitType === 'fix' || commitType === 'refactor') {
135
+ maintenanceWork += typeWeight;
136
+ }
137
+ else if (commitType === 'docs') {
138
+ documentationWork += typeWeight;
139
+ }
140
+ });
141
+ // Normalize overall score to 0-100
142
+ const maxPossibleScore = commits.length * 1.5 * 1.0; // Max type weight * max recency
143
+ const normalizedScore = Math.min(100, Math.round((totalRawScore + recentScore) / Math.max(maxPossibleScore, 1) * 100));
144
+ // Get top 10 impact files
145
+ const topImpactFiles = Array.from(fileImpacts.entries())
146
+ .sort((a, b) => b[1].score - a[1].score)
147
+ .slice(0, 10)
148
+ .map(([path, data]) => ({
149
+ path,
150
+ impactScore: Math.round(data.score * 10) / 10,
151
+ changeCount: data.count,
152
+ lastChanged: data.lastChanged,
153
+ category: data.category
154
+ }));
155
+ // Determine trend
156
+ let impactTrend = 'stable';
157
+ if (recentScore > olderScore * 1.2) {
158
+ impactTrend = 'increasing';
159
+ }
160
+ else if (recentScore < olderScore * 0.8) {
161
+ impactTrend = 'decreasing';
162
+ }
163
+ // Generate insights
164
+ const insights = generateInsights({
165
+ coreContributions,
166
+ featureWork,
167
+ maintenanceWork,
168
+ documentationWork,
169
+ topImpactFiles,
170
+ impactTrend,
171
+ totalCommits: commits.length
172
+ });
173
+ // Normalize breakdown scores
174
+ const breakdownTotal = coreContributions + featureWork + maintenanceWork + documentationWork || 1;
175
+ return {
176
+ overallScore: normalizedScore,
177
+ scoreBreakdown: {
178
+ coreContributions: Math.round((coreContributions / breakdownTotal) * 100),
179
+ featureWork: Math.round((featureWork / breakdownTotal) * 100),
180
+ maintenanceWork: Math.round((maintenanceWork / breakdownTotal) * 100),
181
+ documentationWork: Math.round((documentationWork / breakdownTotal) * 100),
182
+ },
183
+ topImpactFiles,
184
+ impactTrend,
185
+ insights
186
+ };
187
+ }
188
+ /**
189
+ * Generate meaningful insights from impact data
190
+ */
191
+ function generateInsights(data) {
192
+ const insights = [];
193
+ const total = data.coreContributions + data.featureWork + data.maintenanceWork + data.documentationWork || 1;
194
+ // Core contributions insight
195
+ const corePercentage = (data.coreContributions / total) * 100;
196
+ if (corePercentage > 30) {
197
+ insights.push(`Strong focus on core modules — ${corePercentage.toFixed(0)}% of impact from critical paths`);
198
+ }
199
+ // Feature work insight
200
+ const featurePercentage = (data.featureWork / total) * 100;
201
+ if (featurePercentage > 50) {
202
+ insights.push(`Feature-focused development — ${featurePercentage.toFixed(0)}% of contributions are new functionality`);
203
+ }
204
+ // Maintenance insight
205
+ const maintenancePercentage = (data.maintenanceWork / total) * 100;
206
+ if (maintenancePercentage > 40) {
207
+ insights.push(`Strong maintenance focus — keeping the codebase healthy`);
208
+ }
209
+ // Documentation insight
210
+ const docPercentage = (data.documentationWork / total) * 100;
211
+ if (docPercentage < 5 && data.totalCommits > 20) {
212
+ insights.push(`Documentation contributions below average consider adding READMEs or inline docs`);
213
+ }
214
+ else if (docPercentage > 15) {
215
+ insights.push(`Good documentation practices — ${docPercentage.toFixed(0)}% of work includes docs`);
216
+ }
217
+ // Trend insight
218
+ if (data.impactTrend === 'increasing') {
219
+ insights.push(`Impact trending upward — recent contributions are higher value`);
220
+ }
221
+ else if (data.impactTrend === 'decreasing') {
222
+ insights.push(`Recent activity lower than historical average`);
223
+ }
224
+ // Top files insight
225
+ if (data.topImpactFiles.length > 0) {
226
+ const topFile = data.topImpactFiles[0];
227
+ insights.push(`Highest impact: ${topFile.path.split('/').pop()} (${topFile.changeCount} changes)`);
228
+ }
229
+ return insights.slice(0, 5); // Limit to 5 insights
230
+ }
231
+ /**
232
+ * Return empty analysis for no commits
233
+ */
234
+ function getEmptyImpactAnalysis() {
235
+ return {
236
+ overallScore: 0,
237
+ scoreBreakdown: {
238
+ coreContributions: 0,
239
+ featureWork: 0,
240
+ maintenanceWork: 0,
241
+ documentationWork: 0,
242
+ },
243
+ topImpactFiles: [],
244
+ impactTrend: 'stable',
245
+ insights: ['No commit data available for analysis']
246
+ };
247
+ }