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
| @@ -5,21 +5,22 @@ configurations across different file formats (JSON, YAML, TOML). | |
| 5 5 | 
             
            """
         | 
| 6 6 |  | 
| 7 7 | 
             
            import json
         | 
| 8 | 
            -
            import logging
         | 
| 9 8 | 
             
            import os
         | 
| 9 | 
            +
            from functools import lru_cache
         | 
| 10 10 | 
             
            from pathlib import Path
         | 
| 11 11 | 
             
            from typing import Any, Dict, List, Optional, Union
         | 
| 12 | 
            -
            from functools import lru_cache
         | 
| 13 12 |  | 
| 14 13 | 
             
            # Handle optional imports
         | 
| 15 14 | 
             
            try:
         | 
| 16 15 | 
             
                import yaml
         | 
| 16 | 
            +
             | 
| 17 17 | 
             
                HAS_YAML = True
         | 
| 18 18 | 
             
            except ImportError:
         | 
| 19 19 | 
             
                HAS_YAML = False
         | 
| 20 20 |  | 
| 21 21 | 
             
            try:
         | 
| 22 22 | 
             
                import toml
         | 
| 23 | 
            +
             | 
| 23 24 | 
             
                HAS_TOML = True
         | 
| 24 25 | 
             
            except ImportError:
         | 
| 25 26 | 
             
                HAS_TOML = False
         | 
| @@ -31,75 +32,75 @@ logger = get_logger(__name__) | |
| 31 32 |  | 
| 32 33 | 
             
            class ConfigurationManager:
         | 
| 33 34 | 
             
                """Unified configuration management with support for multiple formats."""
         | 
| 34 | 
            -
             | 
| 35 | 
            +
             | 
| 35 36 | 
             
                def __init__(self, cache_enabled: bool = True):
         | 
| 36 37 | 
             
                    """Initialize the configuration manager.
         | 
| 37 | 
            -
             | 
| 38 | 
            +
             | 
| 38 39 | 
             
                    Args:
         | 
| 39 40 | 
             
                        cache_enabled: Whether to enable configuration caching
         | 
| 40 41 | 
             
                    """
         | 
| 41 42 | 
             
                    self.cache_enabled = cache_enabled
         | 
| 42 43 | 
             
                    self._cache: Dict[str, Any] = {}
         | 
| 43 | 
            -
             | 
| 44 | 
            +
             | 
| 44 45 | 
             
                def _get_cache_key(self, file_path: Union[str, Path]) -> str:
         | 
| 45 46 | 
             
                    """Generate a cache key for a configuration file."""
         | 
| 46 47 | 
             
                    path = Path(file_path)
         | 
| 47 48 | 
             
                    stat = path.stat()
         | 
| 48 49 | 
             
                    return f"{path.absolute()}:{stat.st_mtime}:{stat.st_size}"
         | 
| 49 | 
            -
             | 
| 50 | 
            +
             | 
| 50 51 | 
             
                def _check_cache(self, file_path: Union[str, Path]) -> Optional[Dict[str, Any]]:
         | 
| 51 52 | 
             
                    """Check if configuration is cached and still valid."""
         | 
| 52 53 | 
             
                    if not self.cache_enabled:
         | 
| 53 54 | 
             
                        return None
         | 
| 54 | 
            -
             | 
| 55 | 
            +
             | 
| 55 56 | 
             
                    try:
         | 
| 56 57 | 
             
                        cache_key = self._get_cache_key(file_path)
         | 
| 57 58 | 
             
                        return self._cache.get(cache_key)
         | 
| 58 59 | 
             
                    except (OSError, IOError):
         | 
| 59 60 | 
             
                        return None
         | 
| 60 | 
            -
             | 
| 61 | 
            +
             | 
| 61 62 | 
             
                def _update_cache(self, file_path: Union[str, Path], config: Dict[str, Any]):
         | 
| 62 63 | 
             
                    """Update the configuration cache."""
         | 
| 63 64 | 
             
                    if not self.cache_enabled:
         | 
| 64 65 | 
             
                        return
         | 
| 65 | 
            -
             | 
| 66 | 
            +
             | 
| 66 67 | 
             
                    try:
         | 
| 67 68 | 
             
                        cache_key = self._get_cache_key(file_path)
         | 
| 68 69 | 
             
                        self._cache[cache_key] = config
         | 
| 69 70 | 
             
                    except (OSError, IOError):
         | 
| 70 71 | 
             
                        pass
         | 
| 71 | 
            -
             | 
| 72 | 
            +
             | 
| 72 73 | 
             
                def clear_cache(self):
         | 
| 73 74 | 
             
                    """Clear the configuration cache."""
         | 
| 74 75 | 
             
                    self._cache.clear()
         | 
| 75 | 
            -
             | 
| 76 | 
            +
             | 
| 76 77 | 
             
                def load_json(self, file_path: Union[str, Path]) -> Dict[str, Any]:
         | 
| 77 78 | 
             
                    """Load JSON configuration file.
         | 
| 78 | 
            -
             | 
| 79 | 
            +
             | 
| 79 80 | 
             
                    Args:
         | 
| 80 81 | 
             
                        file_path: Path to JSON file
         | 
| 81 | 
            -
             | 
| 82 | 
            +
             | 
| 82 83 | 
             
                    Returns:
         | 
| 83 84 | 
             
                        Configuration dictionary
         | 
| 84 | 
            -
             | 
| 85 | 
            +
             | 
| 85 86 | 
             
                    Raises:
         | 
| 86 87 | 
             
                        FileNotFoundError: If file doesn't exist
         | 
| 87 88 | 
             
                        json.JSONDecodeError: If JSON is invalid
         | 
| 88 89 | 
             
                    """
         | 
| 89 90 | 
             
                    file_path = Path(file_path)
         | 
| 90 | 
            -
             | 
| 91 | 
            +
             | 
| 91 92 | 
             
                    # Check cache
         | 
| 92 93 | 
             
                    cached = self._check_cache(file_path)
         | 
| 93 94 | 
             
                    if cached is not None:
         | 
| 94 95 | 
             
                        logger.debug(f"Using cached configuration for {file_path}")
         | 
| 95 96 | 
             
                        return cached
         | 
| 96 | 
            -
             | 
| 97 | 
            +
             | 
| 97 98 | 
             
                    if not file_path.exists():
         | 
| 98 99 | 
             
                        raise FileNotFoundError(f"Configuration file not found: {file_path}")
         | 
| 99 | 
            -
             | 
| 100 | 
            +
             | 
| 100 101 | 
             
                    logger.debug(f"Loading JSON configuration from {file_path}")
         | 
| 101 102 | 
             
                    try:
         | 
| 102 | 
            -
                        with open(file_path,  | 
| 103 | 
            +
                        with open(file_path, "r", encoding="utf-8") as f:
         | 
| 103 104 | 
             
                            config = json.load(f)
         | 
| 104 105 | 
             
                        self._update_cache(file_path, config)
         | 
| 105 106 | 
             
                        return config
         | 
| @@ -109,38 +110,40 @@ class ConfigurationManager: | |
| 109 110 | 
             
                    except Exception as e:
         | 
| 110 111 | 
             
                        logger.error(f"Error loading JSON from {file_path}: {e}")
         | 
| 111 112 | 
             
                        raise
         | 
| 112 | 
            -
             | 
| 113 | 
            +
             | 
| 113 114 | 
             
                def load_yaml(self, file_path: Union[str, Path]) -> Dict[str, Any]:
         | 
| 114 115 | 
             
                    """Load YAML configuration file.
         | 
| 115 | 
            -
             | 
| 116 | 
            +
             | 
| 116 117 | 
             
                    Args:
         | 
| 117 118 | 
             
                        file_path: Path to YAML file
         | 
| 118 | 
            -
             | 
| 119 | 
            +
             | 
| 119 120 | 
             
                    Returns:
         | 
| 120 121 | 
             
                        Configuration dictionary
         | 
| 121 | 
            -
             | 
| 122 | 
            +
             | 
| 122 123 | 
             
                    Raises:
         | 
| 123 124 | 
             
                        ImportError: If PyYAML is not installed
         | 
| 124 125 | 
             
                        FileNotFoundError: If file doesn't exist
         | 
| 125 126 | 
             
                        yaml.YAMLError: If YAML is invalid
         | 
| 126 127 | 
             
                    """
         | 
| 127 128 | 
             
                    if not HAS_YAML:
         | 
| 128 | 
            -
                        raise ImportError( | 
| 129 | 
            -
             | 
| 129 | 
            +
                        raise ImportError(
         | 
| 130 | 
            +
                            "PyYAML is required for YAML support. Install with: pip install pyyaml"
         | 
| 131 | 
            +
                        )
         | 
| 132 | 
            +
             | 
| 130 133 | 
             
                    file_path = Path(file_path)
         | 
| 131 | 
            -
             | 
| 134 | 
            +
             | 
| 132 135 | 
             
                    # Check cache
         | 
| 133 136 | 
             
                    cached = self._check_cache(file_path)
         | 
| 134 137 | 
             
                    if cached is not None:
         | 
| 135 138 | 
             
                        logger.debug(f"Using cached configuration for {file_path}")
         | 
| 136 139 | 
             
                        return cached
         | 
| 137 | 
            -
             | 
| 140 | 
            +
             | 
| 138 141 | 
             
                    if not file_path.exists():
         | 
| 139 142 | 
             
                        raise FileNotFoundError(f"Configuration file not found: {file_path}")
         | 
| 140 | 
            -
             | 
| 143 | 
            +
             | 
| 141 144 | 
             
                    logger.debug(f"Loading YAML configuration from {file_path}")
         | 
| 142 145 | 
             
                    try:
         | 
| 143 | 
            -
                        with open(file_path,  | 
| 146 | 
            +
                        with open(file_path, "r", encoding="utf-8") as f:
         | 
| 144 147 | 
             
                            config = yaml.safe_load(f) or {}
         | 
| 145 148 | 
             
                        self._update_cache(file_path, config)
         | 
| 146 149 | 
             
                        return config
         | 
| @@ -150,38 +153,40 @@ class ConfigurationManager: | |
| 150 153 | 
             
                    except Exception as e:
         | 
| 151 154 | 
             
                        logger.error(f"Error loading YAML from {file_path}: {e}")
         | 
| 152 155 | 
             
                        raise
         | 
| 153 | 
            -
             | 
| 156 | 
            +
             | 
| 154 157 | 
             
                def load_toml(self, file_path: Union[str, Path]) -> Dict[str, Any]:
         | 
| 155 158 | 
             
                    """Load TOML configuration file.
         | 
| 156 | 
            -
             | 
| 159 | 
            +
             | 
| 157 160 | 
             
                    Args:
         | 
| 158 161 | 
             
                        file_path: Path to TOML file
         | 
| 159 | 
            -
             | 
| 162 | 
            +
             | 
| 160 163 | 
             
                    Returns:
         | 
| 161 164 | 
             
                        Configuration dictionary
         | 
| 162 | 
            -
             | 
| 165 | 
            +
             | 
| 163 166 | 
             
                    Raises:
         | 
| 164 167 | 
             
                        ImportError: If toml is not installed
         | 
| 165 168 | 
             
                        FileNotFoundError: If file doesn't exist
         | 
| 166 169 | 
             
                        toml.TomlDecodeError: If TOML is invalid
         | 
| 167 170 | 
             
                    """
         | 
| 168 171 | 
             
                    if not HAS_TOML:
         | 
| 169 | 
            -
                        raise ImportError( | 
| 170 | 
            -
             | 
| 172 | 
            +
                        raise ImportError(
         | 
| 173 | 
            +
                            "toml is required for TOML support. Install with: pip install toml"
         | 
| 174 | 
            +
                        )
         | 
| 175 | 
            +
             | 
| 171 176 | 
             
                    file_path = Path(file_path)
         | 
| 172 | 
            -
             | 
| 177 | 
            +
             | 
| 173 178 | 
             
                    # Check cache
         | 
| 174 179 | 
             
                    cached = self._check_cache(file_path)
         | 
| 175 180 | 
             
                    if cached is not None:
         | 
| 176 181 | 
             
                        logger.debug(f"Using cached configuration for {file_path}")
         | 
| 177 182 | 
             
                        return cached
         | 
| 178 | 
            -
             | 
| 183 | 
            +
             | 
| 179 184 | 
             
                    if not file_path.exists():
         | 
| 180 185 | 
             
                        raise FileNotFoundError(f"Configuration file not found: {file_path}")
         | 
| 181 | 
            -
             | 
| 186 | 
            +
             | 
| 182 187 | 
             
                    logger.debug(f"Loading TOML configuration from {file_path}")
         | 
| 183 188 | 
             
                    try:
         | 
| 184 | 
            -
                        with open(file_path,  | 
| 189 | 
            +
                        with open(file_path, "r", encoding="utf-8") as f:
         | 
| 185 190 | 
             
                            config = toml.load(f)
         | 
| 186 191 | 
             
                        self._update_cache(file_path, config)
         | 
| 187 192 | 
             
                        return config
         | 
| @@ -191,36 +196,41 @@ class ConfigurationManager: | |
| 191 196 | 
             
                    except Exception as e:
         | 
| 192 197 | 
             
                        logger.error(f"Error loading TOML from {file_path}: {e}")
         | 
| 193 198 | 
             
                        raise
         | 
| 194 | 
            -
             | 
| 199 | 
            +
             | 
| 195 200 | 
             
                def load_auto(self, file_path: Union[str, Path]) -> Dict[str, Any]:
         | 
| 196 201 | 
             
                    """Auto-detect format and load configuration file.
         | 
| 197 | 
            -
             | 
| 202 | 
            +
             | 
| 198 203 | 
             
                    Args:
         | 
| 199 204 | 
             
                        file_path: Path to configuration file
         | 
| 200 | 
            -
             | 
| 205 | 
            +
             | 
| 201 206 | 
             
                    Returns:
         | 
| 202 207 | 
             
                        Configuration dictionary
         | 
| 203 | 
            -
             | 
| 208 | 
            +
             | 
| 204 209 | 
             
                    Raises:
         | 
| 205 210 | 
             
                        ValueError: If file format is not supported
         | 
| 206 211 | 
             
                        FileNotFoundError: If file doesn't exist
         | 
| 207 212 | 
             
                    """
         | 
| 208 213 | 
             
                    file_path = Path(file_path)
         | 
| 209 214 | 
             
                    suffix = file_path.suffix.lower()
         | 
| 210 | 
            -
             | 
| 211 | 
            -
                    if suffix in [ | 
| 215 | 
            +
             | 
| 216 | 
            +
                    if suffix in [".json"]:
         | 
| 212 217 | 
             
                        return self.load_json(file_path)
         | 
| 213 | 
            -
                    elif suffix in [ | 
| 218 | 
            +
                    elif suffix in [".yaml", ".yml"]:
         | 
| 214 219 | 
             
                        return self.load_yaml(file_path)
         | 
| 215 | 
            -
                    elif suffix in [ | 
| 220 | 
            +
                    elif suffix in [".toml"]:
         | 
| 216 221 | 
             
                        return self.load_toml(file_path)
         | 
| 217 222 | 
             
                    else:
         | 
| 218 223 | 
             
                        raise ValueError(f"Unsupported configuration format: {suffix}")
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                def save_json( | 
| 221 | 
            -
             | 
| 224 | 
            +
             | 
| 225 | 
            +
                def save_json(
         | 
| 226 | 
            +
                    self,
         | 
| 227 | 
            +
                    config: Dict[str, Any],
         | 
| 228 | 
            +
                    file_path: Union[str, Path],
         | 
| 229 | 
            +
                    indent: int = 2,
         | 
| 230 | 
            +
                    sort_keys: bool = True,
         | 
| 231 | 
            +
                ):
         | 
| 222 232 | 
             
                    """Save configuration as JSON.
         | 
| 223 | 
            -
             | 
| 233 | 
            +
             | 
| 224 234 | 
             
                    Args:
         | 
| 225 235 | 
             
                        config: Configuration dictionary
         | 
| 226 236 | 
             
                        file_path: Path to save JSON file
         | 
| @@ -229,113 +239,132 @@ class ConfigurationManager: | |
| 229 239 | 
             
                    """
         | 
| 230 240 | 
             
                    file_path = Path(file_path)
         | 
| 231 241 | 
             
                    logger.debug(f"Saving JSON configuration to {file_path}")
         | 
| 232 | 
            -
             | 
| 242 | 
            +
             | 
| 233 243 | 
             
                    try:
         | 
| 234 244 | 
             
                        # Create parent directories if needed
         | 
| 235 245 | 
             
                        file_path.parent.mkdir(parents=True, exist_ok=True)
         | 
| 236 | 
            -
             | 
| 237 | 
            -
                        with open(file_path,  | 
| 246 | 
            +
             | 
| 247 | 
            +
                        with open(file_path, "w", encoding="utf-8") as f:
         | 
| 238 248 | 
             
                            json.dump(config, f, indent=indent, sort_keys=sort_keys)
         | 
| 239 249 | 
             
                        logger.info(f"Configuration saved to {file_path}")
         | 
| 240 250 | 
             
                    except Exception as e:
         | 
| 241 251 | 
             
                        logger.error(f"Error saving JSON to {file_path}: {e}")
         | 
| 242 252 | 
             
                        raise
         | 
| 243 | 
            -
             | 
| 244 | 
            -
                def save_yaml( | 
| 245 | 
            -
             | 
| 253 | 
            +
             | 
| 254 | 
            +
                def save_yaml(
         | 
| 255 | 
            +
                    self,
         | 
| 256 | 
            +
                    config: Dict[str, Any],
         | 
| 257 | 
            +
                    file_path: Union[str, Path],
         | 
| 258 | 
            +
                    default_flow_style: bool = False,
         | 
| 259 | 
            +
                    sort_keys: bool = True,
         | 
| 260 | 
            +
                ):
         | 
| 246 261 | 
             
                    """Save configuration as YAML.
         | 
| 247 | 
            -
             | 
| 262 | 
            +
             | 
| 248 263 | 
             
                    Args:
         | 
| 249 264 | 
             
                        config: Configuration dictionary
         | 
| 250 265 | 
             
                        file_path: Path to save YAML file
         | 
| 251 266 | 
             
                        default_flow_style: Use flow style (default: False)
         | 
| 252 267 | 
             
                        sort_keys: Whether to sort keys (default: True)
         | 
| 253 | 
            -
             | 
| 268 | 
            +
             | 
| 254 269 | 
             
                    Raises:
         | 
| 255 270 | 
             
                        ImportError: If PyYAML is not installed
         | 
| 256 271 | 
             
                    """
         | 
| 257 272 | 
             
                    if not HAS_YAML:
         | 
| 258 | 
            -
                        raise ImportError( | 
| 259 | 
            -
             | 
| 273 | 
            +
                        raise ImportError(
         | 
| 274 | 
            +
                            "PyYAML is required for YAML support. Install with: pip install pyyaml"
         | 
| 275 | 
            +
                        )
         | 
| 276 | 
            +
             | 
| 260 277 | 
             
                    file_path = Path(file_path)
         | 
| 261 278 | 
             
                    logger.debug(f"Saving YAML configuration to {file_path}")
         | 
| 262 | 
            -
             | 
| 279 | 
            +
             | 
| 263 280 | 
             
                    try:
         | 
| 264 281 | 
             
                        # Create parent directories if needed
         | 
| 265 282 | 
             
                        file_path.parent.mkdir(parents=True, exist_ok=True)
         | 
| 266 | 
            -
             | 
| 267 | 
            -
                        with open(file_path,  | 
| 268 | 
            -
                            yaml.dump( | 
| 269 | 
            -
             | 
| 283 | 
            +
             | 
| 284 | 
            +
                        with open(file_path, "w", encoding="utf-8") as f:
         | 
| 285 | 
            +
                            yaml.dump(
         | 
| 286 | 
            +
                                config,
         | 
| 287 | 
            +
                                f,
         | 
| 288 | 
            +
                                default_flow_style=default_flow_style,
         | 
| 289 | 
            +
                                sort_keys=sort_keys,
         | 
| 290 | 
            +
                            )
         | 
| 270 291 | 
             
                        logger.info(f"Configuration saved to {file_path}")
         | 
| 271 292 | 
             
                    except Exception as e:
         | 
| 272 293 | 
             
                        logger.error(f"Error saving YAML to {file_path}: {e}")
         | 
| 273 294 | 
             
                        raise
         | 
| 274 | 
            -
             | 
| 295 | 
            +
             | 
| 275 296 | 
             
                def save_toml(self, config: Dict[str, Any], file_path: Union[str, Path]):
         | 
| 276 297 | 
             
                    """Save configuration as TOML.
         | 
| 277 | 
            -
             | 
| 298 | 
            +
             | 
| 278 299 | 
             
                    Args:
         | 
| 279 300 | 
             
                        config: Configuration dictionary
         | 
| 280 301 | 
             
                        file_path: Path to save TOML file
         | 
| 281 | 
            -
             | 
| 302 | 
            +
             | 
| 282 303 | 
             
                    Raises:
         | 
| 283 304 | 
             
                        ImportError: If toml is not installed
         | 
| 284 305 | 
             
                    """
         | 
| 285 306 | 
             
                    if not HAS_TOML:
         | 
| 286 | 
            -
                        raise ImportError( | 
| 287 | 
            -
             | 
| 307 | 
            +
                        raise ImportError(
         | 
| 308 | 
            +
                            "toml is required for TOML support. Install with: pip install toml"
         | 
| 309 | 
            +
                        )
         | 
| 310 | 
            +
             | 
| 288 311 | 
             
                    file_path = Path(file_path)
         | 
| 289 312 | 
             
                    logger.debug(f"Saving TOML configuration to {file_path}")
         | 
| 290 | 
            -
             | 
| 313 | 
            +
             | 
| 291 314 | 
             
                    try:
         | 
| 292 315 | 
             
                        # Create parent directories if needed
         | 
| 293 316 | 
             
                        file_path.parent.mkdir(parents=True, exist_ok=True)
         | 
| 294 | 
            -
             | 
| 295 | 
            -
                        with open(file_path,  | 
| 317 | 
            +
             | 
| 318 | 
            +
                        with open(file_path, "w", encoding="utf-8") as f:
         | 
| 296 319 | 
             
                            toml.dump(config, f)
         | 
| 297 320 | 
             
                        logger.info(f"Configuration saved to {file_path}")
         | 
| 298 321 | 
             
                    except Exception as e:
         | 
| 299 322 | 
             
                        logger.error(f"Error saving TOML to {file_path}: {e}")
         | 
| 300 323 | 
             
                        raise
         | 
| 301 | 
            -
             | 
| 324 | 
            +
             | 
| 302 325 | 
             
                def merge_configs(self, *configs: Dict[str, Any]) -> Dict[str, Any]:
         | 
| 303 326 | 
             
                    """Merge multiple configurations.
         | 
| 304 | 
            -
             | 
| 327 | 
            +
             | 
| 305 328 | 
             
                    Later configurations override earlier ones. Nested dictionaries
         | 
| 306 329 | 
             
                    are merged recursively.
         | 
| 307 | 
            -
             | 
| 330 | 
            +
             | 
| 308 331 | 
             
                    Args:
         | 
| 309 332 | 
             
                        *configs: Configuration dictionaries to merge
         | 
| 310 | 
            -
             | 
| 333 | 
            +
             | 
| 311 334 | 
             
                    Returns:
         | 
| 312 335 | 
             
                        Merged configuration dictionary
         | 
| 313 336 | 
             
                    """
         | 
| 314 337 | 
             
                    result = {}
         | 
| 315 | 
            -
             | 
| 338 | 
            +
             | 
| 316 339 | 
             
                    for config in configs:
         | 
| 317 340 | 
             
                        self._deep_merge(result, config)
         | 
| 318 | 
            -
             | 
| 341 | 
            +
             | 
| 319 342 | 
             
                    return result
         | 
| 320 | 
            -
             | 
| 343 | 
            +
             | 
| 321 344 | 
             
                def _deep_merge(self, target: Dict[str, Any], source: Dict[str, Any]):
         | 
| 322 345 | 
             
                    """Deep merge source into target dictionary."""
         | 
| 323 346 | 
             
                    for key, value in source.items():
         | 
| 324 | 
            -
                        if  | 
| 347 | 
            +
                        if (
         | 
| 348 | 
            +
                            key in target
         | 
| 349 | 
            +
                            and isinstance(target[key], dict)
         | 
| 350 | 
            +
                            and isinstance(value, dict)
         | 
| 351 | 
            +
                        ):
         | 
| 325 352 | 
             
                            self._deep_merge(target[key], value)
         | 
| 326 353 | 
             
                        else:
         | 
| 327 354 | 
             
                            target[key] = value
         | 
| 328 | 
            -
             | 
| 329 | 
            -
                def validate_schema( | 
| 355 | 
            +
             | 
| 356 | 
            +
                def validate_schema(
         | 
| 357 | 
            +
                    self, config: Dict[str, Any], schema: Dict[str, Any]
         | 
| 358 | 
            +
                ) -> List[str]:
         | 
| 330 359 | 
             
                    """Validate configuration against a schema.
         | 
| 331 | 
            -
             | 
| 360 | 
            +
             | 
| 332 361 | 
             
                    Args:
         | 
| 333 362 | 
             
                        config: Configuration to validate
         | 
| 334 363 | 
             
                        schema: Schema dictionary defining required fields and types
         | 
| 335 | 
            -
             | 
| 364 | 
            +
             | 
| 336 365 | 
             
                    Returns:
         | 
| 337 366 | 
             
                        List of validation errors (empty if valid)
         | 
| 338 | 
            -
             | 
| 367 | 
            +
             | 
| 339 368 | 
             
                    Example schema:
         | 
| 340 369 | 
             
                        {
         | 
| 341 370 | 
             
                            "required": ["field1", "field2"],
         | 
| @@ -347,13 +376,13 @@ class ConfigurationManager: | |
| 347 376 | 
             
                        }
         | 
| 348 377 | 
             
                    """
         | 
| 349 378 | 
             
                    errors = []
         | 
| 350 | 
            -
             | 
| 379 | 
            +
             | 
| 351 380 | 
             
                    # Check required fields
         | 
| 352 381 | 
             
                    required = schema.get("required", [])
         | 
| 353 382 | 
             
                    for field in required:
         | 
| 354 383 | 
             
                        if field not in config:
         | 
| 355 384 | 
             
                            errors.append(f"Required field missing: {field}")
         | 
| 356 | 
            -
             | 
| 385 | 
            +
             | 
| 357 386 | 
             
                    # Check types
         | 
| 358 387 | 
             
                    types = schema.get("types", {})
         | 
| 359 388 | 
             
                    for field, expected_type in types.items():
         | 
| @@ -361,68 +390,76 @@ class ConfigurationManager: | |
| 361 390 | 
             
                            if not isinstance(config[field], expected_type):
         | 
| 362 391 | 
             
                                actual_type = type(config[field]).__name__
         | 
| 363 392 | 
             
                                expected_name = expected_type.__name__
         | 
| 364 | 
            -
                                errors.append( | 
| 365 | 
            -
             | 
| 393 | 
            +
                                errors.append(
         | 
| 394 | 
            +
                                    f"Invalid type for {field}: expected {expected_name}, got {actual_type}"
         | 
| 395 | 
            +
                                )
         | 
| 396 | 
            +
             | 
| 366 397 | 
             
                    return errors
         | 
| 367 | 
            -
             | 
| 368 | 
            -
                def get_with_default( | 
| 369 | 
            -
             | 
| 398 | 
            +
             | 
| 399 | 
            +
                def get_with_default(
         | 
| 400 | 
            +
                    self,
         | 
| 401 | 
            +
                    config: Dict[str, Any],
         | 
| 402 | 
            +
                    key: str,
         | 
| 403 | 
            +
                    default: Any = None,
         | 
| 404 | 
            +
                    separator: str = ".",
         | 
| 405 | 
            +
                ) -> Any:
         | 
| 370 406 | 
             
                    """Get configuration value with default fallback.
         | 
| 371 | 
            -
             | 
| 407 | 
            +
             | 
| 372 408 | 
             
                    Supports nested keys using dot notation.
         | 
| 373 | 
            -
             | 
| 409 | 
            +
             | 
| 374 410 | 
             
                    Args:
         | 
| 375 411 | 
             
                        config: Configuration dictionary
         | 
| 376 412 | 
             
                        key: Key to retrieve (supports dot notation for nested keys)
         | 
| 377 413 | 
             
                        default: Default value if key not found
         | 
| 378 414 | 
             
                        separator: Key separator for nested access (default: ".")
         | 
| 379 | 
            -
             | 
| 415 | 
            +
             | 
| 380 416 | 
             
                    Returns:
         | 
| 381 417 | 
             
                        Configuration value or default
         | 
| 382 | 
            -
             | 
| 418 | 
            +
             | 
| 383 419 | 
             
                    Example:
         | 
| 384 420 | 
             
                        get_with_default(config, "database.host", "localhost")
         | 
| 385 421 | 
             
                    """
         | 
| 386 422 | 
             
                    keys = key.split(separator)
         | 
| 387 423 | 
             
                    value = config
         | 
| 388 | 
            -
             | 
| 424 | 
            +
             | 
| 389 425 | 
             
                    for k in keys:
         | 
| 390 426 | 
             
                        if isinstance(value, dict) and k in value:
         | 
| 391 427 | 
             
                            value = value[k]
         | 
| 392 428 | 
             
                        else:
         | 
| 393 429 | 
             
                            return default
         | 
| 394 | 
            -
             | 
| 430 | 
            +
             | 
| 395 431 | 
             
                    return value
         | 
| 396 | 
            -
             | 
| 397 | 
            -
                def interpolate_env( | 
| 398 | 
            -
             | 
| 432 | 
            +
             | 
| 433 | 
            +
                def interpolate_env(
         | 
| 434 | 
            +
                    self, config: Dict[str, Any], pattern: str = "${%s}"
         | 
| 435 | 
            +
                ) -> Dict[str, Any]:
         | 
| 399 436 | 
             
                    """Interpolate environment variables in configuration.
         | 
| 400 | 
            -
             | 
| 437 | 
            +
             | 
| 401 438 | 
             
                    Args:
         | 
| 402 439 | 
             
                        config: Configuration dictionary
         | 
| 403 440 | 
             
                        pattern: Pattern for environment variables (default: "${VAR}")
         | 
| 404 | 
            -
             | 
| 441 | 
            +
             | 
| 405 442 | 
             
                    Returns:
         | 
| 406 443 | 
             
                        Configuration with environment variables interpolated
         | 
| 407 | 
            -
             | 
| 444 | 
            +
             | 
| 408 445 | 
             
                    Example:
         | 
| 409 446 | 
             
                        Input: {"host": "${DB_HOST}", "port": "${DB_PORT}"}
         | 
| 410 447 | 
             
                        Output: {"host": "localhost", "port": "5432"}
         | 
| 411 448 | 
             
                    """
         | 
| 412 449 | 
             
                    import re
         | 
| 413 | 
            -
             | 
| 450 | 
            +
             | 
| 414 451 | 
             
                    def _interpolate_value(value: Any) -> Any:
         | 
| 415 452 | 
             
                        if isinstance(value, str):
         | 
| 416 453 | 
             
                            # Find all environment variable references
         | 
| 417 454 | 
             
                            env_pattern = pattern.replace("%s", r"([A-Z_][A-Z0-9_]*)")
         | 
| 418 455 | 
             
                            matches = re.findall(env_pattern, value)
         | 
| 419 | 
            -
             | 
| 456 | 
            +
             | 
| 420 457 | 
             
                            result = value
         | 
| 421 458 | 
             
                            for var_name in matches:
         | 
| 422 459 | 
             
                                env_value = os.environ.get(var_name, "")
         | 
| 423 460 | 
             
                                placeholder = pattern % var_name
         | 
| 424 461 | 
             
                                result = result.replace(placeholder, env_value)
         | 
| 425 | 
            -
             | 
| 462 | 
            +
             | 
| 426 463 | 
             
                            return result
         | 
| 427 464 | 
             
                        elif isinstance(value, dict):
         | 
| 428 465 | 
             
                            return {k: _interpolate_value(v) for k, v in value.items()}
         | 
| @@ -430,39 +467,41 @@ class ConfigurationManager: | |
| 430 467 | 
             
                            return [_interpolate_value(item) for item in value]
         | 
| 431 468 | 
             
                        else:
         | 
| 432 469 | 
             
                            return value
         | 
| 433 | 
            -
             | 
| 470 | 
            +
             | 
| 434 471 | 
             
                    return _interpolate_value(config)
         | 
| 435 472 |  | 
| 436 473 |  | 
| 437 474 | 
             
            # Convenience functions
         | 
| 438 475 | 
             
            _default_manager = ConfigurationManager()
         | 
| 439 476 |  | 
| 477 | 
            +
             | 
| 440 478 | 
             
            def load_config(file_path: Union[str, Path]) -> Dict[str, Any]:
         | 
| 441 479 | 
             
                """Load configuration file with auto-detection.
         | 
| 442 | 
            -
             | 
| 480 | 
            +
             | 
| 443 481 | 
             
                Args:
         | 
| 444 482 | 
             
                    file_path: Path to configuration file
         | 
| 445 | 
            -
             | 
| 483 | 
            +
             | 
| 446 484 | 
             
                Returns:
         | 
| 447 485 | 
             
                    Configuration dictionary
         | 
| 448 486 | 
             
                """
         | 
| 449 487 | 
             
                return _default_manager.load_auto(file_path)
         | 
| 450 | 
            -
             | 
| 488 | 
            +
             | 
| 489 | 
            +
             | 
| 451 490 | 
             
            def save_config(config: Dict[str, Any], file_path: Union[str, Path]):
         | 
| 452 491 | 
             
                """Save configuration file with auto-detection.
         | 
| 453 | 
            -
             | 
| 492 | 
            +
             | 
| 454 493 | 
             
                Args:
         | 
| 455 494 | 
             
                    config: Configuration dictionary
         | 
| 456 495 | 
             
                    file_path: Path to save configuration
         | 
| 457 496 | 
             
                """
         | 
| 458 497 | 
             
                file_path = Path(file_path)
         | 
| 459 498 | 
             
                suffix = file_path.suffix.lower()
         | 
| 460 | 
            -
             | 
| 461 | 
            -
                if suffix in [ | 
| 499 | 
            +
             | 
| 500 | 
            +
                if suffix in [".json"]:
         | 
| 462 501 | 
             
                    _default_manager.save_json(config, file_path)
         | 
| 463 | 
            -
                elif suffix in [ | 
| 502 | 
            +
                elif suffix in [".yaml", ".yml"]:
         | 
| 464 503 | 
             
                    _default_manager.save_yaml(config, file_path)
         | 
| 465 | 
            -
                elif suffix in [ | 
| 504 | 
            +
                elif suffix in [".toml"]:
         | 
| 466 505 | 
             
                    _default_manager.save_toml(config, file_path)
         | 
| 467 506 | 
             
                else:
         | 
| 468 | 
            -
                    raise ValueError(f"Unsupported configuration format: {suffix}")
         | 
| 507 | 
            +
                    raise ValueError(f"Unsupported configuration format: {suffix}")
         | 
    
        claude_mpm/utils/console.py
    CHANGED