@entro314labs/ai-changelog-generator 3.0.5 → 3.2.0
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/CHANGELOG.md +383 -785
- package/README.md +30 -3
- package/ai-changelog-mcp.sh +0 -0
- package/ai-changelog.sh +0 -0
- package/bin/ai-changelog-dxt.js +9 -9
- package/bin/ai-changelog-mcp.js +19 -17
- package/bin/ai-changelog.js +6 -6
- package/package.json +84 -52
- package/src/ai-changelog-generator.js +83 -81
- package/src/application/orchestrators/changelog.orchestrator.js +1040 -296
- package/src/application/services/application.service.js +145 -123
- package/src/cli.js +76 -57
- package/src/domains/ai/ai-analysis.service.js +289 -209
- package/src/domains/analysis/analysis.engine.js +253 -193
- package/src/domains/changelog/changelog.service.js +1062 -784
- package/src/domains/changelog/workspace-changelog.service.js +420 -249
- package/src/domains/git/git-repository.analyzer.js +348 -258
- package/src/domains/git/git.service.js +132 -112
- package/src/infrastructure/cli/cli.controller.js +415 -247
- package/src/infrastructure/config/configuration.manager.js +220 -190
- package/src/infrastructure/interactive/interactive-staging.service.js +332 -0
- package/src/infrastructure/interactive/interactive-workflow.service.js +200 -159
- package/src/infrastructure/mcp/mcp-server.service.js +208 -207
- package/src/infrastructure/metrics/metrics.collector.js +140 -123
- package/src/infrastructure/providers/core/base-provider.js +87 -40
- package/src/infrastructure/providers/implementations/anthropic.js +101 -99
- package/src/infrastructure/providers/implementations/azure.js +124 -101
- package/src/infrastructure/providers/implementations/bedrock.js +136 -126
- package/src/infrastructure/providers/implementations/dummy.js +23 -23
- package/src/infrastructure/providers/implementations/google.js +123 -114
- package/src/infrastructure/providers/implementations/huggingface.js +94 -87
- package/src/infrastructure/providers/implementations/lmstudio.js +75 -60
- package/src/infrastructure/providers/implementations/mock.js +69 -73
- package/src/infrastructure/providers/implementations/ollama.js +89 -66
- package/src/infrastructure/providers/implementations/openai.js +88 -89
- package/src/infrastructure/providers/implementations/vertex.js +227 -197
- package/src/infrastructure/providers/provider-management.service.js +245 -207
- package/src/infrastructure/providers/provider-manager.service.js +145 -125
- package/src/infrastructure/providers/utils/base-provider-helpers.js +308 -302
- package/src/infrastructure/providers/utils/model-config.js +220 -195
- package/src/infrastructure/providers/utils/provider-utils.js +105 -100
- package/src/infrastructure/validation/commit-message-validation.service.js +556 -0
- package/src/shared/constants/colors.js +467 -172
- package/src/shared/utils/cli-demo.js +285 -0
- package/src/shared/utils/cli-entry-utils.js +257 -249
- package/src/shared/utils/cli-ui.js +447 -0
- package/src/shared/utils/diff-processor.js +513 -0
- package/src/shared/utils/error-classes.js +125 -156
- package/src/shared/utils/json-utils.js +93 -89
- package/src/shared/utils/utils.js +1299 -775
- package/types/index.d.ts +353 -344
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import colors from '../../shared/constants/colors.js'
|
|
2
|
+
import { outputData } from '../../shared/utils/utils.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Git Repository Analyzer
|
|
@@ -13,178 +13,203 @@ import colors from '../../shared/constants/colors.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export class GitRepositoryAnalyzer {
|
|
15
15
|
constructor(gitManager, aiAnalysisService) {
|
|
16
|
-
this.gitManager = gitManager
|
|
17
|
-
this.aiAnalysisService = aiAnalysisService
|
|
16
|
+
this.gitManager = gitManager
|
|
17
|
+
this.aiAnalysisService = aiAnalysisService
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async analyzeBranches(format = 'markdown') {
|
|
21
21
|
if (!this.gitManager.isGitRepo) {
|
|
22
|
-
const errorMsg = 'Not a git repository'
|
|
22
|
+
const errorMsg = 'Not a git repository'
|
|
23
23
|
if (format === 'json') {
|
|
24
|
-
outputData({ error: errorMsg }, format)
|
|
25
|
-
return
|
|
24
|
+
outputData({ error: errorMsg }, format)
|
|
25
|
+
return
|
|
26
26
|
}
|
|
27
|
-
console.log(colors.errorMessage(errorMsg))
|
|
28
|
-
return
|
|
27
|
+
console.log(colors.errorMessage(errorMsg))
|
|
28
|
+
return
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
if (format === 'markdown') {
|
|
32
|
-
console.log(colors.processingMessage('Analyzing git branches and unmerged commits...'))
|
|
32
|
+
console.log(colors.processingMessage('Analyzing git branches and unmerged commits...'))
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
try {
|
|
36
|
-
const branches = this.gitManager.getAllBranches()
|
|
37
|
-
const unmergedCommits = this.gitManager.getUnmergedCommits()
|
|
38
|
-
const danglingCommits = this.gitManager.getDanglingCommits()
|
|
39
|
-
|
|
40
|
-
let aiAnalysis = null
|
|
41
|
-
if (
|
|
42
|
-
|
|
36
|
+
const branches = this.gitManager.getAllBranches()
|
|
37
|
+
const unmergedCommits = this.gitManager.getUnmergedCommits()
|
|
38
|
+
const danglingCommits = this.gitManager.getDanglingCommits()
|
|
39
|
+
|
|
40
|
+
let aiAnalysis = null
|
|
41
|
+
if (
|
|
42
|
+
this.aiAnalysisService.hasAI &&
|
|
43
|
+
(unmergedCommits.length > 0 || danglingCommits.length > 0)
|
|
44
|
+
) {
|
|
45
|
+
aiAnalysis = await this.aiAnalysisService.getBranchesAIAnalysis(
|
|
46
|
+
branches,
|
|
47
|
+
unmergedCommits,
|
|
48
|
+
danglingCommits
|
|
49
|
+
)
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
const data = {
|
|
46
53
|
type: 'branch_analysis',
|
|
47
54
|
timestamp: new Date().toISOString(),
|
|
48
55
|
branches: {
|
|
49
|
-
local: branches.local.map(branch => ({
|
|
56
|
+
local: branches.local.map((branch) => ({
|
|
50
57
|
name: branch,
|
|
51
|
-
current: branch === branches.current
|
|
58
|
+
current: branch === branches.current,
|
|
52
59
|
})),
|
|
53
60
|
remote: branches.remote,
|
|
54
61
|
current: branches.current,
|
|
55
62
|
summary: {
|
|
56
63
|
localCount: branches.local.length,
|
|
57
|
-
remoteCount: branches.remote.length
|
|
58
|
-
}
|
|
64
|
+
remoteCount: branches.remote.length,
|
|
65
|
+
},
|
|
59
66
|
},
|
|
60
|
-
unmergedCommits: unmergedCommits.map(branch => ({
|
|
67
|
+
unmergedCommits: unmergedCommits.map((branch) => ({
|
|
61
68
|
branch: branch.branch,
|
|
62
69
|
commitCount: branch.commits.length,
|
|
63
|
-
commits: branch.commits.map(commit => ({
|
|
70
|
+
commits: branch.commits.map((commit) => ({
|
|
64
71
|
hash: commit.hash,
|
|
65
72
|
shortHash: commit.shortHash,
|
|
66
73
|
subject: commit.subject,
|
|
67
74
|
author: commit.author,
|
|
68
|
-
date: commit.date
|
|
69
|
-
}))
|
|
75
|
+
date: commit.date,
|
|
76
|
+
})),
|
|
70
77
|
})),
|
|
71
|
-
danglingCommits: danglingCommits.map(commit => ({
|
|
78
|
+
danglingCommits: danglingCommits.map((commit) => ({
|
|
72
79
|
hash: commit.hash,
|
|
73
80
|
shortHash: commit.shortHash,
|
|
74
81
|
subject: commit.subject,
|
|
75
82
|
author: commit.author,
|
|
76
|
-
date: commit.date
|
|
83
|
+
date: commit.date,
|
|
77
84
|
})),
|
|
78
85
|
summary: {
|
|
79
86
|
totalBranches: branches.local.length + branches.remote.length,
|
|
80
|
-
unmergedCommitCount: unmergedCommits.reduce(
|
|
87
|
+
unmergedCommitCount: unmergedCommits.reduce(
|
|
88
|
+
(sum, branch) => sum + branch.commits.length,
|
|
89
|
+
0
|
|
90
|
+
),
|
|
81
91
|
danglingCommitCount: danglingCommits.length,
|
|
82
92
|
hasUnmergedWork: unmergedCommits.length > 0,
|
|
83
|
-
hasDanglingCommits: danglingCommits.length > 0
|
|
93
|
+
hasDanglingCommits: danglingCommits.length > 0,
|
|
84
94
|
},
|
|
85
|
-
aiAnalysis
|
|
86
|
-
}
|
|
95
|
+
aiAnalysis,
|
|
96
|
+
}
|
|
87
97
|
|
|
88
98
|
if (format === 'json') {
|
|
89
|
-
outputData(data, format)
|
|
90
|
-
return data
|
|
99
|
+
outputData(data, format)
|
|
100
|
+
return data
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
// Markdown format display
|
|
94
|
-
console.log(colors.header('\n📊 Branch Analysis:'))
|
|
95
|
-
console.log(colors.subheader(`🌿 Local branches (${colors.number(branches.local.length)}):`))
|
|
96
|
-
branches.local.forEach(branch => {
|
|
97
|
-
const indicator = branch === branches.current ? '* ' : ' '
|
|
98
|
-
const branchColor = branch === branches.current ? colors.highlight : colors.secondary
|
|
99
|
-
console.log(`${indicator}${branchColor(branch)}`)
|
|
100
|
-
})
|
|
104
|
+
console.log(colors.header('\n📊 Branch Analysis:'))
|
|
105
|
+
console.log(colors.subheader(`🌿 Local branches (${colors.number(branches.local.length)}):`))
|
|
106
|
+
branches.local.forEach((branch) => {
|
|
107
|
+
const indicator = branch === branches.current ? '* ' : ' '
|
|
108
|
+
const branchColor = branch === branches.current ? colors.highlight : colors.secondary
|
|
109
|
+
console.log(`${indicator}${branchColor(branch)}`)
|
|
110
|
+
})
|
|
101
111
|
|
|
102
112
|
if (branches.remote.length > 0) {
|
|
103
|
-
console.log(
|
|
104
|
-
|
|
113
|
+
console.log(
|
|
114
|
+
colors.subheader(`\n🌐 Remote branches (${colors.number(branches.remote.length)}):`)
|
|
115
|
+
)
|
|
116
|
+
branches.remote
|
|
117
|
+
.slice(0, 10)
|
|
118
|
+
.forEach((branch) => console.log(` - ${colors.secondary(branch)}`))
|
|
105
119
|
if (branches.remote.length > 10) {
|
|
106
|
-
console.log(colors.dim(` ... and ${branches.remote.length - 10} more`))
|
|
120
|
+
console.log(colors.dim(` ... and ${branches.remote.length - 10} more`))
|
|
107
121
|
}
|
|
108
122
|
}
|
|
109
123
|
|
|
110
124
|
if (unmergedCommits.length > 0) {
|
|
111
|
-
console.log(colors.subheader(
|
|
112
|
-
unmergedCommits.forEach(branch => {
|
|
113
|
-
console.log(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
console.log(colors.subheader('\n🔄 Unmerged commits in other branches:'))
|
|
126
|
+
unmergedCommits.forEach((branch) => {
|
|
127
|
+
console.log(
|
|
128
|
+
`\n ${colors.label(branch.branch)} (${colors.number(branch.commits.length)} commits):`
|
|
129
|
+
)
|
|
130
|
+
branch.commits.slice(0, 3).forEach((commit) => {
|
|
131
|
+
console.log(
|
|
132
|
+
` - ${colors.hash(commit.shortHash)}: ${colors.value(commit.subject)} (${colors.secondary(commit.author)})`
|
|
133
|
+
)
|
|
134
|
+
})
|
|
117
135
|
if (branch.commits.length > 3) {
|
|
118
|
-
console.log(colors.dim(` ... and ${branch.commits.length - 3} more commits`))
|
|
136
|
+
console.log(colors.dim(` ... and ${branch.commits.length - 3} more commits`))
|
|
119
137
|
}
|
|
120
|
-
})
|
|
138
|
+
})
|
|
121
139
|
} else {
|
|
122
|
-
console.log(colors.successMessage('\n✅ No unmerged commits found'))
|
|
140
|
+
console.log(colors.successMessage('\n✅ No unmerged commits found'))
|
|
123
141
|
}
|
|
124
142
|
|
|
125
143
|
if (danglingCommits.length > 0) {
|
|
126
|
-
console.log(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
144
|
+
console.log(
|
|
145
|
+
colors.warningMessage(`\n🗑️ Dangling commits (${colors.number(danglingCommits.length)}):`)
|
|
146
|
+
)
|
|
147
|
+
danglingCommits.slice(0, 5).forEach((commit) => {
|
|
148
|
+
console.log(
|
|
149
|
+
` - ${colors.hash(commit.shortHash)}: ${colors.value(commit.subject)} (${colors.secondary(commit.author)})`
|
|
150
|
+
)
|
|
151
|
+
})
|
|
130
152
|
if (danglingCommits.length > 5) {
|
|
131
|
-
console.log(colors.dim(` ... and ${danglingCommits.length - 5} more`))
|
|
153
|
+
console.log(colors.dim(` ... and ${danglingCommits.length - 5} more`))
|
|
132
154
|
}
|
|
133
|
-
console.log(
|
|
155
|
+
console.log(
|
|
156
|
+
colors.infoMessage(
|
|
157
|
+
'\n💡 These commits are unreachable from any branch. Consider creating a branch or removing them.'
|
|
158
|
+
)
|
|
159
|
+
)
|
|
134
160
|
}
|
|
135
161
|
|
|
136
162
|
if (aiAnalysis) {
|
|
137
|
-
console.log(colors.aiMessage('\n🤖 AI Analysis of branch situation:'))
|
|
138
|
-
console.log(aiAnalysis)
|
|
163
|
+
console.log(colors.aiMessage('\n🤖 AI Analysis of branch situation:'))
|
|
164
|
+
console.log(aiAnalysis)
|
|
139
165
|
}
|
|
140
166
|
|
|
141
|
-
return data
|
|
142
|
-
|
|
167
|
+
return data
|
|
143
168
|
} catch (error) {
|
|
144
169
|
if (format === 'json') {
|
|
145
|
-
outputData({ error: `Error analyzing branches: ${error.message}` }, format)
|
|
146
|
-
return { error: error.message }
|
|
170
|
+
outputData({ error: `Error analyzing branches: ${error.message}` }, format)
|
|
171
|
+
return { error: error.message }
|
|
147
172
|
}
|
|
148
|
-
console.error(colors.errorMessage(`Error analyzing branches: ${error.message}`))
|
|
149
|
-
throw error
|
|
173
|
+
console.error(colors.errorMessage(`Error analyzing branches: ${error.message}`))
|
|
174
|
+
throw error
|
|
150
175
|
}
|
|
151
176
|
}
|
|
152
177
|
|
|
153
178
|
async analyzeComprehensive(format = 'markdown') {
|
|
154
179
|
if (!this.gitManager.isGitRepo) {
|
|
155
|
-
const errorMsg = 'Not a git repository'
|
|
180
|
+
const errorMsg = 'Not a git repository'
|
|
156
181
|
if (format === 'json') {
|
|
157
|
-
outputData({ error: errorMsg }, format)
|
|
158
|
-
return
|
|
182
|
+
outputData({ error: errorMsg }, format)
|
|
183
|
+
return
|
|
159
184
|
}
|
|
160
|
-
console.log(colors.errorMessage(errorMsg))
|
|
161
|
-
return
|
|
185
|
+
console.log(colors.errorMessage(errorMsg))
|
|
186
|
+
return
|
|
162
187
|
}
|
|
163
188
|
|
|
164
189
|
if (format === 'markdown') {
|
|
165
|
-
console.log(colors.processingMessage('Comprehensive repository analysis...'))
|
|
190
|
+
console.log(colors.processingMessage('Comprehensive repository analysis...'))
|
|
166
191
|
}
|
|
167
192
|
|
|
168
193
|
try {
|
|
169
|
-
const comprehensiveData = this.gitManager.getComprehensiveAnalysis()
|
|
194
|
+
const comprehensiveData = this.gitManager.getComprehensiveAnalysis()
|
|
170
195
|
|
|
171
196
|
if (!comprehensiveData) {
|
|
172
|
-
const errorMsg = 'Failed to get comprehensive analysis'
|
|
197
|
+
const errorMsg = 'Failed to get comprehensive analysis'
|
|
173
198
|
if (format === 'json') {
|
|
174
|
-
outputData({ error: errorMsg }, format)
|
|
175
|
-
return
|
|
199
|
+
outputData({ error: errorMsg }, format)
|
|
200
|
+
return
|
|
176
201
|
}
|
|
177
|
-
console.log(colors.errorMessage(errorMsg))
|
|
178
|
-
return
|
|
202
|
+
console.log(colors.errorMessage(errorMsg))
|
|
203
|
+
return
|
|
179
204
|
}
|
|
180
205
|
|
|
181
206
|
// Get untracked files
|
|
182
|
-
const untrackedFiles = this.gitManager.getUntrackedFiles()
|
|
183
|
-
const untrackedCategories = this.categorizeUntrackedFiles(untrackedFiles)
|
|
207
|
+
const untrackedFiles = this.gitManager.getUntrackedFiles()
|
|
208
|
+
const untrackedCategories = this.categorizeUntrackedFiles(untrackedFiles)
|
|
184
209
|
|
|
185
|
-
let aiAnalysis = null
|
|
210
|
+
let aiAnalysis = null
|
|
186
211
|
if (this.aiAnalysisService.hasAI) {
|
|
187
|
-
aiAnalysis = await this.aiAnalysisService.getRepositoryAIAnalysis(comprehensiveData)
|
|
212
|
+
aiAnalysis = await this.aiAnalysisService.getRepositoryAIAnalysis(comprehensiveData)
|
|
188
213
|
}
|
|
189
214
|
|
|
190
215
|
const data = {
|
|
@@ -197,105 +222,118 @@ export class GitRepositoryAnalyzer {
|
|
|
197
222
|
summary: {
|
|
198
223
|
totalFiles: untrackedFiles.length,
|
|
199
224
|
categoryCounts: Object.keys(untrackedCategories).reduce((acc, key) => {
|
|
200
|
-
acc[key] = untrackedCategories[key].length
|
|
201
|
-
return acc
|
|
202
|
-
}, {})
|
|
203
|
-
}
|
|
225
|
+
acc[key] = untrackedCategories[key].length
|
|
226
|
+
return acc
|
|
227
|
+
}, {}),
|
|
228
|
+
},
|
|
204
229
|
},
|
|
205
|
-
aiAnalysis
|
|
206
|
-
}
|
|
230
|
+
aiAnalysis,
|
|
231
|
+
}
|
|
207
232
|
|
|
208
233
|
if (format === 'json') {
|
|
209
|
-
outputData(data, format)
|
|
210
|
-
return data
|
|
234
|
+
outputData(data, format)
|
|
235
|
+
return data
|
|
211
236
|
}
|
|
212
237
|
|
|
213
238
|
// Display comprehensive analysis
|
|
214
|
-
console.log(colors.header('\n📊 Comprehensive Repository Analysis:'))
|
|
239
|
+
console.log(colors.header('\n📊 Comprehensive Repository Analysis:'))
|
|
215
240
|
|
|
216
241
|
// Repository statistics
|
|
217
|
-
console.log(colors.subheader('\n📈 Repository Statistics:'))
|
|
218
|
-
console.log(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
console.log(
|
|
242
|
+
console.log(colors.subheader('\n📈 Repository Statistics:'))
|
|
243
|
+
console.log(
|
|
244
|
+
` ${colors.label('Total commits')}: ${colors.number(comprehensiveData.totalCommits || 0)}`
|
|
245
|
+
)
|
|
246
|
+
console.log(
|
|
247
|
+
` ${colors.label('Contributors')}: ${colors.number(comprehensiveData.contributors?.length || 0)}`
|
|
248
|
+
)
|
|
249
|
+
console.log(
|
|
250
|
+
` ${colors.label('Files tracked')}: ${colors.number(comprehensiveData.totalFiles || 0)}`
|
|
251
|
+
)
|
|
252
|
+
console.log(
|
|
253
|
+
` ${colors.label('Branches')}: ${colors.number(comprehensiveData.branchCount || 0)}`
|
|
254
|
+
)
|
|
222
255
|
|
|
223
256
|
// Recent activity
|
|
224
257
|
if (comprehensiveData.recentActivity) {
|
|
225
|
-
console.log(colors.subheader('\n🔥 Recent Activity:'))
|
|
226
|
-
console.log(
|
|
227
|
-
|
|
258
|
+
console.log(colors.subheader('\n🔥 Recent Activity:'))
|
|
259
|
+
console.log(
|
|
260
|
+
` ${colors.label('Commits this week')}: ${colors.number(comprehensiveData.recentActivity.thisWeek || 0)}`
|
|
261
|
+
)
|
|
262
|
+
console.log(
|
|
263
|
+
` ${colors.label('Commits this month')}: ${colors.number(comprehensiveData.recentActivity.thisMonth || 0)}`
|
|
264
|
+
)
|
|
228
265
|
}
|
|
229
266
|
|
|
230
267
|
// File type breakdown
|
|
231
268
|
if (comprehensiveData.fileTypes) {
|
|
232
|
-
console.log(colors.subheader('\n📁 File Type Breakdown:'))
|
|
269
|
+
console.log(colors.subheader('\n📁 File Type Breakdown:'))
|
|
233
270
|
Object.entries(comprehensiveData.fileTypes).forEach(([type, count]) => {
|
|
234
|
-
console.log(` ${colors.label(type)}: ${colors.number(count)}`)
|
|
235
|
-
})
|
|
271
|
+
console.log(` ${colors.label(type)}: ${colors.number(count)}`)
|
|
272
|
+
})
|
|
236
273
|
}
|
|
237
274
|
|
|
238
275
|
// Untracked files summary
|
|
239
276
|
if (untrackedFiles.length > 0) {
|
|
240
|
-
console.log(
|
|
277
|
+
console.log(
|
|
278
|
+
colors.subheader(`\n📄 Untracked Files (${colors.number(untrackedFiles.length)}):`)
|
|
279
|
+
)
|
|
241
280
|
Object.entries(untrackedCategories).forEach(([category, files]) => {
|
|
242
281
|
if (files.length > 0) {
|
|
243
|
-
console.log(` ${colors.label(category)}: ${colors.number(files.length)} files`)
|
|
282
|
+
console.log(` ${colors.label(category)}: ${colors.number(files.length)} files`)
|
|
244
283
|
}
|
|
245
|
-
})
|
|
284
|
+
})
|
|
246
285
|
}
|
|
247
286
|
|
|
248
287
|
if (aiAnalysis) {
|
|
249
|
-
console.log(colors.aiMessage('\n🤖 AI Repository Analysis:'))
|
|
250
|
-
console.log(aiAnalysis)
|
|
288
|
+
console.log(colors.aiMessage('\n🤖 AI Repository Analysis:'))
|
|
289
|
+
console.log(aiAnalysis)
|
|
251
290
|
}
|
|
252
291
|
|
|
253
|
-
return data
|
|
254
|
-
|
|
292
|
+
return data
|
|
255
293
|
} catch (error) {
|
|
256
294
|
if (format === 'json') {
|
|
257
|
-
outputData({ error: `Error in comprehensive analysis: ${error.message}` }, format)
|
|
258
|
-
return { error: error.message }
|
|
295
|
+
outputData({ error: `Error in comprehensive analysis: ${error.message}` }, format)
|
|
296
|
+
return { error: error.message }
|
|
259
297
|
}
|
|
260
|
-
console.error(colors.errorMessage(`Error in comprehensive analysis: ${error.message}`))
|
|
261
|
-
throw error
|
|
298
|
+
console.error(colors.errorMessage(`Error in comprehensive analysis: ${error.message}`))
|
|
299
|
+
throw error
|
|
262
300
|
}
|
|
263
301
|
}
|
|
264
302
|
|
|
265
303
|
async analyzeUntrackedFiles(format = 'markdown') {
|
|
266
304
|
if (!this.gitManager.isGitRepo) {
|
|
267
|
-
const errorMsg = 'Not a git repository'
|
|
305
|
+
const errorMsg = 'Not a git repository'
|
|
268
306
|
if (format === 'json') {
|
|
269
|
-
outputData({ error: errorMsg }, format)
|
|
270
|
-
return
|
|
307
|
+
outputData({ error: errorMsg }, format)
|
|
308
|
+
return
|
|
271
309
|
}
|
|
272
|
-
console.log(colors.errorMessage(errorMsg))
|
|
273
|
-
return
|
|
310
|
+
console.log(colors.errorMessage(errorMsg))
|
|
311
|
+
return
|
|
274
312
|
}
|
|
275
313
|
|
|
276
314
|
if (format === 'markdown') {
|
|
277
|
-
console.log(colors.processingMessage('Analyzing untracked files...'))
|
|
315
|
+
console.log(colors.processingMessage('Analyzing untracked files...'))
|
|
278
316
|
}
|
|
279
317
|
|
|
280
318
|
try {
|
|
281
|
-
const untrackedFiles = this.gitManager.getUntrackedFiles()
|
|
319
|
+
const untrackedFiles = this.gitManager.getUntrackedFiles()
|
|
282
320
|
|
|
283
321
|
if (untrackedFiles.length === 0) {
|
|
284
|
-
const message = 'No untracked files found'
|
|
322
|
+
const message = 'No untracked files found'
|
|
285
323
|
if (format === 'json') {
|
|
286
|
-
outputData({ message, files: [] }, format)
|
|
287
|
-
return { message, files: [] }
|
|
324
|
+
outputData({ message, files: [] }, format)
|
|
325
|
+
return { message, files: [] }
|
|
288
326
|
}
|
|
289
|
-
console.log(colors.successMessage('✅ No untracked files found'))
|
|
290
|
-
return
|
|
327
|
+
console.log(colors.successMessage('✅ No untracked files found'))
|
|
328
|
+
return
|
|
291
329
|
}
|
|
292
330
|
|
|
293
|
-
const categories = this.categorizeUntrackedFiles(untrackedFiles)
|
|
294
|
-
const recommendations = this.generateUntrackedRecommendations(categories)
|
|
331
|
+
const categories = this.categorizeUntrackedFiles(untrackedFiles)
|
|
332
|
+
const recommendations = this.generateUntrackedRecommendations(categories)
|
|
295
333
|
|
|
296
|
-
let aiAnalysis = null
|
|
334
|
+
let aiAnalysis = null
|
|
297
335
|
if (this.aiAnalysisService.hasAI && untrackedFiles.length > 0) {
|
|
298
|
-
aiAnalysis = await this.aiAnalysisService.getUntrackedFilesAIAnalysis(categories)
|
|
336
|
+
aiAnalysis = await this.aiAnalysisService.getUntrackedFilesAIAnalysis(categories)
|
|
299
337
|
}
|
|
300
338
|
|
|
301
339
|
const data = {
|
|
@@ -305,80 +343,79 @@ export class GitRepositoryAnalyzer {
|
|
|
305
343
|
categories: Object.keys(categories).reduce((acc, key) => {
|
|
306
344
|
acc[key] = {
|
|
307
345
|
count: categories[key].length,
|
|
308
|
-
files: categories[key]
|
|
309
|
-
}
|
|
310
|
-
return acc
|
|
346
|
+
files: categories[key],
|
|
347
|
+
}
|
|
348
|
+
return acc
|
|
311
349
|
}, {}),
|
|
312
350
|
summary: {
|
|
313
351
|
totalFiles: untrackedFiles.length,
|
|
314
352
|
categoryCounts: Object.keys(categories).reduce((acc, key) => {
|
|
315
|
-
acc[key] = categories[key].length
|
|
316
|
-
return acc
|
|
317
|
-
}, {})
|
|
353
|
+
acc[key] = categories[key].length
|
|
354
|
+
return acc
|
|
355
|
+
}, {}),
|
|
318
356
|
},
|
|
319
357
|
recommendations,
|
|
320
|
-
aiAnalysis
|
|
321
|
-
}
|
|
358
|
+
aiAnalysis,
|
|
359
|
+
}
|
|
322
360
|
|
|
323
361
|
if (format === 'json') {
|
|
324
|
-
outputData(data, format)
|
|
325
|
-
return data
|
|
362
|
+
outputData(data, format)
|
|
363
|
+
return data
|
|
326
364
|
}
|
|
327
365
|
|
|
328
366
|
// Markdown format display
|
|
329
|
-
console.log(colors.header('\n📄 Untracked Files Analysis:'))
|
|
367
|
+
console.log(colors.header('\n📄 Untracked Files Analysis:'))
|
|
330
368
|
|
|
331
369
|
Object.entries(categories).forEach(([category, files]) => {
|
|
332
370
|
if (files.length > 0) {
|
|
333
|
-
console.log(colors.subheader(`\n📁 ${category} (${colors.number(files.length)} files):`))
|
|
334
|
-
files.slice(0, 10).forEach(file => console.log(` - ${colors.file(file)}`))
|
|
371
|
+
console.log(colors.subheader(`\n📁 ${category} (${colors.number(files.length)} files):`))
|
|
372
|
+
files.slice(0, 10).forEach((file) => console.log(` - ${colors.file(file)}`))
|
|
335
373
|
if (files.length > 10) {
|
|
336
|
-
console.log(colors.dim(` ... and ${files.length - 10} more`))
|
|
374
|
+
console.log(colors.dim(` ... and ${files.length - 10} more`))
|
|
337
375
|
}
|
|
338
376
|
}
|
|
339
|
-
})
|
|
377
|
+
})
|
|
340
378
|
|
|
341
379
|
// Provide recommendations
|
|
342
380
|
if (recommendations.length > 0) {
|
|
343
|
-
console.log(colors.infoMessage('\n💡 Recommendations:'))
|
|
344
|
-
recommendations.forEach(rec => console.log(` - ${rec}`))
|
|
381
|
+
console.log(colors.infoMessage('\n💡 Recommendations:'))
|
|
382
|
+
recommendations.forEach((rec) => console.log(` - ${rec}`))
|
|
345
383
|
}
|
|
346
384
|
|
|
347
385
|
if (aiAnalysis) {
|
|
348
|
-
console.log(colors.aiMessage('\n🤖 AI Analysis of untracked files:'))
|
|
349
|
-
console.log(aiAnalysis)
|
|
386
|
+
console.log(colors.aiMessage('\n🤖 AI Analysis of untracked files:'))
|
|
387
|
+
console.log(aiAnalysis)
|
|
350
388
|
}
|
|
351
389
|
|
|
352
|
-
return data
|
|
353
|
-
|
|
390
|
+
return data
|
|
354
391
|
} catch (error) {
|
|
355
392
|
if (format === 'json') {
|
|
356
|
-
outputData({ error: `Error analyzing untracked files: ${error.message}` }, format)
|
|
357
|
-
return { error: error.message }
|
|
393
|
+
outputData({ error: `Error analyzing untracked files: ${error.message}` }, format)
|
|
394
|
+
return { error: error.message }
|
|
358
395
|
}
|
|
359
|
-
console.error(colors.errorMessage(`Error analyzing untracked files: ${error.message}`))
|
|
360
|
-
throw error
|
|
396
|
+
console.error(colors.errorMessage(`Error analyzing untracked files: ${error.message}`))
|
|
397
|
+
throw error
|
|
361
398
|
}
|
|
362
399
|
}
|
|
363
400
|
|
|
364
401
|
async assessRepositoryHealth(config = {}) {
|
|
365
402
|
if (!this.gitManager.isGitRepo) {
|
|
366
|
-
const errorMsg = 'Not a git repository'
|
|
403
|
+
const errorMsg = 'Not a git repository'
|
|
367
404
|
if (config.format === 'json') {
|
|
368
|
-
outputData({ error: errorMsg }, config.format)
|
|
369
|
-
return
|
|
405
|
+
outputData({ error: errorMsg }, config.format)
|
|
406
|
+
return
|
|
370
407
|
}
|
|
371
|
-
console.log(colors.errorMessage(errorMsg))
|
|
372
|
-
return
|
|
408
|
+
console.log(colors.errorMessage(errorMsg))
|
|
409
|
+
return
|
|
373
410
|
}
|
|
374
411
|
|
|
375
412
|
try {
|
|
376
|
-
console.log(colors.processingMessage('Assessing repository health...'))
|
|
413
|
+
console.log(colors.processingMessage('Assessing repository health...'))
|
|
377
414
|
|
|
378
415
|
// Gather repository health metrics
|
|
379
|
-
const healthMetrics = await this.gatherHealthMetrics()
|
|
380
|
-
const healthScore = this.calculateHealthScore(healthMetrics)
|
|
381
|
-
const recommendations = this.generateHealthRecommendations(healthMetrics)
|
|
416
|
+
const healthMetrics = await this.gatherHealthMetrics()
|
|
417
|
+
const healthScore = this.calculateHealthScore(healthMetrics)
|
|
418
|
+
const recommendations = this.generateHealthRecommendations(healthMetrics)
|
|
382
419
|
|
|
383
420
|
const data = {
|
|
384
421
|
type: 'repository_health_assessment',
|
|
@@ -391,51 +428,70 @@ export class GitRepositoryAnalyzer {
|
|
|
391
428
|
commitQuality: healthScore.commitQuality,
|
|
392
429
|
branchHygiene: healthScore.branchHygiene,
|
|
393
430
|
fileOrganization: healthScore.fileOrganization,
|
|
394
|
-
activityLevel: healthScore.activityLevel
|
|
395
|
-
}
|
|
396
|
-
}
|
|
431
|
+
activityLevel: healthScore.activityLevel,
|
|
432
|
+
},
|
|
433
|
+
}
|
|
397
434
|
|
|
398
435
|
if (config.format === 'json') {
|
|
399
|
-
outputData(data, config.format)
|
|
400
|
-
return data
|
|
436
|
+
outputData(data, config.format)
|
|
437
|
+
return data
|
|
401
438
|
}
|
|
402
439
|
|
|
403
440
|
// Display health assessment
|
|
404
|
-
console.log(colors.header('\n🏥 Repository Health Assessment:'))
|
|
441
|
+
console.log(colors.header('\n🏥 Repository Health Assessment:'))
|
|
405
442
|
|
|
406
443
|
// Overall score
|
|
407
|
-
const gradeColor = this.getGradeColor(healthScore.grade)
|
|
408
|
-
console.log(
|
|
444
|
+
const gradeColor = this.getGradeColor(healthScore.grade)
|
|
445
|
+
console.log(
|
|
446
|
+
colors.subheader(
|
|
447
|
+
`\n📊 Overall Health: ${gradeColor(healthScore.grade)} (${colors.number(healthScore.overall)}/100)`
|
|
448
|
+
)
|
|
449
|
+
)
|
|
409
450
|
|
|
410
451
|
// Category breakdown
|
|
411
|
-
console.log(colors.subheader('\n📋 Category Scores:'))
|
|
412
|
-
console.log(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
console.log(
|
|
452
|
+
console.log(colors.subheader('\n📋 Category Scores:'))
|
|
453
|
+
console.log(
|
|
454
|
+
` ${colors.label('Commit Quality')}: ${colors.number(healthScore.commitQuality)}/100`
|
|
455
|
+
)
|
|
456
|
+
console.log(
|
|
457
|
+
` ${colors.label('Branch Hygiene')}: ${colors.number(healthScore.branchHygiene)}/100`
|
|
458
|
+
)
|
|
459
|
+
console.log(
|
|
460
|
+
` ${colors.label('File Organization')}: ${colors.number(healthScore.fileOrganization)}/100`
|
|
461
|
+
)
|
|
462
|
+
console.log(
|
|
463
|
+
` ${colors.label('Activity Level')}: ${colors.number(healthScore.activityLevel)}/100`
|
|
464
|
+
)
|
|
416
465
|
|
|
417
466
|
// Key metrics
|
|
418
|
-
console.log(colors.subheader('\n📈 Key Metrics:'))
|
|
419
|
-
console.log(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
console.log(
|
|
467
|
+
console.log(colors.subheader('\n📈 Key Metrics:'))
|
|
468
|
+
console.log(
|
|
469
|
+
` ${colors.label('Average commit message length')}: ${colors.number(healthMetrics.avgCommitMessageLength)} chars`
|
|
470
|
+
)
|
|
471
|
+
console.log(
|
|
472
|
+
` ${colors.label('Recent commits')}: ${colors.number(healthMetrics.recentCommitCount)} (last 30 days)`
|
|
473
|
+
)
|
|
474
|
+
console.log(
|
|
475
|
+
` ${colors.label('Active branches')}: ${colors.number(healthMetrics.activeBranchCount)}`
|
|
476
|
+
)
|
|
477
|
+
console.log(
|
|
478
|
+
` ${colors.label('Untracked files')}: ${colors.number(healthMetrics.untrackedFileCount)}`
|
|
479
|
+
)
|
|
423
480
|
|
|
424
481
|
// Recommendations
|
|
425
482
|
if (recommendations.length > 0) {
|
|
426
|
-
console.log(colors.infoMessage('\n💡 Health Recommendations:'))
|
|
427
|
-
recommendations.forEach(rec => console.log(` - ${rec}`))
|
|
483
|
+
console.log(colors.infoMessage('\n💡 Health Recommendations:'))
|
|
484
|
+
recommendations.forEach((rec) => console.log(` - ${rec}`))
|
|
428
485
|
}
|
|
429
486
|
|
|
430
|
-
return data
|
|
431
|
-
|
|
487
|
+
return data
|
|
432
488
|
} catch (error) {
|
|
433
|
-
console.error(colors.errorMessage(`Error assessing repository health: ${error.message}`))
|
|
489
|
+
console.error(colors.errorMessage(`Error assessing repository health: ${error.message}`))
|
|
434
490
|
if (config.format === 'json') {
|
|
435
|
-
outputData({ error: error.message }, config.format)
|
|
436
|
-
return { error: error.message }
|
|
491
|
+
outputData({ error: error.message }, config.format)
|
|
492
|
+
return { error: error.message }
|
|
437
493
|
}
|
|
438
|
-
return { error: error.message }
|
|
494
|
+
return { error: error.message }
|
|
439
495
|
}
|
|
440
496
|
}
|
|
441
497
|
|
|
@@ -447,98 +503,132 @@ export class GitRepositoryAnalyzer {
|
|
|
447
503
|
documentation: [],
|
|
448
504
|
assets: [],
|
|
449
505
|
temporary: [],
|
|
450
|
-
other: []
|
|
451
|
-
}
|
|
506
|
+
other: [],
|
|
507
|
+
}
|
|
452
508
|
|
|
453
|
-
files.forEach(file => {
|
|
454
|
-
const ext = file.split('.').pop()?.toLowerCase()
|
|
455
|
-
const path = file.toLowerCase()
|
|
509
|
+
files.forEach((file) => {
|
|
510
|
+
const ext = file.split('.').pop()?.toLowerCase()
|
|
511
|
+
const path = file.toLowerCase()
|
|
456
512
|
|
|
457
513
|
if (['js', 'ts', 'py', 'java', 'cpp', 'c', 'rs', 'go'].includes(ext)) {
|
|
458
|
-
categories.source.push(file)
|
|
459
|
-
} else if (
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
514
|
+
categories.source.push(file)
|
|
515
|
+
} else if (
|
|
516
|
+
['json', 'yaml', 'yml', 'toml', 'ini', 'env'].includes(ext) ||
|
|
517
|
+
path.includes('config')
|
|
518
|
+
) {
|
|
519
|
+
categories.config.push(file)
|
|
520
|
+
} else if (
|
|
521
|
+
['md', 'txt', 'rst'].includes(ext) ||
|
|
522
|
+
path.includes('readme') ||
|
|
523
|
+
path.includes('doc')
|
|
524
|
+
) {
|
|
525
|
+
categories.documentation.push(file)
|
|
463
526
|
} else if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'css', 'scss'].includes(ext)) {
|
|
464
|
-
categories.assets.push(file)
|
|
465
|
-
} else if (
|
|
466
|
-
|
|
527
|
+
categories.assets.push(file)
|
|
528
|
+
} else if (
|
|
529
|
+
['tmp', 'temp', 'log', 'cache'].some((temp) => path.includes(temp)) ||
|
|
530
|
+
file.startsWith('.')
|
|
531
|
+
) {
|
|
532
|
+
categories.temporary.push(file)
|
|
467
533
|
} else {
|
|
468
|
-
categories.other.push(file)
|
|
534
|
+
categories.other.push(file)
|
|
469
535
|
}
|
|
470
|
-
})
|
|
536
|
+
})
|
|
471
537
|
|
|
472
|
-
return categories
|
|
538
|
+
return categories
|
|
473
539
|
}
|
|
474
540
|
|
|
475
541
|
generateUntrackedRecommendations(categories) {
|
|
476
|
-
const recommendations = []
|
|
542
|
+
const recommendations = []
|
|
477
543
|
|
|
478
544
|
if (categories.source?.length > 0) {
|
|
479
|
-
recommendations.push('Consider adding source files to git or updating .gitignore')
|
|
545
|
+
recommendations.push('Consider adding source files to git or updating .gitignore')
|
|
480
546
|
}
|
|
481
547
|
if (categories.temporary?.length > 0) {
|
|
482
|
-
recommendations.push('Add temporary files to .gitignore to keep repository clean')
|
|
548
|
+
recommendations.push('Add temporary files to .gitignore to keep repository clean')
|
|
483
549
|
}
|
|
484
550
|
if (categories.config?.length > 0) {
|
|
485
|
-
recommendations.push('Review configuration files - add sensitive configs to .gitignore')
|
|
551
|
+
recommendations.push('Review configuration files - add sensitive configs to .gitignore')
|
|
486
552
|
}
|
|
487
553
|
if (categories.documentation?.length > 0) {
|
|
488
|
-
recommendations.push('Consider adding documentation files to the repository')
|
|
554
|
+
recommendations.push('Consider adding documentation files to the repository')
|
|
489
555
|
}
|
|
490
556
|
|
|
491
|
-
return recommendations
|
|
557
|
+
return recommendations
|
|
492
558
|
}
|
|
493
559
|
|
|
494
|
-
|
|
495
|
-
const recentCommits = this.gitManager.getRecentCommits(100)
|
|
496
|
-
const branches = this.gitManager.getAllBranches()
|
|
497
|
-
const untrackedFiles = this.gitManager.getUntrackedFiles()
|
|
560
|
+
gatherHealthMetrics() {
|
|
561
|
+
const recentCommits = this.gitManager.getRecentCommits(100)
|
|
562
|
+
const branches = this.gitManager.getAllBranches()
|
|
563
|
+
const untrackedFiles = this.gitManager.getUntrackedFiles()
|
|
498
564
|
|
|
499
565
|
return {
|
|
500
566
|
totalCommits: recentCommits.length,
|
|
501
|
-
recentCommitCount: recentCommits.filter(
|
|
502
|
-
new Date(c.date) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
|
|
567
|
+
recentCommitCount: recentCommits.filter(
|
|
568
|
+
(c) => new Date(c.date) > new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
|
|
503
569
|
).length,
|
|
504
|
-
avgCommitMessageLength:
|
|
570
|
+
avgCommitMessageLength:
|
|
571
|
+
recentCommits.reduce((sum, c) => sum + c.subject.length, 0) / recentCommits.length || 0,
|
|
505
572
|
activeBranchCount: branches.local?.length || 0,
|
|
506
573
|
untrackedFileCount: untrackedFiles.length,
|
|
507
574
|
hasGitignore: this.gitManager.hasFile('.gitignore'),
|
|
508
|
-
hasReadme: this.gitManager.hasFile('README.md') || this.gitManager.hasFile('readme.md')
|
|
509
|
-
}
|
|
575
|
+
hasReadme: this.gitManager.hasFile('README.md') || this.gitManager.hasFile('readme.md'),
|
|
576
|
+
}
|
|
510
577
|
}
|
|
511
578
|
|
|
512
579
|
calculateHealthScore(metrics) {
|
|
513
|
-
let commitQuality = 100
|
|
514
|
-
let branchHygiene = 100
|
|
515
|
-
let fileOrganization = 100
|
|
516
|
-
let activityLevel = 100
|
|
580
|
+
let commitQuality = 100
|
|
581
|
+
let branchHygiene = 100
|
|
582
|
+
let fileOrganization = 100
|
|
583
|
+
let activityLevel = 100
|
|
517
584
|
|
|
518
585
|
// Commit quality assessment
|
|
519
|
-
if (metrics.avgCommitMessageLength < 10)
|
|
520
|
-
|
|
586
|
+
if (metrics.avgCommitMessageLength < 10) {
|
|
587
|
+
commitQuality -= 30
|
|
588
|
+
} else if (metrics.avgCommitMessageLength < 20) {
|
|
589
|
+
commitQuality -= 15
|
|
590
|
+
}
|
|
521
591
|
|
|
522
592
|
// Branch hygiene
|
|
523
|
-
if (metrics.activeBranchCount > 10)
|
|
524
|
-
|
|
593
|
+
if (metrics.activeBranchCount > 10) {
|
|
594
|
+
branchHygiene -= 20
|
|
595
|
+
}
|
|
596
|
+
if (metrics.activeBranchCount > 20) {
|
|
597
|
+
branchHygiene -= 30
|
|
598
|
+
}
|
|
525
599
|
|
|
526
600
|
// File organization
|
|
527
|
-
if (!metrics.hasGitignore)
|
|
528
|
-
|
|
529
|
-
|
|
601
|
+
if (!metrics.hasGitignore) {
|
|
602
|
+
fileOrganization -= 20
|
|
603
|
+
}
|
|
604
|
+
if (!metrics.hasReadme) {
|
|
605
|
+
fileOrganization -= 15
|
|
606
|
+
}
|
|
607
|
+
if (metrics.untrackedFileCount > 10) {
|
|
608
|
+
fileOrganization -= 20
|
|
609
|
+
}
|
|
530
610
|
|
|
531
611
|
// Activity level
|
|
532
|
-
if (metrics.recentCommitCount === 0)
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
612
|
+
if (metrics.recentCommitCount === 0) {
|
|
613
|
+
activityLevel -= 50
|
|
614
|
+
} else if (metrics.recentCommitCount < 5) {
|
|
615
|
+
activityLevel -= 20
|
|
616
|
+
}
|
|
536
617
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
618
|
+
const overall = Math.round(
|
|
619
|
+
(commitQuality + branchHygiene + fileOrganization + activityLevel) / 4
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
let grade = 'F'
|
|
623
|
+
if (overall >= 90) {
|
|
624
|
+
grade = 'A'
|
|
625
|
+
} else if (overall >= 80) {
|
|
626
|
+
grade = 'B'
|
|
627
|
+
} else if (overall >= 70) {
|
|
628
|
+
grade = 'C'
|
|
629
|
+
} else if (overall >= 60) {
|
|
630
|
+
grade = 'D'
|
|
631
|
+
}
|
|
542
632
|
|
|
543
633
|
return {
|
|
544
634
|
overall: Math.max(0, overall),
|
|
@@ -546,43 +636,43 @@ export class GitRepositoryAnalyzer {
|
|
|
546
636
|
branchHygiene: Math.max(0, branchHygiene),
|
|
547
637
|
fileOrganization: Math.max(0, fileOrganization),
|
|
548
638
|
activityLevel: Math.max(0, activityLevel),
|
|
549
|
-
grade
|
|
550
|
-
}
|
|
639
|
+
grade,
|
|
640
|
+
}
|
|
551
641
|
}
|
|
552
642
|
|
|
553
643
|
generateHealthRecommendations(metrics) {
|
|
554
|
-
const recommendations = []
|
|
644
|
+
const recommendations = []
|
|
555
645
|
|
|
556
646
|
if (metrics.avgCommitMessageLength < 20) {
|
|
557
|
-
recommendations.push('Write more descriptive commit messages (aim for 20+ characters)')
|
|
647
|
+
recommendations.push('Write more descriptive commit messages (aim for 20+ characters)')
|
|
558
648
|
}
|
|
559
649
|
if (metrics.activeBranchCount > 10) {
|
|
560
|
-
recommendations.push('Consider cleaning up old/merged branches')
|
|
650
|
+
recommendations.push('Consider cleaning up old/merged branches')
|
|
561
651
|
}
|
|
562
652
|
if (!metrics.hasGitignore) {
|
|
563
|
-
recommendations.push('Add a .gitignore file to exclude unwanted files')
|
|
653
|
+
recommendations.push('Add a .gitignore file to exclude unwanted files')
|
|
564
654
|
}
|
|
565
655
|
if (!metrics.hasReadme) {
|
|
566
|
-
recommendations.push('Add a README.md file to document your project')
|
|
656
|
+
recommendations.push('Add a README.md file to document your project')
|
|
567
657
|
}
|
|
568
658
|
if (metrics.untrackedFileCount > 5) {
|
|
569
|
-
recommendations.push('Review and either commit or ignore untracked files')
|
|
659
|
+
recommendations.push('Review and either commit or ignore untracked files')
|
|
570
660
|
}
|
|
571
661
|
if (metrics.recentCommitCount < 5) {
|
|
572
|
-
recommendations.push('Consider more frequent commits for better project tracking')
|
|
662
|
+
recommendations.push('Consider more frequent commits for better project tracking')
|
|
573
663
|
}
|
|
574
664
|
|
|
575
|
-
return recommendations
|
|
665
|
+
return recommendations
|
|
576
666
|
}
|
|
577
667
|
|
|
578
668
|
getGradeColor(grade) {
|
|
579
669
|
const colors_map = {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
}
|
|
586
|
-
return colors_map[grade] || colors.infoMessage
|
|
670
|
+
A: colors.successMessage,
|
|
671
|
+
B: colors.successMessage,
|
|
672
|
+
C: colors.warningMessage,
|
|
673
|
+
D: colors.warningMessage,
|
|
674
|
+
F: colors.errorMessage,
|
|
675
|
+
}
|
|
676
|
+
return colors_map[grade] || colors.infoMessage
|
|
587
677
|
}
|
|
588
|
-
}
|
|
678
|
+
}
|