@entro314labs/ai-changelog-generator 3.7.0 ā 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 +13 -10
- 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
|
@@ -306,7 +306,10 @@ export class CLIController {
|
|
|
306
306
|
.option('output', { alias: 'o', type: 'string', description: 'Output file path' })
|
|
307
307
|
.option('since', { type: 'string', description: 'Analyze changes since this git ref' })
|
|
308
308
|
.option('author', { alias: 'a', type: 'string', description: 'Filter commits by author' })
|
|
309
|
-
.option('tag-range', {
|
|
309
|
+
.option('tag-range', {
|
|
310
|
+
type: 'string',
|
|
311
|
+
description: 'Generate changelog between tags (v1.0..v2.0)',
|
|
312
|
+
})
|
|
310
313
|
.option('silent', { type: 'boolean', description: 'Suppress non-essential output' })
|
|
311
314
|
.option('dry-run', { type: 'boolean', description: 'Preview without writing files' })
|
|
312
315
|
.option('detailed', { type: 'boolean', description: 'Use detailed analysis mode' })
|
|
@@ -544,35 +547,33 @@ class WorkingDirCommand extends BaseCommand {
|
|
|
544
547
|
class FromCommitsCommand extends BaseCommand {
|
|
545
548
|
async execute(argv, appService) {
|
|
546
549
|
const _config = this.processStandardFlags(argv, appService)
|
|
547
|
-
|
|
550
|
+
EnhancedConsole.processing(`Generating changelog from commits: ${argv.commits.join(', ')}`)
|
|
548
551
|
|
|
549
552
|
try {
|
|
550
553
|
const result = await appService.generateChangelogFromCommits(argv.commits)
|
|
551
554
|
|
|
552
555
|
if (result?.changelog) {
|
|
553
|
-
|
|
554
|
-
|
|
556
|
+
EnhancedConsole.success('Changelog generated successfully!')
|
|
557
|
+
EnhancedConsole.divider()
|
|
555
558
|
console.log(result.changelog)
|
|
556
559
|
} else {
|
|
557
|
-
|
|
560
|
+
EnhancedConsole.warn('No changelog could be generated from the specified commits.')
|
|
558
561
|
}
|
|
559
562
|
} catch (error) {
|
|
560
|
-
|
|
563
|
+
EnhancedConsole.error(`Error generating changelog: ${error.message}`)
|
|
561
564
|
}
|
|
562
565
|
}
|
|
563
566
|
}
|
|
564
567
|
|
|
565
568
|
class CommitMessageCommand extends BaseCommand {
|
|
566
569
|
async execute(_argv, appService) {
|
|
567
|
-
|
|
568
|
-
colors.processingMessage('š¤ Analyzing current changes for commit message suggestions...')
|
|
569
|
-
)
|
|
570
|
+
EnhancedConsole.processing('Analyzing current changes for commit message suggestions...')
|
|
570
571
|
|
|
571
572
|
try {
|
|
572
573
|
const result = await appService.generateCommitMessage()
|
|
573
574
|
|
|
574
575
|
if (result?.suggestions && result.suggestions.length > 0) {
|
|
575
|
-
|
|
576
|
+
EnhancedConsole.success('Generated commit message suggestions:')
|
|
576
577
|
result.suggestions.forEach((suggestion, index) => {
|
|
577
578
|
console.log(`${colors.number(index + 1)}. ${colors.highlight(suggestion)}`)
|
|
578
579
|
})
|
|
@@ -581,18 +582,18 @@ class CommitMessageCommand extends BaseCommand {
|
|
|
581
582
|
console.log(colors.dim(`\nContext: ${result.context}`))
|
|
582
583
|
}
|
|
583
584
|
} else {
|
|
584
|
-
|
|
585
|
-
|
|
585
|
+
EnhancedConsole.warn('No commit message suggestions could be generated.')
|
|
586
|
+
EnhancedConsole.info('Make sure you have uncommitted changes.')
|
|
586
587
|
}
|
|
587
588
|
} catch (error) {
|
|
588
|
-
|
|
589
|
+
EnhancedConsole.error(`Error generating commit message: ${error.message}`)
|
|
589
590
|
}
|
|
590
591
|
}
|
|
591
592
|
}
|
|
592
593
|
|
|
593
594
|
class CommitCommand extends BaseCommand {
|
|
594
595
|
async execute(argv, appService) {
|
|
595
|
-
|
|
596
|
+
EnhancedConsole.processing('Starting interactive commit workflow...')
|
|
596
597
|
|
|
597
598
|
try {
|
|
598
599
|
// Process flags and model override
|
|
@@ -611,29 +612,25 @@ class CommitCommand extends BaseCommand {
|
|
|
611
612
|
|
|
612
613
|
if (result?.success) {
|
|
613
614
|
if (argv.dryRun) {
|
|
614
|
-
|
|
615
|
+
EnhancedConsole.success('Commit workflow completed (dry-run mode)')
|
|
615
616
|
console.log(colors.highlight(`Proposed commit message:\n${result.commitMessage}`))
|
|
616
617
|
} else {
|
|
617
|
-
|
|
618
|
+
EnhancedConsole.success('Changes committed successfully!')
|
|
618
619
|
console.log(colors.highlight(`Commit: ${result.commitHash}`))
|
|
619
620
|
console.log(colors.dim(`Message: ${result.commitMessage}`))
|
|
620
621
|
}
|
|
621
622
|
} else {
|
|
622
|
-
|
|
623
|
+
EnhancedConsole.warn('Commit workflow cancelled or no changes to commit.')
|
|
623
624
|
}
|
|
624
625
|
} catch (error) {
|
|
625
|
-
|
|
626
|
+
EnhancedConsole.error(`Commit workflow failed: ${error.message}`)
|
|
626
627
|
|
|
627
628
|
// Provide helpful suggestions based on error type
|
|
628
629
|
if (error.message.includes('No changes')) {
|
|
629
|
-
|
|
630
|
-
colors.infoMessage('š” Try making some changes first, then run the commit command.')
|
|
631
|
-
)
|
|
630
|
+
EnhancedConsole.info('Try making some changes first, then run the commit command.')
|
|
632
631
|
} else if (error.message.includes('git')) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
'š” Make sure you are in a git repository and git is properly configured.'
|
|
636
|
-
)
|
|
632
|
+
EnhancedConsole.info(
|
|
633
|
+
'Make sure you have uncommitted changes.'
|
|
637
634
|
)
|
|
638
635
|
}
|
|
639
636
|
}
|
|
@@ -664,8 +661,8 @@ class ProvidersCommand extends BaseCommand {
|
|
|
664
661
|
await this.listModels(appService, argv.provider)
|
|
665
662
|
break
|
|
666
663
|
default:
|
|
667
|
-
|
|
668
|
-
|
|
664
|
+
EnhancedConsole.error('Unknown provider subcommand')
|
|
665
|
+
EnhancedConsole.info('Available subcommands: list, switch, configure, validate, status, models')
|
|
669
666
|
}
|
|
670
667
|
}
|
|
671
668
|
|
|
@@ -702,8 +699,8 @@ class ProvidersCommand extends BaseCommand {
|
|
|
702
699
|
|
|
703
700
|
async switchProvider(appService, providerName) {
|
|
704
701
|
if (!providerName) {
|
|
705
|
-
|
|
706
|
-
|
|
702
|
+
EnhancedConsole.error('Please specify a provider name')
|
|
703
|
+
EnhancedConsole.info('Usage: ai-changelog providers switch <provider>')
|
|
707
704
|
return
|
|
708
705
|
}
|
|
709
706
|
|
|
@@ -711,12 +708,10 @@ class ProvidersCommand extends BaseCommand {
|
|
|
711
708
|
const result = await appService.switchProvider(providerName)
|
|
712
709
|
|
|
713
710
|
if (result.success) {
|
|
714
|
-
|
|
711
|
+
EnhancedConsole.success(`Switched to ${providerName} provider`)
|
|
715
712
|
} else {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
colors.infoMessage('Use "ai-changelog providers list" to see available providers')
|
|
719
|
-
)
|
|
713
|
+
EnhancedConsole.error(`Failed to switch provider: ${result.error}`)
|
|
714
|
+
EnhancedConsole.info('Use "ai-changelog providers list" to see available providers')
|
|
720
715
|
}
|
|
721
716
|
} catch (error) {
|
|
722
717
|
EnhancedConsole.error(`Error switching provider: ${error.message}`)
|
|
@@ -743,7 +738,7 @@ class ProvidersCommand extends BaseCommand {
|
|
|
743
738
|
}
|
|
744
739
|
|
|
745
740
|
console.log(colors.header(`\nš§ Configuring ${providerName.toUpperCase()} Provider`))
|
|
746
|
-
|
|
741
|
+
EnhancedConsole.info('Please add the following to your .env.local file:\n')
|
|
747
742
|
|
|
748
743
|
switch (providerName.toLowerCase()) {
|
|
749
744
|
case 'openai':
|
|
@@ -776,7 +771,7 @@ class ProvidersCommand extends BaseCommand {
|
|
|
776
771
|
console.log(colors.code(`${providerName.toUpperCase()}_API_KEY=your_api_key_here`))
|
|
777
772
|
}
|
|
778
773
|
|
|
779
|
-
|
|
774
|
+
EnhancedConsole.info('\nAfter adding the configuration, run:')
|
|
780
775
|
console.log(colors.highlight(`ai-changelog providers validate ${providerName}`))
|
|
781
776
|
} catch (error) {
|
|
782
777
|
EnhancedConsole.error(`Error configuring provider: ${error.message}`)
|
|
@@ -895,7 +890,9 @@ class ProvidersCommand extends BaseCommand {
|
|
|
895
890
|
|
|
896
891
|
// Summary
|
|
897
892
|
const healthy = healthResults.filter((r) => r.status === 'healthy').length
|
|
898
|
-
const unhealthy = healthResults.filter((r) =>
|
|
893
|
+
const unhealthy = healthResults.filter((r) =>
|
|
894
|
+
['unhealthy', 'error'].includes(r.status)
|
|
895
|
+
).length
|
|
899
896
|
const unconfigured = healthResults.filter((r) => r.status === 'unconfigured').length
|
|
900
897
|
|
|
901
898
|
console.log(colors.dim('ā'.repeat(40)))
|
|
@@ -918,7 +915,9 @@ class ProvidersCommand extends BaseCommand {
|
|
|
918
915
|
const provider = providers.find((p) => p.name.toLowerCase() === providerName.toLowerCase())
|
|
919
916
|
if (!provider) {
|
|
920
917
|
console.log(colors.errorMessage(`Provider '${providerName}' not found.`))
|
|
921
|
-
console.log(
|
|
918
|
+
console.log(
|
|
919
|
+
colors.infoMessage('Use "ai-changelog providers list" to see available providers.')
|
|
920
|
+
)
|
|
922
921
|
return
|
|
923
922
|
}
|
|
924
923
|
|
|
@@ -974,13 +973,31 @@ class ProvidersCommand extends BaseCommand {
|
|
|
974
973
|
|
|
975
974
|
getKnownModelsForProvider(providerName) {
|
|
976
975
|
const knownModels = {
|
|
977
|
-
openai: [
|
|
978
|
-
|
|
976
|
+
openai: [
|
|
977
|
+
'gpt-4o',
|
|
978
|
+
'gpt-4o-mini',
|
|
979
|
+
'gpt-4-turbo',
|
|
980
|
+
'gpt-4',
|
|
981
|
+
'gpt-3.5-turbo',
|
|
982
|
+
'o1',
|
|
983
|
+
'o1-mini',
|
|
984
|
+
'o3-mini',
|
|
985
|
+
],
|
|
986
|
+
anthropic: [
|
|
987
|
+
'claude-sonnet-4-20250514',
|
|
988
|
+
'claude-3-5-sonnet-20241022',
|
|
989
|
+
'claude-3-opus-20240229',
|
|
990
|
+
'claude-3-haiku-20240307',
|
|
991
|
+
],
|
|
979
992
|
google: ['gemini-2.0-flash-exp', 'gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-pro'],
|
|
980
993
|
azure: ['gpt-4o', 'gpt-4-turbo', 'gpt-35-turbo'],
|
|
981
994
|
ollama: ['llama3.2', 'llama3.1', 'mistral', 'codellama', 'deepseek-coder'],
|
|
982
995
|
lmstudio: ['local-model'],
|
|
983
|
-
bedrock: [
|
|
996
|
+
bedrock: [
|
|
997
|
+
'anthropic.claude-3-sonnet',
|
|
998
|
+
'anthropic.claude-3-haiku',
|
|
999
|
+
'amazon.titan-text-express',
|
|
1000
|
+
],
|
|
984
1001
|
vertex: ['gemini-1.5-pro', 'gemini-1.5-flash'],
|
|
985
1002
|
huggingface: ['mistralai/Mistral-7B-Instruct-v0.2', 'google/flan-t5-xxl'],
|
|
986
1003
|
}
|
|
@@ -1109,7 +1126,9 @@ class StashCommand extends BaseCommand {
|
|
|
1109
1126
|
console.log(colors.header('\nš Stash Changelog:\n'))
|
|
1110
1127
|
console.log(`## Stashed Changes\n`)
|
|
1111
1128
|
console.log(`**Message:** ${details.message}`)
|
|
1112
|
-
console.log(
|
|
1129
|
+
console.log(
|
|
1130
|
+
`**Stats:** ${details.stats.filesChanged} files, +${details.stats.insertions}/-${details.stats.deletions}`
|
|
1131
|
+
)
|
|
1113
1132
|
|
|
1114
1133
|
console.log(`\n**Files affected:**`)
|
|
1115
1134
|
details.files.forEach((f) => {
|
|
@@ -111,9 +111,11 @@ export class ConfigurationManager {
|
|
|
111
111
|
const content = fs.readFileSync(this.configPath, 'utf8')
|
|
112
112
|
const envVars = this.parseEnvFile(content)
|
|
113
113
|
Object.assign(defaults, envVars)
|
|
114
|
-
} catch (
|
|
114
|
+
} catch (error) {
|
|
115
115
|
console.warn(
|
|
116
|
-
colors.warningMessage(
|
|
116
|
+
colors.warningMessage(
|
|
117
|
+
`Warning: Could not load config from ${this.configPath}: ${error.message}`
|
|
118
|
+
)
|
|
117
119
|
)
|
|
118
120
|
}
|
|
119
121
|
}
|
|
@@ -179,10 +181,10 @@ export class ConfigurationManager {
|
|
|
179
181
|
|
|
180
182
|
// Deep merge with defaults
|
|
181
183
|
return this.deepMergeConfig(defaultConfig, yamlConfig)
|
|
182
|
-
} catch (
|
|
184
|
+
} catch (error) {
|
|
183
185
|
console.warn(
|
|
184
186
|
colors.warningMessage(
|
|
185
|
-
`Warning: Could not load changelog config from ${this.changelogConfigPath}, using defaults`
|
|
187
|
+
`Warning: Could not load changelog config from ${this.changelogConfigPath}: ${error.message}, using defaults`
|
|
186
188
|
)
|
|
187
189
|
)
|
|
188
190
|
return defaultConfig
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import process from 'node:process'
|
|
2
|
+
import { execSync } from 'node:child_process'
|
|
2
3
|
|
|
3
4
|
import colors from '../../shared/constants/colors.js'
|
|
4
5
|
import { EnhancedConsole, SimpleSpinner } from '../../shared/utils/cli-ui.js'
|
|
@@ -101,28 +102,40 @@ export class InteractiveWorkflowService {
|
|
|
101
102
|
// Use AI to suggest better commit message
|
|
102
103
|
if (this.aiAnalysisService.hasAI) {
|
|
103
104
|
const prompt = message
|
|
104
|
-
? `Improve this commit message following conventional commit format
|
|
105
|
+
? `Improve this commit message following conventional commit format.
|
|
105
106
|
|
|
106
107
|
Original: "${message}"
|
|
107
108
|
|
|
109
|
+
Provide EXACTLY 3 alternative commit messages, one per line, numbered 1-3.
|
|
110
|
+
Do NOT include any other text, explanations, or conversational phrases.
|
|
111
|
+
|
|
108
112
|
Requirements:
|
|
109
|
-
- Use conventional commit format
|
|
113
|
+
- Use conventional commit format: type(scope): description
|
|
110
114
|
- Keep first line under 72 characters
|
|
111
115
|
- Use imperative mood
|
|
112
116
|
- Be specific and descriptive
|
|
113
117
|
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
Example output format:
|
|
119
|
+
1. feat(auth): add OAuth2 authentication support
|
|
120
|
+
2. fix(api): resolve timeout in user endpoint
|
|
121
|
+
3. refactor(db): optimize query performance`
|
|
122
|
+
: `Generate commit messages for these changes:
|
|
116
123
|
|
|
117
124
|
${analysisContext}
|
|
118
125
|
|
|
126
|
+
Provide EXACTLY 3 commit message suggestions, one per line, numbered 1-3.
|
|
127
|
+
Do NOT include any other text, explanations, or conversational phrases.
|
|
128
|
+
|
|
119
129
|
Requirements:
|
|
120
|
-
- Use conventional commit format
|
|
130
|
+
- Use conventional commit format: type(scope): description
|
|
121
131
|
- Keep first line under 72 characters
|
|
122
132
|
- Use imperative mood
|
|
123
133
|
- Be specific and descriptive
|
|
124
134
|
|
|
125
|
-
|
|
135
|
+
Example output format:
|
|
136
|
+
1. feat(auth): add OAuth2 authentication support
|
|
137
|
+
2. fix(api): resolve timeout in user endpoint
|
|
138
|
+
3. refactor(db): optimize query performance`
|
|
126
139
|
|
|
127
140
|
const response = await this.aiAnalysisService.aiProvider.generateCompletion(
|
|
128
141
|
[
|
|
@@ -195,6 +208,71 @@ Provide 3 suggestions.`
|
|
|
195
208
|
summary += ` (${details.join(', ')})`
|
|
196
209
|
}
|
|
197
210
|
|
|
211
|
+
// Add actual change summaries from git diff for better context
|
|
212
|
+
summary += '\n\nKey changes:\n'
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Get a concise summary of actual changes, limiting to top 10 files
|
|
216
|
+
const filesToAnalyze = changes.slice(0, 10)
|
|
217
|
+
|
|
218
|
+
filesToAnalyze.forEach((change) => {
|
|
219
|
+
const filePath = change.filePath || change.path
|
|
220
|
+
const status = change.status
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
if (status === 'M' || status === 'MM') {
|
|
224
|
+
// For modified files, get a brief diff stat
|
|
225
|
+
const diffStat = execSync(`git diff HEAD -- "${filePath}" | head -50`, {
|
|
226
|
+
encoding: 'utf8',
|
|
227
|
+
maxBuffer: 1024 * 100,
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
if (diffStat) {
|
|
231
|
+
// Extract some meaningful info from diff
|
|
232
|
+
const addedLines = (diffStat.match(/^\+(?!\+)/gm) || []).length
|
|
233
|
+
const removedLines = (diffStat.match(/^-(?!-)/gm) || []).length
|
|
234
|
+
|
|
235
|
+
// Try to extract function/class names or key changes
|
|
236
|
+
const meaningfulLines = diffStat
|
|
237
|
+
.split('\n')
|
|
238
|
+
.filter(
|
|
239
|
+
(line) =>
|
|
240
|
+
line.startsWith('+') &&
|
|
241
|
+
(line.includes('function') ||
|
|
242
|
+
line.includes('class') ||
|
|
243
|
+
line.includes('const') ||
|
|
244
|
+
line.includes('export') ||
|
|
245
|
+
line.includes('import') ||
|
|
246
|
+
line.includes('async') ||
|
|
247
|
+
line.includes('//'))
|
|
248
|
+
)
|
|
249
|
+
.slice(0, 2)
|
|
250
|
+
.map((line) => line.substring(1).trim())
|
|
251
|
+
|
|
252
|
+
if (meaningfulLines.length > 0) {
|
|
253
|
+
summary += `- ${filePath}: +${addedLines}/-${removedLines} lines (${meaningfulLines[0].substring(0, 60)}...)\n`
|
|
254
|
+
} else {
|
|
255
|
+
summary += `- ${filePath}: +${addedLines}/-${removedLines} lines\n`
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
} else if (status === 'A' || status === '??') {
|
|
259
|
+
summary += `- ${filePath}: New file\n`
|
|
260
|
+
} else if (status === 'D') {
|
|
261
|
+
summary += `- ${filePath}: Deleted\n`
|
|
262
|
+
}
|
|
263
|
+
} catch (fileError) {
|
|
264
|
+
// Skip files that can't be diffed
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
if (changes.length > 10) {
|
|
269
|
+
summary += `... and ${changes.length - 10} more files\n`
|
|
270
|
+
}
|
|
271
|
+
} catch (error) {
|
|
272
|
+
// If diff analysis fails, fall back to basic summary
|
|
273
|
+
console.warn('Could not analyze diffs:', error.message)
|
|
274
|
+
}
|
|
275
|
+
|
|
198
276
|
return summary
|
|
199
277
|
}
|
|
200
278
|
|
|
@@ -380,17 +458,46 @@ Provide 3 suggestions.`
|
|
|
380
458
|
}
|
|
381
459
|
|
|
382
460
|
parseCommitSuggestions(content) {
|
|
383
|
-
// Parse AI response to extract suggestions
|
|
461
|
+
// Parse AI response to extract valid commit message suggestions
|
|
384
462
|
const lines = content.split('\n').filter((line) => line.trim())
|
|
385
463
|
const suggestions = []
|
|
386
464
|
|
|
465
|
+
// Valid conventional commit types
|
|
466
|
+
const validTypes = [
|
|
467
|
+
'feat',
|
|
468
|
+
'fix',
|
|
469
|
+
'docs',
|
|
470
|
+
'style',
|
|
471
|
+
'refactor',
|
|
472
|
+
'test',
|
|
473
|
+
'chore',
|
|
474
|
+
'perf',
|
|
475
|
+
'ci',
|
|
476
|
+
'build',
|
|
477
|
+
'revert',
|
|
478
|
+
]
|
|
479
|
+
|
|
387
480
|
lines.forEach((line) => {
|
|
388
481
|
// Remove numbering and clean up
|
|
389
|
-
const cleaned = line
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
482
|
+
const cleaned = line.replace(/^\d+\.\s*/, '').replace(/^-\s*/, '').trim()
|
|
483
|
+
|
|
484
|
+
// Must be a reasonable length
|
|
485
|
+
if (!cleaned || cleaned.length < 10 || cleaned.length > 100) {
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Must have a colon (conventional format)
|
|
490
|
+
if (!cleaned.includes(':')) {
|
|
491
|
+
return
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Must start with a valid conventional commit type
|
|
495
|
+
const startsWithValidType = validTypes.some((type) => {
|
|
496
|
+
const pattern = new RegExp(`^${type}(\\(|:)`, 'i')
|
|
497
|
+
return pattern.test(cleaned)
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
if (startsWithValidType) {
|
|
394
501
|
suggestions.push(cleaned)
|
|
395
502
|
}
|
|
396
503
|
})
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Provides Model Context Protocol interface for changelog generation
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { exec } from 'node:child_process'
|
|
9
|
+
import { promisify } from 'node:util'
|
|
9
10
|
import fs from 'node:fs'
|
|
10
11
|
import path, { dirname } from 'node:path'
|
|
11
12
|
import process from 'node:process'
|
|
@@ -18,8 +19,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
|
|
|
18
19
|
import { ChangelogOrchestrator } from '../../application/orchestrators/changelog.orchestrator.js'
|
|
19
20
|
// Import application services
|
|
20
21
|
import { ApplicationService } from '../../application/services/application.service.js'
|
|
21
|
-
|
|
22
|
-
import { GitService } from '../../domains/git/git.service.js'
|
|
22
|
+
|
|
23
23
|
import { ConfigurationManager } from '../config/configuration.manager.js'
|
|
24
24
|
import { ProviderManagerService } from '../providers/provider-manager.service.js'
|
|
25
25
|
|
|
@@ -312,7 +312,7 @@ class AIChangelogMCPServer {
|
|
|
312
312
|
|
|
313
313
|
let result
|
|
314
314
|
|
|
315
|
-
if (source === 'working-dir' || (source === 'auto' && this.hasWorkingDirectoryChanges())) {
|
|
315
|
+
if (source === 'working-dir' || (source === 'auto' && (await this.hasWorkingDirectoryChanges()))) {
|
|
316
316
|
// Generate from working directory changes using ChangelogService
|
|
317
317
|
// Access the changelogService through the orchestrator
|
|
318
318
|
await this.changelogOrchestrator.ensureInitialized()
|
|
@@ -324,7 +324,7 @@ class AIChangelogMCPServer {
|
|
|
324
324
|
})
|
|
325
325
|
|
|
326
326
|
// Format result to match expected structure
|
|
327
|
-
if (result
|
|
327
|
+
if (result?.changelog) {
|
|
328
328
|
result = {
|
|
329
329
|
content: result.changelog,
|
|
330
330
|
metadata: {
|
|
@@ -522,12 +522,15 @@ class AIChangelogMCPServer {
|
|
|
522
522
|
}
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
hasWorkingDirectoryChanges() {
|
|
525
|
+
async hasWorkingDirectoryChanges() {
|
|
526
526
|
try {
|
|
527
|
-
|
|
528
|
-
const
|
|
529
|
-
return
|
|
530
|
-
} catch (
|
|
527
|
+
const execAsync = promisify(exec)
|
|
528
|
+
const { stdout } = await execAsync('git status --porcelain', { encoding: 'utf8' })
|
|
529
|
+
return stdout.trim().length > 0
|
|
530
|
+
} catch (error) {
|
|
531
|
+
// If git is not installed or not a git repo, we can assume no changes for our purpose
|
|
532
|
+
// but we should log the error for debugging
|
|
533
|
+
console.warn(`[MCP] Git check warning: ${error.message}`)
|
|
531
534
|
return false
|
|
532
535
|
}
|
|
533
536
|
}
|
|
@@ -87,7 +87,8 @@ export class BaseProvider {
|
|
|
87
87
|
async selectOptimalModel(commitInfo) {
|
|
88
88
|
try {
|
|
89
89
|
return this.getModelRecommendation(commitInfo)
|
|
90
|
-
} catch (
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.warn(`Warning: Model selection failed: ${error.message}. Using default model.`)
|
|
91
92
|
return { model: this.getDefaultModel(), reason: 'fallback' }
|
|
92
93
|
}
|
|
93
94
|
}
|
|
@@ -14,7 +14,9 @@ function addWarningToCache(key) {
|
|
|
14
14
|
warningCache.clear()
|
|
15
15
|
// Keep last 80% of entries
|
|
16
16
|
const keepCount = Math.floor(MAX_WARNING_CACHE_SIZE * 0.8)
|
|
17
|
-
entries.slice(-keepCount).forEach((entry) =>
|
|
17
|
+
entries.slice(-keepCount).forEach((entry) => {
|
|
18
|
+
warningCache.add(entry)
|
|
19
|
+
})
|
|
18
20
|
}
|
|
19
21
|
warningCache.add(key)
|
|
20
22
|
}
|
|
@@ -483,7 +483,7 @@ export class CommitMessageValidationService {
|
|
|
483
483
|
* Display validation results with colors
|
|
484
484
|
*/
|
|
485
485
|
displayValidationResults(validationResult) {
|
|
486
|
-
const { valid, errors, warnings, suggestions,
|
|
486
|
+
const { valid, errors, warnings, suggestions, summary } = validationResult
|
|
487
487
|
|
|
488
488
|
EnhancedConsole.section('š Commit Message Validation')
|
|
489
489
|
console.log(colors.secondary(`${summary}`))
|
|
@@ -285,6 +285,7 @@ class Colors {
|
|
|
285
285
|
// Helper to get actual visible length of string (without ANSI codes)
|
|
286
286
|
getVisibleLength(str) {
|
|
287
287
|
// Remove ANSI escape sequences to get actual visible length
|
|
288
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Needed for ANSI codes
|
|
288
289
|
return str.replace(/\x1b\[[0-9;]*m/g, '').length
|
|
289
290
|
}
|
|
290
291
|
|
|
@@ -616,7 +617,7 @@ class Colors {
|
|
|
616
617
|
return ''
|
|
617
618
|
}
|
|
618
619
|
|
|
619
|
-
const { headers = Object.keys(data[0]), align = 'left'
|
|
620
|
+
const { headers = Object.keys(data[0]), align = 'left' } = options
|
|
620
621
|
const rows = data.map((row) => headers.map((header) => String(row[header] || '')))
|
|
621
622
|
|
|
622
623
|
// Calculate column widths
|
|
@@ -29,7 +29,9 @@ export function handleCLIError(error, context = 'application', options = {}) {
|
|
|
29
29
|
if (showTips) {
|
|
30
30
|
const tips = getContextualTips(error, context)
|
|
31
31
|
if (tips.length > 0) {
|
|
32
|
-
tips.forEach((tip) =>
|
|
32
|
+
tips.forEach((tip) => {
|
|
33
|
+
console.error(colors.infoMessage(`š” Tip: ${tip}`))
|
|
34
|
+
})
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -191,41 +191,44 @@ export class MultiProgress {
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
import { format } from 'node:util'
|
|
195
|
+
|
|
194
196
|
/**
|
|
195
197
|
* Enhanced console with better formatting
|
|
196
198
|
*/
|
|
197
199
|
export class EnhancedConsole {
|
|
198
|
-
static log(
|
|
200
|
+
static log(type = 'info', ...args) {
|
|
201
|
+
const message = format(...args)
|
|
199
202
|
console.log(colors.statusSymbol(type, message))
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
static success(
|
|
203
|
-
EnhancedConsole.log(
|
|
205
|
+
static success(...args) {
|
|
206
|
+
EnhancedConsole.log('success', ...args)
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
static error(
|
|
207
|
-
EnhancedConsole.log(
|
|
209
|
+
static error(...args) {
|
|
210
|
+
EnhancedConsole.log('error', ...args)
|
|
208
211
|
}
|
|
209
212
|
|
|
210
|
-
static warn(
|
|
211
|
-
EnhancedConsole.log(
|
|
213
|
+
static warn(...args) {
|
|
214
|
+
EnhancedConsole.log('warning', ...args)
|
|
212
215
|
}
|
|
213
216
|
|
|
214
|
-
static info(
|
|
215
|
-
EnhancedConsole.log(
|
|
217
|
+
static info(...args) {
|
|
218
|
+
EnhancedConsole.log('info', ...args)
|
|
216
219
|
}
|
|
217
220
|
|
|
218
|
-
static processing(
|
|
219
|
-
EnhancedConsole.log(
|
|
221
|
+
static processing(...args) {
|
|
222
|
+
EnhancedConsole.log('processing', ...args)
|
|
220
223
|
}
|
|
221
224
|
|
|
222
|
-
static ai(
|
|
223
|
-
EnhancedConsole.log(
|
|
225
|
+
static ai(...args) {
|
|
226
|
+
EnhancedConsole.log('ai', ...args)
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
static metrics(data, title = 'Metrics') {
|
|
227
230
|
if (typeof data === 'string') {
|
|
228
|
-
EnhancedConsole.log(
|
|
231
|
+
EnhancedConsole.log('metrics', data)
|
|
229
232
|
} else if (data && Object.keys(data).length > 0) {
|
|
230
233
|
EnhancedConsole.section(title)
|
|
231
234
|
console.log(colors.formatMetrics(data))
|
|
@@ -101,7 +101,7 @@ export class DiffProcessor {
|
|
|
101
101
|
* Process a single file's diff with intelligent compression
|
|
102
102
|
*/
|
|
103
103
|
processFileDiff(file, options = {}) {
|
|
104
|
-
const { budget = 4000,
|
|
104
|
+
const { budget = 4000, _patterns = {}, isHighPriority = false } = options // Increased from 1500
|
|
105
105
|
|
|
106
106
|
if (!file.diff || file.diff === 'No diff available') {
|
|
107
107
|
return {
|
|
@@ -120,17 +120,7 @@ export class DiffProcessor {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// Temporarily disable bulk pattern matching to preserve more detail
|
|
123
|
-
//
|
|
124
|
-
// const patternMatch = this.matchesBulkPattern(file, patterns)
|
|
125
|
-
// if (patternMatch) {
|
|
126
|
-
// return {
|
|
127
|
-
// ...file,
|
|
128
|
-
// diff: `[Bulk ${patternMatch.type}]: ${patternMatch.description}`,
|
|
129
|
-
// processed: true,
|
|
130
|
-
// compressionApplied: true,
|
|
131
|
-
// bulkPattern: patternMatch.type,
|
|
132
|
-
// }
|
|
133
|
-
// }
|
|
123
|
+
// Pattern matching logic removed for cleanup
|
|
134
124
|
|
|
135
125
|
// Apply intelligent truncation if still too large
|
|
136
126
|
if (processedDiff.length > budget) {
|
|
@@ -370,7 +360,7 @@ export class DiffProcessor {
|
|
|
370
360
|
* Apply intelligent truncation that preserves structure
|
|
371
361
|
*/
|
|
372
362
|
intelligentTruncation(diff, budget, options = {}) {
|
|
373
|
-
const { preserveStructure = false,
|
|
363
|
+
const { preserveStructure = false, _filePath = '' } = options
|
|
374
364
|
|
|
375
365
|
if (diff.length <= budget) {
|
|
376
366
|
return diff
|