claude-mpm 4.0.31__py3-none-any.whl → 4.0.34__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 (71) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +33 -25
  3. claude_mpm/agents/INSTRUCTIONS.md +14 -10
  4. claude_mpm/agents/templates/documentation.json +51 -34
  5. claude_mpm/agents/templates/research.json +0 -11
  6. claude_mpm/cli/__init__.py +63 -26
  7. claude_mpm/cli/commands/agent_manager.py +10 -8
  8. claude_mpm/core/framework_loader.py +272 -113
  9. claude_mpm/dashboard/static/css/dashboard.css +449 -0
  10. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  11. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  12. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  13. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  14. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  15. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  16. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  17. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +774 -0
  18. claude_mpm/dashboard/static/js/components/agent-inference.js +257 -3
  19. claude_mpm/dashboard/static/js/components/build-tracker.js +289 -0
  20. claude_mpm/dashboard/static/js/components/event-viewer.js +168 -39
  21. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +17 -0
  22. claude_mpm/dashboard/static/js/components/session-manager.js +23 -3
  23. claude_mpm/dashboard/static/js/components/socket-manager.js +2 -0
  24. claude_mpm/dashboard/static/js/dashboard.js +207 -31
  25. claude_mpm/dashboard/static/js/socket-client.js +85 -6
  26. claude_mpm/dashboard/templates/index.html +1 -0
  27. claude_mpm/hooks/claude_hooks/connection_pool.py +12 -2
  28. claude_mpm/hooks/claude_hooks/event_handlers.py +81 -19
  29. claude_mpm/hooks/claude_hooks/hook_handler.py +72 -10
  30. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +398 -0
  31. claude_mpm/hooks/claude_hooks/response_tracking.py +10 -0
  32. claude_mpm/services/agents/deployment/agent_deployment.py +86 -37
  33. claude_mpm/services/agents/deployment/agent_template_builder.py +18 -10
  34. claude_mpm/services/agents/deployment/agents_directory_resolver.py +10 -25
  35. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +189 -3
  36. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +3 -2
  37. claude_mpm/services/agents/deployment/strategies/system_strategy.py +10 -3
  38. claude_mpm/services/agents/deployment/strategies/user_strategy.py +10 -14
  39. claude_mpm/services/agents/deployment/system_instructions_deployer.py +8 -13
  40. claude_mpm/services/agents/memory/agent_memory_manager.py +141 -184
  41. claude_mpm/services/agents/memory/content_manager.py +182 -232
  42. claude_mpm/services/agents/memory/template_generator.py +4 -40
  43. claude_mpm/services/event_bus/__init__.py +18 -0
  44. claude_mpm/services/event_bus/event_bus.py +334 -0
  45. claude_mpm/services/event_bus/relay.py +301 -0
  46. claude_mpm/services/events/__init__.py +44 -0
  47. claude_mpm/services/events/consumers/__init__.py +18 -0
  48. claude_mpm/services/events/consumers/dead_letter.py +296 -0
  49. claude_mpm/services/events/consumers/logging.py +183 -0
  50. claude_mpm/services/events/consumers/metrics.py +242 -0
  51. claude_mpm/services/events/consumers/socketio.py +376 -0
  52. claude_mpm/services/events/core.py +470 -0
  53. claude_mpm/services/events/interfaces.py +230 -0
  54. claude_mpm/services/events/producers/__init__.py +14 -0
  55. claude_mpm/services/events/producers/hook.py +269 -0
  56. claude_mpm/services/events/producers/system.py +327 -0
  57. claude_mpm/services/mcp_gateway/core/process_pool.py +411 -0
  58. claude_mpm/services/mcp_gateway/server/stdio_server.py +13 -0
  59. claude_mpm/services/monitor_build_service.py +345 -0
  60. claude_mpm/services/socketio/event_normalizer.py +667 -0
  61. claude_mpm/services/socketio/handlers/connection.py +78 -20
  62. claude_mpm/services/socketio/handlers/hook.py +14 -5
  63. claude_mpm/services/socketio/migration_utils.py +329 -0
  64. claude_mpm/services/socketio/server/broadcaster.py +26 -33
  65. claude_mpm/services/socketio/server/core.py +4 -3
  66. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/METADATA +4 -3
  67. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/RECORD +71 -50
  68. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/WHEEL +0 -0
  69. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/entry_points.txt +0 -0
  70. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/licenses/LICENSE +0 -0
  71. {claude_mpm-4.0.31.dist-info → claude_mpm-4.0.34.dist-info}/top_level.txt +0 -0
@@ -27,7 +27,11 @@ class AgentInference {
27
27
  // PM delegation tracking for unique instance views
28
28
  pmDelegations: new Map(), // delegation_id -> delegation context
29
29
  // Map of agent events to their PM delegation
30
- agentToDelegation: new Map() // agent_name -> delegation_id
30
+ agentToDelegation: new Map(), // agent_name -> delegation_id
31
+ // Track orphan subagent events (SubagentStart without PM Task)
32
+ orphanSubagents: new Map(), // event_index -> orphan context
33
+ // Track SubagentStart events for orphan detection
34
+ subagentStartEvents: new Map() // agent_name -> array of start events
31
35
  };
32
36
 
33
37
  console.log('Agent inference system initialized');
@@ -43,7 +47,9 @@ class AgentInference {
43
47
  sessionAgents: new Map(),
44
48
  eventAgentMap: new Map(),
45
49
  pmDelegations: new Map(),
46
- agentToDelegation: new Map()
50
+ agentToDelegation: new Map(),
51
+ orphanSubagents: new Map(),
52
+ subagentStartEvents: new Map()
47
53
  };
48
54
  }
49
55
 
@@ -364,6 +370,8 @@ class AgentInference {
364
370
  this.state.eventAgentMap.clear();
365
371
  this.state.pmDelegations.clear();
366
372
  this.state.agentToDelegation.clear();
373
+ this.state.orphanSubagents.clear();
374
+ this.state.subagentStartEvents.clear();
367
375
 
368
376
  console.log('Processing agent inference for', events.length, 'events');
369
377
 
@@ -397,6 +405,25 @@ class AgentInference {
397
405
  };
398
406
  }
399
407
 
408
+ // Track SubagentStart events for orphan detection
409
+ const hookEventName = event.hook_event_name || event.data?.hook_event_name || '';
410
+ const isSubagentStart = hookEventName === 'SubagentStart' ||
411
+ event.type === 'hook.subagent_start' ||
412
+ event.subtype === 'subagent_start';
413
+
414
+ if (isSubagentStart && inference.type === 'subagent') {
415
+ // Track this SubagentStart event
416
+ if (!this.state.subagentStartEvents.has(inference.agentName)) {
417
+ this.state.subagentStartEvents.set(inference.agentName, []);
418
+ }
419
+ this.state.subagentStartEvents.get(inference.agentName).push({
420
+ eventIndex: index,
421
+ event: event,
422
+ timestamp: event.timestamp,
423
+ sessionId: sessionId
424
+ });
425
+ }
426
+
400
427
  // Track delegation boundaries and PM delegations
401
428
  if (event.tool_name === 'Task' && inference.type === 'subagent') {
402
429
  // Start of subagent delegation - create PM delegation entry
@@ -486,12 +513,16 @@ class AgentInference {
486
513
  }
487
514
  });
488
515
 
516
+ // Identify orphan subagents after all events are processed
517
+ this.identifyOrphanSubagents(events);
518
+
489
519
  console.log('Agent inference processing complete. Results:', {
490
520
  total_events: events.length,
491
521
  inferred_agents: this.state.eventAgentMap.size,
492
522
  unique_sessions: this.state.sessionAgents.size,
493
523
  pm_delegations: this.state.pmDelegations.size,
494
- agent_to_delegation_mappings: this.state.agentToDelegation.size
524
+ agent_to_delegation_mappings: this.state.agentToDelegation.size,
525
+ orphan_subagents: this.state.orphanSubagents.size
495
526
  });
496
527
  }
497
528
 
@@ -580,6 +611,229 @@ class AgentInference {
580
611
  return this.state.agentToDelegation;
581
612
  }
582
613
 
614
+ /**
615
+ * Build hierarchical delegation tree structure
616
+ * @returns {Object} Tree structure with PM nodes and subagent children
617
+ */
618
+ buildDelegationHierarchy() {
619
+ // Get all PM delegations
620
+ const pmDelegations = this.getPMDelegations();
621
+ const events = this.eventViewer.events;
622
+
623
+ // Build hierarchy tree
624
+ const hierarchy = {
625
+ mainPM: {
626
+ type: 'pm',
627
+ name: 'PM',
628
+ delegations: [],
629
+ ownEvents: [],
630
+ totalEvents: 0
631
+ },
632
+ impliedPM: {
633
+ type: 'pm_implied',
634
+ name: 'Implied PM',
635
+ delegations: [],
636
+ ownEvents: [],
637
+ totalEvents: 0
638
+ }
639
+ };
640
+
641
+ // Process explicit PM delegations
642
+ for (const [delegationId, delegation] of pmDelegations) {
643
+ hierarchy.mainPM.delegations.push({
644
+ id: delegationId,
645
+ agentName: delegation.agentName,
646
+ taskContext: this.extractTaskContext(delegation.pmCall),
647
+ events: delegation.agentEvents,
648
+ startTime: delegation.timestamp,
649
+ endTime: delegation.endIndex ? events[delegation.endIndex]?.timestamp : null,
650
+ status: delegation.endIndex ? 'completed' : 'active'
651
+ });
652
+ hierarchy.mainPM.totalEvents += delegation.agentEvents.length;
653
+ }
654
+
655
+ // Find PM's own events
656
+ events.forEach((event, index) => {
657
+ const inference = this.getInferredAgent(index);
658
+ if (inference && inference.type === 'main_agent') {
659
+ hierarchy.mainPM.ownEvents.push({
660
+ eventIndex: index,
661
+ event: event
662
+ });
663
+ hierarchy.mainPM.totalEvents++;
664
+ }
665
+ });
666
+
667
+ // Find orphan subagent events
668
+ const orphanEvents = new Map();
669
+ events.forEach((event, index) => {
670
+ const inference = this.getInferredAgent(index);
671
+ if (inference && inference.type === 'subagent') {
672
+ // Check if this is part of any PM delegation
673
+ let isOrphan = true;
674
+ for (const [_, delegation] of pmDelegations) {
675
+ if (delegation.agentEvents.some(e => e.eventIndex === index)) {
676
+ isOrphan = false;
677
+ break;
678
+ }
679
+ }
680
+
681
+ if (isOrphan) {
682
+ const agentName = inference.agentName;
683
+ if (!orphanEvents.has(agentName)) {
684
+ orphanEvents.set(agentName, []);
685
+ }
686
+ orphanEvents.get(agentName).push({
687
+ eventIndex: index,
688
+ event: event,
689
+ inference: inference
690
+ });
691
+ }
692
+ }
693
+ });
694
+
695
+ // Add orphan agents as implied PM delegations
696
+ for (const [agentName, agentEvents] of orphanEvents) {
697
+ hierarchy.impliedPM.delegations.push({
698
+ id: `implied_${agentName}`,
699
+ agentName: agentName,
700
+ taskContext: 'No explicit PM delegation',
701
+ events: agentEvents,
702
+ startTime: agentEvents[0].event.timestamp,
703
+ endTime: agentEvents[agentEvents.length - 1].event.timestamp,
704
+ status: 'completed'
705
+ });
706
+ hierarchy.impliedPM.totalEvents += agentEvents.length;
707
+ }
708
+
709
+ return hierarchy;
710
+ }
711
+
712
+ /**
713
+ * Extract task context from PM call
714
+ * @param {Object} pmCall - PM's Task tool call
715
+ * @returns {string} Task description
716
+ */
717
+ extractTaskContext(pmCall) {
718
+ if (!pmCall) return 'Unknown task';
719
+
720
+ const params = pmCall.tool_parameters || pmCall.data?.tool_parameters || {};
721
+ return params.task || params.request || params.description || 'Task delegation';
722
+ }
723
+
724
+ /**
725
+ * Identify orphan subagents (SubagentStart without PM Task delegation)
726
+ * @param {Array} events - All events to analyze
727
+ */
728
+ identifyOrphanSubagents(events) {
729
+ const ORPHAN_TIME_WINDOW = 5000; // 5 seconds to group orphans together
730
+
731
+ // Check each SubagentStart event
732
+ for (const [agentName, startEvents] of this.state.subagentStartEvents) {
733
+ for (const startEvent of startEvents) {
734
+ const eventIndex = startEvent.eventIndex;
735
+ const timestamp = new Date(startEvent.timestamp).getTime();
736
+
737
+ // Check if this SubagentStart has a corresponding PM Task delegation
738
+ let hasTaskDelegation = false;
739
+
740
+ // Look for a Task tool call within a reasonable time window before this SubagentStart
741
+ for (let i = Math.max(0, eventIndex - 20); i < eventIndex; i++) {
742
+ const prevEvent = events[i];
743
+ if (!prevEvent) continue;
744
+
745
+ const prevTimestamp = new Date(prevEvent.timestamp).getTime();
746
+ const timeDiff = timestamp - prevTimestamp;
747
+
748
+ // Check if this is a Task tool call within 10 seconds
749
+ if (prevEvent.tool_name === 'Task' && timeDiff >= 0 && timeDiff < 10000) {
750
+ const inference = this.state.eventAgentMap.get(i);
751
+ if (inference && inference.agentName === agentName) {
752
+ hasTaskDelegation = true;
753
+ break;
754
+ }
755
+ }
756
+ }
757
+
758
+ // If no Task delegation found, mark as orphan
759
+ if (!hasTaskDelegation) {
760
+ this.state.orphanSubagents.set(eventIndex, {
761
+ agentName: agentName,
762
+ timestamp: startEvent.timestamp,
763
+ sessionId: startEvent.sessionId,
764
+ event: startEvent.event,
765
+ groupingKey: null // Will be set by grouping logic
766
+ });
767
+ }
768
+ }
769
+ }
770
+
771
+ // Group orphan subagents by time proximity or session
772
+ this.groupOrphanSubagents(ORPHAN_TIME_WINDOW);
773
+ }
774
+
775
+ /**
776
+ * Group orphan subagents that occur close together in time or same session
777
+ * @param {number} timeWindow - Time window in milliseconds for grouping
778
+ */
779
+ groupOrphanSubagents(timeWindow) {
780
+ const orphansList = Array.from(this.state.orphanSubagents.values())
781
+ .sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
782
+
783
+ let currentGroup = null;
784
+ let lastTimestamp = null;
785
+
786
+ for (const orphan of orphansList) {
787
+ const timestamp = new Date(orphan.timestamp).getTime();
788
+
789
+ // Check if this orphan should be in the same group
790
+ if (!currentGroup ||
791
+ (lastTimestamp && timestamp - lastTimestamp > timeWindow)) {
792
+ // Start a new group
793
+ currentGroup = `implied_pm_${orphan.sessionId}_${timestamp}`;
794
+ }
795
+
796
+ orphan.groupingKey = currentGroup;
797
+ lastTimestamp = timestamp;
798
+ }
799
+ }
800
+
801
+ /**
802
+ * Check if a subagent event is an orphan (no PM Task delegation)
803
+ * @param {number} eventIndex - Index of the event
804
+ * @returns {boolean} True if the event is from an orphan subagent
805
+ */
806
+ isOrphanSubagent(eventIndex) {
807
+ return this.state.orphanSubagents.has(eventIndex);
808
+ }
809
+
810
+ /**
811
+ * Get orphan subagent context for an event
812
+ * @param {number} eventIndex - Index of the event
813
+ * @returns {Object|null} Orphan context or null
814
+ */
815
+ getOrphanContext(eventIndex) {
816
+ return this.state.orphanSubagents.get(eventIndex) || null;
817
+ }
818
+
819
+ /**
820
+ * Get all orphan subagent groups
821
+ * @returns {Map} Map of groupingKey -> array of orphan contexts
822
+ */
823
+ getOrphanGroups() {
824
+ const groups = new Map();
825
+
826
+ for (const orphan of this.state.orphanSubagents.values()) {
827
+ const key = orphan.groupingKey;
828
+ if (!groups.has(key)) {
829
+ groups.set(key, []);
830
+ }
831
+ groups.get(key).push(orphan);
832
+ }
833
+
834
+ return groups;
835
+ }
836
+
583
837
  /**
584
838
  * Get unique agent instances (one per agent type, consolidating multiple delegations)
585
839
  * This is used for the unique instance view in the agents tab
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Build Tracker Component
3
+ *
4
+ * WHY: Displays and manages version/build information for both MPM and Monitor UI,
5
+ * providing users with clear visibility of the current system versions.
6
+ *
7
+ * DESIGN DECISION: Implemented as a standalone component that can be easily
8
+ * integrated into the dashboard header, with automatic updates from SocketIO.
9
+ */
10
+
11
+ export class BuildTracker {
12
+ constructor() {
13
+ this.element = null;
14
+ this.buildInfo = {
15
+ monitor: {
16
+ version: "1.0.0",
17
+ build: 1,
18
+ formatted_build: "0001",
19
+ full_version: "v1.0.0-0001"
20
+ },
21
+ mpm: {
22
+ version: "unknown",
23
+ build: "unknown",
24
+ full_version: "v0.0.0"
25
+ }
26
+ };
27
+
28
+ // Socket client reference (will be set during initialization)
29
+ this.socketClient = null;
30
+
31
+ // Initialize the component
32
+ this.init();
33
+ }
34
+
35
+ /**
36
+ * Initialize the build tracker component
37
+ */
38
+ init() {
39
+ console.log('Initializing BuildTracker component');
40
+ this.createElements();
41
+ this.setupEventListeners();
42
+ }
43
+
44
+ /**
45
+ * Create the DOM elements for version display
46
+ *
47
+ * WHY: Creates a clean, unobtrusive version display that fits
48
+ * seamlessly into the dashboard header.
49
+ */
50
+ createElements() {
51
+ // Create container element
52
+ this.element = document.createElement('div');
53
+ this.element.className = 'version-display';
54
+ this.element.id = 'version-display';
55
+
56
+ // Create MPM version element
57
+ const mpmVersion = document.createElement('span');
58
+ mpmVersion.className = 'version-item mpm-version';
59
+ mpmVersion.id = 'mpm-version';
60
+ mpmVersion.innerHTML = `
61
+ <span class="version-label">MPM</span>
62
+ <span class="version-value">v0.0.0</span>
63
+ `;
64
+
65
+ // Create separator
66
+ const separator = document.createElement('span');
67
+ separator.className = 'version-separator';
68
+ separator.textContent = '|';
69
+
70
+ // Create Monitor version element
71
+ const monitorVersion = document.createElement('span');
72
+ monitorVersion.className = 'version-item monitor-version';
73
+ monitorVersion.id = 'monitor-version';
74
+ monitorVersion.innerHTML = `
75
+ <span class="version-label">Monitor</span>
76
+ <span class="version-value">v1.0.0-0001</span>
77
+ `;
78
+
79
+ // Assemble elements
80
+ this.element.appendChild(mpmVersion);
81
+ this.element.appendChild(separator);
82
+ this.element.appendChild(monitorVersion);
83
+
84
+ // Add tooltip for detailed info
85
+ this.element.title = 'Click for detailed version information';
86
+ }
87
+
88
+ /**
89
+ * Set the socket client for receiving updates
90
+ *
91
+ * @param {Object} socketClient - The Socket.IO client instance
92
+ */
93
+ setSocketClient(socketClient) {
94
+ this.socketClient = socketClient;
95
+
96
+ // Listen for build info updates
97
+ if (this.socketClient && this.socketClient.socket) {
98
+ // Listen for welcome message with build info
99
+ this.socketClient.socket.on('welcome', (eventData) => {
100
+ // Handle both old format (direct) and new schema (nested in data)
101
+ const buildInfo = eventData.build_info ||
102
+ (eventData.data && eventData.data.build_info);
103
+ if (buildInfo) {
104
+ this.updateBuildInfo(buildInfo);
105
+ }
106
+ });
107
+
108
+ // Listen for status updates with build info
109
+ this.socketClient.socket.on('status', (eventData) => {
110
+ // Handle both old format (direct) and new schema (nested in data)
111
+ const buildInfo = eventData.build_info ||
112
+ (eventData.data && eventData.data.build_info);
113
+ if (buildInfo) {
114
+ this.updateBuildInfo(buildInfo);
115
+ }
116
+ });
117
+
118
+ // Listen for explicit build info updates
119
+ this.socketClient.socket.on('build_info', (data) => {
120
+ this.updateBuildInfo(data);
121
+ });
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Update the build information
127
+ *
128
+ * @param {Object} buildInfo - Build information from server
129
+ */
130
+ updateBuildInfo(buildInfo) {
131
+ console.log('Updating build info:', buildInfo);
132
+
133
+ // Store the build info
134
+ this.buildInfo = buildInfo;
135
+
136
+ // Update display
137
+ this.updateDisplay();
138
+ }
139
+
140
+ /**
141
+ * Update the version display elements
142
+ *
143
+ * WHY: Keeps the UI in sync with the latest version information
144
+ * received from the server.
145
+ */
146
+ updateDisplay() {
147
+ // Update MPM version
148
+ const mpmElement = this.element.querySelector('.mpm-version .version-value');
149
+ if (mpmElement && this.buildInfo.mpm) {
150
+ const mpmVersion = this.buildInfo.mpm.full_version ||
151
+ `v${this.buildInfo.mpm.version}`;
152
+ mpmElement.textContent = mpmVersion;
153
+
154
+ // Add build number to tooltip if available
155
+ if (this.buildInfo.mpm.build && this.buildInfo.mpm.build !== "unknown") {
156
+ mpmElement.parentElement.title = `MPM Build: ${this.buildInfo.mpm.build}`;
157
+ }
158
+ }
159
+
160
+ // Update Monitor version
161
+ const monitorElement = this.element.querySelector('.monitor-version .version-value');
162
+ if (monitorElement && this.buildInfo.monitor) {
163
+ const monitorVersion = this.buildInfo.monitor.full_version ||
164
+ `v${this.buildInfo.monitor.version}-${this.buildInfo.monitor.formatted_build}`;
165
+ monitorElement.textContent = monitorVersion;
166
+
167
+ // Add last updated to tooltip if available
168
+ if (this.buildInfo.monitor.last_updated) {
169
+ const lastUpdated = new Date(this.buildInfo.monitor.last_updated).toLocaleString();
170
+ monitorElement.parentElement.title = `Monitor Build: ${this.buildInfo.monitor.formatted_build}\nLast Updated: ${lastUpdated}`;
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Setup event listeners
177
+ *
178
+ * WHY: Allows users to interact with the version display for
179
+ * additional information or actions.
180
+ */
181
+ setupEventListeners() {
182
+ // Click handler for showing detailed version info
183
+ this.element.addEventListener('click', () => {
184
+ this.showDetailedInfo();
185
+ });
186
+ }
187
+
188
+ /**
189
+ * Show detailed version information in a modal or alert
190
+ *
191
+ * WHY: Provides power users with detailed build and version
192
+ * information for debugging and support purposes.
193
+ */
194
+ showDetailedInfo() {
195
+ const info = [];
196
+
197
+ // MPM information
198
+ if (this.buildInfo.mpm) {
199
+ info.push('=== MPM Framework ===');
200
+ info.push(`Version: ${this.buildInfo.mpm.version}`);
201
+ if (this.buildInfo.mpm.build && this.buildInfo.mpm.build !== "unknown") {
202
+ info.push(`Build: ${this.buildInfo.mpm.build}`);
203
+ }
204
+ info.push(`Full: ${this.buildInfo.mpm.full_version}`);
205
+ }
206
+
207
+ info.push('');
208
+
209
+ // Monitor information
210
+ if (this.buildInfo.monitor) {
211
+ info.push('=== Monitor UI ===');
212
+ info.push(`Version: ${this.buildInfo.monitor.version}`);
213
+ info.push(`Build: ${this.buildInfo.monitor.formatted_build} (${this.buildInfo.monitor.build})`);
214
+ info.push(`Full: ${this.buildInfo.monitor.full_version}`);
215
+ if (this.buildInfo.monitor.last_updated) {
216
+ const lastUpdated = new Date(this.buildInfo.monitor.last_updated).toLocaleString();
217
+ info.push(`Updated: ${lastUpdated}`);
218
+ }
219
+ }
220
+
221
+ // Show in console and alert
222
+ console.log('Version Information:\n' + info.join('\n'));
223
+
224
+ // Create a simple modal-like display
225
+ const modal = document.createElement('div');
226
+ modal.className = 'version-modal';
227
+ modal.innerHTML = `
228
+ <div class="version-modal-content">
229
+ <h3>Version Information</h3>
230
+ <pre>${info.join('\n')}</pre>
231
+ <button onclick="this.parentElement.parentElement.remove()">Close</button>
232
+ </div>
233
+ `;
234
+
235
+ // Add to body
236
+ document.body.appendChild(modal);
237
+
238
+ // Auto-remove after 10 seconds
239
+ setTimeout(() => {
240
+ modal.remove();
241
+ }, 10000);
242
+ }
243
+
244
+ /**
245
+ * Mount the component to a parent element
246
+ *
247
+ * @param {HTMLElement|string} parent - Parent element or selector
248
+ */
249
+ mount(parent) {
250
+ const parentElement = typeof parent === 'string'
251
+ ? document.querySelector(parent)
252
+ : parent;
253
+
254
+ if (parentElement && this.element) {
255
+ parentElement.appendChild(this.element);
256
+ console.log('BuildTracker mounted to', parent);
257
+ } else {
258
+ console.error('Failed to mount BuildTracker: parent element not found');
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Get the component's DOM element
264
+ *
265
+ * @returns {HTMLElement} The component's root element
266
+ */
267
+ getElement() {
268
+ return this.element;
269
+ }
270
+
271
+ /**
272
+ * Destroy the component and clean up
273
+ */
274
+ destroy() {
275
+ if (this.element && this.element.parentNode) {
276
+ this.element.parentNode.removeChild(this.element);
277
+ }
278
+
279
+ // Clean up socket listeners
280
+ if (this.socketClient && this.socketClient.socket) {
281
+ this.socketClient.socket.off('welcome');
282
+ this.socketClient.socket.off('status');
283
+ this.socketClient.socket.off('build_info');
284
+ }
285
+
286
+ this.element = null;
287
+ this.socketClient = null;
288
+ }
289
+ }