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
@@ -59,10 +59,10 @@ exports.ACHIEVEMENTS = [
59
59
  id: 'champion',
60
60
  name: 'Champion',
61
61
  emoji: '🏆',
62
- description: 'Reached 500 commits',
62
+ description: 'Reached 200 commits',
63
63
  category: 'milestone',
64
- tier: 'platinum',
65
- criteria: { type: 'commit-count', threshold: 500, comparator: '>=' },
64
+ tier: 'gold',
65
+ criteria: { type: 'commit-count', threshold: 200, comparator: '>=' },
66
66
  progress: 0,
67
67
  isUnlocked: false,
68
68
  isSecret: false
@@ -71,10 +71,10 @@ exports.ACHIEVEMENTS = [
71
71
  id: 'master',
72
72
  name: 'Master',
73
73
  emoji: '💫',
74
- description: 'Reached 1000 commits',
74
+ description: 'Reached 300 commits',
75
75
  category: 'milestone',
76
76
  tier: 'platinum',
77
- criteria: { type: 'commit-count', threshold: 1000, comparator: '>=' },
77
+ criteria: { type: 'commit-count', threshold: 300, comparator: '>=' },
78
78
  progress: 0,
79
79
  isUnlocked: false,
80
80
  isSecret: false
@@ -83,10 +83,10 @@ exports.ACHIEVEMENTS = [
83
83
  id: 'legend',
84
84
  name: 'Legend',
85
85
  emoji: '🌟',
86
- description: 'Reached 5000 commits',
86
+ description: 'Reached 500 commits',
87
87
  category: 'milestone',
88
88
  tier: 'legendary',
89
- criteria: { type: 'commit-count', threshold: 5000, comparator: '>=' },
89
+ criteria: { type: 'commit-count', threshold: 500, comparator: '>=' },
90
90
  progress: 0,
91
91
  isUnlocked: false,
92
92
  isSecret: false
@@ -98,7 +98,7 @@ exports.ACHIEVEMENTS = [
98
98
  id: 'consistency',
99
99
  name: 'Consistency',
100
100
  emoji: '📅',
101
- description: 'Maintained a 3-day commit streak',
101
+ description: 'Maintained a 3-day business day streak',
102
102
  category: 'time',
103
103
  tier: 'bronze',
104
104
  criteria: { type: 'streak', threshold: 3, comparator: '>=' },
@@ -110,10 +110,10 @@ exports.ACHIEVEMENTS = [
110
110
  id: 'week-warrior',
111
111
  name: 'Week Warrior',
112
112
  emoji: '⚡',
113
- description: 'Maintained a 7-day commit streak',
113
+ description: 'Maintained a 5-day business day streak (full week)',
114
114
  category: 'time',
115
115
  tier: 'silver',
116
- criteria: { type: 'streak', threshold: 7, comparator: '>=' },
116
+ criteria: { type: 'streak', threshold: 5, comparator: '>=' },
117
117
  progress: 0,
118
118
  isUnlocked: false,
119
119
  isSecret: false
@@ -122,10 +122,10 @@ exports.ACHIEVEMENTS = [
122
122
  id: 'streak-on-fire',
123
123
  name: 'Streak On Fire',
124
124
  emoji: '🔥',
125
- description: 'Maintained a 14-day commit streak',
125
+ description: 'Maintained a 10-day business day streak (2 weeks)',
126
126
  category: 'time',
127
127
  tier: 'gold',
128
- criteria: { type: 'streak', threshold: 14, comparator: '>=' },
128
+ criteria: { type: 'streak', threshold: 10, comparator: '>=' },
129
129
  progress: 0,
130
130
  isUnlocked: false,
131
131
  isSecret: false
@@ -134,10 +134,10 @@ exports.ACHIEVEMENTS = [
134
134
  id: 'unstoppable',
135
135
  name: 'Unstoppable',
136
136
  emoji: '💪',
137
- description: 'Maintained a 30-day commit streak',
137
+ description: 'Maintained a 20-day business day streak (1 month)',
138
138
  category: 'time',
139
139
  tier: 'platinum',
140
- criteria: { type: 'streak', threshold: 30, comparator: '>=' },
140
+ criteria: { type: 'streak', threshold: 20, comparator: '>=' },
141
141
  progress: 0,
142
142
  isUnlocked: false,
143
143
  isSecret: false
@@ -146,10 +146,10 @@ exports.ACHIEVEMENTS = [
146
146
  id: 'iron-will',
147
147
  name: 'Iron Will',
148
148
  emoji: '🦾',
149
- description: 'Maintained a 60-day commit streak',
149
+ description: 'Maintained a 40-day business day streak (2 months)',
150
150
  category: 'time',
151
151
  tier: 'platinum',
152
- criteria: { type: 'streak', threshold: 60, comparator: '>=' },
152
+ criteria: { type: 'streak', threshold: 40, comparator: '>=' },
153
153
  progress: 0,
154
154
  isUnlocked: false,
155
155
  isSecret: false
@@ -158,10 +158,10 @@ exports.ACHIEVEMENTS = [
158
158
  id: 'code-machine',
159
159
  name: 'Code Machine',
160
160
  emoji: '👾',
161
- description: 'Maintained a 100-day commit streak',
161
+ description: 'Maintained a 60-day business day streak (3 months)',
162
162
  category: 'time',
163
163
  tier: 'legendary',
164
- criteria: { type: 'streak', threshold: 100, comparator: '>=' },
164
+ criteria: { type: 'streak', threshold: 60, comparator: '>=' },
165
165
  progress: 0,
166
166
  isUnlocked: false,
167
167
  isSecret: false
@@ -230,7 +230,7 @@ exports.ACHIEVEMENTS = [
230
230
  id: 'early-bird',
231
231
  name: 'Early Bird',
232
232
  emoji: '🌅',
233
- description: '50+ commits before 9am',
233
+ description: '20+ commits before 9am',
234
234
  category: 'time',
235
235
  tier: 'silver',
236
236
  criteria: {
@@ -240,7 +240,7 @@ exports.ACHIEVEMENTS = [
240
240
  const hour = new Date(c.date).getHours();
241
241
  return hour >= 5 && hour < 9;
242
242
  }).length;
243
- return earlyCommits >= 50;
243
+ return earlyCommits >= 20;
244
244
  }
245
245
  },
246
246
  progress: 0,
@@ -251,7 +251,7 @@ exports.ACHIEVEMENTS = [
251
251
  id: 'night-owl',
252
252
  name: 'Night Owl',
253
253
  emoji: '🦉',
254
- description: '50+ commits after 9pm',
254
+ description: '20+ commits after 9pm',
255
255
  category: 'time',
256
256
  tier: 'silver',
257
257
  criteria: {
@@ -261,7 +261,7 @@ exports.ACHIEVEMENTS = [
261
261
  const hour = new Date(c.date).getHours();
262
262
  return hour >= 21 || hour < 5;
263
263
  }).length;
264
- return lateCommits >= 50;
264
+ return lateCommits >= 20;
265
265
  }
266
266
  },
267
267
  progress: 0,
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Utility functions for building and working with AnalyzerContext.
4
+ *
5
+ * @module analyzerContextBuilder
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.isAnalyzerContext = isAnalyzerContext;
9
+ exports.buildContext = buildContext;
10
+ exports.getOption = getOption;
11
+ exports.computeFileChanges = computeFileChanges;
12
+ exports.withOptions = withOptions;
13
+ /**
14
+ * Type guard to check if an argument is an AnalyzerContext.
15
+ *
16
+ * @param arg - Value to check
17
+ * @returns True if the argument is an AnalyzerContext
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * function analyze(arg1: CommitData[] | AnalyzerContext, ...rest: unknown[]) {
22
+ * if (isAnalyzerContext(arg1)) {
23
+ * // Use arg1.commits, arg1.dateRange, etc.
24
+ * } else {
25
+ * // Use positional parameters
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ function isAnalyzerContext(arg) {
31
+ return (typeof arg === 'object' &&
32
+ arg !== null &&
33
+ 'commits' in arg &&
34
+ 'dateRange' in arg &&
35
+ typeof arg.dateRange === 'object' &&
36
+ arg.dateRange !== null &&
37
+ 'start' in arg.dateRange &&
38
+ 'end' in arg.dateRange);
39
+ }
40
+ /**
41
+ * Build an AnalyzerContext from individual parameters.
42
+ *
43
+ * @param commits - Array of commits to analyze
44
+ * @param startDate - Start of the analysis period
45
+ * @param endDate - End of the analysis period
46
+ * @param repoPath - Path to the repository (optional)
47
+ * @param options - Additional analyzer options (optional)
48
+ * @returns A fully constructed AnalyzerContext
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const ctx = buildContext(commits, startDate, endDate, '/path/to/repo', { windowWeeks: 8 });
53
+ * const velocity = analyzeVelocity(ctx);
54
+ * ```
55
+ */
56
+ function buildContext(commits, startDate, endDate, repoPath = '', options) {
57
+ return {
58
+ commits,
59
+ dateRange: {
60
+ start: startDate,
61
+ end: endDate,
62
+ },
63
+ repoPath,
64
+ options,
65
+ };
66
+ }
67
+ /**
68
+ * Get an option value from context with a default fallback.
69
+ *
70
+ * @param ctx - The analyzer context
71
+ * @param key - The option key to retrieve
72
+ * @param defaultValue - Default value if option is not set
73
+ * @returns The option value or the default
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const windowWeeks = getOption(ctx, 'windowWeeks', 4);
78
+ * const thresholdDays = getOption(ctx, 'thresholdDays', 3);
79
+ * ```
80
+ */
81
+ function getOption(ctx, key, defaultValue) {
82
+ return ctx.options?.[key] ?? defaultValue;
83
+ }
84
+ /**
85
+ * Compute file change data from commits (cached).
86
+ *
87
+ * This is an expensive operation that should only be done once
88
+ * and then shared across analyzers via the context.
89
+ *
90
+ * @param _repoPath - Path to the git repository
91
+ * @param _commits - Commits to analyze for file changes
92
+ * @returns Map of file paths to change data
93
+ *
94
+ * @remarks
95
+ * This is a placeholder for future implementation.
96
+ * Currently returns an empty map.
97
+ */
98
+ function computeFileChanges(_repoPath, _commits) {
99
+ // TODO: Implement git log --name-only parsing
100
+ // This would be called once and shared across impact/knowledge analyzers
101
+ return new Map();
102
+ }
103
+ /**
104
+ * Clone a context with merged options.
105
+ *
106
+ * @param ctx - Original context
107
+ * @param additionalOptions - Options to merge
108
+ * @returns New context with merged options
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const velocityCtx = withOptions(ctx, { windowWeeks: 8 });
113
+ * const gapCtx = withOptions(ctx, { thresholdDays: 5, excludeWeekends: true });
114
+ * ```
115
+ */
116
+ function withOptions(ctx, additionalOptions) {
117
+ return {
118
+ ...ctx,
119
+ options: {
120
+ ...ctx.options,
121
+ ...additionalOptions,
122
+ },
123
+ };
124
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.analyzeCommitQuality = analyzeCommitQuality;
4
4
  exports.getQualityRating = getQualityRating;
5
5
  exports.getQualityLevel = getQualityLevel;
6
+ const analyzerContextBuilder_1 = require("./analyzerContextBuilder");
6
7
  const CONVENTIONAL_COMMIT_REGEX = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?:\s.+/;
7
8
  const CODE_HYGIENE_PATTERNS = {
8
9
  quickFix: /fix(ed)?\s+(typo|spelling|grammar)/i,
@@ -24,8 +25,18 @@ const IMPERATIVE_STARTERS = [
24
25
  'improve', 'optimize', 'enhance', 'change', 'move', 'rename', 'extract',
25
26
  'merge', 'bump', 'revert', 'document', 'test', 'upgrade', 'downgrade'
26
27
  ];
27
- function analyzeCommitQuality(commits, options = {}) {
28
- const { skipBodyCheck = false } = options;
28
+ function analyzeCommitQuality(ctxOrCommits, options = {}) {
29
+ // Normalize to internal variables
30
+ let commits;
31
+ let skipBodyCheck;
32
+ if ((0, analyzerContextBuilder_1.isAnalyzerContext)(ctxOrCommits)) {
33
+ commits = ctxOrCommits.commits;
34
+ skipBodyCheck = (0, analyzerContextBuilder_1.getOption)(ctxOrCommits, 'skipBodyCheck', false);
35
+ }
36
+ else {
37
+ commits = ctxOrCommits;
38
+ skipBodyCheck = options.skipBodyCheck ?? false;
39
+ }
29
40
  if (commits.length === 0) {
30
41
  return getEmptyQuality();
31
42
  }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ /**
3
+ * Centralized empty result factories for all analyzers
4
+ * Provides consistent default values when there's no data to analyze
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getEmptyImpactAnalysis = getEmptyImpactAnalysis;
8
+ exports.getEmptyVelocityAnalysis = getEmptyVelocityAnalysis;
9
+ exports.getEmptyGapAnalysis = getEmptyGapAnalysis;
10
+ exports.getEmptyKnowledgeDistribution = getEmptyKnowledgeDistribution;
11
+ exports.getEmptyFileHotspotAnalysis = getEmptyFileHotspotAnalysis;
12
+ const date_fns_1 = require("date-fns");
13
+ /**
14
+ * Creates an empty impact analysis result
15
+ */
16
+ function getEmptyImpactAnalysis() {
17
+ return {
18
+ overallScore: 0,
19
+ scoreBreakdown: {
20
+ coreContributions: 0,
21
+ featureWork: 0,
22
+ maintenanceWork: 0,
23
+ documentationWork: 0,
24
+ },
25
+ topImpactFiles: [],
26
+ impactTrend: 'stable',
27
+ insights: ['No commit data available for analysis']
28
+ };
29
+ }
30
+ /**
31
+ * Creates an empty velocity analysis result
32
+ */
33
+ function getEmptyVelocityAnalysis() {
34
+ return {
35
+ timeline: [],
36
+ overallTrend: 'stable',
37
+ trendPercentage: 0,
38
+ averageCommitsPerWeek: 0,
39
+ peakWeek: { weekStart: new Date(), commits: 0 },
40
+ lowestWeek: { weekStart: new Date(), commits: 0 },
41
+ anomalies: [],
42
+ };
43
+ }
44
+ /**
45
+ * Creates an empty gap analysis result
46
+ */
47
+ function getEmptyGapAnalysis(startDate, endDate) {
48
+ const totalDays = (0, date_fns_1.differenceInDays)(endDate, startDate) + 1;
49
+ return {
50
+ gaps: [{
51
+ start: startDate,
52
+ end: endDate,
53
+ durationDays: totalDays,
54
+ commitBefore: null,
55
+ commitAfter: null,
56
+ possibleType: 'blocker',
57
+ }],
58
+ totalGapDays: totalDays,
59
+ percentageOfPeriodInGaps: 100,
60
+ longestGap: null,
61
+ averageGapLength: totalDays,
62
+ gapFrequency: 0,
63
+ patterns: [],
64
+ riskLevel: 'critical',
65
+ riskFactors: ['No commits found in the analysis period'],
66
+ };
67
+ }
68
+ /**
69
+ * Creates an empty knowledge distribution result
70
+ */
71
+ function getEmptyKnowledgeDistribution() {
72
+ return {
73
+ directories: [],
74
+ busFactorRisk: {
75
+ overall: 0,
76
+ level: 'low',
77
+ criticalPaths: []
78
+ },
79
+ knowledgeSilos: [],
80
+ sharedKnowledge: [],
81
+ recommendations: ['No commit data available for analysis']
82
+ };
83
+ }
84
+ /**
85
+ * Creates an empty file hotspot analysis result
86
+ */
87
+ function getEmptyFileHotspotAnalysis() {
88
+ return {
89
+ topFiles: [],
90
+ topDirectories: [],
91
+ technicalDebt: [],
92
+ ownershipRisks: [],
93
+ totalFilesAnalyzed: 0
94
+ };
95
+ }
@@ -0,0 +1,253 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadEventsFile = loadEventsFile;
37
+ exports.parseEventsInline = parseEventsInline;
38
+ exports.correlateEventsWithMetrics = correlateEventsWithMetrics;
39
+ exports.getEventTypeEmoji = getEventTypeEmoji;
40
+ exports.getEventTypeColor = getEventTypeColor;
41
+ exports.formatEventsInsights = formatEventsInsights;
42
+ const fs = __importStar(require("fs"));
43
+ const date_fns_1 = require("date-fns");
44
+ const commitQualityAnalyzer_1 = require("./commitQualityAnalyzer");
45
+ /**
46
+ * Load events from a JSON file
47
+ */
48
+ function loadEventsFile(filePath) {
49
+ try {
50
+ const content = fs.readFileSync(filePath, 'utf-8');
51
+ const parsed = JSON.parse(content);
52
+ // Validate and transform
53
+ const events = (parsed.events || []).map((event) => ({
54
+ date: new Date(event.date),
55
+ label: event.label,
56
+ type: validateEventType(event.type),
57
+ description: event.description,
58
+ }));
59
+ return { events };
60
+ }
61
+ catch (error) {
62
+ if (error.code === 'ENOENT') {
63
+ throw new Error(`Events file not found: ${filePath}`);
64
+ }
65
+ throw new Error(`Failed to parse events file: ${error.message}`);
66
+ }
67
+ }
68
+ /**
69
+ * Parse inline events JSON
70
+ */
71
+ function parseEventsInline(jsonStr) {
72
+ try {
73
+ const parsed = JSON.parse(jsonStr);
74
+ const events = (parsed.events || [parsed]).map((event) => ({
75
+ date: new Date(event.date),
76
+ label: event.label,
77
+ type: validateEventType(event.type),
78
+ description: event.description,
79
+ }));
80
+ return { events };
81
+ }
82
+ catch (error) {
83
+ throw new Error(`Failed to parse inline events JSON: ${error.message}`);
84
+ }
85
+ }
86
+ function validateEventType(type) {
87
+ const validTypes = ['disruption', 'attrition', 'growth', 'release', 'incident', 'external', 'custom'];
88
+ if (validTypes.includes(type)) {
89
+ return type;
90
+ }
91
+ return 'custom';
92
+ }
93
+ /**
94
+ * Calculate correlation between events and metrics changes
95
+ */
96
+ function correlateEventsWithMetrics(events, commits, windowDays = 14, skipBodyCheck = true) {
97
+ return events.map(event => ({
98
+ ...event,
99
+ correlation: calculateEventCorrelation(event, commits, windowDays, skipBodyCheck),
100
+ }));
101
+ }
102
+ function calculateEventCorrelation(event, commits, windowDays, skipBodyCheck) {
103
+ const eventDate = event.date;
104
+ // Before period
105
+ const beforeStart = (0, date_fns_1.subDays)(eventDate, windowDays);
106
+ const beforeEnd = (0, date_fns_1.subDays)(eventDate, 1);
107
+ const beforeCommits = commits.filter(c => {
108
+ const d = new Date(c.date);
109
+ return d >= beforeStart && d <= beforeEnd;
110
+ });
111
+ // After period
112
+ const afterStart = (0, date_fns_1.addDays)(eventDate, 1);
113
+ const afterEnd = (0, date_fns_1.addDays)(eventDate, windowDays);
114
+ const afterCommits = commits.filter(c => {
115
+ const d = new Date(c.date);
116
+ return d >= afterStart && d <= afterEnd;
117
+ });
118
+ const metricsBefore = calculatePeriodMetrics(beforeCommits, beforeStart, beforeEnd, skipBodyCheck);
119
+ const metricsAfter = calculatePeriodMetrics(afterCommits, afterStart, afterEnd, skipBodyCheck);
120
+ // Calculate changes
121
+ const velocityChange = calculatePercentageChange(metricsBefore.commitsPerWeek ?? 0, metricsAfter.commitsPerWeek ?? 0);
122
+ const qualityChange = calculatePercentageChange(metricsBefore.qualityScore ?? 0, metricsAfter.qualityScore ?? 0);
123
+ const activeAuthorsChange = (metricsAfter.totalAuthors ?? 0) - (metricsBefore.totalAuthors ?? 0);
124
+ // Generate assessment
125
+ const assessment = generateAssessment(event, velocityChange, qualityChange, activeAuthorsChange);
126
+ return {
127
+ metricsBefore,
128
+ metricsAfter,
129
+ impact: {
130
+ velocityChange,
131
+ qualityChange,
132
+ activeAuthorsChange,
133
+ },
134
+ assessment,
135
+ };
136
+ }
137
+ function calculatePeriodMetrics(commits, startDate, endDate, skipBodyCheck) {
138
+ const days = (0, date_fns_1.differenceInDays)(endDate, startDate) + 1;
139
+ const weeks = Math.max(1, days / 7);
140
+ const totalCommits = commits.length;
141
+ const commitsPerWeek = Math.round((totalCommits / weeks) * 10) / 10;
142
+ const totalAuthors = new Set(commits.map(c => c.author)).size;
143
+ const qualityResult = commits.length > 0
144
+ ? (0, commitQualityAnalyzer_1.analyzeCommitQuality)(commits, { skipBodyCheck })
145
+ : { overallScore: 0 };
146
+ return {
147
+ totalCommits,
148
+ commitsPerWeek,
149
+ totalAuthors,
150
+ qualityScore: Math.round(qualityResult.overallScore * 10) / 10,
151
+ };
152
+ }
153
+ function calculatePercentageChange(before, after) {
154
+ if (before === 0) {
155
+ return after > 0 ? 100 : 0;
156
+ }
157
+ return Math.round(((after - before) / before) * 1000) / 10;
158
+ }
159
+ function generateAssessment(event, velocityChange, qualityChange, authorChange) {
160
+ const parts = [];
161
+ const eventLabel = `"${event.label}"`;
162
+ if (velocityChange <= -30) {
163
+ parts.push(`velocity dropped ${Math.abs(velocityChange)}%`);
164
+ }
165
+ else if (velocityChange >= 30) {
166
+ parts.push(`velocity increased ${velocityChange}%`);
167
+ }
168
+ if (qualityChange <= -20) {
169
+ parts.push(`quality decreased ${Math.abs(qualityChange)}%`);
170
+ }
171
+ else if (qualityChange >= 20) {
172
+ parts.push(`quality improved ${qualityChange}%`);
173
+ }
174
+ if (authorChange < 0) {
175
+ parts.push(`${Math.abs(authorChange)} fewer active contributor(s)`);
176
+ }
177
+ else if (authorChange > 0) {
178
+ parts.push(`${authorChange} more active contributor(s)`);
179
+ }
180
+ if (parts.length === 0) {
181
+ return `No significant metric changes detected after ${eventLabel}`;
182
+ }
183
+ return `After ${eventLabel}: ${parts.join(', ')}`;
184
+ }
185
+ /**
186
+ * Get emoji for event type
187
+ */
188
+ function getEventTypeEmoji(type) {
189
+ const emojis = {
190
+ disruption: '⚡',
191
+ attrition: '👋',
192
+ growth: '🌱',
193
+ release: '🚀',
194
+ incident: '🔥',
195
+ external: '📅',
196
+ custom: '📌',
197
+ };
198
+ return emojis[type] || '📌';
199
+ }
200
+ /**
201
+ * Get color for event type (for HTML)
202
+ */
203
+ function getEventTypeColor(type) {
204
+ const colors = {
205
+ disruption: '#ff6b6b',
206
+ attrition: '#ffa94d',
207
+ growth: '#69db7c',
208
+ release: '#4dabf7',
209
+ incident: '#ff8787',
210
+ external: '#868e96',
211
+ custom: '#9775fa',
212
+ };
213
+ return colors[type] || '#9775fa';
214
+ }
215
+ /**
216
+ * Format events for display
217
+ */
218
+ function formatEventsInsights(events) {
219
+ const insights = [];
220
+ if (events.length === 0) {
221
+ return ['No events annotated'];
222
+ }
223
+ insights.push(`📅 ${events.length} Event(s) Annotated`);
224
+ insights.push('');
225
+ // Sort by date
226
+ const sortedEvents = [...events].sort((a, b) => a.date.getTime() - b.date.getTime());
227
+ for (const event of sortedEvents) {
228
+ const emoji = getEventTypeEmoji(event.type);
229
+ const dateStr = (0, date_fns_1.format)(event.date, 'MMM d, yyyy');
230
+ insights.push(`${emoji} ${dateStr}: ${event.label}`);
231
+ if (event.description) {
232
+ insights.push(` ${event.description}`);
233
+ }
234
+ if (event.correlation) {
235
+ const { impact } = event.correlation;
236
+ const impactParts = [];
237
+ if (Math.abs(impact.velocityChange) >= 10) {
238
+ impactParts.push(`velocity ${impact.velocityChange >= 0 ? '+' : ''}${impact.velocityChange}%`);
239
+ }
240
+ if (Math.abs(impact.qualityChange) >= 10) {
241
+ impactParts.push(`quality ${impact.qualityChange >= 0 ? '+' : ''}${impact.qualityChange}%`);
242
+ }
243
+ if (impact.activeAuthorsChange !== 0) {
244
+ impactParts.push(`${impact.activeAuthorsChange >= 0 ? '+' : ''}${impact.activeAuthorsChange} contributors`);
245
+ }
246
+ if (impactParts.length > 0) {
247
+ insights.push(` 📊 Impact: ${impactParts.join(', ')}`);
248
+ }
249
+ }
250
+ insights.push('');
251
+ }
252
+ return insights;
253
+ }