claude-mpm 4.2.44__py3-none-any.whl → 4.3.0__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/BASE_PM.md +77 -405
- claude_mpm/agents/{INSTRUCTIONS.md → INSTRUCTIONS_OLD_DEPRECATED.md} +75 -1
- claude_mpm/agents/OUTPUT_STYLE.md +0 -39
- claude_mpm/agents/PM_INSTRUCTIONS.md +122 -0
- claude_mpm/agents/WORKFLOW.md +74 -323
- claude_mpm/agents/frontmatter_validator.py +20 -12
- claude_mpm/agents/templates/nextjs_engineer.json +277 -0
- claude_mpm/agents/templates/prompt-engineer.json +294 -0
- claude_mpm/agents/templates/python_engineer.json +289 -0
- claude_mpm/agents/templates/react_engineer.json +11 -3
- claude_mpm/agents/templates/security.json +50 -9
- claude_mpm/cli/commands/agents.py +2 -2
- claude_mpm/cli/commands/uninstall.py +1 -3
- claude_mpm/cli/interactive/agent_wizard.py +3 -3
- claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
- claude_mpm/cli/parsers/agents_parser.py +1 -1
- claude_mpm/constants.py +1 -1
- claude_mpm/core/error_handler.py +2 -4
- claude_mpm/core/file_utils.py +4 -12
- claude_mpm/core/framework_loader.py +72 -24
- claude_mpm/core/log_manager.py +60 -5
- claude_mpm/core/logger.py +1 -1
- claude_mpm/core/logging_utils.py +36 -18
- claude_mpm/core/unified_agent_registry.py +18 -4
- claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
- claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
- claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
- claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
- claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
- claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
- claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
- claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
- claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
- claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
- claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
- claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
- claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
- claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
- claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
- claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
- claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
- claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
- claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
- claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
- claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
- claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
- claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
- claude_mpm/dashboard/static/built/components/code-viewer.js +2 -1076
- claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
- claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
- claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
- 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/nav-bar.js +145 -0
- claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
- claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -465
- claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/built/connection-manager.js +536 -0
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/built/react/events.js +30 -0
- claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
- claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
- claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
- claude_mpm/dashboard/static/built/shared/logger.js +385 -0
- claude_mpm/dashboard/static/built/shared/page-structure.js +251 -0
- claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/css/dashboard.css +28 -5
- claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-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/react/events.js +30 -0
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/events.html +607 -0
- claude_mpm/dashboard/static/index.html +713 -0
- claude_mpm/dashboard/static/js/components/activity-tree.js +3 -17
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
- claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
- claude_mpm/dashboard/static/js/components/build-tracker.js +8 -0
- claude_mpm/dashboard/static/js/components/code-viewer.js +306 -66
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +39 -2
- claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
- claude_mpm/dashboard/static/js/components/socket-manager.js +4 -0
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +285 -85
- claude_mpm/dashboard/static/js/components/working-directory.js +3 -0
- claude_mpm/dashboard/static/js/dashboard.js +61 -33
- claude_mpm/dashboard/static/js/socket-client.js +12 -8
- claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/legacy/activity.html +736 -0
- claude_mpm/dashboard/static/legacy/agents.html +786 -0
- claude_mpm/dashboard/static/legacy/files.html +747 -0
- claude_mpm/dashboard/static/legacy/tools.html +831 -0
- claude_mpm/dashboard/static/monitors-index.html +218 -0
- claude_mpm/dashboard/static/monitors.html +431 -0
- claude_mpm/dashboard/static/production/events.html +659 -0
- claude_mpm/dashboard/static/production/main.html +715 -0
- claude_mpm/dashboard/static/production/monitors.html +483 -0
- claude_mpm/dashboard/static/socket.io.min.js +7 -0
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
- claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
- claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
- claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
- claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
- claude_mpm/dashboard/templates/index.html +79 -9
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +1 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +285 -26
- claude_mpm/services/agents/deployment/agent_validator.py +3 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +13 -4
- claude_mpm/services/agents/local_template_manager.py +2 -7
- claude_mpm/services/monitor/daemon.py +1 -2
- claude_mpm/services/monitor/daemon_manager.py +2 -7
- claude_mpm/services/monitor/event_emitter.py +6 -2
- claude_mpm/services/monitor/handlers/code_analysis.py +4 -6
- claude_mpm/services/monitor/handlers/hooks.py +2 -6
- claude_mpm/services/monitor/server.py +27 -4
- claude_mpm/tools/code_tree_analyzer.py +2 -4
- claude_mpm/utils/log_cleanup.py +612 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/RECORD +151 -83
- claude_mpm/dashboard/static/test-browser-monitor.html +0 -470
- claude_mpm/dashboard/static/test-simple.html +0 -97
- /claude_mpm/dashboard/static/{test_debug.html → test-archive/test_debug.html} +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.3.0.dist-info}/top_level.txt +0 -0
|
@@ -150,6 +150,23 @@
|
|
|
150
150
|
<div class="header-row">
|
|
151
151
|
<div class="header-title">
|
|
152
152
|
<h1>🚀 Claude MPM Monitor</h1>
|
|
153
|
+
<a href="/static/monitors-index.html" style="
|
|
154
|
+
display: inline-block;
|
|
155
|
+
margin-left: 20px;
|
|
156
|
+
padding: 8px 16px;
|
|
157
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
158
|
+
color: white;
|
|
159
|
+
text-decoration: none;
|
|
160
|
+
border-radius: 8px;
|
|
161
|
+
font-size: 14px;
|
|
162
|
+
font-weight: 500;
|
|
163
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
164
|
+
vertical-align: middle;
|
|
165
|
+
"
|
|
166
|
+
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(102, 126, 234, 0.4)';"
|
|
167
|
+
onmouseout="this.style.transform=''; this.style.boxShadow='';">
|
|
168
|
+
Try New Dashboard →
|
|
169
|
+
</a>
|
|
153
170
|
<div id="connection-status" class="status-badge status-disconnected">
|
|
154
171
|
<span>●</span> Disconnected
|
|
155
172
|
</div>
|
|
@@ -179,6 +196,15 @@
|
|
|
179
196
|
<div class="metric-mini-value" id="error-count">0</div>
|
|
180
197
|
<div class="metric-mini-label">Errors</div>
|
|
181
198
|
</div>
|
|
199
|
+
<!-- Debug metrics for troubleshooting -->
|
|
200
|
+
<div class="metric-mini" style="background: rgba(255,255,255,0.05);">
|
|
201
|
+
<div class="metric-mini-value" id="debug-events-received">0</div>
|
|
202
|
+
<div class="metric-mini-label">Received</div>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="metric-mini" style="background: rgba(255,255,255,0.05);">
|
|
205
|
+
<div class="metric-mini-value" id="debug-events-rendered">0</div>
|
|
206
|
+
<div class="metric-mini-label">Rendered</div>
|
|
207
|
+
</div>
|
|
182
208
|
</div>
|
|
183
209
|
</div>
|
|
184
210
|
|
|
@@ -390,8 +416,9 @@
|
|
|
390
416
|
|
|
391
417
|
<!-- File Tree Tab -->
|
|
392
418
|
<div class="tab-content" id="claude-tree-tab">
|
|
393
|
-
<div id="claude-tree-container" style="width: 100%; height: 100%;">
|
|
419
|
+
<div id="claude-tree-container" style="width: 100%; height: 100%; position: relative;">
|
|
394
420
|
<!-- File activity tree will be rendered here by code-viewer.js -->
|
|
421
|
+
<!-- This container is ISOLATED from other tabs -->
|
|
395
422
|
</div>
|
|
396
423
|
</div>
|
|
397
424
|
|
|
@@ -490,27 +517,68 @@
|
|
|
490
517
|
.then(() => loadModule('/static/js/components/code-tree/tree-search.js'))
|
|
491
518
|
.then(() => loadModule('/static/js/components/code-tree/tree-breadcrumb.js'))
|
|
492
519
|
.then(() => {
|
|
493
|
-
// Load
|
|
494
|
-
return loadModule('/static/js/
|
|
520
|
+
// CRITICAL: Load socket-client.js FIRST (dependency of socket-manager)
|
|
521
|
+
return loadModule('/static/js/socket-client.js');
|
|
522
|
+
})
|
|
523
|
+
.then(() => {
|
|
524
|
+
// CRITICAL: Load socket-manager BEFORE dashboard.js
|
|
525
|
+
// The source version must be loaded to make SocketManager globally available
|
|
526
|
+
return loadModule('/static/js/components/socket-manager.js');
|
|
495
527
|
})
|
|
496
528
|
.then(() => {
|
|
497
|
-
//
|
|
529
|
+
// Load all required dashboard dependencies BEFORE dashboard.js
|
|
530
|
+
// These must be available globally for the dist/dashboard.js to work
|
|
498
531
|
return Promise.all([
|
|
499
|
-
loadModule('/static/
|
|
532
|
+
loadModule('/static/js/components/event-viewer.js'),
|
|
533
|
+
loadModule('/static/js/components/module-viewer.js'),
|
|
534
|
+
loadModule('/static/js/components/session-manager.js'),
|
|
535
|
+
loadModule('/static/js/components/agent-inference.js'),
|
|
536
|
+
loadModule('/static/js/components/agent-hierarchy.js'),
|
|
537
|
+
loadModule('/static/js/components/ui-state-manager.js'),
|
|
538
|
+
loadModule('/static/js/components/event-processor.js'),
|
|
539
|
+
loadModule('/static/js/components/export-manager.js'),
|
|
540
|
+
loadModule('/static/js/components/working-directory.js'),
|
|
541
|
+
loadModule('/static/js/components/file-tool-tracker.js'),
|
|
542
|
+
loadModule('/static/js/components/build-tracker.js')
|
|
543
|
+
]);
|
|
544
|
+
})
|
|
545
|
+
.then(() => {
|
|
546
|
+
// Now load main components including dashboard.js which depends on the above
|
|
547
|
+
return Promise.all([
|
|
548
|
+
loadModule('/static/dist/dashboard.js'), // Use dist version that requires above components
|
|
500
549
|
loadModule('/static/dist/components/activity-tree.js'),
|
|
501
550
|
loadModule('/static/js/components/code-tree.js'), // TEMPORARY: Direct source for debugging
|
|
502
|
-
loadModule('/static/js/components/code-viewer.js')
|
|
551
|
+
loadModule('/static/js/components/code-viewer.js').catch(err => {
|
|
552
|
+
console.error('[CRITICAL] Failed to load code-viewer.js:', err);
|
|
553
|
+
throw err;
|
|
554
|
+
}), // Code viewer now includes file change tracking
|
|
503
555
|
loadModule('/static/dist/components/file-viewer.js') // File viewer for viewing file contents
|
|
504
556
|
]);
|
|
505
557
|
})
|
|
506
558
|
.then(() => {
|
|
507
559
|
console.log('All dashboard modules loaded successfully');
|
|
508
560
|
|
|
561
|
+
// Debug: Check if CodeViewer loaded
|
|
562
|
+
if (window.CodeViewer) {
|
|
563
|
+
console.log('[DEBUG] CodeViewer is available on window object');
|
|
564
|
+
} else {
|
|
565
|
+
console.error('[ERROR] CodeViewer NOT FOUND on window object!');
|
|
566
|
+
}
|
|
567
|
+
|
|
509
568
|
// CodeViewer will auto-initialize and handle tab switching internally
|
|
510
569
|
|
|
511
570
|
// Browser Log Viewer initialization is now handled by UIStateManager
|
|
512
571
|
// This prevents duplicate event handlers and tab selection conflicts
|
|
513
572
|
|
|
573
|
+
// Load bulletproof tab isolation fix
|
|
574
|
+
loadModule('/static/js/tab-isolation-fix.js')
|
|
575
|
+
.then(() => {
|
|
576
|
+
console.log('✅ Tab isolation fix loaded successfully');
|
|
577
|
+
})
|
|
578
|
+
.catch(err => {
|
|
579
|
+
console.error('❌ Failed to load tab isolation fix:', err);
|
|
580
|
+
});
|
|
581
|
+
|
|
514
582
|
// Hash navigation will handle default tab based on URL
|
|
515
583
|
// If no hash, default will be 'events' as per hashToTab mapping
|
|
516
584
|
// To start with File Tree, we can set hash if not present
|
|
@@ -522,6 +590,11 @@
|
|
|
522
590
|
console.log('Hash present:', window.location.hash);
|
|
523
591
|
}
|
|
524
592
|
}, 500);
|
|
593
|
+
})
|
|
594
|
+
.catch(error => {
|
|
595
|
+
console.error('[CRITICAL] Error loading dashboard modules:', error);
|
|
596
|
+
console.error('Stack trace:', error.stack);
|
|
597
|
+
});
|
|
525
598
|
|
|
526
599
|
// Give modules time to execute and initialize dashboard
|
|
527
600
|
setTimeout(() => {
|
|
@@ -583,9 +656,6 @@
|
|
|
583
656
|
console.error('Connection toggle button not found in DOM');
|
|
584
657
|
}
|
|
585
658
|
}, 1000);
|
|
586
|
-
}).catch(error => {
|
|
587
|
-
console.error('Failed to load dashboard modules:', error);
|
|
588
|
-
});
|
|
589
659
|
</script>
|
|
590
660
|
</body>
|
|
591
661
|
</html>
|
|
@@ -151,7 +151,7 @@ class ConnectionManagerService:
|
|
|
151
151
|
|
|
152
152
|
if loop:
|
|
153
153
|
# We're in an async context, create a task
|
|
154
|
-
|
|
154
|
+
loop.create_task(self._async_emit(namespace, event, data))
|
|
155
155
|
# Don't wait for completion to maintain low latency
|
|
156
156
|
if DEBUG:
|
|
157
157
|
print(f"✅ Async emit scheduled: {event}", file=sys.stderr)
|
|
@@ -219,6 +219,9 @@ class AgentDiscoveryService:
|
|
|
219
219
|
agent_info = {
|
|
220
220
|
"name": metadata.get("name", template_file.stem),
|
|
221
221
|
"description": metadata.get("description", "No description available"),
|
|
222
|
+
"type": template_data.get(
|
|
223
|
+
"agent_type", metadata.get("category", "agent")
|
|
224
|
+
), # Extract agent type
|
|
222
225
|
"version": template_data.get(
|
|
223
226
|
"agent_version",
|
|
224
227
|
template_data.get("version", metadata.get("version", "1.0.0")),
|
|
@@ -30,6 +30,52 @@ class AgentTemplateBuilder:
|
|
|
30
30
|
"""Initialize the template builder."""
|
|
31
31
|
self.logger = get_logger(__name__)
|
|
32
32
|
|
|
33
|
+
def normalize_tools_input(self, tools):
|
|
34
|
+
"""Normalize various tool input formats to a consistent list.
|
|
35
|
+
|
|
36
|
+
Handles multiple input formats:
|
|
37
|
+
- None/empty: Returns default tools
|
|
38
|
+
- String: Splits by comma and strips whitespace
|
|
39
|
+
- List: Ensures all items are strings and strips whitespace
|
|
40
|
+
- Dict: Takes enabled tools (where value is True)
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
tools: Tools input in various formats (str, list, dict, or None)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of tool names, normalized and cleaned
|
|
47
|
+
"""
|
|
48
|
+
default_tools = ["Read", "Write", "Edit", "Grep", "Glob", "Bash"]
|
|
49
|
+
|
|
50
|
+
# Handle None or empty
|
|
51
|
+
if not tools:
|
|
52
|
+
self.logger.debug("No tools provided, using defaults")
|
|
53
|
+
return default_tools
|
|
54
|
+
|
|
55
|
+
# Convert to list format
|
|
56
|
+
if isinstance(tools, str):
|
|
57
|
+
# Split by comma, strip whitespace
|
|
58
|
+
tool_list = [t.strip() for t in tools.split(",") if t.strip()]
|
|
59
|
+
self.logger.debug(f"Converted string tools '{tools}' to list: {tool_list}")
|
|
60
|
+
elif isinstance(tools, list):
|
|
61
|
+
# Ensure all items are strings and strip whitespace
|
|
62
|
+
tool_list = [str(t).strip() for t in tools if t and str(t).strip()]
|
|
63
|
+
self.logger.debug(f"Normalized list tools: {tool_list}")
|
|
64
|
+
elif isinstance(tools, dict):
|
|
65
|
+
# Handle dict format - take enabled tools
|
|
66
|
+
tool_list = [k for k, v in tools.items() if v]
|
|
67
|
+
self.logger.info(f"Converting dict tools format: {tools} -> {tool_list}")
|
|
68
|
+
else:
|
|
69
|
+
self.logger.warning(f"Unknown tools format: {type(tools)}, using defaults")
|
|
70
|
+
return default_tools
|
|
71
|
+
|
|
72
|
+
# Return processed list or defaults if empty
|
|
73
|
+
if not tool_list:
|
|
74
|
+
self.logger.debug("Tools list empty after processing, using defaults")
|
|
75
|
+
return default_tools
|
|
76
|
+
|
|
77
|
+
return tool_list
|
|
78
|
+
|
|
33
79
|
def _load_base_agent_instructions(self, agent_type: str) -> str:
|
|
34
80
|
"""Load BASE instructions for a specific agent type.
|
|
35
81
|
|
|
@@ -138,13 +184,39 @@ class AgentTemplateBuilder:
|
|
|
138
184
|
capabilities.get("tools") if isinstance(capabilities, dict) else None
|
|
139
185
|
)
|
|
140
186
|
|
|
141
|
-
tools
|
|
187
|
+
# Get raw tools from various possible locations
|
|
188
|
+
raw_tools = (
|
|
142
189
|
template_data.get("tools")
|
|
143
190
|
or capabilities_tools
|
|
144
191
|
or template_data.get("configuration_fields", {}).get("tools")
|
|
145
|
-
or ["Read", "Write", "Edit", "Grep", "Glob", "LS"] # Default fallback
|
|
146
192
|
)
|
|
147
193
|
|
|
194
|
+
# Normalize tools to a consistent list format
|
|
195
|
+
tools = self.normalize_tools_input(raw_tools)
|
|
196
|
+
|
|
197
|
+
# Log if we see non-standard tool names (info level, not warning)
|
|
198
|
+
standard_tools = {
|
|
199
|
+
"Read",
|
|
200
|
+
"Write",
|
|
201
|
+
"Edit",
|
|
202
|
+
"MultiEdit", # File operations
|
|
203
|
+
"Grep",
|
|
204
|
+
"Glob",
|
|
205
|
+
"LS", # Search and navigation
|
|
206
|
+
"Bash",
|
|
207
|
+
"BashOutput",
|
|
208
|
+
"KillShell", # Command execution
|
|
209
|
+
"TodoWrite",
|
|
210
|
+
"ExitPlanMode", # Task management
|
|
211
|
+
"WebSearch",
|
|
212
|
+
"WebFetch", # Web operations
|
|
213
|
+
"NotebookRead",
|
|
214
|
+
"NotebookEdit", # Jupyter notebook support
|
|
215
|
+
}
|
|
216
|
+
non_standard = [t for t in tools if t not in standard_tools]
|
|
217
|
+
if non_standard:
|
|
218
|
+
self.logger.info(f"Using non-standard tools: {non_standard}")
|
|
219
|
+
|
|
148
220
|
# Extract model from template with fallback
|
|
149
221
|
capabilities_model = (
|
|
150
222
|
capabilities.get("model") if isinstance(capabilities, dict) else None
|
|
@@ -157,15 +229,8 @@ class AgentTemplateBuilder:
|
|
|
157
229
|
or "sonnet" # Default fallback
|
|
158
230
|
)
|
|
159
231
|
|
|
160
|
-
# Convert tools list to comma-separated string (
|
|
161
|
-
tools_str = ",".join(tools)
|
|
162
|
-
|
|
163
|
-
# Validate tools format - CRITICAL: No spaces allowed!
|
|
164
|
-
if ", " in tools_str:
|
|
165
|
-
self.logger.error(f"Tools contain spaces: '{tools_str}'")
|
|
166
|
-
raise ValueError(
|
|
167
|
-
f"Tools must be comma-separated WITHOUT spaces: {tools_str}"
|
|
168
|
-
)
|
|
232
|
+
# Convert tools list to comma-separated string (without spaces for compatibility)
|
|
233
|
+
tools_str = ",".join(tools)
|
|
169
234
|
|
|
170
235
|
# Map model names to Claude Code format (as required)
|
|
171
236
|
model_map = {
|
|
@@ -267,17 +332,21 @@ class AgentTemplateBuilder:
|
|
|
267
332
|
# Include tools field only if agent is clearly restricted (missing core tools or very few tools)
|
|
268
333
|
not has_core_tools or len(agent_tools) < 6
|
|
269
334
|
|
|
270
|
-
# Build YAML frontmatter
|
|
271
|
-
#
|
|
335
|
+
# Build YAML frontmatter with all relevant metadata from JSON template
|
|
336
|
+
# Include all fields that are useful for agent management and functionality
|
|
272
337
|
#
|
|
273
|
-
#
|
|
338
|
+
# COMPREHENSIVE AGENT FRONTMATTER FORMAT:
|
|
274
339
|
# - name: kebab-case agent name (required)
|
|
275
340
|
# - description: when/why to use this agent with examples (required, multiline)
|
|
276
341
|
# - model: mapped model name (required)
|
|
342
|
+
# - type: agent type for categorization and functionality (optional but important)
|
|
343
|
+
# - category: organizational category (optional)
|
|
277
344
|
# - color: visual identifier (optional)
|
|
278
345
|
# - version: agent version for update tracking (optional)
|
|
279
346
|
# - author: creator information (optional)
|
|
280
|
-
#
|
|
347
|
+
# - created_at: creation timestamp (optional)
|
|
348
|
+
# - updated_at: last update timestamp (optional)
|
|
349
|
+
# - tags: list of tags for search and categorization (optional)
|
|
281
350
|
frontmatter_lines = [
|
|
282
351
|
"---",
|
|
283
352
|
f"name: {claude_code_name}",
|
|
@@ -291,15 +360,28 @@ class AgentTemplateBuilder:
|
|
|
291
360
|
# Add model field (required for Claude Code)
|
|
292
361
|
frontmatter_lines.append(f"model: {model}")
|
|
293
362
|
|
|
294
|
-
# Add
|
|
363
|
+
# Add type field (important for agent categorization)
|
|
364
|
+
if agent_type and agent_type != "general":
|
|
365
|
+
frontmatter_lines.append(f"type: {agent_type}")
|
|
366
|
+
|
|
367
|
+
# Add optional metadata fields
|
|
295
368
|
if metadata.get("color"):
|
|
296
369
|
frontmatter_lines.append(f"color: {metadata['color']}")
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
370
|
+
if metadata.get("category"):
|
|
371
|
+
frontmatter_lines.append(f"category: {metadata['category']}")
|
|
372
|
+
# Always include version field to prevent deployment comparison issues
|
|
373
|
+
if agent_version:
|
|
300
374
|
frontmatter_lines.append(f'version: "{agent_version}"')
|
|
301
375
|
if metadata.get("author"):
|
|
302
376
|
frontmatter_lines.append(f'author: "{metadata["author"]}"')
|
|
377
|
+
if metadata.get("created_at"):
|
|
378
|
+
frontmatter_lines.append(f"created_at: {metadata['created_at']}")
|
|
379
|
+
if metadata.get("updated_at"):
|
|
380
|
+
frontmatter_lines.append(f"updated_at: {metadata['updated_at']}")
|
|
381
|
+
# Add tags as comma-separated string if they exist (consistent with tools format)
|
|
382
|
+
if metadata.get("tags") and isinstance(metadata["tags"], list):
|
|
383
|
+
tags_str = ",".join(metadata["tags"])
|
|
384
|
+
frontmatter_lines.append(f"tags: {tags_str}")
|
|
303
385
|
|
|
304
386
|
frontmatter_lines.extend(
|
|
305
387
|
[
|
|
@@ -314,12 +396,20 @@ class AgentTemplateBuilder:
|
|
|
314
396
|
base_instructions = self._load_base_agent_instructions(agent_type)
|
|
315
397
|
|
|
316
398
|
# Get agent instructions from template data (primary) or base agent data (fallback)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
399
|
+
raw_instructions = template_data.get("instructions")
|
|
400
|
+
|
|
401
|
+
# Handle dictionary instructions format
|
|
402
|
+
if isinstance(raw_instructions, dict):
|
|
403
|
+
agent_specific_instructions = self._convert_instructions_dict_to_markdown(
|
|
404
|
+
raw_instructions
|
|
405
|
+
)
|
|
406
|
+
else:
|
|
407
|
+
agent_specific_instructions = (
|
|
408
|
+
raw_instructions
|
|
409
|
+
or base_agent_data.get("content")
|
|
410
|
+
or base_agent_data.get("instructions")
|
|
411
|
+
or "# Agent Instructions\n\nThis agent provides specialized assistance."
|
|
412
|
+
)
|
|
323
413
|
|
|
324
414
|
# Combine BASE instructions with agent-specific instructions
|
|
325
415
|
if base_instructions:
|
|
@@ -406,7 +496,8 @@ Only include memories that are:
|
|
|
406
496
|
)
|
|
407
497
|
|
|
408
498
|
# Get tools and model with fallbacks
|
|
409
|
-
|
|
499
|
+
raw_tools = merged_config.get("tools")
|
|
500
|
+
tools = self.normalize_tools_input(raw_tools)
|
|
410
501
|
model = merged_config.get("model", "sonnet")
|
|
411
502
|
|
|
412
503
|
# Format tools as YAML list
|
|
@@ -873,3 +964,171 @@ tools:
|
|
|
873
964
|
|
|
874
965
|
# Return as quoted string
|
|
875
966
|
return f'"{escaped}"'
|
|
967
|
+
|
|
968
|
+
def _convert_instructions_dict_to_markdown(self, instructions_dict: dict) -> str:
|
|
969
|
+
"""Convert complex instructions dictionary to markdown format.
|
|
970
|
+
|
|
971
|
+
Args:
|
|
972
|
+
instructions_dict: Dictionary containing structured instructions
|
|
973
|
+
|
|
974
|
+
Returns:
|
|
975
|
+
Formatted markdown string representing the instructions
|
|
976
|
+
"""
|
|
977
|
+
if not instructions_dict:
|
|
978
|
+
return "# Agent Instructions\n\nThis agent provides specialized assistance."
|
|
979
|
+
|
|
980
|
+
markdown_parts = []
|
|
981
|
+
|
|
982
|
+
# Add primary role
|
|
983
|
+
if "primary_role" in instructions_dict:
|
|
984
|
+
markdown_parts.extend(["# Role", "", instructions_dict["primary_role"], ""])
|
|
985
|
+
|
|
986
|
+
# Add core identity
|
|
987
|
+
if "core_identity" in instructions_dict:
|
|
988
|
+
markdown_parts.extend(
|
|
989
|
+
["## Core Identity", "", instructions_dict["core_identity"], ""]
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
# Add responsibilities
|
|
993
|
+
if "responsibilities" in instructions_dict:
|
|
994
|
+
markdown_parts.extend(["## Responsibilities", ""])
|
|
995
|
+
|
|
996
|
+
responsibilities = instructions_dict["responsibilities"]
|
|
997
|
+
if isinstance(responsibilities, list):
|
|
998
|
+
for resp in responsibilities:
|
|
999
|
+
if isinstance(resp, dict):
|
|
1000
|
+
area = resp.get("area", "Unknown Area")
|
|
1001
|
+
tasks = resp.get("tasks", [])
|
|
1002
|
+
|
|
1003
|
+
markdown_parts.extend([f"### {area}", ""])
|
|
1004
|
+
|
|
1005
|
+
if isinstance(tasks, list):
|
|
1006
|
+
for task in tasks:
|
|
1007
|
+
markdown_parts.append(f"- {task}")
|
|
1008
|
+
|
|
1009
|
+
markdown_parts.append("")
|
|
1010
|
+
else:
|
|
1011
|
+
markdown_parts.append(f"- {resp}")
|
|
1012
|
+
|
|
1013
|
+
markdown_parts.append("")
|
|
1014
|
+
|
|
1015
|
+
# Add analytical framework
|
|
1016
|
+
if "analytical_framework" in instructions_dict:
|
|
1017
|
+
framework = instructions_dict["analytical_framework"]
|
|
1018
|
+
if isinstance(framework, dict):
|
|
1019
|
+
markdown_parts.extend(["## Analytical Framework", ""])
|
|
1020
|
+
|
|
1021
|
+
for framework_area, framework_data in framework.items():
|
|
1022
|
+
markdown_parts.extend(
|
|
1023
|
+
[f"### {framework_area.replace('_', ' ').title()}", ""]
|
|
1024
|
+
)
|
|
1025
|
+
|
|
1026
|
+
if isinstance(framework_data, dict):
|
|
1027
|
+
for category, items in framework_data.items():
|
|
1028
|
+
markdown_parts.extend(
|
|
1029
|
+
[f"#### {category.replace('_', ' ').title()}", ""]
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1032
|
+
if isinstance(items, list):
|
|
1033
|
+
for item in items:
|
|
1034
|
+
markdown_parts.append(f"- {item}")
|
|
1035
|
+
elif isinstance(items, str):
|
|
1036
|
+
markdown_parts.append(items)
|
|
1037
|
+
|
|
1038
|
+
markdown_parts.append("")
|
|
1039
|
+
elif isinstance(framework_data, list):
|
|
1040
|
+
for item in framework_data:
|
|
1041
|
+
markdown_parts.append(f"- {item}")
|
|
1042
|
+
markdown_parts.append("")
|
|
1043
|
+
|
|
1044
|
+
# Add methodologies
|
|
1045
|
+
if "methodologies" in instructions_dict:
|
|
1046
|
+
methodologies = instructions_dict["methodologies"]
|
|
1047
|
+
if isinstance(methodologies, dict):
|
|
1048
|
+
markdown_parts.extend(["## Methodologies", ""])
|
|
1049
|
+
|
|
1050
|
+
for method_name, method_data in methodologies.items():
|
|
1051
|
+
markdown_parts.extend(
|
|
1052
|
+
[f"### {method_name.replace('_', ' ').title()}", ""]
|
|
1053
|
+
)
|
|
1054
|
+
|
|
1055
|
+
if isinstance(method_data, dict):
|
|
1056
|
+
for key, value in method_data.items():
|
|
1057
|
+
if isinstance(value, list):
|
|
1058
|
+
markdown_parts.extend(
|
|
1059
|
+
[f"#### {key.replace('_', ' ').title()}", ""]
|
|
1060
|
+
)
|
|
1061
|
+
for item in value:
|
|
1062
|
+
markdown_parts.append(f"- {item}")
|
|
1063
|
+
markdown_parts.append("")
|
|
1064
|
+
elif isinstance(value, str):
|
|
1065
|
+
markdown_parts.extend(
|
|
1066
|
+
[
|
|
1067
|
+
f"**{key.replace('_', ' ').title()}**: {value}",
|
|
1068
|
+
"",
|
|
1069
|
+
]
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
# Add quality standards
|
|
1073
|
+
if "quality_standards" in instructions_dict:
|
|
1074
|
+
standards = instructions_dict["quality_standards"]
|
|
1075
|
+
if isinstance(standards, dict):
|
|
1076
|
+
markdown_parts.extend(["## Quality Standards", ""])
|
|
1077
|
+
|
|
1078
|
+
for standard_area, standard_items in standards.items():
|
|
1079
|
+
markdown_parts.extend(
|
|
1080
|
+
[f"### {standard_area.replace('_', ' ').title()}", ""]
|
|
1081
|
+
)
|
|
1082
|
+
|
|
1083
|
+
if isinstance(standard_items, list):
|
|
1084
|
+
for item in standard_items:
|
|
1085
|
+
markdown_parts.append(f"- {item}")
|
|
1086
|
+
elif isinstance(standard_items, str):
|
|
1087
|
+
markdown_parts.append(standard_items)
|
|
1088
|
+
|
|
1089
|
+
markdown_parts.append("")
|
|
1090
|
+
|
|
1091
|
+
# Add communication style
|
|
1092
|
+
if "communication_style" in instructions_dict:
|
|
1093
|
+
comm_style = instructions_dict["communication_style"]
|
|
1094
|
+
if isinstance(comm_style, dict):
|
|
1095
|
+
markdown_parts.extend(["## Communication Style", ""])
|
|
1096
|
+
|
|
1097
|
+
for style_area, style_items in comm_style.items():
|
|
1098
|
+
markdown_parts.extend(
|
|
1099
|
+
[f"### {style_area.replace('_', ' ').title()}", ""]
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
if isinstance(style_items, list):
|
|
1103
|
+
for item in style_items:
|
|
1104
|
+
markdown_parts.append(f"- {item}")
|
|
1105
|
+
elif isinstance(style_items, str):
|
|
1106
|
+
markdown_parts.append(style_items)
|
|
1107
|
+
|
|
1108
|
+
markdown_parts.append("")
|
|
1109
|
+
|
|
1110
|
+
# If no specific sections were found, convert as generic dict
|
|
1111
|
+
if not markdown_parts:
|
|
1112
|
+
markdown_parts = ["# Agent Instructions", ""]
|
|
1113
|
+
for key, value in instructions_dict.items():
|
|
1114
|
+
key_title = key.replace("_", " ").title()
|
|
1115
|
+
if isinstance(value, str):
|
|
1116
|
+
markdown_parts.extend([f"## {key_title}", "", value, ""])
|
|
1117
|
+
elif isinstance(value, list):
|
|
1118
|
+
markdown_parts.extend([f"## {key_title}", ""])
|
|
1119
|
+
for item in value:
|
|
1120
|
+
markdown_parts.append(f"- {item}")
|
|
1121
|
+
markdown_parts.append("")
|
|
1122
|
+
elif isinstance(value, dict):
|
|
1123
|
+
markdown_parts.extend([f"## {key_title}", ""])
|
|
1124
|
+
# Simple dict formatting
|
|
1125
|
+
for subkey, subvalue in value.items():
|
|
1126
|
+
if isinstance(subvalue, str):
|
|
1127
|
+
markdown_parts.extend(
|
|
1128
|
+
[
|
|
1129
|
+
f"**{subkey.replace('_', ' ').title()}**: {subvalue}",
|
|
1130
|
+
"",
|
|
1131
|
+
]
|
|
1132
|
+
)
|
|
1133
|
+
|
|
1134
|
+
return "\n".join(markdown_parts).strip()
|
|
@@ -326,6 +326,7 @@ class AgentValidator:
|
|
|
326
326
|
"path": str(agent_file),
|
|
327
327
|
"description": "No description",
|
|
328
328
|
"version": "unknown",
|
|
329
|
+
"type": "agent", # Default type
|
|
329
330
|
}
|
|
330
331
|
|
|
331
332
|
# Extract from YAML frontmatter
|
|
@@ -342,6 +343,8 @@ class AgentValidator:
|
|
|
342
343
|
agent_info["version"] = (
|
|
343
344
|
stripped_line.split(":", 1)[1].strip().strip("\"'")
|
|
344
345
|
)
|
|
346
|
+
elif stripped_line.startswith("type:"):
|
|
347
|
+
agent_info["type"] = stripped_line.split(":", 1)[1].strip().strip("\"'")
|
|
345
348
|
|
|
346
349
|
return agent_info
|
|
347
350
|
|
|
@@ -190,10 +190,19 @@ class TemplateValidator:
|
|
|
190
190
|
|
|
191
191
|
if "tags" in metadata:
|
|
192
192
|
tags = metadata["tags"]
|
|
193
|
-
if
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
if isinstance(tags, str):
|
|
194
|
+
# Convert comma-separated string to list for validation
|
|
195
|
+
tag_list = [tag.strip() for tag in tags.split(",") if tag.strip()]
|
|
196
|
+
if len(tag_list) == 0:
|
|
197
|
+
result.add_warning("No tags specified", field_name="metadata.tags")
|
|
198
|
+
elif isinstance(tags, list):
|
|
199
|
+
if len(tags) == 0:
|
|
200
|
+
result.add_warning("No tags specified", field_name="metadata.tags")
|
|
201
|
+
else:
|
|
202
|
+
result.add_error(
|
|
203
|
+
"Tags should be a list or comma-separated string",
|
|
204
|
+
field_name="metadata.tags",
|
|
205
|
+
)
|
|
197
206
|
|
|
198
207
|
def _validate_capabilities(
|
|
199
208
|
self, capabilities: Dict[str, Any], result: ValidationResult
|
|
@@ -250,13 +250,10 @@ class LocalAgentTemplateManager:
|
|
|
250
250
|
Created LocalAgentTemplate object
|
|
251
251
|
"""
|
|
252
252
|
# Determine author based on tier
|
|
253
|
-
if tier == "project"
|
|
254
|
-
author = self.get_project_name()
|
|
255
|
-
else:
|
|
256
|
-
author = Path.home().name
|
|
253
|
+
author = self.get_project_name() if tier == "project" else Path.home().name
|
|
257
254
|
|
|
258
255
|
# Create template
|
|
259
|
-
|
|
256
|
+
return LocalAgentTemplate(
|
|
260
257
|
agent_id=agent_id,
|
|
261
258
|
agent_version="1.0.0",
|
|
262
259
|
author=author,
|
|
@@ -283,8 +280,6 @@ class LocalAgentTemplateManager:
|
|
|
283
280
|
parent_agent=parent_agent,
|
|
284
281
|
)
|
|
285
282
|
|
|
286
|
-
return template
|
|
287
|
-
|
|
288
283
|
def save_local_template(
|
|
289
284
|
self, template: LocalAgentTemplate, tier: Optional[str] = None
|
|
290
285
|
) -> Path:
|
|
@@ -163,8 +163,7 @@ class UnifiedMonitorDaemon:
|
|
|
163
163
|
# environment variable to prevent infinite recursion
|
|
164
164
|
if self.daemon_manager.use_subprocess_daemon():
|
|
165
165
|
# Start using subprocess - this returns immediately in parent
|
|
166
|
-
|
|
167
|
-
return success
|
|
166
|
+
return self.daemon_manager.start_daemon_subprocess()
|
|
168
167
|
|
|
169
168
|
# Check if we're in subprocess mode (environment variable set)
|
|
170
169
|
if os.environ.get("CLAUDE_MPM_SUBPROCESS_DAEMON") == "1":
|
|
@@ -183,10 +183,7 @@ class DaemonManager:
|
|
|
183
183
|
return True
|
|
184
184
|
|
|
185
185
|
# Try to identify claude-mpm processes
|
|
186
|
-
|
|
187
|
-
return True
|
|
188
|
-
|
|
189
|
-
return False
|
|
186
|
+
return bool(self._kill_claude_mpm_processes())
|
|
190
187
|
|
|
191
188
|
except Exception as e:
|
|
192
189
|
self.logger.error(f"Error killing processes on port: {e}")
|
|
@@ -578,9 +575,7 @@ class DaemonManager:
|
|
|
578
575
|
stdout=log_file,
|
|
579
576
|
stderr=subprocess.STDOUT if self.log_file else subprocess.DEVNULL,
|
|
580
577
|
start_new_session=True, # Create new process group
|
|
581
|
-
close_fds=(
|
|
582
|
-
False if self.log_file else True
|
|
583
|
-
), # Keep log file open if redirecting
|
|
578
|
+
close_fds=(not self.log_file), # Keep log file open if redirecting
|
|
584
579
|
env=env, # Pass modified environment
|
|
585
580
|
)
|
|
586
581
|
|