claude-mpm 4.1.10__py3-none-any.whl → 4.1.12__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 (56) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +8 -0
  3. claude_mpm/cli/__init__.py +11 -0
  4. claude_mpm/cli/commands/analyze.py +2 -1
  5. claude_mpm/cli/commands/configure.py +9 -8
  6. claude_mpm/cli/commands/configure_tui.py +3 -1
  7. claude_mpm/cli/commands/dashboard.py +288 -0
  8. claude_mpm/cli/commands/debug.py +0 -1
  9. claude_mpm/cli/commands/mpm_init.py +442 -0
  10. claude_mpm/cli/commands/mpm_init_handler.py +84 -0
  11. claude_mpm/cli/parsers/base_parser.py +15 -0
  12. claude_mpm/cli/parsers/dashboard_parser.py +113 -0
  13. claude_mpm/cli/parsers/mpm_init_parser.py +128 -0
  14. claude_mpm/constants.py +10 -0
  15. claude_mpm/core/config.py +18 -0
  16. claude_mpm/core/instruction_reinforcement_hook.py +266 -0
  17. claude_mpm/core/pm_hook_interceptor.py +105 -8
  18. claude_mpm/dashboard/analysis_runner.py +52 -25
  19. claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
  20. claude_mpm/dashboard/static/built/components/code-tree.js +2 -0
  21. claude_mpm/dashboard/static/built/components/code-viewer.js +2 -0
  22. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  23. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  24. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  25. claude_mpm/dashboard/static/css/code-tree.css +330 -1
  26. claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
  27. claude_mpm/dashboard/static/dist/components/code-tree.js +2593 -2
  28. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  29. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  30. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  31. claude_mpm/dashboard/static/js/components/activity-tree.js +212 -13
  32. claude_mpm/dashboard/static/js/components/build-tracker.js +15 -13
  33. claude_mpm/dashboard/static/js/components/code-tree.js +2503 -917
  34. claude_mpm/dashboard/static/js/components/event-viewer.js +58 -19
  35. claude_mpm/dashboard/static/js/dashboard.js +46 -44
  36. claude_mpm/dashboard/static/js/socket-client.js +74 -32
  37. claude_mpm/dashboard/templates/index.html +25 -20
  38. claude_mpm/services/agents/deployment/agent_template_builder.py +11 -7
  39. claude_mpm/services/agents/memory/memory_format_service.py +3 -1
  40. claude_mpm/services/cli/agent_cleanup_service.py +1 -4
  41. claude_mpm/services/cli/socketio_manager.py +39 -8
  42. claude_mpm/services/cli/startup_checker.py +0 -1
  43. claude_mpm/services/core/cache_manager.py +0 -1
  44. claude_mpm/services/infrastructure/monitoring.py +1 -1
  45. claude_mpm/services/socketio/event_normalizer.py +64 -0
  46. claude_mpm/services/socketio/handlers/code_analysis.py +449 -0
  47. claude_mpm/services/socketio/server/connection_manager.py +3 -1
  48. claude_mpm/tools/code_tree_analyzer.py +930 -24
  49. claude_mpm/tools/code_tree_builder.py +0 -1
  50. claude_mpm/tools/code_tree_events.py +113 -15
  51. {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/METADATA +2 -1
  52. {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/RECORD +56 -48
  53. {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/WHEEL +0 -0
  54. {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/entry_points.txt +0 -0
  55. {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/licenses/LICENSE +0 -0
  56. {claude_mpm-4.1.10.dist-info → claude_mpm-4.1.12.dist-info}/top_level.txt +0 -0
@@ -39,7 +39,6 @@ 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');
43
42
  // Ensure we always have a valid events array
44
43
  this.events = Array.isArray(events) ? events : [];
45
44
  this.updateDisplay();
@@ -76,7 +75,6 @@ class EventViewer {
76
75
  setupKeyboardNavigation() {
77
76
  // Keyboard navigation is now handled by Dashboard.setupUnifiedKeyboardNavigation()
78
77
  // This method is kept for backward compatibility but does nothing
79
- console.log('EventViewer: Keyboard navigation handled by unified Dashboard system');
80
78
  }
81
79
 
82
80
  /**
@@ -110,18 +108,10 @@ class EventViewer {
110
108
  }
111
109
 
112
110
  this.filteredEvents = this.events.filter(event => {
113
- // Filter out info level log messages
114
- if (event.type === 'log' && event.data && event.data.level === 'info') {
115
- return false;
116
- }
117
-
118
- // Filter out code analysis events (they're shown in footer status bar)
119
- if (event.type === 'code' ||
120
- (event.type === 'unknown' && event.originalEventName && event.originalEventName.startsWith('code:'))) {
121
- return false;
122
- }
111
+ // NO AUTOMATIC FILTERING - All events are shown by default for complete visibility
112
+ // Users can apply their own filters using the search and type filter controls
123
113
 
124
- // Search filter
114
+ // User-controlled search filter
125
115
  if (this.searchFilter) {
126
116
  const searchableText = [
127
117
  event.type || '',
@@ -134,7 +124,7 @@ class EventViewer {
134
124
  }
135
125
  }
136
126
 
137
- // Type filter - now handles full hook types (like "hook.user_prompt") and main types
127
+ // User-controlled type filter - handles full hook types (like "hook.user_prompt") and main types
138
128
  if (this.typeFilter) {
139
129
  // Use the same logic as formatEventType to get the full event type
140
130
  const eventType = event.type && event.type.trim() !== '' ? event.type : '';
@@ -144,13 +134,14 @@ class EventViewer {
144
134
  }
145
135
  }
146
136
 
147
- // Session filter
137
+ // User-controlled session filter
148
138
  if (this.sessionFilter && this.sessionFilter !== '') {
149
139
  if (!event.data || event.data.session_id !== this.sessionFilter) {
150
140
  return false;
151
141
  }
152
142
  }
153
143
 
144
+ // Allow all events through unless filtered by user controls
154
145
  return true;
155
146
  });
156
147
 
@@ -222,7 +213,6 @@ class EventViewer {
222
213
  * Update the display with current events
223
214
  */
224
215
  updateDisplay() {
225
- console.log('EventViewer updating display with', this.events?.length || 0, 'events');
226
216
  this.updateEventTypeDropdown();
227
217
  this.applyFilters();
228
218
  }
@@ -234,6 +224,9 @@ class EventViewer {
234
224
  const eventsList = document.getElementById('events-list');
235
225
  if (!eventsList) return;
236
226
 
227
+ // Check if user is at bottom BEFORE rendering (for autoscroll decision)
228
+ const wasAtBottom = (eventsList.scrollTop + eventsList.clientHeight >= eventsList.scrollHeight - 10);
229
+
237
230
  if (this.filteredEvents.length === 0) {
238
231
  eventsList.innerHTML = `
239
232
  <div class="no-events">
@@ -281,9 +274,12 @@ class EventViewer {
281
274
  window.dashboard.tabNavigation.events.items = this.filteredEventElements;
282
275
  }
283
276
 
284
- // Auto-scroll to bottom if enabled
285
- if (this.autoScroll && this.filteredEvents.length > 0) {
286
- eventsList.scrollTop = eventsList.scrollHeight;
277
+ // Auto-scroll only if user was already at bottom before rendering
278
+ if (this.filteredEvents.length > 0 && wasAtBottom && this.autoScroll) {
279
+ // Use requestAnimationFrame to ensure DOM has updated
280
+ requestAnimationFrame(() => {
281
+ eventsList.scrollTop = eventsList.scrollHeight;
282
+ });
287
283
  }
288
284
  }
289
285
 
@@ -337,6 +333,8 @@ class EventViewer {
337
333
  return this.formatMemoryEvent(event);
338
334
  case 'log':
339
335
  return this.formatLogEvent(event);
336
+ case 'code':
337
+ return this.formatCodeEvent(event);
340
338
  default:
341
339
  return this.formatGenericEvent(event);
342
340
  }
@@ -480,6 +478,47 @@ class EventViewer {
480
478
  return `<strong>[${level.toUpperCase()}]</strong> ${truncated}`;
481
479
  }
482
480
 
481
+ /**
482
+ * Format code analysis event data
483
+ */
484
+ formatCodeEvent(event) {
485
+ const data = event.data || {};
486
+
487
+ // Handle different code event subtypes
488
+ if (event.subtype === 'progress') {
489
+ const message = data.message || 'Processing...';
490
+ const percentage = data.percentage;
491
+ if (percentage !== undefined) {
492
+ return `<strong>Progress:</strong> ${message} (${Math.round(percentage)}%)`;
493
+ }
494
+ return `<strong>Progress:</strong> ${message}`;
495
+ } else if (event.subtype === 'analysis:queued') {
496
+ return `<strong>Queued:</strong> Analysis for ${data.path || 'Unknown path'}`;
497
+ } else if (event.subtype === 'analysis:start') {
498
+ return `<strong>Started:</strong> Analyzing ${data.path || 'Unknown path'}`;
499
+ } else if (event.subtype === 'analysis:complete') {
500
+ const duration = data.duration ? ` (${data.duration.toFixed(2)}s)` : '';
501
+ return `<strong>Complete:</strong> Analysis finished${duration}`;
502
+ } else if (event.subtype === 'analysis:error') {
503
+ return `<strong>Error:</strong> ${data.message || 'Analysis failed'}`;
504
+ } else if (event.subtype === 'analysis:cancelled') {
505
+ return `<strong>Cancelled:</strong> Analysis stopped for ${data.path || 'Unknown path'}`;
506
+ } else if (event.subtype === 'file:start') {
507
+ return `<strong>File:</strong> Processing ${data.file || 'Unknown file'}`;
508
+ } else if (event.subtype === 'file:complete') {
509
+ const nodes = data.nodes_count !== undefined ? ` (${data.nodes_count} nodes)` : '';
510
+ return `<strong>File done:</strong> ${data.file || 'Unknown file'}${nodes}`;
511
+ } else if (event.subtype === 'node:found') {
512
+ return `<strong>Node:</strong> Found ${data.node_type || 'element'} "${data.name || 'unnamed'}"`;
513
+ } else if (event.subtype === 'error') {
514
+ return `<strong>Error:</strong> ${data.error || 'Unknown error'} in ${data.file || 'file'}`;
515
+ }
516
+
517
+ // Generic fallback for code events
518
+ const json = JSON.stringify(data);
519
+ return `<strong>Code:</strong> ${json.length > 100 ? json.substring(0, 100) + '...' : json}`;
520
+ }
521
+
483
522
  /**
484
523
  * Format generic event data
485
524
  */
@@ -163,14 +163,22 @@ class Dashboard {
163
163
  // Set the socket client for receiving updates
164
164
  this.buildTracker.setSocketClient(this.socketClient);
165
165
 
166
- // Mount to header - find the best location
167
- const headerTitle = document.querySelector('.header-title');
168
- if (headerTitle) {
169
- // Insert after the title and status badge
170
- this.buildTracker.mount(headerTitle);
171
- } else {
172
- console.warn('Could not find header-title element for build tracker');
173
- }
166
+ // Mount to header with retry logic for DOM readiness
167
+ const mountBuildTracker = () => {
168
+ const headerTitle = document.querySelector('.header-title');
169
+ if (headerTitle) {
170
+ // Insert after the title and status badge
171
+ this.buildTracker.mount(headerTitle);
172
+ console.log('BuildTracker mounted successfully');
173
+ } else {
174
+ console.warn('Header-title element not found for build tracker, will retry');
175
+ // Retry after a short delay if DOM is still being constructed
176
+ setTimeout(mountBuildTracker, 100);
177
+ }
178
+ };
179
+
180
+ // Try to mount immediately, with retry logic if needed
181
+ mountBuildTracker();
174
182
 
175
183
  // Make available globally for debugging
176
184
  window.buildTracker = this.buildTracker;
@@ -393,13 +401,25 @@ class Dashboard {
393
401
  console.log('Dashboard triggering activity tree render...');
394
402
  window.activityTreeInstance.renderWhenVisible();
395
403
  }
404
+
405
+ // Force show to ensure the tree is visible
406
+ if (typeof window.activityTreeInstance.forceShow === 'function') {
407
+ console.log('Dashboard forcing activity tree to show...');
408
+ window.activityTreeInstance.forceShow();
409
+ }
396
410
  }
397
411
  } else if (window.activityTree && typeof window.activityTree === 'function') {
398
412
  // Fallback to legacy approach if available
399
413
  const activityTreeInstance = window.activityTree();
400
- if (activityTreeInstance && typeof activityTreeInstance.renderWhenVisible === 'function') {
401
- console.log('Dashboard triggering activity tree render (legacy)...');
402
- activityTreeInstance.renderWhenVisible();
414
+ if (activityTreeInstance) {
415
+ if (typeof activityTreeInstance.renderWhenVisible === 'function') {
416
+ console.log('Dashboard triggering activity tree render (legacy)...');
417
+ activityTreeInstance.renderWhenVisible();
418
+ }
419
+ if (typeof activityTreeInstance.forceShow === 'function') {
420
+ console.log('Dashboard forcing activity tree to show (legacy)...');
421
+ activityTreeInstance.forceShow();
422
+ }
403
423
  }
404
424
  } else {
405
425
  // Module not loaded yet, retry after a delay
@@ -1068,14 +1088,11 @@ async function updateFileViewerModal(modal, filePath, workingDir) {
1068
1088
  working_dir: workingDir
1069
1089
  });
1070
1090
 
1071
- console.log('📄 File viewer request sent:', {
1072
- filePath,
1073
- workingDir
1074
- });
1091
+ // File viewer request sent
1075
1092
 
1076
1093
  // Wait for response
1077
1094
  const result = await responsePromise;
1078
- console.log('📦 File content received:', result);
1095
+ // File content received successfully
1079
1096
 
1080
1097
  // Hide loading
1081
1098
  modal.querySelector('.file-viewer-loading').style.display = 'none';
@@ -1131,7 +1148,7 @@ async function updateFileViewerModal(modal, filePath, workingDir) {
1131
1148
  }
1132
1149
 
1133
1150
  function displayFileContent(modal, result) {
1134
- console.log('📝 displayFileContent called with:', result);
1151
+ // Display file content in modal
1135
1152
  const contentArea = modal.querySelector('.file-viewer-content-area');
1136
1153
  const extensionElement = modal.querySelector('.file-extension');
1137
1154
  const encodingElement = modal.querySelector('.file-encoding');
@@ -1145,7 +1162,7 @@ function displayFileContent(modal, result) {
1145
1162
 
1146
1163
  // Update content with basic syntax highlighting
1147
1164
  if (codeElement && result.content) {
1148
- console.log('💡 Setting file content, length:', result.content.length);
1165
+ // Setting file content
1149
1166
  codeElement.innerHTML = highlightCode(result.content, result.extension);
1150
1167
 
1151
1168
  // Force scrolling to work by setting explicit heights
@@ -1163,12 +1180,7 @@ function displayFileContent(modal, result) {
1163
1180
 
1164
1181
  const availableHeight = modalHeight - headerHeight - toolbarHeight - 40; // 40px for padding
1165
1182
 
1166
- console.log('🎯 Setting file viewer scroll height:', {
1167
- modalHeight,
1168
- headerHeight,
1169
- toolbarHeight,
1170
- availableHeight
1171
- });
1183
+ // Setting file viewer scroll height
1172
1184
 
1173
1185
  wrapper.style.maxHeight = `${availableHeight}px`;
1174
1186
  wrapper.style.overflowY = 'auto';
@@ -1181,7 +1193,7 @@ function displayFileContent(modal, result) {
1181
1193
  // Show content area
1182
1194
  if (contentArea) {
1183
1195
  contentArea.style.display = 'block';
1184
- console.log('✅ File content area displayed');
1196
+ // File content area displayed
1185
1197
  }
1186
1198
  }
1187
1199
 
@@ -1521,7 +1533,7 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) {
1521
1533
  throw new Error(`Server health check failed: ${healthResponse.status} ${healthResponse.statusText}`);
1522
1534
  }
1523
1535
 
1524
- console.log('✅ Server health check passed');
1536
+ // Server health check passed
1525
1537
  } catch (healthError) {
1526
1538
  throw new Error(`Cannot reach server at localhost:${port}. Health check failed: ${healthError.message}`);
1527
1539
  }
@@ -1541,17 +1553,17 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) {
1541
1553
  }
1542
1554
 
1543
1555
  const result = await response.json();
1544
- console.log('📦 Git diff response:', result);
1556
+ // Git diff response received
1545
1557
 
1546
1558
  // Hide loading
1547
1559
  modal.querySelector('.git-diff-loading').style.display = 'none';
1548
1560
 
1549
1561
  if (result.success) {
1550
- console.log('📊 Displaying successful git diff');
1562
+ // Displaying successful git diff
1551
1563
  // Show successful diff
1552
1564
  displayGitDiff(modal, result);
1553
1565
  } else {
1554
- console.log('⚠️ Displaying git diff error:', result);
1566
+ // Displaying git diff error
1555
1567
  // Show error
1556
1568
  displayGitDiffError(modal, result);
1557
1569
  }
@@ -1649,18 +1661,13 @@ function highlightGitDiff(diffText) {
1649
1661
  }
1650
1662
 
1651
1663
  function displayGitDiff(modal, result) {
1652
- console.log('📝 displayGitDiff called with:', result);
1664
+ // Display git diff content
1653
1665
  const contentArea = modal.querySelector('.git-diff-content-area');
1654
1666
  const commitHashElement = modal.querySelector('.commit-hash');
1655
1667
  const methodElement = modal.querySelector('.diff-method');
1656
1668
  const codeElement = modal.querySelector('.git-diff-code');
1657
1669
 
1658
- console.log('🔍 Elements found:', {
1659
- contentArea: !!contentArea,
1660
- commitHashElement: !!commitHashElement,
1661
- methodElement: !!methodElement,
1662
- codeElement: !!codeElement
1663
- });
1670
+ // Elements found for diff display
1664
1671
 
1665
1672
  // Update metadata
1666
1673
  if (commitHashElement) commitHashElement.textContent = `Commit: ${result.commit_hash}`;
@@ -1668,7 +1675,7 @@ function displayGitDiff(modal, result) {
1668
1675
 
1669
1676
  // Update diff content with basic syntax highlighting
1670
1677
  if (codeElement && result.diff) {
1671
- console.log('💡 Setting diff content, length:', result.diff.length);
1678
+ // Setting diff content
1672
1679
  codeElement.innerHTML = highlightGitDiff(result.diff);
1673
1680
 
1674
1681
  // Force scrolling to work by setting explicit heights
@@ -1686,12 +1693,7 @@ function displayGitDiff(modal, result) {
1686
1693
 
1687
1694
  const availableHeight = modalHeight - headerHeight - toolbarHeight - 40; // 40px for padding
1688
1695
 
1689
- console.log('🎯 Setting explicit scroll height:', {
1690
- modalHeight,
1691
- headerHeight,
1692
- toolbarHeight,
1693
- availableHeight
1694
- });
1696
+ // Setting explicit scroll height
1695
1697
 
1696
1698
  wrapper.style.maxHeight = `${availableHeight}px`;
1697
1699
  wrapper.style.overflowY = 'auto';
@@ -1704,7 +1706,7 @@ function displayGitDiff(modal, result) {
1704
1706
  // Show content area
1705
1707
  if (contentArea) {
1706
1708
  contentArea.style.display = 'block';
1707
- console.log('✅ Content area displayed');
1709
+ // Content area displayed
1708
1710
  }
1709
1711
  }
1710
1712
 
@@ -475,11 +475,10 @@ class SocketClient {
475
475
  return;
476
476
  }
477
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
478
+ // Code analysis events are now allowed to flow through to the events list for troubleshooting
479
+ // They will appear in both the Events tab and the Code tab
480
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;
481
+ console.log('Code analysis event received via claude_event, adding to events list for troubleshooting:', validatedEvent.type);
483
482
  }
484
483
 
485
484
  // Transform event to match expected format (for backward compatibility)
@@ -551,46 +550,55 @@ class SocketClient {
551
550
  this.addEvent({ type: 'log', subtype: 'entry', timestamp: new Date().toISOString(), data });
552
551
  });
553
552
 
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
553
+ // Code analysis events - now allowed to flow through for troubleshooting
554
+ // These are ALSO handled by the code-tree component and shown in the footer
555
+ // They will appear in both places: Events tab (for troubleshooting) and Code tab (for visualization)
556
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');
557
+ // Add to events list for troubleshooting
558
+ console.log('Code analysis queued event received, adding to events list for troubleshooting');
559
+ this.addEvent({ type: 'code', subtype: 'analysis:queued', timestamp: new Date().toISOString(), data });
559
560
  });
560
561
 
561
562
  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');
563
+ // Add to events list for troubleshooting
564
+ console.log('Code analysis accepted event received, adding to events list for troubleshooting');
565
+ this.addEvent({ type: 'code', subtype: 'analysis:accepted', timestamp: new Date().toISOString(), data });
564
566
  });
565
567
 
566
568
  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
+ // Add to events list for troubleshooting
570
+ console.log('Code analysis start event received, adding to events list for troubleshooting');
571
+ this.addEvent({ type: 'code', subtype: 'analysis:start', timestamp: new Date().toISOString(), data });
569
572
  });
570
573
 
571
574
  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');
575
+ // Add to events list for troubleshooting
576
+ console.log('Code analysis complete event received, adding to events list for troubleshooting');
577
+ this.addEvent({ type: 'code', subtype: 'analysis:complete', timestamp: new Date().toISOString(), data });
574
578
  });
575
579
 
576
580
  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');
581
+ // Add to events list for troubleshooting
582
+ console.log('Code analysis error event received, adding to events list for troubleshooting');
583
+ this.addEvent({ type: 'code', subtype: 'analysis:error', timestamp: new Date().toISOString(), data });
579
584
  });
580
585
 
581
586
  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');
587
+ // Add to events list for troubleshooting
588
+ console.log('Code file start event received, adding to events list for troubleshooting');
589
+ this.addEvent({ type: 'code', subtype: 'file:start', timestamp: new Date().toISOString(), data });
584
590
  });
585
591
 
586
592
  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');
593
+ // Add to events list for troubleshooting
594
+ console.log('Code node found event received, adding to events list for troubleshooting');
595
+ this.addEvent({ type: 'code', subtype: 'node:found', timestamp: new Date().toISOString(), data });
589
596
  });
590
597
 
591
598
  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');
599
+ // Add to events list for troubleshooting
600
+ console.log('Code analysis progress event received, adding to events list for troubleshooting');
601
+ this.addEvent({ type: 'code', subtype: 'analysis:progress', timestamp: new Date().toISOString(), data });
594
602
  });
595
603
 
596
604
  this.socket.on('history', (data) => {
@@ -1082,6 +1090,7 @@ class SocketClient {
1082
1090
  // 1. Hook events: { type: 'hook.pre_tool', timestamp: '...', data: {...} }
1083
1091
  // 2. Legacy events: { event: 'TestStart', timestamp: '...', ... }
1084
1092
  // 3. Standard events: { type: 'session', subtype: 'started', ... }
1093
+ // 4. Normalized events: { type: 'code', subtype: 'progress', ... } - already normalized, keep as-is
1085
1094
 
1086
1095
  if (!eventData) {
1087
1096
  return eventData; // Return as-is if null/undefined
@@ -1089,8 +1098,26 @@ class SocketClient {
1089
1098
 
1090
1099
  let transformedEvent = { ...eventData };
1091
1100
 
1101
+ // Check if event is already normalized (has both type and subtype as separate fields)
1102
+ // This prevents double-transformation of events that were normalized on the backend
1103
+ const isAlreadyNormalized = eventData.type && eventData.subtype &&
1104
+ !eventData.type.includes('.') &&
1105
+ !eventData.type.includes(':');
1106
+
1107
+ if (isAlreadyNormalized) {
1108
+ // Event is already properly normalized from backend, just preserve it
1109
+ // Store a composite originalEventName for display if needed
1110
+ if (!transformedEvent.originalEventName) {
1111
+ if (eventData.subtype === 'generic' || eventData.type === eventData.subtype) {
1112
+ transformedEvent.originalEventName = eventData.type;
1113
+ } else {
1114
+ transformedEvent.originalEventName = `${eventData.type}.${eventData.subtype}`;
1115
+ }
1116
+ }
1117
+ // Return early to avoid further transformation
1118
+ }
1092
1119
  // Handle legacy format with 'event' field but no 'type'
1093
- if (!eventData.type && eventData.event) {
1120
+ else if (!eventData.type && eventData.event) {
1094
1121
  // Map common event names to proper type/subtype
1095
1122
  const eventName = eventData.event;
1096
1123
 
@@ -1121,8 +1148,10 @@ class SocketClient {
1121
1148
 
1122
1149
  // Remove the 'event' field to avoid confusion
1123
1150
  delete transformedEvent.event;
1151
+ // Store original event name for display purposes
1152
+ transformedEvent.originalEventName = eventName;
1124
1153
  }
1125
- // Handle standard format with 'type' field
1154
+ // Handle standard format with 'type' field that needs transformation
1126
1155
  else if (eventData.type) {
1127
1156
  const type = eventData.type;
1128
1157
 
@@ -1131,30 +1160,43 @@ class SocketClient {
1131
1160
  const subtype = type.substring(5); // Remove 'hook.' prefix
1132
1161
  transformedEvent.type = 'hook';
1133
1162
  transformedEvent.subtype = subtype;
1163
+ transformedEvent.originalEventName = type;
1134
1164
  }
1135
1165
  // Transform 'code:*' events to proper code type
1166
+ // Handle multi-level subtypes like 'code:analysis:queued'
1136
1167
  else if (type.startsWith('code:')) {
1137
1168
  transformedEvent.type = 'code';
1138
- transformedEvent.subtype = type.substring(5); // Remove 'code:' prefix
1169
+ // Replace colons with underscores in subtype for consistency
1170
+ const subtypePart = type.substring(5); // Remove 'code:' prefix
1171
+ transformedEvent.subtype = subtypePart.replace(/:/g, '_');
1172
+ transformedEvent.originalEventName = type;
1139
1173
  }
1140
1174
  // Transform other dotted types like 'session.started' -> type: 'session', subtype: 'started'
1141
1175
  else if (type.includes('.')) {
1142
1176
  const [mainType, ...subtypeParts] = type.split('.');
1143
1177
  transformedEvent.type = mainType;
1144
1178
  transformedEvent.subtype = subtypeParts.join('.');
1179
+ transformedEvent.originalEventName = type;
1180
+ }
1181
+ // Transform any remaining colon-separated types generically
1182
+ else if (type.includes(':')) {
1183
+ const parts = type.split(':', 2); // Split into max 2 parts
1184
+ transformedEvent.type = parts[0];
1185
+ // Replace any remaining colons with underscores in subtype
1186
+ transformedEvent.subtype = parts.length > 1 ? parts[1].replace(/:/g, '_') : 'generic';
1187
+ transformedEvent.originalEventName = type;
1188
+ }
1189
+ // If type doesn't need transformation but has no subtype, set a default
1190
+ else if (!eventData.subtype) {
1191
+ transformedEvent.subtype = 'generic';
1192
+ transformedEvent.originalEventName = type;
1145
1193
  }
1146
1194
  }
1147
1195
  // If no type and no event field, mark as unknown
1148
1196
  else {
1149
1197
  transformedEvent.type = 'unknown';
1150
1198
  transformedEvent.subtype = '';
1151
- }
1152
-
1153
- // Store original event name for display purposes (before any transformation)
1154
- if (!eventData.type && eventData.event) {
1155
- transformedEvent.originalEventName = eventData.event;
1156
- } else if (eventData.type) {
1157
- transformedEvent.originalEventName = eventData.type;
1199
+ transformedEvent.originalEventName = 'unknown';
1158
1200
  }
1159
1201
 
1160
1202
  // Extract and flatten data fields to top level for dashboard compatibility
@@ -389,12 +389,9 @@
389
389
  <!-- Code Tab -->
390
390
  <div class="tab-content" id="code-tab">
391
391
  <div class="code-container">
392
- <!-- Compact header with all controls in one line -->
392
+ <!-- Simplified header with controls -->
393
393
  <div class="code-header-compact">
394
394
  <div class="header-left">
395
- <input type="text" id="analysis-path" placeholder="Path" value="." class="path-input-compact">
396
- <button id="analyze-code" class="btn-compact btn-primary">🔍</button>
397
- <button id="cancel-analysis" class="btn-compact btn-danger" style="display: none;">✕</button>
398
395
  <button id="code-expand-all" class="btn-compact" title="Expand All">⊕</button>
399
396
  <button id="code-collapse-all" class="btn-compact" title="Collapse All">⊖</button>
400
397
  <button id="code-reset-zoom" class="btn-compact" title="Reset Zoom">⟲</button>
@@ -416,22 +413,20 @@
416
413
  <input type="text" id="code-search" placeholder="Search..." class="search-compact">
417
414
  </div>
418
415
  </div>
419
- <!-- Collapsible advanced options -->
420
- <details class="code-advanced-options">
421
- <summary>Advanced Options</summary>
416
+ <!-- Advanced options - visible by default -->
417
+ <div class="code-advanced-options-visible">
422
418
  <div class="advanced-content">
423
419
  <div class="option-group">
424
420
  <label>Languages:</label>
425
421
  <label><input type="checkbox" class="language-checkbox" value="python" checked> Python</label>
426
422
  <label><input type="checkbox" class="language-checkbox" value="javascript" checked> JS</label>
427
- <label><input type="checkbox" class="language-checkbox" value="typescript"> TS</label>
423
+ <label><input type="checkbox" class="language-checkbox" value="typescript" checked> TS</label>
428
424
  </div>
429
425
  <div class="option-group">
430
- <label>Depth: <input type="number" id="max-depth" min="1" max="10" value="5" class="input-compact"></label>
431
- <label>Ignore: <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js" class="input-compact"></label>
426
+ <label>Ignore: <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js, node_modules" class="input-compact" style="width: 200px;"></label>
432
427
  </div>
433
428
  </div>
434
- </details>
429
+ </div>
435
430
  <div id="code-tree-container" class="code-tree-container">
436
431
  <div id="code-tree"></div>
437
432
  <!-- Collapsible legend -->
@@ -515,14 +510,24 @@
515
510
  </div>
516
511
 
517
512
  <!-- JavaScript Modules -->
518
- <!-- Load bundled dashboard assets (built with Vite) -->
519
- <script type="module" src="/static/dist/dashboard.js"></script>
520
-
521
- <!-- Activity Tree Module (from Vite build) -->
522
- <script type="module" src="/static/dist/components/activity-tree.js"></script>
523
-
524
- <!-- Code Tree Module (from Vite build) -->
525
- <script type="module" src="/static/dist/components/code-tree.js"></script>
526
- <script type="module" src="/static/dist/components/code-viewer.js"></script>
513
+ <!-- Load bundled dashboard assets (built with Vite) - with timestamp cache busting -->
514
+ <script type="module">
515
+ // Add timestamp-based cache busting to all module imports
516
+ const timestamp = Date.now();
517
+ const modules = [
518
+ '/static/dist/dashboard.js',
519
+ '/static/dist/components/activity-tree.js',
520
+ '/static/dist/components/code-tree.js',
521
+ '/static/dist/components/code-viewer.js'
522
+ ];
523
+
524
+ // Dynamically import each module with cache busting
525
+ modules.forEach(modulePath => {
526
+ const script = document.createElement('script');
527
+ script.type = 'module';
528
+ script.src = `${modulePath}?t=${timestamp}`;
529
+ document.body.appendChild(script);
530
+ });
531
+ </script>
527
532
  </body>
528
533
  </html>
@@ -190,7 +190,17 @@ class AgentTemplateBuilder:
190
190
  # Include tools field only if agent is clearly restricted (missing core tools or very few tools)
191
191
  include_tools_field = not has_core_tools or len(agent_tools) < 6
192
192
 
193
- # Build YAML frontmatter using Claude Code's working format + our custom fields
193
+ # Build YAML frontmatter using Claude Code's minimal format
194
+ # ONLY include fields that Claude Code recognizes
195
+ #
196
+ # REMOVED FIELDS for Claude Code compatibility:
197
+ # - model, color, version, type, source, author
198
+ # These fields caused Claude Code to silently fail agent discovery
199
+ #
200
+ # CLAUDE CODE COMPATIBLE FORMAT:
201
+ # - name: kebab-case agent name (required)
202
+ # - description: when/why to use this agent (required)
203
+ # - tools: comma-separated tool list (optional, only if restricting)
194
204
  frontmatter_lines = [
195
205
  "---",
196
206
  f"name: {claude_code_name}",
@@ -203,12 +213,6 @@ class AgentTemplateBuilder:
203
213
 
204
214
  frontmatter_lines.extend(
205
215
  [
206
- f"model: {model_type}", # Use explicit model type instead of inherit
207
- f"color: {color}",
208
- f"version: {agent_version}",
209
- f"type: {agent_type}",
210
- f"source: {source_info}", # Track which source provided this agent
211
- "author: claude-mpm", # Mark as system-managed agent
212
216
  "---",
213
217
  "",
214
218
  ]