repo-wrapped 0.0.5 → 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.
@@ -46,6 +46,16 @@ const path = __importStar(require("path"));
46
46
  const gitParser_1 = require("../utils/gitParser");
47
47
  const htmlGenerator_1 = require("../utils/htmlGenerator");
48
48
  const matrixGenerator_1 = require("../utils/matrixGenerator");
49
+ const velocityAnalyzer_1 = require("../utils/velocityAnalyzer");
50
+ const gapAnalyzer_1 = require("../utils/gapAnalyzer");
51
+ const rangeComparisonAnalyzer_1 = require("../utils/rangeComparisonAnalyzer");
52
+ const teamAnalyzer_1 = require("../utils/teamAnalyzer");
53
+ const executiveSummaryGenerator_1 = require("../utils/executiveSummaryGenerator");
54
+ const eventAnnotationParser_1 = require("../utils/eventAnnotationParser");
55
+ const streakCalculator_1 = require("../utils/streakCalculator");
56
+ const timePatternAnalyzer_1 = require("../utils/timePatternAnalyzer");
57
+ const commitQualityAnalyzer_1 = require("../utils/commitQualityAnalyzer");
58
+ const knowledgeDistributionAnalyzer_1 = require("../utils/knowledgeDistributionAnalyzer");
49
59
  async function generateMatrix(repoPath, options) {
50
60
  const spinner = (0, ora_1.default)('Analyzing git repository...').start();
51
61
  try {
@@ -57,19 +67,167 @@ async function generateMatrix(repoPath, options) {
57
67
  process.exit(1);
58
68
  }
59
69
  spinner.text = 'Fetching commit history...';
60
- const commits = (0, gitParser_1.parseGitCommits)(absolutePath);
70
+ let commits = (0, gitParser_1.parseGitCommits)(absolutePath);
61
71
  if (commits.length === 0) {
62
72
  spinner.warn(chalk_1.default.yellow('No commits found in the repository.'));
63
73
  return;
64
74
  }
75
+ // Apply author filtering if specified
76
+ const authorFilter = (0, teamAnalyzer_1.parseAuthorFilter)(options.author, options.authors, options.excludeAuthor, options.excludeAuthors);
77
+ if (authorFilter) {
78
+ spinner.text = 'Filtering commits by author...';
79
+ commits = (0, teamAnalyzer_1.filterCommitsByAuthor)(commits, authorFilter);
80
+ if (commits.length === 0) {
81
+ spinner.warn(chalk_1.default.yellow('No commits found matching the author filter.'));
82
+ return;
83
+ }
84
+ }
85
+ // Load team file if specified
86
+ let teamMembers;
87
+ if (options.team) {
88
+ try {
89
+ teamMembers = (0, teamAnalyzer_1.loadTeamFile)(options.team);
90
+ spinner.text = `Loaded ${teamMembers.length} team members`;
91
+ }
92
+ catch (error) {
93
+ spinner.fail(chalk_1.default.red(error.message));
94
+ process.exit(1);
95
+ }
96
+ }
97
+ // Load events if specified
98
+ let events;
99
+ if (options.events) {
100
+ try {
101
+ const eventsConfig = (0, eventAnnotationParser_1.loadEventsFile)(options.events);
102
+ events = eventsConfig.events;
103
+ spinner.text = `Loaded ${events.length} event annotations`;
104
+ }
105
+ catch (error) {
106
+ spinner.fail(chalk_1.default.red(error.message));
107
+ process.exit(1);
108
+ }
109
+ }
65
110
  spinner.text = 'Generating visualization...';
111
+ // Calculate date range
112
+ const commitDates = commits.map(c => new Date(c.date));
113
+ const dataStartDate = new Date(Math.min(...commitDates.map(d => d.getTime())));
114
+ const dataEndDate = new Date(Math.max(...commitDates.map(d => d.getTime())));
115
+ const skipBodyCheck = !options.bodyCheck;
116
+ const repoName = (0, gitParser_1.getRepositoryName)(absolutePath);
117
+ // Handle --summary-only mode (implies --executive-summary)
118
+ if (options.summaryOnly) {
119
+ const deepAnalysis = options.deepAnalysis ?? false;
120
+ const windowWeeks = options.velocityWindow ? parseInt(options.velocityWindow) : 4;
121
+ const gapThreshold = options.gapThreshold ? parseInt(options.gapThreshold) : 3;
122
+ const streaks = (0, streakCalculator_1.calculateStreaks)(commits, dataStartDate, dataEndDate);
123
+ const timePatterns = (0, timePatternAnalyzer_1.analyzeTimePatterns)(commits, dataStartDate, dataEndDate);
124
+ const quality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits, { skipBodyCheck });
125
+ const knowledge = (0, knowledgeDistributionAnalyzer_1.analyzeKnowledgeDistribution)(commits, absolutePath, deepAnalysis);
126
+ const velocityAnalysis = (0, velocityAnalyzer_1.analyzeVelocity)(commits, dataStartDate, dataEndDate, windowWeeks);
127
+ const gapAnalysis = (0, gapAnalyzer_1.analyzeGaps)(commits, dataStartDate, dataEndDate, gapThreshold);
128
+ const summary = (0, executiveSummaryGenerator_1.generateExecutiveSummary)({
129
+ commits,
130
+ repoName,
131
+ streakData: streaks,
132
+ timePattern: timePatterns,
133
+ commitQuality: quality,
134
+ velocityAnalysis,
135
+ gapAnalysis,
136
+ knowledgeDistribution: knowledge,
137
+ startDate: dataStartDate,
138
+ endDate: dataEndDate
139
+ });
140
+ if (options.html) {
141
+ // Generate summary-only HTML
142
+ const html = (0, htmlGenerator_1.generateSummaryOnlyHTML)(summary, repoName);
143
+ const outputPath = path.join(os.tmpdir(), `executive-summary-${Date.now()}.html`);
144
+ fs.writeFileSync(outputPath, html);
145
+ spinner.succeed(chalk_1.default.green('Executive Summary generated!'));
146
+ console.log();
147
+ console.log(chalk_1.default.cyan('Opening in browser...'));
148
+ console.log(chalk_1.default.dim(`File: ${outputPath}`));
149
+ const command = process.platform === 'win32'
150
+ ? `start "" "${outputPath}"`
151
+ : process.platform === 'darwin'
152
+ ? `open "${outputPath}"`
153
+ : `xdg-open "${outputPath}"`;
154
+ (0, child_process_1.exec)(command);
155
+ }
156
+ else {
157
+ // Terminal-only executive summary
158
+ spinner.succeed(chalk_1.default.green('Executive Summary'));
159
+ console.log();
160
+ for (const line of (0, executiveSummaryGenerator_1.formatExecutiveSummary)(summary)) {
161
+ console.log(line);
162
+ }
163
+ }
164
+ return;
165
+ }
166
+ // Check if we need terminal-only lead dev insights (no HTML)
167
+ const needsLeadDevInsights = options.velocity || options.gapAnalysis ||
168
+ options.compare || options.executiveSummary || teamMembers || events;
66
169
  if (options.html) {
67
170
  // Generate HTML output
68
171
  const repoName = (0, gitParser_1.getRepositoryName)(absolutePath);
69
172
  const repoUrl = (0, gitParser_1.getRepositoryUrl)(absolutePath);
70
- const skipBodyCheck = !options.bodyCheck;
71
173
  const deepAnalysis = options.deepAnalysis ?? false;
72
- const html = (0, htmlGenerator_1.generateHTML)(commits, year, monthsToShow, absolutePath, repoName, repoUrl, skipBodyCheck, options.all, deepAnalysis);
174
+ // Build lead dev options if requested
175
+ let leadDevOptions;
176
+ if (options.velocity || options.gapAnalysis || options.compare || options.executiveSummary || teamMembers || events) {
177
+ leadDevOptions = {};
178
+ const windowWeeks = options.velocityWindow ? parseInt(options.velocityWindow) : 4;
179
+ const gapThreshold = options.gapThreshold ? parseInt(options.gapThreshold) : 3;
180
+ if (options.velocity) {
181
+ leadDevOptions.velocityAnalysis = (0, velocityAnalyzer_1.analyzeVelocity)(commits, dataStartDate, dataEndDate, windowWeeks);
182
+ }
183
+ if (options.gapAnalysis) {
184
+ leadDevOptions.gapAnalysis = (0, gapAnalyzer_1.analyzeGaps)(commits, dataStartDate, dataEndDate, gapThreshold);
185
+ }
186
+ if (options.compare && options.compare.length >= 2) {
187
+ const range1 = (0, rangeComparisonAnalyzer_1.parseDateRange)(options.compare[0]);
188
+ const range2 = (0, rangeComparisonAnalyzer_1.parseDateRange)(options.compare[1]);
189
+ if (range1 && range2) {
190
+ const labelsList = options.compareLabels
191
+ ? options.compareLabels.split(',').map(l => l.trim())
192
+ : ['Period 1', 'Period 2'];
193
+ leadDevOptions.rangeComparison = (0, rangeComparisonAnalyzer_1.compareRanges)(commits, range1.start, range1.end, range2.start, range2.end, labelsList[0], labelsList[1] || 'Period 2', skipBodyCheck);
194
+ }
195
+ }
196
+ if (teamMembers && teamMembers.length > 0) {
197
+ leadDevOptions.teamAnalysis = (0, teamAnalyzer_1.analyzeTeam)(commits, teamMembers, undefined, skipBodyCheck);
198
+ }
199
+ // Add events with correlations
200
+ if (events && events.length > 0) {
201
+ leadDevOptions.events = (0, eventAnnotationParser_1.correlateEventsWithMetrics)(events, commits, 14, skipBodyCheck);
202
+ }
203
+ if (options.executiveSummary) {
204
+ const streaks = (0, streakCalculator_1.calculateStreaks)(commits, dataStartDate, dataEndDate);
205
+ const timePatterns = (0, timePatternAnalyzer_1.analyzeTimePatterns)(commits, dataStartDate, dataEndDate);
206
+ const quality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits, { skipBodyCheck });
207
+ const knowledge = (0, knowledgeDistributionAnalyzer_1.analyzeKnowledgeDistribution)(commits, absolutePath, deepAnalysis);
208
+ let velocityForSummary = leadDevOptions.velocityAnalysis;
209
+ if (!velocityForSummary) {
210
+ velocityForSummary = (0, velocityAnalyzer_1.analyzeVelocity)(commits, dataStartDate, dataEndDate, windowWeeks);
211
+ }
212
+ let gapForSummary = leadDevOptions.gapAnalysis;
213
+ if (!gapForSummary) {
214
+ gapForSummary = (0, gapAnalyzer_1.analyzeGaps)(commits, dataStartDate, dataEndDate, gapThreshold);
215
+ }
216
+ leadDevOptions.executiveSummary = (0, executiveSummaryGenerator_1.generateExecutiveSummary)({
217
+ commits,
218
+ repoName,
219
+ streakData: streaks,
220
+ timePattern: timePatterns,
221
+ commitQuality: quality,
222
+ velocityAnalysis: velocityForSummary,
223
+ gapAnalysis: gapForSummary,
224
+ knowledgeDistribution: knowledge,
225
+ startDate: dataStartDate,
226
+ endDate: dataEndDate
227
+ });
228
+ }
229
+ }
230
+ const html = (0, htmlGenerator_1.generateHTML)(commits, year, monthsToShow, absolutePath, repoName, repoUrl, skipBodyCheck, options.all, deepAnalysis, leadDevOptions);
73
231
  const outputPath = path.join(os.tmpdir(), `git-wrapped-${Date.now()}.html`);
74
232
  fs.writeFileSync(outputPath, html);
75
233
  spinner.succeed(chalk_1.default.green('HTML file generated successfully!'));
@@ -84,8 +242,105 @@ async function generateMatrix(repoPath, options) {
84
242
  : `xdg-open "${outputPath}"`;
85
243
  (0, child_process_1.exec)(command);
86
244
  }
245
+ else if (needsLeadDevInsights) {
246
+ // Generate lead developer insights in terminal
247
+ spinner.succeed(chalk_1.default.green('āœ“ Analysis complete!'));
248
+ console.log();
249
+ // Range comparison
250
+ if (options.compare && options.compare.length >= 2) {
251
+ const range1 = (0, rangeComparisonAnalyzer_1.parseDateRange)(options.compare[0]);
252
+ const range2 = (0, rangeComparisonAnalyzer_1.parseDateRange)(options.compare[1]);
253
+ if (!range1 || !range2) {
254
+ console.log(chalk_1.default.red('Invalid date range format. Use: YYYY-MM-DD..YYYY-MM-DD'));
255
+ }
256
+ else {
257
+ const labelsList = options.compareLabels
258
+ ? options.compareLabels.split(',').map(l => l.trim())
259
+ : ['Period 1', 'Period 2'];
260
+ const comparison = (0, rangeComparisonAnalyzer_1.compareRanges)(commits, range1.start, range1.end, range2.start, range2.end, labelsList[0], labelsList[1] || 'Period 2', skipBodyCheck);
261
+ console.log(chalk_1.default.bold.cyan('\nšŸ“Š RANGE COMPARISON'));
262
+ console.log(chalk_1.default.dim('─'.repeat(60)));
263
+ for (const line of (0, rangeComparisonAnalyzer_1.formatComparisonInsights)(comparison)) {
264
+ console.log(line);
265
+ }
266
+ }
267
+ }
268
+ // Velocity analysis
269
+ if (options.velocity) {
270
+ const velocityWindow = parseInt(options.velocityWindow || '4');
271
+ const velocity = (0, velocityAnalyzer_1.analyzeVelocity)(commits, dataStartDate, dataEndDate, velocityWindow);
272
+ console.log(chalk_1.default.bold.cyan('\nšŸš€ VELOCITY ANALYSIS'));
273
+ console.log(chalk_1.default.dim('─'.repeat(60)));
274
+ for (const line of (0, velocityAnalyzer_1.formatVelocityInsights)(velocity)) {
275
+ console.log(line);
276
+ }
277
+ }
278
+ // Gap analysis
279
+ if (options.gapAnalysis) {
280
+ const gapThreshold = parseInt(options.gapThreshold || '3');
281
+ const gaps = (0, gapAnalyzer_1.analyzeGaps)(commits, dataStartDate, dataEndDate, gapThreshold);
282
+ console.log(chalk_1.default.bold.cyan('\nšŸ•³ļø GAP ANALYSIS'));
283
+ console.log(chalk_1.default.dim('─'.repeat(60)));
284
+ for (const line of (0, gapAnalyzer_1.formatGapInsights)(gaps)) {
285
+ console.log(line);
286
+ }
287
+ }
288
+ // Team analysis
289
+ if (teamMembers) {
290
+ const teamAnalysis = (0, teamAnalyzer_1.analyzeTeam)(commits, teamMembers, undefined, skipBodyCheck);
291
+ console.log(chalk_1.default.bold.cyan('\nšŸ‘„ TEAM ANALYSIS'));
292
+ console.log(chalk_1.default.dim('─'.repeat(60)));
293
+ for (const line of (0, teamAnalyzer_1.formatTeamInsights)(teamAnalysis)) {
294
+ console.log(line);
295
+ }
296
+ }
297
+ // Events analysis
298
+ if (events) {
299
+ const correlatedEvents = (0, eventAnnotationParser_1.correlateEventsWithMetrics)(events, commits, 14, skipBodyCheck);
300
+ console.log(chalk_1.default.bold.cyan('\nšŸ“… EVENT ANNOTATIONS'));
301
+ console.log(chalk_1.default.dim('─'.repeat(60)));
302
+ for (const line of (0, eventAnnotationParser_1.formatEventsInsights)(correlatedEvents)) {
303
+ console.log(line);
304
+ }
305
+ }
306
+ // Executive summary (last, as it summarizes everything)
307
+ if (options.executiveSummary) {
308
+ const repoName = (0, gitParser_1.getRepositoryName)(absolutePath);
309
+ const streakData = (0, streakCalculator_1.calculateStreaks)(commits, dataStartDate, dataEndDate);
310
+ const timePattern = (0, timePatternAnalyzer_1.analyzeTimePatterns)(commits, dataStartDate, dataEndDate);
311
+ const commitQuality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits, { skipBodyCheck });
312
+ const knowledgeDistribution = (0, knowledgeDistributionAnalyzer_1.analyzeKnowledgeDistribution)(commits, absolutePath, false);
313
+ let velocity, gaps;
314
+ if (options.velocity) {
315
+ const velocityWindow = parseInt(options.velocityWindow || '4');
316
+ velocity = (0, velocityAnalyzer_1.analyzeVelocity)(commits, dataStartDate, dataEndDate, velocityWindow);
317
+ }
318
+ if (options.gapAnalysis) {
319
+ const gapThreshold = parseInt(options.gapThreshold || '3');
320
+ gaps = (0, gapAnalyzer_1.analyzeGaps)(commits, dataStartDate, dataEndDate, gapThreshold);
321
+ }
322
+ const summary = (0, executiveSummaryGenerator_1.generateExecutiveSummary)({
323
+ commits,
324
+ startDate: dataStartDate,
325
+ endDate: dataEndDate,
326
+ repoName,
327
+ streakData,
328
+ timePattern,
329
+ commitQuality,
330
+ knowledgeDistribution,
331
+ velocityAnalysis: velocity,
332
+ gapAnalysis: gaps,
333
+ });
334
+ console.log(chalk_1.default.bold.cyan('\n'));
335
+ for (const line of (0, executiveSummaryGenerator_1.formatExecutiveSummary)(summary)) {
336
+ console.log(line);
337
+ }
338
+ }
339
+ console.log();
340
+ console.log(chalk_1.default.cyan('šŸ’” Tip:'), chalk_1.default.dim('Add'), chalk_1.default.white('--html'), chalk_1.default.dim('for interactive HTML report'));
341
+ }
87
342
  else {
88
- // Generate terminal output
343
+ // Generate terminal output (default)
89
344
  const repoName = (0, gitParser_1.getRepositoryName)(absolutePath);
90
345
  const repoUrl = (0, gitParser_1.getRepositoryUrl)(absolutePath);
91
346
  const matrix = (0, matrixGenerator_1.generateCommitMatrix)(commits, year, monthsToShow, absolutePath, repoName, repoUrl);
@@ -93,7 +348,9 @@ async function generateMatrix(repoPath, options) {
93
348
  console.log(matrix);
94
349
  console.log();
95
350
  console.log(chalk_1.default.cyan('šŸ’” Tip:'), chalk_1.default.dim('Run with'), chalk_1.default.white('--html'), chalk_1.default.dim('to see interactive details and per-developer analytics'));
96
- console.log(chalk_1.default.dim(' Example:'), chalk_1.default.white(`git-wrapped generate ${repoPath} --html`));
351
+ console.log(chalk_1.default.dim(' Example:'), chalk_1.default.white(`repo-wrapped generate ${repoPath} --html`));
352
+ console.log();
353
+ console.log(chalk_1.default.cyan('šŸ“Š Lead Dev:'), chalk_1.default.dim('Try'), chalk_1.default.white('--velocity --gap-analysis --executive-summary'), chalk_1.default.dim('for management insights'));
97
354
  }
98
355
  }
99
356
  catch (error) {
@@ -130,7 +130,7 @@ body {
130
130
  border: 1px solid var(--border-default);
131
131
  border-radius: var(--radius-lg);
132
132
  padding: var(--spacing-lg);
133
- max-width: fit-content;
133
+ min-width: 70vw;
134
134
  box-shadow: var(--shadow-md);
135
135
  }
136
136
 
@@ -547,7 +547,6 @@ h3 {
547
547
  .sidebar-header {
548
548
  padding: var(--spacing-md);
549
549
  margin-bottom: var(--spacing-md);
550
- border-bottom: 1px solid var(--border-default);
551
550
  display: flex;
552
551
  align-items: center;
553
552
  justify-content: space-between;
@@ -662,7 +661,7 @@ h3 {
662
661
  /* === Main Content === */
663
662
  .main-content {
664
663
  flex: 1;
665
- min-width: 0;
664
+ min-width: 70vw;
666
665
  }
667
666
 
668
667
  /* === Dashboard Section (Collapsible) === */