claude-mpm 4.1.7__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 (109) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +26 -1
  3. claude_mpm/agents/OUTPUT_STYLE.md +73 -0
  4. claude_mpm/agents/agents_metadata.py +57 -0
  5. claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
  6. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
  7. claude_mpm/agents/templates/agent-manager.json +263 -17
  8. claude_mpm/agents/templates/agent-manager.md +248 -10
  9. claude_mpm/agents/templates/agentic_coder_optimizer.json +222 -0
  10. claude_mpm/agents/templates/code_analyzer.json +18 -8
  11. claude_mpm/agents/templates/engineer.json +1 -1
  12. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
  13. claude_mpm/agents/templates/qa.json +1 -1
  14. claude_mpm/agents/templates/research.json +1 -1
  15. claude_mpm/cli/__init__.py +4 -0
  16. claude_mpm/cli/commands/__init__.py +6 -0
  17. claude_mpm/cli/commands/analyze.py +547 -0
  18. claude_mpm/cli/commands/analyze_code.py +524 -0
  19. claude_mpm/cli/commands/configure.py +223 -25
  20. claude_mpm/cli/commands/configure_tui.py +65 -61
  21. claude_mpm/cli/commands/debug.py +1387 -0
  22. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  23. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  24. claude_mpm/cli/parsers/base_parser.py +29 -0
  25. claude_mpm/cli/parsers/configure_parser.py +23 -0
  26. claude_mpm/cli/parsers/debug_parser.py +319 -0
  27. claude_mpm/config/socketio_config.py +21 -21
  28. claude_mpm/constants.py +3 -1
  29. claude_mpm/core/framework_loader.py +148 -6
  30. claude_mpm/core/log_manager.py +16 -13
  31. claude_mpm/core/logger.py +1 -1
  32. claude_mpm/core/unified_agent_registry.py +1 -1
  33. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
  34. claude_mpm/dashboard/analysis_runner.py +428 -0
  35. claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
  36. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  37. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  38. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  39. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  40. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  41. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  42. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  43. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  44. claude_mpm/dashboard/static/css/activity.css +549 -0
  45. claude_mpm/dashboard/static/css/code-tree.css +846 -0
  46. claude_mpm/dashboard/static/css/dashboard.css +245 -0
  47. claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
  48. claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
  49. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  50. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  51. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  52. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  53. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  54. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  55. claude_mpm/dashboard/static/js/components/activity-tree.js +1139 -0
  56. claude_mpm/dashboard/static/js/components/code-tree.js +1357 -0
  57. claude_mpm/dashboard/static/js/components/code-viewer.js +480 -0
  58. claude_mpm/dashboard/static/js/components/event-viewer.js +11 -0
  59. claude_mpm/dashboard/static/js/components/session-manager.js +40 -4
  60. claude_mpm/dashboard/static/js/components/socket-manager.js +12 -0
  61. claude_mpm/dashboard/static/js/components/ui-state-manager.js +4 -0
  62. claude_mpm/dashboard/static/js/components/working-directory.js +17 -1
  63. claude_mpm/dashboard/static/js/dashboard.js +39 -0
  64. claude_mpm/dashboard/static/js/socket-client.js +414 -20
  65. claude_mpm/dashboard/templates/index.html +184 -4
  66. claude_mpm/hooks/claude_hooks/hook_handler.py +182 -5
  67. claude_mpm/hooks/claude_hooks/installer.py +728 -0
  68. claude_mpm/scripts/claude-hook-handler.sh +161 -0
  69. claude_mpm/scripts/socketio_daemon.py +121 -8
  70. claude_mpm/services/agents/deployment/agent_config_provider.py +127 -27
  71. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +2 -2
  72. claude_mpm/services/agents/deployment/agent_record_service.py +1 -2
  73. claude_mpm/services/agents/memory/memory_format_service.py +1 -5
  74. claude_mpm/services/cli/agent_cleanup_service.py +1 -2
  75. claude_mpm/services/cli/agent_dependency_service.py +1 -1
  76. claude_mpm/services/cli/agent_validation_service.py +3 -4
  77. claude_mpm/services/cli/dashboard_launcher.py +2 -3
  78. claude_mpm/services/cli/startup_checker.py +0 -10
  79. claude_mpm/services/core/cache_manager.py +1 -2
  80. claude_mpm/services/core/path_resolver.py +1 -4
  81. claude_mpm/services/core/service_container.py +2 -2
  82. claude_mpm/services/diagnostics/checks/instructions_check.py +2 -5
  83. claude_mpm/services/event_bus/direct_relay.py +98 -20
  84. claude_mpm/services/infrastructure/monitoring/__init__.py +11 -11
  85. claude_mpm/services/infrastructure/monitoring.py +11 -11
  86. claude_mpm/services/project/architecture_analyzer.py +1 -1
  87. claude_mpm/services/project/dependency_analyzer.py +4 -4
  88. claude_mpm/services/project/language_analyzer.py +3 -3
  89. claude_mpm/services/project/metrics_collector.py +3 -6
  90. claude_mpm/services/socketio/handlers/__init__.py +2 -0
  91. claude_mpm/services/socketio/handlers/code_analysis.py +170 -0
  92. claude_mpm/services/socketio/handlers/registry.py +2 -0
  93. claude_mpm/services/socketio/server/connection_manager.py +95 -65
  94. claude_mpm/services/socketio/server/core.py +125 -17
  95. claude_mpm/services/socketio/server/main.py +44 -5
  96. claude_mpm/services/visualization/__init__.py +19 -0
  97. claude_mpm/services/visualization/mermaid_generator.py +938 -0
  98. claude_mpm/tools/__main__.py +208 -0
  99. claude_mpm/tools/code_tree_analyzer.py +778 -0
  100. claude_mpm/tools/code_tree_builder.py +632 -0
  101. claude_mpm/tools/code_tree_events.py +318 -0
  102. claude_mpm/tools/socketio_debug.py +671 -0
  103. {claude_mpm-4.1.7.dist-info → claude_mpm-4.1.10.dist-info}/METADATA +1 -1
  104. {claude_mpm-4.1.7.dist-info → claude_mpm-4.1.10.dist-info}/RECORD +108 -77
  105. claude_mpm/agents/schema/agent_schema.json +0 -314
  106. {claude_mpm-4.1.7.dist-info → claude_mpm-4.1.10.dist-info}/WHEEL +0 -0
  107. {claude_mpm-4.1.7.dist-info → claude_mpm-4.1.10.dist-info}/entry_points.txt +0 -0
  108. {claude_mpm-4.1.7.dist-info → claude_mpm-4.1.10.dist-info}/licenses/LICENSE +0 -0
  109. {claude_mpm-4.1.7.dist-info → claude_mpm-4.1.10.dist-info}/top_level.txt +0 -0
@@ -1,74 +1,303 @@
1
1
  /**
2
2
  * Socket.IO Client for Claude MPM Dashboard
3
- * Handles WebSocket connections and event processing
3
+ *
4
+ * This module provides real-time WebSocket communication between the Claude MPM dashboard
5
+ * and the backend Socket.IO server. It handles connection management, event processing,
6
+ * retry logic, and health monitoring.
7
+ *
8
+ * Architecture:
9
+ * - Maintains persistent WebSocket connection to Claude MPM backend
10
+ * - Implements robust retry logic with exponential backoff
11
+ * - Provides event queuing during disconnections
12
+ * - Validates event schemas for data integrity
13
+ * - Monitors connection health with ping/pong mechanisms
14
+ *
15
+ * Event Flow:
16
+ * 1. Events from Claude Code hooks → Socket.IO server → Dashboard client
17
+ * 2. Dashboard requests → Socket.IO server → Backend services
18
+ * 3. Status updates → Socket.IO server → All connected clients
19
+ *
20
+ * Thread Safety:
21
+ * - Single-threaded JavaScript execution model ensures safety
22
+ * - Event callbacks are queued and executed sequentially
23
+ * - Connection state changes are atomic
24
+ *
25
+ * Performance Considerations:
26
+ * - Event queue limited to 100 items to prevent memory leaks
27
+ * - Health checks run every 45s to match server ping interval
28
+ * - Exponential backoff prevents connection spam
29
+ * - Lazy event validation reduces overhead
30
+ *
31
+ * Security:
32
+ * - Connects only to localhost to prevent external access
33
+ * - Event schema validation prevents malformed data processing
34
+ * - Connection timeout prevents hanging connections
35
+ *
36
+ * @author Claude MPM Team
37
+ * @version 1.0
38
+ * @since v4.0.25
4
39
  */
5
40
 
6
41
  // Access the global io from window object in ES6 module context
42
+ // WHY: Socket.IO is loaded via CDN in HTML, available as window.io
7
43
  const io = window.io;
8
44
 
45
+ /**
46
+ * Primary Socket.IO client for dashboard communication.
47
+ *
48
+ * Manages WebSocket connection lifecycle, event processing, and error handling.
49
+ * Implements connection resilience with automatic retry and health monitoring.
50
+ *
51
+ * Key Features:
52
+ * - Automatic connection retry with exponential backoff
53
+ * - Event queue management during disconnections
54
+ * - Schema validation for incoming events
55
+ * - Health monitoring with ping/pong
56
+ * - Session management and event history
57
+ *
58
+ * Connection States:
59
+ * - isConnected: Currently connected to server
60
+ * - isConnecting: Connection attempt in progress
61
+ * - disconnectTime: Timestamp of last disconnection
62
+ *
63
+ * Event Processing:
64
+ * - Validates against schema before processing
65
+ * - Queues events during disconnection (max 100)
66
+ * - Maintains event history and session tracking
67
+ *
68
+ * @class SocketClient
69
+ */
9
70
  class SocketClient {
71
+ /**
72
+ * Initialize Socket.IO client with default configuration.
73
+ *
74
+ * Sets up connection management, event processing, and health monitoring.
75
+ * Configures retry logic and event queue management.
76
+ *
77
+ * WHY this initialization approach:
78
+ * - Lazy socket creation allows for port specification
79
+ * - Event queue prevents data loss during reconnections
80
+ * - Health monitoring detects server issues early
81
+ * - Schema validation ensures data integrity
82
+ *
83
+ * @constructor
84
+ */
10
85
  constructor() {
86
+ /**
87
+ * Socket.IO connection instance.
88
+ * @type {Socket|null}
89
+ * @private
90
+ */
11
91
  this.socket = null;
92
+
93
+ /**
94
+ * Current connection port.
95
+ * @type {string|null}
96
+ * @private
97
+ */
12
98
  this.port = null; // Store the current port
99
+
100
+ /**
101
+ * Event callback registry for connection lifecycle events.
102
+ * WHY: Allows multiple components to register for connection events.
103
+ * @type {Object.<string, Function[]>}
104
+ * @private
105
+ */
13
106
  this.connectionCallbacks = {
14
- connect: [],
15
- disconnect: [],
16
- error: [],
17
- event: []
107
+ connect: [], // Called on successful connection
108
+ disconnect: [], // Called on disconnection
109
+ error: [], // Called on connection errors
110
+ event: [] // Called on incoming events
18
111
  };
19
112
 
20
- // Event schema validation
113
+ /**
114
+ * Event schema definition for validation.
115
+ * WHY: Ensures data integrity and prevents processing malformed events.
116
+ * @type {Object}
117
+ * @private
118
+ */
21
119
  this.eventSchema = {
22
120
  required: ['source', 'type', 'subtype', 'timestamp', 'data'],
23
121
  optional: ['event', 'session_id']
24
122
  };
25
123
 
26
- // Connection state
124
+ /**
125
+ * Current connection state.
126
+ * @type {boolean}
127
+ * @private
128
+ */
27
129
  this.isConnected = false;
130
+
131
+ /**
132
+ * Connection attempt in progress flag.
133
+ * WHY: Prevents multiple simultaneous connection attempts.
134
+ * @type {boolean}
135
+ * @private
136
+ */
28
137
  this.isConnecting = false;
138
+
139
+ /**
140
+ * Timestamp of last successful connection.
141
+ * @type {number|null}
142
+ * @private
143
+ */
29
144
  this.lastConnectTime = null;
145
+
146
+ /**
147
+ * Timestamp of last disconnection.
148
+ * WHY: Used to calculate downtime and trigger reconnection logic.
149
+ * @type {number|null}
150
+ * @private
151
+ */
30
152
  this.disconnectTime = null;
31
153
 
32
- // Event processing
154
+ /**
155
+ * Event history storage.
156
+ * WHY: Maintains event history for dashboard display and analysis.
157
+ * @type {Array.<Object>}
158
+ * @private
159
+ */
33
160
  this.events = [];
161
+
162
+ /**
163
+ * Session tracking map.
164
+ * WHY: Groups events by session for better organization.
165
+ * @type {Map<string, Object>}
166
+ * @private
167
+ */
34
168
  this.sessions = new Map();
169
+
170
+ /**
171
+ * Current active session identifier.
172
+ * @type {string|null}
173
+ * @private
174
+ */
35
175
  this.currentSessionId = null;
36
176
 
37
- // Event queue for disconnection periods
177
+ /**
178
+ * Event queue for disconnection periods.
179
+ * WHY: Prevents event loss during temporary disconnections.
180
+ * @type {Array.<Object>}
181
+ * @private
182
+ */
38
183
  this.eventQueue = [];
184
+
185
+ /**
186
+ * Maximum queue size to prevent memory leaks.
187
+ * WHY: Limits memory usage during extended disconnections.
188
+ * @type {number}
189
+ * @private
190
+ * @const
191
+ */
39
192
  this.maxQueueSize = 100;
40
193
 
41
- // Retry configuration - Match server settings
194
+ /**
195
+ * Current retry attempt counter.
196
+ * WHY: Tracks retry attempts for exponential backoff logic.
197
+ * @type {number}
198
+ * @private
199
+ */
42
200
  this.retryAttempts = 0;
201
+
202
+ /**
203
+ * Maximum retry attempts before giving up.
204
+ * WHY: Prevents infinite retry loops that could impact performance.
205
+ * @type {number}
206
+ * @private
207
+ * @const
208
+ */
43
209
  this.maxRetryAttempts = 5; // Increased from 3 to 5 for better stability
210
+
211
+ /**
212
+ * Retry delay intervals in milliseconds (exponential backoff).
213
+ * WHY: Prevents server overload during connection issues.
214
+ * @type {number[]}
215
+ * @private
216
+ * @const
217
+ */
44
218
  this.retryDelays = [1000, 2000, 3000, 4000, 5000]; // Exponential backoff with 5 attempts
219
+
220
+ /**
221
+ * Map of pending emissions for retry logic.
222
+ * WHY: Tracks failed emissions that need to be retried.
223
+ * @type {Map<string, Object>}
224
+ * @private
225
+ */
45
226
  this.pendingEmissions = new Map(); // Track pending emissions for retry
46
227
 
47
- // Health monitoring
228
+ /**
229
+ * Timestamp of last ping sent to server.
230
+ * WHY: Used for health monitoring and connection validation.
231
+ * @type {number|null}
232
+ * @private
233
+ */
48
234
  this.lastPingTime = null;
235
+
236
+ /**
237
+ * Timestamp of last pong received from server.
238
+ * WHY: Confirms server is responsive and connection is healthy.
239
+ * @type {number|null}
240
+ * @private
241
+ */
49
242
  this.lastPongTime = null;
243
+
244
+ /**
245
+ * Health check timeout in milliseconds.
246
+ * WHY: More lenient than Socket.IO timeout to prevent false positives.
247
+ * @type {number}
248
+ * @private
249
+ * @const
250
+ */
50
251
  this.pingTimeout = 90000; // 90 seconds for health check (more lenient than Socket.IO timeout)
252
+
253
+ /**
254
+ * Health check interval timer.
255
+ * @type {number|null}
256
+ * @private
257
+ */
51
258
  this.healthCheckInterval = null;
52
259
 
53
- // Start periodic status check as fallback mechanism
260
+ // Initialize background monitoring
54
261
  this.startStatusCheckFallback();
55
262
  this.startHealthMonitoring();
56
263
  }
57
264
 
58
265
  /**
59
- * Connect to Socket.IO server
60
- * @param {string} port - Port number to connect to
266
+ * Connect to Socket.IO server on specified port.
267
+ *
268
+ * Initiates WebSocket connection to the Claude MPM Socket.IO server.
269
+ * Handles connection conflicts and ensures clean state transitions.
270
+ *
271
+ * Connection Process:
272
+ * 1. Validates port and constructs localhost URL
273
+ * 2. Checks for existing connections and cleans up if needed
274
+ * 3. Delegates to doConnect() for actual connection logic
275
+ *
276
+ * Thread Safety:
277
+ * - Uses setTimeout for async cleanup to prevent race conditions
278
+ * - Connection state flags prevent multiple simultaneous attempts
279
+ *
280
+ * @param {string} [port='8765'] - Port number to connect to (defaults to 8765)
281
+ *
282
+ * @throws {Error} If Socket.IO library is not loaded
283
+ *
284
+ * @example
285
+ * // Connect to default port
286
+ * socketClient.connect();
287
+ *
288
+ * // Connect to specific port
289
+ * socketClient.connect('8766');
61
290
  */
62
291
  connect(port = '8765') {
63
- // Store the port for later use
292
+ // Store the port for later use in reconnections
64
293
  this.port = port;
65
294
  const url = `http://localhost:${port}`;
66
295
 
67
- // Prevent multiple simultaneous connections
296
+ // WHY this check: Prevents connection conflicts that can cause memory leaks
68
297
  if (this.socket && (this.socket.connected || this.socket.connecting)) {
69
298
  console.log('Already connected or connecting, disconnecting first...');
70
299
  this.socket.disconnect();
71
- // Wait a moment for cleanup
300
+ // WHY 100ms delay: Allows cleanup to complete before new connection
72
301
  setTimeout(() => this.doConnect(url), 100);
73
302
  return;
74
303
  }
@@ -77,8 +306,28 @@ class SocketClient {
77
306
  }
78
307
 
79
308
  /**
80
- * Perform the actual connection
81
- * @param {string} url - Socket.IO server URL
309
+ * Execute the actual Socket.IO connection with full configuration.
310
+ *
311
+ * Creates and configures Socket.IO client with appropriate timeouts,
312
+ * retry logic, and transport settings. Sets up event handlers for
313
+ * connection lifecycle management.
314
+ *
315
+ * Configuration Details:
316
+ * - autoConnect: true - Immediate connection attempt
317
+ * - reconnection: true - Built-in reconnection enabled
318
+ * - pingInterval: 45000ms - Matches server configuration
319
+ * - pingTimeout: 20000ms - Health check timeout
320
+ * - transports: ['websocket', 'polling'] - Fallback options
321
+ *
322
+ * WHY these settings:
323
+ * - Ping intervals must match server to prevent timeouts
324
+ * - Limited reconnection attempts prevent infinite loops
325
+ * - forceNew prevents socket reuse issues
326
+ *
327
+ * @param {string} url - Complete Socket.IO server URL (http://localhost:port)
328
+ * @private
329
+ *
330
+ * @throws {Error} If Socket.IO library is not available
82
331
  */
83
332
  doConnect(url) {
84
333
  console.log(`Connecting to Socket.IO server at ${url}`);
@@ -132,6 +381,10 @@ class SocketClient {
132
381
 
133
382
  this.notifyConnectionStatus('Connected', 'connected');
134
383
 
384
+ // Expose socket globally for components that need direct access
385
+ window.socket = this.socket;
386
+ console.log('SocketClient: Exposed socket globally as window.socket');
387
+
135
388
  // Emit connect callback
136
389
  this.connectionCallbacks.connect.forEach(callback =>
137
390
  callback(this.socket.id)
@@ -222,6 +475,13 @@ class SocketClient {
222
475
  return;
223
476
  }
224
477
 
478
+ // Check if this is a code analysis event - if so, don't add to events list
479
+ // Code analysis events are handled by the code-tree component and shown in the footer
480
+ if (validatedEvent.type && validatedEvent.type.startsWith('code:')) {
481
+ console.log('Code analysis event received via claude_event, not adding to events list:', validatedEvent.type);
482
+ return;
483
+ }
484
+
225
485
  // Transform event to match expected format (for backward compatibility)
226
486
  const transformedEvent = this.transformEvent(validatedEvent);
227
487
  console.log('Transformed event:', transformedEvent);
@@ -291,6 +551,48 @@ class SocketClient {
291
551
  this.addEvent({ type: 'log', subtype: 'entry', timestamp: new Date().toISOString(), data });
292
552
  });
293
553
 
554
+ // Code analysis events - don't add to event list, just pass through
555
+ // These are handled by the code-tree component and shown in the footer
556
+ this.socket.on('code:analysis:queued', (data) => {
557
+ // Don't add to events list - handled by code-tree component
558
+ console.log('Code analysis queued event received, not adding to events list');
559
+ });
560
+
561
+ this.socket.on('code:analysis:accepted', (data) => {
562
+ // Don't add to events list
563
+ console.log('Code analysis accepted event received, not adding to events list');
564
+ });
565
+
566
+ this.socket.on('code:analysis:start', (data) => {
567
+ // Don't add to events list
568
+ console.log('Code analysis start event received, not adding to events list');
569
+ });
570
+
571
+ this.socket.on('code:analysis:complete', (data) => {
572
+ // Don't add to events list
573
+ console.log('Code analysis complete event received, not adding to events list');
574
+ });
575
+
576
+ this.socket.on('code:analysis:error', (data) => {
577
+ // Don't add to events list
578
+ console.log('Code analysis error event received, not adding to events list');
579
+ });
580
+
581
+ this.socket.on('code:file:start', (data) => {
582
+ // Don't add to events list
583
+ console.log('Code file start event received, not adding to events list');
584
+ });
585
+
586
+ this.socket.on('code:node:found', (data) => {
587
+ // Don't add to events list
588
+ console.log('Code node found event received, not adding to events list');
589
+ });
590
+
591
+ this.socket.on('code:analysis:progress', (data) => {
592
+ // Don't add to events list
593
+ console.log('Code analysis progress event received, not adding to events list');
594
+ });
595
+
294
596
  this.socket.on('history', (data) => {
295
597
  console.log('Received event history:', data);
296
598
  if (data && Array.isArray(data.events)) {
@@ -528,12 +830,44 @@ class SocketClient {
528
830
  id: sessionId,
529
831
  startTime: eventData.timestamp,
530
832
  lastActivity: eventData.timestamp,
531
- eventCount: 0
833
+ eventCount: 0,
834
+ working_directory: null,
835
+ git_branch: null
532
836
  });
533
837
  }
534
838
  const session = this.sessions.get(sessionId);
535
839
  session.lastActivity = eventData.timestamp;
536
840
  session.eventCount++;
841
+
842
+ // Extract working directory from event data if available (prioritize newer data)
843
+ // Check multiple possible locations for working directory
844
+ const possiblePaths = [
845
+ eventData.data.cwd,
846
+ eventData.data.working_directory,
847
+ eventData.data.working_dir,
848
+ eventData.data.workingDirectory,
849
+ eventData.data.instance_info?.working_dir,
850
+ eventData.data.instance_info?.working_directory,
851
+ eventData.data.instance_info?.cwd,
852
+ eventData.cwd,
853
+ eventData.working_directory,
854
+ eventData.working_dir
855
+ ];
856
+
857
+ for (const path of possiblePaths) {
858
+ if (path && typeof path === 'string' && path.trim()) {
859
+ session.working_directory = path;
860
+ console.log(`[SOCKET-CLIENT] Found working directory for session ${sessionId}:`, path);
861
+ break;
862
+ }
863
+ }
864
+
865
+ // Extract git branch if available
866
+ if (eventData.data.git_branch) {
867
+ session.git_branch = eventData.data.git_branch;
868
+ } else if (eventData.data.instance_info && eventData.data.instance_info.git_branch) {
869
+ session.git_branch = eventData.data.instance_info.git_branch;
870
+ }
537
871
  }
538
872
 
539
873
  if (notify) {
@@ -604,6 +938,32 @@ class SocketClient {
604
938
  this.connectionCallbacks.event.push(callback);
605
939
  }
606
940
 
941
+ /**
942
+ * Subscribe to socket events (proxy to underlying socket)
943
+ * @param {string} event - Event name
944
+ * @param {Function} callback - Callback function
945
+ */
946
+ on(event, callback) {
947
+ if (this.socket) {
948
+ return this.socket.on(event, callback);
949
+ } else {
950
+ console.warn(`Cannot subscribe to '${event}': socket not initialized`);
951
+ }
952
+ }
953
+
954
+ /**
955
+ * Unsubscribe from socket events (proxy to underlying socket)
956
+ * @param {string} event - Event name
957
+ * @param {Function} callback - Callback function (optional)
958
+ */
959
+ off(event, callback) {
960
+ if (this.socket) {
961
+ return this.socket.off(event, callback);
962
+ } else {
963
+ console.warn(`Cannot unsubscribe from '${event}': socket not initialized`);
964
+ }
965
+ }
966
+
607
967
  /**
608
968
  * Notify connection status change
609
969
  * @param {string} status - Status message
@@ -772,6 +1132,11 @@ class SocketClient {
772
1132
  transformedEvent.type = 'hook';
773
1133
  transformedEvent.subtype = subtype;
774
1134
  }
1135
+ // Transform 'code:*' events to proper code type
1136
+ else if (type.startsWith('code:')) {
1137
+ transformedEvent.type = 'code';
1138
+ transformedEvent.subtype = type.substring(5); // Remove 'code:' prefix
1139
+ }
775
1140
  // Transform other dotted types like 'session.started' -> type: 'session', subtype: 'started'
776
1141
  else if (type.includes('.')) {
777
1142
  const [mainType, ...subtypeParts] = type.split('.');
@@ -820,11 +1185,40 @@ class SocketClient {
820
1185
  transformedEvent.data = eventData.data;
821
1186
  }
822
1187
 
1188
+ // Add hook_event_name for ActivityTree compatibility
1189
+ // Map the type/subtype structure to the expected hook_event_name format
1190
+ if (transformedEvent.type === 'hook') {
1191
+ if (transformedEvent.subtype === 'pre_tool') {
1192
+ transformedEvent.hook_event_name = 'PreToolUse';
1193
+ } else if (transformedEvent.subtype === 'post_tool') {
1194
+ transformedEvent.hook_event_name = 'PostToolUse';
1195
+ } else if (transformedEvent.subtype === 'subagent_start') {
1196
+ transformedEvent.hook_event_name = 'SubagentStart';
1197
+ } else if (transformedEvent.subtype === 'subagent_stop') {
1198
+ transformedEvent.hook_event_name = 'SubagentStop';
1199
+ } else if (transformedEvent.subtype === 'todo_write') {
1200
+ transformedEvent.hook_event_name = 'TodoWrite';
1201
+ } else if (transformedEvent.subtype === 'start') {
1202
+ transformedEvent.hook_event_name = 'Start';
1203
+ } else if (transformedEvent.subtype === 'stop') {
1204
+ transformedEvent.hook_event_name = 'Stop';
1205
+ }
1206
+ } else if (transformedEvent.type === 'subagent') {
1207
+ if (transformedEvent.subtype === 'start') {
1208
+ transformedEvent.hook_event_name = 'SubagentStart';
1209
+ } else if (transformedEvent.subtype === 'stop') {
1210
+ transformedEvent.hook_event_name = 'SubagentStop';
1211
+ }
1212
+ } else if (transformedEvent.type === 'todo' && transformedEvent.subtype === 'updated') {
1213
+ transformedEvent.hook_event_name = 'TodoWrite';
1214
+ }
1215
+
823
1216
  // Debug logging for tool events
824
1217
  if (transformedEvent.type === 'hook' && (transformedEvent.subtype === 'pre_tool' || transformedEvent.subtype === 'post_tool')) {
825
1218
  console.log('Transformed tool event:', {
826
1219
  type: transformedEvent.type,
827
1220
  subtype: transformedEvent.subtype,
1221
+ hook_event_name: transformedEvent.hook_event_name,
828
1222
  tool_name: transformedEvent.tool_name,
829
1223
  has_tool_parameters: !!transformedEvent.tool_parameters,
830
1224
  tool_parameters: transformedEvent.tool_parameters,