claude-mpm 3.9.11__py3-none-any.whl → 4.0.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 (419) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +1 -1
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +203 -81
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +305 -197
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +104 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  113. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  114. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  115. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  116. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  117. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  118. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  119. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  120. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  121. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  122. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  123. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  124. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  125. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  126. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  127. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  128. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  129. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  130. claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
  131. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  132. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
  133. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  134. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  135. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  136. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  137. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  138. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  139. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  140. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  141. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  142. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  143. claude_mpm/dashboard/static/js/socket-client.js +120 -54
  144. claude_mpm/dashboard/templates/index.html +40 -50
  145. claude_mpm/experimental/cli_enhancements.py +60 -58
  146. claude_mpm/generators/__init__.py +1 -1
  147. claude_mpm/generators/agent_profile_generator.py +75 -65
  148. claude_mpm/hooks/__init__.py +1 -1
  149. claude_mpm/hooks/base_hook.py +33 -28
  150. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  151. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  152. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  153. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  154. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  155. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  156. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  157. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  158. claude_mpm/hooks/memory_integration_hook.py +140 -100
  159. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  160. claude_mpm/hooks/validation_hooks.py +57 -49
  161. claude_mpm/init.py +145 -121
  162. claude_mpm/models/__init__.py +9 -9
  163. claude_mpm/models/agent_definition.py +33 -23
  164. claude_mpm/models/agent_session.py +228 -200
  165. claude_mpm/scripts/__init__.py +1 -1
  166. claude_mpm/scripts/socketio_daemon.py +192 -75
  167. claude_mpm/scripts/socketio_server_manager.py +328 -0
  168. claude_mpm/scripts/start_activity_logging.py +25 -22
  169. claude_mpm/services/__init__.py +68 -43
  170. claude_mpm/services/agent_capabilities_service.py +271 -0
  171. claude_mpm/services/agents/__init__.py +23 -32
  172. claude_mpm/services/agents/deployment/__init__.py +3 -3
  173. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  174. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  175. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  176. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  177. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  178. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  179. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  180. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  181. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  182. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  183. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  184. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  185. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  186. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  187. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  188. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  189. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  190. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  191. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  192. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  193. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  194. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  195. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  196. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  197. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  198. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  199. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  200. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  201. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  202. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  203. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  204. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  205. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  206. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  207. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  208. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  209. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  210. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  211. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  212. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  213. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  214. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  215. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  216. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  217. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  218. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  219. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  220. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  221. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  222. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  223. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  224. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  225. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  226. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  227. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  228. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  229. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  230. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  231. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  232. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  233. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  234. claude_mpm/services/agents/loading/__init__.py +2 -2
  235. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  236. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  237. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  238. claude_mpm/services/agents/management/__init__.py +2 -2
  239. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  240. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  241. claude_mpm/services/agents/memory/__init__.py +9 -6
  242. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  243. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  244. claude_mpm/services/agents/memory/analyzer.py +430 -0
  245. claude_mpm/services/agents/memory/content_manager.py +376 -0
  246. claude_mpm/services/agents/memory/template_generator.py +468 -0
  247. claude_mpm/services/agents/registry/__init__.py +7 -10
  248. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  249. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  250. claude_mpm/services/async_session_logger.py +187 -153
  251. claude_mpm/services/claude_session_logger.py +87 -72
  252. claude_mpm/services/command_handler_service.py +217 -0
  253. claude_mpm/services/communication/__init__.py +3 -2
  254. claude_mpm/services/core/__init__.py +50 -97
  255. claude_mpm/services/core/base.py +60 -53
  256. claude_mpm/services/core/interfaces/__init__.py +188 -0
  257. claude_mpm/services/core/interfaces/agent.py +351 -0
  258. claude_mpm/services/core/interfaces/communication.py +343 -0
  259. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  260. claude_mpm/services/core/interfaces/service.py +434 -0
  261. claude_mpm/services/core/interfaces.py +19 -944
  262. claude_mpm/services/event_aggregator.py +208 -170
  263. claude_mpm/services/exceptions.py +387 -308
  264. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  265. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  266. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  267. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  268. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  269. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  270. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  271. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  272. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  273. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  274. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  275. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  276. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  277. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  278. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  279. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  280. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  281. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  283. claude_mpm/services/hook_service.py +106 -114
  284. claude_mpm/services/infrastructure/__init__.py +7 -5
  285. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  286. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  287. claude_mpm/services/infrastructure/logging.py +83 -76
  288. claude_mpm/services/infrastructure/monitoring.py +547 -404
  289. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  290. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  291. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  292. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  293. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  294. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  295. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  296. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  297. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  298. claude_mpm/services/mcp_gateway/main.py +287 -137
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  309. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  310. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  311. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  312. claude_mpm/services/memory/__init__.py +2 -2
  313. claude_mpm/services/memory/builder.py +451 -362
  314. claude_mpm/services/memory/cache/__init__.py +2 -2
  315. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  316. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  317. claude_mpm/services/memory/indexed_memory.py +195 -193
  318. claude_mpm/services/memory/optimizer.py +267 -234
  319. claude_mpm/services/memory/router.py +571 -263
  320. claude_mpm/services/memory_hook_service.py +237 -0
  321. claude_mpm/services/port_manager.py +223 -0
  322. claude_mpm/services/project/__init__.py +3 -3
  323. claude_mpm/services/project/analyzer.py +451 -305
  324. claude_mpm/services/project/registry.py +262 -240
  325. claude_mpm/services/recovery_manager.py +287 -231
  326. claude_mpm/services/response_tracker.py +87 -67
  327. claude_mpm/services/runner_configuration_service.py +587 -0
  328. claude_mpm/services/session_management_service.py +304 -0
  329. claude_mpm/services/socketio/__init__.py +4 -4
  330. claude_mpm/services/socketio/client_proxy.py +174 -0
  331. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  332. claude_mpm/services/socketio/handlers/base.py +44 -30
  333. claude_mpm/services/socketio/handlers/connection.py +145 -65
  334. claude_mpm/services/socketio/handlers/file.py +123 -108
  335. claude_mpm/services/socketio/handlers/git.py +607 -373
  336. claude_mpm/services/socketio/handlers/hook.py +170 -0
  337. claude_mpm/services/socketio/handlers/memory.py +4 -4
  338. claude_mpm/services/socketio/handlers/project.py +4 -4
  339. claude_mpm/services/socketio/handlers/registry.py +53 -38
  340. claude_mpm/services/socketio/server/__init__.py +18 -0
  341. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  342. claude_mpm/services/socketio/server/core.py +399 -0
  343. claude_mpm/services/socketio/server/main.py +323 -0
  344. claude_mpm/services/socketio_client_manager.py +160 -133
  345. claude_mpm/services/socketio_server.py +36 -1885
  346. claude_mpm/services/subprocess_launcher_service.py +316 -0
  347. claude_mpm/services/system_instructions_service.py +258 -0
  348. claude_mpm/services/ticket_manager.py +19 -533
  349. claude_mpm/services/utility_service.py +285 -0
  350. claude_mpm/services/version_control/__init__.py +18 -21
  351. claude_mpm/services/version_control/branch_strategy.py +20 -10
  352. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  353. claude_mpm/services/version_control/git_operations.py +52 -21
  354. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  355. claude_mpm/services/version_control/version_parser.py +145 -125
  356. claude_mpm/services/version_service.py +270 -0
  357. claude_mpm/storage/__init__.py +2 -2
  358. claude_mpm/storage/state_storage.py +177 -181
  359. claude_mpm/ticket_wrapper.py +2 -2
  360. claude_mpm/utils/__init__.py +2 -2
  361. claude_mpm/utils/agent_dependency_loader.py +453 -243
  362. claude_mpm/utils/config_manager.py +157 -118
  363. claude_mpm/utils/console.py +1 -1
  364. claude_mpm/utils/dependency_cache.py +102 -107
  365. claude_mpm/utils/dependency_manager.py +52 -47
  366. claude_mpm/utils/dependency_strategies.py +131 -96
  367. claude_mpm/utils/environment_context.py +110 -102
  368. claude_mpm/utils/error_handler.py +75 -55
  369. claude_mpm/utils/file_utils.py +80 -67
  370. claude_mpm/utils/framework_detection.py +12 -11
  371. claude_mpm/utils/import_migration_example.py +12 -60
  372. claude_mpm/utils/imports.py +48 -45
  373. claude_mpm/utils/path_operations.py +100 -93
  374. claude_mpm/utils/robust_installer.py +172 -164
  375. claude_mpm/utils/session_logging.py +30 -23
  376. claude_mpm/utils/subprocess_utils.py +99 -61
  377. claude_mpm/validation/__init__.py +1 -1
  378. claude_mpm/validation/agent_validator.py +151 -111
  379. claude_mpm/validation/frontmatter_validator.py +92 -71
  380. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/cli/commands/run_guarded.py +0 -511
  385. claude_mpm/config/memory_guardian_config.py +0 -325
  386. claude_mpm/config/memory_guardian_yaml.py +0 -335
  387. claude_mpm/core/config_paths.py +0 -150
  388. claude_mpm/core/memory_aware_runner.py +0 -353
  389. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  390. claude_mpm/deployment_paths.py +0 -261
  391. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  392. claude_mpm/models/state_models.py +0 -433
  393. claude_mpm/services/agent/__init__.py +0 -24
  394. claude_mpm/services/agent/deployment.py +0 -2548
  395. claude_mpm/services/agent/management.py +0 -598
  396. claude_mpm/services/agent/registry.py +0 -813
  397. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  398. claude_mpm/services/communication/socketio.py +0 -1935
  399. claude_mpm/services/communication/websocket.py +0 -479
  400. claude_mpm/services/framework_claude_md_generator.py +0 -624
  401. claude_mpm/services/health_monitor.py +0 -893
  402. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  403. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  404. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  405. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  406. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  407. claude_mpm/services/infrastructure/state_manager.py +0 -774
  408. claude_mpm/services/mcp_gateway/manager.py +0 -334
  409. claude_mpm/services/optimized_hook_service.py +0 -542
  410. claude_mpm/services/project_analyzer.py +0 -864
  411. claude_mpm/services/project_registry.py +0 -608
  412. claude_mpm/services/standalone_socketio_server.py +0 -1300
  413. claude_mpm/services/ticket_manager_di.py +0 -318
  414. claude_mpm/services/ticketing_service_original.py +0 -510
  415. claude_mpm/utils/paths.py +0 -395
  416. claude_mpm/utils/platform_memory.py +0 -524
  417. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  418. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  419. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
@@ -9,15 +9,13 @@ DESIGN DECISION: We implement exponential backoff for retries and provide
9
9
  multiple installation strategies (pip, conda, source) to maximize success rate.
10
10
  """
11
11
 
12
+ import re
12
13
  import subprocess
13
14
  import sys
14
15
  import time
15
- import json
16
- import re
17
- from pathlib import Path
18
- from typing import List, Tuple, Optional, Dict, Any
19
- from enum import Enum
20
16
  from dataclasses import dataclass
17
+ from enum import Enum
18
+ from typing import Any, Dict, List, Optional, Tuple
21
19
 
22
20
  from ..core.logger import get_logger
23
21
 
@@ -26,6 +24,7 @@ logger = get_logger(__name__)
26
24
 
27
25
  class InstallStrategy(Enum):
28
26
  """Available installation strategies."""
27
+
29
28
  PIP = "pip"
30
29
  PIP_NO_DEPS = "pip_no_deps"
31
30
  PIP_UPGRADE = "pip_upgrade"
@@ -36,6 +35,7 @@ class InstallStrategy(Enum):
36
35
  @dataclass
37
36
  class InstallAttempt:
38
37
  """Record of an installation attempt."""
38
+
39
39
  strategy: InstallStrategy
40
40
  package: str
41
41
  success: bool
@@ -47,22 +47,22 @@ class InstallAttempt:
47
47
  class RobustPackageInstaller:
48
48
  """
49
49
  Robust package installer with retry logic and multiple strategies.
50
-
50
+
51
51
  WHY: This class handles the complexity of package installation in various
52
52
  environments, network conditions, and Python versions. It ensures maximum
53
53
  success rate while providing clear feedback on failures.
54
54
  """
55
-
55
+
56
56
  def __init__(
57
57
  self,
58
58
  max_retries: int = 3,
59
59
  retry_delay: float = 2.0,
60
60
  timeout: int = 300,
61
- use_cache: bool = True
61
+ use_cache: bool = True,
62
62
  ):
63
63
  """
64
64
  Initialize robust installer.
65
-
65
+
66
66
  Args:
67
67
  max_retries: Maximum number of retry attempts per package
68
68
  retry_delay: Initial delay between retries (uses exponential backoff)
@@ -75,22 +75,20 @@ class RobustPackageInstaller:
75
75
  self.use_cache = use_cache
76
76
  self.attempts: List[InstallAttempt] = []
77
77
  self.success_cache: Dict[str, bool] = {}
78
-
78
+
79
79
  def install_package(
80
- self,
81
- package_spec: str,
82
- strategies: Optional[List[InstallStrategy]] = None
80
+ self, package_spec: str, strategies: Optional[List[InstallStrategy]] = None
83
81
  ) -> Tuple[bool, Optional[str]]:
84
82
  """
85
83
  Install a package using robust retry logic and multiple strategies.
86
-
84
+
87
85
  WHY: Single installation attempts often fail due to transient issues.
88
86
  This method tries multiple strategies with retries to maximize success.
89
-
87
+
90
88
  Args:
91
89
  package_spec: Package specification (e.g., "pandas>=2.0.0")
92
90
  strategies: List of strategies to try (defaults to sensible order)
93
-
91
+
94
92
  Returns:
95
93
  Tuple of (success, error_message)
96
94
  """
@@ -99,7 +97,7 @@ class RobustPackageInstaller:
99
97
  if self.success_cache[package_spec]:
100
98
  logger.debug(f"Package {package_spec} already successfully installed")
101
99
  return True, None
102
-
100
+
103
101
  # Default strategy order
104
102
  if strategies is None:
105
103
  strategies = [
@@ -108,80 +106,81 @@ class RobustPackageInstaller:
108
106
  InstallStrategy.PIP_NO_DEPS,
109
107
  InstallStrategy.PIP_INDEX_URL,
110
108
  ]
111
-
109
+
112
110
  # Extract package name for special handling
113
111
  package_name = self._extract_package_name(package_spec)
114
-
112
+
115
113
  # Special handling for known problematic packages
116
114
  if self._needs_special_handling(package_name):
117
115
  strategies = self._get_special_strategies(package_name)
118
-
116
+
119
117
  # Try each strategy with retries
120
118
  for strategy in strategies:
121
119
  for retry in range(self.max_retries):
122
120
  start_time = time.time()
123
-
121
+
124
122
  # Calculate delay with exponential backoff
125
123
  if retry > 0:
126
124
  delay = self.retry_delay * (2 ** (retry - 1))
127
- logger.info(f"Retry {retry}/{self.max_retries} after {delay:.1f}s delay...")
125
+ logger.info(
126
+ f"Retry {retry}/{self.max_retries} after {delay:.1f}s delay..."
127
+ )
128
128
  time.sleep(delay)
129
-
129
+
130
130
  # Attempt installation
131
131
  success, error = self._attempt_install(package_spec, strategy)
132
132
  duration = time.time() - start_time
133
-
133
+
134
134
  # Record attempt
135
- self.attempts.append(InstallAttempt(
136
- strategy=strategy,
137
- package=package_spec,
138
- success=success,
139
- error=error,
140
- duration=duration,
141
- retry_count=retry
142
- ))
143
-
135
+ self.attempts.append(
136
+ InstallAttempt(
137
+ strategy=strategy,
138
+ package=package_spec,
139
+ success=success,
140
+ error=error,
141
+ duration=duration,
142
+ retry_count=retry,
143
+ )
144
+ )
145
+
144
146
  if success:
145
- logger.info(f"Successfully installed {package_spec} using {strategy.value}")
147
+ logger.info(
148
+ f"Successfully installed {package_spec} using {strategy.value}"
149
+ )
146
150
  self.success_cache[package_spec] = True
147
151
  return True, None
148
-
152
+
149
153
  # Check if error is retryable
150
154
  if not self._is_retryable_error(error):
151
155
  logger.warning(f"Non-retryable error for {package_spec}: {error}")
152
156
  break
153
-
157
+
154
158
  # All attempts failed
155
159
  self.success_cache[package_spec] = False
156
160
  final_error = self._get_consolidated_error(package_spec)
157
161
  return False, final_error
158
-
162
+
159
163
  def _attempt_install(
160
- self,
161
- package_spec: str,
162
- strategy: InstallStrategy
164
+ self, package_spec: str, strategy: InstallStrategy
163
165
  ) -> Tuple[bool, Optional[str]]:
164
166
  """
165
167
  Attempt to install a package using a specific strategy.
166
-
168
+
167
169
  Args:
168
170
  package_spec: Package specification
169
171
  strategy: Installation strategy to use
170
-
172
+
171
173
  Returns:
172
174
  Tuple of (success, error_message)
173
175
  """
174
176
  try:
175
177
  cmd = self._build_install_command(package_spec, strategy)
176
178
  logger.debug(f"Running: {' '.join(cmd)}")
177
-
179
+
178
180
  result = subprocess.run(
179
- cmd,
180
- capture_output=True,
181
- text=True,
182
- timeout=self.timeout
181
+ cmd, capture_output=True, text=True, timeout=self.timeout
183
182
  )
184
-
183
+
185
184
  if result.returncode == 0:
186
185
  # Verify installation
187
186
  if self._verify_installation(package_spec):
@@ -192,141 +191,142 @@ class RobustPackageInstaller:
192
191
  error_msg = self._extract_error_message(result.stderr)
193
192
  logger.debug(f"Installation failed: {error_msg}")
194
193
  return False, error_msg
195
-
194
+
196
195
  except subprocess.TimeoutExpired:
197
196
  return False, f"Installation timed out after {self.timeout}s"
198
197
  except Exception as e:
199
198
  return False, f"Unexpected error: {str(e)}"
200
-
199
+
201
200
  def _build_install_command(
202
- self,
203
- package_spec: str,
204
- strategy: InstallStrategy
201
+ self, package_spec: str, strategy: InstallStrategy
205
202
  ) -> List[str]:
206
203
  """
207
204
  Build the installation command for a given strategy.
208
-
205
+
209
206
  Args:
210
207
  package_spec: Package specification
211
208
  strategy: Installation strategy
212
-
209
+
213
210
  Returns:
214
211
  Command as list of arguments
215
212
  """
216
213
  base_cmd = [sys.executable, "-m", "pip", "install"]
217
-
214
+
218
215
  # Add cache control
219
216
  if not self.use_cache:
220
217
  base_cmd.append("--no-cache-dir")
221
-
218
+
222
219
  if strategy == InstallStrategy.PIP:
223
220
  return base_cmd + [package_spec]
224
-
221
+
225
222
  elif strategy == InstallStrategy.PIP_NO_DEPS:
226
223
  return base_cmd + ["--no-deps", package_spec]
227
-
224
+
228
225
  elif strategy == InstallStrategy.PIP_UPGRADE:
229
226
  return base_cmd + ["--upgrade", package_spec]
230
-
227
+
231
228
  elif strategy == InstallStrategy.PIP_INDEX_URL:
232
229
  # Try alternative index (PyPI mirror)
233
230
  return base_cmd + [
234
- "--index-url", "https://pypi.org/simple",
235
- "--extra-index-url", "https://pypi.python.org/simple",
236
- package_spec
231
+ "--index-url",
232
+ "https://pypi.org/simple",
233
+ "--extra-index-url",
234
+ "https://pypi.python.org/simple",
235
+ package_spec,
237
236
  ]
238
-
237
+
239
238
  else:
240
239
  return base_cmd + [package_spec]
241
-
240
+
242
241
  def _extract_package_name(self, package_spec: str) -> str:
243
242
  """
244
243
  Extract package name from specification.
245
-
244
+
246
245
  Args:
247
246
  package_spec: Package specification (e.g., "pandas>=2.0.0")
248
-
247
+
249
248
  Returns:
250
249
  Package name (e.g., "pandas")
251
250
  """
252
251
  # Remove version specifiers
253
- match = re.match(r'^([a-zA-Z0-9_-]+)', package_spec)
252
+ match = re.match(r"^([a-zA-Z0-9_-]+)", package_spec)
254
253
  if match:
255
254
  return match.group(1)
256
255
  return package_spec
257
-
256
+
258
257
  def _needs_special_handling(self, package_name: str) -> bool:
259
258
  """
260
259
  Check if package needs special installation handling.
261
-
260
+
262
261
  Args:
263
262
  package_name: Name of the package
264
-
263
+
265
264
  Returns:
266
265
  True if package needs special handling
267
266
  """
268
267
  # Known problematic packages
269
268
  special_packages = {
270
- 'tree-sitter-ruby',
271
- 'tree-sitter-php',
272
- 'tree-sitter-javascript',
273
- 'tree-sitter-typescript',
274
- 'tree-sitter-go',
275
- 'tree-sitter-rust',
276
- 'tree-sitter-java',
277
- 'tree-sitter-cpp',
278
- 'tree-sitter-c',
269
+ "tree-sitter-ruby",
270
+ "tree-sitter-php",
271
+ "tree-sitter-javascript",
272
+ "tree-sitter-typescript",
273
+ "tree-sitter-go",
274
+ "tree-sitter-rust",
275
+ "tree-sitter-java",
276
+ "tree-sitter-cpp",
277
+ "tree-sitter-c",
279
278
  }
280
-
279
+
281
280
  return package_name.lower() in special_packages
282
-
281
+
283
282
  def _get_special_strategies(self, package_name: str) -> List[InstallStrategy]:
284
283
  """
285
284
  Get special installation strategies for problematic packages.
286
-
285
+
287
286
  Args:
288
287
  package_name: Name of the package
289
-
288
+
290
289
  Returns:
291
290
  List of strategies to try
292
291
  """
293
292
  # For tree-sitter packages, try upgrade first (often fixes version conflicts)
294
- if package_name.startswith('tree-sitter-'):
293
+ if package_name.startswith("tree-sitter-"):
295
294
  return [
296
295
  InstallStrategy.PIP_UPGRADE,
297
296
  InstallStrategy.PIP,
298
297
  InstallStrategy.PIP_INDEX_URL,
299
298
  InstallStrategy.PIP_NO_DEPS,
300
299
  ]
301
-
300
+
302
301
  return [InstallStrategy.PIP, InstallStrategy.PIP_UPGRADE]
303
-
302
+
304
303
  def _verify_installation(self, package_spec: str) -> bool:
305
304
  """
306
305
  Verify that a package was successfully installed.
307
-
306
+
308
307
  Args:
309
308
  package_spec: Package specification
310
-
309
+
311
310
  Returns:
312
311
  True if package is installed and importable
313
312
  """
314
313
  package_name = self._extract_package_name(package_spec)
315
-
314
+
316
315
  # Convert package name to import name (e.g., tree-sitter-ruby -> tree_sitter_ruby)
317
- import_name = package_name.replace('-', '_')
318
-
316
+ import_name = package_name.replace("-", "_")
317
+
319
318
  try:
320
319
  # Check if package is installed
321
320
  import importlib.metadata
321
+
322
322
  try:
323
323
  version = importlib.metadata.version(package_name)
324
324
  logger.debug(f"Package {package_name} version {version} is installed")
325
-
325
+
326
326
  # For tree-sitter packages, don't try to import (they have C extensions)
327
- if package_name.startswith('tree-sitter-'):
327
+ if package_name.startswith("tree-sitter-"):
328
328
  return True
329
-
329
+
330
330
  # Try to import the package
331
331
  try:
332
332
  __import__(import_name)
@@ -334,80 +334,89 @@ class RobustPackageInstaller:
334
334
  except ImportError:
335
335
  # Some packages have different import names, that's OK
336
336
  return True
337
-
337
+
338
338
  except importlib.metadata.PackageNotFoundError:
339
339
  return False
340
-
340
+
341
341
  except ImportError:
342
342
  # Fallback for older Python versions
343
343
  try:
344
344
  import pkg_resources
345
+
345
346
  pkg_resources.get_distribution(package_name)
346
347
  return True
347
348
  except pkg_resources.DistributionNotFound:
348
349
  return False
349
-
350
+
350
351
  def _is_retryable_error(self, error: Optional[str]) -> bool:
351
352
  """
352
353
  Determine if an error is worth retrying.
353
-
354
+
354
355
  Args:
355
356
  error: Error message
356
-
357
+
357
358
  Returns:
358
359
  True if error is retryable
359
360
  """
360
361
  if not error:
361
362
  return False
362
-
363
+
363
364
  # Retryable error patterns
364
365
  retryable_patterns = [
365
- 'connection', 'timeout', 'temporary failure',
366
- 'network', 'unreachable', 'could not find',
367
- 'no matching distribution', 'httperror',
368
- 'http error', 'ssl', 'certificate',
369
- 'readtimeout', 'connectionerror'
366
+ "connection",
367
+ "timeout",
368
+ "temporary failure",
369
+ "network",
370
+ "unreachable",
371
+ "could not find",
372
+ "no matching distribution",
373
+ "httperror",
374
+ "http error",
375
+ "ssl",
376
+ "certificate",
377
+ "readtimeout",
378
+ "connectionerror",
370
379
  ]
371
-
380
+
372
381
  error_lower = error.lower()
373
382
  return any(pattern in error_lower for pattern in retryable_patterns)
374
-
383
+
375
384
  def _extract_error_message(self, stderr: str) -> str:
376
385
  """
377
386
  Extract meaningful error message from pip stderr.
378
-
387
+
379
388
  Args:
380
389
  stderr: Standard error output from pip
381
-
390
+
382
391
  Returns:
383
392
  Extracted error message
384
393
  """
385
394
  if not stderr:
386
395
  return "Unknown error"
387
-
396
+
388
397
  # Look for ERROR: lines
389
398
  error_lines = []
390
399
  for line in stderr.splitlines():
391
- if 'ERROR:' in line:
392
- error_lines.append(line.split('ERROR:', 1)[1].strip())
393
-
400
+ if "ERROR:" in line:
401
+ error_lines.append(line.split("ERROR:", 1)[1].strip())
402
+
394
403
  if error_lines:
395
- return ' | '.join(error_lines)
396
-
404
+ return " | ".join(error_lines)
405
+
397
406
  # Fall back to last non-empty line
398
407
  lines = [l.strip() for l in stderr.splitlines() if l.strip()]
399
408
  if lines:
400
409
  return lines[-1]
401
-
410
+
402
411
  return "Installation failed"
403
-
412
+
404
413
  def _get_consolidated_error(self, package_spec: str) -> str:
405
414
  """
406
415
  Get a consolidated error message from all attempts.
407
-
416
+
408
417
  Args:
409
418
  package_spec: Package specification that failed
410
-
419
+
411
420
  Returns:
412
421
  Consolidated error message
413
422
  """
@@ -416,87 +425,85 @@ class RobustPackageInstaller:
416
425
  for attempt in self.attempts:
417
426
  if attempt.package == package_spec and attempt.error:
418
427
  errors.add(attempt.error)
419
-
428
+
420
429
  if not errors:
421
- return f"Failed to install {package_spec} after {len(self.attempts)} attempts"
422
-
430
+ return (
431
+ f"Failed to install {package_spec} after {len(self.attempts)} attempts"
432
+ )
433
+
423
434
  # Format error message
424
435
  if len(errors) == 1:
425
436
  return list(errors)[0]
426
437
  else:
427
438
  return f"Multiple errors: {' | '.join(errors)}"
428
-
439
+
429
440
  def install_packages(
430
- self,
431
- packages: List[str],
432
- parallel: bool = False
441
+ self, packages: List[str], parallel: bool = False
433
442
  ) -> Tuple[List[str], List[str], Dict[str, str]]:
434
443
  """
435
444
  Install multiple packages with robust error handling.
436
-
445
+
437
446
  Args:
438
447
  packages: List of package specifications
439
448
  parallel: Whether to attempt parallel installation
440
-
449
+
441
450
  Returns:
442
451
  Tuple of (successful_packages, failed_packages, error_map)
443
452
  """
444
453
  successful = []
445
454
  failed = []
446
455
  errors = {}
447
-
456
+
448
457
  # Group packages that can be installed together
449
458
  if parallel and len(packages) > 1:
450
459
  # Try to install all at once first
451
460
  logger.info(f"Attempting batch installation of {len(packages)} packages...")
452
461
  success, error = self._attempt_batch_install(packages)
453
-
462
+
454
463
  if success:
455
464
  logger.info("Batch installation successful")
456
465
  return packages, [], {}
457
466
  else:
458
467
  logger.warning(f"Batch installation failed: {error}")
459
468
  logger.info("Falling back to individual installation...")
460
-
469
+
461
470
  # Install packages individually
462
471
  for i, package in enumerate(packages, 1):
463
472
  logger.info(f"Installing package {i}/{len(packages)}: {package}")
464
-
473
+
465
474
  success, error = self.install_package(package)
466
-
475
+
467
476
  if success:
468
477
  successful.append(package)
469
478
  else:
470
479
  failed.append(package)
471
480
  errors[package] = error or "Unknown error"
472
-
481
+
473
482
  return successful, failed, errors
474
-
483
+
475
484
  def _attempt_batch_install(self, packages: List[str]) -> Tuple[bool, Optional[str]]:
476
485
  """
477
486
  Attempt to install multiple packages in a single pip command.
478
-
487
+
479
488
  Args:
480
489
  packages: List of package specifications
481
-
490
+
482
491
  Returns:
483
492
  Tuple of (success, error_message)
484
493
  """
485
494
  try:
486
495
  cmd = [sys.executable, "-m", "pip", "install"] + packages
487
-
496
+
488
497
  result = subprocess.run(
489
498
  cmd,
490
499
  capture_output=True,
491
500
  text=True,
492
- timeout=self.timeout * 2 # Longer timeout for batch
501
+ timeout=self.timeout * 2, # Longer timeout for batch
493
502
  )
494
-
503
+
495
504
  if result.returncode == 0:
496
505
  # Verify all packages
497
- all_verified = all(
498
- self._verify_installation(pkg) for pkg in packages
499
- )
506
+ all_verified = all(self._verify_installation(pkg) for pkg in packages)
500
507
  if all_verified:
501
508
  return True, None
502
509
  else:
@@ -504,16 +511,16 @@ class RobustPackageInstaller:
504
511
  else:
505
512
  error_msg = self._extract_error_message(result.stderr)
506
513
  return False, error_msg
507
-
514
+
508
515
  except subprocess.TimeoutExpired:
509
516
  return False, f"Batch installation timed out"
510
517
  except Exception as e:
511
518
  return False, f"Batch installation error: {str(e)}"
512
-
519
+
513
520
  def get_report(self) -> str:
514
521
  """
515
522
  Generate a report of installation attempts.
516
-
523
+
517
524
  Returns:
518
525
  Formatted report string
519
526
  """
@@ -521,67 +528,68 @@ class RobustPackageInstaller:
521
528
  lines.append("=" * 60)
522
529
  lines.append("INSTALLATION REPORT")
523
530
  lines.append("=" * 60)
524
-
531
+
525
532
  # Summary
526
533
  total_attempts = len(self.attempts)
527
534
  successful = sum(1 for a in self.attempts if a.success)
528
535
  failed = total_attempts - successful
529
-
536
+
530
537
  lines.append(f"Total attempts: {total_attempts}")
531
538
  lines.append(f"Successful: {successful}")
532
539
  lines.append(f"Failed: {failed}")
533
540
  lines.append("")
534
-
541
+
535
542
  # Details by package
536
543
  packages = {}
537
544
  for attempt in self.attempts:
538
545
  if attempt.package not in packages:
539
546
  packages[attempt.package] = []
540
547
  packages[attempt.package].append(attempt)
541
-
548
+
542
549
  for package, attempts in packages.items():
543
550
  success = any(a.success for a in attempts)
544
551
  status = "✓" if success else "✗"
545
552
  lines.append(f"{status} {package}:")
546
-
553
+
547
554
  for attempt in attempts:
548
- retry_str = f" (retry {attempt.retry_count})" if attempt.retry_count > 0 else ""
555
+ retry_str = (
556
+ f" (retry {attempt.retry_count})" if attempt.retry_count > 0 else ""
557
+ )
549
558
  result = "success" if attempt.success else f"failed: {attempt.error}"
550
559
  lines.append(f" - {attempt.strategy.value}{retry_str}: {result}")
551
-
560
+
552
561
  lines.append("=" * 60)
553
562
  return "\n".join(lines)
554
563
 
555
564
 
556
565
  def install_with_retry(
557
- packages: List[str],
558
- max_retries: int = 3,
559
- verbose: bool = False
566
+ packages: List[str], max_retries: int = 3, verbose: bool = False
560
567
  ) -> Tuple[bool, str]:
561
568
  """
562
569
  Convenience function to install packages with retry logic.
563
-
570
+
564
571
  Args:
565
572
  packages: List of package specifications
566
573
  max_retries: Maximum retry attempts
567
574
  verbose: Whether to print verbose output
568
-
575
+
569
576
  Returns:
570
577
  Tuple of (all_success, error_message)
571
578
  """
572
579
  if verbose:
573
580
  import logging
581
+
574
582
  logging.getLogger().setLevel(logging.DEBUG)
575
-
583
+
576
584
  installer = RobustPackageInstaller(max_retries=max_retries)
577
585
  successful, failed, errors = installer.install_packages(packages)
578
-
586
+
579
587
  if verbose:
580
588
  print(installer.get_report())
581
-
589
+
582
590
  if failed:
583
591
  error_msg = f"Failed to install {len(failed)} packages: "
584
592
  error_msg += ", ".join(f"{pkg} ({errors[pkg]})" for pkg in failed)
585
593
  return False, error_msg
586
-
587
- return True, ""
594
+
595
+ return True, ""