@entro314labs/ai-changelog-generator 3.1.1 → 3.2.1
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 +412 -875
- package/README.md +8 -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 +80 -48
- package/src/ai-changelog-generator.js +91 -81
- package/src/application/orchestrators/changelog.orchestrator.js +791 -516
- package/src/application/services/application.service.js +137 -128
- package/src/cli.js +76 -57
- package/src/domains/ai/ai-analysis.service.js +289 -209
- package/src/domains/analysis/analysis.engine.js +328 -192
- package/src/domains/changelog/changelog.service.js +1174 -783
- package/src/domains/changelog/workspace-changelog.service.js +487 -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 +390 -274
- package/src/infrastructure/config/configuration.manager.js +220 -190
- package/src/infrastructure/interactive/interactive-staging.service.js +154 -135
- 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 +259 -161
- package/src/shared/constants/colors.js +453 -180
- 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 +1117 -945
- package/types/index.d.ts +353 -344
|
@@ -1,56 +1,68 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import colors from '../../shared/constants/colors.js'
|
|
2
|
+
import { GitError } from '../../shared/utils/error-classes.js'
|
|
3
|
+
import {
|
|
4
|
+
analyzeFunctionalImpact,
|
|
5
|
+
analyzeSemanticChanges,
|
|
6
|
+
assessChangeComplexity,
|
|
7
|
+
assessFileImportance,
|
|
8
|
+
categorizeFile,
|
|
9
|
+
detectLanguage,
|
|
10
|
+
} from '../../shared/utils/utils.js'
|
|
4
11
|
|
|
5
12
|
export class GitService {
|
|
6
13
|
constructor(gitManager, tagger) {
|
|
7
|
-
this.gitManager = gitManager
|
|
8
|
-
this.tagger = tagger
|
|
14
|
+
this.gitManager = gitManager
|
|
15
|
+
this.tagger = tagger
|
|
9
16
|
}
|
|
10
17
|
|
|
11
18
|
async getCommitAnalysis(commitHash) {
|
|
12
19
|
try {
|
|
13
20
|
// Validate commit hash first
|
|
14
21
|
if (!this.gitManager.validateCommitHash(commitHash)) {
|
|
15
|
-
console.warn(colors.warningMessage(`Invalid commit hash: ${colors.hash(commitHash)}`))
|
|
16
|
-
return null
|
|
22
|
+
console.warn(colors.warningMessage(`Invalid commit hash: ${colors.hash(commitHash)}`))
|
|
23
|
+
return null
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
// Get comprehensive commit information
|
|
20
|
-
const commitInfo = this.gitManager.execGit(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
27
|
+
const commitInfo = this.gitManager.execGit(
|
|
28
|
+
`git show --pretty=format:"%H|%s|%an|%ad|%B" --no-patch ${commitHash}`
|
|
29
|
+
)
|
|
30
|
+
const lines = commitInfo.split('\n')
|
|
31
|
+
const [hash, subject, author, date] = lines[0].split('|')
|
|
32
|
+
const body = lines.slice(1).join('\n').trim()
|
|
24
33
|
|
|
25
34
|
// Get files with detailed analysis
|
|
26
|
-
const filesCommand = `git show --name-status --pretty=format: ${commitHash}
|
|
27
|
-
const filesOutput = this.gitManager.execGitSafe(filesCommand)
|
|
35
|
+
const filesCommand = `git show --name-status --pretty=format: ${commitHash}`
|
|
36
|
+
const filesOutput = this.gitManager.execGitSafe(filesCommand)
|
|
28
37
|
const files = await Promise.all(
|
|
29
|
-
filesOutput
|
|
38
|
+
filesOutput
|
|
39
|
+
.split('\n')
|
|
30
40
|
.filter(Boolean)
|
|
31
41
|
.map(async (line) => {
|
|
32
|
-
const parts = line.split('\t')
|
|
33
|
-
if (parts.length < 2)
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
const parts = line.split('\t')
|
|
43
|
+
if (parts.length < 2) {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
const [status, filePath] = parts
|
|
47
|
+
return await this.analyzeFileChange(commitHash, status, filePath)
|
|
36
48
|
})
|
|
37
|
-
)
|
|
49
|
+
)
|
|
38
50
|
|
|
39
51
|
// Filter out null entries
|
|
40
|
-
const validFiles = files.filter(Boolean)
|
|
52
|
+
const validFiles = files.filter(Boolean)
|
|
41
53
|
|
|
42
54
|
// Get overall diff statistics
|
|
43
|
-
const diffStats = this.getCommitDiffStats(commitHash)
|
|
55
|
+
const diffStats = this.getCommitDiffStats(commitHash)
|
|
44
56
|
|
|
45
57
|
// Use intelligent tagging system
|
|
46
58
|
const commitForTagging = {
|
|
47
59
|
hash: hash.substring(0, 7),
|
|
48
60
|
message: subject,
|
|
49
|
-
files: validFiles.map(f => ({ path: f.filePath })),
|
|
50
|
-
stats: diffStats
|
|
51
|
-
}
|
|
61
|
+
files: validFiles.map((f) => ({ path: f.filePath })),
|
|
62
|
+
stats: diffStats,
|
|
63
|
+
}
|
|
52
64
|
|
|
53
|
-
const taggingAnalysis = this.tagger.analyzeCommit(commitForTagging)
|
|
65
|
+
const taggingAnalysis = this.tagger.analyzeCommit(commitForTagging)
|
|
54
66
|
|
|
55
67
|
const analysis = {
|
|
56
68
|
hash: hash.substring(0, 7),
|
|
@@ -65,58 +77,62 @@ export class GitService {
|
|
|
65
77
|
breakingChanges: taggingAnalysis.breakingChanges || [],
|
|
66
78
|
categories: taggingAnalysis.categories || [],
|
|
67
79
|
importance: taggingAnalysis.importance || 'medium',
|
|
68
|
-
tags: taggingAnalysis.tags || []
|
|
69
|
-
}
|
|
80
|
+
tags: taggingAnalysis.tags || [],
|
|
81
|
+
}
|
|
70
82
|
|
|
71
|
-
return analysis
|
|
83
|
+
return analysis
|
|
72
84
|
} catch (error) {
|
|
73
|
-
const gitError = GitError.fromCommandFailure('show', null, null, error.message, error)
|
|
74
|
-
console.error(colors.errorMessage(`Error analyzing commit ${commitHash}:`), gitError.message)
|
|
75
|
-
return null
|
|
85
|
+
const gitError = GitError.fromCommandFailure('show', null, null, error.message, error)
|
|
86
|
+
console.error(colors.errorMessage(`Error analyzing commit ${commitHash}:`), gitError.message)
|
|
87
|
+
return null
|
|
76
88
|
}
|
|
77
89
|
}
|
|
78
90
|
|
|
79
91
|
async analyzeFileChange(commitHash, status, filePath) {
|
|
80
92
|
try {
|
|
81
93
|
// Get file diff with context
|
|
82
|
-
const diffCommand = `git show ${commitHash} --pretty=format: -U5 -- "${filePath}"
|
|
83
|
-
let diff = ''
|
|
94
|
+
const diffCommand = `git show ${commitHash} --pretty=format: -U5 -- "${filePath}"`
|
|
95
|
+
let diff = ''
|
|
84
96
|
|
|
85
|
-
diff = this.gitManager.execGitShow(diffCommand)
|
|
97
|
+
diff = this.gitManager.execGitShow(diffCommand)
|
|
86
98
|
|
|
87
99
|
if (diff === null) {
|
|
88
100
|
if (status === 'D') {
|
|
89
|
-
diff = 'File deleted in this commit'
|
|
101
|
+
diff = 'File deleted in this commit'
|
|
90
102
|
} else {
|
|
91
|
-
console.warn(
|
|
92
|
-
|
|
103
|
+
console.warn(
|
|
104
|
+
colors.warningMessage(
|
|
105
|
+
`⚠️ File ${colors.file(filePath)} diff failed for commit ${commitHash}`
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
diff = 'File content unavailable (git show failed)'
|
|
93
109
|
}
|
|
94
110
|
} else if (!diff || diff.trim() === '') {
|
|
95
111
|
if (status === 'A') {
|
|
96
112
|
// For new files, try to get the content directly
|
|
97
|
-
const newFileContent = this.gitManager.execGitShow(`git show ${commitHash}:"${filePath}"`)
|
|
113
|
+
const newFileContent = this.gitManager.execGitShow(`git show ${commitHash}:"${filePath}"`)
|
|
98
114
|
if (newFileContent && newFileContent.length > 0) {
|
|
99
|
-
diff = `New file created with content:\n${newFileContent.slice(0, 1000)}${newFileContent.length > 1000 ? '\n...' : ''}
|
|
115
|
+
diff = `New file created with content:\n${newFileContent.slice(0, 1000)}${newFileContent.length > 1000 ? '\n...' : ''}`
|
|
100
116
|
} else {
|
|
101
|
-
diff = 'New file created (content unavailable)'
|
|
117
|
+
diff = 'New file created (content unavailable)'
|
|
102
118
|
}
|
|
103
119
|
} else {
|
|
104
|
-
diff = 'No changes detected (binary or empty file)'
|
|
120
|
+
diff = 'No changes detected (binary or empty file)'
|
|
105
121
|
}
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
// Get file content context
|
|
109
|
-
let beforeContent = ''
|
|
110
|
-
let afterContent = ''
|
|
125
|
+
let beforeContent = ''
|
|
126
|
+
let afterContent = ''
|
|
111
127
|
|
|
112
128
|
if (status !== 'A' && !diff.includes('not available')) {
|
|
113
|
-
const beforeResult = this.gitManager.execGitShow(`git show ${commitHash}~1:"${filePath}"`)
|
|
114
|
-
beforeContent = beforeResult ? beforeResult.slice(0, 1000) : ''
|
|
129
|
+
const beforeResult = this.gitManager.execGitShow(`git show ${commitHash}~1:"${filePath}"`)
|
|
130
|
+
beforeContent = beforeResult ? beforeResult.slice(0, 1000) : ''
|
|
115
131
|
}
|
|
116
132
|
|
|
117
133
|
if (status !== 'D' && !diff.includes('not available')) {
|
|
118
|
-
const afterResult = this.gitManager.execGitShow(`git show ${commitHash}:"${filePath}"`)
|
|
119
|
-
afterContent = afterResult ? afterResult.slice(0, 1000) : ''
|
|
134
|
+
const afterResult = this.gitManager.execGitShow(`git show ${commitHash}:"${filePath}"`)
|
|
135
|
+
afterContent = afterResult ? afterResult.slice(0, 1000) : ''
|
|
120
136
|
}
|
|
121
137
|
|
|
122
138
|
return {
|
|
@@ -130,96 +146,96 @@ export class GitService {
|
|
|
130
146
|
importance: assessFileImportance(filePath, status),
|
|
131
147
|
complexity: assessChangeComplexity(diff),
|
|
132
148
|
semanticChanges: analyzeSemanticChanges(diff, filePath),
|
|
133
|
-
functionalImpact: analyzeFunctionalImpact(diff, filePath, status)
|
|
134
|
-
}
|
|
149
|
+
functionalImpact: analyzeFunctionalImpact(diff, filePath, status),
|
|
150
|
+
}
|
|
135
151
|
} catch (error) {
|
|
136
|
-
console.error(colors.errorMessage(`Error analyzing file change ${filePath}:`), error.message)
|
|
137
|
-
return null
|
|
152
|
+
console.error(colors.errorMessage(`Error analyzing file change ${filePath}:`), error.message)
|
|
153
|
+
return null
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
156
|
|
|
141
157
|
async analyzeWorkingDirectoryFileChange(status, filePath) {
|
|
142
158
|
try {
|
|
143
|
-
let diff = ''
|
|
159
|
+
let diff = ''
|
|
144
160
|
|
|
145
161
|
// Get working directory diff based on status
|
|
146
162
|
if (status === 'A' || status === '??') {
|
|
147
163
|
// New/untracked file - show entire content (first 50 lines)
|
|
148
164
|
try {
|
|
149
|
-
const content = this.gitManager.execGitSafe(`cat "${filePath}"`)
|
|
150
|
-
if (content
|
|
151
|
-
const lines = content.split('\n').slice(0, 50)
|
|
152
|
-
diff = `New file created with ${content.split('\n').length} lines\n\nContent preview:\n${lines.join('\n')}${content.split('\n').length > 50 ? '\n... (truncated)' : ''}
|
|
165
|
+
const content = this.gitManager.execGitSafe(`cat "${filePath}"`)
|
|
166
|
+
if (content?.trim()) {
|
|
167
|
+
const lines = content.split('\n').slice(0, 50)
|
|
168
|
+
diff = `New file created with ${content.split('\n').length} lines\n\nContent preview:\n${lines.join('\n')}${content.split('\n').length > 50 ? '\n... (truncated)' : ''}`
|
|
153
169
|
} else {
|
|
154
|
-
diff = 'New empty file created'
|
|
170
|
+
diff = 'New empty file created'
|
|
155
171
|
}
|
|
156
172
|
} catch {
|
|
157
|
-
diff = 'New file created (binary or inaccessible)'
|
|
173
|
+
diff = 'New file created (binary or inaccessible)'
|
|
158
174
|
}
|
|
159
175
|
} else if (status === 'D') {
|
|
160
176
|
// Deleted file - get the content that was removed for better analysis
|
|
161
177
|
try {
|
|
162
|
-
const headContent = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
163
|
-
if (headContent
|
|
178
|
+
const headContent = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
179
|
+
if (headContent?.trim()) {
|
|
164
180
|
// Get first 30 lines to understand what was removed
|
|
165
|
-
const lines = headContent.split('\n').slice(0, 30)
|
|
166
|
-
diff = `File deleted from working directory\n\nRemoved content preview:\n${lines.join('\n')}${headContent.split('\n').length > 30 ? '\n... (truncated)' : ''}
|
|
181
|
+
const lines = headContent.split('\n').slice(0, 30)
|
|
182
|
+
diff = `File deleted from working directory\n\nRemoved content preview:\n${lines.join('\n')}${headContent.split('\n').length > 30 ? '\n... (truncated)' : ''}`
|
|
167
183
|
} else {
|
|
168
|
-
diff = 'File deleted from working directory (content was empty)'
|
|
184
|
+
diff = 'File deleted from working directory (content was empty)'
|
|
169
185
|
}
|
|
170
186
|
} catch {
|
|
171
|
-
diff = 'File deleted from working directory (content unavailable)'
|
|
187
|
+
diff = 'File deleted from working directory (content unavailable)'
|
|
172
188
|
}
|
|
173
189
|
} else if (status === 'M' || status.includes('M')) {
|
|
174
190
|
// Modified file - get actual diff
|
|
175
191
|
try {
|
|
176
|
-
const diffCommand = `git diff HEAD -- "${filePath}"
|
|
177
|
-
diff = this.gitManager.execGitSafe(diffCommand)
|
|
192
|
+
const diffCommand = `git diff HEAD -- "${filePath}"`
|
|
193
|
+
diff = this.gitManager.execGitSafe(diffCommand)
|
|
178
194
|
if (!diff || diff.trim() === '') {
|
|
179
195
|
// Try staged diff if no working directory diff
|
|
180
|
-
const stagedDiff = this.gitManager.execGitSafe(`git diff --cached -- "${filePath}"`)
|
|
181
|
-
diff = stagedDiff || 'No diff available (binary or identical)'
|
|
196
|
+
const stagedDiff = this.gitManager.execGitSafe(`git diff --cached -- "${filePath}"`)
|
|
197
|
+
diff = stagedDiff || 'No diff available (binary or identical)'
|
|
182
198
|
}
|
|
183
199
|
} catch {
|
|
184
|
-
diff = 'Modified file (diff unavailable)'
|
|
200
|
+
diff = 'Modified file (diff unavailable)'
|
|
185
201
|
}
|
|
186
202
|
} else if (status === 'R') {
|
|
187
203
|
// Renamed file
|
|
188
|
-
diff = 'File renamed in working directory'
|
|
204
|
+
diff = 'File renamed in working directory'
|
|
189
205
|
} else {
|
|
190
206
|
// Other status
|
|
191
|
-
diff = `File status: ${status}
|
|
207
|
+
diff = `File status: ${status}`
|
|
192
208
|
}
|
|
193
209
|
|
|
194
210
|
// Get file content context
|
|
195
|
-
let beforeContent = ''
|
|
196
|
-
let afterContent = ''
|
|
211
|
+
let beforeContent = ''
|
|
212
|
+
let afterContent = ''
|
|
197
213
|
|
|
198
214
|
if (status === 'D') {
|
|
199
215
|
// For deleted files, get the content that was removed
|
|
200
216
|
try {
|
|
201
|
-
const headResult = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
202
|
-
beforeContent = headResult ? headResult.slice(0, 1000) : ''
|
|
217
|
+
const headResult = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
218
|
+
beforeContent = headResult ? headResult.slice(0, 1000) : ''
|
|
203
219
|
} catch {
|
|
204
|
-
beforeContent = ''
|
|
220
|
+
beforeContent = ''
|
|
205
221
|
}
|
|
206
222
|
// afterContent stays empty for deleted files
|
|
207
223
|
} else if (status !== 'A' && status !== '??') {
|
|
208
224
|
// For modified files, get both before and after
|
|
209
225
|
try {
|
|
210
226
|
// Get HEAD version
|
|
211
|
-
const headResult = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
212
|
-
beforeContent = headResult ? headResult.slice(0, 1000) : ''
|
|
227
|
+
const headResult = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
228
|
+
beforeContent = headResult ? headResult.slice(0, 1000) : ''
|
|
213
229
|
} catch {
|
|
214
|
-
beforeContent = ''
|
|
230
|
+
beforeContent = ''
|
|
215
231
|
}
|
|
216
232
|
|
|
217
233
|
try {
|
|
218
234
|
// Get current working directory version
|
|
219
|
-
const currentResult = this.gitManager.execGitSafe(`cat "${filePath}"`)
|
|
220
|
-
afterContent = currentResult ? currentResult.slice(0, 1000) : ''
|
|
235
|
+
const currentResult = this.gitManager.execGitSafe(`cat "${filePath}"`)
|
|
236
|
+
afterContent = currentResult ? currentResult.slice(0, 1000) : ''
|
|
221
237
|
} catch {
|
|
222
|
-
afterContent = ''
|
|
238
|
+
afterContent = ''
|
|
223
239
|
}
|
|
224
240
|
}
|
|
225
241
|
|
|
@@ -234,10 +250,13 @@ export class GitService {
|
|
|
234
250
|
importance: assessFileImportance(filePath, status),
|
|
235
251
|
complexity: assessChangeComplexity(diff),
|
|
236
252
|
semanticChanges: analyzeSemanticChanges(diff, filePath),
|
|
237
|
-
functionalImpact: analyzeFunctionalImpact(diff, filePath, status)
|
|
238
|
-
}
|
|
253
|
+
functionalImpact: analyzeFunctionalImpact(diff, filePath, status),
|
|
254
|
+
}
|
|
239
255
|
} catch (error) {
|
|
240
|
-
console.error(
|
|
256
|
+
console.error(
|
|
257
|
+
colors.errorMessage(`Error analyzing working directory file change ${filePath}:`),
|
|
258
|
+
error.message
|
|
259
|
+
)
|
|
241
260
|
return {
|
|
242
261
|
status,
|
|
243
262
|
filePath,
|
|
@@ -249,54 +268,55 @@ export class GitService {
|
|
|
249
268
|
importance: 'medium',
|
|
250
269
|
complexity: { score: 1 },
|
|
251
270
|
semanticChanges: { changeType: 'unknown', patterns: [], frameworks: [] },
|
|
252
|
-
functionalImpact: { scope: 'local', severity: 'low' }
|
|
253
|
-
}
|
|
271
|
+
functionalImpact: { scope: 'local', severity: 'low' },
|
|
272
|
+
}
|
|
254
273
|
}
|
|
255
274
|
}
|
|
256
275
|
|
|
257
276
|
getCommitDiffStats(commitHash) {
|
|
258
277
|
try {
|
|
259
|
-
const command = `git show --stat --pretty=format: ${commitHash}
|
|
260
|
-
const output = this.gitManager.execGitSafe(command)
|
|
261
|
-
const lines = output.split('\n').filter(Boolean)
|
|
262
|
-
const summary = lines
|
|
278
|
+
const command = `git show --stat --pretty=format: ${commitHash}`
|
|
279
|
+
const output = this.gitManager.execGitSafe(command)
|
|
280
|
+
const lines = output.split('\n').filter(Boolean)
|
|
281
|
+
const summary = lines.at(-1)
|
|
263
282
|
|
|
264
|
-
if (summary
|
|
265
|
-
const match = summary.match(
|
|
283
|
+
if (summary?.includes('changed')) {
|
|
284
|
+
const match = summary.match(
|
|
285
|
+
/(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/
|
|
286
|
+
)
|
|
266
287
|
if (match) {
|
|
267
288
|
return {
|
|
268
|
-
files: parseInt(match[1]),
|
|
269
|
-
insertions: parseInt(match[2] || 0),
|
|
270
|
-
deletions: parseInt(match[3] || 0)
|
|
271
|
-
}
|
|
289
|
+
files: Number.parseInt(match[1], 10),
|
|
290
|
+
insertions: Number.parseInt(match[2] || 0, 10),
|
|
291
|
+
deletions: Number.parseInt(match[3] || 0, 10),
|
|
292
|
+
}
|
|
272
293
|
}
|
|
273
294
|
}
|
|
274
295
|
|
|
275
|
-
return { files: 0, insertions: 0, deletions: 0 }
|
|
296
|
+
return { files: 0, insertions: 0, deletions: 0 }
|
|
276
297
|
} catch {
|
|
277
|
-
return { files: 0, insertions: 0, deletions: 0 }
|
|
298
|
+
return { files: 0, insertions: 0, deletions: 0 }
|
|
278
299
|
}
|
|
279
300
|
}
|
|
280
301
|
|
|
281
302
|
async getCommitsSince(since) {
|
|
282
303
|
try {
|
|
283
|
-
const command = since
|
|
284
|
-
? `git log --oneline --since="${since}"`
|
|
285
|
-
: 'git log --oneline -10';
|
|
304
|
+
const command = since ? `git log --oneline --since="${since}"` : 'git log --oneline -10'
|
|
286
305
|
|
|
287
|
-
const output = this.gitManager.execGitSafe(command)
|
|
288
|
-
return output
|
|
306
|
+
const output = this.gitManager.execGitSafe(command)
|
|
307
|
+
return output
|
|
308
|
+
.split('\n')
|
|
289
309
|
.filter(Boolean)
|
|
290
|
-
.map(line => {
|
|
291
|
-
const [hash, ...messageParts] = line.split(' ')
|
|
310
|
+
.map((line) => {
|
|
311
|
+
const [hash, ...messageParts] = line.split(' ')
|
|
292
312
|
return {
|
|
293
313
|
hash: hash.substring(0, 7),
|
|
294
|
-
message: messageParts.join(' ')
|
|
295
|
-
}
|
|
296
|
-
})
|
|
314
|
+
message: messageParts.join(' '),
|
|
315
|
+
}
|
|
316
|
+
})
|
|
297
317
|
} catch (error) {
|
|
298
|
-
console.error(colors.errorMessage('Error getting commits since:'), error.message)
|
|
299
|
-
return []
|
|
318
|
+
console.error(colors.errorMessage('Error getting commits since:'), error.message)
|
|
319
|
+
return []
|
|
300
320
|
}
|
|
301
321
|
}
|
|
302
|
-
}
|
|
322
|
+
}
|