claude-mpm 4.1.8__py3-none-any.whl → 4.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/INSTRUCTIONS.md +26 -1
- claude_mpm/agents/agents_metadata.py +57 -0
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
- claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
- claude_mpm/agents/templates/agent-manager.json +263 -17
- claude_mpm/agents/templates/agentic_coder_optimizer.json +222 -0
- claude_mpm/agents/templates/code_analyzer.json +18 -8
- claude_mpm/agents/templates/engineer.json +1 -1
- claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/research.json +1 -1
- claude_mpm/cli/__init__.py +4 -0
- claude_mpm/cli/commands/__init__.py +6 -0
- claude_mpm/cli/commands/analyze.py +547 -0
- claude_mpm/cli/commands/analyze_code.py +524 -0
- claude_mpm/cli/commands/configure.py +77 -28
- claude_mpm/cli/commands/configure_tui.py +60 -60
- claude_mpm/cli/commands/debug.py +1387 -0
- claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
- claude_mpm/cli/parsers/analyze_parser.py +135 -0
- claude_mpm/cli/parsers/base_parser.py +29 -0
- claude_mpm/cli/parsers/debug_parser.py +319 -0
- claude_mpm/constants.py +3 -1
- claude_mpm/core/framework_loader.py +148 -6
- claude_mpm/core/log_manager.py +16 -13
- claude_mpm/core/logger.py +1 -1
- claude_mpm/core/unified_agent_registry.py +1 -1
- claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
- claude_mpm/dashboard/analysis_runner.py +428 -0
- claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
- claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/css/activity.css +549 -0
- claude_mpm/dashboard/static/css/code-tree.css +846 -0
- claude_mpm/dashboard/static/css/dashboard.css +245 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
- claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
- claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/activity-tree.js +1139 -0
- claude_mpm/dashboard/static/js/components/code-tree.js +1357 -0
- claude_mpm/dashboard/static/js/components/code-viewer.js +480 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +11 -0
- claude_mpm/dashboard/static/js/components/session-manager.js +40 -4
- claude_mpm/dashboard/static/js/components/socket-manager.js +12 -0
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +4 -0
- claude_mpm/dashboard/static/js/components/working-directory.js +17 -1
- claude_mpm/dashboard/static/js/dashboard.js +39 -0
- claude_mpm/dashboard/static/js/socket-client.js +414 -20
- claude_mpm/dashboard/templates/index.html +184 -4
- claude_mpm/hooks/claude_hooks/hook_handler.py +182 -5
- claude_mpm/hooks/claude_hooks/installer.py +386 -113
- claude_mpm/scripts/claude-hook-handler.sh +161 -0
- claude_mpm/scripts/socketio_daemon.py +121 -8
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +2 -2
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -2
- claude_mpm/services/agents/memory/memory_format_service.py +1 -5
- claude_mpm/services/cli/agent_cleanup_service.py +1 -2
- claude_mpm/services/cli/agent_dependency_service.py +1 -1
- claude_mpm/services/cli/agent_validation_service.py +3 -4
- claude_mpm/services/cli/dashboard_launcher.py +2 -3
- claude_mpm/services/cli/startup_checker.py +0 -10
- claude_mpm/services/core/cache_manager.py +1 -2
- claude_mpm/services/core/path_resolver.py +1 -4
- claude_mpm/services/core/service_container.py +2 -2
- claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
- claude_mpm/services/infrastructure/monitoring/__init__.py +11 -11
- claude_mpm/services/infrastructure/monitoring.py +11 -11
- claude_mpm/services/project/architecture_analyzer.py +1 -1
- claude_mpm/services/project/dependency_analyzer.py +4 -4
- claude_mpm/services/project/language_analyzer.py +3 -3
- claude_mpm/services/project/metrics_collector.py +3 -6
- claude_mpm/services/socketio/handlers/__init__.py +2 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +170 -0
- claude_mpm/services/socketio/handlers/registry.py +2 -0
- claude_mpm/services/socketio/server/connection_manager.py +4 -4
- claude_mpm/services/socketio/server/core.py +100 -11
- claude_mpm/services/socketio/server/main.py +8 -2
- claude_mpm/services/visualization/__init__.py +19 -0
- claude_mpm/services/visualization/mermaid_generator.py +938 -0
- claude_mpm/tools/__main__.py +208 -0
- claude_mpm/tools/code_tree_analyzer.py +778 -0
- claude_mpm/tools/code_tree_builder.py +632 -0
- claude_mpm/tools/code_tree_events.py +318 -0
- claude_mpm/tools/socketio_debug.py +671 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/RECORD +102 -73
- claude_mpm/agents/schema/agent_schema.json +0 -314
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/top_level.txt +0 -0
|
@@ -7,6 +7,7 @@ and maintainability.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from .base import BaseEventHandler
|
|
10
|
+
from .code_analysis import CodeAnalysisEventHandler
|
|
10
11
|
from .connection import ConnectionEventHandler
|
|
11
12
|
from .file import FileEventHandler
|
|
12
13
|
from .git import GitEventHandler
|
|
@@ -16,6 +17,7 @@ from .registry import EventHandlerRegistry
|
|
|
16
17
|
|
|
17
18
|
__all__ = [
|
|
18
19
|
"BaseEventHandler",
|
|
20
|
+
"CodeAnalysisEventHandler",
|
|
19
21
|
"ConnectionEventHandler",
|
|
20
22
|
"EventHandlerRegistry",
|
|
21
23
|
"FileEventHandler",
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code Analysis Event Handler for Socket.IO
|
|
3
|
+
==========================================
|
|
4
|
+
|
|
5
|
+
WHY: Handles code analysis requests from the dashboard, managing the analysis
|
|
6
|
+
runner subprocess and streaming results back to connected clients.
|
|
7
|
+
|
|
8
|
+
DESIGN DECISIONS:
|
|
9
|
+
- Single analysis runner instance per server
|
|
10
|
+
- Queue multiple requests for sequential processing
|
|
11
|
+
- Support cancellation of running analysis
|
|
12
|
+
- Stream events in real-time to all connected clients
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import uuid
|
|
16
|
+
from typing import Any, Dict
|
|
17
|
+
|
|
18
|
+
from ....core.logging_config import get_logger
|
|
19
|
+
from ....dashboard.analysis_runner import CodeAnalysisRunner
|
|
20
|
+
from .base import BaseEventHandler
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CodeAnalysisEventHandler(BaseEventHandler):
|
|
24
|
+
"""Handles code analysis events from dashboard clients.
|
|
25
|
+
|
|
26
|
+
WHY: Provides a clean interface between the dashboard UI and the
|
|
27
|
+
code analysis subprocess, managing requests and responses.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, server):
|
|
31
|
+
"""Initialize the code analysis event handler.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
server: The SocketIOServer instance
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(server)
|
|
37
|
+
self.logger = get_logger(__name__)
|
|
38
|
+
self.analysis_runner = None
|
|
39
|
+
|
|
40
|
+
def initialize(self):
|
|
41
|
+
"""Initialize the analysis runner."""
|
|
42
|
+
if not self.analysis_runner:
|
|
43
|
+
self.analysis_runner = CodeAnalysisRunner(self.server)
|
|
44
|
+
self.analysis_runner.start()
|
|
45
|
+
self.logger.info("Code analysis runner initialized")
|
|
46
|
+
|
|
47
|
+
def cleanup(self):
|
|
48
|
+
"""Cleanup the analysis runner on shutdown."""
|
|
49
|
+
if self.analysis_runner:
|
|
50
|
+
self.analysis_runner.stop()
|
|
51
|
+
self.analysis_runner = None
|
|
52
|
+
self.logger.info("Code analysis runner stopped")
|
|
53
|
+
|
|
54
|
+
def get_events(self) -> Dict[str, Any]:
|
|
55
|
+
"""Get the events this handler manages.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dictionary mapping event names to handler methods
|
|
59
|
+
"""
|
|
60
|
+
return {
|
|
61
|
+
"code:analyze:request": self.handle_analyze_request,
|
|
62
|
+
"code:analyze:cancel": self.handle_cancel_request,
|
|
63
|
+
"code:analyze:status": self.handle_status_request,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def register_events(self) -> None:
|
|
67
|
+
"""Register Socket.IO event handlers.
|
|
68
|
+
|
|
69
|
+
WHY: Required by BaseEventHandler to register events with the Socket.IO server.
|
|
70
|
+
"""
|
|
71
|
+
events = self.get_events()
|
|
72
|
+
for event_name, handler_method in events.items():
|
|
73
|
+
self.server.core.sio.on(event_name, handler_method)
|
|
74
|
+
self.logger.info(f"Registered event handler: {event_name}")
|
|
75
|
+
|
|
76
|
+
async def handle_analyze_request(self, sid: str, data: Dict[str, Any]):
|
|
77
|
+
"""Handle code analysis request from client.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
sid: Socket ID of the requesting client
|
|
81
|
+
data: Request data containing path and options
|
|
82
|
+
"""
|
|
83
|
+
self.logger.info(f"Code analysis requested from {sid}: {data}")
|
|
84
|
+
|
|
85
|
+
# Initialize runner if needed
|
|
86
|
+
if not self.analysis_runner:
|
|
87
|
+
self.initialize()
|
|
88
|
+
|
|
89
|
+
# Validate request
|
|
90
|
+
path = data.get("path")
|
|
91
|
+
if not path:
|
|
92
|
+
await self.server.sio.emit(
|
|
93
|
+
"code:analysis:error",
|
|
94
|
+
{
|
|
95
|
+
"message": "Path is required for analysis",
|
|
96
|
+
"request_id": data.get("request_id"),
|
|
97
|
+
},
|
|
98
|
+
room=sid,
|
|
99
|
+
)
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Generate request ID if not provided
|
|
103
|
+
request_id = data.get("request_id") or str(uuid.uuid4())
|
|
104
|
+
|
|
105
|
+
# Extract options
|
|
106
|
+
languages = data.get("languages")
|
|
107
|
+
max_depth = data.get("max_depth")
|
|
108
|
+
ignore_patterns = data.get("ignore_patterns")
|
|
109
|
+
|
|
110
|
+
# Queue analysis request
|
|
111
|
+
success = self.analysis_runner.request_analysis(
|
|
112
|
+
request_id=request_id,
|
|
113
|
+
path=path,
|
|
114
|
+
languages=languages,
|
|
115
|
+
max_depth=max_depth,
|
|
116
|
+
ignore_patterns=ignore_patterns,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if success:
|
|
120
|
+
# Send acknowledgment to requesting client
|
|
121
|
+
await self.server.sio.emit(
|
|
122
|
+
"code:analysis:accepted",
|
|
123
|
+
{
|
|
124
|
+
"request_id": request_id,
|
|
125
|
+
"path": path,
|
|
126
|
+
"message": "Analysis request queued",
|
|
127
|
+
},
|
|
128
|
+
room=sid,
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
# Send error if request failed
|
|
132
|
+
await self.server.sio.emit(
|
|
133
|
+
"code:analysis:error",
|
|
134
|
+
{
|
|
135
|
+
"request_id": request_id,
|
|
136
|
+
"message": "Failed to queue analysis request",
|
|
137
|
+
},
|
|
138
|
+
room=sid,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
async def handle_cancel_request(self, sid: str, data: Dict[str, Any]):
|
|
142
|
+
"""Handle analysis cancellation request.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
sid: Socket ID of the requesting client
|
|
146
|
+
data: Request data (may contain request_id)
|
|
147
|
+
"""
|
|
148
|
+
self.logger.info(f"Analysis cancellation requested from {sid}")
|
|
149
|
+
|
|
150
|
+
# Cancel current analysis
|
|
151
|
+
self.analysis_runner.cancel_current()
|
|
152
|
+
|
|
153
|
+
# Send confirmation
|
|
154
|
+
await self.server.sio.emit(
|
|
155
|
+
"code:analysis:cancelled",
|
|
156
|
+
{"message": "Analysis cancelled", "request_id": data.get("request_id")},
|
|
157
|
+
room=sid,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
async def handle_status_request(self, sid: str, data: Dict[str, Any]):
|
|
161
|
+
"""Handle status request from client.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
sid: Socket ID of the requesting client
|
|
165
|
+
data: Request data (unused)
|
|
166
|
+
"""
|
|
167
|
+
status = self.analysis_runner.get_status()
|
|
168
|
+
|
|
169
|
+
# Send status to requesting client
|
|
170
|
+
await self.server.sio.emit("code:analysis:status", status, room=sid)
|
|
@@ -15,6 +15,7 @@ if TYPE_CHECKING:
|
|
|
15
15
|
|
|
16
16
|
from ..server import SocketIOServer
|
|
17
17
|
|
|
18
|
+
from .code_analysis import CodeAnalysisEventHandler
|
|
18
19
|
from .connection import ConnectionEventHandler
|
|
19
20
|
from .file import FileEventHandler
|
|
20
21
|
from .git import GitEventHandler
|
|
@@ -37,6 +38,7 @@ class EventHandlerRegistry:
|
|
|
37
38
|
HookEventHandler, # Hook events for session tracking
|
|
38
39
|
GitEventHandler, # Git operations
|
|
39
40
|
FileEventHandler, # File operations
|
|
41
|
+
CodeAnalysisEventHandler, # Code analysis for dashboard
|
|
40
42
|
ProjectEventHandler, # Project management (future)
|
|
41
43
|
MemoryEventHandler, # Memory management (future)
|
|
42
44
|
]
|
|
@@ -10,6 +10,7 @@ of client states, proper event delivery, and automatic recovery mechanisms.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import asyncio
|
|
13
|
+
import contextlib
|
|
13
14
|
import time
|
|
14
15
|
from collections import deque
|
|
15
16
|
from dataclasses import dataclass, field
|
|
@@ -148,7 +149,7 @@ class ConnectionManager:
|
|
|
148
149
|
- Automatic event replay on reconnection
|
|
149
150
|
"""
|
|
150
151
|
|
|
151
|
-
def __init__(self, max_buffer_size: int = None, event_ttl: int = None):
|
|
152
|
+
def __init__(self, max_buffer_size: Optional[int] = None, event_ttl: Optional[int] = None):
|
|
152
153
|
"""
|
|
153
154
|
Initialize connection manager with centralized configuration.
|
|
154
155
|
|
|
@@ -263,6 +264,7 @@ class ConnectionManager:
|
|
|
263
264
|
)
|
|
264
265
|
self.connections[sid] = conn
|
|
265
266
|
return conn
|
|
267
|
+
return None
|
|
266
268
|
|
|
267
269
|
def _create_new_connection(
|
|
268
270
|
self, sid: str, client_id: str, now: float
|
|
@@ -434,10 +436,8 @@ class ConnectionManager:
|
|
|
434
436
|
"""Stop the health monitoring task."""
|
|
435
437
|
if self.health_task:
|
|
436
438
|
self.health_task.cancel()
|
|
437
|
-
|
|
439
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
438
440
|
await self.health_task
|
|
439
|
-
except asyncio.CancelledError:
|
|
440
|
-
pass
|
|
441
441
|
self.health_task = None
|
|
442
442
|
self.logger.info("Stopped connection health monitoring")
|
|
443
443
|
|
|
@@ -273,24 +273,113 @@ class SocketIOServerCore:
|
|
|
273
273
|
# Parse JSON payload
|
|
274
274
|
event_data = await request.json()
|
|
275
275
|
|
|
276
|
-
# Log receipt
|
|
277
|
-
event_type =
|
|
278
|
-
|
|
276
|
+
# Log receipt with more detail
|
|
277
|
+
event_type = (
|
|
278
|
+
event_data.get("subtype")
|
|
279
|
+
or event_data.get("hook_event_name")
|
|
280
|
+
or "unknown"
|
|
281
|
+
)
|
|
282
|
+
self.logger.info(f"📨 Received HTTP event: {event_type}")
|
|
283
|
+
self.logger.debug(f"Event data keys: {list(event_data.keys())}")
|
|
284
|
+
self.logger.debug(f"Connected clients: {len(self.connected_clients)}")
|
|
285
|
+
|
|
286
|
+
# Transform hook event format to claude_event format if needed
|
|
287
|
+
if "hook_event_name" in event_data and "event" not in event_data:
|
|
288
|
+
# This is a raw hook event, transform it
|
|
289
|
+
from claude_mpm.services.socketio.event_normalizer import (
|
|
290
|
+
EventNormalizer,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
normalizer = EventNormalizer()
|
|
294
|
+
|
|
295
|
+
# Create the format expected by normalizer
|
|
296
|
+
raw_event = {
|
|
297
|
+
"type": "hook",
|
|
298
|
+
"subtype": event_data.get("hook_event_name", "unknown")
|
|
299
|
+
.lower()
|
|
300
|
+
.replace("submit", "")
|
|
301
|
+
.replace("use", "_use"),
|
|
302
|
+
"timestamp": event_data.get("timestamp"),
|
|
303
|
+
"data": event_data.get("hook_input_data", {}),
|
|
304
|
+
"source": "claude_hooks",
|
|
305
|
+
"session_id": event_data.get("session_id"),
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
# Map hook event names to dashboard subtypes
|
|
309
|
+
subtype_map = {
|
|
310
|
+
"UserPromptSubmit": "user_prompt",
|
|
311
|
+
"PreToolUse": "pre_tool",
|
|
312
|
+
"PostToolUse": "post_tool",
|
|
313
|
+
"Stop": "stop",
|
|
314
|
+
"SubagentStop": "subagent_stop",
|
|
315
|
+
"AssistantResponse": "assistant_response",
|
|
316
|
+
}
|
|
317
|
+
raw_event["subtype"] = subtype_map.get(
|
|
318
|
+
event_data.get("hook_event_name"), "unknown"
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
normalized = normalizer.normalize(raw_event, source="hook")
|
|
322
|
+
event_data = normalized.to_dict()
|
|
323
|
+
self.logger.debug(
|
|
324
|
+
f"Normalized event: type={event_data.get('type')}, subtype={event_data.get('subtype')}"
|
|
325
|
+
)
|
|
279
326
|
|
|
280
327
|
# Broadcast to all connected dashboard clients via SocketIO
|
|
281
328
|
if self.sio:
|
|
282
|
-
#
|
|
283
|
-
|
|
329
|
+
# CRITICAL: Use the main server's broadcaster for proper event handling
|
|
330
|
+
# The broadcaster handles retries, connection management, and buffering
|
|
331
|
+
if (
|
|
332
|
+
self.main_server
|
|
333
|
+
and hasattr(self.main_server, "broadcaster")
|
|
334
|
+
and self.main_server.broadcaster
|
|
335
|
+
):
|
|
336
|
+
# The broadcaster expects raw event data and will normalize it
|
|
337
|
+
# Since we already normalized it, we need to pass it in a way that won't double-normalize
|
|
338
|
+
# We'll emit directly through the broadcaster's sio with proper handling
|
|
339
|
+
|
|
340
|
+
# Add to event buffer and history
|
|
341
|
+
with self.buffer_lock:
|
|
342
|
+
self.event_buffer.append(event_data)
|
|
343
|
+
self.stats["events_buffered"] = len(self.event_buffer)
|
|
344
|
+
|
|
345
|
+
# Add to main server's event history
|
|
346
|
+
if hasattr(self.main_server, "event_history"):
|
|
347
|
+
self.main_server.event_history.append(event_data)
|
|
348
|
+
|
|
349
|
+
# Use the broadcaster's sio to emit (it's the same as self.sio)
|
|
350
|
+
# This ensures the event goes through the proper channels
|
|
351
|
+
await self.sio.emit("claude_event", event_data)
|
|
352
|
+
|
|
353
|
+
# Update broadcaster stats
|
|
354
|
+
if hasattr(self.main_server.broadcaster, "stats"):
|
|
355
|
+
self.main_server.broadcaster.stats["events_sent"] = (
|
|
356
|
+
self.main_server.broadcaster.stats.get("events_sent", 0)
|
|
357
|
+
+ 1
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
self.logger.info(
|
|
361
|
+
f"✅ Event broadcasted: {event_data.get('subtype', 'unknown')} to {len(self.connected_clients)} clients"
|
|
362
|
+
)
|
|
363
|
+
self.logger.debug(
|
|
364
|
+
f"Connected client IDs: {list(self.connected_clients) if self.connected_clients else 'None'}"
|
|
365
|
+
)
|
|
366
|
+
else:
|
|
367
|
+
# Fallback: Direct emit if broadcaster not available (shouldn't happen)
|
|
368
|
+
self.logger.warning(
|
|
369
|
+
"Broadcaster not available, using direct emit"
|
|
370
|
+
)
|
|
371
|
+
await self.sio.emit("claude_event", event_data)
|
|
284
372
|
|
|
285
|
-
|
|
286
|
-
|
|
373
|
+
# Update stats manually if using fallback
|
|
374
|
+
self.stats["events_sent"] = self.stats.get("events_sent", 0) + 1
|
|
287
375
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
376
|
+
# Add to event buffer for late-joining clients
|
|
377
|
+
with self.buffer_lock:
|
|
378
|
+
self.event_buffer.append(event_data)
|
|
379
|
+
self.stats["events_buffered"] = len(self.event_buffer)
|
|
292
380
|
|
|
293
381
|
# Return 204 No Content for success
|
|
382
|
+
self.logger.debug(f"✅ HTTP event processed successfully: {event_type}")
|
|
294
383
|
return web.Response(status=204)
|
|
295
384
|
|
|
296
385
|
except Exception as e:
|
|
@@ -265,10 +265,16 @@ class SocketIOServer(SocketIOServiceInterface):
|
|
|
265
265
|
except Exception as e:
|
|
266
266
|
self.logger.error(f"Error during EventBus teardown: {e}")
|
|
267
267
|
|
|
268
|
-
# Stop
|
|
268
|
+
# Stop code analysis handler
|
|
269
269
|
if self.event_registry:
|
|
270
|
-
from ..handlers import ConnectionEventHandler
|
|
270
|
+
from ..handlers import CodeAnalysisEventHandler, ConnectionEventHandler
|
|
271
271
|
|
|
272
|
+
# Stop analysis runner
|
|
273
|
+
analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
|
|
274
|
+
if analysis_handler and hasattr(analysis_handler, "cleanup"):
|
|
275
|
+
analysis_handler.cleanup()
|
|
276
|
+
|
|
277
|
+
# Stop health monitoring in connection handler
|
|
272
278
|
conn_handler = self.event_registry.get_handler(ConnectionEventHandler)
|
|
273
279
|
if conn_handler and hasattr(conn_handler, "stop_health_monitoring"):
|
|
274
280
|
conn_handler.stop_health_monitoring()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Visualization Services for Claude MPM
|
|
3
|
+
=====================================
|
|
4
|
+
|
|
5
|
+
This module provides visualization services for code analysis,
|
|
6
|
+
including Mermaid diagram generation for various code structures.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .mermaid_generator import (
|
|
10
|
+
DiagramConfig,
|
|
11
|
+
DiagramType,
|
|
12
|
+
MermaidGeneratorService,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"DiagramConfig",
|
|
17
|
+
"DiagramType",
|
|
18
|
+
"MermaidGeneratorService",
|
|
19
|
+
]
|