claude-mpm 4.1.4__py3-none-any.whl → 4.1.5__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/tickets.py +365 -784
- claude_mpm/core/output_style_manager.py +24 -0
- claude_mpm/core/unified_agent_registry.py +46 -15
- claude_mpm/services/agents/deployment/agent_discovery_service.py +12 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +172 -233
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +575 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +419 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +4 -2
- claude_mpm/services/infrastructure/__init__.py +31 -5
- claude_mpm/services/infrastructure/monitoring/__init__.py +43 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +437 -0
- claude_mpm/services/infrastructure/monitoring/base.py +130 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +218 -0
- claude_mpm/services/infrastructure/monitoring/process.py +342 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +243 -0
- claude_mpm/services/infrastructure/monitoring/service.py +367 -0
- claude_mpm/services/infrastructure/monitoring.py +67 -1030
- claude_mpm/services/project/analyzer.py +13 -4
- claude_mpm/services/project/analyzer_refactored.py +450 -0
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- claude_mpm/services/project/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +410 -0
- claude_mpm/services/ticket_manager.py +5 -1
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/RECORD +41 -17
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/top_level.txt +0 -0
|
@@ -21,6 +21,10 @@ from ..utils.imports import safe_import
|
|
|
21
21
|
# Import with fallback support
|
|
22
22
|
get_logger = safe_import("claude_mpm.core.logger", "core.logger", ["get_logger"])
|
|
23
23
|
|
|
24
|
+
# Global cache for Claude version to avoid duplicate detection/logging
|
|
25
|
+
_CACHED_CLAUDE_VERSION: Optional[str] = None
|
|
26
|
+
_VERSION_DETECTED: bool = False
|
|
27
|
+
|
|
24
28
|
|
|
25
29
|
class OutputStyleManager:
|
|
26
30
|
"""Manages output style deployment and version-based handling."""
|
|
@@ -41,10 +45,17 @@ class OutputStyleManager:
|
|
|
41
45
|
def _detect_claude_version(self) -> Optional[str]:
|
|
42
46
|
"""
|
|
43
47
|
Detect Claude Code version by running 'claude --version'.
|
|
48
|
+
Uses global cache to avoid duplicate detection and logging.
|
|
44
49
|
|
|
45
50
|
Returns:
|
|
46
51
|
Version string (e.g., "1.0.82") or None if Claude not found
|
|
47
52
|
"""
|
|
53
|
+
global _CACHED_CLAUDE_VERSION, _VERSION_DETECTED
|
|
54
|
+
|
|
55
|
+
# Return cached version if already detected
|
|
56
|
+
if _VERSION_DETECTED:
|
|
57
|
+
return _CACHED_CLAUDE_VERSION
|
|
58
|
+
|
|
48
59
|
try:
|
|
49
60
|
# Run claude --version command
|
|
50
61
|
result = subprocess.run(
|
|
@@ -57,6 +68,8 @@ class OutputStyleManager:
|
|
|
57
68
|
|
|
58
69
|
if result.returncode != 0:
|
|
59
70
|
self.logger.warning(f"Claude command failed: {result.stderr}")
|
|
71
|
+
_VERSION_DETECTED = True
|
|
72
|
+
_CACHED_CLAUDE_VERSION = None
|
|
60
73
|
return None
|
|
61
74
|
|
|
62
75
|
# Parse version from output
|
|
@@ -66,19 +79,30 @@ class OutputStyleManager:
|
|
|
66
79
|
|
|
67
80
|
if version_match:
|
|
68
81
|
version = version_match.group(1)
|
|
82
|
+
# Only log on first detection
|
|
69
83
|
self.logger.info(f"Detected Claude version: {version}")
|
|
84
|
+
_CACHED_CLAUDE_VERSION = version
|
|
85
|
+
_VERSION_DETECTED = True
|
|
70
86
|
return version
|
|
71
87
|
self.logger.warning(f"Could not parse version from: {version_output}")
|
|
88
|
+
_VERSION_DETECTED = True
|
|
89
|
+
_CACHED_CLAUDE_VERSION = None
|
|
72
90
|
return None
|
|
73
91
|
|
|
74
92
|
except FileNotFoundError:
|
|
75
93
|
self.logger.info("Claude Code not found in PATH")
|
|
94
|
+
_VERSION_DETECTED = True
|
|
95
|
+
_CACHED_CLAUDE_VERSION = None
|
|
76
96
|
return None
|
|
77
97
|
except subprocess.TimeoutExpired:
|
|
78
98
|
self.logger.warning("Claude version check timed out")
|
|
99
|
+
_VERSION_DETECTED = True
|
|
100
|
+
_CACHED_CLAUDE_VERSION = None
|
|
79
101
|
return None
|
|
80
102
|
except Exception as e:
|
|
81
103
|
self.logger.warning(f"Error detecting Claude version: {e}")
|
|
104
|
+
_VERSION_DETECTED = True
|
|
105
|
+
_CACHED_CLAUDE_VERSION = None
|
|
82
106
|
return None
|
|
83
107
|
|
|
84
108
|
def _compare_versions(self, version1: str, version2: str) -> int:
|
|
@@ -136,7 +136,13 @@ class UnifiedAgentRegistry:
|
|
|
136
136
|
|
|
137
137
|
# Discovery configuration
|
|
138
138
|
self.file_extensions = {".md", ".json", ".yaml", ".yml"}
|
|
139
|
-
self.ignore_patterns = {
|
|
139
|
+
self.ignore_patterns = {
|
|
140
|
+
"__pycache__",
|
|
141
|
+
".git",
|
|
142
|
+
"node_modules",
|
|
143
|
+
".pytest_cache",
|
|
144
|
+
"backup",
|
|
145
|
+
}
|
|
140
146
|
|
|
141
147
|
# Statistics
|
|
142
148
|
self.discovery_stats = {
|
|
@@ -166,15 +172,15 @@ class UnifiedAgentRegistry:
|
|
|
166
172
|
if user_path.exists():
|
|
167
173
|
self.discovery_paths.append(user_path)
|
|
168
174
|
|
|
169
|
-
# System-level agents
|
|
175
|
+
# System-level agents (includes templates as a subdirectory)
|
|
170
176
|
system_path = self.path_manager.get_system_agents_dir()
|
|
171
177
|
if system_path.exists():
|
|
172
178
|
self.discovery_paths.append(system_path)
|
|
173
179
|
|
|
174
|
-
# Templates directory
|
|
175
|
-
templates_path =
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
# NOTE: Templates directory is NOT added separately because:
|
|
181
|
+
# - templates_path = system_path / "templates"
|
|
182
|
+
# - The rglob("*") in _discover_path will already find templates
|
|
183
|
+
# - Adding it separately causes duplicate discovery
|
|
178
184
|
|
|
179
185
|
logger.debug(
|
|
180
186
|
f"Discovery paths configured: {[str(p) for p in self.discovery_paths]}"
|
|
@@ -256,7 +262,10 @@ class UnifiedAgentRegistry:
|
|
|
256
262
|
try:
|
|
257
263
|
metadata = self._create_agent_metadata(file_path, agent_name, tier)
|
|
258
264
|
if metadata:
|
|
259
|
-
|
|
265
|
+
# Store all discovered agents temporarily for tier precedence
|
|
266
|
+
# Use a unique key that includes tier to prevent overwrites
|
|
267
|
+
registry_key = f"{agent_name}_{tier.value}"
|
|
268
|
+
self.registry[registry_key] = metadata
|
|
260
269
|
self.discovered_files.add(file_path)
|
|
261
270
|
logger.debug(
|
|
262
271
|
f"Discovered agent: {agent_name} ({tier.value}) at {file_path}"
|
|
@@ -269,9 +278,29 @@ class UnifiedAgentRegistry:
|
|
|
269
278
|
# Remove extension and use filename as agent name
|
|
270
279
|
name = file_path.stem
|
|
271
280
|
|
|
272
|
-
# Skip certain files
|
|
273
|
-
skip_files = {
|
|
274
|
-
|
|
281
|
+
# Skip certain files and non-agent templates
|
|
282
|
+
skip_files = {
|
|
283
|
+
"README",
|
|
284
|
+
"INSTRUCTIONS",
|
|
285
|
+
"template",
|
|
286
|
+
"example",
|
|
287
|
+
"base_agent",
|
|
288
|
+
"base_agent_template",
|
|
289
|
+
"agent_template",
|
|
290
|
+
"agent_schema",
|
|
291
|
+
"base_pm",
|
|
292
|
+
"workflow",
|
|
293
|
+
"output_style",
|
|
294
|
+
"memory",
|
|
295
|
+
"optimization_report",
|
|
296
|
+
"vercel_ops_instructions",
|
|
297
|
+
"agent-template",
|
|
298
|
+
"agent-schema", # Also handle hyphenated versions
|
|
299
|
+
}
|
|
300
|
+
# Case-insensitive comparison
|
|
301
|
+
if name.replace("-", "_").upper() in {
|
|
302
|
+
s.replace("-", "_").upper() for s in skip_files
|
|
303
|
+
}:
|
|
275
304
|
return None
|
|
276
305
|
|
|
277
306
|
# Normalize name
|
|
@@ -424,12 +453,14 @@ class UnifiedAgentRegistry:
|
|
|
424
453
|
|
|
425
454
|
def _apply_tier_precedence(self) -> None:
|
|
426
455
|
"""Apply tier precedence rules to resolve conflicts."""
|
|
427
|
-
# Group agents by name
|
|
456
|
+
# Group agents by their actual name (without tier suffix)
|
|
428
457
|
agent_groups = {}
|
|
429
|
-
for
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
agent_groups
|
|
458
|
+
for registry_key, metadata in self.registry.items():
|
|
459
|
+
# Extract the actual agent name (registry_key is "name_tier")
|
|
460
|
+
agent_name = metadata.name # Use the actual name from metadata
|
|
461
|
+
if agent_name not in agent_groups:
|
|
462
|
+
agent_groups[agent_name] = []
|
|
463
|
+
agent_groups[agent_name].append(metadata)
|
|
433
464
|
|
|
434
465
|
# Resolve conflicts using tier precedence
|
|
435
466
|
resolved_registry = {}
|
|
@@ -34,10 +34,14 @@ class AgentDiscoveryService:
|
|
|
34
34
|
self.logger = get_logger(__name__)
|
|
35
35
|
self.templates_dir = templates_dir
|
|
36
36
|
|
|
37
|
-
def list_available_agents(self) -> List[Dict[str, Any]]:
|
|
37
|
+
def list_available_agents(self, log_discovery: bool = True) -> List[Dict[str, Any]]:
|
|
38
38
|
"""
|
|
39
39
|
List all available agent templates with their metadata.
|
|
40
40
|
|
|
41
|
+
Args:
|
|
42
|
+
log_discovery: Whether to log discovery results (default: True).
|
|
43
|
+
Set to False when called from multi-source discovery to avoid duplicate logs.
|
|
44
|
+
|
|
41
45
|
Returns:
|
|
42
46
|
List of agent information dictionaries containing:
|
|
43
47
|
- name: Agent name
|
|
@@ -73,7 +77,11 @@ class AgentDiscoveryService:
|
|
|
73
77
|
# Sort by agent name for consistent ordering
|
|
74
78
|
agents.sort(key=lambda x: x.get("name", ""))
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
# Only log if requested (to avoid duplicate logging from multi-source discovery)
|
|
81
|
+
if log_discovery:
|
|
82
|
+
self.logger.info(
|
|
83
|
+
f"Discovered {len(agents)} available agent templates from {self.templates_dir.name}"
|
|
84
|
+
)
|
|
77
85
|
return agents
|
|
78
86
|
|
|
79
87
|
def get_filtered_templates(
|
|
@@ -153,7 +161,8 @@ class AgentDiscoveryService:
|
|
|
153
161
|
Dictionary mapping categories to lists of agent names
|
|
154
162
|
"""
|
|
155
163
|
categories = {}
|
|
156
|
-
|
|
164
|
+
# Don't log discovery when called internally
|
|
165
|
+
agents = self.list_available_agents(log_discovery=False)
|
|
157
166
|
|
|
158
167
|
for agent in agents:
|
|
159
168
|
agent_name = agent.get("name", "unknown")
|