claude-mpm 3.8.1__py3-none-any.whl → 3.9.2__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.
Files changed (33) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +59 -135
  3. claude_mpm/agents/MEMORY.md +39 -30
  4. claude_mpm/agents/WORKFLOW.md +54 -4
  5. claude_mpm/agents/agents_metadata.py +25 -1
  6. claude_mpm/agents/schema/agent_schema.json +1 -1
  7. claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +88 -0
  8. claude_mpm/agents/templates/project_organizer.json +178 -0
  9. claude_mpm/agents/templates/research.json +33 -30
  10. claude_mpm/agents/templates/ticketing.json +3 -3
  11. claude_mpm/cli/commands/agents.py +8 -3
  12. claude_mpm/core/claude_runner.py +31 -10
  13. claude_mpm/core/config.py +2 -2
  14. claude_mpm/core/container.py +96 -25
  15. claude_mpm/core/framework_loader.py +43 -1
  16. claude_mpm/core/interactive_session.py +47 -0
  17. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +454 -0
  18. claude_mpm/services/agents/deployment/agent_deployment.py +144 -43
  19. claude_mpm/services/agents/memory/agent_memory_manager.py +4 -3
  20. claude_mpm/services/framework_claude_md_generator/__init__.py +10 -3
  21. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +14 -11
  22. claude_mpm/services/response_tracker.py +3 -5
  23. claude_mpm/services/ticket_manager.py +2 -2
  24. claude_mpm/services/ticket_manager_di.py +1 -1
  25. claude_mpm/services/version_control/semantic_versioning.py +80 -7
  26. claude_mpm/services/version_control/version_parser.py +528 -0
  27. claude_mpm-3.9.2.dist-info/METADATA +200 -0
  28. {claude_mpm-3.8.1.dist-info → claude_mpm-3.9.2.dist-info}/RECORD +32 -28
  29. claude_mpm-3.8.1.dist-info/METADATA +0 -327
  30. {claude_mpm-3.8.1.dist-info → claude_mpm-3.9.2.dist-info}/WHEEL +0 -0
  31. {claude_mpm-3.8.1.dist-info → claude_mpm-3.9.2.dist-info}/entry_points.txt +0 -0
  32. {claude_mpm-3.8.1.dist-info → claude_mpm-3.9.2.dist-info}/licenses/LICENSE +0 -0
  33. {claude_mpm-3.8.1.dist-info → claude_mpm-3.9.2.dist-info}/top_level.txt +0 -0
@@ -110,12 +110,13 @@ class AgentDeploymentService(AgentDeploymentInterface):
110
110
  }
111
111
 
112
112
  # Determine the actual working directory
113
- # Priority: working_directory param > CLAUDE_MPM_USER_PWD env var > current directory
113
+ # For deployment, we need to track the working directory but NOT use it
114
+ # for determining where system agents go (they always go to ~/.claude/agents/)
115
+ # Priority: working_directory param > current directory
114
116
  if working_directory:
115
117
  self.working_directory = Path(working_directory)
116
- elif 'CLAUDE_MPM_USER_PWD' in os.environ:
117
- self.working_directory = Path(os.environ['CLAUDE_MPM_USER_PWD'])
118
118
  else:
119
+ # Use current directory but don't let CLAUDE_MPM_USER_PWD affect system agent deployment
119
120
  self.working_directory = Path.cwd()
120
121
 
121
122
  self.logger.info(f"Working directory for deployment: {self.working_directory}")
@@ -139,7 +140,7 @@ class AgentDeploymentService(AgentDeploymentInterface):
139
140
  self.logger.info(f"Templates directory: {self.templates_dir}")
140
141
  self.logger.info(f"Base agent path: {self.base_agent_path}")
141
142
 
142
- def deploy_agents(self, target_dir: Optional[Path] = None, force_rebuild: bool = False, deployment_mode: str = "update", config: Optional[Config] = None, use_async: bool = True) -> Dict[str, Any]:
143
+ def deploy_agents(self, target_dir: Optional[Path] = None, force_rebuild: bool = False, deployment_mode: str = "update", config: Optional[Config] = None, use_async: bool = False) -> Dict[str, Any]:
143
144
  """
144
145
  Build and deploy agents by combining base_agent.md with templates.
145
146
  Also deploys system instructions for PM framework.
@@ -278,6 +279,9 @@ class AgentDeploymentService(AgentDeploymentInterface):
278
279
  results=results
279
280
  )
280
281
 
282
+ # Deploy system instructions and framework files
283
+ self._deploy_system_instructions(agents_dir, force_rebuild, results)
284
+
281
285
  self.logger.info(
282
286
  f"Deployed {len(results['deployed'])} agents, "
283
287
  f"updated {len(results['updated'])}, "
@@ -1668,7 +1672,11 @@ temperature: {temperature}"""
1668
1672
 
1669
1673
  def _deploy_system_instructions(self, target_dir: Path, force_rebuild: bool, results: Dict[str, Any]) -> None:
1670
1674
  """
1671
- Deploy system instructions for PM framework.
1675
+ Deploy system instructions and framework files for PM framework.
1676
+
1677
+ Deploys INSTRUCTIONS.md, WORKFLOW.md, and MEMORY.md files following hierarchy:
1678
+ - System/User versions → Deploy to ~/.claude/
1679
+ - Project-specific versions → Deploy to <project>/.claude/
1672
1680
 
1673
1681
  Args:
1674
1682
  target_dir: Target directory for deployment
@@ -1676,47 +1684,64 @@ temperature: {temperature}"""
1676
1684
  results: Results dictionary to update
1677
1685
  """
1678
1686
  try:
1679
- # Find the INSTRUCTIONS.md file
1680
- module_path = Path(__file__).parent.parent
1681
- instructions_path = module_path / "agents" / "INSTRUCTIONS.md"
1682
-
1683
- if not instructions_path.exists():
1684
- self.logger.warning(f"System instructions not found: {instructions_path}")
1685
- return
1686
-
1687
- # Target file for system instructions - use CLAUDE.md in user's home .claude directory
1688
- target_file = Path("~/.claude/CLAUDE.md").expanduser()
1687
+ # Determine target location based on deployment type
1688
+ if self._is_project_specific_deployment():
1689
+ # Project-specific files go to project's .claude directory
1690
+ claude_dir = self.working_directory / ".claude"
1691
+ else:
1692
+ # System and user files go to home ~/.claude directory
1693
+ claude_dir = Path.home() / ".claude"
1689
1694
 
1690
1695
  # Ensure .claude directory exists
1691
- target_file.parent.mkdir(exist_ok=True)
1696
+ claude_dir.mkdir(parents=True, exist_ok=True)
1692
1697
 
1693
- # Check if update needed
1694
- if not force_rebuild and target_file.exists():
1695
- # Compare modification times
1696
- if target_file.stat().st_mtime >= instructions_path.stat().st_mtime:
1697
- results["skipped"].append("CLAUDE.md")
1698
- self.logger.debug("System instructions up to date")
1699
- return
1698
+ # Framework files to deploy
1699
+ framework_files = [
1700
+ ("INSTRUCTIONS.md", "CLAUDE.md"), # INSTRUCTIONS.md deploys as CLAUDE.md
1701
+ ("WORKFLOW.md", "WORKFLOW.md"),
1702
+ ("MEMORY.md", "MEMORY.md")
1703
+ ]
1700
1704
 
1701
- # Read and deploy system instructions
1702
- instructions_content = instructions_path.read_text()
1703
- target_file.write_text(instructions_content)
1705
+ # Find the agents directory with framework files
1706
+ # Use centralized paths for consistency
1707
+ from claude_mpm.config.paths import paths
1708
+ agents_path = paths.agents_dir
1704
1709
 
1705
- is_update = target_file.exists()
1706
- if is_update:
1707
- results["updated"].append({
1708
- "name": "CLAUDE.md",
1709
- "template": str(instructions_path),
1710
- "target": str(target_file)
1711
- })
1712
- self.logger.info("Updated system instructions")
1713
- else:
1714
- results["deployed"].append({
1715
- "name": "CLAUDE.md",
1716
- "template": str(instructions_path),
1710
+ for source_name, target_name in framework_files:
1711
+ source_path = agents_path / source_name
1712
+
1713
+ if not source_path.exists():
1714
+ self.logger.warning(f"Framework file not found: {source_path}")
1715
+ continue
1716
+
1717
+ target_file = claude_dir / target_name
1718
+
1719
+ # Check if update needed
1720
+ if not force_rebuild and target_file.exists():
1721
+ # Compare modification times
1722
+ if target_file.stat().st_mtime >= source_path.stat().st_mtime:
1723
+ results["skipped"].append(target_name)
1724
+ self.logger.debug(f"Framework file {target_name} up to date")
1725
+ continue
1726
+
1727
+ # Read and deploy framework file
1728
+ file_content = source_path.read_text()
1729
+ target_file.write_text(file_content)
1730
+
1731
+ # Track deployment
1732
+ file_existed = target_file.exists()
1733
+ deployment_info = {
1734
+ "name": target_name,
1735
+ "template": str(source_path),
1717
1736
  "target": str(target_file)
1718
- })
1719
- self.logger.info("Deployed system instructions")
1737
+ }
1738
+
1739
+ if file_existed:
1740
+ results["updated"].append(deployment_info)
1741
+ self.logger.info(f"Updated framework file: {target_name}")
1742
+ else:
1743
+ results["deployed"].append(deployment_info)
1744
+ self.logger.info(f"Deployed framework file: {target_name}")
1720
1745
 
1721
1746
  except Exception as e:
1722
1747
  error_msg = f"Failed to deploy system instructions: {e}"
@@ -2026,6 +2051,11 @@ metadata:
2026
2051
  WHY: Different deployment scenarios require different directory
2027
2052
  structures. This method centralizes the logic for consistency.
2028
2053
 
2054
+ HIERARCHY:
2055
+ - System agents → Deploy to ~/.claude/agents/ (user's home directory)
2056
+ - User custom agents from ~/.claude-mpm/agents/ → Deploy to ~/.claude/agents/
2057
+ - Project-specific agents from <project>/.claude-mpm/agents/ → Deploy to <project>/.claude/agents/
2058
+
2029
2059
  Args:
2030
2060
  target_dir: Optional target directory
2031
2061
 
@@ -2033,9 +2063,17 @@ metadata:
2033
2063
  Path to agents directory
2034
2064
  """
2035
2065
  if not target_dir:
2036
- # Default to working directory's .claude/agents directory (not home)
2037
- # This ensures we deploy to the user's project directory
2038
- return self.working_directory / ".claude" / "agents"
2066
+ # Default deployment location depends on agent source
2067
+ # Check if we're deploying system agents or user/project agents
2068
+ if self._is_system_agent_deployment():
2069
+ # System agents go to user's home ~/.claude/agents/
2070
+ return Path.home() / ".claude" / "agents"
2071
+ elif self._is_project_specific_deployment():
2072
+ # Project agents stay in project directory
2073
+ return self.working_directory / ".claude" / "agents"
2074
+ else:
2075
+ # Default: User custom agents go to home ~/.claude/agents/
2076
+ return Path.home() / ".claude" / "agents"
2039
2077
 
2040
2078
  # If target_dir provided, use it directly (caller decides structure)
2041
2079
  target_dir = Path(target_dir)
@@ -2054,6 +2092,69 @@ metadata:
2054
2092
  # Assume it's a project directory, add .claude/agents
2055
2093
  return target_dir / ".claude" / "agents"
2056
2094
 
2095
+ def _is_system_agent_deployment(self) -> bool:
2096
+ """
2097
+ Check if this is a deployment of system agents.
2098
+
2099
+ System agents are those provided by the claude-mpm package itself,
2100
+ located in the package's agents/templates directory.
2101
+
2102
+ Returns:
2103
+ True if deploying system agents, False otherwise
2104
+ """
2105
+ # Check if templates_dir points to the system templates
2106
+ if self.templates_dir and self.templates_dir.exists():
2107
+ # System agents are in the package's agents/templates directory
2108
+ try:
2109
+ # Check if templates_dir is within the claude_mpm package structure
2110
+ templates_str = str(self.templates_dir.resolve())
2111
+ return ("site-packages/claude_mpm" in templates_str or
2112
+ "src/claude_mpm/agents/templates" in templates_str or
2113
+ (paths.agents_dir / "templates").resolve() == self.templates_dir.resolve())
2114
+ except Exception:
2115
+ pass
2116
+ return False
2117
+
2118
+ def _is_project_specific_deployment(self) -> bool:
2119
+ """
2120
+ Check if deploying project-specific agents.
2121
+
2122
+ Project-specific agents are those found in the project's
2123
+ .claude-mpm/agents/ directory.
2124
+
2125
+ Returns:
2126
+ True if deploying project-specific agents, False otherwise
2127
+ """
2128
+ # Check if we're in a project directory with .claude-mpm/agents
2129
+ project_agents_dir = self.working_directory / ".claude-mpm" / "agents"
2130
+ if project_agents_dir.exists():
2131
+ # Check if templates_dir points to project agents
2132
+ if self.templates_dir and self.templates_dir.exists():
2133
+ try:
2134
+ return project_agents_dir.resolve() == self.templates_dir.resolve()
2135
+ except Exception:
2136
+ pass
2137
+ return False
2138
+
2139
+ def _is_user_custom_deployment(self) -> bool:
2140
+ """
2141
+ Check if deploying user custom agents.
2142
+
2143
+ User custom agents are those in ~/.claude-mpm/agents/
2144
+
2145
+ Returns:
2146
+ True if deploying user custom agents, False otherwise
2147
+ """
2148
+ user_agents_dir = Path.home() / ".claude-mpm" / "agents"
2149
+ if user_agents_dir.exists():
2150
+ # Check if templates_dir points to user agents
2151
+ if self.templates_dir and self.templates_dir.exists():
2152
+ try:
2153
+ return user_agents_dir.resolve() == self.templates_dir.resolve()
2154
+ except Exception:
2155
+ pass
2156
+ return False
2157
+
2057
2158
  def _initialize_deployment_results(self, agents_dir: Path, deployment_start_time: float) -> Dict[str, Any]:
2058
2159
  """
2059
2160
  Initialize the deployment results dictionary.
@@ -44,13 +44,14 @@ class AgentMemoryManager(MemoryServiceInterface):
44
44
  to keep them organized and separate from other project files. Files follow a
45
45
  standardized markdown format with enforced size limits to prevent unbounded growth.
46
46
 
47
- The 8KB limit (~2000 tokens) balances comprehensive knowledge storage with
47
+ The 80KB limit (~20k tokens) balances comprehensive knowledge storage with
48
48
  reasonable context size for agent prompts.
49
49
  """
50
50
 
51
51
  # Default limits - will be overridden by configuration
52
+ # Updated to support 20k tokens (~80KB) for enhanced memory capacity
52
53
  DEFAULT_MEMORY_LIMITS = {
53
- 'max_file_size_kb': 8,
54
+ 'max_file_size_kb': 80, # Increased from 8KB to 80KB (20k tokens)
54
55
  'max_sections': 10,
55
56
  'max_items_per_section': 15,
56
57
  'max_line_length': 120
@@ -1365,7 +1366,7 @@ Feel free to edit these files to:
1365
1366
  - Add domain-specific knowledge
1366
1367
 
1367
1368
  ## Memory Limits
1368
- - Max file size: 8KB (~2000 tokens)
1369
+ - Max file size: 80KB (~20k tokens)
1369
1370
  - Max sections: 10
1370
1371
  - Max items per section: 15
1371
1372
  - Files auto-truncate when limits exceeded
@@ -24,8 +24,14 @@ class FrameworkClaudeMdGenerator:
24
24
  This is the main facade class that coordinates all the submodules.
25
25
  """
26
26
 
27
- def __init__(self):
28
- """Initialize the generator with current framework version."""
27
+ def __init__(self, target_filename: str = "INSTRUCTIONS.md"):
28
+ """
29
+ Initialize the generator with current framework version.
30
+
31
+ Args:
32
+ target_filename: Target filename for deployment (default: "INSTRUCTIONS.md")
33
+ Can be set to "CLAUDE.md" for legacy compatibility
34
+ """
29
35
  # Initialize managers
30
36
  self.version_manager = VersionManager()
31
37
  self.validator = ContentValidator()
@@ -35,7 +41,8 @@ class FrameworkClaudeMdGenerator:
35
41
  # Initialize deployment manager with dependencies
36
42
  self.deployment_manager = DeploymentManager(
37
43
  self.version_manager,
38
- self.validator
44
+ self.validator,
45
+ target_filename=target_filename
39
46
  )
40
47
 
41
48
  # Get framework version
@@ -6,7 +6,7 @@ Handles deployment operations to parent directories.
6
6
 
7
7
  from pathlib import Path
8
8
  from typing import Tuple, Optional
9
- from datetime import datetime
9
+ from datetime import datetime, timezone
10
10
  from .version_manager import VersionManager
11
11
  from .content_validator import ContentValidator
12
12
 
@@ -17,16 +17,20 @@ from ...utils.framework_detection import is_framework_source_directory
17
17
  class DeploymentManager:
18
18
  """Manages deployment of framework CLAUDE.md to parent directories."""
19
19
 
20
- def __init__(self, version_manager: VersionManager, validator: ContentValidator):
20
+ def __init__(self, version_manager: VersionManager, validator: ContentValidator,
21
+ target_filename: str = "INSTRUCTIONS.md"):
21
22
  """
22
23
  Initialize deployment manager.
23
24
 
24
25
  Args:
25
26
  version_manager: Version management instance
26
27
  validator: Content validator instance
28
+ target_filename: Target filename for deployment (default: "INSTRUCTIONS.md")
29
+ Can be set to "CLAUDE.md" for legacy compatibility
27
30
  """
28
31
  self.version_manager = version_manager
29
32
  self.validator = validator
33
+ self.target_filename = target_filename
30
34
 
31
35
  def deploy_to_parent(self,
32
36
  content: str,
@@ -53,9 +57,8 @@ class DeploymentManager:
53
57
  if is_framework:
54
58
  return True, f"Skipping deployment - detected framework source directory (markers: {', '.join(markers)})"
55
59
 
56
- # Use INSTRUCTIONS.md as primary, with CLAUDE.md as fallback
57
- target_file = parent_path / "INSTRUCTIONS.md"
58
- # TODO: Make this configurable via parameter
60
+ # Use configured target filename
61
+ target_file = parent_path / self.target_filename
59
62
 
60
63
  # Check if content contains template variables that need processing
61
64
  if '{{capabilities-list}}' in content:
@@ -115,10 +118,10 @@ class DeploymentManager:
115
118
  Returns:
116
119
  Tuple of (needed, reason)
117
120
  """
118
- target_file = parent_path / "CLAUDE.md"
121
+ target_file = parent_path / self.target_filename
119
122
 
120
123
  if not target_file.exists():
121
- return True, "CLAUDE.md does not exist"
124
+ return True, f"{self.target_filename} does not exist"
122
125
 
123
126
  try:
124
127
  with open(target_file, 'r') as f:
@@ -134,7 +137,7 @@ class DeploymentManager:
134
137
 
135
138
  def backup_existing(self, parent_path: Path) -> Optional[Path]:
136
139
  """
137
- Create a backup of existing CLAUDE.md before deployment.
140
+ Create a backup of existing target file before deployment.
138
141
 
139
142
  Args:
140
143
  parent_path: Path to parent directory
@@ -142,14 +145,14 @@ class DeploymentManager:
142
145
  Returns:
143
146
  Path to backup file if created, None otherwise
144
147
  """
145
- target_file = parent_path / "CLAUDE.md"
148
+ target_file = parent_path / self.target_filename
146
149
 
147
150
  if not target_file.exists():
148
151
  return None
149
152
 
150
153
  # Create backup filename with timestamp
151
- timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
152
- backup_file = parent_path / f"CLAUDE.md.backup.{timestamp}"
154
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
155
+ backup_file = parent_path / f"{self.target_filename}.backup.{timestamp}"
153
156
 
154
157
  try:
155
158
  import shutil
@@ -71,11 +71,9 @@ class ResponseTracker:
71
71
  if not base_dir:
72
72
  base_dir = response_logging_config.get('session_directory', '.claude-mpm/responses')
73
73
 
74
- # Create session logger with proper configuration
75
- self.session_logger = ClaudeSessionLogger(
76
- base_dir=Path(base_dir),
77
- config=config
78
- )
74
+ # Use singleton session logger for proper sharing
75
+ from claude_mpm.services.claude_session_logger import get_session_logger
76
+ self.session_logger = get_session_logger(config)
79
77
  logger.info(f"Response tracker initialized with base directory: {base_dir}")
80
78
  except Exception as e:
81
79
  logger.error(f"Failed to initialize session logger: {e}")
@@ -49,7 +49,7 @@ class TicketManager(TicketManagerInterface):
49
49
  # Check if config exists, create if needed
50
50
  if not config_file.exists():
51
51
  try:
52
- config = Config.create_default(str(config_file))
52
+ config = Config.create_default(config_file) # Pass Path object directly
53
53
  config.set("paths.tickets_dir", "tickets")
54
54
  config.set("paths.epics_dir", "tickets/epics")
55
55
  config.set("paths.issues_dir", "tickets/issues")
@@ -64,7 +64,7 @@ class TicketManager(TicketManagerInterface):
64
64
 
65
65
  # Initialize TaskManager directly with the project path
66
66
  # TaskManager will handle project initialization internally
67
- task_manager = TaskManager(str(self.project_path))
67
+ task_manager = TaskManager(self.project_path) # Pass Path object directly
68
68
 
69
69
  # Verify it's using the right directory
70
70
  if hasattr(task_manager, 'tasks_dir'):
@@ -56,7 +56,7 @@ class AITrackdownAdapter(ITaskManagerAdapter):
56
56
  # Configure ai-trackdown if needed
57
57
  config_file = self.project_path / ".trackdown.yaml"
58
58
  if not config_file.exists():
59
- config = TrackdownConfig.create_default(config_file)
59
+ config = TrackdownConfig.create_default(config_file) # Already correct - using Path object
60
60
  config.set("paths.tickets_dir", "tickets")
61
61
  config.set("paths.epics_dir", "tickets/epics")
62
62
  config.set("paths.issues_dir", "tickets/issues")
@@ -334,11 +334,42 @@ class SemanticVersionManager:
334
334
 
335
335
  def get_current_version(self) -> Optional[SemanticVersion]:
336
336
  """
337
- Get the current version from project files.
337
+ Get the current version from multiple sources with intelligent fallback.
338
+
339
+ Uses the enhanced version parser to check:
340
+ 1. Git tags (most recent)
341
+ 2. VERSION file
342
+ 3. package.json
343
+ 4. pyproject.toml
344
+ 5. Other configured version files
338
345
 
339
346
  Returns:
340
347
  Current SemanticVersion or None if not found
341
348
  """
349
+ try:
350
+ # Import here to avoid circular dependency
351
+ from claude_mpm.services.version_control.version_parser import get_version_parser
352
+
353
+ # Use enhanced parser for current version
354
+ parser = get_version_parser(self.project_root)
355
+ version_meta = parser.get_current_version()
356
+
357
+ if version_meta:
358
+ version = self.parse_version(version_meta.version)
359
+ if version:
360
+ self.logger.info(f"Found version {version} from {version_meta.source}")
361
+ # Optionally attach metadata
362
+ if hasattr(version, '__dict__'):
363
+ version.source = version_meta.source
364
+ return version
365
+
366
+ except ImportError:
367
+ # Fallback to original implementation
368
+ self.logger.debug("Enhanced version parser not available, using fallback")
369
+ except Exception as e:
370
+ self.logger.error(f"Error getting current version with enhanced parser: {e}")
371
+
372
+ # Fallback to original implementation
342
373
  for filename, parser in self.version_files.items():
343
374
  file_path = self.project_root / filename
344
375
 
@@ -811,19 +842,61 @@ class SemanticVersionManager:
811
842
 
812
843
  def get_version_history(self) -> List[SemanticVersion]:
813
844
  """
814
- Get version history from changelog or Git tags.
845
+ Get version history from multiple sources with intelligent fallback.
846
+
847
+ Uses the enhanced version parser to retrieve version history from:
848
+ 1. Git tags (primary source)
849
+ 2. CHANGELOG.md (fallback)
850
+ 3. VERSION files (current version only)
815
851
 
816
852
  Returns:
817
853
  List of versions in descending order
818
854
  """
855
+ try:
856
+ # Import here to avoid circular dependency
857
+ from claude_mpm.services.version_control.version_parser import get_version_parser
858
+
859
+ # Use enhanced parser for comprehensive version history
860
+ parser = get_version_parser(self.project_root)
861
+ version_metadata = parser.get_version_history(include_prereleases=False)
862
+
863
+ # Convert to SemanticVersion objects
864
+ versions = []
865
+ for meta in version_metadata:
866
+ version = self.parse_version(meta.version)
867
+ if version:
868
+ # Optionally attach metadata to version
869
+ if hasattr(version, '__dict__'):
870
+ version.source = meta.source
871
+ version.release_date = meta.release_date
872
+ version.commit_hash = meta.commit_hash
873
+ versions.append(version)
874
+
875
+ return versions
876
+
877
+ except ImportError:
878
+ # Fallback to original implementation if enhanced parser not available
879
+ self.logger.warning("Enhanced version parser not available, falling back to changelog parsing")
880
+ return self._parse_changelog_versions_fallback()
881
+ except Exception as e:
882
+ self.logger.error(f"Error getting version history: {e}")
883
+ # Fallback to original implementation
884
+ return self._parse_changelog_versions_fallback()
885
+
886
+ def _parse_changelog_versions_fallback(self) -> List[SemanticVersion]:
887
+ """Fallback method: Parse versions from changelog file only."""
819
888
  versions = []
820
889
 
821
890
  # Try to get versions from changelog
822
- changelog_path = self.project_root / "docs" / "CHANGELOG.md"
823
- if changelog_path.exists():
824
- versions.extend(self._parse_changelog_versions(changelog_path))
825
-
826
- # TODO: Add Git tag parsing if needed
891
+ changelog_paths = [
892
+ self.project_root / "CHANGELOG.md",
893
+ self.project_root / "docs" / "CHANGELOG.md"
894
+ ]
895
+
896
+ for changelog_path in changelog_paths:
897
+ if changelog_path.exists():
898
+ versions.extend(self._parse_changelog_versions(changelog_path))
899
+ break
827
900
 
828
901
  # Sort versions in descending order
829
902
  versions.sort(reverse=True)