claude-mpm 4.3.12__py3-none-any.whl → 4.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +390 -28
  3. claude_mpm/agents/templates/data_engineer.json +39 -14
  4. claude_mpm/cli/commands/agent_manager.py +3 -3
  5. claude_mpm/cli/commands/agents.py +2 -2
  6. claude_mpm/cli/commands/aggregate.py +1 -1
  7. claude_mpm/cli/commands/config.py +2 -2
  8. claude_mpm/cli/commands/configure.py +5 -5
  9. claude_mpm/cli/commands/configure_tui.py +7 -7
  10. claude_mpm/cli/commands/dashboard.py +1 -1
  11. claude_mpm/cli/commands/debug.py +5 -5
  12. claude_mpm/cli/commands/mcp.py +1 -1
  13. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  14. claude_mpm/cli/commands/mcp_config.py +7 -10
  15. claude_mpm/cli/commands/mcp_external_commands.py +40 -32
  16. claude_mpm/cli/commands/mcp_install_commands.py +38 -10
  17. claude_mpm/cli/commands/mcp_setup_external.py +143 -102
  18. claude_mpm/cli/commands/monitor.py +2 -2
  19. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  20. claude_mpm/cli/commands/run.py +46 -2
  21. claude_mpm/cli/commands/search.py +41 -34
  22. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  23. claude_mpm/cli/parsers/mcp_parser.py +1 -3
  24. claude_mpm/cli/parsers/search_parser.py +10 -4
  25. claude_mpm/cli/startup_logging.py +3 -5
  26. claude_mpm/cli/utils.py +1 -1
  27. claude_mpm/core/agent_registry.py +12 -8
  28. claude_mpm/core/agent_session_manager.py +8 -8
  29. claude_mpm/core/api_validator.py +4 -4
  30. claude_mpm/core/base_service.py +10 -10
  31. claude_mpm/core/cache.py +5 -5
  32. claude_mpm/core/config_constants.py +1 -1
  33. claude_mpm/core/container.py +1 -1
  34. claude_mpm/core/error_handler.py +2 -2
  35. claude_mpm/core/file_utils.py +1 -1
  36. claude_mpm/core/framework_loader.py +3 -3
  37. claude_mpm/core/hook_manager.py +8 -6
  38. claude_mpm/core/instruction_reinforcement_hook.py +2 -2
  39. claude_mpm/core/interactive_session.py +1 -1
  40. claude_mpm/core/lazy.py +3 -3
  41. claude_mpm/core/log_manager.py +16 -12
  42. claude_mpm/core/logger.py +16 -11
  43. claude_mpm/core/logging_config.py +4 -2
  44. claude_mpm/core/oneshot_session.py +1 -1
  45. claude_mpm/core/optimized_agent_loader.py +6 -6
  46. claude_mpm/core/output_style_manager.py +1 -1
  47. claude_mpm/core/pm_hook_interceptor.py +3 -3
  48. claude_mpm/core/service_registry.py +1 -1
  49. claude_mpm/core/session_manager.py +11 -9
  50. claude_mpm/core/socketio_pool.py +13 -13
  51. claude_mpm/core/types.py +2 -2
  52. claude_mpm/core/unified_agent_registry.py +2 -2
  53. claude_mpm/core/unified_paths.py +1 -1
  54. claude_mpm/dashboard/analysis_runner.py +4 -4
  55. claude_mpm/dashboard/api/simple_directory.py +1 -1
  56. claude_mpm/generators/agent_profile_generator.py +4 -2
  57. claude_mpm/hooks/base_hook.py +2 -2
  58. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  59. claude_mpm/hooks/claude_hooks/event_handlers.py +12 -12
  60. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -4
  61. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +3 -3
  62. claude_mpm/hooks/claude_hooks/hook_handler_original.py +15 -14
  63. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +4 -4
  64. claude_mpm/hooks/claude_hooks/installer.py +3 -3
  65. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  66. claude_mpm/hooks/claude_hooks/response_tracking.py +3 -3
  67. claude_mpm/hooks/claude_hooks/services/connection_manager.py +5 -5
  68. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +3 -3
  69. claude_mpm/hooks/claude_hooks/services/state_manager.py +8 -7
  70. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  71. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  72. claude_mpm/hooks/memory_integration_hook.py +1 -1
  73. claude_mpm/hooks/tool_call_interceptor.py +2 -2
  74. claude_mpm/models/agent_session.py +5 -5
  75. claude_mpm/services/__init__.py +1 -1
  76. claude_mpm/services/agent_capabilities_service.py +1 -1
  77. claude_mpm/services/agents/agent_builder.py +3 -3
  78. claude_mpm/services/agents/deployment/agent_deployment.py +2 -1
  79. claude_mpm/services/agents/deployment/agent_discovery_service.py +9 -3
  80. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +7 -5
  81. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +3 -1
  82. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  83. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  84. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  85. claude_mpm/services/agents/deployment/agent_template_builder.py +1 -1
  86. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  87. claude_mpm/services/agents/deployment/deployment_wrapper.py +2 -3
  88. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +1 -1
  89. claude_mpm/services/agents/loading/agent_profile_loader.py +5 -3
  90. claude_mpm/services/agents/loading/base_agent_manager.py +2 -2
  91. claude_mpm/services/agents/local_template_manager.py +6 -6
  92. claude_mpm/services/agents/management/agent_management_service.py +3 -3
  93. claude_mpm/services/agents/memory/content_manager.py +3 -3
  94. claude_mpm/services/agents/memory/memory_format_service.py +2 -2
  95. claude_mpm/services/agents/memory/template_generator.py +3 -3
  96. claude_mpm/services/agents/registry/__init__.py +1 -1
  97. claude_mpm/services/agents/registry/modification_tracker.py +2 -2
  98. claude_mpm/services/async_session_logger.py +3 -3
  99. claude_mpm/services/claude_session_logger.py +4 -4
  100. claude_mpm/services/cli/agent_listing_service.py +1 -1
  101. claude_mpm/services/cli/agent_validation_service.py +1 -0
  102. claude_mpm/services/cli/memory_crud_service.py +11 -6
  103. claude_mpm/services/cli/memory_output_formatter.py +1 -1
  104. claude_mpm/services/cli/session_manager.py +15 -11
  105. claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
  106. claude_mpm/services/core/memory_manager.py +81 -23
  107. claude_mpm/services/core/path_resolver.py +2 -2
  108. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  109. claude_mpm/services/event_aggregator.py +4 -2
  110. claude_mpm/services/event_bus/direct_relay.py +5 -3
  111. claude_mpm/services/event_bus/event_bus.py +3 -3
  112. claude_mpm/services/event_bus/relay.py +6 -4
  113. claude_mpm/services/events/consumers/dead_letter.py +5 -3
  114. claude_mpm/services/events/core.py +3 -3
  115. claude_mpm/services/events/producers/hook.py +6 -6
  116. claude_mpm/services/events/producers/system.py +8 -8
  117. claude_mpm/services/exceptions.py +5 -5
  118. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -3
  119. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  120. claude_mpm/services/hook_installer_service.py +1 -1
  121. claude_mpm/services/infrastructure/context_preservation.py +6 -4
  122. claude_mpm/services/infrastructure/daemon_manager.py +2 -2
  123. claude_mpm/services/infrastructure/logging.py +2 -2
  124. claude_mpm/services/mcp_config_manager.py +175 -30
  125. claude_mpm/services/mcp_gateway/__init__.py +1 -1
  126. claude_mpm/services/mcp_gateway/auto_configure.py +3 -3
  127. claude_mpm/services/mcp_gateway/config/config_loader.py +1 -1
  128. claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
  129. claude_mpm/services/mcp_gateway/core/base.py +2 -2
  130. claude_mpm/services/mcp_gateway/main.py +21 -7
  131. claude_mpm/services/mcp_gateway/registry/tool_registry.py +10 -8
  132. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
  133. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  134. claude_mpm/services/mcp_gateway/server/stdio_server.py +4 -3
  135. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  136. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +7 -5
  137. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +190 -137
  138. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +5 -5
  139. claude_mpm/services/mcp_gateway/tools/hello_world.py +9 -9
  140. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +16 -16
  141. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +17 -17
  142. claude_mpm/services/memory/builder.py +7 -5
  143. claude_mpm/services/memory/indexed_memory.py +4 -4
  144. claude_mpm/services/memory/optimizer.py +6 -6
  145. claude_mpm/services/memory/router.py +3 -3
  146. claude_mpm/services/monitor/daemon.py +1 -1
  147. claude_mpm/services/monitor/daemon_manager.py +6 -6
  148. claude_mpm/services/monitor/event_emitter.py +2 -2
  149. claude_mpm/services/monitor/handlers/file.py +1 -1
  150. claude_mpm/services/monitor/management/lifecycle.py +1 -1
  151. claude_mpm/services/monitor/server.py +4 -4
  152. claude_mpm/services/monitor_build_service.py +2 -2
  153. claude_mpm/services/port_manager.py +2 -2
  154. claude_mpm/services/response_tracker.py +2 -2
  155. claude_mpm/services/session_management_service.py +3 -2
  156. claude_mpm/services/socketio/client_proxy.py +2 -2
  157. claude_mpm/services/socketio/dashboard_server.py +4 -3
  158. claude_mpm/services/socketio/event_normalizer.py +12 -8
  159. claude_mpm/services/socketio/handlers/base.py +2 -2
  160. claude_mpm/services/socketio/handlers/connection.py +10 -10
  161. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  162. claude_mpm/services/socketio/handlers/file.py +1 -1
  163. claude_mpm/services/socketio/handlers/git.py +1 -1
  164. claude_mpm/services/socketio/handlers/hook.py +16 -15
  165. claude_mpm/services/socketio/migration_utils.py +1 -1
  166. claude_mpm/services/socketio/monitor_client.py +5 -5
  167. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  168. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  169. claude_mpm/services/socketio/server/core.py +7 -5
  170. claude_mpm/services/socketio/server/eventbus_integration.py +18 -11
  171. claude_mpm/services/socketio/server/main.py +13 -13
  172. claude_mpm/services/socketio_client_manager.py +4 -4
  173. claude_mpm/services/system_instructions_service.py +2 -2
  174. claude_mpm/services/ticket_services/validation_service.py +1 -1
  175. claude_mpm/services/utility_service.py +5 -2
  176. claude_mpm/services/version_control/branch_strategy.py +2 -2
  177. claude_mpm/services/version_control/git_operations.py +22 -20
  178. claude_mpm/services/version_control/semantic_versioning.py +3 -3
  179. claude_mpm/services/version_control/version_parser.py +7 -5
  180. claude_mpm/services/visualization/mermaid_generator.py +1 -1
  181. claude_mpm/storage/state_storage.py +1 -1
  182. claude_mpm/tools/code_tree_analyzer.py +19 -18
  183. claude_mpm/tools/code_tree_builder.py +2 -2
  184. claude_mpm/tools/code_tree_events.py +10 -8
  185. claude_mpm/tools/socketio_debug.py +3 -3
  186. claude_mpm/utils/agent_dependency_loader.py +2 -2
  187. claude_mpm/utils/dependency_strategies.py +8 -3
  188. claude_mpm/utils/environment_context.py +2 -2
  189. claude_mpm/utils/error_handler.py +2 -2
  190. claude_mpm/utils/file_utils.py +1 -1
  191. claude_mpm/utils/imports.py +1 -1
  192. claude_mpm/utils/log_cleanup.py +21 -7
  193. claude_mpm/validation/agent_validator.py +2 -2
  194. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/METADATA +1 -1
  195. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/RECORD +199 -199
  196. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/WHEEL +0 -0
  197. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/entry_points.txt +0 -0
  198. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/licenses/LICENSE +0 -0
  199. {claude_mpm-4.3.12.dist-info → claude_mpm-4.3.13.dist-info}/top_level.txt +0 -0
@@ -19,6 +19,7 @@ import hashlib
19
19
  import json
20
20
  import time
21
21
  from dataclasses import dataclass
22
+ from datetime import datetime, timezone
22
23
  from pathlib import Path
23
24
  from typing import Any, Dict, List, Optional
24
25
 
@@ -272,7 +273,7 @@ class GitignoreManager:
272
273
 
273
274
  return patterns
274
275
 
275
- def _basic_should_ignore(self, path: Path, working_dir: Path) -> bool:
276
+ def _basic_should_ignore(self, path: Path, working_dir: Path) -> bool: # noqa: PLR0911
276
277
  """Basic pattern matching fallback when pathspec is not available.
277
278
 
278
279
  Args:
@@ -608,7 +609,7 @@ class PythonAnalyzer:
608
609
 
609
610
  return nodes
610
611
 
611
- def _get_assignment_signature(self, node: ast.Assign, var_name: str) -> str:
612
+ def _get_assignment_signature(self, node: ast.Assign, var_name: str) -> str: # noqa: PLR0911
612
613
  """Get assignment signature string."""
613
614
  try:
614
615
  # Try to get a simple representation of the value
@@ -623,7 +624,7 @@ class PythonAnalyzer:
623
624
  if isinstance(node.value, ast.Dict):
624
625
  return f"{var_name} = {{...}}"
625
626
  return f"{var_name} = ..."
626
- except:
627
+ except Exception:
627
628
  return f"{var_name} = ..."
628
629
 
629
630
 
@@ -1292,7 +1293,7 @@ class CodeTreeAnalyzer:
1292
1293
 
1293
1294
  # Emit discovery start event
1294
1295
  if self.emitter:
1295
- from datetime import datetime
1296
+ from datetime import datetime, timezone
1296
1297
 
1297
1298
  self.emitter.emit(
1298
1299
  "info",
@@ -1301,7 +1302,7 @@ class CodeTreeAnalyzer:
1301
1302
  "action": "scanning_directory",
1302
1303
  "path": str(directory),
1303
1304
  "message": f"Starting discovery of {directory.name}",
1304
- "timestamp": datetime.now().isoformat(),
1305
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1305
1306
  },
1306
1307
  )
1307
1308
 
@@ -1336,7 +1337,7 @@ class CodeTreeAnalyzer:
1336
1337
  "path": str(item),
1337
1338
  "reason": "gitignore pattern",
1338
1339
  "message": f"Ignored by gitignore: {item.name}",
1339
- "timestamp": datetime.now().isoformat(),
1340
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1340
1341
  },
1341
1342
  )
1342
1343
  ignored_count += 1
@@ -1354,7 +1355,7 @@ class CodeTreeAnalyzer:
1354
1355
  "path": str(item),
1355
1356
  "reason": "custom pattern",
1356
1357
  "message": f"Ignored by pattern: {item.name}",
1357
- "timestamp": datetime.now().isoformat(),
1358
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1358
1359
  },
1359
1360
  )
1360
1361
  ignored_count += 1
@@ -1375,7 +1376,7 @@ class CodeTreeAnalyzer:
1375
1376
  "path": str(item.name),
1376
1377
  "reason": "no code files",
1377
1378
  "message": f"Skipped directory without code: {item.name}",
1378
- "timestamp": datetime.now().isoformat(),
1379
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1379
1380
  },
1380
1381
  )
1381
1382
  ignored_count += 1
@@ -1395,7 +1396,7 @@ class CodeTreeAnalyzer:
1395
1396
  "type": "discovery.directory",
1396
1397
  "path": str(item),
1397
1398
  "message": f"Found directory: {item.name}",
1398
- "timestamp": datetime.now().isoformat(),
1399
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1399
1400
  },
1400
1401
  )
1401
1402
  dirs_count += 1
@@ -1441,7 +1442,7 @@ class CodeTreeAnalyzer:
1441
1442
  "language": language,
1442
1443
  "size": item.stat().st_size,
1443
1444
  "message": f"Found file: {item.name} ({language})",
1444
- "timestamp": datetime.now().isoformat(),
1445
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1445
1446
  },
1446
1447
  )
1447
1448
  files_count += 1
@@ -1481,7 +1482,7 @@ class CodeTreeAnalyzer:
1481
1482
  "ignored": ignored_count,
1482
1483
  },
1483
1484
  "message": f"Discovery complete: {files_count} files, {dirs_count} directories, {ignored_count} ignored",
1484
- "timestamp": datetime.now().isoformat(),
1485
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1485
1486
  },
1486
1487
  )
1487
1488
 
@@ -1538,7 +1539,7 @@ class CodeTreeAnalyzer:
1538
1539
  "file": str(path),
1539
1540
  "language": language,
1540
1541
  "message": f"Analyzing: {path.name}",
1541
- "timestamp": datetime.now().isoformat(),
1542
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1542
1543
  },
1543
1544
  )
1544
1545
 
@@ -1557,7 +1558,7 @@ class CodeTreeAnalyzer:
1557
1558
  "type": "cache.hit",
1558
1559
  "file": str(path),
1559
1560
  "message": f"Using cached analysis for {path.name}",
1560
- "timestamp": datetime.now().isoformat(),
1561
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1561
1562
  },
1562
1563
  )
1563
1564
  else:
@@ -1571,7 +1572,7 @@ class CodeTreeAnalyzer:
1571
1572
  "type": "cache.miss",
1572
1573
  "file": str(path),
1573
1574
  "message": f"Cache miss, analyzing fresh: {path.name}",
1574
- "timestamp": datetime.now().isoformat(),
1575
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1575
1576
  },
1576
1577
  )
1577
1578
 
@@ -1594,7 +1595,7 @@ class CodeTreeAnalyzer:
1594
1595
  "type": "analysis.parse",
1595
1596
  "file": str(path),
1596
1597
  "message": f"Parsing file content: {path.name}",
1597
- "timestamp": datetime.now().isoformat(),
1598
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1598
1599
  },
1599
1600
  )
1600
1601
 
@@ -1626,7 +1627,7 @@ class CodeTreeAnalyzer:
1626
1627
  "line_start": node.line_start,
1627
1628
  "complexity": node.complexity,
1628
1629
  "message": f"Found {node.node_type}: {node.name}",
1629
- "timestamp": datetime.now().isoformat(),
1630
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1630
1631
  },
1631
1632
  )
1632
1633
 
@@ -1667,7 +1668,7 @@ class CodeTreeAnalyzer:
1667
1668
  },
1668
1669
  "duration": duration,
1669
1670
  "message": f"Analysis complete: {classes_count} classes, {functions_count} functions, {methods_count} methods",
1670
- "timestamp": datetime.now().isoformat(),
1671
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1671
1672
  },
1672
1673
  )
1673
1674
 
@@ -1725,7 +1726,7 @@ class CodeTreeAnalyzer:
1725
1726
  },
1726
1727
  }
1727
1728
 
1728
- def _is_internal_node(self, node: CodeNode) -> bool:
1729
+ def _is_internal_node(self, node: CodeNode) -> bool: # noqa: PLR0911
1729
1730
  """Check if node is an internal function that should be filtered."""
1730
1731
  # Don't filter classes - always show them
1731
1732
  if node.node_type == "class":
@@ -17,7 +17,7 @@ import fnmatch
17
17
  import hashlib
18
18
  import json
19
19
  from dataclasses import dataclass, field
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, Set
23
23
 
@@ -505,7 +505,7 @@ class CodeTreeBuilder:
505
505
  "files_ignored": self.stats["files_ignored"],
506
506
  "total_size": self.stats["total_size"],
507
507
  "languages": list(self.stats["languages"]),
508
- "generated_at": datetime.utcnow().isoformat(),
508
+ "generated_at": datetime.now(timezone.utc).isoformat(),
509
509
  }
510
510
 
511
511
  with open(output_path, "w") as f:
@@ -19,7 +19,7 @@ import threading
19
19
  import time
20
20
  from collections import deque
21
21
  from dataclasses import asdict, dataclass
22
- from datetime import datetime
22
+ from datetime import datetime, timezone
23
23
  from typing import Any, Dict, List, Optional
24
24
 
25
25
  try:
@@ -54,7 +54,7 @@ class CodeNodeEvent:
54
54
  def to_dict(self) -> Dict[str, Any]:
55
55
  """Convert to dictionary for JSON serialization."""
56
56
  data = asdict(self)
57
- data["timestamp"] = datetime.utcnow().isoformat()
57
+ data["timestamp"] = datetime.now(timezone.utc).isoformat()
58
58
  return data
59
59
 
60
60
 
@@ -157,7 +157,7 @@ class CodeTreeEventEmitter:
157
157
 
158
158
  def start(self):
159
159
  """Start the event emitter and background tasks."""
160
- self.stats["start_time"] = datetime.utcnow()
160
+ self.stats["start_time"] = datetime.now(timezone.utc)
161
161
  self._stop_event.clear()
162
162
 
163
163
  # Start background emit task
@@ -168,7 +168,7 @@ class CodeTreeEventEmitter:
168
168
  self.emit(
169
169
  self.EVENT_ANALYSIS_START,
170
170
  {
171
- "timestamp": datetime.utcnow().isoformat(),
171
+ "timestamp": datetime.now(timezone.utc).isoformat(),
172
172
  "batch_size": self.batch_size,
173
173
  "batch_timeout": self.batch_timeout,
174
174
  },
@@ -183,9 +183,11 @@ class CodeTreeEventEmitter:
183
183
  self.emit(
184
184
  self.EVENT_ANALYSIS_COMPLETE,
185
185
  {
186
- "timestamp": datetime.utcnow().isoformat(),
186
+ "timestamp": datetime.now(timezone.utc).isoformat(),
187
187
  "duration": (
188
- (datetime.utcnow() - self.stats["start_time"]).total_seconds()
188
+ (
189
+ datetime.now(timezone.utc) - self.stats["start_time"]
190
+ ).total_seconds()
189
191
  if self.stats["start_time"]
190
192
  else 0
191
193
  ),
@@ -213,7 +215,7 @@ class CodeTreeEventEmitter:
213
215
  event = {
214
216
  "type": event_type,
215
217
  "data": data,
216
- "timestamp": datetime.utcnow().isoformat(),
218
+ "timestamp": datetime.now(timezone.utc).isoformat(),
217
219
  }
218
220
 
219
221
  if batch:
@@ -393,7 +395,7 @@ class CodeTreeEventEmitter:
393
395
  "events": list(self.event_buffer),
394
396
  "count": len(self.event_buffer),
395
397
  },
396
- "timestamp": datetime.utcnow().isoformat(),
398
+ "timestamp": datetime.now(timezone.utc).isoformat(),
397
399
  }
398
400
 
399
401
  self._emit_event(batch_event)
@@ -13,7 +13,7 @@ import signal
13
13
  import sys
14
14
  import time
15
15
  from dataclasses import dataclass, field
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from enum import Enum
18
18
  from pathlib import Path
19
19
  from typing import Any, Dict, List, Optional, Set
@@ -232,7 +232,7 @@ class SocketIODebugger:
232
232
 
233
233
  def _display_event(self, data: Dict[str, Any]):
234
234
  """Display an event based on current mode."""
235
- timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
235
+ timestamp = datetime.now(timezone.utc).strftime("%H:%M:%S.%f")[:-3]
236
236
  event_type = data.get("type", "unknown")
237
237
  event_data = data.get("data", {})
238
238
 
@@ -449,7 +449,7 @@ class SocketIODebugger:
449
449
  if self.quiet and level not in ["error", "critical"]:
450
450
  return
451
451
 
452
- timestamp = datetime.now().strftime("%H:%M:%S")
452
+ timestamp = datetime.now(timezone.utc).strftime("%H:%M:%S")
453
453
 
454
454
  if RICH_AVAILABLE:
455
455
  styles = {
@@ -107,7 +107,7 @@ class AgentDependencyLoader:
107
107
  logger.info(f"Loaded dependencies for {len(agent_dependencies)} agents")
108
108
  return agent_dependencies
109
109
 
110
- def check_python_dependency(self, package_spec: str) -> Tuple[bool, Optional[str]]:
110
+ def check_python_dependency(self, package_spec: str) -> Tuple[bool, Optional[str]]: # noqa: PLR0911
111
111
  """
112
112
  Check if a Python package dependency is satisfied.
113
113
 
@@ -442,7 +442,7 @@ class AgentDependencyLoader:
442
442
 
443
443
  return compatible, incompatible
444
444
 
445
- def install_missing_dependencies(self, dependencies: List[str]) -> Tuple[bool, str]:
445
+ def install_missing_dependencies(self, dependencies: List[str]) -> Tuple[bool, str]: # noqa: PLR0911
446
446
  """
447
447
  Install missing Python dependencies using robust retry logic.
448
448
 
@@ -10,7 +10,7 @@ based on the execution context and user preferences.
10
10
  import json
11
11
  import os
12
12
  import sys
13
- from datetime import datetime, timedelta
13
+ from datetime import datetime, timedelta, timezone
14
14
  from enum import Enum
15
15
  from typing import Any, Dict, Optional, Tuple
16
16
 
@@ -160,7 +160,9 @@ class DependencyStrategy:
160
160
  last_check = datetime.fromisoformat(cache.get("timestamp", ""))
161
161
 
162
162
  # Check if cache is still valid
163
- if datetime.now() - last_check < timedelta(seconds=cache_ttl):
163
+ if datetime.now(timezone.utc) - last_check < timedelta(
164
+ seconds=cache_ttl
165
+ ):
164
166
  logger.debug(f"Using cached dependency check from {last_check}")
165
167
  return False
166
168
 
@@ -179,7 +181,10 @@ class DependencyStrategy:
179
181
  try:
180
182
  self.cache_path.parent.mkdir(parents=True, exist_ok=True)
181
183
 
182
- cache_data = {"timestamp": datetime.now().isoformat(), "results": results}
184
+ cache_data = {
185
+ "timestamp": datetime.now(timezone.utc).isoformat(),
186
+ "results": results,
187
+ }
183
188
 
184
189
  with open(self.cache_path, "w") as f:
185
190
  json.dump(cache_data, f, indent=2)
@@ -189,7 +189,7 @@ class EnvironmentContext:
189
189
  get_ipython = globals().get("get_ipython")
190
190
  if get_ipython is not None:
191
191
  return True
192
- except:
192
+ except Exception:
193
193
  pass
194
194
 
195
195
  # Check for Jupyter-specific environment variables
@@ -208,7 +208,7 @@ class EnvironmentContext:
208
208
  @classmethod
209
209
  def should_prompt_for_dependencies(
210
210
  cls, force_prompt: bool = False, force_skip: bool = False
211
- ) -> Tuple[bool, str]:
211
+ ) -> Tuple[bool, str]: # noqa: PLR0911
212
212
  """
213
213
  Determine if we should prompt for dependency installation.
214
214
 
@@ -6,7 +6,7 @@ Inspired by awesome-claude-code's comprehensive error handling approach.
6
6
 
7
7
  import logging
8
8
  import sys
9
- from datetime import datetime
9
+ from datetime import datetime, timezone
10
10
  from functools import wraps
11
11
  from typing import Any, Callable, Dict, List, Optional, Type
12
12
 
@@ -26,7 +26,7 @@ class MPMError(Exception):
26
26
  super().__init__(message)
27
27
  self.details = details or {}
28
28
  self.suggestions = suggestions or []
29
- self.timestamp = datetime.now()
29
+ self.timestamp = datetime.now(timezone.utc)
30
30
 
31
31
  def get_user_friendly_message(self) -> str:
32
32
  """Get a user-friendly error message."""
@@ -150,7 +150,7 @@ def atomic_write(
150
150
  try:
151
151
  if "temp_path" in locals():
152
152
  os.unlink(temp_path)
153
- except:
153
+ except Exception:
154
154
  pass
155
155
  raise FileOperationError(f"Failed to atomically write file {path}: {e}")
156
156
 
@@ -15,7 +15,7 @@ def safe_import(
15
15
  fallback_name: Optional[str] = None,
16
16
  from_list: Optional[List[str]] = None,
17
17
  logger: Optional[logging.Logger] = None,
18
- ) -> Optional[Any]:
18
+ ) -> Optional[Any]: # noqa: PLR0911
19
19
  """
20
20
  Safely import a module with fallback support.
21
21
 
@@ -107,7 +107,9 @@ class LogCleanupUtility:
107
107
 
108
108
  try:
109
109
  # Check directory modification time
110
- mtime = datetime.fromtimestamp(session_dir.stat().st_mtime, tz=timezone.utc)
110
+ mtime = datetime.fromtimestamp(
111
+ session_dir.stat().st_mtime, tz=timezone.utc
112
+ )
111
113
 
112
114
  if mtime < cutoff_time:
113
115
  # Calculate directory size
@@ -169,7 +171,9 @@ class LogCleanupUtility:
169
171
  for ext in LogCleanupConfig.ARCHIVE_EXTENSIONS:
170
172
  for archive_file in self.base_log_dir.rglob(f"*{ext}"):
171
173
  try:
172
- mtime = datetime.fromtimestamp(archive_file.stat().st_mtime, tz=timezone.utc)
174
+ mtime = datetime.fromtimestamp(
175
+ archive_file.stat().st_mtime, tz=timezone.utc
176
+ )
173
177
 
174
178
  if mtime < cutoff_time:
175
179
  file_size = archive_file.stat().st_size / (1024 * 1024) # MB
@@ -238,7 +242,9 @@ class LogCleanupUtility:
238
242
 
239
243
  for log_file in log_dir.glob(pattern):
240
244
  try:
241
- mtime = datetime.fromtimestamp(log_file.stat().st_mtime, tz=timezone.utc)
245
+ mtime = datetime.fromtimestamp(
246
+ log_file.stat().st_mtime, tz=timezone.utc
247
+ )
242
248
 
243
249
  if mtime < cutoff_time:
244
250
  file_size = log_file.stat().st_size / (1024 * 1024) # MB
@@ -283,7 +289,7 @@ class LogCleanupUtility:
283
289
  removed_count = 0
284
290
 
285
291
  # Walk bottom-up to remove empty parent directories
286
- for root, dirs, files in os.walk(self.base_log_dir, topdown=False):
292
+ for root, _dirs, _files in os.walk(self.base_log_dir, topdown=False):
287
293
  root_path = Path(root)
288
294
 
289
295
  # Skip the base log directory itself
@@ -331,7 +337,9 @@ class LogCleanupUtility:
331
337
  continue
332
338
 
333
339
  try:
334
- mtime = datetime.fromtimestamp(log_file.stat().st_mtime, tz=timezone.utc)
340
+ mtime = datetime.fromtimestamp(
341
+ log_file.stat().st_mtime, tz=timezone.utc
342
+ )
335
343
 
336
344
  if mtime < cutoff_time:
337
345
  original_size = log_file.stat().st_size / (1024 * 1024) # MB
@@ -408,7 +416,10 @@ class LogCleanupUtility:
408
416
  stats["oldest_session"] = {
409
417
  "name": oldest.name,
410
418
  "age_days": (
411
- datetime.now(timezone.utc) - datetime.fromtimestamp(oldest.stat().st_mtime, tz=timezone.utc)
419
+ datetime.now(timezone.utc)
420
+ - datetime.fromtimestamp(
421
+ oldest.stat().st_mtime, tz=timezone.utc
422
+ )
412
423
  ).days,
413
424
  }
414
425
 
@@ -429,7 +440,10 @@ class LogCleanupUtility:
429
440
  "name": oldest_log.name,
430
441
  "path": str(oldest_log.relative_to(self.base_log_dir)),
431
442
  "age_days": (
432
- datetime.now(timezone.utc) - datetime.fromtimestamp(oldest_log.stat().st_mtime, tz=timezone.utc)
443
+ datetime.now(timezone.utc)
444
+ - datetime.fromtimestamp(
445
+ oldest_log.stat().st_mtime, tz=timezone.utc
446
+ )
433
447
  ).days,
434
448
  }
435
449
 
@@ -22,7 +22,7 @@ Security Considerations:
22
22
  import json
23
23
  import logging
24
24
  from dataclasses import dataclass, field
25
- from datetime import datetime
25
+ from datetime import datetime, timezone
26
26
  from pathlib import Path
27
27
  from typing import Any, Dict, List, Optional, Tuple
28
28
 
@@ -182,7 +182,7 @@ class AgentValidator:
182
182
 
183
183
  # Add metadata
184
184
  result.metadata = {
185
- "validated_at": datetime.utcnow().isoformat(),
185
+ "validated_at": datetime.now(timezone.utc).isoformat(),
186
186
  "schema_version": self.schema.get("version", "1.1.0"),
187
187
  "agent_id": agent_data.get("id", "unknown"),
188
188
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.3.12
3
+ Version: 4.3.13
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team