claude-mpm 3.9.9__py3-none-any.whl → 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -1,33 +1,37 @@ | |
| 1 1 | 
             
            """Framework loader for Claude MPM."""
         | 
| 2 2 |  | 
| 3 | 
            -
            import os
         | 
| 4 3 | 
             
            import logging
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            from typing import Optional, Dict, Any
         | 
| 4 | 
            +
            import os
         | 
| 7 5 | 
             
            from datetime import datetime
         | 
| 6 | 
            +
            from pathlib import Path
         | 
| 7 | 
            +
            from typing import Any, Dict, Optional
         | 
| 8 8 |  | 
| 9 9 | 
             
            from ..utils.imports import safe_import
         | 
| 10 10 |  | 
| 11 11 | 
             
            # Import with fallback support - using absolute imports as primary since we're at module level
         | 
| 12 | 
            -
            get_logger = safe_import( | 
| 13 | 
            -
            AgentRegistryAdapter = safe_import( | 
| 12 | 
            +
            get_logger = safe_import("claude_mpm.core.logger", "core.logger", ["get_logger"])
         | 
| 13 | 
            +
            AgentRegistryAdapter = safe_import(
         | 
| 14 | 
            +
                "claude_mpm.core.agent_registry", "core.agent_registry", ["AgentRegistryAdapter"]
         | 
| 15 | 
            +
            )
         | 
| 14 16 |  | 
| 15 17 |  | 
| 16 18 | 
             
            class FrameworkLoader:
         | 
| 17 19 | 
             
                """
         | 
| 18 20 | 
             
                Load and prepare framework instructions for injection.
         | 
| 19 | 
            -
             | 
| 21 | 
            +
             | 
| 20 22 | 
             
                This component handles:
         | 
| 21 23 | 
             
                1. Finding the framework (claude-multiagent-pm)
         | 
| 22 24 | 
             
                2. Loading INSTRUCTIONS.md instructions
         | 
| 23 25 | 
             
                3. Preparing agent definitions
         | 
| 24 26 | 
             
                4. Formatting for injection
         | 
| 25 27 | 
             
                """
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                def __init__( | 
| 28 | 
            +
             | 
| 29 | 
            +
                def __init__(
         | 
| 30 | 
            +
                    self, framework_path: Optional[Path] = None, agents_dir: Optional[Path] = None
         | 
| 31 | 
            +
                ):
         | 
| 28 32 | 
             
                    """
         | 
| 29 33 | 
             
                    Initialize framework loader.
         | 
| 30 | 
            -
             | 
| 34 | 
            +
             | 
| 31 35 | 
             
                    Args:
         | 
| 32 36 | 
             
                        framework_path: Explicit path to framework (auto-detected if None)
         | 
| 33 37 | 
             
                        agents_dir: Custom agents directory (overrides framework agents)
         | 
| @@ -38,10 +42,10 @@ class FrameworkLoader: | |
| 38 42 | 
             
                    self.framework_version = None
         | 
| 39 43 | 
             
                    self.framework_last_modified = None
         | 
| 40 44 | 
             
                    self.framework_content = self._load_framework_content()
         | 
| 41 | 
            -
             | 
| 45 | 
            +
             | 
| 42 46 | 
             
                    # Initialize agent registry
         | 
| 43 47 | 
             
                    self.agent_registry = AgentRegistryAdapter(self.framework_path)
         | 
| 44 | 
            -
             | 
| 48 | 
            +
             | 
| 45 49 | 
             
                def _detect_framework_path(self) -> Optional[Path]:
         | 
| 46 50 | 
             
                    """Auto-detect claude-mpm framework."""
         | 
| 47 51 | 
             
                    # First check if we're in claude-mpm project
         | 
| @@ -54,7 +58,7 @@ class FrameworkLoader: | |
| 54 58 | 
             
                                    self.logger.info(f"Using claude-mpm at: {parent}")
         | 
| 55 59 | 
             
                                    return parent
         | 
| 56 60 | 
             
                                break
         | 
| 57 | 
            -
             | 
| 61 | 
            +
             | 
| 58 62 | 
             
                    # Otherwise check common locations for claude-mpm
         | 
| 59 63 | 
             
                    candidates = [
         | 
| 60 64 | 
             
                        # Development location
         | 
| @@ -62,26 +66,24 @@ class FrameworkLoader: | |
| 62 66 | 
             
                        # Current directory
         | 
| 63 67 | 
             
                        Path.cwd() / "claude-mpm",
         | 
| 64 68 | 
             
                    ]
         | 
| 65 | 
            -
             | 
| 69 | 
            +
             | 
| 66 70 | 
             
                    for candidate in candidates:
         | 
| 67 71 | 
             
                        if candidate and candidate.exists():
         | 
| 68 72 | 
             
                            # Check for claude-mpm agents directory
         | 
| 69 73 | 
             
                            if (candidate / "src" / "claude_mpm" / "agents").exists():
         | 
| 70 74 | 
             
                                self.logger.info(f"Found claude-mpm at: {candidate}")
         | 
| 71 75 | 
             
                                return candidate
         | 
| 72 | 
            -
             | 
| 76 | 
            +
             | 
| 73 77 | 
             
                    self.logger.warning("Framework not found, will use minimal instructions")
         | 
| 74 78 | 
             
                    return None
         | 
| 75 | 
            -
             | 
| 79 | 
            +
             | 
| 76 80 | 
             
                def _get_npm_global_path(self) -> Optional[Path]:
         | 
| 77 81 | 
             
                    """Get npm global installation path."""
         | 
| 78 82 | 
             
                    try:
         | 
| 79 83 | 
             
                        import subprocess
         | 
| 84 | 
            +
             | 
| 80 85 | 
             
                        result = subprocess.run(
         | 
| 81 | 
            -
                            ["npm", "root", "-g"],
         | 
| 82 | 
            -
                            capture_output=True,
         | 
| 83 | 
            -
                            text=True,
         | 
| 84 | 
            -
                            timeout=5
         | 
| 86 | 
            +
                            ["npm", "root", "-g"], capture_output=True, text=True, timeout=5
         | 
| 85 87 | 
             
                        )
         | 
| 86 88 | 
             
                        if result.returncode == 0:
         | 
| 87 89 | 
             
                            npm_root = Path(result.stdout.strip())
         | 
| @@ -89,159 +91,204 @@ class FrameworkLoader: | |
| 89 91 | 
             
                    except:
         | 
| 90 92 | 
             
                        pass
         | 
| 91 93 | 
             
                    return None
         | 
| 92 | 
            -
             | 
| 93 | 
            -
                def _discover_framework_paths( | 
| 94 | 
            +
             | 
| 95 | 
            +
                def _discover_framework_paths(
         | 
| 96 | 
            +
                    self,
         | 
| 97 | 
            +
                ) -> tuple[Optional[Path], Optional[Path], Optional[Path]]:
         | 
| 94 98 | 
             
                    """
         | 
| 95 99 | 
             
                    Discover agent directories based on priority.
         | 
| 96 | 
            -
             | 
| 100 | 
            +
             | 
| 97 101 | 
             
                    Returns:
         | 
| 98 102 | 
             
                        Tuple of (agents_dir, templates_dir, main_dir)
         | 
| 99 103 | 
             
                    """
         | 
| 100 104 | 
             
                    agents_dir = None
         | 
| 101 105 | 
             
                    templates_dir = None
         | 
| 102 106 | 
             
                    main_dir = None
         | 
| 103 | 
            -
             | 
| 107 | 
            +
             | 
| 104 108 | 
             
                    if self.agents_dir and self.agents_dir.exists():
         | 
| 105 109 | 
             
                        agents_dir = self.agents_dir
         | 
| 106 110 | 
             
                        self.logger.info(f"Using custom agents directory: {agents_dir}")
         | 
| 107 111 | 
             
                    elif self.framework_path:
         | 
| 108 112 | 
             
                        # Prioritize templates directory over main agents directory
         | 
| 109 | 
            -
                        templates_dir =  | 
| 113 | 
            +
                        templates_dir = (
         | 
| 114 | 
            +
                            self.framework_path / "src" / "claude_mpm" / "agents" / "templates"
         | 
| 115 | 
            +
                        )
         | 
| 110 116 | 
             
                        main_dir = self.framework_path / "src" / "claude_mpm" / "agents"
         | 
| 111 | 
            -
             | 
| 117 | 
            +
             | 
| 112 118 | 
             
                        if templates_dir.exists() and any(templates_dir.glob("*.md")):
         | 
| 113 119 | 
             
                            agents_dir = templates_dir
         | 
| 114 120 | 
             
                            self.logger.info(f"Using agents from templates directory: {agents_dir}")
         | 
| 115 121 | 
             
                        elif main_dir.exists() and any(main_dir.glob("*.md")):
         | 
| 116 122 | 
             
                            agents_dir = main_dir
         | 
| 117 123 | 
             
                            self.logger.info(f"Using agents from main directory: {agents_dir}")
         | 
| 118 | 
            -
             | 
| 124 | 
            +
             | 
| 119 125 | 
             
                    return agents_dir, templates_dir, main_dir
         | 
| 120 | 
            -
             | 
| 126 | 
            +
             | 
| 121 127 | 
             
                def _try_load_file(self, file_path: Path, file_type: str) -> Optional[str]:
         | 
| 122 128 | 
             
                    """
         | 
| 123 129 | 
             
                    Try to load a file with error handling.
         | 
| 124 | 
            -
             | 
| 130 | 
            +
             | 
| 125 131 | 
             
                    Args:
         | 
| 126 132 | 
             
                        file_path: Path to the file to load
         | 
| 127 133 | 
             
                        file_type: Description of file type for logging
         | 
| 128 | 
            -
             | 
| 134 | 
            +
             | 
| 129 135 | 
             
                    Returns:
         | 
| 130 136 | 
             
                        File content if successful, None otherwise
         | 
| 131 137 | 
             
                    """
         | 
| 132 138 | 
             
                    try:
         | 
| 133 139 | 
             
                        content = file_path.read_text()
         | 
| 134 | 
            -
                        if hasattr(self.logger,  | 
| 140 | 
            +
                        if hasattr(self.logger, "level") and self.logger.level <= logging.INFO:
         | 
| 135 141 | 
             
                            self.logger.info(f"Loaded {file_type} from: {file_path}")
         | 
| 136 | 
            -
             | 
| 142 | 
            +
             | 
| 137 143 | 
             
                        # Extract metadata if present
         | 
| 138 144 | 
             
                        import re
         | 
| 139 | 
            -
             | 
| 145 | 
            +
             | 
| 146 | 
            +
                        version_match = re.search(r"<!-- FRAMEWORK_VERSION: (\d+) -->", content)
         | 
| 140 147 | 
             
                        if version_match:
         | 
| 141 | 
            -
                            version = version_match.group( | 
| 148 | 
            +
                            version = version_match.group(
         | 
| 149 | 
            +
                                1
         | 
| 150 | 
            +
                            )  # Keep as string to preserve leading zeros
         | 
| 142 151 | 
             
                            self.logger.info(f"Framework version: {version}")
         | 
| 143 152 | 
             
                            # Store framework version if this is the main INSTRUCTIONS.md
         | 
| 144 | 
            -
                            if  | 
| 153 | 
            +
                            if "INSTRUCTIONS.md" in str(file_path):
         | 
| 145 154 | 
             
                                self.framework_version = version
         | 
| 146 | 
            -
             | 
| 155 | 
            +
             | 
| 147 156 | 
             
                        # Extract modification timestamp
         | 
| 148 | 
            -
                        timestamp_match = re.search(r | 
| 157 | 
            +
                        timestamp_match = re.search(r"<!-- LAST_MODIFIED: ([^>]+) -->", content)
         | 
| 149 158 | 
             
                        if timestamp_match:
         | 
| 150 159 | 
             
                            timestamp = timestamp_match.group(1).strip()
         | 
| 151 160 | 
             
                            self.logger.info(f"Last modified: {timestamp}")
         | 
| 152 161 | 
             
                            # Store timestamp if this is the main INSTRUCTIONS.md
         | 
| 153 | 
            -
                            if  | 
| 162 | 
            +
                            if "INSTRUCTIONS.md" in str(file_path):
         | 
| 154 163 | 
             
                                self.framework_last_modified = timestamp
         | 
| 155 | 
            -
             | 
| 164 | 
            +
             | 
| 156 165 | 
             
                        return content
         | 
| 157 166 | 
             
                    except Exception as e:
         | 
| 158 | 
            -
                        if hasattr(self.logger,  | 
| 167 | 
            +
                        if hasattr(self.logger, "level") and self.logger.level <= logging.ERROR:
         | 
| 159 168 | 
             
                            self.logger.error(f"Failed to load {file_type}: {e}")
         | 
| 160 169 | 
             
                        return None
         | 
| 161 | 
            -
             | 
| 170 | 
            +
             | 
| 162 171 | 
             
                def _load_instructions_file(self, content: Dict[str, Any]) -> None:
         | 
| 163 172 | 
             
                    """
         | 
| 164 173 | 
             
                    Load INSTRUCTIONS.md or legacy CLAUDE.md from working directory.
         | 
| 165 | 
            -
             | 
| 174 | 
            +
             | 
| 166 175 | 
             
                    NOTE: We no longer load CLAUDE.md since Claude Code already picks it up automatically.
         | 
| 167 176 | 
             
                    This prevents duplication of instructions.
         | 
| 168 | 
            -
             | 
| 177 | 
            +
             | 
| 169 178 | 
             
                    Args:
         | 
| 170 179 | 
             
                        content: Dictionary to update with loaded instructions
         | 
| 171 180 | 
             
                    """
         | 
| 172 181 | 
             
                    # Disabled - Claude Code already reads CLAUDE.md automatically
         | 
| 173 182 | 
             
                    # We don't need to duplicate it in the PM instructions
         | 
| 174 183 | 
             
                    pass
         | 
| 175 | 
            -
             | 
| 184 | 
            +
             | 
| 176 185 | 
             
                def _load_workflow_instructions(self, content: Dict[str, Any]) -> None:
         | 
| 177 186 | 
             
                    """
         | 
| 178 187 | 
             
                    Load WORKFLOW.md with project-specific override support.
         | 
| 179 | 
            -
             | 
| 188 | 
            +
             | 
| 180 189 | 
             
                    Precedence:
         | 
| 181 190 | 
             
                    1. Project-specific: .claude-mpm/agents/WORKFLOW.md
         | 
| 182 191 | 
             
                    2. System default: src/claude_mpm/agents/WORKFLOW.md
         | 
| 183 | 
            -
             | 
| 192 | 
            +
             | 
| 184 193 | 
             
                    Args:
         | 
| 185 194 | 
             
                        content: Dictionary to update with workflow instructions
         | 
| 186 195 | 
             
                    """
         | 
| 187 196 | 
             
                    # Check for project-specific workflow first
         | 
| 188 197 | 
             
                    project_workflow_path = Path.cwd() / ".claude-mpm" / "agents" / "WORKFLOW.md"
         | 
| 189 198 | 
             
                    if project_workflow_path.exists():
         | 
| 190 | 
            -
                        loaded_content = self._try_load_file( | 
| 199 | 
            +
                        loaded_content = self._try_load_file(
         | 
| 200 | 
            +
                            project_workflow_path, "project-specific WORKFLOW.md"
         | 
| 201 | 
            +
                        )
         | 
| 191 202 | 
             
                        if loaded_content:
         | 
| 192 203 | 
             
                            content["workflow_instructions"] = loaded_content
         | 
| 193 204 | 
             
                            content["project_workflow"] = "project"
         | 
| 194 205 | 
             
                            self.logger.info("Using project-specific WORKFLOW.md")
         | 
| 195 206 | 
             
                            return
         | 
| 196 | 
            -
             | 
| 207 | 
            +
             | 
| 197 208 | 
             
                    # Fall back to system workflow
         | 
| 198 209 | 
             
                    if self.framework_path:
         | 
| 199 | 
            -
                        system_workflow_path =  | 
| 210 | 
            +
                        system_workflow_path = (
         | 
| 211 | 
            +
                            self.framework_path / "src" / "claude_mpm" / "agents" / "WORKFLOW.md"
         | 
| 212 | 
            +
                        )
         | 
| 200 213 | 
             
                        if system_workflow_path.exists():
         | 
| 201 | 
            -
                            loaded_content = self._try_load_file( | 
| 214 | 
            +
                            loaded_content = self._try_load_file(
         | 
| 215 | 
            +
                                system_workflow_path, "system WORKFLOW.md"
         | 
| 216 | 
            +
                            )
         | 
| 202 217 | 
             
                            if loaded_content:
         | 
| 203 218 | 
             
                                content["workflow_instructions"] = loaded_content
         | 
| 204 219 | 
             
                                content["project_workflow"] = "system"
         | 
| 205 220 | 
             
                                self.logger.info("Using system WORKFLOW.md")
         | 
| 206 | 
            -
             | 
| 221 | 
            +
             | 
| 207 222 | 
             
                def _load_memory_instructions(self, content: Dict[str, Any]) -> None:
         | 
| 208 223 | 
             
                    """
         | 
| 209 224 | 
             
                    Load MEMORY.md with project-specific override support.
         | 
| 210 | 
            -
             | 
| 225 | 
            +
             | 
| 211 226 | 
             
                    Precedence:
         | 
| 212 227 | 
             
                    1. Project-specific: .claude-mpm/agents/MEMORY.md
         | 
| 213 228 | 
             
                    2. System default: src/claude_mpm/agents/MEMORY.md
         | 
| 214 | 
            -
             | 
| 229 | 
            +
             | 
| 215 230 | 
             
                    Args:
         | 
| 216 231 | 
             
                        content: Dictionary to update with memory instructions
         | 
| 217 232 | 
             
                    """
         | 
| 218 233 | 
             
                    # Check for project-specific memory instructions first
         | 
| 219 234 | 
             
                    project_memory_path = Path.cwd() / ".claude-mpm" / "agents" / "MEMORY.md"
         | 
| 220 235 | 
             
                    if project_memory_path.exists():
         | 
| 221 | 
            -
                        loaded_content = self._try_load_file( | 
| 236 | 
            +
                        loaded_content = self._try_load_file(
         | 
| 237 | 
            +
                            project_memory_path, "project-specific MEMORY.md"
         | 
| 238 | 
            +
                        )
         | 
| 222 239 | 
             
                        if loaded_content:
         | 
| 223 240 | 
             
                            content["memory_instructions"] = loaded_content
         | 
| 224 241 | 
             
                            content["project_memory"] = "project"
         | 
| 225 242 | 
             
                            self.logger.info("Using project-specific MEMORY.md")
         | 
| 226 243 | 
             
                            return
         | 
| 227 | 
            -
             | 
| 244 | 
            +
             | 
| 228 245 | 
             
                    # Fall back to system memory instructions
         | 
| 229 246 | 
             
                    if self.framework_path:
         | 
| 230 | 
            -
                        system_memory_path =  | 
| 247 | 
            +
                        system_memory_path = (
         | 
| 248 | 
            +
                            self.framework_path / "src" / "claude_mpm" / "agents" / "MEMORY.md"
         | 
| 249 | 
            +
                        )
         | 
| 231 250 | 
             
                        if system_memory_path.exists():
         | 
| 232 | 
            -
                            loaded_content = self._try_load_file( | 
| 251 | 
            +
                            loaded_content = self._try_load_file(
         | 
| 252 | 
            +
                                system_memory_path, "system MEMORY.md"
         | 
| 253 | 
            +
                            )
         | 
| 233 254 | 
             
                            if loaded_content:
         | 
| 234 255 | 
             
                                content["memory_instructions"] = loaded_content
         | 
| 235 256 | 
             
                                content["project_memory"] = "system"
         | 
| 236 257 | 
             
                                self.logger.info("Using system MEMORY.md")
         | 
| 237 258 |  | 
| 238 | 
            -
                def  | 
| 259 | 
            +
                def _load_actual_memories(self, content: Dict[str, Any]) -> None:
         | 
| 239 260 | 
             
                    """
         | 
| 240 | 
            -
                    Load  | 
| 261 | 
            +
                    Load actual PM memories from .claude-mpm/memories/PM.md.
         | 
| 241 262 |  | 
| 263 | 
            +
                    These are the actual memories/knowledge that the PM should have,
         | 
| 264 | 
            +
                    as opposed to the memory system instructions in MEMORY.md.
         | 
| 265 | 
            +
                    
         | 
| 266 | 
            +
                    Args:
         | 
| 267 | 
            +
                        content: Dictionary to update with actual memories
         | 
| 268 | 
            +
                    """
         | 
| 269 | 
            +
                    memories_path = Path.cwd() / ".claude-mpm" / "memories" / "PM.md"
         | 
| 270 | 
            +
                    if memories_path.exists():
         | 
| 271 | 
            +
                        loaded_content = self._try_load_file(
         | 
| 272 | 
            +
                            memories_path, "PM memories"
         | 
| 273 | 
            +
                        )
         | 
| 274 | 
            +
                        if loaded_content:
         | 
| 275 | 
            +
                            content["actual_memories"] = loaded_content
         | 
| 276 | 
            +
                            self.logger.info(f"Loaded PM memories from: {memories_path}")
         | 
| 277 | 
            +
                            # Log memory size for monitoring
         | 
| 278 | 
            +
                            memory_size = len(loaded_content.encode('utf-8'))
         | 
| 279 | 
            +
                            self.logger.debug(f"PM memory size: {memory_size:,} bytes")
         | 
| 280 | 
            +
                    else:
         | 
| 281 | 
            +
                        self.logger.debug(f"No PM memories found at: {memories_path}")
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                def _load_single_agent(
         | 
| 284 | 
            +
                    self, agent_file: Path
         | 
| 285 | 
            +
                ) -> tuple[Optional[str], Optional[str]]:
         | 
| 286 | 
            +
                    """
         | 
| 287 | 
            +
                    Load a single agent file.
         | 
| 288 | 
            +
             | 
| 242 289 | 
             
                    Args:
         | 
| 243 290 | 
             
                        agent_file: Path to the agent file
         | 
| 244 | 
            -
             | 
| 291 | 
            +
             | 
| 245 292 | 
             
                    Returns:
         | 
| 246 293 | 
             
                        Tuple of (agent_name, agent_content) or (None, None) on failure
         | 
| 247 294 | 
             
                    """
         | 
| @@ -256,11 +303,13 @@ class FrameworkLoader: | |
| 256 303 | 
             
                    except Exception as e:
         | 
| 257 304 | 
             
                        self.logger.error(f"Failed to load agent {agent_file}: {e}")
         | 
| 258 305 | 
             
                        return None, None
         | 
| 259 | 
            -
             | 
| 260 | 
            -
                def _load_base_agent_fallback( | 
| 306 | 
            +
             | 
| 307 | 
            +
                def _load_base_agent_fallback(
         | 
| 308 | 
            +
                    self, content: Dict[str, Any], main_dir: Optional[Path]
         | 
| 309 | 
            +
                ) -> None:
         | 
| 261 310 | 
             
                    """
         | 
| 262 311 | 
             
                    Load base_agent.md from main directory as fallback.
         | 
| 263 | 
            -
             | 
| 312 | 
            +
             | 
| 264 313 | 
             
                    Args:
         | 
| 265 314 | 
             
                        content: Dictionary to update with base agent
         | 
| 266 315 | 
             
                        main_dir: Main agents directory path
         | 
| @@ -271,12 +320,17 @@ class FrameworkLoader: | |
| 271 320 | 
             
                            agent_name, agent_content = self._load_single_agent(base_agent_file)
         | 
| 272 321 | 
             
                            if agent_name and agent_content:
         | 
| 273 322 | 
             
                                content["agents"][agent_name] = agent_content
         | 
| 274 | 
            -
             | 
| 275 | 
            -
                def _load_agents_directory( | 
| 276 | 
            -
             | 
| 323 | 
            +
             | 
| 324 | 
            +
                def _load_agents_directory(
         | 
| 325 | 
            +
                    self,
         | 
| 326 | 
            +
                    content: Dict[str, Any],
         | 
| 327 | 
            +
                    agents_dir: Optional[Path],
         | 
| 328 | 
            +
                    templates_dir: Optional[Path],
         | 
| 329 | 
            +
                    main_dir: Optional[Path],
         | 
| 330 | 
            +
                ) -> None:
         | 
| 277 331 | 
             
                    """
         | 
| 278 332 | 
             
                    Load agent definitions from the appropriate directory.
         | 
| 279 | 
            -
             | 
| 333 | 
            +
             | 
| 280 334 | 
             
                    Args:
         | 
| 281 335 | 
             
                        content: Dictionary to update with loaded agents
         | 
| 282 336 | 
             
                        agents_dir: Primary agents directory to load from
         | 
| @@ -285,19 +339,19 @@ class FrameworkLoader: | |
| 285 339 | 
             
                    """
         | 
| 286 340 | 
             
                    if not agents_dir or not agents_dir.exists():
         | 
| 287 341 | 
             
                        return
         | 
| 288 | 
            -
             | 
| 342 | 
            +
             | 
| 289 343 | 
             
                    content["loaded"] = True
         | 
| 290 | 
            -
             | 
| 344 | 
            +
             | 
| 291 345 | 
             
                    # Load all agent files
         | 
| 292 346 | 
             
                    for agent_file in agents_dir.glob("*.md"):
         | 
| 293 347 | 
             
                        agent_name, agent_content = self._load_single_agent(agent_file)
         | 
| 294 348 | 
             
                        if agent_name and agent_content:
         | 
| 295 349 | 
             
                            content["agents"][agent_name] = agent_content
         | 
| 296 | 
            -
             | 
| 350 | 
            +
             | 
| 297 351 | 
             
                    # If we used templates dir, also check main dir for base_agent.md
         | 
| 298 352 | 
             
                    if agents_dir == templates_dir:
         | 
| 299 353 | 
             
                        self._load_base_agent_fallback(content, main_dir)
         | 
| 300 | 
            -
             | 
| 354 | 
            +
             | 
| 301 355 | 
             
                def _load_framework_content(self) -> Dict[str, Any]:
         | 
| 302 356 | 
             
                    """Load framework content."""
         | 
| 303 357 | 
             
                    content = {
         | 
| @@ -310,55 +364,69 @@ class FrameworkLoader: | |
| 310 364 | 
             
                        "workflow_instructions": "",
         | 
| 311 365 | 
             
                        "project_workflow": "",
         | 
| 312 366 | 
             
                        "memory_instructions": "",
         | 
| 313 | 
            -
                        "project_memory": ""
         | 
| 367 | 
            +
                        "project_memory": "",
         | 
| 368 | 
            +
                        "actual_memories": "",  # Add field for actual memories from PM.md
         | 
| 314 369 | 
             
                    }
         | 
| 315 | 
            -
             | 
| 370 | 
            +
             | 
| 316 371 | 
             
                    # Load instructions file from working directory
         | 
| 317 372 | 
             
                    self._load_instructions_file(content)
         | 
| 318 | 
            -
             | 
| 373 | 
            +
             | 
| 319 374 | 
             
                    if not self.framework_path:
         | 
| 320 375 | 
             
                        return content
         | 
| 321 | 
            -
             | 
| 376 | 
            +
             | 
| 322 377 | 
             
                    # Load framework's INSTRUCTIONS.md
         | 
| 323 | 
            -
                    framework_instructions_path =  | 
| 378 | 
            +
                    framework_instructions_path = (
         | 
| 379 | 
            +
                        self.framework_path / "src" / "claude_mpm" / "agents" / "INSTRUCTIONS.md"
         | 
| 380 | 
            +
                    )
         | 
| 324 381 | 
             
                    if framework_instructions_path.exists():
         | 
| 325 | 
            -
                        loaded_content = self._try_load_file( | 
| 382 | 
            +
                        loaded_content = self._try_load_file(
         | 
| 383 | 
            +
                            framework_instructions_path, "framework INSTRUCTIONS.md"
         | 
| 384 | 
            +
                        )
         | 
| 326 385 | 
             
                        if loaded_content:
         | 
| 327 386 | 
             
                            content["framework_instructions"] = loaded_content
         | 
| 328 387 | 
             
                            content["loaded"] = True
         | 
| 329 388 | 
             
                            # Add framework version to content
         | 
| 330 389 | 
             
                            if self.framework_version:
         | 
| 331 390 | 
             
                                content["instructions_version"] = self.framework_version
         | 
| 332 | 
            -
                                content[ | 
| 391 | 
            +
                                content[
         | 
| 392 | 
            +
                                    "version"
         | 
| 393 | 
            +
                                ] = self.framework_version  # Update main version key
         | 
| 333 394 | 
             
                            # Add modification timestamp to content
         | 
| 334 395 | 
             
                            if self.framework_last_modified:
         | 
| 335 396 | 
             
                                content["instructions_last_modified"] = self.framework_last_modified
         | 
| 336 | 
            -
             | 
| 397 | 
            +
             | 
| 337 398 | 
             
                    # Load BASE_PM.md for core framework requirements
         | 
| 338 | 
            -
                    base_pm_path =  | 
| 399 | 
            +
                    base_pm_path = (
         | 
| 400 | 
            +
                        self.framework_path / "src" / "claude_mpm" / "agents" / "BASE_PM.md"
         | 
| 401 | 
            +
                    )
         | 
| 339 402 | 
             
                    if base_pm_path.exists():
         | 
| 340 | 
            -
                        base_pm_content = self._try_load_file( | 
| 403 | 
            +
                        base_pm_content = self._try_load_file(
         | 
| 404 | 
            +
                            base_pm_path, "BASE_PM framework requirements"
         | 
| 405 | 
            +
                        )
         | 
| 341 406 | 
             
                        if base_pm_content:
         | 
| 342 407 | 
             
                            content["base_pm_instructions"] = base_pm_content
         | 
| 343 | 
            -
             | 
| 408 | 
            +
             | 
| 344 409 | 
             
                    # Load WORKFLOW.md - check for project-specific first, then system
         | 
| 345 410 | 
             
                    self._load_workflow_instructions(content)
         | 
| 346 | 
            -
             | 
| 411 | 
            +
             | 
| 347 412 | 
             
                    # Load MEMORY.md - check for project-specific first, then system
         | 
| 348 413 | 
             
                    self._load_memory_instructions(content)
         | 
| 349 414 |  | 
| 415 | 
            +
                    # Load actual memories from .claude-mpm/memories/PM.md
         | 
| 416 | 
            +
                    self._load_actual_memories(content)
         | 
| 417 | 
            +
             | 
| 350 418 | 
             
                    # Discover agent directories
         | 
| 351 419 | 
             
                    agents_dir, templates_dir, main_dir = self._discover_framework_paths()
         | 
| 352 | 
            -
             | 
| 420 | 
            +
             | 
| 353 421 | 
             
                    # Load agents from discovered directory
         | 
| 354 422 | 
             
                    self._load_agents_directory(content, agents_dir, templates_dir, main_dir)
         | 
| 355 | 
            -
             | 
| 423 | 
            +
             | 
| 356 424 | 
             
                    return content
         | 
| 357 | 
            -
             | 
| 425 | 
            +
             | 
| 358 426 | 
             
                def get_framework_instructions(self) -> str:
         | 
| 359 427 | 
             
                    """
         | 
| 360 428 | 
             
                    Get formatted framework instructions for injection.
         | 
| 361 | 
            -
             | 
| 429 | 
            +
             | 
| 362 430 | 
             
                    Returns:
         | 
| 363 431 | 
             
                        Complete framework instructions ready for injection
         | 
| 364 432 | 
             
                    """
         | 
| @@ -368,61 +436,83 @@ class FrameworkLoader: | |
| 368 436 | 
             
                    else:
         | 
| 369 437 | 
             
                        # Use minimal fallback
         | 
| 370 438 | 
             
                        return self._format_minimal_framework()
         | 
| 371 | 
            -
             | 
| 439 | 
            +
             | 
| 372 440 | 
             
                def _strip_metadata_comments(self, content: str) -> str:
         | 
| 373 441 | 
             
                    """Strip metadata HTML comments from content.
         | 
| 374 | 
            -
             | 
| 442 | 
            +
             | 
| 375 443 | 
             
                    Removes comments like:
         | 
| 376 444 | 
             
                    <!-- FRAMEWORK_VERSION: 0010 -->
         | 
| 377 445 | 
             
                    <!-- LAST_MODIFIED: 2025-08-10T00:00:00Z -->
         | 
| 378 446 | 
             
                    """
         | 
| 379 447 | 
             
                    import re
         | 
| 448 | 
            +
             | 
| 380 449 | 
             
                    # Remove HTML comments that contain metadata
         | 
| 381 | 
            -
                    cleaned = re.sub( | 
| 450 | 
            +
                    cleaned = re.sub(
         | 
| 451 | 
            +
                        r"<!--\s*(FRAMEWORK_VERSION|LAST_MODIFIED|WORKFLOW_VERSION|PROJECT_WORKFLOW_VERSION|CUSTOM_PROJECT_WORKFLOW)[^>]*-->\n?",
         | 
| 452 | 
            +
                        "",
         | 
| 453 | 
            +
                        content,
         | 
| 454 | 
            +
                    )
         | 
| 382 455 | 
             
                    # Also remove any leading blank lines that might result
         | 
| 383 | 
            -
                    cleaned = cleaned.lstrip( | 
| 456 | 
            +
                    cleaned = cleaned.lstrip("\n")
         | 
| 384 457 | 
             
                    return cleaned
         | 
| 385 | 
            -
             | 
| 458 | 
            +
             | 
| 386 459 | 
             
                def _format_full_framework(self) -> str:
         | 
| 387 460 | 
             
                    """Format full framework instructions."""
         | 
| 388 461 | 
             
                    from datetime import datetime
         | 
| 389 | 
            -
             | 
| 462 | 
            +
             | 
| 390 463 | 
             
                    # If we have the full framework INSTRUCTIONS.md, use it
         | 
| 391 464 | 
             
                    if self.framework_content.get("framework_instructions"):
         | 
| 392 | 
            -
                        instructions = self._strip_metadata_comments( | 
| 393 | 
            -
             | 
| 465 | 
            +
                        instructions = self._strip_metadata_comments(
         | 
| 466 | 
            +
                            self.framework_content["framework_instructions"]
         | 
| 467 | 
            +
                        )
         | 
| 468 | 
            +
             | 
| 394 469 | 
             
                        # Note: We don't add working directory CLAUDE.md here since Claude Code
         | 
| 395 470 | 
             
                        # already picks it up automatically. This prevents duplication.
         | 
| 396 | 
            -
             | 
| 471 | 
            +
             | 
| 397 472 | 
             
                        # Add WORKFLOW.md after instructions
         | 
| 398 473 | 
             
                        if self.framework_content.get("workflow_instructions"):
         | 
| 399 | 
            -
                            workflow_content = self._strip_metadata_comments( | 
| 474 | 
            +
                            workflow_content = self._strip_metadata_comments(
         | 
| 475 | 
            +
                                self.framework_content["workflow_instructions"]
         | 
| 476 | 
            +
                            )
         | 
| 400 477 | 
             
                            instructions += f"\n\n{workflow_content}\n"
         | 
| 401 478 | 
             
                            # Note: project-specific workflow is being used (logged elsewhere)
         | 
| 402 | 
            -
             | 
| 479 | 
            +
             | 
| 403 480 | 
             
                        # Add MEMORY.md after workflow instructions
         | 
| 404 481 | 
             
                        if self.framework_content.get("memory_instructions"):
         | 
| 405 | 
            -
                            memory_content = self._strip_metadata_comments( | 
| 482 | 
            +
                            memory_content = self._strip_metadata_comments(
         | 
| 483 | 
            +
                                self.framework_content["memory_instructions"]
         | 
| 484 | 
            +
                            )
         | 
| 406 485 | 
             
                            instructions += f"\n\n{memory_content}\n"
         | 
| 407 486 | 
             
                            # Note: project-specific memory instructions being used (logged elsewhere)
         | 
| 408 487 |  | 
| 488 | 
            +
                        # Add actual PM memories after memory instructions
         | 
| 489 | 
            +
                        if self.framework_content.get("actual_memories"):
         | 
| 490 | 
            +
                            instructions += "\n\n## Current PM Memories\n\n"
         | 
| 491 | 
            +
                            instructions += "**The following are your accumulated memories and knowledge from this project:**\n\n"
         | 
| 492 | 
            +
                            instructions += self.framework_content["actual_memories"]
         | 
| 493 | 
            +
                            instructions += "\n"
         | 
| 494 | 
            +
             | 
| 409 495 | 
             
                        # Add dynamic agent capabilities section
         | 
| 410 496 | 
             
                        instructions += self._generate_agent_capabilities_section()
         | 
| 411 | 
            -
             | 
| 497 | 
            +
             | 
| 412 498 | 
             
                        # Add current date for temporal awareness
         | 
| 413 499 | 
             
                        instructions += f"\n\n## Temporal Context\n**Today's Date**: {datetime.now().strftime('%Y-%m-%d')}\n"
         | 
| 414 | 
            -
                        instructions +=  | 
| 415 | 
            -
             | 
| 500 | 
            +
                        instructions += (
         | 
| 501 | 
            +
                            "Apply date awareness to all time-sensitive tasks and decisions.\n"
         | 
| 502 | 
            +
                        )
         | 
| 503 | 
            +
             | 
| 416 504 | 
             
                        # Add BASE_PM.md framework requirements AFTER INSTRUCTIONS.md
         | 
| 417 505 | 
             
                        if self.framework_content.get("base_pm_instructions"):
         | 
| 418 | 
            -
                            base_pm = self._strip_metadata_comments( | 
| 506 | 
            +
                            base_pm = self._strip_metadata_comments(
         | 
| 507 | 
            +
                                self.framework_content["base_pm_instructions"]
         | 
| 508 | 
            +
                            )
         | 
| 419 509 | 
             
                            instructions += f"\n\n{base_pm}"
         | 
| 420 | 
            -
             | 
| 510 | 
            +
             | 
| 421 511 | 
             
                        # Clean up any trailing whitespace
         | 
| 422 512 | 
             
                        instructions = instructions.rstrip() + "\n"
         | 
| 423 | 
            -
             | 
| 513 | 
            +
             | 
| 424 514 | 
             
                        return instructions
         | 
| 425 | 
            -
             | 
| 515 | 
            +
             | 
| 426 516 | 
             
                    # Otherwise fall back to generating framework
         | 
| 427 517 | 
             
                    instructions = """# Claude MPM Framework Instructions
         | 
| 428 518 |  | 
| @@ -437,48 +527,71 @@ You are a multi-agent orchestrator. Your primary responsibilities are: | |
| 437 527 | 
             
            - NEVER perform direct implementation work yourself
         | 
| 438 528 |  | 
| 439 529 | 
             
            """
         | 
| 440 | 
            -
             | 
| 530 | 
            +
             | 
| 441 531 | 
             
                    # Note: We don't add working directory CLAUDE.md here since Claude Code
         | 
| 442 532 | 
             
                    # already picks it up automatically. This prevents duplication.
         | 
| 443 | 
            -
             | 
| 533 | 
            +
             | 
| 444 534 | 
             
                    # Add agent definitions
         | 
| 445 535 | 
             
                    if self.framework_content["agents"]:
         | 
| 446 536 | 
             
                        instructions += "## Available Agents\n\n"
         | 
| 447 537 | 
             
                        instructions += "You have the following specialized agents available for delegation:\n\n"
         | 
| 448 | 
            -
             | 
| 538 | 
            +
             | 
| 449 539 | 
             
                        # List agents with brief descriptions and correct IDs
         | 
| 450 540 | 
             
                        agent_list = []
         | 
| 451 541 | 
             
                        for agent_name in sorted(self.framework_content["agents"].keys()):
         | 
| 452 542 | 
             
                            # Use the actual agent_name as the ID (it's the filename stem)
         | 
| 453 543 | 
             
                            agent_id = agent_name
         | 
| 454 | 
            -
                            clean_name = agent_name.replace( | 
| 455 | 
            -
                            if  | 
| 456 | 
            -
                                 | 
| 457 | 
            -
             | 
| 458 | 
            -
             | 
| 459 | 
            -
             | 
| 460 | 
            -
             | 
| 461 | 
            -
             | 
| 462 | 
            -
             | 
| 463 | 
            -
             | 
| 464 | 
            -
             | 
| 465 | 
            -
             | 
| 466 | 
            -
             | 
| 467 | 
            -
             | 
| 468 | 
            -
             | 
| 469 | 
            -
             | 
| 470 | 
            -
             | 
| 544 | 
            +
                            clean_name = agent_name.replace("-", " ").replace("_", " ").title()
         | 
| 545 | 
            +
                            if (
         | 
| 546 | 
            +
                                "engineer" in agent_name.lower()
         | 
| 547 | 
            +
                                and "data" not in agent_name.lower()
         | 
| 548 | 
            +
                            ):
         | 
| 549 | 
            +
                                agent_list.append(
         | 
| 550 | 
            +
                                    f"- **Engineer Agent** (`{agent_id}`): Code implementation and development"
         | 
| 551 | 
            +
                                )
         | 
| 552 | 
            +
                            elif "qa" in agent_name.lower():
         | 
| 553 | 
            +
                                agent_list.append(
         | 
| 554 | 
            +
                                    f"- **QA Agent** (`{agent_id}`): Testing and quality assurance"
         | 
| 555 | 
            +
                                )
         | 
| 556 | 
            +
                            elif "documentation" in agent_name.lower():
         | 
| 557 | 
            +
                                agent_list.append(
         | 
| 558 | 
            +
                                    f"- **Documentation Agent** (`{agent_id}`): Documentation creation and maintenance"
         | 
| 559 | 
            +
                                )
         | 
| 560 | 
            +
                            elif "research" in agent_name.lower():
         | 
| 561 | 
            +
                                agent_list.append(
         | 
| 562 | 
            +
                                    f"- **Research Agent** (`{agent_id}`): Investigation and analysis"
         | 
| 563 | 
            +
                                )
         | 
| 564 | 
            +
                            elif "security" in agent_name.lower():
         | 
| 565 | 
            +
                                agent_list.append(
         | 
| 566 | 
            +
                                    f"- **Security Agent** (`{agent_id}`): Security analysis and protection"
         | 
| 567 | 
            +
                                )
         | 
| 568 | 
            +
                            elif "version" in agent_name.lower():
         | 
| 569 | 
            +
                                agent_list.append(
         | 
| 570 | 
            +
                                    f"- **Version Control Agent** (`{agent_id}`): Git operations and version management"
         | 
| 571 | 
            +
                                )
         | 
| 572 | 
            +
                            elif "ops" in agent_name.lower():
         | 
| 573 | 
            +
                                agent_list.append(
         | 
| 574 | 
            +
                                    f"- **Ops Agent** (`{agent_id}`): Deployment and operations"
         | 
| 575 | 
            +
                                )
         | 
| 576 | 
            +
                            elif "data" in agent_name.lower():
         | 
| 577 | 
            +
                                agent_list.append(
         | 
| 578 | 
            +
                                    f"- **Data Engineer Agent** (`{agent_id}`): Data management and AI API integration"
         | 
| 579 | 
            +
                                )
         | 
| 471 580 | 
             
                            else:
         | 
| 472 | 
            -
                                agent_list.append( | 
| 473 | 
            -
             | 
| 581 | 
            +
                                agent_list.append(
         | 
| 582 | 
            +
                                    f"- **{clean_name}** (`{agent_id}`): Available for specialized tasks"
         | 
| 583 | 
            +
                                )
         | 
| 584 | 
            +
             | 
| 474 585 | 
             
                        instructions += "\n".join(agent_list) + "\n\n"
         | 
| 475 | 
            -
             | 
| 586 | 
            +
             | 
| 476 587 | 
             
                        # Add full agent details
         | 
| 477 588 | 
             
                        instructions += "### Agent Details\n\n"
         | 
| 478 | 
            -
                        for agent_name, agent_content in sorted( | 
| 589 | 
            +
                        for agent_name, agent_content in sorted(
         | 
| 590 | 
            +
                            self.framework_content["agents"].items()
         | 
| 591 | 
            +
                        ):
         | 
| 479 592 | 
             
                            instructions += f"#### {agent_name.replace('-', ' ').title()}\n"
         | 
| 480 593 | 
             
                            instructions += agent_content + "\n\n"
         | 
| 481 | 
            -
             | 
| 594 | 
            +
             | 
| 482 595 | 
             
                    # Add orchestration principles
         | 
| 483 596 | 
             
                    instructions += """
         | 
| 484 597 | 
             
            ## Orchestration Principles
         | 
| @@ -515,174 +628,219 @@ Extract tickets from these patterns: | |
| 515 628 |  | 
| 516 629 | 
             
            ---
         | 
| 517 630 | 
             
            """
         | 
| 518 | 
            -
             | 
| 631 | 
            +
             | 
| 519 632 | 
             
                    return instructions
         | 
| 520 | 
            -
             | 
| 633 | 
            +
             | 
| 521 634 | 
             
                def _generate_agent_capabilities_section(self) -> str:
         | 
| 522 635 | 
             
                    """Generate dynamic agent capabilities section from deployed agents."""
         | 
| 523 636 | 
             
                    try:
         | 
| 524 637 | 
             
                        from pathlib import Path
         | 
| 638 | 
            +
             | 
| 525 639 | 
             
                        import yaml
         | 
| 526 | 
            -
             | 
| 640 | 
            +
             | 
| 527 641 | 
             
                        # Read directly from deployed agents in .claude/agents/
         | 
| 528 642 | 
             
                        agents_dir = Path.cwd() / ".claude" / "agents"
         | 
| 529 | 
            -
             | 
| 643 | 
            +
             | 
| 530 644 | 
             
                        if not agents_dir.exists():
         | 
| 531 645 | 
             
                            self.logger.warning("No .claude/agents directory found")
         | 
| 532 646 | 
             
                            return self._get_fallback_capabilities()
         | 
| 533 | 
            -
             | 
| 647 | 
            +
             | 
| 534 648 | 
             
                        # Build capabilities section
         | 
| 535 649 | 
             
                        section = "\n\n## Available Agent Capabilities\n\n"
         | 
| 536 | 
            -
             | 
| 650 | 
            +
             | 
| 537 651 | 
             
                        # Collect deployed agents
         | 
| 538 652 | 
             
                        deployed_agents = []
         | 
| 539 653 | 
             
                        for agent_file in agents_dir.glob("*.md"):
         | 
| 540 | 
            -
                            if agent_file.name.startswith( | 
| 654 | 
            +
                            if agent_file.name.startswith("."):
         | 
| 541 655 | 
             
                                continue
         | 
| 542 | 
            -
             | 
| 656 | 
            +
             | 
| 543 657 | 
             
                            # Parse agent metadata
         | 
| 544 658 | 
             
                            agent_data = self._parse_agent_metadata(agent_file)
         | 
| 545 659 | 
             
                            if agent_data:
         | 
| 546 660 | 
             
                                deployed_agents.append(agent_data)
         | 
| 547 | 
            -
             | 
| 661 | 
            +
             | 
| 548 662 | 
             
                        if not deployed_agents:
         | 
| 549 663 | 
             
                            return self._get_fallback_capabilities()
         | 
| 550 | 
            -
             | 
| 664 | 
            +
             | 
| 551 665 | 
             
                        # Sort agents alphabetically by ID
         | 
| 552 | 
            -
                        deployed_agents.sort(key=lambda x: x[ | 
| 553 | 
            -
             | 
| 666 | 
            +
                        deployed_agents.sort(key=lambda x: x["id"])
         | 
| 667 | 
            +
             | 
| 554 668 | 
             
                        # Display all agents with their rich descriptions
         | 
| 555 669 | 
             
                        for agent in deployed_agents:
         | 
| 556 670 | 
             
                            # Clean up display name - handle common acronyms
         | 
| 557 | 
            -
                            display_name = agent[ | 
| 558 | 
            -
                            display_name =  | 
| 559 | 
            -
             | 
| 560 | 
            -
                                 | 
| 561 | 
            -
             | 
| 671 | 
            +
                            display_name = agent["display_name"]
         | 
| 672 | 
            +
                            display_name = (
         | 
| 673 | 
            +
                                display_name.replace("Qa ", "QA ")
         | 
| 674 | 
            +
                                .replace("Ui ", "UI ")
         | 
| 675 | 
            +
                                .replace("Api ", "API ")
         | 
| 676 | 
            +
                            )
         | 
| 677 | 
            +
                            if display_name.lower() == "qa agent":
         | 
| 678 | 
            +
                                display_name = "QA Agent"
         | 
| 679 | 
            +
             | 
| 562 680 | 
             
                            section += f"\n### {display_name} (`{agent['id']}`)\n"
         | 
| 563 681 | 
             
                            section += f"{agent['description']}\n"
         | 
| 564 | 
            -
             | 
| 682 | 
            +
             | 
| 565 683 | 
             
                            # Add any additional metadata if present
         | 
| 566 | 
            -
                            if agent.get( | 
| 684 | 
            +
                            if agent.get("authority"):
         | 
| 567 685 | 
             
                                section += f"- **Authority**: {agent['authority']}\n"
         | 
| 568 | 
            -
                            if agent.get( | 
| 686 | 
            +
                            if agent.get("primary_function"):
         | 
| 569 687 | 
             
                                section += f"- **Primary Function**: {agent['primary_function']}\n"
         | 
| 570 | 
            -
                            if agent.get( | 
| 688 | 
            +
                            if agent.get("handoff_to"):
         | 
| 571 689 | 
             
                                section += f"- **Handoff To**: {agent['handoff_to']}\n"
         | 
| 572 | 
            -
                            if agent.get( | 
| 690 | 
            +
                            if agent.get("tools") and agent["tools"] != "standard":
         | 
| 573 691 | 
             
                                section += f"- **Tools**: {agent['tools']}\n"
         | 
| 574 | 
            -
                            if agent.get( | 
| 692 | 
            +
                            if agent.get("model") and agent["model"] != "opus":
         | 
| 575 693 | 
             
                                section += f"- **Model**: {agent['model']}\n"
         | 
| 576 | 
            -
             | 
| 694 | 
            +
             | 
| 577 695 | 
             
                        # Add simple Context-Aware Agent Selection
         | 
| 578 696 | 
             
                        section += "\n## Context-Aware Agent Selection\n\n"
         | 
| 579 | 
            -
                        section +=  | 
| 697 | 
            +
                        section += (
         | 
| 698 | 
            +
                            "Select agents based on their descriptions above. Key principles:\n"
         | 
| 699 | 
            +
                        )
         | 
| 580 700 | 
             
                        section += "- **PM questions** → Answer directly (only exception)\n"
         | 
| 581 701 | 
             
                        section += "- Match task requirements to agent descriptions and authority\n"
         | 
| 582 702 | 
             
                        section += "- Consider agent handoff recommendations\n"
         | 
| 583 | 
            -
                        section +=  | 
| 584 | 
            -
             | 
| 703 | 
            +
                        section += (
         | 
| 704 | 
            +
                            "- Use the agent ID in parentheses when delegating via Task tool\n"
         | 
| 705 | 
            +
                        )
         | 
| 706 | 
            +
             | 
| 585 707 | 
             
                        # Add summary
         | 
| 586 708 | 
             
                        section += f"\n**Total Available Agents**: {len(deployed_agents)}\n"
         | 
| 587 | 
            -
             | 
| 709 | 
            +
             | 
| 588 710 | 
             
                        return section
         | 
| 589 | 
            -
             | 
| 711 | 
            +
             | 
| 590 712 | 
             
                    except Exception as e:
         | 
| 591 713 | 
             
                        self.logger.warning(f"Could not generate dynamic agent capabilities: {e}")
         | 
| 592 714 | 
             
                        return self._get_fallback_capabilities()
         | 
| 593 | 
            -
             | 
| 715 | 
            +
             | 
| 594 716 | 
             
                def _parse_agent_metadata(self, agent_file: Path) -> Optional[Dict[str, Any]]:
         | 
| 595 717 | 
             
                    """Parse agent metadata from deployed agent file.
         | 
| 596 | 
            -
             | 
| 718 | 
            +
             | 
| 597 719 | 
             
                    Returns:
         | 
| 598 720 | 
             
                        Dictionary with agent metadata directly from YAML frontmatter.
         | 
| 599 721 | 
             
                    """
         | 
| 600 722 | 
             
                    try:
         | 
| 601 723 | 
             
                        import yaml
         | 
| 602 | 
            -
             | 
| 603 | 
            -
                        with open(agent_file,  | 
| 724 | 
            +
             | 
| 725 | 
            +
                        with open(agent_file, "r") as f:
         | 
| 604 726 | 
             
                            content = f.read()
         | 
| 605 | 
            -
             | 
| 727 | 
            +
             | 
| 606 728 | 
             
                        # Default values
         | 
| 607 729 | 
             
                        agent_data = {
         | 
| 608 | 
            -
                             | 
| 609 | 
            -
                             | 
| 610 | 
            -
                             | 
| 730 | 
            +
                            "id": agent_file.stem,
         | 
| 731 | 
            +
                            "display_name": agent_file.stem.replace("_", " ")
         | 
| 732 | 
            +
                            .replace("-", " ")
         | 
| 733 | 
            +
                            .title(),
         | 
| 734 | 
            +
                            "description": "Specialized agent",
         | 
| 611 735 | 
             
                        }
         | 
| 612 | 
            -
             | 
| 736 | 
            +
             | 
| 613 737 | 
             
                        # Extract YAML frontmatter if present
         | 
| 614 | 
            -
                        if content.startswith( | 
| 615 | 
            -
                            end_marker = content.find( | 
| 738 | 
            +
                        if content.startswith("---"):
         | 
| 739 | 
            +
                            end_marker = content.find("---", 3)
         | 
| 616 740 | 
             
                            if end_marker > 0:
         | 
| 617 741 | 
             
                                frontmatter = content[3:end_marker]
         | 
| 618 742 | 
             
                                metadata = yaml.safe_load(frontmatter)
         | 
| 619 743 | 
             
                                if metadata:
         | 
| 620 744 | 
             
                                    # Use name as ID for Task tool
         | 
| 621 | 
            -
                                    agent_data[ | 
| 622 | 
            -
                                    agent_data[ | 
| 623 | 
            -
             | 
| 745 | 
            +
                                    agent_data["id"] = metadata.get("name", agent_data["id"])
         | 
| 746 | 
            +
                                    agent_data["display_name"] = (
         | 
| 747 | 
            +
                                        metadata.get("name", agent_data["display_name"])
         | 
| 748 | 
            +
                                        .replace("-", " ")
         | 
| 749 | 
            +
                                        .title()
         | 
| 750 | 
            +
                                    )
         | 
| 751 | 
            +
             | 
| 624 752 | 
             
                                    # Copy all metadata fields directly
         | 
| 625 753 | 
             
                                    for key, value in metadata.items():
         | 
| 626 | 
            -
                                        if key not in [ | 
| 754 | 
            +
                                        if key not in ["name"]:  # Skip already processed fields
         | 
| 627 755 | 
             
                                            agent_data[key] = value
         | 
| 628 | 
            -
             | 
| 756 | 
            +
             | 
| 629 757 | 
             
                                    # IMPORTANT: Do NOT add spaces to tools field - it breaks deployment!
         | 
| 630 758 | 
             
                                    # Tools must remain as comma-separated without spaces: "Read,Write,Edit"
         | 
| 631 | 
            -
             | 
| 759 | 
            +
             | 
| 632 760 | 
             
                        return agent_data
         | 
| 633 | 
            -
             | 
| 761 | 
            +
             | 
| 634 762 | 
             
                    except Exception as e:
         | 
| 635 763 | 
             
                        self.logger.debug(f"Could not parse metadata from {agent_file}: {e}")
         | 
| 636 764 | 
             
                        return None
         | 
| 637 | 
            -
             | 
| 765 | 
            +
             | 
| 638 766 | 
             
                def _generate_agent_selection_guide(self, deployed_agents: list) -> str:
         | 
| 639 767 | 
             
                    """Generate Context-Aware Agent Selection guide from deployed agents.
         | 
| 640 | 
            -
             | 
| 768 | 
            +
             | 
| 641 769 | 
             
                    Creates a mapping of task types to appropriate agents based on their
         | 
| 642 770 | 
             
                    descriptions and capabilities.
         | 
| 643 771 | 
             
                    """
         | 
| 644 772 | 
             
                    guide = ""
         | 
| 645 | 
            -
             | 
| 773 | 
            +
             | 
| 646 774 | 
             
                    # Build selection mapping based on deployed agents
         | 
| 647 775 | 
             
                    selection_map = {}
         | 
| 648 | 
            -
             | 
| 776 | 
            +
             | 
| 649 777 | 
             
                    for agent in deployed_agents:
         | 
| 650 | 
            -
                        agent_id = agent[ | 
| 651 | 
            -
                        desc_lower = agent[ | 
| 652 | 
            -
             | 
| 778 | 
            +
                        agent_id = agent["id"]
         | 
| 779 | 
            +
                        desc_lower = agent["description"].lower()
         | 
| 780 | 
            +
             | 
| 653 781 | 
             
                        # Map task types to agents based on their descriptions
         | 
| 654 | 
            -
                        if  | 
| 655 | 
            -
                             | 
| 656 | 
            -
                         | 
| 657 | 
            -
                            selection_map[ | 
| 658 | 
            -
             | 
| 659 | 
            -
                             | 
| 660 | 
            -
                        if  | 
| 661 | 
            -
                            selection_map[ | 
| 662 | 
            -
             | 
| 663 | 
            -
                             | 
| 664 | 
            -
                        if  | 
| 665 | 
            -
                            selection_map[ | 
| 666 | 
            -
             | 
| 667 | 
            -
                             | 
| 668 | 
            -
                        if  | 
| 669 | 
            -
                            selection_map[ | 
| 670 | 
            -
             | 
| 671 | 
            -
                             | 
| 672 | 
            -
                        if  | 
| 673 | 
            -
                            selection_map[ | 
| 674 | 
            -
             | 
| 675 | 
            -
                             | 
| 676 | 
            -
             | 
| 782 | 
            +
                        if "implementation" in desc_lower or (
         | 
| 783 | 
            +
                            "engineer" in agent_id and "data" not in agent_id
         | 
| 784 | 
            +
                        ):
         | 
| 785 | 
            +
                            selection_map[
         | 
| 786 | 
            +
                                "Implementation tasks"
         | 
| 787 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 788 | 
            +
                        if "codebase analysis" in desc_lower or "research" in agent_id:
         | 
| 789 | 
            +
                            selection_map[
         | 
| 790 | 
            +
                                "Codebase analysis"
         | 
| 791 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 792 | 
            +
                        if "testing" in desc_lower or "qa" in agent_id:
         | 
| 793 | 
            +
                            selection_map[
         | 
| 794 | 
            +
                                "Testing/quality"
         | 
| 795 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 796 | 
            +
                        if "documentation" in desc_lower:
         | 
| 797 | 
            +
                            selection_map[
         | 
| 798 | 
            +
                                "Documentation"
         | 
| 799 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 800 | 
            +
                        if "security" in desc_lower or "sast" in desc_lower:
         | 
| 801 | 
            +
                            selection_map[
         | 
| 802 | 
            +
                                "Security operations"
         | 
| 803 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 804 | 
            +
                        if (
         | 
| 805 | 
            +
                            "deployment" in desc_lower
         | 
| 806 | 
            +
                            or "infrastructure" in desc_lower
         | 
| 807 | 
            +
                            or "ops" in agent_id
         | 
| 808 | 
            +
                        ):
         | 
| 809 | 
            +
                            selection_map[
         | 
| 810 | 
            +
                                "Deployment/infrastructure"
         | 
| 811 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 812 | 
            +
                        if "data" in desc_lower and (
         | 
| 813 | 
            +
                            "pipeline" in desc_lower or "etl" in desc_lower
         | 
| 814 | 
            +
                        ):
         | 
| 815 | 
            +
                            selection_map[
         | 
| 816 | 
            +
                                "Data pipeline/ETL"
         | 
| 817 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 818 | 
            +
                        if "git" in desc_lower or "version control" in desc_lower:
         | 
| 819 | 
            +
                            selection_map[
         | 
| 820 | 
            +
                                "Version control"
         | 
| 821 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 822 | 
            +
                        if "ticket" in desc_lower or "epic" in desc_lower:
         | 
| 823 | 
            +
                            selection_map[
         | 
| 824 | 
            +
                                "Ticket/issue management"
         | 
| 825 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 826 | 
            +
                        if "browser" in desc_lower or "e2e" in desc_lower:
         | 
| 827 | 
            +
                            selection_map[
         | 
| 828 | 
            +
                                "Browser/E2E testing"
         | 
| 829 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 830 | 
            +
                        if "frontend" in desc_lower or "ui" in desc_lower or "html" in desc_lower:
         | 
| 831 | 
            +
                            selection_map[
         | 
| 832 | 
            +
                                "Frontend/UI development"
         | 
| 833 | 
            +
                            ] = f"{agent['display_name']} (`{agent_id}`)"
         | 
| 834 | 
            +
             | 
| 677 835 | 
             
                    # Always include PM questions
         | 
| 678 | 
            -
                    selection_map[ | 
| 679 | 
            -
             | 
| 836 | 
            +
                    selection_map["PM questions"] = "Answer directly (only exception)"
         | 
| 837 | 
            +
             | 
| 680 838 | 
             
                    # Format the selection guide
         | 
| 681 839 | 
             
                    for task_type, agent_info in selection_map.items():
         | 
| 682 840 | 
             
                        guide += f"- **{task_type}** → {agent_info}\n"
         | 
| 683 | 
            -
             | 
| 841 | 
            +
             | 
| 684 842 | 
             
                    return guide
         | 
| 685 | 
            -
             | 
| 843 | 
            +
             | 
| 686 844 | 
             
                def _get_fallback_capabilities(self) -> str:
         | 
| 687 845 | 
             
                    """Return fallback capabilities when dynamic discovery fails."""
         | 
| 688 846 | 
             
                    return """
         | 
| @@ -692,7 +850,7 @@ Extract tickets from these patterns: | |
| 692 850 | 
             
            You have the following specialized agents available for delegation:
         | 
| 693 851 |  | 
| 694 852 | 
             
            - **Engineer** (`engineer`): Code implementation and development
         | 
| 695 | 
            -
            - **Research** (`research-agent`): Investigation and analysis | 
| 853 | 
            +
            - **Research** (`research-agent`): Investigation and analysis
         | 
| 696 854 | 
             
            - **QA** (`qa-agent`): Testing and quality assurance
         | 
| 697 855 | 
             
            - **Documentation** (`documentation-agent`): Documentation creation and maintenance
         | 
| 698 856 | 
             
            - **Security** (`security-agent`): Security analysis and protection
         | 
| @@ -702,7 +860,7 @@ You have the following specialized agents available for delegation: | |
| 702 860 |  | 
| 703 861 | 
             
            **IMPORTANT**: Use the exact agent ID in parentheses when delegating tasks.
         | 
| 704 862 | 
             
            """
         | 
| 705 | 
            -
             | 
| 863 | 
            +
             | 
| 706 864 | 
             
                def _format_minimal_framework(self) -> str:
         | 
| 707 865 | 
             
                    """Format minimal framework instructions when full framework not available."""
         | 
| 708 866 | 
             
                    return """
         | 
| @@ -719,7 +877,7 @@ You are a multi-agent orchestrator. Your primary responsibilities: | |
| 719 877 |  | 
| 720 878 | 
             
            ## Core Agents
         | 
| 721 879 | 
             
            - Documentation Agent - Documentation tasks
         | 
| 722 | 
            -
            - Engineer Agent - Code implementation | 
| 880 | 
            +
            - Engineer Agent - Code implementation
         | 
| 723 881 | 
             
            - QA Agent - Testing and validation
         | 
| 724 882 | 
             
            - Research Agent - Investigation and analysis
         | 
| 725 883 | 
             
            - Version Control Agent - Git operations
         | 
| @@ -732,7 +890,7 @@ You are a multi-agent orchestrator. Your primary responsibilities: | |
| 732 890 |  | 
| 733 891 | 
             
            ---
         | 
| 734 892 | 
             
            """
         | 
| 735 | 
            -
             | 
| 893 | 
            +
             | 
| 736 894 | 
             
                def get_agent_list(self) -> list:
         | 
| 737 895 | 
             
                    """Get list of available agents."""
         | 
| 738 896 | 
             
                    # First try agent registry
         | 
| @@ -740,10 +898,10 @@ You are a multi-agent orchestrator. Your primary responsibilities: | |
| 740 898 | 
             
                        agents = self.agent_registry.list_agents()
         | 
| 741 899 | 
             
                        if agents:
         | 
| 742 900 | 
             
                            return list(agents.keys())
         | 
| 743 | 
            -
             | 
| 901 | 
            +
             | 
| 744 902 | 
             
                    # Fallback to loaded content
         | 
| 745 903 | 
             
                    return list(self.framework_content["agents"].keys())
         | 
| 746 | 
            -
             | 
| 904 | 
            +
             | 
| 747 905 | 
             
                def get_agent_definition(self, agent_name: str) -> Optional[str]:
         | 
| 748 906 | 
             
                    """Get specific agent definition."""
         | 
| 749 907 | 
             
                    # First try agent registry
         | 
| @@ -751,12 +909,12 @@ You are a multi-agent orchestrator. Your primary responsibilities: | |
| 751 909 | 
             
                        definition = self.agent_registry.get_agent_definition(agent_name)
         | 
| 752 910 | 
             
                        if definition:
         | 
| 753 911 | 
             
                            return definition
         | 
| 754 | 
            -
             | 
| 912 | 
            +
             | 
| 755 913 | 
             
                    # Fallback to loaded content
         | 
| 756 914 | 
             
                    return self.framework_content["agents"].get(agent_name)
         | 
| 757 | 
            -
             | 
| 915 | 
            +
             | 
| 758 916 | 
             
                def get_agent_hierarchy(self) -> Dict[str, list]:
         | 
| 759 917 | 
             
                    """Get agent hierarchy from registry."""
         | 
| 760 918 | 
             
                    if self.agent_registry:
         | 
| 761 919 | 
             
                        return self.agent_registry.get_agent_hierarchy()
         | 
| 762 | 
            -
                    return { | 
| 920 | 
            +
                    return {"project": [], "user": [], "system": []}
         |