@entro314labs/ai-changelog-generator 3.2.1 → 3.6.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +42 -2
  2. package/README.md +21 -1
  3. package/ai-changelog-mcp.sh +0 -0
  4. package/ai-changelog.sh +0 -0
  5. package/bin/ai-changelog-dxt.js +6 -3
  6. package/manifest.json +177 -0
  7. package/package.json +76 -81
  8. package/src/ai-changelog-generator.js +5 -4
  9. package/src/application/orchestrators/changelog.orchestrator.js +19 -203
  10. package/src/cli.js +16 -5
  11. package/src/domains/ai/ai-analysis.service.js +2 -0
  12. package/src/domains/analysis/analysis.engine.js +714 -37
  13. package/src/domains/changelog/changelog.service.js +623 -32
  14. package/src/domains/changelog/workspace-changelog.service.js +445 -622
  15. package/src/domains/git/commit-tagger.js +552 -0
  16. package/src/domains/git/git-manager.js +357 -0
  17. package/src/domains/git/git.service.js +865 -16
  18. package/src/infrastructure/cli/cli.controller.js +14 -9
  19. package/src/infrastructure/config/configuration.manager.js +25 -11
  20. package/src/infrastructure/interactive/interactive-workflow.service.js +8 -1
  21. package/src/infrastructure/mcp/mcp-server.service.js +105 -32
  22. package/src/infrastructure/providers/core/base-provider.js +1 -1
  23. package/src/infrastructure/providers/implementations/anthropic.js +16 -173
  24. package/src/infrastructure/providers/implementations/azure.js +16 -63
  25. package/src/infrastructure/providers/implementations/dummy.js +13 -16
  26. package/src/infrastructure/providers/implementations/mock.js +13 -26
  27. package/src/infrastructure/providers/implementations/ollama.js +12 -4
  28. package/src/infrastructure/providers/implementations/openai.js +13 -165
  29. package/src/infrastructure/providers/provider-management.service.js +126 -412
  30. package/src/infrastructure/providers/utils/base-provider-helpers.js +11 -0
  31. package/src/shared/utils/cli-ui.js +8 -10
  32. package/src/shared/utils/diff-processor.js +21 -19
  33. package/src/shared/utils/error-classes.js +33 -0
  34. package/src/shared/utils/utils.js +83 -63
  35. package/types/index.d.ts +61 -68
  36. package/src/domains/git/git-repository.analyzer.js +0 -678
@@ -16,18 +16,18 @@ export class DiffProcessor {
16
16
 
17
17
  getDefaultMaxSize() {
18
18
  const sizes = {
19
- standard: 12000,
20
- detailed: 20000,
21
- enterprise: 30000,
19
+ standard: 50000, // Increased from 12000 - modern LLMs can handle more
20
+ detailed: 80000, // Increased from 20000
21
+ enterprise: 120000, // Increased from 30000
22
22
  }
23
23
  return sizes[this.analysisMode] || sizes.standard
24
24
  }
25
25
 
26
26
  getDefaultPriorityFiles() {
27
27
  const counts = {
28
- standard: 15,
29
- detailed: 25,
30
- enterprise: 40,
28
+ standard: 30, // Increased from 15 - analyze more files in detail
29
+ detailed: 50, // Increased from 25
30
+ enterprise: 80, // Increased from 40
31
31
  }
32
32
  return counts[this.analysisMode] || counts.standard
33
33
  }
@@ -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 = 1500, patterns = {}, isHighPriority = false } = options
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 {
@@ -119,17 +119,18 @@ export class DiffProcessor {
119
119
  processedDiff = this.applyDiffFiltering(processedDiff, file)
120
120
  }
121
121
 
122
- // Check if file matches bulk patterns
123
- const patternMatch = this.matchesBulkPattern(file, patterns)
124
- if (patternMatch) {
125
- return {
126
- ...file,
127
- diff: `[Bulk ${patternMatch.type}]: ${patternMatch.description}`,
128
- processed: true,
129
- compressionApplied: true,
130
- bulkPattern: patternMatch.type,
131
- }
132
- }
122
+ // Temporarily disable bulk pattern matching to preserve more detail
123
+ // TODO: Make this smarter - only apply to truly repetitive patterns
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
+ // }
133
134
 
134
135
  // Apply intelligent truncation if still too large
135
136
  if (processedDiff.length > budget) {
@@ -313,7 +314,8 @@ export class DiffProcessor {
313
314
  return trimmed.match(/^(import|require|from\s+['"])/)
314
315
  })
315
316
 
316
- if (importLines.length > 10) {
317
+ if (importLines.length > 25) {
318
+ // Increased threshold from 10 to 25
317
319
  // If many import changes, summarize them
318
320
  filteredDiff = filteredDiff.replace(/^[+-]\s*(import|require|from\s+['"]).*$/gm, '')
319
321
  filteredDiff = `[${importLines.length} import/require changes summarized]\n${filteredDiff}`
@@ -355,6 +355,39 @@ export class NetworkError extends AIChangelogError {
355
355
  }
356
356
  }
357
357
 
358
+ /**
359
+ * Parse error for malformed data/responses
360
+ */
361
+ export class ParseError extends Error {
362
+ constructor(message, data = null, originalError = null) {
363
+ super(message)
364
+ this.name = 'ParseError'
365
+ this.data = data
366
+ this.originalError = originalError
367
+
368
+ if (Error.captureStackTrace) {
369
+ Error.captureStackTrace(this, ParseError)
370
+ }
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Concurrency limit error for resource management
376
+ */
377
+ export class ConcurrencyLimitError extends Error {
378
+ constructor(message, limit, active, originalError = null) {
379
+ super(message)
380
+ this.name = 'ConcurrencyLimitError'
381
+ this.limit = limit
382
+ this.active = active
383
+ this.originalError = originalError
384
+
385
+ if (Error.captureStackTrace) {
386
+ Error.captureStackTrace(this, ConcurrencyLimitError)
387
+ }
388
+ }
389
+ }
390
+
358
391
  /**
359
392
  * Error context builder utility
360
393
  */
@@ -17,57 +17,12 @@ import fs from 'node:fs'
17
17
 
18
18
  import colors from '../constants/colors.js'
19
19
  import { DiffProcessor } from './diff-processor.js'
20
+ import { AbstractMethodError, AIChangelogError, ProviderError } from './error-classes.js'
20
21
  import JsonUtils from './json-utils.js'
21
22
 
22
- // ========================================
23
- // ERROR HANDLING UTILITIES
24
- // ========================================
25
-
26
- /**
27
- * Base error class for domain-specific errors
28
- */
29
- export class AIChangelogError extends Error {
30
- constructor(message, type, context = {}, originalError = null) {
31
- super(message)
32
- this.name = this.constructor.name
33
- this.type = type
34
- this.context = context
35
- this.originalError = originalError
36
- this.timestamp = new Date().toISOString()
37
- }
38
-
39
- toJSON() {
40
- return {
41
- name: this.name,
42
- message: this.message,
43
- type: this.type,
44
- context: this.context,
45
- timestamp: this.timestamp,
46
- stack: this.stack,
47
- }
48
- }
49
- }
50
-
51
- /**
52
- * Abstract method error for base classes
53
- */
54
- export class AbstractMethodError extends AIChangelogError {
55
- constructor(message, className, methodName, context = {}) {
56
- super(message, 'abstract', { className, methodName, ...context })
57
- this.className = className
58
- this.methodName = methodName
59
- }
60
- }
61
-
62
- /**
63
- * Provider-specific error
64
- */
65
- export class ProviderError extends AIChangelogError {
66
- constructor(message, providerName, context = {}, originalError = null) {
67
- super(message, 'provider', { providerName, ...context }, originalError)
68
- this.providerName = providerName
69
- }
70
- }
23
+ // Note: Error classes have been moved to './error-classes.js' to avoid duplication
24
+ // and provide better organization. Import them from there instead of defining here.
25
+ export { AbstractMethodError, AIChangelogError, ProviderError }
71
26
 
72
27
  // ========================================
73
28
  // DATA MANIPULATION UTILITIES
@@ -1110,9 +1065,22 @@ export function buildEnhancedPrompt(commitAnalysis, analysisMode = 'standard') {
1110
1065
  riskAssessment = {},
1111
1066
  } = commitAnalysis
1112
1067
 
1113
- // Initialize DiffProcessor with analysis mode
1068
+ // Detect merge commits and upgrade analysis mode for better specificity
1069
+ const isMergeCommit = subject && subject.toLowerCase().includes('merge')
1070
+ const effectiveAnalysisMode = isMergeCommit && files.length > 10 ? 'detailed' : analysisMode
1071
+
1072
+ // Debug logging for merge commits (disabled)
1073
+ // if (isMergeCommit) {
1074
+ // console.log('\n=== MERGE COMMIT DEBUG (EARLY) ===')
1075
+ // console.log('Subject:', subject)
1076
+ // console.log('Files count:', files.length)
1077
+ // console.log('Effective analysis mode:', effectiveAnalysisMode)
1078
+ // console.log('=== END EARLY DEBUG ===\n')
1079
+ // }
1080
+
1081
+ // Initialize DiffProcessor with effective analysis mode
1114
1082
  const diffProcessor = new DiffProcessor({
1115
- analysisMode,
1083
+ analysisMode: effectiveAnalysisMode,
1116
1084
  enableFiltering: true,
1117
1085
  enablePatternDetection: true,
1118
1086
  })
@@ -1148,10 +1116,41 @@ export function buildEnhancedPrompt(commitAnalysis, analysisMode = 'standard') {
1148
1116
  })
1149
1117
  .join('')
1150
1118
 
1119
+ // Check if we have enhanced merge summary from GitService (isMergeCommit already detected above)
1120
+ const enhancedMergeSummary =
1121
+ files.length > 0 && files[0].enhancedMergeSummary ? files[0].enhancedMergeSummary : null
1122
+
1151
1123
  const prompt = `Analyze this git commit for changelog generation.
1152
1124
 
1153
- **COMMIT:** ${subject}
1154
- **FILES:** ${files.length} files (${filesProcessed} analyzed, ${filesSkipped} summarized), ${insertions + deletions} lines changed${patternSummary}
1125
+ **COMMIT:** ${subject}${isMergeCommit ? ' (MERGE COMMIT - categorize as "merge")' : ''}
1126
+ **FILES:** ${files.length} files (${filesProcessed} analyzed, ${filesSkipped} summarized), ${insertions + deletions} lines changed${patternSummary}${
1127
+ enhancedMergeSummary
1128
+ ? `
1129
+
1130
+ **ENHANCED MERGE SUMMARY:**
1131
+ ${enhancedMergeSummary}
1132
+
1133
+ **IMPORTANT FOR MERGE COMMITS:** Use the above enhanced summary as your description. Do NOT create a generic summary. Instead, convert the bullet points above into a flowing description that includes the specific file names, numbers, and technical details provided.`
1134
+ : ''
1135
+ }
1136
+
1137
+ // Debug logging for merge commits with "pull request"
1138
+ if (isMergeCommit && subject.includes('pull request')) {
1139
+ try {
1140
+ const fs = require('fs')
1141
+ const debugContent = '=== EXACT AI INPUT FOR fd97ab7 ===\n' +
1142
+ 'SUBJECT: ' + subject + '\n' +
1143
+ 'FILES COUNT: ' + files.length + '\n' +
1144
+ 'ENHANCED MERGE SUMMARY:\n' + (enhancedMergeSummary || 'None') + '\n\n' +
1145
+ 'FILES SECTION (first 3000 chars):\n' + filesSection.substring(0, 3000) + '\n\n' +
1146
+ 'FULL PROMPT PREVIEW:\n' + prompt.substring(0, 2000) + '\n=== END DEBUG ==='
1147
+
1148
+ fs.writeFileSync('AI_INPUT_DEBUG.txt', debugContent)
1149
+ console.log('*** AI INPUT SAVED TO AI_INPUT_DEBUG.txt ***')
1150
+ } catch (error) {
1151
+ console.log('DEBUG ERROR:', error.message)
1152
+ }
1153
+ }
1155
1154
 
1156
1155
  **TARGET AUDIENCE:** End users and project stakeholders who need to understand what changed and why it matters.
1157
1156
 
@@ -1161,30 +1160,36 @@ export function buildEnhancedPrompt(commitAnalysis, analysisMode = 'standard') {
1161
1160
  1. **First, categorize correctly** using the rules below
1162
1161
  2. **Then, focus on user impact** - what can they do now that they couldn't before?
1163
1162
  3. **Keep technical details minimal** - only what's necessary for understanding
1163
+ 4. **Be definitive and factual** - never use uncertain language like "likely", "probably", "appears to", "seems to", or "possibly"
1164
+ 5. **Base analysis on actual code changes** - only describe what you can verify from the diff content
1165
+ 6. **For merge commits** - ALWAYS categorize as "merge" regardless of content
1166
+ 7. **Use enhanced merge summary** - If an "ENHANCED MERGE SUMMARY" section is provided above, DO NOT analyze individual file diffs. Instead, directly incorporate the specific bullet points from the enhanced summary into your response. Use the exact technical details, file names, and numbers provided in the enhanced summary.
1164
1167
 
1165
1168
  **PROCESSED DIFFS:**${filesSection}
1166
1169
 
1167
1170
  **CATEGORIZATION RULES (STRICTLY ENFORCED):**
1171
+ - **merge**: Any commit with "Merge" in the subject line (branch merges, pull request merges)
1168
1172
  - **fix**: ONLY actual bug fixes - broken functionality now works correctly
1169
- - **feature**: New capabilities, tools, or major functionality additions
1173
+ - **feature**: New capabilities, tools, or major functionality additions (NOT merges)
1170
1174
  - **refactor**: Code restructuring without changing what users can do
1171
1175
  - **perf**: Performance improvements users will notice
1172
1176
  - **docs**: Documentation updates only
1173
1177
  - **build/chore**: Build system, dependencies, maintenance
1174
1178
 
1175
1179
  **CRITICAL VALIDATION:**
1176
- - Large additions (>10 files OR >1000 lines) = "feature" or "refactor", NEVER "fix"
1177
- - New modules/classes/tools = "feature"
1180
+ - Commits with "Merge" in subject = "merge" category ALWAYS
1181
+ - Large additions (>10 files OR >1000 lines) = "feature" or "refactor", NEVER "fix" (unless merge)
1182
+ - New modules/classes/tools = "feature" (unless merge)
1178
1183
  - Only actual bug repairs = "fix"
1179
1184
 
1180
- Provide a JSON response with ONLY these fields:
1185
+ Provide a JSON response with ONLY these fields (use definitive language - NO "likely", "probably", "appears", etc.):
1181
1186
  {
1182
1187
  "summary": "${subject}",
1183
1188
  "impact": "critical|high|medium|low|minimal",
1184
1189
  "category": "feature|fix|security|breaking|docs|style|refactor|perf|test|chore",
1185
- "description": "One clear sentence describing what users can now do (for features) or what now works correctly (for fixes)",
1186
- "technicalDetails": "Maximum 2 sentences about key technical changes - focus on the most important functions/modules/APIs added or modified",
1187
- "businessValue": "Brief user benefit in 1 sentence",
1190
+ "description": "One clear, factual sentence describing what users can now do (for features) or what now works correctly (for fixes)",
1191
+ "technicalDetails": "Maximum 2 factual sentences about key technical changes - focus on the most important functions/modules/APIs added or modified",
1192
+ "businessValue": "Brief, definitive user benefit in 1 sentence",
1188
1193
  "riskFactors": ["minimal", "list"],
1189
1194
  "recommendations": ["minimal", "list"],
1190
1195
  "breakingChanges": false,
@@ -1434,6 +1439,15 @@ function extractCategoryFromText(content) {
1434
1439
  */
1435
1440
  /**
1436
1441
  * Get raw working directory changes from git status
1442
+ *
1443
+ * Note: This is a lightweight utility function that shells out to git directly
1444
+ * for simple status checks. For comprehensive git operations, services should
1445
+ * use GitService/GitManager, but this utility is kept for:
1446
+ * - Performance (avoids service initialization overhead)
1447
+ * - Simplicity (standalone function for basic status checks)
1448
+ * - Backward compatibility (widely used across codebase)
1449
+ *
1450
+ * @returns {Array} Array of change objects with status and filePath
1437
1451
  */
1438
1452
  export function getWorkingDirectoryChanges() {
1439
1453
  try {
@@ -1821,11 +1835,11 @@ function assessWorkspaceComplexity(categorizedChanges, totalFiles) {
1821
1835
  * Handle unified output for analysis commands
1822
1836
  */
1823
1837
  export function handleUnifiedOutput(data, config) {
1824
- const { format = 'markdown', outputFile, silent } = config
1838
+ const { format = 'markdown', outputFile, silent, dryRun } = config
1825
1839
  if (format === 'json') {
1826
1840
  const jsonOutput = safeJsonStringify(data)
1827
1841
 
1828
- if (outputFile) {
1842
+ if (outputFile && !dryRun) {
1829
1843
  // Write to file
1830
1844
  try {
1831
1845
  fs.writeFileSync(outputFile, jsonOutput, 'utf8')
@@ -1839,13 +1853,16 @@ export function handleUnifiedOutput(data, config) {
1839
1853
  }
1840
1854
  }
1841
1855
  } else if (!silent) {
1856
+ if (dryRun && outputFile) {
1857
+ console.log(colors.infoMessage(`[DRY RUN] Would save changelog to: ${colors.file(outputFile)}`))
1858
+ }
1842
1859
  console.log(jsonOutput)
1843
1860
  }
1844
1861
  } else {
1845
1862
  // Markdown format (default)
1846
1863
  const markdownOutput = typeof data === 'string' ? data : safeJsonStringify(data)
1847
1864
 
1848
- if (outputFile) {
1865
+ if (outputFile && !dryRun) {
1849
1866
  try {
1850
1867
  fs.writeFileSync(outputFile, markdownOutput, 'utf8')
1851
1868
  if (!silent) {
@@ -1858,6 +1875,9 @@ export function handleUnifiedOutput(data, config) {
1858
1875
  }
1859
1876
  }
1860
1877
  } else if (!silent) {
1878
+ if (dryRun && outputFile) {
1879
+ console.log(colors.infoMessage(`[DRY RUN] Would save changelog to: ${colors.file(outputFile)}`))
1880
+ }
1861
1881
  console.log(markdownOutput)
1862
1882
  }
1863
1883
  }
package/types/index.d.ts CHANGED
@@ -310,51 +310,42 @@ export class AIChangelogGenerator {
310
310
  constructor(constructorOptions?: {
311
311
  repositoryPath?: string
312
312
  configPath?: string
313
+ analysisMode?: AnalysisMode
314
+ modelOverride?: AIModel
315
+ dryRun?: boolean
316
+ silent?: boolean
313
317
  })
314
318
 
315
- // Core methods
316
- run(): Promise<void>
317
- generateChangelog(changelogOptions?: ChangelogOptions): Promise<string>
318
- analyzeCommits(commitOptions?: {
319
- since?: string
320
- limit?: number
321
- repositoryPath?: string
322
- }): Promise<CommitAnalysis>
319
+ // Core methods (these actually exist in the implementation)
320
+ generateChangelog(version?: string | null, since?: string | null): Promise<string>
323
321
 
324
- // Enhanced analysis methods
325
- analyzeCurrentChanges(analysisOptions?: {
326
- includeAIAnalysis?: boolean
327
- repositoryPath?: string
328
- }): Promise<CurrentChangesAnalysis>
322
+ // Analysis methods
323
+ analyzeRepository(config?: { type?: string }): Promise<any>
324
+ analyzeCurrentChanges(): Promise<CurrentChangesAnalysis>
325
+ analyzeRecentCommits(limit?: number): Promise<CommitAnalysis>
326
+ analyzeBranches(config?: any): Promise<BranchAnalysis>
327
+ analyzeComprehensive(config?: any): Promise<RepositoryHealthCheck>
329
328
 
330
- analyzeBranches(branchOptions?: {
331
- includeAllBranches?: boolean
332
- repositoryPath?: string
333
- }): Promise<BranchAnalysis>
329
+ // Repository health
330
+ assessRepositoryHealth(includeRecommendations?: boolean): Promise<any>
334
331
 
335
- analyzeRepository(repoOptions?: { repositoryPath?: string }): Promise<RepositoryHealthCheck>
332
+ // Interactive mode
333
+ runInteractive(): Promise<void>
336
334
 
337
- // Configuration and validation
338
- validateConfig(): Promise<{
339
- success: boolean
340
- provider?: AIProvider
341
- model?: AIModel
342
- capabilities?: ModelCapabilities
343
- error?: string
344
- }>
335
+ // Configuration
336
+ setAnalysisMode(mode: AnalysisMode): void
337
+ setModelOverride(model: AIModel): void
345
338
 
346
- validateModels(): Promise<{
347
- success: boolean
348
- models?: Array<{
349
- name: AIModel
350
- available: boolean
351
- capabilities?: ModelCapabilities
352
- }>
353
- error?: string
354
- }>
339
+ // Validation
340
+ validateConfiguration(): Promise<void>
355
341
 
356
- // Git operations
357
- getGitInfo(gitOptions?: { includeStats?: boolean; repositoryPath?: string }): Promise<GitInfo>
342
+ // Metrics
343
+ getMetrics(): {
344
+ startTime: number
345
+ commitsProcessed: number
346
+ apiCalls: number
347
+ errors: number
348
+ }
358
349
  }
359
350
 
360
351
  export class AIChangelogMCPServer {
@@ -362,53 +353,55 @@ export class AIChangelogMCPServer {
362
353
 
363
354
  run(): Promise<void>
364
355
 
365
- // MCP Tools
356
+ // MCP Tools (these match the actual implementation)
366
357
  generateChangelog(params: {
367
358
  repositoryPath?: string
359
+ source?: 'commits' | 'working-dir' | 'auto'
368
360
  analysisMode?: AnalysisMode
369
- outputFormat?: OutputFormat
370
361
  since?: string
371
362
  version?: string
372
- includeUnreleased?: boolean
373
- model?: AIModel
374
- }): Promise<string>
363
+ includeAttribution?: boolean
364
+ writeFile?: boolean
365
+ }): Promise<{
366
+ content: Array<{
367
+ type: 'text'
368
+ text: string
369
+ }>
370
+ metadata?: any
371
+ }>
375
372
 
376
- analyzeCommits(params: {
373
+ analyzeRepository(params: {
377
374
  repositoryPath?: string
378
- limit?: number
379
- since?: string
380
- }): Promise<CommitAnalysis>
375
+ analysisType?: 'health' | 'commits' | 'branches' | 'working-dir' | 'comprehensive'
376
+ includeRecommendations?: boolean
377
+ commitLimit?: number
378
+ }): Promise<{
379
+ content: Array<{
380
+ type: 'text'
381
+ text: string
382
+ }>
383
+ }>
381
384
 
382
385
  analyzeCurrentChanges(params: {
383
386
  repositoryPath?: string
384
387
  includeAIAnalysis?: boolean
385
- }): Promise<CurrentChangesAnalysis>
386
-
387
- getGitInfo(params: { repositoryPath?: string; includeStats?: boolean }): Promise<GitInfo>
388
-
389
- configureAIProvider(params: {
390
- provider?: AIProvider
391
- showModels?: boolean
392
- testConnection?: boolean
388
+ includeAttribution?: boolean
393
389
  }): Promise<{
394
- success: boolean
395
- provider?: AIProvider
396
- models?: AIModel[]
397
- error?: string
390
+ content: Array<{
391
+ type: 'text'
392
+ text: string
393
+ }>
398
394
  }>
399
395
 
400
- validateModels(params: {
401
- provider?: AIProvider
402
- checkCapabilities?: boolean
403
- testModels?: boolean
396
+ configureProviders(params: {
397
+ action?: 'list' | 'switch' | 'test' | 'configure' | 'validate'
398
+ provider?: string
399
+ testConnection?: boolean
404
400
  }): Promise<{
405
- success: boolean
406
- models?: Array<{
407
- name: AIModel
408
- available: boolean
409
- capabilities?: ModelCapabilities
401
+ content: Array<{
402
+ type: 'text'
403
+ text: string
410
404
  }>
411
- error?: string
412
405
  }>
413
406
  }
414
407