claude-mpm 4.1.0__py3-none-any.whl → 4.1.2__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 (358) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__main__.py +1 -1
  4. claude_mpm/agents/BASE_PM.md +74 -46
  5. claude_mpm/agents/INSTRUCTIONS.md +11 -153
  6. claude_mpm/agents/WORKFLOW.md +61 -321
  7. claude_mpm/agents/__init__.py +11 -11
  8. claude_mpm/agents/agent_loader.py +23 -20
  9. claude_mpm/agents/agent_loader_integration.py +1 -1
  10. claude_mpm/agents/agents_metadata.py +27 -0
  11. claude_mpm/agents/async_agent_loader.py +5 -8
  12. claude_mpm/agents/base_agent_loader.py +36 -25
  13. claude_mpm/agents/frontmatter_validator.py +6 -6
  14. claude_mpm/agents/schema/agent_schema.json +1 -1
  15. claude_mpm/agents/system_agent_config.py +9 -9
  16. claude_mpm/agents/templates/api_qa.json +47 -2
  17. claude_mpm/agents/templates/imagemagick.json +256 -0
  18. claude_mpm/agents/templates/qa.json +41 -2
  19. claude_mpm/agents/templates/ticketing.json +5 -5
  20. claude_mpm/agents/templates/web_qa.json +133 -58
  21. claude_mpm/agents/templates/web_ui.json +3 -3
  22. claude_mpm/cli/__init__.py +51 -46
  23. claude_mpm/cli/__main__.py +1 -1
  24. claude_mpm/cli/commands/__init__.py +10 -12
  25. claude_mpm/cli/commands/agent_manager.py +186 -181
  26. claude_mpm/cli/commands/agents.py +271 -268
  27. claude_mpm/cli/commands/aggregate.py +30 -29
  28. claude_mpm/cli/commands/cleanup.py +50 -44
  29. claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
  30. claude_mpm/cli/commands/config.py +162 -127
  31. claude_mpm/cli/commands/doctor.py +52 -62
  32. claude_mpm/cli/commands/info.py +37 -25
  33. claude_mpm/cli/commands/mcp.py +3 -7
  34. claude_mpm/cli/commands/mcp_command_router.py +14 -18
  35. claude_mpm/cli/commands/mcp_install_commands.py +28 -23
  36. claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
  37. claude_mpm/cli/commands/mcp_server_commands.py +23 -17
  38. claude_mpm/cli/commands/memory.py +192 -141
  39. claude_mpm/cli/commands/monitor.py +117 -88
  40. claude_mpm/cli/commands/run.py +120 -84
  41. claude_mpm/cli/commands/run_config_checker.py +4 -5
  42. claude_mpm/cli/commands/socketio_monitor.py +17 -19
  43. claude_mpm/cli/commands/tickets.py +92 -92
  44. claude_mpm/cli/parser.py +1 -5
  45. claude_mpm/cli/parsers/__init__.py +1 -1
  46. claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
  47. claude_mpm/cli/parsers/agents_parser.py +2 -3
  48. claude_mpm/cli/parsers/base_parser.py +7 -5
  49. claude_mpm/cli/parsers/mcp_parser.py +4 -2
  50. claude_mpm/cli/parsers/monitor_parser.py +26 -18
  51. claude_mpm/cli/shared/__init__.py +10 -10
  52. claude_mpm/cli/shared/argument_patterns.py +57 -71
  53. claude_mpm/cli/shared/base_command.py +61 -53
  54. claude_mpm/cli/shared/error_handling.py +62 -58
  55. claude_mpm/cli/shared/output_formatters.py +78 -77
  56. claude_mpm/cli/startup_logging.py +204 -172
  57. claude_mpm/cli/utils.py +10 -11
  58. claude_mpm/cli_module/__init__.py +1 -1
  59. claude_mpm/cli_module/args.py +1 -1
  60. claude_mpm/cli_module/migration_example.py +5 -5
  61. claude_mpm/config/__init__.py +9 -9
  62. claude_mpm/config/agent_config.py +15 -14
  63. claude_mpm/config/experimental_features.py +4 -4
  64. claude_mpm/config/paths.py +0 -1
  65. claude_mpm/config/socketio_config.py +5 -6
  66. claude_mpm/constants.py +1 -2
  67. claude_mpm/core/__init__.py +8 -8
  68. claude_mpm/core/agent_name_normalizer.py +1 -1
  69. claude_mpm/core/agent_registry.py +20 -23
  70. claude_mpm/core/agent_session_manager.py +3 -3
  71. claude_mpm/core/base_service.py +7 -15
  72. claude_mpm/core/cache.py +4 -6
  73. claude_mpm/core/claude_runner.py +85 -113
  74. claude_mpm/core/config.py +43 -28
  75. claude_mpm/core/config_aliases.py +0 -9
  76. claude_mpm/core/config_constants.py +52 -30
  77. claude_mpm/core/constants.py +0 -1
  78. claude_mpm/core/container.py +18 -27
  79. claude_mpm/core/exceptions.py +2 -2
  80. claude_mpm/core/factories.py +10 -12
  81. claude_mpm/core/framework_loader.py +581 -280
  82. claude_mpm/core/hook_manager.py +26 -22
  83. claude_mpm/core/hook_performance_config.py +58 -47
  84. claude_mpm/core/injectable_service.py +1 -1
  85. claude_mpm/core/interactive_session.py +61 -152
  86. claude_mpm/core/interfaces.py +1 -100
  87. claude_mpm/core/lazy.py +5 -5
  88. claude_mpm/core/log_manager.py +587 -0
  89. claude_mpm/core/logger.py +125 -8
  90. claude_mpm/core/logging_config.py +15 -15
  91. claude_mpm/core/minimal_framework_loader.py +5 -8
  92. claude_mpm/core/oneshot_session.py +15 -33
  93. claude_mpm/core/optimized_agent_loader.py +4 -6
  94. claude_mpm/core/optimized_startup.py +2 -1
  95. claude_mpm/core/output_style_manager.py +147 -106
  96. claude_mpm/core/pm_hook_interceptor.py +0 -1
  97. claude_mpm/core/service_registry.py +11 -8
  98. claude_mpm/core/session_manager.py +1 -2
  99. claude_mpm/core/shared/__init__.py +1 -1
  100. claude_mpm/core/shared/config_loader.py +101 -97
  101. claude_mpm/core/shared/path_resolver.py +72 -68
  102. claude_mpm/core/shared/singleton_manager.py +56 -50
  103. claude_mpm/core/socketio_pool.py +26 -6
  104. claude_mpm/core/tool_access_control.py +4 -5
  105. claude_mpm/core/typing_utils.py +50 -59
  106. claude_mpm/core/unified_agent_registry.py +14 -19
  107. claude_mpm/core/unified_config.py +4 -6
  108. claude_mpm/core/unified_paths.py +197 -109
  109. claude_mpm/dashboard/open_dashboard.py +2 -4
  110. claude_mpm/experimental/cli_enhancements.py +51 -36
  111. claude_mpm/generators/agent_profile_generator.py +2 -4
  112. claude_mpm/hooks/base_hook.py +1 -2
  113. claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
  114. claude_mpm/hooks/claude_hooks/event_handlers.py +93 -38
  115. claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
  116. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
  117. claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
  118. claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
  119. claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
  120. claude_mpm/hooks/memory_integration_hook.py +5 -5
  121. claude_mpm/hooks/tool_call_interceptor.py +1 -1
  122. claude_mpm/hooks/validation_hooks.py +4 -4
  123. claude_mpm/init.py +4 -9
  124. claude_mpm/models/__init__.py +2 -2
  125. claude_mpm/models/agent_session.py +11 -14
  126. claude_mpm/scripts/mcp_server.py +20 -11
  127. claude_mpm/scripts/mcp_wrapper.py +5 -5
  128. claude_mpm/scripts/mpm_doctor.py +321 -0
  129. claude_mpm/scripts/socketio_daemon.py +28 -25
  130. claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
  131. claude_mpm/scripts/socketio_server_manager.py +116 -95
  132. claude_mpm/services/__init__.py +49 -49
  133. claude_mpm/services/agent_capabilities_service.py +12 -18
  134. claude_mpm/services/agents/__init__.py +22 -22
  135. claude_mpm/services/agents/agent_builder.py +140 -119
  136. claude_mpm/services/agents/deployment/__init__.py +3 -3
  137. claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
  138. claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
  139. claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
  140. claude_mpm/services/agents/deployment/agent_deployment.py +136 -106
  141. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
  142. claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
  143. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
  144. claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
  145. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
  146. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
  147. claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
  148. claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
  149. claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
  150. claude_mpm/services/agents/deployment/agent_validator.py +0 -1
  151. claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
  152. claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
  153. claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
  154. claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
  155. claude_mpm/services/agents/deployment/config/__init__.py +1 -1
  156. claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
  157. claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
  158. claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
  159. claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
  160. claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
  161. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
  162. claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
  163. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
  164. claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
  165. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
  166. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
  167. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
  168. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
  169. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
  170. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
  171. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
  172. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
  173. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
  174. claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
  175. claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
  176. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
  177. claude_mpm/services/agents/deployment/results/__init__.py +1 -1
  178. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
  179. claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
  180. claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
  181. claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
  182. claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
  183. claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
  184. claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
  185. claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
  186. claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
  187. claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
  188. claude_mpm/services/agents/loading/__init__.py +1 -1
  189. claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
  190. claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
  191. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
  192. claude_mpm/services/agents/management/__init__.py +1 -1
  193. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
  194. claude_mpm/services/agents/management/agent_management_service.py +5 -9
  195. claude_mpm/services/agents/memory/__init__.py +4 -4
  196. claude_mpm/services/agents/memory/agent_memory_manager.py +280 -160
  197. claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
  198. claude_mpm/services/agents/memory/content_manager.py +44 -38
  199. claude_mpm/services/agents/memory/template_generator.py +4 -6
  200. claude_mpm/services/agents/registry/__init__.py +10 -6
  201. claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
  202. claude_mpm/services/agents/registry/modification_tracker.py +3 -6
  203. claude_mpm/services/async_session_logger.py +1 -2
  204. claude_mpm/services/claude_session_logger.py +1 -2
  205. claude_mpm/services/command_deployment_service.py +173 -0
  206. claude_mpm/services/command_handler_service.py +20 -22
  207. claude_mpm/services/core/__init__.py +25 -25
  208. claude_mpm/services/core/base.py +0 -5
  209. claude_mpm/services/core/interfaces/__init__.py +32 -32
  210. claude_mpm/services/core/interfaces/agent.py +0 -21
  211. claude_mpm/services/core/interfaces/communication.py +0 -27
  212. claude_mpm/services/core/interfaces/infrastructure.py +0 -56
  213. claude_mpm/services/core/interfaces/service.py +0 -29
  214. claude_mpm/services/diagnostics/__init__.py +1 -1
  215. claude_mpm/services/diagnostics/checks/__init__.py +6 -6
  216. claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
  217. claude_mpm/services/diagnostics/checks/base_check.py +12 -16
  218. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
  219. claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
  220. claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
  221. claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
  222. claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
  223. claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
  224. claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
  225. claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
  226. claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
  227. claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
  228. claude_mpm/services/diagnostics/models.py +21 -19
  229. claude_mpm/services/event_aggregator.py +10 -17
  230. claude_mpm/services/event_bus/__init__.py +1 -1
  231. claude_mpm/services/event_bus/config.py +54 -35
  232. claude_mpm/services/event_bus/event_bus.py +76 -71
  233. claude_mpm/services/event_bus/relay.py +74 -64
  234. claude_mpm/services/events/__init__.py +11 -11
  235. claude_mpm/services/events/consumers/__init__.py +3 -3
  236. claude_mpm/services/events/consumers/dead_letter.py +71 -63
  237. claude_mpm/services/events/consumers/logging.py +39 -37
  238. claude_mpm/services/events/consumers/metrics.py +56 -57
  239. claude_mpm/services/events/consumers/socketio.py +82 -81
  240. claude_mpm/services/events/core.py +110 -99
  241. claude_mpm/services/events/interfaces.py +56 -72
  242. claude_mpm/services/events/producers/__init__.py +1 -1
  243. claude_mpm/services/events/producers/hook.py +38 -38
  244. claude_mpm/services/events/producers/system.py +46 -44
  245. claude_mpm/services/exceptions.py +81 -80
  246. claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
  247. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
  248. claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
  249. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
  250. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
  251. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
  252. claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
  253. claude_mpm/services/hook_service.py +6 -9
  254. claude_mpm/services/infrastructure/__init__.py +1 -1
  255. claude_mpm/services/infrastructure/context_preservation.py +8 -12
  256. claude_mpm/services/infrastructure/monitoring.py +21 -23
  257. claude_mpm/services/mcp_gateway/__init__.py +37 -37
  258. claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
  259. claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
  260. claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
  261. claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
  262. claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
  263. claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
  264. claude_mpm/services/mcp_gateway/core/base.py +0 -3
  265. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
  266. claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
  267. claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
  268. claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
  269. claude_mpm/services/mcp_gateway/main.py +2 -1
  270. claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
  271. claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
  272. claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
  273. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
  274. claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
  275. claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
  276. claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
  277. claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
  278. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
  279. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
  280. claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
  281. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
  282. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
  283. claude_mpm/services/memory/__init__.py +3 -3
  284. claude_mpm/services/memory/builder.py +3 -6
  285. claude_mpm/services/memory/cache/__init__.py +1 -1
  286. claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
  287. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  288. claude_mpm/services/memory/indexed_memory.py +5 -7
  289. claude_mpm/services/memory/optimizer.py +7 -10
  290. claude_mpm/services/memory/router.py +8 -9
  291. claude_mpm/services/memory_hook_service.py +48 -34
  292. claude_mpm/services/monitor_build_service.py +77 -73
  293. claude_mpm/services/port_manager.py +130 -108
  294. claude_mpm/services/project/analyzer.py +12 -10
  295. claude_mpm/services/project/registry.py +11 -11
  296. claude_mpm/services/recovery_manager.py +10 -19
  297. claude_mpm/services/response_tracker.py +0 -1
  298. claude_mpm/services/runner_configuration_service.py +19 -20
  299. claude_mpm/services/session_management_service.py +7 -11
  300. claude_mpm/services/shared/__init__.py +1 -1
  301. claude_mpm/services/shared/async_service_base.py +58 -50
  302. claude_mpm/services/shared/config_service_base.py +73 -67
  303. claude_mpm/services/shared/lifecycle_service_base.py +82 -78
  304. claude_mpm/services/shared/manager_base.py +94 -82
  305. claude_mpm/services/shared/service_factory.py +96 -98
  306. claude_mpm/services/socketio/__init__.py +3 -3
  307. claude_mpm/services/socketio/client_proxy.py +5 -5
  308. claude_mpm/services/socketio/event_normalizer.py +199 -181
  309. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  310. claude_mpm/services/socketio/handlers/base.py +5 -4
  311. claude_mpm/services/socketio/handlers/connection.py +163 -136
  312. claude_mpm/services/socketio/handlers/file.py +13 -14
  313. claude_mpm/services/socketio/handlers/git.py +12 -7
  314. claude_mpm/services/socketio/handlers/hook.py +49 -44
  315. claude_mpm/services/socketio/handlers/memory.py +0 -1
  316. claude_mpm/services/socketio/handlers/project.py +0 -1
  317. claude_mpm/services/socketio/handlers/registry.py +37 -19
  318. claude_mpm/services/socketio/migration_utils.py +98 -84
  319. claude_mpm/services/socketio/server/__init__.py +1 -1
  320. claude_mpm/services/socketio/server/broadcaster.py +81 -87
  321. claude_mpm/services/socketio/server/core.py +65 -54
  322. claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
  323. claude_mpm/services/socketio/server/main.py +64 -38
  324. claude_mpm/services/socketio_client_manager.py +10 -12
  325. claude_mpm/services/subprocess_launcher_service.py +4 -7
  326. claude_mpm/services/system_instructions_service.py +13 -14
  327. claude_mpm/services/ticket_manager.py +2 -2
  328. claude_mpm/services/utility_service.py +5 -13
  329. claude_mpm/services/version_control/__init__.py +16 -16
  330. claude_mpm/services/version_control/branch_strategy.py +5 -8
  331. claude_mpm/services/version_control/conflict_resolution.py +9 -23
  332. claude_mpm/services/version_control/git_operations.py +5 -7
  333. claude_mpm/services/version_control/semantic_versioning.py +16 -17
  334. claude_mpm/services/version_control/version_parser.py +13 -18
  335. claude_mpm/services/version_service.py +10 -11
  336. claude_mpm/storage/__init__.py +1 -1
  337. claude_mpm/storage/state_storage.py +22 -28
  338. claude_mpm/utils/__init__.py +6 -6
  339. claude_mpm/utils/agent_dependency_loader.py +47 -33
  340. claude_mpm/utils/config_manager.py +11 -14
  341. claude_mpm/utils/dependency_cache.py +1 -1
  342. claude_mpm/utils/dependency_manager.py +13 -17
  343. claude_mpm/utils/dependency_strategies.py +8 -10
  344. claude_mpm/utils/environment_context.py +3 -9
  345. claude_mpm/utils/error_handler.py +3 -13
  346. claude_mpm/utils/file_utils.py +1 -1
  347. claude_mpm/utils/path_operations.py +8 -12
  348. claude_mpm/utils/robust_installer.py +110 -33
  349. claude_mpm/utils/subprocess_utils.py +5 -6
  350. claude_mpm/validation/agent_validator.py +3 -6
  351. claude_mpm/validation/frontmatter_validator.py +1 -1
  352. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/METADATA +1 -1
  353. claude_mpm-4.1.2.dist-info/RECORD +498 -0
  354. claude_mpm-4.1.0.dist-info/RECORD +0 -494
  355. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
  356. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
  357. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
  358. {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,587 @@
1
+ """
2
+ Unified log management with async operations and time-based retention.
3
+
4
+ WHY: This module consolidates all logging functionality across the codebase,
5
+ providing a single source of truth for log management with async operations,
6
+ time-based retention, and prompt logging capabilities.
7
+
8
+ DESIGN DECISIONS:
9
+ - Time-based retention (48 hours default) instead of count-based
10
+ - Async fire-and-forget pattern for non-blocking operations
11
+ - Queue-based writing inspired by AsyncSessionLogger
12
+ - Unified cleanup function to replace duplicate implementations
13
+ - Prompt logging for system and agent prompts
14
+ - Configurable retention periods for different log types
15
+ """
16
+
17
+ import asyncio
18
+ import json
19
+ import logging
20
+ import os
21
+ from datetime import datetime, timedelta
22
+ from pathlib import Path
23
+ from queue import Full, Queue
24
+ from threading import Lock, Thread
25
+ from typing import Any, Dict, Optional
26
+
27
+ from ..core.config import Config
28
+ from ..core.constants import SystemLimits
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class LogManager:
34
+ """
35
+ Unified log management with async operations and time-based retention.
36
+
37
+ Features:
38
+ - Async fire-and-forget logging operations
39
+ - Time-based retention (48 hours default)
40
+ - Prompt logging for system and agent prompts
41
+ - Consolidated cleanup functions
42
+ - Queue-based async writing for performance
43
+ - Configurable retention periods
44
+ """
45
+
46
+ # Default retention periods (in hours)
47
+ DEFAULT_RETENTION_HOURS = 48
48
+ DEFAULT_STARTUP_RETENTION_HOURS = 48
49
+ DEFAULT_MPM_RETENTION_HOURS = 48
50
+ DEFAULT_PROMPT_RETENTION_HOURS = 168 # 7 days for prompts
51
+ DEFAULT_SESSION_RETENTION_HOURS = 168 # 7 days for sessions
52
+
53
+ def __init__(self, config: Optional[Config] = None):
54
+ """
55
+ Initialize the LogManager with configuration.
56
+
57
+ Args:
58
+ config: Configuration instance (creates new if not provided)
59
+ """
60
+ self.config = config or Config()
61
+ self._setup_logging_config()
62
+
63
+ # Queue for async operations
64
+ self.write_queue: Queue = Queue(maxsize=SystemLimits.MAX_QUEUE_SIZE)
65
+ self.cleanup_queue: Queue = Queue(maxsize=100)
66
+
67
+ # Thread management
68
+ self._write_thread: Optional[Thread] = None
69
+ self._cleanup_thread: Optional[Thread] = None
70
+ self._shutdown = False
71
+ self._lock = Lock()
72
+
73
+ # Cache for directory paths
74
+ self._dir_cache: Dict[str, Path] = {}
75
+
76
+ # Start background threads
77
+ self._start_background_threads()
78
+
79
+ def _setup_logging_config(self):
80
+ """Load and setup logging configuration from config."""
81
+ logging_config = self.config.get("logging", {})
82
+ response_config = self.config.get("response_logging", {})
83
+
84
+ # Get retention periods from config with defaults
85
+ self.retention_hours = {
86
+ "default": logging_config.get(
87
+ "retention_hours", self.DEFAULT_RETENTION_HOURS
88
+ ),
89
+ "startup": logging_config.get(
90
+ "startup_retention_hours", self.DEFAULT_STARTUP_RETENTION_HOURS
91
+ ),
92
+ "mpm": logging_config.get(
93
+ "mpm_retention_hours", self.DEFAULT_MPM_RETENTION_HOURS
94
+ ),
95
+ "prompts": logging_config.get(
96
+ "prompt_retention_hours", self.DEFAULT_PROMPT_RETENTION_HOURS
97
+ ),
98
+ "sessions": response_config.get(
99
+ "session_retention_hours", self.DEFAULT_SESSION_RETENTION_HOURS
100
+ ),
101
+ }
102
+
103
+ # Base directories
104
+ self.base_log_dir = Path(
105
+ logging_config.get("base_directory", ".claude-mpm/logs")
106
+ )
107
+ if not self.base_log_dir.is_absolute():
108
+ self.base_log_dir = Path.cwd() / self.base_log_dir
109
+
110
+ def _start_background_threads(self):
111
+ """Start background threads for async operations."""
112
+ with self._lock:
113
+ if not self._write_thread or not self._write_thread.is_alive():
114
+ self._write_thread = Thread(
115
+ target=self._process_write_queue, daemon=True
116
+ )
117
+ self._write_thread.start()
118
+
119
+ if not self._cleanup_thread or not self._cleanup_thread.is_alive():
120
+ self._cleanup_thread = Thread(
121
+ target=self._process_cleanup_queue, daemon=True
122
+ )
123
+ self._cleanup_thread.start()
124
+
125
+ def _process_write_queue(self):
126
+ """Process write operations from the queue."""
127
+ while not self._shutdown:
128
+ try:
129
+ # Get write operation with timeout
130
+ operation = self.write_queue.get(timeout=1.0)
131
+ if operation is None: # Shutdown signal
132
+ break
133
+
134
+ # Execute write operation
135
+ try:
136
+ operation()
137
+ except Exception as e:
138
+ logger.error(f"Error in write operation: {e}")
139
+ finally:
140
+ self.write_queue.task_done()
141
+
142
+ except:
143
+ continue # Timeout or other error, continue loop
144
+
145
+ def _process_cleanup_queue(self):
146
+ """Process cleanup operations from the queue."""
147
+ while not self._shutdown:
148
+ try:
149
+ # Get cleanup operation with timeout
150
+ operation = self.cleanup_queue.get(timeout=1.0)
151
+ if operation is None: # Shutdown signal
152
+ break
153
+
154
+ # Execute cleanup operation
155
+ try:
156
+ operation()
157
+ except Exception as e:
158
+ logger.error(f"Error in cleanup operation: {e}")
159
+ finally:
160
+ self.cleanup_queue.task_done()
161
+
162
+ except:
163
+ continue # Timeout or other error, continue loop
164
+
165
+ async def setup_logging(self, log_type: str) -> Path:
166
+ """
167
+ Unified log setup for all log types.
168
+
169
+ Args:
170
+ log_type: Type of logging to setup (startup, mpm, prompts, sessions)
171
+
172
+ Returns:
173
+ Path to the log directory
174
+ """
175
+ # Get or create directory for log type
176
+ log_dir = self._get_log_directory(log_type)
177
+ log_dir.mkdir(parents=True, exist_ok=True)
178
+
179
+ # Add to cache
180
+ self._dir_cache[log_type] = log_dir
181
+
182
+ # Schedule cleanup for old logs
183
+ await self.cleanup_old_logs(
184
+ log_dir,
185
+ pattern="*",
186
+ retention_hours=self.retention_hours.get(
187
+ log_type, self.retention_hours["default"]
188
+ ),
189
+ )
190
+
191
+ return log_dir
192
+
193
+ def _get_log_directory(self, log_type: str) -> Path:
194
+ """
195
+ Get the directory path for a specific log type.
196
+
197
+ Args:
198
+ log_type: Type of log (startup, mpm, prompts, sessions)
199
+
200
+ Returns:
201
+ Path to the log directory
202
+ """
203
+ if log_type in self._dir_cache:
204
+ return self._dir_cache[log_type]
205
+
206
+ # Map log types to directory names
207
+ dir_mapping = {
208
+ "startup": "startup",
209
+ "mpm": "", # Root of logs directory
210
+ "prompts": "prompts",
211
+ "sessions": "sessions",
212
+ "agents": "agents",
213
+ "system": "system",
214
+ }
215
+
216
+ subdir = dir_mapping.get(log_type, log_type)
217
+ log_dir = self.base_log_dir / subdir if subdir else self.base_log_dir
218
+
219
+ self._dir_cache[log_type] = log_dir
220
+ return log_dir
221
+
222
+ async def cleanup_old_logs(
223
+ self, directory: Path, pattern: str = "*", retention_hours: int = 48
224
+ ) -> int:
225
+ """
226
+ Consolidated cleanup with time-based retention.
227
+
228
+ Removes log files older than the retention period.
229
+
230
+ Args:
231
+ directory: Directory to clean up
232
+ pattern: File pattern to match (default: all files)
233
+ retention_hours: Hours to retain logs (default: 48)
234
+
235
+ Returns:
236
+ Number of files deleted
237
+ """
238
+ if not directory.exists():
239
+ return 0
240
+
241
+ # Calculate cutoff time
242
+ cutoff_time = datetime.now() - timedelta(hours=retention_hours)
243
+
244
+ # Schedule async cleanup
245
+ deleted_count = await self._async_cleanup(directory, pattern, cutoff_time)
246
+
247
+ if deleted_count > 0:
248
+ logger.info(f"Cleaned up {deleted_count} old log files from {directory}")
249
+
250
+ return deleted_count
251
+
252
+ async def _async_cleanup(
253
+ self, directory: Path, pattern: str, cutoff_time: datetime
254
+ ) -> int:
255
+ """
256
+ Perform async cleanup of old files.
257
+
258
+ Args:
259
+ directory: Directory to clean
260
+ pattern: File pattern to match
261
+ cutoff_time: Delete files older than this time
262
+
263
+ Returns:
264
+ Number of files deleted
265
+ """
266
+ deleted_count = 0
267
+
268
+ def cleanup_task():
269
+ nonlocal deleted_count
270
+ try:
271
+ # Find matching files
272
+ if pattern == "*":
273
+ files = list(directory.iterdir())
274
+ else:
275
+ files = list(directory.glob(pattern))
276
+
277
+ for file_path in files:
278
+ if not file_path.is_file():
279
+ continue
280
+
281
+ try:
282
+ # Check file modification time
283
+ mtime = datetime.fromtimestamp(file_path.stat().st_mtime)
284
+ if mtime < cutoff_time:
285
+ file_path.unlink()
286
+ deleted_count += 1
287
+ except Exception as e:
288
+ logger.debug(f"Could not delete {file_path}: {e}")
289
+ except Exception as e:
290
+ logger.error(f"Error during cleanup: {e}")
291
+
292
+ # Run cleanup in thread pool
293
+ loop = asyncio.get_event_loop()
294
+ await loop.run_in_executor(None, cleanup_task)
295
+
296
+ return deleted_count
297
+
298
+ def _sync_cleanup_old_logs(
299
+ self, directory: Path, pattern: str = "*", retention_hours: int = 48
300
+ ) -> int:
301
+ """
302
+ Synchronous version of cleanup for backward compatibility.
303
+
304
+ Args:
305
+ directory: Directory to clean up
306
+ pattern: File pattern to match
307
+ retention_hours: Hours to retain logs
308
+
309
+ Returns:
310
+ Number of files deleted
311
+ """
312
+ if not directory.exists():
313
+ return 0
314
+
315
+ # Calculate cutoff time
316
+ cutoff_time = datetime.now() - timedelta(hours=retention_hours)
317
+ deleted_count = 0
318
+
319
+ try:
320
+ # Find matching files
321
+ if pattern == "*":
322
+ files = list(directory.iterdir())
323
+ else:
324
+ files = list(directory.glob(pattern))
325
+
326
+ for file_path in files:
327
+ if not file_path.is_file():
328
+ continue
329
+
330
+ try:
331
+ # Check file modification time
332
+ mtime = datetime.fromtimestamp(file_path.stat().st_mtime)
333
+ if mtime < cutoff_time:
334
+ file_path.unlink()
335
+ deleted_count += 1
336
+ except Exception as e:
337
+ logger.debug(f"Could not delete {file_path}: {e}")
338
+ except Exception as e:
339
+ logger.error(f"Error during sync cleanup: {e}")
340
+
341
+ if deleted_count > 0:
342
+ logger.info(f"Cleaned up {deleted_count} old log files from {directory}")
343
+
344
+ return deleted_count
345
+
346
+ async def log_prompt(
347
+ self, prompt_type: str, content: str, metadata: Optional[Dict[str, Any]] = None
348
+ ) -> Optional[Path]:
349
+ """
350
+ Save prompts to prompts directory.
351
+
352
+ Args:
353
+ prompt_type: Type of prompt (system, agent, custom)
354
+ content: The prompt content
355
+ metadata: Additional metadata to save with prompt
356
+
357
+ Returns:
358
+ Path to the saved prompt file, or None if failed
359
+ """
360
+ try:
361
+ # Setup prompts directory
362
+ prompts_dir = await self.setup_logging("prompts")
363
+
364
+ # Generate filename with timestamp
365
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[
366
+ :-3
367
+ ] # Microseconds to milliseconds
368
+
369
+ # Sanitize prompt type for filename
370
+ if prompt_type is None:
371
+ prompt_type = "unknown"
372
+ safe_type = str(prompt_type).replace(" ", "_").replace("/", "_")
373
+
374
+ # Handle None content
375
+ if content is None:
376
+ content = ""
377
+
378
+ # Determine file extension based on content
379
+ if content and (
380
+ content.strip().startswith("{") or content.strip().startswith("[")
381
+ ):
382
+ extension = ".json"
383
+ else:
384
+ extension = ".md"
385
+
386
+ filename = f"{safe_type}_{timestamp}{extension}"
387
+ file_path = prompts_dir / filename
388
+
389
+ # Prepare prompt data
390
+ prompt_data = {
391
+ "timestamp": datetime.now().isoformat(),
392
+ "type": prompt_type,
393
+ "content": content,
394
+ "metadata": metadata or {},
395
+ }
396
+
397
+ # Add session ID if available
398
+ if "session_id" in os.environ:
399
+ prompt_data["session_id"] = os.environ.get("session_id")
400
+
401
+ # Queue async write
402
+ await self._queue_write(file_path, prompt_data, extension)
403
+
404
+ logger.debug(f"Queued prompt logging to {file_path}")
405
+ return file_path
406
+
407
+ except Exception as e:
408
+ logger.error(f"Failed to log prompt: {e}")
409
+ return None
410
+
411
+ async def _queue_write(self, file_path: Path, data: Any, extension: str):
412
+ """
413
+ Queue a write operation for async processing.
414
+
415
+ Args:
416
+ file_path: Path to write to
417
+ data: Data to write
418
+ extension: File extension to determine format
419
+ """
420
+
421
+ def write_task():
422
+ try:
423
+ file_path.parent.mkdir(parents=True, exist_ok=True)
424
+
425
+ if extension == ".json":
426
+ # JSON files also get structured metadata for consistency
427
+ with open(file_path, "w", encoding="utf-8") as f:
428
+ json.dump(data, f, indent=2, ensure_ascii=False)
429
+ # For markdown or text files
430
+ elif isinstance(data, dict):
431
+ # Write as formatted markdown with metadata
432
+ with open(file_path, "w", encoding="utf-8") as f:
433
+ f.write("---\n")
434
+ f.write(f"timestamp: {data.get('timestamp', 'unknown')}\n")
435
+ f.write(f"type: {data.get('type', 'unknown')}\n")
436
+ if data.get("session_id"):
437
+ f.write(f"session_id: {data['session_id']}\n")
438
+ if data.get("metadata"):
439
+ f.write(f"metadata: {json.dumps(data['metadata'])}\n")
440
+ f.write("---\n\n")
441
+ f.write(data.get("content", ""))
442
+ else:
443
+ # Write content directly
444
+ with open(file_path, "w", encoding="utf-8") as f:
445
+ f.write(str(data))
446
+ except Exception as e:
447
+ logger.error(f"Failed to write {file_path}: {e}")
448
+
449
+ try:
450
+ self.write_queue.put_nowait(write_task)
451
+ except Full:
452
+ # Queue is full, execute synchronously as fallback
453
+ logger.warning("Write queue full, executing synchronously")
454
+ write_task()
455
+
456
+ async def write_log_async(self, message: str, level: str = "INFO"):
457
+ """
458
+ Async fire-and-forget logging.
459
+
460
+ Args:
461
+ message: Log message to write
462
+ level: Log level (INFO, WARNING, ERROR, DEBUG)
463
+ """
464
+ timestamp = datetime.now().isoformat()
465
+ log_entry = f"[{timestamp}] [{level}] {message}\n"
466
+
467
+ # Get appropriate log file based on context
468
+ log_dir = self._get_log_directory("mpm")
469
+ log_file = log_dir / f"mpm_{datetime.now().strftime('%Y%m%d')}.log"
470
+
471
+ def write_task():
472
+ try:
473
+ with open(log_file, "a", encoding="utf-8") as f:
474
+ f.write(log_entry)
475
+ except Exception as e:
476
+ logger.error(f"Failed to write log: {e}")
477
+
478
+ try:
479
+ self.write_queue.put_nowait(write_task)
480
+ except Full:
481
+ # Queue full, log to standard logger as fallback
482
+ getattr(logger, level.lower(), logger.info)(message)
483
+
484
+ def cleanup_old_startup_logs(
485
+ self, project_root: Optional[Path] = None, keep_hours: Optional[int] = None
486
+ ) -> int:
487
+ """
488
+ Replacement for the old cleanup_old_startup_logs function.
489
+
490
+ Now uses time-based retention instead of count-based.
491
+
492
+ Args:
493
+ project_root: Root directory for the project
494
+ keep_hours: Hours to keep logs (default from config)
495
+
496
+ Returns:
497
+ Number of log files deleted
498
+ """
499
+ if keep_hours is None:
500
+ keep_hours = self.retention_hours.get(
501
+ "startup", self.DEFAULT_STARTUP_RETENTION_HOURS
502
+ )
503
+
504
+ if project_root is None:
505
+ project_root = Path.cwd()
506
+
507
+ log_dir = project_root / ".claude-mpm" / "logs" / "startup"
508
+
509
+ # Use synchronous cleanup for compatibility
510
+ return self._sync_cleanup_old_logs(log_dir, "startup-*.log", keep_hours)
511
+
512
+ def cleanup_old_mpm_logs(
513
+ self, log_dir: Optional[Path] = None, keep_hours: Optional[int] = None
514
+ ) -> int:
515
+ """
516
+ Replacement for the old cleanup_old_mpm_logs function.
517
+
518
+ Now uses time-based retention instead of count-based.
519
+
520
+ Args:
521
+ log_dir: Directory containing log files
522
+ keep_hours: Hours to keep logs (default from config)
523
+
524
+ Returns:
525
+ Number of log files deleted
526
+ """
527
+ if keep_hours is None:
528
+ keep_hours = self.retention_hours.get(
529
+ "mpm", self.DEFAULT_MPM_RETENTION_HOURS
530
+ )
531
+
532
+ if log_dir is None:
533
+ from claude_mpm.core.unified_paths import get_project_root
534
+
535
+ deployment_root = get_project_root()
536
+ log_dir = deployment_root / ".claude-mpm" / "logs"
537
+
538
+ # Use synchronous cleanup for compatibility
539
+ return self._sync_cleanup_old_logs(log_dir, "mpm_*.log", keep_hours)
540
+
541
+ def shutdown(self):
542
+ """Gracefully shutdown the LogManager."""
543
+ self._shutdown = True
544
+
545
+ # Signal threads to stop
546
+ try:
547
+ self.write_queue.put_nowait(None)
548
+ self.cleanup_queue.put_nowait(None)
549
+ except:
550
+ pass
551
+
552
+ # Wait for threads to finish
553
+ if self._write_thread and self._write_thread.is_alive():
554
+ self._write_thread.join(timeout=2.0)
555
+
556
+ if self._cleanup_thread and self._cleanup_thread.is_alive():
557
+ self._cleanup_thread.join(timeout=2.0)
558
+
559
+ def __enter__(self):
560
+ """Context manager entry."""
561
+ return self
562
+
563
+ def __exit__(self, exc_type, exc_val, exc_tb):
564
+ """Context manager exit."""
565
+ self.shutdown()
566
+
567
+
568
+ # Global singleton instance
569
+ _log_manager_instance: Optional[LogManager] = None
570
+ _log_manager_lock = Lock()
571
+
572
+
573
+ def get_log_manager() -> LogManager:
574
+ """
575
+ Get or create the global LogManager instance.
576
+
577
+ Returns:
578
+ The global LogManager instance
579
+ """
580
+ global _log_manager_instance
581
+
582
+ if _log_manager_instance is None:
583
+ with _log_manager_lock:
584
+ if _log_manager_instance is None:
585
+ _log_manager_instance = LogManager()
586
+
587
+ return _log_manager_instance