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.
Files changed (64) 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/diagnostics/__init__.py +18 -0
  36. claude_mpm/services/diagnostics/checks/__init__.py +30 -0
  37. claude_mpm/services/diagnostics/checks/agent_check.py +319 -0
  38. claude_mpm/services/diagnostics/checks/base_check.py +64 -0
  39. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +283 -0
  40. claude_mpm/services/diagnostics/checks/common_issues_check.py +354 -0
  41. claude_mpm/services/diagnostics/checks/configuration_check.py +300 -0
  42. claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
  43. claude_mpm/services/diagnostics/checks/installation_check.py +255 -0
  44. claude_mpm/services/diagnostics/checks/mcp_check.py +315 -0
  45. claude_mpm/services/diagnostics/checks/monitor_check.py +282 -0
  46. claude_mpm/services/diagnostics/checks/startup_log_check.py +322 -0
  47. claude_mpm/services/diagnostics/diagnostic_runner.py +247 -0
  48. claude_mpm/services/diagnostics/doctor_reporter.py +283 -0
  49. claude_mpm/services/diagnostics/models.py +120 -0
  50. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -1
  51. claude_mpm/services/mcp_gateway/main.py +1 -1
  52. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +3 -3
  53. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  54. claude_mpm/services/mcp_gateway/server/stdio_server.py +3 -3
  55. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +2 -2
  56. claude_mpm/services/socketio/handlers/registry.py +39 -7
  57. claude_mpm/services/socketio/server/core.py +72 -22
  58. claude_mpm/validation/frontmatter_validator.py +1 -1
  59. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/METADATA +4 -1
  60. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/RECORD +64 -47
  61. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/WHEEL +0 -0
  62. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/entry_points.txt +0 -0
  63. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/licenses/LICENSE +0 -0
  64. {claude_mpm-4.0.29.dist-info → claude_mpm-4.0.30.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,319 @@
1
+ """
2
+ Check agent deployment and health.
3
+
4
+ WHY: Verify that agents are properly deployed, up-to-date, and functioning correctly.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Dict, Any, List
9
+
10
+ from ..models import DiagnosticResult, DiagnosticStatus
11
+ from .base_check import BaseDiagnosticCheck
12
+
13
+
14
+ class AgentCheck(BaseDiagnosticCheck):
15
+ """Check agent deployment and configuration."""
16
+
17
+ @property
18
+ def name(self) -> str:
19
+ return "agent_check"
20
+
21
+ @property
22
+ def category(self) -> str:
23
+ return "Agents"
24
+
25
+ def run(self) -> DiagnosticResult:
26
+ """Run agent diagnostics."""
27
+ try:
28
+ from ....services.agents.deployment.agent_validator import AgentValidator
29
+ from ....services.agents.deployment.agent_discovery_service import AgentDiscoveryService
30
+
31
+ sub_results = []
32
+ details = {}
33
+
34
+ # Get available agents
35
+ discovery = AgentDiscoveryService()
36
+ available_agents = discovery.discover_available_agents()
37
+ details["available_count"] = len(available_agents)
38
+ details["available_agents"] = [a.name for a in available_agents]
39
+
40
+ # Check deployed agents
41
+ deployed_result = self._check_deployed_agents()
42
+ sub_results.append(deployed_result)
43
+ details["deployed_count"] = deployed_result.details.get("count", 0)
44
+
45
+ # Check agent versions
46
+ version_result = self._check_agent_versions()
47
+ sub_results.append(version_result)
48
+ details["outdated_agents"] = version_result.details.get("outdated", [])
49
+
50
+ # Validate agent configurations
51
+ validation_result = self._validate_agents()
52
+ sub_results.append(validation_result)
53
+
54
+ # Check for common agent issues
55
+ issues_result = self._check_common_issues()
56
+ sub_results.append(issues_result)
57
+
58
+ # Determine overall status
59
+ deployed_count = details["deployed_count"]
60
+ available_count = details["available_count"]
61
+
62
+ if deployed_count == 0:
63
+ status = DiagnosticStatus.ERROR
64
+ message = f"No agents deployed (0/{available_count} available)"
65
+ fix_command = "claude-mpm agents deploy"
66
+ fix_description = "Deploy all available agents"
67
+ elif deployed_count < available_count:
68
+ status = DiagnosticStatus.WARNING
69
+ message = f"{deployed_count}/{available_count} agents deployed"
70
+ fix_command = "claude-mpm agents deploy"
71
+ fix_description = f"Deploy remaining {available_count - deployed_count} agents"
72
+ elif any(r.status == DiagnosticStatus.ERROR for r in sub_results):
73
+ status = DiagnosticStatus.ERROR
74
+ message = "Agents have critical issues"
75
+ fix_command = None
76
+ fix_description = None
77
+ elif any(r.status == DiagnosticStatus.WARNING for r in sub_results):
78
+ status = DiagnosticStatus.WARNING
79
+ message = "Agents have minor issues"
80
+ fix_command = None
81
+ fix_description = None
82
+ else:
83
+ status = DiagnosticStatus.OK
84
+ message = f"All {deployed_count} agents properly deployed"
85
+ fix_command = None
86
+ fix_description = None
87
+
88
+ return DiagnosticResult(
89
+ category=self.category,
90
+ status=status,
91
+ message=message,
92
+ details=details,
93
+ fix_command=fix_command,
94
+ fix_description=fix_description,
95
+ sub_results=sub_results if self.verbose else []
96
+ )
97
+
98
+ except Exception as e:
99
+ return DiagnosticResult(
100
+ category=self.category,
101
+ status=DiagnosticStatus.ERROR,
102
+ message=f"Agent check failed: {str(e)}",
103
+ details={"error": str(e)}
104
+ )
105
+
106
+ def _check_deployed_agents(self) -> DiagnosticResult:
107
+ """Check deployed agents in user directory."""
108
+ agents_dir = Path.home() / ".claude" / "agents"
109
+
110
+ if not agents_dir.exists():
111
+ return DiagnosticResult(
112
+ category="Deployed Agents",
113
+ status=DiagnosticStatus.ERROR,
114
+ message="Agents directory does not exist",
115
+ details={"path": str(agents_dir), "count": 0},
116
+ fix_command="claude-mpm agents deploy",
117
+ fix_description="Create agents directory and deploy agents"
118
+ )
119
+
120
+ # Count deployed agent files
121
+ agent_files = list(agents_dir.glob("*.md"))
122
+
123
+ if not agent_files:
124
+ return DiagnosticResult(
125
+ category="Deployed Agents",
126
+ status=DiagnosticStatus.ERROR,
127
+ message="No agents deployed",
128
+ details={"path": str(agents_dir), "count": 0},
129
+ fix_command="claude-mpm agents deploy",
130
+ fix_description="Deploy available agents"
131
+ )
132
+
133
+ # Check for required core agents
134
+ core_agents = ["research.md", "engineer.md", "qa.md", "documentation.md"]
135
+ deployed_names = [f.name for f in agent_files]
136
+ missing_core = [a for a in core_agents if a not in deployed_names]
137
+
138
+ if missing_core:
139
+ return DiagnosticResult(
140
+ category="Deployed Agents",
141
+ status=DiagnosticStatus.WARNING,
142
+ message=f"Missing core agents: {', '.join(missing_core)}",
143
+ details={
144
+ "path": str(agents_dir),
145
+ "count": len(agent_files),
146
+ "deployed": deployed_names,
147
+ "missing_core": missing_core
148
+ },
149
+ fix_command="claude-mpm agents deploy",
150
+ fix_description="Deploy missing core agents"
151
+ )
152
+
153
+ return DiagnosticResult(
154
+ category="Deployed Agents",
155
+ status=DiagnosticStatus.OK,
156
+ message=f"{len(agent_files)} agents deployed",
157
+ details={
158
+ "path": str(agents_dir),
159
+ "count": len(agent_files),
160
+ "deployed": deployed_names
161
+ }
162
+ )
163
+
164
+ def _check_agent_versions(self) -> DiagnosticResult:
165
+ """Check if deployed agents are up-to-date."""
166
+ try:
167
+ from ....services.agents.deployment.agent_version_manager import AgentVersionManager
168
+
169
+ version_manager = AgentVersionManager()
170
+ agents_dir = Path.home() / ".claude" / "agents"
171
+
172
+ if not agents_dir.exists():
173
+ return DiagnosticResult(
174
+ category="Agent Versions",
175
+ status=DiagnosticStatus.SKIPPED,
176
+ message="No agents to check",
177
+ details={}
178
+ )
179
+
180
+ outdated = []
181
+ checked = 0
182
+
183
+ for agent_file in agents_dir.glob("*.md"):
184
+ checked += 1
185
+ agent_name = agent_file.stem
186
+
187
+ # Check if agent needs update (simplified check)
188
+ if version_manager.needs_update(agent_name):
189
+ outdated.append(agent_name)
190
+
191
+ if outdated:
192
+ return DiagnosticResult(
193
+ category="Agent Versions",
194
+ status=DiagnosticStatus.WARNING,
195
+ message=f"{len(outdated)} agent(s) outdated",
196
+ details={"outdated": outdated, "checked": checked},
197
+ fix_command="claude-mpm agents update",
198
+ fix_description="Update agents to latest versions"
199
+ )
200
+
201
+ if checked == 0:
202
+ return DiagnosticResult(
203
+ category="Agent Versions",
204
+ status=DiagnosticStatus.WARNING,
205
+ message="No agents to check",
206
+ details={"checked": 0}
207
+ )
208
+
209
+ return DiagnosticResult(
210
+ category="Agent Versions",
211
+ status=DiagnosticStatus.OK,
212
+ message=f"All {checked} agents up-to-date",
213
+ details={"checked": checked}
214
+ )
215
+
216
+ except Exception as e:
217
+ return DiagnosticResult(
218
+ category="Agent Versions",
219
+ status=DiagnosticStatus.WARNING,
220
+ message=f"Could not check versions: {str(e)}",
221
+ details={"error": str(e)}
222
+ )
223
+
224
+ def _validate_agents(self) -> DiagnosticResult:
225
+ """Validate agent configurations."""
226
+ try:
227
+ from ....services.agents.deployment.agent_validator import AgentValidator
228
+
229
+ validator = AgentValidator()
230
+ agents_dir = Path.home() / ".claude" / "agents"
231
+
232
+ if not agents_dir.exists():
233
+ return DiagnosticResult(
234
+ category="Agent Validation",
235
+ status=DiagnosticStatus.SKIPPED,
236
+ message="No agents to validate",
237
+ details={}
238
+ )
239
+
240
+ invalid = []
241
+ validated = 0
242
+
243
+ for agent_file in agents_dir.glob("*.md"):
244
+ validated += 1
245
+
246
+ # Basic validation
247
+ try:
248
+ with open(agent_file, 'r') as f:
249
+ content = f.read()
250
+
251
+ # Check for required sections
252
+ if "## Core Identity" not in content:
253
+ invalid.append(f"{agent_file.stem}: missing Core Identity")
254
+ elif len(content) < 100:
255
+ invalid.append(f"{agent_file.stem}: file too small")
256
+
257
+ except Exception as e:
258
+ invalid.append(f"{agent_file.stem}: {str(e)}")
259
+
260
+ if invalid:
261
+ return DiagnosticResult(
262
+ category="Agent Validation",
263
+ status=DiagnosticStatus.WARNING,
264
+ message=f"{len(invalid)} validation issue(s)",
265
+ details={"issues": invalid, "validated": validated}
266
+ )
267
+
268
+ return DiagnosticResult(
269
+ category="Agent Validation",
270
+ status=DiagnosticStatus.OK,
271
+ message=f"All {validated} agents valid",
272
+ details={"validated": validated}
273
+ )
274
+
275
+ except Exception as e:
276
+ return DiagnosticResult(
277
+ category="Agent Validation",
278
+ status=DiagnosticStatus.WARNING,
279
+ message=f"Validation failed: {str(e)}",
280
+ details={"error": str(e)}
281
+ )
282
+
283
+ def _check_common_issues(self) -> DiagnosticResult:
284
+ """Check for common agent-related issues."""
285
+ issues = []
286
+
287
+ # Check for duplicate agents
288
+ agents_dir = Path.home() / ".claude" / "agents"
289
+ if agents_dir.exists():
290
+ agent_names = {}
291
+ for agent_file in agents_dir.glob("*.md"):
292
+ name = agent_file.stem.lower()
293
+ if name in agent_names:
294
+ issues.append(f"Duplicate agent: {agent_file.stem}")
295
+ else:
296
+ agent_names[name] = agent_file
297
+
298
+ # Check permissions
299
+ if agents_dir.exists():
300
+ import os
301
+ if not os.access(agents_dir, os.R_OK):
302
+ issues.append("Agents directory not readable")
303
+ if not os.access(agents_dir, os.W_OK):
304
+ issues.append("Agents directory not writable")
305
+
306
+ if issues:
307
+ return DiagnosticResult(
308
+ category="Common Issues",
309
+ status=DiagnosticStatus.WARNING,
310
+ message=f"{len(issues)} issue(s) found",
311
+ details={"issues": issues}
312
+ )
313
+
314
+ return DiagnosticResult(
315
+ category="Common Issues",
316
+ status=DiagnosticStatus.OK,
317
+ message="No common issues detected",
318
+ details={}
319
+ )
@@ -0,0 +1,64 @@
1
+ """
2
+ Base interface for diagnostic checks.
3
+
4
+ WHY: Define a consistent interface for all diagnostic checks to ensure
5
+ they can be easily added, tested, and executed by the diagnostic runner.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Optional
10
+
11
+ from ..models import DiagnosticResult
12
+
13
+
14
+ class BaseDiagnosticCheck(ABC):
15
+ """Base class for all diagnostic checks.
16
+
17
+ WHY: Ensures all checks follow the same pattern and can be
18
+ executed uniformly by the diagnostic runner.
19
+ """
20
+
21
+ def __init__(self, verbose: bool = False):
22
+ """Initialize the check.
23
+
24
+ Args:
25
+ verbose: Whether to include detailed information in results
26
+ """
27
+ self.verbose = verbose
28
+
29
+ @property
30
+ @abstractmethod
31
+ def name(self) -> str:
32
+ """Get the name of this check."""
33
+ pass
34
+
35
+ @property
36
+ @abstractmethod
37
+ def category(self) -> str:
38
+ """Get the category this check belongs to."""
39
+ pass
40
+
41
+ @property
42
+ def description(self) -> str:
43
+ """Get a description of what this check does."""
44
+ return f"Checking {self.category.lower()} health"
45
+
46
+ @abstractmethod
47
+ def run(self) -> DiagnosticResult:
48
+ """Run the diagnostic check.
49
+
50
+ Returns:
51
+ DiagnosticResult with the check results
52
+ """
53
+ pass
54
+
55
+ def should_run(self) -> bool:
56
+ """Determine if this check should run.
57
+
58
+ WHY: Some checks may not be applicable in certain environments
59
+ or configurations. This allows checks to opt out gracefully.
60
+
61
+ Returns:
62
+ True if the check should run, False to skip
63
+ """
64
+ return True
@@ -0,0 +1,283 @@
1
+ """
2
+ Check Claude Desktop integration.
3
+
4
+ WHY: Verify that Claude Desktop is installed, properly configured,
5
+ and integrated with claude-mpm.
6
+ """
7
+
8
+ import json
9
+ import subprocess
10
+ from pathlib import Path
11
+ from typing import Dict, Any, Optional
12
+
13
+ from ..models import DiagnosticResult, DiagnosticStatus
14
+ from .base_check import BaseDiagnosticCheck
15
+
16
+
17
+ class ClaudeDesktopCheck(BaseDiagnosticCheck):
18
+ """Check Claude Desktop installation and integration."""
19
+
20
+ @property
21
+ def name(self) -> str:
22
+ return "claude_desktop_check"
23
+
24
+ @property
25
+ def category(self) -> str:
26
+ return "Claude Desktop"
27
+
28
+ def run(self) -> DiagnosticResult:
29
+ """Run Claude Desktop diagnostics."""
30
+ try:
31
+ sub_results = []
32
+ details = {}
33
+
34
+ # Check if Claude Desktop is installed
35
+ install_result = self._check_installation()
36
+ sub_results.append(install_result)
37
+ details["installed"] = install_result.status == DiagnosticStatus.OK
38
+
39
+ if install_result.status == DiagnosticStatus.OK:
40
+ # Check version compatibility
41
+ version_result = self._check_version()
42
+ sub_results.append(version_result)
43
+ details["version"] = version_result.details.get("version")
44
+
45
+ # Check output style deployment
46
+ style_result = self._check_output_style()
47
+ sub_results.append(style_result)
48
+ details["output_style"] = style_result.details.get("deployed")
49
+
50
+ # Check MCP integration
51
+ mcp_result = self._check_mcp_integration()
52
+ sub_results.append(mcp_result)
53
+ details["mcp_configured"] = mcp_result.status == DiagnosticStatus.OK
54
+
55
+ # Determine overall status
56
+ if any(r.status == DiagnosticStatus.ERROR for r in sub_results):
57
+ status = DiagnosticStatus.ERROR
58
+ message = "Claude Desktop has critical issues"
59
+ elif any(r.status == DiagnosticStatus.WARNING for r in sub_results):
60
+ status = DiagnosticStatus.WARNING
61
+ message = "Claude Desktop needs configuration"
62
+ else:
63
+ status = DiagnosticStatus.OK
64
+ message = "Claude Desktop properly configured"
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"Claude Desktop check failed: {str(e)}",
79
+ details={"error": str(e)}
80
+ )
81
+
82
+ def _check_installation(self) -> DiagnosticResult:
83
+ """Check if Claude Desktop is installed."""
84
+ # Check common installation paths
85
+ mac_path = Path("/Applications/Claude.app")
86
+ linux_paths = [
87
+ Path.home() / ".local/share/applications/claude.desktop",
88
+ Path("/usr/share/applications/claude.desktop"),
89
+ Path("/opt/Claude")
90
+ ]
91
+ windows_paths = [
92
+ Path("C:/Program Files/Claude/Claude.exe"),
93
+ Path.home() / "AppData/Local/Claude/Claude.exe"
94
+ ]
95
+
96
+ # Check for Claude process
97
+ try:
98
+ result = subprocess.run(
99
+ ["pgrep", "-f", "Claude"],
100
+ capture_output=True,
101
+ timeout=2
102
+ )
103
+ if result.returncode == 0:
104
+ return DiagnosticResult(
105
+ category="Claude Desktop Installation",
106
+ status=DiagnosticStatus.OK,
107
+ message="Claude Desktop is running",
108
+ details={"running": True}
109
+ )
110
+ except (subprocess.SubprocessError, FileNotFoundError):
111
+ pass
112
+
113
+ # Check installation paths
114
+ if mac_path.exists():
115
+ return DiagnosticResult(
116
+ category="Claude Desktop Installation",
117
+ status=DiagnosticStatus.OK,
118
+ message="Claude Desktop installed (macOS)",
119
+ details={"path": str(mac_path), "platform": "macos"}
120
+ )
121
+
122
+ for path in linux_paths:
123
+ if path.exists():
124
+ return DiagnosticResult(
125
+ category="Claude Desktop Installation",
126
+ status=DiagnosticStatus.OK,
127
+ message="Claude Desktop installed (Linux)",
128
+ details={"path": str(path), "platform": "linux"}
129
+ )
130
+
131
+ for path in windows_paths:
132
+ if path.exists():
133
+ return DiagnosticResult(
134
+ category="Claude Desktop Installation",
135
+ status=DiagnosticStatus.OK,
136
+ message="Claude Desktop installed (Windows)",
137
+ details={"path": str(path), "platform": "windows"}
138
+ )
139
+
140
+ return DiagnosticResult(
141
+ category="Claude Desktop Installation",
142
+ status=DiagnosticStatus.WARNING,
143
+ message="Claude Desktop not found",
144
+ details={"installed": False},
145
+ fix_description="Install Claude Desktop from https://claude.ai/download"
146
+ )
147
+
148
+ def _check_version(self) -> DiagnosticResult:
149
+ """Check Claude Desktop version compatibility."""
150
+ # Try to get version from config file
151
+ config_paths = [
152
+ Path.home() / "Library/Application Support/Claude/config.json", # macOS
153
+ Path.home() / ".config/Claude/config.json", # Linux
154
+ Path.home() / "AppData/Roaming/Claude/config.json" # Windows
155
+ ]
156
+
157
+ for config_path in config_paths:
158
+ if config_path.exists():
159
+ try:
160
+ with open(config_path, 'r') as f:
161
+ config = json.load(f)
162
+ version = config.get("version", "unknown")
163
+
164
+ # Simple version check (would need real version comparison logic)
165
+ return DiagnosticResult(
166
+ category="Claude Desktop Version",
167
+ status=DiagnosticStatus.OK,
168
+ message=f"Version: {version}",
169
+ details={"version": version, "config_path": str(config_path)}
170
+ )
171
+ except Exception:
172
+ pass
173
+
174
+ return DiagnosticResult(
175
+ category="Claude Desktop Version",
176
+ status=DiagnosticStatus.WARNING,
177
+ message="Could not determine version",
178
+ details={"version": "unknown"}
179
+ )
180
+
181
+ def _check_output_style(self) -> DiagnosticResult:
182
+ """Check if output style is deployed."""
183
+ style_path = Path.home() / ".claude/responses/OUTPUT_STYLE.md"
184
+
185
+ if not style_path.exists():
186
+ return DiagnosticResult(
187
+ category="Output Style",
188
+ status=DiagnosticStatus.WARNING,
189
+ message="Output style not deployed",
190
+ details={"deployed": False, "path": str(style_path)},
191
+ fix_command="claude-mpm deploy-style",
192
+ fix_description="Deploy claude-mpm output style for better formatting"
193
+ )
194
+
195
+ # Check if it's up to date
196
+ try:
197
+ with open(style_path, 'r') as f:
198
+ content = f.read()
199
+ if "Claude MPM Output Style" in content:
200
+ return DiagnosticResult(
201
+ category="Output Style",
202
+ status=DiagnosticStatus.OK,
203
+ message="Output style deployed",
204
+ details={"deployed": True, "path": str(style_path)}
205
+ )
206
+ else:
207
+ return DiagnosticResult(
208
+ category="Output Style",
209
+ status=DiagnosticStatus.WARNING,
210
+ message="Output style outdated",
211
+ details={"deployed": True, "outdated": True, "path": str(style_path)},
212
+ fix_command="claude-mpm deploy-style --force",
213
+ fix_description="Update output style to latest version"
214
+ )
215
+ except Exception as e:
216
+ return DiagnosticResult(
217
+ category="Output Style",
218
+ status=DiagnosticStatus.WARNING,
219
+ message=f"Could not check output style: {str(e)}",
220
+ details={"error": str(e)}
221
+ )
222
+
223
+ def _check_mcp_integration(self) -> DiagnosticResult:
224
+ """Check MCP server integration with Claude Desktop."""
225
+ config_path = Path.home() / ".config/claude/claude_desktop_config.json"
226
+
227
+ if not config_path.exists():
228
+ # Try alternate paths
229
+ alt_paths = [
230
+ Path.home() / "Library/Application Support/Claude/claude_desktop_config.json",
231
+ Path.home() / "AppData/Roaming/Claude/claude_desktop_config.json"
232
+ ]
233
+ for alt_path in alt_paths:
234
+ if alt_path.exists():
235
+ config_path = alt_path
236
+ break
237
+ else:
238
+ return DiagnosticResult(
239
+ category="MCP Integration",
240
+ status=DiagnosticStatus.WARNING,
241
+ message="Claude Desktop config not found",
242
+ details={"configured": False},
243
+ fix_command="claude-mpm mcp install",
244
+ fix_description="Install MCP server integration"
245
+ )
246
+
247
+ try:
248
+ with open(config_path, 'r') as f:
249
+ config = json.load(f)
250
+
251
+ mcp_servers = config.get("mcpServers", {})
252
+ if "claude-mpm-gateway" in mcp_servers:
253
+ return DiagnosticResult(
254
+ category="MCP Integration",
255
+ status=DiagnosticStatus.OK,
256
+ message="MCP server configured",
257
+ details={
258
+ "configured": True,
259
+ "server_count": len(mcp_servers),
260
+ "config_path": str(config_path)
261
+ }
262
+ )
263
+ else:
264
+ return DiagnosticResult(
265
+ category="MCP Integration",
266
+ status=DiagnosticStatus.WARNING,
267
+ message="MCP server not configured",
268
+ details={
269
+ "configured": False,
270
+ "server_count": len(mcp_servers),
271
+ "config_path": str(config_path)
272
+ },
273
+ fix_command="claude-mpm mcp install",
274
+ fix_description="Configure MCP server for Claude Desktop"
275
+ )
276
+
277
+ except Exception as e:
278
+ return DiagnosticResult(
279
+ category="MCP Integration",
280
+ status=DiagnosticStatus.WARNING,
281
+ message=f"Could not check MCP configuration: {str(e)}",
282
+ details={"error": str(e)}
283
+ )