repo-wrapped 0.0.7 → 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 (172) 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 +56 -56
  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 +8 -3
  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 +108 -48
  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/executiveSummarySection.js +0 -4
  97. package/dist/generators/html/templates/impactSection.js +8 -6
  98. package/dist/generators/html/templates/knowledgeSection.js +16 -2
  99. package/dist/generators/html/templates/velocitySection.js +2 -2
  100. package/dist/generators/html/types.js +7 -0
  101. package/dist/generators/html/utils/analysisRunner.js +93 -0
  102. package/dist/generators/html/utils/cardBuilder.js +47 -0
  103. package/dist/generators/html/utils/contextBuilder.js +54 -0
  104. package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
  105. package/dist/generators/html/utils/kpiBuilder.js +76 -0
  106. package/dist/generators/html/utils/sectionWrapper.js +71 -0
  107. package/dist/generators/html/utils/styleLoader.js +2 -2
  108. package/dist/html/analysisRunner.js +93 -0
  109. package/dist/html/htmlDocumentBuilder.js +396 -0
  110. package/dist/html/index.js +29 -0
  111. package/dist/html/shared/colorUtils.js +61 -0
  112. package/dist/html/shared/commitMapBuilder.js +23 -0
  113. package/dist/html/shared/components/cardBuilder.js +47 -0
  114. package/dist/html/shared/components/index.js +18 -0
  115. package/dist/html/shared/components/kpiBuilder.js +76 -0
  116. package/dist/html/shared/components/sectionWrapper.js +71 -0
  117. package/dist/html/shared/contextBuilder.js +54 -0
  118. package/dist/html/shared/dateRangeCalculator.js +56 -0
  119. package/dist/html/shared/developerStatsCalculator.js +28 -0
  120. package/dist/html/shared/index.js +39 -0
  121. package/dist/html/shared/scriptLoader.js +15 -0
  122. package/dist/html/shared/scripts/export.js +125 -0
  123. package/dist/html/shared/scripts/knowledge.js +137 -0
  124. package/dist/html/shared/scripts/modal.js +68 -0
  125. package/dist/html/shared/scripts/navigation.js +156 -0
  126. package/dist/html/shared/scripts/tabs.js +18 -0
  127. package/dist/html/shared/scripts/tooltip.js +21 -0
  128. package/dist/html/shared/styleLoader.js +18 -0
  129. package/dist/html/shared/styles/achievements.css +387 -0
  130. package/dist/html/shared/styles/base.css +822 -0
  131. package/dist/html/shared/styles/components.css +1511 -0
  132. package/dist/html/shared/styles/knowledge.css +242 -0
  133. package/dist/html/shared/styles/strategic-insights.css +1337 -0
  134. package/dist/html/shared/weekGrouper.js +27 -0
  135. package/dist/html/types.js +7 -0
  136. package/dist/index.js +39 -39
  137. package/dist/test/helpers/commitFactory.js +166 -0
  138. package/dist/test/helpers/dateUtils.js +101 -0
  139. package/dist/test/helpers/index.js +29 -0
  140. package/dist/test/setup.js +17 -0
  141. package/dist/test/smoke.test.js +94 -0
  142. package/dist/types/achievements.js +7 -0
  143. package/dist/types/analysis.js +7 -0
  144. package/dist/types/core.js +7 -0
  145. package/dist/types/index.js +38 -0
  146. package/dist/types/options.js +7 -0
  147. package/dist/types/shared.js +7 -0
  148. package/dist/types/strategic.js +7 -0
  149. package/dist/types/summary.js +7 -0
  150. package/dist/utils/achievementDefinitions.js +22 -22
  151. package/dist/utils/analyzerContextBuilder.js +124 -0
  152. package/dist/utils/commitQualityAnalyzer.js +13 -2
  153. package/dist/utils/emptyResults.js +95 -0
  154. package/dist/utils/fileHotspotAnalyzer.js +4 -12
  155. package/dist/utils/gapAnalyzer.js +26 -28
  156. package/dist/utils/gitParser.test.js +363 -0
  157. package/dist/utils/htmlGenerator.js +62 -466
  158. package/dist/utils/impactAnalyzer.js +20 -19
  159. package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
  160. package/dist/utils/matrixGenerator.js +13 -13
  161. package/dist/utils/rangeComparisonAnalyzer.js +2 -2
  162. package/dist/utils/streakCalculator.js +77 -27
  163. package/dist/utils/teamAnalyzer.js +20 -1
  164. package/dist/utils/timePatternAnalyzer.js +18 -3
  165. package/dist/utils/velocityAnalyzer.js +23 -18
  166. package/dist/utils/wrappedGenerator.js +8 -8
  167. package/package.json +74 -64
  168. package/vitest.config.ts +46 -0
  169. package/SPECS.md +0 -490
  170. package/dist/cli.js +0 -24
  171. package/dist/commands/index.js +0 -24
  172. package/test-team.txt +0 -2
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeImpact = analyzeImpact;
4
+ const emptyResults_1 = require("../../utils/emptyResults");
5
+ // File weight patterns based on criticality
6
+ const FILE_WEIGHTS = [
7
+ { pattern: /^src\/(core|lib)\//, weight: 1.5, category: 'core' },
8
+ { pattern: /^(src|lib)\/.*\.(ts|js|tsx|jsx)$/, weight: 1.0, category: 'feature' },
9
+ { pattern: /(test|spec|__tests__)\/|\.test\.|\.spec\./, weight: 0.8, category: 'test' },
10
+ { pattern: /\.(config|rc)\.|\.env|tsconfig|webpack|vite|eslint|prettier/, weight: 0.6, category: 'config' },
11
+ { pattern: /\.md$|^docs\/|^README/, weight: 0.4, category: 'docs' },
12
+ { pattern: /package\.json|\.lock$|yarn\.lock|pnpm-lock/, weight: 0.3, category: 'config' },
13
+ ];
14
+ // Commit type weights based on conventional commits
15
+ const TYPE_WEIGHTS = {
16
+ feat: 1.5,
17
+ fix: 1.2,
18
+ refactor: 1.0,
19
+ perf: 1.3,
20
+ test: 0.9,
21
+ docs: 0.5,
22
+ chore: 0.4,
23
+ style: 0.3,
24
+ ci: 0.4,
25
+ build: 0.5,
26
+ };
27
+ /**
28
+ * Get file weight and category based on path
29
+ */
30
+ function getFileInfo(filePath) {
31
+ const normalizedPath = filePath.toLowerCase().replace(/\\/g, '/');
32
+ for (const { pattern, weight, category } of FILE_WEIGHTS) {
33
+ if (pattern.test(normalizedPath)) {
34
+ return { weight, category };
35
+ }
36
+ }
37
+ // Default for unknown files
38
+ return { weight: 0.7, category: 'feature' };
39
+ }
40
+ /**
41
+ * Extract commit type from conventional commit message
42
+ */
43
+ function getCommitType(message) {
44
+ const match = message.match(/^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?:/i);
45
+ return match ? match[1].toLowerCase() : null;
46
+ }
47
+ /**
48
+ * Calculate recency factor (recent changes weighted higher)
49
+ */
50
+ function getRecencyFactor(commitDate, now = new Date()) {
51
+ const daysSinceCommit = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
52
+ // Exponential decay over 90 days
53
+ if (daysSinceCommit <= 7)
54
+ return 1.0;
55
+ if (daysSinceCommit <= 30)
56
+ return 0.9;
57
+ if (daysSinceCommit <= 60)
58
+ return 0.7;
59
+ if (daysSinceCommit <= 90)
60
+ return 0.5;
61
+ return 0.3;
62
+ }
63
+ /**
64
+ * Extract files changed from commit message (simplified - assumes file paths in message)
65
+ * In a real implementation, this would come from git diff
66
+ */
67
+ function extractFilesFromCommit(commit) {
68
+ // For now, return empty - in a real implementation we'd parse git log --name-only
69
+ // This is a placeholder that allows the analyzer to work with existing data
70
+ return [];
71
+ }
72
+ /**
73
+ * Analyzes the impact of commits based on file criticality and change patterns.
74
+ *
75
+ * Calculates an overall impact score (0-100) by weighting commits based on:
76
+ * - File criticality (core files > features > tests > config > docs)
77
+ * - Commit type (feat/perf > fix/refactor > test > docs/chore)
78
+ * - Recency (recent changes weighted higher)
79
+ *
80
+ * @param commits - Array of commit data to analyze
81
+ * @param changedFiles - Optional pre-computed file change data from fileHotspotAnalyzer
82
+ * @returns Impact analysis with score breakdown, top files, trend, and insights
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const impact = analyzeImpact(commits);
87
+ * console.log(`Impact score: ${impact.overallScore}/100`);
88
+ * console.log(`Trend: ${impact.impactTrend}`);
89
+ * console.log(`Core contributions: ${impact.scoreBreakdown.coreContributions} points`);
90
+ * ```
91
+ */
92
+ function analyzeImpact(commits, changedFiles) {
93
+ if (commits.length === 0) {
94
+ return (0, emptyResults_1.getEmptyImpactAnalysis)();
95
+ }
96
+ const now = new Date();
97
+ const fileImpacts = new Map();
98
+ let coreContributions = 0;
99
+ let featureWork = 0;
100
+ let maintenanceWork = 0;
101
+ let documentationWork = 0;
102
+ let totalRawScore = 0;
103
+ let recentScore = 0; // Last 30 days
104
+ let olderScore = 0; // 30-90 days ago
105
+ // If we have file change data from fileHotspotAnalyzer, use it
106
+ if (changedFiles && changedFiles.size > 0) {
107
+ changedFiles.forEach((data, filePath) => {
108
+ const { weight, category } = getFileInfo(filePath);
109
+ const impactScore = data.count * weight;
110
+ fileImpacts.set(filePath, {
111
+ score: impactScore,
112
+ count: data.count,
113
+ lastChanged: data.lastChanged,
114
+ category
115
+ });
116
+ totalRawScore += impactScore;
117
+ // Categorize contributions
118
+ switch (category) {
119
+ case 'core':
120
+ coreContributions += impactScore;
121
+ break;
122
+ case 'test':
123
+ case 'config':
124
+ maintenanceWork += impactScore;
125
+ break;
126
+ case 'docs':
127
+ documentationWork += impactScore;
128
+ break;
129
+ default:
130
+ featureWork += impactScore;
131
+ }
132
+ });
133
+ }
134
+ // Process commits for type-based weighting and recency
135
+ commits.forEach(commit => {
136
+ const commitDate = new Date(commit.date);
137
+ const commitType = getCommitType(commit.message);
138
+ const typeWeight = commitType ? (TYPE_WEIGHTS[commitType] || 1.0) : 1.0;
139
+ const recencyFactor = getRecencyFactor(commitDate, now);
140
+ const daysSinceCommit = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
141
+ const baseScore = typeWeight * recencyFactor;
142
+ if (daysSinceCommit <= 30) {
143
+ recentScore += baseScore;
144
+ }
145
+ else if (daysSinceCommit <= 90) {
146
+ olderScore += baseScore;
147
+ }
148
+ // Additional category scoring based on commit type
149
+ if (commitType === 'feat') {
150
+ featureWork += typeWeight;
151
+ }
152
+ else if (commitType === 'fix' || commitType === 'refactor') {
153
+ maintenanceWork += typeWeight;
154
+ }
155
+ else if (commitType === 'docs') {
156
+ documentationWork += typeWeight;
157
+ }
158
+ });
159
+ // Normalize overall score to 0-100
160
+ const maxPossibleScore = commits.length * 1.5 * 1.0; // Max type weight * max recency
161
+ const normalizedScore = Math.min(100, Math.round((totalRawScore + recentScore) / Math.max(maxPossibleScore, 1) * 100));
162
+ // Get top 10 impact files
163
+ const topImpactFiles = Array.from(fileImpacts.entries())
164
+ .sort((a, b) => b[1].score - a[1].score)
165
+ .slice(0, 10)
166
+ .map(([path, data]) => ({
167
+ path,
168
+ impactScore: Math.round(data.score * 10) / 10,
169
+ changeCount: data.count,
170
+ lastChanged: data.lastChanged,
171
+ category: data.category
172
+ }));
173
+ // Determine trend
174
+ let impactTrend = 'stable';
175
+ if (recentScore > olderScore * 1.2) {
176
+ impactTrend = 'increasing';
177
+ }
178
+ else if (recentScore < olderScore * 0.8) {
179
+ impactTrend = 'decreasing';
180
+ }
181
+ // Generate insights
182
+ const insights = generateInsights({
183
+ coreContributions,
184
+ featureWork,
185
+ maintenanceWork,
186
+ documentationWork,
187
+ topImpactFiles,
188
+ impactTrend,
189
+ totalCommits: commits.length
190
+ });
191
+ // Normalize breakdown scores
192
+ const breakdownTotal = coreContributions + featureWork + maintenanceWork + documentationWork || 1;
193
+ return {
194
+ overallScore: normalizedScore,
195
+ scoreBreakdown: {
196
+ coreContributions: Math.round((coreContributions / breakdownTotal) * 100),
197
+ featureWork: Math.round((featureWork / breakdownTotal) * 100),
198
+ maintenanceWork: Math.round((maintenanceWork / breakdownTotal) * 100),
199
+ documentationWork: Math.round((documentationWork / breakdownTotal) * 100),
200
+ },
201
+ topImpactFiles,
202
+ impactTrend,
203
+ insights
204
+ };
205
+ }
206
+ /**
207
+ * Generate meaningful insights from impact data
208
+ */
209
+ function generateInsights(data) {
210
+ const insights = [];
211
+ const total = data.coreContributions + data.featureWork + data.maintenanceWork + data.documentationWork || 1;
212
+ // Core contributions insight
213
+ const corePercentage = (data.coreContributions / total) * 100;
214
+ if (corePercentage > 30) {
215
+ insights.push(`Strong focus on core modules — ${corePercentage.toFixed(0)}% of impact from critical paths`);
216
+ }
217
+ // Feature work insight
218
+ const featurePercentage = (data.featureWork / total) * 100;
219
+ if (featurePercentage > 50) {
220
+ insights.push(`Feature-focused development — ${featurePercentage.toFixed(0)}% of contributions are new functionality`);
221
+ }
222
+ // Maintenance insight
223
+ const maintenancePercentage = (data.maintenanceWork / total) * 100;
224
+ if (maintenancePercentage > 40) {
225
+ insights.push(`Strong maintenance focus — keeping the codebase healthy`);
226
+ }
227
+ // Documentation insight
228
+ const docPercentage = (data.documentationWork / total) * 100;
229
+ if (docPercentage < 5 && data.totalCommits > 20) {
230
+ insights.push(`Documentation contributions below average — consider adding READMEs or inline docs`);
231
+ }
232
+ else if (docPercentage > 15) {
233
+ insights.push(`Good documentation practices — ${docPercentage.toFixed(0)}% of work includes docs`);
234
+ }
235
+ // Trend insight
236
+ if (data.impactTrend === 'increasing') {
237
+ insights.push(`Impact trending upward — recent contributions are higher value`);
238
+ }
239
+ else if (data.impactTrend === 'decreasing') {
240
+ insights.push(`Recent activity lower than historical average`);
241
+ }
242
+ // Top files insight
243
+ if (data.topImpactFiles.length > 0) {
244
+ const topFile = data.topImpactFiles[0];
245
+ insights.push(`Highest impact: ${topFile.path.split('/').pop()} (${topFile.changeCount} changes)`);
246
+ }
247
+ return insights.slice(0, 5); // Limit to 5 insights
248
+ }
@@ -0,0 +1,26 @@
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.buildImpactSection = exports.analyzeImpact = void 0;
18
+ /**
19
+ * Impact Feature
20
+ * @module features/impact
21
+ */
22
+ __exportStar(require("./types"), exports);
23
+ var analyzer_1 = require("./analyzer");
24
+ Object.defineProperty(exports, "analyzeImpact", { enumerable: true, get: function () { return analyzer_1.analyzeImpact; } });
25
+ var template_1 = require("./template");
26
+ Object.defineProperty(exports, "buildImpactSection", { enumerable: true, get: function () { return template_1.buildImpactSection; } });
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildImpactSection = buildImpactSection;
4
+ /**
5
+ * Build the Impact Analysis section for the dashboard
6
+ */
7
+ function buildImpactSection(impact) {
8
+ const trendIcon = impact.impactTrend === 'increasing' ? '↑' : impact.impactTrend === 'decreasing' ? '↓' : '→';
9
+ const trendClass = impact.impactTrend === 'increasing' ? 'trend-up' : impact.impactTrend === 'decreasing' ? 'trend-down' : 'trend-stable';
10
+ return `
11
+ <div class="impact-section">
12
+ <h2>Impact Analysis</h2>
13
+
14
+ <div class="impact-content">
15
+ <div class="impact-overview">
16
+ <div class="impact-score-display">
17
+ <div class="score-circle">
18
+ <span class="score-value">${impact.overallScore}</span>
19
+ <span class="score-max">/100</span>
20
+ </div>
21
+ <div class="score-meta">
22
+ <span class="score-label">Impact Score</span>
23
+ <span class="score-trend ${trendClass}">${trendIcon} ${impact.impactTrend}</span>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="impact-breakdown">
28
+ <h3>Contribution Breakdown</h3>
29
+ <div class="breakdown-items">
30
+ <div class="breakdown-item">
31
+ <div class="breakdown-header">
32
+ <span class="breakdown-label">Core Contributions <span class="info-tooltip" title="Changes to src/core/ and src/lib/ folders - foundational code">ⓘ</span></span>
33
+ <span class="breakdown-value">${impact.scoreBreakdown.coreContributions}%</span>
34
+ </div>
35
+ <div class="breakdown-bar">
36
+ <div class="breakdown-fill core" style="width: ${impact.scoreBreakdown.coreContributions}%"></div>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="breakdown-item">
41
+ <div class="breakdown-header">
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>
43
+ <span class="breakdown-value">${impact.scoreBreakdown.featureWork}%</span>
44
+ </div>
45
+ <div class="breakdown-bar">
46
+ <div class="breakdown-fill feature" style="width: ${impact.scoreBreakdown.featureWork}%"></div>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="breakdown-item">
51
+ <div class="breakdown-header">
52
+ <span class="breakdown-label">Maintenance <span class="info-tooltip" title="Changes to tests, configs, CI/CD, and build files">ⓘ</span></span>
53
+ <span class="breakdown-value">${impact.scoreBreakdown.maintenanceWork}%</span>
54
+ </div>
55
+ <div class="breakdown-bar">
56
+ <div class="breakdown-fill maintenance" style="width: ${impact.scoreBreakdown.maintenanceWork}%"></div>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="breakdown-item">
61
+ <div class="breakdown-header">
62
+ <span class="breakdown-label">Documentation <span class="info-tooltip" title="Changes to .md files, README, and docs/ folder">ⓘ</span></span>
63
+ <span class="breakdown-value">${impact.scoreBreakdown.documentationWork}%</span>
64
+ </div>
65
+ <div class="breakdown-bar">
66
+ <div class="breakdown-fill docs" style="width: ${impact.scoreBreakdown.documentationWork}%"></div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ ${impact.topImpactFiles.length > 0 ? `
74
+ <div class="impact-files">
75
+ <h3>Highest Impact Changes</h3>
76
+ <table class="data-table">
77
+ <thead>
78
+ <tr>
79
+ <th>File</th>
80
+ <th>Category</th>
81
+ <th>Changes</th>
82
+ <th>Impact</th>
83
+ </tr>
84
+ </thead>
85
+ <tbody>
86
+ ${impact.topImpactFiles.map(file => `
87
+ <tr>
88
+ <td class="file-path">${truncatePath(file.path)}</td>
89
+ <td><span class="category-badge ${file.category}">${file.category}</span></td>
90
+ <td>${file.changeCount}</td>
91
+ <td><span class="impact-badge">${file.impactScore.toFixed(1)}</span></td>
92
+ </tr>
93
+ `).join('')}
94
+ </tbody>
95
+ </table>
96
+ </div>
97
+ ` : ''}
98
+
99
+ ${impact.insights.length > 0 ? `
100
+ <div class="insights-list">
101
+ <h3>Insights</h3>
102
+ <ul>
103
+ ${impact.insights.map(insight => `<li>💡 ${insight}</li>`).join('')}
104
+ </ul>
105
+ </div>
106
+ ` : ''}
107
+ </div>
108
+ </div>
109
+ `;
110
+ }
111
+ /**
112
+ * Truncate file path for display
113
+ */
114
+ function truncatePath(path, maxLength = 40) {
115
+ if (path.length <= maxLength)
116
+ return path;
117
+ return '...' + path.slice(-maxLength + 3);
118
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * Features Module
4
+ *
5
+ * Central export for all feature modules.
6
+ * Each feature is self-contained with its own types, logic, and templates.
7
+ *
8
+ * @module features
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
22
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ // Core features
26
+ __exportStar(require("./achievements"), exports);
27
+ __exportStar(require("./streaks"), exports);
28
+ __exportStar(require("./time-patterns"), exports);
29
+ // Analysis features
30
+ __exportStar(require("./velocity"), exports);
31
+ __exportStar(require("./gaps"), exports);
32
+ __exportStar(require("./knowledge"), exports);
33
+ __exportStar(require("./impact"), exports);
34
+ __exportStar(require("./commit-quality"), exports);
35
+ __exportStar(require("./team"), exports);
36
+ __exportStar(require("./comparison"), exports);
37
+ // Content features
38
+ __exportStar(require("./executive-summary"), exports);
39
+ __exportStar(require("./events"), exports);
40
+ __exportStar(require("./contribution-graph"), exports);