claude-mpm 3.9.11__py3-none-any.whl → 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +1 -1
- 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 +79 -51
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +20 -20
- 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 +140 -905
- 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 -1156
- 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 +71 -73
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +35 -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 +233 -199
- 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 +30 -13
- 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 +13 -20
- 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 +87 -84
- claude_mpm/services/mcp_gateway/main.py +287 -137
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
- 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 +109 -119
- 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 +19 -533
- 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 +2 -2
- claude_mpm/storage/state_storage.py +177 -181
- 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.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/cli/commands/run_guarded.py +0 -511
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/config/memory_guardian_yaml.py +0 -335
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/core/memory_aware_runner.py +0 -353
- 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/graceful_degradation.py +0 -616
- claude_mpm/services/infrastructure/health_monitor.py +0 -775
- claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
- claude_mpm/services/infrastructure/memory_guardian.py +0 -944
- claude_mpm/services/infrastructure/restart_protection.py +0 -642
- claude_mpm/services/infrastructure/state_manager.py +0 -774
- claude_mpm/services/mcp_gateway/manager.py +0 -334
- 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.11.dist-info/RECORD +0 -306
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,453 @@ | |
| 1 | 
            +
            """Agent Format Converter Service
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This service handles format conversion between different agent file formats,
         | 
| 4 | 
            +
            particularly YAML to Markdown conversion and format migration utilities.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Extracted from AgentDeploymentService as part of the refactoring to improve
         | 
| 7 | 
            +
            maintainability and testability.
         | 
| 8 | 
            +
            """
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            import logging
         | 
| 11 | 
            +
            import re
         | 
| 12 | 
            +
            from datetime import datetime
         | 
| 13 | 
            +
            from pathlib import Path
         | 
| 14 | 
            +
            from typing import Any, Dict, List, Optional
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            from claude_mpm.core.logging_config import get_logger
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
            class AgentFormatConverter:
         | 
| 20 | 
            +
                """Service for converting agent files between different formats.
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                This service handles:
         | 
| 23 | 
            +
                - YAML to Markdown conversion with YAML frontmatter
         | 
| 24 | 
            +
                - Format migration and backward compatibility
         | 
| 25 | 
            +
                - Field extraction from various formats
         | 
| 26 | 
            +
                - Content structure transformation
         | 
| 27 | 
            +
                """
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def __init__(self):
         | 
| 30 | 
            +
                    """Initialize the agent format converter."""
         | 
| 31 | 
            +
                    self.logger = get_logger(__name__)
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def convert_yaml_to_md(self, target_dir: Path) -> Dict[str, Any]:
         | 
| 34 | 
            +
                    """
         | 
| 35 | 
            +
                    Convert existing YAML agent files to MD format with YAML frontmatter.
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    This method handles backward compatibility by finding existing .yaml
         | 
| 38 | 
            +
                    agent files and converting them to .md format expected by Claude Code.
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    Args:
         | 
| 41 | 
            +
                        target_dir: Directory containing agent files to convert
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    Returns:
         | 
| 44 | 
            +
                        Dictionary with conversion results
         | 
| 45 | 
            +
                    """
         | 
| 46 | 
            +
                    results = {"converted": [], "errors": [], "skipped": []}
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    if not target_dir.exists():
         | 
| 49 | 
            +
                        self.logger.debug(f"Target directory does not exist: {target_dir}")
         | 
| 50 | 
            +
                        return results
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    try:
         | 
| 53 | 
            +
                        # Find YAML files that need conversion
         | 
| 54 | 
            +
                        yaml_files = list(target_dir.glob("*.yaml"))
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                        for yaml_file in yaml_files:
         | 
| 57 | 
            +
                            try:
         | 
| 58 | 
            +
                                # Check if corresponding MD file already exists
         | 
| 59 | 
            +
                                md_file = yaml_file.with_suffix(".md")
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                                if md_file.exists():
         | 
| 62 | 
            +
                                    # Check modification times
         | 
| 63 | 
            +
                                    yaml_mtime = yaml_file.stat().st_mtime
         | 
| 64 | 
            +
                                    md_mtime = md_file.stat().st_mtime
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                                    if md_mtime >= yaml_mtime:
         | 
| 67 | 
            +
                                        # MD file is newer or same age, skip conversion
         | 
| 68 | 
            +
                                        results["skipped"].append(yaml_file.name)
         | 
| 69 | 
            +
                                        continue
         | 
| 70 | 
            +
                                    else:
         | 
| 71 | 
            +
                                        # MD file is older, proceed with conversion
         | 
| 72 | 
            +
                                        self.logger.info(
         | 
| 73 | 
            +
                                            f"MD file {md_file.name} is older than YAML, converting..."
         | 
| 74 | 
            +
                                        )
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                                # Read YAML content
         | 
| 77 | 
            +
                                yaml_content = yaml_file.read_text()
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                                # Convert to MD format
         | 
| 80 | 
            +
                                md_content = self.convert_yaml_content_to_md(
         | 
| 81 | 
            +
                                    yaml_content, yaml_file.stem
         | 
| 82 | 
            +
                                )
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                                # Write MD file
         | 
| 85 | 
            +
                                md_file.write_text(md_content)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                                # Remove original YAML file
         | 
| 88 | 
            +
                                yaml_file.unlink()
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                                results["converted"].append(
         | 
| 91 | 
            +
                                    {"from": yaml_file.name, "to": md_file.name}
         | 
| 92 | 
            +
                                )
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                                self.logger.info(f"Converted {yaml_file.name} to {md_file.name}")
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                            except Exception as e:
         | 
| 97 | 
            +
                                error_msg = f"Failed to convert {yaml_file.name}: {e}"
         | 
| 98 | 
            +
                                results["errors"].append(error_msg)
         | 
| 99 | 
            +
                                self.logger.error(error_msg)
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    except Exception as e:
         | 
| 102 | 
            +
                        error_msg = f"YAML to MD conversion failed: {e}"
         | 
| 103 | 
            +
                        self.logger.error(error_msg)
         | 
| 104 | 
            +
                        results["errors"].append(error_msg)
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    return results
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                def convert_yaml_content_to_md(self, yaml_content: str, agent_name: str) -> str:
         | 
| 109 | 
            +
                    """
         | 
| 110 | 
            +
                    Convert YAML agent content to MD format with YAML frontmatter.
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    Args:
         | 
| 113 | 
            +
                        yaml_content: Original YAML content
         | 
| 114 | 
            +
                        agent_name: Name of the agent
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    Returns:
         | 
| 117 | 
            +
                        Converted Markdown content with YAML frontmatter
         | 
| 118 | 
            +
                    """
         | 
| 119 | 
            +
                    # Extract fields from YAML content
         | 
| 120 | 
            +
                    name = self.extract_yaml_field(yaml_content, "name") or agent_name
         | 
| 121 | 
            +
                    description = (
         | 
| 122 | 
            +
                        self.extract_yaml_field(yaml_content, "description")
         | 
| 123 | 
            +
                        or f"{agent_name.title()} agent for specialized tasks"
         | 
| 124 | 
            +
                    )
         | 
| 125 | 
            +
                    version = self.extract_yaml_field(yaml_content, "version") or "1.0.0"
         | 
| 126 | 
            +
                    tools_line = (
         | 
| 127 | 
            +
                        self.extract_yaml_field(yaml_content, "tools")
         | 
| 128 | 
            +
                        or "Read,Write,Edit,Grep,Glob,LS"
         | 
| 129 | 
            +
                    )
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                    # Convert tools string to list format if needed
         | 
| 132 | 
            +
                    if isinstance(tools_line, str):
         | 
| 133 | 
            +
                        if tools_line.startswith("[") and tools_line.endswith("]"):
         | 
| 134 | 
            +
                            # Already in list format
         | 
| 135 | 
            +
                            tools_list = tools_line
         | 
| 136 | 
            +
                        else:
         | 
| 137 | 
            +
                            # Convert comma-separated to list
         | 
| 138 | 
            +
                            tools = [tool.strip() for tool in tools_line.split(",")]
         | 
| 139 | 
            +
                            tools_list = str(tools).replace("'", '"')
         | 
| 140 | 
            +
                    else:
         | 
| 141 | 
            +
                        tools_list = '["Read", "Write", "Edit"]'
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    # Extract additional fields
         | 
| 144 | 
            +
                    model = self.extract_yaml_field(yaml_content, "model") or "sonnet"
         | 
| 145 | 
            +
                    author = (
         | 
| 146 | 
            +
                        self.extract_yaml_field(yaml_content, "author")
         | 
| 147 | 
            +
                        or "claude-mpm@anthropic.com"
         | 
| 148 | 
            +
                    )
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    # Extract instructions from YAML content
         | 
| 151 | 
            +
                    instructions = self._extract_instructions_from_yaml(yaml_content, agent_name)
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    # Build new YAML frontmatter
         | 
| 154 | 
            +
                    current_time = datetime.now().isoformat() + "Z"
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                    new_frontmatter = f"""---
         | 
| 157 | 
            +
            name: {name}
         | 
| 158 | 
            +
            description: "{description}"
         | 
| 159 | 
            +
            version: "{version}"
         | 
| 160 | 
            +
            author: "{author}"
         | 
| 161 | 
            +
            created: "{current_time}"
         | 
| 162 | 
            +
            updated: "{current_time}"
         | 
| 163 | 
            +
            tags: ["{agent_name}", "mpm-framework"]
         | 
| 164 | 
            +
            tools: {tools_list}
         | 
| 165 | 
            +
            model: "{model}"
         | 
| 166 | 
            +
            ---
         | 
| 167 | 
            +
             | 
| 168 | 
            +
            """
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                    return new_frontmatter + instructions
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                def extract_yaml_field(self, yaml_content: str, field_name: str) -> Optional[str]:
         | 
| 173 | 
            +
                    """
         | 
| 174 | 
            +
                    Extract a field value from YAML content.
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                    Args:
         | 
| 177 | 
            +
                        yaml_content: YAML content string
         | 
| 178 | 
            +
                        field_name: Name of the field to extract
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                    Returns:
         | 
| 181 | 
            +
                        Field value or None if not found
         | 
| 182 | 
            +
                    """
         | 
| 183 | 
            +
                    # Try to match multi-line field first (with | or >)
         | 
| 184 | 
            +
                    multiline_pattern = rf"^{field_name}:\s*[|>]\s*\n((?:[ \t]+.+\n?)*)"
         | 
| 185 | 
            +
                    multiline_match = re.search(multiline_pattern, yaml_content, re.MULTILINE)
         | 
| 186 | 
            +
                    if multiline_match:
         | 
| 187 | 
            +
                        # Extract indented content and remove common indentation
         | 
| 188 | 
            +
                        lines = multiline_match.group(1).split("\n")
         | 
| 189 | 
            +
                        # Remove empty lines at the end
         | 
| 190 | 
            +
                        while lines and not lines[-1].strip():
         | 
| 191 | 
            +
                            lines.pop()
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                        if lines:
         | 
| 194 | 
            +
                            # Find minimum indentation (excluding empty lines)
         | 
| 195 | 
            +
                            min_indent = float("inf")
         | 
| 196 | 
            +
                            for line in lines:
         | 
| 197 | 
            +
                                if line.strip():  # Skip empty lines
         | 
| 198 | 
            +
                                    indent = len(line) - len(line.lstrip())
         | 
| 199 | 
            +
                                    min_indent = min(min_indent, indent)
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                            # Remove common indentation
         | 
| 202 | 
            +
                            if min_indent != float("inf"):
         | 
| 203 | 
            +
                                dedented_lines = []
         | 
| 204 | 
            +
                                for line in lines:
         | 
| 205 | 
            +
                                    if line.strip():  # Non-empty line
         | 
| 206 | 
            +
                                        dedented_lines.append(line[min_indent:])
         | 
| 207 | 
            +
                                    else:  # Empty line
         | 
| 208 | 
            +
                                        dedented_lines.append("")
         | 
| 209 | 
            +
                                return "\n".join(dedented_lines).strip()
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                    # Try to match field with various quote styles
         | 
| 212 | 
            +
                    patterns = [
         | 
| 213 | 
            +
                        rf'^{field_name}:\s*"([^"]*)"',  # Double quotes
         | 
| 214 | 
            +
                        rf"^{field_name}:\s*'([^']*)'",  # Single quotes
         | 
| 215 | 
            +
                        rf"^{field_name}:\s*([^\n\r]+)",  # No quotes
         | 
| 216 | 
            +
                    ]
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                    for pattern in patterns:
         | 
| 219 | 
            +
                        match = re.search(pattern, yaml_content, re.MULTILINE)
         | 
| 220 | 
            +
                        if match:
         | 
| 221 | 
            +
                            value = match.group(1).strip()
         | 
| 222 | 
            +
                            if value:
         | 
| 223 | 
            +
                                return value
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                    return None
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                def convert_md_to_yaml(self, md_content: str) -> str:
         | 
| 228 | 
            +
                    """
         | 
| 229 | 
            +
                    Convert Markdown with YAML frontmatter back to pure YAML format.
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    Args:
         | 
| 232 | 
            +
                        md_content: Markdown content with YAML frontmatter
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                    Returns:
         | 
| 235 | 
            +
                        Pure YAML content
         | 
| 236 | 
            +
                    """
         | 
| 237 | 
            +
                    if not md_content.strip().startswith("---"):
         | 
| 238 | 
            +
                        # No frontmatter, treat as plain YAML
         | 
| 239 | 
            +
                        return md_content
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                    # Split frontmatter and markdown content
         | 
| 242 | 
            +
                    parts = md_content.split("---", 2)
         | 
| 243 | 
            +
                    if len(parts) < 3:
         | 
| 244 | 
            +
                        return md_content
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                    frontmatter = parts[1].strip()
         | 
| 247 | 
            +
                    markdown_content = parts[2].strip()
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                    # Add instructions field if there's markdown content
         | 
| 250 | 
            +
                    if markdown_content:
         | 
| 251 | 
            +
                        frontmatter += f"\ninstructions: |\n"
         | 
| 252 | 
            +
                        # Indent markdown content
         | 
| 253 | 
            +
                        for line in markdown_content.split("\n"):
         | 
| 254 | 
            +
                            frontmatter += f"  {line}\n"
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                    return frontmatter
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                def detect_format(self, content: str) -> str:
         | 
| 259 | 
            +
                    """
         | 
| 260 | 
            +
                    Detect the format of agent content.
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                    Args:
         | 
| 263 | 
            +
                        content: Agent file content
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                    Returns:
         | 
| 266 | 
            +
                        Format type: 'markdown_yaml', 'yaml', 'json', or 'unknown'
         | 
| 267 | 
            +
                    """
         | 
| 268 | 
            +
                    content = content.strip()
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                    if content.startswith("---") and "---" in content[3:]:
         | 
| 271 | 
            +
                        return "markdown_yaml"
         | 
| 272 | 
            +
                    elif content.startswith("{") and content.endswith("}"):
         | 
| 273 | 
            +
                        return "json"
         | 
| 274 | 
            +
                    elif ":" in content and not content.startswith("#"):
         | 
| 275 | 
            +
                        # Likely YAML if it has key-value pairs and doesn't start with markdown header
         | 
| 276 | 
            +
                        return "yaml"
         | 
| 277 | 
            +
                    else:
         | 
| 278 | 
            +
                        return "unknown"
         | 
| 279 | 
            +
             | 
| 280 | 
            +
                def normalize_agent_content(
         | 
| 281 | 
            +
                    self, content: str, agent_name: str, target_format: str = "markdown_yaml"
         | 
| 282 | 
            +
                ) -> str:
         | 
| 283 | 
            +
                    """
         | 
| 284 | 
            +
                    Normalize agent content to a specific format.
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                    Args:
         | 
| 287 | 
            +
                        content: Original agent content
         | 
| 288 | 
            +
                        agent_name: Name of the agent
         | 
| 289 | 
            +
                        target_format: Target format ('markdown_yaml', 'yaml', 'json')
         | 
| 290 | 
            +
             | 
| 291 | 
            +
                    Returns:
         | 
| 292 | 
            +
                        Normalized content in target format
         | 
| 293 | 
            +
                    """
         | 
| 294 | 
            +
                    current_format = self.detect_format(content)
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                    if current_format == target_format:
         | 
| 297 | 
            +
                        return content
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                    # Convert to intermediate format first (markdown_yaml)
         | 
| 300 | 
            +
                    if current_format == "yaml":
         | 
| 301 | 
            +
                        intermediate = self.convert_yaml_content_to_md(content, agent_name)
         | 
| 302 | 
            +
                    elif current_format == "json":
         | 
| 303 | 
            +
                        intermediate = self._convert_json_to_md(content, agent_name)
         | 
| 304 | 
            +
                    else:
         | 
| 305 | 
            +
                        intermediate = content
         | 
| 306 | 
            +
             | 
| 307 | 
            +
                    # Convert to target format
         | 
| 308 | 
            +
                    if target_format == "yaml":
         | 
| 309 | 
            +
                        return self.convert_md_to_yaml(intermediate)
         | 
| 310 | 
            +
                    elif target_format == "json":
         | 
| 311 | 
            +
                        return self._convert_md_to_json(intermediate)
         | 
| 312 | 
            +
                    else:
         | 
| 313 | 
            +
                        return intermediate
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                def get_conversion_stats(self, target_dir: Path) -> Dict[str, Any]:
         | 
| 316 | 
            +
                    """
         | 
| 317 | 
            +
                    Get statistics about files that need conversion.
         | 
| 318 | 
            +
             | 
| 319 | 
            +
                    Args:
         | 
| 320 | 
            +
                        target_dir: Directory to analyze
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                    Returns:
         | 
| 323 | 
            +
                        Dictionary with conversion statistics
         | 
| 324 | 
            +
                    """
         | 
| 325 | 
            +
                    stats = {
         | 
| 326 | 
            +
                        "total_files": 0,
         | 
| 327 | 
            +
                        "yaml_files": 0,
         | 
| 328 | 
            +
                        "md_files": 0,
         | 
| 329 | 
            +
                        "json_files": 0,
         | 
| 330 | 
            +
                        "needs_conversion": 0,
         | 
| 331 | 
            +
                        "formats": {},
         | 
| 332 | 
            +
                    }
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                    if not target_dir.exists():
         | 
| 335 | 
            +
                        return stats
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                    # Analyze all agent files
         | 
| 338 | 
            +
                    for file_path in target_dir.glob("*"):
         | 
| 339 | 
            +
                        if file_path.is_file() and file_path.suffix in [
         | 
| 340 | 
            +
                            ".yaml",
         | 
| 341 | 
            +
                            ".yml",
         | 
| 342 | 
            +
                            ".md",
         | 
| 343 | 
            +
                            ".json",
         | 
| 344 | 
            +
                        ]:
         | 
| 345 | 
            +
                            stats["total_files"] += 1
         | 
| 346 | 
            +
             | 
| 347 | 
            +
                            if file_path.suffix in [".yaml", ".yml"]:
         | 
| 348 | 
            +
                                stats["yaml_files"] += 1
         | 
| 349 | 
            +
                                # Check if corresponding .md file exists
         | 
| 350 | 
            +
                                md_file = file_path.with_suffix(".md")
         | 
| 351 | 
            +
                                if not md_file.exists():
         | 
| 352 | 
            +
                                    stats["needs_conversion"] += 1
         | 
| 353 | 
            +
                            elif file_path.suffix == ".md":
         | 
| 354 | 
            +
                                stats["md_files"] += 1
         | 
| 355 | 
            +
                            elif file_path.suffix == ".json":
         | 
| 356 | 
            +
                                stats["json_files"] += 1
         | 
| 357 | 
            +
             | 
| 358 | 
            +
                            # Detect format
         | 
| 359 | 
            +
                            try:
         | 
| 360 | 
            +
                                content = file_path.read_text()
         | 
| 361 | 
            +
                                format_type = self.detect_format(content)
         | 
| 362 | 
            +
                                stats["formats"][format_type] = (
         | 
| 363 | 
            +
                                    stats["formats"].get(format_type, 0) + 1
         | 
| 364 | 
            +
                                )
         | 
| 365 | 
            +
                            except Exception:
         | 
| 366 | 
            +
                                stats["formats"]["unreadable"] = (
         | 
| 367 | 
            +
                                    stats["formats"].get("unreadable", 0) + 1
         | 
| 368 | 
            +
                                )
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                    return stats
         | 
| 371 | 
            +
             | 
| 372 | 
            +
                def _extract_instructions_from_yaml(
         | 
| 373 | 
            +
                    self, yaml_content: str, agent_name: str
         | 
| 374 | 
            +
                ) -> str:
         | 
| 375 | 
            +
                    """
         | 
| 376 | 
            +
                    Extract instructions from YAML content.
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                    Args:
         | 
| 379 | 
            +
                        yaml_content: YAML content
         | 
| 380 | 
            +
                        agent_name: Agent name for default instructions
         | 
| 381 | 
            +
             | 
| 382 | 
            +
                    Returns:
         | 
| 383 | 
            +
                        Instructions text
         | 
| 384 | 
            +
                    """
         | 
| 385 | 
            +
                    # Try to extract instructions field
         | 
| 386 | 
            +
                    instructions = self.extract_yaml_field(yaml_content, "instructions")
         | 
| 387 | 
            +
                    if instructions:
         | 
| 388 | 
            +
                        return instructions
         | 
| 389 | 
            +
             | 
| 390 | 
            +
                    # Try to extract description as instructions
         | 
| 391 | 
            +
                    description = self.extract_yaml_field(yaml_content, "description")
         | 
| 392 | 
            +
                    if (
         | 
| 393 | 
            +
                        description and len(description) > 50
         | 
| 394 | 
            +
                    ):  # Long description might be instructions
         | 
| 395 | 
            +
                        return f"# {agent_name.title()} Agent\n\n{description}"
         | 
| 396 | 
            +
             | 
| 397 | 
            +
                    # Default instructions
         | 
| 398 | 
            +
                    return f"# {agent_name.title()} Agent\n\nThis agent provides specialized functionality for your tasks."
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                def _convert_json_to_md(self, json_content: str, agent_name: str) -> str:
         | 
| 401 | 
            +
                    """Convert JSON content to Markdown with YAML frontmatter."""
         | 
| 402 | 
            +
                    try:
         | 
| 403 | 
            +
                        import json
         | 
| 404 | 
            +
             | 
| 405 | 
            +
                        data = json.loads(json_content)
         | 
| 406 | 
            +
             | 
| 407 | 
            +
                        # Convert JSON data to YAML-like string for processing
         | 
| 408 | 
            +
                        yaml_lines = []
         | 
| 409 | 
            +
                        for key, value in data.items():
         | 
| 410 | 
            +
                            if isinstance(value, str):
         | 
| 411 | 
            +
                                yaml_lines.append(f'{key}: "{value}"')
         | 
| 412 | 
            +
                            elif isinstance(value, list):
         | 
| 413 | 
            +
                                yaml_lines.append(f"{key}: {json.dumps(value)}")
         | 
| 414 | 
            +
                            else:
         | 
| 415 | 
            +
                                yaml_lines.append(f"{key}: {value}")
         | 
| 416 | 
            +
             | 
| 417 | 
            +
                        yaml_content = "\n".join(yaml_lines)
         | 
| 418 | 
            +
                        return self.convert_yaml_content_to_md(yaml_content, agent_name)
         | 
| 419 | 
            +
             | 
| 420 | 
            +
                    except Exception as e:
         | 
| 421 | 
            +
                        self.logger.error(f"Failed to convert JSON to MD: {e}")
         | 
| 422 | 
            +
                        return f"# {agent_name.title()} Agent\n\nConversion failed: {e}"
         | 
| 423 | 
            +
             | 
| 424 | 
            +
                def _convert_md_to_json(self, md_content: str) -> str:
         | 
| 425 | 
            +
                    """Convert Markdown with YAML frontmatter to JSON."""
         | 
| 426 | 
            +
                    try:
         | 
| 427 | 
            +
                        import json
         | 
| 428 | 
            +
             | 
| 429 | 
            +
                        import yaml
         | 
| 430 | 
            +
             | 
| 431 | 
            +
                        if not md_content.strip().startswith("---"):
         | 
| 432 | 
            +
                            return json.dumps({"error": "No YAML frontmatter found"})
         | 
| 433 | 
            +
             | 
| 434 | 
            +
                        # Extract frontmatter
         | 
| 435 | 
            +
                        parts = md_content.split("---", 2)
         | 
| 436 | 
            +
                        if len(parts) < 3:
         | 
| 437 | 
            +
                            return json.dumps({"error": "Invalid frontmatter format"})
         | 
| 438 | 
            +
             | 
| 439 | 
            +
                        frontmatter = parts[1].strip()
         | 
| 440 | 
            +
                        markdown_content = parts[2].strip()
         | 
| 441 | 
            +
             | 
| 442 | 
            +
                        # Parse YAML frontmatter
         | 
| 443 | 
            +
                        data = yaml.safe_load(frontmatter)
         | 
| 444 | 
            +
             | 
| 445 | 
            +
                        # Add instructions from markdown content
         | 
| 446 | 
            +
                        if markdown_content:
         | 
| 447 | 
            +
                            data["instructions"] = markdown_content
         | 
| 448 | 
            +
             | 
| 449 | 
            +
                        return json.dumps(data, indent=2)
         | 
| 450 | 
            +
             | 
| 451 | 
            +
                    except Exception as e:
         | 
| 452 | 
            +
                        self.logger.error(f"Failed to convert MD to JSON: {e}")
         | 
| 453 | 
            +
                        return json.dumps({"error": str(e)})
         | 
| @@ -0,0 +1,161 @@ | |
| 1 | 
            +
            """Agent frontmatter validation for deployment service.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This module provides validation and repair functionality for agent frontmatter.
         | 
| 4 | 
            +
            Extracted from AgentDeploymentService to reduce complexity and improve maintainability.
         | 
| 5 | 
            +
            """
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            import logging
         | 
| 8 | 
            +
            from pathlib import Path
         | 
| 9 | 
            +
            from typing import Any, Dict
         | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            class AgentFrontmatterValidator:
         | 
| 13 | 
            +
                """Validates and repairs agent frontmatter."""
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def __init__(self, logger: logging.Logger):
         | 
| 16 | 
            +
                    """Initialize the validator with a logger."""
         | 
| 17 | 
            +
                    self.logger = logger
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def validate_and_repair_existing_agents(self, agents_dir: Path) -> Dict[str, Any]:
         | 
| 20 | 
            +
                    """
         | 
| 21 | 
            +
                    Validate and repair broken frontmatter in existing agent files.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    This method scans existing .claude/agents/*.md files and validates their
         | 
| 24 | 
            +
                    frontmatter. If the frontmatter is broken or missing, it attempts to repair
         | 
| 25 | 
            +
                    it or marks the agent for replacement during deployment.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    WHY: Ensures all existing agents have valid YAML frontmatter before deployment,
         | 
| 28 | 
            +
                    preventing runtime errors in Claude Code when loading agents.
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    Args:
         | 
| 31 | 
            +
                        agents_dir: Directory containing agent .md files
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    Returns:
         | 
| 34 | 
            +
                        Dictionary with validation results:
         | 
| 35 | 
            +
                        - repaired: List of agent names that were repaired
         | 
| 36 | 
            +
                        - replaced: List of agent names marked for replacement
         | 
| 37 | 
            +
                        - errors: List of validation errors
         | 
| 38 | 
            +
                    """
         | 
| 39 | 
            +
                    results = {"repaired": [], "replaced": [], "errors": []}
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    try:
         | 
| 42 | 
            +
                        # Import frontmatter validator
         | 
| 43 | 
            +
                        from claude_mpm.agents.frontmatter_validator import FrontmatterValidator
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                        validator = FrontmatterValidator()
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                        # Find existing agent files
         | 
| 48 | 
            +
                        agent_files = list(agents_dir.glob("*.md"))
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                        if not agent_files:
         | 
| 51 | 
            +
                            self.logger.debug("No existing agent files to validate")
         | 
| 52 | 
            +
                            return results
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                        self.logger.debug(
         | 
| 55 | 
            +
                            f"Validating frontmatter in {len(agent_files)} existing agents"
         | 
| 56 | 
            +
                        )
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                        for agent_file in agent_files:
         | 
| 59 | 
            +
                            try:
         | 
| 60 | 
            +
                                agent_name = agent_file.stem
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                                # Read agent file content
         | 
| 63 | 
            +
                                content = agent_file.read_text()
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                                # Check if this is a system agent (authored by claude-mpm)
         | 
| 66 | 
            +
                                # Only repair system agents, leave user agents alone
         | 
| 67 | 
            +
                                if (
         | 
| 68 | 
            +
                                    "author: claude-mpm" not in content
         | 
| 69 | 
            +
                                    and "author: 'claude-mpm'" not in content
         | 
| 70 | 
            +
                                ):
         | 
| 71 | 
            +
                                    self.logger.debug(
         | 
| 72 | 
            +
                                        f"Skipping validation for user agent: {agent_name}"
         | 
| 73 | 
            +
                                    )
         | 
| 74 | 
            +
                                    continue
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                                # Extract and validate frontmatter
         | 
| 77 | 
            +
                                if not content.startswith("---"):
         | 
| 78 | 
            +
                                    # No frontmatter at all - mark for replacement
         | 
| 79 | 
            +
                                    self.logger.warning(
         | 
| 80 | 
            +
                                        f"Agent {agent_name} has no frontmatter, marking for replacement"
         | 
| 81 | 
            +
                                    )
         | 
| 82 | 
            +
                                    results["replaced"].append(agent_name)
         | 
| 83 | 
            +
                                    # Delete the file so it will be recreated
         | 
| 84 | 
            +
                                    agent_file.unlink()
         | 
| 85 | 
            +
                                    continue
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                                # Try to extract frontmatter
         | 
| 88 | 
            +
                                try:
         | 
| 89 | 
            +
                                    end_marker = content.find("\n---\n", 4)
         | 
| 90 | 
            +
                                    if end_marker == -1:
         | 
| 91 | 
            +
                                        end_marker = content.find("\n---\r\n", 4)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                                    if end_marker == -1:
         | 
| 94 | 
            +
                                        # Broken frontmatter - mark for replacement
         | 
| 95 | 
            +
                                        self.logger.warning(
         | 
| 96 | 
            +
                                            f"Agent {agent_name} has broken frontmatter, marking for replacement"
         | 
| 97 | 
            +
                                        )
         | 
| 98 | 
            +
                                        results["replaced"].append(agent_name)
         | 
| 99 | 
            +
                                        # Delete the file so it will be recreated
         | 
| 100 | 
            +
                                        agent_file.unlink()
         | 
| 101 | 
            +
                                        continue
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                                    # Validate frontmatter with the validator
         | 
| 104 | 
            +
                                    validation_result = validator.validate_file(agent_file)
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                                    if not validation_result.is_valid:
         | 
| 107 | 
            +
                                        # Check if it can be corrected
         | 
| 108 | 
            +
                                        if validation_result.corrected_frontmatter:
         | 
| 109 | 
            +
                                            # Apply corrections
         | 
| 110 | 
            +
                                            correction_result = validator.correct_file(
         | 
| 111 | 
            +
                                                agent_file, dry_run=False
         | 
| 112 | 
            +
                                            )
         | 
| 113 | 
            +
                                            if correction_result.corrections:
         | 
| 114 | 
            +
                                                results["repaired"].append(agent_name)
         | 
| 115 | 
            +
                                                self.logger.info(
         | 
| 116 | 
            +
                                                    f"Repaired frontmatter for agent {agent_name}"
         | 
| 117 | 
            +
                                                )
         | 
| 118 | 
            +
                                                for correction in correction_result.corrections:
         | 
| 119 | 
            +
                                                    self.logger.debug(f"  - {correction}")
         | 
| 120 | 
            +
                                        else:
         | 
| 121 | 
            +
                                            # Cannot be corrected - mark for replacement
         | 
| 122 | 
            +
                                            self.logger.warning(
         | 
| 123 | 
            +
                                                f"Agent {agent_name} has invalid frontmatter that cannot be repaired, marking for replacement"
         | 
| 124 | 
            +
                                            )
         | 
| 125 | 
            +
                                            results["replaced"].append(agent_name)
         | 
| 126 | 
            +
                                            # Delete the file so it will be recreated
         | 
| 127 | 
            +
                                            agent_file.unlink()
         | 
| 128 | 
            +
                                    elif validation_result.warnings:
         | 
| 129 | 
            +
                                        # Has warnings but is valid
         | 
| 130 | 
            +
                                        for warning in validation_result.warnings:
         | 
| 131 | 
            +
                                            self.logger.debug(
         | 
| 132 | 
            +
                                                f"Agent {agent_name} warning: {warning}"
         | 
| 133 | 
            +
                                            )
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                                except Exception as e:
         | 
| 136 | 
            +
                                    # Any error in parsing - mark for replacement
         | 
| 137 | 
            +
                                    self.logger.warning(
         | 
| 138 | 
            +
                                        f"Failed to parse frontmatter for {agent_name}: {e}, marking for replacement"
         | 
| 139 | 
            +
                                    )
         | 
| 140 | 
            +
                                    results["replaced"].append(agent_name)
         | 
| 141 | 
            +
                                    # Delete the file so it will be recreated
         | 
| 142 | 
            +
                                    try:
         | 
| 143 | 
            +
                                        agent_file.unlink()
         | 
| 144 | 
            +
                                    except Exception:
         | 
| 145 | 
            +
                                        pass
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                            except Exception as e:
         | 
| 148 | 
            +
                                error_msg = f"Failed to validate agent {agent_file.name}: {e}"
         | 
| 149 | 
            +
                                self.logger.error(error_msg)
         | 
| 150 | 
            +
                                results["errors"].append(error_msg)
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                    except ImportError:
         | 
| 153 | 
            +
                        self.logger.warning(
         | 
| 154 | 
            +
                            "FrontmatterValidator not available, skipping validation"
         | 
| 155 | 
            +
                        )
         | 
| 156 | 
            +
                    except Exception as e:
         | 
| 157 | 
            +
                        error_msg = f"Agent validation failed: {e}"
         | 
| 158 | 
            +
                        self.logger.error(error_msg)
         | 
| 159 | 
            +
                        results["errors"].append(error_msg)
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    return results
         |