claude-mpm 4.2.13__py3-none-any.whl → 4.2.14__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 (34) hide show
  1. claude_mpm/core/constants.py +65 -0
  2. claude_mpm/core/error_handler.py +625 -0
  3. claude_mpm/core/file_utils.py +770 -0
  4. claude_mpm/core/logging_utils.py +502 -0
  5. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  6. claude_mpm/dashboard/static/dist/components/file-viewer.js +1 -1
  7. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  8. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  9. claude_mpm/dashboard/static/js/components/code-simple.js +44 -1
  10. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +353 -0
  11. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +235 -0
  12. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +409 -0
  13. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +435 -0
  14. claude_mpm/dashboard/static/js/components/code-tree.js +29 -5
  15. claude_mpm/dashboard/static/js/components/file-viewer.js +69 -27
  16. claude_mpm/dashboard/static/js/components/session-manager.js +1 -1
  17. claude_mpm/dashboard/static/js/components/working-directory.js +18 -9
  18. claude_mpm/dashboard/static/js/dashboard.js +55 -9
  19. claude_mpm/dashboard/static/js/shared/dom-helpers.js +396 -0
  20. claude_mpm/dashboard/static/js/shared/event-bus.js +330 -0
  21. claude_mpm/dashboard/static/js/shared/logger.js +385 -0
  22. claude_mpm/dashboard/static/js/shared/tooltip-service.js +253 -0
  23. claude_mpm/dashboard/static/js/socket-client.js +18 -0
  24. claude_mpm/dashboard/templates/index.html +21 -8
  25. claude_mpm/services/monitor/handlers/__init__.py +2 -1
  26. claude_mpm/services/monitor/handlers/file.py +263 -0
  27. claude_mpm/services/monitor/server.py +81 -1
  28. claude_mpm/services/socketio/handlers/file.py +40 -5
  29. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/METADATA +1 -1
  30. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/RECORD +34 -22
  31. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/WHEEL +0 -0
  32. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/entry_points.txt +0 -0
  33. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/licenses/LICENSE +0 -0
  34. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,435 @@
1
+ /**
2
+ * Tree Utility Functions
3
+ *
4
+ * Common utility functions extracted from code-tree.js
5
+ * Provides formatting, file type detection, and other helper functions.
6
+ *
7
+ * @module tree-utils
8
+ */
9
+
10
+ const treeUtils = {
11
+ /**
12
+ * Get complexity level from numeric complexity
13
+ * @param {number} complexity - Complexity value
14
+ * @returns {string} Complexity level (low/medium/high)
15
+ */
16
+ getComplexityLevel(complexity) {
17
+ if (complexity <= 5) return 'low';
18
+ if (complexity <= 10) return 'medium';
19
+ return 'high';
20
+ },
21
+
22
+ /**
23
+ * Format file size for display
24
+ * @param {number} bytes - Size in bytes
25
+ * @returns {string} Formatted size string
26
+ */
27
+ formatFileSize(bytes) {
28
+ if (bytes === 0) return '0 B';
29
+ const k = 1024;
30
+ const sizes = ['B', 'KB', 'MB', 'GB'];
31
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
32
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
33
+ },
34
+
35
+ /**
36
+ * Get file extension from path
37
+ * @param {string} filePath - File path
38
+ * @returns {string} File extension (lowercase)
39
+ */
40
+ getFileExtension(filePath) {
41
+ if (!filePath) return '';
42
+ const parts = filePath.split('.');
43
+ return parts.length > 1 ? parts.pop().toLowerCase() : '';
44
+ },
45
+
46
+ /**
47
+ * Get descriptive file type for user messages
48
+ * @param {string} fileName - File name
49
+ * @returns {string} Human-readable file type description
50
+ */
51
+ getFileTypeDescription(fileName) {
52
+ if (!fileName) return 'File';
53
+
54
+ const ext = this.getFileExtension(fileName);
55
+ const baseName = fileName.toLowerCase();
56
+
57
+ // Special cases
58
+ if (baseName.endsWith('__init__.py')) {
59
+ return 'Python package initialization';
60
+ }
61
+ if (baseName === 'makefile') {
62
+ return 'Build configuration';
63
+ }
64
+ if (baseName.includes('config') || baseName.includes('settings')) {
65
+ return 'Configuration file';
66
+ }
67
+ if (baseName.includes('test') || baseName.includes('spec')) {
68
+ return 'Test file';
69
+ }
70
+
71
+ // By extension
72
+ const typeMap = {
73
+ 'py': 'Python file',
74
+ 'js': 'JavaScript file',
75
+ 'ts': 'TypeScript file',
76
+ 'jsx': 'React component',
77
+ 'tsx': 'React TypeScript component',
78
+ 'html': 'HTML document',
79
+ 'css': 'Stylesheet',
80
+ 'scss': 'SCSS stylesheet',
81
+ 'sass': 'Sass stylesheet',
82
+ 'less': 'LESS stylesheet',
83
+ 'json': 'JSON data',
84
+ 'md': 'Markdown document',
85
+ 'txt': 'Text file',
86
+ 'yml': 'YAML configuration',
87
+ 'yaml': 'YAML configuration',
88
+ 'xml': 'XML document',
89
+ 'sql': 'SQL script',
90
+ 'sh': 'Shell script',
91
+ 'bash': 'Bash script',
92
+ 'toml': 'TOML configuration',
93
+ 'ini': 'INI configuration',
94
+ 'env': 'Environment variables',
95
+ 'dockerfile': 'Docker configuration',
96
+ 'makefile': 'Build configuration',
97
+ 'gitignore': 'Git ignore rules',
98
+ 'readme': 'Documentation'
99
+ };
100
+
101
+ return typeMap[ext] || typeMap[baseName] || 'File';
102
+ },
103
+
104
+ /**
105
+ * Get file icon based on file type
106
+ * @param {string} filePath - File path
107
+ * @returns {string} Unicode emoji icon
108
+ */
109
+ getFileIcon(filePath) {
110
+ if (!filePath) return '📄';
111
+
112
+ const ext = this.getFileExtension(filePath);
113
+ const fileName = filePath.toLowerCase().split('/').pop();
114
+
115
+ const iconMap = {
116
+ // Programming languages
117
+ 'py': '🐍',
118
+ 'js': '📜',
119
+ 'ts': '📘',
120
+ 'jsx': '⚛️',
121
+ 'tsx': '⚛️',
122
+ 'java': '☕',
123
+ 'c': '🔷',
124
+ 'cpp': '🔷',
125
+ 'cs': '🔷',
126
+ 'go': '🐹',
127
+ 'rs': '🦀',
128
+ 'rb': '💎',
129
+ 'php': '🐘',
130
+ 'swift': '🦉',
131
+ 'kt': '🎯',
132
+
133
+ // Web files
134
+ 'html': '🌐',
135
+ 'css': '🎨',
136
+ 'scss': '🎨',
137
+ 'sass': '🎨',
138
+ 'less': '🎨',
139
+
140
+ // Data files
141
+ 'json': '📋',
142
+ 'xml': '📰',
143
+ 'csv': '📊',
144
+ 'sql': '🗃️',
145
+
146
+ // Documentation
147
+ 'md': '📝',
148
+ 'txt': '📄',
149
+ 'pdf': '📑',
150
+ 'doc': '📃',
151
+ 'docx': '📃',
152
+
153
+ // Configuration
154
+ 'yml': '⚙️',
155
+ 'yaml': '⚙️',
156
+ 'toml': '⚙️',
157
+ 'ini': '⚙️',
158
+ 'env': '🔐',
159
+
160
+ // Scripts
161
+ 'sh': '🐚',
162
+ 'bash': '🐚',
163
+ 'bat': '🖥️',
164
+ 'ps1': '🖥️',
165
+
166
+ // Special files
167
+ 'dockerfile': '🐳',
168
+ 'docker-compose.yml': '🐳',
169
+ 'makefile': '🔨',
170
+ 'package.json': '📦',
171
+ 'requirements.txt': '📦',
172
+ 'gitignore': '🚫',
173
+ '.gitignore': '🚫',
174
+ 'readme': '📖',
175
+ 'readme.md': '📖',
176
+ 'license': '📜',
177
+ 'license.md': '📜'
178
+ };
179
+
180
+ // Check full filename first, then extension
181
+ return iconMap[fileName] || iconMap[ext] || '📄';
182
+ },
183
+
184
+ /**
185
+ * Count different types of AST elements
186
+ * @param {Array} elements - AST elements
187
+ * @returns {Object} Element counts
188
+ */
189
+ getElementCounts(elements) {
190
+ const counts = {
191
+ classes: 0,
192
+ functions: 0,
193
+ methods: 0,
194
+ total: elements.length
195
+ };
196
+
197
+ elements.forEach(elem => {
198
+ if (elem.type === 'class') {
199
+ counts.classes++;
200
+ if (elem.methods) {
201
+ counts.methods += elem.methods.length;
202
+ }
203
+ } else if (elem.type === 'function') {
204
+ counts.functions++;
205
+ }
206
+ });
207
+
208
+ return counts;
209
+ },
210
+
211
+ /**
212
+ * Format element counts into a readable summary
213
+ * @param {Object} counts - Element counts
214
+ * @returns {string} Formatted summary
215
+ */
216
+ formatElementSummary(counts) {
217
+ const parts = [];
218
+
219
+ if (counts.classes > 0) {
220
+ parts.push(`${counts.classes} class${counts.classes !== 1 ? 'es' : ''}`);
221
+ }
222
+ if (counts.functions > 0) {
223
+ parts.push(`${counts.functions} function${counts.functions !== 1 ? 's' : ''}`);
224
+ }
225
+ if (counts.methods > 0) {
226
+ parts.push(`${counts.methods} method${counts.methods !== 1 ? 's' : ''}`);
227
+ }
228
+
229
+ if (parts.length === 0) {
230
+ return 'Structural elements for tree view';
231
+ } else if (parts.length === 1) {
232
+ return parts[0] + ' found';
233
+ } else if (parts.length === 2) {
234
+ return parts.join(' and ') + ' found';
235
+ } else {
236
+ return parts.slice(0, -1).join(', ') + ', and ' + parts[parts.length - 1] + ' found';
237
+ }
238
+ },
239
+
240
+ /**
241
+ * Get node type from data
242
+ * @param {Object} node - Node data
243
+ * @returns {string} Node type
244
+ */
245
+ getNodeType(node) {
246
+ if (!node) return 'unknown';
247
+ if (node.type) return node.type;
248
+ if (node.children && node.children.length > 0) return 'directory';
249
+ if (node.path) return 'file';
250
+ return 'unknown';
251
+ },
252
+
253
+ /**
254
+ * Check if node is a directory
255
+ * @param {Object} node - Node or node data
256
+ * @returns {boolean} True if directory
257
+ */
258
+ isNodeDirectory(node) {
259
+ if (!node) return false;
260
+
261
+ // Handle D3 node structure
262
+ if (node.data) {
263
+ node = node.data;
264
+ }
265
+
266
+ return node.type === 'directory' ||
267
+ node.type === 'folder' ||
268
+ (node.children && node.children.length > 0) ||
269
+ node.isDirectory === true;
270
+ },
271
+
272
+ /**
273
+ * Get node icon for tree visualization
274
+ * @param {string} type - Node type
275
+ * @returns {string} Unicode icon
276
+ */
277
+ getNodeIcon(type) {
278
+ const icons = {
279
+ 'module': '📦',
280
+ 'class': '🏗️',
281
+ 'function': '⚡',
282
+ 'method': '🔧',
283
+ 'property': '📌',
284
+ 'directory': '📁',
285
+ 'folder': '📁',
286
+ 'file': '📄',
287
+ 'python': '🐍',
288
+ 'javascript': '📜',
289
+ 'unknown': '❓'
290
+ };
291
+ return icons[type] || icons['unknown'];
292
+ },
293
+
294
+ /**
295
+ * Sort nodes for tree display
296
+ * @param {Array} nodes - Array of nodes
297
+ * @returns {Array} Sorted nodes
298
+ */
299
+ sortNodes(nodes) {
300
+ return nodes.sort((a, b) => {
301
+ // Directories first
302
+ const aIsDir = this.isNodeDirectory(a);
303
+ const bIsDir = this.isNodeDirectory(b);
304
+
305
+ if (aIsDir && !bIsDir) return -1;
306
+ if (!aIsDir && bIsDir) return 1;
307
+
308
+ // Then alphabetically
309
+ const aName = (a.name || a.data?.name || '').toLowerCase();
310
+ const bName = (b.name || b.data?.name || '').toLowerCase();
311
+
312
+ return aName.localeCompare(bName);
313
+ });
314
+ },
315
+
316
+ /**
317
+ * Filter nodes based on criteria
318
+ * @param {Array} nodes - Array of nodes
319
+ * @param {Object} criteria - Filter criteria
320
+ * @returns {Array} Filtered nodes
321
+ */
322
+ filterNodes(nodes, criteria = {}) {
323
+ return nodes.filter(node => {
324
+ // Language filter
325
+ if (criteria.language && node.language !== criteria.language) {
326
+ return false;
327
+ }
328
+
329
+ // Search term filter
330
+ if (criteria.searchTerm) {
331
+ const term = criteria.searchTerm.toLowerCase();
332
+ const name = (node.name || '').toLowerCase();
333
+ const path = (node.path || '').toLowerCase();
334
+
335
+ if (!name.includes(term) && !path.includes(term)) {
336
+ return false;
337
+ }
338
+ }
339
+
340
+ // Complexity filter
341
+ if (criteria.minComplexity && node.complexity < criteria.minComplexity) {
342
+ return false;
343
+ }
344
+ if (criteria.maxComplexity && node.complexity > criteria.maxComplexity) {
345
+ return false;
346
+ }
347
+
348
+ return true;
349
+ });
350
+ },
351
+
352
+ /**
353
+ * Calculate tree statistics
354
+ * @param {Object} node - Root node
355
+ * @returns {Object} Tree statistics
356
+ */
357
+ calculateTreeStats(node) {
358
+ const stats = {
359
+ files: 0,
360
+ directories: 0,
361
+ classes: 0,
362
+ functions: 0,
363
+ methods: 0,
364
+ lines: 0,
365
+ maxDepth: 0
366
+ };
367
+
368
+ const traverse = (n, depth = 0) => {
369
+ stats.maxDepth = Math.max(stats.maxDepth, depth);
370
+
371
+ if (this.isNodeDirectory(n)) {
372
+ stats.directories++;
373
+ } else {
374
+ stats.files++;
375
+ }
376
+
377
+ if (n.type === 'class') stats.classes++;
378
+ if (n.type === 'function') stats.functions++;
379
+ if (n.type === 'method') stats.methods++;
380
+ if (n.lines) stats.lines += n.lines;
381
+
382
+ if (n.children) {
383
+ n.children.forEach(child => traverse(child, depth + 1));
384
+ }
385
+ };
386
+
387
+ traverse(node);
388
+ return stats;
389
+ },
390
+
391
+ /**
392
+ * Get color based on complexity
393
+ * @param {number} complexity - Complexity value
394
+ * @returns {string} Color hex code
395
+ */
396
+ getComplexityColor(complexity) {
397
+ if (!complexity || complexity <= 5) {
398
+ return '#52c41a'; // Green - low complexity
399
+ } else if (complexity <= 10) {
400
+ return '#faad14'; // Orange - medium complexity
401
+ } else {
402
+ return '#f5222d'; // Red - high complexity
403
+ }
404
+ },
405
+
406
+ /**
407
+ * Format node path for display
408
+ * @param {Object} node - Node object
409
+ * @returns {string} Formatted path
410
+ */
411
+ formatNodePath(node) {
412
+ const parts = [];
413
+ let current = node;
414
+
415
+ while (current) {
416
+ if (current.data?.name) {
417
+ parts.unshift(current.data.name);
418
+ } else if (current.name) {
419
+ parts.unshift(current.name);
420
+ }
421
+ current = current.parent;
422
+ }
423
+
424
+ return parts.join(' / ');
425
+ }
426
+ };
427
+
428
+ // Support both module and global usage
429
+ if (typeof module !== 'undefined' && module.exports) {
430
+ module.exports = treeUtils;
431
+ } else if (typeof define === 'function' && define.amd) {
432
+ define([], () => treeUtils);
433
+ } else {
434
+ window.treeUtils = treeUtils;
435
+ }
@@ -13,10 +13,34 @@
13
13
  *
14
14
  * Version: 2025-08-29T15:30:00Z - ALL CENTERING REMOVED
15
15
  * Last Update: Completely disabled tree centering/movement on node clicks
16
+ *
17
+ * REFACTORED: 2025-09-05 - Extracted utilities, constants, search, and breadcrumb modules
16
18
  */
17
19
 
20
+ // Load dependencies - these should be loaded in the HTML before this file
21
+ // Shared services
22
+ // <script src="/static/js/shared/tooltip-service.js"></script>
23
+ // <script src="/static/js/shared/dom-helpers.js"></script>
24
+ // <script src="/static/js/shared/event-bus.js"></script>
25
+ // <script src="/static/js/shared/logger.js"></script>
26
+ // Tree modules
27
+ // <script src="/static/js/components/code-tree/tree-utils.js"></script>
28
+ // <script src="/static/js/components/code-tree/tree-constants.js"></script>
29
+ // <script src="/static/js/components/code-tree/tree-search.js"></script>
30
+ // <script src="/static/js/components/code-tree/tree-breadcrumb.js"></script>
31
+
18
32
  class CodeTree {
19
33
  constructor() {
34
+ // Initialize services
35
+ this.tooltipService = window.tooltipService || null;
36
+ this.domHelpers = window.domHelpers || null;
37
+ this.eventBus = window.eventBus || null;
38
+ this.logger = window.logger ? window.logger.createComponentLogger('CodeTree') : console;
39
+ this.treeUtils = window.treeUtils || null;
40
+ this.treeConstants = window.treeConstants || {};
41
+ this.treeSearch = window.treeSearch || null;
42
+ this.treeBreadcrumb = window.treeBreadcrumb || null;
43
+
20
44
  this.container = null;
21
45
  this.svg = null;
22
46
  this.treeData = null;
@@ -31,14 +55,14 @@ class CodeTree {
31
55
  methods: 0,
32
56
  lines: 0
33
57
  };
34
- // Radial layout settings
58
+ // Radial layout settings - use constants if available
35
59
  this.isRadialLayout = false; // Toggle for radial vs linear layout - defaulting to linear for better readability
36
- this.margin = {top: 20, right: 20, bottom: 20, left: 20};
37
- this.width = 960 - this.margin.left - this.margin.right;
38
- this.height = 600 - this.margin.top - this.margin.bottom;
60
+ this.margin = this.treeConstants.DEFAULT_MARGIN || {top: 20, right: 20, bottom: 20, left: 20};
61
+ this.width = (this.treeConstants.DEFAULT_WIDTH || 960) - this.margin.left - this.margin.right;
62
+ this.height = (this.treeConstants.DEFAULT_HEIGHT || 600) - this.margin.top - this.margin.bottom;
39
63
  this.radius = Math.min(this.width, this.height) / 2;
40
64
  this.nodeId = 0;
41
- this.duration = 750;
65
+ this.duration = this.treeConstants.ANIMATION_DURATION || 750;
42
66
  this.languageFilter = 'all';
43
67
  this.searchTerm = '';
44
68
  this.tooltip = null;
@@ -248,7 +248,11 @@ class FileViewer {
248
248
  * Show the file viewer with file content
249
249
  */
250
250
  async show(filePath) {
251
+ console.log('[FileViewer] show() called with path:', filePath);
252
+ console.log('[FileViewer] initialized:', this.initialized);
253
+
251
254
  if (!this.initialized) {
255
+ console.log('[FileViewer] Not initialized, initializing now...');
252
256
  this.initialize();
253
257
  }
254
258
 
@@ -258,6 +262,7 @@ class FileViewer {
258
262
  // Update path
259
263
  document.getElementById('file-viewer-path').textContent = filePath;
260
264
 
265
+ console.log('[FileViewer] Modal shown, loading file content...');
261
266
  // Load file content
262
267
  await this.loadFileContent(filePath);
263
268
  }
@@ -276,8 +281,11 @@ class FileViewer {
276
281
  async loadFileContent(filePath) {
277
282
  const codeContent = document.getElementById('file-viewer-code-content');
278
283
 
284
+ console.log('[FileViewer] loadFileContent called with path:', filePath);
285
+
279
286
  // Check cache first
280
287
  if (this.contentCache.has(filePath)) {
288
+ console.log('[FileViewer] Using cached content for:', filePath);
281
289
  this.displayContent(this.contentCache.get(filePath));
282
290
  return;
283
291
  }
@@ -286,35 +294,62 @@ class FileViewer {
286
294
  codeContent.textContent = 'Loading file content...';
287
295
 
288
296
  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);
297
+ // Check if we have a socket connection available
298
+ if (window.socket && window.socket.connected) {
299
+ console.log('[FileViewer] Using Socket.IO to load file:', filePath);
300
+
301
+ // Create a promise to wait for the response
302
+ const responsePromise = new Promise((resolve, reject) => {
303
+ const timeoutId = setTimeout(() => {
304
+ console.error('[FileViewer] Socket.IO request timed out for:', filePath);
305
+ reject(new Error('Socket.IO request timed out'));
306
+ }, 10000); // 10 second timeout
307
+
308
+ // Set up one-time listener for the response
309
+ window.socket.once('file_content_response', (data) => {
310
+ clearTimeout(timeoutId);
311
+ console.log('[FileViewer] Received file_content_response:', data);
312
+ resolve(data);
313
+ });
314
+
315
+ // Emit the read_file event
316
+ console.log('[FileViewer] Emitting read_file event with data:', {
317
+ file_path: filePath,
318
+ working_dir: window.workingDirectory || '/',
319
+ max_size: 5 * 1024 * 1024 // 5MB limit
320
+ });
321
+
322
+ window.socket.emit('read_file', {
323
+ file_path: filePath,
324
+ working_dir: window.workingDirectory || '/',
325
+ max_size: 5 * 1024 * 1024 // 5MB limit
326
+ });
327
+ });
307
328
 
308
- // Display the content
309
- this.displayContent(data.content);
329
+ // Wait for the response
330
+ const data = await responsePromise;
310
331
 
311
- // Update file info
312
- this.updateFileInfo(data);
332
+ if (data.success && data.content !== undefined) {
333
+ console.log('[FileViewer] Successfully loaded file content, caching...');
334
+ // Cache the content
335
+ this.contentCache.set(filePath, data.content);
336
+
337
+ // Display the content
338
+ this.displayContent(data.content);
339
+
340
+ // Update file info
341
+ this.updateFileInfo(data);
342
+ } else {
343
+ console.error('[FileViewer] Server returned error:', data.error);
344
+ throw new Error(data.error || 'Failed to load file content');
345
+ }
313
346
  } else {
314
- throw new Error(data.error || 'Failed to load file content');
347
+ console.error('[FileViewer] No Socket.IO connection available');
348
+ throw new Error('No socket connection available. Please ensure the dashboard is connected to the monitoring server.');
315
349
  }
316
350
  } catch (error) {
317
- console.error('Error loading file:', error);
351
+ console.error('[FileViewer] Error loading file:', error);
352
+ console.error('[FileViewer] Error stack:', error.stack);
318
353
 
319
354
  // If API fails, show error message with helpful information
320
355
  this.displayError(filePath, error.message);
@@ -516,9 +551,16 @@ class FileViewer {
516
551
  const fileViewer = new FileViewer();
517
552
 
518
553
  // Create global function for easy access
519
- window.showFileViewerModal = (filePath) => {
520
- fileViewer.show(filePath);
521
- };
554
+ // Only set if not already defined by dashboard.js
555
+ if (!window.showFileViewerModal) {
556
+ window.showFileViewerModal = (filePath) => {
557
+ console.log('[FileViewer] showFileViewerModal called with path:', filePath);
558
+ fileViewer.show(filePath);
559
+ };
560
+ }
561
+
562
+ // Expose the singleton for debugging
563
+ window.fileViewerInstance = fileViewer;
522
564
 
523
565
  // Export for use in other modules
524
566
  if (typeof window !== 'undefined') {
@@ -93,7 +93,7 @@ class SessionManager {
93
93
  const currentSelection = sessionSelect.value;
94
94
 
95
95
  // Get the default working directory from various sources
96
- let defaultWorkingDir = '/Users/masa/Projects/claude-mpm';
96
+ let defaultWorkingDir = '/';
97
97
 
98
98
  // Try to get from working directory manager
99
99
  if (window.dashboard && window.dashboard.workingDirectoryManager) {
@@ -355,12 +355,21 @@ class WorkingDirectoryManager {
355
355
  // Fallback to a reasonable default - try to get the current project directory
356
356
  // This should be set when the dashboard initializes
357
357
 
358
- // Try getting from the browser's URL or any other hint about the current project
359
- if (window.location.pathname.includes('claude-mpm')) {
360
- // We can infer we're in a claude-mpm project
361
- const cwdFallback = '/Users/masa/Projects/claude-mpm';
362
- console.log('[WORKING-DIR-DEBUG] Using inferred project path as fallback:', cwdFallback);
363
- return cwdFallback;
358
+ // Try getting from events that have a working directory
359
+ if (window.socketClient && window.socketClient.events) {
360
+ // Look for the most recent event with a working directory
361
+ const eventsWithDir = window.socketClient.events
362
+ .filter(e => e.data && (e.data.working_directory || e.data.cwd || e.data.working_dir))
363
+ .reverse();
364
+
365
+ if (eventsWithDir.length > 0) {
366
+ const recentEvent = eventsWithDir[0];
367
+ const dir = recentEvent.data.working_directory ||
368
+ recentEvent.data.cwd ||
369
+ recentEvent.data.working_dir;
370
+ console.log('[WORKING-DIR-DEBUG] Using working directory from recent event:', dir);
371
+ return dir;
372
+ }
364
373
  }
365
374
  const workingDirPath = document.getElementById('working-dir-path');
366
375
  if (workingDirPath?.textContent?.trim()) {
@@ -372,9 +381,9 @@ class WorkingDirectoryManager {
372
381
  }
373
382
  }
374
383
 
375
- // Final fallback to current directory indicator
376
- const fallback = process?.cwd?.() || '/Users/masa/Projects/claude-mpm';
377
- console.log('[WORKING-DIR-DEBUG] Using hard-coded fallback directory:', this.repr(fallback));
384
+ // Final fallback to a generic path
385
+ const fallback = window.location.hostname === 'localhost' ? '/' : '/';
386
+ console.log('[WORKING-DIR-DEBUG] Using generic fallback directory:', this.repr(fallback));
378
387
  return fallback;
379
388
  }
380
389