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.
Files changed (103) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +26 -1
  3. claude_mpm/agents/agents_metadata.py +57 -0
  4. claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
  5. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
  6. claude_mpm/agents/templates/agent-manager.json +263 -17
  7. claude_mpm/agents/templates/agentic_coder_optimizer.json +222 -0
  8. claude_mpm/agents/templates/code_analyzer.json +18 -8
  9. claude_mpm/agents/templates/engineer.json +1 -1
  10. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
  11. claude_mpm/agents/templates/qa.json +1 -1
  12. claude_mpm/agents/templates/research.json +1 -1
  13. claude_mpm/cli/__init__.py +4 -0
  14. claude_mpm/cli/commands/__init__.py +6 -0
  15. claude_mpm/cli/commands/analyze.py +547 -0
  16. claude_mpm/cli/commands/analyze_code.py +524 -0
  17. claude_mpm/cli/commands/configure.py +77 -28
  18. claude_mpm/cli/commands/configure_tui.py +60 -60
  19. claude_mpm/cli/commands/debug.py +1387 -0
  20. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  21. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  22. claude_mpm/cli/parsers/base_parser.py +29 -0
  23. claude_mpm/cli/parsers/debug_parser.py +319 -0
  24. claude_mpm/constants.py +3 -1
  25. claude_mpm/core/framework_loader.py +148 -6
  26. claude_mpm/core/log_manager.py +16 -13
  27. claude_mpm/core/logger.py +1 -1
  28. claude_mpm/core/unified_agent_registry.py +1 -1
  29. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
  30. claude_mpm/dashboard/analysis_runner.py +428 -0
  31. claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
  32. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  33. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  34. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  35. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  36. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  37. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  38. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  39. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  40. claude_mpm/dashboard/static/css/activity.css +549 -0
  41. claude_mpm/dashboard/static/css/code-tree.css +846 -0
  42. claude_mpm/dashboard/static/css/dashboard.css +245 -0
  43. claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
  44. claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
  45. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  46. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  47. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  48. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  49. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  50. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  51. claude_mpm/dashboard/static/js/components/activity-tree.js +1139 -0
  52. claude_mpm/dashboard/static/js/components/code-tree.js +1357 -0
  53. claude_mpm/dashboard/static/js/components/code-viewer.js +480 -0
  54. claude_mpm/dashboard/static/js/components/event-viewer.js +11 -0
  55. claude_mpm/dashboard/static/js/components/session-manager.js +40 -4
  56. claude_mpm/dashboard/static/js/components/socket-manager.js +12 -0
  57. claude_mpm/dashboard/static/js/components/ui-state-manager.js +4 -0
  58. claude_mpm/dashboard/static/js/components/working-directory.js +17 -1
  59. claude_mpm/dashboard/static/js/dashboard.js +39 -0
  60. claude_mpm/dashboard/static/js/socket-client.js +414 -20
  61. claude_mpm/dashboard/templates/index.html +184 -4
  62. claude_mpm/hooks/claude_hooks/hook_handler.py +182 -5
  63. claude_mpm/hooks/claude_hooks/installer.py +386 -113
  64. claude_mpm/scripts/claude-hook-handler.sh +161 -0
  65. claude_mpm/scripts/socketio_daemon.py +121 -8
  66. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +2 -2
  67. claude_mpm/services/agents/deployment/agent_record_service.py +1 -2
  68. claude_mpm/services/agents/memory/memory_format_service.py +1 -5
  69. claude_mpm/services/cli/agent_cleanup_service.py +1 -2
  70. claude_mpm/services/cli/agent_dependency_service.py +1 -1
  71. claude_mpm/services/cli/agent_validation_service.py +3 -4
  72. claude_mpm/services/cli/dashboard_launcher.py +2 -3
  73. claude_mpm/services/cli/startup_checker.py +0 -10
  74. claude_mpm/services/core/cache_manager.py +1 -2
  75. claude_mpm/services/core/path_resolver.py +1 -4
  76. claude_mpm/services/core/service_container.py +2 -2
  77. claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
  78. claude_mpm/services/infrastructure/monitoring/__init__.py +11 -11
  79. claude_mpm/services/infrastructure/monitoring.py +11 -11
  80. claude_mpm/services/project/architecture_analyzer.py +1 -1
  81. claude_mpm/services/project/dependency_analyzer.py +4 -4
  82. claude_mpm/services/project/language_analyzer.py +3 -3
  83. claude_mpm/services/project/metrics_collector.py +3 -6
  84. claude_mpm/services/socketio/handlers/__init__.py +2 -0
  85. claude_mpm/services/socketio/handlers/code_analysis.py +170 -0
  86. claude_mpm/services/socketio/handlers/registry.py +2 -0
  87. claude_mpm/services/socketio/server/connection_manager.py +4 -4
  88. claude_mpm/services/socketio/server/core.py +100 -11
  89. claude_mpm/services/socketio/server/main.py +8 -2
  90. claude_mpm/services/visualization/__init__.py +19 -0
  91. claude_mpm/services/visualization/mermaid_generator.py +938 -0
  92. claude_mpm/tools/__main__.py +208 -0
  93. claude_mpm/tools/code_tree_analyzer.py +778 -0
  94. claude_mpm/tools/code_tree_builder.py +632 -0
  95. claude_mpm/tools/code_tree_events.py +318 -0
  96. claude_mpm/tools/socketio_debug.py +671 -0
  97. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/METADATA +1 -1
  98. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/RECORD +102 -73
  99. claude_mpm/agents/schema/agent_schema.json +0 -314
  100. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/WHEEL +0 -0
  101. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/entry_points.txt +0 -0
  102. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.10.dist-info}/licenses/LICENSE +0 -0
  103. {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
- try:
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 if debugging
277
- event_type = event_data.get("subtype", "unknown")
278
- self.logger.debug(f"Received HTTP event: {event_type}")
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
- # The event is already in claude_event format from the hook handler
283
- await self.sio.emit("claude_event", event_data)
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
- # Update stats
286
- self.stats["events_sent"] = self.stats.get("events_sent", 0) + 1
373
+ # Update stats manually if using fallback
374
+ self.stats["events_sent"] = self.stats.get("events_sent", 0) + 1
287
375
 
288
- # Add to event buffer for late-joining clients
289
- with self.buffer_lock:
290
- self.event_buffer.append(event_data)
291
- self.stats["events_buffered"] = len(self.event_buffer)
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 health monitoring in connection handler
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
+ ]