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
|
@@ -59,10 +59,10 @@ exports.ACHIEVEMENTS = [
|
|
|
59
59
|
id: 'champion',
|
|
60
60
|
name: 'Champion',
|
|
61
61
|
emoji: '🏆',
|
|
62
|
-
description: 'Reached
|
|
62
|
+
description: 'Reached 200 commits',
|
|
63
63
|
category: 'milestone',
|
|
64
|
-
tier: '
|
|
65
|
-
criteria: { type: 'commit-count', threshold:
|
|
64
|
+
tier: 'gold',
|
|
65
|
+
criteria: { type: 'commit-count', threshold: 200, comparator: '>=' },
|
|
66
66
|
progress: 0,
|
|
67
67
|
isUnlocked: false,
|
|
68
68
|
isSecret: false
|
|
@@ -71,10 +71,10 @@ exports.ACHIEVEMENTS = [
|
|
|
71
71
|
id: 'master',
|
|
72
72
|
name: 'Master',
|
|
73
73
|
emoji: '💫',
|
|
74
|
-
description: 'Reached
|
|
74
|
+
description: 'Reached 300 commits',
|
|
75
75
|
category: 'milestone',
|
|
76
76
|
tier: 'platinum',
|
|
77
|
-
criteria: { type: 'commit-count', threshold:
|
|
77
|
+
criteria: { type: 'commit-count', threshold: 300, comparator: '>=' },
|
|
78
78
|
progress: 0,
|
|
79
79
|
isUnlocked: false,
|
|
80
80
|
isSecret: false
|
|
@@ -83,10 +83,10 @@ exports.ACHIEVEMENTS = [
|
|
|
83
83
|
id: 'legend',
|
|
84
84
|
name: 'Legend',
|
|
85
85
|
emoji: '🌟',
|
|
86
|
-
description: 'Reached
|
|
86
|
+
description: 'Reached 500 commits',
|
|
87
87
|
category: 'milestone',
|
|
88
88
|
tier: 'legendary',
|
|
89
|
-
criteria: { type: 'commit-count', threshold:
|
|
89
|
+
criteria: { type: 'commit-count', threshold: 500, comparator: '>=' },
|
|
90
90
|
progress: 0,
|
|
91
91
|
isUnlocked: false,
|
|
92
92
|
isSecret: false
|
|
@@ -98,7 +98,7 @@ exports.ACHIEVEMENTS = [
|
|
|
98
98
|
id: 'consistency',
|
|
99
99
|
name: 'Consistency',
|
|
100
100
|
emoji: '📅',
|
|
101
|
-
description: 'Maintained a 3-day
|
|
101
|
+
description: 'Maintained a 3-day business day streak',
|
|
102
102
|
category: 'time',
|
|
103
103
|
tier: 'bronze',
|
|
104
104
|
criteria: { type: 'streak', threshold: 3, comparator: '>=' },
|
|
@@ -110,10 +110,10 @@ exports.ACHIEVEMENTS = [
|
|
|
110
110
|
id: 'week-warrior',
|
|
111
111
|
name: 'Week Warrior',
|
|
112
112
|
emoji: '⚡',
|
|
113
|
-
description: 'Maintained a
|
|
113
|
+
description: 'Maintained a 5-day business day streak (full week)',
|
|
114
114
|
category: 'time',
|
|
115
115
|
tier: 'silver',
|
|
116
|
-
criteria: { type: 'streak', threshold:
|
|
116
|
+
criteria: { type: 'streak', threshold: 5, comparator: '>=' },
|
|
117
117
|
progress: 0,
|
|
118
118
|
isUnlocked: false,
|
|
119
119
|
isSecret: false
|
|
@@ -122,10 +122,10 @@ exports.ACHIEVEMENTS = [
|
|
|
122
122
|
id: 'streak-on-fire',
|
|
123
123
|
name: 'Streak On Fire',
|
|
124
124
|
emoji: '🔥',
|
|
125
|
-
description: 'Maintained a
|
|
125
|
+
description: 'Maintained a 10-day business day streak (2 weeks)',
|
|
126
126
|
category: 'time',
|
|
127
127
|
tier: 'gold',
|
|
128
|
-
criteria: { type: 'streak', threshold:
|
|
128
|
+
criteria: { type: 'streak', threshold: 10, comparator: '>=' },
|
|
129
129
|
progress: 0,
|
|
130
130
|
isUnlocked: false,
|
|
131
131
|
isSecret: false
|
|
@@ -134,10 +134,10 @@ exports.ACHIEVEMENTS = [
|
|
|
134
134
|
id: 'unstoppable',
|
|
135
135
|
name: 'Unstoppable',
|
|
136
136
|
emoji: '💪',
|
|
137
|
-
description: 'Maintained a
|
|
137
|
+
description: 'Maintained a 20-day business day streak (1 month)',
|
|
138
138
|
category: 'time',
|
|
139
139
|
tier: 'platinum',
|
|
140
|
-
criteria: { type: 'streak', threshold:
|
|
140
|
+
criteria: { type: 'streak', threshold: 20, comparator: '>=' },
|
|
141
141
|
progress: 0,
|
|
142
142
|
isUnlocked: false,
|
|
143
143
|
isSecret: false
|
|
@@ -146,10 +146,10 @@ exports.ACHIEVEMENTS = [
|
|
|
146
146
|
id: 'iron-will',
|
|
147
147
|
name: 'Iron Will',
|
|
148
148
|
emoji: '🦾',
|
|
149
|
-
description: 'Maintained a
|
|
149
|
+
description: 'Maintained a 40-day business day streak (2 months)',
|
|
150
150
|
category: 'time',
|
|
151
151
|
tier: 'platinum',
|
|
152
|
-
criteria: { type: 'streak', threshold:
|
|
152
|
+
criteria: { type: 'streak', threshold: 40, comparator: '>=' },
|
|
153
153
|
progress: 0,
|
|
154
154
|
isUnlocked: false,
|
|
155
155
|
isSecret: false
|
|
@@ -158,10 +158,10 @@ exports.ACHIEVEMENTS = [
|
|
|
158
158
|
id: 'code-machine',
|
|
159
159
|
name: 'Code Machine',
|
|
160
160
|
emoji: '👾',
|
|
161
|
-
description: 'Maintained a
|
|
161
|
+
description: 'Maintained a 60-day business day streak (3 months)',
|
|
162
162
|
category: 'time',
|
|
163
163
|
tier: 'legendary',
|
|
164
|
-
criteria: { type: 'streak', threshold:
|
|
164
|
+
criteria: { type: 'streak', threshold: 60, comparator: '>=' },
|
|
165
165
|
progress: 0,
|
|
166
166
|
isUnlocked: false,
|
|
167
167
|
isSecret: false
|
|
@@ -230,7 +230,7 @@ exports.ACHIEVEMENTS = [
|
|
|
230
230
|
id: 'early-bird',
|
|
231
231
|
name: 'Early Bird',
|
|
232
232
|
emoji: '🌅',
|
|
233
|
-
description: '
|
|
233
|
+
description: '20+ commits before 9am',
|
|
234
234
|
category: 'time',
|
|
235
235
|
tier: 'silver',
|
|
236
236
|
criteria: {
|
|
@@ -240,7 +240,7 @@ exports.ACHIEVEMENTS = [
|
|
|
240
240
|
const hour = new Date(c.date).getHours();
|
|
241
241
|
return hour >= 5 && hour < 9;
|
|
242
242
|
}).length;
|
|
243
|
-
return earlyCommits >=
|
|
243
|
+
return earlyCommits >= 20;
|
|
244
244
|
}
|
|
245
245
|
},
|
|
246
246
|
progress: 0,
|
|
@@ -251,7 +251,7 @@ exports.ACHIEVEMENTS = [
|
|
|
251
251
|
id: 'night-owl',
|
|
252
252
|
name: 'Night Owl',
|
|
253
253
|
emoji: '🦉',
|
|
254
|
-
description: '
|
|
254
|
+
description: '20+ commits after 9pm',
|
|
255
255
|
category: 'time',
|
|
256
256
|
tier: 'silver',
|
|
257
257
|
criteria: {
|
|
@@ -261,7 +261,7 @@ exports.ACHIEVEMENTS = [
|
|
|
261
261
|
const hour = new Date(c.date).getHours();
|
|
262
262
|
return hour >= 21 || hour < 5;
|
|
263
263
|
}).length;
|
|
264
|
-
return lateCommits >=
|
|
264
|
+
return lateCommits >= 20;
|
|
265
265
|
}
|
|
266
266
|
},
|
|
267
267
|
progress: 0,
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for building and working with AnalyzerContext.
|
|
4
|
+
*
|
|
5
|
+
* @module analyzerContextBuilder
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.isAnalyzerContext = isAnalyzerContext;
|
|
9
|
+
exports.buildContext = buildContext;
|
|
10
|
+
exports.getOption = getOption;
|
|
11
|
+
exports.computeFileChanges = computeFileChanges;
|
|
12
|
+
exports.withOptions = withOptions;
|
|
13
|
+
/**
|
|
14
|
+
* Type guard to check if an argument is an AnalyzerContext.
|
|
15
|
+
*
|
|
16
|
+
* @param arg - Value to check
|
|
17
|
+
* @returns True if the argument is an AnalyzerContext
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* function analyze(arg1: CommitData[] | AnalyzerContext, ...rest: unknown[]) {
|
|
22
|
+
* if (isAnalyzerContext(arg1)) {
|
|
23
|
+
* // Use arg1.commits, arg1.dateRange, etc.
|
|
24
|
+
* } else {
|
|
25
|
+
* // Use positional parameters
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function isAnalyzerContext(arg) {
|
|
31
|
+
return (typeof arg === 'object' &&
|
|
32
|
+
arg !== null &&
|
|
33
|
+
'commits' in arg &&
|
|
34
|
+
'dateRange' in arg &&
|
|
35
|
+
typeof arg.dateRange === 'object' &&
|
|
36
|
+
arg.dateRange !== null &&
|
|
37
|
+
'start' in arg.dateRange &&
|
|
38
|
+
'end' in arg.dateRange);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build an AnalyzerContext from individual parameters.
|
|
42
|
+
*
|
|
43
|
+
* @param commits - Array of commits to analyze
|
|
44
|
+
* @param startDate - Start of the analysis period
|
|
45
|
+
* @param endDate - End of the analysis period
|
|
46
|
+
* @param repoPath - Path to the repository (optional)
|
|
47
|
+
* @param options - Additional analyzer options (optional)
|
|
48
|
+
* @returns A fully constructed AnalyzerContext
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const ctx = buildContext(commits, startDate, endDate, '/path/to/repo', { windowWeeks: 8 });
|
|
53
|
+
* const velocity = analyzeVelocity(ctx);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function buildContext(commits, startDate, endDate, repoPath = '', options) {
|
|
57
|
+
return {
|
|
58
|
+
commits,
|
|
59
|
+
dateRange: {
|
|
60
|
+
start: startDate,
|
|
61
|
+
end: endDate,
|
|
62
|
+
},
|
|
63
|
+
repoPath,
|
|
64
|
+
options,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get an option value from context with a default fallback.
|
|
69
|
+
*
|
|
70
|
+
* @param ctx - The analyzer context
|
|
71
|
+
* @param key - The option key to retrieve
|
|
72
|
+
* @param defaultValue - Default value if option is not set
|
|
73
|
+
* @returns The option value or the default
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const windowWeeks = getOption(ctx, 'windowWeeks', 4);
|
|
78
|
+
* const thresholdDays = getOption(ctx, 'thresholdDays', 3);
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
function getOption(ctx, key, defaultValue) {
|
|
82
|
+
return ctx.options?.[key] ?? defaultValue;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Compute file change data from commits (cached).
|
|
86
|
+
*
|
|
87
|
+
* This is an expensive operation that should only be done once
|
|
88
|
+
* and then shared across analyzers via the context.
|
|
89
|
+
*
|
|
90
|
+
* @param _repoPath - Path to the git repository
|
|
91
|
+
* @param _commits - Commits to analyze for file changes
|
|
92
|
+
* @returns Map of file paths to change data
|
|
93
|
+
*
|
|
94
|
+
* @remarks
|
|
95
|
+
* This is a placeholder for future implementation.
|
|
96
|
+
* Currently returns an empty map.
|
|
97
|
+
*/
|
|
98
|
+
function computeFileChanges(_repoPath, _commits) {
|
|
99
|
+
// TODO: Implement git log --name-only parsing
|
|
100
|
+
// This would be called once and shared across impact/knowledge analyzers
|
|
101
|
+
return new Map();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Clone a context with merged options.
|
|
105
|
+
*
|
|
106
|
+
* @param ctx - Original context
|
|
107
|
+
* @param additionalOptions - Options to merge
|
|
108
|
+
* @returns New context with merged options
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const velocityCtx = withOptions(ctx, { windowWeeks: 8 });
|
|
113
|
+
* const gapCtx = withOptions(ctx, { thresholdDays: 5, excludeWeekends: true });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function withOptions(ctx, additionalOptions) {
|
|
117
|
+
return {
|
|
118
|
+
...ctx,
|
|
119
|
+
options: {
|
|
120
|
+
...ctx.options,
|
|
121
|
+
...additionalOptions,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.analyzeCommitQuality = analyzeCommitQuality;
|
|
4
4
|
exports.getQualityRating = getQualityRating;
|
|
5
5
|
exports.getQualityLevel = getQualityLevel;
|
|
6
|
+
const analyzerContextBuilder_1 = require("./analyzerContextBuilder");
|
|
6
7
|
const CONVENTIONAL_COMMIT_REGEX = /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?:\s.+/;
|
|
7
8
|
const CODE_HYGIENE_PATTERNS = {
|
|
8
9
|
quickFix: /fix(ed)?\s+(typo|spelling|grammar)/i,
|
|
@@ -24,8 +25,18 @@ const IMPERATIVE_STARTERS = [
|
|
|
24
25
|
'improve', 'optimize', 'enhance', 'change', 'move', 'rename', 'extract',
|
|
25
26
|
'merge', 'bump', 'revert', 'document', 'test', 'upgrade', 'downgrade'
|
|
26
27
|
];
|
|
27
|
-
function analyzeCommitQuality(
|
|
28
|
-
|
|
28
|
+
function analyzeCommitQuality(ctxOrCommits, options = {}) {
|
|
29
|
+
// Normalize to internal variables
|
|
30
|
+
let commits;
|
|
31
|
+
let skipBodyCheck;
|
|
32
|
+
if ((0, analyzerContextBuilder_1.isAnalyzerContext)(ctxOrCommits)) {
|
|
33
|
+
commits = ctxOrCommits.commits;
|
|
34
|
+
skipBodyCheck = (0, analyzerContextBuilder_1.getOption)(ctxOrCommits, 'skipBodyCheck', false);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
commits = ctxOrCommits;
|
|
38
|
+
skipBodyCheck = options.skipBodyCheck ?? false;
|
|
39
|
+
}
|
|
29
40
|
if (commits.length === 0) {
|
|
30
41
|
return getEmptyQuality();
|
|
31
42
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized empty result factories for all analyzers
|
|
4
|
+
* Provides consistent default values when there's no data to analyze
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getEmptyImpactAnalysis = getEmptyImpactAnalysis;
|
|
8
|
+
exports.getEmptyVelocityAnalysis = getEmptyVelocityAnalysis;
|
|
9
|
+
exports.getEmptyGapAnalysis = getEmptyGapAnalysis;
|
|
10
|
+
exports.getEmptyKnowledgeDistribution = getEmptyKnowledgeDistribution;
|
|
11
|
+
exports.getEmptyFileHotspotAnalysis = getEmptyFileHotspotAnalysis;
|
|
12
|
+
const date_fns_1 = require("date-fns");
|
|
13
|
+
/**
|
|
14
|
+
* Creates an empty impact analysis result
|
|
15
|
+
*/
|
|
16
|
+
function getEmptyImpactAnalysis() {
|
|
17
|
+
return {
|
|
18
|
+
overallScore: 0,
|
|
19
|
+
scoreBreakdown: {
|
|
20
|
+
coreContributions: 0,
|
|
21
|
+
featureWork: 0,
|
|
22
|
+
maintenanceWork: 0,
|
|
23
|
+
documentationWork: 0,
|
|
24
|
+
},
|
|
25
|
+
topImpactFiles: [],
|
|
26
|
+
impactTrend: 'stable',
|
|
27
|
+
insights: ['No commit data available for analysis']
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates an empty velocity analysis result
|
|
32
|
+
*/
|
|
33
|
+
function getEmptyVelocityAnalysis() {
|
|
34
|
+
return {
|
|
35
|
+
timeline: [],
|
|
36
|
+
overallTrend: 'stable',
|
|
37
|
+
trendPercentage: 0,
|
|
38
|
+
averageCommitsPerWeek: 0,
|
|
39
|
+
peakWeek: { weekStart: new Date(), commits: 0 },
|
|
40
|
+
lowestWeek: { weekStart: new Date(), commits: 0 },
|
|
41
|
+
anomalies: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates an empty gap analysis result
|
|
46
|
+
*/
|
|
47
|
+
function getEmptyGapAnalysis(startDate, endDate) {
|
|
48
|
+
const totalDays = (0, date_fns_1.differenceInDays)(endDate, startDate) + 1;
|
|
49
|
+
return {
|
|
50
|
+
gaps: [{
|
|
51
|
+
start: startDate,
|
|
52
|
+
end: endDate,
|
|
53
|
+
durationDays: totalDays,
|
|
54
|
+
commitBefore: null,
|
|
55
|
+
commitAfter: null,
|
|
56
|
+
possibleType: 'blocker',
|
|
57
|
+
}],
|
|
58
|
+
totalGapDays: totalDays,
|
|
59
|
+
percentageOfPeriodInGaps: 100,
|
|
60
|
+
longestGap: null,
|
|
61
|
+
averageGapLength: totalDays,
|
|
62
|
+
gapFrequency: 0,
|
|
63
|
+
patterns: [],
|
|
64
|
+
riskLevel: 'critical',
|
|
65
|
+
riskFactors: ['No commits found in the analysis period'],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates an empty knowledge distribution result
|
|
70
|
+
*/
|
|
71
|
+
function getEmptyKnowledgeDistribution() {
|
|
72
|
+
return {
|
|
73
|
+
directories: [],
|
|
74
|
+
busFactorRisk: {
|
|
75
|
+
overall: 0,
|
|
76
|
+
level: 'low',
|
|
77
|
+
criticalPaths: []
|
|
78
|
+
},
|
|
79
|
+
knowledgeSilos: [],
|
|
80
|
+
sharedKnowledge: [],
|
|
81
|
+
recommendations: ['No commit data available for analysis']
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates an empty file hotspot analysis result
|
|
86
|
+
*/
|
|
87
|
+
function getEmptyFileHotspotAnalysis() {
|
|
88
|
+
return {
|
|
89
|
+
topFiles: [],
|
|
90
|
+
topDirectories: [],
|
|
91
|
+
technicalDebt: [],
|
|
92
|
+
ownershipRisks: [],
|
|
93
|
+
totalFilesAnalyzed: 0
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -4,6 +4,7 @@ exports.analyzeFileHotspots = analyzeFileHotspots;
|
|
|
4
4
|
exports.getRiskLevelColor = getRiskLevelColor;
|
|
5
5
|
exports.getOwnershipIcon = getOwnershipIcon;
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
7
|
+
const emptyResults_1 = require("./emptyResults");
|
|
7
8
|
function analyzeFileHotspots(repoPath, startDate, endDate) {
|
|
8
9
|
try {
|
|
9
10
|
// Get file changes with author info from git log
|
|
@@ -15,10 +16,10 @@ function analyzeFileHotspots(repoPath, startDate, endDate) {
|
|
|
15
16
|
}
|
|
16
17
|
catch (gitError) {
|
|
17
18
|
// Git command failed, likely not in a git repo or no commits in range
|
|
18
|
-
return
|
|
19
|
+
return (0, emptyResults_1.getEmptyFileHotspotAnalysis)();
|
|
19
20
|
}
|
|
20
21
|
if (!gitLog) {
|
|
21
|
-
return
|
|
22
|
+
return (0, emptyResults_1.getEmptyFileHotspotAnalysis)();
|
|
22
23
|
}
|
|
23
24
|
// Parse git log output
|
|
24
25
|
const fileMap = new Map();
|
|
@@ -91,7 +92,7 @@ function analyzeFileHotspots(repoPath, startDate, endDate) {
|
|
|
91
92
|
}
|
|
92
93
|
catch (error) {
|
|
93
94
|
console.error('Error analyzing file hotspots:', error);
|
|
94
|
-
return
|
|
95
|
+
return (0, emptyResults_1.getEmptyFileHotspotAnalysis)();
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
function shouldIgnoreFile(path) {
|
|
@@ -241,15 +242,6 @@ function analyzeOwnership(files) {
|
|
|
241
242
|
.sort((a, b) => b.busFactorRisk - a.busFactorRisk)
|
|
242
243
|
.slice(0, 10);
|
|
243
244
|
}
|
|
244
|
-
function getEmptyAnalysis() {
|
|
245
|
-
return {
|
|
246
|
-
topFiles: [],
|
|
247
|
-
topDirectories: [],
|
|
248
|
-
technicalDebt: [],
|
|
249
|
-
ownershipRisks: [],
|
|
250
|
-
totalFilesAnalyzed: 0
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
245
|
function getRiskLevelColor(level) {
|
|
254
246
|
switch (level) {
|
|
255
247
|
case 'critical': return '🔴';
|
|
@@ -3,20 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.analyzeGaps = analyzeGaps;
|
|
4
4
|
exports.formatGapInsights = formatGapInsights;
|
|
5
5
|
const date_fns_1 = require("date-fns");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const emptyResults_1 = require("./emptyResults");
|
|
7
|
+
const analyzerContextBuilder_1 = require("./analyzerContextBuilder");
|
|
8
|
+
function analyzeGaps(ctxOrCommits, startDate, endDate, thresholdDays = 3, excludeWeekends = false) {
|
|
9
|
+
// Normalize to internal variables
|
|
10
|
+
let commits;
|
|
11
|
+
let start;
|
|
12
|
+
let end;
|
|
13
|
+
let threshold;
|
|
14
|
+
let skipWeekends;
|
|
15
|
+
if ((0, analyzerContextBuilder_1.isAnalyzerContext)(ctxOrCommits)) {
|
|
16
|
+
commits = ctxOrCommits.commits;
|
|
17
|
+
start = ctxOrCommits.dateRange.start;
|
|
18
|
+
end = ctxOrCommits.dateRange.end;
|
|
19
|
+
threshold = (0, analyzerContextBuilder_1.getOption)(ctxOrCommits, 'thresholdDays', 3);
|
|
20
|
+
skipWeekends = (0, analyzerContextBuilder_1.getOption)(ctxOrCommits, 'excludeWeekends', false);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
commits = ctxOrCommits;
|
|
24
|
+
start = startDate;
|
|
25
|
+
end = endDate;
|
|
26
|
+
threshold = thresholdDays;
|
|
27
|
+
skipWeekends = excludeWeekends;
|
|
28
|
+
}
|
|
10
29
|
if (commits.length === 0) {
|
|
11
|
-
return getEmptyGapAnalysis(
|
|
30
|
+
return (0, emptyResults_1.getEmptyGapAnalysis)(start, end);
|
|
12
31
|
}
|
|
13
32
|
// Sort commits by date
|
|
14
33
|
const sortedCommits = [...commits].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
|
15
34
|
// Find all gaps
|
|
16
|
-
const gaps = findGaps(sortedCommits,
|
|
35
|
+
const gaps = findGaps(sortedCommits, start, end, threshold, skipWeekends);
|
|
17
36
|
// Calculate statistics
|
|
18
37
|
const totalGapDays = gaps.reduce((sum, gap) => sum + gap.durationDays, 0);
|
|
19
|
-
const totalPeriodDays = (0, date_fns_1.differenceInDays)(
|
|
38
|
+
const totalPeriodDays = (0, date_fns_1.differenceInDays)(end, start) + 1;
|
|
20
39
|
const percentageOfPeriodInGaps = totalPeriodDays > 0
|
|
21
40
|
? Math.round((totalGapDays / totalPeriodDays) * 1000) / 10
|
|
22
41
|
: 0;
|
|
@@ -245,27 +264,6 @@ function assessGapRisk(gaps, percentageInGaps, longestGap) {
|
|
|
245
264
|
}
|
|
246
265
|
return { riskLevel, riskFactors };
|
|
247
266
|
}
|
|
248
|
-
function getEmptyGapAnalysis(startDate, endDate) {
|
|
249
|
-
const totalDays = (0, date_fns_1.differenceInDays)(endDate, startDate) + 1;
|
|
250
|
-
return {
|
|
251
|
-
gaps: [{
|
|
252
|
-
start: startDate,
|
|
253
|
-
end: endDate,
|
|
254
|
-
durationDays: totalDays,
|
|
255
|
-
commitBefore: null,
|
|
256
|
-
commitAfter: null,
|
|
257
|
-
possibleType: 'blocker',
|
|
258
|
-
}],
|
|
259
|
-
totalGapDays: totalDays,
|
|
260
|
-
percentageOfPeriodInGaps: 100,
|
|
261
|
-
longestGap: null,
|
|
262
|
-
averageGapLength: totalDays,
|
|
263
|
-
gapFrequency: 0,
|
|
264
|
-
patterns: [],
|
|
265
|
-
riskLevel: 'critical',
|
|
266
|
-
riskFactors: ['No commits found in the analysis period'],
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
267
|
/**
|
|
270
268
|
* Format gap analysis for display
|
|
271
269
|
*/
|