claude-mpm 3.9.9__py3-none-any.whl â 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +155 -0
- 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 +1 -1
- 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 +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- 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 +143 -762
- 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 +203 -81
- 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 +305 -197
- claude_mpm/cli/parser.py +24 -1150
- 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 +104 -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 +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -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/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 +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- 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 +120 -54
- 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 +571 -0
- 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 +40 -23
- 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 +14 -21
- 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 +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py â mcp_gateway.py} +149 -153
- 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 +110 -121
- 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 +223 -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 +145 -65
- 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 +170 -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 +20 -534
- 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 +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- 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.9.dist-info â claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info â claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info â claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- 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/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- 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.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info â claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info â claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -10,7 +10,14 @@ class ModuleViewer { | |
| 10 10 | 
             
                    this.jsonContainer = null;
         | 
| 11 11 | 
             
                    this.currentEvent = null;
         | 
| 12 12 | 
             
                    this.eventsByClass = new Map();
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    // Global JSON visibility state - persisted across all events
         | 
| 15 | 
            +
                    // When true, all events show JSON expanded; when false, all collapsed
         | 
| 16 | 
            +
                    this.globalJsonExpanded = localStorage.getItem('dashboard-json-expanded') === 'true';
         | 
| 13 17 |  | 
| 18 | 
            +
                    // Track if keyboard listener has been added to avoid duplicates
         | 
| 19 | 
            +
                    this.keyboardListenerAdded = false;
         | 
| 20 | 
            +
             | 
| 14 21 | 
             
                    this.init();
         | 
| 15 22 | 
             
                }
         | 
| 16 23 |  | 
| @@ -29,7 +36,7 @@ class ModuleViewer { | |
| 29 36 | 
             
                setupContainers() {
         | 
| 30 37 | 
             
                    this.dataContainer = document.getElementById('module-data-content');
         | 
| 31 38 | 
             
                    this.jsonContainer = null; // No longer used - JSON is handled via collapsible sections
         | 
| 32 | 
            -
             | 
| 39 | 
            +
             | 
| 33 40 | 
             
                    if (!this.dataContainer) {
         | 
| 34 41 | 
             
                        console.error('Module viewer data container not found');
         | 
| 35 42 | 
             
                    }
         | 
| @@ -67,9 +74,9 @@ class ModuleViewer { | |
| 67 74 | 
             
                            </div>
         | 
| 68 75 | 
             
                        `;
         | 
| 69 76 | 
             
                    }
         | 
| 70 | 
            -
             | 
| 77 | 
            +
             | 
| 71 78 | 
             
                    // JSON container no longer exists - handled via collapsible sections
         | 
| 72 | 
            -
             | 
| 79 | 
            +
             | 
| 73 80 | 
             
                    this.currentEvent = null;
         | 
| 74 81 | 
             
                }
         | 
| 75 82 |  | 
| @@ -79,10 +86,10 @@ class ModuleViewer { | |
| 79 86 | 
             
                 */
         | 
| 80 87 | 
             
                showEventDetails(event) {
         | 
| 81 88 | 
             
                    this.currentEvent = event;
         | 
| 82 | 
            -
             | 
| 89 | 
            +
             | 
| 83 90 | 
             
                    // Render structured data in top pane
         | 
| 84 91 | 
             
                    this.renderStructuredData(event);
         | 
| 85 | 
            -
             | 
| 92 | 
            +
             | 
| 86 93 | 
             
                    // Render JSON in bottom pane
         | 
| 87 94 | 
             
                    this.renderJsonData(event);
         | 
| 88 95 | 
             
                }
         | 
| @@ -93,19 +100,19 @@ class ModuleViewer { | |
| 93 100 | 
             
                 */
         | 
| 94 101 | 
             
                renderStructuredData(event) {
         | 
| 95 102 | 
             
                    if (!this.dataContainer) return;
         | 
| 96 | 
            -
             | 
| 103 | 
            +
             | 
| 97 104 | 
             
                    // Create contextual header
         | 
| 98 105 | 
             
                    const contextualHeader = this.createContextualHeader(event);
         | 
| 99 | 
            -
             | 
| 106 | 
            +
             | 
| 100 107 | 
             
                    // Create structured view based on event type
         | 
| 101 108 | 
             
                    const structuredView = this.createEventStructuredView(event);
         | 
| 102 | 
            -
             | 
| 109 | 
            +
             | 
| 103 110 | 
             
                    // Create collapsible JSON section
         | 
| 104 111 | 
             
                    const collapsibleJsonSection = this.createCollapsibleJsonSection(event);
         | 
| 105 | 
            -
             | 
| 112 | 
            +
             | 
| 106 113 | 
             
                    // Combine all sections in data container
         | 
| 107 114 | 
             
                    this.dataContainer.innerHTML = contextualHeader + structuredView + collapsibleJsonSection;
         | 
| 108 | 
            -
             | 
| 115 | 
            +
             | 
| 109 116 | 
             
                    // Initialize JSON toggle functionality
         | 
| 110 117 | 
             
                    this.initializeJsonToggle();
         | 
| 111 118 | 
             
                }
         | 
| @@ -147,7 +154,7 @@ class ModuleViewer { | |
| 147 154 | 
             
                 */
         | 
| 148 155 | 
             
                updateEventsByClass(events) {
         | 
| 149 156 | 
             
                    this.eventsByClass.clear();
         | 
| 150 | 
            -
             | 
| 157 | 
            +
             | 
| 151 158 | 
             
                    events.forEach(event => {
         | 
| 152 159 | 
             
                        const eventClass = this.getEventClass(event);
         | 
| 153 160 | 
             
                        if (!this.eventsByClass.has(eventClass)) {
         | 
| @@ -164,7 +171,7 @@ class ModuleViewer { | |
| 164 171 | 
             
                 */
         | 
| 165 172 | 
             
                getEventClass(event) {
         | 
| 166 173 | 
             
                    if (!event.type) return 'unknown';
         | 
| 167 | 
            -
             | 
| 174 | 
            +
             | 
| 168 175 | 
             
                    // Group similar event types
         | 
| 169 176 | 
             
                    switch (event.type) {
         | 
| 170 177 | 
             
                        case 'session':
         | 
| @@ -197,7 +204,7 @@ class ModuleViewer { | |
| 197 204 | 
             
                    const timestamp = this.formatTimestamp(event.timestamp);
         | 
| 198 205 | 
             
                    const data = event.data || {};
         | 
| 199 206 | 
             
                    let headerText = '';
         | 
| 200 | 
            -
             | 
| 207 | 
            +
             | 
| 201 208 | 
             
                    // Determine header text based on event type
         | 
| 202 209 | 
             
                    switch (event.type) {
         | 
| 203 210 | 
             
                        case 'hook':
         | 
| @@ -211,25 +218,25 @@ class ModuleViewer { | |
| 211 218 | 
             
                                headerText = `${hookName}: ${agent} ${timestamp}`;
         | 
| 212 219 | 
             
                            }
         | 
| 213 220 | 
             
                            break;
         | 
| 214 | 
            -
             | 
| 221 | 
            +
             | 
| 215 222 | 
             
                        case 'agent':
         | 
| 216 223 | 
             
                            // For Agents: "Agent: [AgentType] [time]"
         | 
| 217 224 | 
             
                            const agentType = data.agent_type || data.name || 'Unknown';
         | 
| 218 225 | 
             
                            headerText = `Agent: ${agentType} ${timestamp}`;
         | 
| 219 226 | 
             
                            break;
         | 
| 220 | 
            -
             | 
| 227 | 
            +
             | 
| 221 228 | 
             
                        case 'todo':
         | 
| 222 229 | 
             
                            // For TodoWrite: "TodoWrite: [Agent] [time]"
         | 
| 223 230 | 
             
                            const todoAgent = this.extractAgent(event) || 'PM';
         | 
| 224 231 | 
             
                            headerText = `TodoWrite: ${todoAgent} ${timestamp}`;
         | 
| 225 232 | 
             
                            break;
         | 
| 226 | 
            -
             | 
| 233 | 
            +
             | 
| 227 234 | 
             
                        case 'memory':
         | 
| 228 235 | 
             
                            // For Memory: "Memory: [Operation] [time]"
         | 
| 229 236 | 
             
                            const operation = data.operation || 'Unknown';
         | 
| 230 237 | 
             
                            headerText = `Memory: ${operation} ${timestamp}`;
         | 
| 231 238 | 
             
                            break;
         | 
| 232 | 
            -
             | 
| 239 | 
            +
             | 
| 233 240 | 
             
                        case 'session':
         | 
| 234 241 | 
             
                        case 'claude':
         | 
| 235 242 | 
             
                        case 'log':
         | 
| @@ -239,7 +246,7 @@ class ModuleViewer { | |
| 239 246 | 
             
                            const subtype = event.subtype || 'default';
         | 
| 240 247 | 
             
                            headerText = `Event: ${eventType}.${subtype} ${timestamp}`;
         | 
| 241 248 | 
             
                            break;
         | 
| 242 | 
            -
             | 
| 249 | 
            +
             | 
| 243 250 | 
             
                        default:
         | 
| 244 251 | 
             
                            // For Files and other events: "File: [filename] [time]" or generic
         | 
| 245 252 | 
             
                            const fileName = this.extractFileName(data);
         | 
| @@ -252,7 +259,7 @@ class ModuleViewer { | |
| 252 259 | 
             
                            }
         | 
| 253 260 | 
             
                            break;
         | 
| 254 261 | 
             
                    }
         | 
| 255 | 
            -
             | 
| 262 | 
            +
             | 
| 256 263 | 
             
                    return `
         | 
| 257 264 | 
             
                        <div class="contextual-header">
         | 
| 258 265 | 
             
                            <h3 class="contextual-header-text">${headerText}</h3>
         | 
| @@ -316,7 +323,7 @@ class ModuleViewer { | |
| 316 323 | 
             
                createEventDetailCard(eventType, event, count) {
         | 
| 317 324 | 
             
                    const timestamp = new Date(event.timestamp).toLocaleString();
         | 
| 318 325 | 
             
                    const eventIcon = this.getEventIcon(eventType);
         | 
| 319 | 
            -
             | 
| 326 | 
            +
             | 
| 320 327 | 
             
                    return `
         | 
| 321 328 | 
             
                        <div class="event-detail-card">
         | 
| 322 329 | 
             
                            <div class="event-detail-header">
         | 
| @@ -329,7 +336,7 @@ class ModuleViewer { | |
| 329 336 | 
             
                                ${this.createProperty('Event ID', event.id || 'N/A')}
         | 
| 330 337 | 
             
                                ${this.createProperty('Type', `${eventType}.${event.subtype || 'default'}`)}
         | 
| 331 338 | 
             
                                ${this.createProperty('Class Events', count)}
         | 
| 332 | 
            -
                                ${event.data && event.data.session_id ? | 
| 339 | 
            +
                                ${event.data && event.data.session_id ?
         | 
| 333 340 | 
             
                                    this.createProperty('Session', event.data.session_id) : ''}
         | 
| 334 341 | 
             
                            </div>
         | 
| 335 342 | 
             
                        </div>
         | 
| @@ -341,7 +348,7 @@ class ModuleViewer { | |
| 341 348 | 
             
                 */
         | 
| 342 349 | 
             
                createAgentStructuredView(event) {
         | 
| 343 350 | 
             
                    const data = event.data || {};
         | 
| 344 | 
            -
             | 
| 351 | 
            +
             | 
| 345 352 | 
             
                    // Handle Task delegation events (which appear as hook events but contain agent info)
         | 
| 346 353 | 
             
                    if (event.type === 'hook' && data.tool_name === 'Task' && data.tool_parameters?.subagent_type) {
         | 
| 347 354 | 
             
                        const taskData = data.tool_parameters;
         | 
| @@ -371,7 +378,7 @@ class ModuleViewer { | |
| 371 378 | 
             
                            </div>
         | 
| 372 379 | 
             
                        `;
         | 
| 373 380 | 
             
                    }
         | 
| 374 | 
            -
             | 
| 381 | 
            +
             | 
| 375 382 | 
             
                    // Handle regular agent events
         | 
| 376 383 | 
             
                    return `
         | 
| 377 384 | 
             
                        <div class="structured-view-section">
         | 
| @@ -392,17 +399,17 @@ class ModuleViewer { | |
| 392 399 | 
             
                 */
         | 
| 393 400 | 
             
                createHookStructuredView(event) {
         | 
| 394 401 | 
             
                    const data = event.data || {};
         | 
| 395 | 
            -
             | 
| 402 | 
            +
             | 
| 396 403 | 
             
                    // Extract file path information from tool parameters
         | 
| 397 404 | 
             
                    const filePath = this.extractFilePathFromHook(data);
         | 
| 398 405 | 
             
                    const toolInfo = this.extractToolInfoFromHook(data);
         | 
| 399 | 
            -
             | 
| 406 | 
            +
             | 
| 400 407 | 
             
                    // Note: Git diff functionality moved to Files tab only
         | 
| 401 408 | 
             
                    // Events tab no longer shows git diff buttons
         | 
| 402 409 |  | 
| 403 410 | 
             
                    // Create inline tool result content if available (without separate section header)
         | 
| 404 411 | 
             
                    const toolResultContent = this.createInlineToolResultContent(data, event);
         | 
| 405 | 
            -
             | 
| 412 | 
            +
             | 
| 406 413 | 
             
                    return `
         | 
| 407 414 | 
             
                        <div class="structured-view-section">
         | 
| 408 415 | 
             
                            <div class="structured-data">
         | 
| @@ -428,12 +435,12 @@ class ModuleViewer { | |
| 428 435 | 
             
                 */
         | 
| 429 436 | 
             
                createInlineToolResultContent(data, event = null) {
         | 
| 430 437 | 
             
                    const resultSummary = data.result_summary;
         | 
| 431 | 
            -
             | 
| 438 | 
            +
             | 
| 432 439 | 
             
                    // Determine if this is a post-tool event
         | 
| 433 440 | 
             
                    // Check multiple possible locations for the event phase
         | 
| 434 441 | 
             
                    const eventPhase = event?.subtype || data.event_type || data.phase;
         | 
| 435 442 | 
             
                    const isPostTool = eventPhase === 'post_tool' || eventPhase?.includes('post');
         | 
| 436 | 
            -
             | 
| 443 | 
            +
             | 
| 437 444 | 
             
                    // Debug logging to help troubleshoot tool result display issues
         | 
| 438 445 | 
             
                    if (window.DEBUG_TOOL_RESULTS) {
         | 
| 439 446 | 
             
                        console.log('đ§ createInlineToolResultContent debug:', {
         | 
| @@ -447,20 +454,20 @@ class ModuleViewer { | |
| 447 454 | 
             
                            resultSummaryKeys: resultSummary ? Object.keys(resultSummary) : []
         | 
| 448 455 | 
             
                        });
         | 
| 449 456 | 
             
                    }
         | 
| 450 | 
            -
             | 
| 457 | 
            +
             | 
| 451 458 | 
             
                    // Only show results if we have result data and this is a post-tool event
         | 
| 452 459 | 
             
                    // OR if we have result_summary regardless of phase (some events may not have proper phase info)
         | 
| 453 460 | 
             
                    if (!resultSummary) {
         | 
| 454 461 | 
             
                        return '';
         | 
| 455 462 | 
             
                    }
         | 
| 456 | 
            -
             | 
| 463 | 
            +
             | 
| 457 464 | 
             
                    // If we know this is a pre-tool event, don't show results
         | 
| 458 465 | 
             
                    if (eventPhase === 'pre_tool' || (eventPhase?.includes('pre') && !eventPhase?.includes('post'))) {
         | 
| 459 466 | 
             
                        return '';
         | 
| 460 467 | 
             
                    }
         | 
| 461 | 
            -
             | 
| 468 | 
            +
             | 
| 462 469 | 
             
                    let resultContent = '';
         | 
| 463 | 
            -
             | 
| 470 | 
            +
             | 
| 464 471 | 
             
                    // Add output preview if available
         | 
| 465 472 | 
             
                    if (resultSummary.has_output && resultSummary.output_preview) {
         | 
| 466 473 | 
             
                        resultContent += `
         | 
| @@ -468,14 +475,14 @@ class ModuleViewer { | |
| 468 475 | 
             
                            ${resultSummary.output_lines ? this.createProperty('Output Lines', resultSummary.output_lines) : ''}
         | 
| 469 476 | 
             
                        `;
         | 
| 470 477 | 
             
                    }
         | 
| 471 | 
            -
             | 
| 478 | 
            +
             | 
| 472 479 | 
             
                    // Add error preview if available
         | 
| 473 480 | 
             
                    if (resultSummary.has_error && resultSummary.error_preview) {
         | 
| 474 481 | 
             
                        resultContent += `
         | 
| 475 482 | 
             
                            ${this.createProperty('Error', this.truncateText(resultSummary.error_preview, 200))}
         | 
| 476 483 | 
             
                        `;
         | 
| 477 484 | 
             
                    }
         | 
| 478 | 
            -
             | 
| 485 | 
            +
             | 
| 479 486 | 
             
                    // If no specific output or error, but we have other result info
         | 
| 480 487 | 
             
                    if (!resultSummary.has_output && !resultSummary.has_error && Object.keys(resultSummary).length > 3) {
         | 
| 481 488 | 
             
                        // Show other result fields
         | 
| @@ -483,10 +490,10 @@ class ModuleViewer { | |
| 483 490 | 
             
                            .filter(([key, value]) => !['has_output', 'has_error', 'exit_code'].includes(key) && value !== undefined)
         | 
| 484 491 | 
             
                            .map(([key, value]) => this.createProperty(this.formatFieldName(key), String(value)))
         | 
| 485 492 | 
             
                            .join('');
         | 
| 486 | 
            -
             | 
| 493 | 
            +
             | 
| 487 494 | 
             
                        resultContent += otherFields;
         | 
| 488 495 | 
             
                    }
         | 
| 489 | 
            -
             | 
| 496 | 
            +
             | 
| 490 497 | 
             
                    return resultContent;
         | 
| 491 498 | 
             
                }
         | 
| 492 499 |  | 
| @@ -498,12 +505,12 @@ class ModuleViewer { | |
| 498 505 | 
             
                 */
         | 
| 499 506 | 
             
                createToolResultSection(data, event = null) {
         | 
| 500 507 | 
             
                    const resultSummary = data.result_summary;
         | 
| 501 | 
            -
             | 
| 508 | 
            +
             | 
| 502 509 | 
             
                    // Determine if this is a post-tool event
         | 
| 503 510 | 
             
                    // Check multiple possible locations for the event phase
         | 
| 504 511 | 
             
                    const eventPhase = event?.subtype || data.event_type || data.phase;
         | 
| 505 512 | 
             
                    const isPostTool = eventPhase === 'post_tool' || eventPhase?.includes('post');
         | 
| 506 | 
            -
             | 
| 513 | 
            +
             | 
| 507 514 | 
             
                    // Debug logging to help troubleshoot tool result display issues
         | 
| 508 515 | 
             
                    if (window.DEBUG_TOOL_RESULTS) {
         | 
| 509 516 | 
             
                        console.log('đ§ createToolResultSection debug:', {
         | 
| @@ -517,23 +524,23 @@ class ModuleViewer { | |
| 517 524 | 
             
                            resultSummaryKeys: resultSummary ? Object.keys(resultSummary) : []
         | 
| 518 525 | 
             
                        });
         | 
| 519 526 | 
             
                    }
         | 
| 520 | 
            -
             | 
| 527 | 
            +
             | 
| 521 528 | 
             
                    // Only show results if we have result data and this is a post-tool event
         | 
| 522 529 | 
             
                    // OR if we have result_summary regardless of phase (some events may not have proper phase info)
         | 
| 523 530 | 
             
                    if (!resultSummary) {
         | 
| 524 531 | 
             
                        return '';
         | 
| 525 532 | 
             
                    }
         | 
| 526 | 
            -
             | 
| 533 | 
            +
             | 
| 527 534 | 
             
                    // If we know this is a pre-tool event, don't show results
         | 
| 528 535 | 
             
                    if (eventPhase === 'pre_tool' || (eventPhase?.includes('pre') && !eventPhase?.includes('post'))) {
         | 
| 529 536 | 
             
                        return '';
         | 
| 530 537 | 
             
                    }
         | 
| 531 | 
            -
             | 
| 538 | 
            +
             | 
| 532 539 | 
             
                    // Determine result status and icon
         | 
| 533 540 | 
             
                    let statusIcon = 'âŗ';
         | 
| 534 541 | 
             
                    let statusClass = 'tool-running';
         | 
| 535 542 | 
             
                    let statusText = 'Unknown';
         | 
| 536 | 
            -
             | 
| 543 | 
            +
             | 
| 537 544 | 
             
                    if (data.success === true) {
         | 
| 538 545 | 
             
                        statusIcon = 'â
';
         | 
| 539 546 | 
             
                        statusClass = 'tool-success';
         | 
| @@ -555,9 +562,9 @@ class ModuleViewer { | |
| 555 562 | 
             
                        statusClass = 'tool-failure';
         | 
| 556 563 | 
             
                        statusText = 'Error';
         | 
| 557 564 | 
             
                    }
         | 
| 558 | 
            -
             | 
| 565 | 
            +
             | 
| 559 566 | 
             
                    let resultContent = '';
         | 
| 560 | 
            -
             | 
| 567 | 
            +
             | 
| 561 568 | 
             
                    // Add basic result info
         | 
| 562 569 | 
             
                    resultContent += `
         | 
| 563 570 | 
             
                        <div class="tool-result-status ${statusClass}">
         | 
| @@ -566,7 +573,7 @@ class ModuleViewer { | |
| 566 573 | 
             
                            ${data.exit_code !== undefined ? `<span class="tool-exit-code">Exit Code: ${data.exit_code}</span>` : ''}
         | 
| 567 574 | 
             
                        </div>
         | 
| 568 575 | 
             
                    `;
         | 
| 569 | 
            -
             | 
| 576 | 
            +
             | 
| 570 577 | 
             
                    // Add output preview if available
         | 
| 571 578 | 
             
                    if (resultSummary.has_output && resultSummary.output_preview) {
         | 
| 572 579 | 
             
                        resultContent += `
         | 
| @@ -579,7 +586,7 @@ class ModuleViewer { | |
| 579 586 | 
             
                            </div>
         | 
| 580 587 | 
             
                        `;
         | 
| 581 588 | 
             
                    }
         | 
| 582 | 
            -
             | 
| 589 | 
            +
             | 
| 583 590 | 
             
                    // Add error preview if available
         | 
| 584 591 | 
             
                    if (resultSummary.has_error && resultSummary.error_preview) {
         | 
| 585 592 | 
             
                        resultContent += `
         | 
| @@ -591,7 +598,7 @@ class ModuleViewer { | |
| 591 598 | 
             
                            </div>
         | 
| 592 599 | 
             
                        `;
         | 
| 593 600 | 
             
                    }
         | 
| 594 | 
            -
             | 
| 601 | 
            +
             | 
| 595 602 | 
             
                    // If no specific output or error, but we have other result info
         | 
| 596 603 | 
             
                    if (!resultSummary.has_output && !resultSummary.has_error && Object.keys(resultSummary).length > 3) {
         | 
| 597 604 | 
             
                        // Show other result fields
         | 
| @@ -599,7 +606,7 @@ class ModuleViewer { | |
| 599 606 | 
             
                            .filter(([key, value]) => !['has_output', 'has_error', 'exit_code'].includes(key) && value !== undefined)
         | 
| 600 607 | 
             
                            .map(([key, value]) => this.createProperty(this.formatFieldName(key), String(value)))
         | 
| 601 608 | 
             
                            .join('');
         | 
| 602 | 
            -
             | 
| 609 | 
            +
             | 
| 603 610 | 
             
                        if (otherFields) {
         | 
| 604 611 | 
             
                            resultContent += `
         | 
| 605 612 | 
             
                                <div class="tool-result-other">
         | 
| @@ -611,12 +618,12 @@ class ModuleViewer { | |
| 611 618 | 
             
                            `;
         | 
| 612 619 | 
             
                        }
         | 
| 613 620 | 
             
                    }
         | 
| 614 | 
            -
             | 
| 621 | 
            +
             | 
| 615 622 | 
             
                    // Only return content if we have something to show
         | 
| 616 623 | 
             
                    if (!resultContent.trim()) {
         | 
| 617 624 | 
             
                        return '';
         | 
| 618 625 | 
             
                    }
         | 
| 619 | 
            -
             | 
| 626 | 
            +
             | 
| 620 627 | 
             
                    return `
         | 
| 621 628 | 
             
                        <div class="tool-result-section">
         | 
| 622 629 | 
             
                            <div class="contextual-header">
         | 
| @@ -639,30 +646,30 @@ class ModuleViewer { | |
| 639 646 | 
             
                    // Common write operation tool names
         | 
| 640 647 | 
             
                    const writeTools = [
         | 
| 641 648 | 
             
                        'Write',
         | 
| 642 | 
            -
                        'Edit', | 
| 649 | 
            +
                        'Edit',
         | 
| 643 650 | 
             
                        'MultiEdit',
         | 
| 644 651 | 
             
                        'NotebookEdit'
         | 
| 645 652 | 
             
                    ];
         | 
| 646 | 
            -
             | 
| 653 | 
            +
             | 
| 647 654 | 
             
                    if (writeTools.includes(toolName)) {
         | 
| 648 655 | 
             
                        return true;
         | 
| 649 656 | 
             
                    }
         | 
| 650 | 
            -
             | 
| 657 | 
            +
             | 
| 651 658 | 
             
                    // Check for write-related parameters in the data
         | 
| 652 659 | 
             
                    if (data.tool_parameters) {
         | 
| 653 660 | 
             
                        const params = data.tool_parameters;
         | 
| 654 | 
            -
             | 
| 661 | 
            +
             | 
| 655 662 | 
             
                        // Check for content or editing parameters
         | 
| 656 663 | 
             
                        if (params.content || params.new_string || params.edits) {
         | 
| 657 664 | 
             
                            return true;
         | 
| 658 665 | 
             
                        }
         | 
| 659 | 
            -
             | 
| 666 | 
            +
             | 
| 660 667 | 
             
                        // Check for file modification indicators
         | 
| 661 668 | 
             
                        if (params.edit_mode && params.edit_mode !== 'read') {
         | 
| 662 669 | 
             
                            return true;
         | 
| 663 670 | 
             
                        }
         | 
| 664 671 | 
             
                    }
         | 
| 665 | 
            -
             | 
| 672 | 
            +
             | 
| 666 673 | 
             
                    // Check event subtype for write operations
         | 
| 667 674 | 
             
                    if (data.event_type === 'post_tool' || data.event_type === 'pre_tool') {
         | 
| 668 675 | 
             
                        // Additional heuristics based on tool usage patterns
         | 
| @@ -674,7 +681,7 @@ class ModuleViewer { | |
| 674 681 | 
             
                            return true;
         | 
| 675 682 | 
             
                        }
         | 
| 676 683 | 
             
                    }
         | 
| 677 | 
            -
             | 
| 684 | 
            +
             | 
| 678 685 | 
             
                    return false;
         | 
| 679 686 | 
             
                }
         | 
| 680 687 |  | 
| @@ -685,22 +692,22 @@ class ModuleViewer { | |
| 685 692 | 
             
                 */
         | 
| 686 693 | 
             
                isReadOnlyOperation(operation) {
         | 
| 687 694 | 
             
                    if (!operation) return true; // Default to read-only for safety
         | 
| 688 | 
            -
             | 
| 695 | 
            +
             | 
| 689 696 | 
             
                    const readOnlyOperations = ['read'];
         | 
| 690 697 | 
             
                    const editOperations = ['write', 'edit', 'multiedit', 'create', 'delete', 'move', 'copy'];
         | 
| 691 | 
            -
             | 
| 698 | 
            +
             | 
| 692 699 | 
             
                    const opLower = operation.toLowerCase();
         | 
| 693 | 
            -
             | 
| 700 | 
            +
             | 
| 694 701 | 
             
                    // Explicitly read-only operations
         | 
| 695 702 | 
             
                    if (readOnlyOperations.includes(opLower)) {
         | 
| 696 703 | 
             
                        return true;
         | 
| 697 704 | 
             
                    }
         | 
| 698 | 
            -
             | 
| 705 | 
            +
             | 
| 699 706 | 
             
                    // Explicitly edit operations
         | 
| 700 707 | 
             
                    if (editOperations.includes(opLower)) {
         | 
| 701 708 | 
             
                        return false;
         | 
| 702 709 | 
             
                    }
         | 
| 703 | 
            -
             | 
| 710 | 
            +
             | 
| 704 711 | 
             
                    // Default to read-only for unknown operations
         | 
| 705 712 | 
             
                    return true;
         | 
| 706 713 | 
             
                }
         | 
| @@ -710,7 +717,7 @@ class ModuleViewer { | |
| 710 717 | 
             
                 */
         | 
| 711 718 | 
             
                createTodoStructuredView(event) {
         | 
| 712 719 | 
             
                    const data = event.data || {};
         | 
| 713 | 
            -
             | 
| 720 | 
            +
             | 
| 714 721 | 
             
                    let content = '';
         | 
| 715 722 |  | 
| 716 723 | 
             
                    // Add todo checklist if available - start directly with checklist
         | 
| @@ -736,7 +743,7 @@ class ModuleViewer { | |
| 736 743 | 
             
                 */
         | 
| 737 744 | 
             
                createMemoryStructuredView(event) {
         | 
| 738 745 | 
             
                    const data = event.data || {};
         | 
| 739 | 
            -
             | 
| 746 | 
            +
             | 
| 740 747 | 
             
                    return `
         | 
| 741 748 | 
             
                        <div class="structured-view-section">
         | 
| 742 749 | 
             
                            <div class="structured-data">
         | 
| @@ -755,7 +762,7 @@ class ModuleViewer { | |
| 755 762 | 
             
                 */
         | 
| 756 763 | 
             
                createClaudeStructuredView(event) {
         | 
| 757 764 | 
             
                    const data = event.data || {};
         | 
| 758 | 
            -
             | 
| 765 | 
            +
             | 
| 759 766 | 
             
                    return `
         | 
| 760 767 | 
             
                        <div class="structured-view-section">
         | 
| 761 768 | 
             
                            <div class="structured-data">
         | 
| @@ -776,7 +783,7 @@ class ModuleViewer { | |
| 776 783 | 
             
                 */
         | 
| 777 784 | 
             
                createSessionStructuredView(event) {
         | 
| 778 785 | 
             
                    const data = event.data || {};
         | 
| 779 | 
            -
             | 
| 786 | 
            +
             | 
| 780 787 | 
             
                    return `
         | 
| 781 788 | 
             
                        <div class="structured-view-section">
         | 
| 782 789 | 
             
                            <div class="structured-data">
         | 
| @@ -796,16 +803,16 @@ class ModuleViewer { | |
| 796 803 | 
             
                createGenericStructuredView(event) {
         | 
| 797 804 | 
             
                    const data = event.data || {};
         | 
| 798 805 | 
             
                    const keys = Object.keys(data);
         | 
| 799 | 
            -
             | 
| 806 | 
            +
             | 
| 800 807 | 
             
                    if (keys.length === 0) {
         | 
| 801 808 | 
             
                        return '';
         | 
| 802 809 | 
             
                    }
         | 
| 803 | 
            -
             | 
| 810 | 
            +
             | 
| 804 811 | 
             
                    return `
         | 
| 805 812 | 
             
                        <div class="structured-view-section">
         | 
| 806 813 | 
             
                            <div class="structured-data">
         | 
| 807 | 
            -
                                ${keys.map(key => | 
| 808 | 
            -
                                    this.createProperty(key, typeof data[key] === 'object' ? | 
| 814 | 
            +
                                ${keys.map(key =>
         | 
| 815 | 
            +
                                    this.createProperty(key, typeof data[key] === 'object' ?
         | 
| 809 816 | 
             
                                        '[Object]' : String(data[key]))
         | 
| 810 817 | 
             
                                ).join('')}
         | 
| 811 818 | 
             
                            </div>
         | 
| @@ -815,24 +822,34 @@ class ModuleViewer { | |
| 815 822 |  | 
| 816 823 | 
             
                /**
         | 
| 817 824 | 
             
                 * Create collapsible JSON section that appears below main content
         | 
| 825 | 
            +
                 * WHY: Uses global state to maintain consistent JSON visibility across all events
         | 
| 826 | 
            +
                 * DESIGN DECISION: Sticky toggle improves debugging workflow by maintaining JSON
         | 
| 827 | 
            +
                 * visibility preference as user navigates through different events
         | 
| 818 828 | 
             
                 * @param {Object} event - The event to render
         | 
| 819 829 | 
             
                 * @returns {string} HTML content
         | 
| 820 830 | 
             
                 */
         | 
| 821 831 | 
             
                createCollapsibleJsonSection(event) {
         | 
| 822 832 | 
             
                    const uniqueId = 'json-section-' + Math.random().toString(36).substr(2, 9);
         | 
| 823 833 | 
             
                    const jsonString = this.formatJSON(event);
         | 
| 834 | 
            +
                    
         | 
| 835 | 
            +
                    // Use global state to determine initial visibility
         | 
| 836 | 
            +
                    const isExpanded = this.globalJsonExpanded;
         | 
| 837 | 
            +
                    const display = isExpanded ? 'block' : 'none';
         | 
| 838 | 
            +
                    const arrow = isExpanded ? 'â˛' : 'âŧ';
         | 
| 839 | 
            +
                    const ariaExpanded = isExpanded ? 'true' : 'false';
         | 
| 840 | 
            +
                    
         | 
| 824 841 | 
             
                    return `
         | 
| 825 842 | 
             
                        <div class="collapsible-json-section" id="${uniqueId}">
         | 
| 826 | 
            -
                            <div class="json-toggle-header" | 
| 827 | 
            -
                                 onclick="window.moduleViewer.toggleJsonSection()" | 
| 828 | 
            -
                                 role="button" | 
| 829 | 
            -
                                 tabindex="0" | 
| 830 | 
            -
                                 aria-expanded=" | 
| 843 | 
            +
                            <div class="json-toggle-header"
         | 
| 844 | 
            +
                                 onclick="window.moduleViewer.toggleJsonSection()"
         | 
| 845 | 
            +
                                 role="button"
         | 
| 846 | 
            +
                                 tabindex="0"
         | 
| 847 | 
            +
                                 aria-expanded="${ariaExpanded}"
         | 
| 831 848 | 
             
                                 onkeydown="if(event.key==='Enter'||event.key===' '){window.moduleViewer.toggleJsonSection();event.preventDefault();}">
         | 
| 832 849 | 
             
                                <span class="json-toggle-text">Raw JSON</span>
         | 
| 833 | 
            -
                                <span class="json-toggle-arrow" | 
| 850 | 
            +
                                <span class="json-toggle-arrow">${arrow}</span>
         | 
| 834 851 | 
             
                            </div>
         | 
| 835 | 
            -
                            <div class="json-content-collapsible" style="display:  | 
| 852 | 
            +
                            <div class="json-content-collapsible" style="display: ${display};" aria-hidden="${!isExpanded}">
         | 
| 836 853 | 
             
                                <div class="json-display" onclick="window.moduleViewer.copyJsonToClipboard(event)">
         | 
| 837 854 | 
             
                                    <pre>${jsonString}</pre>
         | 
| 838 855 | 
             
                                </div>
         | 
| @@ -850,7 +867,7 @@ class ModuleViewer { | |
| 850 867 | 
             
                    const rect = event.currentTarget.getBoundingClientRect();
         | 
| 851 868 | 
             
                    const clickX = event.clientX - rect.left;
         | 
| 852 869 | 
             
                    const clickY = event.clientY - rect.top;
         | 
| 853 | 
            -
             | 
| 870 | 
            +
             | 
| 854 871 | 
             
                    // Check if click is in the top-right corner (copy icon area)
         | 
| 855 872 | 
             
                    if (clickX > rect.width - 50 && clickY < 30) {
         | 
| 856 873 | 
             
                        const preElement = event.currentTarget.querySelector('pre');
         | 
| @@ -869,49 +886,99 @@ class ModuleViewer { | |
| 869 886 |  | 
| 870 887 | 
             
                /**
         | 
| 871 888 | 
             
                 * Initialize JSON toggle functionality
         | 
| 889 | 
            +
                 * WHY: Ensures newly rendered events respect the current global JSON visibility state
         | 
| 872 890 | 
             
                 */
         | 
| 873 891 | 
             
                initializeJsonToggle() {
         | 
| 874 892 | 
             
                    // Make sure the moduleViewer is available globally for onclick handlers
         | 
| 875 893 | 
             
                    window.moduleViewer = this;
         | 
| 876 | 
            -
             | 
| 877 | 
            -
                    //  | 
| 878 | 
            -
                     | 
| 879 | 
            -
             | 
| 880 | 
            -
             | 
| 881 | 
            -
             | 
| 882 | 
            -
             | 
| 894 | 
            +
             | 
| 895 | 
            +
                    // Apply global state to newly rendered JSON sections
         | 
| 896 | 
            +
                    // This ensures new events respect the current global state
         | 
| 897 | 
            +
                    if (this.globalJsonExpanded) {
         | 
| 898 | 
            +
                        // Small delay to ensure DOM is ready
         | 
| 899 | 
            +
                        setTimeout(() => {
         | 
| 900 | 
            +
                            this.updateAllJsonSections();
         | 
| 901 | 
            +
                        }, 0);
         | 
| 902 | 
            +
                    }
         | 
| 903 | 
            +
             | 
| 904 | 
            +
                    // Add keyboard navigation support (only add once to avoid duplicates)
         | 
| 905 | 
            +
                    if (!this.keyboardListenerAdded) {
         | 
| 906 | 
            +
                        this.keyboardListenerAdded = true;
         | 
| 907 | 
            +
                        document.addEventListener('keydown', (e) => {
         | 
| 908 | 
            +
                            if (e.target.classList.contains('json-toggle-header')) {
         | 
| 909 | 
            +
                                if (e.key === 'Enter' || e.key === ' ') {
         | 
| 910 | 
            +
                                    this.toggleJsonSection();
         | 
| 911 | 
            +
                                    e.preventDefault();
         | 
| 912 | 
            +
                                }
         | 
| 883 913 | 
             
                            }
         | 
| 884 | 
            -
                        }
         | 
| 885 | 
            -
                    } | 
| 914 | 
            +
                        });
         | 
| 915 | 
            +
                    }
         | 
| 886 916 | 
             
                }
         | 
| 887 917 |  | 
| 888 918 | 
             
                /**
         | 
| 889 | 
            -
                 * Toggle JSON section visibility  | 
| 919 | 
            +
                 * Toggle JSON section visibility globally - affects ALL events
         | 
| 920 | 
            +
                 * WHY: Sticky toggle maintains user preference across all events for better debugging
         | 
| 921 | 
            +
                 * DESIGN DECISION: Uses localStorage to persist preference across page refreshes
         | 
| 890 922 | 
             
                 */
         | 
| 891 923 | 
             
                toggleJsonSection() {
         | 
| 892 | 
            -
                     | 
| 893 | 
            -
                     | 
| 894 | 
            -
                     | 
| 924 | 
            +
                    // Toggle the global state
         | 
| 925 | 
            +
                    this.globalJsonExpanded = !this.globalJsonExpanded;
         | 
| 926 | 
            +
                    
         | 
| 927 | 
            +
                    // Persist the preference to localStorage
         | 
| 928 | 
            +
                    localStorage.setItem('dashboard-json-expanded', this.globalJsonExpanded.toString());
         | 
| 895 929 |  | 
| 896 | 
            -
                     | 
| 930 | 
            +
                    // Update ALL JSON sections on the page
         | 
| 931 | 
            +
                    this.updateAllJsonSections();
         | 
| 932 | 
            +
                    
         | 
| 933 | 
            +
                    // Dispatch event to notify other components of the change
         | 
| 934 | 
            +
                    document.dispatchEvent(new CustomEvent('jsonToggleChanged', {
         | 
| 935 | 
            +
                        detail: { expanded: this.globalJsonExpanded }
         | 
| 936 | 
            +
                    }));
         | 
| 937 | 
            +
                }
         | 
| 938 | 
            +
             | 
| 939 | 
            +
                /**
         | 
| 940 | 
            +
                 * Update all JSON sections on the page to match global state
         | 
| 941 | 
            +
                 * WHY: Ensures consistent JSON visibility across all displayed events
         | 
| 942 | 
            +
                 */
         | 
| 943 | 
            +
                updateAllJsonSections() {
         | 
| 944 | 
            +
                    // Find all JSON content sections and toggle headers
         | 
| 945 | 
            +
                    const allJsonContents = document.querySelectorAll('.json-content-collapsible');
         | 
| 946 | 
            +
                    const allArrows = document.querySelectorAll('.json-toggle-arrow');
         | 
| 947 | 
            +
                    const allHeaders = document.querySelectorAll('.json-toggle-header');
         | 
| 897 948 |  | 
| 898 | 
            -
                     | 
| 949 | 
            +
                    // Update each JSON section
         | 
| 950 | 
            +
                    allJsonContents.forEach((jsonContent, index) => {
         | 
| 951 | 
            +
                        if (this.globalJsonExpanded) {
         | 
| 952 | 
            +
                            // Show JSON content
         | 
| 953 | 
            +
                            jsonContent.style.display = 'block';
         | 
| 954 | 
            +
                            jsonContent.setAttribute('aria-hidden', 'false');
         | 
| 955 | 
            +
                            if (allArrows[index]) {
         | 
| 956 | 
            +
                                allArrows[index].textContent = 'â˛';
         | 
| 957 | 
            +
                            }
         | 
| 958 | 
            +
                            if (allHeaders[index]) {
         | 
| 959 | 
            +
                                allHeaders[index].setAttribute('aria-expanded', 'true');
         | 
| 960 | 
            +
                            }
         | 
| 961 | 
            +
                        } else {
         | 
| 962 | 
            +
                            // Hide JSON content
         | 
| 963 | 
            +
                            jsonContent.style.display = 'none';
         | 
| 964 | 
            +
                            jsonContent.setAttribute('aria-hidden', 'true');
         | 
| 965 | 
            +
                            if (allArrows[index]) {
         | 
| 966 | 
            +
                                allArrows[index].textContent = 'âŧ';
         | 
| 967 | 
            +
                            }
         | 
| 968 | 
            +
                            if (allHeaders[index]) {
         | 
| 969 | 
            +
                                allHeaders[index].setAttribute('aria-expanded', 'false');
         | 
| 970 | 
            +
                            }
         | 
| 971 | 
            +
                        }
         | 
| 972 | 
            +
                    });
         | 
| 899 973 |  | 
| 900 | 
            -
                     | 
| 901 | 
            -
             | 
| 902 | 
            -
                        jsonContent.style.display = 'block';
         | 
| 903 | 
            -
                        arrow.textContent = 'â˛';
         | 
| 904 | 
            -
                        toggleHeader.setAttribute('aria-expanded', 'true');
         | 
| 905 | 
            -
                        
         | 
| 906 | 
            -
                        // Scroll the new content into view if needed
         | 
| 974 | 
            +
                    // If expanded and there's content, scroll the first visible one into view
         | 
| 975 | 
            +
                    if (this.globalJsonExpanded && allJsonContents.length > 0) {
         | 
| 907 976 | 
             
                        setTimeout(() => {
         | 
| 908 | 
            -
                             | 
| 977 | 
            +
                            const firstVisible = allJsonContents[0];
         | 
| 978 | 
            +
                            if (firstVisible) {
         | 
| 979 | 
            +
                                firstVisible.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
         | 
| 980 | 
            +
                            }
         | 
| 909 981 | 
             
                        }, 100);
         | 
| 910 | 
            -
                    } else {
         | 
| 911 | 
            -
                        // Hide JSON content
         | 
| 912 | 
            -
                        jsonContent.style.display = 'none';
         | 
| 913 | 
            -
                        arrow.textContent = 'âŧ';
         | 
| 914 | 
            -
                        toggleHeader.setAttribute('aria-expanded', 'false');
         | 
| 915 982 | 
             
                    }
         | 
| 916 983 | 
             
                }
         | 
| 917 984 |  | 
| @@ -920,7 +987,7 @@ class ModuleViewer { | |
| 920 987 | 
             
                 */
         | 
| 921 988 | 
             
                createProperty(key, value) {
         | 
| 922 989 | 
             
                    const displayValue = this.truncateText(String(value), 300);
         | 
| 923 | 
            -
             | 
| 990 | 
            +
             | 
| 924 991 | 
             
                    // Check if this is a file path property that should be clickable
         | 
| 925 992 | 
             
                    if (this.isFilePathProperty(key, value)) {
         | 
| 926 993 | 
             
                        return `
         | 
| @@ -932,7 +999,7 @@ class ModuleViewer { | |
| 932 999 | 
             
                            </div>
         | 
| 933 1000 | 
             
                        `;
         | 
| 934 1001 | 
             
                    }
         | 
| 935 | 
            -
             | 
| 1002 | 
            +
             | 
| 936 1003 | 
             
                    return `
         | 
| 937 1004 | 
             
                        <div class="event-property">
         | 
| 938 1005 | 
             
                            <span class="event-property-key">${key}:</span>
         | 
| @@ -956,16 +1023,16 @@ class ModuleViewer { | |
| 956 1023 | 
             
                        'Working Directory',
         | 
| 957 1024 | 
             
                        'working_directory'
         | 
| 958 1025 | 
             
                    ];
         | 
| 959 | 
            -
             | 
| 1026 | 
            +
             | 
| 960 1027 | 
             
                    // Check if key indicates a file path
         | 
| 961 1028 | 
             
                    if (filePathKeys.some(pathKey => key.toLowerCase().includes(pathKey.toLowerCase()))) {
         | 
| 962 1029 | 
             
                        // Ensure value looks like a file path (contains / or \\ and has reasonable length)
         | 
| 963 1030 | 
             
                        const strValue = String(value);
         | 
| 964 | 
            -
                        return strValue.length > 0 && | 
| 1031 | 
            +
                        return strValue.length > 0 &&
         | 
| 965 1032 | 
             
                               (strValue.includes('/') || strValue.includes('\\')) &&
         | 
| 966 1033 | 
             
                               strValue.length < 500; // Reasonable path length limit
         | 
| 967 1034 | 
             
                    }
         | 
| 968 | 
            -
             | 
| 1035 | 
            +
             | 
| 969 1036 | 
             
                    return false;
         | 
| 970 1037 | 
             
                }
         | 
| 971 1038 |  | 
| @@ -977,9 +1044,9 @@ class ModuleViewer { | |
| 977 1044 | 
             
                createClickableFilePath(filePath) {
         | 
| 978 1045 | 
             
                    const displayPath = this.truncateText(String(filePath), 300);
         | 
| 979 1046 | 
             
                    const escapedPath = filePath.replace(/'/g, "\\'");
         | 
| 980 | 
            -
             | 
| 1047 | 
            +
             | 
| 981 1048 | 
             
                    return `
         | 
| 982 | 
            -
                        <span class="clickable-file-path" | 
| 1049 | 
            +
                        <span class="clickable-file-path"
         | 
| 983 1050 | 
             
                              onclick="showFileViewerModal('${escapedPath}')"
         | 
| 984 1051 | 
             
                              title="Click to view file contents with syntax highlighting
Path: ${filePath}">
         | 
| 985 1052 | 
             
                            ${displayPath}
         | 
| @@ -1037,24 +1104,24 @@ class ModuleViewer { | |
| 1037 1104 | 
             
                    // First check if there's a specific hook name in the data
         | 
| 1038 1105 | 
             
                    if (data.hook_name) return data.hook_name;
         | 
| 1039 1106 | 
             
                    if (data.name) return data.name;
         | 
| 1040 | 
            -
             | 
| 1107 | 
            +
             | 
| 1041 1108 | 
             
                    // Use event.subtype or data.event_type to determine hook name
         | 
| 1042 1109 | 
             
                    const eventType = event.subtype || data.event_type;
         | 
| 1043 | 
            -
             | 
| 1110 | 
            +
             | 
| 1044 1111 | 
             
                    // Map hook event types to meaningful display names
         | 
| 1045 1112 | 
             
                    const hookNames = {
         | 
| 1046 1113 | 
             
                        'user_prompt': 'User Prompt',
         | 
| 1047 1114 | 
             
                        'pre_tool': 'Tool Execution (Pre)',
         | 
| 1048 | 
            -
                        'post_tool': 'Tool Execution (Post)', | 
| 1115 | 
            +
                        'post_tool': 'Tool Execution (Post)',
         | 
| 1049 1116 | 
             
                        'notification': 'Notification',
         | 
| 1050 1117 | 
             
                        'stop': 'Session Stop',
         | 
| 1051 1118 | 
             
                        'subagent_stop': 'Subagent Stop'
         | 
| 1052 1119 | 
             
                    };
         | 
| 1053 | 
            -
             | 
| 1120 | 
            +
             | 
| 1054 1121 | 
             
                    if (hookNames[eventType]) {
         | 
| 1055 1122 | 
             
                        return hookNames[eventType];
         | 
| 1056 1123 | 
             
                    }
         | 
| 1057 | 
            -
             | 
| 1124 | 
            +
             | 
| 1058 1125 | 
             
                    // If it's a compound event type like "hook.user_prompt", extract the part after "hook."
         | 
| 1059 1126 | 
             
                    if (typeof event.type === 'string' && event.type.startsWith('hook.')) {
         | 
| 1060 1127 | 
             
                        const hookType = event.type.replace('hook.', '');
         | 
| @@ -1062,14 +1129,14 @@ class ModuleViewer { | |
| 1062 1129 | 
             
                            return hookNames[hookType];
         | 
| 1063 1130 | 
             
                        }
         | 
| 1064 1131 | 
             
                    }
         | 
| 1065 | 
            -
             | 
| 1132 | 
            +
             | 
| 1066 1133 | 
             
                    // Fallback to formatting the event type nicely
         | 
| 1067 1134 | 
             
                    if (eventType) {
         | 
| 1068 1135 | 
             
                        return eventType.split('_')
         | 
| 1069 1136 | 
             
                            .map(word => word.charAt(0).toUpperCase() + word.slice(1))
         | 
| 1070 1137 | 
             
                            .join(' ');
         | 
| 1071 1138 | 
             
                    }
         | 
| 1072 | 
            -
             | 
| 1139 | 
            +
             | 
| 1073 1140 | 
             
                    return 'Unknown Hook';
         | 
| 1074 1141 | 
             
                }
         | 
| 1075 1142 |  | 
| @@ -1081,22 +1148,22 @@ class ModuleViewer { | |
| 1081 1148 | 
             
                    if (data.tool_parameters && data.tool_parameters.file_path) {
         | 
| 1082 1149 | 
             
                        return data.tool_parameters.file_path;
         | 
| 1083 1150 | 
             
                    }
         | 
| 1084 | 
            -
             | 
| 1151 | 
            +
             | 
| 1085 1152 | 
             
                    // Check direct file_path field
         | 
| 1086 1153 | 
             
                    if (data.file_path) {
         | 
| 1087 1154 | 
             
                        return data.file_path;
         | 
| 1088 1155 | 
             
                    }
         | 
| 1089 | 
            -
             | 
| 1156 | 
            +
             | 
| 1090 1157 | 
             
                    // Check nested in other common locations
         | 
| 1091 1158 | 
             
                    if (data.tool_input && data.tool_input.file_path) {
         | 
| 1092 1159 | 
             
                        return data.tool_input.file_path;
         | 
| 1093 1160 | 
             
                    }
         | 
| 1094 | 
            -
             | 
| 1161 | 
            +
             | 
| 1095 1162 | 
             
                    // Check for notebook path (alternative field name)
         | 
| 1096 1163 | 
             
                    if (data.tool_parameters && data.tool_parameters.notebook_path) {
         | 
| 1097 1164 | 
             
                        return data.tool_parameters.notebook_path;
         | 
| 1098 1165 | 
             
                    }
         | 
| 1099 | 
            -
             | 
| 1166 | 
            +
             | 
| 1100 1167 | 
             
                    return null;
         | 
| 1101 1168 | 
             
                }
         | 
| 1102 1169 |  | 
| @@ -1136,7 +1203,7 @@ class ModuleViewer { | |
| 1136 1203 | 
             
                 */
         | 
| 1137 1204 | 
             
                formatTimestamp(timestamp) {
         | 
| 1138 1205 | 
             
                    if (!timestamp) return 'Unknown time';
         | 
| 1139 | 
            -
             | 
| 1206 | 
            +
             | 
| 1140 1207 | 
             
                    try {
         | 
| 1141 1208 | 
             
                        const date = new Date(timestamp);
         | 
| 1142 1209 | 
             
                        return date.toLocaleTimeString('en-US', {
         | 
| @@ -1184,7 +1251,7 @@ class ModuleViewer { | |
| 1184 1251 | 
             
                    if (data.tool_name) return data.tool_name;
         | 
| 1185 1252 | 
             
                    if (data.tool_parameters && data.tool_parameters.tool_name) return data.tool_parameters.tool_name;
         | 
| 1186 1253 | 
             
                    if (data.tool_input && data.tool_input.tool_name) return data.tool_input.tool_name;
         | 
| 1187 | 
            -
             | 
| 1254 | 
            +
             | 
| 1188 1255 | 
             
                    // Try to infer from other fields
         | 
| 1189 1256 | 
             
                    if (data.tool_parameters) {
         | 
| 1190 1257 | 
             
                        // Common tool patterns
         | 
| @@ -1201,7 +1268,7 @@ class ModuleViewer { | |
| 1201 1268 | 
             
                            return 'TodoWrite';
         | 
| 1202 1269 | 
             
                        }
         | 
| 1203 1270 | 
             
                    }
         | 
| 1204 | 
            -
             | 
| 1271 | 
            +
             | 
| 1205 1272 | 
             
                    return null;
         | 
| 1206 1273 | 
             
                }
         | 
| 1207 1274 |  | 
| @@ -1215,17 +1282,17 @@ class ModuleViewer { | |
| 1215 1282 | 
             
                    if (data._agentName && data._agentName !== 'Unknown Agent') {
         | 
| 1216 1283 | 
             
                        return data._agentName;
         | 
| 1217 1284 | 
             
                    }
         | 
| 1218 | 
            -
             | 
| 1285 | 
            +
             | 
| 1219 1286 | 
             
                    // Check inference data if available
         | 
| 1220 1287 | 
             
                    if (data._inference && data._inference.agentName && data._inference.agentName !== 'Unknown') {
         | 
| 1221 1288 | 
             
                        return data._inference.agentName;
         | 
| 1222 1289 | 
             
                    }
         | 
| 1223 | 
            -
             | 
| 1290 | 
            +
             | 
| 1224 1291 | 
             
                    // Check various locations where agent info might be stored
         | 
| 1225 1292 | 
             
                    if (data.agent) return data.agent;
         | 
| 1226 1293 | 
             
                    if (data.agent_type) return data.agent_type;
         | 
| 1227 1294 | 
             
                    if (data.agent_name) return data.agent_name;
         | 
| 1228 | 
            -
             | 
| 1295 | 
            +
             | 
| 1229 1296 | 
             
                    // Check session data
         | 
| 1230 1297 | 
             
                    if (data.session_id && typeof data.session_id === 'string') {
         | 
| 1231 1298 | 
             
                        // Extract agent from session ID if it contains agent info
         | 
| @@ -1234,11 +1301,11 @@ class ModuleViewer { | |
| 1234 1301 | 
             
                            return sessionParts[0].toUpperCase();
         | 
| 1235 1302 | 
             
                        }
         | 
| 1236 1303 | 
             
                    }
         | 
| 1237 | 
            -
             | 
| 1304 | 
            +
             | 
| 1238 1305 | 
             
                    // Infer from context
         | 
| 1239 1306 | 
             
                    if (data.todos) return 'PM'; // TodoWrite typically from PM agent
         | 
| 1240 1307 | 
             
                    if (data.tool_name === 'TodoWrite') return 'PM';
         | 
| 1241 | 
            -
             | 
| 1308 | 
            +
             | 
| 1242 1309 | 
             
                    return null;
         | 
| 1243 1310 | 
             
                }
         | 
| 1244 1311 |  | 
| @@ -1254,11 +1321,11 @@ class ModuleViewer { | |
| 1254 1321 | 
             
                        const pathParts = filePath.split('/');
         | 
| 1255 1322 | 
             
                        return pathParts[pathParts.length - 1];
         | 
| 1256 1323 | 
             
                    }
         | 
| 1257 | 
            -
             | 
| 1324 | 
            +
             | 
| 1258 1325 | 
             
                    // Check other common file fields
         | 
| 1259 1326 | 
             
                    if (data.filename) return data.filename;
         | 
| 1260 1327 | 
             
                    if (data.file) return data.file;
         | 
| 1261 | 
            -
             | 
| 1328 | 
            +
             | 
| 1262 1329 | 
             
                    return null;
         | 
| 1263 1330 | 
             
                }
         | 
| 1264 1331 |  | 
| @@ -1287,20 +1354,20 @@ class ModuleViewer { | |
| 1287 1354 | 
             
                    // Extract information from pre and post events
         | 
| 1288 1355 | 
             
                    const preEvent = toolCall.pre_event;
         | 
| 1289 1356 | 
             
                    const postEvent = toolCall.post_event;
         | 
| 1290 | 
            -
             | 
| 1357 | 
            +
             | 
| 1291 1358 | 
             
                    // Get parameters from pre-event
         | 
| 1292 1359 | 
             
                    const parameters = preEvent?.tool_parameters || {};
         | 
| 1293 1360 | 
             
                    const target = preEvent ? this.extractToolTarget(toolName, parameters) : 'Unknown target';
         | 
| 1294 | 
            -
             | 
| 1361 | 
            +
             | 
| 1295 1362 | 
             
                    // Get execution results from post-event
         | 
| 1296 1363 | 
             
                    const duration = toolCall.duration_ms ? `${toolCall.duration_ms}ms` : '-';
         | 
| 1297 1364 | 
             
                    const success = toolCall.success !== undefined ? toolCall.success : null;
         | 
| 1298 1365 | 
             
                    const exitCode = toolCall.exit_code !== undefined ? toolCall.exit_code : null;
         | 
| 1299 | 
            -
             | 
| 1366 | 
            +
             | 
| 1300 1367 | 
             
                    // Format result summary
         | 
| 1301 1368 | 
             
                    let resultSummary = toolCall.result_summary || 'No summary available';
         | 
| 1302 1369 | 
             
                    let formattedResultSummary = '';
         | 
| 1303 | 
            -
             | 
| 1370 | 
            +
             | 
| 1304 1371 | 
             
                    if (typeof resultSummary === 'object' && resultSummary !== null) {
         | 
| 1305 1372 | 
             
                        const parts = [];
         | 
| 1306 1373 | 
             
                        if (resultSummary.exit_code !== undefined) {
         | 
| @@ -1330,7 +1397,7 @@ class ModuleViewer { | |
| 1330 1397 | 
             
                    let statusIcon = 'âŗ';
         | 
| 1331 1398 | 
             
                    let statusText = 'Running...';
         | 
| 1332 1399 | 
             
                    let statusClass = 'tool-running';
         | 
| 1333 | 
            -
             | 
| 1400 | 
            +
             | 
| 1334 1401 | 
             
                    if (postEvent) {
         | 
| 1335 1402 | 
             
                        if (success === true) {
         | 
| 1336 1403 | 
             
                            statusIcon = 'â
';
         | 
| @@ -1361,7 +1428,7 @@ class ModuleViewer { | |
| 1361 1428 | 
             
                                ${parameters.todos.map(todo => {
         | 
| 1362 1429 | 
             
                                    const statusIcon = this.getTodoStatusIcon(todo.status);
         | 
| 1363 1430 | 
             
                                    const priorityIcon = this.getTodoPriorityIcon(todo.priority);
         | 
| 1364 | 
            -
             | 
| 1431 | 
            +
             | 
| 1365 1432 | 
             
                                    return `
         | 
| 1366 1433 | 
             
                                        <div class="todo-item todo-${todo.status || 'pending'}">
         | 
| 1367 1434 | 
             
                                            <span class="todo-status">${statusIcon}</span>
         | 
| @@ -1372,7 +1439,7 @@ class ModuleViewer { | |
| 1372 1439 | 
             
                                }).join('')}
         | 
| 1373 1440 | 
             
                            </div>
         | 
| 1374 1441 | 
             
                        `;
         | 
| 1375 | 
            -
             | 
| 1442 | 
            +
             | 
| 1376 1443 | 
             
                        // Create collapsible JSON section
         | 
| 1377 1444 | 
             
                        const toolCallData = {
         | 
| 1378 1445 | 
             
                            toolCall: toolCall,
         | 
| @@ -1380,11 +1447,11 @@ class ModuleViewer { | |
| 1380 1447 | 
             
                            postEvent: postEvent
         | 
| 1381 1448 | 
             
                        };
         | 
| 1382 1449 | 
             
                        const collapsibleJsonSection = this.createCollapsibleJsonSection(toolCallData);
         | 
| 1383 | 
            -
             | 
| 1450 | 
            +
             | 
| 1384 1451 | 
             
                        if (this.dataContainer) {
         | 
| 1385 1452 | 
             
                            this.dataContainer.innerHTML = contextualHeader + todoContent + collapsibleJsonSection;
         | 
| 1386 1453 | 
             
                        }
         | 
| 1387 | 
            -
             | 
| 1454 | 
            +
             | 
| 1388 1455 | 
             
                        // Initialize JSON toggle functionality
         | 
| 1389 1456 | 
             
                        this.initializeJsonToggle();
         | 
| 1390 1457 | 
             
                    } else {
         | 
| @@ -1419,12 +1486,12 @@ class ModuleViewer { | |
| 1419 1486 | 
             
                                            </div>
         | 
| 1420 1487 | 
             
                                        ` : ''}
         | 
| 1421 1488 | 
             
                                    </div>
         | 
| 1422 | 
            -
             | 
| 1489 | 
            +
             | 
| 1423 1490 | 
             
                                    ${this.createToolResultFromToolCall(toolCall)}
         | 
| 1424 1491 | 
             
                                </div>
         | 
| 1425 1492 | 
             
                            </div>
         | 
| 1426 1493 | 
             
                        `;
         | 
| 1427 | 
            -
             | 
| 1494 | 
            +
             | 
| 1428 1495 | 
             
                        // Create collapsible JSON section
         | 
| 1429 1496 | 
             
                        const toolCallData = {
         | 
| 1430 1497 | 
             
                            toolCall: toolCall,
         | 
| @@ -1432,11 +1499,11 @@ class ModuleViewer { | |
| 1432 1499 | 
             
                            postEvent: postEvent
         | 
| 1433 1500 | 
             
                        };
         | 
| 1434 1501 | 
             
                        const collapsibleJsonSection = this.createCollapsibleJsonSection(toolCallData);
         | 
| 1435 | 
            -
             | 
| 1502 | 
            +
             | 
| 1436 1503 | 
             
                        if (this.dataContainer) {
         | 
| 1437 1504 | 
             
                            this.dataContainer.innerHTML = contextualHeader + content + collapsibleJsonSection;
         | 
| 1438 1505 | 
             
                        }
         | 
| 1439 | 
            -
             | 
| 1506 | 
            +
             | 
| 1440 1507 | 
             
                        // Initialize JSON toggle functionality
         | 
| 1441 1508 | 
             
                        this.initializeJsonToggle();
         | 
| 1442 1509 | 
             
                    }
         | 
| @@ -1461,7 +1528,7 @@ class ModuleViewer { | |
| 1461 1528 | 
             
                    const operations = fileData.operations || [];
         | 
| 1462 1529 | 
             
                    const lastOp = operations[operations.length - 1];
         | 
| 1463 1530 | 
             
                    const headerTimestamp = lastOp ? this.formatTimestamp(lastOp.timestamp) : '';
         | 
| 1464 | 
            -
             | 
| 1531 | 
            +
             | 
| 1465 1532 | 
             
                    // Create contextual header
         | 
| 1466 1533 | 
             
                    const contextualHeader = `
         | 
| 1467 1534 | 
             
                        <div class="contextual-header">
         | 
| @@ -1487,7 +1554,7 @@ class ModuleViewer { | |
| 1487 1554 | 
             
                                                <span class="operation-timestamp">${new Date(op.timestamp).toLocaleString()}</span>
         | 
| 1488 1555 | 
             
                                                ${this.isReadOnlyOperation(op.operation) ? `
         | 
| 1489 1556 | 
             
                                                    <!-- Read-only operation: show only file viewer -->
         | 
| 1490 | 
            -
                                                    <span class="file-viewer-icon" | 
| 1557 | 
            +
                                                    <span class="file-viewer-icon"
         | 
| 1491 1558 | 
             
                                                          onclick="showFileViewerModal('${filePath}')"
         | 
| 1492 1559 | 
             
                                                          title="View file contents with syntax highlighting"
         | 
| 1493 1560 | 
             
                                                          style="margin-left: 8px; cursor: pointer; font-size: 16px;">
         | 
| @@ -1495,13 +1562,13 @@ class ModuleViewer { | |
| 1495 1562 | 
             
                                                    </span>
         | 
| 1496 1563 | 
             
                                                ` : `
         | 
| 1497 1564 | 
             
                                                    <!-- Edit operation: show both file viewer and git diff -->
         | 
| 1498 | 
            -
                                                    <span class="file-viewer-icon" | 
| 1565 | 
            +
                                                    <span class="file-viewer-icon"
         | 
| 1499 1566 | 
             
                                                          onclick="showFileViewerModal('${filePath}')"
         | 
| 1500 1567 | 
             
                                                          title="View file contents with syntax highlighting"
         | 
| 1501 1568 | 
             
                                                          style="margin-left: 8px; cursor: pointer; font-size: 16px;">
         | 
| 1502 1569 | 
             
                                                        đī¸
         | 
| 1503 1570 | 
             
                                                    </span>
         | 
| 1504 | 
            -
                                                    <span class="git-diff-icon" | 
| 1571 | 
            +
                                                    <span class="git-diff-icon"
         | 
| 1505 1572 | 
             
                                                          onclick="showGitDiffModal('${filePath}', '${op.timestamp}')"
         | 
| 1506 1573 | 
             
                                                          title="View git diff for this file operation"
         | 
| 1507 1574 | 
             
                                                          style="margin-left: 8px; cursor: pointer; font-size: 16px; display: none;"
         | 
| @@ -1525,21 +1592,21 @@ class ModuleViewer { | |
| 1525 1592 |  | 
| 1526 1593 | 
             
                    // Check git tracking status and show track control if needed
         | 
| 1527 1594 | 
             
                    this.checkAndShowTrackControl(filePath);
         | 
| 1528 | 
            -
             | 
| 1595 | 
            +
             | 
| 1529 1596 | 
             
                    // Check git status and conditionally show git diff icons
         | 
| 1530 1597 | 
             
                    this.checkAndShowGitDiffIcons(filePath);
         | 
| 1531 1598 |  | 
| 1532 1599 | 
             
                    // Create collapsible JSON section for file data
         | 
| 1533 1600 | 
             
                    const collapsibleJsonSection = this.createCollapsibleJsonSection(fileData);
         | 
| 1534 | 
            -
             | 
| 1601 | 
            +
             | 
| 1535 1602 | 
             
                    // Show structured data with JSON section in data pane
         | 
| 1536 1603 | 
             
                    if (this.dataContainer) {
         | 
| 1537 1604 | 
             
                        this.dataContainer.innerHTML = contextualHeader + content + collapsibleJsonSection;
         | 
| 1538 1605 | 
             
                    }
         | 
| 1539 | 
            -
             | 
| 1606 | 
            +
             | 
| 1540 1607 | 
             
                    // Initialize JSON toggle functionality
         | 
| 1541 1608 | 
             
                    this.initializeJsonToggle();
         | 
| 1542 | 
            -
             | 
| 1609 | 
            +
             | 
| 1543 1610 | 
             
                    // Hide JSON pane since data is integrated above
         | 
| 1544 1611 | 
             
                    // JSON container no longer exists - handled via collapsible sections
         | 
| 1545 1612 | 
             
                }
         | 
| @@ -1560,18 +1627,18 @@ class ModuleViewer { | |
| 1560 1627 | 
             
                            </div>
         | 
| 1561 1628 | 
             
                        </div>
         | 
| 1562 1629 | 
             
                    `;
         | 
| 1563 | 
            -
             | 
| 1630 | 
            +
             | 
| 1564 1631 | 
             
                    // Create collapsible JSON section for error data
         | 
| 1565 1632 | 
             
                    const errorData = { title, message };
         | 
| 1566 1633 | 
             
                    const collapsibleJsonSection = this.createCollapsibleJsonSection(errorData);
         | 
| 1567 | 
            -
             | 
| 1634 | 
            +
             | 
| 1568 1635 | 
             
                    if (this.dataContainer) {
         | 
| 1569 1636 | 
             
                        this.dataContainer.innerHTML = content + collapsibleJsonSection;
         | 
| 1570 1637 | 
             
                    }
         | 
| 1571 | 
            -
             | 
| 1638 | 
            +
             | 
| 1572 1639 | 
             
                    // Initialize JSON toggle functionality
         | 
| 1573 1640 | 
             
                    this.initializeJsonToggle();
         | 
| 1574 | 
            -
             | 
| 1641 | 
            +
             | 
| 1575 1642 | 
             
                    // JSON container no longer exists - handled via collapsible sections
         | 
| 1576 1643 | 
             
                }
         | 
| 1577 1644 |  | 
| @@ -1599,7 +1666,7 @@ class ModuleViewer { | |
| 1599 1666 | 
             
                    // Get agent inference to determine which agent this is
         | 
| 1600 1667 | 
             
                    const agentInference = window.dashboard?.agentInference;
         | 
| 1601 1668 | 
             
                    const eventViewer = window.dashboard?.eventViewer;
         | 
| 1602 | 
            -
             | 
| 1669 | 
            +
             | 
| 1603 1670 | 
             
                    if (!agentInference || !eventViewer) {
         | 
| 1604 1671 | 
             
                        console.warn('AgentInference or EventViewer not available, falling back to single event view');
         | 
| 1605 1672 | 
             
                        this.showEventDetails(event);
         | 
| @@ -1608,16 +1675,16 @@ class ModuleViewer { | |
| 1608 1675 |  | 
| 1609 1676 | 
             
                    const inference = agentInference.getInferredAgentForEvent(event);
         | 
| 1610 1677 | 
             
                    const agentName = inference?.agentName || this.extractAgent(event) || 'Unknown';
         | 
| 1611 | 
            -
             | 
| 1678 | 
            +
             | 
| 1612 1679 | 
             
                    // Get all events from this agent
         | 
| 1613 1680 | 
             
                    const allEvents = eventViewer.events || [];
         | 
| 1614 1681 | 
             
                    const agentEvents = this.getAgentSpecificEvents(allEvents, agentName, agentInference);
         | 
| 1615 | 
            -
             | 
| 1682 | 
            +
             | 
| 1616 1683 | 
             
                    console.log(`Showing details for agent: ${agentName}, found ${agentEvents.length} related events`);
         | 
| 1617 | 
            -
             | 
| 1684 | 
            +
             | 
| 1618 1685 | 
             
                    // Extract agent-specific data
         | 
| 1619 1686 | 
             
                    const agentData = this.extractAgentSpecificData(agentName, agentEvents);
         | 
| 1620 | 
            -
             | 
| 1687 | 
            +
             | 
| 1621 1688 | 
             
                    // Render agent-specific view
         | 
| 1622 1689 | 
             
                    this.renderAgentSpecificView(agentName, agentData, event);
         | 
| 1623 1690 | 
             
                }
         | 
| @@ -1634,7 +1701,7 @@ class ModuleViewer { | |
| 1634 1701 | 
             
                        // Use agent inference to determine if this event belongs to the agent
         | 
| 1635 1702 | 
             
                        const inference = agentInference.getInferredAgentForEvent(event);
         | 
| 1636 1703 | 
             
                        const eventAgentName = inference?.agentName || this.extractAgent(event) || 'Unknown';
         | 
| 1637 | 
            -
             | 
| 1704 | 
            +
             | 
| 1638 1705 | 
             
                        // Match agent names (case insensitive)
         | 
| 1639 1706 | 
             
                        return eventAgentName.toLowerCase() === agentName.toLowerCase();
         | 
| 1640 1707 | 
             
                    });
         | 
| @@ -1662,7 +1729,7 @@ class ModuleViewer { | |
| 1662 1729 | 
             
                    agentEvents.forEach(event => {
         | 
| 1663 1730 | 
             
                        const eventData = event.data || {};
         | 
| 1664 1731 | 
             
                        const timestamp = new Date(event.timestamp);
         | 
| 1665 | 
            -
             | 
| 1732 | 
            +
             | 
| 1666 1733 | 
             
                        // Track timing
         | 
| 1667 1734 | 
             
                        if (!data.firstSeen || timestamp < data.firstSeen) {
         | 
| 1668 1735 | 
             
                            data.firstSeen = timestamp;
         | 
| @@ -1670,16 +1737,16 @@ class ModuleViewer { | |
| 1670 1737 | 
             
                        if (!data.lastSeen || timestamp > data.lastSeen) {
         | 
| 1671 1738 | 
             
                            data.lastSeen = timestamp;
         | 
| 1672 1739 | 
             
                        }
         | 
| 1673 | 
            -
             | 
| 1740 | 
            +
             | 
| 1674 1741 | 
             
                        // Track sessions
         | 
| 1675 1742 | 
             
                        if (event.session_id || eventData.session_id) {
         | 
| 1676 1743 | 
             
                            data.sessions.add(event.session_id || eventData.session_id);
         | 
| 1677 1744 | 
             
                        }
         | 
| 1678 | 
            -
             | 
| 1745 | 
            +
             | 
| 1679 1746 | 
             
                        // Track event types
         | 
| 1680 1747 | 
             
                        const eventType = event.hook_event_name || event.type || 'unknown';
         | 
| 1681 1748 | 
             
                        data.eventTypes.add(eventType);
         | 
| 1682 | 
            -
             | 
| 1749 | 
            +
             | 
| 1683 1750 | 
             
                        // Extract prompt from Task delegation events
         | 
| 1684 1751 | 
             
                        if (event.type === 'hook' && eventData.tool_name === 'Task' && eventData.tool_parameters) {
         | 
| 1685 1752 | 
             
                            const taskParams = eventData.tool_parameters;
         | 
| @@ -1694,12 +1761,12 @@ class ModuleViewer { | |
| 1694 1761 | 
             
                                data.prompt = taskParams.prompt;
         | 
| 1695 1762 | 
             
                            }
         | 
| 1696 1763 | 
             
                        }
         | 
| 1697 | 
            -
             | 
| 1764 | 
            +
             | 
| 1698 1765 | 
             
                        // Also check for agent-specific prompts in other event types
         | 
| 1699 1766 | 
             
                        if (eventData.prompt && (eventData.agent_type === agentName || eventData.subagent_type === agentName)) {
         | 
| 1700 1767 | 
             
                            data.prompt = eventData.prompt;
         | 
| 1701 1768 | 
             
                        }
         | 
| 1702 | 
            -
             | 
| 1769 | 
            +
             | 
| 1703 1770 | 
             
                        // Extract todos from TodoWrite events
         | 
| 1704 1771 | 
             
                        if (event.type === 'todo' || (event.type === 'hook' && eventData.tool_name === 'TodoWrite')) {
         | 
| 1705 1772 | 
             
                            const todos = eventData.todos || eventData.tool_parameters?.todos;
         | 
| @@ -1717,12 +1784,12 @@ class ModuleViewer { | |
| 1717 1784 | 
             
                                });
         | 
| 1718 1785 | 
             
                            }
         | 
| 1719 1786 | 
             
                        }
         | 
| 1720 | 
            -
             | 
| 1787 | 
            +
             | 
| 1721 1788 | 
             
                        // Extract tool calls - collect pre and post events separately first
         | 
| 1722 1789 | 
             
                        if (event.type === 'hook' && eventData.tool_name) {
         | 
| 1723 1790 | 
             
                            const phase = event.subtype || eventData.event_type;
         | 
| 1724 1791 | 
             
                            const toolCallId = this.generateToolCallId(eventData.tool_name, eventData.tool_parameters, timestamp);
         | 
| 1725 | 
            -
             | 
| 1792 | 
            +
             | 
| 1726 1793 | 
             
                            if (phase === 'pre_tool') {
         | 
| 1727 1794 | 
             
                                // Store pre-tool event data
         | 
| 1728 1795 | 
             
                                if (!data._preToolEvents) data._preToolEvents = new Map();
         | 
| @@ -1746,20 +1813,20 @@ class ModuleViewer { | |
| 1746 1813 | 
             
                            }
         | 
| 1747 1814 | 
             
                        }
         | 
| 1748 1815 | 
             
                    });
         | 
| 1749 | 
            -
             | 
| 1816 | 
            +
             | 
| 1750 1817 | 
             
                    // Sort todos by timestamp (most recent first)
         | 
| 1751 1818 | 
             
                    data.todos.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
         | 
| 1752 | 
            -
             | 
| 1819 | 
            +
             | 
| 1753 1820 | 
             
                    // Consolidate pre and post tool events into single tool calls
         | 
| 1754 1821 | 
             
                    data.toolsCalled = this.consolidateToolCalls(data._preToolEvents, data._postToolEvents);
         | 
| 1755 | 
            -
             | 
| 1822 | 
            +
             | 
| 1756 1823 | 
             
                    // Clean up temporary data
         | 
| 1757 1824 | 
             
                    delete data._preToolEvents;
         | 
| 1758 1825 | 
             
                    delete data._postToolEvents;
         | 
| 1759 | 
            -
             | 
| 1826 | 
            +
             | 
| 1760 1827 | 
             
                    // Sort tools by timestamp (most recent first)
         | 
| 1761 1828 | 
             
                    data.toolsCalled.sort((a, b) => b.timestamp - a.timestamp);
         | 
| 1762 | 
            -
             | 
| 1829 | 
            +
             | 
| 1763 1830 | 
             
                    return data;
         | 
| 1764 1831 | 
             
                }
         | 
| 1765 1832 |  | 
| @@ -1774,7 +1841,7 @@ class ModuleViewer { | |
| 1774 1841 | 
             
                    // Create a unique identifier based on tool name, key parameters, and approximate timestamp
         | 
| 1775 1842 | 
             
                    // Use a wider time window to account for timing differences between pre/post events
         | 
| 1776 1843 | 
             
                    const timeWindow = Math.floor(timestamp.getTime() / 5000); // Group by 5-second windows
         | 
| 1777 | 
            -
             | 
| 1844 | 
            +
             | 
| 1778 1845 | 
             
                    // Include key parameters that uniquely identify a tool call
         | 
| 1779 1846 | 
             
                    let paramKey = '';
         | 
| 1780 1847 | 
             
                    if (parameters) {
         | 
| @@ -1787,15 +1854,15 @@ class ModuleViewer { | |
| 1787 1854 | 
             
                        if (parameters.notebook_path) keyParams.push(parameters.notebook_path);
         | 
| 1788 1855 | 
             
                        if (parameters.url) keyParams.push(parameters.url);
         | 
| 1789 1856 | 
             
                        if (parameters.prompt) keyParams.push(parameters.prompt.substring(0, 30));
         | 
| 1790 | 
            -
             | 
| 1857 | 
            +
             | 
| 1791 1858 | 
             
                        paramKey = keyParams.join('|');
         | 
| 1792 1859 | 
             
                    }
         | 
| 1793 | 
            -
             | 
| 1860 | 
            +
             | 
| 1794 1861 | 
             
                    // If no specific parameters, use just tool name and time window
         | 
| 1795 1862 | 
             
                    if (!paramKey) {
         | 
| 1796 1863 | 
             
                        paramKey = 'default';
         | 
| 1797 1864 | 
             
                    }
         | 
| 1798 | 
            -
             | 
| 1865 | 
            +
             | 
| 1799 1866 | 
             
                    return `${toolName}:${timeWindow}:${paramKey}`;
         | 
| 1800 1867 | 
             
                }
         | 
| 1801 1868 |  | 
| @@ -1808,16 +1875,16 @@ class ModuleViewer { | |
| 1808 1875 | 
             
                consolidateToolCalls(preToolEvents, postToolEvents) {
         | 
| 1809 1876 | 
             
                    const consolidatedCalls = [];
         | 
| 1810 1877 | 
             
                    const processedIds = new Set();
         | 
| 1811 | 
            -
             | 
| 1878 | 
            +
             | 
| 1812 1879 | 
             
                    if (!preToolEvents) preToolEvents = new Map();
         | 
| 1813 1880 | 
             
                    if (!postToolEvents) postToolEvents = new Map();
         | 
| 1814 | 
            -
             | 
| 1881 | 
            +
             | 
| 1815 1882 | 
             
                    // Process all pre-tool events first
         | 
| 1816 1883 | 
             
                    for (const [toolCallId, preEvent] of preToolEvents) {
         | 
| 1817 1884 | 
             
                        if (processedIds.has(toolCallId)) continue;
         | 
| 1818 | 
            -
             | 
| 1885 | 
            +
             | 
| 1819 1886 | 
             
                        const postEvent = postToolEvents.get(toolCallId);
         | 
| 1820 | 
            -
             | 
| 1887 | 
            +
             | 
| 1821 1888 | 
             
                        // Create consolidated tool call
         | 
| 1822 1889 | 
             
                        const consolidatedCall = {
         | 
| 1823 1890 | 
             
                            toolName: preEvent.toolName,
         | 
| @@ -1828,7 +1895,7 @@ class ModuleViewer { | |
| 1828 1895 | 
             
                            statusIcon: this.getToolCallStatusIcon(preEvent, postEvent),
         | 
| 1829 1896 | 
             
                            phase: postEvent ? 'completed' : 'running'
         | 
| 1830 1897 | 
             
                        };
         | 
| 1831 | 
            -
             | 
| 1898 | 
            +
             | 
| 1832 1899 | 
             
                        // Add post-event data if available
         | 
| 1833 1900 | 
             
                        if (postEvent) {
         | 
| 1834 1901 | 
             
                            consolidatedCall.success = postEvent.success;
         | 
| @@ -1837,15 +1904,15 @@ class ModuleViewer { | |
| 1837 1904 | 
             
                            consolidatedCall.exitCode = postEvent.exitCode;
         | 
| 1838 1905 | 
             
                            consolidatedCall.completedAt = postEvent.timestamp;
         | 
| 1839 1906 | 
             
                        }
         | 
| 1840 | 
            -
             | 
| 1907 | 
            +
             | 
| 1841 1908 | 
             
                        consolidatedCalls.push(consolidatedCall);
         | 
| 1842 1909 | 
             
                        processedIds.add(toolCallId);
         | 
| 1843 1910 | 
             
                    }
         | 
| 1844 | 
            -
             | 
| 1911 | 
            +
             | 
| 1845 1912 | 
             
                    // Process any post-tool events that don't have matching pre-tool events (edge case)
         | 
| 1846 1913 | 
             
                    for (const [toolCallId, postEvent] of postToolEvents) {
         | 
| 1847 1914 | 
             
                        if (processedIds.has(toolCallId)) continue;
         | 
| 1848 | 
            -
             | 
| 1915 | 
            +
             | 
| 1849 1916 | 
             
                        // This is a post-tool event without a corresponding pre-tool event
         | 
| 1850 1917 | 
             
                        const consolidatedCall = {
         | 
| 1851 1918 | 
             
                            toolName: postEvent.toolName,
         | 
| @@ -1861,11 +1928,11 @@ class ModuleViewer { | |
| 1861 1928 | 
             
                            exitCode: postEvent.exitCode,
         | 
| 1862 1929 | 
             
                            completedAt: postEvent.timestamp
         | 
| 1863 1930 | 
             
                        };
         | 
| 1864 | 
            -
             | 
| 1931 | 
            +
             | 
| 1865 1932 | 
             
                        consolidatedCalls.push(consolidatedCall);
         | 
| 1866 1933 | 
             
                        processedIds.add(toolCallId);
         | 
| 1867 1934 | 
             
                    }
         | 
| 1868 | 
            -
             | 
| 1935 | 
            +
             | 
| 1869 1936 | 
             
                    return consolidatedCalls;
         | 
| 1870 1937 | 
             
                }
         | 
| 1871 1938 |  | 
| @@ -1879,7 +1946,7 @@ class ModuleViewer { | |
| 1879 1946 | 
             
                    if (!postEvent) {
         | 
| 1880 1947 | 
             
                        return 'Running...';
         | 
| 1881 1948 | 
             
                    }
         | 
| 1882 | 
            -
             | 
| 1949 | 
            +
             | 
| 1883 1950 | 
             
                    if (postEvent.success === true) {
         | 
| 1884 1951 | 
             
                        return 'Success';
         | 
| 1885 1952 | 
             
                    } else if (postEvent.success === false) {
         | 
| @@ -1891,7 +1958,7 @@ class ModuleViewer { | |
| 1891 1958 | 
             
                    } else if (postEvent.exitCode !== undefined && postEvent.exitCode !== 0) {
         | 
| 1892 1959 | 
             
                        return 'Error';
         | 
| 1893 1960 | 
             
                    }
         | 
| 1894 | 
            -
             | 
| 1961 | 
            +
             | 
| 1895 1962 | 
             
                    return 'Completed';
         | 
| 1896 1963 | 
             
                }
         | 
| 1897 1964 |  | 
| @@ -1905,7 +1972,7 @@ class ModuleViewer { | |
| 1905 1972 | 
             
                    if (!postEvent) {
         | 
| 1906 1973 | 
             
                        return 'âŗ'; // Still running
         | 
| 1907 1974 | 
             
                    }
         | 
| 1908 | 
            -
             | 
| 1975 | 
            +
             | 
| 1909 1976 | 
             
                    if (postEvent.success === true) {
         | 
| 1910 1977 | 
             
                        return 'â
'; // Success
         | 
| 1911 1978 | 
             
                    } else if (postEvent.success === false) {
         | 
| @@ -1917,7 +1984,7 @@ class ModuleViewer { | |
| 1917 1984 | 
             
                    } else if (postEvent.exitCode !== undefined && postEvent.exitCode !== 0) {
         | 
| 1918 1985 | 
             
                        return 'â'; // Error
         | 
| 1919 1986 | 
             
                    }
         | 
| 1920 | 
            -
             | 
| 1987 | 
            +
             | 
| 1921 1988 | 
             
                    return 'â
'; // Default to success for completed calls
         | 
| 1922 1989 | 
             
                }
         | 
| 1923 1990 |  | 
| @@ -1928,12 +1995,12 @@ class ModuleViewer { | |
| 1928 1995 | 
             
                 */
         | 
| 1929 1996 | 
             
                estimateTokenCount(text) {
         | 
| 1930 1997 | 
             
                    if (!text || typeof text !== 'string') return 0;
         | 
| 1931 | 
            -
             | 
| 1998 | 
            +
             | 
| 1932 1999 | 
             
                    // Simple token estimation: words * 1.3 (accounts for subwords)
         | 
| 1933 2000 | 
             
                    // Alternative: characters / 4 (common rule of thumb)
         | 
| 1934 2001 | 
             
                    const wordCount = text.trim().split(/\s+/).length;
         | 
| 1935 2002 | 
             
                    const charBasedEstimate = Math.ceil(text.length / 4);
         | 
| 1936 | 
            -
             | 
| 2003 | 
            +
             | 
| 1937 2004 | 
             
                    // Use the higher of the two estimates for safety
         | 
| 1938 2005 | 
             
                    return Math.max(wordCount * 1.3, charBasedEstimate);
         | 
| 1939 2006 | 
             
                }
         | 
| @@ -1945,19 +2012,19 @@ class ModuleViewer { | |
| 1945 2012 | 
             
                 */
         | 
| 1946 2013 | 
             
                trimPromptWhitespace(text) {
         | 
| 1947 2014 | 
             
                    if (!text || typeof text !== 'string') return '';
         | 
| 1948 | 
            -
             | 
| 2015 | 
            +
             | 
| 1949 2016 | 
             
                    // Remove leading/trailing whitespace from the entire text
         | 
| 1950 2017 | 
             
                    text = text.trim();
         | 
| 1951 | 
            -
             | 
| 2018 | 
            +
             | 
| 1952 2019 | 
             
                    // Reduce multiple consecutive newlines to maximum of 2
         | 
| 1953 2020 | 
             
                    text = text.replace(/\n\s*\n\s*\n+/g, '\n\n');
         | 
| 1954 | 
            -
             | 
| 2021 | 
            +
             | 
| 1955 2022 | 
             
                    // Trim whitespace from each line while preserving intentional indentation
         | 
| 1956 2023 | 
             
                    text = text.split('\n').map(line => {
         | 
| 1957 2024 | 
             
                        // Only trim trailing whitespace, preserve leading whitespace for structure
         | 
| 1958 2025 | 
             
                        return line.replace(/\s+$/, '');
         | 
| 1959 2026 | 
             
                    }).join('\n');
         | 
| 1960 | 
            -
             | 
| 2027 | 
            +
             | 
| 1961 2028 | 
             
                    return text;
         | 
| 1962 2029 | 
             
                }
         | 
| 1963 2030 |  | 
| @@ -1995,7 +2062,7 @@ class ModuleViewer { | |
| 1995 2062 | 
             
                        const trimmedPrompt = this.trimPromptWhitespace(agentData.prompt);
         | 
| 1996 2063 | 
             
                        const tokenCount = Math.round(this.estimateTokenCount(trimmedPrompt));
         | 
| 1997 2064 | 
             
                        const wordCount = trimmedPrompt.trim().split(/\s+/).length;
         | 
| 1998 | 
            -
             | 
| 2065 | 
            +
             | 
| 1999 2066 | 
             
                        content += `
         | 
| 2000 2067 | 
             
                            <div class="agent-prompt-section">
         | 
| 2001 2068 | 
             
                                <div class="contextual-header">
         | 
| @@ -2049,7 +2116,7 @@ class ModuleViewer { | |
| 2049 2116 | 
             
                                        else if (tool.statusIcon === 'â') statusClass = 'status-failed';
         | 
| 2050 2117 | 
             
                                        else if (tool.statusIcon === 'â ī¸') statusClass = 'status-blocked';
         | 
| 2051 2118 | 
             
                                        else if (tool.statusIcon === 'âŗ') statusClass = 'status-running';
         | 
| 2052 | 
            -
             | 
| 2119 | 
            +
             | 
| 2053 2120 | 
             
                                        return `
         | 
| 2054 2121 | 
             
                                            <div class="tool-call-item">
         | 
| 2055 2122 | 
             
                                                <div class="tool-call-header">
         | 
| @@ -2080,15 +2147,15 @@ class ModuleViewer { | |
| 2080 2147 | 
             
                        originalEvent: originalEvent
         | 
| 2081 2148 | 
             
                    };
         | 
| 2082 2149 | 
             
                    const collapsibleJsonSection = this.createCollapsibleJsonSection(agentJsonData);
         | 
| 2083 | 
            -
             | 
| 2150 | 
            +
             | 
| 2084 2151 | 
             
                    // Show structured data with JSON section in data pane
         | 
| 2085 2152 | 
             
                    if (this.dataContainer) {
         | 
| 2086 2153 | 
             
                        this.dataContainer.innerHTML = contextualHeader + content + collapsibleJsonSection;
         | 
| 2087 2154 | 
             
                    }
         | 
| 2088 | 
            -
             | 
| 2155 | 
            +
             | 
| 2089 2156 | 
             
                    // Initialize JSON toggle functionality
         | 
| 2090 2157 | 
             
                    this.initializeJsonToggle();
         | 
| 2091 | 
            -
             | 
| 2158 | 
            +
             | 
| 2092 2159 | 
             
                    // Hide JSON pane since data is integrated above
         | 
| 2093 2160 | 
             
                    // JSON container no longer exists - handled via collapsible sections
         | 
| 2094 2161 | 
             
                }
         | 
| @@ -2119,7 +2186,7 @@ class ModuleViewer { | |
| 2119 2186 |  | 
| 2120 2187 | 
             
                    // Get inline result content
         | 
| 2121 2188 | 
             
                    const inlineContent = this.createInlineToolResultContent(mockData, mockEvent);
         | 
| 2122 | 
            -
             | 
| 2189 | 
            +
             | 
| 2123 2190 | 
             
                    // If we have content, wrap it in a simple section
         | 
| 2124 2191 | 
             
                    if (inlineContent.trim()) {
         | 
| 2125 2192 | 
             
                        return `
         | 
| @@ -2130,7 +2197,7 @@ class ModuleViewer { | |
| 2130 2197 | 
             
                            </div>
         | 
| 2131 2198 | 
             
                        `;
         | 
| 2132 2199 | 
             
                    }
         | 
| 2133 | 
            -
             | 
| 2200 | 
            +
             | 
| 2134 2201 | 
             
                    return '';
         | 
| 2135 2202 | 
             
                }
         | 
| 2136 2203 |  | 
| @@ -2143,7 +2210,7 @@ class ModuleViewer { | |
| 2143 2210 | 
             
                 */
         | 
| 2144 2211 | 
             
                extractToolTarget(toolName, parameters, altParameters) {
         | 
| 2145 2212 | 
             
                    const params = parameters || altParameters || {};
         | 
| 2146 | 
            -
             | 
| 2213 | 
            +
             | 
| 2147 2214 | 
             
                    switch (toolName?.toLowerCase()) {
         | 
| 2148 2215 | 
             
                        case 'write':
         | 
| 2149 2216 | 
             
                        case 'read':
         | 
| @@ -2214,7 +2281,7 @@ class ModuleViewer { | |
| 2214 2281 |  | 
| 2215 2282 | 
             
                        // Get working directory from dashboard with proper fallback
         | 
| 2216 2283 | 
             
                        let workingDir = window.dashboard?.currentWorkingDir;
         | 
| 2217 | 
            -
             | 
| 2284 | 
            +
             | 
| 2218 2285 | 
             
                        // Don't use 'Unknown' as a working directory
         | 
| 2219 2286 | 
             
                        if (!workingDir || workingDir === 'Unknown' || workingDir.trim() === '') {
         | 
| 2220 2287 | 
             
                            // Try to get from footer element
         | 
| @@ -2274,7 +2341,7 @@ class ModuleViewer { | |
| 2274 2341 | 
             
                displayTrackingStatus(filePath, result) {
         | 
| 2275 2342 | 
             
                    const statusElementId = `git-track-status-${filePath.replace(/[^a-zA-Z0-9]/g, '-')}`;
         | 
| 2276 2343 | 
             
                    const statusElement = document.getElementById(statusElementId);
         | 
| 2277 | 
            -
             | 
| 2344 | 
            +
             | 
| 2278 2345 | 
             
                    if (!statusElement) return;
         | 
| 2279 2346 |  | 
| 2280 2347 | 
             
                    if (result.success && result.is_tracked === false) {
         | 
| @@ -2283,7 +2350,7 @@ class ModuleViewer { | |
| 2283 2350 | 
             
                            <div class="untracked-file-notice">
         | 
| 2284 2351 | 
             
                                <span class="untracked-icon">â ī¸</span>
         | 
| 2285 2352 | 
             
                                <span class="untracked-text">This file is not tracked by git</span>
         | 
| 2286 | 
            -
                                <button class="track-file-button" | 
| 2353 | 
            +
                                <button class="track-file-button"
         | 
| 2287 2354 | 
             
                                        onclick="window.moduleViewer.trackFile('${filePath}')"
         | 
| 2288 2355 | 
             
                                        title="Add this file to git tracking">
         | 
| 2289 2356 | 
             
                                    <span class="git-icon">đ</span> Track File
         | 
| @@ -2326,7 +2393,7 @@ class ModuleViewer { | |
| 2326 2393 |  | 
| 2327 2394 | 
             
                        // Get working directory from dashboard with proper fallback
         | 
| 2328 2395 | 
             
                        let workingDir = window.dashboard?.currentWorkingDir;
         | 
| 2329 | 
            -
             | 
| 2396 | 
            +
             | 
| 2330 2397 | 
             
                        // Don't use 'Unknown' as a working directory
         | 
| 2331 2398 | 
             
                        if (!workingDir || workingDir === 'Unknown' || workingDir.trim() === '') {
         | 
| 2332 2399 | 
             
                            // Try to get from footer element
         | 
| @@ -2343,7 +2410,7 @@ class ModuleViewer { | |
| 2343 2410 | 
             
                        // Update button to show loading state
         | 
| 2344 2411 | 
             
                        const statusElementId = `git-track-status-${filePath.replace(/[^a-zA-Z0-9]/g, '-')}`;
         | 
| 2345 2412 | 
             
                        const statusElement = document.getElementById(statusElementId);
         | 
| 2346 | 
            -
             | 
| 2413 | 
            +
             | 
| 2347 2414 | 
             
                        if (statusElement) {
         | 
| 2348 2415 | 
             
                            statusElement.innerHTML = `
         | 
| 2349 2416 | 
             
                                <div class="tracking-file-notice">
         | 
| @@ -2396,7 +2463,7 @@ class ModuleViewer { | |
| 2396 2463 | 
             
                                    </div>
         | 
| 2397 2464 | 
             
                                `;
         | 
| 2398 2465 | 
             
                            }
         | 
| 2399 | 
            -
             | 
| 2466 | 
            +
             | 
| 2400 2467 | 
             
                            // Show success notification
         | 
| 2401 2468 | 
             
                            this.showNotification('File tracked successfully', 'success');
         | 
| 2402 2469 | 
             
                        } else {
         | 
| @@ -2405,7 +2472,7 @@ class ModuleViewer { | |
| 2405 2472 | 
             
                                    <div class="tracking-error-notice">
         | 
| 2406 2473 | 
             
                                        <span class="error-icon">â</span>
         | 
| 2407 2474 | 
             
                                        <span class="error-text">Failed to track file: ${result.error || 'Unknown error'}</span>
         | 
| 2408 | 
            -
                                        <button class="track-file-button" | 
| 2475 | 
            +
                                        <button class="track-file-button"
         | 
| 2409 2476 | 
             
                                                onclick="window.moduleViewer.trackFile('${filePath}')"
         | 
| 2410 2477 | 
             
                                                title="Try again">
         | 
| 2411 2478 | 
             
                                            <span class="git-icon">đ</span> Retry
         | 
| @@ -2413,24 +2480,24 @@ class ModuleViewer { | |
| 2413 2480 | 
             
                                    </div>
         | 
| 2414 2481 | 
             
                                `;
         | 
| 2415 2482 | 
             
                            }
         | 
| 2416 | 
            -
             | 
| 2483 | 
            +
             | 
| 2417 2484 | 
             
                            // Show error notification
         | 
| 2418 2485 | 
             
                            this.showNotification(`Failed to track file: ${result.error}`, 'error');
         | 
| 2419 2486 | 
             
                        }
         | 
| 2420 2487 |  | 
| 2421 2488 | 
             
                    } catch (error) {
         | 
| 2422 2489 | 
             
                        console.error('â Failed to track file:', error);
         | 
| 2423 | 
            -
             | 
| 2490 | 
            +
             | 
| 2424 2491 | 
             
                        // Update UI to show error
         | 
| 2425 2492 | 
             
                        const statusElementId = `git-track-status-${filePath.replace(/[^a-zA-Z0-9]/g, '-')}`;
         | 
| 2426 2493 | 
             
                        const statusElement = document.getElementById(statusElementId);
         | 
| 2427 | 
            -
             | 
| 2494 | 
            +
             | 
| 2428 2495 | 
             
                        if (statusElement) {
         | 
| 2429 2496 | 
             
                            statusElement.innerHTML = `
         | 
| 2430 2497 | 
             
                                <div class="tracking-error-notice">
         | 
| 2431 2498 | 
             
                                    <span class="error-icon">â</span>
         | 
| 2432 2499 | 
             
                                    <span class="error-text">Error: ${error.message}</span>
         | 
| 2433 | 
            -
                                    <button class="track-file-button" | 
| 2500 | 
            +
                                    <button class="track-file-button"
         | 
| 2434 2501 | 
             
                                            onclick="window.moduleViewer.trackFile('${filePath}')"
         | 
| 2435 2502 | 
             
                                            title="Try again">
         | 
| 2436 2503 | 
             
                                        <span class="git-icon">đ</span> Retry
         | 
| @@ -2438,7 +2505,7 @@ class ModuleViewer { | |
| 2438 2505 | 
             
                                </div>
         | 
| 2439 2506 | 
             
                            `;
         | 
| 2440 2507 | 
             
                        }
         | 
| 2441 | 
            -
             | 
| 2508 | 
            +
             | 
| 2442 2509 | 
             
                        // Show error notification
         | 
| 2443 2510 | 
             
                        this.showNotification(`Error tracking file: ${error.message}`, 'error');
         | 
| 2444 2511 | 
             
                    }
         | 
| @@ -2469,7 +2536,7 @@ class ModuleViewer { | |
| 2469 2536 |  | 
| 2470 2537 | 
             
                        // Get working directory from dashboard with proper fallback
         | 
| 2471 2538 | 
             
                        let workingDir = window.dashboard?.currentWorkingDir;
         | 
| 2472 | 
            -
             | 
| 2539 | 
            +
             | 
| 2473 2540 | 
             
                        // Don't use 'Unknown' as a working directory
         | 
| 2474 2541 | 
             
                        if (!workingDir || workingDir === 'Unknown' || workingDir.trim() === '') {
         | 
| 2475 2542 | 
             
                            // Try to get from footer element
         | 
| @@ -2517,7 +2584,7 @@ class ModuleViewer { | |
| 2517 2584 | 
             
                        // Wait for response
         | 
| 2518 2585 | 
             
                        const result = await responsePromise;
         | 
| 2519 2586 | 
             
                        console.debug('[GIT-DIFF-ICONS] Git status check result:', result);
         | 
| 2520 | 
            -
             | 
| 2587 | 
            +
             | 
| 2521 2588 | 
             
                        // Only show git diff icons if git status check was successful
         | 
| 2522 2589 | 
             
                        if (result.success) {
         | 
| 2523 2590 | 
             
                            console.debug('[GIT-DIFF-ICONS] Git status check successful, showing icons for:', filePath);
         | 
| @@ -2539,16 +2606,16 @@ class ModuleViewer { | |
| 2539 2606 | 
             
                 */
         | 
| 2540 2607 | 
             
                showGitDiffIconsForFile(filePath) {
         | 
| 2541 2608 | 
             
                    console.debug('[GIT-DIFF-ICONS] Showing git diff icons for file:', filePath);
         | 
| 2542 | 
            -
             | 
| 2609 | 
            +
             | 
| 2543 2610 | 
             
                    // Find all git diff icons for this file path and show them
         | 
| 2544 2611 | 
             
                    const gitDiffIcons = document.querySelectorAll(`[data-file-path="${filePath}"]`);
         | 
| 2545 2612 | 
             
                    console.debug('[GIT-DIFF-ICONS] Found', gitDiffIcons.length, 'elements with matching file path');
         | 
| 2546 | 
            -
             | 
| 2613 | 
            +
             | 
| 2547 2614 | 
             
                    let shownCount = 0;
         | 
| 2548 2615 | 
             
                    gitDiffIcons.forEach((icon, index) => {
         | 
| 2549 2616 | 
             
                        console.debug('[GIT-DIFF-ICONS] Processing element', index, ':', icon);
         | 
| 2550 2617 | 
             
                        console.debug('[GIT-DIFF-ICONS] Element classes:', icon.classList.toString());
         | 
| 2551 | 
            -
             | 
| 2618 | 
            +
             | 
| 2552 2619 | 
             
                        if (icon.classList.contains('git-diff-icon')) {
         | 
| 2553 2620 | 
             
                            console.debug('[GIT-DIFF-ICONS] Setting display to inline for git-diff-icon');
         | 
| 2554 2621 | 
             
                            icon.style.display = 'inline';
         | 
| @@ -2557,7 +2624,7 @@ class ModuleViewer { | |
| 2557 2624 | 
             
                            console.debug('[GIT-DIFF-ICONS] Element is not a git-diff-icon, skipping');
         | 
| 2558 2625 | 
             
                        }
         | 
| 2559 2626 | 
             
                    });
         | 
| 2560 | 
            -
             | 
| 2627 | 
            +
             | 
| 2561 2628 | 
             
                    console.debug('[GIT-DIFF-ICONS] Showed', shownCount, 'git diff icons for file:', filePath);
         | 
| 2562 2629 | 
             
                }
         | 
| 2563 2630 |  | 
| @@ -2574,7 +2641,7 @@ class ModuleViewer { | |
| 2574 2641 | 
             
                        <span class="notification-icon">${type === 'success' ? 'â
' : type === 'error' ? 'â' : 'âšī¸'}</span>
         | 
| 2575 2642 | 
             
                        <span class="notification-message">${message}</span>
         | 
| 2576 2643 | 
             
                    `;
         | 
| 2577 | 
            -
             | 
| 2644 | 
            +
             | 
| 2578 2645 | 
             
                    // Style the notification
         | 
| 2579 2646 | 
             
                    notification.style.cssText = `
         | 
| 2580 2647 | 
             
                        position: fixed;
         | 
| @@ -2595,7 +2662,7 @@ class ModuleViewer { | |
| 2595 2662 | 
             
                        max-width: 400px;
         | 
| 2596 2663 | 
             
                        animation: slideIn 0.3s ease-out;
         | 
| 2597 2664 | 
             
                    `;
         | 
| 2598 | 
            -
             | 
| 2665 | 
            +
             | 
| 2599 2666 | 
             
                    // Add animation styles
         | 
| 2600 2667 | 
             
                    const style = document.createElement('style');
         | 
| 2601 2668 | 
             
                    style.textContent = `
         | 
| @@ -2609,10 +2676,10 @@ class ModuleViewer { | |
| 2609 2676 | 
             
                        }
         | 
| 2610 2677 | 
             
                    `;
         | 
| 2611 2678 | 
             
                    document.head.appendChild(style);
         | 
| 2612 | 
            -
             | 
| 2679 | 
            +
             | 
| 2613 2680 | 
             
                    // Add to page
         | 
| 2614 2681 | 
             
                    document.body.appendChild(notification);
         | 
| 2615 | 
            -
             | 
| 2682 | 
            +
             | 
| 2616 2683 | 
             
                    // Remove after 5 seconds
         | 
| 2617 2684 | 
             
                    setTimeout(() => {
         | 
| 2618 2685 | 
             
                        notification.style.animation = 'slideOut 0.3s ease-in';
         | 
| @@ -2687,6 +2754,11 @@ class ModuleViewer { | |
| 2687 2754 | 
             
            }
         | 
| 2688 2755 |  | 
| 2689 2756 | 
             
            // Export for global use
         | 
| 2757 | 
            +
            // ES6 Module export
         | 
| 2758 | 
            +
            export { ModuleViewer };
         | 
| 2759 | 
            +
            export default ModuleViewer;
         | 
| 2760 | 
            +
             | 
| 2761 | 
            +
            // Backward compatibility - keep window export for non-module usage
         | 
| 2690 2762 | 
             
            window.ModuleViewer = ModuleViewer;
         | 
| 2691 2763 |  | 
| 2692 2764 | 
             
            // Debug helper function for troubleshooting tool result display
         | 
| @@ -2698,4 +2770,4 @@ window.enableToolResultDebugging = function() { | |
| 2698 2770 | 
             
            window.disableToolResultDebugging = function() {
         | 
| 2699 2771 | 
             
                window.DEBUG_TOOL_RESULTS = false;
         | 
| 2700 2772 | 
             
                console.log('đ§ Tool result debugging disabled.');
         | 
| 2701 | 
            -
            };
         | 
| 2773 | 
            +
            };
         |