claude-mpm 4.1.1__py3-none-any.whl → 4.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +1 -1
- claude_mpm/agents/BASE_PM.md +74 -46
- claude_mpm/agents/INSTRUCTIONS.md +11 -153
- claude_mpm/agents/WORKFLOW.md +61 -321
- claude_mpm/agents/__init__.py +11 -11
- claude_mpm/agents/agent_loader.py +23 -20
- claude_mpm/agents/agent_loader_integration.py +1 -1
- claude_mpm/agents/agents_metadata.py +27 -0
- claude_mpm/agents/async_agent_loader.py +5 -8
- claude_mpm/agents/base_agent_loader.py +36 -25
- claude_mpm/agents/frontmatter_validator.py +6 -6
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +9 -9
- claude_mpm/agents/templates/api_qa.json +47 -2
- claude_mpm/agents/templates/imagemagick.json +256 -0
- claude_mpm/agents/templates/qa.json +41 -2
- claude_mpm/agents/templates/ticketing.json +5 -5
- claude_mpm/agents/templates/web_qa.json +50 -2
- claude_mpm/cli/__init__.py +51 -46
- claude_mpm/cli/__main__.py +1 -1
- claude_mpm/cli/commands/__init__.py +10 -12
- claude_mpm/cli/commands/agent_manager.py +186 -181
- claude_mpm/cli/commands/agents.py +271 -268
- claude_mpm/cli/commands/aggregate.py +30 -29
- claude_mpm/cli/commands/cleanup.py +50 -44
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
- claude_mpm/cli/commands/config.py +162 -127
- claude_mpm/cli/commands/doctor.py +52 -62
- claude_mpm/cli/commands/info.py +37 -25
- claude_mpm/cli/commands/mcp.py +3 -7
- claude_mpm/cli/commands/mcp_command_router.py +14 -18
- claude_mpm/cli/commands/mcp_install_commands.py +28 -23
- claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
- claude_mpm/cli/commands/mcp_server_commands.py +23 -17
- claude_mpm/cli/commands/memory.py +192 -141
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +120 -84
- claude_mpm/cli/commands/run_config_checker.py +4 -5
- claude_mpm/cli/commands/socketio_monitor.py +17 -19
- claude_mpm/cli/commands/tickets.py +92 -92
- claude_mpm/cli/parser.py +1 -5
- claude_mpm/cli/parsers/__init__.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
- claude_mpm/cli/parsers/agents_parser.py +2 -3
- claude_mpm/cli/parsers/base_parser.py +7 -5
- claude_mpm/cli/parsers/mcp_parser.py +4 -2
- claude_mpm/cli/parsers/monitor_parser.py +26 -18
- claude_mpm/cli/shared/__init__.py +10 -10
- claude_mpm/cli/shared/argument_patterns.py +57 -71
- claude_mpm/cli/shared/base_command.py +61 -53
- claude_mpm/cli/shared/error_handling.py +62 -58
- claude_mpm/cli/shared/output_formatters.py +78 -77
- claude_mpm/cli/startup_logging.py +204 -172
- claude_mpm/cli/utils.py +10 -11
- claude_mpm/cli_module/__init__.py +1 -1
- claude_mpm/cli_module/args.py +1 -1
- claude_mpm/cli_module/migration_example.py +5 -5
- claude_mpm/config/__init__.py +9 -9
- claude_mpm/config/agent_config.py +15 -14
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/paths.py +0 -1
- claude_mpm/config/socketio_config.py +5 -6
- claude_mpm/constants.py +1 -2
- claude_mpm/core/__init__.py +8 -8
- claude_mpm/core/agent_name_normalizer.py +1 -1
- claude_mpm/core/agent_registry.py +20 -23
- claude_mpm/core/agent_session_manager.py +3 -3
- claude_mpm/core/base_service.py +7 -15
- claude_mpm/core/cache.py +4 -6
- claude_mpm/core/claude_runner.py +85 -113
- claude_mpm/core/config.py +43 -28
- claude_mpm/core/config_aliases.py +0 -9
- claude_mpm/core/config_constants.py +52 -30
- claude_mpm/core/constants.py +0 -1
- claude_mpm/core/container.py +18 -27
- claude_mpm/core/exceptions.py +2 -2
- claude_mpm/core/factories.py +10 -12
- claude_mpm/core/framework_loader.py +581 -280
- claude_mpm/core/hook_manager.py +26 -22
- claude_mpm/core/hook_performance_config.py +58 -47
- claude_mpm/core/injectable_service.py +1 -1
- claude_mpm/core/interactive_session.py +61 -152
- claude_mpm/core/interfaces.py +1 -100
- claude_mpm/core/lazy.py +5 -5
- claude_mpm/core/log_manager.py +587 -0
- claude_mpm/core/logger.py +125 -8
- claude_mpm/core/logging_config.py +15 -15
- claude_mpm/core/minimal_framework_loader.py +5 -8
- claude_mpm/core/oneshot_session.py +15 -33
- claude_mpm/core/optimized_agent_loader.py +4 -6
- claude_mpm/core/optimized_startup.py +2 -1
- claude_mpm/core/output_style_manager.py +147 -106
- claude_mpm/core/pm_hook_interceptor.py +0 -1
- claude_mpm/core/service_registry.py +11 -8
- claude_mpm/core/session_manager.py +1 -2
- claude_mpm/core/shared/__init__.py +1 -1
- claude_mpm/core/shared/config_loader.py +101 -97
- claude_mpm/core/shared/path_resolver.py +72 -68
- claude_mpm/core/shared/singleton_manager.py +56 -50
- claude_mpm/core/socketio_pool.py +26 -6
- claude_mpm/core/tool_access_control.py +4 -5
- claude_mpm/core/typing_utils.py +50 -59
- claude_mpm/core/unified_agent_registry.py +14 -19
- claude_mpm/core/unified_config.py +4 -6
- claude_mpm/core/unified_paths.py +197 -109
- claude_mpm/dashboard/open_dashboard.py +2 -4
- claude_mpm/experimental/cli_enhancements.py +51 -36
- claude_mpm/generators/agent_profile_generator.py +2 -4
- claude_mpm/hooks/base_hook.py +1 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
- claude_mpm/hooks/claude_hooks/event_handlers.py +93 -38
- claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
- claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
- claude_mpm/hooks/memory_integration_hook.py +5 -5
- claude_mpm/hooks/tool_call_interceptor.py +1 -1
- claude_mpm/hooks/validation_hooks.py +4 -4
- claude_mpm/init.py +4 -9
- claude_mpm/models/__init__.py +2 -2
- claude_mpm/models/agent_session.py +11 -14
- claude_mpm/scripts/mcp_server.py +20 -11
- claude_mpm/scripts/mcp_wrapper.py +5 -5
- claude_mpm/scripts/mpm_doctor.py +321 -0
- claude_mpm/scripts/socketio_daemon.py +28 -25
- claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
- claude_mpm/scripts/socketio_server_manager.py +116 -95
- claude_mpm/services/__init__.py +49 -49
- claude_mpm/services/agent_capabilities_service.py +12 -18
- claude_mpm/services/agents/__init__.py +22 -22
- claude_mpm/services/agents/agent_builder.py +140 -119
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
- claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
- claude_mpm/services/agents/deployment/agent_deployment.py +136 -106
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
- claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
- claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
- claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
- claude_mpm/services/agents/deployment/agent_validator.py +0 -1
- claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
- claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
- claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
- claude_mpm/services/agents/deployment/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
- claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
- claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
- claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
- claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
- claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
- claude_mpm/services/agents/deployment/results/__init__.py +1 -1
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
- claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
- claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
- claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
- claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
- claude_mpm/services/agents/loading/__init__.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
- claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
- claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
- claude_mpm/services/agents/management/__init__.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
- claude_mpm/services/agents/management/agent_management_service.py +5 -9
- claude_mpm/services/agents/memory/__init__.py +4 -4
- claude_mpm/services/agents/memory/agent_memory_manager.py +280 -160
- claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
- claude_mpm/services/agents/memory/content_manager.py +44 -38
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +10 -6
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
- claude_mpm/services/agents/registry/modification_tracker.py +3 -6
- claude_mpm/services/async_session_logger.py +1 -2
- claude_mpm/services/claude_session_logger.py +1 -2
- claude_mpm/services/command_deployment_service.py +173 -0
- claude_mpm/services/command_handler_service.py +20 -22
- claude_mpm/services/core/__init__.py +25 -25
- claude_mpm/services/core/base.py +0 -5
- claude_mpm/services/core/interfaces/__init__.py +32 -32
- claude_mpm/services/core/interfaces/agent.py +0 -21
- claude_mpm/services/core/interfaces/communication.py +0 -27
- claude_mpm/services/core/interfaces/infrastructure.py +0 -56
- claude_mpm/services/core/interfaces/service.py +0 -29
- claude_mpm/services/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
- claude_mpm/services/diagnostics/checks/base_check.py +12 -16
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
- claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
- claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
- claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
- claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
- claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
- claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
- claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
- claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
- claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
- claude_mpm/services/diagnostics/models.py +21 -19
- claude_mpm/services/event_aggregator.py +10 -17
- claude_mpm/services/event_bus/__init__.py +1 -1
- claude_mpm/services/event_bus/config.py +54 -35
- claude_mpm/services/event_bus/event_bus.py +76 -71
- claude_mpm/services/event_bus/relay.py +74 -64
- claude_mpm/services/events/__init__.py +11 -11
- claude_mpm/services/events/consumers/__init__.py +3 -3
- claude_mpm/services/events/consumers/dead_letter.py +71 -63
- claude_mpm/services/events/consumers/logging.py +39 -37
- claude_mpm/services/events/consumers/metrics.py +56 -57
- claude_mpm/services/events/consumers/socketio.py +82 -81
- claude_mpm/services/events/core.py +110 -99
- claude_mpm/services/events/interfaces.py +56 -72
- claude_mpm/services/events/producers/__init__.py +1 -1
- claude_mpm/services/events/producers/hook.py +38 -38
- claude_mpm/services/events/producers/system.py +46 -44
- claude_mpm/services/exceptions.py +81 -80
- claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
- claude_mpm/services/hook_service.py +6 -9
- claude_mpm/services/infrastructure/__init__.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +8 -12
- claude_mpm/services/infrastructure/monitoring.py +21 -23
- claude_mpm/services/mcp_gateway/__init__.py +37 -37
- claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
- claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
- claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
- claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
- claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
- claude_mpm/services/mcp_gateway/core/base.py +0 -3
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
- claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
- claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
- claude_mpm/services/mcp_gateway/main.py +2 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
- claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
- claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
- claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
- claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
- claude_mpm/services/memory/__init__.py +3 -3
- claude_mpm/services/memory/builder.py +3 -6
- claude_mpm/services/memory/cache/__init__.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/memory/indexed_memory.py +5 -7
- claude_mpm/services/memory/optimizer.py +7 -10
- claude_mpm/services/memory/router.py +8 -9
- claude_mpm/services/memory_hook_service.py +48 -34
- claude_mpm/services/monitor_build_service.py +77 -73
- claude_mpm/services/port_manager.py +130 -108
- claude_mpm/services/project/analyzer.py +12 -10
- claude_mpm/services/project/registry.py +11 -11
- claude_mpm/services/recovery_manager.py +10 -19
- claude_mpm/services/response_tracker.py +0 -1
- claude_mpm/services/runner_configuration_service.py +19 -20
- claude_mpm/services/session_management_service.py +7 -11
- claude_mpm/services/shared/__init__.py +1 -1
- claude_mpm/services/shared/async_service_base.py +58 -50
- claude_mpm/services/shared/config_service_base.py +73 -67
- claude_mpm/services/shared/lifecycle_service_base.py +82 -78
- claude_mpm/services/shared/manager_base.py +94 -82
- claude_mpm/services/shared/service_factory.py +96 -98
- claude_mpm/services/socketio/__init__.py +3 -3
- claude_mpm/services/socketio/client_proxy.py +5 -5
- claude_mpm/services/socketio/event_normalizer.py +199 -181
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +5 -4
- claude_mpm/services/socketio/handlers/connection.py +163 -136
- claude_mpm/services/socketio/handlers/file.py +13 -14
- claude_mpm/services/socketio/handlers/git.py +12 -7
- claude_mpm/services/socketio/handlers/hook.py +49 -44
- claude_mpm/services/socketio/handlers/memory.py +0 -1
- claude_mpm/services/socketio/handlers/project.py +0 -1
- claude_mpm/services/socketio/handlers/registry.py +37 -19
- claude_mpm/services/socketio/migration_utils.py +98 -84
- claude_mpm/services/socketio/server/__init__.py +1 -1
- claude_mpm/services/socketio/server/broadcaster.py +81 -87
- claude_mpm/services/socketio/server/core.py +65 -54
- claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
- claude_mpm/services/socketio/server/main.py +64 -38
- claude_mpm/services/socketio_client_manager.py +10 -12
- claude_mpm/services/subprocess_launcher_service.py +4 -7
- claude_mpm/services/system_instructions_service.py +13 -14
- claude_mpm/services/ticket_manager.py +2 -2
- claude_mpm/services/utility_service.py +5 -13
- claude_mpm/services/version_control/__init__.py +16 -16
- claude_mpm/services/version_control/branch_strategy.py +5 -8
- claude_mpm/services/version_control/conflict_resolution.py +9 -23
- claude_mpm/services/version_control/git_operations.py +5 -7
- claude_mpm/services/version_control/semantic_versioning.py +16 -17
- claude_mpm/services/version_control/version_parser.py +13 -18
- claude_mpm/services/version_service.py +10 -11
- claude_mpm/storage/__init__.py +1 -1
- claude_mpm/storage/state_storage.py +22 -28
- claude_mpm/utils/__init__.py +6 -6
- claude_mpm/utils/agent_dependency_loader.py +47 -33
- claude_mpm/utils/config_manager.py +11 -14
- claude_mpm/utils/dependency_cache.py +1 -1
- claude_mpm/utils/dependency_manager.py +13 -17
- claude_mpm/utils/dependency_strategies.py +8 -10
- claude_mpm/utils/environment_context.py +3 -9
- claude_mpm/utils/error_handler.py +3 -13
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/path_operations.py +8 -12
- claude_mpm/utils/robust_installer.py +110 -33
- claude_mpm/utils/subprocess_utils.py +5 -6
- claude_mpm/validation/agent_validator.py +3 -6
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/METADATA +1 -1
- claude_mpm-4.1.2.dist-info/RECORD +498 -0
- claude_mpm-4.1.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
|
@@ -65,12 +65,10 @@ class AgentPersistenceService:
|
|
|
65
65
|
async def start(self) -> None:
|
|
66
66
|
"""Start the persistence service."""
|
|
67
67
|
# No-op for stub
|
|
68
|
-
pass
|
|
69
68
|
|
|
70
69
|
async def stop(self) -> None:
|
|
71
70
|
"""Stop the persistence service."""
|
|
72
71
|
# No-op for stub
|
|
73
|
-
pass
|
|
74
72
|
|
|
75
73
|
async def persist_agent(
|
|
76
74
|
self,
|
|
@@ -67,7 +67,7 @@ class MemoryContentManager:
|
|
|
67
67
|
item_indices.append(i)
|
|
68
68
|
existing_item = line.strip()[2:] # Remove "- " prefix
|
|
69
69
|
similarity = self._calculate_similarity(existing_item, new_item)
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
# If highly similar (>80%), mark for removal
|
|
72
72
|
if similarity > 0.8:
|
|
73
73
|
items_to_remove.append(i)
|
|
@@ -99,7 +99,7 @@ class MemoryContentManager:
|
|
|
99
99
|
if lines[i].strip():
|
|
100
100
|
insert_point = i + 1
|
|
101
101
|
break
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
lines.insert(insert_point, f"- {new_item}")
|
|
104
104
|
|
|
105
105
|
# Update timestamp
|
|
@@ -108,12 +108,12 @@ class MemoryContentManager:
|
|
|
108
108
|
|
|
109
109
|
def add_item_to_section(self, content: str, section: str, new_item: str) -> str:
|
|
110
110
|
"""Legacy method for backward compatibility - delegates to add_item_to_list.
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
Args:
|
|
113
113
|
content: Current memory file content
|
|
114
114
|
section: Section name (ignored in simple list format)
|
|
115
115
|
new_item: Item to add
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
Returns:
|
|
118
118
|
str: Updated content with new item added
|
|
119
119
|
"""
|
|
@@ -172,12 +172,12 @@ class MemoryContentManager:
|
|
|
172
172
|
# Also check max_items limit
|
|
173
173
|
max_items = limits.get("max_items", 100)
|
|
174
174
|
item_count = sum(1 for line in lines if line.strip().startswith("- "))
|
|
175
|
-
|
|
175
|
+
|
|
176
176
|
if item_count > max_items:
|
|
177
177
|
# Remove oldest items to fit within max_items
|
|
178
178
|
items_removed = 0
|
|
179
179
|
target_removals = item_count - max_items
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
i = 0
|
|
182
182
|
while i < len(lines) and items_removed < target_removals:
|
|
183
183
|
if lines[i].strip().startswith("- "):
|
|
@@ -187,7 +187,7 @@ class MemoryContentManager:
|
|
|
187
187
|
i += 1
|
|
188
188
|
|
|
189
189
|
return "\n".join(lines)
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
def truncate_to_limits(
|
|
192
192
|
self, content: str, agent_limits: Optional[Dict[str, Any]] = None
|
|
193
193
|
) -> str:
|
|
@@ -211,12 +211,11 @@ class MemoryContentManager:
|
|
|
211
211
|
content,
|
|
212
212
|
)
|
|
213
213
|
# Also handle legacy format
|
|
214
|
-
|
|
214
|
+
return re.sub(
|
|
215
215
|
r"<!-- Last Updated: .+ \| Auto-updated by: .+ -->",
|
|
216
216
|
f"<!-- Last Updated: {timestamp} -->",
|
|
217
217
|
content,
|
|
218
218
|
)
|
|
219
|
-
return content
|
|
220
219
|
|
|
221
220
|
def validate_and_repair(self, content: str, agent_id: str) -> str:
|
|
222
221
|
"""Validate memory file and repair if needed.
|
|
@@ -232,22 +231,23 @@ class MemoryContentManager:
|
|
|
232
231
|
str: Validated and repaired content
|
|
233
232
|
"""
|
|
234
233
|
lines = content.split("\n")
|
|
235
|
-
|
|
234
|
+
|
|
236
235
|
# Ensure proper header format
|
|
237
236
|
has_header = False
|
|
238
237
|
has_timestamp = False
|
|
239
|
-
|
|
240
|
-
for
|
|
238
|
+
|
|
239
|
+
for _i, line in enumerate(lines[:5]): # Check first 5 lines
|
|
241
240
|
if line.startswith("# Agent Memory:"):
|
|
242
241
|
has_header = True
|
|
243
242
|
elif line.startswith("<!-- Last Updated:"):
|
|
244
243
|
has_timestamp = True
|
|
245
|
-
|
|
244
|
+
|
|
246
245
|
# Add missing header or timestamp
|
|
247
246
|
if not has_header or not has_timestamp:
|
|
248
247
|
from datetime import datetime
|
|
248
|
+
|
|
249
249
|
new_lines = []
|
|
250
|
-
|
|
250
|
+
|
|
251
251
|
if not has_header:
|
|
252
252
|
new_lines.append(f"# Agent Memory: {agent_id}")
|
|
253
253
|
else:
|
|
@@ -257,9 +257,11 @@ class MemoryContentManager:
|
|
|
257
257
|
new_lines.append(line)
|
|
258
258
|
lines.remove(line)
|
|
259
259
|
break
|
|
260
|
-
|
|
260
|
+
|
|
261
261
|
if not has_timestamp:
|
|
262
|
-
new_lines.append(
|
|
262
|
+
new_lines.append(
|
|
263
|
+
f"<!-- Last Updated: {datetime.now().isoformat()}Z -->"
|
|
264
|
+
)
|
|
263
265
|
new_lines.append("")
|
|
264
266
|
else:
|
|
265
267
|
# Keep existing timestamp
|
|
@@ -268,14 +270,16 @@ class MemoryContentManager:
|
|
|
268
270
|
new_lines.append(line)
|
|
269
271
|
lines.remove(line)
|
|
270
272
|
break
|
|
271
|
-
|
|
273
|
+
|
|
272
274
|
# Add remaining content
|
|
273
275
|
for line in lines:
|
|
274
|
-
if not line.startswith("# ") and not line.startswith(
|
|
276
|
+
if not line.startswith("# ") and not line.startswith(
|
|
277
|
+
"<!-- Last Updated:"
|
|
278
|
+
):
|
|
275
279
|
new_lines.append(line)
|
|
276
|
-
|
|
280
|
+
|
|
277
281
|
return "\n".join(new_lines)
|
|
278
|
-
|
|
282
|
+
|
|
279
283
|
return "\n".join(lines)
|
|
280
284
|
|
|
281
285
|
def parse_memory_content_to_list(self, content: str) -> List[str]:
|
|
@@ -296,7 +300,7 @@ class MemoryContentManager:
|
|
|
296
300
|
line = line.strip()
|
|
297
301
|
|
|
298
302
|
# Skip empty lines, headers, and metadata
|
|
299
|
-
if not line or line.startswith("#"
|
|
303
|
+
if not line or line.startswith(("#", "<!--")):
|
|
300
304
|
continue
|
|
301
305
|
|
|
302
306
|
if line.startswith("- "):
|
|
@@ -306,15 +310,15 @@ class MemoryContentManager:
|
|
|
306
310
|
items.append(item)
|
|
307
311
|
|
|
308
312
|
return items
|
|
309
|
-
|
|
313
|
+
|
|
310
314
|
def parse_memory_content_to_dict(self, content: str) -> Dict[str, List[str]]:
|
|
311
315
|
"""Legacy method for backward compatibility.
|
|
312
|
-
|
|
316
|
+
|
|
313
317
|
Returns a dict with single key 'memories' containing all items.
|
|
314
|
-
|
|
318
|
+
|
|
315
319
|
Args:
|
|
316
320
|
content: Raw memory file content
|
|
317
|
-
|
|
321
|
+
|
|
318
322
|
Returns:
|
|
319
323
|
Dict with 'memories' key mapping to list of items
|
|
320
324
|
"""
|
|
@@ -344,23 +348,23 @@ class MemoryContentManager:
|
|
|
344
348
|
# Normalize strings for comparison
|
|
345
349
|
str1_normalized = str1.lower().strip()
|
|
346
350
|
str2_normalized = str2.lower().strip()
|
|
347
|
-
|
|
351
|
+
|
|
348
352
|
# Handle exact matches quickly
|
|
349
353
|
if str1_normalized == str2_normalized:
|
|
350
354
|
return 1.0
|
|
351
|
-
|
|
355
|
+
|
|
352
356
|
# Use SequenceMatcher for fuzzy matching
|
|
353
357
|
# None as first param tells it to use automatic junk heuristic
|
|
354
358
|
matcher = SequenceMatcher(None, str1_normalized, str2_normalized)
|
|
355
359
|
similarity = matcher.ratio()
|
|
356
|
-
|
|
360
|
+
|
|
357
361
|
# Additional check: if one string contains the other (substring match)
|
|
358
362
|
# This catches cases where one item is a more detailed version of another
|
|
359
363
|
if len(str1_normalized) > 20 and len(str2_normalized) > 20:
|
|
360
364
|
if str1_normalized in str2_normalized or str2_normalized in str1_normalized:
|
|
361
365
|
# Boost similarity for substring matches
|
|
362
366
|
similarity = max(similarity, 0.85)
|
|
363
|
-
|
|
367
|
+
|
|
364
368
|
return similarity
|
|
365
369
|
|
|
366
370
|
def deduplicate_list(self, content: str) -> Tuple[str, int]:
|
|
@@ -377,7 +381,7 @@ class MemoryContentManager:
|
|
|
377
381
|
Tuple of (updated content, number of items removed)
|
|
378
382
|
"""
|
|
379
383
|
lines = content.split("\n")
|
|
380
|
-
|
|
384
|
+
|
|
381
385
|
# Collect all items in the list
|
|
382
386
|
items = []
|
|
383
387
|
item_indices = []
|
|
@@ -385,7 +389,7 @@ class MemoryContentManager:
|
|
|
385
389
|
if line.strip().startswith("- "):
|
|
386
390
|
items.append(line.strip()[2:]) # Remove "- " prefix
|
|
387
391
|
item_indices.append(i)
|
|
388
|
-
|
|
392
|
+
|
|
389
393
|
# Find duplicates using pairwise comparison
|
|
390
394
|
duplicates_to_remove = set()
|
|
391
395
|
for i in range(len(items)):
|
|
@@ -403,21 +407,21 @@ class MemoryContentManager:
|
|
|
403
407
|
f"(keeping newer: '{items[j][:50]}...')"
|
|
404
408
|
)
|
|
405
409
|
break # Move to next item
|
|
406
|
-
|
|
410
|
+
|
|
407
411
|
# Remove duplicates (in reverse order to maintain indices)
|
|
408
412
|
removed_count = len(duplicates_to_remove)
|
|
409
413
|
for idx in sorted(duplicates_to_remove, reverse=True):
|
|
410
414
|
lines.pop(item_indices[idx])
|
|
411
|
-
|
|
415
|
+
|
|
412
416
|
return "\n".join(lines), removed_count
|
|
413
|
-
|
|
417
|
+
|
|
414
418
|
def deduplicate_section(self, content: str, section: str) -> Tuple[str, int]:
|
|
415
419
|
"""Legacy method for backward compatibility - delegates to deduplicate_list.
|
|
416
|
-
|
|
420
|
+
|
|
417
421
|
Args:
|
|
418
422
|
content: Current memory file content
|
|
419
423
|
section: Section name (ignored in simple list format)
|
|
420
|
-
|
|
424
|
+
|
|
421
425
|
Returns:
|
|
422
426
|
Tuple of (updated content, number of items removed)
|
|
423
427
|
"""
|
|
@@ -444,7 +448,9 @@ class MemoryContentManager:
|
|
|
444
448
|
)
|
|
445
449
|
|
|
446
450
|
# Check item count
|
|
447
|
-
items = sum(
|
|
451
|
+
items = sum(
|
|
452
|
+
1 for line in content.split("\n") if line.strip().startswith("- ")
|
|
453
|
+
)
|
|
448
454
|
max_items = self.memory_limits.get("max_items", 100)
|
|
449
455
|
|
|
450
456
|
if items > max_items:
|
|
@@ -453,4 +459,4 @@ class MemoryContentManager:
|
|
|
453
459
|
return True, None
|
|
454
460
|
|
|
455
461
|
except Exception as e:
|
|
456
|
-
return False, f"Validation error: {
|
|
462
|
+
return False, f"Validation error: {e!s}"
|
|
@@ -15,7 +15,7 @@ This module provides:
|
|
|
15
15
|
import logging
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Any, Dict
|
|
18
|
+
from typing import Any, Dict
|
|
19
19
|
|
|
20
20
|
from claude_mpm.core.config import Config
|
|
21
21
|
|
|
@@ -35,9 +35,7 @@ class MemoryTemplateGenerator:
|
|
|
35
35
|
"Current Technical Context",
|
|
36
36
|
]
|
|
37
37
|
|
|
38
|
-
def __init__(
|
|
39
|
-
self, config: Config, working_directory: Path
|
|
40
|
-
):
|
|
38
|
+
def __init__(self, config: Config, working_directory: Path):
|
|
41
39
|
"""Initialize the template generator.
|
|
42
40
|
|
|
43
41
|
Args:
|
|
@@ -59,8 +57,8 @@ class MemoryTemplateGenerator:
|
|
|
59
57
|
str: The basic memory template content
|
|
60
58
|
"""
|
|
61
59
|
# Convert agent_id to proper name, handling cases like "test_agent" -> "Test"
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
agent_id.replace("_agent", "").replace("_", " ").title()
|
|
61
|
+
datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
64
62
|
|
|
65
63
|
# Create a simple template that agents will populate through learning
|
|
66
64
|
return self._create_basic_memory_template(agent_id, limits)
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"""Agent registry services for discovery and tracking."""
|
|
2
2
|
|
|
3
|
-
from claude_mpm.core.unified_agent_registry import
|
|
3
|
+
from claude_mpm.core.unified_agent_registry import (
|
|
4
|
+
AgentMetadata,
|
|
5
|
+
AgentTier,
|
|
6
|
+
AgentType,
|
|
7
|
+
)
|
|
4
8
|
from claude_mpm.core.unified_agent_registry import UnifiedAgentRegistry as AgentRegistry
|
|
5
9
|
|
|
6
10
|
from .deployed_agent_discovery import DeployedAgentDiscovery
|
|
@@ -13,14 +17,14 @@ from .modification_tracker import (
|
|
|
13
17
|
)
|
|
14
18
|
|
|
15
19
|
__all__ = [
|
|
16
|
-
"AgentRegistry",
|
|
17
20
|
"AgentMetadata",
|
|
21
|
+
"AgentModification",
|
|
22
|
+
"AgentModificationTracker",
|
|
23
|
+
"AgentRegistry",
|
|
18
24
|
"AgentTier",
|
|
19
25
|
"AgentType",
|
|
20
26
|
"DeployedAgentDiscovery",
|
|
21
|
-
"AgentModificationTracker",
|
|
22
|
-
"ModificationType",
|
|
23
|
-
"ModificationTier",
|
|
24
|
-
"AgentModification",
|
|
25
27
|
"ModificationHistory",
|
|
28
|
+
"ModificationTier",
|
|
29
|
+
"ModificationType",
|
|
26
30
|
]
|
|
@@ -19,7 +19,11 @@ logger = logging.getLogger(__name__)
|
|
|
19
19
|
class DeployedAgentDiscovery(ConfigServiceBase):
|
|
20
20
|
"""Discovers and analyzes deployed agents in the project."""
|
|
21
21
|
|
|
22
|
-
def __init__(
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
project_root: Optional[Path] = None,
|
|
25
|
+
config: Optional[Dict[str, Any]] = None,
|
|
26
|
+
):
|
|
23
27
|
"""Initialize the discovery service.
|
|
24
28
|
|
|
25
29
|
Args:
|
|
@@ -32,7 +36,7 @@ class DeployedAgentDiscovery(ConfigServiceBase):
|
|
|
32
36
|
self.project_root = self.get_config_value(
|
|
33
37
|
"project_root",
|
|
34
38
|
default=project_root or get_path_manager().project_root,
|
|
35
|
-
config_type=Path
|
|
39
|
+
config_type=Path,
|
|
36
40
|
)
|
|
37
41
|
self.agent_registry = AgentRegistryAdapter()
|
|
38
42
|
self.logger.debug(
|
|
@@ -111,7 +115,7 @@ class DeployedAgentDiscovery(ConfigServiceBase):
|
|
|
111
115
|
"tools": agent.get("tools", []),
|
|
112
116
|
}
|
|
113
117
|
# Handle object format with metadata (new standardized schema)
|
|
114
|
-
|
|
118
|
+
if hasattr(agent, "metadata"):
|
|
115
119
|
return {
|
|
116
120
|
"id": agent.agent_id,
|
|
117
121
|
"name": agent.metadata.name,
|
|
@@ -125,28 +129,27 @@ class DeployedAgentDiscovery(ConfigServiceBase):
|
|
|
125
129
|
else []
|
|
126
130
|
),
|
|
127
131
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"description"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
132
|
+
# Legacy object format fallback
|
|
133
|
+
agent_type = getattr(agent, "type", None)
|
|
134
|
+
agent_name = getattr(agent, "name", None)
|
|
135
|
+
|
|
136
|
+
# Generate name from type if name not present
|
|
137
|
+
if not agent_name and agent_type:
|
|
138
|
+
agent_name = agent_type.replace("_", " ").title()
|
|
139
|
+
elif not agent_name:
|
|
140
|
+
agent_name = "Unknown Agent"
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
"id": getattr(agent, "agent_id", agent_type or "unknown"),
|
|
144
|
+
"name": agent_name,
|
|
145
|
+
"description": getattr(
|
|
146
|
+
agent, "description", "No description available"
|
|
147
|
+
),
|
|
148
|
+
"specializations": getattr(agent, "specializations", []),
|
|
149
|
+
"capabilities": {},
|
|
150
|
+
"source_tier": self._determine_source_tier(agent),
|
|
151
|
+
"tools": getattr(agent, "tools", []),
|
|
152
|
+
}
|
|
150
153
|
except Exception as e:
|
|
151
154
|
logger.error(f"Error extracting agent info: {e}")
|
|
152
155
|
return None
|
|
@@ -163,7 +166,7 @@ class DeployedAgentDiscovery(ConfigServiceBase):
|
|
|
163
166
|
try:
|
|
164
167
|
path = Path(agent_path)
|
|
165
168
|
if path.exists() and path.suffix == ".json":
|
|
166
|
-
with open(path
|
|
169
|
+
with open(path) as f:
|
|
167
170
|
return json.load(f)
|
|
168
171
|
except Exception as e:
|
|
169
172
|
logger.warning(f"Failed to load full agent data from {agent_path}: {e}")
|
|
@@ -223,7 +226,7 @@ class DeployedAgentDiscovery(ConfigServiceBase):
|
|
|
223
226
|
source_path = str(agent.source_path)
|
|
224
227
|
if ".claude/agents" in source_path:
|
|
225
228
|
return "project"
|
|
226
|
-
|
|
229
|
+
if str(Path.home()) in source_path:
|
|
227
230
|
return "user"
|
|
228
231
|
|
|
229
232
|
# Default to system tier
|
|
@@ -26,7 +26,6 @@ import asyncio
|
|
|
26
26
|
import hashlib
|
|
27
27
|
import json
|
|
28
28
|
import logging
|
|
29
|
-
import os
|
|
30
29
|
import shutil
|
|
31
30
|
import time
|
|
32
31
|
import uuid
|
|
@@ -42,8 +41,6 @@ from claude_mpm.core.base_service import BaseService
|
|
|
42
41
|
from claude_mpm.core.unified_agent_registry import UnifiedAgentRegistry as AgentRegistry
|
|
43
42
|
from claude_mpm.core.unified_paths import get_path_manager
|
|
44
43
|
from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCache
|
|
45
|
-
from claude_mpm.utils.config_manager import ConfigurationManager
|
|
46
|
-
from claude_mpm.utils.path_operations import path_ops
|
|
47
44
|
|
|
48
45
|
# ============================================================================
|
|
49
46
|
# Data Models
|
|
@@ -664,7 +661,7 @@ class AgentModificationTracker(BaseService):
|
|
|
664
661
|
# Load active modifications
|
|
665
662
|
active_path = self.persistence_root / "active_modifications.json"
|
|
666
663
|
if active_path.exists():
|
|
667
|
-
with open(active_path
|
|
664
|
+
with open(active_path) as f:
|
|
668
665
|
data = json.load(f)
|
|
669
666
|
self.active_modifications = {
|
|
670
667
|
k: AgentModification.from_dict(v) for k, v in data.items()
|
|
@@ -672,7 +669,7 @@ class AgentModificationTracker(BaseService):
|
|
|
672
669
|
|
|
673
670
|
# Load modification history
|
|
674
671
|
for history_file in self.history_root.glob("*.json"):
|
|
675
|
-
with open(history_file
|
|
672
|
+
with open(history_file) as f:
|
|
676
673
|
data = json.load(f)
|
|
677
674
|
agent_name = data["agent_name"]
|
|
678
675
|
history = ModificationHistory(agent_name=agent_name)
|
|
@@ -770,7 +767,7 @@ class AgentModificationTracker(BaseService):
|
|
|
770
767
|
if backup_dir.is_dir():
|
|
771
768
|
metadata_path = backup_dir / "metadata.json"
|
|
772
769
|
if metadata_path.exists():
|
|
773
|
-
with open(metadata_path
|
|
770
|
+
with open(metadata_path) as f:
|
|
774
771
|
metadata = json.load(f)
|
|
775
772
|
if metadata.get("backup_time", 0) < cutoff_time:
|
|
776
773
|
shutil.rmtree(backup_dir)
|
|
@@ -15,7 +15,6 @@ Key Features:
|
|
|
15
15
|
- Configuration via .claude-mpm/configuration.yaml
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
import asyncio
|
|
19
18
|
import json
|
|
20
19
|
import logging
|
|
21
20
|
import logging.handlers
|
|
@@ -27,7 +26,7 @@ from datetime import datetime
|
|
|
27
26
|
from enum import Enum
|
|
28
27
|
from queue import Full, Queue
|
|
29
28
|
from threading import Lock, Thread
|
|
30
|
-
from typing import Any,
|
|
29
|
+
from typing import Any, Dict, Optional
|
|
31
30
|
|
|
32
31
|
from claude_mpm.core.constants import PerformanceConfig, SystemLimits, TimeoutConfig
|
|
33
32
|
|
|
@@ -159,8 +159,7 @@ class ClaudeSessionLogger:
|
|
|
159
159
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
|
|
160
160
|
|
|
161
161
|
# Create filename: session_id-agent-timestamp.json
|
|
162
|
-
|
|
163
|
-
return filename
|
|
162
|
+
return f"{self.session_id}-{agent_name}-{timestamp}.json"
|
|
164
163
|
|
|
165
164
|
def log_response(
|
|
166
165
|
self,
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Service for deploying MPM slash commands to user's Claude configuration.
|
|
2
|
+
|
|
3
|
+
This service handles:
|
|
4
|
+
1. Copying command markdown files from source to user's ~/.claude/commands directory
|
|
5
|
+
2. Creating the commands directory if it doesn't exist
|
|
6
|
+
3. Overwriting existing commands to ensure they're up-to-date
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import shutil
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, List
|
|
12
|
+
|
|
13
|
+
from claude_mpm.core.base_service import BaseService
|
|
14
|
+
from claude_mpm.core.logger import get_logger
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CommandDeploymentService(BaseService):
|
|
18
|
+
"""Service for deploying MPM slash commands."""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
"""Initialize the command deployment service."""
|
|
22
|
+
super().__init__(name="command_deployment")
|
|
23
|
+
|
|
24
|
+
# Source commands directory in the package
|
|
25
|
+
self.source_dir = Path(__file__).parent.parent / "commands"
|
|
26
|
+
|
|
27
|
+
# Target directory in user's home
|
|
28
|
+
self.target_dir = Path.home() / ".claude" / "commands"
|
|
29
|
+
|
|
30
|
+
async def _initialize(self) -> None:
|
|
31
|
+
"""Initialize the service."""
|
|
32
|
+
|
|
33
|
+
async def _cleanup(self) -> None:
|
|
34
|
+
"""Cleanup service resources."""
|
|
35
|
+
|
|
36
|
+
def deploy_commands(self, force: bool = False) -> Dict[str, Any]:
|
|
37
|
+
"""Deploy MPM slash commands to user's Claude configuration.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
force: Force deployment even if files exist
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dictionary with deployment results
|
|
44
|
+
"""
|
|
45
|
+
result = {
|
|
46
|
+
"success": False,
|
|
47
|
+
"deployed": [],
|
|
48
|
+
"errors": [],
|
|
49
|
+
"target_dir": str(self.target_dir),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
# Check if source directory exists
|
|
54
|
+
if not self.source_dir.exists():
|
|
55
|
+
self.logger.warning(
|
|
56
|
+
f"Source commands directory not found: {self.source_dir}"
|
|
57
|
+
)
|
|
58
|
+
result["errors"].append(
|
|
59
|
+
f"Source directory not found: {self.source_dir}"
|
|
60
|
+
)
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
# Create target directory if it doesn't exist
|
|
64
|
+
self.target_dir.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
self.logger.debug(f"Ensured target directory exists: {self.target_dir}")
|
|
66
|
+
|
|
67
|
+
# Get all .md files from source directory
|
|
68
|
+
command_files = list(self.source_dir.glob("*.md"))
|
|
69
|
+
|
|
70
|
+
if not command_files:
|
|
71
|
+
self.logger.info("No command files found to deploy")
|
|
72
|
+
result["success"] = True
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
# Deploy each command file
|
|
76
|
+
for source_file in command_files:
|
|
77
|
+
target_file = self.target_dir / source_file.name
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
# Check if file exists and if we should overwrite
|
|
81
|
+
if target_file.exists() and not force:
|
|
82
|
+
# Check if source is newer
|
|
83
|
+
if source_file.stat().st_mtime <= target_file.stat().st_mtime:
|
|
84
|
+
self.logger.debug(
|
|
85
|
+
f"Skipping {source_file.name} - target is up to date"
|
|
86
|
+
)
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
# Copy the file
|
|
90
|
+
shutil.copy2(source_file, target_file)
|
|
91
|
+
self.logger.info(f"Deployed command: {source_file.name}")
|
|
92
|
+
result["deployed"].append(source_file.name)
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
error_msg = f"Failed to deploy {source_file.name}: {e}"
|
|
96
|
+
self.logger.error(error_msg)
|
|
97
|
+
result["errors"].append(error_msg)
|
|
98
|
+
|
|
99
|
+
result["success"] = len(result["errors"]) == 0
|
|
100
|
+
|
|
101
|
+
if result["deployed"]:
|
|
102
|
+
self.logger.info(
|
|
103
|
+
f"Successfully deployed {len(result['deployed'])} commands to {self.target_dir}"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
error_msg = f"Command deployment failed: {e}"
|
|
110
|
+
self.logger.error(error_msg)
|
|
111
|
+
result["errors"].append(error_msg)
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
def list_available_commands(self) -> List[str]:
|
|
115
|
+
"""List available commands in the source directory.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
List of command file names
|
|
119
|
+
"""
|
|
120
|
+
if not self.source_dir.exists():
|
|
121
|
+
return []
|
|
122
|
+
|
|
123
|
+
return [f.name for f in self.source_dir.glob("*.md")]
|
|
124
|
+
|
|
125
|
+
def list_deployed_commands(self) -> List[str]:
|
|
126
|
+
"""List deployed commands in the target directory.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
List of deployed command file names
|
|
130
|
+
"""
|
|
131
|
+
if not self.target_dir.exists():
|
|
132
|
+
return []
|
|
133
|
+
|
|
134
|
+
return [f.name for f in self.target_dir.glob("mpm*.md")]
|
|
135
|
+
|
|
136
|
+
def remove_deployed_commands(self) -> int:
|
|
137
|
+
"""Remove all deployed MPM commands from target directory.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Number of files removed
|
|
141
|
+
"""
|
|
142
|
+
if not self.target_dir.exists():
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
removed = 0
|
|
146
|
+
for file in self.target_dir.glob("mpm*.md"):
|
|
147
|
+
try:
|
|
148
|
+
file.unlink()
|
|
149
|
+
self.logger.info(f"Removed command: {file.name}")
|
|
150
|
+
removed += 1
|
|
151
|
+
except Exception as e:
|
|
152
|
+
self.logger.error(f"Failed to remove {file.name}: {e}")
|
|
153
|
+
|
|
154
|
+
return removed
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def deploy_commands_on_startup(force: bool = False) -> None:
|
|
158
|
+
"""Convenience function to deploy commands during startup.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
force: Force deployment even if files exist
|
|
162
|
+
"""
|
|
163
|
+
service = CommandDeploymentService()
|
|
164
|
+
result = service.deploy_commands(force=force)
|
|
165
|
+
|
|
166
|
+
if result["deployed"]:
|
|
167
|
+
logger = get_logger("startup")
|
|
168
|
+
logger.info(f"MPM commands deployed: {', '.join(result['deployed'])}")
|
|
169
|
+
|
|
170
|
+
if result["errors"]:
|
|
171
|
+
logger = get_logger("startup")
|
|
172
|
+
for error in result["errors"]:
|
|
173
|
+
logger.warning(f"Command deployment issue: {error}")
|