claude-mpm 3.9.11__py3-none-any.whl → 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +79 -51
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +20 -20
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +140 -905
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1156
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +71 -73
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +35 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +233 -199
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +30 -13
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
- claude_mpm/services/mcp_gateway/main.py +287 -137
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +19 -533
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +2 -2
- claude_mpm/storage/state_storage.py +177 -181
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/cli/commands/run_guarded.py +0 -511
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/config/memory_guardian_yaml.py +0 -335
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/core/memory_aware_runner.py +0 -353
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
- claude_mpm/services/infrastructure/health_monitor.py +0 -775
- claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
- claude_mpm/services/infrastructure/memory_guardian.py +0 -944
- claude_mpm/services/infrastructure/restart_protection.py +0 -642
- claude_mpm/services/infrastructure/state_manager.py +0 -774
- claude_mpm/services/mcp_gateway/manager.py +0 -334
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.11.dist-info/RECORD +0 -306
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -1,13 +1,14 @@ | |
| 1 | 
            +
            from pathlib import Path
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            """
         | 
| 2 4 | 
             
            Session Logging Utilities
         | 
| 3 5 |  | 
| 4 6 | 
             
            Convenience functions for session-based response logging.
         | 
| 5 7 | 
             
            """
         | 
| 6 8 |  | 
| 7 | 
            -
            import os
         | 
| 8 | 
            -
            from typing import Optional, Dict, Any
         | 
| 9 | 
            -
            from pathlib import Path
         | 
| 10 9 | 
             
            import logging
         | 
| 10 | 
            +
            import os
         | 
| 11 | 
            +
            from typing import Any, Dict, Optional
         | 
| 11 12 |  | 
| 12 13 | 
             
            from claude_mpm.services.claude_session_logger import get_session_logger
         | 
| 13 14 |  | 
| @@ -17,14 +18,18 @@ logger = logging.getLogger(__name__) | |
| 17 18 | 
             
            def is_session_logging_enabled() -> bool:
         | 
| 18 19 | 
             
                """
         | 
| 19 20 | 
             
                Check if session logging is enabled.
         | 
| 20 | 
            -
             | 
| 21 | 
            +
             | 
| 21 22 | 
             
                Returns:
         | 
| 22 23 | 
             
                    True if session logging is enabled and configured
         | 
| 23 24 | 
             
                """
         | 
| 24 25 | 
             
                # Check environment variable to disable
         | 
| 25 | 
            -
                if os.environ.get( | 
| 26 | 
            +
                if os.environ.get("CLAUDE_MPM_NO_SESSION_LOGGING", "").lower() in (
         | 
| 27 | 
            +
                    "1",
         | 
| 28 | 
            +
                    "true",
         | 
| 29 | 
            +
                    "yes",
         | 
| 30 | 
            +
                ):
         | 
| 26 31 | 
             
                    return False
         | 
| 27 | 
            -
             | 
| 32 | 
            +
             | 
| 28 33 | 
             
                # Check if session logger has a valid session ID
         | 
| 29 34 | 
             
                session_logger = get_session_logger()
         | 
| 30 35 | 
             
                return session_logger.is_enabled()
         | 
| @@ -34,44 +39,46 @@ def log_agent_response( | |
| 34 39 | 
             
                agent_name: str,
         | 
| 35 40 | 
             
                request: str,
         | 
| 36 41 | 
             
                response: str,
         | 
| 37 | 
            -
                metadata: Optional[Dict[str, Any]] = None
         | 
| 42 | 
            +
                metadata: Optional[Dict[str, Any]] = None,
         | 
| 38 43 | 
             
            ) -> Optional[Path]:
         | 
| 39 44 | 
             
                """
         | 
| 40 45 | 
             
                Log an agent response to the current session.
         | 
| 41 | 
            -
             | 
| 46 | 
            +
             | 
| 42 47 | 
             
                Args:
         | 
| 43 48 | 
             
                    agent_name: Name of the agent
         | 
| 44 49 | 
             
                    request: The request/prompt sent to the agent
         | 
| 45 50 | 
             
                    response: The agent's response
         | 
| 46 51 | 
             
                    metadata: Optional additional metadata
         | 
| 47 | 
            -
             | 
| 52 | 
            +
             | 
| 48 53 | 
             
                Returns:
         | 
| 49 54 | 
             
                    Path to the logged file, or None if logging is disabled
         | 
| 50 55 | 
             
                """
         | 
| 51 56 | 
             
                if not is_session_logging_enabled():
         | 
| 52 57 | 
             
                    return None
         | 
| 53 | 
            -
             | 
| 58 | 
            +
             | 
| 54 59 | 
             
                # Create request summary
         | 
| 55 | 
            -
                request_summary =  | 
| 56 | 
            -
             | 
| 60 | 
            +
                request_summary = (
         | 
| 61 | 
            +
                    f"[{agent_name}] {request[:100]}..."
         | 
| 62 | 
            +
                    if len(request) > 100
         | 
| 63 | 
            +
                    else f"[{agent_name}] {request}"
         | 
| 64 | 
            +
                )
         | 
| 65 | 
            +
             | 
| 57 66 | 
             
                # Add agent name to metadata
         | 
| 58 67 | 
             
                if metadata is None:
         | 
| 59 68 | 
             
                    metadata = {}
         | 
| 60 | 
            -
                metadata[ | 
| 61 | 
            -
             | 
| 69 | 
            +
                metadata["agent"] = agent_name
         | 
| 70 | 
            +
             | 
| 62 71 | 
             
                # Log the response
         | 
| 63 72 | 
             
                session_logger = get_session_logger()
         | 
| 64 73 | 
             
                return session_logger.log_response(
         | 
| 65 | 
            -
                    request_summary=request_summary,
         | 
| 66 | 
            -
                    response_content=response,
         | 
| 67 | 
            -
                    metadata=metadata
         | 
| 74 | 
            +
                    request_summary=request_summary, response_content=response, metadata=metadata
         | 
| 68 75 | 
             
                )
         | 
| 69 76 |  | 
| 70 77 |  | 
| 71 78 | 
             
            def get_current_session_id() -> Optional[str]:
         | 
| 72 79 | 
             
                """
         | 
| 73 80 | 
             
                Get the current session ID.
         | 
| 74 | 
            -
             | 
| 81 | 
            +
             | 
| 75 82 | 
             
                Returns:
         | 
| 76 83 | 
             
                    Current session ID, or None if not available
         | 
| 77 84 | 
             
                """
         | 
| @@ -82,7 +89,7 @@ def get_current_session_id() -> Optional[str]: | |
| 82 89 | 
             
            def get_session_directory() -> Optional[Path]:
         | 
| 83 90 | 
             
                """
         | 
| 84 91 | 
             
                Get the directory for the current session.
         | 
| 85 | 
            -
             | 
| 92 | 
            +
             | 
| 86 93 | 
             
                Returns:
         | 
| 87 94 | 
             
                    Path to session directory, or None if not available
         | 
| 88 95 | 
             
                """
         | 
| @@ -93,7 +100,7 @@ def get_session_directory() -> Optional[Path]: | |
| 93 100 | 
             
            def set_session_id(session_id: str) -> None:
         | 
| 94 101 | 
             
                """
         | 
| 95 102 | 
             
                Manually set the session ID.
         | 
| 96 | 
            -
             | 
| 103 | 
            +
             | 
| 97 104 | 
             
                Args:
         | 
| 98 105 | 
             
                    session_id: The session ID to use
         | 
| 99 106 | 
             
                """
         | 
| @@ -105,10 +112,10 @@ def set_session_id(session_id: str) -> None: | |
| 105 112 | 
             
            # Environment variable helpers
         | 
| 106 113 | 
             
            def enable_session_logging() -> None:
         | 
| 107 114 | 
             
                """Enable session logging by removing disable flag."""
         | 
| 108 | 
            -
                if  | 
| 109 | 
            -
                    del os.environ[ | 
| 115 | 
            +
                if "CLAUDE_MPM_NO_SESSION_LOGGING" in os.environ:
         | 
| 116 | 
            +
                    del os.environ["CLAUDE_MPM_NO_SESSION_LOGGING"]
         | 
| 110 117 |  | 
| 111 118 |  | 
| 112 119 | 
             
            def disable_session_logging() -> None:
         | 
| 113 120 | 
             
                """Disable session logging via environment variable."""
         | 
| 114 | 
            -
                os.environ[ | 
| 121 | 
            +
                os.environ["CLAUDE_MPM_NO_SESSION_LOGGING"] = "1"
         | 
| @@ -7,20 +7,28 @@ with proper error handling, timeouts, and process cleanup. | |
| 7 7 | 
             
            """
         | 
| 8 8 |  | 
| 9 9 | 
             
            import asyncio
         | 
| 10 | 
            -
            import  | 
| 10 | 
            +
            import logging
         | 
| 11 | 
            +
            import shlex
         | 
| 11 12 | 
             
            import signal
         | 
| 12 13 | 
             
            import subprocess
         | 
| 13 | 
            -
            import sys
         | 
| 14 14 | 
             
            import time
         | 
| 15 | 
            -
            from typing import Dict, List, Optional, Union | 
| 15 | 
            +
            from typing import Any, Dict, List, Optional, Union
         | 
| 16 | 
            +
             | 
| 16 17 | 
             
            import psutil
         | 
| 17 18 |  | 
| 19 | 
            +
            logger = logging.getLogger(__name__)
         | 
| 20 | 
            +
             | 
| 18 21 |  | 
| 19 22 | 
             
            class SubprocessError(Exception):
         | 
| 20 23 | 
             
                """Unified exception for subprocess errors."""
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                def __init__( | 
| 23 | 
            -
             | 
| 24 | 
            +
             | 
| 25 | 
            +
                def __init__(
         | 
| 26 | 
            +
                    self,
         | 
| 27 | 
            +
                    message: str,
         | 
| 28 | 
            +
                    returncode: Optional[int] = None,
         | 
| 29 | 
            +
                    stdout: Optional[str] = None,
         | 
| 30 | 
            +
                    stderr: Optional[str] = None,
         | 
| 31 | 
            +
                ):
         | 
| 24 32 | 
             
                    super().__init__(message)
         | 
| 25 33 | 
             
                    self.returncode = returncode
         | 
| 26 34 | 
             
                    self.stdout = stdout
         | 
| @@ -29,18 +37,55 @@ class SubprocessError(Exception): | |
| 29 37 |  | 
| 30 38 | 
             
            class SubprocessResult:
         | 
| 31 39 | 
             
                """Result object for subprocess execution."""
         | 
| 32 | 
            -
             | 
| 40 | 
            +
             | 
| 33 41 | 
             
                def __init__(self, returncode: int, stdout: str = "", stderr: str = ""):
         | 
| 34 42 | 
             
                    self.returncode = returncode
         | 
| 35 43 | 
             
                    self.stdout = stdout
         | 
| 36 44 | 
             
                    self.stderr = stderr
         | 
| 37 | 
            -
             | 
| 45 | 
            +
             | 
| 38 46 | 
             
                @property
         | 
| 39 47 | 
             
                def success(self) -> bool:
         | 
| 40 48 | 
             
                    """True if the subprocess completed successfully."""
         | 
| 41 49 | 
             
                    return self.returncode == 0
         | 
| 42 50 |  | 
| 43 51 |  | 
| 52 | 
            +
            def run_command(command_string: str, timeout: float = 60) -> str:
         | 
| 53 | 
            +
                """
         | 
| 54 | 
            +
                Runs a command securely, avoiding shell injection.
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                Args:
         | 
| 57 | 
            +
                    command_string: Command string to execute
         | 
| 58 | 
            +
                    timeout: Maximum time to wait for completion (seconds)
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                Returns:
         | 
| 61 | 
            +
                    Command stdout output
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                Raises:
         | 
| 64 | 
            +
                    SubprocessError: If the command fails or times out
         | 
| 65 | 
            +
                """
         | 
| 66 | 
            +
                # Split command string into a list to avoid shell=True
         | 
| 67 | 
            +
                command_parts = shlex.split(command_string)
         | 
| 68 | 
            +
                try:
         | 
| 69 | 
            +
                    result = subprocess.run(
         | 
| 70 | 
            +
                        command_parts,
         | 
| 71 | 
            +
                        capture_output=True,
         | 
| 72 | 
            +
                        text=True,
         | 
| 73 | 
            +
                        check=True,  # Raise an exception for non-zero exit codes
         | 
| 74 | 
            +
                        timeout=timeout,  # Prevent the process from hanging
         | 
| 75 | 
            +
                    )
         | 
| 76 | 
            +
                    return result.stdout
         | 
| 77 | 
            +
                except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
         | 
| 78 | 
            +
                    # Log the error, including stderr for better debugging
         | 
| 79 | 
            +
                    stderr = e.stderr if hasattr(e, "stderr") else "N/A"
         | 
| 80 | 
            +
                    logger.error(f'Command "{command_string}" failed: {stderr}')
         | 
| 81 | 
            +
                    raise SubprocessError(
         | 
| 82 | 
            +
                        f'Command "{command_string}" failed: {stderr}',
         | 
| 83 | 
            +
                        returncode=getattr(e, "returncode", None),
         | 
| 84 | 
            +
                        stdout=getattr(e, "stdout", ""),
         | 
| 85 | 
            +
                        stderr=stderr,
         | 
| 86 | 
            +
                    )
         | 
| 87 | 
            +
             | 
| 88 | 
            +
             | 
| 44 89 | 
             
            def run_subprocess(
         | 
| 45 90 | 
             
                cmd: List[str],
         | 
| 46 91 | 
             
                timeout: Optional[float] = None,
         | 
| @@ -48,11 +93,11 @@ def run_subprocess( | |
| 48 93 | 
             
                text: bool = True,
         | 
| 49 94 | 
             
                cwd: Optional[str] = None,
         | 
| 50 95 | 
             
                env: Optional[Dict[str, str]] = None,
         | 
| 51 | 
            -
                **kwargs
         | 
| 96 | 
            +
                **kwargs,
         | 
| 52 97 | 
             
            ) -> SubprocessResult:
         | 
| 53 98 | 
             
                """
         | 
| 54 99 | 
             
                Run a subprocess with enhanced error handling and timeout support.
         | 
| 55 | 
            -
             | 
| 100 | 
            +
             | 
| 56 101 | 
             
                Args:
         | 
| 57 102 | 
             
                    cmd: Command and arguments to execute
         | 
| 58 103 | 
             
                    timeout: Maximum time to wait for completion (seconds)
         | 
| @@ -61,10 +106,10 @@ def run_subprocess( | |
| 61 106 | 
             
                    cwd: Working directory for the subprocess
         | 
| 62 107 | 
             
                    env: Environment variables for the subprocess
         | 
| 63 108 | 
             
                    **kwargs: Additional arguments passed to subprocess.run
         | 
| 64 | 
            -
             | 
| 109 | 
            +
             | 
| 65 110 | 
             
                Returns:
         | 
| 66 111 | 
             
                    SubprocessResult object with returncode, stdout, stderr
         | 
| 67 | 
            -
             | 
| 112 | 
            +
             | 
| 68 113 | 
             
                Raises:
         | 
| 69 114 | 
             
                    SubprocessError: If the subprocess fails or times out
         | 
| 70 115 | 
             
                """
         | 
| @@ -76,28 +121,28 @@ def run_subprocess( | |
| 76 121 | 
             
                        text=text,
         | 
| 77 122 | 
             
                        cwd=cwd,
         | 
| 78 123 | 
             
                        env=env,
         | 
| 79 | 
            -
                        **kwargs
         | 
| 124 | 
            +
                        **kwargs,
         | 
| 80 125 | 
             
                    )
         | 
| 81 | 
            -
             | 
| 126 | 
            +
             | 
| 82 127 | 
             
                    return SubprocessResult(
         | 
| 83 128 | 
             
                        returncode=result.returncode,
         | 
| 84 129 | 
             
                        stdout=result.stdout if capture_output else "",
         | 
| 85 | 
            -
                        stderr=result.stderr if capture_output else ""
         | 
| 130 | 
            +
                        stderr=result.stderr if capture_output else "",
         | 
| 86 131 | 
             
                    )
         | 
| 87 | 
            -
             | 
| 132 | 
            +
             | 
| 88 133 | 
             
                except subprocess.TimeoutExpired as e:
         | 
| 89 134 | 
             
                    raise SubprocessError(
         | 
| 90 135 | 
             
                        f"Command timed out after {timeout}s: {' '.join(cmd)}",
         | 
| 91 136 | 
             
                        returncode=None,
         | 
| 92 137 | 
             
                        stdout=e.stdout.decode() if e.stdout else "",
         | 
| 93 | 
            -
                        stderr=e.stderr.decode() if e.stderr else ""
         | 
| 138 | 
            +
                        stderr=e.stderr.decode() if e.stderr else "",
         | 
| 94 139 | 
             
                    )
         | 
| 95 140 | 
             
                except subprocess.CalledProcessError as e:
         | 
| 96 141 | 
             
                    raise SubprocessError(
         | 
| 97 142 | 
             
                        f"Command failed with return code {e.returncode}: {' '.join(cmd)}",
         | 
| 98 143 | 
             
                        returncode=e.returncode,
         | 
| 99 144 | 
             
                        stdout=e.stdout if e.stdout else "",
         | 
| 100 | 
            -
                        stderr=e.stderr if e.stderr else ""
         | 
| 145 | 
            +
                        stderr=e.stderr if e.stderr else "",
         | 
| 101 146 | 
             
                    )
         | 
| 102 147 | 
             
                except Exception as e:
         | 
| 103 148 | 
             
                    raise SubprocessError(f"Subprocess execution failed: {e}")
         | 
| @@ -109,11 +154,11 @@ async def run_subprocess_async( | |
| 109 154 | 
             
                capture_output: bool = True,
         | 
| 110 155 | 
             
                cwd: Optional[str] = None,
         | 
| 111 156 | 
             
                env: Optional[Dict[str, str]] = None,
         | 
| 112 | 
            -
                **kwargs
         | 
| 157 | 
            +
                **kwargs,
         | 
| 113 158 | 
             
            ) -> SubprocessResult:
         | 
| 114 159 | 
             
                """
         | 
| 115 160 | 
             
                Run a subprocess asynchronously with timeout support.
         | 
| 116 | 
            -
             | 
| 161 | 
            +
             | 
| 117 162 | 
             
                Args:
         | 
| 118 163 | 
             
                    cmd: Command and arguments to execute
         | 
| 119 164 | 
             
                    timeout: Maximum time to wait for completion (seconds)
         | 
| @@ -121,10 +166,10 @@ async def run_subprocess_async( | |
| 121 166 | 
             
                    cwd: Working directory for the subprocess
         | 
| 122 167 | 
             
                    env: Environment variables for the subprocess
         | 
| 123 168 | 
             
                    **kwargs: Additional arguments passed to asyncio.create_subprocess_exec
         | 
| 124 | 
            -
             | 
| 169 | 
            +
             | 
| 125 170 | 
             
                Returns:
         | 
| 126 171 | 
             
                    SubprocessResult object with returncode, stdout, stderr
         | 
| 127 | 
            -
             | 
| 172 | 
            +
             | 
| 128 173 | 
             
                Raises:
         | 
| 129 174 | 
             
                    SubprocessError: If the subprocess fails or times out
         | 
| 130 175 | 
             
                """
         | 
| @@ -135,35 +180,28 @@ async def run_subprocess_async( | |
| 135 180 | 
             
                    else:
         | 
| 136 181 | 
             
                        stdout = None
         | 
| 137 182 | 
             
                        stderr = None
         | 
| 138 | 
            -
             | 
| 183 | 
            +
             | 
| 139 184 | 
             
                    process = await asyncio.create_subprocess_exec(
         | 
| 140 | 
            -
                        *cmd,
         | 
| 141 | 
            -
                        stdout=stdout,
         | 
| 142 | 
            -
                        stderr=stderr,
         | 
| 143 | 
            -
                        cwd=cwd,
         | 
| 144 | 
            -
                        env=env,
         | 
| 145 | 
            -
                        **kwargs
         | 
| 185 | 
            +
                        *cmd, stdout=stdout, stderr=stderr, cwd=cwd, env=env, **kwargs
         | 
| 146 186 | 
             
                    )
         | 
| 147 | 
            -
             | 
| 187 | 
            +
             | 
| 148 188 | 
             
                    try:
         | 
| 149 189 | 
             
                        stdout_data, stderr_data = await asyncio.wait_for(
         | 
| 150 | 
            -
                            process.communicate(),
         | 
| 151 | 
            -
                            timeout=timeout
         | 
| 190 | 
            +
                            process.communicate(), timeout=timeout
         | 
| 152 191 | 
             
                        )
         | 
| 153 192 | 
             
                    except asyncio.TimeoutError:
         | 
| 154 193 | 
             
                        process.kill()
         | 
| 155 194 | 
             
                        await process.wait()
         | 
| 156 195 | 
             
                        raise SubprocessError(
         | 
| 157 | 
            -
                            f"Command timed out after {timeout}s: {' '.join(cmd)}",
         | 
| 158 | 
            -
                            returncode=None
         | 
| 196 | 
            +
                            f"Command timed out after {timeout}s: {' '.join(cmd)}", returncode=None
         | 
| 159 197 | 
             
                        )
         | 
| 160 | 
            -
             | 
| 198 | 
            +
             | 
| 161 199 | 
             
                    return SubprocessResult(
         | 
| 162 200 | 
             
                        returncode=process.returncode,
         | 
| 163 201 | 
             
                        stdout=stdout_data.decode() if stdout_data else "",
         | 
| 164 | 
            -
                        stderr=stderr_data.decode() if stderr_data else ""
         | 
| 202 | 
            +
                        stderr=stderr_data.decode() if stderr_data else "",
         | 
| 165 203 | 
             
                    )
         | 
| 166 | 
            -
             | 
| 204 | 
            +
             | 
| 167 205 | 
             
                except Exception as e:
         | 
| 168 206 | 
             
                    if not isinstance(e, SubprocessError):
         | 
| 169 207 | 
             
                        raise SubprocessError(f"Async subprocess execution failed: {e}")
         | 
| @@ -173,14 +211,14 @@ async def run_subprocess_async( | |
| 173 211 | 
             
            def terminate_process_tree(pid: int, timeout: float = 5.0) -> int:
         | 
| 174 212 | 
             
                """
         | 
| 175 213 | 
             
                Terminate a process and all its children.
         | 
| 176 | 
            -
             | 
| 214 | 
            +
             | 
| 177 215 | 
             
                Args:
         | 
| 178 216 | 
             
                    pid: Process ID to terminate
         | 
| 179 217 | 
             
                    timeout: Time to wait for graceful termination before force killing
         | 
| 180 | 
            -
             | 
| 218 | 
            +
             | 
| 181 219 | 
             
                Returns:
         | 
| 182 220 | 
             
                    Number of processes terminated
         | 
| 183 | 
            -
             | 
| 221 | 
            +
             | 
| 184 222 | 
             
                Raises:
         | 
| 185 223 | 
             
                    SubprocessError: If the process cannot be terminated
         | 
| 186 224 | 
             
                """
         | 
| @@ -188,24 +226,24 @@ def terminate_process_tree(pid: int, timeout: float = 5.0) -> int: | |
| 188 226 | 
             
                    parent = psutil.Process(pid)
         | 
| 189 227 | 
             
                except psutil.NoSuchProcess:
         | 
| 190 228 | 
             
                    return 0
         | 
| 191 | 
            -
             | 
| 229 | 
            +
             | 
| 192 230 | 
             
                # Get all child processes recursively
         | 
| 193 231 | 
             
                children = parent.children(recursive=True)
         | 
| 194 232 | 
             
                processes = [parent] + children
         | 
| 195 | 
            -
             | 
| 233 | 
            +
             | 
| 196 234 | 
             
                terminated_count = 0
         | 
| 197 | 
            -
             | 
| 235 | 
            +
             | 
| 198 236 | 
             
                # First, try graceful termination
         | 
| 199 237 | 
             
                for process in processes:
         | 
| 200 238 | 
             
                    try:
         | 
| 201 239 | 
             
                        process.terminate()
         | 
| 202 240 | 
             
                    except psutil.NoSuchProcess:
         | 
| 203 241 | 
             
                        pass
         | 
| 204 | 
            -
             | 
| 242 | 
            +
             | 
| 205 243 | 
             
                # Wait for processes to terminate gracefully
         | 
| 206 244 | 
             
                gone, alive = psutil.wait_procs(processes, timeout=timeout)
         | 
| 207 245 | 
             
                terminated_count += len(gone)
         | 
| 208 | 
            -
             | 
| 246 | 
            +
             | 
| 209 247 | 
             
                # Force kill any remaining processes
         | 
| 210 248 | 
             
                for process in alive:
         | 
| 211 249 | 
             
                    try:
         | 
| @@ -213,17 +251,17 @@ def terminate_process_tree(pid: int, timeout: float = 5.0) -> int: | |
| 213 251 | 
             
                        terminated_count += 1
         | 
| 214 252 | 
             
                    except psutil.NoSuchProcess:
         | 
| 215 253 | 
             
                        pass
         | 
| 216 | 
            -
             | 
| 254 | 
            +
             | 
| 217 255 | 
             
                return terminated_count
         | 
| 218 256 |  | 
| 219 257 |  | 
| 220 258 | 
             
            def get_process_info(pid: Optional[int] = None) -> Dict[str, Any]:
         | 
| 221 259 | 
             
                """
         | 
| 222 260 | 
             
                Get information about a process.
         | 
| 223 | 
            -
             | 
| 261 | 
            +
             | 
| 224 262 | 
             
                Args:
         | 
| 225 263 | 
             
                    pid: Process ID (defaults to current process)
         | 
| 226 | 
            -
             | 
| 264 | 
            +
             | 
| 227 265 | 
             
                Returns:
         | 
| 228 266 | 
             
                    Dictionary with process information
         | 
| 229 267 | 
             
                """
         | 
| @@ -237,9 +275,9 @@ def get_process_info(pid: Optional[int] = None) -> Dict[str, Any]: | |
| 237 275 | 
             
                        "cpu_percent": process.cpu_percent(),
         | 
| 238 276 | 
             
                        "memory_info": process.memory_info()._asdict(),
         | 
| 239 277 | 
             
                        "cmdline": process.cmdline(),
         | 
| 240 | 
            -
                        "cwd": process.cwd() if hasattr(process,  | 
| 278 | 
            +
                        "cwd": process.cwd() if hasattr(process, "cwd") else None,
         | 
| 241 279 | 
             
                        "num_threads": process.num_threads(),
         | 
| 242 | 
            -
                        "children": [child.pid for child in process.children()]
         | 
| 280 | 
            +
                        "children": [child.pid for child in process.children()],
         | 
| 243 281 | 
             
                    }
         | 
| 244 282 | 
             
                except psutil.NoSuchProcess:
         | 
| 245 283 | 
             
                    return {"error": f"Process {pid} not found"}
         | 
| @@ -250,17 +288,17 @@ def get_process_info(pid: Optional[int] = None) -> Dict[str, Any]: | |
| 250 288 | 
             
            def monitor_process_resources(pid: int) -> Optional[Dict[str, Any]]:
         | 
| 251 289 | 
             
                """
         | 
| 252 290 | 
             
                Monitor resource usage of a process.
         | 
| 253 | 
            -
             | 
| 291 | 
            +
             | 
| 254 292 | 
             
                Args:
         | 
| 255 293 | 
             
                    pid: Process ID to monitor
         | 
| 256 | 
            -
             | 
| 294 | 
            +
             | 
| 257 295 | 
             
                Returns:
         | 
| 258 296 | 
             
                    Dictionary with resource information or None if process not found
         | 
| 259 297 | 
             
                """
         | 
| 260 298 | 
             
                try:
         | 
| 261 299 | 
             
                    process = psutil.Process(pid)
         | 
| 262 300 | 
             
                    memory_info = process.memory_info()
         | 
| 263 | 
            -
             | 
| 301 | 
            +
             | 
| 264 302 | 
             
                    return {
         | 
| 265 303 | 
             
                        "pid": pid,
         | 
| 266 304 | 
             
                        "cpu_percent": process.cpu_percent(),
         | 
| @@ -268,7 +306,7 @@ def monitor_process_resources(pid: int) -> Optional[Dict[str, Any]]: | |
| 268 306 | 
             
                        "memory_percent": process.memory_percent(),
         | 
| 269 307 | 
             
                        "num_threads": process.num_threads(),
         | 
| 270 308 | 
             
                        "status": process.status(),
         | 
| 271 | 
            -
                        "create_time": process.create_time()
         | 
| 309 | 
            +
                        "create_time": process.create_time(),
         | 
| 272 310 | 
             
                    }
         | 
| 273 311 | 
             
                except psutil.NoSuchProcess:
         | 
| 274 312 | 
             
                    return None
         | 
| @@ -279,27 +317,27 @@ def monitor_process_resources(pid: int) -> Optional[Dict[str, Any]]: | |
| 279 317 | 
             
            def cleanup_orphaned_processes(pattern: str, max_age_hours: float = 1.0) -> int:
         | 
| 280 318 | 
             
                """
         | 
| 281 319 | 
             
                Clean up orphaned processes matching a pattern.
         | 
| 282 | 
            -
             | 
| 320 | 
            +
             | 
| 283 321 | 
             
                Args:
         | 
| 284 322 | 
             
                    pattern: String pattern to match in process command line
         | 
| 285 323 | 
             
                    max_age_hours: Maximum age in hours before considering a process orphaned
         | 
| 286 | 
            -
             | 
| 324 | 
            +
             | 
| 287 325 | 
             
                Returns:
         | 
| 288 326 | 
             
                    Number of processes cleaned up
         | 
| 289 327 | 
             
                """
         | 
| 290 328 | 
             
                current_time = time.time()
         | 
| 291 329 | 
             
                cleanup_count = 0
         | 
| 292 | 
            -
             | 
| 293 | 
            -
                for process in psutil.process_iter([ | 
| 330 | 
            +
             | 
| 331 | 
            +
                for process in psutil.process_iter(["pid", "name", "cmdline", "create_time"]):
         | 
| 294 332 | 
             
                    try:
         | 
| 295 | 
            -
                        cmdline =  | 
| 333 | 
            +
                        cmdline = " ".join(process.info["cmdline"] or [])
         | 
| 296 334 | 
             
                        if pattern in cmdline:
         | 
| 297 335 | 
             
                            # Check if process is old enough to be considered orphaned
         | 
| 298 | 
            -
                            age_hours = (current_time - process.info[ | 
| 336 | 
            +
                            age_hours = (current_time - process.info["create_time"]) / 3600
         | 
| 299 337 | 
             
                            if age_hours > max_age_hours:
         | 
| 300 338 | 
             
                                process.terminate()
         | 
| 301 339 | 
             
                                cleanup_count += 1
         | 
| 302 340 | 
             
                    except (psutil.NoSuchProcess, psutil.AccessDenied):
         | 
| 303 341 | 
             
                        continue
         | 
| 304 | 
            -
             | 
| 342 | 
            +
             | 
| 305 343 | 
             
                return cleanup_count
         |