claude-mpm 4.2.1__py3-none-any.whl → 4.2.3__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 (57) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/templates/agent-manager.json +1 -1
  3. claude_mpm/agents/templates/agentic_coder_optimizer.json +1 -1
  4. claude_mpm/agents/templates/api_qa.json +1 -1
  5. claude_mpm/agents/templates/code_analyzer.json +1 -1
  6. claude_mpm/agents/templates/data_engineer.json +1 -1
  7. claude_mpm/agents/templates/documentation.json +1 -1
  8. claude_mpm/agents/templates/engineer.json +2 -2
  9. claude_mpm/agents/templates/gcp_ops_agent.json +14 -9
  10. claude_mpm/agents/templates/imagemagick.json +1 -1
  11. claude_mpm/agents/templates/memory_manager.json +1 -1
  12. claude_mpm/agents/templates/ops.json +1 -1
  13. claude_mpm/agents/templates/project_organizer.json +1 -1
  14. claude_mpm/agents/templates/qa.json +2 -2
  15. claude_mpm/agents/templates/refactoring_engineer.json +1 -1
  16. claude_mpm/agents/templates/research.json +3 -3
  17. claude_mpm/agents/templates/security.json +1 -1
  18. claude_mpm/agents/templates/test-non-mpm.json +20 -0
  19. claude_mpm/agents/templates/ticketing.json +1 -1
  20. claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
  21. claude_mpm/agents/templates/version_control.json +1 -1
  22. claude_mpm/agents/templates/web_qa.json +3 -8
  23. claude_mpm/agents/templates/web_ui.json +1 -1
  24. claude_mpm/cli/commands/agents.py +3 -0
  25. claude_mpm/cli/commands/dashboard.py +3 -3
  26. claude_mpm/cli/commands/monitor.py +227 -64
  27. claude_mpm/core/config.py +25 -0
  28. claude_mpm/core/unified_agent_registry.py +2 -2
  29. claude_mpm/dashboard/static/css/code-tree.css +220 -1
  30. claude_mpm/dashboard/static/css/dashboard.css +286 -0
  31. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  32. claude_mpm/dashboard/static/js/components/code-simple.js +507 -15
  33. claude_mpm/dashboard/static/js/components/code-tree.js +2044 -124
  34. claude_mpm/dashboard/static/js/socket-client.js +5 -2
  35. claude_mpm/dashboard/templates/code_simple.html +79 -0
  36. claude_mpm/dashboard/templates/index.html +42 -41
  37. claude_mpm/services/agents/deployment/agent_deployment.py +4 -1
  38. claude_mpm/services/agents/deployment/agent_discovery_service.py +101 -2
  39. claude_mpm/services/agents/deployment/agent_format_converter.py +53 -9
  40. claude_mpm/services/agents/deployment/agent_template_builder.py +355 -25
  41. claude_mpm/services/agents/deployment/agent_validator.py +11 -6
  42. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +83 -15
  43. claude_mpm/services/agents/deployment/validation/template_validator.py +51 -40
  44. claude_mpm/services/cli/agent_listing_service.py +2 -2
  45. claude_mpm/services/dashboard/stable_server.py +389 -0
  46. claude_mpm/services/socketio/client_proxy.py +16 -0
  47. claude_mpm/services/socketio/dashboard_server.py +360 -0
  48. claude_mpm/services/socketio/handlers/code_analysis.py +27 -5
  49. claude_mpm/services/socketio/monitor_client.py +366 -0
  50. claude_mpm/services/socketio/monitor_server.py +505 -0
  51. claude_mpm/tools/code_tree_analyzer.py +95 -17
  52. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/METADATA +1 -1
  53. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/RECORD +57 -52
  54. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/WHEEL +0 -0
  55. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/entry_points.txt +0 -0
  56. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/licenses/LICENSE +0 -0
  57. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/top_level.txt +0 -0
@@ -20,6 +20,15 @@ function goUp() {
20
20
  }
21
21
  }
22
22
 
23
+ function analyzeFileFromPath(filePath) {
24
+ console.log('[analyzeFileFromPath] Called with path:', filePath);
25
+ if (window.simpleCodeView) {
26
+ window.simpleCodeView.analyzeFileFromPath(filePath);
27
+ } else {
28
+ console.error('[analyzeFileFromPath] simpleCodeView not initialized');
29
+ }
30
+ }
31
+
23
32
  class SimpleCodeView {
24
33
  constructor() {
25
34
  console.log('[SimpleCodeView] Constructor called');
@@ -27,6 +36,17 @@ class SimpleCodeView {
27
36
  this.container = null;
28
37
  this.apiBase = window.location.origin;
29
38
  console.log('[SimpleCodeView] API base:', this.apiBase);
39
+
40
+ // Tree view properties
41
+ this.currentView = 'directory';
42
+ this.socket = null;
43
+ this.svg = null;
44
+ this.treeGroup = null;
45
+ this.treeLayout = null;
46
+ this.treeData = null;
47
+ this.width = 800;
48
+ this.height = 600;
49
+ this.margin = {top: 20, right: 20, bottom: 20, left: 120};
30
50
  }
31
51
 
32
52
  init(container) {
@@ -53,7 +73,18 @@ class SimpleCodeView {
53
73
 
54
74
  const html = `
55
75
  <div class="simple-code-view" style="padding: 20px;">
56
- <h2>Simple Directory Browser</h2>
76
+ <h2>Simple Code Browser</h2>
77
+
78
+ <div class="view-toggle" style="margin: 10px 0; padding: 10px; background: #f0f0f0; border-radius: 4px;">
79
+ <button id="dir-view-btn" onclick="window.simpleCodeView.setView('directory')" class="active"
80
+ style="margin-right: 10px; padding: 8px 16px; border: 1px solid #ccc; background: #007cba; color: white; border-radius: 4px; cursor: pointer;">
81
+ 📁 Directory View
82
+ </button>
83
+ <button id="tree-view-btn" onclick="window.simpleCodeView.setView('tree')"
84
+ style="padding: 8px 16px; border: 1px solid #ccc; background: #f9f9f9; color: #333; border-radius: 4px; cursor: pointer;">
85
+ 🌳 Tree View
86
+ </button>
87
+ </div>
57
88
 
58
89
  <div id="status-bar" style="padding: 10px; background: #e0e0e0; border-radius: 4px; margin-bottom: 10px;">
59
90
  Status: Initializing...
@@ -73,6 +104,20 @@ class SimpleCodeView {
73
104
  <div style="color: #666;">Waiting to load directory...</div>
74
105
  </div>
75
106
 
107
+ <div id="tree-view-container" style="display: none;">
108
+ <div class="file-selector" style="margin: 10px 0; padding: 10px; background: #f9f9f9; border-radius: 4px;">
109
+ <input type="text" id="file-path-input" placeholder="Enter file path to analyze (e.g., ./src/claude_mpm/core/framework_loader.py)"
110
+ style="width: 70%; padding: 8px; margin-right: 10px; border: 1px solid #ccc; border-radius: 4px;">
111
+ <button onclick="window.simpleCodeView.analyzeFile()"
112
+ style="padding: 8px 16px; border: 1px solid #ccc; background: #28a745; color: white; border-radius: 4px; cursor: pointer;">
113
+ Analyze File
114
+ </button>
115
+ </div>
116
+ <div id="tree-visualization" style="border: 1px solid #ccc; min-height: 500px; background: white; overflow: auto; position: relative;">
117
+ <div style="padding: 20px; text-align: center; color: #666;">Enter a file path above and click "Analyze File" to view AST tree</div>
118
+ </div>
119
+ </div>
120
+
76
121
  <div id="debug-info" style="margin-top: 10px; padding: 10px; background: #f9f9f9; font-family: monospace; font-size: 12px;">
77
122
  <strong>Debug Info:</strong><br>
78
123
  API Base: ${this.apiBase}<br>
@@ -225,27 +270,31 @@ class SimpleCodeView {
225
270
  });
226
271
 
227
272
  for (const item of sorted) {
228
- let icon = item.is_directory ? '📁' : '📄';
229
- let nameStyle = 'color: #666;';
230
-
231
- // Special styling for code files
232
- if (!item.is_directory && item.is_code_file) {
233
- icon = '💻'; // Code file icon
234
- nameStyle = 'color: #2e7d32; font-weight: 500;'; // Green color for code files
235
- }
236
-
237
273
  if (item.is_directory) {
238
274
  // Make directories clickable
239
275
  html += `<li style="padding: 5px 0;">
240
- ${icon} <a href="#" onclick="loadDir('${item.path.replace(/'/g, "\\'")}'); return false;" style="color: blue; text-decoration: none; cursor: pointer;">
276
+ 📁 <a href="#" onclick="loadDir('${item.path.replace(/'/g, "\\'")}'); return false;" style="color: blue; text-decoration: none; cursor: pointer;">
241
277
  ${item.name}/
242
278
  </a>
243
279
  </li>`;
244
280
  } else {
245
- // Files are not clickable
246
- html += `<li style="padding: 5px 0;">
247
- ${icon} <span style="${nameStyle}">${item.name}</span>
248
- </li>`;
281
+ // Check if it's a code file and make it clickable
282
+ const isCodeFile = this.isCodeFile(item.name);
283
+ const fileIcon = this.getFileIcon(item.name);
284
+
285
+ if (isCodeFile) {
286
+ // Code files - clickable to show AST
287
+ html += `<li style="padding: 5px 0;">
288
+ ${fileIcon} <a href="#" onclick="analyzeFileFromPath('${item.path.replace(/'/g, "\\'")}'); return false;" style="color: #0066cc; text-decoration: none; cursor: pointer; font-weight: 500;" title="Click to view AST">
289
+ ${item.name}
290
+ </a>
291
+ </li>`;
292
+ } else {
293
+ // Non-code files - not clickable
294
+ html += `<li style="padding: 5px 0;">
295
+ 📄 <span style="color: #666;">${item.name}</span>
296
+ </li>`;
297
+ }
249
298
  }
250
299
  }
251
300
 
@@ -289,6 +338,449 @@ class SimpleCodeView {
289
338
  console.log('[SimpleCodeView.goUp] Going up to:', parent);
290
339
  this.loadDirectory(parent);
291
340
  }
341
+
342
+ // Tree view methods
343
+ setView(view) {
344
+ console.log('[SimpleCodeView.setView] Switching to view:', view);
345
+ this.currentView = view;
346
+
347
+ const dirContents = document.getElementById('directory-contents');
348
+ const treeContainer = document.getElementById('tree-view-container');
349
+ const dirBtn = document.getElementById('dir-view-btn');
350
+ const treeBtn = document.getElementById('tree-view-btn');
351
+
352
+ if (view === 'tree') {
353
+ dirContents.style.display = 'none';
354
+ treeContainer.style.display = 'block';
355
+
356
+ // Update button styles
357
+ dirBtn.style.background = '#f9f9f9';
358
+ dirBtn.style.color = '#333';
359
+ dirBtn.classList.remove('active');
360
+
361
+ treeBtn.style.background = '#007cba';
362
+ treeBtn.style.color = 'white';
363
+ treeBtn.classList.add('active');
364
+
365
+ this.initializeTreeView();
366
+ } else {
367
+ dirContents.style.display = 'block';
368
+ treeContainer.style.display = 'none';
369
+
370
+ // Update button styles
371
+ treeBtn.style.background = '#f9f9f9';
372
+ treeBtn.style.color = '#333';
373
+ treeBtn.classList.remove('active');
374
+
375
+ dirBtn.style.background = '#007cba';
376
+ dirBtn.style.color = 'white';
377
+ dirBtn.classList.add('active');
378
+ }
379
+ }
380
+
381
+ initializeTreeView() {
382
+ console.log('[SimpleCodeView.initializeTreeView] Initializing tree view');
383
+
384
+ if (!window.d3) {
385
+ this.updateStatus('D3.js not loaded - cannot initialize tree view', 'red');
386
+ return;
387
+ }
388
+
389
+ this.initializeSocket();
390
+ }
391
+
392
+ initializeSocket() {
393
+ console.log('[SimpleCodeView.initializeSocket] Initializing Socket.IO connection');
394
+
395
+ if (!window.io) {
396
+ this.updateStatus('Socket.IO not loaded - using fallback mode', 'orange');
397
+ return;
398
+ }
399
+
400
+ try {
401
+ this.socket = io('/');
402
+
403
+ this.socket.on('connect', () => {
404
+ console.log('[SimpleCodeView] Socket connected');
405
+ this.updateStatus('Connected to analysis server', 'green');
406
+ });
407
+
408
+ this.socket.on('disconnect', () => {
409
+ console.log('[SimpleCodeView] Socket disconnected');
410
+ this.updateStatus('Disconnected from analysis server', 'orange');
411
+ });
412
+
413
+ this.socket.on('analysis_progress', (data) => {
414
+ console.log('[SimpleCodeView] Analysis progress:', data);
415
+ this.updateStatus(`Analysis: ${data.message}`, 'blue');
416
+ });
417
+
418
+ this.socket.on('analysis_complete', (data) => {
419
+ console.log('[SimpleCodeView] Analysis complete:', data);
420
+ this.updateStatus('Analysis complete - rendering tree', 'green');
421
+ this.renderTree(data);
422
+ });
423
+
424
+ this.socket.on('analysis_error', (error) => {
425
+ console.error('[SimpleCodeView] Analysis error:', error);
426
+ this.showError(`Analysis failed: ${error.message || error}`);
427
+ });
428
+
429
+ } catch (error) {
430
+ console.error('[SimpleCodeView] Failed to initialize socket:', error);
431
+ this.updateStatus('Socket connection failed - using fallback mode', 'orange');
432
+ }
433
+ }
434
+
435
+ async analyzeFile() {
436
+ console.log('[SimpleCodeView.analyzeFile] Starting file analysis');
437
+
438
+ const fileInput = document.getElementById('file-path-input');
439
+ const filePath = fileInput.value.trim();
440
+
441
+ if (!filePath) {
442
+ this.showError('Please enter a file path');
443
+ return;
444
+ }
445
+
446
+ this.hideError();
447
+ this.updateStatus(`Analyzing file: ${filePath}`, 'blue');
448
+
449
+ // Clear previous tree
450
+ const treeViz = document.getElementById('tree-visualization');
451
+ if (treeViz) {
452
+ treeViz.innerHTML = '<div style="padding: 20px; text-align: center; color: #666;">Analyzing file...</div>';
453
+ }
454
+
455
+ try {
456
+ if (this.socket && this.socket.connected) {
457
+ // Use Socket.IO for real-time analysis
458
+ this.socket.emit('analyze_file', {
459
+ path: filePath,
460
+ working_directory: this.currentPath
461
+ });
462
+ } else {
463
+ // Fallback to HTTP API (we'll create a simple endpoint)
464
+ await this.analyzeFileHTTP(filePath);
465
+ }
466
+ } catch (error) {
467
+ console.error('[SimpleCodeView.analyzeFile] Error:', error);
468
+ this.showError(`Failed to analyze file: ${error.message}`);
469
+ }
470
+ }
471
+
472
+ async analyzeFileHTTP(filePath) {
473
+ console.log('[SimpleCodeView.analyzeFileHTTP] Using HTTP fallback for:', filePath);
474
+
475
+ try {
476
+ // Create a simple mock analysis for demonstration
477
+ // In a real implementation, this would call a proper analysis endpoint
478
+ setTimeout(() => {
479
+ const mockData = this.createMockTreeData(filePath);
480
+ this.renderTree(mockData);
481
+ }, 1000);
482
+
483
+ } catch (error) {
484
+ throw new Error(`HTTP analysis failed: ${error.message}`);
485
+ }
486
+ }
487
+
488
+ createMockTreeData(filePath) {
489
+ // Create mock AST data for demonstration
490
+ const fileName = filePath.split('/').pop() || 'file';
491
+ const ext = fileName.split('.').pop()?.toLowerCase();
492
+
493
+ let mockData = {
494
+ name: fileName,
495
+ type: 'module',
496
+ children: []
497
+ };
498
+
499
+ if (ext === 'py') {
500
+ mockData.children = [
501
+ {
502
+ name: 'imports',
503
+ type: 'imports',
504
+ children: [
505
+ { name: 'import os', type: 'import' },
506
+ { name: 'from pathlib import Path', type: 'import' }
507
+ ]
508
+ },
509
+ {
510
+ name: 'MyClass',
511
+ type: 'class',
512
+ children: [
513
+ { name: '__init__', type: 'method' },
514
+ { name: 'process_data', type: 'method' },
515
+ { name: 'save_results', type: 'method' }
516
+ ]
517
+ },
518
+ {
519
+ name: 'helper_function',
520
+ type: 'function',
521
+ children: []
522
+ }
523
+ ];
524
+ } else if (ext === 'js' || ext === 'ts') {
525
+ mockData.children = [
526
+ {
527
+ name: 'imports',
528
+ type: 'imports',
529
+ children: [
530
+ { name: "import React from 'react'", type: 'import' },
531
+ { name: "import { useState } from 'react'", type: 'import' }
532
+ ]
533
+ },
534
+ {
535
+ name: 'MyComponent',
536
+ type: 'function',
537
+ children: [
538
+ { name: 'useState', type: 'hook' },
539
+ { name: 'useEffect', type: 'hook' },
540
+ { name: 'handleClick', type: 'function' }
541
+ ]
542
+ }
543
+ ];
544
+ } else {
545
+ mockData.children = [
546
+ { name: 'Content Section 1', type: 'section' },
547
+ { name: 'Content Section 2', type: 'section' },
548
+ { name: 'Content Section 3', type: 'section' }
549
+ ];
550
+ }
551
+
552
+ return mockData;
553
+ }
554
+
555
+ renderTree(data) {
556
+ console.log('[SimpleCodeView.renderTree] Rendering tree with data:', data);
557
+
558
+ if (!data || !window.d3) {
559
+ this.showError('Cannot render tree: missing data or D3.js');
560
+ return;
561
+ }
562
+
563
+ this.treeData = data;
564
+
565
+ // Clear previous visualization
566
+ const container = document.getElementById('tree-visualization');
567
+ if (!container) {
568
+ this.showError('Tree visualization container not found');
569
+ return;
570
+ }
571
+
572
+ container.innerHTML = '';
573
+
574
+ // Create SVG
575
+ const margin = this.margin;
576
+ const width = this.width - margin.left - margin.right;
577
+ const height = this.height - margin.top - margin.bottom;
578
+
579
+ this.svg = d3.select('#tree-visualization')
580
+ .append('svg')
581
+ .attr('width', this.width)
582
+ .attr('height', this.height);
583
+
584
+ // Add zoom behavior
585
+ const zoom = d3.zoom()
586
+ .scaleExtent([0.1, 4])
587
+ .on('zoom', (event) => {
588
+ this.treeGroup.attr('transform', event.transform);
589
+ });
590
+
591
+ this.svg.call(zoom);
592
+
593
+ // Create main group
594
+ this.treeGroup = this.svg.append('g')
595
+ .attr('transform', `translate(${margin.left},${margin.top})`);
596
+
597
+ // Create tree layout
598
+ this.treeLayout = d3.tree()
599
+ .size([height, width]);
600
+
601
+ // Convert data to hierarchy
602
+ const hierarchy = d3.hierarchy(data);
603
+
604
+ // Generate tree layout
605
+ const treeData = this.treeLayout(hierarchy);
606
+
607
+ // Add links
608
+ const links = this.treeGroup.selectAll('.link')
609
+ .data(treeData.links())
610
+ .enter()
611
+ .append('path')
612
+ .attr('class', 'link')
613
+ .attr('d', d3.linkHorizontal()
614
+ .x(d => d.y)
615
+ .y(d => d.x)
616
+ )
617
+ .style('fill', 'none')
618
+ .style('stroke', '#ccc')
619
+ .style('stroke-width', '2px');
620
+
621
+ // Add nodes
622
+ const nodes = this.treeGroup.selectAll('.node')
623
+ .data(treeData.descendants())
624
+ .enter()
625
+ .append('g')
626
+ .attr('class', 'tree-node')
627
+ .attr('transform', d => `translate(${d.y},${d.x})`);
628
+
629
+ // Add circles for nodes
630
+ nodes.append('circle')
631
+ .attr('r', 6)
632
+ .style('fill', d => this.getNodeColor(d.data.type))
633
+ .style('stroke', '#333')
634
+ .style('stroke-width', '2px');
635
+
636
+ // Add labels
637
+ nodes.append('text')
638
+ .attr('dy', '.35em')
639
+ .attr('x', d => d.children ? -13 : 13)
640
+ .style('text-anchor', d => d.children ? 'end' : 'start')
641
+ .style('font-size', '12px')
642
+ .style('font-family', 'Arial, sans-serif')
643
+ .text(d => d.data.name);
644
+
645
+ // Add tooltips
646
+ nodes.append('title')
647
+ .text(d => `${d.data.type}: ${d.data.name}`);
648
+
649
+ // Add legend
650
+ this.addLegend(container);
651
+
652
+ this.updateStatus('Tree visualization rendered successfully', 'green');
653
+ }
654
+
655
+ getNodeColor(type) {
656
+ const colors = {
657
+ 'module': '#1f77b4',
658
+ 'class': '#ff7f0e',
659
+ 'function': '#2ca02c',
660
+ 'method': '#d62728',
661
+ 'import': '#9467bd',
662
+ 'imports': '#8c564b',
663
+ 'section': '#e377c2',
664
+ 'hook': '#7f7f7f'
665
+ };
666
+ return colors[type] || '#bcbd22';
667
+ }
668
+
669
+ // Check if file is a code file
670
+ isCodeFile(filename) {
671
+ const codeExtensions = [
672
+ '.py', '.js', '.jsx', '.ts', '.tsx', '.java', '.cpp', '.c', '.h',
673
+ '.cs', '.go', '.rs', '.rb', '.php', '.swift', '.kt', '.scala',
674
+ '.r', '.m', '.mm', '.sh', '.bash', '.zsh', '.sql', '.html',
675
+ '.css', '.scss', '.sass', '.less', '.xml', '.json', '.yaml', '.yml',
676
+ '.md', '.rst', '.txt', '.log', '.conf', '.ini', '.toml'
677
+ ];
678
+ const ext = filename.toLowerCase().substring(filename.lastIndexOf('.'));
679
+ return codeExtensions.includes(ext);
680
+ }
681
+
682
+ // Get appropriate icon for file type
683
+ getFileIcon(filename) {
684
+ const ext = filename.toLowerCase().substring(filename.lastIndexOf('.'));
685
+ const iconMap = {
686
+ '.py': '🐍',
687
+ '.js': '📜',
688
+ '.jsx': '⚛️',
689
+ '.ts': '📘',
690
+ '.tsx': '⚛️',
691
+ '.json': '📋',
692
+ '.html': '🌐',
693
+ '.css': '🎨',
694
+ '.md': '📝',
695
+ '.yml': '⚙️',
696
+ '.yaml': '⚙️',
697
+ '.sh': '🔧',
698
+ '.go': '🐹',
699
+ '.rs': '🦀',
700
+ '.java': '☕',
701
+ '.rb': '💎',
702
+ '.php': '🐘',
703
+ '.cpp': '⚙️',
704
+ '.c': '⚙️',
705
+ '.h': '📄',
706
+ '.cs': '💜',
707
+ '.swift': '🦉',
708
+ '.kt': '🚀',
709
+ '.scala': '📈',
710
+ '.r': '📊',
711
+ '.sql': '🗃️',
712
+ '.scss': '🎨',
713
+ '.sass': '🎨',
714
+ '.less': '🎨',
715
+ '.xml': '📑',
716
+ '.bash': '🔧',
717
+ '.zsh': '🔧',
718
+ '.md': '📝',
719
+ '.rst': '📝',
720
+ '.txt': '📄',
721
+ '.log': '📋',
722
+ '.conf': '⚙️',
723
+ '.ini': '⚙️',
724
+ '.toml': '⚙️'
725
+ };
726
+ return iconMap[ext] || '💻';
727
+ }
728
+
729
+ // New method to analyze file from directory click
730
+ analyzeFileFromPath(filePath) {
731
+ console.log('[SimpleCodeView.analyzeFileFromPath] Analyzing file:', filePath);
732
+
733
+ // Switch to tree view
734
+ this.setView('tree');
735
+
736
+ // Set the file path in the input
737
+ const fileInput = document.getElementById('file-path-input');
738
+ if (fileInput) {
739
+ fileInput.value = filePath;
740
+ }
741
+
742
+ // Trigger analysis
743
+ this.analyzeFile();
744
+ }
745
+
746
+ addLegend(container) {
747
+ // Remove existing legend
748
+ const existingLegend = container.querySelector('.tree-legend');
749
+ if (existingLegend) {
750
+ existingLegend.remove();
751
+ }
752
+
753
+ const legend = document.createElement('div');
754
+ legend.className = 'tree-legend';
755
+
756
+ const legendTypes = [
757
+ { type: 'module', label: 'Module/File' },
758
+ { type: 'class', label: 'Class' },
759
+ { type: 'function', label: 'Function' },
760
+ { type: 'method', label: 'Method' },
761
+ { type: 'imports', label: 'Imports' },
762
+ { type: 'import', label: 'Import' },
763
+ { type: 'section', label: 'Section' },
764
+ { type: 'hook', label: 'Hook/Other' }
765
+ ];
766
+
767
+ legend.innerHTML = `
768
+ <strong>Legend</strong><br>
769
+ ${legendTypes.map(item => `
770
+ <div class="tree-legend-item">
771
+ <div class="tree-legend-color" style="background-color: ${this.getNodeColor(item.type)};"></div>
772
+ ${item.label}
773
+ </div>
774
+ `).join('')}
775
+ <hr style="margin: 8px 0;">
776
+ <div style="font-size: 11px; color: #666;">
777
+ Zoom: Mouse wheel<br>
778
+ Pan: Click and drag
779
+ </div>
780
+ `;
781
+
782
+ container.appendChild(legend);
783
+ }
292
784
  }
293
785
 
294
786
  // Create global instance