claude-mpm 4.1.26__py3-none-any.whl → 5.0.9__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +20 -5
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
- claude_mpm/agents/BASE_DOCUMENTATION.md +53 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/BASE_OPS.md +219 -0
- claude_mpm/agents/BASE_PM.md +432 -158
- claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
- claude_mpm/agents/BASE_QA.md +167 -0
- claude_mpm/agents/BASE_RESEARCH.md +53 -0
- claude_mpm/agents/OUTPUT_STYLE.md +254 -29
- claude_mpm/agents/PM_INSTRUCTIONS.md +969 -0
- claude_mpm/agents/PM_INSTRUCTIONS_TEACH.md +1322 -0
- claude_mpm/agents/WORKFLOW.md +355 -191
- claude_mpm/agents/__init__.py +6 -0
- claude_mpm/agents/agent_loader.py +41 -14
- claude_mpm/agents/agent_loader_integration.py +3 -2
- claude_mpm/agents/async_agent_loader.py +3 -3
- claude_mpm/agents/base_agent.json +6 -3
- claude_mpm/agents/base_agent_loader.py +21 -44
- claude_mpm/agents/frontmatter_validator.py +292 -252
- claude_mpm/agents/system_agent_config.py +3 -2
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/circuit-breakers.md +1005 -0
- claude_mpm/agents/templates/context-management-examples.md +544 -0
- claude_mpm/agents/templates/git-file-tracking.md +584 -0
- claude_mpm/agents/templates/pm-examples.md +474 -0
- claude_mpm/agents/templates/pm-red-flags.md +310 -0
- claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
- claude_mpm/agents/templates/research-gate-examples.md +669 -0
- claude_mpm/agents/templates/response-format.md +583 -0
- claude_mpm/agents/templates/structured-questions-examples.md +615 -0
- claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
- claude_mpm/agents/templates/ticketing-examples.md +277 -0
- claude_mpm/agents/templates/validation-templates.md +312 -0
- claude_mpm/cli/__init__.py +72 -376
- claude_mpm/cli/commands/__init__.py +4 -0
- claude_mpm/cli/commands/agent_manager.py +675 -20
- claude_mpm/cli/commands/agent_source.py +774 -0
- claude_mpm/cli/commands/agent_state_manager.py +344 -0
- claude_mpm/cli/commands/agents.py +1673 -178
- claude_mpm/cli/commands/agents_cleanup.py +210 -0
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_discover.py +338 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- claude_mpm/cli/commands/aggregate.py +11 -7
- claude_mpm/cli/commands/analyze.py +18 -13
- claude_mpm/cli/commands/analyze_code.py +8 -4
- claude_mpm/cli/commands/auto_configure.py +566 -0
- claude_mpm/cli/commands/cleanup.py +12 -12
- claude_mpm/cli/commands/config.py +54 -17
- claude_mpm/cli/commands/configure.py +1184 -1055
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +184 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/dashboard.py +50 -52
- claude_mpm/cli/commands/debug.py +19 -19
- claude_mpm/cli/commands/doctor.py +51 -7
- claude_mpm/cli/commands/hook_errors.py +277 -0
- claude_mpm/cli/commands/info.py +3 -4
- claude_mpm/cli/commands/local_deploy.py +534 -0
- claude_mpm/cli/commands/mcp.py +17 -10
- claude_mpm/cli/commands/mcp_command_router.py +11 -0
- claude_mpm/cli/commands/mcp_config.py +154 -0
- claude_mpm/cli/commands/mcp_external_commands.py +249 -0
- claude_mpm/cli/commands/mcp_install_commands.py +101 -32
- claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
- claude_mpm/cli/commands/mcp_setup_external.py +868 -0
- claude_mpm/cli/commands/memory.py +55 -21
- claude_mpm/cli/commands/monitor.py +160 -70
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +573 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +114 -4
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/run.py +252 -167
- claude_mpm/cli/commands/search.py +458 -0
- claude_mpm/cli/commands/skill_source.py +694 -0
- claude_mpm/cli/commands/skills.py +1225 -0
- claude_mpm/cli/commands/uninstall.py +176 -0
- claude_mpm/cli/commands/upgrade.py +152 -0
- claude_mpm/cli/commands/verify.py +119 -0
- claude_mpm/cli/executor.py +279 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +21 -0
- claude_mpm/cli/interactive/agent_wizard.py +1872 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parser.py +79 -2
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +161 -1
- claude_mpm/cli/parsers/agent_source_parser.py +171 -0
- claude_mpm/cli/parsers/agents_parser.py +369 -1
- claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
- claude_mpm/cli/parsers/base_parser.py +196 -3
- claude_mpm/cli/parsers/config_parser.py +96 -43
- claude_mpm/cli/parsers/configure_parser.py +11 -15
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mcp_parser.py +15 -0
- claude_mpm/cli/parsers/monitor_parser.py +12 -2
- claude_mpm/cli/parsers/mpm_init_parser.py +179 -9
- claude_mpm/cli/parsers/run_parser.py +5 -0
- claude_mpm/cli/parsers/search_parser.py +245 -0
- claude_mpm/cli/parsers/skill_source_parser.py +169 -0
- claude_mpm/cli/parsers/skills_parser.py +282 -0
- claude_mpm/cli/parsers/source_parser.py +138 -0
- claude_mpm/cli/shared/argument_patterns.py +20 -13
- claude_mpm/cli/shared/base_command.py +2 -2
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +994 -0
- claude_mpm/cli/startup_display.py +480 -0
- claude_mpm/cli/startup_logging.py +179 -13
- claude_mpm/cli/utils.py +54 -3
- claude_mpm/cli_module/commands.py +1 -1
- claude_mpm/commands/mpm-agents-auto-configure.md +278 -0
- claude_mpm/commands/mpm-agents-detect.md +177 -0
- claude_mpm/commands/mpm-agents-list.md +131 -0
- claude_mpm/commands/mpm-agents-recommend.md +223 -0
- claude_mpm/commands/mpm-config-view.md +150 -0
- claude_mpm/commands/mpm-doctor.md +9 -0
- claude_mpm/commands/mpm-help.md +297 -5
- claude_mpm/commands/mpm-init.md +401 -17
- claude_mpm/commands/mpm-monitor.md +418 -0
- claude_mpm/commands/mpm-postmortem.md +123 -0
- claude_mpm/commands/mpm-session-resume.md +381 -0
- claude_mpm/commands/mpm-status.md +79 -8
- claude_mpm/commands/mpm-ticket-organize.md +304 -0
- claude_mpm/commands/mpm-ticket-view.md +552 -0
- claude_mpm/commands/mpm-version.md +122 -0
- claude_mpm/commands/mpm.md +12 -0
- claude_mpm/config/agent_config.py +4 -4
- claude_mpm/config/agent_presets.py +488 -0
- claude_mpm/config/agent_sources.py +325 -0
- claude_mpm/config/experimental_features.py +7 -7
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/config/paths.py +3 -2
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/config/skill_sources.py +590 -0
- claude_mpm/config/socketio_config.py +3 -3
- claude_mpm/constants.py +28 -1
- claude_mpm/core/__init__.py +53 -17
- claude_mpm/core/agent_name_normalizer.py +3 -2
- claude_mpm/core/agent_registry.py +2 -2
- claude_mpm/core/agent_session_manager.py +10 -10
- claude_mpm/core/api_validator.py +330 -0
- claude_mpm/core/base_service.py +33 -23
- claude_mpm/core/cache.py +9 -9
- claude_mpm/core/claude_runner.py +24 -42
- claude_mpm/core/config.py +101 -8
- claude_mpm/core/config_aliases.py +7 -6
- claude_mpm/core/constants.py +66 -1
- claude_mpm/core/container.py +11 -5
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/error_handler.py +623 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/file_utils.py +764 -0
- claude_mpm/core/framework/__init__.py +25 -0
- claude_mpm/core/framework/formatters/__init__.py +11 -0
- claude_mpm/core/framework/formatters/capability_generator.py +367 -0
- claude_mpm/core/framework/formatters/content_formatter.py +288 -0
- claude_mpm/core/framework/formatters/context_generator.py +185 -0
- claude_mpm/core/framework/loaders/__init__.py +13 -0
- claude_mpm/core/framework/loaders/agent_loader.py +210 -0
- claude_mpm/core/framework/loaders/file_loader.py +176 -0
- claude_mpm/core/framework/loaders/instruction_loader.py +181 -0
- claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
- claude_mpm/core/framework/processors/__init__.py +11 -0
- claude_mpm/core/framework/processors/memory_processor.py +230 -0
- claude_mpm/core/framework/processors/metadata_processor.py +146 -0
- claude_mpm/core/framework/processors/template_processor.py +244 -0
- claude_mpm/core/framework_loader.py +321 -1631
- claude_mpm/core/hook_error_memory.py +381 -0
- claude_mpm/core/hook_manager.py +49 -8
- claude_mpm/core/injectable_service.py +11 -8
- claude_mpm/core/instruction_reinforcement_hook.py +4 -3
- claude_mpm/core/interactive_session.py +146 -18
- claude_mpm/core/interfaces.py +56 -1
- claude_mpm/core/lazy.py +3 -3
- claude_mpm/core/log_manager.py +92 -23
- claude_mpm/core/logger.py +22 -15
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/logging_utils.py +520 -0
- claude_mpm/core/oneshot_session.py +122 -15
- claude_mpm/core/optimized_agent_loader.py +9 -9
- claude_mpm/core/optimized_startup.py +1 -1
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/pm_hook_interceptor.py +18 -12
- claude_mpm/core/protocols/__init__.py +23 -0
- claude_mpm/core/protocols/runner_protocol.py +103 -0
- claude_mpm/core/protocols/session_protocol.py +131 -0
- claude_mpm/core/service_registry.py +7 -3
- claude_mpm/core/session_manager.py +14 -12
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/shared/singleton_manager.py +11 -4
- claude_mpm/core/socketio_pool.py +15 -15
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/tool_access_control.py +3 -2
- claude_mpm/core/types.py +4 -11
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/core/unified_agent_registry.py +115 -11
- claude_mpm/core/unified_config.py +6 -6
- claude_mpm/core/unified_paths.py +23 -20
- claude_mpm/dashboard/analysis_runner.py +4 -4
- claude_mpm/dashboard/api/simple_directory.py +261 -0
- claude_mpm/dashboard/static/css/activity.css +69 -69
- claude_mpm/dashboard/static/css/connection-status.css +10 -10
- claude_mpm/dashboard/static/css/dashboard.css +600 -18
- claude_mpm/dashboard/static/js/components/activity-tree.js +181 -195
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +105 -102
- claude_mpm/dashboard/static/js/components/agent-inference.js +34 -31
- claude_mpm/dashboard/static/js/components/build-tracker.js +67 -59
- claude_mpm/dashboard/static/js/components/code-simple.js +857 -0
- claude_mpm/dashboard/static/js/components/connection-debug.js +101 -101
- claude_mpm/dashboard/static/js/components/diff-viewer.js +891 -0
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +50 -13
- claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +443 -0
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +690 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +36 -16
- claude_mpm/dashboard/static/js/components/file-viewer.js +580 -0
- claude_mpm/dashboard/static/js/components/module-viewer.js +49 -23
- claude_mpm/dashboard/static/js/components/session-manager.js +19 -19
- claude_mpm/dashboard/static/js/components/socket-manager.js +5 -1
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +356 -41
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +520 -88
- claude_mpm/dashboard/static/js/components/working-directory.js +46 -11
- claude_mpm/dashboard/static/js/connection-manager.js +76 -76
- claude_mpm/dashboard/static/js/dashboard.js +309 -178
- claude_mpm/dashboard/static/js/extension-error-handler.js +22 -22
- claude_mpm/dashboard/static/js/socket-client.js +183 -139
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/socket.io.min.js +7 -0
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
- claude_mpm/dashboard/templates/code_simple.html +153 -0
- claude_mpm/dashboard/templates/index.html +125 -122
- claude_mpm/experimental/cli_enhancements.py +5 -7
- claude_mpm/generators/agent_profile_generator.py +5 -3
- claude_mpm/hooks/__init__.py +37 -1
- claude_mpm/hooks/base_hook.py +5 -4
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +24 -19
- claude_mpm/hooks/claude_hooks/hook_handler.py +29 -22
- claude_mpm/hooks/claude_hooks/installer.py +67 -22
- claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
- claude_mpm/hooks/claude_hooks/response_tracking.py +57 -17
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +62 -64
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +140 -76
- claude_mpm/hooks/claude_hooks/services/state_manager.py +11 -9
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
- claude_mpm/hooks/failure_learning/__init__.py +54 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
- claude_mpm/hooks/instruction_reinforcement.py +301 -0
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +386 -0
- claude_mpm/hooks/kuzu_response_hook.py +179 -0
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
- claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
- claude_mpm/hooks/tool_call_interceptor.py +8 -5
- claude_mpm/hooks/validation_hooks.py +3 -3
- claude_mpm/init.py +23 -4
- claude_mpm/models/agent_session.py +8 -6
- claude_mpm/models/git_repository.py +198 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/scripts/claude-hook-handler.sh +35 -9
- claude_mpm/scripts/launch_monitor.py +85 -0
- claude_mpm/scripts/mcp_server.py +3 -5
- claude_mpm/scripts/mpm_doctor.py +3 -2
- claude_mpm/scripts/socketio_daemon.py +159 -512
- claude_mpm/scripts/start_activity_logging.py +3 -1
- claude_mpm/services/__init__.py +144 -160
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/agent_builder.py +56 -18
- claude_mpm/services/agents/agent_preset_service.py +238 -0
- claude_mpm/services/agents/agent_selection_service.py +484 -0
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
- claude_mpm/services/agents/cache_git_manager.py +621 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_deployment.py +164 -17
- claude_mpm/services/agents/deployment/agent_discovery_service.py +191 -41
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_format_converter.py +56 -12
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -7
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +3 -3
- claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
- claude_mpm/services/agents/deployment/agent_record_service.py +4 -4
- claude_mpm/services/agents/deployment/agent_restore_handler.py +1 -4
- claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
- claude_mpm/services/agents/deployment/agent_template_builder.py +939 -50
- claude_mpm/services/agents/deployment/agent_validator.py +31 -7
- claude_mpm/services/agents/deployment/agent_version_manager.py +8 -5
- claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +101 -15
- claude_mpm/services/agents/deployment/async_agent_deployment.py +3 -2
- claude_mpm/services/agents/deployment/deployment_config_loader.py +131 -7
- claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
- claude_mpm/services/agents/deployment/deployment_wrapper.py +58 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -3
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +360 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +249 -53
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +8 -7
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +7 -5
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +10 -10
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +363 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +2 -2
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +168 -43
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/template_validator.py +64 -44
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/git_source_manager.py +629 -0
- claude_mpm/services/agents/loading/agent_profile_loader.py +10 -9
- claude_mpm/services/agents/loading/base_agent_manager.py +16 -6
- claude_mpm/services/agents/loading/framework_agent_loader.py +11 -14
- claude_mpm/services/agents/local_template_manager.py +784 -0
- claude_mpm/services/agents/management/agent_capabilities_generator.py +3 -2
- claude_mpm/services/agents/management/agent_management_service.py +5 -5
- claude_mpm/services/agents/memory/agent_memory_manager.py +32 -29
- claude_mpm/services/agents/memory/content_manager.py +17 -9
- claude_mpm/services/agents/memory/memory_categorization_service.py +4 -2
- claude_mpm/services/agents/memory/memory_file_service.py +32 -6
- claude_mpm/services/agents/memory/memory_format_service.py +6 -4
- claude_mpm/services/agents/memory/memory_limits_service.py +4 -2
- claude_mpm/services/agents/memory/template_generator.py +3 -3
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +615 -0
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +3 -3
- claude_mpm/services/agents/registry/modification_tracker.py +30 -19
- claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
- claude_mpm/services/agents/sources/__init__.py +13 -0
- claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +1087 -0
- claude_mpm/services/agents/startup_sync.py +239 -0
- claude_mpm/services/agents/toolchain_detector.py +474 -0
- claude_mpm/services/analysis/__init__.py +25 -0
- claude_mpm/services/analysis/postmortem_reporter.py +474 -0
- claude_mpm/services/analysis/postmortem_service.py +765 -0
- claude_mpm/services/async_session_logger.py +141 -98
- claude_mpm/services/claude_session_logger.py +82 -74
- claude_mpm/services/cli/agent_cleanup_service.py +5 -0
- claude_mpm/services/cli/agent_listing_service.py +5 -5
- claude_mpm/services/cli/agent_validation_service.py +3 -1
- claude_mpm/services/cli/memory_crud_service.py +12 -7
- claude_mpm/services/cli/memory_output_formatter.py +2 -2
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +104 -13
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/cli/startup_checker.py +13 -10
- claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
- claude_mpm/services/command_deployment_service.py +209 -13
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/base.py +31 -11
- claude_mpm/services/core/interfaces/__init__.py +88 -3
- claude_mpm/services/core/interfaces/agent.py +184 -0
- claude_mpm/services/core/interfaces/health.py +169 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/interfaces.py +56 -1
- claude_mpm/services/core/memory_manager.py +92 -47
- claude_mpm/services/core/models/__init__.py +70 -0
- claude_mpm/services/core/models/agent_config.py +384 -0
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +239 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/core/path_resolver.py +36 -14
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/__init__.py +8 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +30 -34
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
- claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +28 -27
- claude_mpm/services/diagnostics/checks/configuration_check.py +26 -25
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +165 -60
- claude_mpm/services/diagnostics/checks/instructions_check.py +21 -21
- claude_mpm/services/diagnostics/checks/mcp_check.py +57 -44
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +24 -24
- claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +14 -11
- claude_mpm/services/diagnostics/diagnostic_runner.py +31 -13
- claude_mpm/services/diagnostics/doctor_reporter.py +305 -47
- claude_mpm/services/diagnostics/models.py +37 -21
- claude_mpm/services/event_aggregator.py +5 -3
- claude_mpm/services/event_bus/direct_relay.py +11 -7
- claude_mpm/services/event_bus/event_bus.py +51 -9
- claude_mpm/services/event_bus/relay.py +33 -14
- claude_mpm/services/events/consumers/dead_letter.py +7 -5
- claude_mpm/services/events/consumers/logging.py +1 -2
- claude_mpm/services/events/core.py +5 -6
- claude_mpm/services/events/producers/hook.py +6 -6
- claude_mpm/services/events/producers/system.py +8 -8
- claude_mpm/services/exceptions.py +5 -5
- claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +5 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
- claude_mpm/services/git/__init__.py +21 -0
- claude_mpm/services/git/git_operations_service.py +494 -0
- claude_mpm/services/github/__init__.py +21 -0
- claude_mpm/services/github/github_cli_service.py +397 -0
- claude_mpm/services/hook_installer_service.py +506 -0
- claude_mpm/services/hook_service.py +5 -6
- claude_mpm/services/infrastructure/context_preservation.py +13 -11
- claude_mpm/services/infrastructure/daemon_manager.py +9 -9
- claude_mpm/services/infrastructure/logging.py +2 -2
- claude_mpm/services/infrastructure/monitoring/__init__.py +2 -6
- claude_mpm/services/infrastructure/monitoring/aggregator.py +13 -18
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +8 -7
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/instructions/__init__.py +9 -0
- claude_mpm/services/instructions/instruction_cache_service.py +374 -0
- claude_mpm/services/local_ops/__init__.py +155 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +427 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +1542 -0
- claude_mpm/services/mcp_gateway/__init__.py +97 -93
- claude_mpm/services/mcp_gateway/auto_configure.py +43 -38
- claude_mpm/services/mcp_gateway/config/config_loader.py +3 -3
- claude_mpm/services/mcp_gateway/config/configuration.py +24 -5
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +20 -33
- claude_mpm/services/mcp_gateway/core/process_pool.py +591 -31
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
- claude_mpm/services/mcp_gateway/core/startup_verification.py +3 -3
- claude_mpm/services/mcp_gateway/main.py +90 -15
- claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +12 -9
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +5 -10
- claude_mpm/services/mcp_gateway/server/stdio_server.py +9 -17
- claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +10 -9
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +654 -0
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +36 -34
- claude_mpm/services/mcp_gateway/tools/hello_world.py +8 -8
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +555 -0
- claude_mpm/services/mcp_gateway/utils/__init__.py +14 -0
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +160 -0
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +170 -0
- claude_mpm/services/mcp_service_verifier.py +732 -0
- claude_mpm/services/memory/builder.py +9 -8
- claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
- claude_mpm/services/memory/cache/simple_cache.py +2 -2
- claude_mpm/services/memory/failure_tracker.py +578 -0
- claude_mpm/services/memory/indexed_memory.py +8 -8
- claude_mpm/services/memory/optimizer.py +8 -9
- claude_mpm/services/memory/router.py +3 -3
- claude_mpm/services/memory_hook_service.py +165 -4
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +452 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +691 -0
- claude_mpm/services/monitor/daemon_manager.py +1040 -0
- claude_mpm/services/monitor/event_emitter.py +350 -0
- claude_mpm/services/monitor/handlers/__init__.py +21 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
- claude_mpm/services/monitor/handlers/dashboard.py +299 -0
- claude_mpm/services/monitor/handlers/file.py +264 -0
- claude_mpm/services/monitor/handlers/hooks.py +512 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +724 -0
- claude_mpm/services/monitor/server.py +817 -0
- claude_mpm/services/monitor_build_service.py +2 -2
- claude_mpm/services/native_agent_converter.py +356 -0
- claude_mpm/services/orphan_detection.py +786 -0
- claude_mpm/services/port_manager.py +3 -3
- claude_mpm/services/pr/__init__.py +14 -0
- claude_mpm/services/pr/pr_template_service.py +329 -0
- claude_mpm/services/project/__init__.py +23 -0
- claude_mpm/services/project/analyzer.py +3 -3
- claude_mpm/services/project/architecture_analyzer.py +5 -5
- claude_mpm/services/project/archive_manager.py +1045 -0
- claude_mpm/services/project/dependency_analyzer.py +4 -4
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/documentation_manager.py +554 -0
- claude_mpm/services/project/enhanced_analyzer.py +572 -0
- claude_mpm/services/project/metrics_collector.py +4 -4
- claude_mpm/services/project/project_organizer.py +1005 -0
- claude_mpm/services/project/registry.py +13 -7
- claude_mpm/services/project/toolchain_analyzer.py +583 -0
- claude_mpm/services/project_port_allocator.py +596 -0
- claude_mpm/services/response_tracker.py +21 -10
- claude_mpm/services/runner_configuration_service.py +17 -3
- claude_mpm/services/self_upgrade_service.py +500 -0
- claude_mpm/services/session_management_service.py +23 -9
- claude_mpm/services/session_manager.py +380 -0
- claude_mpm/services/shared/__init__.py +2 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/config_service_base.py +17 -14
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/shared/service_factory.py +8 -5
- claude_mpm/services/skills/__init__.py +18 -0
- claude_mpm/services/skills/git_skill_source_manager.py +1169 -0
- claude_mpm/services/skills/skill_discovery_service.py +568 -0
- claude_mpm/services/skills_config.py +547 -0
- claude_mpm/services/skills_deployer.py +955 -0
- claude_mpm/services/socketio/client_proxy.py +60 -5
- claude_mpm/services/socketio/dashboard_server.py +361 -0
- claude_mpm/services/socketio/event_normalizer.py +10 -6
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/base.py +2 -2
- claude_mpm/services/socketio/handlers/code_analysis.py +90 -27
- claude_mpm/services/socketio/handlers/connection.py +22 -41
- claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
- claude_mpm/services/socketio/handlers/file.py +46 -10
- claude_mpm/services/socketio/handlers/git.py +9 -9
- claude_mpm/services/socketio/handlers/hook.py +29 -17
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/monitor_client.py +364 -0
- claude_mpm/services/socketio/server/broadcaster.py +9 -7
- claude_mpm/services/socketio/server/connection_manager.py +2 -2
- claude_mpm/services/socketio/server/core.py +142 -8
- claude_mpm/services/socketio/server/eventbus_integration.py +20 -14
- claude_mpm/services/socketio/server/main.py +24 -24
- claude_mpm/services/socketio_client_manager.py +4 -4
- claude_mpm/services/subprocess_launcher_service.py +19 -15
- claude_mpm/services/system_instructions_service.py +3 -5
- claude_mpm/services/ticket_services/formatter_service.py +1 -1
- claude_mpm/services/ticket_services/validation_service.py +5 -5
- claude_mpm/services/unified/__init__.py +65 -0
- claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
- claude_mpm/services/unified/config_strategies/__init__.py +175 -0
- claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
- claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
- claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
- claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
- claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
- claude_mpm/services/unified/deployment_strategies/base.py +553 -0
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
- claude_mpm/services/unified/deployment_strategies/local.py +607 -0
- claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
- claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
- claude_mpm/services/unified/interfaces.py +475 -0
- claude_mpm/services/unified/migration.py +509 -0
- claude_mpm/services/unified/strategies.py +534 -0
- claude_mpm/services/unified/unified_analyzer.py +542 -0
- claude_mpm/services/unified/unified_config.py +691 -0
- claude_mpm/services/unified/unified_deployment.py +466 -0
- claude_mpm/services/utility_service.py +6 -3
- claude_mpm/services/version_control/branch_strategy.py +2 -2
- claude_mpm/services/version_control/conflict_resolution.py +14 -8
- claude_mpm/services/version_control/git_operations.py +26 -24
- claude_mpm/services/version_control/semantic_versioning.py +14 -14
- claude_mpm/services/version_control/version_parser.py +14 -11
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/services/visualization/__init__.py +1 -5
- claude_mpm/services/visualization/mermaid_generator.py +2 -3
- claude_mpm/skills/__init__.py +42 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +573 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/skills/skills_registry.py +347 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/storage/state_storage.py +31 -31
- claude_mpm/templates/questions/__init__.py +38 -0
- claude_mpm/templates/questions/base.py +193 -0
- claude_mpm/templates/questions/pr_strategy.py +311 -0
- claude_mpm/templates/questions/project_init.py +385 -0
- claude_mpm/templates/questions/ticket_mgmt.py +394 -0
- claude_mpm/tools/__main__.py +9 -9
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/tools/code_tree_builder.py +6 -6
- claude_mpm/tools/code_tree_events.py +14 -10
- claude_mpm/tools/socketio_debug.py +11 -11
- claude_mpm/utils/agent_dependency_loader.py +184 -36
- claude_mpm/utils/agent_filters.py +288 -0
- claude_mpm/utils/common.py +544 -0
- claude_mpm/utils/config_manager.py +12 -6
- claude_mpm/utils/database_connector.py +298 -0
- claude_mpm/utils/dependency_cache.py +5 -3
- claude_mpm/utils/dependency_strategies.py +15 -10
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/environment_context.py +4 -3
- claude_mpm/utils/error_handler.py +5 -3
- claude_mpm/utils/file_utils.py +13 -14
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/gitignore.py +241 -0
- claude_mpm/utils/log_cleanup.py +627 -0
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/path_operations.py +7 -4
- claude_mpm/utils/progress.py +387 -0
- claude_mpm/utils/robust_installer.py +131 -24
- claude_mpm/utils/session_logging.py +2 -2
- claude_mpm/utils/structured_questions.py +619 -0
- claude_mpm/utils/subprocess_utils.py +9 -8
- claude_mpm/validation/agent_validator.py +6 -6
- claude_mpm/validation/frontmatter_validator.py +6 -6
- claude_mpm-5.0.9.dist-info/METADATA +1028 -0
- claude_mpm-5.0.9.dist-info/RECORD +864 -0
- {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/entry_points.txt +1 -0
- claude_mpm/agents/INSTRUCTIONS.md +0 -261
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -17
- claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +0 -3
- claude_mpm/agents/templates/agent-manager.json +0 -270
- claude_mpm/agents/templates/agent-manager.md +0 -619
- claude_mpm/agents/templates/agentic_coder_optimizer.json +0 -222
- claude_mpm/agents/templates/api_qa.json +0 -171
- claude_mpm/agents/templates/code_analyzer.json +0 -95
- claude_mpm/agents/templates/data_engineer.json +0 -152
- claude_mpm/agents/templates/documentation.json +0 -175
- claude_mpm/agents/templates/engineer.json +0 -176
- claude_mpm/agents/templates/imagemagick.json +0 -261
- claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +0 -39
- claude_mpm/agents/templates/memory_manager.json +0 -155
- claude_mpm/agents/templates/ops.json +0 -175
- claude_mpm/agents/templates/project_organizer.json +0 -130
- claude_mpm/agents/templates/qa.json +0 -223
- claude_mpm/agents/templates/refactoring_engineer.json +0 -266
- claude_mpm/agents/templates/research.json +0 -163
- claude_mpm/agents/templates/security.json +0 -153
- claude_mpm/agents/templates/ticketing.json +0 -169
- claude_mpm/agents/templates/vercel_ops_agent.json +0 -281
- claude_mpm/agents/templates/version_control.json +0 -147
- claude_mpm/agents/templates/web_qa.json +0 -254
- claude_mpm/agents/templates/web_ui.json +0 -176
- claude_mpm/cli/commands/configure_tui.py +0 -1927
- claude_mpm/cli/commands/mpm_init.py +0 -594
- claude_mpm/cli/commands/socketio_monitor.py +0 -233
- claude_mpm/commands/mpm-agents.md +0 -12
- claude_mpm/commands/mpm-config.md +0 -18
- claude_mpm/commands/mpm-tickets.md +0 -102
- claude_mpm/dashboard/.claude-mpm/socketio-instances.json +0 -1
- claude_mpm/dashboard/static/built/components/activity-tree.js +0 -2
- claude_mpm/dashboard/static/built/components/agent-inference.js +0 -2
- claude_mpm/dashboard/static/built/components/code-tree.js +0 -2
- claude_mpm/dashboard/static/built/components/code-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/event-processor.js +0 -2
- claude_mpm/dashboard/static/built/components/event-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/export-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +0 -2
- claude_mpm/dashboard/static/built/components/hud-library-loader.js +0 -2
- claude_mpm/dashboard/static/built/components/hud-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/hud-visualizer.js +0 -2
- claude_mpm/dashboard/static/built/components/module-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/session-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/socket-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/unified-data-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/working-directory.js +0 -2
- claude_mpm/dashboard/static/built/dashboard.js +0 -2
- claude_mpm/dashboard/static/built/socket-client.js +0 -2
- claude_mpm/dashboard/static/css/code-tree.css +0 -1408
- claude_mpm/dashboard/static/dist/components/activity-tree.js +0 -2
- claude_mpm/dashboard/static/dist/components/agent-inference.js +0 -2
- claude_mpm/dashboard/static/dist/components/code-tree.js +0 -2
- claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/event-processor.js +0 -2
- claude_mpm/dashboard/static/dist/components/event-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/export-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +0 -2
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +0 -2
- claude_mpm/dashboard/static/dist/components/hud-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +0 -2
- claude_mpm/dashboard/static/dist/components/module-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/session-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/socket-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/working-directory.js +0 -2
- claude_mpm/dashboard/static/dist/dashboard.js +0 -2
- claude_mpm/dashboard/static/dist/socket-client.js +0 -2
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -3220
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -480
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1040
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
- claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
- claude_mpm/scripts/socketio_server_manager.py +0 -349
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/cli/dashboard_launcher.py +0 -423
- claude_mpm/services/cli/socketio_manager.py +0 -537
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +0 -286
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- claude_mpm/tools/code_tree_analyzer.py +0 -1693
- claude_mpm-4.1.26.dist-info/METADATA +0 -332
- claude_mpm-4.1.26.dist-info/RECORD +0 -606
- {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.26.dist-info → claude_mpm-5.0.9.dist-info}/top_level.txt +0 -0
|
@@ -12,153 +12,53 @@ DESIGN DECISIONS:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import json
|
|
15
|
-
import os
|
|
16
|
-
import sys
|
|
17
15
|
from pathlib import Path
|
|
18
16
|
from typing import Dict, List, Optional
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
from
|
|
18
|
+
import questionary
|
|
19
|
+
from questionary import Style
|
|
22
20
|
from rich.console import Console
|
|
23
|
-
from rich.panel import Panel
|
|
24
21
|
from rich.prompt import Confirm, Prompt
|
|
25
|
-
from rich.syntax import Syntax
|
|
26
|
-
from rich.table import Table
|
|
27
22
|
from rich.text import Text
|
|
28
23
|
|
|
24
|
+
from ...core.config import Config
|
|
29
25
|
from ...services.version_service import VersionService
|
|
26
|
+
from ...utils.agent_filters import apply_all_filters, get_deployed_agent_ids
|
|
30
27
|
from ...utils.console import console as default_console
|
|
31
28
|
from ..shared import BaseCommand, CommandResult
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class SimpleAgentManager:
|
|
46
|
-
"""Simple agent state management that discovers real agents from templates."""
|
|
47
|
-
|
|
48
|
-
def __init__(self, config_dir: Path):
|
|
49
|
-
self.config_dir = config_dir
|
|
50
|
-
self.config_file = config_dir / "agent_states.json"
|
|
51
|
-
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
52
|
-
self._load_states()
|
|
53
|
-
# Path to agent templates directory
|
|
54
|
-
self.templates_dir = (
|
|
55
|
-
Path(__file__).parent.parent.parent / "agents" / "templates"
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
def _load_states(self):
|
|
59
|
-
"""Load agent states from file."""
|
|
60
|
-
if self.config_file.exists():
|
|
61
|
-
with open(self.config_file) as f:
|
|
62
|
-
self.states = json.load(f)
|
|
63
|
-
else:
|
|
64
|
-
self.states = {}
|
|
65
|
-
|
|
66
|
-
def _save_states(self):
|
|
67
|
-
"""Save agent states to file."""
|
|
68
|
-
with open(self.config_file, "w") as f:
|
|
69
|
-
json.dump(self.states, f, indent=2)
|
|
70
|
-
|
|
71
|
-
def is_agent_enabled(self, agent_name: str) -> bool:
|
|
72
|
-
"""Check if an agent is enabled."""
|
|
73
|
-
return self.states.get(agent_name, {}).get("enabled", True)
|
|
74
|
-
|
|
75
|
-
def set_agent_enabled(self, agent_name: str, enabled: bool):
|
|
76
|
-
"""Set agent enabled state."""
|
|
77
|
-
if agent_name not in self.states:
|
|
78
|
-
self.states[agent_name] = {}
|
|
79
|
-
self.states[agent_name]["enabled"] = enabled
|
|
80
|
-
self._save_states()
|
|
81
|
-
|
|
82
|
-
def discover_agents(self) -> List[AgentConfig]:
|
|
83
|
-
"""Discover available agents from template JSON files."""
|
|
84
|
-
agents = []
|
|
85
|
-
|
|
86
|
-
# Scan templates directory for JSON files
|
|
87
|
-
if not self.templates_dir.exists():
|
|
88
|
-
# Fallback to a minimal set if templates dir doesn't exist
|
|
89
|
-
return [
|
|
90
|
-
AgentConfig("engineer", "Engineering agent (templates not found)", []),
|
|
91
|
-
AgentConfig("research", "Research agent (templates not found)", []),
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
try:
|
|
95
|
-
# Read all JSON template files
|
|
96
|
-
for template_file in sorted(self.templates_dir.glob("*.json")):
|
|
97
|
-
# Skip backup files
|
|
98
|
-
if "backup" in template_file.name.lower():
|
|
99
|
-
continue
|
|
100
|
-
|
|
101
|
-
try:
|
|
102
|
-
with open(template_file) as f:
|
|
103
|
-
template_data = json.load(f)
|
|
104
|
-
|
|
105
|
-
# Extract agent information from template
|
|
106
|
-
agent_id = template_data.get("agent_id", template_file.stem)
|
|
107
|
-
|
|
108
|
-
# Get metadata for display info
|
|
109
|
-
metadata = template_data.get("metadata", {})
|
|
110
|
-
metadata.get("name", agent_id)
|
|
111
|
-
description = metadata.get(
|
|
112
|
-
"description", "No description available"
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
# Extract capabilities/tools as dependencies for display
|
|
116
|
-
capabilities = template_data.get("capabilities", {})
|
|
117
|
-
tools = capabilities.get("tools", [])
|
|
118
|
-
# Show first few tools as "dependencies" for UI purposes
|
|
119
|
-
display_tools = tools[:3] if len(tools) > 3 else tools
|
|
120
|
-
|
|
121
|
-
# Normalize agent ID (remove -agent suffix if present, replace underscores)
|
|
122
|
-
normalized_id = agent_id.replace("-agent", "").replace("_", "-")
|
|
123
|
-
|
|
124
|
-
agents.append(
|
|
125
|
-
AgentConfig(
|
|
126
|
-
name=normalized_id,
|
|
127
|
-
description=(
|
|
128
|
-
description[:80] + "..."
|
|
129
|
-
if len(description) > 80
|
|
130
|
-
else description
|
|
131
|
-
),
|
|
132
|
-
dependencies=display_tools,
|
|
133
|
-
)
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
except (json.JSONDecodeError, KeyError):
|
|
137
|
-
# Skip malformed templates
|
|
138
|
-
continue
|
|
139
|
-
|
|
140
|
-
except Exception as e:
|
|
141
|
-
# If there's an error reading templates, return a minimal set
|
|
142
|
-
return [
|
|
143
|
-
AgentConfig("engineer", f"Error loading templates: {e!s}", []),
|
|
144
|
-
AgentConfig("research", "Research agent", []),
|
|
145
|
-
]
|
|
146
|
-
|
|
147
|
-
# Sort agents by name for consistent display
|
|
148
|
-
agents.sort(key=lambda a: a.name)
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
agents
|
|
152
|
-
if agents
|
|
153
|
-
else [
|
|
154
|
-
AgentConfig("engineer", "No agents found in templates", []),
|
|
155
|
-
]
|
|
156
|
-
)
|
|
29
|
+
from .agent_state_manager import SimpleAgentManager
|
|
30
|
+
from .configure_agent_display import AgentDisplay
|
|
31
|
+
from .configure_behavior_manager import BehaviorManager
|
|
32
|
+
from .configure_hook_manager import HookManager
|
|
33
|
+
from .configure_models import AgentConfig
|
|
34
|
+
from .configure_navigation import ConfigNavigation
|
|
35
|
+
from .configure_persistence import ConfigPersistence
|
|
36
|
+
from .configure_startup_manager import StartupManager
|
|
37
|
+
from .configure_template_editor import TemplateEditor
|
|
38
|
+
from .configure_validators import (
|
|
39
|
+
parse_id_selection,
|
|
40
|
+
validate_args as validate_configure_args,
|
|
41
|
+
)
|
|
157
42
|
|
|
158
43
|
|
|
159
44
|
class ConfigureCommand(BaseCommand):
|
|
160
45
|
"""Interactive configuration management command."""
|
|
161
46
|
|
|
47
|
+
# Questionary style optimized for dark terminals (WCAG AAA compliant)
|
|
48
|
+
QUESTIONARY_STYLE = Style(
|
|
49
|
+
[
|
|
50
|
+
("selected", "fg:#e0e0e0 bold"), # Light gray - excellent readability
|
|
51
|
+
("pointer", "fg:#ffd700 bold"), # Gold/yellow - highly visible pointer
|
|
52
|
+
("highlighted", "fg:#e0e0e0"), # Light gray - clear hover state
|
|
53
|
+
("question", "fg:#e0e0e0 bold"), # Light gray bold - prominent questions
|
|
54
|
+
("checkbox", "fg:#00ff00"), # Green - for checked boxes
|
|
55
|
+
(
|
|
56
|
+
"checkbox-selected",
|
|
57
|
+
"fg:#00ff00 bold",
|
|
58
|
+
), # Green bold - for checked selected boxes
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
|
|
162
62
|
def __init__(self):
|
|
163
63
|
super().__init__("configure")
|
|
164
64
|
self.console = default_console
|
|
@@ -166,24 +66,88 @@ class ConfigureCommand(BaseCommand):
|
|
|
166
66
|
self.current_scope = "project"
|
|
167
67
|
self.project_dir = Path.cwd()
|
|
168
68
|
self.agent_manager = None
|
|
69
|
+
self.hook_manager = HookManager(self.console)
|
|
70
|
+
self.behavior_manager = None # Initialized when scope is set
|
|
71
|
+
self._agent_display = None # Lazy-initialized
|
|
72
|
+
self._persistence = None # Lazy-initialized
|
|
73
|
+
self._navigation = None # Lazy-initialized
|
|
74
|
+
self._template_editor = None # Lazy-initialized
|
|
75
|
+
self._startup_manager = None # Lazy-initialized
|
|
169
76
|
|
|
170
77
|
def validate_args(self, args) -> Optional[str]:
|
|
171
78
|
"""Validate command arguments."""
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
79
|
+
return validate_configure_args(args)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def agent_display(self) -> AgentDisplay:
|
|
83
|
+
"""Lazy-initialize agent display handler."""
|
|
84
|
+
if self._agent_display is None:
|
|
85
|
+
if self.agent_manager is None:
|
|
86
|
+
raise RuntimeError(
|
|
87
|
+
"agent_manager must be initialized before agent_display"
|
|
88
|
+
)
|
|
89
|
+
self._agent_display = AgentDisplay(
|
|
90
|
+
self.console,
|
|
91
|
+
self.agent_manager,
|
|
92
|
+
self._get_agent_template_path,
|
|
93
|
+
self._display_header,
|
|
94
|
+
)
|
|
95
|
+
return self._agent_display
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def persistence(self) -> ConfigPersistence:
|
|
99
|
+
"""Lazy-initialize persistence handler."""
|
|
100
|
+
if self._persistence is None:
|
|
101
|
+
# Note: agent_manager might be None for version_info calls
|
|
102
|
+
self._persistence = ConfigPersistence(
|
|
103
|
+
self.console,
|
|
104
|
+
self.version_service,
|
|
105
|
+
self.agent_manager, # Can be None for version operations
|
|
106
|
+
self._get_agent_template_path,
|
|
107
|
+
self._display_header,
|
|
108
|
+
self.current_scope,
|
|
109
|
+
self.project_dir,
|
|
110
|
+
)
|
|
111
|
+
return self._persistence
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def navigation(self) -> ConfigNavigation:
|
|
115
|
+
"""Lazy-initialize navigation handler."""
|
|
116
|
+
if self._navigation is None:
|
|
117
|
+
self._navigation = ConfigNavigation(self.console, self.project_dir)
|
|
118
|
+
# Sync scope from main command
|
|
119
|
+
self._navigation.current_scope = self.current_scope
|
|
120
|
+
return self._navigation
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def template_editor(self) -> TemplateEditor:
|
|
124
|
+
"""Lazy-initialize template editor."""
|
|
125
|
+
if self._template_editor is None:
|
|
126
|
+
if self.agent_manager is None:
|
|
127
|
+
raise RuntimeError(
|
|
128
|
+
"agent_manager must be initialized before template_editor"
|
|
129
|
+
)
|
|
130
|
+
self._template_editor = TemplateEditor(
|
|
131
|
+
self.console, self.agent_manager, self.current_scope, self.project_dir
|
|
132
|
+
)
|
|
133
|
+
return self._template_editor
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def startup_manager(self) -> StartupManager:
|
|
137
|
+
"""Lazy-initialize startup manager."""
|
|
138
|
+
if self._startup_manager is None:
|
|
139
|
+
if self.agent_manager is None:
|
|
140
|
+
raise RuntimeError(
|
|
141
|
+
"agent_manager must be initialized before startup_manager"
|
|
142
|
+
)
|
|
143
|
+
self._startup_manager = StartupManager(
|
|
144
|
+
self.agent_manager,
|
|
145
|
+
self.console,
|
|
146
|
+
self.current_scope,
|
|
147
|
+
self.project_dir,
|
|
148
|
+
self._display_header,
|
|
149
|
+
)
|
|
150
|
+
return self._startup_manager
|
|
187
151
|
|
|
188
152
|
def run(self, args) -> CommandResult:
|
|
189
153
|
"""Execute the configure command."""
|
|
@@ -192,12 +156,15 @@ class ConfigureCommand(BaseCommand):
|
|
|
192
156
|
if getattr(args, "project_dir", None):
|
|
193
157
|
self.project_dir = Path(args.project_dir)
|
|
194
158
|
|
|
195
|
-
# Initialize agent manager with appropriate config directory
|
|
159
|
+
# Initialize agent manager and behavior manager with appropriate config directory
|
|
196
160
|
if self.current_scope == "project":
|
|
197
161
|
config_dir = self.project_dir / ".claude-mpm"
|
|
198
162
|
else:
|
|
199
163
|
config_dir = Path.home() / ".claude-mpm"
|
|
200
164
|
self.agent_manager = SimpleAgentManager(config_dir)
|
|
165
|
+
self.behavior_manager = BehaviorManager(
|
|
166
|
+
config_dir, self.current_scope, self.console
|
|
167
|
+
)
|
|
201
168
|
|
|
202
169
|
# Disable colors if requested
|
|
203
170
|
if getattr(args, "no_colors", False):
|
|
@@ -242,39 +209,15 @@ class ConfigureCommand(BaseCommand):
|
|
|
242
209
|
if getattr(args, "behaviors", False):
|
|
243
210
|
return self._run_behavior_management()
|
|
244
211
|
|
|
212
|
+
if getattr(args, "startup", False):
|
|
213
|
+
return self._run_startup_configuration()
|
|
214
|
+
|
|
245
215
|
# Launch interactive TUI
|
|
246
216
|
return self._run_interactive_tui(args)
|
|
247
217
|
|
|
248
218
|
def _run_interactive_tui(self, args) -> CommandResult:
|
|
249
|
-
"""Run the main interactive
|
|
250
|
-
#
|
|
251
|
-
use_textual = getattr(args, "use_textual", True)
|
|
252
|
-
force_rich = getattr(args, "force_rich", False)
|
|
253
|
-
|
|
254
|
-
if use_textual and not force_rich:
|
|
255
|
-
try:
|
|
256
|
-
# Try to import and use Textual TUI
|
|
257
|
-
from .configure_tui import can_use_tui, launch_tui
|
|
258
|
-
|
|
259
|
-
if can_use_tui():
|
|
260
|
-
self.console.print(
|
|
261
|
-
"[cyan]Launching full-screen configuration interface...[/cyan]"
|
|
262
|
-
)
|
|
263
|
-
return launch_tui(self.current_scope, self.project_dir)
|
|
264
|
-
# Fall back to Rich TUI if terminal doesn't support full-screen
|
|
265
|
-
self.console.print(
|
|
266
|
-
"[yellow]Terminal doesn't support full-screen mode. Using menu interface.[/yellow]"
|
|
267
|
-
)
|
|
268
|
-
except ImportError:
|
|
269
|
-
# Textual not available, fall back to Rich
|
|
270
|
-
self.console.print(
|
|
271
|
-
"[yellow]Textual not installed. Using menu interface.[/yellow]"
|
|
272
|
-
)
|
|
273
|
-
self.console.print(
|
|
274
|
-
"[dim]Install textual for a better experience: pip install textual[/dim]"
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
# Original Rich-based TUI
|
|
219
|
+
"""Run the main interactive menu interface."""
|
|
220
|
+
# Rich-based menu interface
|
|
278
221
|
try:
|
|
279
222
|
self.console.clear()
|
|
280
223
|
|
|
@@ -286,13 +229,49 @@ class ConfigureCommand(BaseCommand):
|
|
|
286
229
|
if choice == "1":
|
|
287
230
|
self._manage_agents()
|
|
288
231
|
elif choice == "2":
|
|
289
|
-
self.
|
|
232
|
+
self._manage_skills()
|
|
290
233
|
elif choice == "3":
|
|
291
|
-
self.
|
|
234
|
+
self._edit_templates()
|
|
292
235
|
elif choice == "4":
|
|
293
|
-
self.
|
|
236
|
+
self._manage_behaviors()
|
|
294
237
|
elif choice == "5":
|
|
238
|
+
# If user saves and wants to proceed to startup, exit the configurator
|
|
239
|
+
if self._manage_startup_configuration():
|
|
240
|
+
self.console.print(
|
|
241
|
+
"\n[green]Configuration saved. Exiting configurator...[/green]"
|
|
242
|
+
)
|
|
243
|
+
break
|
|
244
|
+
elif choice == "6":
|
|
245
|
+
self._switch_scope()
|
|
246
|
+
elif choice == "7":
|
|
295
247
|
self._show_version_info_interactive()
|
|
248
|
+
elif choice == "l":
|
|
249
|
+
# Check for pending agent changes
|
|
250
|
+
if self.agent_manager and self.agent_manager.has_pending_changes():
|
|
251
|
+
should_save = Confirm.ask(
|
|
252
|
+
"[yellow]You have unsaved agent changes. Save them before launching?[/yellow]",
|
|
253
|
+
default=True,
|
|
254
|
+
)
|
|
255
|
+
if should_save:
|
|
256
|
+
self.agent_manager.commit_deferred_changes()
|
|
257
|
+
self.console.print("[green]✓ Agent changes saved[/green]")
|
|
258
|
+
else:
|
|
259
|
+
self.agent_manager.discard_deferred_changes()
|
|
260
|
+
self.console.print(
|
|
261
|
+
"[yellow]⚠ Agent changes discarded[/yellow]"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Save all configuration
|
|
265
|
+
self.console.print("\n[cyan]Saving configuration...[/cyan]")
|
|
266
|
+
if self._save_all_configuration():
|
|
267
|
+
# Launch Claude MPM (this will replace the process if successful)
|
|
268
|
+
self._launch_claude_mpm()
|
|
269
|
+
# If execvp fails, we'll return here and break
|
|
270
|
+
break
|
|
271
|
+
self.console.print(
|
|
272
|
+
"[red]✗ Failed to save configuration. Not launching.[/red]"
|
|
273
|
+
)
|
|
274
|
+
Prompt.ask("\nPress Enter to continue")
|
|
296
275
|
elif choice == "q":
|
|
297
276
|
self.console.print(
|
|
298
277
|
"\n[green]Configuration complete. Goodbye![/green]"
|
|
@@ -312,777 +291,504 @@ class ConfigureCommand(BaseCommand):
|
|
|
312
291
|
|
|
313
292
|
def _display_header(self) -> None:
|
|
314
293
|
"""Display the TUI header."""
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
header_text = Text()
|
|
319
|
-
header_text.append("Claude MPM ", style="bold cyan")
|
|
320
|
-
header_text.append("Configuration Interface", style="bold white")
|
|
321
|
-
|
|
322
|
-
scope_text = Text(f"Scope: {self.current_scope.upper()}", style="yellow")
|
|
323
|
-
dir_text = Text(f"Directory: {self.project_dir}", style="dim")
|
|
324
|
-
|
|
325
|
-
header_content = Columns([header_text], align="center")
|
|
326
|
-
subtitle_content = f"{scope_text} | {dir_text}"
|
|
327
|
-
|
|
328
|
-
header_panel = Panel(
|
|
329
|
-
header_content,
|
|
330
|
-
subtitle=subtitle_content,
|
|
331
|
-
box=ROUNDED,
|
|
332
|
-
style="blue",
|
|
333
|
-
padding=(1, 2),
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
self.console.print(header_panel)
|
|
337
|
-
self.console.print()
|
|
294
|
+
# Sync scope to navigation before display
|
|
295
|
+
self.navigation.current_scope = self.current_scope
|
|
296
|
+
self.navigation.display_header()
|
|
338
297
|
|
|
339
298
|
def _show_main_menu(self) -> str:
|
|
340
299
|
"""Show the main menu and get user choice."""
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
("3", "Behavior Files", "Manage identity and workflow configurations"),
|
|
345
|
-
("4", "Switch Scope", f"Current: {self.current_scope}"),
|
|
346
|
-
("5", "Version Info", "Display MPM and Claude versions"),
|
|
347
|
-
("q", "Quit", "Exit configuration interface"),
|
|
348
|
-
]
|
|
349
|
-
|
|
350
|
-
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
351
|
-
table.add_column("Key", style="cyan", width=3)
|
|
352
|
-
table.add_column("Option", style="bold white", width=20)
|
|
353
|
-
table.add_column("Description", style="dim")
|
|
354
|
-
|
|
355
|
-
for key, option, desc in menu_items:
|
|
356
|
-
table.add_row(f"[{key}]", option, desc)
|
|
357
|
-
|
|
358
|
-
menu_panel = Panel(
|
|
359
|
-
table, title="[bold]Main Menu[/bold]", box=ROUNDED, style="green"
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
self.console.print(menu_panel)
|
|
363
|
-
self.console.print()
|
|
364
|
-
|
|
365
|
-
return Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="q")
|
|
300
|
+
# Sync scope to navigation before display
|
|
301
|
+
self.navigation.current_scope = self.current_scope
|
|
302
|
+
return self.navigation.show_main_menu()
|
|
366
303
|
|
|
367
304
|
def _manage_agents(self) -> None:
|
|
368
|
-
"""
|
|
305
|
+
"""Enhanced agent management with remote agent discovery and installation."""
|
|
369
306
|
while True:
|
|
370
307
|
self.console.clear()
|
|
371
|
-
self.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
self.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
308
|
+
self.navigation.display_header()
|
|
309
|
+
self.console.print("\n[bold blue]═══ Agent Management ═══[/bold blue]\n")
|
|
310
|
+
|
|
311
|
+
# Step 1: Show configured sources
|
|
312
|
+
self.console.print("[bold white]═══ Agent Sources ═══[/bold white]\n")
|
|
313
|
+
|
|
314
|
+
sources = self._get_configured_sources()
|
|
315
|
+
if sources:
|
|
316
|
+
from rich.table import Table
|
|
317
|
+
|
|
318
|
+
sources_table = Table(show_header=True, header_style="bold white")
|
|
319
|
+
sources_table.add_column(
|
|
320
|
+
"Source",
|
|
321
|
+
style="bright_yellow",
|
|
322
|
+
width=40,
|
|
323
|
+
no_wrap=True,
|
|
324
|
+
overflow="ellipsis",
|
|
325
|
+
)
|
|
326
|
+
sources_table.add_column(
|
|
327
|
+
"Status", style="green", width=15, no_wrap=True
|
|
328
|
+
)
|
|
329
|
+
sources_table.add_column(
|
|
330
|
+
"Agents", style="yellow", width=10, no_wrap=True
|
|
331
|
+
)
|
|
386
332
|
|
|
387
|
-
|
|
333
|
+
for source in sources:
|
|
334
|
+
status = "✓ Active" if source.get("enabled", True) else "Disabled"
|
|
335
|
+
agent_count = source.get("agent_count", "?")
|
|
336
|
+
sources_table.add_row(
|
|
337
|
+
source["identifier"], status, str(agent_count)
|
|
338
|
+
)
|
|
388
339
|
|
|
389
|
-
|
|
390
|
-
break
|
|
391
|
-
if choice == "e":
|
|
392
|
-
self._enable_agent_interactive(agents)
|
|
393
|
-
elif choice == "d":
|
|
394
|
-
self._disable_agent_interactive(agents)
|
|
395
|
-
elif choice == "c":
|
|
396
|
-
self._customize_agent_template(agents)
|
|
397
|
-
elif choice == "v":
|
|
398
|
-
self._view_agent_details(agents)
|
|
399
|
-
elif choice == "r":
|
|
400
|
-
self._reset_agent_defaults(agents)
|
|
340
|
+
self.console.print(sources_table)
|
|
401
341
|
else:
|
|
402
|
-
self.console.print("[
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
"""Display a table of available agents."""
|
|
407
|
-
table = Table(
|
|
408
|
-
title=f"Available Agents ({len(agents)} total)",
|
|
409
|
-
box=ROUNDED,
|
|
410
|
-
show_lines=True,
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
table.add_column("ID", style="dim", width=3)
|
|
414
|
-
table.add_column("Name", style="cyan", width=22)
|
|
415
|
-
table.add_column("Status", width=12)
|
|
416
|
-
table.add_column("Description", style="white", width=45)
|
|
417
|
-
table.add_column("Model/Tools", style="dim", width=20)
|
|
418
|
-
|
|
419
|
-
for idx, agent in enumerate(agents, 1):
|
|
420
|
-
# Check if agent is enabled
|
|
421
|
-
is_enabled = self.agent_manager.is_agent_enabled(agent.name)
|
|
422
|
-
status = (
|
|
423
|
-
"[green]✓ Enabled[/green]" if is_enabled else "[red]✗ Disabled[/red]"
|
|
424
|
-
)
|
|
342
|
+
self.console.print("[yellow]No agent sources configured[/yellow]")
|
|
343
|
+
self.console.print(
|
|
344
|
+
"[dim]Default source 'bobmatnyc/claude-mpm-agents' will be used[/dim]\n"
|
|
345
|
+
)
|
|
425
346
|
|
|
426
|
-
#
|
|
427
|
-
|
|
428
|
-
if agent.dependencies:
|
|
429
|
-
if len(agent.dependencies) > 2:
|
|
430
|
-
tools_display = f"{', '.join(agent.dependencies[:2])}..."
|
|
431
|
-
else:
|
|
432
|
-
tools_display = ", ".join(agent.dependencies)
|
|
433
|
-
else:
|
|
434
|
-
# Try to get model from template
|
|
435
|
-
try:
|
|
436
|
-
template_path = self._get_agent_template_path(agent.name)
|
|
437
|
-
if template_path.exists():
|
|
438
|
-
with open(template_path) as f:
|
|
439
|
-
template = json.load(f)
|
|
440
|
-
model = template.get("capabilities", {}).get("model", "default")
|
|
441
|
-
tools_display = f"Model: {model}"
|
|
442
|
-
else:
|
|
443
|
-
tools_display = "Default"
|
|
444
|
-
except:
|
|
445
|
-
tools_display = "Default"
|
|
446
|
-
|
|
447
|
-
# Truncate description for table display
|
|
448
|
-
desc_display = (
|
|
449
|
-
agent.description[:42] + "..."
|
|
450
|
-
if len(agent.description) > 42
|
|
451
|
-
else agent.description
|
|
452
|
-
)
|
|
347
|
+
# Step 2: Discover and display available agents
|
|
348
|
+
self.console.print("\n[bold white]═══ Available Agents ═══[/bold white]\n")
|
|
453
349
|
|
|
454
|
-
|
|
350
|
+
try:
|
|
351
|
+
# Discover agents (includes both local and remote)
|
|
352
|
+
agents = self.agent_manager.discover_agents(include_remote=True)
|
|
455
353
|
|
|
456
|
-
|
|
354
|
+
# Set deployment status on each agent for display
|
|
355
|
+
deployed_ids = get_deployed_agent_ids()
|
|
356
|
+
for agent in agents:
|
|
357
|
+
# Extract leaf name for comparison
|
|
358
|
+
agent_leaf_name = agent.name.split("/")[-1]
|
|
359
|
+
agent.is_deployed = agent_leaf_name in deployed_ids
|
|
457
360
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
agent_id = Prompt.ask("Enter agent ID to enable (or 'all' for all agents)")
|
|
361
|
+
# Filter BASE_AGENT from display (1M-502 Phase 1)
|
|
362
|
+
agents = self._filter_agent_configs(agents, filter_deployed=False)
|
|
461
363
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
for agent in agents:
|
|
465
|
-
self.agent_manager.set_agent_enabled(agent.name, True)
|
|
466
|
-
self.console.print("[green]All agents enabled successfully![/green]")
|
|
467
|
-
else:
|
|
468
|
-
try:
|
|
469
|
-
idx = int(agent_id) - 1
|
|
470
|
-
if 0 <= idx < len(agents):
|
|
471
|
-
agent = agents[idx]
|
|
472
|
-
self.agent_manager.set_agent_enabled(agent.name, True)
|
|
364
|
+
if not agents:
|
|
365
|
+
self.console.print("[yellow]No agents found[/yellow]")
|
|
473
366
|
self.console.print(
|
|
474
|
-
|
|
367
|
+
"[dim]Configure sources with 'claude-mpm agent-source add'[/dim]\n"
|
|
475
368
|
)
|
|
476
369
|
else:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
self.console.print("[red]Invalid input. Please enter a number.[/red]")
|
|
480
|
-
|
|
481
|
-
Prompt.ask("Press Enter to continue")
|
|
370
|
+
# Display agents in a table (already filtered at line 339)
|
|
371
|
+
self._display_agents_with_source_info(agents)
|
|
482
372
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
373
|
+
except Exception as e:
|
|
374
|
+
self.console.print(f"[red]Error discovering agents: {e}[/red]")
|
|
375
|
+
self.logger.error(f"Agent discovery failed: {e}", exc_info=True)
|
|
486
376
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
for agent in agents:
|
|
490
|
-
self.agent_manager.set_agent_enabled(agent.name, False)
|
|
491
|
-
self.console.print("[green]All agents disabled successfully![/green]")
|
|
492
|
-
else:
|
|
377
|
+
# Step 3: Menu options with arrow-key navigation
|
|
378
|
+
self.console.print()
|
|
493
379
|
try:
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
380
|
+
choice = questionary.select(
|
|
381
|
+
"Agent Management:",
|
|
382
|
+
choices=[
|
|
383
|
+
"Manage sources (add/remove repositories)",
|
|
384
|
+
"Select Agents",
|
|
385
|
+
"Install preset (predefined sets)",
|
|
386
|
+
"Remove agents",
|
|
387
|
+
"View agent details",
|
|
388
|
+
"Toggle agents (legacy enable/disable)",
|
|
389
|
+
questionary.Separator(),
|
|
390
|
+
"← Back to main menu",
|
|
391
|
+
],
|
|
392
|
+
style=self.QUESTIONARY_STYLE,
|
|
393
|
+
).ask()
|
|
394
|
+
|
|
395
|
+
if choice is None or choice == "← Back to main menu":
|
|
396
|
+
break
|
|
507
397
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
398
|
+
agents_var = agents if "agents" in locals() else []
|
|
399
|
+
|
|
400
|
+
# Map selection to action
|
|
401
|
+
if choice == "Manage sources (add/remove repositories)":
|
|
402
|
+
self._manage_sources()
|
|
403
|
+
elif choice == "Select Agents":
|
|
404
|
+
self._deploy_agents_individual(agents_var)
|
|
405
|
+
elif choice == "Install preset (predefined sets)":
|
|
406
|
+
self._deploy_agents_preset()
|
|
407
|
+
elif choice == "Remove agents":
|
|
408
|
+
self._remove_agents(agents_var)
|
|
409
|
+
elif choice == "View agent details":
|
|
410
|
+
self._view_agent_details_enhanced(agents_var)
|
|
411
|
+
elif choice == "Toggle agents (legacy enable/disable)":
|
|
412
|
+
self._toggle_agents_interactive(agents_var)
|
|
413
|
+
|
|
414
|
+
except KeyboardInterrupt:
|
|
415
|
+
self.console.print("\n[yellow]Operation cancelled[/yellow]")
|
|
416
|
+
break
|
|
511
417
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
agent = agents[idx]
|
|
516
|
-
self._edit_agent_template(agent)
|
|
517
|
-
else:
|
|
518
|
-
self.console.print("[red]Invalid agent ID.[/red]")
|
|
519
|
-
Prompt.ask("Press Enter to continue")
|
|
520
|
-
except ValueError:
|
|
521
|
-
self.console.print("[red]Invalid input. Please enter a number.[/red]")
|
|
522
|
-
Prompt.ask("Press Enter to continue")
|
|
418
|
+
def _display_agents_table(self, agents: List[AgentConfig]) -> None:
|
|
419
|
+
"""Display a table of available agents."""
|
|
420
|
+
self.agent_display.display_agents_table(agents)
|
|
523
421
|
|
|
524
|
-
def
|
|
525
|
-
"""
|
|
526
|
-
self.
|
|
527
|
-
self.console.print(f"[bold]Editing template for: {agent.name}[/bold]\n")
|
|
422
|
+
def _display_agents_with_pending_states(self, agents: List[AgentConfig]) -> None:
|
|
423
|
+
"""Display agents table with pending state indicators."""
|
|
424
|
+
self.agent_display.display_agents_with_pending_states(agents)
|
|
528
425
|
|
|
529
|
-
|
|
530
|
-
|
|
426
|
+
def _toggle_agents_interactive(self, agents: List[AgentConfig]) -> None:
|
|
427
|
+
"""Interactive multi-agent enable/disable with batch save."""
|
|
531
428
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
str(self.agent_manager.templates_dir)
|
|
537
|
-
)
|
|
538
|
-
else:
|
|
539
|
-
# Create a minimal template structure based on system templates
|
|
540
|
-
template = {
|
|
541
|
-
"schema_version": "1.2.0",
|
|
542
|
-
"agent_id": agent.name,
|
|
543
|
-
"agent_version": "1.0.0",
|
|
544
|
-
"agent_type": agent.name.replace("-", "_"),
|
|
545
|
-
"metadata": {
|
|
546
|
-
"name": agent.name.replace("-", " ").title() + " Agent",
|
|
547
|
-
"description": agent.description,
|
|
548
|
-
"tags": [agent.name],
|
|
549
|
-
"author": "Custom",
|
|
550
|
-
"created_at": "",
|
|
551
|
-
"updated_at": "",
|
|
552
|
-
},
|
|
553
|
-
"capabilities": {
|
|
554
|
-
"model": "opus",
|
|
555
|
-
"tools": (
|
|
556
|
-
agent.dependencies
|
|
557
|
-
if agent.dependencies
|
|
558
|
-
else ["Read", "Write", "Edit", "Bash"]
|
|
559
|
-
),
|
|
560
|
-
},
|
|
561
|
-
"instructions": {
|
|
562
|
-
"base_template": "BASE_AGENT_TEMPLATE.md",
|
|
563
|
-
"custom_instructions": "",
|
|
564
|
-
},
|
|
565
|
-
}
|
|
566
|
-
is_system = False
|
|
567
|
-
|
|
568
|
-
# Display current template
|
|
569
|
-
if is_system:
|
|
570
|
-
self.console.print(
|
|
571
|
-
"[yellow]Viewing SYSTEM template (read-only). Customization will create a local copy.[/yellow]\n"
|
|
572
|
-
)
|
|
429
|
+
# Initialize pending states from current states
|
|
430
|
+
for agent in agents:
|
|
431
|
+
current_state = self.agent_manager.is_agent_enabled(agent.name)
|
|
432
|
+
self.agent_manager.set_agent_enabled_deferred(agent.name, current_state)
|
|
573
433
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
434
|
+
while True:
|
|
435
|
+
# Display table with pending states
|
|
436
|
+
self._display_agents_with_pending_states(agents)
|
|
437
|
+
|
|
438
|
+
# Show menu
|
|
439
|
+
self.console.print("\n[bold]Toggle Agent Status:[/bold]")
|
|
440
|
+
text_toggle = Text(" ")
|
|
441
|
+
text_toggle.append("[t]", style="bold blue")
|
|
442
|
+
text_toggle.append(" Enter agent IDs to toggle (e.g., '1,3,5' or '1-4')")
|
|
443
|
+
self.console.print(text_toggle)
|
|
444
|
+
|
|
445
|
+
text_all = Text(" ")
|
|
446
|
+
text_all.append("[a]", style="bold blue")
|
|
447
|
+
text_all.append(" Enable all agents")
|
|
448
|
+
self.console.print(text_all)
|
|
449
|
+
|
|
450
|
+
text_none = Text(" ")
|
|
451
|
+
text_none.append("[n]", style="bold blue")
|
|
452
|
+
text_none.append(" Disable all agents")
|
|
453
|
+
self.console.print(text_none)
|
|
454
|
+
|
|
455
|
+
text_save = Text(" ")
|
|
456
|
+
text_save.append("[s]", style="bold green")
|
|
457
|
+
text_save.append(" Save changes and return")
|
|
458
|
+
self.console.print(text_save)
|
|
459
|
+
|
|
460
|
+
text_cancel = Text(" ")
|
|
461
|
+
text_cancel.append("[c]", style="bold magenta")
|
|
462
|
+
text_cancel.append(" Cancel (discard changes)")
|
|
463
|
+
self.console.print(text_cancel)
|
|
464
|
+
|
|
465
|
+
choice = (
|
|
466
|
+
Prompt.ask("[bold blue]Select an option[/bold blue]", default="s")
|
|
467
|
+
.strip()
|
|
468
|
+
.lower()
|
|
588
469
|
)
|
|
589
470
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
471
|
+
if choice == "s":
|
|
472
|
+
if self.agent_manager.has_pending_changes():
|
|
473
|
+
self.agent_manager.commit_deferred_changes()
|
|
474
|
+
self.console.print("[green]✓ Changes saved successfully![/green]")
|
|
475
|
+
else:
|
|
476
|
+
self.console.print("[yellow]No changes to save.[/yellow]")
|
|
477
|
+
Prompt.ask("Press Enter to continue")
|
|
478
|
+
break
|
|
479
|
+
if choice == "c":
|
|
480
|
+
self.agent_manager.discard_deferred_changes()
|
|
481
|
+
self.console.print("[yellow]Changes discarded.[/yellow]")
|
|
482
|
+
Prompt.ask("Press Enter to continue")
|
|
483
|
+
break
|
|
484
|
+
if choice == "a":
|
|
485
|
+
for agent in agents:
|
|
486
|
+
self.agent_manager.set_agent_enabled_deferred(agent.name, True)
|
|
487
|
+
elif choice == "n":
|
|
488
|
+
for agent in agents:
|
|
489
|
+
self.agent_manager.set_agent_enabled_deferred(agent.name, False)
|
|
490
|
+
elif choice == "t" or choice.replace(",", "").replace("-", "").isdigit():
|
|
491
|
+
selected_ids = self._parse_id_selection(
|
|
492
|
+
choice if choice != "t" else Prompt.ask("Enter IDs"), len(agents)
|
|
493
|
+
)
|
|
494
|
+
for idx in selected_ids:
|
|
495
|
+
if 1 <= idx <= len(agents):
|
|
496
|
+
agent = agents[idx - 1]
|
|
497
|
+
current = self.agent_manager.get_pending_state(agent.name)
|
|
498
|
+
self.agent_manager.set_agent_enabled_deferred(
|
|
499
|
+
agent.name, not current
|
|
500
|
+
)
|
|
612
501
|
|
|
613
|
-
|
|
502
|
+
def _customize_agent_template(self, agents: List[AgentConfig]) -> None:
|
|
503
|
+
"""Customize agent JSON template."""
|
|
504
|
+
self.template_editor.customize_agent_template(agents)
|
|
614
505
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
self._create_custom_template_copy(agent, template)
|
|
619
|
-
elif choice == "2":
|
|
620
|
-
# View full template
|
|
621
|
-
self._view_full_template(template)
|
|
622
|
-
elif choice == "1":
|
|
623
|
-
self._edit_in_external_editor(template_path, template)
|
|
624
|
-
elif choice == "2":
|
|
625
|
-
self._modify_template_field(template, template_path)
|
|
626
|
-
elif choice == "3":
|
|
627
|
-
self._remove_template_field(template, template_path)
|
|
628
|
-
elif choice == "4":
|
|
629
|
-
self._reset_template(agent, template_path)
|
|
630
|
-
|
|
631
|
-
if choice != "b":
|
|
632
|
-
Prompt.ask("Press Enter to continue")
|
|
506
|
+
def _edit_agent_template(self, agent: AgentConfig) -> None:
|
|
507
|
+
"""Edit an agent's JSON template."""
|
|
508
|
+
self.template_editor.edit_agent_template(agent)
|
|
633
509
|
|
|
634
510
|
def _get_agent_template_path(self, agent_name: str) -> Path:
|
|
635
511
|
"""Get the path to an agent's template file."""
|
|
636
|
-
|
|
637
|
-
if self.current_scope == "project":
|
|
638
|
-
config_dir = self.project_dir / ".claude-mpm" / "agents"
|
|
639
|
-
else:
|
|
640
|
-
config_dir = Path.home() / ".claude-mpm" / "agents"
|
|
641
|
-
|
|
642
|
-
config_dir.mkdir(parents=True, exist_ok=True)
|
|
643
|
-
custom_template = config_dir / f"{agent_name}.json"
|
|
644
|
-
|
|
645
|
-
# If custom template exists, return it
|
|
646
|
-
if custom_template.exists():
|
|
647
|
-
return custom_template
|
|
648
|
-
|
|
649
|
-
# Otherwise, look for the system template
|
|
650
|
-
# Handle various naming conventions
|
|
651
|
-
possible_names = [
|
|
652
|
-
f"{agent_name}.json",
|
|
653
|
-
f"{agent_name.replace('-', '_')}.json",
|
|
654
|
-
f"{agent_name}-agent.json",
|
|
655
|
-
f"{agent_name.replace('-', '_')}_agent.json",
|
|
656
|
-
]
|
|
657
|
-
|
|
658
|
-
for name in possible_names:
|
|
659
|
-
system_template = self.agent_manager.templates_dir / name
|
|
660
|
-
if system_template.exists():
|
|
661
|
-
return system_template
|
|
662
|
-
|
|
663
|
-
# Return the custom template path for new templates
|
|
664
|
-
return custom_template
|
|
512
|
+
return self.template_editor.get_agent_template_path(agent_name)
|
|
665
513
|
|
|
666
514
|
def _edit_in_external_editor(self, template_path: Path, template: Dict) -> None:
|
|
667
515
|
"""Open template in external editor."""
|
|
668
|
-
|
|
669
|
-
import tempfile
|
|
670
|
-
|
|
671
|
-
# Write current template to temp file
|
|
672
|
-
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
673
|
-
json.dump(template, f, indent=2)
|
|
674
|
-
temp_path = f.name
|
|
675
|
-
|
|
676
|
-
# Get editor from environment
|
|
677
|
-
editor = os.environ.get("EDITOR", "nano")
|
|
678
|
-
|
|
679
|
-
try:
|
|
680
|
-
# Open in editor
|
|
681
|
-
subprocess.call([editor, temp_path])
|
|
682
|
-
|
|
683
|
-
# Read back the edited content
|
|
684
|
-
with open(temp_path) as f:
|
|
685
|
-
new_template = json.load(f)
|
|
686
|
-
|
|
687
|
-
# Save to actual template path
|
|
688
|
-
with open(template_path, "w") as f:
|
|
689
|
-
json.dump(new_template, f, indent=2)
|
|
690
|
-
|
|
691
|
-
self.console.print("[green]Template updated successfully![/green]")
|
|
692
|
-
|
|
693
|
-
except Exception as e:
|
|
694
|
-
self.console.print(f"[red]Error editing template: {e}[/red]")
|
|
695
|
-
finally:
|
|
696
|
-
# Clean up temp file
|
|
697
|
-
Path(temp_path).unlink(missing_ok=True)
|
|
516
|
+
self.template_editor.edit_in_external_editor(template_path, template)
|
|
698
517
|
|
|
699
518
|
def _modify_template_field(self, template: Dict, template_path: Path) -> None:
|
|
700
519
|
"""Add or modify a field in the template."""
|
|
701
|
-
|
|
702
|
-
"Enter field name (use dot notation for nested, e.g., 'config.timeout')"
|
|
703
|
-
)
|
|
704
|
-
field_value = Prompt.ask("Enter field value (JSON format)")
|
|
705
|
-
|
|
706
|
-
try:
|
|
707
|
-
# Parse the value as JSON
|
|
708
|
-
value = json.loads(field_value)
|
|
709
|
-
|
|
710
|
-
# Navigate to the field location
|
|
711
|
-
parts = field_name.split(".")
|
|
712
|
-
current = template
|
|
713
|
-
|
|
714
|
-
for part in parts[:-1]:
|
|
715
|
-
if part not in current:
|
|
716
|
-
current[part] = {}
|
|
717
|
-
current = current[part]
|
|
718
|
-
|
|
719
|
-
# Set the value
|
|
720
|
-
current[parts[-1]] = value
|
|
721
|
-
|
|
722
|
-
# Save the template
|
|
723
|
-
with open(template_path, "w") as f:
|
|
724
|
-
json.dump(template, f, indent=2)
|
|
725
|
-
|
|
726
|
-
self.console.print(
|
|
727
|
-
f"[green]Field '{field_name}' updated successfully![/green]"
|
|
728
|
-
)
|
|
729
|
-
|
|
730
|
-
except json.JSONDecodeError:
|
|
731
|
-
self.console.print("[red]Invalid JSON value. Please try again.[/red]")
|
|
732
|
-
except Exception as e:
|
|
733
|
-
self.console.print(f"[red]Error updating field: {e}[/red]")
|
|
520
|
+
self.template_editor.modify_template_field(template, template_path)
|
|
734
521
|
|
|
735
522
|
def _remove_template_field(self, template: Dict, template_path: Path) -> None:
|
|
736
523
|
"""Remove a field from the template."""
|
|
737
|
-
|
|
738
|
-
"Enter field name to remove (use dot notation for nested)"
|
|
739
|
-
)
|
|
740
|
-
|
|
741
|
-
try:
|
|
742
|
-
# Navigate to the field location
|
|
743
|
-
parts = field_name.split(".")
|
|
744
|
-
current = template
|
|
745
|
-
|
|
746
|
-
for part in parts[:-1]:
|
|
747
|
-
if part not in current:
|
|
748
|
-
raise KeyError(f"Field '{field_name}' not found")
|
|
749
|
-
current = current[part]
|
|
750
|
-
|
|
751
|
-
# Remove the field
|
|
752
|
-
if parts[-1] in current:
|
|
753
|
-
del current[parts[-1]]
|
|
754
|
-
|
|
755
|
-
# Save the template
|
|
756
|
-
with open(template_path, "w") as f:
|
|
757
|
-
json.dump(template, f, indent=2)
|
|
758
|
-
|
|
759
|
-
self.console.print(
|
|
760
|
-
f"[green]Field '{field_name}' removed successfully![/green]"
|
|
761
|
-
)
|
|
762
|
-
else:
|
|
763
|
-
self.console.print(f"[red]Field '{field_name}' not found.[/red]")
|
|
764
|
-
|
|
765
|
-
except Exception as e:
|
|
766
|
-
self.console.print(f"[red]Error removing field: {e}[/red]")
|
|
524
|
+
self.template_editor.remove_template_field(template, template_path)
|
|
767
525
|
|
|
768
526
|
def _reset_template(self, agent: AgentConfig, template_path: Path) -> None:
|
|
769
527
|
"""Reset template to defaults."""
|
|
770
|
-
|
|
771
|
-
# Remove custom template file
|
|
772
|
-
template_path.unlink(missing_ok=True)
|
|
773
|
-
self.console.print(
|
|
774
|
-
f"[green]Template for '{agent.name}' reset to defaults![/green]"
|
|
775
|
-
)
|
|
528
|
+
self.template_editor.reset_template(agent, template_path)
|
|
776
529
|
|
|
777
530
|
def _create_custom_template_copy(self, agent: AgentConfig, template: Dict) -> None:
|
|
778
531
|
"""Create a customized copy of a system template."""
|
|
779
|
-
|
|
780
|
-
config_dir = self.project_dir / ".claude-mpm" / "agents"
|
|
781
|
-
else:
|
|
782
|
-
config_dir = Path.home() / ".claude-mpm" / "agents"
|
|
532
|
+
self.template_editor.create_custom_template_copy(agent, template)
|
|
783
533
|
|
|
784
|
-
|
|
785
|
-
|
|
534
|
+
def _view_full_template(self, template: Dict) -> None:
|
|
535
|
+
"""View the full template without truncation."""
|
|
536
|
+
self.template_editor.view_full_template(template)
|
|
786
537
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
)
|
|
790
|
-
return
|
|
538
|
+
def _reset_agent_defaults(self, agents: List[AgentConfig]) -> None:
|
|
539
|
+
"""Reset an agent to default enabled state and remove custom template."""
|
|
540
|
+
self.template_editor.reset_agent_defaults(agents)
|
|
791
541
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
542
|
+
def _edit_templates(self) -> None:
|
|
543
|
+
"""Template editing interface."""
|
|
544
|
+
self.template_editor.edit_templates_interface()
|
|
795
545
|
|
|
796
|
-
|
|
797
|
-
|
|
546
|
+
def _manage_behaviors(self) -> None:
|
|
547
|
+
"""Behavior file management interface."""
|
|
548
|
+
# Note: BehaviorManager handles its own loop and clears screen
|
|
549
|
+
# but doesn't display our header. We'll need to update BehaviorManager
|
|
550
|
+
# to accept a header callback in the future. For now, just delegate.
|
|
551
|
+
self.behavior_manager.manage_behaviors()
|
|
798
552
|
|
|
799
|
-
def
|
|
800
|
-
"""
|
|
801
|
-
|
|
802
|
-
|
|
553
|
+
def _manage_skills(self) -> None:
|
|
554
|
+
"""Skills management interface."""
|
|
555
|
+
from ...cli.interactive.skills_wizard import SkillsWizard
|
|
556
|
+
from ...skills.skill_manager import get_manager
|
|
803
557
|
|
|
804
|
-
|
|
805
|
-
|
|
558
|
+
wizard = SkillsWizard()
|
|
559
|
+
manager = get_manager()
|
|
806
560
|
|
|
807
|
-
|
|
561
|
+
while True:
|
|
562
|
+
self.console.clear()
|
|
563
|
+
self._display_header()
|
|
808
564
|
|
|
809
|
-
|
|
810
|
-
self.console.print(
|
|
565
|
+
self.console.print("\n[bold]Skills Management Options:[/bold]\n")
|
|
566
|
+
self.console.print(" [1] View Available Skills")
|
|
567
|
+
self.console.print(" [2] Configure Skills for Agents")
|
|
568
|
+
self.console.print(" [3] View Current Skill Mappings")
|
|
569
|
+
self.console.print(" [4] Auto-Link Skills to Agents")
|
|
570
|
+
self.console.print(" [b] Back to Main Menu")
|
|
571
|
+
self.console.print()
|
|
811
572
|
|
|
812
|
-
|
|
813
|
-
"""View detailed information about an agent."""
|
|
814
|
-
agent_id = Prompt.ask("Enter agent ID to view")
|
|
573
|
+
choice = Prompt.ask("[bold blue]Select an option[/bold blue]", default="b")
|
|
815
574
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
575
|
+
if choice == "1":
|
|
576
|
+
# View available skills
|
|
577
|
+
self.console.clear()
|
|
578
|
+
self._display_header()
|
|
579
|
+
wizard.list_available_skills()
|
|
580
|
+
Prompt.ask("\nPress Enter to continue")
|
|
820
581
|
|
|
582
|
+
elif choice == "2":
|
|
583
|
+
# Configure skills interactively
|
|
821
584
|
self.console.clear()
|
|
822
585
|
self._display_header()
|
|
823
586
|
|
|
824
|
-
#
|
|
825
|
-
|
|
826
|
-
|
|
587
|
+
# Get list of enabled agents
|
|
588
|
+
agents = self.agent_manager.discover_agents()
|
|
589
|
+
# Filter BASE_AGENT from all agent operations (1M-502 Phase 1)
|
|
590
|
+
agents = self._filter_agent_configs(agents, filter_deployed=False)
|
|
591
|
+
enabled_agents = [
|
|
592
|
+
a.name
|
|
593
|
+
for a in agents
|
|
594
|
+
if self.agent_manager.get_pending_state(a.name)
|
|
595
|
+
]
|
|
596
|
+
|
|
597
|
+
if not enabled_agents:
|
|
598
|
+
self.console.print(
|
|
599
|
+
"[yellow]No agents are currently enabled.[/yellow]"
|
|
600
|
+
)
|
|
601
|
+
self.console.print(
|
|
602
|
+
"Please enable agents first in Agent Management."
|
|
603
|
+
)
|
|
604
|
+
Prompt.ask("\nPress Enter to continue")
|
|
605
|
+
continue
|
|
827
606
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
with open(template_path) as f:
|
|
831
|
-
template = json.load(f)
|
|
607
|
+
# Run skills wizard
|
|
608
|
+
success, mapping = wizard.run_interactive_selection(enabled_agents)
|
|
832
609
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
610
|
+
if success:
|
|
611
|
+
# Save the configuration
|
|
612
|
+
manager.save_mappings_to_config()
|
|
613
|
+
self.console.print("\n[green]✓ Skills configuration saved![/green]")
|
|
614
|
+
else:
|
|
615
|
+
self.console.print(
|
|
616
|
+
"\n[yellow]Skills configuration cancelled.[/yellow]"
|
|
617
|
+
)
|
|
836
618
|
|
|
837
|
-
|
|
838
|
-
full_desc = metadata.get("description", agent.description)
|
|
619
|
+
Prompt.ask("\nPress Enter to continue")
|
|
839
620
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
621
|
+
elif choice == "3":
|
|
622
|
+
# View current mappings
|
|
623
|
+
self.console.clear()
|
|
624
|
+
self._display_header()
|
|
843
625
|
|
|
844
|
-
|
|
845
|
-
tags = metadata.get("tags", [])
|
|
626
|
+
self.console.print("\n[bold]Current Skill Mappings:[/bold]\n")
|
|
846
627
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
628
|
+
mappings = manager.list_agent_skill_mappings()
|
|
629
|
+
if not mappings:
|
|
630
|
+
self.console.print("[dim]No skill mappings configured yet.[/dim]")
|
|
631
|
+
else:
|
|
632
|
+
from rich.table import Table
|
|
850
633
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
634
|
+
table = Table(show_header=True, header_style="bold white")
|
|
635
|
+
table.add_column("Agent", style="white", no_wrap=True)
|
|
636
|
+
table.add_column("Skills", style="green", no_wrap=True)
|
|
854
637
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
[
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
"""
|
|
861
|
-
except:
|
|
862
|
-
pass
|
|
863
|
-
|
|
864
|
-
# Create detail panel
|
|
865
|
-
detail_text = f"""
|
|
866
|
-
[bold]Name:[/bold] {agent.name}
|
|
867
|
-
[bold]Status:[/bold] {'[green]Enabled[/green]' if self.agent_manager.is_agent_enabled(agent.name) else '[red]Disabled[/red]'}
|
|
868
|
-
[bold]Template Path:[/bold] {template_path}
|
|
869
|
-
[bold]Is System Template:[/bold] {'Yes' if str(template_path).startswith(str(self.agent_manager.templates_dir)) else 'No (Custom)'}
|
|
870
|
-
{extra_info}
|
|
871
|
-
"""
|
|
872
|
-
|
|
873
|
-
panel = Panel(
|
|
874
|
-
detail_text.strip(),
|
|
875
|
-
title=f"[bold]{agent.name} Details[/bold]",
|
|
876
|
-
box=ROUNDED,
|
|
877
|
-
style="cyan",
|
|
878
|
-
)
|
|
638
|
+
for agent_id, skills in mappings.items():
|
|
639
|
+
skills_str = (
|
|
640
|
+
", ".join(skills) if skills else "[dim](none)[/dim]"
|
|
641
|
+
)
|
|
642
|
+
table.add_row(agent_id, skills_str)
|
|
879
643
|
|
|
880
|
-
|
|
644
|
+
self.console.print(table)
|
|
881
645
|
|
|
882
|
-
|
|
883
|
-
self.console.print("[red]Invalid agent ID.[/red]")
|
|
646
|
+
Prompt.ask("\nPress Enter to continue")
|
|
884
647
|
|
|
885
|
-
|
|
886
|
-
|
|
648
|
+
elif choice == "4":
|
|
649
|
+
# Auto-link skills
|
|
650
|
+
self.console.clear()
|
|
651
|
+
self._display_header()
|
|
887
652
|
|
|
888
|
-
|
|
653
|
+
self.console.print("\n[bold]Auto-Linking Skills to Agents...[/bold]\n")
|
|
889
654
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
655
|
+
# Get enabled agents
|
|
656
|
+
agents = self.agent_manager.discover_agents()
|
|
657
|
+
# Filter BASE_AGENT from all agent operations (1M-502 Phase 1)
|
|
658
|
+
agents = self._filter_agent_configs(agents, filter_deployed=False)
|
|
659
|
+
enabled_agents = [
|
|
660
|
+
a.name
|
|
661
|
+
for a in agents
|
|
662
|
+
if self.agent_manager.get_pending_state(a.name)
|
|
663
|
+
]
|
|
894
664
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
665
|
+
if not enabled_agents:
|
|
666
|
+
self.console.print(
|
|
667
|
+
"[yellow]No agents are currently enabled.[/yellow]"
|
|
668
|
+
)
|
|
669
|
+
self.console.print(
|
|
670
|
+
"Please enable agents first in Agent Management."
|
|
671
|
+
)
|
|
672
|
+
Prompt.ask("\nPress Enter to continue")
|
|
673
|
+
continue
|
|
900
674
|
|
|
901
|
-
|
|
675
|
+
# Auto-link
|
|
676
|
+
mapping = wizard._auto_link_skills(enabled_agents)
|
|
902
677
|
|
|
903
|
-
|
|
904
|
-
|
|
678
|
+
# Display preview
|
|
679
|
+
self.console.print("Auto-linked skills:\n")
|
|
680
|
+
for agent_id, skills in mapping.items():
|
|
681
|
+
self.console.print(f" [yellow]{agent_id}[/yellow]:")
|
|
682
|
+
for skill in skills:
|
|
683
|
+
self.console.print(f" - {skill}")
|
|
905
684
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
self.console.print(" [cyan][1][/cyan] Edit identity configuration")
|
|
909
|
-
self.console.print(" [cyan][2][/cyan] Edit workflow configuration")
|
|
910
|
-
self.console.print(" [cyan][3][/cyan] Import behavior file")
|
|
911
|
-
self.console.print(" [cyan][4][/cyan] Export behavior file")
|
|
912
|
-
self.console.print(" [cyan][b][/cyan] Back to main menu")
|
|
913
|
-
self.console.print()
|
|
685
|
+
# Confirm
|
|
686
|
+
confirm = Confirm.ask("\nApply this configuration?", default=True)
|
|
914
687
|
|
|
915
|
-
|
|
688
|
+
if confirm:
|
|
689
|
+
wizard._apply_skills_configuration(mapping)
|
|
690
|
+
manager.save_mappings_to_config()
|
|
691
|
+
self.console.print("\n[green]✓ Auto-linking complete![/green]")
|
|
692
|
+
else:
|
|
693
|
+
self.console.print("\n[yellow]Auto-linking cancelled.[/yellow]")
|
|
694
|
+
|
|
695
|
+
Prompt.ask("\nPress Enter to continue")
|
|
916
696
|
|
|
917
|
-
|
|
697
|
+
elif choice == "b":
|
|
918
698
|
break
|
|
919
|
-
if choice == "1":
|
|
920
|
-
self._edit_identity_config()
|
|
921
|
-
elif choice == "2":
|
|
922
|
-
self._edit_workflow_config()
|
|
923
|
-
elif choice == "3":
|
|
924
|
-
self._import_behavior_file()
|
|
925
|
-
elif choice == "4":
|
|
926
|
-
self._export_behavior_file()
|
|
927
699
|
else:
|
|
928
|
-
self.console.print("[red]Invalid choice.[/red]")
|
|
929
|
-
Prompt.ask("
|
|
700
|
+
self.console.print("[red]Invalid choice. Please try again.[/red]")
|
|
701
|
+
Prompt.ask("\nPress Enter to continue")
|
|
930
702
|
|
|
931
703
|
def _display_behavior_files(self) -> None:
|
|
932
704
|
"""Display current behavior files."""
|
|
933
|
-
|
|
934
|
-
config_dir = self.project_dir / ".claude-mpm" / "behaviors"
|
|
935
|
-
else:
|
|
936
|
-
config_dir = Path.home() / ".claude-mpm" / "behaviors"
|
|
937
|
-
|
|
938
|
-
config_dir.mkdir(parents=True, exist_ok=True)
|
|
939
|
-
|
|
940
|
-
table = Table(title="Behavior Files", box=ROUNDED)
|
|
941
|
-
table.add_column("File", style="cyan", width=30)
|
|
942
|
-
table.add_column("Size", style="dim", width=10)
|
|
943
|
-
table.add_column("Modified", style="white", width=20)
|
|
944
|
-
|
|
945
|
-
identity_file = config_dir / "identity.yaml"
|
|
946
|
-
workflow_file = config_dir / "workflow.yaml"
|
|
947
|
-
|
|
948
|
-
for file_path in [identity_file, workflow_file]:
|
|
949
|
-
if file_path.exists():
|
|
950
|
-
stat = file_path.stat()
|
|
951
|
-
size = f"{stat.st_size} bytes"
|
|
952
|
-
modified = f"{stat.st_mtime:.0f}" # Simplified timestamp
|
|
953
|
-
table.add_row(file_path.name, size, modified)
|
|
954
|
-
else:
|
|
955
|
-
table.add_row(file_path.name, "[dim]Not found[/dim]", "-")
|
|
956
|
-
|
|
957
|
-
self.console.print(table)
|
|
705
|
+
self.behavior_manager.display_behavior_files()
|
|
958
706
|
|
|
959
707
|
def _edit_identity_config(self) -> None:
|
|
960
708
|
"""Edit identity configuration."""
|
|
961
|
-
self.
|
|
962
|
-
"[yellow]Identity configuration editor - Coming soon![/yellow]"
|
|
963
|
-
)
|
|
964
|
-
Prompt.ask("Press Enter to continue")
|
|
709
|
+
self.behavior_manager.edit_identity_config()
|
|
965
710
|
|
|
966
711
|
def _edit_workflow_config(self) -> None:
|
|
967
712
|
"""Edit workflow configuration."""
|
|
968
|
-
self.
|
|
969
|
-
"[yellow]Workflow configuration editor - Coming soon![/yellow]"
|
|
970
|
-
)
|
|
971
|
-
Prompt.ask("Press Enter to continue")
|
|
713
|
+
self.behavior_manager.edit_workflow_config()
|
|
972
714
|
|
|
973
715
|
def _import_behavior_file(self) -> None:
|
|
974
716
|
"""Import a behavior file."""
|
|
975
|
-
|
|
717
|
+
self.behavior_manager.import_behavior_file()
|
|
976
718
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
self.console.print(f"[red]File not found: {file_path}[/red]")
|
|
981
|
-
return
|
|
719
|
+
def _export_behavior_file(self) -> None:
|
|
720
|
+
"""Export a behavior file."""
|
|
721
|
+
self.behavior_manager.export_behavior_file()
|
|
982
722
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
else:
|
|
987
|
-
config_dir = Path.home() / ".claude-mpm" / "behaviors"
|
|
723
|
+
def _manage_startup_configuration(self) -> bool:
|
|
724
|
+
"""Manage startup configuration for MCP services and agents."""
|
|
725
|
+
return self.startup_manager.manage_startup_configuration()
|
|
988
726
|
|
|
989
|
-
|
|
727
|
+
def _load_startup_configuration(self, config: Config) -> Dict:
|
|
728
|
+
"""Load current startup configuration from config."""
|
|
729
|
+
return self.startup_manager.load_startup_configuration(config)
|
|
990
730
|
|
|
991
|
-
|
|
992
|
-
|
|
731
|
+
def _display_startup_configuration(self, startup_config: Dict) -> None:
|
|
732
|
+
"""Display current startup configuration in a table."""
|
|
733
|
+
self.startup_manager.display_startup_configuration(startup_config)
|
|
993
734
|
|
|
994
|
-
|
|
995
|
-
|
|
735
|
+
def _configure_mcp_services(self, startup_config: Dict, config: Config) -> None:
|
|
736
|
+
"""Configure which MCP services to enable at startup."""
|
|
737
|
+
self.startup_manager.configure_mcp_services(startup_config, config)
|
|
996
738
|
|
|
997
|
-
|
|
739
|
+
def _configure_hook_services(self, startup_config: Dict, config: Config) -> None:
|
|
740
|
+
"""Configure which hook services to enable at startup."""
|
|
741
|
+
self.startup_manager.configure_hook_services(startup_config, config)
|
|
998
742
|
|
|
999
|
-
|
|
1000
|
-
|
|
743
|
+
def _configure_system_agents(self, startup_config: Dict, config: Config) -> None:
|
|
744
|
+
"""Configure which system agents to deploy at startup."""
|
|
745
|
+
self.startup_manager.configure_system_agents(startup_config, config)
|
|
1001
746
|
|
|
1002
|
-
|
|
747
|
+
def _parse_id_selection(self, selection: str, max_id: int) -> List[int]:
|
|
748
|
+
"""Parse ID selection string (e.g., '1,3,5' or '1-4')."""
|
|
749
|
+
return parse_id_selection(selection, max_id)
|
|
1003
750
|
|
|
1004
|
-
def
|
|
1005
|
-
"""
|
|
1006
|
-
self.
|
|
1007
|
-
Prompt.ask("Press Enter to continue")
|
|
751
|
+
def _enable_all_services(self, startup_config: Dict, config: Config) -> None:
|
|
752
|
+
"""Enable all services and agents."""
|
|
753
|
+
self.startup_manager.enable_all_services(startup_config, config)
|
|
1008
754
|
|
|
1009
|
-
def
|
|
1010
|
-
"""
|
|
1011
|
-
self.
|
|
1012
|
-
self.console.print(f"[green]Switched to {self.current_scope} scope[/green]")
|
|
1013
|
-
Prompt.ask("Press Enter to continue")
|
|
755
|
+
def _disable_all_services(self, startup_config: Dict, config: Config) -> None:
|
|
756
|
+
"""Disable all services and agents."""
|
|
757
|
+
self.startup_manager.disable_all_services(startup_config, config)
|
|
1014
758
|
|
|
1015
|
-
def
|
|
1016
|
-
"""
|
|
1017
|
-
self.
|
|
1018
|
-
self._display_header()
|
|
759
|
+
def _reset_to_defaults(self, startup_config: Dict, config: Config) -> None:
|
|
760
|
+
"""Reset startup configuration to defaults."""
|
|
761
|
+
self.startup_manager.reset_to_defaults(startup_config, config)
|
|
1019
762
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
763
|
+
def _save_startup_configuration(self, startup_config: Dict, config: Config) -> bool:
|
|
764
|
+
"""Save startup configuration to config file and return whether to proceed to startup."""
|
|
765
|
+
return self.startup_manager.save_startup_configuration(startup_config, config)
|
|
1023
766
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
from ...hooks.claude_hooks.installer import HookInstaller
|
|
1028
|
-
|
|
1029
|
-
installer = HookInstaller()
|
|
1030
|
-
detected_version = installer.get_claude_version()
|
|
1031
|
-
if detected_version:
|
|
1032
|
-
is_compatible, _ = installer.is_version_compatible()
|
|
1033
|
-
claude_version = f"{detected_version} (Claude Code)"
|
|
1034
|
-
if not is_compatible:
|
|
1035
|
-
claude_version += (
|
|
1036
|
-
f" - Monitoring requires {installer.MIN_CLAUDE_VERSION}+"
|
|
1037
|
-
)
|
|
1038
|
-
else:
|
|
1039
|
-
# Fallback to direct subprocess call
|
|
1040
|
-
import subprocess
|
|
1041
|
-
|
|
1042
|
-
result = subprocess.run(
|
|
1043
|
-
["claude", "--version"],
|
|
1044
|
-
capture_output=True,
|
|
1045
|
-
text=True,
|
|
1046
|
-
timeout=5,
|
|
1047
|
-
check=False,
|
|
1048
|
-
)
|
|
1049
|
-
if result.returncode == 0:
|
|
1050
|
-
claude_version = result.stdout.strip()
|
|
1051
|
-
except:
|
|
1052
|
-
pass
|
|
1053
|
-
|
|
1054
|
-
# Create version panel
|
|
1055
|
-
version_text = f"""
|
|
1056
|
-
[bold cyan]Claude MPM[/bold cyan]
|
|
1057
|
-
Version: {mpm_version}
|
|
1058
|
-
Build: {build_number}
|
|
1059
|
-
|
|
1060
|
-
[bold cyan]Claude Code[/bold cyan]
|
|
1061
|
-
Version: {claude_version}
|
|
1062
|
-
|
|
1063
|
-
[bold cyan]Python[/bold cyan]
|
|
1064
|
-
Version: {sys.version.split()[0]}
|
|
1065
|
-
|
|
1066
|
-
[bold cyan]Configuration[/bold cyan]
|
|
1067
|
-
Scope: {self.current_scope}
|
|
1068
|
-
Directory: {self.project_dir}
|
|
1069
|
-
"""
|
|
767
|
+
def _save_all_configuration(self) -> bool:
|
|
768
|
+
"""Save all configuration changes across all contexts."""
|
|
769
|
+
return self.startup_manager.save_all_configuration()
|
|
1070
770
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
box=ROUNDED,
|
|
1075
|
-
style="green",
|
|
1076
|
-
)
|
|
771
|
+
def _launch_claude_mpm(self) -> None:
|
|
772
|
+
"""Launch Claude MPM run command, replacing current process."""
|
|
773
|
+
self.navigation.launch_claude_mpm()
|
|
1077
774
|
|
|
1078
|
-
|
|
1079
|
-
|
|
775
|
+
def _switch_scope(self) -> None:
|
|
776
|
+
"""Switch between project and user scope."""
|
|
777
|
+
self.navigation.switch_scope()
|
|
778
|
+
# Sync scope back from navigation
|
|
779
|
+
self.current_scope = self.navigation.current_scope
|
|
780
|
+
|
|
781
|
+
def _show_version_info_interactive(self) -> None:
|
|
782
|
+
"""Show version information in interactive mode."""
|
|
783
|
+
self.persistence.show_version_info_interactive()
|
|
1080
784
|
|
|
1081
785
|
# Non-interactive command methods
|
|
1082
786
|
|
|
1083
787
|
def _list_agents_non_interactive(self) -> CommandResult:
|
|
1084
788
|
"""List agents in non-interactive mode."""
|
|
1085
789
|
agents = self.agent_manager.discover_agents()
|
|
790
|
+
# Filter BASE_AGENT from all agent lists (1M-502 Phase 1)
|
|
791
|
+
agents = self._filter_agent_configs(agents, filter_deployed=False)
|
|
1086
792
|
|
|
1087
793
|
data = []
|
|
1088
794
|
for agent in agents:
|
|
@@ -1118,291 +824,714 @@ Directory: {self.project_dir}
|
|
|
1118
824
|
|
|
1119
825
|
def _export_config(self, file_path: str) -> CommandResult:
|
|
1120
826
|
"""Export configuration to a file."""
|
|
1121
|
-
|
|
1122
|
-
# Gather all configuration
|
|
1123
|
-
config_data = {"scope": self.current_scope, "agents": {}, "behaviors": {}}
|
|
827
|
+
return self.persistence.export_config(file_path)
|
|
1124
828
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
config_data["agents"][agent.name] = {
|
|
1129
|
-
"enabled": self.agent_manager.is_agent_enabled(agent.name),
|
|
1130
|
-
"template_path": str(self._get_agent_template_path(agent.name)),
|
|
1131
|
-
}
|
|
829
|
+
def _import_config(self, file_path: str) -> CommandResult:
|
|
830
|
+
"""Import configuration from a file."""
|
|
831
|
+
return self.persistence.import_config(file_path)
|
|
1132
832
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
json.dump(config_data, f, indent=2)
|
|
833
|
+
def _show_version_info(self) -> CommandResult:
|
|
834
|
+
"""Show version information in non-interactive mode."""
|
|
835
|
+
return self.persistence.show_version_info()
|
|
1137
836
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
837
|
+
def _install_hooks(self, force: bool = False) -> CommandResult:
|
|
838
|
+
"""Install Claude MPM hooks for Claude Code integration."""
|
|
839
|
+
# Share logger with hook manager for consistent error logging
|
|
840
|
+
self.hook_manager.logger = self.logger
|
|
841
|
+
return self.hook_manager.install_hooks(force=force)
|
|
1141
842
|
|
|
843
|
+
def _verify_hooks(self) -> CommandResult:
|
|
844
|
+
"""Verify that Claude MPM hooks are properly installed."""
|
|
845
|
+
# Share logger with hook manager for consistent error logging
|
|
846
|
+
self.hook_manager.logger = self.logger
|
|
847
|
+
return self.hook_manager.verify_hooks()
|
|
848
|
+
|
|
849
|
+
def _uninstall_hooks(self) -> CommandResult:
|
|
850
|
+
"""Uninstall Claude MPM hooks."""
|
|
851
|
+
# Share logger with hook manager for consistent error logging
|
|
852
|
+
self.hook_manager.logger = self.logger
|
|
853
|
+
return self.hook_manager.uninstall_hooks()
|
|
854
|
+
|
|
855
|
+
def _run_agent_management(self) -> CommandResult:
|
|
856
|
+
"""Jump directly to agent management."""
|
|
857
|
+
try:
|
|
858
|
+
self._manage_agents()
|
|
859
|
+
return CommandResult.success_result("Agent management completed")
|
|
860
|
+
except KeyboardInterrupt:
|
|
861
|
+
return CommandResult.success_result("Agent management cancelled")
|
|
1142
862
|
except Exception as e:
|
|
1143
|
-
return CommandResult.error_result(f"
|
|
863
|
+
return CommandResult.error_result(f"Agent management failed: {e}")
|
|
1144
864
|
|
|
1145
|
-
def
|
|
1146
|
-
"""
|
|
865
|
+
def _run_template_editing(self) -> CommandResult:
|
|
866
|
+
"""Jump directly to template editing."""
|
|
1147
867
|
try:
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
# Apply agent states
|
|
1156
|
-
if "agents" in config_data:
|
|
1157
|
-
for agent_name, agent_config in config_data["agents"].items():
|
|
1158
|
-
if "enabled" in agent_config:
|
|
1159
|
-
self.agent_manager.set_agent_enabled(
|
|
1160
|
-
agent_name, agent_config["enabled"]
|
|
1161
|
-
)
|
|
868
|
+
self._edit_templates()
|
|
869
|
+
return CommandResult.success_result("Template editing completed")
|
|
870
|
+
except KeyboardInterrupt:
|
|
871
|
+
return CommandResult.success_result("Template editing cancelled")
|
|
872
|
+
except Exception as e:
|
|
873
|
+
return CommandResult.error_result(f"Template editing failed: {e}")
|
|
1162
874
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
875
|
+
def _run_behavior_management(self) -> CommandResult:
|
|
876
|
+
"""Jump directly to behavior management."""
|
|
877
|
+
return self.behavior_manager.run_behavior_management()
|
|
1166
878
|
|
|
879
|
+
def _run_startup_configuration(self) -> CommandResult:
|
|
880
|
+
"""Jump directly to startup configuration."""
|
|
881
|
+
try:
|
|
882
|
+
proceed = self._manage_startup_configuration()
|
|
883
|
+
if proceed:
|
|
884
|
+
return CommandResult.success_result(
|
|
885
|
+
"Startup configuration saved, proceeding to startup"
|
|
886
|
+
)
|
|
887
|
+
return CommandResult.success_result("Startup configuration completed")
|
|
888
|
+
except KeyboardInterrupt:
|
|
889
|
+
return CommandResult.success_result("Startup configuration cancelled")
|
|
1167
890
|
except Exception as e:
|
|
1168
|
-
return CommandResult.error_result(f"
|
|
891
|
+
return CommandResult.error_result(f"Startup configuration failed: {e}")
|
|
1169
892
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
build_number = self.version_service.get_build_number()
|
|
1174
|
-
|
|
1175
|
-
data = {
|
|
1176
|
-
"mpm_version": mpm_version,
|
|
1177
|
-
"build_number": build_number,
|
|
1178
|
-
"python_version": sys.version.split()[0],
|
|
1179
|
-
}
|
|
893
|
+
# ========================================================================
|
|
894
|
+
# Enhanced Agent Management Methods (Remote Agent Discovery Integration)
|
|
895
|
+
# ========================================================================
|
|
1180
896
|
|
|
1181
|
-
|
|
897
|
+
def _get_configured_sources(self) -> List[Dict]:
|
|
898
|
+
"""Get list of configured agent sources with agent counts."""
|
|
1182
899
|
try:
|
|
1183
|
-
import
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
900
|
+
from claude_mpm.config.agent_sources import AgentSourceConfiguration
|
|
901
|
+
|
|
902
|
+
config = AgentSourceConfiguration.load()
|
|
903
|
+
|
|
904
|
+
# Convert repositories to source dictionaries
|
|
905
|
+
sources = []
|
|
906
|
+
for repo in config.repositories:
|
|
907
|
+
# Extract identifier from repository
|
|
908
|
+
identifier = repo.identifier
|
|
909
|
+
|
|
910
|
+
# Count agents in cache
|
|
911
|
+
cache_dir = (
|
|
912
|
+
Path.home() / ".claude-mpm" / "cache" / "remote-agents" / identifier
|
|
913
|
+
)
|
|
914
|
+
agent_count = 0
|
|
915
|
+
if cache_dir.exists():
|
|
916
|
+
agents_dir = cache_dir / "agents"
|
|
917
|
+
if agents_dir.exists():
|
|
918
|
+
agent_count = len(list(agents_dir.rglob("*.md")))
|
|
919
|
+
|
|
920
|
+
sources.append(
|
|
921
|
+
{
|
|
922
|
+
"identifier": identifier,
|
|
923
|
+
"url": repo.url,
|
|
924
|
+
"enabled": repo.enabled,
|
|
925
|
+
"priority": repo.priority,
|
|
926
|
+
"agent_count": agent_count,
|
|
927
|
+
}
|
|
928
|
+
)
|
|
929
|
+
|
|
930
|
+
return sources
|
|
931
|
+
except Exception as e:
|
|
932
|
+
self.logger.warning(f"Failed to get configured sources: {e}")
|
|
933
|
+
return []
|
|
934
|
+
|
|
935
|
+
def _filter_agent_configs(
|
|
936
|
+
self, agents: List[AgentConfig], filter_deployed: bool = False
|
|
937
|
+
) -> List[AgentConfig]:
|
|
938
|
+
"""Filter AgentConfig objects using agent_filters utilities.
|
|
939
|
+
|
|
940
|
+
Converts AgentConfig objects to dictionaries for filtering,
|
|
941
|
+
then back to AgentConfig. Always filters BASE_AGENT.
|
|
942
|
+
Optionally filters deployed agents.
|
|
943
|
+
|
|
944
|
+
Args:
|
|
945
|
+
agents: List of AgentConfig objects
|
|
946
|
+
filter_deployed: Whether to filter out deployed agents (default: False)
|
|
947
|
+
|
|
948
|
+
Returns:
|
|
949
|
+
Filtered list of AgentConfig objects
|
|
950
|
+
"""
|
|
951
|
+
# Convert AgentConfig to dict format for filtering
|
|
952
|
+
agent_dicts = []
|
|
953
|
+
for agent in agents:
|
|
954
|
+
agent_dicts.append(
|
|
955
|
+
{
|
|
956
|
+
"agent_id": agent.name,
|
|
957
|
+
"name": agent.name,
|
|
958
|
+
"description": agent.description,
|
|
959
|
+
"deployed": getattr(agent, "is_deployed", False),
|
|
960
|
+
}
|
|
1191
961
|
)
|
|
1192
|
-
if result.returncode == 0:
|
|
1193
|
-
data["claude_version"] = result.stdout.strip()
|
|
1194
|
-
except:
|
|
1195
|
-
data["claude_version"] = "Unknown"
|
|
1196
962
|
|
|
1197
|
-
#
|
|
1198
|
-
|
|
1199
|
-
|
|
963
|
+
# Apply filters (always filter BASE_AGENT)
|
|
964
|
+
filtered_dicts = apply_all_filters(
|
|
965
|
+
agent_dicts, filter_base=True, filter_deployed=filter_deployed
|
|
1200
966
|
)
|
|
967
|
+
|
|
968
|
+
# Convert back to AgentConfig objects
|
|
969
|
+
filtered_names = {d["agent_id"] for d in filtered_dicts}
|
|
970
|
+
return [a for a in agents if a.name in filtered_names]
|
|
971
|
+
|
|
972
|
+
def _display_agents_with_source_info(self, agents: List[AgentConfig]) -> None:
|
|
973
|
+
"""Display agents table with source information and installation status."""
|
|
974
|
+
from rich.table import Table
|
|
975
|
+
|
|
976
|
+
agents_table = Table(show_header=True, header_style="bold white")
|
|
977
|
+
agents_table.add_column("#", style="dim", width=4, no_wrap=True)
|
|
978
|
+
agents_table.add_column(
|
|
979
|
+
"Agent ID", style="white", width=35, no_wrap=True, overflow="ellipsis"
|
|
980
|
+
)
|
|
981
|
+
agents_table.add_column(
|
|
982
|
+
"Name", style="white", width=25, no_wrap=True, overflow="ellipsis"
|
|
983
|
+
)
|
|
984
|
+
agents_table.add_column("Source", style="bright_yellow", width=20, no_wrap=True)
|
|
985
|
+
agents_table.add_column("Status", style="white", width=12, no_wrap=True)
|
|
986
|
+
|
|
987
|
+
for idx, agent in enumerate(agents, 1):
|
|
988
|
+
# Determine source with repo name
|
|
989
|
+
source_type = getattr(agent, "source_type", "local")
|
|
990
|
+
|
|
991
|
+
if source_type == "remote":
|
|
992
|
+
# Get repo name from agent metadata
|
|
993
|
+
source_dict = getattr(agent, "source_dict", {})
|
|
994
|
+
repo_url = source_dict.get("source", "")
|
|
995
|
+
|
|
996
|
+
# Extract repo name from URL
|
|
997
|
+
if (
|
|
998
|
+
"bobmatnyc/claude-mpm" in repo_url
|
|
999
|
+
or "claude-mpm" in repo_url.lower()
|
|
1000
|
+
):
|
|
1001
|
+
source_label = "MPM Agents"
|
|
1002
|
+
elif "/" in repo_url:
|
|
1003
|
+
# Extract last part of org/repo
|
|
1004
|
+
parts = repo_url.rstrip("/").split("/")
|
|
1005
|
+
if len(parts) >= 2:
|
|
1006
|
+
source_label = f"{parts[-2]}/{parts[-1]}"
|
|
1007
|
+
else:
|
|
1008
|
+
source_label = "Community"
|
|
1009
|
+
else:
|
|
1010
|
+
source_label = "Community"
|
|
1011
|
+
else:
|
|
1012
|
+
source_label = "Local"
|
|
1013
|
+
|
|
1014
|
+
# Determine installation status (removed symbols for cleaner look)
|
|
1015
|
+
is_installed = getattr(agent, "is_deployed", False)
|
|
1016
|
+
if is_installed:
|
|
1017
|
+
status = "[green]Installed[/green]"
|
|
1018
|
+
else:
|
|
1019
|
+
status = "Available"
|
|
1020
|
+
|
|
1021
|
+
# Get display name (for remote agents, use display_name instead of agent_id)
|
|
1022
|
+
display_name = getattr(agent, "display_name", agent.name)
|
|
1023
|
+
# Let overflow="ellipsis" handle truncation automatically
|
|
1024
|
+
|
|
1025
|
+
agents_table.add_row(
|
|
1026
|
+
str(idx), agent.name, display_name, source_label, status
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
self.console.print(agents_table)
|
|
1030
|
+
self.console.print(f"\n[dim]Total: {len(agents)} agents available[/dim]")
|
|
1031
|
+
|
|
1032
|
+
def _manage_sources(self) -> None:
|
|
1033
|
+
"""Interactive source management."""
|
|
1034
|
+
self.console.print("\n[bold white]═══ Manage Agent Sources ═══[/bold white]\n")
|
|
1201
1035
|
self.console.print(
|
|
1202
|
-
|
|
1036
|
+
"[dim]Use 'claude-mpm agent-source' command to add/remove sources[/dim]"
|
|
1203
1037
|
)
|
|
1204
|
-
self.console.print(
|
|
1038
|
+
self.console.print("\nExamples:")
|
|
1039
|
+
self.console.print(" claude-mpm agent-source add <git-url>")
|
|
1040
|
+
self.console.print(" claude-mpm agent-source remove <identifier>")
|
|
1041
|
+
self.console.print(" claude-mpm agent-source list")
|
|
1042
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1205
1043
|
|
|
1206
|
-
|
|
1044
|
+
def _deploy_agents_individual(self, agents: List[AgentConfig]) -> None:
|
|
1045
|
+
"""Manage agent installation state (unified install/remove interface)."""
|
|
1046
|
+
if not agents:
|
|
1047
|
+
self.console.print("[yellow]No agents available[/yellow]")
|
|
1048
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1049
|
+
return
|
|
1207
1050
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1051
|
+
# Get ALL agents (filter BASE_AGENT but keep deployed agents visible)
|
|
1052
|
+
from claude_mpm.utils.agent_filters import (
|
|
1053
|
+
filter_base_agents,
|
|
1054
|
+
get_deployed_agent_ids,
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
# Filter BASE_AGENT but keep deployed agents visible
|
|
1058
|
+
all_agents = filter_base_agents(
|
|
1059
|
+
[
|
|
1060
|
+
{
|
|
1061
|
+
"agent_id": a.name,
|
|
1062
|
+
"name": a.name,
|
|
1063
|
+
"description": a.description,
|
|
1064
|
+
"deployed": getattr(a, "is_deployed", False),
|
|
1065
|
+
}
|
|
1066
|
+
for a in agents
|
|
1067
|
+
]
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
# Get deployed agent IDs
|
|
1071
|
+
deployed_ids = get_deployed_agent_ids()
|
|
1072
|
+
|
|
1073
|
+
if not all_agents:
|
|
1074
|
+
self.console.print("[yellow]No agents available[/yellow]")
|
|
1075
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1076
|
+
return
|
|
1077
|
+
|
|
1078
|
+
# Loop to allow adjusting selection
|
|
1079
|
+
while True:
|
|
1080
|
+
# Build checkbox choices with pre-selection
|
|
1081
|
+
agent_choices = []
|
|
1082
|
+
agent_map = {} # For lookup after selection
|
|
1083
|
+
|
|
1084
|
+
for agent in agents:
|
|
1085
|
+
if agent.name in {a["agent_id"] for a in all_agents}:
|
|
1086
|
+
display_name = getattr(agent, "display_name", agent.name)
|
|
1087
|
+
|
|
1088
|
+
# Pre-check if deployed
|
|
1089
|
+
# Extract leaf name from full path for comparison with deployed_ids
|
|
1090
|
+
agent_leaf_name = agent.name.split("/")[-1]
|
|
1091
|
+
is_deployed = agent_leaf_name in deployed_ids
|
|
1092
|
+
|
|
1093
|
+
# Simple format: "agent/path - Display Name"
|
|
1094
|
+
# Checkbox state (checked/unchecked) indicates installed status
|
|
1095
|
+
choice_text = f"{agent.name}"
|
|
1096
|
+
if display_name and display_name != agent.name:
|
|
1097
|
+
choice_text += f" - {display_name}"
|
|
1098
|
+
|
|
1099
|
+
# Create choice with checked=True for deployed agents
|
|
1100
|
+
# Note: questionary's default param is for single-select only
|
|
1101
|
+
# For multi-select, must use checked=True on Choice objects
|
|
1102
|
+
choice = questionary.Choice(
|
|
1103
|
+
title=choice_text, value=agent.name, checked=is_deployed
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
agent_choices.append(choice)
|
|
1107
|
+
agent_map[agent.name] = agent
|
|
1108
|
+
|
|
1109
|
+
# Multi-select with pre-selection
|
|
1110
|
+
self.console.print("\n[bold cyan]Manage Agent Installation[/bold cyan]")
|
|
1111
|
+
self.console.print("[dim]✓ Checked = Installed (uncheck to remove)[/dim]")
|
|
1112
|
+
self.console.print("[dim]○ Unchecked = Available (check to install)[/dim]")
|
|
1113
|
+
self.console.print(
|
|
1114
|
+
"[dim]Use arrow keys to navigate, space to toggle, "
|
|
1115
|
+
"Enter to apply changes[/dim]\n"
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
# Pre-selection via checked=True on Choice objects
|
|
1119
|
+
# (questionary's default param is for single-select only)
|
|
1120
|
+
selected_agent_ids = questionary.checkbox(
|
|
1121
|
+
"Agents:", choices=agent_choices, style=self.QUESTIONARY_STYLE
|
|
1122
|
+
).ask()
|
|
1123
|
+
|
|
1124
|
+
# Handle Esc
|
|
1125
|
+
if selected_agent_ids is None:
|
|
1126
|
+
self.console.print("[yellow]No changes made[/yellow]")
|
|
1127
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1128
|
+
return
|
|
1129
|
+
|
|
1130
|
+
# Convert to sets for comparison
|
|
1131
|
+
selected_set = set(selected_agent_ids)
|
|
1132
|
+
deployed_set = deployed_ids
|
|
1133
|
+
|
|
1134
|
+
# Determine actions
|
|
1135
|
+
to_deploy = selected_set - deployed_set # Selected but not deployed
|
|
1136
|
+
to_remove = deployed_set - selected_set # Deployed but not selected
|
|
1137
|
+
|
|
1138
|
+
if not to_deploy and not to_remove:
|
|
1139
|
+
self.console.print("[yellow]No changes made[/yellow]")
|
|
1140
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1141
|
+
return
|
|
1142
|
+
|
|
1143
|
+
# Show what will happen
|
|
1144
|
+
self.console.print("\n[bold]Changes to apply:[/bold]")
|
|
1145
|
+
if to_deploy:
|
|
1146
|
+
self.console.print(f"[green]Install {len(to_deploy)} agent(s)[/green]")
|
|
1147
|
+
for agent_id in to_deploy:
|
|
1148
|
+
self.console.print(f" + {agent_id}")
|
|
1149
|
+
if to_remove:
|
|
1150
|
+
self.console.print(f"[red]Remove {len(to_remove)} agent(s)[/red]")
|
|
1151
|
+
for agent_id in to_remove:
|
|
1152
|
+
self.console.print(f" - {agent_id}")
|
|
1153
|
+
|
|
1154
|
+
# Ask user to confirm, adjust, or cancel
|
|
1155
|
+
action = questionary.select(
|
|
1156
|
+
"\nWhat would you like to do?",
|
|
1157
|
+
choices=[
|
|
1158
|
+
questionary.Choice("Apply these changes", value="apply"),
|
|
1159
|
+
questionary.Choice("Adjust selection", value="adjust"),
|
|
1160
|
+
questionary.Choice("Cancel", value="cancel"),
|
|
1161
|
+
],
|
|
1162
|
+
default="apply",
|
|
1163
|
+
style=self.QUESTIONARY_STYLE,
|
|
1164
|
+
).ask()
|
|
1165
|
+
|
|
1166
|
+
if action == "cancel":
|
|
1167
|
+
self.console.print("[yellow]Changes cancelled[/yellow]")
|
|
1168
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1169
|
+
return
|
|
1170
|
+
if action == "adjust":
|
|
1171
|
+
# Loop back to agent selection
|
|
1172
|
+
continue
|
|
1173
|
+
|
|
1174
|
+
# Execute changes
|
|
1175
|
+
deploy_success = 0
|
|
1176
|
+
deploy_fail = 0
|
|
1177
|
+
remove_success = 0
|
|
1178
|
+
remove_fail = 0
|
|
1179
|
+
|
|
1180
|
+
# Install new agents
|
|
1181
|
+
for agent_id in to_deploy:
|
|
1182
|
+
agent = agent_map.get(agent_id)
|
|
1183
|
+
if agent and self._deploy_single_agent(agent, show_feedback=False):
|
|
1184
|
+
deploy_success += 1
|
|
1185
|
+
self.console.print(f"[green]✓ Installed: {agent_id}[/green]")
|
|
1186
|
+
else:
|
|
1187
|
+
deploy_fail += 1
|
|
1188
|
+
self.console.print(f"[red]✗ Failed to install: {agent_id}[/red]")
|
|
1212
1189
|
|
|
1213
|
-
|
|
1190
|
+
# Remove agents
|
|
1191
|
+
for agent_id in to_remove:
|
|
1192
|
+
try:
|
|
1193
|
+
import json
|
|
1194
|
+
from pathlib import Path
|
|
1214
1195
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1196
|
+
# Remove from project, legacy, and user locations
|
|
1197
|
+
project_path = (
|
|
1198
|
+
Path.cwd() / ".claude-mpm" / "agents" / f"{agent_id}.md"
|
|
1199
|
+
)
|
|
1200
|
+
legacy_path = Path.cwd() / ".claude" / "agents" / f"{agent_id}.md"
|
|
1201
|
+
user_path = Path.home() / ".claude" / "agents" / f"{agent_id}.md"
|
|
1202
|
+
|
|
1203
|
+
removed = False
|
|
1204
|
+
for path in [project_path, legacy_path, user_path]:
|
|
1205
|
+
if path.exists():
|
|
1206
|
+
path.unlink()
|
|
1207
|
+
removed = True
|
|
1208
|
+
|
|
1209
|
+
# Also remove from virtual deployment state
|
|
1210
|
+
deployment_state_paths = [
|
|
1211
|
+
Path.cwd() / ".claude" / "agents" / ".mpm_deployment_state",
|
|
1212
|
+
Path.home() / ".claude" / "agents" / ".mpm_deployment_state",
|
|
1213
|
+
]
|
|
1214
|
+
|
|
1215
|
+
for state_path in deployment_state_paths:
|
|
1216
|
+
if state_path.exists():
|
|
1217
|
+
try:
|
|
1218
|
+
with state_path.open() as f:
|
|
1219
|
+
state = json.load(f)
|
|
1220
|
+
|
|
1221
|
+
# Remove agent from deployment state
|
|
1222
|
+
agents = state.get("last_check_results", {}).get(
|
|
1223
|
+
"agents", {}
|
|
1224
|
+
)
|
|
1225
|
+
if agent_id in agents:
|
|
1226
|
+
del agents[agent_id]
|
|
1227
|
+
removed = True
|
|
1228
|
+
|
|
1229
|
+
# Save updated state
|
|
1230
|
+
with state_path.open("w") as f:
|
|
1231
|
+
json.dump(state, f, indent=2)
|
|
1232
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
1233
|
+
# Log but don't fail - physical removal still counts
|
|
1234
|
+
self.logger.debug(
|
|
1235
|
+
f"Failed to update deployment state at {state_path}: {e}"
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
if removed:
|
|
1239
|
+
remove_success += 1
|
|
1240
|
+
self.console.print(f"[green]✓ Removed: {agent_id}[/green]")
|
|
1241
|
+
else:
|
|
1242
|
+
remove_fail += 1
|
|
1243
|
+
self.console.print(f"[yellow]⚠ Not found: {agent_id}[/yellow]")
|
|
1244
|
+
except Exception as e:
|
|
1245
|
+
remove_fail += 1
|
|
1246
|
+
self.console.print(f"[red]✗ Failed to remove {agent_id}: {e}[/red]")
|
|
1219
1247
|
|
|
1220
|
-
|
|
1248
|
+
# Show summary
|
|
1249
|
+
self.console.print()
|
|
1250
|
+
if deploy_success > 0:
|
|
1221
1251
|
self.console.print(
|
|
1222
|
-
"
|
|
1252
|
+
f"[green]✓ Installed {deploy_success} agent(s)[/green]"
|
|
1223
1253
|
)
|
|
1254
|
+
if deploy_fail > 0:
|
|
1224
1255
|
self.console.print(
|
|
1225
|
-
"
|
|
1256
|
+
f"[red]✗ Failed to install {deploy_fail} agent(s)[/red]"
|
|
1226
1257
|
)
|
|
1258
|
+
if remove_success > 0:
|
|
1227
1259
|
self.console.print(
|
|
1228
|
-
f"
|
|
1260
|
+
f"[green]✓ Removed {remove_success} agent(s)[/green]"
|
|
1229
1261
|
)
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1262
|
+
if remove_fail > 0:
|
|
1263
|
+
self.console.print(
|
|
1264
|
+
f"[red]✗ Failed to remove {remove_fail} agent(s)[/red]"
|
|
1233
1265
|
)
|
|
1234
1266
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
self.console.print("[yellow]Hooks are already installed.[/yellow]")
|
|
1239
|
-
self.console.print("Use --force to reinstall.")
|
|
1267
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1268
|
+
# Exit the loop after successful execution
|
|
1269
|
+
break
|
|
1240
1270
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1271
|
+
def _deploy_agents_preset(self) -> None:
|
|
1272
|
+
"""Install agents using preset configuration."""
|
|
1273
|
+
try:
|
|
1274
|
+
from claude_mpm.services.agents.agent_preset_service import (
|
|
1275
|
+
AgentPresetService,
|
|
1276
|
+
)
|
|
1277
|
+
from claude_mpm.services.agents.git_source_manager import GitSourceManager
|
|
1245
1278
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
)
|
|
1279
|
+
source_manager = GitSourceManager()
|
|
1280
|
+
preset_service = AgentPresetService(source_manager)
|
|
1249
1281
|
|
|
1250
|
-
|
|
1251
|
-
self.console.print("[cyan]Installing Claude MPM hooks...[/cyan]")
|
|
1252
|
-
success = installer.install_hooks(force=force)
|
|
1282
|
+
presets = preset_service.list_presets()
|
|
1253
1283
|
|
|
1254
|
-
if
|
|
1255
|
-
self.console.print("[
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
self.console.print(" /mpm status - Show claude-mpm status")
|
|
1284
|
+
if not presets:
|
|
1285
|
+
self.console.print("[yellow]No presets available[/yellow]")
|
|
1286
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1287
|
+
return
|
|
1259
1288
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
)
|
|
1266
|
-
for issue in issues:
|
|
1267
|
-
self.console.print(f" - {issue}")
|
|
1289
|
+
self.console.print("\n[bold white]═══ Available Presets ═══[/bold white]\n")
|
|
1290
|
+
for idx, preset in enumerate(presets, 1):
|
|
1291
|
+
self.console.print(f" {idx}. [white]{preset['name']}[/white]")
|
|
1292
|
+
self.console.print(f" {preset['description']}")
|
|
1293
|
+
self.console.print(f" [dim]Agents: {len(preset['agents'])}[/dim]\n")
|
|
1268
1294
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1295
|
+
selection = Prompt.ask("\nEnter preset number (or 'c' to cancel)")
|
|
1296
|
+
if selection.lower() == "c":
|
|
1297
|
+
return
|
|
1272
1298
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
return CommandResult.error_result("HookInstaller module not found")
|
|
1277
|
-
except Exception as e:
|
|
1278
|
-
self.logger.error(f"Hook installation error: {e}", exc_info=True)
|
|
1279
|
-
return CommandResult.error_result(f"Hook installation failed: {e}")
|
|
1299
|
+
idx = int(selection) - 1
|
|
1300
|
+
if 0 <= idx < len(presets):
|
|
1301
|
+
preset_name = presets[idx]["name"]
|
|
1280
1302
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
try:
|
|
1284
|
-
from ...hooks.claude_hooks.installer import HookInstaller
|
|
1303
|
+
# Resolve and deploy preset
|
|
1304
|
+
resolution = preset_service.resolve_agents(preset_name)
|
|
1285
1305
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1306
|
+
if resolution.get("missing_agents"):
|
|
1307
|
+
self.console.print(
|
|
1308
|
+
f"[red]Missing agents: {len(resolution['missing_agents'])}[/red]"
|
|
1309
|
+
)
|
|
1310
|
+
for agent_id in resolution["missing_agents"]:
|
|
1311
|
+
self.console.print(f" • {agent_id}")
|
|
1312
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1313
|
+
return
|
|
1288
1314
|
|
|
1289
|
-
|
|
1315
|
+
# Confirm installation
|
|
1316
|
+
self.console.print(
|
|
1317
|
+
f"\n[bold]Preset '{preset_name}' includes {len(resolution['agents'])} agents[/bold]"
|
|
1318
|
+
)
|
|
1319
|
+
if Confirm.ask("Install all agents?", default=True):
|
|
1320
|
+
installed = 0
|
|
1321
|
+
for agent in resolution["agents"]:
|
|
1322
|
+
# Convert dict to AgentConfig-like object for installation
|
|
1323
|
+
agent_config = AgentConfig(
|
|
1324
|
+
name=agent.get("agent_id", "unknown"),
|
|
1325
|
+
description=agent.get("metadata", {}).get(
|
|
1326
|
+
"description", ""
|
|
1327
|
+
),
|
|
1328
|
+
dependencies=[],
|
|
1329
|
+
)
|
|
1330
|
+
agent_config.source_dict = agent
|
|
1331
|
+
agent_config.full_agent_id = agent.get("agent_id", "unknown")
|
|
1332
|
+
|
|
1333
|
+
if self._deploy_single_agent(agent_config, show_feedback=False):
|
|
1334
|
+
installed += 1
|
|
1290
1335
|
|
|
1291
|
-
# Show Claude Code version and compatibility
|
|
1292
|
-
if status.get("claude_version"):
|
|
1293
|
-
self.console.print(f"Claude Code Version: {status['claude_version']}")
|
|
1294
|
-
if status.get("version_compatible"):
|
|
1295
1336
|
self.console.print(
|
|
1296
|
-
"[green]✓[/green]
|
|
1337
|
+
f"\n[green]✓ Installed {installed}/{len(resolution['agents'])} agents[/green]"
|
|
1297
1338
|
)
|
|
1339
|
+
|
|
1340
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1341
|
+
else:
|
|
1342
|
+
self.console.print("[red]Invalid selection[/red]")
|
|
1343
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1344
|
+
|
|
1345
|
+
except Exception as e:
|
|
1346
|
+
self.console.print(f"[red]Error installing preset: {e}[/red]")
|
|
1347
|
+
self.logger.error(f"Preset installation failed: {e}", exc_info=True)
|
|
1348
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1349
|
+
|
|
1350
|
+
def _deploy_single_agent(
|
|
1351
|
+
self, agent: AgentConfig, show_feedback: bool = True
|
|
1352
|
+
) -> bool:
|
|
1353
|
+
"""Install a single agent to the appropriate location."""
|
|
1354
|
+
try:
|
|
1355
|
+
# Check if this is a remote agent with source_dict
|
|
1356
|
+
source_dict = getattr(agent, "source_dict", None)
|
|
1357
|
+
full_agent_id = getattr(agent, "full_agent_id", agent.name)
|
|
1358
|
+
|
|
1359
|
+
if source_dict:
|
|
1360
|
+
# Deploy remote agent using its source file
|
|
1361
|
+
source_file = Path(source_dict.get("source_file", ""))
|
|
1362
|
+
if not source_file.exists():
|
|
1363
|
+
if show_feedback:
|
|
1364
|
+
self.console.print(
|
|
1365
|
+
f"[red]✗ Source file not found: {source_file}[/red]"
|
|
1366
|
+
)
|
|
1367
|
+
return False
|
|
1368
|
+
|
|
1369
|
+
# Determine target file name (use leaf name from hierarchical ID)
|
|
1370
|
+
if "/" in full_agent_id:
|
|
1371
|
+
target_name = full_agent_id.split("/")[-1] + ".md"
|
|
1298
1372
|
else:
|
|
1373
|
+
target_name = full_agent_id + ".md"
|
|
1374
|
+
|
|
1375
|
+
# Deploy to user-level agents directory
|
|
1376
|
+
target_dir = Path.home() / ".claude" / "agents"
|
|
1377
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
1378
|
+
target_file = target_dir / target_name
|
|
1379
|
+
|
|
1380
|
+
if show_feedback:
|
|
1299
1381
|
self.console.print(
|
|
1300
|
-
f"[
|
|
1382
|
+
f"\n[white]Installing {full_agent_id}...[/white]"
|
|
1301
1383
|
)
|
|
1302
|
-
self.console.print()
|
|
1303
|
-
else:
|
|
1304
|
-
self.console.print(
|
|
1305
|
-
"[yellow]Claude Code version could not be detected[/yellow]"
|
|
1306
|
-
)
|
|
1307
|
-
self.console.print()
|
|
1308
1384
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
f"[green]✓[/green] Hooks installed at: {status['hook_script']}"
|
|
1312
|
-
)
|
|
1313
|
-
else:
|
|
1314
|
-
self.console.print("[red]✗[/red] Hooks not installed")
|
|
1385
|
+
# Copy the agent file
|
|
1386
|
+
import shutil
|
|
1315
1387
|
|
|
1316
|
-
|
|
1317
|
-
self.console.print(
|
|
1318
|
-
f"[green]✓[/green] Settings file: {status['settings_file']}"
|
|
1319
|
-
)
|
|
1320
|
-
else:
|
|
1321
|
-
self.console.print("[red]✗[/red] Settings file not found")
|
|
1388
|
+
shutil.copy2(source_file, target_file)
|
|
1322
1389
|
|
|
1323
|
-
|
|
1390
|
+
if show_feedback:
|
|
1391
|
+
self.console.print(
|
|
1392
|
+
f"[green]✓ Successfully installed {full_agent_id} to {target_file}[/green]"
|
|
1393
|
+
)
|
|
1394
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1395
|
+
|
|
1396
|
+
return True
|
|
1397
|
+
# Legacy local template installation (not implemented here)
|
|
1398
|
+
if show_feedback:
|
|
1324
1399
|
self.console.print(
|
|
1325
|
-
|
|
1400
|
+
"[yellow]Local template installation not yet implemented[/yellow]"
|
|
1326
1401
|
)
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
if status["valid"]:
|
|
1331
|
-
self.console.print("\n[green]All checks passed![/green]")
|
|
1332
|
-
else:
|
|
1333
|
-
self.console.print("\n[red]Issues found:[/red]")
|
|
1334
|
-
for issue in status["issues"]:
|
|
1335
|
-
self.console.print(f" - {issue}")
|
|
1402
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1403
|
+
return False
|
|
1336
1404
|
|
|
1337
|
-
return CommandResult.success_result(
|
|
1338
|
-
"Hook verification complete", data=status
|
|
1339
|
-
)
|
|
1340
|
-
|
|
1341
|
-
except ImportError:
|
|
1342
|
-
self.console.print("[red]Error: HookInstaller module not found[/red]")
|
|
1343
|
-
return CommandResult.error_result("HookInstaller module not found")
|
|
1344
1405
|
except Exception as e:
|
|
1345
|
-
|
|
1346
|
-
|
|
1406
|
+
if show_feedback:
|
|
1407
|
+
self.console.print(f"[red]Error installing agent: {e}[/red]")
|
|
1408
|
+
self.logger.error(f"Agent installation failed: {e}", exc_info=True)
|
|
1409
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1410
|
+
return False
|
|
1411
|
+
|
|
1412
|
+
def _remove_agents(self, agents: List[AgentConfig]) -> None:
|
|
1413
|
+
"""Remove installed agents."""
|
|
1414
|
+
# Filter to installed agents only
|
|
1415
|
+
installed = [a for a in agents if getattr(a, "is_deployed", False)]
|
|
1416
|
+
|
|
1417
|
+
if not installed:
|
|
1418
|
+
self.console.print("[yellow]No agents are currently installed[/yellow]")
|
|
1419
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1420
|
+
return
|
|
1347
1421
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1422
|
+
self.console.print(f"\n[bold]Installed agents ({len(installed)}):[/bold]")
|
|
1423
|
+
for idx, agent in enumerate(installed, 1):
|
|
1424
|
+
display_name = getattr(agent, "display_name", agent.name)
|
|
1425
|
+
self.console.print(f" {idx}. {agent.name} - {display_name}")
|
|
1352
1426
|
|
|
1353
|
-
|
|
1427
|
+
selection = Prompt.ask("\nEnter agent number to remove (or 'c' to cancel)")
|
|
1428
|
+
if selection.lower() == "c":
|
|
1429
|
+
return
|
|
1354
1430
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1431
|
+
try:
|
|
1432
|
+
idx = int(selection) - 1
|
|
1433
|
+
if 0 <= idx < len(installed):
|
|
1434
|
+
agent = installed[idx]
|
|
1435
|
+
full_agent_id = getattr(agent, "full_agent_id", agent.name)
|
|
1436
|
+
|
|
1437
|
+
# Determine possible file names (hierarchical and leaf)
|
|
1438
|
+
file_names = [f"{full_agent_id}.md"]
|
|
1439
|
+
if "/" in full_agent_id:
|
|
1440
|
+
leaf_name = full_agent_id.split("/")[-1]
|
|
1441
|
+
file_names.append(f"{leaf_name}.md")
|
|
1442
|
+
|
|
1443
|
+
# Remove from both project and user directories
|
|
1444
|
+
removed = False
|
|
1445
|
+
project_agent_dir = Path.cwd() / ".claude-mpm" / "agents"
|
|
1446
|
+
user_agent_dir = Path.home() / ".claude" / "agents"
|
|
1447
|
+
|
|
1448
|
+
for file_name in file_names:
|
|
1449
|
+
project_file = project_agent_dir / file_name
|
|
1450
|
+
user_file = user_agent_dir / file_name
|
|
1451
|
+
|
|
1452
|
+
if project_file.exists():
|
|
1453
|
+
project_file.unlink()
|
|
1454
|
+
removed = True
|
|
1455
|
+
self.console.print(f"[green]✓ Removed {project_file}[/green]")
|
|
1456
|
+
|
|
1457
|
+
if user_file.exists():
|
|
1458
|
+
user_file.unlink()
|
|
1459
|
+
removed = True
|
|
1460
|
+
self.console.print(f"[green]✓ Removed {user_file}[/green]")
|
|
1461
|
+
|
|
1462
|
+
if removed:
|
|
1463
|
+
self.console.print(
|
|
1464
|
+
f"[green]✓ Successfully removed {full_agent_id}[/green]"
|
|
1465
|
+
)
|
|
1466
|
+
else:
|
|
1467
|
+
self.console.print("[yellow]Agent files not found[/yellow]")
|
|
1360
1468
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1469
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1470
|
+
else:
|
|
1471
|
+
self.console.print("[red]Invalid selection[/red]")
|
|
1472
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1473
|
+
|
|
1474
|
+
except (ValueError, IndexError):
|
|
1475
|
+
self.console.print("[red]Invalid selection[/red]")
|
|
1476
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1477
|
+
|
|
1478
|
+
def _view_agent_details_enhanced(self, agents: List[AgentConfig]) -> None:
|
|
1479
|
+
"""View detailed agent information with enhanced remote agent details."""
|
|
1480
|
+
if not agents:
|
|
1481
|
+
self.console.print("[yellow]No agents available[/yellow]")
|
|
1482
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1483
|
+
return
|
|
1363
1484
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
self.console.print("
|
|
1368
|
-
return CommandResult.error_result("Hook uninstallation failed")
|
|
1485
|
+
self.console.print(f"\n[bold]Available agents ({len(agents)}):[/bold]")
|
|
1486
|
+
for idx, agent in enumerate(agents, 1):
|
|
1487
|
+
display_name = getattr(agent, "display_name", agent.name)
|
|
1488
|
+
self.console.print(f" {idx}. {agent.name} - {display_name}")
|
|
1369
1489
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
return
|
|
1373
|
-
except Exception as e:
|
|
1374
|
-
self.logger.error(f"Hook uninstallation error: {e}", exc_info=True)
|
|
1375
|
-
return CommandResult.error_result(f"Hook uninstallation failed: {e}")
|
|
1490
|
+
selection = Prompt.ask("\nEnter agent number to view (or 'c' to cancel)")
|
|
1491
|
+
if selection.lower() == "c":
|
|
1492
|
+
return
|
|
1376
1493
|
|
|
1377
|
-
def _run_agent_management(self) -> CommandResult:
|
|
1378
|
-
"""Jump directly to agent management."""
|
|
1379
1494
|
try:
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
return CommandResult.success_result("Agent management cancelled")
|
|
1384
|
-
except Exception as e:
|
|
1385
|
-
return CommandResult.error_result(f"Agent management failed: {e}")
|
|
1495
|
+
idx = int(selection) - 1
|
|
1496
|
+
if 0 <= idx < len(agents):
|
|
1497
|
+
agent = agents[idx]
|
|
1386
1498
|
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1499
|
+
self.console.clear()
|
|
1500
|
+
self.console.print("\n[bold white]═══ Agent Details ═══[/bold white]\n")
|
|
1501
|
+
|
|
1502
|
+
# Basic info
|
|
1503
|
+
self.console.print(f"[bold]ID:[/bold] {agent.name}")
|
|
1504
|
+
display_name = getattr(agent, "display_name", "N/A")
|
|
1505
|
+
self.console.print(f"[bold]Name:[/bold] {display_name}")
|
|
1506
|
+
self.console.print(f"[bold]Description:[/bold] {agent.description}")
|
|
1507
|
+
|
|
1508
|
+
# Source info
|
|
1509
|
+
source_type = getattr(agent, "source_type", "local")
|
|
1510
|
+
self.console.print(f"[bold]Source Type:[/bold] {source_type}")
|
|
1511
|
+
|
|
1512
|
+
if source_type == "remote":
|
|
1513
|
+
source_dict = getattr(agent, "source_dict", {})
|
|
1514
|
+
category = source_dict.get("category", "N/A")
|
|
1515
|
+
source = source_dict.get("source", "N/A")
|
|
1516
|
+
version = source_dict.get("version", "N/A")
|
|
1517
|
+
|
|
1518
|
+
self.console.print(f"[bold]Category:[/bold] {category}")
|
|
1519
|
+
self.console.print(f"[bold]Source:[/bold] {source}")
|
|
1520
|
+
self.console.print(f"[bold]Version:[/bold] {version[:16]}...")
|
|
1521
|
+
|
|
1522
|
+
# Installation status
|
|
1523
|
+
is_installed = getattr(agent, "is_deployed", False)
|
|
1524
|
+
status = "Installed" if is_installed else "Available"
|
|
1525
|
+
self.console.print(f"[bold]Status:[/bold] {status}")
|
|
1526
|
+
|
|
1527
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1528
|
+
else:
|
|
1529
|
+
self.console.print("[red]Invalid selection[/red]")
|
|
1530
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1396
1531
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
self._manage_behaviors()
|
|
1401
|
-
return CommandResult.success_result("Behavior management completed")
|
|
1402
|
-
except KeyboardInterrupt:
|
|
1403
|
-
return CommandResult.success_result("Behavior management cancelled")
|
|
1404
|
-
except Exception as e:
|
|
1405
|
-
return CommandResult.error_result(f"Behavior management failed: {e}")
|
|
1532
|
+
except (ValueError, IndexError):
|
|
1533
|
+
self.console.print("[red]Invalid selection[/red]")
|
|
1534
|
+
Prompt.ask("\nPress Enter to continue")
|
|
1406
1535
|
|
|
1407
1536
|
|
|
1408
1537
|
def manage_configure(args) -> int:
|