claude-mpm 4.2.1__py3-none-any.whl → 4.2.3__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 (57) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/templates/agent-manager.json +1 -1
  3. claude_mpm/agents/templates/agentic_coder_optimizer.json +1 -1
  4. claude_mpm/agents/templates/api_qa.json +1 -1
  5. claude_mpm/agents/templates/code_analyzer.json +1 -1
  6. claude_mpm/agents/templates/data_engineer.json +1 -1
  7. claude_mpm/agents/templates/documentation.json +1 -1
  8. claude_mpm/agents/templates/engineer.json +2 -2
  9. claude_mpm/agents/templates/gcp_ops_agent.json +14 -9
  10. claude_mpm/agents/templates/imagemagick.json +1 -1
  11. claude_mpm/agents/templates/memory_manager.json +1 -1
  12. claude_mpm/agents/templates/ops.json +1 -1
  13. claude_mpm/agents/templates/project_organizer.json +1 -1
  14. claude_mpm/agents/templates/qa.json +2 -2
  15. claude_mpm/agents/templates/refactoring_engineer.json +1 -1
  16. claude_mpm/agents/templates/research.json +3 -3
  17. claude_mpm/agents/templates/security.json +1 -1
  18. claude_mpm/agents/templates/test-non-mpm.json +20 -0
  19. claude_mpm/agents/templates/ticketing.json +1 -1
  20. claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
  21. claude_mpm/agents/templates/version_control.json +1 -1
  22. claude_mpm/agents/templates/web_qa.json +3 -8
  23. claude_mpm/agents/templates/web_ui.json +1 -1
  24. claude_mpm/cli/commands/agents.py +3 -0
  25. claude_mpm/cli/commands/dashboard.py +3 -3
  26. claude_mpm/cli/commands/monitor.py +227 -64
  27. claude_mpm/core/config.py +25 -0
  28. claude_mpm/core/unified_agent_registry.py +2 -2
  29. claude_mpm/dashboard/static/css/code-tree.css +220 -1
  30. claude_mpm/dashboard/static/css/dashboard.css +286 -0
  31. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  32. claude_mpm/dashboard/static/js/components/code-simple.js +507 -15
  33. claude_mpm/dashboard/static/js/components/code-tree.js +2044 -124
  34. claude_mpm/dashboard/static/js/socket-client.js +5 -2
  35. claude_mpm/dashboard/templates/code_simple.html +79 -0
  36. claude_mpm/dashboard/templates/index.html +42 -41
  37. claude_mpm/services/agents/deployment/agent_deployment.py +4 -1
  38. claude_mpm/services/agents/deployment/agent_discovery_service.py +101 -2
  39. claude_mpm/services/agents/deployment/agent_format_converter.py +53 -9
  40. claude_mpm/services/agents/deployment/agent_template_builder.py +355 -25
  41. claude_mpm/services/agents/deployment/agent_validator.py +11 -6
  42. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +83 -15
  43. claude_mpm/services/agents/deployment/validation/template_validator.py +51 -40
  44. claude_mpm/services/cli/agent_listing_service.py +2 -2
  45. claude_mpm/services/dashboard/stable_server.py +389 -0
  46. claude_mpm/services/socketio/client_proxy.py +16 -0
  47. claude_mpm/services/socketio/dashboard_server.py +360 -0
  48. claude_mpm/services/socketio/handlers/code_analysis.py +27 -5
  49. claude_mpm/services/socketio/monitor_client.py +366 -0
  50. claude_mpm/services/socketio/monitor_server.py +505 -0
  51. claude_mpm/tools/code_tree_analyzer.py +95 -17
  52. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/METADATA +1 -1
  53. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/RECORD +57 -52
  54. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/WHEEL +0 -0
  55. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/entry_points.txt +0 -0
  56. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/licenses/LICENSE +0 -0
  57. {claude_mpm-4.2.1.dist-info → claude_mpm-4.2.3.dist-info}/top_level.txt +0 -0
@@ -1218,8 +1218,11 @@ class SocketClient {
1218
1218
  transformedEvent[key] = eventData.data[key];
1219
1219
  }
1220
1220
  } else {
1221
- // Log warning if data field would overwrite a protected field
1222
- console.warn(`Protected field '${key}' in data object was not copied to top level to preserve event structure`);
1221
+ // Log debug info if data field would overwrite a protected field
1222
+ // Only log for non-timestamp fields to reduce noise
1223
+ if (key !== 'timestamp') {
1224
+ console.debug(`Protected field '${key}' in data object was not copied to top level to preserve event structure`);
1225
+ }
1223
1226
  }
1224
1227
  });
1225
1228
 
@@ -24,6 +24,75 @@
24
24
  border-radius: 4px;
25
25
  margin-bottom: 20px;
26
26
  }
27
+
28
+ /* Tree visualization styles */
29
+ .tree-node {
30
+ cursor: pointer;
31
+ }
32
+
33
+ .tree-node circle {
34
+ fill: #fff;
35
+ stroke: steelblue;
36
+ stroke-width: 2px;
37
+ transition: all 0.3s ease;
38
+ }
39
+
40
+ .tree-node:hover circle {
41
+ fill: #e6f3ff;
42
+ stroke-width: 3px;
43
+ }
44
+
45
+ .tree-node text {
46
+ font: 12px sans-serif;
47
+ pointer-events: none;
48
+ }
49
+
50
+ .tree-link {
51
+ fill: none;
52
+ stroke: #ccc;
53
+ stroke-width: 2px;
54
+ }
55
+
56
+ .view-toggle button {
57
+ transition: all 0.3s ease;
58
+ }
59
+
60
+ .view-toggle button:hover {
61
+ transform: translateY(-1px);
62
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
63
+ }
64
+
65
+ .file-selector input:focus {
66
+ outline: none;
67
+ border-color: #007cba;
68
+ box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);
69
+ }
70
+
71
+ .tree-legend {
72
+ position: absolute;
73
+ top: 10px;
74
+ right: 10px;
75
+ background: rgba(255, 255, 255, 0.9);
76
+ border: 1px solid #ccc;
77
+ border-radius: 4px;
78
+ padding: 10px;
79
+ font-size: 12px;
80
+ z-index: 1000;
81
+ }
82
+
83
+ .tree-legend-item {
84
+ margin: 2px 0;
85
+ display: flex;
86
+ align-items: center;
87
+ }
88
+
89
+ .tree-legend-color {
90
+ width: 12px;
91
+ height: 12px;
92
+ border-radius: 50%;
93
+ margin-right: 8px;
94
+ border: 1px solid #333;
95
+ }
27
96
  </style>
28
97
  </head>
29
98
  <body>
@@ -54,6 +123,16 @@
54
123
  addStatus('⏳ Loading code-simple.js...');
55
124
  </script>
56
125
 
126
+ <!-- D3.js for tree visualization -->
127
+ <script src="https://d3js.org/d3.v7.min.js"
128
+ onload="addStatus('✅ D3.js loaded successfully')"
129
+ onerror="addStatus('❌ Failed to load D3.js')"></script>
130
+
131
+ <!-- Socket.IO for real-time communication -->
132
+ <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"
133
+ onload="addStatus('✅ Socket.IO loaded successfully')"
134
+ onerror="addStatus('❌ Failed to load Socket.IO')"></script>
135
+
57
136
  <script src="/static/js/components/code-simple.js"
58
137
  onload="addStatus('✅ code-simple.js loaded successfully')"
59
138
  onerror="addStatus('❌ Failed to load code-simple.js')"></script>
@@ -247,7 +247,6 @@
247
247
  <button class="tab-button" data-tab="files">📁 Files</button>
248
248
  <button class="tab-button" data-tab="activity">🌳 Activity</button>
249
249
  <button class="tab-button" data-tab="code">🧬 Code</button>
250
- <a href="/code-simple" class="tab-button" style="background: #f7fafc; color: #4a5568; text-decoration: none; border-left: 2px solid #e2e8f0;">📁 Simple View</a>
251
250
  </div>
252
251
 
253
252
  <!-- Events Tab -->
@@ -389,45 +388,52 @@
389
388
  <!-- Code Tab -->
390
389
  <div class="tab-content" id="code-tab">
391
390
  <div class="code-container">
392
- <!-- Simplified header with controls -->
393
- <div class="code-header-compact">
394
- <div class="header-left">
395
- <button id="code-expand-all" class="btn-compact" title="Expand All">⊕</button>
396
- <button id="code-collapse-all" class="btn-compact" title="Collapse All">⊖</button>
397
- <button id="code-reset-zoom" class="btn-compact" title="Reset Zoom">⟲</button>
398
- <button id="code-toggle-legend" class="btn-compact" title="Toggle Legend">ℹ️</button>
399
- </div>
400
- <div class="header-center">
401
- <span class="stat-compact">📁 <span id="file-count">0</span></span>
402
- <span class="stat-compact">🏛️ <span id="class-count">0</span></span>
403
- <span class="stat-compact">⚡ <span id="function-count">0</span></span>
404
- <span class="stat-compact">📝 <span id="line-count">0</span></span>
391
+ <div id="code-tree-container" class="code-tree-container">
392
+ <!-- Top-left corner: Language selector -->
393
+ <div class="tree-corner-controls top-left">
394
+ <div class="control-group">
395
+ <label class="control-label">Languages:</label>
396
+ <div class="checkbox-group">
397
+ <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="python" checked> Python</label>
398
+ <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="javascript" checked> JS</label>
399
+ <label class="checkbox-label"><input type="checkbox" class="language-checkbox" value="typescript" checked> TS</label>
400
+ </div>
401
+ </div>
405
402
  </div>
406
- <div class="header-right">
407
- <select id="language-filter" class="select-compact">
408
- <option value="all">All</option>
409
- <option value="python">Python</option>
410
- <option value="javascript">JS</option>
411
- <option value="typescript">TS</option>
412
- </select>
413
- <input type="text" id="code-search" placeholder="Search..." class="search-compact">
403
+
404
+ <!-- Top-right corner: Layout and search -->
405
+ <div class="tree-corner-controls top-right">
406
+ <div class="control-group">
407
+ <select id="code-layout" class="select-compact">
408
+ <option value="tree">Tree</option>
409
+ <option value="radial">Radial</option>
410
+ </select>
411
+ <input type="text" id="code-search" placeholder="Search..." class="search-compact">
412
+ </div>
414
413
  </div>
415
- </div>
416
- <!-- Advanced options - visible by default -->
417
- <div class="code-advanced-options-visible">
418
- <div class="advanced-content">
419
- <div class="option-group">
420
- <label>Languages:</label>
421
- <label><input type="checkbox" class="language-checkbox" value="python" checked> Python</label>
422
- <label><input type="checkbox" class="language-checkbox" value="javascript" checked> JS</label>
423
- <label><input type="checkbox" class="language-checkbox" value="typescript" checked> TS</label>
414
+
415
+ <!-- Bottom-left corner: Stats and Status -->
416
+ <div class="tree-corner-controls bottom-left">
417
+ <div class="stats-display" id="code-stats">
418
+ <span id="stats-files">0 files</span> •
419
+ <span id="stats-classes">0 classes</span>
420
+ <span id="stats-functions">0 functions</span>
421
+ <span id="stats-methods">0 methods</span>
424
422
  </div>
425
- <div class="option-group">
426
- <label>Ignore: <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js, node_modules" class="input-compact" style="width: 200px;"></label>
423
+ <div class="status-display" id="code-breadcrumb">
424
+ <div class="breadcrumb-ticker" id="breadcrumb-ticker">
425
+ <span id="breadcrumb-content">Ready to analyze...</span>
426
+ </div>
427
+ </div>
428
+ </div>
429
+
430
+ <!-- Bottom-right corner: Ignore patterns -->
431
+ <div class="tree-corner-controls bottom-right">
432
+ <div class="control-group">
433
+ <label class="control-label">Ignore:</label>
434
+ <input type="text" id="ignore-patterns" placeholder="test*, *.spec.js, node_modules" class="input-compact">
427
435
  </div>
428
436
  </div>
429
- </div>
430
- <div id="code-tree-container" class="code-tree-container">
431
437
  <div id="code-tree"></div>
432
438
  <!-- Collapsible legend -->
433
439
  <div class="tree-legend collapsed" id="tree-legend" style="display: none;">
@@ -447,11 +453,6 @@
447
453
  </div>
448
454
  </div>
449
455
  </div>
450
- <div class="code-breadcrumb" id="code-breadcrumb">
451
- <div class="breadcrumb-ticker" id="breadcrumb-ticker">
452
- <span id="breadcrumb-content">Ready to analyze...</span>
453
- </div>
454
- </div>
455
456
  </div>
456
457
  </div>
457
458
 
@@ -531,7 +532,7 @@
531
532
  Promise.all([
532
533
  loadModule('/static/dist/dashboard.js'),
533
534
  loadModule('/static/dist/components/activity-tree.js'),
534
- loadModule('/static/dist/components/code-tree.js'),
535
+ loadModule('/static/js/components/code-tree.js'), // TEMPORARY: Direct source for debugging
535
536
  loadModule('/static/dist/components/code-viewer.js')
536
537
  ]).then(() => {
537
538
  console.log('All dashboard modules loaded successfully');
@@ -683,7 +683,10 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
683
683
 
684
684
  def _get_filtered_templates(self, excluded_agents: list, config: Config) -> list:
685
685
  """Get and filter template files based on exclusion rules."""
686
- return self.discovery_service.get_filtered_templates(excluded_agents, config)
686
+ filter_non_mpm = config.get("agent_deployment.filter_non_mpm_agents", True)
687
+ return self.discovery_service.get_filtered_templates(
688
+ excluded_agents, config, filter_non_mpm
689
+ )
687
690
 
688
691
  def _validate_and_repair_existing_agents(self, agents_dir: Path) -> Dict[str, Any]:
689
692
  """Validate and repair broken frontmatter in existing agent files."""
@@ -85,7 +85,10 @@ class AgentDiscoveryService:
85
85
  return agents
86
86
 
87
87
  def get_filtered_templates(
88
- self, excluded_agents: List[str], config: Optional[Config] = None
88
+ self,
89
+ excluded_agents: List[str],
90
+ config: Optional[Config] = None,
91
+ filter_non_mpm: bool = False,
89
92
  ) -> List[Path]:
90
93
  """
91
94
  Get filtered list of template files based on configuration.
@@ -93,6 +96,7 @@ class AgentDiscoveryService:
93
96
  Args:
94
97
  excluded_agents: List of agent names to exclude
95
98
  config: Configuration object for additional filtering
99
+ filter_non_mpm: Whether to filter out non-MPM agents
96
100
 
97
101
  Returns:
98
102
  List of template file paths to deploy
@@ -111,6 +115,7 @@ class AgentDiscoveryService:
111
115
  # Apply exclusion filtering
112
116
  filtered_files = []
113
117
  excluded_count = 0
118
+ non_mpm_count = 0
114
119
 
115
120
  for template_file in template_files:
116
121
  agent_name = template_file.stem
@@ -121,14 +126,24 @@ class AgentDiscoveryService:
121
126
  self.logger.debug(f"Excluding agent: {agent_name}")
122
127
  continue
123
128
 
129
+ # Check if we should filter non-MPM agents
130
+ if filter_non_mpm and not self._is_mpm_agent(template_file):
131
+ non_mpm_count += 1
132
+ self.logger.debug(f"Filtering non-MPM agent: {agent_name}")
133
+ continue
134
+
124
135
  # Validate template file
125
136
  if self._validate_template_file(template_file):
126
137
  filtered_files.append(template_file)
127
138
  else:
128
139
  self.logger.warning(f"Invalid template file: {template_file.name}")
129
140
 
141
+ # Log filtering results
142
+ if filter_non_mpm and non_mpm_count > 0:
143
+ self.logger.info(f"Filtered out {non_mpm_count} non-MPM agents")
144
+
130
145
  self.logger.info(
131
- f"Found {len(template_files)} templates, excluded {excluded_count}, deploying {len(filtered_files)}"
146
+ f"Found {len(template_files)} templates, excluded {excluded_count}, filtered {non_mpm_count} non-MPM, deploying {len(filtered_files)}"
132
147
  )
133
148
  return filtered_files
134
149
 
@@ -236,6 +251,46 @@ class AgentDiscoveryService:
236
251
  )
237
252
  return None
238
253
 
254
+ def _is_mpm_agent(self, template_file: Path) -> bool:
255
+ """Check if agent is authored by Claude MPM team.
256
+
257
+ MPM agents must have:
258
+ - An author field containing 'claude mpm', 'claude-mpm', or 'anthropic'
259
+ - A valid agent_version field
260
+
261
+ Args:
262
+ template_file: Path to the agent template JSON file
263
+
264
+ Returns:
265
+ True if this is an MPM agent, False otherwise
266
+ """
267
+ try:
268
+ template_data = json.loads(template_file.read_text())
269
+ metadata = template_data.get("metadata", {})
270
+
271
+ # Check for author field
272
+ author = metadata.get("author", "").lower()
273
+ has_valid_author = any(
274
+ pattern in author
275
+ for pattern in ["claude mpm", "claude-mpm", "anthropic"]
276
+ )
277
+
278
+ # Check for version field
279
+ has_version = bool(template_data.get("agent_version"))
280
+
281
+ if not has_valid_author or not has_version:
282
+ self.logger.debug(
283
+ f"Filtered non-MPM agent {template_file.name}: "
284
+ f"author='{metadata.get('author', 'missing')}', "
285
+ f"version={'present' if has_version else 'missing'}"
286
+ )
287
+
288
+ return has_valid_author and has_version
289
+
290
+ except Exception as e:
291
+ self.logger.debug(f"Error checking if {template_file} is MPM agent: {e}")
292
+ return False # Treat invalid templates as non-MPM
293
+
239
294
  def _is_agent_excluded(
240
295
  self,
241
296
  agent_name: str,
@@ -357,6 +412,50 @@ class AgentDiscoveryService:
357
412
 
358
413
  return bool(re.match(pattern, agent_name))
359
414
 
415
+ def _is_mpm_agent(
416
+ self, template_file: Path, config: Optional[Config] = None
417
+ ) -> bool:
418
+ """Check if agent is authored by Claude MPM team.
419
+
420
+ MPM agents must have:
421
+ - An author field containing configurable MPM patterns (default: 'claude mpm', 'claude-mpm', 'anthropic')
422
+ - A valid agent_version field
423
+
424
+ Args:
425
+ template_file: Path to the agent template JSON file
426
+ config: Configuration object for MPM patterns
427
+
428
+ Returns:
429
+ True if this is an MPM agent, False otherwise
430
+ """
431
+ try:
432
+ template_data = json.loads(template_file.read_text())
433
+ metadata = template_data.get("metadata", {})
434
+
435
+ # Get MPM author patterns from config
436
+ if config:
437
+ mpm_patterns = config.get(
438
+ "agent_deployment.mpm_author_patterns",
439
+ ["claude mpm", "claude-mpm", "anthropic"],
440
+ )
441
+ else:
442
+ mpm_patterns = ["claude mpm", "claude-mpm", "anthropic"]
443
+
444
+ # Check for author field
445
+ author = metadata.get("author", "").lower()
446
+ has_valid_author = any(
447
+ pattern.lower() in author for pattern in mpm_patterns
448
+ )
449
+
450
+ # Check for version field
451
+ has_version = bool(template_data.get("agent_version"))
452
+
453
+ return has_valid_author and has_version
454
+
455
+ except Exception as e:
456
+ self.logger.debug(f"Error checking if {template_file} is MPM agent: {e}")
457
+ return False # Treat invalid templates as non-MPM
458
+
360
459
  def get_discovery_stats(self) -> Dict[str, Any]:
361
460
  """
362
461
  Get statistics about agent discovery.
@@ -8,7 +8,6 @@ maintainability and testability.
8
8
  """
9
9
 
10
10
  import re
11
- from datetime import datetime
12
11
  from pathlib import Path
13
12
  from typing import Any, Dict, Optional
14
13
 
@@ -148,19 +147,38 @@ class AgentFormatConverter:
148
147
  # Extract instructions from YAML content
149
148
  instructions = self._extract_instructions_from_yaml(yaml_content, agent_name)
150
149
 
151
- # Build new YAML frontmatter
152
- current_time = datetime.now().isoformat() + "Z"
150
+ # Map model names to Claude Code format
151
+ model_map = {
152
+ "claude-3-5-sonnet-20241022": "sonnet",
153
+ "claude-3-5-sonnet": "sonnet",
154
+ "claude-3-sonnet": "sonnet",
155
+ "claude-3-haiku": "haiku",
156
+ "claude-3-opus": "opus",
157
+ "sonnet": "sonnet",
158
+ "haiku": "haiku",
159
+ "opus": "opus",
160
+ }
161
+
162
+ mapped_model = model_map.get(model, "sonnet")
163
+
164
+ # Create multiline description with example (Claude Code format)
165
+ multiline_description = f"""{description}
166
+
167
+ <example>
168
+ Context: When you need specialized assistance from the {name} agent.
169
+ user: "I need help with {agent_name.replace('_', ' ').replace('-', ' ')} tasks"
170
+ assistant: "I'll use the {name} agent to provide specialized assistance."
171
+ </example>"""
153
172
 
173
+ # Build new YAML frontmatter - Claude Code compatible format
174
+ # NOTE: Removed tags field and other non-essential fields for Claude Code compatibility
154
175
  new_frontmatter = f"""---
155
176
  name: {name}
156
- description: "{description}"
177
+ description: |
178
+ {self._indent_text(multiline_description, 2)}
179
+ model: {mapped_model}
157
180
  version: "{version}"
158
181
  author: "{author}"
159
- created: "{current_time}"
160
- updated: "{current_time}"
161
- tags: ["{agent_name}", "mpm-framework"]
162
- tools: {tools_list}
163
- model: "{model}"
164
182
  ---
165
183
 
166
184
  """
@@ -447,3 +465,29 @@ model: "{model}"
447
465
  except Exception as e:
448
466
  self.logger.error(f"Failed to convert MD to JSON: {e}")
449
467
  return json.dumps({"error": str(e)})
468
+
469
+ def _indent_text(self, text: str, spaces: int) -> str:
470
+ """
471
+ Indent multiline text with specified number of spaces.
472
+
473
+ Args:
474
+ text: Text to indent
475
+ spaces: Number of spaces for indentation
476
+
477
+ Returns:
478
+ Indented text
479
+ """
480
+ if not text:
481
+ return ""
482
+
483
+ indent = " " * spaces
484
+ lines = text.split("\n")
485
+ indented_lines = []
486
+
487
+ for line in lines:
488
+ if line.strip(): # Non-empty lines get indented
489
+ indented_lines.append(indent + line)
490
+ else: # Empty lines stay empty
491
+ indented_lines.append("")
492
+
493
+ return "\n".join(indented_lines)