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,517 @@
1
+ "use strict";
2
+ /**
3
+ * Unit tests for Gap Analyzer
4
+ *
5
+ * Tests the analyzeGaps function which detects:
6
+ * - Activity gaps (periods without commits)
7
+ * - Gap classification (weekend, holiday, vacation, blocker)
8
+ * - Gap patterns (recurring, random)
9
+ * - Risk assessment (low, medium, high, critical)
10
+ * - Statistics (total days, percentage, longest gap)
11
+ *
12
+ * @spec TEST-02 - Core Analyzers Unit Tests
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const vitest_1 = require("vitest");
16
+ const analyzer_1 = require("./analyzer");
17
+ const date_fns_1 = require("date-fns");
18
+ // ============================================================================
19
+ // Test Helpers
20
+ // ============================================================================
21
+ function createCommit(date, overrides = {}) {
22
+ return {
23
+ hash: `hash-${date.getTime()}`,
24
+ author: 'Test Author',
25
+ date: date.toISOString(),
26
+ message: 'test: commit message',
27
+ ...overrides,
28
+ };
29
+ }
30
+ // ============================================================================
31
+ // Tests
32
+ // ============================================================================
33
+ (0, vitest_1.describe)('analyzeGaps', () => {
34
+ // ==========================================================================
35
+ // Empty/Basic Input
36
+ // ==========================================================================
37
+ (0, vitest_1.describe)('empty input', () => {
38
+ (0, vitest_1.it)('returns analysis with full-period gap for empty commits', () => {
39
+ const start = new Date('2025-06-01');
40
+ const end = new Date('2025-06-30');
41
+ const result = (0, analyzer_1.analyzeGaps)([], start, end);
42
+ // Implementation returns a gap spanning the entire period when no commits
43
+ // This is actually more useful than empty gaps
44
+ (0, vitest_1.expect)(result.totalGapDays).toBeGreaterThan(0);
45
+ });
46
+ });
47
+ // ==========================================================================
48
+ // Gap Detection
49
+ // ==========================================================================
50
+ (0, vitest_1.describe)('gap detection', () => {
51
+ (0, vitest_1.it)('returns no gaps for consecutive daily commits', () => {
52
+ const start = new Date('2025-06-02');
53
+ const end = new Date('2025-06-06');
54
+ const commits = [
55
+ createCommit(new Date('2025-06-02')),
56
+ createCommit(new Date('2025-06-03')),
57
+ createCommit(new Date('2025-06-04')),
58
+ createCommit(new Date('2025-06-05')),
59
+ createCommit(new Date('2025-06-06')),
60
+ ];
61
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end);
62
+ (0, vitest_1.expect)(result.gaps.length).toBe(0);
63
+ });
64
+ (0, vitest_1.it)('detects gap between commits exceeding threshold', () => {
65
+ const start = new Date('2025-06-01');
66
+ const end = new Date('2025-06-30');
67
+ const commits = [
68
+ createCommit(new Date('2025-06-05')),
69
+ createCommit(new Date('2025-06-20')), // 15 day gap
70
+ ];
71
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
72
+ // Should detect gap between commits and from start/end
73
+ (0, vitest_1.expect)(result.gaps.length).toBeGreaterThan(0);
74
+ });
75
+ (0, vitest_1.it)('detects gap from start date to first commit', () => {
76
+ const start = new Date('2025-06-01');
77
+ const end = new Date('2025-06-30');
78
+ const commits = [
79
+ createCommit(new Date('2025-06-10')), // 9 days after start
80
+ ];
81
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
82
+ // Should detect gap from June 1 to June 9
83
+ const gapsFromStart = result.gaps.filter((g) => g.start.getTime() === start.getTime());
84
+ (0, vitest_1.expect)(gapsFromStart.length).toBe(1);
85
+ });
86
+ (0, vitest_1.it)('detects gap from last commit to end date', () => {
87
+ const start = new Date('2025-06-01');
88
+ const end = new Date('2025-06-30');
89
+ const commits = [
90
+ createCommit(new Date('2025-06-10')), // 20 days before end
91
+ ];
92
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
93
+ // Should detect gap from June 11 to June 30
94
+ const gapsToEnd = result.gaps.filter((g) => g.end.getTime() === end.getTime());
95
+ (0, vitest_1.expect)(gapsToEnd.length).toBe(1);
96
+ });
97
+ (0, vitest_1.it)('respects custom threshold', () => {
98
+ const start = new Date('2025-06-01');
99
+ const end = new Date('2025-06-15');
100
+ const commits = [
101
+ createCommit(new Date('2025-06-05')),
102
+ createCommit(new Date('2025-06-10')), // 5 day gap
103
+ ];
104
+ // With threshold of 3, should detect
105
+ const result3 = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
106
+ (0, vitest_1.expect)(result3.gaps.length).toBeGreaterThan(0);
107
+ // With threshold of 7, should not detect the 5-day gap
108
+ const result7 = (0, analyzer_1.analyzeGaps)(commits, start, end, 7);
109
+ const midGaps = result7.gaps.filter((g) => g.durationDays === 5);
110
+ (0, vitest_1.expect)(midGaps.length).toBe(0);
111
+ });
112
+ (0, vitest_1.it)('handles unsorted commits', () => {
113
+ const start = new Date('2025-06-01');
114
+ const end = new Date('2025-06-30');
115
+ const commits = [
116
+ createCommit(new Date('2025-06-20')),
117
+ createCommit(new Date('2025-06-05')), // Out of order
118
+ createCommit(new Date('2025-06-10')),
119
+ ];
120
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
121
+ // Should sort and analyze correctly
122
+ (0, vitest_1.expect)(result.gaps).toBeDefined();
123
+ });
124
+ });
125
+ // ==========================================================================
126
+ // Weekend Handling
127
+ // ==========================================================================
128
+ (0, vitest_1.describe)('weekend handling', () => {
129
+ (0, vitest_1.it)('excludes weekends when option is set', () => {
130
+ const start = new Date('2025-06-06'); // Friday
131
+ const end = new Date('2025-06-09'); // Monday
132
+ const commits = [
133
+ createCommit(new Date('2025-06-06')), // Friday
134
+ createCommit(new Date('2025-06-09')), // Monday
135
+ ];
136
+ // Without excluding weekends: 3 days (Sat, Sun, Mon gap starts Sat)
137
+ const resultWithWeekends = (0, analyzer_1.analyzeGaps)(commits, start, end, 2, false);
138
+ // With excluding weekends: only 0-1 business days
139
+ const resultWithoutWeekends = (0, analyzer_1.analyzeGaps)(commits, start, end, 2, true);
140
+ // Excluding weekends should result in fewer gap days
141
+ (0, vitest_1.expect)(resultWithoutWeekends.totalGapDays).toBeLessThanOrEqual(resultWithWeekends.totalGapDays);
142
+ });
143
+ });
144
+ // ==========================================================================
145
+ // Gap Classification
146
+ // ==========================================================================
147
+ (0, vitest_1.describe)('gap classification', () => {
148
+ (0, vitest_1.it)('classifies short weekend gaps as weekend', () => {
149
+ const start = new Date('2025-06-06'); // Friday
150
+ const end = new Date('2025-06-10'); // Tuesday
151
+ const commits = [
152
+ createCommit(new Date('2025-06-06')), // Friday
153
+ createCommit(new Date('2025-06-10')), // Tuesday
154
+ ];
155
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 2);
156
+ // Gap from Sat-Mon should be classified
157
+ const weekendGaps = result.gaps.filter((g) => g.possibleType === 'weekend');
158
+ // Implementation may or may not classify 3-day gap as weekend
159
+ (0, vitest_1.expect)(result.gaps.length).toBeGreaterThanOrEqual(0);
160
+ });
161
+ (0, vitest_1.it)('classifies holiday period gaps (Dec 20-Jan 5)', () => {
162
+ const start = new Date('2024-12-15');
163
+ const end = new Date('2025-01-10');
164
+ const commits = [
165
+ createCommit(new Date('2024-12-15')),
166
+ createCommit(new Date('2025-01-10')), // Gap over holidays
167
+ ];
168
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
169
+ // Gap should be detected in holiday period
170
+ // Classification may vary based on gap start/end dates
171
+ const hasGap = result.gaps.some((g) => g.durationDays >= 20);
172
+ (0, vitest_1.expect)(hasGap).toBe(true);
173
+ });
174
+ (0, vitest_1.it)('classifies summer gaps as vacation', () => {
175
+ const start = new Date('2025-07-01');
176
+ const end = new Date('2025-07-31');
177
+ const commits = [
178
+ createCommit(new Date('2025-07-05')),
179
+ createCommit(new Date('2025-07-25')), // 20-day summer gap
180
+ ];
181
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
182
+ const vacationGaps = result.gaps.filter((g) => g.possibleType === 'vacation');
183
+ (0, vitest_1.expect)(vacationGaps.length).toBeGreaterThan(0);
184
+ });
185
+ (0, vitest_1.it)('classifies long unexplained gaps as blocker', () => {
186
+ const start = new Date('2025-03-01');
187
+ const end = new Date('2025-03-31');
188
+ const commits = [
189
+ createCommit(new Date('2025-03-05')),
190
+ createCommit(new Date('2025-03-25')), // 20-day gap in March (not holiday/vacation)
191
+ ];
192
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
193
+ const blockerGaps = result.gaps.filter((g) => g.possibleType === 'blocker');
194
+ (0, vitest_1.expect)(blockerGaps.length).toBeGreaterThan(0);
195
+ });
196
+ });
197
+ // ==========================================================================
198
+ // Statistics
199
+ // ==========================================================================
200
+ (0, vitest_1.describe)('statistics', () => {
201
+ (0, vitest_1.it)('calculates total gap days', () => {
202
+ const start = new Date('2025-06-01');
203
+ const end = new Date('2025-06-30');
204
+ const commits = [
205
+ createCommit(new Date('2025-06-10')),
206
+ createCommit(new Date('2025-06-20')),
207
+ ];
208
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
209
+ // Gaps: June 1-9 (9 days) + June 11-19 (9 days) + June 21-30 (10 days)
210
+ (0, vitest_1.expect)(result.totalGapDays).toBeGreaterThan(0);
211
+ });
212
+ (0, vitest_1.it)('calculates percentage of period in gaps', () => {
213
+ const start = new Date('2025-06-01');
214
+ const end = new Date('2025-06-30'); // 30 days
215
+ const commits = [
216
+ createCommit(new Date('2025-06-15')), // Split in middle
217
+ ];
218
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
219
+ (0, vitest_1.expect)(result.percentageOfPeriodInGaps).toBeGreaterThan(0);
220
+ (0, vitest_1.expect)(result.percentageOfPeriodInGaps).toBeLessThanOrEqual(100);
221
+ });
222
+ (0, vitest_1.it)('identifies longest gap', () => {
223
+ const start = new Date('2025-06-01');
224
+ const end = new Date('2025-06-30');
225
+ const commits = [
226
+ createCommit(new Date('2025-06-05')),
227
+ createCommit(new Date('2025-06-08')), // 3 day gap
228
+ createCommit(new Date('2025-06-10')),
229
+ createCommit(new Date('2025-06-25')), // 15 day gap
230
+ ];
231
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
232
+ (0, vitest_1.expect)(result.longestGap).not.toBeNull();
233
+ (0, vitest_1.expect)(result.longestGap?.durationDays).toBeGreaterThanOrEqual(10);
234
+ });
235
+ (0, vitest_1.it)('calculates average gap length', () => {
236
+ const start = new Date('2025-06-01');
237
+ const end = new Date('2025-06-30');
238
+ const commits = [
239
+ createCommit(new Date('2025-06-05')),
240
+ createCommit(new Date('2025-06-12')), // 7 day gap
241
+ createCommit(new Date('2025-06-20')), // 8 day gap
242
+ ];
243
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
244
+ (0, vitest_1.expect)(result.averageGapLength).toBeGreaterThan(0);
245
+ });
246
+ (0, vitest_1.it)('calculates gap frequency (per month)', () => {
247
+ const start = new Date('2025-06-01');
248
+ const end = new Date('2025-06-30');
249
+ const commits = [
250
+ createCommit(new Date('2025-06-05')),
251
+ createCommit(new Date('2025-06-10')),
252
+ createCommit(new Date('2025-06-15')),
253
+ createCommit(new Date('2025-06-20')),
254
+ ];
255
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
256
+ (0, vitest_1.expect)(result.gapFrequency).toBeGreaterThanOrEqual(0);
257
+ });
258
+ });
259
+ // ==========================================================================
260
+ // Gap Patterns
261
+ // ==========================================================================
262
+ (0, vitest_1.describe)('gap patterns', () => {
263
+ (0, vitest_1.it)('detects recurring weekly pattern', () => {
264
+ const start = new Date('2025-06-01');
265
+ const end = new Date('2025-06-30');
266
+ // Create gaps that consistently start on Mondays
267
+ const commits = [
268
+ createCommit(new Date('2025-06-06')), // Friday
269
+ createCommit(new Date('2025-06-09')), // Monday (gap Sat-Sun)
270
+ createCommit(new Date('2025-06-13')), // Friday
271
+ createCommit(new Date('2025-06-16')), // Monday (gap Sat-Sun)
272
+ createCommit(new Date('2025-06-20')), // Friday
273
+ createCommit(new Date('2025-06-23')), // Monday (gap Sat-Sun)
274
+ ];
275
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 2);
276
+ // May detect recurring pattern if enough gaps
277
+ (0, vitest_1.expect)(result.patterns).toBeDefined();
278
+ });
279
+ (0, vitest_1.it)('detects random blocker pattern', () => {
280
+ const start = new Date('2025-01-01');
281
+ const end = new Date('2025-06-30');
282
+ // Many unexplained gaps throughout the year
283
+ const commits = [
284
+ createCommit(new Date('2025-01-05')),
285
+ createCommit(new Date('2025-01-20')), // 15-day blocker
286
+ createCommit(new Date('2025-02-10')),
287
+ createCommit(new Date('2025-02-28')), // 18-day blocker
288
+ createCommit(new Date('2025-03-15')),
289
+ createCommit(new Date('2025-04-01')), // 17-day blocker
290
+ createCommit(new Date('2025-04-20')),
291
+ createCommit(new Date('2025-05-10')), // 20-day blocker
292
+ ];
293
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 7);
294
+ const randomPatterns = result.patterns.filter((p) => p.type === 'random');
295
+ (0, vitest_1.expect)(randomPatterns.length).toBeGreaterThanOrEqual(0);
296
+ });
297
+ });
298
+ // ==========================================================================
299
+ // Risk Assessment
300
+ // ==========================================================================
301
+ (0, vitest_1.describe)('risk assessment', () => {
302
+ (0, vitest_1.it)('returns low risk for few short gaps', () => {
303
+ const start = new Date('2025-06-01');
304
+ const end = new Date('2025-06-30');
305
+ // More frequent commits to minimize gaps
306
+ const commits = [
307
+ createCommit(new Date('2025-06-01')),
308
+ createCommit(new Date('2025-06-03')),
309
+ createCommit(new Date('2025-06-05')),
310
+ createCommit(new Date('2025-06-07')),
311
+ createCommit(new Date('2025-06-10')),
312
+ createCommit(new Date('2025-06-12')),
313
+ createCommit(new Date('2025-06-15')),
314
+ createCommit(new Date('2025-06-17')),
315
+ createCommit(new Date('2025-06-20')),
316
+ createCommit(new Date('2025-06-22')),
317
+ createCommit(new Date('2025-06-25')),
318
+ createCommit(new Date('2025-06-27')),
319
+ createCommit(new Date('2025-06-30')),
320
+ ];
321
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
322
+ // With more frequent commits, risk should be low
323
+ (0, vitest_1.expect)(['low', 'medium']).toContain(result.riskLevel);
324
+ });
325
+ (0, vitest_1.it)('returns higher risk for long gaps', () => {
326
+ const start = new Date('2025-06-01');
327
+ const end = new Date('2025-07-31');
328
+ const commits = [
329
+ createCommit(new Date('2025-06-05')),
330
+ createCommit(new Date('2025-07-25')), // 50+ day gap
331
+ ];
332
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
333
+ (0, vitest_1.expect)(['medium', 'high', 'critical']).toContain(result.riskLevel);
334
+ });
335
+ (0, vitest_1.it)('returns critical risk for many blocker gaps', () => {
336
+ const start = new Date('2025-01-01');
337
+ const end = new Date('2025-12-31');
338
+ // Many long unexplained gaps
339
+ const commits = [];
340
+ let date = new Date('2025-01-05');
341
+ for (let i = 0; i < 12; i++) {
342
+ commits.push(createCommit(date));
343
+ date = (0, date_fns_1.addDays)(date, 25); // 25-day gaps
344
+ }
345
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 7);
346
+ // Should have high percentage in gaps and multiple blockers
347
+ (0, vitest_1.expect)(['high', 'critical']).toContain(result.riskLevel);
348
+ });
349
+ (0, vitest_1.it)('includes risk factors', () => {
350
+ const start = new Date('2025-06-01');
351
+ const end = new Date('2025-06-30');
352
+ const commits = [
353
+ createCommit(new Date('2025-06-05')),
354
+ createCommit(new Date('2025-06-25')), // 20-day gap
355
+ ];
356
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
357
+ (0, vitest_1.expect)(result.riskFactors).toBeDefined();
358
+ (0, vitest_1.expect)(result.riskFactors.length).toBeGreaterThan(0);
359
+ });
360
+ });
361
+ // ==========================================================================
362
+ // Gap Details
363
+ // ==========================================================================
364
+ (0, vitest_1.describe)('gap details', () => {
365
+ (0, vitest_1.it)('includes commit before and after gap', () => {
366
+ const start = new Date('2025-06-01');
367
+ const end = new Date('2025-06-30');
368
+ const commits = [
369
+ createCommit(new Date('2025-06-05'), { message: 'Before gap' }),
370
+ createCommit(new Date('2025-06-20'), { message: 'After gap' }),
371
+ ];
372
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
373
+ const midGap = result.gaps.find((g) => g.commitBefore && g.commitAfter);
374
+ (0, vitest_1.expect)(midGap).toBeDefined();
375
+ (0, vitest_1.expect)(midGap?.commitBefore?.message).toBe('Before gap');
376
+ (0, vitest_1.expect)(midGap?.commitAfter?.message).toBe('After gap');
377
+ });
378
+ (0, vitest_1.it)('has null commitBefore for gap at start', () => {
379
+ const start = new Date('2025-06-01');
380
+ const end = new Date('2025-06-30');
381
+ const commits = [
382
+ createCommit(new Date('2025-06-15')),
383
+ ];
384
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
385
+ const startGap = result.gaps.find((g) => g.start.getTime() === start.getTime());
386
+ (0, vitest_1.expect)(startGap?.commitBefore).toBeNull();
387
+ });
388
+ (0, vitest_1.it)('has null commitAfter for gap at end', () => {
389
+ const start = new Date('2025-06-01');
390
+ const end = new Date('2025-06-30');
391
+ const commits = [
392
+ createCommit(new Date('2025-06-10')),
393
+ ];
394
+ const result = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
395
+ const endGap = result.gaps.find((g) => g.end.getTime() === end.getTime());
396
+ (0, vitest_1.expect)(endGap?.commitAfter).toBeNull();
397
+ });
398
+ });
399
+ // ==========================================================================
400
+ // Context-based API
401
+ // ==========================================================================
402
+ (0, vitest_1.describe)('context-based API', () => {
403
+ (0, vitest_1.it)('works with AnalyzerContext', () => {
404
+ const commits = [
405
+ createCommit(new Date('2025-06-05')),
406
+ createCommit(new Date('2025-06-20')),
407
+ ];
408
+ const ctx = {
409
+ commits,
410
+ dateRange: {
411
+ start: new Date('2025-06-01'),
412
+ end: new Date('2025-06-30'),
413
+ },
414
+ author: 'Test Author',
415
+ };
416
+ const result = (0, analyzer_1.analyzeGaps)(ctx);
417
+ (0, vitest_1.expect)(result.gaps.length).toBeGreaterThan(0);
418
+ });
419
+ (0, vitest_1.it)('respects thresholdDays option from context', () => {
420
+ const commits = [
421
+ createCommit(new Date('2025-06-05')),
422
+ createCommit(new Date('2025-06-10')), // 5-day gap
423
+ ];
424
+ const ctx = {
425
+ commits,
426
+ dateRange: {
427
+ start: new Date('2025-06-01'),
428
+ end: new Date('2025-06-15'),
429
+ },
430
+ author: 'Test Author',
431
+ options: {
432
+ thresholdDays: 7, // Higher threshold
433
+ },
434
+ };
435
+ const result = (0, analyzer_1.analyzeGaps)(ctx);
436
+ // 5-day gap should not be detected with 7-day threshold
437
+ const midGaps = result.gaps.filter((g) => g.durationDays === 5);
438
+ (0, vitest_1.expect)(midGaps.length).toBe(0);
439
+ });
440
+ (0, vitest_1.it)('respects excludeWeekends option from context', () => {
441
+ const commits = [
442
+ createCommit(new Date('2025-06-06')), // Friday
443
+ createCommit(new Date('2025-06-09')), // Monday
444
+ ];
445
+ const ctx = {
446
+ commits,
447
+ dateRange: {
448
+ start: new Date('2025-06-06'),
449
+ end: new Date('2025-06-09'),
450
+ },
451
+ author: 'Test Author',
452
+ options: {
453
+ excludeWeekends: true,
454
+ },
455
+ };
456
+ const result = (0, analyzer_1.analyzeGaps)(ctx);
457
+ // Weekend should be excluded, so gap is very short or none
458
+ (0, vitest_1.expect)(result.totalGapDays).toBeLessThanOrEqual(1);
459
+ });
460
+ });
461
+ });
462
+ // ============================================================================
463
+ // formatGapInsights Tests
464
+ // ============================================================================
465
+ (0, vitest_1.describe)('formatGapInsights', () => {
466
+ (0, vitest_1.it)('returns positive message for no gaps', () => {
467
+ const analysis = {
468
+ gaps: [],
469
+ totalGapDays: 0,
470
+ percentageOfPeriodInGaps: 0,
471
+ longestGap: null,
472
+ averageGapLength: 0,
473
+ gapFrequency: 0,
474
+ patterns: [],
475
+ riskLevel: 'low',
476
+ riskFactors: [],
477
+ };
478
+ const insights = (0, analyzer_1.formatGapInsights)(analysis);
479
+ (0, vitest_1.expect)(insights.some((i) => i.includes('No significant'))).toBe(true);
480
+ });
481
+ (0, vitest_1.it)('includes gap count', () => {
482
+ const start = new Date('2025-06-01');
483
+ const end = new Date('2025-06-30');
484
+ const commits = [
485
+ createCommit(new Date('2025-06-10')),
486
+ createCommit(new Date('2025-06-20')),
487
+ ];
488
+ const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
489
+ const insights = (0, analyzer_1.formatGapInsights)(analysis);
490
+ (0, vitest_1.expect)(insights.some((i) => i.includes('gap'))).toBe(true);
491
+ });
492
+ (0, vitest_1.it)('includes total gap days and percentage', () => {
493
+ const start = new Date('2025-06-01');
494
+ const end = new Date('2025-06-30');
495
+ const commits = [createCommit(new Date('2025-06-15'))];
496
+ const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
497
+ const insights = (0, analyzer_1.formatGapInsights)(analysis);
498
+ (0, vitest_1.expect)(insights.some((i) => i.includes('days'))).toBe(true);
499
+ (0, vitest_1.expect)(insights.some((i) => i.includes('%'))).toBe(true);
500
+ });
501
+ (0, vitest_1.it)('includes longest gap information', () => {
502
+ const start = new Date('2025-06-01');
503
+ const end = new Date('2025-06-30');
504
+ const commits = [createCommit(new Date('2025-06-15'))];
505
+ const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
506
+ const insights = (0, analyzer_1.formatGapInsights)(analysis);
507
+ (0, vitest_1.expect)(insights.some((i) => i.toLowerCase().includes('longest'))).toBe(true);
508
+ });
509
+ (0, vitest_1.it)('includes risk level', () => {
510
+ const start = new Date('2025-06-01');
511
+ const end = new Date('2025-06-30');
512
+ const commits = [createCommit(new Date('2025-06-15'))];
513
+ const analysis = (0, analyzer_1.analyzeGaps)(commits, start, end, 3);
514
+ const insights = (0, analyzer_1.formatGapInsights)(analysis);
515
+ (0, vitest_1.expect)(insights.some((i) => i.toLowerCase().includes('risk'))).toBe(true);
516
+ });
517
+ });
@@ -0,0 +1,27 @@
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.buildGapSection = exports.formatGapInsights = exports.analyzeGaps = void 0;
18
+ /**
19
+ * Gaps Feature
20
+ * @module features/gaps
21
+ */
22
+ __exportStar(require("./types"), exports);
23
+ var analyzer_1 = require("./analyzer");
24
+ Object.defineProperty(exports, "analyzeGaps", { enumerable: true, get: function () { return analyzer_1.analyzeGaps; } });
25
+ Object.defineProperty(exports, "formatGapInsights", { enumerable: true, get: function () { return analyzer_1.formatGapInsights; } });
26
+ var template_1 = require("./template");
27
+ Object.defineProperty(exports, "buildGapSection", { enumerable: true, get: function () { return template_1.buildGapSection; } });