claude-mpm 4.0.29__py3-none-any.whl → 4.0.30__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/agents/BASE_AGENT_TEMPLATE.md +48 -3
- claude_mpm/agents/BASE_PM.md +20 -15
- claude_mpm/agents/INSTRUCTIONS.md +12 -2
- claude_mpm/agents/templates/documentation.json +16 -3
- claude_mpm/agents/templates/engineer.json +19 -5
- claude_mpm/agents/templates/ops.json +19 -5
- claude_mpm/agents/templates/qa.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +25 -7
- claude_mpm/agents/templates/research.json +19 -5
- claude_mpm/cli/__init__.py +2 -0
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/agent_manager.py +10 -6
- claude_mpm/cli/commands/agents.py +2 -1
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/doctor.py +209 -0
- claude_mpm/cli/commands/mcp.py +3 -3
- claude_mpm/cli/commands/mcp_install_commands.py +12 -30
- claude_mpm/cli/commands/mcp_server_commands.py +9 -9
- claude_mpm/cli/commands/run.py +31 -2
- claude_mpm/cli/commands/run_config_checker.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
- claude_mpm/cli/parsers/base_parser.py +5 -1
- claude_mpm/cli/parsers/mcp_parser.py +1 -1
- claude_mpm/cli/parsers/run_parser.py +1 -1
- claude_mpm/cli/startup_logging.py +463 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +78 -0
- claude_mpm/core/framework_loader.py +45 -11
- claude_mpm/core/interactive_session.py +82 -3
- claude_mpm/core/output_style_manager.py +6 -6
- claude_mpm/core/unified_paths.py +128 -0
- claude_mpm/scripts/mcp_server.py +2 -2
- claude_mpm/services/agents/deployment/agent_validator.py +1 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +69 -1
- claude_mpm/services/diagnostics/__init__.py +18 -0
- claude_mpm/services/diagnostics/checks/__init__.py +30 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +319 -0
- claude_mpm/services/diagnostics/checks/base_check.py +64 -0
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +283 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +354 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +300 -0
- claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +255 -0
- claude_mpm/services/diagnostics/checks/mcp_check.py +315 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +282 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +322 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +247 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +283 -0
- claude_mpm/services/diagnostics/models.py +120 -0
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -1
- claude_mpm/services/mcp_gateway/main.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +3 -3
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -3
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +2 -2
- claude_mpm/services/socketio/handlers/registry.py +39 -7
- claude_mpm/services/socketio/server/core.py +72 -22
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/METADATA +4 -1
- {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/RECORD +64 -47
- {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/WHEEL +0 -0
- {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check filesystem permissions and disk space.
|
|
3
|
+
|
|
4
|
+
WHY: Verify that claude-mpm has proper filesystem access and sufficient
|
|
5
|
+
disk space for operation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import shutil
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any
|
|
12
|
+
|
|
13
|
+
from ..models import DiagnosticResult, DiagnosticStatus
|
|
14
|
+
from .base_check import BaseDiagnosticCheck
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FilesystemCheck(BaseDiagnosticCheck):
|
|
18
|
+
"""Check filesystem health and permissions."""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def name(self) -> str:
|
|
22
|
+
return "filesystem_check"
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def category(self) -> str:
|
|
26
|
+
return "File System"
|
|
27
|
+
|
|
28
|
+
def run(self) -> DiagnosticResult:
|
|
29
|
+
"""Run filesystem diagnostics."""
|
|
30
|
+
try:
|
|
31
|
+
sub_results = []
|
|
32
|
+
details = {}
|
|
33
|
+
|
|
34
|
+
# Check critical directory permissions
|
|
35
|
+
perm_result = self._check_permissions()
|
|
36
|
+
sub_results.append(perm_result)
|
|
37
|
+
details["permissions"] = perm_result.details
|
|
38
|
+
|
|
39
|
+
# Check disk space
|
|
40
|
+
space_result = self._check_disk_space()
|
|
41
|
+
sub_results.append(space_result)
|
|
42
|
+
details["disk_space"] = space_result.details
|
|
43
|
+
|
|
44
|
+
# Check .claude directory structure
|
|
45
|
+
structure_result = self._check_directory_structure()
|
|
46
|
+
sub_results.append(structure_result)
|
|
47
|
+
details["structure"] = structure_result.details
|
|
48
|
+
|
|
49
|
+
# Determine overall status
|
|
50
|
+
if any(r.status == DiagnosticStatus.ERROR for r in sub_results):
|
|
51
|
+
status = DiagnosticStatus.ERROR
|
|
52
|
+
message = "File system has critical issues"
|
|
53
|
+
elif any(r.status == DiagnosticStatus.WARNING for r in sub_results):
|
|
54
|
+
status = DiagnosticStatus.WARNING
|
|
55
|
+
message = "File system has minor issues"
|
|
56
|
+
else:
|
|
57
|
+
status = DiagnosticStatus.OK
|
|
58
|
+
message = "File system healthy"
|
|
59
|
+
|
|
60
|
+
return DiagnosticResult(
|
|
61
|
+
category=self.category,
|
|
62
|
+
status=status,
|
|
63
|
+
message=message,
|
|
64
|
+
details=details,
|
|
65
|
+
sub_results=sub_results if self.verbose else []
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
return DiagnosticResult(
|
|
70
|
+
category=self.category,
|
|
71
|
+
status=DiagnosticStatus.ERROR,
|
|
72
|
+
message=f"Filesystem check failed: {str(e)}",
|
|
73
|
+
details={"error": str(e)}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def _check_permissions(self) -> DiagnosticResult:
|
|
77
|
+
"""Check permissions for critical directories."""
|
|
78
|
+
critical_dirs = [
|
|
79
|
+
Path.home() / ".claude",
|
|
80
|
+
Path.home() / ".claude" / "agents",
|
|
81
|
+
Path.home() / ".claude" / "responses",
|
|
82
|
+
Path.cwd() / ".claude"
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
issues = []
|
|
86
|
+
checked = []
|
|
87
|
+
|
|
88
|
+
for dir_path in critical_dirs:
|
|
89
|
+
if dir_path.exists():
|
|
90
|
+
checked.append(str(dir_path))
|
|
91
|
+
|
|
92
|
+
# Check read permission
|
|
93
|
+
if not os.access(dir_path, os.R_OK):
|
|
94
|
+
issues.append(f"{dir_path} not readable")
|
|
95
|
+
|
|
96
|
+
# Check write permission
|
|
97
|
+
if not os.access(dir_path, os.W_OK):
|
|
98
|
+
issues.append(f"{dir_path} not writable")
|
|
99
|
+
|
|
100
|
+
# Check execute permission (needed to list directory)
|
|
101
|
+
if not os.access(dir_path, os.X_OK):
|
|
102
|
+
issues.append(f"{dir_path} not accessible")
|
|
103
|
+
|
|
104
|
+
if issues:
|
|
105
|
+
return DiagnosticResult(
|
|
106
|
+
category="Permissions",
|
|
107
|
+
status=DiagnosticStatus.ERROR,
|
|
108
|
+
message=f"{len(issues)} permission issue(s)",
|
|
109
|
+
details={"issues": issues, "checked": checked},
|
|
110
|
+
fix_command="chmod -R 755 ~/.claude",
|
|
111
|
+
fix_description="Fix directory permissions"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if not checked:
|
|
115
|
+
return DiagnosticResult(
|
|
116
|
+
category="Permissions",
|
|
117
|
+
status=DiagnosticStatus.WARNING,
|
|
118
|
+
message="No claude directories found",
|
|
119
|
+
details={"checked": [], "missing": [str(d) for d in critical_dirs]}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return DiagnosticResult(
|
|
123
|
+
category="Permissions",
|
|
124
|
+
status=DiagnosticStatus.OK,
|
|
125
|
+
message="All permissions valid",
|
|
126
|
+
details={"checked": checked}
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def _check_disk_space(self) -> DiagnosticResult:
|
|
130
|
+
"""Check available disk space."""
|
|
131
|
+
try:
|
|
132
|
+
# Check home directory disk usage
|
|
133
|
+
home_path = Path.home()
|
|
134
|
+
stat = shutil.disk_usage(home_path)
|
|
135
|
+
|
|
136
|
+
free_gb = stat.free / (1024 ** 3)
|
|
137
|
+
total_gb = stat.total / (1024 ** 3)
|
|
138
|
+
used_percent = (stat.used / stat.total) * 100
|
|
139
|
+
|
|
140
|
+
details = {
|
|
141
|
+
"free_gb": round(free_gb, 2),
|
|
142
|
+
"total_gb": round(total_gb, 2),
|
|
143
|
+
"used_percent": round(used_percent, 1),
|
|
144
|
+
"path": str(home_path)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Check for low disk space
|
|
148
|
+
if free_gb < 0.1: # Less than 100MB
|
|
149
|
+
return DiagnosticResult(
|
|
150
|
+
category="Disk Space",
|
|
151
|
+
status=DiagnosticStatus.ERROR,
|
|
152
|
+
message=f"Critical: Only {free_gb:.2f}GB free",
|
|
153
|
+
details=details,
|
|
154
|
+
fix_description="Free up disk space immediately"
|
|
155
|
+
)
|
|
156
|
+
elif free_gb < 1: # Less than 1GB
|
|
157
|
+
return DiagnosticResult(
|
|
158
|
+
category="Disk Space",
|
|
159
|
+
status=DiagnosticStatus.WARNING,
|
|
160
|
+
message=f"Low disk space: {free_gb:.2f}GB free",
|
|
161
|
+
details=details,
|
|
162
|
+
fix_description="Consider freeing up disk space"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
return DiagnosticResult(
|
|
166
|
+
category="Disk Space",
|
|
167
|
+
status=DiagnosticStatus.OK,
|
|
168
|
+
message=f"{free_gb:.1f}GB available",
|
|
169
|
+
details=details
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
return DiagnosticResult(
|
|
174
|
+
category="Disk Space",
|
|
175
|
+
status=DiagnosticStatus.WARNING,
|
|
176
|
+
message=f"Could not check disk space: {str(e)}",
|
|
177
|
+
details={"error": str(e)}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def _check_directory_structure(self) -> DiagnosticResult:
|
|
181
|
+
"""Check claude directory structure."""
|
|
182
|
+
base_dir = Path.home() / ".claude"
|
|
183
|
+
|
|
184
|
+
expected_dirs = {
|
|
185
|
+
"agents": "Agent deployment directory",
|
|
186
|
+
"responses": "Response logging directory",
|
|
187
|
+
"memory": "Agent memory storage",
|
|
188
|
+
"logs": "Application logs"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
missing = []
|
|
192
|
+
present = []
|
|
193
|
+
|
|
194
|
+
if not base_dir.exists():
|
|
195
|
+
return DiagnosticResult(
|
|
196
|
+
category="Directory Structure",
|
|
197
|
+
status=DiagnosticStatus.WARNING,
|
|
198
|
+
message="Base .claude directory missing",
|
|
199
|
+
details={"base_dir": str(base_dir), "exists": False},
|
|
200
|
+
fix_command="mkdir -p ~/.claude/{agents,responses,memory,logs}",
|
|
201
|
+
fix_description="Create claude directory structure"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
for dir_name, description in expected_dirs.items():
|
|
205
|
+
dir_path = base_dir / dir_name
|
|
206
|
+
if dir_path.exists():
|
|
207
|
+
present.append(dir_name)
|
|
208
|
+
else:
|
|
209
|
+
missing.append(dir_name)
|
|
210
|
+
|
|
211
|
+
if missing:
|
|
212
|
+
return DiagnosticResult(
|
|
213
|
+
category="Directory Structure",
|
|
214
|
+
status=DiagnosticStatus.WARNING,
|
|
215
|
+
message=f"Missing {len(missing)} subdirectory(s)",
|
|
216
|
+
details={
|
|
217
|
+
"base_dir": str(base_dir),
|
|
218
|
+
"missing": missing,
|
|
219
|
+
"present": present
|
|
220
|
+
},
|
|
221
|
+
fix_command=f"mkdir -p ~/.claude/{{{','.join(missing)}}}",
|
|
222
|
+
fix_description="Create missing directories"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return DiagnosticResult(
|
|
226
|
+
category="Directory Structure",
|
|
227
|
+
status=DiagnosticStatus.OK,
|
|
228
|
+
message="Directory structure complete",
|
|
229
|
+
details={
|
|
230
|
+
"base_dir": str(base_dir),
|
|
231
|
+
"directories": present
|
|
232
|
+
}
|
|
233
|
+
)
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check claude-mpm installation health.
|
|
3
|
+
|
|
4
|
+
WHY: Verify that claude-mpm is properly installed with correct Python version,
|
|
5
|
+
dependencies, and installation method.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any
|
|
12
|
+
|
|
13
|
+
from ..models import DiagnosticResult, DiagnosticStatus
|
|
14
|
+
from .base_check import BaseDiagnosticCheck
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InstallationCheck(BaseDiagnosticCheck):
|
|
18
|
+
"""Check claude-mpm installation and dependencies."""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def name(self) -> str:
|
|
22
|
+
return "installation_check"
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def category(self) -> str:
|
|
26
|
+
return "Installation"
|
|
27
|
+
|
|
28
|
+
def run(self) -> DiagnosticResult:
|
|
29
|
+
"""Run installation diagnostics."""
|
|
30
|
+
try:
|
|
31
|
+
details = {}
|
|
32
|
+
sub_results = []
|
|
33
|
+
|
|
34
|
+
# Check Python version
|
|
35
|
+
python_result = self._check_python_version()
|
|
36
|
+
sub_results.append(python_result)
|
|
37
|
+
details["python_version"] = python_result.details.get("version")
|
|
38
|
+
|
|
39
|
+
# Check claude-mpm version
|
|
40
|
+
version_result = self._check_claude_mpm_version()
|
|
41
|
+
sub_results.append(version_result)
|
|
42
|
+
details["claude_mpm_version"] = version_result.details.get("version")
|
|
43
|
+
details["build_number"] = version_result.details.get("build_number")
|
|
44
|
+
|
|
45
|
+
# Check installation method
|
|
46
|
+
method_result = self._check_installation_method()
|
|
47
|
+
sub_results.append(method_result)
|
|
48
|
+
details["installation_method"] = method_result.details.get("method")
|
|
49
|
+
|
|
50
|
+
# Check critical dependencies
|
|
51
|
+
deps_result = self._check_dependencies()
|
|
52
|
+
sub_results.append(deps_result)
|
|
53
|
+
details["dependencies"] = deps_result.details.get("status")
|
|
54
|
+
|
|
55
|
+
# Determine overall status
|
|
56
|
+
if any(r.status == DiagnosticStatus.ERROR for r in sub_results):
|
|
57
|
+
status = DiagnosticStatus.ERROR
|
|
58
|
+
message = "Installation has critical issues"
|
|
59
|
+
elif any(r.status == DiagnosticStatus.WARNING for r in sub_results):
|
|
60
|
+
status = DiagnosticStatus.WARNING
|
|
61
|
+
message = "Installation has minor issues"
|
|
62
|
+
else:
|
|
63
|
+
status = DiagnosticStatus.OK
|
|
64
|
+
message = "Installation is healthy"
|
|
65
|
+
|
|
66
|
+
return DiagnosticResult(
|
|
67
|
+
category=self.category,
|
|
68
|
+
status=status,
|
|
69
|
+
message=message,
|
|
70
|
+
details=details,
|
|
71
|
+
sub_results=sub_results if self.verbose else []
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
except Exception as e:
|
|
75
|
+
return DiagnosticResult(
|
|
76
|
+
category=self.category,
|
|
77
|
+
status=DiagnosticStatus.ERROR,
|
|
78
|
+
message=f"Installation check failed: {str(e)}",
|
|
79
|
+
details={"error": str(e)}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def _check_python_version(self) -> DiagnosticResult:
|
|
83
|
+
"""Check Python version compatibility."""
|
|
84
|
+
version = sys.version
|
|
85
|
+
version_info = sys.version_info
|
|
86
|
+
|
|
87
|
+
min_version = (3, 9)
|
|
88
|
+
recommended_version = (3, 11)
|
|
89
|
+
|
|
90
|
+
if version_info < min_version:
|
|
91
|
+
return DiagnosticResult(
|
|
92
|
+
category="Python Version",
|
|
93
|
+
status=DiagnosticStatus.ERROR,
|
|
94
|
+
message=f"Python {version_info.major}.{version_info.minor} is below minimum required {min_version[0]}.{min_version[1]}",
|
|
95
|
+
details={"version": version},
|
|
96
|
+
fix_description="Upgrade Python to 3.9 or higher"
|
|
97
|
+
)
|
|
98
|
+
elif version_info < recommended_version:
|
|
99
|
+
return DiagnosticResult(
|
|
100
|
+
category="Python Version",
|
|
101
|
+
status=DiagnosticStatus.WARNING,
|
|
102
|
+
message=f"Python {version_info.major}.{version_info.minor} works but {recommended_version[0]}.{recommended_version[1]}+ is recommended",
|
|
103
|
+
details={"version": version}
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
return DiagnosticResult(
|
|
107
|
+
category="Python Version",
|
|
108
|
+
status=DiagnosticStatus.OK,
|
|
109
|
+
message=f"Python {version_info.major}.{version_info.minor}.{version_info.micro}",
|
|
110
|
+
details={"version": version}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def _check_claude_mpm_version(self) -> DiagnosticResult:
|
|
114
|
+
"""Check claude-mpm version."""
|
|
115
|
+
try:
|
|
116
|
+
from ....services.version_service import VersionService
|
|
117
|
+
|
|
118
|
+
service = VersionService()
|
|
119
|
+
version = service.get_version()
|
|
120
|
+
semantic_version = service.get_semantic_version()
|
|
121
|
+
build_number = service.get_build_number()
|
|
122
|
+
|
|
123
|
+
return DiagnosticResult(
|
|
124
|
+
category="Claude MPM Version",
|
|
125
|
+
status=DiagnosticStatus.OK,
|
|
126
|
+
message=f"Version: {version}",
|
|
127
|
+
details={
|
|
128
|
+
"version": semantic_version,
|
|
129
|
+
"build_number": build_number,
|
|
130
|
+
"display_version": version
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
return DiagnosticResult(
|
|
135
|
+
category="Claude MPM Version",
|
|
136
|
+
status=DiagnosticStatus.WARNING,
|
|
137
|
+
message="Could not determine version",
|
|
138
|
+
details={"error": str(e)}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def _check_installation_method(self) -> DiagnosticResult:
|
|
142
|
+
"""Detect how claude-mpm was installed."""
|
|
143
|
+
methods_found = []
|
|
144
|
+
|
|
145
|
+
# Check for pipx
|
|
146
|
+
try:
|
|
147
|
+
result = subprocess.run(
|
|
148
|
+
["pipx", "list"],
|
|
149
|
+
capture_output=True,
|
|
150
|
+
text=True,
|
|
151
|
+
timeout=5
|
|
152
|
+
)
|
|
153
|
+
if "claude-mpm" in result.stdout:
|
|
154
|
+
methods_found.append("pipx")
|
|
155
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
# Check for pip installation
|
|
159
|
+
try:
|
|
160
|
+
result = subprocess.run(
|
|
161
|
+
[sys.executable, "-m", "pip", "show", "claude-mpm"],
|
|
162
|
+
capture_output=True,
|
|
163
|
+
text=True,
|
|
164
|
+
timeout=5
|
|
165
|
+
)
|
|
166
|
+
if result.returncode == 0:
|
|
167
|
+
methods_found.append("pip")
|
|
168
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
# Check for development installation
|
|
172
|
+
claude_mpm_path = Path(__file__).parent.parent.parent.parent.parent
|
|
173
|
+
if (claude_mpm_path / "pyproject.toml").exists():
|
|
174
|
+
if (claude_mpm_path / ".git").exists():
|
|
175
|
+
methods_found.append("development")
|
|
176
|
+
|
|
177
|
+
if not methods_found:
|
|
178
|
+
return DiagnosticResult(
|
|
179
|
+
category="Installation Method",
|
|
180
|
+
status=DiagnosticStatus.WARNING,
|
|
181
|
+
message="Installation method unknown",
|
|
182
|
+
details={"method": "unknown"}
|
|
183
|
+
)
|
|
184
|
+
elif "pipx" in methods_found:
|
|
185
|
+
return DiagnosticResult(
|
|
186
|
+
category="Installation Method",
|
|
187
|
+
status=DiagnosticStatus.OK,
|
|
188
|
+
message="Installed via pipx (recommended)",
|
|
189
|
+
details={"method": "pipx", "all_methods": methods_found}
|
|
190
|
+
)
|
|
191
|
+
elif "development" in methods_found:
|
|
192
|
+
return DiagnosticResult(
|
|
193
|
+
category="Installation Method",
|
|
194
|
+
status=DiagnosticStatus.OK,
|
|
195
|
+
message="Development installation",
|
|
196
|
+
details={"method": "development", "all_methods": methods_found}
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
return DiagnosticResult(
|
|
200
|
+
category="Installation Method",
|
|
201
|
+
status=DiagnosticStatus.OK,
|
|
202
|
+
message=f"Installed via {methods_found[0]}",
|
|
203
|
+
details={"method": methods_found[0], "all_methods": methods_found}
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def _check_dependencies(self) -> DiagnosticResult:
|
|
207
|
+
"""Check critical dependencies."""
|
|
208
|
+
missing = []
|
|
209
|
+
warnings = []
|
|
210
|
+
|
|
211
|
+
critical_packages = [
|
|
212
|
+
"aiohttp",
|
|
213
|
+
"click",
|
|
214
|
+
"pyyaml",
|
|
215
|
+
"python-socketio",
|
|
216
|
+
"aiofiles"
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
for package in critical_packages:
|
|
220
|
+
try:
|
|
221
|
+
__import__(package.replace("-", "_"))
|
|
222
|
+
except ImportError:
|
|
223
|
+
missing.append(package)
|
|
224
|
+
|
|
225
|
+
# Check optional but recommended packages
|
|
226
|
+
optional_packages = ["rich", "tabulate"]
|
|
227
|
+
for package in optional_packages:
|
|
228
|
+
try:
|
|
229
|
+
__import__(package)
|
|
230
|
+
except ImportError:
|
|
231
|
+
warnings.append(package)
|
|
232
|
+
|
|
233
|
+
if missing:
|
|
234
|
+
return DiagnosticResult(
|
|
235
|
+
category="Dependencies",
|
|
236
|
+
status=DiagnosticStatus.ERROR,
|
|
237
|
+
message=f"Missing critical dependencies: {', '.join(missing)}",
|
|
238
|
+
details={"missing": missing, "optional_missing": warnings},
|
|
239
|
+
fix_command="pip install -e .",
|
|
240
|
+
fix_description="Reinstall claude-mpm with dependencies"
|
|
241
|
+
)
|
|
242
|
+
elif warnings:
|
|
243
|
+
return DiagnosticResult(
|
|
244
|
+
category="Dependencies",
|
|
245
|
+
status=DiagnosticStatus.WARNING,
|
|
246
|
+
message=f"Missing optional dependencies: {', '.join(warnings)}",
|
|
247
|
+
details={"optional_missing": warnings, "status": "partial"}
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
return DiagnosticResult(
|
|
251
|
+
category="Dependencies",
|
|
252
|
+
status=DiagnosticStatus.OK,
|
|
253
|
+
message="All dependencies installed",
|
|
254
|
+
details={"status": "complete"}
|
|
255
|
+
)
|