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