claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__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 +2 -2
- 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 +330 -86
- 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 +363 -220
- 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 +124 -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/built/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/built/dashboard.js +2 -0
- claude_mpm/dashboard/static/built/socket-client.js +2 -0
- 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 +93 -72
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
- 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 +133 -53
- 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 +575 -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 +166 -64
- 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 +185 -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.4.dist-info}/METADATA +90 -22
- claude_mpm-4.0.4.dist-info/RECORD +417 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.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.4.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/top_level.txt +0 -0
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            from pathlib import Path
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            """Async Agent Loader for high-performance parallel agent discovery and loading.
         | 
| 2 4 |  | 
| 3 5 | 
             
            This module provides async versions of agent loading operations to significantly
         | 
| @@ -26,18 +28,18 @@ import asyncio | |
| 26 28 | 
             
            import json
         | 
| 27 29 | 
             
            import logging
         | 
| 28 30 | 
             
            import time
         | 
| 29 | 
            -
            from pathlib import Path
         | 
| 30 | 
            -
            from typing import Dict, Any, List, Optional, Tuple
         | 
| 31 | 
            -
            from enum import Enum
         | 
| 32 31 | 
             
            from concurrent.futures import ThreadPoolExecutor
         | 
| 32 | 
            +
            from enum import Enum
         | 
| 33 | 
            +
            from typing import Any, Dict, List, Optional, Tuple
         | 
| 33 34 |  | 
| 34 35 | 
             
            import aiofiles
         | 
| 35 36 |  | 
| 36 37 | 
             
            from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCache
         | 
| 37 | 
            -
             | 
| 38 | 
            -
            from ..validation.agent_validator import AgentValidator, ValidationResult
         | 
| 38 | 
            +
             | 
| 39 39 | 
             
            from ..core.agent_name_normalizer import AgentNameNormalizer
         | 
| 40 | 
            -
            from ..core. | 
| 40 | 
            +
            from ..core.unified_paths import get_path_manager
         | 
| 41 | 
            +
            from ..validation.agent_validator import AgentValidator, ValidationResult
         | 
| 42 | 
            +
            from .base_agent_loader import prepend_base_instructions
         | 
| 41 43 | 
             
            from .frontmatter_validator import FrontmatterValidator
         | 
| 42 44 |  | 
| 43 45 | 
             
            # Module-level logger
         | 
| @@ -46,6 +48,7 @@ logger = logging.getLogger(__name__) | |
| 46 48 |  | 
| 47 49 | 
             
            class AgentTier(Enum):
         | 
| 48 50 | 
             
                """Agent precedence tiers."""
         | 
| 51 | 
            +
             | 
| 49 52 | 
             
                PROJECT = "project"
         | 
| 50 53 | 
             
                USER = "user"
         | 
| 51 54 | 
             
                SYSTEM = "system"
         | 
| @@ -53,19 +56,19 @@ class AgentTier(Enum): | |
| 53 56 |  | 
| 54 57 | 
             
            class AsyncAgentLoader:
         | 
| 55 58 | 
             
                """Async agent loader for high-performance parallel operations.
         | 
| 56 | 
            -
             | 
| 59 | 
            +
             | 
| 57 60 | 
             
                WHY: This async loader provides:
         | 
| 58 61 | 
             
                - 60-80% faster agent discovery through parallel tier scanning
         | 
| 59 62 | 
             
                - Non-blocking file I/O for all agent files
         | 
| 60 63 | 
             
                - Concurrent validation and parsing
         | 
| 61 64 | 
             
                - Seamless integration with existing code
         | 
| 62 | 
            -
             | 
| 65 | 
            +
             | 
| 63 66 | 
             
                PERFORMANCE METRICS (typical):
         | 
| 64 67 | 
             
                - Sync loading: 300-500ms for 10 agents across 3 tiers
         | 
| 65 68 | 
             
                - Async loading: 80-150ms for same (70-80% reduction)
         | 
| 66 69 | 
             
                - Scales better with more agents (near-constant time)
         | 
| 67 70 | 
             
                """
         | 
| 68 | 
            -
             | 
| 71 | 
            +
             | 
| 69 72 | 
             
                def __init__(self):
         | 
| 70 73 | 
             
                    """Initialize async agent loader."""
         | 
| 71 74 | 
             
                    self.validator = AgentValidator()
         | 
| @@ -73,50 +76,60 @@ class AsyncAgentLoader: | |
| 73 76 | 
             
                    self._agent_registry: Dict[str, Dict[str, Any]] = {}
         | 
| 74 77 | 
             
                    self._agent_tiers: Dict[str, AgentTier] = {}
         | 
| 75 78 | 
             
                    self.frontmatter_validator = FrontmatterValidator()
         | 
| 76 | 
            -
             | 
| 79 | 
            +
             | 
| 77 80 | 
             
                    # Thread pool for CPU-bound operations
         | 
| 78 81 | 
             
                    self.executor = ThreadPoolExecutor(max_workers=4)
         | 
| 79 | 
            -
             | 
| 82 | 
            +
             | 
| 80 83 | 
             
                    # Performance metrics
         | 
| 81 84 | 
             
                    self._metrics = {
         | 
| 82 | 
            -
                         | 
| 83 | 
            -
                         | 
| 84 | 
            -
                         | 
| 85 | 
            -
                         | 
| 86 | 
            -
                         | 
| 87 | 
            -
                         | 
| 85 | 
            +
                        "agents_loaded": 0,
         | 
| 86 | 
            +
                        "async_operations": 0,
         | 
| 87 | 
            +
                        "parallel_tiers_scanned": 0,
         | 
| 88 | 
            +
                        "time_saved_ms": 0.0,
         | 
| 89 | 
            +
                        "cache_hits": 0,
         | 
| 90 | 
            +
                        "cache_misses": 0,
         | 
| 88 91 | 
             
                    }
         | 
| 89 | 
            -
             | 
| 92 | 
            +
             | 
| 90 93 | 
             
                async def discover_agent_dirs_async(self) -> Dict[AgentTier, Optional[Path]]:
         | 
| 91 94 | 
             
                    """Discover agent directories across all tiers in parallel.
         | 
| 92 | 
            -
             | 
| 95 | 
            +
             | 
| 93 96 | 
             
                    WHY: Checking directory existence across PROJECT/USER/SYSTEM tiers
         | 
| 94 97 | 
             
                    sequentially adds unnecessary latency. Parallel checking reduces
         | 
| 95 98 | 
             
                    this to the time of the slowest check.
         | 
| 96 | 
            -
             | 
| 99 | 
            +
             | 
| 97 100 | 
             
                    Returns:
         | 
| 98 101 | 
             
                        Dictionary mapping tiers to their directories
         | 
| 99 102 | 
             
                    """
         | 
| 100 | 
            -
             | 
| 103 | 
            +
             | 
| 104 | 
            +
                    async def check_tier_dir(
         | 
| 105 | 
            +
                        tier: AgentTier, path: Path
         | 
| 106 | 
            +
                    ) -> Tuple[AgentTier, Optional[Path]]:
         | 
| 101 107 | 
             
                        """Check if a tier directory exists."""
         | 
| 102 108 | 
             
                        if path.exists():
         | 
| 103 109 | 
             
                            logger.debug(f"Found {tier.value.upper()} agents at: {path}")
         | 
| 104 110 | 
             
                            return tier, path
         | 
| 105 111 | 
             
                        return tier, None
         | 
| 106 | 
            -
             | 
| 112 | 
            +
             | 
| 107 113 | 
             
                    # Define tier paths
         | 
| 108 114 | 
             
                    tier_paths = [
         | 
| 109 | 
            -
                        (AgentTier.PROJECT, Path.cwd() /  | 
| 110 | 
            -
                        ( | 
| 111 | 
            -
             | 
| 115 | 
            +
                        (AgentTier.PROJECT, Path.cwd() / get_path_manager().CONFIG_DIR / "agents"),
         | 
| 116 | 
            +
                        (
         | 
| 117 | 
            +
                            AgentTier.USER,
         | 
| 118 | 
            +
                            (
         | 
| 119 | 
            +
                                get_path_manager().get_user_config_dir() / "agents"
         | 
| 120 | 
            +
                                if get_path_manager().get_user_config_dir()
         | 
| 121 | 
            +
                                else None
         | 
| 122 | 
            +
                            ),
         | 
| 123 | 
            +
                        ),
         | 
| 124 | 
            +
                        (AgentTier.SYSTEM, Path(__file__).parent / "templates"),
         | 
| 112 125 | 
             
                    ]
         | 
| 113 | 
            -
             | 
| 126 | 
            +
             | 
| 114 127 | 
             
                    # Check all tiers in parallel
         | 
| 115 128 | 
             
                    results = await asyncio.gather(
         | 
| 116 129 | 
             
                        *[check_tier_dir(tier, path) for tier, path in tier_paths if path],
         | 
| 117 | 
            -
                        return_exceptions=True
         | 
| 130 | 
            +
                        return_exceptions=True,
         | 
| 118 131 | 
             
                    )
         | 
| 119 | 
            -
             | 
| 132 | 
            +
             | 
| 120 133 | 
             
                    dirs = {}
         | 
| 121 134 | 
             
                    for result in results:
         | 
| 122 135 | 
             
                        if isinstance(result, Exception):
         | 
| @@ -125,257 +138,258 @@ class AsyncAgentLoader: | |
| 125 138 | 
             
                        tier, path = result
         | 
| 126 139 | 
             
                        if path:
         | 
| 127 140 | 
             
                            dirs[tier] = path
         | 
| 128 | 
            -
             | 
| 129 | 
            -
                    self._metrics[ | 
| 141 | 
            +
             | 
| 142 | 
            +
                    self._metrics["parallel_tiers_scanned"] = len(dirs)
         | 
| 130 143 | 
             
                    return dirs
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                async def load_agents_from_dir_async( | 
| 144 | 
            +
             | 
| 145 | 
            +
                async def load_agents_from_dir_async(
         | 
| 146 | 
            +
                    self, directory: Path, tier: AgentTier
         | 
| 147 | 
            +
                ) -> List[Dict[str, Any]]:
         | 
| 133 148 | 
             
                    """Load all agents from a directory asynchronously.
         | 
| 134 | 
            -
             | 
| 149 | 
            +
             | 
| 135 150 | 
             
                    WHY: Loading multiple agent files sequentially is slow.
         | 
| 136 151 | 
             
                    This method discovers and loads all files in parallel,
         | 
| 137 152 | 
             
                    dramatically reducing I/O wait time.
         | 
| 138 | 
            -
             | 
| 153 | 
            +
             | 
| 139 154 | 
             
                    Args:
         | 
| 140 155 | 
             
                        directory: Directory containing agent files
         | 
| 141 156 | 
             
                        tier: The tier this directory belongs to
         | 
| 142 | 
            -
             | 
| 157 | 
            +
             | 
| 143 158 | 
             
                    Returns:
         | 
| 144 159 | 
             
                        List of loaded agent configurations
         | 
| 145 160 | 
             
                    """
         | 
| 146 161 | 
             
                    if not directory.exists():
         | 
| 147 162 | 
             
                        return []
         | 
| 148 | 
            -
             | 
| 163 | 
            +
             | 
| 149 164 | 
             
                    start_time = time.time()
         | 
| 150 | 
            -
             | 
| 165 | 
            +
             | 
| 151 166 | 
             
                    # Discover agent files (both .json and .md)
         | 
| 152 167 | 
             
                    loop = asyncio.get_event_loop()
         | 
| 153 168 | 
             
                    json_files = await loop.run_in_executor(
         | 
| 154 | 
            -
                        self.executor,
         | 
| 155 | 
            -
                        lambda: list(directory.glob("*.json"))
         | 
| 169 | 
            +
                        self.executor, lambda: list(directory.glob("*.json"))
         | 
| 156 170 | 
             
                    )
         | 
| 157 171 | 
             
                    md_files = await loop.run_in_executor(
         | 
| 158 | 
            -
                        self.executor,
         | 
| 159 | 
            -
                        lambda: list(directory.glob("*.md"))
         | 
| 172 | 
            +
                        self.executor, lambda: list(directory.glob("*.md"))
         | 
| 160 173 | 
             
                    )
         | 
| 161 | 
            -
             | 
| 174 | 
            +
             | 
| 162 175 | 
             
                    all_files = json_files + md_files
         | 
| 163 176 | 
             
                    logger.debug(f"Found {len(all_files)} agent files in {directory}")
         | 
| 164 | 
            -
             | 
| 177 | 
            +
             | 
| 165 178 | 
             
                    if not all_files:
         | 
| 166 179 | 
             
                        return []
         | 
| 167 | 
            -
             | 
| 180 | 
            +
             | 
| 168 181 | 
             
                    # Load all files in parallel
         | 
| 169 182 | 
             
                    async def load_file(file_path: Path) -> Optional[Dict[str, Any]]:
         | 
| 170 183 | 
             
                        """Load a single agent file."""
         | 
| 171 184 | 
             
                        try:
         | 
| 172 | 
            -
                            if file_path.suffix ==  | 
| 185 | 
            +
                            if file_path.suffix == ".json":
         | 
| 173 186 | 
             
                                return await self.load_json_agent_async(file_path)
         | 
| 174 | 
            -
                            elif file_path.suffix ==  | 
| 187 | 
            +
                            elif file_path.suffix == ".md":
         | 
| 175 188 | 
             
                                return await self.load_md_agent_async(file_path)
         | 
| 176 189 | 
             
                            else:
         | 
| 177 190 | 
             
                                return None
         | 
| 178 191 | 
             
                        except Exception as e:
         | 
| 179 192 | 
             
                            logger.error(f"Failed to load {file_path}: {e}")
         | 
| 180 193 | 
             
                            return None
         | 
| 181 | 
            -
             | 
| 194 | 
            +
             | 
| 182 195 | 
             
                    agents = await asyncio.gather(
         | 
| 183 | 
            -
                        *[load_file(f) for f in all_files],
         | 
| 184 | 
            -
                        return_exceptions=False
         | 
| 196 | 
            +
                        *[load_file(f) for f in all_files], return_exceptions=False
         | 
| 185 197 | 
             
                    )
         | 
| 186 | 
            -
             | 
| 198 | 
            +
             | 
| 187 199 | 
             
                    # Filter out None values and add tier information
         | 
| 188 200 | 
             
                    valid_agents = []
         | 
| 189 201 | 
             
                    for agent in agents:
         | 
| 190 202 | 
             
                        if agent:
         | 
| 191 | 
            -
                            agent[ | 
| 192 | 
            -
                            agent_id = agent.get( | 
| 203 | 
            +
                            agent["_tier"] = tier.value
         | 
| 204 | 
            +
                            agent_id = agent.get("agent_id") or agent.get("_agent_name")
         | 
| 193 205 | 
             
                            if agent_id:
         | 
| 194 206 | 
             
                                self._agent_tiers[agent_id] = tier
         | 
| 195 207 | 
             
                            valid_agents.append(agent)
         | 
| 196 | 
            -
             | 
| 208 | 
            +
             | 
| 197 209 | 
             
                    elapsed = (time.time() - start_time) * 1000
         | 
| 198 | 
            -
                    logger.info( | 
| 199 | 
            -
             | 
| 200 | 
            -
                    
         | 
| 210 | 
            +
                    logger.info(
         | 
| 211 | 
            +
                        f"Loaded {len(valid_agents)} agents from {tier.value} tier in {elapsed:.1f}ms"
         | 
| 212 | 
            +
                    )
         | 
| 213 | 
            +
                    self._metrics["agents_loaded"] += len(valid_agents)
         | 
| 214 | 
            +
             | 
| 201 215 | 
             
                    return valid_agents
         | 
| 202 | 
            -
             | 
| 216 | 
            +
             | 
| 203 217 | 
             
                async def load_json_agent_async(self, file_path: Path) -> Optional[Dict[str, Any]]:
         | 
| 204 218 | 
             
                    """Load a JSON agent file asynchronously.
         | 
| 205 | 
            -
             | 
| 219 | 
            +
             | 
| 206 220 | 
             
                    WHY: JSON files require file I/O (async) and parsing (CPU-bound).
         | 
| 207 221 | 
             
                    We use aiofiles for non-blocking reads and thread pool for parsing.
         | 
| 208 | 
            -
             | 
| 222 | 
            +
             | 
| 209 223 | 
             
                    Args:
         | 
| 210 224 | 
             
                        file_path: Path to JSON agent file
         | 
| 211 | 
            -
             | 
| 225 | 
            +
             | 
| 212 226 | 
             
                    Returns:
         | 
| 213 227 | 
             
                        Parsed agent configuration or None if failed
         | 
| 214 228 | 
             
                    """
         | 
| 215 229 | 
             
                    try:
         | 
| 216 230 | 
             
                        # Non-blocking file read
         | 
| 217 | 
            -
                        async with aiofiles.open(file_path,  | 
| 231 | 
            +
                        async with aiofiles.open(file_path, "r") as f:
         | 
| 218 232 | 
             
                            content = await f.read()
         | 
| 219 | 
            -
             | 
| 233 | 
            +
             | 
| 220 234 | 
             
                        # Parse JSON in thread pool (CPU-bound)
         | 
| 221 235 | 
             
                        loop = asyncio.get_event_loop()
         | 
| 222 | 
            -
                        data = await loop.run_in_executor(
         | 
| 223 | 
            -
             | 
| 224 | 
            -
                            json.loads,
         | 
| 225 | 
            -
                            content
         | 
| 226 | 
            -
                        )
         | 
| 227 | 
            -
                        
         | 
| 236 | 
            +
                        data = await loop.run_in_executor(self.executor, json.loads, content)
         | 
| 237 | 
            +
             | 
| 228 238 | 
             
                        # Add metadata
         | 
| 229 | 
            -
                        data[ | 
| 230 | 
            -
                        data[ | 
| 231 | 
            -
                        data[ | 
| 232 | 
            -
             | 
| 233 | 
            -
                        self._metrics[ | 
| 239 | 
            +
                        data["_source_file"] = str(file_path)
         | 
| 240 | 
            +
                        data["_agent_name"] = file_path.stem
         | 
| 241 | 
            +
                        data["_format"] = "json"
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                        self._metrics["async_operations"] += 1
         | 
| 234 244 | 
             
                        return data
         | 
| 235 | 
            -
             | 
| 245 | 
            +
             | 
| 236 246 | 
             
                    except Exception as e:
         | 
| 237 247 | 
             
                        logger.error(f"Failed to load JSON agent {file_path}: {e}")
         | 
| 238 248 | 
             
                        return None
         | 
| 239 | 
            -
             | 
| 249 | 
            +
             | 
| 240 250 | 
             
                async def load_md_agent_async(self, file_path: Path) -> Optional[Dict[str, Any]]:
         | 
| 241 251 | 
             
                    """Load a Markdown agent file with YAML frontmatter asynchronously.
         | 
| 242 | 
            -
             | 
| 252 | 
            +
             | 
| 243 253 | 
             
                    WHY: MD files with frontmatter require parsing both YAML and markdown.
         | 
| 244 254 | 
             
                    Async loading prevents blocking on file I/O.
         | 
| 245 | 
            -
             | 
| 255 | 
            +
             | 
| 246 256 | 
             
                    Args:
         | 
| 247 257 | 
             
                        file_path: Path to MD agent file
         | 
| 248 | 
            -
             | 
| 258 | 
            +
             | 
| 249 259 | 
             
                    Returns:
         | 
| 250 260 | 
             
                        Parsed agent configuration or None if failed
         | 
| 251 261 | 
             
                    """
         | 
| 252 262 | 
             
                    try:
         | 
| 253 263 | 
             
                        # Non-blocking file read
         | 
| 254 | 
            -
                        async with aiofiles.open(file_path,  | 
| 264 | 
            +
                        async with aiofiles.open(file_path, "r") as f:
         | 
| 255 265 | 
             
                            content = await f.read()
         | 
| 256 | 
            -
             | 
| 266 | 
            +
             | 
| 257 267 | 
             
                        # Parse frontmatter in thread pool
         | 
| 258 268 | 
             
                        loop = asyncio.get_event_loop()
         | 
| 259 269 | 
             
                        data = await loop.run_in_executor(
         | 
| 260 | 
            -
                            self.executor,
         | 
| 261 | 
            -
                            self._parse_frontmatter,
         | 
| 262 | 
            -
                            content
         | 
| 270 | 
            +
                            self.executor, self._parse_frontmatter, content
         | 
| 263 271 | 
             
                        )
         | 
| 264 | 
            -
             | 
| 272 | 
            +
             | 
| 265 273 | 
             
                        if data:
         | 
| 266 274 | 
             
                            # Add metadata
         | 
| 267 | 
            -
                            data[ | 
| 268 | 
            -
                            data[ | 
| 269 | 
            -
                            data[ | 
| 270 | 
            -
             | 
| 271 | 
            -
                            self._metrics[ | 
| 272 | 
            -
             | 
| 275 | 
            +
                            data["_source_file"] = str(file_path)
         | 
| 276 | 
            +
                            data["_agent_name"] = file_path.stem
         | 
| 277 | 
            +
                            data["_format"] = "markdown"
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                            self._metrics["async_operations"] += 1
         | 
| 280 | 
            +
             | 
| 273 281 | 
             
                        return data
         | 
| 274 | 
            -
             | 
| 282 | 
            +
             | 
| 275 283 | 
             
                    except Exception as e:
         | 
| 276 284 | 
             
                        logger.error(f"Failed to load MD agent {file_path}: {e}")
         | 
| 277 285 | 
             
                        return None
         | 
| 278 | 
            -
             | 
| 286 | 
            +
             | 
| 279 287 | 
             
                def _parse_frontmatter(self, content: str) -> Optional[Dict[str, Any]]:
         | 
| 280 288 | 
             
                    """Parse YAML frontmatter from markdown content.
         | 
| 281 | 
            -
             | 
| 289 | 
            +
             | 
| 282 290 | 
             
                    This is a CPU-bound operation run in the thread pool.
         | 
| 283 | 
            -
             | 
| 291 | 
            +
             | 
| 284 292 | 
             
                    Args:
         | 
| 285 293 | 
             
                        content: Markdown file content
         | 
| 286 | 
            -
             | 
| 294 | 
            +
             | 
| 287 295 | 
             
                    Returns:
         | 
| 288 296 | 
             
                        Parsed agent data or None if invalid
         | 
| 289 297 | 
             
                    """
         | 
| 290 298 | 
             
                    try:
         | 
| 291 299 | 
             
                        import yaml
         | 
| 292 | 
            -
             | 
| 300 | 
            +
             | 
| 293 301 | 
             
                        # Extract frontmatter
         | 
| 294 | 
            -
                        if not content.startswith( | 
| 302 | 
            +
                        if not content.startswith("---"):
         | 
| 295 303 | 
             
                            return None
         | 
| 296 | 
            -
             | 
| 297 | 
            -
                        end_marker = content.find( | 
| 304 | 
            +
             | 
| 305 | 
            +
                        end_marker = content.find("\n---\n", 4)
         | 
| 298 306 | 
             
                        if end_marker == -1:
         | 
| 299 307 | 
             
                            return None
         | 
| 300 | 
            -
             | 
| 308 | 
            +
             | 
| 301 309 | 
             
                        frontmatter = content[4:end_marker]
         | 
| 302 | 
            -
                        instructions = content[end_marker + 5:].strip()
         | 
| 303 | 
            -
             | 
| 310 | 
            +
                        instructions = content[end_marker + 5 :].strip()
         | 
| 311 | 
            +
             | 
| 304 312 | 
             
                        # Parse YAML
         | 
| 305 313 | 
             
                        data = yaml.safe_load(frontmatter)
         | 
| 306 314 | 
             
                        if not isinstance(data, dict):
         | 
| 307 315 | 
             
                            return None
         | 
| 308 | 
            -
             | 
| 316 | 
            +
             | 
| 309 317 | 
             
                        # Add instructions
         | 
| 310 | 
            -
                        data[ | 
| 311 | 
            -
             | 
| 318 | 
            +
                        data["instructions"] = instructions
         | 
| 319 | 
            +
             | 
| 312 320 | 
             
                        # Map fields for compatibility
         | 
| 313 | 
            -
                        if  | 
| 314 | 
            -
                            data[ | 
| 315 | 
            -
             | 
| 321 | 
            +
                        if "name" in data:
         | 
| 322 | 
            +
                            data["agent_id"] = data["name"]
         | 
| 323 | 
            +
             | 
| 316 324 | 
             
                        return data
         | 
| 317 | 
            -
             | 
| 325 | 
            +
             | 
| 318 326 | 
             
                    except Exception as e:
         | 
| 319 327 | 
             
                        logger.error(f"Failed to parse frontmatter: {e}")
         | 
| 320 328 | 
             
                        return None
         | 
| 321 | 
            -
             | 
| 329 | 
            +
             | 
| 322 330 | 
             
                async def load_all_agents_async(self) -> Dict[str, Dict[str, Any]]:
         | 
| 323 331 | 
             
                    """Load all agents from all tiers in parallel.
         | 
| 324 | 
            -
             | 
| 332 | 
            +
             | 
| 325 333 | 
             
                    WHY: This is the main performance optimization - loading agents
         | 
| 326 334 | 
             
                    from PROJECT, USER, and SYSTEM tiers simultaneously reduces
         | 
| 327 335 | 
             
                    total load time to that of the slowest tier.
         | 
| 328 | 
            -
             | 
| 336 | 
            +
             | 
| 329 337 | 
             
                    Returns:
         | 
| 330 338 | 
             
                        Dictionary of all loaded agents by ID
         | 
| 331 339 | 
             
                    """
         | 
| 332 340 | 
             
                    start_time = time.time()
         | 
| 333 | 
            -
             | 
| 341 | 
            +
             | 
| 334 342 | 
             
                    # Discover directories in parallel
         | 
| 335 343 | 
             
                    tier_dirs = await self.discover_agent_dirs_async()
         | 
| 336 | 
            -
             | 
| 344 | 
            +
             | 
| 337 345 | 
             
                    if not tier_dirs:
         | 
| 338 346 | 
             
                        logger.warning("No agent directories found")
         | 
| 339 347 | 
             
                        return {}
         | 
| 340 | 
            -
             | 
| 348 | 
            +
             | 
| 341 349 | 
             
                    # Load agents from all tiers in parallel
         | 
| 342 350 | 
             
                    tier_agents = await asyncio.gather(
         | 
| 343 | 
            -
                        *[ | 
| 344 | 
            -
             | 
| 345 | 
            -
             | 
| 351 | 
            +
                        *[
         | 
| 352 | 
            +
                            self.load_agents_from_dir_async(directory, tier)
         | 
| 353 | 
            +
                            for tier, directory in tier_dirs.items()
         | 
| 354 | 
            +
                        ],
         | 
| 355 | 
            +
                        return_exceptions=False,
         | 
| 346 356 | 
             
                    )
         | 
| 347 | 
            -
             | 
| 357 | 
            +
             | 
| 348 358 | 
             
                    # Merge agents with tier precedence (PROJECT > USER > SYSTEM)
         | 
| 349 359 | 
             
                    merged_agents = {}
         | 
| 350 | 
            -
             | 
| 360 | 
            +
             | 
| 351 361 | 
             
                    # Process in reverse precedence order
         | 
| 352 362 | 
             
                    for agents in reversed(tier_agents):
         | 
| 353 363 | 
             
                        for agent in agents:
         | 
| 354 | 
            -
                            agent_id = agent.get( | 
| 364 | 
            +
                            agent_id = agent.get("agent_id") or agent.get("_agent_name")
         | 
| 355 365 | 
             
                            if agent_id:
         | 
| 356 366 | 
             
                                # Check if already loaded from higher precedence tier
         | 
| 357 367 | 
             
                                if agent_id in merged_agents:
         | 
| 358 | 
            -
                                    existing_tier = merged_agents[agent_id].get( | 
| 359 | 
            -
                                    new_tier = agent.get( | 
| 360 | 
            -
                                    logger.debug( | 
| 368 | 
            +
                                    existing_tier = merged_agents[agent_id].get("_tier", "unknown")
         | 
| 369 | 
            +
                                    new_tier = agent.get("_tier", "unknown")
         | 
| 370 | 
            +
                                    logger.debug(
         | 
| 371 | 
            +
                                        f"Agent {agent_id}: keeping {existing_tier} version, skipping {new_tier}"
         | 
| 372 | 
            +
                                    )
         | 
| 361 373 | 
             
                                else:
         | 
| 362 374 | 
             
                                    merged_agents[agent_id] = agent
         | 
| 363 | 
            -
             | 
| 375 | 
            +
             | 
| 364 376 | 
             
                    elapsed = (time.time() - start_time) * 1000
         | 
| 365 | 
            -
                    self._metrics[ | 
| 366 | 
            -
             | 
| 377 | 
            +
                    self._metrics["time_saved_ms"] = max(
         | 
| 378 | 
            +
                        0, (500 - elapsed)
         | 
| 379 | 
            +
                    )  # Assume 500ms for sync
         | 
| 380 | 
            +
             | 
| 367 381 | 
             
                    logger.info(
         | 
| 368 382 | 
             
                        f"Async loaded {len(merged_agents)} agents in {elapsed:.1f}ms "
         | 
| 369 383 | 
             
                        f"(~{self._metrics['time_saved_ms']:.0f}ms saved)"
         | 
| 370 384 | 
             
                    )
         | 
| 371 | 
            -
             | 
| 385 | 
            +
             | 
| 372 386 | 
             
                    self._agent_registry = merged_agents
         | 
| 373 387 | 
             
                    return merged_agents
         | 
| 374 | 
            -
             | 
| 388 | 
            +
             | 
| 375 389 | 
             
                async def cleanup(self):
         | 
| 376 390 | 
             
                    """Clean up resources."""
         | 
| 377 391 | 
             
                    self.executor.shutdown(wait=False)
         | 
| 378 | 
            -
             | 
| 392 | 
            +
             | 
| 379 393 | 
             
                def get_metrics(self) -> Dict[str, Any]:
         | 
| 380 394 | 
             
                    """Get performance metrics."""
         | 
| 381 395 | 
             
                    return self._metrics.copy()
         | 
| @@ -384,33 +398,37 @@ class AsyncAgentLoader: | |
| 384 398 | 
             
            # Convenience function to load agents asynchronously from sync code
         | 
| 385 399 | 
             
            def load_agents_async() -> Dict[str, Dict[str, Any]]:
         | 
| 386 400 | 
             
                """Load all agents using async operations.
         | 
| 387 | 
            -
             | 
| 401 | 
            +
             | 
| 388 402 | 
             
                WHY: This wrapper allows async agent loading from synchronous code,
         | 
| 389 403 | 
             
                providing significant performance improvements without requiring
         | 
| 390 404 | 
             
                a full async refactor of the codebase.
         | 
| 391 | 
            -
             | 
| 405 | 
            +
             | 
| 392 406 | 
             
                Returns:
         | 
| 393 407 | 
             
                    Dictionary of loaded agents by ID
         | 
| 394 408 | 
             
                """
         | 
| 409 | 
            +
             | 
| 395 410 | 
             
                async def run_loading():
         | 
| 396 411 | 
             
                    loader = AsyncAgentLoader()
         | 
| 397 412 | 
             
                    try:
         | 
| 398 413 | 
             
                        agents = await loader.load_all_agents_async()
         | 
| 399 414 | 
             
                        metrics = loader.get_metrics()
         | 
| 400 | 
            -
             | 
| 401 | 
            -
                        if metrics.get( | 
| 402 | 
            -
                            logger.info( | 
| 403 | 
            -
             | 
| 415 | 
            +
             | 
| 416 | 
            +
                        if metrics.get("time_saved_ms", 0) > 0:
         | 
| 417 | 
            +
                            logger.info(
         | 
| 418 | 
            +
                                f"Async loading saved approximately {metrics['time_saved_ms']:.0f}ms"
         | 
| 419 | 
            +
                            )
         | 
| 420 | 
            +
             | 
| 404 421 | 
             
                        return agents
         | 
| 405 422 | 
             
                    finally:
         | 
| 406 423 | 
             
                        await loader.cleanup()
         | 
| 407 | 
            -
             | 
| 424 | 
            +
             | 
| 408 425 | 
             
                # Run in event loop
         | 
| 409 426 | 
             
                try:
         | 
| 410 427 | 
             
                    loop = asyncio.get_event_loop()
         | 
| 411 428 | 
             
                    if loop.is_running():
         | 
| 412 429 | 
             
                        # If loop is already running, use thread pool
         | 
| 413 430 | 
             
                        import concurrent.futures
         | 
| 431 | 
            +
             | 
| 414 432 | 
             
                        with concurrent.futures.ThreadPoolExecutor() as executor:
         | 
| 415 433 | 
             
                            future = executor.submit(asyncio.run, run_loading())
         | 
| 416 434 | 
             
                            return future.result()
         | 
| @@ -418,4 +436,4 @@ def load_agents_async() -> Dict[str, Dict[str, Any]]: | |
| 418 436 | 
             
                        return loop.run_until_complete(run_loading())
         | 
| 419 437 | 
             
                except RuntimeError:
         | 
| 420 438 | 
             
                    # No event loop, create new one
         | 
| 421 | 
            -
                    return asyncio.run(run_loading())
         | 
| 439 | 
            +
                    return asyncio.run(run_loading())
         |