claude-mpm 4.3.12__py3-none-any.whl → 4.3.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +414 -28
  3. claude_mpm/agents/templates/data_engineer.json +39 -14
  4. claude_mpm/agents/templates/engineer.json +11 -3
  5. claude_mpm/cli/commands/agent_manager.py +3 -3
  6. claude_mpm/cli/commands/agents.py +2 -2
  7. claude_mpm/cli/commands/aggregate.py +1 -1
  8. claude_mpm/cli/commands/config.py +2 -2
  9. claude_mpm/cli/commands/configure.py +5 -5
  10. claude_mpm/cli/commands/configure_tui.py +7 -7
  11. claude_mpm/cli/commands/dashboard.py +1 -1
  12. claude_mpm/cli/commands/debug.py +5 -5
  13. claude_mpm/cli/commands/mcp.py +1 -1
  14. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  15. claude_mpm/cli/commands/mcp_config.py +7 -10
  16. claude_mpm/cli/commands/mcp_external_commands.py +40 -32
  17. claude_mpm/cli/commands/mcp_install_commands.py +38 -10
  18. claude_mpm/cli/commands/mcp_setup_external.py +143 -102
  19. claude_mpm/cli/commands/monitor.py +2 -2
  20. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  21. claude_mpm/cli/commands/run.py +54 -2
  22. claude_mpm/cli/commands/search.py +41 -34
  23. claude_mpm/cli/interactive/agent_wizard.py +6 -2
  24. claude_mpm/cli/parsers/mcp_parser.py +1 -3
  25. claude_mpm/cli/parsers/search_parser.py +10 -4
  26. claude_mpm/cli/startup_logging.py +158 -5
  27. claude_mpm/cli/utils.py +1 -1
  28. claude_mpm/core/agent_registry.py +2 -2
  29. claude_mpm/core/agent_session_manager.py +8 -8
  30. claude_mpm/core/api_validator.py +6 -4
  31. claude_mpm/core/base_service.py +10 -10
  32. claude_mpm/core/cache.py +5 -5
  33. claude_mpm/core/config_constants.py +1 -1
  34. claude_mpm/core/container.py +1 -1
  35. claude_mpm/core/error_handler.py +2 -2
  36. claude_mpm/core/file_utils.py +1 -1
  37. claude_mpm/core/framework_loader.py +3 -3
  38. claude_mpm/core/hook_manager.py +8 -6
  39. claude_mpm/core/instruction_reinforcement_hook.py +2 -2
  40. claude_mpm/core/interactive_session.py +3 -1
  41. claude_mpm/core/lazy.py +3 -3
  42. claude_mpm/core/log_manager.py +16 -12
  43. claude_mpm/core/logger.py +16 -11
  44. claude_mpm/core/optimized_agent_loader.py +6 -6
  45. claude_mpm/core/output_style_manager.py +1 -1
  46. claude_mpm/core/pm_hook_interceptor.py +3 -3
  47. claude_mpm/core/service_registry.py +1 -1
  48. claude_mpm/core/session_manager.py +11 -9
  49. claude_mpm/core/socketio_pool.py +13 -13
  50. claude_mpm/core/types.py +2 -2
  51. claude_mpm/core/unified_agent_registry.py +2 -2
  52. claude_mpm/core/unified_paths.py +1 -1
  53. claude_mpm/dashboard/analysis_runner.py +4 -4
  54. claude_mpm/dashboard/api/simple_directory.py +1 -1
  55. claude_mpm/generators/agent_profile_generator.py +4 -2
  56. claude_mpm/hooks/base_hook.py +2 -2
  57. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-313.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  63. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  64. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  65. claude_mpm/hooks/claude_hooks/event_handlers.py +12 -12
  66. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -4
  67. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +3 -3
  68. claude_mpm/hooks/claude_hooks/hook_handler_original.py +15 -14
  69. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +4 -4
  70. claude_mpm/hooks/claude_hooks/installer.py +3 -3
  71. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  72. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -3
  73. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  74. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-313.pyc +0 -0
  75. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  76. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  77. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  78. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  79. claude_mpm/hooks/claude_hooks/services/connection_manager.py +8 -5
  80. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +3 -3
  81. claude_mpm/hooks/claude_hooks/services/state_manager.py +8 -7
  82. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  83. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  84. claude_mpm/hooks/memory_integration_hook.py +1 -1
  85. claude_mpm/hooks/tool_call_interceptor.py +2 -2
  86. claude_mpm/models/agent_session.py +7 -5
  87. claude_mpm/scripts/mcp_server.py +0 -0
  88. claude_mpm/scripts/start_activity_logging.py +0 -0
  89. claude_mpm/services/__init__.py +1 -1
  90. claude_mpm/services/agent_capabilities_service.py +1 -1
  91. claude_mpm/services/agents/agent_builder.py +3 -3
  92. claude_mpm/services/agents/deployment/agent_deployment.py +2 -1
  93. claude_mpm/services/agents/deployment/agent_discovery_service.py +9 -3
  94. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
  95. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +3 -1
  96. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  97. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  98. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  99. claude_mpm/services/agents/deployment/agent_template_builder.py +1 -1
  100. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  101. claude_mpm/services/agents/deployment/deployment_wrapper.py +2 -3
  102. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +1 -1
  103. claude_mpm/services/agents/loading/agent_profile_loader.py +5 -3
  104. claude_mpm/services/agents/loading/base_agent_manager.py +2 -2
  105. claude_mpm/services/agents/local_template_manager.py +6 -6
  106. claude_mpm/services/agents/management/agent_management_service.py +3 -3
  107. claude_mpm/services/agents/memory/content_manager.py +3 -3
  108. claude_mpm/services/agents/memory/memory_format_service.py +2 -2
  109. claude_mpm/services/agents/memory/template_generator.py +3 -3
  110. claude_mpm/services/agents/registry/modification_tracker.py +2 -2
  111. claude_mpm/services/async_session_logger.py +3 -3
  112. claude_mpm/services/claude_session_logger.py +4 -4
  113. claude_mpm/services/cli/agent_listing_service.py +3 -1
  114. claude_mpm/services/cli/agent_validation_service.py +2 -0
  115. claude_mpm/services/cli/memory_crud_service.py +11 -6
  116. claude_mpm/services/cli/memory_output_formatter.py +1 -1
  117. claude_mpm/services/cli/session_manager.py +15 -11
  118. claude_mpm/services/core/memory_manager.py +81 -23
  119. claude_mpm/services/core/path_resolver.py +1 -1
  120. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  121. claude_mpm/services/event_aggregator.py +4 -2
  122. claude_mpm/services/event_bus/direct_relay.py +5 -3
  123. claude_mpm/services/event_bus/event_bus.py +3 -3
  124. claude_mpm/services/event_bus/relay.py +6 -4
  125. claude_mpm/services/events/consumers/dead_letter.py +5 -3
  126. claude_mpm/services/events/core.py +3 -3
  127. claude_mpm/services/events/producers/hook.py +6 -6
  128. claude_mpm/services/events/producers/system.py +8 -8
  129. claude_mpm/services/exceptions.py +5 -5
  130. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -3
  131. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  132. claude_mpm/services/hook_installer_service.py +1 -1
  133. claude_mpm/services/infrastructure/context_preservation.py +6 -4
  134. claude_mpm/services/infrastructure/daemon_manager.py +2 -2
  135. claude_mpm/services/infrastructure/logging.py +2 -2
  136. claude_mpm/services/mcp_config_manager.py +175 -30
  137. claude_mpm/services/mcp_gateway/__init__.py +1 -1
  138. claude_mpm/services/mcp_gateway/auto_configure.py +3 -3
  139. claude_mpm/services/mcp_gateway/config/config_loader.py +1 -1
  140. claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
  141. claude_mpm/services/mcp_gateway/core/base.py +2 -2
  142. claude_mpm/services/mcp_gateway/main.py +21 -7
  143. claude_mpm/services/mcp_gateway/registry/tool_registry.py +10 -8
  144. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
  145. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  146. claude_mpm/services/mcp_gateway/server/stdio_server.py +5 -2
  147. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  148. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +7 -5
  149. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +190 -137
  150. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +5 -5
  151. claude_mpm/services/mcp_gateway/tools/hello_world.py +9 -9
  152. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +16 -16
  153. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +16 -16
  154. claude_mpm/services/memory/builder.py +7 -5
  155. claude_mpm/services/memory/indexed_memory.py +4 -4
  156. claude_mpm/services/memory/optimizer.py +6 -6
  157. claude_mpm/services/memory/router.py +3 -3
  158. claude_mpm/services/monitor/daemon.py +1 -1
  159. claude_mpm/services/monitor/daemon_manager.py +6 -6
  160. claude_mpm/services/monitor/event_emitter.py +2 -2
  161. claude_mpm/services/monitor/management/lifecycle.py +3 -1
  162. claude_mpm/services/monitor/server.py +4 -4
  163. claude_mpm/services/monitor_build_service.py +2 -2
  164. claude_mpm/services/port_manager.py +3 -1
  165. claude_mpm/services/response_tracker.py +2 -2
  166. claude_mpm/services/session_management_service.py +3 -2
  167. claude_mpm/services/socketio/client_proxy.py +2 -2
  168. claude_mpm/services/socketio/dashboard_server.py +4 -3
  169. claude_mpm/services/socketio/event_normalizer.py +11 -7
  170. claude_mpm/services/socketio/handlers/base.py +2 -2
  171. claude_mpm/services/socketio/handlers/connection.py +10 -10
  172. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  173. claude_mpm/services/socketio/handlers/hook.py +16 -15
  174. claude_mpm/services/socketio/migration_utils.py +1 -1
  175. claude_mpm/services/socketio/monitor_client.py +5 -5
  176. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  177. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  178. claude_mpm/services/socketio/server/core.py +7 -5
  179. claude_mpm/services/socketio/server/eventbus_integration.py +18 -12
  180. claude_mpm/services/socketio/server/main.py +13 -13
  181. claude_mpm/services/socketio_client_manager.py +4 -4
  182. claude_mpm/services/system_instructions_service.py +2 -2
  183. claude_mpm/services/utility_service.py +5 -2
  184. claude_mpm/services/version_control/branch_strategy.py +2 -2
  185. claude_mpm/services/version_control/git_operations.py +22 -20
  186. claude_mpm/services/version_control/semantic_versioning.py +3 -3
  187. claude_mpm/services/version_control/version_parser.py +7 -5
  188. claude_mpm/services/visualization/mermaid_generator.py +3 -1
  189. claude_mpm/storage/state_storage.py +1 -1
  190. claude_mpm/tools/code_tree_analyzer.py +23 -18
  191. claude_mpm/tools/code_tree_builder.py +2 -2
  192. claude_mpm/tools/code_tree_events.py +10 -8
  193. claude_mpm/tools/socketio_debug.py +3 -3
  194. claude_mpm/utils/agent_dependency_loader.py +6 -2
  195. claude_mpm/utils/dependency_strategies.py +8 -3
  196. claude_mpm/utils/environment_context.py +1 -1
  197. claude_mpm/utils/error_handler.py +2 -2
  198. claude_mpm/utils/file_utils.py +1 -1
  199. claude_mpm/utils/log_cleanup.py +21 -7
  200. claude_mpm/validation/agent_validator.py +2 -2
  201. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/METADATA +1 -1
  202. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/RECORD +204 -191
  203. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/WHEEL +0 -0
  204. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/entry_points.txt +0 -0
  205. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/licenses/LICENSE +0 -0
  206. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.14.dist-info}/top_level.txt +0 -0
@@ -34,6 +34,7 @@ class MCPSearchInterface:
34
34
  """Initialize the MCP gateway connection."""
35
35
  try:
36
36
  from claude_mpm.services.mcp_gateway import MCPGatewayService
37
+
37
38
  self.mcp_gateway = self.container.resolve(MCPGatewayService)
38
39
  if not self.mcp_gateway:
39
40
  self.mcp_gateway = MCPGatewayService()
@@ -48,13 +49,13 @@ class MCPSearchInterface:
48
49
  limit: int = 10,
49
50
  similarity_threshold: float = 0.3,
50
51
  file_extensions: Optional[list] = None,
51
- language: Optional[str] = None
52
+ language: Optional[str] = None,
52
53
  ) -> Dict[str, Any]:
53
54
  """Search code using semantic similarity."""
54
55
  params = {
55
56
  "query": query,
56
57
  "limit": limit,
57
- "similarity_threshold": similarity_threshold
58
+ "similarity_threshold": similarity_threshold,
58
59
  }
59
60
 
60
61
  if file_extensions:
@@ -69,45 +70,43 @@ class MCPSearchInterface:
69
70
  file_path: str,
70
71
  function_name: Optional[str] = None,
71
72
  limit: int = 10,
72
- similarity_threshold: float = 0.3
73
+ similarity_threshold: float = 0.3,
73
74
  ) -> Dict[str, Any]:
74
75
  """Find code similar to a specific file or function."""
75
76
  params = {
76
77
  "file_path": file_path,
77
78
  "limit": limit,
78
- "similarity_threshold": similarity_threshold
79
+ "similarity_threshold": similarity_threshold,
79
80
  }
80
81
 
81
82
  if function_name:
82
83
  params["function_name"] = function_name
83
84
 
84
- return await self._call_mcp_tool("mcp__mcp-vector-search__search_similar", params)
85
+ return await self._call_mcp_tool(
86
+ "mcp__mcp-vector-search__search_similar", params
87
+ )
85
88
 
86
89
  async def search_context(
87
- self,
88
- description: str,
89
- focus_areas: Optional[list] = None,
90
- limit: int = 10
90
+ self, description: str, focus_areas: Optional[list] = None, limit: int = 10
91
91
  ) -> Dict[str, Any]:
92
92
  """Search for code based on contextual description."""
93
- params = {
94
- "description": description,
95
- "limit": limit
96
- }
93
+ params = {"description": description, "limit": limit}
97
94
 
98
95
  if focus_areas:
99
96
  params["focus_areas"] = focus_areas
100
97
 
101
- return await self._call_mcp_tool("mcp__mcp-vector-search__search_context", params)
98
+ return await self._call_mcp_tool(
99
+ "mcp__mcp-vector-search__search_context", params
100
+ )
102
101
 
103
102
  async def get_status(self) -> Dict[str, Any]:
104
103
  """Get project indexing status and statistics."""
105
- return await self._call_mcp_tool("mcp__mcp-vector-search__get_project_status", {})
104
+ return await self._call_mcp_tool(
105
+ "mcp__mcp-vector-search__get_project_status", {}
106
+ )
106
107
 
107
108
  async def index_project(
108
- self,
109
- force: bool = False,
110
- file_extensions: Optional[list] = None
109
+ self, force: bool = False, file_extensions: Optional[list] = None
111
110
  ) -> Dict[str, Any]:
112
111
  """Index or reindex the project codebase."""
113
112
  params = {"force": force}
@@ -115,16 +114,19 @@ class MCPSearchInterface:
115
114
  if file_extensions:
116
115
  params["file_extensions"] = file_extensions
117
116
 
118
- return await self._call_mcp_tool("mcp__mcp-vector-search__index_project", params)
117
+ return await self._call_mcp_tool(
118
+ "mcp__mcp-vector-search__index_project", params
119
+ )
119
120
 
120
- async def _call_mcp_tool(self, tool_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
121
+ async def _call_mcp_tool(
122
+ self, tool_name: str, params: Dict[str, Any]
123
+ ) -> Dict[str, Any]:
121
124
  """Call an MCP tool through the gateway."""
122
125
  if not self.mcp_gateway:
123
126
  await self.initialize()
124
127
 
125
128
  try:
126
- result = await self.mcp_gateway.call_tool(tool_name, params)
127
- return result
129
+ return await self.mcp_gateway.call_tool(tool_name, params)
128
130
  except Exception as e:
129
131
  return {"error": str(e)}
130
132
 
@@ -161,9 +163,11 @@ def display_search_results(results: Dict[str, Any], output_format: str = "rich")
161
163
  # Show snippet if available
162
164
  if result.get("snippet"):
163
165
  snippet_panel = Panel(
164
- Syntax(result["snippet"], result.get("language", "python"), theme="monokai"),
166
+ Syntax(
167
+ result["snippet"], result.get("language", "python"), theme="monokai"
168
+ ),
165
169
  title=f"[cyan]{file_path}[/cyan]",
166
- border_style="dim"
170
+ border_style="dim",
167
171
  )
168
172
  console.print(snippet_panel)
169
173
 
@@ -172,7 +176,7 @@ def display_search_results(results: Dict[str, Any], output_format: str = "rich")
172
176
  # Show statistics if available
173
177
  if "stats" in results:
174
178
  stats = results["stats"]
175
- console.print(f"\n[bold]Statistics:[/bold]")
179
+ console.print("\n[bold]Statistics:[/bold]")
176
180
  console.print(f" Total indexed files: {stats.get('total_files', 0)}")
177
181
  console.print(f" Total indexed functions: {stats.get('total_functions', 0)}")
178
182
  console.print(f" Index last updated: {stats.get('last_updated', 'Unknown')}")
@@ -206,7 +210,7 @@ async def search_command(
206
210
  function: Optional[str],
207
211
  focus: tuple,
208
212
  force: bool,
209
- output_json: bool
213
+ output_json: bool,
210
214
  ):
211
215
  """
212
216
  Search the codebase using semantic search powered by mcp-vector-search.
@@ -228,8 +232,7 @@ async def search_command(
228
232
  if index:
229
233
  console.print("[cyan]Indexing project...[/cyan]")
230
234
  result = await search.index_project(
231
- force=force,
232
- file_extensions=list(extensions) if extensions else None
235
+ force=force, file_extensions=list(extensions) if extensions else None
233
236
  )
234
237
  if "error" not in result:
235
238
  console.print("[green]✓ Project indexed successfully[/green]")
@@ -244,7 +247,7 @@ async def search_command(
244
247
  file_path=similar,
245
248
  function_name=function,
246
249
  limit=limit,
247
- similarity_threshold=threshold
250
+ similarity_threshold=threshold,
248
251
  )
249
252
  display_search_results(result, output_format)
250
253
 
@@ -252,7 +255,7 @@ async def search_command(
252
255
  result = await search.search_context(
253
256
  description=context,
254
257
  focus_areas=list(focus) if focus else None,
255
- limit=limit
258
+ limit=limit,
256
259
  )
257
260
  display_search_results(result, output_format)
258
261
 
@@ -262,17 +265,21 @@ async def search_command(
262
265
  limit=limit,
263
266
  similarity_threshold=threshold,
264
267
  file_extensions=list(extensions) if extensions else None,
265
- language=language
268
+ language=language,
266
269
  )
267
270
  display_search_results(result, output_format)
268
271
 
269
272
  else:
270
- console.print("[yellow]No search operation specified. Use --help for options.[/yellow]")
273
+ console.print(
274
+ "[yellow]No search operation specified. Use --help for options.[/yellow]"
275
+ )
271
276
 
272
277
  except Exception as e:
273
278
  console.print(f"[red]Search failed: {e}[/red]")
274
279
  if not output_json:
275
- console.print("[dim]Tip: Make sure the project is indexed with --index first[/dim]")
280
+ console.print(
281
+ "[dim]Tip: Make sure the project is indexed with --index first[/dim]"
282
+ )
276
283
  sys.exit(1)
277
284
 
278
285
 
@@ -282,4 +289,4 @@ def main():
282
289
 
283
290
 
284
291
  if __name__ == "__main__":
285
- main()
292
+ main()
@@ -600,7 +600,9 @@ class AgentWizard:
600
600
  tier="project",
601
601
  )
602
602
 
603
- def _manage_single_agent(self, template: LocalAgentTemplate) -> Tuple[bool, str]:
603
+ def _manage_single_agent(
604
+ self, template: LocalAgentTemplate
605
+ ) -> Tuple[bool, str]:
604
606
  """Manage a single agent."""
605
607
  print(f"\n🔧 Managing Agent: {template.agent_id}")
606
608
  print(f" Name: {template.metadata.get('name', template.agent_id)}")
@@ -815,7 +817,9 @@ class AgentWizard:
815
817
  return self.manager.project_agents_dir / f"{template.agent_id}.json"
816
818
  return self.manager.user_agents_dir / f"{template.agent_id}.json"
817
819
 
818
- def _interactive_delete_menu(self, templates: list) -> Tuple[bool, str]:
820
+ def _interactive_delete_menu(
821
+ self, templates: list
822
+ ) -> Tuple[bool, str]:
819
823
  """Interactive deletion menu for multiple agents."""
820
824
  print("\n🗑️ Delete Agents")
821
825
  print("=" * 50)
@@ -189,9 +189,7 @@ def add_mcp_subparser(subparsers) -> argparse.ArgumentParser:
189
189
  help="External service action (default: list)",
190
190
  )
191
191
  external_mcp_parser.add_argument(
192
- "--force",
193
- action="store_true",
194
- help="Force overwrite existing configuration"
192
+ "--force", action="store_true", help="Force overwrite existing configuration"
195
193
  )
196
194
 
197
195
  return mcp_parser
@@ -8,7 +8,9 @@ import argparse
8
8
  from typing import Optional
9
9
 
10
10
 
11
- def add_search_subparser(subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser:
11
+ def add_search_subparser(
12
+ subparsers: argparse._SubParsersAction,
13
+ ) -> argparse.ArgumentParser:
12
14
  """
13
15
  Add the search command parser.
14
16
 
@@ -188,7 +190,7 @@ Examples:
188
190
  return search_parser
189
191
 
190
192
 
191
- def validate_search_args(args: argparse.Namespace) -> Optional[str]:
193
+ def validate_search_args(args: argparse.Namespace) -> Optional[str]: # noqa: PLR0911
192
194
  """
193
195
  Validate search command arguments.
194
196
 
@@ -211,7 +213,11 @@ def validate_search_args(args: argparse.Namespace) -> Optional[str]:
211
213
  return "Limit cannot exceed 50"
212
214
 
213
215
  # Check that function is only used with --similar
214
- if hasattr(args, "function") and args.function and not getattr(args, "similar", None):
216
+ if (
217
+ hasattr(args, "function")
218
+ and args.function
219
+ and not getattr(args, "similar", None)
220
+ ):
215
221
  return "--function can only be used with --similar"
216
222
 
217
223
  # Check that focus is only used with --context
@@ -236,4 +242,4 @@ def validate_search_args(args: argparse.Namespace) -> Optional[str]:
236
242
  if not has_operation:
237
243
  return "No search operation specified. Use --help for options."
238
244
 
239
- return None
245
+ return None
@@ -14,6 +14,7 @@ DESIGN DECISIONS:
14
14
  - Capture all startup logs to timestamped files for analysis
15
15
  """
16
16
 
17
+ import asyncio
17
18
  import logging
18
19
  import shutil
19
20
  import subprocess
@@ -69,7 +70,7 @@ def log_memory_stats(logger=None, prefix="Memory Usage"):
69
70
  f"{prefix}: RSS={rss_mb:.1f}MB, System={memory_percent:.1f}%"
70
71
  )
71
72
  return {"rss_mb": rss_mb, "vms_mb": None, "percent": memory_percent}
72
- except:
73
+ except Exception:
73
74
  logger.info(f"{prefix}: RSS={rss_mb:.1f}MB")
74
75
  return {"rss_mb": rss_mb, "vms_mb": None, "percent": None}
75
76
  else:
@@ -82,7 +83,7 @@ def log_memory_stats(logger=None, prefix="Memory Usage"):
82
83
  f"System={memory_percent:.1f}%"
83
84
  )
84
85
  return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": memory_percent}
85
- except:
86
+ except Exception:
86
87
  logger.info(f"{prefix}: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB")
87
88
  return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": None}
88
89
 
@@ -114,12 +115,10 @@ class StartupStatusLogger:
114
115
  if mcp_executable:
115
116
  self.logger.info(f"MCP Server: Installed at {mcp_executable}")
116
117
 
117
- # Try to get version
118
+ # Try to get version (only log if version is found)
118
119
  version = self._get_mcp_version(mcp_executable)
119
120
  if version:
120
121
  self.logger.info(f"MCP Server: Version {version}")
121
- else:
122
- self.logger.info("MCP Server: Version unknown")
123
122
  else:
124
123
  self.logger.info("MCP Server: Not found in PATH")
125
124
 
@@ -632,6 +631,156 @@ def cleanup_old_startup_logs(
632
631
  return deleted_count
633
632
 
634
633
 
634
+ async def trigger_vector_search_indexing(project_root: Optional[Path] = None) -> None:
635
+ """
636
+ Trigger mcp-vector-search indexing in the background.
637
+
638
+ This function attempts to start the mcp-vector-search indexing process
639
+ asynchronously so it doesn't block startup. If the service is not available,
640
+ it fails silently.
641
+
642
+ Args:
643
+ project_root: Root directory for the project (defaults to cwd)
644
+ """
645
+ logger = get_logger("cli")
646
+
647
+ if project_root is None:
648
+ project_root = Path.cwd()
649
+
650
+ try:
651
+ # Check if mcp-vector-search is available
652
+ from ..services.mcp_config_manager import MCPConfigManager
653
+
654
+ manager = MCPConfigManager()
655
+ vector_search_path = manager.detect_service_path("mcp-vector-search")
656
+
657
+ if not vector_search_path:
658
+ logger.debug("mcp-vector-search not found, skipping indexing")
659
+ return
660
+
661
+ # Build the command based on the service configuration
662
+ if "python" in vector_search_path:
663
+ # Using Python interpreter directly
664
+ cmd = [
665
+ vector_search_path,
666
+ "-m",
667
+ "mcp_vector_search.cli",
668
+ "index",
669
+ str(project_root),
670
+ ]
671
+ else:
672
+ # Using installed binary
673
+ cmd = [vector_search_path, "index", str(project_root)]
674
+
675
+ logger.info(
676
+ "MCP Vector Search: Starting background indexing for improved code search"
677
+ )
678
+
679
+ # Start the indexing process in the background
680
+ # We use subprocess.Popen instead of run to avoid blocking
681
+ process = await asyncio.create_subprocess_exec(
682
+ *cmd,
683
+ stdout=asyncio.subprocess.DEVNULL,
684
+ stderr=asyncio.subprocess.DEVNULL,
685
+ cwd=str(project_root),
686
+ )
687
+
688
+ # Don't wait for completion - let it run in the background
689
+ logger.debug(
690
+ f"MCP Vector Search: Indexing process started (PID: {process.pid})"
691
+ )
692
+
693
+ except ImportError:
694
+ logger.debug(
695
+ "MCP config manager not available, skipping vector search indexing"
696
+ )
697
+ except Exception as e:
698
+ # Don't let indexing failures prevent startup
699
+ logger.debug(f"Failed to start vector search indexing: {e}")
700
+
701
+
702
+ def start_vector_search_indexing(project_root: Optional[Path] = None) -> None:
703
+ """
704
+ Synchronous wrapper to trigger vector search indexing.
705
+
706
+ This creates a new event loop if needed to run the async indexing function.
707
+ Falls back to subprocess.Popen if async fails.
708
+
709
+ Args:
710
+ project_root: Root directory for the project (defaults to cwd)
711
+ """
712
+ logger = get_logger("cli")
713
+
714
+ try:
715
+ # Try to get the current event loop
716
+ loop = asyncio.get_running_loop()
717
+ # If we're in an event loop, create a task
718
+ # Store reference to avoid RUF006 warning
719
+ _ = loop.create_task(trigger_vector_search_indexing(project_root))
720
+ except RuntimeError:
721
+ # No event loop running, try async approach first
722
+ try:
723
+ asyncio.run(trigger_vector_search_indexing(project_root))
724
+ except Exception as e:
725
+ # Fallback to simple subprocess approach
726
+ logger.debug(f"Async indexing failed, trying subprocess: {e}")
727
+ _start_vector_search_subprocess(project_root)
728
+
729
+
730
+ def _start_vector_search_subprocess(project_root: Optional[Path] = None) -> None:
731
+ """
732
+ Fallback method to start vector search indexing using subprocess.Popen.
733
+
734
+ Args:
735
+ project_root: Root directory for the project (defaults to cwd)
736
+ """
737
+ logger = get_logger("cli")
738
+
739
+ if project_root is None:
740
+ project_root = Path.cwd()
741
+
742
+ try:
743
+ from ..services.mcp_config_manager import MCPConfigManager
744
+
745
+ manager = MCPConfigManager()
746
+ vector_search_path = manager.detect_service_path("mcp-vector-search")
747
+
748
+ if not vector_search_path:
749
+ logger.debug("mcp-vector-search not found, skipping indexing")
750
+ return
751
+
752
+ # Build the command
753
+ if "python" in vector_search_path:
754
+ cmd = [
755
+ vector_search_path,
756
+ "-m",
757
+ "mcp_vector_search.cli",
758
+ "index",
759
+ str(project_root),
760
+ ]
761
+ else:
762
+ cmd = [vector_search_path, "index", str(project_root)]
763
+
764
+ logger.info(
765
+ "MCP Vector Search: Starting background indexing for improved code search"
766
+ )
767
+
768
+ # Start the indexing process in the background
769
+ process = subprocess.Popen(
770
+ cmd,
771
+ stdout=subprocess.DEVNULL,
772
+ stderr=subprocess.DEVNULL,
773
+ cwd=str(project_root),
774
+ )
775
+
776
+ logger.debug(
777
+ f"MCP Vector Search: Indexing process started (PID: {process.pid})"
778
+ )
779
+
780
+ except Exception as e:
781
+ logger.debug(f"Failed to start vector search indexing: {e}")
782
+
783
+
635
784
  def get_latest_startup_log(project_root: Optional[Path] = None) -> Optional[Path]:
636
785
  """
637
786
  Get the path to the most recent startup log file.
@@ -680,6 +829,10 @@ def log_startup_status(monitor_mode: bool = False, websocket_port: int = 8765) -
680
829
  # Log monitor setup status
681
830
  status_logger.log_monitor_setup_status(monitor_mode, websocket_port)
682
831
 
832
+ # Trigger vector search indexing in the background after MCP is configured
833
+ # This will run asynchronously and not block startup
834
+ start_vector_search_indexing()
835
+
683
836
  except Exception as e:
684
837
  # Don't let logging failures prevent startup
685
838
  logger = get_logger("cli")
claude_mpm/cli/utils.py CHANGED
@@ -106,7 +106,7 @@ def get_agent_versions_display() -> Optional[str]:
106
106
  base_version_tuple
107
107
  )
108
108
  output_lines.append(f"\n Base Agent Version: {base_version_str}")
109
- except:
109
+ except Exception:
110
110
  pass
111
111
 
112
112
  # Check for agents needing migration
@@ -14,7 +14,7 @@ This module provides:
14
14
  import contextlib
15
15
  import warnings
16
16
  from dataclasses import dataclass
17
- from datetime import datetime
17
+ from datetime import datetime, timezone
18
18
  from pathlib import Path
19
19
  from typing import Any, Dict, List, Optional, Set
20
20
 
@@ -379,7 +379,7 @@ class AgentRegistryAdapter:
379
379
  }
380
380
 
381
381
  nickname = nicknames.get(agent_name, agent_name.title())
382
- today = datetime.now().strftime("%Y-%m-%d")
382
+ today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
383
383
 
384
384
  return f"""**{nickname}**: {task}
385
385
 
@@ -3,7 +3,7 @@
3
3
  import json
4
4
  import uuid
5
5
  from collections import defaultdict
6
- from datetime import datetime, timedelta
6
+ from datetime import datetime, timedelta, timezone
7
7
  from pathlib import Path
8
8
  from typing import Dict, Optional
9
9
 
@@ -53,10 +53,10 @@ class AgentSessionManager:
53
53
  if not self.session_locks.get(session_id, False):
54
54
  # Check if session is still fresh (not too old)
55
55
  created = datetime.fromisoformat(session_data["created_at"])
56
- if datetime.now() - created < timedelta(hours=1):
56
+ if datetime.now(timezone.utc) - created < timedelta(hours=1):
57
57
  # Use this session
58
58
  self.session_locks[session_id] = True
59
- session_data["last_used"] = datetime.now().isoformat()
59
+ session_data["last_used"] = datetime.now(timezone.utc).isoformat()
60
60
  session_data["use_count"] += 1
61
61
  logger.info(f"Reusing session {session_id} for {agent_type} agent")
62
62
  return session_id
@@ -85,8 +85,8 @@ class AgentSessionManager:
85
85
  session_data = {
86
86
  "id": session_id,
87
87
  "agent_type": agent_type,
88
- "created_at": datetime.now().isoformat(),
89
- "last_used": datetime.now().isoformat(),
88
+ "created_at": datetime.now(timezone.utc).isoformat(),
89
+ "last_used": datetime.now(timezone.utc).isoformat(),
90
90
  "use_count": 0,
91
91
  "tasks_completed": [],
92
92
  }
@@ -122,7 +122,7 @@ class AgentSessionManager:
122
122
  sessions[session_id]["tasks_completed"].append(
123
123
  {
124
124
  "task": task[:100], # Truncate long tasks
125
- "timestamp": datetime.now().isoformat(),
125
+ "timestamp": datetime.now(timezone.utc).isoformat(),
126
126
  "success": success,
127
127
  }
128
128
  )
@@ -135,7 +135,7 @@ class AgentSessionManager:
135
135
  Args:
136
136
  max_age_hours: Maximum age in hours
137
137
  """
138
- now = datetime.now()
138
+ now = datetime.now(timezone.utc)
139
139
  max_age = timedelta(hours=max_age_hours)
140
140
 
141
141
  for agent_type in list(self.agent_sessions.keys()):
@@ -207,7 +207,7 @@ class AgentSessionManager:
207
207
  try:
208
208
  data = {
209
209
  "agent_sessions": dict(self.agent_sessions),
210
- "updated_at": datetime.now().isoformat(),
210
+ "updated_at": datetime.now(timezone.utc).isoformat(),
211
211
  }
212
212
  with open(session_file, "w") as f:
213
213
  json.dump(data, f, indent=2)
@@ -84,7 +84,7 @@ class APIKeyValidator:
84
84
 
85
85
  return not bool(self.errors), self.errors, self.warnings
86
86
 
87
- def _validate_openai_key(self, api_key: str) -> bool:
87
+ def _validate_openai_key(self, api_key: str) -> bool: # noqa: PLR0911
88
88
  """Validate OpenAI API key.
89
89
 
90
90
  Args:
@@ -133,7 +133,7 @@ class APIKeyValidator:
133
133
  self.errors.append(f"❌ OpenAI API validation failed with error: {e}")
134
134
  return False
135
135
 
136
- def _validate_anthropic_key(self, api_key: str) -> bool:
136
+ def _validate_anthropic_key(self, api_key: str) -> bool: # noqa: PLR0911
137
137
  """Validate Anthropic API key.
138
138
 
139
139
  Args:
@@ -196,7 +196,7 @@ class APIKeyValidator:
196
196
  self.errors.append(f"❌ Anthropic API validation failed with error: {e}")
197
197
  return False
198
198
 
199
- def _validate_github_token(self, token: str) -> bool:
199
+ def _validate_github_token(self, token: str) -> bool: # noqa: PLR0911
200
200
  """Validate GitHub personal access token.
201
201
 
202
202
  Args:
@@ -244,7 +244,9 @@ class APIKeyValidator:
244
244
  self.errors.append(f"❌ GitHub token validation failed with error: {e}")
245
245
  return False
246
246
 
247
- def _validate_custom_api(self, api_name: str, validation_config: Dict) -> bool:
247
+ def _validate_custom_api(
248
+ self, api_name: str, validation_config: Dict
249
+ ) -> bool:
248
250
  """Validate a custom API key based on configuration.
249
251
 
250
252
  Args: