claude-mpm 0.3.0__py3-none-any.whl

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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,703 @@
1
+ """
2
+ Semantic Versioning Manager - Version management logic for Version Control Agent.
3
+
4
+ This module provides comprehensive semantic versioning management including:
5
+ 1. Version parsing and validation
6
+ 2. Automatic version bumping based on changes
7
+ 3. Changelog generation and management
8
+ 4. Tag creation and management
9
+ 5. Version metadata handling
10
+ """
11
+
12
+ import re
13
+ import json
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+ from typing import Dict, List, Optional, Any, Tuple, Union
17
+ from dataclasses import dataclass, field
18
+ from enum import Enum
19
+ import logging
20
+
21
+ from ...utils.config_manager import ConfigurationManager
22
+
23
+
24
+ class VersionBumpType(Enum):
25
+ """Types of version bumps."""
26
+
27
+ MAJOR = "major"
28
+ MINOR = "minor"
29
+ PATCH = "patch"
30
+ PRERELEASE = "prerelease"
31
+
32
+
33
+ @dataclass
34
+ class SemanticVersion:
35
+ """Represents a semantic version."""
36
+
37
+ major: int
38
+ minor: int
39
+ patch: int
40
+ prerelease: Optional[str] = None
41
+ build: Optional[str] = None
42
+
43
+ def __str__(self) -> str:
44
+ """String representation of version."""
45
+ version = f"{self.major}.{self.minor}.{self.patch}"
46
+ if self.prerelease:
47
+ version += f"-{self.prerelease}"
48
+ if self.build:
49
+ version += f"+{self.build}"
50
+ return version
51
+
52
+ def __lt__(self, other: "SemanticVersion") -> bool:
53
+ """Compare versions for sorting."""
54
+ if self.major != other.major:
55
+ return self.major < other.major
56
+ if self.minor != other.minor:
57
+ return self.minor < other.minor
58
+ if self.patch != other.patch:
59
+ return self.patch < other.patch
60
+
61
+ # Handle prerelease comparison
62
+ if self.prerelease is None and other.prerelease is not None:
63
+ return False
64
+ if self.prerelease is not None and other.prerelease is None:
65
+ return True
66
+ if self.prerelease is not None and other.prerelease is not None:
67
+ return self.prerelease < other.prerelease
68
+
69
+ return False
70
+
71
+ def bump(self, bump_type: VersionBumpType) -> "SemanticVersion":
72
+ """Create a new version with the specified bump applied."""
73
+ if bump_type == VersionBumpType.MAJOR:
74
+ return SemanticVersion(self.major + 1, 0, 0)
75
+ elif bump_type == VersionBumpType.MINOR:
76
+ return SemanticVersion(self.major, self.minor + 1, 0)
77
+ elif bump_type == VersionBumpType.PATCH:
78
+ return SemanticVersion(self.major, self.minor, self.patch + 1)
79
+ elif bump_type == VersionBumpType.PRERELEASE:
80
+ if self.prerelease:
81
+ # Increment prerelease number
82
+ match = re.match(r"(.+?)(\d+)$", self.prerelease)
83
+ if match:
84
+ prefix, num = match.groups()
85
+ new_prerelease = f"{prefix}{int(num) + 1}"
86
+ else:
87
+ new_prerelease = f"{self.prerelease}.1"
88
+ else:
89
+ new_prerelease = "alpha.1"
90
+
91
+ return SemanticVersion(self.major, self.minor, self.patch, prerelease=new_prerelease)
92
+
93
+ return self
94
+
95
+
96
+ @dataclass
97
+ class VersionMetadata:
98
+ """Metadata associated with a version."""
99
+
100
+ version: SemanticVersion
101
+ release_date: datetime
102
+ commit_hash: Optional[str] = None
103
+ tag_name: Optional[str] = None
104
+ changes: List[str] = field(default_factory=list)
105
+ breaking_changes: List[str] = field(default_factory=list)
106
+ contributors: List[str] = field(default_factory=list)
107
+ notes: Optional[str] = None
108
+
109
+
110
+ @dataclass
111
+ class ChangeAnalysis:
112
+ """Analysis of changes for version bumping."""
113
+
114
+ has_breaking_changes: bool = False
115
+ has_new_features: bool = False
116
+ has_bug_fixes: bool = False
117
+ change_descriptions: List[str] = field(default_factory=list)
118
+ suggested_bump: VersionBumpType = VersionBumpType.PATCH
119
+ confidence: float = 0.0
120
+
121
+
122
+ class SemanticVersionManager:
123
+ """
124
+ Manages semantic versioning for the Version Control Agent.
125
+
126
+ Provides comprehensive version management including parsing, bumping,
127
+ changelog generation, and integration with Git tags.
128
+ """
129
+
130
+ def __init__(self, project_root: str, logger: logging.Logger):
131
+ """
132
+ Initialize Semantic Version Manager.
133
+
134
+ Args:
135
+ project_root: Root directory of the project
136
+ logger: Logger instance
137
+ """
138
+ self.project_root = Path(project_root)
139
+ self.logger = logger
140
+ self.config_mgr = ConfigurationManager(cache_enabled=True)
141
+
142
+ # Version file patterns
143
+ self.version_files = {
144
+ "package.json": self._parse_package_json_version,
145
+ "pyproject.toml": self._parse_pyproject_toml_version,
146
+ "Cargo.toml": self._parse_cargo_toml_version,
147
+ "VERSION": self._parse_version_file,
148
+ "version.txt": self._parse_version_file,
149
+ "pom.xml": self._parse_pom_xml_version,
150
+ }
151
+
152
+ # Change patterns for analysis
153
+ self.breaking_change_patterns = [
154
+ r"\bbreaking\b",
155
+ r"\bbreaking[-_]change\b",
156
+ r"\bremove\b.*\bapi\b",
157
+ r"\bdelete\b.*\bapi\b",
158
+ r"\bdrop\b.*\bsupport\b",
159
+ r"\bincompatible\b",
160
+ r"\bmajor\b.*\bchange\b",
161
+ ]
162
+
163
+ self.feature_patterns = [
164
+ r"\badd\b",
165
+ r"\bnew\b.*\bfeature\b",
166
+ r"\bimplement\b",
167
+ r"\benhance\b",
168
+ r"\bintroduce\b",
169
+ r"\bfeature\b.*\badd\b",
170
+ ]
171
+
172
+ self.bug_fix_patterns = [
173
+ r"\bfix\b",
174
+ r"\bbug\b.*\bfix\b",
175
+ r"\bresolve\b",
176
+ r"\bcorrect\b",
177
+ r"\bpatch\b",
178
+ r"\bhotfix\b",
179
+ ]
180
+
181
+ def parse_version(self, version_string: str) -> Optional[SemanticVersion]:
182
+ """
183
+ Parse a version string into a SemanticVersion object.
184
+
185
+ Args:
186
+ version_string: Version string to parse
187
+
188
+ Returns:
189
+ SemanticVersion object or None if parsing fails
190
+ """
191
+ try:
192
+ # Clean up version string
193
+ version_string = version_string.strip().lstrip("v")
194
+
195
+ # Regex pattern for semantic version
196
+ pattern = r"^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\-\.]+))?(?:\+([a-zA-Z0-9\-\.]+))?$"
197
+ match = re.match(pattern, version_string)
198
+
199
+ if match:
200
+ major, minor, patch, prerelease, build = match.groups()
201
+
202
+ return SemanticVersion(
203
+ major=int(major),
204
+ minor=int(minor),
205
+ patch=int(patch),
206
+ prerelease=prerelease,
207
+ build=build,
208
+ )
209
+
210
+ return None
211
+
212
+ except Exception as e:
213
+ self.logger.error(f"Error parsing version '{version_string}': {e}")
214
+ return None
215
+
216
+ def get_current_version(self) -> Optional[SemanticVersion]:
217
+ """
218
+ Get the current version from project files.
219
+
220
+ Returns:
221
+ Current SemanticVersion or None if not found
222
+ """
223
+ for filename, parser in self.version_files.items():
224
+ file_path = self.project_root / filename
225
+
226
+ if file_path.exists():
227
+ try:
228
+ version_string = parser(file_path)
229
+ if version_string:
230
+ version = self.parse_version(version_string)
231
+ if version:
232
+ self.logger.info(f"Found version {version} in {filename}")
233
+ return version
234
+ except Exception as e:
235
+ self.logger.error(f"Error parsing version from {filename}: {e}")
236
+ continue
237
+
238
+ self.logger.warning("No version found in project files")
239
+ return None
240
+
241
+ def _parse_package_json_version(self, file_path: Path) -> Optional[str]:
242
+ """Parse version from package.json."""
243
+ try:
244
+ data = self.config_mgr.load_json(file_path)
245
+ return data.get("version")
246
+ except Exception:
247
+ return None
248
+
249
+ def _parse_pyproject_toml_version(self, file_path: Path) -> Optional[str]:
250
+ """Parse version from pyproject.toml."""
251
+ try:
252
+ import tomllib
253
+ except ImportError:
254
+ try:
255
+ import tomli as tomllib
256
+ except ImportError:
257
+ # Fallback to simple regex parsing
258
+ return self._parse_toml_version_regex(file_path)
259
+
260
+ try:
261
+ with open(file_path, "rb") as f:
262
+ data = tomllib.load(f)
263
+
264
+ # Try different locations for version
265
+ if "project" in data and "version" in data["project"]:
266
+ return data["project"]["version"]
267
+ elif (
268
+ "tool" in data and "poetry" in data["tool"] and "version" in data["tool"]["poetry"]
269
+ ):
270
+ return data["tool"]["poetry"]["version"]
271
+
272
+ return None
273
+
274
+ except Exception:
275
+ return self._parse_toml_version_regex(file_path)
276
+
277
+ def _parse_toml_version_regex(self, file_path: Path) -> Optional[str]:
278
+ """Parse version from TOML file using regex."""
279
+ try:
280
+ with open(file_path, "r") as f:
281
+ content = f.read()
282
+
283
+ # Look for version = "x.y.z" pattern
284
+ patterns = [r'version\s*=\s*["\']([^"\']+)["\']', r'version:\s*["\']([^"\']+)["\']']
285
+
286
+ for pattern in patterns:
287
+ match = re.search(pattern, content)
288
+ if match:
289
+ return match.group(1)
290
+
291
+ return None
292
+
293
+ except Exception:
294
+ return None
295
+
296
+ def _parse_cargo_toml_version(self, file_path: Path) -> Optional[str]:
297
+ """Parse version from Cargo.toml."""
298
+ return self._parse_toml_version_regex(file_path)
299
+
300
+ def _parse_version_file(self, file_path: Path) -> Optional[str]:
301
+ """Parse version from simple version file."""
302
+ try:
303
+ with open(file_path, "r") as f:
304
+ return f.read().strip()
305
+ except Exception:
306
+ return None
307
+
308
+ def _parse_pom_xml_version(self, file_path: Path) -> Optional[str]:
309
+ """Parse version from Maven pom.xml."""
310
+ try:
311
+ with open(file_path, "r") as f:
312
+ content = f.read()
313
+
314
+ # Simple regex to find version in pom.xml
315
+ pattern = r"<version>([^<]+)</version>"
316
+ match = re.search(pattern, content)
317
+
318
+ if match:
319
+ return match.group(1)
320
+
321
+ return None
322
+
323
+ except Exception:
324
+ return None
325
+
326
+ def analyze_changes(self, changes: List[str]) -> ChangeAnalysis:
327
+ """
328
+ Analyze changes to suggest version bump type.
329
+
330
+ Args:
331
+ changes: List of change descriptions (e.g., commit messages)
332
+
333
+ Returns:
334
+ ChangeAnalysis with suggested version bump
335
+ """
336
+ analysis = ChangeAnalysis()
337
+ analysis.change_descriptions = changes
338
+
339
+ # Analyze each change
340
+ for change in changes:
341
+ change_lower = change.lower()
342
+
343
+ # Check for breaking changes
344
+ if any(re.search(pattern, change_lower) for pattern in self.breaking_change_patterns):
345
+ analysis.has_breaking_changes = True
346
+
347
+ # Check for new features
348
+ elif any(re.search(pattern, change_lower) for pattern in self.feature_patterns):
349
+ analysis.has_new_features = True
350
+
351
+ # Check for bug fixes
352
+ elif any(re.search(pattern, change_lower) for pattern in self.bug_fix_patterns):
353
+ analysis.has_bug_fixes = True
354
+
355
+ # Determine suggested bump
356
+ if analysis.has_breaking_changes:
357
+ analysis.suggested_bump = VersionBumpType.MAJOR
358
+ analysis.confidence = 0.9
359
+ elif analysis.has_new_features:
360
+ analysis.suggested_bump = VersionBumpType.MINOR
361
+ analysis.confidence = 0.8
362
+ elif analysis.has_bug_fixes:
363
+ analysis.suggested_bump = VersionBumpType.PATCH
364
+ analysis.confidence = 0.7
365
+ else:
366
+ analysis.suggested_bump = VersionBumpType.PATCH
367
+ analysis.confidence = 0.5
368
+
369
+ return analysis
370
+
371
+ def bump_version(
372
+ self, current_version: SemanticVersion, bump_type: VersionBumpType
373
+ ) -> SemanticVersion:
374
+ """
375
+ Bump version according to semantic versioning rules.
376
+
377
+ Args:
378
+ current_version: Current version
379
+ bump_type: Type of bump to apply
380
+
381
+ Returns:
382
+ New version
383
+ """
384
+ return current_version.bump(bump_type)
385
+
386
+ def suggest_version_bump(self, commit_messages: List[str]) -> Tuple[VersionBumpType, float]:
387
+ """
388
+ Suggest version bump based on commit messages.
389
+
390
+ Args:
391
+ commit_messages: List of commit messages since last version
392
+
393
+ Returns:
394
+ Tuple of (suggested_bump_type, confidence_score)
395
+ """
396
+ analysis = self.analyze_changes(commit_messages)
397
+ return analysis.suggested_bump, analysis.confidence
398
+
399
+ def update_version_files(
400
+ self, new_version: SemanticVersion, files_to_update: Optional[List[str]] = None
401
+ ) -> Dict[str, bool]:
402
+ """
403
+ Update version in project files.
404
+
405
+ Args:
406
+ new_version: New version to set
407
+ files_to_update: Specific files to update (defaults to all found)
408
+
409
+ Returns:
410
+ Dictionary mapping filenames to success status
411
+ """
412
+ results = {}
413
+ version_string = str(new_version)
414
+
415
+ files_to_check = files_to_update or list(self.version_files.keys())
416
+
417
+ for filename in files_to_check:
418
+ file_path = self.project_root / filename
419
+
420
+ if file_path.exists():
421
+ try:
422
+ success = self._update_version_file(file_path, version_string)
423
+ results[filename] = success
424
+
425
+ if success:
426
+ self.logger.info(f"Updated version to {version_string} in {filename}")
427
+ else:
428
+ self.logger.error(f"Failed to update version in {filename}")
429
+
430
+ except Exception as e:
431
+ self.logger.error(f"Error updating version in {filename}: {e}")
432
+ results[filename] = False
433
+
434
+ return results
435
+
436
+ def _update_version_file(self, file_path: Path, new_version: str) -> bool:
437
+ """Update version in a specific file."""
438
+ filename = file_path.name
439
+
440
+ try:
441
+ if filename == "package.json":
442
+ return self._update_package_json_version(file_path, new_version)
443
+ elif filename in ["pyproject.toml", "Cargo.toml"]:
444
+ return self._update_toml_version(file_path, new_version)
445
+ elif filename in ["VERSION", "version.txt"]:
446
+ return self._update_simple_version_file(file_path, new_version)
447
+ elif filename == "pom.xml":
448
+ return self._update_pom_xml_version(file_path, new_version)
449
+
450
+ return False
451
+
452
+ except Exception as e:
453
+ self.logger.error(f"Error updating {filename}: {e}")
454
+ return False
455
+
456
+ def _update_package_json_version(self, file_path: Path, new_version: str) -> bool:
457
+ """Update version in package.json."""
458
+ try:
459
+ data = self.config_mgr.load_json(file_path)
460
+ data["version"] = new_version
461
+
462
+ self.config_mgr.save_json(data, file_path)
463
+
464
+ return True
465
+
466
+ except Exception:
467
+ return False
468
+
469
+ def _update_toml_version(self, file_path: Path, new_version: str) -> bool:
470
+ """Update version in TOML file."""
471
+ try:
472
+ with open(file_path, "r") as f:
473
+ content = f.read()
474
+
475
+ # Replace version field
476
+ patterns = [
477
+ (r'(version\s*=\s*)["\']([^"\']+)["\']', rf'\g<1>"{new_version}"'),
478
+ (r'(version:\s*)["\']([^"\']+)["\']', rf'\g<1>"{new_version}"'),
479
+ ]
480
+
481
+ updated = False
482
+ for pattern, replacement in patterns:
483
+ new_content = re.sub(pattern, replacement, content)
484
+ if new_content != content:
485
+ content = new_content
486
+ updated = True
487
+ break
488
+
489
+ if updated:
490
+ with open(file_path, "w") as f:
491
+ f.write(content)
492
+ return True
493
+
494
+ return False
495
+
496
+ except Exception:
497
+ return False
498
+
499
+ def _update_simple_version_file(self, file_path: Path, new_version: str) -> bool:
500
+ """Update version in simple version file."""
501
+ try:
502
+ with open(file_path, "w") as f:
503
+ f.write(new_version + "\n")
504
+ return True
505
+ except Exception:
506
+ return False
507
+
508
+ def _update_pom_xml_version(self, file_path: Path, new_version: str) -> bool:
509
+ """Update version in Maven pom.xml."""
510
+ try:
511
+ with open(file_path, "r") as f:
512
+ content = f.read()
513
+
514
+ # Replace first version tag (project version)
515
+ pattern = r"(<version>)[^<]+(</version>)"
516
+ replacement = rf"\g<1>{new_version}\g<2>"
517
+
518
+ new_content = re.sub(pattern, replacement, content, count=1)
519
+
520
+ if new_content != content:
521
+ with open(file_path, "w") as f:
522
+ f.write(new_content)
523
+ return True
524
+
525
+ return False
526
+
527
+ except Exception:
528
+ return False
529
+
530
+ def generate_changelog_entry(
531
+ self,
532
+ version: SemanticVersion,
533
+ changes: List[str],
534
+ metadata: Optional[VersionMetadata] = None,
535
+ ) -> str:
536
+ """
537
+ Generate changelog entry for a version.
538
+
539
+ Args:
540
+ version: Version for the changelog entry
541
+ changes: List of changes
542
+ metadata: Optional version metadata
543
+
544
+ Returns:
545
+ Formatted changelog entry
546
+ """
547
+ date_str = datetime.now().strftime("%Y-%m-%d")
548
+ if metadata and metadata.release_date:
549
+ date_str = metadata.release_date.strftime("%Y-%m-%d")
550
+
551
+ # Build changelog entry
552
+ lines = [f"## [{version}] - {date_str}", ""]
553
+
554
+ # Categorize changes
555
+ breaking_changes = []
556
+ features = []
557
+ fixes = []
558
+ other_changes = []
559
+
560
+ for change in changes:
561
+ change_lower = change.lower()
562
+
563
+ if any(re.search(pattern, change_lower) for pattern in self.breaking_change_patterns):
564
+ breaking_changes.append(change)
565
+ elif any(re.search(pattern, change_lower) for pattern in self.feature_patterns):
566
+ features.append(change)
567
+ elif any(re.search(pattern, change_lower) for pattern in self.bug_fix_patterns):
568
+ fixes.append(change)
569
+ else:
570
+ other_changes.append(change)
571
+
572
+ # Add sections
573
+ if breaking_changes:
574
+ lines.extend(["### ⚠ BREAKING CHANGES", ""])
575
+ for change in breaking_changes:
576
+ lines.append(f"- {change}")
577
+ lines.append("")
578
+
579
+ if features:
580
+ lines.extend(["### ✨ Features", ""])
581
+ for change in features:
582
+ lines.append(f"- {change}")
583
+ lines.append("")
584
+
585
+ if fixes:
586
+ lines.extend(["### 🐛 Bug Fixes", ""])
587
+ for change in fixes:
588
+ lines.append(f"- {change}")
589
+ lines.append("")
590
+
591
+ if other_changes:
592
+ lines.extend(["### 📝 Other Changes", ""])
593
+ for change in other_changes:
594
+ lines.append(f"- {change}")
595
+ lines.append("")
596
+
597
+ # Add metadata
598
+ if metadata:
599
+ if metadata.commit_hash:
600
+ lines.append(f"**Commit:** {metadata.commit_hash}")
601
+ if metadata.contributors:
602
+ lines.append(f"**Contributors:** {', '.join(metadata.contributors)}")
603
+ if metadata.notes:
604
+ lines.extend(["", metadata.notes])
605
+
606
+ return "\n".join(lines)
607
+
608
+ def update_changelog(
609
+ self, version: SemanticVersion, changes: List[str], changelog_file: str = "docs/CHANGELOG.md"
610
+ ) -> bool:
611
+ """
612
+ Update CHANGELOG.md with new version entry.
613
+
614
+ Args:
615
+ version: Version for the changelog entry
616
+ changes: List of changes
617
+ changelog_file: Changelog file name
618
+
619
+ Returns:
620
+ True if update was successful
621
+ """
622
+ changelog_path = self.project_root / changelog_file
623
+
624
+ try:
625
+ # Generate new entry
626
+ new_entry = self.generate_changelog_entry(version, changes)
627
+
628
+ # Read existing changelog or create new one
629
+ if changelog_path.exists():
630
+ with open(changelog_path, "r") as f:
631
+ existing_content = f.read()
632
+
633
+ # Insert new entry after title
634
+ lines = existing_content.split("\n")
635
+ insert_index = 0
636
+
637
+ # Find insertion point (after # Changelog title)
638
+ for i, line in enumerate(lines):
639
+ if line.startswith("# ") or line.startswith("## [Unreleased]"):
640
+ insert_index = i + 1
641
+ break
642
+
643
+ # Insert new entry
644
+ lines.insert(insert_index, new_entry)
645
+ lines.insert(insert_index + 1, "")
646
+
647
+ content = "\n".join(lines)
648
+ else:
649
+ # Create new changelog
650
+ content = f"# Changelog\n\n{new_entry}\n"
651
+
652
+ # Write updated changelog
653
+ with open(changelog_path, "w") as f:
654
+ f.write(content)
655
+
656
+ self.logger.info(f"Updated {changelog_file} with version {version}")
657
+ return True
658
+
659
+ except Exception as e:
660
+ self.logger.error(f"Error updating changelog: {e}")
661
+ return False
662
+
663
+ def get_version_history(self) -> List[SemanticVersion]:
664
+ """
665
+ Get version history from changelog or Git tags.
666
+
667
+ Returns:
668
+ List of versions in descending order
669
+ """
670
+ versions = []
671
+
672
+ # Try to get versions from changelog
673
+ changelog_path = self.project_root / "docs" / "CHANGELOG.md"
674
+ if changelog_path.exists():
675
+ versions.extend(self._parse_changelog_versions(changelog_path))
676
+
677
+ # TODO: Add Git tag parsing if needed
678
+
679
+ # Sort versions in descending order
680
+ versions.sort(reverse=True)
681
+ return versions
682
+
683
+ def _parse_changelog_versions(self, changelog_path: Path) -> List[SemanticVersion]:
684
+ """Parse versions from changelog file."""
685
+ versions = []
686
+
687
+ try:
688
+ with open(changelog_path, "r") as f:
689
+ content = f.read()
690
+
691
+ # Find version entries
692
+ pattern = r"##\s*\[([^\]]+)\]"
693
+ matches = re.findall(pattern, content)
694
+
695
+ for match in matches:
696
+ version = self.parse_version(match)
697
+ if version:
698
+ versions.append(version)
699
+
700
+ except Exception as e:
701
+ self.logger.error(f"Error parsing changelog versions: {e}")
702
+
703
+ return versions
@@ -0,0 +1 @@
1
+ """Terminal UI components for claude-mpm."""