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,283 @@
1
+ """
2
+ Reporter for formatting and displaying diagnostic results.
3
+
4
+ WHY: Provide clear, actionable output from diagnostic checks with proper
5
+ formatting for terminal display and JSON export.
6
+ """
7
+
8
+ import json
9
+ import sys
10
+ from typing import Dict, List, Optional
11
+
12
+ from .models import DiagnosticResult, DiagnosticStatus, DiagnosticSummary
13
+
14
+
15
+ class DoctorReporter:
16
+ """Format and display diagnostic results.
17
+
18
+ WHY: Consistent, user-friendly output that clearly shows system health
19
+ status and provides actionable fixes for any issues.
20
+ """
21
+
22
+ # Status symbols and colors
23
+ STATUS_SYMBOLS = {
24
+ DiagnosticStatus.OK: "✅",
25
+ DiagnosticStatus.WARNING: "⚠️ ",
26
+ DiagnosticStatus.ERROR: "❌",
27
+ DiagnosticStatus.SKIPPED: "⏭️ "
28
+ }
29
+
30
+ # ANSI color codes
31
+ COLORS = {
32
+ "reset": "\033[0m",
33
+ "bold": "\033[1m",
34
+ "red": "\033[91m",
35
+ "green": "\033[92m",
36
+ "yellow": "\033[93m",
37
+ "blue": "\033[94m",
38
+ "gray": "\033[90m"
39
+ }
40
+
41
+ def __init__(self, use_color: bool = True, verbose: bool = False):
42
+ """Initialize reporter.
43
+
44
+ Args:
45
+ use_color: Whether to use ANSI color codes
46
+ verbose: Whether to include detailed information
47
+ """
48
+ self.use_color = use_color and sys.stdout.isatty()
49
+ self.verbose = verbose
50
+
51
+ def report(self, summary: DiagnosticSummary, format: str = "terminal"):
52
+ """Generate and output diagnostic report.
53
+
54
+ Args:
55
+ summary: DiagnosticSummary with all results
56
+ format: Output format ("terminal", "json", "markdown")
57
+ """
58
+ if format == "json":
59
+ self._report_json(summary)
60
+ elif format == "markdown":
61
+ self._report_markdown(summary)
62
+ else:
63
+ self._report_terminal(summary)
64
+
65
+ def _report_terminal(self, summary: DiagnosticSummary):
66
+ """Generate terminal-formatted report."""
67
+ # Header
68
+ self._print_header()
69
+
70
+ # Results by category
71
+ for result in summary.results:
72
+ self._print_result(result)
73
+
74
+ # Summary
75
+ self._print_summary(summary)
76
+
77
+ # Fix suggestions
78
+ self._print_fixes(summary)
79
+
80
+ def _print_header(self):
81
+ """Print report header."""
82
+ print()
83
+ print(self._color("Claude MPM Doctor Report", "bold"))
84
+ print("=" * 40)
85
+ print()
86
+
87
+ def _print_result(self, result: DiagnosticResult, indent: int = 0):
88
+ """Print a single diagnostic result."""
89
+ indent_str = " " * indent
90
+
91
+ # Status symbol and category
92
+ symbol = self.STATUS_SYMBOLS.get(result.status, "?")
93
+ color = self._get_status_color(result.status)
94
+
95
+ # Main result line
96
+ line = f"{indent_str}{symbol} {result.category}: "
97
+
98
+ if result.status == DiagnosticStatus.OK:
99
+ line += self._color("OK", color)
100
+ elif result.status == DiagnosticStatus.WARNING:
101
+ line += self._color("Warning", color)
102
+ elif result.status == DiagnosticStatus.ERROR:
103
+ line += self._color("Error", color)
104
+ else:
105
+ line += self._color("Skipped", color)
106
+
107
+ print(line)
108
+
109
+ # Message
110
+ message_indent = " " + indent_str
111
+ print(f"{message_indent}{result.message}")
112
+
113
+ # Details (in verbose mode)
114
+ if self.verbose and result.details:
115
+ for key, value in result.details.items():
116
+ if key not in ["error", "issues"]: # Skip complex fields
117
+ print(f"{message_indent}{self._color(key, 'gray')}: {value}")
118
+
119
+ # Fix suggestion
120
+ if result.fix_command:
121
+ fix_indent = " " + indent_str
122
+ print(f"{fix_indent}{self._color('→ Fix:', 'blue')} Run '{result.fix_command}'")
123
+ if result.fix_description:
124
+ print(f"{fix_indent} {self._color(result.fix_description, 'gray')}")
125
+
126
+ # Sub-results (in verbose mode)
127
+ if self.verbose and result.sub_results:
128
+ for sub_result in result.sub_results:
129
+ self._print_result(sub_result, indent + 1)
130
+
131
+ if indent == 0:
132
+ print() # Extra line between top-level results
133
+
134
+ def _print_summary(self, summary: DiagnosticSummary):
135
+ """Print summary statistics."""
136
+ print(self._color("─" * 40, "gray"))
137
+
138
+ status_line = f"Summary: "
139
+ parts = []
140
+
141
+ if summary.ok_count > 0:
142
+ parts.append(self._color(f"{summary.ok_count} OK", "green"))
143
+ if summary.warning_count > 0:
144
+ parts.append(self._color(f"{summary.warning_count} Warning{'s' if summary.warning_count != 1 else ''}", "yellow"))
145
+ if summary.error_count > 0:
146
+ parts.append(self._color(f"{summary.error_count} Error{'s' if summary.error_count != 1 else ''}", "red"))
147
+ if summary.skipped_count > 0:
148
+ parts.append(self._color(f"{summary.skipped_count} Skipped", "gray"))
149
+
150
+ status_line += " | ".join(parts)
151
+ print(status_line)
152
+
153
+ # Overall health
154
+ overall = summary.overall_status
155
+ if overall == DiagnosticStatus.OK:
156
+ print(self._color("\n✅ System is healthy!", "green"))
157
+ elif overall == DiagnosticStatus.WARNING:
158
+ print(self._color("\n⚠️ System has minor issues that should be addressed.", "yellow"))
159
+ else:
160
+ print(self._color("\n❌ System has critical issues that need immediate attention!", "red"))
161
+
162
+ def _print_fixes(self, summary: DiagnosticSummary):
163
+ """Print consolidated fix suggestions."""
164
+ fixes = []
165
+
166
+ for result in summary.results:
167
+ if result.fix_command and result.has_issues:
168
+ fixes.append((result.category, result.fix_command, result.fix_description))
169
+
170
+ if fixes:
171
+ print()
172
+ print(self._color("Suggested Fixes:", "bold"))
173
+ print(self._color("─" * 40, "gray"))
174
+
175
+ for i, (category, command, description) in enumerate(fixes, 1):
176
+ print(f"{i}. {category}:")
177
+ print(f" {self._color(command, 'blue')}")
178
+ if description:
179
+ print(f" {self._color(description, 'gray')}")
180
+ print()
181
+
182
+ if self.verbose:
183
+ print(self._color("Run 'claude-mpm doctor --fix' to attempt automatic fixes", "gray"))
184
+ else:
185
+ print(self._color("Run 'claude-mpm doctor --verbose' for more details", "gray"))
186
+
187
+ def _report_json(self, summary: DiagnosticSummary):
188
+ """Generate JSON-formatted report."""
189
+ output = summary.to_dict()
190
+
191
+ # Add metadata
192
+ output["metadata"] = {
193
+ "tool": "claude-mpm doctor",
194
+ "version": self._get_version(),
195
+ "verbose": self.verbose
196
+ }
197
+
198
+ # Add fix suggestions
199
+ fixes = []
200
+ for result in summary.results:
201
+ if result.fix_command and result.has_issues:
202
+ fixes.append({
203
+ "category": result.category,
204
+ "command": result.fix_command,
205
+ "description": result.fix_description
206
+ })
207
+ output["fixes"] = fixes
208
+
209
+ print(json.dumps(output, indent=2))
210
+
211
+ def _report_markdown(self, summary: DiagnosticSummary):
212
+ """Generate Markdown-formatted report."""
213
+ print("# Claude MPM Doctor Report\n")
214
+
215
+ # Summary table
216
+ print("## Summary\n")
217
+ print("| Status | Count |")
218
+ print("|--------|-------|")
219
+ print(f"| ✅ OK | {summary.ok_count} |")
220
+ print(f"| ⚠️ Warning | {summary.warning_count} |")
221
+ print(f"| ❌ Error | {summary.error_count} |")
222
+ print(f"| ⏭️ Skipped | {summary.skipped_count} |")
223
+ print()
224
+
225
+ # Detailed results
226
+ print("## Diagnostic Results\n")
227
+
228
+ for result in summary.results:
229
+ symbol = self.STATUS_SYMBOLS.get(result.status, "?")
230
+ print(f"### {symbol} {result.category}\n")
231
+ print(f"**Status:** {result.status.value}")
232
+ print(f"**Message:** {result.message}\n")
233
+
234
+ if result.fix_command:
235
+ print(f"**Fix:** `{result.fix_command}`")
236
+ if result.fix_description:
237
+ print(f"_{result.fix_description}_\n")
238
+
239
+ if self.verbose and result.details:
240
+ print("**Details:**")
241
+ for key, value in result.details.items():
242
+ print(f"- {key}: {value}")
243
+ print()
244
+
245
+ # Fixes section
246
+ fixes = [(r.category, r.fix_command, r.fix_description)
247
+ for r in summary.results if r.fix_command and r.has_issues]
248
+
249
+ if fixes:
250
+ print("## Suggested Fixes\n")
251
+ for category, command, description in fixes:
252
+ print(f"- **{category}:** `{command}`")
253
+ if description:
254
+ print(f" - {description}")
255
+ print()
256
+
257
+ def _color(self, text: str, color: str) -> str:
258
+ """Apply color to text if colors are enabled."""
259
+ if not self.use_color:
260
+ return text
261
+
262
+ color_code = self.COLORS.get(color, "")
263
+ reset_code = self.COLORS["reset"]
264
+ return f"{color_code}{text}{reset_code}"
265
+
266
+ def _get_status_color(self, status: DiagnosticStatus) -> str:
267
+ """Get color for a status."""
268
+ color_map = {
269
+ DiagnosticStatus.OK: "green",
270
+ DiagnosticStatus.WARNING: "yellow",
271
+ DiagnosticStatus.ERROR: "red",
272
+ DiagnosticStatus.SKIPPED: "gray"
273
+ }
274
+ return color_map.get(status, "reset")
275
+
276
+ def _get_version(self) -> str:
277
+ """Get claude-mpm version."""
278
+ try:
279
+ from ..version_service import VersionService
280
+ service = VersionService()
281
+ return service.get_version()
282
+ except Exception:
283
+ return "unknown"
@@ -0,0 +1,120 @@
1
+ """
2
+ Data models for the diagnostic system.
3
+
4
+ WHY: Define clear data structures for diagnostic results to ensure
5
+ consistency across all checks and reporting.
6
+ """
7
+
8
+ from dataclasses import dataclass, field
9
+ from enum import Enum
10
+ from typing import Any, Dict, List, Optional
11
+
12
+
13
+ class DiagnosticStatus(Enum):
14
+ """Status levels for diagnostic results."""
15
+ OK = "ok"
16
+ WARNING = "warning"
17
+ ERROR = "error"
18
+ SKIPPED = "skipped"
19
+
20
+
21
+ @dataclass
22
+ class DiagnosticResult:
23
+ """Result from a diagnostic check.
24
+
25
+ WHY: Standardized result format ensures consistent reporting
26
+ and makes it easy to aggregate and display results.
27
+ """
28
+ category: str # e.g., "Installation", "Agents", "MCP Server"
29
+ status: DiagnosticStatus
30
+ message: str
31
+ details: Dict[str, Any] = field(default_factory=dict)
32
+ fix_command: Optional[str] = None
33
+ fix_description: Optional[str] = None
34
+ sub_results: List['DiagnosticResult'] = field(default_factory=list)
35
+
36
+ def to_dict(self) -> Dict[str, Any]:
37
+ """Convert to dictionary for JSON serialization."""
38
+ return {
39
+ "category": self.category,
40
+ "status": self.status.value,
41
+ "message": self.message,
42
+ "details": self.details,
43
+ "fix_command": self.fix_command,
44
+ "fix_description": self.fix_description,
45
+ "sub_results": [r.to_dict() for r in self.sub_results]
46
+ }
47
+
48
+ @property
49
+ def has_issues(self) -> bool:
50
+ """Check if this result indicates any issues."""
51
+ return self.status in (DiagnosticStatus.WARNING, DiagnosticStatus.ERROR)
52
+
53
+ @property
54
+ def severity_level(self) -> int:
55
+ """Get numeric severity level for sorting."""
56
+ severity_map = {
57
+ DiagnosticStatus.OK: 0,
58
+ DiagnosticStatus.SKIPPED: 1,
59
+ DiagnosticStatus.WARNING: 2,
60
+ DiagnosticStatus.ERROR: 3
61
+ }
62
+ return severity_map.get(self.status, 0)
63
+
64
+
65
+ @dataclass
66
+ class DiagnosticSummary:
67
+ """Summary of all diagnostic results.
68
+
69
+ WHY: Provides a high-level overview of system health
70
+ and quick access to issues that need attention.
71
+ """
72
+ total_checks: int = 0
73
+ ok_count: int = 0
74
+ warning_count: int = 0
75
+ error_count: int = 0
76
+ skipped_count: int = 0
77
+ results: List[DiagnosticResult] = field(default_factory=list)
78
+
79
+ def add_result(self, result: DiagnosticResult):
80
+ """Add a result to the summary."""
81
+ self.results.append(result)
82
+ self.total_checks += 1
83
+
84
+ if result.status == DiagnosticStatus.OK:
85
+ self.ok_count += 1
86
+ elif result.status == DiagnosticStatus.WARNING:
87
+ self.warning_count += 1
88
+ elif result.status == DiagnosticStatus.ERROR:
89
+ self.error_count += 1
90
+ elif result.status == DiagnosticStatus.SKIPPED:
91
+ self.skipped_count += 1
92
+
93
+ @property
94
+ def has_issues(self) -> bool:
95
+ """Check if there are any warnings or errors."""
96
+ return self.warning_count > 0 or self.error_count > 0
97
+
98
+ @property
99
+ def overall_status(self) -> DiagnosticStatus:
100
+ """Get overall system status."""
101
+ if self.error_count > 0:
102
+ return DiagnosticStatus.ERROR
103
+ elif self.warning_count > 0:
104
+ return DiagnosticStatus.WARNING
105
+ else:
106
+ return DiagnosticStatus.OK
107
+
108
+ def to_dict(self) -> Dict[str, Any]:
109
+ """Convert to dictionary for JSON serialization."""
110
+ return {
111
+ "summary": {
112
+ "total_checks": self.total_checks,
113
+ "ok": self.ok_count,
114
+ "warnings": self.warning_count,
115
+ "errors": self.error_count,
116
+ "skipped": self.skipped_count,
117
+ "overall_status": self.overall_status.value
118
+ },
119
+ "results": [r.to_dict() for r in self.results]
120
+ }
@@ -413,7 +413,7 @@ class IMCPGateway(IMCPLifecycle):
413
413
  Main interface for MCP gateway implementation.
414
414
 
415
415
  Orchestrates tool registry, communication, and request handling.
416
- Acts as a protocol bridge between Claude Desktop and internal tools.
416
+ Acts as a protocol bridge between Claude Code and internal tools.
417
417
  """
418
418
 
419
419
  @abstractmethod
@@ -442,7 +442,7 @@ Examples:
442
442
  # Run with debug logging
443
443
  python -m claude_mpm.services.mcp_gateway.main --debug
444
444
 
445
- # Run as MCP server for Claude Desktop
445
+ # Run as MCP server for Claude Code
446
446
  python -m claude_mpm.services.mcp_gateway.main --stdio
447
447
  """,
448
448
  )
@@ -6,8 +6,8 @@ MCP protocol gateway using Anthropic's official MCP package.
6
6
  Handles stdio-based communication, request routing, and tool invocation.
7
7
  Acts as a bridge between Claude Code and internal tools.
8
8
 
9
- NOTE: MCP is ONLY for Claude Code - NOT for Claude Desktop.
10
- Claude Desktop uses a different system for agent deployment.
9
+ NOTE: MCP is ONLY for Claude Code - NOT for Claude Code.
10
+ Claude Code uses a different system for agent deployment.
11
11
 
12
12
  Part of ISS-0035: MCP Gateway Implementation - Core Gateway and Tool Registry
13
13
  """
@@ -339,7 +339,7 @@ class MCPGateway(BaseMCPService, IMCPGateway):
339
339
  to handle incoming requests from Claude Code.
340
340
 
341
341
  WHY: We use stdio (stdin/stdout) as it's the standard communication
342
- method for MCP servers in Claude Desktop. This ensures compatibility
342
+ method for MCP servers in Claude Code. This ensures compatibility
343
343
  and allows the server to be launched as a subprocess.
344
344
  """
345
345
  try:
@@ -24,7 +24,7 @@ class StdioHandler(BaseMCPService, IMCPCommunication):
24
24
  STDIO-based communication handler for MCP.
25
25
 
26
26
  WHY: The MCP protocol uses stdio (stdin/stdout) for communication between
27
- Claude Desktop and MCP servers. This handler manages the low-level
27
+ Claude Code and MCP servers. This handler manages the low-level
28
28
  message exchange, ensuring proper JSON-RPC formatting and error handling.
29
29
 
30
30
  DESIGN DECISIONS:
@@ -3,7 +3,7 @@ Simplified MCP Stdio Server
3
3
  ============================
4
4
 
5
5
  A proper stdio-based MCP server that communicates via JSON-RPC over stdin/stdout.
6
- This server is spawned on-demand by Claude Desktop/Code and exits when the connection closes.
6
+ This server is spawned on-demand by Claude Code/Code and exits when the connection closes.
7
7
 
8
8
  WHY: MCP servers should be simple stdio-based processes that Claude can spawn and control.
9
9
  They should NOT run as persistent background services with lock files.
@@ -45,7 +45,7 @@ def apply_backward_compatibility_patches():
45
45
  Apply backward compatibility patches for MCP protocol differences.
46
46
 
47
47
  This function patches the MCP Server to handle missing clientInfo
48
- in initialize requests from older Claude Desktop versions.
48
+ in initialize requests from older Claude Code versions.
49
49
  """
50
50
  try:
51
51
  from mcp.server import Server
@@ -114,7 +114,7 @@ class SimpleMCPServer:
114
114
  A simple stdio-based MCP server implementation.
115
115
 
116
116
  WHY: This server follows the MCP specification for stdio communication,
117
- making it compatible with Claude Desktop/Code's MCP client.
117
+ making it compatible with Claude Code/Code's MCP client.
118
118
 
119
119
  DESIGN DECISIONS:
120
120
  - No persistent state or lock files
@@ -3,9 +3,9 @@ MCP Tool Adapters for aitrackdown Ticket Management
3
3
  ====================================================
4
4
 
5
5
  Provides MCP tool wrappers for common aitrackdown operations, enabling
6
- ticket management through Claude Desktop's MCP interface.
6
+ ticket management through Claude Code's MCP interface.
7
7
 
8
- WHY: These tools allow Claude Desktop to interact with aitrackdown for
8
+ WHY: These tools allow Claude Code to interact with aitrackdown for
9
9
  ticket management without requiring direct CLI access, providing a
10
10
  seamless integration experience.
11
11
 
@@ -70,21 +70,31 @@ class EventHandlerRegistry:
70
70
  )
71
71
  return
72
72
 
73
+ # Add debug logging for deployment context
74
+ try:
75
+ from ....core.unified_paths import PathContext
76
+ deployment_context = PathContext.detect_deployment_context()
77
+ self.logger.info(f"Initializing event handlers in {deployment_context.value} mode")
78
+ except Exception as e:
79
+ self.logger.debug(f"Could not detect deployment context: {e}")
80
+
73
81
  handler_classes = handler_classes or self.DEFAULT_HANDLERS
82
+ self.logger.debug(f"Initializing {len(handler_classes)} handler classes")
74
83
 
75
84
  for handler_class in handler_classes:
76
85
  try:
86
+ self.logger.debug(f"Creating instance of {handler_class.__name__}")
77
87
  handler = handler_class(self.server)
78
88
  self.handlers.append(handler)
79
- self.logger.info(f"Initialized handler: {handler_class.__name__}")
89
+ self.logger.info(f"Initialized handler: {handler_class.__name__}")
80
90
  except Exception as e:
81
- self.logger.error(f"Failed to initialize {handler_class.__name__}: {e}")
91
+ self.logger.error(f"Failed to initialize {handler_class.__name__}: {e}")
82
92
  import traceback
83
93
 
84
94
  self.logger.error(f"Stack trace: {traceback.format_exc()}")
85
95
 
86
96
  self._initialized = True
87
- self.logger.info(f"Registry initialized with {len(self.handlers)} handlers")
97
+ self.logger.info(f"🎯 Registry initialized with {len(self.handlers)} handlers")
88
98
 
89
99
  def register_all_events(self) -> None:
90
100
  """Register all events from all handlers.
@@ -97,27 +107,49 @@ class EventHandlerRegistry:
97
107
  self.logger.error("Registry not initialized. Call initialize() first.")
98
108
  raise RuntimeError("EventHandlerRegistry not initialized")
99
109
 
110
+ self.logger.info(f"🔄 Starting event registration for {len(self.handlers)} handlers")
111
+
112
+ # Verify Socket.IO server is available
113
+ if not hasattr(self.server, 'core') or not self.server.core or not self.server.core.sio:
114
+ self.logger.error("❌ Socket.IO server instance not available for event registration")
115
+ raise RuntimeError("Socket.IO server not available")
116
+
117
+ self.logger.debug(f"Socket.IO server available: {type(self.server.core.sio)}")
118
+
100
119
  registered_count = 0
101
120
  for handler in self.handlers:
102
121
  try:
122
+ self.logger.debug(f"Registering events for {handler.__class__.__name__}")
123
+
124
+ # Get the number of registered events before and after
125
+ sio_events_before = len(getattr(self.server.core.sio, 'handlers', {}))
126
+
103
127
  handler.register_events()
128
+
129
+ sio_events_after = len(getattr(self.server.core.sio, 'handlers', {}))
130
+ events_added = sio_events_after - sio_events_before
131
+
104
132
  registered_count += 1
105
- self.logger.info(f"Registered events for {handler.__class__.__name__}")
133
+ self.logger.info(f"Registered {events_added} events for {handler.__class__.__name__}")
134
+
106
135
  except NotImplementedError:
107
136
  # Handler has no events to register (like ProjectEventHandler)
108
137
  self.logger.debug(
109
- f"No events to register for {handler.__class__.__name__}"
138
+ f"⏭️ No events to register for {handler.__class__.__name__}"
110
139
  )
111
140
  except Exception as e:
112
141
  self.logger.error(
113
- f"Failed to register events for {handler.__class__.__name__}: {e}"
142
+ f"Failed to register events for {handler.__class__.__name__}: {e}"
114
143
  )
115
144
  import traceback
116
145
 
117
146
  self.logger.error(f"Stack trace: {traceback.format_exc()}")
118
147
 
148
+ # Final verification
149
+ total_sio_events = len(getattr(self.server.core.sio, 'handlers', {}))
119
150
  self.logger.info(
120
- f"Successfully registered events from {registered_count} handlers"
151
+ f"🎉 Event registration complete: {registered_count} handlers processed, "
152
+ f"{total_sio_events} total Socket.IO events registered"
121
153
  )
122
154
 
123
155
  def add_handler(self, handler_class: Type[BaseEventHandler]):