claude-mpm 3.9.9__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.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( | 
| 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( | 
| 136 | 
            -
                                 | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 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( | 
| 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", | 
| 235 | 
            -
                            " | 
| 236 | 
            -
                             | 
| 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 | 
| 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 | 
            -
                         | 
| 271 | 
            -
                         | 
| 272 | 
            -
                         | 
| 273 | 
            -
                         | 
| 274 | 
            -
                         | 
| 275 | 
            -
                         | 
| 276 | 
            -
                         | 
| 277 | 
            -
                         | 
| 278 | 
            -
                         | 
| 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( | 
| 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( | 
| 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 | 
            -
                         | 
| 366 | 
            -
                         | 
| 367 | 
            -
                         | 
| 368 | 
            -
                         | 
| 369 | 
            -
                         | 
| 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  | 
| 392 | 
            -
                            error_lines.append(line.split( | 
| 393 | 
            -
             | 
| 400 | 
            +
                        if "ERROR:" in line:
         | 
| 401 | 
            +
                            error_lines.append(line.split("ERROR:", 1)[1].strip())
         | 
| 402 | 
            +
             | 
| 394 403 | 
             
                    if error_lines:
         | 
| 395 | 
            -
                        return  | 
| 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  | 
| 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 =  | 
| 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, ""
         |