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
@@ -16,7 +16,7 @@ DESIGN DECISIONS:
16
16
  - Maintains backward compatibility with existing dashboard clients
17
17
  """
18
18
 
19
- from datetime import datetime
19
+ from datetime import datetime, timezone
20
20
  from typing import Any, Dict, List, Optional
21
21
 
22
22
  try:
@@ -125,7 +125,7 @@ class DashboardServer(SocketIOServiceInterface):
125
125
  # Update state
126
126
  self.running = self.dashboard_server.is_running()
127
127
  if self.running:
128
- self.stats["start_time"] = datetime.now().isoformat()
128
+ self.stats["start_time"] = datetime.now(timezone.utc).isoformat()
129
129
  self.stats["monitor_connected"] = monitor_connected
130
130
 
131
131
  self.logger.info(
@@ -224,7 +224,8 @@ class DashboardServer(SocketIOServiceInterface):
224
224
  "monitor_stats": monitor_stats,
225
225
  "uptime": (
226
226
  (
227
- datetime.now() - datetime.fromisoformat(self.stats["start_time"])
227
+ datetime.now(timezone.utc)
228
+ - datetime.fromisoformat(self.stats["start_time"])
228
229
  ).total_seconds()
229
230
  if self.stats["start_time"]
230
231
  else 0
@@ -15,7 +15,7 @@ DESIGN DECISION: Transform all events to a consistent schema:
15
15
 
16
16
  import re
17
17
  from dataclasses import dataclass, field
18
- from datetime import datetime
18
+ from datetime import datetime, timezone
19
19
  from enum import Enum
20
20
  from typing import Any, Dict, Optional, Tuple
21
21
 
@@ -243,7 +243,7 @@ class EventNormalizer:
243
243
  source="system",
244
244
  type="unknown",
245
245
  subtype="error",
246
- timestamp=datetime.now().isoformat(),
246
+ timestamp=datetime.now(timezone.utc).isoformat(),
247
247
  data={"original": str(event_data), "error": str(e)},
248
248
  )
249
249
 
@@ -277,7 +277,9 @@ class EventNormalizer:
277
277
  source=source,
278
278
  type=event_data.get("type", "unknown"),
279
279
  subtype=event_data.get("subtype", "generic"),
280
- timestamp=event_data.get("timestamp", datetime.now().isoformat()),
280
+ timestamp=event_data.get(
281
+ "timestamp", datetime.now(timezone.utc).isoformat()
282
+ ),
281
283
  data=event_data.get("data", {}),
282
284
  )
283
285
 
@@ -340,7 +342,7 @@ class EventNormalizer:
340
342
 
341
343
  return "unknown"
342
344
 
343
- def _map_event_name(self, event_name: str) -> Tuple[str, str]:
345
+ def _map_event_name(self, event_name: str) -> Tuple[str, str]: # noqa: PLR0911
344
346
  """Map event name to (type, subtype) tuple.
345
347
 
346
348
  WHY: Consistent categorization helps clients filter and handle events.
@@ -553,12 +555,14 @@ class EventNormalizer:
553
555
  # Convert other formats
554
556
  try:
555
557
  if isinstance(timestamp, (int, float)):
556
- return datetime.fromtimestamp(timestamp).isoformat()
557
- except:
558
+ return datetime.fromtimestamp(
559
+ timestamp, tz=timezone.utc
560
+ ).isoformat()
561
+ except Exception:
558
562
  pass
559
563
 
560
564
  # Generate new timestamp if not found
561
- return datetime.now().isoformat()
565
+ return datetime.now(timezone.utc).isoformat()
562
566
 
563
567
  def _determine_source(
564
568
  self, event_data: Any, event_type: str, source_override: Optional[str] = None
@@ -5,7 +5,7 @@ logging, error handling, and access to the server instance. All handler
5
5
  classes inherit from this to ensure consistent behavior.
6
6
  """
7
7
 
8
- from datetime import datetime
8
+ from datetime import datetime, timezone
9
9
  from typing import TYPE_CHECKING, Any, Dict, List, Optional
10
10
 
11
11
  from ....core.logger import get_logger
@@ -107,7 +107,7 @@ class BaseEventHandler:
107
107
  """
108
108
  event = {
109
109
  "type": event_type,
110
- "timestamp": datetime.utcnow().isoformat() + "Z",
110
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
111
111
  "data": data,
112
112
  }
113
113
  self.event_history.append(event)
@@ -8,7 +8,7 @@ from other handlers makes connection management more maintainable.
8
8
  import asyncio
9
9
  import functools
10
10
  import time
11
- from datetime import datetime
11
+ from datetime import datetime, timezone
12
12
  from typing import Any, Callable, Dict, List, Optional
13
13
 
14
14
  from .base import BaseEventHandler
@@ -48,7 +48,7 @@ def timeout_handler(timeout_seconds: float = 5.0):
48
48
  f"⚠️ Handler {handler_name} took {elapsed:.2f}s "
49
49
  f"(close to {timeout_seconds}s timeout)"
50
50
  )
51
- except:
51
+ except Exception:
52
52
  print(
53
53
  f"⚠️ Handler {handler_name} took {elapsed:.2f}s (close to {timeout_seconds}s timeout)"
54
54
  )
@@ -65,7 +65,7 @@ def timeout_handler(timeout_seconds: float = 5.0):
65
65
  logger.error(
66
66
  f"❌ Handler {handler_name} timed out after {elapsed:.2f}s"
67
67
  )
68
- except:
68
+ except Exception:
69
69
  print(f"❌ Handler {handler_name} timed out after {elapsed:.2f}s")
70
70
 
71
71
  return None
@@ -80,7 +80,7 @@ def timeout_handler(timeout_seconds: float = 5.0):
80
80
  logger.error(
81
81
  f"❌ Handler {handler_name} failed after {elapsed:.2f}s: {e}"
82
82
  )
83
- except:
83
+ except Exception:
84
84
  print(f"❌ Handler {handler_name} failed after {elapsed:.2f}s: {e}")
85
85
  raise
86
86
 
@@ -274,7 +274,7 @@ class ConnectionEventHandler(BaseEventHandler):
274
274
  # Force disconnect if still connected
275
275
  try:
276
276
  await self.sio.disconnect(sid)
277
- except:
277
+ except Exception:
278
278
  pass # Already disconnected
279
279
 
280
280
  self.logger.info(f"🔌 Cleaned up stale connection: {sid}")
@@ -326,7 +326,7 @@ class ConnectionEventHandler(BaseEventHandler):
326
326
  status_data = {
327
327
  "type": "connection",
328
328
  "subtype": "status",
329
- "timestamp": datetime.utcnow().isoformat() + "Z",
329
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
330
330
  "source": "server",
331
331
  "session_id": self.server.session_id,
332
332
  "data": {
@@ -348,13 +348,13 @@ class ConnectionEventHandler(BaseEventHandler):
348
348
  {
349
349
  "type": "connection",
350
350
  "subtype": "welcome",
351
- "timestamp": datetime.utcnow().isoformat() + "Z",
351
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
352
352
  "source": "server",
353
353
  "session_id": self.server.session_id,
354
354
  "data": {
355
355
  "message": "Connected to Claude MPM Socket.IO server",
356
356
  "client_id": sid,
357
- "server_time": datetime.utcnow().isoformat() + "Z",
357
+ "server_time": datetime.now(timezone.utc).isoformat() + "Z",
358
358
  "build_info": monitor_build_info,
359
359
  },
360
360
  },
@@ -401,7 +401,7 @@ class ConnectionEventHandler(BaseEventHandler):
401
401
  status_data = {
402
402
  "type": "connection",
403
403
  "subtype": "status",
404
- "timestamp": datetime.utcnow().isoformat() + "Z",
404
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
405
405
  "source": "server",
406
406
  "session_id": self.server.session_id,
407
407
  "data": {
@@ -456,7 +456,7 @@ class ConnectionEventHandler(BaseEventHandler):
456
456
  {
457
457
  "type": "connection",
458
458
  "subtype": "subscribed",
459
- "timestamp": datetime.utcnow().isoformat() + "Z",
459
+ "timestamp": datetime.now(timezone.utc).isoformat() + "Z",
460
460
  "source": "server",
461
461
  "data": {"channels": channels},
462
462
  },
@@ -8,7 +8,7 @@ DESIGN DECISION: Centralized connection event handling ensures consistent
8
8
  state management and provides resilient event delivery across reconnections.
9
9
  """
10
10
 
11
- from datetime import datetime
11
+ from datetime import datetime, timezone
12
12
 
13
13
  from .base import BaseEventHandler
14
14
 
@@ -50,7 +50,7 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
50
50
  # Store client info
51
51
  self.server.client_info[sid] = {
52
52
  "client_id": conn.client_id,
53
- "connected_at": datetime.now().isoformat(),
53
+ "connected_at": datetime.now(timezone.utc).isoformat(),
54
54
  "user_agent": environ.get("HTTP_USER_AGENT", "unknown"),
55
55
  "remote_addr": environ.get("REMOTE_ADDR", "unknown"),
56
56
  }
@@ -61,7 +61,7 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
61
61
  {
62
62
  "client_id": conn.client_id,
63
63
  "sid": sid,
64
- "timestamp": datetime.now().isoformat(),
64
+ "timestamp": datetime.now(timezone.utc).isoformat(),
65
65
  "server_version": self.get_server_version(),
66
66
  },
67
67
  room=sid,
@@ -120,7 +120,7 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
120
120
  self.server.stats["connections_total"] += 1
121
121
 
122
122
  self.server.client_info[sid] = {
123
- "connected_at": datetime.now().isoformat(),
123
+ "connected_at": datetime.now(timezone.utc).isoformat(),
124
124
  "user_agent": environ.get("HTTP_USER_AGENT", "unknown"),
125
125
  "remote_addr": environ.get("REMOTE_ADDR", "unknown"),
126
126
  }
@@ -185,8 +185,8 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
185
185
  await sio.emit(
186
186
  "pong",
187
187
  {
188
- "timestamp": datetime.now().isoformat(),
189
- "server_time": datetime.now().timestamp(),
188
+ "timestamp": datetime.now(timezone.utc).isoformat(),
189
+ "server_time": datetime.now(timezone.utc).timestamp(),
190
190
  },
191
191
  room=sid,
192
192
  )
@@ -210,7 +210,10 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
210
210
  # Optional: Send confirmation
211
211
  await sio.emit(
212
212
  "ack_confirmed",
213
- {"sequence": sequence, "timestamp": datetime.now().isoformat()},
213
+ {
214
+ "sequence": sequence,
215
+ "timestamp": datetime.now(timezone.utc).isoformat(),
216
+ },
214
217
  room=sid,
215
218
  )
216
219
 
@@ -263,7 +266,7 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
263
266
  """Get connection statistics for debugging."""
264
267
  try:
265
268
  stats = {
266
- "timestamp": datetime.now().isoformat(),
269
+ "timestamp": datetime.now(timezone.utc).isoformat(),
267
270
  "total_connections": len(self.server.connected_clients),
268
271
  "server_stats": self.server.stats,
269
272
  }
@@ -312,7 +315,7 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
312
315
  if self.server.stats.get("start_time")
313
316
  else None
314
317
  ),
315
- "timestamp": datetime.now().isoformat(),
318
+ "timestamp": datetime.now(timezone.utc).isoformat(),
316
319
  }
317
320
 
318
321
  await self.server.core.sio.emit("server_status", status_data, room=sid)
@@ -326,5 +329,5 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
326
329
  from claude_mpm.services.version_service import VersionService
327
330
 
328
331
  return VersionService().get_version()
329
- except:
332
+ except Exception:
330
333
  return "unknown"
@@ -4,7 +4,7 @@ WHY: This module handles hook events from Claude to track session information,
4
4
  agent delegations, and other hook-based activity for the system heartbeat.
5
5
  """
6
6
 
7
- from datetime import datetime
7
+ from datetime import datetime, timezone
8
8
  from typing import Any, Dict
9
9
 
10
10
  from .base import BaseEventHandler
@@ -66,7 +66,8 @@ class HookEventHandler(BaseEventHandler):
66
66
  "type": "hook",
67
67
  "event": hook_event,
68
68
  "data": hook_data,
69
- "timestamp": data.get("timestamp") or datetime.now().isoformat(),
69
+ "timestamp": data.get("timestamp")
70
+ or datetime.now(timezone.utc).isoformat(),
70
71
  }
71
72
 
72
73
  # Add the event to history for replay
@@ -105,11 +106,11 @@ class HookEventHandler(BaseEventHandler):
105
106
  if hasattr(self.server, "active_sessions"):
106
107
  self.server.active_sessions[session_id] = {
107
108
  "session_id": session_id,
108
- "start_time": datetime.now().isoformat(),
109
+ "start_time": datetime.now(timezone.utc).isoformat(),
109
110
  "agent": agent_type,
110
111
  "status": "active",
111
112
  "prompt": data.get("prompt", "")[:100], # First 100 chars
112
- "last_activity": datetime.now().isoformat(),
113
+ "last_activity": datetime.now(timezone.utc).isoformat(),
113
114
  }
114
115
 
115
116
  self.logger.debug(
@@ -132,9 +133,9 @@ class HookEventHandler(BaseEventHandler):
132
133
  if session_id in self.server.active_sessions:
133
134
  # Mark as completed rather than removing immediately
134
135
  self.server.active_sessions[session_id]["status"] = "completed"
135
- self.server.active_sessions[session_id][
136
- "last_activity"
137
- ] = datetime.now().isoformat()
136
+ self.server.active_sessions[session_id]["last_activity"] = datetime.now(
137
+ timezone.utc
138
+ ).isoformat()
138
139
 
139
140
  self.logger.debug(
140
141
  f"Marked session completed: session={session_id[:8]}..."
@@ -156,18 +157,18 @@ class HookEventHandler(BaseEventHandler):
156
157
  if session_id not in self.server.active_sessions:
157
158
  self.server.active_sessions[session_id] = {
158
159
  "session_id": session_id,
159
- "start_time": datetime.now().isoformat(),
160
+ "start_time": datetime.now(timezone.utc).isoformat(),
160
161
  "agent": "pm", # Default to PM
161
162
  "status": "active",
162
163
  "prompt": data.get("prompt_text", "")[:100],
163
164
  "working_directory": data.get("working_directory", ""),
164
- "last_activity": datetime.now().isoformat(),
165
+ "last_activity": datetime.now(timezone.utc).isoformat(),
165
166
  }
166
167
  else:
167
168
  # Update last activity
168
- self.server.active_sessions[session_id][
169
- "last_activity"
170
- ] = datetime.now().isoformat()
169
+ self.server.active_sessions[session_id]["last_activity"] = datetime.now(
170
+ timezone.utc
171
+ ).isoformat()
171
172
 
172
173
  async def _handle_pre_tool(self, data: Dict[str, Any]):
173
174
  """Handle pre-tool events.
@@ -190,9 +191,9 @@ class HookEventHandler(BaseEventHandler):
190
191
  if session_id in self.server.active_sessions:
191
192
  self.server.active_sessions[session_id]["agent"] = agent_type
192
193
  self.server.active_sessions[session_id]["status"] = "delegated"
193
- self.server.active_sessions[session_id][
194
- "last_activity"
195
- ] = datetime.now().isoformat()
194
+ self.server.active_sessions[session_id]["last_activity"] = datetime.now(
195
+ timezone.utc
196
+ ).isoformat()
196
197
 
197
198
  self.logger.debug(
198
199
  f"Updated session delegation: session={session_id[:8]}..., agent={agent_type}"
@@ -223,7 +223,7 @@ class EventTypeMapper:
223
223
  }
224
224
 
225
225
  @classmethod
226
- def map_event_type(cls, old_type: str) -> Tuple[str, str]:
226
+ def map_event_type(cls, old_type: str) -> Tuple[str, str]: # noqa: PLR0911
227
227
  """Map an old event type to new type/subtype.
228
228
 
229
229
  WHY: Provides consistent categorization for all events.
@@ -16,7 +16,7 @@ DESIGN DECISIONS:
16
16
  import asyncio
17
17
  import threading
18
18
  import time
19
- from datetime import datetime
19
+ from datetime import datetime, timezone
20
20
  from typing import Any, Callable, Dict
21
21
 
22
22
  try:
@@ -119,7 +119,7 @@ class MonitorClient:
119
119
  # Use the event loop from the client thread
120
120
  if hasattr(self.client, "disconnect"):
121
121
  asyncio.run(self.client.disconnect())
122
- except:
122
+ except Exception:
123
123
  pass
124
124
 
125
125
  self.connected = False
@@ -152,7 +152,7 @@ class MonitorClient:
152
152
  "monitor_url": self.monitor_url,
153
153
  "uptime": (
154
154
  (
155
- datetime.now()
155
+ datetime.now(timezone.utc)
156
156
  - datetime.fromisoformat(self.stats["last_connected"])
157
157
  ).total_seconds()
158
158
  if self.stats["last_connected"] and self.connected
@@ -224,7 +224,7 @@ class MonitorClient:
224
224
  self.connected = True
225
225
  self.connecting = False
226
226
  self.stats["successful_connections"] += 1
227
- self.stats["last_connected"] = datetime.now().isoformat()
227
+ self.stats["last_connected"] = datetime.now(timezone.utc).isoformat()
228
228
  self.reconnect_delay = 1.0 # Reset reconnect delay on successful connection
229
229
 
230
230
  self.logger.info(f"Connected to monitor server at {self.monitor_url}")
@@ -255,7 +255,7 @@ class MonitorClient:
255
255
  """Handle disconnection."""
256
256
  self.logger.info("Disconnected from monitor server")
257
257
  self.connected = False
258
- self.stats["last_disconnected"] = datetime.now().isoformat()
258
+ self.stats["last_disconnected"] = datetime.now(timezone.utc).isoformat()
259
259
 
260
260
  @self.client.event
261
261
  async def connect_error(data):
@@ -13,7 +13,7 @@ import asyncio
13
13
  import time
14
14
  from collections import deque
15
15
  from dataclasses import dataclass
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from typing import Any, Deque, Dict, List, Optional, Set
18
18
 
19
19
  from ..event_normalizer import EventNormalizer
@@ -284,7 +284,7 @@ class SocketIOEventBroadcaster:
284
284
  # Reconstruct the raw event
285
285
  raw_event = {
286
286
  "type": event.event_type,
287
- "timestamp": datetime.now().isoformat(),
287
+ "timestamp": datetime.now(timezone.utc).isoformat(),
288
288
  "data": {**event.data, "retry_attempt": event.attempt_count + 1},
289
289
  }
290
290
 
@@ -323,7 +323,7 @@ class SocketIOEventBroadcaster:
323
323
  # Create raw event for normalization
324
324
  raw_event = {
325
325
  "type": event_type,
326
- "timestamp": datetime.now().isoformat(),
326
+ "timestamp": datetime.now(timezone.utc).isoformat(),
327
327
  "data": data,
328
328
  }
329
329
 
@@ -388,11 +388,11 @@ class SocketIOEventBroadcaster:
388
388
  asyncio.run_coroutine_threadsafe(
389
389
  update_activities(), self.loop
390
390
  )
391
- except:
391
+ except Exception:
392
392
  pass # Non-critical
393
393
 
394
394
  self.logger.debug(f"Broadcasted event: {event_type}")
395
- except:
395
+ except Exception:
396
396
  # Will be added to retry queue below
397
397
  pass
398
398
  else:
@@ -426,13 +426,15 @@ class SocketIOEventBroadcaster:
426
426
  "session_id": session_id,
427
427
  "launch_method": launch_method,
428
428
  "working_dir": working_dir,
429
- "timestamp": datetime.now().isoformat(),
429
+ "timestamp": datetime.now(timezone.utc).isoformat(),
430
430
  },
431
431
  )
432
432
 
433
433
  def session_ended(self):
434
434
  """Notify that a session has ended."""
435
- self.broadcast_event("session_ended", {"timestamp": datetime.now().isoformat()})
435
+ self.broadcast_event(
436
+ "session_ended", {"timestamp": datetime.now(timezone.utc).isoformat()}
437
+ )
436
438
 
437
439
  def claude_status_changed(
438
440
  self, status: str, pid: Optional[int] = None, message: str = ""
@@ -14,7 +14,7 @@ import contextlib
14
14
  import time
15
15
  from collections import deque
16
16
  from dataclasses import dataclass, field
17
- from datetime import datetime
17
+ from datetime import datetime, timezone
18
18
  from enum import Enum
19
19
  from typing import Any, Deque, Dict, List, Optional
20
20
  from uuid import uuid4
@@ -464,7 +464,7 @@ class ConnectionManager:
464
464
  async with self._lock:
465
465
  now = time.time()
466
466
  report = {
467
- "timestamp": datetime.now().isoformat(),
467
+ "timestamp": datetime.now(timezone.utc).isoformat(),
468
468
  "total_connections": len(self.connections),
469
469
  "healthy": 0,
470
470
  "stale": 0,
@@ -13,7 +13,7 @@ import asyncio
13
13
  import threading
14
14
  import time
15
15
  from collections import deque
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from pathlib import Path
18
18
  from typing import Any, Dict, Set
19
19
 
@@ -211,7 +211,7 @@ class SocketIOServerCore:
211
211
  await self.site.start()
212
212
 
213
213
  self.running = True
214
- self.stats["start_time"] = datetime.now()
214
+ self.stats["start_time"] = datetime.now(timezone.utc)
215
215
 
216
216
  self.logger.info(
217
217
  f"Socket.IO server listening on http://{self.host}:{self.port}"
@@ -428,7 +428,7 @@ class SocketIOServerCore:
428
428
  )
429
429
 
430
430
  # Add file reading endpoint for source viewer
431
- async def file_read_handler(request):
431
+ async def file_read_handler(request): # noqa: PLR0911
432
432
  """Handle GET /api/file/read for reading source files."""
433
433
  import os
434
434
 
@@ -749,7 +749,9 @@ class SocketIOServerCore:
749
749
  uptime_seconds = 0
750
750
  if self.stats.get("start_time"):
751
751
  uptime_seconds = int(
752
- (datetime.now() - self.stats["start_time"]).total_seconds()
752
+ (
753
+ datetime.now(timezone.utc) - self.stats["start_time"]
754
+ ).total_seconds()
753
755
  )
754
756
 
755
757
  # Get active sessions from main server if available
@@ -766,7 +768,7 @@ class SocketIOServerCore:
766
768
  heartbeat_data = {
767
769
  "type": "system",
768
770
  "subtype": "heartbeat",
769
- "timestamp": datetime.now().isoformat(),
771
+ "timestamp": datetime.now(timezone.utc).isoformat(),
770
772
  "source": "server",
771
773
  "data": {
772
774
  "uptime_seconds": uptime_seconds,
@@ -8,6 +8,7 @@ WHY this integration module:
8
8
  """
9
9
 
10
10
  import logging
11
+ from datetime import datetime, timezone
11
12
  from typing import Optional
12
13
 
13
14
  from claude_mpm.services.event_bus import EventBus
@@ -48,17 +49,16 @@ class EventBusIntegration:
48
49
  Returns:
49
50
  bool: True if setup successful
50
51
  """
51
- from datetime import datetime
52
52
 
53
53
  print(
54
- f"[{datetime.now().isoformat()}] EventBusIntegration.setup() called",
54
+ f"[{datetime.now(timezone.utc).isoformat()}] EventBusIntegration.setup() called",
55
55
  flush=True,
56
56
  )
57
57
 
58
58
  if not self.enabled:
59
59
  logger.info("EventBus integration disabled by configuration")
60
60
  print(
61
- f"[{datetime.now().isoformat()}] EventBus integration disabled by configuration",
61
+ f"[{datetime.now(timezone.utc).isoformat()}] EventBus integration disabled by configuration",
62
62
  flush=True,
63
63
  )
64
64
  return False
@@ -66,12 +66,13 @@ class EventBusIntegration:
66
66
  try:
67
67
  # Get EventBus instance
68
68
  print(
69
- f"[{datetime.now().isoformat()}] Getting EventBus instance...",
69
+ f"[{datetime.now(timezone.utc).isoformat()}] Getting EventBus instance...",
70
70
  flush=True,
71
71
  )
72
72
  self.event_bus = EventBus.get_instance()
73
73
  print(
74
- f"[{datetime.now().isoformat()}] EventBus instance obtained", flush=True
74
+ f"[{datetime.now(timezone.utc).isoformat()}] EventBus instance obtained",
75
+ flush=True,
75
76
  )
76
77
 
77
78
  # Apply configuration
@@ -79,31 +80,36 @@ class EventBusIntegration:
79
80
 
80
81
  # Create direct relay that uses server's broadcaster
81
82
  print(
82
- f"[{datetime.now().isoformat()}] Creating DirectSocketIORelay...",
83
+ f"[{datetime.now(timezone.utc).isoformat()}] Creating DirectSocketIORelay...",
83
84
  flush=True,
84
85
  )
85
86
  if self.server:
86
87
  self.relay = DirectSocketIORelay(self.server)
87
88
  print(
88
- f"[{datetime.now().isoformat()}] DirectSocketIORelay created with server instance",
89
+ f"[{datetime.now(timezone.utc).isoformat()}] DirectSocketIORelay created with server instance",
89
90
  flush=True,
90
91
  )
91
92
  else:
92
93
  logger.warning("No server instance provided, relay won't work")
93
94
  print(
94
- f"[{datetime.now().isoformat()}] WARNING: No server instance for relay",
95
+ f"[{datetime.now(timezone.utc).isoformat()}] WARNING: No server instance for relay",
95
96
  flush=True,
96
97
  )
97
98
  return False
98
99
 
99
100
  # Start the relay
100
- print(f"[{datetime.now().isoformat()}] Starting relay...", flush=True)
101
+ print(
102
+ f"[{datetime.now(timezone.utc).isoformat()}] Starting relay...",
103
+ flush=True,
104
+ )
101
105
  self.relay.start()
102
- print(f"[{datetime.now().isoformat()}] Relay started", flush=True)
106
+ print(
107
+ f"[{datetime.now(timezone.utc).isoformat()}] Relay started", flush=True
108
+ )
103
109
 
104
110
  logger.info("EventBus integration setup complete with DirectSocketIORelay")
105
111
  print(
106
- f"[{datetime.now().isoformat()}] EventBus integration setup complete with DirectSocketIORelay",
112
+ f"[{datetime.now(timezone.utc).isoformat()}] EventBus integration setup complete with DirectSocketIORelay",
107
113
  flush=True,
108
114
  )
109
115
  return True
@@ -111,7 +117,7 @@ class EventBusIntegration:
111
117
  except Exception as e:
112
118
  logger.error(f"Failed to setup EventBus integration: {e}")
113
119
  print(
114
- f"[{datetime.now().isoformat()}] Failed to setup EventBus integration: {e}",
120
+ f"[{datetime.now(timezone.utc).isoformat()}] Failed to setup EventBus integration: {e}",
115
121
  flush=True,
116
122
  )
117
123
  import traceback