claude-mpm 4.0.29__py3-none-any.whl → 4.0.31__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 (65) hide show
  1. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +48 -3
  2. claude_mpm/agents/BASE_PM.md +20 -15
  3. claude_mpm/agents/INSTRUCTIONS.md +12 -2
  4. claude_mpm/agents/templates/documentation.json +16 -3
  5. claude_mpm/agents/templates/engineer.json +19 -5
  6. claude_mpm/agents/templates/ops.json +19 -5
  7. claude_mpm/agents/templates/qa.json +16 -3
  8. claude_mpm/agents/templates/refactoring_engineer.json +25 -7
  9. claude_mpm/agents/templates/research.json +19 -5
  10. claude_mpm/cli/__init__.py +2 -0
  11. claude_mpm/cli/commands/__init__.py +2 -0
  12. claude_mpm/cli/commands/agent_manager.py +10 -6
  13. claude_mpm/cli/commands/agents.py +2 -1
  14. claude_mpm/cli/commands/cleanup.py +1 -1
  15. claude_mpm/cli/commands/doctor.py +209 -0
  16. claude_mpm/cli/commands/mcp.py +3 -3
  17. claude_mpm/cli/commands/mcp_install_commands.py +12 -30
  18. claude_mpm/cli/commands/mcp_server_commands.py +9 -9
  19. claude_mpm/cli/commands/run.py +31 -2
  20. claude_mpm/cli/commands/run_config_checker.py +1 -1
  21. claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
  22. claude_mpm/cli/parsers/base_parser.py +5 -1
  23. claude_mpm/cli/parsers/mcp_parser.py +1 -1
  24. claude_mpm/cli/parsers/run_parser.py +1 -1
  25. claude_mpm/cli/startup_logging.py +463 -0
  26. claude_mpm/constants.py +1 -0
  27. claude_mpm/core/claude_runner.py +78 -0
  28. claude_mpm/core/framework_loader.py +45 -11
  29. claude_mpm/core/interactive_session.py +82 -3
  30. claude_mpm/core/output_style_manager.py +6 -6
  31. claude_mpm/core/unified_paths.py +128 -0
  32. claude_mpm/scripts/mcp_server.py +2 -2
  33. claude_mpm/services/agents/deployment/agent_validator.py +1 -0
  34. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +69 -1
  35. claude_mpm/services/agents/deployment/system_instructions_deployer.py +2 -2
  36. claude_mpm/services/diagnostics/__init__.py +18 -0
  37. claude_mpm/services/diagnostics/checks/__init__.py +30 -0
  38. claude_mpm/services/diagnostics/checks/agent_check.py +319 -0
  39. claude_mpm/services/diagnostics/checks/base_check.py +64 -0
  40. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +283 -0
  41. claude_mpm/services/diagnostics/checks/common_issues_check.py +354 -0
  42. claude_mpm/services/diagnostics/checks/configuration_check.py +300 -0
  43. claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
  44. claude_mpm/services/diagnostics/checks/installation_check.py +255 -0
  45. claude_mpm/services/diagnostics/checks/mcp_check.py +315 -0
  46. claude_mpm/services/diagnostics/checks/monitor_check.py +282 -0
  47. claude_mpm/services/diagnostics/checks/startup_log_check.py +322 -0
  48. claude_mpm/services/diagnostics/diagnostic_runner.py +247 -0
  49. claude_mpm/services/diagnostics/doctor_reporter.py +283 -0
  50. claude_mpm/services/diagnostics/models.py +120 -0
  51. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -1
  52. claude_mpm/services/mcp_gateway/main.py +1 -1
  53. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +3 -3
  54. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  55. claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -3
  56. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +2 -2
  57. claude_mpm/services/socketio/handlers/registry.py +39 -7
  58. claude_mpm/services/socketio/server/core.py +72 -22
  59. claude_mpm/validation/frontmatter_validator.py +1 -1
  60. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.31.dist-info}/METADATA +4 -1
  61. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.31.dist-info}/RECORD +65 -48
  62. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.31.dist-info}/WHEEL +0 -0
  63. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.31.dist-info}/entry_points.txt +0 -0
  64. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.31.dist-info}/licenses/LICENSE +0 -0
  65. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.31.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
+ )