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,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONFIG = void 0;
4
+ exports.getConfig = getConfig;
5
+ /**
6
+ * Central configuration for all analysis thresholds and defaults.
7
+ * Modify these values to tune analysis behavior.
8
+ *
9
+ * This file consolidates magic numbers from across the codebase,
10
+ * making it easy to find, modify, and understand analysis parameters.
11
+ */
12
+ exports.CONFIG = {
13
+ /**
14
+ * Commit message quality thresholds
15
+ * Used by: commitQualityAnalyzer.ts
16
+ */
17
+ commitQuality: {
18
+ subject: {
19
+ /** Minimum characters for a valid subject line */
20
+ minLength: 10,
21
+ /** Maximum characters for a valid subject line */
22
+ maxLength: 72,
23
+ /** Ideal minimum for high-quality subjects */
24
+ idealMinLength: 20,
25
+ /** Ideal maximum for high-quality subjects */
26
+ idealMaxLength: 50,
27
+ },
28
+ body: {
29
+ /** Minimum characters for a meaningful body */
30
+ minLength: 20,
31
+ },
32
+ },
33
+ /**
34
+ * Activity gap analysis thresholds
35
+ * Used by: gapAnalyzer.ts
36
+ */
37
+ gaps: {
38
+ /** Default number of days without activity to flag as gap */
39
+ defaultThresholdDays: 3,
40
+ /** Risk level thresholds (in days) */
41
+ risk: {
42
+ medium: 7,
43
+ high: 14,
44
+ critical: 21,
45
+ },
46
+ },
47
+ /**
48
+ * Contribution graph settings
49
+ * Used by: contributionGraph.ts
50
+ */
51
+ contributions: {
52
+ /** Activity levels for contribution graph coloring (commits per day) */
53
+ activityLevels: [0, 3, 6, 9],
54
+ },
55
+ /**
56
+ * Knowledge distribution and bus factor analysis
57
+ * Used by: knowledgeDistributionAnalyzer.ts
58
+ */
59
+ knowledge: {
60
+ /** Percentage ownership to be considered the primary owner */
61
+ significantOwnershipPercent: 65,
62
+ /** Minimum commits to assess a file's ownership */
63
+ minCommitsForAssessment: 20,
64
+ /** Number of owners needed for low bus factor risk */
65
+ safeOwnerCount: 2,
66
+ /** Bus factor risk thresholds */
67
+ busFactor: {
68
+ low: 2, // 2+ owners
69
+ medium: 4, // Score threshold
70
+ high: 6, // Score threshold
71
+ critical: 8, // Score threshold
72
+ },
73
+ },
74
+ /**
75
+ * Velocity analysis settings
76
+ * Used by: velocityAnalyzer.ts
77
+ */
78
+ velocity: {
79
+ /** Rolling average window in weeks */
80
+ defaultWindowWeeks: 4,
81
+ /** Deviation from average to flag as anomaly (percentage) */
82
+ anomalyThresholdPercent: 30,
83
+ /** Threshold for spike detection multiplier */
84
+ spikeMultiplier: 2.0,
85
+ /** Threshold for drop detection percentage */
86
+ dropThresholdPercent: 50,
87
+ },
88
+ /**
89
+ * Achievement system thresholds
90
+ * Used by: achievementDefinitions.ts, achievementEngine.ts
91
+ */
92
+ achievements: {
93
+ /** Thresholds for commit count achievements */
94
+ commitMilestones: {
95
+ gettingStarted: 10,
96
+ onFire: 50,
97
+ centuryClub: 100,
98
+ champion: 200,
99
+ master: 300,
100
+ legend: 500,
101
+ },
102
+ /** Thresholds for streak achievements (business days) */
103
+ streakMilestones: {
104
+ consistency: 3,
105
+ weekWarrior: 5,
106
+ onFire: 10,
107
+ unstoppable: 20,
108
+ ironWill: 40,
109
+ codeMachine: 60,
110
+ },
111
+ /** Quality score thresholds */
112
+ qualityThresholds: {
113
+ qualityConscious: 70,
114
+ craftsman: 80,
115
+ perfectionist: 90,
116
+ qualityGuru: 95,
117
+ },
118
+ /** Collaboration thresholds */
119
+ collaborationThresholds: {
120
+ teamPlayer: 10,
121
+ socialCoder: 25,
122
+ communityBuilder: 50,
123
+ },
124
+ },
125
+ /**
126
+ * Time pattern analysis settings
127
+ * Used by: timePatternAnalyzer.ts
128
+ */
129
+ timePatterns: {
130
+ /** Minimum commits to consider a chronotype reliable */
131
+ minCommitsForChronotype: 10,
132
+ /** Hours that define work boundaries */
133
+ workHours: {
134
+ start: 9,
135
+ end: 17,
136
+ },
137
+ /** Evening/night boundaries */
138
+ eveningStart: 18,
139
+ nightStart: 22,
140
+ nightEnd: 5,
141
+ morningStart: 5,
142
+ morningEnd: 9,
143
+ },
144
+ /**
145
+ * Streak calculation settings
146
+ * Used by: streakCalculator.ts
147
+ */
148
+ streaks: {
149
+ /** Business days per week (excludes weekends by default) */
150
+ businessDaysPerWeek: 5,
151
+ },
152
+ };
153
+ /**
154
+ * Helper to access nested config with type safety
155
+ */
156
+ function getConfig(section) {
157
+ return exports.CONFIG[section];
158
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConfig = exports.CONFIG = void 0;
4
+ /**
5
+ * Configuration module for analysis thresholds and defaults.
6
+ * Import from here to access centralized configuration.
7
+ */
8
+ var defaults_1 = require("./defaults");
9
+ Object.defineProperty(exports, "CONFIG", { enumerable: true, get: function () { return defaults_1.CONFIG; } });
10
+ Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return defaults_1.getConfig; } });
@@ -0,0 +1,284 @@
1
+ {
2
+ "achievements": [
3
+ {
4
+ "id": "first-steps",
5
+ "name": "First Steps",
6
+ "emoji": "🌱",
7
+ "description": "Made your first commit",
8
+ "category": "milestone",
9
+ "tier": "bronze",
10
+ "isSecret": false,
11
+ "criteria": { "type": "commit-count", "threshold": 1 }
12
+ },
13
+ {
14
+ "id": "getting-started",
15
+ "name": "Getting Started",
16
+ "emoji": "⭐",
17
+ "description": "Reached 10 commits",
18
+ "category": "milestone",
19
+ "tier": "bronze",
20
+ "isSecret": false,
21
+ "criteria": { "type": "commit-count", "threshold": 10 }
22
+ },
23
+ {
24
+ "id": "on-fire",
25
+ "name": "On Fire",
26
+ "emoji": "🔥",
27
+ "description": "Reached 50 commits",
28
+ "category": "milestone",
29
+ "tier": "silver",
30
+ "isSecret": false,
31
+ "criteria": { "type": "commit-count", "threshold": 50 }
32
+ },
33
+ {
34
+ "id": "century-club",
35
+ "name": "Century Club",
36
+ "emoji": "👑",
37
+ "description": "Reached 100 commits",
38
+ "category": "milestone",
39
+ "tier": "gold",
40
+ "isSecret": false,
41
+ "criteria": { "type": "commit-count", "threshold": 100 }
42
+ },
43
+ {
44
+ "id": "champion",
45
+ "name": "Champion",
46
+ "emoji": "🏆",
47
+ "description": "Reached 200 commits",
48
+ "category": "milestone",
49
+ "tier": "gold",
50
+ "isSecret": false,
51
+ "criteria": { "type": "commit-count", "threshold": 200 }
52
+ },
53
+ {
54
+ "id": "master",
55
+ "name": "Master",
56
+ "emoji": "💫",
57
+ "description": "Reached 300 commits",
58
+ "category": "milestone",
59
+ "tier": "platinum",
60
+ "isSecret": false,
61
+ "criteria": { "type": "commit-count", "threshold": 300 }
62
+ },
63
+ {
64
+ "id": "legend",
65
+ "name": "Legend",
66
+ "emoji": "🌟",
67
+ "description": "Reached 500 commits",
68
+ "category": "milestone",
69
+ "tier": "legendary",
70
+ "isSecret": false,
71
+ "criteria": { "type": "commit-count", "threshold": 500 }
72
+ },
73
+ {
74
+ "id": "consistency",
75
+ "name": "Consistency",
76
+ "emoji": "📅",
77
+ "description": "Maintained a 3-day business day streak",
78
+ "category": "time",
79
+ "tier": "bronze",
80
+ "isSecret": false,
81
+ "criteria": { "type": "streak", "threshold": 3 }
82
+ },
83
+ {
84
+ "id": "week-warrior",
85
+ "name": "Week Warrior",
86
+ "emoji": "⚡",
87
+ "description": "Maintained a 5-day business day streak (full week)",
88
+ "category": "time",
89
+ "tier": "silver",
90
+ "isSecret": false,
91
+ "criteria": { "type": "streak", "threshold": 5 }
92
+ },
93
+ {
94
+ "id": "streak-on-fire",
95
+ "name": "Streak On Fire",
96
+ "emoji": "🔥",
97
+ "description": "Maintained a 10-day business day streak (2 weeks)",
98
+ "category": "time",
99
+ "tier": "gold",
100
+ "isSecret": false,
101
+ "criteria": { "type": "streak", "threshold": 10 }
102
+ },
103
+ {
104
+ "id": "unstoppable",
105
+ "name": "Unstoppable",
106
+ "emoji": "💪",
107
+ "description": "Maintained a 20-day business day streak (1 month)",
108
+ "category": "time",
109
+ "tier": "platinum",
110
+ "isSecret": false,
111
+ "criteria": { "type": "streak", "threshold": 20 }
112
+ },
113
+ {
114
+ "id": "iron-will",
115
+ "name": "Iron Will",
116
+ "emoji": "🦾",
117
+ "description": "Maintained a 40-day business day streak (2 months)",
118
+ "category": "time",
119
+ "tier": "platinum",
120
+ "isSecret": false,
121
+ "criteria": { "type": "streak", "threshold": 40 }
122
+ },
123
+ {
124
+ "id": "code-machine",
125
+ "name": "Code Machine",
126
+ "emoji": "👾",
127
+ "description": "Maintained a 60-day business day streak (3 months)",
128
+ "category": "time",
129
+ "tier": "legendary",
130
+ "isSecret": false,
131
+ "criteria": { "type": "streak", "threshold": 60 }
132
+ },
133
+ {
134
+ "id": "clean-coder",
135
+ "name": "Clean Coder",
136
+ "emoji": "✨",
137
+ "description": "90%+ commits follow conventions",
138
+ "category": "quality",
139
+ "tier": "gold",
140
+ "isSecret": false,
141
+ "criteria": { "type": "conventional-adherence", "threshold": 90 }
142
+ },
143
+ {
144
+ "id": "documentarian",
145
+ "name": "Documentarian",
146
+ "emoji": "📝",
147
+ "description": "50%+ commits include detailed bodies",
148
+ "category": "quality",
149
+ "tier": "silver",
150
+ "isSecret": false,
151
+ "criteria": { "type": "body-percentage", "threshold": 50 }
152
+ },
153
+ {
154
+ "id": "quality-champion",
155
+ "name": "Quality Champion",
156
+ "emoji": "🏅",
157
+ "description": "Achieved 9.0+ overall quality score",
158
+ "category": "quality",
159
+ "tier": "platinum",
160
+ "isSecret": false,
161
+ "criteria": { "type": "quality-score", "threshold": 9.0 }
162
+ },
163
+ {
164
+ "id": "rising-star",
165
+ "name": "Rising Star",
166
+ "emoji": "📈",
167
+ "description": "Quality score above 7.0",
168
+ "category": "quality",
169
+ "tier": "gold",
170
+ "isSecret": false,
171
+ "criteria": { "type": "quality-score", "threshold": 7.0 }
172
+ },
173
+ {
174
+ "id": "early-bird",
175
+ "name": "Early Bird",
176
+ "emoji": "🌅",
177
+ "description": "20+ commits before 9am",
178
+ "category": "time",
179
+ "tier": "silver",
180
+ "isSecret": false,
181
+ "criteria": { "type": "time-range-commits", "startHour": 5, "endHour": 9, "threshold": 20 }
182
+ },
183
+ {
184
+ "id": "night-owl",
185
+ "name": "Night Owl",
186
+ "emoji": "🦉",
187
+ "description": "20+ commits after 9pm",
188
+ "category": "time",
189
+ "tier": "silver",
190
+ "isSecret": false,
191
+ "criteria": { "type": "night-commits", "threshold": 20 }
192
+ },
193
+ {
194
+ "id": "balanced-soul",
195
+ "name": "Balanced Soul",
196
+ "emoji": "⚖️",
197
+ "description": "Even distribution across all time periods",
198
+ "category": "time",
199
+ "tier": "gold",
200
+ "isSecret": false,
201
+ "criteria": { "type": "chronotype", "value": "balanced" }
202
+ },
203
+ {
204
+ "id": "work-life-balance",
205
+ "name": "Balanced Life",
206
+ "emoji": "🌴",
207
+ "description": "Excellent work-life balance score",
208
+ "category": "time",
209
+ "tier": "gold",
210
+ "isSecret": false,
211
+ "criteria": { "type": "work-life-score", "threshold": 4 }
212
+ },
213
+ {
214
+ "id": "midnight-coder",
215
+ "name": "Midnight Coder",
216
+ "emoji": "🎆",
217
+ "description": "Committed at exactly midnight",
218
+ "category": "special",
219
+ "tier": "silver",
220
+ "isSecret": true,
221
+ "criteria": { "type": "midnight-commit" }
222
+ },
223
+ {
224
+ "id": "lucky-seven",
225
+ "name": "Lucky Seven",
226
+ "emoji": "🎰",
227
+ "description": "Reached exactly 777 commits",
228
+ "category": "special",
229
+ "tier": "gold",
230
+ "isSecret": true,
231
+ "criteria": { "type": "exact-commit-count", "threshold": 777 }
232
+ },
233
+ {
234
+ "id": "binary-master",
235
+ "name": "Binary Master",
236
+ "emoji": "🔢",
237
+ "description": "Reached exactly 1024 commits",
238
+ "category": "special",
239
+ "tier": "platinum",
240
+ "isSecret": true,
241
+ "criteria": { "type": "exact-commit-count", "threshold": 1024 }
242
+ },
243
+ {
244
+ "id": "weekend-warrior",
245
+ "name": "Weekend Warrior",
246
+ "emoji": "🎪",
247
+ "description": "20%+ commits on weekends",
248
+ "category": "special",
249
+ "tier": "silver",
250
+ "isSecret": false,
251
+ "criteria": { "type": "weekend-percentage", "threshold": 20 }
252
+ },
253
+ {
254
+ "id": "collector",
255
+ "name": "Collector",
256
+ "emoji": "🎖️",
257
+ "description": "Earned 10 badges",
258
+ "category": "meta",
259
+ "tier": "silver",
260
+ "isSecret": false,
261
+ "criteria": { "type": "badge-count", "threshold": 10 }
262
+ },
263
+ {
264
+ "id": "completionist",
265
+ "name": "Completionist",
266
+ "emoji": "🏅",
267
+ "description": "Earned 25 badges",
268
+ "category": "meta",
269
+ "tier": "gold",
270
+ "isSecret": false,
271
+ "criteria": { "type": "badge-count", "threshold": 25 }
272
+ },
273
+ {
274
+ "id": "badge-king",
275
+ "name": "Badge King",
276
+ "emoji": "👑",
277
+ "description": "Earned 50 badges",
278
+ "category": "meta",
279
+ "tier": "legendary",
280
+ "isSecret": false,
281
+ "criteria": { "type": "badge-count", "threshold": 50 }
282
+ }
283
+ ]
284
+ }
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * Achievement Engine Module
4
+ *
5
+ * Processes achievements against analysis data using data-driven definitions
6
+ * loaded from JSON and evaluated with pure functions.
7
+ *
8
+ * @module achievementEngine
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.checkAchievements = checkAchievements;
12
+ exports.getRecentAchievements = getRecentAchievements;
13
+ exports.getAchievementsByCategory = getAchievementsByCategory;
14
+ exports.getNextMilestone = getNextMilestone;
15
+ const loader_1 = require("./loader");
16
+ const evaluators_1 = require("./evaluators");
17
+ /**
18
+ * Convert a definition to a runtime achievement with evaluation results
19
+ */
20
+ function evaluateAchievement(definition, data) {
21
+ const result = (0, evaluators_1.evaluate)(data, definition.criteria);
22
+ return {
23
+ id: definition.id,
24
+ name: definition.name,
25
+ emoji: definition.emoji,
26
+ description: definition.description,
27
+ category: definition.category,
28
+ tier: definition.tier,
29
+ isSecret: definition.isSecret,
30
+ criteria: {
31
+ type: definition.criteria.type,
32
+ threshold: definition.criteria.threshold,
33
+ },
34
+ progress: result.progress,
35
+ isUnlocked: result.earned,
36
+ earnedDate: result.earned ? new Date() : undefined,
37
+ };
38
+ }
39
+ /**
40
+ * Evaluate meta achievements based on earned badge count
41
+ */
42
+ function evaluateMetaAchievement(definition, earnedCount) {
43
+ const result = (0, evaluators_1.evaluateMeta)(earnedCount, definition.criteria);
44
+ return {
45
+ id: definition.id,
46
+ name: definition.name,
47
+ emoji: definition.emoji,
48
+ description: definition.description,
49
+ category: definition.category,
50
+ tier: definition.tier,
51
+ isSecret: definition.isSecret,
52
+ criteria: {
53
+ type: definition.criteria.type,
54
+ threshold: definition.criteria.threshold,
55
+ },
56
+ progress: result.progress,
57
+ isUnlocked: result.earned,
58
+ earnedDate: result.earned ? new Date() : undefined,
59
+ };
60
+ }
61
+ /**
62
+ * Check all achievements against analysis data
63
+ * @param data Analysis data to evaluate achievements against
64
+ * @returns Achievement progress including all achievements with their status
65
+ */
66
+ function checkAchievements(data) {
67
+ const newlyEarned = [];
68
+ // First, evaluate all non-meta achievements
69
+ const nonMetaDefinitions = (0, loader_1.getNonMetaAchievements)();
70
+ const nonMetaAchievements = nonMetaDefinitions.map((def) => {
71
+ const achievement = evaluateAchievement(def, data);
72
+ if (achievement.isUnlocked) {
73
+ newlyEarned.push(achievement);
74
+ }
75
+ return achievement;
76
+ });
77
+ // Count earned non-meta achievements for meta evaluation
78
+ const earnedCount = nonMetaAchievements.filter((a) => a.isUnlocked).length;
79
+ // Then evaluate meta achievements based on earned count
80
+ const metaDefinitions = (0, loader_1.getMetaAchievements)();
81
+ const metaAchievements = metaDefinitions.map((def) => {
82
+ const achievement = evaluateMetaAchievement(def, earnedCount);
83
+ if (achievement.isUnlocked) {
84
+ newlyEarned.push(achievement);
85
+ }
86
+ return achievement;
87
+ });
88
+ // Combine all achievements
89
+ const achievements = [...nonMetaAchievements, ...metaAchievements];
90
+ // Find next milestones (top 3 closest to unlocking)
91
+ const lockedAchievements = achievements.filter((a) => !a.isUnlocked);
92
+ const nextMilestones = lockedAchievements.sort((a, b) => b.progress - a.progress).slice(0, 3);
93
+ const totalUnlocked = achievements.filter((a) => a.isUnlocked).length;
94
+ const completionPercentage = (totalUnlocked / achievements.length) * 100;
95
+ return {
96
+ achievements,
97
+ unlockedCount: totalUnlocked,
98
+ totalCount: achievements.length,
99
+ recentlyEarned: newlyEarned,
100
+ nextMilestones,
101
+ completionPercentage,
102
+ };
103
+ }
104
+ /**
105
+ * Get recently earned achievements within a time window
106
+ * @param progress Achievement progress to filter
107
+ * @param days Number of days to look back (default: 7)
108
+ * @returns Array of recently earned achievements
109
+ */
110
+ function getRecentAchievements(progress, days = 7) {
111
+ const cutoffDate = new Date();
112
+ cutoffDate.setDate(cutoffDate.getDate() - days);
113
+ return progress.achievements
114
+ .filter((a) => a.isUnlocked && a.earnedDate && a.earnedDate >= cutoffDate)
115
+ .sort((a, b) => {
116
+ if (!a.earnedDate || !b.earnedDate)
117
+ return 0;
118
+ return b.earnedDate.getTime() - a.earnedDate.getTime();
119
+ });
120
+ }
121
+ /**
122
+ * Get achievements filtered by category
123
+ * @param progress Achievement progress to filter
124
+ * @param category Category to filter by
125
+ * @returns Array of achievements in that category
126
+ */
127
+ function getAchievementsByCategory(progress, category) {
128
+ return progress.achievements.filter((a) => a.category === category);
129
+ }
130
+ /**
131
+ * Get the next closest achievement to being unlocked
132
+ * @param progress Achievement progress to analyze
133
+ * @returns The achievement with highest progress that isn't unlocked, or null
134
+ */
135
+ function getNextMilestone(progress) {
136
+ const locked = progress.achievements.filter((a) => !a.isUnlocked);
137
+ if (locked.length === 0)
138
+ return null;
139
+ return locked.reduce((closest, current) => current.progress > closest.progress ? current : closest);
140
+ }