claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__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 (389) 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/engineer.json +33 -11
  18. claude_mpm/agents/templates/imagemagick.json +256 -0
  19. claude_mpm/agents/templates/qa.json +41 -2
  20. claude_mpm/agents/templates/ticketing.json +5 -5
  21. claude_mpm/agents/templates/web_qa.json +50 -2
  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 +648 -1098
  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 +339 -967
  39. claude_mpm/cli/commands/monitor.py +117 -88
  40. claude_mpm/cli/commands/run.py +233 -542
  41. claude_mpm/cli/commands/socketio_monitor.py +17 -19
  42. claude_mpm/cli/commands/tickets.py +92 -92
  43. claude_mpm/cli/parser.py +1 -5
  44. claude_mpm/cli/parsers/__init__.py +1 -1
  45. claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
  46. claude_mpm/cli/parsers/agents_parser.py +2 -3
  47. claude_mpm/cli/parsers/base_parser.py +7 -5
  48. claude_mpm/cli/parsers/mcp_parser.py +4 -2
  49. claude_mpm/cli/parsers/monitor_parser.py +26 -18
  50. claude_mpm/cli/shared/__init__.py +10 -10
  51. claude_mpm/cli/shared/argument_patterns.py +57 -71
  52. claude_mpm/cli/shared/base_command.py +61 -53
  53. claude_mpm/cli/shared/error_handling.py +62 -58
  54. claude_mpm/cli/shared/output_formatters.py +78 -77
  55. claude_mpm/cli/startup_logging.py +280 -172
  56. claude_mpm/cli/utils.py +10 -11
  57. claude_mpm/cli_module/__init__.py +1 -1
  58. claude_mpm/cli_module/args.py +1 -1
  59. claude_mpm/cli_module/migration_example.py +5 -5
  60. claude_mpm/config/__init__.py +9 -9
  61. claude_mpm/config/agent_config.py +15 -14
  62. claude_mpm/config/experimental_features.py +4 -4
  63. claude_mpm/config/paths.py +0 -1
  64. claude_mpm/config/socketio_config.py +5 -6
  65. claude_mpm/constants.py +1 -2
  66. claude_mpm/core/__init__.py +8 -8
  67. claude_mpm/core/agent_name_normalizer.py +1 -1
  68. claude_mpm/core/agent_registry.py +22 -29
  69. claude_mpm/core/agent_session_manager.py +3 -3
  70. claude_mpm/core/base_service.py +7 -15
  71. claude_mpm/core/cache.py +4 -6
  72. claude_mpm/core/claude_runner.py +85 -113
  73. claude_mpm/core/config.py +43 -28
  74. claude_mpm/core/config_aliases.py +0 -9
  75. claude_mpm/core/config_constants.py +52 -30
  76. claude_mpm/core/constants.py +0 -1
  77. claude_mpm/core/container.py +18 -27
  78. claude_mpm/core/exceptions.py +2 -2
  79. claude_mpm/core/factories.py +10 -12
  80. claude_mpm/core/framework_loader.py +500 -680
  81. claude_mpm/core/hook_manager.py +26 -22
  82. claude_mpm/core/hook_performance_config.py +58 -47
  83. claude_mpm/core/injectable_service.py +1 -1
  84. claude_mpm/core/interactive_session.py +61 -152
  85. claude_mpm/core/interfaces.py +1 -100
  86. claude_mpm/core/lazy.py +5 -5
  87. claude_mpm/core/log_manager.py +587 -0
  88. claude_mpm/core/logger.py +125 -8
  89. claude_mpm/core/logging_config.py +15 -17
  90. claude_mpm/core/minimal_framework_loader.py +5 -8
  91. claude_mpm/core/oneshot_session.py +15 -33
  92. claude_mpm/core/optimized_agent_loader.py +4 -6
  93. claude_mpm/core/optimized_startup.py +2 -1
  94. claude_mpm/core/output_style_manager.py +147 -106
  95. claude_mpm/core/pm_hook_interceptor.py +0 -1
  96. claude_mpm/core/service_registry.py +11 -8
  97. claude_mpm/core/session_manager.py +1 -2
  98. claude_mpm/core/shared/__init__.py +1 -1
  99. claude_mpm/core/shared/config_loader.py +101 -97
  100. claude_mpm/core/shared/path_resolver.py +72 -68
  101. claude_mpm/core/shared/singleton_manager.py +56 -50
  102. claude_mpm/core/socketio_pool.py +26 -6
  103. claude_mpm/core/tool_access_control.py +4 -5
  104. claude_mpm/core/typing_utils.py +50 -59
  105. claude_mpm/core/unified_agent_registry.py +14 -19
  106. claude_mpm/core/unified_config.py +4 -6
  107. claude_mpm/core/unified_paths.py +197 -109
  108. claude_mpm/dashboard/open_dashboard.py +2 -4
  109. claude_mpm/experimental/cli_enhancements.py +51 -36
  110. claude_mpm/generators/agent_profile_generator.py +2 -4
  111. claude_mpm/hooks/base_hook.py +1 -2
  112. claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
  113. claude_mpm/hooks/claude_hooks/event_handlers.py +99 -154
  114. claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
  115. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
  116. claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
  117. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
  118. claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
  119. claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
  120. claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
  121. claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
  122. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  123. claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
  124. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  125. claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
  126. claude_mpm/hooks/memory_integration_hook.py +5 -5
  127. claude_mpm/hooks/tool_call_interceptor.py +1 -1
  128. claude_mpm/hooks/validation_hooks.py +4 -4
  129. claude_mpm/init.py +4 -9
  130. claude_mpm/models/__init__.py +2 -2
  131. claude_mpm/models/agent_session.py +11 -14
  132. claude_mpm/scripts/mcp_server.py +20 -11
  133. claude_mpm/scripts/mcp_wrapper.py +5 -5
  134. claude_mpm/scripts/mpm_doctor.py +321 -0
  135. claude_mpm/scripts/socketio_daemon.py +28 -25
  136. claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
  137. claude_mpm/scripts/socketio_server_manager.py +116 -95
  138. claude_mpm/services/__init__.py +49 -49
  139. claude_mpm/services/agent_capabilities_service.py +12 -18
  140. claude_mpm/services/agents/__init__.py +22 -22
  141. claude_mpm/services/agents/agent_builder.py +140 -119
  142. claude_mpm/services/agents/deployment/__init__.py +3 -3
  143. claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
  144. claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
  145. claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
  146. claude_mpm/services/agents/deployment/agent_deployment.py +129 -511
  147. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
  148. claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
  149. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
  150. claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
  151. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
  152. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
  153. claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
  154. claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
  155. claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
  156. claude_mpm/services/agents/deployment/agent_validator.py +0 -1
  157. claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
  158. claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
  159. claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
  160. claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
  161. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  162. claude_mpm/services/agents/deployment/config/__init__.py +1 -1
  163. claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
  164. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  165. claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
  166. claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
  167. claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
  168. claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
  169. claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
  170. claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
  171. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
  172. claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
  173. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
  174. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
  175. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
  176. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
  177. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
  178. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
  179. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
  180. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
  181. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
  182. claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
  183. claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
  184. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
  185. claude_mpm/services/agents/deployment/results/__init__.py +1 -1
  186. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
  187. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  188. claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
  189. claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
  190. claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
  191. claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
  192. claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
  193. claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
  194. claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
  195. claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
  196. claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
  197. claude_mpm/services/agents/loading/__init__.py +1 -1
  198. claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
  199. claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
  200. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
  201. claude_mpm/services/agents/management/__init__.py +1 -1
  202. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
  203. claude_mpm/services/agents/management/agent_management_service.py +5 -9
  204. claude_mpm/services/agents/memory/__init__.py +4 -4
  205. claude_mpm/services/agents/memory/agent_memory_manager.py +157 -503
  206. claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
  207. claude_mpm/services/agents/memory/content_manager.py +44 -38
  208. claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
  209. claude_mpm/services/agents/memory/memory_file_service.py +103 -0
  210. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  211. claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
  212. claude_mpm/services/agents/memory/template_generator.py +4 -6
  213. claude_mpm/services/agents/registry/__init__.py +11 -7
  214. claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
  215. claude_mpm/services/agents/registry/modification_tracker.py +3 -6
  216. claude_mpm/services/async_session_logger.py +1 -2
  217. claude_mpm/services/claude_session_logger.py +1 -2
  218. claude_mpm/services/cli/__init__.py +18 -0
  219. claude_mpm/services/cli/agent_cleanup_service.py +407 -0
  220. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  221. claude_mpm/services/cli/agent_listing_service.py +463 -0
  222. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  223. claude_mpm/services/cli/agent_validation_service.py +589 -0
  224. claude_mpm/services/cli/dashboard_launcher.py +424 -0
  225. claude_mpm/services/cli/memory_crud_service.py +617 -0
  226. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  227. claude_mpm/services/cli/session_manager.py +513 -0
  228. claude_mpm/services/cli/socketio_manager.py +498 -0
  229. claude_mpm/services/cli/startup_checker.py +370 -0
  230. claude_mpm/services/command_deployment_service.py +173 -0
  231. claude_mpm/services/command_handler_service.py +20 -22
  232. claude_mpm/services/core/__init__.py +25 -25
  233. claude_mpm/services/core/base.py +0 -5
  234. claude_mpm/services/core/cache_manager.py +311 -0
  235. claude_mpm/services/core/interfaces/__init__.py +32 -32
  236. claude_mpm/services/core/interfaces/agent.py +0 -21
  237. claude_mpm/services/core/interfaces/communication.py +0 -27
  238. claude_mpm/services/core/interfaces/infrastructure.py +0 -56
  239. claude_mpm/services/core/interfaces/service.py +0 -29
  240. claude_mpm/services/core/memory_manager.py +637 -0
  241. claude_mpm/services/core/path_resolver.py +498 -0
  242. claude_mpm/services/core/service_container.py +520 -0
  243. claude_mpm/services/core/service_interfaces.py +436 -0
  244. claude_mpm/services/diagnostics/__init__.py +1 -1
  245. claude_mpm/services/diagnostics/checks/__init__.py +6 -6
  246. claude_mpm/services/diagnostics/checks/agent_check.py +152 -97
  247. claude_mpm/services/diagnostics/checks/base_check.py +12 -16
  248. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
  249. claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
  250. claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
  251. claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
  252. claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
  253. claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
  254. claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
  255. claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
  256. claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
  257. claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
  258. claude_mpm/services/diagnostics/models.py +21 -19
  259. claude_mpm/services/event_aggregator.py +10 -17
  260. claude_mpm/services/event_bus/__init__.py +1 -1
  261. claude_mpm/services/event_bus/config.py +54 -35
  262. claude_mpm/services/event_bus/event_bus.py +76 -71
  263. claude_mpm/services/event_bus/relay.py +74 -64
  264. claude_mpm/services/events/__init__.py +11 -11
  265. claude_mpm/services/events/consumers/__init__.py +3 -3
  266. claude_mpm/services/events/consumers/dead_letter.py +71 -63
  267. claude_mpm/services/events/consumers/logging.py +39 -37
  268. claude_mpm/services/events/consumers/metrics.py +56 -57
  269. claude_mpm/services/events/consumers/socketio.py +82 -81
  270. claude_mpm/services/events/core.py +110 -99
  271. claude_mpm/services/events/interfaces.py +56 -72
  272. claude_mpm/services/events/producers/__init__.py +1 -1
  273. claude_mpm/services/events/producers/hook.py +38 -38
  274. claude_mpm/services/events/producers/system.py +46 -44
  275. claude_mpm/services/exceptions.py +81 -80
  276. claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
  277. claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
  278. claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
  279. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
  280. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
  281. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
  283. claude_mpm/services/hook_service.py +6 -9
  284. claude_mpm/services/infrastructure/__init__.py +1 -1
  285. claude_mpm/services/infrastructure/context_preservation.py +8 -12
  286. claude_mpm/services/infrastructure/monitoring.py +21 -23
  287. claude_mpm/services/mcp_gateway/__init__.py +37 -37
  288. claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
  289. claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
  290. claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
  291. claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
  292. claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
  293. claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
  294. claude_mpm/services/mcp_gateway/core/base.py +0 -3
  295. claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
  296. claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
  297. claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
  298. claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
  299. claude_mpm/services/mcp_gateway/main.py +2 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
  302. claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
  309. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
  310. claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
  311. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
  312. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
  313. claude_mpm/services/memory/__init__.py +3 -3
  314. claude_mpm/services/memory/builder.py +3 -6
  315. claude_mpm/services/memory/cache/__init__.py +1 -1
  316. claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
  317. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  318. claude_mpm/services/memory/indexed_memory.py +5 -7
  319. claude_mpm/services/memory/optimizer.py +7 -10
  320. claude_mpm/services/memory/router.py +8 -9
  321. claude_mpm/services/memory_hook_service.py +48 -34
  322. claude_mpm/services/monitor_build_service.py +77 -73
  323. claude_mpm/services/port_manager.py +130 -108
  324. claude_mpm/services/project/analyzer.py +12 -10
  325. claude_mpm/services/project/registry.py +11 -11
  326. claude_mpm/services/recovery_manager.py +10 -19
  327. claude_mpm/services/response_tracker.py +0 -1
  328. claude_mpm/services/runner_configuration_service.py +19 -20
  329. claude_mpm/services/session_management_service.py +7 -11
  330. claude_mpm/services/shared/__init__.py +1 -1
  331. claude_mpm/services/shared/async_service_base.py +58 -50
  332. claude_mpm/services/shared/config_service_base.py +73 -67
  333. claude_mpm/services/shared/lifecycle_service_base.py +82 -78
  334. claude_mpm/services/shared/manager_base.py +94 -82
  335. claude_mpm/services/shared/service_factory.py +96 -98
  336. claude_mpm/services/socketio/__init__.py +3 -3
  337. claude_mpm/services/socketio/client_proxy.py +5 -5
  338. claude_mpm/services/socketio/event_normalizer.py +199 -181
  339. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  340. claude_mpm/services/socketio/handlers/base.py +5 -4
  341. claude_mpm/services/socketio/handlers/connection.py +163 -136
  342. claude_mpm/services/socketio/handlers/file.py +13 -14
  343. claude_mpm/services/socketio/handlers/git.py +12 -7
  344. claude_mpm/services/socketio/handlers/hook.py +49 -44
  345. claude_mpm/services/socketio/handlers/memory.py +0 -1
  346. claude_mpm/services/socketio/handlers/project.py +0 -1
  347. claude_mpm/services/socketio/handlers/registry.py +37 -19
  348. claude_mpm/services/socketio/migration_utils.py +98 -84
  349. claude_mpm/services/socketio/server/__init__.py +1 -1
  350. claude_mpm/services/socketio/server/broadcaster.py +81 -87
  351. claude_mpm/services/socketio/server/core.py +65 -54
  352. claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
  353. claude_mpm/services/socketio/server/main.py +64 -38
  354. claude_mpm/services/socketio_client_manager.py +10 -12
  355. claude_mpm/services/subprocess_launcher_service.py +4 -7
  356. claude_mpm/services/system_instructions_service.py +13 -14
  357. claude_mpm/services/ticket_manager.py +2 -2
  358. claude_mpm/services/utility_service.py +5 -13
  359. claude_mpm/services/version_control/__init__.py +16 -16
  360. claude_mpm/services/version_control/branch_strategy.py +5 -8
  361. claude_mpm/services/version_control/conflict_resolution.py +9 -23
  362. claude_mpm/services/version_control/git_operations.py +5 -7
  363. claude_mpm/services/version_control/semantic_versioning.py +16 -17
  364. claude_mpm/services/version_control/version_parser.py +13 -18
  365. claude_mpm/services/version_service.py +10 -11
  366. claude_mpm/storage/__init__.py +1 -1
  367. claude_mpm/storage/state_storage.py +22 -28
  368. claude_mpm/utils/__init__.py +6 -6
  369. claude_mpm/utils/agent_dependency_loader.py +47 -33
  370. claude_mpm/utils/config_manager.py +11 -14
  371. claude_mpm/utils/dependency_cache.py +1 -1
  372. claude_mpm/utils/dependency_manager.py +13 -17
  373. claude_mpm/utils/dependency_strategies.py +8 -10
  374. claude_mpm/utils/environment_context.py +3 -9
  375. claude_mpm/utils/error_handler.py +3 -13
  376. claude_mpm/utils/file_utils.py +1 -1
  377. claude_mpm/utils/path_operations.py +8 -12
  378. claude_mpm/utils/robust_installer.py +110 -33
  379. claude_mpm/utils/subprocess_utils.py +5 -6
  380. claude_mpm/validation/agent_validator.py +3 -6
  381. claude_mpm/validation/frontmatter_validator.py +1 -1
  382. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
  383. claude_mpm-4.1.3.dist-info/RECORD +528 -0
  384. claude_mpm/cli/commands/run_config_checker.py +0 -160
  385. claude_mpm-4.1.1.dist-info/RECORD +0 -494
  386. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
  387. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
  388. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
  389. {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ import socket
13
13
  import subprocess
14
14
  import time
15
15
  from pathlib import Path
16
- from typing import Dict, List, Optional, Tuple, NamedTuple
16
+ from typing import Dict, List, NamedTuple, Optional, Tuple
17
17
 
18
18
  import psutil
19
19
 
@@ -22,6 +22,7 @@ from ..core.logging_config import get_logger
22
22
 
23
23
  class ProcessInfo(NamedTuple):
24
24
  """Information about a process using a port."""
25
+
25
26
  pid: int
26
27
  name: str
27
28
  cmdline: str
@@ -49,18 +50,18 @@ class PortManager:
49
50
  try:
50
51
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
51
52
  sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
52
- result = sock.bind(("localhost", port))
53
+ sock.bind(("localhost", port))
53
54
  return True
54
55
  except OSError:
55
56
  return False
56
-
57
+
57
58
  def get_process_on_port(self, port: int) -> Optional[ProcessInfo]:
58
59
  """Get information about the process using a specific port.
59
-
60
+
60
61
  WHY: We need to identify what process is using a port to make intelligent
61
62
  decisions about whether we can reclaim it (our debug scripts) or must
62
63
  avoid it (external processes or our daemons).
63
-
64
+
64
65
  Returns:
65
66
  ProcessInfo with details about the process, or None if port is free
66
67
  """
@@ -68,32 +69,35 @@ class PortManager:
68
69
  # First try using lsof as it's more reliable for port detection
69
70
  try:
70
71
  result = subprocess.run(
71
- ['lsof', '-i', f':{port}', '-sTCP:LISTEN', '-t'],
72
+ ["lsof", "-i", f":{port}", "-sTCP:LISTEN", "-t"],
72
73
  capture_output=True,
73
74
  text=True,
74
- timeout=2
75
+ timeout=2,
76
+ check=False,
75
77
  )
76
78
  if result.returncode == 0 and result.stdout.strip():
77
79
  # Get the PID from lsof output
78
80
  pid = int(result.stdout.strip().split()[0])
79
81
  try:
80
82
  process = psutil.Process(pid)
81
- cmdline = ' '.join(process.cmdline())
82
-
83
+ cmdline = " ".join(process.cmdline())
84
+
83
85
  # Determine if this is our process and what type
84
86
  is_ours = self._is_our_process(pid, cmdline)
85
87
  is_debug = self._is_debug_process(cmdline) if is_ours else False
86
- is_daemon = self._is_daemon_process(cmdline) if is_ours else False
87
-
88
+ is_daemon = (
89
+ self._is_daemon_process(cmdline) if is_ours else False
90
+ )
91
+
88
92
  return ProcessInfo(
89
93
  pid=pid,
90
94
  name=process.name(),
91
95
  cmdline=cmdline,
92
96
  is_ours=is_ours,
93
97
  is_debug=is_debug,
94
- is_daemon=is_daemon
98
+ is_daemon=is_daemon,
95
99
  )
96
- except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
100
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
97
101
  # Process exists but we can't access it
98
102
  return ProcessInfo(
99
103
  pid=pid,
@@ -101,31 +105,33 @@ class PortManager:
101
105
  cmdline="<permission denied>",
102
106
  is_ours=False,
103
107
  is_debug=False,
104
- is_daemon=False
108
+ is_daemon=False,
105
109
  )
106
110
  except (subprocess.TimeoutExpired, FileNotFoundError):
107
111
  # lsof not available or timed out, fall back to psutil
108
112
  pass
109
-
113
+
110
114
  # Fallback to psutil method
111
- for conn in psutil.net_connections(kind='inet'):
112
- if conn.laddr.port == port and conn.status == 'LISTEN':
115
+ for conn in psutil.net_connections(kind="inet"):
116
+ if conn.laddr.port == port and conn.status == "LISTEN":
113
117
  try:
114
118
  process = psutil.Process(conn.pid)
115
- cmdline = ' '.join(process.cmdline())
116
-
119
+ cmdline = " ".join(process.cmdline())
120
+
117
121
  # Determine if this is our process and what type
118
122
  is_ours = self._is_our_process(conn.pid, cmdline)
119
123
  is_debug = self._is_debug_process(cmdline) if is_ours else False
120
- is_daemon = self._is_daemon_process(cmdline) if is_ours else False
121
-
124
+ is_daemon = (
125
+ self._is_daemon_process(cmdline) if is_ours else False
126
+ )
127
+
122
128
  return ProcessInfo(
123
129
  pid=conn.pid,
124
130
  name=process.name(),
125
131
  cmdline=cmdline,
126
132
  is_ours=is_ours,
127
133
  is_debug=is_debug,
128
- is_daemon=is_daemon
134
+ is_daemon=is_daemon,
129
135
  )
130
136
  except (psutil.NoSuchProcess, psutil.AccessDenied):
131
137
  # Can't access process details, mark as unknown external
@@ -135,7 +141,7 @@ class PortManager:
135
141
  cmdline="<permission denied>",
136
142
  is_ours=False,
137
143
  is_debug=False,
138
- is_daemon=False
144
+ is_daemon=False,
139
145
  )
140
146
  except psutil.AccessDenied:
141
147
  # No permission to check network connections
@@ -148,158 +154,164 @@ class PortManager:
148
154
  cmdline="<unable to determine>",
149
155
  is_ours=False,
150
156
  is_debug=False,
151
- is_daemon=False
157
+ is_daemon=False,
152
158
  )
153
159
  except Exception as e:
154
160
  self.logger.debug(f"Error getting process on port {port}: {e}")
155
-
161
+
156
162
  return None
157
-
158
- def _is_our_process(self, pid: int, cmdline: str = None) -> bool:
163
+
164
+ def _is_our_process(self, pid: int, cmdline: Optional[str] = None) -> bool:
159
165
  """Check if a process belongs to claude-mpm.
160
-
166
+
161
167
  WHY: We need to distinguish our processes from external ones to know
162
168
  which ports we can potentially reclaim.
163
169
  """
164
170
  try:
165
171
  if cmdline is None:
166
172
  process = psutil.Process(pid)
167
- cmdline = ' '.join(process.cmdline())
168
-
173
+ cmdline = " ".join(process.cmdline())
174
+
169
175
  cmdline_lower = cmdline.lower()
170
-
176
+
171
177
  # Check for claude-mpm related patterns
172
178
  our_patterns = [
173
- 'claude-mpm',
174
- 'claude_mpm',
175
- 'socketio_debug',
176
- 'socketio_daemon',
177
- 'socketio_server',
179
+ "claude-mpm",
180
+ "claude_mpm",
181
+ "socketio_debug",
182
+ "socketio_daemon",
183
+ "socketio_server",
178
184
  str(self.project_root).lower(), # Running from our project directory
179
- 'scripts/test_', # Our test scripts
180
- 'scripts/debug_', # Our debug scripts
181
- 'scripts/demo_', # Our demo scripts
182
- 'scripts/run_', # Our run scripts
183
- 'scripts/validate_', # Our validation scripts
185
+ "scripts/test_", # Our test scripts
186
+ "scripts/debug_", # Our debug scripts
187
+ "scripts/demo_", # Our demo scripts
188
+ "scripts/run_", # Our run scripts
189
+ "scripts/validate_", # Our validation scripts
184
190
  ]
185
-
191
+
186
192
  return any(pattern in cmdline_lower for pattern in our_patterns)
187
-
193
+
188
194
  except (psutil.NoSuchProcess, psutil.AccessDenied):
189
195
  return False
190
-
196
+
191
197
  def _is_debug_process(self, cmdline: str) -> bool:
192
198
  """Check if a process is a debug/test script (safe to kill).
193
-
199
+
194
200
  WHY: Debug and test scripts can be safely terminated to reclaim ports,
195
201
  unlike production daemons which should be preserved.
196
202
  """
197
203
  cmdline_lower = cmdline.lower()
198
-
204
+
199
205
  debug_patterns = [
200
- 'socketio_debug.py',
201
- 'run_socketio_debug.py',
202
- 'test_',
203
- 'debug_',
204
- 'demo_',
205
- 'validate_',
206
- 'scripts/test',
207
- 'scripts/debug',
208
- 'scripts/demo',
209
- 'scripts/validate',
206
+ "socketio_debug.py",
207
+ "run_socketio_debug.py",
208
+ "test_",
209
+ "debug_",
210
+ "demo_",
211
+ "validate_",
212
+ "scripts/test",
213
+ "scripts/debug",
214
+ "scripts/demo",
215
+ "scripts/validate",
210
216
  ]
211
-
217
+
212
218
  # Also check if NOT a daemon (daemons are not debug scripts)
213
- is_not_daemon = 'daemon' not in cmdline_lower or 'debug' in cmdline_lower
214
-
215
- return any(pattern in cmdline_lower for pattern in debug_patterns) and is_not_daemon
216
-
219
+ is_not_daemon = "daemon" not in cmdline_lower or "debug" in cmdline_lower
220
+
221
+ return (
222
+ any(pattern in cmdline_lower for pattern in debug_patterns)
223
+ and is_not_daemon
224
+ )
225
+
217
226
  def _is_daemon_process(self, cmdline: str) -> bool:
218
227
  """Check if a process is a daemon (should be preserved).
219
-
228
+
220
229
  WHY: Daemon processes are production services that should not be
221
230
  automatically killed. Users must explicitly stop them.
222
231
  """
223
232
  cmdline_lower = cmdline.lower()
224
-
233
+
225
234
  daemon_patterns = [
226
- 'socketio_daemon',
227
- 'claude-mpm monitor',
228
- 'daemon',
235
+ "socketio_daemon",
236
+ "claude-mpm monitor",
237
+ "daemon",
229
238
  ]
230
-
239
+
231
240
  # Exclude debug daemons
232
- if 'debug' in cmdline_lower:
241
+ if "debug" in cmdline_lower:
233
242
  return False
234
-
243
+
235
244
  return any(pattern in cmdline_lower for pattern in daemon_patterns)
236
-
245
+
237
246
  def kill_process_on_port(self, port: int, force: bool = False) -> bool:
238
247
  """Kill a process using a specific port if it's safe to do so.
239
-
248
+
240
249
  WHY: Automatically reclaim ports from our debug scripts while preserving
241
250
  daemons and avoiding external processes.
242
-
251
+
243
252
  Args:
244
253
  port: Port number to reclaim
245
254
  force: If True, kill even daemon processes (requires explicit user action)
246
-
255
+
247
256
  Returns:
248
257
  True if process was killed or port is now free, False otherwise
249
258
  """
250
259
  process_info = self.get_process_on_port(port)
251
-
260
+
252
261
  if not process_info:
253
262
  self.logger.info(f"Port {port} is already free")
254
263
  return True
255
-
264
+
256
265
  if not process_info.is_ours:
257
266
  self.logger.warning(
258
267
  f"Port {port} is used by external process '{process_info.name}' "
259
268
  f"(PID: {process_info.pid}). Cannot reclaim."
260
269
  )
261
270
  return False
262
-
271
+
263
272
  if process_info.is_daemon and not force:
264
273
  self.logger.warning(
265
274
  f"Port {port} is used by our daemon process (PID: {process_info.pid}). "
266
275
  f"Use --force flag or stop the daemon explicitly."
267
276
  )
268
277
  return False
269
-
278
+
270
279
  if process_info.is_debug or force:
271
280
  try:
272
281
  self.logger.info(
273
282
  f"Killing {'debug' if process_info.is_debug else 'daemon'} process "
274
283
  f"{process_info.pid} on port {port}"
275
284
  )
276
-
285
+
277
286
  # Try graceful termination first
278
287
  os.kill(process_info.pid, signal.SIGTERM)
279
-
288
+
280
289
  # Wait up to 2 seconds for graceful shutdown
281
290
  for _ in range(20):
282
291
  time.sleep(0.1)
283
292
  if not psutil.pid_exists(process_info.pid):
284
- self.logger.info(f"Process {process_info.pid} terminated gracefully")
293
+ self.logger.info(
294
+ f"Process {process_info.pid} terminated gracefully"
295
+ )
285
296
  return True
286
-
297
+
287
298
  # Force kill if still running
288
- self.logger.warning(f"Process {process_info.pid} didn't terminate, forcing kill")
299
+ self.logger.warning(
300
+ f"Process {process_info.pid} didn't terminate, forcing kill"
301
+ )
289
302
  os.kill(process_info.pid, signal.SIGKILL)
290
303
  time.sleep(0.5)
291
-
304
+
292
305
  if not psutil.pid_exists(process_info.pid):
293
306
  self.logger.info(f"Process {process_info.pid} force killed")
294
307
  return True
295
- else:
296
- self.logger.error(f"Failed to kill process {process_info.pid}")
297
- return False
298
-
308
+ self.logger.error(f"Failed to kill process {process_info.pid}")
309
+ return False
310
+
299
311
  except Exception as e:
300
312
  self.logger.error(f"Error killing process {process_info.pid}: {e}")
301
313
  return False
302
-
314
+
303
315
  return False
304
316
 
305
317
  def is_claude_mpm_instance(self, port: int) -> Tuple[bool, Optional[Dict]]:
@@ -344,14 +356,14 @@ class PortManager:
344
356
  self, preferred_port: Optional[int] = None, reclaim: bool = True
345
357
  ) -> Optional[int]:
346
358
  """Find an available port, preferring the specified port if given.
347
-
359
+
348
360
  WHY: Enhanced to intelligently reclaim ports from our debug processes
349
361
  while avoiding external processes and preserving daemons.
350
-
362
+
351
363
  Args:
352
364
  preferred_port: Port to try first
353
365
  reclaim: If True, try to reclaim ports from our debug scripts
354
-
366
+
355
367
  Returns:
356
368
  Available port number or None if no ports available
357
369
  """
@@ -359,7 +371,7 @@ class PortManager:
359
371
  if preferred_port and preferred_port in self.PORT_RANGE:
360
372
  if self.is_port_available(preferred_port):
361
373
  return preferred_port
362
-
374
+
363
375
  # Port is in use - check if we can reclaim it
364
376
  if reclaim:
365
377
  process_info = self.get_process_on_port(preferred_port)
@@ -384,7 +396,7 @@ class PortManager:
384
396
  # Try default port
385
397
  if self.is_port_available(self.DEFAULT_PORT):
386
398
  return self.DEFAULT_PORT
387
-
399
+
388
400
  # Check if we can reclaim default port
389
401
  if reclaim:
390
402
  process_info = self.get_process_on_port(self.DEFAULT_PORT)
@@ -404,7 +416,7 @@ class PortManager:
404
416
 
405
417
  if self.is_port_available(port):
406
418
  return port
407
-
419
+
408
420
  # Try to reclaim if it's our debug process
409
421
  if reclaim:
410
422
  process_info = self.get_process_on_port(port)
@@ -462,7 +474,7 @@ class PortManager:
462
474
  """Load registered instances from file."""
463
475
  try:
464
476
  if self.instances_file.exists():
465
- with open(self.instances_file, "r") as f:
477
+ with open(self.instances_file) as f:
466
478
  return json.load(f)
467
479
  except Exception as e:
468
480
  self.logger.warning(f"Failed to load instances file: {e}")
@@ -522,13 +534,13 @@ class PortManager:
522
534
  return instance_info
523
535
 
524
536
  return None
525
-
537
+
526
538
  def get_port_status(self, port: int) -> Dict[str, any]:
527
539
  """Get detailed status of a port including what's using it.
528
-
540
+
529
541
  WHY: Provides comprehensive information for users to understand
530
542
  port conflicts and make informed decisions.
531
-
543
+
532
544
  Returns:
533
545
  Dictionary with port status details
534
546
  """
@@ -537,9 +549,9 @@ class PortManager:
537
549
  "available": self.is_port_available(port),
538
550
  "process": None,
539
551
  "instance": None,
540
- "recommendation": None
552
+ "recommendation": None,
541
553
  }
542
-
554
+
543
555
  # Check for process using the port
544
556
  process_info = self.get_process_on_port(port)
545
557
  if process_info:
@@ -549,27 +561,37 @@ class PortManager:
549
561
  "is_ours": process_info.is_ours,
550
562
  "is_debug": process_info.is_debug,
551
563
  "is_daemon": process_info.is_daemon,
552
- "cmdline": process_info.cmdline[:100] + "..." if len(process_info.cmdline) > 100 else process_info.cmdline
564
+ "cmdline": (
565
+ process_info.cmdline[:100] + "..."
566
+ if len(process_info.cmdline) > 100
567
+ else process_info.cmdline
568
+ ),
553
569
  }
554
-
570
+
555
571
  # Provide recommendation based on process type
556
572
  if process_info.is_ours:
557
573
  if process_info.is_debug:
558
- status["recommendation"] = "Can be automatically reclaimed (debug process)"
574
+ status["recommendation"] = (
575
+ "Can be automatically reclaimed (debug process)"
576
+ )
559
577
  elif process_info.is_daemon:
560
- status["recommendation"] = "Stop daemon with 'claude-mpm monitor stop' or use --force"
578
+ status["recommendation"] = (
579
+ "Stop daemon with 'claude-mpm monitor stop' or use --force"
580
+ )
561
581
  else:
562
- status["recommendation"] = "Our process, consider stopping it manually"
582
+ status["recommendation"] = (
583
+ "Our process, consider stopping it manually"
584
+ )
563
585
  else:
564
586
  status["recommendation"] = "External process, choose a different port"
565
-
587
+
566
588
  # Check for registered instance
567
589
  instance_info = self.get_instance_by_port(port)
568
590
  if instance_info:
569
591
  status["instance"] = {
570
592
  "id": instance_info.get("instance_id"),
571
593
  "pid": instance_info.get("pid"),
572
- "start_time": instance_info.get("start_time")
594
+ "start_time": instance_info.get("start_time"),
573
595
  }
574
-
596
+
575
597
  return status
@@ -25,10 +25,10 @@ This service analyzes:
25
25
  import json
26
26
  import logging
27
27
  import re
28
- from collections import Counter, defaultdict
28
+ from collections import Counter
29
29
  from dataclasses import asdict, dataclass
30
30
  from pathlib import Path
31
- from typing import Any, Dict, List, Optional, Set, Tuple
31
+ from typing import Any, Dict, List, Optional
32
32
 
33
33
  from claude_mpm.core.config import Config
34
34
  from claude_mpm.core.interfaces import ProjectAnalyzerInterface
@@ -304,7 +304,7 @@ class ProjectAnalyzer(ProjectAnalyzerInterface):
304
304
  ) -> None:
305
305
  """Parse package.json for Node.js project details."""
306
306
  try:
307
- with open(package_path, "r") as f:
307
+ with open(package_path) as f:
308
308
  package_data = json.load(f)
309
309
 
310
310
  # Extract dependencies
@@ -313,7 +313,7 @@ class ProjectAnalyzer(ProjectAnalyzerInterface):
313
313
  all_deps.update(package_data.get("devDependencies", {}))
314
314
 
315
315
  # Identify frameworks and tools
316
- for dep_name in all_deps.keys():
316
+ for dep_name in all_deps:
317
317
  dep_lower = dep_name.lower()
318
318
 
319
319
  # Web frameworks
@@ -327,12 +327,14 @@ class ProjectAnalyzer(ProjectAnalyzerInterface):
327
327
  db in dep_lower for db in ["mysql", "postgres", "mongodb", "redis"]
328
328
  ):
329
329
  characteristics.databases.append(dep_name)
330
- elif any(
331
- test in dep_lower
332
- for test in ["jest", "mocha", "cypress", "playwright"]
330
+ elif (
331
+ any(
332
+ test in dep_lower
333
+ for test in ["jest", "mocha", "cypress", "playwright"]
334
+ )
335
+ and not characteristics.testing_framework
333
336
  ):
334
- if not characteristics.testing_framework:
335
- characteristics.testing_framework = dep_name
337
+ characteristics.testing_framework = dep_name
336
338
 
337
339
  characteristics.key_dependencies.append(dep_name)
338
340
 
@@ -423,7 +425,7 @@ class ProjectAnalyzer(ProjectAnalyzerInterface):
423
425
  cargo_data = tomllib.load(f)
424
426
 
425
427
  deps = cargo_data.get("dependencies", {})
426
- for dep_name in deps.keys():
428
+ for dep_name in deps:
427
429
  characteristics.key_dependencies.append(dep_name)
428
430
 
429
431
  # Identify common Rust frameworks
@@ -18,7 +18,6 @@ to provide complete project lifecycle tracking.
18
18
 
19
19
  import os
20
20
  import platform
21
- import shutil
22
21
  import subprocess
23
22
  import sys
24
23
  import uuid
@@ -29,14 +28,11 @@ from typing import Any, Dict, List, Optional
29
28
  import yaml
30
29
 
31
30
  from claude_mpm.core.logger import get_logger
32
- from claude_mpm.core.unified_paths import get_project_root
33
31
 
34
32
 
35
33
  class ProjectRegistryError(Exception):
36
34
  """Base exception for project registry operations."""
37
35
 
38
- pass
39
-
40
36
 
41
37
  class ProjectRegistry:
42
38
  """
@@ -102,10 +98,9 @@ class ProjectRegistry:
102
98
  )
103
99
  # Update existing entry with current session info
104
100
  return self._update_existing_entry(existing_entry)
105
- else:
106
- self.logger.debug("Creating new project registry entry")
107
- # Create new entry
108
- return self._create_new_entry()
101
+ self.logger.debug("Creating new project registry entry")
102
+ # Create new entry
103
+ return self._create_new_entry()
109
104
 
110
105
  except Exception as e:
111
106
  self.logger.error(f"Failed to get or create project entry: {e}")
@@ -129,7 +124,7 @@ class ProjectRegistry:
129
124
  # Search all registry files
130
125
  for registry_file in self.registry_dir.glob("*.yaml"):
131
126
  try:
132
- with open(registry_file, "r", encoding="utf-8") as f:
127
+ with open(registry_file, encoding="utf-8") as f:
133
128
  data = yaml.safe_load(f) or {}
134
129
 
135
130
  # Check if project_path matches
@@ -311,6 +306,7 @@ class ProjectRegistry:
311
306
  capture_output=True,
312
307
  text=True,
313
308
  timeout=5,
309
+ check=False,
314
310
  )
315
311
 
316
312
  if result.returncode == 0:
@@ -324,6 +320,7 @@ class ProjectRegistry:
324
320
  capture_output=True,
325
321
  text=True,
326
322
  timeout=5,
323
+ check=False,
327
324
  )
328
325
  if result.returncode == 0:
329
326
  git_info["branch"] = result.stdout.strip()
@@ -338,6 +335,7 @@ class ProjectRegistry:
338
335
  capture_output=True,
339
336
  text=True,
340
337
  timeout=5,
338
+ check=False,
341
339
  )
342
340
  if result.returncode == 0:
343
341
  git_info["remote_url"] = result.stdout.strip()
@@ -352,6 +350,7 @@ class ProjectRegistry:
352
350
  capture_output=True,
353
351
  text=True,
354
352
  timeout=5,
353
+ check=False,
355
354
  )
356
355
  if result.returncode == 0:
357
356
  git_info["last_commit"] = result.stdout.strip()
@@ -366,6 +365,7 @@ class ProjectRegistry:
366
365
  capture_output=True,
367
366
  text=True,
368
367
  timeout=5,
368
+ check=False,
369
369
  )
370
370
  if result.returncode == 0:
371
371
  git_info["has_uncommitted"] = bool(result.stdout.strip())
@@ -517,7 +517,7 @@ class ProjectRegistry:
517
517
  try:
518
518
  for registry_file in self.registry_dir.glob("*.yaml"):
519
519
  try:
520
- with open(registry_file, "r", encoding="utf-8") as f:
520
+ with open(registry_file, encoding="utf-8") as f:
521
521
  data = yaml.safe_load(f) or {}
522
522
  projects.append(data)
523
523
  except Exception as e:
@@ -553,7 +553,7 @@ class ProjectRegistry:
553
553
  try:
554
554
  for registry_file in self.registry_dir.glob("*.yaml"):
555
555
  try:
556
- with open(registry_file, "r", encoding="utf-8") as f:
556
+ with open(registry_file, encoding="utf-8") as f:
557
557
  data = yaml.safe_load(f) or {}
558
558
 
559
559
  # Check last accessed time