@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
|
@@ -5,41 +5,41 @@
|
|
|
5
5
|
* Provides Model Context Protocol interface for changelog generation
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
8
|
+
import fs from 'node:fs'
|
|
9
|
+
import path, { dirname } from 'node:path'
|
|
10
|
+
import process from 'node:process'
|
|
11
|
+
import { fileURLToPath } from 'node:url'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { GitRepositoryAnalyzer } from '../../domains/git/git-repository.analyzer.js';
|
|
16
|
-
import { AnalysisEngine } from '../../domains/analysis/analysis.engine.js';
|
|
17
|
-
import { ProviderManagementService } from '../providers/provider-management.service.js';
|
|
18
|
-
import { ConfigurationManager } from '../config/configuration.manager.js';
|
|
13
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
|
14
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
15
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
|
19
16
|
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
17
|
+
import { ChangelogOrchestrator } from '../../application/orchestrators/changelog.orchestrator.js'
|
|
18
|
+
// Import application services
|
|
19
|
+
import { ApplicationService } from '../../application/services/application.service.js'
|
|
20
|
+
import { AnalysisEngine } from '../../domains/analysis/analysis.engine.js'
|
|
21
|
+
import { GitRepositoryAnalyzer } from '../../domains/git/git-repository.analyzer.js'
|
|
22
|
+
import { ConfigurationManager } from '../config/configuration.manager.js'
|
|
23
|
+
import { ProviderManagementService } from '../providers/provider-management.service.js'
|
|
24
24
|
|
|
25
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
26
|
-
const __dirname = dirname(__filename)
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
26
|
+
const __dirname = dirname(__filename)
|
|
27
27
|
|
|
28
28
|
class AIChangelogMCPServer {
|
|
29
29
|
constructor() {
|
|
30
|
-
this.initializeServer()
|
|
31
|
-
this.initializeServices()
|
|
32
|
-
this.setupHandlers()
|
|
30
|
+
this.initializeServer()
|
|
31
|
+
this.initializeServices()
|
|
32
|
+
this.setupHandlers()
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
initializeServer() {
|
|
36
|
-
let packageJson
|
|
36
|
+
let packageJson
|
|
37
37
|
try {
|
|
38
|
-
const packagePath = path.join(__dirname, '../../../package.json')
|
|
39
|
-
const packageContent = fs.readFileSync(packagePath, 'utf8')
|
|
40
|
-
packageJson = JSON.parse(packageContent)
|
|
41
|
-
} catch (
|
|
42
|
-
packageJson = { version: '1.0.0' }
|
|
38
|
+
const packagePath = path.join(__dirname, '../../../package.json')
|
|
39
|
+
const packageContent = fs.readFileSync(packagePath, 'utf8')
|
|
40
|
+
packageJson = JSON.parse(packageContent)
|
|
41
|
+
} catch (_error) {
|
|
42
|
+
packageJson = { version: '1.0.0' }
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
this.server = new Server(
|
|
@@ -53,40 +53,42 @@ class AIChangelogMCPServer {
|
|
|
53
53
|
resources: {},
|
|
54
54
|
},
|
|
55
55
|
}
|
|
56
|
-
)
|
|
56
|
+
)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
initializeServices() {
|
|
60
60
|
try {
|
|
61
61
|
// Set MCP server mode to suppress verbose logging
|
|
62
|
-
process.env.MCP_SERVER_MODE = 'true'
|
|
63
|
-
|
|
64
|
-
this.configManager = new ConfigurationManager()
|
|
65
|
-
this.applicationService = new ApplicationService()
|
|
66
|
-
this.changelogOrchestrator = new ChangelogOrchestrator(this.configManager)
|
|
67
|
-
this.gitAnalyzer = new GitRepositoryAnalyzer()
|
|
68
|
-
this.analysisEngine = new AnalysisEngine()
|
|
69
|
-
this.providerService = new ProviderManagementService()
|
|
70
|
-
|
|
62
|
+
process.env.MCP_SERVER_MODE = 'true'
|
|
63
|
+
|
|
64
|
+
this.configManager = new ConfigurationManager()
|
|
65
|
+
this.applicationService = new ApplicationService()
|
|
66
|
+
this.changelogOrchestrator = new ChangelogOrchestrator(this.configManager)
|
|
67
|
+
this.gitAnalyzer = new GitRepositoryAnalyzer()
|
|
68
|
+
this.analysisEngine = new AnalysisEngine()
|
|
69
|
+
this.providerService = new ProviderManagementService()
|
|
70
|
+
|
|
71
71
|
// Log available configuration
|
|
72
|
-
const hasProvider = process.env.AI_PROVIDER
|
|
73
|
-
const hasApiKey =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
const hasProvider = process.env.AI_PROVIDER
|
|
73
|
+
const hasApiKey =
|
|
74
|
+
process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.GOOGLE_API_KEY
|
|
75
|
+
|
|
76
|
+
if (!(hasProvider && hasApiKey)) {
|
|
77
|
+
console.error(
|
|
78
|
+
'[MCP] Warning: No AI provider or API key configured. Tools will provide configuration guidance.'
|
|
79
|
+
)
|
|
77
80
|
} else {
|
|
78
|
-
console.error(`[MCP] Configured with provider: ${process.env.AI_PROVIDER}`)
|
|
81
|
+
console.error(`[MCP] Configured with provider: ${process.env.AI_PROVIDER}`)
|
|
79
82
|
}
|
|
80
|
-
|
|
81
83
|
} catch (error) {
|
|
82
|
-
console.error('[MCP] Failed to initialize services:', error.message)
|
|
83
|
-
console.error('[MCP] Server will start but tools may require configuration')
|
|
84
|
+
console.error('[MCP] Failed to initialize services:', error.message)
|
|
85
|
+
console.error('[MCP] Server will start but tools may require configuration')
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
setupHandlers() {
|
|
88
|
-
this.server.setRequestHandler(ListToolsRequestSchema, this.handleListTools.bind(this))
|
|
89
|
-
this.server.setRequestHandler(CallToolRequestSchema, this.handleCallTool.bind(this))
|
|
90
|
+
this.server.setRequestHandler(ListToolsRequestSchema, this.handleListTools.bind(this))
|
|
91
|
+
this.server.setRequestHandler(CallToolRequestSchema, this.handleCallTool.bind(this))
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
handleListTools() {
|
|
@@ -110,7 +112,8 @@ class AIChangelogMCPServer {
|
|
|
110
112
|
},
|
|
111
113
|
since: {
|
|
112
114
|
type: 'string',
|
|
113
|
-
description:
|
|
115
|
+
description:
|
|
116
|
+
'For commit analysis: since tag/commit/date (e.g., "v1.0.0", "HEAD~10")',
|
|
114
117
|
},
|
|
115
118
|
version: {
|
|
116
119
|
type: 'string',
|
|
@@ -219,67 +222,66 @@ class AIChangelogMCPServer {
|
|
|
219
222
|
},
|
|
220
223
|
},
|
|
221
224
|
],
|
|
222
|
-
}
|
|
225
|
+
}
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
async handleCallTool(request) {
|
|
226
|
-
const { name, arguments: args } = request.params
|
|
227
|
-
console.log(`[MCP] Tool call: ${name}`)
|
|
229
|
+
const { name, arguments: args } = request.params
|
|
230
|
+
console.log(`[MCP] Tool call: ${name}`)
|
|
228
231
|
|
|
229
232
|
try {
|
|
230
|
-
console.time(`[MCP-TIMER] ${name}`)
|
|
233
|
+
console.time(`[MCP-TIMER] ${name}`)
|
|
231
234
|
|
|
232
235
|
const timeouts = {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
236
|
+
generate_changelog: 120000,
|
|
237
|
+
analyze_repository: 90000,
|
|
238
|
+
analyze_current_changes: 60000,
|
|
239
|
+
configure_providers: 30000,
|
|
240
|
+
}
|
|
238
241
|
|
|
239
|
-
const timeout = timeouts[name] || 60000
|
|
242
|
+
const timeout = timeouts[name] || 60000
|
|
240
243
|
const timeoutPromise = new Promise((_, reject) => {
|
|
241
|
-
setTimeout(() => reject(new Error(`Tool '${name}' timed out after ${timeout}ms`)), timeout)
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
const operationPromise = this.executeOperation(name, args);
|
|
245
|
-
const result = await Promise.race([operationPromise, timeoutPromise]);
|
|
244
|
+
setTimeout(() => reject(new Error(`Tool '${name}' timed out after ${timeout}ms`)), timeout)
|
|
245
|
+
})
|
|
246
246
|
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
const operationPromise = this.executeOperation(name, args)
|
|
248
|
+
const result = await Promise.race([operationPromise, timeoutPromise])
|
|
249
249
|
|
|
250
|
+
console.timeEnd(`[MCP-TIMER] ${name}`)
|
|
251
|
+
return result
|
|
250
252
|
} catch (error) {
|
|
251
|
-
console.error(`[MCP] Tool Error [${name}]:`, error.message)
|
|
252
|
-
console.timeEnd(`[MCP-TIMER] ${name}`)
|
|
253
|
-
return this.formatError(error, name)
|
|
253
|
+
console.error(`[MCP] Tool Error [${name}]:`, error.message)
|
|
254
|
+
console.timeEnd(`[MCP-TIMER] ${name}`)
|
|
255
|
+
return this.formatError(error, name)
|
|
254
256
|
}
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
async executeOperation(name, args) {
|
|
258
|
-
const originalCwd = process.cwd()
|
|
259
|
-
|
|
260
|
+
const originalCwd = process.cwd()
|
|
261
|
+
|
|
260
262
|
try {
|
|
261
263
|
// Change to repository path if specified
|
|
262
264
|
if (args.repositoryPath && args.repositoryPath !== process.cwd()) {
|
|
263
265
|
if (!fs.existsSync(args.repositoryPath)) {
|
|
264
|
-
throw new Error(`Repository path does not exist: ${args.repositoryPath}`)
|
|
266
|
+
throw new Error(`Repository path does not exist: ${args.repositoryPath}`)
|
|
265
267
|
}
|
|
266
|
-
process.chdir(args.repositoryPath)
|
|
268
|
+
process.chdir(args.repositoryPath)
|
|
267
269
|
}
|
|
268
270
|
|
|
269
271
|
switch (name) {
|
|
270
272
|
case 'generate_changelog':
|
|
271
|
-
return await this.generateChangelog(args)
|
|
273
|
+
return await this.generateChangelog(args)
|
|
272
274
|
case 'analyze_repository':
|
|
273
|
-
return await this.analyzeRepository(args)
|
|
275
|
+
return await this.analyzeRepository(args)
|
|
274
276
|
case 'analyze_current_changes':
|
|
275
|
-
return await this.analyzeCurrentChanges(args)
|
|
277
|
+
return await this.analyzeCurrentChanges(args)
|
|
276
278
|
case 'configure_providers':
|
|
277
|
-
return await this.configureProviders(args)
|
|
279
|
+
return await this.configureProviders(args)
|
|
278
280
|
default:
|
|
279
|
-
throw new Error(`Unknown tool: ${name}`)
|
|
281
|
+
throw new Error(`Unknown tool: ${name}`)
|
|
280
282
|
}
|
|
281
283
|
} finally {
|
|
282
|
-
process.chdir(originalCwd)
|
|
284
|
+
process.chdir(originalCwd)
|
|
283
285
|
}
|
|
284
286
|
}
|
|
285
287
|
|
|
@@ -290,251 +292,250 @@ class AIChangelogMCPServer {
|
|
|
290
292
|
version,
|
|
291
293
|
analysisMode = 'detailed',
|
|
292
294
|
includeAttribution = true,
|
|
293
|
-
writeFile = true
|
|
294
|
-
} = args
|
|
295
|
+
writeFile = true,
|
|
296
|
+
} = args
|
|
295
297
|
|
|
296
298
|
try {
|
|
297
|
-
let result
|
|
299
|
+
let result
|
|
298
300
|
|
|
299
301
|
if (source === 'working-dir' || (source === 'auto' && this.hasWorkingDirectoryChanges())) {
|
|
300
302
|
// Generate from working directory changes
|
|
301
303
|
result = await this.changelogOrchestrator.generateWorkspaceChangelog({
|
|
302
304
|
version,
|
|
303
305
|
analysisMode,
|
|
304
|
-
includeAttribution
|
|
305
|
-
})
|
|
306
|
+
includeAttribution,
|
|
307
|
+
})
|
|
306
308
|
} else {
|
|
307
309
|
// Generate from commits
|
|
308
310
|
result = await this.changelogOrchestrator.generateChangelog({
|
|
309
311
|
version,
|
|
310
312
|
since,
|
|
311
313
|
analysisMode,
|
|
312
|
-
includeAttribution
|
|
313
|
-
})
|
|
314
|
+
includeAttribution,
|
|
315
|
+
})
|
|
314
316
|
}
|
|
315
317
|
|
|
316
318
|
// Write file if requested
|
|
317
319
|
if (writeFile && result.content) {
|
|
318
|
-
const changelogPath = path.join(process.cwd(), 'AI_CHANGELOG.md')
|
|
320
|
+
const changelogPath = path.join(process.cwd(), 'AI_CHANGELOG.md')
|
|
319
321
|
try {
|
|
320
|
-
fs.writeFileSync(changelogPath, result.content, 'utf8')
|
|
321
|
-
console.log(`[MCP] Changelog written to: ${changelogPath}`)
|
|
322
|
+
fs.writeFileSync(changelogPath, result.content, 'utf8')
|
|
323
|
+
console.log(`[MCP] Changelog written to: ${changelogPath}`)
|
|
322
324
|
} catch (writeError) {
|
|
323
|
-
console.warn(`[MCP] Could not write file: ${writeError.message}`)
|
|
325
|
+
console.warn(`[MCP] Could not write file: ${writeError.message}`)
|
|
324
326
|
}
|
|
325
327
|
}
|
|
326
328
|
|
|
327
329
|
return {
|
|
328
|
-
content: [
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
330
|
+
content: [
|
|
331
|
+
{
|
|
332
|
+
type: 'text',
|
|
333
|
+
text: result.content || 'No changelog content generated',
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
metadata: result.metadata,
|
|
337
|
+
}
|
|
335
338
|
} catch (error) {
|
|
336
|
-
throw new Error(`Changelog generation failed: ${error.message}`)
|
|
339
|
+
throw new Error(`Changelog generation failed: ${error.message}`)
|
|
337
340
|
}
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
async analyzeRepository(args) {
|
|
341
|
-
const {
|
|
342
|
-
analysisType = 'comprehensive',
|
|
343
|
-
includeRecommendations = true,
|
|
344
|
-
commitLimit = 50
|
|
345
|
-
} = args;
|
|
344
|
+
const { analysisType = 'comprehensive', includeRecommendations = true, commitLimit = 50 } = args
|
|
346
345
|
|
|
347
346
|
try {
|
|
348
|
-
let result
|
|
347
|
+
let result
|
|
349
348
|
|
|
350
349
|
switch (analysisType) {
|
|
351
350
|
case 'health':
|
|
352
|
-
result = await this.gitAnalyzer.assessRepositoryHealth(includeRecommendations)
|
|
353
|
-
break
|
|
351
|
+
result = await this.gitAnalyzer.assessRepositoryHealth(includeRecommendations)
|
|
352
|
+
break
|
|
354
353
|
case 'commits':
|
|
355
|
-
result = await this.analysisEngine.analyzeRecentCommits(commitLimit)
|
|
356
|
-
break
|
|
354
|
+
result = await this.analysisEngine.analyzeRecentCommits(commitLimit)
|
|
355
|
+
break
|
|
357
356
|
case 'branches':
|
|
358
|
-
result = await this.gitAnalyzer.analyzeBranches()
|
|
359
|
-
break
|
|
357
|
+
result = await this.gitAnalyzer.analyzeBranches()
|
|
358
|
+
break
|
|
360
359
|
case 'working-dir':
|
|
361
|
-
result = await this.analysisEngine.analyzeCurrentChanges()
|
|
362
|
-
break
|
|
363
|
-
case 'comprehensive':
|
|
360
|
+
result = await this.analysisEngine.analyzeCurrentChanges()
|
|
361
|
+
break
|
|
364
362
|
default:
|
|
365
|
-
result = await this.gitAnalyzer.analyzeComprehensive(includeRecommendations)
|
|
366
|
-
break
|
|
363
|
+
result = await this.gitAnalyzer.analyzeComprehensive(includeRecommendations)
|
|
364
|
+
break
|
|
367
365
|
}
|
|
368
366
|
|
|
369
367
|
return {
|
|
370
|
-
content: [
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
368
|
+
content: [
|
|
369
|
+
{
|
|
370
|
+
type: 'text',
|
|
371
|
+
text: JSON.stringify(result, null, 2),
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
}
|
|
376
375
|
} catch (error) {
|
|
377
|
-
throw new Error(`Repository analysis failed: ${error.message}`)
|
|
376
|
+
throw new Error(`Repository analysis failed: ${error.message}`)
|
|
378
377
|
}
|
|
379
378
|
}
|
|
380
379
|
|
|
381
380
|
async analyzeCurrentChanges(args) {
|
|
382
|
-
const {
|
|
383
|
-
includeAIAnalysis = true,
|
|
384
|
-
includeAttribution = true
|
|
385
|
-
} = args;
|
|
381
|
+
const { includeAIAnalysis = true, includeAttribution = true } = args
|
|
386
382
|
|
|
387
383
|
try {
|
|
388
384
|
const result = await this.analysisEngine.analyzeCurrentChanges({
|
|
389
385
|
includeAIAnalysis,
|
|
390
|
-
includeAttribution
|
|
391
|
-
})
|
|
386
|
+
includeAttribution,
|
|
387
|
+
})
|
|
392
388
|
|
|
393
389
|
return {
|
|
394
|
-
content: [
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
390
|
+
content: [
|
|
391
|
+
{
|
|
392
|
+
type: 'text',
|
|
393
|
+
text: JSON.stringify(result, null, 2),
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
}
|
|
400
397
|
} catch (error) {
|
|
401
|
-
throw new Error(`Current changes analysis failed: ${error.message}`)
|
|
398
|
+
throw new Error(`Current changes analysis failed: ${error.message}`)
|
|
402
399
|
}
|
|
403
400
|
}
|
|
404
401
|
|
|
405
402
|
async configureProviders(args) {
|
|
406
|
-
const {
|
|
407
|
-
action = 'list',
|
|
408
|
-
provider,
|
|
409
|
-
testConnection = false
|
|
410
|
-
} = args;
|
|
403
|
+
const { action = 'list', provider, testConnection = false } = args
|
|
411
404
|
|
|
412
405
|
try {
|
|
413
|
-
let result
|
|
406
|
+
let result
|
|
414
407
|
|
|
415
408
|
switch (action) {
|
|
416
409
|
case 'list':
|
|
417
|
-
result = await this.providerService.listProviders()
|
|
418
|
-
break
|
|
410
|
+
result = await this.providerService.listProviders()
|
|
411
|
+
break
|
|
419
412
|
case 'switch':
|
|
420
|
-
if (!provider)
|
|
421
|
-
|
|
413
|
+
if (!provider) {
|
|
414
|
+
throw new Error('Provider required for switch action')
|
|
415
|
+
}
|
|
416
|
+
result = await this.providerService.switchProvider(provider)
|
|
422
417
|
if (testConnection) {
|
|
423
|
-
const testResult = await this.providerService.testProvider(provider)
|
|
424
|
-
result += `\n${testResult}
|
|
418
|
+
const testResult = await this.providerService.testProvider(provider)
|
|
419
|
+
result += `\n${testResult}`
|
|
425
420
|
}
|
|
426
|
-
break
|
|
421
|
+
break
|
|
427
422
|
case 'test':
|
|
428
|
-
result = await this.providerService.testCurrentProvider()
|
|
429
|
-
break
|
|
423
|
+
result = await this.providerService.testCurrentProvider()
|
|
424
|
+
break
|
|
430
425
|
case 'configure':
|
|
431
|
-
result = await this.providerService.configureProvider(provider)
|
|
432
|
-
break
|
|
426
|
+
result = await this.providerService.configureProvider(provider)
|
|
427
|
+
break
|
|
433
428
|
case 'validate':
|
|
434
|
-
result = await this.providerService.validateModels(provider)
|
|
435
|
-
break
|
|
429
|
+
result = await this.providerService.validateModels(provider)
|
|
430
|
+
break
|
|
436
431
|
default:
|
|
437
|
-
throw new Error(`Unknown action: ${action}`)
|
|
432
|
+
throw new Error(`Unknown action: ${action}`)
|
|
438
433
|
}
|
|
439
434
|
|
|
440
435
|
return {
|
|
441
|
-
content: [
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
436
|
+
content: [
|
|
437
|
+
{
|
|
438
|
+
type: 'text',
|
|
439
|
+
text: result,
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
}
|
|
447
443
|
} catch (error) {
|
|
448
|
-
throw new Error(`Provider management failed: ${error.message}`)
|
|
444
|
+
throw new Error(`Provider management failed: ${error.message}`)
|
|
449
445
|
}
|
|
450
446
|
}
|
|
451
447
|
|
|
452
448
|
hasWorkingDirectoryChanges() {
|
|
453
449
|
try {
|
|
454
450
|
// Simple check for working directory changes
|
|
455
|
-
const { execSync } = require('child_process')
|
|
456
|
-
const result = execSync('git status --porcelain', { encoding: 'utf8' })
|
|
457
|
-
return result.trim().length > 0
|
|
458
|
-
} catch (
|
|
459
|
-
return false
|
|
451
|
+
const { execSync } = require('node:child_process')
|
|
452
|
+
const result = execSync('git status --porcelain', { encoding: 'utf8' })
|
|
453
|
+
return result.trim().length > 0
|
|
454
|
+
} catch (_error) {
|
|
455
|
+
return false
|
|
460
456
|
}
|
|
461
457
|
}
|
|
462
458
|
|
|
463
459
|
formatError(error, toolName) {
|
|
464
460
|
if (error.message.includes('timed out')) {
|
|
465
461
|
return {
|
|
466
|
-
content: [
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
462
|
+
content: [
|
|
463
|
+
{
|
|
464
|
+
type: 'text',
|
|
465
|
+
text: `⏱️ Timeout: '${toolName}' exceeded time limit. Try with smaller scope or check connectivity.`,
|
|
466
|
+
},
|
|
467
|
+
],
|
|
470
468
|
isError: true,
|
|
471
|
-
}
|
|
469
|
+
}
|
|
472
470
|
}
|
|
473
471
|
|
|
474
472
|
return {
|
|
475
|
-
content: [
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
473
|
+
content: [
|
|
474
|
+
{
|
|
475
|
+
type: 'text',
|
|
476
|
+
text: `❌ Error in '${toolName}': ${error.message}`,
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
479
|
isError: true,
|
|
480
|
-
}
|
|
480
|
+
}
|
|
481
481
|
}
|
|
482
482
|
|
|
483
483
|
async run() {
|
|
484
484
|
try {
|
|
485
|
-
const transport = new StdioServerTransport()
|
|
486
|
-
await this.server.connect(transport)
|
|
485
|
+
const transport = new StdioServerTransport()
|
|
486
|
+
await this.server.connect(transport)
|
|
487
487
|
|
|
488
488
|
// Setup graceful shutdown
|
|
489
|
-
let isShuttingDown = false
|
|
489
|
+
let isShuttingDown = false
|
|
490
490
|
const gracefulShutdown = async (signal) => {
|
|
491
|
-
if (isShuttingDown)
|
|
492
|
-
|
|
493
|
-
|
|
491
|
+
if (isShuttingDown) {
|
|
492
|
+
return
|
|
493
|
+
}
|
|
494
|
+
isShuttingDown = true
|
|
495
|
+
|
|
494
496
|
try {
|
|
495
|
-
console.error(`[MCP] Received ${signal}, shutting down gracefully...`)
|
|
496
|
-
await this.server.close()
|
|
497
|
-
process.exit(0)
|
|
497
|
+
console.error(`[MCP] Received ${signal}, shutting down gracefully...`)
|
|
498
|
+
await this.server.close()
|
|
499
|
+
process.exit(0)
|
|
498
500
|
} catch (error) {
|
|
499
|
-
console.error('[MCP] Error during shutdown:', error)
|
|
500
|
-
process.exit(1)
|
|
501
|
+
console.error('[MCP] Error during shutdown:', error)
|
|
502
|
+
process.exit(1)
|
|
501
503
|
}
|
|
502
|
-
}
|
|
504
|
+
}
|
|
503
505
|
|
|
504
|
-
process.on('SIGINT', () => gracefulShutdown('SIGINT'))
|
|
505
|
-
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'))
|
|
506
|
-
process.on('SIGQUIT', () => gracefulShutdown('SIGQUIT'))
|
|
506
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'))
|
|
507
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'))
|
|
508
|
+
process.on('SIGQUIT', () => gracefulShutdown('SIGQUIT'))
|
|
507
509
|
|
|
508
510
|
// Handle uncaught exceptions
|
|
509
511
|
process.on('uncaughtException', (error) => {
|
|
510
|
-
console.error('[MCP] Uncaught exception:', error)
|
|
511
|
-
gracefulShutdown('uncaughtException')
|
|
512
|
-
})
|
|
512
|
+
console.error('[MCP] Uncaught exception:', error)
|
|
513
|
+
gracefulShutdown('uncaughtException')
|
|
514
|
+
})
|
|
513
515
|
|
|
514
516
|
process.on('unhandledRejection', (reason, promise) => {
|
|
515
|
-
console.error('[MCP] Unhandled rejection at:', promise, 'reason:', reason)
|
|
516
|
-
gracefulShutdown('unhandledRejection')
|
|
517
|
-
})
|
|
517
|
+
console.error('[MCP] Unhandled rejection at:', promise, 'reason:', reason)
|
|
518
|
+
gracefulShutdown('unhandledRejection')
|
|
519
|
+
})
|
|
518
520
|
|
|
519
|
-
console.error('[MCP] AI Changelog Generator server running...')
|
|
520
|
-
|
|
521
|
-
// Keep process alive
|
|
522
|
-
return new Promise(() => {});
|
|
521
|
+
console.error('[MCP] AI Changelog Generator server running...')
|
|
523
522
|
|
|
523
|
+
// Keep process alive
|
|
524
|
+
return new Promise(() => {})
|
|
524
525
|
} catch (error) {
|
|
525
|
-
console.error('[MCP] Server failed to start:', error.message)
|
|
526
|
-
throw error
|
|
526
|
+
console.error('[MCP] Server failed to start:', error.message)
|
|
527
|
+
throw error
|
|
527
528
|
}
|
|
528
529
|
}
|
|
529
530
|
}
|
|
530
531
|
|
|
531
532
|
// Start server if called directly
|
|
532
533
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
533
|
-
const server = new AIChangelogMCPServer()
|
|
534
|
+
const server = new AIChangelogMCPServer()
|
|
534
535
|
server.run().catch((error) => {
|
|
535
|
-
console.error('MCP Server startup failed:', error)
|
|
536
|
-
process.exit(1)
|
|
537
|
-
})
|
|
536
|
+
console.error('MCP Server startup failed:', error)
|
|
537
|
+
process.exit(1)
|
|
538
|
+
})
|
|
538
539
|
}
|
|
539
540
|
|
|
540
|
-
export default AIChangelogMCPServer
|
|
541
|
+
export default AIChangelogMCPServer
|