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.
Files changed (176) hide show
  1. package/.github/agents/complete.agent.md +257 -0
  2. package/.github/agents/feature-scaffold.agent.md +248 -0
  3. package/.github/agents/jsdoc.agent.md +243 -0
  4. package/.github/agents/plan.agent.md +202 -0
  5. package/.github/agents/spec-writer.agent.md +169 -0
  6. package/.github/agents/test-writer.agent.md +169 -0
  7. package/.stylelintrc.json +27 -0
  8. package/README.md +94 -94
  9. package/coverage/base.css +224 -0
  10. package/coverage/block-navigation.js +87 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +446 -0
  13. package/coverage/lcov-report/base.css +224 -0
  14. package/coverage/lcov-report/block-navigation.js +87 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/index.html +446 -0
  17. package/coverage/lcov-report/prettify.css +1 -0
  18. package/coverage/lcov-report/prettify.js +2 -0
  19. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  20. package/coverage/lcov-report/sorter.js +210 -0
  21. package/coverage/lcov.info +7039 -0
  22. package/coverage/prettify.css +1 -0
  23. package/coverage/prettify.js +2 -0
  24. package/coverage/sort-arrow-sprite.png +0 -0
  25. package/coverage/sorter.js +210 -0
  26. package/dist/commands/generate.js +262 -5
  27. package/dist/config/defaults.js +158 -0
  28. package/dist/config/index.js +10 -0
  29. package/dist/features/achievements/data/achievements.json +284 -0
  30. package/dist/features/achievements/engine.js +140 -0
  31. package/dist/features/achievements/evaluators.js +246 -0
  32. package/dist/features/achievements/helpers.js +58 -0
  33. package/dist/features/achievements/index.js +57 -0
  34. package/dist/features/achievements/loader.js +88 -0
  35. package/dist/features/achievements/template.js +155 -0
  36. package/dist/features/achievements/types.js +7 -0
  37. package/dist/features/commit-quality/analyzer.js +378 -0
  38. package/dist/features/commit-quality/analyzer.test.js +484 -0
  39. package/dist/features/commit-quality/index.js +28 -0
  40. package/dist/features/commit-quality/template.js +114 -0
  41. package/dist/features/commit-quality/types.js +2 -0
  42. package/dist/features/comparison/analyzer.js +222 -0
  43. package/dist/features/comparison/index.js +28 -0
  44. package/dist/features/comparison/template.js +119 -0
  45. package/dist/features/comparison/types.js +2 -0
  46. package/dist/features/contribution-graph/index.js +9 -0
  47. package/dist/features/contribution-graph/template.js +89 -0
  48. package/dist/features/events/index.js +31 -0
  49. package/dist/features/events/parser.js +253 -0
  50. package/dist/features/events/template.js +113 -0
  51. package/dist/features/events/types.js +2 -0
  52. package/dist/features/executive-summary/generator.js +275 -0
  53. package/dist/features/executive-summary/index.js +27 -0
  54. package/dist/features/executive-summary/template.js +80 -0
  55. package/dist/features/executive-summary/types.js +2 -0
  56. package/dist/features/gaps/analyzer.js +298 -0
  57. package/dist/features/gaps/analyzer.test.js +517 -0
  58. package/dist/features/gaps/index.js +27 -0
  59. package/dist/features/gaps/template.js +190 -0
  60. package/dist/features/gaps/types.js +2 -0
  61. package/dist/features/impact/analyzer.js +248 -0
  62. package/dist/features/impact/index.js +26 -0
  63. package/dist/features/impact/template.js +118 -0
  64. package/dist/features/impact/types.js +2 -0
  65. package/dist/features/index.js +40 -0
  66. package/dist/features/knowledge/analyzer.js +385 -0
  67. package/dist/features/knowledge/index.js +26 -0
  68. package/dist/features/knowledge/template.js +239 -0
  69. package/dist/features/knowledge/types.js +2 -0
  70. package/dist/features/streaks/calculator.js +184 -0
  71. package/dist/features/streaks/calculator.test.js +366 -0
  72. package/dist/features/streaks/index.js +36 -0
  73. package/dist/features/streaks/template.js +41 -0
  74. package/dist/features/streaks/types.js +9 -0
  75. package/dist/features/team/analyzer.js +316 -0
  76. package/dist/features/team/index.js +30 -0
  77. package/dist/features/team/template.js +146 -0
  78. package/dist/features/team/types.js +2 -0
  79. package/dist/features/time-patterns/analyzer.js +319 -0
  80. package/dist/features/time-patterns/analyzer.test.js +278 -0
  81. package/dist/features/time-patterns/index.js +37 -0
  82. package/dist/features/time-patterns/template.js +109 -0
  83. package/dist/features/time-patterns/types.js +9 -0
  84. package/dist/features/velocity/analyzer.js +257 -0
  85. package/dist/features/velocity/analyzer.test.js +383 -0
  86. package/dist/features/velocity/index.js +27 -0
  87. package/dist/features/velocity/template.js +189 -0
  88. package/dist/features/velocity/types.js +2 -0
  89. package/dist/generators/html/scripts/knowledge.js +17 -0
  90. package/dist/generators/html/styles/base.css +10 -6
  91. package/dist/generators/html/styles/components.css +121 -1
  92. package/dist/generators/html/styles/knowledge.css +21 -0
  93. package/dist/generators/html/styles/leaddev.css +1335 -0
  94. package/dist/generators/html/styles/strategic-insights.css +1337 -0
  95. package/dist/generators/html/templates/commitQualitySection.js +28 -2
  96. package/dist/generators/html/templates/comparisonSection.js +119 -0
  97. package/dist/generators/html/templates/eventsSection.js +113 -0
  98. package/dist/generators/html/templates/executiveSummarySection.js +80 -0
  99. package/dist/generators/html/templates/gapSection.js +190 -0
  100. package/dist/generators/html/templates/impactSection.js +8 -6
  101. package/dist/generators/html/templates/knowledgeSection.js +16 -2
  102. package/dist/generators/html/templates/teamSection.js +146 -0
  103. package/dist/generators/html/templates/velocitySection.js +189 -0
  104. package/dist/generators/html/types.js +7 -0
  105. package/dist/generators/html/utils/analysisRunner.js +93 -0
  106. package/dist/generators/html/utils/cardBuilder.js +47 -0
  107. package/dist/generators/html/utils/contextBuilder.js +54 -0
  108. package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
  109. package/dist/generators/html/utils/kpiBuilder.js +76 -0
  110. package/dist/generators/html/utils/sectionWrapper.js +71 -0
  111. package/dist/generators/html/utils/styleLoader.js +2 -1
  112. package/dist/html/analysisRunner.js +93 -0
  113. package/dist/html/htmlDocumentBuilder.js +396 -0
  114. package/dist/html/index.js +29 -0
  115. package/dist/html/shared/colorUtils.js +61 -0
  116. package/dist/html/shared/commitMapBuilder.js +23 -0
  117. package/dist/html/shared/components/cardBuilder.js +47 -0
  118. package/dist/html/shared/components/index.js +18 -0
  119. package/dist/html/shared/components/kpiBuilder.js +76 -0
  120. package/dist/html/shared/components/sectionWrapper.js +71 -0
  121. package/dist/html/shared/contextBuilder.js +54 -0
  122. package/dist/html/shared/dateRangeCalculator.js +56 -0
  123. package/dist/html/shared/developerStatsCalculator.js +28 -0
  124. package/dist/html/shared/index.js +39 -0
  125. package/dist/html/shared/scriptLoader.js +15 -0
  126. package/dist/html/shared/scripts/export.js +125 -0
  127. package/dist/html/shared/scripts/knowledge.js +137 -0
  128. package/dist/html/shared/scripts/modal.js +68 -0
  129. package/dist/html/shared/scripts/navigation.js +156 -0
  130. package/dist/html/shared/scripts/tabs.js +18 -0
  131. package/dist/html/shared/scripts/tooltip.js +21 -0
  132. package/dist/html/shared/styleLoader.js +18 -0
  133. package/dist/html/shared/styles/achievements.css +387 -0
  134. package/dist/html/shared/styles/base.css +822 -0
  135. package/dist/html/shared/styles/components.css +1511 -0
  136. package/dist/html/shared/styles/knowledge.css +242 -0
  137. package/dist/html/shared/styles/strategic-insights.css +1337 -0
  138. package/dist/html/shared/weekGrouper.js +27 -0
  139. package/dist/html/types.js +7 -0
  140. package/dist/index.js +54 -21
  141. package/dist/test/helpers/commitFactory.js +166 -0
  142. package/dist/test/helpers/dateUtils.js +101 -0
  143. package/dist/test/helpers/index.js +29 -0
  144. package/dist/test/setup.js +17 -0
  145. package/dist/test/smoke.test.js +94 -0
  146. package/dist/types/achievements.js +7 -0
  147. package/dist/types/analysis.js +7 -0
  148. package/dist/types/core.js +7 -0
  149. package/dist/types/index.js +38 -0
  150. package/dist/types/options.js +7 -0
  151. package/dist/types/shared.js +7 -0
  152. package/dist/types/strategic.js +7 -0
  153. package/dist/types/summary.js +7 -0
  154. package/dist/utils/achievementDefinitions.js +22 -22
  155. package/dist/utils/analyzerContextBuilder.js +124 -0
  156. package/dist/utils/commitQualityAnalyzer.js +13 -2
  157. package/dist/utils/emptyResults.js +95 -0
  158. package/dist/utils/eventAnnotationParser.js +253 -0
  159. package/dist/utils/executiveSummaryGenerator.js +275 -0
  160. package/dist/utils/fileHotspotAnalyzer.js +4 -12
  161. package/dist/utils/gapAnalyzer.js +298 -0
  162. package/dist/utils/gitParser.test.js +363 -0
  163. package/dist/utils/htmlGenerator.js +126 -450
  164. package/dist/utils/impactAnalyzer.js +20 -19
  165. package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
  166. package/dist/utils/matrixGenerator.js +13 -13
  167. package/dist/utils/rangeComparisonAnalyzer.js +222 -0
  168. package/dist/utils/streakCalculator.js +77 -27
  169. package/dist/utils/teamAnalyzer.js +316 -0
  170. package/dist/utils/timePatternAnalyzer.js +18 -3
  171. package/dist/utils/velocityAnalyzer.js +257 -0
  172. package/dist/utils/wrappedGenerator.js +8 -8
  173. package/package.json +74 -55
  174. package/vitest.config.ts +46 -0
  175. package/dist/cli.js +0 -24
  176. package/dist/commands/index.js +0 -24
@@ -44,9 +44,9 @@ function buildCommitQualitySection(commitQuality, totalCommits, skipBodyCheck =
44
44
  <div class="quality-metric">
45
45
  <div class="metric-label">${getQualityIndicator(commitQuality.subjectQuality.score, 7)} Subject Quality</div>
46
46
  <div class="metric-bar-container">
47
- <div class="metric-bar" style="width: ${(commitQuality.subjectQuality.avgLength / 72) * 100}%"></div>
47
+ <div class="metric-bar" style="width: ${commitQuality.subjectQuality.score * 10}%"></div>
48
48
  </div>
49
- <div class="metric-value">${commitQuality.subjectQuality.score.toFixed(1)}/10 (avg ${commitQuality.subjectQuality.avgLength} chars)</div>
49
+ <div class="metric-value">${commitQuality.subjectQuality.score.toFixed(1)}/10</div>
50
50
  </div>
51
51
 
52
52
  ${!skipBodyCheck ? `
@@ -61,6 +61,32 @@ function buildCommitQualitySection(commitQuality, totalCommits, skipBodyCheck =
61
61
  </div>
62
62
  </div>
63
63
 
64
+ <div class="subject-quality-breakdown">
65
+ <h3>Subject Quality Breakdown</h3>
66
+ <div class="subject-metrics-grid">
67
+ <div class="subject-metric">
68
+ <span class="metric-icon">${commitQuality.conventionalCommits.adherence >= 50 ? '✅' : '⚠️'}</span>
69
+ <span class="metric-name">Semantic Format</span>
70
+ <span class="metric-detail">${commitQuality.conventionalCommits.adherence.toFixed(0)}% follow conventional commits</span>
71
+ </div>
72
+ <div class="subject-metric">
73
+ <span class="metric-icon">${((totalCommits - commitQuality.subjectQuality.tooShort - commitQuality.subjectQuality.tooLong) / totalCommits) >= 0.8 ? '✅' : '⚠️'}</span>
74
+ <span class="metric-name">Proper Length</span>
75
+ <span class="metric-detail">${Math.round(((totalCommits - commitQuality.subjectQuality.tooShort - commitQuality.subjectQuality.tooLong) / totalCommits) * 100)}% within 10-72 chars</span>
76
+ </div>
77
+ <div class="subject-metric">
78
+ <span class="metric-icon">${(commitQuality.codeHygiene.vague + commitQuality.codeHygiene.workInProgress + commitQuality.codeHygiene.singleChar) === 0 ? '✅' : '⚠️'}</span>
79
+ <span class="metric-name">Quality Messages</span>
80
+ <span class="metric-detail">${commitQuality.codeHygiene.vague + commitQuality.codeHygiene.workInProgress + commitQuality.codeHygiene.singleChar} low-effort commits</span>
81
+ </div>
82
+ <div class="subject-metric">
83
+ <span class="metric-icon">${commitQuality.subjectQuality.imperativeMood / totalCommits >= 0.7 ? '✅' : '⚠️'}</span>
84
+ <span class="metric-name">Imperative Mood</span>
85
+ <span class="metric-detail">${Math.round((commitQuality.subjectQuality.imperativeMood / totalCommits) * 100)}% use imperative verbs</span>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
64
90
  ${Object.keys(commitQuality.conventionalCommits.types).length > 0 ? `
65
91
  <div class="commit-types-chart">
66
92
  <h3>Commit Type Distribution</h3>
@@ -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,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildEventsSection = buildEventsSection;
4
+ const date_fns_1 = require("date-fns");
5
+ const EVENT_ICONS = {
6
+ disruption: '⚠️',
7
+ attrition: '👋',
8
+ growth: '🌱',
9
+ release: '🚀',
10
+ incident: '🔥',
11
+ external: '📅',
12
+ custom: '📌'
13
+ };
14
+ const EVENT_COLORS = {
15
+ disruption: '#f59e0b',
16
+ attrition: '#ef4444',
17
+ growth: '#10b981',
18
+ release: '#3b82f6',
19
+ incident: '#dc2626',
20
+ external: '#6b7280',
21
+ custom: '#8b5cf6'
22
+ };
23
+ function buildEventsSection(events) {
24
+ if (!events || events.length === 0) {
25
+ return '<p class="no-data">No events annotated</p>';
26
+ }
27
+ const sortedEvents = [...events].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
28
+ return `
29
+ <div class="events-section">
30
+ <!-- Event Legend -->
31
+ <div class="events-legend">
32
+ ${Object.entries(EVENT_ICONS).map(([type, icon]) => `
33
+ <span class="legend-item">
34
+ <span class="event-icon">${icon}</span>
35
+ <span class="legend-label">${type}</span>
36
+ </span>
37
+ `).join('')}
38
+ </div>
39
+
40
+ <!-- Event Stats Summary -->
41
+ <div class="events-summary">
42
+ <div class="summary-stat">
43
+ <span class="stat-value">${events.length}</span>
44
+ <span class="stat-label">Total Events</span>
45
+ </div>
46
+ ${buildEventTypeCounts(events)}
47
+ </div>
48
+
49
+ <!-- Events Timeline -->
50
+ <div class="events-timeline">
51
+ ${sortedEvents.map(event => buildEventCard(event)).join('')}
52
+ </div>
53
+ </div>
54
+ `;
55
+ }
56
+ function buildEventTypeCounts(events) {
57
+ const counts = events.reduce((acc, e) => {
58
+ acc[e.type] = (acc[e.type] || 0) + 1;
59
+ return acc;
60
+ }, {});
61
+ const topTypes = Object.entries(counts)
62
+ .sort((a, b) => b[1] - a[1])
63
+ .slice(0, 3);
64
+ return topTypes.map(([type, count]) => `
65
+ <div class="summary-stat">
66
+ <span class="stat-value">${EVENT_ICONS[type] || '📌'} ${count}</span>
67
+ <span class="stat-label">${type}</span>
68
+ </div>
69
+ `).join('');
70
+ }
71
+ function buildEventCard(event) {
72
+ const icon = EVENT_ICONS[event.type] || '📌';
73
+ const color = EVENT_COLORS[event.type] || '#6b7280';
74
+ const dateStr = (0, date_fns_1.format)(new Date(event.date), 'MMM d, yyyy');
75
+ let correlationHtml = '';
76
+ if (event.correlation) {
77
+ const { velocityChange, qualityChange, activeAuthorsChange } = event.correlation.impact;
78
+ correlationHtml = `
79
+ <div class="event-correlation">
80
+ <div class="correlation-metrics">
81
+ <div class="correlation-metric ${velocityChange >= 0 ? 'positive' : 'negative'}">
82
+ <span class="metric-label">Velocity</span>
83
+ <span class="metric-value">${velocityChange >= 0 ? '+' : ''}${velocityChange.toFixed(0)}%</span>
84
+ </div>
85
+ <div class="correlation-metric ${qualityChange >= 0 ? 'positive' : 'negative'}">
86
+ <span class="metric-label">Quality</span>
87
+ <span class="metric-value">${qualityChange >= 0 ? '+' : ''}${qualityChange.toFixed(1)}</span>
88
+ </div>
89
+ <div class="correlation-metric ${activeAuthorsChange >= 0 ? 'positive' : 'negative'}">
90
+ <span class="metric-label">Contributors</span>
91
+ <span class="metric-value">${activeAuthorsChange >= 0 ? '+' : ''}${activeAuthorsChange}</span>
92
+ </div>
93
+ </div>
94
+ <div class="correlation-assessment">
95
+ <span class="assessment-icon">💡</span>
96
+ ${event.correlation.assessment}
97
+ </div>
98
+ </div>
99
+ `;
100
+ }
101
+ return `
102
+ <div class="event-card" style="--event-color: ${color}">
103
+ <div class="event-header">
104
+ <span class="event-icon">${icon}</span>
105
+ <span class="event-date">${dateStr}</span>
106
+ <span class="event-type-badge">${event.type}</span>
107
+ </div>
108
+ <div class="event-label">${event.label}</div>
109
+ ${event.description ? `<div class="event-description">${event.description}</div>` : ''}
110
+ ${correlationHtml}
111
+ </div>
112
+ `;
113
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildExecutiveSummarySection = buildExecutiveSummarySection;
4
+ const date_fns_1 = require("date-fns");
5
+ function buildExecutiveSummarySection(summary) {
6
+ return `
7
+ <div class="executive-summary-section">
8
+ <div class="executive-header">
9
+ <h2>📊 Executive Summary</h2>
10
+ </div>
11
+
12
+ <div class="kpi-grid">
13
+ ${summary.kpis.map(kpi => buildKPICard(kpi)).join('')}
14
+ </div>
15
+
16
+ <div class="insights-risks-grid">
17
+ <div class="insights-column">
18
+ <h3>💡 Key Insights</h3>
19
+ <div class="insights-list">
20
+ ${summary.keyInsights.map(insight => `
21
+ <div class="insight-item ${insight.type}">
22
+ <span class="insight-icon">${insight.type === 'positive' ? '✅' : insight.type === 'negative' ? '⚠️' : 'ℹ️'}</span>
23
+ <span class="insight-text">${insight.insight}</span>
24
+ </div>
25
+ `).join('')}
26
+ </div>
27
+ </div>
28
+
29
+ <div class="risks-column">
30
+ <h3>🚨 Risk Assessment</h3>
31
+ <div class="risks-list">
32
+ ${summary.risks.length > 0 ? summary.risks.map(risk => `
33
+ <div class="risk-item ${risk.level}">
34
+ <span class="risk-badge">${risk.level.toUpperCase()}</span>
35
+ <div class="risk-content">
36
+ <span class="risk-category">${risk.category}</span>
37
+ <span class="risk-description">${risk.description}</span>
38
+ </div>
39
+ </div>
40
+ `).join('') : '<div class="no-risks">No significant risks identified</div>'}
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="recommendations-section">
46
+ <h3>📋 Recommendations</h3>
47
+ <ol class="recommendations-list">
48
+ ${summary.recommendations.map(rec => `<li>${rec}</li>`).join('')}
49
+ </ol>
50
+ </div>
51
+
52
+ <div class="summary-footer">
53
+ Generated: ${(0, date_fns_1.format)(summary.generatedAt, 'MMM d, yyyy HH:mm')}
54
+ </div>
55
+ </div>
56
+ `;
57
+ }
58
+ function buildKPICard(kpi) {
59
+ const statusColors = {
60
+ green: '#69db7c',
61
+ yellow: '#ffd43b',
62
+ red: '#ff6b6b',
63
+ };
64
+ const trendArrow = kpi.trend === 'up' ? '↑' : kpi.trend === 'down' ? '↓' : '→';
65
+ const trendClass = kpi.trend === 'up' ? 'positive' : kpi.trend === 'down' ? 'negative' : 'neutral';
66
+ return `
67
+ <div class="kpi-card" style="--status-color: ${statusColors[kpi.status]}">
68
+ <div class="kpi-status-indicator" title="${kpi.status}"></div>
69
+ <div class="kpi-content">
70
+ <div class="kpi-label">${kpi.name}</div>
71
+ <div class="kpi-value">${kpi.value}</div>
72
+ <div class="kpi-trend ${trendClass}">
73
+ <span class="trend-arrow">${trendArrow}</span>
74
+ <span class="trend-value">${kpi.trendValue}</span>
75
+ </div>
76
+ ${kpi.benchmark ? `<div class="kpi-benchmark">${kpi.benchmark}</div>` : ''}
77
+ </div>
78
+ </div>
79
+ `;
80
+ }
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildGapSection = buildGapSection;
4
+ const date_fns_1 = require("date-fns");
5
+ function buildGapSection(gapAnalysis) {
6
+ const statsHtml = buildGapStats(gapAnalysis);
7
+ const timelineHtml = buildGapTimeline(gapAnalysis);
8
+ const gapsListHtml = buildGapsList(gapAnalysis.gaps);
9
+ return `
10
+ <div class="gap-section">
11
+ <h2>🕳️ Gap & Blocker Analysis</h2>
12
+
13
+ ${statsHtml}
14
+
15
+ ${timelineHtml}
16
+
17
+ ${gapsListHtml}
18
+ </div>
19
+ `;
20
+ }
21
+ function buildGapStats(gapAnalysis) {
22
+ const riskColors = {
23
+ low: '#69db7c',
24
+ medium: '#ffd43b',
25
+ high: '#ff922b',
26
+ critical: '#ff6b6b',
27
+ };
28
+ const riskEmoji = {
29
+ low: '🟢',
30
+ medium: '🟡',
31
+ high: '🟠',
32
+ critical: '🔴',
33
+ };
34
+ return `
35
+ <div class="gap-stats">
36
+ <div class="gap-stat-card">
37
+ <div class="stat-icon">🕳️</div>
38
+ <div class="stat-info">
39
+ <div class="stat-label">Total Gaps</div>
40
+ <div class="stat-value">${gapAnalysis.gaps.length}</div>
41
+ <div class="stat-detail">${gapAnalysis.gapFrequency}/month avg</div>
42
+ </div>
43
+ </div>
44
+ <div class="gap-stat-card">
45
+ <div class="stat-icon">📅</div>
46
+ <div class="stat-info">
47
+ <div class="stat-label">Gap Days</div>
48
+ <div class="stat-value">${gapAnalysis.totalGapDays}</div>
49
+ <div class="stat-detail">${gapAnalysis.percentageOfPeriodInGaps}% of period</div>
50
+ </div>
51
+ </div>
52
+ <div class="gap-stat-card">
53
+ <div class="stat-icon">⏱️</div>
54
+ <div class="stat-info">
55
+ <div class="stat-label">Longest Gap</div>
56
+ <div class="stat-value">${gapAnalysis.longestGap?.durationDays || 0} days</div>
57
+ <div class="stat-detail">avg: ${gapAnalysis.averageGapLength} days</div>
58
+ </div>
59
+ </div>
60
+ <div class="gap-stat-card risk-card" style="--risk-color: ${riskColors[gapAnalysis.riskLevel]}">
61
+ <div class="stat-icon">${riskEmoji[gapAnalysis.riskLevel]}</div>
62
+ <div class="stat-info">
63
+ <div class="stat-label">Risk Level</div>
64
+ <div class="stat-value risk-${gapAnalysis.riskLevel}">${gapAnalysis.riskLevel.toUpperCase()}</div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ ${gapAnalysis.riskFactors.length > 0 ? `
70
+ <div class="risk-factors">
71
+ <h4>Risk Factors</h4>
72
+ <ul>
73
+ ${gapAnalysis.riskFactors.map(f => `<li>${f}</li>`).join('')}
74
+ </ul>
75
+ </div>
76
+ ` : ''}
77
+ `;
78
+ }
79
+ function buildGapTimeline(gapAnalysis) {
80
+ if (gapAnalysis.gaps.length === 0) {
81
+ return `
82
+ <div class="gap-timeline-container">
83
+ <h3>Activity Timeline</h3>
84
+ <div class="no-gaps-message">
85
+ <span class="success-icon">✅</span>
86
+ <span>No significant activity gaps detected</span>
87
+ </div>
88
+ </div>
89
+ `;
90
+ }
91
+ // Find the full date range
92
+ const allDates = gapAnalysis.gaps.flatMap(g => [g.start, g.end]);
93
+ const minDate = new Date(Math.min(...allDates.map(d => d.getTime())));
94
+ const maxDate = new Date(Math.max(...allDates.map(d => d.getTime())));
95
+ const totalDays = Math.ceil((maxDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
96
+ if (totalDays <= 0) {
97
+ return '';
98
+ }
99
+ // Build gap segments
100
+ const segments = gapAnalysis.gaps.map(gap => {
101
+ const startOffset = Math.ceil((gap.start.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24));
102
+ const width = gap.durationDays;
103
+ const leftPercent = (startOffset / totalDays) * 100;
104
+ const widthPercent = (width / totalDays) * 100;
105
+ const typeClass = gap.possibleType === 'blocker' ? 'blocker' :
106
+ gap.possibleType === 'vacation' ? 'vacation' :
107
+ gap.possibleType === 'holiday' ? 'holiday' : 'unknown';
108
+ return `
109
+ <div class="gap-segment ${typeClass}"
110
+ style="left: ${leftPercent}%; width: ${widthPercent}%"
111
+ title="${gap.durationDays} days - ${gap.possibleType}">
112
+ </div>
113
+ `;
114
+ }).join('');
115
+ return `
116
+ <div class="gap-timeline-container">
117
+ <h3>Activity Timeline</h3>
118
+ <div class="gap-timeline">
119
+ <div class="timeline-track">
120
+ ${segments}
121
+ </div>
122
+ <div class="timeline-labels">
123
+ <span>${(0, date_fns_1.format)(minDate, 'MMM d, yyyy')}</span>
124
+ <span>${(0, date_fns_1.format)(maxDate, 'MMM d, yyyy')}</span>
125
+ </div>
126
+ </div>
127
+ <div class="timeline-legend">
128
+ <span class="legend-item"><span class="legend-color blocker"></span> Potential Blocker</span>
129
+ <span class="legend-item"><span class="legend-color vacation"></span> Vacation</span>
130
+ <span class="legend-item"><span class="legend-color holiday"></span> Holiday</span>
131
+ <span class="legend-item"><span class="legend-color unknown"></span> Unknown</span>
132
+ </div>
133
+ </div>
134
+ `;
135
+ }
136
+ function buildGapsList(gaps) {
137
+ if (gaps.length === 0) {
138
+ return '';
139
+ }
140
+ // Sort by duration descending, take top 5
141
+ const sortedGaps = [...gaps].sort((a, b) => b.durationDays - a.durationDays).slice(0, 5);
142
+ const gapItems = sortedGaps.map(gap => {
143
+ const typeEmoji = {
144
+ blocker: '🚧',
145
+ vacation: '🏖️',
146
+ holiday: '🎄',
147
+ weekend: '📅',
148
+ unknown: '❓',
149
+ };
150
+ return `
151
+ <div class="gap-item">
152
+ <div class="gap-item-header">
153
+ <span class="gap-type-icon">${typeEmoji[gap.possibleType]}</span>
154
+ <span class="gap-duration">${gap.durationDays} days</span>
155
+ <span class="gap-type-badge ${gap.possibleType}">${gap.possibleType}</span>
156
+ </div>
157
+ <div class="gap-dates">
158
+ ${(0, date_fns_1.format)(gap.start, 'MMM d')} - ${(0, date_fns_1.format)(gap.end, 'MMM d, yyyy')}
159
+ </div>
160
+ <div class="gap-context">
161
+ ${gap.commitBefore ? `
162
+ <div class="context-item before">
163
+ <span class="context-label">Before:</span>
164
+ <span class="context-message">"${truncate(gap.commitBefore.message, 50)}"</span>
165
+ </div>
166
+ ` : ''}
167
+ ${gap.commitAfter ? `
168
+ <div class="context-item after">
169
+ <span class="context-label">After:</span>
170
+ <span class="context-message">"${truncate(gap.commitAfter.message, 50)}"</span>
171
+ </div>
172
+ ` : ''}
173
+ </div>
174
+ </div>
175
+ `;
176
+ }).join('');
177
+ return `
178
+ <div class="gaps-list-section">
179
+ <h3>Top Gaps by Duration</h3>
180
+ <div class="gaps-list">
181
+ ${gapItems}
182
+ </div>
183
+ </div>
184
+ `;
185
+ }
186
+ function truncate(str, maxLength) {
187
+ if (str.length <= maxLength)
188
+ return str;
189
+ return str.substring(0, maxLength - 3) + '...';
190
+ }
@@ -11,7 +11,8 @@ function buildImpactSection(impact) {
11
11
  <div class="impact-section">
12
12
  <h2>Impact Analysis</h2>
13
13
 
14
- <div class="impact-overview">
14
+ <div class="impact-content">
15
+ <div class="impact-overview">
15
16
  <div class="impact-score-display">
16
17
  <div class="score-circle">
17
18
  <span class="score-value">${impact.overallScore}</span>
@@ -28,7 +29,7 @@ function buildImpactSection(impact) {
28
29
  <div class="breakdown-items">
29
30
  <div class="breakdown-item">
30
31
  <div class="breakdown-header">
31
- <span class="breakdown-label">Core Contributions</span>
32
+ <span class="breakdown-label">Core Contributions <span class="info-tooltip" title="Changes to src/core/ and src/lib/ folders - foundational code">ⓘ</span></span>
32
33
  <span class="breakdown-value">${impact.scoreBreakdown.coreContributions}%</span>
33
34
  </div>
34
35
  <div class="breakdown-bar">
@@ -38,7 +39,7 @@ function buildImpactSection(impact) {
38
39
 
39
40
  <div class="breakdown-item">
40
41
  <div class="breakdown-header">
41
- <span class="breakdown-label">Feature Work</span>
42
+ <span class="breakdown-label">Feature Work <span class="info-tooltip" title="Changes to source files (.ts, .js, .tsx, .jsx) outside core/lib">ⓘ</span></span>
42
43
  <span class="breakdown-value">${impact.scoreBreakdown.featureWork}%</span>
43
44
  </div>
44
45
  <div class="breakdown-bar">
@@ -48,7 +49,7 @@ function buildImpactSection(impact) {
48
49
 
49
50
  <div class="breakdown-item">
50
51
  <div class="breakdown-header">
51
- <span class="breakdown-label">Maintenance</span>
52
+ <span class="breakdown-label">Maintenance <span class="info-tooltip" title="Changes to tests, configs, CI/CD, and build files">ⓘ</span></span>
52
53
  <span class="breakdown-value">${impact.scoreBreakdown.maintenanceWork}%</span>
53
54
  </div>
54
55
  <div class="breakdown-bar">
@@ -58,7 +59,7 @@ function buildImpactSection(impact) {
58
59
 
59
60
  <div class="breakdown-item">
60
61
  <div class="breakdown-header">
61
- <span class="breakdown-label">Documentation</span>
62
+ <span class="breakdown-label">Documentation <span class="info-tooltip" title="Changes to .md files, README, and docs/ folder">ⓘ</span></span>
62
63
  <span class="breakdown-value">${impact.scoreBreakdown.documentationWork}%</span>
63
64
  </div>
64
65
  <div class="breakdown-bar">
@@ -99,10 +100,11 @@ function buildImpactSection(impact) {
99
100
  <div class="insights-list">
100
101
  <h3>Insights</h3>
101
102
  <ul>
102
- ${impact.insights.map(insight => `<li>${insight}</li>`).join('')}
103
+ ${impact.insights.map(insight => `<li>💡 ${insight}</li>`).join('')}
103
104
  </ul>
104
105
  </div>
105
106
  ` : ''}
107
+ </div>
106
108
  </div>
107
109
  `;
108
110
  }
@@ -149,6 +149,9 @@ function buildContributorBreakdown(dir) {
149
149
  function buildFileList(files) {
150
150
  if (!files || files.length === 0)
151
151
  return '';
152
+ const visibleCount = 10;
153
+ const hasMore = files.length > visibleCount;
154
+ const uniqueId = `files-${Math.random().toString(36).substr(2, 9)}`;
152
155
  return `
153
156
  <div class="file-breakdown">
154
157
  <h4>High-Risk Files</h4>
@@ -163,7 +166,7 @@ function buildFileList(files) {
163
166
  </tr>
164
167
  </thead>
165
168
  <tbody>
166
- ${files.slice(0, 10).map(file => `
169
+ ${files.slice(0, visibleCount).map(file => `
167
170
  <tr class="${file.busFactorRisk ? 'high-risk-file' : ''}">
168
171
  <td class="file-path" title="${file.path}">${getFileName(file.path)}</td>
169
172
  <td class="file-owner">${file.primaryOwner}</td>
@@ -174,9 +177,20 @@ function buildFileList(files) {
174
177
  <td><span class="risk-badge risk-${file.riskLevel}">${file.riskLevel}</span></td>
175
178
  </tr>
176
179
  `).join('')}
180
+ ${hasMore ? files.slice(visibleCount).map(file => `
181
+ <tr class="${file.busFactorRisk ? 'high-risk-file' : ''} hidden-file" data-group="${uniqueId}" style="display: none;">
182
+ <td class="file-path" title="${file.path}">${getFileName(file.path)}</td>
183
+ <td class="file-owner">${file.primaryOwner}</td>
184
+ <td>
185
+ <span class="file-ownership">${file.ownershipPercentage}%</span>
186
+ </td>
187
+ <td class="file-age">${file.knowledgeAge}</td>
188
+ <td><span class="risk-badge risk-${file.riskLevel}">${file.riskLevel}</span></td>
189
+ </tr>
190
+ `).join('') : ''}
177
191
  </tbody>
178
192
  </table>
179
- ${files.length > 10 ? `<p class="more-files">+ ${files.length - 10} more files</p>` : ''}
193
+ ${hasMore ? `<button class="more-files-btn" data-target="${uniqueId}" data-count="${files.length - visibleCount}" onclick="toggleMoreFiles(this)">+ ${files.length - visibleCount} more files</button>` : ''}
180
194
  </div>
181
195
  `;
182
196
  }