claude-mpm 4.3.12__py3-none-any.whl → 4.3.13__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 (199) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +390 -28
  3. claude_mpm/agents/templates/data_engineer.json +39 -14
  4. claude_mpm/cli/commands/agent_manager.py +3 -3
  5. claude_mpm/cli/commands/agents.py +2 -2
  6. claude_mpm/cli/commands/aggregate.py +1 -1
  7. claude_mpm/cli/commands/config.py +2 -2
  8. claude_mpm/cli/commands/configure.py +5 -5
  9. claude_mpm/cli/commands/configure_tui.py +7 -7
  10. claude_mpm/cli/commands/dashboard.py +1 -1
  11. claude_mpm/cli/commands/debug.py +5 -5
  12. claude_mpm/cli/commands/mcp.py +1 -1
  13. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  14. claude_mpm/cli/commands/mcp_config.py +7 -10
  15. claude_mpm/cli/commands/mcp_external_commands.py +40 -32
  16. claude_mpm/cli/commands/mcp_install_commands.py +38 -10
  17. claude_mpm/cli/commands/mcp_setup_external.py +143 -102
  18. claude_mpm/cli/commands/monitor.py +2 -2
  19. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  20. claude_mpm/cli/commands/run.py +46 -2
  21. claude_mpm/cli/commands/search.py +41 -34
  22. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  23. claude_mpm/cli/parsers/mcp_parser.py +1 -3
  24. claude_mpm/cli/parsers/search_parser.py +10 -4
  25. claude_mpm/cli/startup_logging.py +3 -5
  26. claude_mpm/cli/utils.py +1 -1
  27. claude_mpm/core/agent_registry.py +12 -8
  28. claude_mpm/core/agent_session_manager.py +8 -8
  29. claude_mpm/core/api_validator.py +4 -4
  30. claude_mpm/core/base_service.py +10 -10
  31. claude_mpm/core/cache.py +5 -5
  32. claude_mpm/core/config_constants.py +1 -1
  33. claude_mpm/core/container.py +1 -1
  34. claude_mpm/core/error_handler.py +2 -2
  35. claude_mpm/core/file_utils.py +1 -1
  36. claude_mpm/core/framework_loader.py +3 -3
  37. claude_mpm/core/hook_manager.py +8 -6
  38. claude_mpm/core/instruction_reinforcement_hook.py +2 -2
  39. claude_mpm/core/interactive_session.py +1 -1
  40. claude_mpm/core/lazy.py +3 -3
  41. claude_mpm/core/log_manager.py +16 -12
  42. claude_mpm/core/logger.py +16 -11
  43. claude_mpm/core/logging_config.py +4 -2
  44. claude_mpm/core/oneshot_session.py +1 -1
  45. claude_mpm/core/optimized_agent_loader.py +6 -6
  46. claude_mpm/core/output_style_manager.py +1 -1
  47. claude_mpm/core/pm_hook_interceptor.py +3 -3
  48. claude_mpm/core/service_registry.py +1 -1
  49. claude_mpm/core/session_manager.py +11 -9
  50. claude_mpm/core/socketio_pool.py +13 -13
  51. claude_mpm/core/types.py +2 -2
  52. claude_mpm/core/unified_agent_registry.py +2 -2
  53. claude_mpm/core/unified_paths.py +1 -1
  54. claude_mpm/dashboard/analysis_runner.py +4 -4
  55. claude_mpm/dashboard/api/simple_directory.py +1 -1
  56. claude_mpm/generators/agent_profile_generator.py +4 -2
  57. claude_mpm/hooks/base_hook.py +2 -2
  58. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  59. claude_mpm/hooks/claude_hooks/event_handlers.py +12 -12
  60. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -4
  61. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +3 -3
  62. claude_mpm/hooks/claude_hooks/hook_handler_original.py +15 -14
  63. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +4 -4
  64. claude_mpm/hooks/claude_hooks/installer.py +3 -3
  65. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  66. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -3
  67. claude_mpm/hooks/claude_hooks/services/connection_manager.py +5 -5
  68. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +3 -3
  69. claude_mpm/hooks/claude_hooks/services/state_manager.py +8 -7
  70. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  71. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  72. claude_mpm/hooks/memory_integration_hook.py +1 -1
  73. claude_mpm/hooks/tool_call_interceptor.py +2 -2
  74. claude_mpm/models/agent_session.py +5 -5
  75. claude_mpm/services/__init__.py +1 -1
  76. claude_mpm/services/agent_capabilities_service.py +1 -1
  77. claude_mpm/services/agents/agent_builder.py +3 -3
  78. claude_mpm/services/agents/deployment/agent_deployment.py +2 -1
  79. claude_mpm/services/agents/deployment/agent_discovery_service.py +9 -3
  80. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +7 -5
  81. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +3 -1
  82. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  83. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  84. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  85. claude_mpm/services/agents/deployment/agent_template_builder.py +1 -1
  86. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  87. claude_mpm/services/agents/deployment/deployment_wrapper.py +2 -3
  88. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +1 -1
  89. claude_mpm/services/agents/loading/agent_profile_loader.py +5 -3
  90. claude_mpm/services/agents/loading/base_agent_manager.py +2 -2
  91. claude_mpm/services/agents/local_template_manager.py +6 -6
  92. claude_mpm/services/agents/management/agent_management_service.py +3 -3
  93. claude_mpm/services/agents/memory/content_manager.py +3 -3
  94. claude_mpm/services/agents/memory/memory_format_service.py +2 -2
  95. claude_mpm/services/agents/memory/template_generator.py +3 -3
  96. claude_mpm/services/agents/registry/__init__.py +1 -1
  97. claude_mpm/services/agents/registry/modification_tracker.py +2 -2
  98. claude_mpm/services/async_session_logger.py +3 -3
  99. claude_mpm/services/claude_session_logger.py +4 -4
  100. claude_mpm/services/cli/agent_listing_service.py +1 -1
  101. claude_mpm/services/cli/agent_validation_service.py +1 -0
  102. claude_mpm/services/cli/memory_crud_service.py +11 -6
  103. claude_mpm/services/cli/memory_output_formatter.py +1 -1
  104. claude_mpm/services/cli/session_manager.py +15 -11
  105. claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
  106. claude_mpm/services/core/memory_manager.py +81 -23
  107. claude_mpm/services/core/path_resolver.py +2 -2
  108. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  109. claude_mpm/services/event_aggregator.py +4 -2
  110. claude_mpm/services/event_bus/direct_relay.py +5 -3
  111. claude_mpm/services/event_bus/event_bus.py +3 -3
  112. claude_mpm/services/event_bus/relay.py +6 -4
  113. claude_mpm/services/events/consumers/dead_letter.py +5 -3
  114. claude_mpm/services/events/core.py +3 -3
  115. claude_mpm/services/events/producers/hook.py +6 -6
  116. claude_mpm/services/events/producers/system.py +8 -8
  117. claude_mpm/services/exceptions.py +5 -5
  118. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -3
  119. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  120. claude_mpm/services/hook_installer_service.py +1 -1
  121. claude_mpm/services/infrastructure/context_preservation.py +6 -4
  122. claude_mpm/services/infrastructure/daemon_manager.py +2 -2
  123. claude_mpm/services/infrastructure/logging.py +2 -2
  124. claude_mpm/services/mcp_config_manager.py +175 -30
  125. claude_mpm/services/mcp_gateway/__init__.py +1 -1
  126. claude_mpm/services/mcp_gateway/auto_configure.py +3 -3
  127. claude_mpm/services/mcp_gateway/config/config_loader.py +1 -1
  128. claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
  129. claude_mpm/services/mcp_gateway/core/base.py +2 -2
  130. claude_mpm/services/mcp_gateway/main.py +21 -7
  131. claude_mpm/services/mcp_gateway/registry/tool_registry.py +10 -8
  132. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
  133. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  134. claude_mpm/services/mcp_gateway/server/stdio_server.py +4 -3
  135. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  136. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +7 -5
  137. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +190 -137
  138. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +5 -5
  139. claude_mpm/services/mcp_gateway/tools/hello_world.py +9 -9
  140. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +16 -16
  141. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +17 -17
  142. claude_mpm/services/memory/builder.py +7 -5
  143. claude_mpm/services/memory/indexed_memory.py +4 -4
  144. claude_mpm/services/memory/optimizer.py +6 -6
  145. claude_mpm/services/memory/router.py +3 -3
  146. claude_mpm/services/monitor/daemon.py +1 -1
  147. claude_mpm/services/monitor/daemon_manager.py +6 -6
  148. claude_mpm/services/monitor/event_emitter.py +2 -2
  149. claude_mpm/services/monitor/handlers/file.py +1 -1
  150. claude_mpm/services/monitor/management/lifecycle.py +1 -1
  151. claude_mpm/services/monitor/server.py +4 -4
  152. claude_mpm/services/monitor_build_service.py +2 -2
  153. claude_mpm/services/port_manager.py +2 -2
  154. claude_mpm/services/response_tracker.py +2 -2
  155. claude_mpm/services/session_management_service.py +3 -2
  156. claude_mpm/services/socketio/client_proxy.py +2 -2
  157. claude_mpm/services/socketio/dashboard_server.py +4 -3
  158. claude_mpm/services/socketio/event_normalizer.py +12 -8
  159. claude_mpm/services/socketio/handlers/base.py +2 -2
  160. claude_mpm/services/socketio/handlers/connection.py +10 -10
  161. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  162. claude_mpm/services/socketio/handlers/file.py +1 -1
  163. claude_mpm/services/socketio/handlers/git.py +1 -1
  164. claude_mpm/services/socketio/handlers/hook.py +16 -15
  165. claude_mpm/services/socketio/migration_utils.py +1 -1
  166. claude_mpm/services/socketio/monitor_client.py +5 -5
  167. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  168. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  169. claude_mpm/services/socketio/server/core.py +7 -5
  170. claude_mpm/services/socketio/server/eventbus_integration.py +18 -11
  171. claude_mpm/services/socketio/server/main.py +13 -13
  172. claude_mpm/services/socketio_client_manager.py +4 -4
  173. claude_mpm/services/system_instructions_service.py +2 -2
  174. claude_mpm/services/ticket_services/validation_service.py +1 -1
  175. claude_mpm/services/utility_service.py +5 -2
  176. claude_mpm/services/version_control/branch_strategy.py +2 -2
  177. claude_mpm/services/version_control/git_operations.py +22 -20
  178. claude_mpm/services/version_control/semantic_versioning.py +3 -3
  179. claude_mpm/services/version_control/version_parser.py +7 -5
  180. claude_mpm/services/visualization/mermaid_generator.py +1 -1
  181. claude_mpm/storage/state_storage.py +1 -1
  182. claude_mpm/tools/code_tree_analyzer.py +19 -18
  183. claude_mpm/tools/code_tree_builder.py +2 -2
  184. claude_mpm/tools/code_tree_events.py +10 -8
  185. claude_mpm/tools/socketio_debug.py +3 -3
  186. claude_mpm/utils/agent_dependency_loader.py +2 -2
  187. claude_mpm/utils/dependency_strategies.py +8 -3
  188. claude_mpm/utils/environment_context.py +2 -2
  189. claude_mpm/utils/error_handler.py +2 -2
  190. claude_mpm/utils/file_utils.py +1 -1
  191. claude_mpm/utils/imports.py +1 -1
  192. claude_mpm/utils/log_cleanup.py +21 -7
  193. claude_mpm/validation/agent_validator.py +2 -2
  194. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/METADATA +1 -1
  195. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/RECORD +199 -199
  196. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/WHEEL +0 -0
  197. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/entry_points.txt +0 -0
  198. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/licenses/LICENSE +0 -0
  199. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.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,7 @@ 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(self, template: LocalAgentTemplate) -> Tuple[bool, str]: # noqa: PLR0911
604
604
  """Manage a single agent."""
605
605
  print(f"\n🔧 Managing Agent: {template.agent_id}")
606
606
  print(f" Name: {template.metadata.get('name', template.agent_id)}")
@@ -815,7 +815,7 @@ class AgentWizard:
815
815
  return self.manager.project_agents_dir / f"{template.agent_id}.json"
816
816
  return self.manager.user_agents_dir / f"{template.agent_id}.json"
817
817
 
818
- def _interactive_delete_menu(self, templates: list) -> Tuple[bool, str]:
818
+ def _interactive_delete_menu(self, templates: list) -> Tuple[bool, str]: # noqa: PLR0911
819
819
  """Interactive deletion menu for multiple agents."""
820
820
  print("\n🗑️ Delete Agents")
821
821
  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
@@ -69,7 +69,7 @@ def log_memory_stats(logger=None, prefix="Memory Usage"):
69
69
  f"{prefix}: RSS={rss_mb:.1f}MB, System={memory_percent:.1f}%"
70
70
  )
71
71
  return {"rss_mb": rss_mb, "vms_mb": None, "percent": memory_percent}
72
- except:
72
+ except Exception:
73
73
  logger.info(f"{prefix}: RSS={rss_mb:.1f}MB")
74
74
  return {"rss_mb": rss_mb, "vms_mb": None, "percent": None}
75
75
  else:
@@ -82,7 +82,7 @@ def log_memory_stats(logger=None, prefix="Memory Usage"):
82
82
  f"System={memory_percent:.1f}%"
83
83
  )
84
84
  return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": memory_percent}
85
- except:
85
+ except Exception:
86
86
  logger.info(f"{prefix}: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB")
87
87
  return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": None}
88
88
 
@@ -114,12 +114,10 @@ class StartupStatusLogger:
114
114
  if mcp_executable:
115
115
  self.logger.info(f"MCP Server: Installed at {mcp_executable}")
116
116
 
117
- # Try to get version
117
+ # Try to get version (only log if version is found)
118
118
  version = self._get_mcp_version(mcp_executable)
119
119
  if version:
120
120
  self.logger.info(f"MCP Server: Version {version}")
121
- else:
122
- self.logger.info("MCP Server: Version unknown")
123
121
  else:
124
122
  self.logger.info("MCP Server: Not found in PATH")
125
123
 
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,23 +14,27 @@ 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
 
21
21
  # Import from the unified agent registry system
22
+ from .unified_agent_registry import AgentMetadata as UnifiedAgentMetadata
22
23
  from .unified_agent_registry import (
23
- AgentMetadata as UnifiedAgentMetadata,
24
24
  AgentTier,
25
25
  AgentType,
26
- discover_agents as unified_discover_agents,
27
- get_agent as unified_get_agent,
26
+ )
27
+ from .unified_agent_registry import discover_agents as unified_discover_agents
28
+ from .unified_agent_registry import get_agent as unified_get_agent
29
+ from .unified_agent_registry import (
28
30
  get_agent_registry,
29
- get_core_agents as unified_get_core_agents,
30
- get_registry_stats as unified_get_registry_stats,
31
+ )
32
+ from .unified_agent_registry import get_core_agents as unified_get_core_agents
33
+ from .unified_agent_registry import get_registry_stats as unified_get_registry_stats
34
+ from .unified_agent_registry import (
31
35
  get_specialized_agents as unified_get_specialized_agents,
32
- list_agents as unified_list_agents,
33
36
  )
37
+ from .unified_agent_registry import list_agents as unified_list_agents
34
38
 
35
39
  try:
36
40
  from ..core.logger import get_logger
@@ -379,7 +383,7 @@ class AgentRegistryAdapter:
379
383
  }
380
384
 
381
385
  nickname = nicknames.get(agent_name, agent_name.title())
382
- today = datetime.now().strftime("%Y-%m-%d")
386
+ today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
383
387
 
384
388
  return f"""**{nickname}**: {task}
385
389
 
@@ -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,7 @@ 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(self, api_name: str, validation_config: Dict) -> bool: # noqa: PLR0911
248
248
  """Validate a custom API key based on configuration.
249
249
 
250
250
  Args:
@@ -26,7 +26,7 @@ import traceback
26
26
  from abc import ABC, abstractmethod
27
27
  from contextlib import asynccontextmanager
28
28
  from dataclasses import dataclass, field
29
- from datetime import datetime, timedelta
29
+ from datetime import datetime, timedelta, timezone
30
30
  from pathlib import Path
31
31
  from typing import Any, Dict, List, Optional
32
32
 
@@ -144,7 +144,7 @@ class BaseService(LoggerMixin, ABC):
144
144
  self._health = ServiceHealth(
145
145
  status="unknown",
146
146
  message="Service not started",
147
- timestamp=datetime.now().isoformat(),
147
+ timestamp=datetime.now(timezone.utc).isoformat(),
148
148
  )
149
149
  self._metrics = ServiceMetrics()
150
150
  self._last_health_check: Optional[float] = None
@@ -199,7 +199,7 @@ class BaseService(LoggerMixin, ABC):
199
199
  def uptime(self) -> Optional[float]:
200
200
  """Get service uptime in seconds."""
201
201
  if self._start_time and self._running:
202
- return (datetime.now() - self._start_time).total_seconds()
202
+ return (datetime.now(timezone.utc) - self._start_time).total_seconds()
203
203
  return None
204
204
 
205
205
  @property
@@ -237,7 +237,7 @@ class BaseService(LoggerMixin, ABC):
237
237
  self._health = ServiceHealth(
238
238
  status="unhealthy",
239
239
  message=f"Startup failed: {e!s}",
240
- timestamp=datetime.now().isoformat(),
240
+ timestamp=datetime.now(timezone.utc).isoformat(),
241
241
  checks={"startup": False},
242
242
  )
243
243
 
@@ -268,13 +268,13 @@ class BaseService(LoggerMixin, ABC):
268
268
 
269
269
  # Mark as running
270
270
  self._running = True
271
- self._start_time = datetime.now()
271
+ self._start_time = datetime.now(timezone.utc)
272
272
 
273
273
  # Update health status
274
274
  self._health = ServiceHealth(
275
275
  status="healthy",
276
276
  message="Service started successfully",
277
- timestamp=datetime.now().isoformat(),
277
+ timestamp=datetime.now(timezone.utc).isoformat(),
278
278
  checks={"startup": True},
279
279
  metrics=self._get_health_metrics() if self._enable_enhanced else {},
280
280
  )
@@ -338,7 +338,7 @@ class BaseService(LoggerMixin, ABC):
338
338
  self._health = ServiceHealth(
339
339
  status="unknown",
340
340
  message="Service stopped",
341
- timestamp=datetime.now().isoformat(),
341
+ timestamp=datetime.now(timezone.utc).isoformat(),
342
342
  checks={"running": False},
343
343
  )
344
344
 
@@ -388,7 +388,7 @@ class BaseService(LoggerMixin, ABC):
388
388
  self._health = ServiceHealth(
389
389
  status=status,
390
390
  message=message,
391
- timestamp=datetime.now().isoformat(),
391
+ timestamp=datetime.now(timezone.utc).isoformat(),
392
392
  checks=checks,
393
393
  metrics={
394
394
  "uptime": self.uptime,
@@ -404,7 +404,7 @@ class BaseService(LoggerMixin, ABC):
404
404
  self._health = ServiceHealth(
405
405
  status="unhealthy",
406
406
  message=f"Health check error: {e!s}",
407
- timestamp=datetime.now().isoformat(),
407
+ timestamp=datetime.now(timezone.utc).isoformat(),
408
408
  checks={"health_check_error": True},
409
409
  )
410
410
  return self._health
@@ -586,7 +586,7 @@ class BaseService(LoggerMixin, ABC):
586
586
  return ServiceHealth(
587
587
  status="degraded",
588
588
  message="Service circuit breaker is open",
589
- timestamp=datetime.now().isoformat(),
589
+ timestamp=datetime.now(timezone.utc).isoformat(),
590
590
  checks={"circuit_breaker": False},
591
591
  metrics=self._get_health_metrics(),
592
592
  )
claude_mpm/core/cache.py CHANGED
@@ -19,7 +19,7 @@ import pickle
19
19
  import threading
20
20
  from collections import OrderedDict
21
21
  from dataclasses import dataclass, field
22
- from datetime import datetime
22
+ from datetime import datetime, timezone
23
23
  from typing import Any, Callable, Dict, Optional, TypeVar, Union
24
24
 
25
25
  from ..core.logger import get_logger
@@ -43,12 +43,12 @@ class CacheEntry:
43
43
  """Check if entry has expired based on TTL."""
44
44
  if self.ttl is None:
45
45
  return False
46
- age = (datetime.now() - self.created_at).total_seconds()
46
+ age = (datetime.now(timezone.utc) - self.created_at).total_seconds()
47
47
  return age > self.ttl
48
48
 
49
49
  def touch(self):
50
50
  """Update last access time and increment counter."""
51
- self.last_accessed = datetime.now()
51
+ self.last_accessed = datetime.now(timezone.utc)
52
52
  self.access_count += 1
53
53
 
54
54
 
@@ -129,13 +129,13 @@ class FileSystemCache:
129
129
  # Rough estimate using JSON serialization
130
130
  try:
131
131
  return len(json.dumps(value))
132
- except:
132
+ except Exception:
133
133
  return 1000 # Default estimate
134
134
  else:
135
135
  # Use pickle for size estimation
136
136
  try:
137
137
  return len(pickle.dumps(value))
138
- except:
138
+ except Exception:
139
139
  return 100 # Default small size
140
140
 
141
141
  def _evict_lru(self):
@@ -99,7 +99,7 @@ class ConfigConstants:
99
99
  cls._config_service = config_service
100
100
 
101
101
  @classmethod
102
- def get_timeout(cls, timeout_type: str) -> int:
102
+ def get_timeout(cls, timeout_type: str) -> int: # noqa: PLR0911
103
103
  """
104
104
  Get timeout value by type.
105
105
 
@@ -636,7 +636,7 @@ class DIContainer(IServiceContainer):
636
636
  if reg_type.__name__ == param_type:
637
637
  param_type = reg_type
638
638
  break
639
- except:
639
+ except Exception:
640
640
  # If we can't resolve, skip this parameter
641
641
  if param.default != param.empty:
642
642
  kwargs[param_name] = param.default
@@ -384,7 +384,7 @@ def with_error_handling(
384
384
  if callable(fallback_value):
385
385
  try:
386
386
  fb_value = fallback_value(*args, **kwargs)
387
- except:
387
+ except Exception:
388
388
  fb_value = None
389
389
 
390
390
  return handle_error(
@@ -428,7 +428,7 @@ def safe_operation(
428
428
  if callable(fallback_value):
429
429
  try:
430
430
  return fallback_value(*args, **kwargs)
431
- except:
431
+ except Exception:
432
432
  return None
433
433
  return fallback_value
434
434
 
@@ -652,7 +652,7 @@ def file_lock(filepath: Union[str, Path], timeout: float = 5.0):
652
652
  fcntl.flock(lock_handle, fcntl.LOCK_UN)
653
653
  lock_handle.close()
654
654
  lock_file.unlink(missing_ok=True)
655
- except:
655
+ except Exception:
656
656
  pass
657
657
 
658
658
 
@@ -6,7 +6,7 @@ import logging
6
6
  import os
7
7
  import platform
8
8
  import time
9
- from datetime import datetime
9
+ from datetime import datetime, timezone
10
10
  from pathlib import Path
11
11
  from typing import Any, Dict, Optional
12
12
 
@@ -1432,7 +1432,7 @@ Extract tickets from these patterns:
1432
1432
 
1433
1433
  try:
1434
1434
  # Get current datetime with timezone awareness
1435
- now = datetime.now()
1435
+ now = datetime.now(timezone.utc)
1436
1436
 
1437
1437
  # Try to get timezone info - fallback to UTC offset if timezone name not available
1438
1438
  try:
@@ -1470,7 +1470,7 @@ Extract tickets from these patterns:
1470
1470
  # Fallback to basic date if enhanced datetime fails
1471
1471
  self.logger.debug(f"Error generating enhanced datetime context: {e}")
1472
1472
  context_lines.append(
1473
- f"**Today's Date**: {datetime.now().strftime('%Y-%m-%d')}\n"
1473
+ f"**Today's Date**: {datetime.now(timezone.utc).strftime('%Y-%m-%d')}\n"
1474
1474
  )
1475
1475
 
1476
1476
  try: