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
@@ -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
  )
@@ -129,7 +129,7 @@ def has_code_files(directory_path, max_depth=5, current_depth=0):
129
129
  return False
130
130
 
131
131
 
132
- def should_show_item(item_name, item_path, is_directory):
132
+ def should_show_item(item_name, item_path, is_directory): # noqa: PLR0911
133
133
  """Determine if an item should be shown based on filtering rules"""
134
134
  # Always hide system files
135
135
  if item_name in {".DS_Store", "Thumbs.db", "desktop.ini"}:
@@ -7,7 +7,7 @@ Inspired by awesome-claude-code's template generation approach.
7
7
  """
8
8
 
9
9
  import re
10
- from datetime import datetime
10
+ from datetime import datetime, timezone
11
11
  from typing import Any, Dict, Optional
12
12
 
13
13
  import yaml
@@ -36,7 +36,9 @@ class AgentProfileGenerator:
36
36
  """Generate an agent profile from configuration."""
37
37
  # Set default values
38
38
  config.setdefault("VERSION", "1.0.0")
39
- config.setdefault("CREATED_DATE", datetime.now().strftime("%Y-%m-%d"))
39
+ config.setdefault(
40
+ "CREATED_DATE", datetime.now(timezone.utc).strftime("%Y-%m-%d")
41
+ )
40
42
  config.setdefault("AUTHOR", "claude-mpm")
41
43
 
42
44
  # Convert template to string
@@ -4,7 +4,7 @@ import asyncio
4
4
  import logging
5
5
  from abc import ABC, abstractmethod
6
6
  from dataclasses import dataclass
7
- from datetime import datetime
7
+ from datetime import datetime, timezone
8
8
  from enum import Enum
9
9
  from typing import Any, Dict, Optional
10
10
 
@@ -35,7 +35,7 @@ class HookContext:
35
35
  def __post_init__(self):
36
36
  """Ensure timestamp is set."""
37
37
  if not hasattr(self, "timestamp") or self.timestamp is None:
38
- self.timestamp = datetime.now()
38
+ self.timestamp = datetime.now(timezone.utc)
39
39
 
40
40
 
41
41
  @dataclass
@@ -131,7 +131,7 @@ class SocketIOConnectionPool:
131
131
  "source": "connection_pool",
132
132
  },
133
133
  )
134
- except:
134
+ except Exception:
135
135
  pass # Ignore ping errors
136
136
  return client
137
137
  except Exception:
@@ -168,10 +168,10 @@ class SocketIOConnectionPool:
168
168
  },
169
169
  )
170
170
  return True
171
- except:
171
+ except Exception:
172
172
  # If ping fails, connection might be dead
173
173
  return client.connected # Fall back to basic check
174
- except:
174
+ except Exception:
175
175
  return False
176
176
 
177
177
  def _close_connection(self, client: Any) -> None:
@@ -179,7 +179,7 @@ class SocketIOConnectionPool:
179
179
  try:
180
180
  if client:
181
181
  client.disconnect()
182
- except:
182
+ except Exception:
183
183
  pass
184
184
 
185
185
  def _cleanup_dead_connections(self) -> None:
@@ -9,7 +9,7 @@ import os
9
9
  import re
10
10
  import subprocess
11
11
  import sys
12
- from datetime import datetime
12
+ from datetime import datetime, timezone
13
13
  from typing import Optional
14
14
 
15
15
  # Import tool analysis with fallback for direct execution
@@ -77,7 +77,7 @@ class EventHandlers:
77
77
  "session_id": event.get("session_id", ""),
78
78
  "working_directory": working_dir,
79
79
  "git_branch": git_branch,
80
- "timestamp": datetime.now().isoformat(),
80
+ "timestamp": datetime.now(timezone.utc).isoformat(),
81
81
  "is_command": prompt.startswith("/"),
82
82
  "contains_code": "```" in prompt
83
83
  or "python" in prompt.lower()
@@ -101,7 +101,7 @@ class EventHandlers:
101
101
  if session_id:
102
102
  self.hook_handler.pending_prompts[session_id] = {
103
103
  "prompt": prompt,
104
- "timestamp": datetime.now().isoformat(),
104
+ "timestamp": datetime.now(timezone.utc).isoformat(),
105
105
  "working_directory": working_dir,
106
106
  }
107
107
  if DEBUG:
@@ -150,7 +150,7 @@ class EventHandlers:
150
150
  "session_id": event.get("session_id", ""),
151
151
  "working_directory": working_dir,
152
152
  "git_branch": git_branch,
153
- "timestamp": datetime.now().isoformat(),
153
+ "timestamp": datetime.now(timezone.utc).isoformat(),
154
154
  "parameter_count": len(tool_input) if isinstance(tool_input, dict) else 0,
155
155
  "is_file_operation": tool_name
156
156
  in ["Write", "Edit", "MultiEdit", "Read", "LS", "Glob"],
@@ -249,7 +249,7 @@ class EventHandlers:
249
249
  "session_id": session_id,
250
250
  "prompt": tool_input.get("prompt", ""),
251
251
  "description": tool_input.get("description", ""),
252
- "timestamp": datetime.now().isoformat(),
252
+ "timestamp": datetime.now(timezone.utc).isoformat(),
253
253
  "hook_event_name": "SubagentStart", # For dashboard compatibility
254
254
  }
255
255
  self.hook_handler._emit_socketio_event(
@@ -277,7 +277,7 @@ class EventHandlers:
277
277
  "session_id": session_id,
278
278
  "delegation_context": {
279
279
  "description": tool_input.get("description", ""),
280
- "timestamp": datetime.now().isoformat(),
280
+ "timestamp": datetime.now(timezone.utc).isoformat(),
281
281
  },
282
282
  }
283
283
 
@@ -312,7 +312,7 @@ class EventHandlers:
312
312
  working_dir = os.getcwd()
313
313
 
314
314
  # Check cache first (cache for 30 seconds)
315
- current_time = datetime.now().timestamp()
315
+ current_time = datetime.now(timezone.utc).timestamp()
316
316
  cache_key = working_dir
317
317
 
318
318
  if (
@@ -397,7 +397,7 @@ class EventHandlers:
397
397
  "session_id": event.get("session_id", ""),
398
398
  "working_directory": working_dir,
399
399
  "git_branch": git_branch,
400
- "timestamp": datetime.now().isoformat(),
400
+ "timestamp": datetime.now(timezone.utc).isoformat(),
401
401
  "has_output": bool(result_data.get("output")),
402
402
  "has_error": bool(result_data.get("error")),
403
403
  "output_size": (
@@ -448,7 +448,7 @@ class EventHandlers:
448
448
  "session_id": event.get("session_id", ""),
449
449
  "working_directory": working_dir,
450
450
  "git_branch": git_branch,
451
- "timestamp": datetime.now().isoformat(),
451
+ "timestamp": datetime.now(timezone.utc).isoformat(),
452
452
  "is_user_input_request": "input" in message.lower()
453
453
  or "waiting" in message.lower(),
454
454
  "is_error_notification": "error" in message.lower()
@@ -492,7 +492,7 @@ class EventHandlers:
492
492
  """Extract metadata from stop event."""
493
493
  working_dir = event.get("cwd", "")
494
494
  return {
495
- "timestamp": datetime.now().isoformat(),
495
+ "timestamp": datetime.now(timezone.utc).isoformat(),
496
496
  "working_directory": working_dir,
497
497
  "git_branch": (
498
498
  self._get_git_branch(working_dir) if working_dir else "Unknown"
@@ -639,7 +639,7 @@ class EventHandlers:
639
639
  "has_error": reason in ["error", "timeout", "failed", "blocked"],
640
640
  "working_directory": working_dir,
641
641
  "git_branch": git_branch,
642
- "timestamp": datetime.now().isoformat(),
642
+ "timestamp": datetime.now(timezone.utc).isoformat(),
643
643
  "event_type": "subagent_stop",
644
644
  "reason": reason,
645
645
  "original_request_timestamp": request_info.get("timestamp"),
@@ -715,7 +715,7 @@ class EventHandlers:
715
715
  "session_id": session_id,
716
716
  "working_directory": working_dir,
717
717
  "git_branch": git_branch,
718
- "timestamp": datetime.now().isoformat(),
718
+ "timestamp": datetime.now(timezone.utc).isoformat(),
719
719
  "contains_code": "```" in response_text,
720
720
  "contains_json": "```json" in response_text,
721
721
  "hook_event_name": "AssistantResponse", # Explicitly set for dashboard
@@ -25,7 +25,7 @@ import signal
25
25
  import subprocess
26
26
  import sys
27
27
  import threading
28
- from datetime import datetime
28
+ from datetime import datetime, timezone
29
29
  from typing import Optional, Tuple
30
30
 
31
31
  # Import extracted modules with fallback for direct execution
@@ -284,7 +284,7 @@ class ClaudeHookHandler:
284
284
  if self.duplicate_detector.is_duplicate(event):
285
285
  if DEBUG:
286
286
  print(
287
- f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
287
+ f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
288
288
  file=sys.stderr,
289
289
  )
290
290
  # Still need to output continue for this invocation
@@ -297,7 +297,7 @@ class ClaudeHookHandler:
297
297
  if DEBUG:
298
298
  hook_type = event.get("hook_event_name", "unknown")
299
299
  print(
300
- f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
300
+ f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
301
301
  file=sys.stderr,
302
302
  )
303
303
 
@@ -461,7 +461,7 @@ class ClaudeHookHandler:
461
461
  if hasattr(self, "connection_manager") and self.connection_manager:
462
462
  try:
463
463
  self.connection_manager.cleanup()
464
- except:
464
+ except Exception:
465
465
  pass # Ignore cleanup errors during destruction
466
466
 
467
467
 
@@ -22,7 +22,7 @@ import sys
22
22
  import threading
23
23
  import time
24
24
  from collections import deque
25
- from datetime import datetime
25
+ from datetime import datetime, timezone
26
26
  from pathlib import Path
27
27
  from typing import Optional
28
28
 
@@ -55,7 +55,7 @@ except ImportError:
55
55
  "type": event_data.get("type", "unknown"),
56
56
  "subtype": event_data.get("subtype", "generic"),
57
57
  "timestamp": event_data.get(
58
- "timestamp", datetime.now().isoformat()
58
+ "timestamp", datetime.now(timezone.utc).isoformat()
59
59
  ),
60
60
  "data": event_data.get("data", event_data),
61
61
  "source": source,
@@ -174,7 +174,7 @@ class HookHandler:
174
174
  raw_event = {
175
175
  "type": "hook",
176
176
  "subtype": event_type,
177
- "timestamp": datetime.now().isoformat(),
177
+ "timestamp": datetime.now(timezone.utc).isoformat(),
178
178
  "data": data,
179
179
  "source": "claude_hooks",
180
180
  "session_id": data.get("sessionId", self.current_session_id),
@@ -21,7 +21,7 @@ import sys
21
21
  import threading
22
22
  import time
23
23
  from collections import deque
24
- from datetime import datetime
24
+ from datetime import datetime, timezone
25
25
  from typing import Optional
26
26
 
27
27
  # Import extracted modules with fallback for direct execution
@@ -71,7 +71,7 @@ except ImportError:
71
71
  "type": event_data.get("type", "unknown"),
72
72
  "subtype": event_data.get("subtype", "generic"),
73
73
  "timestamp": event_data.get(
74
- "timestamp", datetime.now().isoformat()
74
+ "timestamp", datetime.now(timezone.utc).isoformat()
75
75
  ),
76
76
  "data": event_data.get("data", event_data),
77
77
  }
@@ -216,7 +216,7 @@ class ClaudeHookHandler:
216
216
 
217
217
  if session_id and agent_type and agent_type != "unknown":
218
218
  self.active_delegations[session_id] = agent_type
219
- key = f"{session_id}:{datetime.now().timestamp()}"
219
+ key = f"{session_id}:{datetime.now(timezone.utc).timestamp()}"
220
220
  self.delegation_history.append((key, agent_type))
221
221
 
222
222
  # Store request data for response tracking correlation
@@ -224,7 +224,7 @@ class ClaudeHookHandler:
224
224
  self.delegation_requests[session_id] = {
225
225
  "agent_type": agent_type,
226
226
  "request": request_data,
227
- "timestamp": datetime.now().isoformat(),
227
+ "timestamp": datetime.now(timezone.utc).isoformat(),
228
228
  }
229
229
  if DEBUG:
230
230
  print(
@@ -237,7 +237,7 @@ class ClaudeHookHandler:
237
237
  )
238
238
 
239
239
  # Clean up old delegations (older than 5 minutes)
240
- cutoff_time = datetime.now().timestamp() - 300
240
+ cutoff_time = datetime.now(timezone.utc).timestamp() - 300
241
241
  keys_to_remove = []
242
242
  for sid in list(self.active_delegations.keys()):
243
243
  # Check if this is an old entry by looking in history
@@ -259,7 +259,7 @@ class ClaudeHookHandler:
259
259
 
260
260
  def _cleanup_old_entries(self):
261
261
  """Clean up old entries to prevent memory growth."""
262
- datetime.now().timestamp() - self.MAX_CACHE_AGE_SECONDS
262
+ datetime.now(timezone.utc).timestamp() - self.MAX_CACHE_AGE_SECONDS
263
263
 
264
264
  # Clean up delegation tracking dictionaries
265
265
  for storage in [self.active_delegations, self.delegation_requests]:
@@ -281,7 +281,8 @@ class ClaudeHookHandler:
281
281
  expired_keys = [
282
282
  key
283
283
  for key, cache_time in self._git_branch_cache_time.items()
284
- if datetime.now().timestamp() - cache_time > self.MAX_CACHE_AGE_SECONDS
284
+ if datetime.now(timezone.utc).timestamp() - cache_time
285
+ > self.MAX_CACHE_AGE_SECONDS
285
286
  ]
286
287
  for key in expired_keys:
287
288
  self._git_branch_cache.pop(key, None)
@@ -315,7 +316,7 @@ class ClaudeHookHandler:
315
316
  working_dir = os.getcwd()
316
317
 
317
318
  # Check cache first (cache for 30 seconds)
318
- current_time = datetime.now().timestamp()
319
+ current_time = datetime.now(timezone.utc).timestamp()
319
320
  cache_key = working_dir
320
321
 
321
322
  if (
@@ -413,7 +414,7 @@ class ClaudeHookHandler:
413
414
  if recent_key == event_key and (current_time - recent_time) < 0.1:
414
415
  if DEBUG:
415
416
  print(
416
- f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
417
+ f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
417
418
  file=sys.stderr,
418
419
  )
419
420
  # Still need to output continue for this invocation
@@ -429,7 +430,7 @@ class ClaudeHookHandler:
429
430
  if DEBUG:
430
431
  hook_type = event.get("hook_event_name", "unknown")
431
432
  print(
432
- f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
433
+ f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
433
434
  file=sys.stderr,
434
435
  )
435
436
 
@@ -582,7 +583,7 @@ class ClaudeHookHandler:
582
583
  raw_event = {
583
584
  "type": "hook",
584
585
  "subtype": event, # e.g., "user_prompt", "pre_tool", "subagent_stop"
585
- "timestamp": datetime.now().isoformat(),
586
+ "timestamp": datetime.now(timezone.utc).isoformat(),
586
587
  "data": data,
587
588
  "source": "claude_hooks", # Identify the source
588
589
  "session_id": data.get("sessionId"), # Include session if available
@@ -857,7 +858,7 @@ class ClaudeHookHandler:
857
858
  "duration_ms": event.get("duration_ms"),
858
859
  "working_directory": working_dir,
859
860
  "git_branch": git_branch,
860
- "timestamp": datetime.now().isoformat(),
861
+ "timestamp": datetime.now(timezone.utc).isoformat(),
861
862
  "event_type": "subagent_stop",
862
863
  "reason": reason,
863
864
  "original_request_timestamp": request_info.get("timestamp"),
@@ -922,7 +923,7 @@ class ClaudeHookHandler:
922
923
  "session_id": session_id,
923
924
  "working_directory": working_dir,
924
925
  "git_branch": git_branch,
925
- "timestamp": datetime.now().isoformat(),
926
+ "timestamp": datetime.now(timezone.utc).isoformat(),
926
927
  "is_successful_completion": reason in ["completed", "finished", "done"],
927
928
  "is_error_termination": reason in ["error", "timeout", "failed", "blocked"],
928
929
  "is_delegation_related": agent_type
@@ -971,7 +972,7 @@ class ClaudeHookHandler:
971
972
  if hasattr(self, "connection_pool") and self.connection_pool:
972
973
  try:
973
974
  self.connection_pool.cleanup()
974
- except:
975
+ except Exception:
975
976
  pass # Ignore cleanup errors during destruction
976
977
 
977
978
 
@@ -20,7 +20,7 @@ import select
20
20
  import signal
21
21
  import sys
22
22
  import threading
23
- from datetime import datetime
23
+ from datetime import datetime, timezone
24
24
 
25
25
  # Import extracted modules with fallback for direct execution
26
26
  try:
@@ -127,7 +127,7 @@ class ClaudeHookHandler:
127
127
  if self.duplicate_detector.is_duplicate(event):
128
128
  if DEBUG:
129
129
  print(
130
- f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
130
+ f"[{datetime.now(timezone.utc).isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
131
131
  file=sys.stderr,
132
132
  )
133
133
  # Still need to output continue for this invocation
@@ -140,7 +140,7 @@ class ClaudeHookHandler:
140
140
  if DEBUG:
141
141
  hook_type = event.get("hook_event_name", "unknown")
142
142
  print(
143
- f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
143
+ f"\n[{datetime.now(timezone.utc).isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
144
144
  file=sys.stderr,
145
145
  )
146
146
 
@@ -278,7 +278,7 @@ class ClaudeHookHandler:
278
278
  if hasattr(self, "connection_manager") and self.connection_manager:
279
279
  try:
280
280
  self.connection_manager.cleanup()
281
- except:
281
+ except Exception:
282
282
  pass # Ignore cleanup errors during destruction
283
283
 
284
284