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
@@ -19,7 +19,7 @@ import queue
19
19
  import subprocess
20
20
  import threading
21
21
  import uuid
22
- from datetime import datetime
22
+ from datetime import datetime, timezone
23
23
  from typing import Any, Dict, Optional
24
24
 
25
25
  from ..core.logger import get_logger
@@ -106,7 +106,9 @@ class HookManager:
106
106
  hook_event = {
107
107
  "hook_event_name": hook_type,
108
108
  "session_id": self.session_id,
109
- "timestamp": hook_data.get("timestamp", datetime.utcnow().isoformat()),
109
+ "timestamp": hook_data.get(
110
+ "timestamp", datetime.now(timezone.utc).isoformat()
111
+ ),
110
112
  **event_data,
111
113
  }
112
114
 
@@ -182,7 +184,7 @@ class HookManager:
182
184
  {
183
185
  "tool_name": tool_name,
184
186
  "tool_args": tool_args or {},
185
- "timestamp": datetime.utcnow().isoformat(),
187
+ "timestamp": datetime.now(timezone.utc).isoformat(),
186
188
  },
187
189
  )
188
190
 
@@ -205,7 +207,7 @@ class HookManager:
205
207
  "tool_name": tool_name,
206
208
  "exit_code": exit_code,
207
209
  "result": str(result) if result is not None else None,
208
- "timestamp": datetime.utcnow().isoformat(),
210
+ "timestamp": datetime.now(timezone.utc).isoformat(),
209
211
  },
210
212
  )
211
213
 
@@ -220,7 +222,7 @@ class HookManager:
220
222
  """
221
223
  return self._trigger_hook_event(
222
224
  "UserPromptSubmit",
223
- {"prompt": prompt, "timestamp": datetime.utcnow().isoformat()},
225
+ {"prompt": prompt, "timestamp": datetime.now(timezone.utc).isoformat()},
224
226
  )
225
227
 
226
228
  def _trigger_hook_event(self, hook_type: str, event_data: Dict[str, Any]) -> bool:
@@ -250,7 +252,7 @@ class HookManager:
250
252
  hook_data = {
251
253
  "hook_type": hook_type,
252
254
  "event_data": event_data,
253
- "timestamp": datetime.utcnow().isoformat(),
255
+ "timestamp": datetime.now(timezone.utc).isoformat(),
254
256
  }
255
257
 
256
258
  # Try to queue without blocking
@@ -18,7 +18,7 @@ The hook works by:
18
18
  """
19
19
 
20
20
  import threading
21
- from datetime import datetime
21
+ from datetime import datetime, timezone
22
22
  from typing import Any, Dict, List, Optional
23
23
 
24
24
  from ..core.logger import get_logger
@@ -197,7 +197,7 @@ class InstructionReinforcementHook:
197
197
  "enabled": self.enabled,
198
198
  "test_mode": self.test_mode,
199
199
  "injection_interval": self.injection_interval,
200
- "timestamp": datetime.utcnow().isoformat(),
200
+ "timestamp": datetime.now(timezone.utc).isoformat(),
201
201
  }
202
202
 
203
203
  def reset_counters(self):
@@ -154,7 +154,7 @@ class InteractiveSession:
154
154
  self.logger.error(error_msg)
155
155
  return False, {}
156
156
 
157
- def handle_interactive_input(self, environment: Dict[str, Any]) -> bool:
157
+ def handle_interactive_input(self, environment: Dict[str, Any]) -> bool: # noqa: PLR0911
158
158
  """Handle the interactive input/output loop.
159
159
 
160
160
  Launches Claude and manages the interactive session using either
claude_mpm/core/lazy.py CHANGED
@@ -16,7 +16,7 @@ import functools
16
16
  import threading
17
17
  import time
18
18
  from dataclasses import dataclass, field
19
- from datetime import datetime
19
+ from datetime import datetime, timezone
20
20
  from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar
21
21
 
22
22
  from ..core.logger import get_logger
@@ -121,7 +121,7 @@ class LazyService(Generic[T]):
121
121
  # Track initialization
122
122
  start_time = time.time()
123
123
  if self._metrics.first_access is None:
124
- self._metrics.first_access = datetime.now()
124
+ self._metrics.first_access = datetime.now(timezone.utc)
125
125
 
126
126
  try:
127
127
  self._logger.debug(f"Initializing lazy service: {self._name}")
@@ -417,7 +417,7 @@ class AsyncLazyService(Generic[T]):
417
417
 
418
418
  start_time = time.time()
419
419
  if self._metrics.first_access is None:
420
- self._metrics.first_access = datetime.now()
420
+ self._metrics.first_access = datetime.now(timezone.utc)
421
421
 
422
422
  try:
423
423
  self._logger.debug(f"Async initializing: {self._name}")
@@ -18,7 +18,7 @@ import asyncio
18
18
  import json
19
19
  import logging
20
20
  import os
21
- from datetime import datetime, timedelta
21
+ from datetime import datetime, timedelta, timezone
22
22
  from pathlib import Path
23
23
  from queue import Full, Queue
24
24
  from threading import Lock, Thread
@@ -191,7 +191,7 @@ class LogManager:
191
191
  finally:
192
192
  self.write_queue.task_done()
193
193
 
194
- except:
194
+ except Exception:
195
195
  continue # Timeout or other error, continue loop
196
196
 
197
197
  def _process_cleanup_queue(self):
@@ -211,7 +211,7 @@ class LogManager:
211
211
  finally:
212
212
  self.cleanup_queue.task_done()
213
213
 
214
- except:
214
+ except Exception:
215
215
  continue # Timeout or other error, continue loop
216
216
 
217
217
  async def setup_logging(self, log_type: str) -> Path:
@@ -296,7 +296,7 @@ class LogManager:
296
296
  return 0
297
297
 
298
298
  # Calculate cutoff time
299
- cutoff_time = datetime.now() - timedelta(hours=retention_hours)
299
+ cutoff_time = datetime.now(timezone.utc) - timedelta(hours=retention_hours)
300
300
 
301
301
  # Schedule async cleanup
302
302
  deleted_count = await self._async_cleanup(directory, pattern, cutoff_time)
@@ -337,7 +337,9 @@ class LogManager:
337
337
 
338
338
  try:
339
339
  # Check file modification time
340
- mtime = datetime.fromtimestamp(file_path.stat().st_mtime)
340
+ mtime = datetime.fromtimestamp(
341
+ file_path.stat().st_mtime, tz=timezone.utc
342
+ )
341
343
  if mtime < cutoff_time:
342
344
  file_path.unlink()
343
345
  deleted_count += 1
@@ -370,7 +372,7 @@ class LogManager:
370
372
  return 0
371
373
 
372
374
  # Calculate cutoff time
373
- cutoff_time = datetime.now() - timedelta(hours=retention_hours)
375
+ cutoff_time = datetime.now(timezone.utc) - timedelta(hours=retention_hours)
374
376
  deleted_count = 0
375
377
 
376
378
  try:
@@ -386,7 +388,9 @@ class LogManager:
386
388
 
387
389
  try:
388
390
  # Check file modification time
389
- mtime = datetime.fromtimestamp(file_path.stat().st_mtime)
391
+ mtime = datetime.fromtimestamp(
392
+ file_path.stat().st_mtime, tz=timezone.utc
393
+ )
390
394
  if mtime < cutoff_time:
391
395
  file_path.unlink()
392
396
  deleted_count += 1
@@ -468,7 +472,7 @@ class LogManager:
468
472
  prompts_dir = await self.setup_logging("prompts")
469
473
 
470
474
  # Generate filename with timestamp
471
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[
475
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S_%f")[
472
476
  :-3
473
477
  ] # Microseconds to milliseconds
474
478
 
@@ -494,7 +498,7 @@ class LogManager:
494
498
 
495
499
  # Prepare prompt data
496
500
  prompt_data = {
497
- "timestamp": datetime.now().isoformat(),
501
+ "timestamp": datetime.now(timezone.utc).isoformat(),
498
502
  "type": prompt_type,
499
503
  "content": content,
500
504
  "metadata": metadata or {},
@@ -567,12 +571,12 @@ class LogManager:
567
571
  message: Log message to write
568
572
  level: Log level (INFO, WARNING, ERROR, DEBUG)
569
573
  """
570
- timestamp = datetime.now().isoformat()
574
+ timestamp = datetime.now(timezone.utc).isoformat()
571
575
  log_entry = f"[{timestamp}] [{level}] {message}\n"
572
576
 
573
577
  # Get appropriate log file based on context
574
578
  log_dir = self._get_log_directory("mpm")
575
- log_file = log_dir / f"mpm_{datetime.now().strftime('%Y%m%d')}.log"
579
+ log_file = log_dir / f"mpm_{datetime.now(timezone.utc).strftime('%Y%m%d')}.log"
576
580
 
577
581
  def write_task():
578
582
  try:
@@ -652,7 +656,7 @@ class LogManager:
652
656
  try:
653
657
  self.write_queue.put_nowait(None)
654
658
  self.cleanup_queue.put_nowait(None)
655
- except:
659
+ except Exception:
656
660
  pass
657
661
 
658
662
  # Wait for threads to finish
claude_mpm/core/logger.py CHANGED
@@ -14,7 +14,7 @@ import sys
14
14
  import threading
15
15
  import time
16
16
  from collections import defaultdict
17
- from datetime import datetime
17
+ from datetime import datetime, timezone
18
18
  from enum import Enum
19
19
  from pathlib import Path
20
20
  from typing import Any, Dict, Optional
@@ -84,7 +84,7 @@ class StreamingHandler(logging.StreamHandler):
84
84
 
85
85
  except (KeyboardInterrupt, SystemExit):
86
86
  raise
87
- except:
87
+ except Exception:
88
88
  self.handleError(record)
89
89
 
90
90
  def finalize_info_line(self):
@@ -237,7 +237,7 @@ def setup_logging(
237
237
  log_dir.mkdir(parents=True, exist_ok=True)
238
238
 
239
239
  # Create timestamped log file
240
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
240
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
241
241
  log_file = log_dir / f"mpm_{timestamp}.log"
242
242
 
243
243
  file_handler = logging.FileHandler(log_file)
@@ -485,8 +485,8 @@ class ProjectLogger:
485
485
  path.mkdir(parents=True, exist_ok=True)
486
486
 
487
487
  # Create session directory
488
- self.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
489
- self.session_start_time = datetime.now()
488
+ self.session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
489
+ self.session_start_time = datetime.now(timezone.utc)
490
490
  self.session_dir = self.dirs["logs_sessions"] / self.session_id
491
491
  self.session_dir.mkdir(parents=True, exist_ok=True)
492
492
 
@@ -513,7 +513,7 @@ class ProjectLogger:
513
513
  if self.log_level == LogLevel.OFF:
514
514
  return
515
515
 
516
- timestamp = datetime.now().isoformat()
516
+ timestamp = datetime.now(timezone.utc).isoformat()
517
517
  log_entry = {
518
518
  "timestamp": timestamp,
519
519
  "level": level,
@@ -523,7 +523,8 @@ class ProjectLogger:
523
523
 
524
524
  # Write to daily log file
525
525
  log_file = (
526
- self.dirs["logs_system"] / f"{datetime.now().strftime('%Y%m%d')}.jsonl"
526
+ self.dirs["logs_system"]
527
+ / f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
527
528
  )
528
529
  with open(log_file, "a") as f:
529
530
  f.write(json.dumps(log_entry) + "\n")
@@ -543,10 +544,10 @@ class ProjectLogger:
543
544
  if self.log_level == LogLevel.OFF:
544
545
  return
545
546
 
546
- timestamp = datetime.now().isoformat()
547
+ timestamp = datetime.now(timezone.utc).isoformat()
547
548
 
548
549
  # Update statistics
549
- today = datetime.now().strftime("%Y-%m-%d")
550
+ today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
550
551
  self.stats[today]["total_calls"] += 1
551
552
  self.stats[today]["total_tokens"] += tokens
552
553
  self.stats[today]["total_time_seconds"] += execution_time
@@ -576,7 +577,9 @@ class ProjectLogger:
576
577
  agent_log_dir = self.dirs["logs_agents"] / agent.lower()
577
578
  agent_log_dir.mkdir(exist_ok=True)
578
579
 
579
- daily_log = agent_log_dir / f"{datetime.now().strftime('%Y%m%d')}.jsonl"
580
+ daily_log = (
581
+ agent_log_dir / f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
582
+ )
580
583
  with open(daily_log, "a") as f:
581
584
  f.write(json.dumps(log_entry) + "\n")
582
585
 
@@ -586,7 +589,9 @@ class ProjectLogger:
586
589
  "session_id": self.session_id,
587
590
  "session_dir": str(self.session_dir),
588
591
  "start_time": self.session_id,
589
- "stats": self.stats.get(datetime.now().strftime("%Y-%m-%d"), {}),
592
+ "stats": self.stats.get(
593
+ datetime.now(timezone.utc).strftime("%Y-%m-%d"), {}
594
+ ),
590
595
  }
591
596
 
592
597
 
@@ -27,11 +27,13 @@ from typing import Any, Dict, Optional, Union
27
27
  from claude_mpm.core.logger import (
28
28
  JsonFormatter,
29
29
  finalize_streaming_logs,
30
- get_logger as _get_logger,
30
+ )
31
+ from claude_mpm.core.logger import get_logger as _get_logger
32
+ from claude_mpm.core.logger import (
31
33
  log_async_performance,
32
34
  log_performance,
33
- setup_logging as _setup_logging,
34
35
  )
36
+ from claude_mpm.core.logger import setup_logging as _setup_logging
35
37
 
36
38
  # Standard log format for consistency
37
39
  STANDARD_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
@@ -166,7 +166,7 @@ class OneshotSession:
166
166
 
167
167
  def _run_subprocess(
168
168
  self, cmd: list, env: dict, prompt: str
169
- ) -> Tuple[bool, Optional[str]]:
169
+ ) -> Tuple[bool, Optional[str]]: # noqa: PLR0911
170
170
  """Run the subprocess and handle all exception types."""
171
171
  try:
172
172
  # Debug: log the command being run
@@ -22,7 +22,7 @@ import threading
22
22
  import time
23
23
  from concurrent.futures import ThreadPoolExecutor, as_completed
24
24
  from dataclasses import dataclass
25
- from datetime import datetime
25
+ from datetime import datetime, timezone
26
26
  from typing import Any, Dict, List, Optional
27
27
 
28
28
  try:
@@ -111,7 +111,7 @@ class OptimizedAgentLoader:
111
111
  try:
112
112
  mtime = file_path.stat().st_mtime
113
113
  return f"agent:{file_path}:{mtime}"
114
- except:
114
+ except Exception:
115
115
  return f"agent:{file_path}"
116
116
 
117
117
  def _load_agent_file(self, file_path: Path) -> Optional[Dict[str, Any]]:
@@ -154,7 +154,7 @@ class OptimizedAgentLoader:
154
154
 
155
155
  # Add metadata
156
156
  agent_data["_file_path"] = str(file_path)
157
- agent_data["_loaded_at"] = datetime.now().isoformat()
157
+ agent_data["_loaded_at"] = datetime.now(timezone.utc).isoformat()
158
158
 
159
159
  # Cache the result
160
160
  self.cache.put(cache_key, agent_data, ttl=self.cache_ttl)
@@ -188,7 +188,7 @@ class OptimizedAgentLoader:
188
188
  data = yaml.safe_load(frontmatter) or {}
189
189
  data["instructions"] = body
190
190
  return data
191
- except:
191
+ except Exception:
192
192
  pass
193
193
 
194
194
  # No frontmatter, treat as pure instructions
@@ -322,7 +322,7 @@ class OptimizedAgentLoader:
322
322
 
323
323
  # Add metadata
324
324
  agent_data["_file_path"] = str(file_path)
325
- agent_data["_loaded_at"] = datetime.now().isoformat()
325
+ agent_data["_loaded_at"] = datetime.now(timezone.utc).isoformat()
326
326
 
327
327
  # Cache the result
328
328
  self.cache.put(cache_key, agent_data, ttl=self.cache_ttl)
@@ -366,7 +366,7 @@ class OptimizedAgentLoader:
366
366
  "template": template,
367
367
  "variables": self._extract_variables(template),
368
368
  "sections": self._extract_sections(template),
369
- "compiled_at": datetime.now().isoformat(),
369
+ "compiled_at": datetime.now(timezone.utc).isoformat(),
370
370
  }
371
371
 
372
372
  # Cache compiled template
@@ -42,7 +42,7 @@ class OutputStyleManager:
42
42
  Path(__file__).parent.parent / "agents" / "OUTPUT_STYLE.md"
43
43
  )
44
44
 
45
- def _detect_claude_version(self) -> Optional[str]:
45
+ def _detect_claude_version(self) -> Optional[str]: # noqa: PLR0911
46
46
  """
47
47
  Detect Claude Code version by running 'claude --version'.
48
48
  Uses global cache to avoid duplicate detection and logging.
@@ -14,7 +14,7 @@ WHY this is needed:
14
14
  import functools
15
15
  import threading
16
16
  import time
17
- from datetime import datetime
17
+ from datetime import datetime, timezone
18
18
  from typing import Any, Dict, List, Optional
19
19
 
20
20
  from ..core.hook_manager import get_hook_manager
@@ -195,7 +195,7 @@ class PMHookInterceptor:
195
195
  {
196
196
  "todos": todos,
197
197
  "source": "PM_Manual",
198
- "timestamp": datetime.utcnow().isoformat(),
198
+ "timestamp": datetime.now(timezone.utc).isoformat(),
199
199
  },
200
200
  )
201
201
 
@@ -210,7 +210,7 @@ class PMHookInterceptor:
210
210
  "todos_count": len(todos),
211
211
  "source": "PM_Manual",
212
212
  "success": True,
213
- "timestamp": datetime.utcnow().isoformat(),
213
+ "timestamp": datetime.now(timezone.utc).isoformat(),
214
214
  },
215
215
  )
216
216
 
@@ -160,7 +160,7 @@ class ServiceRegistry:
160
160
  try:
161
161
  # Use the enhanced container's named resolution
162
162
  return self.container.get(BaseService, name=service_type)
163
- except:
163
+ except Exception:
164
164
  # Fall back to looking up class and resolving
165
165
  if service_type not in self._services:
166
166
  raise KeyError(f"Service '{service_type}' not registered")
@@ -4,7 +4,7 @@ import gzip
4
4
  import json
5
5
  import shutil
6
6
  import uuid
7
- from datetime import datetime, timedelta
7
+ from datetime import datetime, timedelta, timezone
8
8
  from pathlib import Path
9
9
  from typing import Any, Dict, List, Optional
10
10
 
@@ -41,8 +41,8 @@ class SessionManager:
41
41
  self.active_sessions[session_id] = {
42
42
  "id": session_id,
43
43
  "context": context,
44
- "created_at": datetime.now().isoformat(),
45
- "last_used": datetime.now().isoformat(),
44
+ "created_at": datetime.now(timezone.utc).isoformat(),
45
+ "last_used": datetime.now(timezone.utc).isoformat(),
46
46
  "use_count": 0,
47
47
  "agents_run": [],
48
48
  }
@@ -65,7 +65,7 @@ class SessionManager:
65
65
  Session ID
66
66
  """
67
67
  # Look for existing session in context
68
- now = datetime.now()
68
+ now = datetime.now(timezone.utc)
69
69
  max_age = timedelta(minutes=max_age_minutes)
70
70
 
71
71
  for session_id, session_data in self.active_sessions.items():
@@ -95,10 +95,12 @@ class SessionManager:
95
95
  {
96
96
  "agent": agent,
97
97
  "task": task[:100], # Truncate long tasks
98
- "timestamp": datetime.now().isoformat(),
98
+ "timestamp": datetime.now(timezone.utc).isoformat(),
99
99
  }
100
100
  )
101
- self.active_sessions[session_id]["last_used"] = datetime.now().isoformat()
101
+ self.active_sessions[session_id]["last_used"] = datetime.now(
102
+ timezone.utc
103
+ ).isoformat()
102
104
  self._save_sessions()
103
105
 
104
106
  def cleanup_old_sessions(self, max_age_hours: int = 24, archive: bool = True):
@@ -111,7 +113,7 @@ class SessionManager:
111
113
  max_age_hours: Maximum age in hours
112
114
  archive: Whether to archive sessions before removing
113
115
  """
114
- now = datetime.now()
116
+ now = datetime.now(timezone.utc)
115
117
  max_age = timedelta(hours=max_age_hours)
116
118
 
117
119
  expired = []
@@ -223,7 +225,7 @@ class SessionManager:
223
225
  archive_dir.mkdir(parents=True, exist_ok=True)
224
226
 
225
227
  # Create timestamped archive file
226
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
228
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
227
229
  archive_name = f"sessions_archive_{timestamp}.json.gz"
228
230
  archive_path = archive_dir / archive_name
229
231
 
@@ -279,7 +281,7 @@ class SessionManager:
279
281
  archive_dir = Path.home() / ".claude-mpm" / "archives"
280
282
  archive_dir.mkdir(parents=True, exist_ok=True)
281
283
 
282
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
284
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
283
285
  backup_name = f"claude_json_backup_{timestamp}.json.gz"
284
286
  backup_path = archive_dir / backup_name
285
287
 
@@ -17,7 +17,7 @@ import threading
17
17
  import time
18
18
  from collections import defaultdict, deque
19
19
  from dataclasses import dataclass, field
20
- from datetime import datetime, timedelta
20
+ from datetime import datetime, timedelta, timezone
21
21
  from enum import Enum
22
22
  from typing import Any, Deque, Dict, List, Optional
23
23
 
@@ -97,11 +97,9 @@ class CircuitBreaker:
97
97
  return True
98
98
  if self.state == CircuitState.OPEN:
99
99
  # Check if recovery timeout has passed
100
- if (
101
- self.last_failure_time
102
- and datetime.now() - self.last_failure_time
103
- > timedelta(seconds=self.recovery_timeout)
104
- ):
100
+ if self.last_failure_time and datetime.now(
101
+ timezone.utc
102
+ ) - self.last_failure_time > timedelta(seconds=self.recovery_timeout):
105
103
  self.state = CircuitState.HALF_OPEN
106
104
  self.logger.info(
107
105
  "Circuit breaker transitioning to HALF_OPEN for testing"
@@ -127,7 +125,7 @@ class CircuitBreaker:
127
125
  def record_failure(self):
128
126
  """Record failed execution."""
129
127
  self.failure_count += 1
130
- self.last_failure_time = datetime.now()
128
+ self.last_failure_time = datetime.now(timezone.utc)
131
129
 
132
130
  if self.state == CircuitState.HALF_OPEN:
133
131
  # Test failed, go back to OPEN
@@ -184,7 +182,7 @@ class SocketIOConnectionPool:
184
182
  # Health monitoring
185
183
  self.health_thread = None
186
184
  self.health_running = False
187
- self.last_health_check = datetime.now()
185
+ self.last_health_check = datetime.now(timezone.utc)
188
186
 
189
187
  # Server configuration
190
188
  self.server_url = None
@@ -311,7 +309,7 @@ class SocketIOConnectionPool:
311
309
  self.server_url = f"http://localhost:{port}"
312
310
  self.logger.debug(f"Detected Socket.IO server on port {port}")
313
311
  return
314
- except:
312
+ except Exception:
315
313
  continue
316
314
 
317
315
  # Fall back to default
@@ -375,7 +373,7 @@ class SocketIOConnectionPool:
375
373
  # Check if connection is still valid
376
374
  for conn_id, stats in self.connection_stats.items():
377
375
  if stats.is_connected:
378
- stats.last_used = datetime.now()
376
+ stats.last_used = datetime.now(timezone.utc)
379
377
  return client
380
378
 
381
379
  # Create new connection if under limit
@@ -581,7 +579,7 @@ class SocketIOConnectionPool:
581
579
  loop.stop()
582
580
  loop.run_until_complete(loop.shutdown_asyncgens())
583
581
  loop.close()
584
- except:
582
+ except Exception:
585
583
  pass
586
584
 
587
585
  async def _connect_client(self, client: socketio.AsyncClient):
@@ -623,7 +621,7 @@ class SocketIOConnectionPool:
623
621
  self._check_connections_health()
624
622
 
625
623
  # Update last health check time
626
- self.last_health_check = datetime.now()
624
+ self.last_health_check = datetime.now(timezone.utc)
627
625
 
628
626
  except Exception as e:
629
627
  self.logger.error(f"Health monitor error: {e}")
@@ -655,7 +653,9 @@ class SocketIOConnectionPool:
655
653
  continue
656
654
 
657
655
  # 3. Connection idle for too long (>5 minutes)
658
- idle_time = (datetime.now() - stats.last_used).total_seconds()
656
+ idle_time = (
657
+ datetime.now(timezone.utc) - stats.last_used
658
+ ).total_seconds()
659
659
  if idle_time > 300 and conn_id not in [
660
660
  id for id, _ in enumerate(self.available_connections)
661
661
  ]:
claude_mpm/core/types.py CHANGED
@@ -16,7 +16,7 @@ Module-specific types should remain in their respective modules.
16
16
  """
17
17
 
18
18
  from dataclasses import dataclass
19
- from datetime import datetime
19
+ from datetime import datetime, timezone
20
20
  from enum import Enum
21
21
  from typing import Any, Dict, List, Optional, Tuple, Union
22
22
 
@@ -206,7 +206,7 @@ class TaskInfo:
206
206
  if self.metadata is None:
207
207
  self.metadata = {}
208
208
  if self.created_at is None:
209
- self.created_at = datetime.now()
209
+ self.created_at = datetime.now(timezone.utc)
210
210
  if self.updated_at is None:
211
211
  self.updated_at = self.created_at
212
212
 
@@ -30,7 +30,7 @@ import json
30
30
  import logging
31
31
  import time
32
32
  from dataclasses import asdict, dataclass
33
- from datetime import datetime
33
+ from datetime import datetime, timezone
34
34
  from enum import Enum
35
35
  from pathlib import Path
36
36
  from typing import Any, Dict, List, Optional, Set, Union
@@ -718,7 +718,7 @@ class UnifiedAgentRegistry:
718
718
 
719
719
  export_data = {
720
720
  "metadata": {
721
- "export_time": datetime.now().isoformat(),
721
+ "export_time": datetime.now(timezone.utc).isoformat(),
722
722
  "total_agents": len(self.registry),
723
723
  "discovery_paths": [str(p) for p in self.discovery_paths],
724
724
  },
@@ -166,7 +166,7 @@ class PathContext:
166
166
 
167
167
  @staticmethod
168
168
  @lru_cache(maxsize=1)
169
- def detect_deployment_context() -> DeploymentContext:
169
+ def detect_deployment_context() -> DeploymentContext: # noqa: PLR0911
170
170
  """Detect the current deployment context.
171
171
 
172
172
  Priority order:
@@ -20,7 +20,7 @@ import subprocess
20
20
  import sys
21
21
  import threading
22
22
  from dataclasses import asdict, dataclass
23
- from datetime import datetime
23
+ from datetime import datetime, timezone
24
24
  from pathlib import Path
25
25
  from queue import Queue
26
26
  from typing import Any, Dict, List, Optional
@@ -41,7 +41,7 @@ class AnalysisRequest:
41
41
 
42
42
  def __post_init__(self):
43
43
  if self.timestamp is None:
44
- self.timestamp = datetime.utcnow()
44
+ self.timestamp = datetime.now(timezone.utc)
45
45
 
46
46
 
47
47
  class CodeAnalysisRunner:
@@ -433,7 +433,7 @@ class CodeAnalysisRunner:
433
433
  if self.server:
434
434
  # Add timestamp if not present
435
435
  if "timestamp" not in data:
436
- data["timestamp"] = datetime.utcnow().isoformat()
436
+ data["timestamp"] = datetime.now(timezone.utc).isoformat()
437
437
 
438
438
  # Broadcast to all clients
439
439
  self.server.broadcast_event(event_type, data)
@@ -450,6 +450,6 @@ class CodeAnalysisRunner:
450
450
  {
451
451
  "request_id": request_id,
452
452
  "message": message,
453
- "timestamp": datetime.utcnow().isoformat(),
453
+ "timestamp": datetime.now(timezone.utc).isoformat(),
454
454
  },
455
455
  )