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.
- package/.github/agents/complete.agent.md +257 -0
- package/.github/agents/feature-scaffold.agent.md +248 -0
- package/.github/agents/jsdoc.agent.md +243 -0
- package/.github/agents/plan.agent.md +202 -0
- package/.github/agents/spec-writer.agent.md +169 -0
- package/.github/agents/test-writer.agent.md +169 -0
- package/.stylelintrc.json +27 -0
- package/README.md +94 -94
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +446 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +446 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +7039 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/commands/generate.js +56 -56
- package/dist/config/defaults.js +158 -0
- package/dist/config/index.js +10 -0
- package/dist/features/achievements/data/achievements.json +284 -0
- package/dist/features/achievements/engine.js +140 -0
- package/dist/features/achievements/evaluators.js +246 -0
- package/dist/features/achievements/helpers.js +58 -0
- package/dist/features/achievements/index.js +57 -0
- package/dist/features/achievements/loader.js +88 -0
- package/dist/features/achievements/template.js +155 -0
- package/dist/features/achievements/types.js +7 -0
- package/dist/features/commit-quality/analyzer.js +378 -0
- package/dist/features/commit-quality/analyzer.test.js +484 -0
- package/dist/features/commit-quality/index.js +28 -0
- package/dist/features/commit-quality/template.js +114 -0
- package/dist/features/commit-quality/types.js +2 -0
- package/dist/features/comparison/analyzer.js +222 -0
- package/dist/features/comparison/index.js +28 -0
- package/dist/features/comparison/template.js +119 -0
- package/dist/features/comparison/types.js +2 -0
- package/dist/features/contribution-graph/index.js +9 -0
- package/dist/features/contribution-graph/template.js +89 -0
- package/dist/features/events/index.js +31 -0
- package/dist/features/events/parser.js +253 -0
- package/dist/features/events/template.js +113 -0
- package/dist/features/events/types.js +2 -0
- package/dist/features/executive-summary/generator.js +275 -0
- package/dist/features/executive-summary/index.js +27 -0
- package/dist/features/executive-summary/template.js +80 -0
- package/dist/features/executive-summary/types.js +2 -0
- package/dist/features/gaps/analyzer.js +298 -0
- package/dist/features/gaps/analyzer.test.js +517 -0
- package/dist/features/gaps/index.js +27 -0
- package/dist/features/gaps/template.js +190 -0
- package/dist/features/gaps/types.js +2 -0
- package/dist/features/impact/analyzer.js +248 -0
- package/dist/features/impact/index.js +26 -0
- package/dist/features/impact/template.js +118 -0
- package/dist/features/impact/types.js +2 -0
- package/dist/features/index.js +40 -0
- package/dist/features/knowledge/analyzer.js +385 -0
- package/dist/features/knowledge/index.js +26 -0
- package/dist/features/knowledge/template.js +239 -0
- package/dist/features/knowledge/types.js +2 -0
- package/dist/features/streaks/calculator.js +184 -0
- package/dist/features/streaks/calculator.test.js +366 -0
- package/dist/features/streaks/index.js +36 -0
- package/dist/features/streaks/template.js +41 -0
- package/dist/features/streaks/types.js +9 -0
- package/dist/features/team/analyzer.js +316 -0
- package/dist/features/team/index.js +30 -0
- package/dist/features/team/template.js +146 -0
- package/dist/features/team/types.js +2 -0
- package/dist/features/time-patterns/analyzer.js +319 -0
- package/dist/features/time-patterns/analyzer.test.js +278 -0
- package/dist/features/time-patterns/index.js +37 -0
- package/dist/features/time-patterns/template.js +109 -0
- package/dist/features/time-patterns/types.js +9 -0
- package/dist/features/velocity/analyzer.js +257 -0
- package/dist/features/velocity/analyzer.test.js +383 -0
- package/dist/features/velocity/index.js +27 -0
- package/dist/features/velocity/template.js +189 -0
- package/dist/features/velocity/types.js +2 -0
- package/dist/generators/html/scripts/knowledge.js +17 -0
- package/dist/generators/html/styles/base.css +8 -3
- package/dist/generators/html/styles/components.css +121 -1
- package/dist/generators/html/styles/knowledge.css +21 -0
- package/dist/generators/html/styles/leaddev.css +108 -48
- package/dist/generators/html/styles/strategic-insights.css +1337 -0
- package/dist/generators/html/templates/commitQualitySection.js +28 -2
- package/dist/generators/html/templates/executiveSummarySection.js +0 -4
- package/dist/generators/html/templates/impactSection.js +8 -6
- package/dist/generators/html/templates/knowledgeSection.js +16 -2
- package/dist/generators/html/templates/velocitySection.js +2 -2
- package/dist/generators/html/types.js +7 -0
- package/dist/generators/html/utils/analysisRunner.js +93 -0
- package/dist/generators/html/utils/cardBuilder.js +47 -0
- package/dist/generators/html/utils/contextBuilder.js +54 -0
- package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
- package/dist/generators/html/utils/kpiBuilder.js +76 -0
- package/dist/generators/html/utils/sectionWrapper.js +71 -0
- package/dist/generators/html/utils/styleLoader.js +2 -2
- package/dist/html/analysisRunner.js +93 -0
- package/dist/html/htmlDocumentBuilder.js +396 -0
- package/dist/html/index.js +29 -0
- package/dist/html/shared/colorUtils.js +61 -0
- package/dist/html/shared/commitMapBuilder.js +23 -0
- package/dist/html/shared/components/cardBuilder.js +47 -0
- package/dist/html/shared/components/index.js +18 -0
- package/dist/html/shared/components/kpiBuilder.js +76 -0
- package/dist/html/shared/components/sectionWrapper.js +71 -0
- package/dist/html/shared/contextBuilder.js +54 -0
- package/dist/html/shared/dateRangeCalculator.js +56 -0
- package/dist/html/shared/developerStatsCalculator.js +28 -0
- package/dist/html/shared/index.js +39 -0
- package/dist/html/shared/scriptLoader.js +15 -0
- package/dist/html/shared/scripts/export.js +125 -0
- package/dist/html/shared/scripts/knowledge.js +137 -0
- package/dist/html/shared/scripts/modal.js +68 -0
- package/dist/html/shared/scripts/navigation.js +156 -0
- package/dist/html/shared/scripts/tabs.js +18 -0
- package/dist/html/shared/scripts/tooltip.js +21 -0
- package/dist/html/shared/styleLoader.js +18 -0
- package/dist/html/shared/styles/achievements.css +387 -0
- package/dist/html/shared/styles/base.css +822 -0
- package/dist/html/shared/styles/components.css +1511 -0
- package/dist/html/shared/styles/knowledge.css +242 -0
- package/dist/html/shared/styles/strategic-insights.css +1337 -0
- package/dist/html/shared/weekGrouper.js +27 -0
- package/dist/html/types.js +7 -0
- package/dist/index.js +39 -39
- package/dist/test/helpers/commitFactory.js +166 -0
- package/dist/test/helpers/dateUtils.js +101 -0
- package/dist/test/helpers/index.js +29 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/smoke.test.js +94 -0
- package/dist/types/achievements.js +7 -0
- package/dist/types/analysis.js +7 -0
- package/dist/types/core.js +7 -0
- package/dist/types/index.js +38 -0
- package/dist/types/options.js +7 -0
- package/dist/types/shared.js +7 -0
- package/dist/types/strategic.js +7 -0
- package/dist/types/summary.js +7 -0
- package/dist/utils/achievementDefinitions.js +22 -22
- package/dist/utils/analyzerContextBuilder.js +124 -0
- package/dist/utils/commitQualityAnalyzer.js +13 -2
- package/dist/utils/emptyResults.js +95 -0
- package/dist/utils/fileHotspotAnalyzer.js +4 -12
- package/dist/utils/gapAnalyzer.js +26 -28
- package/dist/utils/gitParser.test.js +363 -0
- package/dist/utils/htmlGenerator.js +62 -466
- package/dist/utils/impactAnalyzer.js +20 -19
- package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
- package/dist/utils/matrixGenerator.js +13 -13
- package/dist/utils/rangeComparisonAnalyzer.js +2 -2
- package/dist/utils/streakCalculator.js +77 -27
- package/dist/utils/teamAnalyzer.js +20 -1
- package/dist/utils/timePatternAnalyzer.js +18 -3
- package/dist/utils/velocityAnalyzer.js +23 -18
- package/dist/utils/wrappedGenerator.js +8 -8
- package/package.json +74 -64
- package/vitest.config.ts +46 -0
- package/SPECS.md +0 -490
- package/dist/cli.js +0 -24
- package/dist/commands/index.js +0 -24
- package/test-team.txt +0 -2
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Achievement Evaluators Module
|
|
4
|
+
*
|
|
5
|
+
* Pure functions that evaluate achievement criteria against analysis data.
|
|
6
|
+
* Each evaluator returns { earned: boolean, progress: number } where progress is 0-100.
|
|
7
|
+
*
|
|
8
|
+
* @module achievementEvaluators
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.evaluate = evaluate;
|
|
12
|
+
exports.evaluateMeta = evaluateMeta;
|
|
13
|
+
exports.isMetaCriteria = isMetaCriteria;
|
|
14
|
+
/**
|
|
15
|
+
* Calculate progress as percentage (capped at 100)
|
|
16
|
+
*/
|
|
17
|
+
function progressPercent(current, target) {
|
|
18
|
+
if (target === 0)
|
|
19
|
+
return 100;
|
|
20
|
+
return Math.min(100, Math.round((current / target) * 100));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Evaluate commit count threshold
|
|
24
|
+
*/
|
|
25
|
+
function evaluateCommitCount(data, criteria) {
|
|
26
|
+
const count = data.totalCommits ?? 0;
|
|
27
|
+
const threshold = criteria.threshold ?? 0;
|
|
28
|
+
return {
|
|
29
|
+
earned: count >= threshold,
|
|
30
|
+
progress: progressPercent(count, threshold),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Evaluate exact commit count (for special achievements)
|
|
35
|
+
*/
|
|
36
|
+
function evaluateExactCommitCount(data, criteria) {
|
|
37
|
+
const count = data.totalCommits ?? 0;
|
|
38
|
+
const threshold = criteria.threshold ?? 0;
|
|
39
|
+
return {
|
|
40
|
+
earned: count === threshold,
|
|
41
|
+
progress: count === threshold ? 100 : progressPercent(count, threshold),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Evaluate streak length
|
|
46
|
+
*/
|
|
47
|
+
function evaluateStreak(data, criteria) {
|
|
48
|
+
const streak = data.streakData?.longestStreak?.days ?? 0;
|
|
49
|
+
const threshold = criteria.threshold ?? 0;
|
|
50
|
+
return {
|
|
51
|
+
earned: streak >= threshold,
|
|
52
|
+
progress: progressPercent(streak, threshold),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Evaluate quality score threshold
|
|
57
|
+
*/
|
|
58
|
+
function evaluateQualityScore(data, criteria) {
|
|
59
|
+
const score = data.commitQuality?.overallScore ?? 0;
|
|
60
|
+
const threshold = criteria.threshold ?? 0;
|
|
61
|
+
return {
|
|
62
|
+
earned: score >= threshold,
|
|
63
|
+
progress: progressPercent(score, threshold),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Evaluate conventional commit adherence percentage
|
|
68
|
+
*/
|
|
69
|
+
function evaluateConventionalAdherence(data, criteria) {
|
|
70
|
+
const adherence = data.commitQuality?.conventionalCommits?.adherence ?? 0;
|
|
71
|
+
const threshold = criteria.threshold ?? 0;
|
|
72
|
+
return {
|
|
73
|
+
earned: adherence >= threshold,
|
|
74
|
+
progress: progressPercent(adherence, threshold),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Evaluate percentage of commits with body content
|
|
79
|
+
*/
|
|
80
|
+
function evaluateBodyPercentage(data, criteria) {
|
|
81
|
+
const withBody = data.commitQuality?.bodyQuality?.withBody ?? 0;
|
|
82
|
+
const total = data.totalCommits ?? 1;
|
|
83
|
+
const percentage = (withBody / total) * 100;
|
|
84
|
+
const threshold = criteria.threshold ?? 0;
|
|
85
|
+
return {
|
|
86
|
+
earned: percentage >= threshold,
|
|
87
|
+
progress: progressPercent(percentage, threshold),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Evaluate commits within a time range (e.g., early morning)
|
|
92
|
+
*/
|
|
93
|
+
function evaluateTimeRangeCommits(data, criteria) {
|
|
94
|
+
const hourlyData = data.timePattern?.hourly ?? [];
|
|
95
|
+
const startHour = criteria.startHour ?? 0;
|
|
96
|
+
const endHour = criteria.endHour ?? 24;
|
|
97
|
+
const threshold = criteria.threshold ?? 0;
|
|
98
|
+
let count = 0;
|
|
99
|
+
for (const hourEntry of hourlyData) {
|
|
100
|
+
if (hourEntry.hour >= startHour && hourEntry.hour < endHour) {
|
|
101
|
+
count += hourEntry.commitCount;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
earned: count >= threshold,
|
|
106
|
+
progress: progressPercent(count, threshold),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Evaluate night commits (after 9pm or before 5am)
|
|
111
|
+
*/
|
|
112
|
+
function evaluateNightCommits(data, criteria) {
|
|
113
|
+
const hourlyData = data.timePattern?.hourly ?? [];
|
|
114
|
+
const threshold = criteria.threshold ?? 0;
|
|
115
|
+
let count = 0;
|
|
116
|
+
for (const hourEntry of hourlyData) {
|
|
117
|
+
// Night hours: 21, 22, 23, 0, 1, 2, 3, 4
|
|
118
|
+
if (hourEntry.hour >= 21 || hourEntry.hour < 5) {
|
|
119
|
+
count += hourEntry.commitCount;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
earned: count >= threshold,
|
|
124
|
+
progress: progressPercent(count, threshold),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Evaluate chronotype match
|
|
129
|
+
*/
|
|
130
|
+
function evaluateChronotype(data, criteria) {
|
|
131
|
+
const chronotype = data.timePattern?.chronotype ?? '';
|
|
132
|
+
const targetValue = criteria.value ?? '';
|
|
133
|
+
const earned = chronotype.toLowerCase() === targetValue.toLowerCase();
|
|
134
|
+
return {
|
|
135
|
+
earned,
|
|
136
|
+
progress: earned ? 100 : 0,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Evaluate work-life balance score
|
|
141
|
+
*/
|
|
142
|
+
function evaluateWorkLifeScore(data, criteria) {
|
|
143
|
+
const score = data.timePattern?.workLifeBalance?.score ?? 0;
|
|
144
|
+
const threshold = criteria.threshold ?? 0;
|
|
145
|
+
return {
|
|
146
|
+
earned: score >= threshold,
|
|
147
|
+
progress: progressPercent(score, threshold),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Evaluate if any commit was made at exactly midnight
|
|
152
|
+
*/
|
|
153
|
+
function evaluateMidnightCommit(data) {
|
|
154
|
+
const commits = data.commits ?? [];
|
|
155
|
+
const hasMidnightCommit = commits.some((commit) => {
|
|
156
|
+
const date = new Date(commit.date);
|
|
157
|
+
return date.getHours() === 0 && date.getMinutes() === 0;
|
|
158
|
+
});
|
|
159
|
+
return {
|
|
160
|
+
earned: hasMidnightCommit,
|
|
161
|
+
progress: hasMidnightCommit ? 100 : 0,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Evaluate weekend commit percentage
|
|
166
|
+
*/
|
|
167
|
+
function evaluateWeekendPercentage(data, criteria) {
|
|
168
|
+
const commits = data.commits ?? [];
|
|
169
|
+
const threshold = criteria.threshold ?? 0;
|
|
170
|
+
if (commits.length === 0) {
|
|
171
|
+
return { earned: false, progress: 0 };
|
|
172
|
+
}
|
|
173
|
+
const weekendCommits = commits.filter((commit) => {
|
|
174
|
+
const day = new Date(commit.date).getDay();
|
|
175
|
+
return day === 0 || day === 6;
|
|
176
|
+
}).length;
|
|
177
|
+
const percentage = (weekendCommits / commits.length) * 100;
|
|
178
|
+
return {
|
|
179
|
+
earned: percentage >= threshold,
|
|
180
|
+
progress: progressPercent(percentage, threshold),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Evaluate badge count for meta achievements
|
|
185
|
+
*/
|
|
186
|
+
function evaluateBadgeCount(earnedCount, criteria) {
|
|
187
|
+
const threshold = criteria.threshold ?? 0;
|
|
188
|
+
return {
|
|
189
|
+
earned: earnedCount >= threshold,
|
|
190
|
+
progress: progressPercent(earnedCount, threshold),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Registry of all evaluator functions by criteria type
|
|
195
|
+
*/
|
|
196
|
+
const evaluators = {
|
|
197
|
+
'commit-count': evaluateCommitCount,
|
|
198
|
+
'exact-commit-count': evaluateExactCommitCount,
|
|
199
|
+
streak: evaluateStreak,
|
|
200
|
+
'quality-score': evaluateQualityScore,
|
|
201
|
+
'conventional-adherence': evaluateConventionalAdherence,
|
|
202
|
+
'body-percentage': evaluateBodyPercentage,
|
|
203
|
+
'time-range-commits': evaluateTimeRangeCommits,
|
|
204
|
+
'night-commits': evaluateNightCommits,
|
|
205
|
+
chronotype: evaluateChronotype,
|
|
206
|
+
'work-life-score': evaluateWorkLifeScore,
|
|
207
|
+
'midnight-commit': evaluateMidnightCommit,
|
|
208
|
+
'weekend-percentage': evaluateWeekendPercentage,
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Evaluate an achievement criteria against analysis data
|
|
212
|
+
* @param data Analysis data to evaluate against
|
|
213
|
+
* @param criteria The criteria to evaluate
|
|
214
|
+
* @returns Evaluation result with earned status and progress
|
|
215
|
+
*/
|
|
216
|
+
function evaluate(data, criteria) {
|
|
217
|
+
const evaluator = evaluators[criteria.type];
|
|
218
|
+
if (!evaluator) {
|
|
219
|
+
console.warn(`Unknown criteria type: ${criteria.type}`);
|
|
220
|
+
return { earned: false, progress: 0 };
|
|
221
|
+
}
|
|
222
|
+
return evaluator(data, criteria);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Evaluate badge-count criteria (for meta achievements)
|
|
226
|
+
* This is separate because it depends on the count of earned achievements,
|
|
227
|
+
* not on the raw analysis data.
|
|
228
|
+
* @param earnedCount Number of achievements already earned
|
|
229
|
+
* @param criteria The criteria to evaluate
|
|
230
|
+
* @returns Evaluation result with earned status and progress
|
|
231
|
+
*/
|
|
232
|
+
function evaluateMeta(earnedCount, criteria) {
|
|
233
|
+
if (criteria.type !== 'badge-count') {
|
|
234
|
+
console.warn(`evaluateMeta only supports badge-count criteria, got: ${criteria.type}`);
|
|
235
|
+
return { earned: false, progress: 0 };
|
|
236
|
+
}
|
|
237
|
+
return evaluateBadgeCount(earnedCount, criteria);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Check if a criteria type is a meta achievement type
|
|
241
|
+
* @param type The criteria type to check
|
|
242
|
+
* @returns True if this is a meta achievement type
|
|
243
|
+
*/
|
|
244
|
+
function isMetaCriteria(type) {
|
|
245
|
+
return type === 'badge-count';
|
|
246
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Achievement Helper Functions
|
|
4
|
+
*
|
|
5
|
+
* Utility functions for achievement display formatting.
|
|
6
|
+
* Achievement data is now loaded from src/data/achievements.json
|
|
7
|
+
* via the achievementLoader module.
|
|
8
|
+
*
|
|
9
|
+
* @module achievementDefinitions
|
|
10
|
+
* @deprecated Import from achievementLoader for achievement data
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.getTierColor = getTierColor;
|
|
14
|
+
exports.getCategoryLabel = getCategoryLabel;
|
|
15
|
+
/**
|
|
16
|
+
* Get the display color for an achievement tier
|
|
17
|
+
* @param tier Achievement tier level
|
|
18
|
+
* @returns CSS color value for the tier
|
|
19
|
+
*/
|
|
20
|
+
function getTierColor(tier) {
|
|
21
|
+
switch (tier) {
|
|
22
|
+
case 'bronze':
|
|
23
|
+
return '#cd7f32';
|
|
24
|
+
case 'silver':
|
|
25
|
+
return '#c0c0c0';
|
|
26
|
+
case 'gold':
|
|
27
|
+
return '#ffd700';
|
|
28
|
+
case 'platinum':
|
|
29
|
+
return '#e5e4e2';
|
|
30
|
+
case 'legendary':
|
|
31
|
+
return '#ff6b6b';
|
|
32
|
+
default:
|
|
33
|
+
return '#8b949e';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the display label for an achievement category
|
|
38
|
+
* @param category Achievement category
|
|
39
|
+
* @returns Formatted label with emoji
|
|
40
|
+
*/
|
|
41
|
+
function getCategoryLabel(category) {
|
|
42
|
+
switch (category) {
|
|
43
|
+
case 'milestone':
|
|
44
|
+
return '🎯 Milestone';
|
|
45
|
+
case 'quality':
|
|
46
|
+
return '🎨 Quality';
|
|
47
|
+
case 'collaboration':
|
|
48
|
+
return '🤝 Collaboration';
|
|
49
|
+
case 'time':
|
|
50
|
+
return '⏰ Time & Consistency';
|
|
51
|
+
case 'special':
|
|
52
|
+
return '🎮 Special';
|
|
53
|
+
case 'meta':
|
|
54
|
+
return '🏆 Meta';
|
|
55
|
+
default:
|
|
56
|
+
return category;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Achievements Feature
|
|
4
|
+
*
|
|
5
|
+
* Self-contained achievement system including:
|
|
6
|
+
* - Data: JSON achievement definitions
|
|
7
|
+
* - Types: Achievement interfaces
|
|
8
|
+
* - Engine: Processing and evaluation
|
|
9
|
+
* - Template: HTML rendering
|
|
10
|
+
*
|
|
11
|
+
* @module features/achievements
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
25
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.buildAchievementsSection = exports.getCategoryLabel = exports.getTierColor = exports.isMetaCriteria = exports.evaluateMeta = exports.evaluate = exports.getTotalAchievementCount = exports.getMetaAchievements = exports.getNonMetaAchievements = exports.getAchievementsByTier = exports.getDefinitionsByCategory = exports.getAchievementById = exports.loadAchievementDefinitions = exports.getNextMilestone = exports.getAchievementsByCategory = exports.getRecentAchievements = exports.checkAchievements = void 0;
|
|
29
|
+
// Types
|
|
30
|
+
__exportStar(require("./types"), exports);
|
|
31
|
+
// Engine & Processing
|
|
32
|
+
var engine_1 = require("./engine");
|
|
33
|
+
Object.defineProperty(exports, "checkAchievements", { enumerable: true, get: function () { return engine_1.checkAchievements; } });
|
|
34
|
+
Object.defineProperty(exports, "getRecentAchievements", { enumerable: true, get: function () { return engine_1.getRecentAchievements; } });
|
|
35
|
+
Object.defineProperty(exports, "getAchievementsByCategory", { enumerable: true, get: function () { return engine_1.getAchievementsByCategory; } });
|
|
36
|
+
Object.defineProperty(exports, "getNextMilestone", { enumerable: true, get: function () { return engine_1.getNextMilestone; } });
|
|
37
|
+
// Loader
|
|
38
|
+
var loader_1 = require("./loader");
|
|
39
|
+
Object.defineProperty(exports, "loadAchievementDefinitions", { enumerable: true, get: function () { return loader_1.loadAchievementDefinitions; } });
|
|
40
|
+
Object.defineProperty(exports, "getAchievementById", { enumerable: true, get: function () { return loader_1.getAchievementById; } });
|
|
41
|
+
Object.defineProperty(exports, "getDefinitionsByCategory", { enumerable: true, get: function () { return loader_1.getAchievementsByCategory; } });
|
|
42
|
+
Object.defineProperty(exports, "getAchievementsByTier", { enumerable: true, get: function () { return loader_1.getAchievementsByTier; } });
|
|
43
|
+
Object.defineProperty(exports, "getNonMetaAchievements", { enumerable: true, get: function () { return loader_1.getNonMetaAchievements; } });
|
|
44
|
+
Object.defineProperty(exports, "getMetaAchievements", { enumerable: true, get: function () { return loader_1.getMetaAchievements; } });
|
|
45
|
+
Object.defineProperty(exports, "getTotalAchievementCount", { enumerable: true, get: function () { return loader_1.getTotalAchievementCount; } });
|
|
46
|
+
// Evaluators
|
|
47
|
+
var evaluators_1 = require("./evaluators");
|
|
48
|
+
Object.defineProperty(exports, "evaluate", { enumerable: true, get: function () { return evaluators_1.evaluate; } });
|
|
49
|
+
Object.defineProperty(exports, "evaluateMeta", { enumerable: true, get: function () { return evaluators_1.evaluateMeta; } });
|
|
50
|
+
Object.defineProperty(exports, "isMetaCriteria", { enumerable: true, get: function () { return evaluators_1.isMetaCriteria; } });
|
|
51
|
+
// Helpers
|
|
52
|
+
var helpers_1 = require("./helpers");
|
|
53
|
+
Object.defineProperty(exports, "getTierColor", { enumerable: true, get: function () { return helpers_1.getTierColor; } });
|
|
54
|
+
Object.defineProperty(exports, "getCategoryLabel", { enumerable: true, get: function () { return helpers_1.getCategoryLabel; } });
|
|
55
|
+
// Template
|
|
56
|
+
var template_1 = require("./template");
|
|
57
|
+
Object.defineProperty(exports, "buildAchievementsSection", { enumerable: true, get: function () { return template_1.buildAchievementsSection; } });
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Achievement Loader Module
|
|
4
|
+
*
|
|
5
|
+
* Loads achievement definitions from JSON and constructs Achievement objects.
|
|
6
|
+
* This separates data (JSON) from logic (evaluators).
|
|
7
|
+
*
|
|
8
|
+
* @module achievementLoader
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.loadAchievementDefinitions = loadAchievementDefinitions;
|
|
15
|
+
exports.getAchievementById = getAchievementById;
|
|
16
|
+
exports.getAchievementsByCategory = getAchievementsByCategory;
|
|
17
|
+
exports.getAchievementsByTier = getAchievementsByTier;
|
|
18
|
+
exports.getNonMetaAchievements = getNonMetaAchievements;
|
|
19
|
+
exports.getMetaAchievements = getMetaAchievements;
|
|
20
|
+
exports.createRuntimeAchievement = createRuntimeAchievement;
|
|
21
|
+
exports.getTotalAchievementCount = getTotalAchievementCount;
|
|
22
|
+
const achievements_json_1 = __importDefault(require("./data/achievements.json"));
|
|
23
|
+
/**
|
|
24
|
+
* Load all achievement definitions from JSON
|
|
25
|
+
* @returns Array of achievement definitions
|
|
26
|
+
*/
|
|
27
|
+
function loadAchievementDefinitions() {
|
|
28
|
+
return achievements_json_1.default.achievements;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get achievement definition by ID
|
|
32
|
+
* @param id Achievement ID to find
|
|
33
|
+
* @returns Achievement definition or undefined
|
|
34
|
+
*/
|
|
35
|
+
function getAchievementById(id) {
|
|
36
|
+
return loadAchievementDefinitions().find((a) => a.id === id);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get achievements by category
|
|
40
|
+
* @param category Category to filter by
|
|
41
|
+
* @returns Array of achievements in that category
|
|
42
|
+
*/
|
|
43
|
+
function getAchievementsByCategory(category) {
|
|
44
|
+
return loadAchievementDefinitions().filter((a) => a.category === category);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get achievements by tier
|
|
48
|
+
* @param tier Tier to filter by
|
|
49
|
+
* @returns Array of achievements at that tier
|
|
50
|
+
*/
|
|
51
|
+
function getAchievementsByTier(tier) {
|
|
52
|
+
return loadAchievementDefinitions().filter((a) => a.tier === tier);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get all non-meta achievements (for evaluating before meta achievements)
|
|
56
|
+
* @returns Array of non-meta achievements
|
|
57
|
+
*/
|
|
58
|
+
function getNonMetaAchievements() {
|
|
59
|
+
return loadAchievementDefinitions().filter((a) => a.category !== 'meta');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get all meta achievements (evaluated last, based on badge count)
|
|
63
|
+
* @returns Array of meta achievements
|
|
64
|
+
*/
|
|
65
|
+
function getMetaAchievements() {
|
|
66
|
+
return loadAchievementDefinitions().filter((a) => a.category === 'meta');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create a runtime achievement from a definition
|
|
70
|
+
* @param definition Achievement definition from JSON
|
|
71
|
+
* @returns LoadedAchievement with default runtime state
|
|
72
|
+
*/
|
|
73
|
+
function createRuntimeAchievement(definition) {
|
|
74
|
+
return {
|
|
75
|
+
...definition,
|
|
76
|
+
progress: 0,
|
|
77
|
+
isUnlocked: false,
|
|
78
|
+
earnedDate: undefined,
|
|
79
|
+
rarity: undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get total count of all achievements
|
|
84
|
+
* @returns Total number of achievements
|
|
85
|
+
*/
|
|
86
|
+
function getTotalAchievementCount() {
|
|
87
|
+
return loadAchievementDefinitions().length;
|
|
88
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAchievementsSection = buildAchievementsSection;
|
|
4
|
+
// Helper: Build SVG progress ring
|
|
5
|
+
function buildProgressRing(progress, size = 50) {
|
|
6
|
+
const radius = (size / 2) - 4;
|
|
7
|
+
const circumference = 2 * Math.PI * radius;
|
|
8
|
+
const offset = circumference * (1 - progress / 100);
|
|
9
|
+
return `
|
|
10
|
+
<svg class="progress-ring" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
|
|
11
|
+
<circle class="progress-ring-bg" cx="${size / 2}" cy="${size / 2}" r="${radius}"
|
|
12
|
+
fill="none" stroke="var(--border-subtle)" stroke-width="3" />
|
|
13
|
+
<circle class="progress-ring-fill" cx="${size / 2}" cy="${size / 2}" r="${radius}"
|
|
14
|
+
fill="none" stroke="var(--accent-blue)" stroke-width="3"
|
|
15
|
+
stroke-dasharray="${circumference}"
|
|
16
|
+
stroke-dashoffset="${offset}"
|
|
17
|
+
stroke-linecap="round"
|
|
18
|
+
transform="rotate(-90 ${size / 2} ${size / 2})" />
|
|
19
|
+
</svg>
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
// Helper: Build individual achievement card
|
|
23
|
+
function buildAchievementCard(achievement) {
|
|
24
|
+
const cardClass = achievement.isUnlocked ? 'unlocked' : 'locked';
|
|
25
|
+
const showProgress = !achievement.isUnlocked && achievement.progress > 0;
|
|
26
|
+
return `
|
|
27
|
+
<div class="achievement-card ${cardClass}">
|
|
28
|
+
${showProgress ? `
|
|
29
|
+
<div class="achievement-ring-wrapper">
|
|
30
|
+
${buildProgressRing(achievement.progress, 60)}
|
|
31
|
+
</div>
|
|
32
|
+
` : ''}
|
|
33
|
+
<div class="achievement-emoji">${achievement.emoji}</div>
|
|
34
|
+
<div class="achievement-name">${achievement.name}</div>
|
|
35
|
+
${!achievement.isSecret || achievement.isUnlocked ? `
|
|
36
|
+
<div class="achievement-description">${achievement.description}</div>
|
|
37
|
+
` : `
|
|
38
|
+
<div class="achievement-description">???</div>
|
|
39
|
+
`}
|
|
40
|
+
<div class="badge-tier ${achievement.tier}">${achievement.tier}</div>
|
|
41
|
+
${showProgress ? `
|
|
42
|
+
<div class="achievement-progress-text">${achievement.progress.toFixed(0)}%</div>
|
|
43
|
+
` : ''}
|
|
44
|
+
</div>
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
// Helper: Generate growth narrative
|
|
48
|
+
function buildGrowthNarrative(progress) {
|
|
49
|
+
const { unlockedCount, totalCount, completionPercentage, categories, nextMilestones } = progress;
|
|
50
|
+
// Find strongest category
|
|
51
|
+
let strongestCategory = '';
|
|
52
|
+
let strongestCount = 0;
|
|
53
|
+
if (categories && categories.length > 0) {
|
|
54
|
+
for (const cat of categories) {
|
|
55
|
+
if (cat.unlocked > strongestCount) {
|
|
56
|
+
strongestCount = cat.unlocked;
|
|
57
|
+
strongestCategory = cat.name;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Get next milestone info
|
|
62
|
+
let nextMilestoneText = '';
|
|
63
|
+
if (nextMilestones && nextMilestones.length > 0) {
|
|
64
|
+
const next = nextMilestones[0];
|
|
65
|
+
const remaining = 100 - next.progress;
|
|
66
|
+
nextMilestoneText = `Next milestone: <strong>${next.name}</strong> — ${remaining.toFixed(0)}% to go.`;
|
|
67
|
+
}
|
|
68
|
+
return `
|
|
69
|
+
<div class="growth-narrative">
|
|
70
|
+
<h3>Your Developer Journey</h3>
|
|
71
|
+
<p class="narrative-text">
|
|
72
|
+
You've unlocked <strong>${unlockedCount} of ${totalCount}</strong> achievements (${completionPercentage.toFixed(0)}%).
|
|
73
|
+
${strongestCategory ? `Your strongest area is <strong>${strongestCategory}</strong> with ${strongestCount} badges earned.` : ''}
|
|
74
|
+
${nextMilestoneText}
|
|
75
|
+
</p>
|
|
76
|
+
</div>
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
function buildAchievementsSection(achievementProgress) {
|
|
80
|
+
return `
|
|
81
|
+
<div class="achievements-section">
|
|
82
|
+
<h2>Achievements</h2>
|
|
83
|
+
|
|
84
|
+
${buildGrowthNarrative(achievementProgress)}
|
|
85
|
+
|
|
86
|
+
<div class="achievement-stats">
|
|
87
|
+
<div class="stat-card">
|
|
88
|
+
<div class="stat-value">${achievementProgress.unlockedCount}</div>
|
|
89
|
+
<div class="stat-label">Badges Unlocked</div>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="stat-card">
|
|
92
|
+
<div class="stat-value">${achievementProgress.completionPercentage.toFixed(0)}%</div>
|
|
93
|
+
<div class="stat-label">Completion</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="stat-card">
|
|
96
|
+
<div class="stat-value">${achievementProgress.totalCount - achievementProgress.unlockedCount}</div>
|
|
97
|
+
<div class="stat-label">Remaining</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
${achievementProgress.nextMilestones && achievementProgress.nextMilestones.length > 0 ? `
|
|
102
|
+
<div class="next-milestones">
|
|
103
|
+
<h3>Next Milestones</h3>
|
|
104
|
+
${achievementProgress.nextMilestones.slice(0, 3).map(milestone => `
|
|
105
|
+
<div class="milestone-item">
|
|
106
|
+
<div class="milestone-header">
|
|
107
|
+
<span class="milestone-emoji">${milestone.emoji}</span>
|
|
108
|
+
<span class="milestone-name">${milestone.name}</span>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="milestone-description">${milestone.description}</div>
|
|
111
|
+
<div class="milestone-progress-bar">
|
|
112
|
+
<div class="milestone-progress-fill" style="width: ${milestone.progress}%"></div>
|
|
113
|
+
<div class="milestone-progress-text">${milestone.progress.toFixed(0)}%</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
`).join('')}
|
|
117
|
+
</div>
|
|
118
|
+
` : ''}
|
|
119
|
+
|
|
120
|
+
${achievementProgress.newBadges && achievementProgress.newBadges.length > 0 ? `
|
|
121
|
+
<div class="new-badges">
|
|
122
|
+
<h3>Recently Unlocked</h3>
|
|
123
|
+
<div class="badge-list">
|
|
124
|
+
${achievementProgress.newBadges.map(badge => `
|
|
125
|
+
<div class="badge-item-mini">${badge.emoji} ${badge.name}</div>
|
|
126
|
+
`).join('')}
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
` : ''}
|
|
130
|
+
|
|
131
|
+
${achievementProgress.categories ? `
|
|
132
|
+
<div class="achievement-categories">
|
|
133
|
+
${achievementProgress.categories.map(category => `
|
|
134
|
+
<div class="category-section">
|
|
135
|
+
<h3>${category.emoji} ${category.name}</h3>
|
|
136
|
+
<div class="category-progress">
|
|
137
|
+
<div class="category-bar">
|
|
138
|
+
<div class="category-fill" style="width: ${category.progress}%"></div>
|
|
139
|
+
</div>
|
|
140
|
+
<span class="category-count">${category.unlocked}/${category.total}</span>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="achievement-grid">
|
|
143
|
+
${category.achievements.map(achievement => buildAchievementCard(achievement)).join('')}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
`).join('')}
|
|
147
|
+
</div>
|
|
148
|
+
` : achievementProgress.achievements && achievementProgress.achievements.length > 0 ? `
|
|
149
|
+
<div class="achievements-grid">
|
|
150
|
+
${achievementProgress.achievements.map(achievement => buildAchievementCard(achievement)).join('')}
|
|
151
|
+
</div>
|
|
152
|
+
` : ''}
|
|
153
|
+
</div>
|
|
154
|
+
`;
|
|
155
|
+
}
|