claude-mpm 4.2.44__py3-none-any.whl → 4.2.51__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 (148) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +43 -1
  3. claude_mpm/agents/INSTRUCTIONS.md +75 -1
  4. claude_mpm/agents/WORKFLOW.md +46 -1
  5. claude_mpm/agents/frontmatter_validator.py +20 -12
  6. claude_mpm/agents/templates/nextjs_engineer.json +277 -0
  7. claude_mpm/agents/templates/python_engineer.json +289 -0
  8. claude_mpm/agents/templates/react_engineer.json +11 -3
  9. claude_mpm/agents/templates/security.json +50 -9
  10. claude_mpm/cli/commands/agents.py +2 -2
  11. claude_mpm/cli/commands/uninstall.py +1 -2
  12. claude_mpm/cli/interactive/agent_wizard.py +3 -3
  13. claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
  14. claude_mpm/cli/parsers/agents_parser.py +1 -1
  15. claude_mpm/constants.py +1 -1
  16. claude_mpm/core/error_handler.py +2 -4
  17. claude_mpm/core/file_utils.py +4 -12
  18. claude_mpm/core/log_manager.py +8 -5
  19. claude_mpm/core/logger.py +1 -1
  20. claude_mpm/core/logging_utils.py +6 -6
  21. claude_mpm/core/unified_agent_registry.py +18 -4
  22. claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
  23. claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
  24. claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
  25. claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
  26. claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
  27. claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
  28. claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
  29. claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
  30. claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
  31. claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
  32. claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
  33. claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
  34. claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
  35. claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
  36. claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
  37. claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
  38. claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
  39. claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
  40. claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
  41. claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
  42. claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
  43. claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
  44. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  45. claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
  46. claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
  47. claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
  48. claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
  49. claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
  50. claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
  51. claude_mpm/dashboard/static/built/components/code-viewer.js +2 -1076
  52. claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
  53. claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
  54. claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
  55. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  56. claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
  57. claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
  58. claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
  59. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  60. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  61. claude_mpm/dashboard/static/built/components/nav-bar.js +145 -0
  62. claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
  63. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  64. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -465
  65. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  66. claude_mpm/dashboard/static/built/connection-manager.js +536 -0
  67. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  68. claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
  69. claude_mpm/dashboard/static/built/react/events.js +30 -0
  70. claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
  71. claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
  72. claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
  73. claude_mpm/dashboard/static/built/shared/logger.js +385 -0
  74. claude_mpm/dashboard/static/built/shared/page-structure.js +251 -0
  75. claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
  76. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  77. claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
  78. claude_mpm/dashboard/static/css/dashboard.css +28 -5
  79. claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
  80. claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
  81. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  82. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  83. claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
  84. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  85. claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
  86. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  87. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  88. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  89. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  90. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  91. claude_mpm/dashboard/static/dist/react/events.js +30 -0
  92. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  93. claude_mpm/dashboard/static/events.html +607 -0
  94. claude_mpm/dashboard/static/index.html +713 -0
  95. claude_mpm/dashboard/static/js/components/activity-tree.js +3 -17
  96. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
  97. claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
  98. claude_mpm/dashboard/static/js/components/build-tracker.js +8 -0
  99. claude_mpm/dashboard/static/js/components/code-viewer.js +306 -66
  100. claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
  101. claude_mpm/dashboard/static/js/components/event-viewer.js +39 -2
  102. claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
  103. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
  104. claude_mpm/dashboard/static/js/components/socket-manager.js +4 -0
  105. claude_mpm/dashboard/static/js/components/ui-state-manager.js +285 -85
  106. claude_mpm/dashboard/static/js/components/working-directory.js +3 -0
  107. claude_mpm/dashboard/static/js/dashboard.js +61 -33
  108. claude_mpm/dashboard/static/js/socket-client.js +12 -8
  109. claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
  110. claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
  111. claude_mpm/dashboard/static/legacy/activity.html +736 -0
  112. claude_mpm/dashboard/static/legacy/agents.html +786 -0
  113. claude_mpm/dashboard/static/legacy/files.html +747 -0
  114. claude_mpm/dashboard/static/legacy/tools.html +831 -0
  115. claude_mpm/dashboard/static/monitors-index.html +218 -0
  116. claude_mpm/dashboard/static/monitors.html +431 -0
  117. claude_mpm/dashboard/static/production/events.html +659 -0
  118. claude_mpm/dashboard/static/production/main.html +715 -0
  119. claude_mpm/dashboard/static/production/monitors.html +483 -0
  120. claude_mpm/dashboard/static/socket.io.min.js +7 -0
  121. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
  122. claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
  123. claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
  124. claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
  125. claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
  126. claude_mpm/dashboard/templates/index.html +79 -9
  127. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +1 -1
  128. claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -0
  129. claude_mpm/services/agents/deployment/agent_template_builder.py +25 -8
  130. claude_mpm/services/agents/deployment/agent_validator.py +3 -0
  131. claude_mpm/services/agents/deployment/validation/template_validator.py +13 -4
  132. claude_mpm/services/agents/local_template_manager.py +2 -6
  133. claude_mpm/services/monitor/daemon.py +1 -2
  134. claude_mpm/services/monitor/daemon_manager.py +2 -5
  135. claude_mpm/services/monitor/event_emitter.py +2 -2
  136. claude_mpm/services/monitor/handlers/code_analysis.py +4 -6
  137. claude_mpm/services/monitor/handlers/hooks.py +2 -4
  138. claude_mpm/services/monitor/server.py +27 -4
  139. claude_mpm/tools/code_tree_analyzer.py +2 -2
  140. {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/METADATA +1 -1
  141. {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/RECORD +146 -81
  142. claude_mpm/dashboard/static/test-browser-monitor.html +0 -470
  143. claude_mpm/dashboard/static/test-simple.html +0 -97
  144. /claude_mpm/dashboard/static/{test_debug.html → test-archive/test_debug.html} +0 -0
  145. {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/WHEEL +0 -0
  146. {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/entry_points.txt +0 -0
  147. {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/licenses/LICENSE +0 -0
  148. {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.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 UI State Manager v2 from source with nuclear browser log handling
494
- return loadModule('/static/js/components/ui-state-manager.js?v=2.0-NUCLEAR');
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
- // Now load main components in parallel
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/dist/dashboard.js'),
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'), // Code viewer now includes file change tracking
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
- task = loop.create_task(self._async_emit(namespace, event, data))
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")),
@@ -267,17 +267,21 @@ class AgentTemplateBuilder:
267
267
  # Include tools field only if agent is clearly restricted (missing core tools or very few tools)
268
268
  not has_core_tools or len(agent_tools) < 6
269
269
 
270
- # Build YAML frontmatter using Claude Code's compatible format
271
- # ONLY include fields that Claude Code recognizes
270
+ # Build YAML frontmatter with all relevant metadata from JSON template
271
+ # Include all fields that are useful for agent management and functionality
272
272
  #
273
- # CLAUDE CODE COMPATIBLE FORMAT:
273
+ # COMPREHENSIVE AGENT FRONTMATTER FORMAT:
274
274
  # - name: kebab-case agent name (required)
275
275
  # - description: when/why to use this agent with examples (required, multiline)
276
276
  # - model: mapped model name (required)
277
+ # - type: agent type for categorization and functionality (optional but important)
278
+ # - category: organizational category (optional)
277
279
  # - color: visual identifier (optional)
278
280
  # - version: agent version for update tracking (optional)
279
281
  # - author: creator information (optional)
280
- # NOTE: tags field REMOVED - not supported by Claude Code
282
+ # - created_at: creation timestamp (optional)
283
+ # - updated_at: last update timestamp (optional)
284
+ # - tags: list of tags for search and categorization (optional)
281
285
  frontmatter_lines = [
282
286
  "---",
283
287
  f"name: {claude_code_name}",
@@ -291,15 +295,28 @@ class AgentTemplateBuilder:
291
295
  # Add model field (required for Claude Code)
292
296
  frontmatter_lines.append(f"model: {model}")
293
297
 
294
- # Add optional metadata (excluding tags field completely)
298
+ # Add type field (important for agent categorization)
299
+ if agent_type and agent_type != "general":
300
+ frontmatter_lines.append(f"type: {agent_type}")
301
+
302
+ # Add optional metadata fields
295
303
  if metadata.get("color"):
296
304
  frontmatter_lines.append(f"color: {metadata['color']}")
297
- if (
298
- agent_version and agent_version != "1.0.0"
299
- ): # Only include non-default versions
305
+ if metadata.get("category"):
306
+ frontmatter_lines.append(f"category: {metadata['category']}")
307
+ # Always include version field to prevent deployment comparison issues
308
+ if agent_version:
300
309
  frontmatter_lines.append(f'version: "{agent_version}"')
301
310
  if metadata.get("author"):
302
311
  frontmatter_lines.append(f'author: "{metadata["author"]}"')
312
+ if metadata.get("created_at"):
313
+ frontmatter_lines.append(f"created_at: {metadata['created_at']}")
314
+ if metadata.get("updated_at"):
315
+ frontmatter_lines.append(f"updated_at: {metadata['updated_at']}")
316
+ # Add tags as comma-separated string if they exist (consistent with tools format)
317
+ if metadata.get("tags") and isinstance(metadata["tags"], list):
318
+ tags_str = ",".join(metadata["tags"])
319
+ frontmatter_lines.append(f"tags: {tags_str}")
303
320
 
304
321
  frontmatter_lines.extend(
305
322
  [
@@ -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 not isinstance(tags, list):
194
- result.add_error("Tags should be a list", field_name="metadata.tags")
195
- elif len(tags) == 0:
196
- result.add_warning("No tags specified", field_name="metadata.tags")
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
- template = LocalAgentTemplate(
256
+ return LocalAgentTemplate(
260
257
  agent_id=agent_id,
261
258
  agent_version="1.0.0",
262
259
  author=author,
@@ -283,7 +280,6 @@ class LocalAgentTemplateManager:
283
280
  parent_agent=parent_agent,
284
281
  )
285
282
 
286
- return template
287
283
 
288
284
  def save_local_template(
289
285
  self, template: LocalAgentTemplate, tier: Optional[str] = None
@@ -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
- success = self.daemon_manager.start_daemon_subprocess()
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
- if self._kill_claude_mpm_processes():
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}")
@@ -579,7 +576,7 @@ class DaemonManager:
579
576
  stderr=subprocess.STDOUT if self.log_file else subprocess.DEVNULL,
580
577
  start_new_session=True, # Create new process group
581
578
  close_fds=(
582
- False if self.log_file else True
579
+ not self.log_file
583
580
  ), # Keep log file open if redirecting
584
581
  env=env, # Pass modified environment
585
582
  )
@@ -115,7 +115,7 @@ class AsyncEventEmitter:
115
115
  event: str,
116
116
  data: Dict[str, Any],
117
117
  force_http: bool = False,
118
- endpoint: str = None,
118
+ endpoint: Optional[str] = None,
119
119
  ) -> bool:
120
120
  """
121
121
  Emit event with optimal routing (direct calls vs HTTP).
@@ -186,7 +186,7 @@ class AsyncEventEmitter:
186
186
  return False
187
187
 
188
188
  async def _emit_http(
189
- self, namespace: str, event: str, data: Dict[str, Any], endpoint: str = None
189
+ self, namespace: str, event: str, data: Dict[str, Any], endpoint: Optional[str] = None
190
190
  ) -> bool:
191
191
  """Emit event via HTTP with connection pooling."""
192
192
  if not self._http_session:
@@ -248,14 +248,14 @@ class CodeAnalysisHandler:
248
248
  self.logger.info("All analysis cache cleared")
249
249
  elif cache_type == "file":
250
250
  keys_to_remove = [
251
- k for k in self.analysis_cache.keys() if k.startswith("file:")
251
+ k for k in self.analysis_cache if k.startswith("file:")
252
252
  ]
253
253
  for key in keys_to_remove:
254
254
  del self.analysis_cache[key]
255
255
  self.logger.info("File analysis cache cleared")
256
256
  elif cache_type == "directory":
257
257
  keys_to_remove = [
258
- k for k in self.analysis_cache.keys() if k.startswith("dir:")
258
+ k for k in self.analysis_cache if k.startswith("dir:")
259
259
  ]
260
260
  for key in keys_to_remove:
261
261
  del self.analysis_cache[key]
@@ -281,10 +281,9 @@ class CodeAnalysisHandler:
281
281
  try:
282
282
  # Run analysis in thread pool to avoid blocking
283
283
  loop = asyncio.get_event_loop()
284
- result = await loop.run_in_executor(
284
+ return await loop.run_in_executor(
285
285
  None, self.analyzer.analyze_file, file_path
286
286
  )
287
- return result
288
287
 
289
288
  except Exception as e:
290
289
  self.logger.error(f"Error in async file analysis: {e}")
@@ -305,10 +304,9 @@ class CodeAnalysisHandler:
305
304
  try:
306
305
  # Run tree building in thread pool
307
306
  loop = asyncio.get_event_loop()
308
- result = await loop.run_in_executor(
307
+ return await loop.run_in_executor(
309
308
  None, self.builder.build_tree, dir_path, max_depth
310
309
  )
311
- return result
312
310
 
313
311
  except Exception as e:
314
312
  self.logger.error(f"Error in async directory tree building: {e}")
@@ -416,7 +416,7 @@ class HookHandler:
416
416
  Returns:
417
417
  Processed event data
418
418
  """
419
- processed = {
419
+ return {
420
420
  "type": data.get("type", "hook"),
421
421
  "subtype": data.get("subtype", "unknown"),
422
422
  "timestamp": data.get("timestamp", asyncio.get_event_loop().time()),
@@ -428,7 +428,6 @@ class HookHandler:
428
428
  "original_event": data, # Keep original for debugging
429
429
  }
430
430
 
431
- return processed
432
431
 
433
432
  def _process_hook_event(self, data: Dict) -> Dict:
434
433
  """Process and normalize hook event data.
@@ -439,7 +438,7 @@ class HookHandler:
439
438
  Returns:
440
439
  Processed event data
441
440
  """
442
- processed = {
441
+ return {
443
442
  "type": data.get("type"),
444
443
  "timestamp": data.get("timestamp"),
445
444
  "session_id": data.get("session_id"),
@@ -448,7 +447,6 @@ class HookHandler:
448
447
  "processed_at": asyncio.get_event_loop().time(),
449
448
  }
450
449
 
451
- return processed
452
450
 
453
451
  def _update_session_tracking(self, session_id: str, event: Dict):
454
452
  """Update session tracking with new event.
@@ -15,6 +15,7 @@ DESIGN DECISIONS:
15
15
  """
16
16
 
17
17
  import asyncio
18
+ import contextlib
18
19
  import os
19
20
  import threading
20
21
  import time
@@ -348,7 +349,7 @@ class UnifiedMonitorServer:
348
349
  data = await request.json()
349
350
 
350
351
  # Extract event data
351
- namespace = data.get("namespace", "hook")
352
+ data.get("namespace", "hook")
352
353
  event = data.get("event", "claude_event")
353
354
  event_data = data.get("data", {})
354
355
 
@@ -508,6 +509,19 @@ class UnifiedMonitorServer:
508
509
  {"working_directory": os.getcwd(), "success": True}
509
510
  )
510
511
 
512
+ # Monitor page routes
513
+ async def monitor_page_handler(request):
514
+ """Serve monitor HTML pages."""
515
+ page_name = request.match_info.get("page", "agents")
516
+ static_dir = dashboard_dir / "static"
517
+ file_path = static_dir / f"{page_name}.html"
518
+
519
+ if file_path.exists() and file_path.is_file():
520
+ with open(file_path, encoding="utf-8") as f:
521
+ content = f.read()
522
+ return web.Response(text=content, content_type="text/html")
523
+ return web.Response(text="Page not found", status=404)
524
+
511
525
  # Register routes
512
526
  self.app.router.add_get("/", dashboard_index)
513
527
  self.app.router.add_get("/health", health_check)
@@ -518,6 +532,17 @@ class UnifiedMonitorServer:
518
532
  self.app.router.add_post("/api/events", api_events_handler)
519
533
  self.app.router.add_post("/api/file", api_file_handler)
520
534
 
535
+ # Monitor page routes
536
+ self.app.router.add_get("/monitor", lambda r: monitor_page_handler(r))
537
+ self.app.router.add_get(
538
+ "/monitor/agents", lambda r: monitor_page_handler(r)
539
+ )
540
+ self.app.router.add_get("/monitor/tools", lambda r: monitor_page_handler(r))
541
+ self.app.router.add_get("/monitor/files", lambda r: monitor_page_handler(r))
542
+ self.app.router.add_get(
543
+ "/monitor/events", lambda r: monitor_page_handler(r)
544
+ )
545
+
521
546
  # Static files with cache busting headers for development
522
547
  static_dir = dashboard_dir / "static"
523
548
  if static_dir.exists():
@@ -666,10 +691,8 @@ class UnifiedMonitorServer:
666
691
  # Cancel heartbeat task if running
667
692
  if self.heartbeat_task and not self.heartbeat_task.done():
668
693
  self.heartbeat_task.cancel()
669
- try:
694
+ with contextlib.suppress(asyncio.CancelledError):
670
695
  await self.heartbeat_task
671
- except asyncio.CancelledError:
672
- pass
673
696
  self.logger.debug("Heartbeat task cancelled")
674
697
 
675
698
  # Close the Socket.IO server first to stop accepting new connections
@@ -1739,7 +1739,7 @@ class CodeTreeAnalyzer:
1739
1739
 
1740
1740
  # Filter only very specific internal patterns
1741
1741
  # Be more conservative - only filter obvious internal handlers
1742
- if name_lower.startswith("handle_") or name_lower.startswith("on_"):
1742
+ if name_lower.startswith(("handle_", "on_")):
1743
1743
  return True
1744
1744
 
1745
1745
  # Filter Python magic methods except important ones
@@ -1756,7 +1756,7 @@ class CodeTreeAnalyzer:
1756
1756
  return node.name not in important_magic
1757
1757
 
1758
1758
  # Filter very generic getters/setters only if they're trivial
1759
- if (name_lower.startswith("get_") or name_lower.startswith("set_")) and len(
1759
+ if (name_lower.startswith(("get_", "set_"))) and len(
1760
1760
  node.name
1761
1761
  ) <= 8:
1762
1762
  return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.2.44
3
+ Version: 4.2.51
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team