claude-mpm 4.13.2__py3-none-any.whl → 4.18.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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +18 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +1217 -6
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +10 -3
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/__init__.py +34 -706
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +204 -148
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +294 -1788
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +39 -25
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +98 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +10 -11
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/__init__.py +74 -2
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/models/__init__.py +33 -0
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +430 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/memory_hook_service.py +4 -1
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stability Monitoring Data Models for Claude MPM Framework
|
|
3
|
+
===========================================================
|
|
4
|
+
|
|
5
|
+
WHY: This module defines data structures for stability monitoring operations,
|
|
6
|
+
including memory leak detection, log pattern matching, and resource usage tracking.
|
|
7
|
+
|
|
8
|
+
DESIGN DECISION: Uses dataclasses for immutability and type safety. Provides
|
|
9
|
+
clear data structures for proactive monitoring and crash prevention.
|
|
10
|
+
|
|
11
|
+
ARCHITECTURE:
|
|
12
|
+
- MemoryTrend: Memory usage trend analysis with leak detection
|
|
13
|
+
- LogPatternMatch: Log pattern match with severity and context
|
|
14
|
+
- ResourceUsage: Comprehensive resource usage snapshot
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dataclasses import asdict, dataclass, field
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from typing import Any, Dict, List
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class MemoryTrend:
|
|
24
|
+
"""
|
|
25
|
+
Memory usage trend analysis result.
|
|
26
|
+
|
|
27
|
+
WHY: Provides structured data for memory leak detection, including
|
|
28
|
+
historical measurements, slope calculation, and leak detection status.
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
deployment_id: Unique deployment identifier
|
|
32
|
+
timestamps: List of measurement timestamps
|
|
33
|
+
memory_mb: List of memory measurements in megabytes
|
|
34
|
+
slope_mb_per_minute: Calculated memory growth rate (MB/minute)
|
|
35
|
+
is_leaking: Whether a memory leak was detected
|
|
36
|
+
window_size: Number of measurements in the analysis window
|
|
37
|
+
threshold_mb_per_minute: Leak detection threshold used
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
deployment_id: str
|
|
41
|
+
timestamps: List[datetime] = field(default_factory=list)
|
|
42
|
+
memory_mb: List[float] = field(default_factory=list)
|
|
43
|
+
slope_mb_per_minute: float = 0.0
|
|
44
|
+
is_leaking: bool = False
|
|
45
|
+
window_size: int = 0
|
|
46
|
+
threshold_mb_per_minute: float = 10.0
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
49
|
+
"""
|
|
50
|
+
Convert to dictionary for JSON serialization.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Dictionary representation with datetimes converted to ISO format
|
|
54
|
+
"""
|
|
55
|
+
data = asdict(self)
|
|
56
|
+
data["timestamps"] = [ts.isoformat() for ts in self.timestamps]
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_dict(cls, data: Dict[str, Any]) -> "MemoryTrend":
|
|
61
|
+
"""
|
|
62
|
+
Create MemoryTrend from dictionary.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
data: Dictionary from JSON deserialization
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
MemoryTrend instance
|
|
69
|
+
"""
|
|
70
|
+
# Convert ISO strings to datetime
|
|
71
|
+
if isinstance(data.get("timestamps"), list):
|
|
72
|
+
data["timestamps"] = [
|
|
73
|
+
datetime.fromisoformat(ts) if isinstance(ts, str) else ts
|
|
74
|
+
for ts in data["timestamps"]
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
return cls(**data)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def latest_memory_mb(self) -> float:
|
|
81
|
+
"""Get the most recent memory measurement."""
|
|
82
|
+
return self.memory_mb[-1] if self.memory_mb else 0.0
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def oldest_memory_mb(self) -> float:
|
|
86
|
+
"""Get the oldest memory measurement in the window."""
|
|
87
|
+
return self.memory_mb[0] if self.memory_mb else 0.0
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def time_span_minutes(self) -> float:
|
|
91
|
+
"""Get the time span covered by the measurements in minutes."""
|
|
92
|
+
if len(self.timestamps) < 2:
|
|
93
|
+
return 0.0
|
|
94
|
+
delta = self.timestamps[-1] - self.timestamps[0]
|
|
95
|
+
return delta.total_seconds() / 60.0
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass
|
|
99
|
+
class LogPatternMatch:
|
|
100
|
+
"""
|
|
101
|
+
Result of a log pattern match.
|
|
102
|
+
|
|
103
|
+
WHY: Contains all information about a detected error pattern in logs,
|
|
104
|
+
enabling analysis, alerting, and debugging of issues before they cause crashes.
|
|
105
|
+
|
|
106
|
+
Attributes:
|
|
107
|
+
deployment_id: Unique deployment identifier
|
|
108
|
+
pattern: Regex pattern that matched
|
|
109
|
+
line: The log line that matched
|
|
110
|
+
timestamp: When the match was detected
|
|
111
|
+
severity: Error severity level (ERROR, CRITICAL, WARNING)
|
|
112
|
+
line_number: Line number in log file (if available)
|
|
113
|
+
context: Additional context lines (before/after)
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
deployment_id: str
|
|
117
|
+
pattern: str
|
|
118
|
+
line: str
|
|
119
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
120
|
+
severity: str = "ERROR"
|
|
121
|
+
line_number: int = 0
|
|
122
|
+
context: List[str] = field(default_factory=list)
|
|
123
|
+
|
|
124
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
125
|
+
"""
|
|
126
|
+
Convert to dictionary for JSON serialization.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Dictionary representation with datetime converted to ISO format
|
|
130
|
+
"""
|
|
131
|
+
data = asdict(self)
|
|
132
|
+
data["timestamp"] = self.timestamp.isoformat()
|
|
133
|
+
return data
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def from_dict(cls, data: Dict[str, Any]) -> "LogPatternMatch":
|
|
137
|
+
"""
|
|
138
|
+
Create LogPatternMatch from dictionary.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
data: Dictionary from JSON deserialization
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
LogPatternMatch instance
|
|
145
|
+
"""
|
|
146
|
+
# Convert ISO string to datetime
|
|
147
|
+
if isinstance(data.get("timestamp"), str):
|
|
148
|
+
data["timestamp"] = datetime.fromisoformat(data["timestamp"])
|
|
149
|
+
|
|
150
|
+
return cls(**data)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def is_critical(self) -> bool:
|
|
154
|
+
"""Check if this match represents a critical error."""
|
|
155
|
+
return self.severity == "CRITICAL"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass
|
|
159
|
+
class ResourceUsage:
|
|
160
|
+
"""
|
|
161
|
+
Comprehensive resource usage snapshot.
|
|
162
|
+
|
|
163
|
+
WHY: Provides detailed resource consumption metrics across multiple
|
|
164
|
+
resource types to enable preemptive action before exhaustion.
|
|
165
|
+
|
|
166
|
+
Attributes:
|
|
167
|
+
deployment_id: Unique deployment identifier
|
|
168
|
+
file_descriptors: Current file descriptor count
|
|
169
|
+
max_file_descriptors: Maximum file descriptors allowed (ulimit -n)
|
|
170
|
+
threads: Current thread count
|
|
171
|
+
connections: Current network connection count
|
|
172
|
+
disk_free_mb: Free disk space in working directory (MB)
|
|
173
|
+
is_critical: Whether any resource exceeds 80% threshold
|
|
174
|
+
timestamp: When the measurement was taken
|
|
175
|
+
details: Additional resource-specific details
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
deployment_id: str
|
|
179
|
+
file_descriptors: int = 0
|
|
180
|
+
max_file_descriptors: int = 0
|
|
181
|
+
threads: int = 0
|
|
182
|
+
connections: int = 0
|
|
183
|
+
disk_free_mb: float = 0.0
|
|
184
|
+
is_critical: bool = False
|
|
185
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
186
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
187
|
+
|
|
188
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
189
|
+
"""
|
|
190
|
+
Convert to dictionary for JSON serialization.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Dictionary representation with datetime converted to ISO format
|
|
194
|
+
"""
|
|
195
|
+
data = asdict(self)
|
|
196
|
+
data["timestamp"] = self.timestamp.isoformat()
|
|
197
|
+
return data
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ResourceUsage":
|
|
201
|
+
"""
|
|
202
|
+
Create ResourceUsage from dictionary.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
data: Dictionary from JSON deserialization
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
ResourceUsage instance
|
|
209
|
+
"""
|
|
210
|
+
# Convert ISO string to datetime
|
|
211
|
+
if isinstance(data.get("timestamp"), str):
|
|
212
|
+
data["timestamp"] = datetime.fromisoformat(data["timestamp"])
|
|
213
|
+
|
|
214
|
+
return cls(**data)
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def fd_usage_percent(self) -> float:
|
|
218
|
+
"""Calculate file descriptor usage percentage."""
|
|
219
|
+
if self.max_file_descriptors == 0:
|
|
220
|
+
return 0.0
|
|
221
|
+
return (self.file_descriptors / self.max_file_descriptors) * 100.0
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def is_fd_critical(self) -> bool:
|
|
225
|
+
"""Check if file descriptor usage is critical (>80%)."""
|
|
226
|
+
return self.fd_usage_percent >= 80.0 # >= instead of > for 80% exactly
|
|
227
|
+
|
|
228
|
+
def get_critical_resources(self) -> List[str]:
|
|
229
|
+
"""
|
|
230
|
+
Get list of resources at critical levels.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
List of resource names exceeding 80% threshold
|
|
234
|
+
"""
|
|
235
|
+
critical = []
|
|
236
|
+
|
|
237
|
+
if self.is_fd_critical:
|
|
238
|
+
critical.append(
|
|
239
|
+
f"file_descriptors ({self.file_descriptors}/{self.max_file_descriptors})"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Check thread count (threshold from details if available)
|
|
243
|
+
thread_threshold = self.details.get("thread_threshold", 1000)
|
|
244
|
+
if self.threads > thread_threshold * 0.8:
|
|
245
|
+
critical.append(f"threads ({self.threads})")
|
|
246
|
+
|
|
247
|
+
# Check connection count (threshold from details if available)
|
|
248
|
+
connection_threshold = self.details.get("connection_threshold", 500)
|
|
249
|
+
if self.connections > connection_threshold * 0.8:
|
|
250
|
+
critical.append(f"connections ({self.connections})")
|
|
251
|
+
|
|
252
|
+
# Check disk space (threshold from details if available)
|
|
253
|
+
disk_threshold_mb = self.details.get("disk_threshold_mb", 100)
|
|
254
|
+
if self.disk_free_mb < disk_threshold_mb:
|
|
255
|
+
critical.append(f"disk_space ({self.disk_free_mb:.1f}MB free)")
|
|
256
|
+
|
|
257
|
+
return critical
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
__all__ = [
|
|
261
|
+
"LogPatternMatch",
|
|
262
|
+
"MemoryTrend",
|
|
263
|
+
"ResourceUsage",
|
|
264
|
+
]
|
|
@@ -13,6 +13,7 @@ that was previously embedded in FrameworkLoader. It manages:
|
|
|
13
13
|
The service consolidates path management logic while maintaining backward compatibility.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
import os
|
|
16
17
|
import subprocess
|
|
17
18
|
from enum import Enum
|
|
18
19
|
from pathlib import Path
|
|
@@ -74,10 +75,25 @@ class PathResolver(IPathResolver):
|
|
|
74
75
|
return path_obj
|
|
75
76
|
|
|
76
77
|
if base_dir is None:
|
|
77
|
-
base_dir =
|
|
78
|
+
base_dir = self._get_working_dir()
|
|
78
79
|
|
|
79
80
|
return (base_dir / path_obj).resolve()
|
|
80
81
|
|
|
82
|
+
def _get_working_dir(self) -> Path:
|
|
83
|
+
"""Get working directory respecting CLAUDE_MPM_USER_PWD.
|
|
84
|
+
|
|
85
|
+
When Claude MPM runs from a global installation, CLAUDE_MPM_USER_PWD
|
|
86
|
+
contains the user's actual working directory. This ensures project-local
|
|
87
|
+
paths are resolved correctly.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Path: The user's working directory
|
|
91
|
+
"""
|
|
92
|
+
user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD")
|
|
93
|
+
if user_pwd:
|
|
94
|
+
return Path(user_pwd)
|
|
95
|
+
return Path.cwd()
|
|
96
|
+
|
|
81
97
|
def validate_path(self, path: Path, must_exist: bool = False) -> bool:
|
|
82
98
|
"""
|
|
83
99
|
Validate a path for security and existence.
|
|
@@ -129,7 +145,7 @@ class PathResolver(IPathResolver):
|
|
|
129
145
|
Project root path or None if not found
|
|
130
146
|
"""
|
|
131
147
|
if start_path is None:
|
|
132
|
-
start_path =
|
|
148
|
+
start_path = self._get_working_dir()
|
|
133
149
|
|
|
134
150
|
start_path = start_path.resolve()
|
|
135
151
|
|
|
@@ -265,7 +281,7 @@ class PathResolver(IPathResolver):
|
|
|
265
281
|
|
|
266
282
|
if agents_dir and agents_dir.exists():
|
|
267
283
|
discovered_agents_dir = agents_dir
|
|
268
|
-
self.logger.
|
|
284
|
+
self.logger.debug(f"Using custom agents directory: {discovered_agents_dir}")
|
|
269
285
|
elif framework_path and framework_path != Path("__PACKAGED__"):
|
|
270
286
|
# Prioritize templates directory over main agents directory
|
|
271
287
|
templates_dir = (
|
|
@@ -299,7 +315,7 @@ class PathResolver(IPathResolver):
|
|
|
299
315
|
paths = {"project": None, "user": None, "system": None}
|
|
300
316
|
|
|
301
317
|
# Project-specific instructions
|
|
302
|
-
project_path =
|
|
318
|
+
project_path = self._get_working_dir() / ".claude-mpm" / "INSTRUCTIONS.md"
|
|
303
319
|
if project_path.exists():
|
|
304
320
|
paths["project"] = project_path
|
|
305
321
|
|
|
@@ -423,11 +439,11 @@ class PathResolver(IPathResolver):
|
|
|
423
439
|
"""Check common locations for claude-mpm."""
|
|
424
440
|
candidates = [
|
|
425
441
|
# Current directory (if we're already in claude-mpm)
|
|
426
|
-
|
|
442
|
+
self._get_working_dir(),
|
|
427
443
|
# Development location
|
|
428
444
|
Path.home() / "Projects" / "claude-mpm",
|
|
429
445
|
# Current directory subdirectory
|
|
430
|
-
|
|
446
|
+
self._get_working_dir() / "claude-mpm",
|
|
431
447
|
]
|
|
432
448
|
|
|
433
449
|
for candidate in candidates:
|
|
@@ -487,7 +503,7 @@ class PathResolver(IPathResolver):
|
|
|
487
503
|
pass
|
|
488
504
|
|
|
489
505
|
# Check if we're in development
|
|
490
|
-
if (
|
|
506
|
+
if (self._get_working_dir() / "pyproject.toml").exists():
|
|
491
507
|
return DeploymentContext.DEVELOPMENT
|
|
492
508
|
|
|
493
509
|
return DeploymentContext.UNKNOWN
|
|
@@ -13,6 +13,6 @@ DESIGN DECISIONS:
|
|
|
13
13
|
|
|
14
14
|
from .diagnostic_runner import DiagnosticRunner
|
|
15
15
|
from .doctor_reporter import DoctorReporter
|
|
16
|
-
from .models import DiagnosticResult
|
|
16
|
+
from .models import DiagnosticResult
|
|
17
17
|
|
|
18
|
-
__all__ = ["DiagnosticResult", "DiagnosticRunner", "
|
|
18
|
+
__all__ = ["DiagnosticResult", "DiagnosticRunner", "DoctorReporter"]
|
|
@@ -6,7 +6,8 @@ WHY: Verify that agents are properly deployed, up-to-date, and functioning corre
|
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from ....core.enums import OperationResult, ValidationSeverity
|
|
10
|
+
from ..models import DiagnosticResult
|
|
10
11
|
from .base_check import BaseDiagnosticCheck
|
|
11
12
|
|
|
12
13
|
|
|
@@ -64,29 +65,29 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
64
65
|
available_count = details["available_count"]
|
|
65
66
|
|
|
66
67
|
if deployed_count == 0:
|
|
67
|
-
status =
|
|
68
|
+
status = ValidationSeverity.ERROR
|
|
68
69
|
message = f"No agents deployed (0/{available_count} available)"
|
|
69
70
|
fix_command = "claude-mpm agents deploy"
|
|
70
71
|
fix_description = "Deploy all available agents"
|
|
71
72
|
elif deployed_count < available_count:
|
|
72
|
-
status =
|
|
73
|
+
status = ValidationSeverity.WARNING
|
|
73
74
|
message = f"{deployed_count}/{available_count} agents deployed"
|
|
74
75
|
fix_command = "claude-mpm agents deploy"
|
|
75
76
|
fix_description = (
|
|
76
77
|
f"Deploy remaining {available_count - deployed_count} agents"
|
|
77
78
|
)
|
|
78
|
-
elif any(r.status ==
|
|
79
|
-
status =
|
|
79
|
+
elif any(r.status == ValidationSeverity.ERROR for r in sub_results):
|
|
80
|
+
status = ValidationSeverity.ERROR
|
|
80
81
|
message = "Agents have critical issues"
|
|
81
82
|
fix_command = None
|
|
82
83
|
fix_description = None
|
|
83
|
-
elif any(r.status ==
|
|
84
|
-
status =
|
|
84
|
+
elif any(r.status == ValidationSeverity.WARNING for r in sub_results):
|
|
85
|
+
status = ValidationSeverity.WARNING
|
|
85
86
|
message = "Agents have minor issues"
|
|
86
87
|
fix_command = None
|
|
87
88
|
fix_description = None
|
|
88
89
|
else:
|
|
89
|
-
status =
|
|
90
|
+
status = OperationResult.SUCCESS
|
|
90
91
|
message = f"All {deployed_count} agents properly deployed"
|
|
91
92
|
fix_command = None
|
|
92
93
|
fix_description = None
|
|
@@ -104,7 +105,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
104
105
|
except Exception as e:
|
|
105
106
|
return DiagnosticResult(
|
|
106
107
|
category=self.category,
|
|
107
|
-
status=
|
|
108
|
+
status=ValidationSeverity.ERROR,
|
|
108
109
|
message=f"Agent check failed: {e!s}",
|
|
109
110
|
details={"error": str(e)},
|
|
110
111
|
)
|
|
@@ -127,7 +128,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
127
128
|
# Neither exists, default to user directory for error message
|
|
128
129
|
return DiagnosticResult(
|
|
129
130
|
category="Deployed Agents",
|
|
130
|
-
status=
|
|
131
|
+
status=ValidationSeverity.ERROR,
|
|
131
132
|
message="No agents directory found (checked project and user)",
|
|
132
133
|
details={
|
|
133
134
|
"project_path": str(project_agents_dir),
|
|
@@ -144,7 +145,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
144
145
|
if not agent_files:
|
|
145
146
|
return DiagnosticResult(
|
|
146
147
|
category="Deployed Agents",
|
|
147
|
-
status=
|
|
148
|
+
status=ValidationSeverity.ERROR,
|
|
148
149
|
message=f"No agents deployed in {location} directory",
|
|
149
150
|
details={"path": str(agents_dir), "location": location, "count": 0},
|
|
150
151
|
fix_command="claude-mpm agents deploy",
|
|
@@ -159,7 +160,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
159
160
|
if missing_core:
|
|
160
161
|
return DiagnosticResult(
|
|
161
162
|
category="Deployed Agents",
|
|
162
|
-
status=
|
|
163
|
+
status=ValidationSeverity.WARNING,
|
|
163
164
|
message=f"Missing core agents in {location}: {', '.join(missing_core)}",
|
|
164
165
|
details={
|
|
165
166
|
"path": str(agents_dir),
|
|
@@ -174,7 +175,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
174
175
|
|
|
175
176
|
return DiagnosticResult(
|
|
176
177
|
category="Deployed Agents",
|
|
177
|
-
status=
|
|
178
|
+
status=OperationResult.SUCCESS,
|
|
178
179
|
message=f"{len(agent_files)} agents deployed ({location} level)",
|
|
179
180
|
details={
|
|
180
181
|
"path": str(agents_dir),
|
|
@@ -205,7 +206,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
205
206
|
else:
|
|
206
207
|
return DiagnosticResult(
|
|
207
208
|
category="Agent Versions",
|
|
208
|
-
status=
|
|
209
|
+
status=OperationResult.SKIPPED,
|
|
209
210
|
message="No agents to check",
|
|
210
211
|
details={},
|
|
211
212
|
)
|
|
@@ -224,7 +225,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
224
225
|
if outdated:
|
|
225
226
|
return DiagnosticResult(
|
|
226
227
|
category="Agent Versions",
|
|
227
|
-
status=
|
|
228
|
+
status=ValidationSeverity.WARNING,
|
|
228
229
|
message=f"{len(outdated)} agent(s) outdated",
|
|
229
230
|
details={"outdated": outdated, "checked": checked},
|
|
230
231
|
fix_command="claude-mpm agents update",
|
|
@@ -234,14 +235,14 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
234
235
|
if checked == 0:
|
|
235
236
|
return DiagnosticResult(
|
|
236
237
|
category="Agent Versions",
|
|
237
|
-
status=
|
|
238
|
+
status=ValidationSeverity.WARNING,
|
|
238
239
|
message="No agents to check",
|
|
239
240
|
details={"checked": 0},
|
|
240
241
|
)
|
|
241
242
|
|
|
242
243
|
return DiagnosticResult(
|
|
243
244
|
category="Agent Versions",
|
|
244
|
-
status=
|
|
245
|
+
status=OperationResult.SUCCESS,
|
|
245
246
|
message=f"All {checked} agents up-to-date",
|
|
246
247
|
details={"checked": checked},
|
|
247
248
|
)
|
|
@@ -249,7 +250,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
249
250
|
except Exception as e:
|
|
250
251
|
return DiagnosticResult(
|
|
251
252
|
category="Agent Versions",
|
|
252
|
-
status=
|
|
253
|
+
status=ValidationSeverity.WARNING,
|
|
253
254
|
message=f"Could not check versions: {e!s}",
|
|
254
255
|
details={"error": str(e)},
|
|
255
256
|
)
|
|
@@ -273,7 +274,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
273
274
|
else:
|
|
274
275
|
return DiagnosticResult(
|
|
275
276
|
category="Agent Validation",
|
|
276
|
-
status=
|
|
277
|
+
status=OperationResult.SKIPPED,
|
|
277
278
|
message="No agents to validate",
|
|
278
279
|
details={},
|
|
279
280
|
)
|
|
@@ -301,14 +302,14 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
301
302
|
if invalid:
|
|
302
303
|
return DiagnosticResult(
|
|
303
304
|
category="Agent Validation",
|
|
304
|
-
status=
|
|
305
|
+
status=ValidationSeverity.WARNING,
|
|
305
306
|
message=f"{len(invalid)} validation issue(s)",
|
|
306
307
|
details={"issues": invalid, "validated": validated},
|
|
307
308
|
)
|
|
308
309
|
|
|
309
310
|
return DiagnosticResult(
|
|
310
311
|
category="Agent Validation",
|
|
311
|
-
status=
|
|
312
|
+
status=OperationResult.SUCCESS,
|
|
312
313
|
message=f"All {validated} agents valid",
|
|
313
314
|
details={"validated": validated},
|
|
314
315
|
)
|
|
@@ -316,7 +317,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
316
317
|
except Exception as e:
|
|
317
318
|
return DiagnosticResult(
|
|
318
319
|
category="Agent Validation",
|
|
319
|
-
status=
|
|
320
|
+
status=ValidationSeverity.WARNING,
|
|
320
321
|
message=f"Validation failed: {e!s}",
|
|
321
322
|
details={"error": str(e)},
|
|
322
323
|
)
|
|
@@ -358,14 +359,14 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
358
359
|
if issues:
|
|
359
360
|
return DiagnosticResult(
|
|
360
361
|
category="Common Issues",
|
|
361
|
-
status=
|
|
362
|
+
status=ValidationSeverity.WARNING,
|
|
362
363
|
message=f"{len(issues)} issue(s) found",
|
|
363
364
|
details={"issues": issues},
|
|
364
365
|
)
|
|
365
366
|
|
|
366
367
|
return DiagnosticResult(
|
|
367
368
|
category="Common Issues",
|
|
368
|
-
status=
|
|
369
|
+
status=OperationResult.SUCCESS,
|
|
369
370
|
message="No common issues detected",
|
|
370
371
|
details={},
|
|
371
372
|
)
|