claude-mpm 4.1.8__py3-none-any.whl → 4.1.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/agents/INSTRUCTIONS.md +26 -1
- claude_mpm/agents/agents_metadata.py +57 -0
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
- claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
- claude_mpm/agents/templates/agent-manager.json +263 -17
- claude_mpm/agents/templates/agentic_coder_optimizer.json +222 -0
- claude_mpm/agents/templates/code_analyzer.json +18 -8
- claude_mpm/agents/templates/engineer.json +1 -1
- claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/research.json +1 -1
- claude_mpm/cli/__init__.py +15 -0
- claude_mpm/cli/commands/__init__.py +6 -0
- claude_mpm/cli/commands/analyze.py +548 -0
- claude_mpm/cli/commands/analyze_code.py +524 -0
- claude_mpm/cli/commands/configure.py +78 -28
- claude_mpm/cli/commands/configure_tui.py +62 -60
- claude_mpm/cli/commands/dashboard.py +288 -0
- claude_mpm/cli/commands/debug.py +1386 -0
- claude_mpm/cli/commands/mpm_init.py +427 -0
- claude_mpm/cli/commands/mpm_init_handler.py +83 -0
- claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
- claude_mpm/cli/parsers/analyze_parser.py +135 -0
- claude_mpm/cli/parsers/base_parser.py +44 -0
- claude_mpm/cli/parsers/dashboard_parser.py +113 -0
- claude_mpm/cli/parsers/debug_parser.py +319 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +122 -0
- claude_mpm/constants.py +13 -1
- claude_mpm/core/framework_loader.py +148 -6
- claude_mpm/core/log_manager.py +16 -13
- claude_mpm/core/logger.py +1 -1
- claude_mpm/core/unified_agent_registry.py +1 -1
- claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
- claude_mpm/dashboard/analysis_runner.py +455 -0
- claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
- claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/built/components/code-tree.js +2 -0
- claude_mpm/dashboard/static/built/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/css/activity.css +549 -0
- claude_mpm/dashboard/static/css/code-tree.css +1175 -0
- claude_mpm/dashboard/static/css/dashboard.css +245 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
- claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
- claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/working-directory.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/activity-tree.js +1338 -0
- claude_mpm/dashboard/static/js/components/code-tree.js +2535 -0
- claude_mpm/dashboard/static/js/components/code-viewer.js +480 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +59 -9
- claude_mpm/dashboard/static/js/components/session-manager.js +40 -4
- claude_mpm/dashboard/static/js/components/socket-manager.js +12 -0
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +4 -0
- claude_mpm/dashboard/static/js/components/working-directory.js +17 -1
- claude_mpm/dashboard/static/js/dashboard.js +51 -0
- claude_mpm/dashboard/static/js/socket-client.js +465 -29
- claude_mpm/dashboard/templates/index.html +182 -4
- claude_mpm/hooks/claude_hooks/hook_handler.py +182 -5
- claude_mpm/hooks/claude_hooks/installer.py +386 -113
- claude_mpm/scripts/claude-hook-handler.sh +161 -0
- claude_mpm/scripts/socketio_daemon.py +121 -8
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +2 -2
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -2
- claude_mpm/services/agents/memory/memory_format_service.py +1 -3
- claude_mpm/services/cli/agent_cleanup_service.py +1 -5
- claude_mpm/services/cli/agent_dependency_service.py +1 -1
- claude_mpm/services/cli/agent_validation_service.py +3 -4
- claude_mpm/services/cli/dashboard_launcher.py +2 -3
- claude_mpm/services/cli/startup_checker.py +0 -11
- claude_mpm/services/core/cache_manager.py +1 -3
- claude_mpm/services/core/path_resolver.py +1 -4
- claude_mpm/services/core/service_container.py +2 -2
- claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
- claude_mpm/services/infrastructure/monitoring/__init__.py +11 -11
- claude_mpm/services/infrastructure/monitoring.py +11 -11
- claude_mpm/services/project/architecture_analyzer.py +1 -1
- claude_mpm/services/project/dependency_analyzer.py +4 -4
- claude_mpm/services/project/language_analyzer.py +3 -3
- claude_mpm/services/project/metrics_collector.py +3 -6
- claude_mpm/services/socketio/event_normalizer.py +64 -0
- claude_mpm/services/socketio/handlers/__init__.py +2 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +672 -0
- claude_mpm/services/socketio/handlers/registry.py +2 -0
- claude_mpm/services/socketio/server/connection_manager.py +6 -4
- claude_mpm/services/socketio/server/core.py +100 -11
- claude_mpm/services/socketio/server/main.py +8 -2
- claude_mpm/services/visualization/__init__.py +19 -0
- claude_mpm/services/visualization/mermaid_generator.py +938 -0
- claude_mpm/tools/__main__.py +208 -0
- claude_mpm/tools/code_tree_analyzer.py +1596 -0
- claude_mpm/tools/code_tree_builder.py +631 -0
- claude_mpm/tools/code_tree_events.py +416 -0
- claude_mpm/tools/socketio_debug.py +671 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/METADATA +2 -1
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/RECORD +110 -74
- claude_mpm/agents/schema/agent_schema.json +0 -314
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Viewer Component
|
|
3
|
+
*
|
|
4
|
+
* Modal window for displaying source code with syntax highlighting.
|
|
5
|
+
* Supports navigation between parent/child nodes and shows code metrics.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class CodeViewer {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.modal = null;
|
|
11
|
+
this.currentNode = null;
|
|
12
|
+
this.socket = null;
|
|
13
|
+
this.initialized = false;
|
|
14
|
+
this.codeCache = new Map();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the code viewer
|
|
19
|
+
*/
|
|
20
|
+
initialize() {
|
|
21
|
+
if (this.initialized) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.createModal();
|
|
26
|
+
this.setupEventHandlers();
|
|
27
|
+
this.subscribeToEvents();
|
|
28
|
+
|
|
29
|
+
this.initialized = true;
|
|
30
|
+
console.log('Code viewer initialized');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create modal DOM structure
|
|
35
|
+
*/
|
|
36
|
+
createModal() {
|
|
37
|
+
const modalHtml = `
|
|
38
|
+
<div class="code-viewer-modal" id="code-viewer-modal">
|
|
39
|
+
<div class="code-viewer-content">
|
|
40
|
+
<div class="code-viewer-header">
|
|
41
|
+
<div class="code-viewer-title" id="code-viewer-title">
|
|
42
|
+
Loading...
|
|
43
|
+
</div>
|
|
44
|
+
<div class="code-viewer-info">
|
|
45
|
+
<span id="code-viewer-type">Type: --</span>
|
|
46
|
+
<span id="code-viewer-lines">Lines: --</span>
|
|
47
|
+
<span id="code-viewer-complexity">Complexity: --</span>
|
|
48
|
+
</div>
|
|
49
|
+
<button class="code-viewer-close" id="code-viewer-close">×</button>
|
|
50
|
+
</div>
|
|
51
|
+
<div class="code-viewer-body">
|
|
52
|
+
<pre class="code-viewer-code line-numbers" id="code-viewer-code">
|
|
53
|
+
<code class="language-python" id="code-viewer-code-content"></code>
|
|
54
|
+
</pre>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="code-viewer-navigation">
|
|
57
|
+
<div class="nav-group">
|
|
58
|
+
<button class="code-nav-button" id="code-nav-parent" disabled>
|
|
59
|
+
⬆️ Parent
|
|
60
|
+
</button>
|
|
61
|
+
<button class="code-nav-button" id="code-nav-prev" disabled>
|
|
62
|
+
⬅️ Previous
|
|
63
|
+
</button>
|
|
64
|
+
<button class="code-nav-button" id="code-nav-next" disabled>
|
|
65
|
+
➡️ Next
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="nav-info">
|
|
69
|
+
<span id="code-nav-position">-- / --</span>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="nav-actions">
|
|
72
|
+
<button class="code-nav-button" id="code-copy">
|
|
73
|
+
📋 Copy
|
|
74
|
+
</button>
|
|
75
|
+
<button class="code-nav-button" id="code-open-file">
|
|
76
|
+
📂 Open File
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
// Add modal to body
|
|
85
|
+
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|
86
|
+
this.modal = document.getElementById('code-viewer-modal');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Setup event handlers
|
|
91
|
+
*/
|
|
92
|
+
setupEventHandlers() {
|
|
93
|
+
// Close button
|
|
94
|
+
document.getElementById('code-viewer-close').addEventListener('click', () => {
|
|
95
|
+
this.hide();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Close on backdrop click
|
|
99
|
+
this.modal.addEventListener('click', (e) => {
|
|
100
|
+
if (e.target === this.modal) {
|
|
101
|
+
this.hide();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Close on ESC key
|
|
106
|
+
document.addEventListener('keydown', (e) => {
|
|
107
|
+
if (e.key === 'Escape' && this.modal.classList.contains('show')) {
|
|
108
|
+
this.hide();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Navigation buttons
|
|
113
|
+
document.getElementById('code-nav-parent').addEventListener('click', () => {
|
|
114
|
+
this.navigateToParent();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
document.getElementById('code-nav-prev').addEventListener('click', () => {
|
|
118
|
+
this.navigateToPrevious();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
document.getElementById('code-nav-next').addEventListener('click', () => {
|
|
122
|
+
this.navigateToNext();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Action buttons
|
|
126
|
+
document.getElementById('code-copy').addEventListener('click', () => {
|
|
127
|
+
this.copyCode();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
document.getElementById('code-open-file').addEventListener('click', () => {
|
|
131
|
+
this.openInEditor();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Subscribe to Socket.IO events
|
|
137
|
+
*/
|
|
138
|
+
subscribeToEvents() {
|
|
139
|
+
if (window.socket) {
|
|
140
|
+
this.socket = window.socket;
|
|
141
|
+
|
|
142
|
+
// Listen for code content responses
|
|
143
|
+
this.socket.on('code:content:response', (data) => {
|
|
144
|
+
this.handleCodeContent(data);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Show the code viewer with node data
|
|
151
|
+
*/
|
|
152
|
+
show(nodeData) {
|
|
153
|
+
if (!this.initialized) {
|
|
154
|
+
this.initialize();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.currentNode = nodeData;
|
|
158
|
+
this.modal.classList.add('show');
|
|
159
|
+
|
|
160
|
+
// Update header
|
|
161
|
+
this.updateHeader(nodeData);
|
|
162
|
+
|
|
163
|
+
// Load code content
|
|
164
|
+
this.loadCode(nodeData);
|
|
165
|
+
|
|
166
|
+
// Update navigation
|
|
167
|
+
this.updateNavigation(nodeData);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Hide the code viewer
|
|
172
|
+
*/
|
|
173
|
+
hide() {
|
|
174
|
+
this.modal.classList.remove('show');
|
|
175
|
+
this.currentNode = null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Update modal header
|
|
180
|
+
*/
|
|
181
|
+
updateHeader(nodeData) {
|
|
182
|
+
// Update title
|
|
183
|
+
const title = document.getElementById('code-viewer-title');
|
|
184
|
+
title.textContent = `${nodeData.name} (${nodeData.path || 'Unknown'})`;
|
|
185
|
+
|
|
186
|
+
// Update info
|
|
187
|
+
document.getElementById('code-viewer-type').textContent = `Type: ${nodeData.type}`;
|
|
188
|
+
document.getElementById('code-viewer-lines').textContent = `Lines: ${nodeData.lines || '--'}`;
|
|
189
|
+
document.getElementById('code-viewer-complexity').textContent = `Complexity: ${nodeData.complexity || '--'}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Load code content
|
|
194
|
+
*/
|
|
195
|
+
loadCode(nodeData) {
|
|
196
|
+
const codeContent = document.getElementById('code-viewer-code-content');
|
|
197
|
+
|
|
198
|
+
// Check cache first
|
|
199
|
+
const cacheKey = `${nodeData.path}:${nodeData.line}`;
|
|
200
|
+
if (this.codeCache.has(cacheKey)) {
|
|
201
|
+
this.displayCode(this.codeCache.get(cacheKey));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Show loading state
|
|
206
|
+
codeContent.textContent = 'Loading code...';
|
|
207
|
+
|
|
208
|
+
// Request code from server
|
|
209
|
+
if (this.socket) {
|
|
210
|
+
this.socket.emit('code:content:request', {
|
|
211
|
+
path: nodeData.path,
|
|
212
|
+
line: nodeData.line,
|
|
213
|
+
type: nodeData.type,
|
|
214
|
+
name: nodeData.name
|
|
215
|
+
});
|
|
216
|
+
} else {
|
|
217
|
+
// Fallback: show mock code for demo
|
|
218
|
+
this.displayMockCode(nodeData);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Handle code content response
|
|
224
|
+
*/
|
|
225
|
+
handleCodeContent(data) {
|
|
226
|
+
if (!data.success) {
|
|
227
|
+
this.displayError(data.error || 'Failed to load code');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Cache the content
|
|
232
|
+
const cacheKey = `${data.path}:${data.line}`;
|
|
233
|
+
this.codeCache.set(cacheKey, data.content);
|
|
234
|
+
|
|
235
|
+
// Display the code
|
|
236
|
+
this.displayCode(data.content);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Display code with syntax highlighting
|
|
241
|
+
*/
|
|
242
|
+
displayCode(code) {
|
|
243
|
+
const codeContent = document.getElementById('code-viewer-code-content');
|
|
244
|
+
const codeElement = document.getElementById('code-viewer-code');
|
|
245
|
+
|
|
246
|
+
// Set the code content
|
|
247
|
+
codeContent.textContent = code;
|
|
248
|
+
|
|
249
|
+
// Update language class based on file extension
|
|
250
|
+
const language = this.detectLanguage(this.currentNode.path);
|
|
251
|
+
codeContent.className = `language-${language}`;
|
|
252
|
+
|
|
253
|
+
// Apply Prism syntax highlighting
|
|
254
|
+
if (window.Prism) {
|
|
255
|
+
Prism.highlightElement(codeContent);
|
|
256
|
+
|
|
257
|
+
// Add line numbers if plugin is available
|
|
258
|
+
if (Prism.plugins && Prism.plugins.lineNumbers) {
|
|
259
|
+
Prism.plugins.lineNumbers.resize(codeElement);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Display mock code for demo purposes
|
|
266
|
+
*/
|
|
267
|
+
displayMockCode(nodeData) {
|
|
268
|
+
let mockCode = '';
|
|
269
|
+
|
|
270
|
+
switch (nodeData.type) {
|
|
271
|
+
case 'class':
|
|
272
|
+
mockCode = `class ${nodeData.name}:
|
|
273
|
+
"""
|
|
274
|
+
${nodeData.docstring || 'A sample class implementation.'}
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
def __init__(self):
|
|
278
|
+
"""Initialize the ${nodeData.name} class."""
|
|
279
|
+
self._data = {}
|
|
280
|
+
self._initialized = False
|
|
281
|
+
|
|
282
|
+
def process(self, input_data):
|
|
283
|
+
"""Process the input data."""
|
|
284
|
+
if not self._initialized:
|
|
285
|
+
self._initialize()
|
|
286
|
+
return self._transform(input_data)
|
|
287
|
+
|
|
288
|
+
def _initialize(self):
|
|
289
|
+
"""Initialize internal state."""
|
|
290
|
+
self._initialized = True
|
|
291
|
+
|
|
292
|
+
def _transform(self, data):
|
|
293
|
+
"""Transform the data."""
|
|
294
|
+
return data`;
|
|
295
|
+
break;
|
|
296
|
+
|
|
297
|
+
case 'function':
|
|
298
|
+
mockCode = `def ${nodeData.name}(${nodeData.params ? nodeData.params.join(', ') : ''}):
|
|
299
|
+
"""
|
|
300
|
+
${nodeData.docstring || 'A sample function implementation.'}
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
${nodeData.params ? nodeData.params.map(p => `${p}: Description of ${p}`).join('\n ') : 'None'}
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
${nodeData.returns || 'None'}: Return value description
|
|
307
|
+
"""
|
|
308
|
+
# Implementation here
|
|
309
|
+
result = None
|
|
310
|
+
|
|
311
|
+
# Process logic
|
|
312
|
+
for item in range(10):
|
|
313
|
+
result = process_item(item)
|
|
314
|
+
|
|
315
|
+
return result`;
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case 'method':
|
|
319
|
+
mockCode = ` def ${nodeData.name}(self${nodeData.params ? ', ' + nodeData.params.join(', ') : ''}):
|
|
320
|
+
"""
|
|
321
|
+
${nodeData.docstring || 'A sample method implementation.'}
|
|
322
|
+
"""
|
|
323
|
+
# Method implementation
|
|
324
|
+
self._validate()
|
|
325
|
+
result = self._process()
|
|
326
|
+
return result`;
|
|
327
|
+
break;
|
|
328
|
+
|
|
329
|
+
default:
|
|
330
|
+
mockCode = `# ${nodeData.name}
|
|
331
|
+
# Type: ${nodeData.type}
|
|
332
|
+
# Path: ${nodeData.path || 'Unknown'}
|
|
333
|
+
# Line: ${nodeData.line || 'Unknown'}
|
|
334
|
+
|
|
335
|
+
# Code content would appear here
|
|
336
|
+
# This is a placeholder for demonstration purposes`;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
this.displayCode(mockCode);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Display error message
|
|
344
|
+
*/
|
|
345
|
+
displayError(message) {
|
|
346
|
+
const codeContent = document.getElementById('code-viewer-code-content');
|
|
347
|
+
codeContent.textContent = `# Error loading code\n# ${message}`;
|
|
348
|
+
codeContent.className = 'language-python';
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Detect language from file path
|
|
353
|
+
*/
|
|
354
|
+
detectLanguage(path) {
|
|
355
|
+
if (!path) return 'python';
|
|
356
|
+
|
|
357
|
+
const ext = path.split('.').pop().toLowerCase();
|
|
358
|
+
const languageMap = {
|
|
359
|
+
'py': 'python',
|
|
360
|
+
'js': 'javascript',
|
|
361
|
+
'ts': 'typescript',
|
|
362
|
+
'jsx': 'jsx',
|
|
363
|
+
'tsx': 'tsx',
|
|
364
|
+
'css': 'css',
|
|
365
|
+
'html': 'html',
|
|
366
|
+
'json': 'json',
|
|
367
|
+
'yaml': 'yaml',
|
|
368
|
+
'yml': 'yaml',
|
|
369
|
+
'md': 'markdown',
|
|
370
|
+
'sh': 'bash',
|
|
371
|
+
'bash': 'bash',
|
|
372
|
+
'sql': 'sql',
|
|
373
|
+
'go': 'go',
|
|
374
|
+
'rs': 'rust',
|
|
375
|
+
'cpp': 'cpp',
|
|
376
|
+
'c': 'c',
|
|
377
|
+
'h': 'c',
|
|
378
|
+
'hpp': 'cpp',
|
|
379
|
+
'java': 'java',
|
|
380
|
+
'rb': 'ruby',
|
|
381
|
+
'php': 'php'
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
return languageMap[ext] || 'plaintext';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Update navigation buttons
|
|
389
|
+
*/
|
|
390
|
+
updateNavigation(nodeData) {
|
|
391
|
+
// For now, disable navigation buttons
|
|
392
|
+
// In a real implementation, these would navigate through the AST
|
|
393
|
+
document.getElementById('code-nav-parent').disabled = true;
|
|
394
|
+
document.getElementById('code-nav-prev').disabled = true;
|
|
395
|
+
document.getElementById('code-nav-next').disabled = true;
|
|
396
|
+
document.getElementById('code-nav-position').textContent = '1 / 1';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Navigate to parent node
|
|
401
|
+
*/
|
|
402
|
+
navigateToParent() {
|
|
403
|
+
console.log('Navigate to parent node');
|
|
404
|
+
// Implementation would load parent node's code
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Navigate to previous sibling
|
|
409
|
+
*/
|
|
410
|
+
navigateToPrevious() {
|
|
411
|
+
console.log('Navigate to previous sibling');
|
|
412
|
+
// Implementation would load previous sibling's code
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Navigate to next sibling
|
|
417
|
+
*/
|
|
418
|
+
navigateToNext() {
|
|
419
|
+
console.log('Navigate to next sibling');
|
|
420
|
+
// Implementation would load next sibling's code
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Copy code to clipboard
|
|
425
|
+
*/
|
|
426
|
+
async copyCode() {
|
|
427
|
+
const codeContent = document.getElementById('code-viewer-code-content');
|
|
428
|
+
const code = codeContent.textContent;
|
|
429
|
+
|
|
430
|
+
try {
|
|
431
|
+
await navigator.clipboard.writeText(code);
|
|
432
|
+
|
|
433
|
+
// Show feedback
|
|
434
|
+
const button = document.getElementById('code-copy');
|
|
435
|
+
const originalText = button.textContent;
|
|
436
|
+
button.textContent = '✅ Copied!';
|
|
437
|
+
setTimeout(() => {
|
|
438
|
+
button.textContent = originalText;
|
|
439
|
+
}, 2000);
|
|
440
|
+
} catch (err) {
|
|
441
|
+
console.error('Failed to copy code:', err);
|
|
442
|
+
alert('Failed to copy code to clipboard');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Open file in editor
|
|
448
|
+
*/
|
|
449
|
+
openInEditor() {
|
|
450
|
+
if (!this.currentNode || !this.currentNode.path) {
|
|
451
|
+
alert('File path not available');
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Emit event to open file
|
|
456
|
+
if (this.socket) {
|
|
457
|
+
this.socket.emit('file:open', {
|
|
458
|
+
path: this.currentNode.path,
|
|
459
|
+
line: this.currentNode.line
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
console.log('Opening file in editor:', this.currentNode.path);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Create singleton instance
|
|
468
|
+
const codeViewer = new CodeViewer();
|
|
469
|
+
|
|
470
|
+
// Export for use in other modules
|
|
471
|
+
if (typeof window !== 'undefined') {
|
|
472
|
+
window.CodeViewer = codeViewer;
|
|
473
|
+
|
|
474
|
+
// Initialize when DOM is ready
|
|
475
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
476
|
+
codeViewer.initialize();
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export default codeViewer;
|
|
@@ -39,7 +39,6 @@ class EventViewer {
|
|
|
39
39
|
|
|
40
40
|
// Subscribe to socket events
|
|
41
41
|
this.socketClient.onEventUpdate((events, sessions) => {
|
|
42
|
-
console.log('EventViewer received event update:', events?.length || 0, 'events');
|
|
43
42
|
// Ensure we always have a valid events array
|
|
44
43
|
this.events = Array.isArray(events) ? events : [];
|
|
45
44
|
this.updateDisplay();
|
|
@@ -76,7 +75,6 @@ class EventViewer {
|
|
|
76
75
|
setupKeyboardNavigation() {
|
|
77
76
|
// Keyboard navigation is now handled by Dashboard.setupUnifiedKeyboardNavigation()
|
|
78
77
|
// This method is kept for backward compatibility but does nothing
|
|
79
|
-
console.log('EventViewer: Keyboard navigation handled by unified Dashboard system');
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
/**
|
|
@@ -110,7 +108,10 @@ class EventViewer {
|
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
this.filteredEvents = this.events.filter(event => {
|
|
113
|
-
//
|
|
111
|
+
// NO AUTOMATIC FILTERING - All events are shown by default for complete visibility
|
|
112
|
+
// Users can apply their own filters using the search and type filter controls
|
|
113
|
+
|
|
114
|
+
// User-controlled search filter
|
|
114
115
|
if (this.searchFilter) {
|
|
115
116
|
const searchableText = [
|
|
116
117
|
event.type || '',
|
|
@@ -123,7 +124,7 @@ class EventViewer {
|
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
//
|
|
127
|
+
// User-controlled type filter - handles full hook types (like "hook.user_prompt") and main types
|
|
127
128
|
if (this.typeFilter) {
|
|
128
129
|
// Use the same logic as formatEventType to get the full event type
|
|
129
130
|
const eventType = event.type && event.type.trim() !== '' ? event.type : '';
|
|
@@ -133,13 +134,14 @@ class EventViewer {
|
|
|
133
134
|
}
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
//
|
|
137
|
+
// User-controlled session filter
|
|
137
138
|
if (this.sessionFilter && this.sessionFilter !== '') {
|
|
138
139
|
if (!event.data || event.data.session_id !== this.sessionFilter) {
|
|
139
140
|
return false;
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
// Allow all events through unless filtered by user controls
|
|
143
145
|
return true;
|
|
144
146
|
});
|
|
145
147
|
|
|
@@ -211,7 +213,6 @@ class EventViewer {
|
|
|
211
213
|
* Update the display with current events
|
|
212
214
|
*/
|
|
213
215
|
updateDisplay() {
|
|
214
|
-
console.log('EventViewer updating display with', this.events?.length || 0, 'events');
|
|
215
216
|
this.updateEventTypeDropdown();
|
|
216
217
|
this.applyFilters();
|
|
217
218
|
}
|
|
@@ -223,6 +224,9 @@ class EventViewer {
|
|
|
223
224
|
const eventsList = document.getElementById('events-list');
|
|
224
225
|
if (!eventsList) return;
|
|
225
226
|
|
|
227
|
+
// Check if user is at bottom BEFORE rendering (for autoscroll decision)
|
|
228
|
+
const wasAtBottom = (eventsList.scrollTop + eventsList.clientHeight >= eventsList.scrollHeight - 10);
|
|
229
|
+
|
|
226
230
|
if (this.filteredEvents.length === 0) {
|
|
227
231
|
eventsList.innerHTML = `
|
|
228
232
|
<div class="no-events">
|
|
@@ -270,9 +274,12 @@ class EventViewer {
|
|
|
270
274
|
window.dashboard.tabNavigation.events.items = this.filteredEventElements;
|
|
271
275
|
}
|
|
272
276
|
|
|
273
|
-
// Auto-scroll
|
|
274
|
-
if (this.
|
|
275
|
-
|
|
277
|
+
// Auto-scroll only if user was already at bottom before rendering
|
|
278
|
+
if (this.filteredEvents.length > 0 && wasAtBottom && this.autoScroll) {
|
|
279
|
+
// Use requestAnimationFrame to ensure DOM has updated
|
|
280
|
+
requestAnimationFrame(() => {
|
|
281
|
+
eventsList.scrollTop = eventsList.scrollHeight;
|
|
282
|
+
});
|
|
276
283
|
}
|
|
277
284
|
}
|
|
278
285
|
|
|
@@ -326,6 +333,8 @@ class EventViewer {
|
|
|
326
333
|
return this.formatMemoryEvent(event);
|
|
327
334
|
case 'log':
|
|
328
335
|
return this.formatLogEvent(event);
|
|
336
|
+
case 'code':
|
|
337
|
+
return this.formatCodeEvent(event);
|
|
329
338
|
default:
|
|
330
339
|
return this.formatGenericEvent(event);
|
|
331
340
|
}
|
|
@@ -469,6 +478,47 @@ class EventViewer {
|
|
|
469
478
|
return `<strong>[${level.toUpperCase()}]</strong> ${truncated}`;
|
|
470
479
|
}
|
|
471
480
|
|
|
481
|
+
/**
|
|
482
|
+
* Format code analysis event data
|
|
483
|
+
*/
|
|
484
|
+
formatCodeEvent(event) {
|
|
485
|
+
const data = event.data || {};
|
|
486
|
+
|
|
487
|
+
// Handle different code event subtypes
|
|
488
|
+
if (event.subtype === 'progress') {
|
|
489
|
+
const message = data.message || 'Processing...';
|
|
490
|
+
const percentage = data.percentage;
|
|
491
|
+
if (percentage !== undefined) {
|
|
492
|
+
return `<strong>Progress:</strong> ${message} (${Math.round(percentage)}%)`;
|
|
493
|
+
}
|
|
494
|
+
return `<strong>Progress:</strong> ${message}`;
|
|
495
|
+
} else if (event.subtype === 'analysis:queued') {
|
|
496
|
+
return `<strong>Queued:</strong> Analysis for ${data.path || 'Unknown path'}`;
|
|
497
|
+
} else if (event.subtype === 'analysis:start') {
|
|
498
|
+
return `<strong>Started:</strong> Analyzing ${data.path || 'Unknown path'}`;
|
|
499
|
+
} else if (event.subtype === 'analysis:complete') {
|
|
500
|
+
const duration = data.duration ? ` (${data.duration.toFixed(2)}s)` : '';
|
|
501
|
+
return `<strong>Complete:</strong> Analysis finished${duration}`;
|
|
502
|
+
} else if (event.subtype === 'analysis:error') {
|
|
503
|
+
return `<strong>Error:</strong> ${data.message || 'Analysis failed'}`;
|
|
504
|
+
} else if (event.subtype === 'analysis:cancelled') {
|
|
505
|
+
return `<strong>Cancelled:</strong> Analysis stopped for ${data.path || 'Unknown path'}`;
|
|
506
|
+
} else if (event.subtype === 'file:start') {
|
|
507
|
+
return `<strong>File:</strong> Processing ${data.file || 'Unknown file'}`;
|
|
508
|
+
} else if (event.subtype === 'file:complete') {
|
|
509
|
+
const nodes = data.nodes_count !== undefined ? ` (${data.nodes_count} nodes)` : '';
|
|
510
|
+
return `<strong>File done:</strong> ${data.file || 'Unknown file'}${nodes}`;
|
|
511
|
+
} else if (event.subtype === 'node:found') {
|
|
512
|
+
return `<strong>Node:</strong> Found ${data.node_type || 'element'} "${data.name || 'unnamed'}"`;
|
|
513
|
+
} else if (event.subtype === 'error') {
|
|
514
|
+
return `<strong>Error:</strong> ${data.error || 'Unknown error'} in ${data.file || 'file'}`;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Generic fallback for code events
|
|
518
|
+
const json = JSON.stringify(data);
|
|
519
|
+
return `<strong>Code:</strong> ${json.length > 100 ? json.substring(0, 100) + '...' : json}`;
|
|
520
|
+
}
|
|
521
|
+
|
|
472
522
|
/**
|
|
473
523
|
* Format generic event data
|
|
474
524
|
*/
|
|
@@ -65,6 +65,8 @@ class SessionManager {
|
|
|
65
65
|
setupSocketListeners() {
|
|
66
66
|
// Listen for socket event updates
|
|
67
67
|
this.socketClient.onEventUpdate((events, sessions) => {
|
|
68
|
+
// Log the sessions data to debug
|
|
69
|
+
console.log('[SESSION-MANAGER] Received sessions update:', sessions);
|
|
68
70
|
this.sessions = sessions;
|
|
69
71
|
this.updateSessionSelect();
|
|
70
72
|
// Update footer info when new events arrive
|
|
@@ -90,9 +92,28 @@ class SessionManager {
|
|
|
90
92
|
// Store current selection
|
|
91
93
|
const currentSelection = sessionSelect.value;
|
|
92
94
|
|
|
93
|
-
//
|
|
95
|
+
// Get the default working directory from various sources
|
|
96
|
+
let defaultWorkingDir = '/Users/masa/Projects/claude-mpm';
|
|
97
|
+
|
|
98
|
+
// Try to get from working directory manager
|
|
99
|
+
if (window.dashboard && window.dashboard.workingDirectoryManager) {
|
|
100
|
+
defaultWorkingDir = window.dashboard.workingDirectoryManager.getDefaultWorkingDir();
|
|
101
|
+
} else {
|
|
102
|
+
// Fallback: Try to get from header display element
|
|
103
|
+
const headerWorkingDir = document.getElementById('working-dir-path');
|
|
104
|
+
if (headerWorkingDir?.textContent?.trim()) {
|
|
105
|
+
const headerPath = headerWorkingDir.textContent.trim();
|
|
106
|
+
if (headerPath !== 'Loading...' && headerPath !== 'Unknown') {
|
|
107
|
+
defaultWorkingDir = headerPath;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('[SESSION-MANAGER] Using default working directory:', defaultWorkingDir);
|
|
113
|
+
|
|
114
|
+
// Update "All Sessions" option to show working directory
|
|
94
115
|
sessionSelect.innerHTML = `
|
|
95
|
-
<option value=""
|
|
116
|
+
<option value="">${defaultWorkingDir} | All Sessions</option>
|
|
96
117
|
`;
|
|
97
118
|
|
|
98
119
|
// Add sessions from the sessions map
|
|
@@ -108,8 +129,23 @@ class SessionManager {
|
|
|
108
129
|
const startTime = new Date(session.startTime || session.last_activity).toLocaleString();
|
|
109
130
|
const eventCount = session.eventCount || session.event_count || 0;
|
|
110
131
|
const isActive = session.id === this.currentSessionId;
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
|
|
133
|
+
// Extract working directory from session or events
|
|
134
|
+
let workingDir = session.working_directory || session.workingDirectory || '';
|
|
135
|
+
|
|
136
|
+
// Log for debugging
|
|
137
|
+
console.log(`[SESSION-DROPDOWN] Session ${session.id.substring(0, 8)} working_directory:`, workingDir);
|
|
138
|
+
|
|
139
|
+
if (!workingDir) {
|
|
140
|
+
const sessionData = this.extractSessionInfoFromEvents(session.id);
|
|
141
|
+
workingDir = sessionData.workingDir || defaultWorkingDir;
|
|
142
|
+
console.log(`[SESSION-DROPDOWN] Extracted working directory from events:`, workingDir);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Format display: working_directory | session_id...
|
|
146
|
+
const shortId = session.id.substring(0, 8);
|
|
147
|
+
const dirDisplay = workingDir || defaultWorkingDir;
|
|
148
|
+
option.textContent = `${dirDisplay} | ${shortId}...${isActive ? ' [ACTIVE]' : ''}`;
|
|
113
149
|
sessionSelect.appendChild(option);
|
|
114
150
|
});
|
|
115
151
|
}
|