claude-mpm 4.2.2__py3-none-any.whl → 4.2.3__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.
@@ -1218,8 +1218,11 @@ class SocketClient {
1218
1218
  transformedEvent[key] = eventData.data[key];
1219
1219
  }
1220
1220
  } else {
1221
- // Log warning if data field would overwrite a protected field
1222
- console.warn(`Protected field '${key}' in data object was not copied to top level to preserve event structure`);
1221
+ // Log debug info if data field would overwrite a protected field
1222
+ // Only log for non-timestamp fields to reduce noise
1223
+ if (key !== 'timestamp') {
1224
+ console.debug(`Protected field '${key}' in data object was not copied to top level to preserve event structure`);
1225
+ }
1223
1226
  }
1224
1227
  });
1225
1228
 
@@ -247,7 +247,6 @@
247
247
  <button class="tab-button" data-tab="files">📁 Files</button>
248
248
  <button class="tab-button" data-tab="activity">🌳 Activity</button>
249
249
  <button class="tab-button" data-tab="code">🧬 Code</button>
250
- <a href="/code-simple" class="tab-button" style="background: #f7fafc; color: #4a5568; text-decoration: none; border-left: 2px solid #e2e8f0;">📁 Simple View</a>
251
250
  </div>
252
251
 
253
252
  <!-- Events Tab -->
@@ -389,45 +388,52 @@
389
388
  <!-- Code Tab -->
390
389
  <div class="tab-content" id="code-tab">
391
390
  <div class="code-container">
392
- <!-- Simplified header with controls -->
393
- <div class="code-header-compact">
394
- <div class="header-left">
395
- <button id="code-expand-all" class="btn-compact" title="Expand All">⊕</button>
396
- <button id="code-collapse-all" class="btn-compact" title="Collapse All">⊖</button>
397
- <button id="code-reset-zoom" class="btn-compact" title="Reset Zoom">⟲</button>
398
- <button id="code-toggle-legend" class="btn-compact" title="Toggle Legend">ℹ️</button>
399
- </div>
400
- <div class="header-center">
401
- <span class="stat-compact">📁 <span id="file-count">0</span></span>
402
- <span class="stat-compact">🏛️ <span id="class-count">0</span></span>
403
- <span class="stat-compact">⚡ <span id="function-count">0</span></span>
404
- <span class="stat-compact">📝 <span id="line-count">0</span></span>
391
+ <div id="code-tree-container" class="code-tree-container">
392
+ <!-- Top-left corner: Language selector -->
393
+ <div class="tree-corner-controls top-left">
394
+ <div class="control-group">
395
+ <label class="control-label">Languages:</label>
396
+ <div class="checkbox-group">
397
+ <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="python" checked> Python</label>
398
+ <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="javascript" checked> JS</label>
399
+ <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="typescript" checked> TS</label>
400
+ </div>
401
+ </div>
405
402
  </div>
406
- <div class="header-right">
407
- <select id="language-filter" class="select-compact">
408
- <option value="all">All</option>
409
- <option value="python">Python</option>
410
- <option value="javascript">JS</option>
411
- <option value="typescript">TS</option>
412
- </select>
413
- <input type="text" id="code-search" placeholder="Search..." class="search-compact">
403
+
404
+ <!-- Top-right corner: Layout and search -->
405
+ <div class="tree-corner-controls top-right">
406
+ <div class="control-group">
407
+ <select id="code-layout" class="select-compact">
408
+ <option value="tree">Tree</option>
409
+ <option value="radial">Radial</option>
410
+ </select>
411
+ <input type="text" id="code-search" placeholder="Search..." class="search-compact">
412
+ </div>
414
413
  </div>
415
- </div>
416
- <!-- Advanced options - visible by default -->
417
- <div class="code-advanced-options-visible">
418
- <div class="advanced-content">
419
- <div class="option-group">
420
- <label>Languages:</label>
421
- <label><input type="checkbox" class="language-checkbox" value="python" checked> Python</label>
422
- <label><input type="checkbox" class="language-checkbox" value="javascript" checked> JS</label>
423
- <label><input type="checkbox" class="language-checkbox" value="typescript" checked> TS</label>
414
+
415
+ <!-- Bottom-left corner: Stats and Status -->
416
+ <div class="tree-corner-controls bottom-left">
417
+ <div class="stats-display" id="code-stats">
418
+ <span id="stats-files">0 files</span> •
419
+ <span id="stats-classes">0 classes</span>
420
+ <span id="stats-functions">0 functions</span>
421
+ <span id="stats-methods">0 methods</span>
424
422
  </div>
425
- <div class="option-group">
426
- <label>Ignore: <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js, node_modules" class="input-compact" style="width: 200px;"></label>
423
+ <div class="status-display" id="code-breadcrumb">
424
+ <div class="breadcrumb-ticker" id="breadcrumb-ticker">
425
+ <span id="breadcrumb-content">Ready to analyze...</span>
426
+ </div>
427
+ </div>
428
+ </div>
429
+
430
+ <!-- Bottom-right corner: Ignore patterns -->
431
+ <div class="tree-corner-controls bottom-right">
432
+ <div class="control-group">
433
+ <label class="control-label">Ignore:</label>
434
+ <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js, node_modules" class="input-compact">
427
435
  </div>
428
436
  </div>
429
- </div>
430
- <div id="code-tree-container" class="code-tree-container">
431
437
  <div id="code-tree"></div>
432
438
  <!-- Collapsible legend -->
433
439
  <div class="tree-legend collapsed" id="tree-legend" style="display: none;">
@@ -447,11 +453,6 @@
447
453
  </div>
448
454
  </div>
449
455
  </div>
450
- <div class="code-breadcrumb" id="code-breadcrumb">
451
- <div class="breadcrumb-ticker" id="breadcrumb-ticker">
452
- <span id="breadcrumb-content">Ready to analyze...</span>
453
- </div>
454
- </div>
455
456
  </div>
456
457
  </div>
457
458
 
@@ -704,16 +704,29 @@ tools:
704
704
  if triggers and not examples:
705
705
  # Convert first trigger to example with commentary
706
706
  trigger = triggers[0]
707
+
708
+ # Handle both string and dict trigger formats
709
+ if isinstance(trigger, dict):
710
+ # New format with pattern and confidence
711
+ trigger_text = trigger.get("pattern", "")
712
+ else:
713
+ # Old format with simple string
714
+ trigger_text = str(trigger)
715
+
716
+ # Skip if we don't have valid trigger text
717
+ if not trigger_text:
718
+ return examples
719
+
707
720
  agent_type = template_data.get("agent_type", "general")
708
721
 
709
722
  examples.extend(
710
723
  [
711
724
  "<example>",
712
- f"Context: When user needs {trigger}",
713
- f'user: "{trigger}"',
714
- f'assistant: "I\'ll use the {agent_name} agent for {trigger}."',
725
+ f"Context: When user needs {trigger_text}",
726
+ f'user: "{trigger_text}"',
727
+ f'assistant: "I\'ll use the {agent_name} agent for {trigger_text}."',
715
728
  "<commentary>",
716
- f"This {agent_type} agent is appropriate because it has specialized capabilities for {trigger.lower()} tasks.",
729
+ f"This {agent_type} agent is appropriate because it has specialized capabilities for {trigger_text.lower()} tasks.",
717
730
  "</commentary>",
718
731
  "</example>",
719
732
  ]
@@ -0,0 +1,389 @@
1
+ """
2
+ Stable Dashboard Server for claude-mpm.
3
+
4
+ WHY: This module provides a simple, stable HTTP + SocketIO server that works
5
+ across all installation methods (direct, pip, pipx, homebrew, npm).
6
+
7
+ DESIGN DECISIONS:
8
+ - Uses proven python-socketio + aiohttp combination
9
+ - Automatically finds dashboard files across installation methods
10
+ - Provides both HTTP endpoints and SocketIO real-time features
11
+ - Simple mock AST analysis to avoid complex backend dependencies
12
+ - Graceful fallbacks for missing dependencies
13
+ """
14
+
15
+ import glob
16
+ import os
17
+ import sys
18
+ from pathlib import Path
19
+ from typing import Any, Dict, Optional
20
+
21
+ try:
22
+ import aiohttp
23
+ import socketio
24
+ from aiohttp import web
25
+
26
+ DEPENDENCIES_AVAILABLE = True
27
+ except ImportError:
28
+ DEPENDENCIES_AVAILABLE = False
29
+ socketio = None
30
+ aiohttp = None
31
+ web = None
32
+
33
+
34
+ def find_dashboard_files() -> Optional[Path]:
35
+ """Find dashboard files across different installation methods."""
36
+ # Try different possible locations
37
+ possible_locations = [
38
+ # Development/direct install
39
+ Path(__file__).parent.parent.parent / "dashboard",
40
+ # Current working directory (for development)
41
+ Path.cwd() / "src" / "claude_mpm" / "dashboard",
42
+ # Pip install in current Python environment
43
+ Path(sys.prefix)
44
+ / "lib"
45
+ / f"python{sys.version_info.major}.{sys.version_info.minor}"
46
+ / "site-packages"
47
+ / "claude_mpm"
48
+ / "dashboard",
49
+ # User site-packages
50
+ Path.home()
51
+ / ".local"
52
+ / "lib"
53
+ / f"python{sys.version_info.major}.{sys.version_info.minor}"
54
+ / "site-packages"
55
+ / "claude_mpm"
56
+ / "dashboard",
57
+ ]
58
+
59
+ # Add glob patterns for different Python versions
60
+ python_patterns = [
61
+ f"/opt/homebrew/lib/python{sys.version_info.major}.{sys.version_info.minor}/site-packages/claude_mpm/dashboard",
62
+ f"/usr/local/lib/python{sys.version_info.major}.{sys.version_info.minor}/site-packages/claude_mpm/dashboard",
63
+ ]
64
+
65
+ # Check direct paths first
66
+ for location in possible_locations:
67
+ if location.exists() and (location / "templates" / "index.html").exists():
68
+ return location
69
+
70
+ # Check pattern-based paths
71
+ for pattern in python_patterns:
72
+ matches = glob.glob(pattern)
73
+ for match in matches:
74
+ path = Path(match)
75
+ if path.exists() and (path / "templates" / "index.html").exists():
76
+ return path
77
+
78
+ # Fallback: try to find via module import
79
+ try:
80
+ import claude_mpm.dashboard
81
+
82
+ module_path = Path(claude_mpm.dashboard.__file__).parent
83
+ if (module_path / "templates" / "index.html").exists():
84
+ return module_path
85
+ except ImportError:
86
+ pass
87
+
88
+ return None
89
+
90
+
91
+ def create_mock_ast_data(file_path: str, file_name: str) -> Dict[str, Any]:
92
+ """Create mock AST analysis data."""
93
+ ext = file_name.split(".")[-1].lower() if "." in file_name else ""
94
+
95
+ elements = []
96
+ if ext == "py":
97
+ elements = [
98
+ {
99
+ "name": "MockClass",
100
+ "type": "class",
101
+ "line": 10,
102
+ "complexity": 2,
103
+ "docstring": "Mock class for demonstration",
104
+ "methods": [
105
+ {"name": "__init__", "type": "method", "line": 11, "complexity": 1},
106
+ {
107
+ "name": "mock_method",
108
+ "type": "method",
109
+ "line": 15,
110
+ "complexity": 1,
111
+ },
112
+ ],
113
+ },
114
+ {
115
+ "name": "mock_function",
116
+ "type": "function",
117
+ "line": 20,
118
+ "complexity": 1,
119
+ "docstring": "Mock function for demonstration",
120
+ },
121
+ ]
122
+ elif ext in ["js", "ts", "jsx", "tsx"]:
123
+ elements = [
124
+ {
125
+ "name": "MockClass",
126
+ "type": "class",
127
+ "line": 5,
128
+ "complexity": 2,
129
+ "methods": [
130
+ {
131
+ "name": "constructor",
132
+ "type": "method",
133
+ "line": 6,
134
+ "complexity": 1,
135
+ },
136
+ {
137
+ "name": "mockMethod",
138
+ "type": "method",
139
+ "line": 10,
140
+ "complexity": 1,
141
+ },
142
+ ],
143
+ },
144
+ {"name": "mockFunction", "type": "function", "line": 15, "complexity": 1},
145
+ ]
146
+
147
+ return {
148
+ "path": file_path,
149
+ "elements": elements,
150
+ "complexity": sum(e.get("complexity", 1) for e in elements),
151
+ "lines": 50,
152
+ "stats": {
153
+ "classes": len([e for e in elements if e["type"] == "class"]),
154
+ "functions": len([e for e in elements if e["type"] == "function"]),
155
+ "methods": sum(len(e.get("methods", [])) for e in elements),
156
+ "lines": 50,
157
+ },
158
+ }
159
+
160
+
161
+ class StableDashboardServer:
162
+ """Stable dashboard server that works across all installation methods."""
163
+
164
+ def __init__(self, host: str = "localhost", port: int = 8765, debug: bool = False):
165
+ self.host = host
166
+ self.port = port
167
+ self.debug = debug
168
+ self.dashboard_path = None
169
+ self.app = None
170
+ self.sio = None
171
+
172
+ def setup(self) -> bool:
173
+ """Set up the server components."""
174
+ if not DEPENDENCIES_AVAILABLE:
175
+ print(
176
+ "❌ Error: Missing dependencies. Install with: pip install aiohttp python-socketio"
177
+ )
178
+ return False
179
+
180
+ # Find dashboard files
181
+ self.dashboard_path = find_dashboard_files()
182
+ if not self.dashboard_path:
183
+ print("❌ Error: Could not find dashboard files")
184
+ print("Please ensure Claude MPM is properly installed")
185
+ return False
186
+
187
+ print(f"📁 Using dashboard files from: {self.dashboard_path}")
188
+
189
+ # Create SocketIO server
190
+ self.sio = socketio.AsyncServer(
191
+ cors_allowed_origins="*", logger=True, engineio_logger=True
192
+ )
193
+ self.app = web.Application()
194
+ self.sio.attach(self.app)
195
+ print("✅ SocketIO server created and attached")
196
+
197
+ # Set up routes
198
+ self._setup_routes()
199
+ self._setup_socketio_events()
200
+
201
+ print("✅ Server setup complete!")
202
+
203
+ return True
204
+
205
+ def _setup_routes(self):
206
+ """Set up HTTP routes."""
207
+ self.app.router.add_get("/", self._serve_dashboard)
208
+ self.app.router.add_get("/static/{path:.*}", self._serve_static)
209
+ self.app.router.add_get("/api/directory/list", self._list_directory)
210
+ self.app.router.add_get("/version.json", self._serve_version)
211
+
212
+ def _setup_socketio_events(self):
213
+ """Set up SocketIO event handlers."""
214
+
215
+ @self.sio.event
216
+ async def connect(sid, environ):
217
+ print(f"✅ SocketIO client connected: {sid}")
218
+ print(f" Client info: {environ.get('HTTP_USER_AGENT', 'Unknown')}")
219
+ # Send a test message to confirm connection
220
+ await self.sio.emit(
221
+ "connection_test", {"status": "connected", "server": "stable"}, room=sid
222
+ )
223
+
224
+ @self.sio.event
225
+ async def disconnect(sid):
226
+ print(f"❌ SocketIO client disconnected: {sid}")
227
+
228
+ @self.sio.event
229
+ async def code_analyze_file(sid, data):
230
+ print(
231
+ f"📡 Received file analysis request from {sid}: {data.get('path', 'unknown')}"
232
+ )
233
+
234
+ file_path = data.get("path", "")
235
+ file_name = file_path.split("/")[-1] if file_path else "unknown"
236
+
237
+ # Create mock response
238
+ response = create_mock_ast_data(file_path, file_name)
239
+
240
+ print(f"📤 Sending analysis response: {len(response['elements'])} elements")
241
+ await self.sio.emit("code:file:analyzed", response, room=sid)
242
+
243
+ # CRITICAL: Handle the actual event name with colons that the client sends
244
+ @self.sio.on("code:analyze:file")
245
+ async def handle_code_analyze_file(sid, data):
246
+ print(
247
+ f"📡 Received code:analyze:file from {sid}: {data.get('path', 'unknown')}"
248
+ )
249
+
250
+ file_path = data.get("path", "")
251
+ file_name = file_path.split("/")[-1] if file_path else "unknown"
252
+
253
+ # Create mock response
254
+ response = create_mock_ast_data(file_path, file_name)
255
+
256
+ print(f"📤 Sending analysis response: {len(response['elements'])} elements")
257
+ await self.sio.emit("code:file:analyzed", response, room=sid)
258
+
259
+ # Handle other events the dashboard sends
260
+ @self.sio.event
261
+ async def get_git_branch(sid, data):
262
+ print(f"📡 Received git branch request from {sid}: {data}")
263
+ await self.sio.emit(
264
+ "git_branch_response", {"branch": "main", "path": data}, room=sid
265
+ )
266
+
267
+ @self.sio.event
268
+ async def request_status(sid, data):
269
+ print(f"📡 Received status request from {sid}")
270
+ await self.sio.emit(
271
+ "status_response", {"status": "running", "server": "stable"}, room=sid
272
+ )
273
+
274
+ # Handle the event with dots (SocketIO converts colons to dots sometimes)
275
+ @self.sio.event
276
+ async def request_dot_status(sid, data):
277
+ print(f"📡 Received request.status from {sid}")
278
+ await self.sio.emit(
279
+ "status_response", {"status": "running", "server": "stable"}, room=sid
280
+ )
281
+
282
+ @self.sio.event
283
+ async def code_discover_top_level(sid, data):
284
+ print(f"📡 Received top-level discovery request from {sid}")
285
+ await self.sio.emit("code:top_level:discovered", {"status": "ok"}, room=sid)
286
+
287
+ async def _serve_dashboard(self, request):
288
+ """Serve the main dashboard HTML."""
289
+ dashboard_file = self.dashboard_path / "templates" / "index.html"
290
+ if dashboard_file.exists():
291
+ with open(dashboard_file) as f:
292
+ content = f.read()
293
+ return web.Response(text=content, content_type="text/html")
294
+ return web.Response(text="Dashboard not found", status=404)
295
+
296
+ async def _serve_static(self, request):
297
+ """Serve static files."""
298
+ file_path = request.match_info["path"]
299
+ static_file = self.dashboard_path / "static" / file_path
300
+
301
+ if static_file.exists() and static_file.is_file():
302
+ content_type = (
303
+ "text/javascript"
304
+ if file_path.endswith(".js")
305
+ else "text/css" if file_path.endswith(".css") else "text/plain"
306
+ )
307
+ with open(static_file) as f:
308
+ content = f.read()
309
+ return web.Response(text=content, content_type=content_type)
310
+ return web.Response(text="File not found", status=404)
311
+
312
+ async def _list_directory(self, request):
313
+ """List directory contents."""
314
+ path = request.query.get("path", ".")
315
+ abs_path = os.path.abspath(os.path.expanduser(path))
316
+
317
+ result = {"path": abs_path, "exists": os.path.exists(abs_path), "contents": []}
318
+
319
+ if os.path.exists(abs_path) and os.path.isdir(abs_path):
320
+ try:
321
+ for item in sorted(os.listdir(abs_path)):
322
+ item_path = os.path.join(abs_path, item)
323
+ result["contents"].append(
324
+ {
325
+ "name": item,
326
+ "path": item_path,
327
+ "is_directory": os.path.isdir(item_path),
328
+ "is_file": os.path.isfile(item_path),
329
+ "is_code_file": item.endswith(
330
+ (".py", ".js", ".ts", ".jsx", ".tsx")
331
+ ),
332
+ }
333
+ )
334
+ except PermissionError:
335
+ result["error"] = "Permission denied"
336
+
337
+ return web.json_response(result)
338
+
339
+ async def _serve_version(self, request):
340
+ """Serve version information."""
341
+ version_info = {
342
+ "version": "4.2.2",
343
+ "server": "stable",
344
+ "features": ["http", "socketio", "mock_ast"],
345
+ "status": "running",
346
+ }
347
+ return web.json_response(version_info)
348
+
349
+ def run(self):
350
+ """Run the server."""
351
+ print("🔧 Setting up server...")
352
+ if not self.setup():
353
+ print("❌ Server setup failed")
354
+ return False
355
+
356
+ print(f"🚀 Starting stable dashboard server at http://{self.host}:{self.port}")
357
+ print("✅ Server ready: HTTP + SocketIO on same port")
358
+ print("📡 SocketIO events registered:")
359
+ print(" - connect/disconnect")
360
+ print(" - code_analyze_file (from 'code:analyze:file')")
361
+ print("🌐 HTTP endpoints available:")
362
+ print(" - GET / (dashboard)")
363
+ print(" - GET /static/* (static files)")
364
+ print(" - GET /api/directory/list (directory API)")
365
+ print(f"🔗 Open in browser: http://{self.host}:{self.port}")
366
+
367
+ try:
368
+ web.run_app(self.app, host=self.host, port=self.port, access_log=None)
369
+ except KeyboardInterrupt:
370
+ print("\n🛑 Server stopped by user")
371
+ except Exception as e:
372
+ print(f"❌ Server error: {e}")
373
+ if self.debug:
374
+ import traceback
375
+
376
+ traceback.print_exc()
377
+ return False
378
+
379
+ return True
380
+
381
+
382
+ def create_stable_server(
383
+ dashboard_path: Optional[Path] = None, **kwargs
384
+ ) -> StableDashboardServer:
385
+ """Create a stable dashboard server instance."""
386
+ server = StableDashboardServer(**kwargs)
387
+ if dashboard_path:
388
+ server.dashboard_path = dashboard_path
389
+ return server
@@ -45,6 +45,22 @@ class SocketIOClientProxy:
45
45
  self._client_thread = None
46
46
  self._client_loop = None
47
47
 
48
+ def start(self):
49
+ """Start the Socket.IO client connection (compatibility wrapper).
50
+
51
+ This method exists for backward compatibility with code that expects
52
+ a start() method. It simply calls start_sync().
53
+ """
54
+ return self.start_sync()
55
+
56
+ def stop(self):
57
+ """Stop the Socket.IO client connection (compatibility wrapper).
58
+
59
+ This method exists for backward compatibility with code that expects
60
+ a stop() method. It simply calls stop_sync().
61
+ """
62
+ return self.stop_sync()
63
+
48
64
  def start_sync(self):
49
65
  """Start the Socket.IO client connection to the persistent server."""
50
66
  self.logger.debug(
@@ -591,8 +591,11 @@ class CodeAnalysisEventHandler(BaseEventHandler):
591
591
  return
592
592
 
593
593
  try:
594
+ self.logger.info(f"Starting file analysis for: {path}")
595
+
594
596
  # Ensure analyzer exists
595
597
  if not self.code_analyzer:
598
+ self.logger.info("Creating new CodeTreeAnalyzer instance")
596
599
  emitter = CodeTreeEventEmitter(use_stdout=False)
597
600
  # Override emit method to send to Socket.IO
598
601
  original_emit = emitter.emit
@@ -630,23 +633,42 @@ class CodeAnalysisEventHandler(BaseEventHandler):
630
633
  self.code_analyzer = CodeTreeAnalyzer(
631
634
  emit_events=False, emitter=emitter
632
635
  )
636
+ self.logger.info("CodeTreeAnalyzer created successfully")
633
637
 
634
638
  # Analyze file
639
+ self.logger.info(f"Calling analyze_file for: {path}")
635
640
  result = self.code_analyzer.analyze_file(path)
641
+ self.logger.info(
642
+ f"Analysis complete. Result keys: {list(result.keys()) if result else 'None'}"
643
+ )
644
+
645
+ if result:
646
+ self.logger.info(
647
+ f"Analysis result: elements={len(result.get('elements', []))}, nodes={len(result.get('nodes', []))}"
648
+ )
649
+ else:
650
+ self.logger.warning("Analysis returned None or empty result")
636
651
 
637
652
  # Send result with correct event name (using colons, not dots!)
653
+ response_data = {
654
+ "request_id": request_id,
655
+ "path": path,
656
+ **result,
657
+ }
658
+
659
+ self.logger.info(f"Emitting code:file:analyzed event to {sid}")
638
660
  await self.server.core.sio.emit(
639
661
  "code:file:analyzed",
640
- {
641
- "request_id": request_id,
642
- "path": path,
643
- **result,
644
- },
662
+ response_data,
645
663
  room=sid,
646
664
  )
665
+ self.logger.info("Event emitted successfully")
647
666
 
648
667
  except Exception as e:
649
668
  self.logger.error(f"Error analyzing file {path}: {e}")
669
+ import traceback
670
+
671
+ self.logger.error(f"Full traceback: {traceback.format_exc()}")
650
672
  await self.server.core.sio.emit(
651
673
  "code:analysis:error",
652
674
  {
@@ -2,7 +2,7 @@
2
2
  Lightweight MonitorServer for claude-mpm.
3
3
 
4
4
  WHY: This module provides a minimal, independent monitoring service that:
5
- - Runs as a stable background service on port 8766
5
+ - Runs as a stable background service on port 8765
6
6
  - Only handles event collection and relay (no UI components)
7
7
  - Has minimal dependencies and resource usage
8
8
  - Can run as always-on background service
@@ -60,7 +60,7 @@ class MonitorServer(SocketIOServiceInterface):
60
60
  monitor_config = config.get("monitor_server", {})
61
61
 
62
62
  self.host = host or monitor_config.get("host", "localhost")
63
- self.port = port or monitor_config.get("port", 8766)
63
+ self.port = port or monitor_config.get("port", 8765)
64
64
  self.logger = get_logger(__name__ + ".MonitorServer")
65
65
 
66
66
  # Configuration-based settings