claude-mpm 4.0.31__py3-none-any.whl → 4.0.34__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 (71) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +33 -25
  3. claude_mpm/agents/INSTRUCTIONS.md +14 -10
  4. claude_mpm/agents/templates/documentation.json +51 -34
  5. claude_mpm/agents/templates/research.json +0 -11
  6. claude_mpm/cli/__init__.py +63 -26
  7. claude_mpm/cli/commands/agent_manager.py +10 -8
  8. claude_mpm/core/framework_loader.py +272 -113
  9. claude_mpm/dashboard/static/css/dashboard.css +449 -0
  10. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  11. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  12. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  13. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  14. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  15. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  16. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  17. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +774 -0
  18. claude_mpm/dashboard/static/js/components/agent-inference.js +257 -3
  19. claude_mpm/dashboard/static/js/components/build-tracker.js +289 -0
  20. claude_mpm/dashboard/static/js/components/event-viewer.js +168 -39
  21. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +17 -0
  22. claude_mpm/dashboard/static/js/components/session-manager.js +23 -3
  23. claude_mpm/dashboard/static/js/components/socket-manager.js +2 -0
  24. claude_mpm/dashboard/static/js/dashboard.js +207 -31
  25. claude_mpm/dashboard/static/js/socket-client.js +85 -6
  26. claude_mpm/dashboard/templates/index.html +1 -0
  27. claude_mpm/hooks/claude_hooks/connection_pool.py +12 -2
  28. claude_mpm/hooks/claude_hooks/event_handlers.py +81 -19
  29. claude_mpm/hooks/claude_hooks/hook_handler.py +72 -10
  30. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +398 -0
  31. claude_mpm/hooks/claude_hooks/response_tracking.py +10 -0
  32. claude_mpm/services/agents/deployment/agent_deployment.py +86 -37
  33. claude_mpm/services/agents/deployment/agent_template_builder.py +18 -10
  34. claude_mpm/services/agents/deployment/agents_directory_resolver.py +10 -25
  35. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +189 -3
  36. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +3 -2
  37. claude_mpm/services/agents/deployment/strategies/system_strategy.py +10 -3
  38. claude_mpm/services/agents/deployment/strategies/user_strategy.py +10 -14
  39. claude_mpm/services/agents/deployment/system_instructions_deployer.py +8 -13
  40. claude_mpm/services/agents/memory/agent_memory_manager.py +141 -184
  41. claude_mpm/services/agents/memory/content_manager.py +182 -232
  42. claude_mpm/services/agents/memory/template_generator.py +4 -40
  43. claude_mpm/services/event_bus/__init__.py +18 -0
  44. claude_mpm/services/event_bus/event_bus.py +334 -0
  45. claude_mpm/services/event_bus/relay.py +301 -0
  46. claude_mpm/services/events/__init__.py +44 -0
  47. claude_mpm/services/events/consumers/__init__.py +18 -0
  48. claude_mpm/services/events/consumers/dead_letter.py +296 -0
  49. claude_mpm/services/events/consumers/logging.py +183 -0
  50. claude_mpm/services/events/consumers/metrics.py +242 -0
  51. claude_mpm/services/events/consumers/socketio.py +376 -0
  52. claude_mpm/services/events/core.py +470 -0
  53. claude_mpm/services/events/interfaces.py +230 -0
  54. claude_mpm/services/events/producers/__init__.py +14 -0
  55. claude_mpm/services/events/producers/hook.py +269 -0
  56. claude_mpm/services/events/producers/system.py +327 -0
  57. claude_mpm/services/mcp_gateway/core/process_pool.py +411 -0
  58. claude_mpm/services/mcp_gateway/server/stdio_server.py +13 -0
  59. claude_mpm/services/monitor_build_service.py +345 -0
  60. claude_mpm/services/socketio/event_normalizer.py +667 -0
  61. claude_mpm/services/socketio/handlers/connection.py +78 -20
  62. claude_mpm/services/socketio/handlers/hook.py +14 -5
  63. claude_mpm/services/socketio/migration_utils.py +329 -0
  64. claude_mpm/services/socketio/server/broadcaster.py +26 -33
  65. claude_mpm/services/socketio/server/core.py +4 -3
  66. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/METADATA +4 -3
  67. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/RECORD +71 -50
  68. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/WHEEL +0 -0
  69. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/entry_points.txt +0 -0
  70. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/licenses/LICENSE +0 -0
  71. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/top_level.txt +0 -0
@@ -39,6 +39,7 @@ class EventViewer {
39
39
 
40
40
  // Subscribe to socket events
41
41
  this.socketClient.onEventUpdate((events, sessions) => {
42
+ console.log('EventViewer received event update:', events?.length || 0, 'events');
42
43
  // Ensure we always have a valid events array
43
44
  this.events = Array.isArray(events) ? events : [];
44
45
  this.updateDisplay();
@@ -210,6 +211,7 @@ class EventViewer {
210
211
  * Update the display with current events
211
212
  */
212
213
  updateDisplay() {
214
+ console.log('EventViewer updating display with', this.events?.length || 0, 'events');
213
215
  this.updateEventTypeDropdown();
214
216
  this.applyFilters();
215
217
  }
@@ -282,8 +284,8 @@ class EventViewer {
282
284
  formatEventType(event) {
283
285
  // If we have type and subtype, use them
284
286
  if (event.type && event.subtype) {
285
- // Check if type and subtype are identical to prevent "type.type" display
286
- if (event.type === event.subtype) {
287
+ // Check if type and subtype are identical or subtype is 'generic' to prevent redundant display
288
+ if (event.type === event.subtype || event.subtype === 'generic') {
287
289
  return event.type;
288
290
  }
289
291
  return `${event.type}.${event.subtype}`;
@@ -407,10 +409,25 @@ class EventViewer {
407
409
  const stopType = data.stop_type || 'normal';
408
410
  return `<strong>Stop (${stopType}):</strong> ${reason}`;
409
411
 
412
+ case 'subagent_start':
413
+ // Try multiple locations for agent type
414
+ const startAgentType = data.agent_type || data.agent || data.subagent_type || 'Unknown';
415
+ const startPrompt = data.prompt || data.description || data.task || 'No description';
416
+ const startTruncated = startPrompt.length > 60 ? startPrompt.substring(0, 60) + '...' : startPrompt;
417
+ // Format with proper agent type display
418
+ const startAgentDisplay = this.formatAgentType(startAgentType);
419
+ return `<strong>Subagent Start (${startAgentDisplay}):</strong> ${startTruncated}`;
420
+
410
421
  case 'subagent_stop':
411
- const agentType = data.agent_type || 'unknown agent';
412
- const stopReason = data.reason || 'unknown';
413
- return `<strong>Subagent Stop (${agentType}):</strong> ${stopReason}`;
422
+ // Try multiple locations for agent type
423
+ const agentType = data.agent_type || data.agent || data.subagent_type || 'Unknown';
424
+ const stopReason = data.reason || data.stop_reason || 'completed';
425
+ // Format with proper agent type display
426
+ const stopAgentDisplay = this.formatAgentType(agentType);
427
+ // Include task completion status if available
428
+ const isCompleted = data.structured_response?.task_completed;
429
+ const completionStatus = isCompleted !== undefined ? (isCompleted ? ' ✓' : ' ✗') : '';
430
+ return `<strong>Subagent Stop (${stopAgentDisplay})${completionStatus}:</strong> ${stopReason}`;
414
431
 
415
432
  default:
416
433
  // Fallback to original logic for unknown hook types
@@ -463,77 +480,186 @@ class EventViewer {
463
480
  return JSON.stringify(data);
464
481
  }
465
482
 
483
+ /**
484
+ * Format agent type for display with proper capitalization
485
+ * @param {string} agentType - The raw agent type string
486
+ * @returns {string} Formatted agent type for display
487
+ */
488
+ formatAgentType(agentType) {
489
+ // Handle common agent type patterns
490
+ const agentTypeMap = {
491
+ 'research': 'Research',
492
+ 'architect': 'Architect',
493
+ 'engineer': 'Engineer',
494
+ 'qa': 'QA',
495
+ 'pm': 'PM',
496
+ 'project_manager': 'PM',
497
+ 'research_agent': 'Research',
498
+ 'architect_agent': 'Architect',
499
+ 'engineer_agent': 'Engineer',
500
+ 'qa_agent': 'QA',
501
+ 'unknown': 'Unknown'
502
+ };
503
+
504
+ // Try to find a match in the map (case-insensitive)
505
+ const lowerType = (agentType || 'unknown').toLowerCase();
506
+ if (agentTypeMap[lowerType]) {
507
+ return agentTypeMap[lowerType];
508
+ }
509
+
510
+ // If not in map, try to extract the agent name from patterns like "Research Agent" or "research_agent"
511
+ const match = agentType.match(/^(\w+)(?:_agent|Agent)?$/i);
512
+ if (match && match[1]) {
513
+ // Capitalize first letter
514
+ return match[1].charAt(0).toUpperCase() + match[1].slice(1).toLowerCase();
515
+ }
516
+
517
+ // Fallback: just capitalize first letter of whatever we have
518
+ return agentType.charAt(0).toUpperCase() + agentType.slice(1);
519
+ }
520
+
466
521
  /**
467
522
  * Format event content for single-row display (without timestamp)
468
- * Format: "hook.pre_tool Pre-Tool (task_management): TodoWrite"
523
+ * Format: "{type}.{subtype}" followed by data details
469
524
  * @param {Object} event - Event object
470
525
  * @returns {string} Formatted single-row event content string
471
526
  */
472
527
  formatSingleRowEventContent(event) {
473
528
  const eventType = this.formatEventType(event);
474
529
  const data = event.data || {};
530
+
531
+ // Include source if it's not the default 'system' source
532
+ const sourcePrefix = event.source && event.source !== 'system' ? `[${event.source}] ` : '';
475
533
 
476
- // Extract event details for different event types
477
- let eventDetails = '';
478
- let category = '';
479
- let action = '';
534
+ // Extract meaningful details from the data package for different event types
535
+ let dataDetails = '';
480
536
 
481
537
  switch (event.type) {
482
538
  case 'hook':
483
- // Hook events: extract tool name and hook type
539
+ // Hook events: show tool name and operation details
484
540
  const toolName = event.tool_name || data.tool_name || 'Unknown';
485
541
  const hookType = event.subtype || 'Unknown';
486
- const hookDisplayName = this.getHookDisplayName(hookType, data);
487
- category = this.getEventCategory(event);
488
- eventDetails = `${hookDisplayName} (${category}): ${toolName}`;
542
+
543
+ // Format specific hook types
544
+ if (hookType === 'pre_tool' || hookType === 'post_tool') {
545
+ const operation = data.operation_type || '';
546
+ const status = hookType === 'post_tool' && data.success !== undefined
547
+ ? (data.success ? '✓' : '✗')
548
+ : '';
549
+ dataDetails = `${toolName}${operation ? ` (${operation})` : ''}${status ? ` ${status}` : ''}`;
550
+ } else if (hookType === 'user_prompt') {
551
+ const prompt = data.prompt_text || data.prompt_preview || '';
552
+ const truncated = prompt.length > 60 ? prompt.substring(0, 60) + '...' : prompt;
553
+ dataDetails = truncated || 'No prompt text';
554
+ } else if (hookType === 'subagent_start') {
555
+ // Enhanced agent type detection
556
+ const agentType = data.agent_type || data.agent || data.subagent_type || 'Unknown';
557
+ const agentDisplay = this.formatAgentType(agentType);
558
+ const prompt = data.prompt || data.description || data.task || '';
559
+ const truncated = prompt.length > 40 ? prompt.substring(0, 40) + '...' : prompt;
560
+ dataDetails = truncated ? `${agentDisplay} - ${truncated}` : agentDisplay;
561
+ } else if (hookType === 'subagent_stop') {
562
+ // Enhanced agent type detection for subagent_stop
563
+ const agentType = data.agent_type || data.agent || data.subagent_type || 'Unknown';
564
+ const agentDisplay = this.formatAgentType(agentType);
565
+ const reason = data.reason || data.stop_reason || 'completed';
566
+ const isCompleted = data.structured_response?.task_completed;
567
+ const status = isCompleted !== undefined ? (isCompleted ? '✓' : '✗') : '';
568
+ dataDetails = `${agentDisplay}${status ? ' ' + status : ''} - ${reason}`;
569
+ } else if (hookType === 'stop') {
570
+ const reason = data.reason || 'completed';
571
+ const stopType = data.stop_type || 'normal';
572
+ dataDetails = `${stopType} - ${reason}`;
573
+ } else {
574
+ dataDetails = toolName;
575
+ }
489
576
  break;
490
577
 
491
578
  case 'agent':
492
- // Agent events
579
+ // Agent events: show agent name and status
493
580
  const agentName = event.subagent_type || data.subagent_type || 'PM';
494
- const agentAction = event.subtype || 'action';
495
- category = 'agent_operations';
496
- eventDetails = `${agentName} ${agentAction}`;
581
+ const status = data.status || '';
582
+ dataDetails = `${agentName}${status ? ` - ${status}` : ''}`;
497
583
  break;
498
584
 
499
585
  case 'todo':
500
- // Todo events
501
- const todoCount = data.todos ? data.todos.length : 0;
502
- category = 'task_management';
503
- eventDetails = `TodoWrite (${todoCount} items)`;
586
+ // Todo events: show item count and status changes
587
+ if (data.todos && Array.isArray(data.todos)) {
588
+ const count = data.todos.length;
589
+ const completed = data.todos.filter(t => t.status === 'completed').length;
590
+ const inProgress = data.todos.filter(t => t.status === 'in_progress').length;
591
+ dataDetails = `${count} items (${completed} completed, ${inProgress} in progress)`;
592
+ } else {
593
+ dataDetails = 'Todo update';
594
+ }
504
595
  break;
505
596
 
506
597
  case 'memory':
507
- // Memory events
598
+ // Memory events: show operation and key
508
599
  const operation = data.operation || 'unknown';
509
600
  const key = data.key || 'unknown';
510
- category = 'memory_operations';
511
- eventDetails = `${operation} ${key}`;
601
+ const value = data.value ? ` = ${JSON.stringify(data.value).substring(0, 30)}...` : '';
602
+ dataDetails = `${operation}: ${key}${value}`;
512
603
  break;
513
604
 
514
605
  case 'session':
515
- // Session events
516
- const sessionAction = event.subtype || 'unknown';
517
- category = 'session_management';
518
- eventDetails = `Session ${sessionAction}`;
606
+ // Session events: show session ID
607
+ const sessionId = data.session_id || 'unknown';
608
+ dataDetails = `ID: ${sessionId}`;
519
609
  break;
520
610
 
521
611
  case 'claude':
522
- // Claude events
523
- const claudeAction = event.subtype || 'interaction';
524
- category = 'claude_interactions';
525
- eventDetails = `Claude ${claudeAction}`;
612
+ // Claude events: show request/response preview
613
+ if (event.subtype === 'request') {
614
+ const prompt = data.prompt || data.message || '';
615
+ const truncated = prompt.length > 60 ? prompt.substring(0, 60) + '...' : prompt;
616
+ dataDetails = truncated || 'Empty request';
617
+ } else if (event.subtype === 'response') {
618
+ const response = data.response || data.content || '';
619
+ const truncated = response.length > 60 ? response.substring(0, 60) + '...' : response;
620
+ dataDetails = truncated || 'Empty response';
621
+ } else {
622
+ dataDetails = data.message || 'Claude interaction';
623
+ }
624
+ break;
625
+
626
+ case 'log':
627
+ // Log events: show log level and message
628
+ const level = data.level || 'info';
629
+ const message = data.message || '';
630
+ const truncated = message.length > 60 ? message.substring(0, 60) + '...' : message;
631
+ dataDetails = `[${level.toUpperCase()}] ${truncated}`;
632
+ break;
633
+
634
+ case 'test':
635
+ // Test events: show test name or details
636
+ const testName = data.test_name || data.name || 'Test';
637
+ dataDetails = testName;
526
638
  break;
527
639
 
528
640
  default:
529
- // Generic events
530
- category = 'general';
531
- eventDetails = event.type || 'Unknown Event';
641
+ // Generic events: show any available data
642
+ if (typeof data === 'string') {
643
+ dataDetails = data.length > 60 ? data.substring(0, 60) + '...' : data;
644
+ } else if (data.message) {
645
+ dataDetails = data.message.length > 60 ? data.message.substring(0, 60) + '...' : data.message;
646
+ } else if (data.name) {
647
+ dataDetails = data.name;
648
+ } else if (Object.keys(data).length > 0) {
649
+ // Show first meaningful field from data
650
+ const firstKey = Object.keys(data).find(k => !['timestamp', 'id'].includes(k));
651
+ if (firstKey) {
652
+ const value = data[firstKey];
653
+ dataDetails = `${firstKey}: ${typeof value === 'object' ? JSON.stringify(value).substring(0, 40) + '...' : value}`;
654
+ }
655
+ }
532
656
  break;
533
657
  }
534
658
 
535
- // Return formatted string: "type.subtype DisplayName (category): Details"
536
- return `${eventType} ${eventDetails}`;
659
+ // Return formatted string: "[source] {type}.{subtype} - {data details}"
660
+ // The eventType already contains the type.subtype format from formatEventType()
661
+ const fullType = `${sourcePrefix}${eventType}`;
662
+ return dataDetails ? `${fullType} - ${dataDetails}` : fullType;
537
663
  }
538
664
 
539
665
  /**
@@ -548,6 +674,7 @@ class EventViewer {
548
674
  'post_tool': 'Post-Tool',
549
675
  'user_prompt': 'User-Prompt',
550
676
  'stop': 'Stop',
677
+ 'subagent_start': 'Subagent-Start',
551
678
  'subagent_stop': 'Subagent-Stop',
552
679
  'notification': 'Notification'
553
680
  };
@@ -580,7 +707,9 @@ class EventViewer {
580
707
  return 'task_management';
581
708
  } else if (toolName === 'Task') {
582
709
  return 'agent_delegation';
583
- } else if (event.subtype === 'stop' || event.subtype === 'subagent_stop') {
710
+ } else if (event.subtype === 'subagent_start' || event.subtype === 'subagent_stop') {
711
+ return 'agent_delegation';
712
+ } else if (event.subtype === 'stop') {
584
713
  return 'session_control';
585
714
  }
586
715
 
@@ -361,10 +361,27 @@ class FileToolTracker {
361
361
  * @returns {string|null} - File path or null
362
362
  */
363
363
  extractFilePath(event) {
364
+ // Debug logging for file path extraction
365
+ const fileTools = ['Read', 'Write', 'Edit', 'MultiEdit', 'NotebookEdit'];
366
+ const toolName = event.tool_name || (event.data && event.data.tool_name);
367
+
368
+ if (fileTools.includes(toolName)) {
369
+ console.log('Extracting file path from event:', {
370
+ tool_name: toolName,
371
+ has_tool_parameters_top: !!event.tool_parameters,
372
+ has_tool_parameters_data: !!(event.data && event.data.tool_parameters),
373
+ tool_parameters: event.tool_parameters,
374
+ data_tool_parameters: event.data?.tool_parameters
375
+ });
376
+ }
377
+
364
378
  // Try various locations where file path might be stored
379
+ // Check top-level tool_parameters first (after transformation)
365
380
  if (event.tool_parameters?.file_path) return event.tool_parameters.file_path;
366
381
  if (event.tool_parameters?.path) return event.tool_parameters.path;
367
382
  if (event.tool_parameters?.notebook_path) return event.tool_parameters.notebook_path;
383
+
384
+ // Check in data object as fallback
368
385
  if (event.data?.tool_parameters?.file_path) return event.data.tool_parameters.file_path;
369
386
  if (event.data?.tool_parameters?.path) return event.data.tool_parameters.path;
370
387
  if (event.data?.tool_parameters?.notebook_path) return event.data.tool_parameters.notebook_path;
@@ -1,6 +1,16 @@
1
1
  /**
2
2
  * Session Manager Component
3
3
  * Handles session selection and management
4
+ *
5
+ * WHY: Provides session filtering and management for the dashboard, allowing users
6
+ * to view events from specific sessions or all sessions.
7
+ *
8
+ * BROWSER COMPATIBILITY: This component runs in the browser. All Node.js-specific
9
+ * globals (process, require, etc.) have been removed. Uses browser-compatible
10
+ * alternatives for path handling and defaults.
11
+ *
12
+ * FIX APPLIED: Removed process.cwd() reference that caused "process is not defined"
13
+ * error in browser. Now uses window.location.pathname or hardcoded fallbacks.
4
14
  */
5
15
 
6
16
  class SessionManager {
@@ -171,7 +181,12 @@ class SessionManager {
171
181
  }
172
182
 
173
183
  let sessionInfo = 'All Sessions';
174
- let workingDir = window.dashboard?.workingDirectoryManager?.getDefaultWorkingDir() || process?.cwd?.() || '/Users/masa/Projects/claude-mpm';
184
+ // Use browser-compatible fallback for working directory
185
+ // WHY: Removed process.cwd() Node.js reference - not available in browser
186
+ // BROWSER FIX: Use dashboard manager or hardcoded fallback
187
+ let workingDir = window.dashboard?.workingDirectoryManager?.getDefaultWorkingDir() ||
188
+ window.location.pathname ||
189
+ '/Users/masa/Projects/claude-mpm';
175
190
  let gitBranch = 'Unknown';
176
191
 
177
192
  console.log('[SESSION-DEBUG] Initial values - sessionInfo:', sessionInfo, 'workingDir:', workingDir, 'gitBranch:', gitBranch);
@@ -184,7 +199,11 @@ class SessionManager {
184
199
  // For current session, try to extract info from recent events
185
200
  if (this.currentSessionId) {
186
201
  const sessionData = this.extractSessionInfoFromEvents(this.currentSessionId);
187
- workingDir = sessionData.workingDir || window.dashboard?.workingDirectoryManager?.getDefaultWorkingDir() || '/Users/masa/Projects/claude-mpm';
202
+ // Browser-compatible working directory fallback
203
+ workingDir = sessionData.workingDir ||
204
+ window.dashboard?.workingDirectoryManager?.getDefaultWorkingDir() ||
205
+ window.location.pathname ||
206
+ '/Users/masa/Projects/claude-mpm';
188
207
  gitBranch = sessionData.gitBranch || 'Unknown';
189
208
  }
190
209
  } else if (this.selectedSessionId) {
@@ -197,7 +216,8 @@ class SessionManager {
197
216
  // If session doesn't have these values, extract from events
198
217
  if (!workingDir || !gitBranch) {
199
218
  const sessionData = this.extractSessionInfoFromEvents(this.selectedSessionId);
200
- workingDir = workingDir || sessionData.workingDir || '.';
219
+ // Browser-compatible fallback - no process.cwd()
220
+ workingDir = workingDir || sessionData.workingDir || window.location.pathname || '.';
201
221
  gitBranch = gitBranch || sessionData.gitBranch || '';
202
222
  }
203
223
  }
@@ -338,8 +338,10 @@ class SocketManager {
338
338
  }
339
339
 
340
340
  // Auto-connect by default unless explicitly disabled
341
+ // Changed: Always auto-connect by default even without URL params
341
342
  const shouldAutoConnect = params.get('connect') !== 'false';
342
343
  if (shouldAutoConnect && !this.isConnected() && !this.isConnecting()) {
344
+ console.log(`SocketManager: Auto-connecting to port ${connectPort}`);
343
345
  this.connect(connectPort);
344
346
  }
345
347
  }