repo-wrapped 0.0.6 → 0.0.7

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.
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSummaryOnlyHTML = generateSummaryOnlyHTML;
3
4
  exports.generateHTML = generateHTML;
4
5
  const date_fns_1 = require("date-fns");
5
6
  const achievementEngine_1 = require("./achievementEngine");
@@ -18,6 +19,12 @@ const commitQualitySection_1 = require("../generators/html/templates/commitQuali
18
19
  const achievementsSection_1 = require("../generators/html/templates/achievementsSection");
19
20
  const impactSection_1 = require("../generators/html/templates/impactSection");
20
21
  const knowledgeSection_1 = require("../generators/html/templates/knowledgeSection");
22
+ const velocitySection_1 = require("../generators/html/templates/velocitySection");
23
+ const gapSection_1 = require("../generators/html/templates/gapSection");
24
+ const comparisonSection_1 = require("../generators/html/templates/comparisonSection");
25
+ const executiveSummarySection_1 = require("../generators/html/templates/executiveSummarySection");
26
+ const teamSection_1 = require("../generators/html/templates/teamSection");
27
+ const eventsSection_1 = require("../generators/html/templates/eventsSection");
21
28
  const commitMapBuilder_1 = require("../generators/html/utils/commitMapBuilder");
22
29
  const developerStatsCalculator_1 = require("../generators/html/utils/developerStatsCalculator");
23
30
  const dateRangeCalculator_1 = require("../generators/html/utils/dateRangeCalculator");
@@ -69,7 +76,82 @@ function wrapInSection(id, title, content, icon = '') {
69
76
  </section>
70
77
  `;
71
78
  }
72
- function generateHTML(commits, year, monthsToShow, repoPath, repoName, repoUrl, skipBodyCheck = false, allTime = false, deepAnalysis = false) {
79
+ /**
80
+ * Generate a minimal HTML page with only the executive summary
81
+ */
82
+ function generateSummaryOnlyHTML(summary, repoName) {
83
+ return `<!DOCTYPE html>
84
+ <html lang="en">
85
+ <head>
86
+ <meta charset="UTF-8">
87
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
88
+ <title>Executive Summary - ${repoName}</title>
89
+ <style>
90
+ ${(0, styleLoader_1.loadStyles)()}
91
+
92
+ /* Summary-only specific styles */
93
+ body.summary-only {
94
+ background: #0d1117;
95
+ color: #e6edf3;
96
+ min-height: 100vh;
97
+ padding: 2rem;
98
+ }
99
+
100
+ .summary-page {
101
+ max-width: 900px;
102
+ margin: 0 auto;
103
+ }
104
+
105
+ .summary-header {
106
+ text-align: center;
107
+ margin-bottom: 2rem;
108
+ padding-bottom: 1.5rem;
109
+ border-bottom: 1px solid rgba(255,255,255,0.1);
110
+ }
111
+
112
+ .summary-header h1 {
113
+ font-size: 2rem;
114
+ margin-bottom: 0.5rem;
115
+ }
116
+
117
+ .summary-header .period {
118
+ color: #8b949e;
119
+ font-size: 1rem;
120
+ }
121
+
122
+ .summary-header .generated {
123
+ color: #6e7681;
124
+ font-size: 0.875rem;
125
+ margin-top: 0.5rem;
126
+ }
127
+
128
+ @media print {
129
+ body.summary-only {
130
+ background: white;
131
+ color: black;
132
+ padding: 0;
133
+ }
134
+
135
+ .summary-page {
136
+ max-width: 100%;
137
+ }
138
+ }
139
+ </style>
140
+ </head>
141
+ <body class="summary-only">
142
+ <div class="summary-page">
143
+ <header class="summary-header">
144
+ <h1>📊 ${repoName}</h1>
145
+ <p class="period">${(0, date_fns_1.format)(summary.period.start, 'MMM d, yyyy')} – ${(0, date_fns_1.format)(summary.period.end, 'MMM d, yyyy')}</p>
146
+ <p class="generated">Generated ${(0, date_fns_1.format)(summary.generatedAt, 'MMM d, yyyy \'at\' h:mm a')}</p>
147
+ </header>
148
+
149
+ ${(0, executiveSummarySection_1.buildExecutiveSummarySection)(summary)}
150
+ </div>
151
+ </body>
152
+ </html>`;
153
+ }
154
+ function generateHTML(commits, year, monthsToShow, repoPath, repoName, repoUrl, skipBodyCheck = false, allTime = false, deepAnalysis = false, leadDevOptions) {
73
155
  // Get current git user
74
156
  const { getCurrentGitUser } = require('./gitParser');
75
157
  const currentUser = getCurrentGitUser(repoPath);
@@ -126,7 +208,7 @@ function generateHTML(commits, year, monthsToShow, repoPath, repoName, repoUrl,
126
208
  // Check achievements
127
209
  const achievementProgress = (0, achievementEngine_1.checkAchievements)(analysisData);
128
210
  const personalAchievementProgress = (0, achievementEngine_1.checkAchievements)(personalAnalysisData);
129
- return generateHTMLContent(commitMap, commitDetailsMap, developerStats, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, personalCommitMap, personalCommitDetailsMap, personalStreakData, personalTimePattern, personalCommitQuality, personalAchievementProgress, weekStartDate, weekEndDate, startDate, endDate, repoName, repoUrl, currentUser, skipBodyCheck, impactAnalysis, knowledgeDistribution);
211
+ return generateHTMLContent(commitMap, commitDetailsMap, developerStats, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, personalCommitMap, personalCommitDetailsMap, personalStreakData, personalTimePattern, personalCommitQuality, personalAchievementProgress, weekStartDate, weekEndDate, startDate, endDate, repoName, repoUrl, currentUser, skipBodyCheck, impactAnalysis, knowledgeDistribution, leadDevOptions);
130
212
  }
131
213
  function generatePersonalContent(personalCommitMap, personalCommitDetailsMap, streakData, timePattern, commitQuality, achievementProgress, weekStartDate, weekEndDate, dataStartDate, dataEndDate, currentUser, totalCommits, skipBodyCheck = false) {
132
214
  const weeks = (0, weekGrouper_1.groupDaysIntoWeeks)(weekStartDate, weekEndDate);
@@ -160,7 +242,7 @@ function generatePersonalContent(personalCommitMap, personalCommitDetailsMap, st
160
242
  ${wrapInSection('personal-achievements', 'Achievements', achievementsHtml)}
161
243
  `;
162
244
  }
163
- function generateHTMLContent(commitMap, commitDetailsMap, developerStats, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, personalCommitMap, personalCommitDetailsMap, personalStreakData, personalTimePattern, personalCommitQuality, personalAchievementProgress, weekStartDate, weekEndDate, dataStartDate, dataEndDate, repoName, repoUrl, currentUser, skipBodyCheck = false, impactAnalysis, knowledgeDistribution) {
245
+ function generateHTMLContent(commitMap, commitDetailsMap, developerStats, streakData, timePattern, commitQuality, fileHotspots, achievementProgress, personalCommitMap, personalCommitDetailsMap, personalStreakData, personalTimePattern, personalCommitQuality, personalAchievementProgress, weekStartDate, weekEndDate, dataStartDate, dataEndDate, repoName, repoUrl, currentUser, skipBodyCheck = false, impactAnalysis, knowledgeDistribution, leadDevOptions) {
164
246
  const totalPersonalCommits = Array.from(personalCommitMap.values()).reduce((sum, count) => sum + count, 0);
165
247
  const weeks = (0, weekGrouper_1.groupDaysIntoWeeks)(weekStartDate, weekEndDate);
166
248
  // Create dynamic color function based on commit distribution
@@ -312,32 +394,16 @@ function generateHTMLContent(commitMap, commitDetailsMap, developerStats, streak
312
394
  <div class="nav-section">
313
395
  <div class="nav-section-title">Views</div>
314
396
  <a href="#" class="nav-item active" data-tab="overall">
315
- <span class="nav-icon">📊</span>
316
397
  <span class="nav-label">Team Overview</span>
317
398
  </a>
318
399
  <a href="#" class="nav-item" data-tab="personal">
319
- <span class="nav-icon">👤</span>
320
400
  <span class="nav-label">Personal Stats</span>
321
401
  </a>
322
- </div>
323
-
324
- <div class="nav-section">
325
- <div class="nav-section-title">Quick Jump</div>
326
- <a href="#contributions" class="nav-item nav-item-small">
327
- <span class="nav-label">Contributions</span>
328
- </a>
329
- <a href="#personal-streaks" class="nav-item nav-item-small" data-tab="personal">
330
- <span class="nav-label">Streaks</span>
331
- </a>
332
- <a href="#personal-patterns" class="nav-item nav-item-small" data-tab="personal">
333
- <span class="nav-label">Patterns</span>
334
- </a>
335
- <a href="#personal-quality" class="nav-item nav-item-small" data-tab="personal">
336
- <span class="nav-label">Quality</span>
337
- </a>
338
- <a href="#personal-achievements" class="nav-item nav-item-small" data-tab="personal">
339
- <span class="nav-label">Achievements</span>
402
+ ${leadDevOptions?.velocityAnalysis || leadDevOptions?.gapAnalysis || leadDevOptions?.rangeComparison || leadDevOptions?.executiveSummary || leadDevOptions?.teamAnalysis || leadDevOptions?.events?.length ? `
403
+ <a href="#" class="nav-item" data-tab="leaddev">
404
+ <span class="nav-label">Strategic Insights</span>
340
405
  </a>
406
+ ` : ''}
341
407
  </div>
342
408
 
343
409
  <div class="sidebar-footer">
@@ -378,6 +444,7 @@ function generateHTMLContent(commitMap, commitDetailsMap, developerStats, streak
378
444
  <div class="tab-nav">
379
445
  <button class="tab-button active" data-tab="overall">📊 Team Overview</button>
380
446
  <button class="tab-button" data-tab="personal">👤 Personal</button>
447
+ ${leadDevOptions?.velocityAnalysis || leadDevOptions?.gapAnalysis || leadDevOptions?.rangeComparison || leadDevOptions?.executiveSummary || leadDevOptions?.teamAnalysis ? '<button class="tab-button" data-tab="leaddev">📊 Strategic Insights</button>' : ''}
381
448
  </div>
382
449
 
383
450
  <!-- Overall Tab Content -->
@@ -391,6 +458,19 @@ function generateHTMLContent(commitMap, commitDetailsMap, developerStats, streak
391
458
  </div>
392
459
  <!-- End Overall Tab Content -->
393
460
 
461
+ <!-- Lead Developer Tab Content -->
462
+ ${(leadDevOptions?.velocityAnalysis || leadDevOptions?.gapAnalysis || leadDevOptions?.rangeComparison || leadDevOptions?.executiveSummary || leadDevOptions?.teamAnalysis || leadDevOptions?.events?.length) ? `
463
+ <div class="tab-content" id="leaddev-content">
464
+ ${leadDevOptions?.executiveSummary ? wrapInSection('executive-summary', 'Executive Summary', (0, executiveSummarySection_1.buildExecutiveSummarySection)(leadDevOptions.executiveSummary), '📋') : ''}
465
+ ${leadDevOptions?.velocityAnalysis ? wrapInSection('velocity', 'Velocity Timeline', (0, velocitySection_1.buildVelocitySection)(leadDevOptions.velocityAnalysis), '📈') : ''}
466
+ ${leadDevOptions?.gapAnalysis ? wrapInSection('gaps', 'Gap Analysis', (0, gapSection_1.buildGapSection)(leadDevOptions.gapAnalysis), '🚧') : ''}
467
+ ${leadDevOptions?.rangeComparison ? wrapInSection('comparison', 'Period Comparison', (0, comparisonSection_1.buildComparisonSection)(leadDevOptions.rangeComparison), '⚖️') : ''}
468
+ ${leadDevOptions?.teamAnalysis ? wrapInSection('team-analysis', 'Team Analysis', (0, teamSection_1.buildTeamSection)(leadDevOptions.teamAnalysis), '👥') : ''}
469
+ ${leadDevOptions?.events?.length ? wrapInSection('events', 'Event Timeline', (0, eventsSection_1.buildEventsSection)(leadDevOptions.events), '📅') : ''}
470
+ </div>
471
+ ` : ''}
472
+ <!-- End Lead Developer Tab Content -->
473
+
394
474
  <!-- Personal Tab Content -->
395
475
  <div class="tab-content" id="personal-content">
396
476
  ${generatePersonalContent(personalCommitMap, personalCommitDetailsMap, personalStreakData, personalTimePattern, personalCommitQuality, personalAchievementProgress, weekStartDate, weekEndDate, dataStartDate, dataEndDate, currentUser, totalPersonalCommits, skipBodyCheck)}
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compareRanges = compareRanges;
4
+ exports.parseDateRange = parseDateRange;
5
+ exports.formatComparisonInsights = formatComparisonInsights;
6
+ const date_fns_1 = require("date-fns");
7
+ const commitQualityAnalyzer_1 = require("./commitQualityAnalyzer");
8
+ const streakCalculator_1 = require("./streakCalculator");
9
+ /**
10
+ * Compares metrics between two date ranges to show before/after impact
11
+ */
12
+ function compareRanges(commits, range1Start, range1End, range2Start, range2End, label1 = 'Period 1', label2 = 'Period 2', skipBodyCheck = true) {
13
+ // Filter commits for each range
14
+ const range1Commits = filterCommitsByDateRange(commits, range1Start, range1End);
15
+ const range2Commits = filterCommitsByDateRange(commits, range2Start, range2End);
16
+ // Calculate metrics for each range
17
+ const metrics1 = calculateRangeMetrics(range1Commits, range1Start, range1End, skipBodyCheck);
18
+ const metrics2 = calculateRangeMetrics(range2Commits, range2Start, range2End, skipBodyCheck);
19
+ // Calculate changes
20
+ const changes = calculateChanges(metrics1, metrics2);
21
+ // Generate summary narrative
22
+ const summary = generateSummary(label1, label2, metrics1, metrics2, changes);
23
+ return {
24
+ range1: {
25
+ label: label1,
26
+ start: range1Start,
27
+ end: range1End,
28
+ metrics: metrics1,
29
+ },
30
+ range2: {
31
+ label: label2,
32
+ start: range2Start,
33
+ end: range2End,
34
+ metrics: metrics2,
35
+ },
36
+ changes,
37
+ summary,
38
+ };
39
+ }
40
+ function filterCommitsByDateRange(commits, start, end) {
41
+ return commits.filter(commit => {
42
+ const commitDate = new Date(commit.date);
43
+ return commitDate >= start && commitDate <= end;
44
+ });
45
+ }
46
+ function calculateRangeMetrics(commits, startDate, endDate, skipBodyCheck) {
47
+ const totalDays = (0, date_fns_1.differenceInDays)(endDate, startDate) + 1;
48
+ const totalWeeks = Math.max(1, (0, date_fns_1.differenceInWeeks)(endDate, startDate) + 1);
49
+ // Basic counts
50
+ const totalCommits = commits.length;
51
+ const commitsPerWeek = Math.round((totalCommits / totalWeeks) * 10) / 10;
52
+ // Unique authors
53
+ const authors = [...new Set(commits.map(c => c.author))];
54
+ const totalAuthors = authors.length;
55
+ // Active days
56
+ const activeDatesSet = new Set(commits.map(c => {
57
+ const d = new Date(c.date);
58
+ return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
59
+ }));
60
+ const activeDays = activeDatesSet.size;
61
+ const activeDaysPercentage = Math.round((activeDays / totalDays) * 1000) / 10;
62
+ const averageCommitsPerDay = activeDays > 0 ? Math.round((totalCommits / activeDays) * 10) / 10 : 0;
63
+ // Quality score
64
+ const qualityResult = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits, { skipBodyCheck });
65
+ const qualityScore = Math.round(qualityResult.overallScore * 10) / 10;
66
+ // Streak data
67
+ const streakData = (0, streakCalculator_1.calculateStreaks)(commits, startDate, endDate);
68
+ const longestStreak = streakData.longestStreak.days;
69
+ const averageStreak = streakData.streaks.length > 0
70
+ ? Math.round(streakData.streaks.reduce((sum, s) => sum + s.days, 0) / streakData.streaks.length * 10) / 10
71
+ : 0;
72
+ // Commits by type (from conventional commits)
73
+ const commitsByType = {
74
+ feat: 0,
75
+ fix: 0,
76
+ docs: 0,
77
+ refactor: 0,
78
+ test: 0,
79
+ chore: 0,
80
+ other: 0,
81
+ };
82
+ for (const commit of commits) {
83
+ const match = commit.message.match(/^(feat|fix|docs|refactor|test|chore|style|perf|ci|build|revert)(\(.+\))?:/i);
84
+ if (match) {
85
+ const type = match[1].toLowerCase();
86
+ if (type in commitsByType) {
87
+ commitsByType[type]++;
88
+ }
89
+ else {
90
+ commitsByType.other++;
91
+ }
92
+ }
93
+ else {
94
+ commitsByType.other++;
95
+ }
96
+ }
97
+ // Commits by author
98
+ const commitsByAuthor = {};
99
+ for (const commit of commits) {
100
+ commitsByAuthor[commit.author] = (commitsByAuthor[commit.author] || 0) + 1;
101
+ }
102
+ return {
103
+ totalCommits,
104
+ commitsPerWeek,
105
+ totalAuthors,
106
+ activeDays,
107
+ activeDaysPercentage,
108
+ averageCommitsPerDay,
109
+ qualityScore,
110
+ longestStreak,
111
+ averageStreak,
112
+ commitsByType,
113
+ commitsByAuthor,
114
+ };
115
+ }
116
+ function calculateChanges(metrics1, metrics2) {
117
+ return {
118
+ commits: calculateMetricChange(metrics1.totalCommits, metrics2.totalCommits),
119
+ velocity: calculateMetricChange(metrics1.commitsPerWeek, metrics2.commitsPerWeek),
120
+ quality: calculateMetricChange(metrics1.qualityScore, metrics2.qualityScore),
121
+ activeDays: calculateMetricChange(metrics1.activeDays, metrics2.activeDays),
122
+ authors: calculateMetricChange(metrics1.totalAuthors, metrics2.totalAuthors),
123
+ };
124
+ }
125
+ function calculateMetricChange(value1, value2) {
126
+ const absolute = Math.round((value2 - value1) * 10) / 10;
127
+ const percentage = value1 !== 0
128
+ ? Math.round(((value2 - value1) / value1) * 1000) / 10
129
+ : (value2 > 0 ? 100 : 0);
130
+ let trend;
131
+ if (percentage > 5) {
132
+ trend = 'up';
133
+ }
134
+ else if (percentage < -5) {
135
+ trend = 'down';
136
+ }
137
+ else {
138
+ trend = 'stable';
139
+ }
140
+ return { absolute, percentage, trend };
141
+ }
142
+ function generateSummary(label1, label2, metrics1, metrics2, changes) {
143
+ const parts = [];
144
+ // Velocity change
145
+ if (changes.velocity.trend === 'down' && changes.velocity.percentage <= -20) {
146
+ parts.push(`Velocity decreased ${Math.abs(changes.velocity.percentage)}% in ${label2}`);
147
+ }
148
+ else if (changes.velocity.trend === 'up' && changes.velocity.percentage >= 20) {
149
+ parts.push(`Velocity increased ${changes.velocity.percentage}% in ${label2}`);
150
+ }
151
+ // Active days change
152
+ if (changes.activeDays.trend === 'down' && changes.activeDays.percentage <= -20) {
153
+ parts.push(`${Math.abs(changes.activeDays.percentage)}% fewer active days`);
154
+ }
155
+ else if (changes.activeDays.trend === 'up' && changes.activeDays.percentage >= 20) {
156
+ parts.push(`${changes.activeDays.percentage}% more active days`);
157
+ }
158
+ // Quality change
159
+ if (changes.quality.trend === 'down' && changes.quality.percentage <= -15) {
160
+ parts.push(`commit quality dropped ${Math.abs(changes.quality.percentage)}%`);
161
+ }
162
+ else if (changes.quality.trend === 'up' && changes.quality.percentage >= 15) {
163
+ parts.push(`commit quality improved ${changes.quality.percentage}%`);
164
+ }
165
+ // Author change
166
+ if (changes.authors.trend === 'down' && changes.authors.absolute < 0) {
167
+ parts.push(`${Math.abs(changes.authors.absolute)} fewer contributor(s)`);
168
+ }
169
+ else if (changes.authors.trend === 'up' && changes.authors.absolute > 0) {
170
+ parts.push(`${changes.authors.absolute} additional contributor(s)`);
171
+ }
172
+ if (parts.length === 0) {
173
+ return `Metrics remained relatively stable between ${label1} and ${label2}.`;
174
+ }
175
+ return parts.join(', ') + '.';
176
+ }
177
+ /**
178
+ * Parse date range string in format "YYYY-MM-DD..YYYY-MM-DD"
179
+ */
180
+ function parseDateRange(rangeStr) {
181
+ const match = rangeStr.match(/^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/);
182
+ if (!match) {
183
+ return null;
184
+ }
185
+ const start = new Date(match[1]);
186
+ const end = new Date(match[2]);
187
+ if (isNaN(start.getTime()) || isNaN(end.getTime())) {
188
+ return null;
189
+ }
190
+ return { start, end };
191
+ }
192
+ /**
193
+ * Format comparison for display
194
+ */
195
+ function formatComparisonInsights(comparison) {
196
+ const insights = [];
197
+ const { range1, range2, changes } = comparison;
198
+ insights.push(`📊 Comparing: ${range1.label} vs ${range2.label}`);
199
+ insights.push('');
200
+ // Commits
201
+ const commitEmoji = changes.commits.trend === 'up' ? '📈' : changes.commits.trend === 'down' ? '📉' : '➡️';
202
+ insights.push(`${commitEmoji} Commits: ${range1.metrics.totalCommits} → ${range2.metrics.totalCommits} (${formatChange(changes.commits)})`);
203
+ // Velocity
204
+ const velocityEmoji = changes.velocity.trend === 'up' ? '🚀' : changes.velocity.trend === 'down' ? '🐢' : '➡️';
205
+ insights.push(`${velocityEmoji} Velocity: ${range1.metrics.commitsPerWeek} → ${range2.metrics.commitsPerWeek}/week (${formatChange(changes.velocity)})`);
206
+ // Quality
207
+ const qualityEmoji = changes.quality.trend === 'up' ? '✨' : changes.quality.trend === 'down' ? '⚠️' : '➡️';
208
+ insights.push(`${qualityEmoji} Quality: ${range1.metrics.qualityScore} → ${range2.metrics.qualityScore}/10 (${formatChange(changes.quality)})`);
209
+ // Active days
210
+ const activeEmoji = changes.activeDays.trend === 'up' ? '📅' : changes.activeDays.trend === 'down' ? '🗓️' : '➡️';
211
+ insights.push(`${activeEmoji} Active days: ${range1.metrics.activeDays} → ${range2.metrics.activeDays} (${formatChange(changes.activeDays)})`);
212
+ // Contributors
213
+ const authorEmoji = changes.authors.trend === 'up' ? '👥' : changes.authors.trend === 'down' ? '👤' : '➡️';
214
+ insights.push(`${authorEmoji} Contributors: ${range1.metrics.totalAuthors} → ${range2.metrics.totalAuthors} (${formatChange(changes.authors)})`);
215
+ insights.push('');
216
+ insights.push(`💡 ${comparison.summary}`);
217
+ return insights;
218
+ }
219
+ function formatChange(change) {
220
+ const sign = change.percentage >= 0 ? '+' : '';
221
+ return `${sign}${change.percentage}%`;
222
+ }