@entro314labs/ai-changelog-generator 3.7.1 → 3.8.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/bin/ai-changelog.js +5 -1
- package/package.json +12 -9
- package/src/application/orchestrators/changelog.orchestrator.js +16 -10
- package/src/application/services/application.service.js +10 -2
- package/src/domains/analysis/analysis.engine.js +25 -22
- package/src/domains/changelog/changelog.service.js +66 -8
- package/src/domains/git/git-manager.js +15 -7
- package/src/domains/git/git.service.js +36 -13
- package/src/infrastructure/cli/cli.controller.js +61 -42
- package/src/infrastructure/config/configuration.manager.js +6 -4
- package/src/infrastructure/interactive/interactive-workflow.service.js +119 -12
- package/src/infrastructure/mcp/mcp-server.service.js +13 -10
- package/src/infrastructure/providers/core/base-provider.js +2 -1
- package/src/infrastructure/providers/utils/model-config.js +3 -1
- package/src/infrastructure/validation/commit-message-validation.service.js +1 -1
- package/src/shared/constants/colors.js +2 -1
- package/src/shared/utils/cli-entry-utils.js +3 -1
- package/src/shared/utils/cli-ui.js +17 -14
- package/src/shared/utils/diff-processor.js +3 -13
- package/src/shared/utils/json-utils.js +6 -2
- package/src/shared/utils/utils.js +16 -10
package/bin/ai-changelog.js
CHANGED
|
@@ -9,7 +9,11 @@ import { CLIController } from '../src/infrastructure/cli/cli.controller.js'
|
|
|
9
9
|
|
|
10
10
|
// Run the CLI with new refactored architecture using proper CLI controller
|
|
11
11
|
const cliController = new CLIController()
|
|
12
|
-
cliController.runCLI().
|
|
12
|
+
cliController.runCLI().then(() => {
|
|
13
|
+
if (process.exitCode !== undefined) {
|
|
14
|
+
process.exit(process.exitCode)
|
|
15
|
+
}
|
|
16
|
+
}).catch((error) => {
|
|
13
17
|
console.error('❌ CLI Error:', error.message)
|
|
14
18
|
if (process.env.DEBUG) {
|
|
15
19
|
console.error(error.stack)
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entro314labs/ai-changelog-generator",
|
|
3
3
|
"displayName": "AI Changelog Generator",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.8.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "AI-powered changelog generator with MCP server support - works with most providers, online and local models",
|
|
7
7
|
"main": "src/ai-changelog-generator.js",
|
|
8
8
|
"types": "types/index.d.ts",
|
|
9
|
+
"workspaces": [
|
|
10
|
+
"vscode-extension"
|
|
11
|
+
],
|
|
9
12
|
"bin": {
|
|
10
13
|
"ai-changelog": "./bin/ai-changelog.js",
|
|
11
14
|
"ai-changelog-mcp": "./bin/ai-changelog-mcp.js"
|
|
@@ -23,15 +26,15 @@
|
|
|
23
26
|
],
|
|
24
27
|
"dependencies": {
|
|
25
28
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
26
|
-
"@aws-sdk/client-bedrock-runtime": "^3.
|
|
29
|
+
"@aws-sdk/client-bedrock-runtime": "^3.966.0",
|
|
27
30
|
"@azure/identity": "^4.13.0",
|
|
28
31
|
"@clack/prompts": "^0.11.0",
|
|
29
|
-
"@google/genai": "^1.
|
|
32
|
+
"@google/genai": "^1.35.0",
|
|
30
33
|
"@google/generative-ai": "^0.24.1",
|
|
31
34
|
"@huggingface/hub": "^2.7.1",
|
|
32
35
|
"@huggingface/inference": "^4.13.5",
|
|
33
36
|
"@lmstudio/sdk": "^1.5.0",
|
|
34
|
-
"@modelcontextprotocol/sdk": "^1.25.
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
35
38
|
"@modelcontextprotocol/server-filesystem": "2025.12.18",
|
|
36
39
|
"boxen": "^8.0.1",
|
|
37
40
|
"chalk": "^5.6.2",
|
|
@@ -42,13 +45,13 @@
|
|
|
42
45
|
"gradient-string": "^3.0.0",
|
|
43
46
|
"js-yaml": "^4.1.1",
|
|
44
47
|
"ollama": "^0.6.3",
|
|
45
|
-
"openai": "^6.
|
|
48
|
+
"openai": "^6.16.0",
|
|
46
49
|
"ora": "^9.0.0",
|
|
47
50
|
"yargs": "^18.0.0"
|
|
48
51
|
},
|
|
49
52
|
"devDependencies": {
|
|
50
53
|
"@anthropic-ai/mcpb": "^2.1.2",
|
|
51
|
-
"@types/node": "25.0.
|
|
54
|
+
"@types/node": "25.0.5",
|
|
52
55
|
"@vitest/browser": "latest",
|
|
53
56
|
"@vitest/coverage-v8": "latest",
|
|
54
57
|
"@vitest/ui": "latest",
|
|
@@ -96,7 +99,7 @@
|
|
|
96
99
|
},
|
|
97
100
|
"engines": {
|
|
98
101
|
"node": ">=22.21.1",
|
|
99
|
-
"pnpm": ">=10.
|
|
102
|
+
"pnpm": ">=10.27.0"
|
|
100
103
|
},
|
|
101
104
|
"funding": {
|
|
102
105
|
"type": "github",
|
|
@@ -108,8 +111,8 @@
|
|
|
108
111
|
}
|
|
109
112
|
},
|
|
110
113
|
"volta": {
|
|
111
|
-
"node": "22.
|
|
112
|
-
"pnpm": "10.
|
|
114
|
+
"node": "22.21.1",
|
|
115
|
+
"pnpm": "10.27.0"
|
|
113
116
|
},
|
|
114
117
|
"scripts": {
|
|
115
118
|
"start": "node bin/ai-changelog.js",
|
|
@@ -61,7 +61,12 @@ export class ChangelogOrchestrator {
|
|
|
61
61
|
this.aiProvider = this.providerManager.getActiveProvider()
|
|
62
62
|
|
|
63
63
|
// Create proper implementations using the new classes
|
|
64
|
-
|
|
64
|
+
// Pass cwd/repositoryPath to GitManager so it runs commands in correct directory
|
|
65
|
+
const gitOptions = {}
|
|
66
|
+
if (this.options.cwd || this.options.repositoryPath) {
|
|
67
|
+
gitOptions.cwd = this.options.cwd || this.options.repositoryPath
|
|
68
|
+
}
|
|
69
|
+
this.gitManager = new GitManager(gitOptions)
|
|
65
70
|
this.tagger = new CommitTagger()
|
|
66
71
|
this.promptEngine = await this.createPromptEngine()
|
|
67
72
|
|
|
@@ -178,7 +183,11 @@ export class ChangelogOrchestrator {
|
|
|
178
183
|
if (toTag) {
|
|
179
184
|
effectiveUntil = toTag
|
|
180
185
|
}
|
|
181
|
-
console.log(
|
|
186
|
+
console.log(
|
|
187
|
+
colors.infoMessage(
|
|
188
|
+
`📦 Generating changelog between tags: ${fromTag} → ${toTag || 'HEAD'}`
|
|
189
|
+
)
|
|
190
|
+
)
|
|
182
191
|
}
|
|
183
192
|
|
|
184
193
|
// Display author filter if specified
|
|
@@ -194,7 +203,11 @@ export class ChangelogOrchestrator {
|
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
// Generate changelog using the service
|
|
197
|
-
const result = await this.changelogService.generateChangelog(
|
|
206
|
+
const result = await this.changelogService.generateChangelog(
|
|
207
|
+
version,
|
|
208
|
+
effectiveSince,
|
|
209
|
+
mergedOptions
|
|
210
|
+
)
|
|
198
211
|
|
|
199
212
|
if (!result) {
|
|
200
213
|
console.log(colors.warningMessage('No changelog generated'))
|
|
@@ -883,13 +896,6 @@ export class ChangelogOrchestrator {
|
|
|
883
896
|
filesStaged: true,
|
|
884
897
|
}
|
|
885
898
|
}
|
|
886
|
-
|
|
887
|
-
return {
|
|
888
|
-
success: true,
|
|
889
|
-
commitMessage,
|
|
890
|
-
stagedFiles: finalStatus.staged.length,
|
|
891
|
-
phase: 'staging-complete',
|
|
892
|
-
}
|
|
893
899
|
} catch (error) {
|
|
894
900
|
console.error(colors.errorMessage(`Commit workflow error: ${error.message}`))
|
|
895
901
|
throw error
|
|
@@ -7,8 +7,16 @@ import { ChangelogOrchestrator } from '../orchestrators/changelog.orchestrator.j
|
|
|
7
7
|
export class ApplicationService {
|
|
8
8
|
constructor(options = {}) {
|
|
9
9
|
this.options = options
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
// Normalize repositoryPath option (can be cwd or repositoryPath)
|
|
12
|
+
const repositoryPath = options.repositoryPath || options.cwd
|
|
13
|
+
if (repositoryPath) {
|
|
14
|
+
this.options.repositoryPath = repositoryPath
|
|
15
|
+
this.options.cwd = repositoryPath
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.configManager = new ConfigurationManager(options.configPath)
|
|
19
|
+
this.orchestrator = new ChangelogOrchestrator(this.configManager, this.options)
|
|
12
20
|
this.initialized = false
|
|
13
21
|
|
|
14
22
|
// Apply options
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getWorkingDirectoryChanges,
|
|
14
14
|
performSemanticAnalysis,
|
|
15
15
|
} from '../../shared/utils/utils.js'
|
|
16
|
+
import { EnhancedConsole } from '../../shared/utils/cli-ui.js'
|
|
16
17
|
|
|
17
18
|
export class AnalysisEngine {
|
|
18
19
|
constructor(gitService, aiAnalysisService, gitManager = null) {
|
|
@@ -51,16 +52,16 @@ export class AnalysisEngine {
|
|
|
51
52
|
let enhancedChanges = []
|
|
52
53
|
|
|
53
54
|
try {
|
|
54
|
-
|
|
55
|
+
// Get cwd from git manager if available
|
|
56
|
+
const cwd = this.gitService?.gitManager?.options?.cwd
|
|
57
|
+
const changes = getWorkingDirectoryChanges(cwd)
|
|
55
58
|
|
|
56
59
|
if (changes.length === 0) {
|
|
57
|
-
|
|
60
|
+
EnhancedConsole.info('No changes detected in working directory.')
|
|
58
61
|
return { changes: [], analysis: null }
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
colors.processingMessage(`Analyzing ${changes.length} working directory changes...`)
|
|
63
|
-
)
|
|
64
|
+
EnhancedConsole.processing(`Analyzing ${changes.length} working directory changes...`)
|
|
64
65
|
|
|
65
66
|
// Enhanced file analysis with actual diff content
|
|
66
67
|
enhancedChanges = []
|
|
@@ -102,9 +103,10 @@ export class AnalysisEngine {
|
|
|
102
103
|
semanticChanges: { changeType: 'unknown', patterns: [], frameworks: [] },
|
|
103
104
|
functionalImpact: { scope: 'local', severity: 'low' },
|
|
104
105
|
})
|
|
106
|
+
|
|
105
107
|
}
|
|
106
108
|
} catch (error) {
|
|
107
|
-
|
|
109
|
+
EnhancedConsole.error(`Error processing change ${i}:`, change, error.message)
|
|
108
110
|
enhancedChanges.push({
|
|
109
111
|
...change,
|
|
110
112
|
category: 'other',
|
|
@@ -130,7 +132,7 @@ export class AnalysisEngine {
|
|
|
130
132
|
'working-directory'
|
|
131
133
|
)
|
|
132
134
|
} catch (error) {
|
|
133
|
-
|
|
135
|
+
EnhancedConsole.error('Error in AI analysis:', error.message)
|
|
134
136
|
aiAnalysis = null
|
|
135
137
|
}
|
|
136
138
|
}
|
|
@@ -140,7 +142,7 @@ export class AnalysisEngine {
|
|
|
140
142
|
try {
|
|
141
143
|
ruleBasedAnalysis = this.analyzeChangesRuleBased(enhancedChanges)
|
|
142
144
|
} catch (error) {
|
|
143
|
-
|
|
145
|
+
EnhancedConsole.error('Error in rule-based analysis:', error.message)
|
|
144
146
|
ruleBasedAnalysis = {
|
|
145
147
|
summary: 'Analysis failed',
|
|
146
148
|
category: 'other',
|
|
@@ -154,7 +156,7 @@ export class AnalysisEngine {
|
|
|
154
156
|
try {
|
|
155
157
|
finalAnalysis = this.combineAnalyses(aiAnalysis, ruleBasedAnalysis)
|
|
156
158
|
} catch (error) {
|
|
157
|
-
|
|
159
|
+
EnhancedConsole.error('Error combining analyses:', error.message)
|
|
158
160
|
finalAnalysis = {
|
|
159
161
|
summary: `${enhancedChanges.length} working directory changes detected`,
|
|
160
162
|
category: 'working-directory',
|
|
@@ -169,7 +171,7 @@ export class AnalysisEngine {
|
|
|
169
171
|
summary: generateAnalysisSummary(enhancedChanges, finalAnalysis),
|
|
170
172
|
}
|
|
171
173
|
} catch (error) {
|
|
172
|
-
|
|
174
|
+
EnhancedConsole.error('Error analyzing current changes:', error.message)
|
|
173
175
|
// Return enhanced changes if they were created before the error
|
|
174
176
|
return {
|
|
175
177
|
changes: enhancedChanges,
|
|
@@ -189,13 +191,13 @@ export class AnalysisEngine {
|
|
|
189
191
|
|
|
190
192
|
async analyzeRecentCommits(limit = 10, _config = {}) {
|
|
191
193
|
try {
|
|
192
|
-
|
|
194
|
+
EnhancedConsole.processing(`Analyzing recent ${limit} commits...`)
|
|
193
195
|
|
|
194
196
|
const commits = await this.gitService.getCommitsSince(null)
|
|
195
197
|
const recentCommits = commits.slice(0, limit)
|
|
196
198
|
|
|
197
199
|
if (recentCommits.length === 0) {
|
|
198
|
-
|
|
200
|
+
EnhancedConsole.info('No recent commits found.')
|
|
199
201
|
return { commits: [], analysis: null }
|
|
200
202
|
}
|
|
201
203
|
|
|
@@ -219,7 +221,7 @@ export class AnalysisEngine {
|
|
|
219
221
|
summary: this.generateCommitsSummary(analyzedCommits, overallAnalysis),
|
|
220
222
|
}
|
|
221
223
|
} catch (error) {
|
|
222
|
-
|
|
224
|
+
EnhancedConsole.error('Error analyzing recent commits:', error.message)
|
|
223
225
|
return { commits: [], analysis: null, error: error.message }
|
|
224
226
|
}
|
|
225
227
|
}
|
|
@@ -259,11 +261,9 @@ export class AnalysisEngine {
|
|
|
259
261
|
},
|
|
260
262
|
}
|
|
261
263
|
} catch (error) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
error.message
|
|
266
|
-
)
|
|
264
|
+
EnhancedConsole.warn(
|
|
265
|
+
`Failed to enhance analysis for commit ${commitAnalysis.hash}:`,
|
|
266
|
+
error.message
|
|
267
267
|
)
|
|
268
268
|
return commitAnalysis
|
|
269
269
|
}
|
|
@@ -552,8 +552,9 @@ export class AnalysisEngine {
|
|
|
552
552
|
totalCommits: commits.length,
|
|
553
553
|
suggestions,
|
|
554
554
|
}
|
|
555
|
+
|
|
555
556
|
} catch (error) {
|
|
556
|
-
|
|
557
|
+
EnhancedConsole.warn(`Commit pattern analysis failed: ${error.message}`)
|
|
557
558
|
return {
|
|
558
559
|
patterns: [],
|
|
559
560
|
compliance: 0,
|
|
@@ -621,8 +622,9 @@ export class AnalysisEngine {
|
|
|
621
622
|
totalChanges: changes.length,
|
|
622
623
|
details: `Detected ${detectedTypes.size} change types from ${changes.length} changes`,
|
|
623
624
|
}
|
|
625
|
+
|
|
624
626
|
} catch (error) {
|
|
625
|
-
|
|
627
|
+
EnhancedConsole.warn(`Change type detection failed: ${error.message}`)
|
|
626
628
|
return {
|
|
627
629
|
types: [],
|
|
628
630
|
confidence: 'low',
|
|
@@ -706,8 +708,9 @@ export class AnalysisEngine {
|
|
|
706
708
|
recommendations,
|
|
707
709
|
filesAnalyzed: files.length,
|
|
708
710
|
}
|
|
711
|
+
|
|
709
712
|
} catch (error) {
|
|
710
|
-
|
|
713
|
+
EnhancedConsole.warn(`Code quality assessment failed: ${error.message}`)
|
|
711
714
|
return {
|
|
712
715
|
score: 0,
|
|
713
716
|
issues: [`Assessment failed: ${error.message}`],
|
|
@@ -775,7 +778,7 @@ export class AnalysisEngine {
|
|
|
775
778
|
|
|
776
779
|
return dependencies
|
|
777
780
|
} catch (error) {
|
|
778
|
-
|
|
781
|
+
EnhancedConsole.warn(`Dependency analysis failed: ${error.message}`)
|
|
779
782
|
return {
|
|
780
783
|
added: [],
|
|
781
784
|
removed: [],
|
|
@@ -1805,11 +1805,12 @@ export class ChangelogService {
|
|
|
1805
1805
|
]
|
|
1806
1806
|
|
|
1807
1807
|
patterns.forEach((pattern) => {
|
|
1808
|
-
let match
|
|
1809
|
-
while (
|
|
1808
|
+
let match = pattern.exec(content)
|
|
1809
|
+
while (match !== null) {
|
|
1810
1810
|
if (match[1] && !functions.includes(match[1])) {
|
|
1811
1811
|
functions.push(match[1])
|
|
1812
1812
|
}
|
|
1813
|
+
match = pattern.exec(content)
|
|
1813
1814
|
}
|
|
1814
1815
|
})
|
|
1815
1816
|
|
|
@@ -2186,7 +2187,7 @@ export class ChangelogService {
|
|
|
2186
2187
|
|
|
2187
2188
|
// Add context information for detailed modes
|
|
2188
2189
|
if (options.analysisMode === 'detailed' || options.analysisMode === 'enterprise') {
|
|
2189
|
-
changelog += this.generateContextSection(result.
|
|
2190
|
+
changelog += this.generateContextSection(result.summary)
|
|
2190
2191
|
}
|
|
2191
2192
|
|
|
2192
2193
|
return {
|
|
@@ -2209,6 +2210,8 @@ export class ChangelogService {
|
|
|
2209
2210
|
// Enhanced analysis of changes with diff content for AI analysis
|
|
2210
2211
|
const enhancedChanges = await this.enhanceChangesWithDiff(rawChanges)
|
|
2211
2212
|
const changesSummary = summarizeFileChanges(enhancedChanges)
|
|
2213
|
+
|
|
2214
|
+
console.log('DEBUG generateComprehensiveWorkspaceChangelog changesSummary:', JSON.stringify(changesSummary))
|
|
2212
2215
|
|
|
2213
2216
|
// Use DiffProcessor for intelligent processing
|
|
2214
2217
|
const analysisMode = options.analysisMode || 'standard'
|
|
@@ -2244,6 +2247,9 @@ export class ChangelogService {
|
|
|
2244
2247
|
}
|
|
2245
2248
|
|
|
2246
2249
|
async generateCommitStyleWorkingDirectoryEntries(options = {}) {
|
|
2250
|
+
console.log('DEBUG: generateCommitStyleWorkingDirectoryEntries called')
|
|
2251
|
+
console.log('DEBUG: summarizeFileChanges type:', typeof summarizeFileChanges)
|
|
2252
|
+
|
|
2247
2253
|
// Use provided working directory analysis or get current changes
|
|
2248
2254
|
let rawChanges
|
|
2249
2255
|
if (options.workingDirAnalysis?.changes) {
|
|
@@ -2260,6 +2266,9 @@ export class ChangelogService {
|
|
|
2260
2266
|
// Enhanced analysis of changes with diff content for AI analysis
|
|
2261
2267
|
const enhancedChanges = await this.enhanceChangesWithDiff(rawChanges)
|
|
2262
2268
|
const changesSummary = summarizeFileChanges(enhancedChanges)
|
|
2269
|
+
|
|
2270
|
+
console.log('DEBUG: enhancedChanges length:', enhancedChanges.length)
|
|
2271
|
+
console.log('DEBUG: changesSummary:', changesSummary)
|
|
2263
2272
|
|
|
2264
2273
|
// Use DiffProcessor for intelligent diff processing
|
|
2265
2274
|
const analysisMode =
|
|
@@ -2272,6 +2281,10 @@ export class ChangelogService {
|
|
|
2272
2281
|
|
|
2273
2282
|
const processedResult = diffProcessor.processFiles(enhancedChanges)
|
|
2274
2283
|
const { processedFiles, patterns } = processedResult
|
|
2284
|
+
|
|
2285
|
+
console.log('DEBUG: changesSummary type:', typeof changesSummary)
|
|
2286
|
+
console.log('DEBUG: changesSummary:', JSON.stringify(changesSummary))
|
|
2287
|
+
console.log('DEBUG: processedResult:', JSON.stringify(processedResult))
|
|
2275
2288
|
|
|
2276
2289
|
// Build pattern summary if patterns were detected
|
|
2277
2290
|
const patternSummary =
|
|
@@ -2507,6 +2520,8 @@ Generate one entry per file or logical change group. Only describe what you can
|
|
|
2507
2520
|
}
|
|
2508
2521
|
|
|
2509
2522
|
async generateAIChangelogContentFromChanges(changes, changesSummary, analysisMode = 'standard') {
|
|
2523
|
+
console.log('DEBUG generateAIChangelogContentFromChanges changesSummary:', JSON.stringify(changesSummary))
|
|
2524
|
+
|
|
2510
2525
|
if (!this.aiAnalysisService.hasAI) {
|
|
2511
2526
|
console.log(colors.infoMessage('AI not available, using rule-based analysis...'))
|
|
2512
2527
|
return this.generateBasicChangelogContentFromChanges(changes, changesSummary)
|
|
@@ -2548,6 +2563,7 @@ Generate one entry per file or logical change group. Only describe what you can
|
|
|
2548
2563
|
.join('\n')
|
|
2549
2564
|
|
|
2550
2565
|
// Build comprehensive prompt with processed changes
|
|
2566
|
+
console.log('DEBUG before prompt, changesSummary:', changesSummary ? 'defined' : 'undefined', 'totalFiles:', changesSummary?.totalFiles)
|
|
2551
2567
|
const prompt = `Generate a comprehensive AI changelog for the following working directory changes:
|
|
2552
2568
|
|
|
2553
2569
|
**Analysis Mode**: ${analysisMode}
|
|
@@ -2727,13 +2743,55 @@ ONLY describe what you can literally see in the diff content. Do not invent conn
|
|
|
2727
2743
|
}
|
|
2728
2744
|
|
|
2729
2745
|
generateContextSection(context) {
|
|
2746
|
+
if (!context) {
|
|
2747
|
+
return ''
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2730
2750
|
let section = '## Context Analysis\n\n'
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2751
|
+
|
|
2752
|
+
// Handle totalFiles from either structure
|
|
2753
|
+
if (context.totalFiles !== undefined) {
|
|
2754
|
+
section += `- **Total Files:** ${context.totalFiles}\n`
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
// Handle categories from summarizeFileChanges structure
|
|
2758
|
+
if (context.categories && typeof context.categories === 'object') {
|
|
2759
|
+
const categoryNames = Object.keys(context.categories)
|
|
2760
|
+
if (categoryNames.length > 0) {
|
|
2761
|
+
section += `- **Affected Areas:** ${categoryNames.join(', ')}\n`
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
// Handle stats if available
|
|
2766
|
+
if (context.stats) {
|
|
2767
|
+
const statParts = []
|
|
2768
|
+
if (context.stats.added > 0) statParts.push(`${context.stats.added} added`)
|
|
2769
|
+
if (context.stats.modified > 0) statParts.push(`${context.stats.modified} modified`)
|
|
2770
|
+
if (context.stats.deleted > 0) statParts.push(`${context.stats.deleted} deleted`)
|
|
2771
|
+
if (statParts.length > 0) {
|
|
2772
|
+
section += `- **Changes:** ${statParts.join(', ')}\n`
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
// Handle languages if available
|
|
2777
|
+
if (context.languages && Array.isArray(context.languages) && context.languages.length > 0) {
|
|
2778
|
+
section += `- **Languages:** ${context.languages.join(', ')}\n`
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
// Legacy properties for backward compatibility
|
|
2782
|
+
if (context.primaryCategory) {
|
|
2783
|
+
section += `- **Primary Category:** ${context.primaryCategory}\n`
|
|
2784
|
+
}
|
|
2785
|
+
if (context.riskLevel) {
|
|
2786
|
+
section += `- **Risk Level:** ${context.riskLevel}\n`
|
|
2787
|
+
}
|
|
2788
|
+
if (context.complexity) {
|
|
2789
|
+
section += `- **Complexity:** ${context.complexity}\n`
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
section += '\n'
|
|
2735
2793
|
|
|
2736
|
-
if (context.recommendations.length > 0) {
|
|
2794
|
+
if (context.recommendations && Array.isArray(context.recommendations) && context.recommendations.length > 0) {
|
|
2737
2795
|
section += '### Recommendations\n\n'
|
|
2738
2796
|
context.recommendations.forEach((rec) => {
|
|
2739
2797
|
section += `- ${rec}\n`
|
|
@@ -14,6 +14,7 @@ export class GitManager {
|
|
|
14
14
|
encoding: 'utf8',
|
|
15
15
|
timeout: 30000,
|
|
16
16
|
stdio: 'pipe',
|
|
17
|
+
cwd: undefined, // Will use process.cwd() if not specified
|
|
17
18
|
...options,
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -56,11 +57,18 @@ export class GitManager {
|
|
|
56
57
|
*/
|
|
57
58
|
execGit(command) {
|
|
58
59
|
try {
|
|
59
|
-
|
|
60
|
+
const execOptions = {
|
|
60
61
|
encoding: this.options.encoding,
|
|
61
62
|
stdio: this.options.stdio,
|
|
62
63
|
timeout: this.options.timeout,
|
|
63
|
-
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Add cwd if specified
|
|
67
|
+
if (this.options.cwd) {
|
|
68
|
+
execOptions.cwd = this.options.cwd
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return execSync(command, execOptions)
|
|
64
72
|
} catch (error) {
|
|
65
73
|
throw this._createGitError(command, error)
|
|
66
74
|
}
|
|
@@ -239,9 +247,7 @@ export class GitManager {
|
|
|
239
247
|
*/
|
|
240
248
|
getCommitsByAuthor(author, limit = 50) {
|
|
241
249
|
try {
|
|
242
|
-
const output = this.execGitSafe(
|
|
243
|
-
`git log --author="${author}" --oneline -n ${limit}`
|
|
244
|
-
)
|
|
250
|
+
const output = this.execGitSafe(`git log --author="${author}" --oneline -n ${limit}`)
|
|
245
251
|
return output
|
|
246
252
|
.split('\n')
|
|
247
253
|
.filter((line) => line.trim())
|
|
@@ -341,7 +347,9 @@ export class GitManager {
|
|
|
341
347
|
.filter(Boolean)
|
|
342
348
|
|
|
343
349
|
// Get summary stats
|
|
344
|
-
const summaryMatch = statOutput.match(
|
|
350
|
+
const summaryMatch = statOutput.match(
|
|
351
|
+
/(\d+) files? changed(?:, (\d+) insertions?)?(?:, (\d+) deletions?)?/
|
|
352
|
+
)
|
|
345
353
|
|
|
346
354
|
return {
|
|
347
355
|
ref: stashRef,
|
|
@@ -351,7 +359,7 @@ export class GitManager {
|
|
|
351
359
|
stats: {
|
|
352
360
|
filesChanged: summaryMatch ? parseInt(summaryMatch[1], 10) : files.length,
|
|
353
361
|
insertions: summaryMatch && summaryMatch[2] ? parseInt(summaryMatch[2], 10) : 0,
|
|
354
|
-
deletions: summaryMatch
|
|
362
|
+
deletions: summaryMatch?.[3] ? parseInt(summaryMatch[3], 10) : 0,
|
|
355
363
|
},
|
|
356
364
|
}
|
|
357
365
|
} catch {
|
|
@@ -41,7 +41,8 @@ export class GitService {
|
|
|
41
41
|
.trim()
|
|
42
42
|
.split(' ')
|
|
43
43
|
isMergeCommit = parents.length > 1
|
|
44
|
-
} catch {
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.warn(colors.warningMessage(`Failed to check parent commits: ${error.message}`))
|
|
45
46
|
isMergeCommit = false
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -203,7 +204,8 @@ export class GitService {
|
|
|
203
204
|
} else {
|
|
204
205
|
diff = 'New empty file created'
|
|
205
206
|
}
|
|
206
|
-
} catch {
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.warn(colors.warningMessage(`Failed to read new file content: ${error.message}`))
|
|
207
209
|
diff = 'New file created (binary or inaccessible)'
|
|
208
210
|
}
|
|
209
211
|
} else if (status === 'D') {
|
|
@@ -217,7 +219,10 @@ export class GitService {
|
|
|
217
219
|
} else {
|
|
218
220
|
diff = 'File deleted from working directory (content was empty)'
|
|
219
221
|
}
|
|
220
|
-
} catch {
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.warn(
|
|
224
|
+
colors.warningMessage(`Failed to read deleted file content: ${error.message}`)
|
|
225
|
+
)
|
|
221
226
|
diff = 'File deleted from working directory (content unavailable)'
|
|
222
227
|
}
|
|
223
228
|
} else if (status === 'M' || status.includes('M')) {
|
|
@@ -230,7 +235,8 @@ export class GitService {
|
|
|
230
235
|
const stagedDiff = this.gitManager.execGitSafe(`git diff --cached -- "${filePath}"`)
|
|
231
236
|
diff = stagedDiff || 'No diff available (binary or identical)'
|
|
232
237
|
}
|
|
233
|
-
} catch {
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.warn(colors.warningMessage(`Failed to generate diff: ${error.message}`))
|
|
234
240
|
diff = 'Modified file (diff unavailable)'
|
|
235
241
|
}
|
|
236
242
|
} else if (status === 'R') {
|
|
@@ -250,7 +256,8 @@ export class GitService {
|
|
|
250
256
|
try {
|
|
251
257
|
const headResult = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
252
258
|
beforeContent = headResult ? headResult.slice(0, 1000) : ''
|
|
253
|
-
} catch {
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.warn(colors.warningMessage(`Failed to get before content: ${error.message}`))
|
|
254
261
|
beforeContent = ''
|
|
255
262
|
}
|
|
256
263
|
// afterContent stays empty for deleted files
|
|
@@ -260,7 +267,10 @@ export class GitService {
|
|
|
260
267
|
// Get HEAD version
|
|
261
268
|
const headResult = this.gitManager.execGitSafe(`git show HEAD:"${filePath}"`)
|
|
262
269
|
beforeContent = headResult ? headResult.slice(0, 1000) : ''
|
|
263
|
-
} catch {
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.warn(
|
|
272
|
+
colors.warningMessage(`Failed to get before content (modified): ${error.message}`)
|
|
273
|
+
)
|
|
264
274
|
beforeContent = ''
|
|
265
275
|
}
|
|
266
276
|
|
|
@@ -268,7 +278,8 @@ export class GitService {
|
|
|
268
278
|
// Get current working directory version
|
|
269
279
|
const currentResult = this.gitManager.execGitSafe(`cat "${filePath}"`)
|
|
270
280
|
afterContent = currentResult ? currentResult.slice(0, 1000) : ''
|
|
271
|
-
} catch {
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.warn(colors.warningMessage(`Failed to get after content: ${error.message}`))
|
|
272
283
|
afterContent = ''
|
|
273
284
|
}
|
|
274
285
|
}
|
|
@@ -301,7 +312,11 @@ export class GitService {
|
|
|
301
312
|
language: 'unknown',
|
|
302
313
|
importance: 'medium',
|
|
303
314
|
complexity: { score: 1 },
|
|
304
|
-
semanticChanges: {
|
|
315
|
+
semanticChanges: {
|
|
316
|
+
changeType: 'unknown',
|
|
317
|
+
patterns: [],
|
|
318
|
+
frameworks: [],
|
|
319
|
+
},
|
|
305
320
|
functionalImpact: { scope: 'local', severity: 'low' },
|
|
306
321
|
}
|
|
307
322
|
}
|
|
@@ -328,7 +343,8 @@ export class GitService {
|
|
|
328
343
|
}
|
|
329
344
|
|
|
330
345
|
return { files: 0, insertions: 0, deletions: 0 }
|
|
331
|
-
} catch {
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.warn(colors.warningMessage(`Failed to get diff stats: ${error.message}`))
|
|
332
348
|
return { files: 0, insertions: 0, deletions: 0 }
|
|
333
349
|
}
|
|
334
350
|
}
|
|
@@ -420,7 +436,9 @@ export class GitService {
|
|
|
420
436
|
category: categorizeFile(filePath),
|
|
421
437
|
language: detectLanguage(filePath),
|
|
422
438
|
importance: assessFileImportance(filePath, status),
|
|
423
|
-
complexity: {
|
|
439
|
+
complexity: {
|
|
440
|
+
level: changes > 100 ? 'high' : changes > 20 ? 'medium' : 'low',
|
|
441
|
+
},
|
|
424
442
|
semanticChanges: { patterns: ['merge-commit'] },
|
|
425
443
|
functionalImpact: { level: changes > 50 ? 'high' : 'medium' },
|
|
426
444
|
isMergeCommit: true,
|
|
@@ -662,7 +680,9 @@ export class GitService {
|
|
|
662
680
|
details.push(techDetail)
|
|
663
681
|
}
|
|
664
682
|
}
|
|
665
|
-
} catch (
|
|
683
|
+
} catch (error) {
|
|
684
|
+
console.warn(colors.warningMessage(`Failed to extract technical details: ${error.message}`))
|
|
685
|
+
}
|
|
666
686
|
}
|
|
667
687
|
|
|
668
688
|
return details
|
|
@@ -830,7 +850,10 @@ export class GitService {
|
|
|
830
850
|
.filter((line) => line.trim())
|
|
831
851
|
.map((line) => {
|
|
832
852
|
const parts = line.split(' ')
|
|
833
|
-
return {
|
|
853
|
+
return {
|
|
854
|
+
size: parseInt(parts[1], 10),
|
|
855
|
+
path: parts.slice(2).join(' '),
|
|
856
|
+
}
|
|
834
857
|
})
|
|
835
858
|
.filter((file) => file.size > 10 * 1024 * 1024) // 10MB threshold
|
|
836
859
|
|
|
@@ -1101,7 +1124,7 @@ ${
|
|
|
1101
1124
|
)
|
|
1102
1125
|
if (firstCommitOutput.trim()) {
|
|
1103
1126
|
const firstCommitDate = new Date(firstCommitOutput.trim())
|
|
1104
|
-
const ageInDays = Math.floor((
|
|
1127
|
+
const ageInDays = Math.floor((Date.now() - firstCommitDate) / (1000 * 60 * 60 * 24))
|
|
1105
1128
|
analysis.metrics.repositoryAgeInDays = ageInDays
|
|
1106
1129
|
}
|
|
1107
1130
|
} catch (error) {
|