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,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildTeamSection = buildTeamSection;
|
|
4
|
+
/**
|
|
5
|
+
* Builds the HTML section for team analysis visualization
|
|
6
|
+
*/
|
|
7
|
+
function buildTeamSection(analysis) {
|
|
8
|
+
if (!analysis || analysis.members.length === 0) {
|
|
9
|
+
return '<p class="no-data">No team data available</p>';
|
|
10
|
+
}
|
|
11
|
+
const { teamMetrics, memberBreakdown, loadDistribution } = analysis;
|
|
12
|
+
// Build summary cards
|
|
13
|
+
const summaryHtml = buildSummaryCards(teamMetrics, analysis.members.length);
|
|
14
|
+
// Build load distribution indicator
|
|
15
|
+
const loadDistHtml = buildLoadDistribution(loadDistribution);
|
|
16
|
+
// Build member breakdown
|
|
17
|
+
const membersHtml = buildMemberList(memberBreakdown);
|
|
18
|
+
// Build insights
|
|
19
|
+
const insightsHtml = buildInsights(loadDistribution.insights);
|
|
20
|
+
return `
|
|
21
|
+
<div class="team-section">
|
|
22
|
+
${summaryHtml}
|
|
23
|
+
${loadDistHtml}
|
|
24
|
+
${membersHtml}
|
|
25
|
+
${insightsHtml}
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
function buildSummaryCards(metrics, memberCount) {
|
|
30
|
+
return `
|
|
31
|
+
<div class="team-summary">
|
|
32
|
+
<div class="team-stat-card">
|
|
33
|
+
<span class="stat-value">${metrics.totalCommits.toLocaleString()}</span>
|
|
34
|
+
<span class="stat-label">Total Commits</span>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="team-stat-card">
|
|
37
|
+
<span class="stat-value">${memberCount}</span>
|
|
38
|
+
<span class="stat-label">Team Members</span>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="team-stat-card">
|
|
41
|
+
<span class="stat-value">${metrics.commitsPerMember.toFixed(1)}</span>
|
|
42
|
+
<span class="stat-label">Commits/Member</span>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="team-stat-card">
|
|
45
|
+
<span class="stat-value">${metrics.activeDays}</span>
|
|
46
|
+
<span class="stat-label">Active Days</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="team-stat-card">
|
|
49
|
+
<span class="stat-value">${metrics.qualityScore.toFixed(1)}</span>
|
|
50
|
+
<span class="stat-label">Avg Quality</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
function buildLoadDistribution(loadDist) {
|
|
56
|
+
const gini = loadDist.giniCoefficient;
|
|
57
|
+
const percentage = Math.round(gini * 100);
|
|
58
|
+
// Color based on assessment
|
|
59
|
+
let colorClass = 'balanced';
|
|
60
|
+
let statusIcon = '✅';
|
|
61
|
+
if (loadDist.assessment === 'moderate-imbalance') {
|
|
62
|
+
colorClass = 'moderate';
|
|
63
|
+
statusIcon = '⚠️';
|
|
64
|
+
}
|
|
65
|
+
else if (loadDist.assessment === 'high-imbalance') {
|
|
66
|
+
colorClass = 'imbalanced';
|
|
67
|
+
statusIcon = '🚨';
|
|
68
|
+
}
|
|
69
|
+
const assessmentLabel = loadDist.assessment.replace('-', ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
70
|
+
return `
|
|
71
|
+
<div class="load-distribution">
|
|
72
|
+
<h3>Load Distribution</h3>
|
|
73
|
+
<div class="gini-container">
|
|
74
|
+
<div class="gini-bar-bg">
|
|
75
|
+
<div class="gini-bar ${colorClass}" style="width: ${percentage}%"></div>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="gini-info">
|
|
78
|
+
<span class="gini-value">Gini: ${gini.toFixed(2)}</span>
|
|
79
|
+
<span class="gini-assessment ${colorClass}">${statusIcon} ${assessmentLabel}</span>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<p class="gini-explanation">
|
|
83
|
+
${gini < 0.3 ? 'Work is evenly distributed across the team.' :
|
|
84
|
+
gini < 0.5 ? 'Some team members are contributing more than others.' :
|
|
85
|
+
'Work is concentrated among a few contributors. Consider redistributing.'}
|
|
86
|
+
</p>
|
|
87
|
+
</div>
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
90
|
+
function buildMemberList(members) {
|
|
91
|
+
// Sort by commits descending
|
|
92
|
+
const sorted = [...members].sort((a, b) => b.commits - a.commits);
|
|
93
|
+
const maxCommits = sorted[0]?.commits || 1;
|
|
94
|
+
const membersHtml = sorted.map((member, index) => {
|
|
95
|
+
const barWidth = (member.commits / maxCommits) * 100;
|
|
96
|
+
const rankBadge = index === 0 ? '👑' : index < 3 ? '⭐' : '';
|
|
97
|
+
return `
|
|
98
|
+
<div class="member-card">
|
|
99
|
+
<div class="member-rank">${index + 1}</div>
|
|
100
|
+
<div class="member-info">
|
|
101
|
+
<div class="member-name">${rankBadge} ${escapeHtml(member.author)}</div>
|
|
102
|
+
<div class="member-areas">${member.topAreas.slice(0, 3).map(a => `<span class="area-tag">${escapeHtml(a)}</span>`).join('')}</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="member-bar-container">
|
|
105
|
+
<div class="member-bar" style="width: ${barWidth}%"></div>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="member-stats">
|
|
108
|
+
<span class="member-commits">${member.commits}</span>
|
|
109
|
+
<span class="member-percentage">(${member.percentage.toFixed(1)}%)</span>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="member-quality" title="Commit quality score">
|
|
112
|
+
<span class="quality-badge">${member.qualityScore.toFixed(1)}</span>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
`;
|
|
116
|
+
}).join('');
|
|
117
|
+
return `
|
|
118
|
+
<div class="member-breakdown">
|
|
119
|
+
<h3>Team Members</h3>
|
|
120
|
+
<div class="member-list">
|
|
121
|
+
${membersHtml}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
function buildInsights(insights) {
|
|
127
|
+
if (!insights || insights.length === 0) {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
return `
|
|
131
|
+
<div class="team-insights">
|
|
132
|
+
<h3>Insights</h3>
|
|
133
|
+
<ul class="insights-list">
|
|
134
|
+
${insights.map(insight => `<li class="insight-item">💡 ${escapeHtml(insight)}</li>`).join('')}
|
|
135
|
+
</ul>
|
|
136
|
+
</div>
|
|
137
|
+
`;
|
|
138
|
+
}
|
|
139
|
+
function escapeHtml(str) {
|
|
140
|
+
return str
|
|
141
|
+
.replace(/&/g, '&')
|
|
142
|
+
.replace(/</g, '<')
|
|
143
|
+
.replace(/>/g, '>')
|
|
144
|
+
.replace(/"/g, '"')
|
|
145
|
+
.replace(/'/g, ''');
|
|
146
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildVelocitySection = buildVelocitySection;
|
|
4
|
+
const date_fns_1 = require("date-fns");
|
|
5
|
+
function buildVelocitySection(velocity) {
|
|
6
|
+
if (velocity.timeline.length === 0) {
|
|
7
|
+
return `
|
|
8
|
+
<div class="velocity-section">
|
|
9
|
+
<h2>📈 Velocity Analysis</h2>
|
|
10
|
+
<div class="empty-state">No velocity data available</div>
|
|
11
|
+
</div>
|
|
12
|
+
`;
|
|
13
|
+
}
|
|
14
|
+
const chartHtml = buildVelocityChart(velocity);
|
|
15
|
+
const statsHtml = buildVelocityStats(velocity);
|
|
16
|
+
const anomaliesHtml = buildAnomaliesList(velocity.anomalies);
|
|
17
|
+
return `
|
|
18
|
+
<div class="velocity-section">
|
|
19
|
+
<h2>📈 Velocity Analysis</h2>
|
|
20
|
+
|
|
21
|
+
${statsHtml}
|
|
22
|
+
|
|
23
|
+
<div class="velocity-chart-container">
|
|
24
|
+
<h3>Commits Per Week</h3>
|
|
25
|
+
${chartHtml}
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
${anomaliesHtml}
|
|
29
|
+
</div>
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
function buildVelocityStats(velocity) {
|
|
33
|
+
const trendEmoji = velocity.overallTrend === 'increasing' ? '📈' :
|
|
34
|
+
velocity.overallTrend === 'decreasing' ? '📉' :
|
|
35
|
+
velocity.overallTrend === 'volatile' ? '📊' : '➡️';
|
|
36
|
+
const trendClass = velocity.overallTrend === 'increasing' ? 'positive' :
|
|
37
|
+
velocity.overallTrend === 'decreasing' ? 'negative' : 'neutral';
|
|
38
|
+
const trendSign = velocity.trendPercentage >= 0 ? '+' : '';
|
|
39
|
+
return `
|
|
40
|
+
<div class="velocity-stats">
|
|
41
|
+
<div class="velocity-stat-card">
|
|
42
|
+
<div class="stat-icon">${trendEmoji}</div>
|
|
43
|
+
<div class="stat-info">
|
|
44
|
+
<div class="stat-label">Overall Trend</div>
|
|
45
|
+
<div class="stat-value ${trendClass}">${velocity.overallTrend}</div>
|
|
46
|
+
<div class="stat-detail">${trendSign}${velocity.trendPercentage}% over period</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="velocity-stat-card">
|
|
50
|
+
<div class="stat-icon">📊</div>
|
|
51
|
+
<div class="stat-info">
|
|
52
|
+
<div class="stat-label">Average</div>
|
|
53
|
+
<div class="stat-value">${velocity.averageCommitsPerWeek}</div>
|
|
54
|
+
<div class="stat-detail">commits/week</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="velocity-stat-card">
|
|
58
|
+
<div class="stat-icon">🚀</div>
|
|
59
|
+
<div class="stat-info">
|
|
60
|
+
<div class="stat-label">Peak Week</div>
|
|
61
|
+
<div class="stat-value">${velocity.peakWeek.commits}</div>
|
|
62
|
+
<div class="stat-detail">${(0, date_fns_1.format)(velocity.peakWeek.weekStart, 'MMM d, yyyy')}</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="velocity-stat-card">
|
|
66
|
+
<div class="stat-icon">🐢</div>
|
|
67
|
+
<div class="stat-info">
|
|
68
|
+
<div class="stat-label">Lowest Week</div>
|
|
69
|
+
<div class="stat-value">${velocity.lowestWeek.commits}</div>
|
|
70
|
+
<div class="stat-detail">${(0, date_fns_1.format)(velocity.lowestWeek.weekStart, 'MMM d, yyyy')}</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
76
|
+
function buildVelocityChart(velocity) {
|
|
77
|
+
const timeline = velocity.timeline;
|
|
78
|
+
const maxCommits = Math.max(...timeline.map(d => d.commits), 1);
|
|
79
|
+
const chartWidth = 800;
|
|
80
|
+
const chartHeight = 200;
|
|
81
|
+
const padding = { top: 20, right: 20, bottom: 40, left: 50 };
|
|
82
|
+
const innerWidth = chartWidth - padding.left - padding.right;
|
|
83
|
+
const innerHeight = chartHeight - padding.top - padding.bottom;
|
|
84
|
+
// Build SVG path for commits
|
|
85
|
+
const xScale = (i) => padding.left + (i / (timeline.length - 1 || 1)) * innerWidth;
|
|
86
|
+
const yScale = (v) => padding.top + innerHeight - (v / maxCommits) * innerHeight;
|
|
87
|
+
// Commits line
|
|
88
|
+
const commitsPath = timeline.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xScale(i)} ${yScale(d.commits)}`).join(' ');
|
|
89
|
+
// Rolling average line
|
|
90
|
+
const avgPath = timeline.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xScale(i)} ${yScale(d.rollingAverage)}`).join(' ');
|
|
91
|
+
// Area fill under commits line
|
|
92
|
+
const areaPath = `${commitsPath} L ${xScale(timeline.length - 1)} ${yScale(0)} L ${xScale(0)} ${yScale(0)} Z`;
|
|
93
|
+
// Find anomaly positions
|
|
94
|
+
const anomalyMarkers = velocity.anomalies.map(anomaly => {
|
|
95
|
+
const idx = timeline.findIndex(d => (0, date_fns_1.format)(d.weekStart, 'yyyy-MM-dd') === (0, date_fns_1.format)(anomaly.weekStart, 'yyyy-MM-dd'));
|
|
96
|
+
if (idx === -1)
|
|
97
|
+
return '';
|
|
98
|
+
const x = xScale(idx);
|
|
99
|
+
const y = yScale(timeline[idx].commits);
|
|
100
|
+
const color = anomaly.type === 'drop' ? '#ff6b6b' : anomaly.type === 'spike' ? '#ffa94d' : '#868e96';
|
|
101
|
+
return `<circle cx="${x}" cy="${y}" r="6" fill="${color}" class="anomaly-marker" data-type="${anomaly.type}" data-severity="${anomaly.severity}"/>`;
|
|
102
|
+
}).join('');
|
|
103
|
+
// X-axis labels (show every nth label to avoid crowding)
|
|
104
|
+
const labelInterval = Math.max(1, Math.floor(timeline.length / 8));
|
|
105
|
+
const xLabels = timeline.map((d, i) => {
|
|
106
|
+
if (i % labelInterval !== 0 && i !== timeline.length - 1)
|
|
107
|
+
return '';
|
|
108
|
+
return `<text x="${xScale(i)}" y="${chartHeight - 10}" text-anchor="middle" class="axis-label">${(0, date_fns_1.format)(d.weekStart, 'MMM d')}</text>`;
|
|
109
|
+
}).join('');
|
|
110
|
+
// Y-axis labels
|
|
111
|
+
const yTicks = [0, Math.round(maxCommits / 2), maxCommits];
|
|
112
|
+
const yLabels = yTicks.map(v => `<text x="${padding.left - 10}" y="${yScale(v) + 4}" text-anchor="end" class="axis-label">${v}</text>`).join('');
|
|
113
|
+
// Grid lines
|
|
114
|
+
const gridLines = yTicks.map(v => `<line x1="${padding.left}" y1="${yScale(v)}" x2="${chartWidth - padding.right}" y2="${yScale(v)}" class="grid-line"/>`).join('');
|
|
115
|
+
return `
|
|
116
|
+
<svg class="velocity-chart" viewBox="0 0 ${chartWidth} ${chartHeight}" preserveAspectRatio="xMidYMid meet">
|
|
117
|
+
<defs>
|
|
118
|
+
<linearGradient id="areaGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
119
|
+
<stop offset="0%" style="stop-color:var(--accent-color);stop-opacity:0.3"/>
|
|
120
|
+
<stop offset="100%" style="stop-color:var(--accent-color);stop-opacity:0.05"/>
|
|
121
|
+
</linearGradient>
|
|
122
|
+
</defs>
|
|
123
|
+
|
|
124
|
+
<!-- Grid -->
|
|
125
|
+
${gridLines}
|
|
126
|
+
|
|
127
|
+
<!-- Area fill -->
|
|
128
|
+
<path d="${areaPath}" fill="url(#areaGradient)" class="area-fill"/>
|
|
129
|
+
|
|
130
|
+
<!-- Commits line -->
|
|
131
|
+
<path d="${commitsPath}" fill="none" stroke="var(--accent-color)" stroke-width="2" class="commits-line"/>
|
|
132
|
+
|
|
133
|
+
<!-- Rolling average line -->
|
|
134
|
+
<path d="${avgPath}" fill="none" stroke="#ff922b" stroke-width="3" stroke-dasharray="6,4" class="avg-line"/>
|
|
135
|
+
|
|
136
|
+
<!-- Anomaly markers -->
|
|
137
|
+
${anomalyMarkers}
|
|
138
|
+
|
|
139
|
+
<!-- Axes labels -->
|
|
140
|
+
${xLabels}
|
|
141
|
+
${yLabels}
|
|
142
|
+
|
|
143
|
+
<!-- Legend -->
|
|
144
|
+
<g transform="translate(${chartWidth - 150}, 10)">
|
|
145
|
+
<line x1="0" y1="5" x2="20" y2="5" stroke="var(--accent-color)" stroke-width="2"/>
|
|
146
|
+
<text x="25" y="9" class="legend-label">Commits</text>
|
|
147
|
+
<line x1="0" y1="20" x2="20" y2="20" stroke="#ff922b" stroke-width="3" stroke-dasharray="6,4"/>
|
|
148
|
+
<text x="25" y="24" class="legend-label">Rolling Avg</text>
|
|
149
|
+
</g>
|
|
150
|
+
</svg>
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
function buildAnomaliesList(anomalies) {
|
|
154
|
+
if (anomalies.length === 0) {
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
const criticalAnomalies = anomalies.filter(a => a.severity === 'critical' || a.severity === 'significant');
|
|
158
|
+
if (criticalAnomalies.length === 0) {
|
|
159
|
+
return '';
|
|
160
|
+
}
|
|
161
|
+
const anomalyItems = criticalAnomalies.slice(0, 5).map(anomaly => {
|
|
162
|
+
const typeEmoji = anomaly.type === 'drop' ? '📉' : anomaly.type === 'spike' ? '📈' : '🚫';
|
|
163
|
+
const severityClass = anomaly.severity === 'critical' ? 'critical' : 'warning';
|
|
164
|
+
const dateStr = (0, date_fns_1.format)(anomaly.weekStart, 'MMM d, yyyy');
|
|
165
|
+
return `
|
|
166
|
+
<div class="anomaly-item ${severityClass}">
|
|
167
|
+
<span class="anomaly-icon">${typeEmoji}</span>
|
|
168
|
+
<div class="anomaly-info">
|
|
169
|
+
<div class="anomaly-header">
|
|
170
|
+
<span class="anomaly-type">${anomaly.type}</span>
|
|
171
|
+
<span class="anomaly-date">${dateStr}</span>
|
|
172
|
+
<span class="anomaly-change">${anomaly.percentageChange >= 0 ? '+' : ''}${anomaly.percentageChange}%</span>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="anomaly-causes">
|
|
175
|
+
Possible: ${anomaly.possibleCauses.slice(0, 2).join(', ')}
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
`;
|
|
180
|
+
}).join('');
|
|
181
|
+
return `
|
|
182
|
+
<div class="anomalies-section">
|
|
183
|
+
<h3>⚠️ Detected Anomalies</h3>
|
|
184
|
+
<div class="anomalies-list">
|
|
185
|
+
${anomalyItems}
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Analysis runner - executes all analyzers with shared context.
|
|
4
|
+
*
|
|
5
|
+
* @module generators/html/utils/analysisRunner
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.runAllAnalyzers = runAllAnalyzers;
|
|
9
|
+
const streaks_1 = require("../../../features/streaks");
|
|
10
|
+
const time_patterns_1 = require("../../../features/time-patterns");
|
|
11
|
+
const commitQualityAnalyzer_1 = require("../../../utils/commitQualityAnalyzer");
|
|
12
|
+
const fileHotspotAnalyzer_1 = require("../../../utils/fileHotspotAnalyzer");
|
|
13
|
+
const impactAnalyzer_1 = require("../../../utils/impactAnalyzer");
|
|
14
|
+
const knowledgeDistributionAnalyzer_1 = require("../../../utils/knowledgeDistributionAnalyzer");
|
|
15
|
+
const achievements_1 = require("../../../features/achievements");
|
|
16
|
+
const commitMapBuilder_1 = require("./commitMapBuilder");
|
|
17
|
+
/**
|
|
18
|
+
* Run all analyzers for the given context.
|
|
19
|
+
*
|
|
20
|
+
* @param context - The HTML generation context
|
|
21
|
+
* @param skipBodyCheck - Whether to skip commit body validation
|
|
22
|
+
* @param deepAnalysis - Whether to enable deep analysis
|
|
23
|
+
* @param leadDevFeatures - Pre-computed strategic insights (optional)
|
|
24
|
+
* @returns Analysis results for all views
|
|
25
|
+
*/
|
|
26
|
+
function runAllAnalyzers(context, skipBodyCheck = false, deepAnalysis = false, leadDevFeatures) {
|
|
27
|
+
const { filteredCommits, personalCommits, dateRange, repoInfo } = context;
|
|
28
|
+
const { start: startDate, end: endDate } = dateRange;
|
|
29
|
+
// Create commit maps for overall
|
|
30
|
+
const { commitMap, commitDetailsMap } = (0, commitMapBuilder_1.createCommitMaps)(filteredCommits, startDate, endDate);
|
|
31
|
+
const totalCommits = Array.from(commitMap.values()).reduce((sum, count) => sum + count, 0);
|
|
32
|
+
// Create commit maps for personal
|
|
33
|
+
const { commitMap: personalCommitMap, commitDetailsMap: personalCommitDetailsMap } = (0, commitMapBuilder_1.createCommitMaps)(personalCommits, startDate, endDate);
|
|
34
|
+
const totalPersonalCommits = Array.from(personalCommitMap.values()).reduce((sum, count) => sum + count, 0);
|
|
35
|
+
// Run overall analyzers
|
|
36
|
+
const streakData = (0, streaks_1.calculateStreaks)(filteredCommits, startDate, endDate);
|
|
37
|
+
const timePattern = (0, time_patterns_1.analyzeTimePatterns)(filteredCommits, startDate, endDate);
|
|
38
|
+
const commitQuality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(filteredCommits, { skipBodyCheck });
|
|
39
|
+
// Run personal analyzers
|
|
40
|
+
const personalStreakData = (0, streaks_1.calculateStreaks)(personalCommits, startDate, endDate);
|
|
41
|
+
const personalTimePattern = (0, time_patterns_1.analyzeTimePatterns)(personalCommits, startDate, endDate);
|
|
42
|
+
const personalCommitQuality = (0, commitQualityAnalyzer_1.analyzeCommitQuality)(personalCommits, { skipBodyCheck });
|
|
43
|
+
// Run enterprise insight analyzers
|
|
44
|
+
const fileHotspots = (0, fileHotspotAnalyzer_1.analyzeFileHotspots)(repoInfo.path, startDate, endDate);
|
|
45
|
+
const impactAnalysis = (0, impactAnalyzer_1.analyzeImpact)(filteredCommits);
|
|
46
|
+
const knowledgeDistribution = (0, knowledgeDistributionAnalyzer_1.analyzeKnowledgeDistribution)(filteredCommits, repoInfo.path, deepAnalysis);
|
|
47
|
+
// Build analysis data for achievements
|
|
48
|
+
const overallAnalysisData = {
|
|
49
|
+
commits: filteredCommits,
|
|
50
|
+
totalCommits,
|
|
51
|
+
streakData,
|
|
52
|
+
timePattern,
|
|
53
|
+
commitQuality,
|
|
54
|
+
fileHotspots,
|
|
55
|
+
dateRange: { start: startDate, end: endDate }
|
|
56
|
+
};
|
|
57
|
+
const personalAnalysisData = {
|
|
58
|
+
commits: personalCommits,
|
|
59
|
+
totalCommits: totalPersonalCommits,
|
|
60
|
+
streakData: personalStreakData,
|
|
61
|
+
timePattern: personalTimePattern,
|
|
62
|
+
commitQuality: personalCommitQuality,
|
|
63
|
+
fileHotspots,
|
|
64
|
+
dateRange: { start: startDate, end: endDate }
|
|
65
|
+
};
|
|
66
|
+
// Check achievements
|
|
67
|
+
const achievementProgress = (0, achievements_1.checkAchievements)(overallAnalysisData);
|
|
68
|
+
const personalAchievementProgress = (0, achievements_1.checkAchievements)(personalAnalysisData);
|
|
69
|
+
return {
|
|
70
|
+
overall: {
|
|
71
|
+
commitMap,
|
|
72
|
+
commitDetailsMap,
|
|
73
|
+
totalCommits,
|
|
74
|
+
streakData,
|
|
75
|
+
timePattern,
|
|
76
|
+
commitQuality,
|
|
77
|
+
achievementProgress,
|
|
78
|
+
},
|
|
79
|
+
personal: {
|
|
80
|
+
commitMap: personalCommitMap,
|
|
81
|
+
commitDetailsMap: personalCommitDetailsMap,
|
|
82
|
+
totalCommits: totalPersonalCommits,
|
|
83
|
+
streakData: personalStreakData,
|
|
84
|
+
timePattern: personalTimePattern,
|
|
85
|
+
commitQuality: personalCommitQuality,
|
|
86
|
+
achievementProgress: personalAchievementProgress,
|
|
87
|
+
},
|
|
88
|
+
fileHotspots,
|
|
89
|
+
impactAnalysis,
|
|
90
|
+
knowledgeDistribution,
|
|
91
|
+
leadDev: leadDevFeatures,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared utility for building consistent stat cards across templates
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildStatCard = buildStatCard;
|
|
7
|
+
exports.buildStatGrid = buildStatGrid;
|
|
8
|
+
exports.buildCardSection = buildCardSection;
|
|
9
|
+
/**
|
|
10
|
+
* Builds a single stat card with consistent styling
|
|
11
|
+
*/
|
|
12
|
+
function buildStatCard(options) {
|
|
13
|
+
const { icon, value, label, detail, className = '', valueClass = '' } = options;
|
|
14
|
+
return `
|
|
15
|
+
<div class="stat-card-base ${className}">
|
|
16
|
+
${icon ? `<div class="stat-icon">${icon}</div>` : ''}
|
|
17
|
+
<div class="stat-info">
|
|
18
|
+
<div class="stat-label">${label}</div>
|
|
19
|
+
<div class="stat-value ${valueClass}">${value}</div>
|
|
20
|
+
${detail ? `<div class="stat-detail">${detail}</div>` : ''}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Builds a grid of stat cards
|
|
27
|
+
*/
|
|
28
|
+
function buildStatGrid(options) {
|
|
29
|
+
return `
|
|
30
|
+
<div class="stat-grid ${options.className || ''}">
|
|
31
|
+
${options.cards.map(card => buildStatCard(card)).join('')}
|
|
32
|
+
</div>
|
|
33
|
+
`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Builds a card section with title and content
|
|
37
|
+
*/
|
|
38
|
+
function buildCardSection(options) {
|
|
39
|
+
const { title, content, className = '', icon } = options;
|
|
40
|
+
const titleContent = icon ? `${icon} ${title}` : title;
|
|
41
|
+
return `
|
|
42
|
+
<div class="card-section ${className}">
|
|
43
|
+
<h3>${titleContent}</h3>
|
|
44
|
+
${content}
|
|
45
|
+
</div>
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Context builder for HTML generation.
|
|
4
|
+
*
|
|
5
|
+
* @module generators/html/utils/contextBuilder
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.buildHtmlContext = buildHtmlContext;
|
|
9
|
+
const dateRangeCalculator_1 = require("./dateRangeCalculator");
|
|
10
|
+
const developerStatsCalculator_1 = require("./developerStatsCalculator");
|
|
11
|
+
/**
|
|
12
|
+
* Build the HTML generation context from options.
|
|
13
|
+
*
|
|
14
|
+
* @param options - Generation options
|
|
15
|
+
* @returns Fully populated context for HTML generation
|
|
16
|
+
*/
|
|
17
|
+
function buildHtmlContext(options) {
|
|
18
|
+
const { commits, repoPath, repoName, repoUrl, year = new Date().getFullYear(), monthsToShow = 12, allTime = false, } = options;
|
|
19
|
+
// Get current git user
|
|
20
|
+
const { getCurrentGitUser } = require('../../../utils/gitParser');
|
|
21
|
+
const currentUser = getCurrentGitUser(repoPath);
|
|
22
|
+
// Calculate date ranges
|
|
23
|
+
const dateRanges = allTime
|
|
24
|
+
? (0, dateRangeCalculator_1.calculateDateRangesFromCommits)(commits)
|
|
25
|
+
: (0, dateRangeCalculator_1.calculateDateRanges)(year, monthsToShow);
|
|
26
|
+
const { startDate, endDate, weekStartDate, weekEndDate } = dateRanges;
|
|
27
|
+
// Filter commits to date range
|
|
28
|
+
const filteredCommits = commits.filter(commit => {
|
|
29
|
+
const commitDate = new Date(commit.date);
|
|
30
|
+
return commitDate >= startDate && commitDate <= endDate;
|
|
31
|
+
});
|
|
32
|
+
// Filter personal commits
|
|
33
|
+
const personalCommits = filteredCommits.filter(c => c.author === currentUser);
|
|
34
|
+
// Calculate developer stats
|
|
35
|
+
const developerStats = (0, developerStatsCalculator_1.calculateDeveloperStats)(filteredCommits, startDate, endDate);
|
|
36
|
+
return {
|
|
37
|
+
options,
|
|
38
|
+
repoInfo: {
|
|
39
|
+
name: repoName,
|
|
40
|
+
url: repoUrl,
|
|
41
|
+
path: repoPath,
|
|
42
|
+
},
|
|
43
|
+
currentUser,
|
|
44
|
+
dateRange: {
|
|
45
|
+
start: startDate,
|
|
46
|
+
end: endDate,
|
|
47
|
+
weekStart: weekStartDate,
|
|
48
|
+
weekEnd: weekEndDate,
|
|
49
|
+
},
|
|
50
|
+
filteredCommits,
|
|
51
|
+
personalCommits,
|
|
52
|
+
developerStats,
|
|
53
|
+
};
|
|
54
|
+
}
|