repo-wrapped 0.0.6 → 0.0.9
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/.github/agents/complete.agent.md +257 -0
- package/.github/agents/feature-scaffold.agent.md +248 -0
- package/.github/agents/jsdoc.agent.md +243 -0
- package/.github/agents/plan.agent.md +202 -0
- package/.github/agents/spec-writer.agent.md +169 -0
- package/.github/agents/test-writer.agent.md +169 -0
- package/.stylelintrc.json +27 -0
- package/README.md +94 -94
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +446 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +446 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +7039 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/commands/generate.js +262 -5
- package/dist/config/defaults.js +158 -0
- package/dist/config/index.js +10 -0
- package/dist/features/achievements/data/achievements.json +284 -0
- package/dist/features/achievements/engine.js +140 -0
- package/dist/features/achievements/evaluators.js +246 -0
- package/dist/features/achievements/helpers.js +58 -0
- package/dist/features/achievements/index.js +57 -0
- package/dist/features/achievements/loader.js +88 -0
- package/dist/features/achievements/template.js +155 -0
- package/dist/features/achievements/types.js +7 -0
- package/dist/features/commit-quality/analyzer.js +378 -0
- package/dist/features/commit-quality/analyzer.test.js +484 -0
- package/dist/features/commit-quality/index.js +28 -0
- package/dist/features/commit-quality/template.js +114 -0
- package/dist/features/commit-quality/types.js +2 -0
- package/dist/features/comparison/analyzer.js +222 -0
- package/dist/features/comparison/index.js +28 -0
- package/dist/features/comparison/template.js +119 -0
- package/dist/features/comparison/types.js +2 -0
- package/dist/features/contribution-graph/index.js +9 -0
- package/dist/features/contribution-graph/template.js +89 -0
- package/dist/features/events/index.js +31 -0
- package/dist/features/events/parser.js +253 -0
- package/dist/features/events/template.js +113 -0
- package/dist/features/events/types.js +2 -0
- package/dist/features/executive-summary/generator.js +275 -0
- package/dist/features/executive-summary/index.js +27 -0
- package/dist/features/executive-summary/template.js +80 -0
- package/dist/features/executive-summary/types.js +2 -0
- package/dist/features/gaps/analyzer.js +298 -0
- package/dist/features/gaps/analyzer.test.js +517 -0
- package/dist/features/gaps/index.js +27 -0
- package/dist/features/gaps/template.js +190 -0
- package/dist/features/gaps/types.js +2 -0
- package/dist/features/impact/analyzer.js +248 -0
- package/dist/features/impact/index.js +26 -0
- package/dist/features/impact/template.js +118 -0
- package/dist/features/impact/types.js +2 -0
- package/dist/features/index.js +40 -0
- package/dist/features/knowledge/analyzer.js +385 -0
- package/dist/features/knowledge/index.js +26 -0
- package/dist/features/knowledge/template.js +239 -0
- package/dist/features/knowledge/types.js +2 -0
- package/dist/features/streaks/calculator.js +184 -0
- package/dist/features/streaks/calculator.test.js +366 -0
- package/dist/features/streaks/index.js +36 -0
- package/dist/features/streaks/template.js +41 -0
- package/dist/features/streaks/types.js +9 -0
- package/dist/features/team/analyzer.js +316 -0
- package/dist/features/team/index.js +30 -0
- package/dist/features/team/template.js +146 -0
- package/dist/features/team/types.js +2 -0
- package/dist/features/time-patterns/analyzer.js +319 -0
- package/dist/features/time-patterns/analyzer.test.js +278 -0
- package/dist/features/time-patterns/index.js +37 -0
- package/dist/features/time-patterns/template.js +109 -0
- package/dist/features/time-patterns/types.js +9 -0
- package/dist/features/velocity/analyzer.js +257 -0
- package/dist/features/velocity/analyzer.test.js +383 -0
- package/dist/features/velocity/index.js +27 -0
- package/dist/features/velocity/template.js +189 -0
- package/dist/features/velocity/types.js +2 -0
- package/dist/generators/html/scripts/knowledge.js +17 -0
- package/dist/generators/html/styles/base.css +10 -6
- package/dist/generators/html/styles/components.css +121 -1
- package/dist/generators/html/styles/knowledge.css +21 -0
- package/dist/generators/html/styles/leaddev.css +1335 -0
- package/dist/generators/html/styles/strategic-insights.css +1337 -0
- package/dist/generators/html/templates/commitQualitySection.js +28 -2
- package/dist/generators/html/templates/comparisonSection.js +119 -0
- package/dist/generators/html/templates/eventsSection.js +113 -0
- package/dist/generators/html/templates/executiveSummarySection.js +80 -0
- package/dist/generators/html/templates/gapSection.js +190 -0
- package/dist/generators/html/templates/impactSection.js +8 -6
- package/dist/generators/html/templates/knowledgeSection.js +16 -2
- package/dist/generators/html/templates/teamSection.js +146 -0
- package/dist/generators/html/templates/velocitySection.js +189 -0
- package/dist/generators/html/types.js +7 -0
- package/dist/generators/html/utils/analysisRunner.js +93 -0
- package/dist/generators/html/utils/cardBuilder.js +47 -0
- package/dist/generators/html/utils/contextBuilder.js +54 -0
- package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
- package/dist/generators/html/utils/kpiBuilder.js +76 -0
- package/dist/generators/html/utils/sectionWrapper.js +71 -0
- package/dist/generators/html/utils/styleLoader.js +2 -1
- package/dist/html/analysisRunner.js +93 -0
- package/dist/html/htmlDocumentBuilder.js +396 -0
- package/dist/html/index.js +29 -0
- package/dist/html/shared/colorUtils.js +61 -0
- package/dist/html/shared/commitMapBuilder.js +23 -0
- package/dist/html/shared/components/cardBuilder.js +47 -0
- package/dist/html/shared/components/index.js +18 -0
- package/dist/html/shared/components/kpiBuilder.js +76 -0
- package/dist/html/shared/components/sectionWrapper.js +71 -0
- package/dist/html/shared/contextBuilder.js +54 -0
- package/dist/html/shared/dateRangeCalculator.js +56 -0
- package/dist/html/shared/developerStatsCalculator.js +28 -0
- package/dist/html/shared/index.js +39 -0
- package/dist/html/shared/scriptLoader.js +15 -0
- package/dist/html/shared/scripts/export.js +125 -0
- package/dist/html/shared/scripts/knowledge.js +137 -0
- package/dist/html/shared/scripts/modal.js +68 -0
- package/dist/html/shared/scripts/navigation.js +156 -0
- package/dist/html/shared/scripts/tabs.js +18 -0
- package/dist/html/shared/scripts/tooltip.js +21 -0
- package/dist/html/shared/styleLoader.js +18 -0
- package/dist/html/shared/styles/achievements.css +387 -0
- package/dist/html/shared/styles/base.css +822 -0
- package/dist/html/shared/styles/components.css +1511 -0
- package/dist/html/shared/styles/knowledge.css +242 -0
- package/dist/html/shared/styles/strategic-insights.css +1337 -0
- package/dist/html/shared/weekGrouper.js +27 -0
- package/dist/html/types.js +7 -0
- package/dist/index.js +54 -21
- package/dist/test/helpers/commitFactory.js +166 -0
- package/dist/test/helpers/dateUtils.js +101 -0
- package/dist/test/helpers/index.js +29 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/smoke.test.js +94 -0
- package/dist/types/achievements.js +7 -0
- package/dist/types/analysis.js +7 -0
- package/dist/types/core.js +7 -0
- package/dist/types/index.js +38 -0
- package/dist/types/options.js +7 -0
- package/dist/types/shared.js +7 -0
- package/dist/types/strategic.js +7 -0
- package/dist/types/summary.js +7 -0
- package/dist/utils/achievementDefinitions.js +22 -22
- package/dist/utils/analyzerContextBuilder.js +124 -0
- package/dist/utils/commitQualityAnalyzer.js +13 -2
- package/dist/utils/emptyResults.js +95 -0
- package/dist/utils/eventAnnotationParser.js +253 -0
- package/dist/utils/executiveSummaryGenerator.js +275 -0
- package/dist/utils/fileHotspotAnalyzer.js +4 -12
- package/dist/utils/gapAnalyzer.js +298 -0
- package/dist/utils/gitParser.test.js +363 -0
- package/dist/utils/htmlGenerator.js +126 -450
- package/dist/utils/impactAnalyzer.js +20 -19
- package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
- package/dist/utils/matrixGenerator.js +13 -13
- package/dist/utils/rangeComparisonAnalyzer.js +222 -0
- package/dist/utils/streakCalculator.js +77 -27
- package/dist/utils/teamAnalyzer.js +316 -0
- package/dist/utils/timePatternAnalyzer.js +18 -3
- package/dist/utils/velocityAnalyzer.js +257 -0
- package/dist/utils/wrappedGenerator.js +8 -8
- package/package.json +74 -55
- package/vitest.config.ts +46 -0
- package/dist/cli.js +0 -24
- package/dist/commands/index.js +0 -24
|
@@ -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 commit_quality_1 = require("../commit-quality");
|
|
8
|
+
const streaks_1 = require("../streaks");
|
|
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, commit_quality_1.analyzeCommitQuality)(commits, { skipBodyCheck });
|
|
65
|
+
const qualityScore = Math.round(qualityResult.overallScore * 10) / 10;
|
|
66
|
+
// Streak data
|
|
67
|
+
const streakData = (0, streaks_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
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.buildComparisonSection = exports.formatComparisonInsights = exports.parseDateRange = exports.compareRanges = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* Comparison Feature
|
|
20
|
+
* @module features/comparison
|
|
21
|
+
*/
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
var analyzer_1 = require("./analyzer");
|
|
24
|
+
Object.defineProperty(exports, "compareRanges", { enumerable: true, get: function () { return analyzer_1.compareRanges; } });
|
|
25
|
+
Object.defineProperty(exports, "parseDateRange", { enumerable: true, get: function () { return analyzer_1.parseDateRange; } });
|
|
26
|
+
Object.defineProperty(exports, "formatComparisonInsights", { enumerable: true, get: function () { return analyzer_1.formatComparisonInsights; } });
|
|
27
|
+
var template_1 = require("./template");
|
|
28
|
+
Object.defineProperty(exports, "buildComparisonSection", { enumerable: true, get: function () { return template_1.buildComparisonSection; } });
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildComparisonSection = buildComparisonSection;
|
|
4
|
+
const date_fns_1 = require("date-fns");
|
|
5
|
+
function buildComparisonSection(comparison) {
|
|
6
|
+
const { range1, range2, changes, summary } = comparison;
|
|
7
|
+
return `
|
|
8
|
+
<div class="comparison-section">
|
|
9
|
+
<h2>📊 Period Comparison</h2>
|
|
10
|
+
|
|
11
|
+
<div class="comparison-header">
|
|
12
|
+
<div class="period-label period-1">
|
|
13
|
+
<span class="period-name">${range1.label}</span>
|
|
14
|
+
<span class="period-dates">${(0, date_fns_1.format)(range1.start, 'MMM d, yyyy')} - ${(0, date_fns_1.format)(range1.end, 'MMM d, yyyy')}</span>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="vs-divider">VS</div>
|
|
17
|
+
<div class="period-label period-2">
|
|
18
|
+
<span class="period-name">${range2.label}</span>
|
|
19
|
+
<span class="period-dates">${(0, date_fns_1.format)(range2.start, 'MMM d, yyyy')} - ${(0, date_fns_1.format)(range2.end, 'MMM d, yyyy')}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="comparison-summary">
|
|
24
|
+
<p>${summary}</p>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="comparison-grid">
|
|
28
|
+
${buildComparisonCard('Commits', range1.metrics.totalCommits, range2.metrics.totalCommits, changes.commits, '📝')}
|
|
29
|
+
${buildComparisonCard('Velocity', range1.metrics.commitsPerWeek, range2.metrics.commitsPerWeek, changes.velocity, '🚀', '/week')}
|
|
30
|
+
${buildComparisonCard('Quality', range1.metrics.qualityScore, range2.metrics.qualityScore, changes.quality, '✨', '/10')}
|
|
31
|
+
${buildComparisonCard('Active Days', range1.metrics.activeDays, range2.metrics.activeDays, changes.activeDays, '📅')}
|
|
32
|
+
${buildComparisonCard('Contributors', range1.metrics.totalAuthors, range2.metrics.totalAuthors, changes.authors, '👥')}
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
${buildTypeDistributionComparison(range1, range2)}
|
|
36
|
+
</div>
|
|
37
|
+
`;
|
|
38
|
+
}
|
|
39
|
+
function buildComparisonCard(label, value1, value2, change, emoji, suffix = '') {
|
|
40
|
+
const trendClass = change.trend === 'up' ? 'positive' : change.trend === 'down' ? 'negative' : 'neutral';
|
|
41
|
+
const trendArrow = change.trend === 'up' ? '↑' : change.trend === 'down' ? '↓' : '→';
|
|
42
|
+
const sign = change.percentage >= 0 ? '+' : '';
|
|
43
|
+
return `
|
|
44
|
+
<div class="comparison-card">
|
|
45
|
+
<div class="card-header">
|
|
46
|
+
<span class="card-emoji">${emoji}</span>
|
|
47
|
+
<span class="card-label">${label}</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="card-values">
|
|
50
|
+
<div class="value-box period-1">
|
|
51
|
+
<span class="value">${value1}${suffix}</span>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="change-indicator ${trendClass}">
|
|
54
|
+
<span class="change-arrow">${trendArrow}</span>
|
|
55
|
+
<span class="change-percent">${sign}${change.percentage}%</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="value-box period-2">
|
|
58
|
+
<span class="value">${value2}${suffix}</span>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
function buildTypeDistributionComparison(range1, range2) {
|
|
65
|
+
const types = ['feat', 'fix', 'docs', 'refactor', 'test', 'chore', 'other'];
|
|
66
|
+
const typeLabels = {
|
|
67
|
+
feat: 'Features',
|
|
68
|
+
fix: 'Fixes',
|
|
69
|
+
docs: 'Docs',
|
|
70
|
+
refactor: 'Refactor',
|
|
71
|
+
test: 'Tests',
|
|
72
|
+
chore: 'Chores',
|
|
73
|
+
other: 'Other',
|
|
74
|
+
};
|
|
75
|
+
const typeColors = {
|
|
76
|
+
feat: '#69db7c',
|
|
77
|
+
fix: '#ff6b6b',
|
|
78
|
+
docs: '#4dabf7',
|
|
79
|
+
refactor: '#ffa94d',
|
|
80
|
+
test: '#da77f2',
|
|
81
|
+
chore: '#868e96',
|
|
82
|
+
other: '#495057',
|
|
83
|
+
};
|
|
84
|
+
const total1 = Object.values(range1.metrics.commitsByType).reduce((a, b) => a + b, 0) || 1;
|
|
85
|
+
const total2 = Object.values(range2.metrics.commitsByType).reduce((a, b) => a + b, 0) || 1;
|
|
86
|
+
const bars = types.map(type => {
|
|
87
|
+
const count1 = range1.metrics.commitsByType[type] || 0;
|
|
88
|
+
const count2 = range2.metrics.commitsByType[type] || 0;
|
|
89
|
+
const pct1 = Math.round((count1 / total1) * 100);
|
|
90
|
+
const pct2 = Math.round((count2 / total2) * 100);
|
|
91
|
+
return `
|
|
92
|
+
<div class="type-row">
|
|
93
|
+
<div class="type-label" style="color: ${typeColors[type]}">${typeLabels[type]}</div>
|
|
94
|
+
<div class="type-bars">
|
|
95
|
+
<div class="bar-container period-1">
|
|
96
|
+
<div class="bar" style="width: ${pct1}%; background: ${typeColors[type]}"></div>
|
|
97
|
+
<span class="bar-value">${count1} (${pct1}%)</span>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="bar-container period-2">
|
|
100
|
+
<div class="bar" style="width: ${pct2}%; background: ${typeColors[type]}"></div>
|
|
101
|
+
<span class="bar-value">${count2} (${pct2}%)</span>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
`;
|
|
106
|
+
}).join('');
|
|
107
|
+
return `
|
|
108
|
+
<div class="type-distribution-comparison">
|
|
109
|
+
<h3>Commit Type Distribution</h3>
|
|
110
|
+
<div class="distribution-legend">
|
|
111
|
+
<span class="legend-item period-1">${range1.label}</span>
|
|
112
|
+
<span class="legend-item period-2">${range2.label}</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="type-bars-container">
|
|
115
|
+
${bars}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildContributionGraph = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Contribution Graph Feature
|
|
6
|
+
* @module features/contribution-graph
|
|
7
|
+
*/
|
|
8
|
+
var template_1 = require("./template");
|
|
9
|
+
Object.defineProperty(exports, "buildContributionGraph", { enumerable: true, get: function () { return template_1.buildContributionGraph; } });
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildContributionGraph = buildContributionGraph;
|
|
4
|
+
const date_fns_1 = require("date-fns");
|
|
5
|
+
/**
|
|
6
|
+
* Sanitize commit data for safe HTML attribute embedding
|
|
7
|
+
*/
|
|
8
|
+
function sanitizeCommitsForHtml(commits) {
|
|
9
|
+
return commits.map(c => ({
|
|
10
|
+
...c,
|
|
11
|
+
message: c.message
|
|
12
|
+
.replace(/[\r\n]+/g, ' ') // Replace newlines with space
|
|
13
|
+
.replace(/'/g, ''') // Escape single quotes
|
|
14
|
+
.replace(/"/g, '"') // Escape double quotes
|
|
15
|
+
.replace(/</g, '<') // Escape HTML
|
|
16
|
+
.replace(/>/g, '>')
|
|
17
|
+
.substring(0, 200) // Limit length
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
function buildContributionGraph(options) {
|
|
21
|
+
const { commitMap, commitDetailsMap, weeks, dayLabels, dataStartDate, dataEndDate, totalCommits, title, getColorFn } = options;
|
|
22
|
+
// Build week columns
|
|
23
|
+
let weekColumns = '';
|
|
24
|
+
weeks.forEach((week) => {
|
|
25
|
+
let weekColumn = `<div class="graph-column">`;
|
|
26
|
+
week.forEach((day) => {
|
|
27
|
+
const dateKey = (0, date_fns_1.format)(day, 'yyyy-MM-dd');
|
|
28
|
+
const count = commitMap.get(dateKey) || 0;
|
|
29
|
+
const isInRange = day >= dataStartDate && day <= dataEndDate;
|
|
30
|
+
const color = isInRange ? getColorFn(count) : 'transparent';
|
|
31
|
+
const dateStr = (0, date_fns_1.format)(day, 'MMM d, yyyy');
|
|
32
|
+
const emptyClass = count === 0 ? ' empty' : '';
|
|
33
|
+
const clickable = count > 0 ? ' clickable' : '';
|
|
34
|
+
const rawCommits = commitDetailsMap.get(dateKey) || [];
|
|
35
|
+
const sanitizedCommits = sanitizeCommitsForHtml(rawCommits);
|
|
36
|
+
const detailsData = count > 0 ? `data-details='${JSON.stringify(sanitizedCommits)}'` : '';
|
|
37
|
+
weekColumn += `<div class="day${emptyClass}${clickable}" style="background-color: ${color};" data-count="${count}" data-date="${dateStr}" ${detailsData}></div>`;
|
|
38
|
+
});
|
|
39
|
+
weekColumn += '</div>';
|
|
40
|
+
weekColumns += weekColumn;
|
|
41
|
+
});
|
|
42
|
+
// Build day labels column
|
|
43
|
+
let dayLabelsHtml = '<div class="day-labels">';
|
|
44
|
+
dayLabels.forEach((label, index) => {
|
|
45
|
+
const displayLabel = [1, 3, 5].includes(index) ? label : '';
|
|
46
|
+
dayLabelsHtml += `<div class="day-label">${displayLabel}</div>`;
|
|
47
|
+
});
|
|
48
|
+
dayLabelsHtml += '</div>';
|
|
49
|
+
// Build month labels header
|
|
50
|
+
let monthLabelsHtml = '';
|
|
51
|
+
let currentMonth = '';
|
|
52
|
+
weeks.forEach((week) => {
|
|
53
|
+
const firstDayOfWeek = week[0];
|
|
54
|
+
const monthName = (0, date_fns_1.format)(firstDayOfWeek, 'MMM');
|
|
55
|
+
if (monthName !== currentMonth) {
|
|
56
|
+
monthLabelsHtml += `<div class="month-label">${monthName}</div>`;
|
|
57
|
+
currentMonth = monthName;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
monthLabelsHtml += `<div class="month-label"></div>`;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return `
|
|
64
|
+
<div class="stats">
|
|
65
|
+
${title}: <strong>${totalCommits}</strong> commits
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="graph-container">
|
|
69
|
+
<div class="months">
|
|
70
|
+
${monthLabelsHtml}
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="graph">
|
|
74
|
+
${dayLabelsHtml}
|
|
75
|
+
${weekColumns}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="legend">
|
|
79
|
+
<span>Less</span>
|
|
80
|
+
<span class="legend-box" style="background-color: rgba(235, 237, 240, 0.1); border: 1px solid #21262d;"></span>
|
|
81
|
+
<span class="legend-box" style="background-color: #9be9a8;"></span>
|
|
82
|
+
<span class="legend-box" style="background-color: #40c463;"></span>
|
|
83
|
+
<span class="legend-box" style="background-color: #30a14e;"></span>
|
|
84
|
+
<span class="legend-box" style="background-color: #216e39;"></span>
|
|
85
|
+
<span>More</span>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.buildEventsSection = exports.formatEventsInsights = exports.getEventTypeColor = exports.getEventTypeEmoji = exports.correlateEventsWithMetrics = exports.parseEventsInline = exports.loadEventsFile = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* Events Feature
|
|
20
|
+
* @module features/events
|
|
21
|
+
*/
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
var parser_1 = require("./parser");
|
|
24
|
+
Object.defineProperty(exports, "loadEventsFile", { enumerable: true, get: function () { return parser_1.loadEventsFile; } });
|
|
25
|
+
Object.defineProperty(exports, "parseEventsInline", { enumerable: true, get: function () { return parser_1.parseEventsInline; } });
|
|
26
|
+
Object.defineProperty(exports, "correlateEventsWithMetrics", { enumerable: true, get: function () { return parser_1.correlateEventsWithMetrics; } });
|
|
27
|
+
Object.defineProperty(exports, "getEventTypeEmoji", { enumerable: true, get: function () { return parser_1.getEventTypeEmoji; } });
|
|
28
|
+
Object.defineProperty(exports, "getEventTypeColor", { enumerable: true, get: function () { return parser_1.getEventTypeColor; } });
|
|
29
|
+
Object.defineProperty(exports, "formatEventsInsights", { enumerable: true, get: function () { return parser_1.formatEventsInsights; } });
|
|
30
|
+
var template_1 = require("./template");
|
|
31
|
+
Object.defineProperty(exports, "buildEventsSection", { enumerable: true, get: function () { return template_1.buildEventsSection; } });
|