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
@@ -15,7 +15,7 @@ Key Features:
15
15
 
16
16
  import json
17
17
  from dataclasses import dataclass
18
- from datetime import datetime
18
+ from datetime import datetime, timezone
19
19
  from pathlib import Path
20
20
  from typing import Any, Dict, List, Optional, Tuple, Union
21
21
 
@@ -57,9 +57,9 @@ class LocalAgentTemplate:
57
57
  if self.configuration is None:
58
58
  self.configuration = {}
59
59
  if self.created_at is None:
60
- self.created_at = datetime.now().isoformat()
60
+ self.created_at = datetime.now(timezone.utc).isoformat()
61
61
  if self.updated_at is None:
62
- self.updated_at = datetime.now().isoformat()
62
+ self.updated_at = datetime.now(timezone.utc).isoformat()
63
63
 
64
64
  # Ensure metadata has required fields
65
65
  if "name" not in self.metadata:
@@ -303,7 +303,7 @@ class LocalAgentTemplateManager:
303
303
  target_dir.mkdir(parents=True, exist_ok=True)
304
304
 
305
305
  # Update timestamp
306
- template.updated_at = datetime.now().isoformat()
306
+ template.updated_at = datetime.now(timezone.utc).isoformat()
307
307
 
308
308
  # Save to JSON file
309
309
  template_file = target_dir / f"{template.agent_id}.json"
@@ -455,7 +455,7 @@ class LocalAgentTemplateManager:
455
455
 
456
456
  try:
457
457
  # Create backup directory
458
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
458
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
459
459
  backup_dir = (
460
460
  self.working_directory
461
461
  / ".claude-mpm"
@@ -633,7 +633,7 @@ class LocalAgentTemplateManager:
633
633
 
634
634
  # Update template version
635
635
  template.agent_version = new_version
636
- template.updated_at = datetime.now().isoformat()
636
+ template.updated_at = datetime.now(timezone.utc).isoformat()
637
637
 
638
638
  # Save updated template
639
639
  self.save_local_template(template)
@@ -13,7 +13,7 @@ Uses python-frontmatter and mistune for markdown parsing as recommended.
13
13
 
14
14
  import logging
15
15
  import re
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from typing import Any, Dict, List, Optional
18
18
 
19
19
  import frontmatter
@@ -156,7 +156,7 @@ class AgentManager:
156
156
  # Increment version if requested
157
157
  if increment_version:
158
158
  agent_def.metadata.increment_serial_version()
159
- agent_def.metadata.last_updated = datetime.now()
159
+ agent_def.metadata.last_updated = datetime.now(timezone.utc)
160
160
 
161
161
  # Write back
162
162
  agent_path = self._find_agent_file(name)
@@ -234,7 +234,7 @@ class AgentManager:
234
234
  # Increment version
235
235
  if increment_version:
236
236
  agent_def.metadata.increment_serial_version()
237
- agent_def.metadata.last_updated = datetime.now()
237
+ agent_def.metadata.last_updated = datetime.now(timezone.utc)
238
238
 
239
239
  # Write back
240
240
  return self.update_agent(name, {}, increment_version=False)
@@ -14,7 +14,7 @@ This module provides:
14
14
 
15
15
  import logging
16
16
  import re
17
- from datetime import datetime
17
+ from datetime import datetime, timezone
18
18
  from difflib import SequenceMatcher
19
19
  from typing import Any, Dict, List, Optional, Tuple
20
20
 
@@ -203,7 +203,7 @@ class MemoryContentManager:
203
203
  Returns:
204
204
  str: Content with updated timestamp
205
205
  """
206
- timestamp = datetime.now().isoformat() + "Z"
206
+ timestamp = datetime.now(timezone.utc).isoformat() + "Z"
207
207
  # Handle both old and new timestamp formats
208
208
  content = re.sub(
209
209
  r"<!-- Last Updated: .+? -->",
@@ -260,7 +260,7 @@ class MemoryContentManager:
260
260
 
261
261
  if not has_timestamp:
262
262
  new_lines.append(
263
- f"<!-- Last Updated: {datetime.now().isoformat()}Z -->"
263
+ f"<!-- Last Updated: {datetime.now(timezone.utc).isoformat()}Z -->"
264
264
  )
265
265
  new_lines.append("")
266
266
  else:
@@ -3,7 +3,7 @@
3
3
 
4
4
  import logging
5
5
  import re
6
- from datetime import datetime
6
+ from datetime import datetime, timezone
7
7
  from typing import Dict, List
8
8
 
9
9
 
@@ -26,7 +26,7 @@ class MemoryFormatService:
26
26
  """
27
27
  # Build header
28
28
  header = f"# {agent_id.title()} Agent Memory\n\n"
29
- header += f"Last Updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
29
+ header += f"Last Updated: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}\n\n"
30
30
  header += "## Learnings\n\n"
31
31
 
32
32
  # Build item list
@@ -13,7 +13,7 @@ This module provides:
13
13
  """
14
14
 
15
15
  import logging
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from pathlib import Path
18
18
  from typing import Any, Dict
19
19
 
@@ -58,7 +58,7 @@ class MemoryTemplateGenerator:
58
58
  """
59
59
  # Convert agent_id to proper name, handling cases like "test_agent" -> "Test"
60
60
  agent_id.replace("_agent", "").replace("_", " ").title()
61
- datetime.now().strftime("%Y-%m-%d %H:%M:%S")
61
+ datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
62
62
 
63
63
  # Create a simple template that agents will populate through learning
64
64
  return self._create_basic_memory_template(agent_id, limits)
@@ -75,7 +75,7 @@ class MemoryTemplateGenerator:
75
75
  Returns:
76
76
  str: Basic memory template
77
77
  """
78
- timestamp = datetime.now().isoformat() + "Z"
78
+ timestamp = datetime.now(timezone.utc).isoformat() + "Z"
79
79
 
80
80
  return f"""# Agent Memory: {agent_id}
81
81
  <!-- Last Updated: {timestamp} -->
@@ -30,7 +30,7 @@ import shutil
30
30
  import time
31
31
  import uuid
32
32
  from dataclasses import asdict, dataclass, field
33
- from datetime import datetime
33
+ from datetime import datetime, timezone
34
34
  from enum import Enum
35
35
  from typing import Any, Callable, Dict, List, Optional, Set, Tuple
36
36
 
@@ -90,7 +90,7 @@ class AgentModification:
90
90
  @property
91
91
  def modification_datetime(self) -> datetime:
92
92
  """Get modification timestamp as datetime."""
93
- return datetime.fromtimestamp(self.timestamp)
93
+ return datetime.fromtimestamp(self.timestamp, tz=timezone.utc)
94
94
 
95
95
  @property
96
96
  def age_seconds(self) -> float:
@@ -22,7 +22,7 @@ import os
22
22
  import sys
23
23
  import time
24
24
  from dataclasses import asdict, dataclass
25
- from datetime import datetime
25
+ from datetime import datetime, timezone
26
26
  from enum import Enum
27
27
  from queue import Full, Queue
28
28
  from threading import Lock, Thread
@@ -184,7 +184,7 @@ class AsyncSessionLogger:
184
184
  return session_id
185
185
 
186
186
  # Generate timestamp-based session ID
187
- session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
187
+ session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
188
188
  logger.info(f"Generated session ID: {session_id}")
189
189
  return session_id
190
190
 
@@ -406,7 +406,7 @@ class AsyncSessionLogger:
406
406
  agent_name = metadata["agent"].replace(" ", "_").lower()
407
407
 
408
408
  # Create timestamp with microsecond precision
409
- now = datetime.now()
409
+ now = datetime.now(timezone.utc)
410
410
  timestamp = now.isoformat()
411
411
  microseconds = now.microsecond
412
412
 
@@ -13,7 +13,7 @@ Configuration via .claude-mpm/configuration.yaml.
13
13
  import json
14
14
  import logging
15
15
  import os
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from typing import Any, Dict, Optional
18
18
 
19
19
  # Import configuration manager
@@ -133,7 +133,7 @@ class ClaudeSessionLogger:
133
133
  # Generate a default based on timestamp if nothing found
134
134
  if not session_id:
135
135
  # Use a timestamp-based session ID as fallback
136
- session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
136
+ session_id = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
137
137
  logger.info(f"No Claude session ID found, using generated: {session_id}")
138
138
  else:
139
139
  logger.info(f"Using Claude session ID: {session_id}")
@@ -156,7 +156,7 @@ class ClaudeSessionLogger:
156
156
  agent_name = agent_name.replace(" ", "_").lower()
157
157
 
158
158
  # Generate timestamp with microseconds for uniqueness
159
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
159
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S_%f")
160
160
 
161
161
  # Create filename: session_id-agent-timestamp.json
162
162
  return f"{self.session_id}-{agent_name}-{timestamp}.json"
@@ -216,7 +216,7 @@ class ClaudeSessionLogger:
216
216
 
217
217
  # Prepare response data with standardized field names
218
218
  response_data = {
219
- "timestamp": datetime.now().isoformat(),
219
+ "timestamp": datetime.now(timezone.utc).isoformat(),
220
220
  "session_id": self.session_id,
221
221
  "request": request_summary, # Standardized field name
222
222
  "response": response_content, # Already correct
@@ -336,7 +336,9 @@ class AgentListingService(IAgentListingService):
336
336
  self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
337
337
  return AgentTierInfo(project=[], user=[], system=[])
338
338
 
339
- def get_agent_details(self, agent_name: str) -> Optional[Dict[str, Any]]:
339
+ def get_agent_details(
340
+ self, agent_name: str
341
+ ) -> Optional[Dict[str, Any]]:
340
342
  """Get detailed information for a specific agent."""
341
343
  cache_key = f"agent_details_{agent_name}"
342
344
  cached = self._get_from_cache(cache_key)
@@ -1,3 +1,5 @@
1
+ import re
2
+
1
3
  """
2
4
  Agent Validation Service
3
5
  ========================
@@ -17,7 +17,7 @@ DESIGN DECISIONS:
17
17
 
18
18
  import os
19
19
  from abc import ABC, abstractmethod
20
- from datetime import datetime
20
+ from datetime import datetime, timezone
21
21
  from pathlib import Path
22
22
  from typing import Any, Dict, List, Optional
23
23
 
@@ -223,7 +223,9 @@ class MemoryCRUDService(IMemoryCRUDService):
223
223
  stat = memory_file.stat()
224
224
  file_stats = {
225
225
  "size_kb": stat.st_size / 1024,
226
- "modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
226
+ "modified": datetime.fromtimestamp(
227
+ stat.st_mtime, tz=timezone.utc
228
+ ).isoformat(),
227
229
  "path": str(memory_file),
228
230
  }
229
231
 
@@ -256,7 +258,7 @@ class MemoryCRUDService(IMemoryCRUDService):
256
258
  "file_stats": {
257
259
  "size_kb": stat.st_size / 1024,
258
260
  "modified": datetime.fromtimestamp(
259
- stat.st_mtime
261
+ stat.st_mtime, tz=timezone.utc
260
262
  ).isoformat(),
261
263
  "path": str(memory_file),
262
264
  },
@@ -431,10 +433,10 @@ class MemoryCRUDService(IMemoryCRUDService):
431
433
  {
432
434
  "size_kb": stat.st_size / 1024,
433
435
  "modified": datetime.fromtimestamp(
434
- stat.st_mtime
436
+ stat.st_mtime, tz=timezone.utc
435
437
  ).isoformat(),
436
438
  "created": datetime.fromtimestamp(
437
- stat.st_ctime
439
+ stat.st_ctime, tz=timezone.utc
438
440
  ).isoformat(),
439
441
  }
440
442
  )
@@ -499,7 +501,10 @@ class MemoryCRUDService(IMemoryCRUDService):
499
501
  continue
500
502
 
501
503
  stat = memory_file.stat()
502
- age_days = (datetime.now() - datetime.fromtimestamp(stat.st_mtime)).days
504
+ age_days = (
505
+ datetime.now(timezone.utc)
506
+ - datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
507
+ ).days
503
508
 
504
509
  # Identify files older than 30 days as candidates
505
510
  if age_days > 30:
@@ -200,7 +200,7 @@ class MemoryOutputFormatter(IMemoryOutputFormatter):
200
200
  try:
201
201
  dt = datetime.fromisoformat(last_modified.replace("Z", "+00:00"))
202
202
  last_modified_str = dt.strftime("%Y-%m-%d %H:%M:%S")
203
- except:
203
+ except Exception:
204
204
  last_modified_str = last_modified
205
205
 
206
206
  # Status indicator based on usage
@@ -18,7 +18,7 @@ import json
18
18
  import uuid
19
19
  from abc import ABC, abstractmethod
20
20
  from dataclasses import dataclass, field
21
- from datetime import datetime, timedelta
21
+ from datetime import datetime, timedelta, timezone
22
22
  from pathlib import Path
23
23
  from typing import Any, Dict, List, Optional
24
24
 
@@ -151,8 +151,12 @@ class SessionInfo:
151
151
 
152
152
  id: str
153
153
  context: str = "default"
154
- created_at: str = field(default_factory=lambda: datetime.now().isoformat())
155
- last_used: str = field(default_factory=lambda: datetime.now().isoformat())
154
+ created_at: str = field(
155
+ default_factory=lambda: datetime.now(timezone.utc).isoformat()
156
+ )
157
+ last_used: str = field(
158
+ default_factory=lambda: datetime.now(timezone.utc).isoformat()
159
+ )
156
160
  use_count: int = 0
157
161
  agents_run: List[Dict[str, Any]] = field(default_factory=list)
158
162
  metadata: Dict[str, Any] = field(default_factory=dict)
@@ -175,8 +179,8 @@ class SessionInfo:
175
179
  return cls(
176
180
  id=data["id"],
177
181
  context=data.get("context", "default"),
178
- created_at=data.get("created_at", datetime.now().isoformat()),
179
- last_used=data.get("last_used", datetime.now().isoformat()),
182
+ created_at=data.get("created_at", datetime.now(timezone.utc).isoformat()),
183
+ last_used=data.get("last_used", datetime.now(timezone.utc).isoformat()),
180
184
  use_count=data.get("use_count", 0),
181
185
  agents_run=data.get("agents_run", []),
182
186
  metadata=data.get("metadata", {}),
@@ -284,7 +288,7 @@ class SessionManager(ISessionManager):
284
288
  # Check session age
285
289
  try:
286
290
  created = datetime.fromisoformat(session.created_at)
287
- age = datetime.now() - created
291
+ age = datetime.now(timezone.utc) - created
288
292
 
289
293
  if age > timedelta(days=7):
290
294
  validation.warnings.append(f"Session is {age.days} days old")
@@ -355,10 +359,10 @@ class SessionManager(ISessionManager):
355
359
  {
356
360
  "agent": agent,
357
361
  "task": task[:100], # Truncate long tasks
358
- "timestamp": datetime.now().isoformat(),
362
+ "timestamp": datetime.now(timezone.utc).isoformat(),
359
363
  }
360
364
  )
361
- session.last_used = datetime.now().isoformat()
365
+ session.last_used = datetime.now(timezone.utc).isoformat()
362
366
  session.use_count += 1
363
367
 
364
368
  self.save_session(session)
@@ -370,7 +374,7 @@ class SessionManager(ISessionManager):
370
374
 
371
375
  WHY: Prevents unbounded growth of session data and improves performance.
372
376
  """
373
- now = datetime.now()
377
+ now = datetime.now(timezone.utc)
374
378
  max_age = timedelta(hours=max_age_hours)
375
379
 
376
380
  expired_ids = []
@@ -418,7 +422,7 @@ class SessionManager(ISessionManager):
418
422
  return True
419
423
 
420
424
  # Create timestamped archive file
421
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
425
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
422
426
  archive_name = f"sessions_archive_{timestamp}.json.gz"
423
427
  archive_path = archive_dir / archive_name
424
428
 
@@ -509,5 +513,5 @@ class ManagedSession:
509
513
  """Exit session context with cleanup."""
510
514
  if self.session:
511
515
  # Update last used time
512
- self.session.last_used = datetime.now().isoformat()
516
+ self.session.last_used = datetime.now(timezone.utc).isoformat()
513
517
  self.manager.save_session(self.session)
@@ -21,7 +21,7 @@ ARCHITECTURE:
21
21
  """
22
22
 
23
23
  import logging
24
- from datetime import datetime
24
+ from datetime import datetime, timezone
25
25
  from pathlib import Path
26
26
  from typing import Any, Dict, List, Optional, Set
27
27
 
@@ -169,7 +169,7 @@ class MemoryManager(IMemoryManager):
169
169
  ]
170
170
 
171
171
  # Add new memory as a bullet point
172
- timestamp = datetime.now().isoformat()
172
+ timestamp = datetime.now(timezone.utc).isoformat()
173
173
  lines.append(f"- [{timestamp}] {key}: {value}")
174
174
 
175
175
  # Write back
@@ -335,19 +335,37 @@ class MemoryManager(IMemoryManager):
335
335
  if pm_memories:
336
336
  aggregated_pm = self._aggregate_memories(pm_memories)
337
337
  result["actual_memories"] = aggregated_pm
338
- memory_size = len(aggregated_pm.encode("utf-8"))
339
- self.logger.info(
340
- f"Aggregated PM memory ({memory_size:,} bytes) from {len(pm_memories)} source(s)"
341
- )
338
+ # Count actual memory items in aggregated content
339
+ memory_items = [
340
+ line.strip()
341
+ for line in aggregated_pm.split("\n")
342
+ if line.strip().startswith("-")
343
+ ]
344
+ if memory_items:
345
+ self.logger.info(
346
+ f"Aggregated PM memory: {len(memory_items)} total items from {len(pm_memories)} source(s)"
347
+ )
348
+ else:
349
+ self.logger.debug(
350
+ f"Aggregated PM memory from {len(pm_memories)} source(s) (no items)"
351
+ )
342
352
 
343
353
  # Store agent memories (already aggregated per agent)
344
354
  if agent_memories_dict:
345
355
  result["agent_memories"] = agent_memories_dict
346
356
  for agent_name, memory_content in agent_memories_dict.items():
347
- memory_size = len(memory_content.encode("utf-8"))
348
- self.logger.debug(
349
- f"Aggregated {agent_name} memory: {memory_size:,} bytes"
350
- )
357
+ # Count actual memory items
358
+ memory_items = [
359
+ line.strip()
360
+ for line in memory_content.split("\n")
361
+ if line.strip().startswith("-")
362
+ ]
363
+ if memory_items:
364
+ self.logger.debug(
365
+ f"Aggregated {agent_name} memory: {len(memory_items)} items"
366
+ )
367
+ else:
368
+ self.logger.debug(f"Aggregated {agent_name} memory: no items")
351
369
 
352
370
  # Log summary
353
371
  if self._stats["loaded_count"] > 0 or self._stats["skipped_count"] > 0:
@@ -410,10 +428,21 @@ class MemoryManager(IMemoryManager):
410
428
  "path": pm_memory_path,
411
429
  }
412
430
  )
413
- memory_size = len(loaded_content.encode("utf-8"))
414
- self.logger.info(
415
- f"Loaded {source} PM memory: {pm_memory_path} ({memory_size:,} bytes)"
416
- )
431
+ # Count actual memory items (lines starting with "-")
432
+ memory_items = [
433
+ line.strip()
434
+ for line in loaded_content.split("\n")
435
+ if line.strip().startswith("-")
436
+ ]
437
+ if memory_items:
438
+ self.logger.info(
439
+ f"Loaded {source} PM memory: {len(memory_items)} items"
440
+ )
441
+ else:
442
+ # Skip logging if no actual memory items
443
+ self.logger.debug(
444
+ f"Skipped {source} PM memory: {pm_memory_path} (no memory items)"
445
+ )
417
446
  self._stats["loaded_count"] += 1
418
447
  except Exception as e:
419
448
  self.logger.error(
@@ -471,20 +500,47 @@ class MemoryManager(IMemoryManager):
471
500
  }
472
501
  )
473
502
 
474
- memory_size = len(loaded_content.encode("utf-8"))
475
- self.logger.info(
476
- f"Loaded {source} memory for {agent_name}: {memory_file.name} ({memory_size:,} bytes)"
477
- )
503
+ # Count actual memory items (lines starting with "-")
504
+ memory_items = [
505
+ line.strip()
506
+ for line in loaded_content.split("\n")
507
+ if line.strip().startswith("-")
508
+ ]
509
+ if memory_items:
510
+ self.logger.info(
511
+ f"Loaded {source} memory for {agent_name}: {len(memory_items)} items"
512
+ )
513
+ else:
514
+ # Skip logging if no actual memory items
515
+ self.logger.debug(
516
+ f"Skipped {source} memory for {agent_name}: {memory_file.name} (no memory items)"
517
+ )
478
518
  self._stats["loaded_count"] += 1
479
519
  except Exception as e:
480
520
  self.logger.error(
481
521
  f"Failed to load agent memory from {memory_file}: {e}"
482
522
  )
483
523
  else:
484
- # Log skipped memories
485
- self.logger.info(
486
- f"Skipped {source} memory: {memory_file.name} (agent '{agent_name}' not deployed)"
487
- )
524
+ # Log skipped memories only if they contain actual items
525
+ try:
526
+ loaded_content = memory_file.read_text(encoding="utf-8")
527
+ memory_items = [
528
+ line.strip()
529
+ for line in loaded_content.split("\n")
530
+ if line.strip().startswith("-")
531
+ ]
532
+ if memory_items:
533
+ self.logger.info(
534
+ f"Skipped {source} memory: {memory_file.name} (agent '{agent_name}' not deployed, {len(memory_items)} items)"
535
+ )
536
+ else:
537
+ # Don't log if file has no actual memory items
538
+ self.logger.debug(
539
+ f"Skipped {source} memory: {memory_file.name} (agent '{agent_name}' not deployed, no items)"
540
+ )
541
+ except Exception:
542
+ # If we can't read the file, just skip silently
543
+ pass
488
544
 
489
545
  # Detect naming mismatches
490
546
  alt_name = (
@@ -570,7 +626,9 @@ class MemoryManager(IMemoryManager):
570
626
  lines.append("# Agent Memory")
571
627
 
572
628
  # Add latest timestamp
573
- lines.append(f"<!-- Last Updated: {datetime.now().isoformat()}Z -->")
629
+ lines.append(
630
+ f"<!-- Last Updated: {datetime.now(timezone.utc).isoformat()}Z -->"
631
+ )
574
632
  lines.append("")
575
633
 
576
634
  # Add all unique items (sorted for consistency)
@@ -163,7 +163,7 @@ class PathResolver(IPathResolver):
163
163
  self.logger.debug(f"No project root found from {start_path}")
164
164
  return None
165
165
 
166
- def detect_framework_path(self) -> Optional[Path]:
166
+ def detect_framework_path(self) -> Optional[Path]: # noqa: PLR0911
167
167
  """
168
168
  Auto-detect claude-mpm framework using unified path management.
169
169
 
@@ -136,7 +136,7 @@ class InstallationCheck(BaseDiagnosticCheck):
136
136
  details={"error": str(e)},
137
137
  )
138
138
 
139
- def _check_installation_method(self) -> DiagnosticResult:
139
+ def _check_installation_method(self) -> DiagnosticResult: # noqa: PLR0911
140
140
  """Detect how claude-mpm was installed."""
141
141
  methods_found = []
142
142
  details = {}
@@ -18,7 +18,7 @@ import sys
18
18
  import threading
19
19
  import time
20
20
  from collections import defaultdict
21
- from datetime import datetime
21
+ from datetime import datetime, timezone
22
22
  from typing import Any, Dict, List, Optional
23
23
 
24
24
  try:
@@ -267,7 +267,9 @@ class EventAggregator:
267
267
  try:
268
268
  # Extract event metadata
269
269
  event_type = event_data.get("type", "unknown")
270
- timestamp = event_data.get("timestamp", datetime.utcnow().isoformat() + "Z")
270
+ timestamp = event_data.get(
271
+ "timestamp", datetime.now(timezone.utc).isoformat() + "Z"
272
+ )
271
273
  data = event_data.get("data", {})
272
274
 
273
275
  # Update statistics
@@ -15,7 +15,7 @@ DO NOT use "event" or "type" fields - use "hook_event_name" instead!
15
15
  """
16
16
 
17
17
  import logging
18
- from datetime import datetime
18
+ from datetime import datetime, timezone
19
19
  from typing import Any
20
20
 
21
21
  from .event_bus import EventBus
@@ -223,7 +223,9 @@ class DirectSocketIORelay:
223
223
  event_type, broadcast_data
224
224
  )
225
225
  self.stats["events_relayed"] += 1
226
- self.stats["last_relay_time"] = datetime.now().isoformat()
226
+ self.stats["last_relay_time"] = datetime.now(
227
+ timezone.utc
228
+ ).isoformat()
227
229
 
228
230
  # Reset retry counter on successful broadcast
229
231
  if self.connection_retries > 0:
@@ -258,7 +260,7 @@ class DirectSocketIORelay:
258
260
  logger.info(
259
261
  f"[DirectRelay] Retry successful for {event_type}"
260
262
  )
261
- except:
263
+ except Exception:
262
264
  pass # Already counted as failed
263
265
  else:
264
266
  # Enhanced logging when broadcaster is not available
@@ -11,7 +11,7 @@ WHY pyee over alternatives:
11
11
  import asyncio
12
12
  import logging
13
13
  import threading
14
- from datetime import datetime
14
+ from datetime import datetime, timezone
15
15
  from typing import Any, Callable, Dict, List, Optional, Set
16
16
 
17
17
  from pyee.asyncio import AsyncIOEventEmitter
@@ -210,7 +210,7 @@ class EventBus:
210
210
 
211
211
  # Update stats
212
212
  self._stats["events_published"] += 1
213
- self._stats["last_event_time"] = datetime.now().isoformat()
213
+ self._stats["last_event_time"] = datetime.now(timezone.utc).isoformat()
214
214
 
215
215
  if self._debug:
216
216
  logger.debug(f"Published event: {event_type}")
@@ -302,7 +302,7 @@ class EventBus:
302
302
  data: The event data
303
303
  """
304
304
  event_record = {
305
- "timestamp": datetime.now().isoformat(),
305
+ "timestamp": datetime.now(timezone.utc).isoformat(),
306
306
  "type": event_type,
307
307
  "data": data,
308
308
  }