claude-mpm 4.2.43__py3-none-any.whl → 4.2.51__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 (155) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +117 -12
  3. claude_mpm/agents/INSTRUCTIONS.md +154 -10
  4. claude_mpm/agents/WORKFLOW.md +46 -1
  5. claude_mpm/agents/frontmatter_validator.py +20 -12
  6. claude_mpm/agents/templates/nextjs_engineer.json +277 -0
  7. claude_mpm/agents/templates/python_engineer.json +289 -0
  8. claude_mpm/agents/templates/react_engineer.json +11 -3
  9. claude_mpm/agents/templates/security.json +50 -9
  10. claude_mpm/cli/commands/agents.py +2 -2
  11. claude_mpm/cli/commands/uninstall.py +1 -2
  12. claude_mpm/cli/interactive/agent_wizard.py +3 -3
  13. claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
  14. claude_mpm/cli/parsers/agents_parser.py +1 -1
  15. claude_mpm/constants.py +1 -1
  16. claude_mpm/core/api_validator.py +330 -0
  17. claude_mpm/core/error_handler.py +2 -4
  18. claude_mpm/core/file_utils.py +4 -12
  19. claude_mpm/core/framework_loader.py +22 -0
  20. claude_mpm/core/log_manager.py +8 -5
  21. claude_mpm/core/logger.py +1 -1
  22. claude_mpm/core/logging_utils.py +6 -6
  23. claude_mpm/core/unified_agent_registry.py +18 -4
  24. claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
  25. claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
  26. claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
  27. claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
  28. claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
  29. claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
  30. claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
  31. claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
  32. claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
  33. claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
  34. claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
  35. claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
  36. claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
  37. claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
  38. claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
  39. claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
  40. claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
  41. claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
  42. claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
  43. claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
  44. claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
  45. claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
  46. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  47. claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
  48. claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
  49. claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
  50. claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
  51. claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
  52. claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
  53. claude_mpm/dashboard/static/built/components/code-viewer.js +2 -1076
  54. claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
  55. claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
  56. claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
  57. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  58. claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
  59. claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
  60. claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
  61. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  62. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  63. claude_mpm/dashboard/static/built/components/nav-bar.js +145 -0
  64. claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
  65. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  66. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -465
  67. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  68. claude_mpm/dashboard/static/built/connection-manager.js +536 -0
  69. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  70. claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
  71. claude_mpm/dashboard/static/built/react/events.js +30 -0
  72. claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
  73. claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
  74. claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
  75. claude_mpm/dashboard/static/built/shared/logger.js +385 -0
  76. claude_mpm/dashboard/static/built/shared/page-structure.js +251 -0
  77. claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
  78. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  79. claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
  80. claude_mpm/dashboard/static/css/dashboard.css +28 -5
  81. claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
  82. claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
  83. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  84. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  85. claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
  86. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  87. claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
  88. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  89. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  90. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  91. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  92. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  93. claude_mpm/dashboard/static/dist/react/events.js +30 -0
  94. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  95. claude_mpm/dashboard/static/events.html +607 -0
  96. claude_mpm/dashboard/static/index.html +713 -0
  97. claude_mpm/dashboard/static/js/components/activity-tree.js +3 -17
  98. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
  99. claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
  100. claude_mpm/dashboard/static/js/components/build-tracker.js +8 -0
  101. claude_mpm/dashboard/static/js/components/code-viewer.js +387 -72
  102. claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
  103. claude_mpm/dashboard/static/js/components/event-viewer.js +39 -2
  104. claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
  105. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
  106. claude_mpm/dashboard/static/js/components/socket-manager.js +4 -0
  107. claude_mpm/dashboard/static/js/components/ui-state-manager.js +286 -108
  108. claude_mpm/dashboard/static/js/components/working-directory.js +3 -0
  109. claude_mpm/dashboard/static/js/dashboard.js +61 -49
  110. claude_mpm/dashboard/static/js/socket-client.js +12 -8
  111. claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
  112. claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
  113. claude_mpm/dashboard/static/legacy/activity.html +736 -0
  114. claude_mpm/dashboard/static/legacy/agents.html +786 -0
  115. claude_mpm/dashboard/static/legacy/files.html +747 -0
  116. claude_mpm/dashboard/static/legacy/tools.html +831 -0
  117. claude_mpm/dashboard/static/monitors-index.html +218 -0
  118. claude_mpm/dashboard/static/monitors.html +431 -0
  119. claude_mpm/dashboard/static/production/events.html +659 -0
  120. claude_mpm/dashboard/static/production/main.html +715 -0
  121. claude_mpm/dashboard/static/production/monitors.html +483 -0
  122. claude_mpm/dashboard/static/socket.io.min.js +7 -0
  123. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
  124. claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
  125. claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
  126. claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
  127. claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
  128. claude_mpm/dashboard/templates/index.html +82 -38
  129. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +1 -1
  130. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -0
  131. claude_mpm/services/agents/deployment/agent_template_builder.py +25 -8
  132. claude_mpm/services/agents/deployment/agent_validator.py +3 -0
  133. claude_mpm/services/agents/deployment/validation/template_validator.py +13 -4
  134. claude_mpm/services/agents/local_template_manager.py +2 -6
  135. claude_mpm/services/monitor/daemon.py +1 -2
  136. claude_mpm/services/monitor/daemon_manager.py +2 -5
  137. claude_mpm/services/monitor/event_emitter.py +2 -2
  138. claude_mpm/services/monitor/handlers/code_analysis.py +4 -6
  139. claude_mpm/services/monitor/handlers/hooks.py +2 -4
  140. claude_mpm/services/monitor/server.py +23 -226
  141. claude_mpm/tools/code_tree_analyzer.py +2 -2
  142. {claude_mpm-4.2.43.dist-info → claude_mpm-4.2.51.dist-info}/METADATA +1 -1
  143. {claude_mpm-4.2.43.dist-info → claude_mpm-4.2.51.dist-info}/RECORD +148 -87
  144. claude_mpm/commands/mpm-browser-monitor.md +0 -370
  145. claude_mpm/commands/mpm-monitor.md +0 -177
  146. claude_mpm/dashboard/static/js/browser-console-monitor.js +0 -495
  147. claude_mpm/dashboard/static/js/components/browser-log-viewer.js +0 -763
  148. claude_mpm/dashboard/static/test-browser-monitor.html +0 -470
  149. claude_mpm/dashboard/static/test-simple.html +0 -97
  150. claude_mpm/services/monitor/handlers/browser.py +0 -451
  151. /claude_mpm/dashboard/static/{test_debug.html → test-archive/test_debug.html} +0 -0
  152. {claude_mpm-4.2.43.dist-info → claude_mpm-4.2.51.dist-info}/WHEEL +0 -0
  153. {claude_mpm-4.2.43.dist-info → claude_mpm-4.2.51.dist-info}/entry_points.txt +0 -0
  154. {claude_mpm-4.2.43.dist-info → claude_mpm-4.2.51.dist-info}/licenses/LICENSE +0 -0
  155. {claude_mpm-4.2.43.dist-info → claude_mpm-4.2.51.dist-info}/top_level.txt +0 -0
@@ -537,3 +537,6 @@ class EventProcessor {
537
537
  // ES6 Module export
538
538
  export { EventProcessor };
539
539
  export default EventProcessor;
540
+
541
+ // Make EventProcessor globally available for dist/dashboard.js
542
+ window.EventProcessor = EventProcessor;
@@ -41,6 +41,14 @@ class EventViewer {
41
41
  this.socketClient.onEventUpdate((events, sessions) => {
42
42
  // Ensure we always have a valid events array
43
43
  this.events = Array.isArray(events) ? events : [];
44
+ console.log('[EventViewer] Events updated - received:', this.events.length);
45
+
46
+ // Update debug metrics
47
+ const debugReceivedEl = document.getElementById('debug-events-received');
48
+ if (debugReceivedEl) {
49
+ debugReceivedEl.textContent = this.events.length;
50
+ }
51
+
44
52
  this.updateDisplay();
45
53
  });
46
54
  }
@@ -221,8 +229,29 @@ class EventViewer {
221
229
  * Render events in the UI
222
230
  */
223
231
  renderEvents() {
224
- const eventsList = document.getElementById('events-list');
225
- if (!eventsList) return;
232
+ // CRITICAL FIX: Use the container passed to constructor, not hardcoded events-list
233
+ // This prevents events from being rendered in the wrong tab
234
+ const eventsList = this.container;
235
+ if (!eventsList) {
236
+ console.warn('[EventViewer] Container not found, skipping render');
237
+ return;
238
+ }
239
+
240
+ // SAFETY: Basic check to ensure we're rendering to the correct container
241
+ if (eventsList.id !== 'events-list') {
242
+ console.error('[EventViewer] CRITICAL: Attempting to render to wrong container:', eventsList.id);
243
+ return;
244
+ }
245
+
246
+ // Check if events tab exists and render regardless of active state
247
+ // This allows events to be pre-rendered when tab becomes active
248
+ const eventsTab = document.getElementById('events-tab');
249
+ if (!eventsTab) {
250
+ console.warn('[EventViewer] Events tab not found in DOM');
251
+ return;
252
+ }
253
+
254
+ console.log('[EventViewer] Rendering events - count:', this.filteredEvents.length);
226
255
 
227
256
  // Check if user is at bottom BEFORE rendering (for autoscroll decision)
228
257
  const wasAtBottom = (eventsList.scrollTop + eventsList.clientHeight >= eventsList.scrollHeight - 10);
@@ -268,6 +297,14 @@ class EventViewer {
268
297
  // Update filtered elements reference
269
298
  this.filteredEventElements = Array.from(eventsList.querySelectorAll('.event-item'));
270
299
 
300
+ console.log('[EventViewer] Events rendered - filtered:', this.filteredEvents.length, 'elements:', this.filteredEventElements.length);
301
+
302
+ // Update debug metrics
303
+ const debugRenderedEl = document.getElementById('debug-events-rendered');
304
+ if (debugRenderedEl) {
305
+ debugRenderedEl.textContent = this.filteredEvents.length;
306
+ }
307
+
271
308
  // Update Dashboard navigation items if we're in the events tab
272
309
  if (window.dashboard && window.dashboard.currentTab === 'events' &&
273
310
  window.dashboard.tabNavigation && window.dashboard.tabNavigation.events) {
@@ -363,3 +363,6 @@ class ExportManager {
363
363
  // ES6 Module export
364
364
  export { ExportManager };
365
365
  export default ExportManager;
366
+
367
+ // Make ExportManager globally available for dist/dashboard.js
368
+ window.ExportManager = ExportManager;
@@ -316,14 +316,24 @@ class FileToolTracker {
316
316
  * @returns {boolean} - True if tool operation
317
317
  */
318
318
  isToolOperation(event) {
319
- // Tool operations have tool_name and are hook events with pre_tool or post_tool subtype
319
+ // Tool operations have tool_name - be more inclusive about event types
320
320
  // Check both top-level and data.tool_name for compatibility
321
321
  const hasToolName = event.tool_name || (event.data && event.data.tool_name);
322
- const isHookEvent = event.type === 'hook';
323
- const isToolSubtype = event.subtype === 'pre_tool' || event.subtype === 'post_tool' ||
324
- (event.subtype && typeof event.subtype === 'string' && event.subtype.includes('tool'));
325
-
326
- return hasToolName && isHookEvent && isToolSubtype;
322
+
323
+ // Accept multiple event types that might contain tool operations
324
+ const validEventTypes = ['hook', 'tool_use', 'tool', 'agent', 'response'];
325
+ const isValidEventType = validEventTypes.includes(event.type) ||
326
+ (event.type && event.type.includes('tool'));
327
+
328
+ // Check for tool-related subtypes or any indication this is a tool operation
329
+ const isToolSubtype = event.subtype === 'pre_tool' ||
330
+ event.subtype === 'post_tool' ||
331
+ (event.subtype && typeof event.subtype === 'string' && event.subtype.includes('tool')) ||
332
+ event.type === 'tool_use' ||
333
+ event.type === 'tool';
334
+
335
+ // If it has a tool_name and either a valid event type or tool subtype, it's a tool operation
336
+ return hasToolName && (isValidEventType || isToolSubtype);
327
337
  }
328
338
 
329
339
  /**
@@ -332,14 +342,20 @@ class FileToolTracker {
332
342
  * @returns {boolean} - True if file operation
333
343
  */
334
344
  isFileOperation(event) {
335
- // File operations are tool events with tools that operate on files
345
+ // File operations are events with file-related tools - be more inclusive
336
346
  // Check both top-level and data for tool_name
337
347
  let toolName = event.tool_name || (event.data && event.data.tool_name) || '';
348
+
349
+ // If no tool name, not a file operation
350
+ if (!toolName) {
351
+ return false;
352
+ }
353
+
338
354
  toolName = toolName.toLowerCase();
339
-
355
+
340
356
  // Check case-insensitively since tool names can come in different cases
341
357
  const fileTools = ['read', 'write', 'edit', 'grep', 'multiedit', 'glob', 'ls', 'bash', 'notebookedit'];
342
-
358
+
343
359
  // Get tool parameters from either location
344
360
  const toolParams = event.tool_parameters || (event.data && event.data.tool_parameters);
345
361
 
@@ -352,7 +368,8 @@ class FileToolTracker {
352
368
  }
353
369
  }
354
370
 
355
- return toolName && fileTools.includes(toolName);
371
+ // If it's a file tool, it's a file operation regardless of event type
372
+ return fileTools.includes(toolName);
356
373
  }
357
374
 
358
375
  /**
@@ -702,3 +719,6 @@ class FileToolTracker {
702
719
  // ES6 Module export
703
720
  export { FileToolTracker };
704
721
  export default FileToolTracker;
722
+
723
+ // Make FileToolTracker globally available for dist/dashboard.js
724
+ window.FileToolTracker = FileToolTracker;
@@ -362,3 +362,7 @@ class SocketManager {
362
362
  // ES6 Module export
363
363
  export { SocketManager };
364
364
  export default SocketManager;
365
+
366
+ // Make SocketManager globally available for the dist/dashboard.js
367
+ // This ensures compatibility with the minified version
368
+ window.SocketManager = SocketManager;
@@ -14,6 +14,9 @@
14
14
  */
15
15
  class UIStateManager {
16
16
  constructor() {
17
+ // Switching lock to prevent race conditions
18
+ this._switching = false;
19
+
17
20
  // Hash to tab mapping
18
21
  this.hashToTab = {
19
22
  '#events': 'events',
@@ -22,7 +25,6 @@ class UIStateManager {
22
25
  '#files': 'files',
23
26
  '#activity': 'activity',
24
27
  '#file_tree': 'claude-tree',
25
- '#browser_logs': 'browser-logs',
26
28
  '': 'events', // default
27
29
  };
28
30
 
@@ -33,8 +35,7 @@ class UIStateManager {
33
35
  'tools': '#tools',
34
36
  'files': '#files',
35
37
  'activity': '#activity',
36
- 'claude-tree': '#file_tree',
37
- 'browser-logs': '#browser_logs'
38
+ 'claude-tree': '#file_tree'
38
39
  };
39
40
 
40
41
  // Current active tab - will be set based on URL hash
@@ -80,6 +81,7 @@ class UIStateManager {
80
81
  */
81
82
  setupEventHandlers() {
82
83
  this.setupHashNavigation();
84
+ this.setupTabClickHandlers(); // Add explicit tab click handlers
83
85
  this.setupUnifiedKeyboardNavigation();
84
86
  }
85
87
 
@@ -105,8 +107,23 @@ class UIStateManager {
105
107
  */
106
108
  handleHashChange() {
107
109
  const hash = window.location.hash || '';
110
+ console.log('[Hash Navigation] DETAILED DEBUG:');
111
+ console.log('[Hash Navigation] - Current hash:', hash);
112
+ console.log('[Hash Navigation] - hashToTab mapping:', this.hashToTab);
113
+ console.log('[Hash Navigation] - Direct lookup result:', this.hashToTab[hash]);
114
+ console.log('[Hash Navigation] - Is hash in mapping?', hash in this.hashToTab);
115
+ console.log('[Hash Navigation] - Hash length:', hash.length);
116
+ console.log('[Hash Navigation] - Hash char codes:', hash.split('').map(c => c.charCodeAt(0)));
117
+
108
118
  const tabName = this.hashToTab[hash] || 'events';
109
- console.log('[Hash Navigation] Switching to tab:', tabName, 'from hash:', hash);
119
+ console.log('[Hash Navigation] Final resolved tab name:', tabName);
120
+
121
+ // Special logging for File Tree tab
122
+ if (tabName === 'claude-tree' || hash === '#file_tree') {
123
+ console.log('[UIStateManager] FILE TREE TAB SELECTED via hash:', hash);
124
+ console.log('[UIStateManager] Tab name resolved to:', tabName);
125
+ }
126
+
110
127
  this.switchTab(tabName, false); // false = don't update hash (we're responding to hash change)
111
128
  }
112
129
 
@@ -118,6 +135,36 @@ class UIStateManager {
118
135
  console.log('[Hash Navigation] setupTabNavigation is deprecated - using hash navigation instead');
119
136
  }
120
137
 
138
+ /**
139
+ * Set up explicit click handlers for tab buttons to ensure proper routing
140
+ * This ensures tab clicks work even if other modules interfere
141
+ */
142
+ setupTabClickHandlers() {
143
+ document.querySelectorAll('.tab-button').forEach(button => {
144
+ button.addEventListener('click', (e) => {
145
+ console.log('[UIStateManager] Tab button clicked:', e.target);
146
+
147
+ // Prevent default only if we're going to handle it
148
+ const tabName = this.getTabNameFromButton(e.target);
149
+ console.log('[UIStateManager] Resolved tab name:', tabName);
150
+
151
+ if (tabName) {
152
+ // Let the href attribute update the hash naturally, which will trigger our hashchange handler
153
+ // But also explicitly trigger the switch in case href doesn't work
154
+ setTimeout(() => {
155
+ const expectedHash = this.tabToHash[tabName];
156
+ if (window.location.hash !== expectedHash && expectedHash) {
157
+ console.log('[UIStateManager] Hash not updated, forcing update:', expectedHash);
158
+ window.location.hash = expectedHash;
159
+ }
160
+ }, 10);
161
+ }
162
+ });
163
+ });
164
+
165
+ console.log('[UIStateManager] Tab click handlers set up for', document.querySelectorAll('.tab-button').length, 'buttons');
166
+ }
167
+
121
168
  /**
122
169
  * Set up unified keyboard navigation across all tabs
123
170
  */
@@ -147,142 +194,270 @@ class UIStateManager {
147
194
  * @returns {string} - Tab name
148
195
  */
149
196
  getTabNameFromButton(button) {
197
+ console.log('[getTabNameFromButton] DEBUG: button object:', button);
198
+ console.log('[getTabNameFromButton] DEBUG: button.nodeType:', button.nodeType);
199
+ console.log('[getTabNameFromButton] DEBUG: button.tagName:', button.tagName);
200
+
201
+ // CRITICAL FIX: Make sure we're dealing with the actual button element
202
+ // Sometimes the click target might be a child element (like the emoji icon)
203
+ let targetButton = button;
204
+ if (button && button.closest && button.closest('.tab-button')) {
205
+ targetButton = button.closest('.tab-button');
206
+ console.log('[getTabNameFromButton] DEBUG: Used closest() to find actual button');
207
+ }
208
+
150
209
  // First check for data-tab attribute
151
- const dataTab = button.getAttribute('data-tab');
152
- if (dataTab) return dataTab;
210
+ const dataTab = targetButton ? targetButton.getAttribute('data-tab') : null;
211
+ console.log('[getTabNameFromButton] DEBUG: data-tab attribute:', dataTab);
212
+ console.log('[getTabNameFromButton] DEBUG: dataTab truthy:', !!dataTab);
213
+
214
+ // CRITICAL: Specifically handle the File Tree case
215
+ if (dataTab === 'claude-tree') {
216
+ console.log('[getTabNameFromButton] DEBUG: Found claude-tree data-tab, returning it');
217
+ return 'claude-tree';
218
+ }
219
+
220
+ if (dataTab) {
221
+ console.log('[getTabNameFromButton] DEBUG: Returning dataTab:', dataTab);
222
+ return dataTab;
223
+ }
153
224
 
154
225
  // Fallback to text content matching
155
- const text = button.textContent.toLowerCase();
156
- if (text.includes('events')) return 'events';
157
- if (text.includes('activity')) return 'activity';
158
- if (text.includes('agents')) return 'agents';
159
- if (text.includes('tools')) return 'tools';
160
- if (text.includes('browser')) return 'browser-logs'; // Added browser logs support
161
- if (text.includes('files')) return 'files';
162
- if (text.includes('file tree')) return 'claude-tree';
226
+ const text = targetButton ? targetButton.textContent.toLowerCase() : '';
227
+ console.log('[getTabNameFromButton] DEBUG: text content:', text);
228
+ console.log('[getTabNameFromButton] DEBUG: text includes file tree:', text.includes('file tree'));
229
+ console.log('[getTabNameFromButton] DEBUG: text includes events:', text.includes('events'));
230
+
231
+ // CRITICAL: Check File Tree FIRST since it's the problematic one
232
+ if (text.includes('file tree') || text.includes('📝')) {
233
+ console.log('[getTabNameFromButton] DEBUG: Matched file tree, returning claude-tree');
234
+ return 'claude-tree';
235
+ }
236
+ if (text.includes('activity') || text.includes('🌳')) return 'activity';
237
+ if (text.includes('agents') || text.includes('🤖')) return 'agents';
238
+ if (text.includes('tools') || text.includes('🔧')) return 'tools';
239
+ if (text.includes('files') || text.includes('📁')) return 'files';
163
240
  if (text.includes('code')) return 'code';
164
241
  if (text.includes('sessions')) return 'sessions';
165
242
  if (text.includes('system')) return 'system';
243
+ if (text.includes('events') || text.includes('📊')) return 'events';
244
+
245
+ console.log('[getTabNameFromButton] DEBUG: No match, falling back to events');
166
246
  return 'events';
167
247
  }
168
248
 
169
249
  /**
170
- * Switch to specified tab
250
+ * Switch to specified tab - BULLETPROOF VERSION
171
251
  * @param {string} tabName - Name of tab to switch to
172
252
  * @param {boolean} updateHash - Whether to update URL hash (default: true)
173
253
  */
174
254
  switchTab(tabName, updateHash = true) {
175
- console.log(`[Hash Navigation] switchTab called with tabName: ${tabName}, updateHash: ${updateHash}`);
176
-
177
- // Update URL hash if requested (when triggered by user action, not hash change)
178
- if (updateHash && this.tabToHash[tabName]) {
179
- const newHash = this.tabToHash[tabName];
180
- if (window.location.hash !== newHash) {
181
- console.log(`[Hash Navigation] Updating hash to: ${newHash}`);
182
- window.location.hash = newHash;
183
- return; // The hashchange event will trigger switchTab again
184
- }
255
+ // CRITICAL: Prevent race conditions by using a switching lock
256
+ if (this._switching) {
257
+ console.log(`[UIStateManager] Tab switch already in progress, queuing: ${tabName}`);
258
+ setTimeout(() => this.switchTab(tabName, updateHash), 50);
259
+ return;
185
260
  }
261
+ this._switching = true;
186
262
 
187
- const previousTab = this.currentTab;
188
- this.currentTab = tabName;
263
+ console.log(`[UIStateManager] BULLETPROOF switchTab: ${tabName}, updateHash: ${updateHash}`);
189
264
 
190
- // Update tab button active states - ensure ALL tabs are deselected first
191
- const allTabButtons = document.querySelectorAll('.tab-button');
192
- allTabButtons.forEach(btn => {
193
- btn.classList.remove('active');
194
- });
195
-
196
- // Now add active class ONLY to the selected tab
197
- allTabButtons.forEach(btn => {
198
- const btnTabName = this.getTabNameFromButton(btn);
199
- if (btnTabName === tabName) {
200
- btn.classList.add('active');
201
- console.log(`[DEBUG] Set active on button with data-tab: ${btn.getAttribute('data-tab')}`);
265
+ try {
266
+ // Extra logging for File Tree debugging
267
+ if (tabName === 'claude-tree') {
268
+ console.log('[UIStateManager] SWITCHING TO FILE TREE TAB');
269
+ console.log('[UIStateManager] Current tab before switch:', this.currentTab);
202
270
  }
271
+
272
+ // Update URL hash if requested (when triggered by user action, not hash change)
273
+ if (updateHash && this.tabToHash[tabName]) {
274
+ const newHash = this.tabToHash[tabName];
275
+ if (window.location.hash !== newHash) {
276
+ console.log(`[UIStateManager] Updating hash to: ${newHash}`);
277
+ this._switching = false; // Release lock before hash change
278
+ window.location.hash = newHash;
279
+ return; // The hashchange event will trigger switchTab again
280
+ }
281
+ }
282
+
283
+ const previousTab = this.currentTab;
284
+ this.currentTab = tabName;
285
+
286
+ // STEP 1: NUCLEAR RESET - Remove ALL active states unconditionally
287
+ this._removeAllActiveStates();
288
+
289
+ // STEP 2: Set the ONE correct tab as active
290
+ this._setActiveTab(tabName);
291
+
292
+ // STEP 3: Show ONLY the correct content
293
+ this._showTabContent(tabName);
294
+
295
+ // STEP 4: Cleanup and validation
296
+ this._validateTabState(tabName);
297
+
298
+ // Clear previous selections when switching tabs
299
+ this.clearUnifiedSelection();
300
+
301
+ // Trigger tab change event for other modules
302
+ document.dispatchEvent(new CustomEvent('tabChanged', {
303
+ detail: {
304
+ newTab: tabName,
305
+ previousTab: previousTab
306
+ }
307
+ }));
308
+
309
+ // Auto-scroll to bottom after a brief delay to ensure content is rendered
310
+ setTimeout(() => {
311
+ if (this.autoScroll) {
312
+ this.scrollCurrentTabToBottom();
313
+ }
314
+
315
+ // Special handling for File Tree tab - trigger the tree render
316
+ // But DON'T let it manipulate tabs itself
317
+ if (tabName === 'claude-tree' && window.CodeViewer) {
318
+ // Call a new method that only renders content, not tab switching
319
+ if (window.CodeViewer.renderContent) {
320
+ window.CodeViewer.renderContent();
321
+ } else {
322
+ // Fallback to show() but it should be fixed to not switch tabs
323
+ window.CodeViewer.show();
324
+ }
325
+ }
326
+ }, 100);
327
+
328
+ } finally {
329
+ // ALWAYS release the lock
330
+ setTimeout(() => {
331
+ this._switching = false;
332
+ }, 200);
333
+ }
334
+ }
335
+
336
+ /**
337
+ * NUCLEAR RESET: Remove ALL active states from ALL elements
338
+ * This ensures no stale states remain
339
+ */
340
+ _removeAllActiveStates() {
341
+ // Remove active class from ALL tab buttons
342
+ document.querySelectorAll('.tab-button').forEach(btn => {
343
+ btn.classList.remove('active');
344
+ // Also remove any inline styling that might interfere
345
+ btn.style.removeProperty('border-bottom');
346
+ btn.style.removeProperty('color');
203
347
  });
204
348
 
205
- // Show/hide tab content using CSS classes - ensure ALL are hidden first
206
- const allTabContents = document.querySelectorAll('.tab-content');
207
- allTabContents.forEach(content => {
349
+ // Remove active class from ALL tab content
350
+ document.querySelectorAll('.tab-content').forEach(content => {
208
351
  content.classList.remove('active');
352
+ // Clear any inline display styles
353
+ content.style.removeProperty('display');
354
+
355
+ // CRITICAL: Clean leaked content in non-events tabs
356
+ if (content.id !== 'events-tab') {
357
+ this._cleanLeakedEventContent(content);
358
+ }
209
359
  });
210
360
 
211
- // Now show ONLY the selected tab content
212
- const activeTab = document.getElementById(`${tabName}-tab`);
213
- if (activeTab) {
214
- activeTab.classList.add('active');
215
- console.log(`[DEBUG] Set active on content: ${tabName}-tab`);
216
-
217
- // Special handling for File Tree tab - ensure it never shows events
361
+ console.log('[UIStateManager] NUCLEAR: All active states removed');
362
+ }
363
+
364
+ /**
365
+ * Set ONLY the specified tab as active
366
+ */
367
+ _setActiveTab(tabName) {
368
+ const targetTab = document.querySelector(`[data-tab="${tabName}"]`);
369
+ if (targetTab) {
370
+ targetTab.classList.add('active');
371
+ console.log(`[UIStateManager] Set active: ${tabName}`);
372
+ } else {
373
+ console.error(`[UIStateManager] Could not find tab button for: ${tabName}`);
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Show ONLY the specified tab content
379
+ */
380
+ _showTabContent(tabName) {
381
+ const targetContent = document.getElementById(`${tabName}-tab`);
382
+ if (targetContent) {
383
+ targetContent.classList.add('active');
384
+ console.log(`[UIStateManager] Showing content: ${tabName}-tab`);
385
+
386
+ // Special handling for File Tree tab
218
387
  if (tabName === 'claude-tree') {
219
- const claudeTreeContainer = document.getElementById('claude-tree-container');
220
- if (claudeTreeContainer) {
221
- // Check if events list somehow got into this container
222
- const eventsList = claudeTreeContainer.querySelector('#events-list');
223
- if (eventsList) {
224
- console.warn('[UIStateManager] Found events-list in File Tree container, removing it!');
225
- eventsList.remove();
226
- }
227
-
228
- // Check for event items
229
- const eventItems = claudeTreeContainer.querySelectorAll('.event-item');
230
- if (eventItems.length > 0) {
231
- console.warn('[UIStateManager] Found event items in File Tree container, clearing!');
232
- eventItems.forEach(item => item.remove());
233
- }
234
- }
388
+ this._prepareFileTreeContent(targetContent);
235
389
  }
390
+ } else {
391
+ console.error(`[UIStateManager] Could not find content for: ${tabName}`);
236
392
  }
393
+ }
237
394
 
238
- // Clear previous selections when switching tabs
239
- this.clearUnifiedSelection();
395
+ /**
396
+ * Clean any leaked event content from non-event tabs
397
+ */
398
+ _cleanLeakedEventContent(contentElement) {
399
+ // Remove any event items that may have leaked
400
+ const leakedEventItems = contentElement.querySelectorAll('.event-item');
401
+ if (leakedEventItems.length > 0) {
402
+ console.warn(`[UIStateManager] Found ${leakedEventItems.length} leaked event items in ${contentElement.id}, removing...`);
403
+ leakedEventItems.forEach(item => item.remove());
404
+ }
240
405
 
241
- // Trigger tab change event for other modules
242
- document.dispatchEvent(new CustomEvent('tabChanged', {
243
- detail: {
244
- newTab: tabName,
245
- previousTab: previousTab
246
- }
247
- }));
406
+ // Remove any events-list elements
407
+ const leakedEventsList = contentElement.querySelectorAll('#events-list, .events-list');
408
+ if (leakedEventsList.length > 0) {
409
+ console.warn(`[UIStateManager] Found leaked events-list in ${contentElement.id}, removing...`);
410
+ leakedEventsList.forEach(list => list.remove());
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Prepare File Tree content area
416
+ */
417
+ _prepareFileTreeContent(fileTreeContent) {
418
+ const claudeTreeContainer = document.getElementById('claude-tree-container');
419
+ if (claudeTreeContainer) {
420
+ // Final cleanup check
421
+ this._cleanLeakedEventContent(claudeTreeContainer);
422
+
423
+ // Ensure container is properly marked for CodeViewer
424
+ claudeTreeContainer.setAttribute('data-owner', 'code-viewer');
425
+ claudeTreeContainer.setAttribute('data-component', 'CodeViewer');
248
426
 
249
- // Auto-scroll to bottom after a brief delay to ensure content is rendered
427
+ console.log('[UIStateManager] File Tree container prepared');
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Validate that tab state is correct after switching
433
+ */
434
+ _validateTabState(expectedTab) {
250
435
  setTimeout(() => {
251
- if (this.autoScroll) {
252
- this.scrollCurrentTabToBottom();
253
- }
254
-
255
- // Special handling for File Tree tab - trigger the tree render
256
- // But DON'T let it manipulate tabs itself
257
- if (tabName === 'claude-tree' && window.CodeViewer) {
258
- // Call a new method that only renders content, not tab switching
259
- if (window.CodeViewer.renderContent) {
260
- window.CodeViewer.renderContent();
261
- } else {
262
- // Fallback to show() but it should be fixed to not switch tabs
263
- window.CodeViewer.show();
264
- }
436
+ const activeTabs = document.querySelectorAll('.tab-button.active');
437
+ const activeContents = document.querySelectorAll('.tab-content.active');
438
+
439
+ if (activeTabs.length !== 1) {
440
+ console.error(`[UIStateManager] VALIDATION FAILED: Expected 1 active tab, found ${activeTabs.length}`);
441
+ activeTabs.forEach((tab, idx) => {
442
+ console.error(` - Active tab ${idx + 1}: ${tab.textContent.trim()} (${tab.getAttribute('data-tab')})`);
443
+ });
444
+ // Force fix
445
+ this._removeAllActiveStates();
446
+ this._setActiveTab(expectedTab);
265
447
  }
266
-
267
- // SIMPLEST POSSIBLE Browser Logs tab - just static text
268
- if (tabName === 'browser-logs') {
269
- console.log('[UIStateManager] Switching to Browser Logs tab - simple mode');
270
-
271
- const container = document.getElementById('browser-logs-container');
272
- if (container) {
273
- // Clear EVERYTHING - no complex logic, just clear it
274
- container.innerHTML = '';
275
-
276
- // Set the simplest possible content - just text
277
- container.innerHTML = '<h2 style="padding: 20px;">Browser Logs</h2>';
278
-
279
- // That's it. Nothing else. No event listeners, no watchers, nothing.
280
- console.log('[UIStateManager] Browser Logs tab set to simple text');
281
- } else {
282
- console.warn('[UIStateManager] Browser logs container not found');
283
- }
448
+
449
+ if (activeContents.length !== 1) {
450
+ console.error(`[UIStateManager] VALIDATION FAILED: Expected 1 active content, found ${activeContents.length}`);
451
+ activeContents.forEach((content, idx) => {
452
+ console.error(` - Active content ${idx + 1}: ${content.id}`);
453
+ });
454
+ // Force fix
455
+ this._removeAllActiveStates();
456
+ this._showTabContent(expectedTab);
284
457
  }
285
- }, 100);
458
+
459
+ console.log(`[UIStateManager] Tab state validated for: ${expectedTab}`);
460
+ }, 50);
286
461
  }
287
462
 
288
463
  /**
@@ -569,3 +744,6 @@ class UIStateManager {
569
744
  // ES6 Module export
570
745
  export { UIStateManager };
571
746
  export default UIStateManager;
747
+
748
+ // Make UIStateManager globally available for dist/dashboard.js
749
+ window.UIStateManager = UIStateManager;
@@ -915,3 +915,6 @@ class WorkingDirectoryManager {
915
915
  // ES6 Module export
916
916
  export { WorkingDirectoryManager };
917
917
  export default WorkingDirectoryManager;
918
+
919
+ // Make WorkingDirectoryManager globally available for dist/dashboard.js
920
+ window.WorkingDirectoryManager = WorkingDirectoryManager;