claude-mpm 4.2.9__py3-none-any.whl → 4.2.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/dashboard.py +59 -126
- claude_mpm/cli/commands/monitor.py +71 -212
- claude_mpm/cli/commands/run.py +33 -33
- claude_mpm/dashboard/static/css/code-tree.css +8 -16
- claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/js/components/code-tree.js +692 -114
- claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
- claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
- claude_mpm/dashboard/static/js/dashboard.js +108 -91
- claude_mpm/dashboard/static/js/socket-client.js +9 -7
- claude_mpm/dashboard/templates/index.html +2 -7
- claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
- claude_mpm/services/agents/deployment/agent_template_builder.py +0 -1
- claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +256 -0
- claude_mpm/services/monitor/event_emitter.py +279 -0
- claude_mpm/services/monitor/handlers/__init__.py +20 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
- claude_mpm/services/monitor/handlers/dashboard.py +298 -0
- claude_mpm/services/monitor/handlers/hooks.py +491 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +298 -0
- claude_mpm/services/monitor/server.py +442 -0
- claude_mpm/tools/code_tree_analyzer.py +33 -17
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/RECORD +41 -36
- claude_mpm/cli/commands/socketio_monitor.py +0 -233
- claude_mpm/scripts/socketio_daemon.py +0 -571
- claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
- claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
- claude_mpm/scripts/socketio_server_manager.py +0 -349
- claude_mpm/services/cli/dashboard_launcher.py +0 -423
- claude_mpm/services/cli/socketio_manager.py +0 -595
- claude_mpm/services/dashboard/stable_server.py +0 -1020
- claude_mpm/services/socketio/monitor_server.py +0 -505
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/top_level.txt +0 -0
|
@@ -16,6 +16,26 @@ class UnifiedDataViewer {
|
|
|
16
16
|
this.container = document.getElementById(containerId);
|
|
17
17
|
this.currentData = null;
|
|
18
18
|
this.currentType = null;
|
|
19
|
+
|
|
20
|
+
// Global JSON visibility state - synchronized with localStorage
|
|
21
|
+
// This ensures all JSON sections maintain consistent state
|
|
22
|
+
this.globalJsonExpanded = localStorage.getItem('dashboard-json-expanded') === 'true';
|
|
23
|
+
|
|
24
|
+
// Separate state for "Full Event Data" sections - uses its own localStorage key
|
|
25
|
+
// This allows independent control of Full Event Data visibility
|
|
26
|
+
this.fullEventDataExpanded = localStorage.getItem('dashboard-full-event-expanded') === 'true';
|
|
27
|
+
|
|
28
|
+
// Listen for global JSON toggle changes from other components
|
|
29
|
+
document.addEventListener('jsonToggleChanged', (e) => {
|
|
30
|
+
this.globalJsonExpanded = e.detail.expanded;
|
|
31
|
+
this.updateAllJsonSections();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Listen for full event data toggle changes
|
|
35
|
+
document.addEventListener('fullEventToggleChanged', (e) => {
|
|
36
|
+
this.fullEventDataExpanded = e.detail.expanded;
|
|
37
|
+
this.updateAllFullEventSections();
|
|
38
|
+
});
|
|
19
39
|
}
|
|
20
40
|
|
|
21
41
|
/**
|
|
@@ -1533,6 +1553,112 @@ class UnifiedDataViewer {
|
|
|
1533
1553
|
div.textContent = text;
|
|
1534
1554
|
return div.innerHTML;
|
|
1535
1555
|
}
|
|
1556
|
+
|
|
1557
|
+
/**
|
|
1558
|
+
* Toggle JSON section visibility and update global state
|
|
1559
|
+
* WHY: Maintains sticky state across all JSON sections for consistent behavior
|
|
1560
|
+
* @param {string} sectionId - ID of the specific section being toggled
|
|
1561
|
+
* @param {HTMLElement} button - The button element that was clicked
|
|
1562
|
+
*/
|
|
1563
|
+
toggleJsonSection(sectionId, button) {
|
|
1564
|
+
// Toggle the global state
|
|
1565
|
+
this.globalJsonExpanded = !this.globalJsonExpanded;
|
|
1566
|
+
|
|
1567
|
+
// Persist the preference to localStorage
|
|
1568
|
+
localStorage.setItem('dashboard-json-expanded', this.globalJsonExpanded.toString());
|
|
1569
|
+
|
|
1570
|
+
// Update ALL JSON sections on the page
|
|
1571
|
+
this.updateAllJsonSections();
|
|
1572
|
+
|
|
1573
|
+
// Dispatch event to notify other components (like module-viewer) of the change
|
|
1574
|
+
document.dispatchEvent(new CustomEvent('jsonToggleChanged', {
|
|
1575
|
+
detail: { expanded: this.globalJsonExpanded }
|
|
1576
|
+
}));
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Toggle Full Event Data section visibility and update state
|
|
1581
|
+
* WHY: Maintains separate sticky state for Full Event Data sections
|
|
1582
|
+
* @param {string} sectionId - ID of the specific section being toggled
|
|
1583
|
+
* @param {HTMLElement} button - The button element that was clicked
|
|
1584
|
+
*/
|
|
1585
|
+
toggleFullEventSection(sectionId, button) {
|
|
1586
|
+
// Toggle the full event data state
|
|
1587
|
+
this.fullEventDataExpanded = !this.fullEventDataExpanded;
|
|
1588
|
+
|
|
1589
|
+
// Persist the preference to localStorage
|
|
1590
|
+
localStorage.setItem('dashboard-full-event-expanded', this.fullEventDataExpanded.toString());
|
|
1591
|
+
|
|
1592
|
+
// Update ALL Full Event sections on the page
|
|
1593
|
+
this.updateAllFullEventSections();
|
|
1594
|
+
|
|
1595
|
+
// Dispatch event to notify other components of the change
|
|
1596
|
+
document.dispatchEvent(new CustomEvent('fullEventToggleChanged', {
|
|
1597
|
+
detail: { expanded: this.fullEventDataExpanded }
|
|
1598
|
+
}));
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/**
|
|
1602
|
+
* Update all JSON sections on the page to match global state
|
|
1603
|
+
* WHY: Ensures all "Structured Data" sections maintain consistent visibility
|
|
1604
|
+
*/
|
|
1605
|
+
updateAllJsonSections() {
|
|
1606
|
+
// Find all unified JSON sections (NOT full event sections)
|
|
1607
|
+
const allJsonContents = document.querySelectorAll('.unified-json-content');
|
|
1608
|
+
const allJsonButtons = document.querySelectorAll('.unified-json-toggle');
|
|
1609
|
+
|
|
1610
|
+
// Update each JSON section
|
|
1611
|
+
allJsonContents.forEach(content => {
|
|
1612
|
+
if (this.globalJsonExpanded) {
|
|
1613
|
+
content.style.display = 'block';
|
|
1614
|
+
} else {
|
|
1615
|
+
content.style.display = 'none';
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
|
|
1619
|
+
// Update all button states
|
|
1620
|
+
allJsonButtons.forEach(button => {
|
|
1621
|
+
const title = button.textContent.substring(2); // Remove arrow
|
|
1622
|
+
if (this.globalJsonExpanded) {
|
|
1623
|
+
button.innerHTML = '▼ ' + title;
|
|
1624
|
+
button.classList.add('expanded');
|
|
1625
|
+
} else {
|
|
1626
|
+
button.innerHTML = '▶ ' + title;
|
|
1627
|
+
button.classList.remove('expanded');
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
/**
|
|
1633
|
+
* Update all Full Event Data sections on the page to match state
|
|
1634
|
+
* WHY: Ensures all "Full Event Data" sections maintain consistent visibility
|
|
1635
|
+
*/
|
|
1636
|
+
updateAllFullEventSections() {
|
|
1637
|
+
// Find all full event sections
|
|
1638
|
+
const allFullEventContents = document.querySelectorAll('.full-event-content');
|
|
1639
|
+
const allFullEventButtons = document.querySelectorAll('.full-event-toggle');
|
|
1640
|
+
|
|
1641
|
+
// Update each full event section
|
|
1642
|
+
allFullEventContents.forEach(content => {
|
|
1643
|
+
if (this.fullEventDataExpanded) {
|
|
1644
|
+
content.style.display = 'block';
|
|
1645
|
+
} else {
|
|
1646
|
+
content.style.display = 'none';
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
|
|
1650
|
+
// Update all button states
|
|
1651
|
+
allFullEventButtons.forEach(button => {
|
|
1652
|
+
const title = button.textContent.substring(2); // Remove arrow
|
|
1653
|
+
if (this.fullEventDataExpanded) {
|
|
1654
|
+
button.innerHTML = '▼ ' + title;
|
|
1655
|
+
button.classList.add('expanded');
|
|
1656
|
+
} else {
|
|
1657
|
+
button.innerHTML = '▶ ' + title;
|
|
1658
|
+
button.classList.remove('expanded');
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1536
1662
|
|
|
1537
1663
|
/**
|
|
1538
1664
|
* Create a collapsible JSON viewer for secondary details
|
|
@@ -1545,22 +1671,28 @@ class UnifiedDataViewer {
|
|
|
1545
1671
|
// Filter out sensitive or overly verbose properties
|
|
1546
1672
|
const cleanData = this.cleanDataForDisplay(data);
|
|
1547
1673
|
|
|
1674
|
+
// Determine which state to use based on title
|
|
1675
|
+
// "Full Event Data" and similar titles use the fullEventDataExpanded state
|
|
1676
|
+
// Other titles use the global JSON state (for backward compatibility)
|
|
1677
|
+
const isFullEventData = title.includes('Full Event') || title.includes('Full Details') ||
|
|
1678
|
+
title.includes('Full Agent') || title.includes('Full Tool');
|
|
1679
|
+
const isExpanded = isFullEventData ? this.fullEventDataExpanded : this.globalJsonExpanded;
|
|
1680
|
+
const display = isExpanded ? 'block' : 'none';
|
|
1681
|
+
const arrow = isExpanded ? '▼' : '▶';
|
|
1682
|
+
const expandedClass = isExpanded ? 'expanded' : '';
|
|
1683
|
+
|
|
1684
|
+
// Use different toggle function based on section type
|
|
1685
|
+
const toggleFunction = isFullEventData ? 'toggleFullEventSection' : 'toggleJsonSection';
|
|
1686
|
+
|
|
1548
1687
|
return `
|
|
1549
1688
|
<div class="collapsible-json-section">
|
|
1550
|
-
<button class="collapsible-json-toggle"
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
} else {
|
|
1558
|
-
content.style.display = 'none';
|
|
1559
|
-
button.classList.remove('expanded');
|
|
1560
|
-
button.innerHTML = '▶ ${title}';
|
|
1561
|
-
}
|
|
1562
|
-
">▶ ${title}</button>
|
|
1563
|
-
<div id="${sectionId}" class="collapsible-json-content" style="display: none;">
|
|
1689
|
+
<button class="collapsible-json-toggle ${isFullEventData ? 'full-event-toggle' : 'unified-json-toggle'} ${expandedClass}"
|
|
1690
|
+
data-section-id="${sectionId}"
|
|
1691
|
+
data-is-full-event="${isFullEventData}"
|
|
1692
|
+
onclick="window.unifiedDataViewer.${toggleFunction}('${sectionId}', this)">
|
|
1693
|
+
${arrow} ${title}
|
|
1694
|
+
</button>
|
|
1695
|
+
<div id="${sectionId}" class="collapsible-json-content ${isFullEventData ? 'full-event-content' : 'unified-json-content'}" style="display: ${display};">
|
|
1564
1696
|
<pre class="json-viewer">${this.escapeHtml(JSON.stringify(cleanData, null, 2))}</pre>
|
|
1565
1697
|
</div>
|
|
1566
1698
|
</div>
|
|
@@ -1639,6 +1771,26 @@ export default UnifiedDataViewer;
|
|
|
1639
1771
|
// Make globally available for non-module usage
|
|
1640
1772
|
window.UnifiedDataViewer = UnifiedDataViewer;
|
|
1641
1773
|
|
|
1774
|
+
// Create a global instance immediately for inline onclick handlers
|
|
1775
|
+
// This ensures the instance is available when HTML is rendered dynamically
|
|
1776
|
+
if (typeof window !== 'undefined') {
|
|
1777
|
+
// Always create/update the global instance
|
|
1778
|
+
window.unifiedDataViewer = new UnifiedDataViewer();
|
|
1779
|
+
|
|
1780
|
+
// Also expose the methods directly on window as a fallback
|
|
1781
|
+
window.toggleFullEventSection = function(sectionId, button) {
|
|
1782
|
+
if (window.unifiedDataViewer) {
|
|
1783
|
+
window.unifiedDataViewer.toggleFullEventSection(sectionId, button);
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1787
|
+
window.toggleJsonSection = function(sectionId, button) {
|
|
1788
|
+
if (window.unifiedDataViewer) {
|
|
1789
|
+
window.unifiedDataViewer.toggleJsonSection(sectionId, button);
|
|
1790
|
+
}
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1642
1794
|
// Create a global instance for inline preview functionality
|
|
1643
1795
|
if (typeof window !== 'undefined') {
|
|
1644
1796
|
window.addEventListener('DOMContentLoaded', function() {
|
|
@@ -1073,35 +1073,7 @@ window.switchTab = function(tabName) {
|
|
|
1073
1073
|
}
|
|
1074
1074
|
};
|
|
1075
1075
|
|
|
1076
|
-
// File Viewer Modal Functions -
|
|
1077
|
-
window.showFileViewerModal = function(filePath, workingDir) {
|
|
1078
|
-
// Use the dashboard's current working directory if not provided
|
|
1079
|
-
if (!workingDir && window.dashboard && window.dashboard.currentWorkingDir) {
|
|
1080
|
-
workingDir = window.dashboard.currentWorkingDir;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
// Create modal if it doesn't exist
|
|
1084
|
-
let modal = document.getElementById('file-viewer-modal');
|
|
1085
|
-
if (!modal) {
|
|
1086
|
-
modal = createFileViewerModal();
|
|
1087
|
-
document.body.appendChild(modal);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
// Update modal content
|
|
1091
|
-
updateFileViewerModal(modal, filePath, workingDir);
|
|
1092
|
-
|
|
1093
|
-
// Show the modal as flex container
|
|
1094
|
-
modal.style.display = 'flex';
|
|
1095
|
-
document.body.style.overflow = 'hidden'; // Prevent background scrolling
|
|
1096
|
-
};
|
|
1097
|
-
|
|
1098
|
-
window.hideFileViewerModal = function() {
|
|
1099
|
-
const modal = document.getElementById('file-viewer-modal');
|
|
1100
|
-
if (modal) {
|
|
1101
|
-
modal.style.display = 'none';
|
|
1102
|
-
document.body.style.overflow = ''; // Restore background scrolling
|
|
1103
|
-
}
|
|
1104
|
-
};
|
|
1076
|
+
// File Viewer Modal Functions - Removed broken duplicate (using the one at line 1505)
|
|
1105
1077
|
|
|
1106
1078
|
window.copyFileContent = function() {
|
|
1107
1079
|
const modal = document.getElementById('file-viewer-modal');
|
|
@@ -1116,11 +1088,13 @@ window.copyFileContent = function() {
|
|
|
1116
1088
|
navigator.clipboard.writeText(text).then(() => {
|
|
1117
1089
|
// Show brief feedback
|
|
1118
1090
|
const button = modal.querySelector('.file-content-copy');
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1091
|
+
if (button) {
|
|
1092
|
+
const originalText = button.textContent;
|
|
1093
|
+
button.textContent = '✅ Copied!';
|
|
1094
|
+
setTimeout(() => {
|
|
1095
|
+
button.textContent = originalText;
|
|
1096
|
+
}, 2000);
|
|
1097
|
+
}
|
|
1124
1098
|
}).catch(err => {
|
|
1125
1099
|
console.error('Failed to copy text:', err);
|
|
1126
1100
|
});
|
|
@@ -1134,11 +1108,13 @@ window.copyFileContent = function() {
|
|
|
1134
1108
|
document.body.removeChild(textarea);
|
|
1135
1109
|
|
|
1136
1110
|
const button = modal.querySelector('.file-content-copy');
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1111
|
+
if (button) {
|
|
1112
|
+
const originalText = button.textContent;
|
|
1113
|
+
button.textContent = '✅ Copied!';
|
|
1114
|
+
setTimeout(() => {
|
|
1115
|
+
button.textContent = originalText;
|
|
1116
|
+
}, 2000);
|
|
1117
|
+
}
|
|
1142
1118
|
}
|
|
1143
1119
|
};
|
|
1144
1120
|
|
|
@@ -1214,13 +1190,27 @@ async function updateFileViewerModal(modal, filePath, workingDir) {
|
|
|
1214
1190
|
const filePathElement = modal.querySelector('.file-viewer-file-path');
|
|
1215
1191
|
const fileSizeElement = modal.querySelector('.file-viewer-file-size');
|
|
1216
1192
|
|
|
1217
|
-
filePathElement
|
|
1218
|
-
|
|
1193
|
+
if (filePathElement) {
|
|
1194
|
+
filePathElement.textContent = filePath;
|
|
1195
|
+
}
|
|
1196
|
+
if (fileSizeElement) {
|
|
1197
|
+
fileSizeElement.textContent = '';
|
|
1198
|
+
}
|
|
1219
1199
|
|
|
1220
1200
|
// Show loading state
|
|
1221
|
-
modal.querySelector('.file-viewer-loading')
|
|
1222
|
-
modal.querySelector('.file-viewer-error')
|
|
1223
|
-
modal.querySelector('.file-viewer-content-area')
|
|
1201
|
+
const loadingElement = modal.querySelector('.file-viewer-loading');
|
|
1202
|
+
const errorElement = modal.querySelector('.file-viewer-error');
|
|
1203
|
+
const contentArea = modal.querySelector('.file-viewer-content-area');
|
|
1204
|
+
|
|
1205
|
+
if (loadingElement) {
|
|
1206
|
+
loadingElement.style.display = 'flex';
|
|
1207
|
+
}
|
|
1208
|
+
if (errorElement) {
|
|
1209
|
+
errorElement.style.display = 'none';
|
|
1210
|
+
}
|
|
1211
|
+
if (contentArea) {
|
|
1212
|
+
contentArea.style.display = 'none';
|
|
1213
|
+
}
|
|
1224
1214
|
|
|
1225
1215
|
try {
|
|
1226
1216
|
// Get the Socket.IO client
|
|
@@ -1264,7 +1254,10 @@ async function updateFileViewerModal(modal, filePath, workingDir) {
|
|
|
1264
1254
|
// File content received successfully
|
|
1265
1255
|
|
|
1266
1256
|
// Hide loading
|
|
1267
|
-
modal.querySelector('.file-viewer-loading')
|
|
1257
|
+
const loadingEl = modal.querySelector('.file-viewer-loading');
|
|
1258
|
+
if (loadingEl) {
|
|
1259
|
+
loadingEl.style.display = 'none';
|
|
1260
|
+
}
|
|
1268
1261
|
|
|
1269
1262
|
// Show successful content
|
|
1270
1263
|
displayFileContent(modal, result);
|
|
@@ -1272,7 +1265,10 @@ async function updateFileViewerModal(modal, filePath, workingDir) {
|
|
|
1272
1265
|
} catch (error) {
|
|
1273
1266
|
console.error('❌ Failed to fetch file content:', error);
|
|
1274
1267
|
|
|
1275
|
-
modal.querySelector('.file-viewer-loading')
|
|
1268
|
+
const loadingEl2 = modal.querySelector('.file-viewer-loading');
|
|
1269
|
+
if (loadingEl2) {
|
|
1270
|
+
loadingEl2.style.display = 'none';
|
|
1271
|
+
}
|
|
1276
1272
|
|
|
1277
1273
|
// Create detailed error message
|
|
1278
1274
|
let errorMessage = error.message || 'Unknown error occurred';
|
|
@@ -1373,21 +1369,25 @@ function displayFileError(modal, result) {
|
|
|
1373
1369
|
|
|
1374
1370
|
let errorMessage = result.error || 'Unknown error occurred';
|
|
1375
1371
|
|
|
1376
|
-
messageElement
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
if (result.suggestions && result.suggestions.length > 0) {
|
|
1383
|
-
suggestionsElement.innerHTML = `
|
|
1384
|
-
<h4>Suggestions:</h4>
|
|
1385
|
-
<ul>
|
|
1386
|
-
${result.suggestions.map(s => `<li>${s}</li>`).join('')}
|
|
1387
|
-
</ul>
|
|
1372
|
+
if (messageElement) {
|
|
1373
|
+
messageElement.innerHTML = `
|
|
1374
|
+
<div class="error-main">${errorMessage}</div>
|
|
1375
|
+
${result.file_path ? `<div class="error-file">File: ${result.file_path}</div>` : ''}
|
|
1376
|
+
${result.working_dir ? `<div class="error-dir">Working directory: ${result.working_dir}</div>` : ''}
|
|
1388
1377
|
`;
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
if (suggestionsElement) {
|
|
1381
|
+
if (result.suggestions && result.suggestions.length > 0) {
|
|
1382
|
+
suggestionsElement.innerHTML = `
|
|
1383
|
+
<h4>Suggestions:</h4>
|
|
1384
|
+
<ul>
|
|
1385
|
+
${result.suggestions.map(s => `<li>${s}</li>`).join('')}
|
|
1386
|
+
</ul>
|
|
1387
|
+
`;
|
|
1388
|
+
} else {
|
|
1389
|
+
suggestionsElement.innerHTML = '';
|
|
1390
|
+
}
|
|
1391
1391
|
}
|
|
1392
1392
|
|
|
1393
1393
|
console.log('📋 Displaying file viewer error:', {
|
|
@@ -1396,7 +1396,9 @@ function displayFileError(modal, result) {
|
|
|
1396
1396
|
suggestions: result.suggestions
|
|
1397
1397
|
});
|
|
1398
1398
|
|
|
1399
|
-
errorArea
|
|
1399
|
+
if (errorArea) {
|
|
1400
|
+
errorArea.style.display = 'block';
|
|
1401
|
+
}
|
|
1400
1402
|
}
|
|
1401
1403
|
|
|
1402
1404
|
function highlightCode(code, extension) {
|
|
@@ -1502,7 +1504,7 @@ function formatFileSize(bytes) {
|
|
|
1502
1504
|
}
|
|
1503
1505
|
|
|
1504
1506
|
// File Viewer Modal Functions
|
|
1505
|
-
window.showFileViewerModal = function(filePath) {
|
|
1507
|
+
window.showFileViewerModal = async function(filePath) {
|
|
1506
1508
|
// Use the dashboard's current working directory
|
|
1507
1509
|
let workingDir = '';
|
|
1508
1510
|
if (window.dashboard && window.dashboard.currentWorkingDir) {
|
|
@@ -1514,10 +1516,15 @@ window.showFileViewerModal = function(filePath) {
|
|
|
1514
1516
|
if (!modal) {
|
|
1515
1517
|
modal = createFileViewerModal();
|
|
1516
1518
|
document.body.appendChild(modal);
|
|
1519
|
+
|
|
1520
|
+
// Small delay to ensure DOM is fully updated
|
|
1521
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
1517
1522
|
}
|
|
1518
1523
|
|
|
1519
1524
|
// Update modal content
|
|
1520
|
-
updateFileViewerModal(modal, filePath, workingDir)
|
|
1525
|
+
updateFileViewerModal(modal, filePath, workingDir).catch(error => {
|
|
1526
|
+
console.error('Error updating file viewer modal:', error);
|
|
1527
|
+
});
|
|
1521
1528
|
|
|
1522
1529
|
// Show the modal as flex container
|
|
1523
1530
|
modal.style.display = 'flex';
|
|
@@ -1545,11 +1552,13 @@ window.copyFileContent = function() {
|
|
|
1545
1552
|
navigator.clipboard.writeText(text).then(() => {
|
|
1546
1553
|
// Show brief feedback
|
|
1547
1554
|
const button = modal.querySelector('.file-content-copy');
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1555
|
+
if (button) {
|
|
1556
|
+
const originalText = button.textContent;
|
|
1557
|
+
button.textContent = '✅ Copied!';
|
|
1558
|
+
setTimeout(() => {
|
|
1559
|
+
button.textContent = originalText;
|
|
1560
|
+
}, 2000);
|
|
1561
|
+
}
|
|
1553
1562
|
}).catch(err => {
|
|
1554
1563
|
console.error('Failed to copy text:', err);
|
|
1555
1564
|
});
|
|
@@ -1563,11 +1572,13 @@ window.copyFileContent = function() {
|
|
|
1563
1572
|
document.body.removeChild(textarea);
|
|
1564
1573
|
|
|
1565
1574
|
const button = modal.querySelector('.file-content-copy');
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1575
|
+
if (button) {
|
|
1576
|
+
const originalText = button.textContent;
|
|
1577
|
+
button.textContent = '✅ Copied!';
|
|
1578
|
+
setTimeout(() => {
|
|
1579
|
+
button.textContent = originalText;
|
|
1580
|
+
}, 2000);
|
|
1581
|
+
}
|
|
1571
1582
|
}
|
|
1572
1583
|
};
|
|
1573
1584
|
|
|
@@ -1592,25 +1603,29 @@ function displayFileContentError(modal, result) {
|
|
|
1592
1603
|
errorMessage = `⚠️ ${errorMessage}`;
|
|
1593
1604
|
}
|
|
1594
1605
|
|
|
1595
|
-
messageElement
|
|
1606
|
+
if (messageElement) {
|
|
1607
|
+
messageElement.textContent = errorMessage;
|
|
1608
|
+
}
|
|
1596
1609
|
|
|
1597
1610
|
// Add suggestions if available
|
|
1598
|
-
if (
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
<
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1611
|
+
if (suggestionsElement) {
|
|
1612
|
+
if (result.suggestions && result.suggestions.length > 0) {
|
|
1613
|
+
suggestionsElement.innerHTML = `
|
|
1614
|
+
<h4>Suggestions:</h4>
|
|
1615
|
+
<ul>
|
|
1616
|
+
${result.suggestions.map(suggestion => `<li>${suggestion}</li>`).join('')}
|
|
1617
|
+
</ul>
|
|
1618
|
+
`;
|
|
1619
|
+
} else {
|
|
1620
|
+
suggestionsElement.innerHTML = `
|
|
1621
|
+
<h4>Try:</h4>
|
|
1622
|
+
<ul>
|
|
1623
|
+
<li>Check if the file exists and is readable</li>
|
|
1624
|
+
<li>Verify file permissions</li>
|
|
1625
|
+
<li>Ensure the monitoring server has access to this file</li>
|
|
1626
|
+
</ul>
|
|
1627
|
+
`;
|
|
1628
|
+
}
|
|
1614
1629
|
}
|
|
1615
1630
|
|
|
1616
1631
|
console.log('📋 Displaying file content error:', {
|
|
@@ -1619,7 +1634,9 @@ function displayFileContentError(modal, result) {
|
|
|
1619
1634
|
suggestions: result.suggestions
|
|
1620
1635
|
});
|
|
1621
1636
|
|
|
1622
|
-
errorArea
|
|
1637
|
+
if (errorArea) {
|
|
1638
|
+
errorArea.style.display = 'block';
|
|
1639
|
+
}
|
|
1623
1640
|
}
|
|
1624
1641
|
|
|
1625
1642
|
// Search Viewer Modal Functions
|
|
@@ -530,13 +530,15 @@ class SocketClient {
|
|
|
530
530
|
this.addEvent({ type: 'agent', subtype: 'executed', timestamp: new Date().toISOString(), data });
|
|
531
531
|
});
|
|
532
532
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
533
|
+
// DISABLED: Legacy hook handlers - events now come through claude_event pathway
|
|
534
|
+
// to prevent duplication. Hook events are processed by the claude_event handler above.
|
|
535
|
+
// this.socket.on('hook.pre', (data) => {
|
|
536
|
+
// this.addEvent({ type: 'hook', subtype: 'pre', timestamp: new Date().toISOString(), data });
|
|
537
|
+
// });
|
|
538
|
+
|
|
539
|
+
// this.socket.on('hook.post', (data) => {
|
|
540
|
+
// this.addEvent({ type: 'hook', subtype: 'post', timestamp: new Date().toISOString(), data });
|
|
541
|
+
// });
|
|
540
542
|
|
|
541
543
|
this.socket.on('todo.updated', (data) => {
|
|
542
544
|
this.addEvent({ type: 'todo', subtype: 'updated', timestamp: new Date().toISOString(), data });
|
|
@@ -454,12 +454,6 @@
|
|
|
454
454
|
</div>
|
|
455
455
|
</div>
|
|
456
456
|
</div>
|
|
457
|
-
<!-- Code Content Area -->
|
|
458
|
-
<div id="code-module-data-content" class="code-module-data-content">
|
|
459
|
-
<div class="module-empty">
|
|
460
|
-
<p>Select a file from the tree to view its content</p>
|
|
461
|
-
</div>
|
|
462
|
-
</div>
|
|
463
457
|
</div>
|
|
464
458
|
</div>
|
|
465
459
|
</div>
|
|
@@ -541,7 +535,8 @@
|
|
|
541
535
|
loadModule('/static/dist/dashboard.js'),
|
|
542
536
|
loadModule('/static/dist/components/activity-tree.js'),
|
|
543
537
|
loadModule('/static/js/components/code-tree.js'), // TEMPORARY: Direct source for debugging
|
|
544
|
-
loadModule('/static/dist/components/code-viewer.js')
|
|
538
|
+
loadModule('/static/dist/components/code-viewer.js'),
|
|
539
|
+
loadModule('/static/dist/components/file-viewer.js') // File viewer for viewing file contents
|
|
545
540
|
]).then(() => {
|
|
546
541
|
console.log('All dashboard modules loaded successfully');
|
|
547
542
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
This handler uses a service-oriented architecture with:
|
|
5
5
|
- StateManagerService: Manages state and delegation tracking
|
|
6
|
-
- ConnectionManagerService: Handles SocketIO
|
|
6
|
+
- ConnectionManagerService: Handles SocketIO connections with HTTP fallback
|
|
7
7
|
- SubagentResponseProcessor: Processes complex subagent responses
|
|
8
8
|
- DuplicateEventDetector: Detects and filters duplicate events
|
|
9
9
|
|
|
@@ -73,19 +73,10 @@ DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
|
73
73
|
Conditional imports with graceful fallbacks for testing and modularity.
|
|
74
74
|
|
|
75
75
|
WHY conditional imports:
|
|
76
|
-
- EventBus is optional for basic hook functionality
|
|
77
76
|
- Tests may not have full environment setup
|
|
78
77
|
- Allows hooks to work in minimal configurations
|
|
79
78
|
- Graceful degradation when dependencies unavailable
|
|
80
79
|
"""
|
|
81
|
-
# Import EventBus availability flag for backward compatibility with tests
|
|
82
|
-
try:
|
|
83
|
-
from claude_mpm.services.event_bus import EventBus
|
|
84
|
-
|
|
85
|
-
EVENTBUS_AVAILABLE = True
|
|
86
|
-
except ImportError:
|
|
87
|
-
EVENTBUS_AVAILABLE = False
|
|
88
|
-
EventBus = None
|
|
89
80
|
|
|
90
81
|
# Import get_connection_pool for backward compatibility with tests
|
|
91
82
|
try:
|
|
@@ -241,7 +232,6 @@ class ClaudeHookHandler:
|
|
|
241
232
|
|
|
242
233
|
# Backward compatibility properties for tests
|
|
243
234
|
self.connection_pool = self.connection_manager.connection_pool
|
|
244
|
-
self.event_bus = self.connection_manager.event_bus
|
|
245
235
|
|
|
246
236
|
# Expose state manager properties for backward compatibility
|
|
247
237
|
self.active_delegations = self.state_manager.active_delegations
|