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.
- 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 +262 -5
- 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 +10 -6
- 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 +1335 -0
- package/dist/generators/html/styles/strategic-insights.css +1337 -0
- package/dist/generators/html/templates/commitQualitySection.js +28 -2
- package/dist/generators/html/templates/comparisonSection.js +119 -0
- package/dist/generators/html/templates/eventsSection.js +113 -0
- package/dist/generators/html/templates/executiveSummarySection.js +80 -0
- package/dist/generators/html/templates/gapSection.js +190 -0
- package/dist/generators/html/templates/impactSection.js +8 -6
- package/dist/generators/html/templates/knowledgeSection.js +16 -2
- package/dist/generators/html/templates/teamSection.js +146 -0
- package/dist/generators/html/templates/velocitySection.js +189 -0
- 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 -1
- 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 +54 -21
- 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/eventAnnotationParser.js +253 -0
- package/dist/utils/executiveSummaryGenerator.js +275 -0
- package/dist/utils/fileHotspotAnalyzer.js +4 -12
- package/dist/utils/gapAnalyzer.js +298 -0
- package/dist/utils/gitParser.test.js +363 -0
- package/dist/utils/htmlGenerator.js +126 -450
- 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 +222 -0
- package/dist/utils/streakCalculator.js +77 -27
- package/dist/utils/teamAnalyzer.js +316 -0
- package/dist/utils/timePatternAnalyzer.js +18 -3
- package/dist/utils/velocityAnalyzer.js +257 -0
- package/dist/utils/wrappedGenerator.js +8 -8
- package/package.json +74 -55
- package/vitest.config.ts +46 -0
- package/dist/cli.js +0 -24
- package/dist/commands/index.js +0 -24
|
@@ -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
|
+
}
|