@entro314labs/ai-changelog-generator 3.3.0 → 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.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ### Next Release
11
11
  - TBD
12
12
 
13
+ ## [3.6.0] - 2025-11-27
14
+
15
+ ### Feat
16
+ - **MCPB Support**: Added full support for Model Context Protocol Bundle (MCPB) extension format.
17
+ - **Packaging**: Updated manifest and packaging for seamless MCP server integration.
18
+
19
+ ### Fix
20
+ - **Dry Run**: Fixed `dry-run` mode to correctly skip file writing while preserving output preview.
21
+ - **CLI**: Fixed interactive mode timeouts in non-TTY/CI environments.
22
+ - **MCP Server**: Fixed `ReferenceError` by using dynamic imports for `child_process` in ESM environment.
23
+ - **Validation**: Fixed `WorkspaceChangelogService` validation to correctly detect git repositories.
24
+ - **UI**: Fixed `EnhancedConsole.metrics` method shadowing in CLI UI utilities.
25
+ - **Tests**: Fixed integration test failures in styling and workspace services.
26
+
27
+ ### Test
28
+ - **Coverage**: Validated full test suite with 696 passing tests.
29
+ - **Mocking**: Fixed mocking strategies in styling integration tests.
30
+
31
+ ### Chore
32
+ - **Dependencies**: Updated project dependencies and type definitions.
33
+ - **Audit**: Verified project robustness and MCP tool integration.
34
+
35
+ ## [3.3.0] - 2025-10-19
36
+
37
+ ### Refactor
38
+ - **Architecture**: Major refactor of git management with new `GitManager` and `CommitTagger` domains.
39
+ - **Services**: Re-architected `AnalysisEngine`, `ChangelogService`, and `WorkspaceChangelogService` for better separation of concerns.
40
+ - **Config**: Modernized `biome.json` and `package.json` configurations.
41
+
42
+ ### Feat
43
+ - **Git Analysis**: Enhanced merge commit analysis and processing.
44
+ - **Configuration**: Added support for `merge` commit type in configuration.
45
+
46
+ ### Fix
47
+ - **Reliability**: Resolved intermittent test failures and improved test suite reliability.
48
+ - **Error Handling**: Added comprehensive error classes and improved exception handling.
49
+
50
+ ### Test
51
+ - **New Tests**: Added `missing-functionality.test.js` covering edge cases and new architectural components.
52
+
13
53
  ## [3.2.1] - 2025-08-10
14
54
 
15
55
  ### feat
@@ -22,7 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
62
  - add professional documentation and provider setup guides
23
63
 
24
64
  ### fix
25
- - fix AI provider initialization and isAvailable method calls
65
+ - fix AI provider initialization and isAvailable method calls
26
66
  - fix configuration manager null safety with proper error handling
27
67
  - fix CLI test timeouts and hanging commands
28
68
  - fix missing boolean getters (hasAI, gitExists) in main facade
@@ -31,7 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
31
71
  - fix Node.js version consistency across all configurations
32
72
  - fix Vitest reporter deprecation warnings
33
73
 
34
- ### test
74
+ ### test
35
75
  - add 658 comprehensive tests with 100% pass rate
36
76
  - add missing service method implementations for test compatibility
37
77
  - add resilient CLI end-to-end testing with proper timeouts
package/README.md CHANGED
@@ -133,7 +133,7 @@ ai-changelog --help
133
133
  # Generate changelog from recent commits
134
134
  ./ai-changelog.sh
135
135
 
136
- # Interactive mode with guided setup
136
+ # Interactive mode with guided setup
137
137
  ./ai-changelog.sh --interactive
138
138
 
139
139
  # Analyze working directory changes
@@ -351,9 +351,29 @@ ai-changelog health --detailed
351
351
  - **[AI Provider Setup](./docs/providers.md)** - Configure OpenAI, Claude, and other providers
352
352
  - **[Configuration Reference](./docs/configuration.md)** - All YAML and environment options
353
353
  - **[MCP Integration](./docs/mcp-integration.md)** - Claude Desktop and MCP server setup
354
+ - **[Desktop Extension (MCPB)](./MCPB_BUILD.md)** - Build and package as Claude Desktop Extension
354
355
  - **[API Reference](./docs/api-reference.md)** - All commands, options, and programmatic usage
355
356
  - **[Demo Media](./docs/media/)** - Interactive demos, GIFs, and video walkthroughs
356
357
 
358
+ ### Claude Desktop Extension
359
+
360
+ Install AI Changelog Generator in Claude Desktop with **one click** using the MCPB (MCP Bundle) format:
361
+
362
+ 1. Download the latest `.mcpb` file from [Releases](https://github.com/entro314-labs/AI-changelog-generator/releases)
363
+ 2. Open Claude Desktop → Settings → Extensions
364
+ 3. Drag and drop the `.mcpb` file
365
+ 4. Click "Install"
366
+
367
+ No configuration files, no terminal commands - just drag, drop, and start generating changelogs!
368
+
369
+ **For developers:** Learn how to build and package the extension in [MCPB_BUILD.md](./MCPB_BUILD.md)
370
+
371
+ ```bash
372
+ # Build Desktop Extension
373
+ pnpm mcpb:validate # Validate manifest
374
+ pnpm mcpb:pack # Create .mcpb file
375
+ ```
376
+
357
377
  ## Contributing
358
378
 
359
379
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -2,16 +2,19 @@
2
2
 
3
3
  /**
4
4
  * AI Changelog Generator - Claude Desktop Extension Entry Point
5
- * Optimized for DXT packaging and Claude Desktop integration
5
+ * Optimized for MCPB (MCP Bundle) packaging and Claude Desktop integration
6
+ *
7
+ * Note: This file is maintained for backward compatibility.
8
+ * The main MCP server entry point is bin/ai-changelog-mcp.js
6
9
  */
7
10
 
8
11
  import AIChangelogMCPServer from '../src/infrastructure/mcp/mcp-server.service.js'
9
12
  import { runMCPServer, setupProcessErrorHandlers } from '../src/shared/utils/cli-entry-utils.js'
10
13
 
11
- // Setup error handlers for DXT environment
14
+ // Setup error handlers for MCPB environment
12
15
  setupProcessErrorHandlers('AI Changelog Generator - Claude Desktop Extension')
13
16
 
14
- // DXT-specific initialization
17
+ // MCPB-specific initialization (legacy DXT compatibility)
15
18
  async function initializeDXTServer() {
16
19
  await runMCPServer(
17
20
  'AI Changelog Generator - Claude Desktop Extension',
package/manifest.json ADDED
@@ -0,0 +1,177 @@
1
+ {
2
+ "manifest_version": "0.3",
3
+ "name": "ai-changelog-generator",
4
+ "display_name": "AI Changelog Generator",
5
+ "version": "3.6.0",
6
+ "description": "AI-powered changelog generator with MCP server support - works with most providers, online and local models",
7
+ "long_description": "Generate intelligent changelogs from git commits using AI. Supports multiple providers including OpenAI, Claude, Gemini, Ollama, and LM Studio. Perfect for automating release documentation with smart commit analysis and categorization.\n\nFeatures:\n- Automatic changelog generation from git commits\n- Working directory change analysis\n- Multiple AI provider support (OpenAI, Azure, Claude, Gemini, Ollama, LM Studio)\n- Interactive and batch modes\n- Repository health analysis\n- Branch analysis and recommendations\n- Commit categorization and impact assessment",
8
+ "author": {
9
+ "name": "entro314-labs",
10
+ "email": "hey@entro314.com",
11
+ "url": "https://github.com/entro314-labs"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/entro314-labs/AI-changelog-generator.git"
16
+ },
17
+ "homepage": "https://github.com/entro314-labs/AI-changelog-generator",
18
+ "documentation": "https://github.com/entro314-labs/AI-changelog-generator#readme",
19
+ "support": "https://github.com/entro314-labs/AI-changelog-generator/issues",
20
+ "icon": "icon.png",
21
+ "server": {
22
+ "type": "node",
23
+ "entry_point": "bin/ai-changelog-mcp.js",
24
+ "mcp_config": {
25
+ "command": "node",
26
+ "args": ["${__dirname}/bin/ai-changelog-mcp.js"],
27
+ "env": {
28
+ "AI_PROVIDER": "${user_config.AI_PROVIDER}",
29
+ "OPENAI_API_KEY": "${user_config.OPENAI_API_KEY}",
30
+ "AZURE_OPENAI_KEY": "${user_config.AZURE_OPENAI_KEY}",
31
+ "AZURE_OPENAI_ENDPOINT": "${user_config.AZURE_OPENAI_ENDPOINT}",
32
+ "AZURE_OPENAI_DEPLOYMENT_NAME": "${user_config.AZURE_OPENAI_DEPLOYMENT_NAME}",
33
+ "ANTHROPIC_API_KEY": "${user_config.ANTHROPIC_API_KEY}",
34
+ "GOOGLE_API_KEY": "${user_config.GOOGLE_API_KEY}",
35
+ "HUGGINGFACE_API_KEY": "${user_config.HUGGINGFACE_API_KEY}",
36
+ "VERTEX_PROJECT_ID": "${user_config.VERTEX_PROJECT_ID}",
37
+ "OLLAMA_HOST": "${user_config.OLLAMA_HOST}",
38
+ "LMSTUDIO_API_BASE": "${user_config.LMSTUDIO_API_BASE}",
39
+ "DEFAULT_ANALYSIS_MODE": "${user_config.DEFAULT_ANALYSIS_MODE}"
40
+ }
41
+ }
42
+ },
43
+ "tools": [
44
+ {
45
+ "name": "generate_changelog",
46
+ "description": "Generate AI-powered changelog from git commits or working directory changes"
47
+ },
48
+ {
49
+ "name": "analyze_repository",
50
+ "description": "Comprehensive repository analysis including health, commits, and branches"
51
+ },
52
+ {
53
+ "name": "analyze_current_changes",
54
+ "description": "Analyze staged and unstaged changes in working directory with AI-powered insights"
55
+ },
56
+ {
57
+ "name": "configure_providers",
58
+ "description": "Manage AI providers - list, switch, test, and configure"
59
+ }
60
+ ],
61
+ "tools_generated": false,
62
+ "user_config": {
63
+ "AI_PROVIDER": {
64
+ "type": "string",
65
+ "title": "AI Provider",
66
+ "description": "Choose your preferred AI provider, choose from: azure, openai, google, huggingface, ollama, lmstudio",
67
+ "required": false,
68
+ "sensitive": false,
69
+ "default": "openai"
70
+ },
71
+ "OPENAI_API_KEY": {
72
+ "type": "string",
73
+ "title": "OpenAI API Key",
74
+ "description": "Your OpenAI API key for GPT models",
75
+ "required": false,
76
+ "sensitive": true
77
+ },
78
+ "AZURE_OPENAI_KEY": {
79
+ "type": "string",
80
+ "title": "Azure OpenAI API Key",
81
+ "description": "Your Azure OpenAI API key",
82
+ "required": false,
83
+ "sensitive": true
84
+ },
85
+ "ANTHROPIC_API_KEY": {
86
+ "type": "string",
87
+ "title": "Anthropic API Key",
88
+ "description": "Your Anthropic API key for Claude models",
89
+ "required": false,
90
+ "sensitive": true
91
+ },
92
+ "GOOGLE_API_KEY": {
93
+ "type": "string",
94
+ "title": "Google AI API Key",
95
+ "description": "Your Google AI API key for Gemini models",
96
+ "required": false,
97
+ "sensitive": true
98
+ },
99
+ "HUGGINGFACE_API_KEY": {
100
+ "type": "string",
101
+ "title": "Hugging Face API Key",
102
+ "description": "Your Hugging Face API key",
103
+ "required": false,
104
+ "sensitive": true
105
+ },
106
+ "DEFAULT_ANALYSIS_MODE": {
107
+ "type": "string",
108
+ "title": "Default Analysis Mode",
109
+ "description": "Default mode for changelog generation (standard, detailed, or enterprise)",
110
+ "required": false,
111
+ "sensitive": false,
112
+ "default": "standard"
113
+ },
114
+ "AZURE_OPENAI_ENDPOINT": {
115
+ "type": "string",
116
+ "title": "Azure OpenAI Endpoint",
117
+ "description": "Your Azure OpenAI endpoint URL (e.g., https://your-resource.openai.azure.com)",
118
+ "required": false,
119
+ "sensitive": false
120
+ },
121
+ "AZURE_OPENAI_DEPLOYMENT_NAME": {
122
+ "type": "string",
123
+ "title": "Azure Deployment Name",
124
+ "description": "Your Azure OpenAI deployment name (e.g., gpt-4o)",
125
+ "required": false,
126
+ "sensitive": false
127
+ },
128
+ "VERTEX_PROJECT_ID": {
129
+ "type": "string",
130
+ "title": "Google Cloud Project ID",
131
+ "description": "Your Google Cloud Project ID for Vertex AI",
132
+ "required": false,
133
+ "sensitive": false
134
+ },
135
+ "OLLAMA_HOST": {
136
+ "type": "string",
137
+ "title": "Ollama Host",
138
+ "description": "Your Ollama server URL",
139
+ "required": false,
140
+ "sensitive": false,
141
+ "default": "http://localhost:11434"
142
+ },
143
+ "LMSTUDIO_API_BASE": {
144
+ "type": "string",
145
+ "title": "LM Studio API Base",
146
+ "description": "Your LM Studio server URL",
147
+ "required": false,
148
+ "sensitive": false,
149
+ "default": "http://localhost:1234/v1"
150
+ }
151
+ },
152
+ "keywords": [
153
+ "changelog",
154
+ "git",
155
+ "ai",
156
+ "automation",
157
+ "mcp",
158
+ "openai",
159
+ "claude",
160
+ "anthropic",
161
+ "gemini",
162
+ "azure",
163
+ "ollama",
164
+ "lmstudio",
165
+ "devops",
166
+ "release-notes",
167
+ "commit-analysis"
168
+ ],
169
+ "license": "MIT",
170
+ "compatibility": {
171
+ "claude_desktop": ">=1.0.0",
172
+ "platforms": ["darwin", "win32", "linux"],
173
+ "runtimes": {
174
+ "node": ">=22.x"
175
+ }
176
+ }
177
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@entro314labs/ai-changelog-generator",
3
3
  "displayName": "AI Changelog Generator",
4
- "version": "3.3.0",
4
+ "version": "3.6.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",
@@ -14,6 +14,7 @@
14
14
  "bin/",
15
15
  "src/",
16
16
  "types/",
17
+ "manifest.json",
17
18
  "ai-changelog.sh",
18
19
  "ai-changelog-mcp.sh",
19
20
  "README.md",
@@ -21,38 +22,38 @@
21
22
  "CHANGELOG.md"
22
23
  ],
23
24
  "dependencies": {
24
- "@anthropic-ai/sdk": "^0.67.0",
25
- "@aws-sdk/client-bedrock-runtime": "^3.913.0",
25
+ "@anthropic-ai/sdk": "^0.71.0",
26
+ "@aws-sdk/client-bedrock-runtime": "^3.941.0",
26
27
  "@azure/identity": "^4.13.0",
27
28
  "@clack/prompts": "^0.11.0",
28
- "@google/genai": "^1.25.0",
29
+ "@google/genai": "^1.30.0",
29
30
  "@google/generative-ai": "^0.24.1",
30
- "@huggingface/hub": "^2.6.12",
31
- "@huggingface/inference": "^4.11.3",
31
+ "@huggingface/hub": "^2.7.1",
32
+ "@huggingface/inference": "^4.13.4",
32
33
  "@lmstudio/sdk": "^1.5.0",
33
- "@modelcontextprotocol/sdk": "^1.20.1",
34
- "@modelcontextprotocol/server-filesystem": "2025.8.21",
34
+ "@modelcontextprotocol/sdk": "^1.23.0",
35
+ "@modelcontextprotocol/server-filesystem": "2025.11.25",
35
36
  "boxen": "^8.0.1",
36
37
  "chalk": "^5.6.2",
37
38
  "cli-table3": "^0.6.5",
38
39
  "dotenv": "^17.2.3",
39
40
  "figures": "^6.1.0",
40
- "google-auth-library": "^10.4.1",
41
+ "google-auth-library": "^10.5.0",
41
42
  "gradient-string": "^3.0.0",
42
- "js-yaml": "^4.1.0",
43
- "ollama": "^0.6.0",
44
- "openai": "^6.5.0",
43
+ "js-yaml": "^4.1.1",
44
+ "ollama": "^0.6.3",
45
+ "openai": "^6.9.1",
45
46
  "ora": "^9.0.0",
46
47
  "yargs": "^18.0.0"
47
48
  },
48
49
  "devDependencies": {
49
- "@anthropic-ai/mcpb": "^1.1.1",
50
- "@types/node": "24.8.1",
51
- "@vitest/browser": "beta",
52
- "@vitest/coverage-v8": "beta",
53
- "@vitest/ui": "beta",
50
+ "@anthropic-ai/mcpb": "^2.0.1",
51
+ "@types/node": "24.10.1",
52
+ "@vitest/browser": "latest",
53
+ "@vitest/coverage-v8": "latest",
54
+ "@vitest/ui": "latest",
54
55
  "tempy": "^3.1.0",
55
- "vitest": "beta"
56
+ "vitest": "latest"
56
57
  },
57
58
  "keywords": [
58
59
  "changelog",
@@ -94,8 +95,8 @@
94
95
  "access": "public"
95
96
  },
96
97
  "engines": {
97
- "node": ">=22.20.0",
98
- "pnpm": ">=10.18.3"
98
+ "node": ">=22.21.1",
99
+ "pnpm": ">=10.23.0"
99
100
  },
100
101
  "funding": {
101
102
  "type": "github",
@@ -107,8 +108,8 @@
107
108
  }
108
109
  },
109
110
  "volta": {
110
- "node": "22.20.0",
111
- "pnpm": "10.18.3"
111
+ "node": "22.22.1",
112
+ "pnpm": "10.23.0"
112
113
  },
113
114
  "scripts": {
114
115
  "start": "node bin/ai-changelog.js",
@@ -146,14 +147,16 @@
146
147
  "test:legacy": "node test/test-runner.js comprehensive",
147
148
  "test:git": "node src/domains/git/git.service.js info",
148
149
  "setup": "node scripts/setup-azure-openai.js",
149
- "config:create": "node src/infrastructure/config/configuration.manager.js create-sample",
150
- "config:validate": "node src/infrastructure/config/configuration.manager.js validate",
151
- "config:test-models": "node src/infrastructure/config/configuration.manager.js model-test",
152
150
  "providers:test": "node test/test-providers.js",
153
151
  "providers:validate": "node test/test-provider-switching.js",
154
152
  "git:info": "node src/domains/git/git.service.js info",
155
153
  "git:commits": "node src/domains/git/git.service.js commits 10",
156
154
  "git:stats": "node src/domains/git/git.service.js stats 5",
157
- "postinstall": "node -e \"console.log('\\n🎉 AI Changelog Generator installed!\\n\\n⚡ Quick Start:\\n ai-changelog - Generate changelog\\n ai-changelog --detailed - Detailed analysis\\n ai-changelog --interactive - Interactive mode\\n ai-changelog-mcp - Start MCP server\\n\\n🤖 AI Models Supported:\\n • GPT-4.1 series (1M context, 75% cost reduction)\\n • o3/o4 reasoning models (Azure-only)\\n • Automatic model selection\\n\\n📖 Docs: https://github.com/entro314-labs/AI-changelog-generator\\n\\n© All trademarks belong to their respective owners.\\n')\""
155
+ "mcpb:init": "mcpb init .",
156
+ "mcpb:validate": "mcpb validate manifest.json",
157
+ "mcpb:pack": "mcpb pack . ai-changelog-generator.mcpb",
158
+ "mcpb:build": "pnpm mcpb:validate && pnpm mcpb:pack",
159
+ "mcpb:info": "mcpb info ai-changelog-generator.mcpb",
160
+ "postinstall": "node -e \"console.log('🎉 AI Changelog Generator installed!⚡ Quick Start: ai-changelog - Generate changelog ai-changelog --detailed - Detailed analysis ai-changelog --interactive - Interactive mode ai-changelog-mcp - Start MCP server🤖 AI Models Supported: • GPT-4.1 series (1M context, 75% cost reduction) • o3/o4 reasoning models (Azure-only) • Automatic model selection📖 Docs: https://github.com/entro314-labs/AI-changelog-generator© All trademarks belong to their respective owners.')\""
158
161
  }
159
162
  }
@@ -168,7 +168,7 @@ export class ChangelogOrchestrator {
168
168
  }
169
169
 
170
170
  // Generate changelog using the service
171
- const result = await this.changelogService.generateChangelog(version, since)
171
+ const result = await this.changelogService.generateChangelog(version, since, this.options)
172
172
 
173
173
  if (!result) {
174
174
  console.log(colors.warningMessage('No changelog generated'))
@@ -211,6 +211,12 @@ export class ChangelogOrchestrator {
211
211
  async runInteractive() {
212
212
  await this.ensureInitialized()
213
213
 
214
+ // Check for interactive environment
215
+ if (!process.stdin.isTTY && !process.env.CI) {
216
+ console.log(colors.warningMessage('Interactive mode requires a TTY terminal.'))
217
+ return { interactive: false, status: 'skipped' }
218
+ }
219
+
214
220
  const { runInteractiveMode } = await import('../../shared/utils/utils.js')
215
221
  const { confirm } = await this._getCachedImport('@clack/prompts')
216
222
 
package/src/cli.js CHANGED
@@ -1,5 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ /**
4
+ * DEPRECATED: Legacy CLI Entry Point
5
+ *
6
+ * This file is deprecated and maintained only for backward compatibility.
7
+ * Please use bin/ai-changelog.js which delegates to CLIController instead.
8
+ *
9
+ * @deprecated Use bin/ai-changelog.js with CLIController
10
+ */
11
+
12
+ console.warn('\n⚠️ WARNING: This CLI entry point is deprecated.')
13
+ console.warn('Please use the main CLI: ai-changelog (or bin/ai-changelog.js)\n')
14
+
3
15
  /**
4
16
  * CLI Entry Point for AI Changelog Generator
5
17
  * Provides command-line interface for changelog generation functionality
@@ -31,9 +31,10 @@ export class ChangelogService {
31
31
  * Generates a changelog from committed changes (git history)
32
32
  * @param {string} version - Version number for the release
33
33
  * @param {string} since - Generate changelog since this date/commit
34
+ * @param {Object} options - Additional options (dryRun, etc.)
34
35
  * @returns {Promise<string>} Complete changelog content
35
36
  */
36
- async generateChangelog(version = null, since = null) {
37
+ async generateChangelog(version = null, since = null, options = {}) {
37
38
  console.log(colors.processingMessage('🤖 Analyzing changes with AI...'))
38
39
 
39
40
  // Get committed changes
@@ -95,7 +96,12 @@ export class ChangelogService {
95
96
  // Write changelog to file using existing utility
96
97
  const filename = version && version !== 'latest' ? `CHANGELOG-${version}.md` : 'AI_CHANGELOG.md'
97
98
  const outputFile = path.join(process.cwd(), filename)
98
- handleUnifiedOutput(changelog, { format: 'markdown', outputFile, silent: false })
99
+ handleUnifiedOutput(changelog, {
100
+ format: 'markdown',
101
+ outputFile,
102
+ silent: false,
103
+ dryRun: options.dryRun,
104
+ })
99
105
 
100
106
  return {
101
107
  changelog,
@@ -1,9 +1,14 @@
1
1
  import colors from '../../shared/constants/colors.js'
2
+ import { getWorkingDirectoryChanges } from '../../shared/utils/utils.js'
2
3
  import { ChangelogService } from './changelog.service.js'
3
4
 
4
5
  /**
5
6
  * WorkspaceChangelogService extends ChangelogService with workspace-specific functionality
6
7
  * for analyzing uncommitted changes and workspace state
8
+ *
9
+ * @deprecated This class is being phased out. The workspace changelog logic has been
10
+ * consolidated into ChangelogService. This class is maintained for backward compatibility
11
+ * with existing tests but should not be used in new code.
7
12
  */
8
13
  export class WorkspaceChangelogService extends ChangelogService {
9
14
  constructor(gitService, aiAnalysisService, analysisEngine = null, configManager = null) {
@@ -24,16 +29,34 @@ export class WorkspaceChangelogService extends ChangelogService {
24
29
  console.log(colors.processingMessage('🔍 Analyzing workspace changes...'))
25
30
 
26
31
  try {
27
- // Get git status information
28
- const status = await this.gitService.getStatus()
32
+ // Get git status information using utility function
33
+ const changes = getWorkingDirectoryChanges()
34
+
35
+ // Categorize changes
36
+ const status = {
37
+ staged: [],
38
+ unstaged: [],
39
+ untracked: []
40
+ }
41
+
42
+ changes.forEach(change => {
43
+ const statusCode = change.status || '??'
44
+ if (statusCode.startsWith('??')) {
45
+ status.untracked.push(change.filePath)
46
+ } else if (statusCode[0] !== ' ' && statusCode[0] !== '?') {
47
+ status.staged.push(change.filePath)
48
+ } else {
49
+ status.unstaged.push(change.filePath)
50
+ }
51
+ })
29
52
 
30
53
  // Update workspace metrics
31
54
  this.workspaceMetrics.unstagedFiles = status.unstaged?.length || 0
32
55
  this.workspaceMetrics.stagedFiles = status.staged?.length || 0
33
56
  this.workspaceMetrics.untrackedFiles = status.untracked?.length || 0
34
57
 
35
- // Get detailed diff for staged/unstaged changes
36
- const diff = await this.gitService.getDiff()
58
+ // Get detailed diff for staged/unstaged changes (empty for now)
59
+ const diff = ''
37
60
 
38
61
  // Use analysis engine if available
39
62
  let analysis = null
@@ -124,14 +147,24 @@ export class WorkspaceChangelogService extends ChangelogService {
124
147
  async validateWorkspace() {
125
148
  try {
126
149
  // Check if we're in a git repository
127
- const isGitRepo = await this.gitService.isGitRepository()
150
+ let isGitRepo = false
151
+ try {
152
+ const { execSync } = await import('node:child_process')
153
+ execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' })
154
+ isGitRepo = true
155
+ } catch {
156
+ isGitRepo = false
157
+ }
158
+
128
159
  if (!isGitRepo) {
129
160
  console.error(colors.errorMessage('Not in a git repository'))
130
161
  return false
131
162
  }
132
163
 
164
+ const changes = getWorkingDirectoryChanges()
165
+
133
166
  // Check if workspace has changes
134
- if (!this.hasWorkspaceChanges()) {
167
+ if (!this.hasWorkspaceChanges() && changes.length === 0) {
135
168
  console.log(colors.infoMessage('No workspace changes detected'))
136
169
  return false
137
170
  }
@@ -201,14 +234,13 @@ export class WorkspaceChangelogService extends ChangelogService {
201
234
 
202
235
  for (const change of changes) {
203
236
  try {
204
- // Get detailed diff for the file
205
- const diff = await this.gitService.getFileDiff(change.file)
206
-
237
+ // For workspace changelog service, we don't have detailed diffs
238
+ // Just add basic enhancement
207
239
  enhancedChanges.push({
208
240
  ...change,
209
- diff,
210
- complexity: this.assessChangeComplexity(diff),
211
- impact: this.assessChangeImpact(change.file, diff),
241
+ diff: '',
242
+ complexity: this.assessChangeComplexity(''),
243
+ impact: this.assessChangeImpact(change.file, ''),
212
244
  })
213
245
  } catch (error) {
214
246
  console.warn(
@@ -5,15 +5,7 @@ import process from 'node:process'
5
5
  import yaml from 'js-yaml'
6
6
 
7
7
  import colors from '../../shared/constants/colors.js'
8
-
9
- // Model configurations - moved from lib/utils/model-config.js
10
- const MODEL_CONFIGS = {
11
- 'gpt-4': { maxTokens: 8192, cost: 0.03 },
12
- 'gpt-3.5-turbo': { maxTokens: 4096, cost: 0.002 },
13
- 'claude-3-opus': { maxTokens: 200000, cost: 0.015 },
14
- 'claude-3-sonnet': { maxTokens: 200000, cost: 0.003 },
15
- 'claude-3-haiku': { maxTokens: 200000, cost: 0.00025 },
16
- }
8
+ import { MODEL_CONFIGS } from '../providers/utils/model-config.js'
17
9
 
18
10
  /**
19
11
  * Unified Configuration Manager
@@ -9,6 +9,7 @@ import fs from 'node:fs'
9
9
  import path, { dirname } from 'node:path'
10
10
  import process from 'node:process'
11
11
  import { fileURLToPath } from 'node:url'
12
+ import { execSync } from 'node:child_process'
12
13
 
13
14
  import { Server } from '@modelcontextprotocol/sdk/server/index.js'
14
15
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
@@ -61,12 +62,17 @@ class AIChangelogMCPServer {
61
62
  // Set MCP server mode to suppress verbose logging
62
63
  process.env.MCP_SERVER_MODE = 'true'
63
64
 
65
+ // Initialize configuration
64
66
  this.configManager = new ConfigurationManager()
67
+
68
+ // Initialize application service
65
69
  this.applicationService = new ApplicationService()
70
+
71
+ // Initialize orchestrator with proper dependencies (it will create its own services)
66
72
  this.changelogOrchestrator = new ChangelogOrchestrator(this.configManager)
67
- this.gitAnalyzer = new GitService()
68
- this.analysisEngine = new AnalysisEngine()
69
- this.providerService = new ProviderManagerService(this.configManager.getConfig())
73
+
74
+ // Initialize provider service
75
+ this.providerService = new ProviderManagerService(this.configManager.getAll())
70
76
 
71
77
  // Log available configuration
72
78
  const hasProvider = process.env.AI_PROVIDER
@@ -80,6 +86,9 @@ class AIChangelogMCPServer {
80
86
  } else {
81
87
  console.error(`[MCP] Configured with provider: ${process.env.AI_PROVIDER}`)
82
88
  }
89
+
90
+ // Wait for orchestrator to initialize its services
91
+ this.initPromise = this.changelogOrchestrator.ensureInitialized()
83
92
  } catch (error) {
84
93
  console.error('[MCP] Failed to initialize services:', error.message)
85
94
  console.error('[MCP] Server will start but tools may require configuration')
@@ -296,27 +305,55 @@ class AIChangelogMCPServer {
296
305
  } = args
297
306
 
298
307
  try {
308
+ // Ensure services are initialized
309
+ if (this.initPromise) {
310
+ await this.initPromise
311
+ }
312
+
299
313
  let result
300
314
 
301
315
  if (source === 'working-dir' || (source === 'auto' && this.hasWorkingDirectoryChanges())) {
302
- // Generate from working directory changes
303
- result = await this.changelogOrchestrator.generateWorkspaceChangelog({
304
- version,
316
+ // Generate from working directory changes using ChangelogService
317
+ // Access the changelogService through the orchestrator
318
+ await this.changelogOrchestrator.ensureInitialized()
319
+ const changelogService = this.changelogOrchestrator.changelogService
320
+
321
+ result = await changelogService.generateWorkspaceChangelog(version, {
305
322
  analysisMode,
306
323
  includeAttribution,
307
324
  })
325
+
326
+ // Format result to match expected structure
327
+ if (result && result.changelog) {
328
+ result = {
329
+ content: result.changelog,
330
+ metadata: {
331
+ version,
332
+ source: 'working-directory',
333
+ filesProcessed: result.filesProcessed,
334
+ filesSkipped: result.filesSkipped,
335
+ }
336
+ }
337
+ }
308
338
  } else {
309
- // Generate from commits
310
- result = await this.changelogOrchestrator.generateChangelog({
311
- version,
312
- since,
313
- analysisMode,
314
- includeAttribution,
315
- })
339
+ // Generate from commits - use orchestrator's generateChangelog(version, since)
340
+ result = await this.changelogOrchestrator.generateChangelog(version, since)
341
+
342
+ // Wrap result if needed
343
+ if (result && typeof result === 'string') {
344
+ result = {
345
+ content: result,
346
+ metadata: {
347
+ version,
348
+ since,
349
+ source: 'commits'
350
+ }
351
+ }
352
+ }
316
353
  }
317
354
 
318
355
  // Write file if requested
319
- if (writeFile && result.content) {
356
+ if (writeFile && result && result.content) {
320
357
  const changelogPath = path.join(process.cwd(), 'AI_CHANGELOG.md')
321
358
  try {
322
359
  fs.writeFileSync(changelogPath, result.content, 'utf8')
@@ -330,10 +367,10 @@ class AIChangelogMCPServer {
330
367
  content: [
331
368
  {
332
369
  type: 'text',
333
- text: result.content || 'No changelog content generated',
370
+ text: result?.content || 'No changelog content generated',
334
371
  },
335
372
  ],
336
- metadata: result.metadata,
373
+ metadata: result?.metadata,
337
374
  }
338
375
  } catch (error) {
339
376
  throw new Error(`Changelog generation failed: ${error.message}`)
@@ -344,23 +381,30 @@ class AIChangelogMCPServer {
344
381
  const { analysisType = 'comprehensive', includeRecommendations = true, commitLimit = 50 } = args
345
382
 
346
383
  try {
384
+ // Ensure services are initialized
385
+ if (this.initPromise) {
386
+ await this.initPromise
387
+ }
388
+ await this.changelogOrchestrator.ensureInitialized()
389
+
347
390
  let result
348
391
 
349
392
  switch (analysisType) {
350
393
  case 'health':
351
- result = await this.gitAnalyzer.assessRepositoryHealth(includeRecommendations)
394
+ result = await this.changelogOrchestrator.gitService.assessRepositoryHealth(includeRecommendations)
352
395
  break
353
396
  case 'commits':
354
- result = await this.analysisEngine.analyzeRecentCommits(commitLimit)
397
+ result = await this.changelogOrchestrator.analysisEngine.analyzeRecentCommits(commitLimit)
355
398
  break
356
399
  case 'branches':
357
- result = await this.gitAnalyzer.analyzeBranches()
400
+ result = await this.changelogOrchestrator.gitService.analyzeBranches()
358
401
  break
359
402
  case 'working-dir':
360
- result = await this.analysisEngine.analyzeCurrentChanges()
403
+ result = await this.changelogOrchestrator.analysisEngine.analyzeCurrentChanges()
361
404
  break
362
405
  default:
363
- result = await this.gitAnalyzer.analyzeComprehensive(includeRecommendations)
406
+ // Comprehensive analysis
407
+ result = await this.changelogOrchestrator.analyzeRepository({ type: analysisType })
364
408
  break
365
409
  }
366
410
 
@@ -381,7 +425,13 @@ class AIChangelogMCPServer {
381
425
  const { includeAIAnalysis = true, includeAttribution = true } = args
382
426
 
383
427
  try {
384
- const result = await this.analysisEngine.analyzeCurrentChanges({
428
+ // Ensure services are initialized
429
+ if (this.initPromise) {
430
+ await this.initPromise
431
+ }
432
+ await this.changelogOrchestrator.ensureInitialized()
433
+
434
+ const result = await this.changelogOrchestrator.analysisEngine.analyzeCurrentChanges({
385
435
  includeAIAnalysis,
386
436
  includeAttribution,
387
437
  })
@@ -472,7 +522,6 @@ class AIChangelogMCPServer {
472
522
  hasWorkingDirectoryChanges() {
473
523
  try {
474
524
  // Simple check for working directory changes
475
- const { execSync } = require('node:child_process')
476
525
  const result = execSync('git status --porcelain', { encoding: 'utf8' })
477
526
  return result.trim().length > 0
478
527
  } catch (_error) {
@@ -223,8 +223,13 @@ export class EnhancedConsole {
223
223
  EnhancedConsole.log(message, 'ai')
224
224
  }
225
225
 
226
- static metrics(message) {
227
- EnhancedConsole.log(message, 'metrics')
226
+ static metrics(data, title = 'Metrics') {
227
+ if (typeof data === 'string') {
228
+ EnhancedConsole.log(data, 'metrics')
229
+ } else if (data && Object.keys(data).length > 0) {
230
+ EnhancedConsole.section(title)
231
+ console.log(colors.formatMetrics(data))
232
+ }
228
233
  }
229
234
 
230
235
  static section(title, content = '') {
@@ -252,13 +257,6 @@ export class EnhancedConsole {
252
257
  }
253
258
  }
254
259
 
255
- static metrics(metrics, title = 'Metrics') {
256
- if (metrics && Object.keys(metrics).length > 0) {
257
- EnhancedConsole.section(title)
258
- console.log(colors.formatMetrics(metrics))
259
- }
260
- }
261
-
262
260
  static divider(char = '─', length = 60) {
263
261
  console.log(colors.separator(char, length))
264
262
  }
@@ -1144,7 +1144,7 @@ ${enhancedMergeSummary}
1144
1144
  'ENHANCED MERGE SUMMARY:\n' + (enhancedMergeSummary || 'None') + '\n\n' +
1145
1145
  'FILES SECTION (first 3000 chars):\n' + filesSection.substring(0, 3000) + '\n\n' +
1146
1146
  'FULL PROMPT PREVIEW:\n' + prompt.substring(0, 2000) + '\n=== END DEBUG ==='
1147
-
1147
+
1148
1148
  fs.writeFileSync('AI_INPUT_DEBUG.txt', debugContent)
1149
1149
  console.log('*** AI INPUT SAVED TO AI_INPUT_DEBUG.txt ***')
1150
1150
  } catch (error) {
@@ -1439,6 +1439,15 @@ function extractCategoryFromText(content) {
1439
1439
  */
1440
1440
  /**
1441
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
1442
1451
  */
1443
1452
  export function getWorkingDirectoryChanges() {
1444
1453
  try {
@@ -1826,11 +1835,11 @@ function assessWorkspaceComplexity(categorizedChanges, totalFiles) {
1826
1835
  * Handle unified output for analysis commands
1827
1836
  */
1828
1837
  export function handleUnifiedOutput(data, config) {
1829
- const { format = 'markdown', outputFile, silent } = config
1838
+ const { format = 'markdown', outputFile, silent, dryRun } = config
1830
1839
  if (format === 'json') {
1831
1840
  const jsonOutput = safeJsonStringify(data)
1832
1841
 
1833
- if (outputFile) {
1842
+ if (outputFile && !dryRun) {
1834
1843
  // Write to file
1835
1844
  try {
1836
1845
  fs.writeFileSync(outputFile, jsonOutput, 'utf8')
@@ -1844,13 +1853,16 @@ export function handleUnifiedOutput(data, config) {
1844
1853
  }
1845
1854
  }
1846
1855
  } else if (!silent) {
1856
+ if (dryRun && outputFile) {
1857
+ console.log(colors.infoMessage(`[DRY RUN] Would save changelog to: ${colors.file(outputFile)}`))
1858
+ }
1847
1859
  console.log(jsonOutput)
1848
1860
  }
1849
1861
  } else {
1850
1862
  // Markdown format (default)
1851
1863
  const markdownOutput = typeof data === 'string' ? data : safeJsonStringify(data)
1852
1864
 
1853
- if (outputFile) {
1865
+ if (outputFile && !dryRun) {
1854
1866
  try {
1855
1867
  fs.writeFileSync(outputFile, markdownOutput, 'utf8')
1856
1868
  if (!silent) {
@@ -1863,6 +1875,9 @@ export function handleUnifiedOutput(data, config) {
1863
1875
  }
1864
1876
  }
1865
1877
  } else if (!silent) {
1878
+ if (dryRun && outputFile) {
1879
+ console.log(colors.infoMessage(`[DRY RUN] Would save changelog to: ${colors.file(outputFile)}`))
1880
+ }
1866
1881
  console.log(markdownOutput)
1867
1882
  }
1868
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