@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.
@@ -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().catch((error) => {
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.7.1",
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.958.0",
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.34.0",
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.1",
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.15.0",
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.3",
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.23.0"
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.22.1",
112
- "pnpm": "10.23.0"
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
- this.gitManager = new GitManager()
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(colors.infoMessage(`📦 Generating changelog between tags: ${fromTag} → ${toTag || 'HEAD'}`))
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(version, effectiveSince, mergedOptions)
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
- this.configManager = new ConfigurationManager()
11
- this.orchestrator = new ChangelogOrchestrator(this.configManager, options)
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
- const changes = getWorkingDirectoryChanges()
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
- console.log(colors.infoMessage('No changes detected in working directory.'))
60
+ EnhancedConsole.info('No changes detected in working directory.')
58
61
  return { changes: [], analysis: null }
59
62
  }
60
63
 
61
- console.log(
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
- console.error(`Error processing change ${i}:`, change, error.message)
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
- console.error('Error in AI analysis:', error.message)
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
- console.error('Error in rule-based analysis:', error.message)
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
- console.error('Error combining analyses:', error.message)
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
- console.error(colors.errorMessage('Error analyzing current changes:'), error.message)
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
- console.log(colors.processingMessage(`Analyzing recent ${limit} commits...`))
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
- console.log(colors.infoMessage('No recent commits found.'))
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
- console.error(colors.errorMessage('Error analyzing recent commits:'), error.message)
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
- console.warn(
263
- colors.warningMessage(
264
- `Failed to enhance analysis for commit ${commitAnalysis.hash}:`,
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
- console.warn(`Commit pattern analysis failed: ${error.message}`)
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
- console.warn(`Change type detection failed: ${error.message}`)
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
- console.warn(`Code quality assessment failed: ${error.message}`)
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
- console.warn(`Dependency analysis failed: ${error.message}`)
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 ((match = pattern.exec(content)) !== null) {
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.context)
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
- section += `- **Total Files:** ${context.totalFiles}\n`
2732
- section += `- **Primary Category:** ${context.primaryCategory}\n`
2733
- section += `- **Risk Level:** ${context.riskLevel}\n`
2734
- section += `- **Complexity:** ${context.complexity}\n\n`
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
- return execSync(command, {
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(/(\d+) files? changed(?:, (\d+) insertions?)?(?:, (\d+) deletions?)?/)
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 && summaryMatch[3] ? parseInt(summaryMatch[3], 10) : 0,
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: { changeType: 'unknown', patterns: [], frameworks: [] },
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: { level: changes > 100 ? 'high' : changes > 20 ? 'medium' : 'low' },
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 (_error) {}
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 { size: parseInt(parts[1], 10), path: parts.slice(2).join(' ') }
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((new Date() - firstCommitDate) / (1000 * 60 * 60 * 24))
1127
+ const ageInDays = Math.floor((Date.now() - firstCommitDate) / (1000 * 60 * 60 * 24))
1105
1128
  analysis.metrics.repositoryAgeInDays = ageInDays
1106
1129
  }
1107
1130
  } catch (error) {