claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +2 -2
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +79 -51
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +20 -20
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +140 -905
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +330 -86
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +363 -220
- claude_mpm/cli/parser.py +24 -1156
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +124 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +71 -73
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +35 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/built/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/built/dashboard.js +2 -0
- claude_mpm/dashboard/static/built/socket-client.js +2 -0
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +133 -53
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +233 -199
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +30 -13
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
- claude_mpm/services/mcp_gateway/main.py +287 -137
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +575 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +166 -64
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +185 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +19 -533
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +2 -2
- claude_mpm/storage/state_storage.py +177 -181
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
- claude_mpm-4.0.4.dist-info/RECORD +417 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/cli/commands/run_guarded.py +0 -511
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/config/memory_guardian_yaml.py +0 -335
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/core/memory_aware_runner.py +0 -353
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
- claude_mpm/services/infrastructure/health_monitor.py +0 -775
- claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
- claude_mpm/services/infrastructure/memory_guardian.py +0 -944
- claude_mpm/services/infrastructure/restart_protection.py +0 -642
- claude_mpm/services/infrastructure/state_manager.py +0 -774
- claude_mpm/services/mcp_gateway/manager.py +0 -334
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.11.dist-info/RECORD +0 -306
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
| @@ -1,24 +1,36 @@ | |
| 1 1 | 
             
            /**
         | 
| 2 2 | 
             
             * Refactored Dashboard Coordinator
         | 
| 3 | 
            -
             * | 
| 3 | 
            +
             *
         | 
| 4 4 | 
             
             * Main coordinator class that orchestrates all dashboard modules while maintaining
         | 
| 5 5 | 
             
             * backward compatibility with the original dashboard interface.
         | 
| 6 | 
            -
             * | 
| 6 | 
            +
             *
         | 
| 7 7 | 
             
             * WHY: This refactored version breaks down the monolithic 4,133-line dashboard
         | 
| 8 8 | 
             
             * into manageable, focused modules while preserving all existing functionality.
         | 
| 9 9 | 
             
             * Each module handles a specific concern, improving maintainability and testability.
         | 
| 10 | 
            -
             * | 
| 10 | 
            +
             *
         | 
| 11 11 | 
             
             * DESIGN DECISION: Acts as a thin coordinator layer that initializes modules,
         | 
| 12 12 | 
             
             * manages inter-module communication through events, and provides backward
         | 
| 13 13 | 
             
             * compatibility for existing code that depends on the dashboard interface.
         | 
| 14 14 | 
             
             */
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            // ES6 Module imports
         | 
| 17 | 
            +
            import { SocketManager } from '@components/socket-manager.js';
         | 
| 18 | 
            +
            import { EventViewer } from '@components/event-viewer.js';
         | 
| 19 | 
            +
            import { ModuleViewer } from '@components/module-viewer.js';
         | 
| 20 | 
            +
            import { SessionManager } from '@components/session-manager.js';
         | 
| 21 | 
            +
            import { AgentInference } from '@components/agent-inference.js';
         | 
| 22 | 
            +
            import { UIStateManager } from '@components/ui-state-manager.js';
         | 
| 23 | 
            +
            import { EventProcessor } from '@components/event-processor.js';
         | 
| 24 | 
            +
            import { ExportManager } from '@components/export-manager.js';
         | 
| 25 | 
            +
            import { WorkingDirectoryManager } from '@components/working-directory.js';
         | 
| 26 | 
            +
            import { FileToolTracker } from '@components/file-tool-tracker.js';
         | 
| 15 27 | 
             
            class Dashboard {
         | 
| 16 28 | 
             
                constructor() {
         | 
| 17 29 | 
             
                    // Core components (existing)
         | 
| 18 30 | 
             
                    this.eventViewer = null;
         | 
| 19 31 | 
             
                    this.moduleViewer = null;
         | 
| 20 32 | 
             
                    this.sessionManager = null;
         | 
| 21 | 
            -
             | 
| 33 | 
            +
             | 
| 22 34 | 
             
                    // New modular components
         | 
| 23 35 | 
             
                    this.socketManager = null;
         | 
| 24 36 | 
             
                    this.agentInference = null;
         | 
| @@ -27,7 +39,7 @@ class Dashboard { | |
| 27 39 | 
             
                    this.exportManager = null;
         | 
| 28 40 | 
             
                    this.workingDirectoryManager = null;
         | 
| 29 41 | 
             
                    this.fileToolTracker = null;
         | 
| 30 | 
            -
             | 
| 42 | 
            +
             | 
| 31 43 | 
             
                    // Initialize the dashboard
         | 
| 32 44 | 
             
                    this.init();
         | 
| 33 45 | 
             
                }
         | 
| @@ -37,7 +49,7 @@ class Dashboard { | |
| 37 49 | 
             
                 */
         | 
| 38 50 | 
             
                init() {
         | 
| 39 51 | 
             
                    console.log('Initializing refactored Claude MPM Dashboard...');
         | 
| 40 | 
            -
             | 
| 52 | 
            +
             | 
| 41 53 | 
             
                    // Initialize modules in dependency order
         | 
| 42 54 | 
             
                    this.initializeSocketManager();
         | 
| 43 55 | 
             
                    this.initializeCoreComponents();
         | 
| @@ -47,13 +59,13 @@ class Dashboard { | |
| 47 59 | 
             
                    this.initializeFileToolTracker();
         | 
| 48 60 | 
             
                    this.initializeEventProcessor();
         | 
| 49 61 | 
             
                    this.initializeExportManager();
         | 
| 50 | 
            -
             | 
| 62 | 
            +
             | 
| 51 63 | 
             
                    // Set up inter-module communication
         | 
| 52 64 | 
             
                    this.setupModuleInteractions();
         | 
| 53 | 
            -
             | 
| 65 | 
            +
             | 
| 54 66 | 
             
                    // Initialize from URL parameters
         | 
| 55 67 | 
             
                    this.initializeFromURL();
         | 
| 56 | 
            -
             | 
| 68 | 
            +
             | 
| 57 69 | 
             
                    console.log('Claude MPM Dashboard initialized successfully');
         | 
| 58 70 | 
             
                }
         | 
| 59 71 |  | 
| @@ -62,10 +74,10 @@ class Dashboard { | |
| 62 74 | 
             
                 */
         | 
| 63 75 | 
             
                initializeSocketManager() {
         | 
| 64 76 | 
             
                    this.socketManager = new SocketManager();
         | 
| 65 | 
            -
             | 
| 77 | 
            +
             | 
| 66 78 | 
             
                    // Set up connection controls
         | 
| 67 79 | 
             
                    this.socketManager.setupConnectionControls();
         | 
| 68 | 
            -
             | 
| 80 | 
            +
             | 
| 69 81 | 
             
                    // Backward compatibility
         | 
| 70 82 | 
             
                    this.socketClient = this.socketManager.getSocketClient();
         | 
| 71 83 | 
             
                    window.socketClient = this.socketClient;
         | 
| @@ -79,7 +91,7 @@ class Dashboard { | |
| 79 91 | 
             
                    this.eventViewer = new EventViewer('events-list', this.socketClient);
         | 
| 80 92 | 
             
                    this.moduleViewer = new ModuleViewer();
         | 
| 81 93 | 
             
                    this.sessionManager = new SessionManager(this.socketClient);
         | 
| 82 | 
            -
             | 
| 94 | 
            +
             | 
| 83 95 | 
             
                    // Backward compatibility
         | 
| 84 96 | 
             
                    window.eventViewer = this.eventViewer;
         | 
| 85 97 | 
             
                    window.moduleViewer = this.moduleViewer;
         | 
| @@ -139,15 +151,15 @@ class Dashboard { | |
| 139 151 | 
             
                    this.socketManager.onEventUpdate((events) => {
         | 
| 140 152 | 
             
                        this.fileToolTracker.updateFileOperations(events);
         | 
| 141 153 | 
             
                        this.fileToolTracker.updateToolCalls(events);
         | 
| 142 | 
            -
             | 
| 154 | 
            +
             | 
| 143 155 | 
             
                        // Process agent inference for new events
         | 
| 144 156 | 
             
                        this.agentInference.processAgentInference();
         | 
| 145 | 
            -
             | 
| 157 | 
            +
             | 
| 146 158 | 
             
                        // Auto-scroll events list if on events tab
         | 
| 147 159 | 
             
                        if (this.uiStateManager.getCurrentTab() === 'events') {
         | 
| 148 160 | 
             
                            this.exportManager.scrollListToBottom('events-list');
         | 
| 149 161 | 
             
                        }
         | 
| 150 | 
            -
             | 
| 162 | 
            +
             | 
| 151 163 | 
             
                        // Re-render current tab
         | 
| 152 164 | 
             
                        this.renderCurrentTab();
         | 
| 153 165 | 
             
                    });
         | 
| @@ -156,14 +168,9 @@ class Dashboard { | |
| 156 168 | 
             
                    this.socketManager.onConnectionStatusChange((status, type) => {
         | 
| 157 169 | 
             
                        // Set up git branch listener when connected
         | 
| 158 170 | 
             
                        if (type === 'connected') {
         | 
| 159 | 
            -
                             | 
| 160 | 
            -
             | 
| 161 | 
            -
                             | 
| 162 | 
            -
                            this.workingDirectoryManager.whenDirectoryReady(() => {
         | 
| 163 | 
            -
                                const currentDir = this.workingDirectoryManager.getCurrentWorkingDir();
         | 
| 164 | 
            -
                                console.log('[DASHBOARD-INIT-DEBUG] Directory ready, requesting git branch for:', currentDir);
         | 
| 165 | 
            -
                                this.workingDirectoryManager.updateGitBranch(currentDir);
         | 
| 166 | 
            -
                            });
         | 
| 171 | 
            +
                            this.workingDirectoryManager.updateGitBranch(
         | 
| 172 | 
            +
                                this.workingDirectoryManager.getCurrentWorkingDir()
         | 
| 173 | 
            +
                            );
         | 
| 167 174 | 
             
                        }
         | 
| 168 175 | 
             
                    });
         | 
| 169 176 |  | 
| @@ -198,13 +205,13 @@ class Dashboard { | |
| 198 205 | 
             
                    // Agents tab filters
         | 
| 199 206 | 
             
                    const agentsSearchInput = document.getElementById('agents-search-input');
         | 
| 200 207 | 
             
                    const agentsTypeFilter = document.getElementById('agents-type-filter');
         | 
| 201 | 
            -
             | 
| 208 | 
            +
             | 
| 202 209 | 
             
                    if (agentsSearchInput) {
         | 
| 203 210 | 
             
                        agentsSearchInput.addEventListener('input', () => {
         | 
| 204 211 | 
             
                            if (this.uiStateManager.getCurrentTab() === 'agents') this.renderCurrentTab();
         | 
| 205 212 | 
             
                        });
         | 
| 206 213 | 
             
                    }
         | 
| 207 | 
            -
             | 
| 214 | 
            +
             | 
| 208 215 | 
             
                    if (agentsTypeFilter) {
         | 
| 209 216 | 
             
                        agentsTypeFilter.addEventListener('change', () => {
         | 
| 210 217 | 
             
                            if (this.uiStateManager.getCurrentTab() === 'agents') this.renderCurrentTab();
         | 
| @@ -214,29 +221,29 @@ class Dashboard { | |
| 214 221 | 
             
                    // Tools tab filters
         | 
| 215 222 | 
             
                    const toolsSearchInput = document.getElementById('tools-search-input');
         | 
| 216 223 | 
             
                    const toolsTypeFilter = document.getElementById('tools-type-filter');
         | 
| 217 | 
            -
             | 
| 224 | 
            +
             | 
| 218 225 | 
             
                    if (toolsSearchInput) {
         | 
| 219 226 | 
             
                        toolsSearchInput.addEventListener('input', () => {
         | 
| 220 227 | 
             
                            if (this.uiStateManager.getCurrentTab() === 'tools') this.renderCurrentTab();
         | 
| 221 228 | 
             
                        });
         | 
| 222 229 | 
             
                    }
         | 
| 223 | 
            -
             | 
| 230 | 
            +
             | 
| 224 231 | 
             
                    if (toolsTypeFilter) {
         | 
| 225 232 | 
             
                        toolsTypeFilter.addEventListener('change', () => {
         | 
| 226 233 | 
             
                            if (this.uiStateManager.getCurrentTab() === 'tools') this.renderCurrentTab();
         | 
| 227 234 | 
             
                        });
         | 
| 228 235 | 
             
                    }
         | 
| 229 236 |  | 
| 230 | 
            -
                    // Files tab filters | 
| 237 | 
            +
                    // Files tab filters
         | 
| 231 238 | 
             
                    const filesSearchInput = document.getElementById('files-search-input');
         | 
| 232 239 | 
             
                    const filesTypeFilter = document.getElementById('files-type-filter');
         | 
| 233 | 
            -
             | 
| 240 | 
            +
             | 
| 234 241 | 
             
                    if (filesSearchInput) {
         | 
| 235 242 | 
             
                        filesSearchInput.addEventListener('input', () => {
         | 
| 236 243 | 
             
                            if (this.uiStateManager.getCurrentTab() === 'files') this.renderCurrentTab();
         | 
| 237 244 | 
             
                        });
         | 
| 238 245 | 
             
                    }
         | 
| 239 | 
            -
             | 
| 246 | 
            +
             | 
| 240 247 | 
             
                    if (filesTypeFilter) {
         | 
| 241 248 | 
             
                        filesTypeFilter.addEventListener('change', () => {
         | 
| 242 249 | 
             
                            if (this.uiStateManager.getCurrentTab() === 'files') this.renderCurrentTab();
         | 
| @@ -257,7 +264,7 @@ class Dashboard { | |
| 257 264 | 
             
                 */
         | 
| 258 265 | 
             
                renderCurrentTab() {
         | 
| 259 266 | 
             
                    const currentTab = this.uiStateManager.getCurrentTab();
         | 
| 260 | 
            -
             | 
| 267 | 
            +
             | 
| 261 268 | 
             
                    switch (currentTab) {
         | 
| 262 269 | 
             
                        case 'events':
         | 
| 263 270 | 
             
                            // Events tab is handled by EventViewer
         | 
| @@ -272,13 +279,13 @@ class Dashboard { | |
| 272 279 | 
             
                            this.renderFiles();
         | 
| 273 280 | 
             
                            break;
         | 
| 274 281 | 
             
                    }
         | 
| 275 | 
            -
             | 
| 282 | 
            +
             | 
| 276 283 | 
             
                    // Update selection UI if we have a selected card
         | 
| 277 284 | 
             
                    const selectedCard = this.uiStateManager.getSelectedCard();
         | 
| 278 285 | 
             
                    if (selectedCard.tab === currentTab) {
         | 
| 279 286 | 
             
                        this.uiStateManager.updateCardSelectionUI();
         | 
| 280 287 | 
             
                    }
         | 
| 281 | 
            -
             | 
| 288 | 
            +
             | 
| 282 289 | 
             
                    // Update unified selection UI to maintain consistency
         | 
| 283 290 | 
             
                    this.uiStateManager.updateUnifiedSelectionUI();
         | 
| 284 291 | 
             
                }
         | 
| @@ -292,14 +299,14 @@ class Dashboard { | |
| 292 299 |  | 
| 293 300 | 
             
                    // Process agent inference to get PM delegations
         | 
| 294 301 | 
             
                    this.agentInference.processAgentInference();
         | 
| 295 | 
            -
             | 
| 302 | 
            +
             | 
| 296 303 | 
             
                    // Generate HTML for unique agent instances
         | 
| 297 304 | 
             
                    const events = this.eventProcessor.getFilteredEventsForTab('agents');
         | 
| 298 305 | 
             
                    const agentHTML = this.eventProcessor.generateAgentHTML(events);
         | 
| 299 | 
            -
             | 
| 306 | 
            +
             | 
| 300 307 | 
             
                    agentsList.innerHTML = agentHTML;
         | 
| 301 308 | 
             
                    this.exportManager.scrollListToBottom('agents-list');
         | 
| 302 | 
            -
             | 
| 309 | 
            +
             | 
| 303 310 | 
             
                    // Update filter dropdowns with unique instances
         | 
| 304 311 | 
             
                    const uniqueInstances = this.agentInference.getUniqueAgentInstances();
         | 
| 305 312 | 
             
                    this.updateAgentsFilterDropdowns(uniqueInstances);
         | 
| @@ -316,10 +323,10 @@ class Dashboard { | |
| 316 323 | 
             
                    const toolCallsArray = Array.from(toolCalls.entries());
         | 
| 317 324 | 
             
                    const uniqueToolInstances = this.eventProcessor.getUniqueToolInstances(toolCallsArray);
         | 
| 318 325 | 
             
                    const toolHTML = this.eventProcessor.generateToolHTML(uniqueToolInstances);
         | 
| 319 | 
            -
             | 
| 326 | 
            +
             | 
| 320 327 | 
             
                    toolsList.innerHTML = toolHTML;
         | 
| 321 328 | 
             
                    this.exportManager.scrollListToBottom('tools-list');
         | 
| 322 | 
            -
             | 
| 329 | 
            +
             | 
| 323 330 | 
             
                    // Update filter dropdowns
         | 
| 324 331 | 
             
                    this.updateToolsFilterDropdowns(uniqueToolInstances);
         | 
| 325 332 | 
             
                }
         | 
| @@ -335,10 +342,10 @@ class Dashboard { | |
| 335 342 | 
             
                    const filesArray = Array.from(fileOperations.entries());
         | 
| 336 343 | 
             
                    const uniqueFileInstances = this.eventProcessor.getUniqueFileInstances(filesArray);
         | 
| 337 344 | 
             
                    const fileHTML = this.eventProcessor.generateFileHTML(uniqueFileInstances);
         | 
| 338 | 
            -
             | 
| 345 | 
            +
             | 
| 339 346 | 
             
                    filesList.innerHTML = fileHTML;
         | 
| 340 347 | 
             
                    this.exportManager.scrollListToBottom('files-list');
         | 
| 341 | 
            -
             | 
| 348 | 
            +
             | 
| 342 349 | 
             
                    // Update filter dropdowns
         | 
| 343 350 | 
             
                    this.updateFilesFilterDropdowns(filesArray);
         | 
| 344 351 | 
             
                }
         | 
| @@ -348,22 +355,22 @@ class Dashboard { | |
| 348 355 | 
             
                 */
         | 
| 349 356 | 
             
                updateAgentsFilterDropdowns(uniqueInstances) {
         | 
| 350 357 | 
             
                    const agentTypes = new Set();
         | 
| 351 | 
            -
             | 
| 358 | 
            +
             | 
| 352 359 | 
             
                    // uniqueInstances is already an array of unique agent instances
         | 
| 353 360 | 
             
                    uniqueInstances.forEach(instance => {
         | 
| 354 361 | 
             
                        if (instance.agentName && instance.agentName !== 'Unknown') {
         | 
| 355 362 | 
             
                            agentTypes.add(instance.agentName);
         | 
| 356 363 | 
             
                        }
         | 
| 357 364 | 
             
                    });
         | 
| 358 | 
            -
             | 
| 365 | 
            +
             | 
| 359 366 | 
             
                    const sortedTypes = Array.from(agentTypes).filter(type => type && type.trim() !== '');
         | 
| 360 367 | 
             
                    this.populateFilterDropdown('agents-type-filter', sortedTypes, 'All Agent Types');
         | 
| 361 | 
            -
             | 
| 368 | 
            +
             | 
| 362 369 | 
             
                    // Debug log
         | 
| 363 370 | 
             
                    if (sortedTypes.length > 0) {
         | 
| 364 371 | 
             
                        console.log('Agent types found for filter:', sortedTypes);
         | 
| 365 372 | 
             
                    } else {
         | 
| 366 | 
            -
                        console.log('No agent types found for filter.  | 
| 373 | 
            +
                        console.log('No agent types found for filter. Instances:', uniqueInstances.length);
         | 
| 367 374 | 
             
                    }
         | 
| 368 375 | 
             
                }
         | 
| 369 376 |  | 
| @@ -373,7 +380,7 @@ class Dashboard { | |
| 373 380 | 
             
                updateToolsFilterDropdowns(toolCallsArray) {
         | 
| 374 381 | 
             
                    const toolNames = [...new Set(toolCallsArray.map(([key, toolCall]) => toolCall.tool_name))]
         | 
| 375 382 | 
             
                        .filter(name => name);
         | 
| 376 | 
            -
             | 
| 383 | 
            +
             | 
| 377 384 | 
             
                    this.populateFilterDropdown('tools-type-filter', toolNames, 'All Tools');
         | 
| 378 385 | 
             
                }
         | 
| 379 386 |  | 
| @@ -381,10 +388,10 @@ class Dashboard { | |
| 381 388 | 
             
                 * Update files filter dropdowns
         | 
| 382 389 | 
             
                 */
         | 
| 383 390 | 
             
                updateFilesFilterDropdowns(filesArray) {
         | 
| 384 | 
            -
                    const operations = [...new Set(filesArray.flatMap(([path, data]) => | 
| 391 | 
            +
                    const operations = [...new Set(filesArray.flatMap(([path, data]) =>
         | 
| 385 392 | 
             
                        data.operations.map(op => op.operation)
         | 
| 386 393 | 
             
                    ))].filter(op => op);
         | 
| 387 | 
            -
             | 
| 394 | 
            +
             | 
| 388 395 | 
             
                    this.populateFilterDropdown('files-type-filter', operations, 'All Operations');
         | 
| 389 396 | 
             
                }
         | 
| 390 397 |  | 
| @@ -397,10 +404,10 @@ class Dashboard { | |
| 397 404 |  | 
| 398 405 | 
             
                    const currentValue = select.value;
         | 
| 399 406 | 
             
                    const sortedValues = values.sort((a, b) => a.localeCompare(b));
         | 
| 400 | 
            -
             | 
| 407 | 
            +
             | 
| 401 408 | 
             
                    // Clear existing options except the first "All" option
         | 
| 402 409 | 
             
                    select.innerHTML = `<option value="">${allOption}</option>`;
         | 
| 403 | 
            -
             | 
| 410 | 
            +
             | 
| 404 411 | 
             
                    // Add sorted values
         | 
| 405 412 | 
             
                    sortedValues.forEach(value => {
         | 
| 406 413 | 
             
                        const option = document.createElement('option');
         | 
| @@ -408,7 +415,7 @@ class Dashboard { | |
| 408 415 | 
             
                        option.textContent = value;
         | 
| 409 416 | 
             
                        select.appendChild(option);
         | 
| 410 417 | 
             
                    });
         | 
| 411 | 
            -
             | 
| 418 | 
            +
             | 
| 412 419 | 
             
                    // Restore previous selection if it still exists
         | 
| 413 420 | 
             
                    if (currentValue && sortedValues.includes(currentValue)) {
         | 
| 414 421 | 
             
                        select.value = currentValue;
         | 
| @@ -442,16 +449,16 @@ class Dashboard { | |
| 442 449 | 
             
                 */
         | 
| 443 450 | 
             
                showAgentDetailsByIndex(index) {
         | 
| 444 451 | 
             
                    const events = this.eventProcessor.getFilteredEventsForTab('agents');
         | 
| 445 | 
            -
             | 
| 452 | 
            +
             | 
| 446 453 | 
             
                    // Defensive checks
         | 
| 447 454 | 
             
                    if (!events || !Array.isArray(events) || index < 0 || index >= events.length) {
         | 
| 448 455 | 
             
                        console.warn('Dashboard: Invalid agent index or events array');
         | 
| 449 456 | 
             
                        return;
         | 
| 450 457 | 
             
                    }
         | 
| 451 | 
            -
             | 
| 458 | 
            +
             | 
| 452 459 | 
             
                    const filteredSingleEvent = this.eventProcessor.applyAgentsFilters([events[index]]);
         | 
| 453 | 
            -
             | 
| 454 | 
            -
                    if (filteredSingleEvent.length > 0 && this.moduleViewer && | 
| 460 | 
            +
             | 
| 461 | 
            +
                    if (filteredSingleEvent.length > 0 && this.moduleViewer &&
         | 
| 455 462 | 
             
                        typeof this.moduleViewer.showAgentEvent === 'function') {
         | 
| 456 463 | 
             
                        const event = filteredSingleEvent[0];
         | 
| 457 464 | 
             
                        this.moduleViewer.showAgentEvent(event, index);
         | 
| @@ -465,22 +472,22 @@ class Dashboard { | |
| 465 472 | 
             
                showAgentInstanceDetails(instanceId) {
         | 
| 466 473 | 
             
                    const pmDelegations = this.agentInference.getPMDelegations();
         | 
| 467 474 | 
             
                    const instance = pmDelegations.get(instanceId);
         | 
| 468 | 
            -
             | 
| 475 | 
            +
             | 
| 469 476 | 
             
                    if (!instance) {
         | 
| 470 477 | 
             
                        // Check if it's an implied delegation
         | 
| 471 478 | 
             
                        const uniqueInstances = this.agentInference.getUniqueAgentInstances();
         | 
| 472 479 | 
             
                        const impliedInstance = uniqueInstances.find(inst => inst.id === instanceId);
         | 
| 473 | 
            -
             | 
| 480 | 
            +
             | 
| 474 481 | 
             
                        if (!impliedInstance) {
         | 
| 475 482 | 
             
                            console.error('Agent instance not found:', instanceId);
         | 
| 476 483 | 
             
                            return;
         | 
| 477 484 | 
             
                        }
         | 
| 478 | 
            -
             | 
| 485 | 
            +
             | 
| 479 486 | 
             
                        // For implied instances, show basic info
         | 
| 480 487 | 
             
                        this.showImpliedAgentDetails(impliedInstance);
         | 
| 481 488 | 
             
                        return;
         | 
| 482 489 | 
             
                    }
         | 
| 483 | 
            -
             | 
| 490 | 
            +
             | 
| 484 491 | 
             
                    // Show full PM delegation details
         | 
| 485 492 | 
             
                    if (this.moduleViewer && typeof this.moduleViewer.showAgentInstance === 'function') {
         | 
| 486 493 | 
             
                        this.moduleViewer.showAgentInstance(instance);
         | 
| @@ -524,7 +531,7 @@ class Dashboard { | |
| 524 531 | 
             
                    const toolCalls = this.fileToolTracker.getToolCalls();
         | 
| 525 532 | 
             
                    const toolCallsArray = Array.from(toolCalls.entries());
         | 
| 526 533 | 
             
                    const filteredToolCalls = this.eventProcessor.applyToolCallFilters(toolCallsArray);
         | 
| 527 | 
            -
             | 
| 534 | 
            +
             | 
| 528 535 | 
             
                    if (index >= 0 && index < filteredToolCalls.length) {
         | 
| 529 536 | 
             
                        const [toolCallKey] = filteredToolCalls[index];
         | 
| 530 537 | 
             
                        this.showToolCallDetails(toolCallKey);
         | 
| @@ -532,13 +539,13 @@ class Dashboard { | |
| 532 539 | 
             
                }
         | 
| 533 540 |  | 
| 534 541 | 
             
                /**
         | 
| 535 | 
            -
                 * Show file details by index | 
| 542 | 
            +
                 * Show file details by index
         | 
| 536 543 | 
             
                 */
         | 
| 537 544 | 
             
                showFileDetailsByIndex(index) {
         | 
| 538 545 | 
             
                    const fileOperations = this.fileToolTracker.getFileOperations();
         | 
| 539 546 | 
             
                    let filesArray = Array.from(fileOperations.entries());
         | 
| 540 547 | 
             
                    filesArray = this.eventProcessor.applyFilesFilters(filesArray);
         | 
| 541 | 
            -
             | 
| 548 | 
            +
             | 
| 542 549 | 
             
                    if (index >= 0 && index < filesArray.length) {
         | 
| 543 550 | 
             
                        const [filePath] = filesArray[index];
         | 
| 544 551 | 
             
                        this.showFileDetails(filePath);
         | 
| @@ -648,7 +655,7 @@ class Dashboard { | |
| 648 655 |  | 
| 649 656 | 
             
                /**
         | 
| 650 657 | 
             
                 * Get tool calls (backward compatibility)
         | 
| 651 | 
            -
                 */ | 
| 658 | 
            +
                 */
         | 
| 652 659 | 
             
                get toolCalls() {
         | 
| 653 660 | 
             
                    return this.fileToolTracker.getToolCalls();
         | 
| 654 661 | 
             
                }
         | 
| @@ -693,17 +700,17 @@ window.showFileViewerModal = function(filePath, workingDir) { | |
| 693 700 | 
             
                if (!workingDir && window.dashboard && window.dashboard.currentWorkingDir) {
         | 
| 694 701 | 
             
                    workingDir = window.dashboard.currentWorkingDir;
         | 
| 695 702 | 
             
                }
         | 
| 696 | 
            -
             | 
| 703 | 
            +
             | 
| 697 704 | 
             
                // Create modal if it doesn't exist
         | 
| 698 705 | 
             
                let modal = document.getElementById('file-viewer-modal');
         | 
| 699 706 | 
             
                if (!modal) {
         | 
| 700 707 | 
             
                    modal = createFileViewerModal();
         | 
| 701 708 | 
             
                    document.body.appendChild(modal);
         | 
| 702 709 | 
             
                }
         | 
| 703 | 
            -
             | 
| 710 | 
            +
             | 
| 704 711 | 
             
                // Update modal content
         | 
| 705 712 | 
             
                updateFileViewerModal(modal, filePath, workingDir);
         | 
| 706 | 
            -
             | 
| 713 | 
            +
             | 
| 707 714 | 
             
                // Show the modal as flex container
         | 
| 708 715 | 
             
                modal.style.display = 'flex';
         | 
| 709 716 | 
             
                document.body.style.overflow = 'hidden'; // Prevent background scrolling
         | 
| @@ -720,12 +727,12 @@ window.hideFileViewerModal = function() { | |
| 720 727 | 
             
            window.copyFileContent = function() {
         | 
| 721 728 | 
             
                const modal = document.getElementById('file-viewer-modal');
         | 
| 722 729 | 
             
                if (!modal) return;
         | 
| 723 | 
            -
             | 
| 730 | 
            +
             | 
| 724 731 | 
             
                const codeElement = modal.querySelector('.file-content-code');
         | 
| 725 732 | 
             
                if (!codeElement) return;
         | 
| 726 | 
            -
             | 
| 733 | 
            +
             | 
| 727 734 | 
             
                const text = codeElement.textContent;
         | 
| 728 | 
            -
             | 
| 735 | 
            +
             | 
| 729 736 | 
             
                if (navigator.clipboard && navigator.clipboard.writeText) {
         | 
| 730 737 | 
             
                    navigator.clipboard.writeText(text).then(() => {
         | 
| 731 738 | 
             
                        // Show brief feedback
         | 
| @@ -746,7 +753,7 @@ window.copyFileContent = function() { | |
| 746 753 | 
             
                    textarea.select();
         | 
| 747 754 | 
             
                    document.execCommand('copy');
         | 
| 748 755 | 
             
                    document.body.removeChild(textarea);
         | 
| 749 | 
            -
             | 
| 756 | 
            +
             | 
| 750 757 | 
             
                    const button = modal.querySelector('.file-content-copy');
         | 
| 751 758 | 
             
                    const originalText = button.textContent;
         | 
| 752 759 | 
             
                    button.textContent = '✅ Copied!';
         | 
| @@ -760,7 +767,7 @@ function createFileViewerModal() { | |
| 760 767 | 
             
                const modal = document.createElement('div');
         | 
| 761 768 | 
             
                modal.id = 'file-viewer-modal';
         | 
| 762 769 | 
             
                modal.className = 'modal file-viewer-modal';
         | 
| 763 | 
            -
             | 
| 770 | 
            +
             | 
| 764 771 | 
             
                modal.innerHTML = `
         | 
| 765 772 | 
             
                    <div class="modal-content file-viewer-content">
         | 
| 766 773 | 
             
                        <div class="file-viewer-header">
         | 
| @@ -805,21 +812,21 @@ function createFileViewerModal() { | |
| 805 812 | 
             
                        </div>
         | 
| 806 813 | 
             
                    </div>
         | 
| 807 814 | 
             
                `;
         | 
| 808 | 
            -
             | 
| 815 | 
            +
             | 
| 809 816 | 
             
                // Close modal when clicking outside
         | 
| 810 817 | 
             
                modal.addEventListener('click', (e) => {
         | 
| 811 818 | 
             
                    if (e.target === modal) {
         | 
| 812 819 | 
             
                        hideFileViewerModal();
         | 
| 813 820 | 
             
                    }
         | 
| 814 821 | 
             
                });
         | 
| 815 | 
            -
             | 
| 822 | 
            +
             | 
| 816 823 | 
             
                // Close modal with Escape key
         | 
| 817 824 | 
             
                document.addEventListener('keydown', (e) => {
         | 
| 818 825 | 
             
                    if (e.key === 'Escape' && modal.style.display === 'flex') {
         | 
| 819 826 | 
             
                        hideFileViewerModal();
         | 
| 820 827 | 
             
                    }
         | 
| 821 828 | 
             
                });
         | 
| 822 | 
            -
             | 
| 829 | 
            +
             | 
| 823 830 | 
             
                return modal;
         | 
| 824 831 | 
             
            }
         | 
| 825 832 |  | 
| @@ -827,22 +834,22 @@ async function updateFileViewerModal(modal, filePath, workingDir) { | |
| 827 834 | 
             
                // Update header info
         | 
| 828 835 | 
             
                const filePathElement = modal.querySelector('.file-viewer-file-path');
         | 
| 829 836 | 
             
                const fileSizeElement = modal.querySelector('.file-viewer-file-size');
         | 
| 830 | 
            -
             | 
| 837 | 
            +
             | 
| 831 838 | 
             
                filePathElement.textContent = filePath;
         | 
| 832 839 | 
             
                fileSizeElement.textContent = '';
         | 
| 833 | 
            -
             | 
| 840 | 
            +
             | 
| 834 841 | 
             
                // Show loading state
         | 
| 835 842 | 
             
                modal.querySelector('.file-viewer-loading').style.display = 'flex';
         | 
| 836 843 | 
             
                modal.querySelector('.file-viewer-error').style.display = 'none';
         | 
| 837 844 | 
             
                modal.querySelector('.file-viewer-content-area').style.display = 'none';
         | 
| 838 | 
            -
             | 
| 845 | 
            +
             | 
| 839 846 | 
             
                try {
         | 
| 840 847 | 
             
                    // Get the Socket.IO client
         | 
| 841 848 | 
             
                    const socket = window.socket || window.dashboard?.socketClient?.socket;
         | 
| 842 849 | 
             
                    if (!socket) {
         | 
| 843 850 | 
             
                        throw new Error('No socket connection available');
         | 
| 844 851 | 
             
                    }
         | 
| 845 | 
            -
             | 
| 852 | 
            +
             | 
| 846 853 | 
             
                    // Set up one-time listener for file content response
         | 
| 847 854 | 
             
                    const responsePromise = new Promise((resolve, reject) => {
         | 
| 848 855 | 
             
                        const responseHandler = (data) => {
         | 
| @@ -855,46 +862,46 @@ async function updateFileViewerModal(modal, filePath, workingDir) { | |
| 855 862 | 
             
                                }
         | 
| 856 863 | 
             
                            }
         | 
| 857 864 | 
             
                        };
         | 
| 858 | 
            -
             | 
| 865 | 
            +
             | 
| 859 866 | 
             
                        socket.on('file_content_response', responseHandler);
         | 
| 860 | 
            -
             | 
| 867 | 
            +
             | 
| 861 868 | 
             
                        // Timeout after 10 seconds
         | 
| 862 869 | 
             
                        setTimeout(() => {
         | 
| 863 870 | 
             
                            socket.off('file_content_response', responseHandler);
         | 
| 864 871 | 
             
                            reject(new Error('Request timeout'));
         | 
| 865 872 | 
             
                        }, 10000);
         | 
| 866 873 | 
             
                    });
         | 
| 867 | 
            -
             | 
| 874 | 
            +
             | 
| 868 875 | 
             
                    // Send file read request
         | 
| 869 876 | 
             
                    socket.emit('read_file', {
         | 
| 870 877 | 
             
                        file_path: filePath,
         | 
| 871 878 | 
             
                        working_dir: workingDir
         | 
| 872 879 | 
             
                    });
         | 
| 873 | 
            -
             | 
| 880 | 
            +
             | 
| 874 881 | 
             
                    console.log('📄 File viewer request sent:', {
         | 
| 875 882 | 
             
                        filePath,
         | 
| 876 883 | 
             
                        workingDir
         | 
| 877 884 | 
             
                    });
         | 
| 878 | 
            -
             | 
| 885 | 
            +
             | 
| 879 886 | 
             
                    // Wait for response
         | 
| 880 887 | 
             
                    const result = await responsePromise;
         | 
| 881 888 | 
             
                    console.log('📦 File content received:', result);
         | 
| 882 | 
            -
             | 
| 889 | 
            +
             | 
| 883 890 | 
             
                    // Hide loading
         | 
| 884 891 | 
             
                    modal.querySelector('.file-viewer-loading').style.display = 'none';
         | 
| 885 | 
            -
             | 
| 892 | 
            +
             | 
| 886 893 | 
             
                    // Show successful content
         | 
| 887 894 | 
             
                    displayFileContent(modal, result);
         | 
| 888 | 
            -
             | 
| 895 | 
            +
             | 
| 889 896 | 
             
                } catch (error) {
         | 
| 890 897 | 
             
                    console.error('❌ Failed to fetch file content:', error);
         | 
| 891 | 
            -
             | 
| 898 | 
            +
             | 
| 892 899 | 
             
                    modal.querySelector('.file-viewer-loading').style.display = 'none';
         | 
| 893 | 
            -
             | 
| 900 | 
            +
             | 
| 894 901 | 
             
                    // Create detailed error message
         | 
| 895 902 | 
             
                    let errorMessage = error.message || 'Unknown error occurred';
         | 
| 896 903 | 
             
                    let suggestions = [];
         | 
| 897 | 
            -
             | 
| 904 | 
            +
             | 
| 898 905 | 
             
                    if (error.message.includes('No socket connection')) {
         | 
| 899 906 | 
             
                        errorMessage = 'Failed to connect to the monitoring server';
         | 
| 900 907 | 
             
                        suggestions = [
         | 
| @@ -923,7 +930,7 @@ async function updateFileViewerModal(modal, filePath, workingDir) { | |
| 923 930 | 
             
                            'File access is restricted for security reasons'
         | 
| 924 931 | 
             
                        ];
         | 
| 925 932 | 
             
                    }
         | 
| 926 | 
            -
             | 
| 933 | 
            +
             | 
| 927 934 | 
             
                    displayFileError(modal, {
         | 
| 928 935 | 
             
                        error: errorMessage,
         | 
| 929 936 | 
             
                        file_path: filePath,
         | 
| @@ -940,17 +947,17 @@ function displayFileContent(modal, result) { | |
| 940 947 | 
             
                const encodingElement = modal.querySelector('.file-encoding');
         | 
| 941 948 | 
             
                const fileSizeElement = modal.querySelector('.file-viewer-file-size');
         | 
| 942 949 | 
             
                const codeElement = modal.querySelector('.file-content-code');
         | 
| 943 | 
            -
             | 
| 950 | 
            +
             | 
| 944 951 | 
             
                // Update metadata
         | 
| 945 952 | 
             
                if (extensionElement) extensionElement.textContent = `Type: ${result.extension || 'unknown'}`;
         | 
| 946 953 | 
             
                if (encodingElement) encodingElement.textContent = `Encoding: ${result.encoding || 'unknown'}`;
         | 
| 947 954 | 
             
                if (fileSizeElement) fileSizeElement.textContent = `Size: ${formatFileSize(result.file_size)}`;
         | 
| 948 | 
            -
             | 
| 955 | 
            +
             | 
| 949 956 | 
             
                // Update content with basic syntax highlighting
         | 
| 950 957 | 
             
                if (codeElement && result.content) {
         | 
| 951 958 | 
             
                    console.log('💡 Setting file content, length:', result.content.length);
         | 
| 952 959 | 
             
                    codeElement.innerHTML = highlightCode(result.content, result.extension);
         | 
| 953 | 
            -
             | 
| 960 | 
            +
             | 
| 954 961 | 
             
                    // Force scrolling to work by setting explicit heights
         | 
| 955 962 | 
             
                    const wrapper = modal.querySelector('.file-viewer-scroll-wrapper');
         | 
| 956 963 | 
             
                    if (wrapper) {
         | 
| @@ -959,20 +966,20 @@ function displayFileContent(modal, result) { | |
| 959 966 | 
             
                            const modalContent = modal.querySelector('.modal-content');
         | 
| 960 967 | 
             
                            const header = modal.querySelector('.file-viewer-header');
         | 
| 961 968 | 
             
                            const toolbar = modal.querySelector('.file-viewer-toolbar');
         | 
| 962 | 
            -
             | 
| 969 | 
            +
             | 
| 963 970 | 
             
                            const modalHeight = modalContent?.offsetHeight || 0;
         | 
| 964 971 | 
             
                            const headerHeight = header?.offsetHeight || 0;
         | 
| 965 972 | 
             
                            const toolbarHeight = toolbar?.offsetHeight || 0;
         | 
| 966 | 
            -
             | 
| 973 | 
            +
             | 
| 967 974 | 
             
                            const availableHeight = modalHeight - headerHeight - toolbarHeight - 40; // 40px for padding
         | 
| 968 | 
            -
             | 
| 975 | 
            +
             | 
| 969 976 | 
             
                            console.log('🎯 Setting file viewer scroll height:', {
         | 
| 970 977 | 
             
                                modalHeight,
         | 
| 971 978 | 
             
                                headerHeight,
         | 
| 972 979 | 
             
                                toolbarHeight,
         | 
| 973 980 | 
             
                                availableHeight
         | 
| 974 981 | 
             
                            });
         | 
| 975 | 
            -
             | 
| 982 | 
            +
             | 
| 976 983 | 
             
                            wrapper.style.maxHeight = `${availableHeight}px`;
         | 
| 977 984 | 
             
                            wrapper.style.overflowY = 'auto';
         | 
| 978 985 | 
             
                        }, 50);
         | 
| @@ -980,7 +987,7 @@ function displayFileContent(modal, result) { | |
| 980 987 | 
             
                } else {
         | 
| 981 988 | 
             
                    console.warn('⚠️ Missing codeElement or file content');
         | 
| 982 989 | 
             
                }
         | 
| 983 | 
            -
             | 
| 990 | 
            +
             | 
| 984 991 | 
             
                // Show content area
         | 
| 985 992 | 
             
                if (contentArea) {
         | 
| 986 993 | 
             
                    contentArea.style.display = 'block';
         | 
| @@ -992,15 +999,15 @@ function displayFileError(modal, result) { | |
| 992 999 | 
             
                const errorArea = modal.querySelector('.file-viewer-error');
         | 
| 993 1000 | 
             
                const messageElement = modal.querySelector('.error-message');
         | 
| 994 1001 | 
             
                const suggestionsElement = modal.querySelector('.error-suggestions');
         | 
| 995 | 
            -
             | 
| 1002 | 
            +
             | 
| 996 1003 | 
             
                let errorMessage = result.error || 'Unknown error occurred';
         | 
| 997 | 
            -
             | 
| 1004 | 
            +
             | 
| 998 1005 | 
             
                messageElement.innerHTML = `
         | 
| 999 1006 | 
             
                    <div class="error-main">${errorMessage}</div>
         | 
| 1000 1007 | 
             
                    ${result.file_path ? `<div class="error-file">File: ${result.file_path}</div>` : ''}
         | 
| 1001 1008 | 
             
                    ${result.working_dir ? `<div class="error-dir">Working directory: ${result.working_dir}</div>` : ''}
         | 
| 1002 1009 | 
             
                `;
         | 
| 1003 | 
            -
             | 
| 1010 | 
            +
             | 
| 1004 1011 | 
             
                if (result.suggestions && result.suggestions.length > 0) {
         | 
| 1005 1012 | 
             
                    suggestionsElement.innerHTML = `
         | 
| 1006 1013 | 
             
                        <h4>Suggestions:</h4>
         | 
| @@ -1011,13 +1018,13 @@ function displayFileError(modal, result) { | |
| 1011 1018 | 
             
                } else {
         | 
| 1012 1019 | 
             
                    suggestionsElement.innerHTML = '';
         | 
| 1013 1020 | 
             
                }
         | 
| 1014 | 
            -
             | 
| 1021 | 
            +
             | 
| 1015 1022 | 
             
                console.log('📋 Displaying file viewer error:', {
         | 
| 1016 1023 | 
             
                    originalError: result.error,
         | 
| 1017 1024 | 
             
                    processedMessage: errorMessage,
         | 
| 1018 1025 | 
             
                    suggestions: result.suggestions
         | 
| 1019 1026 | 
             
                });
         | 
| 1020 | 
            -
             | 
| 1027 | 
            +
             | 
| 1021 1028 | 
             
                errorArea.style.display = 'block';
         | 
| 1022 1029 | 
             
            }
         | 
| 1023 1030 |  | 
| @@ -1028,13 +1035,13 @@ function highlightCode(code, extension) { | |
| 1028 1035 | 
             
                 * This is a simple implementation that can be enhanced with full syntax highlighting
         | 
| 1029 1036 | 
             
                 * libraries like highlight.js or Prism.js if needed.
         | 
| 1030 1037 | 
             
                 */
         | 
| 1031 | 
            -
             | 
| 1038 | 
            +
             | 
| 1032 1039 | 
             
                // Escape HTML entities first
         | 
| 1033 1040 | 
             
                const escaped = code
         | 
| 1034 1041 | 
             
                    .replace(/&/g, '&')
         | 
| 1035 1042 | 
             
                    .replace(/</g, '<')
         | 
| 1036 1043 | 
             
                    .replace(/>/g, '>');
         | 
| 1037 | 
            -
             | 
| 1044 | 
            +
             | 
| 1038 1045 | 
             
                // Basic highlighting based on file extension
         | 
| 1039 1046 | 
             
                switch (extension) {
         | 
| 1040 1047 | 
             
                    case '.js':
         | 
| @@ -1110,7 +1117,7 @@ function highlightMarkdown(code) { | |
| 1110 1117 |  | 
| 1111 1118 | 
             
            function addLineNumbers(code) {
         | 
| 1112 1119 | 
             
                const lines = code.split('\n');
         | 
| 1113 | 
            -
                return lines.map((line, index) => | 
| 1120 | 
            +
                return lines.map((line, index) =>
         | 
| 1114 1121 | 
             
                    `<span class="line-number">${String(index + 1).padStart(3, ' ')}</span> ${line || ' '}`
         | 
| 1115 1122 | 
             
                ).join('\n');
         | 
| 1116 1123 | 
             
            }
         | 
| @@ -1129,17 +1136,17 @@ window.showGitDiffModal = function(filePath, timestamp, workingDir) { | |
| 1129 1136 | 
             
                if (!workingDir && window.dashboard && window.dashboard.currentWorkingDir) {
         | 
| 1130 1137 | 
             
                    workingDir = window.dashboard.currentWorkingDir;
         | 
| 1131 1138 | 
             
                }
         | 
| 1132 | 
            -
             | 
| 1139 | 
            +
             | 
| 1133 1140 | 
             
                // Create modal if it doesn't exist
         | 
| 1134 1141 | 
             
                let modal = document.getElementById('git-diff-modal');
         | 
| 1135 1142 | 
             
                if (!modal) {
         | 
| 1136 1143 | 
             
                    modal = createGitDiffModal();
         | 
| 1137 1144 | 
             
                    document.body.appendChild(modal);
         | 
| 1138 1145 | 
             
                }
         | 
| 1139 | 
            -
             | 
| 1146 | 
            +
             | 
| 1140 1147 | 
             
                // Update modal content
         | 
| 1141 1148 | 
             
                updateGitDiffModal(modal, filePath, timestamp, workingDir);
         | 
| 1142 | 
            -
             | 
| 1149 | 
            +
             | 
| 1143 1150 | 
             
                // Show the modal as flex container
         | 
| 1144 1151 | 
             
                modal.style.display = 'flex';
         | 
| 1145 1152 | 
             
                document.body.style.overflow = 'hidden'; // Prevent background scrolling
         | 
| @@ -1156,12 +1163,12 @@ window.hideGitDiffModal = function() { | |
| 1156 1163 | 
             
            window.copyGitDiff = function() {
         | 
| 1157 1164 | 
             
                const modal = document.getElementById('git-diff-modal');
         | 
| 1158 1165 | 
             
                if (!modal) return;
         | 
| 1159 | 
            -
             | 
| 1166 | 
            +
             | 
| 1160 1167 | 
             
                const codeElement = modal.querySelector('.git-diff-code');
         | 
| 1161 1168 | 
             
                if (!codeElement) return;
         | 
| 1162 | 
            -
             | 
| 1169 | 
            +
             | 
| 1163 1170 | 
             
                const text = codeElement.textContent;
         | 
| 1164 | 
            -
             | 
| 1171 | 
            +
             | 
| 1165 1172 | 
             
                if (navigator.clipboard && navigator.clipboard.writeText) {
         | 
| 1166 1173 | 
             
                    navigator.clipboard.writeText(text).then(() => {
         | 
| 1167 1174 | 
             
                        // Show brief feedback
         | 
| @@ -1182,7 +1189,7 @@ window.copyGitDiff = function() { | |
| 1182 1189 | 
             
                    textarea.select();
         | 
| 1183 1190 | 
             
                    document.execCommand('copy');
         | 
| 1184 1191 | 
             
                    document.body.removeChild(textarea);
         | 
| 1185 | 
            -
             | 
| 1192 | 
            +
             | 
| 1186 1193 | 
             
                    const button = modal.querySelector('.git-diff-copy');
         | 
| 1187 1194 | 
             
                    const originalText = button.textContent;
         | 
| 1188 1195 | 
             
                    button.textContent = '✅ Copied!';
         | 
| @@ -1196,7 +1203,7 @@ function createGitDiffModal() { | |
| 1196 1203 | 
             
                const modal = document.createElement('div');
         | 
| 1197 1204 | 
             
                modal.id = 'git-diff-modal';
         | 
| 1198 1205 | 
             
                modal.className = 'modal git-diff-modal';
         | 
| 1199 | 
            -
             | 
| 1206 | 
            +
             | 
| 1200 1207 | 
             
                modal.innerHTML = `
         | 
| 1201 1208 | 
             
                    <div class="modal-content git-diff-content">
         | 
| 1202 1209 | 
             
                        <div class="git-diff-header">
         | 
| @@ -1241,21 +1248,21 @@ function createGitDiffModal() { | |
| 1241 1248 | 
             
                        </div>
         | 
| 1242 1249 | 
             
                    </div>
         | 
| 1243 1250 | 
             
                `;
         | 
| 1244 | 
            -
             | 
| 1251 | 
            +
             | 
| 1245 1252 | 
             
                // Close modal when clicking outside
         | 
| 1246 1253 | 
             
                modal.addEventListener('click', (e) => {
         | 
| 1247 1254 | 
             
                    if (e.target === modal) {
         | 
| 1248 1255 | 
             
                        hideGitDiffModal();
         | 
| 1249 1256 | 
             
                    }
         | 
| 1250 1257 | 
             
                });
         | 
| 1251 | 
            -
             | 
| 1258 | 
            +
             | 
| 1252 1259 | 
             
                // Close modal with Escape key
         | 
| 1253 1260 | 
             
                document.addEventListener('keydown', (e) => {
         | 
| 1254 1261 | 
             
                    if (e.key === 'Escape' && modal.style.display === 'flex') {
         | 
| 1255 1262 | 
             
                        hideGitDiffModal();
         | 
| 1256 1263 | 
             
                    }
         | 
| 1257 1264 | 
             
                });
         | 
| 1258 | 
            -
             | 
| 1265 | 
            +
             | 
| 1259 1266 | 
             
                return modal;
         | 
| 1260 1267 | 
             
            }
         | 
| 1261 1268 |  | 
| @@ -1263,19 +1270,19 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1263 1270 | 
             
                // Update header info
         | 
| 1264 1271 | 
             
                const filePathElement = modal.querySelector('.git-diff-file-path');
         | 
| 1265 1272 | 
             
                const timestampElement = modal.querySelector('.git-diff-timestamp');
         | 
| 1266 | 
            -
             | 
| 1273 | 
            +
             | 
| 1267 1274 | 
             
                filePathElement.textContent = filePath;
         | 
| 1268 1275 | 
             
                timestampElement.textContent = timestamp ? new Date(timestamp).toLocaleString() : 'Latest';
         | 
| 1269 | 
            -
             | 
| 1276 | 
            +
             | 
| 1270 1277 | 
             
                // Show loading state
         | 
| 1271 1278 | 
             
                modal.querySelector('.git-diff-loading').style.display = 'flex';
         | 
| 1272 1279 | 
             
                modal.querySelector('.git-diff-error').style.display = 'none';
         | 
| 1273 1280 | 
             
                modal.querySelector('.git-diff-content-area').style.display = 'none';
         | 
| 1274 | 
            -
             | 
| 1281 | 
            +
             | 
| 1275 1282 | 
             
                try {
         | 
| 1276 1283 | 
             
                    // Get the Socket.IO server port with multiple fallbacks
         | 
| 1277 1284 | 
             
                    let port = 8765; // Default fallback
         | 
| 1278 | 
            -
             | 
| 1285 | 
            +
             | 
| 1279 1286 | 
             
                    // Try to get port from socketClient first
         | 
| 1280 1287 | 
             
                    if (window.dashboard && window.dashboard.socketClient && window.dashboard.socketClient.port) {
         | 
| 1281 1288 | 
             
                        port = window.dashboard.socketClient.port;
         | 
| @@ -1287,19 +1294,19 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1287 1294 | 
             
                            port = portInput.value;
         | 
| 1288 1295 | 
             
                        }
         | 
| 1289 1296 | 
             
                    }
         | 
| 1290 | 
            -
             | 
| 1297 | 
            +
             | 
| 1291 1298 | 
             
                    // Build URL parameters
         | 
| 1292 1299 | 
             
                    const params = new URLSearchParams({
         | 
| 1293 1300 | 
             
                        file: filePath
         | 
| 1294 1301 | 
             
                    });
         | 
| 1295 | 
            -
             | 
| 1302 | 
            +
             | 
| 1296 1303 | 
             
                    if (timestamp) {
         | 
| 1297 1304 | 
             
                        params.append('timestamp', timestamp);
         | 
| 1298 1305 | 
             
                    }
         | 
| 1299 1306 | 
             
                    if (workingDir) {
         | 
| 1300 1307 | 
             
                        params.append('working_dir', workingDir);
         | 
| 1301 1308 | 
             
                    }
         | 
| 1302 | 
            -
             | 
| 1309 | 
            +
             | 
| 1303 1310 | 
             
                    const requestUrl = `http://localhost:${port}/api/git-diff?${params}`;
         | 
| 1304 1311 | 
             
                    console.log('🌐 Making git diff request to:', requestUrl);
         | 
| 1305 1312 | 
             
                    console.log('📋 Git diff request parameters:', {
         | 
| @@ -1308,7 +1315,7 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1308 1315 | 
             
                        workingDir,
         | 
| 1309 1316 | 
             
                        urlParams: params.toString()
         | 
| 1310 1317 | 
             
                    });
         | 
| 1311 | 
            -
             | 
| 1318 | 
            +
             | 
| 1312 1319 | 
             
                    // Test server connectivity first
         | 
| 1313 1320 | 
             
                    try {
         | 
| 1314 1321 | 
             
                        const healthResponse = await fetch(`http://localhost:${port}/health`, {
         | 
| @@ -1319,16 +1326,16 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1319 1326 | 
             
                            },
         | 
| 1320 1327 | 
             
                            mode: 'cors'
         | 
| 1321 1328 | 
             
                        });
         | 
| 1322 | 
            -
             | 
| 1329 | 
            +
             | 
| 1323 1330 | 
             
                        if (!healthResponse.ok) {
         | 
| 1324 1331 | 
             
                            throw new Error(`Server health check failed: ${healthResponse.status} ${healthResponse.statusText}`);
         | 
| 1325 1332 | 
             
                        }
         | 
| 1326 | 
            -
             | 
| 1333 | 
            +
             | 
| 1327 1334 | 
             
                        console.log('✅ Server health check passed');
         | 
| 1328 1335 | 
             
                    } catch (healthError) {
         | 
| 1329 1336 | 
             
                        throw new Error(`Cannot reach server at localhost:${port}. Health check failed: ${healthError.message}`);
         | 
| 1330 1337 | 
             
                    }
         | 
| 1331 | 
            -
             | 
| 1338 | 
            +
             | 
| 1332 1339 | 
             
                    // Make the actual git diff request
         | 
| 1333 1340 | 
             
                    const response = await fetch(requestUrl, {
         | 
| 1334 1341 | 
             
                        method: 'GET',
         | 
| @@ -1338,17 +1345,17 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1338 1345 | 
             
                        },
         | 
| 1339 1346 | 
             
                        mode: 'cors'
         | 
| 1340 1347 | 
             
                    });
         | 
| 1341 | 
            -
             | 
| 1348 | 
            +
             | 
| 1342 1349 | 
             
                    if (!response.ok) {
         | 
| 1343 1350 | 
             
                        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
         | 
| 1344 1351 | 
             
                    }
         | 
| 1345 | 
            -
             | 
| 1352 | 
            +
             | 
| 1346 1353 | 
             
                    const result = await response.json();
         | 
| 1347 1354 | 
             
                    console.log('📦 Git diff response:', result);
         | 
| 1348 | 
            -
             | 
| 1355 | 
            +
             | 
| 1349 1356 | 
             
                    // Hide loading
         | 
| 1350 1357 | 
             
                    modal.querySelector('.git-diff-loading').style.display = 'none';
         | 
| 1351 | 
            -
             | 
| 1358 | 
            +
             | 
| 1352 1359 | 
             
                    if (result.success) {
         | 
| 1353 1360 | 
             
                        console.log('📊 Displaying successful git diff');
         | 
| 1354 1361 | 
             
                        // Show successful diff
         | 
| @@ -1358,7 +1365,7 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1358 1365 | 
             
                        // Show error
         | 
| 1359 1366 | 
             
                        displayGitDiffError(modal, result);
         | 
| 1360 1367 | 
             
                    }
         | 
| 1361 | 
            -
             | 
| 1368 | 
            +
             | 
| 1362 1369 | 
             
                } catch (error) {
         | 
| 1363 1370 | 
             
                    console.error('❌ Failed to fetch git diff:', error);
         | 
| 1364 1371 | 
             
                    console.error('Error details:', {
         | 
| @@ -1369,13 +1376,13 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1369 1376 | 
             
                        timestamp,
         | 
| 1370 1377 | 
             
                        workingDir
         | 
| 1371 1378 | 
             
                    });
         | 
| 1372 | 
            -
             | 
| 1379 | 
            +
             | 
| 1373 1380 | 
             
                    modal.querySelector('.git-diff-loading').style.display = 'none';
         | 
| 1374 | 
            -
             | 
| 1381 | 
            +
             | 
| 1375 1382 | 
             
                    // Create detailed error message based on error type
         | 
| 1376 1383 | 
             
                    let errorMessage = `Network error: ${error.message}`;
         | 
| 1377 1384 | 
             
                    let suggestions = [];
         | 
| 1378 | 
            -
             | 
| 1385 | 
            +
             | 
| 1379 1386 | 
             
                    if (error.message.includes('Failed to fetch')) {
         | 
| 1380 1387 | 
             
                        errorMessage = 'Failed to connect to the monitoring server';
         | 
| 1381 1388 | 
             
                        suggestions = [
         | 
| @@ -1399,7 +1406,7 @@ async function updateGitDiffModal(modal, filePath, timestamp, workingDir) { | |
| 1399 1406 | 
             
                            'Try with a different file or working directory'
         | 
| 1400 1407 | 
             
                        ];
         | 
| 1401 1408 | 
             
                    }
         | 
| 1402 | 
            -
             | 
| 1409 | 
            +
             | 
| 1403 1410 | 
             
                    displayGitDiffError(modal, {
         | 
| 1404 1411 | 
             
                        error: errorMessage,
         | 
| 1405 1412 | 
             
                        file_path: filePath,
         | 
| @@ -1420,7 +1427,7 @@ function highlightGitDiff(diffText) { | |
| 1420 1427 | 
             
                 * Apply basic syntax highlighting to git diff output
         | 
| 1421 1428 | 
             
                 * WHY: Git diffs have a standard format that can be highlighted for better readability:
         | 
| 1422 1429 | 
             
                 * - Lines starting with '+' are additions (green)
         | 
| 1423 | 
            -
                 * - Lines starting with '-' are deletions (red) | 
| 1430 | 
            +
                 * - Lines starting with '-' are deletions (red)
         | 
| 1424 1431 | 
             
                 * - Lines starting with '@@' are context headers (blue)
         | 
| 1425 1432 | 
             
                 * - File headers and metadata get special formatting
         | 
| 1426 1433 | 
             
                 */
         | 
| @@ -1432,7 +1439,7 @@ function highlightGitDiff(diffText) { | |
| 1432 1439 | 
             
                            .replace(/&/g, '&')
         | 
| 1433 1440 | 
             
                            .replace(/</g, '<')
         | 
| 1434 1441 | 
             
                            .replace(/>/g, '>');
         | 
| 1435 | 
            -
             | 
| 1442 | 
            +
             | 
| 1436 1443 | 
             
                        // Apply diff highlighting
         | 
| 1437 1444 | 
             
                        if (line.startsWith('+++') || line.startsWith('---')) {
         | 
| 1438 1445 | 
             
                            return `<span class="diff-header">${escaped}</span>`;
         | 
| @@ -1457,23 +1464,23 @@ function displayGitDiff(modal, result) { | |
| 1457 1464 | 
             
                const commitHashElement = modal.querySelector('.commit-hash');
         | 
| 1458 1465 | 
             
                const methodElement = modal.querySelector('.diff-method');
         | 
| 1459 1466 | 
             
                const codeElement = modal.querySelector('.git-diff-code');
         | 
| 1460 | 
            -
             | 
| 1467 | 
            +
             | 
| 1461 1468 | 
             
                console.log('🔍 Elements found:', {
         | 
| 1462 1469 | 
             
                    contentArea: !!contentArea,
         | 
| 1463 1470 | 
             
                    commitHashElement: !!commitHashElement,
         | 
| 1464 1471 | 
             
                    methodElement: !!methodElement,
         | 
| 1465 1472 | 
             
                    codeElement: !!codeElement
         | 
| 1466 1473 | 
             
                });
         | 
| 1467 | 
            -
             | 
| 1474 | 
            +
             | 
| 1468 1475 | 
             
                // Update metadata
         | 
| 1469 1476 | 
             
                if (commitHashElement) commitHashElement.textContent = `Commit: ${result.commit_hash}`;
         | 
| 1470 1477 | 
             
                if (methodElement) methodElement.textContent = `Method: ${result.method}`;
         | 
| 1471 | 
            -
             | 
| 1478 | 
            +
             | 
| 1472 1479 | 
             
                // Update diff content with basic syntax highlighting
         | 
| 1473 1480 | 
             
                if (codeElement && result.diff) {
         | 
| 1474 1481 | 
             
                    console.log('💡 Setting diff content, length:', result.diff.length);
         | 
| 1475 1482 | 
             
                    codeElement.innerHTML = highlightGitDiff(result.diff);
         | 
| 1476 | 
            -
             | 
| 1483 | 
            +
             | 
| 1477 1484 | 
             
                    // Force scrolling to work by setting explicit heights
         | 
| 1478 1485 | 
             
                    const wrapper = modal.querySelector('.git-diff-scroll-wrapper');
         | 
| 1479 1486 | 
             
                    if (wrapper) {
         | 
| @@ -1482,20 +1489,20 @@ function displayGitDiff(modal, result) { | |
| 1482 1489 | 
             
                            const modalContent = modal.querySelector('.modal-content');
         | 
| 1483 1490 | 
             
                            const header = modal.querySelector('.git-diff-header');
         | 
| 1484 1491 | 
             
                            const toolbar = modal.querySelector('.git-diff-toolbar');
         | 
| 1485 | 
            -
             | 
| 1492 | 
            +
             | 
| 1486 1493 | 
             
                            const modalHeight = modalContent?.offsetHeight || 0;
         | 
| 1487 1494 | 
             
                            const headerHeight = header?.offsetHeight || 0;
         | 
| 1488 1495 | 
             
                            const toolbarHeight = toolbar?.offsetHeight || 0;
         | 
| 1489 | 
            -
             | 
| 1496 | 
            +
             | 
| 1490 1497 | 
             
                            const availableHeight = modalHeight - headerHeight - toolbarHeight - 40; // 40px for padding
         | 
| 1491 | 
            -
             | 
| 1498 | 
            +
             | 
| 1492 1499 | 
             
                            console.log('🎯 Setting explicit scroll height:', {
         | 
| 1493 1500 | 
             
                                modalHeight,
         | 
| 1494 1501 | 
             
                                headerHeight,
         | 
| 1495 1502 | 
             
                                toolbarHeight,
         | 
| 1496 1503 | 
             
                                availableHeight
         | 
| 1497 1504 | 
             
                            });
         | 
| 1498 | 
            -
             | 
| 1505 | 
            +
             | 
| 1499 1506 | 
             
                            wrapper.style.maxHeight = `${availableHeight}px`;
         | 
| 1500 1507 | 
             
                            wrapper.style.overflowY = 'auto';
         | 
| 1501 1508 | 
             
                        }, 50);
         | 
| @@ -1503,7 +1510,7 @@ function displayGitDiff(modal, result) { | |
| 1503 1510 | 
             
                } else {
         | 
| 1504 1511 | 
             
                    console.warn('⚠️ Missing codeElement or diff data');
         | 
| 1505 1512 | 
             
                }
         | 
| 1506 | 
            -
             | 
| 1513 | 
            +
             | 
| 1507 1514 | 
             
                // Show content area
         | 
| 1508 1515 | 
             
                if (contentArea) {
         | 
| 1509 1516 | 
             
                    contentArea.style.display = 'block';
         | 
| @@ -1515,24 +1522,24 @@ function displayGitDiffError(modal, result) { | |
| 1515 1522 | 
             
                const errorArea = modal.querySelector('.git-diff-error');
         | 
| 1516 1523 | 
             
                const messageElement = modal.querySelector('.error-message');
         | 
| 1517 1524 | 
             
                const suggestionsElement = modal.querySelector('.error-suggestions');
         | 
| 1518 | 
            -
             | 
| 1525 | 
            +
             | 
| 1519 1526 | 
             
                // Create more user-friendly error messages
         | 
| 1520 1527 | 
             
                let errorMessage = result.error || 'Unknown error occurred';
         | 
| 1521 1528 | 
             
                let isUntracked = false;
         | 
| 1522 | 
            -
             | 
| 1529 | 
            +
             | 
| 1523 1530 | 
             
                if (errorMessage.includes('not tracked by git')) {
         | 
| 1524 1531 | 
             
                    errorMessage = '📝 This file is not tracked by git yet';
         | 
| 1525 1532 | 
             
                    isUntracked = true;
         | 
| 1526 1533 | 
             
                } else if (errorMessage.includes('No git history found')) {
         | 
| 1527 1534 | 
             
                    errorMessage = '📋 No git history available for this file';
         | 
| 1528 1535 | 
             
                }
         | 
| 1529 | 
            -
             | 
| 1536 | 
            +
             | 
| 1530 1537 | 
             
                messageElement.innerHTML = `
         | 
| 1531 1538 | 
             
                    <div class="error-main">${errorMessage}</div>
         | 
| 1532 1539 | 
             
                    ${result.file_path ? `<div class="error-file">File: ${result.file_path}</div>` : ''}
         | 
| 1533 1540 | 
             
                    ${result.working_dir ? `<div class="error-dir">Working directory: ${result.working_dir}</div>` : ''}
         | 
| 1534 1541 | 
             
                `;
         | 
| 1535 | 
            -
             | 
| 1542 | 
            +
             | 
| 1536 1543 | 
             
                if (result.suggestions && result.suggestions.length > 0) {
         | 
| 1537 1544 | 
             
                    const suggestionTitle = isUntracked ? 'How to track this file:' : 'Suggestions:';
         | 
| 1538 1545 | 
             
                    suggestionsElement.innerHTML = `
         | 
| @@ -1544,14 +1551,14 @@ function displayGitDiffError(modal, result) { | |
| 1544 1551 | 
             
                } else {
         | 
| 1545 1552 | 
             
                    suggestionsElement.innerHTML = '';
         | 
| 1546 1553 | 
             
                }
         | 
| 1547 | 
            -
             | 
| 1554 | 
            +
             | 
| 1548 1555 | 
             
                console.log('📋 Displaying git diff error:', {
         | 
| 1549 1556 | 
             
                    originalError: result.error,
         | 
| 1550 1557 | 
             
                    processedMessage: errorMessage,
         | 
| 1551 1558 | 
             
                    isUntracked,
         | 
| 1552 1559 | 
             
                    suggestions: result.suggestions
         | 
| 1553 1560 | 
             
                });
         | 
| 1554 | 
            -
             | 
| 1561 | 
            +
             | 
| 1555 1562 | 
             
                errorArea.style.display = 'block';
         | 
| 1556 1563 | 
             
            }
         | 
| 1557 1564 |  | 
| @@ -1562,17 +1569,17 @@ window.showFileViewerModal = function(filePath) { | |
| 1562 1569 | 
             
                if (window.dashboard && window.dashboard.currentWorkingDir) {
         | 
| 1563 1570 | 
             
                    workingDir = window.dashboard.currentWorkingDir;
         | 
| 1564 1571 | 
             
                }
         | 
| 1565 | 
            -
             | 
| 1572 | 
            +
             | 
| 1566 1573 | 
             
                // Create modal if it doesn't exist
         | 
| 1567 1574 | 
             
                let modal = document.getElementById('file-viewer-modal');
         | 
| 1568 1575 | 
             
                if (!modal) {
         | 
| 1569 1576 | 
             
                    modal = createFileViewerModal();
         | 
| 1570 1577 | 
             
                    document.body.appendChild(modal);
         | 
| 1571 1578 | 
             
                }
         | 
| 1572 | 
            -
             | 
| 1579 | 
            +
             | 
| 1573 1580 | 
             
                // Update modal content
         | 
| 1574 1581 | 
             
                updateFileViewerModal(modal, filePath, workingDir);
         | 
| 1575 | 
            -
             | 
| 1582 | 
            +
             | 
| 1576 1583 | 
             
                // Show the modal as flex container
         | 
| 1577 1584 | 
             
                modal.style.display = 'flex';
         | 
| 1578 1585 | 
             
                document.body.style.overflow = 'hidden'; // Prevent background scrolling
         | 
| @@ -1589,12 +1596,12 @@ window.hideFileViewerModal = function() { | |
| 1589 1596 | 
             
            window.copyFileContent = function() {
         | 
| 1590 1597 | 
             
                const modal = document.getElementById('file-viewer-modal');
         | 
| 1591 1598 | 
             
                if (!modal) return;
         | 
| 1592 | 
            -
             | 
| 1599 | 
            +
             | 
| 1593 1600 | 
             
                const codeElement = modal.querySelector('.file-content-code');
         | 
| 1594 1601 | 
             
                if (!codeElement) return;
         | 
| 1595 | 
            -
             | 
| 1602 | 
            +
             | 
| 1596 1603 | 
             
                const text = codeElement.textContent;
         | 
| 1597 | 
            -
             | 
| 1604 | 
            +
             | 
| 1598 1605 | 
             
                if (navigator.clipboard && navigator.clipboard.writeText) {
         | 
| 1599 1606 | 
             
                    navigator.clipboard.writeText(text).then(() => {
         | 
| 1600 1607 | 
             
                        // Show brief feedback
         | 
| @@ -1615,7 +1622,7 @@ window.copyFileContent = function() { | |
| 1615 1622 | 
             
                    textarea.select();
         | 
| 1616 1623 | 
             
                    document.execCommand('copy');
         | 
| 1617 1624 | 
             
                    document.body.removeChild(textarea);
         | 
| 1618 | 
            -
             | 
| 1625 | 
            +
             | 
| 1619 1626 | 
             
                    const button = modal.querySelector('.file-content-copy');
         | 
| 1620 1627 | 
             
                    const originalText = button.textContent;
         | 
| 1621 1628 | 
             
                    button.textContent = '✅ Copied!';
         | 
| @@ -1625,303 +1632,17 @@ window.copyFileContent = function() { | |
| 1625 1632 | 
             
                }
         | 
| 1626 1633 | 
             
            };
         | 
| 1627 1634 |  | 
| 1628 | 
            -
            function createFileViewerModal() {
         | 
| 1629 | 
            -
                const modal = document.createElement('div');
         | 
| 1630 | 
            -
                modal.id = 'file-viewer-modal';
         | 
| 1631 | 
            -
                modal.className = 'modal file-viewer-modal';
         | 
| 1632 | 
            -
                
         | 
| 1633 | 
            -
                modal.innerHTML = `
         | 
| 1634 | 
            -
                    <div class="modal-content file-viewer-content">
         | 
| 1635 | 
            -
                        <div class="file-viewer-header">
         | 
| 1636 | 
            -
                            <h2 class="file-viewer-title">
         | 
| 1637 | 
            -
                                <span class="file-viewer-icon">👁️</span>
         | 
| 1638 | 
            -
                                <span class="file-viewer-title-text">File Viewer</span>
         | 
| 1639 | 
            -
                            </h2>
         | 
| 1640 | 
            -
                            <div class="file-viewer-meta">
         | 
| 1641 | 
            -
                                <span class="file-viewer-file-path"></span>
         | 
| 1642 | 
            -
                                <span class="file-viewer-file-size"></span>
         | 
| 1643 | 
            -
                            </div>
         | 
| 1644 | 
            -
                            <button class="file-viewer-close" onclick="hideFileViewerModal()">
         | 
| 1645 | 
            -
                                <span>×</span>
         | 
| 1646 | 
            -
                            </button>
         | 
| 1647 | 
            -
                        </div>
         | 
| 1648 | 
            -
                        <div class="file-viewer-body">
         | 
| 1649 | 
            -
                            <div class="file-viewer-loading">
         | 
| 1650 | 
            -
                                <div class="loading-spinner"></div>
         | 
| 1651 | 
            -
                                <span>Loading file contents...</span>
         | 
| 1652 | 
            -
                            </div>
         | 
| 1653 | 
            -
                            <div class="file-viewer-error" style="display: none;">
         | 
| 1654 | 
            -
                                <div class="error-icon">⚠️</div>
         | 
| 1655 | 
            -
                                <div class="error-message"></div>
         | 
| 1656 | 
            -
                                <div class="error-suggestions"></div>
         | 
| 1657 | 
            -
                            </div>
         | 
| 1658 | 
            -
                            <div class="file-viewer-content-area" style="display: none;">
         | 
| 1659 | 
            -
                                <div class="file-viewer-toolbar">
         | 
| 1660 | 
            -
                                    <div class="file-viewer-info">
         | 
| 1661 | 
            -
                                        <span class="file-extension"></span>
         | 
| 1662 | 
            -
                                        <span class="file-encoding">UTF-8</span>
         | 
| 1663 | 
            -
                                    </div>
         | 
| 1664 | 
            -
                                    <div class="file-viewer-actions">
         | 
| 1665 | 
            -
                                        <button class="file-content-copy" onclick="copyFileContent()">
         | 
| 1666 | 
            -
                                            📋 Copy
         | 
| 1667 | 
            -
                                        </button>
         | 
| 1668 | 
            -
                                    </div>
         | 
| 1669 | 
            -
                                </div>
         | 
| 1670 | 
            -
                                <div class="file-viewer-scroll-wrapper">
         | 
| 1671 | 
            -
                                    <pre class="file-content-display line-numbers"><code class="file-content-code"></code></pre>
         | 
| 1672 | 
            -
                                </div>
         | 
| 1673 | 
            -
                            </div>
         | 
| 1674 | 
            -
                        </div>
         | 
| 1675 | 
            -
                    </div>
         | 
| 1676 | 
            -
                `;
         | 
| 1677 | 
            -
                
         | 
| 1678 | 
            -
                // Close modal when clicking outside
         | 
| 1679 | 
            -
                modal.addEventListener('click', (e) => {
         | 
| 1680 | 
            -
                    if (e.target === modal) {
         | 
| 1681 | 
            -
                        hideFileViewerModal();
         | 
| 1682 | 
            -
                    }
         | 
| 1683 | 
            -
                });
         | 
| 1684 | 
            -
                
         | 
| 1685 | 
            -
                // Close modal with Escape key
         | 
| 1686 | 
            -
                document.addEventListener('keydown', (e) => {
         | 
| 1687 | 
            -
                    if (e.key === 'Escape' && modal.style.display === 'flex') {
         | 
| 1688 | 
            -
                        hideFileViewerModal();
         | 
| 1689 | 
            -
                    }
         | 
| 1690 | 
            -
                });
         | 
| 1691 | 
            -
                
         | 
| 1692 | 
            -
                return modal;
         | 
| 1693 | 
            -
            }
         | 
| 1694 1635 |  | 
| 1695 | 
            -
            async function updateFileViewerModal(modal, filePath, workingDir) {
         | 
| 1696 | 
            -
                // Update header info
         | 
| 1697 | 
            -
                const filePathElement = modal.querySelector('.file-viewer-file-path');
         | 
| 1698 | 
            -
                const fileSizeElement = modal.querySelector('.file-viewer-file-size');
         | 
| 1699 | 
            -
                const fileExtensionElement = modal.querySelector('.file-extension');
         | 
| 1700 | 
            -
                
         | 
| 1701 | 
            -
                filePathElement.textContent = filePath;
         | 
| 1702 | 
            -
                
         | 
| 1703 | 
            -
                // Extract and display file extension
         | 
| 1704 | 
            -
                const extension = filePath.split('.').pop() || 'txt';
         | 
| 1705 | 
            -
                fileExtensionElement.textContent = `Type: ${extension.toUpperCase()}`;
         | 
| 1706 | 
            -
                
         | 
| 1707 | 
            -
                // Show loading state
         | 
| 1708 | 
            -
                modal.querySelector('.file-viewer-loading').style.display = 'flex';
         | 
| 1709 | 
            -
                modal.querySelector('.file-viewer-error').style.display = 'none';
         | 
| 1710 | 
            -
                modal.querySelector('.file-viewer-content-area').style.display = 'none';
         | 
| 1711 | 
            -
                
         | 
| 1712 | 
            -
                try {
         | 
| 1713 | 
            -
                    // Get the Socket.IO server port with multiple fallbacks
         | 
| 1714 | 
            -
                    let port = 8765; // Default fallback
         | 
| 1715 | 
            -
                    
         | 
| 1716 | 
            -
                    // Try to get port from socketClient first
         | 
| 1717 | 
            -
                    if (window.dashboard && window.dashboard.socketClient && window.dashboard.socketClient.port) {
         | 
| 1718 | 
            -
                        port = window.dashboard.socketClient.port;
         | 
| 1719 | 
            -
                    }
         | 
| 1720 | 
            -
                    // Fallback to port input field if socketClient port is not available
         | 
| 1721 | 
            -
                    else if (document.getElementById('port-input')) {
         | 
| 1722 | 
            -
                        const portInput = document.getElementById('port-input');
         | 
| 1723 | 
            -
                        if (portInput.value && !isNaN(portInput.value)) {
         | 
| 1724 | 
            -
                            port = parseInt(portInput.value);
         | 
| 1725 | 
            -
                        }
         | 
| 1726 | 
            -
                    }
         | 
| 1727 | 
            -
                    
         | 
| 1728 | 
            -
                    // Construct API request URL for file content
         | 
| 1729 | 
            -
                    const params = new URLSearchParams();
         | 
| 1730 | 
            -
                    params.append('file_path', filePath);
         | 
| 1731 | 
            -
                    if (workingDir) {
         | 
| 1732 | 
            -
                        params.append('working_dir', workingDir);
         | 
| 1733 | 
            -
                    }
         | 
| 1734 | 
            -
                    
         | 
| 1735 | 
            -
                    const requestUrl = `http://localhost:${port}/api/file-content?${params}`;
         | 
| 1736 | 
            -
                    console.log('🌐 Making file content request to:', requestUrl);
         | 
| 1737 | 
            -
                    console.log('📄 File content request parameters:', {
         | 
| 1738 | 
            -
                        filePath,
         | 
| 1739 | 
            -
                        workingDir,
         | 
| 1740 | 
            -
                        urlParams: params.toString()
         | 
| 1741 | 
            -
                    });
         | 
| 1742 | 
            -
                    
         | 
| 1743 | 
            -
                    // Test server connectivity first
         | 
| 1744 | 
            -
                    try {
         | 
| 1745 | 
            -
                        const healthResponse = await fetch(`http://localhost:${port}/health`, {
         | 
| 1746 | 
            -
                            method: 'GET',
         | 
| 1747 | 
            -
                            mode: 'cors',
         | 
| 1748 | 
            -
                            timeout: 5000
         | 
| 1749 | 
            -
                        });
         | 
| 1750 | 
            -
                        
         | 
| 1751 | 
            -
                        if (!healthResponse.ok) {
         | 
| 1752 | 
            -
                            throw new Error(`Health check failed: ${healthResponse.status}`);
         | 
| 1753 | 
            -
                        }
         | 
| 1754 | 
            -
                        
         | 
| 1755 | 
            -
                        console.log('✅ Server health check passed');
         | 
| 1756 | 
            -
                    } catch (healthError) {
         | 
| 1757 | 
            -
                        console.warn('⚠️ Server health check failed:', healthError);
         | 
| 1758 | 
            -
                        throw new Error(`Unable to connect to monitoring server on port ${port}. Please ensure the server is running.`);
         | 
| 1759 | 
            -
                    }
         | 
| 1760 | 
            -
                    
         | 
| 1761 | 
            -
                    // Make the actual file content request
         | 
| 1762 | 
            -
                    const response = await fetch(requestUrl, {
         | 
| 1763 | 
            -
                        method: 'GET',
         | 
| 1764 | 
            -
                        headers: {
         | 
| 1765 | 
            -
                            'Accept': 'application/json',
         | 
| 1766 | 
            -
                            'Content-Type': 'application/json'
         | 
| 1767 | 
            -
                        },
         | 
| 1768 | 
            -
                        mode: 'cors'
         | 
| 1769 | 
            -
                    });
         | 
| 1770 | 
            -
                    
         | 
| 1771 | 
            -
                    if (!response.ok) {
         | 
| 1772 | 
            -
                        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
         | 
| 1773 | 
            -
                    }
         | 
| 1774 | 
            -
                    
         | 
| 1775 | 
            -
                    const result = await response.json();
         | 
| 1776 | 
            -
                    console.log('📦 File content received:', result);
         | 
| 1777 | 
            -
                    
         | 
| 1778 | 
            -
                    // Hide loading
         | 
| 1779 | 
            -
                    modal.querySelector('.file-viewer-loading').style.display = 'none';
         | 
| 1780 | 
            -
                    
         | 
| 1781 | 
            -
                    if (result.success) {
         | 
| 1782 | 
            -
                        console.log('📊 Displaying file content');
         | 
| 1783 | 
            -
                        // Show file content
         | 
| 1784 | 
            -
                        displayFileContent(modal, result, extension);
         | 
| 1785 | 
            -
                    } else {
         | 
| 1786 | 
            -
                        console.log('⚠️ Displaying file content error:', result);
         | 
| 1787 | 
            -
                        // Show error
         | 
| 1788 | 
            -
                        displayFileContentError(modal, result);
         | 
| 1789 | 
            -
                    }
         | 
| 1790 | 
            -
                    
         | 
| 1791 | 
            -
                } catch (error) {
         | 
| 1792 | 
            -
                    console.error('❌ Failed to fetch file content:', error);
         | 
| 1793 | 
            -
                    
         | 
| 1794 | 
            -
                    modal.querySelector('.file-viewer-loading').style.display = 'none';
         | 
| 1795 | 
            -
                    
         | 
| 1796 | 
            -
                    // Create detailed error message based on error type
         | 
| 1797 | 
            -
                    let errorMessage = `Network error: ${error.message}`;
         | 
| 1798 | 
            -
                    let suggestions = [];
         | 
| 1799 | 
            -
                    
         | 
| 1800 | 
            -
                    if (error.message.includes('Failed to fetch')) {
         | 
| 1801 | 
            -
                        errorMessage = 'Failed to connect to the monitoring server';
         | 
| 1802 | 
            -
                        suggestions = [
         | 
| 1803 | 
            -
                            'Check if the monitoring server is running on port 8765',
         | 
| 1804 | 
            -
                            'Verify the port configuration in the dashboard',
         | 
| 1805 | 
            -
                            'Ensure CORS is enabled on the server'
         | 
| 1806 | 
            -
                        ];
         | 
| 1807 | 
            -
                    } else if (error.message.includes('Health check failed')) {
         | 
| 1808 | 
            -
                        errorMessage = 'Unable to connect to monitoring server';
         | 
| 1809 | 
            -
                        suggestions = [
         | 
| 1810 | 
            -
                            'The server may be starting up - try again in a few seconds',
         | 
| 1811 | 
            -
                            'Check if another process is using the port',
         | 
| 1812 | 
            -
                            'Restart the claude-mpm monitoring server'
         | 
| 1813 | 
            -
                        ];
         | 
| 1814 | 
            -
                    } else if (error.message.includes('HTTP 404')) {
         | 
| 1815 | 
            -
                        errorMessage = 'File content API endpoint not found';
         | 
| 1816 | 
            -
                        suggestions = [
         | 
| 1817 | 
            -
                            'The server may be running an older version',
         | 
| 1818 | 
            -
                            'Try restarting the monitoring server',
         | 
| 1819 | 
            -
                            'Check server logs for errors'
         | 
| 1820 | 
            -
                        ];
         | 
| 1821 | 
            -
                    } else if (error.message.includes('timeout')) {
         | 
| 1822 | 
            -
                        errorMessage = 'Request timed out while fetching file content';
         | 
| 1823 | 
            -
                        suggestions = [
         | 
| 1824 | 
            -
                            'File may be too large or server is slow',
         | 
| 1825 | 
            -
                            'Try again in a moment',
         | 
| 1826 | 
            -
                            'Check server logs for performance issues'
         | 
| 1827 | 
            -
                        ];
         | 
| 1828 | 
            -
                    }
         | 
| 1829 | 
            -
                    
         | 
| 1830 | 
            -
                    displayFileContentError(modal, {
         | 
| 1831 | 
            -
                        success: false,
         | 
| 1832 | 
            -
                        error: errorMessage,
         | 
| 1833 | 
            -
                        suggestions: suggestions
         | 
| 1834 | 
            -
                    });
         | 
| 1835 | 
            -
                }
         | 
| 1836 | 
            -
            }
         | 
| 1837 1636 |  | 
| 1838 | 
            -
            function displayFileContent(modal, result, extension) {
         | 
| 1839 | 
            -
                console.log('📝 displayFileContent called with:', result);
         | 
| 1840 | 
            -
                const contentArea = modal.querySelector('.file-viewer-content-area');
         | 
| 1841 | 
            -
                const fileSizeElement = modal.querySelector('.file-viewer-file-size');
         | 
| 1842 | 
            -
                const codeElement = modal.querySelector('.file-content-code');
         | 
| 1843 | 
            -
                
         | 
| 1844 | 
            -
                console.log('🔍 File content elements found:', {
         | 
| 1845 | 
            -
                    contentArea: !!contentArea,
         | 
| 1846 | 
            -
                    fileSizeElement: !!fileSizeElement,
         | 
| 1847 | 
            -
                    codeElement: !!codeElement
         | 
| 1848 | 
            -
                });
         | 
| 1849 | 
            -
                
         | 
| 1850 | 
            -
                // Update file size
         | 
| 1851 | 
            -
                if (fileSizeElement && result.file_size) {
         | 
| 1852 | 
            -
                    const sizeInBytes = result.file_size;
         | 
| 1853 | 
            -
                    const sizeFormatted = sizeInBytes < 1024 
         | 
| 1854 | 
            -
                        ? `${sizeInBytes} bytes`
         | 
| 1855 | 
            -
                        : sizeInBytes < 1024 * 1024
         | 
| 1856 | 
            -
                        ? `${(sizeInBytes / 1024).toFixed(1)} KB`
         | 
| 1857 | 
            -
                        : `${(sizeInBytes / (1024 * 1024)).toFixed(1)} MB`;
         | 
| 1858 | 
            -
                    fileSizeElement.textContent = `Size: ${sizeFormatted}`;
         | 
| 1859 | 
            -
                }
         | 
| 1860 | 
            -
                
         | 
| 1861 | 
            -
                // Set up content with syntax highlighting
         | 
| 1862 | 
            -
                if (codeElement && result.content) {
         | 
| 1863 | 
            -
                    // Determine language for Prism.js
         | 
| 1864 | 
            -
                    const languageMap = {
         | 
| 1865 | 
            -
                        'js': 'javascript',
         | 
| 1866 | 
            -
                        'jsx': 'javascript',
         | 
| 1867 | 
            -
                        'ts': 'typescript',
         | 
| 1868 | 
            -
                        'tsx': 'typescript',
         | 
| 1869 | 
            -
                        'py': 'python',
         | 
| 1870 | 
            -
                        'rb': 'ruby',
         | 
| 1871 | 
            -
                        'php': 'php',
         | 
| 1872 | 
            -
                        'html': 'html',
         | 
| 1873 | 
            -
                        'htm': 'html',
         | 
| 1874 | 
            -
                        'css': 'css',
         | 
| 1875 | 
            -
                        'scss': 'scss',
         | 
| 1876 | 
            -
                        'sass': 'sass',
         | 
| 1877 | 
            -
                        'json': 'json',
         | 
| 1878 | 
            -
                        'xml': 'xml',
         | 
| 1879 | 
            -
                        'yaml': 'yaml',
         | 
| 1880 | 
            -
                        'yml': 'yaml',
         | 
| 1881 | 
            -
                        'md': 'markdown',
         | 
| 1882 | 
            -
                        'sh': 'bash',
         | 
| 1883 | 
            -
                        'bash': 'bash',
         | 
| 1884 | 
            -
                        'zsh': 'bash',
         | 
| 1885 | 
            -
                        'sql': 'sql',
         | 
| 1886 | 
            -
                        'go': 'go',
         | 
| 1887 | 
            -
                        'java': 'java',
         | 
| 1888 | 
            -
                        'c': 'c',
         | 
| 1889 | 
            -
                        'cpp': 'cpp',
         | 
| 1890 | 
            -
                        'h': 'c',
         | 
| 1891 | 
            -
                        'hpp': 'cpp'
         | 
| 1892 | 
            -
                    };
         | 
| 1893 | 
            -
                    
         | 
| 1894 | 
            -
                    const language = languageMap[extension.toLowerCase()] || 'text';
         | 
| 1895 | 
            -
                    
         | 
| 1896 | 
            -
                    // Set up code element with Prism.js classes
         | 
| 1897 | 
            -
                    codeElement.className = `file-content-code language-${language}`;
         | 
| 1898 | 
            -
                    codeElement.textContent = result.content;
         | 
| 1899 | 
            -
                    
         | 
| 1900 | 
            -
                    // Apply syntax highlighting with Prism.js if available
         | 
| 1901 | 
            -
                    if (window.Prism) {
         | 
| 1902 | 
            -
                        // Add line numbers
         | 
| 1903 | 
            -
                        codeElement.parentElement.className = 'file-content-display line-numbers';
         | 
| 1904 | 
            -
                        
         | 
| 1905 | 
            -
                        // Apply highlighting
         | 
| 1906 | 
            -
                        window.Prism.highlightElement(codeElement);
         | 
| 1907 | 
            -
                    }
         | 
| 1908 | 
            -
                    
         | 
| 1909 | 
            -
                    contentArea.style.display = 'flex';
         | 
| 1910 | 
            -
                    
         | 
| 1911 | 
            -
                    console.log('✅ File content displayed successfully');
         | 
| 1912 | 
            -
                } else {
         | 
| 1913 | 
            -
                    console.error('❌ Missing code element or content');
         | 
| 1914 | 
            -
                }
         | 
| 1915 | 
            -
            }
         | 
| 1916 1637 |  | 
| 1917 1638 | 
             
            function displayFileContentError(modal, result) {
         | 
| 1918 1639 | 
             
                const errorArea = modal.querySelector('.file-viewer-error');
         | 
| 1919 1640 | 
             
                const messageElement = modal.querySelector('.error-message');
         | 
| 1920 1641 | 
             
                const suggestionsElement = modal.querySelector('.error-suggestions');
         | 
| 1921 | 
            -
             | 
| 1642 | 
            +
             | 
| 1922 1643 | 
             
                // Create user-friendly error messages
         | 
| 1923 1644 | 
             
                let errorMessage = result.error || 'Unknown error occurred';
         | 
| 1924 | 
            -
             | 
| 1645 | 
            +
             | 
| 1925 1646 | 
             
                if (errorMessage.includes('not found')) {
         | 
| 1926 1647 | 
             
                    errorMessage = '📁 File not found or not accessible';
         | 
| 1927 1648 | 
             
                } else if (errorMessage.includes('permission')) {
         | 
| @@ -1931,9 +1652,9 @@ function displayFileContentError(modal, result) { | |
| 1931 1652 | 
             
                } else if (!errorMessage.includes('📁') && !errorMessage.includes('🔒') && !errorMessage.includes('📏')) {
         | 
| 1932 1653 | 
             
                    errorMessage = `⚠️ ${errorMessage}`;
         | 
| 1933 1654 | 
             
                }
         | 
| 1934 | 
            -
             | 
| 1655 | 
            +
             | 
| 1935 1656 | 
             
                messageElement.textContent = errorMessage;
         | 
| 1936 | 
            -
             | 
| 1657 | 
            +
             | 
| 1937 1658 | 
             
                // Add suggestions if available
         | 
| 1938 1659 | 
             
                if (result.suggestions && result.suggestions.length > 0) {
         | 
| 1939 1660 | 
             
                    suggestionsElement.innerHTML = `
         | 
| @@ -1952,13 +1673,13 @@ function displayFileContentError(modal, result) { | |
| 1952 1673 | 
             
                        </ul>
         | 
| 1953 1674 | 
             
                    `;
         | 
| 1954 1675 | 
             
                }
         | 
| 1955 | 
            -
             | 
| 1676 | 
            +
             | 
| 1956 1677 | 
             
                console.log('📋 Displaying file content error:', {
         | 
| 1957 1678 | 
             
                    originalError: result.error,
         | 
| 1958 1679 | 
             
                    processedMessage: errorMessage,
         | 
| 1959 1680 | 
             
                    suggestions: result.suggestions
         | 
| 1960 1681 | 
             
                });
         | 
| 1961 | 
            -
             | 
| 1682 | 
            +
             | 
| 1962 1683 | 
             
                errorArea.style.display = 'block';
         | 
| 1963 1684 | 
             
            }
         | 
| 1964 1685 |  | 
| @@ -1975,4 +1696,8 @@ window.showAgentInstanceDetails = function(instanceId) { | |
| 1975 1696 | 
             
            document.addEventListener('DOMContentLoaded', function() {
         | 
| 1976 1697 | 
             
                window.dashboard = new Dashboard();
         | 
| 1977 1698 | 
             
                console.log('Dashboard loaded and initialized');
         | 
| 1978 | 
            -
            });
         | 
| 1699 | 
            +
            });
         | 
| 1700 | 
            +
             | 
| 1701 | 
            +
            // ES6 Module export
         | 
| 1702 | 
            +
            export { Dashboard };
         | 
| 1703 | 
            +
            export default Dashboard;
         |