claude-mpm 3.5.4__py3-none-any.whl → 3.6.0__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 (68) hide show
  1. claude_mpm/.claude-mpm/logs/hooks_20250728.log +10 -0
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +96 -23
  4. claude_mpm/agents/BASE_PM.md +273 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +114 -102
  6. claude_mpm/agents/agent-template.yaml +83 -0
  7. claude_mpm/agents/agent_loader.py +36 -1
  8. claude_mpm/agents/async_agent_loader.py +421 -0
  9. claude_mpm/agents/templates/code_analyzer.json +81 -0
  10. claude_mpm/agents/templates/data_engineer.json +18 -3
  11. claude_mpm/agents/templates/documentation.json +18 -3
  12. claude_mpm/agents/templates/engineer.json +19 -4
  13. claude_mpm/agents/templates/ops.json +18 -3
  14. claude_mpm/agents/templates/qa.json +20 -4
  15. claude_mpm/agents/templates/research.json +20 -4
  16. claude_mpm/agents/templates/security.json +18 -3
  17. claude_mpm/agents/templates/version_control.json +16 -3
  18. claude_mpm/cli/README.md +108 -0
  19. claude_mpm/cli/__init__.py +5 -1
  20. claude_mpm/cli/commands/__init__.py +5 -1
  21. claude_mpm/cli/commands/agents.py +233 -6
  22. claude_mpm/cli/commands/aggregate.py +462 -0
  23. claude_mpm/cli/commands/config.py +277 -0
  24. claude_mpm/cli/commands/run.py +228 -47
  25. claude_mpm/cli/parser.py +176 -1
  26. claude_mpm/cli/utils.py +9 -1
  27. claude_mpm/cli_module/refactoring_guide.md +253 -0
  28. claude_mpm/config/async_logging_config.yaml +145 -0
  29. claude_mpm/constants.py +19 -0
  30. claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +34 -0
  31. claude_mpm/core/claude_runner.py +413 -76
  32. claude_mpm/core/config.py +161 -4
  33. claude_mpm/core/config_paths.py +0 -1
  34. claude_mpm/core/factories.py +9 -3
  35. claude_mpm/core/framework_loader.py +81 -0
  36. claude_mpm/dashboard/.claude-mpm/memories/README.md +36 -0
  37. claude_mpm/dashboard/README.md +121 -0
  38. claude_mpm/dashboard/static/js/dashboard.js.backup +1973 -0
  39. claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +36 -0
  40. claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +39 -0
  41. claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +38 -0
  42. claude_mpm/hooks/README.md +96 -0
  43. claude_mpm/hooks/claude_hooks/hook_handler.py +391 -9
  44. claude_mpm/init.py +123 -18
  45. claude_mpm/models/agent_session.py +511 -0
  46. claude_mpm/schemas/agent_schema.json +435 -0
  47. claude_mpm/scripts/__init__.py +15 -0
  48. claude_mpm/scripts/start_activity_logging.py +86 -0
  49. claude_mpm/services/agents/deployment/agent_deployment.py +326 -24
  50. claude_mpm/services/agents/deployment/async_agent_deployment.py +461 -0
  51. claude_mpm/services/agents/management/agent_management_service.py +2 -1
  52. claude_mpm/services/event_aggregator.py +547 -0
  53. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  54. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +3 -3
  55. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +2 -2
  56. claude_mpm/services/version_control/VERSION +1 -0
  57. claude_mpm/utils/agent_dependency_loader.py +655 -0
  58. claude_mpm/utils/console.py +11 -0
  59. claude_mpm/utils/dependency_cache.py +376 -0
  60. claude_mpm/utils/dependency_strategies.py +343 -0
  61. claude_mpm/utils/environment_context.py +310 -0
  62. {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/METADATA +87 -1
  63. {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/RECORD +67 -37
  64. claude_mpm/agents/templates/pm.json +0 -122
  65. {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/WHEEL +0 -0
  66. {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/entry_points.txt +0 -0
  67. {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/licenses/LICENSE +0 -0
  68. {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/top_level.txt +0 -0
claude_mpm/core/config.py CHANGED
@@ -7,8 +7,10 @@ and default values with proper validation and type conversion.
7
7
 
8
8
  import os
9
9
  from pathlib import Path
10
- from typing import Any, Dict, Optional, Union
10
+ from typing import Any, Dict, Optional, Union, List, Tuple
11
11
  import logging
12
+ import yaml
13
+ import json
12
14
 
13
15
  from ..utils.config_manager import ConfigurationManager
14
16
  from .config_paths import ConfigPaths
@@ -49,14 +51,25 @@ class Config:
49
51
  if config:
50
52
  self._config.update(config)
51
53
 
54
+ # Track where configuration was loaded from
55
+ self._loaded_from = None
56
+
52
57
  # Load from file if provided
53
58
  if config_file:
54
59
  self.load_file(config_file)
60
+ self._loaded_from = str(config_file)
55
61
  else:
56
62
  # Try to load from standard location: .claude-mpm/configuration.yaml
57
63
  default_config = Path.cwd() / ".claude-mpm" / "configuration.yaml"
58
64
  if default_config.exists():
59
65
  self.load_file(default_config)
66
+ self._loaded_from = str(default_config)
67
+ else:
68
+ # Also try .yml extension
69
+ alt_config = Path.cwd() / ".claude-mpm" / "configuration.yml"
70
+ if alt_config.exists():
71
+ self.load_file(alt_config)
72
+ self._loaded_from = str(alt_config)
60
73
 
61
74
  # Load from environment variables (new and legacy prefixes)
62
75
  self._load_env_vars()
@@ -66,21 +79,64 @@ class Config:
66
79
  self._apply_defaults()
67
80
 
68
81
  def load_file(self, file_path: Union[str, Path]) -> None:
69
- """Load configuration from file."""
82
+ """Load configuration from file with enhanced error handling.
83
+
84
+ WHY: Configuration loading failures can cause silent issues. We need
85
+ to provide clear, actionable error messages to help users fix problems.
86
+ """
70
87
  file_path = Path(file_path)
71
88
 
72
89
  if not file_path.exists():
73
90
  logger.warning(f"Configuration file not found: {file_path}")
91
+ logger.info(f"TIP: Create a configuration file with: mkdir -p {file_path.parent} && touch {file_path}")
74
92
  return
75
93
 
76
94
  try:
95
+ # Check if file is readable
96
+ if not os.access(file_path, os.R_OK):
97
+ logger.error(f"Configuration file is not readable: {file_path}")
98
+ logger.info(f"TIP: Fix permissions with: chmod 644 {file_path}")
99
+ return
100
+
101
+ # Check file size (warn if too large)
102
+ file_size = file_path.stat().st_size
103
+ if file_size > 1024 * 1024: # 1MB
104
+ logger.warning(f"Configuration file is large ({file_size} bytes): {file_path}")
105
+
106
+ # Try to load the configuration
77
107
  file_config = self._config_mgr.load_auto(file_path)
78
108
  if file_config:
79
109
  self._config = self._config_mgr.merge_configs(self._config, file_config)
80
- logger.info(f"Loaded configuration from {file_path}")
81
-
110
+ logger.info(f" Successfully loaded configuration from {file_path}")
111
+
112
+ # Log important configuration values for debugging
113
+ if logger.isEnabledFor(logging.DEBUG):
114
+ response_logging = file_config.get('response_logging', {})
115
+ if response_logging:
116
+ logger.debug(f"Response logging enabled: {response_logging.get('enabled', False)}")
117
+ logger.debug(f"Response logging format: {response_logging.get('format', 'json')}")
118
+
119
+ except yaml.YAMLError as e:
120
+ logger.error(f"YAML syntax error in {file_path}: {e}")
121
+ if hasattr(e, 'problem_mark'):
122
+ mark = e.problem_mark
123
+ logger.error(f"Error at line {mark.line + 1}, column {mark.column + 1}")
124
+ logger.info("TIP: Validate your YAML at https://www.yamllint.com/ or run: python scripts/validate_configuration.py")
125
+ logger.info("TIP: Common issue - YAML requires spaces, not tabs. Fix with: sed -i '' 's/\t/ /g' " + str(file_path))
126
+ # Store error for later retrieval
127
+ self._config['_load_error'] = str(e)
128
+
129
+ except json.JSONDecodeError as e:
130
+ logger.error(f"JSON syntax error in {file_path}: {e}")
131
+ logger.error(f"Error at line {e.lineno}, column {e.colno}")
132
+ logger.info("TIP: Validate your JSON at https://jsonlint.com/")
133
+ self._config['_load_error'] = str(e)
134
+
82
135
  except Exception as e:
83
136
  logger.error(f"Failed to load configuration from {file_path}: {e}")
137
+ logger.info("TIP: Check file permissions and format (YAML/JSON)")
138
+ logger.info("TIP: Run validation with: python scripts/validate_configuration.py")
139
+ self._config['_load_error'] = str(e)
84
140
 
85
141
  def _load_env_vars(self) -> None:
86
142
  """Load configuration from environment variables."""
@@ -323,6 +379,12 @@ class Config:
323
379
  "emergency_stop": True
324
380
  }
325
381
  }
382
+ },
383
+ # Agent deployment configuration
384
+ "agent_deployment": {
385
+ "excluded_agents": [], # List of agent IDs to exclude from deployment
386
+ "exclude_dependencies": False, # Whether to exclude agent dependencies too
387
+ "case_sensitive": False # Whether agent name matching is case-sensitive
326
388
  }
327
389
  }
328
390
 
@@ -515,6 +577,101 @@ class Config:
515
577
 
516
578
  return base_config
517
579
 
580
+ def validate_configuration(self) -> Tuple[bool, List[str], List[str]]:
581
+ """Validate the loaded configuration programmatically.
582
+
583
+ WHY: Provide a programmatic way to validate configuration that can be
584
+ used by other components to check configuration health.
585
+
586
+ Returns:
587
+ Tuple of (is_valid, errors, warnings)
588
+ """
589
+ errors = []
590
+ warnings = []
591
+
592
+ # Check if there was a load error
593
+ if '_load_error' in self._config:
594
+ errors.append(f"Configuration load error: {self._config['_load_error']}")
595
+
596
+ # Validate response_logging configuration
597
+ response_logging = self.get('response_logging', {})
598
+ if response_logging:
599
+ # Check enabled field
600
+ if 'enabled' in response_logging and not isinstance(response_logging['enabled'], bool):
601
+ errors.append(
602
+ f"response_logging.enabled must be boolean, got {type(response_logging['enabled']).__name__}"
603
+ )
604
+
605
+ # Check format field
606
+ if 'format' in response_logging:
607
+ valid_formats = ['json', 'syslog', 'journald']
608
+ if response_logging['format'] not in valid_formats:
609
+ errors.append(
610
+ f"response_logging.format must be one of {valid_formats}, "
611
+ f"got '{response_logging['format']}'"
612
+ )
613
+
614
+ # Check session_directory
615
+ if 'session_directory' in response_logging:
616
+ session_dir = Path(response_logging['session_directory'])
617
+ if session_dir.is_absolute() and not session_dir.parent.exists():
618
+ warnings.append(
619
+ f"Parent directory for session_directory does not exist: {session_dir.parent}"
620
+ )
621
+
622
+ # Validate memory configuration
623
+ memory_config = self.get('memory', {})
624
+ if memory_config:
625
+ if 'enabled' in memory_config and not isinstance(memory_config['enabled'], bool):
626
+ errors.append(f"memory.enabled must be boolean")
627
+
628
+ # Check limits
629
+ limits = memory_config.get('limits', {})
630
+ for field in ['default_size_kb', 'max_sections', 'max_items_per_section']:
631
+ if field in limits:
632
+ value = limits[field]
633
+ if not isinstance(value, int) or value <= 0:
634
+ errors.append(f"memory.limits.{field} must be positive integer, got {value}")
635
+
636
+ # Validate health thresholds
637
+ health_thresholds = self.get('health_thresholds', {})
638
+ if health_thresholds:
639
+ cpu = health_thresholds.get('cpu_percent')
640
+ if cpu is not None and (not isinstance(cpu, (int, float)) or cpu < 0 or cpu > 100):
641
+ errors.append(f"health_thresholds.cpu_percent must be 0-100, got {cpu}")
642
+
643
+ mem = health_thresholds.get('memory_mb')
644
+ if mem is not None and (not isinstance(mem, (int, float)) or mem <= 0):
645
+ errors.append(f"health_thresholds.memory_mb must be positive, got {mem}")
646
+
647
+ is_valid = len(errors) == 0
648
+ return is_valid, errors, warnings
649
+
650
+ def get_configuration_status(self) -> Dict[str, Any]:
651
+ """Get detailed configuration status for debugging.
652
+
653
+ WHY: Provide a comprehensive view of configuration state for
654
+ troubleshooting and health checks.
655
+
656
+ Returns:
657
+ Dictionary with configuration status information
658
+ """
659
+ is_valid, errors, warnings = self.validate_configuration()
660
+
661
+ status = {
662
+ 'valid': is_valid,
663
+ 'errors': errors,
664
+ 'warnings': warnings,
665
+ 'loaded_from': getattr(self, '_loaded_from', 'defaults'),
666
+ 'key_count': len(self._config),
667
+ 'has_response_logging': 'response_logging' in self._config,
668
+ 'has_memory_config': 'memory' in self._config,
669
+ 'response_logging_enabled': self.get('response_logging.enabled', False),
670
+ 'memory_enabled': self.get('memory.enabled', False)
671
+ }
672
+
673
+ return status
674
+
518
675
  def __repr__(self) -> str:
519
676
  """String representation of configuration."""
520
677
  return f"<Config({len(self._config)} keys)>"
@@ -35,7 +35,6 @@ class ConfigPaths:
35
35
  RESPONSES_DIR = "responses"
36
36
 
37
37
  # Agent subdirectories
38
- AGENT_PROJECT_SPECIFIC = "project-specific"
39
38
  AGENT_USER_AGENTS = "user-agents"
40
39
  AGENT_USER_DEFINED = "user-defined"
41
40
  AGENT_SYSTEM = "templates"
@@ -57,14 +57,20 @@ class AgentServiceFactory(ServiceFactory):
57
57
  config = container.resolve(Config)
58
58
 
59
59
  # Get directories from config if not provided
60
+ import os
61
+
60
62
  if framework_dir is None:
61
63
  framework_dir = Path(config.get('framework.dir', 'framework'))
62
64
 
63
65
  if project_dir is None:
64
- project_dir = Path(config.get('project.dir', '.'))
66
+ # Check for user working directory from environment
67
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
68
+ project_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
69
+ else:
70
+ project_dir = Path(config.get('project.dir', '.'))
65
71
 
66
- # Create service with dependencies
67
- service = AgentDeploymentService()
72
+ # Create service with proper working directory
73
+ service = AgentDeploymentService(working_directory=project_dir)
68
74
 
69
75
  # Inject any required dependencies
70
76
  if hasattr(service, 'set_directories'):
@@ -274,6 +274,13 @@ class FrameworkLoader:
274
274
  if self.framework_last_modified:
275
275
  content["instructions_last_modified"] = self.framework_last_modified
276
276
 
277
+ # Load BASE_PM.md for core framework requirements
278
+ base_pm_path = self.framework_path / "src" / "claude_mpm" / "agents" / "BASE_PM.md"
279
+ if base_pm_path.exists():
280
+ base_pm_content = self._try_load_file(base_pm_path, "BASE_PM framework requirements")
281
+ if base_pm_content:
282
+ content["base_pm_instructions"] = base_pm_content
283
+
277
284
  # Discover agent directories
278
285
  agents_dir, templates_dir, main_dir = self._discover_framework_paths()
279
286
 
@@ -308,6 +315,17 @@ class FrameworkLoader:
308
315
  if self.framework_content["working_claude_md"]:
309
316
  instructions += f"\n\n## Working Directory Instructions\n{self.framework_content['working_claude_md']}\n"
310
317
 
318
+ # Add dynamic agent capabilities section
319
+ instructions += self._generate_agent_capabilities_section()
320
+
321
+ # Add current date for temporal awareness
322
+ instructions += f"\n\n## Temporal Context\n**Today's Date**: {datetime.now().strftime('%Y-%m-%d')}\n"
323
+ instructions += "Apply date awareness to all time-sensitive tasks and decisions.\n"
324
+
325
+ # Add BASE_PM.md framework requirements AFTER INSTRUCTIONS.md
326
+ if self.framework_content.get("base_pm_instructions"):
327
+ instructions += f"\n\n{self.framework_content['base_pm_instructions']}\n"
328
+
311
329
  return instructions
312
330
 
313
331
  # Otherwise fall back to generating framework
@@ -413,6 +431,69 @@ Extract tickets from these patterns:
413
431
 
414
432
  return instructions
415
433
 
434
+ def _generate_agent_capabilities_section(self) -> str:
435
+ """Generate dynamic agent capabilities section from deployed agents."""
436
+ try:
437
+ # Try to get agents from agent_loader
438
+ from claude_mpm.agents.agent_loader import list_available_agents
439
+ agents = list_available_agents()
440
+
441
+ if not agents:
442
+ return ""
443
+
444
+ # Build capabilities section
445
+ section = "\n\n## Available Agent Capabilities\n\n"
446
+ section += "You have the following specialized agents available for delegation:\n\n"
447
+
448
+ # Group agents by category
449
+ categories = {}
450
+ for agent_id, info in agents.items():
451
+ category = info.get('category', 'general')
452
+ if category not in categories:
453
+ categories[category] = []
454
+ categories[category].append((agent_id, info))
455
+
456
+ # List agents by category
457
+ for category in sorted(categories.keys()):
458
+ section += f"\n### {category.title()} Agents\n"
459
+ for agent_id, info in sorted(categories[category]):
460
+ name = info.get('name', agent_id)
461
+ desc = info.get('description', 'Specialized agent')
462
+ tools = info.get('tools', [])
463
+ section += f"- **{name}** (`{agent_id}`): {desc}\n"
464
+ if tools:
465
+ section += f" - Tools: {', '.join(tools[:5])}"
466
+ if len(tools) > 5:
467
+ section += f" (+{len(tools)-5} more)"
468
+ section += "\n"
469
+
470
+ # Add summary
471
+ section += f"\n**Total Available Agents**: {len(agents)}\n"
472
+ section += "Use the agent ID in parentheses when delegating tasks via the Task tool.\n"
473
+
474
+ return section
475
+
476
+ except Exception as e:
477
+ self.logger.warning(f"Could not generate dynamic agent capabilities: {e}")
478
+ # Return static fallback
479
+ return """
480
+
481
+ ## Available Agent Capabilities
482
+
483
+ You have the following specialized agents available for delegation:
484
+
485
+ - **Engineer Agent**: Code implementation and development
486
+ - **Research Agent**: Investigation and analysis
487
+ - **QA Agent**: Testing and quality assurance
488
+ - **Documentation Agent**: Documentation creation and maintenance
489
+ - **Security Agent**: Security analysis and protection
490
+ - **Data Engineer Agent**: Data management and pipelines
491
+ - **Ops Agent**: Deployment and operations
492
+ - **Version Control Agent**: Git operations and version management
493
+
494
+ Use these agents to delegate specialized work via the Task tool.
495
+ """
496
+
416
497
  def _format_minimal_framework(self) -> str:
417
498
  """Format minimal framework instructions when full framework not available."""
418
499
  return """
@@ -0,0 +1,36 @@
1
+ # Agent Memory System
2
+
3
+ ## Purpose
4
+ Each agent maintains project-specific knowledge in these files. Agents read their memory file before tasks and update it when they learn something new.
5
+
6
+ ## Manual Editing
7
+ Feel free to edit these files to:
8
+ - Add project-specific guidelines
9
+ - Remove outdated information
10
+ - Reorganize for better clarity
11
+ - Add domain-specific knowledge
12
+
13
+ ## Memory Limits
14
+ - Max file size: 8KB (~2000 tokens)
15
+ - Max sections: 10
16
+ - Max items per section: 15
17
+ - Files auto-truncate when limits exceeded
18
+
19
+ ## File Format
20
+ Standard markdown with structured sections. Agents expect:
21
+ - Project Architecture
22
+ - Implementation Guidelines
23
+ - Common Mistakes to Avoid
24
+ - Current Technical Context
25
+
26
+ ## How It Works
27
+ 1. Agents read their memory file before starting tasks
28
+ 2. Agents add learnings during or after task completion
29
+ 3. Files automatically enforce size limits
30
+ 4. Developers can manually edit for accuracy
31
+
32
+ ## Memory File Lifecycle
33
+ - Created automatically when agent first runs
34
+ - Updated through hook system after delegations
35
+ - Manually editable by developers
36
+ - Version controlled with project
@@ -0,0 +1,121 @@
1
+ # Claude MPM Web Dashboard
2
+
3
+ This directory contains the modular web dashboard for Claude MPM monitoring and management.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ src/claude_mpm/dashboard/
9
+ ├── static/
10
+ │ ├── css/
11
+ │ │ └── dashboard.css # Main stylesheet
12
+ │ └── js/
13
+ │ ├── socket-client.js # Socket.IO connection management
14
+ │ ├── dashboard.js # Main dashboard application
15
+ │ └── components/
16
+ │ ├── event-viewer.js # Event display and filtering
17
+ │ ├── module-viewer.js # Event detail viewer
18
+ │ └── session-manager.js # Session management
19
+ ├── templates/
20
+ │ └── index.html # Main dashboard HTML
21
+ ├── index.html # Root index with redirect
22
+ ├── test_dashboard.html # Test version for verification
23
+ └── README.md # This file
24
+ ```
25
+
26
+ ## Components
27
+
28
+ ### SocketClient (`socket-client.js`)
29
+ - Manages WebSocket connections to the Claude MPM server
30
+ - Handles event reception and processing
31
+ - Provides callbacks for connection state changes
32
+ - Maintains event history and session tracking
33
+
34
+ ### EventViewer (`components/event-viewer.js`)
35
+ - Displays events in a filterable list
36
+ - Supports search, type filtering, and session filtering
37
+ - Handles event selection and keyboard navigation
38
+ - Updates metrics display (total events, events per minute, etc.)
39
+
40
+ ### ModuleViewer (`components/module-viewer.js`)
41
+ - Shows detailed information about selected events
42
+ - Provides structured views for different event types
43
+ - Displays raw JSON data for debugging
44
+ - Organizes events by class/category
45
+
46
+ ### SessionManager (`components/session-manager.js`)
47
+ - Manages session selection and filtering
48
+ - Updates session dropdown with available sessions
49
+ - Tracks current active session
50
+ - Updates footer information
51
+
52
+ ### Dashboard (`dashboard.js`)
53
+ - Main application coordinator
54
+ - Handles tab switching between Events, Agents, Tools, and Files
55
+ - Manages component interactions
56
+ - Provides global functions for backward compatibility
57
+
58
+ ## Features
59
+
60
+ ### File-Centric Files Tab
61
+ The Files tab now shows a file-centric view where:
62
+ - Each file appears only once in the list
63
+ - Files show read/write icons based on operations performed
64
+ - Most recently accessed files appear at the bottom
65
+ - Clicking a file shows all operations performed on it with timestamps and agent information
66
+
67
+ ### Tab Organization
68
+ - **Events**: All events with filtering and search
69
+ - **Agents**: Agent-specific events and operations
70
+ - **Tools**: Tool usage and parameters
71
+ - **Files**: File operations organized by file path
72
+
73
+ ### Enhanced Event Details
74
+ - Structured views for different event types
75
+ - Todo checklists with status indicators
76
+ - Agent and tool information
77
+ - Session tracking and filtering
78
+
79
+ ## Usage
80
+
81
+ ### Development
82
+ For development and testing, use `test_dashboard.html` which includes module verification.
83
+
84
+ ### Production
85
+ Use `templates/dashboard.html` as the main template, ensuring all static files are served correctly.
86
+
87
+ ### Integration
88
+ The dashboard can be integrated into a Flask/FastAPI application by serving the static files and using the template.
89
+
90
+ ## Migration from Original
91
+
92
+ The original monolithic `claude_mpm_socketio_dashboard.html` has been replaced with a modular structure:
93
+
94
+ 1. **CSS**: Extracted to `static/css/dashboard.css`
95
+ 2. **JavaScript**: Split into logical modules in `static/js/`
96
+ 3. **HTML**: Clean template in `templates/dashboard.html`
97
+
98
+ All original functionality has been preserved while improving:
99
+ - Code organization and maintainability
100
+ - Module reusability
101
+ - Easier debugging and development
102
+ - Better separation of concerns
103
+
104
+ ## Backward Compatibility
105
+
106
+ Global functions are maintained for compatibility:
107
+ - `connectSocket()`
108
+ - `disconnectSocket()`
109
+ - `clearEvents()`
110
+ - `exportEvents()`
111
+ - `clearSelection()`
112
+ - `switchTab(tabName)`
113
+
114
+ ## Browser Support
115
+
116
+ Requires modern browsers with support for:
117
+ - ES6 Classes
118
+ - Fetch API
119
+ - Custom Events
120
+ - CSS Grid/Flexbox
121
+ - Socket.IO 4.x