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.
Files changed (50) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/commands/dashboard.py +59 -126
  3. claude_mpm/cli/commands/monitor.py +71 -212
  4. claude_mpm/cli/commands/run.py +33 -33
  5. claude_mpm/dashboard/static/css/code-tree.css +8 -16
  6. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  7. claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
  8. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  9. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
  10. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  11. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  12. claude_mpm/dashboard/static/js/components/code-tree.js +692 -114
  13. claude_mpm/dashboard/static/js/components/file-viewer.js +538 -0
  14. claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
  15. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +166 -14
  16. claude_mpm/dashboard/static/js/dashboard.js +108 -91
  17. claude_mpm/dashboard/static/js/socket-client.js +9 -7
  18. claude_mpm/dashboard/templates/index.html +2 -7
  19. claude_mpm/hooks/claude_hooks/hook_handler.py +1 -11
  20. claude_mpm/hooks/claude_hooks/services/connection_manager.py +54 -59
  21. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +112 -72
  22. claude_mpm/services/agents/deployment/agent_template_builder.py +0 -1
  23. claude_mpm/services/cli/unified_dashboard_manager.py +354 -0
  24. claude_mpm/services/monitor/__init__.py +20 -0
  25. claude_mpm/services/monitor/daemon.py +256 -0
  26. claude_mpm/services/monitor/event_emitter.py +279 -0
  27. claude_mpm/services/monitor/handlers/__init__.py +20 -0
  28. claude_mpm/services/monitor/handlers/code_analysis.py +334 -0
  29. claude_mpm/services/monitor/handlers/dashboard.py +298 -0
  30. claude_mpm/services/monitor/handlers/hooks.py +491 -0
  31. claude_mpm/services/monitor/management/__init__.py +18 -0
  32. claude_mpm/services/monitor/management/health.py +124 -0
  33. claude_mpm/services/monitor/management/lifecycle.py +298 -0
  34. claude_mpm/services/monitor/server.py +442 -0
  35. claude_mpm/tools/code_tree_analyzer.py +33 -17
  36. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/METADATA +1 -1
  37. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/RECORD +41 -36
  38. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  39. claude_mpm/scripts/socketio_daemon.py +0 -571
  40. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  41. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  42. claude_mpm/scripts/socketio_server_manager.py +0 -349
  43. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  44. claude_mpm/services/cli/socketio_manager.py +0 -595
  45. claude_mpm/services/dashboard/stable_server.py +0 -1020
  46. claude_mpm/services/socketio/monitor_server.py +0 -505
  47. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/WHEEL +0 -0
  48. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/entry_points.txt +0 -0
  49. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/licenses/LICENSE +0 -0
  50. {claude_mpm-4.2.9.dist-info → claude_mpm-4.2.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,538 @@
1
+ /**
2
+ * File Viewer Component
3
+ *
4
+ * A simple file content viewer that displays file contents in a modal window.
5
+ * This component handles file loading via HTTP requests and displays the content
6
+ * with basic syntax highlighting support.
7
+ */
8
+
9
+ class FileViewer {
10
+ constructor() {
11
+ this.modal = null;
12
+ this.currentFile = null;
13
+ this.initialized = false;
14
+ this.contentCache = new Map();
15
+ }
16
+
17
+ /**
18
+ * Initialize the file viewer
19
+ */
20
+ initialize() {
21
+ if (this.initialized) {
22
+ return;
23
+ }
24
+
25
+ this.createModal();
26
+ this.setupEventHandlers();
27
+
28
+ this.initialized = true;
29
+ console.log('File viewer initialized');
30
+ }
31
+
32
+ /**
33
+ * Create modal DOM structure
34
+ */
35
+ createModal() {
36
+ const modalHtml = `
37
+ <div class="file-viewer-modal" id="file-viewer-modal">
38
+ <div class="file-viewer-content">
39
+ <div class="file-viewer-header">
40
+ <h2>📄 File Viewer</h2>
41
+ <button class="file-viewer-close" id="file-viewer-close">×</button>
42
+ </div>
43
+ <div class="file-viewer-path" id="file-viewer-path">
44
+ Loading...
45
+ </div>
46
+ <div class="file-viewer-body">
47
+ <pre class="file-viewer-code" id="file-viewer-code">
48
+ <code id="file-viewer-code-content">Loading file content...</code>
49
+ </pre>
50
+ </div>
51
+ <div class="file-viewer-footer">
52
+ <div class="file-viewer-info">
53
+ <span id="file-viewer-type">Type: --</span>
54
+ <span id="file-viewer-lines">Lines: --</span>
55
+ <span id="file-viewer-size">Size: --</span>
56
+ </div>
57
+ <button class="file-viewer-copy" id="file-viewer-copy">📋 Copy</button>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ `;
62
+
63
+ // Add modal to body
64
+ document.body.insertAdjacentHTML('beforeend', modalHtml);
65
+ this.modal = document.getElementById('file-viewer-modal');
66
+
67
+ // Add styles if not already present
68
+ if (!document.getElementById('file-viewer-styles')) {
69
+ const styles = `
70
+ <style id="file-viewer-styles">
71
+ .file-viewer-modal {
72
+ display: none;
73
+ position: fixed;
74
+ top: 0;
75
+ left: 0;
76
+ width: 100%;
77
+ height: 100%;
78
+ background: rgba(0, 0, 0, 0.7);
79
+ z-index: 10000;
80
+ animation: fadeIn 0.2s;
81
+ }
82
+
83
+ .file-viewer-modal.show {
84
+ display: flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ }
88
+
89
+ .file-viewer-content {
90
+ background: #1e1e1e;
91
+ border-radius: 8px;
92
+ width: 90%;
93
+ max-width: 1200px;
94
+ height: 80%;
95
+ display: flex;
96
+ flex-direction: column;
97
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
98
+ }
99
+
100
+ .file-viewer-header {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ padding: 15px 20px;
105
+ background: #2d2d30;
106
+ border-radius: 8px 8px 0 0;
107
+ border-bottom: 1px solid #3e3e42;
108
+ }
109
+
110
+ .file-viewer-header h2 {
111
+ margin: 0;
112
+ color: #cccccc;
113
+ font-size: 18px;
114
+ }
115
+
116
+ .file-viewer-close {
117
+ background: none;
118
+ border: none;
119
+ color: #999;
120
+ font-size: 24px;
121
+ cursor: pointer;
122
+ padding: 0;
123
+ width: 30px;
124
+ height: 30px;
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ }
129
+
130
+ .file-viewer-close:hover {
131
+ color: #fff;
132
+ }
133
+
134
+ .file-viewer-path {
135
+ padding: 10px 20px;
136
+ background: #252526;
137
+ color: #8b8b8b;
138
+ font-family: 'Consolas', 'Monaco', monospace;
139
+ font-size: 12px;
140
+ border-bottom: 1px solid #3e3e42;
141
+ word-break: break-all;
142
+ }
143
+
144
+ .file-viewer-body {
145
+ flex: 1;
146
+ overflow: auto;
147
+ padding: 20px;
148
+ background: #1e1e1e;
149
+ }
150
+
151
+ .file-viewer-code {
152
+ margin: 0;
153
+ padding: 0;
154
+ background: transparent;
155
+ overflow: visible;
156
+ }
157
+
158
+ .file-viewer-code code {
159
+ font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
160
+ font-size: 13px;
161
+ line-height: 1.5;
162
+ color: #d4d4d4;
163
+ white-space: pre;
164
+ display: block;
165
+ }
166
+
167
+ .file-viewer-footer {
168
+ padding: 15px 20px;
169
+ background: #2d2d30;
170
+ border-top: 1px solid #3e3e42;
171
+ display: flex;
172
+ justify-content: space-between;
173
+ align-items: center;
174
+ border-radius: 0 0 8px 8px;
175
+ }
176
+
177
+ .file-viewer-info {
178
+ display: flex;
179
+ gap: 20px;
180
+ color: #8b8b8b;
181
+ font-size: 12px;
182
+ }
183
+
184
+ .file-viewer-copy {
185
+ background: #0e639c;
186
+ color: white;
187
+ border: none;
188
+ padding: 6px 12px;
189
+ border-radius: 4px;
190
+ cursor: pointer;
191
+ font-size: 12px;
192
+ }
193
+
194
+ .file-viewer-copy:hover {
195
+ background: #1177bb;
196
+ }
197
+
198
+ .file-viewer-copy.copied {
199
+ background: #4ec9b0;
200
+ }
201
+
202
+ .file-viewer-error {
203
+ color: #f48771;
204
+ padding: 20px;
205
+ text-align: center;
206
+ }
207
+
208
+ @keyframes fadeIn {
209
+ from { opacity: 0; }
210
+ to { opacity: 1; }
211
+ }
212
+ </style>
213
+ `;
214
+ document.head.insertAdjacentHTML('beforeend', styles);
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Setup event handlers
220
+ */
221
+ setupEventHandlers() {
222
+ // Close button
223
+ document.getElementById('file-viewer-close').addEventListener('click', () => {
224
+ this.hide();
225
+ });
226
+
227
+ // Close on backdrop click
228
+ this.modal.addEventListener('click', (e) => {
229
+ if (e.target === this.modal) {
230
+ this.hide();
231
+ }
232
+ });
233
+
234
+ // Close on ESC key
235
+ document.addEventListener('keydown', (e) => {
236
+ if (e.key === 'Escape' && this.modal.classList.contains('show')) {
237
+ this.hide();
238
+ }
239
+ });
240
+
241
+ // Copy button
242
+ document.getElementById('file-viewer-copy').addEventListener('click', () => {
243
+ this.copyContent();
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Show the file viewer with file content
249
+ */
250
+ async show(filePath) {
251
+ if (!this.initialized) {
252
+ this.initialize();
253
+ }
254
+
255
+ this.currentFile = filePath;
256
+ this.modal.classList.add('show');
257
+
258
+ // Update path
259
+ document.getElementById('file-viewer-path').textContent = filePath;
260
+
261
+ // Load file content
262
+ await this.loadFileContent(filePath);
263
+ }
264
+
265
+ /**
266
+ * Hide the file viewer
267
+ */
268
+ hide() {
269
+ this.modal.classList.remove('show');
270
+ this.currentFile = null;
271
+ }
272
+
273
+ /**
274
+ * Load file content
275
+ */
276
+ async loadFileContent(filePath) {
277
+ const codeContent = document.getElementById('file-viewer-code-content');
278
+
279
+ // Check cache first
280
+ if (this.contentCache.has(filePath)) {
281
+ this.displayContent(this.contentCache.get(filePath));
282
+ return;
283
+ }
284
+
285
+ // Show loading state
286
+ codeContent.textContent = 'Loading file content...';
287
+
288
+ try {
289
+ // Try to fetch file content via API
290
+ const response = await fetch('/api/file', {
291
+ method: 'POST',
292
+ headers: {
293
+ 'Content-Type': 'application/json',
294
+ },
295
+ body: JSON.stringify({ path: filePath })
296
+ });
297
+
298
+ if (!response.ok) {
299
+ throw new Error(`HTTP error! status: ${response.status}`);
300
+ }
301
+
302
+ const data = await response.json();
303
+
304
+ if (data.success && data.content !== undefined) {
305
+ // Cache the content
306
+ this.contentCache.set(filePath, data.content);
307
+
308
+ // Display the content
309
+ this.displayContent(data.content);
310
+
311
+ // Update file info
312
+ this.updateFileInfo(data);
313
+ } else {
314
+ throw new Error(data.error || 'Failed to load file content');
315
+ }
316
+ } catch (error) {
317
+ console.error('Error loading file:', error);
318
+
319
+ // If API fails, show error message with helpful information
320
+ this.displayError(filePath, error.message);
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Display file content
326
+ */
327
+ displayContent(content) {
328
+ const codeContent = document.getElementById('file-viewer-code-content');
329
+
330
+ // Set the content
331
+ codeContent.textContent = content || '(Empty file)';
332
+
333
+ // Update line count
334
+ const lines = content ? content.split('\n').length : 0;
335
+ document.getElementById('file-viewer-lines').textContent = `Lines: ${lines}`;
336
+
337
+ // Update file size
338
+ const size = content ? new Blob([content]).size : 0;
339
+ document.getElementById('file-viewer-size').textContent = `Size: ${this.formatFileSize(size)}`;
340
+
341
+ // Detect and set file type
342
+ const fileType = this.detectFileType(this.currentFile);
343
+ document.getElementById('file-viewer-type').textContent = `Type: ${fileType}`;
344
+
345
+ // Apply syntax highlighting if Prism is available
346
+ if (window.Prism) {
347
+ const language = this.detectLanguage(this.currentFile);
348
+ codeContent.className = `language-${language}`;
349
+ Prism.highlightElement(codeContent);
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Display error message
355
+ */
356
+ displayError(filePath, errorMessage) {
357
+ const codeContent = document.getElementById('file-viewer-code-content');
358
+
359
+ // For now, show a helpful message since the API endpoint doesn't exist yet
360
+ const errorHtml = `
361
+ <div class="file-viewer-error">
362
+ ⚠️ File content loading is not yet implemented
363
+
364
+ File path: ${filePath}
365
+
366
+ The file viewing functionality requires:
367
+ 1. A server-side /api/file endpoint
368
+ 2. Proper file reading permissions
369
+ 3. Security validation for file access
370
+
371
+ Error: ${errorMessage}
372
+
373
+ This feature will be available once the backend API is implemented.
374
+ </div>
375
+ `;
376
+
377
+ codeContent.innerHTML = errorHtml;
378
+
379
+ // Update info
380
+ document.getElementById('file-viewer-lines').textContent = 'Lines: --';
381
+ document.getElementById('file-viewer-size').textContent = 'Size: --';
382
+ document.getElementById('file-viewer-type').textContent = 'Type: --';
383
+ }
384
+
385
+ /**
386
+ * Update file info
387
+ */
388
+ updateFileInfo(data) {
389
+ if (data.lines !== undefined) {
390
+ document.getElementById('file-viewer-lines').textContent = `Lines: ${data.lines}`;
391
+ }
392
+
393
+ if (data.size !== undefined) {
394
+ document.getElementById('file-viewer-size').textContent = `Size: ${this.formatFileSize(data.size)}`;
395
+ }
396
+
397
+ if (data.type) {
398
+ document.getElementById('file-viewer-type').textContent = `Type: ${data.type}`;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Format file size for display
404
+ */
405
+ formatFileSize(bytes) {
406
+ if (bytes === 0) return '0 Bytes';
407
+
408
+ const k = 1024;
409
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
410
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
411
+
412
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
413
+ }
414
+
415
+ /**
416
+ * Detect file type from path
417
+ */
418
+ detectFileType(path) {
419
+ if (!path) return 'Unknown';
420
+
421
+ const ext = path.split('.').pop()?.toLowerCase();
422
+ const typeMap = {
423
+ 'py': 'Python',
424
+ 'js': 'JavaScript',
425
+ 'ts': 'TypeScript',
426
+ 'jsx': 'React JSX',
427
+ 'tsx': 'React TSX',
428
+ 'html': 'HTML',
429
+ 'css': 'CSS',
430
+ 'json': 'JSON',
431
+ 'xml': 'XML',
432
+ 'yaml': 'YAML',
433
+ 'yml': 'YAML',
434
+ 'md': 'Markdown',
435
+ 'txt': 'Text',
436
+ 'sh': 'Shell Script',
437
+ 'bash': 'Bash Script',
438
+ 'sql': 'SQL',
439
+ 'go': 'Go',
440
+ 'rs': 'Rust',
441
+ 'java': 'Java',
442
+ 'cpp': 'C++',
443
+ 'c': 'C',
444
+ 'cs': 'C#',
445
+ 'rb': 'Ruby',
446
+ 'php': 'PHP'
447
+ };
448
+
449
+ return typeMap[ext] || 'Text';
450
+ }
451
+
452
+ /**
453
+ * Detect language for syntax highlighting
454
+ */
455
+ detectLanguage(path) {
456
+ if (!path) return 'plaintext';
457
+
458
+ const ext = path.split('.').pop()?.toLowerCase();
459
+ const languageMap = {
460
+ 'py': 'python',
461
+ 'js': 'javascript',
462
+ 'ts': 'typescript',
463
+ 'jsx': 'jsx',
464
+ 'tsx': 'tsx',
465
+ 'html': 'html',
466
+ 'css': 'css',
467
+ 'json': 'json',
468
+ 'xml': 'xml',
469
+ 'yaml': 'yaml',
470
+ 'yml': 'yaml',
471
+ 'md': 'markdown',
472
+ 'sh': 'bash',
473
+ 'bash': 'bash',
474
+ 'sql': 'sql',
475
+ 'go': 'go',
476
+ 'rs': 'rust',
477
+ 'java': 'java',
478
+ 'cpp': 'cpp',
479
+ 'c': 'c',
480
+ 'cs': 'csharp',
481
+ 'rb': 'ruby',
482
+ 'php': 'php'
483
+ };
484
+
485
+ return languageMap[ext] || 'plaintext';
486
+ }
487
+
488
+ /**
489
+ * Copy file content to clipboard
490
+ */
491
+ async copyContent() {
492
+ const codeContent = document.getElementById('file-viewer-code-content');
493
+ const button = document.getElementById('file-viewer-copy');
494
+ const content = codeContent.textContent;
495
+
496
+ try {
497
+ await navigator.clipboard.writeText(content);
498
+
499
+ // Show feedback
500
+ const originalText = button.textContent;
501
+ button.textContent = '✅ Copied!';
502
+ button.classList.add('copied');
503
+
504
+ setTimeout(() => {
505
+ button.textContent = originalText;
506
+ button.classList.remove('copied');
507
+ }, 2000);
508
+ } catch (err) {
509
+ console.error('Failed to copy:', err);
510
+ alert('Failed to copy content to clipboard');
511
+ }
512
+ }
513
+ }
514
+
515
+ // Create singleton instance
516
+ const fileViewer = new FileViewer();
517
+
518
+ // Create global function for easy access
519
+ window.showFileViewerModal = (filePath) => {
520
+ fileViewer.show(filePath);
521
+ };
522
+
523
+ // Export for use in other modules
524
+ if (typeof window !== 'undefined') {
525
+ window.FileViewer = fileViewer;
526
+
527
+ // Initialize when DOM is ready
528
+ if (document.readyState === 'loading') {
529
+ document.addEventListener('DOMContentLoaded', () => {
530
+ fileViewer.initialize();
531
+ });
532
+ } else {
533
+ // DOM is already loaded
534
+ fileViewer.initialize();
535
+ }
536
+ }
537
+
538
+ export default fileViewer;
@@ -18,11 +18,37 @@ class ModuleViewer {
18
18
  // When true, all events show JSON expanded; when false, all collapsed
19
19
  this.globalJsonExpanded = localStorage.getItem('dashboard-json-expanded') === 'true';
20
20
 
21
+ // Separate state for Full Event Data sections
22
+ this.fullEventDataExpanded = localStorage.getItem('dashboard-full-event-expanded') === 'true';
23
+
21
24
  // Track if keyboard listener has been added to avoid duplicates
22
25
  this.keyboardListenerAdded = false;
23
26
 
24
27
  // Initialize unified data viewer
25
28
  this.unifiedViewer = new UnifiedDataViewer('module-data-content');
29
+
30
+ // Sync unified viewer's JSON state with module viewer's state
31
+ // This ensures both viewers maintain consistent JSON visibility
32
+ this.unifiedViewer.globalJsonExpanded = this.globalJsonExpanded;
33
+ this.unifiedViewer.fullEventDataExpanded = this.fullEventDataExpanded;
34
+
35
+ // Listen for JSON state changes from unified viewer
36
+ document.addEventListener('jsonToggleChanged', (e) => {
37
+ this.globalJsonExpanded = e.detail.expanded;
38
+ // Also update the unified viewer's state if it's different
39
+ if (this.unifiedViewer.globalJsonExpanded !== e.detail.expanded) {
40
+ this.unifiedViewer.globalJsonExpanded = e.detail.expanded;
41
+ }
42
+ });
43
+
44
+ // Listen for Full Event Data state changes
45
+ document.addEventListener('fullEventToggleChanged', (e) => {
46
+ this.fullEventDataExpanded = e.detail.expanded;
47
+ // Also update the unified viewer's state if it's different
48
+ if (this.unifiedViewer.fullEventDataExpanded !== e.detail.expanded) {
49
+ this.unifiedViewer.fullEventDataExpanded = e.detail.expanded;
50
+ }
51
+ });
26
52
 
27
53
  this.init();
28
54
  }