claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__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 (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
@@ -1,1914 +0,0 @@
1
- /**
2
- * Refactored Dashboard Coordinator
3
- *
4
- * Main coordinator class that orchestrates all dashboard modules while maintaining
5
- * backward compatibility with the original dashboard interface.
6
- *
7
- * WHY: This refactored version breaks down the monolithic 4,133-line dashboard
8
- * into manageable, focused modules while preserving all existing functionality.
9
- * Each module handles a specific concern, improving maintainability and testability.
10
- *
11
- * DESIGN DECISION: Acts as a thin coordinator layer that initializes modules,
12
- * manages inter-module communication through events, and provides backward
13
- * compatibility for existing code that depends on the dashboard interface.
14
- */
15
-
16
- // NOTE: Components are loaded as ES6 modules via index.html
17
- // They expose their classes globally for backward compatibility
18
- // Commenting out ES6 imports to avoid module resolution errors
19
-
20
- // import { SocketManager } from './components/socket-manager.js';
21
- // import { EventViewer } from './components/event-viewer.js';
22
- // import { ModuleViewer } from './components/module-viewer.js';
23
- // import { SessionManager } from './components/session-manager.js';
24
- // import { AgentInference } from './components/agent-inference.js';
25
- // import { AgentHierarchy } from './components/agent-hierarchy.js';
26
- // import { UIStateManager } from './components/ui-state-manager.js';
27
- // import { EventProcessor } from './components/event-processor.js';
28
- // import { ExportManager } from './components/export-manager.js';
29
- // import { WorkingDirectoryManager } from './components/working-directory.js';
30
- // import { FileToolTracker } from './components/file-tool-tracker.js';
31
- // import { BuildTracker } from './components/build-tracker.js';
32
- // import { UnifiedDataViewer } from './components/unified-data-viewer.js';
33
-
34
- class Dashboard {
35
- constructor() {
36
- // Core components (existing)
37
- this.eventViewer = null;
38
- this.moduleViewer = null;
39
- this.sessionManager = null;
40
-
41
- // Retry prevention
42
- this.activityTreeRetryCount = 0;
43
- this.maxRetryAttempts = 10;
44
-
45
- // New modular components
46
- this.socketManager = null;
47
- this.agentInference = null;
48
- this.agentHierarchy = null;
49
- this.uiStateManager = null;
50
- this.eventProcessor = null;
51
- this.exportManager = null;
52
- this.workingDirectoryManager = null;
53
- this.fileToolTracker = null;
54
- this.buildTracker = null;
55
-
56
- // Initialize the dashboard
57
- this.init();
58
- }
59
-
60
- /**
61
- * Initialize the dashboard and all modules
62
- */
63
- init() {
64
- console.log('Initializing refactored Claude MPM Dashboard...');
65
-
66
- try {
67
- // Fetch server configuration first
68
- this.fetchServerConfig();
69
-
70
- // Initialize modules in dependency order
71
- this.initializeSocketManager();
72
- this.initializeCoreComponents();
73
- this.initializeBuildTracker();
74
- this.initializeAgentInference();
75
- this.initializeAgentHierarchy();
76
- this.initializeUIStateManager();
77
- this.initializeWorkingDirectoryManager();
78
- this.initializeFileToolTracker();
79
- this.initializeEventProcessor();
80
- this.initializeExportManager();
81
-
82
- // Set up inter-module communication
83
- this.setupModuleInteractions();
84
-
85
- // Initialize from URL parameters
86
- this.initializeFromURL();
87
-
88
- // FIX: Render current tab after initialization to display any existing data
89
- // WHY: If events were loaded before dashboard init completed, they won't be visible
90
- console.log('[Dashboard] Initial render after dashboard initialization');
91
- this.renderCurrentTab();
92
-
93
- console.log('Claude MPM Dashboard initialized successfully');
94
- } catch (error) {
95
- console.error('Error during dashboard initialization:', error);
96
- // Re-throw to be caught by DOMContentLoaded handler
97
- throw error;
98
- }
99
- }
100
-
101
- /**
102
- * Fetch server configuration for dashboard initialization
103
- */
104
- fetchServerConfig() {
105
- fetch('/api/config')
106
- .then(response => response.json())
107
- .then(config => {
108
- // Store config globally for other components
109
- window.dashboardConfig = config;
110
-
111
- // Update initial UI elements if they exist
112
- const workingDirEl = document.getElementById('working-dir-path');
113
- if (workingDirEl && config.workingDirectory) {
114
- workingDirEl.textContent = config.workingDirectory;
115
- }
116
-
117
- const gitBranchEl = document.getElementById('footer-git-branch');
118
- if (gitBranchEl && config.gitBranch) {
119
- gitBranchEl.textContent = config.gitBranch;
120
- }
121
-
122
- console.log('Dashboard configuration loaded:', config);
123
- })
124
- .catch(error => {
125
- console.warn('Failed to fetch server config:', error);
126
- // Set default config as fallback
127
- window.dashboardConfig = {
128
- workingDirectory: '.',
129
- gitBranch: 'Unknown'
130
- };
131
- });
132
- }
133
-
134
- /**
135
- * Validate that all critical components are initialized
136
- * WHY: Ensures dashboard is in a valid state after initialization
137
- */
138
- validateInitialization() {
139
- const criticalComponents = [
140
- { name: 'socketManager', component: this.socketManager },
141
- { name: 'eventViewer', component: this.eventViewer },
142
- { name: 'agentHierarchy', component: this.agentHierarchy }
143
- ];
144
-
145
- const missing = criticalComponents.filter(c => !c.component);
146
- if (missing.length > 0) {
147
- console.warn('Missing critical components:', missing.map(c => c.name));
148
- }
149
- }
150
-
151
- /**
152
- * Post-initialization setup that requires window.dashboard to be set
153
- * WHY: Some components need to reference window.dashboard but it's not available
154
- * during constructor execution. This method is called after the Dashboard instance
155
- * is assigned to window.dashboard, ensuring proper initialization order.
156
- *
157
- * DESIGN DECISION: Separate post-init phase prevents "cannot read property of undefined"
158
- * errors when components try to access window.dashboard during construction.
159
- */
160
- postInit() {
161
- try {
162
- // Set global reference for agent hierarchy after dashboard is available
163
- if (this.agentHierarchy) {
164
- window.dashboard.agentHierarchy = this.agentHierarchy;
165
- }
166
-
167
- // Initialize any other components that need window.dashboard
168
- this.validateInitialization();
169
- } catch (error) {
170
- console.error('Error in dashboard postInit:', error);
171
- // Continue execution - non-critical error
172
- }
173
- }
174
-
175
- /**
176
- * Initialize socket manager
177
- */
178
- initializeSocketManager() {
179
- this.socketManager = new SocketManager();
180
-
181
- // Set up connection controls
182
- this.socketManager.setupConnectionControls();
183
-
184
- // Backward compatibility
185
- this.socketClient = this.socketManager.getSocketClient();
186
- window.socketClient = this.socketClient;
187
- }
188
-
189
- /**
190
- * Initialize core existing components
191
- */
192
- initializeCoreComponents() {
193
- // Initialize existing components with socket client
194
- this.eventViewer = new EventViewer('events-list', this.socketClient);
195
- this.moduleViewer = new ModuleViewer();
196
- this.sessionManager = new SessionManager(this.socketClient);
197
-
198
- // Backward compatibility
199
- window.eventViewer = this.eventViewer;
200
- window.moduleViewer = this.moduleViewer;
201
- window.sessionManager = this.sessionManager;
202
- }
203
-
204
- /**
205
- * Initialize build tracker
206
- */
207
- initializeBuildTracker() {
208
- this.buildTracker = new BuildTracker();
209
-
210
- // Set the socket client for receiving updates
211
- this.buildTracker.setSocketClient(this.socketClient);
212
-
213
- // Mount to header with retry logic for DOM readiness
214
- const mountBuildTracker = () => {
215
- const headerTitle = document.querySelector('.header-title');
216
- if (headerTitle) {
217
- // Insert after the title and status badge
218
- this.buildTracker.mount(headerTitle);
219
- console.log('BuildTracker mounted successfully');
220
- } else {
221
- console.warn('Header-title element not found for build tracker, will retry');
222
- // Retry after a short delay if DOM is still being constructed
223
- setTimeout(mountBuildTracker, 100);
224
- }
225
- };
226
-
227
- // Try to mount immediately, with retry logic if needed
228
- mountBuildTracker();
229
-
230
- // Make available globally for debugging
231
- window.buildTracker = this.buildTracker;
232
- }
233
-
234
- /**
235
- * Initialize agent inference system
236
- */
237
- initializeAgentInference() {
238
- this.agentInference = new AgentInference(this.eventViewer);
239
- this.agentInference.initialize();
240
- }
241
-
242
- /**
243
- * Initialize agent hierarchy component
244
- * WHY: Creates the agent hierarchy visualization component but defers global
245
- * reference setting to postInit() to avoid initialization order issues.
246
- */
247
- initializeAgentHierarchy() {
248
- try {
249
- this.agentHierarchy = new AgentHierarchy(this.agentInference, this.eventViewer);
250
- // Global reference will be set in postInit() after window.dashboard exists
251
- } catch (error) {
252
- console.error('Failed to initialize agent hierarchy:', error);
253
- // Create a stub to prevent further errors
254
- this.agentHierarchy = {
255
- render: () => '<div class="error">Agent hierarchy unavailable</div>',
256
- expandAllNodes: () => {},
257
- collapseAllNodes: () => {},
258
- updateWithNewEvents: () => {}
259
- };
260
- }
261
- }
262
-
263
- /**
264
- * Initialize UI state manager
265
- */
266
- initializeUIStateManager() {
267
- this.uiStateManager = new UIStateManager();
268
- this.setupTabFilters(); // Set up filters after UI state manager
269
- }
270
-
271
- /**
272
- * Initialize working directory manager
273
- */
274
- initializeWorkingDirectoryManager() {
275
- this.workingDirectoryManager = new WorkingDirectoryManager(this.socketManager);
276
- }
277
-
278
- /**
279
- * Initialize file-tool tracker
280
- */
281
- initializeFileToolTracker() {
282
- this.fileToolTracker = new FileToolTracker(this.agentInference, this.workingDirectoryManager);
283
- }
284
-
285
- /**
286
- * Initialize event processor
287
- */
288
- initializeEventProcessor() {
289
- this.eventProcessor = new EventProcessor(this.eventViewer, this.agentInference);
290
- }
291
-
292
-
293
- /**
294
- * Initialize export manager
295
- */
296
- initializeExportManager() {
297
- this.exportManager = new ExportManager(this.eventViewer);
298
- }
299
-
300
- /**
301
- * Set up interactions between modules
302
- */
303
- setupModuleInteractions() {
304
- // Socket events to update file operations and tool calls
305
- this.socketManager.onEventUpdate((events) => {
306
- console.log('[Dashboard] Processing event update with', events.length, 'events');
307
-
308
- // Debug: Log some sample events to see their structure
309
- if (events.length > 0) {
310
- console.log('[Dashboard] Sample event structure:', {
311
- first_event: events[0],
312
- has_tool_events: events.some(e => e.tool_name || (e.data && e.data.tool_name)),
313
- hook_events: events.filter(e => e.type === 'hook').length,
314
- tool_subtypes: events.filter(e => e.subtype === 'pre_tool' || e.subtype === 'post_tool').length
315
- });
316
- }
317
-
318
- this.fileToolTracker.updateFileOperations(events);
319
- this.fileToolTracker.updateToolCalls(events);
320
-
321
- // Debug: Check what was tracked
322
- const fileOps = this.fileToolTracker.getFileOperations();
323
- const toolCalls = this.fileToolTracker.getToolCalls();
324
- console.log('[Dashboard] After update - File operations:', fileOps.size, 'Tool calls:', toolCalls.size);
325
-
326
- // Process agent inference for new events
327
- this.agentInference.processAgentInference();
328
-
329
- // Update agent hierarchy with new events
330
- this.agentHierarchy.updateWithNewEvents(events);
331
-
332
- // Auto-scroll events list if on events tab
333
- if (this.uiStateManager.getCurrentTab() === 'events') {
334
- this.exportManager.scrollListToBottom('events-list');
335
- }
336
-
337
- // Re-render current tab
338
- this.renderCurrentTab();
339
- });
340
-
341
- // Connection status changes
342
- this.socketManager.onConnectionStatusChange((status, type) => {
343
- // Set up git branch listener when connected
344
- if (type === 'connected') {
345
- this.workingDirectoryManager.updateGitBranch(
346
- this.workingDirectoryManager.getCurrentWorkingDir()
347
- );
348
-
349
- // FIX: Force render current tab to display initial/historical event data
350
- // WHY: Events are loaded on connection but panes aren't rendered with this data
351
- console.log('[Dashboard] Connection established - rendering current tab with initial data');
352
- this.renderCurrentTab();
353
- }
354
- });
355
-
356
- // Tab changes
357
- document.addEventListener('tabChanged', (e) => {
358
- this.renderCurrentTab();
359
- this.uiStateManager.updateTabNavigationItems();
360
- });
361
-
362
- // Events clearing
363
- document.addEventListener('eventsClearing', () => {
364
- this.fileToolTracker.clear();
365
- this.agentInference.initialize();
366
- });
367
-
368
- // Card details requests
369
- document.addEventListener('showCardDetails', (e) => {
370
- this.showCardDetails(e.detail.tabName, e.detail.index);
371
- });
372
-
373
- // Session changes
374
- document.addEventListener('sessionFilterChanged', (e) => {
375
- this.renderCurrentTab();
376
- });
377
-
378
- // FIX: Listen for history loaded event to render initial data
379
- // WHY: When historical events are loaded from server, panes need to be rendered
380
- document.addEventListener('historyLoaded', (e) => {
381
- console.log('[Dashboard] History loaded event received:', e.detail);
382
- console.log('[Dashboard] Rendering current tab with historical data');
383
- this.renderCurrentTab();
384
- });
385
- }
386
-
387
- /**
388
- * Set up tab filters
389
- */
390
- setupTabFilters() {
391
- // Agents tab filters
392
- const agentsSearchInput = document.getElementById('agents-search-input');
393
- const agentsTypeFilter = document.getElementById('agents-type-filter');
394
-
395
- if (agentsSearchInput) {
396
- agentsSearchInput.addEventListener('input', () => {
397
- if (this.uiStateManager.getCurrentTab() === 'agents') this.renderCurrentTab();
398
- });
399
- }
400
-
401
- if (agentsTypeFilter) {
402
- agentsTypeFilter.addEventListener('change', () => {
403
- if (this.uiStateManager.getCurrentTab() === 'agents') this.renderCurrentTab();
404
- });
405
- }
406
-
407
- // Tools tab filters
408
- const toolsSearchInput = document.getElementById('tools-search-input');
409
- const toolsTypeFilter = document.getElementById('tools-type-filter');
410
-
411
- if (toolsSearchInput) {
412
- toolsSearchInput.addEventListener('input', () => {
413
- if (this.uiStateManager.getCurrentTab() === 'tools') this.renderCurrentTab();
414
- });
415
- }
416
-
417
- if (toolsTypeFilter) {
418
- toolsTypeFilter.addEventListener('change', () => {
419
- if (this.uiStateManager.getCurrentTab() === 'tools') this.renderCurrentTab();
420
- });
421
- }
422
-
423
- // Files tab filters
424
- const filesSearchInput = document.getElementById('files-search-input');
425
- const filesTypeFilter = document.getElementById('files-type-filter');
426
-
427
- if (filesSearchInput) {
428
- filesSearchInput.addEventListener('input', () => {
429
- if (this.uiStateManager.getCurrentTab() === 'files') this.renderCurrentTab();
430
- });
431
- }
432
-
433
- if (filesTypeFilter) {
434
- filesTypeFilter.addEventListener('change', () => {
435
- if (this.uiStateManager.getCurrentTab() === 'files') this.renderCurrentTab();
436
- });
437
- }
438
- }
439
-
440
- /**
441
- * Initialize from URL parameters
442
- */
443
- initializeFromURL() {
444
- const params = new URLSearchParams(window.location.search);
445
- this.socketManager.initializeFromURL(params);
446
- }
447
-
448
- /**
449
- * Render current tab content
450
- */
451
- renderCurrentTab() {
452
- const currentTab = this.uiStateManager.getCurrentTab();
453
-
454
- switch (currentTab) {
455
- case 'events':
456
- // Events tab is handled by EventViewer
457
- break;
458
- case 'activity':
459
- // Trigger Activity tab rendering through the component
460
- // Check if ActivityTree class is available (from built module)
461
- if (window.ActivityTree && typeof window.ActivityTree === 'function') {
462
- // Reset retry count on successful load
463
- this.activityTreeRetryCount = 0;
464
-
465
- // Create or get instance
466
- if (!window.activityTreeInstance) {
467
- window.activityTreeInstance = new window.ActivityTree();
468
- }
469
-
470
- // Initialize if needed and render
471
- if (window.activityTreeInstance) {
472
- if (!window.activityTreeInstance.initialized) {
473
- window.activityTreeInstance.initialize();
474
- }
475
-
476
- if (typeof window.activityTreeInstance.renderWhenVisible === 'function') {
477
- window.activityTreeInstance.renderWhenVisible();
478
- }
479
-
480
- // Force show to ensure the tree is visible
481
- if (typeof window.activityTreeInstance.forceShow === 'function') {
482
- window.activityTreeInstance.forceShow();
483
- }
484
- }
485
- } else if (window.activityTree && typeof window.activityTree === 'function') {
486
- // Fallback to legacy approach if available
487
- const activityTreeInstance = window.activityTree();
488
- if (activityTreeInstance) {
489
- if (typeof activityTreeInstance.renderWhenVisible === 'function') {
490
- activityTreeInstance.renderWhenVisible();
491
- }
492
- if (typeof activityTreeInstance.forceShow === 'function') {
493
- activityTreeInstance.forceShow();
494
- }
495
- }
496
- } else {
497
- // Module not loaded yet, retry after a delay (with retry limit)
498
- if (this.activityTreeRetryCount < this.maxRetryAttempts) {
499
- this.activityTreeRetryCount++;
500
- console.warn(`Activity tree component not available, retrying in 100ms... (attempt ${this.activityTreeRetryCount}/${this.maxRetryAttempts})`);
501
- setTimeout(() => {
502
- if (this.uiStateManager.getCurrentTab() === 'activity') {
503
- this.renderCurrentTab();
504
- }
505
- }, 100);
506
- } else {
507
- console.error('Maximum retry attempts reached for ActivityTree initialization. Giving up.');
508
- const activityContainer = document.getElementById('activity-tree-container') || document.getElementById('activity-tree');
509
- if (activityContainer) {
510
- activityContainer.innerHTML = '<div class="error-message">⚠️ Activity Tree failed to load. Please refresh the page.</div>';
511
- }
512
- }
513
- }
514
- break;
515
- case 'agents':
516
- this.renderAgents();
517
- break;
518
- case 'tools':
519
- this.renderTools();
520
- break;
521
- case 'files':
522
- this.renderFiles();
523
- break;
524
- }
525
-
526
- // Update selection UI if we have a selected card
527
- const selectedCard = this.uiStateManager.getSelectedCard();
528
- if (selectedCard.tab === currentTab) {
529
- this.uiStateManager.updateCardSelectionUI();
530
- }
531
-
532
- // Update unified selection UI to maintain consistency
533
- this.uiStateManager.updateUnifiedSelectionUI();
534
- }
535
-
536
- /**
537
- * Render agents tab with flat chronological view
538
- */
539
- renderAgents() {
540
- const agentsList = document.getElementById('agents-list');
541
- if (!agentsList) return;
542
-
543
- // Get filter values
544
- const searchText = document.getElementById('agents-search-input')?.value || '';
545
- const agentType = document.getElementById('agents-type-filter')?.value || '';
546
-
547
- // Generate flat HTML
548
- const flatHTML = this.renderAgentsFlat(searchText, agentType);
549
- agentsList.innerHTML = flatHTML;
550
-
551
- // Remove hierarchy controls if they exist
552
- this.removeHierarchyControls();
553
-
554
- // Update filter dropdowns with available agent types
555
- const uniqueInstances = this.agentInference.getUniqueAgentInstances();
556
- this.updateAgentsFilterDropdowns(uniqueInstances);
557
- }
558
-
559
- /**
560
- * Remove hierarchy control buttons (flat view doesn't need them)
561
- */
562
- removeHierarchyControls() {
563
- const existingControls = document.getElementById('hierarchy-controls');
564
- if (existingControls) {
565
- existingControls.remove();
566
- }
567
- }
568
-
569
- /**
570
- * Render agents as a flat chronological list
571
- * @param {string} searchText - Search filter
572
- * @param {string} agentType - Agent type filter
573
- * @returns {string} HTML for flat agent list
574
- */
575
- renderAgentsFlat(searchText, agentType) {
576
- const events = this.eventViewer.events;
577
- if (!events || events.length === 0) {
578
- return '<div class="no-events">No agent events found...</div>';
579
- }
580
-
581
- // Process agent inference to get agent mappings
582
- this.agentInference.processAgentInference();
583
- const eventAgentMap = this.agentInference.getEventAgentMap();
584
-
585
- // Collect all agent events with metadata
586
- const agentEvents = [];
587
- events.forEach((event, index) => {
588
- const inference = eventAgentMap.get(index);
589
- if (inference && (inference.type === 'subagent' || inference.type === 'main_agent')) {
590
- // Apply filters
591
- let includeEvent = true;
592
-
593
- if (searchText) {
594
- const searchLower = searchText.toLowerCase();
595
- includeEvent = includeEvent && (
596
- inference.agentName.toLowerCase().includes(searchLower) ||
597
- (event.tool_name && event.tool_name.toLowerCase().includes(searchLower)) ||
598
- (event.data && JSON.stringify(event.data).toLowerCase().includes(searchLower))
599
- );
600
- }
601
-
602
- if (agentType) {
603
- includeEvent = includeEvent && inference.agentName.includes(agentType);
604
- }
605
-
606
- if (includeEvent) {
607
- agentEvents.push({
608
- event,
609
- inference,
610
- index,
611
- timestamp: new Date(event.timestamp)
612
- });
613
- }
614
- }
615
- });
616
-
617
- if (agentEvents.length === 0) {
618
- return '<div class="no-events">No agent events match the current filters...</div>';
619
- }
620
-
621
- // Generate HTML for each event
622
- const html = agentEvents.map((item, listIndex) => {
623
- const { event, inference, index, timestamp } = item;
624
-
625
- // Determine action/tool
626
- let action = 'Activity';
627
- let actionIcon = '📋';
628
- let details = '';
629
-
630
- if (event.event_type === 'SubagentStart') {
631
- action = 'Started';
632
- actionIcon = '🟢';
633
- details = 'Agent session began';
634
- } else if (event.event_type === 'SubagentStop') {
635
- action = 'Stopped';
636
- actionIcon = '🔴';
637
- details = 'Agent session ended';
638
- } else if (event.tool_name) {
639
- action = `Tool: ${event.tool_name}`;
640
- actionIcon = this.getToolIcon(event.tool_name);
641
-
642
- // Add tool parameters as details
643
- if (event.data && event.data.tool_parameters) {
644
- const params = event.data.tool_parameters;
645
- if (params.file_path) {
646
- details = params.file_path;
647
- } else if (params.command) {
648
- details = params.command.substring(0, 50) + (params.command.length > 50 ? '...' : '');
649
- } else if (params.pattern) {
650
- details = `pattern="${params.pattern}"`;
651
- } else if (params.query) {
652
- details = `query="${params.query}"`;
653
- }
654
- }
655
- }
656
-
657
- // Status based on event type
658
- let status = 'completed';
659
- if (event.event_type === 'SubagentStart') {
660
- status = 'active';
661
- } else if (event.data && event.data.error) {
662
- status = 'error';
663
- }
664
-
665
- return `
666
- <div class="agent-event-item" data-index="${listIndex}" onclick="window.dashboard.showCardDetails('agents', ${index})">
667
- <div class="agent-event-header">
668
- <div class="agent-event-time">${this.formatTimestamp(timestamp)}</div>
669
- <div class="agent-event-agent">
670
- ${this.getAgentIcon(inference.agentName)} ${inference.agentName}
671
- </div>
672
- <div class="agent-event-action">
673
- ${actionIcon} ${action}
674
- </div>
675
- <div class="agent-event-status status-${status}">
676
- ${this.getStatusIcon(status)}
677
- </div>
678
- </div>
679
- ${details ? `<div class="agent-event-details">${this.escapeHtml(details)}</div>` : ''}
680
- </div>
681
- `;
682
- }).join('');
683
-
684
- return `<div class="agent-events-flat">${html}</div>`;
685
- }
686
-
687
- /**
688
- * Get icon for agent type
689
- */
690
- getAgentIcon(agentName) {
691
- const agentIcons = {
692
- 'PM': '🎯',
693
- 'Engineer Agent': '🔧',
694
- 'Research Agent': '🔍',
695
- 'QA Agent': '✅',
696
- 'Documentation Agent': '📝',
697
- 'Security Agent': '🔒',
698
- 'Ops Agent': '⚙️',
699
- 'Version Control Agent': '📦',
700
- 'Data Engineer Agent': '💾',
701
- 'Test Integration Agent': '🧪'
702
- };
703
- return agentIcons[agentName] || '🤖';
704
- }
705
-
706
- /**
707
- * Get icon for tool
708
- */
709
- getToolIcon(toolName) {
710
- const toolIcons = {
711
- 'Read': '📖',
712
- 'Write': '✏️',
713
- 'Edit': '📝',
714
- 'Bash': '💻',
715
- 'Grep': '🔍',
716
- 'Glob': '📂',
717
- 'LS': '📁',
718
- 'Task': '📋'
719
- };
720
- return toolIcons[toolName] || '🔧';
721
- }
722
-
723
- /**
724
- * Get icon for status
725
- */
726
- getStatusIcon(status) {
727
- const statusIcons = {
728
- 'active': '🟢',
729
- 'completed': '✅',
730
- 'error': '❌',
731
- 'pending': '🟡'
732
- };
733
- return statusIcons[status] || '❓';
734
- }
735
-
736
- /**
737
- * Format timestamp for display
738
- */
739
- formatTimestamp(timestamp) {
740
- return timestamp.toLocaleTimeString('en-US', {
741
- hour: '2-digit',
742
- minute: '2-digit',
743
- second: '2-digit',
744
- hour12: false
745
- });
746
- }
747
-
748
- /**
749
- * Escape HTML for safe display
750
- */
751
- escapeHtml(text) {
752
- if (!text) return '';
753
- const div = document.createElement('div');
754
- div.textContent = text;
755
- return div.innerHTML;
756
- }
757
-
758
- /**
759
- * Render tools tab with unique instance view (one row per unique tool call)
760
- */
761
- renderTools() {
762
- const toolsList = document.getElementById('tools-list');
763
- if (!toolsList) return;
764
-
765
- const toolCalls = this.fileToolTracker.getToolCalls();
766
- const toolCallsArray = Array.from(toolCalls.entries());
767
- const uniqueToolInstances = this.eventProcessor.getUniqueToolInstances(toolCallsArray);
768
- const toolHTML = this.eventProcessor.generateToolHTML(uniqueToolInstances);
769
-
770
- toolsList.innerHTML = toolHTML;
771
- this.exportManager.scrollListToBottom('tools-list');
772
-
773
- // Update filter dropdowns
774
- this.updateToolsFilterDropdowns(uniqueToolInstances);
775
- }
776
-
777
- /**
778
- * Render files tab with unique instance view (one row per unique file)
779
- */
780
- renderFiles() {
781
- const filesList = document.getElementById('files-list');
782
- if (!filesList) return;
783
-
784
- const fileOperations = this.fileToolTracker.getFileOperations();
785
- const filesArray = Array.from(fileOperations.entries());
786
-
787
- console.log('[renderFiles] File operations map size:', fileOperations.size);
788
- console.log('[renderFiles] Files array:', filesArray);
789
-
790
- const uniqueFileInstances = this.eventProcessor.getUniqueFileInstances(filesArray);
791
- const fileHTML = this.eventProcessor.generateFileHTML(uniqueFileInstances);
792
-
793
- if (filesArray.length === 0) {
794
- filesList.innerHTML = '<div class="empty-state">No file operations tracked yet. File operations will appear here when tools like Read, Write, Edit, or Grep are used.</div>';
795
- } else {
796
- filesList.innerHTML = fileHTML;
797
- }
798
-
799
- this.exportManager.scrollListToBottom('files-list');
800
-
801
- // Update filter dropdowns
802
- this.updateFilesFilterDropdowns(filesArray);
803
- }
804
-
805
- /**
806
- * Update agents filter dropdowns for unique instances
807
- */
808
- updateAgentsFilterDropdowns(uniqueInstances) {
809
- const agentTypes = new Set();
810
-
811
- // uniqueInstances is already an array of unique agent instances
812
- uniqueInstances.forEach(instance => {
813
- if (instance.agentName && instance.agentName !== 'Unknown') {
814
- agentTypes.add(instance.agentName);
815
- }
816
- });
817
-
818
- const sortedTypes = Array.from(agentTypes).filter(type => type && type.trim() !== '');
819
- this.populateFilterDropdown('agents-type-filter', sortedTypes, 'All Agent Types');
820
-
821
- // Agent filter types populated
822
- }
823
-
824
- /**
825
- * Update tools filter dropdowns
826
- */
827
- updateToolsFilterDropdowns(toolCallsArray) {
828
- const toolNames = [...new Set(toolCallsArray.map(([key, toolCall]) => toolCall.tool_name))]
829
- .filter(name => name);
830
-
831
- this.populateFilterDropdown('tools-type-filter', toolNames, 'All Tools');
832
- }
833
-
834
- /**
835
- * Update files filter dropdowns
836
- */
837
- updateFilesFilterDropdowns(filesArray) {
838
- const operations = [...new Set(filesArray.flatMap(([path, data]) =>
839
- data.operations.map(op => op.operation)
840
- ))].filter(op => op);
841
-
842
- this.populateFilterDropdown('files-type-filter', operations, 'All Operations');
843
- }
844
-
845
- /**
846
- * Populate filter dropdown with values
847
- */
848
- populateFilterDropdown(selectId, values, allOption = 'All') {
849
- const select = document.getElementById(selectId);
850
- if (!select) return;
851
-
852
- const currentValue = select.value;
853
- const sortedValues = values.sort((a, b) => a.localeCompare(b));
854
-
855
- // Clear existing options except the first "All" option
856
- select.innerHTML = `<option value="">${allOption}</option>`;
857
-
858
- // Add sorted values
859
- sortedValues.forEach(value => {
860
- const option = document.createElement('option');
861
- option.value = value;
862
- option.textContent = value;
863
- select.appendChild(option);
864
- });
865
-
866
- // Restore previous selection if it still exists
867
- if (currentValue && sortedValues.includes(currentValue)) {
868
- select.value = currentValue;
869
- }
870
- }
871
-
872
- /**
873
- * Show card details for specified tab and index
874
- */
875
- showCardDetails(tabName, index) {
876
- switch (tabName) {
877
- case 'events':
878
- if (this.eventViewer) {
879
- this.eventViewer.showEventDetails(index);
880
- }
881
- break;
882
- case 'agents':
883
- this.showAgentDetailsByIndex(index);
884
- break;
885
- case 'tools':
886
- this.showToolDetailsByIndex(index);
887
- break;
888
- case 'files':
889
- this.showFileDetailsByIndex(index);
890
- break;
891
- }
892
- }
893
-
894
- /**
895
- * Show agent details by index
896
- */
897
- showAgentDetailsByIndex(index) {
898
- const events = this.eventProcessor.getFilteredEventsForTab('agents');
899
-
900
- // Defensive checks
901
- if (!events || !Array.isArray(events) || index < 0 || index >= events.length) {
902
- console.warn('Dashboard: Invalid agent index or events array');
903
- return;
904
- }
905
-
906
- const filteredSingleEvent = this.eventProcessor.applyAgentsFilters([events[index]]);
907
-
908
- if (filteredSingleEvent.length > 0 && this.moduleViewer &&
909
- typeof this.moduleViewer.showAgentEvent === 'function') {
910
- const event = filteredSingleEvent[0];
911
- this.moduleViewer.showAgentEvent(event, index);
912
- }
913
- }
914
-
915
- /**
916
- * Show agent instance details for unique instance view
917
- * @param {string} instanceId - Agent instance ID
918
- */
919
- showAgentInstanceDetails(instanceId) {
920
- const pmDelegations = this.agentInference.getPMDelegations();
921
- const instance = pmDelegations.get(instanceId);
922
-
923
- if (!instance) {
924
- // Check if it's an implied delegation
925
- const uniqueInstances = this.agentInference.getUniqueAgentInstances();
926
- const impliedInstance = uniqueInstances.find(inst => inst.id === instanceId);
927
-
928
- if (!impliedInstance) {
929
- console.error('Agent instance not found:', instanceId);
930
- return;
931
- }
932
-
933
- // For implied instances, show basic info
934
- this.showImpliedAgentDetails(impliedInstance);
935
- return;
936
- }
937
-
938
- // Show full PM delegation details
939
- if (this.moduleViewer && typeof this.moduleViewer.showAgentInstance === 'function') {
940
- this.moduleViewer.showAgentInstance(instance);
941
- } else {
942
- // Fallback: show in console or basic modal
943
- console.log('Agent Instance Details:', {
944
- id: instanceId,
945
- agentName: instance.agentName,
946
- type: 'PM Delegation',
947
- eventCount: instance.agentEvents.length,
948
- startTime: instance.timestamp,
949
- pmCall: instance.pmCall
950
- });
951
- }
952
- }
953
-
954
- /**
955
- * Show implied agent details (agents without explicit PM delegation)
956
- * @param {Object} impliedInstance - Implied agent instance
957
- */
958
- showImpliedAgentDetails(impliedInstance) {
959
- if (this.moduleViewer && typeof this.moduleViewer.showImpliedAgent === 'function') {
960
- this.moduleViewer.showImpliedAgent(impliedInstance);
961
- } else {
962
- // Fallback: show in console or basic modal
963
- console.log('Implied Agent Details:', {
964
- id: impliedInstance.id,
965
- agentName: impliedInstance.agentName,
966
- type: 'Implied PM Delegation',
967
- eventCount: impliedInstance.eventCount,
968
- startTime: impliedInstance.timestamp,
969
- note: 'No explicit PM call found - inferred from agent activity'
970
- });
971
- }
972
- }
973
-
974
- /**
975
- * Show tool details by index
976
- */
977
- showToolDetailsByIndex(index) {
978
- const toolCalls = this.fileToolTracker.getToolCalls();
979
- const toolCallsArray = Array.from(toolCalls.entries());
980
- const filteredToolCalls = this.eventProcessor.applyToolCallFilters(toolCallsArray);
981
-
982
- if (index >= 0 && index < filteredToolCalls.length) {
983
- const [toolCallKey] = filteredToolCalls[index];
984
- this.showToolCallDetails(toolCallKey);
985
- }
986
- }
987
-
988
- /**
989
- * Show file details by index
990
- */
991
- showFileDetailsByIndex(index) {
992
- const fileOperations = this.fileToolTracker.getFileOperations();
993
- let filesArray = Array.from(fileOperations.entries());
994
- filesArray = this.eventProcessor.applyFilesFilters(filesArray);
995
-
996
- if (index >= 0 && index < filesArray.length) {
997
- const [filePath] = filesArray[index];
998
- this.showFileDetails(filePath);
999
- }
1000
- }
1001
-
1002
- /**
1003
- * Show tool call details
1004
- */
1005
- showToolCallDetails(toolCallKey) {
1006
- const toolCall = this.fileToolTracker.getToolCall(toolCallKey);
1007
- if (toolCall && this.moduleViewer) {
1008
- this.moduleViewer.showToolCall(toolCall, toolCallKey);
1009
- }
1010
- }
1011
-
1012
- /**
1013
- * Show file details
1014
- */
1015
- showFileDetails(filePath) {
1016
- const fileData = this.fileToolTracker.getFileOperationsForFile(filePath);
1017
- if (fileData && this.moduleViewer) {
1018
- this.moduleViewer.showFileOperations(fileData, filePath);
1019
- }
1020
- }
1021
-
1022
- // ====================================
1023
- // BACKWARD COMPATIBILITY METHODS
1024
- // ====================================
1025
-
1026
- /**
1027
- * Switch tab (backward compatibility)
1028
- */
1029
- switchTab(tabName) {
1030
- this.uiStateManager.switchTab(tabName);
1031
- }
1032
-
1033
- /**
1034
- * Select card (backward compatibility)
1035
- */
1036
- selectCard(tabName, index, type, data) {
1037
- this.uiStateManager.selectCard(tabName, index, type, data);
1038
- }
1039
-
1040
- /**
1041
- * Clear events (backward compatibility)
1042
- */
1043
- clearEvents() {
1044
- this.exportManager.clearEvents();
1045
- }
1046
-
1047
- /**
1048
- * Export events (backward compatibility)
1049
- */
1050
- exportEvents() {
1051
- this.exportManager.exportEvents();
1052
- }
1053
-
1054
- /**
1055
- * Clear selection (backward compatibility)
1056
- */
1057
- clearSelection() {
1058
- this.uiStateManager.clearSelection();
1059
- if (this.eventViewer) {
1060
- this.eventViewer.clearSelection();
1061
- }
1062
- if (this.moduleViewer) {
1063
- this.moduleViewer.clear();
1064
- }
1065
- }
1066
-
1067
-
1068
- /**
1069
- * Get current working directory (backward compatibility)
1070
- */
1071
- get currentWorkingDir() {
1072
- return this.workingDirectoryManager.getCurrentWorkingDir();
1073
- }
1074
-
1075
- /**
1076
- * Set current working directory (backward compatibility)
1077
- */
1078
- set currentWorkingDir(dir) {
1079
- this.workingDirectoryManager.setWorkingDirectory(dir);
1080
- }
1081
-
1082
- /**
1083
- * Get current tab (backward compatibility)
1084
- */
1085
- get currentTab() {
1086
- return this.uiStateManager.getCurrentTab();
1087
- }
1088
-
1089
- /**
1090
- * Get selected card (backward compatibility)
1091
- */
1092
- get selectedCard() {
1093
- return this.uiStateManager.getSelectedCard();
1094
- }
1095
-
1096
- /**
1097
- * Get file operations (backward compatibility)
1098
- */
1099
- get fileOperations() {
1100
- return this.fileToolTracker.getFileOperations();
1101
- }
1102
-
1103
- /**
1104
- * Get tool calls (backward compatibility)
1105
- */
1106
- get toolCalls() {
1107
- return this.fileToolTracker.getToolCalls();
1108
- }
1109
-
1110
-
1111
- /**
1112
- * Get tab navigation state (backward compatibility)
1113
- */
1114
- get tabNavigation() {
1115
- return this.uiStateManager ? this.uiStateManager.tabNavigation : null;
1116
- }
1117
- }
1118
-
1119
- // Global functions for backward compatibility
1120
- window.clearEvents = function() {
1121
- if (window.dashboard) {
1122
- window.dashboard.clearEvents();
1123
- }
1124
- };
1125
-
1126
- window.exportEvents = function() {
1127
- if (window.dashboard) {
1128
- window.dashboard.exportEvents();
1129
- }
1130
- };
1131
-
1132
- window.clearSelection = function() {
1133
- if (window.dashboard) {
1134
- window.dashboard.clearSelection();
1135
- }
1136
- };
1137
-
1138
- window.switchTab = function(tabName) {
1139
- if (window.dashboard) {
1140
- window.dashboard.switchTab(tabName);
1141
- }
1142
- };
1143
-
1144
- // File Viewer Modal Functions - Removed broken duplicate (using the one at line 1505)
1145
-
1146
- window.copyFileContent = function() {
1147
- const modal = document.getElementById('file-viewer-modal');
1148
- if (!modal) return;
1149
-
1150
- const codeElement = modal.querySelector('.file-content-code');
1151
- if (!codeElement) return;
1152
-
1153
- const text = codeElement.textContent;
1154
-
1155
- if (navigator.clipboard && navigator.clipboard.writeText) {
1156
- navigator.clipboard.writeText(text).then(() => {
1157
- // Show brief feedback
1158
- const button = modal.querySelector('.file-content-copy');
1159
- if (button) {
1160
- const originalText = button.textContent;
1161
- button.textContent = '✅ Copied!';
1162
- setTimeout(() => {
1163
- button.textContent = originalText;
1164
- }, 2000);
1165
- }
1166
- }).catch(err => {
1167
- console.error('Failed to copy text:', err);
1168
- });
1169
- } else {
1170
- // Fallback for older browsers
1171
- const textarea = document.createElement('textarea');
1172
- textarea.value = text;
1173
- document.body.appendChild(textarea);
1174
- textarea.select();
1175
- document.execCommand('copy');
1176
- document.body.removeChild(textarea);
1177
-
1178
- const button = modal.querySelector('.file-content-copy');
1179
- if (button) {
1180
- const originalText = button.textContent;
1181
- button.textContent = '✅ Copied!';
1182
- setTimeout(() => {
1183
- button.textContent = originalText;
1184
- }, 2000);
1185
- }
1186
- }
1187
- };
1188
-
1189
- function createFileViewerModal() {
1190
- const modal = document.createElement('div');
1191
- modal.id = 'file-viewer-modal';
1192
- modal.className = 'modal file-viewer-modal';
1193
-
1194
- modal.innerHTML = `
1195
- <div class="modal-content file-viewer-content">
1196
- <div class="file-viewer-header">
1197
- <h2 class="file-viewer-title">
1198
- <span class="file-viewer-icon">📄</span>
1199
- <span class="file-viewer-title-text">File Viewer</span>
1200
- </h2>
1201
- <div class="file-viewer-meta">
1202
- <span class="file-viewer-file-path"></span>
1203
- <span class="file-viewer-file-size"></span>
1204
- </div>
1205
- <button class="file-viewer-close" onclick="hideFileViewerModal()">
1206
- <span>&times;</span>
1207
- </button>
1208
- </div>
1209
- <div class="file-viewer-body">
1210
- <div class="file-viewer-loading">
1211
- <div class="loading-spinner"></div>
1212
- <span>Loading file content...</span>
1213
- </div>
1214
- <div class="file-viewer-error" style="display: none;">
1215
- <div class="error-icon">⚠️</div>
1216
- <div class="error-message"></div>
1217
- <div class="error-suggestions"></div>
1218
- </div>
1219
- <div class="file-viewer-content-area" style="display: none;">
1220
- <div class="file-viewer-toolbar">
1221
- <div class="file-viewer-info">
1222
- <span class="file-extension"></span>
1223
- <span class="file-encoding"></span>
1224
- </div>
1225
- <div class="file-viewer-actions">
1226
- <button class="file-content-copy" onclick="copyFileContent()">
1227
- 📋 Copy
1228
- </button>
1229
- </div>
1230
- </div>
1231
- <div class="file-viewer-scroll-wrapper">
1232
- <pre class="file-content-display"><code class="file-content-code"></code></pre>
1233
- </div>
1234
- </div>
1235
- </div>
1236
- </div>
1237
- `;
1238
-
1239
- // Close modal when clicking outside
1240
- modal.addEventListener('click', (e) => {
1241
- if (e.target === modal) {
1242
- hideFileViewerModal();
1243
- }
1244
- });
1245
-
1246
- // Close modal with Escape key
1247
- document.addEventListener('keydown', (e) => {
1248
- if (e.key === 'Escape' && modal.style.display === 'flex') {
1249
- hideFileViewerModal();
1250
- }
1251
- });
1252
-
1253
- return modal;
1254
- }
1255
-
1256
- async function updateFileViewerModal(modal, filePath, workingDir) {
1257
- // Update header info
1258
- const filePathElement = modal.querySelector('.file-viewer-file-path');
1259
- const fileSizeElement = modal.querySelector('.file-viewer-file-size');
1260
-
1261
- if (filePathElement) {
1262
- filePathElement.textContent = filePath;
1263
- }
1264
- if (fileSizeElement) {
1265
- fileSizeElement.textContent = '';
1266
- }
1267
-
1268
- // Show loading state
1269
- const loadingElement = modal.querySelector('.file-viewer-loading');
1270
- const errorElement = modal.querySelector('.file-viewer-error');
1271
- const contentArea = modal.querySelector('.file-viewer-content-area');
1272
-
1273
- if (loadingElement) {
1274
- loadingElement.style.display = 'flex';
1275
- }
1276
- if (errorElement) {
1277
- errorElement.style.display = 'none';
1278
- }
1279
- if (contentArea) {
1280
- contentArea.style.display = 'none';
1281
- }
1282
-
1283
- try {
1284
- // Get the Socket.IO client
1285
- const socket = window.socket || window.dashboard?.socketClient?.socket || window.socketClient?.socket;
1286
-
1287
- console.log('[FileViewer] Socket search results:', {
1288
- 'window.socket': !!window.socket,
1289
- 'window.socket.connected': window.socket?.connected,
1290
- 'dashboard.socketClient.socket': !!window.dashboard?.socketClient?.socket,
1291
- 'dashboard.socketClient.socket.connected': window.dashboard?.socketClient?.socket?.connected,
1292
- 'window.socketClient.socket': !!window.socketClient?.socket,
1293
- 'window.socketClient.socket.connected': window.socketClient?.socket?.connected
1294
- });
1295
-
1296
- if (!socket) {
1297
- throw new Error('No socket connection available. Please ensure the dashboard is connected.');
1298
- }
1299
-
1300
- if (!socket.connected) {
1301
- console.warn('[FileViewer] Socket found but not connected, attempting to use anyway...');
1302
- }
1303
-
1304
- console.log('[FileViewer] Socket found, setting up listener for file_content_response');
1305
-
1306
- // Set up one-time listener for file content response
1307
- const responsePromise = new Promise((resolve, reject) => {
1308
- const responseHandler = (data) => {
1309
- console.log('[FileViewer] Received file_content_response:', data);
1310
- if (data.file_path === filePath) {
1311
- socket.off('file_content_response', responseHandler);
1312
- if (data.success) {
1313
- console.log('[FileViewer] File content loaded successfully');
1314
- resolve(data);
1315
- } else {
1316
- console.error('[FileViewer] File read failed:', data.error);
1317
- reject(new Error(data.error || 'Failed to read file'));
1318
- }
1319
- }
1320
- };
1321
-
1322
- socket.on('file_content_response', responseHandler);
1323
- console.log('[FileViewer] Listener registered for file_content_response');
1324
-
1325
- // Timeout after 10 seconds
1326
- setTimeout(() => {
1327
- socket.off('file_content_response', responseHandler);
1328
- console.error('[FileViewer] Request timeout after 10 seconds');
1329
- reject(new Error('Request timeout - server did not respond'));
1330
- }, 10000);
1331
- });
1332
-
1333
- // Send file read request
1334
- const requestData = {
1335
- file_path: filePath,
1336
- working_dir: workingDir
1337
- };
1338
- console.log('[FileViewer] Emitting read_file event with data:', requestData);
1339
- socket.emit('read_file', requestData);
1340
-
1341
- // File viewer request sent
1342
-
1343
- // Wait for response
1344
- const result = await responsePromise;
1345
- // File content received successfully
1346
-
1347
- // Hide loading
1348
- const loadingEl = modal.querySelector('.file-viewer-loading');
1349
- if (loadingEl) {
1350
- loadingEl.style.display = 'none';
1351
- }
1352
-
1353
- // Show successful content
1354
- displayFileContent(modal, result);
1355
-
1356
- } catch (error) {
1357
- console.error('❌ Failed to fetch file content:', error);
1358
-
1359
- const loadingEl2 = modal.querySelector('.file-viewer-loading');
1360
- if (loadingEl2) {
1361
- loadingEl2.style.display = 'none';
1362
- }
1363
-
1364
- // Create detailed error message
1365
- let errorMessage = error.message || 'Unknown error occurred';
1366
- let suggestions = [];
1367
-
1368
- if (error.message.includes('No socket connection')) {
1369
- errorMessage = 'Failed to connect to the monitoring server';
1370
- suggestions = [
1371
- 'Check if the monitoring server is running',
1372
- 'Verify the socket connection in the dashboard',
1373
- 'Try refreshing the page and reconnecting'
1374
- ];
1375
- } else if (error.message.includes('timeout')) {
1376
- errorMessage = 'Request timed out';
1377
- suggestions = [
1378
- 'The file may be too large to load quickly',
1379
- 'Check your network connection',
1380
- 'Try again in a few moments'
1381
- ];
1382
- } else if (error.message.includes('File does not exist')) {
1383
- errorMessage = 'File not found';
1384
- suggestions = [
1385
- 'The file may have been moved or deleted',
1386
- 'Check the file path spelling',
1387
- 'Refresh the file list to see current files'
1388
- ];
1389
- } else if (error.message.includes('Access denied')) {
1390
- errorMessage = 'Access denied';
1391
- suggestions = [
1392
- 'The file is outside the allowed directories',
1393
- 'File access is restricted for security reasons'
1394
- ];
1395
- }
1396
-
1397
- displayFileError(modal, {
1398
- error: errorMessage,
1399
- file_path: filePath,
1400
- working_dir: workingDir,
1401
- suggestions: suggestions
1402
- });
1403
- }
1404
- }
1405
-
1406
- function displayFileContent(modal, result) {
1407
- // Display file content in modal
1408
- const contentArea = modal.querySelector('.file-viewer-content-area');
1409
- const extensionElement = modal.querySelector('.file-extension');
1410
- const encodingElement = modal.querySelector('.file-encoding');
1411
- const fileSizeElement = modal.querySelector('.file-viewer-file-size');
1412
- const codeElement = modal.querySelector('.file-content-code');
1413
-
1414
- // Update metadata
1415
- if (extensionElement) extensionElement.textContent = `Type: ${result.extension || 'unknown'}`;
1416
- if (encodingElement) encodingElement.textContent = `Encoding: ${result.encoding || 'unknown'}`;
1417
- if (fileSizeElement) fileSizeElement.textContent = `Size: ${formatFileSize(result.file_size)}`;
1418
-
1419
- // Update content with basic syntax highlighting
1420
- if (codeElement && result.content) {
1421
- // Setting file content
1422
- codeElement.innerHTML = highlightCode(result.content, result.extension);
1423
-
1424
- // Force scrolling to work by setting explicit heights
1425
- const wrapper = modal.querySelector('.file-viewer-scroll-wrapper');
1426
- if (wrapper) {
1427
- // Give it a moment for content to render
1428
- setTimeout(() => {
1429
- const modalContent = modal.querySelector('.modal-content');
1430
- const header = modal.querySelector('.file-viewer-header');
1431
- const toolbar = modal.querySelector('.file-viewer-toolbar');
1432
-
1433
- const modalHeight = modalContent?.offsetHeight || 0;
1434
- const headerHeight = header?.offsetHeight || 0;
1435
- const toolbarHeight = toolbar?.offsetHeight || 0;
1436
-
1437
- const availableHeight = modalHeight - headerHeight - toolbarHeight - 40; // 40px for padding
1438
-
1439
- // Setting file viewer scroll height
1440
-
1441
- wrapper.style.maxHeight = `${availableHeight}px`;
1442
- wrapper.style.overflowY = 'auto';
1443
- }, 50);
1444
- }
1445
- } else {
1446
- console.warn('⚠️ Missing codeElement or file content');
1447
- }
1448
-
1449
- // Show content area
1450
- if (contentArea) {
1451
- contentArea.style.display = 'block';
1452
- // File content area displayed
1453
- }
1454
- }
1455
-
1456
- function displayFileError(modal, result) {
1457
- const errorArea = modal.querySelector('.file-viewer-error');
1458
- const messageElement = modal.querySelector('.error-message');
1459
- const suggestionsElement = modal.querySelector('.error-suggestions');
1460
-
1461
- let errorMessage = result.error || 'Unknown error occurred';
1462
-
1463
- if (messageElement) {
1464
- messageElement.innerHTML = `
1465
- <div class="error-main">${errorMessage}</div>
1466
- ${result.file_path ? `<div class="error-file">File: ${result.file_path}</div>` : ''}
1467
- ${result.working_dir ? `<div class="error-dir">Working directory: ${result.working_dir}</div>` : ''}
1468
- `;
1469
- }
1470
-
1471
- if (suggestionsElement) {
1472
- if (result.suggestions && result.suggestions.length > 0) {
1473
- suggestionsElement.innerHTML = `
1474
- <h4>Suggestions:</h4>
1475
- <ul>
1476
- ${result.suggestions.map(s => `<li>${s}</li>`).join('')}
1477
- </ul>
1478
- `;
1479
- } else {
1480
- suggestionsElement.innerHTML = '';
1481
- }
1482
- }
1483
-
1484
- console.log('📋 Displaying file viewer error:', {
1485
- originalError: result.error,
1486
- processedMessage: errorMessage,
1487
- suggestions: result.suggestions
1488
- });
1489
-
1490
- if (errorArea) {
1491
- errorArea.style.display = 'block';
1492
- }
1493
- }
1494
-
1495
- function highlightCode(code, extension) {
1496
- /**
1497
- * Apply basic syntax highlighting to code content
1498
- * WHY: Provides basic highlighting for common file types to improve readability.
1499
- * This is a simple implementation that can be enhanced with full syntax highlighting
1500
- * libraries like highlight.js or Prism.js if needed.
1501
- */
1502
-
1503
- // Escape HTML entities first
1504
- const escaped = code
1505
- .replace(/&/g, '&amp;')
1506
- .replace(/</g, '&lt;')
1507
- .replace(/>/g, '&gt;');
1508
-
1509
- // Basic highlighting based on file extension
1510
- switch (extension) {
1511
- case '.js':
1512
- case '.jsx':
1513
- case '.ts':
1514
- case '.tsx':
1515
- return highlightJavaScript(escaped);
1516
- case '.py':
1517
- return highlightPython(escaped);
1518
- case '.json':
1519
- return highlightJSON(escaped);
1520
- case '.css':
1521
- return highlightCSS(escaped);
1522
- case '.html':
1523
- case '.htm':
1524
- return highlightHTML(escaped);
1525
- case '.md':
1526
- case '.markdown':
1527
- return highlightMarkdown(escaped);
1528
- default:
1529
- // Return with line numbers for plain text
1530
- return addLineNumbers(escaped);
1531
- }
1532
- }
1533
-
1534
- function highlightJavaScript(code) {
1535
- return addLineNumbers(code
1536
- .replace(/\b(function|const|let|var|if|else|for|while|return|import|export|class|extends)\b/g, '<span class="keyword">$1</span>')
1537
- .replace(/(\/\*[\s\S]*?\*\/|\/\/.*)/g, '<span class="comment">$1</span>')
1538
- .replace(/('[^']*'|"[^"]*"|`[^`]*`)/g, '<span class="string">$1</span>')
1539
- .replace(/\b(\d+)\b/g, '<span class="number">$1</span>'));
1540
- }
1541
-
1542
- function highlightPython(code) {
1543
- return addLineNumbers(code
1544
- .replace(/\b(def|class|if|elif|else|for|while|return|import|from|as|try|except|finally|with)\b/g, '<span class="keyword">$1</span>')
1545
- .replace(/(#.*)/g, '<span class="comment">$1</span>')
1546
- .replace(/('[^']*'|"[^"]*"|"""[\s\S]*?""")/g, '<span class="string">$1</span>')
1547
- .replace(/\b(\d+)\b/g, '<span class="number">$1</span>'));
1548
- }
1549
-
1550
- function highlightJSON(code) {
1551
- return addLineNumbers(code
1552
- .replace(/("[\w\s]*")\s*:/g, '<span class="property">$1</span>:')
1553
- .replace(/:\s*(".*?")/g, ': <span class="string">$1</span>')
1554
- .replace(/:\s*(\d+)/g, ': <span class="number">$1</span>')
1555
- .replace(/:\s*(true|false|null)/g, ': <span class="keyword">$1</span>'));
1556
- }
1557
-
1558
- function highlightCSS(code) {
1559
- return addLineNumbers(code
1560
- .replace(/([.#]?[\w-]+)\s*\{/g, '<span class="selector">$1</span> {')
1561
- .replace(/([\w-]+)\s*:/g, '<span class="property">$1</span>:')
1562
- .replace(/:\s*([^;]+);/g, ': <span class="value">$1</span>;')
1563
- .replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="comment">$1</span>'));
1564
- }
1565
-
1566
- function highlightHTML(code) {
1567
- return addLineNumbers(code
1568
- .replace(/(&lt;\/?[\w-]+)/g, '<span class="tag">$1</span>')
1569
- .replace(/([\w-]+)=(['"][^'"]*['"])/g, '<span class="attribute">$1</span>=<span class="string">$2</span>')
1570
- .replace(/(&lt;!--[\s\S]*?--&gt;)/g, '<span class="comment">$1</span>'));
1571
- }
1572
-
1573
- function highlightMarkdown(code) {
1574
- return addLineNumbers(code
1575
- .replace(/^(#{1,6})\s+(.*)$/gm, '<span class="header">$1</span> <span class="header-text">$2</span>')
1576
- .replace(/\*\*(.*?)\*\*/g, '<span class="bold">**$1**</span>')
1577
- .replace(/\*(.*?)\*/g, '<span class="italic">*$1*</span>')
1578
- .replace(/`([^`]+)`/g, '<span class="code">`$1`</span>')
1579
- .replace(/^\s*[-*+]\s+(.*)$/gm, '<span class="list-marker">•</span> $1'));
1580
- }
1581
-
1582
- function addLineNumbers(code) {
1583
- const lines = code.split('\n');
1584
- return lines.map((line, index) =>
1585
- `<span class="line-number">${String(index + 1).padStart(3, ' ')}</span> ${line || ' '}`
1586
- ).join('\n');
1587
- }
1588
-
1589
- function formatFileSize(bytes) {
1590
- if (!bytes) return '0 B';
1591
- const k = 1024;
1592
- const sizes = ['B', 'KB', 'MB', 'GB'];
1593
- const i = Math.floor(Math.log(bytes) / Math.log(k));
1594
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
1595
- }
1596
-
1597
- // File Viewer Modal Functions
1598
- window.showFileViewerModal = async function(filePath) {
1599
- console.log('[FileViewer] Opening file:', filePath);
1600
-
1601
- // Use the dashboard's current working directory
1602
- let workingDir = '';
1603
- if (window.dashboard && window.dashboard.currentWorkingDir) {
1604
- workingDir = window.dashboard.currentWorkingDir;
1605
- console.log('[FileViewer] Using working directory:', workingDir);
1606
- }
1607
-
1608
- // Create modal if it doesn't exist
1609
- let modal = document.getElementById('file-viewer-modal');
1610
- if (!modal) {
1611
- console.log('[FileViewer] Creating new modal');
1612
- modal = createFileViewerModal();
1613
- document.body.appendChild(modal);
1614
-
1615
- // Small delay to ensure DOM is fully updated
1616
- await new Promise(resolve => setTimeout(resolve, 10));
1617
- }
1618
-
1619
- // Show the modal as flex container first (ensures proper rendering)
1620
- modal.style.display = 'flex';
1621
- document.body.style.overflow = 'hidden'; // Prevent background scrolling
1622
-
1623
- // Update modal content
1624
- updateFileViewerModal(modal, filePath, workingDir).catch(error => {
1625
- console.error('Error updating file viewer modal:', error);
1626
- // Show error in the modal
1627
- displayFileContentError(modal, { error: error.message });
1628
- });
1629
- };
1630
-
1631
- window.hideFileViewerModal = function() {
1632
- const modal = document.getElementById('file-viewer-modal');
1633
- if (modal) {
1634
- modal.style.display = 'none';
1635
- document.body.style.overflow = ''; // Restore background scrolling
1636
- }
1637
- };
1638
-
1639
- window.copyFileContent = function() {
1640
- const modal = document.getElementById('file-viewer-modal');
1641
- if (!modal) return;
1642
-
1643
- const codeElement = modal.querySelector('.file-content-code');
1644
- if (!codeElement) return;
1645
-
1646
- const text = codeElement.textContent;
1647
-
1648
- if (navigator.clipboard && navigator.clipboard.writeText) {
1649
- navigator.clipboard.writeText(text).then(() => {
1650
- // Show brief feedback
1651
- const button = modal.querySelector('.file-content-copy');
1652
- if (button) {
1653
- const originalText = button.textContent;
1654
- button.textContent = '✅ Copied!';
1655
- setTimeout(() => {
1656
- button.textContent = originalText;
1657
- }, 2000);
1658
- }
1659
- }).catch(err => {
1660
- console.error('Failed to copy text:', err);
1661
- });
1662
- } else {
1663
- // Fallback for older browsers
1664
- const textarea = document.createElement('textarea');
1665
- textarea.value = text;
1666
- document.body.appendChild(textarea);
1667
- textarea.select();
1668
- document.execCommand('copy');
1669
- document.body.removeChild(textarea);
1670
-
1671
- const button = modal.querySelector('.file-content-copy');
1672
- if (button) {
1673
- const originalText = button.textContent;
1674
- button.textContent = '✅ Copied!';
1675
- setTimeout(() => {
1676
- button.textContent = originalText;
1677
- }, 2000);
1678
- }
1679
- }
1680
- };
1681
-
1682
-
1683
-
1684
-
1685
- function displayFileContentError(modal, result) {
1686
- const errorArea = modal.querySelector('.file-viewer-error');
1687
- const messageElement = modal.querySelector('.error-message');
1688
- const suggestionsElement = modal.querySelector('.error-suggestions');
1689
- const loadingElement = modal.querySelector('.file-viewer-loading');
1690
- const contentArea = modal.querySelector('.file-viewer-content-area');
1691
-
1692
- // Hide loading and content areas, show error
1693
- if (loadingElement) {
1694
- loadingElement.style.display = 'none';
1695
- }
1696
- if (contentArea) {
1697
- contentArea.style.display = 'none';
1698
- }
1699
- if (errorArea) {
1700
- errorArea.style.display = 'flex';
1701
- }
1702
-
1703
- // Create user-friendly error messages
1704
- let errorMessage = result.error || 'Unknown error occurred';
1705
-
1706
- if (errorMessage.includes('not found')) {
1707
- errorMessage = '📁 File not found or not accessible';
1708
- } else if (errorMessage.includes('permission')) {
1709
- errorMessage = '🔒 Permission denied accessing this file';
1710
- } else if (errorMessage.includes('too large')) {
1711
- errorMessage = '📏 File is too large to display';
1712
- } else if (errorMessage.includes('socket connection')) {
1713
- errorMessage = '🔌 Not connected to the server. Please check your connection.';
1714
- } else if (errorMessage.includes('timeout')) {
1715
- errorMessage = '⏱️ Request timed out. The server may be busy or unresponsive.';
1716
- } else if (!errorMessage.includes('📁') && !errorMessage.includes('🔒') && !errorMessage.includes('📏')) {
1717
- errorMessage = `⚠️ ${errorMessage}`;
1718
- }
1719
-
1720
- if (messageElement) {
1721
- messageElement.textContent = errorMessage;
1722
- }
1723
-
1724
- // Add suggestions if available
1725
- if (suggestionsElement) {
1726
- if (result.suggestions && result.suggestions.length > 0) {
1727
- suggestionsElement.innerHTML = `
1728
- <h4>Suggestions:</h4>
1729
- <ul>
1730
- ${result.suggestions.map(suggestion => `<li>${suggestion}</li>`).join('')}
1731
- </ul>
1732
- `;
1733
- } else {
1734
- suggestionsElement.innerHTML = `
1735
- <h4>Try:</h4>
1736
- <ul>
1737
- <li>Check if the file exists and is readable</li>
1738
- <li>Verify file permissions</li>
1739
- <li>Ensure the monitoring server has access to this file</li>
1740
- </ul>
1741
- `;
1742
- }
1743
- }
1744
-
1745
- console.log('📋 Displaying file content error:', {
1746
- originalError: result.error,
1747
- processedMessage: errorMessage,
1748
- suggestions: result.suggestions
1749
- });
1750
-
1751
- if (errorArea) {
1752
- errorArea.style.display = 'block';
1753
- }
1754
- }
1755
-
1756
- // Search Viewer Modal Functions
1757
- window.showSearchViewerModal = function(searchParams, searchResults) {
1758
- // Create modal if it doesn't exist
1759
- let modal = document.getElementById('search-viewer-modal');
1760
- if (!modal) {
1761
- modal = createSearchViewerModal();
1762
- document.body.appendChild(modal);
1763
- }
1764
-
1765
- // Update modal content
1766
- updateSearchViewerModal(modal, searchParams, searchResults);
1767
-
1768
- // Show the modal as flex container
1769
- modal.style.display = 'flex';
1770
- document.body.style.overflow = 'hidden'; // Prevent background scrolling
1771
- };
1772
-
1773
- window.hideSearchViewerModal = function() {
1774
- const modal = document.getElementById('search-viewer-modal');
1775
- if (modal) {
1776
- modal.style.display = 'none';
1777
- document.body.style.overflow = ''; // Restore background scrolling
1778
- }
1779
- };
1780
-
1781
- function createSearchViewerModal() {
1782
- const modal = document.createElement('div');
1783
- modal.id = 'search-viewer-modal';
1784
- modal.className = 'modal search-viewer-modal';
1785
-
1786
- modal.innerHTML = `
1787
- <div class="modal-content search-viewer-content">
1788
- <div class="search-viewer-header">
1789
- <h2 class="search-viewer-title">
1790
- <span class="search-viewer-icon">🔍</span>
1791
- <span class="search-viewer-title-text">Search Results</span>
1792
- </h2>
1793
- <button class="search-viewer-close" onclick="hideSearchViewerModal()">
1794
- <span>&times;</span>
1795
- </button>
1796
- </div>
1797
- <div class="search-viewer-body">
1798
- <div class="search-params-section">
1799
- <h3>Search Parameters</h3>
1800
- <pre class="search-params-display"></pre>
1801
- </div>
1802
- <div class="search-results-section">
1803
- <h3>Search Results</h3>
1804
- <div class="search-results-display"></div>
1805
- </div>
1806
- </div>
1807
- </div>
1808
- `;
1809
-
1810
- // Close modal when clicking outside
1811
- modal.addEventListener('click', (e) => {
1812
- if (e.target === modal) {
1813
- hideSearchViewerModal();
1814
- }
1815
- });
1816
-
1817
- // Close modal on Escape key
1818
- document.addEventListener('keydown', (e) => {
1819
- if (e.key === 'Escape' && modal.style.display === 'flex') {
1820
- hideSearchViewerModal();
1821
- }
1822
- });
1823
-
1824
- return modal;
1825
- }
1826
-
1827
- function updateSearchViewerModal(modal, searchParams, searchResults) {
1828
- const paramsDisplay = modal.querySelector('.search-params-display');
1829
- const resultsDisplay = modal.querySelector('.search-results-display');
1830
-
1831
- // Display search parameters in formatted JSON
1832
- if (paramsDisplay && searchParams) {
1833
- paramsDisplay.textContent = JSON.stringify(searchParams, null, 2);
1834
- }
1835
-
1836
- // Display search results
1837
- if (resultsDisplay && searchResults) {
1838
- let resultsHTML = '';
1839
-
1840
- if (typeof searchResults === 'string') {
1841
- // If results are a string, display as preformatted text
1842
- resultsHTML = `<pre class="search-results-text">${escapeHtml(searchResults)}</pre>`;
1843
- } else if (Array.isArray(searchResults)) {
1844
- // If results are an array, display as a list
1845
- resultsHTML = '<ul class="search-results-list">';
1846
- searchResults.forEach(result => {
1847
- if (typeof result === 'object') {
1848
- resultsHTML += `<li><pre>${JSON.stringify(result, null, 2)}</pre></li>`;
1849
- } else {
1850
- resultsHTML += `<li>${escapeHtml(String(result))}</li>`;
1851
- }
1852
- });
1853
- resultsHTML += '</ul>';
1854
- } else if (typeof searchResults === 'object') {
1855
- // If results are an object, display as formatted JSON
1856
- resultsHTML = `<pre class="search-results-json">${JSON.stringify(searchResults, null, 2)}</pre>`;
1857
- } else {
1858
- // Fallback: display as text
1859
- resultsHTML = `<div class="search-results-text">${escapeHtml(String(searchResults))}</div>`;
1860
- }
1861
-
1862
- resultsDisplay.innerHTML = resultsHTML;
1863
- }
1864
- }
1865
-
1866
- function escapeHtml(text) {
1867
- const div = document.createElement('div');
1868
- div.textContent = text;
1869
- return div.innerHTML;
1870
- }
1871
-
1872
- // Global window functions for backward compatibility
1873
- window.showAgentInstanceDetails = function(instanceId) {
1874
- if (window.dashboard && typeof window.dashboard.showAgentInstanceDetails === 'function') {
1875
- window.dashboard.showAgentInstanceDetails(instanceId);
1876
- } else {
1877
- console.error('Dashboard not available or method not found');
1878
- }
1879
- };
1880
-
1881
- // Initialize dashboard when page loads
1882
- document.addEventListener('DOMContentLoaded', function() {
1883
- try {
1884
- // Create dashboard instance
1885
- window.dashboard = new Dashboard();
1886
-
1887
- // Call post-initialization setup that requires window.dashboard
1888
- // This must happen after window.dashboard is set
1889
- if (window.dashboard && typeof window.dashboard.postInit === 'function') {
1890
- window.dashboard.postInit();
1891
- }
1892
-
1893
- console.log('Dashboard loaded and initialized successfully');
1894
-
1895
- // Dispatch custom event to signal dashboard ready
1896
- document.dispatchEvent(new CustomEvent('dashboardReady', {
1897
- detail: { dashboard: window.dashboard }
1898
- }));
1899
- } catch (error) {
1900
- console.error('Failed to initialize dashboard:', error);
1901
- // Show user-friendly error message
1902
- document.body.innerHTML = `
1903
- <div style="padding: 20px; font-family: sans-serif;">
1904
- <h1>Dashboard Initialization Error</h1>
1905
- <p>The dashboard failed to load properly. Please refresh the page or check the console for details.</p>
1906
- <pre style="background: #f5f5f5; padding: 10px; border-radius: 4px;">${error.message}</pre>
1907
- </div>
1908
- `;
1909
- }
1910
- });
1911
-
1912
- // ES6 Module export
1913
- export { Dashboard };
1914
- export default Dashboard;