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
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Code Tree Event Emitter
4
+ =======================
5
+
6
+ WHY: Provides incremental event emission for real-time code tree visualization
7
+ in the dashboard. Uses Socket.IO to stream events as code is analyzed.
8
+
9
+ DESIGN DECISIONS:
10
+ - Batch events for performance (emit every 10 nodes or 100ms)
11
+ - Include progress tracking for large codebases
12
+ - Use structured event format for dashboard consumption
13
+ - Support resumable processing with event checkpoints
14
+ """
15
+
16
+ import json
17
+ import threading
18
+ import time
19
+ from collections import deque
20
+ from dataclasses import asdict, dataclass
21
+ from datetime import datetime
22
+ from typing import Any, Dict, List, Optional
23
+
24
+ try:
25
+ import socketio
26
+
27
+ SOCKETIO_AVAILABLE = True
28
+ except ImportError:
29
+ SOCKETIO_AVAILABLE = False
30
+ socketio = None
31
+
32
+ from ..core.logging_config import get_logger
33
+
34
+
35
+ @dataclass
36
+ class CodeNodeEvent:
37
+ """Represents a code node discovery event."""
38
+
39
+ file_path: str
40
+ node_type: str # 'class', 'function', 'method', 'module', 'import'
41
+ name: str
42
+ line_start: int
43
+ line_end: int
44
+ complexity: int = 0
45
+ has_docstring: bool = False
46
+ decorators: List[str] = None
47
+ parent: Optional[str] = None
48
+ children_count: int = 0
49
+ language: str = "python"
50
+
51
+ def to_dict(self) -> Dict[str, Any]:
52
+ """Convert to dictionary for JSON serialization."""
53
+ data = asdict(self)
54
+ data["timestamp"] = datetime.utcnow().isoformat()
55
+ return data
56
+
57
+
58
+ class CodeTreeEventEmitter:
59
+ """Emits code analysis events to dashboard via Socket.IO.
60
+
61
+ WHY: Real-time streaming of analysis progress allows users to see
62
+ the tree building incrementally, providing feedback for large codebases.
63
+ """
64
+
65
+ # Event types
66
+ EVENT_FILE_START = "code:file:start"
67
+ EVENT_FILE_COMPLETE = "code:file:complete"
68
+ EVENT_NODE_FOUND = "code:node:found"
69
+ EVENT_ANALYSIS_START = "code:analysis:start"
70
+ EVENT_ANALYSIS_COMPLETE = "code:analysis:complete"
71
+ EVENT_PROGRESS = "code:progress"
72
+ EVENT_ERROR = "code:error"
73
+
74
+ def __init__(
75
+ self,
76
+ socketio_url: str = "http://localhost:8765",
77
+ batch_size: int = 10,
78
+ batch_timeout: float = 0.1,
79
+ use_stdout: bool = False,
80
+ ):
81
+ """Initialize event emitter.
82
+
83
+ Args:
84
+ socketio_url: Socket.IO server URL
85
+ batch_size: Number of events to batch before emitting
86
+ batch_timeout: Maximum time to wait before emitting batch (seconds)
87
+ use_stdout: If True, emit to stdout instead of Socket.IO
88
+ """
89
+ self.logger = get_logger(__name__)
90
+ self.socketio_url = socketio_url
91
+ self.batch_size = batch_size
92
+ self.batch_timeout = batch_timeout
93
+ self.use_stdout = use_stdout
94
+
95
+ # Event buffer for batching
96
+ self.event_buffer = deque()
97
+ self.buffer_lock = threading.Lock()
98
+ self.last_emit_time = time.time()
99
+
100
+ # Socket.IO client
101
+ self.sio = None
102
+ self.connected = False
103
+ if not use_stdout:
104
+ self._init_socketio()
105
+
106
+ # Statistics
107
+ self.stats = {
108
+ "events_sent": 0,
109
+ "events_buffered": 0,
110
+ "files_processed": 0,
111
+ "nodes_found": 0,
112
+ "errors": 0,
113
+ "start_time": None,
114
+ }
115
+
116
+ # Background task for periodic emission
117
+ self._emit_task = None
118
+ self._stop_event = threading.Event()
119
+
120
+ def _init_socketio(self):
121
+ """Initialize Socket.IO client connection."""
122
+ if not SOCKETIO_AVAILABLE:
123
+ self.logger.warning("Socket.IO not available - events will be logged only")
124
+ return
125
+
126
+ try:
127
+ self.sio = socketio.Client(
128
+ reconnection=True,
129
+ reconnection_attempts=3,
130
+ reconnection_delay=1,
131
+ logger=False,
132
+ engineio_logger=False,
133
+ )
134
+
135
+ @self.sio.event
136
+ def connect():
137
+ self.connected = True
138
+ self.logger.info(
139
+ f"Connected to Socket.IO server at {self.socketio_url}"
140
+ )
141
+
142
+ @self.sio.event
143
+ def disconnect():
144
+ self.connected = False
145
+ self.logger.info("Disconnected from Socket.IO server")
146
+
147
+ # Attempt connection
148
+ self.sio.connect(self.socketio_url, wait_timeout=2)
149
+
150
+ except Exception as e:
151
+ self.logger.warning(f"Failed to connect to Socket.IO: {e}")
152
+ self.sio = None
153
+
154
+ def start(self):
155
+ """Start the event emitter and background tasks."""
156
+ self.stats["start_time"] = datetime.utcnow()
157
+ self._stop_event.clear()
158
+
159
+ # Start background emit task
160
+ self._emit_task = threading.Thread(target=self._emit_loop, daemon=True)
161
+ self._emit_task.start()
162
+
163
+ # Emit analysis start event
164
+ self.emit(
165
+ self.EVENT_ANALYSIS_START,
166
+ {
167
+ "timestamp": datetime.utcnow().isoformat(),
168
+ "batch_size": self.batch_size,
169
+ "batch_timeout": self.batch_timeout,
170
+ },
171
+ )
172
+
173
+ def stop(self):
174
+ """Stop the event emitter and flush remaining events."""
175
+ # Flush remaining events
176
+ self._flush_events()
177
+
178
+ # Emit analysis complete event with statistics
179
+ self.emit(
180
+ self.EVENT_ANALYSIS_COMPLETE,
181
+ {
182
+ "timestamp": datetime.utcnow().isoformat(),
183
+ "duration": (
184
+ (datetime.utcnow() - self.stats["start_time"]).total_seconds()
185
+ if self.stats["start_time"]
186
+ else 0
187
+ ),
188
+ "stats": self.stats,
189
+ },
190
+ )
191
+
192
+ # Stop background task
193
+ self._stop_event.set()
194
+ if self._emit_task:
195
+ self._emit_task.join(timeout=1)
196
+
197
+ # Disconnect Socket.IO
198
+ if self.sio and self.connected:
199
+ self.sio.disconnect()
200
+
201
+ def emit(self, event_type: str, data: Dict[str, Any], batch: bool = False):
202
+ """Emit an event, either immediately or batched.
203
+
204
+ Args:
205
+ event_type: Type of event to emit
206
+ data: Event data
207
+ batch: Whether to batch this event
208
+ """
209
+ event = {
210
+ "type": event_type,
211
+ "data": data,
212
+ "timestamp": datetime.utcnow().isoformat(),
213
+ }
214
+
215
+ if batch:
216
+ with self.buffer_lock:
217
+ self.event_buffer.append(event)
218
+ self.stats["events_buffered"] += 1
219
+
220
+ # Check if we should flush
221
+ if len(self.event_buffer) >= self.batch_size:
222
+ self._flush_events()
223
+ else:
224
+ self._emit_event(event)
225
+
226
+ def emit_file_start(self, file_path: str, language: Optional[str] = None):
227
+ """Emit file processing start event."""
228
+ self.emit(
229
+ self.EVENT_FILE_START,
230
+ {"file": file_path, "language": language or "unknown"},
231
+ )
232
+
233
+ def emit_file_complete(
234
+ self, file_path: str, nodes_count: int = 0, duration: float = 0
235
+ ):
236
+ """Emit file processing complete event."""
237
+ self.stats["files_processed"] += 1
238
+ self.emit(
239
+ self.EVENT_FILE_COMPLETE,
240
+ {"file": file_path, "nodes_count": nodes_count, "duration": duration},
241
+ )
242
+
243
+ def emit_node(self, node: CodeNodeEvent):
244
+ """Emit code node discovery event (batched)."""
245
+ self.stats["nodes_found"] += 1
246
+ # In stdout mode, don't batch - emit immediately for real-time updates
247
+ batch_mode = not self.use_stdout
248
+ self.emit(self.EVENT_NODE_FOUND, node.to_dict(), batch=batch_mode)
249
+
250
+ def emit_progress(self, current: int, total: int, message: str = ""):
251
+ """Emit progress update event."""
252
+ self.emit(
253
+ self.EVENT_PROGRESS,
254
+ {
255
+ "current": current,
256
+ "total": total,
257
+ "percentage": (current / total * 100) if total > 0 else 0,
258
+ "message": message,
259
+ },
260
+ )
261
+
262
+ def emit_error(self, file_path: str, error: str):
263
+ """Emit error event."""
264
+ self.stats["errors"] += 1
265
+ self.emit(self.EVENT_ERROR, {"file": file_path, "error": str(error)})
266
+
267
+ def _emit_event(self, event: Dict[str, Any]):
268
+ """Emit a single event."""
269
+ if self.use_stdout:
270
+ # Emit to stdout as JSON for subprocess communication
271
+ print(json.dumps(event), flush=True)
272
+ self.stats["events_sent"] += 1
273
+ elif self.sio and self.connected:
274
+ try:
275
+ self.sio.emit("code_tree_event", event)
276
+ self.stats["events_sent"] += 1
277
+ except Exception as e:
278
+ self.logger.error(f"Failed to emit event: {e}")
279
+ else:
280
+ # Fallback to logging
281
+ self.logger.debug(
282
+ f"Event: {event['type']} - {json.dumps(event['data'])[:100]}"
283
+ )
284
+
285
+ def _flush_events(self):
286
+ """Flush all buffered events."""
287
+ with self.buffer_lock:
288
+ if not self.event_buffer:
289
+ return
290
+
291
+ # Emit as batch
292
+ batch_event = {
293
+ "type": "code:batch",
294
+ "data": {
295
+ "events": list(self.event_buffer),
296
+ "count": len(self.event_buffer),
297
+ },
298
+ "timestamp": datetime.utcnow().isoformat(),
299
+ }
300
+
301
+ self._emit_event(batch_event)
302
+ self.event_buffer.clear()
303
+ self.last_emit_time = time.time()
304
+
305
+ def _emit_loop(self):
306
+ """Background loop for periodic event emission."""
307
+ while not self._stop_event.is_set():
308
+ time.sleep(self.batch_timeout)
309
+
310
+ # Check if enough time has passed since last emit
311
+ if time.time() - self.last_emit_time >= self.batch_timeout:
312
+ with self.buffer_lock:
313
+ if self.event_buffer:
314
+ self._flush_events()
315
+
316
+ def get_stats(self) -> Dict[str, Any]:
317
+ """Get current statistics."""
318
+ return self.stats.copy()