claude-mpm 4.2.39__py3-none-any.whl → 4.2.42__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 (39) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +114 -1
  3. claude_mpm/agents/BASE_OPS.md +156 -1
  4. claude_mpm/agents/INSTRUCTIONS.md +120 -11
  5. claude_mpm/agents/WORKFLOW.md +160 -10
  6. claude_mpm/agents/templates/agentic-coder-optimizer.json +17 -12
  7. claude_mpm/agents/templates/react_engineer.json +217 -0
  8. claude_mpm/agents/templates/web_qa.json +40 -4
  9. claude_mpm/cli/__init__.py +3 -5
  10. claude_mpm/commands/mpm-browser-monitor.md +370 -0
  11. claude_mpm/commands/mpm-monitor.md +177 -0
  12. claude_mpm/dashboard/static/built/components/code-viewer.js +1076 -2
  13. claude_mpm/dashboard/static/built/components/ui-state-manager.js +465 -2
  14. claude_mpm/dashboard/static/css/dashboard.css +2 -0
  15. claude_mpm/dashboard/static/js/browser-console-monitor.js +495 -0
  16. claude_mpm/dashboard/static/js/components/browser-log-viewer.js +763 -0
  17. claude_mpm/dashboard/static/js/components/code-viewer.js +931 -340
  18. claude_mpm/dashboard/static/js/components/diff-viewer.js +891 -0
  19. claude_mpm/dashboard/static/js/components/file-change-tracker.js +443 -0
  20. claude_mpm/dashboard/static/js/components/file-change-viewer.js +690 -0
  21. claude_mpm/dashboard/static/js/components/ui-state-manager.js +307 -19
  22. claude_mpm/dashboard/static/js/socket-client.js +2 -2
  23. claude_mpm/dashboard/static/test-browser-monitor.html +470 -0
  24. claude_mpm/dashboard/templates/index.html +62 -99
  25. claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
  26. claude_mpm/services/monitor/daemon.py +69 -36
  27. claude_mpm/services/monitor/daemon_manager.py +186 -29
  28. claude_mpm/services/monitor/handlers/browser.py +451 -0
  29. claude_mpm/services/monitor/server.py +272 -5
  30. {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.42.dist-info}/METADATA +1 -1
  31. {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.42.dist-info}/RECORD +35 -29
  32. claude_mpm/agents/templates/agentic-coder-optimizer.md +0 -44
  33. claude_mpm/agents/templates/agentic_coder_optimizer.json +0 -238
  34. claude_mpm/agents/templates/test-non-mpm.json +0 -20
  35. claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
  36. {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.42.dist-info}/WHEEL +0 -0
  37. {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.42.dist-info}/entry_points.txt +0 -0
  38. {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.42.dist-info}/licenses/LICENSE +0 -0
  39. {claude_mpm-4.2.39.dist-info → claude_mpm-4.2.42.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,9 @@
18
18
 
19
19
  <!-- D3.js for Activity Tree Visualization -->
20
20
  <script src="https://d3js.org/d3.v7.min.js"></script>
21
+
22
+ <!-- Font Awesome for icons -->
23
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
21
24
 
22
25
  <!-- Syntax Highlighting - Prism.js -->
23
26
  <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
@@ -239,18 +242,19 @@
239
242
 
240
243
  <!-- Right: Tabbed Content -->
241
244
  <div class="events-container">
242
- <!-- Tab Navigation -->
245
+ <!-- Tab Navigation - Using hash-based navigation -->
243
246
  <div class="tab-nav">
244
- <button class="tab-button active" data-tab="events">📊 Events</button>
245
- <button class="tab-button" data-tab="agents">🤖 Agents</button>
246
- <button class="tab-button" data-tab="tools">🔧 Tools</button>
247
- <button class="tab-button" data-tab="files">📁 Files</button>
248
- <button class="tab-button" data-tab="activity">🌳 Activity</button>
249
- <button class="tab-button" data-tab="code">🧬 Code</button>
247
+ <a href="#events" class="tab-button" data-tab="events">📊 Events</a>
248
+ <a href="#agents" class="tab-button" data-tab="agents">🤖 Agents</a>
249
+ <a href="#tools" class="tab-button" data-tab="tools">🔧 Tools</a>
250
+ <a href="#files" class="tab-button" data-tab="files">📁 Files</a>
251
+ <a href="#activity" class="tab-button" data-tab="activity">🌳 Activity</a>
252
+ <a href="#file_tree" class="tab-button active" data-tab="claude-tree">📝 File Tree</a>
253
+ <a href="#browser_logs" class="tab-button" data-tab="browser-logs">🌐 Browser Logs</a>
250
254
  </div>
251
255
 
252
256
  <!-- Events Tab -->
253
- <div class="tab-content active" id="events-tab">
257
+ <div class="tab-content" id="events-tab">
254
258
  <div class="tab-filters">
255
259
  <input type="text" id="events-search-input" placeholder="Search events...">
256
260
  <select id="events-type-filter">
@@ -385,76 +389,18 @@
385
389
  </div>
386
390
  </div>
387
391
 
388
- <!-- Code Tab -->
389
- <div class="tab-content" id="code-tab">
390
- <div class="code-container">
391
- <div class="code-split-container">
392
- <div id="code-tree-container" class="code-tree-container">
393
- <!-- Top-left corner: Language selector -->
394
- <div class="tree-corner-controls top-left">
395
- <div class="control-group">
396
- <label class="control-label">Languages:</label>
397
- <div class="checkbox-group">
398
- <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="python" checked> Python</label>
399
- <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="javascript" checked> JS</label>
400
- <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="typescript" checked> TS</label>
401
- </div>
402
- </div>
403
- </div>
404
-
405
- <!-- Top-right corner: Layout and search -->
406
- <div class="tree-corner-controls top-right">
407
- <div class="control-group">
408
- <select id="code-layout" class="select-compact">
409
- <option value="tree">Tree</option>
410
- <option value="radial">Radial</option>
411
- </select>
412
- <input type="text" id="code-search" placeholder="Search..." class="search-compact">
413
- </div>
414
- </div>
415
-
416
- <!-- Bottom-left corner: Stats and Status -->
417
- <div class="tree-corner-controls bottom-left">
418
- <div class="stats-display" id="code-stats">
419
- <span id="stats-files">0 files</span> •
420
- <span id="stats-classes">0 classes</span> •
421
- <span id="stats-functions">0 functions</span> •
422
- <span id="stats-methods">0 methods</span>
423
- </div>
424
- <div class="status-display" id="code-breadcrumb">
425
- <div class="breadcrumb-ticker" id="breadcrumb-ticker">
426
- <span id="breadcrumb-content">Ready to analyze...</span>
427
- </div>
428
- </div>
429
- </div>
430
-
431
- <!-- Bottom-right corner: Ignore patterns -->
432
- <div class="tree-corner-controls bottom-right">
433
- <div class="control-group">
434
- <label class="control-label">Ignore:</label>
435
- <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js, node_modules" class="input-compact">
436
- </div>
437
- </div>
438
- <div id="code-tree"></div>
439
- <!-- Collapsible legend -->
440
- <div class="tree-legend collapsed" id="tree-legend" style="display: none;">
441
- <button class="legend-close" onclick="document.getElementById('tree-legend').style.display='none'">✕</button>
442
- <div class="legend-content">
443
- <div class="legend-column">
444
- <div class="legend-item"><span class="legend-icon">📦</span> Module</div>
445
- <div class="legend-item"><span class="legend-icon">🏛️</span> Class</div>
446
- <div class="legend-item"><span class="legend-icon">⚡</span> Function</div>
447
- <div class="legend-item"><span class="legend-icon">🔧</span> Method</div>
448
- </div>
449
- <div class="legend-column">
450
- <div class="legend-item"><span class="legend-icon complexity-low">●</span> Low</div>
451
- <div class="legend-item"><span class="legend-icon complexity-medium">●</span> Med</div>
452
- <div class="legend-item"><span class="legend-icon complexity-high">●</span> High</div>
453
- </div>
454
- </div>
455
- </div>
456
- </div>
392
+ <!-- File Tree Tab -->
393
+ <div class="tab-content active" id="claude-tree-tab">
394
+ <div id="claude-tree-container" style="width: 100%; height: 100%;">
395
+ <!-- File activity tree will be rendered here by code-viewer.js -->
457
396
  </div>
397
+ </div>
398
+
399
+ <!-- Browser Logs Tab -->
400
+ <div class="tab-content" id="browser-logs-tab">
401
+ <div id="browser-logs-container" style="width: 100%; height: 100%;" data-component="browser-logs">
402
+ <!-- Browser logs will be rendered here by browser-log-viewer.js -->
403
+ <!-- This container is EXCLUSIVELY for browser console logs -->
458
404
  </div>
459
405
  </div>
460
406
 
@@ -463,26 +409,6 @@
463
409
  </div>
464
410
  </div>
465
411
 
466
- <!-- Code Viewer Modal -->
467
- <div id="code-viewer-modal" class="modal" style="display: none;">
468
- <div class="modal-content">
469
- <div class="modal-header">
470
- <h2 id="code-viewer-title">Code Viewer</h2>
471
- <button class="close" onclick="closeCodeViewer()">&times;</button>
472
- </div>
473
- <div class="modal-body">
474
- <pre><code id="code-viewer-content"></code></pre>
475
- </div>
476
- <div class="modal-footer">
477
- <div class="code-viewer-info">
478
- <span id="code-viewer-path"></span>
479
- <span id="code-viewer-lines"></span>
480
- <span id="code-viewer-language"></span>
481
- </div>
482
- <button onclick="closeCodeViewer()">Close</button>
483
- </div>
484
- </div>
485
- </div>
486
412
 
487
413
  <!-- Footer -->
488
414
  <div class="footer">
@@ -571,19 +497,56 @@
571
497
  .then(() => loadModule('/static/js/components/code-tree/tree-constants.js'))
572
498
  .then(() => loadModule('/static/js/components/code-tree/tree-search.js'))
573
499
  .then(() => loadModule('/static/js/components/code-tree/tree-breadcrumb.js'))
500
+ .then(() => {
501
+ // Load UI State Manager v2 from source with nuclear browser log handling
502
+ return loadModule('/static/js/components/ui-state-manager.js?v=2.0-NUCLEAR');
503
+ })
574
504
  .then(() => {
575
505
  // Now load main components in parallel
576
506
  return Promise.all([
577
507
  loadModule('/static/dist/dashboard.js'),
578
508
  loadModule('/static/dist/components/activity-tree.js'),
579
509
  loadModule('/static/js/components/code-tree.js'), // TEMPORARY: Direct source for debugging
580
- loadModule('/static/dist/components/code-viewer.js'),
581
- loadModule('/static/dist/components/file-viewer.js') // File viewer for viewing file contents
510
+ loadModule('/static/js/components/code-viewer.js'), // Code viewer now includes file change tracking
511
+ loadModule('/static/dist/components/file-viewer.js'), // File viewer for viewing file contents
512
+ loadModule('/static/js/components/browser-log-viewer.js?v=2.0-NUCLEAR') // Browser console log viewer v2.0 NUCLEAR
513
+ .then(() => {
514
+ // Initialize BrowserLogViewer v2.0 immediately after loading
515
+ if (typeof BrowserLogViewer !== 'undefined') {
516
+ const container = document.getElementById('browser-logs-container');
517
+ if (container && !window.browserLogViewer) {
518
+ console.error('[Main] Initializing BrowserLogViewer v2.0 NUCLEAR...');
519
+ window.browserLogViewer = new BrowserLogViewer(container);
520
+ console.error('[Main] BrowserLogViewer v2.0 NUCLEAR initialized successfully');
521
+ } else {
522
+ console.error('[Main] BrowserLogViewer v2.0 already initialized or container not found');
523
+ }
524
+ } else {
525
+ console.error('[Main] BrowserLogViewer class not found - script may not have loaded');
526
+ }
527
+ })
582
528
  ]);
583
529
  })
584
530
  .then(() => {
585
531
  console.log('All dashboard modules loaded successfully');
586
532
 
533
+ // CodeViewer will auto-initialize and handle tab switching internally
534
+
535
+ // Browser Log Viewer initialization is now handled by UIStateManager
536
+ // This prevents duplicate event handlers and tab selection conflicts
537
+
538
+ // Hash navigation will handle default tab based on URL
539
+ // If no hash, default will be 'events' as per hashToTab mapping
540
+ // To start with File Tree, we can set hash if not present
541
+ setTimeout(() => {
542
+ if (!window.location.hash) {
543
+ console.log('No hash present, setting default to File Tree tab...');
544
+ window.location.hash = '#file_tree';
545
+ } else {
546
+ console.log('Hash present:', window.location.hash);
547
+ }
548
+ }, 500);
549
+
587
550
  // Give modules time to execute and initialize dashboard
588
551
  setTimeout(() => {
589
552
  console.log('Checking dashboard initialization...');
@@ -133,7 +133,7 @@ class UnifiedDashboardManager(IUnifiedDashboardManager):
133
133
  if background:
134
134
  # The daemon.start() method will handle cleanup when force_restart=True
135
135
  # We don't need pre-emptive cleanup here as it causes race conditions
136
-
136
+
137
137
  # Try to start daemon with retry on port conflicts
138
138
  max_retries = 3
139
139
  retry_count = 0
@@ -157,35 +157,67 @@ class UnifiedMonitorDaemon:
157
157
  )
158
158
  return False
159
159
 
160
- # Wait for any pre-warming threads to complete before forking
161
- self._wait_for_prewarm_completion()
162
-
163
- # Use daemon manager's daemonize which includes cleanup
164
- # DO NOT reset startup_status_file - it's needed for parent-child communication!
165
- # self.daemon_manager.startup_status_file = None # BUG: This breaks communication
166
- success = self.daemon_manager.daemonize()
167
- if not success:
168
- return False
160
+ # Use subprocess approach for clean daemon startup (v4.2.40)
161
+ # This avoids all fork() + threading issues by starting in a fresh process
162
+ # The daemon_manager.use_subprocess_daemon() now checks for CLAUDE_MPM_SUBPROCESS_DAEMON
163
+ # environment variable to prevent infinite recursion
164
+ if self.daemon_manager.use_subprocess_daemon():
165
+ # Start using subprocess - this returns immediately in parent
166
+ success = self.daemon_manager.start_daemon_subprocess()
167
+ return success
168
+
169
+ # Check if we're in subprocess mode (environment variable set)
170
+ if os.environ.get("CLAUDE_MPM_SUBPROCESS_DAEMON") == "1":
171
+ # We're in a subprocess started by start_daemon_subprocess
172
+ # We need to write the PID file ourselves since parent didn't
173
+ self.logger.info("Running in subprocess daemon mode, writing PID file")
174
+ self.daemon_manager.write_pid_file()
175
+
176
+ # Setup signal handlers for graceful shutdown
177
+ self._setup_signal_handlers()
178
+
179
+ # Start the server (this will run until shutdown)
180
+ try:
181
+ result = self._run_server()
182
+ if not result:
183
+ self.logger.error("Failed to start server in subprocess mode")
184
+ return result
185
+ except Exception as e:
186
+ self.logger.error(f"Server startup exception in subprocess: {e}")
187
+ raise
188
+ else:
189
+ # Legacy fork approach (kept for compatibility but not used by default)
190
+ # Wait for any pre-warming threads to complete before forking
191
+ self._wait_for_prewarm_completion()
192
+
193
+ # Use daemon manager's daemonize which includes cleanup
194
+ # DO NOT reset startup_status_file - it's needed for parent-child communication!
195
+ # self.daemon_manager.startup_status_file = None # BUG: This breaks communication
196
+ success = self.daemon_manager.daemonize()
197
+ if not success:
198
+ return False
169
199
 
170
- # We're now in the daemon process
171
- # Update our PID references and status file
172
- self.lifecycle.pid_file = self.daemon_manager.pid_file
173
- self.lifecycle.startup_status_file = self.daemon_manager.startup_status_file
200
+ # We're now in the daemon process
201
+ # Update our PID references and status file
202
+ self.lifecycle.pid_file = self.daemon_manager.pid_file
203
+ self.lifecycle.startup_status_file = self.daemon_manager.startup_status_file
174
204
 
175
- # Start the server in daemon mode
176
- try:
177
- result = self._run_server()
178
- if not result:
179
- # Report failure before exiting
180
- self.daemon_manager._report_startup_error("Failed to start server")
181
- else:
182
- # Report success
183
- self.daemon_manager._report_startup_success()
184
- return result
185
- except Exception as e:
186
- # Report any exceptions during startup
187
- self.daemon_manager._report_startup_error(f"Server startup exception: {e}")
188
- raise
205
+ # Start the server in daemon mode
206
+ try:
207
+ result = self._run_server()
208
+ if not result:
209
+ # Report failure before exiting
210
+ self.daemon_manager._report_startup_error("Failed to start server")
211
+ else:
212
+ # Report success
213
+ self.daemon_manager._report_startup_success()
214
+ return result
215
+ except Exception as e:
216
+ # Report any exceptions during startup
217
+ self.daemon_manager._report_startup_error(
218
+ f"Server startup exception: {e}"
219
+ )
220
+ raise
189
221
 
190
222
  def _start_foreground(self, force_restart: bool = False) -> bool:
191
223
  """Start in foreground mode.
@@ -602,29 +634,30 @@ class UnifiedMonitorDaemon:
602
634
  self.logger.info(
603
635
  f"Waiting for {len(active_threads)} background threads to complete before forking"
604
636
  )
605
-
637
+
606
638
  # List thread names for debugging
607
639
  thread_names = [t.name for t in active_threads]
608
640
  self.logger.debug(f"Active threads: {thread_names}")
609
641
 
610
642
  # Wait for threads to complete or timeout
611
643
  while time.time() - start_time < timeout:
612
- remaining_threads = [
613
- t for t in active_threads if t.is_alive()
614
- ]
644
+ remaining_threads = [t for t in active_threads if t.is_alive()]
615
645
  if not remaining_threads:
616
646
  self.logger.debug("All threads completed")
617
647
  break
618
-
648
+
619
649
  # Log remaining threads periodically
620
650
  if int(time.time() - start_time) % 1 == 0:
621
- self.logger.debug(f"{len(remaining_threads)} threads still active")
622
-
651
+ self.logger.debug(
652
+ f"{len(remaining_threads)} threads still active"
653
+ )
654
+
623
655
  time.sleep(0.1)
624
-
656
+
625
657
  # Final check
626
658
  final_threads = [
627
- t for t in threading.enumerate()
659
+ t
660
+ for t in threading.enumerate()
628
661
  if t.is_alive() and t != threading.current_thread()
629
662
  ]
630
663
  if final_threads: