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
| @@ -0,0 +1,316 @@ | |
| 1 | 
            +
            """Subprocess launcher service for launching Claude as a subprocess with PTY.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This service handles:
         | 
| 4 | 
            +
            1. PTY (pseudo-terminal) creation and management
         | 
| 5 | 
            +
            2. Subprocess launching with proper I/O handling
         | 
| 6 | 
            +
            3. Terminal state management and restoration
         | 
| 7 | 
            +
            4. Signal handling for graceful shutdown
         | 
| 8 | 
            +
            5. WebSocket integration for status updates
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            Extracted from ClaudeRunner to follow Single Responsibility Principle.
         | 
| 11 | 
            +
            """
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            import os
         | 
| 14 | 
            +
            import pty
         | 
| 15 | 
            +
            import select
         | 
| 16 | 
            +
            import signal
         | 
| 17 | 
            +
            import subprocess
         | 
| 18 | 
            +
            import sys
         | 
| 19 | 
            +
            import termios
         | 
| 20 | 
            +
            import tty
         | 
| 21 | 
            +
            from typing import Any, Dict, List, Optional
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            from claude_mpm.core.base_service import BaseService
         | 
| 24 | 
            +
            from claude_mpm.services.core.interfaces import SubprocessLauncherInterface
         | 
| 25 | 
            +
             | 
| 26 | 
            +
             | 
| 27 | 
            +
            class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
         | 
| 28 | 
            +
                """Service for launching Claude as a subprocess with PTY support."""
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def __init__(self, project_logger=None, websocket_server=None):
         | 
| 31 | 
            +
                    """Initialize the subprocess launcher service.
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    Args:
         | 
| 34 | 
            +
                        project_logger: Optional logger for system events
         | 
| 35 | 
            +
                        websocket_server: Optional WebSocket server for status updates
         | 
| 36 | 
            +
                    """
         | 
| 37 | 
            +
                    super().__init__(name="subprocess_launcher_service")
         | 
| 38 | 
            +
                    self.project_logger = project_logger
         | 
| 39 | 
            +
                    self.websocket_server = websocket_server
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                async def _initialize(self) -> None:
         | 
| 42 | 
            +
                    """Initialize the service. No special initialization needed."""
         | 
| 43 | 
            +
                    pass
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                async def _cleanup(self) -> None:
         | 
| 46 | 
            +
                    """Cleanup service resources. No cleanup needed."""
         | 
| 47 | 
            +
                    pass
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                # Implementation of abstract methods from SubprocessLauncherInterface
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def launch_subprocess(self, command: List[str], **kwargs) -> Dict[str, Any]:
         | 
| 52 | 
            +
                    """Launch a subprocess with PTY support.
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    Args:
         | 
| 55 | 
            +
                        command: Command and arguments to execute
         | 
| 56 | 
            +
                        **kwargs: Additional subprocess options
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    Returns:
         | 
| 59 | 
            +
                        Dictionary with subprocess information and handles
         | 
| 60 | 
            +
                    """
         | 
| 61 | 
            +
                    # For now, delegate to the existing interactive method
         | 
| 62 | 
            +
                    # In a full implementation, this would return process info
         | 
| 63 | 
            +
                    try:
         | 
| 64 | 
            +
                        env = kwargs.get("env", self.prepare_subprocess_environment())
         | 
| 65 | 
            +
                        self.launch_subprocess_interactive(command, env)
         | 
| 66 | 
            +
                        return {"status": "launched", "command": command, "method": "interactive"}
         | 
| 67 | 
            +
                    except Exception as e:
         | 
| 68 | 
            +
                        return {"status": "failed", "error": str(e), "command": command}
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                async def launch_subprocess_async(
         | 
| 71 | 
            +
                    self, command: List[str], **kwargs
         | 
| 72 | 
            +
                ) -> Dict[str, Any]:
         | 
| 73 | 
            +
                    """Launch a subprocess asynchronously with PTY support.
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    Args:
         | 
| 76 | 
            +
                        command: Command and arguments to execute
         | 
| 77 | 
            +
                        **kwargs: Additional subprocess options
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    Returns:
         | 
| 80 | 
            +
                        Dictionary with subprocess information and handles
         | 
| 81 | 
            +
                    """
         | 
| 82 | 
            +
                    # For async version, we'd use asyncio subprocess
         | 
| 83 | 
            +
                    # For now, delegate to sync version
         | 
| 84 | 
            +
                    return self.launch_subprocess(command, **kwargs)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def terminate_subprocess(self, process_id: str) -> bool:
         | 
| 87 | 
            +
                    """Terminate a running subprocess.
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    Args:
         | 
| 90 | 
            +
                        process_id: ID of the process to terminate
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    Returns:
         | 
| 93 | 
            +
                        True if termination successful
         | 
| 94 | 
            +
                    """
         | 
| 95 | 
            +
                    # This would need process tracking to be implemented
         | 
| 96 | 
            +
                    # For now, return False as we don't track processes
         | 
| 97 | 
            +
                    self.logger.warning(f"Process termination not implemented for ID: {process_id}")
         | 
| 98 | 
            +
                    return False
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def get_subprocess_status(self, process_id: str) -> Dict[str, Any]:
         | 
| 101 | 
            +
                    """Get status of a running subprocess.
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    Args:
         | 
| 104 | 
            +
                        process_id: ID of the process
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    Returns:
         | 
| 107 | 
            +
                        Dictionary with process status information
         | 
| 108 | 
            +
                    """
         | 
| 109 | 
            +
                    # This would need process tracking to be implemented
         | 
| 110 | 
            +
                    # For now, return unknown status
         | 
| 111 | 
            +
                    return {
         | 
| 112 | 
            +
                        "process_id": process_id,
         | 
| 113 | 
            +
                        "status": "unknown",
         | 
| 114 | 
            +
                        "message": "Process tracking not implemented",
         | 
| 115 | 
            +
                    }
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                def launch_subprocess_interactive(
         | 
| 118 | 
            +
                    self, cmd: List[str], env: Dict[str, str]
         | 
| 119 | 
            +
                ) -> None:
         | 
| 120 | 
            +
                    """Launch Claude as a subprocess with PTY for interactive mode.
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    This method launches Claude as a subprocess when explicitly requested
         | 
| 123 | 
            +
                    (via --launch-method subprocess). Subprocess mode maintains the parent process,
         | 
| 124 | 
            +
                    which can be useful for:
         | 
| 125 | 
            +
                    1. Maintaining WebSocket connections and monitoring
         | 
| 126 | 
            +
                    2. Providing proper cleanup and error handling
         | 
| 127 | 
            +
                    3. Debugging and development scenarios
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    Uses PTY (pseudo-terminal) to maintain full interactive capabilities.
         | 
| 130 | 
            +
                    Response logging is handled through the hook system, not I/O interception,
         | 
| 131 | 
            +
                    for better performance and compatibility.
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    Args:
         | 
| 134 | 
            +
                        cmd: Command list to execute
         | 
| 135 | 
            +
                        env: Environment variables for the subprocess
         | 
| 136 | 
            +
                    """
         | 
| 137 | 
            +
                    # Save original terminal settings
         | 
| 138 | 
            +
                    original_tty = None
         | 
| 139 | 
            +
                    if sys.stdin.isatty():
         | 
| 140 | 
            +
                        original_tty = termios.tcgetattr(sys.stdin)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    # Create PTY
         | 
| 143 | 
            +
                    master_fd, slave_fd = pty.openpty()
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    try:
         | 
| 146 | 
            +
                        # Start Claude process
         | 
| 147 | 
            +
                        process = subprocess.Popen(
         | 
| 148 | 
            +
                            cmd, stdin=slave_fd, stdout=slave_fd, stderr=slave_fd, env=env
         | 
| 149 | 
            +
                        )
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                        # Close slave in parent
         | 
| 152 | 
            +
                        os.close(slave_fd)
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                        if self.project_logger:
         | 
| 155 | 
            +
                            self.project_logger.log_system(
         | 
| 156 | 
            +
                                f"Claude subprocess started with PID {process.pid}",
         | 
| 157 | 
            +
                                level="INFO",
         | 
| 158 | 
            +
                                component="subprocess",
         | 
| 159 | 
            +
                            )
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                        # Notify WebSocket clients
         | 
| 162 | 
            +
                        if self.websocket_server:
         | 
| 163 | 
            +
                            self.websocket_server.claude_status_changed(
         | 
| 164 | 
            +
                                status="running",
         | 
| 165 | 
            +
                                pid=process.pid,
         | 
| 166 | 
            +
                                message="Claude subprocess started",
         | 
| 167 | 
            +
                            )
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                        # Set terminal to raw mode for proper interaction
         | 
| 170 | 
            +
                        if sys.stdin.isatty():
         | 
| 171 | 
            +
                            tty.setraw(sys.stdin)
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                        # Handle Ctrl+C gracefully
         | 
| 174 | 
            +
                        def signal_handler(signum, frame):
         | 
| 175 | 
            +
                            if process.poll() is None:
         | 
| 176 | 
            +
                                process.terminate()
         | 
| 177 | 
            +
                            raise KeyboardInterrupt()
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                        signal.signal(signal.SIGINT, signal_handler)
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                        # I/O loop
         | 
| 182 | 
            +
                        self._handle_subprocess_io(master_fd, process)
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                        # Wait for process to complete
         | 
| 185 | 
            +
                        process.wait()
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                        # Note: Response logging is handled through the hook system
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                        if self.project_logger:
         | 
| 190 | 
            +
                            self.project_logger.log_system(
         | 
| 191 | 
            +
                                f"Claude subprocess exited with code {process.returncode}",
         | 
| 192 | 
            +
                                level="INFO",
         | 
| 193 | 
            +
                                component="subprocess",
         | 
| 194 | 
            +
                            )
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                        # Notify WebSocket clients
         | 
| 197 | 
            +
                        if self.websocket_server:
         | 
| 198 | 
            +
                            self.websocket_server.claude_status_changed(
         | 
| 199 | 
            +
                                status="stopped",
         | 
| 200 | 
            +
                                message=f"Claude subprocess exited with code {process.returncode}",
         | 
| 201 | 
            +
                            )
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                    finally:
         | 
| 204 | 
            +
                        # Restore terminal
         | 
| 205 | 
            +
                        if original_tty and sys.stdin.isatty():
         | 
| 206 | 
            +
                            termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_tty)
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                        # Close PTY
         | 
| 209 | 
            +
                        try:
         | 
| 210 | 
            +
                            os.close(master_fd)
         | 
| 211 | 
            +
                        except:
         | 
| 212 | 
            +
                            pass
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                        # Ensure process is terminated
         | 
| 215 | 
            +
                        if "process" in locals() and process.poll() is None:
         | 
| 216 | 
            +
                            process.terminate()
         | 
| 217 | 
            +
                            try:
         | 
| 218 | 
            +
                                process.wait(timeout=2)
         | 
| 219 | 
            +
                            except subprocess.TimeoutExpired:
         | 
| 220 | 
            +
                                process.kill()
         | 
| 221 | 
            +
                                process.wait()
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                        # End WebSocket session if in subprocess mode
         | 
| 224 | 
            +
                        if self.websocket_server:
         | 
| 225 | 
            +
                            self.websocket_server.session_ended()
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                def _handle_subprocess_io(self, master_fd: int, process: subprocess.Popen) -> None:
         | 
| 228 | 
            +
                    """Handle I/O between the subprocess and the terminal.
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                    Args:
         | 
| 231 | 
            +
                        master_fd: Master file descriptor for the PTY
         | 
| 232 | 
            +
                        process: The subprocess instance
         | 
| 233 | 
            +
                    """
         | 
| 234 | 
            +
                    while True:
         | 
| 235 | 
            +
                        # Check if process is still running
         | 
| 236 | 
            +
                        if process.poll() is not None:
         | 
| 237 | 
            +
                            break
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                        # Check for data from Claude or stdin
         | 
| 240 | 
            +
                        r, _, _ = select.select([master_fd, sys.stdin], [], [], 0)
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                        if master_fd in r:
         | 
| 243 | 
            +
                            try:
         | 
| 244 | 
            +
                                data = os.read(master_fd, 4096)
         | 
| 245 | 
            +
                                if data:
         | 
| 246 | 
            +
                                    os.write(sys.stdout.fileno(), data)
         | 
| 247 | 
            +
                                    # Broadcast output to WebSocket clients
         | 
| 248 | 
            +
                                    if self.websocket_server:
         | 
| 249 | 
            +
                                        try:
         | 
| 250 | 
            +
                                            # Decode and send
         | 
| 251 | 
            +
                                            output = data.decode("utf-8", errors="replace")
         | 
| 252 | 
            +
                                            self.websocket_server.claude_output(output, "stdout")
         | 
| 253 | 
            +
                                        except Exception as e:
         | 
| 254 | 
            +
                                            self.logger.debug(f"Failed to broadcast output: {e}")
         | 
| 255 | 
            +
                                else:
         | 
| 256 | 
            +
                                    break  # EOF
         | 
| 257 | 
            +
                            except OSError:
         | 
| 258 | 
            +
                                break
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                        if sys.stdin in r:
         | 
| 261 | 
            +
                            try:
         | 
| 262 | 
            +
                                data = os.read(sys.stdin.fileno(), 4096)
         | 
| 263 | 
            +
                                if data:
         | 
| 264 | 
            +
                                    os.write(master_fd, data)
         | 
| 265 | 
            +
                            except OSError:
         | 
| 266 | 
            +
                                break
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                def is_subprocess_mode_available(self) -> bool:
         | 
| 269 | 
            +
                    """Check if subprocess mode is available on this platform.
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                    Returns:
         | 
| 272 | 
            +
                        True if subprocess mode with PTY is available
         | 
| 273 | 
            +
                    """
         | 
| 274 | 
            +
                    try:
         | 
| 275 | 
            +
                        # Check if we can import required modules
         | 
| 276 | 
            +
                        import pty
         | 
| 277 | 
            +
                        import select
         | 
| 278 | 
            +
                        import termios
         | 
| 279 | 
            +
                        import tty
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                        return True
         | 
| 282 | 
            +
                    except ImportError:
         | 
| 283 | 
            +
                        return False
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                def create_subprocess_command(
         | 
| 286 | 
            +
                    self, base_cmd: List[str], additional_args: Optional[List[str]] = None
         | 
| 287 | 
            +
                ) -> List[str]:
         | 
| 288 | 
            +
                    """Create a subprocess command with proper arguments.
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                    Args:
         | 
| 291 | 
            +
                        base_cmd: Base command to execute
         | 
| 292 | 
            +
                        additional_args: Additional arguments to append
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                    Returns:
         | 
| 295 | 
            +
                        Complete command list for subprocess execution
         | 
| 296 | 
            +
                    """
         | 
| 297 | 
            +
                    cmd = base_cmd.copy()
         | 
| 298 | 
            +
                    if additional_args:
         | 
| 299 | 
            +
                        cmd.extend(additional_args)
         | 
| 300 | 
            +
                    return cmd
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                def prepare_subprocess_environment(
         | 
| 303 | 
            +
                    self, base_env: Optional[Dict[str, str]] = None
         | 
| 304 | 
            +
                ) -> Dict[str, str]:
         | 
| 305 | 
            +
                    """Prepare environment variables for subprocess execution.
         | 
| 306 | 
            +
             | 
| 307 | 
            +
                    Args:
         | 
| 308 | 
            +
                        base_env: Base environment variables to extend
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                    Returns:
         | 
| 311 | 
            +
                        Complete environment dictionary for subprocess
         | 
| 312 | 
            +
                    """
         | 
| 313 | 
            +
                    env = os.environ.copy()
         | 
| 314 | 
            +
                    if base_env:
         | 
| 315 | 
            +
                        env.update(base_env)
         | 
| 316 | 
            +
                    return env
         | 
| @@ -0,0 +1,258 @@ | |
| 1 | 
            +
            from pathlib import Path
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            """System instructions service for loading and processing system instructions.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            This service handles:
         | 
| 6 | 
            +
            1. Loading system instructions from multiple sources (project, framework)
         | 
| 7 | 
            +
            2. Processing template variables in instructions
         | 
| 8 | 
            +
            3. Stripping metadata comments
         | 
| 9 | 
            +
            4. Creating system prompts with fallbacks
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Extracted from ClaudeRunner to follow Single Responsibility Principle.
         | 
| 12 | 
            +
            """
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            import re
         | 
| 15 | 
            +
            from datetime import datetime
         | 
| 16 | 
            +
            from typing import List, Optional, Tuple
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            from claude_mpm.config.paths import paths
         | 
| 19 | 
            +
            from claude_mpm.core.base_service import BaseService
         | 
| 20 | 
            +
            from claude_mpm.services.core.interfaces import SystemInstructionsInterface
         | 
| 21 | 
            +
             | 
| 22 | 
            +
             | 
| 23 | 
            +
            class SystemInstructionsService(BaseService, SystemInstructionsInterface):
         | 
| 24 | 
            +
                """Service for loading and processing system instructions."""
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def __init__(self, agent_capabilities_service=None):
         | 
| 27 | 
            +
                    """Initialize the system instructions service.
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    Args:
         | 
| 30 | 
            +
                        agent_capabilities_service: Optional service for generating agent capabilities
         | 
| 31 | 
            +
                    """
         | 
| 32 | 
            +
                    super().__init__(name="system_instructions_service")
         | 
| 33 | 
            +
                    self.agent_capabilities_service = agent_capabilities_service
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                async def _initialize(self) -> None:
         | 
| 36 | 
            +
                    """Initialize the service. No special initialization needed."""
         | 
| 37 | 
            +
                    pass
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                async def _cleanup(self) -> None:
         | 
| 40 | 
            +
                    """Cleanup service resources. No cleanup needed."""
         | 
| 41 | 
            +
                    pass
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def load_system_instructions(self, instruction_type: str = "default") -> str:
         | 
| 44 | 
            +
                    """Load and process system instructions from agents/INSTRUCTIONS.md.
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    Args:
         | 
| 47 | 
            +
                        instruction_type: Type of instructions to load (currently only "default" supported)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    Now uses the FrameworkLoader for comprehensive instruction loading including:
         | 
| 50 | 
            +
                    - INSTRUCTIONS.md
         | 
| 51 | 
            +
                    - WORKFLOW.md  
         | 
| 52 | 
            +
                    - MEMORY.md
         | 
| 53 | 
            +
                    - Actual PM memories from .claude-mpm/memories/PM.md
         | 
| 54 | 
            +
                    - Agent capabilities
         | 
| 55 | 
            +
                    - BASE_PM.md
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    Returns:
         | 
| 58 | 
            +
                        Processed system instructions string
         | 
| 59 | 
            +
                    """
         | 
| 60 | 
            +
                    try:
         | 
| 61 | 
            +
                        # Use FrameworkLoader for comprehensive instruction loading
         | 
| 62 | 
            +
                        from claude_mpm.core.framework_loader import FrameworkLoader
         | 
| 63 | 
            +
                        
         | 
| 64 | 
            +
                        loader = FrameworkLoader()
         | 
| 65 | 
            +
                        instructions = loader.get_framework_instructions()
         | 
| 66 | 
            +
                        
         | 
| 67 | 
            +
                        if instructions:
         | 
| 68 | 
            +
                            self.logger.info("Loaded framework instructions via FrameworkLoader")
         | 
| 69 | 
            +
                            return instructions
         | 
| 70 | 
            +
                        
         | 
| 71 | 
            +
                        # Fallback if FrameworkLoader returns empty
         | 
| 72 | 
            +
                        self.logger.warning("FrameworkLoader returned empty instructions, using fallback")
         | 
| 73 | 
            +
                        return "# System Instructions\n\nNo specific system instructions found. Using default behavior."
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    except Exception as e:
         | 
| 76 | 
            +
                        self.logger.error(f"Failed to load system instructions: {e}")
         | 
| 77 | 
            +
                        return "# System Instructions\n\nError loading system instructions. Using default behavior."
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def process_base_pm_content(self, base_pm_content: str) -> str:
         | 
| 80 | 
            +
                    """Process BASE_PM.md content with dynamic injections.
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    This method replaces template variables in BASE_PM.md with:
         | 
| 83 | 
            +
                    - {{AGENT_CAPABILITIES}}: List of deployed agents from .claude/agents/
         | 
| 84 | 
            +
                    - {{VERSION}}: Current framework version
         | 
| 85 | 
            +
                    - {{CURRENT_DATE}}: Today's date for temporal context
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    Args:
         | 
| 88 | 
            +
                        base_pm_content: Raw BASE_PM.md content
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    Returns:
         | 
| 91 | 
            +
                        Processed content with variables replaced
         | 
| 92 | 
            +
                    """
         | 
| 93 | 
            +
                    try:
         | 
| 94 | 
            +
                        # Replace agent capabilities if service is available
         | 
| 95 | 
            +
                        if (
         | 
| 96 | 
            +
                            self.agent_capabilities_service
         | 
| 97 | 
            +
                            and "{{AGENT_CAPABILITIES}}" in base_pm_content
         | 
| 98 | 
            +
                        ):
         | 
| 99 | 
            +
                            capabilities = (
         | 
| 100 | 
            +
                                self.agent_capabilities_service.generate_deployed_agent_capabilities()
         | 
| 101 | 
            +
                            )
         | 
| 102 | 
            +
                            base_pm_content = base_pm_content.replace(
         | 
| 103 | 
            +
                                "{{AGENT_CAPABILITIES}}", capabilities
         | 
| 104 | 
            +
                            )
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                        # Replace version
         | 
| 107 | 
            +
                        if "{{VERSION}}" in base_pm_content:
         | 
| 108 | 
            +
                            version = self._get_version()
         | 
| 109 | 
            +
                            base_pm_content = base_pm_content.replace("{{VERSION}}", version)
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                        # Replace current date
         | 
| 112 | 
            +
                        if "{{CURRENT_DATE}}" in base_pm_content:
         | 
| 113 | 
            +
                            current_date = datetime.now().strftime("%Y-%m-%d")
         | 
| 114 | 
            +
                            base_pm_content = base_pm_content.replace(
         | 
| 115 | 
            +
                                "{{CURRENT_DATE}}", current_date
         | 
| 116 | 
            +
                            )
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    except Exception as e:
         | 
| 119 | 
            +
                        self.logger.warning(f"Error processing BASE_PM content: {e}")
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    return base_pm_content
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                def strip_metadata_comments(self, content: str) -> str:
         | 
| 124 | 
            +
                    """Strip HTML metadata comments from content.
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                    Removes comments like:
         | 
| 127 | 
            +
                    <!-- FRAMEWORK_VERSION: 0010 -->
         | 
| 128 | 
            +
                    <!-- LAST_MODIFIED: 2025-08-10T00:00:00Z -->
         | 
| 129 | 
            +
                    <!-- metadata: {...} -->
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                    Args:
         | 
| 132 | 
            +
                        content: Content with potential metadata comments
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    Returns:
         | 
| 135 | 
            +
                        Content with metadata comments removed
         | 
| 136 | 
            +
                    """
         | 
| 137 | 
            +
                    try:
         | 
| 138 | 
            +
                        # Remove HTML comments that contain metadata keywords
         | 
| 139 | 
            +
                        metadata_patterns = [
         | 
| 140 | 
            +
                            r"<!--\s*FRAMEWORK_VERSION:.*?-->",
         | 
| 141 | 
            +
                            r"<!--\s*LAST_MODIFIED:.*?-->",
         | 
| 142 | 
            +
                            r"<!--\s*metadata:.*?-->",
         | 
| 143 | 
            +
                            r"<!--\s*META:.*?-->",
         | 
| 144 | 
            +
                            r"<!--\s*VERSION:.*?-->",
         | 
| 145 | 
            +
                        ]
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                        cleaned = content
         | 
| 148 | 
            +
                        for pattern in metadata_patterns:
         | 
| 149 | 
            +
                            cleaned = re.sub(pattern, "", cleaned, flags=re.DOTALL | re.IGNORECASE)
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                        # Remove any remaining empty lines that might result from comment removal
         | 
| 152 | 
            +
                        lines = cleaned.split("\n")
         | 
| 153 | 
            +
                        cleaned_lines = []
         | 
| 154 | 
            +
                        prev_empty = False
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                        for line in lines:
         | 
| 157 | 
            +
                            is_empty = not line.strip()
         | 
| 158 | 
            +
                            if not (is_empty and prev_empty):  # Avoid consecutive empty lines
         | 
| 159 | 
            +
                                cleaned_lines.append(line)
         | 
| 160 | 
            +
                            prev_empty = is_empty
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                        cleaned = "\n".join(cleaned_lines)
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                        # Also remove any leading blank lines that might result
         | 
| 165 | 
            +
                        cleaned = cleaned.lstrip("\n")
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                        return cleaned
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                    except Exception as e:
         | 
| 170 | 
            +
                        self.logger.warning(f"Error stripping metadata comments: {e}")
         | 
| 171 | 
            +
                        return content
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                def create_system_prompt(self, system_instructions: Optional[str] = None) -> str:
         | 
| 174 | 
            +
                    """Create the complete system prompt including instructions.
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                    Args:
         | 
| 177 | 
            +
                        system_instructions: Optional pre-loaded instructions, will load if None
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    Returns:
         | 
| 180 | 
            +
                        Complete system prompt
         | 
| 181 | 
            +
                    """
         | 
| 182 | 
            +
                    if system_instructions is None:
         | 
| 183 | 
            +
                        system_instructions = self.load_system_instructions()
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    return system_instructions
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                def _process_base_pm_content(self, base_pm_content: str) -> str:
         | 
| 188 | 
            +
                    """Internal method for processing BASE_PM content."""
         | 
| 189 | 
            +
                    return self.process_base_pm_content(base_pm_content)
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                def _strip_metadata_comments(self, content: str) -> str:
         | 
| 192 | 
            +
                    """Internal method for stripping metadata comments."""
         | 
| 193 | 
            +
                    return self.strip_metadata_comments(content)
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                def _get_version(self) -> str:
         | 
| 196 | 
            +
                    """Get the current framework version.
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                    Returns:
         | 
| 199 | 
            +
                        Version string or 'unknown' if not found
         | 
| 200 | 
            +
                    """
         | 
| 201 | 
            +
                    try:
         | 
| 202 | 
            +
                        version_file = paths.project_root / "VERSION"
         | 
| 203 | 
            +
                        if version_file.exists():
         | 
| 204 | 
            +
                            return version_file.read_text().strip()
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                        # Try to get version from package info
         | 
| 207 | 
            +
                        try:
         | 
| 208 | 
            +
                            import claude_mpm
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                            if hasattr(claude_mpm, "__version__"):
         | 
| 211 | 
            +
                                return claude_mpm.__version__
         | 
| 212 | 
            +
                        except ImportError:
         | 
| 213 | 
            +
                            pass
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                        return "unknown"
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    except Exception as e:
         | 
| 218 | 
            +
                        self.logger.debug(f"Could not determine version: {e}")
         | 
| 219 | 
            +
                        return "unknown"
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                def get_available_instruction_types(self) -> List[str]:
         | 
| 222 | 
            +
                    """Get list of available instruction types.
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                    Returns:
         | 
| 225 | 
            +
                        List of available instruction type names
         | 
| 226 | 
            +
                    """
         | 
| 227 | 
            +
                    # Currently only "default" type is supported
         | 
| 228 | 
            +
                    return ["default"]
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                def validate_instructions(self, instructions: str) -> Tuple[bool, List[str]]:
         | 
| 231 | 
            +
                    """Validate system instructions format and content.
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                    Args:
         | 
| 234 | 
            +
                        instructions: Instructions content to validate
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                    Returns:
         | 
| 237 | 
            +
                        Tuple of (is_valid, list_of_errors)
         | 
| 238 | 
            +
                    """
         | 
| 239 | 
            +
                    errors = []
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                    if not instructions or not instructions.strip():
         | 
| 242 | 
            +
                        errors.append("Instructions cannot be empty")
         | 
| 243 | 
            +
                        return False, errors
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                    # Check for basic structure
         | 
| 246 | 
            +
                    if len(instructions.strip()) < 10:
         | 
| 247 | 
            +
                        errors.append("Instructions appear to be too short")
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                    # Check for potentially problematic content
         | 
| 250 | 
            +
                    if "{{" in instructions and "}}" in instructions:
         | 
| 251 | 
            +
                        # Check if template variables are properly processed
         | 
| 252 | 
            +
                        unprocessed_vars = re.findall(r"\{\{([^}]+)\}\}", instructions)
         | 
| 253 | 
            +
                        if unprocessed_vars:
         | 
| 254 | 
            +
                            errors.append(
         | 
| 255 | 
            +
                                f"Unprocessed template variables found: {', '.join(unprocessed_vars)}"
         | 
| 256 | 
            +
                            )
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                    return len(errors) == 0, errors
         |